Site Tools


Identifying Curve Shapes

Developer: .NET
Summary: Demonstrates how to identify curve shapes.
Warning: This page may be out-of-date. RhinoCommon samples coming soon.

Question

As part of a plug-in that I'm writing, it would be fantastic to be able to identify shapes that planar curves might be identified as (within tolerance). I have not found anything like this in the libraries available for .NET.

Answer

Rhino supports several basic curve types, including lines (OnLineCurve), polylines (OnPolylineCurve), arcs and circles (OnCrvCurve), NURBS curves (OnNurbsCurve), and more. You can verify if a geometric object is one of these curve types by trying to cast that geometry as one of these types.

The following example command demonstrates how to classify a selected curve. You can also download this sample.

C#

using System;
using RMA.Rhino;
using RMA.OpenNURBS;
 
namespace <put_your_plugin_namespace_here>
{
  public class ClassifyCurveCommand : RMA.Rhino.MRhinoCommand
  {
    public override System.Guid CommandUUID()
    {
      return new System.Guid("{<put_unique_command_uuid_here>}");
    }
 
    public override string EnglishCommandName()
    {
      return "ClassifyCurve";
    }
 
    public override IRhinoCommand.result RunCommand(IRhinoCommandContext context)
    {
      // Select a curve to clasify
      MRhinoGetObject go = new MRhinoGetObject();
      go.SetCommandPrompt("Select curve to classify");
      go.SetGeometryFilter(IRhinoGetObject.GEOMETRY_TYPE_FILTER.curve_object);
      go.EnableSubObjectSelect(false);
      go.GetObjects(1, 1);
      if (go.CommandResult() != IRhinoCommand.result.success)
        return go.CommandResult();
 
      // Get the geometry
      MRhinoObjRef objref = go.Object(0);
      IOnCurve crv = objref.Curve();
      if (crv == null)
        return IRhinoCommand.result.failure;
 
      // Obtain the document tolerances
      double tol = context.m_doc.AbsoluteTolerance();
      double angtol = context.m_doc.AngleToleranceRadians();
 
      // Is the curve a line?
      if (IsLine(crv))
      {
        RhUtil.RhinoApp().Print("Curve is a line.\n");
        return IRhinoCommand.result.success;
      }
 
      // Is the curve an arc or circle?
      bool bCircle = false;
      if (IsArc(crv, tol, ref bCircle))
      {
        if (bCircle)
          RhUtil.RhinoApp().Print("Curve is a circle.\n");
        else
          RhUtil.RhinoApp().Print("Curve is an arc.\n");
        return IRhinoCommand.result.success;
      }
 
      // Is the curve an ellipse or an elliptical arc?
      bool bEllipticalArc = false;
      if (IsEllipse(crv, tol, ref bEllipticalArc))
      {
        if (bEllipticalArc)
          RhUtil.RhinoApp().Print("Curve is an elliptical arc.\n");
        else
          RhUtil.RhinoApp().Print("Curve is an ellipse.\n");
        return IRhinoCommand.result.success;
      }
 
      // Is the curve some kind of polyline?
      int point_count = 0;     // number of vertices
      bool bClosed = false;    // curve is closed
      bool bDistance = false;  // segments are identical
      bool bAngle = false;     // angles are identical
      bool bIntersect = false; // curve self-intersects
      if (IsPolyline(crv, tol, angtol, ref point_count, 
        ref bClosed, ref bDistance, ref bAngle, ref bIntersect))
      {
        // If the curve is closed and does not self-intersect,
        // then it must be a polygon.
        if (bClosed && !bIntersect)
        {
          // If segment distances and angles are identical, 
          // then it must be a regular polygon.
          if (bDistance && bAngle && !bIntersect)
          {
            if (point_count == 3)
              RhUtil.RhinoApp().Print("Curve is an equilateral triangle.\n");
            else if (point_count == 4)
              RhUtil.RhinoApp().Print("Curve is a square.\n");
            else if (point_count == 5)
              RhUtil.RhinoApp().Print("Curve is a regular pentagon.\n");
            else if (point_count == 6)
              RhUtil.RhinoApp().Print("Curve is a regular hexagon.\n");
            else if (point_count == 7)
              RhUtil.RhinoApp().Print("Curve is a regular heptagon.\n");
            else if (point_count == 8)
              RhUtil.RhinoApp().Print("Curve is a regular octagon.\n");
            else if (point_count == 9)
              RhUtil.RhinoApp().Print("Curve is a regular nonagon.\n");
            else if (point_count == 10)
              RhUtil.RhinoApp().Print("Curve is a regular decagon.\n");
            else
              RhUtil.RhinoApp().Print("Curve is a regular polygon.\n");
          }
          // If segment distances or angles are not identical, 
          // then it must be an irregular polygon.
          else if (!bIntersect)
          {
            if (point_count == 3)
            {
              // I'll leave searching for isosceles, scalene, acute, obtuse
              // and right triangles for somebody else...
              RhUtil.RhinoApp().Print("Curve is a triangle.\n");
            }
            else if (point_count == 4)
            {
              // I'll leave searching for parallelogram and trapezoids
              // for somebody else...
              if (bAngle)
                RhUtil.RhinoApp().Print("Curve is a rectangle.\n");
              else if (bDistance)
                RhUtil.RhinoApp().Print("Curve is a rhombus.\n");
              else
                RhUtil.RhinoApp().Print("Curve is a quadrilateral.\n");
            }
            else if (point_count == 5)
              RhUtil.RhinoApp().Print("Curve is an irregular pentagon.\n");
            else if (point_count == 6)
              RhUtil.RhinoApp().Print("Curve is an irregular hexagon.\n");
            else if (point_count == 7)
              RhUtil.RhinoApp().Print("Curve is an irregular heptagon.\n");
            else if (point_count == 8)
              RhUtil.RhinoApp().Print("Curve is an irregular octagon.\n");
            else if (point_count == 9)
              RhUtil.RhinoApp().Print("Curve is an irregular nonagon.\n");
            else if (point_count == 10)
              RhUtil.RhinoApp().Print("Curve is an irregular decagon.\n");
            else
              RhUtil.RhinoApp().Print("Curve is an irregular polygon.\n");
          }
        }
        else
          RhUtil.RhinoApp().Print("Curve is a polyline.\n");
        return IRhinoCommand.result.success;
      }
 
      // Is the curve a [[rhino:nurbs|NURBs]] curve?
      if (IsNurbsCurve(crv))
      {
        RhUtil.RhinoApp().Print("Curve is a NURBS curve.\n");
        return IRhinoCommand.result.success;
      }
 
      // Give up...
      RhUtil.RhinoApp().Print("Curve is unclassified.\n");
 
      return IRhinoCommand.result.success;
    }
 
