How to use BezierSurface.CreateLoftedBezier

Does anyone have an example on how to use BezierSurface.CreateLoftedBezier? Here is my attempt:

Python code
from __future__ import absolute_import, print_function, unicode_literals

import Rhino
import Rhino.Geometry as rg
import rhinoscriptsyntax as rs
import scriptcontext as sc

import random

def epsilonEquals(ptA, ptB, epsilon=Rhino.RhinoMath.ZeroTolerance):
    if not Rhino.RhinoMath.EpsilonEquals(ptA.X, ptB.X, epsilon):
        return False
    if not Rhino.RhinoMath.EpsilonEquals(ptA.Y, ptB.Y, epsilon):
        return False
    try:
        return Rhino.RhinoMath.EpsilonEquals(ptA.Z, ptB.Z, epsilon)
    except:
        return True

def evalBezierCrv(bc):
    
    sEval="bc.Dimension"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bc.IsRational"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bc.IsValid"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bc.ControlVertexCount"; print("{}: {}".format(sEval, eval(sEval)))
    
    print("\nGetControlVertex2d, 3d, 4d")
    for i in range(bc.ControlVertexCount):
        pt2d = bc.GetControlVertex2d(i)
        pt3d = bc.GetControlVertex3d(i)
        pt4d = bc.GetControlVertex4d(i)
        print("iV[{}]".format(i))
        print("2D:{}".format(pt2d))
        print(
            "3D:{}".format(pt3d),
            "" if epsilonEquals(pt2d, pt3d) else "<---")
        print(
            "4D:{}".format(pt4d),
            "" if epsilonEquals(pt4d, pt3d) else "<---")

def main():
    
    degree = rs.GetInteger(
        "Degree",
        number=5,
        minimum=1,
        maximum=11)
    if not degree: return
    
    bcs = []
    
    for i in range(degree+1):
        pts = Rhino.Collections.Point3dList()
        for j in range(degree+1):
            pts.Add(
                float(i) + 0.2*(random.random()-0.5),
                float(j) + 0.2*(random.random()-0.5),
                           0.2*(random.random()-0.5))
        bc = rg.BezierCurve.CreateLoftedBezier(pts)
        sc.doc.Objects.AddCurve(bc.ToNurbsCurve())
        bcs.append(bc)
        
    bs = rg.BezierSurface.CreateLoftedBezier(curves=bcs)
    if not bs.IsValid:
        print("BezierSurface is not valid.")
        return
    
    sEval="bs.Dimension"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.IsRational"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.IsValid"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.ControlVertexCount(0)"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.ControlVertexCount(1)"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.Domain(0)"; print("{}: {}".format(sEval, eval(sEval)))
    sEval="bs.Domain(1)"; print("{}: {}".format(sEval, eval(sEval)))
    
    print("\nGetControlVertex2d, 3d, 4d")
    for i in range(bs.ControlVertexCount(0)):
        for j in range(bs.ControlVertexCount(1)):
            pt2d = bs.GetControlVertex2d(i,j)
            pt3d = bs.GetControlVertex3d(i,j)
            pt4d = bs.GetControlVertex4d(i,j)
            print("i[{}],j[{}]".format(i,j))
            print("2D:{}".format(pt2d))
            print(
                "3D:{}".format(pt3d),
                "" if epsilonEquals(pt2d, pt3d) else "<---")
            print(
                "4D:{}".format(pt4d),
                "" if epsilonEquals(pt4d, pt3d) else "<---")
    
    ns = bs.ToNurbsSurface()
    
    if not ns.IsValid:
        print("NurbsSurface is not valid.")
        return
    
    # Fix last CP.
    #ns.Points.SetControlPoint(i,j,nc_from_bc.Points[nc_from_bc.Points.Count-1])
    
    sc.doc.Objects.AddSurface(ns)
    sc.doc.Views.Redraw()

if __name__ == '__main__': main()

Thanks

Hi @spb,

I don’t see this function called anywhere in the core. Any particular reason you need it?

– Dale

I’ve searched for a way to create lofts with degree above 3 in the loft direction, and CreateLoftedBezier would be a solution to this, albeit limited to Bezier patches.

Hi Steve @spb . It seems no one has ever run this code except maybe with degree=2. I found a bug that was easy to fix for V7. There may be other problems. Let us know if you find any. Please note that your curves are a mess (look at the control points) and that mess gets worse in the surfaces. There is not much leeway when restricting to beziers. The result after my fix is a surface that goes through the curves. Dale added the dots to your script so I could see the order of the curves.

Loft.3dm (183.7 KB)

2 Likes

I concur that the script creates very wiggly curves and that will lead to a messy surface control point pattern, but I presume that if the mid-domain of U passed between curves [2] and [3] instead of exactly at [3], the shape on the right side of the surface would be softer.

The right image shows the uneven parameter / point distribution with a degree-2 surface. The middle curve is at 1/3 of the domain of U.

What is your take on this?

imageimage

I also noticed what you are seeing. It does seem odd. The code is at least 15 years old and I was looking at it for the first time. An obvious mistake jumped out at me and I fixed it. I have a thought on how it could be done better and will take a shot when I have a chance.

I would expect the input curves to appear at the Greville abcissa, so in the quadratic case the middle curve would be at the middle of the domain. There might be another simple error in the code. If not, I’ll just rewrite it so that happens.

This is what my alternative method would give for you degree 5 case. Much better, I think. I should probably just rewrite the whole thing.

6quintsBetter.3dm (46.9 KB)

1 Like

As viewed from the Right,
image
the surface control points of the new (green) have a leaning distribution versus that of the previous (red) surface. Even though the set of curves for each surface is different, is this leaning a product of the new algorithm? Can the points be distributed in a more natural way? (I’m not sure what exactly is natural, maybe where each point lies on the perpendicular plane passing though the relative curve’s Greville point.)

I think this is the natural way to loft these curves. Since the distribution of points on the input curves is quite different from curve to curve, and pretty wild, it is expected that the surface points will be even worse.

I just found another bug in the original algorithm. Now that I’ve fixed it, the results are exactly the same as the result I posted last night.

1 Like

Thanks. I’ll test BezierSurface.CreateLoftedBezier Method in a future SR and post here if I find any problems.

Out of curiosity, do you know why BezierSurface Class was added to RC7? Was it for Rhino.Inside?

Could be Rhino.Inside, but Grasshopper seems more likely to me. I can’t find any notes on it.

BezierSurface.CreateLoftedBezier works well in 7.18. Thanks for fixing it.

The only way I know how to replicate CreateLoftedBezier’s results is through using a fitting routine that translates control points until Greville points match their targets. _SrfPtGrid (NurbsSurface.CreateThroughPoints) gets close.

Hi Steve. I’m glad it’s working for you. You are correct. The algorithm is just doing Greville interpolation.