Any progress in SubD methods?

Hi all.

Sometime I check whether or not SubD rhinocommon method have been worked on or not.
… seems not?

I mainly use c# scripts inside gh (which hugely scare me how it’s clunky and slow, currently), but I plan to convert lots of my definitions to plain c# scripts to use as commands in Rhino.

SubD.Vertices is still not a list to simply interact with, with index and such. Still that .First() and .Next() …
(Even gh component “Point Deform” still can’t handle SubDs … :confused:)
SubD vertices tags. Edge crease status, etc etc
Topology?
Custom face packing when doing ToNurbs()?

Everything is far from accessible.
How can we develop new methods for SubDs?
There are any example that go deep?

1 Like

Hi @maje90,

I’m actually adding new SubD APIs to RhinoCommon right now (like this one: RH-76637 RhinoCommon SDK: Allow for setting the limit surface position of one vertex in a SubD). We’ve been slowly adding more SubD methods to the SDK for Rhino 8, mostly on SubD creation and component tagging, and it feels like the main thing missing now are certain big SubD commands like bridge.

Are C# scripts inside GH slower now than they were before, or are you talking about GH in general? Any example of something that you think should be faster?

SubD vertices are not indexed with a continuous list of indices (for example, you can make a valid SubD that has 4 vertices with indices 1, 3, 4, and 5). That makes it difficult to store them in a C# array, because they expect all indices to be valid: if we stored these SubD vertices in a C# array where the indices are the vertices’ index, we would end up with the array [Null, v1, Null, v3, v4, v5], which would lead to all kinds of confusion and inefficiencies.

The normal way to go through some or all of the components in a SubD is to use these iterators:

vertex = subd.Vertices.First
while vertex is not None:
    doSomething(vertex)
    vertex = vertex.Next

And the normal way to get a vertex with a certain Id in a SubD will always be subd.Vertices.Find(id). This method is faster than iterating through an array of SubD vertices, because vertices are organized in a way that lets us find one vertex in a big list of vertices with just a couple of memory reads.

What needs to be done here is: making subd.Vertices an iterable, so Python syntaxic sugar like for vertex in subd.Vertices: works, and give access to the neighborhood iterators from C#.

That has worked in RhinoCommon for a long time now, as well as in Grasshopper components (although I’ll admit these components are a little clunky).

I’m not sure what you are referring to here? You can add/delete SubD components from RhinoCommon now.

That’s not in here, true. What would you expect of the SDK here? Would it be enough if you were able to tag each face with a face group id, and the face packer would use these if they represented a valid face packing? This is an area fraught with peril and I would rather improve what Rhino is doing for face packing than support a complex API here.

1 Like

Preface:
it’s incredible for me that we are discussing this.
What I would expect as response is someone telling me: “yes, subd methods are a mess and our target is to make an overhaul of the whole situation”.

I’m mainly scripting with C# scripts inside GH.
Maybe the SDK via c++ is much better? I don’t know. Never informed myself about it.


Do a honest comparison between Mesh methods and current SubD methods. The difference is insane.
The “average joe” like me can literally play with meshes via rhinocommon/c#… having fun while developing!
With SubD after 5 minutes you want to give up and change job! :smiling_face_with_tear:


… with SubDs we work always with the Control Points2023-09-09 12_10_45-Window
We never use "Edit points"2023-09-09 12_10_53-Window
Editing surface points is not useful for us… even less if it’s 1 by 1!

