After trying to dig in the code step by step, I think I understand that edge cases for curve’s start and end aren’t fully handled.
For instance at start point:
for i = 1, we have following situation:
Therefore
bLeft = true
and
bRight = true
, we then go into this case:
if (bLeft && bRight)
{
if (FindMinRadius(nurb, t0, t2, kk1, epsilon, out var pt))
points.Add(pt);
}
…where the `FindMinRadius’ function seems to converge to the mid parameter between t0 and t2.
For end point, we have kk0 < kk1 < kk2
(also bLeft && kk2 > kk1
). I don’t see this case to be handled in the three if statements of the GetMinMaxRadiusPoints
function.
Here’s my dirty kinda-fix version that seems at least to output start and end points:
public static bool GetMinMaxRadiusPoints(Curve curve, List<Point3d> points, RhinoDoc doc)
{
var nurb = curve?.ToNurbsCurve();
if (nurb == null)
return false;
var start_count = points.Count;
var count = nurb.Points.Count * 8;
var mul = 1.0 / count;
var epsilon = nurb.Domain.Length > 1.0
? RhinoMath.ZeroTolerance
: nurb.Domain.Length * RhinoMath.ZeroTolerance;
var t0 = 0.0;
var t1 = 0.0;
var kk0 = 0.0;
var kk1 = 0.0;
for (var i = 0; i <= count; i++)
{
var t2 = nurb.Domain.ParameterAt(i * mul);
var k = nurb.CurvatureAt(t2);
//doc.Objects.AddPoint(nurb.PointAt(t2));
//doc.Views.Redraw();
if (k.IsValid)
{
var kk2 = k * k;
var bLeft = kk0 < kk1 - RhinoMath.ZeroTolerance;
var bRight = kk2 < kk1 - RhinoMath.ZeroTolerance;
if (bLeft && bRight)
{
if (FindMinRadius(nurb, t0, t2, kk1, epsilon, out var pt))
points.Add(pt);
}
else if (bLeft && kk2 < kk1 + RhinoMath.ZeroTolerance)
{
var t = nurb.Domain.ParameterAt((t1 + t2) * 0.5);
k = nurb.CurvatureAt(t);
if (k.IsValid)
{
double kk = k * k;
if (kk1 < kk - RhinoMath.ZeroTolerance)
{
if (FindMinRadius(nurb, t1, t2, kk, epsilon, out var pt))
points.Add(pt);
}
}
}
else if (bRight && kk0 < kk1 + RhinoMath.ZeroTolerance)
{
var t = nurb.Domain.ParameterAt((t0 + t1) * 0.5);
k = nurb.CurvatureAt(t);
if (k.IsValid)
{
var kk = k * k;
if (kk1 < kk - RhinoMath.ZeroTolerance)
{
if (FindMinRadius(nurb, t0, t1, kk, epsilon, out var pt))
points.Add(pt);
}
}
}
//---------------------------------------
//Check for last segment
else if (i == count)
{
if (FindMinRadius(nurb, t0, t2, kk2, epsilon, out var pt))
points.Add(pt);
}
//---------------------------------------
t0 = t1;
kk0 = kk1;
t1 = t2;
kk1 = kk2;
}
}
return points.Count - start_count > 0;
}
private static bool FindMinRadius(NurbsCurve nurb, double t0, double t1, double kk, double epsilon, out Point3d pt)
{
pt = Point3d.Unset;
Vector3d k;
var done = false;
if (null == nurb)
return false;
for (var i = 0; i < 10000; i++)
{
if (done || t1 - t0 < epsilon)
{
var t = (t0 + t1) * 0.5;
k = nurb.CurvatureAt(t);
pt = nurb.PointAt(t);
return k.IsValid;
}
var tl = t0 * 0.75 + t1 * 0.25;
k = nurb.CurvatureAt(tl);
if (!k.IsValid)
break;
done = tl == t0;
var kkl = k * k;
var tr = t0 * 0.25 + t1 * 0.75;
k = nurb.CurvatureAt(tr);
if (!k.IsValid)
break;
done = tr == t1;
var kkr = k * k;
if (kkl > kkr && kkl > kk)
{
kk = kkl;
t1 = (t0 + t1) * 0.5;
}
else if (kkr > kkl && kkr > kk)
{
kk = kkr;
t0 = (t0 + t1) * 0.5;
}
//---------------------------------------
//Case max. curvature is at start point
else if (kkl > kkr && kkl < kk)
{
t1 = (t0 + t1) * 0.5;
}
//Case max. curvature is at end point
else if (kkr > kkl && kkr < kk)
{
t0 = (t0 + t1) * 0.5;
}
//---------------------------------------
else
{
t0 = tl;
t1 = tr;
}
}
return false;
}
Please feel free to correct me if I’m telling nonsense, which is highly likely.