Create NURBS surface or mesh from SubDFace

I have a simple SubD plane and can select one or more of the 4 faces it has. In the code I want to extract the geometry and create a NURBS surface or a mesh from it. I can see that the picking returns the object references to the SubDFaces I selected but I don’t know how to work with those any further. If I go back to the SubD and create a Brep from it I get the whole surface but I only want the selected parts. I also tried to find the ID of the SubDFace somewhere in the other objects but wasn’t able to so I just don’t know how to find a relation between the 4 brep faces I can get and the 4 SubDFaces I have.

Hey @ModuleWorks_Marcelb, are you recieving a SubDFaceList?

Do you have a small code sample you could post to help? :slight_smile:

I don’t have a SubDFaceList. I have a list of Rhino.DocObjects.ObjRef that I can turn into a SubD, SubDFace and a Brep. If I select 2 out of 4 parts of a simple plane I can see two objects in my list. Now if I extract the SubDFace I don’t know how to continue with it to extract a NURBS surface or a mesh from it.

I already implemented conversion functions for all kinds of geometries like Brep or BrepFace but that doesn’t help me here because extracting the Brep from the ObjRef returns the whole surface, the same as extracting the SubD. SubDFace just doesn’t seem to have any conversion functions.

Rhino.DocObjects.ObjRef reference;
var brep = reference.Brep(); // Returns the whole surface
var subDFace = reference.SubDFace();
subDFace.ParentSubD; // Useful?
subDFace.Id; // Useful?
subDFace.ToBrep(); // This is what I need

Hey @ModuleWorks_Marcelb, I had a good crack at this one and I’m unfortunately as stumped as you, I have a SubDFace and can do nothing with it. Sorry about that!

Hi @ModuleWorks_Marcelb,

With your SubD plane, run the ExtractSrf command. Is this what you want?

You are probably better off converting the SubD to a Brep and then splitting.

– Dale

This command will probably alter the geometry or create a new one, right? I just need to extract the information about the surface. Do I need to extract a copy and delete it afterwards to get my hands on the brep?

In the picture below you can see I selected 2 out of 4 faces. And I can see that the objects actually reflect these two faces with their respective IDs. Is it possible to map these to the faces of the Brep somehow? I wasn’t able to find those IDs anywhere in the Brep, unfortunately.

Rhino_zbTD7LJ0w0

Hi @dale, do you have any other ideas how to continue with this? It really bugs me that we cannot support these new surfaces and it is quite a weakness of our plugin.

Hi @ModuleWorks_Marcelb

Currently there is no mechanism to do subobject conversion of SubD to NURBS. The work item “RH-59619 Sub-object support for ToNURBS” tracks work on this. I don’t think this will be done for v7, it would be only for v8.

In the shorter term (i.e. this should happen in v7), we will make more of the SubD to NURBS conversion data accessible from the SDK. This means you will be able to query each component of the converted Brep, and know from which component of the original SubD it came from. I think this will help you solve your problem. You could do something like this:

  • Get the component ID of the SubD faces you are interested in.
  • Convert the whole SubD (brep = subd.ToBrep(Rhino.Geometry.SubDToBrepOptions.DefaultUnpacked)).
  • Look through the new Brep faces for the ones that have matching component IDs in ProxyBrepSubDFaceId().
  • Extract these and delete the rest.

This work is tracked here: RH-68365 RhinoCommon SDK: Expose ProxyBrepSubD information.

Subobject support for ToNURBS will not be as wasteful as the conversion above, where we convert the whole SubD just for a few of its faces. But subobject conversions will make it harder to use the same post-processing steps as whole-object conversions (face packing and extraordinary vertex smoothing), which are generally expected from end users.

Why do you need to convert only select pieces?

Hi Pierre,

thanks for linking the cases. I appreciate you acknowledge the use-case.

I need to convert only selected pieces because it’s possible to do this selection. If I allow my users to select only parts of the geometry it’s clear to me that only these parts should be used. I’m not sure about the possibilities of SubD geometries but would like to support them properly.

Best Regards
Marcel

Hi Marcel,

One problem with the argument that selectable subobjects must be exportable individually (like you would do with a Brep) is that SubD faces do not behave like Brep faces. They aren’t the same and I don’t think users want to use them the same way. In fact I would say that SubD faces behave more like one span of a multi-span Brep face, and these are not typically seen as independent, exportable objects.

Extracting a SubD face (keeping it of type SubD) changes its shape as you have seen, and that cannot be repaired by trimming or clamping knots because these concepts do not exist in SubD. The shape of a SubD face depends on its neighbors, and there isn’t much we can do about this.

I still think we are missing a few pieces in Rhino in SubD to NURBS and Mesh, and with your help I’m trying to understand what should be done and in what order. I do not know how your plugins interact with Rhino, please forgive me for the simple questions:

  • What geometry type do you use internally to run your computations, mesh or NURBS? Would it make sense to use a SubD geometry type internally in your plugin? Any conversion will have some drawbacks.
  • What do your users expect when they select one face of a Brep to use it in your plugin? Will they repeat the operation on other faces of the Brep, maybe with different properties?

