Flatten model components (simple "flat" boxes) into co-planar rectangles

So, I design kitchens, and I’m trying to figure out a way to easily take a fully modeled set of cabinets (modeled with every piece of plywood and wood as individual polysurfaces) and turn those into 2D rectangles (or at the very least, flatten all of the boxes onto the c-plane, and then I can easily Make2D them) from which I can more easily pull dimensions for cutting and also to easily figure out how much material I’ll need. I was able to find (on this site - Unroll multiple polysurface? - #3 by Helvetosaur) a Python script that someone wrote that allows you to UnrollSrf, but with multiple polysurfaces, which the normal UnrollSrf does not do. So, that was a step in the right direction, but it, of course, generates every surface of each component (because they’re polysurface boxes, not flat surfaces) - but I only want the “one flat rectangle”, as it were, of each piece of plywood/face frame wood. I’ve attached a couple things for reference (the .3dm of a sample cabinet, and a pic that show what I’m trying to generate). Even though I’d ultimately love to generate only one rectangle polyline for each component, I’d be fine with simply getting the boxes all laid out co-planer (as shown in my attached pic). Thanks in advance for any help!

Cabinets_UnrollSrf File.3dm (2.5 MB)

Hi Joe - try:

  1. ExtractSrf, Copy=Yes a ‘representative’ face from each
  2. NonManifoldMerge the result.
  3. UnrollSrf the merged object.
  4. DupBorder on the results of unrolling to get the outlines, and delete the faces if you don’t need them.

Cabinets_UnrollSrf FileUnrolled.3dm (2.6 MB)

any luck?
-Pascal

Following Pascal’s method, here is a script that will attempt to automate this.

Note the following:

  • It assumes that the largest area face is the most important one to unroll. In your work, there may be exceptions to this, however the script will not be able to determine those.
  • The largest face in the panel must be unrollable - the script does check for that - if it is not, that panel is ignored. There is a command line report in this case. Everything that can be unrolled will be.
  • The largest face edges are unrolled onto the World XY plane.
  • I have not added a start point for the unrolls - could be added later if desired.
  • I have not added any kind of text dots to indicate which unrolled outline goes with which panel - can be added later.
  • The spacing of the items on the 0 plane is determined by the unroller. With Non-ManifoldMerged objects, that spacing may be unpredictable. With some work, that can also be resolved later if necessary.

Let me know if this works as a start point for you, and if you find any bugs… (likely).

UnrollPolySrfPanels.py (2.9 KB)

Edit:

I just found one - and it’s not something I can correct easily, as it has to do with how NonManifoldMerge actually works. On your model you will get some extra outlines that shouldn’t be there, it happens when one surface edge could actually trim another. When the surfaces to be NonManifoldMerged touch in that way, it creates more regions than desired. You can see the result even manually by following @pascal 's procedure and duplicating just the 4 inside surfaces (shown in red below). With NonManifoldMerge and Unroll, you will get 8 outlines…

I’m sure Pascal didn’t notice this because he selected only the outer surfaces to test. So, this is perhaps going to need a different methodology than NonManifoldMerge to automate… :confounded:

Here is a test proof of concept with a different unroll methodology, needs to be refined, but works on your object. Will post a more refined script later, need to go out now.

Edit:
Here is a more sophisticated version. It has the following features:

  • It still assumes that the largest area face is the most important one to unroll. In your work, there may be exceptions to this, however the script will not be able to determine those.
  • The largest face in the panel must be unrollable - the script does check for that - if it is not, that panel is ignored. There is a command line report in this case. Everything that can be unrolled will be.
  • The largest face edges are unrolled onto the World XY plane.
  • User can now input a start point for the unrolls - pressing Enter will use World 0
  • User can now input a spacing between unrolls - default is 10 units (can be changed) - the last custom value input is remembered in the same session.
  • I still have not added any kind of text dots to indicate which unrolled outline goes with which panel - can be added later.

UnrollPolySrfPanels.py (4.1 KB)

And one last one - I added a text dot labeling option. I’m leaving the one above for now in case the numbering is unreliable.

UnrollPolySrfPanels3.py (5.2 KB)

That last script (UnrollPolySrfPanels3) worked perfectly! Thank you so much! To your point about it ID’ing the largest face - I can’t think of an instance where the largest face wouldn’t be what I’m looking for - but if it ends up occurring, I can always just get what I need manually. All of this is just to improve efficiency, so even if it doesn’t work 100% of the time, that’s all good.

I don’t know if any of this is possible because I have no idea how writing Python scripts works or how Rhino works on this “coding” level, but here’s 2 things that could improve the script for my purposes (IF, of course, you’re willing to edit it - I know it’s gotta be at least a little time consuming, and I appreciate that you produced the script as is to begin with!):

  1. Prior to running any Python script, let’s say I do SetObjectName for each piece of the cabinet so that each piece has an Object Name that describes where it goes on the cabinet - something like “Side” or “Back”. And then I select all of the objects that are to be cut from the same material and I put them all on the same layer which is named after the material - in this instance it would either be “3/4” Plywood" or “3/4” Hardwood". Lastly, before running the script, I group together all of those named objects and I do SetGroupName so that each piece additionally has a Group Name, let’s say “C1”, as they’re all pieces for “Cabinet 1”. Is there a way to write the script so that when I unroll those objects (via the script), the 2D curves that are generated have the same named properties (Object Name, Layer, and Group Name) as the 3D objects they were pulled from? The number Dot that the script currently produces is fine if I were to only be working on one cabinet which only has something like 10-20 pieces like this post’s sample cabinet has - but if I run this python script on a whole kitchen of cabinets which has potentially hundreds of pieces (which was the whole point of why I made the post at all), I’d be left with a super long line of very similar looking curves and could only figure out what is what by referring back to the corresponding number Dot on the 3D model. So, if this is possible, I could then easily select all of the 2D curves that are to be cut from the same material (by selecting all objects on the same layer) or all the 2D curves that belong to a specific cabinet (by selecting everything with the same Group Name). However, if this is possible and you are able to edit the script, leave the “Add Label” prompt which currently produces the number Dots, though, because there might be an instance where I do actually want to use that - like if I was only dealing with this one cabinet like we have been and could easily view and make sense of the corresponding number Dots.
  2. Is there a way to write the script in order to affect (or even choose?) the orientation of the produced 2D curves? Like how each 2D curve is individually oriented (like how its rotated in and of itself - not how it’s positioned relative to the other curves) on the World XY Plane - specifically, I’m wondering if there’s a way to write the script so that the 2D curves are oriented to their longest “side” running in the x direction.

I’ve attached a new sample cabinet file, with every object labeled as I mentioned above in my first question - so every object has a name, is on a specific layer, and belongs to a group which has a name.

Sample Cabinet_Keep Named Properties.3dm (2.5 MB)

Thanks again for all the help you’ve already given!

Yes, that should be possible, and not too hard, it’s easy enough to transfer any existing object name to the unrolled object and put it on a specific layer The question I have is if the unrolled objects are put in the same group as the original 3D panels, when you click on one of the flattened panels in the group they will all select - the 2D and the 3D. I’m not sure if that’s desired or not, it might lead to accidentally moving stuff?

Yes, also not too hard, basically just need to get the bounding box of the objects and re-orient the longest side along the X-axis if necessary.

I will look into it sometime soon.

Edit:
Hmm, I ran into a snag where grouping the dots with the original 3d panels causes a problem with the entire cabinet being grouped. It adds another layer of nested grouping so after adding the individual dot+panel groups, the entire cabinet no longer selects as a whole, as the top group is no longer the whole thing. I think there are some workarounds, but I have to figure out the logic. The rest of the stuff with the names/layers/orientation is done. Still working on it.

Love this stuff guys. Is there someone collecting all these joinery scripts in a GIT repo
? Would be a great idea. Im starting to learn rhino / python with joinery first so would love to contribute to a combined script library.

1 Like