discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Unwrap a polyhedron

RP
Ronaldo Persiano
Thu, Apr 1, 2021 2:33 PM

With immutable variables, the only way I see to mark already processed
triangles is to generate the whole thing with new marks. And that is
inefficient.

With immutable variables, the only way I see to mark already processed triangles is to generate the whole thing with new marks. And that is inefficient.
G
gilboonet
Thu, Apr 1, 2021 3:28 PM

I think I have a little chance to make it if I can create a list of neighbors
to unfold starting from the first one that I choose. It starts with its
direct neighbours, then continue with their neighbors and so on, but I
cannot see how to create that with OpenSCAD, I need to read more about list
comprehension.

--
Sent from: http://forum.openscad.org/

I think I have a little chance to make it if I can create a list of neighbors to unfold starting from the first one that I choose. It starts with its direct neighbours, then continue with their neighbors and so on, but I cannot see how to create that with OpenSCAD, I need to read more about list comprehension. -- Sent from: http://forum.openscad.org/
N
NateTG
Thu, Apr 1, 2021 5:11 PM

gilboonet wrote

I think I have a little chance to make it if I can create a list of
neighbors
to unfold starting from the first one that I choose. It starts with its
direct neighbours, then continue with their neighbors and so on, but I
cannot see how to create that with OpenSCAD, I need to read more about
list
comprehension.
...

I imagine the easiest way to do something like that in OpenSCAD would be
recursion.  For example with a function that takes a partial net, tries to
add one face to the net, and then calls itself with the new partial net if
it works.

There are relatively simple shapes (like a small cube stacked on top of the
center of a big cube) that don't have 'one piece' nets.  So it might make
sense to take a "breadth first" approach to flattening without overlaps, and
then looking for minimal cuts to make printable pieces from it.

--
Sent from: http://forum.openscad.org/

gilboonet wrote > I think I have a little chance to make it if I can create a list of > neighbors > to unfold starting from the first one that I choose. It starts with its > direct neighbours, then continue with their neighbors and so on, but I > cannot see how to create that with OpenSCAD, I need to read more about > list > comprehension. > ... I imagine the easiest way to do something like that in OpenSCAD would be recursion. For example with a function that takes a partial net, tries to add one face to the net, and then calls itself with the new partial net if it works. There are relatively simple shapes (like a small cube stacked on top of the center of a big cube) that don't have 'one piece' nets. So it might make sense to take a "breadth first" approach to flattening without overlaps, and then looking for minimal cuts to make printable pieces from it. -- Sent from: http://forum.openscad.org/
N
NateTG
Thu, Apr 1, 2021 11:55 PM

Here's a quick and dirty unfolder that I wrote. It  uses a stupid method for
generating the unfolding tree that is unlikely to scale well.  I think
recursion is necessary to make one with better scaling behavior.

I also don't have fancy polyhedra to hand for testing it, but it's still
cute with animation on.

unfold.scad http://forum.openscad.org/file/t2140/unfold.scad

--
Sent from: http://forum.openscad.org/

Here's a quick and dirty unfolder that I wrote. It uses a stupid method for generating the unfolding tree that is unlikely to scale well. I think recursion is necessary to make one with better scaling behavior. I also don't have fancy polyhedra to hand for testing it, but it's still cute with animation on. unfold.scad <http://forum.openscad.org/file/t2140/unfold.scad> -- Sent from: http://forum.openscad.org/
A
adrianv
Fri, Apr 2, 2021 1:40 AM

I don't think that this process is particularly horrible in terms of
efficiency.  You have the polygon as a list of vertices and faces.  You
first need to construct a list of the faces that are across each edge.
This can be done by enumerating the edges and then sorting the list.  Then
you can look at a face and find the faces that are adjacent with a simple
list index.  Tracking which faces have been placed already does of course
require updating a list, but it doesn't have to be a huge data structure,
just a simple list with one boolean entry per face.  I wouldn't think you'd
want to do thousands of polygons, but this would be fine for moderate sized
cases, certainly it should be doable for <100 faces.  Updating a length 100
list of booleans isn't going to be a huge time suck.  I think checking
intersection is probably the most time consuming step, because you'll have
to check lots of segments as more of the net is assembled.

There are only two ways you could do this: recursion for C-style for loop.
The C-style for loop is paradoxically slower than recursion in my tests, and
can be harder to use than one would expect.  I would do it with recursion.
It seems like the most complicated aspect of this would be maintaining a
stack, because you need to be able to pop the stack to return to the last
branch point as you search the tree.

Here's how it's possible to compute the faces that are associated with edges
using the BOSL2 library.  This runs in 3s with 3500 faces, which should be a
lot more than you'd ever want to make a net for.

include<BOSL2/std.scad>

