mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-03 04:57:01 +08:00
1271 lines
29 KiB
C++
1271 lines
29 KiB
C++
/* $NoKeywords: $ */
|
|
/*
|
|
//
|
|
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
bool ON_BezierCage::Read(ON_BinaryArchive& archive)
|
|
{
|
|
Destroy();
|
|
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
|
|
if ( rc )
|
|
{
|
|
while(rc)
|
|
{
|
|
if ( major_version != 1 )
|
|
{
|
|
ON_ERROR("ON_BezierCage::Read - old code unable to read new version of chunk");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
int dim=0,order0=0,order1=0,order2=0;
|
|
bool is_rat=false;
|
|
|
|
rc = archive.ReadInt(&dim);
|
|
if (!rc)
|
|
break;
|
|
if (dim < 1 || dim > 10000)
|
|
{
|
|
ON_ERROR("ON_BezierCage::Read - invalid dim");
|
|
rc=false;
|
|
break;
|
|
}
|
|
|
|
rc = archive.ReadBool(&is_rat);
|
|
if (!rc)
|
|
break;
|
|
|
|
rc = archive.ReadInt(&order0);
|
|
if (!rc)
|
|
break;
|
|
if ( order0 < 2 || order0 > 10000 )
|
|
{
|
|
ON_ERROR("ON_BezierCage::Read - invalid order0");
|
|
rc=false;
|
|
break;
|
|
}
|
|
|
|
rc = archive.ReadInt(&order1);
|
|
if (!rc)
|
|
break;
|
|
if ( order1 < 2 || order1 > 10000 )
|
|
{
|
|
ON_ERROR("ON_BezierCage::Read - invalid order1");
|
|
rc=false;
|
|
break;
|
|
}
|
|
|
|
rc = archive.ReadInt(&order2);
|
|
if (!rc)
|
|
break;
|
|
if ( order2 < 2 || order2 > 10000 )
|
|
{
|
|
ON_ERROR("ON_BezierCage::Read - invalid order2");
|
|
rc=false;
|
|
break;
|
|
}
|
|
|
|
rc = Create(dim,is_rat,order0,order1,order2);
|
|
if (!rc)
|
|
break;
|
|
|
|
int i,j,k;
|
|
const int cv_dim = m_is_rat?(m_dim+1):m_dim;
|
|
for(i = 0; i < order0 && rc; i++)
|
|
{
|
|
for(j = 0; j < order1 && rc; j++)
|
|
{
|
|
for ( k = 0; k < order2 && rc; k++)
|
|
{
|
|
rc = archive.ReadDouble(cv_dim,CV(i,j,k));
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !archive.EndRead3dmChunk() )
|
|
{
|
|
rc = false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::Write(ON_BinaryArchive& archive) const
|
|
{
|
|
bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
|
|
|
|
if (rc)
|
|
{
|
|
rc = archive.WriteInt(m_dim);
|
|
if(rc)
|
|
rc = archive.WriteInt(m_is_rat);
|
|
if (rc)
|
|
rc = archive.WriteInt(m_order[0]);
|
|
if (rc)
|
|
rc = archive.WriteInt(m_order[1]);
|
|
if (rc)
|
|
rc = archive.WriteInt(m_order[2]);
|
|
int i,j,k;
|
|
const int cv_dim = m_is_rat?(m_dim+1):m_dim;
|
|
double* bogus_cv = 0;
|
|
for(i = 0; i < m_order[0] && rc; i++)
|
|
{
|
|
for(j = 0; j < m_order[1] && rc; j++)
|
|
{
|
|
for ( k = 0; k < m_order[2] && rc; k++)
|
|
{
|
|
const double* cv = CV(i,j,k);
|
|
if ( !cv )
|
|
{
|
|
if ( 0 == bogus_cv )
|
|
{
|
|
bogus_cv = (double*)onmalloc(cv_dim*sizeof(*bogus_cv));
|
|
for ( int n = 0; n < cv_dim; n++ )
|
|
bogus_cv[n] = ON_UNSET_VALUE;
|
|
}
|
|
cv = bogus_cv;
|
|
}
|
|
rc = archive.WriteDouble(cv_dim,cv);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 != bogus_cv )
|
|
onfree(bogus_cv);
|
|
|
|
if ( !archive.EndWrite3dmChunk() )
|
|
{
|
|
rc = false;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
ON_BezierCage::ON_BezierCage()
|
|
: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_order[2] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
m_cv_stride[2] = 0;
|
|
}
|
|
|
|
ON_BezierCage::ON_BezierCage( int dim, bool is_rat, int order0, int order1, int order2 )
|
|
: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_order[2] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
m_cv_stride[2] = 0;
|
|
Create( dim, is_rat, order0, order1, order2 );
|
|
}
|
|
|
|
ON_BezierCage::ON_BezierCage( const ON_BoundingBox& bbox, int order0, int order1, int order2 )
|
|
: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0)
|
|
{
|
|
Create(bbox,order0,order1,order2);
|
|
}
|
|
|
|
ON_BezierCage::ON_BezierCage( const ON_3dPoint* box_corners, int order0, int order1, int order2 )
|
|
: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0)
|
|
{
|
|
Create(box_corners,order0,order1,order2);
|
|
}
|
|
|
|
ON_BezierCage::~ON_BezierCage()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_BezierCage::ON_BezierCage(const ON_BezierCage& src)
|
|
: m_dim(0),m_is_rat(0),m_cv_capacity(0),m_cv(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_order[2] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
m_cv_stride[2] = 0;
|
|
*this = src;
|
|
}
|
|
|
|
ON_BezierCage& ON_BezierCage::operator=(const ON_BezierCage& src)
|
|
{
|
|
if ( this != &src ) {
|
|
if ( Create( src.m_dim, src.m_is_rat,
|
|
src.m_order[0], src.m_order[1], src.m_order[2] ) )
|
|
{
|
|
const int sizeof_cv = src.CVSize()*sizeof(m_cv[0]);
|
|
int i, j, k;
|
|
for ( i = 0; i < m_order[0]; i++ )
|
|
for ( j = 0; j < m_order[1]; j++ )
|
|
for ( k = 0; k < m_order[2]; k++ )
|
|
{
|
|
memcpy( CV(i,j,k), src.CV(i,j,k), sizeof_cv );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Destroy();
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_BezierCage::IsValid() const
|
|
{
|
|
if ( m_cv == nullptr )
|
|
return false;
|
|
|
|
if ( m_order[0] < 2 )
|
|
return false;
|
|
if ( m_order[1] < 2 )
|
|
return false;
|
|
if ( m_order[2] < 2 )
|
|
return false;
|
|
|
|
if ( m_dim <= 0 )
|
|
return false;
|
|
if ( m_is_rat != 0 && m_is_rat != 1 )
|
|
return false;
|
|
|
|
const int cvdim = m_is_rat ? (m_dim+1) : m_dim;
|
|
|
|
if ( m_cv_capacity > 0 && m_cv_capacity < cvdim*m_order[0]*m_order[1]*m_order[2] )
|
|
return false;
|
|
|
|
int i[3];
|
|
i[0] = (m_cv_stride[0] <= m_cv_stride[1]) ? 0 : 1;
|
|
i[1] = 1-i[0];
|
|
if ( m_cv_stride[2] < m_cv_stride[i[0]] )
|
|
{
|
|
i[2] = i[1];
|
|
i[1] = i[0];
|
|
i[0] = 2;
|
|
}
|
|
else if ( m_cv_stride[2] < m_cv_stride[i[1]] )
|
|
{
|
|
i[2] = i[1];
|
|
i[1] = 2;
|
|
}
|
|
else
|
|
{
|
|
i[2] = 2;
|
|
}
|
|
|
|
if ( m_cv_stride[i[0]] < cvdim )
|
|
return false;
|
|
if ( m_cv_stride[i[1]] < m_cv_stride[i[0]]*m_order[i[0]] )
|
|
return false;
|
|
if ( m_cv_stride[i[2]] < m_cv_stride[i[1]]*m_order[i[1]] )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ON_BezierCage::Dump( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print( "ON_BezierCage dim = %d is_rat = %d\n"
|
|
" order = (%d, %d, %d) \n",
|
|
m_dim, m_is_rat, m_order[0], m_order[1], m_order[2] );
|
|
dump.Print( "Control Points %d %s points\n"
|
|
" index value\n",
|
|
m_order[0]*m_order[1]*m_order[2],
|
|
(m_is_rat) ? "rational" : "non-rational" );
|
|
if ( !m_cv )
|
|
{
|
|
dump.Print(" nullptr cv array\n");
|
|
}
|
|
else
|
|
{
|
|
int i,j;
|
|
char sPreamble[128] = { 0 };
|
|
const size_t sPremable_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]);
|
|
for ( i = 0; i < m_order[0]; i++ )
|
|
{
|
|
for ( j = 0; j < m_order[1]; j++ )
|
|
{
|
|
if ( i > 0 || j > 0)
|
|
dump.Print("\n");
|
|
ON_String::FormatIntoBuffer(sPreamble, sPremable_capacity," CV[%2d][%2d]", i, j);
|
|
dump.PrintPointList( m_dim, m_is_rat,
|
|
m_order[2], m_cv_stride[2],
|
|
CV(i,j,0),
|
|
sPreamble );
|
|
}
|
|
if ( i < m_order[0]-1)
|
|
dump.Print("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
int ON_BezierCage::Dimension() const
|
|
{
|
|
return m_dim;
|
|
}
|
|
|
|
bool ON_BezierCage::Create( const ON_BoundingBox& bbox, int order0, int order1, int order2 )
|
|
{
|
|
/*
|
|
7______________6
|
|
|\ |\
|
|
| \ | \
|
|
| \ _____________\
|
|
| 4 | 5
|
|
| | | |
|
|
| | | |
|
|
3---|----------2 |
|
|
\ | \ |
|
|
\ |z \ |
|
|
y \ | \ |
|
|
\0_____________\1
|
|
x
|
|
*/
|
|
ON_3dPoint box_corners[8];
|
|
box_corners[0] = bbox.Corner(0,0,0);
|
|
box_corners[1] = bbox.Corner(1,0,0);
|
|
box_corners[2] = bbox.Corner(1,1,0);
|
|
box_corners[3] = bbox.Corner(0,1,0);
|
|
box_corners[4] = bbox.Corner(0,0,1);
|
|
box_corners[5] = bbox.Corner(1,0,1);
|
|
box_corners[6] = bbox.Corner(1,1,1);
|
|
box_corners[7] = bbox.Corner(0,1,1);
|
|
return Create(box_corners,order0,order1,order2);
|
|
}
|
|
|
|
bool ON_BezierCage::Create( int dim, bool is_rat, int order0, int order1, int order2 )
|
|
{
|
|
if ( m_cv_capacity < 1 )
|
|
m_cv = 0;
|
|
m_dim = (dim>0) ? dim : 0;
|
|
m_is_rat = is_rat ? 1 : 0;
|
|
m_order[0] = (order0 >= 2) ? order0 : 0;
|
|
m_order[1] = (order1 >= 2) ? order1 : 0;
|
|
m_order[2] = (order2 >= 2) ? order2 : 0;
|
|
m_cv_stride[2] = (m_dim > 0) ? m_dim+m_is_rat : 0;
|
|
m_cv_stride[1] = m_cv_stride[2]*m_order[2];
|
|
m_cv_stride[0] = m_cv_stride[1]*m_order[1];
|
|
m_cv_capacity = m_cv_stride[0]*m_order[0];
|
|
m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(m_cv[0]) );
|
|
return IsValid() ? true : false;
|
|
}
|
|
|
|
bool ON_BezierCage::Create(
|
|
const ON_3dPoint* box_corners,
|
|
int order0,
|
|
int order1,
|
|
int order2
|
|
)
|
|
{
|
|
int i, j, k;
|
|
double r,s,t;
|
|
|
|
if ( 0 == box_corners )
|
|
return false;
|
|
for( i = 0; i < 8; i++ )
|
|
{
|
|
if ( !box_corners[i].IsValid() )
|
|
return false;
|
|
}
|
|
|
|
// create trilinear "cube" to make it easy
|
|
// to calculate CV locations.
|
|
ON_BezierCage cube(3,0,2,2,2);
|
|
cube.SetCV(0,0,0,box_corners[0]);
|
|
cube.SetCV(1,0,0,box_corners[1]);
|
|
cube.SetCV(1,1,0,box_corners[2]);
|
|
cube.SetCV(0,1,0,box_corners[3]);
|
|
cube.SetCV(0,0,1,box_corners[4]);
|
|
cube.SetCV(1,0,1,box_corners[5]);
|
|
cube.SetCV(1,1,1,box_corners[6]);
|
|
cube.SetCV(0,1,1,box_corners[7]);
|
|
|
|
if ( 2 == order0 && 2 == order1 && 2 == order2 )
|
|
{
|
|
operator=(cube);
|
|
}
|
|
else
|
|
{
|
|
if (!Create(3,0,order0,order1,order2))
|
|
return false;
|
|
const int d0 = Degree(0);
|
|
const int d1 = Degree(1);
|
|
const int d2 = Degree(2);
|
|
|
|
for (i = 0; i <= d0; i++)
|
|
{
|
|
r = ((double)i)/((double)d0);
|
|
for (j = 0; j <= d1; j++)
|
|
{
|
|
s = ((double)j)/((double)d1);
|
|
for (k = 0; k <= d2; k++)
|
|
{
|
|
t = ((double)k)/((double)d2);
|
|
SetCV(i,j,k,cube.PointAt(r,s,t));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return IsValid();
|
|
}
|
|
|
|
|
|
void ON_BezierCage::Destroy()
|
|
{
|
|
if ( m_cv && m_cv_capacity > 0 )
|
|
onfree(m_cv);
|
|
m_cv_capacity = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
m_cv_stride[2] = 0;
|
|
m_cv = 0;
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_order[2] = 0;
|
|
}
|
|
|
|
void ON_BezierCage::EmergencyDestroy()
|
|
{
|
|
m_cv_capacity = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
m_cv_stride[2] = 0;
|
|
m_cv = 0;
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_order[2] = 0;
|
|
}
|
|
|
|
|
|
bool ON_BezierCage::GetBBox( // returns true if successful
|
|
double* boxmin, // minimum
|
|
double* boxmax, // maximum
|
|
bool bGrowBox // true means grow box
|
|
) const
|
|
{
|
|
int i, j;
|
|
bool rc = (m_order[0] > 0 && m_order[1] > 0 && m_order[2] > 0) ? true : false;
|
|
for ( i = 0; rc && i < m_order[0]; i++ )
|
|
for ( j = 0; rc && j < m_order[1]; j++ )
|
|
{
|
|
rc = ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order[2], m_cv_stride[2],
|
|
CV(i,j,0), boxmin, boxmax, bGrowBox );
|
|
bGrowBox = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::Transform( const ON_Xform& xform )
|
|
{
|
|
int i,j;
|
|
bool rc = (m_order[0] > 0 && m_order[1] > 0 && m_order[2]) ? true : false;
|
|
if (rc)
|
|
{
|
|
if ( 0 == m_is_rat )
|
|
{
|
|
if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 )
|
|
{
|
|
MakeRational();
|
|
}
|
|
}
|
|
|
|
for ( i = 0; rc && i < m_order[0]; i++ )
|
|
{
|
|
for ( j = 0; rc && j < m_order[1]; j++ )
|
|
{
|
|
rc = ON_TransformPointList( m_dim, m_is_rat,
|
|
m_order[2], m_cv_stride[2],
|
|
CV(i,j,0), xform );
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::Rotate(
|
|
double sin_angle, // sin(angle)
|
|
double cos_angle, // cos(angle)
|
|
const ON_3dVector& axis, // axis of rotation
|
|
const ON_3dPoint& center // center of rotation
|
|
)
|
|
{
|
|
ON_Xform rot;
|
|
rot.Rotation( sin_angle, cos_angle, axis, center );
|
|
return Transform( rot );
|
|
}
|
|
|
|
bool ON_BezierCage::Rotate(
|
|
double angle, // angle in radians
|
|
const ON_3dVector& axis, // axis of rotation
|
|
const ON_3dPoint& center // center of rotation
|
|
)
|
|
{
|
|
return Rotate( sin(angle), cos(angle), axis, center );
|
|
}
|
|
|
|
bool ON_BezierCage::Translate( const ON_3dVector& delta )
|
|
{
|
|
ON_Xform tr(ON_Xform::TranslationTransformation( delta ));
|
|
return Transform( tr );
|
|
}
|
|
|
|
bool ON_BezierCage::Scale( double x )
|
|
{
|
|
ON_Xform s(ON_Xform::DiagonalTransformation(x));
|
|
return Transform( s );
|
|
}
|
|
|
|
ON_Interval ON_BezierCage::Domain(
|
|
int // dir - formal parameter intentionally ignored in this virtual function
|
|
) const
|
|
{
|
|
return ON_Interval(0.0,1.0);
|
|
}
|
|
|
|
bool ON_BezierCage::Evaluate( // returns false if unable to evaluate
|
|
double r, double s, double t, // evaluation parameter
|
|
int der_count, // number of derivatives (>=0)
|
|
int v_stride, // array stride (>=Dimension())
|
|
double* v // array of length stride*(ndir+1)*(ndir+2)/2
|
|
) const
|
|
{
|
|
const int cvdim = m_is_rat?(m_dim+1):m_dim;
|
|
int i,j,k,n;
|
|
double Barray[64], vtmparray[10*4], Bi, Bij, Bijk, *Bj, *Bk, *vtmp;
|
|
const double* CVi;
|
|
const double* CVij;
|
|
const double* CVijk;
|
|
void* freeme1 = 0;
|
|
void* freeme2 = 0;
|
|
size_t sz;
|
|
|
|
if ( der_count > 0 )
|
|
{
|
|
// TODO - time to add support for derivative evaluation
|
|
ON_ERROR("ON_BezierCage::Evaluate does not evaluate derivatives");
|
|
}
|
|
|
|
sz = cvdim*sizeof(*vtmp);
|
|
vtmp = m_is_rat
|
|
? (sz <= sizeof(vtmparray) ? vtmparray : (double*)(freeme1 = onmalloc(sz)))
|
|
: v;
|
|
memset(vtmp,0,sz);
|
|
|
|
// get arrays to hold values of Bernstein basis functions
|
|
sz = (m_order[1]+m_order[2])*sizeof(*Bj);
|
|
Bj = (sz <= sizeof(Barray))
|
|
? Barray
|
|
: ((double*)(freeme2=onmalloc( sz ) ));
|
|
Bk = Bj + m_order[1];
|
|
|
|
const int d2 = m_order[2]-1;
|
|
for ( k = 0; k <= d2; k++)
|
|
{
|
|
Bk[k] = ON_EvaluateBernsteinBasis(d2,k,t);
|
|
}
|
|
|
|
const int d1 = m_order[1]-1;
|
|
for ( j = 0; j <= d1; j++)
|
|
{
|
|
Bj[j] = ON_EvaluateBernsteinBasis(d1,j,s);
|
|
}
|
|
|
|
const int d0 = m_order[0]-1;
|
|
|
|
for ( i = 0; i <= d0; i++ )
|
|
{
|
|
CVi = m_cv + i*m_cv_stride[0];
|
|
Bi = ON_EvaluateBernsteinBasis(d0,i,r);
|
|
for ( j = 0; j <= d1; j++ )
|
|
{
|
|
CVij = CVi + j*m_cv_stride[1];
|
|
Bij = Bi*Bj[j];
|
|
for ( k = 0; k <= d2; k++ )
|
|
{
|
|
CVijk = CVij + k*m_cv_stride[2];
|
|
Bijk = Bij*Bk[k];
|
|
n = cvdim;
|
|
while(n--)
|
|
{
|
|
*vtmp++ += *CVijk++ * Bijk;
|
|
}
|
|
vtmp -= cvdim;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_is_rat )
|
|
{
|
|
|
|
Bi = (vtmp[m_dim] != 0.0) ? (1.0/vtmp[m_dim]) : 1.0;
|
|
for ( n = 0; n < m_dim; n++ )
|
|
{
|
|
v[n] = vtmp[n]*Bi;
|
|
}
|
|
}
|
|
|
|
if ( 0 != freeme1 )
|
|
onfree(freeme1);
|
|
|
|
if ( 0 != freeme2 )
|
|
onfree(freeme2);
|
|
|
|
return (0 == der_count);
|
|
}
|
|
|
|
ON_3dPoint ON_BezierCage::PointAt(
|
|
double r,
|
|
double s,
|
|
double t
|
|
) const
|
|
{
|
|
ON_3dPoint pt;
|
|
if ( m_dim <= 3 )
|
|
{
|
|
pt.x = 0.0;
|
|
pt.y = 0.0;
|
|
pt.z = 0.0;
|
|
Evaluate(r,s,t,0,3,&pt.x);
|
|
}
|
|
else
|
|
{
|
|
double stack_buffer[16];
|
|
double* v;
|
|
size_t sizeof_buffer = m_dim*sizeof(*v);
|
|
v = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)onmalloc(sizeof_buffer);
|
|
v[0] = 0.0;
|
|
v[1] = 0.0;
|
|
v[2] = 0.0;
|
|
Evaluate(r,s,t,0,m_dim,v);
|
|
pt.x = v[0];
|
|
pt.y = v[1];
|
|
pt.z = v[2];
|
|
if ( v != stack_buffer )
|
|
onfree(v);
|
|
}
|
|
return pt;
|
|
}
|
|
|
|
ON_3dPoint ON_BezierCage::PointAt( ON_3dPoint rst ) const
|
|
{
|
|
ON_3dPoint pt;
|
|
if ( m_dim <= 3 )
|
|
{
|
|
pt.x = 0.0;
|
|
pt.y = 0.0;
|
|
pt.z = 0.0;
|
|
Evaluate(rst.x,rst.y,rst.z,0,3,&pt.x);
|
|
}
|
|
else
|
|
{
|
|
double stack_buffer[16];
|
|
double* v;
|
|
size_t sizeof_buffer = m_dim*sizeof(*v);
|
|
v = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)onmalloc(sizeof_buffer);
|
|
v[0] = 0.0;
|
|
v[1] = 0.0;
|
|
v[2] = 0.0;
|
|
Evaluate(rst.x,rst.y,rst.z,0,m_dim,v);
|
|
pt.x = v[0];
|
|
pt.y = v[1];
|
|
pt.z = v[2];
|
|
if ( v != stack_buffer )
|
|
onfree(v);
|
|
}
|
|
return pt;
|
|
}
|
|
|
|
bool ON_BezierCage::IsRational() const
|
|
{
|
|
return m_is_rat ? true : false;
|
|
}
|
|
|
|
int ON_BezierCage::CVSize() const
|
|
{
|
|
return ( m_is_rat && m_dim>0 ) ? m_dim+1 : m_dim;
|
|
}
|
|
|
|
int ON_BezierCage::Order( int dir ) const
|
|
{
|
|
return (dir>=0&&dir<=2) ? m_order[dir] : 0;
|
|
}
|
|
|
|
int ON_BezierCage::Degree(int dir) const
|
|
{
|
|
int order = Order(dir);
|
|
return (order>=2) ? order-1 : 0;
|
|
}
|
|
|
|
double* ON_BezierCage::CV( int i, int j, int k ) const
|
|
{
|
|
#if defined(ON_DEBUG)
|
|
if ( 0 == m_cv )
|
|
{
|
|
ON_ERROR("ON_BezierCage::CV - nullptr m_cv");
|
|
return 0;
|
|
}
|
|
if ( i < 0 || i >= m_order[0] || j< 0 || j >= m_order[1] || k < 0 || k >= m_order[2])
|
|
{
|
|
ON_ERROR("ON_BezierCage::CV - (i,j,k) out of range");
|
|
return 0;
|
|
}
|
|
#endif
|
|
return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2]) : 0;
|
|
}
|
|
|
|
ON::point_style ON_BezierCage::CVStyle() const
|
|
{
|
|
return m_is_rat ? ON::homogeneous_rational : ON::not_rational;
|
|
}
|
|
|
|
double ON_BezierCage::Weight( int i, int j, int k ) const
|
|
{
|
|
return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + k*m_cv_stride[2] + + m_dim] : 1.0;
|
|
}
|
|
|
|
|
|
bool ON_BezierCage::SetWeight( int i, int j, int k, double w )
|
|
{
|
|
bool rc = false;
|
|
if ( m_is_rat )
|
|
{
|
|
double* cv = CV(i,j,k);
|
|
if (cv)
|
|
{
|
|
cv[m_dim] = w;
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( w == 1.0 )
|
|
{
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::SetCV( int i, int j, int k, ON::point_style style, const double* Point )
|
|
{
|
|
bool rc = true;
|
|
int n;
|
|
double w;
|
|
|
|
double* cv = CV(i,j,k);
|
|
if ( !cv )
|
|
return false;
|
|
|
|
switch ( style ) {
|
|
|
|
case ON::not_rational: // input Point is not rational
|
|
memcpy( cv, Point, m_dim*sizeof(*cv) );
|
|
if ( IsRational() ) {
|
|
// NURBS surface is rational - set weight to one
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
break;
|
|
|
|
case ON::homogeneous_rational: // input Point is homogeneous rational
|
|
if ( IsRational() ) {
|
|
// NURBS surface is rational
|
|
memcpy( cv, Point, (m_dim+1)*sizeof(*cv) );
|
|
}
|
|
else {
|
|
// NURBS surface is not rational
|
|
w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0;
|
|
for ( n = 0; n < m_dim; n++ ) {
|
|
cv[n] = w*Point[n];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ON::euclidean_rational: // input Point is euclidean rational
|
|
if ( IsRational() ) {
|
|
// NURBS surface is rational - convert euclean point to homogeneous form
|
|
w = Point[m_dim];
|
|
for ( n = 0; n < m_dim; n++ )
|
|
cv[i] = w*Point[i];
|
|
cv[m_dim] = w;
|
|
}
|
|
else {
|
|
// NURBS surface is not rational
|
|
memcpy( cv, Point, m_dim*sizeof(*cv) );
|
|
}
|
|
break;
|
|
|
|
case ON::intrinsic_point_style:
|
|
n = m_is_rat?m_dim+1:m_dim;
|
|
memcpy(cv,Point,n*sizeof(*cv));
|
|
break;
|
|
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::SetCV( int i, int j, int k, const ON_3dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i,j,k);
|
|
if ( cv ) {
|
|
cv[0] = point.x;
|
|
if ( m_dim > 1 ) {
|
|
cv[1] = point.y;
|
|
if ( m_dim > 2 )
|
|
cv[2] = point.z;
|
|
}
|
|
if ( m_is_rat ) {
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::SetCV( int i, int j, int k, const ON_4dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i,j,k);
|
|
if ( cv ) {
|
|
if ( m_is_rat ) {
|
|
cv[0] = point.x;
|
|
if ( m_dim > 1 ) {
|
|
cv[1] = point.y;
|
|
if ( m_dim > 2 )
|
|
cv[2] = point.z;
|
|
}
|
|
cv[m_dim] = point.w;
|
|
rc = true;
|
|
}
|
|
else {
|
|
double w;
|
|
if ( point.w != 0.0 ) {
|
|
w = 1.0/point.w;
|
|
rc = true;
|
|
}
|
|
else {
|
|
w = 1.0;
|
|
}
|
|
cv[0] = w*point.x;
|
|
if ( m_dim > 1 ) {
|
|
cv[1] = w*point.y;
|
|
if ( m_dim > 2 ) {
|
|
cv[2] = w*point.z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::GetCV( int i, int j, int k, ON::point_style style, double* Point ) const
|
|
{
|
|
const double* cv = CV(i,j,k);
|
|
if ( !cv )
|
|
return false;
|
|
int dim = Dimension();
|
|
double w = ( IsRational() ) ? cv[dim] : 1.0;
|
|
switch(style) {
|
|
case ON::euclidean_rational:
|
|
Point[dim] = w;
|
|
// no break here
|
|
case ON::not_rational:
|
|
if ( w == 0.0 )
|
|
return false;
|
|
w = 1.0/w;
|
|
while(dim--) *Point++ = *cv++ * w;
|
|
break;
|
|
case ON::homogeneous_rational:
|
|
Point[dim] = w;
|
|
memcpy( Point, cv, dim*sizeof(*Point) );
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierCage::GetCV( int i, int j, int k, ON_3dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
const double* cv = CV(i,j,k);
|
|
if ( cv ) {
|
|
if ( m_is_rat ) {
|
|
if (cv[m_dim] != 0.0) {
|
|
const double w = 1.0/cv[m_dim];
|
|
point.x = cv[0]*w;
|
|
point.y = (m_dim>1)? cv[1]*w : 0.0;
|
|
point.z = (m_dim>2)? cv[2]*w : 0.0;
|
|
rc = true;
|
|
}
|
|
}
|
|
else {
|
|
point.x = cv[0];
|
|
point.y = (m_dim>1)? cv[1] : 0.0;
|
|
point.z = (m_dim>2)? cv[2] : 0.0;
|
|
rc = true;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::GetCV( int i, int j, int k, ON_4dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
const double* cv = CV(i,j,k);
|
|
if ( cv ) {
|
|
point.x = cv[0];
|
|
point.y = (m_dim>1)? cv[1] : 0.0;
|
|
point.z = (m_dim>2)? cv[2] : 0.0;
|
|
point.w = (m_is_rat) ? cv[m_dim] : 1.0;
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::ZeroCVs()
|
|
{
|
|
// zeros control vertices and, if rational, sets weights to 1
|
|
bool rc = false;
|
|
int i,j,k;
|
|
if ( m_cv ) {
|
|
if ( m_cv_capacity > 0 ) {
|
|
memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) );
|
|
if ( m_is_rat ) {
|
|
for ( i = 0; i < m_order[0]; i++ ) {
|
|
for ( j = 0; j < m_order[1]; j++ ) {
|
|
for ( k = 0; k < m_order[2]; k++ ) {
|
|
SetWeight( i,j,k, 1.0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rc = true;
|
|
}
|
|
else {
|
|
double* cv;
|
|
int s = CVSize()*sizeof(*cv);
|
|
for ( i = 0; i < m_order[0]; i++ ) {
|
|
for ( j = 0; j < m_order[1]; j++ ) {
|
|
for ( k = 0; k < m_order[2]; k++ ) {
|
|
cv = CV(i,j,k);
|
|
memset(cv,0,s);
|
|
if ( m_is_rat )
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
}
|
|
}
|
|
rc = (i>0) ? true : false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCage::MakeRational()
|
|
{
|
|
if ( !IsRational() )
|
|
{
|
|
ON_ERROR("TODO: fill in ON_BezierCage::MakeRational()");
|
|
/*
|
|
const int dim = Dimension();
|
|
if ( m_order[0] > 0 && m_order[1] > 0 && m_order[2] > 0 && dim > 0 ) {
|
|
const double* old_cv;
|
|
double* new_cv;
|
|
int cvi, cvj, j, cvstride;
|
|
if ( m_cv_stride[0] < m_cv_stride[1] ) {
|
|
cvstride = m_cv_stride[0] > dim ? m_cv_stride[0] : dim+1;
|
|
ReserveCVCapacity( cvstride*m_order[0]*m_order[1] );
|
|
new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1;
|
|
for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) {
|
|
for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) {
|
|
old_cv = CV(cvi,cvj)+dim-1;
|
|
*new_cv-- = 1.0;
|
|
for ( j = 0; j < dim; j++ ) {
|
|
*new_cv-- = *old_cv--;
|
|
}
|
|
}
|
|
}
|
|
m_cv_stride[0] = dim+1;
|
|
m_cv_stride[1] = (dim+1)*m_order[0];
|
|
}
|
|
else {
|
|
cvstride = m_cv_stride[1] > dim ? m_cv_stride[1] : dim+1;
|
|
ReserveCVCapacity( cvstride*m_order[0]*m_order[1] );
|
|
new_cv = m_cv + cvstride*m_order[0]*m_order[1]-1;
|
|
for ( cvi = m_order[0]-1; cvi >= 0; cvi-- ) {
|
|
for ( cvj = m_order[1]-1; cvj >= 0; cvj-- ) {
|
|
old_cv = CV(cvi,cvj)+dim-1;
|
|
*new_cv-- = 1.0;
|
|
for ( j = 0; j < dim; j++ ) {
|
|
*new_cv-- = *old_cv--;
|
|
}
|
|
}
|
|
}
|
|
m_cv_stride[1] = dim+1;
|
|
m_cv_stride[0] = (dim+1)*m_order[1];
|
|
}
|
|
m_is_rat = 1;
|
|
}
|
|
*/
|
|
}
|
|
return IsRational();
|
|
}
|
|
|
|
bool ON_BezierCage::MakeNonRational()
|
|
{
|
|
if ( IsRational() )
|
|
{
|
|
ON_ERROR("TODO: fill in ON_BezierCage::MakeNonRational()");
|
|
/*
|
|
const int dim = Dimension();
|
|
if ( m_order[0] > 0 && m_order[1] > 0 && dim > 0 ) {
|
|
double w;
|
|
const double* old_cv;
|
|
double* new_cv = m_cv;
|
|
int cvi, cvj, j;
|
|
if ( m_cv_stride[0] < m_cv_stride[1] ) {
|
|
for ( cvj = 0; cvj < m_order[1]; cvj++ ) {
|
|
for ( cvi = 0; cvi < m_order[0]; cvi++ ) {
|
|
old_cv = CV(cvi,cvj);
|
|
w = old_cv[dim];
|
|
w = ( w != 0.0 ) ? 1.0/w : 1.0;
|
|
for ( j = 0; j < dim; j++ ) {
|
|
*new_cv++ = w*(*old_cv++);
|
|
}
|
|
}
|
|
}
|
|
m_cv_stride[0] = dim;
|
|
m_cv_stride[1] = dim*m_order[0];
|
|
}
|
|
else {
|
|
for ( cvi = 0; cvi < m_order[0]; cvi++ ) {
|
|
for ( cvj = 0; cvj < m_order[1]; cvj++ ) {
|
|
old_cv = CV(cvi,cvj);
|
|
w = old_cv[dim];
|
|
w = ( w != 0.0 ) ? 1.0/w : 1.0;
|
|
for ( j = 0; j < dim; j++ ) {
|
|
*new_cv++ = w*(*old_cv++);
|
|
}
|
|
}
|
|
}
|
|
m_cv_stride[1] = dim;
|
|
m_cv_stride[0] = dim*m_order[1];
|
|
}
|
|
m_is_rat = 0;
|
|
}
|
|
*/
|
|
}
|
|
return ( !IsRational() ) ? true : false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Tools for managing CV and knot memory
|
|
bool ON_BezierCage::ReserveCVCapacity(
|
|
int capacity// number of doubles to reserve
|
|
)
|
|
{
|
|
if ( m_cv_capacity < capacity ) {
|
|
if ( m_cv ) {
|
|
if ( m_cv_capacity ) {
|
|
m_cv = (double*)onrealloc( m_cv, capacity*sizeof(*m_cv) );
|
|
m_cv_capacity = (m_cv) ? capacity : 0;
|
|
}
|
|
// else user supplied m_cv[] array
|
|
}
|
|
else {
|
|
m_cv = (double*)onmalloc( capacity*sizeof(*m_cv) );
|
|
m_cv_capacity = (m_cv) ? capacity : 0;
|
|
}
|
|
}
|
|
return ( m_cv ) ? true : false;
|
|
}
|
|
|
|
|
|
|
|
bool ON_BezierCage::IsSingular( // true if surface side is collapsed to a point
|
|
int side // side of parameter space to test
|
|
// 0 = south, 1 = east, 2 = north, 3 = west, 4 = bottom, 5 =top
|
|
) const
|
|
{
|
|
ON_ERROR("TODO: fill in ON_BezierCage::IsSingular\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
const ON_Xform& ON_BezierCageMorph::WorldToUnitCube() const
|
|
{
|
|
// maps world point into unit cube that can be morphed by
|
|
// evaluation of the BezierCage() function.
|
|
return m_xyz2rst;
|
|
}
|
|
|
|
const ON_BezierCage& ON_BezierCageMorph::BezierCage() const
|
|
{
|
|
// morphs a point in the unit cube into a world point.
|
|
return m_rst2xyz;
|
|
}
|
|
|
|
|
|
bool ON_BezierCageMorph::Transform(const ON_Xform& xform)
|
|
{
|
|
// transforms the VolumeMorph as a piece of geometry
|
|
return m_rst2xyz.Transform(xform);
|
|
}
|
|
|
|
bool ON_BezierCageMorph::Read(ON_BinaryArchive& archive)
|
|
{
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
|
|
if ( rc )
|
|
{
|
|
if ( major_version != 1 )
|
|
rc = false;
|
|
if (rc)
|
|
rc = archive.ReadXform(m_xyz2rst);
|
|
if(rc)
|
|
rc = m_rst2xyz.Read(archive);
|
|
if ( !archive.EndRead3dmChunk() )
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCageMorph::Write(ON_BinaryArchive& archive) const
|
|
{
|
|
bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
|
|
if (rc)
|
|
{
|
|
if (rc)
|
|
rc = archive.WriteXform(m_xyz2rst);
|
|
if(rc)
|
|
rc = m_rst2xyz.Write(archive);
|
|
if ( !archive.EndWrite3dmChunk() )
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
ON_BezierCageMorph::ON_BezierCageMorph() : m_bValid(0)
|
|
{
|
|
}
|
|
|
|
ON_BezierCageMorph::~ON_BezierCageMorph()
|
|
{
|
|
}
|
|
|
|
|
|
bool ON_BezierCageMorph::Create(
|
|
ON_3dPoint P0,
|
|
ON_3dPoint P1,
|
|
ON_3dPoint P2,
|
|
ON_3dPoint P3,
|
|
int point_countX,
|
|
int point_countY,
|
|
int point_countZ
|
|
)
|
|
{
|
|
if ( point_countX < 2 || point_countY < 2 || point_countZ < 2
|
|
|| !P0.IsValid()
|
|
|| !P1.IsValid()
|
|
|| !P2.IsValid()
|
|
|| !P3.IsValid() )
|
|
{
|
|
ON_ERROR("ON_BezierCageMorph::Create - invalid input");
|
|
}
|
|
|
|
m_bValid = false;
|
|
ON_3dVector X = P1-P0;
|
|
ON_3dVector Y = P2-P0;
|
|
ON_3dVector Z = P3-P0;
|
|
ON_Xform xform(ON_Xform::IdentityTransformation);
|
|
xform[0][0] = X.x;
|
|
xform[1][0] = X.y;
|
|
xform[2][0] = X.z;
|
|
xform[0][1] = Y.x;
|
|
xform[1][1] = Y.y;
|
|
xform[2][1] = Y.z;
|
|
xform[0][2] = Z.x;
|
|
xform[1][2] = Z.y;
|
|
xform[2][2] = Z.z;
|
|
xform[0][3] = P0.x;
|
|
xform[1][3] = P0.y;
|
|
xform[2][3] = P0.z;
|
|
double min_pivot = 0.0;
|
|
m_bValid = xform.Invert(&min_pivot);
|
|
if (m_bValid)
|
|
{
|
|
ON_3dPoint box_corners[8];
|
|
box_corners[0] = P0;
|
|
box_corners[1] = P1;
|
|
box_corners[2] = P0+X+Y;
|
|
box_corners[3] = P2;
|
|
box_corners[4] = P3;
|
|
box_corners[5] = P3+X;
|
|
box_corners[6] = P3+X+Y;
|
|
box_corners[7] = P3+Y;
|
|
m_bValid = m_rst2xyz.Create(box_corners,point_countX,point_countY,point_countZ);
|
|
m_xyz2rst = xform;
|
|
}
|
|
else
|
|
{
|
|
ON_ERROR("ON_BezierCageMorph::Create - invalid input - P0,P1,P2,P3 are coplanar");
|
|
m_rst2xyz.Destroy();
|
|
}
|
|
return m_bValid;
|
|
}
|
|
|
|
bool ON_BezierCageMorph::SetXform( ON_Xform world2unitcube )
|
|
{
|
|
m_xyz2rst = world2unitcube;
|
|
m_bValid = m_xyz2rst.IsValid() && m_rst2xyz.IsValid();
|
|
return m_bValid;
|
|
}
|
|
|
|
bool ON_BezierCageMorph::SetBezierCage( ON_BezierCage& unitcube2world )
|
|
{
|
|
m_rst2xyz = unitcube2world;
|
|
m_bValid = m_xyz2rst.IsValid() && m_rst2xyz.IsValid();
|
|
return m_bValid;
|
|
}
|