成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

幾何算法:判斷兩條線段是否相交

開發 前端
判斷兩條線段是否相交,可以判斷兩條線段的兩端點是否分別在各自的兩側,對應地需要用到二維向量叉乘結果的正負值代表向量旋轉方向的特性。

大家好,我是前端西瓜哥。

如何判斷兩條線段(注意不是直線)是否有交點?

傳統幾何算法的局限

上過一點學的西瓜哥我,只用高中學過的知識,還是可以解這個問題的。

一條線段兩個點,可以列出一個兩點式(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1)),兩條線段是兩個兩點式,這樣就是 二元一次方程組 了 ,就能求出兩條直線的交點。

然后判斷這個點是否在其中一條線段上。如果在,說明兩線段相交,否則不相交。

看起來不錯,但這里要考慮直線垂直或水平于坐標軸的特殊情況,還有兩條直線平行導致沒有唯一解的情況,除數不能為 0 的情況。

特殊情況實在是太多了,能用是能用,但不好用。

那么,有其他的更好的解法嗎?

有的,叉乘。

叉乘是什么?

叉乘(cross product)是線性代數的一個概念,也叫外積、叉積、向量積,是在三維空間中兩個向量的二元運算的結果,該結果為一個向量。

但那是嚴格意義上的。實際也可以用在二維空間的二維向量中,不過此時它們的叉乘結果變成了標量。

假設向量 A 為 (x1, y1),向量 B 為 (x2, y2),則叉乘 AxB 的結果為 x1 * y2 - x2 * y1。

(注意叉乘不滿足交換律)

在幾何意義上,這個叉乘結果的絕對值對應兩個向量組成的平行四邊形的面積。

此外可通過符號判斷向量 A 變成向量 B 的旋轉方向。

如果叉乘為正數,說明 A 變成 B 需要逆時針旋轉(旋轉角度小于 180 度);

如果為負數,說明 A 到 B 需要順時針旋轉;

如果為 0,說明兩個向量平行(或重合)。

叉乘解法的原理

回到題目本身。

假設線段 1 的端點為 A 和 B,線段 2 的端點為 C 和 D。

我們可以換另一個角度去解,即判斷線段 1 的兩個端點是否在線段 2 的兩邊,然后再反過來比線段 2 的兩點是否線段 1 的兩邊。

這里我們可以利用上面 叉乘的正負代表旋轉方向的特性。

以上圖為例, AB 向量到 AD 向量位置需要逆時針旋轉,AB 向量到 AC 向量則需要順時針,代表 C 和 D 在 AB 的兩側,對應就是兩個叉乘相乘為負數。

function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

const [a, b] = seg1;
const [c, d] = seg2;

// d1 的符號表示 AB 旋轉到 AC 的旋轉方向
const d1 = crossProduct(a, b, c);

只是判斷了 C 和 D 在 AB 線段的兩側還不行,因為可能還有下面這種情況。

所以我們還要再判斷一下,A 和 B 是否在 CD 線的的兩側。計算過程同上,這里不贅述。

一般實現

type Point = [number, number];

function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

function isSegmentIntersect(
  seg1: [Point, Point],
  seg2: [Point, Point],
): boolean {
  const [a, b] = seg1;
  const [c, d] = seg2;

  const d1 = crossProduct(a, b, c);
  const d2 = crossProduct(a, b, d);
  const d3 = crossProduct(c, d, a);
  const d4 = crossProduct(c, d, b);

  return d1 * d2 < 0 && d3 * d4 < 0;
}

// 測試
const seg1: [Point, Point] = [
  [0, 0],
  [1, 1],
];
const seg2: [Point, Point] = [
  [0, 1],
  [1, 0],
];

console.log(isSegmentIntersect(seg1, seg2)); // true

注意,這個算法認為線段的端點剛好在另一條線段上的情況,不屬于相交。

考慮點在線段上或重合

如果你需要考慮線段的端點剛好在另一條線段上的情況,需要額外在叉乘為 0 的情況下,再判斷一下線段 1 的端點是否在另一個線段的 x  和 y 范圍內。

對應的算法實現:

type Point = [number, number];

function crossProduct(p1: Point, p2: Point, p3: Point): number {
  const x1 = p2[0] - p1[0];
  const y1 = p2[1] - p1[1];
  const x2 = p3[0] - p1[0];
  const y2 = p3[1] - p1[1];
  return x1 * y2 - x2 * y1;
}

