Files
opennurbs/opennurbs_linecurve.cpp
2024-04-11 14:21:33 -07:00

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;
}