[Python] Avoid waiting for mesh generation

Use Case:
Scenario 1:
You have closed polysurfaces exported from another CAD. Export them with IGES. Assume that is the only way and STEP is not supported from that other application.

You then import the IGES in Rhino. Polysurfaces are exploaded. If you try to use Python to find separate surfaces by name in order to join them into polysurfaces again the time it takes increases exponentially with the number of the polysurfaces. Due to waiting for mesh generation.

Scenario 2:
If you use STEP the join operation is executed upon import. Surfaces are joined really fast and the mesh generation happens after they are all joined.

Questions:

  1. Why does it take so much time to do this with Python and not with STEP?
  2. How can I avoid mesh generation during the join?

Thanks in advance.

Not sure if this is the reason, unless you are doing this by scripting rs.Command(“Join”) and watching stuff happen onscreen…

You should be able to get a collection of all the surfaces with the same name in a list and then use something like rs.JoinSurfaces() or better, dive down into RhinoCommon and do JoinBreps(). However, one of the problems is that Rhino internally still joins stuff sequentially as far as I know, each time a new brep is added to an existing brep it creates a new brep object.

STEP files already have the join info in them, so I imagine the joining process may be handled directly by the importer which may be more efficient.

Are you sure it’s the joining that takes the time and not the selection by name?
Which methods are you using for each?

it’s not even joining it’s the mesh generation.

I might as well be very wrong :slight_smile:

Here test these:
issue.igs (9.4 MB)
issue.stp (3.9 MB)
get_objects_names_v2.py (3.4 KB)

Import the stp and choose to join the surfaces. Check the time

Then import the igs (or just explode the polysurfaces that you already have in rhino and run the script. Select all surfaces and press Enter.

Last time I measured it took me 2h 40min for the script to complete.

Dunno, the following takes 0.42 seconds here for the iges file you posted. What did I miss/do wrong?

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

objs=rs.ObjectsByType(8)
tol=sc.doc.ModelAbsoluteTolerance

rs.EnableRedraw(False)
all_names=set([rs.ObjectName(obj) for obj in objs])
st=time.time()

for name in all_names:
    ids=rs.ObjectsByName(name)
    breps=[rs.coercebrep(obj) for obj in ids]
    if breps:
        joined=Rhino.Geometry.Brep.JoinBreps(breps,tol)
        if joined:
            new_ids=[sc.doc.Objects.AddBrep(jbrep) for jbrep in joined]
            for new_id in new_ids: rs.ObjectName(new_id,name)
            rs.DeleteObjects(ids)
print "Elapsed time is {:.2f}".format(time.time()-st)

Edit - sorry, forgot to re-name the new polysurfaces - now it’s 0.49 seconds

1 Like

You’re using RhinoCommon,

I am using rhinoscriptsyntax.

This is what I see so far.

Rhino common joins them without casting them (or whatever is called) then you cast them by adding them to the model by creating Breps.

This would mean the mesh generation happens after they are joined.

Then rhinoscriptsyntax performance is just abysmal.

Another thing I saw you’re disabling the redraw. Perhaps this also brings the performance down.

I don’t think your analysis is correct…

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

objs=rs.ObjectsByType(8)
tol=sc.doc.ModelAbsoluteTolerance

rs.EnableRedraw(False)
all_names=set([rs.ObjectName(obj) for obj in objs])
st=time.time()

for name in all_names:
    ids=rs.ObjectsByName(name)
    joined=rs.JoinSurfaces(ids,False)
    if joined:
        rs.ObjectName(joined,name)
        rs.DeleteObjects(ids)
print "Elapsed time is {:.2f}".format(time.time()-st)

0.54 seconds here.

The thing about using rs.JoinSurfaces() is that it returns only one object ID. If you have a collection of surfaces that are not all joinable, it will return None. Whereas with Rhino.Geometry.Brep.JoinBreps(), it will return everything it can join, that may be a list of more than one object.

I don’t quite understand why rs.JoinSurfaces() was deliberately limited in that way, there’s no real need for that, as it’s just calling Rhino.Geometry.Brep.JoinBreps() in the background anyway.

1 Like

Yes, it can, significantly.

1 Like

Thanks Mitch, I’ll test the scenario again with redraw disabled.

There are a lot of methods there that are weirdly developed like for example trim Curve it requires you to provide a float parameter or domain or something to show where to trim it instead of picking an object.

This is what lead me to creating this thread:

Another example is adding fillet. In RhinoCommon method you have a boolean that allows you to choose if you wanna trim the curves or extend them to make the fillet. And join them afterwards. In rhinoscriptsyntax you don’t have a choice you simply create an arc there no extend no trim no nothing. This is useless done like this. (to say the least)

I saw you marked the thread @wim,.

perhaps also add an issue to your tracking system. Some of the methods there are badly developed.

This is the same in RhinoCommon, most operations that need locations along a curve want curve parameters, not 3dpoints.

To be fair it is in line with the documentation for JoinSurfaces(): “Joins two or more surface or polysurface objects together to form one polysurface object.” (emphasis by me).

Yep - in line with the documentation, corresponds with the VB version and probably because historically it was designed that way a long time ago…

However, things could be different, in order to not invalidate the original method, perhaps we just need a new one like rs.JoinBreps()

Agreed, a new function would be better, maybe TryJoinSurfaces that returns all successful joins, even if there are more than one.

As long as the user can appreciate that the results aren’t necessarily what one would expect, like with JoinSurfaces()- either you get the one joined surface, or you don’t.