import { beziersToSvgPathStr, simplifyPaths } from 'flo-boolean'; import { getLoopsMetrics } from '../loop/get-max-coordinate.js'; import { Mat } from '../mat/mat.js'; import { simplifyMat } from '../mat/simplify-mat.js'; import { toScaleAxis } from '../sat/to-scale-axis.js'; import { clipOptions } from './clip-options.js'; import { findMat } from './find-mat.js'; import { defaultMatOptions, MatOptions } from './mat-options.js'; const { ceil, log2 } = Math; /** * Finds and returns the Medial Axis Transforms (MATs) from the given array of * bezier loops representing shape boundaries. * * @param bezierLoops An array of (possibly intersecting) loops with each loop * representing one or more piecewise smooth closed curves (i.e. shapes). Each * loop consists of an array of beziers represented by an array of control * points with 2,3 or 4 elements corresponding to linear, quadratic and cubic * beziers respectively. Each point is a two-element array (ordered pair), the * first of which is the x-coordinate and the second the y-coordinate. * * @param maxCurviness The maximum value the 'curviness' of a curve can have * before an additional MAT point is inserted in between. Defaults to 0.4. * (Curviness is measured as the total angle in radians between the consecutive * vectors formed by the ordered control points of th bezier curve). The value * is clipped in the range `[0.01,3]`. * * @param maxLength The maximum length a curve can have before an additional MAT * point is inserted. This value is scaled to a reference 1024 x 1024 * grid (e.g. if the shape fits in a 512 x 512 axis-aligned box the value will be * halved, e.g. from 10 to 5). Together with maxCurviness it represents a * tolerance for the accuracy of the MAT. Defaults to 4. The value is clipped * in [1,100]. */ function findMats( bezierLoops: number[][][][], options?: MatOptions): Mat[] { // if (typeof _debug_ !== 'undefined') { var timingStart = performance.now(); } const { maxCoordinate } = getLoopsMetrics(bezierLoops); const maxCoordPowerOf2 = ceil(log2(maxCoordinate)); const options_: Required = clipOptions( maxCoordPowerOf2, { ...defaultMatOptions, ...(options || {}) }); const { applySat, simplify, satScale, simplifyTolerance } = options_; const loopss = simplifyPaths(bezierLoops, { forceOrientationNegative: true, }); // console.log(loopsToSvgPathStr(bezierLoops)); const mats: Mat[] = []; for (const loops of loopss) { let mat = findMat(loops, maxCoordPowerOf2, options_); if (mat === undefined) { continue; } if (applySat) { mat = toScaleAxis(mat, satScale); } if (simplify) { mat = simplifyMat(mat, simplifyTolerance, 50) } mats.push(mat); } // console.log(structuredClone(circumCenter.getStats())); // circumCenter.resetStats(); return mats; } /** * Returns an SVG path string representation of the given bezier loops. * @param loops An array of loops having an array of bezier curves each given as * an array of control points. */ function loopsToSvgPathStr( loops: number[][][][]) { let str = ''; for (const loop of loops) { str = str + beziersToSvgPathStr(loop) + '\n'; } return str; } export { findMats }