Adding NurbsSurface to Model

Trying to add a Nurbs surface to a Model using rhino3d.

I am getting a popup error when trying to add a surface to a model.

How do I add a NurbsSurface to the list of Objects in a File3DM?

Code is

def processSurfaceUV(self, surface):
        print(f"=========== Process Surface UV")
        print(dir(surface))
        UDegree = surface.UDegree
        UOrder = UDegree + 1
        VDegree = surface.VDegree
        VOrder =VDegree + 1
        UPoles = surface.NbUPoles
        VPoles = surface.NbVPoles
        poles = surface.getPoles()
        print(f"UPoles {UPoles} VPoles {VPoles}")
        print(f"Poles {poles}")
        print(f"Poles {len(poles)} x {len(poles[0])}")
        print(f"Surface UDegree {UDegree} VDegree {VDegree}")
        VKnots = surface.getVKnots()
        UKnots = surface.getUKnots()
        VMults = surface.getVMultiplicities()
        UMults = surface.getUMultiplicities()
        nurbSurf = r3.NurbsSurface.Create(3, False, UOrder, VOrder, UPoles, VPoles)
        for c in range(0, len(self.curves)-1, 2):
            nurbSurf.CreateRuledSurface(self.curves[c], self.curves[c+1])
        self.model.Objects.AddSurface(nurbSurf)

And report view has

06:52:48  =========== Process Surface UV
06:52:48  ['Content', 'Continuity', 'FirstUKnotIndex', 'FirstVKnotIndex', 'LastUKnotIndex', 'LastVKnotIndex', 'MaxDegree', 'MemSize', 'Module', 'NbUKnots', 'NbUPoles', 'NbVKnots', 'NbVPoles', 'Rotation', 'Tag', 'TypeId', 'UDegree', 'UKnotSequence', 'UPeriod', 'VDegree', 'VKnotSequence', 'VPeriod', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'approximate', 'bounds', 'buildFromNSections', 'buildFromPolesMultsKnots', 'clone', 'copy', 'curvature', 'curvatureDirections', 'deleteExtensionOfName', 'deleteExtensionOfType', 'dumpContent', 'exchangeUV', 'getAllDerivedFrom', 'getD0', 'getDN', 'getExtensionOfName', 'getExtensionOfType', 'getExtensions', 'getPole', 'getPoles', 'getPolesAndWeights', 'getResolution', 'getUKnot', 'getUKnots', 'getUMultiplicities', 'getUMultiplicity', 'getVKnot', 'getVKnots', 'getVMultiplicities', 'getVMultiplicity', 'getWeight', 'getWeights', 'hasExtensionOfName', 'hasExtensionOfType', 'increaseDegree', 'increaseUMultiplicity', 'increaseVMultiplicity', 'incrementUMultiplicity', 'incrementVMultiplicity', 'insertUKnot', 'insertUKnots', 'insertVKnot', 'insertVKnots', 'interpolate', 'intersect', 'intersectSS', 'isDerivedFrom', 'isPlanar', 'isUClosed', 'isUPeriodic', 'isURational', 'isUmbillic', 'isVClosed', 'isVPeriodic', 'isVRational', 'mirror', 'movePoint', 'normal', 'parameter', 'projectPoint', 'removeUKnot', 'removeVKnot', 'reparametrize', 'restoreContent', 'rotate', 'scale', 'scaleKnotsToBounds', 'segment', 'setExtension', 'setPole', 'setPoleCol', 'setPoleRow', 'setUKnot', 'setUKnots', 'setUNotPeriodic', 'setUOrigin', 'setUPeriodic', 'setVKnot', 'setVKnots', 'setVNotPeriodic', 'setVOrigin', 'setVPeriodic', 'setWeight', 'setWeightCol', 'setWeightRow', 'tangent', 'toBSpline', 'toShape', 'toShell', 'transform', 'translate', 'uIso', 'vIso', 'value']
06:52:48  UPoles 4 VPoles 4
06:52:48  Poles [[Vector (0.0, -2.1094e-13, 950.0000000000002), Vector (9.659258262890681, 5.0, 947.4118095489748), Vector (28.977774788672047, 8.0, 942.2354286469243), Vector (57.438078953694884, 8.0, 934.6095131295624)], [Vector (0.0, -1.4063e-13, 633.3333333333334), Vector (42.76981488940422, 10.366509419193466, 639.800038783734), Vector (114.98804248098074, 25.942211699085767, 653.0411226863124), Vector (183.98534651073962, 26.38138706943139, 663.2280220819055)], [Vector (0.0, -7.031e-14, 316.6666666666667), Vector (57.53550845184717, 19.535200851630535, 330.32632856766327), Vector (168.69102989879252, 30.690388721092194, 358.1915456916792), Vector (286.11668478862555, 21.750615453785397, 385.25146526120216)], [Vector (0.0, 0.0, 0.0), Vector (77.27406610312545, 18.0, 20.70552360820168), Vector (193.18516525781365, 15.0, 51.763809020504205), Vector (338.0740392011739, 0.0, 90.58666578588236)]]
06:52:48  Poles 4 x 4
06:52:48  Surface UDegree 3 VDegree 3

