RhinoCommon UnrollSurface differs from UnrollSrf command

Have a look at the file in attachment. When I use the UnrollSrf command I get a flat polysurface with smooth edges. When I use RhinoCommons UnrollSurface I get a surface with ugly jaggered edges. How can I achieve the result from the UnrollSrf command using Rhinocommon?

For repeating the problem, try this code using Python:

import rhinoscriptsyntax as rs

obj = rs.GetObject()
rs.UnrollSurface(obj)

211214 UnrollSrf vs sr.UnrollSurface.3dm (116.0 KB)

Hi @siemen,

it uses the Absolute tolerance of your document which is 0.1 millimeters. You can either lower it under Document Properties > Units or set it in the function call:

import rhinoscriptsyntax as rs

obj_id = rs.GetObject("Surface to unroll", 8, True, False)
if obj_id:
    rc = rs.UnrollSurface(obj_id, False, None, 0.00001)

it takes longer to unroll but looks better.
_
c.

Hi @clement. Can you specify what uses Absolute tolerance?
When I use the document tolerance (0.1 mm) for rs.UnrollSurface I get the same crappy result with deviations of 1-1.5 mm and UnrollSurface command take the document units automatically and I get a smooth result? Why does this differ so much?

import rhinoscriptsyntax as rs

obj = rs.GetObject()
tol = rs.UnitAbsoluteTolerance()
rs.UnrollSurface(obj,False,None,tol)

It also like the UnrollSrf command creates a polysurface. How does one achieve that with rs.UnrollSurface?

Hi @siemen, my guess is that the _UnrollSrf command either does some tolerance reduction on it’s own and since it’s not merging the results as rs.UnrollSurface does, it’s outcome looks better. Probably the wobble you see is caused by the merging, i’m not sure.

@dale, can you look at this and explain why the results are wobbly, even with tight tolerances ?

thanks,
c.

It seems that the _UnrollSrf command has more going on behind the scenes.

This code produces the same results as the _UnrollSrf command. (Note: I’m using a GhPython component in grasshopper)

# input x type hint set to Brep

import Rhino

doc = Rhino.RhinoDoc.ActiveDoc

x.Faces.SplitFacesAtTangents()

for face in x.Faces:
    face.ShrinkFace(Rhino.Geometry.BrepFace.ShrinkDisableSide.ShrinkAllSides)

unroll = Rhino.Geometry.Unroller(x)
unroll.AbsoluteTolerance = doc.ModelAbsoluteTolerance
unroll.RelativeTolerance = doc.ModelRelativeTolerance

breps, curves, points, dots = unroll.PerformUnroll()

a = Rhino.Geometry.Brep.JoinBreps(breps, doc.ModelAbsoluteTolerance)

unroll_srf.gh (9.9 KB - Your geometry is internalized in this file)

-Kevin

1 Like

I reused your code to make it work outside of Grasshopper as below (a bit of a quick fix, I know). But I’m still getting issues with unrolling which the UnrollSrf command doesn’t get. See the example part I have below. Would be great if somebody from McNeel could help out here. @dale

import Rhino
import rhinoscriptsyntax as rs

obj = rs.GetObject()

brep = rs.coercebrep(obj)

brep.Faces.SplitFacesAtTangents()

for face in brep.Faces:
    face.ShrinkFace(Rhino.Geometry.BrepFace.ShrinkDisableSide.ShrinkAllSides)

unroll = Rhino.Geometry.Unroller(brep)
unroll.AbsoluteTolerance = Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance
unroll.RelativeTolerance = Rhino.RhinoDoc.ActiveDoc.ModelRelativeTolerance

breps, curves, points, dots = unroll.PerformUnroll()

a = Rhino.Geometry.Brep.JoinBreps(breps, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)

for i in a:
    Rhino.RhinoDoc.ActiveDoc.Objects.AddBrep(i)

rs.Redraw()


220208 UnrollSrf vs sr.UnrollSurface.3dm (506.8 KB)

Another one which doesn’t give the same result:


220209 UnrollSrf vs sr.UnrollSurface.3dm (189.5 KB)

I’ve tried a couple of things and made some observations.

Your original surface has an area of 4830641.3, whereas the unrolled, green one has 4831059.05 and the red one 4831058.85, which means that both procedures actually don’t unroll your surface super faithfully. I guess the unrolling of surfaces with non-zero Gaussian curvature is always somewhat of a guessing game, since they are not really developable in the traditional sense.

Furthermore, since you’re dealing with a doubly-curved surface, whose Gaussian curvature is not 0 and it thus is not developable, you probably shouldn’t use UnrollSrf.
It used to be exclusively for developable surfaces, which might be why Smash seems to get called in the background by the Rhino.Geometry.Unroller(). It must detect that you’re dealing with an unrollable surface and chose to well smash it instead?
Simply try Smash in Rhino. It yields the same result as your unrolled, red surface.

Instead of Smash, you could use the more modern Squish command, which is also available for scripting in the API.

It will always remain somewhat of a guessing game and playing around with tolerances though, unless you come up with some sort of algorithm that does that for you and searches for an optimal result by for instance area. Squish wants to stretch and/or compress your surface to make it unrollable. It’s a matter of finding the right settings and tolerance for the least deviation in area.

