Possibility of supporting ImplicitCAD (extopenscad)? #134
Description
ImplicitCAD doesn't yet have everything that OpenSCAD has. But it does have at least one killer feature that OpenSCAD lacks: trivial fillets for cubes and boolean operations.
https://github.com/colah/ImplicitCAD
(It has several other nice features -- but fillets are the one single thing that would force me to leave OpenSCAD and use FreeCAD or something else that "gets" fillets. So it makes me happy to have them in an "extended OpenSCAD" compiler.)
Instead of cube([10,10,10]);, if you say cube([10,10,10],false,1);, you'll get a 10 unit cube with 1 unit fillets along all edges. (The 'false' is a value for 'centered'. My quick and dirty experiments only worked if I used positional arguments in that order, but there may be better ways to do this).
difference(), union(), and intersection() now all take an 'r' parameter that specifies the radius of the fillet to apply along the edges.
It's fairly trivial to load the result of scad_render_to_file(object) into a text editor and just paste the necessary extra parameters into place, but it would be slick if there was some way to do this directly from SolidPython.
Not sure of the best approach, but probably one settable flag to enable .escad output, and then a way to specify a fillet radius. My lazy feeling is to just make it a setting in the solid library, so that I can tell solid to use 1 unit fillets and then go on about my business of creating geometry, knowing that it will apply a 1 unit r to any cubes or operations that it generates. In my head, this seems reasonable, because you probably want the same fillet for each conceptual chunk of your model, so set it once, build, then change it before building a different piece.
You could allow the fillet radius in the cube function easily enough. And you could likewise accept and pass through the fillet in the union/difference/intersection functions. But it's kind of slick to be able to use +, -, and * instead, and there's no straightforward way to give them that extra parameter...
Alternatively, maybe there could be a way to specify a fillet radius for a SCAD object. That seems more complicated to me, because then you have to answer some design questions:
- If the SCAD object is the result of operations on other objects, should you recursively apply the new fillet down the tree? That would override any lower level fillets already in place...
- When performing an operation on SCAD objects, should the result inherit fillets from the children? This raises questions if the input objects have different fillets...
- If you neither recurse nor inherit, then you have to spend a lot of time setting fillets on each piecewise operation result, which seems tedious...
Sorry if this is rambling or disorganized. I found ImplicitCAD and SolidPython essentially on the same day after years of loving OpenSCAD's programmability and hating how hard it made some things. I may have gotten a bit over-excited ;)
Activity
etjones commentedon Jan 22, 2020
I've been following Chris Olah's work for a long time now. Dudes' straight-up brilliant. ImplicitCAD's integral rounding is pretty sweet.
I've been revisiting fillets & 2D offsetting in SP in the last couple weeks. I don't know how much you've looked at the OpenSCAD's
offset()function, but it's surprisingly capable, albeit limited to 2D fillets/rounds. See here for some capable helper functions for dealing with the fine points ofoffset()arguments.That doesn't really get you to the impressive state of ImplicitCAD's 3D rounding, though. Because SP is just a translation layer from Python to OpenSCAD, it's difficult to just add functionality to the base functions like
cube()orunion(). Not impossible, but I'm not super inclined to go replacing calls tocube(side, r=fillet_rad)with, for example,minkowski(fillet_rad){ cube(side-2*fillet_rad)};That would yield a rounded cube, but at the expense of straying a little far from the base OpenSCAD language.A couple directions I could see this going are:
Make a suite of
cube_rounded(),cyclinder_rounded(), etc. functions that create primitives usingminkowski(). These would mimic ImplicitCAD's native rad argument. Usingoffset(), you could even apply fillets/rounds topolygon(), although you'd have to do a bunch of work to round the corners of arbitrarypolyhedron()s. Note that use ofminkowski()also slows rendering down dramatically. And it wouldn't get you the real magic of roundedunion()s that ImplicitCAD has.We could add **kwargs options to all native classes which would allow them to accept extra arguments outside of the OpenSCAD spec (e.g,
r). But that's digging pretty deep into the guts of things for a relatively small use case. I'd probably be more comfortable forking SolidPython to make a separate project that targeted ImplicitCAD, er, explicitly. That would also enable things like parameters that accept functions, which SolidPython/OpenSCAD definitely doesn't do.So that's a long way of saying, no, SolidPython probably won't have explicit support for ImplicitCAD; it's different enough that we'd have to make some deep changes and I think it's dangerous for a project to try to serve two masters like that. I'd be happy to see a PyImplicitCAD fork of SP, though. I don't think it would take too much work, and could open up ImplicitCAD's potential use cases a lot farther. If that's something you want to take on, I'd be happy to talk you through any of the less-clear bits of SolidPython's rendering codebase.
etjones commentedon Jan 22, 2020
Hmm. On thinking about this a little more, it would actually be a pretty simple (although touching a lot of classes) change to allow all SP classes (
cube(), etc) to take extra keyword args. Animplicit_scad_render()function would then output those extra args, whilescad_render()would ignore them. That feels a little hacky, since all classes would now accept arbitrary arguments, but it's not too far out of reach. I'd be willing to talk through a PR for that.You also might be able to write a monkey-patching system,
add_implicitCAD_args()that would alter all currently existing classes to accept implicitCAD's arguments, and render those classes accordingly. I kind of prefer that approach, since all ImplicitCAD-related code could be kept in one filesolid.implicitand we wouldn't have to make changes to existing OpenSCAD-based code. I'd definitely be OK with a single-file, monkey-patched, ImplicitCAD compatibility system. Any interest?Digital-Monk commentedon Jan 22, 2020
Sounds entertaining. Never monkey patched before, but quick search makes it look straightforward enough. As a starting point (80% solution), I would focus on cube, union, difference, and intersection. Haven't looked at the SolidPython code yet, but is that 4 classes, or noticeably more complicated than that? Guess I should just clone the repo and look :)
Glancing and thinking out loud:
__init__?__init__s?nickc92 commentedon Jan 22, 2020
Just thought I'd mention that you can get a cube with rounded edges using the Minkowski sum with a sphere; in solidpython, e.g.:
That being said, ImplicitCAD does look intriguing, and I'd consider contributing to
implicit_scad_render(). I'm curious to see if it renders STLs much faster (it claims that it can, at the expense of accuracy). I wrote theviewscadpackage, which lets you do SolidPython designing within a jupyter notebook, and produces interactive 3D renderings within the notebook. I use it for my own CAD stuff. The main problem with it is that in order to render something, it calls OpenSCAD to fully render an STL, which can take some time if it's an object of any complication. If ImplicitCAD can do this rendering much faster, it might make for a much better experience in viewscad.Digital-Monk commentedon Jan 22, 2020
STL generation speed is dependent on the resolution you request. Basically, ImplicitCAD is building a "field strength" model, and then it uses some variant of marching cubes to tile triangles at the "field strength == 0" surface. (Oversimplified, and probably wrong in critical details, but close enough to distinguish it from OpenSCAD's approach)
Building up the internal model seems to be ridiculously fast. Marching the cubes over it can be fast, or can be painful... It's a little like setting $fn too high, except that $fn only affects curved surfaces, but setting ImplicitCAD's output resolution too high affects everything. Still, pretty easy to have a couple of resolution settings for preview and final. And the compiler will pick a resolution on its own that lets it do a pretty-fast-but-not-very-clean model.
(I love minkowski, but I hate the render times...)
Basic support for some elements of ImplicitCAD, as suggested in #134
etjones commentedon Jan 22, 2020
So... I got to thinking about this one and decided to just go ahead and implement it. I've pushed a trial to a branch, and the monkey patch code is here. Basically, for any of several SolidPython classes (cube, union, etc.), I subclass the original classes and add a
rargument. Usage is:solid.implicittoo implicit? Issolid.implicitCADbetter?I'm going to leave this issue open for now. Ideas or edits welcome!
etjones commentedon Jan 22, 2020
Just tried this out on ImplicitCad.org's web editor. Here's a SP version of the first rounded ImplicitCad shape in their docs
Yields the ImplicitCad code:
This renders correctly on http://www.implicitcad.org/, but generates a bunch of errors; looks like this feature might require some more massaging to keep those APIs synced up.
Error samples from code above:
Digital-Monk commentedon Jan 23, 2020
solid.implicitseems perfectly reasonable to me, because thecabalpackage (likepipbut for haskell) is just calledimplicit.I've never really done animation in OpenSCAD/ImplicitCAD, so I'm probably not going to be much use there, even for ideas. As a ludicrously cheesy pass-through solution, could your implicit rendering code check the type of the values in the object, and if it's a string, just place that string directly in the .escad code? Would allow passing functions into ImplicitCAD, but it's probably too horrible to consider...
My personal experience with parameters to ImplicitCAD suggested that it doesn't really understand named parameters. At least, the only way I was able to get stuff to work without errors was to use positional arguments. So, the cube() call (at least for me) had to be
cube([x,y,z], centered, radius). I haven't played with the 2D stuff much, so I don't know if it's any better.Hmmm... Played with that example some more, and it appears that ImplicitCAD does understand named parameters and can handle them being "out of order", but there must be some kind of pre-parser that is out of sync with the actual logic, and uses different rules to validate the syntax. So you get syntax "errors", and then it goes ahead and does the right thing anyway... For now, it looks like the quick patch got the job done, and the extraneous errors need to be fixed on the ImplicitCAD side.
Digital-Monk commentedon Jan 23, 2020
So, turns out that the "stable" build of ImplicitCAD may be almost 2 years old, so the warnings from the online interpreter might just be stale artifacts. Sounds like you've got a good enough implementation for now. Thanks!
etjones commentedon Jan 23, 2020
Nice! I think it wouldn't be too hard to do a custom version of
scad_render()that removed argument names, for instance, if we needed to. I'll keep this issue open for a few days in case anyone playing with the branch wants to look through things (@nickc92 ? might be a speedup for viewscad), and then I'll merge to master if nothing else pops up. Cheers!etjones commentedon Feb 9, 2020
Hi guys - I just looked over this some more. There's some more work -- unit tests, documentation, README-- that I want to add before merging to master. I've got some things going on right now (baby on the way, yay!) that mean I may not get to that maintenance work right away, but I'll leave this issue open for the moment with a pointer to the branch for anybody who stumbles on it. I'll update when I've cleaned up all the plumbing work.
replaced "hard coded" OpenSCAD builtins with "dynamic" builtins
jeff-dh commentedon May 17, 2021
The mentioned branch above is based ontop of the
use_py_scadparserbranch and basicly uses theimport_scadfeature to import the OpenSCAD builtins ("commands" likesquare, sphere, translate, union, difference, intersection, hull,....) from a *.scad file (which is actually name builtins.openscad).This allows it to easily replace the complete set of builtins (e.g. OpenSCAD builtins / parameters) with a different set of builtins (e.g. ImplicitCAD builtins / parameters) by just editing or adding an OpenSCAD file which only includes module definitions and looks like this:
If I understand the "issue(s) with ImplicitCAD" right (basically a different set of parameters to the builtins?), this could be a pretty nice way to get there by changing a config file (and reducing the code size). One would only need to create an interface to switch between builtins.openscad and builtins.implicitCAD config file.
This is not tested a lot! These are all more drafts and proof of concepts than ready to ship code.
added ImplicitCAD support (SolidCode#134)
jeff-dh commentedon Jun 2, 2021
ExpSolid
ImplicitCAD example
Works with ImplicitCAD 0.3.0.1 and the online renderer without any issues.
BUT this creates limitations: you can't use a lot of libraries (mcad, bosl, bosl2 they all seem to be not ImplicitCAD compatible). You can only use the implicit builtins / api.
jeff-dh commentedon Jun 2, 2021
https://github.com/jeff-dh/SolidPython/blob/exp_solid/solid/examples/15-implicitCAD2.py