UNPKG

@davepagurek/flo-mat

Version:

Medial / Scale Axis Transform (MAT/SAT) Library.

99 lines (74 loc) 2.65 kB
import { evalDeCasteljau } from "flo-bezier3"; import { dot, lineLineIntersection, squaredDistanceBetween } from "flo-vector2d"; import { BezierPiece } from '../mat/bezier-piece.js'; /** * @internal * * Reduces the circle radius initially as an optimization step. */ function reduceRadius( extreme: number, bezierPieces: BezierPiece[], p: number[], x: number[]) { const TOLERANCE = 1 + 2**-10; let prevP: number[] | undefined = undefined; let minRadius = Number.POSITIVE_INFINITY; for (let i=0; i<bezierPieces.length; i++) { const bezierPiece = bezierPieces[i]; const ps = bezierPiece.curve.ps; let r1 = Number.POSITIVE_INFINITY; const p1 = evalDeCasteljau(ps, bezierPiece.ts[0]); // Prevent evaluating the same points twice if (!prevP || prevP[0] !== p1[0] || prevP[1] !== p1[1]) { const cc1 = getCircleCenterFrom2PointsAndNormal(extreme, p, x, p1); if (cc1) { r1 = squaredDistanceBetween(p, cc1); } } let r2 = Number.POSITIVE_INFINITY; const p2 = evalDeCasteljau(ps, bezierPiece.ts[1]); const cc2 = getCircleCenterFrom2PointsAndNormal(extreme, p, x, p2); if (cc2) { r2 = squaredDistanceBetween(p, cc2); } prevP = p2; const d = Math.min(r1, r2); if (d < minRadius) { minRadius = d; } } // The extra bit is to account for floating point precision. return TOLERANCE*minRadius; } /** * @internal * @param p A point on the circle with normal pointing to x towards the center * of the circle. * @param x * @param p1 Another point on the circle. */ function getCircleCenterFrom2PointsAndNormal( extreme: number, p: number[], x: number[], p1: number[]) { const TOLERANCE = (2**-14*extreme)**2; // Ignore if p and p1 are too close together if (squaredDistanceBetween(p,p1) < TOLERANCE) { return undefined; } /** The perpindicular bisector between the two given points on the circle */ const pb = [ (p[0] + p1[0]) / 2, (p[1] + p1[1]) / 2, ]; const tan = [p1[0] - p[0], p1[1] - p[1]]; const norm = [-tan[1], tan[0]]; // Rotate by 90 degrees const pb2 = [pb[0] + norm[0], pb[1] + norm[1]]; const res = lineLineIntersection([p,x], [pb, pb2]); if (!res) { return undefined; } const resO = [res[0] - p[0], res[1] - p[1]]; const xO = [x[0] - p[0], x[1] - p[1]]; if (dot(resO, xO) < 0) { return undefined; } return res; } export { reduceRadius }