Exported file with just edges and no surface processing
Silk_WF_3_CubicSurface-CubicSurface_44_000.3dm (8.7 KB)

CreateRuledSurface is a static method (in Python I think that is called a class method?)
You are using it as an instance method in the for loop. I’m not exactly sure what you are trying to do with the surface input (please clarify), but if you wanted to create ruled surfaces from a list of curves, the whole thing can be replaced by this.

for c in range(0, len(self.curves)-1, 2):
    # use CreateRuledSurface as classmethod/static method:
    nurbSurf = r3.NurbsSurface.CreateRuledSurface(self.curves[c], self.curves[c+1]) 
    self.model.Objects.AddSurface(nurbSurf)

I am trying to process a surface that is curved in more than one direction.
It has four B-Spline edges

I had assumed that the

nurbSurf = r3.NurbsSurface.Create(3, False, UOrder, VOrder, UPoles, VPoles)

Created the surface and that the loop with

nurbSurf.CreateRuledSurface(self.curves[c], self.curves[c+1])

Created the ruledsurfaces and then I could just add the NurbsSurface.

Where as I now think I should be able to create the same surface with just a UV surface using info from either pair of B-Spline edges

        nurbSurf = r3.NurbsSurface.Create(3, False, UOrder, VOrder, UPoles, VPoles)
        rs = nurbSurf.CreateRuledSurface(self.curves[c], self.curves[2])
        self.model.Objects.AddSurface(rs)

But I still get the barf/popup error

My reading of the error message, is that it is expecting a Sphere and not accepting a Surface

Thinking about it I think I need to create the UV surface not from two of the edges but from the 16 poles in the surface, where it is reporting the U and V Degree as both 3 and therefore 4 poles in U and same in V direction.

For me still does not explain the error message

Okay changed to creating the U and V curve from the poles in the surface i.e.

def processSurfaceUV(self, surface):
        print(f"=========== Process Surface UV")
        print(dir(surface))
        UDegree = surface.UDegree
        UOrder = UDegree + 1
        VDegree = surface.VDegree
        VOrder =VDegree + 1
        UPoles = surface.NbUPoles
        VPoles = surface.NbVPoles
        poles = surface.getPoles()
        print(f"UPoles {UPoles} VPoles {VPoles}")
        print(f"Poles {poles}")
        print(f"Poles {len(poles)} x {len(poles[0])}")
        print(f"Surface UDegree {UDegree} VDegree {VDegree}")
        VKnots = surface.getVKnots()
        UKnots = surface.getUKnots()
        VMults = surface.getVMultiplicities()
        UMults = surface.getUMultiplicities()
        Ucurve = self.createNurbsCurve(UDegree, poles[0])
        Vcurve = self.createNurbsCurve(VDegree, poles[1])
        nurbSurf = r3.NurbsSurface.Create(3, False, UOrder, VOrder, UPoles, VPoles)
        #for c in range(0, len(self.curves)-1, 2):
        #    nurbSurf.CreateRuledSurface(self.curves[c], self.curves[c+1])
        #    self.model.Objects.AddSurface(nurbSurf)
        #ns =  nurbSurf.CreateRuledSurface(self.curves[0], self.curves[2])
        ns =  nurbSurf.CreateRuledSurface(Ucurve, Vcurve)
        if ns is not None:
            self.model.Objects.AddSurface(ns)

But still get the error on creating the surface

If I look at help i.e

file=rhino3dm.File3dm.Read("/Users/keithsloan/MEGA/MEGAsync/CAD_Files/3DM/testCases/Surface.3dm")
help(rhino3dm.File3dm.Objects)

