[迴轉數法]判斷一個點是否在多邊形內部

射線法是一種很簡單直觀的判斷平面內點是否在多邊形內的方法。除了射線法還有很多其他的方法,今天就再介紹一種通過迴轉數來判斷的方法。

平面中的閉合曲線關於一個點的迴轉數(又叫卷繞數),代表了曲線繞過該點的總次數。下面這張圖動態演示了迴轉數的概念:圖中紅色曲線關於點(人所在位置)的迴轉數爲 2。

enter image description here

迴轉數是拓撲學中的一個基本概念,具有很重要的性質和用途。本文並不打算在這一點上展開論述,這需要具備相當的數學知識,否則會非常乏味和難以理解。我們暫時只需要記住迴轉數的一個特性就行了:

當迴轉數爲 0 時,點在閉合曲線外部。

對於給定的點和多邊形,迴轉數應該怎麼計算呢?

  1. 用線段分別連接點和多邊形的全部頂點。

    enter image description here

  2. 計算所有點與相鄰頂點連線的夾角。

    enter image description here

  3. 計算所有夾角和。注意每個夾角都是有方向的,所以有可能是負值。

    enter image description here

  4. 最後根據角度累加值計算迴轉數。看過本文開頭的介紹,很容易理解 360°(2π)相當於一次迴轉。

思路介紹完了,下面兩點是實現中需要留意的問題。

  1. JavaScript 的數只有 64 位雙精度浮點這一種。對於三角函數產生的無理數,浮點數計算不可避免會造成一些誤差,因此在最後計算迴轉數需要做取整操作。

  2. 通常情況下,平面直角座標系內一個角的取值範圍是 -π 到 π 這個區間,這也是 JavaScript 三角函數 Math.atan2() 返回值的範圍。但 JavaScript 並不能直接計算任意兩條線的夾角,我們只能先計算兩條線與 X 正軸夾角,再取兩者差值。這個差值的結果就有可能超出 -π 到 π 這個區間,因此我們還需要處理差值超出取值區間的情況。

老規矩,上代碼。

/**
   * @description 迴轉數法判斷點是否在多邊形內部
   * @param {Object} p 待判斷的點,格式:{ x: X座標, y: Y座標 }
   * @param {Array} poly 多邊形頂點,數組成員的格式同 p
   * @return {String} 點 p 和多邊形 poly 的幾何關係
   */
  function windingNumber(p, poly) {
    var px = p.x,
        py = p.y,
        sum = 0

    for(var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
      var sx = poly[i].x,
          sy = poly[i].y,
          tx = poly[j].x,
          ty = poly[j].y

      // 點與多邊形頂點重合或在多邊形的邊上
      if((sx - px) * (px - tx) >= 0 && (sy - py) * (py - ty) >= 0 && (px - sx) * (ty - sy) === (py - sy) * (tx - sx)) {
        return 'on'
      }

      // 點與相鄰頂點連線的夾角
      var angle = Math.atan2(sy - py, sx - px) - Math.atan2(ty - py, tx - px)

      // 確保夾角不超出取值範圍(-π 到 π)
      if(angle >= Math.PI) {
        angle = angle - Math.PI * 2
      } else if(angle <= -Math.PI) {
        angle = angle + Math.PI * 2
      }

      sum += angle
    }

    // 計算迴轉數並判斷點和多邊形的幾何關係
    return Math.round(sum / Math.PI) === 0 ? 'out' : 'in'
  }

轉自“前端亂燉”http://www.html-js.com/article/1538

發佈了34 篇原創文章 · 獲贊 39 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章