javascript 凸边查找边界点

/**
 * 查找散点凸边界算法
 * 参考https://blog.csdn.net/manerfan/article/details/46610683
 */
let FindBound = {
  // 设置轴字符串
  setAxis(x = 'x', y = 'y') {
    this.x = x
    this.y = y
  },
  // 比较两个对象
  objEqual(s, o) {
    let skeys = Object.keys(s),
      tkeys = Object.keys(o)
    if (skeys.length != tkeys.length) return false
    for (let i = 0; i < tkeys.length; i++) {
      if (s[tkeys[i]] != o[tkeys[i]]) return false
    }
    return true
  },
  // 获取起点
  getFirstPoint(ps) {
    let tag = ps[0]
    ps.forEach(x => {
      if (this.objEqual(x, tag)) return
      if (x[this.y] > tag[this.y] || (x[this.y] == tag[this.y] && x[this.x] < tag[this.x])) tag = x
    })
    return tag
  },
  // 计算两点长度
  calLineLen(pa, pb) {
    // 如果相同或有一个为空返回0
    if (!pa || !pb || this.objEqual(pa, pb)) return 0.0
    let la = Math.abs(pa[this.x] - pb[this.x]), // 直角三角形的直边a
      lb = Math.abs(pa[this.y] - pb[this.y]), // 直角三角形的直边b
      min = Math.min(la, lb), // 短直边
      max = Math.max(la, lb), // 长直边
      inner = min / max
    /**
     * 为防止计算平方时float溢出,做如下转换
     * √(min²+max²) = √((min/max)²+1) * abs(max)
     */
    return Math.sqrt(inner * inner + 1.0) * max
  },
  // 计算夹角
  calAngle(pa, pb) {
    let lineLen = this.calLineLen(pa, pb)
    if (lineLen <= 0) return 0
    let la = pa[this.x] - pb[this.x], // 直角三角形的直边a
      lb = pa[this.y] - pb[this.y] // 直角三角形的直边b
    // 根据象限返回
    return lb >= 0 ? Math.acos(la / lineLen) : Math.acos(-la / lineLen) + Math.PI
  },
  // 修正夹角
  reviseAngle(angle) {
    while (angle < 0) {
      angle += 2 * Math.PI
    }
    while (angle >= 2 * Math.PI) {
      angle -= 2 * Math.PI
    }
    return angle
  },
  // 查找边界
  getConvexPoints(ps) {
    let cur = this.getFirstPoint(ps),
      minAngle,
      oldAngle = 2 * Math.PI,
      first = { ...cur },
      points = []
    // 结束循环时,对象已有founded
    first['founded'] = true
    do {
      points.push(cur)
      minAngle = 2 * Math.PI
      let nextPoint = cur,
        nextAngle = oldAngle
      for (let n = 0; n < ps.length; n++) {
        let p = ps[n]
        if (p.founded || this.objEqual(p, cur)) continue
        let cAngle = this.calAngle(cur, p)
        let dAngle = this.reviseAngle(oldAngle - cAngle)
        if (dAngle < minAngle) {
          minAngle = dAngle
          nextPoint = p
          nextAngle = cAngle
        }
      }
      oldAngle = nextAngle
      cur = nextPoint
      cur['founded'] = true
    } while (!this.objEqual(cur, first))
    return points
  }
}
// export default FindBound

 

posted @ 2019-11-06 10:44  山城炮灰  阅读(327)  评论(0)    收藏  举报