Bug? mesh.Smooth() it is controlled by SMOOTH command settings

Hi guys, I have a script where I use

coordinateSystem = Rhino.Geometry.SmoothingCoordinateSystem(2)
plane = Rhino.Geometry.Plane.WorldXY
mesh.Smooth(grips,1,True,True,True,False, coordinateSystem, plane)

And I see that if I have ran the Smooth command in Rhino first and changed it’s setting to exclude some directions, then that affects the mesh.Smooth() too…

(to sum it up: The command Smooth’s settings overrides the mesh.Smooth() setttings)

I presume that is not as intended.

Hi @Holo,

Can you provide a full python code sample, plus a sample mesh, that isn’t smoothing?

Thanks,

– Dale

Hi Dale, sure,
This is the backbone of our most important internal script right now, so to me it is top priority, so here is a custom script made just for you :slight_smile:

import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc
import time

mesh_id = rs.GetObject("mesh to smooth", rs.filter.mesh, preselect=True)
if mesh_id:
    ### Make a mesh to smooth
    mesh = rs.coercemesh(mesh_id)
    ### make a list for vertice indicators
    vertexIndices = []
    ### add them all
    nrV = len(mesh.Vertices)
    for i in range(nrV):
        vertexIndices.append(i)
    ### setup the variables to smooth 100% in all directions according to world
    smoothFactor = 1
    bXSmooth = True
    bYSmooth = True
    bZSmooth = True
    bFixBoundaries = True
    coordinateSystem = Rhino.Geometry.SmoothingCoordinateSystem(0)
    plane = rs.WorldXYPlane()
    ### Set up timers for feedback
    runTime = time.time()
    startTime = time.time()
    ### Set it up to smooth 1/10 of the number of vertices
    nrPasses = int(nrV/10)
    ### Make sure it smooths at least 10 times
    if nrPasses < 10:
        nrPasses = 10
    ###
    ### Smooth the mesh
    ###
    for i in range(nrPasses):
        mesh.Smooth(vertexIndices, 1, bXSmooth, bYSmooth, bZSmooth, bFixBoundaries, coordinateSystem, plane)
        
        ###
        ### Make the script escapable and give feedback 
        ###
        if sc.escape_test(False):
            print "ESC pressed"
            break
        ### Update the user how it is going on because blank screens are horrible
        if time.time()-runTime > 0.5:
            rs.Prompt("Smoothing pass "+str(i)+" of "+str(nrPasses)+" - Press ESC to terminate" )
            runTime = time.time()
    
    ### Add the mesh, update the view and give feedback
    sc.doc.Objects.AddMesh(mesh)
    sc.doc.Views.Redraw()
    print "Completed in "+str(round(time.time()-startTime,2))+" sec"

And here is a super simple mesh to test it on:

mesh.Smooth bug.3dm (60.4 KB)

Now here is what I want you to test:
1 the all is good test:
Run the command SMOOTH on the mesh in Rhino, make sure XYZ are all on.
Undo and run the script on the same mesh. (ALL IS GOOD!)

2 this is the problem test:
Undo and now Run the command SMOOTH with ONLY Z on.
Undo and run the script and now see that X and Y is ignored now as well, even if it says True in the script.

So for some strange reason the SMOOTH command overrides the script settings.

(And an OT question: Why is running the command SMOOTH much faster on say 500 smoothings than if I do the same in the script? To see this you would need a larger mesh, so please make a 100x100 mesh heightfield from image to test it, if you need it. And IMO it would be great if the mesh.Smooth could have a “number of passes” option to it so I don’t need to put it in a loop)

Here you can see an example of how we use mesh.Smooth:
I have made a script that takes input curves as a boundrycurve + breaklines + holes and then pulls points from these + populates within the boundrycurve with points (minus the holes minus a given distance to all curves) then builds a mesh of all of this, and then gets all the “inbetween” vertices and smoothes those.
Then it builds the edgemesh and give it thickness and groups it all, all in one go.

And the latest revision now tags the curves and the mesh with data so I can adjust them, select the group and hit the same button for the command to automatically update the geometry (I use replace so materials, layers etc are kept) And I can remove curves and add new ones, or retag them so I can change a hole to a breakline etc.

It’s the most complex thing I have made so far, but it’s robust and fast, and not impossible to maintain either… yeah, I have finally learned to use definitions and to annotate so the future me can understand what I did :wink:

