SolidCode / SolidPython

A python frontend for solid modelling that compiles to OpenSCAD
1.12k stars 174 forks source link

assemblies #32

Closed marmalodak closed 9 years ago

marmalodak commented 9 years ago

Just learning about SolidPython.

How do you create assemblies with many pieces? Is there a facility to "attach" an object to another at a specific point? I'm struggling to connect two beams, especially if I change my mind about the length of the beam that is not at the very tip of the assembly.

Is this the right place to ask this sort of question since it's not a bug?

Great work. I enjoy being able to write code that builds my pieces.

etjones commented 9 years ago

Sorry I missed this earlier, John. This isn't a bug in the program, but it is a limitation in the general capacity of SolidPython (and OpenSCAD), and this is a great place to bring that up for the benefit of future Googlers.

You'd like to be able to say something like "Attach the pin on this end of Part A to the socket at the end of Part B" and have that relationship maintained. Unfortunately, OpenSCAD (and thus Solid Python, since SP just compiles to OpenSCAD code) doesn't have any way to do introspection like "How long is Part A", and it would be difficult to maintain relationships without that kind of ability. Without a much more capable geometry engine, that kind of use case is probably best left to professional CAD systems.

So... that's the pessimistic sense. In the pragmatic sense of just wanting to create a model, you can keep track of some of those relationships by doing your own math.

I've attached a simple (animated! for free!) assembly that shows how I usually put things together. You can't say 'attach the end of this piece to the end of that one, but if you create them with their ends in the same place, you can then rotate around that point and get something approximating an assembly joint. SolidWorks it's not, but then, that's OpenSCAD.

Let me know if you've got any other questions, and I hope you can make some great things.

Here's the gist:

pin_joint.py

buildxyz-git commented 2 years ago

@etjones, is that gist still available? Curious to see it as well. I'm an OpenSCAD newb and am still trying to figure out the best way to demonstrate simple assemblies and keep them organized.

Is this typically done by creating .scad file for each part, bringing them together later in a main .scad file, and translating/rotating them into place?

Cheers!

etjones commented 2 years ago

I'm sorry, that gist seems to have been lost to time. Let me put something together and get it to you in the next couple days. Generally, I'll make separate functions for separate parts, and if I got to a high enough level of complexity (more than maybe 100 lines per part? YMMV) I might break those up into separate Python modules, and then import those like you normally would in Python; if your Python works, your OpenSCAD should.

buildxyz-git commented 2 years ago

Sounds great and thanks for the speedy reply!

I guess one thing I'm confused about is that it seems scad_render_to_file(object) will only render one object to a .scad file? I tried passing in a list of objects but that is not accepted.

I thought there must be SolidPython way to import/create multiple parts into one .scad

e.g.

translate(v = [0.0000000000, 0, 29]) {
    cube(size = [0.7500000000, 96, 3]);
}
translate(v = [0.7500000000, 0, 29]) {
    cube(size = [0.7500000000, 96, 3]);
}
etjones commented 2 years ago

When I’ve got several things to include, the simplest thing is to union them together. You can do that with obj_c = union()( obj_a, obj_b, obj_c) or with something like obj_c = obj_a + obj_b + obj_c

I’ll sometimes render separate objects out to separate SCAD files if I’m going to print them separately, but render them all together while I’m designing.

On Jan 22, 2022, at 6:50 PM, buildxyz @.***> wrote:

 Sounds great and thanks for the speedy reply!

I guess one thing I'm confused about is that it seems scad_render_to_file(object) will only render one object to a .scad file? I tried passing in a list of objects but that is not accepted.

I thought there must be SolidPython way to import/create multiple parts into one .scad

e.g.

translate(v = [0.0000000000, 0, 29]) { cube(size = [0.7500000000, 96, 3]); } translate(v = [0.7500000000, 0, 29]) { cube(size = [0.7500000000, 96, 3]); } — Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you were mentioned.

etjones commented 2 years ago

(So, from your example:

a = translate([0,0,29])( cube(size = [0.75, 96, 3]))
a += translate( [0.75, 0, 29]) (cube(size = [0.75, 96, 3]))
scad_render_to_file(a)

)

On Jan 22, 2022, at 6:50 PM, buildxyz @.***> wrote:

translate(v = [0.0000000000, 0, 29]) { cube(size = [0.7500000000, 96, 3]); } translate(v = [0.7500000000, 0, 29]) { cube(size = [0.7500000000, 96, 3]); }

buildxyz-git commented 2 years ago

I was playing with that earlier and that workflow makes good sense for 3D printing.

I may be trying to get OpenScad to do something it wasn't designed to do? I'd like to use it to design furniture from sheet goods. So an assembly would contain (many) simple parts. Coming from Solidworks/Fusion360 and being comfortable in Python has me really interested in OpenSCAD/SolidPython.

I would like everything to remain as a separate part instead of having to union them together. This will help when generating a BOM and DXFs etc. I can do this in scad directly just not SolidPython.

Sounds like this would require some modifications to the scad rendering engine to support things like a list of objects? Or even an scad_append_to_file()

Or I'm using OpenSCAD completely wrong lol.

etjones commented 2 years ago

