/**
* 查找散点凸边界算法
* 参考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