The input is just boundry and the light curves as breaklines.

And I have another script to pull the top off the mesh and make height curves from those.
Here shown with transparency, I love that “hidden option” for curves.
And another script that tags it with IFC data, gives it materials and puts it on layers.

So having control over how the smoothing works would be great.

Hi @Holo,

Thanks for providing the sample. There is a pretty obvious typo in the internal SDK function, clearly my fault. A fix should be available in next week’s Rhino 7 SR18 RC.

https://mcneel.myjetbrains.com/youtrack/issue/RH-67886

Thanks for reporting,

– Dale

Awesome! Thanks for handling this so fast Dale!

And a million thanks for your incredible stamina regarding all my noob questions during the last decade, you have really changed my life in many ways. And thanks to everybody else who have contributed too of course (and all our partners who endure periods of time with absentmindedness due to single tracked, problem solving man-cave-attributes :wink: )

3 Likes

Hi Dale, I have worked a lot on the script lately, but the slowness of mesh.Smooth compared to Rhino’s “Smooth” command boggles me.

The mesh in the image is just a HeightField that turned out a 200x200 mesh. (Any image will do, so easy to repeat)

When I use Rhino’s command it is swift and I guess it takes 0.2 seconds to complete (I can drag the sliders in close to realtime)
But when I use Python it takes 2.2 seconds, can you please look into what causes that?
I would love for my scripts to work much faster on large terrains.

Here is the script so you don’t have to type it in again:

import rhinoscriptsyntax as rs
import Rhino
import time
import Rhino.Geometry.Mesh as mesh
import scriptcontext as sc

plane = rs.WorldXYPlane()
coordinatesystem = Rhino.Geometry.SmoothingCoordinateSystem.World


mesh_id = rs.GetObject("mesh",rs.filter.mesh,preselect=True)
if mesh_id:
    mesh = rs.coercemesh(mesh_id)
    vertices = mesh.Vertices
    
    list = range(0,len(vertices)-1)
    
    startTime = time.time()
    for i in range(100):
        mesh.Smooth(list, 1, True, True, True, True, Rhino.Geometry.SmoothingCoordinateSystem.World, plane)
    print time.time()-startTime
    sc.doc.Objects.Replace(mesh_id,mesh)
    sc.doc.Views.Redraw()

Hi @Holo,

This “should” be fixed in this week’s Rhino WIP.

https://mcneel.myjetbrains.com/youtrack/issue/RH-71530

If not, then next week.

– Dale

1 Like

Wow, that was fast! -3 days, a new record! :smile:
Too bad it is v8 only, but it makes the future look bright though!

You might give it a try - to keep me honest.

– Dale

I will! I use v8 for special tasks due to python 3, but I can’t rely on it or port my tools there yet for every day use. Thanks a lot!

Hey, I forgot to reply to this.

Here are my results on your script
(slightly updated to work on a larger mesh and with more iterations and to work on both v7 and v8)

Rhino 7: 6.27 sec
Rhino 8: 0.722 sec
= 8.7x faster!

import scriptcontext as sc
import rhinoscriptsyntax as rs
import Rhino
import time

def test():
    p0 = Rhino.Geometry.Point3d(0, 0, 0)
    p1 = Rhino.Geometry.Point3d(10, 10, 10)
    bbox = Rhino.Geometry.BoundingBox(p0, p1)
    mesh = Rhino.Geometry.Mesh.CreateFromBox(bbox, 40, 40, 40)
    cs = Rhino.Geometry.SmoothingCoordinateSystem.World
    pl = Rhino.Geometry.Plane.WorldXY
    startTime=time.time()
    if rs.ExeVersion()==7:
        for i in range(1000):
            mesh.Smooth(0.2, True, True, True, True, cs, pl)
    elif rs.ExeVersion() >=8:
        mesh.Smooth(0.2, 1000, True, True, True, True, cs, pl)
    if mesh:
        sc.doc.Objects.Add(mesh)
        sc.doc.Views.Redraw()
    
    print round(time.time()-startTime,3),"sec"
test()

With a 60x60x60 cube I get 14.8 vs 1.6 sec, so 9.25x faster.
and 100x100x100 is 46 vs 4.475 sec, so 10.2x faster

Fantastic work, thank you!

1 Like