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
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
Build123d is much better (supports STEP export and import) but a tightly integrated CAD frontend would be ideal!
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.
You can do that in FreeCAD. Performance is horribly slow in my experience, but maybe I'm doing something wrong.
https://wiki.freecad.org/Scripted_objects
https://wiki.freecad.org/Create_a_FeaturePython_object_part_...
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 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.
On one project I was using autolisp in AutoCAD, Then another language in an external database, and some pascal work to tie them together. I had to segregate my work to separate days to keep from getting my syntax all screwed up.
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.
I've tried various code CADs before, and they're fine as long as what you're creating is simple enough to build from independent pieces, ideally that you can keep in your head. As soon as you need to refer to aspects that have been created within the CAD, like an edge, or a particular face of an extruded shape, the paradigm falls apart.
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.
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.
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.
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.
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 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()Since SolveSpace has a helix tool that can "extrude" any sketch along a helix it should be doable.
https://www.linkedin.com/pulse/dreaded-double-helix-tutorial...
I think the very latest Blender can do it, I haven't got it working yet though.
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?
PS. I also use a lot of OpenSCAD. Liked your comment. I started in POVRay so it made a lot of sense after managing non-FOSS CAD people for years.
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!
Experimental extension to make code-cadding as terse as possible.
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
(context: https://github.com/bernhard-42/vscode-ocp-cad-viewer/)
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.
Some artists have done some truly magical things with it: https://hof.povray.org
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.
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...