Problem
Rhino’s draft angle analysis is very useful. However, it would be great it it could create contour curves at specific angles. For example:
Notice the red curve on the right-hand image above. This is what you would like to automate. Is there an Rhino function that will help do this?
Solution
There is not an 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 plugin 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.
Sample
The follow sample code demonstrates this process:
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;
}