opennurbs_annotation2.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 // This define is up here so anybody who want's to defeat the
00020 // bozo vaccine has to be doing it on purpose.
00021 #define BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926
00022 
00023 // Added for v5 - 12-10-2009 LW
00024 ON_OBJECT_IMPLEMENT(ON_TextExtra,ON_UserData,"D90490A5-DB86-49f8-BDA1-9080B1F4E976");
00025 
00026 ON_TextExtra::ON_TextExtra()
00027 {
00028   m_userdata_uuid = ON_TextExtra::m_ON_TextExtra_class_id.Uuid();
00029   m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
00030                                          // The id must be the version 5 id because
00031                                          // V6 SaveAs V5 needs to work, but SaveAs
00032                                          // V4 should not write this userdata.
00033   m_userdata_copycount = 1;
00034   SetDefaults();
00035 }
00036 
00037 ON_TextExtra::~ON_TextExtra()
00038 {
00039 }
00040 
00041 ON_TextExtra* ON_TextExtra::TextExtension(ON_TextEntity2* pText, bool bCreate)
00042 {
00043   ON_TextExtra* pExtra = 0;
00044   if(pText)
00045   {
00046     pExtra = ON_TextExtra::Cast(pText->GetUserData(ON_TextExtra::m_ON_TextExtra_class_id.Uuid()));
00047     if(pExtra == 0 && bCreate)
00048     {
00049       pExtra = new ON_TextExtra;
00050       if(pExtra)
00051       {
00052         if(!pText->AttachUserData(pExtra))
00053         {
00054           delete pExtra;
00055           pExtra = 0;
00056         }
00057       }
00058     }
00059   }
00060   return pExtra;
00061 }
00062 
00063 const
00064 ON_TextExtra* ON_TextExtra::TextExtension(const ON_TextEntity2* pText, bool bCreate)
00065 {
00066   return TextExtension((ON_TextEntity2*)pText, bCreate);
00067 }
00068 
00069 void ON_TextExtra::SetDefaults()
00070 {
00071   m_parent_uuid = ON_nil_uuid;
00072   
00073   m_color_source = 0;
00074   m_mask_color = 0;
00075   m_border_offset = 0.1;
00076 }
00077 
00078 void ON_TextExtra::Dump( ON_TextLog& text_log ) const
00079 {
00080   // do nothing
00081 }
00082 
00083 unsigned int ON_TextExtra::SizeOf() const
00084 {
00085   unsigned int sz = ON_UserData::SizeOf();
00086   sz += sizeof(*this) - sizeof(ON_UserData);
00087   return sz;
00088 }
00089 
00090 ON_BOOL32 ON_TextExtra::Write(ON_BinaryArchive& archive) const
00091 {
00092   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
00093 
00094   if(rc) rc = archive.WriteUuid(m_parent_uuid);
00095   if(rc) rc = archive.WriteBool(m_bDrawMask);
00096   if(rc) rc = archive.WriteInt(m_color_source);
00097   if(rc) rc = archive.WriteColor(m_mask_color);
00098   if(rc) rc = archive.WriteDouble(m_border_offset);
00099 
00100   if(!archive.EndWrite3dmChunk())
00101     rc = false;
00102 
00103   return rc;
00104 }
00105 
00106 ON_BOOL32 ON_TextExtra::Read(ON_BinaryArchive& archive)
00107 {
00108   int major_version = 1;
00109   int minor_version = 0;
00110   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
00111   if(!rc)
00112     return false;
00113   if(major_version != 1)
00114     return false;
00115 
00116   if(rc) rc = archive.ReadUuid(m_parent_uuid);
00117   if(rc) rc = archive.ReadBool(&m_bDrawMask);
00118   if(rc) rc = archive.ReadInt(&m_color_source);
00119   if(rc) rc = archive.ReadColor(m_mask_color);
00120   if(rc) rc = archive.ReadDouble(&m_border_offset);
00121 
00122   if ( !archive.EndRead3dmChunk() )
00123     rc = false;
00124 
00125   return rc;
00126 }
00127 
00128 ON_BOOL32 ON_TextExtra::GetDescription( ON_wString& description)
00129 {
00130   description.Format( "Userdata extension of ON_TextEntity");
00131   return true;
00132 }
00133 
00134 ON_BOOL32 ON_TextExtra::Archive() const
00135 {
00136   // true to write to file
00137   return true;
00138 }
00139 
00140 
00141 ON_UUID ON_TextExtra::ParentUUID() const
00142 {
00143   return m_parent_uuid;
00144 }
00145 
00146 void ON_TextExtra::SetParentUUID( ON_UUID parent_uuid)
00147 {
00148   m_parent_uuid = parent_uuid;
00149 }
00150 
00151 bool ON_TextExtra::DrawTextMask() const
00152 {
00153   return m_bDrawMask;
00154 }
00155 
00156 void ON_TextExtra::SetDrawTextMask(bool bDraw)
00157 {
00158   m_bDrawMask = bDraw;
00159 }
00160 
00161 int ON_TextExtra::MaskColorSource() const
00162 {
00163   return m_color_source;
00164 }
00165 
00166 void ON_TextExtra::SetMaskColorSource(int source)
00167 {
00168   if(source == 1)
00169     m_color_source = 1;
00170   else
00171     m_color_source = 0;
00172 }
00173 
00174 ON_Color ON_TextExtra::MaskColor() const
00175 {
00176   return m_mask_color;
00177 }
00178 
00179 void ON_TextExtra::SetMaskColor(ON_Color color)
00180 {
00181   m_mask_color = color;
00182 }
00183 
00184 double ON_TextExtra::MaskOffsetFactor() const
00185 {
00186   return m_border_offset;
00187 }
00188 
00189 void ON_TextExtra::SetMaskOffsetFactor(double offset)
00190 {
00191   m_border_offset = offset;
00192 }
00193 
00194 //--------------------
00195 
00196 
00197 
00198 // Added for v5 - 4-20-07 LW
00199 ON_OBJECT_IMPLEMENT(ON_DimensionExtra,ON_UserData,"8AD5B9FC-0D5C-47fb-ADFD-74C28B6F661E");
00200 
00201 ON_DimensionExtra::ON_DimensionExtra()
00202 {
00203   m_userdata_uuid = ON_DimensionExtra::m_ON_DimensionExtra_class_id.Uuid();
00204   m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
00205                                          // The id must be the version 5 id because
00206                                          // V6 SaveAs V5 needs to work, but SaveAs
00207                                          // V4 should not write this userdata.
00208   m_userdata_copycount = 1;
00209   SetDefaults();
00210 }
00211 
00212 ON_DimensionExtra::~ON_DimensionExtra()
00213 {
00214 }
00215 
00216 static ON_DimensionExtra* AnnotationExtension(ON_Annotation2* pDim, bool bCreate)
00217 {
00218   ON_DimensionExtra* pExtra = 0;
00219   if(pDim)
00220   {
00221     pExtra = ON_DimensionExtra::Cast(pDim->GetUserData(ON_DimensionExtra::m_ON_DimensionExtra_class_id.Uuid()));
00222     if(pExtra == 0 && bCreate)
00223     {
00224       pExtra = new ON_DimensionExtra;
00225       if( pExtra)
00226       {
00227         if(!pDim->AttachUserData(pExtra))
00228         {
00229           delete pExtra;
00230           pExtra = 0;
00231         }
00232       }
00233     }
00234   }
00235   return pExtra;
00236 }
00237 
00238 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(ON_LinearDimension2* pDim, bool bCreate)
00239 {
00240   return AnnotationExtension((ON_Annotation2*)pDim, bCreate);
00241 }
00242 
00243 const
00244 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(const ON_LinearDimension2* pDim, bool bCreate)
00245 {
00246   return DimensionExtension((ON_LinearDimension2*)pDim, bCreate);
00247 }
00248 
00249 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(ON_RadialDimension2* pDim, bool bCreate)
00250 {
00251   return AnnotationExtension((ON_Annotation2*)pDim, bCreate);
00252 }
00253 
00254 const
00255 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(const ON_RadialDimension2* pDim, bool bCreate)
00256 {
00257   return DimensionExtension((ON_RadialDimension2*)pDim, bCreate);
00258 }
00259 
00260 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(ON_OrdinateDimension2* pDim, bool bCreate)
00261 {
00262   return AnnotationExtension((ON_Annotation2*)pDim, bCreate);
00263 }
00264 
00265 const
00266 ON_DimensionExtra* ON_DimensionExtra::DimensionExtension(const ON_OrdinateDimension2* pDim, bool bCreate)
00267 {
00268   return DimensionExtension((ON_OrdinateDimension2*)pDim, bCreate);
00269 }
00270 
00271 void ON_DimensionExtra::SetDefaults()
00272 {
00273   m_partent_uuid = ON_nil_uuid;
00274   
00275   m_arrow_position = 0;
00276   m_text_rects = 0;
00277   m_distance_scale = 1.0;
00278   m_modelspace_basepoint = ON_origin;
00279 }
00280 
00281 void ON_DimensionExtra::Dump( ON_TextLog& text_log ) const
00282 {
00283   // do nothing
00284 }
00285 
00286 unsigned int ON_DimensionExtra::SizeOf() const
00287 {
00288   unsigned int sz = ON_UserData::SizeOf();
00289   sz += sizeof(*this) - sizeof(ON_UserData);
00290   return sz;
00291 }
00292 
00293 ON_BOOL32 ON_DimensionExtra::Write(ON_BinaryArchive& archive) const
00294 {
00295   int major_version = 1;
00296   int minor_version = 1;
00297   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);
00298 
00299   if(rc) rc = archive.WriteUuid( m_partent_uuid);
00300   if(rc) rc = archive.WriteInt( m_arrow_position);
00301   if(rc)
00302   {
00303     if( m_text_rects)
00304     {
00305       rc = archive.WriteInt( 7);
00306       rc = archive.WriteInt( 28, (const int*)m_text_rects);
00307     }
00308     else
00309       rc = archive.WriteInt( 0);
00310 
00311   }
00312   // 21 June 2010 Added distance scale, minor version 1
00313   if(rc) rc = archive.WriteDouble(m_distance_scale);
00314 
00315   if(!archive.EndWrite3dmChunk())
00316     rc = false;
00317 
00318   return rc;
00319 }
00320 
00321 ON_BOOL32 ON_DimensionExtra::Read(ON_BinaryArchive& archive)
00322 {
00323   int major_version = 1;
00324   int minor_version = 0;
00325   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
00326   if(!rc)
00327     return false;
00328   if(major_version != 1)
00329     return false;
00330 
00331   if(rc) rc = archive.ReadUuid(m_partent_uuid);
00332   if(rc) rc = archive.ReadInt(&m_arrow_position);
00333 
00334   int rect_count = 0;
00335   if(rc) rc = archive.ReadInt( &rect_count);
00336   if( rc && rect_count)
00337     rc = archive.ReadInt( rect_count, (int*)m_text_rects);
00338 
00339   // 21 June 2010 Added distance scale, minor version 1
00340   if(minor_version > 0)
00341   {
00342     if(rc) rc = archive.ReadDouble(&m_distance_scale);
00343   }
00344 
00345   if ( !archive.EndRead3dmChunk() )
00346     rc = false;
00347 
00348   return rc;
00349 }
00350 
00351 ON_BOOL32 ON_DimensionExtra::GetDescription( ON_wString& description)
00352 {
00353   description.Format( "Userdata extension of ON_Dimensions");
00354   return true;
00355 }
00356 
00357 ON_BOOL32 ON_DimensionExtra::Archive() const
00358 {
00359   // true to write to file
00360   return true;
00361 }
00362 
00363 
00364 ON_UUID ON_DimensionExtra::ParentUUID() const
00365 {
00366   return m_partent_uuid;
00367 }
00368 
00369 void ON_DimensionExtra::SetParentUUID( ON_UUID partent_uuid)
00370 {
00371   m_partent_uuid = partent_uuid;
00372 }
00373 
00374 int ON_DimensionExtra::ArrowPosition() const
00375 {
00376   return m_arrow_position;
00377 }
00378 
00379 void ON_DimensionExtra::SetArrowPosition( int position)
00380 {
00381   if( position > 0)
00382     m_arrow_position = 1;
00383   else if( position < 0)
00384     m_arrow_position = -1;
00385   else
00386     m_arrow_position = 0;
00387 }
00388 
00389 double ON_DimensionExtra::DistanceScale() const
00390 {
00391   return m_distance_scale;
00392 }
00393 
00394 void ON_DimensionExtra::SetDistanceScale(double s)
00395 {
00396   m_distance_scale = s;
00397 }
00398 
00399 void ON_DimensionExtra::SetModelSpaceBasePoint(ON_3dPoint basepoint)
00400 {
00401   m_modelspace_basepoint = basepoint;
00402 }
00403 
00404 ON_3dPoint ON_DimensionExtra::ModelSpaceBasePoint() const
00405 {
00406   return m_modelspace_basepoint;
00407 }
00408 
00409 /*
00410 const wchar_t* ON_DimensionExtra::ToleranceUpperString() const
00411 {
00412   return m_upper_string;
00413 }
00414 
00415 ON_wString& ON_DimensionExtra::ToleranceUpperString()
00416 {
00417   return m_upper_string;
00418 }
00419 
00420 void ON_DimensionExtra::SetToleranceUpperString( const wchar_t* upper_string)
00421 {
00422   m_upper_string = upper_string;
00423 }
00424 
00425 void ON_DimensionExtra::SetToleranceUpperString( ON_wString& upper_string)
00426 {
00427   m_upper_string = upper_string;
00428 }
00429 
00430 const wchar_t* ON_DimensionExtra::ToleranceLowerString() const
00431 {
00432   return m_lower_string;
00433 }
00434 
00435 ON_wString& ON_DimensionExtra::ToleranceLowerString()
00436 {
00437   return m_lower_string;
00438 }
00439 
00440 void ON_DimensionExtra::SetToleranceLowerString( const wchar_t* lower_string)
00441 {
00442   m_lower_string = lower_string;
00443 }
00444 
00445 void ON_DimensionExtra::SetToleranceLowerString( ON_wString& lower_string)
00446 {
00447   m_lower_string = lower_string;
00448 }
00449 
00450 
00451 
00452 const wchar_t* ON_DimensionExtra::AlternateString() const
00453 {
00454   return m_alt_string;
00455 }
00456 
00457 ON_wString& ON_DimensionExtra::AlternateString()
00458 {
00459   return m_alt_string;
00460 }
00461 
00462 void ON_DimensionExtra::SetAlternateString( const wchar_t* alt_string)
00463 {
00464   m_alt_string = alt_string;
00465 }
00466 
00467 void ON_DimensionExtra::SetAlternateString( ON_wString& alt_string)
00468 {
00469   m_alt_string = alt_string;
00470 }
00471 
00472 const wchar_t* ON_DimensionExtra::AlternateToleranceUpperString() const
00473 {
00474   return m_alt_upper_string;
00475 }
00476 
00477 ON_wString& ON_DimensionExtra::AlternateToleranceUpperString()
00478 {
00479   return m_alt_upper_string;
00480 }
00481 
00482 void ON_DimensionExtra::SetAlternateToleranceUpperString( const wchar_t* upper_string)
00483 {
00484   m_alt_upper_string = upper_string;
00485 }
00486 
00487 void ON_DimensionExtra::SetAlternateToleranceUpperString( ON_wString& upper_string)
00488 {
00489   m_alt_upper_string = upper_string;
00490 }
00491 
00492 const wchar_t* ON_DimensionExtra::AlternateToleranceLowerString() const
00493 {
00494   return m_alt_lower_string;
00495 }
00496 
00497 ON_wString& ON_DimensionExtra::AlternateToleranceLowerString()
00498 {
00499   return m_alt_lower_string;
00500 }
00501 
00502 void ON_DimensionExtra::SetAlternateToleranceLowerString( const wchar_t* lower_string)
00503 {
00504   m_alt_lower_string = lower_string;
00505 }
00506 
00507 void ON_DimensionExtra::SetAlternateToleranceLowerString( ON_wString& lower_string)
00508 {
00509   m_alt_lower_string = lower_string;
00510 }
00511 */
00512 
00513 //--------------------
00514 
00515 
00516 ON_VIRTUAL_OBJECT_IMPLEMENT( ON_Annotation2,       ON_Geometry,    "8D820224-BC6C-46b4-9066-BF39CC13AEFB");
00517 ON_OBJECT_IMPLEMENT( ON_LinearDimension2,  ON_Annotation2, "BD57F33B-A1B2-46e9-9C6E-AF09D30FFDDE");
00518 ON_OBJECT_IMPLEMENT( ON_RadialDimension2,  ON_Annotation2, "B2B683FC-7964-4e96-B1F9-9B356A76B08B");
00519 ON_OBJECT_IMPLEMENT( ON_AngularDimension2, ON_Annotation2, "841BC40B-A971-4a8e-94E5-BBA26D67348E");
00520 ON_OBJECT_IMPLEMENT( ON_TextEntity2,       ON_Annotation2, "46F75541-F46B-48be-AA7E-B353BBE068A7");
00521 ON_OBJECT_IMPLEMENT( ON_Leader2,           ON_Annotation2, "14922B7A-5B65-4f11-8345-D415A9637129");
00522 ON_OBJECT_IMPLEMENT( ON_TextDot,           ON_Geometry,    "74198302-CDF4-4f95-9609-6D684F22AB37");
00523 ON_OBJECT_IMPLEMENT( ON_OrdinateDimension2,ON_Annotation2, "C8288D69-5BD8-4f50-9BAF-525A0086B0C3");
00524 
00525 // class ON_Annotation2
00526 //--------------------------------------------------------------------
00527 
00528 int ON_Annotation2::Index() const
00529 {
00530   return m_index;
00531 }
00532 
00533 void ON_Annotation2::SetIndex( int index)
00534 {
00535   m_index = index;
00536 }
00537 
00538 
00539 
00540 void ON_Annotation2::Create()
00541 {
00542   m_textdisplaymode = ON::dtAboveLine;
00543   m_index = -1;
00544   m_textheight = 1.0;
00545   Destroy();
00546 }
00547 
00548 void ON_Annotation2::Destroy()
00549 {
00550   // 10-27-03 LW memory leak prevention
00551   m_points.Empty();
00552   SetTextValue(0);
00553   SetTextFormula(0);
00554   m_type = ON::dtNothing;
00555   m_plane = ON_xy_plane;
00556   m_userpositionedtext = false;
00557   m_justification = 0;
00558   m_annotative_scale = true;
00559 }
00560 
00561 void ON_Annotation2::EmergencyDestroy()
00562 {
00563   m_points.EmergencyDestroy();
00564   m_usertext.EmergencyDestroy();
00565 }
00566 
00567 ON_Annotation2::ON_Annotation2()
00568 {
00569   Create();
00570 }
00571 
00572 ON_Annotation2::~ON_Annotation2()
00573 {
00574   Destroy();
00575 }
00576 
00577 bool ON_Annotation2::EvaluatePoint( const ON_ObjRef& objref, ON_3dPoint& P) const
00578 {
00579   bool rc = false;
00580   switch( objref.m_component_index.m_type )
00581   {
00582   case ON_COMPONENT_INDEX::dim_linear_point:
00583   case ON_COMPONENT_INDEX::dim_radial_point:
00584   case ON_COMPONENT_INDEX::dim_angular_point:
00585   case ON_COMPONENT_INDEX::dim_ordinate_point:
00586   case ON_COMPONENT_INDEX::dim_text_point:
00587     {
00588       ON_2dPoint uv = Point(objref.m_component_index.m_index);
00589       if ( uv.IsValid() )
00590       {
00591         P = m_plane.PointAt(uv.x,uv.y);
00592         rc = true;
00593       }
00594     }
00595     break;
00596   default:
00597     // other enum values skipped on purpose
00598     break;
00599   }
00600   if (!rc)
00601   {
00602     P = ON_UNSET_POINT;
00603   }
00604   return rc;
00605 }
00606 
00607 // convert from old style annotation
00608 ON_Annotation2& ON_Annotation2::operator=(const ON_Annotation& src)
00609 {
00610   // get a clean and empty "this"
00611   Destroy();
00612   Create();
00613   ON_Geometry::operator=(src);
00614 
00615   m_type = src.Type();
00616   m_textdisplaymode = src.TextDisplayMode();
00617   m_plane = src.Plane();
00618 
00619   // 13 November 2006 Dale Lear
00620   //    Copying 5 points isn't always the right
00621   //    thing to do.  Sometimes there should be
00622   //    no points or fewer points.  I'm leaving
00623   //    this unchnaged for now and fixing the
00624   //    immediate bugs downstream.  When we
00625   //    have a larger window (> 1 week before
00626   //    release) for beta testing, I'll change
00627   //    this to copy the number of points
00628   //    that actually exist in src
00629   m_points.Reserve(5);
00630   for( int i = 0; i < 5; i++)
00631     SetPoint( 0, src.Point( i));
00632 
00633 
00634   SetTextValue(src.UserText());
00635   SetTextFormula(0);
00636   m_userpositionedtext = src.UserPositionedText()?true:false;
00637   m_index = 0;
00638   m_textheight = 1.0;
00639 
00640   return *this;
00641 }
00642 
00643 ON_Annotation2::ON_Annotation2(const ON_Annotation& src)
00644 {
00645   Create();
00646   *this = src;
00647 }
00648 
00649 
00650 ON_BOOL32 ON_Annotation2::IsValid( ON_TextLog* text_log ) const
00651 {
00652   if ( !m_plane.IsValid() )
00653   {
00654     if ( text_log )
00655     {
00656       text_log->Print("ON_Annotation2 - m_plane is not valid\n");
00657     }
00658     return false;
00659   }
00660 
00661   const int points_count = m_points.Count();
00662 
00663   int i;
00664   for ( i = 0; i < points_count; i++ )
00665   {
00666     if ( !m_points[i].IsValid() )
00667     {
00668       if ( text_log )
00669       {
00670         text_log->Print("ON_Annotation2 - m_points[%d] is not valid.\n");
00671       }
00672       return false;
00673     }
00674   }
00675 
00676   switch ( m_type )
00677   {
00678   case ON::dtDimLinear:
00679   case ON::dtDimAligned:
00680   case ON::dtDimAngular:
00681   case ON::dtDimDiameter:
00682   case ON::dtDimRadius:
00683   case ON::dtLeader:
00684   case ON::dtTextBlock:
00685   case ON::dtDimOrdinate:
00686     break;
00687 
00688   default:
00689     if ( text_log )
00690     {
00691       text_log->Print("ON_Annotation2 - m_type = %d is not a valid enum value\n",m_type);
00692     }
00693     return false;
00694     break;
00695   }
00696 
00697   return true;
00698 }
00699 
00700 /*
00701 bool ON_LinearDimension2::GetAnnotationBoundingBox(
00702           const ON_DimStyle* dimstyle,
00703           ON_2dPoint text0,
00704           ON_2dPoint text1,
00705                             ON_BoundingBox& tight_bbox, 
00706           int bGrowBox,
00707                             const ON_Xform* xform
00708         ) const
00709 {
00710   if ( 5 == m_points.Count() )
00711   {
00712     // Get 5 points that are always in the 
00713     ON_3dPointArray P(10);
00714     ON_2dPoint uv0, uv1, uv2, uv3;
00715 
00716     P.Append( m_plane.origin );
00717 
00718     uv0 = m_points[0];
00719     uv1 = uv0;
00720     uv1.y = m_points[1].y;
00721     uv2 = m_points[2];
00722     uv3.x = uv2.x;
00723     uv3.y = uv1.y;
00724     P.Append( m_plane.PointAt(uv0.x,uv0.y) );
00725     P.Append( m_plane.PointAt(uv1.x,uv1.y) );
00726     P.Append( m_plane.PointAt(uv2.x,uv2.y) );
00727     P.Append( m_plane.PointAt(uv3.x,uv3.y) );
00728 
00729     if ( dimstyle )
00730     {
00731       // The ends of the displayed extension lines 
00732       // have their "y" values adjusted by the dimstyle.
00733       const double el_offset    = dimstyle->ExtOffset();
00734       const double el_extension = dimstyle->ExtExtension();
00735 
00736       const double v0 = (uv0.y < uv1.y) ? -1.0 : 1.0;
00737       uv0.y += v0*el_offset;
00738       uv1.y += v0*el_extension;
00739 
00740       const double v1 = (uv2.y < uv3.y) ? -1.0 : 1.0;
00741       uv2.y += v1*el_offset;
00742       uv3.y += v1*el_extension;
00743 
00744       P.Append( m_plane.PointAt(uv0.x,uv0.y) );
00745       P.Append( m_plane.PointAt(uv1.x,uv1.y) );
00746       P.Append( m_plane.PointAt(uv2.x,uv2.y) );
00747       P.Append( m_plane.PointAt(uv3.x,uv3.y) );
00748     }
00749 
00750     ON_2dPoint corners[4];
00751     if ( GetAnnotationText2dBoundingBox(dimstyle,text0,text1,corners) )
00752     {
00753       P.Append( m_plane.PointAt(corners[0].x,corners[0].y) );
00754       P.Append( m_plane.PointAt(corners[1].x,corners[1].y) );
00755       P.Append( m_plane.PointAt(corners[2].x,corners[2].y) );
00756       P.Append( m_plane.PointAt(corners[3].x,corners[3].y) );
00757     }
00758 
00759     // TODO Add arrowheads
00760     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
00761       bGrowBox = true;
00762   }
00763   else if ( bGrowBox && !tight_bbox.IsValid() )
00764   {
00765     // invalid linear dimension
00766     tight_bbox.Destroy();
00767     bGrowBox = false;
00768   }
00769 
00770   return (0!=bGrowBox);
00771 }
00772 */
00773 static bool WriteAnnotation2UserText_V4( ON_BinaryArchive& file, const ON_wString& s )
00774 {
00775   bool rc;
00776   ON_wString s4;
00777   int len = s.Length();
00778   
00779   for(int i = 0; i < len; i++)
00780   {
00781     if(s[i] == '\r' || s[i] == '\n')
00782     {
00783       s4 += L'\r';
00784       s4 += L'\n';
00785       
00786       // May 24, 2012  Tim - Fix for RR 100260.  If we use a while here we
00787       // miss adding carriage returns where the user really meant to have a 
00788       // blank line.  If we only check the next character and then continue on
00789       // we preserve the blank lines.
00790       if(i < len-1 && (s[i+1] == L'\r' || s[i+1] == L'\n'))
00791         i++;
00792       continue;
00793     }
00794     s4 += s[i];
00795   }
00796   rc = file.WriteString(s4);
00797   return rc;
00798 }
00799 
00800 static bool WriteAnnotation2UserText_V5( ON_BinaryArchive& file, const ON_wString& s )
00801 {
00802   bool rc;
00803   rc = file.WriteString( s);
00804   return rc;
00805 }
00806 
00807 //static int CountTextLines(const ON_wString& text)
00808 //{
00809 //  int len = text.Length();
00810 //  int lines = len > 0 ? 1 : 0;
00811 //  for(int i = 0; i < len; i++)
00812 //  {
00813 //    if(text[i] == L'\n' || text[i] == L'\r')
00814 //    {
00815 //      lines++;
00816 //      if(i < len-1 && text[i] == L'\r' && text[i+1] == L'\n')
00817 //        i++;
00818 //    }
00819 //  }
00820 //  return lines;
00821 //}
00822 
00823 ON_BOOL32 ON_Annotation2::Write( ON_BinaryArchive& file ) const
00824 {
00825   int i;
00826   bool rc = false;
00827   bool bInChunk = (file.Archive3dmVersion() >= 5 );
00828   if ( bInChunk )
00829   {
00830     // 18 October 2007 Dale Lear
00831     //   I modified this code so that V5 files can add
00832     //   information in ON_Annotation2 chunks without
00833     //   breaking past and future file IO.
00834     //   The opennurbs version number before this change was
00835     //   20071017*.  I changed the version to 20071018* when
00836     //   I checked in this IO change.  The reason I can get
00837     //   away with this is that nobody except developers has
00838     //   a copy of V5 Rhino.
00839     // 28 Aug, 2010 - Lowell - changed minor version 0->1 to write
00840     //   annotative scale flag
00841     // 24 September 2010 Dale Lear 
00842     //   I incremented chunk version to 1.2 and wrote the TextFormaula() string.
00843     rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,2);
00844     if (!rc)
00845       return false;
00846   }
00847   else
00848   {
00849     // For archives with opennurbs version < 200710180
00850     // The code before version 200710180 does not properly
00851     // handle new additions to the ON_Annotation2 chunk.  
00852     rc = file.Write3dmChunkVersion( 1, 0 );
00853   }
00854 
00855   while(rc)
00856   {
00857     i = m_type;
00858     rc = file.WriteInt( i);
00859     if ( !rc) break;
00860 
00861     i = m_textdisplaymode;    
00862     rc = file.WriteInt( i);
00863     if ( !rc) break;
00864 
00865     // June 17, 2010 - Lowell - Added adjustment to position text
00866     // a little better in pre-v5 files.
00867     // There's no adjustment for right/left justify becasue we don't 
00868     // know the width of the text here
00869     // This doesn't change the size or position of any fields being
00870     // written, but just adjusts the plane to tune up the alignment
00871 
00872     // 16 Nov, 2011 - Lowell - Change text to bottom left justified for pre-v5 files rr94270
00873     // This stuff is moved to CRhinoDoc::Write3DMHelper() so it will help with other file
00874     // formats too
00875     //ON_Plane plane = m_plane;
00876     //if(file.Archive3dmVersion() <= 4 && m_type == ON::dtTextBlock)
00877     //{
00878     //  double height = m_textheight;
00879     //  int lines = CountTextLines(m_usertext);
00880     //  double linefeed = ON_Font::m_default_linefeed_ratio;
00881 
00882     //  if(m_justification & tjBottom)
00883     //  {
00884     //    if(lines > 1)
00885     //    {
00886     //      ON_3dPoint p = plane.PointAt(0.0, -height * (lines-1) * linefeed);
00887     //      plane.SetOrigin(p);
00888     //    }
00889     //  }
00890     //  else if(m_justification & tjMiddle)
00891     //  {
00892     //    double h = height * (lines-1) * linefeed + height;
00893     //    ON_3dPoint p = plane.PointAt(0.0, h * 0.5);
00894     //    plane.SetOrigin(p);
00895     //  }
00896     //  else if(m_justification & tjTop)
00897     //  {
00898     //    ON_3dPoint p = plane.PointAt(0.0, -height);
00899     //    plane.SetOrigin(p);
00900     //  }
00901     //}
00902   
00903     rc = file.WritePlane(m_plane);
00904     if ( !rc) break;
00905 
00906     ON_2dPointArray points = m_points;
00907     int bUserPositionedText = m_userpositionedtext?1:0;
00908     switch( m_type )
00909     {
00910     case ON::dtDimAligned:
00911     case ON::dtDimLinear:
00912       if ( 4 == points.Count() )
00913       {
00914         // so old versions will read enough points.
00915         points.AppendNew();
00916         points[4].Set(0.5*(points[0].x + points[2].x),points[1].y);
00917         bUserPositionedText = false;
00918       }
00919       break;
00920 
00921     case ON::dtDimAngular:
00922       //user positioned text is supported.
00923       break;
00924 
00925     case ON::dtDimRadius:
00926     case ON::dtDimDiameter:
00927       // 9 August 2005 Dale Lear - radial dimensions do
00928       // not support user postioned text.  The never have
00929       // in Rhino, but the old files had 5 points in them.
00930       if ( 4 == points.Count() )
00931       {
00932         // so old versions will read enough points.
00933         points.AppendNew();
00934       }
00935       if ( points.Count() >= 5 )
00936       {
00937         points[4] = points[2];
00938       }
00939       bUserPositionedText = false;
00940       break;
00941 
00942     default:
00943       bUserPositionedText = false;
00944       break;
00945     }
00946     
00947     rc = file.WriteArray( points);
00948     if ( !rc) break;
00949     
00950     // June 17, 2010 - Lowell - Added support for writing word-wrapped text
00951     // to pre-v5 files with hard returns in place of wrapping markers
00952     rc = ( file.Archive3dmVersion() <= 4 )
00953        ? WriteAnnotation2UserText_V4(file,m_usertext)
00954        : WriteAnnotation2UserText_V5(file,m_usertext);
00955     if ( !rc) break;
00956     // 7-9-03 lw removed extra text string getting written
00957     
00958     rc = file.WriteInt( bUserPositionedText );
00959     if ( !rc) break;
00960     
00961     rc = file.WriteInt( m_index);  // font or dimstyle index
00962     if ( !rc) break;
00963 
00964     rc = file.WriteDouble( m_textheight);
00965     if ( !rc) break;
00966 
00967     if ( !bInChunk )
00968       break;
00969 
00970     // NOTE WELL - NEVER change the code in this function
00971     //             above this comment.  If you do, you will
00972     //             break reading and writing V4 files.
00973     //             Ask Dale Lear if you have any questions.
00974 
00975     // 18 October 2007 - Dale Lear added m_justification IO
00976     rc = file.WriteInt(m_justification);
00977     if ( !rc) 
00978       break;
00979 
00980     // 28 Aug 2010 - Lowell - Added flag for whether text gets scaled in modelspace
00981     rc = file.WriteBool(m_annotative_scale);
00982     if(!rc)
00983       break;
00984 
00985     // 24 September 2010 Dale Lear 
00986     //   I incremented chunk version to 1.2
00987     ON_wString text_formula = TextFormula();
00988     rc = file.WriteString(text_formula);
00989     if(!rc)
00990       break;
00991 
00992     // To write more ON_Annotation2 fields, increment the minor version
00993     // number and write the new information here.
00994 
00995 
00996     // Finished writing ON_Annotation2 information
00997     break;
00998   }
00999 
01000   if ( bInChunk )
01001   {
01002     if ( !file.EndWrite3dmChunk() )
01003       rc = false;
01004   }
01005 
01006   return rc;
01007 }
01008 
01009 ON_BOOL32 ON_Annotation2::Read( ON_BinaryArchive& file )
01010 {
01011   // NOTE WELL - If you make any changes to this code,
01012   //             you break file IO for some annotation
01013   //             objects.  Please discuss all changes
01014   //             with Dale Lear BEFORE you check in any
01015   //             changes.
01016 
01017   Destroy();
01018   
01019   // If annotation is read from old files that do not contain
01020   // the m_annotative_scale setting, then m_annotative_scale 
01021   // must be false so the text behaves the way it did in old
01022   // files.  The "Destroy()" function above can set m_annotative_scale
01023   // any way that makes sense for new objects.
01024   m_annotative_scale = false;
01025   
01026   int major_version = 0;
01027   int minor_version = 0;
01028   bool rc = false;
01029 
01030   bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180);
01031 
01032   if ( bInChunk )
01033   {
01034     rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
01035     if ( !rc )
01036       return false;
01037   }
01038   else
01039   {
01040     rc = file.Read3dmChunkVersion(&major_version,&minor_version);
01041   }
01042 
01043   while(rc)
01044   {
01045     if ( 1 != major_version )
01046     {
01047       rc = false;
01048       break;
01049     }
01050 
01051     int i;
01052     rc = file.ReadInt( &i);
01053     if (!rc) break;
01054     m_type = ON::AnnotationType( i);
01055 
01056     rc = file.ReadInt( &i);
01057     if (!rc) break;
01058     m_textdisplaymode = ON::eTextDisplayMode( i);
01059 
01060     rc = file.ReadPlane( m_plane);
01061     if (!rc) break;
01062 
01063     rc = file.ReadArray( m_points);
01064     if (!rc) break;
01065     
01066     rc = file.ReadString( m_usertext);
01067     if (!rc) break;
01068     
01069     i = 0;
01070     rc = file.ReadInt( &i );
01071     if (!rc) break;
01072     m_userpositionedtext = i ? true : false;
01073 
01074     rc = file.ReadInt( &m_index);
01075     if (!rc) break;
01076     
01077     rc = file.ReadDouble( &m_textheight);
01078     if (!rc) break;
01079 
01080     switch( m_type )
01081     {
01082     case ON::dtDimAligned:
01083     case ON::dtDimLinear:
01084       if ( m_points.Count() < 5 )
01085       {
01086         m_userpositionedtext = false;
01087       }
01088       break;
01089 
01090     case ON::dtDimAngular:
01091       if ( m_points.Count() <= 0 )
01092       {
01093         m_userpositionedtext = false;
01094       }
01095       break;
01096 
01097     case ON::dtDimRadius:
01098     case ON::dtDimDiameter:
01099       // 9 August 2005 Dale Lear - radial dimensions do
01100       // not support user postioned text.  The never have
01101       // in Rhino, but the old files had 5 points in them.
01102       if ( 5 == m_points.Count() )
01103       {
01104         m_points.SetCount(4);
01105       }
01106       m_userpositionedtext = false;
01107       break;
01108 
01109     default:
01110       m_userpositionedtext = false;
01111       break;
01112     }
01113 
01114     if ( !bInChunk )
01115       break;
01116 
01117     // 18 October 2007 - Dale Lear added m_justification IO
01118     rc = file.ReadInt( &m_justification );
01119     if (!rc) break;
01120 
01121     if ( minor_version <= 0 )
01122       break;
01123 
01124     if(minor_version >= 1)
01125     {
01126       // 28 Aug, 2010 - Lowell - added reading annotative scale flag
01127       rc = file.ReadBool(&m_annotative_scale);
01128       if (!rc) break;
01129 
01130       if ( minor_version >= 2 )
01131       {
01132         // 24 September 2010 Dale Lear 
01133         //   I incremented chunk version to 1.2
01134         ON_wString text_formula;
01135         rc = file.ReadString(text_formula);
01136         if(!rc) break;
01137         SetTextFormula(text_formula);
01138       }
01139 
01140     }
01141 
01142     // Read new additions to ON_Annotation2 here
01143 
01144     break;
01145   }
01146 
01147   if ( bInChunk )
01148   {
01149     if (!file.EndRead3dmChunk() )
01150       rc = false;
01151   }
01152 
01153 
01154   return rc;
01155 }
01156 
01157 ON::object_type ON_Annotation2::ObjectType() const
01158 {
01159   return ON::annotation_object;
01160 }
01161 
01162 int ON_Annotation2::Dimension() const
01163 {
01164   return 3;
01165 }
01166 
01167 ON_BOOL32 ON_Annotation2::Transform( const ON_Xform& xform )
01168 {
01169   ON_Geometry::Transform(xform);
01170 
01171   ON_2dPoint p;
01172   ON_Xform scalexf;
01173   double scale = xform.Determinant();
01174   scale = fabs( scale);
01175   if( fabs( scale - 1.0) > ON_SQRT_EPSILON && fabs( scale) > ON_SQRT_EPSILON)
01176   {
01177     scale = pow( scale, 1.0/3.0);
01178     scalexf.Scale( scale, scale, scale);
01179     for( int i = 0; i < m_points.Count(); i++)
01180     {
01181       p = Point( i);
01182       p.Transform( scalexf);
01183       SetPoint( i, p);
01184     }
01185     // This scales the text height on text but not on dimensions
01186     if( ON_Annotation2::IsText())
01187     {
01188       SetHeight( scale * Height());
01189     }
01190   }
01191 
01192   return m_plane.Transform( xform );
01193  
01194 }
01195 
01196 int ON_Plane_Repair(ON_Plane& plane)
01197 {
01198   int rc;
01199   if ( plane.IsValid() )
01200   {
01201     rc = 1;
01202   }
01203   else
01204   {
01205     rc = 2;
01206     if (!plane.origin.IsValid())
01207     {
01208       plane.origin.Set(0.0,0.0,0.0);
01209     }
01210 
01211     bool bGoodX = (plane.xaxis.IsValid() && !plane.xaxis.IsZero() );
01212     bool bGoodY = (plane.yaxis.IsValid() && !plane.yaxis.IsZero() );
01213     bool bGoodZ = (plane.zaxis.IsValid() && !plane.zaxis.IsZero() );
01214     if ( bGoodX )
01215     {
01216       if ( fabs(plane.xaxis.Length()-1.0) > ON_SQRT_EPSILON )
01217         plane.xaxis.Unitize();
01218     }
01219     if ( bGoodY )
01220     {
01221       if ( fabs(plane.yaxis.Length()-1.0) > ON_SQRT_EPSILON )
01222         plane.yaxis.Unitize();
01223     }
01224     if ( bGoodZ )
01225     {
01226       if ( fabs(plane.zaxis.Length()-1.0) > ON_SQRT_EPSILON )
01227         plane.zaxis.Unitize();
01228     }
01229 
01230     if ( bGoodZ )
01231     {
01232       double x = bGoodX ? fabs(plane.zaxis*plane.xaxis) : 99.0;
01233       double y = bGoodX ? fabs(plane.zaxis*plane.yaxis) : 99.0;
01234       if ( x <= ON_SQRT_EPSILON )
01235       {
01236         if ( y > ON_SQRT_EPSILON )
01237         {
01238           plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01239           plane.yaxis.Unitize();
01240         }
01241       }
01242       else if ( y <= ON_SQRT_EPSILON )
01243       {
01244         plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
01245         plane.xaxis.Unitize();
01246       }
01247       else if ( x <= y && x < 1.0 )
01248       {
01249         plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01250         if( plane.yaxis.Unitize() )
01251         {
01252           plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
01253           plane.xaxis.Unitize();
01254         }
01255         else if ( y < 1.0 )
01256         {
01257           plane.CreateFromNormal( plane.origin, plane.zaxis );
01258         }
01259       }
01260       else if ( y < 1.0 )
01261       {
01262         plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
01263         if( plane.xaxis.Unitize() )
01264         {
01265           plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01266           plane.yaxis.Unitize();
01267         }
01268         else
01269         {
01270           plane.CreateFromNormal( plane.origin, plane.zaxis );
01271         }
01272       }
01273     }
01274     else if ( bGoodX )
01275     {
01276       if ( bGoodY )
01277       {
01278         plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
01279         if ( plane.zaxis.Unitize() )
01280         {
01281           if ( fabs(plane.yaxis*plane.xaxis) > ON_SQRT_EPSILON )
01282           {
01283             plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01284             plane.yaxis.Unitize();
01285           }
01286         }
01287         else
01288         {
01289           plane.yaxis.PerpendicularTo(plane.xaxis);
01290           plane.yaxis.Unitize();
01291           plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
01292           plane.zaxis.Unitize();
01293         }
01294       }
01295       else
01296       {
01297         plane.yaxis.PerpendicularTo(plane.xaxis);
01298         plane.yaxis.Unitize();
01299         plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
01300         plane.zaxis.Unitize();
01301       }
01302     }
01303     else if ( bGoodY )
01304     {
01305       plane.zaxis.PerpendicularTo(plane.yaxis);
01306       plane.zaxis.Unitize();
01307       plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
01308       plane.xaxis.Unitize();
01309     }
01310     else
01311     {
01312       plane.xaxis.Set(1.0,0.0,0.0);
01313       plane.yaxis.Set(0.0,1.0,0.0);
01314       plane.zaxis.Set(0.0,0.0,1.0);
01315     }
01316     plane.UpdateEquation();
01317   }
01318   return rc;
01319 }
01320 
01321 // overrides virtual ON_Geometry::Transform()
01322 ON_BOOL32 ON_RadialDimension2::Transform( const ON_Xform& xform )
01323 {
01324   // TODO fill in something that works for non-rigid transforms
01325   return ON_Annotation2::Transform(xform);
01326 }
01327 
01328 ON_BOOL32 ON_Leader2::Transform( const ON_Xform& xform )
01329 {
01330   bool rc = xform.IsIdentity();
01331   if ( !rc)
01332   {
01333     ON_Plane plane = m_plane;
01334     rc = plane.Transform(xform);
01335     if ( rc )
01336     {
01337       int i;
01338       const int point_count =  m_points.Count();
01339       ON_2dPointArray q(point_count);
01340       ON_2dPoint p2, q2;
01341       ON_3dPoint P, Q;
01342       bool bUpdatePoints = false;
01343       for ( i = 0; i < point_count && rc; i++ )
01344       {
01345         p2 = m_points[i];
01346         P = m_plane.PointAt( p2.x, p2.y );
01347         Q = xform*P;
01348         if( !plane.ClosestPointTo(Q,&q2.x,&q2.y) )
01349           rc = false;
01350         if ( fabs(p2.x - q2.x) <= ON_SQRT_EPSILON )
01351           q2.x = p2.x;
01352         else
01353           bUpdatePoints = true;
01354         if ( fabs(p2.y - q2.y) <= ON_SQRT_EPSILON )
01355           q2.y = p2.y;
01356         else
01357           bUpdatePoints = true;
01358         q.Append(q2);
01359       }
01360 
01361       if(rc)
01362       {
01363         ON_Geometry::Transform(xform);
01364         m_plane = plane;
01365 
01366         if ( bUpdatePoints )
01367           m_points = q;
01368 
01369         if ( m_points[0].x != 0.0 || m_points[0].y != 0.0 )
01370         {
01371           ON_2dVector v = m_points[0];
01372           if ( !v.IsZero() )
01373           {
01374             m_plane.origin = m_plane.PointAt(v.x,v.y);
01375             m_plane.UpdateEquation();
01376             v.Reverse();
01377             for ( i = 1; i < point_count; i++ )
01378             {
01379               m_points[i] += v;
01380             }
01381             m_points[0].Set(0.0,0.0);
01382           }
01383         }
01384       }
01385     }
01386   }
01387   return rc;
01388 }
01389 
01390 ON_BOOL32 ON_AngularDimension2::Transform( const ON_Xform& xform )
01391 {
01392   // Dale Lear - this override fixes RR 11114 by correctly
01393   //             handling non uniform scaling.
01394   bool rc = xform.IsIdentity();
01395   if ( !rc)
01396   {
01397     ON_Plane plane = m_plane;
01398     if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) )
01399     {
01400       rc = true;
01401       ON_3dPoint P[dim_pt_count], Q[dim_pt_count], A[3], B[3];
01402       ON_2dVector p2[dim_pt_count], q2[dim_pt_count], a[3], b[3];
01403       double r[3];
01404       int i;
01405       bool bUpdatePoints = false;
01406       double a0 = 0.0;
01407       double a1 = m_angle;
01408       a[0].Set( m_radius*cos(a0), m_radius*sin(a0) );
01409       a[1].Set( m_radius*cos(0.5*(a0+a1)), m_radius*sin(0.5*(a0+a1)) );
01410       a[2].Set( m_radius*cos(a1), m_radius*sin(a1) );
01411       for ( i = 0; i < dim_pt_count && rc; i++ )
01412       {
01413         p2[i] = m_points[i];
01414         P[i] = m_plane.PointAt( p2[i].x, p2[i].y );
01415         Q[i] = xform*P[i];
01416         if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) )
01417           rc = false;
01418         if (   fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON 
01419             || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON )
01420         {
01421           // transformation is not a rigid motion
01422           bUpdatePoints = true;
01423         }
01424       }
01425 
01426       for ( i = 0; i < 3 && rc; i++ )
01427       {
01428         A[i] = m_plane.PointAt( a[i].x, a[i].y );
01429         B[i] = xform*A[i];
01430         if( !plane.ClosestPointTo(B[i],&b[i].x,&b[i].y) )
01431           rc = false;
01432         r[i] = B[i].DistanceTo(plane.origin);
01433         if (   fabs(a[i].x - b[i].x) > ON_SQRT_EPSILON 
01434             || fabs(a[i].y - b[i].y) > ON_SQRT_EPSILON )
01435         {
01436           // transformation is not a rigid motion
01437           bUpdatePoints = true;
01438         }
01439         if ( r[i] < ON_SQRT_EPSILON )
01440           rc = false;
01441         else if ( r[i] > m_radius*(1.0+ON_SQRT_EPSILON) )
01442           bUpdatePoints = true;
01443         else if ( r[i] < m_radius*(1.0-ON_SQRT_EPSILON) )
01444           bUpdatePoints = true;
01445       }
01446 
01447       if (rc)
01448       {
01449         if ( bUpdatePoints )
01450         {
01451           ON_3dVector X = B[0] - plane.origin;
01452           X.Unitize();
01453 
01454           ON_3dVector Y1 = B[1] - plane.origin;
01455           Y1.Unitize();
01456           ON_3dVector Z1 = ON_CrossProduct(X,Y1);
01457           double z1 = Z1.Length();
01458           Z1.Unitize();
01459 
01460           ON_3dVector Y2 = B[2] - plane.origin;
01461           Y2.Unitize();
01462           ON_3dVector Z2 = ON_CrossProduct(X,Y2);
01463           double z2 = Z2.Length();
01464           Z2.Unitize();
01465 
01466           if ( z2 >= z1 && z2 >= 0.05 )
01467           {
01468             plane.xaxis = X;
01469             plane.zaxis = Z2;
01470             if ( Z1*Z2 < 0.0 )
01471               plane.zaxis.Reverse();
01472             plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01473             plane.yaxis.Unitize();
01474           }
01475           else if ( z1 >= 0.05 )
01476           {
01477             plane.xaxis = X;
01478             plane.zaxis = Z1;
01479             plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
01480             plane.yaxis.Unitize();
01481           }
01482           else
01483           {
01484             rc = false;
01485           }
01486 
01487           if (rc)
01488           {
01489             plane.UpdateEquation();
01490             ON_3dVector V = B[2] - plane.origin;
01491             double x = V*plane.xaxis;
01492             double y = V*plane.yaxis;
01493             double angle = atan2(y,x);
01494             if ( angle < 0.0 )
01495               angle += 2.0*ON_PI;
01496             double radius = (r[0]+r[1]+r[2])/3.0;
01497 
01498             double arc_pt_angle = 1.0/3.0;
01499             if ( m_angle > 0.0 && m_points[arc_pt_index].IsValid() )
01500             {
01501               arc_pt_angle = atan2(m_points[arc_pt_index].y,m_points[arc_pt_index].x);
01502               if ( arc_pt_angle < 0.0 ) arc_pt_angle += 2.0*ON_PI;
01503               if ( arc_pt_angle > m_angle )
01504                 arc_pt_angle = 1.0/3.0;
01505               else
01506                 arc_pt_angle /= m_angle;
01507 
01508               if ( arc_pt_angle < 0.0 ) 
01509                 arc_pt_angle = 0.0; 
01510               else if ( arc_pt_angle > 1.0 ) 
01511                 arc_pt_angle = 1.0;
01512             }
01513             arc_pt_angle *= angle;
01514 
01515 
01516             ON_Geometry::Transform(xform);
01517             m_plane = plane;
01518             m_radius = radius;
01519             m_angle = angle;
01520             m_points[start_pt_index].Set(m_radius,0.0);
01521             m_points[end_pt_index].Set(m_radius*cos(m_angle),m_radius*sin(m_angle));
01522             m_points[arc_pt_index].Set(m_radius*cos(arc_pt_angle),m_radius*sin(arc_pt_angle));
01523             if ( m_userpositionedtext )
01524             {
01525               m_plane.ClosestPointTo(Q[userpositionedtext_pt_index],
01526                 &m_points[userpositionedtext_pt_index].x,
01527                 &m_points[userpositionedtext_pt_index].y
01528                 );
01529             }
01530             else
01531             {
01532               m_points[userpositionedtext_pt_index].Set(m_radius*cos(0.5*m_angle),m_radius*sin(0.5*m_angle));
01533             }
01534 
01535           }
01536         }
01537         else
01538         {
01539           ON_Geometry::Transform(xform);
01540           m_plane = plane;
01541         }
01542       }
01543     }
01544   }
01545 
01546   return rc;
01547 }
01548 
01549 bool ON_Annotation2::IsText() const 
01550 { return (ON::dtTextBlock == m_type); }
01551 
01552 bool ON_Annotation2::IsLeader() const 
01553 { return (ON::dtLeader == m_type); }
01554 
01555 bool ON_Annotation2::IsDimension() const 
01556 { return (ON::dtTextBlock != m_type && ON::dtLeader != m_type); }
01557 
01558 double ON_Annotation2::NumericValue() const
01559 { return ON_UNSET_VALUE; }
01560 
01561 void ON_Annotation2::SetHeight( double ht) 
01562 { if( ht > ON_SQRT_EPSILON) m_textheight = ht; }
01563 
01564 double ON_Annotation2::Height() const 
01565 { return m_textheight; }
01566 
01567 void ON_Annotation2::SetType( ON::eAnnotationType type ) 
01568 { 
01569   m_type = type;
01570   if(type == ON::dtDimRadius)
01571     SetTextValue(ON_RadialDimension2::DefaultRadiusText());
01572   else if(type == ON::dtDimDiameter)
01573     SetTextValue(ON_RadialDimension2::DefaultDiameterText());
01574   else
01575     SetTextValue(0);
01576 
01577   SetTextFormula(0);
01578 }
01579 
01580 ON::eAnnotationType ON_Annotation2::Type() const 
01581 { return m_type; }
01582 
01583 void ON_Annotation2::SetPlane( const ON_Plane& plane ) 
01584 { m_plane = plane; }
01585 
01586 const ON_Plane& ON_Annotation2::Plane() const 
01587 { return m_plane; }
01588 
01589 void ON_Annotation2::SetPointCount( int count) 
01590 {
01591   if( m_points.Count() < count)
01592   {
01593     m_points.Reserve( count);
01594     for( int i = m_points.Count(); i < count; i++)
01595       m_points.Append( ON_2dPoint());
01596   }
01597 }
01598 
01599 int ON_Annotation2::PointCount() const 
01600 { return m_points.Count(); }
01601 
01602 void ON_Annotation2::SetPoints( const ON_2dPointArray& points ) 
01603 { m_points = points; }
01604 
01605 const ON_2dPointArray& ON_Annotation2::Points() const 
01606 { return m_points; }
01607 
01608 void ON_Annotation2::SetPoint( int idx, const ON_2dPoint& point )
01609 {
01610   if ( idx >= 0 )
01611   {
01612     if ( idx < m_points.Count() )
01613       m_points[idx] = point;
01614     else if ( idx == m_points.Count() )
01615       m_points.Append(point);
01616   }
01617 }
01618 
01619 ON_2dPoint ON_Annotation2::Point( int idx ) const
01620 {
01621   return ( idx >= 0 && idx < m_points.Count() ) 
01622          ? m_points[idx] 
01623          : ON_2dPoint( 0.0, 0.0 );
01624 }
01625 
01626 void ON_Annotation2::SetUserText( const wchar_t* text_value )
01627 // ON_Annotation2::SetUserText is OBSOLETE - use ON_Annotation2::SetTextValue();
01628 {
01629   SetTextValue( text_value );
01630 }
01631 
01632 const ON_wString& ON_Annotation2::UserText() const 
01633 // ON_Annotation2::UserText() is OBSOLETE - use ON_Annotation2::TextValue();
01634 {
01635   return m_usertext; 
01636 }
01637 
01638 void ON_Annotation2::SetUserPositionedText( int bMoved ) 
01639 { m_userpositionedtext = bMoved?true:false; }
01640 bool ON_Annotation2::UserPositionedText() const 
01641 { return m_userpositionedtext; }
01642 
01643 
01644 // Converts 2d points in annotation to 3d WCS points
01645 ON_BOOL32 ON_Annotation2::GetECStoWCSXform( ON_Xform& xform ) const
01646 {
01647   ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis );
01648   return xform.ChangeBasis( m_plane.origin, m_plane.xaxis, m_plane.yaxis, z,
01649                             ON_origin, ON_xaxis, ON_yaxis, ON_zaxis );
01650 }
01651 
01652 // Converts from WCS 3d points to 2d points in annotation
01653 ON_BOOL32 ON_Annotation2::GetWCStoECSXform( ON_Xform& xform ) const
01654 {
01655   ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis );
01656   return xform.ChangeBasis( ON_origin, ON_xaxis, ON_yaxis, ON_zaxis,
01657                             m_plane.origin, m_plane.xaxis, m_plane.yaxis, z );
01658 }
01659 
01660 void ON_Annotation2::ReservePoints( int count)
01661 {
01662   m_points.SetCapacity( count);
01663   m_points.SetCount( count);
01664 }
01665 
01666 const wchar_t* ON_Annotation2::DefaultText() { return L""; }
01667 
01668 
01669 void ON_Annotation2::SetTextDisplayMode( ON::eTextDisplayMode mode)
01670 {
01671   m_textdisplaymode = mode;
01672 }
01673 
01674 ON::eTextDisplayMode ON_Annotation2::TextDisplayMode() const
01675 {
01676   return m_textdisplaymode;
01677 }
01678 
01679 
01680 void ON_Annotation2::ConvertBack( ON_Annotation& target)
01681 {
01682   target.SetType( Type());
01683   target.SetTextDisplayMode( TextDisplayMode());
01684   target.SetPlane( Plane());
01685   target.SetPoints( Points());
01686   target.SetUserText( TextValue());
01687   target.SetDefaultText( DefaultText());
01688   target.SetUserPositionedText( UserPositionedText());
01689 }
01690 
01691 void ON_Annotation2::SetJustification( unsigned int justification)
01692 {
01693   // SDKBREAK - move to ON_Leader virtual SetJustification
01694   if(this->IsLeader())
01695     m_justification = justification;
01696 }
01697 
01698 unsigned int ON_Annotation2::Justification()
01699 {
01700   // SDKBREAK - move to ON_Leader virtual Justification
01701   if(this->IsLeader())
01702     return   m_justification;
01703   else
01704     return 0;
01705 }
01706 
01707 
01708 //----- ON_LinearDimension2 ------------------------------------------
01709 ON_LinearDimension2::ON_LinearDimension2()
01710 {
01711   //ON_DimensionExtra* pDE = new ON_DimensionExtra;
01712   //if( pDE)
01713   //{
01714   //  if( !AttachUserData( pDE))
01715   //    delete pDE;
01716   //  else
01717   //    pDE->SetDefaults();
01718   //}
01719 
01720   m_type = ON::dtDimLinear;
01721   m_textdisplaymode = ON::dtAboveLine;
01722   m_plane = ON_xy_plane;
01723   SetTextValue(DefaultText());
01724   SetTextFormula(0);
01725   m_points.Reserve(ON_LinearDimension2::dim_pt_count);
01726   m_points.SetCount(ON_LinearDimension2::dim_pt_count);
01727   m_points.Zero();
01728 }
01729 
01730 ON_LinearDimension2::~ON_LinearDimension2()
01731 {
01732 }
01733 
01734 ON_BOOL32 ON_LinearDimension2::IsValid( ON_TextLog* text_log ) const
01735 {
01736   if ( m_type != ON::dtDimLinear && m_type != ON::dtDimAligned )
01737   {
01738     if ( text_log )
01739     {
01740       text_log->Print("ON_LinearDimension2 - m_type !=  ON::dtDimLinear or ON::dtDimAligned.\n");
01741     }
01742     return false;
01743   }
01744 
01745   if ( !ON_Annotation2::IsValid( text_log ))
01746   {
01747     if ( text_log )
01748     {
01749       text_log->Print("ON_LinearDimension2 - invalid ON_Annotation2 base class.\n");
01750     }
01751     return false;
01752   }
01753 
01754   if ( m_points.Count() != 5 )
01755   {
01756     if ( text_log )
01757     {
01758       text_log->Print("ON_LinearDimension2 - m_points.Count() = %d (should be 5).\n",m_points.Count());
01759     }
01760     return false;
01761   }
01762 
01763   if ( m_points[1].x != m_points[0].x )
01764   {
01765     if ( text_log )
01766     {
01767       text_log->Print("ON_LinearDimension2 - m_points[1].x = %g != %g = m_points[0].x (should be equal)\n",
01768                       m_points[1].x, m_points[0].x
01769                       );
01770     }
01771     return false;
01772   }
01773 
01774   if ( m_points[3].x != m_points[2].x )
01775   {
01776     if ( text_log )
01777     {
01778       text_log->Print("ON_LinearDimension2 - m_points[3].x = %g != %g = m_points[2].x\n",
01779                       m_points[3].x, m_points[2].x
01780                      );
01781     }
01782     return false;
01783   }
01784 
01785   if ( m_points[3].y != m_points[1].y )
01786   {
01787     if ( text_log )
01788     {
01789       text_log->Print("ON_LinearDimension2 - m_points[3].y = %g != %g = m_points[1].y\n",
01790                       m_points[3].y, m_points[1].y
01791                       );
01792     }
01793     return false;
01794   }
01795 
01796   return true;
01797 }
01798 
01799 ON_BOOL32 ON_LinearDimension2::Write(ON_BinaryArchive& archive) const
01800 {
01801   // 18 October 2007 Dale Lear
01802   //    I added the chunk wrapping so V5 and future versions can
01803   //    add IO support for information specific to ON_LinearDimension2
01804   //    V4 did not have a ON_LinearDimension2::Write and simply called
01805   //    ON_Annotation2::Write.
01806   bool rc = false;
01807   bool bInChunk = (archive.Archive3dmVersion() >= 5);
01808   if ( bInChunk )
01809   {
01810     rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
01811     if ( !rc )
01812       return false;
01813   }
01814   else
01815   {
01816     rc = true;
01817   }
01818 
01819   while(rc)
01820   {
01821     rc = ON_Annotation2::Write(archive)?true:false;
01822     if (!rc) break;
01823     if ( !bInChunk )
01824       break;
01825 
01826     // To write new fields, increment minor version number
01827     // and write values here.  Ask Dale Lear for help.
01828 
01829     break;
01830   }
01831 
01832   if ( bInChunk )
01833   {
01834     if (!archive.EndWrite3dmChunk())
01835       rc = false;
01836   }
01837   return rc;
01838 }
01839 
01840 ON_BOOL32 ON_LinearDimension2::Read(ON_BinaryArchive& archive)
01841 {
01842   // 18 October 2007 Dale Lear
01843   //    I added the chunk wrapping so V5 and future versions can
01844   //    add IO support for information specific to ON_LinearDimension2
01845   int major_version = 0;
01846   int minor_version = 0;
01847   bool rc = false;
01848   bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
01849   if ( bInChunk )
01850   {
01851     rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
01852     if ( !rc )
01853       return false;
01854   }
01855   else
01856   {
01857     rc = true;
01858   }
01859 
01860   while(rc)
01861   {
01862     rc = ON_Annotation2::Read(archive)?true:false;
01863     if (!rc) break;
01864     if ( !bInChunk || minor_version <= 0 )
01865       break;
01866 
01867     // read future addition here
01868 
01869     break;
01870   }
01871 
01872   if ( bInChunk )
01873   {
01874     if ( !archive.EndRead3dmChunk() )
01875       rc = false;
01876   }
01877   return rc;
01878 }
01879 
01880 ON_BOOL32 ON_LinearDimension2::GetBBox(
01881         double* boxmax,
01882         double* boxmin,
01883         ON_BOOL32 bGrowBox
01884         ) const
01885 {
01886   ON_BoundingBox bbox;
01887   if ( bGrowBox )
01888   {
01889     bbox.m_min.x = boxmin[0]; 
01890     bbox.m_min.y = boxmin[1]; 
01891     bbox.m_min.z = boxmin[2];
01892     bbox.m_max.x = boxmax[0]; 
01893     bbox.m_max.y = boxmax[1]; 
01894     bbox.m_max.z = boxmax[2];
01895     if ( !bbox.IsValid() )
01896     {
01897       bbox.Destroy();
01898       bGrowBox = false;
01899     }
01900   }
01901 
01902   if ( 5 == m_points.Count() )
01903   {
01904     ON_3dPointArray P(5);
01905     ON_2dPoint uv;
01906     if ( m_userpositionedtext )
01907     {
01908       uv = m_points[0]; // point someplace in text
01909       P.Append( m_plane.PointAt(uv.x,uv.y) );
01910     }
01911 
01912     P.Append( m_plane.origin );
01913 
01914     uv.x = 0.0;
01915     uv.y = m_points[1].y;
01916     P.Append( m_plane.PointAt(uv.x,uv.y) );
01917 
01918     uv = m_points[2];
01919     P.Append( m_plane.PointAt(uv.x,uv.y) );
01920 
01921     uv.y = m_points[1].y;
01922     P.Append( m_plane.PointAt(uv.x,uv.y) );
01923     bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
01924   }
01925 
01926   if ( bGrowBox )
01927   {
01928     boxmin[0] = bbox.m_min.x; 
01929     boxmin[1] = bbox.m_min.y; 
01930     boxmin[2] = bbox.m_min.z; 
01931     boxmax[0] = bbox.m_max.x; 
01932     boxmax[1] = bbox.m_max.y; 
01933     boxmax[2] = bbox.m_max.z; 
01934   }
01935 
01936   return bGrowBox;
01937 }
01938 
01939 
01940 
01941 
01942 bool ON_LinearDimension2::GetTightBoundingBox( 
01943                 ON_BoundingBox& tight_bbox, 
01944     int bGrowBox,
01945                 const ON_Xform* xform
01946     ) const
01947 {
01948   if ( 5 == m_points.Count() )
01949   {
01950     ON_3dPointArray P(5);
01951     ON_2dPoint uv;
01952     if ( m_userpositionedtext )
01953     {
01954       uv = m_points[0]; // point someplace in text
01955       P.Append( m_plane.PointAt(uv.x,uv.y) );
01956     }
01957 
01958     P.Append( m_plane.origin );
01959 
01960     uv.x = 0.0;
01961     uv.y = m_points[1].y;
01962     P.Append( m_plane.PointAt(uv.x,uv.y) );
01963 
01964     uv = m_points[2];
01965     P.Append( m_plane.PointAt(uv.x,uv.y) );
01966 
01967     uv.y = m_points[1].y;
01968     P.Append( m_plane.PointAt(uv.x,uv.y) );
01969 
01970     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
01971       bGrowBox = true;
01972   }
01973   else if ( bGrowBox && !tight_bbox.IsValid() )
01974   {
01975     tight_bbox.Destroy();
01976     bGrowBox = false;
01977   }
01978 
01979   return (0!=bGrowBox);
01980 }
01981 
01982 int ON_LinearDimension2::Repair()
01983 {
01984   // returns 0 = unable to repair
01985   //         1 = in perfect condtion
01986   //         2 == repaired.
01987 
01988   const int ext0_pt_index   = ON_LinearDimension2::ext0_pt_index;
01989   const int arrow0_pt_index = ON_LinearDimension2::arrow0_pt_index;
01990   const int ext1_pt_index   = ON_LinearDimension2::ext1_pt_index;
01991   const int arrow1_pt_index = ON_LinearDimension2::arrow1_pt_index;
01992   const int userpositionedtext_pt_index = ON_LinearDimension2::userpositionedtext_pt_index;
01993   const int dim_pt_count    = ON_LinearDimension2::dim_pt_count;
01994 
01995   int rc = 0;
01996   if (    m_points.Count() >= dim_pt_count 
01997        && m_points[ext0_pt_index].IsValid() 
01998        && m_points[ext1_pt_index].IsValid() )
01999   {
02000     rc = 1;
02001     if ( !m_plane.IsValid() )
02002     {
02003       rc = ON_Plane_Repair(m_plane);
02004     }
02005 
02006     if ( m_points.Count() > dim_pt_count )
02007     {
02008       rc = 2;
02009       m_points.SetCount(dim_pt_count);
02010     }
02011 
02012     // m_points[ext0_pt_index] must be at (0,0)
02013     ON_2dVector v = m_points[ext0_pt_index];
02014     double d;
02015     int i;
02016     if ( !v.IsZero() )
02017     {
02018       rc = 2;
02019       m_plane.origin = m_plane.PointAt(v.x,v.y);
02020       m_plane.UpdateEquation();
02021       v.Reverse();
02022       for ( i = 0; i < dim_pt_count; i++ )
02023       {
02024         m_points[i] += v;
02025       }
02026       m_points[ext0_pt_index].Set(0.0,0.0);
02027     }
02028 
02029     if ( ON::dtDimAligned == m_type && (m_points[ext1_pt_index].x < 0.0 || m_points[ext1_pt_index].y != 0.0) )
02030     {
02031       rc = 2;
02032       // Aligned dims must have m_points[ext1_pt_index].x = m_points[ext0_pt_index].x
02033       if (   m_points[ext1_pt_index].x > 100.0*ON_SQRT_EPSILON
02034           && fabs(m_points[ext1_pt_index].y) <= ON_SQRT_EPSILON )
02035       {
02036         m_points[ext1_pt_index].y = 0.0;
02037       }
02038       else
02039       {
02040         // Dimension line parallel to point0 -> point2
02041         // rotate the plane so p2 is on the x axis
02042         v = m_points[ext1_pt_index];
02043         d = v.Length();
02044         v.Unitize();
02045         m_plane.Rotate(v.y,v.x,m_plane.zaxis,m_plane.origin);
02046 
02047         // rotate points in opposite direction
02048         v.y = -v.y; 
02049         for ( i = 0; i < dim_pt_count; i++ )
02050         {
02051           ON_2dPoint p = m_points[i];
02052           m_points[i].Set( v.x*p.x - v.y*p.y, v.y*p.x + v.x*p.y );
02053         }
02054         m_points[ext0_pt_index].Set(0.0,0.0);
02055         m_points[ext1_pt_index].Set(d,0.0);
02056       }
02057     }
02058     else if ( ON::dtDimLinear != m_type )
02059     {
02060       rc = 2;
02061       m_type = ON::dtDimLinear;
02062     }
02063 
02064     if ( m_points[arrow0_pt_index].x != m_points[ext0_pt_index].x )
02065     {
02066       rc = 2;
02067       m_points[arrow0_pt_index].x = m_points[ext0_pt_index].x;
02068     }
02069 
02070     if ( m_points[arrow1_pt_index].x != m_points[ext1_pt_index].x )
02071     {
02072       rc = 2;
02073       m_points[arrow1_pt_index].x = m_points[ext1_pt_index].x;
02074     }
02075 
02076     if ( !ON_IsValid(m_points[arrow0_pt_index].y) )
02077     {
02078       rc = 2;
02079       if ( !ON_IsValid(m_points[arrow1_pt_index].y) )
02080         m_points[arrow1_pt_index].y = 0.5*(m_points[ext0_pt_index].y + m_points[ext1_pt_index].y);
02081       m_points[arrow0_pt_index].y = m_points[arrow1_pt_index].y;
02082     }
02083     else if ( !ON_IsValid(m_points[arrow1_pt_index].y) )
02084     {
02085       rc = 2;
02086       m_points[arrow1_pt_index].y = m_points[arrow0_pt_index].y;
02087     }
02088     else if ( m_points[arrow0_pt_index].y != m_points[arrow1_pt_index].y )
02089     {
02090       rc = 2;
02091       d = 0.5*(m_points[arrow0_pt_index].y + m_points[arrow1_pt_index].y);
02092       m_points[arrow0_pt_index].y = d;
02093       m_points[arrow1_pt_index].y = d;
02094     }
02095 
02096     if ( m_userpositionedtext )
02097     {
02098       if ( !m_points[userpositionedtext_pt_index].IsValid() )
02099       {
02100         rc = 2;
02101         m_userpositionedtext = false;
02102       }
02103     }
02104     
02105     if ( !m_userpositionedtext )
02106     {
02107       if (    m_points[userpositionedtext_pt_index].y != m_points[arrow0_pt_index].y
02108            || m_points[userpositionedtext_pt_index].x != 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x) )
02109       {
02110         rc = 2;
02111         m_points[userpositionedtext_pt_index].y = m_points[arrow0_pt_index].y;
02112         m_points[userpositionedtext_pt_index].x = 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x);
02113       }
02114     }
02115 
02116     if ( !m_plane.IsValid() )
02117     {
02118       rc = 2;
02119       ON_Plane_Repair(m_plane);
02120     }
02121   }
02122   return rc;
02123 }
02124 
02125 ON_BOOL32 ON_LinearDimension2::Transform( const ON_Xform& xform )
02126 {
02127   // Dale Lear - this override fixes RR 11114 by correctly
02128   //             handling non uniform scaling.
02129   bool rc = xform.IsIdentity();
02130   if ( !rc)
02131   {
02132     ON_Plane plane = m_plane;
02133     if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) )
02134     {
02135       rc = true;
02136       ON_3dPoint P[dim_pt_count], Q[dim_pt_count];
02137       ON_2dVector p2[dim_pt_count], q2[dim_pt_count];
02138       int i;
02139       bool bUpdatePoints = false;
02140       for ( i = 0; i < dim_pt_count && rc; i++ )
02141       {
02142         p2[i] = m_points[i];
02143         P[i] = m_plane.PointAt( p2[i].x, p2[i].y );
02144         Q[i] = xform*P[i];
02145         if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) )
02146           rc = false;
02147         if (   fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON 
02148             || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON )
02149         {
02150           // transformation is not in SL3
02151           bUpdatePoints = true;
02152         }
02153       }
02154 
02155       if (rc)
02156       {
02157         ON_Geometry::Transform(xform);
02158         m_plane = plane;
02159         if ( bUpdatePoints )
02160         {
02161           for ( i = 0; i < dim_pt_count && rc; i++ )
02162           {
02163             m_points[i] = q2[i];
02164           }
02165           // Repair() will properly align the arrow points, etc.
02166           Repair();
02167         }
02168       }
02169     }
02170   }
02171 
02172   return rc;
02173 }
02174 
02175 double ON_LinearDimension2::NumericValue() const
02176 {
02177   // Use y coords of ext points instead of 3d distance
02178   // to reduce noise in the answer.
02179   return (m_points.Count() >= dim_pt_count) 
02180          ? fabs(m_points[ext0_pt_index].x - m_points[ext1_pt_index].x) 
02181          : 0.0;
02182 }
02183 
02184 int ON_LinearDimension2::StyleIndex() const
02185 {
02186   return ON_Annotation2::Index();
02187 }
02188 
02189 void ON_LinearDimension2::SetStyleIndex( int i)
02190 {
02191   ON_Annotation2::SetIndex( i);
02192 }
02193 
02194 /*
02195 
02196     ext0_pt_index    = 0, // end of first extension line
02197     arrow0_pt_index  = 1, // arrowhead tip on first extension line
02198     ext1_pt_index    = 2, // end of second extension line
02199     arrow1_pt_index  = 3, // arrowhead tip on second extension line
02200     dim_pt_count     = 5, // number of m_points[] in an angular dim
02201 
02202     // Points calculated from values in m_points[]
02203     text_pivot_pt = 10000 // center of dimension text
02204     dim_mid_pt    = 10001 // midpoint of dimension line
02205 
02206 */
02207 
02208 ON_2dPoint ON_LinearDimension2::Dim2dPoint( int point_index ) const
02209 {
02210   ON_2dPoint p2;
02211   if ( m_points.Count() < dim_pt_count )
02212   {
02213     p2.x = p2.y = ON_UNSET_VALUE;
02214   }
02215   else
02216   {
02217     if ( text_pivot_pt == point_index )
02218     {
02219       point_index = m_userpositionedtext ? userpositionedtext_pt_index : dim_mid_pt;
02220     }
02221 
02222     const ON_2dPoint* points = m_points.Array();
02223     switch(point_index)
02224     {
02225     case ext0_pt_index:
02226       p2 = points[0];
02227       break;
02228 
02229     case arrow0_pt_index:
02230       p2.x = points[0].x;
02231       p2.y = points[1].y;
02232       break;
02233 
02234     case ext1_pt_index:
02235       p2 = points[2];
02236       break;
02237 
02238     case arrow1_pt_index:
02239       p2.x = points[2].x;
02240       p2.y = points[1].y;
02241       break;
02242 
02243     case userpositionedtext_pt_index:
02244       p2.x = points[4].x;
02245       p2.y = points[4].y;
02246       break;
02247 
02248     case dim_mid_pt:
02249       p2.x = 0.5*(points[0].x + points[2].x);
02250       p2.y = points[1].y;
02251       break;
02252 
02253     default:
02254       p2.x = p2.y = ON_UNSET_VALUE;
02255       break;
02256     }
02257   }
02258   return p2;
02259 }
02260 
02261 ON_3dPoint ON_LinearDimension2::Dim3dPoint( int point_index ) const
02262 {
02263   ON_2dPoint p2 = Dim2dPoint(point_index);
02264   return (ON_UNSET_VALUE == p2.x) ? ON_UNSET_POINT : m_plane.PointAt(p2.x,p2.y);
02265 }
02266 
02267 
02268 // 6-23-03 lw Added v2 file writing of annotation
02269 void ON_LinearDimension2::GetV2Form( ON_LinearDimension& dim)
02270 {
02271   ON_Annotation2::ConvertBack( dim);
02272 }
02273 
02274 
02275 
02276 bool ON_LinearDimension2::CreateFromV2( 
02277     const ON_Annotation& v2_ann,
02278     const ON_3dmAnnotationSettings& settings,
02279     int dimstyle_index
02280     )
02281 {
02282   bool rc = false;
02283   if ( ON::dtDimLinear == v2_ann.m_type || ON::dtDimAligned == v2_ann.m_type )
02284   {
02285     if ( v2_ann.m_points.Count() >=  ON_LinearDimension2::dim_pt_count )
02286     {
02287       m_points.Reserve(ON_LinearDimension2::dim_pt_count);
02288       m_points.SetCount(0);
02289       m_points.Append(ON_LinearDimension2::dim_pt_count,v2_ann.Points().Array());
02290       m_userpositionedtext = v2_ann.UserPositionedText();
02291       m_type = v2_ann.m_type;
02292       SetTextValue(v2_ann.UserText());
02293       SetTextFormula(0);
02294 
02295       m_plane = v2_ann.m_plane;
02296       m_plane.UpdateEquation();
02297 
02298       switch( settings.m_textalign)
02299       {
02300       case 1:
02301         m_textdisplaymode =  ON::dtInLine;
02302         break;
02303       case 2:
02304         m_textdisplaymode = ON::dtHorizontal;
02305         break;
02306       default:
02307         m_textdisplaymode = ON::dtAboveLine;
02308         break;
02309       }
02310 
02311       ON_2dVector v = m_points[0];
02312       if ( !v.IsZero() )
02313       {
02314         m_plane.origin = m_plane.PointAt(v.x,v.y);
02315         m_plane.UpdateEquation();
02316         m_points[0].Set(0.0,0.0);
02317         v.Reverse();
02318         int i;
02319         for ( i = 1; i < ON_LinearDimension2::dim_pt_count; i++ )
02320         {
02321           m_points[i] += v;
02322         }
02323       }
02324 
02325       m_index = dimstyle_index;
02326       rc = true;
02327     }
02328   }
02329   return rc;
02330 }
02331 
02332 int ON_LinearDimension2::GetDimensionLineSegments(
02333     ON_RECT gdi_text_rect,
02334     int gdi_height_of_I,
02335     ON_Xform gdi_to_world,
02336     const ON_DimStyle& dimstyle,
02337     double dimscale,
02338     const ON_Viewport* vp,
02339     double x[6],
02340     bool& bInside
02341     ) const
02342 {
02343   int rc = 0;
02344 
02345   x[0] = 0.0;
02346   x[1] = 0.0;
02347   x[2] = 0.0;
02348   x[3] = 0.0;
02349   x[4] = 0.0;
02350   x[5] = 0.0;
02351   bInside = true;
02352 
02353   if ( m_points.Count() < 3 )
02354     return 0;
02355 
02356   int i = (m_points[ext0_pt_index].x <= m_points[ext1_pt_index].x) 
02357       ? ext0_pt_index 
02358       : ext1_pt_index;
02359   const double x0 = m_points[i].x;
02360   const double x1 = m_points[(ext0_pt_index==i) ? ext1_pt_index : ext0_pt_index].x;
02361   x[0] = x0;  // left  end of first dimension line
02362   x[1] = x1;  // right end of first dimension line
02363   x[2] = x0;  // left  end of second dimension line
02364   x[3] = x1;  // right end of second dimension line
02365   x[4] = x0;  // tip of first arrow
02366   x[5] = x1;  // tip of second arrow
02367 
02368   if ( 0 == gdi_height_of_I )
02369   {
02370     // Default to height of Ariel 'I'
02371     gdi_height_of_I = (165*ON_Font::normal_font_height)/256;
02372   }
02373 
02374   if ( 0.0 == dimscale )
02375   {
02376     dimscale = 1.0;
02377   }
02378 
02379   double t;
02380 
02381   ON::eTextDisplayMode textdisplay = ON::TextDisplayMode(dimstyle.TextAlignment());
02382   if ( ON::dtHorizontal == textdisplay && !vp )
02383     textdisplay = ON::dtInLine;
02384 
02385   const double text_height_of_I    = dimscale*dimstyle.TextHeight();
02386   const double textgap             = fabs(dimscale*dimstyle.TextGap());
02387   const double gdi_to_plane_scale  = text_height_of_I/gdi_height_of_I;
02388   const double textwidth           = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left));
02389   const double arrowwidth          = dimscale*dimstyle.ArrowSize();
02390   const double tailwidth           = 0.5*arrowwidth;
02391   const double dimwidth            = x1 - x0;
02392   const double mindimwidth         = (ON::dtInLine == textdisplay)
02393                                    ? (2.0*(arrowwidth + tailwidth + textgap) + textwidth)
02394                                    : (2.0*arrowwidth + tailwidth);
02395   const double dimextension        = dimscale*dimstyle.DimExtension();
02396 
02397   int ForceArrows = 0;
02398   const ON_DimensionExtra* pDE = ON_DimensionExtra::DimensionExtension(this,false);
02399   if( pDE)
02400     ForceArrows = pDE->ArrowPosition();
02401 
02402   // 19 Apr 2012 - Lowell - Fixed so forcing arrows inside won't cause the dim line to 
02403   // draw through InLine text  rr103322
02404   if(ForceArrows == -1 ||        // force outside
02405      (dimwidth < mindimwidth  && ForceArrows != 1))    // arrowheads have to be "outside" - they won't fit inside even without text
02406   {
02407     t = arrowwidth + tailwidth;
02408     x[0] = x0;
02409     x[1] = x0-t;
02410     x[2] = x1+t;
02411     x[3] = x1;
02412     x[4] = x0;  // arrow tips
02413     x[5] = x1;
02414     if( dimextension != 0.0)
02415     {
02416       x[0] += dimextension;
02417       x[3] -= dimextension;
02418     }
02419     bInside = false;
02420     rc = 2;
02421   }
02422   // Horizontal text or Userpositioned text
02423   else if ( (ON::dtHorizontal == textdisplay && vp) || m_userpositionedtext )
02424   {
02425     // use projected rectangle to clip dimension line
02426     double xx0, xx1, xx, y0, y1, t;
02427     ON_3dPoint P, R;
02428     ON_2dPoint corners[4];  // corners of text rect in plane coords
02429     ON_Line ray;
02430 
02431     ON_3dVector vp_zaxis = (ON::dtHorizontal == textdisplay && vp)
02432                          ? vp->CameraZ() 
02433                          : m_plane.zaxis;
02434 
02435     // 30 July 2012 - Lowell - Slightly shrink the text gap value
02436     // so that text + gap won't intersect the dim line if when text
02437     // is moved along the dim line. rr110504
02438     double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12;
02439     if(gdi_gap < 0.0) gdi_gap = 0.0;
02440 
02441     R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
02442     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
02443     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
02444     m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y);
02445 
02446     R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
02447     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
02448     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
02449     m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y);
02450 
02451     R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
02452     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
02453     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
02454     m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y);
02455 
02456     R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
02457     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
02458     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
02459     m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y);
02460 
02461     // Test if text rect intersects dimension line
02462     xx0 = xx1 = ON_UNSET_VALUE;
02463     for ( i = 0; i < 4; i++ )
02464     {
02465       y0 = corners[i].y       - m_points[1].y;    // vertical dist from corner to dimension line
02466       y1 = corners[(i+1)%4].y - m_points[1].y;    // vertical dist from next corner to dimension line
02467       // if they're both above or both below, no intersection of this segment
02468       if ( (y0 > 0.0 && y1 > 0.0) || (y0 < 0.0 && y1 < 0.0) || y0 == y1 )
02469         continue;
02470 
02471       // segment intersects dimension line.
02472       // find x-coord of intersection
02473       t = y0/(y0-y1);
02474       xx = (1.0-t)*corners[i].x + t*corners[(i+1)%4].x;
02475       if ( ON_UNSET_VALUE == xx0 )
02476       {
02477         xx0 = xx1 = xx;
02478       }
02479       else if ( xx < xx0 )
02480       {
02481         xx0 = xx;
02482       }
02483       else if ( xx > xx1 )
02484       {
02485         xx1 = xx;
02486       }
02487     }
02488 
02489     // Nov 4 2009 - Lowell - Added test for no intersection of text with dimension line rr33003
02490     // Important for user positioned text that's moved out of the way of the dimension line
02491     if(xx0 != ON_UNSET_VALUE && xx1 != ON_UNSET_VALUE)
02492     {
02493       // xx0 is left edge of text rect
02494       // xx1 is right edge of text rect
02495       t = arrowwidth + tailwidth;
02496       if ( x0 + t <= xx0 && xx0 < xx1 && xx1 <= x1 - t )
02497       {
02498         // clip line, 2 segments arrows inside
02499         x[0] = x0;
02500         x[1] = xx0;
02501         x[2] = xx1;
02502         x[3] = x1;
02503         x[4] = x0;
02504         x[5] = x1;
02505         if( dimextension != 0.0)
02506         {
02507           x[0] -= dimextension;
02508           x[3] += dimextension;
02509         }
02510         rc = 2;
02511       }
02512       else  // 2 segments, put the arrows outside the extension lines
02513       {
02514         x[0] = x0;
02515         x[1] = x0 - t;
02516         x[2] = x1 + t;
02517         x[3] = x1;
02518         x[4] = x0;
02519         x[5] = x1;
02520         if( dimextension != 0.0)
02521         {
02522           x[0] += dimextension;
02523           x[3] -= dimextension;
02524         }
02525         bInside = false;
02526         rc = 2;
02527       }
02528     }
02529     else
02530     {
02531       // 1 segment, arrows inside
02532       x[0] = x0;
02533       x[1] = x1;
02534       x[2] = x0;
02535       x[3] = x1;
02536       x[4] = x0;
02537       x[5] = x1;
02538       if( dimextension != 0.0)
02539       {
02540         x[0] -= dimextension;
02541         x[1] += dimextension;
02542       }
02543       rc = 1;
02544     }
02545   }
02546   // Above line text
02547   else if ( ON::dtAboveLine == textdisplay || m_userpositionedtext )
02548   {
02549     // 1 segment, arrows inside
02550     x[0] = x0;
02551     x[1] = x1;
02552     x[2] = x0;
02553     x[3] = x1;
02554     x[4] = x0;
02555     x[5] = x1;
02556     if( dimextension != 0.0)
02557     {
02558       x[0] -= dimextension;
02559       x[1] += dimextension;
02560     }
02561     rc = 1;
02562   }
02563   // In line text
02564   else if ( ON::dtInLine == textdisplay )
02565   {
02566     // 2 segments, arrows inside
02567     t = 0.5*(dimwidth - textwidth - 2.0*textgap);
02568     x[0] = x0;
02569     x[1] = x0+t;
02570     x[2] = x1-t;
02571     x[3] = x1;
02572     x[4] = x0;
02573     x[5] = x1;
02574     if( dimextension != 0.0)
02575     {
02576       x[0] -= dimextension;
02577       x[3] += dimextension;
02578     }
02579 
02580     rc = 2;
02581   }
02582 
02583   return rc;
02584 }
02585 
02586 
02587 const wchar_t* ON_LinearDimension2::DefaultText() { return L"<>"; }
02588 
02589 
02590 
02591 
02592 //----- ON_RadialDimension2 ------------------------------------------
02593 ON_RadialDimension2::ON_RadialDimension2()
02594 {
02595   m_type = ON::dtDimDiameter;
02596   m_textdisplaymode = ON::dtInLine;
02597   SetTextValue(DefaultDiameterText());
02598   SetTextFormula(0);
02599   m_points.Reserve(ON_RadialDimension2::dim_pt_count);
02600   m_points.SetCount(ON_RadialDimension2::dim_pt_count);
02601   m_points.Zero();
02602 }
02603 
02604 ON_RadialDimension2::~ON_RadialDimension2()
02605 {
02606 }
02607 
02608 ON_BOOL32 ON_RadialDimension2::IsValid( ON_TextLog* text_log ) const
02609 {
02610   if ( m_type != ON::dtDimRadius && m_type != ON::dtDimDiameter  )
02611   {
02612     if ( text_log )
02613     {
02614       text_log->Print("ON_RadialDimension2 - m_type !=  ON::dtDimRadius or ON::dtDimDiameter\n");
02615     }
02616     return false;
02617   }
02618 
02619   if ( !ON_Annotation2::IsValid( text_log ))
02620   {
02621     if ( text_log )
02622     {
02623       text_log->Print("ON_RadialDimension2 - invalid ON_Annotation2 base class.\n");
02624     }
02625     return false;
02626   }
02627 
02628   if ( 4 != m_points.Count() )
02629   {
02630     if ( text_log )
02631     {
02632       text_log->Print("ON_RadialDimension2 - m_points.Count() = %d (should be 4 or 5)\n", m_points.Count() );
02633     }
02634     return false;
02635   }
02636 
02637   return true;
02638 }
02639 
02640 
02641 ON_BOOL32 ON_RadialDimension2::Write(ON_BinaryArchive& archive) const
02642 {
02643   // 18 October 2007 Dale Lear
02644   //    I added the chunk wrapping so V5 and future versions can
02645   //    add IO support for information specific to ON_RadialDimension2
02646   //    V4 did not have a ON_RadialDimension2::Write and simply called
02647   //    ON_Annotation2::Write.
02648   bool rc = false;
02649   bool bInChunk = (archive.Archive3dmVersion() >= 5);
02650   if ( bInChunk )
02651   {
02652     rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
02653     if ( !rc )
02654       return false;
02655   }
02656   else
02657   {
02658     rc = true;
02659   }
02660 
02661   while(rc)
02662   {
02663     rc = ON_Annotation2::Write(archive)?true:false;
02664     if (!rc) break;
02665     if ( !bInChunk )
02666       break;
02667 
02668     // To write new fields, increment minor version number
02669     // and write values here.  Ask Dale Lear for help.
02670 
02671     break;
02672   }
02673 
02674   if ( bInChunk )
02675   {
02676     if (!archive.EndWrite3dmChunk())
02677       rc = false;
02678   }
02679   return rc;
02680 }
02681 
02682 ON_BOOL32 ON_RadialDimension2::Read(ON_BinaryArchive& archive)
02683 {
02684   // 18 October 2007 Dale Lear
02685   //    I added the chunk wrapping so V5 and future versions can
02686   //    add IO support for information specific to ON_RadialDimension2
02687   int major_version = 0;
02688   int minor_version = 0;
02689   bool rc = false;
02690   bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
02691   if ( bInChunk )
02692   {
02693     rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
02694     if ( !rc )
02695       return false;
02696   }
02697   else
02698   {
02699     rc = true;
02700   }
02701 
02702   while(rc)
02703   {
02704     rc = ON_Annotation2::Read(archive)?true:false;
02705     if (!rc) break;
02706     if ( !bInChunk || minor_version <= 0 )
02707       break;
02708 
02709     // read future addition here
02710 
02711     break;
02712   }
02713 
02714   if ( bInChunk )
02715   {
02716     if ( !archive.EndRead3dmChunk() )
02717       rc = false;
02718   }
02719   return rc;
02720 }
02721 
02722 
02723 ON_BOOL32 ON_RadialDimension2::GetBBox(
02724         double* boxmax,
02725         double* boxmin,
02726         ON_BOOL32 bGrowBox
02727         ) const
02728 {
02729   ON_BoundingBox bbox;
02730   if ( bGrowBox )
02731   {
02732     bbox.m_min.x = boxmin[0]; 
02733     bbox.m_min.y = boxmin[1]; 
02734     bbox.m_min.z = boxmin[2];
02735     bbox.m_max.x = boxmax[0]; 
02736     bbox.m_max.y = boxmax[1]; 
02737     bbox.m_max.z = boxmax[2];
02738     if ( !bbox.IsValid() )
02739     {
02740       bbox.Destroy();
02741       bGrowBox = false;
02742     }
02743   }
02744 
02745   if ( 4 == m_points.Count() )
02746   {
02747     ON_3dPointArray P(4);
02748     ON_2dPoint uv;
02749     if ( m_userpositionedtext )
02750     {
02751       uv = m_points[0]; // point someplace in text
02752       P.Append( m_plane.PointAt(uv.x,uv.y) );
02753     }
02754 
02755     P.Append( m_plane.origin ); // + sign at center of dimension
02756 
02757     uv = m_points[1];
02758     P.Append( m_plane.PointAt(uv.x,uv.y) );
02759 
02760     uv = m_points[2];
02761     P.Append( m_plane.PointAt(uv.x,uv.y) );
02762 
02763     uv = m_points[3];
02764     P.Append( m_plane.PointAt(uv.x,uv.y) );
02765     bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
02766   }
02767 
02768   if ( bGrowBox )
02769   {
02770     boxmin[0] = bbox.m_min.x; 
02771     boxmin[1] = bbox.m_min.y; 
02772     boxmin[2] = bbox.m_min.z; 
02773     boxmax[0] = bbox.m_max.x; 
02774     boxmax[1] = bbox.m_max.y; 
02775     boxmax[2] = bbox.m_max.z; 
02776   }
02777 
02778   return bGrowBox;
02779 }
02780 
02781 
02782 
02783 
02784 
02785 bool ON_RadialDimension2::GetTightBoundingBox( 
02786                 ON_BoundingBox& tight_bbox, 
02787     int bGrowBox,
02788                 const ON_Xform* xform
02789     ) const
02790 {
02791   if ( 4 == m_points.Count() )
02792   {
02793     ON_3dPointArray P(4);
02794     ON_2dPoint uv;
02795 
02796     uv = m_points[0]; // + sign at center of dimension (usually at (0,0)
02797     P.Append( m_plane.PointAt(uv.x,uv.y) );
02798 
02799     uv = m_points[1];
02800     P.Append( m_plane.PointAt(uv.x,uv.y) );
02801 
02802     uv = m_points[2];
02803     P.Append( m_plane.PointAt(uv.x,uv.y) );
02804 
02805     uv = m_points[3];
02806     P.Append( m_plane.PointAt(uv.x,uv.y) );
02807 
02808     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
02809       bGrowBox = true;
02810   }
02811   else if ( bGrowBox && !tight_bbox.IsValid() )
02812   {
02813     tight_bbox.Destroy();
02814     bGrowBox = false;
02815   }
02816 
02817   return (0!=bGrowBox);
02818 }
02819 
02820 ON_2dPoint ON_RadialDimension2::Dim2dPoint(
02821       int point_index
02822       ) const
02823 {
02824   ON_2dPoint p2;
02825   if ( m_points.Count() < dim_pt_count || point_index < 0 )
02826   {
02827     p2.x = p2.y = ON_UNSET_VALUE;
02828   }
02829   else
02830   {
02831     if ( text_pivot_pt == point_index )
02832     {
02833       point_index = tail_pt_index;
02834     }
02835     if ( point_index < dim_pt_count )
02836     {
02837       p2 = m_points[point_index];
02838     }    
02839     else
02840     {
02841       p2.x = p2.y = ON_UNSET_VALUE;
02842     }
02843   }
02844   return p2;
02845 }
02846 
02847 ON_3dPoint ON_RadialDimension2::Dim3dPoint(
02848       int point_index
02849       ) const
02850 {
02851   ON_2dPoint p2 = Dim2dPoint(point_index);
02852   return (ON_UNSET_VALUE == p2.x) ? ON_UNSET_POINT : m_plane.PointAt(p2.x,p2.y);
02853 }
02854 
02855 // set the plane, center, and point on curve.
02856 // the rest will be set by the Rhino dimension
02857 bool ON_RadialDimension2::CreateFromPoints( 
02858   ON_3dPoint center, 
02859   ON_3dPoint arrowtip, 
02860   ON_3dVector xaxis, 
02861   ON_3dVector normal,
02862   double offset_distance)
02863 {
02864   if ( m_type != ON::dtDimDiameter )
02865     m_type = ON::dtDimRadius;
02866   bool rc = false;
02867   if ( center.IsValid() && arrowtip.IsValid() && normal.IsValid() && !normal.IsZero() && xaxis.IsValid() && !xaxis.IsZero() )
02868   {
02869     ON_Plane plane( center, normal);
02870     double c = xaxis*plane.xaxis;
02871     double s = xaxis*plane.yaxis;
02872     if ( c != 0.0 || s != 0.0 )
02873     {
02874       if ( c <= 0.0 || s != 0.0 )
02875       {
02876         plane.Rotate( s, c, plane.zaxis );
02877       }
02878       m_plane = plane;
02879       ON_2dVector tip;
02880       if ( m_plane.ClosestPointTo(arrowtip,&tip.x,&tip.y) )
02881       {
02882         //double r = tip.Length();
02883         m_points.SetCapacity(dim_pt_count);
02884         m_points.SetCount(dim_pt_count);
02885 
02886         m_points[center_pt_index].Set(0.0,0.0);
02887         m_points[arrow_pt_index] = tip;
02888         tip.Unitize();
02889 
02890         m_points[knee_pt_index] = m_points[arrow_pt_index] + offset_distance*tip;
02891         m_points[tail_pt_index] = m_points[knee_pt_index];
02892         if ( m_points[arrow_pt_index].x < 0.0 )
02893           m_points[tail_pt_index].x -= offset_distance;
02894         else
02895           m_points[tail_pt_index].x += offset_distance;
02896         m_plane = plane;
02897         m_userpositionedtext = false;
02898 
02899         rc = true;
02900       }
02901     }
02902   }
02903   return rc;
02904 }
02905 
02906 
02907 double ON_RadialDimension2::NumericValue() const
02908 {
02909   double d = 0.0;
02910   if ( m_points.Count() >= dim_pt_count )
02911   {
02912     d = (m_points[center_pt_index] - m_points[arrow_pt_index]).Length();
02913     if( ON::dtDimDiameter == m_type )
02914       d *= 2.0;
02915   }
02916   return d;
02917 }
02918 
02919 int ON_RadialDimension2::StyleIndex() const
02920 {
02921   return ON_Annotation2::Index();
02922 }
02923 
02924 void ON_RadialDimension2::SetStyleIndex( int i)
02925 {
02926   ON_Annotation2::SetIndex( i);
02927 }
02928 
02929 
02930 const wchar_t* ON_RadialDimension2::DefaultRadiusText()
02931 {
02932   static wchar_t defstr[4] = { radiussym,L'<',L'>',0 };
02933   return defstr;
02934 }
02935 
02936 const wchar_t* ON_RadialDimension2::DefaultDiameterText()
02937 {
02938   static wchar_t defstr[4] = { diametersym,L'<',L'>',0 };
02939   return defstr;
02940 }
02941 
02942 // 6-23-03 lw Added v2 file writing of annotation
02943 void ON_RadialDimension2::GetV2Form( ON_RadialDimension& dim)
02944 {
02945   ON_Annotation2::ConvertBack( dim);
02946 }
02947 
02948 
02949 bool ON_RadialDimension2::CreateFromV2( 
02950     const ON_Annotation& v2_ann,
02951     const ON_3dmAnnotationSettings& settings,
02952     int dimstyle_index
02953     )
02954 {
02955   bool rc = false;
02956   if( ON::dtDimRadius == v2_ann.m_type || ON::dtDimDiameter == v2_ann.m_type )
02957   {
02958     const ON_SimpleArray<ON_2dPoint>& points = v2_ann.Points();
02959     if ( points.Count() >= 4 )
02960     {
02961       m_points.Reserve(4);
02962       m_points.SetCount(0);
02963       m_points.Append(4,points.Array());
02964       m_plane = v2_ann.m_plane;
02965       m_plane.UpdateEquation();
02966       SetTextValue(v2_ann.UserText());
02967       SetTextFormula(0);
02968       m_userpositionedtext = false;
02969       m_type = v2_ann.Type();
02970       m_textdisplaymode = ( 2 == settings.m_textalign )
02971                         ? ON::dtHorizontal
02972                         : ON::dtInLine;
02973       m_index = dimstyle_index;
02974       ON_2dVector v = m_points[0];
02975       if ( !v.IsZero() )
02976       {
02977         m_plane.origin = m_plane.PointAt(v.x,v.y);
02978         m_plane.UpdateEquation();
02979         v.Reverse();
02980         m_points[0].Set(0.0,0.0);
02981         m_points[1] += v;
02982         m_points[2] += v;
02983         m_points[3] += v;
02984       }
02985 
02986       rc = true;
02987     }
02988   }
02989   return rc;
02990 }
02991 
02992 
02993 //----- ON_AngularDimension2Extra -----------------------------------------
02994 // Additions to ON_AngularDimension2 class
02995 
02996 class ON_AngularDimension2Extra : public ON_UserData
02997 {
02998   ON_OBJECT_DECLARE(ON_AngularDimension2Extra);
02999 public:
03000   static ON_AngularDimension2Extra* AngularDimensionExtra(ON_AngularDimension2* pDim/*, bool bCreate*/);
03001   static const ON_AngularDimension2Extra* AngularDimensionExtra(const ON_AngularDimension2* pDim/*, bool bCreate*/);
03002 
03003   ON_AngularDimension2Extra();
03004   ~ON_AngularDimension2Extra();
03005 
03006   // override virtual ON_Object::Dump function
03007   void Dump( ON_TextLog& text_log ) const;
03008 
03009   // override virtual ON_Object::SizeOf function
03010   unsigned int SizeOf() const;
03011 
03012   // override virtual ON_Object::Write function
03013   ON_BOOL32 Write(ON_BinaryArchive& binary_archive) const;
03014 
03015   // override virtual ON_Object::Read function
03016   ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
03017 
03018   // override virtual ON_UserData::GetDescription function
03019   ON_BOOL32 GetDescription( ON_wString& description );
03020 
03021   // override virtual ON_UserData::Archive function
03022   ON_BOOL32 Archive() const; 
03023 
03024   // Scale all of the length values
03025   void Scale( double scale);
03026 
03027   // 
03028   double DimpointOffset(int index) const;
03029   void SetDimpointOffset(int index, double offset);
03030 
03031   // offsets from apex of dimension to point from which extension lines start
03032   // if these are < 0.0, they are ignored
03033   // Extension lines are drawn from theses points to the Arrow tip points
03034   // subject to dimexe & dimexo & dimse1 & dimse2
03035   double m_dimpoint_offset[2];
03036 };
03037 
03038 ON_OBJECT_IMPLEMENT(ON_AngularDimension2Extra,ON_UserData,"A68B151F-C778-4a6e-BCB4-23DDD1835677");
03039 
03040 ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(ON_AngularDimension2* pDim)
03041 {
03042   ON_AngularDimension2Extra* pExtra = 0;
03043   if(pDim)
03044   {
03045     pExtra = ON_AngularDimension2Extra::Cast(pDim->GetUserData(ON_AngularDimension2Extra::m_ON_AngularDimension2Extra_class_id.Uuid()));
03046     if(pExtra == 0)
03047     {
03048       pExtra = new ON_AngularDimension2Extra;
03049       if( pExtra)
03050       {
03051         if(!pDim->AttachUserData(pExtra))
03052         {
03053           delete pExtra;
03054           pExtra = 0;
03055         }
03056       }
03057     }
03058   }
03059   return pExtra;
03060 }
03061 
03062 const ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(const ON_AngularDimension2* pDim)
03063 {
03064   return AngularDimensionExtra((ON_AngularDimension2*)pDim);
03065 }
03066 
03067 ON_AngularDimension2Extra::ON_AngularDimension2Extra()
03068 {
03069   m_userdata_uuid = ON_AngularDimension2Extra::m_ON_AngularDimension2Extra_class_id.Uuid();
03070   m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
03071                                          // The id must be the version 5 id because
03072                                          // V6 SaveAs V5 needs to work, but SaveAs
03073                                          // V4 should not write this userdata.
03074   m_userdata_copycount = 1;
03075 
03076   m_dimpoint_offset[0] = 0;
03077   m_dimpoint_offset[1] = 0;
03078 }
03079 
03080 ON_AngularDimension2Extra::~ON_AngularDimension2Extra()
03081 {
03082 }
03083 
03084 void ON_AngularDimension2Extra::Dump( ON_TextLog& text_log ) const
03085 {
03086   // do nothing
03087 }
03088 
03089 unsigned int ON_AngularDimension2Extra::SizeOf() const
03090 {
03091   unsigned int sz = ON_UserData::SizeOf();
03092   sz += sizeof(*this) - sizeof(ON_UserData);
03093   return sz;
03094 }
03095 
03096 ON_BOOL32 ON_AngularDimension2Extra::Write(ON_BinaryArchive& archive) const
03097 {
03098   int major_version = 1;
03099   int minor_version = 0;
03100   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);
03101 
03102   if(rc) rc = archive.WriteDouble(m_dimpoint_offset[0]);
03103   if(rc) rc = archive.WriteDouble(m_dimpoint_offset[1]);
03104 
03105   if(!archive.EndWrite3dmChunk())
03106     rc = false;
03107 
03108   return rc;
03109 }
03110 
03111 ON_BOOL32 ON_AngularDimension2Extra::Read(ON_BinaryArchive& archive)
03112 {
03113   int major_version = 1;
03114   int minor_version = 0;
03115   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
03116   if(major_version != 1)
03117     rc = false;
03118 
03119   if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[0]);
03120   if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[1]);
03121 
03122   if ( !archive.EndRead3dmChunk() )
03123     rc = false;
03124 
03125   return rc;
03126 }
03127 
03128 ON_BOOL32 ON_AngularDimension2Extra::GetDescription( ON_wString& description)
03129 {
03130   description.Format( "Userdata extension of ON_AngularDimension2");
03131   return true;
03132 }
03133 
03134 ON_BOOL32 ON_AngularDimension2Extra::Archive() const
03135 {
03136   // true to write to file
03137   return true;
03138 }
03139 
03140 void ON_AngularDimension2Extra::Scale(double scale)
03141 {
03142   if( ON_IsValid(scale) && scale > ON_SQRT_EPSILON)
03143   {
03144     m_dimpoint_offset[0] *= scale;
03145     m_dimpoint_offset[1] *= scale;
03146   }
03147 }
03148 
03149 double ON_AngularDimension2Extra::DimpointOffset(int index) const
03150 {
03151   if(index == 0)
03152     return m_dimpoint_offset[0];
03153   if(index == 1)
03154     return m_dimpoint_offset[1];
03155   return -1;
03156 }
03157 void ON_AngularDimension2Extra::SetDimpointOffset(int index, double offset)
03158 {
03159   if(index >= 0 && index <= 1)
03160     m_dimpoint_offset[index] = offset;
03161 }
03162 
03163 //----- ON_AngularDimension2 -----------------------------------------
03164 ON_AngularDimension2::ON_AngularDimension2() : m_angle(0.0), m_radius(1.0)
03165 {
03166   m_type = ON::dtDimAngular;
03167   m_textdisplaymode = ON::dtAboveLine;
03168   SetTextValue(DefaultText());
03169   SetTextFormula(0);
03170   m_points.Reserve(ON_AngularDimension2::dim_pt_count);
03171   m_points.SetCount(ON_AngularDimension2::dim_pt_count);
03172   m_points.Zero();
03173 
03174   // Add this userdata to every angular dimension
03175 //  ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this, true);
03176 //  if(pDE)
03177 //  {
03178 //    pDE->SetDimpointOffset(0, -1);
03179 //    pDE->SetDimpointOffset(1, -1);
03180 //  }
03181 }
03182 
03183 ON_AngularDimension2::~ON_AngularDimension2()
03184 {
03185 }
03186 
03187 
03188 ON_BOOL32 ON_AngularDimension2::IsValid( ON_TextLog* text_log ) const
03189 {
03190   if ( m_type != ON::dtDimAngular )
03191   {
03192     if ( text_log )
03193     {
03194       text_log->Print("ON_AngularDimension2 - m_type !=  ON::dtDimAngular\n");
03195     }
03196     return false;
03197   }
03198 
03199   if ( !ON_Annotation2::IsValid( text_log ))
03200   {
03201     if ( text_log )
03202     {
03203       text_log->Print("ON_AngularDimension2 - invalid ON_Annotation2 base class.\n");
03204     }
03205     return false;
03206   }
03207 
03208   if ( 4 != m_points.Count() )
03209   {
03210     if ( text_log )
03211     {
03212       text_log->Print("ON_AngularDimension2 - m_points.Count() = %d (should be 4)\n", m_points.Count() );
03213     }
03214     return false;
03215   }
03216 
03217   if ( !ON_IsValid(m_angle) || m_angle <= 0.0 || m_angle > 2.0*ON_PI )
03218   {
03219     if ( text_log )
03220     {
03221       text_log->Print("ON_AngularDimension2 - bogus m_angle = %g\n",m_angle);
03222     }
03223     return false;
03224   }
03225 
03226   if ( !ON_IsValid(m_radius) || m_radius <= 0.0 )
03227   {
03228     if ( text_log )
03229     {
03230       text_log->Print("ON_AngularDimension2 - bogus m_radius = %g\n",m_radius);
03231     }
03232     return false;
03233   }
03234 
03235   if ( 0.0 == m_points[1].x && 0.0 == m_points[1].y )
03236   {
03237     if ( text_log )
03238     {
03239       text_log->Print("ON_AngularDimension2 - angle dim m_points[1] = center (should be on start ray).\n");
03240     }
03241     return false;
03242   }
03243 
03244   if ( 0.0 == m_points[2].x && 0.0 == m_points[2].y )
03245   {
03246     if ( text_log )
03247     {
03248       text_log->Print("ON_AngularDimension2 - angle dim m_points[2] = center (should be on end ray).\n");
03249     }
03250     return false;
03251   }
03252 
03253   if ( 0.0 == m_points[3].x && 0.0 == m_points[3].y )
03254   {
03255     if ( text_log )
03256     {
03257       text_log->Print("ON_AngularDimension2 - angle dim m_points[3] = center (should be on interior of arc).\n");
03258     }
03259     return false;
03260   }
03261 
03262   double a1 = atan2(m_points[1].y, m_points[1].x);
03263   double a2 = atan2(m_points[2].y, m_points[2].x);
03264   double a3 = atan2(m_points[3].y, m_points[3].x);
03265   if ( a1 < 0.0 )
03266     a1 += 2.0*ON_PI;
03267   while ( a2 <= a1 )
03268     a2 += 2.0*ON_PI;
03269   while ( a3 < a1 )  // Oct 23 2009 LW changed from a3 <= a1 to allow point at end of arc rr53634
03270     a3 += 2.0*ON_PI;
03271 
03272   if ( fabs(m_angle - (a2-a1)) > ON_ZERO_TOLERANCE + m_angle*ON_SQRT_EPSILON )
03273   {
03274     if ( text_log )
03275     {
03276       text_log->Print("ON_AngularDimension2 - m_angle = %g != %g = (end angle - start angle)\n",m_angle,a2-a1);
03277     }
03278     return false;
03279   }
03280 
03281   double r = ON_2dVector(m_points[3]).Length();
03282   if ( fabs(r - m_radius) > ON_ZERO_TOLERANCE + m_radius*ON_SQRT_EPSILON )
03283   {
03284     if ( text_log )
03285     {
03286       text_log->Print("ON_AngularDimension2 - m_radius = %g != %g = |m_point[3])|\n",m_radius,r);
03287     }
03288     return false;
03289   }
03290   
03291 
03292   if ( a3 > a2 )  // Oct 23 2009 LW changed from a3 >= a2 to allow point at end of arc rr53634
03293   {
03294     if ( text_log )
03295     {
03296       text_log->Print("ON_AngularDimension2 - angle dim m_points[3] = not on arc interior.\n");
03297     }
03298     return false;
03299   }
03300 
03301   return true;
03302 }
03303 
03304 
03305 
03306 ON_BOOL32 ON_AngularDimension2::GetBBox(
03307         double* boxmax,
03308         double* boxmin,
03309         ON_BOOL32 bGrowBox
03310         ) const
03311 {
03312   ON_BoundingBox bbox;
03313   if ( bGrowBox )
03314   {
03315     bbox.m_min.x = boxmin[0]; 
03316     bbox.m_min.y = boxmin[1]; 
03317     bbox.m_min.z = boxmin[2];
03318     bbox.m_max.x = boxmax[0]; 
03319     bbox.m_max.y = boxmax[1]; 
03320     bbox.m_max.z = boxmax[2];
03321     if ( !bbox.IsValid() )
03322     {
03323       bbox.Destroy();
03324       bGrowBox = false;
03325     }
03326   }
03327 
03328   ON_Arc arc;
03329   if ( GetArc(arc) )
03330   {
03331     if ( arc.GetTightBoundingBox(bbox,bGrowBox?true:false,0) )
03332       bGrowBox = true;    
03333   }
03334 
03335   if ( bGrowBox )
03336   {
03337     boxmin[0] = bbox.m_min.x; 
03338     boxmin[1] = bbox.m_min.y; 
03339     boxmin[2] = bbox.m_min.z; 
03340     boxmax[0] = bbox.m_max.x; 
03341     boxmax[1] = bbox.m_max.y; 
03342     boxmax[2] = bbox.m_max.z; 
03343   }
03344 
03345   return bGrowBox;
03346 }
03347 
03348 
03349 
03350 
03351 
03352 bool ON_AngularDimension2::GetTightBoundingBox( 
03353                 ON_BoundingBox& tight_bbox, 
03354     int bGrowBox,
03355                 const ON_Xform* xform
03356     ) const
03357 {
03358   ON_Arc arc;
03359   if ( GetArc(arc) )
03360   {
03361     if ( arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform) )
03362       bGrowBox = true;    
03363   }
03364   else if ( bGrowBox && !tight_bbox.IsValid() )
03365   {
03366     tight_bbox.Destroy();
03367     bGrowBox = false;
03368   }
03369 
03370   return (0!=bGrowBox);
03371 
03372 }
03373 
03374 ON_2dPoint ON_AngularDimension2::Dim2dPoint( int point_index ) const
03375 {
03376   ON_2dPoint p2;
03377   if ( m_points.Count() < dim_pt_count || point_index < 0 )
03378   {
03379     p2.x = p2.y = ON_UNSET_VALUE;
03380   }
03381   else
03382   {
03383     if ( text_pivot_pt == point_index )
03384     {
03385       point_index = m_userpositionedtext 
03386                   ? userpositionedtext_pt_index
03387                   : arcmid_pt;
03388     }
03389     
03390     if ( point_index < dim_pt_count )
03391     {
03392       p2 = m_points[point_index];
03393     }
03394     else
03395     {
03396       switch(point_index)
03397       {
03398       case arcstart_pt:
03399         p2.x = m_radius;
03400         p2.y = 0.0;
03401         break;
03402       case arcend_pt:
03403         p2.x = m_radius*cos(m_angle);
03404         p2.y = m_radius*sin(m_angle);
03405         break;
03406       case arccenter_pt:
03407         p2.x = 0.0;
03408         p2.y = 0.0;
03409         break;
03410       case arcmid_pt:
03411         p2.x = m_radius*cos(0.5*m_angle);
03412         p2.y = m_radius*sin(0.5*m_angle);
03413         break;
03414       case extension0_pt:
03415         {
03416           p2 = m_points[start_pt_index];
03417           double dp0 = DimpointOffset(0);
03418           if(dp0 >= 0)
03419           {
03420             ON_2dVector v2 = (ON_2dVector)p2;
03421             v2.Unitize();
03422             p2 = (ON_2dPoint)v2 * dp0;
03423           }
03424         }
03425         break;
03426       case extension1_pt:
03427         {
03428           p2 = m_points[end_pt_index];
03429           double dp1 = DimpointOffset(1);
03430           if(dp1 >= 0)
03431           {
03432             ON_2dVector v2 = (ON_2dVector)p2;
03433             v2.Unitize();
03434             p2 = (ON_2dPoint)v2 * dp1;
03435           }
03436         }
03437         break;
03438       default:
03439         p2.x = p2.y = ON_UNSET_VALUE;
03440         break;
03441       }
03442     }
03443   }
03444   return p2;
03445 }
03446 
03447 ON_3dPoint ON_AngularDimension2::Dim3dPoint( int point_index ) const
03448 {
03449   ON_2dPoint p2 = Dim2dPoint(point_index);
03450   return (ON_UNSET_VALUE == p2.x) ? ON_UNSET_POINT : m_plane.PointAt(p2.x,p2.y);
03451 }
03452 
03453 
03454 ON_BOOL32 ON_AngularDimension2::Write( ON_BinaryArchive& file ) const
03455 {
03456   // 18 October 2007 Dale Lear
03457   //    I added the chunk wrapping so V5 and future versions can
03458   //    add IO support for information specific to ON_LinearDimension2
03459   //    V4 did not have a ON_LinearDimension2::Write and simply called
03460   //    ON_Annotation2::Write.
03461   bool rc = false;
03462   bool bInChunk = (file.Archive3dmVersion() >= 5);
03463   if ( bInChunk )
03464   {
03465     rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
03466     if ( !rc )
03467       return false;
03468   }
03469   else
03470   {
03471     rc = true;
03472   }
03473 
03474   while(rc)
03475   {
03476     rc = ON_Annotation2::Write(file)?true:false;
03477     if (!rc) break;
03478     rc = file.WriteDouble( m_angle);
03479     if (!rc) break;
03480     rc = file.WriteDouble( m_radius);
03481     if (!rc) break;
03482     if ( !bInChunk )
03483       break;
03484 
03485     // to write new V5 fields, increment the minor
03486     // version number and write them below
03487 
03488     break;
03489   }
03490 
03491   if ( bInChunk )
03492   {
03493     if (!file.EndWrite3dmChunk())
03494       rc = false;
03495   }
03496 
03497   return rc;
03498 }
03499 
03500 ON_BOOL32 ON_AngularDimension2::Read( ON_BinaryArchive& file )
03501 {
03502   // 18 October 2007 Dale Lear
03503   //    I added the chunk wrapping so V5 and future versions can
03504   //    add IO support for information specific to ON_AngularDimension2
03505   int major_version = 0;
03506   int minor_version = 0;
03507   bool rc = false;
03508   bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180);
03509   if ( bInChunk )
03510   {
03511     rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
03512     if ( !rc )
03513       return false;
03514   }
03515   else
03516   {
03517     rc = true;
03518   }
03519 
03520   while(rc)
03521   {
03522     rc = ON_Annotation2::Read(file)?true:false;
03523     if (!rc) break;
03524     rc = file.ReadDouble( &m_angle);
03525     if (!rc) break;
03526     rc = file.ReadDouble( &m_radius);
03527     if (!rc) break;
03528     if ( !bInChunk || minor_version <= 0 )
03529       break;
03530 
03531     // Code to read any new ON_AngularDimension2 fields will 
03532     // go here.
03533 
03534     break;
03535   }
03536 
03537   if ( bInChunk )
03538   {
03539     // To read new ON_AngularDimension2 specific additions,
03540     // examine the minor version number and read the information
03541     // here.  Please ask Dale Lear for help.
03542 
03543     if ( !file.EndRead3dmChunk() )
03544       rc = false;
03545   }
03546   return rc;
03547 }
03548 
03549 static ON_BOOL32 VectorAngle( const ON_2dVector& v, double& angle)
03550 {
03551   if( v.IsTiny())
03552     return false;
03553 
03554   angle = atan2( v.y, v.x);
03555 
03556   if( angle < 0.0)
03557     angle += 2.0 * ON_PI;
03558 
03559   if( angle > 2.0 * ON_PI)
03560     angle -= 2.0 * ON_PI;
03561 
03562   return true;
03563 }
03564 
03565 bool ON_AngularDimension2::CreateFromV2( 
03566     const ON_Annotation& v2_ann,
03567     const ON_3dmAnnotationSettings& settings,
03568     int dimstyle_index
03569     )
03570 {
03571   if( ON::dtDimAngular != v2_ann.Type() )
03572     return false;
03573 
03574   if ( v2_ann.m_points.Count() < 3 )
03575     return false;
03576 
03577   ON_Plane plane = v2_ann.m_plane;
03578   plane.UpdateEquation();
03579   if ( !plane.IsValid() )
03580     return false;
03581 
03582   ON_2dVector p0 = v2_ann.m_points[0]; // point on first line
03583   ON_2dVector p1 = v2_ann.m_points[1]; // point on second line
03584   ON_2dPoint tp = v2_ann.m_points[2]; // user positioned text point
03585   if ( !p0.IsValid() || !p1.IsValid() || p0.IsZero() || p1.IsZero() )
03586     return false;
03587 
03588   bool bUserPositionedText = v2_ann.UserPositionedText() && tp.IsValid();
03589 
03590   if ( p0.x <= 0.0 && 0.0 != p0.y )
03591   {
03592     bUserPositionedText = false;
03593     ON_3dPoint P0 = plane.PointAt(p0.x,p0.y);
03594     ON_3dPoint P1 = plane.PointAt(p1.x,p1.y);
03595     plane.xaxis = P0-plane.origin;
03596     if ( !plane.xaxis.Unitize() )
03597       return false;
03598     plane.yaxis = ON_CrossProduct( plane.zaxis, plane.xaxis );
03599     plane.yaxis.Unitize();
03600     if ( !plane.IsValid() )
03601       return false;
03602     p0.x = p0.Length();
03603     p0.y = 0.0;
03604     if ( !plane.ClosestPointTo(P1,&p1.x,&p1.y) )
03605       return false;
03606   }
03607 
03608   if ( p1.x >= 0.0 && p1.y == 0.0 )
03609     return false;
03610 
03611   double angle = atan2(p1.y,p1.x);
03612   if ( angle < 0.0 )
03613     angle += 2.0*ON_PI;
03614 
03615   double radius = 0.5*(p0.Length() + p1.Length());
03616 
03617   const ON_AngularDimension* v2_angdim = ON_AngularDimension::Cast(&v2_ann);
03618   if ( v2_angdim && v2_angdim->Radius() > 0.0 )
03619     radius = v2_angdim->Radius();
03620 
03621   if ( !bUserPositionedText )
03622   {
03623     tp.x = radius*cos(0.5*angle);
03624     tp.y = radius*sin(0.5*angle);
03625   }
03626 
03627   ON_2dPoint arcpt( radius*cos(angle/3.0), radius*sin(angle/3.0) );
03628 
03629   m_plane = plane;
03630   m_points.SetCapacity(4);
03631   m_points.SetCount(4);
03632   m_points[0] = tp;
03633   m_points[1] = p0;
03634   m_points[2] = p1;
03635   m_points[3] = arcpt;
03636   m_angle = angle;
03637   m_radius = radius;
03638 
03639   SetTextValue(v2_ann.UserText());
03640   SetTextFormula(0);
03641   m_userpositionedtext = bUserPositionedText;
03642 
03643   switch( settings.m_textalign)
03644   {
03645   case 1:
03646     m_textdisplaymode = ON::dtInLine;
03647     break;
03648   case 2:
03649     m_textdisplaymode = ON::dtHorizontal;
03650     break;
03651   default:
03652     m_textdisplaymode = ON::dtAboveLine;
03653     break;
03654   }
03655 
03656   m_index = dimstyle_index;
03657 
03658   return true;
03659 }
03660 
03661 bool ON_AngularDimension2::CreateFromArc( const ON_Arc& arc )
03662 {
03663   // June 9, 2010 - Lowell - Changed to call CreateFromPoints()
03664   bool rc = arc.IsValid();
03665   if (rc)
03666   {
03667     ON_3dPoint C = arc.Center();
03668     ON_3dPoint S = arc.StartPoint();
03669     ON_3dPoint E = arc.EndPoint();
03670     ON_3dPoint M = arc.MidPoint();
03671     ON_3dVector N = arc.Plane().zaxis;
03672 
03673     rc = CreateFromPoints(C, S, E, M, N);
03674   }
03675 
03676   return rc;
03677 }
03678 
03679 bool ON_AngularDimension2::GetArc( ON_Arc& arc ) const
03680 {
03681   bool rc = false;
03682   // Jan 25, 2007 - changed min radius from >0 to >ON_SQRT_EPSILON
03683   // to avoid domain problems trying to use very small arcs later
03684   if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON
03685       && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI
03686       && m_plane.origin.IsValid() 
03687       && m_plane.xaxis.IsValid() 
03688       && m_plane.yaxis.IsValid() 
03689       && m_plane.zaxis.IsValid()
03690       && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON
03691       && 4 == m_points.Count()
03692       )
03693   {
03694     ON_3dVector X = m_plane.PointAt( m_points[start_pt_index].x, m_points[start_pt_index].y ) - m_plane.origin;
03695     if ( fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize() )
03696     {
03697       if ( fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON )
03698       {
03699         ON_3dVector Y = ON_CrossProduct( m_plane.zaxis, X );
03700         if ( fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize() )
03701         {
03702           arc.plane = m_plane;
03703           arc.plane.xaxis = X;
03704           arc.plane.yaxis = Y;
03705           arc.plane.UpdateEquation();
03706           arc.SetAngleIntervalRadians( ON_Interval(0.0,m_angle) );
03707           arc.radius = m_radius;
03708           rc = true;
03709         }
03710       }
03711     }
03712   }
03713 
03714   return rc;
03715 }
03716 
03717 bool ON_AngularDimension2::GetExtensionLines(ON_Line extensions[2]) const
03718 {
03719   bool rc = false;
03720 
03721   if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON
03722       && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI
03723       && m_plane.origin.IsValid() 
03724       && m_plane.xaxis.IsValid() 
03725       && m_plane.yaxis.IsValid() 
03726       && m_plane.zaxis.IsValid()
03727       && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON
03728       && 4 == m_points.Count()
03729       )
03730   {
03731     const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
03732     if(pDE != 0)
03733     {
03734       double exoffset0 = pDE->DimpointOffset(0);
03735       double exoffset1 = pDE->DimpointOffset(1);
03736       ON_3dPoint e00, e01, e10, e11;
03737       e00 = m_plane.PointAt(m_points[start_pt_index].x, m_points[start_pt_index].y);
03738       e10 = m_plane.PointAt(m_points[end_pt_index].x, m_points[end_pt_index].y);
03739       ON_3dVector X = e00 - m_plane.origin;
03740       ON_3dVector Y = e10 - m_plane.origin;
03741       if((fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize()) &&
03742          (fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize()))
03743       {
03744         if((fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON) &&
03745            (fabs(Y*m_plane.zaxis) <= ON_SQRT_EPSILON))
03746         {
03747           e00 = m_plane.origin + X * exoffset0;
03748           e10 = m_plane.origin + Y * exoffset1;
03749           e01 = m_plane.origin + X * m_radius;
03750           e11 = m_plane.origin + Y * m_radius;
03751 
03752           extensions[0].from = e00;
03753           extensions[0].to   = e01;
03754           extensions[1].from = e10;
03755           extensions[1].to   = e11;
03756           rc = true;
03757         }
03758       }
03759     }
03760   }
03761   return rc;
03762 }
03763 
03764 
03765 bool ON_AngularDimension2::CreateFromPoints( 
03766             const ON_3dPoint& pc, 
03767             const ON_3dPoint& p0in,
03768             const ON_3dPoint& p1in,
03769             ON_3dPoint& arcpt, 
03770             ON_3dVector& Normal)
03771 {
03772   ON_3dPoint p0, p1;
03773   p0 = p0in;
03774   p1 = p1in;
03775 
03776   ON_Plane plane( pc, Normal);
03777 
03778   ON_2dPoint pa, pp0, pp1;
03779 
03780   if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y))
03781     return false;
03782 
03783   // rotate so that p0 is on x-axis
03784   ON_2dVector v0( pp0);
03785   v0.Unitize();
03786   plane.Rotate( v0.y, v0.x, plane.Normal());
03787 
03788   if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y))
03789     return false;
03790 
03791   if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y))
03792     return false;
03793 
03794   if( !plane.ClosestPointTo( p1, &pp1.x, &pp1.y))
03795     return false;
03796 
03797   double a1, aa;
03798   if( !VectorAngle( ON_2dVector( pp1), a1) || !VectorAngle( ON_2dVector( pa), aa))    
03799     return false;
03800 
03801   if( aa > a1)  // the angle is really the bigger one ( > 180)
03802   {
03803     // rotate so that p0 is on x-axis
03804     v0.Set( pp1.x, pp1.y);
03805     v0.Unitize();
03806     plane.Rotate( v0.y, v0.x, plane.Normal());
03807 
03808     if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y))
03809       return false;
03810     if( !plane.ClosestPointTo( p1, &pp0.x, &pp0.y))
03811       return false;
03812     if( !plane.ClosestPointTo( p0, &pp1.x, &pp1.y))
03813       return false;
03814   }
03815 
03816   VectorAngle( ON_2dVector( pp1), a1);
03817 
03818   SetAngle( a1);
03819   SetRadius( ON_2dVector( pa).Length());
03820 
03821   ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
03822   if(pDE != 0)
03823   {
03824     double os = ((ON_2dVector)pp0).Length();
03825     pDE->SetDimpointOffset(0, os);
03826     os = ((ON_2dVector)pp1).Length();
03827     pDE->SetDimpointOffset(1, os);
03828   }
03829 
03830   ReservePoints( 4);
03831   SetPlane( plane);
03832   SetPoint( 1, pp0);
03833   SetPoint( 2, pp1);
03834   SetPoint( 3, pa);
03835 
03836 
03837   return true;
03838 }
03839 
03840 void ON_AngularDimension2::SetAngle( double angle)
03841 {
03842   m_angle = angle;
03843 }
03844 
03845 double ON_AngularDimension2::Angle() const
03846 {
03847   return m_angle;
03848 }
03849 
03850 void ON_AngularDimension2::SetRadius( double radius)
03851 {
03852   m_radius = radius;
03853 }
03854 
03855 double ON_AngularDimension2::Radius() const
03856 {
03857   return m_radius;
03858 }
03859 
03860 double ON_AngularDimension2::NumericValue() const
03861 {
03862   // This returns degrees - if we get another angular unit system, add it
03863   return (m_angle*180.0/ON_PI);
03864 }
03865 
03866 int ON_AngularDimension2::StyleIndex() const
03867 {
03868   return ON_Annotation2::Index();
03869 }
03870 
03871 void ON_AngularDimension2::SetStyleIndex( int i)
03872 {
03873   ON_Annotation2::SetIndex( i);
03874 }
03875 
03876 const wchar_t* ON_AngularDimension2::DefaultText()
03877 {
03878   return L"<>"; // Aug 31, 2009 - Lowell
03879 }
03880 
03881 void ON_AngularDimension2::ConvertBack( 
03882         ON_AngularDimension2& // target - formal parameter intentionally ignored in this virtual function
03883         )
03884 {
03885 }
03886 
03887 // 6-23-03 lw Added v2 file writing of annotation
03888 void ON_AngularDimension2::GetV2Form( ON_AngularDimension& dim)
03889 {
03890   ON_Annotation2::ConvertBack( dim);
03891   dim.SetPoint(0, Point(1));
03892   dim.SetPoint(1, Point(2));
03893   dim.SetPoint(2, Point(3));
03894   dim.SetPoint(3, Point(0)); // text point or apex
03895   dim.SetAngle( Angle());
03896   dim.SetRadius( Radius());
03897 }
03898 
03899 
03900 
03901 static void OrientRectHelper( ON_2dVector corners[4] )
03902 {
03903   double twice_area = 0.0;
03904   ON_2dVector p0, p1;
03905   int i;
03906   p1 = corners[3];
03907   for ( i = 0; i < 4; i++ ) 
03908   {
03909     p0 = p1;
03910     p1 = corners[i];
03911     twice_area += (p0.x-p1.x)*(p0.y+p1.y);
03912   }
03913   if ( twice_area < 0.0 )
03914   {
03915     p1 = corners[1];
03916     corners[1] = corners[3];
03917     corners[3] = p1;
03918   }
03919 }
03920 
03921 int ON_AngularDimension2::GetDimensionArcSegments(
03922     ON_RECT gdi_text_rect,
03923     int gdi_height_of_I,
03924     ON_Xform gdi_to_world,
03925     const ON_DimStyle& dimstyle,
03926     double dimscale,
03927     const ON_Viewport* vp,
03928     double a[6],
03929     bool& bInside
03930     ) const
03931 {
03932   int rc = 0;
03933 
03934   a[0] = 0.0;
03935   a[1] = 0.0;
03936 
03937   if ( m_angle <= 0.0 || !ON_IsValid(m_angle) )
03938     return 0;
03939 
03940   a[1] = m_angle;
03941 
03942   if ( m_radius <= 0.0 || !ON_IsValid(m_radius) || m_angle >= 2.0*ON_PI )
03943     return 0;
03944 
03945   if ( 0 == gdi_height_of_I )
03946   {
03947     // Default to height of Ariel 'I' (this code still works if ON_Font::normal_font_height != 256)
03948     gdi_height_of_I = (165*ON_Font::normal_font_height)/256;
03949   }
03950 
03951   if ( 0.0 == dimscale )
03952   {
03953     dimscale = 1.0;
03954   }
03955 
03956   double t;
03957 
03958   ON::eTextDisplayMode textdisplay = ON::TextDisplayMode(dimstyle.TextAlignment());
03959   if ( ON::dtHorizontal == textdisplay && !vp )
03960     textdisplay = ON::dtInLine;
03961 
03962   const double text_height_of_I    = dimscale*dimstyle.TextHeight();
03963   const double textgap             = dimscale*dimstyle.TextGap();
03964   const double gdi_to_plane_scale  = text_height_of_I/gdi_height_of_I;
03965   const double textwidth           = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left));
03966   const double arrowwidth          = fabs(dimscale*dimstyle.ArrowSize());
03967   const double tailwidth           = 0.5*arrowwidth;
03968   const double dimextension        = fabs(dimscale*dimstyle.DimExtension());
03969   const double a0 = 0.0;
03970   const double a1 = m_angle;
03971 
03972   double sin_angle = 0.5*dimextension/m_radius;
03973   if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
03974   const double  dimextension_angle = 2.0*asin(sin_angle);
03975 
03976   sin_angle = 0.5*arrowwidth/m_radius;
03977   if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
03978   const double arrowangle = 2.0*asin(sin_angle);
03979 
03980   sin_angle = 0.5*tailwidth/m_radius;
03981   if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
03982   const double tailangle = 2.0*asin(sin_angle);
03983 
03984   // June 7, 2010 - Lowell - Added some special handling for very small dimensions 
03985   double arrow_angle = arrowangle + tailangle;
03986   if(arrow_angle > ON_PI * 0.5)
03987     arrow_angle = ON_PI * 0.5;
03988   if ( m_radius <= arrowwidth + tailwidth || m_radius*(a1-a0) < 2.0*(arrowwidth) + tailwidth )
03989   {
03990     // arc is tiny with respect to arrowhead size - arrowheads have to be "outside"
03991     // 2 arc segments, arrowheads outside
03992     a[0] = a0;
03993     a[1] = a0 - arrow_angle;
03994     a[2] = a1 + arrow_angle;
03995     a[3] = a1;
03996     a[4] = a0;
03997     a[5] = a1;
03998     if( dimextension_angle != 0.0)
03999     {
04000       a[0] += dimextension_angle;
04001       a[3] -= dimextension_angle;
04002     }
04003     bInside = false;
04004     return 2;
04005   }
04006   
04007   if ( ON::dtInLine == textdisplay && m_radius <= 0.5*(textwidth+2.0*textgap) )
04008   {
04009     // 2 arc segments, arrowheads outside
04010     a[0] = a0;
04011     a[1] = a0 - arrow_angle;
04012     a[2] = a1 + arrow_angle;
04013     a[3] = a1;
04014     a[4] = a0;
04015     a[5] = a1;
04016     if( dimextension_angle != 0.0)
04017     {
04018       a[0] += dimextension_angle;
04019       a[3] -= dimextension_angle;
04020     }
04021     bInside = false;
04022     return 2;
04023   }
04024 
04025   sin_angle = 0.5*(textwidth+2.0*textgap)/m_radius;
04026   if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
04027   const double textangle = (ON::dtInLine == textdisplay && !m_userpositionedtext )
04028     ? 2.0*asin(sin_angle)
04029     : 0.0;
04030 
04031   if ( (a1-a0) <= 2.0*(arrow_angle) + textangle )
04032   {
04033     // 2 arc segments, arrowheads outside
04034     a[0] = a0;
04035     a[1] = a0 - arrow_angle;
04036     a[2] = a1 + arrow_angle;
04037     a[3] = a1;
04038     a[4] = a0;
04039     a[5] = a1;
04040     if( dimextension_angle != 0.0)
04041     {
04042       a[0] += dimextension_angle;
04043       a[3] -= dimextension_angle;
04044     }
04045     bInside = false;
04046     return 2;
04047   }
04048   
04049   if ( (ON::dtHorizontal == textdisplay && vp) || m_userpositionedtext )
04050   {
04051     // use projected rectangle to clip dimension arc
04052     double aa0, aa1, aa, r0, r1, t, tt[2];
04053     ON_3dPoint P, R, c[2];
04054     ON_2dVector corners[4], N;
04055     ON_Line ray;
04056     int i,xi, xrc;
04057     const ON_Circle circle(ON_xy_plane,1.0);
04058 
04059     ON_3dVector vp_zaxis = (ON::dtHorizontal == textdisplay && vp)
04060                          ? vp->CameraZ() 
04061                          : m_plane.zaxis;
04062 
04063     // 30 July 2012 - Lowell - Slightly shrink the text gap value
04064     // so that text + gap won't intersect the dim line if when text
04065     // is moved along the dim line. rr110504
04066     double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12;
04067     if(gdi_gap < 0.0) gdi_gap = 0.0;
04068 
04069     R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
04070     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
04071     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
04072     m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y);
04073 
04074     R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
04075     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
04076     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
04077     m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y);
04078 
04079     R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
04080     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
04081     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
04082     m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y);
04083 
04084     R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
04085     ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
04086     P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
04087     m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y);
04088 
04089     // orient projected rect so it is counter-clockwise
04090     OrientRectHelper(corners);
04091 
04092     aa0 = a0;
04093     aa1 = a1;
04094     P.Set(cos(a0)*m_radius,sin(a0)*m_radius,0.0);
04095     R.Set(cos(a1)*m_radius,sin(a1)*m_radius,0.0);
04096     for ( i = 0; i < 4; i++ )
04097     {
04098       N.x = (corners[i].y-corners[(i+1)%4].y);
04099       N.y = (corners[(i+1)%4].x-corners[i].x);
04100       if ( !N.Unitize() )
04101         continue;
04102       if ( (P.x - corners[i].x)*N.x + (P.y - corners[i].y)*N.y  < 0.0 )
04103       {
04104         // end point not in text box
04105         aa0 = ON_UNSET_VALUE;
04106       }
04107       if ( (R.x - corners[i].x)*N.x + (R.y - corners[i].y)*N.y  < 0.0 )
04108       {
04109         // start point not in text box
04110         aa1 = ON_UNSET_VALUE;
04111       }
04112     }
04113 
04114     if ( ON_UNSET_VALUE == aa0 )
04115       aa0 = aa1;
04116     else if ( ON_UNSET_VALUE == aa1 )
04117       aa1 = aa0;
04118 
04119     r1 = corners[3].Length() - m_radius;
04120     ray.to.x = corners[3].x/m_radius; ray.to.y = corners[3].y/m_radius; 
04121     ray.to.z = ray.from.z = 0.0;
04122     for ( i = 0; i < 4; i++ )
04123     {
04124       r0 = r1;
04125       r1 = corners[i].Length() - m_radius;
04126       ray.from = ray.to;
04127       ray.to.x = corners[i].x/m_radius; ray.to.y = corners[i].y/m_radius;
04128       if ( r0 < 0.0 && r1 < 0.0 )
04129       {
04130         continue; // ray is inside circle
04131       }
04132       if ( r0 > 0.0 && r1 > 0.0 )
04133       {
04134         if ( !ray.ClosestPointTo(ON_2dPoint(0.0,0.0),&t ) )
04135           continue;
04136         R = ray.PointAt(t);
04137         if ( R.x*R.x + R.y*R.y >= 1.0 )
04138           continue; // ray is outside circle
04139       }
04140 
04141 
04142       xrc = 0;
04143       if ( 0.0 == r0 )
04144         tt[xrc++] = 0.0;
04145 
04146       if ( 0.0 == r1 )
04147         tt[xrc++] = 1.0;
04148 
04149       if ( 0 == xrc )
04150         xrc = ON_Intersect(ray,circle,&tt[0],c[0],&tt[1],c[1]);
04151 
04152       if ( xrc < 1 || xrc > 2 )
04153         continue; // ray does not intersect circle;
04154 
04155       for ( xi = 0; xi < xrc; xi++ )
04156       {
04157         if ( tt[xi] < 0.0 || tt[xi] > 1.0 )
04158           continue; // intersection point not on segment
04159 
04160         P = ray.PointAt(tt[xi]);
04161         if ( 0.0 == P.x && 0.0 == P.y )
04162           continue; // bogus
04163 
04164         aa = atan2(P.y,P.x);
04165         if ( aa < a0 ) aa += 2.0*ON_PI; else if ( aa > a1 ) aa -= 2.0*ON_PI;
04166         if ( aa < a0 || aa > a1 )
04167         {
04168           continue;
04169         }
04170         if ( ON_UNSET_VALUE == aa0 )
04171           aa0 = aa1 = aa;
04172         else if ( aa < aa0 )
04173           aa0 = aa;
04174         else if ( aa > aa1 )
04175           aa1 = aa;
04176       }
04177     }
04178     if ( ON_UNSET_VALUE != aa0 && ON_UNSET_VALUE != aa1 
04179         && a0 <= aa0 && aa0 < aa1 && aa1 <= a1 )
04180     {
04181       t = arrow_angle;
04182       if ( aa0 < a0+t && aa1 > a1-t )
04183       {
04184         // text box hits both arrowheads
04185         // 2 arc segments, arrowheads outside
04186         a[0] = a0;
04187         a[1] = a0 - arrow_angle;
04188         a[2] = a1 + arrow_angle;
04189         a[3] = a1;
04190         a[4] = a0;
04191         a[5] = a1;
04192         if( dimextension_angle != 0.0)
04193         {
04194           a[0] += dimextension_angle;
04195           a[3] -= dimextension_angle;
04196         }
04197         bInside = false;
04198         rc = 2;
04199       }
04200       else
04201       {
04202         if ( aa0 < a0+t ) aa0 = a0+t;
04203         if ( aa1 > a1-t ) aa1 = a1-t;
04204         if ( a0 < aa0 && aa0 < aa1 && aa1 < a1 )
04205         {
04206           // clip arc to text rectangle
04207           // 2 arc segments, arrowheads inside
04208           a[0] = a0;
04209           a[1] = aa0;
04210           a[2] = aa1;
04211           a[3] = a1;
04212           a[4] = a0;
04213           a[5] = a1;
04214           if( dimextension_angle != 0.0)
04215           {
04216             a[0] -= dimextension_angle;
04217             a[3] += dimextension_angle;
04218           }
04219           bInside = true;
04220           rc = 2;
04221         }
04222         else
04223         {
04224           // nasty case - don't bother clipping
04225           // 1 segment, arrows inside
04226           a[0] = a0;
04227           a[1] = a1;
04228           a[2] = a0;
04229           a[3] = a1;
04230           a[4] = a0;
04231           a[5] = a1;
04232           if( dimextension_angle != 0.0)
04233           {
04234             a[0] -= dimextension_angle;
04235             a[1] += dimextension_angle;
04236           }
04237           bInside = true;
04238           rc = 1;
04239         }
04240       }
04241     }
04242     else
04243     {
04244       // 1 segment, arrows inside
04245       a[0] = a0;
04246       a[1] = a1;
04247       a[2] = a0;
04248       a[3] = a1;
04249       a[4] = a0;
04250       a[5] = a1;
04251       if( dimextension_angle != 0.0)
04252       {
04253         a[0] -= dimextension_angle;
04254         a[1] += dimextension_angle;
04255       }
04256       bInside = true;
04257       rc = 1;
04258     }
04259   }
04260   else if ( ON::dtAboveLine == textdisplay || m_userpositionedtext )
04261   {
04262     // 1 segment, arrows inside
04263     a[0] = a0;
04264     a[1] = a1;
04265     a[2] = a0;
04266     a[3] = a1;
04267     a[4] = a0;
04268     a[5] = a1;
04269     if( dimextension_angle != 0.0)
04270     {
04271       a[0] -= dimextension_angle;
04272       a[1] += dimextension_angle;
04273     }
04274     bInside = true;
04275     rc = 1;
04276   }
04277   else if ( ON::dtInLine == textdisplay )
04278   {
04279     // 2 segments, arrows inside
04280     t = 0.5*((a1-a0) - textangle);
04281     a[0] = a0;
04282     a[1] = a0+t;
04283     a[2] = a1-t;
04284     a[3] = a1;
04285     a[4] = a0;
04286     a[5] = a1;
04287     if( dimextension_angle != 0.0)
04288     {
04289       a[0] -= dimextension_angle;
04290       a[3] += dimextension_angle;
04291     }
04292     bInside = true;
04293     rc = 2;
04294   }
04295 
04296   return rc;
04297 }
04298 
04299 double ON_AngularDimension2::DimpointOffset(int index) const
04300 {
04301   const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
04302   if(pDE != 0)
04303     return pDE->DimpointOffset(index);
04304   return -1.0;
04305 }
04306 void ON_AngularDimension2::SetDimpointOffset(int index, double offset)
04307 {
04308   ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
04309   if(pDE != 0)
04310     pDE->SetDimpointOffset(index, offset);
04311 }
04312 
04313 
04314 //----- ON_OrdinateDimension2 -----------------------------------------
04315 ON_OrdinateDimension2::ON_OrdinateDimension2()
04316 {
04317   m_type = ON::dtDimOrdinate;
04318   SetTextValue(DefaultText());
04319   SetTextFormula(0);
04320   m_direction = -1;  // undetermined direction
04321   m_points.Reserve(ON_OrdinateDimension2::dim_pt_count);
04322   m_points.SetCount(ON_OrdinateDimension2::dim_pt_count);
04323   m_points.Zero();
04324   m_kink_offset_0 = ON_UNSET_VALUE;
04325   m_kink_offset_1 = ON_UNSET_VALUE;
04326 }
04327 
04328 ON_OrdinateDimension2::~ON_OrdinateDimension2()
04329 {
04330 }
04331 
04332 ON_BOOL32 ON_OrdinateDimension2::Transform( const ON_Xform& xform )
04333 {
04334   bool rc = xform.IsIdentity();
04335   if ( !rc)
04336   {
04337     return ON_Annotation2::Transform(xform);
04338   }
04339   return rc;
04340 }
04341 
04342 ON_2dPoint ON_OrdinateDimension2::Dim2dPoint( int point_index, double default_offset) const
04343 {
04344   ON_2dPoint p2( ON_UNSET_VALUE, ON_UNSET_VALUE);
04345   int dir = m_direction;
04346   if( dir == -1 && ( point_index == offset_pt_0 || point_index == offset_pt_1))
04347   {
04348     if( fabs( m_points[definition_pt_index].y - m_points[leader_end_pt_index].y) > 
04349         fabs( m_points[definition_pt_index].x - m_points[leader_end_pt_index].x))
04350       dir = 0;
04351     else
04352       dir = 1;
04353   }
04354 
04355   if( point_index >= 0 && point_index < dim_pt_count && m_points.Count() == dim_pt_count)
04356   {
04357     p2 = m_points[point_index];
04358   }
04359   else if( point_index == text_pivot_pt)
04360   {
04361     // ON_UNSET_POINT
04362   }
04363   else if( point_index == offset_pt_0)
04364   {
04365     double offset;
04366     if( m_kink_offset_0 == ON_UNSET_VALUE)
04367       offset = default_offset;
04368     else
04369       offset = m_kink_offset_0;
04370 
04371     if( dir == x)
04372     {
04373       p2 = m_points[leader_end_pt_index];
04374       if( p2.y > m_points[definition_pt_index].y)
04375         p2.y -= offset;
04376       else
04377         p2.y += offset;
04378     }
04379     else if( dir == y)
04380     {
04381       p2 = m_points[leader_end_pt_index];
04382       if( p2.x > m_points[definition_pt_index].x)
04383         p2.x -= offset;
04384       else
04385         p2.x += offset;
04386     }
04387   }
04388   else if( point_index == offset_pt_1)
04389   {
04390     double offset0;
04391     if( m_kink_offset_0 == ON_UNSET_VALUE)
04392       offset0 = default_offset;
04393     else
04394       offset0 = m_kink_offset_0;
04395 
04396     double offset1;
04397     if( m_kink_offset_1 == ON_UNSET_VALUE)
04398       offset1 = default_offset;
04399     else
04400       offset1 = m_kink_offset_1;
04401 
04402     if( dir == x)
04403     {
04404       p2.x = m_points[definition_pt_index].x;
04405       if( m_points[leader_end_pt_index].y > m_points[definition_pt_index].y)
04406         p2.y = m_points[leader_end_pt_index].y - offset0 - offset1;
04407       else
04408         p2.y = m_points[leader_end_pt_index].y + offset0 + offset1;
04409     }
04410     else if( dir == y)
04411     {
04412       p2.y = m_points[definition_pt_index].y;
04413       if( m_points[leader_end_pt_index].x > m_points[definition_pt_index].x)
04414         p2.x = m_points[leader_end_pt_index].x - offset0 - offset1;
04415       else
04416         p2.x = m_points[leader_end_pt_index].x + offset0 + offset1;
04417     }
04418   }
04419   return p2;
04420 }
04421 
04422 ON_3dPoint ON_OrdinateDimension2::Dim3dPoint( int point_index, double default_offset) const
04423 {
04424   ON_2dPoint p2 = Dim2dPoint(point_index, default_offset);
04425   return (ON_UNSET_VALUE == p2.x) ? ON_UNSET_POINT : m_plane.PointAt(p2.x,p2.y);
04426 }
04427 
04428 ON_BOOL32 ON_OrdinateDimension2::IsValid( ON_TextLog* text_log) const
04429 {
04430   if ( m_type != ON::dtDimOrdinate)
04431   {
04432     if ( text_log )
04433     {
04434       text_log->Print("ON_OrdinateDimension2 - m_type !=  ON::dtDimOrdinate.\n");
04435     }
04436     return false;
04437   }
04438 
04439   if ( !ON_Annotation2::IsValid( text_log ))
04440   {
04441     if ( text_log )
04442     {
04443       text_log->Print("ON_OrdinateDimension2 - invalid ON_Annotation2 base class.\n");
04444     }
04445     return false;
04446   }
04447 
04448   if ( m_points.Count() != 2 )
04449   {
04450     if ( text_log )
04451     {
04452       text_log->Print("ON_OrdinateDimension2 - m_points.Count() = %d (should be 2).\n",m_points.Count());
04453     }
04454     return false;
04455   }
04456 
04457   return true;
04458 
04459 }
04460 
04461 ON_BOOL32 ON_OrdinateDimension2::Write( ON_BinaryArchive& file ) const
04462 {
04463   // put the entire ON_OrdinateDimension2 in a chunk so we can
04464   // add fields without breaking the file IO for old product.
04465   bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
04466   if (rc)
04467   {
04468     rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
04469     if (rc)
04470     {
04471       // As of 18 October 2007, the following comment is out of date.
04472       // But, we still need to write this "extra" chunk so we don't
04473       // break V4 file writing.
04474       //
04475       //   The output of ON_Annotation2::Write must be wrapped
04476       //   in an additional chunk because it does not put 
04477       //   itself in a chunk.  If you don't put it in a chunk,
04478       //   the versioning in the ON_Annotation2::Write is useless and
04479       //   will cause serious IO bugs if fields are ever added.
04480       rc = ON_Annotation2::Write( file) ? true : false;
04481       if (!file.EndWrite3dmChunk() )
04482         rc = false;
04483     }
04484     if (rc)
04485       rc = file.WriteInt( m_direction);
04486     // kink offsets, ver 1.1 added 2-4-06
04487     if (rc)
04488       rc = file.WriteDouble( m_kink_offset_0);
04489     if (rc)
04490       rc = file.WriteDouble( m_kink_offset_1);
04491 
04492     // end of ON_OrdinateDimension2 chunk
04493     if (!file.EndWrite3dmChunk() )
04494       rc = false;
04495   }
04496 
04497   return rc;
04498 }
04499 
04500 ON_BOOL32 ON_OrdinateDimension2::Read( ON_BinaryArchive& file )
04501 {
04502   int major_version=0, minor_version=0;
04503   bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
04504   if (rc)
04505   {
04506     if ( 1 != major_version )
04507     {
04508       rc = false;
04509     }
04510     else
04511     {
04512       int submajor_version=0, subminor_version=0;
04513 
04514       // subchunk wraps ON_Annotation2 field so this
04515       // function won't break if ON_Annotation2 changes.
04516       rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&submajor_version,&subminor_version);
04517       if (rc)
04518       {
04519         if ( 1 != submajor_version )
04520           rc = false;
04521         else
04522         {
04523           rc = ON_Annotation2::Read( file) ? true : false;
04524         }
04525         if ( !file.EndRead3dmChunk() )
04526           rc = false;
04527       }
04528 
04529       if( rc) 
04530         rc = file.ReadInt( &m_direction);
04531 
04532       if( minor_version > 0)
04533       {
04534         if( rc) 
04535           rc = file.ReadDouble( &m_kink_offset_0);
04536         if( rc) 
04537           rc = file.ReadDouble( &m_kink_offset_1);
04538       }
04539     }
04540 
04541     if (!file.EndRead3dmChunk())
04542       rc = false;
04543   }
04544   return rc;
04545 }
04546 
04547 double ON_OrdinateDimension2::NumericValue() const
04548 {
04549   if( m_direction == 0)
04550     return m_points[1].x - m_points[0].x;
04551   else
04552     return m_points[1].y - m_points[0].y;
04553 }
04554 
04555 int ON_OrdinateDimension2::StyleIndex() const
04556 {
04557   return ON_Annotation2::Index();
04558 }
04559 
04560 void ON_OrdinateDimension2::SetStyleIndex( int i)
04561 {
04562   ON_Annotation2::SetIndex( i);
04563 }
04564 
04565 ON_BOOL32 ON_OrdinateDimension2::GetBBox( double* boxmin,
04566                                      double* boxmax,
04567                                      ON_BOOL32 bGrowBox) const
04568 {
04569   ON_BoundingBox bbox;
04570   if ( bGrowBox )
04571   {
04572     bbox.m_min.x = boxmin[0]; 
04573     bbox.m_min.y = boxmin[1]; 
04574     bbox.m_min.z = boxmin[2];
04575     bbox.m_max.x = boxmax[0]; 
04576     bbox.m_max.y = boxmax[1]; 
04577     bbox.m_max.z = boxmax[2];
04578     if ( !bbox.IsValid() )
04579     {
04580       bbox.Destroy();
04581       bGrowBox = false;
04582     }
04583   }
04584 
04585   if( m_points.Count() == 2)
04586   {
04587     ON_3dPointArray P( 2);
04588 
04589     P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y));
04590     P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y));
04591     bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
04592   }
04593 
04594   if ( bGrowBox )
04595   {
04596     boxmin[0] = bbox.m_min.x; 
04597     boxmin[1] = bbox.m_min.y; 
04598     boxmin[2] = bbox.m_min.z; 
04599     boxmax[0] = bbox.m_max.x; 
04600     boxmax[1] = bbox.m_max.y; 
04601     boxmax[2] = bbox.m_max.z; 
04602   }
04603 
04604   return bGrowBox;
04605 }
04606 
04607 bool ON_OrdinateDimension2::GetTightBoundingBox( ON_BoundingBox& tight_bbox,
04608                                                  int bGrowBox,
04609                                                  const ON_Xform* xform) const
04610 {
04611   if( m_points.Count() == 2)
04612   {
04613     ON_3dPointArray P(2);
04614 
04615     P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y));
04616     P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y));
04617 
04618     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform))
04619       bGrowBox = true;
04620   }
04621   else if( bGrowBox && !tight_bbox.IsValid())
04622   {
04623     tight_bbox.Destroy();
04624     bGrowBox = false;
04625   }
04626 
04627   return( 0 != bGrowBox);
04628 }
04629 
04630 int ON_OrdinateDimension2::ImpliedDirection() const
04631 {
04632   int direction = -1;
04633   const ON_2dPoint& p0 = m_points[definition_pt_index];
04634   const ON_2dPoint& p1 = m_points[leader_end_pt_index];
04635   if( fabs( p1.x - p0.x) <= fabs( p1.y - p0.y))
04636     direction = 0; // measures along x axis
04637   else
04638     direction = 1; // measures along y axis
04639   
04640   return direction;
04641 } 
04642 
04643 int ON_OrdinateDimension2::Direction() const
04644 {
04645   return m_direction;
04646 } 
04647 
04648 void ON_OrdinateDimension2::SetDirection( int direction)
04649 {
04650   m_direction = direction;
04651 }
04652 
04653 const wchar_t* ON_OrdinateDimension2::DefaultText() { return L"<>"; }
04654 
04655 double ON_OrdinateDimension2::KinkOffset( int index) const
04656 {
04657   if( index == 0)
04658     return m_kink_offset_0;
04659   else if( index == 1)
04660     return m_kink_offset_1;
04661   else
04662     return ON_UNSET_VALUE;
04663 }
04664 
04665 void ON_OrdinateDimension2::SetKinkOffset( int index, double offset)
04666 {
04667   if( index == 0)
04668     m_kink_offset_0 = offset;
04669   else  if( index == 1)
04670     m_kink_offset_1 = offset;
04671 }
04672 
04673 void ON_OrdinateDimension2::CalcKinkPoints( ON_2dPoint p0, ON_2dPoint p1, 
04674                                             int direction, double default_offset,
04675                                             ON_2dPoint& k0, ON_2dPoint& k1) const
04676 {
04677   double offset0 = KinkOffset( 0);
04678   double offset1 = KinkOffset( 1);
04679 
04680   // if these haven't been set by dragging the offset points
04681   // use 2*textheight
04682   if( offset0 == ON_UNSET_VALUE)
04683     offset0 = default_offset;
04684   if( offset1 == ON_UNSET_VALUE)
04685     offset1 = default_offset;
04686 
04687   if( p0[1-direction] > p1[1-direction])
04688   {
04689     offset0 = -offset0;
04690     offset1 = -offset1;
04691   }
04692 
04693   //double d = fabs( p0[1-direction] - p1[1-direction]);
04694 
04695   if( direction == 0)
04696   {
04697     //if( d - fabs( offset0) > default_offset)
04698     //{
04699       k1.x = p0.x;
04700       k1.y = p1.y - offset0 - offset1;
04701     //}
04702     //else
04703     //{
04704     //  k1.x = p0.x;
04705     //  k1.y = p1.y + offset0 - offset1;
04706     //}
04707 
04708     k0.x = p1.x;
04709     k0.y = p1.y - offset0;
04710   }
04711   else
04712   {
04713     //if( d - fabs( offset0) > default_offset)
04714     //{
04715       k1.x = p1.x - offset0 - offset1;
04716       k1.y = p0.y;
04717     //}
04718     //else
04719     //{
04720     //  k1.x = p1.x + offset0 - offset1;
04721     //  k1.y = p0.y;
04722     //}
04723 
04724     k0.x = p1.x - offset0;
04725     k0.y = p1.y;
04726   }
04727 }
04728 
04729 
04730 
04731 //----- ON_TextEntity2 -----------------------------------------------
04732 ON_TextEntity2::ON_TextEntity2()
04733 {
04734   m_type = ON::dtTextBlock;
04735   m_textdisplaymode = ON::dtNormal;
04736 }
04737 
04738 ON_TextEntity2::~ON_TextEntity2()
04739 {
04740 }
04741 
04742 ON_BOOL32 ON_TextEntity2::IsValid( ON_TextLog* text_log ) const
04743 {
04744   if ( m_type != ON::dtTextBlock )
04745   {
04746     if ( text_log )
04747     {
04748       text_log->Print("ON_TextEntity2 - m_type !=  ON::dtTextBlock\n");
04749     }
04750     return false;
04751   }
04752 
04753   // 05 March 2009 S. Baer
04754   // Text blocks with no "printable" characters are considered invalid. Any
04755   // character with a value greater than 32 (the value of space) is "printable."
04756   int count = m_usertext.Length();
04757   bool bValidText = false;
04758   for( int i=0; i<count; i++ )
04759   {
04760     wchar_t c = m_usertext[i];
04761     // all characters <= space are nonprintable
04762     if( c > L' ' )
04763     {
04764       bValidText = true;
04765       break;
04766     }
04767   }
04768   // 9 Oct 2010 S. Baer
04769   // With the addition of text formulas, the user text can be 0 length
04770   if( !bValidText && count<1 )
04771   {
04772     const wchar_t* formula = TextFormula();
04773     if( formula && formula[0] )
04774       bValidText = true;
04775   }
04776 
04777   if( !bValidText )
04778   {
04779     if( text_log )
04780     {
04781       text_log->Print("ON_TextEntity2 - m_usertext does not contain printable characters.\n");
04782     }
04783     return false;
04784   }
04785 
04786 
04787   if ( !ON_Annotation2::IsValid( text_log ))
04788   {
04789     if ( text_log )
04790     {
04791       text_log->Print("ON_TextEntity2 - invalid ON_Annotation2 base class.\n");
04792     }
04793     return false;
04794   }
04795 
04796   if ( 0 != m_points.Count() )
04797   {
04798     if ( text_log )
04799     {
04800       text_log->Print("ON_TextEntity2 - m_points.Count() = %d (should be 0)\n", m_points.Count() );
04801     }
04802     return false;
04803   }
04804 
04805   return true;
04806 }
04807 
04808 
04809 ON_BOOL32 ON_TextEntity2::Write(ON_BinaryArchive& archive) const
04810 {
04811   // 18 October 2007 Dale Lear
04812   //    I added the chunk wrapping so V5 and future versions can
04813   //    add IO support for information specific to ON_TextEntity2
04814   //    V4 did not have a ON_TextEntity2::Write and simply called
04815   //    ON_Annotation2::Write.
04816   bool rc = false;
04817   bool bInChunk = (archive.Archive3dmVersion() >= 5);
04818   if ( bInChunk )
04819   {
04820     rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
04821     if ( !rc )
04822       return false;
04823   }
04824   else
04825   {
04826     rc = true;
04827   }
04828 
04829   while(rc)
04830   {
04831     rc = ON_Annotation2::Write(archive)?true:false;
04832     if (!rc) break;
04833     if ( !bInChunk )
04834       break;
04835 
04836     // To write new fields, increment minor version number
04837     // and write values here.  Ask Dale Lear for help.
04838 
04839     break;
04840   }
04841 
04842   if ( bInChunk )
04843   {
04844     if (!archive.EndWrite3dmChunk())
04845       rc = false;
04846   }
04847   return rc;
04848 }
04849 
04850 ON_BOOL32 ON_TextEntity2::Read(ON_BinaryArchive& archive)
04851 {
04852   // 18 October 2007 Dale Lear
04853   //    I added the chunk wrapping so V5 and future versions can
04854   //    add IO support for information specific to ON_TextEntity2
04855   int major_version = 0;
04856   int minor_version = 0;
04857   bool rc = false;
04858   bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
04859   if ( bInChunk )
04860   {
04861     rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
04862     if ( !rc )
04863       return false;
04864   }
04865   else
04866   {
04867     rc = true;
04868   }
04869 
04870   while(rc)
04871   {
04872     rc = ON_Annotation2::Read(archive)?true:false;
04873     if (!rc) break;
04874     if ( !bInChunk || minor_version <= 0 )
04875       break;
04876 
04877     // read future addition here
04878 
04879     break;
04880   }
04881 
04882   if ( bInChunk )
04883   {
04884     // To read new ON_TextEntity2 specific additions,
04885     // examine the minor version number and read the information
04886     // here.  Please ask Dale Lear for help.
04887 
04888     if ( !archive.EndRead3dmChunk() )
04889       rc = false;
04890   }
04891   return rc;
04892 }
04893 
04894 
04895 ON_BOOL32 ON_TextEntity2::GetBBox(
04896         double* boxmax,
04897         double* boxmin,
04898         ON_BOOL32 bGrowBox
04899         ) const
04900 {
04901   ON_BoundingBox bbox;
04902   if ( bGrowBox )
04903   {
04904     bbox.m_min.x = boxmin[0]; 
04905     bbox.m_min.y = boxmin[1]; 
04906     bbox.m_min.z = boxmin[2];
04907     bbox.m_max.x = boxmax[0]; 
04908     bbox.m_max.y = boxmax[1]; 
04909     bbox.m_max.z = boxmax[2];
04910     if ( !bbox.IsValid() )
04911     {
04912       bbox.Destroy();
04913       bGrowBox = false;
04914     }
04915   }
04916 
04917   if ( 1 == m_points.Count() )
04918   {
04919     ON_2dPoint uv = m_points[0];
04920     bbox.Set( m_plane.PointAt(uv.x,uv.y), bGrowBox );
04921     bGrowBox = true;
04922   }
04923   else if ( 0 == m_points.Count() )
04924   {
04925     bbox.Set( m_plane.origin, bGrowBox );
04926     bGrowBox = true;
04927   }
04928 
04929   if ( bGrowBox )
04930   {
04931     boxmin[0] = bbox.m_min.x; 
04932     boxmin[1] = bbox.m_min.y; 
04933     boxmin[2] = bbox.m_min.z; 
04934     boxmax[0] = bbox.m_max.x; 
04935     boxmax[1] = bbox.m_max.y; 
04936     boxmax[2] = bbox.m_max.z; 
04937   }
04938 
04939   return bGrowBox;
04940 }
04941 
04942 
04943 
04944 
04945 
04946 bool ON_TextEntity2::GetTightBoundingBox( 
04947                 ON_BoundingBox& tight_bbox, 
04948     int bGrowBox,
04949                 const ON_Xform* xform
04950     ) const
04951 {
04952   if ( 1 == m_points.Count() )
04953   {
04954     ON_3dPointArray P(1);
04955     P.Append( m_plane.PointAt(m_points[0].x,m_points[0].y) );
04956     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
04957       bGrowBox = true;
04958   }
04959   else if ( bGrowBox && !tight_bbox.IsValid() )
04960   {
04961     tight_bbox.Destroy();
04962     bGrowBox = false;
04963   }
04964 
04965   return (0!=bGrowBox);
04966 }
04967 
04968 int ON_TextEntity2::FontIndex() const
04969 {
04970   return m_index;
04971 }
04972 
04973 void ON_TextEntity2::SetFontIndex( int i)
04974 {
04975   m_index = i;
04976 }
04977 
04978 ON_BOOL32 ON_TextEntity2::Transform( const ON_Xform& xform )
04979 {
04980   // Dale Lear - this override fixes RR 11114 by correctly
04981   //             handling non uniform scaling.
04982   bool rc = xform.IsIdentity();
04983   if ( !rc)
04984   {
04985     ON_Plane xformed_plane = m_plane;
04986     rc = xformed_plane.Transform(xform);
04987     if (rc)
04988     rc = ON_Geometry::Transform(xform)?true:false;
04989     if (rc)
04990     {
04991       ON_3dPoint P0 = xform*m_plane.origin;
04992       ON_3dPoint P1 = xform*(m_plane.origin + m_plane.xaxis);
04993       double s = P0.DistanceTo(P1);
04994       if ( s <= ON_ZERO_TOLERANCE )
04995       {
04996         P1 = xform*(m_plane.origin + m_plane.yaxis);
04997         s = P0.DistanceTo(P1);
04998       }
04999       m_plane = xformed_plane;
05000       if ( s > ON_ZERO_TOLERANCE && fabs(s-1.0) > ON_SQRT_EPSILON )
05001       {
05002         s *= m_textheight;
05003         if ( s > ON_SQRT_EPSILON )
05004           m_textheight = s;
05005       }
05006     }
05007   }
05008 
05009   return rc;
05010 }
05011 
05012 
05013 
05014 // 6-23-03 lw Added v2 file writing of annotation
05015 void ON_TextEntity2::GetV2Form( ON_TextEntity& text)
05016 {
05017   ON_Annotation2::ConvertBack( text);
05018   text.SetHeight( Height());
05019   // 8-20-03 lw convert from lower-left to upper-left reference point
05020   ON_Plane plane = Plane();
05021   plane.origin  += plane.yaxis * 1.1 * m_textheight;
05022   plane.UpdateEquation();
05023   text.SetPlane( plane);
05024 }
05025 
05026 void ON_TextEntity2::SetJustification( unsigned int justification)
05027 {
05028   m_justification = justification;
05029 }
05030 
05031 unsigned int ON_TextEntity2::Justification()
05032 {
05033   return m_justification;
05034 }
05035 
05036 bool ON_TextEntity2::DrawTextMask() const
05037 {
05038   const ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, false);
05039   if(pTE)
05040     return pTE->DrawTextMask();
05041   else
05042     return false;
05043 }
05044 
05045 void ON_TextEntity2::SetDrawTextMask(bool bDraw)
05046 {
05047   ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, true);
05048   if(pTE)
05049     pTE->SetDrawTextMask(bDraw);
05050 }
05051 
05052 int ON_TextEntity2::MaskColorSource() const
05053 {
05054   const ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, false);
05055   if(pTE)
05056     return pTE->MaskColorSource();
05057   else
05058     return 0;
05059 }
05060 
05061 void ON_TextEntity2::SetMaskColorSource(int source)
05062 {
05063   ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, true);
05064   if(pTE)
05065     pTE->SetMaskColorSource(source);
05066 }
05067 
05068 ON_Color ON_TextEntity2::MaskColor() const
05069 {
05070   const ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, false);
05071   if(pTE)
05072     return pTE->MaskColor();
05073   else
05074     return 0;
05075 }
05076 
05077 void ON_TextEntity2::SetMaskColor(ON_Color color)
05078 {
05079   ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, true);
05080   if(pTE)
05081     pTE->SetMaskColor(color);
05082 }
05083 
05084 double ON_TextEntity2::MaskOffsetFactor() const
05085 {
05086   const ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, false);
05087   if(pTE)
05088     return pTE->MaskOffsetFactor();
05089   else
05090     return 0;
05091 }
05092 
05093 void ON_TextEntity2::SetMaskOffsetFactor(double offset)
05094 {
05095   ON_TextExtra* pTE = ON_TextExtra::TextExtension(this, true);
05096   if(pTE)
05097     pTE->SetMaskOffsetFactor(offset);
05098 }
05099 
05100 bool ON_TextEntity2::AnnotativeScaling() const
05101 {
05102   return m_annotative_scale;
05103 }
05104 
05105 void ON_TextEntity2::SetAnnotativeScaling(bool b)
05106 {
05107   m_annotative_scale = b;
05108 }
05109 
05110 
05111 
05112 //----- ON_Leader2 ------------------------------------------
05113 ON_Leader2::ON_Leader2()
05114 {
05115   m_type = ON::dtLeader;
05116   m_textdisplaymode = ON::dtInLine;
05117 }
05118 
05119 ON_Leader2::~ON_Leader2()
05120 {
05121 }
05122 
05123 ON_BOOL32 ON_Leader2::IsValid( ON_TextLog* text_log ) const
05124 {
05125   if ( m_type != ON::dtLeader )
05126   {
05127     if ( text_log )
05128     {
05129       text_log->Print("ON_Leader2 - m_type !=  ON::dtLeader\n");
05130     }
05131     return false;
05132   }
05133 
05134   if ( !ON_Annotation2::IsValid( text_log ))
05135   {
05136     if ( text_log )
05137     {
05138       text_log->Print("ON_Leader2 - invalid ON_Annotation2 base class.\n");
05139     }
05140     return false;
05141   }
05142 
05143   if ( m_points.Count() < 2 )
05144   {
05145     if ( text_log )
05146     {
05147       text_log->Print("ON_Leader2 - m_points.Count() = %d (should be >= 2)\n", m_points.Count() );
05148     }
05149     return false;
05150   }
05151 
05152   return true;
05153 }
05154 
05155 ON_BOOL32 ON_Leader2::Write(ON_BinaryArchive& archive) const
05156 {
05157   // 18 October 2007 Dale Lear
05158   //    I added the chunk wrapping so V5 and future versions can
05159   //    add IO support for information specific to ON_Leader2
05160   //    V4 did not have a ON_Leader2::Write and simply called
05161   //    ON_Leader2::Write.
05162   bool rc = false;
05163   bool bInChunk = (archive.Archive3dmVersion() >= 5);
05164   if ( bInChunk )
05165   {
05166     rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
05167     if ( !rc )
05168       return false;
05169   }
05170   else
05171   {
05172     rc = true;
05173   }
05174 
05175   while(rc)
05176   {
05177     rc = ON_Annotation2::Write(archive)?true:false;
05178     if (!rc) break;
05179     if ( !bInChunk )
05180       break;
05181 
05182     // To write new fields, increment minor version number
05183     // and write values here.  Ask Dale Lear for help.
05184 
05185     break;
05186   }
05187 
05188   if ( bInChunk )
05189   {
05190     if (!archive.EndWrite3dmChunk())
05191       rc = false;
05192   }
05193   return rc;
05194 }
05195 
05196 ON_BOOL32 ON_Leader2::Read(ON_BinaryArchive& archive)
05197 {
05198   // 18 October 2007 Dale Lear
05199   //    I added the chunk wrapping so V5 and future versions can
05200   //    add IO support for information specific to ON_Leader2
05201   int major_version = 0;
05202   int minor_version = 0;
05203   bool rc = false;
05204   bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
05205   if ( bInChunk )
05206   {
05207     rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
05208     if ( !rc )
05209       return false;
05210   }
05211   else
05212   {
05213     rc = true;
05214   }
05215 
05216   while(rc)
05217   {
05218     rc = ON_Annotation2::Read(archive)?true:false;
05219     if (!rc) break;
05220     if ( !bInChunk || minor_version <= 0 )
05221       break;
05222 
05223     // read future addition here
05224 
05225     break;
05226   }
05227 
05228   if ( bInChunk )
05229   {
05230     if ( !archive.EndRead3dmChunk() )
05231       rc = false;
05232   }
05233   return rc;
05234 }
05235 
05236 ON_BOOL32 ON_Leader2::GetBBox(
05237         double* boxmax,
05238         double* boxmin,
05239         ON_BOOL32 bGrowBox
05240         ) const
05241 {
05242   ON_BoundingBox bbox;
05243   if ( bGrowBox )
05244   {
05245     bbox.m_min.x = boxmin[0]; 
05246     bbox.m_min.y = boxmin[1]; 
05247     bbox.m_min.z = boxmin[2];
05248     bbox.m_max.x = boxmax[0]; 
05249     bbox.m_max.y = boxmax[1]; 
05250     bbox.m_max.z = boxmax[2];
05251     if ( !bbox.IsValid() )
05252     {
05253       bbox.Destroy();
05254       bGrowBox = false;
05255     }
05256   }
05257 
05258   const int point_count = m_points.Count();
05259   if ( point_count > 0 )
05260   {
05261     ON_3dPointArray P(point_count);
05262     int i;
05263     for ( i = 0; i < point_count; i++ )
05264     {
05265       ON_2dPoint uv = m_points[i];
05266       P.Append( m_plane.PointAt(uv.x,uv.y));
05267     }
05268     if ( P.GetBoundingBox(bbox,bGrowBox?true:false) )
05269       bGrowBox = true;
05270   }
05271 
05272   if ( bGrowBox )
05273   {
05274     boxmin[0] = bbox.m_min.x; 
05275     boxmin[1] = bbox.m_min.y; 
05276     boxmin[2] = bbox.m_min.z; 
05277     boxmax[0] = bbox.m_max.x; 
05278     boxmax[1] = bbox.m_max.y; 
05279     boxmax[2] = bbox.m_max.z; 
05280   }
05281 
05282   return bGrowBox;
05283 }
05284 
05285 
05286 
05287 bool ON_Leader2::GetTightBoundingBox( 
05288                 ON_BoundingBox& tight_bbox, 
05289     int bGrowBox,
05290                 const ON_Xform* xform
05291     ) const
05292 {
05293   const int point_count = m_points.Count();
05294   if ( point_count >= 2 )
05295   {
05296     ON_3dPointArray P(point_count);
05297     int i;
05298     for ( i = 0; i < point_count; i++ )
05299     {
05300       ON_2dPoint uv = m_points[i];
05301       P.Append( m_plane.PointAt(uv.x,uv.y));
05302     }
05303     if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
05304       bGrowBox = true;
05305   }
05306   else if ( bGrowBox && !tight_bbox.IsValid() )
05307   {
05308     tight_bbox.Destroy();
05309     bGrowBox = false;
05310   }
05311 
05312   return (0!=bGrowBox);
05313 }
05314 
05315 ON_2dPoint ON_Leader2::Dim2dPoint(
05316       int point_index
05317       ) const
05318 {
05319   ON_2dPoint p2;
05320   int point_count = m_points.Count();
05321   if ( point_index < 0 || point_count < 1 )
05322   {
05323     p2.x = p2.y = ON_UNSET_VALUE;
05324   }
05325   else
05326   {
05327     switch(point_index)
05328     {
05329     case arrow_pt_index:
05330       p2 = m_points[0];
05331       break;
05332 
05333     case text_pivot_pt:
05334     case tail_pt:
05335       p2 = *m_points.Last();
05336       break;
05337 
05338     default:
05339       if ( point_index < point_count )
05340       {
05341         p2 = m_points[point_index];
05342       }
05343       else
05344       {
05345         p2.x = p2.y = ON_UNSET_VALUE;
05346       }
05347       break;
05348     }
05349   }
05350   return p2;
05351 }
05352 
05353 ON_3dPoint ON_Leader2::Dim3dPoint(
05354       int point_index
05355       ) const
05356 {
05357   ON_2dPoint p2 = Dim2dPoint(point_index);
05358   return (ON_UNSET_VALUE == p2.x) ? ON_UNSET_POINT : m_plane.PointAt(p2.x,p2.y);
05359 }
05360 
05361 
05362 void ON_Leader2::AddPoint( const ON_2dPoint& point )
05363 {
05364   m_points.Append( point);
05365 
05366 }
05367 
05368 bool ON_Leader2::RemovePoint( int idx )
05369 {
05370   bool rc = true;
05371   if( idx == -1)  // -1 removes the last point
05372   {
05373     m_points.Remove();
05374   }
05375   else if( idx >= 0 && idx < m_points.Count())
05376   {
05377     m_points.Remove( idx);
05378   }
05379   else
05380   {
05381     rc = false;
05382   }
05383 
05384   return rc;
05385 }
05386 
05387 // April 22, 2010 Lowell - Added to support right justified text on left pointing leader tails rr64292
05388 bool ON_Leader2::GetTextDirection( ON_2dVector& text_dir ) const 
05389 {
05390   bool rc = false;
05391   const int point_count = m_points.Count();
05392   if ( point_count < 2 )
05393   {
05394     text_dir.Set(-1.0,0.0);
05395   }
05396   else
05397   {
05398     int i; // 20 June 2011 Fixed textdir for leaders with 2 points. rr86801
05399     for(i = point_count-1; i >= 1; i--)
05400     {
05401       text_dir = m_points[point_count-1] -  m_points[i-1];
05402       if(text_dir.Unitize())
05403       {
05404         rc = true;
05405         break;
05406       }
05407       text_dir.Set(-1.0,0.0);
05408     }
05409   }
05410   return rc;
05411 }
05412 
05413 bool ON_Leader2::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const
05414 {
05415   bool rc = false;
05416   const int point_count = m_points.Count();
05417   if ( point_count < 2 )
05418   {
05419     arrowhead_dir.Set(-1.0,0.0);
05420   }
05421   else
05422   {
05423     int i;
05424     for ( i = 1; i < point_count; i++ )
05425     {
05426       arrowhead_dir = m_points[0] -  m_points[i];
05427       if ( arrowhead_dir.Unitize() )
05428       {
05429         rc = true;
05430         break;
05431       }
05432       arrowhead_dir.Set(-1.0,0.0);
05433     }
05434   }
05435   return rc;
05436 }
05437 
05438 bool ON_Leader2::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const
05439 {
05440   bool rc = false;
05441   switch( m_points.Count())
05442   {
05443   case 0:
05444     arrowhead_tip.Set(0.0,0.0);
05445     break;
05446   case 1:
05447     arrowhead_tip = m_points[0];
05448     break;
05449   default:
05450     arrowhead_tip = m_points[0];
05451     rc = true;
05452     break;
05453   }
05454   return rc;
05455 }
05456 
05457 
05458 
05459 bool ON_RadialDimension2::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const
05460 {
05461   bool rc = false;
05462   if ( m_points.Count() < 4 )
05463   {
05464     arrowhead_dir.Set(-1.0,0.0);
05465   }
05466   else
05467   {
05468     arrowhead_dir = m_points[1] - m_points[3];
05469     if ( 0 == (rc=arrowhead_dir.Unitize()) )
05470     {
05471       arrowhead_dir = m_points[1] - m_points[2];
05472       if ( 0 == (rc=arrowhead_dir.Unitize()) )
05473       {
05474         arrowhead_dir = m_points[0] - m_points[1];
05475         rc = arrowhead_dir.Unitize();
05476       }
05477     }
05478   }
05479   return rc;
05480 }
05481 
05482 bool ON_RadialDimension2::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const
05483 {
05484   bool rc = false;
05485   if ( m_points.Count() >= 2 )
05486   {
05487     arrowhead_tip = m_points[1];
05488     rc = true;
05489   }
05490   else
05491   {
05492     arrowhead_tip.Set(0.0,0.0);
05493     rc = false;
05494   }
05495   return rc;
05496 }
05497 
05498 // 6-23-03 lw Added v2 file writing of annotation
05499 void ON_Leader2::GetV2Form( ON_Leader& leader)
05500 {
05501   ON_Annotation2::ConvertBack( leader);
05502 }
05503 
05504 
05505 bool ON_Leader2::CreateFromV2( 
05506     const ON_Annotation& v2_ann,
05507     const ON_3dmAnnotationSettings& settings,
05508     int dimstyle_index
05509     )
05510 {
05511   bool rc = false;
05512   if( ON::dtLeader == v2_ann.m_type && v2_ann.m_points.Count() >= 2 )
05513   {
05514     m_plane = v2_ann.m_plane;
05515     m_plane.UpdateEquation();
05516     m_points.Reserve(v2_ann.m_points.Count());
05517     m_points.SetCount(0);
05518     m_points.Append(v2_ann.m_points.Count(),v2_ann.m_points.Array());
05519     ON_2dVector v = m_points[0];
05520     SetTextValue(v2_ann.UserText());
05521     SetTextFormula(0);
05522     m_userpositionedtext = false;
05523     m_textdisplaymode = ( 2 == settings.m_textalign )
05524                       ? ON::dtHorizontal
05525                       : ON::dtInLine;
05526     m_type = ON::dtLeader;
05527     m_index = dimstyle_index;
05528 
05529     if ( !v.IsZero() )
05530     {
05531       m_plane.origin = m_plane.PointAt(v.x,v.y);
05532       m_plane.UpdateEquation();
05533       v.Reverse();
05534       int i;
05535       for ( i = 1; i < m_points.Count(); i++ )
05536       {
05537         m_points[i] += v;
05538       }
05539       m_points[0].Set(0.0,0.0);
05540     }
05541     rc = true;
05542   }
05543   return rc;
05544 }
05545 
05546 
05547 // class ON_TextDot
05548 //--------------------------------------------------------------------
05549 ON_TextDot::ON_TextDot() :
05550   m_point( ON_origin), m_height( 14), m_text( L'1'), 
05551   m_fontface( L"Arial bold"), m_display( 0)
05552 {
05553 }
05554 
05555 ON_TextDot::~ON_TextDot()
05556 {
05557 }
05558 
05559 void ON_TextDot::EmergencyDestroy()
05560 {
05561   m_text.EmergencyDestroy();
05562   m_fontface.EmergencyDestroy();
05563   m_point = ON_origin;
05564   m_height = 0;
05565   m_display = 0;
05566 }
05567 
05568 ON_BOOL32 ON_TextDot::IsValid( 
05569             ON_TextLog* text_log
05570             ) const
05571 {
05572   // 5/6/03 LW made dots with no text valid.
05573   if ( !m_point.IsValid() )
05574   {
05575     if ( 0 != text_log )
05576     {
05577       text_log->Print("ON_TextDot m_point is not valid\n");
05578     }
05579     return false;
05580   }
05581   return true;
05582 }
05583 
05584 void ON_TextDot::Dump( ON_TextLog& log) const
05585 {
05586   log.Print("ON_TextDot \"%ls\" at ",m_text.Array());
05587   log.Print( m_point);  // ON_Geometry 3d location
05588   log.Print("\n");
05589 }
05590 
05591 ON_BOOL32 ON_TextDot::Write( ON_BinaryArchive& file) const
05592 {
05593   ON_BOOL32 rc = file.Write3dmChunkVersion(1,0);
05594   if (rc) rc = file.WritePoint( m_point );
05595   if (rc) rc = file.WriteInt( m_height);
05596   if (rc) rc = file.WriteString( m_text);
05597   if (rc) rc = file.WriteString( m_fontface);
05598   if (rc) rc = file.WriteInt( m_display);
05599 
05600   return rc;
05601 }
05602 
05603 ON_BOOL32 ON_TextDot::Read( ON_BinaryArchive& file)
05604 {
05605   m_text.Empty();
05606   int major_version = 0;
05607   int minor_version = 0;
05608   ON_BOOL32 rc = file.Read3dmChunkVersion(&major_version,&minor_version);
05609   if ( major_version == 1 ) {
05610     if (rc) rc = file.ReadPoint( m_point);
05611     if (rc) rc = file.ReadInt( &m_height);
05612     if (rc) rc = file.ReadString( m_text);
05613     if (rc) rc = file.ReadString( m_fontface);
05614     if (rc) rc = file.ReadInt( &m_display);
05615   }
05616   else {
05617     rc = false;
05618   }
05619   return rc;
05620 }
05621 
05622 ON::object_type ON_TextDot::ObjectType() const
05623 {
05624   return ON::text_dot;
05625 }
05626 
05627 int ON_TextDot::Dimension() const
05628 {
05629   return 3;
05630 }
05631 
05632 ON_BOOL32 ON_TextDot::GetBBox( double* box_min, double* box_max, ON_BOOL32 grow_box /*= false*/) const
05633 {
05634   return ON_GetPointListBoundingBox( 3, 0, 1, 3, &m_point.x, box_min, box_max, grow_box?true:false );
05635 }
05636 
05637 ON_BOOL32 ON_TextDot::Transform( const ON_Xform& xform)
05638 {
05639   TransformUserData( xform);
05640   return ON_TransformPointList( 3, 0, 1, 3, &m_point.x, xform);
05641 }
05642 
05643 bool ON_TextDot::IsDeformable() const
05644 {
05645   return true;
05646 }
05647 
05648 bool ON_TextDot::MakeDeformable()
05649 {
05650   return true;
05651 }
05652 
05653 const ON_3dPoint& ON_TextDot::Point() const
05654 {
05655   return m_point;
05656 }
05657 
05658 void ON_TextDot::SetPoint( const ON_3dPoint& point)
05659 {
05660   m_point =  point;
05661 }
05662 
05663 int ON_TextDot::Height() const
05664 {
05665   return m_height;
05666 }
05667 
05668 void ON_TextDot::SetHeight( int height)
05669 {
05670   if( height > 2)
05671     m_height = height;
05672 }
05673 
05674 const wchar_t* ON_TextDot::TextString() const 
05675 {
05676   if( m_text.IsEmpty())
05677     return L"";
05678   else
05679     return m_text;
05680 }
05681 
05682 void ON_TextDot::SetTextString( const wchar_t* string)
05683 {
05684   m_text.Empty();
05685   if( string)
05686   {
05687     int len = (int)wcslen(string);
05688     wchar_t* str = 0;
05689     if(len > 0 && string[len-1] <= L' ')
05690     {
05691       // trim off trailing white space
05692       str = (wchar_t*)onmalloc((len+1)*sizeof(wchar_t));
05693       int j = 0;
05694       for(int i = 0; i < len; i++)
05695       {
05696         if(string[i] == L'\r' || string[i] == L'\n')
05697           continue;
05698         str[j++] = string[i];
05699       }
05700       str[j] = 0;
05701 //      wcscpy(str, string);
05702 
05703       for(int i = len-1; i >= 0 && str[i] <= L' '; i--)
05704         str[i] = 0;
05705     }
05706     if(str)
05707     {
05708       if(wcslen(str) > 0)
05709         m_text = str;
05710       onfree(str);
05711     }
05712     else
05713       m_text = string;
05714   }
05715 }
05716 
05717 const wchar_t* ON_TextDot::FontFace() const
05718 {
05719   if( m_fontface.IsEmpty())
05720     return L"";
05721   else
05722     return m_fontface;
05723 }
05724 
05725 void ON_TextDot::SetFontFace( const wchar_t* face)
05726 {
05727   if( face)
05728     m_fontface = face;
05729   else
05730     m_fontface.Empty();
05731 }
05732 
05733 void ON_TextDot::SetAlwaysOnTop(bool bTop)
05734 {
05735   if(bTop)
05736     m_display |= 1;
05737   else
05738     m_display &= (~1);
05739 }
05740 
05741 bool ON_TextDot::AlwaysOnTop() const
05742 {
05743   return (m_display & 1) == 1;
05744 }
05745 
05746 void ON_TextDot::SetTransparent(bool bTransparent)
05747 {
05748   if(bTransparent)
05749     m_display |= 2;
05750   else
05751     m_display &= (~2);
05752 }
05753 
05754 bool ON_TextDot::Transparent() const
05755 {
05756   return (m_display & 2) == 2;
05757 }
05758 
05759 void ON_TextDot::SetBold(bool bBold)
05760 {
05761   if(bBold)
05762     m_display |= 4;
05763   else
05764     m_display &= (~4);
05765 }
05766 
05767 bool ON_TextDot::Bold() const
05768 {
05769   return (m_display & 4) == 4;
05770 }
05771 
05772 void ON_TextDot::SetItalic(bool bItalic)
05773 {
05774   if(bItalic)
05775     m_display |= 8;
05776   else
05777     m_display &= (~8);
05778 }
05779 
05780 bool ON_TextDot::Italic() const
05781 {
05782   return (m_display & 8) == 8;
05783 }
05784 
05785 
05786 
05787 
05788 ON_Annotation2Text::ON_Annotation2Text()
05789 {
05790   memset(&m_rect,0,sizeof(m_rect));
05791 }
05792 
05793 ON_Annotation2Text::~ON_Annotation2Text()
05794 {
05795 }
05796 
05797 ON_Annotation2Text& ON_Annotation2Text::operator=(const char* s)
05798 {
05799   SetText(s);
05800   return *this;
05801 }
05802 
05803 ON_Annotation2Text& ON_Annotation2Text::operator=(const wchar_t* s)
05804 {
05805   SetText(s);
05806   return *this;
05807 }
05808 
05809 void ON_Annotation2Text::SetText(const char* s)
05810 {
05811   ON_wString::operator=(s);
05812   memset(&m_rect,0,sizeof(m_rect));
05813 }
05814 
05815 void ON_Annotation2Text::SetText(const wchar_t* s)
05816 {
05817   ON_wString::operator=(s);
05818   memset(&m_rect,0,sizeof(m_rect));
05819 }
05820 
05821 // SDKBREAK Oct 30, 07 - LW
05822 // This function should not be used any longer
05823 bool ON_Annotation2::GetTextXform( 
05824       ON_RECT gdi_text_rect,
05825       const ON_Font& font,
05826       const ON_DimStyle& dimstyle,
05827       double dimscale,
05828       const ON_Viewport* vp,
05829       ON_Xform& xform
05830       ) const
05831 {
05832   ON_ERROR("This function should not be used. Use the version that takes a model transform argument.");
05833   return false;
05834 
05835   //const int gdi_height_of_I = font.HeightOfI();
05836   //const double dimstyle_textheight = dimstyle.TextHeight();
05837   //double dimstyle_textgap = dimstyle.TextGap();
05838   //const ON::eTextDisplayMode dimstyle_textalignment 
05839   //          =  ON::TextDisplayMode(dimstyle.TextAlignment()) ;
05840   //const ON_3dVector cameraX = (vp) ? vp->CameraX() : m_plane.xaxis;
05841   //const ON_3dVector cameraY = (vp) ? vp->CameraY() : m_plane.yaxis;
05842 
05845   //if(( dimstyle.ToleranceStyle() == 2 || dimstyle.ToleranceStyle() == 3) &&
05846   //  ( Type() == ON::dtDimLinear || Type() == ON::dtDimAligned))
05847   //  dimstyle_textgap += dimstyle_textheight * 0.5;
05848 
05849   //return GetTextXform( 
05850   //    gdi_text_rect,
05851   //    gdi_height_of_I,
05852   //    dimstyle_textheight, dimstyle_textgap, dimstyle_textalignment,
05853   //    dimscale,
05854   //    cameraX, cameraY, 
05855   //    xform
05856   //    );
05857 }
05858 
05859 // New function added Oct 30, 07 - LW 
05860 // To use model xform to draw annotation in blocks correctly
05861 #if 0
05862 bool ON_Annotation2::GetTextXform( 
05863       ON_RECT gdi_text_rect,
05864       const ON_Font& font,
05865       const ON_DimStyle& dimstyle,
05866       double dimscale,
05867       const ON_Viewport* vp,
05868       const ON_Xform* model_xform,
05869       ON_Xform& xform
05870       ) const
05871 {
05872   ON_ERROR("This function should not be used. Use the one below that takes a dimstyle pointer.");
05873   return false;
05874 }
05875 #endif
05876 
05877 bool ON_Annotation2::GetTextXform( 
05878       ON_RECT gdi_text_rect,
05879       const ON_Font& font,
05880       const ON_DimStyle* dimstyle,
05881       double dimscale,
05882       const ON_Viewport* vp,
05883       const ON_Xform* model_xform,
05884       ON_Xform& xform
05885       ) const
05886 {
05887   int gdi_height_of_I = font.HeightOfI();
05888   const double textheight = dimstyle ? dimstyle->TextHeight() : m_textheight;
05889   double textgap =  dimstyle ? dimstyle->TextGap() : 0.0;
05890   const ON::eTextDisplayMode textalignment = dimstyle ? ON::TextDisplayMode(dimstyle->TextAlignment()) : ON::dtNormal;
05891   const ON_3dVector cameraX = (vp) ? vp->CameraX() : m_plane.xaxis;
05892   const ON_3dVector cameraY = (vp) ? vp->CameraY() : m_plane.yaxis;
05893   if(dimstyle)
05894   {
05895     // SDKBREAK - Oct 4, 07 LW Get correct text gap using 
05896     // multi-line tolerance text since GetTextXform doesn't do that.
05897     if(( dimstyle->ToleranceStyle() == 2 || dimstyle->ToleranceStyle() == 3) &&
05898       ( Type() == ON::dtDimLinear || Type() == ON::dtDimAligned))
05899         textgap += textheight * 0.5;
05900   }
05901   return GetTextXform( 
05902       gdi_text_rect,
05903       gdi_height_of_I,
05904       textheight, textgap, textalignment,
05905       dimscale,
05906       cameraX, cameraY, 
05907       model_xform,
05908       xform
05909       );
05910 }
05911 
05912 
05913 
05914 static bool GetLeaderEndAndDirection( const ON_Annotation2* pAnn,
05915                                      ON_2dPoint& E,
05916                                      ON_2dVector& R )
05917 {
05918   bool rc = false;
05919 
05920   ON::eAnnotationType ann_type = pAnn->m_type;
05921   const ON_2dPointArray& ann_m_points = pAnn->m_points;
05922 
05923 
05924   R.Set(1.0,0.0); // unit vector points to end
05925   E.Set(0.0,0.0); // end point
05926 
05927   if ( ann_m_points.Count() >= 4 && (ON::dtDimDiameter == ann_type || ON::dtDimRadius == ann_type) )
05928   {
05929     E = ann_m_points[2]; // end of radial dimension
05930     R = E - ann_m_points[3];
05931     if ( !R.Unitize() )
05932     {
05933       R = E - ann_m_points[1];
05934       if ( !R.Unitize() )
05935       {
05936         R = E - ann_m_points[0];
05937         if ( !R.Unitize() )
05938         {
05939           R.Set(1.0,0.0);
05940         }
05941       }
05942     }
05943     rc = true;
05944   }
05945   else if ( ann_m_points.Count() >= 2 && ON::dtLeader == ann_type )
05946   {
05947     int i;
05948     E = *ann_m_points.Last();
05949     for (i = ann_m_points.Count()-2; i >= 0; i-- )
05950     {
05951       R = E - ann_m_points[i];
05952       if ( R.Unitize() )
05953       {
05954         break;
05955       }
05956       R.Set(1.0,0.0);
05957     }
05958     rc = true;
05959   }
05960   else if ( ann_m_points.Count() >= 2 && ON::dtDimOrdinate == ann_type )
05961   {
05962     E = ann_m_points[1];
05963 
05964     int direction = (( ON_OrdinateDimension2*)pAnn)->Direction();
05965     if( direction == -1)
05966     {
05967       if( fabs( ann_m_points[1].x - ann_m_points[0].x) 
05968        <= fabs( ann_m_points[1].y - ann_m_points[0].y))
05969         direction = 0;
05970       else
05971         direction = 1;
05972     }
05973 
05974     if( direction == 0)
05975       R.Set( 0.0, ann_m_points[1].y - ann_m_points[0].y);
05976     else
05977       R.Set( ann_m_points[1].x - ann_m_points[0].x, 0.0);
05978 
05979     if( !R.Unitize())
05980       R.Set(1.0,0.0);
05981 
05982     rc = true;
05983   }
05984 
05985   return rc;
05986 }
05987 
05988 // SDKBREAK Oct 30, 07 - LW
05989 // This function should not be used any longer
05990 bool ON_Annotation2::GetTextXform( 
05991       ON_RECT gdi_text_rect,
05992       int gdi_height_of_I,
05993       double dimstyle_textheight,
05994       double dimstyle_textgap,
05995       ON::eTextDisplayMode dimstyle_textalignment,
05996       double dimscale,
05997       ON_3dVector cameraX,
05998       ON_3dVector cameraY,
05999       ON_Xform& xform
06000       ) const
06001 {
06002   ON_ERROR("This function should not be used. Use the version that takes a model transform argument.");
06003   return false;
06004 
06005   //const ON_Annotation2* ann = this;
06006 
06007   //const ON::eAnnotationType ann_type = ann->m_type;
06008 
06009   //if ( 0 == gdi_height_of_I )
06010   //{
06011   //  // Default to height of Ariel 'I'
06012   //  gdi_height_of_I = (165*ON_Font::normal_font_height)/256;
06013   //}
06014 
06015   //if ( 0.0 == dimscale )
06016   //{
06017   //  dimscale = 1.0;
06018   //}
06019 
06020   //dimstyle_textheight *= dimscale;
06021   //dimstyle_textgap *= dimscale;
06022 
06023   //double textheight = ( ON::dtTextBlock == ann_type )
06024   //                  ? m_textheight*dimscale
06025   //                  : dimstyle_textheight;
06026   //if ( 0.0 == textheight )
06027   //  textheight = 1.0;
06028 
06029   //ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY );
06030   //if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON )
06031   //{
06032   //  cameraZ.Unitize();
06033   //}
06034 
06037   //const double gdi_to_plane_scale = textheight/gdi_height_of_I;
06038   //ON_Xform gdi_to_plane(1.0);
06039   //gdi_to_plane.m_xform[0][0] =  gdi_to_plane_scale;
06040   //gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale;
06041 
06043   //const double text_line_width  = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left);
06045 
06046   //if ( ON::dtTextBlock == ann_type )
06047   //{
06048   //  // The orientation of the text is text blocks
06049   //  // does not depend on the view or text alignment
06050   //  // settings.  The position and orientation of 
06051   //  // the text in every other annotation depends on
06052   //  // the view and text alignment settings.  
06053   //  //
06054   //  // It simplifies the code for the rest of the
06055   //  // annotation settings to quickly deal with text
06056   //  // blocks here.
06057   //  ON_Xform plane_to_world(1.0);
06058   //  plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
06059   //  xform = plane_to_world*gdi_to_plane;
06060   //  return true;
06061   //}
06062 
06063 
06069   //int position_style = 0;
06070   //switch( ann_type )
06071   //{
06072   //case ON::dtDimAligned:
06073   //case ON::dtDimLinear:
06074   //case ON::dtDimAngular:
06075   //  // dimension definition determines center point of text box
06076   //  position_style = 1;
06077   //  break;
06078 
06079   //case ON::dtLeader:
06080   //case ON::dtDimRadius:
06081   //case ON::dtDimDiameter:
06082   //case ON::dtDimOrdinate:
06083   //  // dimension definition determines end of text box
06084   //  position_style = 2;
06085   //  break;
06086 
06087   //case ON::dtTextBlock:
06088   //case ON::dtNothing:
06089   //  break;
06090   //}
06091 
06092 
06095   //if ( ON::dtHorizontal != dimstyle_textalignment || 1 == position_style )
06096   //{
06097 
06098   //  gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
06099   //  gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
06100   //}
06101   //gdi_to_plane.m_xform[1][3] = -0.5*textheight;
06102 
06103   //if ( ON::dtHorizontal != dimstyle_textalignment )
06104   //{
06105   //  if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) )
06106   //  {
06107   //    // Viewing dimension from the backside
06108   //    ON_Xform flip(1.0);
06109   //    switch ( position_style )
06110   //    {
06111   //    case 1: // ON::dtDimLinear, ON::dtDimAligned, ON::dtDimAngular
06112   //      flip.m_xform[0][0] = -1.0;
06113   //      flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right;
06114   //      break;
06115 
06116   //    case 2: // ON::dtDimDiameter, ON::dtDimRadius, ON::dtLeader
06117   //      flip.m_xform[1][1] = -1.0;
06118   //      flip.m_xform[1][3] = gdi_text_rect.top + gdi_text_rect.bottom;
06119   //      break;
06120   //    }
06121   //    gdi_to_plane = gdi_to_plane*flip;
06122   //  }
06123   //}
06124 
06127   //ON_2dVector text_centering_rotation(1.0,0.0);
06128 
06133   //ON_2dVector text_centering_translation(0.0,0.0);
06134 
06135   //double x, y;
06136 
06137   //if ( ON::dtHorizontal != dimstyle_textalignment )
06138   //{
06139   //  if ( ON::dtDimLinear  == ann_type || ON::dtDimAligned == ann_type )
06140   //  {
06141   //    if ( ON::dtAboveLine == dimstyle_textalignment )
06142   //    {
06143   //      text_centering_translation.y =  0.5*textheight+dimstyle_textgap;
06144   //    }
06145   //    y =  ann->m_plane.yaxis*cameraY;
06146   //    x = -ann->m_plane.yaxis*cameraX;
06147   //    if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
06148   //    {
06149   //      y = x;
06150   //    }
06151   //    if ( y < 0.0 )
06152   //    {
06153   //      text_centering_translation.Reverse();
06154   //      text_centering_rotation.Reverse(); // rotate 180 degrees
06155   //    }
06156   //  }
06157   //  else if ( ON::dtDimAngular == ann_type )
06158   //  {
06159   //    // This transform rotates the text in the annotation plane.
06160   //    const ON_AngularDimension2* angular_dim = ON_AngularDimension2::Cast(ann);
06161   //    if ( 0 != angular_dim )
06162   //    {
06163   //      double a = 0.5*angular_dim->m_angle;
06164   //      ON_2dVector R(cos(a),sin(a));
06165   //      a -= 0.5*ON_PI;
06166   //      text_centering_rotation.x = cos(a);
06167   //      text_centering_rotation.y = sin(a);
06168   //      ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
06169   //      x = V*cameraX;
06170   //      y = V*cameraY;
06171   //      if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
06172   //      {
06173   //        y = -x;
06174   //      }
06175   //      if ( y < 0.0 )
06176   //      {
06177   //        text_centering_rotation.Reverse(); // add another 180 degrees of rotation
06178   //      }
06179 
06180   //      if ( ON::dtAboveLine == dimstyle_textalignment )
06181   //      {
06182   //        y = 0.5*textheight + dimstyle_textgap;
06183   //        text_centering_translation.x = -y*text_centering_rotation.y;
06184   //        text_centering_translation.y =  y*text_centering_rotation.x;
06185   //      }
06186   //    }
06187   //  }
06188   //  else if (    ON::dtDimDiameter == ann_type 
06189   //            || ON::dtDimRadius == ann_type 
06190   //            || ON::dtLeader == ann_type 
06191   //            || ON::dtDimOrdinate == ann_type)
06192   //  {
06193   //    ON_2dPoint E(0.0,0.0); // end point
06194   //    ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
06195   //    GetLeaderEndAndDirection( this, E, R );
06196 
06197   //    text_centering_rotation = R;
06198 
06199   //    text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation;
06200 
06201   //    ON_3dVector V = text_centering_rotation.x*m_plane.xaxis + text_centering_rotation.y*m_plane.yaxis;
06202   //    x = V*cameraX;
06203   //    y = V*cameraY;
06204   //    if ( fabs(x) <= ON_SQRT_EPSILON && fabs(y) > ON_SQRT_EPSILON )
06205   //    {
06206   //      x = y;
06207   //    }
06208   //    if ( x < 0.0 )
06209   //    {
06210   //      text_centering_rotation.Reverse(); // rotate 180 degrees
06211   //    }
06212   //  }
06213   //}
06214 
06215   //ON_Xform text_centering_xform(1.0);
06216   //text_centering_xform.m_xform[0][0] =  text_centering_rotation.x;
06217   //text_centering_xform.m_xform[0][1] = -text_centering_rotation.y;
06218   //text_centering_xform.m_xform[1][0] =  text_centering_rotation.y;
06219   //text_centering_xform.m_xform[1][1] =  text_centering_rotation.x;
06222   //text_centering_xform.m_xform[0][3] =  text_centering_translation.x;
06223   //text_centering_xform.m_xform[1][3] =  text_centering_translation.y;
06224 
06227   //ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset()
06228   //switch( ann_type )
06229   //{
06230   //case ON::dtDimLinear:
06231   //case ON::dtDimAligned:
06232   //  if ( m_points.Count() >= ON_LinearDimension2::dim_pt_count )
06233   //  {
06234   //    const ON_LinearDimension2* linear_dim = ON_LinearDimension2::Cast(ann);
06235   //    if ( linear_dim )
06236   //    {
06237   //      text_offset_translation = linear_dim->Dim2dPoint(ON_LinearDimension2::text_pivot_pt);
06238   //    }
06239   //  }
06240   //  break;
06241 
06242   //case ON::dtDimAngular:
06243   //  if ( m_points.Count() >= ON_AngularDimension2::dim_pt_count )
06244   //  {
06245   //    const ON_AngularDimension2* angular_dim = ON_AngularDimension2::Cast(ann);
06246   //    if ( angular_dim )
06247   //    {
06248   //      text_offset_translation = angular_dim->Dim2dPoint(ON_AngularDimension2::text_pivot_pt);
06249   //    }
06250   //  }
06251   //  break;
06252 
06253   //case ON::dtDimDiameter:
06254   //case ON::dtDimRadius:
06255   //  if ( m_points.Count() >= ON_RadialDimension2::dim_pt_count )
06256   //  {
06257   //    // No user positioned text on radial dimensions.
06258   //    text_offset_translation = m_points[ON_RadialDimension2::tail_pt_index];
06259   //  }
06260   //  break;
06261 
06262   //case ON::dtLeader:
06263   //  if ( m_points.Count() > 0 )
06264   //  {
06265   //    // No user positioned text on leaders.
06266   //    text_offset_translation = *m_points.Last();
06267   //  }
06268   //  break;
06269 
06270   //case ON::dtDimOrdinate:
06271   //  if ( m_points.Count() == 2 )
06272   //  {
06273   //    // No user positioned text on leaders.
06274   //    text_offset_translation = m_points[1];
06275   //  }
06276   //  break;
06277 
06278   //case ON::dtTextBlock:
06279   //case ON::dtNothing:
06280   //  break;
06281   //}
06282 
06283   //ON_Xform plane_translation(1.0);
06284   //plane_translation.m_xform[0][3] = text_offset_translation.x;
06285   //plane_translation.m_xform[1][3] = text_offset_translation.y;
06286 
06288   //ON_Xform plane_to_world(1.0);
06289   //plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
06290 
06291   //ON_Xform horizonal_xform(1.0);
06292   //if ( ON::dtHorizontal == dimstyle_textalignment )
06293   //{
06294   //  ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y);
06295   //  horizonal_xform.Rotation( 
06296   //      fixed_point,
06297   //      ann->m_plane.xaxis,
06298   //      ann->m_plane.yaxis,
06299   //      ann->m_plane.zaxis,
06300   //      fixed_point,
06301   //      cameraX,
06302   //      cameraY,
06303   //      cameraZ
06304   //      );
06305 
06306   //  if ( 2 == position_style )
06307   //  {
06308   //    // leaders, radial, and diameter
06309   //    ON_2dPoint E(0.0,0.0); // end point
06310   //    ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
06311   //    GetLeaderEndAndDirection( this, E, R );
06312   //    ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
06313   //    x = V*cameraX;
06314   //    y = ( x > -ON_SQRT_EPSILON )
06315   //      ? dimstyle_textgap
06316   //      : -(dimstyle_textgap + text_line_width);
06317   //    V = y*cameraX;
06318   //    horizonal_xform.m_xform[0][3] += V.x;
06319   //    horizonal_xform.m_xform[1][3] += V.y;
06320   //    horizonal_xform.m_xform[2][3] += V.z;
06321   //  }
06322   //}
06323 
06324   //ON_Xform gdi_to_world;
06325   //gdi_to_world = horizonal_xform
06326   //              * plane_to_world
06327   //              * plane_translation
06328   //              * text_centering_xform
06329   //              * gdi_to_plane;
06330 
06331   //xform = gdi_to_world;
06332 
06333   //return true;
06334 }
06335 
06336 //static bool do_plane_translation = true;
06337 //static bool do_text_centering_xform = true;
06338 //static bool do_text_centering_rotation = true;
06339 //static bool do_text_centering_translation = true;
06340 //static bool do_mirror_flip = true;
06341 //static bool do_flip_x = true;
06342 //static bool do_flip_y = true;
06343 
06344 // New function added Oct 30, 07 - LW 
06345 // To use model xform to draw annotation in blocks correctly
06346 bool ON_Annotation2::GetTextXform( 
06347       ON_RECT gdi_text_rect,
06348       int gdi_height_of_I,
06349       double dimstyle_textheight,
06350       double dimstyle_textgap,
06351       ON::eTextDisplayMode dimstyle_textalignment,
06352       double dimscale,
06353       ON_3dVector cameraX,
06354       ON_3dVector cameraY,
06355       const ON_Xform* model_xform,
06356       ON_Xform& xform
06357       ) const
06358 {
06359   ON_Xform mxi;
06360   if( model_xform)
06361   {
06362     mxi = model_xform->Inverse();
06363     cameraX.Transform( mxi);
06364     cameraY.Transform( mxi);
06365   }
06366   const ON_Annotation2* ann = this;
06367 
06368   const ON::eAnnotationType ann_type = ann->m_type;
06369 
06370   if ( 0 == gdi_height_of_I )
06371   {
06372     // Default to height of Ariel 'I'
06373     gdi_height_of_I = (165*ON_Font::normal_font_height)/256;
06374   }
06375 
06376   if ( 0.0 == dimscale )
06377   {
06378     dimscale = 1.0;
06379   }
06380 
06381   dimstyle_textheight *= dimscale;
06382   dimstyle_textgap *= dimscale;
06383 
06384   double textheight = ( ON::dtTextBlock == ann_type )
06385                     ? m_textheight*dimscale
06386                     : dimstyle_textheight;
06387   if ( 0.0 == textheight )
06388     textheight = 1.0;
06389 
06390   ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY );
06391   if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON )
06392   {
06393     cameraZ.Unitize();
06394   }
06395 
06396   // This xform is a scale from Windows gdi coordinates 
06397   // to annotation plane coordinates.
06398   const double gdi_to_plane_scale = textheight/gdi_height_of_I;
06399   ON_Xform gdi_to_plane(1.0);
06400   gdi_to_plane.m_xform[0][0] =  gdi_to_plane_scale;
06401   gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale;
06402 
06403   // width and height of text line in Rhino units.
06404   const double text_line_width  = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left);
06405   //const double text_line_height = gdi_to_plane_scale*(gdi_text_rect.bottom - gdi_text_rect.top);
06406 
06407   if ( ON::dtTextBlock == ann_type )
06408   {
06409     // The orientation of the text is text blocks
06410     // does not depend on the view or text alignment
06411     // settings.  The position and orientation of 
06412     // the text in every other annotation depends on
06413     // the view and text alignment settings.  
06414     //
06415     // It simplifies the code for the rest of the
06416     // annotation settings to quickly deal with text
06417     // blocks here.
06418     ON_Xform plane_to_world(1.0);
06419     plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
06420     xform = plane_to_world*gdi_to_plane;
06421     return true;
06422   }
06423 
06424   // text_position_mode
06425   //   1 = linear, aligned, or anglular dimension
06426   //       (dimension definition determines center point of text box)
06427   //   2 = radial, diameter, leader
06428   //       (dimension definition determined end point of text box)
06429   int position_style = 0;
06430   switch( ann_type )
06431   {
06432   case ON::dtDimAligned:
06433   case ON::dtDimLinear:
06434   case ON::dtDimAngular:
06435     // dimension definition determines center point of text box
06436     position_style = 1;
06437     break;
06438 
06439   case ON::dtLeader:
06440     if(ON::dtHorizontal == dimstyle_textalignment)
06441       position_style = 1;
06442     else
06443       position_style = 2;
06444     break;
06445 
06446   case ON::dtDimRadius:
06447   case ON::dtDimDiameter:
06448   case ON::dtDimOrdinate:
06449     // dimension definition determines end of text box
06450     position_style = 2;
06451     break;
06452 
06453   case ON::dtTextBlock:
06454   case ON::dtNothing:
06455     break;
06456   }
06457 
06458   // This translation puts the center of the fist line of text at
06459   // (0,0) in the annotation's plane.
06460   if ( ON::dtHorizontal != dimstyle_textalignment || 1 == position_style )
06461   {
06462     if((m_justification & tjRight) == tjRight)
06463       gdi_to_plane.m_xform[0][3] = 0.5*text_line_width;
06464     else
06465       gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
06466   }
06467   gdi_to_plane.m_xform[1][3] = -0.5*textheight;
06468 
06469   if ( ON::dtHorizontal != dimstyle_textalignment )
06470   {
06471     if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) )
06472     {
06473       // Viewing dimension from the backside
06474       ON_Xform flip(1.0);
06475       switch ( position_style )
06476       {
06477       case 1: // ON::dtDimLinear, ON::dtDimAligned, ON::dtDimAngular
06478         flip.m_xform[0][0] = -1.0;
06479         flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right;
06480         break;
06481 
06482       case 2: // ON::dtDimDiameter, ON::dtDimRadius, ON::dtLeader
06483             //flip.m_xform[1][1] = -1.0;
06484             //flip.m_xform[1][3] = -(gdi_text_rect.top + gdi_text_rect.bottom);
06485         break;
06486       }
06487       gdi_to_plane = gdi_to_plane*flip;
06488     }
06489   }
06490 
06491   // text_centering_rotation rotates about the "center".  Angular,
06492   // radial, and leader dimensions use this rotation.
06493   ON_2dVector text_centering_rotation(1.0,0.0);
06494 
06495   // text_centering_translation is a small translation deals with
06496   // text that is above or to the right of the "center" point.  
06497   // It is no larger than dimstyle_gap + 1/2 the size of the
06498   // text's bounding box.
06499   ON_2dVector text_centering_translation(0.0,0.0);
06500   bool text_y_flip = false;
06501   double x, y;
06502 
06503   if ( ON::dtHorizontal != dimstyle_textalignment )
06504   {
06505     if ( ON::dtDimLinear  == ann_type || ON::dtDimAligned == ann_type )
06506     {
06507       if ( ON::dtAboveLine == dimstyle_textalignment )
06508       {
06509         text_centering_translation.y =  0.5*textheight+dimstyle_textgap;
06510       }
06511       y =  ann->m_plane.yaxis*cameraY;
06512       x = -ann->m_plane.yaxis*cameraX;
06513       if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
06514       {
06515         y = x;
06516       }
06517       if ( y < 0.0 )
06518       {
06519         text_centering_translation.Reverse();
06520         text_centering_rotation.Reverse(); // rotate 180 degrees
06521       }
06522     }
06523     else if ( ON::dtDimAngular == ann_type )
06524     {
06525       // This transform rotates the text in the annotation plane.
06526       const ON_AngularDimension2* angular_dim = ON_AngularDimension2::Cast(ann);
06527       if ( 0 != angular_dim )
06528       {
06529         double a = 0.5*angular_dim->m_angle;
06530         ON_2dVector R(cos(a),sin(a));
06531         a -= 0.5*ON_PI;
06532         text_centering_rotation.x = cos(a);
06533         text_centering_rotation.y = sin(a);
06534         ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
06535         x = V*cameraX;
06536         y = V*cameraY;
06537         if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
06538         {
06539           y = -x;
06540         }
06541         if ( y < 0.0 )
06542         {
06543           text_centering_rotation.Reverse(); // add another 180 degrees of rotation
06544         }
06545 
06546         if ( ON::dtAboveLine == dimstyle_textalignment )
06547         {
06548           y = 0.5*textheight + dimstyle_textgap;
06549           text_centering_translation.x = -y*text_centering_rotation.y;
06550           text_centering_translation.y =  y*text_centering_rotation.x;
06551         }
06552       }
06553     }
06554     else if (    ON::dtDimDiameter == ann_type 
06555               || ON::dtDimRadius == ann_type 
06556               || ON::dtLeader == ann_type 
06557               || ON::dtDimOrdinate == ann_type)
06558     {
06559       ON_2dPoint E(0.0,0.0); // end point
06560       ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
06561       GetLeaderEndAndDirection( this, E, R );
06562 
06563       text_centering_rotation = R;
06564 
06565       text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation;
06566 
06567       ON_3dVector V = text_centering_rotation.x*m_plane.xaxis + text_centering_rotation.y*m_plane.yaxis;
06568       x = V*cameraX;
06569       y = V*cameraY;
06570       if ( fabs(x) <= ON_SQRT_EPSILON && fabs(y) > ON_SQRT_EPSILON )
06571       {
06572         x = y;
06573       }
06574       if ( x < 0.0 )
06575         text_centering_rotation.Reverse(); // rotate 180 degrees
06576 
06577       if(cameraZ * m_plane.zaxis < 0.0)
06578         text_y_flip = true;
06579     }
06580   }
06581   else if(ann_type == ON::dtLeader)
06582   {
06583     if((m_justification & tjRight) == tjRight)
06584       text_centering_translation.Set(-(dimstyle_textgap + 0.5*text_line_width), 0.0);
06585     else if((m_justification & tjLeft) == tjLeft)
06586       text_centering_translation.Set(dimstyle_textgap + 0.5*text_line_width, 0.0);
06587 
06588   }
06589 
06590   ON_Xform text_centering_xform(1.0);
06591   text_centering_xform.m_xform[0][0] =  text_centering_rotation.x;
06592   text_centering_xform.m_xform[0][1] = -text_centering_rotation.y;
06593   text_centering_xform.m_xform[1][0] =  text_centering_rotation.y;
06594   if(text_y_flip)
06595     text_centering_xform.m_xform[1][1] =  -text_centering_rotation.x;
06596   else
06597     text_centering_xform.m_xform[1][1] =  text_centering_rotation.x;
06598  
06599 
06600   // Since the translation happens after the rotation about (0,0),
06601   // we can just tack it on here.
06602   text_centering_xform.m_xform[0][3] =  text_centering_translation.x;
06603   text_centering_xform.m_xform[1][3] =  text_centering_translation.y;
06604 
06605   // This transform translates the text in the annotation plane
06606   // from the plane origin to the final location of the annotation text
06607   // It can be a large translation
06608   ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset()
06609   switch( ann_type )
06610   {
06611   case ON::dtDimLinear:
06612   case ON::dtDimAligned:
06613     if ( m_points.Count() >= ON_LinearDimension2::dim_pt_count )
06614     {
06615       const ON_LinearDimension2* linear_dim = ON_LinearDimension2::Cast(ann);
06616       if ( linear_dim )
06617       {
06618         text_offset_translation = linear_dim->Dim2dPoint(ON_LinearDimension2::text_pivot_pt);
06619       }
06620     }
06621     break;
06622 
06623   case ON::dtDimAngular:
06624     if ( m_points.Count() >= ON_AngularDimension2::dim_pt_count )
06625     {
06626       const ON_AngularDimension2* angular_dim = ON_AngularDimension2::Cast(ann);
06627       if ( angular_dim )
06628       {
06629         text_offset_translation = angular_dim->Dim2dPoint(ON_AngularDimension2::text_pivot_pt);
06630       }
06631     }
06632     break;
06633 
06634   case ON::dtDimDiameter:
06635   case ON::dtDimRadius:
06636     if ( m_points.Count() >= ON_RadialDimension2::dim_pt_count )
06637     {
06638       // No user positioned text on radial dimensions.
06639       text_offset_translation = m_points[ON_RadialDimension2::tail_pt_index];
06640     }
06641     break;
06642 
06643   case ON::dtLeader:
06644     if ( m_points.Count() > 0 )
06645     {
06646       // No user positioned text on leaders.
06647       text_offset_translation = *m_points.Last();
06648     }
06649     break;
06650 
06651   case ON::dtDimOrdinate:
06652     if ( m_points.Count() == 2 )
06653     {
06654       // No user positioned text on leaders.
06655       text_offset_translation = m_points[1];
06656     }
06657     break;
06658 
06659   case ON::dtTextBlock:
06660   case ON::dtNothing:
06661     break;
06662   }
06663 
06664   ON_Xform plane_translation(1.0);
06665   plane_translation.m_xform[0][3] = text_offset_translation.x;
06666   plane_translation.m_xform[1][3] = text_offset_translation.y;
06667 
06668   // this transform maps a point in the annotation plane to world coordinates
06669   ON_Xform plane_to_world(1.0);
06670   plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
06671 
06672   ON_Xform horizonal_xform(1.0);
06673   if ( ON::dtHorizontal == dimstyle_textalignment )
06674   {
06675     ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y);
06676     horizonal_xform.Rotation( 
06677         fixed_point,
06678         ann->m_plane.xaxis,
06679         ann->m_plane.yaxis,
06680         ann->m_plane.zaxis,
06681         fixed_point,
06682         cameraX,
06683         cameraY,
06684         cameraZ
06685         );
06686 
06687     if ( 2 == position_style )
06688     {
06689       // leaders, radial, and diameter
06690       ON_2dPoint E(0.0,0.0); // end point
06691       ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
06692       GetLeaderEndAndDirection( this, E, R );
06693       ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
06694       x = V*cameraX;
06695       y = ( x > -ON_SQRT_EPSILON )
06696         ? dimstyle_textgap
06697         : -(dimstyle_textgap + text_line_width);
06698       V = y*cameraX;
06699       horizonal_xform.m_xform[0][3] += V.x;
06700       horizonal_xform.m_xform[1][3] += V.y;
06701       horizonal_xform.m_xform[2][3] += V.z;
06702     }
06703   }
06704 
06705   ON_Xform gdi_to_world;
06706 
06707   gdi_to_world = horizonal_xform
06708                * plane_to_world
06709                * plane_translation
06710                * text_centering_xform
06711                * gdi_to_plane;
06712 
06713   xform = gdi_to_world;
06714 
06715   return true;
06716 }
06717 
06718 bool ON_Annotation2::GetTextPoint( ON_2dPoint& text_2d_point ) const
06719 {
06720   bool rc = false;
06721   switch ( m_type )
06722   {
06723   case ON::dtTextBlock:
06724     text_2d_point.Set(0.0,0.0);
06725     rc = true;
06726     break;
06727 
06728   case ON::dtDimLinear:
06729   case ON::dtDimAligned:
06730     if ( m_userpositionedtext )
06731     {
06732       if ( m_points.Count() >= 5 )
06733       {
06734         text_2d_point = m_points[4];
06735         rc = true;
06736       }
06737     }
06738     else if ( m_points.Count() >= 3 )
06739     {
06740       text_2d_point.x = 0.5*(m_points[0].x + m_points[2].x);
06741       text_2d_point.y = m_points[2].y;
06742       rc = true;
06743     }
06744     break;
06745 
06746   case ON::dtLeader:
06747     if ( m_points.Count() > 0 )
06748     {
06749       text_2d_point = *m_points.Last();
06750       rc = true;
06751     }
06752     break;
06753 
06754   case ON::dtDimAngular:
06755     {
06756       const ON_AngularDimension2* angular_dim = ON_AngularDimension2::Cast(this);
06757       if ( angular_dim )
06758       {
06759         if ( m_userpositionedtext )
06760         {
06761           if ( m_points.Count() >= 0 )
06762           {
06763             text_2d_point = m_points[0];
06764           }
06765         }
06766         else
06767         {
06768           text_2d_point.x = angular_dim->m_radius*cos(angular_dim->m_angle);
06769           text_2d_point.y = angular_dim->m_radius*sin(angular_dim->m_angle);
06770           rc = true;
06771         }
06772       }
06773     }
06774     break;
06775 
06776   case ON::dtDimRadius:
06777   case ON::dtDimDiameter:
06778     // no user positioned text
06779     if ( m_points.Count() >= 3 )
06780     {
06781       text_2d_point = m_points[2];
06782       rc = true;
06783     }
06784 
06785     break;
06786 
06787   case ON::dtDimOrdinate:
06788   case ON::dtNothing:
06789     break;
06790   }
06791   return rc;
06792 }
06793 
06794 
06796 //
06797 // do not copy or export this class definition.
06798 //
06799 class /*NEVER PUT THIS CLASS IN THE SDK*/ ON_AnnotationTextFormula : public ON_UserData
06800 {
06801 #if !defined(BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926)
06802 #error Never copy this class definition or put this definition in a header file!
06803 #endif
06804   ON_OBJECT_DECLARE(ON_AnnotationTextFormula);
06805 public:
06806   ON_AnnotationTextFormula();
06807   ~ON_AnnotationTextFormula();
06808   // NO! - do not add IO support to this userdata! // ON_BOOL32 Write(ON_BinaryArchive&) const;
06809   // NO! - do not add IO support to this userdata! // ON_BOOL32 Read(ON_BinaryArchive&);
06810   ON_BOOL32 GetDescription(ON_wString&);
06811   // NO! - do not add IO support to this userdata! // ON_BOOL32 Archive() const; 
06812   static ON_AnnotationTextFormula* Get(const ON_Annotation2*);
06813   static void Set(ON_Annotation2*,const wchar_t* text_formula);
06814 
06815   ON_wString m_text_formula;
06816 };
06817 
06818 #undef BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926
06819 
06820 ON_OBJECT_IMPLEMENT(ON_AnnotationTextFormula,ON_UserData,"699FCC42-62D4-488c-9109-F1B7A37CE926");
06821 
06822 ON_AnnotationTextFormula::~ON_AnnotationTextFormula()
06823 {}
06824 
06825 ON_AnnotationTextFormula::ON_AnnotationTextFormula()
06826 {
06827   m_userdata_uuid = ON_AnnotationTextFormula::m_ON_AnnotationTextFormula_class_id.Uuid();
06828   m_application_uuid = ON_opennurbs5_id;
06829   m_userdata_copycount = 1;
06830 }
06831 
06832 ON_BOOL32 ON_AnnotationTextFormula::GetDescription( ON_wString& description )
06833 {
06834   description = "Annotation Text Formula";
06835   return true;
06836 }
06837 
06838 ON_AnnotationTextFormula* ON_AnnotationTextFormula::Get(const ON_Annotation2* p)
06839 {
06840   return (0 != p)
06841          ? ON_AnnotationTextFormula::Cast(p->GetUserData(ON_AnnotationTextFormula::m_ON_AnnotationTextFormula_class_id.Uuid()))
06842          : 0;
06843 }
06844 
06845 void ON_AnnotationTextFormula::Set(ON_Annotation2* p,const wchar_t* text_formula)
06846 {
06847   if ( 0 != p )
06848   {
06849     ON_AnnotationTextFormula* tf = Get(p);
06850     if ( 0 == text_formula || 0 == text_formula[0] )
06851     {
06852       if (0 != tf )
06853         delete tf; 
06854     }
06855     else
06856     {
06857       if ( 0 == tf )
06858       {
06859         tf = new ON_AnnotationTextFormula();
06860         p->AttachUserData(tf);
06861       }
06862       tf->m_text_formula = text_formula;
06863     }
06864   }
06865 }
06866 //
06867 // do not copy or export this class definition.
06868 //
06870 
06871 void ON_Annotation2::SetTextValue( const wchar_t* text_value )
06872 {
06873   m_usertext = text_value; 
06874 }
06875 
06876 const wchar_t* ON_Annotation2::TextValue() const
06877 {
06878   return ((const wchar_t*)m_usertext);
06879 }
06880 
06881 void ON_Annotation2::SetTextFormula( const wchar_t* text_formula )
06882 {
06883   ON_AnnotationTextFormula::Set(this,text_formula);
06884 }
06885 
06886 const wchar_t* ON_Annotation2::TextFormula() const
06887 {
06888   const ON_AnnotationTextFormula* tf = ON_AnnotationTextFormula::Get(this);
06889   return (0 != tf) ? ((const wchar_t*)tf->m_text_formula) : 0;
06890 }
06891 


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