The importer turns real icons into a rig. It parses d attributes and node lists, fits
subpaths to slots, and tells you how confident it is about each decision.
Two functions do the whole job, both from lib/morph.
importSet takes a list of named icons and returns one object holding two things: the rig
it built, and a report on every decision it had to make along the way. The snippets below
call that object result, and the second half of this chapter is entirely about reading
the report half of it, because that is where the guesses are recorded.
emitRigSource takes the same object and turns it into TypeScript you can commit to your
repository, warnings included as comments.
import { importSet } from "@/lib/morph/compile";
const result = importSet([
{ name: "search", icon: Search },
{ name: "x", icon: X },
// A pack on another canvas is re-gridded once, here, rather than by every consumer.
{ name: "small", icon: "M0 0 15 15", viewBox: 15 },
]);
// Arity is the per-slot maximum across the set. Splitting a cubic is exact, so every
// icon is subdivided UP to it and none is ever approximated.
console.log(result.rig.arity);Accepts a raw d string or a [tag, attrs][] node list. Lucide's shape is the reference
but nothing depends on Lucide. Supported tags are path, line, circle, ellipse,
rect, polyline and polygon.
<g> and transform are rejected rather than ignored, because silently dropping a
transform moves the geometry.
Pass the source viewBox and it is re-gridded once, at import, using the SVG
xMidYMid meet rule. Heroicons solid on 20, Carbon on 32, Teenyicons on 15: all fine,
none of it the consumer's problem later.
Arity is the per-slot maximum across the set. Splitting a cubic is exact, so an icon with fewer segments is subdivided up to the count. Reducing a count would need least-squares fitting and is refused outright.
This is the part to actually use.
// The fitter reports how much better each chosen slot was than the runner-up. X's two
// diagonals share a centroid and a length, so any tie-break there is a guess and it says
// so rather than pretending.
for (const icon of result.fits) {
for (const fit of icon.fits) {
if (fit.subpath !== null && fit.confidence < 0.15) {
console.warn(`${icon.name} slot ${fit.slot}: confidence ${fit.confidence}`);
}
}
}Every assignment reports how much better its chosen slot was than the runner-up. High confidence means the fit was obvious. Near zero means two slots fitted about equally well and the tie-break was a guess.
X is the honest example: its two diagonals have identical centroids and identical
lengths, so nothing but their orientation distinguishes them.
import { emitRigSource } from "@/lib/morph/compile";
// Committable TypeScript, with the warnings written in as comments so the uncertainty
// travels with the artefact into review.
export const source = emitRigSource(result);The emitted source carries the warnings as comments, so the uncertainty travels with the artefact into code review rather than being lost in a terminal.