    /////////////////////////////////////////////////////////////////
    // Test to see if a curve has the properties of a line
    private bool IsLine(IOnCurve crv)
    {
      if (crv == null)
        return false;
 
      // Is the curve a line curve?
      IOnLineCurve line_crv = OnLineCurve.ConstCast(crv);
      if (line_crv != null)
        return true;
 
      // Is the curve a polyline that looks like a line?
      IOnPolylineCurve pline_crv = OnPolylineCurve.ConstCast(crv);
      if (pline_crv != null && pline_crv.m_pline.Count() == 2)
        return true;
 
      // Is the curve a polycurve that looks like a line?
      IOnPolyCurve poly_crv = OnPolyCurve.ConstCast(crv);
      if (poly_crv != null)
      {
        ArrayOn3dPoint pline_points = new ArrayOn3dPoint();
        if (poly_crv.IsPolyline(pline_points) == 2)
          return true;
      }
 
      // Is the curve a [[rhino:nurbs|NURBs]] curve that looks like a line?
      IOnNurbsCurve nurb_crv = OnNurbsCurve.ConstCast(crv);
      if (nurb_crv != null && nurb_crv.IsPolyline() == 2)
        return true;
 
      return false;
    }
 
    /////////////////////////////////////////////////////////////////
    // Test to see if a curve has the properties of an arc
    private bool IsArc(IOnCurve crv, double tol, ref bool bCircle)
    {
      if (crv == null)
        return false;
 
      // Is the curve an arc curve?
      IOnArcCurve arc_crv = OnArcCurve.ConstCast(crv);
      if (arc_crv != null)
      {
        bCircle = arc_crv.IsCircle();
        return true;
      }
 
      // Is the curve a polycurve that looks like an arc?
      IOnPolyCurve poly_crv = OnPolyCurve.ConstCast(crv);
      if (poly_crv != null)
      {
        OnPlane arc_plane = new OnPlane();
        OnArc arc = new OnArc();
        if (poly_crv.IsArc(arc_plane, arc, tol))
        {
          bCircle = arc.IsCircle();
          return true;
        }
      }
 
      // Is the curve an ellipse that looks like an arc?
      OnPlane ellipse_plane = new OnPlane();
      OnEllipse ellipse = new OnEllipse();
      if (crv.IsEllipse(ellipse_plane, ellipse, tol))
      {
        if (ellipse.IsCircle())
        {
          bCircle = crv.IsClosed();
          return true;
        }
      }
 
      // Is the curve a [[rhino:nurbs|NURBs]] curve that looks like an arc?
      IOnNurbsCurve nurb_crv = OnNurbsCurve.ConstCast(crv);
      if (nurb_crv != null)
      {
        OnPlane arc_plane = new OnPlane();
        OnArc arc = new OnArc();
        if (nurb_crv.IsArc(arc_plane, arc, tol))
        {
          bCircle = nurb_crv.IsClosed();
          return true;
        }
      }
 
      return false;
    }
 
