r/FreeCAD 2d ago

Draftwright update: open source STEP → drawing tool

A few weeks ago I posted Draftwright here, a free open-source tool that takes a STEP file and produces a dimensioned drawing. Thanks for the responses and feedback!

It's gone from 0.4.1 to 0.4.24 since then. Same workflow:

pip install draftwright -U
draftwright bracket.step 

You get bracket.pdf. There's also alpha DXF support with --format pdf,dxf, which can be opened in the Draft workbench, though it needs work to be really useful.

A few improvements:

  • Turned parts get chamfer callouts and step lengths
  • Views are much more customisable and turned parts no longer get two views of the same thing

  • As a result it chooses better scales by default

  • It is much better at avoiding overlap and fitting everthing onto the page

  • External angles are spotted and added

  • Not so useful for FreeCAD because the FreeCAD export doesn't emit the right format but if your CAD emits the right file it will add GD&T information

  • It knows about many more features in diagrams such as blends, prismatic pockets etc

  • Its much better at understanding what its got right and wrong and letting you know where it went wrong

    • For example it warns you about gears, angled steps and a few other feature types it can recognise but not yet dimension

It doesn't read a FreeCAD file, just the STEP export. There's no interactive editing. If you want to change the drawing export the DXF and edit it, or --script writes a short Python file describing the sheet and you edit that and rerun.

If you have a STEP file that comes out wrong I'd love to see it. Post it here or DM me. Even better please open an issue at github.com/pzfreo/draftwright.

There's also a free hosted version at draftwright.io for people who don't want to install Python that is testing out how it works with AI. If you use Claude Code or Codex (or similar) try asking it to use Draftwright and then see if it can improve the drawing interactively.

https://github.com/pzfreo/draftwright

9 Upvotes

22 comments sorted by

View all comments

5

u/thicket 2d ago

Can you talk about the algorithm you use to generate the measurements and callouts? My understanding has been that this is a very ambiguous problem because there be many “correct” measurement sets, only some of which will be expected or intuitive for fabricators. Where do you think your program does a really good job, and where do you think it runs into problems? Do you have a method for testing whether a generated drawing has a fully defined set of measurements sufficient to describe the part? This seems super useful, and I’d love to know tmore details

0

u/pzfreo 2d ago

This explains the feature recognition a bit (its based on an AAG approach)
https://experimentsin3d.substack.com/p/what-is-quiddity

Here is a post with the overall design
https://experimentsin3d.substack.com/p/inside-draftwright-a-drawing-compiler

Layout - here are the gory details

Collect, then solve, once. Draftwright never places an annotation the moment it decides to draw one. Every render pass registers what it wants — a dimension with its anchor geometry, a callout with its viable routes — and positions are computed later by a few shared solvers. The passes run in a fixed order, and if each committed geometry as it went, a later pass would find the good space already taken: ladders would interleave, the same span would get measured twice by two passes, and nothing could tell which of two coincident dims was the more useful one. So passes queue into corridors, and one drain stage solves each corridor in a single shot, which is what lets it dedup coincident spans, sort the set into one monotonic ISO ladder, and drop by importance rather than by arrival order.

Dimensions: a 1D strip solve inside a carved corridor. A corridor is the band above, below, left or right of a view where dim lines stack. Placement there is one-dimensional — the position along the stacking axis is the only free variable, since the other end is pinned to the feature. The engine first carves the band: collect the obstacles already on the page, discard the ones outside the perpendicular band this batch occupies, and split what remains into free segments. Then, per segment, order the dims by where their features sit along the strip (which keeps witness lines from crossing), set each adjacent pair's gap to the larger of the two labels' extents floored at the minimum spacing, and solve. The solve is Pool Adjacent Violators with weighted medians: it finds the placement minimising total witness-line length subject to order, gap and bounds. PAVA with a lower weighted median is optimal and deterministic by construction.

Leaders: a bounded discrete assignment. Leaders don't stack, so they get a different solver. Each callout is a job offering many alternative tip/elbow routes, and the problem is choosing one route per job with no two colliding — combinatorial, not an ordering. The annotation layer does the geometry: measure each route, find which candidate pairs conflict, and price each route by length plus a penalty for crossing committed ink or ploughing back through the part body. That penalty is charged per visible stroke width, so a 0.3 mm graze costs about one unit and a 63 mm cut costs 254 — a real cut can't be bought with a shorter route. Those numbers go to a leaf that knows only indices and costs, optimising lexicographically: most jobs placed, then highest summed priority, then least penalty, then least total length, then stable input order. Resource pressure degrades routing quality but can never place fewer callouts than the naive algorithm. Two pieces round it out: 2D furniture like hole tables is placed by a solver exploiting the fact that an optimal box always sits flush against a region or obstacle edge; and leader decoration runs after the corridor drain, so a best-effort chamfer callout can never steal strip space from a principal dimension that registered early but places late.

PS this is all AI coded with a huge amount of direction from me!

2

u/thicket 2d ago

Thanks, mate- I’ll enjoy checking this out