Site Tools


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:

sdkdraftanglecontour.jpg

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
developer/sdksamples/draftanglecontour.txt ยท Last modified: 2020/08/14 (external edit)