Surface.ClosestPoint() gives large deviations where none should exist

If I create a surface using the code below and evaluate points on the surface’s boundary curves, I expected that the points that are found on the surface are within zero tolerance to the point on the boundary curve. The output of the code below, however, paints a different picture, where deviations up to 0.23 are found.

The problem is quite sensitive to 1) the location of the initial points and 2) the choice of knot style. But, even so, for a relatively simple surface like the one in the example, I don’t expect such large deviations.

output:
large distance! 0.0185262564697653
large distance! 0.233779029389498
large distance! 0.23783011958997
large distance! 0.0503318751917803



        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            Point3dList nort = new Point3dList(
                new Point3d( 0, 0, 0),
                new Point3d( 1, 0, 0),
                new Point3d( 2, 0, 0),
                new Point3d( 3, 0, 0),
                new Point3d(10, 0, 0)
                );
            
            Transform rotate90 = Transform.Rotation(Math.PI/2, new Point3d(5,5,0));
            Point3dList west = new Point3dList(nort);
            west.Transform(rotate90);
            Point3dList sout = new Point3dList(west);
            sout.Transform(rotate90);
            Point3dList east = new Point3dList(sout);
            east.Transform(rotate90);

            NurbsCurve cnort = Curve.CreateInterpolatedCurve(nort, 3, CurveKnotStyle.Uniform).ToNurbsCurve();
            NurbsCurve cwest = Curve.CreateInterpolatedCurve(west, 3, CurveKnotStyle.Uniform).ToNurbsCurve();
            NurbsCurve csout = Curve.CreateInterpolatedCurve(sout, 3, CurveKnotStyle.Uniform).ToNurbsCurve();
            NurbsCurve ceast = Curve.CreateInterpolatedCurve(east, 3, CurveKnotStyle.Uniform).ToNurbsCurve();

            Brep b = Brep.CreateEdgeSurface(new[] {cnort, cwest, csout, ceast});

            NurbsSurface s = b.Surfaces[0].ToNurbsSurface();
            
            Test(s, cnort, doc);
            Test(s, cwest, doc);
            Test(s, csout, doc);
            Test(s, ceast, doc);
                         
            doc.Objects.Add(b);
           
            return Result.Success;
        }

        private void Test(NurbsSurface s, NurbsCurve curve, RhinoDoc doc)
        {
            foreach (double t in Range(curve.Domain, 100))
            {
                Point3d pt = curve.PointAt(t);
                double u, v;
                if (s.ClosestPoint(pt, out u, out v))
                {
                    Point3d onSrf = s.PointAt(u, v);
                    if (onSrf.DistanceTo(pt) > RhinoMath.ZeroTolerance)
                    {
                        RhinoApp.WriteLine("large distance! "+onSrf.DistanceTo(pt).ToString("g"));
                        doc.Objects.AddLine(pt, onSrf);
                    }
                }
            }
        }

        private IEnumerable Range(Interval interval, int nSteps)
        {
            yield return interval.T0;
            double step = interval.Length/nSteps;
            for (int i = 0; i < nSteps - 1; ++i)
                yield return interval.T0 + i*step;
            yield return interval.T1;
        }

Clearly something is happening to the input curves when creating the surface (Coons Patch). Let me get with the developer of the function and see if I can figure out what is going on.

I don’t think the problem is the Coons patch, I have it also for surfaces that are not created by this method. I will send you an email with an attachment - it is confidential.

I’ve logged a bug for this item.

In the mean time I have created the workaround as below. This solves my problem. Maybe this can also help with fixing the bug?

Surface srf; // some surface 
Point3dList parmeters; // the list to store (u, v) pairs found
Point3d pt; // some point to get parameters on surface for
Interval domainU = srf.Domain(0);
Interval domainV = srf.Domain(1);
Curve v0Curve = srf.IsoCurve(0, domainV.T0);
Curve v1Curve = srf.IsoCurve(0, domainV.T1);
Curve u0Curve = srf.IsoCurve(1, domainU.T0);
Curve u1Curve = srf.IsoCurve(1, domainU.T1);

double t,u,v;
if (u0Curve.ClosestPoint(pt, out t, RhinoMath.SqrtEpsilon))
{
    parmeters.Add(domainU.T0, t, 0);
}
else if (u1Curve.ClosestPoint(pt, out t, RhinoMath.SqrtEpsilon))
{
    parmeters.Add(domainU.T1, t, 0);
}
else if (v0Curve.ClosestPoint(pt, out t, RhinoMath.SqrtEpsilon))
{
    parmeters.Add(t, domainV.T0, 0);
}
else if (v1Curve.ClosestPoint(pt, out t, RhinoMath.SqrtEpsilon))
{
    parmeters.Add(t, domainV.T1, 0);
}

else if (srf.ClosestPoint(pt, out u, out v))
{
    u = RhinoMath.Clamp(u, domainU.T0, domainU.T1);
    v = RhinoMath.Clamp(v, domainV.T0, domainV.T1);
    parmeters.Add(u, v, 0);
}
else throw new ArgumentException("Could not find point on surface edges or surface.");