Interpolated curve goes back on itself

Hello!

I’m having the following odd behaviour with RhinoCommon:

The user selects a guide curve and a set of irregularly sampled points. The points are then ordered depending on the closest curve parameter. I then wish to programmatically create an interpolated curve through the points.
The issue I have is that for some reason, the curve goes back on itself. I’ve tried this for several (similar) input files.
I tried to reproduce the (hopefully exact) same functionality in Grasshopper, and the curve interpolates fine. How come? Are there any settings I am missing? The points seem to be sorted fine, as proven by a polyline created through them and the debug Information produced by the command. I’ve also tried different chord spacings etc

The Rhino file:
interpCrvError.3dm (77.1 KB)
A screenshot showing the curve produced:

The grasshopper file for comparison:
interpCrvTest.gh (10.0 KB)

It would be great if anyone could try and reproduce this and perhaps point me to what I am doing wrong!
Thanks a mllion!

Here is the whole command code

public class Geometry_GenericTest2 : Command
{
    public Geometry_GenericTest2()
    {
        // Rhino only creates one instance of each command class defined in a
        // plug-in, so it is safe to store a refence in a static property.
        Instance = this;
    }

    ///<summary>The only instance of this command.</summary>
    public static Geometry_GenericTest2 Instance
    {
        get;
        private set;
    }

    ///<returns>The command name as it appears on the Rhino command line.</returns>
    public override string EnglishName
    {
        get { return "Geometry_GenericTest2"; }
    }

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
        //get curve
        RhinoApp.WriteLine("Please select guide curve");
        Rhino.Input.Custom.GetObject gO = new GetObject();
        gO.GeometryFilter = ObjectType.Curve;
        gO.Get();

        Curve c0 = gO.Object(0).Geometry() as Curve;

        //collect sample points
        RhinoApp.WriteLine("Please select sample points");
        Rhino.Input.Custom.GetObject gP = new GetObject();
        gP.GeometryFilter = ObjectType.Point;
        gP.GetMultiple(2, 0);

        ObjRef[] ors = gP.Objects();
        List<Point3d> pts = new List<Point3d>();
        foreach(ObjRef or in ors)
        {
          pts.Add(or.Point().Location);
        }


        //pair points and closest curve parameter. Sort automatically by parameter
        SortedDictionary<double, Point3d> pairs = new SortedDictionary<double, Point3d>();
        double t;
        foreach(Point3d pt in pts)
        {
        c0.ClosestPoint(pt, out t);
        pairs[t] = pt;
        }
        
        //create interpolated curve through sorted points
        Curve cI = NurbsCurve.CreateInterpolatedCurve(pairs.Values, 3);

        //add curve to document
        doc.Objects.AddCurve(cI);


        //debug, display order information
        int count = 0;
        foreach (KeyValuePair<double, Point3d> kvp in pairs)
        {
            doc.Objects.AddTextDot(kvp.Key.ToString() + " " + count.ToString(), kvp.Value); 
            count++;
        }         

        return Result.Success;
    }

I didn’t spend any time analyzing your code. But the following is similar and works.

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
  GetObject gc = new GetObject();
  gc.SetCommandPrompt("Select guide curve");
  gc.GeometryFilter =  ObjectType.Curve;
  gc.SubObjectSelect = false;
  gc.Get();
  if (gc.CommandResult() != Result.Success)
    return gc.CommandResult();
      
  ObjRef curve_ref = gc.Object(0);
  Curve curve = curve_ref.Curve();
  if (null == curve)
    return Result.Failure;

  GetObject gp = new GetObject();
  gp.SetCommandPrompt("Select sample points");
  gp.GeometryFilter = ObjectType.Point;
  gp.SubObjectSelect = false;
  gp.EnablePreSelect(false, true);
  gp.DeselectAllBeforePostSelect = false;
  gp.GetMultiple(2, 0);
  if (gp.CommandResult() != Result.Success)
    return gp.CommandResult();

  List<Point3d> points = new List<Point3d>(gp.ObjectCount);
  foreach (ObjRef objref in gp.Objects())
  {
    PointObject point_obj = objref.Object() as PointObject;
    if (null != point_obj)
      points.Add(point_obj.PointGeometry.Location);
  }

  SortedDictionary<double, Point3d> pairs = new SortedDictionary<double, Point3d>();
  foreach (Point3d pt in points)
  {
    double t = Rhino.RhinoMath.UnsetValue;
    if (curve.ClosestPoint(pt, out t))
      pairs.Add(t, pt);
  }

  Curve nurb = NurbsCurve.CreateInterpolatedCurve(pairs.Values, 3);
  if (null != nurb)
  {
    doc.Objects.AddCurve(nurb);
    doc.Views.Redraw();
  }

  return Result.Success;
}

