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

1271 lines
34 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_OBJECT_IMPLEMENT(ON_CurveProxy,ON_Curve,"4ED7D4D9-E947-11d3-BFE5-0010830122F0");
ON_CurveProxy::ON_CurveProxy() ON_NOEXCEPT
: m_real_curve(0)
, m_bReversed(false)
{}
ON_CurveProxy::~ON_CurveProxy()
{
m_real_curve = 0;
}
ON_CurveProxy::ON_CurveProxy( const ON_CurveProxy& src )
: ON_Curve(src)
, m_real_curve(src.m_real_curve)
, m_bReversed(src.m_bReversed)
, m_real_curve_domain(src.m_real_curve_domain)
, m_this_domain(src.m_this_domain)
{}
ON_CurveProxy& ON_CurveProxy::operator=( const ON_CurveProxy& src )
{
if ( this != &src )
{
ON_Curve::operator=(src);
m_real_curve = src.m_real_curve;
m_bReversed = src.m_bReversed;
m_real_curve_domain = src.m_real_curve_domain;
m_this_domain = src.m_this_domain;
}
return *this;
}
#if defined(ON_HAS_RVALUEREF)
ON_CurveProxy::ON_CurveProxy( ON_CurveProxy&& src) ON_NOEXCEPT
: ON_Curve(std::move(src))
, m_real_curve(src.m_real_curve)
, m_bReversed(src.m_bReversed)
, m_real_curve_domain(src.m_real_curve_domain)
, m_this_domain(src.m_this_domain)
{
src.m_real_curve = 0;
}
ON_CurveProxy& ON_CurveProxy::operator=( ON_CurveProxy&& src)
{
if ( this != &src )
{
ON_Curve::operator=(std::move(src));
m_real_curve = src.m_real_curve;
m_bReversed = src.m_bReversed;
m_real_curve_domain = src.m_real_curve_domain;
m_this_domain = src.m_this_domain;
src.m_real_curve = 0;
}
return *this;
}
#endif
ON_CurveProxy::ON_CurveProxy( const ON_Curve* c )
: m_real_curve(c), m_bReversed(0)
{
if ( m_real_curve )
m_real_curve_domain =m_this_domain = m_real_curve->Domain();
}
ON_CurveProxy::ON_CurveProxy( const ON_Curve* c, ON_Interval domain )
: m_real_curve(c),
m_bReversed(0),
m_real_curve_domain(domain),
m_this_domain(domain)
{
}
unsigned int ON_CurveProxy::SizeOf() const
{
unsigned int sz = ON_Curve::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Curve));
// Do not add in size of m_real_curve - its memory is not
// managed by this class.
return sz;
}
ON__UINT32 ON_CurveProxy::DataCRC(ON__UINT32 current_remainder) const
{
if ( m_real_curve )
current_remainder = m_real_curve->DataCRC(current_remainder);
current_remainder = ON_CRC32(current_remainder,sizeof(m_bReversed),&m_bReversed);
current_remainder = ON_CRC32(current_remainder,sizeof(m_real_curve_domain),&m_real_curve_domain);
current_remainder = ON_CRC32(current_remainder,sizeof(m_this_domain),&m_this_domain);
return current_remainder;
}
double ON_CurveProxy::RealCurveParameter( double t ) const
{
// change a "this" curve parameter into an m_real_curve parameter
double s;
if ( m_bReversed || m_real_curve_domain != m_this_domain )
{
s = m_this_domain.NormalizedParameterAt(t);
if (m_bReversed)
s = 1.0 - s;
t = m_real_curve_domain.ParameterAt(s);
}
return t;
}
double ON_CurveProxy::ThisCurveParameter( double real_curve_parameter ) const
{
// change an m_real_curve curve parameter into a "this" parameter
double s;
double t = real_curve_parameter;
if ( m_bReversed || m_real_curve_domain != m_this_domain )
{
s = m_real_curve_domain.NormalizedParameterAt(real_curve_parameter);
if (m_bReversed)
s = 1.0 - s;
t = m_this_domain.ParameterAt(s);
}
return t;
}
ON_Interval ON_CurveProxy::RealCurveInterval( const ON_Interval* sub_domain ) const
{
if ( !sub_domain )
return m_real_curve_domain;
ON_Interval d = m_this_domain;
d.Intersection(*sub_domain);
double t0 = RealCurveParameter( d[m_bReversed?1:0] );
double t1 = RealCurveParameter( d[m_bReversed?0:1] );
return ON_Interval(t0,t1);
}
bool ON_CurveProxy::ProxyCurveIsReversed() const
{
return m_bReversed;
}
void ON_CurveProxy::SetProxyCurveIsReversed(bool bReversed)
{
m_bReversed = bReversed;
}
void ON_CurveProxy::SetProxyCurve( const ON_Curve* real_curve )
{
// setting m_real_curve=0 prevents crashes if user has deleted
// the "real" curve before calling SetProxyCurve().
m_real_curve = 0;
if ( real_curve )
SetProxyCurve( real_curve, real_curve->Domain() );
else
{
DestroyCurveTree();
m_real_curve_domain = ON_Interval::EmptyInterval;
m_this_domain = ON_Interval::EmptyInterval;
m_bReversed = false;
}
}
void ON_CurveProxy::SetProxyCurve( const ON_Curve* real_curve,
ON_Interval real_curve_subdomain)
{
if ( real_curve != this )
{
// setting m_real_curve=0 prevents crashes if user has deleted
// the "real" curve before calling SetProxyCurve().
m_real_curve = 0;
DestroyCurveTree();
m_real_curve_domain = ON_Interval::EmptyInterval;
m_this_domain = ON_Interval::EmptyInterval;
m_bReversed = false;
}
else
{
// If you are debugging and end up here, there is a 99% chance
// that you passed the wrong pointer to SetProxyCurve().
// However, I will assume you really meant to use a fancy self
// reference to adjust domains.
if ( IsValid() && m_this_domain.Includes(real_curve_subdomain) )
{
real_curve = m_real_curve;
// because input real_curve_subdomain was with respect to "this".
double r0 = RealCurveParameter(real_curve_subdomain[0]);
double r1 = RealCurveParameter(real_curve_subdomain[1]);
real_curve_subdomain.Set(r0,r1);
}
else
{
real_curve = 0;
}
// setting m_real_curve=0 prevents crashes if user has deleted
// the "real" curve before calling SetProxyCurve().
m_real_curve = 0;
DestroyCurveTree();
}
m_real_curve = real_curve;
if ( m_real_curve )
{
SetProxyCurveDomain( real_curve_subdomain );
}
else
{
m_real_curve_domain = real_curve_subdomain;
}
m_this_domain = m_real_curve_domain;
}
const ON_Curve* ON_CurveProxy::ProxyCurve() const
{
return m_real_curve;
}
bool ON_CurveProxy::SetProxyCurveDomain( ON_Interval proxy_curve_subdomain )
{
DestroyCurveTree();
bool rc = proxy_curve_subdomain.IsIncreasing();
if ( rc )
{
if ( m_real_curve )
{
ON_Interval cdom = m_real_curve->Domain();
cdom.Intersection( proxy_curve_subdomain );
rc = cdom.IsIncreasing();
if (rc )
m_real_curve_domain = cdom;
}
else
{
m_real_curve_domain = proxy_curve_subdomain;
}
}
return rc;
}
ON_Interval ON_CurveProxy::ProxyCurveDomain() const
{
return m_real_curve_domain;
}
ON_Curve* ON_CurveProxy::DuplicateCurve() const
{
// duplicate underlying curve
ON_Curve* dup_crv = 0;
if ( m_real_curve && m_real_curve != this )
{
dup_crv = m_real_curve->DuplicateCurve();
if ( dup_crv )
{
dup_crv->Trim(m_real_curve_domain);
if( m_bReversed )
dup_crv->Reverse();
dup_crv->SetDomain( m_this_domain );
}
}
return dup_crv;
}
bool ON_CurveProxy::IsValid( ON_TextLog* text_log ) const
{
bool rc = ( m_real_curve ) ? m_real_curve->IsValid(text_log) : false;
if ( rc && !m_real_curve_domain.IsIncreasing() )
{
rc = false;
if ( text_log)
text_log->Print("ON_CurveProxy.m_real_curve_domain is not increasing.\n");
}
if ( rc && !m_real_curve->Domain().Includes( m_real_curve_domain ) )
{
rc = false;
if ( text_log)
text_log->Print("ON_CurveProxy.m_real_curve_domain is not included m_real_curve->Domain().\n");
}
if ( rc && !m_this_domain.IsIncreasing() )
{
rc = false;
if ( text_log)
text_log->Print("ON_CurveProxy.m_this_domain is not increasing.\n");
}
return rc;
}
void
ON_CurveProxy::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_CurveProxy uses %x on [%g,%g]\n",m_real_curve,m_real_curve_domain[0],m_real_curve_domain[1]);
}
bool
ON_CurveProxy::Write(
ON_BinaryArchive& // open binary file
) const
{
return false;
}
bool
ON_CurveProxy::Read(
ON_BinaryArchive& // open binary file
)
{
return false;
}
int
ON_CurveProxy::Dimension() const
{
return ( m_real_curve ) ? m_real_curve->Dimension() : 0;
}
bool ON_CurveProxy::GetBBox( // returns true if successful
double* boxmin, // minimum
double* boxmax, // maximum
bool bGrowBox
) const
{
return ( m_real_curve ) ? m_real_curve->GetBBox(boxmin,boxmax,bGrowBox) : false;
}
bool
ON_CurveProxy::Transform( const ON_Xform& )
{
return false; // cannot modify proxy objects
}
ON_Interval ON_CurveProxy::Domain() const
{
return m_this_domain;
}
bool ON_CurveProxy::SetDomain( double t0, double t1 )
{
bool rc = false;
if (t0 < t1)
{
DestroyCurveTree();
m_this_domain.Set(t0, t1);
rc = true;
}
return rc;
}
bool ON_CurveProxy::SetDomain( ON_Interval domain )
{
return SetDomain( domain[0], domain[1] ) ? true : false;
}
//Do not change SpanCount() without making sure it gives the correct result for use in GetSpanVector()
int ON_CurveProxy::SpanCount() const
{
if (!m_real_curve) return 0;
int rsc = m_real_curve->SpanCount();
ON_Interval domain = m_real_curve->Domain();
if (m_real_curve_domain == domain)
return rsc;
double* rsv = (double*)onmalloc((rsc+1)*sizeof(double));
if (!rsv) return 0;
if (!m_real_curve->GetSpanVector(rsv)){
onfree((void*)rsv);
return 0;
}
int i=0;
int sc = 0;
while (i <= rsc && rsv[i] <= m_real_curve_domain[0]) i++;
while (i <= rsc && rsv[i] < m_real_curve_domain[1]){
sc++;
i++;
}
sc++;
onfree((void*)rsv);
return sc;
}
//Do not change GetSpanVector() without making sure it is consistent with SpanCount()
bool ON_CurveProxy::GetSpanVector( double* d ) const
{
#if 0
bool rc = m_real_curve ? m_real_curve->GetSpanVector(d) : false;
if (rc && (m_bReversed || m_this_domain != m_real_curve_domain) )
{
double x;
int i, j, count = SpanCount();
for ( i = 0; i <= count; i++ )
{
x = m_real_curve_domain.NormalizedParameterAt(d[i]);
d[i] = x;
}
if ( m_bReversed )
{
for ( i = 0, j = count; i <= j; i++, j-- )
{
x = d[i];
d[i] = 1.0-d[j];
d[j] = 1.0-x;
}
}
for ( i = 0; i <= count; i++ )
{
d[i] = m_this_domain.ParameterAt(d[i]);
}
}
return rc;
#endif
if (!m_real_curve) return false;
int rsc = m_real_curve->SpanCount();
if (rsc < 1) return false;
double* rsv = (double*)onmalloc((rsc+1)*sizeof(double));
if (!rsv || !m_real_curve->GetSpanVector(rsv)) return false;
ON_Interval domain = m_real_curve->Domain();
if (m_real_curve_domain == m_this_domain && m_real_curve_domain == domain){
int i;
for (i=0; i <= rsc; i++) d[i] = rsv[i];
onfree((void*)rsv);
return true;
}
if (m_real_curve_domain[1] <= domain[0] || m_real_curve_domain[0] >= domain[1]){
onfree((void*)rsv);
return false;
}
int i=0;
int sc = 0;
d[0] = m_real_curve_domain[0];
while (i<= rsc && rsv[i] <= d[0]) i++;
while (i <= rsc && rsv[i] < m_real_curve_domain[1]){
sc++;
d[sc] = rsv[i];
i++;
}
sc++;
d[sc] = m_real_curve_domain[1];
onfree((void*)rsv);
if (m_bReversed || m_real_curve_domain != m_this_domain){
double x;
int j;
for ( i = 0; i <= sc; i++ )
{
x = m_real_curve_domain.NormalizedParameterAt(d[i]);
d[i] = x;
}
if ( m_bReversed )
{
for ( i = 0, j = sc; i <= j; i++, j-- )
{
x = d[i];
d[i] = 1.0-d[j];
d[j] = 1.0-x;
}
}
for ( i = 0; i <= sc; i++ )
{
d[i] = m_this_domain.ParameterAt(d[i]);
}
}
return true;
}
int ON_CurveProxy::Degree() const
{
return m_real_curve ? m_real_curve->Degree() : 0;
}
bool
ON_CurveProxy::GetParameterTolerance(
double t, // t = parameter in domain
double* tminus, // tminus
double* tplus // tplus
) const
{
bool rc = ( m_real_curve )
? m_real_curve->GetParameterTolerance( RealCurveParameter(t),tminus,tplus)
: false;
if (rc)
{
if ( tminus )
*tminus = ThisCurveParameter(*tminus);
if ( tplus )
*tplus = ThisCurveParameter(*tplus);
}
return rc;
}
bool
ON_CurveProxy::IsLinear( // true if curve locus is a line segment
double tolerance // tolerance to use when checking linearity
) const
{
// 12 December 2003 Dale Lear - fixed bug so this works
// when a proxy is restricted to using a linear portion
// of a non-linear real curve.
bool rc = false;
if ( 0 != m_real_curve )
{
ON_Interval cdom = m_real_curve->Domain();
if ( cdom == m_real_curve_domain )
{
rc = m_real_curve->IsLinear(tolerance) ? true : false;
}
else
{
// The ON_CurveProxy::DuplicateCurve() scope is critical
// because there are situation where people derive a
// class from ON_CurveProxy, and override the virtual
// DuplicateCurve(). In this situation I rely on getting
// the result returned by ON_CurveProxy::DuplicateCurve().
ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve();
if ( 0 != temp_curve )
{
rc = temp_curve->IsLinear(tolerance) ? true : false;
delete temp_curve;
}
}
}
return rc;
}
int ON_CurveProxy::IsPolyline(
ON_SimpleArray<ON_3dPoint>* pline_points,
ON_SimpleArray<double>* pline_t
) const
{
ON_SimpleArray<double> tmp_t;
if ( pline_points )
pline_points->SetCount(0);
if ( pline_t )
pline_t->SetCount(0);
if ( !m_real_curve_domain.IsIncreasing() )
return 0;
if ( !m_real_curve )
return 0;
const ON_Interval cdom = m_real_curve->Domain();
if ( !cdom.Includes(m_real_curve_domain) )
return 0;
// See if the "real" curve is a polyline
int rc = 0;
if ( m_real_curve_domain == cdom )
{
// proxy uses entire curve
rc = m_real_curve->IsPolyline(pline_points,pline_t);
if ( rc < 2 )
rc = 0;
if ( pline_points && pline_points->Count() != rc)
{
// The pline_points info is bogus, clear everything and
// return 0.
pline_points->SetCount(0);
if ( pline_t )
pline_t->SetCount(0);
rc = 0;
}
if ( pline_t && pline_t->Count() != rc)
{
// The pline_t info is bogus, clear everything and
// return 0.
pline_t->SetCount(0);
if ( pline_points )
pline_points->SetCount(0);
rc = 0;
}
if ( rc )
{
if ( m_bReversed )
{
if ( pline_points )
pline_points->Reverse();
if ( pline_t )
pline_t->Reverse();
}
if ( pline_points && IsClosed() && pline_points->Count() > 3 )
{
// 27 February 2003 Dale Lear
// If proxy curve says it's closed, then
// make sure end point of returned polyline
// is exactly equal to start point.
*pline_points->Last() = *pline_points->First();
}
if ( pline_t && (m_bReversed || m_real_curve_domain != m_this_domain) )
{
int i;
for ( i = 0; i < rc; i++ )
{
(*pline_t)[i] = ThisCurveParameter( (*pline_t)[i] );
}
}
}
}
else
{
// 12 December 2003 Dale Lear
// We have to extract a sub curve for the test, because
// the region in question may be a polyline and the unused
// portion may not be a polyline. This tends to happen
// when the "real" curve is a polycurve that contains
// a polyline and the polycurve has been parametrically
// trimmed during a brep operation (like a boolean).
//
// The ON_CurveProxy::DuplicateCurve() scope is critical
// because there are situation where people derive a
// class from ON_CurveProxy, and override the virtual
// DuplicateCurve(). In this situation I rely on getting
// the result returned by ON_CurveProxy::DuplicateCurve().
ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve();
if ( temp_curve )
{
rc = temp_curve->IsPolyline(pline_points,pline_t);
delete temp_curve;
}
}
return rc;
}
bool
ON_CurveProxy::IsArc( // true if curve locus in an arc or circle
const ON_Plane* plane, // if not nullptr, test is performed in this plane
ON_Arc* arc, // if not nullptr and true is returned, then arc
// arc parameters are filled in
double tolerance // tolerance to use when checking linearity
) const
{
bool rc = false;
const ON_Interval cdom = m_real_curve->Domain();
if ( cdom == m_real_curve_domain )
{
rc = m_real_curve->IsArc(plane,arc,tolerance) ? true : false;
if ( rc && arc && m_bReversed )
arc->Reverse();
}
else
{
// The ON_CurveProxy::DuplicateCurve() scope is critical
// because there are situation where people derive a
// class from ON_CurveProxy, and override the virtual
// DuplicateCurve(). In this situation I rely on getting
// the result returned by ON_CurveProxy::DuplicateCurve().
ON_Curve* temp_curve = ON_CurveProxy::DuplicateCurve();
if ( 0 != temp_curve )
{
rc = temp_curve->IsArc(plane,arc,tolerance) ? true : false;
delete temp_curve;
}
}
return rc;
}
bool
ON_CurveProxy::IsPlanar(
ON_Plane* plane, // if not nullptr and true is returned, then plane parameters
// are filled in
double tolerance // tolerance to use when checking linearity
) const
{
// RH-75060 GBA 9-June-23 Changed to consider only the m_real_curve_domain if need be.
// return (m_real_curve) ? m_real_curve->IsPlanar(plane, tolerance) : false;
bool rc = false;
if(m_real_curve)
{
rc = m_real_curve->IsPlanar(plane, tolerance);
if (!rc)
{
rc = ON_Curve::IsPlanar(plane, tolerance);
}
}
return rc;
}
bool
ON_CurveProxy::IsInPlane(
const ON_Plane& plane, // plane to test
double tolerance // tolerance to use when checking linearity
) const
{
return ( m_real_curve ) ? m_real_curve->IsInPlane(plane,tolerance) : false;
}
bool
ON_CurveProxy::IsClosed() const
{
bool rc = false;
if ( m_real_curve && m_real_curve->Domain() == m_real_curve_domain )
{
rc = m_real_curve->IsClosed();
}
return rc;
}
bool
ON_CurveProxy::IsPeriodic() const
{
bool rc = false;
if ( m_real_curve && m_real_curve->Domain() == m_real_curve_domain )
{
rc = m_real_curve->IsPeriodic();
}
return rc;
}
bool ON_CurveProxy::GetNextDiscontinuity(
ON::continuity c,
double t0,
double t1,
double* t,
int* hint,
int* dtype,
double cos_angle_tolerance,
double curvature_tolerance
) const
{
bool rc = false;
if ( 0 != dtype )
*dtype = 0;
if ( 0 != m_real_curve )
{
double s;
// convert to "real" curve parameters
double s0 = RealCurveParameter( t0 );
double s1 = RealCurveParameter( t1 );
// 21 October 2005 Dale Lear:
//
// NOTE: If m_bReversed is true, then RealCurveParameter
// will reverse the direction of the search.
// The commented out code below just messed things up.
//
//if ( m_bReversed )
//{
// // NOTE: GetNextDiscontinuity search begins at
// // "t0" and goes towards "t1" so it is ok if s0 > s1.
// s = s0; s0 = s1; s1 = s;
//}
ON::continuity parametric_c = ON::ParametricContinuity((int)c);
int realcrv_dtype = 0;
bool realcrv_rc = m_real_curve->GetNextDiscontinuity(parametric_c,s0,s1,&s,hint,&realcrv_dtype,cos_angle_tolerance,curvature_tolerance);
if ( realcrv_rc )
{
double thiscrv_t = ThisCurveParameter(s);
if ( !(t0 < thiscrv_t && thiscrv_t < t1) && !(t1 < thiscrv_t && thiscrv_t < t0) )
{
realcrv_rc = false;
realcrv_dtype = 0;
// Sometimes proxy domain adjustments kill all the precision.
// To avoid infinite loops, it is critical that *t != t0
double s2 = ON_SQRT_EPSILON*s1 + (1.0 - ON_SQRT_EPSILON)*s0;
if ( (s0 < s2 && s2 < s1) || (s1 < s2 && s2 < s0) )
{
realcrv_rc = m_real_curve->GetNextDiscontinuity(parametric_c,s2,s1,&s,hint,&realcrv_dtype,cos_angle_tolerance,curvature_tolerance);
if ( realcrv_rc )
thiscrv_t = ThisCurveParameter(s);
}
}
if ( realcrv_rc )
{
if ( (t0 < thiscrv_t && thiscrv_t < t1) || (t1 < thiscrv_t && thiscrv_t < t0) )
{
*t = thiscrv_t;
if ( dtype )
*dtype = realcrv_dtype;
rc = true;
}
}
}
if ( !rc && parametric_c != c )
{
// 20 March 2003 Dale Lear:
// Let base class test decide locus continuity questions at ends
rc = ON_Curve::GetNextDiscontinuity( c, t0, t1, t, hint, dtype, cos_angle_tolerance, curvature_tolerance );
}
}
return rc;
}
bool ON_CurveProxy::IsContinuous(
ON::continuity desired_continuity,
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
{
bool rc = true;
if ( m_real_curve )
{
if ( m_real_curve_domain != m_real_curve->Domain() )
{
// 20 March 2003 Dale Lear
// Added this code to correctly handle the new locus
// flavors of ON::continuity.
switch(desired_continuity)
{
case ON::continuity::unknown_continuity:
case ON::continuity::C0_continuous:
case ON::continuity::C1_continuous:
case ON::continuity::C2_continuous:
case ON::continuity::G1_continuous:
case ON::continuity::G2_continuous:
case ON::continuity::Cinfinity_continuous:
case ON::continuity::Gsmooth_continuous:
break;
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:
if ( t >= Domain()[1] )
{
// Since the proxy curve is using a subset of the real curve,
// the proxy can't be closed. So if the query parameter
// is >= domain max, the curve cannot be locus continuous.
rc = false;
}
else
{
// otherwise we want the answer for a non-locus test
desired_continuity = ON::ParametricContinuity((int)desired_continuity);
}
break;
}
}
if (rc)
rc = m_real_curve->IsContinuous( desired_continuity,
RealCurveParameter(t), hint,
point_tolerance, d1_tolerance, d2_tolerance,
cos_angle_tolerance, curvature_tolerance );
}
return rc;
}
bool
ON_CurveProxy::Reverse()
{
if ( m_this_domain.IsIncreasing() )
{
m_bReversed = (m_bReversed) ? false : true;
DestroyCurveTree();
m_this_domain.Reverse();
}
return true;
}
bool
ON_CurveProxy::Evaluate( // returns false if unable to evaluate
double t, // evaluation parameter
int der_count, // number of derivatives (>=0)
int v_stride, // v[] array stride (>=Dimension())
double* v, // v[] array of length stride*(ndir+1)
int side, // optional - determines which side to evaluate from
// 0 = default
// < 0 to evaluate from below,
// > 0 to evaluate from above
int* hint // optional - evaluation hint (int) used to speed
// repeated evaluations
) const
{
// When the proxy domain is a proper subdomain of the
// real curve and we are evaluating at the end, we
// need to be certain we are getting the values
// from the active part of the curve.
double normt = m_this_domain.NormalizedParameterAt(t);
if( fabs( normt )<ON_ZERO_TOLERANCE)
side = ( std::abs(side) <= 1) ? 1 : 2;
else if( fabs(1.0 - normt)<ON_ZERO_TOLERANCE)
side = ( std::abs(side) <= 1) ? -1 : -2;
if ( 0 != side )
{
if ( m_bReversed )
{
side = -side;
}
if (m_bReversed || m_real_curve_domain != m_this_domain )
{
// 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix
// If we have to adjust the evaluation parameter for the
// real curve and the evaluation side was specified, then
// set side to +2 or -2 to trigger the use of
// ON_TuneupEvaluationParameter() when it matters.
if ( -1 == side )
side = -2;
else if ( 1 == side )
side = 2;
}
}
double r = RealCurveParameter(t);
bool rc = ( m_real_curve )
? m_real_curve->Evaluate( r,der_count,v_stride,v,side,hint)
: false;
if ( rc && m_bReversed )
{
// negate odd derivatives
const int dim = m_real_curve->Dimension();
int di, i;
for ( di = 1; di <= der_count; di+=2 )
{
v += v_stride;
for ( i = 0; i < dim; i++ )
{
v[i] = -v[i];
}
v += v_stride;
}
}
return rc;
}
bool ON_CurveProxy::Trim(
const ON_Interval& domain
)
{
bool rc = false;
if ( m_this_domain.IsIncreasing() && m_real_curve_domain.IsIncreasing() )
{
ON_Interval trim_dom = m_this_domain;
trim_dom.Intersection(domain);
if ( trim_dom.IsIncreasing() )
{
ON_Interval real_dom = RealCurveInterval( &trim_dom );
if ( real_dom.IsIncreasing() )
{
DestroyCurveTree();
m_real_curve_domain = real_dom;
m_this_domain = trim_dom;
rc = true;
}
}
}
return rc;
}
// override of virtual ON_Curve::Split
bool ON_CurveProxy::Split(
double t,
ON_Curve*& left_side,
ON_Curve*& right_side
) const
{
bool rc = false;
if ( m_this_domain.IsIncreasing() && m_real_curve_domain.IsIncreasing() && m_this_domain.Includes(t,true) )
{
double crv_t = RealCurveParameter(t);
if ( m_real_curve_domain.Includes(crv_t,true) )
{
ON_CurveProxy* left_proxy = 0;
ON_CurveProxy* right_proxy = 0;
if ( left_side )
{
left_proxy = ON_CurveProxy::Cast(left_side);
if ( !left_proxy )
return false;
}
if ( right_side )
{
right_proxy = ON_CurveProxy::Cast(right_side);
if ( !right_proxy )
return false;
if ( right_side == left_side )
return false;
}
bool bRev = m_bReversed;
ON_Interval left_real_dom, right_real_dom;
if ( bRev )
{
left_real_dom.Set(crv_t,m_real_curve_domain[1]);
right_real_dom.Set(m_real_curve_domain[0],crv_t);
}
else
{
left_real_dom.Set(m_real_curve_domain[0],crv_t);
right_real_dom.Set(crv_t,m_real_curve_domain[1]);
}
ON_Interval left_this_dom(m_this_domain[0],t);
ON_Interval right_this_dom(t,m_this_domain[1]);
if ( left_real_dom.IsIncreasing()
&& right_real_dom.IsIncreasing()
&& left_this_dom.IsIncreasing()
&& right_this_dom.IsIncreasing()
)
{
// note well that left_proxy or right_proxy might also be this
const ON_Curve* real_crv = m_real_curve;
if ( real_crv )
{
ON_Interval d = real_crv->Domain();
if ( !d.Includes(left_real_dom) )
return false;
if ( !d.Includes(right_real_dom) )
return false;
}
if ( !left_proxy )
left_proxy = new ON_CurveProxy();
if ( !right_proxy )
right_proxy = new ON_CurveProxy();
left_proxy->SetProxyCurve( real_crv, left_real_dom );
right_proxy->SetProxyCurve( real_crv, right_real_dom );
if ( bRev )
{
left_proxy->Reverse();
right_proxy->Reverse();
}
left_proxy->SetDomain(left_this_dom[0],left_this_dom[1]);
right_proxy->SetDomain(right_this_dom[0],right_this_dom[1]);
if (!left_side) left_side = left_proxy;
if (!right_side) right_side = right_proxy;
rc = true;
}
}
}
return rc;
}
int ON_CurveProxy::GetNurbForm( // returns 0: unable to create NURBS representation
// with desired accuracy.
// 1: success - returned NURBS parameterization
// matches the curve's to wthe desired accuracy
// 2: success - returned NURBS point locus matches
// the curve's to the desired accuracy but, on
// the interior of the curve's domain, the
// curve's parameterization and the NURBS
// parameterization may not match to the
// desired accuracy.
ON_NurbsCurve& nurbs,
double tolerance, // (>=0)
const ON_Interval* sub_domain // OPTIONAL subdomain of ON::ProxyCurve::Domain()
) const
{
int rc = 0;
if ( m_real_curve )
{
ON_Interval scratch_domain = RealCurveInterval( sub_domain );
rc = m_real_curve->GetNurbForm(nurbs,tolerance,&scratch_domain);
if ( rc > 0 )
{
if ( m_bReversed )
nurbs.Reverse();
ON_Interval d = m_this_domain;
if ( sub_domain )
d.Intersection( *sub_domain );
nurbs.SetDomain( d[0], d[1] );
if ( nurbs.m_dim <= 3 && nurbs.m_dim >= 1 )
{
double t0 = Domain()[0];
double t1 = Domain()[1];
if ( 0 != sub_domain )
{
if ( t0 < sub_domain->Min() )
t0 = sub_domain->Min();
if ( sub_domain->Max() < t1 )
t1 = sub_domain->Max();
}
// set ends of NURBS curve to be exactly on ends of proxy curve
ON_3dPoint P0 = PointAt(t0);
ON_3dPoint P1 = PointAt(t1);
ON_3dPoint N0 = nurbs.PointAtStart();
ON_3dPoint N1 = nurbs.PointAtEnd();
// 22 September 2003, GBA. The end tuning code below should only be applied
// to clamped nurbs curves. In particular it should not be used on
// periodic nurbs curves. Fixes TRR#11502.
bool clamped = nurbs.IsClamped(2);
if ( clamped && (P0 != N0 || P1 != N1) )
{
if ( 0==nurbs.m_is_rat )
{
nurbs.SetCV(0,P0);
nurbs.SetCV(nurbs.m_cv_count-1,P1);
}
else
{
ON_4dPoint H0, H1;
H0 = P0;
H0.w = nurbs.Weight(0);
H0.x *= H0.w;
H0.y *= H0.w;
H0.z *= H0.w;
nurbs.SetCV(0,H0);
H1 = P1;
H1.w = nurbs.Weight(nurbs.m_cv_count-1);
H1.x *= H1.w;
H1.y *= H1.w;
H1.z *= H1.w;
nurbs.SetCV(nurbs.m_cv_count-1,H1);
}
}
}
}
}
return rc;
}
int
ON_CurveProxy::HasNurbForm( // returns 0: unable to create NURBS representation
// with desired accuracy.
// 1: success - returned NURBS parameterization
// matches the curve's to wthe desired accuracy
// 2: success - returned NURBS point locus matches
// the curve's to the desired accuracy but, on
// the interior of the curve's domain, the
// curve's parameterization and the NURBS
// parameterization may not match to the
// desired accuracy.
) const
{
if (!m_real_curve)
return 0;
return m_real_curve->HasNurbForm();
}
bool ON_CurveProxy::GetCurveParameterFromNurbFormParameter(
double nurbs_t,
double* curve_t
) const
{
bool rc = false;
if ( m_real_curve )
{
// 18 June 2003 Dale Lear and Chuck
// Fixing joining bug in STEP TEST 2 caused by error
// in converting NURBS parameter to arc parameter.
const ON_Curve* real_crv = m_real_curve;
ON_Curve* tmp_real_crv = 0;
if ( m_real_curve_domain != m_real_curve->Domain() )
{
const ON_ArcCurve* arc_curve = ON_ArcCurve::Cast(m_real_curve);
if ( 0 != arc_curve )
{
tmp_real_crv = arc_curve->DuplicateCurve();
if ( 0 != tmp_real_crv )
{
if ( tmp_real_crv->Trim(m_real_curve_domain) )
{
real_crv = tmp_real_crv;
}
}
}
}
rc = real_crv->GetCurveParameterFromNurbFormParameter( RealCurveParameter(nurbs_t),curve_t);
if ( rc )
*curve_t = ThisCurveParameter(*curve_t);
if ( 0 != tmp_real_crv )
delete tmp_real_crv;
}
return rc;
}
bool ON_CurveProxy::GetNurbFormParameterFromCurveParameter(
double curve_t,
double* nurbs_t
) const
{
bool rc = false;
if ( m_real_curve )
{
// 18 June 2003 Dale Lear and Chuck
// Fixing joining bug in STEP TEST 2 caused by error
// in converting NURBS parameter to arc parameter.
const ON_Curve* real_crv = m_real_curve;
ON_Curve* tmp_real_crv = 0;
if ( m_real_curve_domain != m_real_curve->Domain() )
{
const ON_ArcCurve* arc_curve = ON_ArcCurve::Cast(m_real_curve);
if ( 0 != arc_curve )
{
tmp_real_crv = arc_curve->DuplicateCurve();
if ( 0 != tmp_real_crv )
{
if ( tmp_real_crv->Trim(m_real_curve_domain) )
{
real_crv = tmp_real_crv;
}
}
}
}
rc = real_crv->GetNurbFormParameterFromCurveParameter( RealCurveParameter(curve_t),nurbs_t);
if ( rc )
*nurbs_t = ThisCurveParameter(*nurbs_t);
if ( 0 != tmp_real_crv )
delete tmp_real_crv;
}
return rc;
}