86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
import { Vector2 } from "../Vector2"
|
|
|
|
interface P {
|
|
x: number
|
|
y: number
|
|
}
|
|
|
|
export interface IOffset {
|
|
negativeOffset: number
|
|
positiveOffset: number
|
|
}
|
|
|
|
/** 点p到线段P1P2 的最短距离的平方,线段不延伸 */
|
|
function GetSqSegDist(p: P, p1: P, p2: P): number {
|
|
let x = p1.x
|
|
let y = p1.y
|
|
let dx = p2.x - x
|
|
let dy = p2.y - y
|
|
|
|
if (dx !== 0 || dy !== 0)// 不是0长度线
|
|
{
|
|
const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy)
|
|
if (t > 1) {
|
|
x = p2.x
|
|
y = p2.y
|
|
}
|
|
else if (t > 0) {
|
|
x += dx * t
|
|
y += dy * t
|
|
}
|
|
}
|
|
dx = p.x - x
|
|
dy = p.y - y
|
|
return dx * dx + dy * dy
|
|
}
|
|
|
|
function CrossVector2(a: P, b: P)
|
|
{
|
|
return a.x * b.y - a.y * b.x
|
|
}
|
|
|
|
// Ramer-Douglas-Peucker algorithm
|
|
function SimplifyDPStep(points: P[], first: number, last: number, sqTolerance: number, simplified: P[], offset: IOffset): void {
|
|
let maxSqDist = 0
|
|
let index: number
|
|
const fp = points[first]
|
|
const lp = points[last]
|
|
|
|
for (let i = first + 1; i < last; i++) {
|
|
const p = points[i]
|
|
const sqDist = GetSqSegDist(p, fp, lp)
|
|
if (sqDist > maxSqDist) {
|
|
index = i
|
|
maxSqDist = sqDist
|
|
}
|
|
}
|
|
|
|
if (maxSqDist > sqTolerance) {
|
|
if (index - first > 1)
|
|
SimplifyDPStep(points, first, index, sqTolerance, simplified, offset)
|
|
simplified.push(points[index])
|
|
if (last - index > 1)
|
|
SimplifyDPStep(points, index, last, sqTolerance, simplified, offset)
|
|
}
|
|
else {
|
|
// 记录偏移
|
|
const v = new Vector2(lp.x - fp.x, lp.y - fp.y).normalize()
|
|
for (let i = first + 1; i < last; i++) {
|
|
const p = points[i]
|
|
const offsetDist = -CrossVector2(v, { x: p.x - fp.x, y: p.y - fp.y })
|
|
offset.positiveOffset = Math.max(offset.positiveOffset, offsetDist)
|
|
offset.negativeOffset = Math.min(offset.negativeOffset, offsetDist)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ramer-Douglas-Peucker 算法
|
|
export function SimplifyDouglasPeucker(points: P[], sqTolerance: number): [P[], IOffset] {
|
|
const last = points.length - 1
|
|
const simplified: P[] = [points[0]]
|
|
const offset: IOffset = { negativeOffset: 0, positiveOffset: 0 }
|
|
SimplifyDPStep(points, 0, last, sqTolerance, simplified, offset)
|
|
simplified.push(points[last])
|
|
return [simplified, offset]
|
|
}
|