mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-22 00:37:05 +08:00
137 lines
2.5 KiB
TypeScript
137 lines
2.5 KiB
TypeScript
import { type IVec, Vec } from './model/index.js';
|
|
|
|
export class Polyline {
|
|
static len(points: IVec[]) {
|
|
const n = points.length;
|
|
|
|
if (n < 2) {
|
|
return 0;
|
|
}
|
|
|
|
let i = 0;
|
|
let len = 0;
|
|
let curr: IVec;
|
|
let prev = points[0];
|
|
|
|
while (++i < n) {
|
|
curr = points[i];
|
|
len += Vec.dist(prev, curr);
|
|
prev = curr;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static lenAtPoint(points: IVec[], point: IVec) {
|
|
const n = points.length;
|
|
let len = n;
|
|
|
|
for (let i = 0; i < n - 1; i++) {
|
|
const a = points[i];
|
|
const b = points[i + 1];
|
|
|
|
// start
|
|
if (a[0] === point[0] && a[1] === point[1]) {
|
|
return len;
|
|
}
|
|
|
|
const aa = Vec.angle(a, point);
|
|
const ba = Vec.angle(b, point);
|
|
|
|
if ((aa + ba) % Math.PI === 0) {
|
|
len += Vec.dist(a, point);
|
|
return len;
|
|
}
|
|
|
|
len += Vec.dist(a, b);
|
|
|
|
// end
|
|
if (b[0] === point[0] && b[1] === point[1]) {
|
|
return len;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static nearestPoint(points: IVec[], point: IVec): IVec {
|
|
const n = points.length;
|
|
const r: IVec = [0, 0];
|
|
let len = Infinity;
|
|
|
|
for (let i = 0; i < n - 1; i++) {
|
|
const a = points[i];
|
|
const b = points[i + 1];
|
|
const p = Vec.nearestPointOnLineSegment(a, b, point, true);
|
|
const d = Vec.dist(p, point);
|
|
if (d < len) {
|
|
len = d;
|
|
r[0] = p[0];
|
|
r[1] = p[1];
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static pointAt(points: IVec[], ratio: number) {
|
|
const n = points.length;
|
|
|
|
if (n === 0) {
|
|
return null;
|
|
}
|
|
|
|
if (n === 1) {
|
|
return points[0];
|
|
}
|
|
|
|
if (ratio <= 0) {
|
|
return points[0];
|
|
}
|
|
|
|
if (ratio >= 1) {
|
|
return points[n - 1];
|
|
}
|
|
|
|
const total = Polyline.len(points);
|
|
const len = total * ratio;
|
|
return Polyline.pointAtLen(points, len);
|
|
}
|
|
|
|
static pointAtLen(points: IVec[], len: number): IVec | null {
|
|
const n = points.length;
|
|
|
|
if (n === 0) {
|
|
return null;
|
|
}
|
|
|
|
if (n === 1) {
|
|
return points[0];
|
|
}
|
|
|
|
let fromStart = true;
|
|
if (len < 0) {
|
|
fromStart = false;
|
|
len = -len;
|
|
}
|
|
|
|
let tmp = 0;
|
|
for (let j = 0, k = n - 1; j < k; j++) {
|
|
const i = fromStart ? j : k - 1 - j;
|
|
const a = points[i];
|
|
const b = points[i + 1];
|
|
const d = Vec.dist(a, b);
|
|
|
|
if (len <= tmp + d) {
|
|
const t = ((fromStart ? 1 : -1) * (len - tmp)) / d;
|
|
return Vec.lrp(a, b, t) as IVec;
|
|
}
|
|
|
|
tmp += d;
|
|
}
|
|
|
|
const lastPoint = fromStart ? points[n - 1] : points[0];
|
|
return lastPoint;
|
|
}
|
|
}
|