Hm. I’d have to take a look at how generated DXFs handle union operations. For most purposes, unioning non-overlapping geometry is a no-op and nothing should be different with a top-level union or not, so I’ve always proceeded with something like assembly = union()( parts_array ); scad_render_to_file(assembly)

However, when you mention DXF export, I wonder if that yields an identical export to writing a multi-part OpenSCAD file like you’re talking about. I’ll check it out tomorrow and get back to you.

In the meantime, you could make your own multi-part SCAD file with something like:

scad_str = ‘\n’.join([scad_render(p) for p in parts_array]); with open(‘file.scad’, ‘w’) as f: f.write(scad_str)

I’ve always treated the two options as identical, but it’s possible they’d have different output when exporting from OpenSCAD.

You might also take a look at the BOM example in the examples folder; it’s mostly the workflow I’m talking about. I don’t know how comprehensive your BOM needs are, but this might do what you need.

Ok. More later on when I’ve got the code in front of me. Cheers.

buildxyz-git commented 2 years ago

That's a great suggestion, working with scad strings instead of relying on scad_render_to_file(object) may just be what I need to do.

I did look through the BOM example (it's included in my 3 hours total or OpenSCAD experience). I honestly haven't exported DXFs yet, I've just read this is possible with OpenSCAD and sort of key to my application. SolidPython doesn't extend this feature, does it?

Seems I may need each part as a separate file to run projection() on in order to generate DXF from the OpenSCAD CLI?

Intuitively, I thought having them as separate parts like in this example of a tabletop: Screen Shot 2022-01-22 at 8 44 04 PM Would be better than: Screen Shot 2022-01-22 at 8 45 03 PM Which is what is created with a union on these parts that touch.

Thanks for all the great suggestions and I know I'm sort of going off the rails into general OpenSCAD territory here so it's much appreciated!

etjones commented 2 years ago

I did a little looking at how OpenSCAD generates DXFs, and it works as I feared; OpenSCAD will combine all intersecting objects in the final DXF regardless of whether they're included separately or unioned together.

The attached file generates two SCAD files, with identical geometry except that one has a top-level union() and the other does not. When we use OpenSCAD to import DXFs from these files, the output is byte-identical. Basically, I think this means that OpenSCAD has an implicit union() around everything in the file. So if you wanted a DXF with two pieces as separate units ('blocks'? What's the DXF term?) but which touch or overlap each other, OpenSCAD won't be able to give you that.

If you're laying out sheet goods for use on a CNC router for example, you probably wouldn't want pieces right against each other anyway, though, right? You'd need to make allowances for saw kerf or bit width, and the layout you'd make a cut in would be different from the layout you might assemble the piece in.

(A couple other notes about DXF generation. Looks like OpenSCAD will only do 2D DXF generation, so you either need to use square() or polygon() instead of cube() or polyhedron(), and make sure all objects lie in the XY plane or else get comfortable with OpenSCAD's projection() primitive which takes 3D objects to 2D. If you set ELEVATION to, say, 29 in the attached file, OpenSCAD's Preview mode will show you slabs at 29 units up in the air, but its Render mode (which has to run before exporting to DXF or STL) will project them down to Z==0. There are definitely some funny corners to working in 2D vs 3D in OpenSCAD.)

Anyway, here's the demo file: sp32_implicit_union.py.txt

etjones commented 2 years ago

All that said @buildxyz-git, this may all be in the weeds. Can you describe your particular use case at all? I can take a swing at how I'd go about it in SP/OpenSCAD and that might be more useful to you than these technical details.

buildxyz-git commented 2 years ago

That's all really great info! Things I've been looking into as well.

My use case is broad at the moment. I would like to be able to generate woodworking plans with built-in optimizers. So I need 3D renderings, exploded/assembly diagrams, DXF for digital fabrication and material layout optimizations (cutting stock problem). Easy right? ;)

I'm still playing with just doing a proof of concept in SP to make a complete model but not sure if I'll use OpenSCADs DXF export or something like ezDXF.

I plan to wrap this all up in classes and modules so new designs can leverage them.

etjones commented 2 years ago

Right on. That sounds like a big chunk of project, and a useful one! Back when I was doing a lot of laser cutting, I could really have used something like that.

Especially when it comes to things like optimized layouts, I suspect you may have use for an introspectable system so you can say things like "Put this 2 units to the right of object B's right sides". OpenSCAD provides no capacity for that at all (and the workarounds I've looked into in SolidPython, like a BoundingBox for each OpenSCADObject have enough corner cases that I've never done much with them.) While you can definitely design a bunch of things with square(), circle(), and polygon(), I think you may find it worthwhile to look into more capable geometry APIs. Blender is a huge huge program but it's more capable than OpenSCAD and its Python API might be more suitable. FreeCAD has a Python API as well and is relatively accessible but relatively younger than Blender, so a little rough around the edges.

There's plenty you could build in OpenSCAD, but I don't think it's where I'd start if I were building a tool as general as you're talking about. I'm happy to answer any more questions you've got while you're still working with SP/OpenSCAD, though.

buildxyz-git commented 2 years ago

I haven't explored the Blender Python API but it seems I should have a peek at that first before proceeding. Blender can run Python from the command line (headless) so that could be the ticket. SP/OpenSCAD is still a good option though so I may be back!

Thanks for all your help and direction @etjones !