@antv/util
Version:
> AntV 底层依赖的工具库,不建议在自己业务中使用。
112 lines (100 loc) • 2.49 kB
text/typescript
import type { LengthFactory, PathLengthFactoryOptions } from '../types';
import { distanceSquareRoot } from './distance-square-root';
/**
* Returns a {x,y} point at a given length, the total length and
* the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
*/
function getPointAtCubicSegmentLength(
x1: number,
y1: number,
c1x: number,
c1y: number,
c2x: number,
c2y: number,
x2: number,
y2: number,
t: number,
) {
const t1 = 1 - t;
return {
x: t1 ** 3 * x1 + 3 * t1 ** 2 * t * c1x + 3 * t1 * t ** 2 * c2x + t ** 3 * x2,
y: t1 ** 3 * y1 + 3 * t1 ** 2 * t * c1y + 3 * t1 * t ** 2 * c2y + t ** 3 * y2,
};
}
/**
* Returns the length of a C (cubic-bezier) segment
* or an {x,y} point at a given length.
*/
export function segmentCubicFactory(
x1: number,
y1: number,
c1x: number,
c1y: number,
c2x: number,
c2y: number,
x2: number,
y2: number,
distance: number,
options: Partial<PathLengthFactoryOptions>,
): LengthFactory {
const { bbox = true, length = true, sampleSize = 10 } = options;
const distanceIsNumber = typeof distance === 'number';
let x = x1;
let y = y1;
let LENGTH = 0;
let prev = [x, y, LENGTH];
let cur: [number, number] = [x, y];
let t = 0;
let POINT = { x: 0, y: 0 };
const POINTS = [{ x, y }];
if (distanceIsNumber && distance <= 0) {
POINT = { x, y };
}
// bad perf when size = 300
for (let j = 0; j <= sampleSize; j += 1) {
t = j / sampleSize;
({ x, y } = getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t));
if (bbox) {
POINTS.push({ x, y });
}
if (length) {
LENGTH += distanceSquareRoot(cur, [x, y]);
}
cur = [x, y];
if (distanceIsNumber && LENGTH >= distance && distance > prev[2]) {
const dv = (LENGTH - distance) / (LENGTH - prev[2]);
POINT = {
x: cur[0] * (1 - dv) + prev[0] * dv,
y: cur[1] * (1 - dv) + prev[1] * dv,
};
}
prev = [x, y, LENGTH];
}
if (distanceIsNumber && distance >= LENGTH) {
POINT = { x: x2, y: y2 };
}
return {
length: LENGTH,
point: POINT,
min: {
x: Math.min.apply(
null,
POINTS.map((n) => n.x),
),
y: Math.min.apply(
null,
POINTS.map((n) => n.y),
),
},
max: {
x: Math.max.apply(
null,
POINTS.map((n) => n.x),
),
y: Math.max.apply(
null,
POINTS.map((n) => n.y),
),
},
};
}