mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-08 08:35:53 +08:00
Co-authored-by: Bozo <bozo@mcneel.com> Co-authored-by: croudyj <croudyj@gmail.com> Co-authored-by: Dale Fugier <dale@mcneel.com> Co-authored-by: Dale Lear <dalelear@mcneel.com> Co-authored-by: Joshua Kennedy <joshuakennedy102@gmail.com> Co-authored-by: Jussi Aaltonen <jussi@mcneel.com> Co-authored-by: kike-garbo <kike@mcneel.com> Co-authored-by: Luis Fraguada <luis@mcneel.com> Co-authored-by: piac <giulio@mcneel.com> Co-authored-by: Pierre Cuvilliers <pierre@mcneel.com>
452 lines
9.9 KiB
C++
452 lines
9.9 KiB
C++
#include "../opennurbs.h"
|
|
#include "Tests.h"
|
|
|
|
class CF
|
|
{
|
|
public:
|
|
CF() {r = ON_UNSET_VALUE; s = ON_UNSET_VALUE; t = ON_UNSET_VALUE;}
|
|
double r, s, t;
|
|
double f[6]; // = x/wr
|
|
double g[6]; // = y/ws
|
|
double h[6]; // = z/wt
|
|
double x[6];
|
|
double y[6];
|
|
double z[6];
|
|
double wr[6];
|
|
double ws[6];
|
|
double wt[6];
|
|
};
|
|
|
|
class CV
|
|
{
|
|
public:
|
|
double F, X, W;
|
|
};
|
|
|
|
static class CV func(double r, double s, double t,
|
|
ON_3dex ijk)
|
|
{
|
|
static CF ev;
|
|
if ( r != ev.r )
|
|
{
|
|
ev.x[0] = tan(r);
|
|
ev.x[1] = 1.0+ev.x[0]*ev.x[0];
|
|
ev.x[2] = 2.0*ev.x[0]*ev.x[1];
|
|
ev.x[3] = 2.0*(ev.x[1]*ev.x[1] + ev.x[0]*ev.x[2]);
|
|
ev.x[4] = 2.0*(3.0*ev.x[1]*ev.x[2] + ev.x[0]*ev.x[3]);
|
|
ev.x[5] = 2.0*(3.0*ev.x[2]*ev.x[2] + 4.0*ev.x[1]*ev.x[3] + ev.x[0]*ev.x[4]);
|
|
ev.r = r;
|
|
|
|
ev.wr[0] = 1.0/cos(r);
|
|
ev.wr[1] = ev.wr[0]*ev.x[0];
|
|
ev.wr[2] = ev.wr[1]*ev.x[0] + ev.wr[0]*ev.x[1];
|
|
ev.wr[3] = ev.wr[2]*ev.x[0] + 2.0*ev.wr[1]*ev.x[1] + ev.wr[0]*ev.x[2];
|
|
ev.wr[4] = ev.wr[3]*ev.x[0] + 3.0*ev.wr[2]*ev.x[1] + 3.0*ev.wr[1]*ev.x[2] + ev.wr[0]*ev.x[3];
|
|
ev.wr[5] = ev.wr[4]*ev.x[0] + 4.0*ev.wr[3]*ev.x[1] + 6.0*ev.wr[2]*ev.x[2] + 4.0*ev.wr[1]*ev.x[3] + ev.wr[0]*ev.x[4];
|
|
|
|
ev.f[0] = sin(r);
|
|
ev.f[1] = cos(r);
|
|
ev.f[2] = -ev.f[0];
|
|
ev.f[3] = -ev.f[1];
|
|
ev.f[4] = -ev.f[2];
|
|
ev.f[5] = -ev.f[3];
|
|
}
|
|
|
|
if ( s != ev.s )
|
|
{
|
|
ev.y[0] = exp(3.0*s);
|
|
ev.y[1] = 3.0*ev.y[0];
|
|
ev.y[2] = 9.0*ev.y[0];
|
|
ev.y[3] = 27.0*ev.y[0];
|
|
ev.y[4] = 81.0*ev.y[0];
|
|
ev.y[5] = 243.0*ev.y[0];
|
|
|
|
ev.ws[0] = exp(2.0*s);
|
|
ev.ws[1] = 2.0*ev.ws[0];
|
|
ev.ws[2] = 4.0*ev.ws[0];
|
|
ev.ws[3] = 8.0*ev.ws[0];
|
|
ev.ws[4] = 16.0*ev.ws[0];
|
|
ev.ws[5] = 32.0*ev.ws[0];
|
|
|
|
ev.g[0] = exp(s);
|
|
ev.g[1] = ev.g[0];
|
|
ev.g[2] = ev.g[0];
|
|
ev.g[3] = ev.g[0];
|
|
ev.g[4] = ev.g[0];
|
|
ev.g[5] = ev.g[0];
|
|
ev.s = s;
|
|
}
|
|
|
|
if ( t != ev.t )
|
|
{
|
|
double t2 = t*t;
|
|
double t3 = t*t2;
|
|
double t4 = t*t3;
|
|
double t5 = t*t4;
|
|
double t6 = t*t5;
|
|
double t7 = t*t6;
|
|
|
|
ev.z[0] = t7;
|
|
ev.z[1] = 7.0*t6;
|
|
ev.z[2] = 42.0*t5;
|
|
ev.z[3] = 210.0*t4;
|
|
ev.z[4] = 840.0*t3;
|
|
ev.z[5] = 2520.0*t2;
|
|
|
|
ev.wt[0] = t5;
|
|
ev.wt[1] = 5.0*t4;
|
|
ev.wt[2] = 20.0*t3;
|
|
ev.wt[3] = 60.0*t2;
|
|
ev.wt[4] = 120.0*t;
|
|
ev.wt[5] = 120.0;
|
|
|
|
ev.h[0] = t2;
|
|
ev.h[1] = 2.0*t;
|
|
ev.h[2] = 2.0;
|
|
ev.h[3] = 0.0;
|
|
ev.h[4] = 0.0;
|
|
ev.h[5] = 0.0;
|
|
ev.t = t;
|
|
}
|
|
|
|
|
|
CV V;
|
|
V.X = ev.x[ijk.i]*ev.y[ijk.j]*ev.z[ijk.k];
|
|
V.W = ev.wr[ijk.i]*ev.ws[ijk.j]*ev.wt[ijk.k];
|
|
V.F = ev.f[ijk.i]*ev.g[ijk.j]*ev.h[ijk.k];
|
|
|
|
return V;
|
|
}
|
|
|
|
struct TEST_INPUT
|
|
{
|
|
int err_count;
|
|
int pass_count;
|
|
double der_tol[6];
|
|
int der_count; // 0 <= der_count <= 5
|
|
int dim; // dim > 0
|
|
int n; // 0 <= n < dim
|
|
int v_stride; // dim < v_stride <= 7
|
|
double v[6*7*8*7]; // >= (dc+1)*(dc+2)*(dc+3)&v_stride dc = max der_count, vs = max v_stride
|
|
double a[6*7*8]; // >= (dc+1)*(dc+2)*(dc+3)
|
|
ON_3dex ijk[6*7*8]; // >= (dc+1)*(dc+2)*(dc+3)
|
|
|
|
double maxe1;
|
|
double maxe2;
|
|
double maxe3;
|
|
};
|
|
|
|
static void TestQR1(double r, double s, double t,
|
|
int coord,
|
|
TEST_INPUT& TI
|
|
)
|
|
{
|
|
CV V;
|
|
double e;
|
|
|
|
int err_count = 0;
|
|
|
|
int ii = (coord==0)?1:0;
|
|
int jj = (coord==1)?1:0;
|
|
int kk = (coord==2)?1:0;
|
|
|
|
int dc;
|
|
|
|
for (dc = 0; dc <= TI.der_count; dc++ )
|
|
{
|
|
TI.ijk[dc].i = ii*dc;
|
|
TI.ijk[dc].j = jj*dc;
|
|
TI.ijk[dc].k = kk*dc;
|
|
V = func(r,s,t, TI.ijk[dc]);
|
|
TI.v[dc*TI.v_stride+TI.n] = V.X;
|
|
TI.v[dc*TI.v_stride+TI.dim] = V.W;
|
|
TI.a[dc] = V.F;
|
|
}
|
|
|
|
if ( !ON_EvaluateQuotientRule(TI.dim,TI.der_count,TI.v_stride,TI.v) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule - error");
|
|
TI.err_count += (TI.der_count+1);
|
|
}
|
|
else
|
|
{
|
|
for ( dc = 0; dc <= TI.der_count; dc++ )
|
|
{
|
|
e = fabs(TI.a[dc] - TI.v[dc*TI.v_stride+TI.n]);
|
|
if ( e > TI.der_tol[dc] && e > ON_SQRT_EPSILON*fabs(TI.a[dc]) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule - error");
|
|
TI.err_count++;
|
|
}
|
|
else
|
|
{
|
|
if ( e > TI.maxe1 )
|
|
TI.maxe1 = e;
|
|
TI.pass_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void TestQR2(double r, double s, double t,
|
|
int coord,
|
|
TEST_INPUT& TI
|
|
)
|
|
{
|
|
CV V;
|
|
double e;
|
|
|
|
int imax = (coord==0)?0:1;
|
|
int jmax = (coord==1)?0:1;
|
|
int kmax = (coord==2)?0:1;
|
|
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int mmax=0;
|
|
int m,der_count;
|
|
|
|
for ( der_count = 0; der_count <= TI.der_count; der_count++ )
|
|
{
|
|
for (m = 0; m <= der_count; m++ )
|
|
{
|
|
if ( 0 == coord )
|
|
{
|
|
TI.ijk[mmax].i = 0;
|
|
TI.ijk[mmax].j = der_count-m;
|
|
TI.ijk[mmax].k = m;
|
|
}
|
|
else if ( 1 == coord )
|
|
{
|
|
TI.ijk[mmax].i = der_count-m;
|
|
TI.ijk[mmax].j = 0;
|
|
TI.ijk[mmax].k = m;
|
|
}
|
|
else
|
|
{
|
|
TI.ijk[mmax].i = der_count-m;
|
|
TI.ijk[mmax].j = m;
|
|
TI.ijk[mmax].k = 0;
|
|
}
|
|
V = func(r,s,t, TI.ijk[mmax]);
|
|
TI.v[mmax*TI.v_stride+TI.n] = V.X;
|
|
TI.v[mmax*TI.v_stride+TI.dim] = V.W;
|
|
TI.a[mmax] = V.F;
|
|
mmax++;
|
|
}
|
|
}
|
|
|
|
if ( !ON_EvaluateQuotientRule2(TI.dim,TI.der_count,TI.v_stride,TI.v) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule2 - error");
|
|
TI.err_count += mmax;
|
|
}
|
|
else
|
|
{
|
|
for ( m = 0; m < mmax; m++ )
|
|
{
|
|
e = fabs(TI.a[m] - TI.v[m*TI.v_stride+TI.n]);
|
|
der_count = TI.ijk[m].i+TI.ijk[m].j+TI.ijk[m].k;
|
|
if ( e > TI.der_tol[der_count] && e > ON_SQRT_EPSILON*fabs(TI.a[m]) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule2 - error");
|
|
TI.err_count++;
|
|
}
|
|
else
|
|
{
|
|
if ( e > TI.maxe2 )
|
|
TI.maxe2 = e;
|
|
TI.pass_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void TestQR3(double r, double s, double t,
|
|
TEST_INPUT& TI
|
|
)
|
|
{
|
|
CV V;
|
|
double e;
|
|
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int mmax=0;
|
|
int m,der_count;
|
|
|
|
for ( der_count = 0; der_count <= TI.der_count; der_count++ )
|
|
{
|
|
for ( i = der_count; i >= 0; i-- )
|
|
{
|
|
for ( j = der_count-i; j >= 0; j-- )
|
|
{
|
|
k = der_count-i-j;
|
|
TI.ijk[mmax].i = i;
|
|
TI.ijk[mmax].j = j;
|
|
TI.ijk[mmax].k = k;
|
|
V = func(r,s,t, TI.ijk[mmax]);
|
|
TI.v[mmax*TI.v_stride+TI.n] = V.X;
|
|
TI.v[mmax*TI.v_stride+TI.dim] = V.W;
|
|
TI.a[mmax] = V.F;
|
|
mmax++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !ON_EvaluateQuotientRule3(TI.dim,TI.der_count,TI.v_stride,TI.v) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule3 - error");
|
|
TI.err_count += mmax;
|
|
}
|
|
else
|
|
{
|
|
for ( m = 0; m < mmax; m++ )
|
|
{
|
|
e = fabs(TI.a[m] - TI.v[m*TI.v_stride+TI.n]);
|
|
der_count = TI.ijk[m].i+TI.ijk[m].j+TI.ijk[m].k;
|
|
if ( e > TI.der_tol[der_count] && e > ON_SQRT_EPSILON*fabs(TI.a[m]) )
|
|
{
|
|
ON_ERROR("ON_EvaluateQuotientRule3 - error");
|
|
TI.err_count++;
|
|
}
|
|
else
|
|
{
|
|
if ( e > TI.maxe3 )
|
|
TI.maxe3 = e;
|
|
TI.pass_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TestQuotientRule( ON_TextLog& text_log )
|
|
{
|
|
|
|
double r,s,t;
|
|
struct TEST_INPUT TI;
|
|
|
|
memset(&TI,0,sizeof(TI));
|
|
TI.der_tol[0] = 1.0e-12;
|
|
TI.der_tol[1] = 1.0e-11;
|
|
TI.der_tol[2] = 1.0e-10;
|
|
TI.der_tol[3] = 1.0e-9;
|
|
TI.der_tol[4] = 1.0e-8;
|
|
TI.der_tol[5] = 5.0e-7;
|
|
|
|
TI.der_count = 0;
|
|
TI.dim = 1;
|
|
TI.n = 0;
|
|
TI.v_stride = TI.dim+1;
|
|
bool ok = true;
|
|
for (TI.der_count=0; TI.der_count<=5 && ok; TI.der_count++)
|
|
for (TI.dim = 1; TI.dim <= 4 && ok; TI.dim++ )
|
|
for (TI.n= 0; TI.n < TI.dim && ok; TI.n++)
|
|
for (TI.v_stride=TI.dim+1; TI.v_stride<=7 && ok; TI.v_stride++)
|
|
{
|
|
memset(TI.v,0,sizeof(TI.v));
|
|
|
|
// one paramter tests
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( r = -ON_PI/3.0; r <= ON_PI/3.0 && ok; r += ON_PI/120.0)
|
|
{
|
|
TestQR1(r,s,t,0,TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( s = -2.0; s <= 2.0 && ok; s += 0.1 )
|
|
{
|
|
TestQR1(r,s,t,1,TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( t = 0.1; t < 2.5 && ok; t += 0.1 )
|
|
{
|
|
TestQR1(r,s,t,2,TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
|
|
// two paramter tests
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( r = -ON_PI/3.0; r <= ON_PI/3.0 && ok; r += ON_PI/120.0)
|
|
{
|
|
for ( s = -2.0; s <= 2.0 && ok; s += 0.1 )
|
|
{
|
|
TestQR2(r,s,t, 2, TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( r = -ON_PI/3.0; r <= ON_PI/3.0 && ok; r += ON_PI/120.0)
|
|
{
|
|
for ( t = 0.1; t < 2.5 && ok; t += 0.1 )
|
|
{
|
|
TestQR2(r,s,t, 1, TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( s = -2.0; s <= 2.0 && ok; s += 0.1 )
|
|
{
|
|
for ( t = 0.1; t < 2.5 && ok; t += 0.1 )
|
|
{
|
|
TestQR2(r,s,t, 1, TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
// three paramter tests
|
|
r = 0.25*ON_PI;
|
|
s = 0.0;
|
|
t = 1.0;
|
|
|
|
for ( r = -ON_PI/3.0; r <= ON_PI/3.0 && ok; r += ON_PI/120.0)
|
|
{
|
|
for ( s = -2.0; s <= 2.0 && ok; s += 0.1 )
|
|
{
|
|
for ( t = 0.1; t < 2.5 && ok; t += 0.1 )
|
|
{
|
|
TestQR3(r,s,t, TI);
|
|
if (TI.err_count > 100 )
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
text_log.Print("pass_count = %d err_count = %d maxe1 = %g maxe2 = %g maxe3 = %g\n",
|
|
TI.pass_count,TI.err_count,TI.maxe1,TI.maxe2,TI.maxe3);
|
|
|
|
return (TI.err_count == 0);
|
|
}
|