Files
opennurbs/tests/TestTree.cpp
2024-02-15 08:00:36 -08:00

647 lines
19 KiB
C++

#include "Tests.h"
static const bool bPurify = false;
static const bool bDoCurves = true;
static const bool bDoSurfaces = true;
bool TestCurveTree(ON_TextLog& text_log, const ON_Curve* curve )
{
if ( !curve )
{
text_log.Print("Curve pointer is NULL.\n");
return false;
}
const ON_CurveTree* tree = curve->CurveTree();
if ( !tree )
{
text_log.Print("CurveTree() returned NULL.\n");
return false;
}
bool rc = tree->IsValid( &text_log, curve );
if ( !rc )
{
text_log.Print("Curve tree is not valid\n");
}
if ( tree != tree->m_root )
{
// As of 29 March, I want tree to be = tree->m_root.
// and I'd like to get rid of the m_root field, but
// I don't know if that will work for making
// auxillary tree's over a set of trees.
text_log.Print("Curve tree != tree->m_root - please tell Dale Lear.\n");
}
const int span_count = curve->SpanCount();
ON_SimpleArray<double> t(10*(span_count+1));
t.SetCount(span_count+1);
curve->GetSpanVector(t.Array());
int i;
for ( i = 0; i < span_count; i++ )
{
t.Append(0.5*(t[i]+t[i+1]));
}
ON_CurveTreeNode* leaf = tree->FirstLeaf();
int leaf_count=0;
double maxar = 0.0;
double maxr = 0.0;
double maxlen = 0.0;
double longest = 0.0;
double thickest = 0.0;
double shortest = fabs(ON_UNSET_VALUE);
ON_Line axis;
int notmonocount = 0;
while (leaf)
{
if ( t.Search( leaf->m_domain[0] ) < 0 )
t.Append(leaf->m_domain[0]);
t.Append( leaf->m_domain.ParameterAt(0.2) );
t.Append( leaf->m_domain.ParameterAt(0.7) );
if( !leaf->m_bez->m_leafbox.m_bMono )
notmonocount++;
axis.from = leaf->m_bez->PointAt(0.0);
axis.to = leaf->m_bez->PointAt(1.0);
double len = axis.Length();
if (len > longest )
longest = len;
if ( len < shortest )
shortest = len;
if ( leaf->m_bez->m_leafbox.m_r > thickest )
thickest = leaf->m_bez->m_leafbox.m_r;
if ( leaf->m_bez->m_leafbox.m_r > maxar*len )
{
maxar = leaf->m_bez->m_leafbox.m_r/len;
maxlen = len;
maxr = leaf->m_bez->m_leafbox.m_r;
}
leaf_count++;
leaf = leaf->NextLeaf();
}
if ( notmonocount > 0 )
{
text_log.Print("ON_CurveTree: %d spans, %d leaves (%d are not monotone).\n",span_count,leaf_count,notmonocount);
}
else
{
text_log.Print("ON_CurveTree: %d spans, %d leaves (all are monotone).\n",span_count,leaf_count);
}
text_log.PushIndent();
text_log.Print("Shortest: %g\n",shortest);
text_log.Print("Longest: %g\n",longest);
text_log.Print("Thickest: radius = %g\n",thickest);
text_log.Print("Fattest: rad/len = %g (rad = %g, len = %g)\n",maxar,maxr,maxlen);
text_log.PopIndent();
// test evaluation
const bool bAdjustParameter = tree->AdjustParameter();
double x, y, d0, d1, tol0, tol1;
ON_3dVector X[2], Y[2], Xerr[2], Yerr[2];
double xerr, yerr, d0err=0.0, d1err=0.0;
int error_count = 0;
int test_count = 0;
int hint = 0;
int side = 0;
const ON_CurveTreeNode* evaluation_node;
if ( 2 == curve->Dimension() )
{
X[0].z = X[1].z = Y[0].z = Y[1].z = 0.0;
}
for ( i = 0; i < t.Count(); i++ )
{
x = y = t[i];
if ( i > span_count && bAdjustParameter)
{
// NOTE - the curve paramter = nurbs form paramter
// at all span vector values.
curve->GetCurveParameterFromNurbFormParameter(x,&y);
}
for ( side = -1; side < 1; side++ )
{
test_count++;
X[0].x = -99.0;
Y[0].x = 99.0;
curve->Evaluate(y,1,3,&Y[0].x,side,&hint);
evaluation_node = tree->Evaluate(x,1,3,&X[0].x,side);
if ( !evaluation_node )
{
error_count++;
ON_ERROR("TestCurveTree() - tree failed to evaluate.");
d0err = 1.0e100;
d1err = 1.0e100;
xerr = x;
yerr = y;
Xerr[0] = ON_UNSET_POINT;
Xerr[1] = ON_UNSET_POINT;
Yerr[0] = Y[0];
Yerr[1] = Y[1];
}
else
{
if ( side < 0 && x == evaluation_node->m_domain[0] )
{
// x should be at the start of the curve's domain
// and there shouldn't be any leaves before this one.
if ( evaluation_node->PrevLeaf() || x != t[0] )
{
ON_ERROR("TestCurveTree() - evaluation used wrong node");
}
}
else if ( side > 0 && x == evaluation_node->m_domain[1] )
{
// x should be at the end of the curve's domain
// and there shouldn't be any leaves after this one.
if ( evaluation_node->NextLeaf() || x != t[span_count] )
{
ON_ERROR("TestCurveTree() - evaluation used wrong node");
}
}
if ( bAdjustParameter )
{
// Different parameterizations result in derivatives
// with different lengths.
X[1].Unitize();
Y[1].Unitize();
}
tol0 = ON_ZERO_TOLERANCE + ON_SQRT_EPSILON*Y[0].MaximumCoordinate();
tol1 = ON_ZERO_TOLERANCE + ON_SQRT_EPSILON*Y[1].MaximumCoordinate();
d0 = (X[0]-Y[0]).Length();
d1 = (X[1]-Y[1]).Length();
if ( d0 > tol0 || d1 > tol1 )
{
rc = false;
error_count++;
if ( d0 > d0err || (d1 > d1err && d0err < 0.01) )
{
if ( d0 > d0err) d0err = d0;
if ( d1 > d1err) d1err = d1;
xerr = x;
yerr = y;
Xerr[0] = X[0];
Xerr[1] = X[1];
Yerr[0] = Y[0];
Yerr[1] = Y[1];
}
}
}
}
}
text_log.PushIndent();
if ( error_count > 0 )
{
ON_ERROR("TestCurveTree() - tree failed evaluation test.");
text_log.Print("Evaluation test at %d points had %d errors.\n",test_count,error_count);
text_log.PushIndent();
text_log.Print("curve(%g) = (%g,%g,%g)\n",yerr,Y[0].x,Y[0].y,Y[0].z);
text_log.Print("tree(%g) = (%g,%g,%g)\n",xerr,X[0].x,X[0].y,X[0].z);
text_log.Print("curve D = (%g,%g,%g)\n",Y[1].x,Y[1].y,Y[1].z);
text_log.Print("tree D = (%g,%g,%g)\n",X[1].x,X[1].y,X[1].z);
text_log.PopIndent();
}
else
{
text_log.Print("Evaluation test at %d points was successful.\n",test_count);
}
text_log.PopIndent();
return rc;
}
bool TestSurfaceTree(ON_TextLog& text_log, const ON_Surface* surface )
{
if ( !surface )
{
text_log.Print("Surface pointer is NULL.\n");
return false;
}
const ON_SurfaceTree* tree = surface->SurfaceTree();
if ( !tree )
{
text_log.Print("SurfaceTree() returned NULL.\n");
return false;
}
bool rc = tree->IsValid( &text_log, surface );
if ( !rc )
{
text_log.Print("Surface tree is not valid.\n");
}
int i;
const int span_count0 = surface->SpanCount(0);
ON_SimpleArray<double> s(10*(span_count0 + 1));
s.SetCount(span_count0+1);
surface->GetSpanVector( 0, s.Array() );
for ( i = 0; i < span_count0; i++ )
{
s.Append(0.5*(s[i]+s[i+1]));
}
int j;
const int span_count1 = surface->SpanCount(1);
ON_SimpleArray<double> t(10*(span_count1 + 1));
t.SetCount(span_count1+1);
surface->GetSpanVector( 1, t.Array() );
for ( j = 0; j < span_count1; j++ )
{
t.Append(0.5*(t[j]+t[j+1]));
}
const int span_count = span_count0*span_count1;
ON_SurfaceTreeNode* leaf = tree->FirstLeaf();
int leaf_count=0;
double maxar = 0.0;
double maxr = 0.0;
double maxlen = 0.0;
double longest = 0.0;
double thickest = 0.0;
double shortest = fabs(ON_UNSET_VALUE);
ON_3dPoint C[4];
int notmonocount = 0;
while (leaf)
{
if ( s.Search(leaf->m_domain[0][0]) < 0 )
s.Append(leaf->m_domain[0][0]);
if ( s.Search(leaf->m_domain[0].ParameterAt(0.3)) < 0 )
s.Append(leaf->m_domain[0].ParameterAt(0.3));
if ( s.Search(leaf->m_domain[0].ParameterAt(0.8)) < 0 )
s.Append(leaf->m_domain[0].ParameterAt(0.8));
if ( t.Search(leaf->m_domain[1][0]) < 0 )
t.Append(leaf->m_domain[1][0]);
if ( t.Search(leaf->m_domain[1].ParameterAt(0.2)) < 0 )
t.Append(leaf->m_domain[1].ParameterAt(0.2));
if ( t.Search(leaf->m_domain[1].ParameterAt(0.7)) < 0 )
t.Append(leaf->m_domain[1].ParameterAt(0.7));
if( !leaf->m_bez->m_leafbox.m_bMono )
notmonocount++;
C[0] = leaf->m_bez->PointAt(0.0,0.0);
C[1] = leaf->m_bez->PointAt(1.0,0.0);
C[2] = leaf->m_bez->PointAt(1.0,1.0);
C[3] = leaf->m_bez->PointAt(0.0,1.0);
double len = C[3].DistanceTo(C[0]);
for ( i = 0; i < 3; i++ )
{
double x = C[i].DistanceTo(C[i+1]);
if ( x > len )
len = x;
}
if (len > longest )
longest = len;
if ( len < shortest )
shortest = len;
if ( leaf->m_bez->m_leafbox.Height() > thickest )
thickest = leaf->m_bez->m_leafbox.Height();
if ( leaf->m_bez->m_leafbox.Height() > maxar*len )
{
maxlen = len;
maxr = leaf->m_bez->m_leafbox.Height();
maxar = maxr/len;
}
leaf_count++;
leaf = leaf->NextLeaf();
}
if ( notmonocount > 0 )
{
text_log.Print("ON_SurfaceTree: %d spans, %d leaves (%d are not monotone).\n",span_count,leaf_count,notmonocount);
}
else
{
text_log.Print("ON_SurfaceTree: %d spans, %d leaves (all are monotone).\n",span_count,leaf_count);
}
text_log.PushIndent();
text_log.Print("Shortest: %g\n",shortest);
text_log.Print("Longest: %g\n",longest);
text_log.Print("Thickest: height = %g\n",thickest);
text_log.Print("Fattest: ht/len = %g (ht = %g, len = %g)\n",maxar,maxr,maxlen);
text_log.PopIndent();
// test evaluation
const bool bAdjustParameter = tree->AdjustParameter();
ON_2dPoint x,y;
double d0, ds, dt, tol0, tol1, tol2;
ON_3dVector X[3], Y[3], Xerr[3], Yerr[3];
ON_2dVector xerr, yerr;
double d0err=0.0, d1err=0.0;
int error_count = 0;
int test_count = 0;
int hint[2] ={0,0};
int quadrant = 0;
const ON_SurfaceTreeNode* evaluation_node;
for ( i = 0; i < s.Count(); i++ )
{
x.x = y.x = s[i];
for ( j = 0; j < t.Count(); j++ )
{
x.y = y.y = t[j];
if (( i > span_count0 || j > span_count1) && bAdjustParameter)
{
// NOTE - the surface paramter = nurbs form paramter
// at all span vector values.
surface->GetSurfaceParameterFromNurbFormParameter(s[i],t[j],&y.x,&y.y);
}
for (quadrant = 0; quadrant <= 4; quadrant++ )
{
test_count++;
X[0].x = -99.0;
Y[0].x = 99.0;
surface->Evaluate(y.x,y.y,1,3,&Y[0].x,quadrant,hint);
evaluation_node = tree->Evaluate(x.x,x.y,1,3,&X[0].x,quadrant);
if ( !evaluation_node )
{
error_count++;
ON_ERROR("TestSuraceTree() - tree failed to evaluate.");
d0err = 1.0e100;
d1err = 1.0e100;
xerr = x;
yerr = y;
Xerr[0] = ON_UNSET_POINT;
Xerr[1] = ON_UNSET_POINT;
Xerr[2] = ON_UNSET_POINT;
Yerr[0] = Y[0];
Yerr[1] = Y[1];
Yerr[2] = Y[2];
}
else
{
if ( x.x == evaluation_node->m_domain[0][0] && x.x > s[0] )
{
if ( 2 == quadrant || 3 == quadrant )
{
ON_ERROR("TestSuraceTree() ON_SurfaceTreeNode::Evaluate() used the wrong node.");
}
}
else if ( x.x == evaluation_node->m_domain[0][1] && x.x < s[span_count0] )
{
if ( 1 == quadrant || 4 == quadrant )
{
ON_ERROR("TestSuraceTree() ON_SurfaceTreeNode::Evaluate() used the wrong node.");
}
}
if ( x.y == evaluation_node->m_domain[1][0] && x.y > t[0] )
{
if ( 3 == quadrant || 4 == quadrant )
{
ON_ERROR("TestSuraceTree() ON_SurfaceTreeNode::Evaluate() used the wrong node.");
}
}
else if ( x.y == evaluation_node->m_domain[1][1] && x.y < t[span_count1] )
{
if ( 1 == quadrant || 2 == quadrant )
{
ON_ERROR("TestSuraceTree() ON_SurfaceTreeNode::Evaluate() used the wrong node.");
}
}
if ( bAdjustParameter )
{
// Different parameterizations result in derivatives
// with different lengths.
if ( X[1].Length() <= ON_ZERO_TOLERANCE && Y[1].Length() <= ON_ZERO_TOLERANCE )
{
// singular point
// Probably a place where the revolute hits the axis in a surface of revolution
X[1].Zero();
Y[1].Zero();
}
else
{
X[1].Unitize();
Y[1].Unitize();
}
if ( X[2].Length() <= ON_ZERO_TOLERANCE && Y[2].Length() <= ON_ZERO_TOLERANCE )
{
// singular point
// Probably a place where the revolute hits the axis in a surface of revolution.
X[2].Zero();
Y[2].Zero();
}
else
{
X[2].Unitize();
Y[2].Unitize();
}
}
tol0 = ON_ZERO_TOLERANCE + ON_SQRT_EPSILON*Y[0].MaximumCoordinate();
tol1 = ON_ZERO_TOLERANCE + ON_SQRT_EPSILON*Y[1].MaximumCoordinate();
tol2 = ON_ZERO_TOLERANCE + ON_SQRT_EPSILON*Y[2].MaximumCoordinate();
d0 = (X[0]-Y[0]).Length();
ds = (X[1]-Y[1]).Length();
dt = (X[2]-Y[2]).Length();
if ( d0 > tol0 || ds > tol1 || dt > tol2 )
{
rc = false;
error_count++;
if ( d0 > d0err || (ds > d1err && d0err < 0.01) || (dt > d1err && d0err < 0.01) )
{
if ( d0 > d0err) d0err = d0;
if ( ds > d1err) d1err = ds;
if ( dt > d1err) d1err = dt;
xerr = x;
yerr = y;
Xerr[0] = X[0];
Xerr[1] = X[1];
Xerr[2] = X[2];
Yerr[0] = Y[0];
Yerr[1] = Y[1];
Yerr[2] = Y[2];
}
}
}
}
}
}
text_log.PushIndent();
if ( error_count > 0 )
{
ON_ERROR("TestSurfaceTree() - tree failed evaluation test.");
text_log.Print("Evaluation test at %d points had %d errors.\n",test_count,error_count);
text_log.PushIndent();
text_log.Print("surface(%g,%g) = (%g,%g,%g)\n",yerr.x,yerr.y,Y[0].x,Y[0].y,Y[0].z);
text_log.Print("tree(%g,%g) = (%g,%g,%g)\n",xerr.x,xerr.y,X[0].x,X[0].y,X[0].z);
text_log.Print("curve Ds = (%g,%g,%g)\n",Y[1].x,Y[1].y,Y[1].z);
text_log.Print("tree Ds = (%g,%g,%g)\n",X[1].x,X[1].y,X[1].z);
text_log.Print("curve Dt = (%g,%g,%g)\n",Y[2].x,Y[2].y,Y[2].z);
text_log.Print("tree Dt = (%g,%g,%g)\n",X[2].x,X[2].y,X[2].z);
text_log.PopIndent();
}
else
{
text_log.Print("Evaluation test at %d points was successful.\n",test_count);
}
text_log.PopIndent();
return rc;
}
static
void TestCurveTreeHelper( ON_TextLog& text_log, const ON_Curve* curve, const wchar_t* name )
{
TL_NurbsCurve nurbs_curve;
if ( curve )
{
if ( 0 == name || 0 == *name)
{
name = L"anonymous";
}
curve->GetNurbForm(nurbs_curve);
text_log.Print(L"Curve class = %S, name = %s\n",
curve->ClassId()->ClassName(),
name
);
text_log.PushIndent();
text_log.Print(L"degree = %d, %s, CV count=%d\n",
nurbs_curve.m_order-1,
(nurbs_curve.m_is_rat ? L"rational" : L"non-rational"),
nurbs_curve.m_cv_count
);
TestCurveTree( text_log, curve );
text_log.PopIndent();
}
}
static
void TestSurfaceTreeHelper( ON_TextLog& text_log, const ON_Surface* surface, const wchar_t* name )
{
TL_NurbsSurface nurbs_surface;
if ( surface )
{
if ( 0 == name || 0 == *name)
{
name = L"anonymous";
}
surface->GetNurbForm(nurbs_surface);
text_log.Print(L"Surface class = %S, name = %s\n",
surface->ClassId()->ClassName(),
name
);
text_log.PushIndent();
text_log.Print(L"degree = (%d,%d), %s, CV count= (%d,%d)\n",
nurbs_surface.m_order[0]-1,
nurbs_surface.m_order[1]-1,
(nurbs_surface.m_is_rat ? L"rational" : L"non-rational"),
nurbs_surface.m_cv_count[0],
nurbs_surface.m_cv_count[1]
);
TestSurfaceTree( text_log, surface );
text_log.PopIndent();
}
}
void TestTree( const ONX_Model& model, ON_TextLog& text_log )
{
TEST_HEADER(text_log,"TestClosestPoint");
int i, k;
ON_wString name;
const wchar_t* attributes_name;
// first do curves
if (bDoCurves)
{
for ( i = 0; i < model.m_object_table.Count(); i++ )
{
const ON_Curve* curve = ON_Curve::Cast(model.m_object_table[i].m_object);
if ( curve )
{
attributes_name = model.m_object_table[i].m_attributes.m_name;
TestCurveTreeHelper(text_log,curve,attributes_name);
continue;
}
const ON_Brep* brep = ON_Brep::Cast(model.m_object_table[i].m_object);
if ( brep )
{
for ( k = 0; k < brep->m_C3.Count(); k++ )
{
curve = brep->m_C3[k];
if ( curve )
{
attributes_name = model.m_object_table[i].m_attributes.m_name;
if ( !attributes_name )
attributes_name = L"anonymous";
name.Format(L"%s - brep.m_C3[%d]",attributes_name,k);
TestCurveTreeHelper(text_log,curve,name);
}
}
for ( k = 0; k < brep->m_C2.Count(); k++ )
{
curve = brep->m_C2[k];
if ( curve )
{
attributes_name = model.m_object_table[i].m_attributes.m_name;
if ( !attributes_name )
attributes_name = L"anonymous";
name.Format(L"%s - brep.m_C2[%d]",attributes_name,k);
TestCurveTreeHelper(text_log,curve,name);
}
}
continue;
}
}
}
// then do surfaces
if ( bDoSurfaces )
{
for ( i = 0; i < model.m_object_table.Count(); i++ )
{
const ON_Surface* surface = ON_Surface::Cast(model.m_object_table[i].m_object);
if ( surface )
{
attributes_name = model.m_object_table[i].m_attributes.m_name;
TestSurfaceTreeHelper(text_log,surface,attributes_name);
continue;
}
const ON_Brep* brep = ON_Brep::Cast(model.m_object_table[i].m_object);
if ( brep )
{
for ( k = 0; k < brep->m_S.Count(); k++ )
{
surface = brep->m_S[k];
if ( surface )
{
attributes_name = model.m_object_table[i].m_attributes.m_name;
if ( !attributes_name )
attributes_name = L"anonymous";
name.Format(L"%s - brep.m_S[%d]",attributes_name,k);
TestSurfaceTreeHelper(text_log,surface,model.m_object_table[i].m_attributes.m_name);
}
}
continue;
}
}
}
}