| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import line from './line';
- import { distance, isNumberEqual, getBBoxByArray, piMod } from './util';
- import { nearestPoint } from './bezier';
- import { Point } from './types';
- // 差值公式
- function quadraticAt(p0: number, p1: number, p2: number, t: number) {
- const onet = 1 - t;
- return onet * onet * p0 + 2 * t * onet * p1 + t * t * p2;
- }
- // 求极值
- function extrema(p0: number, p1: number, p2: number) {
- const a = p0 + p2 - 2 * p1;
- if (isNumberEqual(a, 0)) {
- return [0.5];
- }
- const rst = (p0 - p1) / a;
- if (rst <= 1 && rst >= 0) {
- return [rst];
- }
- return [];
- }
- function derivativeAt(p0: number, p1: number, p2: number, t: number) {
- return 2 * (1 - t) * (p1 - p0) + 2 * t * (p2 - p1);
- }
- // 分割贝塞尔曲线
- function divideQuadratic(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, t: number) {
- // 划分点
- const xt = quadraticAt(x1, x2, x3, t);
- const yt = quadraticAt(y1, y2, y3, t);
- // 分割的第一条曲线的控制点
- const controlPoint1 = line.pointAt(x1, y1, x2, y2, t);
- // 分割的第二条曲线的控制点
- const controlPoint2 = line.pointAt(x2, y2, x3, y3, t);
- return [
- [x1, y1, controlPoint1.x, controlPoint1.y, xt, yt],
- [xt, yt, controlPoint2.x, controlPoint2.y, x3, y3],
- ];
- }
- // 使用迭代法取贝塞尔曲线的长度
- function quadraticLength(
- x1: number,
- y1: number,
- x2: number,
- y2: number,
- x3: number,
- y3: number,
- iterationCount: number
- ) {
- if (iterationCount === 0) {
- return (distance(x1, y1, x2, y2) + distance(x2, y2, x3, y3) + distance(x1, y1, x3, y3)) / 2;
- }
- const quadratics = divideQuadratic(x1, y1, x2, y2, x3, y3, 0.5);
- const left = quadratics[0];
- const right = quadratics[1];
- left.push(iterationCount - 1);
- right.push(iterationCount - 1);
- return quadraticLength.apply(null, left) + quadraticLength.apply(null, right);
- }
- export default {
- box(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
- const xExtrema = extrema(x1, x2, x3)[0];
- const yExtrema = extrema(y1, y2, y3)[0];
- // 控制点不加入 box 的计算
- const xArr = [x1, x3];
- const yArr = [y1, y3];
- if (xExtrema !== undefined) {
- xArr.push(quadraticAt(x1, x2, x3, xExtrema));
- }
- if (yExtrema !== undefined) {
- yArr.push(quadraticAt(y1, y2, y3, yExtrema));
- }
- return getBBoxByArray(xArr, yArr);
- },
- length(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
- return quadraticLength(x1, y1, x2, y2, x3, y3, 3);
- },
- nearestPoint(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x0: number, y0: number) {
- return nearestPoint([x1, x2, x3], [y1, y2, y3], x0, y0, quadraticAt);
- },
- pointDistance(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x0: number, y0: number) {
- const point = this.nearestPoint(x1, y1, x2, y2, x3, y3, x0, y0);
- return distance(point.x, point.y, x0, y0);
- },
- interpolationAt: quadraticAt,
- pointAt(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, t: number): Point {
- return {
- x: quadraticAt(x1, x2, x3, t),
- y: quadraticAt(y1, y2, y3, t),
- };
- },
- divide(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, t: number) {
- return divideQuadratic(x1, y1, x2, y2, x3, y3, t);
- },
- tangentAngle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, t: number) {
- const dx = derivativeAt(x1, x2, x3, t);
- const dy = derivativeAt(y1, y2, y3, t);
- const angle = Math.atan2(dy, dx);
- return piMod(angle);
- },
- };
|