    /////////////////////////////////////////////////////////////////
    // Test to see if a curve has the properties of an ellipse
    private bool IsEllipse(IOnCurve crv, double tol, ref bool bEllipticalArc)
    {
      if (crv == null)
        return false;
 
      // If curve is a polycure, get the first curve segment
      IOnPolyCurve poly_crv = OnPolyCurve.ConstCast(crv);
      if (poly_crv != null)
      {
        if (poly_crv.Count() == 1)
          crv = poly_crv.SegmentCurve(0);
        else
          return false;
      }
 
      OnPlane ellipse_plane = new OnPlane();
      OnEllipse ellipse = new OnEllipse();
      if (crv.IsEllipse(ellipse_plane, ellipse, tol))
      {
        if (!ellipse.IsCircle())
        {
          bEllipticalArc = !crv.IsClosed();
          return true;
        }
      }
 
      return false;
    }
 
    /////////////////////////////////////////////////////////////////
    // Test to see if a curve has the properties of a polyline
    bool IsPolyline(IOnCurve crv, double tol, double angtol, ref int point_count, 
      ref bool bClosed, ref bool bDistance, ref bool bAngle, ref bool bIntersect)
    {
      if (crv == null)
        return false;
 
      ArrayOn3dPoint pline_points = new ArrayOn3dPoint();
 
      IOnPolylineCurve pline_crv = OnPolylineCurve.ConstCast(crv);
      IOnPolyCurve poly_crv = OnPolyCurve.ConstCast(crv);
      IOnNurbsCurve nurb_crv = OnNurbsCurve.ConstCast(crv);
 
      // Is the curve a polyline?
      if (pline_crv != null)
      {
        if (pline_crv.m_pline.Count() <= 2)
          return false;
 
        for (int i = 0; i < pline_crv.m_pline.Count(); i++)
          pline_points.Append(pline_crv.m_pline[i]);
      }
      // Is the curve a polycurve that looks like a polyline?
      else if (poly_crv != null)
      {
        if (poly_crv.IsPolyline(pline_points) <= 2)
          return true;
      }
      // Is the curve a [[rhino:nurbs|NURBs]] curve that looks like a line?
      else if (nurb_crv != null)
      {
        if (nurb_crv.IsPolyline(pline_points) <= 2)
          return false;
      }
      else
      {
        return false;
      }
 
      // Is the curve closed?
      bClosed = crv.IsClosed();
 
      // Get the vertex count
      point_count = (bClosed ? pline_points.Count() - 1 : pline_points.Count());
 
      // Test for self-intersection
      ArrayOnX_EVENT x = new ArrayOnX_EVENT();
      bIntersect = (crv.IntersectSelf(ref x) > 0 ? true : false);
 
      // If the curve is closed, no reason to continue...
      if (!bClosed)
        return true;
 
      // Test if the distance between each point is identical
      double distance = 0.0;
      for (int i = 1; i < pline_points.Count(); i++)
      {
        On3dPoint p0 = new On3dPoint(pline_points[i - 1]);
        On3dPoint p1 = new On3dPoint(pline_points[i]);
        On3dVector v = new On3dVector();
        v = p0 - p1;
        double d = v.Length();
        if (i == 1)
        {
          distance = d;
          continue;
        }
        else if (Math.Abs(distance - d) < tol)
          continue;
        else
        {
          distance = OnUtil.On_UNSET_VALUE;
          break;
        }
      }
 
      // Set return value
      bDistance = (distance == OnUtil.On_UNSET_VALUE) ? false : true;
 
      // Test if the angle between each point is identical
      double angle = 0.0;
      for (int i = 1; i < pline_points.Count() - 1; i++)
      {
        On3dPoint p0 = new On3dPoint(pline_points[i - 1]);
        On3dPoint p1 = new On3dPoint(pline_points[i]);
        On3dPoint p2 = new On3dPoint(pline_points[i + 1]);
 
        On3dVector v0 = new On3dVector();
        On3dVector v1 = new On3dVector();
        v0 = p1 - p0;
        v0.Unitize();
        v1 = p1 - p2;
        v1.Unitize();
 
        double a = AngleBetweenVectors(v0, v1);
        if (i == 1)
        {
          angle = a;
          continue;
        }
        else if (Math.Abs(angle - a) < angtol)
          continue;
        else
        {
          angle = OnUtil.On_UNSET_VALUE;
          break;
        }
      }
 
      // Set return value
      bAngle = (angle == OnUtil.On_UNSET_VALUE) ? false : true;
 
      return true;
    }
 
    /////////////////////////////////////////////////////////////////
    // Test to see if a curve has the properties of a [[rhino:nurbs|NURBs]] curve
    private bool IsNurbsCurve(IOnCurve crv)
    {
      if (crv == null)
        return false;
 
      IOnNurbsCurve nurb_crv = OnNurbsCurve.ConstCast(crv);
      if (nurb_crv != null)
        return true;
 
      return false;
    }
 
    /////////////////////////////////////////////////////////////////
    // Calculates angle (in radians) between two 3-d vectors
    private double AngleBetweenVectors(On3dVector v0, On3dVector v1)
    {
      v0.Unitize();
      v1.Unitize();
 
      double dot = OnUtil.ON_DotProduct(v0, v1);
      // Force the dot product of the two input vectors to 
      // fall within the domain for inverse cosine, which 
      // is -1.0 <= x <= 1.0. This will prevent runtime 
      // "domain error" math exceptions.
      dot = (dot < -1.0 ? -1.0 : (dot > 1.0 ? 1.0 : dot));
 
      return Math.Acos(dot);
    }
  }
}
developer/sdksamples/classifycurve.txt ยท Last modified: 2016/04/07 by sandy