DIAGRAM EDITION  the stages in plain languageReference edition →

Stage 15 (on by default in Quality mode)

Lines drawn as lines: one centreline and one width

A line icon is drawn with a pen: a path down the middle of each line and one line width for all of them. Traced as filled shapes, every line costs both of its edges and its rounded ends, about four times the numbers the artist wrote. In Quality mode the tracer looks for shapes that were drawn this way and writes them back as lines — but only where that is both shorter and as faithful as the filled outline. Each line is then simplified the way an artist would draw it: two arcs on one circle become one arc, two lines in line become one.

The same shape, two ways to write it

A finished trace stores every coloured area as its outline. For a line of the drawing, the outline is two long edges side by side plus a cap at each end. A line written the way the artist drew it is a single path down its middle and one number for its width; the SVG renderer paints the edges and the round ends itself.

filled outline: two edges, two caps stroke: one centreline, one width
Half the edges, none of the caps. On lucide's icons the stroke form brings the number count from about four times the artist's to about the artist's own.

Finding the two edges of a line

The tracer already knows every edge of every shape to a few hundredths of a pixel. From each point of an edge it walks straight inward until it meets the opposite edge. If that edge faces back the other way, the two are the two sides of a line, and the distance walked is the line's width. When most of a shape's edge pairs up at the same width, the shape is a candidate stroke; a blob or a disc pairs at all sorts of widths, or not at all, and is left alone.

The middle of each pair is a point on the centreline. Where lines meet, turn sharply or end, the pairs break down; there the tracer rebuilds the junction from where the lines leading into it would meet, and each end from the farthest edge point of its rounded cap.

When the shape is too thick to thin

Which centrelines a shape has — where its lines branch and end — is read first from a "skeleton": the shape's pixels peeled away from the outside until one-pixel-wide lines remain. That works for thin lines but fails for lines that are thick compared with their length. A check mark drawn with a 43-pixel pen on arms of 60 and 121 pixels peels down to a short stub, and the V is lost.

For such shapes the tracer reads the structure from the outline itself instead. It joins the outline's points into triangles (a constrained Delaunay triangulation, in which every edge of the outline is a side of some triangle). A triangle with one outline side lies along a line; one with two is the tip of a line; one with none is where lines meet. The path through the middles of the triangles' inner sides is the "chordal axis", and its branches are the lines. The check mark comes back as one path with a corner — 14 numbers, the artist's own count, where the filled outline took 92.

Fitting the lines to the edges

Each centreline is first drawn through its middle points by the same curve fitter that draws outlines. Then all the centrelines and the width are moved together until the edges the lines would paint sit on the measured edges — a least-squares fit, solved by the Levenberg-Marquardt method. Where a stretch of edge still disagrees, a curve is split in two and the fit run again; the split is kept only if it pays for the numbers it adds.

The ends and corners of a line can be drawn several ways, and the tracer tries the ones SVG offers: round or mitred corners, round or flat ("butt") ends, keeping whichever describes the shape more cheaply.

This fitting is the stage's main cost, and most of the work on it went into making it fast without changing what it finds: the solver now uses exact derivatives instead of numerical ones, solves its equations in a form that skips the zeros, finds the nearest piece of line for each edge point with a search tree instead of a grid, measures the edge points in parallel, and checks whether a split can pay before fitting it. On lucide and openmoji icons at 512 pixels these steps took the stage from about ten times the rest of the trace to well under one.

The decision

A shape becomes a stroke when the stroke form is the shorter description — fewer numbers, weighed against how well each form matches the measured edge in the same currency the curve fitter uses — and when it matches the edge about as well as the outline does. The second test was added after two shapes came back wrong: a gear outlined with rounded corners, read as a line with sharp corners, and a puzzle piece whose curved parts are slightly wider than its straight ones. Neither is really drawn with one pen, and both now stay filled shapes.

The stage is on by default in Quality mode; --no-detect-strokes turns it off. It adds about a sixth to the trace time at 512 px.