opennurbs_nurbscurve.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 ON_OBJECT_IMPLEMENT(ON_NurbsCurve,ON_Curve,"4ED7D4DD-E947-11d3-BFE5-0010830122F0");
00020 
00021 ON_NurbsCurve* ON_NurbsCurve::New()
00022 {
00023   return new ON_NurbsCurve();
00024 }
00025 
00026 ON_NurbsCurve* ON_NurbsCurve::New(
00027           const ON_NurbsCurve& nurbs_curve 
00028           )
00029 {
00030   return new ON_NurbsCurve(nurbs_curve);
00031 }
00032 
00033 ON_NurbsCurve* ON_NurbsCurve::New(
00034           const ON_BezierCurve& bezier_curve 
00035           )
00036 {
00037   return new ON_NurbsCurve(bezier_curve);
00038 }
00039 
00040 ON_NurbsCurve* ON_NurbsCurve::New(
00041           int dimension,
00042           ON_BOOL32 bIsRational,
00043           int order,
00044           int cv_count
00045           )
00046 {
00047   return new ON_NurbsCurve(dimension,bIsRational,order,cv_count);
00048 }
00049 
00050 
00051 ON_NurbsCurve::ON_NurbsCurve()
00052 {
00053   ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
00054   Initialize();
00055 }
00056 
00057 ON_NurbsCurve::ON_NurbsCurve( const ON_NurbsCurve& src )
00058 {
00059   ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
00060   Initialize();
00061   *this = src;
00062 }
00063 
00064 ON_NurbsCurve::ON_NurbsCurve( int dim, ON_BOOL32 bIsRational, int order, int cv_count )
00065 {
00066   ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
00067   Initialize();
00068   Create(dim,bIsRational,order,cv_count);
00069 }
00070 
00071 ON_NurbsCurve::ON_NurbsCurve(const ON_BezierCurve& src)
00072 {
00073   ON__SET__THIS__PTR(m_s_ON_NurbsCurve_ptr);
00074   Initialize();
00075   ON_NurbsCurve::operator=(src);
00076 }
00077 
00078 ON_NurbsCurve::~ON_NurbsCurve()
00079 {
00080   Destroy();
00081 }
00082 
00083 unsigned int ON_NurbsCurve::SizeOf() const
00084 {
00085   unsigned int sz = ON_Curve::SizeOf();
00086   sz += (sizeof(*this) - sizeof(ON_Curve));
00087   sz += m_knot_capacity*sizeof(*m_knot);
00088   sz += m_cv_capacity*sizeof(*m_cv);
00089   return sz;
00090 }
00091 
00092 ON__UINT32 ON_NurbsCurve::DataCRC(ON__UINT32 current_remainder) const
00093 {
00094   current_remainder = ON_CRC32(current_remainder,sizeof(m_dim),&m_dim);
00095   current_remainder = ON_CRC32(current_remainder,sizeof(m_is_rat),&m_is_rat);
00096   current_remainder = ON_CRC32(current_remainder,sizeof(m_order),&m_order);
00097   current_remainder = ON_CRC32(current_remainder,sizeof(m_cv_count),&m_cv_count);
00098   if ( m_cv_count > 0 && m_cv_stride > 0 && m_cv )
00099   {
00100     size_t sizeof_cv = CVSize()*sizeof(m_cv[0]);
00101     const double* cv = m_cv;
00102     int i;
00103     for ( i = 0; i < m_cv_count; i++ )
00104     {
00105       current_remainder = ON_CRC32(current_remainder,sizeof_cv,cv);
00106       cv += m_cv_stride;
00107     }
00108   }
00109   current_remainder = ON_CRC32(current_remainder,KnotCount()*sizeof(m_knot[0]),m_knot);
00110   return current_remainder;
00111 }
00112 
00113 int ON_NurbsCurve::Dimension() const
00114 {
00115   return m_dim;
00116 }
00117 
00118 bool ON_NurbsCurve::IsRational() const
00119 {
00120   return m_is_rat?true:false;
00121 }
00122 
00123 int ON_NurbsCurve::CVSize() const
00124 {
00125   return (m_dim>0) ? (m_is_rat?(m_dim+1):m_dim) : 0;
00126 }
00127 
00128 int ON_NurbsCurve::Order( void ) const
00129 {
00130   return m_order;
00131 }
00132 
00133 int ON_NurbsCurve::CVCount( void ) const
00134 {
00135   return m_cv_count;
00136 }
00137 
00138 int ON_NurbsCurve::KnotCount( void ) const
00139 {
00140   return ON_KnotCount( m_order, m_cv_count );
00141 }
00142 
00143 double* ON_NurbsCurve::CV( int i ) const
00144 {
00145   return (m_cv) ? (m_cv + i*m_cv_stride) : NULL;
00146 }
00147 
00148 ON::point_style ON_NurbsCurve::CVStyle() const
00149 {
00150   return m_is_rat ? ON::homogeneous_rational : ON::not_rational;
00151 }
00152 
00153 
00154 double ON_NurbsCurve::Weight( int i ) const
00155 {
00156   return (m_cv && m_is_rat) ? m_cv[i*m_cv_stride+m_dim] : 1.0;
00157 }
00158 
00159 
00160 double ON_NurbsCurve::Knot( int i ) const
00161 {
00162   return (m_knot) ? m_knot[i] : 0.0;
00163 }
00164 
00165 int ON_NurbsCurve::KnotMultiplicity( int i ) const
00166 {
00167   return ON_KnotMultiplicity(m_order,m_cv_count,m_knot,i);
00168 }
00169 
00170 const double* ON_NurbsCurve::Knot() const
00171 {
00172   return m_knot;
00173 }
00174 
00175 double ON_NurbsCurve::SuperfluousKnot( int end ) const
00176 {
00177   return(m_knot) ? ON_SuperfluousKnot(m_order,m_cv_count,m_knot,end) : 0.0;
00178 }
00179 
00180 
00181 bool ON_NurbsCurve::MakePeriodicUniformKnotVector( double delta )
00182 {
00183         DestroyCurveTree();
00184   ReserveKnotCapacity( ON_KnotCount( m_order, m_cv_count ) );
00185   return ON_MakePeriodicUniformKnotVector( m_order, m_cv_count, m_knot, delta );
00186 }
00187 
00188 
00189 bool ON_NurbsCurve::MakeClampedUniformKnotVector( double delta )
00190 {
00191         DestroyCurveTree();
00192   ReserveKnotCapacity( ON_KnotCount( m_order, m_cv_count ) );
00193   return ON_MakeClampedUniformKnotVector( m_order, m_cv_count, m_knot, delta );
00194 }
00195 
00196 bool ON_NurbsCurve::CreateClampedUniformNurbs( 
00197         int dimension,
00198         int order,
00199         int point_count,
00200         const ON_3dPoint* point,
00201         double knot_delta
00202         )
00203 {
00204   bool rc = (dimension >= 1 && dimension<=3 && point!=NULL);
00205   if (rc)
00206     rc = Create( dimension, false, order, point_count );
00207   if ( rc )
00208   {
00209     int i;
00210     for (i = 0; i < point_count; i++)
00211       SetCV( i, ON::intrinsic_point_style, point[i] );
00212   }
00213   if ( rc )
00214     rc = MakeClampedUniformKnotVector( knot_delta );
00215   return rc;
00216 }
00217 
00218 bool ON_NurbsCurve::CreatePeriodicUniformNurbs( 
00219         int dimension,
00220         int order,
00221         int point_count,
00222         const ON_3dPoint* point,
00223         double knot_delta
00224         )
00225 {
00226   bool rc = (dimension >= 1 && dimension<=3 && point!=NULL);
00227   if (rc)
00228     rc = Create( dimension, false, order, point_count+(order-1) );
00229   if ( rc )
00230   {
00231     int i;
00232     for (i = 0; i < point_count; i++)
00233       SetCV( i, ON::intrinsic_point_style, point[i] );
00234     for (i = 0; i <= order-2; i++)
00235       SetCV( m_cv_count-m_order+1+i, ON::intrinsic_point_style, CV(i) );
00236   }
00237   if ( rc )
00238     rc = MakePeriodicUniformKnotVector( knot_delta );
00239   return rc;
00240 }
00241 
00242 bool ON_NurbsCurve::Create(
00243         int dim,      // dimension (>= 1)
00244         ON_BOOL32 is_rat,  // true to make a rational NURBS
00245         int order,    // order (>= 2)
00246         int cv_count  // cv count (>= order)
00247         )
00248 {
00249   DestroyCurveTree();
00250   if ( dim < 1 )
00251     return false;
00252   if ( order < 2 )
00253     return false;
00254   if ( cv_count < order )
00255     return false;
00256   m_dim = dim;
00257   m_is_rat = (is_rat) ? true : false;
00258   m_order = order;
00259   m_cv_count = cv_count;
00260   m_cv_stride = (m_is_rat) ? m_dim+1 : m_dim;
00261   bool rc = ReserveKnotCapacity( KnotCount() );
00262   if ( !ReserveCVCapacity( CVCount()*m_cv_stride ) )
00263     rc = false;
00264   return rc;
00265 }
00266 
00267 void ON_NurbsCurve::Destroy()
00268 {
00269   DestroyCurveTree();
00270   double* cv = ( m_cv && m_cv_capacity ) ? m_cv : NULL;
00271   double* knot = ( m_knot && m_knot_capacity ) ? m_knot : NULL;
00272   Initialize();
00273   if ( cv )
00274     onfree(cv);
00275   if ( knot )
00276     onfree(knot);
00277 }
00278 
00279 void ON_NurbsCurve::EmergencyDestroy()
00280 {
00281   Initialize();
00282 }
00283 
00284 
00285 void ON_NurbsCurve::Initialize()
00286 {
00287   m_dim = 0;
00288   m_is_rat = 0;
00289   m_order = 0;
00290   m_cv_count = 0;
00291   m_knot_capacity = 0;
00292   m_knot = 0;
00293   m_cv_stride = 0;
00294   m_cv_capacity = 0;
00295   m_cv = 0;
00296 }
00297 
00298 static void ON_NurbsCurveCopyHelper( const ON_NurbsCurve& src, ON_NurbsCurve& dest )
00299 {
00300   dest.DestroyCurveTree();
00301   dest.m_dim       = src.m_dim;
00302   dest.m_is_rat    = src.m_is_rat;
00303   dest.m_order     = src.m_order;
00304   dest.m_cv_count  = src.m_cv_count;
00305   dest.m_cv_stride = dest.m_is_rat ? dest.m_dim+1 : dest.m_dim;
00306   if ( src.m_knot ) 
00307   {
00308     // copy knot array
00309     dest.ReserveKnotCapacity( dest.KnotCount() );
00310     memcpy( dest.m_knot, src.m_knot, dest.KnotCount()*sizeof(*dest.m_knot) );
00311   }
00312   if ( src.m_cv ) 
00313   {
00314     // copy cv array
00315     dest.ReserveCVCapacity( dest.m_cv_stride*dest.m_cv_count );
00316     const int dst_cv_size = dest.CVSize()*sizeof(*dest.m_cv);
00317     const int src_stride = src.m_cv_stride;
00318     const int dst_stride = dest.m_cv_stride;
00319     const double *src_cv = src.CV(0);
00320     double *dst_cv = dest.m_cv;
00321     if ( src_stride == dst_stride ) 
00322     {
00323       memcpy( dst_cv, src_cv, dest.m_cv_count*dst_stride*sizeof(*dst_cv) );
00324     }
00325     else 
00326     {
00327       int i;
00328       for ( i = 0; i < dest.m_cv_count; i++ )
00329       {
00330         memcpy( dst_cv, src_cv, dst_cv_size );
00331         dst_cv += dst_stride;
00332         src_cv += src_stride;
00333       }
00334     }
00335   }
00336 }
00337 
00338 ON_NurbsCurve& ON_NurbsCurve::operator=( const ON_NurbsCurve& src )
00339 {
00340   if ( this != &src ) 
00341   {
00342     ON_Curve::operator=(src);
00343     ON_NurbsCurveCopyHelper(src,*this);
00344   }
00345   return *this;
00346 }
00347 
00348 
00349 ON_NurbsCurve& ON_NurbsCurve::operator=(const ON_BezierCurve& src)
00350 {
00351   int i;
00352   Create(src.m_dim,src.m_is_rat,src.m_order,src.m_order);
00353   const int sizeof_cv = src.CVSize()*sizeof(double);
00354   for ( i = 0; i < m_cv_count; i++ ) {
00355     memcpy( CV(i), src.CV(i), sizeof_cv );
00356   }
00357   for ( i = 0; i <= m_order-2; i++ )
00358     m_knot[i] = 0.0;
00359   const int knot_count = KnotCount();
00360   for ( i = m_order-1; i < knot_count; i++ )
00361     m_knot[i] = 1.0;
00362   return *this;
00363 }
00364 
00365 void ON_NurbsCurve::Dump( ON_TextLog& dump ) const
00366 {
00367   dump.Print( "ON_NurbsCurve dim = %d is_rat = %d\n"
00368                "        order = %d cv_count = %d\n",
00369                m_dim, m_is_rat, m_order, m_cv_count );
00370   dump.Print( "Knot Vector ( %d knots )\n", KnotCount() );
00371   dump.PrintKnotVector( m_order, m_cv_count, m_knot );
00372   dump.Print( "Control Points  %d %s points\n"
00373                "  index               value\n",
00374                m_cv_count, 
00375                (m_is_rat) ? "rational" : "non-rational" );
00376   if ( !m_cv ) {
00377     dump.Print("  NULL cv array\n");
00378   }
00379   else {
00380     dump.PrintPointList( m_dim, m_is_rat, 
00381                       m_cv_count, m_cv_stride,
00382                       m_cv, 
00383                       "  CV" );
00384   }
00385 }
00386 
00387 
00388 static bool ON_NurbsCurveIsNotValid()
00389 {
00390   return ON_IsNotValid(); // <-- good place for a breakpoint
00391 }
00392 
00393 static bool ON_ControlPointsAreNotValid()
00394 {
00395   return ON_IsNotValid(); // <-- good place for a breakpoint
00396 }
00397 
00398 static bool ON_ControlPointsAreValid( int cv_size, int cv_count, int cv_stride, const double* cv, ON_TextLog* text_log )
00399 {
00400   int i, j;
00401   if ( 0 == cv  )
00402   {
00403     if ( text_log )
00404     {
00405       text_log->Print("cv pointer is null.\n");
00406     }
00407     return ON_ControlPointsAreNotValid();
00408   }
00409 
00410   if ( cv_count < 2 )
00411   {
00412     if ( text_log )
00413     {
00414       text_log->Print("cv_count = %d (must be >= 2).\n",cv_count);
00415     }
00416     return ON_ControlPointsAreNotValid();
00417   }
00418 
00419   if ( cv_size < 1 )
00420   {
00421     if ( text_log )
00422     {
00423       text_log->Print("cv_size = %d (must be >= 1).\n",cv_size);
00424     }
00425     return ON_ControlPointsAreNotValid();
00426   }
00427 
00428   if ( cv_stride < cv_size )
00429   {
00430     if ( text_log )
00431     {
00432       text_log->Print("cv_stride = %d and cv_size = %d (cv_stride must be >= cv_size).\n",cv_stride,cv_size);
00433     }
00434     return ON_ControlPointsAreNotValid();
00435   }
00436 
00437   for ( i = 0; i < cv_count; i++, cv += cv_stride )
00438   {
00439     for ( j = 0; j < cv_size; j++ )
00440     {
00441       if ( !ON_IsValid(cv[j]) )
00442       {
00443         if ( text_log )
00444         {
00445           text_log->Print("cv[%d*cv_stride + %d] = %g is not valid.\n",i,j,cv[j]);
00446         }
00447         return ON_ControlPointsAreNotValid();
00448       }
00449     }    
00450   }
00451 
00452   return true;
00453 }
00454 
00455 ON_BOOL32 ON_NurbsCurve::IsValid( ON_TextLog* text_log ) const
00456 {
00457   if ( m_dim <= 0 )
00458   {
00459     if ( text_log )
00460     {
00461       text_log->Print("ON_NurbsCurve.m_dim = %d (should be > 0).\n",m_dim);
00462     }
00463     return ON_NurbsCurveIsNotValid();
00464   }
00465   
00466   if (m_order < 2 )
00467   {
00468     if ( text_log )
00469     {
00470       text_log->Print("ON_NurbsCurve.m_order = %d (should be >= 2).\n",m_order);
00471     }
00472     return ON_NurbsCurveIsNotValid();
00473   }
00474   
00475   if (m_cv_count < m_order )
00476   {
00477     if ( text_log )
00478     {
00479       text_log->Print("ON_NurbsCurve.m_cv_count = %d (should be >= m_order=%d).\n",m_cv_count,m_order);
00480     }
00481     return ON_NurbsCurveIsNotValid();
00482   }
00483   
00484   if (m_cv_stride < CVSize() )
00485   {
00486     if ( text_log )
00487     {
00488       text_log->Print("ON_NurbsCurve.m_cv_stride = %d (should be >= %d).\n",m_cv_stride,CVSize());
00489     }
00490   }
00491   
00492   if (m_cv == NULL)
00493   {
00494     if ( text_log )
00495     {
00496       text_log->Print("ON_NurbsCurve.m_cv is NULL.\n");
00497     }
00498     return ON_NurbsCurveIsNotValid();
00499   }
00500   
00501   if (m_knot == NULL)
00502   {
00503     if ( text_log )
00504     {
00505       text_log->Print("ON_NurbsCurve.m_knot is NULL.\n");
00506     }
00507     return ON_NurbsCurveIsNotValid();
00508   }
00509 
00510   if ( !ON_IsValidKnotVector( m_order, m_cv_count, m_knot, text_log ) ) 
00511   {
00512     if ( text_log )
00513     {
00514       text_log->Print("ON_NurbsCurve.m_knot[] is not a valid knot vector.\n");
00515     }
00516     return ON_NurbsCurveIsNotValid();
00517   }
00518 
00519   if ( !ON_ControlPointsAreValid(CVSize(),m_cv_count,m_cv_stride,m_cv,text_log) )
00520   {
00521     if ( text_log )
00522     {
00523       text_log->Print("ON_NurbsCurve.m_cv[] is not valid.\n");
00524     }
00525     return ON_NurbsCurveIsNotValid();
00526   }
00527 
00528   if ( m_is_rat )
00529   {
00530     // weights at fully multiple knots must be nonzero
00531     // partial test for weight function being zero
00532     double sign = 0.0;
00533     const double* w = &m_cv[m_dim];
00534     int zcount = 0;
00535     int i;
00536     for ( i = 0; i < m_cv_count; i++, w += m_cv_stride )
00537     {
00538       if ( *w == 0.0 )
00539         zcount++;
00540       else
00541         zcount = 0;
00542 
00543       if ( zcount >= m_order )
00544       {
00545         if ( text_log )
00546         {
00547           text_log->Print("ON_NurbsCurve.m_cv has zero weights for CV[%d],...,CV[%d].\n",i-m_order+1,i);
00548         }
00549         return ON_NurbsCurveIsNotValid(); // denominator is zero for entire span
00550       }
00551       
00552       if ( m_knot[i] == m_knot[i+m_order-2] ) 
00553       {
00554         if ( *w == 0.0 )
00555         {
00556           if ( text_log )
00557           {
00558             text_log->Print("ON_NurbsCurve.m_cv has zero weights for CV[%d].\n",i);
00559           }
00560           return ON_NurbsCurveIsNotValid();
00561         }
00562         
00563         if (sign == 0.0) 
00564         {
00565           sign = (*w > 0.0) ? 1.0 : -1.0;
00566         }
00567         else if ( *w * sign <= 0.0 ) 
00568         {
00569           if ( text_log )
00570           {
00571             text_log->Print("ON_NurbsCurve.m_cv has a zero denominator in the parameter interval [%g,%g].\n",
00572                             m_knot[i-1],m_knot[i]);
00573           }
00574           return ON_NurbsCurveIsNotValid();
00575         }
00576       }
00577     }
00578 
00579     if ( m_dim <= 3 && 2 == m_order && 2 == m_cv_count )
00580     {
00581       // fix for RR 21239
00582       // 16 November 2010 Chuck and Dale Lear added m_dim <= 3
00583       // so the 3d checking does not interfer with applications
00584       // that use high dimension curves where the start and end
00585       // points can be arbitrary.
00586       ON_3dPoint P0 = PointAtStart();
00587       ON_3dPoint P1 = PointAtEnd();
00588       if ( P0 == P1 )
00589       {
00590         if ( text_log )
00591         {
00592           text_log->Print("ON_NurbsCurve is a line with no length.\n");
00593         }
00594         return ON_NurbsCurveIsNotValid();
00595       }
00596     }
00597   }
00598 
00599   return true;
00600 }
00601 
00602 ON_BOOL32 ON_NurbsCurve::GetBBox( // returns true if successful
00603        double* boxmin,    // minimum
00604        double* boxmax,    // maximum
00605        ON_BOOL32 bGrowBox  // true means grow box
00606        ) const
00607 {
00608   return ON_GetPointListBoundingBox( 
00609                  m_dim, m_is_rat, m_cv_count, 
00610                  m_cv_stride, m_cv, 
00611                  boxmin, boxmax, bGrowBox?true:false
00612                  );
00613 }
00614 
00615 ON_BOOL32 ON_NurbsCurve::Transform( const ON_Xform& xform )
00616 {
00617   TransformUserData(xform);
00618         DestroyCurveTree();
00619   if ( 0 == m_is_rat )
00620   {
00621     if ( xform.m_xform[3][0] != 0.0 || xform.m_xform[3][1] != 0.0 || xform.m_xform[3][2] != 0.0 )
00622     {
00623       MakeRational();
00624     }
00625   }
00626   return ON_TransformPointList( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv, xform );
00627 }
00628 
00629 bool ON_NurbsCurve::IsDeformable() const
00630 {
00631   return true;
00632 }
00633 
00634 bool ON_NurbsCurve::MakeDeformable()
00635 {
00636   return true;
00637 }
00638 
00639 bool ON_PolylineCurve::IsDeformable() const
00640 {
00641   return true;
00642 }
00643 
00644 bool ON_PolylineCurve::MakeDeformable()
00645 {
00646   return true;
00647 }
00648 
00649 
00650 ON_BOOL32 ON_NurbsCurve::Write(
00651        ON_BinaryArchive& file // open binary file
00652      ) const
00653 {
00654   // NOTE - check legacy I/O code if changed
00655   ON_BOOL32 rc = file.Write3dmChunkVersion(1,0);
00656   if (rc)
00657   {
00658     if (rc) rc = file.WriteInt( m_dim );
00659     if (rc) rc = file.WriteInt( m_is_rat );
00660     if (rc) rc = file.WriteInt( m_order );
00661     if (rc) rc = file.WriteInt( m_cv_count );
00662     if (rc) rc = file.WriteInt( 0 ); // reserved - legacy flag values
00663     if (rc) rc = file.WriteInt( 0 ); // reserved   
00664     
00665     // write invalid bounding box - may be used in future
00666     if (rc) 
00667     {
00668       ON_BoundingBox bbox;
00669       rc = file.WriteBoundingBox(bbox);
00670     }
00671 
00672     // write knots
00673     int count = (0 != m_knot && m_cv_count >= m_order && m_order >= 2)
00674               ? KnotCount() 
00675               : 0;
00676     if (rc) rc = file.WriteInt(count);
00677     if (rc) rc = file.WriteDouble( count, m_knot );
00678 
00679     // write cvs
00680     const int cv_size = CVSize();
00681     count = ( m_cv && cv_size > 0 && m_cv_count > 0  && m_cv_stride >= cv_size ) 
00682           ? m_cv_count
00683           : 0;
00684     if (rc) rc = file.WriteInt(count);
00685     if (rc && count > 0 )
00686     {
00687       int i;
00688       for ( i = 0; i < m_cv_count && rc; i++ )
00689       {
00690         rc = file.WriteDouble( cv_size, CV(i) );
00691       }
00692     }
00693 
00694   }
00695   return rc;
00696 }
00697 
00698 ON_BOOL32 ON_NurbsCurve::Read(
00699        ON_BinaryArchive& file // open binary file
00700      )
00701 {
00702   // NOTE - check legacy I/O code if changed
00703   Destroy();
00704   int major_version = 0;
00705   int minor_version = 0;
00706   ON_BOOL32 rc = file.Read3dmChunkVersion(&major_version,&minor_version);
00707   if (rc && major_version==1)
00708   {
00709     // common to all 1.x versions
00710     int dim = 0, is_rat = 0, order = 0, cv_count = 0;
00711     int reserved1 = 0, reserved2 = 0;
00712     if (rc) rc = file.ReadInt( &dim );
00713     if (rc) rc = file.ReadInt( &is_rat );
00714     if (rc) rc = file.ReadInt( &order );
00715     if ( order < 0 )
00716       rc = false;
00717     if (rc) rc = file.ReadInt( &cv_count );
00718     if ( cv_count < order )
00719       rc = false;
00720     if (rc) rc = file.ReadInt( &reserved1 ); // reserved - legacy flag values
00721     if (rc) rc = file.ReadInt( &reserved2 ); // reserved    
00722 
00723     // bounding box
00724     if (rc) {
00725       // write invalid bounding box - may be used in future
00726       ON_BoundingBox bbox;
00727       rc = file.ReadBoundingBox( bbox );
00728     }
00729     
00730     if ( !Create( dim, is_rat, order, cv_count ) )
00731       rc = false;
00732 
00733     // read knots
00734     int count = 0;
00735     if (rc) rc = file.ReadInt(&count);
00736     if ( count < 0 )
00737       rc = false; // fix crash bug 92841
00738     else if ( 0 != count && count != ON_KnotCount(order,cv_count) )
00739       rc = false;
00740     if (rc ) rc = ReserveKnotCapacity(count);
00741     if (rc) rc = file.ReadDouble( count, m_knot );
00742 
00743     // read cvs
00744     count = 0;
00745     if (rc) rc = file.ReadInt(&count);
00746     const int cv_size = CVSize();
00747     if (rc) rc = ReserveCVCapacity( count*cv_size );
00748     if (count > 0 && cv_size > 0 && rc )
00749     {
00750       int i;
00751       for ( i = 0; i < m_cv_count && rc; i++ )
00752       {
00753         rc = file.ReadDouble( cv_size, CV(i) );
00754       }
00755     }
00756   }
00757   if ( !rc )
00758     Destroy();
00759   return rc;
00760 }
00761 
00762 ON_Interval ON_NurbsCurve::Domain() const
00763 {
00764   ON_Interval d;
00765   if ( !ON_GetKnotVectorDomain( m_order, m_cv_count, m_knot, &d.m_t[0], &d.m_t[1] ) )
00766     d.Destroy();
00767   return d;
00768 }
00769 
00770 ON_BOOL32 ON_NurbsCurve::SetDomain( double t0, double t1 )
00771 {
00772   ON_BOOL32 rc = false;
00773   if ( m_order >= 2 && m_cv_count >= m_order && m_knot && t0 < t1 ) {
00774          //DestroyCurveTree();
00775    const double k0 = m_knot[m_order-2];
00776     const double k1 = m_knot[m_cv_count-1];
00777     if ( k0 == t0 && k1 == t1 )
00778       rc = true;
00779     else if ( k0 < k1 ) 
00780     {
00781       DestroyCurveTree();
00782       const double d = (t1-t0)/(k1-k0);
00783       const double km = 0.5*(k0+k1);
00784       const int knot_count = KnotCount();
00785       int i;
00786       for ( i = 0; i < knot_count; i++ ) 
00787       {
00788         if ( m_knot[i] <= km ) {
00789           m_knot[i] = (m_knot[i] - k0)*d + t0;
00790         }
00791         else {
00792           m_knot[i] = (m_knot[i] - k1)*d + t1;
00793         }
00794       }
00795       rc = true;
00796     }
00797   }
00798   return rc;
00799 }
00800 
00801 ON_BOOL32 ON_NurbsCurve::ChangeClosedCurveSeam( double t )
00802 {
00803   ON_BOOL32 rc = IsClosed();
00804   if ( rc )
00805   {
00806     //ON_BOOL32 bIsPeriodic = IsPeriodic();
00807     const ON_Interval old_dom = Domain();
00808     double k = t;
00809     double s = old_dom.NormalizedParameterAt(t);
00810     if ( s < 0.0 || s > 1.0 )
00811     {
00812       s = fmod( s, 1.0 );
00813       if ( s < 0.0 )
00814         s += 1.0;
00815       k = old_dom.ParameterAt(s);
00816     }
00817                 s = old_dom.NormalizedParameterAt( k);
00818     if ( old_dom.Includes(k,true) )
00819     {
00820       ON_NurbsCurve left, right;
00821       // TODO - if periodic - dont' put multi knot in middle
00822       //if ( IsPeriodic() ){} else
00823       {
00824         ON_Curve* pleft = &left;
00825         ON_Curve* pright = &right;
00826         rc = Split( k, pleft, pright );
00827         if ( rc )
00828         {
00829           //int a = left.IsValid();
00830           //int b = right.IsValid();
00831           right.Append(left);
00832           *this = right;
00833         }
00834       }
00835     }
00836     else
00837     {
00838       // k already at start/end of domain
00839       rc = true;
00840     }
00841     if (rc)
00842       SetDomain( t, t+old_dom.Length() );
00843   }
00844   return rc;
00845 }
00846 
00847 
00848 int ON_NurbsCurve::SpanCount() const
00849 {
00850   // number of smooth spans in curve
00851   return ON_KnotVectorSpanCount( m_order, m_cv_count, m_knot );
00852 }
00853 
00854 ON_BOOL32 ON_NurbsCurve::GetSpanVector( // span "knots" 
00855        double* s // array of length SpanCount() + 1 
00856        ) const
00857 {
00858   return ON_GetKnotVectorSpanVector( m_order, m_cv_count, m_knot, s );
00859 }
00860 
00861 int ON_NurbsCurve::Degree() const
00862 {
00863   return m_order >= 2 ? m_order-1 : 0;
00864 }
00865 
00866 
00867 // true if curve locus is a line segment
00868 // tolerance to use when checking linearity
00869 ON_BOOL32
00870 ON_NurbsCurve::IsLinear(
00871       double tolerance
00872       ) const
00873 {
00874   // 9 March 2009 Dale Lear
00875   //   Speed up IsLinear() test to accelerate loading curves into Rhino.
00876   //
00877 
00878   if ( !IsClamped() )
00879     return false;
00880 
00881   // IsClamped returning true insures that 2 <= order <= cv_count
00882   // and that the knot vector is clamped.
00883 
00884   ON_Line L;
00885   ON_3dPoint P, Q;
00886   ON_3dVector V0, V1, D;
00887   double t0, t, d, s;
00888   int i;
00889 
00890   // When m_cv is a null pointer, the GetCV() fails.
00891   if ( !GetCV(0,L.from) )
00892     return false;
00893   if ( !GetCV(m_cv_count-1,L.to) )
00894     return false;
00895 
00896   // if CV coordinates are NaNs, infinities or ON_UNSET_VALUE,
00897   // then "t" will end failing the ON_IsValid() test.
00898   D.x = L.to.x - L.from.x; D.y = L.to.y - L.from.y; D.z = L.to.z - L.from.z; 
00899   t = D.x*D.x + D.y*D.y + D.z*D.z;
00900   if ( !ON_IsValid(t) || !(t > 0.0) )
00901     return false;
00902 
00903   if ( 2 == m_cv_count )
00904     return true;
00905 
00906   t0 = 0.0;
00907   bool bDefaultTolerance = false;
00908   if ( !(tolerance > 0.0) )
00909   {
00910     bDefaultTolerance = true;
00911     tolerance = ON_ZERO_TOLERANCE;
00912   }
00913   const double tol2 = tolerance*tolerance;
00914   t = 1.0/t;
00915   D.x *= t; D.y *= t; D.z *= t;
00916 
00917   for ( i = 1; i < m_cv_count-1; i++ )
00918   {
00919     // This call to GetCV will return true because earlier calls to
00920     // GetCV(0,...) and GetCV(m_cv_count-1,...) worked
00921     GetCV(i,P); 
00922 
00923     // Set t = parameter of point on line L closest to P
00924     V0.x = P.x - L.from.x; V0.y = P.y - L.from.y; V0.z = P.z - L.from.z;
00925     V1.x = P.x - L.to.x;   V1.y = P.y - L.to.y;   V1.z = P.z - L.to.z;
00926     if ( V0.x*V0.x + V0.y*V0.y + V0.z*V0.z <= V1.x*V1.x + V1.y*V1.y + V1.z*V1.z )
00927     {
00928       // P is closest to L.from
00929       t = V0.x*D.x + V0.y*D.y + V0.z*D.z;
00930       if ( t < -0.01 )
00931         return false;
00932     }
00933     else 
00934     {
00935       // P is closest to L.to
00936       t = 1.0 + V1.x*D.x + V1.y*D.y + V1.z*D.z;
00937       if ( t > 1.01 )
00938         return false;
00939     }
00940 
00941     // set Q = L.PointAt(t)
00942     s = 1.0-t;
00943     Q.x = s*L.from.x + t*L.to.x; Q.y = s*L.from.y + t*L.to.y; Q.z = s*L.from.z + t*L.to.z;
00944 
00945     if ( bDefaultTolerance )
00946     {
00947       if ( !ON_PointsAreCoincident(3,0,&P.x,&Q.x) )
00948         return false;
00949     }
00950     else
00951     {
00952       // verify that the distance from P to Q is <= tolerance
00953       s = P.x-Q.x;
00954       d = tol2 - s*s;
00955       if ( d < 0.0 )
00956         return false;
00957       s = P.y-Q.y;
00958       d -= s*s;
00959       if ( d < 0.0 )
00960         return false;
00961       s = P.z-Q.z;
00962       d -= s*s;
00963       if ( d < 0.0 )
00964         return false;
00965     }
00966 
00967     if ( t > t0 && t0 < 1.0 )
00968     {
00969       t0 = (t < 1.0) ? t : 1.0;
00970     }
00971 
00972     if ( !(t >= t0 && t <= 1.0) )
00973     {
00974       // 14 Dec 2004
00975       //  Chuck and Dale Lear added this test to weed out
00976       //  garbage curves whose locus is a line but that self intersect.
00977       //  For example, circles that have been projected onto a plane 
00978       //  perpendicular to their axis are garbage and are not "linear".
00979 
00980       // either a (nearly) stacked control point or
00981       // the curve has reversed direction        
00982       P = L.PointAt(t0);
00983       d = Q.DistanceTo(P);
00984       if ( !(d <= tolerance) )
00985         return false; // curve has reversed direction ("garbage")
00986     }
00987   }
00988 
00989   return true;
00990 }
00991 
00992 int ON_NurbsCurve::IsPolyline(
00993       ON_SimpleArray<ON_3dPoint>* pline_points,
00994       ON_SimpleArray<double>* pline_t
00995       ) const
00996 {
00997   int i;
00998   int rc = 0;
00999   if ( pline_points )
01000     pline_points->SetCount(0);
01001   if ( pline_t )
01002     pline_t->SetCount(0);
01003   if ( IsValid() )
01004   {
01005     if ( 2 == m_order )
01006     {
01007       rc = m_cv_count;
01008       if ( pline_points )
01009       {
01010         pline_points->Reserve(m_cv_count);
01011         for ( i = 0; i < m_cv_count; i++ )
01012         {
01013           GetCV(i,pline_points->AppendNew());
01014         }
01015       }
01016       if ( pline_t )
01017       {
01018         pline_t->Reserve(m_cv_count);
01019         for ( i = 0; i < m_cv_count; i++ )
01020           pline_t->Append(m_knot[i]);
01021       }
01022     }
01023     else if ( m_order > 2 && m_dim >= 2 && m_dim <= 3 )
01024     {
01025       // 16 April 2003 Dale Lear:
01026       //     Added to so the high degree polylines get
01027       //     flagged as polylines.
01028       const double *k = m_knot;
01029       double t;
01030       int i, j, span_count = m_cv_count-m_order+1;
01031       ON_Line line;
01032       ON_3dPoint P, Q;
01033       GetCV(0,line.to);
01034       for ( i = 0; i < span_count; i++, k++ )
01035       {
01036         if ( k[m_order-2] < k[m_order-1] )
01037         {
01038           if ( k[0] != k[m_order-2] )
01039           {
01040             // not a bezier span
01041             return 0;
01042           }
01043           if ( k[m_order-1] != k[2*m_order-3] )
01044           {
01045             // not a bezier span
01046             return 0;
01047           }
01048 
01049           // see if this bezier segment is a line
01050           // with (roughly) linear parameterization.
01051           line.from = line.to;
01052           GetCV(i+m_order-1,line.to);
01053           for ( j = 1; j < m_order-1; j++ )
01054           {
01055             GetCV(i+j,P);
01056             t = 0.0;
01057             if ( !line.ClosestPointTo(P,&t) )
01058               return 0;
01059             if ( fabs( t*(m_order-1) - j ) > 0.01 )
01060             {
01061               // not linearly parameterized.
01062               // Please check with Dale Lear before changing
01063               // this test.
01064               return 0;
01065             }
01066             Q = line.PointAt(t);
01067             // Please check with Dale Lear before changing
01068             // this test.
01069             if ( fabs(P.x - Q.x) > (fabs(P.x) + fabs(Q.x))*0.00001 + ON_ZERO_TOLERANCE )
01070               return 0;
01071             if ( fabs(P.y - Q.y) > (fabs(P.y) + fabs(Q.y))*0.00001 + ON_ZERO_TOLERANCE )
01072               return 0;
01073             if ( fabs(P.z - Q.z) > (fabs(P.z) + fabs(Q.z))*0.00001 + ON_ZERO_TOLERANCE )
01074               return 0;
01075           }
01076           rc++;
01077         }
01078       }
01079 
01080       if (rc > 0 )
01081       {
01082         rc++;
01083         // it's a polyline
01084         if ( 0 != pline_points || 0 != pline_t )
01085         {
01086           GetCV(0,P);
01087           if ( 0 != pline_points )
01088           {
01089             pline_points->Reserve(rc);
01090             GetCV(0,pline_points->AppendNew());
01091           }
01092           if ( 0 != pline_t )
01093           {
01094             pline_t->Reserve(rc);
01095             pline_t->Append( m_knot[m_order-2] );
01096           }
01097 
01098           const double *k = m_knot;
01099           for ( i = 0; i < span_count; i++, k++ )
01100           {
01101             if ( k[m_order-2] < k[m_order-1] )
01102             {
01103               // see if this bezier segment is a line
01104               // with (roughly) linear parameterization.
01105               if ( 0 != pline_points )
01106                 GetCV(i+m_order-1,pline_points->AppendNew());
01107               if ( 0 != pline_t )
01108                 pline_t->Append(k[m_order-1]);
01109             }
01110           }
01111         }
01112       }
01113     }
01114 
01115     if( IsClosed() && rc >= 4 && 0 != pline_points )
01116     {
01117       // GBA 2/26/03
01118       // Make polyline spot on closed.  
01119       *pline_points->Last() = *pline_points->First();
01120     }
01121 
01122   }
01123   return rc;
01124 }
01125 
01126 
01127 
01128 ON_BOOL32
01129 ON_NurbsCurve::IsArc(
01130       const ON_Plane* plane,
01131       ON_Arc* arc,
01132       double tolerance
01133       ) const
01134 {
01135   // If changes are made, verify that RR 8497 still works.
01136   // On 30 April 2003, Mikko changed the strict test to a sloppy
01137   // test to fix RR 8497.  This change broke other things like 
01138   // IGES simple entity export tests that require a strict test.
01139   // So on 6 May 2003 Dale Lear added in the "bLooseTest" check.
01140   // If the tolerance > ON_ZERO_TOLERANCE a fuzzy fit to arc
01141   // arc test is performed.  If tolerance <= ON_ZERO_TOLERANCE,
01142   // a stricter arc test is performed.
01143   const bool bLooseTest = (tolerance > ON_ZERO_TOLERANCE);
01144   const int knotcount = KnotCount();
01145   const int degree = m_order-1;
01146   if ( (2 == m_dim || 3 == m_dim)
01147        && m_cv_count >= m_order
01148        && degree >= 2
01149        && 0 != m_knot
01150        && 0 != m_cv
01151        && (bLooseTest || 0 != m_is_rat)
01152        )
01153   {
01154     if ( bLooseTest || 0 == (knotcount % degree) )
01155     {
01156 
01157       if ( !bLooseTest )
01158       {
01159         for ( int i = 0; i < m_cv_count; i += degree )
01160         {
01161           if ( m_knot[i] != m_knot[i+degree-1] )
01162             return false;
01163         }
01164       }
01165 
01166       // 24 July 2012 Dale Lear
01167       //   You can't be a line and an arc.  This test prevents
01168       //   creating arcs with small angles and large radii and
01169       //   prevents sloppy tolerances from classifying a span
01170       //   as an arc when it makes no sense.
01171       if ( IsLinear(tolerance) )
01172         return false;
01173 
01174       if ( ON_Curve::IsArc( plane, arc, tolerance ) )
01175         return true;
01176     }
01177   }
01178 
01179   return false;
01180 }
01181 
01182 ON_BOOL32
01183 ON_NurbsCurve::IsPlanar(
01184       ON_Plane* plane, // if not NULL and true is returned, then plane parameters
01185                          // are filled in
01186       double tolerance // tolerance to use when checking linearity
01187       ) const
01188 {
01189   if ( m_dim == 2 )
01190   {
01191     return ON_Curve::IsPlanar(plane,tolerance);
01192   }
01193 
01194   ON_BOOL32 rc = false;
01195   ON_3dPoint P;
01196   ON_3dVector X;
01197   EvTangent( Domain()[0], P, X );
01198   if ( IsLinear(tolerance) )
01199   {
01200     if ( plane ) 
01201     {
01202       ON_Line line( P, PointAtEnd() );
01203       if ( !line.InPlane( *plane, tolerance) )
01204         line.InPlane( *plane, 0.0 );
01205     }
01206     rc = true;
01207   }
01208   else if ( m_cv_count >= 3 )
01209   {
01210     // set P, Q, R to corners of biggest triangle
01211     // with corners at control points.
01212     ON_Plane test_plane;
01213     ON_3dPoint A, B, Q, R;
01214     Q = P;
01215     R = P;
01216     double d, maxd = 0.0;
01217     int i, j, k;
01218     k = m_cv_count/64; // to keep sample time reasonable on giant NURBS
01219     if ( k < 1 )
01220       k = 1;
01221     for ( i = 1; i < m_cv_count; i += k )
01222     {
01223       GetCV(i,A);
01224       for ( j = i+k; j < m_cv_count; j += k )
01225       {
01226         GetCV(j,B);
01227         d = ON_CrossProduct( A-P, B-P ).Length();
01228         if ( d > maxd )
01229         {
01230           maxd = d;
01231           Q = A;
01232           R = B;
01233         }
01234       }
01235     }
01236     if ( test_plane.CreateFromPoints(P,Q,R) )
01237     {
01238       ON_2dVector v( X*test_plane.xaxis, X*test_plane.yaxis );
01239       if ( v.Unitize() )
01240       {
01241         // Rotate test_plane's x,y axes so its x axis is parallel
01242         // to the curve's initial tangent.
01243         if ( fabs(v.y) <= ON_SQRT_EPSILON )
01244         {
01245           v.x = (v.x >= 0.0) ? 1.0 : -1.0;
01246           v.y = 0.0;
01247         }
01248         else if ( fabs(v.x) <= ON_SQRT_EPSILON )
01249         {
01250           v.y = (v.y >= 0.0) ? 1.0 : -1.0;
01251           v.x = 0.0;
01252         }
01253         X = test_plane.xaxis;
01254         ON_3dVector Y = test_plane.yaxis;
01255         test_plane.xaxis = v.x*X + v.y*Y;
01256         test_plane.yaxis = v.x*Y - v.y*X;
01257       }
01258       rc = IsInPlane( test_plane, tolerance );
01259       if ( rc && plane )
01260         *plane = test_plane;
01261     }
01262   }
01263   return rc;
01264 }
01265 
01266 ON_BOOL32
01267 ON_NurbsCurve::IsInPlane(
01268       const ON_Plane& plane, // plane to test
01269       double tolerance // tolerance to use when checking linearity
01270       ) const
01271 {
01272   ON_BOOL32 rc = IsValid();
01273   ON_3dPoint P;
01274   int i;
01275   for ( i = 0; rc && i < m_cv_count; i++ )
01276   {
01277     GetCV(i,P);
01278     if ( fabs( plane.DistanceTo(P)) > tolerance)
01279       rc = false;
01280   }
01281   return rc;
01282 }
01283 
01284 ON_BOOL32
01285 ON_NurbsCurve::GetParameterTolerance( // returns tminus < tplus: parameters tminus <= s <= tplus
01286        double t,       // t = parameter in domain
01287        double* tminus, // tminus
01288        double* tplus   // tplus
01289        ) const
01290 {
01291   ON_BOOL32 rc = false;
01292   ON_Interval d = Domain();
01293   if ( d.IsIncreasing() ) {
01294     const double* knot = Knot();
01295     const int order = Order();
01296     const int cv_count = CVCount();
01297     if ( t < knot[order-1] )
01298       d.m_t[1] = knot[order-1];
01299     else if ( t > knot[cv_count-2] )
01300       d.m_t[0] = knot[cv_count-2];
01301     rc = ON_GetParameterTolerance( d.m_t[0], d.m_t[1], t, tminus, tplus );
01302   }
01303   return rc;
01304 }
01305 
01306 ON_BOOL32 
01307 ON_NurbsCurve::Evaluate( // returns false if unable to evaluate
01308        double t,       // evaluation parameter
01309        int der_count,  // number of derivatives (>=0)
01310        int v_stride,   // v[] array stride (>=Dimension())
01311        double* v,      // v[] array of length stride*(ndir+1)
01312        int side,       // optional - determines which side to evaluate from
01313                        //         0 = default
01314                        //      <  0 to evaluate from below, 
01315                        //      >  0 to evaluate from above
01316        int* hint       // optional - evaluation hint (int) used to speed
01317                        //            repeated evaluations
01318        ) const
01319 {
01320   ON_BOOL32 rc = false;
01321 
01322   if( m_order<2)      // GBA added 01-12-06 to fix crash bug
01323      return false;
01324 
01325   int span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,side,(hint)?*hint:0);
01326 
01327   if ( -2 == side || 2 == side )
01328   {
01329     // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix
01330     //   When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization
01331     //   and the original side parameter was -1 or +1, it is changed to -2 or +2
01332     //   to indicate that if t is numerically closed to an end paramter, then
01333     //   it should be tuned up to be at the end paramter.
01334     double a = t;
01335     if ( ON_TuneupEvaluationParameter( side, m_knot[span_index+m_order-2], m_knot[span_index+m_order-1], &a) )
01336     {
01337       // recalculate span_index
01338       t = a;
01339       span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,side,span_index);
01340     }
01341   }
01342 
01343   rc = ON_EvaluateNurbsSpan(
01344      m_dim, m_is_rat, m_order, 
01345      m_knot + span_index, 
01346      m_cv_stride, m_cv + (m_cv_stride*span_index),
01347      der_count, 
01348      t,
01349      v_stride, v 
01350      );
01351   if ( hint ) 
01352     *hint = span_index;
01353   return rc;
01354 }
01355 
01356 
01357 ON_BOOL32 
01358 ON_NurbsCurve::IsClosed() const
01359 {
01360   ON_BOOL32 bIsClosed = false;
01361   if ( m_dim > 0 && m_cv_count >= 4 ) 
01362   {
01363     if ( IsPeriodic() ) {
01364       bIsClosed = true;
01365     }
01366     else {
01367       bIsClosed = ON_Curve::IsClosed();
01368     }
01369   }
01370   return bIsClosed;
01371 }
01372 
01373 ON_BOOL32 
01374 ON_NurbsCurve::IsPeriodic() const
01375 {
01376   ON_BOOL32 bIsPeriodic = ON_IsKnotVectorPeriodic( m_order, m_cv_count, m_knot );
01377   if ( bIsPeriodic ) {
01378     int i0 = m_order-2;
01379     int i1 = m_cv_count-1;
01380     const double* cv0 = m_cv + i0*m_cv_stride;
01381     const double* cv1 = m_cv + i1*m_cv_stride;
01382     for ( /*empty*/; i0 >= 0; i0--, i1-- ) {
01383       if ( false == ON_PointsAreCoincident( m_dim, m_is_rat, cv0, cv1 ) )
01384         return false;
01385       cv0 -= m_cv_stride;
01386       cv1 -= m_cv_stride;      
01387     }
01388   }
01389   return bIsPeriodic;
01390 }
01391 
01392 
01393 bool ON_IsCurvatureDiscontinuity( 
01394   const ON_3dVector Km, 
01395   const ON_3dVector Kp,
01396   double cos_angle_tolerance,
01397   double curvature_tolerance,
01398   double zero_curvature,
01399   double radius_tolerance
01400   )
01401 {
01402   // This function is provided so old code will link.
01403   // New code should use the version with an explicit relative_tolerance
01404   return ON_IsCurvatureDiscontinuity( Km, Kp, 
01405     cos_angle_tolerance, 
01406     curvature_tolerance,
01407     zero_curvature,
01408     radius_tolerance,
01409     0.001 // relative_tolerance - used to be hard coded in this version of the function
01410     );
01411 }
01412 
01413 bool ON_IsG2CurvatureContinuous(
01414   const ON_3dVector Km, 
01415   const ON_3dVector Kp,
01416   double cos_angle_tolerance,
01417   double curvature_tolerance
01418   )
01419 {
01420   // 6 November 2010 Dale Lear
01421   //  I made up these values used for cos_Kangle_tolerance
01422   //  and rel_tol today.  They may need adjusting as we 
01423   //  get test results.
01424   double rel_tol = 0.05; // disables using curvature_tolerance to flag unequal scalars
01425 
01426   double cos_Kangle_tolerance = cos_angle_tolerance;
01427   if ( cos_Kangle_tolerance > ON_DEFAULT_ANGLE_TOLERANCE_COSINE )
01428     cos_Kangle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE_COSINE;
01429   if ( cos_Kangle_tolerance > 0.95 )
01430   {
01431     // double the tangent angle
01432     if ( cos_angle_tolerance < 0.0 )
01433     {
01434       cos_Kangle_tolerance = -1.0;
01435     }
01436     else
01437     {
01438       cos_Kangle_tolerance = 2.0*cos_Kangle_tolerance*cos_Kangle_tolerance - 1.0; // = cos(2*tangent_angle_tolerace)
01439       if ( cos_angle_tolerance >= 0.0 && cos_Kangle_tolerance < 0.0 )
01440         cos_Kangle_tolerance = 0.0;
01441     }
01442   }
01443 
01444   return !ON_IsCurvatureDiscontinuity(Km,Kp,
01445             cos_Kangle_tolerance,
01446             curvature_tolerance,
01447             ON_ZERO_CURVATURE_TOLERANCE,
01448             ON_UNSET_VALUE, // no absolute delta radius tolerance
01449             rel_tol
01450             );
01451 }
01452 
01453 bool ON_IsGsmoothCurvatureContinuous(
01454   const ON_3dVector Km, 
01455   const ON_3dVector Kp,
01456   double cos_angle_tolerance,
01457   double curvature_tolerance
01458   )
01459 {
01460   double rel_tol = 1.0; // disables using curvature_tolerance to flag unequal scalars
01461 
01462   // point of inflection angle >= 90 degrees
01463   double cos_Kangle_tolerance = 0.0;
01464 
01465   // Since cos_Kangle_tolerance and rel_tol are valid settings,
01466   // curvature_tolerance is used only to test for equality.
01467   return !ON_IsCurvatureDiscontinuity(Km,Kp,
01468             cos_Kangle_tolerance,
01469             curvature_tolerance,
01470             ON_ZERO_CURVATURE_TOLERANCE,
01471             ON_UNSET_VALUE, // no absolute delta radius tolerance
01472             rel_tol
01473             );
01474 }
01475 
01476 bool ON_IsCurvatureDiscontinuity( 
01477   const ON_3dVector Km, 
01478   const ON_3dVector Kp,
01479   double cos_angle_tolerance,
01480   double curvature_tolerance,
01481   double zero_curvature,
01482   double radius_tolerance,
01483   double relative_tolerance
01484   )
01485 {
01486   const double d = (Km-Kp).Length();
01487   if ( !ON_IsValid(d) )
01488   {
01489     // invalid curvatures - call it discontinuous because
01490     // there is a good chance the derivative evaluation 
01491     // failed or the first derivative is zero.
01492     return true;
01493   }
01494 
01495   // The d <= 0.0 test handles the case when 
01496   // curvature_tolerance is ON_UNSET_VALUE
01497   if ( d <= 0.0 || d <= curvature_tolerance )
01498     return false; // "equal" curvature vectors
01499 
01500   // If the scalar curvature is <= zero_curvature,
01501   // then K is small enough that we assume it is zero.
01502   // The primary reason for doing this is to prevent
01503   // reporting a curvature discontinuity when one
01504   // or both of the curvatures is small1.0e-300 and 1.0e-200
01505   if ( !(zero_curvature > 7.7037197787136e-34) )
01506     zero_curvature = 7.7037197787136e-34;
01507   double km = Km.Length();
01508   double kp = Kp.Length();
01509   // If km or kp are NaNs, I treat them as zero so
01510   // the rest of this code can assume they are 0.0
01511   // or positive.
01512   if ( !(km > zero_curvature) )
01513     km = 0.0;
01514   if ( !(kp > zero_curvature) )
01515   {
01516     kp = 0.0;
01517     if ( 0.0 == km )
01518     {
01519       // both curvatures are "zero"
01520       return false;
01521     }
01522   }
01523 
01524   if ( !(km > 0.0 && kp > 0.0) )
01525   {
01526     // one side is flat and the other is curved.
01527     return true;
01528   }
01529 
01530   if ( 0.0 == curvature_tolerance )
01531   {
01532     // User is asking for exact match.
01533     return true;
01534   }
01535 
01536 
01537   // At this point we know km > 0 and kp > 0.
01538 
01539   bool bPointOfInflection = (curvature_tolerance > 0.0);
01540   bool bDifferentScalars = bPointOfInflection;
01541   // NOTE: 
01542   //   At this point either curvature_tolerance was not
01543   //   specified or |Km - Kp| > curvature_tolerance. Because
01544   //   it is difficult to come up with a meaningful value for
01545   //   curvature_tolerance that works at all scales, the
01546   //   additional tests below are used to determine if 
01547   //   Km and Kp are different enough to matter. If additional
01548   //   tests are performed, then bTestCurvatureTolerance
01549   //   is set to false. If no additional tests are performed,
01550   //   then bTestCurvatureTolerance will remain true and
01551   //   the |Km-Kp| > curvature_tolerance test is performed
01552   //   at the end of this function.
01553 
01554   if ( cos_angle_tolerance >= -1.0 && cos_angle_tolerance <= 1.0 )
01555   {
01556     const double KmoKp = Kp*Km;
01557     if ( KmoKp < km*kp*cos_angle_tolerance )
01558       return true; // Km and Kp are not parallel
01559     bPointOfInflection = false;
01560   }
01561 
01562   // At this point we assume Km and Kp are parallel and we
01563   // know km > 0 and kp > 0.
01564   if ( radius_tolerance >= 0.0 )
01565   {
01566     // This test is if (fabs(rm - rp) > radius_tolerance) return true
01567     // where rm = 1.0/km and rp = 1.0/kp.  It is coded the way
01568     // it is to avoid divides (not that that really matters any more).
01569     if ( fabs(km-kp) > kp*km*radius_tolerance )
01570       // radii do not agree
01571       return true;
01572     bDifferentScalars = false;
01573   }
01574 
01575   if ( relative_tolerance > 0.0 )
01576   {
01577     if ( fabs(km-kp) > ((km>kp)?km:kp)*relative_tolerance )
01578     {
01579       return true;
01580     }
01581     bDifferentScalars = false;
01582   }
01583 
01584   if ( bPointOfInflection || bDifferentScalars )
01585   {
01586     // |Km - Kp| > curvature_tolerance and we were not asked to
01587     // perform and other tests.
01588     return true;
01589   }
01590 
01591   return false; // treat Km and Kp as equal curvatures.
01592 }
01593 
01594 
01595 bool ON_NurbsCurve::GetNextDiscontinuity( 
01596                 ON::continuity c,
01597                 double t0,
01598                 double t1,
01599                 double* t,
01600                 int* hint,
01601                 int* dtype,
01602                 double cos_angle_tolerance,
01603                 double curvature_tolerance
01604                 ) const
01605 {
01606   const double is_linear_tolerance = 1.0e-8;  
01607   const double is_linear_min_length = 1.0e-8;
01608   int tmp_hint = 0, tmp_dtype=0;
01609   double d, tmp_t;
01610   ON_3dPoint Pm, Pp;
01611   ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km, Kp;
01612   int ki;
01613   if ( !hint )
01614     hint = &tmp_hint;
01615   if ( !dtype )
01616     dtype = &tmp_dtype;
01617   if ( !t )
01618     t = &tmp_t;
01619   
01620   if ( c == ON::C0_continuous )
01621     return false;
01622   if ( c == ON::C0_locus_continuous )
01623   {
01624     return ON_Curve::GetNextDiscontinuity( c, t0, t1, t, hint, dtype, 
01625                                     cos_angle_tolerance, curvature_tolerance );
01626   }
01627   if ( t0 == t1 )
01628     return false;
01629 
01630   // First test for parametric discontinuities.  If none are found
01631   // then we will look for locus discontinuities at ends
01632   if ( m_order <= 2 )
01633     c = ON::PolylineContinuity(c); // no need to check 2nd derivatives that are zero
01634   const ON::continuity input_c = c;
01635   c = ON::ParametricContinuity(c);
01636   bool bEv2ndDer    = (c == ON::C2_continuous || c == ON::G2_continuous || c == ON::Gsmooth_continuous) && (m_order>2);
01637   bool bTestKappa   = ( bEv2ndDer && c != ON::C2_continuous );
01638   bool bTestTangent = ( bTestKappa || c == ON::G1_continuous );
01639 
01640   int delta_ki = 1;
01641   int delta = ((bEv2ndDer) ? 3 : 2) - m_order; // delta <= 0
01642   if ( ON::Cinfinity_continuous == c )
01643     delta = 0;
01644 
01645   ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,(t0>t1)?-1:1,*hint);
01646   double segtol = (fabs(m_knot[ki]) + fabs(m_knot[ki+1]) + fabs(m_knot[ki+1]-m_knot[ki]))*ON_SQRT_EPSILON;
01647 
01648   const bool bLineWiggleTest = (c == ON::Gsmooth_continuous && m_order >= 4);
01649   bool bSpanIsLinear = false;
01650 
01651   if ( t0 < t1 )
01652   {
01653     int ii = ki+m_order-2;
01654     if ( t0 < m_knot[ii+1] && t1 > m_knot[ii+1] && (m_knot[ii+1]-t0) <= segtol && ii+2 < m_cv_count )
01655     {
01656       t0 = m_knot[ii+1];
01657       ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,1,*hint);
01658     }
01659     if ( bLineWiggleTest )
01660       bSpanIsLinear = SpanIsLinear(ki,is_linear_min_length,is_linear_tolerance);
01661     *hint = ki;
01662     ki += m_order-2;
01663     while (ki < m_cv_count-1 && m_knot[ki] <= t0) 
01664       ki++;
01665     if (ki >= m_cv_count-1) 
01666     {
01667       if ( input_c != c && t0 < m_knot[m_cv_count-1] && t1 >= m_knot[m_cv_count-1] )
01668       {
01669         // have to do locus end test
01670         return ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, 
01671                                     cos_angle_tolerance, curvature_tolerance );
01672       }
01673       return false;
01674     }
01675   }
01676   else
01677   {
01678     // (t0 > t1) work backwards
01679     int ii = ki+m_order-2;
01680     if ( t0 > m_knot[ii] && t1 < m_knot[ii] && (t0-m_knot[ii]) <= segtol && ii > m_order-2 )
01681     {
01682       t0 = m_knot[ii];
01683       ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t0,-1,*hint);
01684     }
01685     if ( bLineWiggleTest )
01686       bSpanIsLinear = SpanIsLinear(ki,is_linear_min_length,is_linear_tolerance);
01687     *hint = ki;
01688     ki += m_order-2;
01689     while (ki > m_order-2 && m_knot[ki] >= t0) 
01690       ki--;
01691     if (ki <= m_order-2) 
01692     {
01693       if ( input_c != c && t0 > m_knot[m_order-2] && t1 < m_knot[m_order-2] )
01694       {
01695         // have to do locus end test
01696         return ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, 
01697                                     cos_angle_tolerance, curvature_tolerance );
01698       }
01699       return false;
01700     }
01701     delta_ki = -1;
01702     delta = -delta;
01703   }
01704 
01705   double search_domain[2];
01706   if ( t0 <= t1 )
01707   {
01708     search_domain[0] = t0;
01709     search_domain[1] = t1;
01710   }
01711   else
01712   {
01713     search_domain[0] = t1;
01714     search_domain[1] = t0;
01715   }
01716 
01717   while ( search_domain[0] < m_knot[ki] && m_knot[ki] < search_domain[1] ) 
01718   {
01719     if ( delta_ki > 0 )
01720     {
01721       // t0 < t1 case
01722       while (ki < m_cv_count-1 && m_knot[ki] == m_knot[ki+1])
01723         ki++;
01724       if (ki >= m_cv_count-1) 
01725         break;    
01726     }
01727     else
01728     {
01729       // t0 > t1 case
01730       // 20 March 2003 Dale Lear:
01731       //     Added to make t0 > t1 case work
01732       while (ki > m_order-2 && m_knot[ki] == m_knot[ki-1])
01733         ki--;
01734       if (ki <= m_order-2) 
01735         break;    
01736     }
01737 
01738     if (m_knot[ki] == m_knot[ki+delta]) 
01739     {  
01740       if ( ON::Cinfinity_continuous == c )
01741       {
01742         // Cinfinity_continuous is treated as asking for the next knot
01743         *dtype = 3;
01744         *t = m_knot[ki];
01745         return true;
01746       }
01747 
01748       if ( bEv2ndDer ) {
01749         Ev2Der( m_knot[ki], Pm, D1m, D2m, -1, hint );
01750         Ev2Der( m_knot[ki], Pp, D1p, D2p,  1, hint );
01751       }
01752       else {
01753         Ev1Der( m_knot[ki], Pm, D1m, -1, hint );
01754         Ev1Der( m_knot[ki], Pp, D1p,  1, hint );
01755       }
01756 
01757       if ( bTestTangent )
01758       {
01759         if ( bTestKappa )
01760         {
01761           ON_EvCurvature( D1m, D2m, Tm, Km );
01762           ON_EvCurvature( D1p, D2p, Tp, Kp );
01763         }
01764         else
01765         {
01766           Tm = D1m;
01767           Tp = D1p;
01768           Tm.Unitize();
01769           Tp.Unitize();
01770         }
01771         d = Tm*Tp;
01772         if ( d < cos_angle_tolerance )
01773         {
01774           *dtype = 1;
01775           *t = m_knot[ki];
01776           return true;
01777         }
01778         else if ( bTestKappa )
01779         {
01780           bool bIsCurvatureContinuous = ( ON::Gsmooth_continuous == c )
01781                   ? ON_IsGsmoothCurvatureContinuous( Km, Kp, cos_angle_tolerance, curvature_tolerance )
01782                   : ON_IsG2CurvatureContinuous( Km, Kp, cos_angle_tolerance, curvature_tolerance );
01783           if ( !bIsCurvatureContinuous )
01784           {
01785             // NOTE:
01786             //   The test to enter this scope must exactly match
01787             //   the one used in ON_PolyCurve::GetNextDiscontinuity()
01788             //   and  ON_Curve::GetNextDiscontinuity().
01789             *dtype = 2;
01790             *t = m_knot[ki];
01791             return true;
01792           }
01793           if ( bLineWiggleTest )
01794           {
01795             if ( bSpanIsLinear != (( delta_ki < 0 )
01796                                   ? SpanIsLinear(ki - m_order + 1,is_linear_min_length,is_linear_tolerance)
01797                                   : SpanIsLinear(ki - m_order + 2,is_linear_min_length,is_linear_tolerance))
01798                )
01799             {
01800               // we are at a transition between a line segment and a wiggle
01801               *dtype = 3;
01802               *t = m_knot[ki];
01803               return true;
01804             }
01805           }
01806         }
01807       }
01808       else
01809       {
01810         if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) )
01811         {
01812           *dtype = 1;
01813           *t = m_knot[ki];
01814           return true;
01815         }
01816         else if ( bEv2ndDer )
01817         {
01818           if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) )
01819           {
01820             *dtype = 2;
01821             *t = m_knot[ki];
01822             return true;
01823           }         
01824         }
01825       }
01826     }
01827     ki += delta_ki;
01828   }
01829 
01830   // 20 March 2003 Dale Lear:
01831   //   If we get here, there are not discontinuities strictly between
01832   //   t0 and t1.
01833   bool rc = false;
01834 
01835   if ( input_c != c )
01836   {
01837     // use base class for consistent start/end locus testing 
01838     rc = ON_Curve::GetNextDiscontinuity( input_c, t0, t1, t, hint, dtype, 
01839                                     cos_angle_tolerance, curvature_tolerance );
01840   }
01841 
01842   return rc;
01843 }
01844 
01845 bool ON_NurbsCurve::IsContinuous(
01846     ON::continuity desired_continuity,
01847     double t, 
01848     int* hint, // default = NULL,
01849     double point_tolerance, // default=ON_ZERO_TOLERANCE
01850     double d1_tolerance, // default==ON_ZERO_TOLERANCE
01851     double d2_tolerance, // default==ON_ZERO_TOLERANCE
01852     double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE
01853     double curvature_tolerance  // default==ON_SQRT_EPSILON
01854     ) const
01855 {
01856   bool rc = true;
01857 
01858   if ( m_order <= 2 )
01859     desired_continuity = ON::PolylineContinuity(desired_continuity);
01860 
01861   if ( t <= m_knot[m_order-2] || t >= m_knot[m_cv_count-1] )
01862   {
01863     // 20 March 2003 Dale Lear
01864     //     Consistently handles locus case and out of domain case.
01865     rc = ON_Curve::IsContinuous( 
01866                desired_continuity, t, hint, 
01867                point_tolerance, 
01868                d1_tolerance, d2_tolerance, 
01869                cos_angle_tolerance, 
01870                curvature_tolerance );
01871     return rc;
01872   }
01873 
01874   // "locus" and "parametric" are the same at this point.
01875   desired_continuity = ON::ParametricContinuity(desired_continuity);
01876 
01877   if ( m_order < m_cv_count && desired_continuity != ON::C0_continuous )
01878   {
01879     int tmp_hint;
01880     if ( !hint )
01881     {
01882       tmp_hint = 0;
01883       hint = &tmp_hint;
01884     }
01885     int ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,*hint);
01886 
01887     {
01888       // 20 March 2003 Dale Lear:
01889       //     If t is very near interior m_t[] value, see if it
01890       //     should be set to that value.  A bit or two of 
01891       //     precision sometimes gets lost in proxy
01892       //     domain to real curve domain conversions on the interior
01893       //     of a curve domain.
01894       int ii = ki+m_order-2;
01895       double segtol = (fabs(m_knot[ii]) + fabs(m_knot[ii+1]) + fabs(m_knot[ii+1]-m_knot[ii]))*ON_SQRT_EPSILON;
01896       if ( m_knot[ii]+segtol < m_knot[ii+1]-segtol )
01897       {
01898         if ( fabs(t-m_knot[ii]) <= segtol && ii > m_order-2 )
01899         {
01900           t = m_knot[ii];
01901         }
01902         else if ( fabs(t-m_knot[ii+1]) <= segtol && ii+2 < m_cv_count )
01903         {
01904           t = m_knot[ii+1];
01905           ki = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,*hint);
01906         }
01907       }
01908     }
01909 
01910     if ( ki < 0 )
01911       ki = 0;
01912     *hint = ki;
01913     ki += m_order-2;
01914     if ( ki > m_order-2 && ki < m_cv_count-1 && m_knot[ki] == t  )
01915     {
01916       if ( ON::Cinfinity_continuous == desired_continuity )
01917       {
01918         // Cinfinity_continuous is a euphanisim for "at a knot"
01919         return false;
01920       }
01921 
01922       // t = interior knot value - check for discontinuity
01923       int knot_mult = ON_KnotMultiplicity( m_order, m_cv_count, m_knot, ki );
01924 
01925       switch(desired_continuity)
01926       {
01927       case ON::C2_continuous: 
01928         if ( m_order - knot_mult >= 3 )
01929           return true;
01930         break;
01931       case ON::C1_continuous: 
01932         if ( m_order - knot_mult >= 2 )
01933           return true;
01934         break;
01935       case ON::G2_continuous: 
01936       case ON::Gsmooth_continuous: 
01937         if ( m_order - knot_mult >= 3 )
01938           return true;
01939         break;
01940       case ON::G1_continuous: 
01941         if ( m_order - knot_mult >= 2 )
01942           return true;
01943         break;
01944       default:
01945         // intentionally ignoring other ON::continuity enum values
01946         break;
01947       }
01948  
01949       // need to evaluate at knot
01950       rc = ON_Curve::IsContinuous( desired_continuity, t, hint, 
01951                            point_tolerance, d1_tolerance, d2_tolerance, 
01952                            cos_angle_tolerance, curvature_tolerance );
01953 
01954       if ( rc 
01955            && ON::Gsmooth_continuous == desired_continuity 
01956            && knot_mult == m_order-1 
01957            && ki > m_order-2
01958            && ki < m_cv_count-1
01959            )
01960       {
01961         // See if we are transitioning from linear to non-linear
01962         const double is_linear_tolerance = 1.0e-8;  
01963         const double is_linear_min_length = 1.0e-8;
01964         if ( SpanIsLinear(ki - m_order + 2,is_linear_min_length,is_linear_tolerance) != SpanIsLinear(ki - 2*m_order + 3,is_linear_min_length,is_linear_tolerance) )
01965           rc = false;
01966         
01967       }
01968     }
01969   }
01970   return rc;
01971 }
01972 
01973 
01974 ON_BOOL32 
01975 ON_NurbsCurve::SetWeight( int i, double w )
01976 {
01977   ON_BOOL32 rc = false;
01978   if ( m_is_rat ) {
01979     double* cv = CV(i);
01980     if (cv) {
01981       cv[m_dim] = w;
01982       rc = true;
01983     }
01984   }
01985   else if ( w == 1.0 ) {
01986     rc = true;
01987   }
01988         DestroyCurveTree();
01989   return rc;
01990 }
01991 
01992 ON_BOOL32 
01993 ON_NurbsCurve::SetCV( int i, ON::point_style style, const double* Point )
01994 {
01995   ON_BOOL32 rc = true;
01996   int k;
01997   double w;
01998 
01999   // feeble but fast check for properly initialized class
02000   if ( !m_cv || i < 0 || i >= m_cv_count )
02001     return false;
02002 
02003   double* cv = m_cv + i*m_cv_stride;
02004 
02005   switch ( style ) {
02006 
02007   case ON::not_rational:  // input Point is not rational
02008     memcpy( cv, Point, m_dim*sizeof(*cv) );
02009     if ( IsRational() ) {
02010       // NURBS curve is rational - set weight to one
02011       cv[m_dim] = 1.0;
02012     }
02013     break;
02014 
02015   case ON::homogeneous_rational:  // input Point is homogeneous rational
02016     if ( IsRational() ) {
02017       // NURBS curve is rational
02018       memcpy( cv, Point, (m_dim+1)*sizeof(*cv) );
02019     }
02020     else {
02021       // NURBS curve is not rational
02022       w = (Point[m_dim] != 0.0) ? 1.0/Point[m_dim] : 1.0;
02023       for ( k = 0; k < m_dim; k++ ) {
02024         cv[k] = w*Point[k];
02025       }
02026     }
02027     break;
02028 
02029   case ON::euclidean_rational:  // input Point is euclidean rational
02030     if ( IsRational() ) {
02031       // NURBS curve is rational - convert euclean point to homogeneous form
02032       w = Point[m_dim];
02033       for ( k = 0; k < m_dim; k++ )
02034         cv[k] = w*Point[k]; // 22 April 2003 - bug fix [i] to [k]
02035       cv[m_dim] = w;
02036     }
02037     else {
02038       // NURBS curve is not rational
02039       memcpy( cv, Point, m_dim*sizeof(*cv) );
02040     }
02041     break;
02042 
02043   case ON::intrinsic_point_style:
02044     memcpy( cv, Point, CVSize()*sizeof(*cv) );
02045     break;
02046     
02047   default:
02048     rc = false;
02049     break;
02050   }
02051         DestroyCurveTree();
02052   return rc;
02053 }
02054 
02055 ON_BOOL32 
02056 ON_NurbsCurve::SetCV( int i, const ON_3dPoint& point )
02057 {
02058   ON_BOOL32 rc = false;
02059   double* cv = CV(i);
02060   if ( cv ) {
02061     cv[0] = point.x;
02062     if ( m_dim > 1 ) {
02063       cv[1] = point.y;
02064       if ( m_dim > 2 )
02065         cv[2] = point.z;
02066       if ( m_dim > 3 ) {
02067         memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
02068       }
02069     }
02070     if ( m_is_rat ) {
02071       cv[m_dim] = 1.0;
02072     }
02073     rc = true;
02074   }
02075         DestroyCurveTree();
02076   return rc;
02077 }
02078 
02079 ON_BOOL32 
02080 ON_NurbsCurve::SetCV( int i, const ON_4dPoint& point )
02081 {
02082   ON_BOOL32 rc = false;
02083   double* cv = CV(i);
02084   if ( cv ) {
02085     if ( m_is_rat ) {
02086       cv[0] = point.x;
02087       if ( m_dim > 1 ) {
02088         cv[1] = point.y;
02089         if ( m_dim > 2 )
02090           cv[2] = point.z;
02091         if ( m_dim > 3 ) {
02092           memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
02093         }
02094       }
02095       cv[m_dim] = point.w;
02096       rc = true;
02097     }
02098     else {
02099       double w;
02100       if ( point.w != 0.0 ) {
02101         w = 1.0/point.w;
02102         rc = true;
02103       }
02104       else {
02105         w = 1.0;
02106       }
02107       cv[0] = w*point.x;
02108       if ( m_dim > 1 ) {
02109         cv[1] = w*point.y;
02110         if ( m_dim > 2 ) {
02111           cv[2] = w*point.z;
02112         }
02113         if ( m_dim > 3 ) {
02114           memset( &cv[3], 0, (m_dim-3)*sizeof(*cv) );
02115         }
02116       }
02117     }
02118   }
02119         DestroyCurveTree();
02120   return rc;
02121 }
02122 
02123 ON_BOOL32 
02124 ON_NurbsCurve::GetCV( int i, ON::point_style style, double* Point ) const
02125 {
02126   const double* cv = CV(i);
02127   if ( !cv )
02128     return false;
02129   int dim = Dimension();
02130   double w = ( IsRational() ) ? cv[dim] : 1.0;
02131   switch(style) {
02132   case ON::euclidean_rational:
02133     Point[dim] = w;
02134     // no break here
02135   case ON::not_rational:
02136     if ( w == 0.0 )
02137       return false;
02138     w = 1.0/w;
02139     while(dim--) *Point++ = *cv++ * w;
02140     break;
02141   case ON::homogeneous_rational:
02142     Point[dim] = w;
02143     memcpy( Point, cv, dim*sizeof(*Point) );
02144     break;
02145   case ON::intrinsic_point_style:
02146     memcpy( Point, cv, CVSize()*sizeof(*Point) );
02147     break;
02148   default:
02149     return false;
02150   }
02151   return true;
02152 }
02153 
02154 ON_BOOL32 
02155 ON_NurbsCurve::GetCV( int i, ON_3dPoint& point ) const
02156 {
02157   ON_BOOL32 rc = false;
02158   const double* cv = CV(i);
02159   if ( cv ) {
02160     if ( m_is_rat ) {
02161       if (cv[m_dim] != 0.0) {
02162         const double w = 1.0/cv[m_dim];
02163         point.x = cv[0]*w;
02164         point.y = (m_dim>1)? cv[1]*w : 0.0;
02165         point.z = (m_dim>2)? cv[2]*w : 0.0;
02166         rc = true;
02167       }
02168     }
02169     else {
02170       point.x = cv[0];
02171       point.y = (m_dim>1)? cv[1] : 0.0;
02172       point.z = (m_dim>2)? cv[2] : 0.0;
02173       rc = true;
02174     }
02175   }
02176   return rc;
02177 }
02178 
02179 ON_BOOL32 
02180 ON_NurbsCurve::GetCV( int i, ON_4dPoint& point ) const
02181 {
02182   ON_BOOL32 rc = false;
02183   const double* cv = CV(i);
02184   if ( cv ) {
02185     point.x = cv[0];
02186     point.y = (m_dim>1)? cv[1] : 0.0;
02187     point.z = (m_dim>2)? cv[2] : 0.0;
02188     point.w = (m_is_rat) ? cv[m_dim] : 1.0;
02189     rc = true;
02190   }
02191   return rc;
02192 }
02193 
02194 bool ON_NurbsCurve::SetKnot( int knot_index, double k )
02195 {
02196   if ( knot_index < 0 || knot_index >= KnotCount() )
02197     return false;
02198   m_knot[knot_index] = k;
02199         DestroyCurveTree();
02200   return true;
02201 }
02202 
02203 ON_BOOL32 ON_NurbsCurve::SetStartPoint(
02204         ON_3dPoint start_point
02205         )
02206 {
02207   ON_BOOL32 rc = false;
02208   if ( IsValid() )
02209   {
02210     if ( PointAtStart() == start_point )
02211     {
02212       rc = true;
02213     }
02214     else
02215     {
02216       ClampEnd(2);
02217 
02218       double w = 1.0;
02219       if (IsRational()) {
02220         w = Weight(0);
02221         start_point *= w;
02222       }
02223       SetCV(0,start_point);
02224       if (IsRational())
02225         SetWeight(0, w);
02226 
02227       DestroyCurveTree();
02228       rc = true;
02229     }
02230   }
02231   return rc;
02232 }
02233 
02234 ON_BOOL32 ON_NurbsCurve::SetEndPoint(
02235         ON_3dPoint end_point
02236         )
02237 {
02238   bool rc = false;
02239   if ( IsValid() )
02240   {
02241     if ( PointAtEnd() == end_point )
02242     {
02243       rc = true;
02244     }
02245     else
02246     {
02247       ClampEnd(2);
02248 
02249       double w = 1.0;
02250       if (IsRational()) {
02251         w = Weight(m_cv_count-1);
02252         end_point *= w;
02253       }
02254       SetCV(m_cv_count-1,end_point);
02255       if (IsRational())
02256         SetWeight(m_cv_count-1, w);
02257 
02258       DestroyCurveTree();
02259       rc = true;
02260     }
02261   }
02262   return rc;
02263 }
02264 
02265 
02266 ON_BOOL32
02267 ON_NurbsCurve::Reverse()
02268 {
02269   ON_BOOL32 rc0 = ON_ReverseKnotVector( m_order, m_cv_count, m_knot );
02270   ON_BOOL32 rc1 = ON_ReversePointList( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv );
02271         DestroyCurveTree();
02272   return rc0 && rc1;
02273 }
02274 
02275 ON_BOOL32
02276 ON_NurbsCurve::SwapCoordinates( int i, int j )
02277 {
02278         DestroyCurveTree();
02279   return  ON_SwapPointListCoordinates( m_cv_count, m_cv_stride, m_cv, i, j );
02280 }
02281 
02282 double ON_NurbsCurve::GrevilleAbcissa(
02283          int gindex  // index (0 <= index < CVCount(dir)
02284          ) const
02285 {
02286   return ON_GrevilleAbcissa( m_order, m_knot+gindex );
02287 }
02288 
02289 bool ON_NurbsCurve::GetGrevilleAbcissae( // see ON_GetGrevilleAbcissa() for details
02290          double* g         // g[m_cv_count]
02291          ) const
02292 {
02293   return ON_GetGrevilleAbcissae( m_order, m_cv_count, m_knot, false, g );
02294 }
02295 
02296 
02297 bool ON_NurbsCurve::ZeroCVs()
02298 {
02299   bool rc = false;
02300   int i;
02301   if ( m_cv ) {
02302     if ( m_cv_capacity > 0 ) {
02303       memset( m_cv, 0, m_cv_capacity*sizeof(*m_cv) );
02304       if ( m_is_rat ) {
02305         for ( i = 0; i < m_cv_count; i++ ) {
02306           SetWeight( i, 1.0 );
02307         }
02308       }
02309       rc = true;
02310     }
02311     else {
02312       double* cv;
02313       int s = CVSize()*sizeof(*cv);
02314       for ( i = 0; i < m_cv_count; i++ ) {
02315         cv = CV(i);
02316         memset(cv,0,s);
02317         if ( m_is_rat )
02318           cv[m_dim] = 1.0;
02319       }
02320       rc = (i>0) ? true : false;
02321     }
02322   }
02323         DestroyCurveTree();
02324   return rc;
02325 }
02326 
02327 bool ON_NurbsCurve::IsClamped( // determine if knot vector is clamped
02328       int end // (default= 2) end to check: 0 = start, 1 = end, 2 = start and end
02329       ) const
02330 {
02331   return ON_IsKnotVectorClamped( m_order, m_cv_count, m_knot, end );
02332 }
02333   
02334 
02335 bool ON_NurbsCurve::ReserveCVCapacity(int desired_capacity)
02336 {
02337   // If m_cv_capacity == 0 and m_cv != NULL, then the user
02338   // has hand built the ON_NurbsCurve.m_cv array and is responsible
02339   // for making sure it's always big enough.
02340   bool rc = true;
02341   if ( desired_capacity > m_cv_capacity ) {
02342     if ( !m_cv ) {
02343       // no cv array - allocate one
02344       m_cv = (double*)onmalloc(desired_capacity*sizeof(*m_cv));
02345       if ( !m_cv ) {
02346         rc = false;
02347       }
02348       else {
02349         m_cv_capacity = desired_capacity;
02350       }
02351     }
02352     else if ( m_cv_capacity > 0 ) {
02353       // existing m_cv[] is too small and the fact that
02354       // m_cv_capacity > 0 indicates that ON_NurbsCurve is
02355       // managing the m_cv[] memory, so we need to grow
02356       // the m_cv[] array.
02357       m_cv = (double*)onrealloc(m_cv,desired_capacity*sizeof(*m_cv));
02358       if ( !m_cv ) {
02359         rc = false;
02360         m_cv_capacity = 0;
02361       }
02362       else {
02363         m_cv_capacity = desired_capacity;
02364       }
02365     }
02366   }
02367   return rc;
02368 }
02369 
02370 bool ON_NurbsCurve::ReserveKnotCapacity(int desired_capacity)
02371 {
02372   // If m_knot_capacity == 0 and m_knot != NULL, then the user
02373   // has hand built the ON_NurbsCurve.m_knot array and is responsible
02374   // for making sure it's always big enough.
02375   bool rc = true;
02376   if ( desired_capacity > m_knot_capacity ) {
02377     if ( !m_knot ) {
02378       // no knot array - allocate one
02379       m_knot = (double*)onmalloc(desired_capacity*sizeof(*m_knot));
02380       if ( !m_knot ) {
02381         m_knot_capacity = 0;
02382         rc = false;
02383       }
02384       else {
02385         m_knot_capacity = desired_capacity;
02386       }
02387     }
02388     else if ( m_knot_capacity > 0 ) {
02389       // existing m_knot[] is too small and the fact that
02390       // m_knot_capacity > 0 indicates that ON_NurbsCurve is
02391       // managing the m_knot[] memory, so we need to grow
02392       // the m_knot[] array.
02393       m_knot = (double*)onrealloc(m_knot,desired_capacity*sizeof(*m_knot));
02394       if ( !m_knot ) {
02395         rc = false;
02396         m_knot_capacity = 0;
02397       }
02398       else {
02399         m_knot_capacity = desired_capacity;
02400       }
02401     }
02402   }
02403   return rc;
02404 }
02405 
02406 bool ON_NurbsCurve::ConvertSpanToBezier( int span_index, ON_BezierCurve& bez ) const
02407 {
02408   bool rc = false;
02409   if ( span_index >= 0 && span_index <= m_cv_count-m_order && m_knot && m_cv ) 
02410   {
02411     const int cvdim = CVSize();
02412     const int sizeof_cv = cvdim*sizeof(*bez.m_cv);
02413     int i;
02414     rc = bez.ReserveCVCapacity( cvdim*m_order );
02415     if ( rc ) {
02416       bez.m_dim = m_dim;
02417       bez.m_is_rat = m_is_rat;
02418       bez.m_order = m_order;
02419       bez.m_cv_stride = cvdim;
02420       if ( bez.m_cv_stride == m_cv_stride )
02421       {
02422         memcpy( bez.m_cv, CV(span_index), bez.m_order*sizeof_cv );
02423       }
02424       else
02425       {
02426         for ( i = 0; i < m_order; i++ ) {
02427           memcpy( bez.CV(i), CV(span_index+i), sizeof_cv );
02428         }
02429       }
02430       const double* knot = m_knot + span_index;
02431                         if( knot[m_order-2] < knot[m_order-1] )
02432                                 ON_ConvertNurbSpanToBezier( cvdim, bez.m_order, bez.m_cv_stride, bez.m_cv,
02433                                                                                                                                                 knot, knot[m_order-2], knot[m_order-1] );
02434                         else
02435                                 rc = false;
02436     }
02437   }
02438   return rc;
02439 }
02440 
02441 
02442 bool ON_NurbsCurve::HasBezierSpans() const
02443 {
02444   return ON_KnotVectorHasBezierSpans( m_order, m_cv_count, m_knot );
02445 }
02446 
02447 bool ON_NurbsCurve::MakePiecewiseBezier( bool bSetEndWeightsToOne )
02448 {
02449   bool rc = HasBezierSpans();
02450   if ( !rc && IsValid() ) 
02451   {
02452     ON_Workspace ws;
02453     DestroyRuntimeCache();
02454     if ( !ClampEnd(2) )
02455       return false;
02456     int span_count = SpanCount();
02457     //int knot_count = KnotCount();
02458     ReserveKnotCapacity( (span_count + 1)*(m_order-1) );
02459     ReserveCVCapacity( m_cv_stride*(span_count*(m_order-1) + 1) );
02460     double* t = ws.GetDoubleMemory( span_count+1);
02461     GetSpanVector( t );
02462     int cvdim = CVSize();
02463     ON_BezierCurve* bez = new ON_BezierCurve[span_count];
02464     int ki, spani, i;
02465     for ( ki = m_order-2, spani = 0; ki < m_cv_count-1 && spani < span_count; ki++ ) {
02466       if ( m_knot[ki] < m_knot[ki+1] ) {
02467         bez[spani].Create(m_dim,m_is_rat,m_order);
02468         for ( i = 0; i < m_order; i++ )
02469           bez[spani].SetCV(  i, ON::intrinsic_point_style, CV( i + ki - m_order + 2 ) );
02470         ON_ConvertNurbSpanToBezier( cvdim, bez[spani].m_order, bez[spani].m_cv_stride, bez[spani].m_cv,
02471                                     m_knot+ki-m_order+2, m_knot[ki], m_knot[ki+1] );
02472         spani++;
02473       }
02474     }
02475     m_cv_count = span_count*(m_order-1) + 1;
02476     for ( spani = 0; spani < span_count; spani++ ) {
02477       for ( i = 0; i < m_order; i++ ) {
02478         SetCV(  spani*(m_order-1) + i, ON::intrinsic_point_style, bez[spani].CV( i ) );
02479       }
02480       for ( ki = 0; ki < m_order-1; ki++ )
02481         m_knot[ki+spani*(m_order-1)] = t[spani];
02482     }
02483     for ( ki = 0; ki < m_order-1; ki++ )
02484       m_knot[ki+span_count*(m_order-1)] = t[spani];
02485     delete[] bez;
02486     rc = true;
02487   }
02488   if ( rc && bSetEndWeightsToOne && m_is_rat )
02489   {
02490     // 2 June 2003 Dale Lear - added support for bSetEndWeightsToOne=true option.
02491     double w0, w1;
02492     ON_BezierCurve bez;
02493     bez.m_dim = m_dim;
02494     bez.m_is_rat = m_is_rat;
02495     bez.m_order = m_order;
02496     bez.m_cv_stride = m_cv_stride;
02497 
02498     bez.m_cv = CV(0);
02499     if ( bez.Weight(0) != 1.0 )
02500     {
02501       DestroyRuntimeCache();
02502       w0 = 1.0;
02503       w1 = (m_order == m_cv_count) ? 1.0 : bez.Weight(m_order-1);
02504       bez.ChangeWeights(0,w0,m_order-1,w1);
02505     }
02506  
02507     bez.m_cv = CV(m_cv_count-m_order);
02508     if ( bez.Weight(m_order-1) != 1.0 )
02509     {
02510       DestroyRuntimeCache();
02511       w0 = bez.Weight(0);
02512       w1 = 1.0; // 23 June 2003
02513       bez.ChangeWeights(0,w0,m_order-1,w1);
02514     }
02515 
02516     bez.m_cv = 0;
02517   }
02518   return rc;
02519 }
02520 
02521 
02522 double ON_NurbsCurve::ControlPolygonLength() const
02523 {
02524   double length = 0.0;
02525   ON_GetPolylineLength( m_dim, m_is_rat, m_cv_count, m_cv_stride, m_cv, &length );
02526   return length;
02527 }
02528 
02529 
02530 bool ON_NurbsCurve::InsertKnot( double knot_value, int knot_multiplicity )
02531 {
02532   bool rc = false;
02533 
02534   const int degree = Degree();
02535 
02536   double t0, t1;
02537   {
02538     ON_Interval d = Domain();
02539     if ( !d.IsIncreasing() )
02540       return false;
02541     t0 = d[0];
02542     t1 = d[1];
02543   }
02544   
02545   if ( knot_multiplicity < 1 || knot_multiplicity > degree ) 
02546   {
02547     ON_ERROR("ON_NurbsCurve::ON_InsertKnot(): knot_multiplicity < 1 or knot_multiplicity > degree.");
02548     return false;
02549   }
02550 
02551   if( knot_value < t0 || knot_value > t1 )
02552   {
02553     ON_ERROR("ON_InsertKnot(): knot_value not in NURBS curve domain.");
02554     return false;
02555   }
02556 
02557   if ( knot_value == t0 ) 
02558   {
02559     if ( knot_multiplicity == degree ) 
02560     {
02561       rc = ClampEnd(0);
02562     }
02563     else if ( knot_multiplicity == 1 ) 
02564     {
02565       rc = true;
02566     }
02567     else 
02568     {
02569       ON_ERROR("ON_InsertKnot(): knot_value = t0 and 1 < knot_multiplicity < degree.");
02570       rc = false;
02571     }
02572     return rc;
02573   }
02574 
02575   if ( knot_value == t1 ) 
02576   {
02577     if ( knot_multiplicity == degree ) 
02578     {
02579       rc = ClampEnd(1);
02580     }
02581     else if ( knot_multiplicity == 1 ) 
02582     {
02583       rc = true;
02584     }
02585     else 
02586     {
02587       ON_ERROR("ON_InsertKnot(): knot_value = t1 and 1 < knot_multiplicity < degree.");
02588       rc = false;
02589     }
02590     return rc;
02591   }
02592 
02593   DestroyCurveTree();
02594 
02595   ON_BOOL32 bIsPeriodic = (degree>1) ? IsPeriodic() : false;
02596   int span_index = ON_NurbsSpanIndex( m_order, m_cv_count, m_knot, knot_value, 0, 0 );
02597 
02598   // reserve room for new knots and cvs
02599   if ( !ReserveCVCapacity( m_cv_stride*(m_cv_count+knot_multiplicity) ) )
02600     return false;
02601   if ( !ReserveKnotCapacity( KnotCount()+knot_multiplicity ) )
02602     return false;
02603   if ( bIsPeriodic ) {
02604   }
02605 
02606   rc = true;
02607   int span_hint = span_index;
02608   int new_knot_count = ON_InsertKnot( knot_value, knot_multiplicity, 
02609                                       CVSize(), m_order, m_cv_count, 
02610                                       m_cv_stride, m_cv, m_knot, &span_hint );
02611   if ( new_knot_count > 0 ) 
02612   {
02613     m_cv_count += new_knot_count;
02614   }
02615 
02616   if ( bIsPeriodic && rc && !IsPeriodic() ) {
02617     // restore periodic form
02618     if ( ON_MakeKnotVectorPeriodic( m_order, m_cv_count, m_knot ) ) {
02619       int i0, i1;
02620       for ( i0 = 0, i1 = m_cv_count-degree; i0 < degree; i0++, i1++ ) {
02621         if ( span_index < degree-1 )
02622           SetCV( i1, ON::intrinsic_point_style, CV(i0) ); // cv[i1] = cv[i0]
02623         else
02624           SetCV( i0, ON::intrinsic_point_style, CV(i1) ); // cv[i0] = cv[i1]
02625       }
02626     }
02627     else {
02628       ClampEnd(2);
02629     }
02630   }
02631 
02632   return rc;
02633 }
02634 
02635 bool ON_NurbsCurve::MakeRational()
02636 {
02637   if ( !IsRational() ) {
02638     const int dim = Dimension();
02639     const int cv_count = CVCount();
02640     if ( cv_count > 0 && m_cv_stride >= dim && dim > 0 ) {
02641       const int new_stride = (m_cv_stride == dim) ? dim+1 : m_cv_stride;
02642       ReserveCVCapacity( cv_count*new_stride );
02643       const double* old_cv;
02644       double* new_cv;
02645       int cvi, j;
02646       for ( cvi = cv_count-1; cvi>=0; cvi-- ) {
02647         old_cv = CV(cvi);
02648         new_cv = m_cv+(cvi*new_stride);
02649         for ( j = dim-1; j >= 0; j-- ) {
02650           new_cv[j] = old_cv[j];
02651         }
02652         new_cv[dim] = 1.0;
02653       }
02654       m_cv_stride = new_stride;
02655       m_is_rat = 1;
02656     }
02657   }
02658   return IsRational();
02659 }
02660 
02661 bool ON_NurbsCurve::MakeNonRational()
02662 {
02663   if ( IsRational() ) {
02664     const int dim = Dimension();
02665     const int cv_count = CVCount();
02666     if ( cv_count > 0 && m_cv_stride >= dim+1 && dim > 0 ) {
02667       double w;
02668       const double* old_cv;
02669       double* new_cv = m_cv;
02670       int cvi, j;
02671       for ( cvi = 0; cvi < cv_count; cvi++ ) {
02672         old_cv = CV(cvi);
02673         w = old_cv[dim];
02674         w = ( w != 0.0 ) ? 1.0/w : 1.0;
02675         for ( j = 0; j < dim; j++ ) {
02676           *new_cv++ = w*(*old_cv++);
02677         }
02678       }
02679       m_is_rat = 0;
02680       m_cv_stride = dim;
02681     }
02682   }
02683         DestroyCurveTree();
02684   return ( !IsRational() ) ? true : false;
02685 }
02686 
02687 static ON_BOOL32 GetRaisedDegreeCV(int old_order, 
02688                               int cvdim,
02689                               int old_cvstride,
02690                               const double* oldCV, //old_cvstride*old_order
02691                               const double* oldkn, //2*old_degree
02692                               const double* newkn, //2*old_order
02693                               int cv_id,     //0 <= cv_id <= old_order
02694                               double* newCV  //cvdim
02695                               )
02696 
02697 {
02698   int i,j,k;
02699 
02700   if (!oldCV || !oldkn || !newkn || !newCV || cv_id < 0 || cv_id > old_order)
02701     return false;
02702 
02703   int old_degree = old_order-1;
02704   int new_degree = old_degree+1;
02705 
02706   double* t = (double*)onmalloc(old_degree*sizeof(double));
02707   if (!t) return false;
02708   double* P = (double*)onmalloc(cvdim*sizeof(double));
02709   if (!P) {
02710     onfree((void*)t);
02711     return false;
02712   }
02713 
02714   memset(newCV, 0, cvdim*sizeof(double));
02715 
02716   const double* kn = newkn + cv_id;
02717 
02718   for (i=0; i<new_degree; i++){
02719     k=0;
02720     for (j=0; j<new_degree; j++){
02721       if (j != i) {
02722         t[k] = kn[j];
02723         k++;
02724       }
02725     }
02726     if (!ON_EvaluateNurbsBlossom(cvdim, old_order, 
02727       old_cvstride, oldCV, oldkn, t, P)){
02728       onfree((void*)t);
02729       onfree((void*)P);
02730       return false;
02731     }
02732     for (k=0; k<cvdim; k++) newCV[k] += P[k];
02733   }
02734 
02735   double denom = (double)new_degree;
02736   for (i=0; i<cvdim; i++) 
02737     newCV[i] /= denom;
02738 
02739   onfree((void*)t);
02740   onfree((void*)P);
02741 
02742   return true;
02743 }
02744 
02745 
02746 static ON_BOOL32 IncrementNurbDegree(ON_NurbsCurve& N)
02747 //for use only in ON_NurbsCurve::IncreaseDegree().  No validation done.
02748 //N is assumed to have sufficient knot and cv capacities, clamped ends
02749 
02750 {
02751   ON_NurbsCurve M = N;
02752   int span_count = M.SpanCount();
02753   int new_kcount = M.KnotCount() + span_count + 1;
02754   N.m_order = M.Order()+1;
02755   N.m_cv_count = new_kcount - N.Order() + 2;
02756 
02757   //set N's knots
02758   int i=0;
02759   int k=0;
02760   int j;
02761   while (i<M.CVCount()){
02762     double kn = M.Knot(i);
02763     int mult = M.KnotMultiplicity(i);
02764     for (j=0; j<=mult; j++) {
02765       N.SetKnot(k, kn);
02766       k++;
02767     }
02768     i+=mult;
02769   }
02770   //zero out N's cvs
02771   memset(N.m_cv, 0, N.m_cv_capacity*sizeof(double));
02772   int cvdim = N.CVSize();
02773   const double* cvM;
02774   double* cvN;
02775   const double* knotN;
02776   const double* knotM;
02777   int siN = 0;
02778   int siM = 0;
02779 
02780   for (i=0; i<span_count; i++){
02781     knotN = &N.m_knot[siN];
02782     knotM = &M.m_knot[siM];
02783     cvM = M.CV(siM);
02784     cvN = N.CV(siN);
02785     int span_mult = N.KnotMultiplicity(siN+N.Degree()-1);
02786     int skip = N.Order()-span_mult;
02787     cvN += skip*N.m_cv_stride;
02788     for (j=skip; j<N.Order(); j++){//calculate each cv of the span
02789       GetRaisedDegreeCV(M.Order(), cvdim, M.m_cv_stride, cvM, knotM, knotN, j, cvN);
02790       cvN += N.m_cv_stride;
02791     }
02792     siN = ON_NextNurbsSpanIndex(N.Order(), N.CVCount(), N.m_knot, siN);
02793     siM = ON_NextNurbsSpanIndex(M.Order(), M.CVCount(), M.m_knot, siM);
02794   }
02795 
02796   //set first and last equal to original
02797   cvM = M.CV(0);
02798   cvN = N.CV(0);
02799   for (i=0; i<cvdim; i++) cvN[i] = cvM[i];
02800 
02801   cvM = M.CV(M.CVCount()-1);
02802   cvN = N.CV(N.CVCount()-1);
02803   for (i=0; i<cvdim; i++) cvN[i] = cvM[i];
02804 
02805   return true;
02806 }
02807 
02808 bool ON_NurbsCurve::IncreaseDegree( int desired_degree )
02809 {
02810   if ( desired_degree < 1 || desired_degree < m_order-1 ) return false;
02811   if ( desired_degree == m_order-1 ) return true;
02812   if (!ClampEnd(2)) return false;
02813 
02814   int del = desired_degree - Degree();
02815   int new_order = Order()+del;
02816   int span_count = SpanCount();
02817   int new_kcount = KnotCount()+(span_count+1)*del;
02818   int new_cvcount = new_kcount - new_order + 2;
02819 
02820   if (!ReserveKnotCapacity(new_kcount)) return false;
02821   if (!ReserveCVCapacity(new_cvcount*m_cv_stride)) return false;
02822 
02823   for (int i=0; i<del; i++) {
02824     if (!IncrementNurbDegree(*this)) return false;
02825   }
02826 
02827   return true;
02828 }
02829 
02830 bool ON_NurbsCurve::ChangeDimension( int desired_dimension )
02831 {
02832   bool rc = false;
02833   int i, j;
02834   if ( desired_dimension < 1 )
02835     return false;
02836   if ( desired_dimension == m_dim )
02837     return true;
02838 
02839   DestroyCurveTree();
02840 
02841   if ( desired_dimension < m_dim ) 
02842   {
02843     if ( m_is_rat ) {
02844       double* cv;
02845       for ( i = 0; i < m_cv_count; i++ ) {
02846         cv = CV(i);
02847         cv[desired_dimension] = cv[m_dim];
02848       }
02849     }
02850     m_dim = desired_dimension;
02851     rc = true;
02852   }
02853   else 
02854   {
02855     const double* cv0;
02856     double* cv1;
02857     //const int cv_size0 = CVSize();
02858     const int cv_size1 = m_is_rat ? desired_dimension + 1 : desired_dimension;
02859     const int cv_stride1 = (m_cv_stride < cv_size1) ? cv_size1 : m_cv_stride;
02860     if ( m_cv_stride < cv_stride1 && m_cv_capacity > 0 ) {
02861       m_cv_capacity = cv_stride1*m_cv_count;
02862       m_cv = (double*)onrealloc( m_cv, m_cv_capacity*sizeof(*m_cv) );
02863     }
02864     for ( i = CVCount()-1; i >= 0; i-- ) {
02865       cv0 = CV(i);
02866       cv1 = m_cv + (i*cv_stride1);
02867       if ( m_is_rat )
02868         cv1[desired_dimension] = cv0[m_dim];
02869       for ( j = desired_dimension-1; j >= m_dim; j-- ) {
02870         cv1[j] = 0.0;
02871       }
02872       for ( j = m_dim-1; j >= 0; j-- ) {
02873         cv1[j] = cv0[j];
02874       }
02875     }
02876     m_dim = desired_dimension;
02877     m_cv_stride = cv_stride1;
02878     rc = true;
02879   }
02880   return rc;
02881 }
02882 
02883 bool ON_NurbsCurve::Append( const ON_NurbsCurve& c )
02884 {
02885   bool rc = false;
02886 
02887   if ( CVCount() == 0 ) {
02888     *this = c;
02889     return IsValid()?true:false;
02890   }
02891 
02892   if ( c.IsRational() && !IsRational() ) {
02893     if ( !MakeRational() )
02894       return false;
02895   }
02896   if ( c.Degree() > Degree() ) {
02897     if ( !IncreaseDegree( c.Degree() ) )
02898       return false;
02899   }
02900   if ( c.Dimension() > Dimension() ) {
02901     if ( !ChangeDimension( c.Dimension() ) )
02902       return false;
02903   }
02904 
02905   if (    (IsRational() && !c.IsRational())
02906        || c.Degree() < Degree() 
02907        || !c.IsClamped(0) 
02908        || c.Dimension() < Dimension() ) 
02909   {
02910     ON_NurbsCurve tmp(c);
02911     if ( !tmp.IncreaseDegree( Degree() ) )
02912       return false;
02913     if ( !tmp.ChangeDimension( Dimension() ) )
02914       return false;
02915     if ( IsRational() ) {
02916       if ( !tmp.MakeRational() )
02917         return false;
02918     }
02919     if ( !tmp.ClampEnd(0) )
02920       return false;
02921 
02922     // make sure we don't reenter this scope
02923     if ( tmp.IsRational() != IsRational() )
02924       return false;
02925     if ( tmp.Degree() != Degree() )
02926       return false;
02927     if ( tmp.Dimension() != Dimension() )
02928       return false;
02929     if ( !tmp.IsClamped(0) )
02930       return false;
02931     return Append(tmp);
02932   }
02933 
02934   if (    IsValid() 
02935        && c.IsValid() 
02936        && Degree() == c.Degree() 
02937        && IsRational() == c.IsRational() 
02938        && Dimension() == c.Dimension() ) 
02939   {
02940     if ( !ClampEnd(1) )
02941       return false;
02942     const double w0 = c.Weight(0);
02943     const double w1 = Weight(CVCount()-1);
02944     double w = 1.0;
02945     if ( IsRational() && w0 != w1 ) {
02946       w = w1/w0;
02947     }
02948     ReserveCVCapacity( (CVCount()+c.CVCount())*m_cv_stride );
02949     ReserveKnotCapacity( ON_KnotCount(Order(),CVCount()+c.CVCount()) );
02950     const double dk = Knot(CVCount()-1) - c.Knot(c.Order()-2);
02951 
02952     double* cv = CV(CVCount()-1);
02953     const int cv_dim = CVSize();
02954     const int sizeof_cv = cv_dim*sizeof(*cv);
02955     const int c_cv_count = c.CVCount();
02956     const int c_knot_count = c.KnotCount();
02957     int i0, i1, j;
02958 
02959     i0 = KnotCount();
02960     for ( i1 = c.Order()-1; i1 < c_knot_count; i1++ ) {
02961       m_knot[i0++] = c.Knot(i1) + dk;
02962     }
02963 
02964     for ( i1 = 1; i1 < c_cv_count; i1++ ) {
02965       cv += m_cv_stride;
02966       memcpy( cv, c.CV(i1), sizeof_cv );
02967       if ( w != 1.0 ) {
02968         for ( j = 0; j < cv_dim; j++ )
02969           cv[j] *= w;
02970       }
02971       m_cv_count++;
02972     }
02973     rc = true;
02974   }
02975   return rc;
02976 }
02977 
02978 static
02979 ON_BOOL32 TweakSplitTrimParameter( double k0, double k1, double& t )
02980 {
02981   // [k0,k1] = knots that bracket trim/split parameter t
02982   // returns true if parameter is tweaked
02983   // The purpose of the tweak is to avoid creating knot
02984   // vectors that cause numerical problems during evaluation.
02985   ON_BOOL32 rc = false;
02986   double ktol;
02987   if ( k0 < t && t < k1 )
02988   {
02989     // Nov 7, 2008 Lowell Walmsley 
02990     // Changed this from ON_SQRT_EPSILON to 4*ON_SQRT_EPSILON because 
02991     // to make it less likely to get very narrow knot spans
02992     // Dale says the intention is to round off to 8 sig figs
02993     ktol = (fabs(k0) + fabs(k1))*4.0*ON_SQRT_EPSILON;
02994     if ( t-k0 <= ktol && k1-t > 16.0*ktol )
02995     {
02996       t = k0;
02997       rc = true;
02998     }
02999     else if ( k1-t <= ktol && t-k0 > 16.0*ktol )
03000     {
03001       t = k1;
03002       rc = true;
03003     }
03004   }
03005   return rc;
03006 }
03007 
03008 
03009 
03010 ON_BOOL32 ON_NurbsCurve::Trim( const ON_Interval& in )
03011 {
03012   if ( !in.IsIncreasing() )
03013     return false;
03014 
03015   const int cv_dim = CVSize();
03016   const int order = Order();
03017   double t, split_t;
03018   int ki, side, i0, i1, i1_max, new_cv_count;
03019 
03020         //Greg Arden 28 April 2003.  Do not change any curve that is trimmed to its entire domain.
03021         //             This is especiallly important for periodic curves.
03022         if(in==Domain())
03023           return true;
03024 
03025 
03026   DestroyCurveTree();
03027 
03028   // cut off right end (or extend if in.m_t[1] > Domain.Max()
03029   side = -1;
03030   t = in.m_t[1]; // trimming parameter
03031   ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, t, side, 0 );
03032 
03033   // if t is very close to a knot value, then trim at the knot
03034   split_t = t;
03035   if ( TweakSplitTrimParameter( m_knot[ki+order-2], m_knot[ki+order-1], split_t ) )
03036     ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, split_t, side, ki );
03037 
03038   if ( !ON_EvaluateNurbsDeBoor( cv_dim, order, m_cv_stride, CV(ki),
03039                                 m_knot + ki, side, 0.0, t ) ) 
03040   {
03041     ON_ERROR("ON_NurbsCurve::Trim() - right end de Boor algorithm failed.");
03042     return false;
03043   }
03044   // clamp right end knots
03045   m_cv_count = ki + order;
03046   for ( i0 = ON_KnotCount( order, m_cv_count)-1; i0 >= m_cv_count-1; i0--)
03047     m_knot[i0] = t;
03048 
03049   // cut off left end (or extend if in.m_t[0] < Domain.Max()
03050   side = 1;
03051   t = in.m_t[0]; // trimming parameter
03052   ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, t, side, 0 );
03053 
03054   // if t is very close to a knot value, then trim at the knot
03055   split_t = t;
03056   if ( TweakSplitTrimParameter( m_knot[ki+order-2], m_knot[ki+order-1], split_t ) )
03057     ki = ON_NurbsSpanIndex( order, m_cv_count, m_knot, split_t, side, ki );
03058 
03059   if ( !ON_EvaluateNurbsDeBoor( cv_dim, order, m_cv_stride, CV(ki),
03060                                 m_knot + ki, side, 0.0, t ) ) 
03061   {
03062     ON_ERROR("ON_NurbsCurve::Trim() - right end de Boor algorithm failed.");
03063     return false;
03064   }
03065 
03066   // remove surplus cvs and knots
03067   new_cv_count = m_cv_count - ki;
03068   if ( new_cv_count < m_cv_count ) {
03069     // move cvs and knots over
03070     i1_max = m_cv_stride*m_cv_count;
03071     for ( i0 = 0, i1 = ki*m_cv_stride; i1 < i1_max; i0++, i1++ )
03072       m_cv[i0] = m_cv[i1];
03073     i1_max = ON_KnotCount( order, m_cv_count );
03074     for ( i0 = 0, i1 = ki; i1 < i1_max; i0++, i1++ )
03075       m_knot[i0] = m_knot[i1];
03076     m_cv_count = new_cv_count;
03077   }
03078 
03079   // clamp left end knots
03080   for (i0 = 0; i0 <= order-2; i0++)
03081     m_knot[i0] = t;
03082 
03083   ClampEnd(2); // 26 June 2003 Dale Lear
03084 
03085         DestroyCurveTree();
03086   return true;
03087 }
03088 
03089 bool ON_NurbsCurve::Extend(
03090   const ON_Interval& domain
03091   )
03092 
03093 {
03094   if (IsClosed()) return false;
03095   int is_rat = IsRational() ? 1 : 0;
03096   int dim = Dimension();
03097   int cvdim = dim+is_rat;
03098 
03099   bool changed = false;
03100   if (domain[0] < Domain()[0]){
03101     ClampEnd(0);
03102     ON_EvaluateNurbsDeBoor(cvdim,Order(),m_cv_stride, CV(0),m_knot,1,0.0,domain[0]);
03103     for (int i = 0; i < Order()-1; i++)
03104                         m_knot[i] = domain[0];
03105     changed = true;
03106   }
03107   if (domain[1] > Domain()[1]){
03108     ClampEnd(1);
03109     int i = CVCount() - Order();
03110     ON_EvaluateNurbsDeBoor(cvdim,Order(),m_cv_stride, CV(i),m_knot + i,-1,0.0,domain[1]);
03111     for (i = KnotCount()-1; i >= CVCount()-1; i--)
03112                         m_knot[i] = domain[1];
03113     changed = true;
03114   }
03115 
03116   if (changed){
03117     DestroyCurveTree();
03118   }
03119   return changed;
03120 }
03121 
03122 ON_BOOL32 ON_NurbsCurve::Split(
03123     double t,    // t = curve parameter to split curve at
03124     ON_Curve*& left_crv, // left portion returned here (must be an ON_NurbsCurve)
03125     ON_Curve*& right_crv // right portion returned here (must be an ON_NurbsCurve)
03126   ) const
03127 {
03128 
03129   int i;
03130   ON_BOOL32 rc = false;
03131   if ( left_crv && !ON_NurbsCurve::Cast(left_crv) )
03132     return false;
03133   if ( right_crv && !ON_NurbsCurve::Cast(right_crv) )
03134     return false;
03135   if ( IsValid() && t > m_knot[m_order-2] && t < m_knot[m_cv_count-1] ) 
03136   {
03137     ON_NurbsCurve* left = (ON_NurbsCurve*)left_crv;
03138     ON_NurbsCurve* right = (ON_NurbsCurve*)right_crv;
03139     if ( !left )
03140       left = new ON_NurbsCurve();
03141     else if ( left == right )
03142       return false;
03143 
03144     if ( !right )
03145       right = new ON_NurbsCurve();
03146     left->DestroyCurveTree();
03147     right->DestroyCurveTree();
03148     
03149     int span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,t,1,0);
03150     // if t is very close to a knot value, then split at the knot
03151     double split_t = t;
03152     if ( TweakSplitTrimParameter( m_knot[span_index+m_order-2], m_knot[span_index+m_order-1], split_t ) )
03153     {
03154       if ( split_t <= m_knot[m_order-2] || split_t >= m_knot[m_cv_count-1] ){
03155         // 22 October - greg added bailout code but didn't clean up???
03156         //   chuck fixed leak.
03157         if (!left_crv) delete left;
03158         if (!right_crv) delete right;
03159         return false;
03160       }
03161       span_index = ON_NurbsSpanIndex(m_order,m_cv_count,m_knot,split_t,1,span_index);
03162     }
03163 
03164     if ( span_index >= 0 && span_index <= m_cv_count - m_order ) 
03165     {
03166       const int cvdim = CVSize();
03167       const int sizeof_cv = cvdim*sizeof(double);
03168 
03169       int left_cv_count = m_order + span_index;
03170       if ( span_index > 0 && split_t == m_knot[span_index+m_order-2] )
03171       { 
03172         // splitting exactly at a knot value
03173         int k;
03174         for ( k = 0; left_cv_count >= m_order && k <= span_index+m_order-2; k++ )
03175         {
03176           if ( split_t == m_knot[span_index+m_order-2-k] )
03177             left_cv_count--;
03178           else
03179             break;
03180         }
03181       }
03182       int right_cv_count = m_cv_count - span_index;
03183       if ( left_cv_count < m_order || right_cv_count < m_order ){
03184         // 22 October - greg added bailout code but didn't clean up???
03185         //   chuck fixed leak.
03186         if (!left_crv) delete left;
03187         if (!right_crv) delete right;
03188         return false;
03189       }
03190 
03191       if ( left != this )
03192       {
03193         left->m_dim = m_dim;
03194         left->m_is_rat = m_is_rat;
03195         left->m_order = m_order;
03196         left->m_cv_count = left_cv_count;
03197         left->m_cv_stride = cvdim;
03198       }
03199       if ( right != this )
03200       {
03201         right->m_dim = m_dim;
03202         right->m_is_rat = m_is_rat;
03203         right->m_order = m_order;
03204         right->m_cv_count = right_cv_count;
03205         right->m_cv_stride = cvdim;
03206       }
03207 
03208       // fill in left allowing for possibility that left = this
03209       if ( left->m_cv != m_cv ) 
03210       {
03211         left->ReserveCVCapacity(cvdim*left_cv_count);
03212         for( i = 0; i < left_cv_count; i++ ) {
03213           memcpy( left->m_cv + i*cvdim, CV(i), sizeof_cv );
03214         }
03215       }
03216       if ( left->m_knot != m_knot ) 
03217       {
03218         i = ON_KnotCount( m_order, left_cv_count);
03219         left->ReserveKnotCapacity( i );
03220         memcpy( left->m_knot, m_knot, i*sizeof(left->m_knot[0]) );
03221       }
03222       
03223       // fill in right allowing for possibility that right = this
03224       if ( right->m_cv != m_cv || span_index > 0 )
03225       {
03226         right->ReserveCVCapacity(cvdim*right_cv_count);
03227         for( i = 0; i < right_cv_count; i++ ) {
03228           memmove( right->m_cv + i*cvdim, CV(i+span_index), sizeof_cv );
03229         }
03230       }
03231       if ( right->m_knot != m_knot || span_index > 0 ) 
03232       {
03233         i = ON_KnotCount(m_order, right_cv_count);
03234         right->ReserveKnotCapacity( i );
03235         memmove( right->m_knot, m_knot + span_index, i*sizeof(right->m_knot[0]) );
03236       }
03237 
03238       if ( right == this ) {
03239         right->m_cv_count = right_cv_count;
03240         right->m_cv_stride = cvdim;
03241       }
03242 
03243       if ( left == this ) {
03244         left->m_cv_count = left_cv_count;
03245         left->m_cv_stride = cvdim;
03246       }
03247 
03248       // trim right end of left NURBS
03249       i = left->m_cv_count - left->m_order;
03250       ON_EvaluateNurbsDeBoor( cvdim, m_order, cvdim, left->CV(i), left->m_knot + i, -1, 0.0, t );
03251       for ( i = left->m_cv_count-1; i < ON_KnotCount(left->m_order,left->m_cv_count); i++ )
03252         left->m_knot[i] = t;
03253       left->ClampEnd(2); // 26 June 2003 Dale Lear
03254 
03255       // trim left end of right NURBS
03256       ON_EvaluateNurbsDeBoor( cvdim, m_order, cvdim, right->m_cv, right->m_knot, +1, 0.0, t );
03257       for ( i = 0; i <= right->m_order-2; i++ )
03258         right->m_knot[i] = t;
03259       right->ClampEnd(2); // 26 June 2003 Dale Lear
03260 
03261       if ( 0 == left_crv )
03262         left_crv = left;
03263       if ( 0 == right_crv )
03264         right_crv = right;
03265       rc = true;
03266     }
03267   }
03268   return rc;
03269 }
03270 
03271 
03272 int ON_NurbsCurve::GetNurbForm( ON_NurbsCurve& curve, 
03273                                 double tolerance,
03274                                 const ON_Interval* subdomain  // OPTIONAL subdomain of ON::ProxyCurve::Domain()
03275                                 ) const
03276 {
03277   int rc = 1;
03278 
03279   // 4 May 2007 Dale Lear
03280   //   I'm replacing the call to operator= with a call to
03281   //   ON_NurbsCurveCopyHelper().  The operator= call
03282   //   was copying userdata and that does not happen for
03283   //   any other GetNurbForm overrides.  Copying userdata
03284   //   in GetNurbForm is causing trouble in Make2D and 
03285   //   other places that are creating NURBS copies in
03286   //   worker memory pools.
03287 
03288   // curve = *this; // copied user data
03289   ON_NurbsCurveCopyHelper(*this,curve); // does not copy user data
03290 
03291   if ( subdomain ) 
03292   {
03293     if ( !curve.Trim(*subdomain) )
03294       rc = 0;
03295   }
03296   return rc;
03297 }
03298 
03299 int ON_NurbsCurve::HasNurbForm() const
03300 {
03301   return 1;
03302 }
03303 
03304 
03305 ON_BOOL32 ON_NurbsCurve::GetCurveParameterFromNurbFormParameter(
03306       double nurbs_t,
03307       double* curve_t
03308       ) const
03309 {
03310   *curve_t = nurbs_t;
03311   return true;
03312 }
03313 
03314 ON_BOOL32 ON_NurbsCurve::GetNurbFormParameterFromCurveParameter(
03315       double curve_t,
03316       double* nurbs_t
03317       ) const
03318 {
03319   *nurbs_t = curve_t;
03320   return true;
03321 }
03322 
03323 bool ON_NurbsCurve::ClampEnd( 
03324         int end // 0 = clamp start, 1 = clamp end, 2 = clamp start and end 
03325         )
03326 {
03327   // Curve tree doesn't change when you clamp // DestroyCurveTree();
03328   return ON_ClampKnotVector( CVSize(), m_order, 
03329                              m_cv_count, m_cv_stride, m_cv, 
03330                              m_knot, end );
03331 }
03332 
03333 
03334 /*
03335 static 
03336 bool ON_DuplicateKnots( int order, int cv_count, bool bRevKnot1,
03337                         double* knot0, 
03338                         double* knot1
03339                         )
03340 {
03341   return false;
03342 }
03343 
03344 static 
03345 bool ON_DuplicateContolPoints( int dim, int is_rat, int cv_count, 
03346                                bool bRevCV1,
03347                                int stride0, double* cv0, 
03348                                int stride1, double* cv1
03349                               )
03350 {
03351   return false;
03352 }
03353 */
03354 
03355 static bool ON_IsDuplicateKnotVector( int order, int cv_count, 
03356                const double* knot, const double* other_knot,
03357                bool bIgnoreParameterization )
03358 {
03359   bool rc = (    0 != knot 
03360               && 0 != other_knot 
03361               && order >= 2 
03362               && cv_count >= order);
03363 
03364   if (rc)
03365   {
03366     const int knot_count = ON_KnotCount( order, cv_count );
03367     int i;
03368     if ( bIgnoreParameterization )
03369     {
03370       const ON_Interval dom(knot[order-2],knot[cv_count-1]);
03371       const ON_Interval other_dom(other_knot[order-2],other_knot[cv_count-1]);
03372       double k, other_k;
03373       for ( i = 0; i < knot_count && rc; i++ )
03374       {
03375         k = dom.NormalizedParameterAt(knot[i]);
03376         other_k = dom.NormalizedParameterAt(other_knot[i]);
03377         rc = (fabs(k-other_k) <= ON_ZERO_TOLERANCE);
03378       }
03379     }
03380     else
03381     {
03382       for ( i = 0; i < knot_count && rc; i++ )
03383       {
03384         rc = (knot[i] == other_knot[i]);
03385       }
03386     }
03387   }
03388   return rc;
03389 }
03390 
03391 static bool ON_IsDuplicatePointList( int dim, int is_rat,
03392                                      int count, 
03393                                      int stride, 
03394                                      const double* cv,
03395                                      int other_stride,
03396                                      const double* other_cv,
03397                                      double tolerance
03398                                      )
03399 {
03400   bool rc = (dim > 0 && is_rat >= 0 && is_rat <= 1 && count > 0
03401              && abs(stride) >= dim+is_rat && abs(other_stride) >= (dim+is_rat)
03402              && 0 != cv && 0 != other_cv);
03403   if (rc)
03404   {
03405     if ( tolerance < 0.0 )
03406       tolerance = 0.0;
03407     int i, j;
03408     double w = 1.0;
03409     double other_w = 1.0;
03410     double cv_tol = tolerance;
03411     for ( i = 0; i < count && rc; i++ )
03412     {
03413       if ( is_rat )
03414       {
03415         w = cv[dim];
03416         other_w = other_cv[dim];
03417         cv_tol = fabs(w*tolerance);
03418         rc = ( w == other_w );
03419       }
03420 
03421       for ( j = 0; j < dim && rc; j++ )
03422       {
03423         rc = (fabs(cv[j] - other_cv[j]) <= cv_tol);
03424       }
03425       cv += stride;
03426       other_cv += other_stride;
03427     }
03428   }
03429   return rc;
03430 }
03431                                    
03432 
03433 bool ON_NurbsCurve::IsDuplicate( 
03434         const ON_NurbsCurve& other, 
03435         bool bIgnoreParameterization,
03436         double tolerance 
03437         ) const
03438 {
03439   bool rc = (this == &other);
03440   if ( !rc
03441        && m_dim      == other.m_dim
03442        && m_is_rat   == other.m_is_rat
03443        && m_order    == other.m_order
03444        && m_cv_count == other.m_cv_count
03445        )
03446   {
03447     // compare knots
03448     rc = ON_IsDuplicateKnotVector( m_order, m_cv_count, m_knot, other.m_knot, bIgnoreParameterization );
03449 
03450     // compare control points
03451     if (rc)
03452       rc = ON_IsDuplicatePointList( m_dim, m_is_rat?1:0, m_cv_count,
03453                                     m_cv_stride, m_cv,
03454                                     other.m_cv_stride, other.m_cv,
03455                                     tolerance );
03456   }
03457   return rc;
03458 }
03459 
03460 bool ON_NurbsSurface::IsDuplicate( 
03461         const ON_NurbsSurface& other, 
03462         bool bIgnoreParameterization,
03463         double tolerance 
03464         ) const
03465 {
03466   bool rc = (this == &other);
03467   if ( !rc
03468        && m_dim      == other.m_dim
03469        && m_is_rat   == other.m_is_rat
03470        && m_order[0] == other.m_order[0]
03471        && m_order[1] == other.m_order[1]
03472        && m_cv_count[0] == other.m_cv_count[0]
03473        && m_cv_count[1] == other.m_cv_count[1]
03474        )
03475   {
03476     // compare knots
03477     rc = ON_IsDuplicateKnotVector( m_order[0], m_cv_count[0], m_knot[0], other.m_knot[0], bIgnoreParameterization );
03478     if (rc)
03479       rc = ON_IsDuplicateKnotVector( m_order[1], m_cv_count[1], m_knot[1], other.m_knot[1], bIgnoreParameterization );
03480 
03481     // compare control points
03482     int i;
03483     for ( i = 0; i < m_cv_count[0] && rc; i++ )
03484     {
03485       rc = ON_IsDuplicatePointList( m_dim, m_is_rat?1:0, m_cv_count[1],
03486                                     m_cv_stride[1], CV(i,0),
03487                                     other.m_cv_stride[1], other.CV(i,0),
03488                                     tolerance );
03489     }
03490   }
03491   return rc;
03492 }
03493 
03494 
03495 bool ON_Brep::IsDuplicate( 
03496         const ON_Brep& other, 
03497         double tolerance 
03498         ) const
03499 {
03500   // OBSOLETE FUNCTION - REMOVE
03501   return false;
03502 }
03503 
03504 bool ON_NurbsCurve::Reparameterize(double c)
03505 {
03506   if ( !ON_IsValid(c) || 0.0 == c )
03507     return false;
03508 
03509   if ( 1.0 == c )
03510     return true;
03511 
03512   if ( !MakeRational() )
03513     return false;
03514 
03515   return ON_ReparameterizeRationalNurbsCurve(
03516            c,
03517            m_dim,m_order,m_cv_count,
03518            m_cv_stride,m_cv,m_knot
03519            );
03520 }
03521 
03522 bool ON_ReparameterizeRationalNurbsCurve(
03523           double c, 
03524           int dim, 
03525           int order, 
03526           int cv_count,
03527           int cvstride,
03528           double* cv,
03529           double* knot
03530           )
03531 {
03532   // Reference
03533   //  E. T. Y. Lee and M. L. Lucian 
03534   //  Mobius reparameterization of rational B-splines
03535   //  CAGD Vol8 pp 213-215 1991
03536   const double c1 = c-1.0;
03537   double k0, k1, k, d, w0, w1;
03538   int i,j;
03539 
03540   if ( !ON_IsValid(c) || !ON_IsValid(c1) || 0.0 == c )
03541     return false;
03542 
03543   if ( 1.0 == c )
03544     return true;
03545 
03546   // change domain to [0,1] and then adjust knots
03547   k0 = knot[order-2];
03548   k1 = knot[cv_count-1];
03549   d = k1 - k0;
03550   if ( !ON_IsValid(d) || d <= 0.0 )
03551     return false;
03552   d = 1.0/d;
03553   j = cv_count+order-2;
03554   for ( i = 0; i < j; i++ )
03555   {
03556     k = knot[i];
03557     k = (k - k0)*d;
03558     knot[i] = c*k/(c1*k+1.0);
03559   }
03560 
03561   // adjust cvs
03562   order -= 2;
03563   cvstride -= (dim+1);
03564   for ( i = 0; i < cv_count; i++ )
03565   {
03566     d = c - c1*(*knot++);
03567     j = order;
03568     while(j--)
03569     {
03570       d *= c - c1*knot[j];
03571     }
03572     w0 = cv[dim];
03573     w1 = w0*d;
03574     j = dim;
03575     while(j--)
03576       *cv++ *= d;
03577     *cv++ = w1;
03578     cv += cvstride;
03579   }
03580   order += 2;
03581   cvstride += (dim+1);
03582   cv -= cv_count*cvstride;
03583   knot -= cv_count;
03584 
03585   // change domain back to [k0,k1]
03586   j = cv_count+order-2;
03587   for ( i = 0; i < j; i++ )
03588   {
03589     k = knot[i];
03590     knot[i] = (1.0-k)*k0 + k*k1;
03591   }
03592 
03593   return true;
03594 }
03595 
03596 bool ON_NurbsCurve::ChangeEndWeights(double w0, double w1)
03597 {
03598   if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 )
03599     return false;
03600   if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w0 < 0.0) )
03601     return false;
03602 
03603   if (!ClampEnd(2))
03604     return false;
03605 
03606   if ( w0 == Weight(0) && w1 == Weight(m_cv_count) )
03607     return true;
03608 
03609   if ( !MakeRational() )
03610     return false;
03611 
03612   return ON_ChangeRationalNurbsCurveEndWeights(
03613           m_dim,m_order,
03614           m_cv_count,m_cv_stride,m_cv,
03615           m_knot,
03616           w0,w1);
03617 }
03618 
03619 bool ON_ChangeRationalNurbsCurveEndWeights(
03620           int dim, 
03621           int order, 
03622           int cv_count,
03623           int cvstride, 
03624           double* cv, 
03625           double* knot,
03626           double w0, 
03627           double w1
03628           )
03629 {
03630   double r, s, v0, v1;
03631   int i, j;
03632 
03633   if ( !ON_IsValid(w0) || !ON_IsValid(w1) || 0.0 == w0 || 0.0 == w1 )
03634     return false;
03635   if ( (w0 < 0.0 && w1 > 0.0) || (w0 > 0.0 && w0 < 0.0) )
03636     return false;
03637 
03638   if ( !ON_ClampKnotVector( dim+1, order, cv_count, cvstride, cv, knot, 2 ) )
03639     return false;
03640 
03641   v0 = cv[dim];
03642   v1 = cv[cvstride*(cv_count-1)+dim];
03643   if (!ON_IsValid(v0) || !ON_IsValid(v1) || v0 == 0.0 || v1 == 0.0)
03644     return false;
03645   if (v0 < 0.0 && v1 > 0.0)
03646     return false;
03647   if ( v0 > 0.0 && v1 < 0.0)
03648     return false;
03649 
03650   r = w0/v0;
03651   s = w1/v1;
03652   if ( fabs(r-s) <= fabs(s)*ON_SQRT_EPSILON )
03653   {
03654     // simply scale
03655     if ( r != s )
03656       s = 0.5*(r+s);
03657     r = s;
03658   }
03659 
03660   if ( 1.0 != s && v1 != w1 )
03661   {
03662     // scale to get last weight set to w1
03663     dim++;
03664     cvstride -= dim;
03665     i = cv_count;
03666     while(i--)
03667     {
03668       j = dim;
03669       while(j--)
03670         *cv++ *= s;
03671       cv += cvstride;
03672     }
03673     cvstride += dim;
03674     dim--;
03675     cv -= cvstride*cv_count;
03676   }
03677 
03678   if ( r != s )
03679   {
03680     v0 = cv[dim];
03681     v1 = cv[cvstride*(cv_count-1)+dim];
03682     if ( ON_IsValid(v0) && ON_IsValid(v1) && 0.0 != v0 )
03683     {
03684       // need to scale and reparameterize
03685       r = pow(w0/v0,1.0/((double)(order-1)));
03686       if ( !ON_IsValid(r) )
03687         return false;
03688       if ( !ON_ReparameterizeRationalNurbsCurve(r,dim,order,cv_count,cvstride,cv,knot) )
03689         return false;
03690     }
03691   }
03692 
03693   // make sure weights agree to the last bit! 
03694   cv[dim] = w0;
03695   cv[cvstride*(cv_count-1)+dim] = w1;
03696 
03697   return true;
03698 }
03699 
03700 double ON_Fuzz( double x, double absolute_tolerance )
03701 {
03702   double fuzz = fabs(x)*ON_RELATIVE_TOLERANCE;
03703   return(fuzz > absolute_tolerance) ? fuzz : absolute_tolerance;
03704 }
03705 
03706 bool ON_NurbsCurve::SpanIsSingular( 
03707   int span_index 
03708   ) const
03709 {
03710   const int cv_size = CVSize();
03711   if (    m_order < 2 
03712        || m_cv_count < m_order
03713        || m_dim <= 0
03714        || cv_size > m_cv_stride 
03715        || 0 == m_knot
03716        || 0 == m_cv
03717        )
03718   {
03719     ON_ERROR("Invalid NURBS curve.");
03720     return false;
03721   }
03722 
03723   if ( span_index < 0 || span_index > m_cv_count-m_order )
03724   {
03725     ON_ERROR("span_index parameter is out of range.");
03726     return false;
03727   }
03728 
03729   const double* cv = CV(span_index);
03730   const double* knot = m_knot + span_index;
03731 
03732   if ( !(knot[m_order-2] < knot[m_order-1]) )
03733   {
03734     // vacuous question because there is no "span" evaluate.
03735     // I chose return false here so people won't try to
03736     // remove this empty span.
03737     // no call to ON_ERROR here
03738     return false; 
03739   }
03740 
03741   double* p = 0;
03742   int cv_stride = m_cv_stride;
03743   if ( knot[0] != knot[m_order-2] || knot[m_order-1] != knot[2*m_order-3] )
03744   {
03745     const size_t sizeof_cv = cv_size*sizeof(p[0]);
03746     p = (double*)onmalloc(sizeof_cv*m_order);
03747     for ( int i = 0; i < m_order; i++ )
03748       memcpy( p+(i*cv_size), cv+(i*cv_stride), sizeof_cv );
03749     ON_ConvertNurbSpanToBezier( cv_size, m_order, cv_size, p,
03750                                                                                                                           knot, knot[m_order-2], knot[m_order-1] 
03751                               );
03752     cv_stride = cv_size;
03753     cv = p;
03754   }
03755   const bool rc = ON_PointsAreCoincident(m_dim,m_is_rat,m_order,cv_stride,cv);
03756   if ( 0 != p )
03757     onfree(p);
03758 
03759   return rc;
03760 }
03761 
03762 bool ON_NurbsCurve::RemoveSpan(
03763   int span_index 
03764   )
03765 {
03766   const int cv_size = CVSize();
03767   if (    m_order < 2 
03768        || m_cv_count < m_order
03769        || m_dim <= 0
03770        || cv_size > m_cv_stride 
03771        || 0 == m_knot
03772        || 0 == m_cv
03773        )
03774   {
03775     ON_ERROR("Invalid NURBS curve.");
03776     return false;
03777   }
03778 
03779   if ( span_index < 0 || span_index > m_cv_count-m_order )
03780   {
03781     ON_ERROR("span_index parameter is out of range.");
03782     return false;
03783   }
03784 
03785   if ( m_cv_count == m_order )
03786   {
03787     ON_ERROR("Cannot remove the only span from a Bezier NURBS curve.");
03788     return false;
03789   }
03790 
03791   const size_t sizeof_cv = cv_size*sizeof(m_cv[0]);
03792   int i, j;
03793 
03794   const double knot0 = m_knot[span_index+m_order-2];
03795   const double knot1 = m_knot[span_index+m_order-1];
03796   const double knot_delta = (knot0 < knot1) ? (knot1 - knot0) : 0.0;
03797 
03798   const bool bIsPeriodic0 = IsPeriodic()?true:false;
03799 
03800   if ( span_index <= 0 )
03801   {
03802     // remove initial span
03803     // set span_index = index of the span to keep.
03804     for ( span_index = 1; span_index < m_cv_count-m_order; span_index++ )
03805     {
03806       if ( m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] )
03807         break;
03808     }
03809     for ( i = 0; i+span_index < m_cv_count; i++ )
03810       memcpy(CV(i),CV(i+span_index),sizeof_cv);
03811     for ( i = 0; i+span_index < m_cv_count+m_order-2; i++ )
03812     {
03813       m_knot[i] = (knot1 == m_knot[i+span_index])
03814                 ? knot0
03815                 : (m_knot[i+span_index] - knot_delta);
03816     }
03817     m_cv_count -= span_index;
03818   }
03819   else if ( span_index >= m_cv_count-m_order )
03820   {
03821     // remove final span
03822     // set span_index = index of the span to keep.
03823     for ( span_index = m_cv_count-m_order-1; span_index > 0; span_index-- )
03824     {
03825       if ( m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] )
03826         break;
03827     }
03828     m_cv_count = span_index+m_order;
03829   }
03830   else
03831   {
03832     // remove interior span
03833     int k0 = span_index+m_order-2;
03834     int k1 = span_index+m_order-1;
03835     int i0 = k0;
03836     int i1 = k1;
03837     for ( i0 = k0; i0 > 0; i0-- )
03838     {
03839       if ( m_knot[i0-1] < m_knot[k0] )
03840         break;
03841     }
03842     for ( i1 = k1; i1 < m_cv_count+m_order-3; i1++ )
03843     {
03844       if ( m_knot[i1+1] > m_knot[k1] )
03845         break;
03846     }
03847     int m = (i1-i0+1);
03848     if ( !(knot_delta > 0.0) )
03849     {
03850       if ( !(m_knot[i0] == m_knot[i1]) || m < m_order )
03851       {
03852         ON_ERROR("span_index parameter identifies an empty span.");
03853         return false;
03854       }
03855     }
03856 
03857     int span_index0 = i0 - (m_order-1);
03858     double* cv0 = 0;
03859     if ( span_index0 >= 0 && k0 - i0 + 1 < m_order-1 )
03860     {
03861       cv0 = (double*)onmalloc( (m_order*cv_size + 2*m_order-2)*sizeof(cv0[0]) );
03862       double* knot0 = cv0 + (m_order*cv_size);
03863       memcpy( knot0, m_knot+span_index0, (2*m_order-2)*sizeof(knot0[0]) );
03864       for ( i = 0; i < m_order; i++ )
03865         memcpy( cv0 + (i*cv_size), CV(span_index0+i), sizeof_cv );
03866       ON_ClampKnotVector( cv_size, m_order, m_order, cv_size, cv0, knot0, 1 );
03867     }
03868 
03869     if ( m < m_order-1 )
03870     {
03871       i = m_order-1 - m;
03872       ReserveCVCapacity( m_cv_stride*(m_cv_count+i) );
03873       ReserveKnotCapacity( m_cv_count+m_order-2+i );
03874       for ( j = m_cv_count+m_order-3; j >= i1-m_order+2; j-- )
03875         m_knot[j+i] = m_knot[j];
03876       for ( j = m_cv_count-1; j >= i1-m_order+2; j-- )
03877         memcpy(CV(j+i),CV(j),sizeof_cv);
03878       i1 += i;
03879       k1 += i;
03880       m_cv_count += i;
03881     }
03882 
03883     if ( i1-k1 < m_order-2 )
03884       ON_ClampKnotVector( cv_size, m_order, m_order, m_cv_stride, 
03885                           m_cv + ((i1-m_order+2)*m_cv_stride), 
03886                           m_knot + (i1-m_order+2), 
03887                           0 );
03888 
03889     k0 = i0;
03890     k1 = i1-m_order+2;
03891 
03892     if ( 0 != cv0 )
03893     {
03894       for ( i = 0; i < m_order-1; i++ )
03895         memcpy(CV(i+span_index0),cv0 + (i*cv_size),sizeof_cv);
03896       onfree(cv0);
03897       cv0 = 0;
03898     }
03899 
03900     if ( k0 < k1 )
03901     {
03902       for ( i = 0; i + k1 < m_cv_count; i++ )
03903         memcpy(CV(i+k0),CV(i+k1),sizeof_cv);
03904       for ( i = 0; i + k1 < m_cv_count+m_order-2; i++ )
03905       {
03906         m_knot[i+k0] = (knot1 == m_knot[i+k1]) 
03907                      ? knot0
03908                      : (m_knot[i+k1] - knot_delta);
03909       }
03910       m_cv_count -= (k1-k0);
03911     }
03912     else if ( k0 == k1 && knot_delta > 0.0 )
03913     {
03914       for ( i = k0; i < m_cv_count+m_order-2; i++ )
03915       {
03916         m_knot[i] = (knot1 == m_knot[i])
03917                   ? knot0
03918                   : (m_knot[i] - knot_delta);
03919       }
03920     }
03921   }
03922 
03923   if ( false == bIsPeriodic0 || false == IsPeriodic() )
03924     ClampEnd(2);
03925 
03926   return true;
03927 }
03928 
03929 int ON_NurbsCurve::RemoveSingularSpans()
03930 {
03931   const int cv_size = CVSize();
03932   if (    m_order < 2 
03933        || m_cv_count < m_order
03934        || m_dim <= 0
03935        || cv_size > m_cv_stride 
03936        || 0 == m_knot
03937        || 0 == m_cv
03938        )
03939   {
03940     ON_ERROR("Invalid NURBS curve.");
03941     return 0;
03942   }
03943 
03944   int singular_span_count = 0;
03945 
03946   for ( int span_index = 0; m_cv_count > m_order && span_index <= m_cv_count-m_order; span_index++ )
03947   {
03948     if (    m_knot[span_index+m_order-2] < m_knot[span_index+m_order-1] 
03949          && SpanIsSingular(span_index) 
03950        )
03951     {
03952       const int cv_count0 = m_cv_count;
03953       if ( RemoveSpan(span_index) )
03954         singular_span_count++;
03955       if ( 0 == span_index || m_cv_count < cv_count0 )
03956         span_index--;
03957     }
03958   }
03959 
03960   return singular_span_count;
03961 }


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:27:02