56 lines
2.9 KiB
JavaScript
56 lines
2.9 KiB
JavaScript
export const POLYGON = 'polygon'
|
|
export const DIRECTION_LINE = 'direction_line'
|
|
|
|
export function clamp(value) {
|
|
return Math.min(1, Math.max(0, Number(value)))
|
|
}
|
|
|
|
export function geometryError(kind, points, direction = '') {
|
|
if (!Array.isArray(points)) return '请在画面中添加坐标点'
|
|
if (kind === POLYGON && (points.length < 3 || points.length > 64)) return '多边形需要 3 到 64 个点'
|
|
if (kind === DIRECTION_LINE && points.length !== 2) return '方向警戒线需要恰好 2 个点'
|
|
if (kind === DIRECTION_LINE && !['forward', 'reverse'].includes(direction)) return '请选择警戒方向'
|
|
if (![POLYGON, DIRECTION_LINE].includes(kind)) return '请选择配置类型'
|
|
if (points.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y) || point.x < 0 || point.x > 1 || point.y < 0 || point.y > 1)) return '坐标必须位于画面范围内'
|
|
if (kind === POLYGON && selfIntersects(points)) return '多边形边线不能交叉,请调整顶点'
|
|
if (kind === POLYGON && Math.abs(polygonArea(points)) < 0.000001) return '多边形面积过小,请重新绘制'
|
|
if (kind === DIRECTION_LINE && samePoint(points[0], points[1])) return '警戒线起点和终点不能重合'
|
|
return ''
|
|
}
|
|
|
|
function samePoint(a, b) {
|
|
return Math.abs(a.x - b.x) < 0.0000001 && Math.abs(a.y - b.y) < 0.0000001
|
|
}
|
|
|
|
function polygonArea(points) {
|
|
return points.reduce((total, point, index) => {
|
|
const next = points[(index + 1) % points.length]
|
|
return total + point.x * next.y - next.x * point.y
|
|
}, 0) / 2
|
|
}
|
|
|
|
function selfIntersects(points) {
|
|
for (let first = 0; first < points.length; first += 1) {
|
|
const a = points[first]
|
|
const b = points[(first + 1) % points.length]
|
|
for (let second = first + 1; second < points.length; second += 1) {
|
|
if (second === first || second === (first + 1) % points.length || first === (second + 1) % points.length) continue
|
|
const c = points[second]
|
|
const d = points[(second + 1) % points.length]
|
|
if (segmentsIntersect(a, b, c, d)) return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
function segmentsIntersect(a, b, c, d) {
|
|
const orientation = (p, q, r) => (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
|
|
const values = [orientation(a, b, c), orientation(a, b, d), orientation(c, d, a), orientation(c, d, b)]
|
|
if (((values[0] > 0 && values[1] < 0) || (values[0] < 0 && values[1] > 0)) && ((values[2] > 0 && values[3] < 0) || (values[2] < 0 && values[3] > 0))) return true
|
|
const onSegment = (p, q, r) => q.x <= Math.max(p.x, r.x) + 0.0000001 && q.x >= Math.min(p.x, r.x) - 0.0000001 && q.y <= Math.max(p.y, r.y) + 0.0000001 && q.y >= Math.min(p.y, r.y) - 0.0000001
|
|
return (Math.abs(values[0]) < 0.0000001 && onSegment(a, c, b)) ||
|
|
(Math.abs(values[1]) < 0.0000001 && onSegment(a, d, b)) ||
|
|
(Math.abs(values[2]) < 0.0000001 && onSegment(c, a, d)) ||
|
|
(Math.abs(values[3]) < 0.0000001 && onSegment(c, b, d))
|
|
}
|