@davepagurek/flo-mat
Version:
Medial / Scale Axis Transform (MAT/SAT) Library.
98 lines (77 loc) • 2.83 kB
text/typescript
import { hausdorffDistance } from 'flo-bezier3';
import { CpNode, getCpNodesOnCircle, isTerminating, removeCpNode } from '../cp-node/cp-node.js';
import { getBranches } from './get-branches.js';
import { getCurveToNext } from '../cp-node/get-curve-to-next.js';
import { getCurveBetween } from '../get-curve/get-curve-between.js';
import { Mat } from './mat.js';
/**
* Simplifies the given MAT by replacing the piecewise quad beziers composing
* the MAT with fewer ones to within a given tolerance.
* @param cpNode A representation of the MAT
* @param anlgeTolerance Tolerance given as the degrees difference of the unit
* direction vectors at the interface between curves. A tolerance of zero means
* perfect smoothness is required - defaults to 15.
* @param hausdorffTolerance The approximate maximum Hausdorff Distance tolerance -
* defaults to `2**-3`
* @param maxIterations The max iterations, defaults to `50`
*/
function simplifyMat(
mat: Mat,
anlgeTolerance = 15,
hausdorffTolerance = 2**-3,
maxIterations = 50): Mat {
let cpNode = mat.cpNode;
// Start from a leaf
while (!isTerminating(cpNode)) { cpNode = cpNode.next; }
const branches = getBranches(cpNode, anlgeTolerance);
const canDeletes: CpNode[] = [];
for (let k=0; k<branches.length; k++) {
const branch = branches[k];
// Try to remove some
let j = 0;
while (j < branch.length) {
const i = j;
while (true) {
j++;
if (j === branch.length) { break; }
const hd = getTotalHausdorffDistance(i, j, branch, maxIterations);
if (hd > hausdorffTolerance) {
break;
} else {
canDeletes.push(branch[j]);
}
}
if (i+1 === j) {
// no simplification occured
break;
}
}
}
for (const cpNode of canDeletes) {
const isTerminating_ = isTerminating(cpNode);
const onCircleCount = getCpNodesOnCircle(cpNode).length;
if (isTerminating_ || onCircleCount !== 2) {
continue;
}
removeCpNode(cpNode);
}
//return { cpNode, cpTrees: createNewCpTree(cpNode) };
return { cpNode, cpTrees: undefined! };
}
function getTotalHausdorffDistance(
i: number,
j: number,
branch: CpNode[],
hausdorffSpacing: number) {
const hds: number[] = [];
const longCurve = getCurveBetween(branch[i], branch[j].next);
for (; i<j+1; i++) {
hds.push(hausdorffDistance(
getCurveToNext(branch[i]),
longCurve,
hausdorffSpacing
));
}
return Math.max(...hds);
}
export { simplifyMat }