Get curve maximum curvature points

If you have a copy of the Rhino WIP, you might try Rhino.Geometry.Curve.ExtremeParameters.

Hi Dale,

Thanks for your quick answer, as always! the point is I would like to do it for a Rhino 5 plugin.
I guess I will have to do it by approach :slight_smile:

Thank you anyway.

Best regards

Hi,

Is there a solid way that gets you the precise max / min points? I’ve been looking for online literature about it but haven’t found anything useful. At the moment I am iteratively sampling the curve’s domain, so it can take a while to get a reasonable result…

There is a way - and we’ve added a method to RhinoCommon in the Rhino WIP to do this.

Rhino.Geometry.Curve.ExtremeParameters

Hi dale,

Thanks for that, I saw it mentioned somewhere else, but I’m not sure how to
get the newer Rhinocommon? Is the WIP mature enough to switch over? Or is
there a way to use the new Rhinocommon with Rhino 5?

Much thanks!

Tom Svilans, M. Arch.

m: +44 (0)75 1926 0882
e: tom.svilans@gmail.com
w: tomsvilans.com

··· On Thu, Mar 17, 2016 at 4:36 PM, Dale Fugier wrote:

dale http://discourse.mcneel.com/users/dale McNeel
March 17

tom_svilans:

Is there a solid way that gets you the precise max / min points?

There is a way - and we’ve added a method to RhinoCommon in the Rhino WIP
to do this.

Rhino.Geometry.Curve.ExtremeParameters

Visit Topic
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/5
or reply to this email to respond

In Reply To
tom_svilans http://discourse.mcneel.com/users/tom_svilans
March 17
Hi, Is there a solid way that gets you the precise max / min points? I’ve
been looking for online literature about it but haven’t found anything
useful. At the moment I am iteratively sampling the curve’s domain, so it
can take a while to get a reasonable result…

Visit Topic
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/5
or reply to this email to respond

To stop receiving notifications for this particular topic, click here
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/unsubscribe.
To unsubscribe from these emails, change your user preferences
http://discourse.mcneel.com/my/preferences

Why don’t you ask those on Serengeti?

http://discourse.mcneel.com/c/serengeti

Ah, wasn’t aware of that. Cheers!

Tom Svilans, M. Arch.

m: +44 (0)75 1926 0882
e: tom.svilans@gmail.com
w: tomsvilans.com

··· On Thu, Mar 17, 2016 at 5:49 PM, Dale Fugier wrote:

dale http://discourse.mcneel.com/users/dale McNeel
March 17

tom_svilans:

Is the WIP mature enough to switch over?

Why don’t you ask those on Serengeti?

http://discourse.mcneel.com/c/serengeti

Visit Topic
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/7
or reply to this email to respond

In Reply To
tom_svilans http://discourse.mcneel.com/users/tom_svilans
March 17
Hi dale, Thanks for that, I saw it mentioned somewhere else, but I’m not
sure how to get the newer Rhinocommon? Is the WIP mature enough to switch
over? Or is there a way to use the new Rhinocommon with Rhino 5? Much
thanks! Tom Svilans, M. Arch. m: +44 (0)75 1926 0882 e:
tom.svilans@gmail.c…

Visit Topic
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/7
or reply to this email to respond

To stop receiving notifications for this particular topic, click here
http://discourse.mcneel.com/t/get-curve-maximum-curvature-points/28693/unsubscribe.
To unsubscribe from these emails, change your user preferences
http://discourse.mcneel.com/my/preferences

Hi there,

Sorry for resurrecting that thread, but there’s still something I don’t get with the ‘ExtremeParameters’ method.

What I want to achieve: get the curvature extremas parameters displayed by the ‘Curvature’ command (aka. max. curvature points, for both planar and 3D curves) into Rhinocommon.

Curve.ExtremeParameters requires a 3d vector:
image
What is that vector excatly for? The doc is not so much explaining IMO…
I understand, that this vector determines in which order the output parameters are sorted in the returned array, but I does not seem to be the case, as different vectors output completely different parameters arrays…

As a result, the ‘ExtremeParameters’ method always returns different points (round points) that the ‘Curvature’ command does (white squares):


Any explanation will be welcome!

Hi @jeffoulet

The functions need to know what axis to calculate the extreme points. For example, with the original image, the axis would be the world x-axis.

– Dale

Hi @dale,

Thank you for the explanation but I still have the feeling to be off the track…
Why does ‘ExtremeParameters’ require a direction vector as the Rhino ‘Curvature’ command is not prompting for anything?
The curve’s local maximum curvature locations should be independent from any direction: if I have two copies of one curve, but one of them rotated, the ‘Curvature’ command always indicates me the maximal curvature location to be at the same place:


Or maybe we are just not talking about the same thing?

What I definitely need is the Rhinocommon method underlying the Rhino ‘Curvature’ command.

Hi @jeffoulet.

ExtremeParameters returns local extrema. The Curvature command marks inflection points.

Does this help?

SampleCsExtractInflectionPoints.cs

– Dale

Hi @dale,

Many thanks, It helps a lot to understand the logic use to find the inflection points.

However, I’m looking for max. curvature positions (the white dots of the ‘Curvature’ command), both for 2D and 3D curves.

Here’s one proposed method:

It numerically constructs the hodograph of the curve and solves the locations of curvature extremes by geometrically finding the hodograph intersections with the x-axis.

I was wondering if there is any nicer method to get these points. I assume the ‘Curvature’ command does not proceed as the above proposed grasshopper definition.

Hi @jeffoulet,

Ah yes, sorry about that. Here is what you are looking for:

SampleCsExtractMinMaxRadiusPoints.cs

It seems reasonable that RhinoCommon would have curve methods that would do this for you. I’ve added the ‘wishes’ to the pile.

https://mcneel.myjetbrains.com/youtrack/issue/RH-53747

– Dale

2 Likes

Hi @dale,

Thanks a lot! Works like a charm :slight_smile:

I must now wrap my head around that algorithm…

RH-53747 is fixed in the latest WIP

Hi @dale,

Sorry for bumping this thread, but I just noticed some strange behavior: it seems that for some curves, some minimal radius points aren’t properly identified, see screenshot below:

By doubling the number of points (l. 46), the point gets sometime marked, sometime not (higher factors required):
image

Sample curves: MinRadius_curves.3dm (45.6 KB)

Same issue occurs with the ‘Curvature’ Rhino command, where some points aren’t highlighted:

What would then be the appropriate factor, to ensure that no point gets omitted?

Thanks in advance :slight_smile:

Hi @jeffoulet,

Are you using my sample code or are you using the new RhinoCommon methods? Can you post your curves and your code?

– Dale

Hi @dale,

I’m using the unmodified C# ‘SampleCsExtractMinMaxRadiusPoints.cs’ sample provided on github.
The ‘Curvature’ command in v6 also produce similar results.

If you consider the sample curve attached:
sample_curve.3dm (37.2 KB)

Expected result:

  • both curves’s start and end points should be marked (as they’re actual maximal curvature locations)

Current result:

  • only start point is marked and still slightly lies off the right location

As @spb rightly pointed out, certainly related to this: Inconsistent maximum curvature locations

1 Like

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.

image

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. :slight_smile:

1 Like

Hi @jeffoulet,

Can you review this and see if its related?

https://mcneel.myjetbrains.com/youtrack/issue/RH-56657

One thing I noticed is the local max curvature (per the Help file) is found where the curvature decreases on both sides, so dealing with curves that acclerate right to the end may be dodgy.

– Dale