On rhino, you turn on control points, select a whole set of 100-1000 control points, and move/transform them all simultaneously, in a split second! Those are simple 3D points with an index/ID/whatever.
Iterating with a .First or .Next or by .Find( … is the opposite of a solution, really!

I can’t believe it’s impossible to access simple 3d points locations as an index-accessible list or array.
Same with edges and faces.
With meshes we can create methods that work with indexes of vertices, edges, faces, even in multithread! Topology (where is the topology???), etc etc …

There is absolutely no reason we shouldn’t be able to work with SubD in the same way.
It still feels an incomplete type of geometry.

Rhino 7, release day: best way to work with SubD in c#/rhinocommon is to cast them to Mesh (where creases are unweld edges) , work it as meshes, at the end cast them back to SubD.
Rhino 8 today: exact same situation. :unamused:

If I’m wrong, please tell me why and how!
Provide me a decent example of a deep/heavy SubD manipulation via code.
Show me how using .Find() is any good for any complex script.
It isn’t for me. I tried.
For example, this, is a decent result of Mesh manipulation via rhinocommon methods. I want to do something similar with SubDs!


2023-09-09 12_57_13-Window
WHAT?
Are SubD made with sticks and marshmallows?
Or maybe SubD are indeed made with control points??? (from common user point of view)
This confirms the feeling: your SubD methods are so incomplete that you didn’t even bother TRY to complete gh “Point Deform” component. Anybody would expect this to be working since Rhino 7 release day. I hope you will not try to release 8 without solving this… again!


Absolutely. I agree.
But please do something.
As someone that uses SubD manually on rhino like 4 days/week: we need it.
Again: we need it since Rhino 7 release day. It’s late!

_SetPerFaceColorByFacePack set the colors? Good.
The same way users can see future packing, users should be able to manually sub-select faces and simply change object color. The ToNurbs command, then, should check colors and, if possible, respect the colors while packing. Maybe even with the option to “extract” as Surface/Brep only a portion of the SubD.
Nobody even discussed about this? I have many ideas… I hope you have more!

1 Like

Sorry, it’s OT, my fault. GH script editor feels clunky, slow, stuttering to edit. I’m not talking about actual code executing performance.
Why, if you first open it, it rearrange its code and UI shape? You have to wait a second or two while it “sort itself” before starting to work.
If you even try to click it before it finish (easier on old machines), it will stuck/kill the whole Rhino UI. I’ve already reported this.

No methods descriptions. No icon difference between types, methods, etc etc…
Why can I delete Script_Instance and RunScript “stucture” (brackets and stuff)??
Why using statements are not collapse-able now?

“X” and “Ok” buttons. On 7 meant “Close and discard edits” and “Close and keep edits”… now on 8?
You do a wrong edit… want to Undo it? Too bad, not a single script code edit is saved by GH now!
How can anybody even try to test GH scripts with this situation?
Did any dev even try using GH c# script in WIP for more than 5 rows of code?

I’m sorry to be salty… this is far from ready to work.

1 Like

Please, I need a reply.
I know I was hard with my words. I’m frustrated. Sorry.


There will be any changes in SubD c#-accessible methods?

If the situation is doomed to stay like this (really similar of 7 release) , I might as well start hitting my head over it and start coding.

… but, if you expect to make some changes, I want to hopefully wait and postpone everything. I’ll wait!


I mainly test my code directly from c# scripts inside gh. Currently it’s a mess. :smiling_face_with_tear:

I think this might be out of my league, but I’ll try… one of the long-standing frustrations with working with BReps and meshes is that the list of indices is fragile. If you add a mesh face to a 200,000 face mesh, it’s almost guaranteed that the indexes you had for specific vertices are now invalid. The same is true when joining a BRep to another - geometrically unchanged objects become scrambled.

With SubDs we had the opportunity to remedy this by changing the way the data is structured. If you have a reference to a SubD edge or vertex, and make radical changes somewhere else in the SubD, your references are still valid (I think).

The downside to this is that, for now, the only way to access all of the vertices belonging to a face is to use an iterator. I heard @dalelear and @pierre chatting about this the other day; it’s not obvious how to force Rhino back into an integer based indexing scheme when the components are not currently referenced by an integer.

I’ve done a little bit of trying to manipulate SubDs and meshes in Rhino/GH. I’m kinda surprised to say that you like how meshes work - it’s so complicated! But anyway, maybe we’ll get there.

I can’t promise you that this is going to get better, and I also won’t promise you that it’ll stay in its current state forever. But I do know some people much smarter than me are trying to sort it out. It might take a while.

This is new for Rhino 8.

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

– Dale

I’m not sure I understand why this is a downside.

If I haven’t read through the list and don’t already have details about the contents, I should as a developer have no reason to say “I want #67 out of 100 and I want it as an instant array lookup!”. If I have read through the list and found some items I find interesting, I can store the handles myself. If I have some particular sort order I want to put them in so I can access in O(1) time while I run a calculation, I can build and sort the array myself.

Do you have an example in which the indices are important? I see it as a big win if McNeel found a better way to store things internally and the only thing I have to do is ForEach instead of For (i =.

Edit: I do agree strongly with some of your visible feature requests, such as direct control of points on the surface. It’s just the Indices-being-vital part that confuses me.

This never happened for me…

I see it similar to many other “mesh structures” in other context.
Searching for piece of codes to implement (stackexchange etc) you’ll find methods that are almost directly useable with rhinocommon. At least, this is my personal experience.

You mean Tags and stuff… ?
That surely looks good! Promising.
But how? Where? Examples?
With meshes I can use smoothing/pulling/projecting and “walking the topology” multithreaded , resulting in commands that compute real-time!
With SubD , with .First and .Next and no topology … can we even compare the methods?
I hope someone will provide some good example of how to use current SubD rhinocommon-c# methods, because currently it seems a very short dead end!

Iterating through vertexes/faces/edges is not the only problem.
No methods at all!
No ClosestPoint, no EvaluateAt (or something similar with facei+UVparameters), no topology, intersections, etc…
Or everything is hidden somewhere? Please tell me where to search!


Are they working on the c++ side? Or also for the c# side? (I don’t even know if this question make sense…)
My fear it that “a while” means Rhino 9 or later.

With new c# scripting I’m ready to create loads of functions for SubD for me an my colleagues …

One of the things that we do focus on in service releases is the addition of SDK functions that plug-in developers find that they need. Simplified functions for SubD aren’t necessary at the moment to try and wrap up Rhino 8 for shipping, but they are something that we can focus on in service releases of Rhino.

1 Like

@maje90 We are working on this currently and it will soon be better than it is now. This is work happening in Rhino 8.

I went through the mesh example you posted above and realized there are a couple of pieces missing to make that work on a SubD (make all our iterators compatible with for each, add and delete components API); I’m adding them to the SDK ASAP. Apart from these missing things, I think you will find that the SubD code is very similar to the Mesh code. I’ll share examples when they work in the public SDK.

I’m also looking at ways to create good examples of how to work with the SubD SDK, I realize that what feels commonplace to the SubD C++ devs might not be evident at all for someone coming from meshes in the .NET SDK.

With that said, there is still a few places where I do not really understand what’s missing for you or why you think the current design doesn’t work.

In SubD you can do Parallel.ForEach(face in subd.Faces) {...}. Is that not walking the topology in a multi-threaded fashion? (And a lot more pleasant to write than the equivalent code on the indices of an array?)

For the part about using an iterator on the collection rather than indices in an array, please see @Nathan_Bossett’s answer. I do not understand what use case makes this impossible for you.

What I am really curious about is this topology: what information are you trying to access here? The entire topology of a SubD is defined by its edges and faces (and vertex tags), and that information is already available in the SDK. Are you looking for an equivalent to Mesh.TopologyEdges? These things have no reason at all to exist in SubD, the only reason why they exist in meshes is that mesh edges are computed “in addition” to the mesh data coming from .OBJ files, where only vertices and faces are defined. (This is an awful design for mesh editors, as it makes it impossible to have stable indices for vertices and edges.) There are no TopologyEdges and TopologyVertices in SubD, just regular Faces, Edges, and Vertices, and that is all the topology of a SubD.

None of these are available in Rhino either, or in the C++ SDK, unless you are looking for the versions based on the proxy Brep of the SubD. I’ll have a look again at using the proxy Brep from a SubD in the C# SDK but I think that’s working now.

We’ve got plans of writing more core C++ methods to work with the SubD limit surface directly (evaluation, closest point, intersection with a curve at first and other things much later), but that will take time. Some of it will likely end up in Rhino 8, and the rest will be Rhino 9. These are things that AFAIK are not available in other SubD software so they did not feel urgent to us.

As usual, examples of why these things are useful (and not easily replaced by the same operation on the proxy Brep) will help us 1. put the appropriate amount of urgency on writing these new methods, and 2. design them in a way that works best for users, right from the start.

PS:

Doing that in the .NET SDK is and has been working for control points since 7.0, for edit points you could do whole SubD only in 7.0 and I just added the missing pieces this week to 8.0. I do not know why it is not working with the Grasshopper component but will try to sort that out soon.

1 Like

Fantastic! I look forward to giving it a try.

Most of my process is, conceptually, fitting to points on curves and surfaces rather than using control points to move curves and surfaces.

No, it’s not needed, thanks. The Brep proxy indeed can work but it’s slow…
I’ve tried using “proxy” meshes, and refining them near the intersection event, but it’s not that much better.

Speed.
When I have SubD with 1500 faces it gets quite slow to compute the Brep each time.
Also, ToBrep have the G1x or G1xx options, but they still create some “noise” near extraordinary vertex.
I would pay the price of losing speed, if it at least worked on a fully-continue surface (like Tsplines)…


I don’t know what to say, then.
I do like rhinocommon Mesh topology methods.
All the times I tried to code with SubD I ended up with a messed up geometry, edges or faces disappearing, invalid SubD. I tried many times, always gave up at the end.

Even on Rhino: history on, extracting some edge curve, if you continue editing the subd, later you’ll find the extracted edges (updated by history) are completely different, in another place, scrambled up.

Or sometime the SubD normals simply won’t uniformate.
Only solution is to ExtractControlPolygon and do UnifyMeshNormals and back to SubD or to extract SubD faces 1 by 1 and then rejoin them 1 by 1…
It never felt a 100% stable type of geometry. And I’m using it daily.

But you tell me this SubD structure is better… ok!

Now that you confirmed me that current SubD methods are as planned by you developers, I’ll have to try again and again until it make sense and code start to work.

Maybe I’m wrong to expect SubD methods to be as mature as Mesh methods, that have 10+ years of tests and fixes behind them… :see_no_evil:


Super!

I’m really looking forward for that!

Thank you for the great support!

Thank you to all the McNeel staff spending time for my questions!

Make sense… that’s one more reason to promptly update the licence asap.

It’s really hard for us to fix abstract problems. However, if you have code that messes up SubDs, can you post it here? That way we can have a conversation about real problems. We can perhaps find alternate syntax that works, or fix the bugs on our end.

1 Like

Sure, I’ll remember to attach a copy here when it happens again. Thanks.

A post was split to a new topic: SubD Primitives and Creases

I’m not sure if this is the best place or it should be split off:

SubDEdge and SubDFace don’t inherit from GeometryBase, either directly or via SubDComponent.

The actual problems I’ve run into:

  1. That makes it inconvenient to pass a collection of things that otherwise act like model components: aside from the inheritance, they’re peers of Curve and Surface. Passing around collections of objects and then verifying each time they’re received and used that they’re actually GeometryBase, SubDEdge, and SubDFace types is inconvenient.
  2. I couldn’t find an equivalent of GeometryEquals
  3. Trying to get properties of faces and edges is more indirect- area, center, etc. This could be fixed for SubDFace as an additional function signature for Rhino.Geometry.AreaMassProperties Compute
  4. (related but not inheitance) support in Display.Draw…

What are the plans, if any, for these areas?

Hi @Nathan_Bossett,

Indeed, it seems like it would be easier to have SubDEdge inherit from GeometryBase, the same way that BrepEdge does. I’m going to have to think about this one a little bit more, right now I see 2 big potential pain points:

  • I cannot break the SDK between versions (by removing existing methods for example), so any new inheritance chain to an existing class might be an issue.
  • To make a SubDEdge inherit from a geometry type, I need to keep a reference to the parent SubD, because the shape of a SubDEdge depends on the position of other vertices around it that can only be accessed by looking them up in the parent SubD. In C++ the class ON_SubDComponentRef (which keeps a reference to the parent SubD) inherits from ON_Geometry, but ON_SubDComponentPtr (which is just a simple pointer to the component’s data, and is a part of an ON_SubDComponentRef) does not.

These might be solved by making a new SubDComponentRef type or similar. I’ll try a few things and see what works best.

Do you have an example here? FYI, The CRhinoSubDObject handles drawing a little bit differently than the other Rhino objects (via static and dynamic displays), and the .Net SubD has pointers to these display properties. They have not been made public in the .Net SDK and I’m not sure why.

We may be fortunate in that regard in that both SubDEdge and SubDFace are subclasses of SubDComponent, and SubDComponent is a subclass of nothing.

I’d like to think any implementations can switch ot the correct overrides as necessary, but I’m looking at it from the outside- with a little browse around OpenNurbs.

I was specifically looking at the DisplayConduit and the way in the drawforeground event one can:
e.Display.DrawCurve
or e.Display.DrawSurface

There are two functions to draw SubD’s, but nothing for either SubDEdge or SubDFace specifically.

For the moment I’m faking SubDEdge by creating a line then drawing that and SubDFace by drawing the relevant SubDEdges as lines. The second (Faces) is the one that probably makes it obvious I’m faking it if one compares to what Rhino does.

Related: Should I expect the Edges to be in order to form a polygon if traced in succession? Whether the answer is yes or no, it would be nice to include that information in the documentation.