Files
opennurbs/opennurbs_surface.cpp
Bozo The Builder e7c29061e3 Sync changes from upstream repository
Co-authored-by: Alain <alain@mcneel.com>
Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: croudyj <croudyj@gmail.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Giulio Piacentino <giulio@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: Jussi Aaltonen <jussi@mcneel.com>
Co-authored-by: kike-garbo <kike@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
2022-11-21 14:18:57 -08:00

1567 lines
43 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
ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Surface,ON_Geometry,"4ED7D4E1-E947-11d3-BFE5-0010830122F0");
ON_Surface::ON_Surface()
: ON_Geometry()
{}
ON_Surface::ON_Surface(const ON_Surface& src)
: ON_Geometry(src)
{}
unsigned int ON_Surface::SizeOf() const
{
unsigned int sz = ON_Geometry::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Geometry));
// Currently, the size of m_stree is not included
// because this is cached runtime information.
// Applications that care about object size are
// typically storing "inactive" objects for potential
// future use and should call DestroyRuntimeCache(true)
// to remove any runtime cache information.
return sz;
}
ON_Surface& ON_Surface::operator=(const ON_Surface& src)
{
DestroySurfaceTree();
ON_Geometry::operator=(src);
return *this;
}
ON_Surface::~ON_Surface()
{
// Do not call the (virtual) DestroyRuntimeCache or
// DestroySurfaceTree (which calls DestroyRuntimeCache()
// because it opens the potential for crashes in a
// "dirty" destructors of classes derived from ON_Surface
// that to not use DestroyRuntimeCache() in their
// destructors and to not set deleted pointers to zero.
}
ON_Surface* ON_Surface::DuplicateSurface() const
{
return Duplicate();
}
ON::object_type ON_Surface::ObjectType() const
{
return ON::surface_object;
}
bool ON_Surface::GetDomain( int dir, double* t0, double* t1 ) const
{
ON_Interval d = Domain(dir);
if ( t0 ) *t0 = d[0];
if ( t1 ) *t1 = d[1];
return d.IsIncreasing();
}
bool ON_Surface::GetSurfaceSize(
double* width,
double* height
) const
{
if ( width )
*width = 0.0;
if ( height )
*height = 0.0;
return false;
}
bool ON_Surface::SetDomain( int dir, ON_Interval domain )
{
return ( dir >= 0
&& dir <= 1
&& domain.IsIncreasing()
&& SetDomain( dir, domain[0], domain[1] )) ? true : false;
}
bool ON_Surface::SetDomain(
int, // 0 sets first parameter's domain, 1 gets second parameter's domain
double, double
)
{
// TODO make this pure virutual when all the source code is available
return false;
}
//////////
// If t is in the domain of the surface, GetSpanVectorIndex() returns the
// span vector index "i" such that span_vector[i] <= t <= span_vector[i+1].
// The "side" parameter determines which span is selected when t is at the
// end of a span.
//
//virtual
bool ON_Surface::GetSpanVectorIndex(
int dir, // 0 gets first parameter's domain, 1 gets second parameter's domain
double t, // [IN] t = evaluation parameter
int side, // [IN] side 0 = default, -1 = from below, +1 = from above
int* span_vector_i, // [OUT] span vector index
ON_Interval* span_domain // [OUT] domain of the span containing "t"
) const
{
bool rc = false;
int i;
int span_count = SpanCount(dir);
if ( span_count > 0 ) {
double* span_vector = (double*)onmalloc((span_count+1)*sizeof(span_vector[0]));
rc = GetSpanVector( dir, span_vector );
if (rc) {
i = ON_NurbsSpanIndex( 2, span_count, span_vector, t, side, 0 );
if ( i >= 0 && i <= span_count ) {
if ( span_vector_i )
*span_vector_i = i;
if ( span_domain )
span_domain->Set( span_vector[i], span_vector[i+1] );
}
else
rc = false;
}
onfree(span_vector);
}
return rc;
}
bool ON_Surface::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus
int dir,
double t, // t = parameter in domain
double* tminus, // tminus
double* tplus // tplus
) const
{
bool rc = false;
ON_Interval d = Domain( dir );
if ( d.IsIncreasing() )
rc = ON_GetParameterTolerance( d.Min(), d.Max(), t, tminus, tplus );
return rc;
}
ON_Surface::ISO
ON_Surface::IsIsoparametric( const ON_Curve& curve, const ON_Interval* subdomain ) const
{
ISO iso = not_iso;
if ( subdomain )
{
ON_Interval cdom = curve.Domain();
double t0 = cdom.NormalizedParameterAt(subdomain->Min());
double t1 = cdom.NormalizedParameterAt(subdomain->Max());
if ( t0 < t1-ON_SQRT_EPSILON )
{
if ( (t0 > ON_SQRT_EPSILON && t0 < 1.0-ON_SQRT_EPSILON) || (t1 > ON_SQRT_EPSILON && t1 < 1.0-ON_SQRT_EPSILON) )
{
cdom.Intersection(*subdomain);
if ( cdom.IsIncreasing() )
{
ON_NurbsCurve nurbs_curve;
if ( curve.GetNurbForm( nurbs_curve, 0.0,&cdom) )
{
return IsIsoparametric( nurbs_curve, 0 );
}
}
}
}
}
ON_BoundingBox bbox;
double tolerance = 0.0;
const int dim = curve.Dimension();
if ( (dim == 2 || dim==3) && curve.GetBoundingBox(bbox) )
{
iso = IsIsoparametric( bbox );
switch (iso) {
case x_iso:
case W_iso:
case E_iso:
// make sure curve is a (nearly) vertical line
// and weed out vertical scribbles
tolerance = bbox.m_max.x - bbox.m_min.x;
if ( tolerance < ON_ZERO_TOLERANCE && ON_ZERO_TOLERANCE*1024.0 <= (bbox.m_max.y-bbox.m_min.y) )
{
// 26 March 2007 Dale Lear
// If tolerance is tiny, then use ON_ZERO_TOLERANCE
// This fixes cases where iso curves where not getting
// the correct flag because tol=1e-16 and the closest
// point to line had calculation errors of 1e-15.
tolerance = ON_ZERO_TOLERANCE;
}
if ( !curve.IsLinear( tolerance ) )
iso = not_iso;
break;
case y_iso:
case S_iso:
case N_iso:
// make sure curve is a (nearly) horizontal line
// and weed out horizontal scribbles
tolerance = bbox.m_max.y - bbox.m_min.y;
if ( tolerance < ON_ZERO_TOLERANCE && ON_ZERO_TOLERANCE*1024.0 <= (bbox.m_max.x-bbox.m_min.x) )
{
// 26 March 2007 Dale Lear
// If tolerance is tiny, then use ON_ZERO_TOLERANCE
// This fixes cases where iso curves where not getting
// the correct flag because tol=1e-16 and the closest
// point to line had calculation errors of 1e-15.
tolerance = ON_ZERO_TOLERANCE;
}
if ( !curve.IsLinear( tolerance ) )
iso = not_iso;
break;
default:
// nothing here
break;
}
}
return iso;
}
ON_Surface::ISO
ON_Surface::IsIsoparametric( const ON_BoundingBox& bbox ) const
{
ISO iso = not_iso;
if ( bbox.m_min.z == bbox.m_max.z ) {
const double ds = bbox.m_max.x - bbox.m_min.x;
const double dt = bbox.m_max.y - bbox.m_min.y;
double a, b, s0, s1, t0, t1;
ON_Interval d = Domain(0);
s0 = d.Min();
s1 = d.Max();
d = Domain(1);
t0 = d.Min();
t1 = d.Max();
double stol = (s1-s0)/32.0;
double ttol = (t1-t0)/32.0;
if ( s0 < s1 && t0 < t1 && ( ds <= stol || dt <= ttol) )
{
if ( ds*(t1-t0) <= dt*(s1-s0) )
{
// check for s = constant iso
if ( bbox.m_max.x <= s0+stol )
{
// check for west side iso
GetParameterTolerance( 0, s0, &a, &b);
if ( a <= bbox.m_min.x && bbox.m_max.x <= b )
iso = W_iso;
}
else if ( bbox.m_min.x >= s1-stol )
{
// check for east side iso
GetParameterTolerance( 0, s1, &a, &b);
if ( a <= bbox.m_min.x && bbox.m_max.x <= b )
iso = E_iso;
}
if ( iso == not_iso && (s0 < bbox.m_max.x || bbox.m_min.x < s1) )
{
// check for interior "u = constant" iso
GetParameterTolerance( 0, 0.5*(bbox.m_min.x+bbox.m_max.x), &a, &b);
if ( a <= bbox.m_min.x && bbox.m_max.x <= b )
iso = x_iso;
}
}
else
{
// check for t = constant iso
if ( bbox.m_max.y <= t0+ttol )
{
// check for south side iso
GetParameterTolerance( 1, t0, &a, &b);
if ( a < bbox.m_min.y && bbox.m_max.y <= b )
iso = S_iso;
}
else if ( bbox.m_min.y >= t1-ttol )
{
// check for north side iso
GetParameterTolerance( 1, t1, &a, &b);
if ( a < bbox.m_min.y && bbox.m_max.y <= b )
iso = N_iso;
}
if ( iso == not_iso && (t0 < bbox.m_max.x || bbox.m_min.x < t1) )
{
// check for interior "t = constant" iso
GetParameterTolerance( 1, 0.5*(bbox.m_min.y+bbox.m_max.y), &a, &b);
if ( a < bbox.m_min.y && bbox.m_max.y <= b )
iso = y_iso;
}
}
}
}
return iso;
}
bool ON_Surface::IsPlanar( ON_Plane* plane, double tolerance ) const
{
return false;
}
bool
ON_Surface::IsClosed(int dir) const
{
ON_Interval d = Domain(dir);
if ( d.IsIncreasing() && Dimension() <= 3 ) {
const int span_count = SpanCount(dir?0:1);
const int span_degree = Degree(dir?0:1);
if ( span_count > 0 && span_degree > 0 )
{
ON_SimpleArray<double> s(span_count+1);
s.SetCount(span_count+1);
int n = 2*span_degree+1;
double delta = 1.0/n;
ON_3dPoint P(ON_3dPoint::Origin);
ON_3dPoint Q(ON_3dPoint::Origin);
int hintP[2] = {0,0};
int hintQ[2] = {0,0};
double *u0, *u1, *v0, *v1;
double t;
ON_Interval sp;
if ( dir ) {
v0 = &d.m_t[0];
v1 = &d.m_t[1];
u0 = &t;
u1 = &t;
}
else {
u0 = &d.m_t[0];
u1 = &d.m_t[1];
v0 = &t;
v1 = &t;
}
if ( GetSpanVector( dir?0:1, s.Array() ) )
{
int span_index, i;
for ( span_index = 0; span_index < span_count; span_index++ ) {
sp.Set(s[span_index],s[span_index+1]);
for ( i = 0; i < n; i++ ) {
t = sp.ParameterAt(i*delta);
if ( !Evaluate( *u0, *v0, 1, 3, P, 0, hintP ) )
return false;
if ( !Evaluate( *u1, *v1, 2, 3, Q, 0, hintQ ) )
return false;
if ( false == ON_PointsAreCoincident( 3, 0, &P.x, &Q.x ) )
return false;
}
}
return true;
}
}
}
return false;
}
bool ON_Surface::IsPeriodic(int dir) const
{
return false;
}
bool ON_Surface::GetNextDiscontinuity(
int dir,
ON::continuity c,
double t0,
double t1,
double* t,
int* hint,
int* dtype,
double cos_angle_tolerance,
double curvature_tolerance
) const
{
// 28 Jan 2005 - untested code
// this function must be overridden by surface objects that
// can have parametric discontinuities on the interior of the curve.
bool rc = false;
// using tmp_dtype means I don't have to keep checking for a null dtype;
int tmp_dtype = 0;
if ( !dtype )
dtype = &tmp_dtype;
*dtype = 0;
if ( t0 != t1 )
{
bool bTestC0 = false;
bool bTestD1 = false;
bool bTestD2 = false;
bool bTestT = false;
bool bTestK = false;
switch(c)
{
case ON::continuity::C0_locus_continuous:
bTestC0 = true;
break;
case ON::continuity::C1_locus_continuous:
bTestC0 = true;
bTestD1 = true;
break;
case ON::continuity::C2_locus_continuous:
bTestC0 = true;
bTestD1 = true;
bTestD2 = true;
break;
case ON::continuity::G1_locus_continuous:
bTestC0 = true;
bTestT = true;
break;
case ON::continuity::G2_locus_continuous:
bTestC0 = true;
bTestT = true;
bTestK = true;
break;
default:
// other values ignored on purpose.
break;
}
if ( bTestC0 )
{
// 20 March 2003 Dale Lear:
// Have to look for locus discontinuities at ends.
// Must test both ends becuase t0 > t1 is valid input.
// In particular, for ON_CurveProxy::GetNextDiscontinuity()
// to work correctly on reversed "real" curves, the
// t0 > t1 must work right.
int hinta[2], hintb[2], span_index, j;
ON_Interval domain = Domain(dir);
ON_Interval span_domain;
ON_2dPoint st0, st1;
ON_3dVector Da[6], Db[6];
ON_3dVector& D1a = Da[1+dir];
ON_3dVector& D1b = Db[1+dir];
ON_3dVector& D2a = Da[3+2*dir];
ON_3dVector& D2b = Db[3+2*dir];
if ( t0 < domain[1] && t1 >= domain[1] )
t1 = domain[1];
else if ( t0 > domain[0] && t1 <= domain[0] )
t1 = domain[0];
if ( (t0 < domain[1] && t1 >= domain[1]) || (t0 > domain[0] && t1 <= domain[0]) )
{
if ( IsClosed(dir) )
{
int span_count = SpanCount(1-dir);
double* span_vector = (span_count>0) ? ((double*)onmalloc((span_count+1)*sizeof(*span_vector))) : 0;
if (!GetSpanVector(1-dir,span_vector))
span_count = 0;
st0[dir] = domain[0];
st1[dir] = domain[1];
for ( span_index = 0; span_index < span_count && 1 != *dtype; span_index++ )
{
span_domain.Set(span_vector[span_index],span_vector[span_index+1]);
for ( j = (span_index?1:0); j <= 2 && 1 != *dtype; j++ )
{
st0[1-dir] = span_domain.ParameterAt(0.5*j);
st1[1-dir] = st0[1-dir];
if ( bTestD1 || bTestT )
{
// need to check locus continuity at start/end of closed surface.
if ( Evaluate(st0.x,st0.y,2,3,&Da[0].x,1,hinta)
&& Evaluate(st1.x,st1.y,2,3,&Db[0].x,2,hintb) )
{
if ( bTestD1 )
{
if ( !(D1a-D1b).IsTiny(D1b.MaximumCoordinate()*ON_SQRT_EPSILON ) )
{
if ( dtype )
*dtype = 1;
*t = t1;
rc = true;
}
else if ( bTestD2 && !(D2a-D2b).IsTiny(D2b.MaximumCoordinate()*ON_SQRT_EPSILON) )
{
if ( dtype )
*dtype = 2;
*t = t1;
rc = true;
}
}
else if ( bTestT )
{
ON_3dVector Ta, Tb, Ka, Kb;
ON_EvCurvature( D1a, D2a, Ta, Ka );
ON_EvCurvature( D1b, D2b, Tb, Kb );
if ( Ta*Tb < cos_angle_tolerance )
{
if ( dtype )
*dtype = 1;
*t = t1;
rc = true;
}
else if ( bTestK && (Ka-Kb).Length() > curvature_tolerance )
{
if ( dtype )
*dtype = 2;
*t = t1;
rc = true;
}
}
}
}
}
}
if ( span_vector)
{
onfree(span_vector);
}
}
else
{
// open curves are not locus continuous at ends.
*dtype = 0; // locus C0 discontinuity
*t = t1;
rc = true;
}
}
}
}
return rc;
}
void ON_IsG1Closed(const ON_Surface& Srf, bool closed[2])
{
ON_Interval dom[2];
double t;
dom[0] = Srf.Domain(0);
dom[1] = Srf.Domain(1);
closed[0] = Srf.IsClosed(0) && !Srf.GetNextDiscontinuity(0, ON::continuity::G1_locus_continuous, dom[0][0], dom[0][1], &t);
closed[1] = Srf.IsClosed(1) && !Srf.GetNextDiscontinuity(1, ON::continuity::G1_locus_continuous, dom[1][0], dom[1][1], &t);
}
static
bool PrincipalCurvaturesAreContinuous(
bool bSmoothTest,
double k1a, double k2a, // side "a" principal curvatures
double k1b, double k2b, // side "b" principal curvatures
double curvature_tolerance
)
{
ON_3dVector K[2];
K[0].x = k1a;
K[0].y = 0.0;
K[0].z = 0.0;
K[1].x = k1b;
K[1].y = 0.0;
K[1].z = 0.0;
// compare the first principal curvatures
bool rc = ( bSmoothTest )
? ON_IsGsmoothCurvatureContinuous(K[0],K[1],0.0,curvature_tolerance)
: ON_IsG2CurvatureContinuous(K[0],K[1],0.0,curvature_tolerance);
if ( rc )
{
// compare the second principal curvatures
K[0].x = k2a;
K[1].x = k2b;
rc = ( bSmoothTest )
? ON_IsGsmoothCurvatureContinuous(K[0],K[1],0.0,curvature_tolerance)
: ON_IsG2CurvatureContinuous(K[0],K[1],0.0,curvature_tolerance);
}
return rc;
}
bool ON_Surface::IsContinuous(
ON::continuity desired_continuity,
double s,
double t,
int* hint, // default = nullptr,
double point_tolerance, // default=ON_ZERO_TOLERANCE
double d1_tolerance, // default==ON_ZERO_TOLERANCE
double d2_tolerance, // default==ON_ZERO_TOLERANCE
double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE
double curvature_tolerance // default==ON_SQRT_EPSILON
) const
{
int qi, span_count[2];
span_count[0] = SpanCount(0);
span_count[1] = SpanCount(1);
if ( span_count[0] <= 1 && span_count[1] <= 1 )
return true;
ON_3dPoint P[4];
ON_3dVector Ds[4], Dt[4], Dss[4], Dst[4], Dtt[4], N[4], K1[4], K2[4];
double gauss[4], mean[4], kappa1[4], kappa2[4], sq[4], tq[4];
switch ( desired_continuity )
{
case ON::continuity::C0_locus_continuous:
case ON::continuity::C1_locus_continuous:
case ON::continuity::C2_locus_continuous:
case ON::continuity::G1_locus_continuous:
case ON::continuity::G2_locus_continuous:
{
// 7 April 2005 Dale Lear
// This locus continuity test was added. Prior to
// this time, this function ignored the word "locus".
// The reason for the change is that Chuck's filleting code
// needs to query the continuity at the seams of closed surfaces.
// See ON::continuity comments. The different sq[] values
// must NOT be used when s == Domain(0)[0] and must always
// be used when s == Domain(0)[1]. In particular, if a surface
// is not closed in the "s" direction and s == Domain(1)[1], then
// the answer to any locus query is false.
ON_Interval d = Domain(0);
if ( s == d[1] )
{
sq[0] = sq[1] = d[0];
sq[2] = sq[3] = d[1];
}
else
{
sq[0] = sq[1] = sq[2] = sq[3] = s;
}
d = Domain(1);
// See ON::continuity comments. The different tq[] values
// must NOT be used when t == Domain(1)[0] and must always
// be used when t == Domain(1)[1]. In particular, if a surface
// is not closed in the "t" direction and t == Domain(1)[1], then
// the answer to any locus query is false.
if ( t == d[1] )
{
tq[0] = tq[3] = d[0];
tq[1] = tq[2] = d[1];
}
else
{
tq[0] = tq[1] = tq[2] = tq[3] = t;
}
}
break;
default:
sq[0] = sq[1] = sq[2] = sq[3] = s;
tq[0] = tq[1] = tq[2] = tq[3] = t;
break;
}
desired_continuity = ON::ParametricContinuity((int)desired_continuity);
// this is slow and uses evaluation
// virtual overrides on curve classes that can have multiple spans
// are much faster because the avoid evaluation
switch ( desired_continuity )
{
case ON::continuity::C0_continuous:
for ( qi = 0; qi < 4; qi++ )
{
if ( !EvPoint( sq[qi], tq[qi], P[qi], qi+1 ) )
return false;
if ( qi )
{
if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) )
return false;
}
}
if ( !(P[3]-P[0]).IsTiny(point_tolerance) )
return false;
break;
case ON::continuity::C1_continuous:
for ( qi = 0; qi < 4; qi++ )
{
if ( !Ev1Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi], qi+1, hint ) )
return false;
if ( qi )
{
if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) )
return false;
if ( !(Ds[qi]-Ds[qi-1]).IsTiny(d1_tolerance) )
return false;
if ( !(Dt[qi]-Dt[qi-1]).IsTiny(d1_tolerance) )
return false;
}
}
if ( !(P[3]-P[0]).IsTiny(point_tolerance) )
return false;
if ( !(Ds[3]-Ds[0]).IsTiny(d1_tolerance) )
return false;
if ( !(Dt[3]-Dt[0]).IsTiny(d1_tolerance) )
return false;
break;
case ON::continuity::C2_continuous:
for ( qi = 0; qi < 4; qi++ )
{
if ( !Ev2Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi],
Dss[qi], Dst[qi], Dtt[qi],
qi+1, hint ) )
return false;
if ( qi )
{
if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) )
return false;
if ( !(Ds[qi]-Ds[qi-1]).IsTiny(d1_tolerance) )
return false;
if ( !(Dt[qi]-Dt[qi-1]).IsTiny(d1_tolerance) )
return false;
if ( !(Dss[qi]-Dss[qi-1]).IsTiny(d2_tolerance) )
return false;
if ( !(Dst[qi]-Dst[qi-1]).IsTiny(d2_tolerance) )
return false;
if ( !(Dtt[qi]-Dtt[qi-1]).IsTiny(d2_tolerance) )
return false;
}
}
if ( !(P[3]-P[0]).IsTiny(point_tolerance) )
return false;
if ( !(Ds[3]-Ds[0]).IsTiny(d1_tolerance) )
return false;
if ( !(Dt[3]-Dt[0]).IsTiny(d1_tolerance) )
return false;
if ( !(Dss[3]-Dss[0]).IsTiny(d2_tolerance) )
return false;
if ( !(Dst[3]-Dst[0]).IsTiny(d2_tolerance) )
return false;
if ( !(Dtt[3]-Dtt[0]).IsTiny(d2_tolerance) )
return false;
break;
case ON::continuity::G1_continuous:
for ( qi = 0; qi < 4; qi++ )
{
if ( !EvNormal( sq[qi], tq[qi], P[qi], N[qi], qi+1 ) )
return false;
if ( qi )
{
if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) )
return false;
if ( N[qi]*N[qi-1] < cos_angle_tolerance )
return false;
}
}
if ( !(P[3]-P[0]).IsTiny(point_tolerance) )
return false;
if ( N[3]*N[0] < cos_angle_tolerance )
return false;
break;
case ON::continuity::G2_continuous:
case ON::continuity::Gsmooth_continuous:
{
bool bSmoothCon = (ON::continuity::Gsmooth_continuous == desired_continuity);
for ( qi = 0; qi < 4; qi++ )
{
if ( !Ev2Der( sq[qi], tq[qi], P[qi], Ds[qi], Dt[qi],
Dss[qi], Dst[qi], Dtt[qi],
qi+1, hint ) )
return false;
if (!ON_EvNormal(qi+1, Ds[qi], Dt[qi], Dss[qi], Dst[qi], Dtt[qi], N[qi]))
return false;
ON_EvPrincipalCurvatures( Ds[qi], Dt[qi], Dss[qi], Dst[qi], Dtt[qi], N[qi],
&gauss[qi], &mean[qi], &kappa1[qi], &kappa2[qi],
K1[qi], K2[qi] );
if ( qi )
{
if ( !(P[qi]-P[qi-1]).IsTiny(point_tolerance) )
return false;
if ( N[qi]*N[qi-1] < cos_angle_tolerance )
return false;
if ( !PrincipalCurvaturesAreContinuous(bSmoothCon,kappa1[qi],kappa2[qi],kappa1[qi-1],kappa2[qi-1],curvature_tolerance) )
return false;
}
}
if ( !(P[3]-P[0]).IsTiny(point_tolerance) )
return false;
if ( N[3]*N[0] < cos_angle_tolerance )
return false;
if ( !PrincipalCurvaturesAreContinuous(bSmoothCon,kappa1[3],kappa2[3],kappa1[0],kappa2[0],curvature_tolerance) )
return false;
}
break;
default:
// intentionally ignoring other ON::continuity enum values
break;
}
return true;
}
bool
ON_Surface::IsSingular(int side) const
{
return false;
}
bool ON_Surface::IsSolid() const
{
const bool bIsClosed0 = ( IsClosed(0) || ( IsSingular(1) && IsSingular(3) ) );
const bool bIsClosed1 = ( IsClosed(1) || ( IsSingular(0) && IsSingular(2) ) );
if ( bIsClosed0 && bIsClosed1 )
return true;
const ON_Extrusion* extrusion = ON_Extrusion::Cast(this);
if ( 0 != extrusion && extrusion->IsSolid() )
return true;
return false;
}
bool
ON_Surface::IsAtSingularity(double s, double t,
bool bExact //true by default
) const
{
if (bExact){
if (s == Domain(0)[0]){
if (IsSingular(3))
return true;
}
else if (s == Domain(0)[1]){
if (IsSingular(1))
return true;
}
if (t == Domain(1)[0]){
if (IsSingular(0))
return true;
}
else if (t == Domain(1)[1]){
if (IsSingular(2))
return true;
}
return false;
}
if (IsAtSingularity(s, t, true))
return true;
bool bCheckPartials[2] = {false, false};
int i;
double m[2];
for (i=0; i<2; i++)
m[i] = Domain(i).Mid();
if (s < m[0]){
if (IsSingular(3))
bCheckPartials[1] = true;
}
else {
if (IsSingular(1))
bCheckPartials[1] = true;
}
if (!bCheckPartials[0] && !bCheckPartials[1]){
if (t < m[1]){
if (IsSingular(0))
bCheckPartials[0] = true;
}
else {
if (IsSingular(2))
bCheckPartials[0] = true;
}
}
if (!bCheckPartials[0] && !bCheckPartials[1])
return false;
ON_3dPoint P;
ON_3dVector M[2], S[2];
if (!Ev1Der(s, t, P, S[0], S[1]))
return false;
if (!Ev1Der(m[0], m[1], P, M[0], M[1]))
return false;
for (i=0; i<2; i++){
if (!bCheckPartials[i])
continue;
if (S[i].Length() < 1.0e-6 * M[i].Length())
return true;
}
return false;
}
int ON_Surface::IsAtSeam(double s, double t) const
{
int rc = 0;
int i;
for (i=0; i<2; i++){
if (!IsClosed(i))
continue;
double p = (i) ? t : s;
if (p == Domain(i)[0] || p == Domain(i)[1])
rc += (i+1);
}
return rc;
}
ON_3dPoint
ON_Surface::PointAt( double s, double t ) const
{
ON_3dPoint p(0.0,0.0,0.0);
EvPoint( s, t, p );
return p;
}
ON_3dVector
ON_Surface::NormalAt( double s, double t ) const
{
ON_3dVector N(0.0,0.0,0.0);
EvNormal( s, t, N );
return N;
}
bool ON_Surface::FrameAt( double u, double v, ON_Plane& frame) const
{
bool rc = false;
ON_3dPoint origin;
ON_3dVector udir, vdir, normal;
if( EvNormal( u, v, origin, udir, vdir, normal))
{
if ( udir.Unitize() )
vdir = ON_CrossProduct( normal, udir);
else if ( vdir.Unitize() )
udir = ON_CrossProduct( vdir, normal);
frame.CreateFromFrame( origin, udir, vdir);
rc = frame.IsValid();
}
return rc;
}
bool
ON_Surface::EvPoint( // returns false if unable to evaluate
double s, double t, // evaluation parameters
ON_3dPoint& point,
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) 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( s, t, 0, Dimension(), v, side, hint );
if ( Dimension() > 3 ) {
point.x = v[0];
point.y = v[1];
point.z = v[2];
if ( Dimension() > 128 )
onfree(v);
}
return rc;
}
bool
ON_Surface::Ev1Der( // returns false if unable to evaluate
double s, double t, // evaluation parameters
ON_3dPoint& point,
ON_3dVector& ds,
ON_3dVector& dt,
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) const
{
bool rc = false;
const int dim = Dimension();
double ws[3*32];
double* v;
point.x = 0.0;
point.y = 0.0;
point.z = 0.0;
ds.x = 0.0;
ds.y = 0.0;
ds.z = 0.0;
dt.x = 0.0;
dt.y = 0.0;
dt.z = 0.0;
if ( dim <= 32 ) {
v = ws;
}
else {
v = (double*)onmalloc(3*dim*sizeof(*v));
}
rc = Evaluate( s, t, 1, dim, v, side, hint );
point.x = v[0];
ds.x = v[dim];
dt.x = v[2*dim];
if ( dim > 1 ) {
point.y = v[1];
ds.y = v[dim+1];
dt.y = v[2*dim+1];
if ( dim > 2 ) {
point.z = v[2];
ds.z = v[dim+2];
dt.z = v[2*dim+2];
if ( dim > 32 )
onfree(v);
}
}
return rc;
}
bool
ON_Surface::Ev2Der( // returns false if unable to evaluate
double s, double t, // evaluation parameters
ON_3dPoint& point,
ON_3dVector& ds,
ON_3dVector& dt,
ON_3dVector& dss,
ON_3dVector& dst,
ON_3dVector& dtt,
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) const
{
bool rc = false;
const int dim = Dimension();
double ws[6*16];
double* v;
point.x = 0.0;
point.y = 0.0;
point.z = 0.0;
ds.x = 0.0;
ds.y = 0.0;
ds.z = 0.0;
dt.x = 0.0;
dt.y = 0.0;
dt.z = 0.0;
dss.x = 0.0;
dss.y = 0.0;
dss.z = 0.0;
dst.x = 0.0;
dst.y = 0.0;
dst.z = 0.0;
dtt.x = 0.0;
dtt.y = 0.0;
dtt.z = 0.0;
if ( dim <= 16 ) {
v = ws;
}
else {
v = (double*)onmalloc(6*dim*sizeof(*v));
}
rc = Evaluate( s, t, 2, dim, v, side, hint );
point.x = v[0];
ds.x = v[dim];
dt.x = v[2*dim];
dss.x = v[3*dim];
dst.x = v[4*dim];
dtt.x = v[5*dim];
if ( dim > 1 ) {
point.y = v[1];
ds.y = v[dim+1];
dt.y = v[2*dim+1];
dss.y = v[3*dim+1];
dst.y = v[4*dim+1];
dtt.y = v[5*dim+1];
if ( dim > 2 ) {
point.z = v[2];
ds.z = v[dim+2];
dt.z = v[2*dim+2];
dss.z = v[3*dim+2];
dst.z = v[4*dim+2];
dtt.z = v[5*dim+2];
if ( dim > 16 )
onfree(v);
}
}
return rc;
}
bool
ON_Surface::EvNormal( // returns false if unable to evaluate
double s, double t, // evaluation parameters (s,t)
ON_3dVector& normal, // unit normal
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) const
{
ON_3dPoint point;
ON_3dVector ds, dt;
return EvNormal( s, t, point, ds, dt, normal, side, hint );
}
bool
ON_Surface::EvNormal( // returns false if unable to evaluate
double s, double t, // evaluation parameters (s,t)
ON_3dPoint& point, // returns value of surface
ON_3dVector& normal, // unit normal
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) const
{
ON_3dVector ds, dt;
return EvNormal( s, t, point, ds, dt, normal, side, hint );
}
bool
ON_Surface::EvNormal( // returns false if unable to evaluate
double s, double t, // evaluation parameters (s,t)
ON_3dPoint& point, // returns value of surface
ON_3dVector& ds, // first partial derivatives (Ds)
ON_3dVector& dt, // (Dt)
ON_3dVector& normal, // unit normal
int side, // optional - determines which side to evaluate from
// 0 = default
// 1 from NE quadrant
// 2 from NW quadrant
// 3 from SW quadrant
// 4 from SE quadrant
int* hint // optional - evaluation hint (int[2]) used to speed
// repeated evaluations
) const
{
// simple cross product normal - override to support singular surfaces
bool rc = Ev1Der( s, t, point, ds, dt, side, hint );
if ( rc ) {
const double len_ds = ds.Length();
const double len_dt = dt.Length();
// do not reduce the tolerance used here - there is a retry in the code
// below.
//17 Oct 2022 - Chuck - Added test for parallel partials.
if ( len_ds > ON_SQRT_EPSILON*len_dt && len_dt > ON_SQRT_EPSILON*len_ds &&
ds.IsParallelTo(dt, 0.01*ON_DEFAULT_ANGLE_TOLERANCE) == 0)
{
ON_3dVector a = ds/len_ds;
ON_3dVector b = dt/len_dt;
normal = ON_CrossProduct( a, b );
rc = normal.Unitize();
}
else
{
// see if we have a singular point
ON_3dVector v[6];
int normal_side = side;
bool bOnSide = false;
ON_Interval sdom = Domain(0);
ON_Interval tdom = Domain(1);
if (s == sdom.Min()) {
normal_side = (normal_side >= 3) ? 4 : 1;
bOnSide = true;
}
else if (s == sdom.Max()) {
normal_side = (normal_side >= 3) ? 3 : 2;
bOnSide = true;
}
if (t == tdom.Min()) {
normal_side = (normal_side == 2 || normal_side == 3) ? 2 : 1;
bOnSide = true;
}
else if (t == tdom.Max()) {
normal_side = (normal_side == 2 || normal_side == 3) ? 3 : 4;
bOnSide = true;
}
if ( !bOnSide )
{
// 2004 November 11 Dale Lear
// Added a retry again with a more generous tolerance
if ( len_ds > ON_EPSILON*len_dt && len_dt > ON_EPSILON*len_ds )
{
ON_3dVector a = ds/len_ds;
ON_3dVector b = dt/len_dt;
normal = ON_CrossProduct( a, b );
rc = normal.Unitize();
}
else
{
rc = false;
}
}
else {
rc = Evaluate( s, t, 2, 3, &v[0].x, normal_side, hint );
if ( rc ) {
rc = ON_EvNormal( normal_side, v[1], v[2], v[3], v[4], v[5], normal);
}
}
}
}
if ( !rc )
{
normal = ON_3dVector::ZeroVector;
}
return rc;
}
//virtual
ON_Curve* ON_Surface::IsoCurve(
int dir, // 0 first parameter varies and second parameter is constant
// e.g., point on IsoCurve(0,c) at t is srf(t,c) - Horizontal
// 1 first parameter is constant and second parameter varies
// e.g., point on IsoCurve(1,c) at t is srf(c,t) - Vertical
double c // value of constant parameter
) const
{
return nullptr;
}
//virtual
bool ON_Surface::Trim(
int dir,
const ON_Interval& domain
)
{
return false;
}
//virtual
bool ON_Surface::Extend(
int dir,
const ON_Interval& domain
)
{
return false;
}
//virtual
bool ON_Surface::Split(
int dir,
double c,
ON_Surface*& west_or_south_side,
ON_Surface*& east_or_north_side
) const
{
return false;
}
// virtual
int ON_Surface::GetNurbForm(
ON_NurbsSurface& nurbs_surface,
double tolerance
) const
{
return 0;
}
// virtual
int ON_Surface::HasNurbForm( ) const
{
return 0;
}
bool ON_Surface::GetSurfaceParameterFromNurbFormParameter(
double nurbs_s, double nurbs_t,
double* surface_s, double* surface_t
) const
{
// NOTE: ON_SumSurface and ON_RevSurface override this virtual function
*surface_s = nurbs_s;
*surface_t = nurbs_t;
return true;
}
bool ON_Surface::GetNurbFormParameterFromSurfaceParameter(
double surface_s, double surface_t,
double* nurbs_s, double* nurbs_t
) const
{
// NOTE: ON_SumSurface and ON_RevSurface override this virtual function
*nurbs_s = surface_s;
*nurbs_t = surface_t;
return true;
}
ON_NurbsSurface* ON_Surface::NurbsSurface(
ON_NurbsSurface* pNurbsSurface,
double tolerance,
const ON_Interval* s_subdomain,
const ON_Interval* t_subdomain
) const
{
ON_NurbsSurface* nurbs_surface = pNurbsSurface;
if ( !nurbs_surface )
nurbs_surface = new ON_NurbsSurface();
int rc = GetNurbForm( *nurbs_surface, tolerance );
if ( !rc )
{
if (!pNurbsSurface)
delete nurbs_surface;
nurbs_surface = nullptr;
}
return nurbs_surface;
}
ON_SurfaceArray::ON_SurfaceArray( int initial_capacity )
: ON_SimpleArray<ON_Surface*>(initial_capacity)
{}
ON_SurfaceArray::~ON_SurfaceArray()
{
Destroy();
}
void ON_SurfaceArray::Destroy()
{
int i = m_capacity;
while ( i-- > 0 ) {
if ( m_a[i] ) {
delete m_a[i];
m_a[i] = nullptr;
}
}
Empty();
}
bool ON_SurfaceArray::Duplicate( ON_SurfaceArray& dst ) const
{
dst.Destroy();
dst.SetCapacity( Capacity() );
const int count = Count();
int i;
ON_Surface* surface;
for ( i = 0; i < count; i++ )
{
surface = 0;
if ( m_a[i] )
{
surface = m_a[i]->Duplicate();
}
dst.Append(surface);
}
return true;
}
bool ON_SurfaceArray::Write( ON_BinaryArchive& file ) const
{
bool rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
if (rc) rc = file.Write3dmChunkVersion(1,0);
if (rc )
{
int i;
rc = file.WriteInt( Count() );
for ( i = 0; rc && i < Count(); i++ ) {
if ( m_a[i] )
{
rc = file.WriteInt(1);
if ( rc )
rc = file.WriteObject( *m_a[i] ); // polymorphic surfaces
}
else
{
// nullptr surface
rc = file.WriteInt(0);
}
}
if ( !file.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
bool ON_SurfaceArray::Read( ON_BinaryArchive& file )
{
int major_version = 0;
int minor_version = 0;
ON__UINT32 tcode = 0;
ON__INT64 big_value = 0;
int flag;
Destroy();
bool rc = file.BeginRead3dmBigChunk( &tcode, &big_value );
if (rc)
{
rc = ( tcode == TCODE_ANONYMOUS_CHUNK );
if (rc) rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc && major_version == 1) {
ON_Object* p;
int count = 0;
rc = file.ReadInt( &count );
if (rc && count < 0)
rc = false;
if (rc) {
SetCapacity(count);
SetCount(count);
Zero();
int i;
for ( i = 0; rc && i < count && rc; i++ ) {
flag = 0;
rc = file.ReadInt(&flag);
if (rc && flag==1) {
p = 0;
rc = file.ReadObject( &p ); // polymorphic surfaces
m_a[i] = ON_Surface::Cast(p);
if ( !m_a[i] )
delete p;
}
}
}
}
else {
rc = false;
}
if ( !file.EndRead3dmChunk() )
rc = false;
}
return rc;
}
bool ON_Surface::HasBrepForm() const
{
return true;
}
ON_Brep* ON_Surface::BrepForm( ON_Brep* brep ) const
{
ON_Brep* pBrep = nullptr;
if ( brep )
brep->Destroy();
// 26 August 2008 Dale Lear - fixed bug
// When this function was called on an ON_SurfaceProxy
//ON_Surface* pSurface = Duplicate();
ON_Surface* pSurface = DuplicateSurface();
if ( pSurface )
{
if ( brep )
pBrep = brep;
else
pBrep = new ON_Brep();
if ( !pBrep->Create(pSurface) )
{
if ( pSurface )
{
delete pSurface;
pSurface = nullptr;
}
if ( !brep )
delete pBrep;
pBrep = nullptr;
}
}
return pBrep;
}
void ON_Surface::DestroySurfaceTree()
{
DestroyRuntimeCache(true);
}
ON_SurfaceProperties::ON_SurfaceProperties()
{
memset(this,0,sizeof(*this));
}
void ON_SurfaceProperties::Set( const ON_Surface* surface )
{
m_surface = surface;
if ( 0 == m_surface )
{
m_bIsSet = false;
m_bHasSingularity = false;
m_bIsSingular[0] = false;
m_bIsSingular[1] = false;
m_bIsSingular[2] = false;
m_bIsSingular[3] = false;
m_bHasSeam = false;
m_bIsClosed[0] = false;
m_bIsClosed[1] = false;
m_domain[0].Set(0.0,0.0);
m_domain[1].Set(0.0,0.0);
}
else
{
m_bIsSet = true;
m_bIsSingular[0] = m_surface->IsSingular(0)?true:false;
m_bIsSingular[1] = m_surface->IsSingular(1)?true:false;
m_bIsSingular[2] = m_surface->IsSingular(2)?true:false;
m_bIsSingular[3] = m_surface->IsSingular(3)?true:false;
m_bHasSingularity = (m_bIsSingular[0] || m_bIsSingular[1] || m_bIsSingular[2] || m_bIsSingular[3]);
m_bIsClosed[0] = m_surface->IsClosed(0)?true:false;
m_bIsClosed[1] = m_surface->IsClosed(1)?true:false;
m_bHasSeam = (m_bIsClosed[0] || m_bIsClosed[1]);
m_domain[0] = m_surface->Domain(0);
m_domain[1] = m_surface->Domain(1);
}
}