I’m writing a plugin that should unroll sheet metal part (closed polysurface) by mid-surface.
Mid-surface is located at half thickness of the part and I need to create this surface somehow.
I have an idea to create it by offsetting the largest surface within polysurface for half of the thickness.
Each side of my part is a single surface, so the largest surface is an outside “shell”
I came to the point where I need to implement the offset command on this surface but I don’t know how to “tell it” exactly on which side I need the offset.
Maybe you can offset both sides, compare the two resulting surfaces and use the smallest. On the other hand, if you have a closed polysurface, the normals are facing outward, so you need the offset to be negative
For example (btw: no checks if it is unrollable):
import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc
def unrollSheet():
poly=rs.GetObject("Select sheet to unroll",rs.filter.polysurface)
if poly==None:
return
thickness=rs.GetReal("Enter sheet thickness",3,1,20)
if thickness==None:
return
poly=rs.coercebrep(poly)
surfaces=poly.Faces
area=0
for srf in surfaces:
mp=Rhino.Geometry.AreaMassProperties.Compute(srf)
area_temp=mp.Area
if area<area_temp:
area=area_temp
new_srf=srf
offset=new_srf.Offset(-thickness,0.01)
unroll = Rhino.Geometry.Unroller(offset)
breps, curves, points, dots = unroll.PerformUnroll()
rc = [sc.doc.Objects.AddBrep(brep) for brep in breps]
sc.doc.Views.Redraw()
unrollSheet()
I have closed polysurface 99.99% of the time so negative half thickness offset gives me the the midsurface that I need to develop.
For the remaining 0.01% i will do the both side offset ant take the smaller surface
(currently not implemented in the script bellow )
For some reason your example unrolls the “untrimmed version” of the surface.
anyhow, here is my current version of the test script:
import rhinoscriptsyntax as rs
def TestExpander():
# Input
part = rs.GetObject("Select part for expansion" , rs.filter.polysurface or rs.filter.surface )
if part is None:
print ("Nothing Selected")
return
thickness=rs.GetReal("Enter sheet thickness",3,1,20)
if not thickness:
return
# Find largest surface
arrSrfs = rs.ExplodePolysurfaces(part)
if not arrSrfs:
# single surface selected
theSurface = part
else:
areaMax=0
theSurface = None
for srf in arrSrfs:
tempSrf=rs.SurfaceArea(srf)
if tempSrf>areaMax:
areaMax = tempSrf
theSurface = srf
if not theSurface:
print "no surface found!"
return
offset = rs.OffsetSurface(theSurface,-thickness/2)
if not offset:
print "error offseting"
return
unRolled = rs.UnrollSurface(offset)
if not unRolled:
print "error unrolling"
return
# CleanUp temporary objects
if arrSrfs:
rs.DeleteObjects(arrSrfs)
if offset:
rs.DeleteObject(offset)
if __name__=="__main__":
TestExpander()
I had to dive into RhinoCommon a bit deeper to understand what’s happening when working with polysurfaces faces and why it gave back the untrimmed surface. The script below does return the trimmed version of your surface.
What I like about doing it through use of rhino common is that the script performs actions on Rhino objects without the need to ‘bake’ them to the scene. This prevents you from doing the cleanup of unwanted geometry at the end of the script.
import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc
def unrollSheet():
poly=rs.GetObject("Select sheet to unroll",rs.filter.polysurface)
if poly==None:
return
thickness=rs.GetReal("Enter sheet thickness",3,1,20)
if thickness==None:
return
poly=rs.coercebrep(poly)
faces=poly.Faces
area=0
for face in faces:
mp=Rhino.Geometry.AreaMassProperties.Compute(face)
area_temp=mp.Area
if area<area_temp:
area=area_temp
new_face=face
distance = -thickness/2
tolerance = sc.doc.ModelAbsoluteTolerance
both_sides=False
create_solid=False
offset = Rhino.Geometry.Brep.CreateFromOffsetFace(new_face, distance, tolerance, both_sides, create_solid)
unroll = Rhino.Geometry.Unroller(offset)
breps, curves, points, dots = unroll.PerformUnroll()
srfs = [sc.doc.Objects.AddBrep(brep) for brep in breps]
sc.doc.Views.Redraw()
unrollSheet()