mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-03 04:57:01 +08:00
683 lines
15 KiB
C++
683 lines
15 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_LineCurve,ON_Curve,"4ED7D4DB-E947-11d3-BFE5-0010830122F0");
|
|
|
|
ON_LineCurve::ON_LineCurve() ON_NOEXCEPT
|
|
{
|
|
m_t.m_t[0] = 0.0;
|
|
m_t.m_t[1] = 1.0;
|
|
m_dim = 3;
|
|
}
|
|
|
|
ON_LineCurve::~ON_LineCurve()
|
|
{}
|
|
|
|
ON_LineCurve::ON_LineCurve( const ON_LineCurve& src )
|
|
: ON_Curve(src) // copies userdata
|
|
, m_line(src.m_line)
|
|
, m_t(src.m_t)
|
|
, m_dim(src.m_dim)
|
|
{}
|
|
|
|
ON_LineCurve& ON_LineCurve::operator=( const ON_LineCurve& src )
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
ON_Curve::operator=(src); // copies userdata
|
|
m_line = src.m_line;
|
|
m_t = src.m_t;
|
|
m_dim = src.m_dim;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
#if defined(ON_HAS_RVALUEREF)
|
|
|
|
ON_LineCurve::ON_LineCurve( ON_LineCurve&& src) ON_NOEXCEPT
|
|
: ON_Curve(std::move(src)) // moves userdata
|
|
, m_line(src.m_line)
|
|
, m_t(src.m_t)
|
|
, m_dim(src.m_dim)
|
|
{}
|
|
|
|
ON_LineCurve& ON_LineCurve::operator=( ON_LineCurve&& src)
|
|
{
|
|
if ( this != &src )
|
|
{
|
|
ON_Curve::operator=(std::move(src)); // moves userdata
|
|
m_line = src.m_line;
|
|
m_t = src.m_t;
|
|
m_dim = src.m_dim;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
#endif
|
|
|
|
ON_LineCurve::ON_LineCurve(const ON_2dPoint& a,const ON_2dPoint& b)
|
|
: m_line(ON_3dPoint(a),ON_3dPoint(b))
|
|
, m_dim(2)
|
|
{
|
|
double len = m_line.Length();
|
|
if ( len <= ON_ZERO_TOLERANCE )
|
|
len = 1.0;
|
|
m_t.Set(0.0,len);
|
|
}
|
|
|
|
ON_LineCurve::ON_LineCurve(const ON_3dPoint& a,const ON_3dPoint& b) : m_line(a,b), m_dim(3)
|
|
{
|
|
double len = m_line.Length();
|
|
if ( len <= ON_ZERO_TOLERANCE )
|
|
len = 1.0;
|
|
m_t.Set(0.0,len);
|
|
}
|
|
|
|
|
|
ON_LineCurve::ON_LineCurve( const ON_Line& L ) : m_line(L), m_dim(3)
|
|
{
|
|
double len = m_line.Length();
|
|
if ( len <= ON_ZERO_TOLERANCE )
|
|
len = 1.0;
|
|
m_t.Set(0.0,len);
|
|
}
|
|
|
|
ON_LineCurve::ON_LineCurve( const ON_Line& L, double t0, double t1 ) : m_line(L), m_t(t0,t1), m_dim(3)
|
|
{
|
|
}
|
|
|
|
unsigned int ON_LineCurve::SizeOf() const
|
|
{
|
|
unsigned int sz = ON_Curve::SizeOf();
|
|
sz += (sizeof(*this) - sizeof(ON_Curve));
|
|
return sz;
|
|
}
|
|
|
|
ON__UINT32 ON_LineCurve::DataCRC(ON__UINT32 current_remainder) const
|
|
{
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_line),&m_line);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_t),&m_t);
|
|
current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim);
|
|
|
|
return current_remainder;
|
|
}
|
|
|
|
ON_LineCurve& ON_LineCurve::operator=( const ON_Line& L )
|
|
{
|
|
m_line = L;
|
|
m_t.m_t[0] = 0.0;
|
|
m_t.m_t[1] = L.Length();
|
|
if ( m_t.m_t[1] == 0.0 )
|
|
m_t.m_t[1] = 1.0;
|
|
m_dim = 3;
|
|
return *this;
|
|
}
|
|
|
|
int ON_LineCurve::Dimension() const
|
|
{
|
|
return m_dim;
|
|
}
|
|
|
|
bool ON_LineCurve::GetBBox( // returns true if successful
|
|
double* boxmin, // minimum
|
|
double* boxmax, // maximum
|
|
bool bGrowBox
|
|
) const
|
|
{
|
|
return ON_GetPointListBoundingBox( m_dim, false, 2, 3, m_line.from,
|
|
boxmin, boxmax, bGrowBox?true:false
|
|
);
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::Transform( const ON_Xform& xform )
|
|
{
|
|
TransformUserData(xform);
|
|
DestroyCurveTree();
|
|
return m_line.Transform( xform );
|
|
}
|
|
|
|
bool ON_LineCurve::IsDeformable() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool ON_LineCurve::MakeDeformable()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
ON_LineCurve::SwapCoordinates( int i, int j )
|
|
{
|
|
bool rc = false;
|
|
if ( i >= 0 && i < 3 && j >= 0 && j < 3 && i != j ) {
|
|
double t = m_line.from[i];
|
|
m_line.from[i] = m_line.from[j];
|
|
m_line.from[j] = t;
|
|
t = m_line.to[i];
|
|
m_line.to[i] = m_line.to[j];
|
|
m_line.to[j] = t;
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_LineCurve::IsValid( ON_TextLog* text_log ) const
|
|
{
|
|
bool rc = true;
|
|
if (m_t[0] > m_t[1])
|
|
{
|
|
if (text_log) text_log->Print(L"Line domain not valid.");
|
|
rc = false;
|
|
}
|
|
if (m_line.from.IsCoincident(m_line.to))
|
|
{
|
|
if (text_log) text_log->Print(L"Line points are coincident.");
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void ON_LineCurve::Dump( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print( "ON_LineCurve: domain = [%g,%g]\n",m_t[0],m_t[1]);
|
|
dump.PushIndent();
|
|
dump.Print( "start = ");
|
|
dump.Print( m_line.from );
|
|
dump.Print( "\nend = ");
|
|
dump.Print( m_line.to );
|
|
dump.Print( "\n");
|
|
dump.Print( "length = %g\n",m_line.Length());
|
|
dump.PopIndent();
|
|
}
|
|
|
|
bool ON_LineCurve::Write(
|
|
ON_BinaryArchive& file // open binary file
|
|
) const
|
|
{
|
|
bool rc = file.Write3dmChunkVersion(1,0);
|
|
if (rc) {
|
|
rc = file.WriteLine( m_line );
|
|
if (rc) rc = file.WriteInterval( m_t );
|
|
if (rc) rc = file.WriteInt(m_dim);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_LineCurve::Read(
|
|
ON_BinaryArchive& file // open binary file
|
|
)
|
|
{
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
|
|
if (rc && major_version==1) {
|
|
// common to all 1.x versions
|
|
rc = file.ReadLine( m_line );
|
|
if (rc) rc = file.ReadInterval( m_t );
|
|
if (rc) rc = file.ReadInt(&m_dim);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON_Interval ON_LineCurve::Domain() const
|
|
{
|
|
return m_t;
|
|
}
|
|
|
|
bool ON_LineCurve::SetDomain( double t0, double t1)
|
|
{
|
|
if (t0 < t1)
|
|
{
|
|
m_t.Set(t0, t1);
|
|
DestroyCurveTree();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ON_LineCurve::ChangeDimension( int desired_dimension )
|
|
{
|
|
bool rc = (desired_dimension>=2 && desired_dimension<=3);
|
|
|
|
if (rc && m_dim != desired_dimension )
|
|
{
|
|
DestroyCurveTree();
|
|
if ( desired_dimension == 2 )
|
|
{
|
|
// 7 April 2003 Dale Lear - zero z coords if x coord are set
|
|
if( ON_UNSET_VALUE != m_line.from.x )
|
|
m_line.from.z = 0.0;
|
|
if( ON_UNSET_VALUE != m_line.to.x )
|
|
m_line.to.z = 0.0;
|
|
m_dim = 2;
|
|
}
|
|
else
|
|
{
|
|
if ( 2 == m_dim )
|
|
{
|
|
// 7 April 2003 Dale Lear
|
|
// zero z coords if x coords are set and z coords are not set
|
|
if( ON_UNSET_VALUE != m_line.from.x && ON_UNSET_VALUE == m_line.from.z )
|
|
m_line.from.z = 0.0;
|
|
if( ON_UNSET_VALUE != m_line.from.x && ON_UNSET_VALUE == m_line.to.z )
|
|
m_line.from.z = 0.0;
|
|
}
|
|
m_dim = 3;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
int ON_LineCurve::SpanCount() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bool ON_LineCurve::GetSpanVector( // span "knots"
|
|
double* s
|
|
) const
|
|
{
|
|
s[0] = m_t[0];
|
|
s[1] = m_t[1];
|
|
return m_t.IsIncreasing();
|
|
}
|
|
|
|
int ON_LineCurve::Degree() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
bool
|
|
ON_LineCurve::IsLinear( // true if curve locus is a line segment
|
|
double tolerance // tolerance to use when checking linearity
|
|
) const
|
|
{
|
|
return IsValid();
|
|
}
|
|
|
|
int ON_LineCurve::IsPolyline(
|
|
ON_SimpleArray<ON_3dPoint>* pline_points,
|
|
ON_SimpleArray<double>* pline_t
|
|
) const
|
|
{
|
|
int rc = 0;
|
|
if ( pline_points )
|
|
pline_points->SetCount(0);
|
|
if ( pline_t )
|
|
pline_t->SetCount(0);
|
|
if ( IsValid() )
|
|
{
|
|
rc = 2;
|
|
if ( pline_points )
|
|
{
|
|
pline_points->Reserve(2);
|
|
pline_points->Append( m_line.from );
|
|
pline_points->Append( m_line.to );
|
|
}
|
|
if ( pline_t )
|
|
{
|
|
pline_t->Reserve(2);
|
|
pline_t->Append( m_t[0] );
|
|
pline_t->Append( m_t[1] );
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool
|
|
ON_LineCurve::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
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::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
|
|
{
|
|
bool rc = IsValid();
|
|
if ( plane != nullptr && rc )
|
|
{
|
|
if ( m_dim == 2 )
|
|
rc = ON_Curve::IsPlanar(plane,tolerance);
|
|
else if ( !m_line.InPlane(*plane,tolerance) )
|
|
m_line.InPlane(*plane,0.0);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::IsInPlane(
|
|
const ON_Plane& plane, // plane to test
|
|
double tolerance // tolerance to use when checking linearity
|
|
) const
|
|
{
|
|
bool rc = false;
|
|
double d = fabs( plane.DistanceTo( PointAtStart() ));
|
|
if ( d <= tolerance ) {
|
|
d = fabs( plane.DistanceTo( PointAtEnd() ));
|
|
if ( d <= tolerance )
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::IsClosed() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::IsPeriodic() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ON_LineCurve::Reverse()
|
|
{
|
|
const ON_3dPoint p = m_line.from;
|
|
m_line.from = m_line.to;
|
|
m_line.to = p;
|
|
m_t.Reverse();
|
|
DestroyCurveTree();
|
|
return true;
|
|
}
|
|
|
|
bool ON_LineCurve::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
|
|
{
|
|
bool rc = false;
|
|
if ( m_t[0] < m_t[1] ) {
|
|
double s = (t == m_t[1]) ? 1.0 : (t-m_t[0])/(m_t[1]-m_t[0]);
|
|
const ON_3dPoint p = m_line.PointAt(s);
|
|
v[0] = p.x;
|
|
v[1] = p.y;
|
|
if ( m_dim == 3 )
|
|
v[2] = p.z;
|
|
if ( der_count >= 1 )
|
|
{
|
|
v += v_stride;
|
|
ON_3dVector d = m_line.to - m_line.from;
|
|
double dt = m_t[1] - m_t[0];
|
|
v[0] = d.x/dt;
|
|
v[1] = d.y/dt;
|
|
if ( m_dim == 3 )
|
|
v[2] = d.z/dt;
|
|
for ( int di = 2; di <= der_count; di++ ) {
|
|
v += v_stride;
|
|
v[0] = 0.0;
|
|
v[1] = 0.0;
|
|
if ( m_dim == 3 )
|
|
v[2] = 0.0;
|
|
}
|
|
}
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_LineCurve::SetStartPoint(ON_3dPoint start_point)
|
|
{
|
|
m_line.from = start_point;
|
|
DestroyCurveTree();
|
|
return true;
|
|
}
|
|
|
|
bool ON_LineCurve::SetEndPoint(ON_3dPoint end_point)
|
|
{
|
|
m_line.to = end_point;
|
|
DestroyCurveTree();
|
|
return true;
|
|
}
|
|
|
|
|
|
int ON_LineCurve::GetNurbForm(
|
|
ON_NurbsCurve& c,
|
|
double tolerance,
|
|
const ON_Interval* subdomain
|
|
) const
|
|
{
|
|
int rc = 0;
|
|
if ( c.Create( m_dim==2?2:3, false, 2, 2 ) )
|
|
{
|
|
rc = 1;
|
|
double t0 = m_t[0];
|
|
double t1 = m_t[1];
|
|
if (subdomain )
|
|
{
|
|
if ( t0 < t1 )
|
|
{
|
|
const ON_Interval& sd = *subdomain;
|
|
double s0 = sd[0];
|
|
double s1 = sd[1];
|
|
if (s0 < t0) s0 = t0;
|
|
if (s1 > t1) s1 = t1;
|
|
if (s0 < s1)
|
|
{
|
|
t0 = s0;
|
|
t1 = s1;
|
|
}
|
|
else
|
|
rc = 0;
|
|
}
|
|
else
|
|
{
|
|
rc = 0;
|
|
}
|
|
}
|
|
if ( t0 < t1 )
|
|
{
|
|
c.m_knot[0] = t0;
|
|
c.m_knot[1] = t1;
|
|
c.SetCV( 0, PointAt(t0));
|
|
c.SetCV( 1, PointAt(t1));
|
|
}
|
|
else if ( t0 > t1 )
|
|
{
|
|
rc = 0;
|
|
c.m_knot[0] = t1;
|
|
c.m_knot[1] = t0;
|
|
c.SetCV( 0, PointAt(t1));
|
|
c.SetCV( 1, PointAt(t0));
|
|
}
|
|
else
|
|
{
|
|
rc = 0;
|
|
c.m_knot[0] = 0.0;
|
|
c.m_knot[1] = 1.0;
|
|
c.SetCV( 0, m_line.from );
|
|
c.SetCV( 1, m_line.to );
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int ON_LineCurve::HasNurbForm() const
|
|
|
|
{
|
|
if (!IsValid())
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
bool ON_LineCurve::Trim( const ON_Interval& domain )
|
|
{
|
|
bool rc = false;
|
|
if ( domain.IsIncreasing() && m_t.Includes(domain) )
|
|
{
|
|
ON_3dPoint p = PointAt( domain[0] );
|
|
ON_3dPoint q = PointAt( domain[1] );
|
|
if( !p.IsCoincident(q)){
|
|
// 7-May-21 GBA, A successfull trim should return an IsValid ON_LineCurve .
|
|
m_line.from = p;
|
|
m_line.to = q;
|
|
m_t = domain;
|
|
DestroyCurveTree();
|
|
rc = true;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_LineCurve::Extend(
|
|
const ON_Interval& domain
|
|
)
|
|
|
|
{
|
|
double len = Domain().Length();
|
|
ON_3dVector V = m_line.Direction();
|
|
ON_3dPoint Q0 = m_line.from;
|
|
ON_3dPoint Q1 = m_line.to;
|
|
double t0 = Domain()[0];
|
|
double t1 = Domain()[1];
|
|
bool do_it = false;
|
|
if (domain[1] > Domain()[1]) {
|
|
Q1 += (domain[1]-Domain()[1])/len*V;
|
|
t1 = domain[1];
|
|
do_it = true;
|
|
}
|
|
if (domain[0] < Domain()[0]) {
|
|
Q0 += (domain[0]-Domain()[0])/len*V;
|
|
t0 = domain[0];
|
|
do_it = true;
|
|
}
|
|
|
|
if (do_it){
|
|
m_line = ON_Line(Q0, Q1);
|
|
SetDomain(t0, t1);
|
|
DestroyCurveTree();
|
|
}
|
|
return do_it;
|
|
}
|
|
|
|
bool ON_LineCurve::Split(
|
|
double t,
|
|
ON_Curve*& left_side,
|
|
ON_Curve*& right_side
|
|
) const
|
|
|
|
{
|
|
bool rc = false;
|
|
if ( m_t.Includes(t,true) )
|
|
{
|
|
const int dim = m_dim;
|
|
double t0 = m_t[0];
|
|
double t1 = m_t[1];
|
|
ON_Line left, right;
|
|
left.from = m_line.from;
|
|
left.to = m_line.PointAt(m_t.NormalizedParameterAt(t));
|
|
right.from = left.to;
|
|
right.to = m_line.to;
|
|
|
|
// 27 March 2003, Greg Arden. Result must pass IsValid()
|
|
// Fixes RH-64018
|
|
// 6 May 21, GBA, changed ON_LineCurve::IsValid and so this was updated
|
|
if( left.from.IsCoincident(left.to) || right.from.IsCoincident(right.to) )
|
|
return false;
|
|
|
|
ON_LineCurve* left_line = ON_LineCurve::Cast(left_side);
|
|
ON_LineCurve* right_line = ON_LineCurve::Cast(right_side);
|
|
if ( left_side && !left_line )
|
|
{
|
|
ON_ERROR("ON_LineCurve::Split - input left_side not an ON_LineCurve*");
|
|
return false;
|
|
}
|
|
if ( right_side && !right_line )
|
|
{
|
|
ON_ERROR("ON_LineCurve::Split - input right_side not an ON_LineCurve*");
|
|
return false;
|
|
}
|
|
if ( !left_line )
|
|
{
|
|
left_line = new ON_LineCurve();
|
|
left_side = left_line;
|
|
}
|
|
if ( !right_line )
|
|
{
|
|
right_line = new ON_LineCurve();
|
|
right_side = right_line;
|
|
}
|
|
|
|
left_line->DestroyCurveTree();
|
|
left_line->m_line = left;
|
|
left_line->m_t.Set( t0, t );
|
|
left_line->m_dim = dim;
|
|
|
|
right_line->DestroyCurveTree();
|
|
right_line->m_line = right;
|
|
right_line->m_t.Set( t, t1 );
|
|
right_line->m_dim = dim;
|
|
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool ON_LineCurve::GetCurveParameterFromNurbFormParameter(
|
|
double nurbs_t,
|
|
double* curve_t
|
|
) const
|
|
{
|
|
*curve_t = nurbs_t;
|
|
return true;
|
|
}
|
|
|
|
bool ON_LineCurve::GetNurbFormParameterFromCurveParameter(
|
|
double curve_t,
|
|
double* nurbs_t
|
|
) const
|
|
{
|
|
*nurbs_t = curve_t;
|
|
return true;
|
|
}
|