// Returns list of edges in sorted order with the pair of faces for each
edge
// edge table has the form
//    [[v1,v2],[f1,f2]]    the edge [v1,v2] connects f1 to f2
//                          vertices are indices into the vertex list and
faces
//                          indices into face list
// facetable
//    entry i lists the edges that compose face i as indices into the edge
table above
function edge_faces(vnf) =
let(
faces=vnf[1],
// [v1,v2,face_index]
edgelist= [ for(faceind=idx(faces), j=[0:len(faces[faceind])-1])
let(
v1=faces[faceind][j],
v2=faces[faceind][(j+1)%len(faces[faceind])]
)
v1<v2 ? [v1,v2,faceind,j] : [v2,v1,faceind,j]
],
sedge = sort(edgelist),
edgetable = [for(i=[0:2:len(sedge)-1])
assert(sedge[i][0]==sedge[i+1][0] &&
sedge[i][1]==sedge[i+1][1],
str("Found unpaired edge, ",
sedge[i-1]," ",sedge[i]," ",
sedge[i+1]," ",sedge[i+2],"
",sedge[i+3]," ",sedge[i+4]," ",len(sedge)))
//  add check for too many of the same edge?
[select(sedge[i],0,1), [sedge[i][2], sedge[i+1][2]]]],
facetable = group_data(subindex(sedge,2), [for(i=idx(sedge))
floor(i/2)], sortidx(sedge,[2,3]))
)
[edgetable,facetable];

// Function: group_data
// Given a vector of group numbers and a vector of values, produces an
// output with all the values in each group: [[v0_0,v0_1,v0_2,..],
[v1_0,v1_1,v1_2,...],...]
// The first list in the output will be all items corresponding to group 0,
the second list group 1, and
// so on.  If no members exist for a group the empty list is output.
function group_data(group, value, sidx) =
assert(is_list(group) && is_list(value) &&
len(group)==len(value),"Must provide lists of matching length")
let(
sidx = is_def(sidx) ? sidx : sortidx(group),
cuts =[each repeat(0,1+group[sidx[0]]),
for(i=[1:len(group)-1]) let(delta =
group[sidx[i]]-group[sidx[i-1]])  each if (delta>0) repeat(i,delta),
len(group)]
)
[for(i=[0:len(cuts)-2])
[for(j=[cuts[i]:1:cuts[i+1]-1]) value[sidx[j]]]];

Ronaldo wrote

With immutable variables, the only way I see to mark already processed
triangles is to generate the whole thing with new marks. And that is
inefficient.


OpenSCAD mailing list
To unsubscribe send an email to

discuss-leave@.openscad

I don't think that this process is particularly horrible in terms of efficiency. You have the polygon as a list of vertices and faces. You first need to construct a list of the faces that are across each edge. This can be done by enumerating the edges and then sorting the list. Then you can look at a face and find the faces that are adjacent with a simple list index. Tracking which faces have been placed already does of course require updating a list, but it doesn't have to be a huge data structure, just a simple list with one boolean entry per face. I wouldn't think you'd want to do thousands of polygons, but this would be fine for moderate sized cases, certainly it should be doable for <100 faces. Updating a length 100 list of booleans isn't going to be a huge time suck. I think checking intersection is probably the most time consuming step, because you'll have to check lots of segments as more of the net is assembled. There are only two ways you could do this: recursion for C-style for loop. The C-style for loop is paradoxically slower than recursion in my tests, and can be harder to use than one would expect. I would do it with recursion. It seems like the most complicated aspect of this would be maintaining a stack, because you need to be able to pop the stack to return to the last branch point as you search the tree. Here's how it's possible to compute the faces that are associated with edges using the BOSL2 library. This runs in 3s with 3500 faces, which should be a lot more than you'd ever want to make a net for. include<BOSL2/std.scad> // Returns list of edges in sorted order with the pair of faces for each edge // edge table has the form // [[v1,v2],[f1,f2]] the edge [v1,v2] connects f1 to f2 // vertices are indices into the vertex list and faces // indices into face list // facetable // entry i lists the edges that compose face i as indices into the edge table above function edge_faces(vnf) = let( faces=vnf[1], // [v1,v2,face_index] edgelist= [ for(faceind=idx(faces), j=[0:len(faces[faceind])-1]) let( v1=faces[faceind][j], v2=faces[faceind][(j+1)%len(faces[faceind])] ) v1<v2 ? [v1,v2,faceind,j] : [v2,v1,faceind,j] ], sedge = sort(edgelist), edgetable = [for(i=[0:2:len(sedge)-1]) assert(sedge[i][0]==sedge[i+1][0] &amp;&amp; sedge[i][1]==sedge[i+1][1], str(&quot;Found unpaired edge, &quot;, sedge[i-1],&quot; &quot;,sedge[i],&quot; &quot;, sedge[i+1],&quot; &quot;,sedge[i+2],&quot; &quot;,sedge[i+3],&quot; &quot;,sedge[i+4],&quot; &quot;,len(sedge))) // add check for too many of the same edge? [select(sedge[i],0,1), [sedge[i][2], sedge[i+1][2]]]], facetable = group_data(subindex(sedge,2), [for(i=idx(sedge)) floor(i/2)], sortidx(sedge,[2,3])) ) [edgetable,facetable]; // Function: group_data // Given a vector of group numbers and a vector of values, produces an // output with all the values in each group: [[v0_0,v0_1,v0_2,..], [v1_0,v1_1,v1_2,...],...] // The first list in the output will be all items corresponding to group 0, the second list group 1, and // so on. If no members exist for a group the empty list is output. function group_data(group, value, sidx) = assert(is_list(group) &amp;&amp; is_list(value) &amp;&amp; len(group)==len(value),&quot;Must provide lists of matching length&quot;) let( sidx = is_def(sidx) ? sidx : sortidx(group), cuts =[each repeat(0,1+group[sidx[0]]), for(i=[1:len(group)-1]) let(delta = group[sidx[i]]-group[sidx[i-1]]) each if (delta>0) repeat(i,delta), len(group)] ) [for(i=[0:len(cuts)-2]) [for(j=[cuts[i]:1:cuts[i+1]-1]) value[sidx[j]]]]; Ronaldo wrote > With immutable variables, the only way I see to mark already processed > triangles is to generate the whole thing with new marks. And that is > inefficient. > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to > discuss-leave@.openscad -- Sent from: http://forum.openscad.org/
G
gilboonet
Fri, Apr 2, 2021 6:39 AM

NateTG wrote

Here's a quick and dirty unfolder that I wrote.

Thanks a lot for that script. I need to dig it as at first look I didn't
understand it.

NateTG wrote

I also don't have fancy polyhedra to hand for testing it

I have lots of low poly models that I usually use for my tests; it's easy to
grab polyhedron data from them :

--
Sent from: http://forum.openscad.org/

NateTG wrote > Here's a quick and dirty unfolder that I wrote. Thanks a lot for that script. I need to dig it as at first look I didn't understand it. NateTG wrote > I also don't have fancy polyhedra to hand for testing it I have lots of low poly models that I usually use for my tests; it's easy to grab polyhedron data from them : - open this link <https://gilboonet.github.io/OpenJSCAD.org/packages/web/scripts.html> into your internet browser - choose a model from the (choisir volume) list - choose JSCAD or js from the first list (after "Ready"), save the file The data are into faces and vertices arrays -- Sent from: http://forum.openscad.org/
G
gilboonet
Fri, Apr 2, 2021 7:04 AM

adrianv wrote

Here's how it's possible to compute the faces that are associated with
edges
using the BOSL2 library.  This runs in 3s with 3500 faces, which should be
a
lot more than you'd ever want to make a net for.

Thank you too for the script, apparently recursion is the way to go. That's
very interesting for me as it introduces me to BOSL. 3500 faces is lots more
than my usual model, that's great that it runs so fast. Now I need to try to
understand your code. About running time of the process, I think that it's
only the unfolding that takes time, the rendering of the net with all
numbers also takes lot of time. At first I did it with text primitive, then
added a function to cache text injection, then replaced the built-in text
polygons with a minimalistic set of polygons, and finally (but not only to
improve speed, also to improve rendering quality) I rendered the net with
svg injection. With a 2000 faces model I ended with a svg containing 13K
lines (700 Ko).

--
Sent from: http://forum.openscad.org/

adrianv wrote > Here's how it's possible to compute the faces that are associated with > edges > using the BOSL2 library. This runs in 3s with 3500 faces, which should be > a > lot more than you'd ever want to make a net for. Thank you too for the script, apparently recursion is the way to go. That's very interesting for me as it introduces me to BOSL. 3500 faces is lots more than my usual model, that's great that it runs so fast. Now I need to try to understand your code. About running time of the process, I think that it's only the unfolding that takes time, the rendering of the net with all numbers also takes lot of time. At first I did it with text primitive, then added a function to cache text injection, then replaced the built-in text polygons with a minimalistic set of polygons, and finally (but not only to improve speed, also to improve rendering quality) I rendered the net with svg injection. With a 2000 faces model I ended with a svg containing 13K lines (700 Ko). -- Sent from: http://forum.openscad.org/
N
NateTG
Sat, Apr 3, 2021 3:40 PM

gilboonet wrote

I have lots of low poly models that I usually use for my tests; it's easy
to
grab polyhedron data from them :

  • open  this link
    <https://gilboonet.github.io/OpenJSCAD.org/packages/web/scripts.html>
    into
    your internet browser
  • choose a model from the (choisir volume) list
  • choose JSCAD or js from the first list (after "Ready"), save the file
    The data are into faces and vertices arrays

Thanks, that helped me discover a bug.  Line 51 of the script that I
uploaded should have been:

theta=$tatan2(untouw,unto*unfrom)

rather than

theta=$t*-((nfrom*nto > 0)? ...

--
Sent from: http://forum.openscad.org/

gilboonet wrote > I have lots of low poly models that I usually use for my tests; it's easy > to > grab polyhedron data from them : > - open this link > &lt;https://gilboonet.github.io/OpenJSCAD.org/packages/web/scripts.html&gt; > into > your internet browser > - choose a model from the (choisir volume) list > - choose JSCAD or js from the first list (after "Ready"), save the file > The data are into faces and vertices arrays Thanks, that helped me discover a bug. Line 51 of the script that I uploaded should have been: theta=$t*atan2(unto*uw,unto*unfrom) rather than theta=$t*-((nfrom*nto > 0)? ... -- Sent from: http://forum.openscad.org/