Interpolated curve goes back on itself


#1

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;
    }

(Dale Fugier) #2

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;
}

#3

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?


(Menno Deij - van Rijswijk) #4

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


#6

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!


(Menno Deij - van Rijswijk) #7

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.