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 #include "opennurbs.h"
17 
18 //March 23, 2008 - LW
19 //Adding ON_HatchExtra class to support movable base point for hatches
20 //This should be combined with the ON_Hatch class next time that is possible
21 // Don't put this extension class in a header file or export it.
22 
23 class ON_HatchExtra : public ON_UserData
24 {
25   ON_OBJECT_DECLARE(ON_HatchExtra);
26 public:
27   static ON_HatchExtra* HatchExtension(ON_Hatch* pHatch, bool bCreate);
28   static const ON_HatchExtra* HatchExtension(const ON_Hatch* pHatch, bool bCreate);
29 
30   ON_HatchExtra();
31   ~ON_HatchExtra();
32 
33   void SetDefaults();
34 
35   // override virtual ON_Object::Dump function
36   void Dump( ON_TextLog& text_log ) const;
37 
38   // override virtual ON_Object::SizeOf function
39   unsigned int SizeOf() const;
40 
41   // override virtual ON_Object::Write function
42   ON_BOOL32 Write(ON_BinaryArchive& binary_archive) const;
43 
44   // override virtual ON_Object::Read function
45   ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
46 
47   // override virtual ON_UserData::GetDescription function
48   ON_BOOL32 GetDescription( ON_wString& description );
49 
50   // override virtual ON_UserData::Archive function
51   ON_BOOL32 Archive() const;
52 
53   // Get and set a 2d point in the hatch's ECS coordinates
54   void SetBasePoint(ON_2dPoint& basepoint);
55   ON_2dPoint BasePoint() const;
56 
57   ON_UUID    m_parent_hatch; // Hatch this extends or ON_nil_uuid
58   ON_2dPoint m_basepoint;    // Base point in hatch's ECS
59 
60 };
61 
62 ON_OBJECT_IMPLEMENT(ON_HatchExtra,ON_UserData,"3FF7007C-3D04-463f-84E3-132ACEB91062");
63 
HatchExtension(ON_Hatch * pHatch,bool bCreate)64 ON_HatchExtra* ON_HatchExtra::HatchExtension(ON_Hatch* pHatch, bool bCreate)
65 {
66   ON_HatchExtra* pExtra = 0;
67   if(pHatch)
68   {
69     pExtra = ON_HatchExtra::Cast(pHatch->GetUserData(ON_HatchExtra::m_ON_HatchExtra_class_id.Uuid()));
70     if(pExtra == 0 && bCreate)
71     {
72       pExtra = new ON_HatchExtra;
73       if(pExtra)
74       {
75         if(!pHatch->AttachUserData(pExtra))
76         {
77           delete pExtra;
78           pExtra = 0;
79         }
80       }
81     }
82   }
83   return pExtra;
84 }
85 
HatchExtension(const ON_Hatch * pHatch,bool bCreate)86 const ON_HatchExtra* ON_HatchExtra::HatchExtension(const ON_Hatch* pHatch, bool bCreate)
87 {
88   return HatchExtension((ON_Hatch*)pHatch, bCreate);
89 }
90 
ON_HatchExtra()91 ON_HatchExtra::ON_HatchExtra()
92 {
93   m_userdata_uuid = ON_HatchExtra::m_ON_HatchExtra_class_id.Uuid();
94   m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
95                                          // The id must be the version 5 id because
96                                          // V6 SaveAs V5 needs to work, but SaveAs
97                                          // V4 should not write this userdata.
98   m_userdata_copycount = 1;
99   SetDefaults();
100 }
101 
~ON_HatchExtra()102 ON_HatchExtra::~ON_HatchExtra()
103 {
104 }
105 
SetDefaults()106 void ON_HatchExtra::SetDefaults()
107 {
108   m_parent_hatch = ON_nil_uuid;
109   m_basepoint.Set(0.0,0.0);
110 }
111 
Dump(ON_TextLog & text_log) const112 void ON_HatchExtra::Dump(ON_TextLog& text_log) const
113 {
114 }
115 
SizeOf() const116 unsigned int ON_HatchExtra::SizeOf() const
117 {
118   unsigned int sz = ON_UserData::SizeOf();
119   sz += sizeof(*this)-sizeof(ON_UserData);
120   return sz;
121 }
122 
Write(ON_BinaryArchive & archive) const123 ON_BOOL32 ON_HatchExtra::Write(ON_BinaryArchive& archive) const
124 {
125   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
126 
127   if(rc) rc = archive.WriteUuid( m_parent_hatch);
128   if(rc) rc = archive.WritePoint(m_basepoint);
129 
130   if(!archive.EndWrite3dmChunk())
131     rc = false;
132 
133   return rc;
134 }
135 
Read(ON_BinaryArchive & archive)136 ON_BOOL32 ON_HatchExtra::Read(ON_BinaryArchive& archive)
137 {
138   int major_version = 0;
139   int minor_version = 0;
140   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
141 
142   if(major_version != 1)
143       rc = false;
144 
145   m_basepoint.Set(0.0,0.0);
146   if(rc) rc = archive.ReadUuid(m_parent_hatch);
147   if(rc) rc = archive.ReadPoint(m_basepoint);
148 
149   if(!archive.EndRead3dmChunk())
150     rc = false;
151 
152   return rc;
153 }
154 
GetDescription(ON_wString & description)155 ON_BOOL32 ON_HatchExtra::GetDescription( ON_wString& description)
156 {
157   description.Format( "Userdata extension of ON_Hatch (contains basepoint)");
158   return true;
159 }
160 
Archive() const161 ON_BOOL32 ON_HatchExtra::Archive() const
162 {
163   return true;
164 }
165 
SetBasePoint(ON_2dPoint & point)166 void ON_HatchExtra::SetBasePoint(ON_2dPoint& point)
167 {
168   if(point.IsValid())
169     m_basepoint = point;
170 }
171 
BasePoint() const172 ON_2dPoint ON_HatchExtra::BasePoint() const
173 {
174   return m_basepoint;
175 }
176 
177 /////////////////////////////////////////////////////////////////
178 //  class ON_HatchLine
179 /////////////////////////////////////////////////////////////////
180 
ON_HatchLine()181 ON_HatchLine::ON_HatchLine()
182 : m_angle( 0.0), m_base( 0.0,0.0), m_offset( 0.0, 1.0)
183 {
184 }
185 
ON_HatchLine(double angle,const ON_2dPoint & base,const ON_2dVector & offset,const ON_SimpleArray<double> dashes)186 ON_HatchLine::ON_HatchLine(double angle,
187                            const ON_2dPoint& base,
188                            const ON_2dVector& offset,
189                            const ON_SimpleArray<double> dashes)
190 : m_angle( angle), m_base( base), m_offset( offset), m_dashes( dashes)
191 {
192 }
193 
operator ==(const ON_HatchLine & src) const194 bool ON_HatchLine::operator==(const ON_HatchLine& src) const
195 {
196   return( m_angle == src.m_angle &&
197           m_base == src.m_base &&
198           m_offset == src.m_offset &&
199           m_dashes == src.m_dashes);
200 }
201 
operator !=(const ON_HatchLine & src) const202 bool ON_HatchLine::operator!=(const ON_HatchLine& src) const
203 {
204   return !operator==( src);
205 }
206 
IsValid(ON_TextLog * text_log) const207 ON_BOOL32 ON_HatchLine::IsValid( ON_TextLog* text_log) const
208 {
209   bool rc = m_angle >= 0.0;
210   if( !rc)
211   {
212     if( text_log)
213       text_log->Print( "Angle ( %lf) must be >= 0.0\n", m_angle);
214     return false;
215   }
216   rc = m_angle < ON_PI * 2.0;
217   if( !rc)
218   {
219     if( text_log)
220       text_log->Print( "Angle ( %lf) must be < 2*Pi.\n", m_angle);
221     return false;
222   }
223   rc = m_base != ON_2dPoint( ON_UNSET_VALUE, ON_UNSET_VALUE);
224   if( !rc)
225   {
226     if( text_log)
227       text_log->Print( "Base is not a valid point.\n");
228     return false;
229   }
230   rc = m_offset.x != ON_UNSET_VALUE;
231   if( !rc)
232   {
233     if( text_log)
234       text_log->Print( "Offset is not a valid vector.\n");
235     return false;
236   }
237   rc = m_offset.y > ON_SQRT_EPSILON;
238   if( !rc)
239   {
240     if( text_log)
241       text_log->Print( "Offset.y ( %lf) must be > 0.0", m_offset.y);
242     return false;
243   }
244   return true;
245 }
246 
Dump(ON_TextLog & dump) const247 void ON_HatchLine::Dump( ON_TextLog& dump) const
248 {
249   dump.Print( "ON_HatchLine: angle = %lf radians ( %lf degrees) ",
250     Angle(), ON_RADIANS_TO_DEGREES * Angle());
251   dump.Print( " base = ");
252   dump.Print( m_base);
253   dump.Print( " offset = ");
254   dump.Print( m_offset);
255   int count = m_dashes.Count();
256   dump.Print( "\nDash count = %d: ", count);
257   for( int i = 0; i < count; i++)
258   {
259     dump.Print( "%lf", Dash( i));
260     if( i < count-1)
261       dump.Print( ", ");
262   }
263   dump.Print( "\n");
264 }
265 
Write(ON_BinaryArchive & ar) const266 ON_BOOL32 ON_HatchLine::Write( ON_BinaryArchive& ar) const
267 {
268   ON_BOOL32 rc = ar.Write3dmChunkVersion(1,1);
269 
270   if (rc) rc = ar.WriteDouble( m_angle);
271   if (rc) rc = ar.WritePoint( m_base);
272   if (rc) rc = ar.WriteVector( m_offset);
273   if (rc) rc = ar.WriteArray( m_dashes);
274 
275   return rc;
276 }
277 
Read(ON_BinaryArchive & ar)278 ON_BOOL32 ON_HatchLine::Read( ON_BinaryArchive& ar)
279 {
280   m_angle = 0.0;
281   m_base.Set( 0.0, 0.0);
282   m_offset.Set( 0.0, 1.0);
283   m_dashes.Empty();
284   int major_version = 0;
285   int minor_version = 0;
286   ON_BOOL32 rc = ar.Read3dmChunkVersion( &major_version, &minor_version);
287   if ( major_version == 1 )
288   {
289     if ( rc) rc = ar.ReadDouble( &m_angle);
290     if ( rc) rc = ar.ReadPoint( m_base);
291     if ( rc) rc = ar.ReadVector( m_offset);
292     if ( rc) rc = ar.ReadArray( m_dashes);
293   }
294   return rc;
295 }
296 
297 // ON_HatchLine Interface
Angle() const298 double ON_HatchLine::Angle() const
299 {
300   return m_angle;
301 }
302 
SetAngle(double angle)303 void ON_HatchLine::SetAngle( double angle)
304 {
305   m_angle = angle;
306   double twopi = ON_PI * 2.0;
307 
308   // clamp between [0  2pi)
309   while( m_angle < 0.0)
310     m_angle += twopi;
311   while( m_angle > twopi)
312     m_angle -= twopi;
313 }
314 
Base() const315 ON_2dPoint ON_HatchLine::Base() const
316 {
317   return m_base;
318 }
319 
SetBase(const ON_2dPoint & base)320 void ON_HatchLine::SetBase( const ON_2dPoint& base)
321 {
322   m_base = base;
323 }
324 
Offset() const325 ON_2dVector ON_HatchLine::Offset() const
326 {
327   return m_offset;
328 }
329 
SetOffset(const ON_2dVector & offset)330 void ON_HatchLine::SetOffset( const ON_2dVector& offset)
331 {
332   m_offset = offset;
333 }
334 
DashCount() const335 int ON_HatchLine::DashCount() const
336 {
337   return m_dashes.Count();
338 }
339 
Dash(int index) const340 double ON_HatchLine::Dash( int index) const
341 {
342   if( index >= 0 && index < m_dashes.Count())
343     return m_dashes[index];
344   return 0.0;
345 }
346 
AppendDash(double dash)347 void ON_HatchLine::AppendDash( double dash)
348 {
349 //  if( fabs( dash) > ON_SQRT_EPSILON)
350     m_dashes.Append( dash);
351 }
352 
SetPattern(const ON_SimpleArray<double> & dashes)353 void ON_HatchLine::SetPattern( const ON_SimpleArray<double>& dashes)
354 {
355   m_dashes = dashes;
356 }
357 
GetLineData(double & angle,ON_2dPoint & base,ON_2dVector & offset,ON_SimpleArray<double> & dashes) const358 void ON_HatchLine::GetLineData( double& angle,
359                                 ON_2dPoint& base,
360                                 ON_2dVector& offset,
361                                 ON_SimpleArray<double>& dashes) const
362 {
363   angle = m_angle;
364   base = m_base;
365   offset = m_offset;  dashes = m_dashes;
366 }
367 
GetPatternLength() const368 double ON_HatchLine::GetPatternLength() const
369 {
370   int i;
371   double length = 0.0;
372   for( i = 0; i < m_dashes.Count(); i++)
373     length += fabs( m_dashes[i]);
374 
375   return length;
376 }
377 
378 
379 //  class ON_HatchPattern
380 /////////////////////////////////////////////////////////////////
381 ON_OBJECT_IMPLEMENT( ON_HatchPattern, ON_Object, "064E7C91-35F6-4734-A446-79FF7CD659E1" );
382 
ON_HatchPattern()383 ON_HatchPattern::ON_HatchPattern()
384 : m_hatchpattern_index(-1)
385 , m_hatchpattern_id(ON_nil_uuid)
386 , m_type(ON_HatchPattern::ftSolid)
387 {
388 }
389 
~ON_HatchPattern()390 ON_HatchPattern::~ON_HatchPattern()
391 {
392 }
393 
IsValid(ON_TextLog * text_log) const394 ON_BOOL32 ON_HatchPattern::IsValid( ON_TextLog* text_log) const
395 {
396   eFillType type = FillType();
397   ON_BOOL32 rc = true;
398   if( type != ftSolid && type != ftLines && type != ftGradient)
399   {
400     if( text_log)
401       text_log->Print( "Type field not set correctly.\n");
402     rc = false;
403   }
404   if( type == ftLines)
405   {
406     int count = m_lines.Count();
407     if( count < 1)
408     {
409       if( text_log)
410         text_log->Print( "Line type patetern with no lines.\n");
411       return false;
412     }
413     for( int i = 0; i < count; i++)
414     {
415       if( !m_lines[i].IsValid())
416       {
417         if( text_log)
418           text_log->Print( "Line[%d] is not valid.\n", i);
419         return false;
420       }
421     }
422     return true;
423   }
424   return rc;
425 }
426 
Dump(ON_TextLog & dump) const427 void ON_HatchPattern::Dump( ON_TextLog& dump) const
428 {
429   dump.Print( "Hatch pattern - ");
430   switch( m_type)
431   {
432   case ftSolid:
433     dump.Print( "fill type: Solid");
434     break;
435   case ftLines:
436     dump.Print( "fill type: Lines");
437     break;
438   case ftGradient:
439     dump.Print( "fill type: Gradient");
440     break;
441   case ftLast:
442     // no action, but this keeps gcc happy
443     break;
444   }
445   dump.Print( "\n");
446 
447   const wchar_t* wsHatchPatternName = m_hatchpattern_name;
448   if ( 0 == wsHatchPatternName )
449     wsHatchPatternName = L"";
450   dump.Print( "Name: %ls\n", wsHatchPatternName);
451 
452   const wchar_t* wsDescription =  m_description;
453   if ( 0 == wsDescription )
454     wsDescription = L"";
455   dump.Print( "Description: %ls\n", wsDescription);
456 
457   if( m_type == ftLines)
458   {
459     int count = m_lines.Count();
460     dump.Print( "Line count = %d\n", count);
461     for( int i = 0; i < count; i++)
462     {
463       m_lines[i].Dump( dump);
464     }
465     dump.Print( "\n");
466   }
467 }
468 
Write(ON_BinaryArchive & ar) const469 ON_BOOL32 ON_HatchPattern::Write( ON_BinaryArchive& ar) const
470 {
471   ON_BOOL32 rc = ar.Write3dmChunkVersion(1,2);
472 
473   if (rc) rc = ar.WriteInt( m_hatchpattern_index);
474   if (rc) rc = ar.WriteInt( m_type);
475   if (rc) rc = ar.WriteString( m_hatchpattern_name);
476   if (rc) rc = ar.WriteString( m_description);
477   if( rc)
478   {
479     if( m_type == ftLines)
480     {
481       int i, count = m_lines.Count();
482       if ( count < 0 )
483         count = 0;
484       rc = ar.WriteInt( count );
485       for( i = 0; i < count && rc; i++)
486         rc = m_lines[i].Write( ar);
487     }
488   }
489   // version 1.2 field
490   if (rc) rc = ar.WriteUuid(m_hatchpattern_id);
491 
492   return rc;
493 }
494 
Read(ON_BinaryArchive & ar)495 ON_BOOL32 ON_HatchPattern::Read( ON_BinaryArchive& ar)
496 {
497   m_hatchpattern_index = -1;
498   memset(&m_hatchpattern_id,0,sizeof(m_hatchpattern_id));
499   m_type = ftSolid;
500   m_hatchpattern_name.Empty();
501   m_description.Empty();
502   m_lines.Empty();
503   int i;
504 
505   int major_version = 0;
506   int minor_version = 0;
507   ON_BOOL32 rc = ar.Read3dmChunkVersion( &major_version, &minor_version);
508   if ( major_version == 1 )
509   {
510     if( rc) rc = ar.ReadInt( &m_hatchpattern_index);
511     i = 0;
512     if( rc) rc = ar.ReadInt( &i);
513     if( rc)
514     {
515       switch( i)
516       {
517       case 0:  m_type = ftSolid;    break;
518       case 1:  m_type = ftLines;    break;
519       case 2:  m_type = ftGradient; break;
520       default: rc = false;          break;
521       }
522     }
523     if( rc) rc = ar.ReadString( m_hatchpattern_name);
524     if( rc) rc = ar.ReadString( m_description);
525     if( rc)
526     {
527       if( m_type == ftLines)
528       {
529         m_lines.Empty();
530         int count = 0;
531         rc = ar.ReadInt( &count);
532         if( rc && count > 0 )
533         {
534           m_lines.SetCapacity( count);
535           int i;
536           for( i = 0; rc && i < count; i++)
537           {
538             ON_HatchLine& line = m_lines.AppendNew();
539             rc = line.Read( ar);
540           }
541         }
542       }
543     }
544     if ( minor_version >= 2 )
545     {
546       rc = ar.ReadUuid(m_hatchpattern_id);
547     }
548   }
549   return rc;
550 }
551 
FillType() const552 ON_HatchPattern::eFillType ON_HatchPattern::FillType() const
553 {
554   if( m_type >= ftSolid && m_type < ftLast)
555     return m_type;
556 
557   return ftLast;
558 }
559 
SetFillType(eFillType type)560 void ON_HatchPattern::SetFillType( eFillType type)
561 {
562   m_type = type;
563 }
564 
SetName(const wchar_t * pName)565 void ON_HatchPattern::SetName( const wchar_t* pName)
566 {
567   m_hatchpattern_name = pName;
568   m_hatchpattern_name.TrimLeftAndRight();
569 }
570 
SetName(const char * pName)571 void ON_HatchPattern::SetName( const char* pName)
572 {
573   m_hatchpattern_name = pName;
574   m_hatchpattern_name.TrimLeftAndRight();
575 }
576 
GetName(ON_wString & string) const577 void ON_HatchPattern::GetName( ON_wString& string) const
578 {
579   string = m_hatchpattern_name;
580 }
581 
Name() const582 const wchar_t* ON_HatchPattern::Name() const
583 {
584   return m_hatchpattern_name;
585 }
586 
587 
SetDescription(const wchar_t * pDescription)588 void ON_HatchPattern::SetDescription( const wchar_t* pDescription)
589 {
590   m_description = pDescription;
591 }
592 
SetDescription(const char * pDescription)593 void ON_HatchPattern::SetDescription( const char* pDescription)
594 {
595   m_description = pDescription;
596 }
597 
GetDescription(ON_wString & string) const598 void ON_HatchPattern::GetDescription( ON_wString& string) const
599 {
600   string = m_description;
601 }
602 
Description() const603 const wchar_t* ON_HatchPattern::Description() const
604 {
605   return m_description;
606 }
607 
608 
SetIndex(int i)609 void ON_HatchPattern::SetIndex( int i)
610 {
611   m_hatchpattern_index = i;
612 }
613 
Index() const614 int ON_HatchPattern::Index() const
615 {
616   return m_hatchpattern_index;
617 }
618 
619 
620 //  Line HatchPattern functions
621 
HatchLineCount() const622 int ON_HatchPattern::HatchLineCount() const
623 {
624   return m_lines.Count();
625 }
626 
AddHatchLine(const ON_HatchLine & line)627 int ON_HatchPattern::AddHatchLine( const ON_HatchLine& line)
628 {
629   m_lines.Append( line);
630   return m_lines.Count()-1;
631 }
632 
HatchLine(int index) const633 const ON_HatchLine* ON_HatchPattern::HatchLine( int index) const
634 {
635   if( index >= 0 && index < m_lines.Count())
636     return &m_lines[index];
637   else
638     return NULL;
639 }
640 
RemoveHatchLine(int index)641 bool ON_HatchPattern::RemoveHatchLine( int index)
642 {
643   if( index >= 0 && index < m_lines.Count())
644   {
645     m_lines.Remove( index);
646     return true;
647   }
648   return false;
649 }
650 
RemoveAllHatchLines()651 void ON_HatchPattern::RemoveAllHatchLines()
652 {
653   m_lines.Empty();
654 }
655 
SetHatchLines(const ON_ClassArray<ON_HatchLine> lines)656 int ON_HatchPattern::SetHatchLines( const ON_ClassArray<ON_HatchLine> lines)
657 {
658   m_lines = lines;
659   return m_lines.Count();
660 }
661 
662 
663 
664 
665 //  class ON_HatchLoop
666 /////////////////////////////////////////////////////////////////
667 
668 #if defined(ON_DLL_EXPORTS)
669 
670 // When the Microsoft CRT(s) is/are used, this is the best
671 // way to prevent crashes that happen when a hatch loop is
672 // allocated with new in one DLL and deallocated with
673 // delete in another DLL.
674 
operator new(size_t sz)675 void* ON_HatchLoop::operator new(size_t sz)
676 {
677   // ON_HatchLoop new
678   return onmalloc(sz);
679 }
680 
operator delete(void * p)681 void ON_HatchLoop::operator delete(void* p)
682 {
683   // ON_HatchLoop delete
684   onfree(p);
685 }
686 
operator new[](size_t sz)687 void* ON_HatchLoop::operator new[] (size_t sz)
688 {
689   // ON_HatchLoop array new
690   return onmalloc(sz);
691 }
692 
operator delete[](void * p)693 void ON_HatchLoop::operator delete[] (void* p)
694 {
695   // ON_HatchLoop array delete
696   onfree(p);
697 }
698 
operator new(size_t,void * p)699 void* ON_HatchLoop::operator new(size_t, void* p)
700 {
701   // ON_HatchLoop placement new
702   return p;
703 }
704 
operator delete(void *,void *)705 void ON_HatchLoop::operator delete(void*, void*)
706 {
707   // ON_HatchLoop placement delete
708   return;
709 }
710 
711 #endif
712 
713 
ON_HatchLoop()714 ON_HatchLoop::ON_HatchLoop()
715 : m_type( ON_HatchLoop::ltOuter), m_p2dCurve( NULL)
716 {
717 }
718 
ON_HatchLoop(ON_Curve * pCurve2d,eLoopType type)719 ON_HatchLoop::ON_HatchLoop( ON_Curve* pCurve2d, eLoopType type)
720 : m_type( type), m_p2dCurve( pCurve2d)
721 {
722 }
723 
ON_HatchLoop(const ON_HatchLoop & src)724 ON_HatchLoop::ON_HatchLoop( const ON_HatchLoop& src)
725 : m_type( src.m_type), m_p2dCurve( NULL)
726 {
727   if( src.m_p2dCurve)
728     m_p2dCurve = src.m_p2dCurve->DuplicateCurve();
729 }
730 
~ON_HatchLoop()731 ON_HatchLoop::~ON_HatchLoop()
732 {
733   delete m_p2dCurve;
734 }
735 
operator =(const ON_HatchLoop & src)736 ON_HatchLoop& ON_HatchLoop::operator=( const ON_HatchLoop& src)
737 {
738   if( this != &src)
739   {
740     if( m_p2dCurve)
741       delete m_p2dCurve;
742     m_p2dCurve = src.m_p2dCurve->DuplicateCurve();
743 
744     m_type = src.m_type;
745   }
746   return *this;
747 }
748 
IsValid(ON_TextLog * text_log) const749 ON_BOOL32 ON_HatchLoop::IsValid( ON_TextLog* text_log) const
750 {
751   ON_BOOL32 rc = m_p2dCurve != NULL;
752   if( !rc)
753   {
754     if( text_log)
755       text_log->Print( "2d loop curve is NULL\n");
756   }
757   if( rc)
758   {
759     rc = m_p2dCurve->IsValid( text_log);
760     if( !rc)
761     {
762       if( text_log)
763         text_log->Print( "Loop curve is not valid\n");
764     }
765   }
766 
767   if( rc)
768   {
769     ON_BoundingBox box;
770     m_p2dCurve->GetBoundingBox( box);
771     rc = ( box.Max().z == box.Min().z && box.Max().z == 0.0);
772     if( !rc)
773     {
774       if( text_log)
775         text_log->Print( "2d loop curve has non-zero z coordinates\n");
776     }
777   }
778 
779   if( rc && m_type != ltOuter && m_type != ltInner)
780   {
781     if( text_log)
782       text_log->Print( "Loop type is invalid.\n");
783     rc = false;
784   }
785 
786   return rc;
787 }
788 
Dump(ON_TextLog & dump) const789 void ON_HatchLoop::Dump( ON_TextLog& dump) const
790 {
791   if( m_type == ltOuter)
792     dump.Print( "Outer hatch loop\n");
793   if( m_type == ltInner)
794     dump.Print( "Inner hatch loop\n");
795 
796   if ( 0 == m_p2dCurve )
797   {
798     dump.Print( "2d curve: null pointer\n");
799   }
800   else
801   {
802     dump.Print( "2d curve:\n");
803     m_p2dCurve->Dump(dump);
804   }
805 
806 }
807 
Write(ON_BinaryArchive & ar) const808 ON_BOOL32 ON_HatchLoop::Write( ON_BinaryArchive& ar) const
809 {
810   ON_BOOL32 rc = ar.Write3dmChunkVersion(1,1);
811   if( rc) rc = ar.WriteInt( m_type);
812   if( rc) rc = ar.WriteObject( m_p2dCurve);
813   return rc;
814 }
815 
Read(ON_BinaryArchive & ar)816 ON_BOOL32 ON_HatchLoop::Read( ON_BinaryArchive& ar)
817 {
818   m_type = ltOuter;
819   delete m_p2dCurve;
820   m_p2dCurve = NULL;
821   int major_version = 0;
822   int minor_version = 0;
823   ON_BOOL32 rc = ar.Read3dmChunkVersion( &major_version, &minor_version);
824   if ( major_version == 1 )
825   {
826     int type = 0;
827     if( rc) rc = ar.ReadInt( &type);
828     if( rc)
829     {
830       switch( type)
831       {
832       case ltOuter:  m_type = ltOuter; break;
833       case ltInner:  m_type = ltInner; break;
834       default: rc = false; break;
835       }
836     }
837     if( rc)
838     {
839       ON_Object* pObj = NULL;
840       rc = ar.ReadObject( &pObj);
841       if( pObj)
842       {
843         m_p2dCurve = ON_Curve::Cast( pObj);
844         if( !m_p2dCurve) // read something, but it wasn't right
845         {
846           rc = false;
847           delete pObj;
848         }
849       }
850     }
851   }
852   return rc;
853 }
854 
Curve() const855 const ON_Curve* ON_HatchLoop::Curve() const
856 {
857   return m_p2dCurve;
858 }
859 
SetCurve(const ON_Curve & curve)860 bool ON_HatchLoop::SetCurve( const ON_Curve& curve)
861 {
862   ON_Curve* pC = curve.DuplicateCurve();
863   if( pC)
864   {
865     if(pC->Dimension() == 3 && !pC->ChangeDimension(2))
866       return false;
867 
868     if( m_p2dCurve)
869       delete m_p2dCurve;
870     m_p2dCurve = pC;
871   }
872   return true;
873 }
Type() const874 ON_HatchLoop::eLoopType ON_HatchLoop::Type() const
875 {
876   return m_type;
877 }
878 
SetType(eLoopType type)879 void ON_HatchLoop::SetType( eLoopType type)
880 {
881   m_type = type;
882 }
883 
884 //  class ON_Hatch
885 /////////////////////////////////////////////////////////////////
886 ON_OBJECT_IMPLEMENT( ON_Hatch, ON_Geometry, "0559733B-5332-49d1-A936-0532AC76ADE5");
887 
888 
ON_Hatch()889 ON_Hatch::ON_Hatch()
890 : m_pattern_scale( 1.0),
891   m_pattern_rotation( 0.0),
892   m_pattern_index( -1)
893 {
894 }
895 
ON_Hatch(const ON_Hatch & src)896 ON_Hatch::ON_Hatch( const ON_Hatch& src)
897 :  ON_Geometry(src),
898    m_plane( src.m_plane),
899    m_pattern_scale( src.m_pattern_scale),
900    m_pattern_rotation( src.m_pattern_rotation),
901    m_pattern_index( src.m_pattern_index)
902 {
903   m_loops.Reserve( src.m_loops.Count());
904   for( int i = 0; i < src.m_loops.Count(); i++)
905   {
906     ON_HatchLoop* pL = new ON_HatchLoop( *src.m_loops[i]);
907     m_loops.Append( pL);
908   }
909 }
910 
operator =(const ON_Hatch & src)911 ON_Hatch& ON_Hatch::operator=( const ON_Hatch& src)
912 {
913   if( this != &src)
914   {
915     // Nov 3 2004 Dale Lear:
916     //   Delete existing loops so we don't leak the memory;
917     int i;
918     for ( i = 0; i < m_loops.Count(); i++ )
919     {
920       ON_HatchLoop* pL = m_loops[i];
921       if ( pL )
922       {
923         m_loops[i] = 0;
924         delete pL;
925       }
926     }
927     m_loops.SetCount(0);
928 
929     ON_Geometry::operator =(src);
930 
931     m_plane = src.m_plane;
932     m_pattern_index = src.m_pattern_index;
933     m_pattern_scale = src.m_pattern_scale;
934     m_pattern_rotation = src.m_pattern_rotation;
935     m_loops.Reserve( src.m_loops.Count());
936     for( i = 0; i < src.m_loops.Count(); i++)
937     {
938       ON_HatchLoop* pL = new ON_HatchLoop( *src.m_loops[i]);
939       m_loops.Append( pL);
940     }
941   }
942   return *this;
943 }
944 
~ON_Hatch()945 ON_Hatch::~ON_Hatch()
946 {
947   int i;
948   for ( i = 0; i < m_loops.Count(); i++ )
949   {
950     ON_HatchLoop* pL = m_loops[i];
951     if ( pL )
952     {
953       m_loops[i] = 0;
954       delete pL;
955     }
956   }
957 }
958 
959 
DuplicateHatch() const960 ON_Hatch* ON_Hatch::DuplicateHatch() const
961 {
962   return Duplicate();
963 }
964 
IsValid(ON_TextLog * text_log) const965 ON_BOOL32 ON_Hatch::IsValid( ON_TextLog* text_log) const
966 {
967   ON_BOOL32 rc = m_plane.IsValid();
968   if( !rc)
969   {
970     if( text_log)
971       text_log->Print( "Plane is not valid\n");
972     return false;
973   }
974   // 18 June 2012 - Lowell - Added loop self-intersection and
975   // intersecting other loops tests
976   int count = m_loops.Count();
977   for(int i = 0; i < count; i++)
978   {
979     if(m_loops[i] == 0)
980     {
981       if( text_log)
982         text_log->Print( "Loop[%d] is NULL\n", i);
983       return false;
984     }
985     if(rc)
986       rc = m_loops[i]->IsValid( text_log);
987     if( !rc)
988     {
989       if( text_log)
990         text_log->Print( "Loop[%d] is not valid\n", i);
991       return false;
992     }
993   }
994 
995   return true;
996 }
997 
Dump(ON_TextLog & dump) const998 void ON_Hatch::Dump( ON_TextLog& dump) const
999 {
1000   dump.Print("Hatch: Pattern index: %d\n", PatternIndex());
1001   dump.Print("Pattern rotation: %g\n", PatternRotation());
1002   dump.Print("Pattern scale: %g\n", PatternScale());
1003   ON_3dPoint p = this->BasePoint();
1004   dump.Print("Base point: %g, %g, %g\n", p.x, p.y, p.z);
1005   dump.Print("Plane origin: %g, %g, %g\n", m_plane.origin.x, m_plane.origin.y, m_plane.origin.z);
1006   dump.Print("Plane x axis: %g, %g, %g\n", m_plane.xaxis.x, m_plane.xaxis.y, m_plane.xaxis.z);
1007   dump.Print("Plane y axis: %g, %g, %g\n", m_plane.yaxis.x, m_plane.yaxis.y, m_plane.yaxis.z);
1008   dump.Print("Plane z axis: %g, %g, %g\n", m_plane.zaxis.x, m_plane.zaxis.y, m_plane.zaxis.z);
1009   int count = m_loops.Count();
1010   dump.Print("Loop count = %d\n", count);
1011   for( int i = 0; i < count; i++)
1012     m_loops[i]->Dump( dump);
1013 }
1014 
Write(ON_BinaryArchive & ar) const1015 ON_BOOL32 ON_Hatch::Write( ON_BinaryArchive& ar) const
1016 {
1017   ON_BOOL32 rc = ar.Write3dmChunkVersion(1,1);
1018   if (rc) rc = ar.WritePlane( m_plane);
1019   if (rc) rc = ar.WriteDouble( m_pattern_scale);
1020   if (rc) rc = ar.WriteDouble( m_pattern_rotation);
1021   if (rc) rc = ar.WriteInt( m_pattern_index);
1022   if (rc)
1023   {
1024     int i, count = m_loops.Count();
1025     if( count < 0 )
1026       count = 0;
1027     ON_BOOL32 rc = ar.WriteInt( count);
1028     for( i = 0; i < count && rc; i++)
1029       rc = m_loops[i]->Write( ar);
1030   }
1031   return rc;
1032 }
1033 
Read(ON_BinaryArchive & ar)1034 ON_BOOL32 ON_Hatch::Read( ON_BinaryArchive& ar)
1035 {
1036   m_plane.CreateFromNormal( ON_origin, ON_zaxis);
1037   m_pattern_scale = 1.0;
1038   m_pattern_rotation = 0.0;
1039   m_pattern_index = -1;
1040   m_loops.Empty();
1041   int major_version = 0;
1042   int minor_version = 0;
1043   ON_BOOL32 rc = ar.Read3dmChunkVersion( &major_version, &minor_version);
1044   if ( major_version == 1 )
1045   {
1046     if( rc) rc = ar.ReadPlane( m_plane);
1047     if( rc) rc = ar.ReadDouble( &m_pattern_scale);
1048     if( rc) rc = ar.ReadDouble( &m_pattern_rotation);
1049     if( rc) rc = ar.ReadInt( &m_pattern_index);
1050     if( rc)
1051     {
1052       m_loops.Empty();
1053       int i, count = 0;
1054       rc = ar.ReadInt( &count);
1055       if( rc && count > 0)
1056       {
1057         m_loops.SetCapacity( count );
1058         for( i = 0; rc && i < count; i++)
1059         {
1060           ON_HatchLoop*& pLoop = m_loops.AppendNew();
1061           pLoop = new ON_HatchLoop;
1062           if( pLoop)
1063             rc = pLoop->Read( ar);
1064           else
1065             rc = false;
1066         }
1067       }
1068     }
1069   }
1070   return rc;
1071 }
1072 
ObjectType() const1073 ON::object_type ON_Hatch::ObjectType() const
1074 {
1075   return ON::hatch_object;
1076 }
1077 
Dimension() const1078 int ON_Hatch::Dimension() const
1079 {
1080   return 3;
1081 }
1082 
1083 // Copy the 2d curve, make it 3d, and transform it
1084 // to the 3d plane position
LoopCurve3d(int index) const1085 ON_Curve* ON_Hatch::LoopCurve3d( int index) const
1086 {
1087   int count = m_loops.Count();
1088   ON_Curve* pC = NULL;
1089 
1090   if( index >= 0 && index < count)
1091   {
1092     if( m_loops[index]->Curve())
1093     {
1094       pC = m_loops[index]->Curve()->DuplicateCurve();
1095       if( pC)
1096       {
1097         pC->ChangeDimension( 3);
1098 
1099         ON_Xform xf;
1100         xf.Rotation( ON_xy_plane, m_plane);
1101 
1102         pC->Transform( xf);
1103       }
1104     }
1105   }
1106   return pC;
1107 }
1108 
1109 
PatternIndex() const1110 int ON_Hatch::PatternIndex() const
1111 {
1112   return m_pattern_index;
1113 }
1114 
SetPatternIndex(int index)1115 void ON_Hatch::SetPatternIndex( int index)
1116 {
1117   m_pattern_index = index;
1118 }
1119 
1120 
GetBBox(double * bmin,double * bmax,ON_BOOL32 bGrowBox) const1121 ON_BOOL32 ON_Hatch::GetBBox( double* bmin, double* bmax, ON_BOOL32 bGrowBox) const
1122 {
1123   int i;
1124   int count = m_loops.Count();
1125   ON_BOOL32 rc = true;
1126   ON_Curve* pC;
1127   for( i = 0; rc && i < count; i++)
1128   {
1129     pC = LoopCurve3d( i);
1130     if( pC)
1131     {
1132       rc = pC->GetBBox( bmin, bmax, i?true:bGrowBox);
1133       delete pC;
1134     }
1135   }
1136   return rc;
1137 }
1138 
GetTightBoundingBox(ON_BoundingBox & tight_bbox,int bGrowBox,const ON_Xform * xform) const1139 bool ON_Hatch::GetTightBoundingBox( ON_BoundingBox& tight_bbox, int bGrowBox, const ON_Xform* xform) const
1140 {
1141   int i;
1142   int count = m_loops.Count();
1143   ON_CurveArray curves(count);
1144   for( i = 0; i < count; i++)
1145   {
1146     curves.Append( LoopCurve3d(i) );
1147   }
1148   return curves.GetTightBoundingBox(tight_bbox,bGrowBox,xform);
1149 }
1150 
Angle3d(const ON_3dVector & axis,ON_3dVector & from,const ON_3dVector & to)1151 static double Angle3d(const ON_3dVector& axis, ON_3dVector& from, const ON_3dVector& to)
1152 {
1153   ON_3dVector x = from, a = to;
1154   x.Unitize();
1155   a.Unitize();
1156 
1157   ON_3dVector y = ON_CrossProduct(axis, from);
1158   y.Unitize();
1159 
1160   double cosa = x * a;
1161 
1162   if(cosa > 1.0 - ON_SQRT_EPSILON)
1163     return 0.0;
1164   if(cosa < ON_SQRT_EPSILON - 1.0)
1165     return ON_PI;
1166 
1167   double sina = a * y;
1168 
1169   return atan2(sina, cosa);
1170 }
1171 
1172 
1173 #define ARBBOUND  0.015625
arbaxis(const ON_3dVector & givenaxis,ON_3dVector & newaxis)1174 void arbaxis(const ON_3dVector& givenaxis, ON_3dVector& newaxis)
1175 {
1176   if(fabs(givenaxis[0]) < ARBBOUND && fabs(givenaxis[1]) < ARBBOUND) // near world z
1177     newaxis = ON_CrossProduct(ON_yaxis, givenaxis);
1178   else
1179     newaxis = ON_CrossProduct(ON_zaxis, givenaxis);
1180 
1181   newaxis.Unitize();
1182 }
1183 
arbaxisRotation(const ON_Plane & plane)1184 double arbaxisRotation(const ON_Plane& plane)
1185 {
1186   // get arbaxis frame and angle of rotation from it
1187   ON_3dVector arbXaxis;
1188   arbaxis(plane.zaxis, arbXaxis);
1189   return Angle3d(plane.zaxis, arbXaxis, plane.xaxis);
1190 }
1191 
1192 // 20 June 2012 - Lowell - rr44706, 68320
1193 // This will find A, the arbaxis direction for the hatch plane
1194 // and rotate the hatch plane by -A and rotate the hatch boundaries
1195 // by A and add A to the hatch rotation.
1196 // The picture will be the same after that, but the way the
1197 // angle is represented will match the way AutoCAD does it
1198 // so hatches can be round-tripped with acad files.
1199 // In addition, after several hatches are rotated by different amounts
1200 // the hatch angles can be set to look the same by setting them all
1201 // to the same pattern rotation
1202 
UnrotateHatch(ON_Hatch * hatch)1203 static void UnrotateHatch(ON_Hatch* hatch)
1204 {
1205   double a = arbaxisRotation(hatch->Plane());
1206   ON_Plane& plane = *(ON_Plane*)(&hatch->Plane());
1207   if(fabs(a) > ON_ZERO_TOLERANCE)
1208   {
1209     plane.Rotate(-a, plane.zaxis);
1210     for(int i = 0; i < hatch->LoopCount(); i++)
1211     {
1212       ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve();
1213       pC->Rotate(a, ON_zaxis, ON_origin);
1214     }
1215     hatch->SetPatternRotation(hatch->PatternRotation()+a);
1216   }
1217   ON_3dPoint P;
1218   plane.ClosestPointTo(ON_origin, &P.x, &P.y);
1219 
1220   if(fabs(P.x) > ON_ZERO_TOLERANCE ||fabs(P.y) > ON_ZERO_TOLERANCE ||fabs(P.z) > ON_ZERO_TOLERANCE)
1221   {
1222     ON_2dVector V(-P.x, -P.y);
1223     for(int i = 0; i < hatch->LoopCount(); i++)
1224     {
1225       ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve();
1226       pC->Translate(V);
1227     }
1228     P = plane.PointAt(P.x, P.y);
1229     plane.origin = P;
1230   }
1231 }
1232 
Transform(const ON_Xform & xform)1233 ON_BOOL32 ON_Hatch::Transform( const ON_Xform& xform)
1234 {
1235   if( fabs( fabs( xform.Determinant()) - 1.0) > 1.0e-4)
1236   {
1237     // xform has a scale component
1238     ON_Plane tmp( m_plane);
1239     tmp.Transform( xform);
1240     ON_Xform A, B, T;
1241     A.Rotation( ON_xy_plane, m_plane);
1242     B.Rotation( tmp, ON_xy_plane);
1243     T = B * xform * A;
1244 
1245     // kill translation and z-scaling
1246     T[0][2] = T[0][3] = 0.0;
1247     T[1][2] = T[1][3] = 0.0;
1248     T[2][0] = T[2][1] = 0.0; T[2][2] = 1.0; T[2][3] = 0.0;
1249     T[3][0] = T[3][1] = T[3][2] = 0.0; T[3][3] = 1.0;
1250 
1251     for( int i = 0; i < LoopCount(); i++)
1252       m_loops[i]->m_p2dCurve->Transform( T);
1253   }
1254   int rc = m_plane.Transform( xform);
1255 
1256   UnrotateHatch(this);
1257 
1258   TransformUserData(xform);
1259 
1260 
1261 
1262 
1263   return rc;
1264 }
1265 
Create(const ON_Plane & plane,const ON_SimpleArray<const ON_Curve * > loops,int pattern_index,double pattern_rotation,double pattern_scale)1266 bool ON_Hatch::Create( const ON_Plane& plane,
1267                        const ON_SimpleArray<const ON_Curve*> loops,
1268                        int pattern_index,
1269                        double pattern_rotation,
1270                        double pattern_scale)
1271 {
1272   if( loops.Count() < 1)
1273     return false;
1274   if( pattern_index < 0)
1275     return false;
1276   SetPlane( plane);
1277   for( int i = 0; i < loops.Count(); i++)
1278   {
1279     ON_HatchLoop* pLoop = new ON_HatchLoop;
1280     pLoop->SetCurve( *loops[i]);
1281     pLoop->SetType( i?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter);
1282     AddLoop( pLoop);
1283   }
1284   SetPatternIndex( pattern_index);
1285   SetPatternRotation( pattern_rotation);
1286   SetPatternScale( pattern_scale);
1287   return true;
1288 }
1289 
Plane() const1290 const ON_Plane& ON_Hatch::Plane() const
1291 {
1292   return m_plane;
1293 }
1294 
SetPlane(const ON_Plane & plane)1295 void ON_Hatch::SetPlane( const ON_Plane& plane)
1296 {
1297   m_plane = plane;
1298 }
1299 
PatternRotation() const1300 double ON_Hatch::PatternRotation() const
1301 {
1302   return m_pattern_rotation;
1303 }
1304 
SetPatternRotation(double rotation)1305 void ON_Hatch::SetPatternRotation( double rotation)
1306 {
1307   m_pattern_rotation = rotation;
1308 }
1309 
PatternScale() const1310 double ON_Hatch::PatternScale() const
1311 {
1312   return m_pattern_scale;
1313 }
1314 
SetPatternScale(double scale)1315 void ON_Hatch::SetPatternScale( double scale)
1316 {
1317   if( scale > 0.001) // Changed May 13, 2009 - Lowell - rr39185
1318     m_pattern_scale = scale;
1319 }
1320 
LoopCount() const1321 int ON_Hatch::LoopCount() const
1322 {
1323   return m_loops.Count();
1324 }
1325 
AddLoop(ON_HatchLoop * pLoop)1326 void ON_Hatch::AddLoop( ON_HatchLoop* pLoop)
1327 {
1328   m_loops.Append( pLoop);
1329 }
1330 
InsertLoop(int index,ON_HatchLoop * loop)1331 bool ON_Hatch::InsertLoop( int index, ON_HatchLoop* loop)
1332 {
1333   if( index >= 0 && index <= m_loops.Count()) // 26 June 2012 - Lowell - Changed ndex < to ndex <=
1334   {
1335     m_loops.Insert(index, loop);
1336 	return true;
1337   }
1338 
1339   return false;
1340 }
1341 
RemoveLoop(int index)1342 bool ON_Hatch::RemoveLoop( int index)
1343 {
1344   if( index >= 0 && index < m_loops.Count())
1345   {
1346     delete m_loops[index];
1347     m_loops.Remove(index);
1348     return true;
1349   }
1350 
1351   return false;
1352 }
1353 
1354 
ReplaceLoops(ON_SimpleArray<const ON_Curve * > loop_curves)1355 bool ON_Hatch::ReplaceLoops(ON_SimpleArray<const ON_Curve*> loop_curves)
1356 {
1357   if(loop_curves.Count() < 1)
1358     return false;
1359 
1360   bool rc = true;
1361   ON_Xform xf;
1362   bool flat = false;
1363   ON_SimpleArray<ON_HatchLoop*> loops;
1364 
1365   for(int i = 0; i < loop_curves.Count(); i++)
1366   {
1367     if(loop_curves[i] == 0)
1368     {
1369       rc = false;
1370       break;
1371     }
1372     ON_Curve* p2d = loop_curves[i]->Duplicate();
1373     if(p2d == 0)
1374     {
1375       rc = false;
1376       break;
1377     }
1378     if(p2d->Dimension() == 3)
1379     {
1380       if(!flat)
1381       {
1382         xf.PlanarProjection(m_plane);
1383         flat = true;
1384       }
1385       if(!p2d->Transform(xf) ||
1386          !p2d->ChangeDimension(2))
1387       {
1388         delete p2d;
1389         rc = false;
1390         break;
1391       }
1392     }
1393     ON_HatchLoop* loop = new ON_HatchLoop(p2d,loops.Count()?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter);
1394     if(loop)
1395       loops.Append(loop);
1396     else
1397       delete p2d;
1398   }
1399   if(!rc)
1400   {
1401     for(int i = 0; i < loops.Count(); i++)
1402       delete loops[i];
1403 
1404     loops.Empty();
1405   }
1406 
1407   if(loops.Count() < 1)
1408     return false;
1409 
1410   for(int i = 0; i < m_loops.Count(); i++)
1411     delete m_loops[i];
1412   m_loops.Empty();
1413   for(int i = 0; i < loops.Count(); i++)
1414     m_loops.Append(loops[i]);
1415   return true;
1416 }
1417 
Loop(int index) const1418 const ON_HatchLoop* ON_Hatch::Loop( int index) const
1419 {
1420   if( index >= 0 && index < m_loops.Count())
1421     return m_loops[index];
1422 
1423   return NULL;
1424 }
1425 
1426 // Basepoint functions added March 23, 2008 -LW
SetBasePoint(ON_2dPoint basepoint)1427 void ON_Hatch::SetBasePoint(ON_2dPoint basepoint)
1428 {
1429   ON_HatchExtra* pE = ON_HatchExtra::HatchExtension(this,true);
1430   if(pE)
1431   {
1432     pE->SetBasePoint(basepoint);
1433   }
1434 }
1435 
SetBasePoint(ON_3dPoint point)1436 void ON_Hatch::SetBasePoint(ON_3dPoint point)
1437 {
1438   ON_HatchExtra* pE = ON_HatchExtra::HatchExtension(this,true);
1439   if(pE)
1440   {
1441     ON_2dPoint base;
1442     if(m_plane.ClosestPointTo(point, &base.x, &base.y))
1443       pE->SetBasePoint(base);
1444   }
1445 }
1446 
BasePoint() const1447 ON_3dPoint ON_Hatch::BasePoint() const
1448 {
1449   ON_3dPoint point(ON_origin);
1450   const ON_HatchExtra* pE = ON_HatchExtra::HatchExtension(this,false);
1451   if(pE)
1452   {
1453     ON_2dPoint base = pE->BasePoint();
1454     point = m_plane.PointAt(base.x, base.y);
1455   }
1456   return point;
1457 }
1458 
BasePoint2d() const1459 ON_2dPoint ON_Hatch::BasePoint2d() const
1460 {
1461   ON_2dPoint basepoint(0.0,0.0);
1462   const ON_HatchExtra* pE = ON_HatchExtra::HatchExtension(this,false);
1463   if(pE)
1464     basepoint = pE->BasePoint();
1465 
1466   return basepoint;
1467 }
1468 
1469 // Added March 23, 2008 -LW
1470 // This function is temporary and will be removed next time the SDK can be modified.
HatchExtension()1471 class ON_HatchExtra* ON_Hatch::HatchExtension()
1472 {
1473   ON_HatchExtra* pExtra = ON_HatchExtra::Cast( GetUserData( ON_HatchExtra::m_ON_HatchExtra_class_id.Uuid()));
1474   return pExtra;
1475 }
1476