Engineering Journal
Schema Editor
Schema Editor

Most of a CAD export is not the drawing, and your parser cannot tell

2026-08-21

TLDR: An Arduino schematic exported from EAGLE contains 7848 vector elements. 6280 of them are two logos in the title block, exported as scanline-traced rectangles. Any analysis that treats element count as document complexity is reasoning about a bitmap.

The shape of the problem

You have a vector document and a pipeline that finds structure in it. Wires and components in a schematic; walls and doors in a floor plan; axes and series in a chart.

The pipeline sees primitives. It counts them, classifies them, builds topology from them. Every part of that assumes the primitives are the subject.

In real CAD exports they mostly are not. A drawing produced for humans carries a frame, a title block, revision tables, licence badges, company logos. None of that is the circuit, and the export format has no way to say so.

Why the ratio is so extreme

It is not that decoration is verbose. It is that decoration is often a raster, and vector exporters have exactly one way to draw a raster into a vector stream: trace it into scan rows.

Here is the Arduino sheet's PDF content stream:

13073 12915 2348 2 re
B
13071 12913 2352 2 re
B
13070 12912 2354 2 re
B

One filled rectangle per scan row, two units tall, marching down the image. 6280 of them in a single uninterrupted run, all one colour. That is the Creative Commons badge and the Arduino infinity mark.

The circuit itself, the entire schematic, is the other 1568 elements.

So the naive reading of this document is that it contains 6336 filled shapes and roughly 1300 lines. The true reading is that it contains roughly 1300 wires, 193 symbols, and two pictures.

What that breaks

Complexity limits. Ours bailed above 1200 elements. The logo alone is five times that, so the guard fired on a decorative bitmap and refused to analyse the circuit behind it.

Classification counts. Every scanline rect is closed and filled, which is the exact signature of a component body. The classifier dutifully produced 6336 components, and the 193 real ones were a rounding error inside them.

The layers panel. Six thousand identical rows.

Interaction. Six thousand elements to hit-test, wrap, and repaint on every camera change.

Each of those looks like a separate bug with a separate fix. They are one bug seen from four places.

Detecting it

The temptation is to detect it geometrically after the fact: cluster thin filled shapes, look for regular spacing, call a dense enough cluster artwork. That works and it is fragile, because by then you have lost the one piece of evidence that makes it easy.

Adjacency in the instruction stream. A traced bitmap is emitted as an unbroken run of rectangle-then-fill, same colour, same thin dimension, nothing between them. Real drawings emit filled rectangles in ones and twos: a pin, a junction dot, a filled arrowhead. They do not emit two dozen in a row with nothing else interleaved.

That signal exists only in the operator stream. Once you have an SVG, or a display list, or a scene graph, the ordering that made it obvious is gone and you are back to clustering.

So the rule is: a contiguous run of filled hairline rectangles sharing a fill colour, longer than a couple of dozen, is traced artwork. Any stroke, any non-rect fill, any colour change ends the run.

On this file that rule finds exactly one run, of exactly 6280. On a 23-page academic paper, it finds none.

Marking, not deleting

The instinct is to drop it. Do not.

The logo is real content. The user placed it, expects to see it, and will export the sheet with it. A parser that improves its own numbers by discarding parts of the input has not understood the document, it has edited it.

What it needs is a category. Ours already had one: ink, the word the engine used for freehand pen marks, which draw but are excluded from wire and component analysis. Traced artwork is precisely that, so it needed no new concept and no new branch anywhere downstream.

Then collapse it

Marking fixes the analysis. It does not fix rendering, because 6280 elements still cost 6280 elements to paint every frame.

But those 6280 subpaths share one fill, one stroke, one width, and never move relative to each other. That is one <path> with 6280 subpaths in its d. Identical pixels, one node.

The document went from 7848 elements to 5246 while gaining 1272 wire hitboxes and 745 text labels it did not have before.

The measurement that matters

Panning the finished document costs about 17ms per frame. Before any of this, it cost about 4.5ms.

That is the honest number and it is worth stating plainly, because the pipeline that now runs is doing real work the old one skipped: it built topology, wrapped every wire in a selectable hitbox, and drew 745 labels. Slower and working beats instant and blank, but it is still slower, and rounding that off would be lying about the trade.

The general lesson

Before tuning a threshold on element count, ask what fraction of the elements are the subject. If you have not measured that, your threshold is calibrated against whatever decoration your test file happened to contain.

And keep the parse close to the source format for as long as you can. Adjacency, ordering, and grouping in the original instruction stream are information. Every conversion into a friendlier intermediate form drops some of it, and the things you most need to detect are usually the things that were only visible upstream.

Read this post in the full Engineering Journal →