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
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.
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.
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..
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
What you describe is one of the main reasons why I use Rhino3D. It can be scripted via the Grasshopper plugin, which integrates really nicely with Rhino and its primitives. Sadly, Rhino isn't open source and is quite pricy
Most CAD is more similar to graphic design, painting, etc. You wouldn't expect a "code first workflow" for Illustrator, and as far as I know nobody has ever successfully done anything like that.
The closest I've seen are things like UI design tools that can generate code. But a) they usually suck, and b) that is a much simpler problem than CAD.
Build123d is much better (supports STEP export and import) but a tightly integrated CAD frontend would be ideal!
It's actually very pleasing to work with. I wish there was more stuff like this. Lispy programming languages and CAD seems like a natural fit.
That said, python is preferable for most people.
https://build123d.readthedocs.io/en/latest/tutorial_constrai...
Traditionally CAD programs require declaring geometry, then defining constraint relationships between them. That leaves ambiguity. I often create under-constrained sketches in Fusion, then change a dimension, which breaks the geometry in ways that technically respect the constraints.
They designed an imperative constraint system. A lot of constraints are linear, so you can just order the sketches and reference as needed. For circular or bi-directional references you probably have to define some construction geometry first, solve the constrains there, and reference it.
Something I haven’t seen before is their filter syntax for disambiguating solutions. You can express how the desired solution relates to the surrounding geometry. This constrains correctly across large parameter changes and will error when there is no longer a solution rather than switching to an unexpected solution to the constraint.
https://build123d.readthedocs.io/en/latest/tutorial_constrai...
https://build123d.readthedocs.io/en/latest/tutorial_constrai...
Some artists have done some truly magical things with it: https://hof.povray.org
The biggest caveat is that there is currently absolutely zero mitigation for toponaming. This feature is extremely brittle, so I tend not to use it very much -- but I am still glad it exists for those situations when composing a selector is too annoying.
Documentation | Cheat Sheet | Discord | Discussions | Issues | Contributing
build123d is a Python-based, parametric boundary representation (BREP) modeling framework for 2D and 3D CAD. Built on the Open Cascade geometric kernel, it provides a clean, fully Pythonic interface for creating precise models suitable for 3D printing, CNC machining, laser cutting, and other manufacturing processes.
Designed for modern, maintainable CAD-as-code, build123d combines clear architecture with expressive, algebraic modeling. It offers:
Solid(shell), tuple(Vector)),obj += sub_obj, Plane.XZ * Pos(X=5) * Rectangle(1, 1)) for algebraic, readable, and composable design logic,Although wildcard imports are generally bad practice, build123d scripts are usually self contained and importing the large number of objects and methods into the namespace is common:
from build123d import *
Edges, Wires (multiple connected Edges), and Curves (a Compound of Edges and Wires) are the 1D Shapes available in build123d. A single Edge can be created from a Line object with two vector-like positions:
line = Line((0, -3), (6, -3))
Additional Edges and Wires may be added to (or subtracted from) the initial line. These objects can reference coordinates along another line through the position (@) and tangent (%) operators to specify input Vectors:
line += JernArc(line @ 1, line % 1, radius=3, arc_size=180)
line += PolarLine(line @ 1, 6, direction=line % 1)
Faces, Shells (multiple connected Faces), and Sketches (a Compound of Faces and Shells) are the 2D Shapes available in build123d. The previous line is sufficiently defined to close the Wire and create a Face with make_hull:
sketch = make_hull(line.edges())
A Circle face is translated with Pos, a Location object like Rot for transforming Shapes, and subtracted from the sketch. This sketch face is then extruded into a Solid part:
sketch -= Pos(6, 0, 0) * Circle(2)
part = extrude(sketch, amount= 2)
Solids and Parts (a Compound of Solids) are the 1D Shapes available in build123d. A second part can be created from an additional Face. Planes can also be used for positioning and orienting Shape objects. Many objects offer an affordance for alignment relative to the object origin:
plate_sketch = Plane.YZ * RectangleRounded(16, 6, 1.5, align=(Align.CENTER, Align.MIN))
plate = extrude(plate_sketch, amount=-2)
Shape topology can be extracted from Shapes with selectors which return ShapeLists. ShapeLists offer methods for sorting, grouping, and filtering Shapes by Shape properties, such as finding a Face by area and selecting position along an Axis and specifying a target with a list slice. A Plane is created from the specified Face to locate an iterable of Locations to place multiple objects on the second part before it is added to the main part:
plate_face = plate.faces().group_by(Face.area)[-1].sort_by(Axis.X)[-1]
plate -= Plane(plate_face) * GridLocations(13, 3, 2, 2) * CounterSinkHole(.5, 1, 2)
part += plate
ShapeList selectors and operators offer powerful methods for specifying Shape features through properties such as length/area/volume, orientation relative to an Axis or Plane, and geometry type:
part = fillet(part.edges().filter_by(lambda e: e.length == 2).filter_by(Axis.Z), 1)
bore = part.faces().filter_by(GeomType.CYLINDER).filter_by(lambda f: f.radius == 2)
part = chamfer(bore.edges(), .2)
The previous construction is through the Algebra Mode interface, which follows a stateless paradigm where each object is explicitly tracked and mutated by algebraic operators.
Builder Mode is an alternative build123d interface where state is tracked and structured in a design history-like way where each dimension is distinct. Operations are aware pending faces and edges from Build contexts and location transformations are applied to all child objects in Build and Locations contexts. While each Build context tracks state, operations like extrude can still optionally take explicit Shape input instead of implicitly using pending Shapes. Builder mode also introduces the mode affordance to objects to specify how new Shapes are combined with the context:
with BuildPart() as part_context:
with BuildSketch() as sketch:
with BuildLine() as line:
l1 = Line((0, -3), (6, -3))
l2 = JernArc(l1 @ 1, l1 % 1, radius=3, arc_size=180)
l3 = PolarLine(l2 @ 1, 6, direction=l2 % 1)
l4 = Line(l1 @ 0, l3 @ 1)
make_face()
with Locations((6, 0, 0)):
Circle(2, mode=Mode.SUBTRACT)
extrude(amount=2)
with BuildSketch(Plane.YZ) as plate_sketch:
RectangleRounded(16, 6, 1.5, align=(Align.CENTER, Align.MIN))
plate = extrude(amount=-2)
with Locations(plate.faces().group_by(Face.area)[-1].sort_by(Axis.X)[-1]):
with GridLocations(13, 3, 2, 2):
CounterSinkHole(.5, 1)
fillet(edges().filter_by(lambda e: e.length == 2).filter_by(Axis.Z), 1)
bore = faces().filter_by(GeomType.CYLINDER).filter_by(lambda f: f.radius == 2)
chamfer(bore.edges(), .2)
New objects may be created for parametric reusability from base object classes:
class Punch(BaseSketchObject):
def __init__(
self,
radius: float,
size: float,
blobs: float,
mode: Mode = Mode.ADD,
):
with BuildSketch() as punch:
if blobs == 1:
Circle(size)
else:
with PolarLocations(radius, blobs):
Circle(size)
if len(faces()) > 1:
raise RuntimeError("radius is too large for number and size of blobs")
add(Face(faces()[0].outer_wire()), mode=Mode.REPLACE)
super().__init__(obj=punch.sketch, mode=mode)
tape = Rectangle(20, 5)
for i, location in enumerate(GridLocations(5, 0, 4, 1)):
tape -= location * Punch(.8, 1, i + 1)
build123d can import and export a number data formats for interchange with 2D and 3D design tools, 3D printing slicers, and traditional CAM:
svg = import_svg("spade.svg")
step = import_step("nema-17-bracket.step")
export_stl(part, "bracket.stl")
export_step(part_context.part, "bracket.step")
More Examples and Tutorials are found in the documentation.
For additional installation options see Installation
Installing build123d from pip is recommended for most users:
pip install build123d
If you receive errors about conflicting dependencies, retry the installation after upgrading pip to the latest version:
pip install --upgrade pip
build123d is under active development and up-to-date features are found in the development branch:
pip install git+https://github.com/gumyr/build123d
build123d is best used with a viewer. The most popular viewer is ocp_vscode, a Python package with a standalone viewer and VS Code extension. Other Editors & Viewers are found in the documentation.
build123d is a rapidly growing project and we welcome all contributions. Whether you want to share ideas, report bugs, or implement new features, your contribution is welcome! Please see our CONTRIBUTING.md file to get started.
build123d is derived from portions of CadQuery, but is extensively refactored and restructured into an independent framework over Open Cascade.
This project is licensed under the Apache License 2.0.
OpenSCAD uses Constructive Solid Geometry (CSG) which represents objects as boolean combinations of primitives (union, difference, intersection). It does not maintain an explicit boundary/face structure internally
As the sibling comment mentioned, the classic problem of chamfer/fillet. Inconvenient in OpenSCAD, trivial in build123d.
There are various features I've missed: polylines with rounded corners, extruding along a path, and more I can't recall.
As you mention: code organization. I didn't have the need early on, but over time I often wanted to do add custom things, for example my own hack of an implementation for extruding along a path. And passing data around is just painful and ugly... since you can't read any of your input shapes, you have to pass around all data explicitly -- alongside or instead of shapes -- and then only actually render them in the latest stage. Generally I found it hard to make reusable code, and generally it has to make many assumptions about the calling code.
The OpenSCAD editor is one big IDE, and it's not a great one. My workflow was to keep VSCodium on one side and OpenSCAD on the other, just to use a better editor. I actually thought to myself that a good project direction would be to make it more modular and focus on the unique parts rather than the IDE. And that's indeed how build123d does it: primary repo only handles the rendering, and then suggest compatible viewers, with one shipped as a VS Code extension being the primary suggestion.
Speaking of workflow, a huge difference is local coordinate systems: lacked in OpenSCAD and encouraged in build123d. It fits my brain really well. I just made a shape, now I want to make some holes in it. Previously I've often had to think about what global coordinates to put some volume, but now I'll just select that face and work with those local 2D coordinates.
And another workflow annoyance before is when I'm designing something to be printed in separate parts. First, meticulously fit things together in the global coordinate system so that I can see them in the assembled state. Then to allow printing, also conditionally move all parts to manually specified other coordinates. With build123d one can define the parts separately, place joints in desired locations, and have them automatically snap together for the assembled view. It looks useful for integrating third-party parts as well.
Minor thing, but I'm always slightly annoyed by the fact that a rectangle is called a square and a slab/box called a cube in OpenSCAD...
Oh, and it's often useful to use polar coordinates. More passing around custom data and manually calling your custom conversion function whenever passing to OpenSCAD. build123d has first-party suitable objects for it.
OpenSCAD development seems fairly dormant as well. Latest stable release was five (!) years ago, but by reading around you see that you're supposed to use the nightly version because it's superior. Not very friendly for newcomers. By contrast, build123d seems very active.
I should stop now, because this already got pretty long. As you can see, I had some bottled up though -- thanks for letting me vent!
In reality, CAD version control is done just like assets in the artistic world (game design, animation, films, etc): with file locks.
I used to work for Dyson. There's no way you're designing a vacuum cleaner with code-based CAD. We used TeamCenter (which is probably the worst software I've ever used, but for unrelated reasons).
(context: https://github.com/bernhard-42/vscode-ocp-cad-viewer/)
FeatureScript is a different beast. It actually runs as part of regeneration in Onshape. Standard features (extrude, loft...) are also defined in FeatureScript, so your custom features are the same first-class citizens with a interactive GUIs and stable updates to upstream changes. You can freely mix interactive CAD and custom code by adding standard features and custom features.
However the downside with featurescript and I think a big mistake on their part was to use a custom language rather than python or javascript. Featurescript is almost javascript but with some syntax changes and magic DSL's. You are also forced to use the inbuilt editor which is horrible and if you have burned VIM keybinding into your nerve endings, going back to non modal editing is horrible.
Also the discovery of featurescript modules in the community has terrible UX. It's super weird that they have such a great system but finding useful extensions is horrible.