mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-02-28 19:16:09 +08:00
1271 lines
34 KiB
C++
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;
|
|
}
|