计算线段的交点

假设点 A 和点 B 组成线段 F, 点 C 和点 D 组成线段 G, 求交点 E 的坐标

要计算线段的交点, 分成两个步骤

  1. 判断线段是否相交
  2. 如果相交计算交点

判断 F 和 G 是否存在相交

用投影法来判断是否相交

投影

两个向量 OA 和 OB ,从 A 出发做一条垂直于 OB 上直线, 交点是 C, OC 就是 OA 在 OB 上的投影

投影的公式是 |OC| = (OA * OB) / |OB|

用投影法判断相交

做一条垂直于 AB 的直线 (E,F)

将 D,A,C 分别投影于 EF 上, 得到点 d1, a1, c1

如果 d1 和 c1 分别在 a1 的两侧, 就说明点 C 和点 D 分别在线段 AB 的两侧

再做一条 CD 的垂直线,投影点 A,C,B 于 a1,c1,b1, 如果 a1 和 b1 也分别在 c1 的两侧, 就说明线段 CD 和线段 AB 一定相交

计算投影

这里有一个隐藏的投影点就是原点 O

比如 (o1,c1) 就是 OC 在垂直线上的投影, 以此类推

实际上要计算的是 (o1,c1) - (o1,a1) 是否和 (o1,a1) - (o1,d1) 同号, 如果是同号, 那么说明 c1 和 d1 分别在 a1 的两侧

开始编写代码

ts 复制代码
// 定义向量
type Vector = { x: number; y: number }
// 定义坐标点
type Point = Vector
// 定义线段,两个坐标点组成
type Segment = [Point, Point]

// 创建从点 A 到点 B 的向量
function createVector(pointA: Point, pointB: Point): Vector {
  return {
    x: pointB.x - pointA.x,
    y: pointB.y - pointA.y,
  }
}

// 获取垂直线段
function getVerticalVector(vector: Vector): Vector {
  return {
    x: -vector.y,
    y: vector.x,
  }
}

function dotProduct(vectorA: Vector, vectorB: Vector): number {
  return vectorA.x * vectorB.x + vectorA.y * vectorB.y
}

// 判断是否相交
function isTwoSegmentIntersect(segmentA: Segment, segmentB: Segment): boolean {
  const [a, b] = segmentA
  const [c, d] = segmentB

  const ab = createVector(a, b)
  const abVertical = getVerticalVector(ab)
  const c1 = dotProduct(c, abVertical)
  let a1 = dotProduct(a, abVertical)
  let d1 = dotProduct(d, abVertical)

  if ((c1 - a1) * (a1 - d1) < 0) {
    return false
  }

  const cd = createVector(c, d)
  const cdVertical = getVerticalVector(cd)
  const b1 = dotProduct(b, cdVertical)
  a1 = dotProduct(a, cdVertical)
  d1 = dotProduct(d, cdVertical)

  return (b1 - d1) * (d1 - a1) >= 0
}

计算线段的交点

如果确定线段相交, 那么下一步就是计算交点

投影法计算交点

交点是 E , 通过观察可以知道, |DE|/|DC| 的比例等于 |(d1,a1)|/|(d1,c1)| 的比例

所以可以得到这个结论,假设比例为 K

E = D + K * DC

代码如下

ts 复制代码
function addVector(vectorA: Vector, vectorB: Vector): Vector {
  return { x: vectorA.x + vectorB.x, y: vectorA.y + vectorB.y }
}

function productVector(vector: Vector, num: number) {
  return { x: vector.x * num, y: vector.y * num }
}

function getIntersectPointFromTwoSegment(
  segmentA: Segment,
  segmentB: Segment
): Point | null {
  if (!isTwoSegmentIntersect(segmentA, segmentB)) {
    return null
  }

  const [a, b] = segmentA
  const [c, d] = segmentB

  const ab = createVector(a, b)
  const abVertical = getVerticalVector(ab)
  const c1 = dotProduct(c, abVertical)
  const d1 = dotProduct(d, abVertical)
  const a1 = dotProduct(a, abVertical)

  const k = (a1 - d1) / (c1 - d1)

  const dc = createVector(d, c)

  return addVector(d, productVector(dc, k))
}

还有一点是值得注意的, 就是当两条线段平行并且在延长线上重合的时候

此时只要判断 A 和 C 任意一点是否在另外一条线段上即可, 当然你也可以认为这种情况下有两个交点, 或者不算有交点, 或者有无穷个交点, 此处不再纠结这个问题

补全这个逻辑, 核心代码逻辑如下

ts 复制代码
function getIntersectPointFromTwoSegment(
  segmentA: Segment,
  segmentB: Segment
): Point | null {
  if (!isTwoSegmentIntersect(segmentA, segmentB)) {
    return null
  }

  const [a, b] = segmentA
  const [c, d] = segmentB

  const ab = createVector(a, b)
  const abVertical = getVerticalVector(ab)
  const c1 = dotProduct(c, abVertical)
  const d1 = dotProduct(d, abVertical)
  const a1 = dotProduct(a, abVertical)

  if (c1 - d1 === 0) {
    // 特殊情况平行并且延长线重合
    const cd = createVector(c, d)
    const a1 = dotProduct(a, cd)
    const c1 = dotProduct(c, cd)
    const d1 = dotProduct(d, cd)
    if (a1 >= c1 && a1 <= d1) {
      return a
    }
    const b1 = dotProduct(b, cd)
    if (c1 >= a1 && c1 <= b1) {
      return c
    }

    return null
  }

  const k = (a1 - d1) / (c1 - d1)

  const dc = createVector(d, c)

  return addVector(d, productVector(dc, k))
}

验证代码

用 leetcode 测试一下

相关推荐
顶呱呱程序6 分钟前
2-143 基于matlab-GUI的脉冲响应不变法实现音频滤波功能
算法·matlab·音视频·matlab-gui·音频滤波·脉冲响应不变法
爱吃生蚝的于勒28 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~31 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
王哈哈^_^1 小时前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt
星沁城1 小时前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
脉牛杂德1 小时前
多项式加法——C语言
数据结构·c++·算法
legend_jz1 小时前
STL--哈希
c++·算法·哈希算法
kingmax542120081 小时前
初三数学,最优解问题
算法
一直学习永不止步2 小时前
LeetCode题练习与总结:赎金信--383
java·数据结构·算法·leetcode·字符串·哈希表·计数
小刘|2 小时前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法