Thanks,
Pierre

More details below:

Since “exporting” a SubD face needs to be part of a conversion to NURBS or Mesh, what I’m trying to understand is what do we really want when doing this. There is a tradeoff to balance between conversion quality, speed and compatibility with other conversions of the same SubD.

Some possibilities and issues I am considering:

  • Get the smallest possible SubD region that will keep that face intact, and convert that.
  • Get an as-close-as-possible NURBS representation of the geometrical surface defined by the SubD face. Next to extraordinary vertices that might not line up with separate NURBS conversions of neighboring faces.
  • Get the same NURBS surface as what we would get by converting the whole SubD to NURBS and then keeping only the surfaces that come from the selected faces.
  • Get the whole SubD converted, and tag the selected faces for further processing. Generally prioritize better support for propagation of face attributes from SubD to NURBS.
  • By the time the end user tries to export one SubD face, will they have converted the whole SubD to NURBS already?
  • If multiple faces are selected, should we try to pack the output NURBS?
  • Should this only work for conversion to Mesh?

Currently we have discussed the 3rd option above, but I’m wondering if the options 2 and 4 might be more appropriate.

Hi Pierre,

our plugin uses either NURBS or mesh (so in case we have a NURBS representation we can create a mesh obviously) and there’s actually no way of supporting SubD other than doing a conversion. We wont be supporting SubD faces right away.

If only one face of a brep is selected this should behave the same as if the other faces wouldn’t even exist. Only the selected geometries will be passed to our calculation core and most of the time you have to select a subset of the faces in order for the calculation to work. This depends on the type of calculation because we have many different strategies how to iterate over the selected geometries.

I cannot really give you an answer to your second post but I will ask a colleague to take a look and get back to you.

Best Regards
Marcel

RH-68365 is fixed in Rhino 7 Service Release 19

Hi again, I stumbled upon another issue and thought maybe it fits into this case. If it doesn’t please let me know and I’ll open up a new one.

I created a SubD sphere and selected 4 faces. In the code I have my 4 ObjRefs and all of them show a ComponentIndexType of SubdFace. But only in some cases I don’t really understand it’s possible to get an actual SubDFace from them by calling the SubDFace() function on them. Most of the time they only return null. Is there a trick to this?

Best Regards
Marcel

Hi Marcel, could you post an example of the code that is failing? I’m not sure I understand enough to reproduce this myself.

Hi Pierre,

I included the code for the picking that I use. Below the code is a check of the picked objects and trying to convert them.

                var go = new GetObject();
                OptionToggle polySurfaceSelection = null;

                go.GeometryFilter = ObjectType.Surface;

                if (type.HasFlag(ObjectType.Surface))
                {
                    polySurfaceSelection = AddToggleOption(go, "PolySurfaceSelection", polySurfaceSelectionEnabled);
                    SetPolySurfaceSelection(go, polySurfaceSelectionEnabled);
                }

                go.EnablePreSelect(true, false);
                go.SetCommandPrompt(message);
                go.EnableHighlight(true);
                go.EnablePostSelect(true);
                go.AcceptNothing(true);

                while (true)
                {
                    var res = go.GetMultiple(1, 0);
                    if (go.CommandResult() != Rhino.Commands.Result.Success)
                    {
                        return null;
                    }

                    if (res == Rhino.Input.GetResult.Option)
                    {
                        SetPolySurfaceSelection(go, polySurfaceSelection != null && polySurfaceSelection.CurrentValue);
                        continue;
                    }

                    var canBreak = true;
                    if (go.ObjectsWerePreselected)
                    {
                        go.EnablePreSelect(false, true);
                        go.AlreadySelectedObjectSelect = true;
                        go.EnableClearObjectsOnEntry(false);
                        go.DeselectAllBeforePostSelect = false;
                        go.EnableUnselectObjectsOnExit(false);

                        canBreak = false;
                    }

                    if (!multiple && go.ObjectCount > 1)
                    {
                        go.SetCommandPrompt("Please make sure you select one item: " + message);

                        canBreak = false;
                    }

                    if (canBreak)
                    {
                        break;
                    }
                }

                ObjRef[] objects = go.Objects() ?? new Rhino.DocObjects.ObjRef[0];

                foreach (var obj in objects)
                {
                    if (obj.SubDFace() != null)
                    {
                        // Is a SubDFace...
                    }
                }

Best Regards
Marcel

Hi Marcel,

Sorry for the delayed reply. The issue here is with your geometry filters. Since a SubDFace is not a Surface, GetObject will convert the selected SubD faces to their Brep representation when adding them to the selection list.

To enable selecting SubD faces, use the MeshFace object type.

To turn off the automatic conversion, use GetObject.ProxyBrepFromSubD = false.

–Pierre