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