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