Draft Angle Contouring
C++
Summary: Demonstrates how to create contour curves based on draft angle using the Rhino SDK.
Question
We find Rhino's draft angle analysis very useful. But, it would be great it it could create contour curves at specific angles. For example:
Notice the red curve on the right-hand image. This is what we would like to automate. Is there an SDK function that will help us to this?
Answer
There is not an SDK function that will help you do this. But, it is possible to write your own tool.
Draft angle analysis works by calculating the angles between mesh vertex normals and the unit normal (in most cases this is the world z-axis). It is possible to perform this calculation from a plug-in command. From these angles, it is possible to determine whether or not a contour line would pass through a mesh vertex or if it would cross between two mesh vertices.
The follow sample code demonstrates this process:
C++
CRhinoCommand::result CCommandTest::RunCommand( const CRhinoCommandContext& context ) { // Pick a mesh object CRhinoGetObject go; go.SetCommandPrompt( L"Select mesh for draft angle contour" ); go.SetGeometryFilter( CRhinoGetObject::mesh_object ); go.GetObjects( 1, 1 ); if( go.CommandResult() != success ) return go.CommandResult(); const ON_Mesh* pMesh = go.Object(0).Mesh(); if( 0 == pMesh ) return failure; // Copy mesh so we can tweak it if necessary ON_Mesh mesh( *pMesh ); // To make our life easy, convert all quads to triangles. mesh.ConvertQuadsToTriangles(); // For draft angle analysis, mesh must have vertex normals. if( !mesh.HasVertexNormals() ) { if( !mesh.ComputeVertexNormals() ) return failure; } // Specify a draft angle CRhinoGetNumber gn; gn.SetCommandPrompt( L"Draft angle" ); gn.SetDefaultNumber( m_angle ); gn.SetLowerLimit( 0.0, TRUE ); gn.SetUpperLimit( 90.0, TRUE ); gn.GetNumber(); if( gn.CommandResult() != success ) return gn.CommandResult(); m_angle = gn.Number(); // degrees ////////////////////////////////////////////////////////////// double A = m_angle * ( ON_PI / 180.0 ); // alpha ON_3dVector D = ON_zaxis; // unit normal int fvcnt = 3; // triangles // Process each mesh face int fi, i, j; for( fi = 0; fi < mesh.m_F.Count(); fi++ ) { const ON_MeshFace& f = mesh.m_F[fi]; // For each face vertex, calculate the draft angle double d[3], a[3]; for( i = fvcnt - 1; i >= 0; i-- ) { ON_3dVector N = mesh.m_N[f.vi[i]]; d[i] = RHINO_CLAMP( N * D, -1.0, 1.0 ); a[i] = acos(d[i]) - A; } // Determine if any of the angles meet our criteria. // If so, calculate a point on an edge at that angle. int P_count = 0; ON_3dPoint P[3]; for( i = 0; i < fvcnt; i++ ) { j = (i + 1) % fvcnt; // If zero, then draft angle point passes through vertex if( a[i] == 0 ) P[P_count++] = mesh.m_V[f.vi[i]]; // See if draft angle point crosses the edge between two vertices else if( a[i] * a[j] < 0.0 ) { double t = a[i] / (a[i] - a[j]); P[P_count++] = ((1 - t) * mesh.m_V[f.vi[i]]) + (t * mesh.m_V[f.vi[j]]); } } // If we have calculated enough points, create some geometry if( P_count == 2 ) context.m_doc.AddCurveObject( ON_Line(P[0], P[1]) ); else if( P_count == 3 ) { context.m_doc.AddCurveObject( ON_Line(P[0], P[1]) ); context.m_doc.AddCurveObject( ON_Line(P[1], P[2]) ); context.m_doc.AddCurveObject( ON_Line(P[2], P[0]) ); } } context.m_doc.Redraw(); return success; }
VB.NET
Public Overrides Function RunCommand(ByVal context As RMA.Rhino.IRhinoCommandContext) As RMA.Rhino.IRhinoCommand.result ' Pick a mesh object Dim go As New MRhinoGetObject go.SetCommandPrompt("Select mesh for draft angle contour") go.SetGeometryFilter(IRhinoGetObject.GEOMETRY_TYPE_FILTER.mesh_object) go.GetObjects(1, 1) If (go.CommandResult() <> IRhinoCommand.result.success) Then Return go.CommandResult() End If Dim pMesh As IOnMesh = go.Object(0).Mesh() If (pMesh Is Nothing) Then Return IRhinoCommand.result.failure End If ' Copy mesh so we can tweak it if necessary Dim mesh As New OnMesh(pMesh) ' To make our life easy, convert all quads to triangles. mesh.ConvertQuadsToTriangles() ' For draft angle analysis, mesh must have vertex normals. If (mesh.HasVertexNormals() = False) Then If (mesh.ComputeVertexNormals() = False) Then Return IRhinoCommand.result.failure End If End If ' Specify a draft angle Dim gn As New MRhinoGetNumber() gn.SetCommandPrompt("Draft angle") gn.SetLowerLimit(0.0, True) gn.SetUpperLimit(90.0, True) gn.GetNumber() If (gn.CommandResult() <> IRhinoCommand.result.success) Then Return gn.CommandResult() End If Dim angle As Double = gn.Number() 'degrees '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Dim A As Double = angle * (OnUtil.On_PI / 180.0) ' alpha Dim D As New On3dVector(OnUtil.On_zaxis) ' unit normal Dim fvcnt As Integer = 3 ' triangles ' Process each mesh face Dim fi As Integer = 0 For fi = 0 To mesh.m_F.Count() - 1 Dim f As OnMeshFace = mesh.m_F(fi) ' For each face vertex, calculate the draft angle Dim dd(2) As Double Dim aa(2) As Double Dim i As Integer = 0 Dim j As Integer = 0 For i = fvcnt - 1 To 0 Step -1 Dim N As On3fVector = mesh.m_N(f.vi(i)) dd(i) = N * D If (dd(i) < -1.0) Then dd(i) = -1.0 If (dd(i) > 1.0) Then dd(i) = 1.0 aa(i) = Math.Acos(dd(i)) - A Next ' Determine if any of the angles meet our criteria. ' If so, calculate a point on an edge at that angle. Dim P_count As Integer = 0 Dim P(2) As On3dPoint For i = 0 To fvcnt - 1 j = (i + 1) Mod fvcnt If (aa(i) = 0) Then Dim tmp As On3fPoint = mesh.m_V(f.vi(i)) P(P_count) = New On3dPoint(tmp.x, tmp.y, tmp.z) P_count = P_count + 1 ElseIf (aa(i) * aa(j) < 0.0) Then Dim t As Double = aa(i) / (aa(i) - aa(j)) Dim tmpi As New On3fPoint(mesh.m_V(f.vi(i)) * (1 - System.Convert.ToSingle(t))) Dim tmpj As New On3fPoint(mesh.m_V(f.vi(j)) * System.Convert.ToSingle(t)) Dim tmp As New On3fPoint(tmpi + tmpj) P(P_count) = New On3dPoint(tmp.x, tmp.y, tmp.z) P_count = P_count + 1 End If Next If (P_count = 2) Then context.m_doc.AddCurveObject(New OnLine(P(0), P(1))) ElseIf (P_count = 3) Then context.m_doc.AddCurveObject(New OnLine(P(0), P(1))) context.m_doc.AddCurveObject(New OnLine(P(1), P(2))) context.m_doc.AddCurveObject(New OnLine(P(2), P(0))) End If Next Return IRhinoCommand.result.success End Function
