Developer: .NET
Summary: Demonstrates how to identify curve shapes.
Warning: This page may be out-of-date. RhinoCommon samples coming soon.
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.
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.
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); } } }