1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
18 
19 ON_OBJECT_IMPLEMENT(ON_PolyCurve,ON_Curve,"4ED7D4E0-E947-11d3-BFE5-0010830122F0");
20 
ON_PolyCurve()21 ON_PolyCurve::ON_PolyCurve()
22 {
23 }
24 
ON_PolyCurve(int capacity)25 ON_PolyCurve::ON_PolyCurve( int capacity )
26               : m_segment(capacity), m_t(capacity+1)
27 {
28   m_segment.Zero();
29 }
30 
ON_PolyCurve(const ON_PolyCurve & src)31 ON_PolyCurve::ON_PolyCurve( const ON_PolyCurve& src )
32               : ON_Curve(src), m_segment(src.Count()), m_t(src.Count()+1)
33 {
34   *this = src;
35 }
36 
Destroy()37 void ON_PolyCurve::Destroy()
38 {
39   // release memory
40   m_segment.Destroy();
41   m_t.Destroy();
42 }
43 
~ON_PolyCurve()44 ON_PolyCurve::~ON_PolyCurve()
45 {
46   Destroy();
47 }
48 
EmergencyDestroy()49 void ON_PolyCurve::EmergencyDestroy()
50 {
51   m_segment.EmergencyDestroy();
52   m_t.EmergencyDestroy();
53 }
54 
DestroyRuntimeCache(bool bDelete)55 void ON_PolyCurve::DestroyRuntimeCache( bool bDelete )
56 {
57   ON_Curve::DestroyRuntimeCache(bDelete);
58   int i, count = m_segment.Count();
59   for ( i = 0; i < count; i++ )
60   {
61     ON_Curve* segment_curve = m_segment[i];
62     if ( 0 != segment_curve && this != segment_curve )
63       segment_curve->DestroyRuntimeCache(bDelete);
64   }
65 }
66 
SizeOf() const67 unsigned int ON_PolyCurve::SizeOf() const
68 {
69   unsigned int sz = ON_Curve::SizeOf();
70   sz += (sizeof(*this) - sizeof(ON_Curve));
71   sz += m_t.SizeOfArray();
72   sz += m_segment.SizeOfArray();
73   int i, count = m_segment.Count();
74   for ( i = 0; i < count; i++ )
75   {
76     const ON_Curve* crv = m_segment[i];
77     if ( crv )
78       sz += crv->SizeOf();
79   }
80   return sz;
81 }
82 
DataCRC(ON__UINT32 current_remainder) const83 ON__UINT32 ON_PolyCurve::DataCRC(ON__UINT32 current_remainder) const
84 {
85   int i, count = m_segment.Count();
86   for ( i = 0; i < count; i++ )
87   {
88     const ON_Curve* crv = m_segment[i];
89     if ( crv )
90       current_remainder = crv->DataCRC(current_remainder);
91   }
92   current_remainder = m_t.DataCRC(current_remainder);
93   return current_remainder;
94 }
95 
96 
operator =(const ON_PolyCurve & src)97 ON_PolyCurve& ON_PolyCurve::operator=( const ON_PolyCurve& src )
98 {
99   if ( this != &src ) {
100     ON_Curve::operator=(src);
101     const int segment_capacity = m_segment.Capacity();
102     ON_Curve** segment = m_segment.Array();
103     int i;
104     for ( i = 0; i < segment_capacity; i++ ) {
105       if ( segment[i] ) {
106         delete segment[i];
107         segment[i] = 0;
108       }
109     }
110     src.m_segment.Duplicate(m_segment);
111     m_t.SetCount(0);
112     m_t.SetCapacity(src.m_t.Count());
113     m_t.Zero();
114     m_t = src.m_t;
115   }
116   return *this;
117 }
118 
Dimension() const119 int ON_PolyCurve::Dimension() const
120 {
121   const ON_Curve* p = SegmentCurve(0);
122   return (p) ? p->Dimension() : 0;
123 }
124 
125 ON_BOOL32
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const126 ON_PolyCurve::GetBBox( // returns true if successful
127          double* boxmin,    // minimum
128          double* boxmax,    // maximum
129          ON_BOOL32 bGrowBox
130          ) const
131 {
132   const int count = Count();
133   int segment_index;
134   ON_BOOL32 rc = (count>0) ? true : false;
135   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
136     rc = m_segment[segment_index]->GetBBox( boxmin, boxmax, bGrowBox );
137     bGrowBox = true;
138   }
139   return rc;
140 }
141 
142 ON_BOOL32
Transform(const ON_Xform & xform)143 ON_PolyCurve::Transform( const ON_Xform& xform )
144 {
145   TransformUserData(xform);
146   DestroyRuntimeCache();
147   const int count = Count();
148   int segment_index;
149   ON_BOOL32 rc = (count>0) ? true : false;
150   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
151     rc = m_segment[segment_index]->Transform( xform );
152   }
153   return rc;
154 }
155 
IsDeformable() const156 bool ON_PolyCurve::IsDeformable() const
157 {
158   bool rc = true;
159   const int count = Count();
160   int segment_index;
161   for ( segment_index = 0; segment_index < count ; segment_index++ )
162   {
163     const ON_Curve* seg = m_segment[segment_index];
164     if ( seg && !seg->IsDeformable() )
165     {
166       rc = false;
167       break;
168     }
169   }
170   return rc;
171 }
172 
MakeDeformable()173 bool ON_PolyCurve::MakeDeformable()
174 {
175   bool rc = true;
176   bool bDestroyRuntimeCache = false;
177   const int count = Count();
178   int segment_index;
179   for ( segment_index = 0; segment_index < count ; segment_index++ )
180   {
181     ON_Curve* seg = m_segment[segment_index];
182     if ( seg && !seg->IsDeformable() )
183     {
184       bDestroyRuntimeCache = true;
185       if ( !seg->MakeDeformable() )
186       {
187         ON_NurbsCurve* nurbs_curve = seg->NurbsCurve();
188         if ( nurbs_curve )
189         {
190           delete seg;
191           m_segment[segment_index] = nurbs_curve;
192         }
193         else
194           rc = false;
195       }
196     }
197   }
198   if ( bDestroyRuntimeCache )
199     DestroyRuntimeCache();
200   return rc;
201 }
202 
203 
204 ON_BOOL32
SwapCoordinates(int i,int j)205 ON_PolyCurve::SwapCoordinates( int i, int j )
206 {
207   const int count = Count();
208   int segment_index;
209   ON_BOOL32 rc = (count>0) ? true : false;
210   for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
211     rc = m_segment[segment_index]->SwapCoordinates( i, j );
212   }
213 	DestroyCurveTree();
214   return rc;
215 }
216 
IsValid(ON_TextLog * text_log) const217 ON_BOOL32 ON_PolyCurve::IsValid( ON_TextLog* text_log ) const
218 {
219   return IsValid(false,text_log);
220 }
221 
IsValid(bool bAllowGaps,ON_TextLog * text_log) const222 bool ON_PolyCurve::IsValid( bool bAllowGaps, ON_TextLog* text_log ) const
223 {
224   const int count = Count();
225   const int dim = Dimension();
226   ON_3dPoint p0, p1;
227   int segment_index;
228   if ( count <= 0 || dim <= 0 )
229   {
230     if ( text_log )
231       text_log->Print("Polycurve segment count = %d and dim = %d\n",count,dim);
232     return ON_IsNotValid();
233   }
234 
235   if ( m_t.Count() != count+1 )
236   {
237     if ( text_log )
238       text_log->Print("Polycurve segment count = %d and m_t.Count()=%d (should be segment count+1)\n",
239                       count,m_t.Count());
240     return ON_IsNotValid();
241   }
242 
243   for ( segment_index = 0; segment_index < count; segment_index++ )
244   {
245     if ( 0 == m_segment[segment_index] )
246     {
247       if ( text_log )
248       {
249         text_log->Print("Polycurve segment[%d] is null.\n",segment_index);
250       }
251       return ON_IsNotValid();
252     }
253 
254     if ( !m_segment[segment_index]->IsValid( text_log ) )
255     {
256       if ( text_log )
257       {
258         text_log->Print("Polycurve segment[%d] is not valid.\n",segment_index);
259       }
260       return ON_IsNotValid();
261     }
262 
263     int seg_dim = m_segment[segment_index]->Dimension();
264     if ( seg_dim != dim )
265     {
266       if ( text_log )
267         text_log->Print("Polycurve segment[%d]->Dimension()=%d (should be %d).\n",segment_index,seg_dim,dim);
268       return ON_IsNotValid(); // all segments must have same dimension
269     }
270 
271     if ( m_t[segment_index] >= m_t[segment_index+1] )
272     {
273       if ( text_log )
274         text_log->Print("Polycurve m_t[%d]=%g and m_t[%d]=%g (should be increasing)\n",
275                          segment_index,   m_t[segment_index],
276                          segment_index+1, m_t[segment_index+1]);
277       return ON_IsNotValid(); // segment domain must be non-empty
278     }
279 
280     if ( count > 1 && !bAllowGaps && m_segment[segment_index]->IsClosed() )
281     {
282       if ( text_log )
283         text_log->Print("Polycurve segment[%d] is closed (%d segments).\n",segment_index,count);
284       return ON_IsNotValid(); // closed segments not permitted in multi segment curve
285     }
286   }
287 
288   if ( !bAllowGaps )
289   {
290     // check for gaps
291     int gap_index = FindNextGap(0);
292     if ( gap_index > 0 )
293     {
294       p0 = m_segment[gap_index-1]->PointAtEnd();
295       p1 = m_segment[gap_index]->PointAtStart();
296       double d = p0.DistanceTo(p1);
297       if ( text_log )
298         text_log->Print("Polycurve end of segment[%d] != start of segment[%d] (distance=%g)\n",
299                         gap_index-1, gap_index, d );
300       return ON_IsNotValid(); // not contiguous
301     }
302   }
303 
304   return true;
305 }
306 
Dump(ON_TextLog & dump) const307 void ON_PolyCurve::Dump( ON_TextLog& dump ) const
308 {
309   const int count = Count();
310   int i;
311 
312   ON_3dPoint segment_start = ON_3dPoint::UnsetPoint;
313   ON_3dPoint segment_end = ON_3dPoint::UnsetPoint;
314   double gap;
315 
316   dump.Print( "ON_PolyCurve segment count = %d\n", count );
317   dump.PushIndent();
318   for ( i = 0; i < count; i++ )
319   {
320     if ( 0 != m_segment[i] )
321       segment_start = m_segment[i]->PointAtStart();
322     else
323       segment_start = ON_3dPoint::UnsetPoint;
324     gap = (segment_start.IsValid() && segment_end.IsValid())
325         ? segment_start.DistanceTo(segment_end)
326         : ON_UNSET_VALUE;
327     dump.Print( "Segment %d: (%g,%g)", i+1, m_t[i], m_t[i+1] );
328     if ( i > 0 )
329     {
330       if ( ON_IsValid(gap) )
331         dump.Print(" gap = %.17g",gap);
332       else if ( !segment_start.IsValid() )
333         dump.Print(" invalid segment curve");
334       else if ( !segment_end.IsValid() )
335         dump.Print(" invalid previous segment curve");
336     }
337     dump.Print("\n");
338 
339     dump.PushIndent();
340     if ( 0 == m_segment[i] )
341     {
342       dump.Print("null curve pointer\n");
343       segment_end = ON_3dPoint::UnsetPoint;
344     }
345     else
346     {
347       m_segment[i]->Dump(dump);
348       segment_end = m_segment[i]->PointAtEnd();
349     }
350     dump.PopIndent();
351   }
352   dump.PopIndent();
353 }
354 
Write(ON_BinaryArchive & file) const355 ON_BOOL32 ON_PolyCurve::Write(
356        ON_BinaryArchive& file // open binary file
357      ) const
358 {
359   // NOTE - if changed, check legacy I/O code
360   ON_BOOL32 rc = file.Write3dmChunkVersion(1,0);
361   if (rc) {
362     int reserved1 = 0;
363     int reserved2 = 0;
364     const int count = Count();
365     int segment_index;
366 
367     rc = file.WriteInt( count );
368     if (rc) file.WriteInt(reserved1);
369     if (rc) file.WriteInt(reserved2);
370     if (rc) {
371       // may be set in future
372       ON_BoundingBox bbox;
373       rc = file.WriteBoundingBox(bbox);
374     }
375     if (rc) rc = file.WriteArray( m_t );
376 
377     for ( segment_index = 0; segment_index < count && rc; segment_index++ ) {
378       rc = file.WriteObject( *SegmentCurve(segment_index) );
379     }
380 
381 
382   }
383   return rc;
384 }
385 
Read(ON_BinaryArchive & file)386 ON_BOOL32 ON_PolyCurve::Read(
387        ON_BinaryArchive& file  // open binary file
388      )
389 {
390   // NOTE - if changed, check legacy I/O code
391   Destroy();
392   int major_version = 0;
393   int minor_version = 0;
394 
395   ON_BOOL32 rc = file.Read3dmChunkVersion(&major_version,&minor_version);
396 
397   if (rc)
398   {
399     ON_Object* obj;
400     ON_Curve* crv;
401     int segment_index;
402     int segment_count = 0;
403     int reserved1 = 0;
404     int reserved2 = 0;
405     rc = file.ReadInt(&segment_count);
406     if (rc) rc = file.ReadInt(&reserved1);
407     if (rc) rc = file.ReadInt(&reserved2);
408     if (rc) {
409       // may be set in future
410       ON_BoundingBox bbox;
411       rc = file.ReadBoundingBox(bbox);
412     }
413     if (rc) rc = file.ReadArray( m_t );
414 
415     for ( segment_index = 0; segment_index < segment_count && rc; segment_index++ ) {
416       obj = 0;
417       crv = 0;
418       rc = file.ReadObject( &obj );
419       if (rc) {
420         crv = ON_Curve::Cast(obj);
421         if (crv) {
422           //Append(crv); - this one adds to m_t[]
423           m_segment.Append(crv);
424         }
425         else {
426           ON_ERROR("ON_PolyCurve::Read() - non ON_Curve object in segment list\n");
427           delete obj;
428           rc = false;
429         }
430       }
431     }
432 
433     if ( rc && m_segment.Count()>0 &&
434 								m_segment.Count()==segment_count && m_t.Count()==segment_count+1)
435     {
436       // remove "fuzz" in m_t[] domain array that is in some older files.
437       double s, t, d0, d1, fuzz;
438       ON_Interval in0, in1;
439       in1 = SegmentCurve(0)->Domain();
440       d1 = in1.Length();
441       for ( segment_index = 1; segment_index < segment_count; segment_index++ )
442       {
443         t = m_t[segment_index];
444         in0 = in1;
445         d0 = d1;
446         in1 = SegmentCurve(segment_index)->Domain();
447         d1 = in1.Length();
448         s = in0[1];
449         if ( s != t && s == in1[0] && t > in0[0] && t < in1[1] )
450         {
451           fuzz = ((d0<=d1)?d0:d1)*ON_SQRT_EPSILON;
452           if ( fabs(t-s) <= fuzz )
453             m_t[segment_index] = s;
454         }
455       }
456       fuzz = d1*ON_SQRT_EPSILON;
457       t = m_t[segment_count];
458       s = in1[1];
459       if ( s != t && t > in1[0] && fabs(s-t) <= fuzz )
460         m_t[segment_count] = s;
461     }
462   }
463 
464   if (rc && file.ArchiveOpenNURBSVersion() < 200304080 )
465   {
466     // 8 April 2003 Dale Lear:
467     //   Some archives written by earlier versions
468     //   of Rhino had nested polycurves and polycurves with
469     //   zero length segments.  This code cleans up
470     //   those problems.  See RR 8932.
471     RemoveNesting();
472     //RemoveShortSegments(ON_ZERO_TOLERANCE);
473   }
474 
475   return rc;
476 }
477 
478 
DuplicateCurve() const479 ON_Curve* ON_PolyCurve::DuplicateCurve() const
480 {
481 	// Call DuplicateCurve on each segment to construct duplicate curve.
482 	int cnt = Count();
483 	ON_PolyCurve* dup_crv = new ON_PolyCurve( cnt );
484   dup_crv->CopyUserData(*this);
485 	for( int i=0; i<cnt; i++){
486 		const ON_Curve* seg = SegmentCurve(i);
487 		if(seg)
488 			dup_crv->Append( seg->DuplicateCurve() );
489 	}
490 	if( cnt == dup_crv->Count() )
491 		dup_crv->SetParameterization( m_t);
492 	return dup_crv;
493 }
494 
495 
Domain() const496 ON_Interval ON_PolyCurve::Domain() const
497 {
498   ON_Interval d;
499   const int count = Count();
500   if ( count > 0 && count+1 == m_t.Count() && m_t[0] < m_t[count] ) {
501     d.Set(m_t[0],m_t[count]);
502   }
503   return d;
504 }
505 
SetDomain(double t0,double t1)506 ON_BOOL32 ON_PolyCurve::SetDomain( double t0, double t1 )
507 {
508   ON_Interval d0 = Domain();
509   ON_Interval d1(t0,t1);
510   ON_BOOL32 rc = d1.IsIncreasing();
511   if ( rc && d0 != d1 )
512   {
513     int i, count = m_t.Count();
514     double s;
515     for ( i = 0; i < count; i++ )
516     {
517       s = d0.NormalizedParameterAt( m_t[i] );
518       m_t[i] = d1.ParameterAt( s );
519     }
520   	DestroyRuntimeCache();
521   }
522   return rc;
523 }
524 
525 
ChangeDimension(int desired_dimension)526 bool ON_PolyCurve::ChangeDimension( int desired_dimension )
527 {
528   int i, count = m_segment.Count();
529   bool rc = (count>0);
530   for ( i = 0; i < count; i++ )
531   {
532     ON_Curve* curve = m_segment[i];
533     if ( 0 != curve )
534     {
535       if ( !curve->ChangeDimension(desired_dimension) )
536         rc = false;
537     }
538     else
539       rc = false;
540   }
541   return rc;
542 }
543 
SetParameterization(const double * t)544 bool ON_PolyCurve::SetParameterization( const double* t )
545 {
546   bool rc = false;
547   int i, count = m_segment.Count()+1;
548   if ( count >= 2 && 0 != t && ON_UNSET_VALUE != t[0] )
549   {
550     for ( i = 1; i < count; i++ )
551     {
552       if ( t[i] == ON_UNSET_VALUE )
553         break;
554       if ( t[i-1] >= t[i] )
555         break;
556     }
557     if ( i == count )
558     {
559       m_t.Reserve(count);
560       m_t.SetCount(0);
561       m_t.Append( count, t );
562       rc = true;
563     }
564   }
565   return rc;
566 }
567 
ChangeClosedCurveSeam(double t)568 ON_BOOL32 ON_PolyCurve::ChangeClosedCurveSeam( double t )
569 {
570   ON_BOOL32 rc = IsClosed();
571   if ( rc )
572   {
573   	DestroyRuntimeCache();
574     rc = false;
575     const int old_count = Count();
576     const ON_Interval old_dom = Domain();
577     ON_Curve* scrv = 0;
578     if ( old_count == 1 )
579     {
580       scrv = SegmentCurve(0);
581       if ( scrv )
582       {
583         ON_Interval sdom = scrv->Domain();
584         double s = ( old_dom == sdom )
585                  ? t
586                  : sdom.ParameterAt( old_dom.NormalizedParameterAt(t) );
587         rc = scrv->ChangeClosedCurveSeam(s);
588         if ( rc )
589           SetDomain( t, t + old_dom.Length() );
590       }
591     }
592     else
593     {
594       double k = t;
595       if ( !old_dom.Includes(t) )
596       {
597         double s = old_dom.NormalizedParameterAt(t);
598         s = fmod(s,1.0);
599         if ( s < 0.0 )
600           s += 1.0;
601         k = old_dom.ParameterAt(s);
602       }
603       if ( old_dom.Includes(k,true) )
604       {
605         int segment_index = ON_NurbsSpanIndex(2,old_count+1,m_t.Array(),k,0,0);
606         scrv = m_segment[segment_index];
607         if ( k < m_t[segment_index] )
608           return false;
609         if ( k >= m_t[segment_index+1] )
610           return false;
611         int new_count = (k==m_t[segment_index]) ? old_count : old_count+1;
612         ON_Curve* sleft = 0;
613         ON_Curve* sright = 0;
614         if ( new_count > old_count )
615         {
616 					ON_Interval subdom(m_t[segment_index], m_t[segment_index+1]);
617 					double nt = subdom.NormalizedParameterAt(k);
618 					ON_Interval Segdom = scrv->Domain();
619 					double segt = Segdom.ParameterAt(nt);
620           rc = scrv->Split( segt, sleft, sright );
621 
622 //				Greg Arden 6 May 2003. Fixes TRR#10332.  If split fails we break the
623 //				curve between segments and adjust the parameterization
624 					if(!rc){
625 						if(nt>.5){
626 							segment_index++;
627 							if(segment_index<old_count)
628 								scrv = m_segment[segment_index];
629 							else
630 								scrv = NULL;
631 						}
632 						new_count--;
633 					}
634         }
635         if(new_count==old_count)
636         {
637           sright = scrv;
638           scrv = 0;
639           rc = true;
640         }
641         if ( rc && segment_index<old_count)
642         {
643           m_segment[segment_index] = 0;//todo
644           ON_SimpleArray<ON_Curve*> new_c(new_count);
645           ON_SimpleArray<double> new_t(new_count+1);
646           new_c.Append(sright);
647           new_t.Append(k);
648           new_c.Append( old_count-segment_index-1, m_segment.Array()+segment_index+1);
649           new_t.Append( old_count-segment_index-1, m_t.Array()+segment_index+1);
650           int j = new_t.Count();
651           new_c.Append( segment_index, m_segment.Array() );
652           new_t.Append( segment_index, m_t.Array() );
653           if ( sleft )
654           {
655             new_c.Append(sleft);
656             new_t.Append(m_t[segment_index]);
657           }
658           new_t.Append(k);
659           double d = old_dom.Length();
660           while (j < new_t.Count() )
661           {
662             new_t[j] += d;
663             j++;
664           }
665 
666           // take care when moving new_c pointers to m_segment
667           // so that we don't delete any curves.
668           memset( m_segment.Array(), 0, m_segment.Capacity()*sizeof( *m_segment.Array() ) );
669           m_segment.SetCount(0);
670           m_segment.Append( new_c.Count(), new_c.Array() );
671           m_t = new_t;
672           if ( scrv )
673           {
674             delete scrv;
675             scrv = 0;
676           }
677         }
678       }
679       else
680       {
681         // k is already the start or  end of the existing curve
682         rc = true;
683       }
684       if ( rc )
685         SetDomain( t, t + old_dom.Length() );
686     }
687   }
688   return rc;
689 }
690 
SpanCount() const691 int ON_PolyCurve::SpanCount() const
692 {
693   int span_count = 0;
694   const int segment_count = Count();
695   int i, j;
696   for ( i = 0; i < segment_count; i++  ) {
697     if ( !m_segment[i] )
698       return false;
699     j = m_segment[i]->SpanCount();
700     if ( j == 0 )
701       return 0;
702     span_count += j;
703   }
704   return span_count;
705 }
706 
GetSpanVector(double * s) const707 ON_BOOL32 ON_PolyCurve::GetSpanVector( // span "knots"
708        double* s // array of length SpanCount() + 1
709        ) const
710 {
711   ON_Interval sp;
712   double t;
713   const int segment_count = Count();
714   int i, j, k;
715   for ( i = 0; i < segment_count; i++  ) {
716     if ( !m_segment[i] )
717       return false;
718     j = m_segment[i]->SpanCount();
719     if ( j == 0 )
720       return 0;
721     if ( !m_segment[i]->GetSpanVector( s ) )
722       return false;
723     sp.Set( m_t[i], m_t[i+1] );
724 		ON_Interval segloc(s[0],s[j]);
725     if ( sp.Min() != s[0] || sp.Max() != s[j] ) {
726       for ( k = 0; k <= j; k++ ) {
727         t = segloc.NormalizedParameterAt(s[k]);
728         s[k] = sp.ParameterAt(t);
729       }
730     }
731 
732     s += j;
733   }
734   return true;
735 }
736 
Degree() const737 int ON_PolyCurve::Degree() const
738 {
739   const int segment_count = Count();
740   int span_degree = 0;
741   int segment_index, d;
742   for ( segment_index = 0; segment_index < segment_count; segment_index++  ) {
743     if ( !m_segment[segment_index] )
744       return false;
745     d = m_segment[segment_index]->Degree();
746     if ( d <= 0 )
747       return 0;
748     if ( d > span_degree )
749       span_degree = d;
750   }
751   return span_degree;
752 }
753 
754 
755 ON_BOOL32
IsLinear(double tolerance) const756 ON_PolyCurve::IsLinear( // true if curve locus is a line segment
757       double tolerance // tolerance to use when checking linearity
758       ) const
759 {
760   ON_BOOL32 rc = false;
761   int i, count = Count();
762 	if ( count==1)
763 		return m_segment[0]->IsLinear(tolerance);
764 
765 	else if ( count > 1 ) {
766     rc = true;
767     for ( i = 0; rc && i < count; i++ ) {
768       if ( !m_segment[i] )
769         rc = false;
770       else
771         rc = m_segment[i]->IsLinear(tolerance);
772 
773     }
774     if (rc)
775       rc = ON_Curve::IsLinear(tolerance);
776   }
777   return rc;
778 }
779 
IsPolyline(ON_SimpleArray<ON_3dPoint> * pline_points,ON_SimpleArray<double> * pline_t) const780 int ON_PolyCurve::IsPolyline(
781       ON_SimpleArray<ON_3dPoint>* pline_points,
782       ON_SimpleArray<double>* pline_t
783       ) const
784 {
785   int i, seg_i, seg_rc;
786   ON_Interval sdom, cdom;
787   int rc = 0;
788   if ( pline_points )
789     pline_points->SetCount(0);
790   if ( pline_t )
791     pline_t->SetCount(0);
792   const int seg_count = Count();
793   if ( seg_count == 1 )
794   {
795     if ( m_segment[0] )
796       rc = m_segment[0]->IsPolyline(pline_points,pline_t);
797     if (pline_t && rc > 0)
798     {
799       sdom.Set(m_t[0],m_t[1]);
800       cdom = m_segment[0]->Domain();
801       if ( sdom != cdom )
802       {
803         for ( i = 0; i < pline_t->Count(); i++ )
804           (*pline_t)[i] = sdom.ParameterAt(cdom.NormalizedParameterAt((*pline_t)[i]));
805       }
806     }
807   }
808   else if (seg_count > 1 )
809   {
810     ON_SimpleArray<ON_3dPoint> seg_points;
811     ON_SimpleArray<double> seg_t;
812     for ( seg_i = 0; seg_i < seg_count; seg_i++ )
813     {
814       seg_points.SetCount(0);
815       seg_t.SetCount(0);
816       seg_rc = m_segment[seg_i]->IsPolyline(pline_points?&seg_points:0,pline_t?&seg_t:0);
817       if ( seg_rc < 2 )
818       {
819         if ( pline_points )
820           pline_points->SetCount(0);
821         if ( pline_t )
822           pline_t->SetCount(0);
823         rc = 0;
824         break;
825       }
826       rc += seg_rc;
827       if ( seg_i )
828         rc--;
829       if ( pline_t )
830       {
831         sdom.Set( m_t[seg_i], m_t[seg_i+1] );
832         cdom = m_segment[seg_i]->Domain();
833         if ( sdom != cdom )
834         {
835           for ( i = 0; i < seg_t.Count(); i++ )
836             seg_t[i] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg_t[i]));
837         }
838         if ( pline_t->Count() > 0 )
839           pline_t->Remove();
840         pline_t->Append( seg_t.Count(), seg_t.Array() );
841       }
842       if ( pline_points )
843       {
844         if ( pline_points->Count() > 0 )
845           pline_points->Remove();
846         pline_points->Append( seg_points.Count(), seg_points.Array() );
847       }
848     }
849     if(IsClosed() && pline_points && pline_points->Count() > 3)
850     {
851       // GBA 2/26/03. Make closed polylines spot on closed
852       *pline_points->Last() = *pline_points->First();
853     }
854   }
855   return rc;
856 }
857 
858 
859 ON_BOOL32
IsArc(const ON_Plane * plane,ON_Arc * arc,double tolerance) const860 ON_PolyCurve::IsArc( // true if curve locus in an arc or circle
861       const ON_Plane* plane, // if not NULL, test is performed in this plane
862       ON_Arc* arc,         // if not NULL and true is returned, then arc
863                               // arc parameters are filled in
864       double tolerance // tolerance to use when checking linearity
865       ) const
866 {
867   bool rc = false;
868   if ( 1 == m_segment.Count() && 0 != m_segment[0] )
869   {
870     rc = m_segment[0]->IsArc( plane, arc, tolerance )?true:false;
871   }
872   return rc;
873 }
874 
875 
GetTestPlane(const ON_Curve & curve,ON_Plane & plane)876 static bool GetTestPlane( const ON_Curve& curve, ON_Plane& plane )
877 {
878   int i, j;
879   ON_3dPoint P, Q, R;
880   ON_3dVector X;
881   ON_Interval d = curve.Domain();
882   if ( !curve.Ev1Der( d[0], P, X ) )
883   {
884     return false;
885   }
886   if ( !X.Unitize() )
887   {
888     return false;
889   }
890 
891   Q = P+X;
892   for ( i = 2; i <= 16; i += 2 )
893   {
894     for ( j = 1; j < i; j += 2 )
895     {
896       R = curve.PointAt( d.ParameterAt( ((double)j)/((double)i) ) );
897       if ( plane.CreateFromFrame( P, X, R-P ) )
898         return true;
899     }
900   }
901   return false;
902 }
903 
904 
905 ON_BOOL32
IsPlanar(ON_Plane * plane,double tolerance) const906 ON_PolyCurve::IsPlanar(
907       ON_Plane* plane, // if not NULL and true is returned, then plane parameters
908                          // are filled in
909       double tolerance // tolerance to use when checking linearity
910       ) const
911 {
912   if ( Dimension() == 2 )
913   {
914     return ON_Curve::IsPlanar(plane,tolerance);
915   }
916 
917   ON_BOOL32 rc = false;
918   ON_Plane test_plane;
919   const int count = Count();
920   const ON_Curve* crv = FirstSegmentCurve();
921   if ( count == 1 && crv )
922     rc = crv->IsPlanar( plane, tolerance );
923   else if ( count > 1 )
924   {
925     if ( IsLinear(tolerance) )
926     {
927       if ( plane )
928       {
929         ON_Line line(PointAtStart(), PointAtEnd() );
930         if ( !line.InPlane( *plane, tolerance ) )
931           line.InPlane( *plane, 0.0 );
932       }
933       return true;
934     }
935     if ( !GetTestPlane( *this, test_plane ) )
936     {
937       // 5 May 2006 Dale Lear
938       //   Added additiional attempts to get a test_plane
939       //   for poorly parameterized polycurves. (RR 20057 fix).
940       ON_3dPoint P, Q;
941       ON_3dVector X;
942       if (!Ev1Der(m_t[0],P,X))
943         return false;
944 
945       if ( !X.Unitize() )
946       {
947         X = PointAt(Domain().ParameterAt(0.5)) - P;
948         if ( !X.Unitize() )
949           return false;
950       }
951 
952       int i;
953       for ( i = 1; i < count; i++ )
954       {
955         if ( m_segment[i] )
956         {
957           Q = m_segment[i]->PointAt(m_segment[i]->Domain().ParameterAt(0.5));
958           if ( test_plane.CreateFromFrame(P,X,Q-P) )
959             break;
960         }
961       }
962       if ( i >= count )
963         return false;
964     }
965     rc = IsInPlane( test_plane, tolerance );
966     if (rc && plane)
967       *plane = test_plane;
968   }
969   return rc;
970 }
971 
972 ON_BOOL32
IsInPlane(const ON_Plane & plane,double tolerance) const973 ON_PolyCurve::IsInPlane(
974       const ON_Plane& plane, // plane to test
975       double tolerance // tolerance to use when checking linearity
976       ) const
977 {
978   ON_BOOL32 rc = false;
979   int i, count = Count();
980   for ( i = 0; i < count; i++ )
981   {
982     if ( !m_segment[i] )
983       return false;
984     rc = m_segment[i]->IsInPlane( plane, tolerance );
985     if ( !rc )
986       break;
987   }
988   return rc;
989 }
990 
991 ON_BOOL32
IsClosed() const992 ON_PolyCurve::IsClosed() const
993 {
994   ON_BOOL32 bIsClosed = false;
995   const int count = Count();
996   if ( count == 1 ) {
997     // evaluation test required
998     const ON_Curve* c = FirstSegmentCurve();
999     if ( c )
1000       bIsClosed = c->IsClosed();
1001   }
1002   else if ( count > 1 )
1003   {
1004     // 17 May2005 Dale Lear
1005     //  I added the FindNextGap(0) <= 0 test to
1006     //  prevent discontinuous curves from being
1007     //  classified as closed.
1008     bIsClosed = ( ON_Curve::IsClosed() && FindNextGap(0) <= 0 );
1009   }
1010   return bIsClosed;
1011 }
1012 
GetLineIsoCoordinates(const ON_Line & line,const ON_3dPoint P,ON_3dPoint & C)1013 static bool GetLineIsoCoordinates( const ON_Line& line, const ON_3dPoint P, ON_3dPoint& C )
1014 {
1015   C.x = (line.from.x == line.to.x) ? P.x : ON_UNSET_VALUE;
1016   C.y = (line.from.y == line.to.y) ? P.y : ON_UNSET_VALUE;
1017   C.z = (line.from.z == line.to.z) ? P.z : ON_UNSET_VALUE;
1018   return ( ON_3dPoint::UnsetPoint != C );
1019 }
1020 
LineLineTieBreaker(const ON_Line & line0,const ON_Line & line1,ON_3dPoint & Q0,ON_3dPoint & Q1)1021 static void LineLineTieBreaker( const ON_Line& line0, const ON_Line& line1,
1022                                 ON_3dPoint& Q0, ON_3dPoint& Q1 )
1023 {
1024   double line0_length = line0.Length();
1025   double line1_length = line1.Length();
1026 
1027   ON_3dPoint C0, C1;
1028   bool bHaveIsoCoords0 = GetLineIsoCoordinates(line0,Q0,C0);
1029   bool bHaveIsoCoords1 = GetLineIsoCoordinates(line1,Q1,C1);
1030   if ( bHaveIsoCoords0 || bHaveIsoCoords1 )
1031   {
1032     for ( int i = 0; i < 3; i++ )
1033     {
1034       double c0 = C0[i];
1035       double c1 = C1[i];
1036       if ( ON_UNSET_VALUE == c0 && ON_UNSET_VALUE == c1 )
1037         continue;
1038       double c = ON_UNSET_VALUE;
1039       if ( c0 == c1 )
1040         c = c0;
1041       else if ( ON_UNSET_VALUE == c0 )
1042         c = c1;
1043       else if ( ON_UNSET_VALUE == c1 )
1044         c = c0;
1045       else if ( line0_length > line1_length )
1046         c = c0;
1047       else
1048         c = c1;
1049       if ( ON_UNSET_VALUE != c && ON_IsValid(c) )
1050       {
1051         Q0[i] = c;
1052         Q1[i] = c;
1053       }
1054     }
1055   }
1056 }
1057 
SetLineIsoCoords(const ON_Line & line,const ON_3dPoint & P,ON_3dPoint & Q)1058 static void SetLineIsoCoords( const ON_Line& line, const ON_3dPoint& P, ON_3dPoint& Q )
1059 {
1060   ON_3dPoint C;
1061   if ( GetLineIsoCoordinates(line,P,C) )
1062   {
1063     if ( ON_UNSET_VALUE != C.x && ON_IsValid(C.x) )
1064       Q.x = P.x;
1065     if ( ON_UNSET_VALUE != C.y && ON_IsValid(C.y) )
1066       Q.y = P.y;
1067     if ( ON_UNSET_VALUE != C.z && ON_IsValid(C.z) )
1068       Q.z = P.z;
1069   }
1070 }
1071 
ChangeArcEnd(const ON_ArcCurve * arc,ON_3dPoint P,ON_3dPoint Q,int end_index)1072 static ON_NurbsCurve* ChangeArcEnd( const ON_ArcCurve* arc, ON_3dPoint P, ON_3dPoint Q, int end_index )
1073 {
1074   if ( P == Q )
1075     return 0;
1076 
1077   ON_NurbsCurve* nc = arc->NurbsCurve();
1078   if ( 0 == nc || nc->m_cv_count < 3 )
1079     return 0;
1080 
1081   int cv0_dex, cv1_dex;
1082   if ( 1 == end_index )
1083   {
1084     cv0_dex = nc->m_cv_count-1;
1085     cv1_dex = cv0_dex - 1;
1086   }
1087   else
1088   {
1089     cv0_dex = 0;
1090     cv1_dex = cv0_dex + 1;
1091   }
1092 
1093   if ( !nc->SetCV(cv0_dex,Q) )
1094   {
1095     delete nc;
1096     return 0;
1097   }
1098 
1099   ON_4dPoint R;
1100   if ( !nc->GetCV(cv1_dex,R) )
1101   {
1102     delete nc;
1103     return 0;
1104   }
1105 
1106   R.x += (Q.x-P.x)*R.w;
1107   R.y += (Q.y-P.y)*R.w;
1108   R.z += (Q.z-P.z)*R.w;
1109   nc->SetCV(cv1_dex,R);
1110 
1111   return nc;
1112 }
1113 
CloseGap(int gap_index,int)1114 bool ON_PolyCurve::CloseGap( int gap_index, int )
1115 {
1116   const int count = m_segment.Count();
1117 
1118   if ( gap_index <= 0 || gap_index >= count )
1119   {
1120     ON_ERROR("Invalid gap_index parameter.");
1121     return 0; // nothing to do
1122   }
1123 
1124   ON_Curve* c0 = m_segment[gap_index-1];
1125   ON_Curve* c1 = m_segment[gap_index];
1126   if ( 0 == c0 || 0 == c1 )
1127   {
1128     ON_ERROR("Null curve segments.");
1129     return false; // invalid polycurve
1130   }
1131 
1132   const ON_3dPoint P0 = c0->PointAtEnd();
1133   const ON_3dPoint P1 = c1->PointAtStart();
1134   if ( P0 == P1 )
1135     return false; // nothing to do
1136 
1137   ON_3dPoint Q0(P0);
1138   ON_3dPoint Q1(P1);
1139 
1140   const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0);
1141   const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1);
1142 
1143   if ( 0 != arc0 && 0 != arc1 )
1144   {
1145     if ( arc1->m_arc.Length() < arc0->m_arc.Length() )
1146       Q1 = P0;
1147     else
1148       Q0 = P1;
1149   }
1150   else if ( 0 != arc0 && 0 == arc1 )
1151   {
1152     Q1 = P0;
1153   }
1154   else if ( 0 != arc1 && 0 == arc0 )
1155   {
1156     Q0 = P1;
1157   }
1158   else
1159   {
1160     ON_Line line0, line1;
1161     double min_line_length = 0.0;
1162     double is_linear_tolerance = 0.0;
1163     bool bLine0 = (0 == arc0)
1164                 ? c0->LastSpanIsLinear(min_line_length,is_linear_tolerance,&line0)
1165                 : false;
1166     bool bLine1 = (0 == arc0)
1167                 ? c1->FirstSpanIsLinear(min_line_length,is_linear_tolerance,&line1)
1168                 : false;
1169     if ( bLine0 && bLine1 )
1170       LineLineTieBreaker(line0,line1,Q0,Q1);
1171     else if ( bLine0 )
1172       SetLineIsoCoords(line0,P0,Q1);
1173     else if ( bLine1 )
1174       SetLineIsoCoords(line1,P1,Q0);
1175   }
1176 
1177   if ( Q0.x != Q1.x )
1178     Q0.x = Q1.x = 0.5*(P0.x + P1.x);
1179   if ( Q0.y != Q1.y )
1180     Q0.y = Q1.y = 0.5*(P0.y + P1.y);
1181   if ( Q0.z != Q1.z )
1182     Q0.z = Q1.z = 0.5*(P0.z + P1.z);
1183 
1184   if ( Q0 != P0 )
1185   {
1186     if ( 0 != arc0 )
1187     {
1188       ON_NurbsCurve* nc0 = ChangeArcEnd( arc0, P0, Q0 , 1 );
1189       if ( nc0 )
1190       {
1191         delete m_segment[gap_index-1];
1192         m_segment[gap_index-1] = nc0;
1193         c0 = nc0;
1194         arc0 = 0;
1195       }
1196     }
1197     else
1198     {
1199       c0->SetEndPoint(Q0);
1200     }
1201   }
1202 
1203   if ( Q1 != P1 )
1204   {
1205     if ( 0 != arc1 )
1206     {
1207       ON_NurbsCurve* nc1 = ChangeArcEnd( arc1, P1, Q1, 0 );
1208       if ( nc1 )
1209       {
1210         delete m_segment[gap_index];
1211         m_segment[gap_index] = nc1;
1212         c0 = nc1;
1213         arc1 = 0;
1214       }
1215     }
1216     else
1217     {
1218       c1->SetStartPoint(Q1);
1219     }
1220   }
1221 
1222   return HasGapAt(gap_index-1) ? false : true;
1223 }
1224 
CloseGaps()1225 int ON_PolyCurve::CloseGaps()
1226 {
1227   int rc = 0;
1228   int segment_index0 = 0;
1229   int gap_index = 0;
1230 
1231   for(;;)
1232   {
1233     gap_index = FindNextGap(segment_index0);
1234     if ( gap_index <= segment_index0 || gap_index >= m_segment.Count() )
1235       break;
1236     if ( CloseGap(gap_index,0) )
1237       rc++;
1238     segment_index0 = gap_index;
1239   }
1240 
1241   return rc;
1242 }
1243 
HasGap() const1244 int ON_PolyCurve::HasGap() const
1245 {
1246   return FindNextGap(0);
1247 }
1248 
1249 
HasGapAt(int segment_index) const1250 bool ON_PolyCurve::HasGapAt(int segment_index) const
1251 {
1252   const int count = m_segment.Count();
1253 
1254   if ( segment_index < 0 || segment_index >= count-1 )
1255     return 0;
1256 
1257   const ON_Curve* c0 = m_segment[segment_index];
1258   const ON_Curve* c1 = m_segment[segment_index+1];
1259   if ( 0 == c0 || 0 == c1 )
1260     return false;
1261 
1262   ON_3dPoint P0 = c0->PointAtEnd();
1263   ON_3dPoint P1 = c1->PointAtStart();
1264   // Note:  The point compare test should be the same
1265   //        as the one used in ON_Curve::IsClosed().
1266   if ( false == ON_PointsAreCoincident( 3, false, &P0.x, &P1.x ) )
1267   {
1268     // To fix RR 13325 I allow a little more leeway for arcs.
1269     const ON_ArcCurve* arc0 = ON_ArcCurve::Cast(c0);
1270     const ON_ArcCurve* arc1 = ON_ArcCurve::Cast(c1);
1271     if ( 0 == arc0 && 0 == arc1 )
1272       return true; // gap
1273 
1274     double tol = ON_ZERO_TOLERANCE;
1275     const double tol0 = arc0  ? ( arc0->m_arc.radius*arc0->m_arc.AngleRadians()*1.0e-10 ) : 0.0;
1276     const double tol1 = arc1  ? ( arc1->m_arc.radius*arc1->m_arc.AngleRadians()*1.0e-10 ) : 0.0;
1277     if ( tol < tol0 )
1278       tol = tol0;
1279     if ( tol < tol1 )
1280       tol = tol1;
1281     const double d = P0.DistanceTo(P1);
1282     if ( d > tol )
1283     {
1284       return true; // gap
1285     }
1286   }
1287 
1288   return false; // no gap
1289 }
1290 
1291 
FindNextGap(int segment_index0) const1292 int ON_PolyCurve::FindNextGap(int segment_index0) const
1293 {
1294   if ( segment_index0 >= 0 )
1295   {
1296     const int count = m_segment.Count();
1297     for (int gap_index = segment_index0+1; gap_index < count; gap_index++ )
1298     {
1299       if ( HasGapAt(gap_index-1) )
1300         return gap_index;
1301     }
1302   }
1303   return 0;
1304 }
1305 
1306 
1307 ON_BOOL32
IsPeriodic() const1308 ON_PolyCurve::IsPeriodic() const
1309 {
1310   ON_BOOL32 bIsPeriodic = false;
1311   if ( Count() == 1 ) {
1312     const ON_Curve* c = FirstSegmentCurve();
1313     if ( c )
1314       bIsPeriodic = c->IsPeriodic();
1315   }
1316   return bIsPeriodic;
1317 }
1318 
GetNextDiscontinuity(ON::continuity c,double t0,double t1,double * t,int * hint,int * dtype,double cos_angle_tolerance,double curvature_tolerance) const1319 bool ON_PolyCurve::GetNextDiscontinuity(
1320         ON::continuity c,
1321         double t0,
1322         double t1,
1323         double* t,
1324         int* hint,
1325         int* dtype,
1326         double cos_angle_tolerance,
1327         double curvature_tolerance
1328         ) const
1329 {
1330   ON_3dPoint Pm, Pp;
1331   ON_3dVector D1m, D1p, D2m, D2p, Tm, Tp, Km, Kp;
1332   double s0, s1, s;
1333   bool rc = false;
1334   ON_Interval sdom, cdom;
1335   const int count = Count();
1336   int segment_hint=0, curve_hint=0;
1337   if ( dtype )
1338     *dtype = 0;
1339   if ( count > 0 && t0 != t1 )
1340   {
1341     // 20 March 2003 Dale Lear:
1342     //     look for parametric discontinuities on the interior.
1343     //     If we don't find any, then well check for locus
1344     //     discontinuities at the appropriate end
1345     ON::continuity input_c = c;
1346     c = ON::ParametricContinuity(c);
1347 
1348     segment_hint = (hint) ? (*hint & 0x3FFF) : 0;
1349     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,(t0>t1)?-1:1,segment_hint);
1350     curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0;
1351 
1352 
1353     {
1354       // 20 March 2003 Dale Lear:
1355       //     If t0 is very near interior m_t[] value, see if it
1356       //     should be set to that value.  A bit or two of
1357       //     precision sometimes gets lost in proxy
1358       //     domain to real curve domain conversions on the interior
1359       //     of a curve domain.
1360       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;
1361       if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol )
1362       {
1363         if ( t0 < t1 )
1364         {
1365           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 )
1366           {
1367             t0 = m_t[segment_index+1];
1368             segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t0,1,segment_hint);
1369           }
1370         }
1371         else
1372         {
1373           if ( t0 > m_t[segment_index] && t1 < m_t[segment_index] && fabs(t0-m_t[segment_index]) <= segtol && segment_index > 0 )
1374           {
1375             t0 = m_t[segment_index];
1376           }
1377         }
1378       }
1379     }
1380 
1381     double tmin, tmax;
1382     int segment_index_delta;
1383     if (t0 > t1)
1384     {
1385       segment_index_delta = -1;
1386       tmin = t1;
1387       tmax = t0;
1388     }
1389     else
1390     {
1391       segment_index_delta = 1;
1392       tmin = t0;
1393       tmax = t1;
1394     }
1395 
1396     const ON_Curve* crv;
1397     for ( /*empty*/;
1398               segment_index >= 0
1399            && segment_index < count
1400            && tmin < m_t[segment_index+1] && m_t[segment_index] < tmax;
1401           segment_index += segment_index_delta )
1402     {
1403       crv = m_segment[segment_index];
1404       if ( !crv )
1405         break;
1406       cdom = crv->Domain();
1407       sdom.Set( m_t[segment_index], m_t[segment_index+1] );
1408       if ( sdom == cdom )
1409       {
1410         s0 = t0;
1411         s1 = t1;
1412       }
1413       else
1414       {
1415         s0 = cdom.ParameterAt( sdom.NormalizedParameterAt(t0) );
1416         s1 = cdom.ParameterAt( sdom.NormalizedParameterAt(t1) );
1417       }
1418       rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance );
1419       if ( rc )
1420       {
1421         double kink_t;
1422         if ( sdom == cdom )
1423         {
1424           kink_t = s;
1425         }
1426         else
1427         {
1428           kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) );
1429           double t_tol = (fabs(t0)+fabs(t1)+fabs(t0-t1))*ON_ZERO_TOLERANCE;
1430           if ( kink_t <= tmin+t_tol || kink_t >= tmax-t_tol)
1431           {
1432             // 24 January 2002 Dale Lear -
1433             // It is possible that lost precision in the
1434             // domain conversion is giving us trouble.
1435             // In particular, if this code is not here,
1436             // "t0" is right at a kink, and s0 gets bumped
1437             // down a little bit due to rounding/truncation,
1438             // we end up finding the kink at "t0" that we were
1439             // supposed to skip.
1440             double e = fabs(sdom.Length()/cdom.Length());
1441             if ( e < 1.0 ) e = 1.0; else if (e > 1000.0) e = 1000.0;
1442             double s_tol = (fabs(s0)+fabs(s1)+fabs(s0-s1))*ON_ZERO_TOLERANCE*e;
1443             if ( kink_t <= tmin+t_tol )
1444             {
1445               if( s0>s1 )
1446                 s1 = s1 + s_tol;
1447               else
1448                 s0 = s0 + s_tol;
1449             }
1450             if ( kink_t >= tmax-t_tol )
1451             {
1452               if ( s0>s1 )
1453                 s0 = s0 - s_tol;
1454               else
1455                 s1 = s1 - s_tol;
1456             }
1457             rc = crv->GetNextDiscontinuity( c, s0, s1, &s, &curve_hint, dtype, cos_angle_tolerance, curvature_tolerance );
1458             if (rc)
1459             {
1460               kink_t = sdom.ParameterAt( cdom.NormalizedParameterAt(s) );
1461               if ( kink_t <= tmin || kink_t >= tmax )
1462                 rc = false;
1463             }
1464           }
1465         }
1466 
1467         if (rc)
1468         {
1469           if ( t )
1470           {
1471             *t = kink_t;
1472             if ( hint )
1473             {
1474               *hint = segment_index | (curve_hint<<14);
1475             }
1476           }
1477           break;
1478         }
1479       }
1480 
1481 
1482       // check for discontinity between curve segments
1483       int next_segment_index = segment_index+segment_index_delta;
1484       if ( next_segment_index < 0 || next_segment_index >= count )
1485       {
1486         // no more curve segments in search interval
1487         break;
1488       }
1489       const ON_Curve* crv1 = m_segment[next_segment_index];
1490       if ( !crv1 )
1491         break;
1492 
1493       if ( t0 > t1 )
1494       {
1495         if ( sdom[0] <= t1 ) // this line is correct - search is decreasing towards t1
1496         {
1497           // INTERIOR of search interval does not include
1498           // start this crv = end of next curve
1499           break;
1500         }
1501       }
1502       else
1503       {
1504         if ( t1 <= sdom[1] )
1505         {
1506           // INTERIOR of search interval does not include
1507           // end of this crv = start of next curve
1508           break;
1509         }
1510       }
1511 
1512       double crv0_t, crv1_t;
1513       int crv0_side;
1514       if ( t0 > t1 )
1515       {
1516         // compare start if this curve against end of next curve
1517         crv0_t = cdom[0];
1518         crv1_t = crv1->Domain()[1];
1519         crv0_side = 1;
1520       }
1521       else
1522       {
1523         // compare end if this curve against start of next curve
1524         crv0_t = cdom[1];
1525         crv1_t = crv1->Domain()[0];
1526         crv0_side = -1;
1527       }
1528 
1529       switch( c )
1530       {
1531       case ON::C1_continuous:
1532       case ON::G1_continuous:
1533         crv->Ev1Der( crv0_t, Pm, D1m, crv0_side );   // point on this curve
1534         crv1->Ev1Der( crv1_t, Pp, D1p, -crv0_side ); // corresponding point on next curve
1535         if ( c == ON::C1_continuous )
1536         {
1537           if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) )
1538             rc = true;
1539         }
1540         else
1541         {
1542           Tm = D1m;
1543           Tp = D1p;
1544           Tm.Unitize();
1545           Tp.Unitize();
1546           if ( Tm*Tp < cos_angle_tolerance )
1547             rc = true;
1548         }
1549         if ( rc && dtype )
1550           *dtype = 1;
1551         break;
1552 
1553       case ON::C2_continuous:
1554       case ON::G2_continuous:
1555       case ON::Gsmooth_continuous:
1556         crv->Ev2Der( crv0_t, Pm, D1m, D2m, crv0_side );   // point on this curve
1557         crv1->Ev2Der( crv1_t, Pp, D1p, D2p, -crv0_side ); // corresponding point on next curve
1558         if ( c == ON::C2_continuous )
1559         {
1560           if ( !(D1m-D1p).IsTiny(D1m.MaximumCoordinate()*ON_SQRT_EPSILON) )
1561           {
1562             rc = true;
1563             if ( dtype )
1564               *dtype = 1;
1565           }
1566           else if ( !(D2m-D2p).IsTiny(D2m.MaximumCoordinate()*ON_SQRT_EPSILON) )
1567           {
1568             rc = true;
1569             if ( dtype )
1570               *dtype = 2;
1571           }
1572         }
1573         else
1574         {
1575           ON_EvCurvature( D1m, D2m, Tm, Km );
1576           ON_EvCurvature( D1p, D2p, Tp, Kp );
1577           if ( Tm*Tp < cos_angle_tolerance )
1578           {
1579             rc = true;
1580             if ( dtype )
1581               *dtype = 1;
1582           }
1583           else
1584           {
1585             bool bIsCurvatureContinuous = ( ON::Gsmooth_continuous == c )
1586               ? ON_IsGsmoothCurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance)
1587               : ON_IsG2CurvatureContinuous(Km, Kp, cos_angle_tolerance, curvature_tolerance);
1588             if ( !bIsCurvatureContinuous )
1589             {
1590               // NOTE:
1591               //   The test to enter this scope must exactly match
1592               //   the one used in ON_NurbsCurve::GetNextDiscontinuity().
1593               rc = true;
1594               if ( dtype )
1595                 *dtype = 2;
1596             }
1597             else if ( ON::Gsmooth_continuous == c )
1598             {
1599               const double is_linear_tolerance = 1.0e-8;
1600               const double is_linear_min_length = 1.0e-8;
1601               const ON_Curve* seg0;
1602               const ON_Curve* seg1;
1603               if (crv0_side<0)
1604               {
1605                 seg0 = crv;
1606                 seg1 = crv1;
1607               }
1608               else
1609               {
1610                 seg0 = crv1;
1611                 seg1 = crv;
1612               }
1613               bool b0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance);
1614               bool b1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance);
1615               if ( b0 != b1 )
1616               {
1617                 rc = true;
1618                 if ( dtype )
1619                   *dtype = 3;
1620               }
1621             }
1622           }
1623         }
1624         break;
1625       default:
1626         // intentionally ignoring other ON::continuity enum values
1627         break;
1628       }
1629       if (rc)
1630       {
1631         int tindex = (t0>t1)?segment_index:(segment_index+1);
1632         if ( t )
1633           *t = m_t[tindex];
1634         if ( hint )
1635         {
1636           *hint = tindex;
1637         }
1638         break;
1639       }
1640     }
1641 
1642     if ( !rc && input_c != c )
1643     {
1644       // 20 March 2003 Dale Lear
1645       //   See if we need to do a locus check at an end
1646       rc = ON_Curve::GetNextDiscontinuity( input_c,
1647                         t0, t1, t, NULL,
1648                         dtype,
1649                         cos_angle_tolerance, curvature_tolerance );
1650     }
1651   }
1652   return rc;
1653 }
1654 
IsContinuous(ON::continuity desired_continuity,double t,int * hint,double point_tolerance,double d1_tolerance,double d2_tolerance,double cos_angle_tolerance,double curvature_tolerance) const1655 bool ON_PolyCurve::IsContinuous(
1656     ON::continuity desired_continuity,
1657     double t,
1658     int* hint, // default = NULL,
1659     double point_tolerance, // default=ON_ZERO_TOLERANCE
1660     double d1_tolerance, // default==ON_ZERO_TOLERANCE
1661     double d2_tolerance, // default==ON_ZERO_TOLERANCE
1662     double cos_angle_tolerance, // default==ON_DEFAULT_ANGLE_TOLERANCE_COSINE
1663     double curvature_tolerance  // default==ON_SQRT_EPSILON
1664     ) const
1665 {
1666   bool rc = true;
1667   const int count = Count();
1668   if ( count > 0 )
1669   {
1670     if ( t <= m_t[0] || t >= m_t[count] )
1671     {
1672       // 20 March 2003 Dale Lear
1673       //     Consistently handles locus case and out of domain case.
1674       rc = ON_Curve::IsContinuous(
1675                  desired_continuity, t, hint,
1676                  point_tolerance,
1677                  d1_tolerance, d2_tolerance,
1678                  cos_angle_tolerance,
1679                  curvature_tolerance );
1680       return rc;
1681     }
1682 
1683     // "locus" and "parametric" are the same at this point.
1684     desired_continuity = ON::ParametricContinuity(desired_continuity);
1685 
1686 
1687     int segment_hint = 0, curve_hint = 0;
1688     if ( hint )
1689       segment_hint = (*hint & 0x3FFF);
1690     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint);
1691 
1692     {
1693       // 20 March 2003 Dale Lear:
1694       //     If t is very near interior m_t[] value, see if it
1695       //     should be set to that value.  A bit or two of
1696       //     precision sometimes gets lost in proxy
1697       //     domain to real curve domain conversions on the interior
1698       //     of a curve domain.
1699       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;
1700       if ( m_t[segment_index]+segtol < m_t[segment_index+1]-segtol )
1701       {
1702         if ( fabs(t-m_t[segment_index]) <= segtol && segment_index > 0 )
1703         {
1704           t = m_t[segment_index];
1705         }
1706         else if ( fabs(t-m_t[segment_index+1]) <= segtol && segment_index+1 < count )
1707         {
1708           t = m_t[segment_index+1];
1709           segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,1,segment_hint);
1710         }
1711       }
1712     }
1713 
1714     if ( hint )
1715     {
1716       if ( segment_hint == segment_index )
1717         curve_hint = ((*hint)>>14);
1718       else
1719       {
1720         segment_hint = segment_index;
1721         *hint = segment_hint;
1722       }
1723     }
1724 
1725     if ( m_t[segment_index] < t && t < m_t[segment_index+1] )
1726     {
1727       // test interior of this segment
1728       const ON_Curve* segment_curve = SegmentCurve(segment_index);
1729       if ( segment_curve )
1730       {
1731         ON_Interval sdom, cdom;
1732         cdom = segment_curve->Domain();
1733         sdom.Set( m_t[segment_index], m_t[segment_index+1] );
1734         if ( sdom != cdom )
1735           t = cdom.ParameterAt( sdom.NormalizedParameterAt(t) );
1736         rc = segment_curve->IsContinuous( desired_continuity, t, &curve_hint,
1737                                           point_tolerance, d1_tolerance, d2_tolerance,
1738                                           cos_angle_tolerance, curvature_tolerance );
1739         if ( hint )
1740           *hint = (segment_index | (curve_hint<<14));
1741       }
1742     }
1743     else if ( count > 0 )
1744     {
1745       if ( segment_index == 0 && t == m_t[0] )
1746         rc = true; // t at start of domain
1747       else if ( segment_index == count-1 && t == m_t[count] )
1748         rc = true; // t and end of domain
1749       else
1750       {
1751         // evaluate ends of segments
1752         rc = ON_Curve::IsContinuous( desired_continuity, t, hint,
1753                            point_tolerance, d1_tolerance, d2_tolerance,
1754                            cos_angle_tolerance, curvature_tolerance );
1755         if ( 0 != rc
1756              && ON::Gsmooth_continuous == desired_continuity
1757              && segment_index >= 0
1758              && segment_index < count
1759            )
1760         {
1761           // check for linear to non-linear transition
1762           const int i0 = ( t >= m_t[segment_index] ) ? segment_index-1 : segment_index;
1763           if ( i0 >= 0 && t == m_t[i0+1] )
1764           {
1765             const ON_Curve* seg0 = SegmentCurve(i0);
1766             const ON_Curve* seg1 = SegmentCurve(i0+1);
1767             if ( 0 != seg0 && 0 != seg1 )
1768             {
1769               const double is_linear_tolerance = 1.0e-8;
1770               const double is_linear_min_length = 1.0e-8;
1771               bool b0 = seg0->LastSpanIsLinear(is_linear_min_length,is_linear_tolerance);
1772               bool b1 = seg1->FirstSpanIsLinear(is_linear_min_length,is_linear_tolerance);
1773               if ( b0 != b1 )
1774                 rc = false;
1775             }
1776           }
1777         }
1778       }
1779     }
1780   }
1781   return rc;
1782 }
1783 
1784 ON_BOOL32
Reverse()1785 ON_PolyCurve::Reverse()
1786 {
1787   const int count = Count();
1788   int i;
1789   ON_BOOL32 rc = (count>0) ? true : false;
1790   if ( rc ) {
1791     m_segment.Reverse();
1792     m_t.Reverse();
1793     for ( i = 0; i < count; i++ ) {
1794       m_segment[i]->Reverse();
1795       m_t[i] = -m_t[i];
1796     }
1797     m_t[count] = -m_t[count];
1798   }
1799 	DestroyCurveTree();
1800   return rc;
1801 }
1802 
ON_TuneupEvaluationParameter(int side,double s0,double s1,double * s)1803 bool ON_TuneupEvaluationParameter(
1804    int side,
1805    double s0, double s1, // segment domain
1806    double *s             // segment parameter
1807    )
1808 {
1809   double t = *s;
1810   if ( 0 != side && s0 < t && t < s1 )
1811   {
1812     // 9 November 2010 Dale Lear
1813     //   I wrote this function today and chose
1814     //   1.0e-10 as the "noise" factor.  1.0e-10
1815     //   may need to be adjusted but it should
1816     //   not be larger unless there is a very
1817     //   good reason.  You must document any changes
1818     //   and include a bug track number so subsequent
1819     //   changes can be tested.  Any value used to
1820     //   replace 1.0e-10 must be strictly smaller
1821     //   than ON_SQRT_EPSILON because some solvers
1822     //   use (s1-s0)*ON_SQRT_EPSILON as a minimum step
1823     //   size.
1824     double ds = (s1-s0)*1.0e-10;
1825     if ( side < 0 )
1826     {
1827       if ( t <= s0+ds )
1828       {
1829         *s = s0;
1830         return true;
1831       }
1832     }
1833     else // side > 0
1834     {
1835       if ( t >= s1-ds )
1836       {
1837         *s = s1;
1838         return true;
1839       }
1840     }
1841   }
1842   return false;
1843 }
1844 
1845 
Evaluate(double t,int der_count,int v_stride,double * v,int side,int * hint) const1846 ON_BOOL32 ON_PolyCurve::Evaluate( // returns false if unable to evaluate
1847        double t,       // evaluation parameter
1848        int der_count,  // number of derivatives (>=0)
1849        int v_stride,   // v[] array stride (>=Dimension())
1850        double* v,      // v[] array of length stride*(ndir+1)
1851        int side,       // optional - determines which side to evaluate from
1852                        //         0 = default
1853                        //      <  0 to evaluate from below,
1854                        //      >  0 to evaluate from above
1855        int* hint       // optional - evaluation hint (int) used to speed
1856                        //            repeated evaluations
1857        ) const
1858 {
1859   ON_BOOL32 rc = false;
1860   const int count = Count();
1861   const int dim = Dimension();
1862   int segment_hint, curve_hint;
1863   if ( count > 0 && dim > 0 && dim <= v_stride )
1864   {
1865     segment_hint = (hint) ? (*hint & 0x3FFF) : 0;
1866     int segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_hint);
1867     if ( -2 == side || 2 == side )
1868     {
1869       // 9 November 2010 Dale Lear - ON_TuneupEvaluationParameter fix
1870       //   When evluation passes through ON_CurveProxy or ON_PolyCurve reparamterization
1871       //   and the original side parameter was -1 or +1, it is changed to -2 or +2
1872       //   to indicate that if t is numerically closed to an end paramter, then
1873       //   it should be tuned up to be at the end paramter.
1874       double a = t;
1875       if ( ON_TuneupEvaluationParameter( side, m_t[segment_index], m_t[segment_index+1], &a) )
1876       {
1877         // recalculate segment index
1878         t = a;
1879         segment_index = ON_NurbsSpanIndex(2,count+1,m_t,t,side,segment_index);
1880       }
1881     }
1882     const ON_Curve* c = m_segment[segment_index];
1883     if ( c ) {
1884       double s0, s1;
1885       {
1886         ON_Interval dom = c->Domain();
1887         s0 = dom.Min();
1888         s1 = dom.Max();
1889       }
1890       if ( s0 != s1 )
1891       {
1892         const double t0 = m_t[segment_index];
1893         const double t1 = m_t[segment_index+1];
1894         double s;
1895         if ( s0 == t0 && s1 == t1 )
1896         {
1897           // segment domain = c->Domain()
1898           s = t;
1899         }
1900         else
1901         {
1902           // adjust segment domain parameter
1903           if ( fabs(t1 - t0) < (ON_ZERO_TOLERANCE + ON_EPSILON*fabs(t0)) )
1904           {
1905             // segment domain is insanely short
1906             s = (fabs(t-t0) < fabs(t-t1)) ? s0 : s1;
1907           }
1908           else
1909           {
1910             // 30 May 2012 Dale Lear bug # 105974
1911             //   The arithmetic below was setting b = 0 and a = 0.9999999999999...
1912             //   so I added the checking for 0 and 1 stuff.
1913             const double d = t1-t0;
1914             double a = (t - t0)/d;
1915             double b = (t1 - t)/d;
1916             if ( 0.0 == b )
1917               a = 1.0;
1918             else if ( 1.0 == b )
1919               a = 0.0;
1920             else if ( 0.0 == a )
1921               b = 1.0;
1922             else if ( 1.0 == a )
1923               b = 0.0;
1924             s = b*s0 + a*s1;
1925           }
1926           if ( -1 == side )
1927             side = -2;
1928           else if ( 1 == side )
1929             side = 2;
1930         }
1931         curve_hint = ( hint && segment_hint == segment_index ) ? ((*hint)>>14) : 0;
1932         rc = c->Evaluate(
1933            s,
1934            der_count,
1935            v_stride, v,
1936            side,
1937            &curve_hint );
1938 
1939         if ( rc )
1940         {
1941           if ( der_count > 0 && s1 - s0 != t1 - t0 && t0 != t1 )
1942           {
1943             // Adjust segment derivative evaluation bug by applying chain rule
1944             // to get polycurve derivative value.
1945             const double d = (s1-s0)/(t1-t0);
1946             s = d;
1947             int di, vi;
1948             v += v_stride;
1949             for ( di = 1; di <= der_count; di++ )
1950             {
1951               for ( vi = 0; vi < dim; vi++ )
1952               {
1953                 v[vi] = s*v[vi];
1954               }
1955               s *= d;
1956               v += v_stride;
1957             }
1958           }
1959 
1960           if ( hint )
1961             *hint = segment_index | (curve_hint<<14);
1962         }
1963 
1964       }
1965     }
1966   }
1967   return rc;
1968 }
1969 
1970 int
Count() const1971 ON_PolyCurve::Count() const
1972 {
1973   return m_segment.Count();
1974 }
1975 
1976 
1977 ON_Curve*
operator [](int segment_index) const1978 ON_PolyCurve::operator[](int segment_index) const
1979 {
1980   return SegmentCurve(segment_index);
1981 }
1982 
1983 ON_Curve*
SegmentCurve(int segment_index) const1984 ON_PolyCurve::SegmentCurve(int segment_index) const
1985 {
1986   return ( segment_index >= 0 && segment_index < Count() )
1987          ? m_segment[segment_index]
1988          : NULL;
1989 }
1990 
1991 
SegmentCurveParameter(double polycurve_parameter) const1992 double ON_PolyCurve::SegmentCurveParameter(
1993   double polycurve_parameter
1994   ) const
1995 {
1996   int segment_index = SegmentIndex( polycurve_parameter );
1997   const ON_Curve* segment_curve = SegmentCurve(segment_index);
1998   if ( !segment_curve )
1999     return ON_UNSET_VALUE;
2000   ON_Interval cdom = segment_curve->Domain();
2001   ON_Interval sdom = SegmentDomain(segment_index);
2002   if ( cdom == sdom )
2003     return polycurve_parameter;
2004   double s = sdom.NormalizedParameterAt(polycurve_parameter);
2005   return cdom.ParameterAt(s);
2006 }
2007 
2008 
PolyCurveParameter(int segment_index,double segmentcurve_parameter) const2009 double ON_PolyCurve::PolyCurveParameter(
2010   int segment_index,
2011   double segmentcurve_parameter
2012   ) const
2013 {
2014   const ON_Curve* segment_curve = SegmentCurve(segment_index);
2015   if ( !segment_curve )
2016     return ON_UNSET_VALUE;
2017   ON_Interval cdom = segment_curve->Domain();
2018   ON_Interval sdom = SegmentDomain(segment_index);
2019   if ( cdom == sdom )
2020     return segmentcurve_parameter;
2021   double s = cdom.NormalizedParameterAt(segmentcurve_parameter);
2022   return sdom.ParameterAt(s);
2023 }
2024 
2025 
2026 ON_Interval
SegmentDomain(int segment_index) const2027 ON_PolyCurve::SegmentDomain( int segment_index ) const
2028 {
2029   ON_Interval domain;
2030   if ( segment_index >= 0 && segment_index < Count() ) {
2031     domain.m_t[0] = m_t[segment_index];
2032     domain.m_t[1] = m_t[segment_index+1];
2033   }
2034   return domain;
2035 }
2036 
2037 
2038 ON_Curve*
FirstSegmentCurve() const2039 ON_PolyCurve::FirstSegmentCurve() const
2040 {
2041   return SegmentCurve(0);
2042 }
2043 
2044 ON_Curve*
LastSegmentCurve() const2045 ON_PolyCurve::LastSegmentCurve() const
2046 {
2047   return SegmentCurve(Count()-1);
2048 }
2049 
2050 void
Reserve(int capacity)2051 ON_PolyCurve::Reserve( int capacity )
2052 {
2053   m_segment.Reserve(capacity);
2054   m_t.Reserve(capacity+1);
2055 }
2056 
Prepend(ON_Curve * c)2057 ON_BOOL32 ON_PolyCurve::Prepend( ON_Curve* c )
2058 {
2059 	DestroyCurveTree();
2060   return Insert( 0, c );
2061 }
2062 
Append(ON_Curve * c)2063 ON_BOOL32 ON_PolyCurve::Append( ON_Curve* c )
2064 {
2065 	DestroyCurveTree();
2066   return Insert( Count(), c );
2067 }
2068 
PrependAndMatch(ON_Curve * c)2069 ON_BOOL32 ON_PolyCurve::PrependAndMatch(ON_Curve* c)
2070 
2071 {
2072   if (Count() == 0) return Prepend(c);
2073   //if (IsClosed() || c->IsClosed()) return false;
2074   if (!c->SetEndPoint(PointAtStart())){
2075     if (!SetStartPoint(c->PointAtEnd()))
2076       return false;
2077   }
2078   return Prepend(c);
2079 }
2080 
AppendAndMatch(ON_Curve * c)2081 ON_BOOL32 ON_PolyCurve::AppendAndMatch(ON_Curve* c)
2082 
2083 {
2084   if (Count() == 0) return Append(c);
2085   //if (IsClosed() || c->IsClosed()) return false;
2086   if (!c->SetStartPoint(PointAtEnd())){
2087     if (!SetEndPoint(c->PointAtStart()))
2088       return false;
2089   }
2090   return Append(c);
2091 }
2092 
2093 
Remove()2094 ON_BOOL32 ON_PolyCurve::Remove( )
2095 {
2096   return Remove(Count()-1);
2097 }
2098 
Remove(int segment_index)2099 ON_BOOL32 ON_PolyCurve::Remove( int segment_index )
2100 {
2101   ON_BOOL32 rc = false;
2102   const int segment_count = Count();
2103   if ( segment_index >= 0 && segment_index < segment_count ) {
2104     delete m_segment[segment_index];
2105     m_segment[segment_index] = 0;
2106     m_segment.Remove(segment_index);
2107 		// GBA 18 September 2003. m_t array not properly updated when last segment removed.
2108     if ( segment_index >= 1 ) {
2109       double* d = m_t.Array();
2110       const double delta = d[segment_index] - d[segment_index+1];
2111       int i;
2112       for (i=segment_index+1; i <= segment_count; i++ ) {
2113         d[i] += delta;
2114       }
2115     }
2116 		// GBA 12/02/02.  When removing the last segment remove both m_t values so
2117 		// the polycurve will have the same state as ON_PolyCurve().
2118 		if( segment_count==1)
2119 			m_t.Empty();
2120 		else
2121 	    m_t.Remove(segment_index);
2122     rc = true;
2123   }
2124   return rc;
2125 }
2126 
Insert(int segment_index,ON_Curve * c)2127 ON_BOOL32 ON_PolyCurve::Insert( int segment_index, ON_Curve* c )
2128 {
2129   double s0, s1;
2130   ON_BOOL32 rc = false;
2131   const int count = Count();
2132   if ( segment_index >= 0 && segment_index <= count && c && c != this && c->GetDomain(&s0,&s1) )
2133   {
2134     rc = true;
2135     m_segment.Insert( segment_index, c );
2136 
2137     // determine polycurve parameters for this segment
2138     double t0, t1;
2139     if ( segment_index == count ) {
2140       // append segment
2141       if ( count == 0 ) {
2142         m_t.Append(s0);
2143         m_t.Append(s1);
2144       }
2145       else {
2146         t0 = m_t[count];
2147         t1 = ( s0 == t0 ) ? s1 : (s1-s0+t0);
2148         m_t.Append(t1);
2149       }
2150     }
2151     else if ( segment_index == 0 ) {
2152       // prepend segment
2153       t1 = m_t[0];
2154       t0 = ( s1 == t1 ) ? s0 : (s0-s1+t1);
2155       m_t.Insert(0,t0);
2156     }
2157     else {
2158       // insert segment
2159       t0 = m_t[segment_index];
2160       t1 = ( s0 == t0 ) ? s1 : (s1-s0+t0);
2161       const double dt = t1-t0;
2162       m_t.Insert(segment_index+1,t1);
2163       double* t = m_t.Array();
2164       for ( int i = segment_index+2; i <= count+1; i++ ) {
2165         t[i] += dt;
2166       }
2167     }
2168   }
2169   return rc;
2170 }
2171 
SetStartPoint(ON_3dPoint start_point)2172 ON_BOOL32 ON_PolyCurve::SetStartPoint(ON_3dPoint start_point)
2173 {
2174   ON_BOOL32 rc = false;
2175   // just do it // if ( !IsClosed() )
2176   {
2177     ON_Curve* c = FirstSegmentCurve();
2178     if ( c )
2179       rc = c->SetStartPoint(start_point);
2180   }
2181 	DestroyCurveTree();
2182   return rc;
2183 }
2184 
SetEndPoint(ON_3dPoint end_point)2185 ON_BOOL32 ON_PolyCurve::SetEndPoint(ON_3dPoint end_point)
2186 {
2187   ON_BOOL32 rc = false;
2188   // just do it // if ( !IsClosed() )
2189   {
2190     ON_Curve* c = LastSegmentCurve();
2191     if ( c )
2192       rc = c->SetEndPoint(end_point);
2193   }
2194 	DestroyCurveTree();
2195   return rc;
2196 }
2197 
GetNurbForm(ON_NurbsCurve & nurb,double tol,const ON_Interval * subdomain) const2198 int ON_PolyCurve::GetNurbForm(
2199                               ON_NurbsCurve& nurb,
2200                               double tol,
2201                               const ON_Interval* subdomain  // OPTIONAL subdomain of ON::ProxyCurve::Domain()
2202                               ) const
2203 {
2204   ON_Interval domain = Domain();
2205   if ( !domain.IsIncreasing() )
2206     return false;
2207   int rc = 0;
2208   int si0 = 0;
2209   int si1 = Count();
2210   if ( subdomain ) {
2211     if ( !subdomain->IsIncreasing() )
2212       return 0;
2213     if ( !domain.Includes(subdomain->Min()) )
2214       return 0;
2215     if ( !domain.Includes(subdomain->Max()) )
2216       return 0;
2217     domain = *subdomain;
2218   }
2219 
2220   while ( si0 < si1 && m_t[si0+1] <= domain.m_t[0] )
2221     si0++;
2222   while ( si0 < si1 && m_t[si1-1] >= domain.m_t[1] )
2223     si1--;
2224   if ( si0 >= si1 )
2225     return 0;
2226   {
2227     ON_NurbsCurve c;
2228     int i, rci;
2229     for ( i = si0; i < si1; i++ ) {
2230       if ( !m_segment[i] )
2231         return 0;
2232       if ( i == si0 ) {
2233         rc = m_segment[i]->GetNurbForm( nurb, tol, NULL );
2234         if ( rc < 1 )
2235           return rc;
2236         nurb.SetDomain( m_t[i], m_t[i+1] );
2237       }
2238       else {
2239         rci = m_segment[i]->GetNurbForm( c, tol, NULL );
2240         if ( rci < 1 )
2241           return rci;
2242         else if ( rc < rci )
2243           rc = rci;
2244         c.SetDomain( m_t[i], m_t[i+1] );
2245         if ( !nurb.Append( c ) )
2246           return 0;
2247         c.Destroy();
2248       }
2249     }
2250   }
2251 
2252   if ( subdomain )
2253     nurb.Trim( *subdomain );
2254 
2255   return rc;
2256 }
2257 
HasNurbForm() const2258 int ON_PolyCurve::HasNurbForm() const
2259 
2260 {
2261   int count = m_segment.Count();
2262   if (!count)
2263     return 0;
2264   int i;
2265   int rc = 1;
2266   for (i=0; i<count; i++){
2267     const ON_Curve* scrv = SegmentCurve(i);
2268     if (!scrv)
2269       return 0;
2270     int nf = scrv->HasNurbForm();
2271     if (nf == 0)
2272       return 0;
2273     if (nf == 2)
2274       rc = 2;
2275   }
2276   return rc;
2277 }
2278 
2279 
SegmentIndex(double curve_t) const2280 int ON_PolyCurve::SegmentIndex( double curve_t ) const
2281 {
2282   int count = m_segment.Count();
2283   int seg_index = ON_SearchMonotoneArray( m_t.Array(), m_t.Count(), curve_t );
2284   if ( seg_index < 0 )
2285     seg_index = 0;
2286   else if ( seg_index >= count )
2287     seg_index = count-1;
2288   return seg_index;
2289 }
2290 
SegmentIndex(ON_Interval sub_domain,int * segment_index0,int * segment_index1) const2291 int ON_PolyCurve::SegmentIndex(
2292   ON_Interval sub_domain,
2293   int* segment_index0,
2294   int* segment_index1
2295   ) const
2296 {
2297   const int segment_count = m_segment.Count();
2298   int s0 = 0, s1 = 0;
2299   ON_Interval seg_dom;
2300   sub_domain.Intersection( Domain() );
2301   if ( sub_domain.IsIncreasing() )
2302   {
2303     s0 = SegmentIndex(sub_domain.Min());
2304     for ( s1 = s0+1; s1 < segment_count; s1++ )
2305     {
2306       seg_dom = SegmentDomain(s1);
2307       if ( seg_dom[0] >= sub_domain.Max() )
2308         break;
2309     }
2310   }
2311   if ( segment_index0 )
2312     *segment_index0 = s0;
2313   if ( segment_index1 )
2314     *segment_index1 = s1;
2315   return s1-s0;
2316 }
2317 
2318 
GetCurveParameterFromNurbFormParameter(double nurbs_t,double * curve_t) const2319 ON_BOOL32 ON_PolyCurve::GetCurveParameterFromNurbFormParameter(
2320       double nurbs_t,
2321       double* curve_t
2322       ) const
2323 {
2324   ON_BOOL32 rc = false;
2325   int i = SegmentIndex( nurbs_t );
2326   const ON_Curve* curve = SegmentCurve(i);
2327   if ( curve ) {
2328     ON_Interval in( m_t[i], m_t[i+1] );
2329     ON_Interval cdom = curve->Domain();
2330     if ( in != cdom ) {
2331       nurbs_t = cdom.ParameterAt(in.NormalizedParameterAt(nurbs_t));
2332       rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t);
2333       if ( rc )
2334         *curve_t = in.ParameterAt(cdom.NormalizedParameterAt(*curve_t));
2335     }
2336     else {
2337       rc = curve->GetCurveParameterFromNurbFormParameter(nurbs_t,curve_t);
2338     }
2339   }
2340   return rc;
2341 }
2342 
GetNurbFormParameterFromCurveParameter(double curve_t,double * nurbs_t) const2343 ON_BOOL32 ON_PolyCurve::GetNurbFormParameterFromCurveParameter(
2344       double curve_t,
2345       double* nurbs_t
2346       ) const
2347 {
2348   ON_BOOL32 rc = false;
2349   int i = SegmentIndex( curve_t );
2350   const ON_Curve* curve = SegmentCurve(i);
2351   if ( curve ) {
2352     ON_Interval in( m_t[i], m_t[i+1] );
2353     ON_Interval cdom = curve->Domain();
2354     if ( in != cdom ) {
2355       curve_t = cdom.ParameterAt(in.NormalizedParameterAt(curve_t));
2356       rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t);
2357       if ( rc )
2358         *nurbs_t = in.ParameterAt(cdom.NormalizedParameterAt(*nurbs_t));
2359     }
2360     else {
2361       rc = curve->GetNurbFormParameterFromCurveParameter(curve_t,nurbs_t);
2362     }
2363   }
2364   return rc;
2365 }
2366 
2367 
HarvestSegment(int i)2368 ON_Curve* ON_PolyCurve::HarvestSegment( int i )
2369 {
2370   ON_Curve* segment_curve = 0;
2371   if ( i >= 0 && i < m_segment.Count() ) {
2372     segment_curve = m_segment[i];
2373     m_segment[i] = 0;
2374   }
2375   return segment_curve;
2376 }
2377 
Trim(const ON_Interval & domain)2378 ON_BOOL32 ON_PolyCurve::Trim(
2379   const ON_Interval& domain
2380   )
2381 {
2382   // Please talk to Dale Lear before you change code in this function.
2383 
2384   // m_t[] = Increasing array of segment_count+1 parameter values
2385   //         that specify segment domains.
2386   //         Domain of polycurve = (m_t[0],m_t[segment_count]).
2387   // m_segment[] = array of segment curves
2388   int segment_count = m_segment.Count();
2389   if ( m_t.Count() < 2 || segment_count+1 != m_t.Count() || !domain.IsIncreasing() )
2390   {
2391     // bogus input
2392     return false;
2393   }
2394 
2395   const ON_Interval original_polycurve_domain = Domain();
2396   if ( !original_polycurve_domain.IsIncreasing() )
2397     return false;
2398 
2399   ON_Interval output_domain = domain;
2400   if ( !output_domain.Intersection(original_polycurve_domain) )
2401     return false;
2402 
2403 	if(!output_domain.IsIncreasing())
2404 		return false;
2405 
2406   if (output_domain == original_polycurve_domain )
2407     return true;
2408 
2409   ON_Interval actual_trim_domain = output_domain;
2410 
2411   int s0 = -2; // s0 gets set to index of first segment we keep
2412   int s1 = -3; // s1 gets set to index of last segment we keep
2413 
2414   // 22 October 2003 Dale Lear - redid Greg's parameter search
2415   //   snapping stuff.  New stuff is in sourcesafe version 72.
2416   //   In particular, attempting using "Trim" to extend polycurves
2417   //   will not be supported.  You have to use "Extend" if you
2418   //   want a curve to get longer.
2419 
2420   // In mid 3003, Greg added ParameterSearch to do "microtol" snapping
2421   // to segment end parameters.  The goal was to handle fuzz that gets
2422   // introduces by reparameterizations that happen when the top level
2423   // curve is a proxy/poly curve and the proxy/polycurve trim parameters get
2424   // readjusted as we move toward trimming the "real" curve that is
2425   // stored in the m_segment[] array.
2426 	if ( ParameterSearch(output_domain[0], s0, true ) )
2427   {
2428     // ParameterSearch says domain[0] is within "microtol" of
2429     // m_t[s0].  So we will actually trim at m_t[s0].
2430     if (s0 >= 0 && s0 <= segment_count)
2431     {
2432       actual_trim_domain[0]=m_t[s0];
2433     }
2434   }
2435 
2436 	if ( ParameterSearch(output_domain[1], s1, true ) )
2437   {
2438     if (s1 >= 0 && s1 <= segment_count )
2439     {
2440       // ParameterSearch says domain[1] is within "microtol" of
2441       // m_t[s1].  So we will actually trim at m_t[s1].
2442       actual_trim_domain[1]=m_t[s1];
2443       s1--;
2444     }
2445   }
2446 
2447   if ( !actual_trim_domain.IsIncreasing() )
2448   {
2449     // After microtol snapping, there is not enough curve left to trim.
2450     return false;
2451   }
2452 
2453   if ( s0 < 0 || s0 > s1 || s1 >= segment_count )
2454   {
2455     // Because output_domain is a subinterval of original_polycurve_domain,
2456     // the only way that (s0 < 0 || s0 > s1 || s1 >= segment_count) can be true
2457     // is if something is seriously wrong with the m_t[] values.
2458     return false;
2459   }
2460 
2461   // we will begin modifying the polycurve
2462   DestroyCurveTree();
2463 
2464   if ( actual_trim_domain == original_polycurve_domain )
2465   {
2466     // ParameterSearch says that the ends of output_domain
2467     // were microtol away from being the entire curve.
2468     // Set the domain and return.
2469     m_t[0] = output_domain[0];
2470     m_t[segment_count] = output_domain[1];
2471     return true;
2472   }
2473 
2474   int i;
2475   for ( i = 0; i < s0; i++ )
2476   {
2477     // delete curves in segments [0,...,s0-1]
2478     delete m_segment[i];
2479     m_segment[i] = 0;
2480   }
2481   for ( i = s1+1; i < segment_count; i++ )
2482   {
2483     // delete curves in segments [s1+1,...,segment_count-1]
2484     delete m_segment[i];
2485     m_segment[i] = 0;
2486   }
2487 
2488   // remove segments [s1+1,...,segment_count-1] from polycurve
2489   m_segment.SetCount( s1+1 );
2490   m_t.SetCount(s1+2);
2491   segment_count = s1+1;
2492 
2493   if ( s0 > 0 )
2494   {
2495     // remove segments [0,...,s0-1] from polycurve
2496     ON_SimpleArray<ON_Curve*> tmp_seg(s1+1-s0);
2497     ON_SimpleArray<double> tmp_t(s1+2-s0);
2498     tmp_seg.Append( s1+1-s0, m_segment.Array()+s0 );
2499     tmp_t.Append( s1+2-s0, m_t.Array()+s0 );
2500     m_segment.Zero();
2501     m_segment.SetCount( 0 );
2502     m_segment.Append( tmp_seg.Count(), tmp_seg.Array() );
2503     m_t = tmp_t;
2504     segment_count = s1-s0+1;
2505     s1 = segment_count-1;
2506     s0 = 0;
2507   }
2508 
2509 
2510   const double fuzz = 0.001; // Greg says: anything small and > 1.0e-6 will work about the same
2511   bool bTrimFirstSegment = ( m_t[0] < actual_trim_domain[0] || (0 == s1 && actual_trim_domain[1] < m_t[s1+1]) );
2512   bool bTrimLastSegment = (s1>s0 && actual_trim_domain[1] < m_t[s1+1]);
2513 
2514   // if needed, trim left end of first segment
2515   ON_Interval trim_s_dom, trim_c_dom, c_dom, s_dom;
2516 
2517   if ( bTrimFirstSegment )
2518   {
2519     ON_Curve* curve = SegmentCurve(0);
2520     if ( 0 == curve )
2521       return false; // bogus polycurve (m_segment[0] is a NULL pointer)
2522 
2523     c_dom = curve->Domain();
2524     if ( !c_dom.IsIncreasing() )
2525     {
2526       // first segment curve is bogus
2527       return false;
2528     }
2529 
2530     s_dom = SegmentDomain(0);
2531     if ( !s_dom.IsIncreasing() )
2532     {
2533       // m_t[0] or m_t[1] is bogus
2534       return false;
2535     }
2536 
2537     trim_s_dom = s_dom;
2538 	  if ( !trim_s_dom.Intersection(actual_trim_domain) )
2539     {
2540       // Should never happen; if it does, we have to give up.
2541       // (and there is probably a bug in the code above)
2542       return false;
2543     }
2544 
2545     if ( s1 > 0 && trim_s_dom[1] != s_dom[1] )
2546     {
2547       // Should never happen; if it does, we have to give up.
2548       // (and there is probably a bug in the code above)
2549       return false;
2550     }
2551 
2552     if ( !trim_s_dom.IsIncreasing() )
2553     {
2554       // Should never happen; if it does, we have to give up.
2555       // (and there is probably a bug in the code above)
2556       return false;
2557     }
2558 
2559     if ( c_dom != s_dom )
2560     {
2561       // need to convert polycurve parameters to "real" segment curve parameters
2562       trim_c_dom[0] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[0]) );
2563       trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) );
2564       if ( !trim_c_dom.IsIncreasing() )
2565       {
2566         if ( s_dom.NormalizedParameterAt(trim_s_dom[0]) >= 1.0-fuzz && s1 > 0 )
2567         {
2568           // We were trying to throw away all but a microscopic bit on the right
2569           // end of the first segment of a multi segment polycurve
2570           // and the parameter conversion killed the "real" trim interval.
2571           // In this case, we can just throw away the first segment.
2572           bTrimFirstSegment = false;
2573           curve = 0;
2574           delete m_segment[0];
2575           m_segment[0] = 0;
2576 			    m_t.Remove(0);
2577 			    m_segment.Remove(0);
2578 			    s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
2579         }
2580         else
2581           return false;
2582       }
2583     }
2584     else
2585     {
2586       trim_c_dom = trim_s_dom;
2587     }
2588 
2589     // trim_s_dom = polycurve segment parameters after trimming
2590     // trim_c_dom = "real" segment curve parameters after trimming
2591 
2592     if ( bTrimFirstSegment && trim_c_dom != c_dom )
2593     {
2594       // trim first segment
2595       if ( !curve->Trim(trim_c_dom) )
2596       {
2597         // trimming first segment failed - see if we should give up or
2598         // or just discard the first segment.
2599 
2600 			  if ( c_dom.NormalizedParameterAt(trim_c_dom[0]) >= 1.0 - fuzz && s1 > 0 )
2601         {
2602 			    // remove entire first segment
2603           bTrimFirstSegment = false;
2604           curve = 0;
2605           delete m_segment[0];
2606           m_segment[0] = 0;
2607 			    m_t.Remove(0);
2608 			    m_segment.Remove(0);
2609 			    s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
2610         }
2611         else
2612           return false;
2613 		  }
2614       else
2615       {
2616         m_t[0] = actual_trim_domain[0]; // will be tweaked below when we've finished.
2617         if ( 0 == s1 && 2 == m_t.Count() && !bTrimLastSegment )
2618           m_t[1] = actual_trim_domain[1];
2619       }
2620     }
2621   }
2622 
2623 
2624 
2625   if ( bTrimLastSegment )
2626   {
2627     // If we get in here, it means we need to trim a portion off of
2628     // the right end of the last segment.
2629 
2630     if ( s1+1 != m_segment.Count() )
2631     {
2632       // Should never happen; if it does, we have to give up.
2633       // (and there is probably a bug in the code above)
2634       return false;
2635     }
2636 
2637     ON_Curve* curve = SegmentCurve(s1);
2638     if ( 0 == curve )
2639       return false; // bogus polycurve (m_segment[s1] array has a null pointer)
2640 
2641     c_dom = curve->Domain();
2642     if ( !c_dom.IsIncreasing() )
2643     {
2644       // first segment curve is bogus
2645       return false;
2646     }
2647 
2648     s_dom = SegmentDomain(s1);
2649     if ( !s_dom.IsIncreasing() )
2650     {
2651       // m_t[s1] or m_t[s1+1] is bogus
2652       return false;
2653     }
2654 
2655 		// trim the curve on the right
2656     trim_s_dom= ON_Interval(m_t[s1], actual_trim_domain[1]);
2657 
2658     if ( !trim_s_dom.IsIncreasing() )
2659     {
2660       // Should never happen; if it does, we have to give up.
2661       // (and there is probably a bug in the code above)
2662       return false;
2663     }
2664 
2665     trim_c_dom[0] = c_dom[0];
2666     if ( c_dom != s_dom )
2667     {
2668       trim_c_dom[1] = c_dom.ParameterAt( s_dom.NormalizedParameterAt(trim_s_dom[1]) );
2669       if ( !trim_c_dom.IsIncreasing() )
2670       {
2671         if ( s_dom.NormalizedParameterAt(trim_s_dom[1]) <= fuzz && s1 > 0 )
2672         {
2673           // We were trying to throw away all but a microscopic bit on the left
2674           // end of the last segment of a multi segment polycurve
2675           // and the parameter conversion killed the "real" trim interval.
2676           // In this case, we can just throw away the last segment.
2677           bTrimLastSegment = false;
2678           curve = 0;
2679           delete m_segment[s1];
2680           m_segment[s1] = 0;
2681 			    m_t.Remove();       // remove last array entry in the m_t[] array
2682 			    m_segment.Remove(); // remove last array entry in the m_segment[] array
2683 			    s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
2684         }
2685         else
2686           return false;
2687       }
2688     }
2689     else
2690     {
2691       trim_c_dom[1] = trim_s_dom[1];
2692     }
2693 
2694     if ( bTrimLastSegment && c_dom != trim_c_dom )
2695     {
2696       // trim last segment
2697       if ( !curve->Trim(trim_c_dom) )
2698       {
2699 
2700 				if ( c_dom.NormalizedParameterAt(trim_c_dom[1]) <= fuzz && s1 > 0)
2701         {
2702           // We were trying to throw away all but a microscopic bit on the left
2703           // end of the last segment of a multi segment polycurve
2704           // and the segment curve's trimmer failed.  I'm assuming the
2705           // failure was caused because the part that would be left was
2706           // too short.
2707           // In this case, we can just throw away the last segment.
2708           bTrimLastSegment = false;
2709           curve = 0;
2710           delete m_segment[s1];
2711           m_segment[s1] = 0;
2712 			    m_t.Remove();       // remove last array entry in the m_t[] array
2713 			    m_segment.Remove(); // remove last array entry in the m_segment[] array
2714 			    s1--; // removing a segment, means s1=(index of last valid segment) has to get decremented.
2715         }
2716         else
2717           return false;
2718 			}
2719       else
2720         m_t[m_t.Count()-1] = actual_trim_domain[1]; // will be tweaked below when we've finished.
2721 		}
2722   }
2723 
2724   // If we get this far, trims were is successful.
2725   // The following makes potential tiny adjustments
2726   // that need to happen when trims get snapped to
2727   // m_t[] values that are within fuzz of the
2728   // output_domain[] values.
2729 	m_t[0] = output_domain[0];
2730   m_t[m_t.Count()-1] = output_domain[1];
2731 
2732 	DestroyCurveTree();
2733 
2734   return true;
2735 }
2736 
2737 
2738 
2739 
Extend(const ON_Interval & domain)2740 bool ON_PolyCurve::Extend(
2741   const ON_Interval& domain
2742   )
2743 
2744 {
2745   if (IsClosed() || Count() < 1) return false;
2746 
2747   bool changed = false;
2748   if (Domain()[0] > domain[0]){
2749     ON_Curve* seg = SegmentCurve(0);
2750     if (!seg) return false;
2751     ON_Interval sdom = SegmentDomain(0);
2752     ON_Interval cdom = seg->Domain();
2753     double a = (sdom == cdom) ? domain[0] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[0]));
2754     ON_Interval DesiredDom(a, cdom[1]);
2755     changed = seg->Extend(DesiredDom);
2756     if (changed) {
2757       if (seg->Domain() == DesiredDom)
2758         m_t[0] = domain[0];
2759       else
2760         m_t[0] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[0]));
2761     }
2762   }
2763   if (Domain()[1] < domain[1]){
2764     bool chgd = false;
2765     ON_Curve* seg = SegmentCurve(Count()-1);
2766     if (!seg) return false;
2767     ON_Interval sdom = SegmentDomain(Count()-1);
2768     ON_Interval cdom = seg->Domain();
2769     double a = (sdom == cdom) ? domain[1] : cdom.ParameterAt(sdom.NormalizedParameterAt(domain[1]));
2770     ON_Interval DesiredDom(cdom[0], a);
2771     chgd = seg->Extend(DesiredDom);
2772     if (chgd) {
2773       if (seg->Domain() == DesiredDom)
2774         m_t[Count()] = domain[1];
2775       else
2776         m_t[Count()] = sdom.ParameterAt(cdom.NormalizedParameterAt(seg->Domain()[1]));
2777       changed = true;
2778     }
2779   }
2780 
2781   if (changed){
2782     DestroyCurveTree();
2783   }
2784 
2785   return changed;
2786 }
2787 
2788 
2789 
2790 
Split(double split_parameter,ON_Curve * & left_side,ON_Curve * & right_side) const2791 ON_BOOL32 ON_PolyCurve::Split(
2792     double split_parameter,
2793     ON_Curve*& left_side, // left portion returned here
2794     ON_Curve*& right_side // right portion returned here
2795   ) const
2796 {
2797   int si;
2798   ON_Interval dom = Domain();
2799 
2800   ON_PolyCurve* pLeftSide  = ON_PolyCurve::Cast(left_side);
2801   ON_PolyCurve* pRightSide = ON_PolyCurve::Cast(right_side);
2802 
2803   if ( pLeftSide && pLeftSide != this )
2804     pLeftSide->Destroy();
2805   else if ( pLeftSide == this )
2806     pLeftSide->DestroyCurveTree();
2807 
2808   if ( pRightSide && pRightSide != this )
2809     pRightSide->Destroy();
2810   else if ( pRightSide == this )
2811     pRightSide->DestroyCurveTree();
2812 
2813   if ( left_side && !pLeftSide )
2814     return false;
2815   if ( right_side && !pRightSide )
2816     return false;
2817   if ( !dom.Includes( split_parameter, true ) )
2818     return false; // split_parameter is not an interior parameter
2819 
2820 
2821   const ON_BOOL32 bDupSegs = ( this != pLeftSide && this != pRightSide );
2822 
2823 		/* 4 April 2003 Greg Arden		Made the following changes:
2824 																		1.	Use ParameterSearch() to decide if we should snap domain
2825 																				boundries to m_t array values.
2826 																		2.  Make sure resulting polycurves have Domain() specified as
2827 																				split parameter.
2828 																		3.  When true is returned the result passes IsValid().
2829 		*/
2830 	bool split_at_break = ParameterSearch(split_parameter, si, true);
2831 	if( split_at_break && (si<=0 || si>=Count() ) )
2832 		return false;
2833 
2834 	ON_Interval s_dom = SegmentDomain(si);
2835   ON_Curve* seg_curve = SegmentCurve(si);
2836   if ( !seg_curve )
2837     return false;
2838   ON_Interval c_dom = seg_curve->Domain();
2839 
2840   double c;
2841 	if( split_at_break)
2842 		c = c_dom[0];
2843 	else
2844 		c = ( c_dom == s_dom )
2845            ? split_parameter
2846            : c_dom.ParameterAt( s_dom.NormalizedParameterAt(split_parameter) );
2847 
2848   ON_Curve* seg_left = 0;
2849   ON_Curve* seg_right = 0;
2850 
2851   if ( !split_at_break  && c_dom.Includes(c,true) )
2852   {
2853     if ( !seg_curve->Split( c, seg_left, seg_right ) )
2854     {
2855       double fuzz = 0.001; // anything small and > 1.0e-6 will work about the same
2856       if ( c_dom.NormalizedParameterAt(c) <= fuzz )
2857         c = c_dom[0];
2858       else if ( c_dom.NormalizedParameterAt(c) >= 1.0 - fuzz )
2859         c = c_dom[1];
2860       else
2861         return false; // unable to split this segment
2862     }
2863   }
2864   else if ( c <= c_dom.ParameterAt(0.5) )
2865     c = c_dom[0];
2866   else
2867     c = c_dom[1];
2868 
2869   // use scratch arrays since this may also be pLeftSide or pRightSide
2870   ON_SimpleArray< ON_Curve* > left_segment;
2871   ON_SimpleArray< ON_Curve* > right_segment;
2872   ON_SimpleArray< double > left_t;
2873   ON_SimpleArray< double > right_t;
2874 
2875   int i;
2876 
2877   if ( seg_left && seg_right )
2878   {
2879     // we split a segment
2880     left_segment.Reserve(si+1);
2881     right_segment.Reserve(m_segment.Count()-si);
2882     left_t.Reserve(left_segment.Count()+1);
2883     right_t.Reserve(right_segment.Count()+1);
2884     if ( !bDupSegs )
2885     {
2886       delete m_segment[si];
2887       const_cast<ON_PolyCurve*>(this)->m_segment[si] = 0;
2888     }
2889 
2890     for ( i = 0; i < si; i++ )
2891     {
2892       if ( bDupSegs )
2893         left_segment.Append( m_segment[i]->Duplicate() );
2894       else
2895         left_segment.Append( m_segment[i] );
2896       left_t.Append( m_t[i] );
2897     }
2898     left_segment.Append( seg_left );
2899     left_t.Append( m_t[si] );
2900     left_t.Append( split_parameter );
2901 
2902     right_segment.Append(seg_right);
2903     right_t.Append( split_parameter );
2904     for ( i = si+1; i < m_segment.Count(); i++ )
2905     {
2906       if ( bDupSegs )
2907         right_segment.Append( m_segment[i]->Duplicate() );
2908       else
2909         right_segment.Append( m_segment[i] );
2910       right_t.Append( m_t[i] );
2911     }
2912     right_t.Append( m_t[m_segment.Count()] );
2913   }
2914   else
2915   {
2916     if ( c == c_dom[1] )
2917       si++;
2918 		if( (c==c_dom[0] && si==0 ) ||								// attempting split at curve start
2919 				(c==c_dom[1] && si==m_segment.Count() ) )	// attempting split at curve end
2920 			return false;
2921 
2922     left_segment.Reserve(si);
2923     right_segment.Reserve(m_segment.Count()-si);
2924     left_t.Reserve(left_segment.Count()+1);
2925     right_t.Reserve(right_segment.Count()+1);
2926 
2927     for ( i = 0; i < si; i++ )
2928     {
2929       if ( bDupSegs )
2930         left_segment.Append( m_segment[i]->Duplicate() );
2931       else
2932         left_segment.Append( m_segment[i] );
2933       left_t.Append( m_t[i] );
2934     }
2935     left_t.Append( split_parameter );
2936 
2937     for ( i = si; i < m_segment.Count(); i++ )
2938     {
2939       if ( bDupSegs )
2940         right_segment.Append( m_segment[i]->Duplicate() );
2941       else
2942         right_segment.Append( m_segment[i] );
2943       if ( i == si )
2944         right_t.Append( split_parameter );
2945       else
2946         right_t.Append( m_t[i] );
2947     }
2948     right_t.Append( m_t[m_segment.Count()] );
2949   }
2950 
2951   if ( !pLeftSide )
2952     pLeftSide = new ON_PolyCurve();
2953   if ( !pRightSide )
2954     pRightSide = new ON_PolyCurve();
2955   if ( !bDupSegs )
2956   {
2957     // pLeftSide or pRightSide is the same as this
2958     ON_PolyCurve* this_ptr = const_cast<ON_PolyCurve*>(this);
2959     this_ptr->m_segment.Zero();
2960     this_ptr->m_t.Zero();
2961     this_ptr->m_segment.SetCount(0);
2962     this_ptr->m_t.SetCount(0);
2963   }
2964 
2965   pLeftSide->m_segment.Append( left_segment.Count(), left_segment.Array() );
2966   pLeftSide->m_t.Append( left_t.Count(), left_t.Array() );
2967   pRightSide->m_segment.Append( right_segment.Count(), right_segment.Array() );
2968   pRightSide->m_t.Append( right_t.Count(), right_t.Array() );
2969 
2970   left_side = pLeftSide;
2971   right_side = pRightSide;
2972 
2973   return true;
2974 }
2975 
2976 // Flatten a poly curve reparameterized over pdom.
2977 // Harvests all the segments recursively and places them in the arrays
2978 static
Flatten(ON_PolyCurve * poly,ON_Interval pdom,ON_SimpleArray<double> & new_t,ON_SimpleArray<ON_Curve * > & new_seg)2979 void Flatten( ON_PolyCurve* poly, ON_Interval pdom, ON_SimpleArray<double>& new_t, ON_SimpleArray<ON_Curve*>& new_seg){
2980 		int n= poly->Count();
2981 		double t0 = pdom[0];
2982 		ON_Interval pcdom = poly->Domain();
2983 		for(int i=0; i<n; i++){
2984 			double sdom=poly->SegmentDomain(i)[1];
2985 			double ndom=pcdom.NormalizedParameterAt(sdom);
2986 			double t1 =pdom.ParameterAt(ndom);
2987 			ON_Curve* seg = poly->SegmentCurve(i);
2988 			ON_PolyCurve* spoly =  ON_PolyCurve::Cast(seg);
2989 			if(spoly){
2990 				Flatten(spoly, ON_Interval(t0,t1), new_t, new_seg );
2991 				poly->HarvestSegment(i);
2992 				delete spoly;
2993 			} else {
2994 				new_t.Append(t1);
2995 				new_seg.Append(seg);
2996 				poly->HarvestSegment(i);
2997 			}
2998 			t0 = t1;
2999 		}
3000 }
3001 
HasSynchronizedSegmentDomains() const3002 bool ON_PolyCurve::HasSynchronizedSegmentDomains() const
3003 {
3004   double t0, t1;
3005   int i, count = m_segment.Count();
3006   const ON_Curve* const * c = m_segment.Array();
3007   if ( count < 1 || 0 == c )
3008     return false;
3009   if ( count != m_t.Count()+1 )
3010     return false;
3011   const double* t = m_t.Array();
3012   if ( 0 == t )
3013     return false;
3014 
3015   for ( i = 0; i < count; i++ )
3016   {
3017     t0 = -ON_UNSET_VALUE;
3018     t1 = ON_UNSET_VALUE;
3019     if ( 0 != c[i]
3020          && c[i]->GetDomain(&t0,&t1)
3021          && t0 == t[i]
3022          && t1 == t[i+1]
3023          )
3024      {
3025        continue;
3026      }
3027      return false;
3028   }
3029 
3030   return true;
3031 }
3032 
3033 /*
3034 Description:
3035   Sets the domain of the curve int the m_segment[] array to exactly
3036   match the domain defined in the m_t[] array.  This is not required,
3037   but can simplify some coding situations.
3038 Returns:
3039   True if at least one segment was reparameterized. False if no
3040   changes were made.
3041 */
SynchronizeSegmentDomains()3042 bool ON_PolyCurve::SynchronizeSegmentDomains()
3043 {
3044   double t0, t1;
3045   int i, count = m_segment.Count();
3046   ON_Curve** c = m_segment.Array();
3047   if ( count < 1 || 0 == c )
3048     return false;
3049   if ( count+1 != m_t.Count() )
3050     return false;
3051   const double* t = m_t.Array();
3052   if ( 0 == t )
3053     return false;
3054 
3055   bool rc = false;
3056   for ( i = 0; i < count; i++ )
3057   {
3058     if ( !c[i] )
3059       continue;
3060     t0 = -ON_UNSET_VALUE;
3061     t1 = ON_UNSET_VALUE;
3062     if ( c[i]->GetDomain(&t0,&t1)
3063          && t0 == t[i]
3064          && t1 == t[i+1]
3065          )
3066     {
3067      continue;
3068     }
3069 
3070     if (    ON_IsValid(t[i])
3071         && ON_IsValid(t[i+1])
3072         && t[i] < t[i+1]
3073         && c[i]->SetDomain(t[i],t[i+1])
3074       )
3075     {
3076      rc = true; // indicates a change was made
3077     }
3078   }
3079   return rc;
3080 }
3081 
3082 
3083 
RemoveNestingEx()3084 bool ON_PolyCurve::RemoveNestingEx( )
3085 {
3086   // 19 March 2003 Dale Lear
3087   //     Added the true/false return code to RemoveNesting()
3088   //     so that CRhinoDoc::AddObjects() can easily detect when
3089   //     nested polycurves were added.  I had to add use
3090   //     a new function name to avoid breaking the SDK.
3091   bool rc = false;
3092 	int n = Count();
3093 
3094 	ON_SimpleArray<double> old_t = m_t;
3095 	ON_SimpleArray<ON_Curve*> old_seg = m_segment;
3096 
3097 	m_t.SetCount(1);
3098 	m_segment.SetCount(0);
3099 
3100 	for(int i=0; i<n;i++){
3101 		ON_PolyCurve* poly = ON_PolyCurve::Cast( old_seg[i]);
3102 		if(poly){
3103       rc = true;
3104 			Flatten( poly, ON_Interval(old_t[i], old_t[i+1]), m_t, m_segment );
3105 			delete poly;
3106 		} else {
3107 			m_t.Append( old_t[i+1]);
3108 			m_segment.Append( old_seg[i] );
3109 		}
3110 	}
3111   return rc;
3112 }
3113 
RemoveNesting()3114 void  ON_PolyCurve::RemoveNesting( )
3115 {
3116   RemoveNestingEx();
3117 }
3118 
IsNested() const3119 bool ON_PolyCurve::IsNested() const
3120 {
3121   int i, count = m_segment.Count();
3122 	for ( i = 0; i < count; i++ )
3123   {
3124     if (  ON_PolyCurve::Cast(m_segment[i]) )
3125       return true;
3126   }
3127   return false;
3128 }
3129 
3130 
3131 //   Sets the m_segment[index] to crv.
SetSegment(int i,ON_Curve * crv)3132 void ON_PolyCurve::SetSegment(int i, ON_Curve* crv){
3133 	if(i>=0 && i<Count())
3134 		m_segment[i] = crv;
3135 }
3136 
3137 // returns true if t is sufficiently close to m_t[index]
ParameterSearch(double t,int & index,bool bEnableSnap) const3138 bool ON_PolyCurve::ParameterSearch(double t, int& index, bool bEnableSnap) const{
3139 	return ON_Curve::ParameterSearch(t, index, bEnableSnap, m_t, ON_SQRT_EPSILON);
3140 }
3141 
SegmentCurves() const3142 const ON_CurveArray& ON_PolyCurve::SegmentCurves() const
3143 {
3144   return m_segment;
3145 }
3146 
SegmentParameters() const3147 const ON_SimpleArray<double>& ON_PolyCurve::SegmentParameters() const
3148 {
3149   return m_t;
3150 }
3151