import type { CpNode } from '../cp-node.js'; import { fromTo as fromToVec, dot, cross, rotate90Degrees, interpolate, translate, lineLineIntersection } from 'flo-vector2d'; import { getEdgeDirection } from './get-edge-direction.js'; const { abs } = Math; /** @internal */ const TOLERANCE_ADD_2PRONG = 0.01; /** @internal */ const TOLERANCE_USE_LINE = 0.0001; // else cubic /** * Returns the bezier curve from the maximal disk of one `CpNode` to another * `CpNode`'s maximal disk. * @param cpNodeFrom * @param cpNodeTo */ function getMatCurveBetween( cpNodeFrom: CpNode, cpNodeTo: CpNode): number[][] { const fromCc = cpNodeFrom.pointOnShape.circle.center; const fromL = getEdgeDirection(cpNodeFrom); const toCc = cpNodeTo.pointOnShape.circle.center; const toL = getEdgeDirection(cpNodeTo.prevOnCircle); const mid = lineLineIntersection(fromL, toL); const c = fromToVec(fromCc,toCc); let twisted: boolean; if (!mid) { twisted = true; } else { const a = fromToVec(fromCc, mid); const b = fromToVec(toCc, mid); twisted = dot(a,c) < 0 || dot(b,c) > 0; } if (!twisted) { return [fromCc, mid!, toCc]; } const r = rotate90Degrees(c); const w1 = fromToVec(fromL[0], fromL[1]); // This is a unit vector const w2 = fromToVec(toL[0], toL[1]); // This is a unit vector const d1 = abs(cross(c, w1)) / (3*3); const d2 = abs(cross(c, w2)) / (3*3); if (d1 > TOLERANCE_ADD_2PRONG || d2 > TOLERANCE_ADD_2PRONG) { // FUTURE - not within tolerance - must add additional 2-prong return [fromCc, toCc]; } if (d1 > TOLERANCE_USE_LINE || d2 > TOLERANCE_USE_LINE) { // approximate with cubic bezier const m1 = interpolate(fromCc,toCc,1/3); const m2 = interpolate(fromCc,toCc,2/3); const v1 = translate(r, m1); const v2 = translate(r, m2); const l1 = [m1,v1]; const l2 = [m2,v2]; const mid1 = lineLineIntersection(fromL, l1)!; const mid2 = lineLineIntersection(toL, l2)!; return [fromCc, mid1, mid2, toCc]; } // Within tolerance - approximate with a straight line. return [fromCc, toCc]; } export { getMatCurveBetween }