简介
大家好,我是simple
,我的理想是利用科技手段来解决生活中遇到的各种问题。
计算两线段在平面中的交点需要以下步骤:
- 确定两线段的端点坐标。
- 判断线段是否平行。
- 计算交点坐标。
- 判断交点是否在两线段之间。
斜截式
初始思路是这样的,既然知道了两条线段的点,那我直接根据公式列出y = kx + b
。只要通过斜率判断如果斜率相同就表示为重合或者是平行,那就没有交点,如果斜率不相同,那必然有一个交点,只要解了x,y值就大功告成了。
判断是否在线上:判断解出的x和y是否分别在x1,x2与y1,y2之间即可。
浅浅证明一下: 因为:
math
∵ y1 = k1 * x1 + b1
∵ y2 = k1 * x2 + b1
两等式相减,得:
y1 - y2 = k1 * (x1 - x2)
∴ k1 = (y1 - y2) / (x1 - x2)
k2同理可求
js
/**
* 计算两条线段的交点坐标
* @param {Array} path1 - 第一条线段顶点数组
* @param {Array} path2 - 第二条线段顶点数组
* @returns {Object} - 交点坐标对象
*/
getCross(path1, path2) {
// 计算交点的坐标
const x1 = path1[0].x;
const y1 = path1[0].y;
const x2 = path1[1].x;
const y2 = path1[1].y;
const x3 = path2[0].x;
const y3 = path2[0].y;
const x4 = path2[1].x;
const y4 = path2[1].y;
const k1 = (y1 - y2) / (x1 - x2);
const b1 = y1 - k1 * x1;
const k2 = (y3 - y4) / (x3 - x4);
const b2 = y3 - k2 * x3;
const x = (b2 - b1) / (k1 - k2);
const y = k1 * x + b1;
if (
y >= Math.min(y1, y2) &&
y <= Math.max(y1, y2) &&
y >= Math.min(y3, y4) &&
y <= Math.max(y3, y4) &&
x >= Math.min(x1, x2) &&
x <= Math.max(x1, x2) &&
x >= Math.min(x3, x4) &&
x <= Math.max(x3, x4)
) {
return {
x: x,
y: y
}
}
return null;
}
一顿操作猛如虎,战绩一看0-9。
y1是有可能等于y2的,这还不是最致命的,最致命的是x1也是有可能等于x2的。当y1 == y2的时候,在求得k1 = (y1 - y2) / (x1 - x2)
的过程时k1直接变成0,而公式变成了y = b,x1 == x2的时候,分母变成0。害,换方案吧。
参数方程式
三个参数分别表示直线上的两个已知点以及参数,即:
scss
x = x1 + t1(x2 - x1)
y = y1 + t1(y2 - y1)
且符合0 <= t1 <= 1(如果t>1或者t<0,表明在该线段的延长线上,但不在该线段上,等于0的时候表明在x1上,等于1表明在x2上)
知道了上述公式,计算就简单很多了。也浅浅推导一下公式吧。
scss
∵ x = x1 + t1(x2 - x1)
∵ x = x2 + t2(x4 - x3)
∴ x1 + t1(x2 - x1) = x2 + t2(x4 - x3)
同理 y1 + t1(y2 - y1) = y2 + t2(y4 - y3)
简化后得到
<math xmlns="http://www.w3.org/1998/Math/MathML"> t 1 = x 3 ( y 4 − y 3 ) + y 1 ( x 4 − x 3 ) − y 3 ( x 4 − x 3 ) − x 1 ( y 4 − y 3 ) ( ( x 2 − x 1 ) ( y 4 − y 3 ) − ( x 4 − x 3 ) ( y 2 − y 1 ) ) t1 = \frac{x3(y4 - y3) + y1(x4 - x3) - y3(x4 - x3) - x1(y4 - y3)}{((x2 - x1)(y4 - y3) - (x4 - x3)(y2 - y1))} </math>t1=((x2−x1)(y4−y3)−(x4−x3)(y2−y1))x3(y4−y3)+y1(x4−x3)−y3(x4−x3)−x1(y4−y3)
t2暂时用不上了,这里就先不简化了。
仔细看看得到简化后的分母,(x2 - x1)(y4 - y3) - (x4 - x3)(y2 - y1);有点眼熟吗?这个叫叉积,当叉积为0的时候,表明两线段平行或者重合。我们只需要提前将分母计算一下,刚好也可以避免分母为0的情况。
js
/**
* 计算两条线段的交点坐标
* @param {Array} path1 - 第一条线段顶点数组
* @param {Array} path2 - 第二条线段顶点数组
* @returns {Object} - 交点坐标对象
*/
getCross(path1, path2) {
// 计算交点的坐标
const x1 = path1[0].x;
const y1 = path1[0].y;
const x2 = path1[1].x;
const y2 = path1[1].y;
const x3 = path2[0].x;
const y3 = path2[0].y;
const x4 = path2[1].x;
const y4 = path2[1].y;
const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denom === 0) {
// 两条线段平行,无交点
return null;
}
const t = (x3 * (y4 - y3) + y1 * (x4 - x3) - y3 * (x4 - x3) - x1 * (y4 - y3)) / denom;
const intersectionX = x1 + t * (x2 - x1);
const intersectionY = y1 + t * (y2 - y1);
// 判断交点是否在两条线段之间
if (t < 1 && t > 0) {
return {
x: intersectionX,
y: intersectionY
}
} else {
return null;
}
}
总结
至此大功告成,使用参数式方程可以解决所有的线段交点的问题,但是使用斜截式方程会对平行于y轴的线段无效。