mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 11:36:09 +08:00
3972 lines
96 KiB
C++
3972 lines
96 KiB
C++
//
|
|
// Copyright (c) 1993-2022 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
|
|
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#include <vector>
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
bool ON_BezierCurve::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
|
|
// The result from ON_GetPointListBoundingBox() is good enough
|
|
// for file IO needs in the public source code version.
|
|
return ON_GetPointListBoundingBox(
|
|
m_dim,
|
|
m_is_rat,
|
|
m_order,
|
|
m_cv_stride,
|
|
m_cv,
|
|
tight_bbox,
|
|
bGrowBox,
|
|
xform
|
|
);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
ON_PolynomialCurve::ON_PolynomialCurve()
|
|
: m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0)
|
|
{}
|
|
|
|
ON_PolynomialCurve::ON_PolynomialCurve( int dim, bool is_rat, int order )
|
|
: m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0)
|
|
{
|
|
Create( dim, is_rat, order );
|
|
}
|
|
|
|
ON_PolynomialCurve::~ON_PolynomialCurve()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_PolynomialCurve::ON_PolynomialCurve(const ON_PolynomialCurve& src)
|
|
: m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0)
|
|
{
|
|
*this = src;
|
|
}
|
|
|
|
ON_PolynomialCurve::ON_PolynomialCurve(const ON_BezierCurve& src)
|
|
: m_dim(0), m_is_rat(0), m_order(0), m_domain(0.0,1.0)
|
|
{
|
|
*this = src;
|
|
}
|
|
|
|
bool ON_PolynomialCurve::Create( int dim, bool is_rat, int order )
|
|
{
|
|
bool rc = true;
|
|
if ( dim > 0 ) m_dim = dim; else {m_dim = 0; rc = false;}
|
|
m_is_rat = is_rat?1:0;
|
|
if ( order >= 1 ) m_order = order; else {m_order = 0; rc = false;}
|
|
m_cv.SetCapacity(m_order);
|
|
m_domain.m_t[0] = 0.0;
|
|
m_domain.m_t[1] = 1.0;
|
|
return rc;
|
|
}
|
|
|
|
void ON_PolynomialCurve::Destroy()
|
|
{
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order = 0;
|
|
m_cv.Destroy();
|
|
m_domain.m_t[0] = 0.0;
|
|
m_domain.m_t[1] = 1.0;
|
|
}
|
|
|
|
|
|
ON_PolynomialCurve& ON_PolynomialCurve::operator=(const ON_PolynomialCurve& src)
|
|
{
|
|
if ( this != &src ) {
|
|
m_dim = src.m_dim;
|
|
m_is_rat = src.m_is_rat;
|
|
m_order = src.m_order;
|
|
m_cv = src.m_cv;
|
|
m_domain = src.m_domain;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_PolynomialCurve& ON_PolynomialCurve::operator=(const ON_BezierCurve& src)
|
|
{
|
|
int i;
|
|
double d;
|
|
m_dim = src.m_dim;
|
|
m_is_rat = src.m_is_rat;
|
|
m_order = src.m_order;
|
|
m_cv.Reserve( src.m_order );
|
|
m_cv.SetCount( src.m_order );
|
|
m_cv.Zero();
|
|
//m_domain = src.m_domain;
|
|
|
|
if ( m_order >= 2 && src.CVSize() <= 4 ) {
|
|
ON_BezierCurve s; // scratch surface for homogeneous evaluation
|
|
s.m_dim = src.m_is_rat ? src.m_dim+1 : src.m_dim;
|
|
s.m_is_rat = 0;
|
|
s.m_order = src.m_order;
|
|
s.m_cv = src.m_cv;
|
|
//s.m_domain.m_t[0] = 0.0;
|
|
//s.m_domain.m_t[1] = 1.0;
|
|
if ( s.Evaluate( 0.0, m_order-1, 4, &m_cv[0].x ) ) {
|
|
if ( m_is_rat ) {
|
|
if ( m_dim < 3 ) {
|
|
for ( i = 0; i < m_order; i++ ) {
|
|
ON_4dPoint& cv = m_cv[i];
|
|
cv.w = cv[m_dim];
|
|
cv[m_dim] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
m_cv[0].w = 1.0;
|
|
}
|
|
for ( i = 2; i < m_order; i++ ) {
|
|
d = 1.0/i;
|
|
ON_4dPoint& cv = m_cv[i];
|
|
cv.x *= d;
|
|
cv.y *= d;
|
|
cv.z *= d;
|
|
cv.w *= d;
|
|
}
|
|
}
|
|
else {
|
|
m_cv.Zero();
|
|
m_cv[0].w = 1.0;
|
|
}
|
|
s.m_cv = 0;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool ON_PolynomialCurve::Evaluate( // returns false if unable to evaluate
|
|
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)
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
if ( m_order >= 1 && m_cv.Count() == m_order )
|
|
{
|
|
if ( m_domain.m_t[0] != 0.0 || m_domain.m_t[1] != 1.0 )
|
|
{
|
|
t = (1.0-t)*m_domain.m_t[0] + t*m_domain.m_t[1];
|
|
}
|
|
ON_4dPointArray p(der_count+1);
|
|
ON_4dPoint c;
|
|
double s;
|
|
int i, j, der;
|
|
p.Zero();
|
|
for ( i = m_order-1; i >= 0; i-- ) {
|
|
c = m_cv[i];
|
|
p[0].x = t*p[0].x + c.x;
|
|
p[0].y = t*p[0].y + c.y;
|
|
p[0].z = t*p[0].z + c.z;
|
|
p[0].w = t*p[0].w + c.w;
|
|
}
|
|
if ( der_count >= 1 ) {
|
|
for ( i = m_order-1; i >= 1; i-- ) {
|
|
c = m_cv[i];
|
|
p[1].x = t*p[1].x + i*c.x;
|
|
p[1].y = t*p[1].y + i*c.y;
|
|
p[1].z = t*p[1].z + i*c.z;
|
|
p[1].w = t*p[1].w + i*c.w;
|
|
}
|
|
for ( der = 2; der <= der_count; der++ ) {
|
|
for ( i = m_order-1; i >= der; i-- ) {
|
|
s = i;
|
|
for ( j = 1; j < der; j++ ) {
|
|
s *= (i-j);
|
|
}
|
|
c = m_cv[i];
|
|
p[der].x = t*p[der].x + s*c.x;
|
|
p[der].y = t*p[der].y + s*c.y;
|
|
p[der].z = t*p[der].z + s*c.z;
|
|
p[der].w = t*p[der].w + s*c.w;
|
|
}
|
|
}
|
|
if ( m_is_rat ) {
|
|
ON_EvaluateQuotientRule( 3, der_count, 4, &p[0].x );
|
|
}
|
|
}
|
|
const int sz = m_dim*sizeof(v[0]);
|
|
for ( i = 0; i <= der_count; i++ ) {
|
|
memcpy( v, &p[i].x, sz );
|
|
v += v_stride;
|
|
}
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON_PolynomialSurface::ON_PolynomialSurface() : m_dim(0), m_is_rat(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_domain[0].m_t[0] = 0.0;
|
|
m_domain[0].m_t[1] = 1.0;
|
|
m_domain[1].m_t[0] = 0.0;
|
|
m_domain[1].m_t[1] = 1.0;
|
|
}
|
|
|
|
ON_PolynomialSurface::ON_PolynomialSurface( int dim, bool is_rat, int order0, int order1 )
|
|
{
|
|
Create( dim, is_rat, order0, order1 );
|
|
}
|
|
|
|
ON_PolynomialSurface::~ON_PolynomialSurface()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_PolynomialSurface::ON_PolynomialSurface(const ON_PolynomialSurface& src)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_domain[0].m_t[0] = 0.0;
|
|
m_domain[0].m_t[1] = 1.0;
|
|
m_domain[1].m_t[0] = 0.0;
|
|
m_domain[1].m_t[1] = 1.0;
|
|
*this = src;
|
|
}
|
|
|
|
ON_PolynomialSurface::ON_PolynomialSurface(const ON_BezierSurface& src)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_domain[0].m_t[0] = 0.0;
|
|
m_domain[0].m_t[1] = 1.0;
|
|
m_domain[1].m_t[0] = 0.0;
|
|
m_domain[1].m_t[1] = 1.0;
|
|
*this = src;
|
|
}
|
|
|
|
ON_PolynomialSurface& ON_PolynomialSurface::operator=(const ON_PolynomialSurface& src)
|
|
{
|
|
if ( this != &src ) {
|
|
if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) {
|
|
m_cv = src.m_cv;
|
|
m_domain[0] = src.m_domain[0];
|
|
m_domain[1] = src.m_domain[1];
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_PolynomialSurface& ON_PolynomialSurface::operator=(const ON_BezierSurface& src)
|
|
{
|
|
if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) {
|
|
//m_domain[0] = src.m_domain[0];
|
|
//m_domain[1] = src.m_domain[1];
|
|
// TODO - convert coefficients from Bernstein to monomial basis
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_PolynomialSurface::Create( int dim, bool is_rat, int order0, int order1 )
|
|
{
|
|
bool rc = true;
|
|
if ( dim > 0 ) m_dim = dim; else {m_dim = 0; rc = false;};
|
|
m_is_rat = is_rat?1:0;
|
|
if ( order0 > 0 ) m_order[0] = order0; else { m_order[0] = 0; rc = false;}
|
|
if ( order1 > 0 ) m_order[1] = order1; else { m_order[1] = 0; rc = false;}
|
|
m_cv.SetCapacity( m_order[0]*m_order[1] );
|
|
if ( m_order[0] > 0 && m_order[1] > 0 ) {
|
|
m_cv.Zero();
|
|
m_cv[0].w = 1.0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void ON_PolynomialSurface::Destroy()
|
|
{
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_cv.Destroy();
|
|
}
|
|
|
|
bool ON_PolynomialSurface::Evaluate( // returns false if unable to evaluate
|
|
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
|
|
{
|
|
ON_ERROR("TODO: - finish ON_PolynomialSurface::Evaluate()\n");
|
|
return false;
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve()
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve( int dim, bool is_rat, int order )
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
Create(dim,is_rat,order);
|
|
}
|
|
|
|
ON_BezierCurve::~ON_BezierCurve()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve(const ON_BezierCurve& src)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
*this = src;
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve(const ON_PolynomialCurve& src)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
*this = src;
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve(const ON_2dPointArray& cv)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
*this = cv;
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve(const ON_3dPointArray& cv)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
*this = cv;
|
|
}
|
|
|
|
ON_BezierCurve::ON_BezierCurve(const ON_4dPointArray& cv)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_order(0),
|
|
m_cv_stride(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierCurve = 0;
|
|
#endif
|
|
*this = cv;
|
|
}
|
|
|
|
ON_BezierCurve& ON_BezierCurve::operator=(const ON_2dPointArray& cv)
|
|
{
|
|
if ( Create( 2, false, cv.Count() ) )
|
|
{
|
|
int i;
|
|
for ( i = 0; i < m_order; i++ )
|
|
SetCV( i, ON::intrinsic_point_style, cv[i] );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_BezierCurve& ON_BezierCurve::operator=(const ON_3dPointArray& cv)
|
|
{
|
|
if ( Create( 3, false, cv.Count() ) )
|
|
{
|
|
int i;
|
|
for ( i = 0; i < m_order; i++ )
|
|
SetCV( i, ON::intrinsic_point_style, cv[i] );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_BezierCurve& ON_BezierCurve::operator=(const ON_4dPointArray& cv)
|
|
{
|
|
if ( Create( 3, true, cv.Count() ) )
|
|
{
|
|
int i;
|
|
for ( i = 0; i < m_order; i++ )
|
|
SetCV( i, ON::intrinsic_point_style, cv[i] );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
ON_BezierCurve& ON_BezierCurve::operator=(const ON_BezierCurve& src)
|
|
{
|
|
int i;
|
|
if ( this != &src ) {
|
|
//m_domain = src.m_domain;
|
|
if ( Create( src.m_dim, src.m_is_rat, src.m_order ) ) {
|
|
const int sizeof_cv = CVSize()*sizeof(m_cv[0]);
|
|
for ( i = 0; i < m_order; i++ ) {
|
|
memcpy( CV(i), src.CV(i), sizeof_cv );
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_BezierCurve& ON_BezierCurve::operator=(const ON_PolynomialCurve& src)
|
|
{
|
|
//m_domain = src.m_domain;
|
|
if ( src.m_dim > 0 && src.m_cv.Count() == src.m_order && src.m_order >= 2 )
|
|
{
|
|
int i;
|
|
|
|
ON_PolynomialCurve s; // scratch poly for homogeneous evaluation
|
|
s.m_dim = src.m_is_rat ? 4 : src.m_dim;
|
|
s.m_is_rat = 0;
|
|
s.m_domain.m_t[0] = 0.0;
|
|
s.m_domain.m_t[1] = 1.0;
|
|
s.m_order = src.m_order;
|
|
s.m_cv = src.m_cv;
|
|
|
|
if ( src.m_is_rat ) {
|
|
m_dim++;
|
|
m_is_rat = 0;
|
|
}
|
|
const int degree = src.m_order-1;
|
|
const double d = 1.0/degree;
|
|
double t;
|
|
ON_4dPointArray pt(src.m_order);
|
|
for ( i = 0; i < src.m_order; i++ )
|
|
{
|
|
if ( i == 0 )
|
|
t = 0.0;
|
|
else if ( i < degree )
|
|
t = i*d;
|
|
else
|
|
t = 1.0;
|
|
s.Evaluate( t, 0, 4, pt[i] );
|
|
}
|
|
s.m_cv = 0;
|
|
if ( src.m_is_rat && src.m_dim < 3 ) {
|
|
for ( i = 0; i < src.m_order; i++ )
|
|
pt[i][src.m_dim] = pt[i].w;
|
|
}
|
|
Loft( src.m_is_rat ? src.m_dim+1 : src.m_dim, src.m_order, 4, &pt[0].x, 0, nullptr );
|
|
if ( IsValid() && src.m_is_rat ) {
|
|
m_is_rat = 1;
|
|
m_dim--;
|
|
}
|
|
}
|
|
else {
|
|
Destroy();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool ON_BezierCurve::Loft( const ON_3dPointArray& pt )
|
|
{
|
|
const ON_3dPoint* p0 = pt.Array();
|
|
return Loft( 3, pt.Count(), 3, p0?&p0->x:0, 0, nullptr );
|
|
}
|
|
|
|
|
|
bool ON_BezierCurve::Loft( int pt_dim, int pt_count, int pt_stride, const double* pt,
|
|
int t_stride, const double* t )
|
|
{
|
|
bool rc = false;
|
|
if ( pt_dim >= 1 && pt_count >= 2 && pt_stride >= pt_dim && pt != nullptr && (t_stride >= 1 || t == 0) ) {
|
|
int i, j;
|
|
ON_SimpleArray<double> uniform_t;
|
|
double s;
|
|
if ( !t ) {
|
|
uniform_t.Reserve(pt_count);
|
|
s = 1.0/(pt_count-1);
|
|
for ( i = 0; i < pt_count; i++ ) {
|
|
uniform_t.Append(i*s);
|
|
}
|
|
uniform_t[0] = 0.0;
|
|
uniform_t[pt_count-1] = 1.0;
|
|
t = uniform_t.Array();
|
|
t_stride = 1;
|
|
}
|
|
Create( pt_dim, false, pt_count );
|
|
const int sizeof_cv = CVSize()*sizeof(m_cv[0]);
|
|
const int degree = m_order-1;
|
|
const double t0 = t[0];
|
|
const double t1 = t[t_stride*(pt_count-1)];
|
|
const double tm = 0.5*(t1-t0);
|
|
const double d = (t1-t0);
|
|
ON_Matrix M( m_order, m_order );
|
|
for ( i = 0; i < m_order; i++ ) {
|
|
if ( t[i] <= tm ) {
|
|
s = (t[i] - t[0])/d;
|
|
}
|
|
else {
|
|
s = 1.0 - (t1 - t[i])/d;
|
|
}
|
|
for ( j = 0; j < m_order; j++ ) {
|
|
M.m[i][j] = ON_EvaluateBernsteinBasis( degree, j, s );
|
|
}
|
|
memcpy( m_cv + i*m_cv_stride, pt + i*pt_stride, sizeof_cv );
|
|
}
|
|
|
|
int rank = M.RowReduce( ON_EPSILON, m_dim, m_cv_stride, m_cv );
|
|
M.BackSolve( ON_EPSILON, m_dim, m_order,
|
|
m_cv_stride, m_cv,
|
|
m_cv_stride, m_cv );
|
|
if ( rank == m_order )
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::IsValid() const
|
|
{
|
|
if ( m_dim <= 0 )
|
|
return false;
|
|
if ( m_is_rat != 0 && m_is_rat != 1 )
|
|
return false;
|
|
if ( m_order < 2 )
|
|
return false;
|
|
if ( m_cv_stride < m_dim+m_is_rat )
|
|
return false;
|
|
if ( m_cv_capacity > 0 && m_cv_capacity < m_cv_stride*m_order )
|
|
return false;
|
|
if ( m_cv == nullptr )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void ON_BezierCurve::Dump( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print( "ON_BezierCurve dim = %d is_rat = %d\n"
|
|
" order = %d \n",
|
|
m_dim, m_is_rat, m_order );
|
|
dump.Print( "Control Points %d %s points\n"
|
|
" index value\n",
|
|
m_order,
|
|
(m_is_rat) ? "rational" : "non-rational" );
|
|
if ( !m_cv ) {
|
|
dump.Print(" nullptr cv array\n");
|
|
}
|
|
else {
|
|
dump.PrintPointList( m_dim, m_is_rat,
|
|
m_order, m_cv_stride,
|
|
m_cv,
|
|
" CV" );
|
|
}
|
|
}
|
|
|
|
int ON_BezierCurve::Dimension() const
|
|
{
|
|
return m_dim;
|
|
}
|
|
|
|
bool ON_BezierCurve::Create( int dim, bool is_rat, int order )
|
|
{
|
|
m_dim = (dim>0) ? dim : 0;
|
|
m_is_rat = is_rat ? 1 : 0;
|
|
// 7 July 2005 Dale Lear
|
|
// Permit order=1 (degree=0) beziers with 1 cv
|
|
// for dots.
|
|
m_order = (order >= 1) ? order : 0;
|
|
m_cv_stride = (m_dim > 0) ? m_dim+m_is_rat : 0;
|
|
m_cv_capacity = m_cv_stride*m_order;
|
|
m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(m_cv[0]) );
|
|
//m_domain.m_t[0] = 0.0;
|
|
//m_domain.m_t[1] = 1.0;
|
|
return IsValid();
|
|
}
|
|
|
|
void ON_BezierCurve::Destroy()
|
|
{
|
|
if ( m_cv && m_cv_capacity > 0 )
|
|
onfree(m_cv);
|
|
m_cv_capacity = 0;
|
|
m_cv_stride = 0;
|
|
m_cv = 0;
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order = 0;
|
|
//m_domain.m_t[0] = 0.0;
|
|
//m_domain.m_t[1] = 1.0;
|
|
}
|
|
|
|
void ON_BezierCurve::EmergencyDestroy()
|
|
{
|
|
m_cv = 0;
|
|
}
|
|
|
|
bool ON_BezierCurve::GetBBox( // returns true if successful
|
|
double* boxmin, // minimum
|
|
double* boxmax, // maximum
|
|
bool bGrowBox
|
|
) const
|
|
{
|
|
return ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, boxmin, boxmax, bGrowBox );
|
|
}
|
|
|
|
bool ON_BezierCurve::GetBoundingBox( // returns true if successful
|
|
ON_BoundingBox& bbox,
|
|
int bGrowBox // true means grow box
|
|
) const
|
|
{
|
|
void* heap_buffer = 0;
|
|
double *boxmin, *boxmax;
|
|
|
|
if ( m_dim > 3 )
|
|
{
|
|
heap_buffer = onmalloc(2*m_dim*sizeof(*boxmin));
|
|
boxmin = (double*)heap_buffer;
|
|
memset( boxmin, 0, 2*m_dim*sizeof(*boxmin) );
|
|
boxmax = boxmin + m_dim;
|
|
if ( bGrowBox )
|
|
{
|
|
boxmin[0] = bbox.m_min.x;
|
|
boxmin[1] = bbox.m_min.y;
|
|
boxmin[2] = bbox.m_min.z;
|
|
boxmax[0] = bbox.m_max.x;
|
|
boxmax[1] = bbox.m_max.y;
|
|
boxmax[2] = bbox.m_max.z;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boxmin = &bbox.m_min.x;
|
|
boxmax = &bbox.m_max.x;
|
|
}
|
|
|
|
bool rc = GetBBox( boxmin, boxmax, bGrowBox );
|
|
|
|
if ( rc && m_dim > 3 )
|
|
{
|
|
bbox.m_min = boxmin;
|
|
bbox.m_max = boxmax;
|
|
}
|
|
|
|
if ( 0 != heap_buffer )
|
|
onfree(heap_buffer);
|
|
|
|
return rc;
|
|
}
|
|
|
|
ON_BoundingBox ON_BezierCurve::BoundingBox() const
|
|
{
|
|
ON_BoundingBox bbox;
|
|
GetBoundingBox( bbox );
|
|
return bbox;
|
|
}
|
|
|
|
bool ON_WorldBBoxIsInTightBBox(
|
|
const ON_BoundingBox& tight_bbox,
|
|
const ON_BoundingBox& world_bbox,
|
|
const ON_Xform* xform
|
|
)
|
|
{
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
ON_3dPoint P, Q;
|
|
int i,j,k;
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
P.x = (i) ? world_bbox.m_min.x : world_bbox.m_max.x;
|
|
for ( j = 0; j < 2; j++ )
|
|
{
|
|
P.y = (j) ? world_bbox.m_min.y : world_bbox.m_max.y;
|
|
for ( k = 0; k < 2; k++ )
|
|
{
|
|
P.z = (k) ? world_bbox.m_min.z : world_bbox.m_max.z;
|
|
Q = (*xform)*P;
|
|
if ( !tight_bbox.IsPointIn(Q) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return ( tight_bbox.Includes(world_bbox) );
|
|
}
|
|
|
|
bool ON_Line::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
ON_3dPoint P = (*xform)*from;
|
|
tight_bbox.Set(P,bGrowBox);
|
|
bGrowBox = true;
|
|
P = (*xform)*to;
|
|
tight_bbox.Set(P,bGrowBox);
|
|
}
|
|
else
|
|
{
|
|
tight_bbox.Set(from,bGrowBox);
|
|
bGrowBox = true;
|
|
tight_bbox.Set(to,bGrowBox);
|
|
}
|
|
|
|
return (0!=bGrowBox);
|
|
}
|
|
|
|
bool ON_Arc::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if ( IsCircle() && (nullptr == xform || xform->IsIdentity()) )
|
|
{
|
|
return ON_Circle::GetTightBoundingBox(tight_bbox,bGrowBox,nullptr);
|
|
}
|
|
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
if ( !bGrowBox )
|
|
{
|
|
tight_bbox.Destroy();
|
|
}
|
|
|
|
// Using the nurbs_knot[] and nurbs_cv[] arrays
|
|
// removes all calls to onmalloc() and onfree().
|
|
double nurbs_knot[10];
|
|
ON_4dPoint nurbs_cv[9];
|
|
ON_NurbsCurve nurbs_arc;
|
|
nurbs_arc.m_knot = nurbs_knot;
|
|
nurbs_arc.m_cv = &nurbs_cv[0].x;
|
|
if ( GetNurbForm(nurbs_arc) )
|
|
{
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
nurbs_arc.Transform(*xform);
|
|
}
|
|
ON_BezierCurve bez_arc;
|
|
bez_arc.m_dim = nurbs_arc.m_dim;
|
|
bez_arc.m_is_rat = nurbs_arc.m_is_rat;
|
|
bez_arc.m_order = nurbs_arc.m_order;
|
|
bez_arc.m_cv_stride = nurbs_arc.m_cv_stride;
|
|
bez_arc.m_cv = nurbs_arc.m_cv;
|
|
int i;
|
|
for ( i = nurbs_arc.m_order-2; i < nurbs_arc.m_cv_count-1; i++, bez_arc.m_cv += bez_arc.m_cv_stride )
|
|
{
|
|
if ( nurbs_arc.m_knot[i] < nurbs_arc.m_knot[i+1] )
|
|
{
|
|
if ( bez_arc.GetTightBoundingBox( tight_bbox, bGrowBox, 0 ) )
|
|
bGrowBox = true;
|
|
}
|
|
}
|
|
bez_arc.m_cv = 0;
|
|
}
|
|
nurbs_arc.m_cv = 0;
|
|
nurbs_arc.m_knot = 0;
|
|
|
|
return (0!=bGrowBox);
|
|
}
|
|
|
|
bool ON_Circle::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
// April 8, 2010 Dale Lear:
|
|
// Changed this function to be faster when xform is the identity.
|
|
if ( 0 != xform && !xform->IsIdentity() )
|
|
{
|
|
// The ON_Arc version handles all transformations including
|
|
// ones that are not in rotations.
|
|
ON_Arc arc(*this,2.0*ON_PI);
|
|
return arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
|
|
const double rx = radius*ON_Length2d(plane.zaxis.y, plane.zaxis.z);
|
|
const double ry = radius*ON_Length2d(plane.zaxis.z, plane.zaxis.x);
|
|
const double rz = radius*ON_Length2d(plane.zaxis.x, plane.zaxis.y);
|
|
if ( bGrowBox )
|
|
{
|
|
if ( plane.origin.x-rx < tight_bbox.m_min.x )
|
|
tight_bbox.m_min.x = plane.origin.x-rx;
|
|
if ( plane.origin.x+rx > tight_bbox.m_max.x )
|
|
tight_bbox.m_max.x = plane.origin.x+rx;
|
|
if ( plane.origin.y-ry < tight_bbox.m_min.y )
|
|
tight_bbox.m_min.y = plane.origin.y-ry;
|
|
if ( plane.origin.y+ry > tight_bbox.m_max.y )
|
|
tight_bbox.m_max.y = plane.origin.y+ry;
|
|
if ( plane.origin.z-rz < tight_bbox.m_min.z )
|
|
tight_bbox.m_min.z = plane.origin.z-rz;
|
|
if ( plane.origin.z+rz > tight_bbox.m_max.z )
|
|
tight_bbox.m_max.z = plane.origin.z+rz;
|
|
}
|
|
else
|
|
{
|
|
tight_bbox.m_min.x = plane.origin.x-rx;
|
|
tight_bbox.m_max.x = plane.origin.x+rx;
|
|
tight_bbox.m_min.y = plane.origin.y-ry;
|
|
tight_bbox.m_max.y = plane.origin.y+ry;
|
|
tight_bbox.m_min.z = plane.origin.z-rz;
|
|
tight_bbox.m_max.z = plane.origin.z+rz;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_ArcCurve::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
return m_arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
bool ON_LineCurve::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
return m_line.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
bool ON_PolylineCurve::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
return m_pline.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
bool ON_PolyCurve::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
return m_segment.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
bool ON_CurveArray::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if ( 1 == m_count && m_a[0] )
|
|
{
|
|
return m_a[0]->GetTightBoundingBox(tight_bbox,bGrowBox,xform);
|
|
}
|
|
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
if ( !bGrowBox )
|
|
{
|
|
tight_bbox.Destroy();
|
|
}
|
|
|
|
if ( m_count > 0 )
|
|
{
|
|
int i;
|
|
// getting box of endpoints tends to help us avoid testing curves
|
|
ON_3dPointArray P(2*m_count);
|
|
for ( i = 0; i < m_count; i++ )
|
|
{
|
|
if ( m_a[i] )
|
|
{
|
|
P.Append( m_a[i]->PointAtStart() );
|
|
P.Append( m_a[i]->PointAtEnd() );
|
|
}
|
|
}
|
|
if ( P.GetTightBoundingBox(tight_bbox,bGrowBox,xform) )
|
|
{
|
|
bGrowBox = true;
|
|
}
|
|
|
|
for ( i = 0; i < m_count; i++ )
|
|
{
|
|
if ( m_a[i] )
|
|
{
|
|
if ( m_a[i]->GetTightBoundingBox(tight_bbox,bGrowBox,xform) )
|
|
{
|
|
bGrowBox = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0!=bGrowBox);
|
|
}
|
|
|
|
bool ON_3dPointArray::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
if ( !bGrowBox )
|
|
{
|
|
tight_bbox.Destroy();
|
|
}
|
|
if ( m_count > 0 )
|
|
{
|
|
ON_BoundingBox points_bbox;
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
ON_3dPoint P;
|
|
points_bbox.m_min = (*xform)*m_a[0];
|
|
points_bbox.m_max = points_bbox.m_min;
|
|
int i;
|
|
for ( i = 1; i < m_count; i++ )
|
|
{
|
|
P = (*xform)*m_a[i];
|
|
if ( P.x < points_bbox.m_min.x ) points_bbox.m_min.x = P.x; else if ( P.x > points_bbox.m_max.x ) points_bbox.m_max.x = P.x;
|
|
if ( P.y < points_bbox.m_min.y ) points_bbox.m_min.y = P.y; else if ( P.y > points_bbox.m_max.y ) points_bbox.m_max.y = P.y;
|
|
if ( P.z < points_bbox.m_min.z ) points_bbox.m_min.z = P.z; else if ( P.z > points_bbox.m_max.z ) points_bbox.m_max.z = P.z;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
points_bbox = BoundingBox();
|
|
}
|
|
tight_bbox.Union(points_bbox);
|
|
bGrowBox = true;
|
|
}
|
|
return (0!=bGrowBox);
|
|
}
|
|
|
|
bool ON_PointCloud::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if ( bGrowBox && !tight_bbox.IsValid() )
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
|
|
if ( !bGrowBox )
|
|
{
|
|
tight_bbox.Destroy();
|
|
}
|
|
|
|
if ( m_P.Count() > 0 )
|
|
{
|
|
ON_BoundingBox pc_bbox = BoundingBox();
|
|
if ( bGrowBox )
|
|
{
|
|
if ( ON_WorldBBoxIsInTightBBox( tight_bbox, pc_bbox, xform ) )
|
|
return true;
|
|
}
|
|
|
|
if ( xform && !xform->IsIdentity() )
|
|
{
|
|
if ( m_P.GetTightBoundingBox(tight_bbox,bGrowBox,xform) )
|
|
bGrowBox = true;
|
|
}
|
|
else
|
|
{
|
|
tight_bbox.Union(pc_bbox);
|
|
bGrowBox = tight_bbox.IsValid();
|
|
}
|
|
}
|
|
|
|
return (0!=bGrowBox);
|
|
}
|
|
|
|
|
|
bool ON_Mesh::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
ON_SimpleArray<ON_PlaneEquation> NoCP;
|
|
return GetTightBoundingBox(tight_bbox, bGrowBox, NoCP, xform);
|
|
}
|
|
|
|
|
|
// return true if vertex vi is clipped out
|
|
static
|
|
bool GetClipData(int vi, const std::vector<bool>& ClipData,
|
|
unsigned int* data,
|
|
int n,
|
|
int bpv)
|
|
{
|
|
*data = 0;
|
|
int cdi = bpv*vi;
|
|
bool out = ClipData[cdi];
|
|
if (n == 1)
|
|
*data = out;
|
|
else
|
|
{
|
|
for (int j = 0; j <n; j++)
|
|
{
|
|
*data = *data << 1;
|
|
if (ClipData[cdi + 1 + j])
|
|
*data |= 1;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
//GBA 12/20/16 rewrote this function
|
|
bool ON_Mesh::GetTightBoundingBox(
|
|
ON_BoundingBox& Xtight_bbox,
|
|
bool bGrowBox,
|
|
const ON_SimpleArray<ON_PlaneEquation>& Clip,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
if (bGrowBox && false == Xtight_bbox.IsNotEmpty() )
|
|
bGrowBox = false;
|
|
|
|
if (!bGrowBox)
|
|
Xtight_bbox = ON_BoundingBox::EmptyBoundingBox;
|
|
|
|
// Avoid applying the identity transformation repeatedly
|
|
if (xform && xform->IsIdentity())
|
|
xform = nullptr;
|
|
|
|
if (Clip.Count() <= 0 && xform == nullptr)
|
|
return (GetBBox(Xtight_bbox.m_min, Xtight_bbox.m_max, bGrowBox) ? true : false);
|
|
|
|
// Dale Lear
|
|
// Set hash = sha1 hash of information needed to get the tight bounding box.
|
|
// This does not include a hash of mesh vertex locations. These cached
|
|
// boxes are removed by ON_Mesh.InvalidateBoundingBoxes() so this hash
|
|
// does not need to include vertex locations or face information.
|
|
ON_SHA1 sha1;
|
|
if (nullptr != xform)
|
|
sha1.AccumulateTransformation(*xform);
|
|
for (unsigned int i = 0; i < Clip.UnsignedCount(); i++)
|
|
{
|
|
sha1.AccumulateDoubleArray(4, &Clip[i].x);
|
|
}
|
|
const ON_SHA1_Hash hash = sha1.Hash();
|
|
|
|
ON_BoundingBox bbox;
|
|
|
|
if (m_tight_bbox_cache.GetBoundingBox(hash, bbox))
|
|
{
|
|
if (bGrowBox)
|
|
Xtight_bbox.Union(bbox);
|
|
else
|
|
Xtight_bbox = bbox;
|
|
return Xtight_bbox.IsNotEmpty();
|
|
}
|
|
|
|
// Perform clip test on cached bbox
|
|
if (xform == nullptr && m_vertex_bbox.IsNotEmpty())
|
|
{
|
|
ON_3dPoint Corner[8];
|
|
m_vertex_bbox.GetCorners(Corner);
|
|
|
|
bool AllIn = true;
|
|
for( int i=0; AllIn && i<8; i++)
|
|
for (int j = 0; AllIn && j < Clip.Count(); j++)
|
|
{
|
|
AllIn = (Clip[j].ValueAt(Corner[i]) <= 0.0);
|
|
}
|
|
|
|
if (AllIn)
|
|
{
|
|
bool rc = GetBBox(bbox.m_min, bbox.m_max, false) ? true : false;
|
|
m_tight_bbox_cache.AddBoundingBox(bbox, hash);
|
|
if (rc && bGrowBox)
|
|
Xtight_bbox.Union(bbox);
|
|
else
|
|
Xtight_bbox = bbox;
|
|
return Xtight_bbox.IsNotEmpty();
|
|
}
|
|
}
|
|
|
|
// Now just add verticies of the clipped mesh
|
|
int vcnt = VertexCount();
|
|
|
|
// 16 Sept 2021, Mikko, RH-49510:
|
|
// Bail out if the mesh is bogus. In the associated youtrack item
|
|
// this fixes the bounding box of a block being way too big.
|
|
if (vcnt < 3)
|
|
return false;
|
|
|
|
// n = number of clipping planes. At most 32 clipping planes are allowed so that this function can use unsigned int
|
|
// to represent the 32 clip predicates.
|
|
int n = Clip.Count();
|
|
if (n > 32)
|
|
n = 32;
|
|
int bits = (n > 1) ? n + 1 : n;
|
|
int bpv = (n>0)?1<<int(ceil(log2(bits))) : 1; // bits per vertex this is a power of 2
|
|
std::vector<bool> ClipData(vcnt*bpv, false); // ClipData[ vi*bpv ] is true if vertex vi is clipped out of the view
|
|
// If n>1 then further information is provided by
|
|
// ClipData[vi*bpv + j+ 1] is true if Clip[j](m_V[vi])>0 that is
|
|
// vertex vi is clipped out by Clip[j]
|
|
// Compute ClipData for each vertex
|
|
for (int vi = 0; vi < vcnt; vi++)
|
|
{
|
|
ON_3dPoint P = m_V[vi];
|
|
if (xform)
|
|
P = (*xform)*P;
|
|
|
|
bool clipped = false;
|
|
if (n > 0)
|
|
{
|
|
int cdi0 = vi*bpv;
|
|
clipped = Clip[0].ValueAt(P) > 0;
|
|
|
|
if (n > 1)
|
|
{
|
|
int cdi1 = cdi0 + 1;
|
|
ClipData[cdi1++] = clipped;
|
|
for (int j = 1; j < n; j++)
|
|
{
|
|
bool out = Clip[j].ValueAt(P) > 0;
|
|
ClipData[cdi1++] = out;
|
|
clipped = clipped || out;
|
|
}
|
|
}
|
|
ClipData[cdi0] = clipped;
|
|
}
|
|
// If the vertex is not clipped add it to the boundingbox
|
|
if( !clipped)
|
|
bbox.Set(P, true);
|
|
}
|
|
|
|
if (n > 0)
|
|
{
|
|
// Now process each mesh face and look for intersections with the clipping region boundary
|
|
for (int fi = 0; fi < FaceCount(); fi++)
|
|
{
|
|
const ON_MeshFace& F = m_F[fi];
|
|
unsigned int LastData=0;
|
|
bool LastDataValid = false;
|
|
bool LastOut = ClipData[F.vi[0] * bpv];
|
|
if (LastOut)
|
|
{
|
|
GetClipData(F.vi[0], ClipData, &LastData, n, bpv);
|
|
LastDataValid = true;
|
|
}
|
|
for (int fvi = 0; fvi < 4; fvi++)
|
|
{
|
|
if (fvi == 2 && F.vi[fvi] == F.vi[fvi + 1])
|
|
continue;
|
|
|
|
unsigned int NextData=0;
|
|
bool NextDataValid=false;
|
|
bool NextOut = ClipData[F.vi[(fvi + 1) % 4] * bpv];
|
|
if (NextOut)
|
|
{
|
|
GetClipData(F.vi[(fvi + 1) % 4], ClipData, &NextData, n, bpv);
|
|
NextDataValid = true;
|
|
}
|
|
|
|
if (LastOut || NextOut)
|
|
{
|
|
// Make sure *Data is Valid
|
|
if (!LastDataValid)
|
|
{
|
|
GetClipData(F.vi[fvi], ClipData, &LastData, n, bpv);
|
|
LastDataValid = true;
|
|
}
|
|
if (!NextDataValid)
|
|
{
|
|
GetClipData(F.vi[fvi], ClipData, &NextData, n, bpv);
|
|
NextDataValid = true;
|
|
}
|
|
}
|
|
if ((LastOut || NextOut) && (LastData & NextData) == 0)
|
|
{
|
|
// Is there a segment of this edge that is not clipped out
|
|
ON_3dPoint Last = m_V[F.vi[fvi]];
|
|
ON_3dPoint Next = m_V[F.vi[(fvi + 1) % 4]];
|
|
if (xform)
|
|
{
|
|
Last = (*xform)*Last;
|
|
Next = (*xform)*Next;
|
|
}
|
|
|
|
ON_Interval Dom(0.0, 1.0); // Dom parameterizes the line segment from Last to Next
|
|
for (int j = 0; j < n && Dom != ON_Interval::EmptyInterval; j++)
|
|
{
|
|
unsigned int mask = 1 << (n - 1 - j);
|
|
if (mask & LastData)
|
|
{
|
|
double cplast = Clip[j].ValueAt(Last); // out >0
|
|
double cpnext = Clip[j].ValueAt(Next); // in <0
|
|
ON_Interval D(cplast / (cplast - cpnext), 1.0);
|
|
Dom.Intersection(D);
|
|
}
|
|
else if (mask & NextData)
|
|
{
|
|
double cplast = Clip[j].ValueAt(Last);
|
|
double cpnext = Clip[j].ValueAt(Next);
|
|
ON_Interval D(0.0, -cplast / (cpnext - cplast));
|
|
Dom.Intersection(D);
|
|
}
|
|
}
|
|
if (Dom != ON_Interval::EmptyInterval)
|
|
{
|
|
if (Dom[0] > 0.0)
|
|
{
|
|
ON_3dPoint Pt = (1 - Dom[0])*Last + Dom[0] * Next;
|
|
bbox.Set(Pt, true);
|
|
}
|
|
if (Dom[1] < 1.0)
|
|
{
|
|
ON_3dPoint Pt = (1 - Dom[1])*Last + Dom[1] * Next;
|
|
bbox.Set(Pt, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
LastOut = NextOut;
|
|
LastDataValid = NextDataValid;
|
|
LastData = NextData;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_tight_bbox_cache.AddBoundingBox(bbox, hash);
|
|
if (bbox.IsNotEmpty())
|
|
{
|
|
if (bGrowBox)
|
|
Xtight_bbox.Union(bbox);
|
|
else
|
|
Xtight_bbox = bbox;
|
|
}
|
|
|
|
return Xtight_bbox.IsNotEmpty();
|
|
}
|
|
|
|
|
|
|
|
bool ON_BezierCurve::Transform(
|
|
const ON_Xform& xform
|
|
)
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
return ON_TransformPointList( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, xform );
|
|
}
|
|
|
|
bool ON_BezierCurve::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_BezierCurve::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_BezierCurve::Translate( const ON_3dVector& delta )
|
|
{
|
|
ON_Xform tr(ON_Xform::TranslationTransformation( delta ));
|
|
return Transform( tr );
|
|
}
|
|
|
|
bool ON_BezierCurve::Scale( double x )
|
|
{
|
|
ON_Xform s(ON_Xform::DiagonalTransformation( x ));
|
|
return Transform( s );
|
|
}
|
|
|
|
|
|
ON_Interval ON_BezierCurve::Domain() const
|
|
{
|
|
//return m_domain;
|
|
return ON_Interval(0.0,1.0);
|
|
}
|
|
|
|
bool ON_BezierCurve::Reverse()
|
|
{
|
|
bool rc = ON_ReversePointList( m_dim, m_is_rat, m_order, m_cv_stride, m_cv )?true:false;
|
|
//if ( rc )
|
|
// m_domain.Reverse();
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::Evaluate( // returns false if unable to evaluate
|
|
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)
|
|
) const
|
|
{
|
|
return ON_EvaluateBezier( m_dim, m_is_rat, m_order, m_cv_stride, m_cv,
|
|
0.0, 1.0, //m_domain[0], m_domain[1],
|
|
der_count, t, v_stride, v )?true:false;
|
|
}
|
|
|
|
int ON_BezierCurve::GetNurbForm( ON_NurbsCurve& n ) const
|
|
{
|
|
int rc = 0;
|
|
if ( n.Create( m_dim, m_is_rat, m_order, m_order ) ) {
|
|
const int sizeof_cv = CVSize()*sizeof(m_cv[0]);
|
|
int i;
|
|
for ( i = 0; i < m_order; i++ ) {
|
|
memcpy( n.CV(i), CV(i), sizeof_cv );
|
|
}
|
|
n.m_knot[m_order-2] = 0.0;//m_domain.Min();
|
|
n.m_knot[m_order-1] = 1.0;//m_domain.Max();
|
|
rc = ON_ClampKnotVector(n.m_order, n.m_cv_count, n.m_knot, 2) ? 1 : 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::IsRational() const
|
|
{
|
|
return m_is_rat ? true : false;
|
|
}
|
|
|
|
int ON_BezierCurve::CVSize() const
|
|
{
|
|
return (m_dim>0&&m_is_rat) ? m_dim+1 : m_dim;
|
|
}
|
|
|
|
int ON_BezierCurve::Order() const
|
|
{
|
|
return m_order;
|
|
}
|
|
|
|
int ON_BezierCurve::Degree() const
|
|
{
|
|
return m_order>=2 ? m_order-1 : 0;
|
|
}
|
|
|
|
double* ON_BezierCurve::CV( int i ) const
|
|
{
|
|
return m_cv ? m_cv + (i*m_cv_stride) : 0;
|
|
}
|
|
|
|
const ON_4dPoint ON_BezierCurve::ControlPoint(
|
|
int i
|
|
) const
|
|
{
|
|
ON_4dPoint cv;
|
|
if (false == GetCV(i, cv))
|
|
cv = ON_4dPoint::Nan;
|
|
return cv;
|
|
}
|
|
|
|
ON::point_style ON_BezierCurve::CVStyle() const
|
|
{
|
|
return m_is_rat ? ON::homogeneous_rational : ON::not_rational;
|
|
}
|
|
|
|
double ON_BezierCurve::Weight( int i ) const
|
|
{
|
|
return ( m_is_rat && m_cv ) ? CV(i)[m_dim] : 1.0;
|
|
}
|
|
|
|
bool ON_BezierCurve::SetWeight( int i, double w )
|
|
{
|
|
bool rc = false;
|
|
if ( m_is_rat ) {
|
|
double* cv = CV(i);
|
|
if (cv) {
|
|
cv[m_dim] = w;
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( w == 1.0 ) {
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::SetCV( int i, ON::point_style style, const double* Point )
|
|
{
|
|
bool rc = true;
|
|
int k;
|
|
double w;
|
|
|
|
// feeble but fast check for properly initialized class
|
|
if ( !m_cv || i < 0 || i >= m_order )
|
|
return false;
|
|
|
|
double* cv = m_cv + i*m_cv_stride;
|
|
|
|
switch ( style ) {
|
|
|
|
case ON::not_rational: // input Point is not rational
|
|
memcpy( cv, Point, m_dim*sizeof(*cv) );
|
|
if ( IsRational() ) {
|
|
// NURBS curve is rational - set weight to one
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
break;
|
|
|
|
case ON::homogeneous_rational: // input Point is homogeneous rational
|
|
if ( IsRational() ) {
|
|
// NURBS curve is rational
|
|
memcpy( cv, Point, (m_dim+1)*sizeof(*cv) );
|
|
}
|
|
else {
|
|
// NURBS curve is not rational
|
|
w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0;
|
|
for ( k = 0; k < m_dim; k++ ) {
|
|
cv[k] = w*Point[k];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ON::euclidean_rational: // input Point is euclidean rational
|
|
if ( IsRational() ) {
|
|
// NURBS curve is rational - convert euclean point to homogeneous form
|
|
w = Point[m_dim];
|
|
for ( k = 0; k < m_dim; k++ )
|
|
cv[k] = w*Point[k];
|
|
cv[m_dim] = w;
|
|
}
|
|
else {
|
|
// NURBS curve is not rational
|
|
memcpy( cv, Point, m_dim*sizeof(*cv) );
|
|
}
|
|
break;
|
|
|
|
case ON::intrinsic_point_style:
|
|
k = m_is_rat?m_dim+1:m_dim;
|
|
memcpy(cv,Point,k*sizeof(cv[0]));
|
|
break;
|
|
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::SetCV( int i, const ON_3dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i);
|
|
if ( cv ) {
|
|
cv[0] = point.x;
|
|
if ( m_dim > 1 ) {
|
|
cv[1] = point.y;
|
|
if ( m_dim > 2 )
|
|
cv[2] = point.z;
|
|
if ( m_dim > 3 ) {
|
|
memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
|
|
}
|
|
}
|
|
if ( m_is_rat ) {
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::SetCV( int i, const ON_4dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i);
|
|
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;
|
|
if ( m_dim > 3 ) {
|
|
memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
if ( m_dim > 3 ) {
|
|
memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::GetCV( int i, ON::point_style style, double* Point ) const
|
|
{
|
|
const double* cv = CV(i);
|
|
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;
|
|
case ON::intrinsic_point_style:
|
|
if (IsRational()) dim++;
|
|
memcpy(Point, cv, dim * sizeof(*Point));
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierCurve::GetCV( int i, ON_3dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
const double* cv = CV(i);
|
|
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_BezierCurve::GetCV( int i, ON_4dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
if (m_dim > 0 && i >= 0 && i < m_order)
|
|
{
|
|
const double* cv = CV(i);
|
|
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_BezierCurve::ZeroCVs()
|
|
{
|
|
bool rc = false;
|
|
int i;
|
|
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; i++ ) {
|
|
SetWeight( i, 1.0 );
|
|
}
|
|
}
|
|
rc = true;
|
|
}
|
|
else {
|
|
double* cv;
|
|
int s = CVSize()*sizeof(*cv);
|
|
for ( i = 0; i < m_order; i++ ) {
|
|
cv = CV(i);
|
|
memset(cv,0,s);
|
|
if ( m_is_rat )
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
rc = (i>0) ? true : false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int ON_BezierCurve::CVCount() const
|
|
{
|
|
return Order();
|
|
}
|
|
|
|
bool ON_BezierCurve::MakeRational()
|
|
{
|
|
if ( !IsRational() ) {
|
|
const int dim = Dimension();
|
|
const int cv_count = CVCount();
|
|
if ( cv_count > 0 && m_cv_stride >= dim && dim > 0 ) {
|
|
const int new_stride = (m_cv_stride == dim) ? dim+1 : m_cv_stride;
|
|
ReserveCVCapacity( cv_count*new_stride );
|
|
const double* old_cv;
|
|
double* new_cv;
|
|
int cvi, j;
|
|
for ( cvi = cv_count-1; cvi>=0; cvi-- ) {
|
|
old_cv = CV(cvi);
|
|
new_cv = m_cv+(cvi*new_stride);
|
|
for ( j = dim-1; j >= 0; j-- ) {
|
|
new_cv[j] = old_cv[j];
|
|
}
|
|
new_cv[dim] = 1.0;
|
|
}
|
|
m_cv_stride = new_stride;
|
|
m_is_rat = 1;
|
|
}
|
|
}
|
|
return IsRational();
|
|
}
|
|
|
|
bool ON_BezierCurve::MakeNonRational()
|
|
{
|
|
if ( IsRational() ) {
|
|
const int dim = Dimension();
|
|
const int cv_count = CVCount();
|
|
if ( cv_count > 0 && m_cv_stride >= dim+1 && dim > 0 ) {
|
|
double w;
|
|
const double* old_cv;
|
|
double* new_cv = m_cv;
|
|
int cvi, j;
|
|
for ( cvi = 0; cvi < cv_count; cvi++ ) {
|
|
old_cv = CV(cvi);
|
|
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_is_rat = 0;
|
|
m_cv_stride = dim;
|
|
}
|
|
}
|
|
return ( !IsRational() ) ? true : false;
|
|
}
|
|
|
|
bool ON_BezierCurve::IncreaseDegree( int desired_degree )
|
|
{
|
|
bool rc = false;
|
|
if ( desired_degree > 0 ) {
|
|
if ( desired_degree == m_order-1 )
|
|
rc = true;
|
|
else if ( desired_degree >= m_order ) {
|
|
ReserveCVCapacity( m_cv_stride*(desired_degree+1) );
|
|
while ( m_order <= desired_degree ) {
|
|
rc = ON_IncreaseBezierDegree( m_dim, m_is_rat, m_order, m_cv_stride, m_cv )?true:false;
|
|
if ( !rc )
|
|
break;
|
|
m_order++;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Tools for managing CV and knot memory
|
|
bool ON_BezierCurve::ReserveCVCapacity( int desired_capacity )
|
|
{
|
|
bool rc = false;
|
|
if ( desired_capacity > m_cv_capacity ) {
|
|
if ( !m_cv ) {
|
|
m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv));
|
|
if ( !m_cv ) {
|
|
m_cv_capacity = 0;
|
|
}
|
|
else {
|
|
m_cv_capacity = desired_capacity;
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( m_cv_capacity > 0 ) {
|
|
m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv));
|
|
if ( !m_cv ) {
|
|
m_cv_capacity = 0;
|
|
}
|
|
else {
|
|
m_cv_capacity = desired_capacity;
|
|
rc = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
rc =true;
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::ChangeDimension( int dim )
|
|
{
|
|
ON_NurbsCurve c;
|
|
c.m_dim = m_dim;
|
|
c.m_is_rat = m_is_rat;
|
|
c.m_order = m_order;
|
|
c.m_cv_count = m_order;
|
|
c.m_cv_capacity = m_cv_capacity;
|
|
c.m_cv_stride = m_cv_stride;
|
|
c.m_cv = m_cv;
|
|
bool rc = c.ChangeDimension(dim);
|
|
m_dim = c.m_dim;
|
|
m_cv_stride = c.m_cv_stride;
|
|
m_cv = c.m_cv;
|
|
m_cv_capacity = c.m_cv_capacity;
|
|
|
|
// don't let destruction of stack "c" delete m_cv array.
|
|
c.m_cv = 0;
|
|
c.m_cv_capacity = 0;
|
|
c.m_cv_stride = 0;
|
|
return rc;
|
|
}
|
|
|
|
double ON_BezierCurve::ControlPolygonLength() const
|
|
{
|
|
double length = 0.0;
|
|
ON_GetPolylineLength( m_dim, m_is_rat, m_order, m_cv_stride, m_cv, &length );
|
|
return length;
|
|
}
|
|
|
|
bool ON_BezierCurve::Trim( const ON_Interval& n )
|
|
{
|
|
bool rc = n.IsIncreasing();
|
|
if ( rc ) {
|
|
double t0 = n.Min();
|
|
double t1 = n.Max();
|
|
int cvdim = CVSize(); // 9/9/03 fix rat trim bug - use cvdim instead of m_dim
|
|
if ( t0 != 1.0 ) {
|
|
double s1 = (t1-t0)/(1.0 - t0);
|
|
ON_EvaluatedeCasteljau( cvdim, m_order, +1, m_cv_stride, m_cv, t0 );
|
|
ON_EvaluatedeCasteljau( cvdim, m_order, -1, m_cv_stride, m_cv, s1 );
|
|
}
|
|
else {
|
|
ON_EvaluatedeCasteljau( cvdim, m_order, -1, m_cv_stride, m_cv, t1 );
|
|
if ( t0 != 0.0 )
|
|
{
|
|
// 9/9/03 fix add != 0 test to avoid divide by zero bug
|
|
double s0 = t1/t0;
|
|
ON_EvaluatedeCasteljau( cvdim, m_order, +1, m_cv_stride, m_cv, s0 );
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::Split(
|
|
double t, // t = splitting parameter must 0 < t < 1
|
|
ON_BezierCurve& left_bez, // left side returned here (can pass *this)
|
|
ON_BezierCurve& right_bez // right side returned here (can pass *this)
|
|
) const
|
|
{
|
|
bool rc = ( 0.0 < t && t < 1.0 && IsValid() ) ? true : false;
|
|
if ( rc )
|
|
{
|
|
const int cvdim = CVSize();
|
|
int i,j,k,n;
|
|
double *p, *r, s;
|
|
const double* q;
|
|
|
|
double** b;
|
|
double *stack_buffer[2*9-1];
|
|
void* heap_buffer = 0;
|
|
const size_t sizeof_buffer = (2*m_order-1)*sizeof(*b);
|
|
|
|
b = (sizeof_buffer <= sizeof(stack_buffer))
|
|
? stack_buffer
|
|
: (double**)(heap_buffer = onmalloc(sizeof_buffer));
|
|
|
|
if ( this != &left_bez )
|
|
{
|
|
if ( 0 == left_bez.m_cv || (0 < left_bez.m_cv_capacity && left_bez.m_cv_capacity < cvdim*m_order) )
|
|
{
|
|
left_bez.Create( m_dim, m_is_rat, m_order );
|
|
}
|
|
else if ( left_bez.m_dim != m_dim || left_bez.m_is_rat != m_is_rat || left_bez.m_order != m_order || left_bez.m_cv_stride < cvdim )
|
|
{
|
|
left_bez.m_dim = m_dim;
|
|
left_bez.m_is_rat = m_is_rat?1:0;
|
|
left_bez.m_order = m_order;
|
|
left_bez.m_cv_stride = cvdim;
|
|
}
|
|
}
|
|
|
|
if ( this != &right_bez )
|
|
{
|
|
if ( !right_bez.m_cv || (0 < right_bez.m_cv_capacity && right_bez.m_cv_capacity < cvdim*m_order) )
|
|
{
|
|
right_bez.Create( m_dim, m_is_rat, m_order );
|
|
}
|
|
else if ( right_bez.m_dim != m_dim || right_bez.m_is_rat != m_is_rat || right_bez.m_order != m_order || right_bez.m_cv_stride < cvdim )
|
|
{
|
|
right_bez.m_dim = m_dim;
|
|
right_bez.m_is_rat = m_is_rat?1:0;
|
|
right_bez.m_order = m_order;
|
|
right_bez.m_cv_stride = cvdim;
|
|
}
|
|
}
|
|
|
|
b[0] = left_bez.m_cv;
|
|
b[m_order-1] = right_bez.m_cv;
|
|
for ( i = 1, j = m_order; i < m_order; i++, j++ ) {
|
|
b[j] = b[j-1]+cvdim;
|
|
b[i] = b[i-1]+cvdim;
|
|
}
|
|
|
|
if (m_cv == left_bez.m_cv) {
|
|
// copy from back to front
|
|
for (i = 2*m_order-2; i >= 0; i -= 2) {
|
|
p = b[i]+cvdim;
|
|
q = CV(i/2)+cvdim;
|
|
k = cvdim;
|
|
while(k--) *(--p) = *(--q);
|
|
}
|
|
}
|
|
else {
|
|
// copy from front to back
|
|
for (i = 0; i < 2*m_order; i += 2) {
|
|
p = b[i];
|
|
q = CV(i/2);
|
|
k = cvdim;
|
|
while(k--) *p++ = *q++;
|
|
}
|
|
}
|
|
|
|
left_bez.m_dim = m_dim;
|
|
left_bez.m_is_rat = m_is_rat;
|
|
left_bez.m_order = m_order;
|
|
left_bez.m_cv_stride = CVSize();
|
|
|
|
right_bez.m_dim = left_bez.m_dim;
|
|
right_bez.m_is_rat = left_bez.m_is_rat;
|
|
right_bez.m_order = left_bez.m_order;
|
|
right_bez.m_cv_stride = left_bez.m_cv_stride;
|
|
|
|
// deCasteljau
|
|
if (t == 0.5) {
|
|
// use faster aritmetic for this common case
|
|
for (i = 1, k = 2*m_order-2; i < k; i++, k--) {
|
|
for (j = i; j < k; j++,j++) {
|
|
p = b[j-1];
|
|
q = b[j+1];
|
|
r = b[j];
|
|
n = cvdim;
|
|
while(n--)
|
|
*r++ = 0.5*(*p++ + *q++);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
s = 1.0-t;
|
|
for (i = 1, k = 2*m_order-2; i < k; i++, k--) {
|
|
for (j = i; j < k; j++,j++) {
|
|
p = b[j-1];
|
|
q = b[j+1];
|
|
r = b[j];
|
|
n = cvdim;
|
|
while(n--)
|
|
*r++ = (*p++ * s + *q++ * t);
|
|
}
|
|
}
|
|
}
|
|
|
|
// last cv of left side = first cv of right side
|
|
p = right_bez.CV(0);
|
|
q = left_bez.CV(m_order-1);
|
|
if (p != q) {
|
|
j = cvdim;
|
|
while(j--) *p++ = *q++;
|
|
}
|
|
|
|
if ( 0 != heap_buffer )
|
|
onfree(heap_buffer);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
ON_BezierSurface::ON_BezierSurface()
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierSurface = 0;
|
|
#endif
|
|
}
|
|
|
|
ON_BezierSurface::ON_BezierSurface( int dim, bool is_rat, int order0, int order1 )
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierSurface = 0;
|
|
#endif
|
|
Create( dim, is_rat, order0, order1 );
|
|
}
|
|
|
|
ON_BezierSurface::~ON_BezierSurface()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
ON_BezierSurface::ON_BezierSurface(const ON_BezierSurface& src)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierSurface = 0;
|
|
#endif
|
|
*this = src;
|
|
}
|
|
|
|
ON_BezierSurface::ON_BezierSurface(const ON_PolynomialSurface& src)
|
|
: m_dim(0),
|
|
m_is_rat(0),
|
|
m_cv(0),
|
|
m_cv_capacity(0)
|
|
{
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
m_cv_stride[0] = 0;
|
|
m_cv_stride[1] = 0;
|
|
#if 8 == ON_SIZEOF_POINTER
|
|
m_reserved_ON_BezierSurface = 0;
|
|
#endif
|
|
*this = src;
|
|
}
|
|
|
|
ON_BezierSurface& ON_BezierSurface::operator=(const ON_BezierSurface& src)
|
|
{
|
|
if ( this != &src ) {
|
|
if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) {
|
|
const int sizeof_cv = src.CVSize()*sizeof(m_cv[0]);
|
|
int i, j;
|
|
for ( i = 0; i < m_order[0]; i++ ) for ( j = 0; j < m_order[1]; j++ ) {
|
|
memcpy( CV(i,j), src.CV(i,j), sizeof_cv );
|
|
}
|
|
}
|
|
else {
|
|
Destroy();
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ON_BezierSurface& ON_BezierSurface::operator=(const ON_PolynomialSurface& src)
|
|
{
|
|
if ( Create( src.m_dim, src.m_is_rat, src.m_order[0], src.m_order[1] ) ) {
|
|
// TODO - convert to bi-bezier
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_BezierSurface::IsValid() const
|
|
{
|
|
if ( m_dim <= 0 )
|
|
return false;
|
|
if ( m_is_rat != 0 && m_is_rat != 1 )
|
|
return false;
|
|
if ( m_order[0] < 2 )
|
|
return false;
|
|
if ( m_order[0] < 2 )
|
|
return false;
|
|
if ( m_cv_stride[0] < m_dim+m_is_rat )
|
|
return false;
|
|
if ( m_cv_stride[1] < m_dim+m_is_rat )
|
|
return false;
|
|
if ( m_cv_capacity > 0 && m_cv_capacity < (m_dim+m_is_rat)*m_order[0]*m_order[1] )
|
|
return false;
|
|
//if ( !m_domain[0].IsIncreasing() )
|
|
// return false;
|
|
//if ( !m_domain[1].IsIncreasing() )
|
|
// return false;
|
|
if ( m_cv == nullptr )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void ON_BezierSurface::Dump( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print( "ON_BezierSurface dim = %d is_rat = %d\n"
|
|
" order = (%d, %d) \n",
|
|
m_dim, m_is_rat, m_order[0], m_order[1] );
|
|
dump.Print( "Control Points %d %s points\n"
|
|
" index value\n",
|
|
m_order[0]*m_order[1],
|
|
(m_is_rat) ? "rational" : "non-rational" );
|
|
if ( !m_cv ) {
|
|
dump.Print(" nullptr cv array\n");
|
|
}
|
|
else {
|
|
int i;
|
|
char sPreamble[128] = { 0 };
|
|
const size_t sPreamble_capacity = sizeof(sPreamble) / sizeof(sPreamble[0]);
|
|
for ( i = 0; i < m_order[0]; i++ )
|
|
{
|
|
if ( i > 0 )
|
|
dump.Print("\n");
|
|
sPreamble[0] = 0;
|
|
ON_String::FormatIntoBuffer(sPreamble, sPreamble_capacity, " CV[%2d]", i);
|
|
dump.PrintPointList( m_dim, m_is_rat,
|
|
m_order[1], m_cv_stride[1],
|
|
CV(i,0),
|
|
sPreamble );
|
|
}
|
|
}
|
|
}
|
|
|
|
int ON_BezierSurface::Dimension() const
|
|
{
|
|
return m_dim;
|
|
}
|
|
|
|
bool ON_BezierSurface::Create( int dim, bool is_rat, int order0, int order1 )
|
|
{
|
|
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_cv_stride[1] = (m_dim > 0) ? m_dim+m_is_rat : 0;
|
|
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]) );
|
|
//m_domain[0].m_t[0] = 0.0;
|
|
//m_domain[0].m_t[1] = 1.0;
|
|
//m_domain[1].m_t[0] = 0.0;
|
|
//m_domain[1].m_t[1] = 1.0;
|
|
return IsValid();
|
|
}
|
|
|
|
void ON_BezierSurface::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 = 0;
|
|
m_dim = 0;
|
|
m_is_rat = 0;
|
|
m_order[0] = 0;
|
|
m_order[1] = 0;
|
|
//m_domain[0].m_t[0] = 0.0;
|
|
//m_domain[0].m_t[1] = 1.0;
|
|
//m_domain[1].m_t[0] = 0.0;
|
|
//m_domain[1].m_t[1] = 1.0;
|
|
}
|
|
|
|
void ON_BezierSurface::EmergencyDestroy()
|
|
{
|
|
m_cv = 0;
|
|
}
|
|
|
|
bool ON_BezierSurface::Loft( const ON_ClassArray<ON_BezierCurve>& curve_list )
|
|
{
|
|
int i;
|
|
int count = curve_list.Count();
|
|
ON_SimpleArray<const ON_BezierCurve*> ptr_list(count);
|
|
for (i = 0; i < count; i++ )
|
|
{
|
|
ptr_list.Append(&curve_list[i]);
|
|
}
|
|
return Loft(ptr_list.Count(),ptr_list.Array());
|
|
}
|
|
|
|
bool ON_BezierSurface::Loft(
|
|
int curve_count,
|
|
const ON_BezierCurve* const* curve_list
|
|
)
|
|
{
|
|
// 06-06-06 Dale Fugier - tested
|
|
bool rc = false;
|
|
if (curve_count >= 2 && 0 != curve_list && 0 != curve_list[0] )
|
|
{
|
|
// determine order, dimension, is_rat, and cv_stride of compatible shape curves
|
|
int shape_order = curve_list[0]->m_order;
|
|
int shape_dim = curve_list[0]->m_dim;
|
|
int shape_is_rat = (0 != curve_list[0]->m_is_rat) ? 1 : 0;
|
|
if ( shape_dim < 1 || shape_order < 2 )
|
|
return false;
|
|
int i, j, k;
|
|
for ( i = 0; i < curve_count; i++ )
|
|
{
|
|
if ( curve_list[i]->m_order < 2 || curve_list[i]->m_dim < 1 || 0 == curve_list[i]->m_cv)
|
|
return false;
|
|
if ( curve_list[i]->m_dim != shape_dim )
|
|
return false;
|
|
if ( curve_list[i]->m_order > shape_order )
|
|
shape_order = curve_list[i]->m_order;
|
|
if ( 0 != curve_list[i]->m_is_rat )
|
|
shape_is_rat = 1;
|
|
}
|
|
|
|
// build a list of compatible shape curves
|
|
const int shape_cv_stride = (shape_is_rat) ? (shape_dim + 1) : shape_dim;
|
|
ON_SimpleArray<double> meta_point(curve_count*shape_cv_stride*shape_order);
|
|
ON_BezierCurve* temp_shape = 0;
|
|
for ( i = 0; i < curve_count; i++ )
|
|
{
|
|
const ON_BezierCurve* shape = curve_list[i];
|
|
if ( shape->m_order != shape_order
|
|
|| shape->m_is_rat != shape_is_rat
|
|
|| shape->m_cv_stride != shape_cv_stride )
|
|
{
|
|
if ( 0 == temp_shape )
|
|
temp_shape = new ON_BezierCurve();
|
|
temp_shape->operator=(*shape);
|
|
if ( shape_is_rat )
|
|
temp_shape->MakeRational();
|
|
temp_shape->IncreaseDegree(shape_order-1);
|
|
if ( temp_shape->m_dim != shape_dim
|
|
|| temp_shape->m_is_rat != shape_is_rat
|
|
|| temp_shape->m_order != shape_order
|
|
|| temp_shape->m_cv_stride != shape_cv_stride )
|
|
{
|
|
break;
|
|
}
|
|
shape = temp_shape;
|
|
}
|
|
for ( int j_for_loop = 0; j_for_loop < shape->m_order; j_for_loop++ )
|
|
{
|
|
const double* cv = shape->CV(j_for_loop);
|
|
for ( int k_for_loop = 0; k_for_loop < shape_cv_stride; k_for_loop++ )
|
|
meta_point.Append(cv[k_for_loop]);
|
|
}
|
|
}
|
|
if ( 0 != temp_shape )
|
|
{
|
|
delete temp_shape;
|
|
temp_shape = 0;
|
|
}
|
|
if ( meta_point.Count() == curve_count*shape_cv_stride*shape_order )
|
|
{
|
|
ON_BezierCurve bez;
|
|
ON_SimpleArray<double>t(curve_count);
|
|
//22 March 2022 - Chuck - t values should be multiples of
|
|
//1/ccm rather than 1/curve_count.
|
|
int ccm = curve_count-1;
|
|
double dt = 1.0/((double)ccm);
|
|
for ( i = 0; i < ccm; i++ )
|
|
{
|
|
t.Append( i*dt );
|
|
}
|
|
t[curve_count-1] = 1.0;
|
|
// use high dimensional curve loft trick
|
|
//21 March 2022 - Chuck - Shape_dim changed to shape_order in two places.
|
|
rc = bez.Loft( shape_cv_stride*shape_order, curve_count, shape_cv_stride*shape_order, meta_point.Array(), 1, t.Array() )
|
|
? true : false;
|
|
if (rc)
|
|
{
|
|
Create(shape_dim,shape_is_rat,curve_count,shape_order);
|
|
// fill in surface CVs
|
|
for ( i = 0; i < curve_count; i++ )
|
|
{
|
|
const double* bez_cv = bez.CV(i);
|
|
for ( j = 0; j < shape_order; j++ )
|
|
{
|
|
double* srf_cv = CV(i,j);
|
|
for ( k = 0; k < shape_cv_stride; k++ )
|
|
srf_cv[k] = *bez_cv++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::GetBBox( // returns true if successful
|
|
double* boxmin, // minimum
|
|
double* boxmax, // maximum
|
|
bool bGrowBox // true means grow box
|
|
) const
|
|
{
|
|
int i;
|
|
bool rc = (m_order[0] > 0 && m_order[1] > 0) ? true : false;
|
|
for ( i = 0; rc && i < m_order[0]; i++ ) {
|
|
rc = ON_GetPointListBoundingBox( m_dim, m_is_rat, m_order[1], m_cv_stride[1],
|
|
CV(i,0), boxmin, boxmax, bGrowBox );
|
|
bGrowBox = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::GetBoundingBox( // returns true if successful
|
|
ON_BoundingBox& bbox,
|
|
int bGrowBox // true means grow box
|
|
) const
|
|
{
|
|
double *boxmin, *boxmax;
|
|
void* heap_buffer = 0;
|
|
|
|
if ( m_dim > 3 )
|
|
{
|
|
boxmin = (double*)(heap_buffer = onmalloc(2*m_dim*sizeof(*boxmin)));
|
|
memset( boxmin, 0, 2*m_dim*sizeof(*boxmin) );
|
|
boxmax = boxmin + m_dim;
|
|
if ( bGrowBox ) {
|
|
boxmin[0] = bbox.m_min.x;
|
|
boxmin[1] = bbox.m_min.y;
|
|
boxmin[2] = bbox.m_min.z;
|
|
boxmax[0] = bbox.m_max.x;
|
|
boxmax[1] = bbox.m_max.y;
|
|
boxmax[2] = bbox.m_max.z;
|
|
}
|
|
}
|
|
else {
|
|
boxmin = &bbox.m_min.x;
|
|
boxmax = &bbox.m_max.x;
|
|
}
|
|
bool rc = GetBBox( boxmin, boxmax, bGrowBox );
|
|
if ( rc && m_dim > 3 ) {
|
|
bbox.m_min = boxmin;
|
|
bbox.m_max = boxmax;
|
|
}
|
|
|
|
if ( 0 != heap_buffer )
|
|
onfree(heap_buffer);
|
|
return rc;
|
|
}
|
|
|
|
ON_BoundingBox ON_BezierSurface::BoundingBox() const
|
|
{
|
|
ON_BoundingBox bbox;
|
|
if ( !GetBoundingBox(bbox,false) )
|
|
bbox.Destroy();
|
|
return bbox;
|
|
}
|
|
|
|
bool ON_BezierSurface::Transform( const ON_Xform& xform )
|
|
{
|
|
int i;
|
|
bool rc = (m_order[0] > 0 && m_order[1] > 0) ? 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++ )
|
|
{
|
|
rc = ON_TransformPointList( m_dim, m_is_rat,
|
|
m_order[1], m_cv_stride[1],
|
|
CV(i,0), xform );
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::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_BezierSurface::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_BezierSurface::Translate( const ON_3dVector& delta )
|
|
{
|
|
ON_Xform tr(ON_Xform::TranslationTransformation( delta ));
|
|
return Transform( tr );
|
|
}
|
|
|
|
bool ON_BezierSurface::Scale( double x )
|
|
{
|
|
ON_Xform s(ON_Xform::DiagonalTransformation(x));
|
|
return Transform( s );
|
|
}
|
|
|
|
ON_Interval ON_BezierSurface::Domain(
|
|
int // dir - formal parameter intentionally ignored in this virtual function
|
|
) const
|
|
{
|
|
// 0 = "u" domain, 1 = "v" domain
|
|
return ON_Interval(0.0,1.0); //m_domain[(dir>0)?1:0];
|
|
}
|
|
|
|
bool ON_BezierSurface::Reverse( int dir )
|
|
{
|
|
int i;
|
|
bool rc = (m_order[0] > 0 && m_order[1] > 0) ? true : false;
|
|
if ( dir > 0 ) {
|
|
for ( i = 0; rc && i < m_order[0]; i++ ) {
|
|
rc = ON_ReversePointList( m_dim, m_is_rat, m_order[1], m_cv_stride[1], CV(i,0) );
|
|
}
|
|
//m_domain[1].Reverse();
|
|
}
|
|
else {
|
|
for ( i = 0; rc && i < m_order[1]; i++ ) {
|
|
rc = ON_ReversePointList( m_dim, m_is_rat, m_order[0], m_cv_stride[0], CV(0,i) );
|
|
}
|
|
//m_domain[0].Reverse();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::Transpose()
|
|
{
|
|
// transpose surface parameterization (swap "s" and "t")
|
|
int i = m_order[0]; m_order[0] = m_order[1]; m_order[1] = i;
|
|
i = m_cv_stride[0]; m_cv_stride[0] = m_cv_stride[1]; m_cv_stride[1] = i;
|
|
//ON_Interval d = m_domain[0]; m_domain[0] = m_domain[1]; m_domain[1] = d;
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierSurface::Evaluate( // returns false if unable to evaluate
|
|
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
|
|
{
|
|
// TODO: When time permits write a faster special case bezier surface evaluator.
|
|
// For now, cook up some fake knot vectors and use NURBS surface span evaluator.
|
|
double stack_buffer[24];
|
|
double *knot0, *knot1, *p;
|
|
int deg0 = m_order[0]-1;
|
|
int deg1 = m_order[1]-1;
|
|
int i = (deg0<=deg1)?deg1 : deg0;
|
|
|
|
void* heap_buffer = 0;
|
|
size_t sizeof_buffer = i*2*sizeof(*knot0);
|
|
|
|
knot0 = (sizeof_buffer <= sizeof(stack_buffer))
|
|
? stack_buffer
|
|
: (double*)(heap_buffer=onmalloc(sizeof_buffer));
|
|
|
|
p = knot0;
|
|
memset(p,0,i*sizeof(*p));
|
|
p += i;
|
|
while (i--)
|
|
*p++ = 1.0;
|
|
|
|
if ( deg0 >= deg1 )
|
|
{
|
|
knot1 = knot0 + (deg0-deg1);
|
|
}
|
|
else
|
|
{
|
|
knot1 = knot0;
|
|
knot0 = knot1 + (deg1-deg0);
|
|
}
|
|
|
|
bool rc = ON_EvaluateNurbsSurfaceSpan(
|
|
m_dim, // dimension
|
|
m_is_rat, // true if NURBS is rational
|
|
m_order[0], m_order[1], // order0, order1
|
|
knot0, // knot0[] array of (2*order0-2) doubles
|
|
knot1, // knot1[] array of (2*order1-2) doubles
|
|
m_cv_stride[0], m_cv_stride[1], // cv_stride0, cv_stride1
|
|
m_cv, // cv at "lower left" of bispan
|
|
der_count, // number of derivatives to compute (>=0)
|
|
s,t, // evaluation parameters ("s" and "t")
|
|
v_stride, // answer_stride (>=dimension)
|
|
v // answer[] array of length (ndir+1)*answer_stride
|
|
);
|
|
|
|
if ( 0 != heap_buffer )
|
|
onfree(heap_buffer);
|
|
|
|
return rc;
|
|
}
|
|
|
|
ON_3dPoint ON_BezierSurface::PointAt(double s, double t) const
|
|
{
|
|
ON_3dPoint P;
|
|
Evaluate(s,t,0,3,&P.x);
|
|
return P;
|
|
}
|
|
|
|
ON_BezierCurve* ON_BezierSurface::IsoCurve(int dir, double t, ON_BezierCurve* pCrv) const
|
|
{
|
|
if( pCrv == nullptr )
|
|
{
|
|
pCrv = new ON_BezierCurve(m_dim, m_is_rat, m_order[dir]);
|
|
}
|
|
else if ( pCrv->m_dim!=m_dim || pCrv->m_is_rat!= m_is_rat || pCrv->m_order!= m_order[dir])
|
|
{
|
|
pCrv->Create(m_dim, m_is_rat, m_order[dir]);
|
|
}
|
|
|
|
|
|
int bigdim = CVSize() * m_order[dir];
|
|
int stride;
|
|
double* cv = 0;
|
|
double* workspace = 0;
|
|
if( m_cv_stride[1-dir]>m_cv_stride[dir])
|
|
{
|
|
stride = m_cv_stride[1-dir];
|
|
cv = m_cv;
|
|
}
|
|
else
|
|
{
|
|
// purify FMM trauma - // workspace = new double[bigdim*m_order[1-dir]];
|
|
workspace = (double*)onmalloc((bigdim*m_order[1-dir])*sizeof(*workspace));
|
|
|
|
cv = workspace;
|
|
stride = bigdim;
|
|
|
|
int i,j;
|
|
int cvsize = CVSize();
|
|
int cvsize_bytes = cvsize*sizeof(double);
|
|
double* dst = workspace;
|
|
for(i=0; i<m_order[1-dir] ; i++)
|
|
{
|
|
double* src = dir ? CV(i,0): CV(0,i);
|
|
for(j=0; j<m_order[dir]; j++,dst+=cvsize, src+=m_cv_stride[dir] )
|
|
memcpy(dst, src, cvsize_bytes );
|
|
}
|
|
}
|
|
|
|
ON_EvaluateBezier( bigdim, 0, m_order[1-dir], stride, cv, 0.0, 1.0, 0, t, bigdim, pCrv->m_cv );
|
|
|
|
if(workspace)
|
|
{
|
|
// purify FMM trauma - // delete[] workspace;
|
|
onfree(workspace);
|
|
}
|
|
|
|
return pCrv;
|
|
}
|
|
|
|
int ON_BezierSurface::GetNurbForm( ON_NurbsSurface& n ) const
|
|
{
|
|
int rc = 0;
|
|
if ( n.Create( m_dim, m_is_rat, m_order[0], m_order[1], m_order[0], m_order[1] ) )
|
|
{
|
|
if ( n.m_cv == m_cv )
|
|
{
|
|
n.m_cv_stride[0] = m_cv_stride[0];
|
|
n.m_cv_stride[1] = m_cv_stride[1];
|
|
}
|
|
else
|
|
{
|
|
const int sizeof_cv = CVSize()*sizeof(m_cv[0]);
|
|
int i,j;
|
|
for ( i = 0; i < m_order[0]; i++ ) for ( j = 0; j < m_order[1]; j++ ) {
|
|
memcpy( n.CV(i,j), CV(i,j), sizeof_cv );
|
|
}
|
|
}
|
|
n.m_knot[0][m_order[0]-2] = 0.0; //m_domain[0].Min();
|
|
n.m_knot[0][m_order[0]-1] = 1.0; //m_domain[0].Max();
|
|
n.m_knot[1][m_order[1]-2] = 0.0; //m_domain[1].Min();
|
|
n.m_knot[1][m_order[1]-1] = 1.0; //m_domain[1].Max();
|
|
bool b0 = ON_ClampKnotVector( n.m_order[0], n.m_cv_count[0], n.m_knot[0], 2 );
|
|
bool b1 = ON_ClampKnotVector( n.m_order[1], n.m_cv_count[1], n.m_knot[1], 2 );
|
|
rc = (b0 && b1) ? 1 : 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::IsRational() const
|
|
{
|
|
return m_is_rat ? true : false;
|
|
}
|
|
|
|
int ON_BezierSurface::CVSize() const
|
|
{
|
|
return ( m_is_rat && m_dim>0 ) ? m_dim+1 : m_dim;
|
|
}
|
|
|
|
int ON_BezierSurface::Order( int dir ) const
|
|
{
|
|
return (dir>=0&&dir<=1) ? m_order[dir] : 0;
|
|
}
|
|
|
|
int ON_BezierSurface::Degree(int dir) const
|
|
{
|
|
int order = Order(dir);
|
|
return (order>=2) ? order-1 : 0;
|
|
}
|
|
|
|
double* ON_BezierSurface::CV( int i, int j ) const
|
|
{
|
|
return (m_cv) ? (m_cv + i*m_cv_stride[0] + j*m_cv_stride[1]) : nullptr;
|
|
}
|
|
|
|
ON::point_style ON_BezierSurface::CVStyle() const
|
|
{
|
|
return m_is_rat ? ON::homogeneous_rational : ON::not_rational;
|
|
}
|
|
|
|
double ON_BezierSurface::Weight( int i, int j ) const
|
|
{
|
|
return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride[0] + j*m_cv_stride[1] + m_dim] : 1.0;
|
|
}
|
|
|
|
|
|
bool ON_BezierSurface::SetWeight( int i, int j, double w )
|
|
{
|
|
bool rc = false;
|
|
if ( m_is_rat ) {
|
|
double* cv = CV(i,j);
|
|
if (cv) {
|
|
cv[m_dim] = w;
|
|
rc = true;
|
|
}
|
|
}
|
|
else if ( w == 1.0 ) {
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::SetCV( int i, int j, ON::point_style style, const double* Point )
|
|
{
|
|
bool rc = true;
|
|
int k;
|
|
double w;
|
|
|
|
double* cv = CV(i,j);
|
|
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 ( k = 0; k < m_dim; k++ ) {
|
|
cv[k] = w*Point[k];
|
|
}
|
|
}
|
|
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 ( k = 0; k < m_dim; k++ )
|
|
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:
|
|
k = m_is_rat?m_dim+1:m_dim;
|
|
memcpy(cv,Point,k*sizeof(*cv));
|
|
break;
|
|
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::SetCV( int i, int j, const ON_3dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i,j);
|
|
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_BezierSurface::SetCV( int i, int j, const ON_4dPoint& point )
|
|
{
|
|
bool rc = false;
|
|
double* cv = CV(i,j);
|
|
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_BezierSurface::GetCV( int i, int j, ON::point_style style, double* Point ) const
|
|
{
|
|
const double* cv = CV(i,j);
|
|
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;
|
|
case ON::intrinsic_point_style:
|
|
if (IsRational()) dim++;
|
|
memcpy(Point, cv, dim * sizeof(*Point));
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierSurface::GetCV( int i, int j, ON_3dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
const double* cv = CV(i,j);
|
|
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_BezierSurface::GetCV( int i, int j, ON_4dPoint& point ) const
|
|
{
|
|
bool rc = false;
|
|
const double* cv = CV(i,j);
|
|
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_BezierSurface::ZeroCVs()
|
|
{
|
|
// zeros control vertices and, if rational, sets weights to 1
|
|
bool rc = false;
|
|
int i,j;
|
|
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++ ) {
|
|
SetWeight( i,j, 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++ ) {
|
|
cv = CV(i,j);
|
|
memset(cv,0,s);
|
|
if ( m_is_rat )
|
|
cv[m_dim] = 1.0;
|
|
}
|
|
}
|
|
rc = (i>0) ? true : false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::MakeRational()
|
|
{
|
|
if ( !IsRational() ) {
|
|
const int dim = Dimension();
|
|
if ( m_order[0] > 0 && m_order[1] > 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_BezierSurface::MakeNonRational()
|
|
{
|
|
if ( IsRational() ) {
|
|
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_BezierSurface::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_BezierSurface::Trim(
|
|
int dir,
|
|
const ON_Interval& domain
|
|
)
|
|
{
|
|
bool rc = false;
|
|
ON_BezierCurve crv;
|
|
double* cv;
|
|
const int k = m_is_rat ? (m_dim+1) : m_dim;
|
|
const int sizeofcv = k*sizeof(*cv);
|
|
|
|
// GBA 5-Dec-2007. Rewrote this function. The previous implementation
|
|
// would never work if dir==0.
|
|
|
|
if( m_cv_stride[dir] > m_cv_stride[1-dir])
|
|
{
|
|
// cv's are layed out in the right direction so we can interpret the
|
|
// them as the cv's of a high dim'l curve.
|
|
crv.m_cv = m_cv;
|
|
crv.m_dim = crv.m_cv_stride = m_cv_stride[dir];
|
|
crv.m_is_rat = false;
|
|
crv.m_order = m_order[0];
|
|
|
|
rc = crv.Trim(domain);
|
|
|
|
crv.m_cv = nullptr;
|
|
crv.m_dim = crv.m_order = crv.m_cv_stride = 0;
|
|
}
|
|
|
|
else
|
|
{
|
|
// cv's are layed out in the wrong direction so make a curve
|
|
// and copy the cv's into the curve.
|
|
|
|
crv.Create(k*m_order[1-dir],false,m_order[dir]);
|
|
int ind[2];
|
|
int& i= ind[dir];
|
|
int& j= ind[1-dir];
|
|
for( i=0; i<m_order[dir]; i++)
|
|
{
|
|
cv = crv.CV(i);
|
|
for( j=0; j<m_order[1-dir]; j++){
|
|
memcpy( cv, CV( ind[0],ind[1]), sizeofcv);
|
|
cv += k;
|
|
}
|
|
}
|
|
|
|
rc = crv.Trim(domain);
|
|
|
|
if (rc)
|
|
{
|
|
for( i=0; i<m_order[dir]; i++)
|
|
{
|
|
cv = crv.CV(i);
|
|
for( j=0; j<m_order[1-dir]; j++){
|
|
memcpy( CV( ind[0],ind[1]), cv, sizeofcv);
|
|
cv += k;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_BezierSurface::Split(
|
|
int dir, // 0 split at "u"=t, 1= split at "v"=t
|
|
double t, // t = splitting parameter must 0 < t < 1
|
|
ON_BezierSurface& left_bez, // west/south side returned here (can pass *this)
|
|
ON_BezierSurface& right_bez // east/north side returned here (can pass *this)
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
if ( 0.0 < t && t < 1.0 ) {
|
|
double *crvcv;
|
|
int i, j;
|
|
const int hdim = (m_is_rat) ? m_dim+1 : m_dim;
|
|
const int crvdim = hdim*m_order[dir?0:1];
|
|
ON_BezierCurve leftcrv, rightcrv;
|
|
ON_BezierCurve crv(crvdim,0,m_order[dir?1:0]);
|
|
if (dir) {
|
|
for ( j = 0; j < m_order[1]; j++ ) {
|
|
crvcv = crv.CV(j);
|
|
for ( i = 0; i < m_order[0]; i++) {
|
|
memcpy( crvcv, CV(i,j), hdim*sizeof(crvcv[0]) );
|
|
crvcv += hdim;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for ( i = 0; i < m_order[0]; i++) {
|
|
crvcv = crv.CV(i);
|
|
for ( j = 0; j < m_order[1]; j++ ) {
|
|
memcpy( crvcv, CV(i,j), hdim*sizeof(crvcv[0]) );
|
|
crvcv += hdim;
|
|
}
|
|
}
|
|
}
|
|
|
|
// transfer output srf cv memory to output curves
|
|
leftcrv.m_cv_capacity = left_bez.m_cv_capacity;
|
|
leftcrv.m_cv = left_bez.m_cv;
|
|
left_bez.m_cv = 0;
|
|
|
|
rightcrv.m_cv_capacity = right_bez.m_cv_capacity;
|
|
rightcrv.m_cv = right_bez.m_cv;
|
|
right_bez.m_cv = 0;
|
|
|
|
// call curve splitter
|
|
rc = crv.Split( t, leftcrv, rightcrv );
|
|
|
|
// transfer output crv cv memory back to output surfaces
|
|
left_bez.m_cv_capacity = leftcrv.m_cv_capacity;
|
|
left_bez.m_cv = leftcrv.m_cv;
|
|
leftcrv.m_cv = 0;
|
|
|
|
right_bez.m_cv_capacity = rightcrv.m_cv_capacity;
|
|
right_bez.m_cv = rightcrv.m_cv;
|
|
rightcrv.m_cv = 0;
|
|
|
|
if ( rc )
|
|
{
|
|
// Greg Arden, 12 May 2003 Fixes TRR 10627. This block of code was wrong.
|
|
right_bez.m_dim = left_bez.m_dim = m_dim;
|
|
right_bez.m_is_rat = left_bez.m_is_rat = m_is_rat;
|
|
right_bez.m_order[0] = left_bez.m_order[0] = m_order[0];
|
|
right_bez.m_order[1] = left_bez.m_order[1] = m_order[1];
|
|
right_bez.m_cv_stride[1-dir] = left_bez.m_cv_stride[1-dir] = hdim;
|
|
left_bez.m_cv_stride[dir] = leftcrv.m_cv_stride; // 3 March 2005 - Dale Lear changed crvdim to m_cv_stride
|
|
right_bez.m_cv_stride[dir] = rightcrv.m_cv_stride;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_BezierSurface::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
|
|
) const
|
|
{
|
|
const double* points = 0;
|
|
int point_count = 0;
|
|
int point_stride = 0;
|
|
|
|
switch ( side )
|
|
{
|
|
case 0: // south
|
|
points = CV(0,0);
|
|
point_count = m_order[0];
|
|
point_stride = m_cv_stride[0];
|
|
break;
|
|
|
|
case 1: // east
|
|
points = CV(m_order[0]-1,0);
|
|
point_count = m_order[1];
|
|
point_stride = m_cv_stride[1];
|
|
break;
|
|
|
|
case 2: // north
|
|
points = CV(0,m_order[1]-1);
|
|
point_count = m_order[0];
|
|
point_stride = m_cv_stride[0];
|
|
break;
|
|
|
|
case 3: // west
|
|
points = CV(0,0);
|
|
point_count = m_order[1];
|
|
point_stride = m_cv_stride[1];
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return ON_PointsAreCoincident(m_dim,m_is_rat,point_count,point_stride,points);
|
|
}
|
|
|
|
static bool BezierControlPolygonLength(const ON_BezierSurface& Bez, int dir, double* length)
|
|
{
|
|
bool rc = false;
|
|
if (length && dir >= 0 && dir <= 1 && Bez.m_order[0] >= 2 && Bez.m_order[1] >= 2 && Bez.m_cv != nullptr)
|
|
{
|
|
rc = true;
|
|
*length = 0.0;
|
|
|
|
for (int i = 0; i < Bez.m_order[1 - dir]; i++)
|
|
{
|
|
double len = 0.0;
|
|
const double*p = (dir) ? Bez.CV(i, 0) : Bez.CV(0, i);
|
|
ON_GetPolylineLength(Bez.m_dim, Bez.m_is_rat, Bez.m_order[dir], Bez.m_cv_stride[dir], p, &len);
|
|
if (len > *length)
|
|
*length = len;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierSurface::GetSurfaceSize(
|
|
double* width,
|
|
double* height
|
|
) const
|
|
{
|
|
bool rc = BezierControlPolygonLength(*this, 0, width);
|
|
rc = rc && BezierControlPolygonLength(*this, 1, height);
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_ReparameterizeRationalBezierCurve(
|
|
double c,
|
|
int dim,
|
|
int order,
|
|
int cvstride,
|
|
double* cv
|
|
)
|
|
{
|
|
double d;
|
|
int j;
|
|
|
|
if ( !ON_IsValid(c) || 0.0 == c )
|
|
return false;
|
|
|
|
if (c == 1.0)
|
|
return true;
|
|
|
|
d = c;
|
|
cv += cvstride;
|
|
dim++;
|
|
cvstride -= dim;
|
|
while(--order)
|
|
{
|
|
j = dim;
|
|
while(j--)
|
|
*cv++ *= d;
|
|
cv += cvstride;
|
|
d *= c;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierCurve::Reparametrize( double c )
|
|
{
|
|
return Reparameterize(c);
|
|
}
|
|
|
|
bool ON_BezierCurve::Reparameterize( double c )
|
|
{
|
|
if ( !ON_IsValid(c) || 0.0 == c )
|
|
return false;
|
|
|
|
if (c == 1.0)
|
|
return true;
|
|
|
|
MakeRational();
|
|
|
|
return ON_ReparameterizeRationalBezierCurve(c,m_dim,m_order,m_cv_stride,m_cv);
|
|
|
|
//return true;
|
|
}
|
|
|
|
bool ON_BezierCurve::ScaleConrolPoints( int i, double w )
|
|
{
|
|
if ( i < 0 || i >= m_order || w == 0.0 || w == ON_UNSET_VALUE )
|
|
return false;
|
|
if ( w == Weight(i) )
|
|
return true;
|
|
|
|
if ( !IsRational() )
|
|
MakeRational();
|
|
|
|
double c = Weight(i);
|
|
if ( 0.0 == c || ON_UNSET_VALUE == c )
|
|
return false;
|
|
c = w/c;
|
|
|
|
int k, j;
|
|
double* cv;
|
|
int cvdim = CVSize();
|
|
for ( k = 0; k < m_order; k++ )
|
|
{
|
|
cv = CV(k);
|
|
j = cvdim;
|
|
while(j--)
|
|
*cv++ *= c;
|
|
}
|
|
CV(i)[m_dim] = w;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_ChangeRationalBezierCurveWeights(
|
|
int dim, int order, int cvstride, double* cv,
|
|
int i0, double w0,
|
|
int i1, double w1
|
|
)
|
|
{
|
|
// Reference - Farauki
|
|
double r, s, v0, v1;
|
|
int i, j;
|
|
|
|
if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 )
|
|
return false;
|
|
if ( i0 < 0 || i1 >= order )
|
|
return false;
|
|
if ( i0 == i1 && w0 != w1 )
|
|
return false;
|
|
if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w1 < 0.0) )
|
|
return false;
|
|
if (i0 > i1)
|
|
{
|
|
i = i0; i0 = i1; i1 = i; r = w0; w0 = w1; w1 = r;
|
|
}
|
|
|
|
v0 = cv[cvstride*i0 + dim];
|
|
v1 = cv[cvstride*i1 + dim];
|
|
if (!ON_IsValid(v0) || !ON_IsValid(v1) || v0 == 0.0 || v1 == 0.0)
|
|
return false;
|
|
if (v0 < 0.0 && v1 > 0.0)
|
|
return false;
|
|
if ( v0 > 0.0 && v1 < 0.0)
|
|
return false;
|
|
|
|
if (i0 == 0 || i0 == i1)
|
|
{
|
|
s = w0/v0;
|
|
r = (i0 != i1) ? pow( (w1/v1)/s, 1.0/((double)i1)) : 1.0;
|
|
}
|
|
else
|
|
{
|
|
r = pow( (w1/v1)*(v0/w0), 1.0/((double)(i1-i0)) );
|
|
s = (w0/v0)/pow(r,(double)i0);
|
|
}
|
|
if ( !ON_IsValid(r) || r <= 0.0 )
|
|
return false;
|
|
if ( !ON_IsValid(s) || 0.0 == s )
|
|
return false;
|
|
|
|
if (s != 1.0)
|
|
{
|
|
dim++;
|
|
cvstride -= dim;
|
|
for ( i = 0; i < order; i++ )
|
|
{
|
|
j = dim;
|
|
while(j--)
|
|
*cv++ *= s;
|
|
cv += cvstride;
|
|
}
|
|
cvstride += dim;
|
|
dim--;
|
|
cv -= (cvstride*order);
|
|
}
|
|
if (r != 1.0)
|
|
ON_ReparameterizeRationalBezierCurve(r,dim,order,cvstride,cv);
|
|
|
|
// make sure weights agree to the last bit!
|
|
cv[cvstride*i0+dim] = w0;
|
|
cv[cvstride*i1+dim] = w1;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_BezierCurve::ChangeWeights( int i0, double w0, int i1, double w1 )
|
|
{
|
|
//double r, s, v0, v1;
|
|
double v0, v1;
|
|
int i;
|
|
// 2 June 2003 Dale Lear bug fixes made this function work
|
|
|
|
if ( i0 < 0 || i0 >= m_order || i1 < 0 || i1 >= m_order )
|
|
return false;
|
|
if ( 0.0 == w0 || !ON_IsValid(w0) || 0.0 == w1 || !ON_IsValid(w1) )
|
|
return false;
|
|
if ( w0 < 0.0 && w1 > 0.0 )
|
|
return false;
|
|
if ( w0 > 0.0 && w1 < 0.0 )
|
|
return false;
|
|
if ( i0 == i1 && w0 != w1 )
|
|
return false;
|
|
|
|
if (i0 > i1)
|
|
{
|
|
i = i0; i0 = i1; i1 = i; v0 = w0; w0 = w1; w1 = v0;
|
|
}
|
|
|
|
v0 = Weight(i0);
|
|
v1 = Weight(i1);
|
|
if ( w0 == v0 && w1 == v1 )
|
|
return true;
|
|
|
|
MakeRational();
|
|
return ON_ChangeRationalBezierCurveWeights(m_dim,m_order,m_cv_stride,m_cv,i0,w0,i1,w1);
|
|
|
|
/*
|
|
if ( 0.0 == v0 || !ON_IsValid(v0) || 0.0 == v1 || !ON_IsValid(v1) )
|
|
return false;
|
|
if ( v0 < 0.0 && v1 > 0.0 )
|
|
return false;
|
|
if ( v0 > 0.0 && v1 < 0.0 )
|
|
return false;
|
|
|
|
if (i0 == 0 || i0 == i1)
|
|
{
|
|
s = w0/v0;
|
|
r = (i0 != i1) ? pow( (w1/v1)/s, 1.0/((double)i1)) : 1.0;
|
|
}
|
|
else
|
|
{
|
|
r = pow( (w1/v1)*(v0/w0), 1.0/((double)(i1-i0)) );
|
|
s = (w0/v0)/pow(r,(double)i0);
|
|
}
|
|
if (r <= 0.0)
|
|
return false;
|
|
|
|
MakeRational();
|
|
int j, cvdim = CVSize();
|
|
double* cv;
|
|
if (s != 1.0)
|
|
{
|
|
for ( i = 0; i < m_order; i++ )
|
|
{
|
|
cv = CV(i);
|
|
j = cvdim;
|
|
while (j--) *cv++ *= s;
|
|
}
|
|
}
|
|
if (r != 1.0)
|
|
Reparametrize(r);
|
|
|
|
// make sure weights agree to the last bit!
|
|
CV(i0)[m_dim] = w0;
|
|
CV(i1)[m_dim] = w1;
|
|
|
|
return true;
|
|
*/
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
|
|
|
|
ON_3dPoint ON_BezierCurve::PointAt( double t ) const
|
|
{
|
|
ON_3dPoint p(0.0,0.0,0.0);
|
|
EvPoint(t,p);
|
|
return p;
|
|
}
|
|
|
|
ON_3dVector ON_BezierCurve::DerivativeAt( double t ) const
|
|
{
|
|
ON_3dPoint p(0.0,0.0,0.0);
|
|
ON_3dVector d(0.0,0.0,0.0);
|
|
Ev1Der(t,p,d);
|
|
return d;
|
|
}
|
|
|
|
ON_3dVector ON_BezierCurve::TangentAt( double t ) const
|
|
{
|
|
ON_3dPoint point;
|
|
ON_3dVector tangent;
|
|
EvTangent( t, point, tangent );
|
|
return tangent;
|
|
}
|
|
|
|
ON_3dVector ON_BezierCurve::CurvatureAt( double t ) const
|
|
{
|
|
ON_3dPoint point;
|
|
ON_3dVector tangent, kappa;
|
|
EvCurvature( t, point, tangent, kappa );
|
|
return kappa;
|
|
}
|
|
|
|
bool ON_BezierCurve::EvTangent(
|
|
double t,
|
|
ON_3dPoint& point,
|
|
ON_3dVector& tangent
|
|
) const
|
|
{
|
|
ON_3dVector D1, D2;//, K;
|
|
tangent = ON_3dVector::ZeroVector;
|
|
bool rc = Ev1Der( t, point, tangent );
|
|
if ( rc && !tangent.Unitize() )
|
|
{
|
|
if ( Ev2Der( t, point, D1, D2 ) )
|
|
{
|
|
// Use l'Hopital's rule to show that if the unit tangent
|
|
// exists, the 1rst derivative is zero, and the 2nd
|
|
// derivative is nonzero, then the unit tangent is equal
|
|
// to +/-the unitized 2nd derivative. The sign is equal
|
|
// to the sign of D1(s) o D2(s) as s approaches the
|
|
// evaluation parameter.
|
|
tangent = D2;
|
|
rc = tangent.Unitize();
|
|
if ( rc )
|
|
{
|
|
ON_Interval domain = Domain();
|
|
double tminus = 0.0;
|
|
double tplus = 0.0;
|
|
if ( domain.IsIncreasing() && ON_GetParameterTolerance( domain[0], domain[1], t, &tminus, &tplus ) )
|
|
{
|
|
ON_3dPoint p;
|
|
ON_3dVector d1, d2;
|
|
double eps = 0.0;
|
|
double d1od2tol = 0.0; //1.0e-10; // 1e-5 is too big
|
|
double d1od2;
|
|
double tt = t;
|
|
//double dt = 0.0;
|
|
|
|
if ( t < domain[1] )
|
|
{
|
|
eps = tplus-t;
|
|
if ( eps <= 0.0 || t+eps > domain.ParameterAt(0.1) )
|
|
return rc;
|
|
}
|
|
else
|
|
{
|
|
eps = tminus - t;
|
|
if ( eps >= 0.0 || t+eps < domain.ParameterAt(0.9) )
|
|
return rc;
|
|
}
|
|
|
|
int i, negative_count=0, zero_count=0;
|
|
int test_count = 3;
|
|
for ( i = 0; i < test_count; i++, eps *= 0.5 )
|
|
{
|
|
tt = t + eps;
|
|
if ( tt == t )
|
|
break;
|
|
if (!Ev2Der( tt, p, d1, d2 ))
|
|
break;
|
|
d1od2 = d1*d2;
|
|
if ( d1od2 > d1od2tol )
|
|
break;
|
|
if ( d1od2 < d1od2tol )
|
|
negative_count++;
|
|
else
|
|
zero_count++;
|
|
}
|
|
if ( negative_count > 0 && test_count == negative_count+zero_count )
|
|
{
|
|
// all sampled d1od2 values were <= 0
|
|
// and at least one was strictly < 0.
|
|
tangent = -tangent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::EvCurvature(
|
|
double t,
|
|
ON_3dPoint& point,
|
|
ON_3dVector& tangent,
|
|
ON_3dVector& kappa
|
|
) const
|
|
{
|
|
ON_3dVector d1, d2;
|
|
bool rc = Ev2Der( t, point, d1, d2 );
|
|
if ( rc )
|
|
{
|
|
rc = ON_EvCurvature( d1, d2, tangent, kappa )?true:false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_BezierCurve::EvPoint( // returns false if unable to evaluate
|
|
double t, // evaluation parameter
|
|
ON_3dPoint& point // returns value of curve
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
double ws[128];
|
|
double* v;
|
|
if ( Dimension() <= 3 ) {
|
|
v = &point.x;
|
|
point.x = 0.0;
|
|
point.y = 0.0;
|
|
point.z = 0.0;
|
|
}
|
|
else if ( Dimension() <= 128 ) {
|
|
v = ws;
|
|
}
|
|
else {
|
|
v = (double*)onmalloc(Dimension()*sizeof(*v));
|
|
}
|
|
rc = Evaluate( t, 0, Dimension(), v );
|
|
if ( Dimension() > 3 ) {
|
|
point.x = v[0];
|
|
point.y = v[1];
|
|
point.z = v[2];
|
|
if ( Dimension() > 128 )
|
|
onfree(v);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::Ev1Der( // returns false if unable to evaluate
|
|
double t, // evaluation parameter
|
|
ON_3dPoint& point,
|
|
ON_3dVector& derivative
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
const int dim = Dimension();
|
|
double ws[2*64];
|
|
double* v;
|
|
point.x = 0.0;
|
|
point.y = 0.0;
|
|
point.z = 0.0;
|
|
derivative.x = 0.0;
|
|
derivative.y = 0.0;
|
|
derivative.z = 0.0;
|
|
if ( dim <= 64 ) {
|
|
v = ws;
|
|
}
|
|
else {
|
|
v = (double*)onmalloc(2*dim*sizeof(*v));
|
|
}
|
|
rc = Evaluate( t, 1, dim, v);
|
|
point.x = v[0];
|
|
derivative.x = v[dim];
|
|
if ( dim > 1 ) {
|
|
point.y = v[1];
|
|
derivative.y = v[dim+1];
|
|
if ( dim > 2 ) {
|
|
point.z = v[2];
|
|
derivative.z = v[dim+2];
|
|
if ( dim > 64 )
|
|
onfree(v);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_BezierCurve::Ev2Der( // returns false if unable to evaluate
|
|
double t, // evaluation parameter
|
|
ON_3dPoint& point,
|
|
ON_3dVector& firstDervative,
|
|
ON_3dVector& secondDervative
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
const int dim = Dimension();
|
|
double ws[3*64];
|
|
double* v;
|
|
point.x = 0.0;
|
|
point.y = 0.0;
|
|
point.z = 0.0;
|
|
firstDervative.x = 0.0;
|
|
firstDervative.y = 0.0;
|
|
firstDervative.z = 0.0;
|
|
secondDervative.x = 0.0;
|
|
secondDervative.y = 0.0;
|
|
secondDervative.z = 0.0;
|
|
if ( dim <= 64 ) {
|
|
v = ws;
|
|
}
|
|
else {
|
|
v = (double*)onmalloc(3*dim*sizeof(*v));
|
|
}
|
|
rc = Evaluate( t, 2, dim, v );
|
|
point.x = v[0];
|
|
firstDervative.x = v[dim];
|
|
secondDervative.x = v[2*dim];
|
|
if ( dim > 1 ) {
|
|
point.y = v[1];
|
|
firstDervative.y = v[dim+1];
|
|
secondDervative.y = v[2*dim+1];
|
|
if ( dim > 2 ) {
|
|
point.z = v[2];
|
|
firstDervative.z = v[dim+2];
|
|
secondDervative.z = v[2*dim+2];
|
|
if ( dim > 64 )
|
|
onfree(v);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#define Internal_ON_BezierSurfaceInterpolateMaxOrder 10U
|
|
|
|
static bool Internal_CalculateON_BezierSurfaceGridInterploationMatrix(
|
|
unsigned order0,
|
|
unsigned order1,
|
|
ON_Matrix& T
|
|
)
|
|
{
|
|
const unsigned max_order = Internal_ON_BezierSurfaceInterpolateMaxOrder;
|
|
if (order0 < 2 || order0 > max_order || order1 < 2 || order1 > max_order)
|
|
{
|
|
ON_ERROR("Invalid input point grid size.");
|
|
return false;
|
|
}
|
|
|
|
// TWODEX() converts 0 <= k < order0*order1 into (0<=i<order0,0<=j<order1)
|
|
// and is used to map count x count matrix row and column indices into bezier cv and grid point indices.
|
|
#define TWODEX(k) ((k) % order0), ((k) / order0)
|
|
|
|
const unsigned count = order0 * order1;
|
|
const double degree0 = (double)(order0 - 1);
|
|
const double degree1 = (double)(order1 - 1);
|
|
|
|
const double zero = 0.0;
|
|
const double one = 1.0;
|
|
const double zero_tolerance = 1e-12;
|
|
|
|
// Create a 1-dimensional bezier surface
|
|
ON_BezierSurface bibez(1, 0, order0, order1);
|
|
for (unsigned cvdex = 0; cvdex < count; ++cvdex)
|
|
{
|
|
const ON_2udex cv2dex(TWODEX(cvdex));
|
|
bibez.SetCV(cv2dex.i, cv2dex.j, ON::point_style::intrinsic_point_style, &zero);
|
|
}
|
|
|
|
// Calculate the evaluation matrix "E"
|
|
ON_Matrix E(count, count);
|
|
for (unsigned uvdex = 0; uvdex < count; ++uvdex)
|
|
{
|
|
const ON_2udex uv2dex(TWODEX(uvdex));
|
|
const double uv[2] = { ((double)uv2dex.i) / degree0, ((double)uv2dex.j) / degree1 };
|
|
|
|
for (unsigned cvdex = 0; cvdex < count; ++cvdex)
|
|
{
|
|
const ON_2udex bibezCVdex(TWODEX(cvdex));
|
|
bibez.SetCV(bibezCVdex.i, bibezCVdex.j, ON::point_style::intrinsic_point_style, &one);
|
|
E[uvdex][cvdex] = ON_DBL_QNAN;
|
|
if (false == bibez.Evaluate(uv[0], uv[1], 0, 1, &E[uvdex][cvdex]))
|
|
{
|
|
ON_ERROR("Invalid parameters passed to bibez.Evaluate().");
|
|
return false;
|
|
}
|
|
bibez.SetCV(bibezCVdex.i, bibezCVdex.j, ON::point_style::intrinsic_point_style, &zero);
|
|
if (fabs(E[uvdex][cvdex]) <= zero_tolerance)
|
|
E[uvdex][cvdex] = 0.0;
|
|
else if (fabs(E[uvdex][cvdex] - 1.0) <= zero_tolerance)
|
|
E[uvdex][cvdex] = 1.0;
|
|
}
|
|
}
|
|
|
|
// Calculate the interpolation matrix "T"
|
|
T = E;
|
|
T.Invert(0.0);
|
|
|
|
for (unsigned i = 0; i < count; ++i) for (unsigned j = 0; j < count; ++j)
|
|
{
|
|
if (fabs(T[i][j]) <= zero_tolerance)
|
|
T[i][j] = 0.0;
|
|
else if (fabs(T[i][j] - 1.0) <= zero_tolerance)
|
|
T[i][j] = 1.0;
|
|
}
|
|
|
|
#if 0
|
|
// Validate E*T = T*E = identity.
|
|
ON_Matrix ET;
|
|
ET.Multiply(E, T);
|
|
ON_Matrix TE;
|
|
TE.Multiply(T, E);
|
|
|
|
for (unsigned i = 0; i < count; ++i) for (unsigned j = 0; j < count; ++j)
|
|
{
|
|
const double te = TE[i][j];
|
|
const double et = ET[i][j];
|
|
const double id = (i == j) ? 1.0 : 0.0;
|
|
|
|
if (false == (fabs(id - te) <= zero_tolerance))
|
|
{
|
|
ON_ERROR("T*E != identity matrix.");
|
|
return false;
|
|
}
|
|
|
|
if (false == (fabs(id - et) <= zero_tolerance))
|
|
{
|
|
ON_ERROR("E*T != identity matrix.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Valitate the interpolation matrix "T"
|
|
double maxe = 0.0;
|
|
ON_Matrix grid(order0, order1);
|
|
grid.Zero();
|
|
ON_2udex grid2dex;
|
|
for (grid2dex.i = 0; grid2dex.i < order0; ++grid2dex.i)
|
|
{
|
|
for (grid2dex.j = 0; grid2dex.j < order1; ++grid2dex.j)
|
|
{
|
|
grid[grid2dex.i][grid2dex.j] = 1.0;
|
|
|
|
// Use bibez = T*grid to set bibez cvs
|
|
for (unsigned cvdex = 0; cvdex < count; cvdex++)
|
|
{
|
|
double cv = 0.0;
|
|
for (unsigned k = 0; k < count; ++k)
|
|
{
|
|
const ON_2udex gdex(TWODEX(k));
|
|
const double g = grid[gdex.i][gdex.j];
|
|
cv += T[cvdex][k] * g;
|
|
const ON_2udex bibezCVdex(TWODEX(cvdex));
|
|
bibez.SetCV(bibezCVdex.i, bibezCVdex.j, ON::point_style::intrinsic_point_style, &cv);
|
|
}
|
|
}
|
|
|
|
// validate bibez(i/degree0, j/degree1) = grid(i,j)
|
|
for (unsigned i = 0; i < order0; ++i)
|
|
{
|
|
const double s = ((double)i) / degree0;
|
|
for (unsigned j = 0; j < order1; ++j)
|
|
{
|
|
const double t = ((double)j) / degree1;
|
|
double v = ON_DBL_QNAN;
|
|
bibez.Evaluate(s, t, 0, 1, &v);
|
|
const double g = grid[i][j];
|
|
const double e = fabs(v - g);
|
|
if (false == (e <= zero_tolerance))
|
|
{
|
|
text_log.Print(
|
|
"Interpolation test failed. bibez(%g,%g) = %g. grid[%u][%u] = %g.\n",
|
|
s, t, v, i, j, g
|
|
);
|
|
ON_ERROR("Interpolation validation failed.");
|
|
return false;
|
|
}
|
|
if (e > maxe)
|
|
maxe = e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
text_log.Print("order = (%u,%u). Maximimum normalized interpolation error = %g.\n", order0, order1, maxe);
|
|
#endif
|
|
|
|
#undef TWODEX
|
|
|
|
return true;
|
|
}
|
|
|
|
static const ON_Matrix* Internal_ON_BezierSurfaceGridInterploationMatrix(
|
|
unsigned order0, unsigned order1
|
|
)
|
|
{
|
|
if (
|
|
order0 < 2 || order0 > Internal_ON_BezierSurfaceInterpolateMaxOrder
|
|
||
|
|
order1 < 2 || order1 > Internal_ON_BezierSurfaceInterpolateMaxOrder)
|
|
{
|
|
ON_ERROR("Invalid input point grid size.");
|
|
return nullptr;
|
|
}
|
|
|
|
static ON_Matrix* Tcache[Internal_ON_BezierSurfaceInterpolateMaxOrder - 1U][Internal_ON_BezierSurfaceInterpolateMaxOrder - 1U] = {};
|
|
if (nullptr == Tcache[order0-2][order1-2])
|
|
{
|
|
ON_Matrix T;
|
|
if (Internal_CalculateON_BezierSurfaceGridInterploationMatrix(order0,order1,T))
|
|
{
|
|
// The Tcache[][] matrices are created as needed and then used for the lifetime of the instance.
|
|
// The memory used to store the matrices is app workspace memory and is not leaked.
|
|
ON_MemoryAllocationTracking disable_tracking(false);
|
|
Tcache[order0 - 2][order1 - 2] = new ON_Matrix(T);
|
|
}
|
|
}
|
|
|
|
return Tcache[order0 - 2][order1 - 2];
|
|
}
|
|
|
|
|
|
|
|
ON_BezierSurface* ON_BezierSurface::InterpolateGrid(
|
|
const double* point_grid,
|
|
int dim,
|
|
int point_count0, int point_count1,
|
|
size_t point_stride0, size_t point_stride1,
|
|
ON_BezierSurface* dest
|
|
)
|
|
{
|
|
const unsigned max_order = Internal_ON_BezierSurfaceInterpolateMaxOrder;
|
|
|
|
if (nullptr == point_grid)
|
|
return nullptr;
|
|
if (dim < 1)
|
|
return nullptr;
|
|
if (point_count0 < 2 || point_count0 > max_order)
|
|
return nullptr;
|
|
if (point_count1 < 2 || point_count1 > max_order)
|
|
return nullptr;
|
|
if (point_stride0 < dim)
|
|
return nullptr;
|
|
if (point_stride1 < dim)
|
|
return nullptr;
|
|
if (point_stride0 < point_stride1 * dim && point_stride1 < point_stride0 * dim)
|
|
return nullptr;
|
|
|
|
const ON_Matrix* T = Internal_ON_BezierSurfaceGridInterploationMatrix(point_count0, point_count1);
|
|
if (nullptr == T)
|
|
return nullptr;
|
|
|
|
ON_BezierSurface* bez = (nullptr != dest) ? dest : new ON_BezierSurface();
|
|
if (
|
|
dim != bez->m_dim
|
|
|| 0 != bez->m_is_rat
|
|
|| point_count0 != bez->m_order[0]
|
|
|| point_count1 != bez->m_order[1]
|
|
|| nullptr == bez->m_cv
|
|
|| bez->m_cv_stride[0] < dim
|
|
|| bez->m_cv_stride[1] < dim
|
|
|| (bez->m_cv_stride[0] < bez->m_cv_stride[1]*dim && bez->m_cv_stride[1] < bez->m_cv_stride[0] * dim)
|
|
)
|
|
{
|
|
bez->ReserveCVCapacity(point_count0 * point_count1 * dim);
|
|
bez->m_dim = dim;
|
|
bez->m_is_rat = 0;
|
|
bez->m_order[0] = point_count0;
|
|
bez->m_order[1] = point_count1;
|
|
bez->m_cv_stride[0] = bez->m_dim * bez->m_order[1];
|
|
bez->m_cv_stride[1] = bez->m_dim;
|
|
}
|
|
|
|
const int Tcount = point_count0 * point_count1;
|
|
for (int Ti = 0; Ti < Tcount; ++Ti)
|
|
{
|
|
const ON_2dex cvdex( Ti % point_count0, Ti / point_count0 );
|
|
double* bezcv = bez->CV(cvdex.i, cvdex.j);
|
|
for (int k = 0; k < dim; ++k)
|
|
bezcv[k] = 0.0;
|
|
const double* Trow = T->m[Ti];
|
|
for (int Tj = 0; Tj < Tcount; ++Tj)
|
|
{
|
|
const double t = Trow[Tj];
|
|
if (0.0 == t)
|
|
continue;
|
|
const ON_2dex griddex(Tj % point_count0, Tj / point_count0);
|
|
const double* G = point_grid + (griddex.i * point_stride0 + griddex.j * point_stride1);
|
|
for (int k = 0; k < dim; ++k)
|
|
bezcv[k] += t * G[k];
|
|
}
|
|
}
|
|
|
|
return bez;
|
|
}
|
|
|
|
|
|
ON_BezierSurface* ON_BezierSurface::InterpolateGrid(
|
|
const ON_3dPoint* point_grid,
|
|
int point_count0, int point_count1,
|
|
size_t point_stride0, size_t point_stride1,
|
|
ON_BezierSurface* dest
|
|
)
|
|
{
|
|
return ON_BezierSurface::InterpolateGrid(
|
|
(const double*)point_grid,
|
|
3,
|
|
point_count0, point_count1,
|
|
point_stride0*3, point_stride1*3,
|
|
dest
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
|