22 comments

[ 0.26 ms ] story [ 48.7 ms ] thread
These types of CAD scripting tools are great but always try to position themselves as an “alternative” to GUI-driven CAD, whereas in reality they are complementary. OnShape got it right with FeatureScript (https://cad.onshape.com/FsDoc/ ), which provides a very similar experience to Build123d at the scripting level. However, the insight that OnShape got right is that these scripts automatically become available as possible nodes within the history-based modeller. The OnShape UI is infinitely extendable beyond the fixed set of tools that comes with the base modeller.

Build an FOSS CAD front end using something like Build123d as the extension engine, and then add hooks so the user can select edges, surfaces, objects, etc., and feed them to inputs on the scripts. The output of the script is then the new state of the history-based modeller. That would be killer

Despite being aware of its existence, I stuck with OpenSCAD out of habit. Only last week did I read through the documentation, and feel strongly that I've been missing out… it seems to solve all of my gripes with OpenSCAD. I'm excited to try it out!
I like Build123d but I really want a hybrid mouse/code CAD built around it. I want to be able to click on entities and have them show up in the code editor instead of blindly trying to select edges.
I agree, I love code-based CAD but there needs to be a hybrid with GUI tools because selecting stuff with a mouse will almost always be easier.

I known there is research out there (can't dig it up at the moment), but the goal would probably be to generate a robust geometric query for a selected item, so that small changes in the model don't affect which edge gets selected after subsequent operations.

So if you extruded a face upwards, and then filleted all the edges at the top of that extrusion, this hybrid tool would generate a query which gives you all the top edges, instead of whatever the indices of those edges happens to be. I can't imagine it's an easy problem though to generate robust queries for all possible geometry a user might select.

> I known there is research out there (can't dig it up at the moment), but the goal would probably be to generate a robust geometric query for a selected item, so that small changes in the model don't affect which edge gets selected after subsequent operations.

There is quite a bit of research that this is impossible. No matter what algorithm or heuristic you use, the second that symmetry is introduced, the query breaks down. The only way to resolve those issues is to present them to the user as an underspecified constraint, and no geometric kernel is well designed to do that.

I have been using this library for a few months alongside Gemini 3.1 Fast

It's really useful to get an iteration loop going with an LLM.

The OCCP viewer extension for VS Code helps make sure you can see and manipulate the resulting model

CAD has needed a proper code-first workflow for years. The existing options always felt like they were built for the GUI first and scripting was bolted on after.
as someone mentioned recently somebody made this build123d-playground on the web:

https://jojain.github.io/build123d-sandbox/

learning curve is steep, but the examples get you going in no time..

though not really CAD, favorite example: https://build123d.readthedocs.io/en/latest/examples_1.html#c...

shows the ability of this implementation of the open cascade kernel.. i havent found this kind of projection function too often in other cad programs, so this is really cool.. i remember trying to do similar with ptc creo and it was a pain..

In many ways this looks fun. I love the precise control and programming power of tools like this, but when I need something in real life, I never use them any more. The productivity of graphical tools is so much greater (as far as my brain works).

When I was younger I used POVray for a few small projects, but once I had access to graphical interfaces the difference in output quantity and quality was huge. I still keep tools like POVray installed, but all I ever do with them is tinker once in a while.

Oh my, thank you so much for bring back the POVray memories!
This is cool, seems like a next gen cadquery, which was really cool to see.
I used to do a lot with AutoLisp in AutoCAD back when it ran in DOS. Did a lot of dynamic creation and manipulation of the models with it. It was useful and a lot of fun (aside from parenthesis nesting).
Why do these tools never have the equivalent of sketch contraints in FreeCAD? That's how I build my models and it avoids a lot of math.

I'd really like a "CAD as code" tool that's basically the FreeCAD Part Design workflow but with code. I know FreeCAD has a python interface but it's far from developer friendly.

This is also something I really want, what is the best way to include constraint solvers? I have been messing around with sympy see what insights it provides in this domain, but to actually use it the cad DSL would have to be python, is there an easy way to build a simple constraint solver out of a normal imperative workflow?
My 2 cents: I fell in love with build123d (not at affiliated with build123d or gumyr) that I built a wrapper on top of it to generate both python code and export step stp 3mf. Please check here if you are interested texocad.ai
Ahh, but can it do a clean self-reversing diamond thread including the reversing portion?

You'd be amazed how hard this is to achieve with open source tools. IIRC modern FreeCAD can't, old FreeCAD can, ~5 ways to achieve it in OpenSCAD don't work properly, Blender keeps shifting-sands and mostly can't but I believe the very latest can maybe do it with difficulty using geometry nodes.

Do you have an image of the reversing portion? Any examples of pitfalls?

On the face of it, it seems like you'd define paths and sweep profiles for the material to remove. Is the difficulty in defining the path of the reversing portion, where it's not a helix?

build123d can absolutely accomplish this. I downloaded a STEP file of a self-reversing (diamond) thread part from McMaster Carr for analysis. I will reply when I finish a recreation of it. Based on my analysis of the STEP I most likely will start with a regular left and right helices that are trimmed slightly before they intersect at the end. From there will need to apply constraints for tangency with the helices while staying fixed on the cylindrical body. There are multiple ways to achieve this. The next step is to perform a sweep (cut) on half of the transition plus one side of the helix. This can be done in two stages to avoid self intersecting sweep geometry (a big no-no in most/all BREP CAD kernels).
I'm no expert on self-reversing diamond thread but I think this generates what you're looking for. Are pictures allowed here?

    import copy
    from build123d import *
    from ocp_vscode import show_all

    od, p, n, d, e = 10 * MM, 12 * MM, 5, 1 * MM, 15 * MM

    with BuildPart() as thread_segment_builder:
        with BuildLine():
            path = Helix(p, 1.1 * p, od / 2 + 0.001)
        with BuildSketch(path ^ 0) as diamond:
            xsection = Polygon((0, d), (0, 0), (d, d / 2))
        track = sweep(is_frenet=True)
        # Trim the ends to align with Plane.XZ
        Box(2 * p, p, p / 2, align=(Align.MIN, Align.MAX, Align.MIN), mode=Mode.SUBTRACT)
        with Locations((0, 0, p)):
            Box(2 * p, p, p / 2, align=Align.MIN, mode=Mode.SUBTRACT)

    # Create the rod end with an extension beyond the threaded section
    with BuildPart() as threaded_rod_end_segment_builder:
        with Locations((0, 0, -e)):
            Cylinder(od / 2, p + e + d / 2, align=Align.NONE)
        add(thread_segment_builder.part, mode=Mode.SUBTRACT)
        add(mirror(thread_segment_builder.part, Plane.XZ), mode=Mode.SUBTRACT)


    # Position and trim the end and mid segments
    threaded_rod_end_segment = Pos(Z=-d / 2) * threaded_rod_end_segment_builder.part
    threaded_rod_mid_segment = split(threaded_rod_end_segment, Plane.XY)

    # Build the rod from copies which is very efficient
    threaded_rod = Compound(
        children=[Pos(Z=i * p) * copy.copy(threaded_rod_mid_segment) for i in range(n - 2)]
        + [
            Pos(Z=-p) * threaded_rod_end_segment,
            Pos(Z=p * (n - 1)) * (Rot(X=180) * copy.copy(threaded_rod_end_segment)),
        ]
    )

    show_all()
Ok here is my result, which matches up well with the examples I have found from McMaster Carr. Note that not all STEP importers are robust enough to deal with files from OCCT based CAD packages. As I mentioned previously there are transition arcs from the left to right hand helices on both ends which enable the automatic reversing behavior.

    from ocp_vscode import show_all
    from build123d import *

    od, p, n, d, e = 10 * MM, 12 * MM, 5, 1 * MM, 15 * MM
    base_helix = Helix(p, 2.2 * p, od / 2 + 0.001, center=(0, 0, -p))
    # retain a small piece below XY plane for orienting sketch
    trim_base_helix = base_helix.trim(0.4, 1)
    trim2_base_helix = trim_base_helix.trim(0, 0.72)

    p0 = trim2_base_helix @ 1
    t0 = (trim2_base_helix % 1).normalized()
    t1 = Vector(0, 1, 0).normalized()

    bisector = (t0 + t1).normalized()
    if bisector.length == 0:
        bisector = t0  # Fallback if tangents are perfectly opposite

    ray = Axis(p0, bisector)
    end_pt = ray.intersect(Plane.XZ)

    transition_arc = TangentArc(p0, end_pt, tangent=t0)

    rh_curve = Curve() + [trim2_base_helix, transition_arc]
    profile = (rh_curve ^ 0) * Rot(Z=99) * Circle(d)  # rotate seam out of the way
    rh_sweep = sweep(profile, rh_curve)
    splitter = Plane.XZ * Rectangle(10, 40, align=(Align.MIN, Align.CENTER)).face()
    rh_sweep = split(rh_sweep, bisect_by=splitter, keep=Keep.BOTH).solids()[1]
    lh_sweep = mirror(rh_sweep, about=Plane.XZ)

    both_sweeps = Part() + [rh_sweep, lh_sweep]

    cyl = Part() + Cylinder(od / 2, p + e + d / 2)
    cyl -= both_sweeps
    cyl = split(cyl, bisect_by=Plane.XY)
    cyl += mirror(cyl, about=Plane.XY)
    show_all()
I also tested the above in the online build123d-sandbox here, which worked great: https://jojain.github.io/build123d-sandbox