I get

....

 the AddSphere(...)
 |      AddSphere(self: rhino3dm._rhino3dm.File3dmObjectTable, sphere: rhino3dm._rhino3dm.Sphere, attributes: rhino3dm._rhino3dm.ObjectAttributes = None) -> object
 |  
 |  AddSurface(...)
 |      AddSurface(self: rhino3dm._rhino3dm.File3dmObjectTable, surface: rhino3dm._rhino3dm.Sphere, attributes: rhino3dm._rhino3dm.ObjectAttributes = None) -> object
 |  
...

So AddSurface has the same option for surface as AddSphere i.e. sphere - rhino3dm._rhino3dm.Sphere

This looks to me like a bug

Hi @Keith_Sloan,

This seems to work. Perhaps this helps?

#! python 2
import Rhino
import System

def _make_deformable_plane():
    plane = Rhino.Geometry.Plane.WorldXY
    u_interval = Rhino.Geometry.Interval(0.0, 15.0)
    v_interval = Rhino.Geometry.Interval(0.0, 7.5)
    u_degree = 2
    v_degree = 2
    u_points = 10
    v_points = 10
    srf = Rhino.Geometry.NurbsSurface.CreateFromPlane(plane, u_interval, v_interval, u_degree, v_degree, u_points, v_points)
    if srf and srf.IsValid:
        return srf
    return None

def test_write():
    archive = Rhino.FileIO.File3dm()
    
    color = System.Drawing.Color.CornflowerBlue
    archive.AllLayers.AddDefaultLayer("Default", color)
    
    srf = _make_deformable_plane()
    attribs = Rhino.DocObjects.ObjectAttributes()
    archive.Objects.Add(srf, attribs)
    
    path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop)
    path = System.IO.Path.Combine(path, "test_write.3dm");
    archive.Write(path, 70)

if __name__ == "__main__":
    test_write()

– Dale

I am trying to implement a 3DM exporter in FreeCAD using python3 and rhino3dm.

I tried adapting you code to my environment

    def processSurfaceUV2(self, surface):
        print(f"=========== Process Surface UV")
        plane = r3.Plane.WorldXY
        u_interval = r3.Interval(0.0, 15.0)
        v_interval = r3.Interval(0.0, 7.5)
        u_degree = 2
        v_degree = 2
        u_points = 10
        v_points = 10
        print(dir(r3.NurbsSurface))
        srf = r3.NurbsSurface.CreateFromPlane(plane, u_interval, v_interval, u_degree, v_degree, u_points, v_points)
        if srf and srf.IsValid:
            return srf
        return None

And the print(dir(r3.NurbsSurface)) produces

16:30:51  ['Create', 'CreateFromCone', 'CreateFromCylinder', 'CreateFromSphere', 'CreateRuledSurface', 'Decode', 'Degree', 'Domain', 'Duplicate', 'Encode', 'FrameAt', 'GetBoundingBox', 'GetNurbsFormParameterFromSurfaceParameter', 'GetSpanVector', 'GetSurfaceParameterFromNurbsFormParameter', 'GetUserString', 'GetUserStrings', 'HasBrepForm', 'IncreaseDegreeU', 'IncreaseDegreeV', 'IsAtSeam', 'IsAtSingularity', 'IsClosed', 'IsCone', 'IsCylinder', 'IsDeformable', 'IsPeriodic', 'IsPlanar', 'IsRational', 'IsSingular', 'IsSolid', 'IsSphere', 'IsTorus', 'IsValid', 'IsoCurve', 'KnotsU', 'KnotsV', 'MakeDeformable', 'MakeNonRational', 'MakeRational', 'NormalAt', 'ObjectType', 'OrderU', 'OrderV', 'PointAt', 'Points', 'RdkXml', 'Rotate', 'Scale', 'SetDomain', 'SetUserString', 'SpanCount', 'ToNurbsSurface', 'Transform', 'Translate', 'UserStringCount', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

No CreateFromPlane

Maybe the problem is my level of rhino3dm python library?

>>> import rhino3dm as r3
>>> print(r3.__version__)
8.4.0
>>> 

There is no NurbsSurface.CreateFromPlane method exposed in rhino3dm. You could create a PlaneSurface(plane, u_interval, v_interval) and then call PlaneSurface.ToNurbsSurface().