1 Like

Thanks a lot for your findings & insights @diff-arch

I realize that unrolling a surface with curvature in both directions creates something which is impossible to flatten without knowing the technical properties of the material it will be made of.

My tool copies the UnrollSurface command and adds some automation features to it. So I’d like it to copy what the UnrollSurface command does. Would be great if anybody could provide an example of how this is set up. Or at least an answer from McNeel that this is not possible so I know I’m not trying to do something that won’t work in RhinoCommon? @dale @pascal

Hi @siemen,

Maybe this is better (or different at least):

import Rhino
import scriptcontext as sc

def test_unroller():
    filter = Rhino.DocObjects.ObjectType.Surface | Rhino.DocObjects.ObjectType.PolysrfFilter
    rc, objref = Rhino.Input.RhinoGet.GetOneObject("Select surface or polysurface to unroll", False, filter)
    if not objref or rc != Rhino.Commands.Result.Success: 
        return
    
    brep = objref.Brep()
    if not brep:
        return
        
    bcopy = brep.DuplicateBrep()
    bcopy.Compact()
    bcopy.Faces.SplitFacesAtTangents()
    for f in bcopy.Faces:
        f.RebuildEdges(0.00001, True, True)
    for f in bcopy.Faces:
        f.ShrinkFace(Rhino.Geometry.BrepFace.ShrinkDisableSide.ShrinkAllSides)
    bcopy.CullUnusedSurfaces()
    for f in bcopy.Faces:
        if f.OrientationIsReversed:
            f.UnderlyingSurface().Reverse(1)
        
    unroll = Rhino.Geometry.Unroller(bcopy)
    unroll.AbsoluteTolerance = sc.doc.ModelAbsoluteTolerance
    unroll.RelativeTolerance = 0.01
    out_breps, curves, points, dots = unroll.PerformUnroll()
    if out_breps:
        for b in out_breps:
            sc.doc.Objects.AddBrep(b)
            
    sc.doc.Views.Redraw()

if __name__=="__main__":
    test_unroller()

The UnrollSrf command can be scripted too.

– Dale

3 Likes

Thanks @dale. I’ll have a look at this and try to understand what those different parts do.

Hi @dale
Just so I understand:

Why do you create a duplicate here to then shrink the faces of the original while you use the duplicate for the unroller? Is that duplicate still linked to the original somehow?

for f in bcopy.Faces:
        f.RebuildEdges(0.00001, True, True)

Is there a reason why you used a hard coded value as a tolerance here instead of document tolerance?

Typo…

I don’t know - this is just what the command does.

– Dale

Hi again @dale, running into another discrepancy between the UnrollSrf command and rhinocommon implementation. Could you share some insights in the joining process after unrolling as used by the UnrollSrf command?
When I use the JoinBrep method from RhinoCommon with the tolerance used in the RhinoScriptSyntax implementation, as I got explained by Willem in this topic, it doesn’t join 1 part of the unrolled surface whereas UnrollSrf returns one single polysurface. I tried on this file using the code below:

220226 UnrollSrfJoin.3dm (240.4 KB)

import Rhino
import scriptcontext as sc

def test_unroller():
    filter = Rhino.DocObjects.ObjectType.Surface | Rhino.DocObjects.ObjectType.PolysrfFilter
    rc, objref = Rhino.Input.RhinoGet.GetOneObject("Select surface or polysurface to unroll", False, filter)
    if not objref or rc != Rhino.Commands.Result.Success: 
        return
    
    brep = objref.Brep()
    if not brep:
        return
        
    bcopy = brep.DuplicateBrep()
    bcopy.Compact()
    bcopy.Faces.SplitFacesAtTangents()
    for f in bcopy.Faces:
        f.RebuildEdges(0.00001, True, True)
    for f in bcopy.Faces:
        f.ShrinkFace(Rhino.Geometry.BrepFace.ShrinkDisableSide.ShrinkAllSides)
    bcopy.CullUnusedSurfaces()
    for f in bcopy.Faces:
        if f.OrientationIsReversed:
            f.UnderlyingSurface().Reverse(1)
        
    unroll = Rhino.Geometry.Unroller(bcopy)
    unroll.AbsoluteTolerance = sc.doc.ModelAbsoluteTolerance
    unroll.RelativeTolerance = 0.01
    out_breps, curves, points, dots = unroll.PerformUnroll()
    
    if out_breps:
        joinBrep = Rhino.Geometry.Brep.JoinBreps(out_breps,sc.doc.ModelAbsoluteTolerance*2.1)
    
    if joinBrep:
        for b in joinBrep:
            sc.doc.Objects.AddBrep(b)
    sc.doc.Views.Redraw()

if __name__=="__main__":
    test_unroller()

Hi @siemen,

UnrollSrf joins faces in this manner:

if out_breps:
    joined_brep = Rhino.Geometry.Brep()
    for b in out_breps:
        joined_brep.Append(b)
    joined_brep.JoinNakedEdges(0.0)

    sc.doc.Objects.AddBrep(joined_brep)
    sc.doc.Views.Redraw()

– Dale

1 Like

Thanks @dale

Hi @dale
It’s very helpful to me !Thanks a lot .