Files
AFFiNE-Mirror/blocksuite/framework/global/src/gfx/polyline.ts

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;
}
}