opennurbs_polycurve.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_PolyCurve,ON_Curve,"4ED7D4E0-E947-11d3-BFE5-0010830122F0");
00020 
00021 ON_PolyCurve::ON_PolyCurve()
00022 {
00023 }
00024 
00025 ON_PolyCurve::ON_PolyCurve( int capacity )
00026               : m_segment(capacity), m_t(capacity+1)
00027 {
00028   m_segment.Zero();
00029 }
00030 
00031 ON_PolyCurve::ON_PolyCurve( const ON_PolyCurve& src )
00032               : m_segment(src.Count()), m_t(src.Count()+1)
00033 {
00034   *this = src;
00035 }
00036 
00037 void ON_PolyCurve::Destroy()
00038 {
00039   // release memory
00040   m_segment.Destroy();
00041   m_t.Destroy();
00042 }
00043 
00044 ON_PolyCurve::~ON_PolyCurve()
00045 {
00046   Destroy();
00047 }
00048 
00049 void ON_PolyCurve::EmergencyDestroy()
00050 {
00051   m_segment.EmergencyDestroy();
00052   m_t.EmergencyDestroy();
00053 }
00054 
00055 void ON_PolyCurve::DestroyRuntimeCache( bool bDelete )
00056 {
00057   ON_Curve::DestroyRuntimeCache(bDelete);
00058   int i, count = m_segment.Count();
00059   for ( i = 0; i < count; i++ )
00060   {
00061     ON_Curve* segment_curve = m_segment[i];
00062     if ( 0 != segment_curve && this != segment_curve )
00063       segment_curve->DestroyRuntimeCache(bDelete);
00064   }
00065 }
00066 
00067 unsigned int ON_PolyCurve::SizeOf() const
00068 {
00069   unsigned int sz = ON_Curve::SizeOf();
00070   sz += (sizeof(*this) - sizeof(ON_Curve));
00071   sz += m_t.SizeOfArray();
00072   sz += m_segment.SizeOfArray();
00073   int i, count = m_segment.Count();
00074   for ( i = 0; i < count; i++ )
00075   {
00076     const ON_Curve* crv = m_segment[i];
00077     if ( crv )
00078       sz += crv->SizeOf();
00079   }
00080   return sz;
00081 }
00082 
00083 ON__UINT32 ON_PolyCurve::DataCRC(ON__UINT32 current_remainder) const
00084 {
00085   int i, count = m_segment.Count();
00086   for ( i = 0; i < count; i++ )
00087   {
00088     const ON_Curve* crv = m_segment[i];
00089     if ( crv )
00090       current_remainder = crv->DataCRC(current_remainder);
00091   }
00092   current_remainder = m_t.DataCRC(current_remainder);
00093   return current_remainder;
00094 }
00095 
00096 
00097 ON_PolyCurve& ON_PolyCurve::operator=( const ON_PolyCurve& src )
00098 {
00099   if ( this != &src ) {
00100     ON_Curve::operator=(src);
00101     const int segment_capacity = m_segment.Capacity();
00102     ON_Curve** segment = m_segment.Array();
00103     int i;
00104     for ( i = 0; i < segment_capacity; i++ ) {
00105       if ( segment[i] ) {
00106         delete segment[i];
00107         segment[i] = 0;
00108       }
00109     }
00110     src.m_segment.Duplicate(m_segment);
00111     m_t.SetCount(0);
00112     m_t.SetCapacity(src.m_t.Count());
00113     m_t.Zero();
00114     m_t = src.m_t;
00115   }
00116   return *this;
00117 }
00118 
00119 int ON_PolyCurve::Dimension() const
00120 {
00121   const ON_Curve* p = SegmentCurve(0);
00122   return (p) ? p->Dimension() : 0;
00123 }
00124 
00125 ON_BOOL32 
00126 ON_PolyCurve::GetBBox( // returns true if successful
00127          double* boxmin,    // minimum
00128          double* boxmax,    // maximum
00129          ON_BOOL32 bGrowBox
00130          ) const
00131 {
00132   const int count = Count();
00133   int segment_index;
00134   ON_BOOL32 rc = (count>0) ? true : false;
00135   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
00136     rc = m_segment[segment_index]->GetBBox( boxmin, boxmax, bGrowBox );
00137     bGrowBox = true;
00138   }
00139   return rc;
00140 }
00141 
00142 ON_BOOL32
00143 ON_PolyCurve::Transform( const ON_Xform& xform )
00144 {
00145   TransformUserData(xform);
00146   DestroyRuntimeCache();
00147   const int count = Count();
00148   int segment_index;
00149   ON_BOOL32 rc = (count>0) ? true : false;
00150   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
00151     rc = m_segment[segment_index]->Transform( xform );
00152   }
00153   return rc;
00154 }
00155 
00156 bool ON_PolyCurve::IsDeformable() const
00157 {
00158   bool rc = true;
00159   const int count = Count();
00160   int segment_index;
00161   for ( segment_index = 0; segment_index < count ; segment_index++ )
00162   {
00163     const ON_Curve* seg = m_segment[segment_index];
00164     if ( seg && !seg->IsDeformable() )
00165     {
00166       rc = false;
00167       break;
00168     }
00169   }
00170   return rc;
00171 }
00172 
00173 bool ON_PolyCurve::MakeDeformable()
00174 {
00175   bool rc = true;
00176   bool bDestroyRuntimeCache = false;
00177   const int count = Count();
00178   int segment_index;
00179   for ( segment_index = 0; segment_index < count ; segment_index++ )
00180   {
00181     ON_Curve* seg = m_segment[segment_index];
00182     if ( seg && !seg->IsDeformable() )
00183     {
00184       bDestroyRuntimeCache = true;
00185       if ( !seg->MakeDeformable() )
00186       {
00187         ON_NurbsCurve* nurbs_curve = seg->NurbsCurve();
00188         if ( nurbs_curve )
00189         {
00190           delete seg;
00191           m_segment[segment_index] = nurbs_curve;
00192         }
00193         else
00194           rc = false;
00195       }
00196     }
00197   }
00198   if ( bDestroyRuntimeCache )
00199     DestroyRuntimeCache();
00200   return rc;
00201 }
00202 
00203 
00204 ON_BOOL32
00205 ON_PolyCurve::SwapCoordinates( int i, int j )
00206 {
00207   const int count = Count();
00208   int segment_index;
00209   ON_BOOL32 rc = (count>0) ? true : false;
00210   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
00211     rc = m_segment[segment_index]->SwapCoordinates( i, j );
00212   }
00213         DestroyCurveTree();
00214   return rc;
00215 }
00216 
00217 ON_BOOL32 ON_PolyCurve::IsValid( ON_TextLog* text_log ) const
00218 {
00219   return IsValid(false,text_log);
00220 }
00221 
00222 bool ON_PolyCurve::IsValid( bool bAllowGaps, ON_TextLog* text_log ) const
00223 {
00224   const int count = Count();
00225   const int dim = Dimension();
00226   ON_3dPoint p0, p1;
00227   int segment_index;
00228   if ( count <= 0 || dim <= 0 )
00229   {
00230     if ( text_log )
00231       text_log->Print("Polycurve segment count = %d and dim = %d\n",count,dim);
00232     return ON_IsNotValid();
00233   }
00234 
00235   if ( m_t.Count() != count+1 )
00236   {
00237     if ( text_log )
00238       text_log->Print("Polycurve segment count = %d and m_t.Count()=%d (should be segment count+1)\n",
00239                       count,m_t.Count());
00240     return ON_IsNotValid();
00241   }
00242 
00243   for ( segment_index = 0; segment_index < count; segment_index++ ) 
00244   {
00245     if ( 0 == m_segment[segment_index] )
00246     {
00247       if ( text_log )
00248       {
00249         text_log->Print("Polycurve segment[%d] is null.\n",segment_index);
00250       }
00251       return ON_IsNotValid();
00252     }
00253 
00254     if ( !m_segment[segment_index]->IsValid( text_log ) )
00255     {
00256       if ( text_log )
00257       {
00258         text_log->Print("Polycurve segment[%d] is not valid.\n",segment_index);
00259       }
00260       return ON_IsNotValid();
00261     }
00262 
00263     int seg_dim = m_segment[segment_index]->Dimension();
00264     if ( seg_dim != dim )
00265     {
00266       if ( text_log )
00267         text_log->Print("Polycurve segment[%d]->Dimension()=%d (should be %d).\n",segment_index,seg_dim,dim);
00268       return ON_IsNotValid(); // all segments must have same dimension
00269     }
00270 
00271     if ( m_t[segment_index] >= m_t[segment_index+1] )
00272     {
00273       if ( text_log )
00274         text_log->Print("Polycurve m_t[%d]=%g and m_t[%d]=%g (should be increasing)\n",
00275                          segment_index,   m_t[segment_index],
00276                          segment_index+1, m_t[segment_index+1]);
00277       return ON_IsNotValid(); // segment domain must be non-empty
00278     }
00279 
00280     if ( count > 1 && !bAllowGaps && m_segment[segment_index]->IsClosed() ) 
00281     {
00282       if ( text_log )
00283         text_log->Print("Polycurve segment[%d] is closed (%d segments).\n",segment_index,count);
00284       return ON_IsNotValid(); // closed segments not permitted in multi segment curve
00285     }
00286   }
00287 
00288   if ( !bAllowGaps )
00289   {
00290     // check for gaps
00291     int gap_index = FindNextGap(0);
00292     if ( gap_index > 0 )
00293     {
00294       p0 = m_segment[gap_index-1]->PointAtEnd();
00295       p1 = m_segment[gap_index]->PointAtStart();
00296       double d = p0.DistanceTo(p1);
00297       if ( text_log )
00298         text_log->Print("Polycurve end of segment[%d] != start of segment[%d] (distance=%g)\n",
00299                         gap_index-1, gap_index, d );
00300       return ON_IsNotValid(); // not contiguous
00301     }
00302   }
00303 
00304   return true;
00305 }
00306 
00307 void ON_PolyCurve::Dump( ON_TextLog& dump ) const
00308 {
00309   const int count = Count();
00310   int i;
00311 
00312   ON_3dPoint segment_start = ON_3dPoint::UnsetPoint;
00313   ON_3dPoint segment_end = ON_3dPoint::UnsetPoint;
00314   double gap;
00315 
00316   dump.Print( "ON_PolyCurve segment count = %d\n", count );
00317   dump.PushIndent();
00318   for ( i = 0; i < count; i++ )
00319   {
00320     if ( 0 != m_segment[i] )
00321       segment_start = m_segment[i]->PointAtStart();
00322     else
00323       segment_start = ON_3dPoint::UnsetPoint;
00324     gap = (segment_start.IsValid() && segment_end.IsValid())
00325         ? segment_start.DistanceTo(segment_end)
00326         : ON_UNSET_VALUE;
00327     dump.Print( "Segment %d: (%g,%g)", i+1, m_t[i], m_t[i+1] );
00328     if ( i > 0 )
00329     {
00330       if ( ON_IsValid(gap) )
00331         dump.Print(" gap = %.17g",gap);
00332       else if ( !segment_start.IsValid() )
00333         dump.Print(" invalid segment curve");
00334       else if ( !segment_end.IsValid() )
00335         dump.Print(" invalid previous segment curve");
00336     }
00337     dump.Print("\n");
00338 
00339     dump.PushIndent();
00340     if ( 0 == m_segment[i] )
00341     {
00342       dump.Print("null curve pointer\n");
00343       segment_end = ON_3dPoint::UnsetPoint;
00344     }
00345     else
00346     {
00347       m_segment[i]->Dump(dump);
00348       segment_end = m_segment[i]->PointAtEnd();
00349     }
00350     dump.PopIndent();
00351   }
00352   dump.PopIndent();
00353 }
00354 
00355 ON_BOOL32 ON_PolyCurve::Write(
00356        ON_BinaryArchive& file // open binary file
00357      ) const
00358 {
00359   // NOTE - if changed, check legacy I/O code
00360   ON_BOOL32 rc = file.Write3dmChunkVersion(1,0);
00361   if (rc) {
00362     int reserved1 = 0;
00363     int reserved2 = 0;
00364     const int count = Count();
00365     int segment_index;
00366 
00367     rc = file.WriteInt( count );
00368     if (rc) file.WriteInt(reserved1);
00369     if (rc) file.WriteInt(reserved2);
00370     if (rc) {
00371       // may be set in future
00372       ON_BoundingBox bbox;
00373       rc = file.WriteBoundingBox(bbox);
00374     }
00375     if (rc) rc = file.WriteArray( m_t );
00376 
00377     for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
00378       rc = file.WriteObject( *SegmentCurve(segment_index) );
00379     }
00380 
00381 
00382   }
00383   return rc;
00384 }
00385 
00386 ON_BOOL32 ON_PolyCurve::Read(
00387        ON_BinaryArchive& file  // open binary file
00388      )
00389 {
00390   // NOTE - if changed, check legacy I/O code
00391   Destroy();
00392   int major_version = 0;
00393   int minor_version = 0;
00394   
00395   ON_BOOL32 rc = file.Read3dmChunkVersion(&major_version,&minor_version);
00396   
00397   if (rc) 
00398   {
00399     ON_Object* obj;
00400     ON_Curve* crv;
00401     int segment_index;
00402     int segment_count = 0;
00403     int reserved1 = 0;
00404     int reserved2 = 0;
00405     rc = file.ReadInt(&segment_count);
00406     if (rc) rc = file.ReadInt(&reserved1);
00407     if (rc) rc = file.ReadInt(&reserved2);
00408     if (rc) {
00409       // may be set in future
00410       ON_BoundingBox bbox;
00411       rc = file.ReadBoundingBox(bbox);
00412     }
00413     if (rc) rc = file.ReadArray( m_t );
00414 
00415     for ( segment_index = 0; segment_index < segment_count && rc; segment_index++ ) {
00416       obj = 0;
00417       crv = 0;
00418       rc = file.ReadObject( &obj );
00419       if (rc) {
00420         crv = ON_Curve::Cast(obj);
00421         if (crv) {
00422           //Append(crv); - this one adds to m_t[]
00423           m_segment.Append(crv);
00424         }
00425         else {
00426           ON_ERROR("ON_PolyCurve::Read() - non ON_Curve object in segment list\n");
00427           delete obj;
00428           rc = false;
00429         }
00430       }
00431     }
00432 
00433     if ( rc && m_segment.Count()>0 && 
00434                                                                 m_segment.Count()==segment_count && m_t.Count()==segment_count+1)
00435     {
00436       // remove "fuzz" in m_t[] domain array that is in some older files.
00437       double s, t, d0, d1, fuzz;
00438       ON_Interval in0, in1;
00439       in1 = SegmentCurve(0)->Domain();
00440       d1 = in1.Length();
00441       for ( segment_index = 1; segment_index < segment_count; segment_index++ )
00442       {
00443         t = m_t[segment_index];
00444         in0 = in1;
00445         d0 = d1;
00446         in1 = SegmentCurve(segment_index)->Domain();
00447         d1 = in1.Length();
00448         s = in0[1];
00449         if ( s != t && s == in1[0] && t > in0[0] && t < in1[1] )
00450         {
00451           fuzz = ((d0<=d1)?d0:d1)*ON_SQRT_EPSILON;
00452           if ( fabs(t-s) <= fuzz )
00453             m_t[segment_index] = s;
00454         }
00455       }
00456       fuzz = d1*ON_SQRT_EPSILON;
00457       t = m_t[segment_count];
00458       s = in1[1];
00459       if ( s != t && t > in1[0] && fabs(s-t) <= fuzz )
00460         m_t[segment_count] = s;
00461     }
00462   }
00463 
00464   if (rc && file.ArchiveOpenNURBSVersion() < 200304080 )
00465   {
00466     // 8 April 2003 Dale Lear:
00467     //   Some archives written by earlier versions
00468     //   of Rhino had nested polycurves and polycurves with
00469     //   zero length segments.  This code cleans up
00470     //   those problems.  See RR 8932.
00471     RemoveNesting();
00472     //RemoveShortSegments(ON_ZERO_TOLERANCE);
00473   }
00474 
00475   return rc;
00476 }
00477 
00478 
00479 ON_Curve* ON_PolyCurve::DuplicateCurve() const
00480 {
00481         // Call DuplicateCurve on each segment to construct duplicate curve.
00482         int cnt = Count();
00483         ON_PolyCurve* dup_crv = new ON_PolyCurve( cnt );
00484   dup_crv->CopyUserData(*this);
00485         for( int i=0; i<cnt; i++){
00486                 const ON_Curve* seg = SegmentCurve(i);
00487                 if(seg)
00488                         dup_crv->Append( seg->DuplicateCurve() );
00489         }       
00490         if( cnt == dup_crv->Count() )
00491                 dup_crv->SetParameterization( m_t);
00492         return dup_crv;
00493 }
00494 
00495 
00496 ON_Interval ON_PolyCurve::Domain() const
00497 {
00498   ON_Interval d;
00499   const int count = Count();
00500   if ( count > 0 && count+1 == m_t.Count() && m_t[0] < m_t[count] ) {
00501     d.Set(m_t[0],m_t[count]);
00502   }
00503   return d;
00504 }
00505 
00506 ON_BOOL32 ON_PolyCurve::SetDomain( double t0, double t1 )
00507 {
00508   ON_Interval d0 = Domain();
00509   ON_Interval d1(t0,t1);
00510   ON_BOOL32 rc = d1.IsIncreasing();
00511   if ( rc && d0 != d1 )
00512   {
00513     int i, count = m_t.Count();
00514     double s;
00515     for ( i = 0; i < count; i++ )
00516     {
00517       s = d0.NormalizedParameterAt( m_t[i] );
00518       m_t[i] = d1.ParameterAt( s );
00519     }
00520         DestroyRuntimeCache();
00521   }
00522   return rc;
00523 }
00524 
00525 
00526 bool ON_PolyCurve::ChangeDimension( int desired_dimension )
00527 {
00528   int i, count = m_segment.Count();
00529   bool rc = (count>0);
00530   for ( i = 0; i < count; i++ )
00531   {
00532     ON_Curve* curve = m_segment[i];
00533     if ( 0 != curve )
00534     {
00535       if ( !curve->ChangeDimension(desired_dimension) )
00536         rc = false;
00537     }
00538     else
00539       rc = false;
00540   }
00541   return rc;
00542 }
00543 
00544 bool ON_PolyCurve::SetParameterization( const double* t )
00545 {
00546   bool rc = false;
00547   int i, count = m_segment.Count()+1;
00548   if ( count >= 2 && 0 != t && ON_UNSET_VALUE != t[0] )
00549   {
00550     for ( i = 1; i < count; i++ )
00551     {
00552       if ( t[i] == ON_UNSET_VALUE )
00553         break;
00554       if ( t[i-1] >= t[i] )
00555         break;
00556     }
00557     if ( i == count )
00558     {
00559       m_t.Reserve(count);
00560       m_t.SetCount(0);
00561       m_t.Append( count, t );
00562       rc = true;
00563     }
00564   }
00565   return rc;
00566 }
00567 
00568 ON_BOOL32 ON_PolyCurve::ChangeClosedCurveSeam( double t )
00569 {
00570   ON_BOOL32 rc = IsClosed();
00571   if ( rc )
00572   {
00573         DestroyRuntimeCache();
00574     rc = false;
00575     const int old_count = Count();
00576     const ON_Interval old_dom = Domain();
00577     ON_Curve* scrv = 0;
00578     if ( old_count == 1 )
00579     {
00580       scrv = SegmentCurve(0);
00581       if ( scrv )
00582       {
00583         ON_Interval sdom = scrv->Domain();
00584         double s = ( old_dom == sdom )
00585                  ? t
00586                  : sdom.ParameterAt( old_dom.NormalizedParameterAt(t) );
00587         rc = scrv->ChangeClosedCurveSeam(s);
00588         if ( rc )
00589           SetDomain( t, t + old_dom.Length() );
00590       }
00591     }
00592     else
00593     {
00594       double k = t;
00595       if ( !old_dom.Includes(t) )
00596       {
00597         double s = old_dom.NormalizedParameterAt(t);
00598         s = fmod(s,1.0);
00599         if ( s < 0.0 )
00600           s += 1.0;
00601         k = old_dom.ParameterAt(s);
00602       }
00603       if ( old_dom.Includes(k,true) )
00604       {
00605         int segment_index = ON_NurbsSpanIndex(2,old_count+1,m_t.Array(),k,0,0);
00606         scrv = m_segment[segment_index];
00607         if ( k < m_t[segment_index] )
00608           return false;
00609         if ( k >= m_t[segment_index+1] )
00610           return false;
00611         int new_count = (k==m_t[segment_index]) ? old_count : old_count+1;
00612         ON_Curve* sleft = 0;
00613         ON_Curve* sright = 0;
00614         if ( new_count > old_count )
00615         {
00616                                         ON_Interval subdom(m_t[segment_index], m_t[segment_index+1]);
00617                                         double nt = subdom.NormalizedParameterAt(k);
00618                                         ON_Interval Segdom = scrv->Domain();
00619                                         double segt = Segdom.ParameterAt(nt);
00620           rc = scrv->Split( segt, sleft, sright );
00621 
00622 //                              Greg Arden 6 May 2003. Fixes TRR#10332.  If split fails we break the
00623 //                              curve between segments and adjust the parameterization
00624                                         if(!rc){
00625                                                 if(nt>.5){
00626                                                         segment_index++;
00627                                                         if(segment_index<old_count)
00628                                                                 scrv = m_segment[segment_index];                
00629                                                         else
00630                                                                 scrv = NULL;
00631                                                 }
00632                                                 new_count--;
00633                                         }
00634         }
00635         if(new_count==old_count)
00636         {
00637           sright = scrv;
00638           scrv = 0;
00639           rc = true;
00640         }
00641         if ( rc && segment_index<old_count)
00642         {
00643           m_segment[segment_index] = 0;//todo
00644           ON_SimpleArray<ON_Curve*> new_c(new_count);
00645           ON_SimpleArray<double> new_t(new_count+1);
00646           new_c.Append(sright);
00647           new_t.Append(k);
00648           new_c.Append( old_count-segment_index-1, m_segment.Array()+segment_index+1);
00649           new_t.Append( old_count-segment_index-1, m_t.Array()+segment_index+1);
00650           int j = new_t.Count();
00651           new_c.Append( segment_index, m_segment.Array() );
00652           new_t.Append( segment_index, m_t.Array() );
00653           if ( sleft )
00654           {
00655             new_c.Append(sleft);
00656             new_t.Append(m_t[segment_index]);
00657           }
00658           new_t.Append(k);
00659           double d = old_dom.Length();
00660           while (j < new_t.Count() )
00661           {
00662             new_t[j] += d;
00663             j++;
00664           }
00665 
00666           // take care when moving new_c pointers to m_segment
00667           // so that we don't delete any curves.
00668           memset( m_segment.Array(), 0, m_segment.Capacity()*sizeof( *m_segment.Array() ) );
00669           m_segment.SetCount(0);
00670           m_segment.Append( new_c.Count(), new_c.Array() );
00671           m_t = new_t;
00672           if ( scrv )
00673           {
00674             delete scrv;
00675             scrv = 0;
00676           }
00677         }
00678       }
00679       else
00680       {
00681         // k is already the start or  end of the existing curve
00682         rc = true;
00683       }
00684       if ( rc )
00685         SetDomain( t, t + old_dom.Length() );
00686     }
00687   }
00688   return rc;
00689 }
00690 
00691 int ON_PolyCurve::SpanCount() const
00692 {
00693   int span_count = 0;
00694   const int segment_count = Count();
00695   int i, j;
00696   for ( i = 0; i < segment_count; i++  ) {
00697     if ( !m_segment[i] )
00698       return false;
00699     j = m_segment[i]->SpanCount();
00700     if ( j == 0 )
00701       return 0;
00702     span_count += j;
00703   }
00704   return span_count;
00705 }
00706 
00707 ON_BOOL32 ON_PolyCurve::GetSpanVector( // span "knots" 
00708        double* s // array of length SpanCount() + 1 
00709        ) const
00710 {
00711   ON_Interval sp;
00712   double t;
00713   const int segment_count = Count();
00714   int i, j, k;
00715   for ( i = 0; i < segment_count; i++  ) {
00716     if ( !m_segment[i] )
00717       return false;
00718     j = m_segment[i]->SpanCount();
00719     if ( j == 0 )
00720       return 0;
00721     if ( !m_segment[i]->GetSpanVector( s ) )
00722       return false;
00723     sp.Set( m_t[i], m_t[i+1] );
00724                 ON_Interval segloc(s[0],s[j]);
00725     if ( sp.Min() != s[0] || sp.Max() != s[j] ) {
00726       for ( k = 0; k <= j; k++ ) {
00727         t = segloc.NormalizedParameterAt(s[k]);
00728         s[k] = sp.ParameterAt(t);
00729       }
00730     }
00731 
00732     s += j;
00733   }
00734   return true;
00735 }
00736 
00737 int ON_PolyCurve::Degree() const
00738 {
00739   const int segment_count = Count();
00740   int span_degree = 0;
00741   int segment_index, d;
00742   for ( segment_index = 0; segment_index < segment_count; segment_index++  ) {
00743     if ( !m_segment[segment_index] )
00744       return false;
00745     d = m_segment[segment_index]->Degree();
00746     if ( d <= 0 )
00747       return 0;
00748     if ( d > span_degree )
00749       span_degree = d;
00750   }
00751   return span_degree;
00752 }
00753 
00754 
00755 ON_BOOL32
00756 ON_PolyCurve::IsLinear( // true if curve locus is a line segment
00757       double tolerance // tolerance to use when checking linearity
00758       ) const
00759 {
00760   ON_BOOL32 rc = false;
00761   int i, count = Count();
00762         if ( count==1)
00763                 return m_segment[0]->IsLinear(tolerance);
00764   
00765         else if ( count > 1 ) {
00766     rc = true;
00767     for ( i = 0; rc && i < count; i++ ) {
00768       if ( !m_segment[i] )
00769         rc = false;
00770       else
00771         rc = m_segment[i]->IsLinear(tolerance);
00772     
00773     }
00774     if (rc)
00775       rc = ON_Curve::IsLinear(tolerance);
00776   }
00777   return rc;
00778 }
00779 
00780 int ON_PolyCurve::IsPolyline(
00781       ON_SimpleArray<ON_3dPoint>* pline_points,
00782       ON_SimpleArray<double>* pline_t
00783       ) const
00784 {
00785   int i, seg_i, seg_rc;
00786   ON_Interval sdom, cdom;
00787   int rc = 0;
00788   if ( pline_points )
00789     pline_points->SetCount(0);
00790   if ( pline_t )
00791     pline_t->SetCount(0);
00792   const int seg_count = Count();
00793   if ( seg_count == 1 )
00794   {
00795     if ( m_segment[0] )
00796       rc = m_segment[0]->IsPolyline(pline_points,pline_t);
00797     if (pline_t && rc > 0)
00798     {
00799       sdom.Set(m_t[0],m_t[1]);
00800       cdom = m_segment[0]->Domain();
00801       if ( sdom != cdom )
00802       {
00803         for ( i = 0; i < pline_t->Count(); i++ )
00804           (*pline_t)[i] = sdom.ParameterAt(cdom.NormalizedParameterAt((*pline_t)[i]));
00805       }
00806     }
00807   }
00808   else if (seg_count > 1 )
00809   {
00810     ON_SimpleArray<ON_3dPoint> seg_points;
00811     ON_SimpleArray<double> seg_t;
00812     for ( seg_i = 0; seg_i < seg_count; seg_i++ )
00813     {
00814       seg_points.SetCount(0);
00815       seg_t.SetCount(0);
00816       seg_rc = m_segment[seg_i]->IsPolyline(pline_points?&seg_points:0,pline_t?&seg_t:0);
00817       if ( seg_rc < 2 )
00818       {
00819         if ( pline_points )
00820           pline_points->SetCount(0);
00821         if ( pline_t )
00822           pline_t->SetCount(0);
00823         rc = 0;
00824         break;
00825       }
00826       rc += seg_rc;
00827       if ( seg_i )
00828         rc--;
00829       if ( pline_t )
00830       {
00831         sdom.Set( m_t[seg_i], m_t[seg_i+1] );
00832         cdom = m_segment[seg_i]->Domain();
00833         if ( sdom != cdom )
00834         {
00835           for ( i = 0; i < seg_t.Count(); i++ )
00836             seg_t[i] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg_t[i]));
00837         }
00838         if ( pline_t->Count() > 0 )
00839           pline_t->Remove();
00840         pline_t->Append( seg_t.Count(), seg_t.Array() );
00841       }
00842       if ( pline_points )
00843       {
00844         if ( pline_points->Count() > 0 )
00845           pline_points->Remove();
00846         pline_points->Append( seg_points.Count(), seg_points.Array() );
00847       }
00848     }
00849     if(IsClosed() && pline_points && pline_points->Count() > 3)
00850     {
00851       // GBA 2/26/03. Make closed polylines spot on closed
00852       *pline_points->Last() = *pline_points->First();
00853     }
00854   }
00855   return rc;
00856 }
00857 
00858 
00859 ON_BOOL32
00860 ON_PolyCurve::IsArc( // true if curve locus in an arc or circle
00861       const ON_Plane* plane, // if not NULL, test is performed in this plane
00862       ON_Arc* arc,         // if not NULL and true is returned, then arc
00863                               // arc parameters are filled in
00864       double tolerance // tolerance to use when checking linearity
00865       ) const
00866 {
00867   bool rc = false;
00868   if ( 1 == m_segment.Count() && 0 != m_segment[0] )
00869   {
00870     rc = m_segment[0]->IsArc( plane, arc, tolerance )?true:false;
00871   }
00872   return rc;
00873 }
00874 
00875 
00876 static bool GetTestPlane( const ON_Curve& curve, ON_Plane& plane )
00877 {
00878   int i, j;
00879   ON_3dPoint P, Q, R;
00880   ON_3dVector X;
00881   ON_Interval d = curve.Domain();
00882   if ( !curve.Ev1Der( d[0], P, X ) )
00883   {
00884     return false;
00885   }
00886   if ( !X.Unitize() )
00887   {
00888     return false;
00889   }
00890 
00891   Q = P+X;
00892   for ( i = 2; i <= 16; i += 2 )
00893   {
00894     for ( j = 1; j < i; j += 2 )
00895     {
00896       R = curve.PointAt( d.ParameterAt( ((double)j)/((double)i) ) );
00897       if ( plane.CreateFromFrame( P, X, R-P ) )
00898         return true;
00899     }
00900   }
00901   return false;
00902 }
00903 
00904 
00905 ON_BOOL32
00906 ON_PolyCurve::IsPlanar(
00907       ON_Plane* plane, // if not NULL and true is returned, then plane parameters
00908                          // are filled in
00909       double tolerance // tolerance to use when checking linearity
00910       ) const
00911 {
00912   if ( Dimension() == 2 )
00913   {
00914     return ON_Curve::IsPlanar(plane,tolerance);
00915   }
00916 
00917   ON_BOOL32 rc = false;
00918   ON_Plane test_plane;
00919   const int count = Count();
00920   const ON_Curve* crv = FirstSegmentCurve();
00921   if ( count == 1 && crv )
00922     rc = crv->IsPlanar( plane, tolerance );
00923   else if ( count > 1 )
00924   {
00925     if ( IsLinear(tolerance) )
00926     {
00927       if ( plane )
00928       {
00929         ON_Line line(PointAtStart(), PointAtEnd() );
00930         if ( !line.InPlane( *plane, tolerance ) )
00931           line.InPlane( *plane, 0.0 );
00932       }
00933       return true;
00934     }
00935     if ( !GetTestPlane( *this, test_plane ) )
00936     {
00937       // 5 May 2006 Dale Lear
00938       //   Added additiional attempts to get a test_plane
00939       //   for poorly parameterized polycurves. (RR 20057 fix).
00940       ON_3dPoint P, Q;
00941       ON_3dVector X;
00942       if (!Ev1Der(m_t[0],P,X))
00943         return false;
00944 
00945       if ( !X.Unitize() )
00946       {
00947         X = PointAt(Domain().ParameterAt(0.5)) - P;
00948         if ( !X.Unitize() )
00949           return false;
00950       }
00951 
00952       int i;
00953       for ( i = 1; i < count; i++ )
00954       {
00955         if ( m_segment[i] )
00956         {
00957           Q = m_segment[i]->PointAt(m_segment[i]->Domain().ParameterAt(0.5));
00958           if ( test_plane.CreateFromFrame(P,X,Q-P) )
00959             break;
00960         }
00961       }
00962       if ( i >= count )
00963         return false;
00964     }
00965     rc = IsInPlane( test_plane, tolerance );
00966     if (rc && plane)
00967       *plane = test_plane;
00968   }
00969   return rc;
00970 }
00971 
00972 ON_BOOL32
00973 ON_PolyCurve::IsInPlane(
00974       const ON_Plane& plane, // plane to test
00975       double tolerance // tolerance to use when checking linearity
00976       ) const
00977 {
00978   ON_BOOL32 rc = false;
00979   int i, count = Count();
00980   for ( i = 0; i < count; i++ )
00981   {
00982     if ( !m_segment[i] )
00983       return false;
00984     rc = m_segment[i]->IsInPlane( plane, tolerance );
00985     if ( !rc )
00986       break;
00987   }
00988   return rc;
00989 }
00990 
00991 ON_BOOL32 
00992 ON_PolyCurve::IsClosed() const
00993 {
00994   ON_BOOL32 bIsClosed = false;
00995   const int count = Count();
00996   if ( count == 1 ) {
00997     // evaluation test required
00998     const ON_Curve* c = FirstSegmentCurve();
00999     if ( c )
01000       bIsClosed = c->IsClosed();
01001   }
01002   else if ( count > 1 ) 
01003   {
01004     // 17 May2005 Dale Lear 
01005     //  I added the FindNextGap(0) <= 0 test to
01006     //  prevent discontinuous curves from being
01007     //  classified as closed.
01008     bIsClosed = ( ON_Curve::IsClosed() && FindNextGap(0) <= 0 );
01009   }
01010   return bIsClosed;
01011 }
01012 
01013 static bool GetLineIsoCoordinates( const ON_Line& line, const ON_3dPoint P, ON_3dPoint& C )
01014 {
01015   C.x = (line.from.x == line.to.x) ? P.x : ON_UNSET_VALUE;
01016   C.y = (line.from.y == line.to.y) ? P.y : ON_UNSET_VALUE;
01017   C.z = (line.from.z == line.to.z) ? P.z : ON_UNSET_VALUE;
01018   return ( ON_3dPoint::UnsetPoint != C );
01019 }
01020 
01021 static void LineLineTieBreaker( const ON_Line& line0, const ON_Line& line1, 
01022                                 ON_3dPoint& Q0, ON_3dPoint& Q1 )
01023 {
01024   double line0_length = line0.Length();
01025   double line1_length = line1.Length();
01026 
01027   ON_3dPoint C0, C1;
01028   bool bHaveIsoCoords0 = GetLineIsoCoordinates(line0,Q0,C0);
01029   bool bHaveIsoCoords1 = GetLineIsoCoordinates(line1,Q1,C1);
01030   if ( bHaveIsoCoords0 || bHaveIsoCoords1 )
01031   {
01032     for ( int i = 0; i < 3; i++ )
01033     {
01034       double c0 = C0[i];
01035       double c1 = C1[i];
01036       if ( ON_UNSET_VALUE == c0 && ON_UNSET_VALUE == c1 )
01037         continue;
01038       double c = ON_UNSET_VALUE;
01039       if ( c0 == c1 )
01040         c = c0;
01041       else if ( ON_UNSET_VALUE == c0 )
01042         c = c1;
01043       else if ( ON_UNSET_VALUE == c1 )
01044         c = c0;
01045       else if ( line0_length > line1_length )
01046         c = c0;
01047       else
01048         c = c1;
01049       if ( ON_UNSET_VALUE != c && ON_IsValid(c) )
01050       {
01051         Q0[i] = c;
01052         Q1[i] = c;
01053       }
01054     }
01055   }
01056 }
01057 
01058 static void SetLineIsoCoords( const ON_Line& line, const ON_3dPoint& P, ON_3dPoint& Q )
01059 {
01060   ON_3dPoint C;
01061   if ( GetLineIsoCoordinates(line,P,C) )
01062   {
01063     if ( ON_UNSET_VALUE != C.x && ON_IsValid(C.x) )
01064       Q.x = P.x;
01065     if ( ON_UNSET_VALUE != C.y && ON_IsValid(C.y) )
01066       Q.y = P.y;
01067     if ( ON_UNSET_VALUE != C.z && ON_IsValid(C.z) )
01068       Q.z = P.z;
01069   }
01070 }
01071 
01072 static ON_NurbsCurve* ChangeArcEnd( const ON_ArcCurve* arc, ON_3dPoint P, ON_3dPoint Q, int end_index )
01073 {
01074   if ( P == Q )
01075     return 0;
01076 
01077   ON_NurbsCurve* nc = arc->NurbsCurve();
01078   if ( 0 == nc || nc->m_cv_count < 3 )
01079     return 0;
01080   
01081   int cv0_dex, cv1_dex;
01082   if ( 1 == end_index )
01083   {
01084     cv0_dex = nc->m_cv_count-1;
01085     cv1_dex = cv0_dex - 1;
01086   }
01087   else
01088   {
01089     cv0_dex = 0;
01090     cv1_dex = cv0_dex + 1;
01091   }
01092 
01093   if ( !nc->SetCV(cv0_dex,Q) )
01094   {
01095     delete nc;
01096     return 0;
01097   }
01098 
01099   ON_4dPoint R;
01100   if ( !nc->GetCV(cv1_dex,R) )
01101   {
01102     delete nc;
01103     return 0;
01104   }
01105 
01106   R.x += (Q.x-P.x)*R.w;
01107   R.y += (Q.y-P.y)*R.w;
01108   R.z += (Q.z-P.z)*R.w;
01109   nc->SetCV(cv1_dex,R);
01110 
01111   return nc;
01112 }
01113 
01114 bool ON_PolyCurve::CloseGap( int gap_index, int ends_to_modify )
01115 {
01116   const int count = m_segment.Count();
01117 
01118   if ( gap_index <= 0 || gap_index >= count )
01119   {
01120     ON_ERROR("Invalid gap_index parameter.");
01121     return 0; // nothing to do
01122   }
01123 
01124   ON_Curve* c0 = m_segment[gap_index-1];
01125   ON_Curve* c1 = m_segment[gap_index];
01126   if ( 0 == c0 || 0 == c1 )
01127   {
01128     ON_ERROR("Null curve segments.");
01129     return false; // invalid polycurve
01130   }
01131 
01132   const ON_3dPoint P0 = c0->PointAtEnd();
01133   const ON_3dPoint P1 = c1->PointAtStart();
01134   if ( P0 == P1 )
01135     return false; // nothing to do
01136 
01137   ON_3dPoint Q0(P0);
01138   ON_3dPoint Q1(P1);
01139 
01140   const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0);
01141   const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1);
01142 
01143   if ( 0 != arc0 && 0 != arc1 )
01144   {
01145     if ( arc1->m_arc.Length() < arc0->m_arc.Length() )
01146       Q1 = P0;
01147     else
01148       Q0 = P1;
01149   }
01150   else if ( 0 != arc0 && 0 == arc1 )
01151   {
01152     Q1 = P0;
01153   }
01154   else if ( 0 != arc1 && 0 == arc0 )
01155   {
01156     Q0 = P1;
01157   }
01158   else
01159   {
01160     ON_Line line0, line1;
01161     double min_line_length = 0.0;
01162     double is_linear_tolerance = 0.0;
01163     bool bLine0 = (0 == arc0)
01164                 ? c0->LastSpanIsLinear(min_line_length,is_linear_tolerance,&line0)
01165                 : false;
01166     bool bLine1 = (0 == arc0)
01167                 ? c1->FirstSpanIsLinear(min_line_length,is_linear_tolerance,&line1)
01168                 : false;
01169     if ( bLine0 && bLine1 )
01170       LineLineTieBreaker(line0,line1,Q0,Q1);
01171     else if ( bLine0 )
01172       SetLineIsoCoords(line0,P0,Q1);
01173     else if ( bLine1 )
01174       SetLineIsoCoords(line1,P1,Q0);
01175   }
01176 
01177   if ( Q0.x != Q1.x )
01178     Q0.x = Q1.x = 0.5*(P0.x + P1.x);
01179   if ( Q0.y != Q1.y )
01180     Q0.y = Q1.y = 0.5*(P0.y + P1.y);
01181   if ( Q0.z != Q1.z )
01182     Q0.z = Q1.z = 0.5*(P0.z + P1.z);
01183 
01184   if ( Q0 != P0 )
01185   {
01186     if ( 0 != arc0 )
01187     {
01188       ON_NurbsCurve* nc0 = ChangeArcEnd( arc0, P0, Q0 , 1 );
01189       if ( nc0 )
01190       {
01191         delete m_segment[gap_index-1];
01192         m_segment[gap_index-1] = nc0;
01193         c0 = nc0;
01194         arc0 = 0;
01195       }
01196     }
01197     else
01198     {
01199       c0->SetEndPoint(Q0);
01200     }
01201   }
01202 
01203   if ( Q1 != P1 )
01204   {
01205     if ( 0 != arc1 )
01206     {
01207       ON_NurbsCurve* nc1 = ChangeArcEnd( arc1, P1, Q1, 0 );
01208       if ( nc1 )
01209       {
01210         delete m_segment[gap_index];
01211         m_segment[gap_index] = nc1;
01212         c0 = nc1;
01213         arc1 = 0;
01214       }
01215     }
01216     else
01217     {
01218       c1->SetStartPoint(Q1);
01219     }
01220   }
01221 
01222   return HasGapAt(gap_index-1) ? false : true;
01223 }
01224 
01225 int ON_PolyCurve::CloseGaps()
01226 {
01227   int rc = 0;
01228   int segment_index0 = 0;
01229   int gap_index = 0;
01230   
01231   for(;;)
01232   {
01233     gap_index = FindNextGap(segment_index0);
01234     if ( gap_index <= segment_index0 || gap_index >= m_segment.Count() )
01235       break;
01236     if ( CloseGap(gap_index,0) )
01237       rc++;
01238     segment_index0 = gap_index;
01239   }
01240 
01241   return rc;
01242 }
01243 
01244 int ON_PolyCurve::HasGap() const
01245 {
01246   return FindNextGap(0);
01247 }
01248 
01249 
01250 bool ON_PolyCurve::HasGapAt(int segment_index) const
01251 {
01252   const int count = m_segment.Count();
01253 
01254   if ( segment_index < 0 || segment_index >= count-1 )
01255     return 0;
01256 
01257   const ON_Curve* c0 = m_segment[segment_index];
01258   const ON_Curve* c1 = m_segment[segment_index+1];
01259   if ( 0 == c0 || 0 == c1 )
01260     return false;
01261 
01262   ON_3dPoint P0 = c0->PointAtEnd();
01263   ON_3dPoint P1 = c1->PointAtStart();
01264   // Note:  The point compare test should be the same
01265   //        as the one used in ON_Curve::IsClosed().
01266   if ( false == ON_PointsAreCoincident( 3, false, &P0.x, &P1.x ) )
01267   {
01268     // To fix RR 13325 I allow a little more leeway for arcs.
01269     const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0);
01270     const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1);
01271     if ( 0 == arc0 && 0 == arc1 )
01272       return true; // gap
01273 
01274     double tol = ON_ZERO_TOLERANCE;
01275     const double tol0 = arc0  ? ( arc0->m_arc.radius*arc0->m_arc.AngleRadians()*1.0e-10 ) : 0.0;
01276     const double tol1 = arc1  ? ( arc1->m_arc.radius*arc1->m_arc.AngleRadians()*1.0e-10 ) : 0.0;
01277     if ( tol < tol0 ) 
01278       tol = tol0;
01279     if ( tol < tol1 ) 
01280       tol = tol1;
01281     const double d = P0.DistanceTo(P1);
01282     if ( d > tol )
01283     {
01284       return true; // gap
01285     }
01286   }
01287 
01288   return false; // no gap
01289 }
01290 
01291 
01292 int ON_PolyCurve::FindNextGap(int segment_index0) const
01293 {
01294   if ( segment_index0 >= 0 )
01295   {
01296     const int count = m_segment.Count();
01297     for (int gap_index = segment_index0+1; gap_index < count; gap_index++ )
01298     {
01299       if ( HasGapAt(gap_index-1) )
01300         return gap_index;
01301     }
01302   }
01303   return 0;
01304 }
01305 
01306 
01307 ON_BOOL32 
01308 ON_PolyCurve::IsPeriodic() const
01309 {
01310   ON_BOOL32 bIsPeriodic = false;
01311   if ( Count() == 1 ) {
01312     const ON_Curve* c = FirstSegmentCurve();
01313     if ( c )
01314       bIsPeriodic = c->IsPeriodic();
01315   }
01316   return bIsPeriodic;
01317 }
01318 
01319 bool ON_PolyCurve::GetNextDiscontinuity( 
01320         ON::continuity c,
01321         double t0,
01322         double t1,
01323         double* t,
01324         int* hint,
01325         int* dtype,
01326         double cos_angle_tolerance,
01327         double curvature_tolerance
01328         ) const
01329 {
01330   ON_3dPoint Pm, Pp;
01331   ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km, Kp;
01332   double s0, s1, s;
01333   bool rc = false;
01334   ON_Interval sdom, cdom;
01335   const int count = Count();
01336   int segment_hint=0, curve_hint=0;
01337   if ( dtype )
01338     *dtype = 0;
01339   if ( count > 0 && t0 != t1 ) 
01340   {
01341     // 20 March 2003 Dale Lear:
01342     //     look for parametric discontinuities on the interior.
01343     //     If we don't find any, then well check for locus 
01344     //     discontinuities at the appropriate end
01345     ON::continuity input_c = c;
01346     c = ON::ParametricContinuity(c);
01347 
01348     segment_hint = (hint) ? (*hint & 0x3FFF) : 0;
01349     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,(t0>t1)?-1:1,segment_hint);
01350     curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0;
01351 
01352 
01353     {
01354       // 20 March 2003 Dale Lear:
01355       //     If t0 is very near interior m_t[] value, see if it
01356       //     should be set to that value.  A bit or two of 
01357       //     precision sometimes gets lost in proxy
01358       //     domain to real curve domain conversions on the interior
01359       //     of a curve domain.
01360       double segtol = (fabs(m_t[segment_index]) + fabs(m_t[segment_index+1]) + fabs(m_t[segment_index+1]-m_t[segment_index]))*ON_SQRT_EPSILON;
01361       if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol )
01362       {
01363         if ( t0 < t1 )
01364         {
01365           if ( t0 < m_t[segment_index+1] && t1 > m_t[segment_index+1] && fabs(t0-m_t[segment_index+1]) <= segtol && segment_index+1 < count )
01366           {
01367             t0 = m_t[segment_index+1];
01368             segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,1,segment_hint);
01369           }
01370         }
01371         else
01372         {
01373           if ( t0 > m_t[segment_index] && t1 < m_t[segment_index] && fabs(t0-m_t[segment_index]) <= segtol && segment_index > 0 )
01374           {
01375             t0 = m_t[segment_index];
01376           }
01377         }
01378       }
01379     }
01380 
01381     double tmin, tmax;
01382     int segment_index_delta;
01383     if (t0 > t1)
01384     {
01385       segment_index_delta = -1;
01386       tmin = t1;
01387       tmax = t0;
01388     }
01389     else
01390     {
01391       segment_index_delta = 1;
01392       tmin = t0;
01393       tmax = t1;
01394     }
01395 
01396     const ON_Curve* crv;
01397     for ( /*empty*/; 
01398               segment_index >= 0 
01399            && segment_index < count
01400            && tmin < m_t[segment_index+1] && m_t[segment_index] < tmax; 
01401           segment_index += segment_index_delta )
01402     {
01403       crv = m_segment[segment_index];
01404       if ( !crv )
01405         break;
01406       cdom = crv->Domain();
01407       sdom.Set( m_t[segment_index], m_t[segment_index+1] );
01408       if ( sdom == cdom )
01409       {
01410         s0 = t0;
01411         s1 = t1;
01412       }
01413       else
01414       {
01415         s0 = cdom.ParameterAt( sdom.NormalizedParameterAt(t0) );
01416         s1 = cdom.ParameterAt( sdom.NormalizedParameterAt(t1) );
01417       }
01418       rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance );
01419       if ( rc )
01420       {
01421         double kink_t;
01422         if ( sdom == cdom )
01423         {
01424           kink_t = s;
01425         }
01426         else
01427         {
01428           kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) );
01429           double t_tol = (fabs(t0)+fabs(t1)+fabs(t0-t1))*ON_ZERO_TOLERANCE;
01430           if ( kink_t <= tmin+t_tol || kink_t >= tmax-t_tol)
01431           {
01432             // 24 January 2002 Dale Lear -
01433             // It is possible that lost precision in the 
01434             // domain conversion is giving us trouble.
01435             // In particular, if this code is not here,
01436             // "t0" is right at a kink, and s0 gets bumped
01437             // down a little bit due to rounding/truncation,
01438             // we end up finding the kink at "t0" that we were
01439             // supposed to skip.
01440             double e = fabs(sdom.Length()/cdom.Length());
01441             if ( e < 1.0 ) e = 1.0; else if (e > 1000.0) e = 1000.0;
01442             double s_tol = (fabs(s0)+fabs(s1)+fabs(s0-s1))*ON_ZERO_TOLERANCE*e;
01443             if ( kink_t <= tmin+t_tol )
01444             {
01445               if( s0>s1 )
01446                 s1 = s1 + s_tol;
01447               else
01448                 s0 = s0 + s_tol;
01449             }
01450             if ( kink_t >= tmax-t_tol )
01451             {
01452               if ( s0>s1 )
01453                 s0 = s0 - s_tol;
01454               else
01455                 s1 = s1 - s_tol;
01456             }
01457             rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance );
01458             if (rc)
01459             {
01460               kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) );
01461               if ( kink_t <= tmin || kink_t >= tmax )
01462                 rc = false;
01463             }
01464           }
01465         }
01466 
01467         if (rc)
01468         {
01469           if ( t )
01470           {
01471             *t = kink_t;
01472             if ( hint )
01473             {
01474               *hint = segment_index | (curve_hint<<14);
01475             }
01476           }
01477           break;
01478         }
01479       }
01480 
01481 
01482       // check for discontinity between curve segments
01483       int next_segment_index = segment_index+segment_index_delta;
01484       if ( next_segment_index < 0 || next_segment_index >= count )
01485       {
01486         // no more curve segments in search interval
01487         break;
01488       }
01489       const ON_Curve* crv1 = m_segment[next_segment_index];
01490       if ( !crv1 )
01491         break;
01492 
01493       if ( t0 > t1 )
01494       {
01495         if ( sdom[0] <= t1 ) // this line is correct - search is decreasing towards t1
01496         {
01497           // INTERIOR of search interval does not include 
01498           // start this crv = end of next curve
01499           break;
01500         }
01501       }
01502       else
01503       {
01504         if ( t1 <= sdom[1] )
01505         {
01506           // INTERIOR of search interval does not include 
01507           // end of this crv = start of next curve
01508           break;
01509         }
01510       }
01511 
01512       double crv0_t, crv1_t;
01513       int crv0_side;
01514       if ( t0 > t1 )
01515       {
01516         // compare start if this curve against end of next curve
01517         crv0_t = cdom[0];
01518         crv1_t = crv1->Domain()[1];
01519         crv0_side = 1;
01520       }
01521       else
01522       {
01523         // compare end if this curve against start of next curve
01524         crv0_t = cdom[1];
01525         crv1_t = crv1->Domain()[0];
01526         crv0_side = -1;
01527       }
01528 
01529       switch( c )
01530       {
01531       case ON::C1_continuous:
01532       case ON::G1_continuous:
01533         crv->Ev1Der( crv0_t, Pm, D1m, crv0_side );   // point on this curve
01534         crv1->Ev1Der( crv1_t, Pp, D1p, -crv0_side ); // corresponding point on next curve
01535         if ( c == ON::C1_continuous )
01536         {
01537           if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) )
01538             rc = true;
01539         }
01540         else
01541         {
01542           Tm = D1m;
01543           Tp = D1p;
01544           Tm.Unitize();
01545           Tp.Unitize();
01546           if ( Tm*Tp < cos_angle_tolerance )
01547             rc = true;
01548         }
01549         if ( rc && dtype )
01550           *dtype = 1;
01551         break;
01552 
01553       case ON::C2_continuous:
01554       case ON::G2_continuous:
01555       case ON::Gsmooth_continuous:
01556         crv->Ev2Der( crv0_t, Pm, D1m, D2m, crv0_side );   // point on this curve
01557         crv1->Ev2Der( crv1_t, Pp, D1p, D2p, -crv0_side ); // corresponding point on next curve
01558         if ( c == ON::C2_continuous )
01559         {
01560           if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) ) 
01561           {
01562             rc = true;
01563             if ( dtype )
01564               *dtype = 1;
01565           }
01566           else if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) )
01567           {
01568             rc = true;
01569             if ( dtype )
01570               *dtype = 2;
01571           }
01572         }
01573         else
01574         {
01575           ON_EvCurvature( D1m, D2m, Tm, Km );
01576           ON_EvCurvature( D1p, D2p, Tp, Kp );
01577           if ( Tm*Tp < cos_angle_tolerance )
01578           {
01579             rc = true;
01580             if ( dtype )
01581               *dtype = 1;
01582           }
01583           else 
01584           {
01585             bool bIsCurvatureContinuous = ( ON::Gsmooth_continuous == c )
01586               ? ON_IsGsmoothCurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance)
01587               : ON_IsG2CurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance);
01588             if ( !bIsCurvatureContinuous )
01589             {
01590               // NOTE:
01591               //   The test to enter this scope must exactly match
01592               //   the one used in ON_NurbsCurve::GetNextDiscontinuity().
01593               rc = true;
01594               if ( dtype )
01595                 *dtype = 2;
01596             }
01597             else if ( ON::Gsmooth_continuous == c )
01598             {
01599               const double is_linear_tolerance = 1.0e-8;  
01600               const double is_linear_min_length = 1.0e-8;
01601               const ON_Curve* seg0;
01602               const ON_Curve* seg1;
01603               if (crv0_side<0)
01604               {
01605                 seg0 = crv;
01606                 seg1 = crv1;
01607               }
01608               else
01609               {
01610                 seg0 = crv1;
01611                 seg1 = crv;
01612               }
01613               bool b0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance);
01614               bool b1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance);
01615               if ( b0 != b1 )
01616               {
01617                 rc = true;
01618                 if ( dtype )
01619                   *dtype = 3;
01620               }
01621             }
01622           }
01623         }
01624         break;
01625       default:
01626         // intentionally ignoring other ON::continuity enum values
01627         break;
01628       }
01629       if (rc)
01630       {
01631         int tindex = (t0>t1)?segment_index:(segment_index+1); 
01632         if ( t )
01633           *t = m_t[tindex];
01634         if ( hint )
01635         {
01636           *hint = tindex;
01637         }
01638         break;
01639       }
01640     }
01641 
01642     if ( !rc && input_c != c )
01643     {
01644       // 20 March 2003 Dale Lear
01645       //   See if we need to do a locus check at an end
01646       rc = ON_Curve::GetNextDiscontinuity( input_c, 
01647                         t0, t1, t, NULL, 
01648                         dtype, 
01649                         cos_angle_tolerance, curvature_tolerance );
01650     }
01651   }
01652   return rc;
01653 }
01654 
01655 bool ON_PolyCurve::IsContinuous(
01656     ON::continuity desired_continuity,
01657     double t, 
01658     int* hint, // default = NULL,
01659     double point_tolerance, // default=ON_ZERO_TOLERANCE
01660     double d1_tolerance, // default==ON_ZERO_TOLERANCE
01661     double d2_tolerance, // default==ON_ZERO_TOLERANCE
01662     double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE
01663     double curvature_tolerance  // default==ON_SQRT_EPSILON
01664     ) const
01665 {
01666   bool rc = true;
01667   const int count = Count();
01668   if ( count > 0 ) 
01669   {
01670     if ( t <= m_t[0] || t >= m_t[count] )
01671     {
01672       // 20 March 2003 Dale Lear
01673       //     Consistently handles locus case and out of domain case.
01674       rc = ON_Curve::IsContinuous( 
01675                  desired_continuity, t, hint, 
01676                  point_tolerance, 
01677                  d1_tolerance, d2_tolerance, 
01678                  cos_angle_tolerance, 
01679                  curvature_tolerance );
01680       return rc;
01681     }
01682 
01683     // "locus" and "parametric" are the same at this point.
01684     desired_continuity = ON::ParametricContinuity(desired_continuity);
01685 
01686 
01687     int segment_hint = 0, curve_hint = 0;
01688     if ( hint )
01689       segment_hint = (*hint & 0x3FFF);
01690     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint);
01691 
01692     {
01693       // 20 March 2003 Dale Lear:
01694       //     If t is very near interior m_t[] value, see if it
01695       //     should be set to that value.  A bit or two of 
01696       //     precision sometimes gets lost in proxy
01697       //     domain to real curve domain conversions on the interior
01698       //     of a curve domain.
01699       double segtol = (fabs(m_t[segment_index]) + fabs(m_t[segment_index+1]) + fabs(m_t[segment_index+1]-m_t[segment_index]))*ON_SQRT_EPSILON;
01700       if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol )
01701       {
01702         if ( fabs(t-m_t[segment_index]) <= segtol && segment_index > 0 )
01703         {
01704           t = m_t[segment_index];
01705         }
01706         else if ( fabs(t-m_t[segment_index+1]) <= segtol && segment_index+1 < count )
01707         {
01708           t = m_t[segment_index+1];
01709           segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint);
01710         }
01711       }
01712     }
01713 
01714     if ( hint )
01715     {
01716       if ( segment_hint == segment_index )
01717         curve_hint = ((*hint)>>14);
01718       else
01719       {
01720         segment_hint = segment_index;
01721         *hint = segment_hint;
01722       }
01723     }
01724 
01725     if ( m_t[segment_index] < t && t < m_t[segment_index+1] )
01726     {
01727       // test interior of this segment
01728       const ON_Curve* segment_curve = SegmentCurve(segment_index);
01729       if ( segment_curve )
01730       {
01731         ON_Interval sdom, cdom;
01732         cdom = segment_curve->Domain();
01733         sdom.Set( m_t[segment_index], m_t[segment_index+1] );
01734         if ( sdom != cdom )
01735           t = cdom.ParameterAt( sdom.NormalizedParameterAt(t) );
01736         rc = segment_curve->IsContinuous( desired_continuity, t, &curve_hint, 
01737                                           point_tolerance, d1_tolerance, d2_tolerance, 
01738                                           cos_angle_tolerance, curvature_tolerance );
01739         if ( hint )
01740           *hint = (segment_index | (curve_hint<<14));
01741       }
01742     }
01743     else if ( count > 0 )
01744     {
01745       if ( segment_index == 0 && t == m_t[0] )
01746         rc = true; // t at start of domain
01747       else if ( segment_index == count-1 && t == m_t[count] )
01748         rc = true; // t and end of domain
01749       else
01750       {
01751         // evaluate ends of segments
01752         rc = ON_Curve::IsContinuous( desired_continuity, t, hint, 
01753                            point_tolerance, d1_tolerance, d2_tolerance, 
01754                            cos_angle_tolerance, curvature_tolerance );
01755         if ( 0 != rc 
01756              && ON::Gsmooth_continuous == desired_continuity 
01757              && segment_index >= 0
01758              && segment_index < count
01759            )
01760         {
01761           // check for linear to non-linear transition
01762           const int i0 = ( t >= m_t[segment_index] ) ? segment_index-1 : segment_index;
01763           if ( i0 >= 0 && t == m_t[i0+1] )
01764           {
01765             const ON_Curve* seg0 = SegmentCurve(i0);
01766             const ON_Curve* seg1 = SegmentCurve(i0+1);
01767             if ( 0 != seg0 && 0 != seg1 )
01768             {
01769               const double is_linear_tolerance = 1.0e-8;  
01770               const double is_linear_min_length = 1.0e-8;
01771               bool b0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance);
01772               bool b1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance);
01773               if ( b0 != b1 )
01774                 rc = false;
01775             }
01776           }
01777         }
01778       }
01779     }
01780   }
01781   return rc;
01782 }
01783 
01784 ON_BOOL32
01785 ON_PolyCurve::Reverse()
01786 {
01787   const int count = Count();
01788   int i;
01789   ON_BOOL32 rc = (count>0) ? true : false;
01790   if ( rc ) {
01791     m_segment.Reverse();
01792     m_t.Reverse();
01793     for ( i = 0; i < count; i++ ) {
01794       m_segment[i]->Reverse();
01795       m_t[i] = -m_t[i];
01796     }
01797     m_t[count] = -m_t[count];
01798   }
01799         DestroyCurveTree();
01800   return rc;
01801 }
01802 
01803 bool ON_TuneupEvaluationParameter( 
01804    int side,
01805    double s0, double s1, // segment domain
01806    double *s             // segment parameter
01807    )
01808 {
01809   double t = *s;
01810   if ( 0 != side && s0 < t && t < s1 )
01811   {
01812     // 9 November 2010 Dale Lear
01813     //   I wrote this function today and chose
01814     //   1.0e-10 as the "noise" factor.  1.0e-10
01815     //   may need to be adjusted but it should
01816     //   not be larger unless there is a very
01817     //   good reason.  You must document any changes
01818     //   and include a bug track number so subsequent
01819     //   changes can be tested.  Any value used to
01820     //   replace 1.0e-10 must be strictly smaller
01821     //   than ON_SQRT_EPSILON because some solvers
01822     //   use (s1-s0)*ON_SQRT_EPSILON as a minimum step
01823     //   size.
01824     double ds = (s1-s0)*1.0e-10;
01825     if ( side < 0 )
01826     {
01827       if ( t <= s0+ds )
01828       {
01829         *s = s0;
01830         return true;
01831       }
01832     }
01833     else // side > 0
01834     {
01835       if ( t >= s1-ds )
01836       {
01837         *s = s1;
01838         return true;
01839       }
01840     }
01841   }
01842   return false;
01843 }
01844 
01845 
01846 ON_BOOL32 ON_PolyCurve::Evaluate( // returns false if unable to evaluate
01847        double t,       // evaluation parameter
01848        int der_count,  // number of derivatives (>=0)
01849        int v_stride,   // v[] array stride (>=Dimension())
01850        double* v,      // v[] array of length stride*(ndir+1)
01851        int side,       // optional - determines which side to evaluate from
01852                        //         0 = default
01853                        //      <  0 to evaluate from below, 
01854                        //      >  0 to evaluate from above
01855        int* hint       // optional - evaluation hint (int) used to speed
01856                        //            repeated evaluations
01857        ) const
01858 {
01859   ON_BOOL32 rc = false;
01860   const int count = Count();
01861   const int dim = Dimension();
01862   int segment_hint, curve_hint;
01863   if ( count > 0 && dim > 0 && dim <= v_stride ) 
01864   {
01865     segment_hint = (hint) ? (*hint & 0x3FFF) : 0;
01866     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_hint);
01867     if ( -2 == side || 2 == side )
01868     {
01869       // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix
01870       //   When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization
01871       //   and the original side parameter was -1 or +1, it is changed to -2 or +2
01872       //   to indicate that if t is numerically closed to an end paramter, then
01873       //   it should be tuned up to be at the end paramter.
01874       double a = t;
01875       if ( ON_TuneupEvaluationParameter( side, m_t[segment_index], m_t[segment_index+1], &a) )
01876       {
01877         // recalculate segment index
01878         t = a;
01879         segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_index);
01880       }
01881     }
01882     const ON_Curve* c = m_segment[segment_index];
01883     if ( c ) {
01884       double s0, s1;
01885       {
01886         ON_Interval dom = c->Domain();
01887         s0 = dom.Min();
01888         s1 = dom.Max();
01889       }
01890       if ( s0 != s1 ) 
01891       {
01892         const double t0 = m_t[segment_index];
01893         const double t1 = m_t[segment_index+1];
01894         double s;
01895         if ( s0 == t0 && s1 == t1 )
01896         {
01897           // segment domain = c->Domain()
01898           s = t;
01899         }
01900         else 
01901         {
01902           // adjust segment domain parameter
01903           if ( fabs(t1 - t0) < (ON_ZERO_TOLERANCE + ON_EPSILON*fabs(t0)) )
01904           {
01905             // segment domain is insanely short
01906             s = (fabs(t-t0) < fabs(t-t1)) ? s0 : s1;
01907           }
01908           else 
01909           {
01910             // 30 May 2012 Dale Lear bug # 105974
01911             //   The arithmetic below was setting b = 0 and a = 0.9999999999999...
01912             //   so I added the checking for 0 and 1 stuff.
01913             const double d = t1-t0;
01914             double a = (t - t0)/d;
01915             double b = (t1 - t)/d;
01916             if ( 0.0 == b )
01917               a = 1.0;
01918             else if ( 1.0 == b )
01919               a = 0.0;
01920             else if ( 0.0 == a )
01921               b = 1.0;
01922             else if ( 1.0 == a )
01923               b = 0.0;
01924             s = b*s0 + a*s1;
01925           }
01926           if ( -1 == side )
01927             side = -2;
01928           else if ( 1 == side )
01929             side = 2;
01930         }
01931         curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0;
01932         rc = c->Evaluate(
01933            s, 
01934            der_count, 
01935            v_stride, v, 
01936            side, 
01937            &curve_hint );
01938 
01939         if ( rc ) 
01940         {
01941           if ( der_count > 0 && s1 - s0 != t1 - t0 && t0 != t1 )
01942           {
01943             // Adjust segment derivative evaluation bug by applying chain rule
01944             // to get polycurve derivative value.
01945             const double d = (s1-s0)/(t1-t0);
01946             s = d;
01947             int di, vi;
01948             v += v_stride;
01949             for ( di = 1; di <= der_count; di++ )
01950             {
01951               for ( vi = 0; vi < dim; vi++ ) 
01952               {
01953                 v[vi] = s*v[vi];
01954               }
01955               s *= d;
01956               v += v_stride;
01957             }
01958           }
01959 
01960           if ( hint )
01961             *hint = segment_index | (curve_hint<<14);
01962         }
01963 
01964       }
01965     }
01966   }
01967   return rc;
01968 }
01969 
01970 int
01971 ON_PolyCurve::Count() const
01972 {
01973   return m_segment.Count();
01974 }
01975 
01976 
01977 ON_Curve*
01978 ON_PolyCurve::operator[](int segment_index) const
01979 {
01980   return SegmentCurve(segment_index);
01981 }
01982 
01983 ON_Curve*
01984 ON_PolyCurve::SegmentCurve(int segment_index) const
01985 {
01986   return ( segment_index >= 0 && segment_index < Count() ) 
01987          ? m_segment[segment_index] 
01988          : NULL;
01989 }
01990 
01991 
01992 double ON_PolyCurve::SegmentCurveParameter(
01993   double polycurve_parameter
01994   ) const
01995 {
01996   int segment_index = SegmentIndex( polycurve_parameter );
01997   const ON_Curve* segment_curve = SegmentCurve(segment_index);
01998   if ( !segment_curve )
01999     return ON_UNSET_VALUE;
02000   ON_Interval cdom = segment_curve->Domain();
02001   ON_Interval sdom = SegmentDomain(segment_index);
02002   if ( cdom == sdom )
02003     return polycurve_parameter;
02004   double s = sdom.NormalizedParameterAt(polycurve_parameter);
02005   return cdom.ParameterAt(s);
02006 }
02007 
02008 
02009 double ON_PolyCurve::PolyCurveParameter(
02010   int segment_index,
02011   double segmentcurve_parameter
02012   ) const
02013 {
02014   const ON_Curve* segment_curve = SegmentCurve(segment_index);
02015   if ( !segment_curve )
02016     return ON_UNSET_VALUE;
02017   ON_Interval cdom = segment_curve->Domain();
02018   ON_Interval sdom = SegmentDomain(segment_index);
02019   if ( cdom == sdom )
02020     return segmentcurve_parameter;
02021   double s = cdom.NormalizedParameterAt(segmentcurve_parameter);
02022   return sdom.ParameterAt(s);
02023 }
02024 
02025 
02026 ON_Interval 
02027 ON_PolyCurve::SegmentDomain( int segment_index ) const
02028 {
02029   ON_Interval domain;
02030   if ( segment_index >= 0 && segment_index < Count() ) {
02031     domain.m_t[0] = m_t[segment_index];
02032     domain.m_t[1] = m_t[segment_index+1];
02033   }
02034   return domain;
02035 }
02036 
02037 
02038 ON_Curve*
02039 ON_PolyCurve::FirstSegmentCurve() const
02040 {
02041   return SegmentCurve(0);
02042 }
02043 
02044 ON_Curve*
02045 ON_PolyCurve::LastSegmentCurve() const
02046 {
02047   return SegmentCurve(Count()-1);
02048 }
02049 
02050 void
02051 ON_PolyCurve::Reserve( int capacity )
02052 {
02053   m_segment.Reserve(capacity);
02054   m_t.Reserve(capacity+1);
02055 }
02056 
02057 ON_BOOL32 ON_PolyCurve::Prepend( ON_Curve* c )
02058 {
02059         DestroyCurveTree();
02060   return Insert( 0, c );
02061 }
02062 
02063 ON_BOOL32 ON_PolyCurve::Append( ON_Curve* c )
02064 {
02065         DestroyCurveTree();
02066   return Insert( Count(), c );
02067 }
02068 
02069 ON_BOOL32 ON_PolyCurve::PrependAndMatch(ON_Curve* c)
02070 
02071 {
02072   if (Count() == 0) return Prepend(c);
02073   //if (IsClosed() || c->IsClosed()) return false;
02074   if (!c->SetEndPoint(PointAtStart())){
02075     if (!SetStartPoint(c->PointAtEnd()))
02076       return false;
02077   }
02078   return Prepend(c);
02079 }
02080 
02081 ON_BOOL32 ON_PolyCurve::AppendAndMatch(ON_Curve* c)
02082 
02083 {
02084   if (Count() == 0) return Append(c);
02085   //if (IsClosed() || c->IsClosed()) return false;
02086   if (!c->SetStartPoint(PointAtEnd())){
02087     if (!SetEndPoint(c->PointAtStart()))
02088       return false;
02089   }
02090   return Append(c);
02091 }
02092 
02093 
02094 ON_BOOL32 ON_PolyCurve::Remove( )
02095 {
02096   return Remove(Count()-1);
02097 }
02098 
02099 ON_BOOL32 ON_PolyCurve::Remove( int segment_index )
02100 {
02101   ON_BOOL32 rc = false;
02102   const int segment_count = Count();
02103   if ( segment_index >= 0 && segment_index < segment_count ) {
02104     delete m_segment[segment_index];
02105     m_segment[segment_index] = 0;
02106     m_segment.Remove(segment_index);
02107                 // GBA 18 September 2003. m_t array not properly updated when last segment removed.
02108     if ( segment_index >= 1 ) {
02109       double* d = m_t.Array();
02110       const double delta = d[segment_index] - d[segment_index+1];
02111       int i;
02112       for (i=segment_index+1; i <= segment_count; i++ ) {
02113         d[i] += delta;
02114       }
02115     }
02116                 // GBA 12/02/02.  When removing the last segment remove both m_t values so
02117                 // the polycurve will have the same state as ON_PolyCurve().
02118                 if( segment_count==1)
02119                         m_t.Empty();
02120                 else
02121             m_t.Remove(segment_index);
02122     rc = true;
02123   }
02124   return rc;
02125 }
02126 
02127 ON_BOOL32 ON_PolyCurve::Insert( int segment_index, ON_Curve* c )
02128 {
02129   double s0, s1;
02130   ON_BOOL32 rc = false;
02131   const int count = Count();
02132   if ( segment_index >= 0 && segment_index <= count && c && c != this && c->GetDomain(&s0,&s1) ) 
02133   {
02134     rc = true;
02135     m_segment.Insert( segment_index, c );
02136 
02137     // determine polycurve parameters for this segment
02138     double t0, t1;
02139     if ( segment_index == count ) {
02140       // append segment
02141       if ( count == 0 ) {
02142         m_t.Append(s0);
02143         m_t.Append(s1);
02144       }
02145       else {
02146         t0 = m_t[count];
02147         t1 = ( s0 == t0 ) ? s1 : (s1-s0+t0);
02148         m_t.Append(t1);
02149       }
02150     }
02151     else if ( segment_index == 0 ) {
02152       // prepend segment
02153       t1 = m_t[0];
02154       t0 = ( s1 == t1 ) ? s0 : (s0-s1+t1);
02155       m_t.Insert(0,t0);
02156     }
02157     else {
02158       // insert segment
02159       t0 = m_t[segment_index];
02160       t1 = ( s0 == t0 ) ? s1 : (s1-s0+t0);
02161       const double dt = t1-t0;
02162       m_t.Insert(segment_index+1,t1);
02163       double* t = m_t.Array();
02164       for ( int i = segment_index+2; i <= count+1; i++ ) {
02165         t[i] += dt;
02166       }      
02167     }
02168   }
02169   return rc;
02170 }
02171 
02172 ON_BOOL32 ON_PolyCurve::SetStartPoint(ON_3dPoint start_point)
02173 {
02174   ON_BOOL32 rc = false;
02175   // just do it // if ( !IsClosed() )
02176   {
02177     ON_Curve* c = FirstSegmentCurve();
02178     if ( c )
02179       rc = c->SetStartPoint(start_point);
02180   }
02181         DestroyCurveTree();
02182   return rc;
02183 }
02184 
02185 ON_BOOL32 ON_PolyCurve::SetEndPoint(ON_3dPoint end_point)
02186 {
02187   ON_BOOL32 rc = false;
02188   // just do it // if ( !IsClosed() )
02189   {
02190     ON_Curve* c = LastSegmentCurve();
02191     if ( c )
02192       rc = c->SetEndPoint(end_point);
02193   }
02194         DestroyCurveTree();
02195   return rc;
02196 }
02197 
02198 int ON_PolyCurve::GetNurbForm( 
02199                               ON_NurbsCurve& nurb, 
02200                               double tol,
02201                               const ON_Interval* subdomain  // OPTIONAL subdomain of ON::ProxyCurve::Domain()
02202                               ) const
02203 {
02204   ON_Interval domain = Domain();
02205   if ( !domain.IsIncreasing() )
02206     return false;
02207   int rc = 0;
02208   int si0 = 0;
02209   int si1 = Count();
02210   if ( subdomain ) {
02211     if ( !subdomain->IsIncreasing() )
02212       return 0;
02213     if ( !domain.Includes(subdomain->Min()) )
02214       return 0;
02215     if ( !domain.Includes(subdomain->Max()) )
02216       return 0;
02217     domain = *subdomain;
02218   }
02219 
02220   while ( si0 < si1 && m_t[si0+1] <= domain.m_t[0] )
02221     si0++;
02222   while ( si0 < si1 && m_t[si1-1] >= domain.m_t[1] )
02223     si1--;
02224   if ( si0 >= si1 )
02225     return 0;
02226   {
02227     ON_NurbsCurve c;
02228     int i, rci;
02229     for ( i = si0; i < si1; i++ ) {
02230       if ( !m_segment[i] )
02231         return 0;
02232       if ( i == si0 ) {
02233         rc = m_segment[i]->GetNurbForm( nurb, tol, NULL );
02234         if ( rc < 1 )
02235           return rc;
02236         nurb.SetDomain( m_t[i], m_t[i+1] );
02237       }
02238       else {
02239         rci = m_segment[i]->GetNurbForm( c, tol, NULL );
02240         if ( rci < 1 )
02241           return rci;
02242         else if ( rc < rci )
02243           rc = rci;
02244         c.SetDomain( m_t[i], m_t[i+1] );
02245         if ( !nurb.Append( c ) )
02246           return 0;
02247         c.Destroy();
02248       }
02249     }
02250   }
02251 
02252   if ( subdomain )
02253     nurb.Trim( *subdomain );
02254 
02255   return rc;
02256 }
02257 
02258 int ON_PolyCurve::HasNurbForm() const
02259 
02260 {
02261   int count = m_segment.Count();
02262   if (!count)
02263     return 0;
02264   int i;
02265   int rc = 1;
02266   for (i=0; i<count; i++){
02267     const ON_Curve* scrv = SegmentCurve(i);
02268     if (!scrv)
02269       return 0;
02270     int nf = scrv->HasNurbForm();
02271     if (nf == 0)
02272       return 0;
02273     if (nf == 2)
02274       rc = 2;
02275   }
02276   return rc;
02277 }
02278 
02279 
02280 int ON_PolyCurve::SegmentIndex( double curve_t ) const
02281 {
02282   int count = m_segment.Count();
02283   int seg_index = ON_SearchMonotoneArray( m_t.Array(), m_t.Count(), curve_t );
02284   if ( seg_index < 0 )
02285     seg_index = 0;
02286   else if ( seg_index >= count )
02287     seg_index = count-1;
02288   return seg_index;
02289 }
02290 
02291 int ON_PolyCurve::SegmentIndex(
02292   ON_Interval sub_domain,
02293   int* segment_index0,
02294   int* segment_index1
02295   ) const
02296 {
02297   const int segment_count = m_segment.Count();
02298   int s0 = 0, s1 = 0;
02299   ON_Interval seg_dom;
02300   sub_domain.Intersection( Domain() );
02301   if ( sub_domain.IsIncreasing() )
02302   {
02303     s0 = SegmentIndex(sub_domain.Min());
02304     for ( s1 = s0+1; s1 < segment_count; s1++ )
02305     {
02306       seg_dom = SegmentDomain(s1);
02307       if ( seg_dom[0] >= sub_domain.Max() )
02308         break;
02309     }
02310   }
02311   if ( segment_index0 )
02312     *segment_index0 = s0;
02313   if ( segment_index1 )
02314     *segment_index1 = s1;
02315   return s1-s0;
02316 }
02317 
02318 
02319 ON_BOOL32 ON_PolyCurve::GetCurveParameterFromNurbFormParameter(
02320       double nurbs_t,
02321       double* curve_t
02322       ) const
02323 {
02324   ON_BOOL32 rc = false;
02325   int i = SegmentIndex( nurbs_t );
02326   const ON_Curve* curve = SegmentCurve(i);
02327   if ( curve ) {
02328     ON_Interval in( m_t[i], m_t[i+1] );
02329     ON_Interval cdom = curve->Domain();
02330     if ( in != cdom ) {
02331       nurbs_t = cdom.ParameterAt(in.NormalizedParameterAt(nurbs_t));
02332       rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t);
02333       if ( rc )
02334         *curve_t = in.ParameterAt(cdom.NormalizedParameterAt(*curve_t));
02335     }
02336     else {
02337       rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t);
02338     }
02339   }
02340   return rc;
02341 }
02342 
02343 ON_BOOL32 ON_PolyCurve::GetNurbFormParameterFromCurveParameter(
02344       double curve_t,
02345       double* nurbs_t
02346       ) const
02347 {
02348   ON_BOOL32 rc = false;
02349   int i = SegmentIndex( curve_t );
02350   const ON_Curve* curve = SegmentCurve(i);
02351   if ( curve ) {
02352     ON_Interval in( m_t[i], m_t[i+1] );
02353     ON_Interval cdom = curve->Domain();
02354     if ( in != cdom ) {
02355       curve_t = cdom.ParameterAt(in.NormalizedParameterAt(curve_t));
02356       rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t);
02357       if ( rc )
02358         *nurbs_t = in.ParameterAt(cdom.NormalizedParameterAt(*nurbs_t));
02359     }
02360     else {
02361       rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t);
02362     }
02363   }
02364   return rc;
02365 }
02366 
02367 
02368 ON_Curve* ON_PolyCurve::HarvestSegment( int i )
02369 {
02370   ON_Curve* segment_curve = 0;
02371   if ( i >= 0 && i < m_segment.Count() ) {
02372     segment_curve = m_segment[i];
02373     m_segment[i] = 0;
02374   }
02375   return segment_curve;
02376 }
02377 
02378 ON_BOOL32 ON_PolyCurve::Trim(
02379   const ON_Interval& domain
02380   )
02381 {
02382   // Please talk to Dale Lear before you change code in this function.
02383 
02384   // m_t[] = Increasing array of segment_count+1 parameter values
02385   //         that specify segment domains.  
02386   //         Domain of polycurve = (m_t[0],m_t[segment_count]).
02387   // m_segment[] = array of segment curves
02388   int segment_count = m_segment.Count();
02389   if ( m_t.Count() < 2 || segment_count+1 != m_t.Count() || !domain.IsIncreasing() )
02390   {
02391     // bogus input
02392     return false;
02393   }
02394 
02395   const ON_Interval original_polycurve_domain = Domain();
02396   if ( !original_polycurve_domain.IsIncreasing() )
02397     return false;
02398 
02399   ON_Interval output_domain = domain;
02400   if ( !output_domain.Intersection(original_polycurve_domain) )
02401     return false;
02402 
02403         if(!output_domain.IsIncreasing())
02404                 return false;
02405 
02406   if (output_domain == original_polycurve_domain )
02407     return true;
02408 
02409   ON_Interval actual_trim_domain = output_domain;
02410 
02411   int s0 = -2; // s0 gets set to index of first segment we keep
02412   int s1 = -3; // s1 gets set to index of last segment we keep
02413   
02414   // 22 October 2003 Dale Lear - redid Greg's parameter search 
02415   //   snapping stuff.  New stuff is in sourcesafe version 72.
02416   //   In particular, attempting using "Trim" to extend polycurves
02417   //   will not be supported.  You have to use "Extend" if you
02418   //   want a curve to get longer.
02419 
02420   // In mid 3003, Greg added ParameterSearch to do "microtol" snapping 
02421   // to segment end parameters.  The goal was to handle fuzz that gets
02422   // introduces by reparameterizations that happen when the top level
02423   // curve is a proxy/poly curve and the proxy/polycurve trim parameters get
02424   // readjusted as we move toward trimming the "real" curve that is 
02425   // stored in the m_segment[] array.
02426         if ( ParameterSearch(output_domain[0], s0, true ) )
02427   {
02428     // ParameterSearch says domain[0] is within "microtol" of
02429     // m_t[s0].  So we will actually trim at m_t[s0].
02430     if (s0 >= 0 && s0 <= segment_count)
02431     {
02432       actual_trim_domain[0]=m_t[s0];
02433     }
02434   }
02435 
02436         if ( ParameterSearch(output_domain[1], s1, true ) )
02437   {
02438     if (s1 >= 0 && s1 <= segment_count )
02439     {
02440       // ParameterSearch says domain[1] is within "microtol" of
02441       // m_t[s1].  So we will actually trim at m_t[s1].
02442       actual_trim_domain[1]=m_t[s1];
02443       s1--;
02444     }
02445   }
02446 
02447   if ( !actual_trim_domain.IsIncreasing() )
02448   {
02449     // After microtol snapping, there is not enough curve left to trim.
02450     return false;
02451   }
02452 
02453   if ( s0 < 0 || s0 > s1 || s1 >= segment_count )
02454   {
02455     // Because output_domain is a subinterval of original_polycurve_domain,
02456     // the only way that (s0 < 0 || s0 > s1 || s1 >= segment_count) can be true
02457     // is if something is seriously wrong with the m_t[] values.
02458     return false;
02459   }
02460 
02461   // we will begin modifying the polycurve
02462   DestroyCurveTree();
02463 
02464   if ( actual_trim_domain == original_polycurve_domain )
02465   {
02466     // ParameterSearch says that the ends of output_domain
02467     // were microtol away from being the entire curve.  
02468     // Set the domain and return.
02469     m_t[0] = output_domain[0];
02470     m_t[segment_count] = output_domain[1];
02471     return true;
02472   }
02473         
02474   int i;
02475   for ( i = 0; i < s0; i++ )
02476   {
02477     // delete curves in segments [0,...,s0-1]
02478     delete m_segment[i];
02479     m_segment[i] = 0;
02480   }
02481   for ( i = s1+1; i < segment_count; i++ )
02482   {
02483     // delete curves in segments [s1+1,...,segment_count-1]
02484     delete m_segment[i];
02485     m_segment[i] = 0;
02486   }
02487 
02488   // remove segments [s1+1,...,segment_count-1] from polycurve
02489   m_segment.SetCount( s1+1 );
02490   m_t.SetCount(s1+2);
02491   segment_count = s1+1;
02492 
02493   if ( s0 > 0 )
02494   {
02495     // remove segments [0,...,s0-1] from polycurve
02496     ON_SimpleArray<ON_Curve*> tmp_seg(s1+1-s0);
02497     ON_SimpleArray<double> tmp_t(s1+2-s0);
02498     tmp_seg.Append( s1+1-s0, m_segment.Array()+s0 );
02499     tmp_t.Append( s1+2-s0, m_t.Array()+s0 );
02500     m_segment.Zero();
02501     m_segment.SetCount( 0 );
02502     m_segment.Append( tmp_seg.Count(), tmp_seg.Array() );
02503     m_t = tmp_t;
02504     segment_count = s1-s0+1;
02505     s1 = segment_count-1;
02506     s0 = 0;
02507   }
02508 
02509 
02510   const double fuzz = 0.001; // Greg says: anything small and > 1.0e-6 will work about the same
02511   bool bTrimFirstSegment = ( m_t[0] < actual_trim_domain[0] || (0 == s1 && actual_trim_domain[1] < m_t[s1+1]) );
02512   bool bTrimLastSegment = (s1>s0 && actual_trim_domain[1] < m_t[s1+1]);
02513 
02514   // if needed, trim left end of first segment
02515   ON_Interval trim_s_dom, trim_c_dom, c_dom, s_dom;
02516 
02517   if ( bTrimFirstSegment )
02518   {
02519     ON_Curve* curve = SegmentCurve(0);
02520     if ( 0 == curve )
02521       return false; // bogus polycurve (m_segment[0] is a NULL pointer)
02522 
02523     c_dom = curve->Domain();
02524     if ( !c_dom.IsIncreasing() )
02525     {
02526       // first segment curve is bogus
02527       return false;
02528     }
02529 
02530     s_dom = SegmentDomain(0);
02531     if ( !s_dom.IsIncreasing() )
02532     {
02533       // m_t[0] or m_t[1] is bogus
02534       return false;
02535     }
02536 
02537     trim_s_dom = s_dom;
02538           if ( !trim_s_dom.Intersection(actual_trim_domain) )
02539     {
02540       // Should never happen; if it does, we have to give up.
02541       // (and there is probably a bug in the code above)
02542       return false;
02543     }
02544 
02545     if ( s1 > 0 && trim_s_dom[1] != s_dom[1] )
02546     {
02547       // Should never happen; if it does, we have to give up.
02548       // (and there is probably a bug in the code above)
02549       return false;
02550     }
02551 
02552     if ( !trim_s_dom.IsIncreasing() )
02553     {
02554       // Should never happen; if it does, we have to give up.
02555       // (and there is probably a bug in the code above)
02556       return false;
02557     }
02558 
02559     if ( c_dom != s_dom )
02560     {
02561       // need to convert polycurve parameters to "real" segment curve parameters
02562       trim_c_dom[0] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[0]) );
02563       trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) );
02564       if ( !trim_c_dom.IsIncreasing() )
02565       {
02566         if ( s_dom.NormalizedParameterAt(trim_s_dom[0]) >= 1.0-fuzz && s1 > 0 )
02567         {
02568           // We were trying to throw away all but a microscopic bit on the right
02569           // end of the first segment of a multi segment polycurve
02570           // and the parameter conversion killed the "real" trim interval.
02571           // In this case, we can just throw away the first segment.
02572           bTrimFirstSegment = false;
02573           curve = 0;
02574           delete m_segment[0];
02575           m_segment[0] = 0;
02576                             m_t.Remove(0);
02577                             m_segment.Remove(0);
02578                             s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
02579         }
02580         else
02581           return false;
02582       }
02583     }
02584     else
02585     {
02586       trim_c_dom = trim_s_dom;
02587     }
02588 
02589     // trim_s_dom = polycurve segment parameters after trimming
02590     // trim_c_dom = "real" segment curve parameters after trimming
02591 
02592     if ( bTrimFirstSegment && trim_c_dom != c_dom )
02593     {
02594       // trim first segment
02595       if ( !curve->Trim(trim_c_dom) )
02596       {
02597         // trimming first segment failed - see if we should give up or
02598         // or just discard the first segment.
02599 
02600                           if ( c_dom.NormalizedParameterAt(trim_c_dom[0]) >= 1.0 - fuzz && s1 > 0 )
02601         {
02602                             // remove entire first segment
02603           bTrimFirstSegment = false;
02604           curve = 0;
02605           delete m_segment[0];
02606           m_segment[0] = 0;
02607                             m_t.Remove(0);
02608                             m_segment.Remove(0);
02609                             s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
02610         }
02611         else
02612           return false;
02613                   }
02614       else
02615       {
02616         m_t[0] = actual_trim_domain[0]; // will be tweaked below when we've finished.
02617         if ( 0 == s1 && 2 == m_t.Count() && !bTrimLastSegment )
02618           m_t[1] = actual_trim_domain[1];
02619       }
02620     }
02621   }
02622 
02623 
02624 
02625   if ( bTrimLastSegment ) 
02626   {
02627     // If we get in here, it means we need to trim a portion off of
02628     // the right end of the last segment.
02629     
02630     if ( s1+1 != m_segment.Count() )
02631     {
02632       // Should never happen; if it does, we have to give up.
02633       // (and there is probably a bug in the code above)
02634       return false;
02635     }
02636 
02637     ON_Curve* curve = SegmentCurve(s1);
02638     if ( 0 == curve )
02639       return false; // bogus polycurve (m_segment[s1] array has a null pointer)
02640 
02641     c_dom = curve->Domain();
02642     if ( !c_dom.IsIncreasing() )
02643     {
02644       // first segment curve is bogus
02645       return false;
02646     }
02647 
02648     s_dom = SegmentDomain(s1);
02649     if ( !s_dom.IsIncreasing() )
02650     {
02651       // m_t[s1] or m_t[s1+1] is bogus
02652       return false;
02653     }
02654 
02655                 // trim the curve on the right
02656     trim_s_dom= ON_Interval(m_t[s1], actual_trim_domain[1]);
02657 
02658     if ( !trim_s_dom.IsIncreasing() )
02659     {
02660       // Should never happen; if it does, we have to give up.
02661       // (and there is probably a bug in the code above)
02662       return false;
02663     }
02664 
02665     trim_c_dom[0] = c_dom[0];
02666     if ( c_dom != s_dom )
02667     {
02668       trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) );
02669       if ( !trim_c_dom.IsIncreasing() )
02670       {
02671         if ( s_dom.NormalizedParameterAt(trim_s_dom[1]) <= fuzz && s1 > 0 )
02672         {
02673           // We were trying to throw away all but a microscopic bit on the left
02674           // end of the last segment of a multi segment polycurve
02675           // and the parameter conversion killed the "real" trim interval.
02676           // In this case, we can just throw away the last segment.
02677           bTrimLastSegment = false;
02678           curve = 0;
02679           delete m_segment[s1];
02680           m_segment[s1] = 0;
02681                             m_t.Remove();       // remove last array entry in the m_t[] array
02682                             m_segment.Remove(); // remove last array entry in the m_segment[] array
02683                             s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
02684         }
02685         else
02686           return false;
02687       }
02688     }
02689     else
02690     {
02691       trim_c_dom[1] = trim_s_dom[1];
02692     }
02693 
02694     if ( bTrimLastSegment && c_dom != trim_c_dom )
02695     {
02696       // trim last segment
02697       if ( !curve->Trim(trim_c_dom) )
02698       {
02699 
02700                                 if ( c_dom.NormalizedParameterAt(trim_c_dom[1]) <= fuzz && s1 > 0)
02701         {
02702           // We were trying to throw away all but a microscopic bit on the left
02703           // end of the last segment of a multi segment polycurve
02704           // and the segment curve's trimmer failed.  I'm assuming the
02705           // failure was caused because the part that would be left was
02706           // too short.
02707           // In this case, we can just throw away the last segment.
02708           bTrimLastSegment = false;
02709           curve = 0;
02710           delete m_segment[s1];
02711           m_segment[s1] = 0;
02712                             m_t.Remove();       // remove last array entry in the m_t[] array
02713                             m_segment.Remove(); // remove last array entry in the m_segment[] array
02714                             s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
02715         }
02716         else
02717           return false;
02718                         }
02719       else
02720         m_t[m_t.Count()-1] = actual_trim_domain[1]; // will be tweaked below when we've finished.
02721                 }
02722   }
02723 
02724   // If we get this far, trims were is successful.
02725   // The following makes potential tiny adjustments
02726   // that need to happen when trims get snapped to
02727   // m_t[] values that are within fuzz of the
02728   // output_domain[] values.
02729         m_t[0] = output_domain[0];
02730   m_t[m_t.Count()-1] = output_domain[1];
02731 
02732         DestroyCurveTree();
02733 
02734   return true;
02735 }
02736 
02737 
02738 
02739 
02740 bool ON_PolyCurve::Extend(
02741   const ON_Interval& domain
02742   )
02743  
02744 {
02745   if (IsClosed() || Count() < 1) return false;
02746  
02747   bool changed = false;
02748   if (Domain()[0] > domain[0]){
02749     ON_Curve* seg = SegmentCurve(0);
02750     if (!seg) return false;
02751     ON_Interval sdom = SegmentDomain(0);
02752     ON_Interval cdom = seg->Domain();
02753     double a = (sdom == cdom) ? domain[0] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[0]));
02754     ON_Interval DesiredDom(a, cdom[1]);
02755     changed = seg->Extend(DesiredDom);
02756     if (changed) {
02757       if (seg->Domain() == DesiredDom)
02758         m_t[0] = domain[0];
02759       else
02760         m_t[0] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[0]));
02761     }
02762   }
02763   if (Domain()[1] < domain[1]){
02764     bool chgd = false;
02765     ON_Curve* seg = SegmentCurve(Count()-1);
02766     if (!seg) return false;
02767     ON_Interval sdom = SegmentDomain(Count()-1);
02768     ON_Interval cdom = seg->Domain();
02769     double a = (sdom == cdom) ? domain[1] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[1]));
02770     ON_Interval DesiredDom(cdom[0], a);
02771     chgd = seg->Extend(DesiredDom);
02772     if (chgd) {
02773       if (seg->Domain() == DesiredDom)
02774         m_t[Count()] = domain[1];
02775       else
02776         m_t[Count()] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[1]));
02777       changed = true;
02778     }
02779   }
02780  
02781   if (changed){
02782     DestroyCurveTree();
02783   }
02784  
02785   return changed;
02786 }
02787  
02788 
02789 
02790 
02791 ON_BOOL32 ON_PolyCurve::Split(
02792     double split_parameter,
02793     ON_Curve*& left_side, // left portion returned here
02794     ON_Curve*& right_side // right portion returned here
02795   ) const
02796 {
02797   int si;
02798   ON_Interval dom = Domain();
02799   
02800   ON_PolyCurve* pLeftSide  = ON_PolyCurve::Cast(left_side);
02801   ON_PolyCurve* pRightSide = ON_PolyCurve::Cast(right_side);
02802 
02803   if ( pLeftSide && pLeftSide != this )
02804     pLeftSide->Destroy();
02805   else if ( pLeftSide == this )
02806     pLeftSide->DestroyCurveTree();
02807 
02808   if ( pRightSide && pRightSide != this )
02809     pRightSide->Destroy();
02810   else if ( pRightSide == this )
02811     pRightSide->DestroyCurveTree();
02812 
02813   if ( left_side && !pLeftSide )
02814     return false;
02815   if ( right_side && !pRightSide )
02816     return false;
02817   if ( !dom.Includes( split_parameter, true ) )
02818     return false; // split_parameter is not an interior parameter
02819 
02820 
02821   const ON_BOOL32 bDupSegs = ( this != pLeftSide && this != pRightSide );
02822 
02823                 /* 4 April 2003 Greg Arden              Made the following changes:
02824                                                                                                                                                 1.      Use ParameterSearch() to decide if we should snap domain
02825                                                                                                                                                                 boundries to m_t array values.  
02826                                                                                                                                                 2.  Make sure resulting polycurves have Domain() specified as 
02827                                                                                                                                                                 split parameter.   
02828                                                                                                                                                 3.  When true is returned the result passes IsValid().
02829                 */
02830         bool split_at_break = ParameterSearch(split_parameter, si, true);
02831         if( split_at_break && (si<=0 || si>=Count() ) )
02832                 return false;
02833 
02834         ON_Interval s_dom = SegmentDomain(si);
02835   ON_Curve* seg_curve = SegmentCurve(si);
02836   if ( !seg_curve )
02837     return false;
02838   ON_Interval c_dom = seg_curve->Domain();
02839 
02840   double c;
02841         if( split_at_break)
02842                 c = c_dom[0];
02843         else
02844                 c = ( c_dom == s_dom )
02845            ? split_parameter
02846            : c_dom.ParameterAt( s_dom.NormalizedParameterAt(split_parameter) );
02847 
02848   ON_Curve* seg_left = 0;
02849   ON_Curve* seg_right = 0;
02850 
02851   if ( !split_at_break  && c_dom.Includes(c,true) )
02852   {
02853     if ( !seg_curve->Split( c, seg_left, seg_right ) )
02854     {
02855       double fuzz = 0.001; // anything small and > 1.0e-6 will work about the same
02856       if ( c_dom.NormalizedParameterAt(c) <= fuzz )
02857         c = c_dom[0];
02858       else if ( c_dom.NormalizedParameterAt(c) >= 1.0 - fuzz )
02859         c = c_dom[1];
02860       else
02861         return false; // unable to split this segment
02862     }
02863   }
02864   else if ( c <= c_dom.ParameterAt(0.5) )
02865     c = c_dom[0];
02866   else
02867     c = c_dom[1];
02868 
02869   // use scratch arrays since this may also be pLeftSide or pRightSide
02870   ON_SimpleArray< ON_Curve* > left_segment;
02871   ON_SimpleArray< ON_Curve* > right_segment;
02872   ON_SimpleArray< double > left_t;
02873   ON_SimpleArray< double > right_t;
02874 
02875   int i;
02876 
02877   if ( seg_left && seg_right )
02878   {
02879     // we split a segment
02880     left_segment.Reserve(si+1);
02881     right_segment.Reserve(m_segment.Count()-si);
02882     left_t.Reserve(left_segment.Count()+1);
02883     right_t.Reserve(right_segment.Count()+1);
02884     if ( !bDupSegs )
02885     {
02886       delete m_segment[si];
02887       const_cast<ON_PolyCurve*>(this)->m_segment[si] = 0;
02888     }
02889     
02890     for ( i = 0; i < si; i++ )
02891     {
02892       if ( bDupSegs )
02893         left_segment.Append( m_segment[i]->Duplicate() );
02894       else
02895         left_segment.Append( m_segment[i] );
02896       left_t.Append( m_t[i] );
02897     }
02898     left_segment.Append( seg_left );
02899     left_t.Append( m_t[si] );
02900     left_t.Append( split_parameter );
02901 
02902     right_segment.Append(seg_right);
02903     right_t.Append( split_parameter );
02904     for ( i = si+1; i < m_segment.Count(); i++ )
02905     {
02906       if ( bDupSegs )
02907         right_segment.Append( m_segment[i]->Duplicate() );
02908       else
02909         right_segment.Append( m_segment[i] );
02910       right_t.Append( m_t[i] );
02911     }
02912     right_t.Append( m_t[m_segment.Count()] );
02913   }
02914   else 
02915   {
02916     if ( c == c_dom[1] )
02917       si++;
02918                 if( (c==c_dom[0] && si==0 ) ||                                                          // attempting split at curve start
02919                                 (c==c_dom[1] && si==m_segment.Count() ) )       // attempting split at curve end
02920                         return false;
02921 
02922     left_segment.Reserve(si);
02923     right_segment.Reserve(m_segment.Count()-si);
02924     left_t.Reserve(left_segment.Count()+1);
02925     right_t.Reserve(right_segment.Count()+1);
02926     
02927     for ( i = 0; i < si; i++ )
02928     {
02929       if ( bDupSegs )
02930         left_segment.Append( m_segment[i]->Duplicate() );
02931       else
02932         left_segment.Append( m_segment[i] );
02933       left_t.Append( m_t[i] );
02934     }
02935     left_t.Append( split_parameter );
02936 
02937     for ( i = si; i < m_segment.Count(); i++ )
02938     {
02939       if ( bDupSegs )
02940         right_segment.Append( m_segment[i]->Duplicate() );
02941       else
02942         right_segment.Append( m_segment[i] );
02943       if ( i == si )
02944         right_t.Append( split_parameter );
02945       else
02946         right_t.Append( m_t[i] );
02947     }
02948     right_t.Append( m_t[m_segment.Count()] );
02949   }
02950 
02951   if ( !pLeftSide )
02952     pLeftSide = new ON_PolyCurve();
02953   if ( !pRightSide )
02954     pRightSide = new ON_PolyCurve();
02955   if ( !bDupSegs )
02956   {
02957     // pLeftSide or pRightSide is the same as this
02958     ON_PolyCurve* this_ptr = const_cast<ON_PolyCurve*>(this);
02959     this_ptr->m_segment.Zero();
02960     this_ptr->m_t.Zero();
02961     this_ptr->m_segment.SetCount(0);
02962     this_ptr->m_t.SetCount(0);
02963   }
02964 
02965   pLeftSide->m_segment.Append( left_segment.Count(), left_segment.Array() );
02966   pLeftSide->m_t.Append( left_t.Count(), left_t.Array() );
02967   pRightSide->m_segment.Append( right_segment.Count(), right_segment.Array() );
02968   pRightSide->m_t.Append( right_t.Count(), right_t.Array() );
02969 
02970   left_side = pLeftSide;
02971   right_side = pRightSide;
02972 
02973   return true;
02974 }
02975 
02976 // Flatten a poly curve reparameterized over pdom. 
02977 // Harvests all the segments recursively and places them in the arrays
02978 static
02979 void Flatten( ON_PolyCurve* poly, ON_Interval pdom, ON_SimpleArray<double>& new_t, ON_SimpleArray<ON_Curve*>& new_seg){
02980                 int n= poly->Count();
02981                 double t0 = pdom[0];
02982                 ON_Interval pcdom = poly->Domain();
02983                 for(int i=0; i<n; i++){
02984                         double sdom=poly->SegmentDomain(i)[1];
02985                         double ndom=pcdom.NormalizedParameterAt(sdom);  
02986                         double t1 =pdom.ParameterAt(ndom);
02987                         ON_Curve* seg = poly->SegmentCurve(i);
02988                         ON_PolyCurve* spoly =  ON_PolyCurve::Cast(seg);
02989                         if(spoly){
02990                                 Flatten(spoly, ON_Interval(t0,t1), new_t, new_seg );
02991                                 poly->HarvestSegment(i);                
02992                                 delete spoly;
02993                         } else {
02994                                 new_t.Append(t1);
02995                                 new_seg.Append(seg);
02996                                 poly->HarvestSegment(i);
02997                         }
02998                         t0 = t1;
02999                 }
03000 } 
03001 
03002 bool ON_PolyCurve::HasSynchronizedSegmentDomains() const
03003 {
03004   double t0, t1;
03005   int i, count = m_segment.Count();
03006   const ON_Curve* const * c = m_segment.Array();
03007   if ( count < 1 || 0 == c )
03008     return false;
03009   if ( count != m_t.Count()+1 )
03010     return false;
03011   const double* t = m_t.Array();
03012   if ( 0 == t )
03013     return false;
03014 
03015   for ( i = 0; i < count; i++ )
03016   {
03017     t0 = -ON_UNSET_VALUE;
03018     t1 = ON_UNSET_VALUE;
03019     if ( 0 != c[i] 
03020          && c[i]->GetDomain(&t0,&t1) 
03021          && t0 == t[i]
03022          && t1 == t[i+1]
03023          )
03024      {
03025        continue;
03026      }
03027      return false;
03028   }
03029 
03030   return true;  
03031 }
03032 
03033 /* 
03034 Description:
03035   Sets the domain of the curve int the m_segment[] array to exactly
03036   match the domain defined in the m_t[] array.  This is not required,
03037   but can simplify some coding situations.
03038 Returns:
03039   True if at least one segment was reparameterized. False if no
03040   changes were made.
03041 */
03042 bool ON_PolyCurve::SynchronizeSegmentDomains()
03043 {
03044   double t0, t1;
03045   int i, count = m_segment.Count();
03046   ON_Curve** c = m_segment.Array();
03047   if ( count < 1 || 0 == c )
03048     return false;
03049   if ( count+1 != m_t.Count() )
03050     return false;
03051   const double* t = m_t.Array();
03052   if ( 0 == t )
03053     return false;
03054 
03055   bool rc = false;
03056   for ( i = 0; i < count; i++ )
03057   {
03058     if ( !c[i] )
03059       continue;
03060     t0 = -ON_UNSET_VALUE;
03061     t1 = ON_UNSET_VALUE;
03062     if ( c[i]->GetDomain(&t0,&t1) 
03063          && t0 == t[i]
03064          && t1 == t[i+1]
03065          )
03066     {
03067      continue;
03068     }
03069 
03070     if (    ON_IsValid(t[i]) 
03071         && ON_IsValid(t[i+1])
03072         && t[i] < t[i+1] 
03073         && c[i]->SetDomain(t[i],t[i+1]) 
03074       )
03075     {
03076      rc = true; // indicates a change was made
03077     }
03078   }
03079   return rc;
03080 }
03081 
03082 
03083 
03084 bool ON_PolyCurve::RemoveNestingEx( )
03085 {
03086   // 19 March 2003 Dale Lear
03087   //     Added the true/false return code to RemoveNesting()
03088   //     so that CRhinoDoc::AddObjects() can easily detect when
03089   //     nested polycurves were added.  I had to add use
03090   //     a new function name to avoid breaking the SDK.
03091   bool rc = false;
03092         int n = Count();
03093 
03094         ON_SimpleArray<double> old_t = m_t;
03095         ON_SimpleArray<ON_Curve*> old_seg = m_segment;
03096 
03097         m_t.SetCount(1);
03098         m_segment.SetCount(0);
03099 
03100         for(int i=0; i<n;i++){
03101                 ON_PolyCurve* poly = ON_PolyCurve::Cast( old_seg[i]);
03102                 if(poly){
03103       rc = true;
03104                         Flatten( poly, ON_Interval(old_t[i], old_t[i+1]), m_t, m_segment );
03105                         delete poly;
03106                 } else {
03107                         m_t.Append( old_t[i+1]); 
03108                         m_segment.Append( old_seg[i] );
03109                 }
03110         }
03111   return rc;
03112 }
03113 
03114 void  ON_PolyCurve::RemoveNesting( )
03115 {
03116   RemoveNestingEx();
03117 }
03118 
03119 bool ON_PolyCurve::IsNested() const
03120 {
03121   int i, count = m_segment.Count();
03122         for ( i = 0; i < count; i++ )
03123   {
03124     if (  ON_PolyCurve::Cast(m_segment[i]) )
03125       return true;
03126   }
03127   return false;
03128 }
03129 
03130 
03131 //   Sets the m_segment[index] to crv. 
03132 void ON_PolyCurve::SetSegment(int i, ON_Curve* crv){
03133         if(i>=0 && i<Count())
03134                 m_segment[i] = crv;
03135 }
03136 
03137 // returns true if t is sufficiently close to m_t[index]
03138 bool ON_PolyCurve::ParameterSearch(double t, int& index, bool bEnableSnap) const{
03139         return ON_Curve::ParameterSearch(t, index, bEnableSnap, m_t, ON_SQRT_EPSILON);
03140 }
03141 
03142 const ON_CurveArray& ON_PolyCurve::SegmentCurves() const
03143 {
03144   return m_segment;
03145 }
03146 
03147 const ON_SimpleArray<double>& ON_PolyCurve::SegmentParameters() const
03148 {
03149   return m_t;
03150 }


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