Dale, thanks. Have you tried this though?

I just copied and pasted your code but the oddity persists (at least in last posts Rhino file)…
The code does look identical in terms of core functionality, so its perhaps not all that surprising.
At the end of the more densly spaced points the curve still reverses.
Oddly it doesnt do so when I interpolate using Grasshopper, do it manually or use a custom c# code in GH.

Any other ideas why the Rhino command behaves differently from GH/manually drawn curve?

The problem and solution is in the type of knots that you employ. By default, CreateInterpolatedCurve uses uniform knots. But the distances between points change abruptly going from the finely sampled to the coarsely sampled points along the curve. In that case you should use distance-based knots (CurveKnotStyle.Chord).

See the differences using this code:

Curve nurb = Curve.CreateInterpolatedCurve(pairs.Values, 3, CurveKnotStyle.Chord);
Curve nurb2 = Curve.CreateInterpolatedCurve(pairs.Values, 3, CurveKnotStyle.Uniform);
if (null != nurb)
{
    doc.Objects.AddCurve(nurb, new ObjectAttributes { Name = "Chord" });
}
if (null != nurb2)
{
    doc.Objects.AddCurve(nurb2, new ObjectAttributes {Name = "Uniform"});
}
doc.Views.Redraw();

The curve with the chord-style knots does not go back on itself, whereas the uniform-knot style curve does.

This is probably the reason that Grasshopper gives a better curve - my bet is that the routine there uses chord-style knots.

HTH,

Menno

1 Like

Yes! That does seem to do the trick! Strangely, it does the trick only on Dales revised code and not on my (almost identical) orginal code (I had tested that)!

Anyhow, thanks a million to you and Dave for sorting this out!

I’m glad that it worked!

I’m always surprised at the influence of the knot distribution on the shape of a curve. This is yet another example of this.

Continuing the discussion from Interpolated curve goes back on itself:

I seem to be getting dodgy Interpolated curves as well. It seems to be on the end of the curves. :see_no_evil:

@menno, I have tried your recommendation with

Below are short extracts from the solution.
I have a segment class. Shortened for explanation only.

//Segment Class
public class Segment
{
    public Point3d StartPoint { get; set; }
    public Point3d EndPoint { get; set; }
}

I have this method

//Method
public Curve CreateProfile(List<Segment> Segments)
{
    List<Point3d> curvePoints = new List<Point3d>();
    foreach (var segment in Segments)
    {
        curvePoints.Add(new Point3d(segment.StartPoint));
        curvePoints.Add(new Point3d(segment.EndPoint));
    }
    var CulledPoints = Point3d.CullDuplicates(curvePoints, 100);
    var curve = Curve.CreateInterpolatedCurve(CulledPoints, 3, CurveKnotStyle.Chord);
    return curve;
}

which is called like so:

//Method Call in progam
Curve curve = CreateProfile(Segments);

Why am I still getting this funny behavior?
In short. The method takes a List of segments which is really only startpoint - endpoint. Not a line.
I iterate through the list to populate another list with the points.
I cull duplicate points.
Create the Curve and return it

Any advice will be apreciated. @dale

Guys,

I can confirm that the above method by @menno work well indeed.

It turns out that the data file received from the analysis file had dodgy point data.

Once the client re-exported the file, all worked as intended.

Happiness is!