function onSegment(p: Point, seg: [Point, Point]): boolean {
  const [a, b] = seg;
  const [x, y] = p;
  return (
    x >= Math.min(a[0], b[0]) &&
    x <= Math.max(a[0], b[0]) &&
    y >= Math.min(a[1], b[1]) &&
    y <= Math.max(a[1], b[1])
  );
}

function isSegmentIntersect(
  seg1: [Point, Point],
  seg2: [Point, Point],
): boolean {
  const [a, b] = seg1;
  const [c, d] = seg2;

  const d1 = crossProduct(a, b, c);
  const d2 = crossProduct(a, b, d);
  const d3 = crossProduct(c, d, a);
  const d4 = crossProduct(c, d, b);

  if (d1 * d2 < 0 && d3 * d4 < 0) {
    return true;
  }
 
  // d1 為 0 表示 C 點在 AB 所在的直線上
  // 接著會用 onSegment 再判斷這個 C 是不是在 AB 的 x 和 y 的范圍內
  if (d1 === 0 && onSegment(c, seg1)) return true;
  if (d2 === 0 && onSegment(d, seg1)) return true;
  if (d3 === 0 && onSegment(a, seg2)) return true;
  if (d4 === 0 && onSegment(b, seg2)) return true;

  return false;
}

// 測試
const seg1: [Point, Point] = [
  [0, 0],
  [1, 1],
];
const seg2: [Point, Point] = [
  [0, 1],
  [1, 0],
];
const seg3: [Point, Point] = [
  [0, 0],
  [2, 2],
];
const seg4: [Point, Point] = [
  [1, 1],
  [1, 0],
];
// 普通相交情況
console.log(isSegmentIntersect(seg1, seg2)); //  true
// 線段 1 的一個端點剛好在線段 2 上
console.log(isSegmentIntersect(seg3, seg4)); // true

結尾

總結一下,判斷兩條線段是否相交,可以判斷兩條線段的兩端點是否分別在各自的兩側,對應地需要用到二維向量叉乘結果的正負值代表向量旋轉方向的特性。

責任編輯:姜華 來源: 前端西瓜哥
相關推薦

2023-11-08 08:09:36

幾何算法解析幾何

2012-12-20 10:19:08

華為路由器接入設置

2023-04-05 14:31:19

Java計算移動

2009-06-19 15:25:13

ITSMNSM運維管理

2014-10-24 15:17:07

Android

2014-12-24 09:15:54

PaaS開源云服務

2019-04-04 13:36:25

云計算互聯網云廠商

2024-12-27 00:00:00

SQL死鎖數據庫

2011-06-07 11:21:34

路由負載

2012-05-11 13:15:12

戴爾虛擬化

2019-11-06 15:16:12

16GB8GB內存

2020-10-16 08:09:58

算法代碼字符串

2010-01-12 18:05:56

Linux Redha

2016-08-18 09:53:33

軟件定義存儲

2022-06-06 23:22:44

互聯網產品模式

2022-08-11 13:11:48

斯坦福大學英偉達VR 頭顯

2017-01-11 15:45:52

中國聯通光纜亞歐5號

2009-03-06 12:17:24

IBMPowerSystemPower6

2021-12-08 09:00:25

LeetCode容器算法

2018-06-06 00:23:35

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级看片免费视频囗交动图 | 99久久久无码国产精品 | 国产精品一区三区 | 亚洲精品片| 国产 日韩 欧美 中文 在线播放 | 99精品欧美一区二区三区综合在线 | 亚洲第1页 | 97人人爱| 精品国产91乱码一区二区三区 | 99久久99 | 国产一二三区免费视频 | 精品国产欧美一区二区 | 不卡av电影在线播放 | 欧美一级黄色免费 | 一区二区三区精品视频 | 亚洲色综合 | 精品一区二区三区在线观看国产 | 国产综合网站 | 一区二区三区四区视频 | 国产男女猛烈无遮掩视频免费网站 | 亚洲一二三区精品 | 欧美久久一级 | 成人免费看片 | 东京久久 | 色男人天堂av | 欧美一级黄色片免费观看 | 最新毛片网站 | 欧美色人 | 精品在线观看一区二区 | 午夜一级黄色片 | 国产精品日韩在线观看一区二区 | 中文字幕免费视频 | 成人小视频在线免费观看 | 久久综合成人精品亚洲另类欧美 | 免费视频一区二区 | 欧美一级在线免费观看 | 国产精品黄色 | 亚洲精品视频在线 | 欧美精品一区二区三区四区 | 亚洲欧美国产一区二区三区 | 欧美狠狠操 |