/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#include "opennurbs.h"

#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined 
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif

// obsolete V2 and V5 annotation objects
#include "opennurbs_internal_V2_annotation.h"
#include "opennurbs_internal_V5_annotation.h"

// This define is up here so anybody who want's to defeat the
// bozo vaccine has to be doing it on purpose.
#define BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926

// Added for v5 - 12-10-2009 LW
ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_TextExtra,ON_UserData,"D90490A5-DB86-49f8-BDA1-9080B1F4E976");

ON_OBSOLETE_V5_TextExtra::ON_OBSOLETE_V5_TextExtra()
{
  m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_TextExtra);
  m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
                                         // The id must be the version 5 id because
                                         // V6 SaveAs V5 needs to work, but SaveAs
                                         // V4 should not write this userdata.
  m_userdata_copycount = 1;
  SetDefaults();
}

ON_OBSOLETE_V5_TextExtra::~ON_OBSOLETE_V5_TextExtra()
{
}

ON_OBSOLETE_V5_TextExtra* ON_OBSOLETE_V5_TextExtra::TextExtension(ON_OBSOLETE_V5_TextObject* pText, bool bCreate)
{
  ON_OBSOLETE_V5_TextExtra* pExtra = 0;
  if(pText)
  {
    pExtra = ON_OBSOLETE_V5_TextExtra::Cast(pText->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_TextExtra)));
    if(pExtra == 0 && bCreate)
    {
      pExtra = new ON_OBSOLETE_V5_TextExtra;
      if(pExtra)
      {
        if(!pText->AttachUserData(pExtra))
        {
          delete pExtra;
          pExtra = 0;
        }
      }
    }
  }
  return pExtra;
}

const
ON_OBSOLETE_V5_TextExtra* ON_OBSOLETE_V5_TextExtra::TextExtension(const ON_OBSOLETE_V5_TextObject* pText, bool bCreate)
{
  return TextExtension((ON_OBSOLETE_V5_TextObject*)pText, bCreate);
}

void ON_OBSOLETE_V5_TextExtra::SetDefaults()
{
  m_parent_uuid = ON_nil_uuid;
  
  m_color_source = 0;
  m_mask_color = 0;
  m_border_offset = 0.1;
}

void ON_OBSOLETE_V5_TextExtra::Dump( ON_TextLog& text_log ) const
{
  // do nothing
}

unsigned int ON_OBSOLETE_V5_TextExtra::SizeOf() const
{
  unsigned int sz = ON_UserData::SizeOf();
  sz += sizeof(*this) - sizeof(ON_UserData);
  return sz;
}

bool ON_OBSOLETE_V5_TextExtra::Write(ON_BinaryArchive& archive) const
{
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);

  if(rc) rc = archive.WriteUuid(m_parent_uuid);
  if(rc) rc = archive.WriteBool(m_bDrawMask);
  if(rc) rc = archive.WriteInt(m_color_source);
  if(rc) rc = archive.WriteColor(m_mask_color);
  if(rc) rc = archive.WriteDouble(m_border_offset);

  if(!archive.EndWrite3dmChunk())
    rc = false;

  return rc;
}

bool ON_OBSOLETE_V5_TextExtra::Read(ON_BinaryArchive& archive)
{
  int major_version = 1;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
  if(!rc)
    return false;
  if(major_version != 1)
    return false;

  if(rc) rc = archive.ReadUuid(m_parent_uuid);
  if(rc) rc = archive.ReadBool(&m_bDrawMask);
  if(rc) rc = archive.ReadInt(&m_color_source);
  if(rc) rc = archive.ReadColor(m_mask_color);
  if(rc) rc = archive.ReadDouble(&m_border_offset);

  if ( !archive.EndRead3dmChunk() )
    rc = false;

  return rc;
}

bool ON_OBSOLETE_V5_TextExtra::GetDescription( ON_wString& description)
{
  description = L"Userdata extension of ON_OBSOLETE_V2_TextObject";
  return true;
}

bool ON_OBSOLETE_V5_TextExtra::Archive() const
{
  // true to write to file
  return true;
}


ON_UUID ON_OBSOLETE_V5_TextExtra::ParentUUID() const
{
  return m_parent_uuid;
}

void ON_OBSOLETE_V5_TextExtra::SetParentUUID( ON_UUID parent_uuid)
{
  m_parent_uuid = parent_uuid;
}

bool ON_OBSOLETE_V5_TextExtra::DrawTextMask() const
{
  return m_bDrawMask;
}

void ON_OBSOLETE_V5_TextExtra::SetDrawTextMask(bool bDraw)
{
  m_bDrawMask = bDraw;
}

int ON_OBSOLETE_V5_TextExtra::MaskColorSource() const
{
  return m_color_source;
}

void ON_OBSOLETE_V5_TextExtra::SetMaskColorSource(int source)
{
  if(source == 1)
    m_color_source = 1;
  else
    m_color_source = 0;
}

ON_Color ON_OBSOLETE_V5_TextExtra::MaskColor() const
{
  return m_mask_color;
}

void ON_OBSOLETE_V5_TextExtra::SetMaskColor(ON_Color color)
{
  m_mask_color = color;
}

double ON_OBSOLETE_V5_TextExtra::MaskOffsetFactor() const
{
  return m_border_offset;
}

void ON_OBSOLETE_V5_TextExtra::SetMaskOffsetFactor(double offset)
{
  m_border_offset = offset;
}

//--------------------



// Added for v5 - 4-20-07 LW
ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_DimExtra,ON_UserData,"8AD5B9FC-0D5C-47fb-ADFD-74C28B6F661E");

ON_OBSOLETE_V5_DimExtra::ON_OBSOLETE_V5_DimExtra()
{
  m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_DimExtra);
  m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
                                         // The id must be the version 5 id because
                                         // V6 SaveAs V5 needs to work, but SaveAs
                                         // V4 should not write this userdata.
  m_userdata_copycount = 1;
  SetDefaults();
}

ON_OBSOLETE_V5_DimExtra::~ON_OBSOLETE_V5_DimExtra()
{
}

static ON_OBSOLETE_V5_DimExtra* AnnotationExtension(ON_OBSOLETE_V5_Annotation* pDim, bool bCreate)
{
  ON_OBSOLETE_V5_DimExtra* pExtra = 0;
  if(pDim)
  {
    pExtra = ON_OBSOLETE_V5_DimExtra::Cast(pDim->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_DimExtra)));
    if(pExtra == 0 && bCreate)
    {
      pExtra = new ON_OBSOLETE_V5_DimExtra;
      if( pExtra)
      {
        if(!pDim->AttachUserData(pExtra))
        {
          delete pExtra;
          pExtra = 0;
        }
      }
    }
  }
  return pExtra;
}

ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate)
{
  return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate);
}

const
ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimLinear* pDim, bool bCreate)
{
  return DimensionExtension((ON_OBSOLETE_V5_DimLinear*)pDim, bCreate);
}

ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate)
{
  return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate);
}

const
ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimRadial* pDim, bool bCreate)
{
  return DimensionExtension((ON_OBSOLETE_V5_DimRadial*)pDim, bCreate);
}

ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate)
{
  return AnnotationExtension((ON_OBSOLETE_V5_Annotation*)pDim, bCreate);
}

const
ON_OBSOLETE_V5_DimExtra* ON_OBSOLETE_V5_DimExtra::DimensionExtension(const ON_OBSOLETE_V5_DimOrdinate* pDim, bool bCreate)
{
  return DimensionExtension((ON_OBSOLETE_V5_DimOrdinate*)pDim, bCreate);
}

void ON_OBSOLETE_V5_DimExtra::SetDefaults()
{
  m_partent_uuid = ON_nil_uuid;
  
  m_arrow_position = 0;
  m_text_rects = 0;
  m_distance_scale = 1.0;
  m_modelspace_basepoint = ON_3dPoint::Origin;
  m_detail_measured = ON_nil_uuid;
}

void ON_OBSOLETE_V5_DimExtra::Dump( ON_TextLog& text_log ) const
{
  // do nothing
}

unsigned int ON_OBSOLETE_V5_DimExtra::SizeOf() const
{
  unsigned int sz = ON_UserData::SizeOf();
  sz += sizeof(*this) - sizeof(ON_UserData);
  return sz;
}

bool ON_OBSOLETE_V5_DimExtra::Write(ON_BinaryArchive& archive) const
{
  int major_version = 1;
  int minor_version = 2;
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);

  if(rc) rc = archive.WriteUuid( m_partent_uuid);
  if(rc) rc = archive.WriteInt( m_arrow_position);
  if(rc)
  {
    if( m_text_rects)
    {
      rc = archive.WriteInt( 7);
      rc = archive.WriteInt( 28, (const int*)m_text_rects);
    }
    else
      rc = archive.WriteInt( 0);

  }
  // 21 June 2010 Added distance scale, minor version 1
  if(rc) rc = archive.WriteDouble(m_distance_scale);

  // 27 Aug, 2014 Added detail measured, minor version 2
  if (rc) rc = archive.WriteUuid(m_detail_measured);

  if(!archive.EndWrite3dmChunk())
    rc = false;

  return rc;
}

bool ON_OBSOLETE_V5_DimExtra::Read(ON_BinaryArchive& archive)
{
  int major_version = 1;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
  if(!rc)
    return false;
  if(major_version < 1)
    return false;

  if(rc) rc = archive.ReadUuid(m_partent_uuid);
  if(rc) rc = archive.ReadInt(&m_arrow_position);

  int rect_count = 0;
  if(rc) rc = archive.ReadInt( &rect_count);
  if( rc && rect_count)
    rc = archive.ReadInt( rect_count, (int*)m_text_rects);

  // 21 June 2010 Added distance scale, minor version 1
  if (minor_version > 0)
  {
    if (rc) rc = archive.ReadDouble(&m_distance_scale);
  }

  // 27 Aug, 2014 Added detail measured, minor version 2
  if (minor_version > 1)
  {
    m_detail_measured = ON_nil_uuid;
    if (rc) rc = archive.ReadUuid(m_detail_measured);
  }

  if (!archive.EndRead3dmChunk())
    rc = false;

  return rc;
}

bool ON_OBSOLETE_V5_DimExtra::GetDescription( ON_wString& description)
{
  description = L"Userdata extension of ON_Dimensions";
  return true;
}

bool ON_OBSOLETE_V5_DimExtra::Archive() const
{
  // true to write to file
  return true;
}


ON_UUID ON_OBSOLETE_V5_DimExtra::ParentUUID() const
{
  return m_partent_uuid;
}

void ON_OBSOLETE_V5_DimExtra::SetParentUUID( ON_UUID partent_uuid)
{
  m_partent_uuid = partent_uuid;
}

int ON_OBSOLETE_V5_DimExtra::ArrowPosition() const
{
  return m_arrow_position;
}

void ON_OBSOLETE_V5_DimExtra::SetArrowPosition( int position)
{
  if( position > 0)
    m_arrow_position = 1;
  else if( position < 0)
    m_arrow_position = -1;
  else
    m_arrow_position = 0;
}

double ON_OBSOLETE_V5_DimExtra::DistanceScale() const
{
  return m_distance_scale;
}

void ON_OBSOLETE_V5_DimExtra::SetDistanceScale(double s)
{
  m_distance_scale = s;
}

void ON_OBSOLETE_V5_DimExtra::SetModelSpaceBasePoint(ON_3dPoint basepoint)
{
  m_modelspace_basepoint = basepoint;
}

ON_3dPoint ON_OBSOLETE_V5_DimExtra::ModelSpaceBasePoint() const
{
  return m_modelspace_basepoint;
}

ON_UUID ON_OBSOLETE_V5_DimExtra::DetailMeasured() const
{
  return m_detail_measured;
}

void ON_OBSOLETE_V5_DimExtra::SetDetailMeasured(ON_UUID detail_id)
{
  m_detail_measured = detail_id;
}


/*
const wchar_t* ON_OBSOLETE_V5_DimExtra::ToleranceUpperString() const
{
  return m_upper_string;
}

ON_wString& ON_OBSOLETE_V5_DimExtra::ToleranceUpperString()
{
  return m_upper_string;
}

void ON_OBSOLETE_V5_DimExtra::SetToleranceUpperString( const wchar_t* upper_string)
{
  m_upper_string = upper_string;
}

void ON_OBSOLETE_V5_DimExtra::SetToleranceUpperString( ON_wString& upper_string)
{
  m_upper_string = upper_string;
}

const wchar_t* ON_OBSOLETE_V5_DimExtra::ToleranceLowerString() const
{
  return m_lower_string;
}

ON_wString& ON_OBSOLETE_V5_DimExtra::ToleranceLowerString()
{
  return m_lower_string;
}

void ON_OBSOLETE_V5_DimExtra::SetToleranceLowerString( const wchar_t* lower_string)
{
  m_lower_string = lower_string;
}

void ON_OBSOLETE_V5_DimExtra::SetToleranceLowerString( ON_wString& lower_string)
{
  m_lower_string = lower_string;
}



const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateString() const
{
  return m_alt_string;
}

ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateString()
{
  return m_alt_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateString( const wchar_t* alt_string)
{
  m_alt_string = alt_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateString( ON_wString& alt_string)
{
  m_alt_string = alt_string;
}

const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateToleranceUpperString() const
{
  return m_alt_upper_string;
}

ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateToleranceUpperString()
{
  return m_alt_upper_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceUpperString( const wchar_t* upper_string)
{
  m_alt_upper_string = upper_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceUpperString( ON_wString& upper_string)
{
  m_alt_upper_string = upper_string;
}

const wchar_t* ON_OBSOLETE_V5_DimExtra::AlternateToleranceLowerString() const
{
  return m_alt_lower_string;
}

ON_wString& ON_OBSOLETE_V5_DimExtra::AlternateToleranceLowerString()
{
  return m_alt_lower_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceLowerString( const wchar_t* lower_string)
{
  m_alt_lower_string = lower_string;
}

void ON_OBSOLETE_V5_DimExtra::SetAlternateToleranceLowerString( ON_wString& lower_string)
{
  m_alt_lower_string = lower_string;
}
*/

//--------------------


ON_VIRTUAL_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_Annotation,       ON_Geometry,    "8D820224-BC6C-46b4-9066-BF39CC13AEFB");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimLinear,  ON_OBSOLETE_V5_Annotation, "BD57F33B-A1B2-46e9-9C6E-AF09D30FFDDE");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimRadial,  ON_OBSOLETE_V5_Annotation, "B2B683FC-7964-4e96-B1F9-9B356A76B08B");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimAngular, ON_OBSOLETE_V5_Annotation, "841BC40B-A971-4a8e-94E5-BBA26D67348E");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_TextObject,       ON_OBSOLETE_V5_Annotation, "46F75541-F46B-48be-AA7E-B353BBE068A7");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_Leader,           ON_OBSOLETE_V5_Annotation, "14922B7A-5B65-4f11-8345-D415A9637129");
ON_OBJECT_IMPLEMENT( ON_TextDot,           ON_Geometry,    "74198302-CDF4-4f95-9609-6D684F22AB37");
ON_OBJECT_IMPLEMENT( ON_OBSOLETE_V5_DimOrdinate,ON_OBSOLETE_V5_Annotation, "C8288D69-5BD8-4f50-9BAF-525A0086B0C3");

// class ON_OBSOLETE_V5_Annotation
//--------------------------------------------------------------------

int ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex() const
{
  return m_v5_3dm_archive_dimstyle_index;
}

void ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex(
  int V5_dim_style_index
)
{
  m_v5_3dm_archive_dimstyle_index = V5_dim_style_index;
}

void ON_OBSOLETE_V5_Annotation::Create()
{
  Destroy();

  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine;
  m_v5_3dm_archive_dimstyle_index = -1;
  m_textheight = 1.0;
}

void ON_OBSOLETE_V5_Annotation::Destroy()
{
  m_v5_3dm_archive_dimstyle_index = -1;

  // 10-27-03 LW memory leak prevention
  m_points.Empty();
  SetTextValue(0);
  SetTextFormula(0);
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing;
  m_plane = ON_xy_plane;
  m_userpositionedtext = false;
  m_justification = 0;
  m_annotative_scale = true;
}

void ON_OBSOLETE_V5_Annotation::EmergencyDestroy()
{
  m_points.EmergencyDestroy();
  m_usertext.EmergencyDestroy();
}

ON_OBSOLETE_V5_Annotation::ON_OBSOLETE_V5_Annotation()
{
  Create();
}

ON_OBSOLETE_V5_Annotation::~ON_OBSOLETE_V5_Annotation()
{
  Destroy();
}


ON_OBSOLETE_V5_Annotation* ON_OBSOLETE_V5_Annotation::CreateFromV2Annotation(
  const class ON_OBSOLETE_V2_Annotation& V2_annotation,
  const class ON_3dmAnnotationContext* annotation_context
)
{
  for (;;)
  {
    const ON_OBSOLETE_V2_Leader* V2_leader = ON_OBSOLETE_V2_Leader::Cast(&V2_annotation);
    if (nullptr == V2_leader && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == V2_annotation.m_type)
      V2_leader = static_cast<const ON_OBSOLETE_V2_Leader*>(&V2_annotation);
    if (nullptr == V2_leader)
      break;
    return ON_OBSOLETE_V5_Leader::CreateFromV2Leader(
      *V2_leader,
      annotation_context,
      nullptr
    );
  }

  for(;;)
  {
    const ON_OBSOLETE_V2_TextObject* V2_text_object = ON_OBSOLETE_V2_TextObject::Cast(&V2_annotation);
    if (nullptr == V2_text_object)
      break;
    return ON_OBSOLETE_V5_TextObject::CreateFromV2TextObject(
      *V2_text_object,
      annotation_context,
      nullptr
    );
  }

  for(;;)
  {
    const ON_OBSOLETE_V2_DimRadial* V2_radial_dimension = ON_OBSOLETE_V2_DimRadial::Cast(&V2_annotation);
    if (nullptr == V2_radial_dimension)
      break;
    return ON_OBSOLETE_V5_DimRadial::CreateFromV2RadialDimension(
      *V2_radial_dimension,
      annotation_context,
      nullptr
    );
  }

  for(;;)
  {
    const ON_OBSOLETE_V2_DimLinear* V2_linear_dimension = ON_OBSOLETE_V2_DimLinear::Cast(&V2_annotation);
    if (nullptr == V2_linear_dimension)
      break;
    return ON_OBSOLETE_V5_DimLinear::CreateFromV2LinearDimension(
      *V2_linear_dimension,
      annotation_context,
      nullptr
    );
  }
  
  for(;;)
  {
    const ON_OBSOLETE_V2_DimAngular* V2_angular_dimension = ON_OBSOLETE_V2_DimAngular::Cast(&V2_annotation);
    if (nullptr == V2_angular_dimension)
      break;
    return ON_OBSOLETE_V5_DimAngular::CreateFromV2AngularDimension(
      *V2_angular_dimension,
      annotation_context,
      nullptr
    );
  }

  return nullptr;
}

////void ON_OBSOLETE_V5_Annotation::Internal_SetDimStyleFromV6Annotation(
////  const class ON_Annotation& V6_annotation,
////  const class ON_3dmAnnotationContext* annotation_context
////)
////{
////  if (nullptr != annotation_context)
////    SetV6_DimStyleId(annotation_context->DimStyleId(), annotation_context->V5_ArchiveDimStyleIndex());
////  else
////    SetV6_DimStyleId(V6_annotation.DimensionStyleId(), ON_UNSET_INT_INDEX);
////}

ON_OBSOLETE_V5_Annotation* ON_OBSOLETE_V5_Annotation::CreateFromV6Annotation(
  const class ON_Annotation& V6_annotation,
  const class ON_3dmAnnotationContext* annotation_context
)
{
  const ON_Text* V6_text_object = ON_Text::Cast(&V6_annotation);
  if (nullptr != V6_text_object)
    return ON_OBSOLETE_V5_TextObject::CreateFromV6TextObject(*V6_text_object, annotation_context, nullptr);

  const ON_Leader* V6_leader = ON_Leader::Cast(&V6_annotation);
  if (nullptr != V6_leader)
    return ON_OBSOLETE_V5_Leader::CreateFromV6Leader(*V6_leader, annotation_context, nullptr);

  const ON_DimRadial* V6_dim_radial = ON_DimRadial::Cast(&V6_annotation);
  if (nullptr != V6_dim_radial)
    return ON_OBSOLETE_V5_DimRadial::CreateFromV6DimRadial(*V6_dim_radial, annotation_context, nullptr);

  const ON_DimLinear* V6_dim_linear = ON_DimLinear::Cast(&V6_annotation);
  if (nullptr != V6_dim_linear)
    return ON_OBSOLETE_V5_DimLinear::CreateFromV6DimLinear(*V6_dim_linear, annotation_context, nullptr);

  const ON_DimAngular* V6_dim_angle = ON_DimAngular::Cast(&V6_annotation);
  if (nullptr != V6_dim_angle)
    return ON_OBSOLETE_V5_DimAngular::CreateFromV6DimAngular(*V6_dim_angle, annotation_context, nullptr);

  const ON_DimOrdinate* V6_dim_ordinate = ON_DimOrdinate::Cast(&V6_annotation);
  if (nullptr != V6_dim_ordinate)
    return ON_OBSOLETE_V5_DimOrdinate::CreateFromV6DimOrdinate(*V6_dim_ordinate, annotation_context, nullptr);

  return nullptr;
}

bool ON_OBSOLETE_V5_Annotation::EvaluatePoint( const ON_ObjRef& objref, ON_3dPoint& P) const
{
  bool rc = false;
  switch( objref.m_component_index.m_type )
  {
  case ON_COMPONENT_INDEX::dim_linear_point:
  case ON_COMPONENT_INDEX::dim_radial_point:
  case ON_COMPONENT_INDEX::dim_angular_point:
  case ON_COMPONENT_INDEX::dim_ordinate_point:
  case ON_COMPONENT_INDEX::dim_text_point:
    {
      ON_2dPoint uv = Point(objref.m_component_index.m_index);
      if ( uv.IsValid() )
      {
        P = m_plane.PointAt(uv.x,uv.y);
        rc = true;
      }
    }
    break;
  default:
    // other enum values skipped on purpose
    break;
  }
  if (!rc)
  {
    P = ON_3dPoint::UnsetPoint;
  }
  return rc;
}

bool ON_OBSOLETE_V5_Annotation::IsValid( ON_TextLog* text_log ) const
{
  if ( !m_plane.IsValid() )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_Annotation - m_plane is not valid\n");
    }
    return false;
  }

  const int points_count = m_points.Count();

  int i;
  for ( i = 0; i < points_count; i++ )
  {
    if ( !m_points[i].IsValid() )
    {
      if ( text_log )
      {
        text_log->Print("ON_OBSOLETE_V5_Annotation - m_points[%d] is not valid.\n");
      }
      return false;
    }
  }

  switch ( m_type )
  {
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
    break;

  default:
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_Annotation - m_type = %d is not a valid enum value\n",m_type);
    }
    return false;
    break;
  }

  return true;
}

static bool WriteAnnotation2UserText_V4( ON_BinaryArchive& file, const ON_wString& s )
{
  bool rc;
  ON_wString s4;
  int len = s.Length();
  
  for(int i = 0; i < len; i++)
  {
    if(s[i] == '\r' || s[i] == '\n')
    {
      s4 += L'\r';
      s4 += L'\n';
      
      // May 24, 2012  Tim - Fix for RR 100260.  If we use a while here we
      // miss adding carriage returns where the user really meant to have a 
      // blank line.  If we only check the next character and then continue on
      // we preserve the blank lines.
      if(i < len-1 && (s[i+1] == L'\r' || s[i+1] == L'\n'))
        i++;
      continue;
    }
    s4 += s[i];
  }
  rc = file.WriteString(s4);
  return rc;
}

static bool WriteAnnotation2UserText_V5( ON_BinaryArchive& file, const ON_wString& s )
{
  bool rc;
  rc = file.WriteString( s);
  return rc;
}

bool ON_OBSOLETE_V5_Annotation::Write( ON_BinaryArchive& file ) const
{
  //int i;
  unsigned int ui;
  bool rc = false;
  bool bInChunk = (file.Archive3dmVersion() >= 5 );
  if ( bInChunk )
  {
    // 18 October 2007 Dale Lear
    //   I modified this code so that V5 files can add
    //   information in ON_OBSOLETE_V5_Annotation chunks without
    //   breaking past and future file IO.
    //   The opennurbs version number before this change was
    //   20071017*.  I changed the version to 20071018* when
    //   I checked in this IO change.  The reason I can get
    //   away with this is that nobody except developers has
    //   a copy of V5 Rhino.
    // 28 Aug, 2010 - Lowell - changed minor version 0->1 to write
    //   annotative scale flag
    // 24 September 2010 Dale Lear 
    //   I incremented chunk version to 1.2 and wrote the TextFormaula() string.
    rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3);
    if (!rc)
      return false;
  }
  else
  {
    // For archives with opennurbs version < 200710180
    // The code before version 200710180 does not properly
    // handle new additions to the ON_OBSOLETE_V5_Annotation chunk.  
    rc = file.Write3dmChunkVersion( 1, 0 );
  }

  while(rc)
  {
    ui = static_cast<unsigned char>(m_type);
    rc = file.WriteInt(ui);
    if ( !rc) break;

    ui = (unsigned int)m_textdisplaymode;    
    rc = file.WriteInt(ui);
    if ( !rc) break;

    // June 17, 2010 - Lowell - Added adjustment to position text
    // a little better in pre-v5 files.
    // There's no adjustment for right/left justify becasue we don't 
    // know the width of the text here
    // This doesn't change the size or position of any fields being
    // written, but just adjusts the plane to tune up the alignment

    // 16 Nov, 2011 - Lowell - Change text to bottom left justified for pre-v5 files rr94270
    // This stuff is moved to CRhinoDoc::Write3DMHelper() so it will help with other file
    // formats too
    //ON_Plane plane = m_plane;
    //if(file.Archive3dmVersion() <= 4 && m_type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock)
    //{
    //  double height = m_textheight;
    //  int lines = CountTextLines(m_usertext);
    //  double linefeed = ON_Font::m_default_linefeed_ratio;

    //  if(m_justification & tjBottom)
    //  {
    //    if(lines > 1)
    //    {
    //      ON_3dPoint p = plane.PointAt(0.0, -height * (lines-1) * linefeed);
    //      plane.SetOrigin(p);
    //    }
    //  }
    //  else if(m_justification & tjMiddle)
    //  {
    //    double h = height * (lines-1) * linefeed + height;
    //    ON_3dPoint p = plane.PointAt(0.0, h * 0.5);
    //    plane.SetOrigin(p);
    //  }
    //  else if(m_justification & tjTop)
    //  {
    //    ON_3dPoint p = plane.PointAt(0.0, -height);
    //    plane.SetOrigin(p);
    //  }
    //}
  
    rc = file.WritePlane(m_plane);
    if ( !rc) break;

    ON_2dPointArray points = m_points;
    int bUserPositionedText = m_userpositionedtext?1:0;
    switch( m_type )
    {
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
      if ( 4 == points.Count() )
      {
        // so old versions will read enough points.
        points.AppendNew();
        points[4].Set(0.5*(points[0].x + points[2].x),points[1].y);
        bUserPositionedText = false;
      }
      break;

    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
      //user positioned text is supported.
      break;

    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
      // 9 August 2005 Dale Lear - radial dimensions do
      // not support user postioned text.  The never have
      // in Rhino, but the old files had 5 points in them.
      if ( 4 == points.Count() )
      {
        // so old versions will read enough points.
        points.AppendNew();
      }
      if ( points.Count() >= 5 )
      {
        points[4] = points[2];
      }
      bUserPositionedText = false;
      break;

    default:
      bUserPositionedText = false;
      break;
    }
    
    rc = file.WriteArray( points);
    if ( !rc) break;
    
    // June 17, 2010 - Lowell - Added support for writing word-wrapped text
    // to pre-v5 files with hard returns in place of wrapping markers
    rc = ( file.Archive3dmVersion() <= 4 )
       ? WriteAnnotation2UserText_V4(file,m_usertext)
       : WriteAnnotation2UserText_V5(file,m_usertext);
    if ( !rc) break;
    // 7-9-03 lw removed extra text string getting written
    
    rc = file.WriteInt( bUserPositionedText );
    if ( !rc) break;
    

    const bool bIsText = IsText();
    //rc = file.Write3dmReferencedComponentIndex(
    //  bIsText ? ON_ModelComponent::Type::TextStyle : ON_ModelComponent::Type::DimStyle, m_dimstyle_index );
    rc = file.WriteInt(m_v5_3dm_archive_dimstyle_index);
    if ( !rc) break;

    rc = file.WriteDouble( m_textheight);
    if ( !rc) break;

    if ( !bInChunk )
      break;

    // NOTE WELL - NEVER change the code in this function
    //             above this comment.  If you do, you will
    //             break reading and writing V4 files.
    //             Ask Dale Lear if you have any questions.

    // 18 October 2007 - Dale Lear added m_justification IO
    rc = file.WriteInt(m_justification);
    if ( !rc) 
      break;

    // 28 Aug 2010 - Lowell - Added flag for whether text gets scaled in modelspace
    rc = file.WriteBool(m_annotative_scale);
    if(!rc)
      break;

    // 24 September 2010 Dale Lear 
    //   I incremented chunk version to 1.2
    ON_wString text_formula = TextFormula();
    rc = file.WriteString(text_formula);
    if(!rc)
      break;

    // To write more ON_OBSOLETE_V5_Annotation fields, increment the minor version
    // number and write the new information here.

    // September 2015 Dale Lear
    //   I incremented chunk version to 1.3
    //   and separated text style and dim style
    //rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::TextStyle, bIsText ? m_dimstyle_index : -1 );
    //if ( !rc) break;
    //rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::DimStyle, bIsText ? -1 : m_dimstyle_index );
    //if ( !rc) break;
    rc = file.WriteInt( bIsText ? m_v5_3dm_archive_dimstyle_index : -1 );
    if ( !rc) break;
    rc = file.WriteInt( bIsText ? -1 : m_v5_3dm_archive_dimstyle_index );
    if ( !rc) break;

    // Finished writing ON_OBSOLETE_V5_Annotation information
    break;
  }

  if ( bInChunk )
  {
    if ( !file.EndWrite3dmChunk() )
      rc = false;
  }

  return rc;
}

bool ON_OBSOLETE_V5_Annotation::Read( ON_BinaryArchive& file )
{
  // NOTE WELL - If you make any changes to this code,
  //             you break file IO for some annotation
  //             objects.  Please discuss all changes
  //             with Dale Lear BEFORE you check in any
  //             changes.

  Destroy();
  
  // If annotation is read from old files that do not contain
  // the m_annotative_scale setting, then m_annotative_scale 
  // must be false so the text behaves the way it did in old
  // files.  The "Destroy()" function above can set m_annotative_scale
  // any way that makes sense for new objects.
  m_annotative_scale = false;
  
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;

  bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180);

  if ( bInChunk )
  {
    rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = file.Read3dmChunkVersion(&major_version,&minor_version);
  }

  bool bIsText = false;
  int dim_style_index = ON_UNSET_INT_INDEX;
  int dim_style_index0 = ON_UNSET_INT_INDEX;
  int dim_style_index1 = ON_UNSET_INT_INDEX;
  int dim_style_index2 = ON_UNSET_INT_INDEX;
  while(rc)
  {
    if ( 1 != major_version )
    {
      rc = false;
      break;
    }

    unsigned int ui;
    rc = file.ReadInt(&ui);
    if (!rc) break;
    m_type = ON_INTERNAL_OBSOLETE::V5AnnotationTypeFromUnsigned(ui);

    rc = file.ReadInt(&ui);
    if (!rc) break;
    m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode(ui);

    rc = file.ReadPlane( m_plane);
    if (!rc) break;

    rc = file.ReadArray( m_points);
    if (!rc) break;
    
    rc = file.ReadString( m_usertext);
    if (!rc) break;
    
    ui = 0;
    rc = file.ReadInt( &ui );
    if (!rc) break;
    m_userpositionedtext = ui ? true : false;

    // Initially, a single integer was used. It was either a text style index or a dim style index.
    // In August 2016, text styles were removed and font information is saved on ON_DimStyle.
    // When Read3dmReferencedComponentIndex() is called with the first parameter = ON_ModelComponent::Type::TextStyle,
    // it returns the value to use as a dimstyle index.
    bIsText = IsText();
    rc = file.Read3dmReferencedComponentIndex(
      bIsText ? ON_ModelComponent::Type::TextStyle : ON_ModelComponent::Type::DimStyle,
      &dim_style_index0
    );
    if (!rc) break;
    dim_style_index = dim_style_index0;
    
    rc = file.ReadDouble( &m_textheight);
    if (!rc) break;

    switch( m_type )
    {
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
      if ( m_points.Count() < 5 )
      {
        m_userpositionedtext = false;
      }
      break;

    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
      if ( m_points.Count() <= 0 )
      {
        m_userpositionedtext = false;
      }
      break;

    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
    case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
      // 9 August 2005 Dale Lear - radial dimensions do
      // not support user postioned text.  The never have
      // in Rhino, but the old files had 5 points in them.
      if ( 5 == m_points.Count() )
      {
        m_points.SetCount(4);
      }
      m_userpositionedtext = false;
      break;

    default:
      m_userpositionedtext = false;
      break;
    }

    if ( !bInChunk )
      break;

    // 18 October 2007 - Dale Lear added m_justification IO
    rc = file.ReadInt( &m_justification );
    if (!rc) break;

    if ( minor_version <= 0 )
      break;

    if(minor_version >= 1)
    {
      // 28 Aug, 2010 - Lowell - added reading annotative scale flag
      rc = file.ReadBool(&m_annotative_scale);
      if (!rc) break;

      if ( minor_version >= 2 )
      {
        // 24 September 2010 Dale Lear 
        //   I incremented chunk version to 1.2
        ON_wString text_formula;
        rc = file.ReadString(text_formula);
        if(!rc) break;
        SetTextFormula(static_cast< const wchar_t* >(text_formula));

        if (minor_version >= 3)
        {
          // September 2015 Dale Lear
          //   I incremented chunk version to 1.3
          //   and separated text style and dim style
          // In August 2016, text styles were removed and font information is saved on ON_DimStyle.
          // When Read3dmReferencedComponentIndex() is called with the first parameter = ON_ModelComponent::Type::TextStyle,
          // it returns the value to use as a dimstyle index.
          rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle, &dim_style_index1);
          if (!rc) break;
          rc = file.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::DimStyle, &dim_style_index2);
          if (!rc) break;
          dim_style_index = ON_UNSET_INT_INDEX;
        }
      }
    }

    // Read new additions to ON_OBSOLETE_V5_Annotation here

    break;
  }

  if ( bInChunk )
  {
    if (!file.EndRead3dmChunk() )
      rc = false;
  }

  //if (!bInChunk)
  {
    // If justification is 0, change it to top-left
    // and move the text point up by text height
    if (bIsText && 0 == m_justification)
    {
      m_justification = eTextJustification::tjTopLeft;
      m_plane.origin = m_plane.PointAt(0.0, m_textheight);
    }
  }

  if (ON_UNSET_INT_INDEX == dim_style_index)
  {
    if (bIsText)
    {
      if (dim_style_index0 > ON_UNSET_INT_INDEX && dim_style_index0 == dim_style_index1)
        dim_style_index = dim_style_index1;
      else
      {
        ON_ERROR("Unexpected text object dim style index.");
        if (dim_style_index1 >= 0)
          dim_style_index = dim_style_index1;
        else if (dim_style_index0 >= 0)
          dim_style_index = dim_style_index0;
        else if (dim_style_index2 >= 0)
          dim_style_index = dim_style_index2;
      }
    }
    else
    {
      // not text 
      if (dim_style_index0 > ON_UNSET_INT_INDEX && dim_style_index0 == dim_style_index2)
        dim_style_index = dim_style_index2;
      else
      {
        ON_ERROR("Unexpected dimension object dim style index.");
        if (dim_style_index2 >= 0)
          dim_style_index = dim_style_index2;
        else if (dim_style_index0 >= 0)
          dim_style_index = dim_style_index0;
        else if (dim_style_index1 >= 0)
          dim_style_index = dim_style_index1;
      }
    }
  }

  if (ON_UNSET_INT_INDEX != dim_style_index)
    this->SetV5_3dmArchiveDimStyleIndex( dim_style_index );

  return rc;
}

ON::object_type ON_OBSOLETE_V5_Annotation::ObjectType() const
{
  return ON::annotation_object;
}

int ON_OBSOLETE_V5_Annotation::Dimension() const
{
  return 3;
}

bool ON_OBSOLETE_V5_Annotation::Transform( const ON_Xform& xform )
{
  ON_Geometry::Transform(xform);
  const double tol = 1.0e-4;
  ON_3dVector vx = Plane().xaxis;
  ON_3dVector vy = Plane().yaxis;
  vx.Transform(xform);
  vy.Transform(xform);
  double sx = vx.Length();
  double sy = vy.Length();
  if ((fabs(sx - 1.0) > tol && fabs(sx) > tol) ||
      (fabs(sy - 1.0) > tol && fabs(sy) > tol))
  {
    ON_Xform xfscale(ON_Xform::DiagonalTransformation(sx, sy, 1.0));

    ON_2dPoint p;
    for (int i = 0; i < m_points.Count(); i++)
    {
      p = Point(i);
      p.Transform(xfscale);
      SetPoint(i, p);
    }
  }
  // This scales the text height on text but not on dimensions
  if (ON_OBSOLETE_V5_Annotation::IsText())
  {
    SetHeight(sy * Height());
  }

  return m_plane.Transform(xform);
}

int ON_Plane_Repair(ON_Plane& plane)
{
  int rc;
  if ( plane.IsValid() )
  {
    rc = 1;
  }
  else
  {
    rc = 2;
    if (!plane.origin.IsValid())
    {
      plane.origin.Set(0.0,0.0,0.0);
    }

    bool bGoodX = (plane.xaxis.IsValid() && !plane.xaxis.IsZero() );
    bool bGoodY = (plane.yaxis.IsValid() && !plane.yaxis.IsZero() );
    bool bGoodZ = (plane.zaxis.IsValid() && !plane.zaxis.IsZero() );
    if ( bGoodX )
    {
      if ( fabs(plane.xaxis.Length()-1.0) > ON_SQRT_EPSILON )
        plane.xaxis.Unitize();
    }
    if ( bGoodY )
    {
      if ( fabs(plane.yaxis.Length()-1.0) > ON_SQRT_EPSILON )
        plane.yaxis.Unitize();
    }
    if ( bGoodZ )
    {
      if ( fabs(plane.zaxis.Length()-1.0) > ON_SQRT_EPSILON )
        plane.zaxis.Unitize();
    }

    if ( bGoodZ )
    {
      double x = bGoodX ? fabs(plane.zaxis*plane.xaxis) : 99.0;
      double y = bGoodX ? fabs(plane.zaxis*plane.yaxis) : 99.0;
      if ( x <= ON_SQRT_EPSILON )
      {
        if ( y > ON_SQRT_EPSILON )
        {
          plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
          plane.yaxis.Unitize();
        }
      }
      else if ( y <= ON_SQRT_EPSILON )
      {
        plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
        plane.xaxis.Unitize();
      }
      else if ( x <= y && x < 1.0 )
      {
        plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
        if( plane.yaxis.Unitize() )
        {
          plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
          plane.xaxis.Unitize();
        }
        else if ( y < 1.0 )
        {
          plane.CreateFromNormal( plane.origin, plane.zaxis );
        }
      }
      else if ( y < 1.0 )
      {
        plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
        if( plane.xaxis.Unitize() )
        {
          plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
          plane.yaxis.Unitize();
        }
        else
        {
          plane.CreateFromNormal( plane.origin, plane.zaxis );
        }
      }
    }
    else if ( bGoodX )
    {
      if ( bGoodY )
      {
        plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
        if ( plane.zaxis.Unitize() )
        {
          if ( fabs(plane.yaxis*plane.xaxis) > ON_SQRT_EPSILON )
          {
            plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
            plane.yaxis.Unitize();
          }
        }
        else
        {
          plane.yaxis.PerpendicularTo(plane.xaxis);
          plane.yaxis.Unitize();
          plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
          plane.zaxis.Unitize();
        }
      }
      else
      {
        plane.yaxis.PerpendicularTo(plane.xaxis);
        plane.yaxis.Unitize();
        plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
        plane.zaxis.Unitize();
      }
    }
    else if ( bGoodY )
    {
      plane.zaxis.PerpendicularTo(plane.yaxis);
      plane.zaxis.Unitize();
      plane.xaxis = ON_CrossProduct(plane.yaxis,plane.zaxis);
      plane.xaxis.Unitize();
    }
    else
    {
      plane.xaxis.Set(1.0,0.0,0.0);
      plane.yaxis.Set(0.0,1.0,0.0);
      plane.zaxis.Set(0.0,0.0,1.0);
    }
    plane.UpdateEquation();
  }
  return rc;
}

// overrides virtual ON_Geometry::Transform()
bool ON_OBSOLETE_V5_DimRadial::Transform( const ON_Xform& xform )
{
  // TODO fill in something that works for non-rigid transforms
  return ON_OBSOLETE_V5_Annotation::Transform(xform);
}

bool ON_OBSOLETE_V5_Leader::Transform( const ON_Xform& xform )
{
  bool rc = xform.IsIdentity();
  if ( !rc)
  {
    ON_Plane plane = m_plane;
    rc = plane.Transform(xform);
    if ( rc )
    {
      int i;
      const int point_count =  m_points.Count();
      ON_2dPointArray q(point_count);
      ON_2dPoint p2, q2;
      ON_3dPoint P, Q;
      bool bUpdatePoints = false;
      for ( i = 0; i < point_count && rc; i++ )
      {
        p2 = m_points[i];
        P = m_plane.PointAt( p2.x, p2.y );
        Q = xform*P;
        if( !plane.ClosestPointTo(Q,&q2.x,&q2.y) )
          rc = false;
        if ( fabs(p2.x - q2.x) <= ON_SQRT_EPSILON )
          q2.x = p2.x;
        else
          bUpdatePoints = true;
        if ( fabs(p2.y - q2.y) <= ON_SQRT_EPSILON )
          q2.y = p2.y;
        else
          bUpdatePoints = true;
        q.Append(q2);
      }

      if(rc)
      {
        ON_Geometry::Transform(xform);
        m_plane = plane;

        if ( bUpdatePoints )
          m_points = q;

        if ( m_points[0].x != 0.0 || m_points[0].y != 0.0 )
        {
          ON_2dVector v = m_points[0];
          if ( !v.IsZero() )
          {
            m_plane.origin = m_plane.PointAt(v.x,v.y);
            m_plane.UpdateEquation();
            v = -v;
            for ( i = 1; i < point_count; i++ )
            {
              m_points[i] += v;
            }
            m_points[0].Set(0.0,0.0);
          }
        }
      }
    }
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimAngular::Transform( const ON_Xform& xform )
{
  // Dale Lear - this override fixes RR 11114 by correctly
  //             handling non uniform scaling.
  bool rc = xform.IsIdentity();
  if ( !rc)
  {
    ON_Plane plane = m_plane;
    if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) )
    {
      rc = true;
      ON_3dPoint P[dim_pt_count], Q[dim_pt_count], A[3], B[3];
      ON_2dVector p2[dim_pt_count], q2[dim_pt_count], a[3], b[3];
      double r[3];
      int i;
      bool bUpdatePoints = false;
      double a0 = 0.0;
      double a1 = m_angle;
      a[0].Set( m_radius*cos(a0), m_radius*sin(a0) );
      a[1].Set( m_radius*cos(0.5*(a0+a1)), m_radius*sin(0.5*(a0+a1)) );
      a[2].Set( m_radius*cos(a1), m_radius*sin(a1) );
      for ( i = 0; i < dim_pt_count && rc; i++ )
      {
        p2[i] = m_points[i];
        P[i] = m_plane.PointAt( p2[i].x, p2[i].y );
        Q[i] = xform*P[i];
        if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) )
          rc = false;
        if (   fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON 
            || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON )
        {
          // transformation is not a rigid motion
          bUpdatePoints = true;
        }
      }

      for ( i = 0; i < 3 && rc; i++ )
      {
        A[i] = m_plane.PointAt( a[i].x, a[i].y );
        B[i] = xform*A[i];
        if( !plane.ClosestPointTo(B[i],&b[i].x,&b[i].y) )
          rc = false;
        r[i] = B[i].DistanceTo(plane.origin);
        if (   fabs(a[i].x - b[i].x) > ON_SQRT_EPSILON 
            || fabs(a[i].y - b[i].y) > ON_SQRT_EPSILON )
        {
          // transformation is not a rigid motion
          bUpdatePoints = true;
        }
        if ( r[i] < ON_SQRT_EPSILON )
          rc = false;
        else if ( r[i] > m_radius*(1.0+ON_SQRT_EPSILON) )
          bUpdatePoints = true;
        else if ( r[i] < m_radius*(1.0-ON_SQRT_EPSILON) )
          bUpdatePoints = true;
      }

      if (rc)
      {
        if ( bUpdatePoints )
        {
          ON_3dVector X = B[0] - plane.origin;
          X.Unitize();

          ON_3dVector Y1 = B[1] - plane.origin;
          Y1.Unitize();
          ON_3dVector Z1 = ON_CrossProduct(X,Y1);
          double z1 = Z1.Length();
          Z1.Unitize();

          ON_3dVector Y2 = B[2] - plane.origin;
          Y2.Unitize();
          ON_3dVector Z2 = ON_CrossProduct(X,Y2);
          double z2 = Z2.Length();
          Z2.Unitize();

          if ( z2 >= z1 && z2 >= 0.00001 ) // 4 April 2014 - Lowell - At 0.05, this was too big a tolerance
          {                               // when the dimension had previously been scaled by 0.001 etc.  Rh-25926
            plane.xaxis = X;
            plane.zaxis = Z2;
            if (Z1*Z2 < 0.0)
              plane.zaxis = -plane.zaxis;
            plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
            plane.yaxis.Unitize();
          }
          else if ( z1 >= 0.00001 )
          {
            plane.xaxis = X;
            plane.zaxis = Z1;
            plane.yaxis = ON_CrossProduct(plane.zaxis,plane.xaxis);
            plane.yaxis.Unitize();
          }
          else
          {
            rc = false;
          }

          if (rc)
          {
            plane.UpdateEquation();
            ON_3dVector V = B[2] - plane.origin;
            double x = V*plane.xaxis;
            double y = V*plane.yaxis;
            double angle = atan2(y,x);
            if ( angle < 0.0 )
              angle += 2.0*ON_PI;
            double radius = (r[0]+r[1]+r[2])/3.0;

            double arc_pt_angle = 1.0/3.0;
            if ( m_angle > 0.0 && m_points[arc_pt_index].IsValid() )
            {
              arc_pt_angle = atan2(m_points[arc_pt_index].y,m_points[arc_pt_index].x);
              if ( arc_pt_angle < 0.0 ) arc_pt_angle += 2.0*ON_PI;
              if ( arc_pt_angle > m_angle )
                arc_pt_angle = 1.0/3.0;
              else
                arc_pt_angle /= m_angle;

              if ( arc_pt_angle < 0.0 ) 
                arc_pt_angle = 0.0; 
              else if ( arc_pt_angle > 1.0 ) 
                arc_pt_angle = 1.0;
            }
            arc_pt_angle *= angle;


            ON_Geometry::Transform(xform);
            m_plane = plane;
            m_radius = radius;
            m_angle = angle;
            m_points[start_pt_index].Set(m_radius,0.0);
            m_points[end_pt_index].Set(m_radius*cos(m_angle),m_radius*sin(m_angle));
            m_points[arc_pt_index].Set(m_radius*cos(arc_pt_angle),m_radius*sin(arc_pt_angle));
            if ( m_userpositionedtext )
            {
              m_plane.ClosestPointTo(Q[userpositionedtext_pt_index],
                &m_points[userpositionedtext_pt_index].x,
                &m_points[userpositionedtext_pt_index].y
                );
            }
            else
            {
              m_points[userpositionedtext_pt_index].Set(m_radius*cos(0.5*m_angle),m_radius*sin(0.5*m_angle));
            }

          }
        }
        else
        {
          ON_Geometry::Transform(xform);
          m_plane = plane;
        }
      }
    }
  }

  return rc;
}

bool ON_OBSOLETE_V5_Annotation::IsText() const 
{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == m_type); }

bool ON_OBSOLETE_V5_Annotation::IsLeader() const 
{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == m_type); }

bool ON_OBSOLETE_V5_Annotation::IsDimension() const 
{ return (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock != m_type && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader != m_type); }

double ON_OBSOLETE_V5_Annotation::NumericValue() const
{ return ON_UNSET_VALUE; }

void ON_OBSOLETE_V5_Annotation::SetHeight( double ht) 
{ if( ht > ON_SQRT_EPSILON) m_textheight = ht; }

double ON_OBSOLETE_V5_Annotation::Height() const 
{ return m_textheight; }

void ON_OBSOLETE_V5_Annotation::SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType type ) 
{ 
  m_type = type;
  if(type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius)
    SetTextValue(ON_OBSOLETE_V5_DimRadial::DefaultRadiusText());
  else if(type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter)
    SetTextValue(ON_OBSOLETE_V5_DimRadial::DefaultDiameterText());
  else
    SetTextValue(0);

  SetTextFormula(0);
}

ON_INTERNAL_OBSOLETE::V5_eAnnotationType ON_OBSOLETE_V5_Annotation::Type() const 
{ return m_type; }

void ON_OBSOLETE_V5_Annotation::SetPlane( const ON_Plane& plane ) 
{ m_plane = plane; }

const ON_Plane& ON_OBSOLETE_V5_Annotation::Plane() const 
{ return m_plane; }

void ON_OBSOLETE_V5_Annotation::SetPointCount( int count) 
{
  if( m_points.Count() < count)
  {
    m_points.Reserve( count);
    for( int i = m_points.Count(); i < count; i++)
      m_points.Append( ON_2dPoint());
  }
}

int ON_OBSOLETE_V5_Annotation::PointCount() const 
{ return m_points.Count(); }

void ON_OBSOLETE_V5_Annotation::SetPoints( const ON_2dPointArray& points ) 
{ m_points = points; }

const ON_2dPointArray& ON_OBSOLETE_V5_Annotation::Points() const 
{ return m_points; }

void ON_OBSOLETE_V5_Annotation::SetPoint( int idx, const ON_2dPoint& point )
{
  if ( idx >= 0 )
  {
    if ( idx < m_points.Count() )
      m_points[idx] = point;
    else if ( idx == m_points.Count() )
      m_points.Append(point);
  }
}

ON_2dPoint ON_OBSOLETE_V5_Annotation::Point( int idx ) const
{
  return ( idx >= 0 && idx < m_points.Count() ) 
         ? m_points[idx] 
         : ON_2dPoint( 0.0, 0.0 );
}

void ON_OBSOLETE_V5_Annotation::SetUserText( const wchar_t* text_value )
// ON_OBSOLETE_V5_Annotation::SetUserText is OBSOLETE - use ON_OBSOLETE_V5_Annotation::SetTextValue();
{
  SetTextValue( text_value );
}

const ON_wString& ON_OBSOLETE_V5_Annotation::UserText() const 
// ON_OBSOLETE_V5_Annotation::UserText() is OBSOLETE - use ON_OBSOLETE_V5_Annotation::TextValue();
{
  return m_usertext; 
}

void ON_OBSOLETE_V5_Annotation::SetUserPositionedText( int bMoved ) 
{ m_userpositionedtext = bMoved?true:false; }
bool ON_OBSOLETE_V5_Annotation::UserPositionedText() const 
{ return m_userpositionedtext; }


// Converts 2d points in annotation to 3d WCS points
bool ON_OBSOLETE_V5_Annotation::GetECStoWCSXform( ON_Xform& xform ) const
{
  ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis );
  return xform.ChangeBasis( m_plane.origin, m_plane.xaxis, m_plane.yaxis, z,
                            ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis );
}

// Converts from WCS 3d points to 2d points in annotation
bool ON_OBSOLETE_V5_Annotation::GetWCStoECSXform( ON_Xform& xform ) const
{
  ON_3dVector z = ON_CrossProduct( m_plane.xaxis, m_plane.yaxis );
  return xform.ChangeBasis( ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis, ON_3dVector::ZAxis,
                            m_plane.origin, m_plane.xaxis, m_plane.yaxis, z );
}

void ON_OBSOLETE_V5_Annotation::ReservePoints( int count)
{
  m_points.SetCapacity( count);
  m_points.SetCount( count);
}

const wchar_t* ON_OBSOLETE_V5_Annotation::DefaultText() { return L""; }


void ON_OBSOLETE_V5_Annotation::SetTextDisplayMode( ON_INTERNAL_OBSOLETE::V5_TextDisplayMode mode)
{
  m_textdisplaymode = mode;
}

ON_INTERNAL_OBSOLETE::V5_TextDisplayMode ON_OBSOLETE_V5_Annotation::TextDisplayMode() const
{
  return m_textdisplaymode;
}

void ON_OBSOLETE_V2_Annotation::Internal_InitializeFromV5Annotation( 
  const ON_OBSOLETE_V5_Annotation& V5_annotation,
  const class ON_3dmAnnotationContext* annotation_context
) 
{
  SetType( V5_annotation.Type());
  SetTextDisplayMode( V5_annotation.TextDisplayMode());
  SetPlane( V5_annotation.Plane());
  SetPoints( V5_annotation.Points());
  SetUserText( V5_annotation.TextValue());
  SetDefaultText( V5_annotation.DefaultText());
  SetUserPositionedText( V5_annotation.UserPositionedText());
}

void ON_OBSOLETE_V5_Annotation::SetJustification( unsigned int justification)
{
  if(this->IsLeader())
    m_justification = justification;
}

unsigned int ON_OBSOLETE_V5_Annotation::Justification() const
{
  if(this->IsLeader())
    return   m_justification;
  else
    return 0;
}


//----- ON_OBSOLETE_V5_DimLinear ------------------------------------------
ON_OBSOLETE_V5_DimLinear::ON_OBSOLETE_V5_DimLinear()
{
  //ON_OBSOLETE_V5_DimExtra* pDE = new ON_OBSOLETE_V5_DimExtra;
  //if( pDE)
  //{
  //  if( !AttachUserData( pDE))
  //    delete pDE;
  //  else
  //    pDE->SetDefaults();
  //}

  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear;
  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine;
  m_plane = ON_xy_plane;
  SetTextValue(DefaultText());
  SetTextFormula(0);
  m_points.Reserve(ON_OBSOLETE_V5_DimLinear::dim_pt_count);
  m_points.SetCount(ON_OBSOLETE_V5_DimLinear::dim_pt_count);
  m_points.Zero();
}

ON_OBSOLETE_V5_DimLinear::~ON_OBSOLETE_V5_DimLinear()
{
}

bool ON_OBSOLETE_V5_DimLinear::IsValid( ON_TextLog* text_log ) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear && m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear or ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned.\n");
    }
    return false;
  }

  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( m_points.Count() != 5 )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points.Count() = %d (should be 5).\n",m_points.Count());
    }
    return false;
  }

  if ( m_points[1].x != m_points[0].x )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[1].x = %g != %g = m_points[0].x (should be equal)\n",
                      m_points[1].x, m_points[0].x
                      );
    }
    return false;
  }

  if ( m_points[3].x != m_points[2].x )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[3].x = %g != %g = m_points[2].x\n",
                      m_points[3].x, m_points[2].x
                     );
    }
    return false;
  }

  if ( m_points[3].y != m_points[1].y )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimLinear - m_points[3].y = %g != %g = m_points[1].y\n",
                      m_points[3].y, m_points[1].y
                      );
    }
    return false;
  }

  return true;
}

bool ON_OBSOLETE_V5_DimLinear::Write(ON_BinaryArchive& archive) const
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimLinear
  //    V4 did not have a ON_OBSOLETE_V5_DimLinear::Write and simply called
  //    ON_OBSOLETE_V5_Annotation::Write.
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5);
  if ( bInChunk )
  {
    rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk )
      break;

    // To write new fields, increment minor version number
    // and write values here.  Ask Dale Lear for help.

    break;
  }

  if ( bInChunk )
  {
    if (!archive.EndWrite3dmChunk())
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimLinear::Read(ON_BinaryArchive& archive)
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimLinear
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
  if ( bInChunk )
  {
    rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk || minor_version <= 0 )
      break;

    // read future addition here

    break;
  }

  if ( bInChunk )
  {
    if ( !archive.EndRead3dmChunk() )
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimLinear::GetBBox(
        double* boxmin,
        double* boxmax,
        bool bGrowBox
        ) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  if ( 5 == m_points.Count() )
  {
    ON_3dPointArray P(5);
    ON_2dPoint uv;
    if ( m_userpositionedtext )
    {
      uv = m_points[0]; // point someplace in text
      P.Append( m_plane.PointAt(uv.x,uv.y) );
    }

    P.Append( m_plane.origin );

    uv.x = 0.0;
    uv.y = m_points[1].y;
    P.Append( m_plane.PointAt(uv.x,uv.y) );

    uv = m_points[2];
    P.Append( m_plane.PointAt(uv.x,uv.y) );

    uv.y = m_points[1].y;
    P.Append( m_plane.PointAt(uv.x,uv.y) );
    bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}




bool ON_OBSOLETE_V5_DimLinear::GetTightBoundingBox( 
		ON_BoundingBox& tight_bbox, 
    bool bGrowBox,
		const ON_Xform* xform
    ) const
{
  if ( 5 == m_points.Count() )
  {
    ON_3dPointArray P(5);
    // 18 Oct 2012 - Lowell - Fixed this to add all of the points to the bbox rr116270
    for(int i = 0; i < 5; i++)
      P.Append( m_plane.PointAt(m_points[i].x,m_points[i].y) );

    if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
      bGrowBox = true;
  }
  else if ( bGrowBox && !tight_bbox.IsValid() )
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return (0!=bGrowBox);
}

int ON_OBSOLETE_V5_DimLinear::Repair()
{
  // returns 0 = unable to repair
  //         1 = in perfect condtion
  //         2 == repaired.

  const int ext0_pt_index   = ON_OBSOLETE_V5_DimLinear::ext0_pt_index;
  const int arrow0_pt_index = ON_OBSOLETE_V5_DimLinear::arrow0_pt_index;
  const int ext1_pt_index   = ON_OBSOLETE_V5_DimLinear::ext1_pt_index;
  const int arrow1_pt_index = ON_OBSOLETE_V5_DimLinear::arrow1_pt_index;
  const int userpositionedtext_pt_index = ON_OBSOLETE_V5_DimLinear::userpositionedtext_pt_index;
  const int dim_pt_count    = ON_OBSOLETE_V5_DimLinear::dim_pt_count;

  int rc = 0;
  if (    m_points.Count() >= dim_pt_count 
       && m_points[ext0_pt_index].IsValid() 
       && m_points[ext1_pt_index].IsValid() )
  {
    rc = 1;
    if ( !m_plane.IsValid() )
    {
      rc = ON_Plane_Repair(m_plane);
    }

    if ( m_points.Count() > dim_pt_count )
    {
      rc = 2;
      m_points.SetCount(dim_pt_count);
    }

    // m_points[ext0_pt_index] must be at (0,0)
    ON_2dVector v = m_points[ext0_pt_index];
    double d;
    int i;
    if ( !v.IsZero() )
    {
      rc = 2;
      m_plane.origin = m_plane.PointAt(v.x,v.y);
      m_plane.UpdateEquation();
      v = -v;
      for ( i = 0; i < dim_pt_count; i++ )
      {
        m_points[i] += v;
      }
      m_points[ext0_pt_index].Set(0.0,0.0);
    }

    if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == m_type && (m_points[ext1_pt_index].x < 0.0 || m_points[ext1_pt_index].y != 0.0) )
    {
      rc = 2;
      // Aligned dims must have m_points[ext1_pt_index].x = m_points[ext0_pt_index].x
      if (   m_points[ext1_pt_index].x > 100.0*ON_SQRT_EPSILON
          && fabs(m_points[ext1_pt_index].y) <= ON_SQRT_EPSILON )
      {
        m_points[ext1_pt_index].y = 0.0;
      }
      else
      {
        // Dimension line parallel to point0 -> point2
        // rotate the plane so p2 is on the x axis
        v = m_points[ext1_pt_index];
        d = v.Length();
        v.Unitize();
        m_plane.Rotate(v.y,v.x,m_plane.zaxis,m_plane.origin);

        // rotate points in opposite direction
        v.y = -v.y; 
        for ( i = 0; i < dim_pt_count; i++ )
        {
          ON_2dPoint p = m_points[i];
          m_points[i].Set( v.x*p.x - v.y*p.y, v.y*p.x + v.x*p.y );
        }
        m_points[ext0_pt_index].Set(0.0,0.0);
        m_points[ext1_pt_index].Set(d,0.0);
      }
    }
    else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear != m_type )
    {
      rc = 2;
      m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear;
    }

    if ( m_points[arrow0_pt_index].x != m_points[ext0_pt_index].x )
    {
      rc = 2;
      m_points[arrow0_pt_index].x = m_points[ext0_pt_index].x;
    }

    if ( m_points[arrow1_pt_index].x != m_points[ext1_pt_index].x )
    {
      rc = 2;
      m_points[arrow1_pt_index].x = m_points[ext1_pt_index].x;
    }

    if ( !ON_IsValid(m_points[arrow0_pt_index].y) )
    {
      rc = 2;
      if ( !ON_IsValid(m_points[arrow1_pt_index].y) )
        m_points[arrow1_pt_index].y = 0.5*(m_points[ext0_pt_index].y + m_points[ext1_pt_index].y);
      m_points[arrow0_pt_index].y = m_points[arrow1_pt_index].y;
    }
    else if ( !ON_IsValid(m_points[arrow1_pt_index].y) )
    {
      rc = 2;
      m_points[arrow1_pt_index].y = m_points[arrow0_pt_index].y;
    }
    else if ( m_points[arrow0_pt_index].y != m_points[arrow1_pt_index].y )
    {
      rc = 2;
      d = 0.5*(m_points[arrow0_pt_index].y + m_points[arrow1_pt_index].y);
      m_points[arrow0_pt_index].y = d;
      m_points[arrow1_pt_index].y = d;
    }

    if ( m_userpositionedtext )
    {
      if ( !m_points[userpositionedtext_pt_index].IsValid() )
      {
        rc = 2;
        m_userpositionedtext = false;
      }
    }
    
    if ( !m_userpositionedtext )
    {
      if (    m_points[userpositionedtext_pt_index].y != m_points[arrow0_pt_index].y
           || m_points[userpositionedtext_pt_index].x != 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x) )
      {
        rc = 2;
        m_points[userpositionedtext_pt_index].y = m_points[arrow0_pt_index].y;
        m_points[userpositionedtext_pt_index].x = 0.5*(m_points[arrow0_pt_index].x + m_points[arrow1_pt_index].x);
      }
    }

    if ( !m_plane.IsValid() )
    {
      rc = 2;
      ON_Plane_Repair(m_plane);
    }
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimLinear::Transform( const ON_Xform& xform )
{
  // Dale Lear - this override fixes RR 11114 by correctly
  //             handling non uniform scaling.
  bool rc = xform.IsIdentity();
  if ( !rc)
  {
    ON_Plane plane = m_plane;
    if ( dim_pt_count == m_points.Count() && plane.Transform( xform ) )
    {
      rc = true;
      ON_3dPoint P[dim_pt_count], Q[dim_pt_count];
      ON_2dVector p2[dim_pt_count], q2[dim_pt_count];
      int i;
      bool bUpdatePoints = false;
      for ( i = 0; i < dim_pt_count && rc; i++ )
      {
        p2[i] = m_points[i];
        P[i] = m_plane.PointAt( p2[i].x, p2[i].y );
        Q[i] = xform*P[i];
        if( !plane.ClosestPointTo(Q[i],&q2[i].x,&q2[i].y) )
          rc = false;
        if (   fabs(p2[i].x - q2[i].x) > ON_SQRT_EPSILON 
            || fabs(p2[i].y - q2[i].y) > ON_SQRT_EPSILON )
        {
          // transformation is not in SL3
          bUpdatePoints = true;
        }
      }

      if (rc)
      {
        ON_Geometry::Transform(xform);
        m_plane = plane;
        if ( bUpdatePoints )
        {
          for ( i = 0; i < dim_pt_count && rc; i++ )
          {
            m_points[i] = q2[i];
          }
          // Repair() will properly align the arrow points, etc.
          Repair();
        }
      }
    }
  }

  return rc;
}

double ON_OBSOLETE_V5_DimLinear::NumericValue() const
{
  // Use y coords of ext points instead of 3d distance
  // to reduce noise in the answer.
  return (m_points.Count() >= dim_pt_count) 
         ? fabs(m_points[ext0_pt_index].x - m_points[ext1_pt_index].x) 
         : 0.0;
}

int ON_OBSOLETE_V5_DimLinear::StyleIndex() const
{
  return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex();
}

void ON_OBSOLETE_V5_DimLinear::SetStyleIndex( int i)
{
  ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i);
}

/*

    ext0_pt_index    = 0, // end of first extension line
    arrow0_pt_index  = 1, // arrowhead tip on first extension line
    ext1_pt_index    = 2, // end of second extension line
    arrow1_pt_index  = 3, // arrowhead tip on second extension line
    dim_pt_count     = 5, // number of m_points[] in an angular dim

    // Points calculated from values in m_points[]
    text_pivot_pt = 10000 // center of dimension text
    dim_mid_pt    = 10001 // midpoint of dimension line

*/

ON_2dPoint ON_OBSOLETE_V5_DimLinear::Dim2dPoint( int point_index ) const
{
  ON_2dPoint p2;
  if ( m_points.Count() < dim_pt_count )
  {
    p2.x = p2.y = ON_UNSET_VALUE;
  }
  else
  {
    if ( text_pivot_pt == point_index )
    {
      point_index = m_userpositionedtext ? userpositionedtext_pt_index : dim_mid_pt;
    }

    const ON_2dPoint* points = m_points.Array();
    switch(point_index)
    {
    case ext0_pt_index:
      p2 = points[0];
      break;

    case arrow0_pt_index:
      p2.x = points[0].x;
      p2.y = points[1].y;
      break;

    case ext1_pt_index:
      p2 = points[2];
      break;

    case arrow1_pt_index:
      p2.x = points[2].x;
      p2.y = points[1].y;
      break;

    case userpositionedtext_pt_index:
      p2.x = points[4].x;
      p2.y = points[4].y;
      break;

    case dim_mid_pt:
      p2.x = 0.5*(points[0].x + points[2].x);
      p2.y = points[1].y;
      break;

    default:
      p2.x = p2.y = ON_UNSET_VALUE;
      break;
    }
  }
  return p2;
}

ON_3dPoint ON_OBSOLETE_V5_DimLinear::Dim3dPoint( int point_index ) const
{
  ON_2dPoint p2 = Dim2dPoint(point_index);
  return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y);
}

ON_OBSOLETE_V2_TextObject* ON_OBSOLETE_V2_TextObject::CreateFromV5TextObject(
  const class ON_OBSOLETE_V5_TextObject& V5_text_object,
  const ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V2_TextObject* destination
)
{
  if (nullptr == annotation_context)
    annotation_context = &ON_3dmAnnotationContext::Default;
  const ON_DimStyle& dim_style = annotation_context->DimStyle();

  ON_OBSOLETE_V2_TextObject* V2_text_object
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V2_TextObject();
  V2_text_object->Internal_InitializeFromV5Annotation(V5_text_object,annotation_context);

  V2_text_object->m_facename = dim_style.Font().WindowsLogfontName();

  const double V5_text_height = V5_text_object.Height();
  V2_text_object->m_fontweight = 400;
  V2_text_object->m_height = V5_text_height;

  if (V2_text_object->m_plane.IsValid())
  {
    // 8-20-03 lw convert from lower-left to upper-left reference point
    V2_text_object->m_plane.origin += V2_text_object->m_plane.yaxis * 1.1 * V5_text_height;
    V2_text_object->m_plane.UpdateEquation();
  }
  
  return V2_text_object;
}

ON_OBSOLETE_V2_DimAngular* ON_OBSOLETE_V2_DimAngular::CreateFromV5AngularDimension(
  const class ON_OBSOLETE_V5_DimAngular& V5_angular_dimension,
  const ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V2_DimAngular* destination
)
{
  ON_OBSOLETE_V2_DimAngular* V2_angular_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V2_DimAngular();
  V2_angular_dimension->Internal_InitializeFromV5Annotation(V5_angular_dimension,annotation_context);
  ON_SimpleArray<ON_2dPoint> V5_points(V5_angular_dimension.m_points);
  V2_angular_dimension->m_points.SetCount(0);
  V2_angular_dimension->SetPoint(0, ON_3dPoint(V5_angular_dimension.Point(1)));
  V2_angular_dimension->SetPoint(1, ON_3dPoint(V5_angular_dimension.Point(2)));
  V2_angular_dimension->SetPoint(2, ON_3dPoint(V5_angular_dimension.Point(3)));
  V2_angular_dimension->SetPoint(3, ON_3dPoint(V5_angular_dimension.Point(0))); // text point or apex
  V2_angular_dimension->SetAngle( V5_angular_dimension.Angle());
  V2_angular_dimension->SetRadius( V5_angular_dimension.Radius());

  return V2_angular_dimension;
}

ON_OBSOLETE_V2_DimRadial* ON_OBSOLETE_V2_DimRadial::CreateFromV5RadialDimension(
  const class ON_OBSOLETE_V5_DimRadial& V5_linear_dimension,
  const ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V2_DimRadial* destination
)
{
  ON_OBSOLETE_V2_DimRadial* V2_radial_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V2_DimRadial();
  V2_radial_dimension->Internal_InitializeFromV5Annotation(V5_linear_dimension,annotation_context);
  return V2_radial_dimension;
}

ON_OBSOLETE_V2_DimLinear* ON_OBSOLETE_V2_DimLinear::CreateFromV5LinearDimension(
  const class ON_OBSOLETE_V5_DimLinear& V5_linear_dimension,
  const ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V2_DimLinear* destination
)
{
  ON_OBSOLETE_V2_DimLinear* V2_linear_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V2_DimLinear();
  V2_linear_dimension->Internal_InitializeFromV5Annotation(V5_linear_dimension,annotation_context);
  return V2_linear_dimension;
}

ON_OBSOLETE_V5_DimLinear* ON_OBSOLETE_V5_DimLinear::CreateFromV2LinearDimension( 
  const class ON_OBSOLETE_V2_DimLinear& V2_linear_dimension,
  const ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V5_DimLinear* destination
  )
{
  ON_OBSOLETE_V5_DimLinear* V5_linear_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V5_DimLinear();

  V5_linear_dimension->Internal_InitializeFromV2Annotation(V2_linear_dimension,annotation_context);

  return V5_linear_dimension;
}

int ON_OBSOLETE_V5_DimLinear::GetDimensionLineSegments(
    ON_OBSOLETE_V5_RECT gdi_text_rect,
    int gdi_height_of_I,
    ON_Xform gdi_to_world,
    const ON_DimStyle& dimstyle,
    double dimscale,
    const ON_Viewport* vp,
    double x[6],
    bool& bInside
    ) const
{
  int rc = 0;

  x[0] = 0.0;
  x[1] = 0.0;
  x[2] = 0.0;
  x[3] = 0.0;
  x[4] = 0.0;
  x[5] = 0.0;
  bInside = true;

  if ( m_points.Count() < 3 )
    return 0;

  int i = (m_points[ext0_pt_index].x <= m_points[ext1_pt_index].x) 
      ? ext0_pt_index 
      : ext1_pt_index;
  const double x0 = m_points[i].x;
  const double x1 = m_points[(ext0_pt_index==i) ? ext1_pt_index : ext0_pt_index].x;
  x[0] = x0;  // left  end of first dimension line
  x[1] = x1;  // right end of first dimension line
  x[2] = x0;  // left  end of second dimension line
  x[3] = x1;  // right end of second dimension line
  x[4] = x0;  // tip of first arrow
  x[5] = x1;  // tip of second arrow

  if ( 0 == gdi_height_of_I )
  {
    // Default to height of Ariel 'I'
    gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256;
  }

  if ( 0.0 == dimscale )
  {
    dimscale = 1.0;
  }

  double t;

  ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textdisplay = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(dimstyle);

  const double text_height_of_I    = dimscale*dimstyle.TextHeight();
  const double textgap             = fabs(dimscale*dimstyle.TextGap());
  const double gdi_to_plane_scale  = text_height_of_I/gdi_height_of_I;
  const double textwidth           = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left));
  const double arrowwidth          = dimscale*dimstyle.ArrowSize();
  const double tailwidth           = 0.5*arrowwidth;
  const double dimwidth            = x1 - x0;
  const double mindimwidth         = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay)
                                     ? (2.0*(arrowwidth + tailwidth + textgap) + textwidth)
                                   : (2.0*arrowwidth + tailwidth);
  const double dimextension        = dimscale*dimstyle.DimExtension();

  int ForceArrows = 0;
  const ON_OBSOLETE_V5_DimExtra* pDE = ON_OBSOLETE_V5_DimExtra::DimensionExtension(this,false);
  if( pDE)
    ForceArrows = pDE->ArrowPosition();

  // 19 Apr 2012 - Lowell - Fixed so forcing arrows inside won't cause the dim line to 
  // draw through InLine text  rr103322
  if(ForceArrows == -1 ||        // force outside
     (dimwidth < mindimwidth  && ForceArrows != 1))    // arrowheads have to be "outside" - they won't fit inside even without text
  {
    t = arrowwidth + tailwidth;
    x[0] = x0;
    x[1] = x0-t;
    x[2] = x1+t;
    x[3] = x1;
    x[4] = x0;  // arrow tips
    x[5] = x1;
    if( dimextension != 0.0)
    {
      x[0] += dimextension;
      x[3] -= dimextension;
    }
    bInside = false;
    rc = 2;
  }
  // Horizontal text or Userpositioned text
  else if ((ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) || m_userpositionedtext)
  {
    // use projected rectangle to clip dimension line
    double xx0, xx1, xx, y0, y1;
    ON_3dPoint P, R;
    ON_2dPoint corners[4];  // corners of text rect in plane coords
    ON_Line ray;

    ON_3dVector vp_zaxis = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp)
                         ? vp->CameraZ() 
                         : m_plane.zaxis;

    // 30 July 2012 - Lowell - Slightly shrink the text gap value
    // so that text + gap won't intersect the dim line if when text
    // is moved along the dim line. rr110504
    double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12;
    if(gdi_gap < 0.0) gdi_gap = 0.0;

    R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y);

    R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y);

    R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y);

    R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y);

    // Test if text rect intersects dimension line
    xx0 = xx1 = ON_UNSET_VALUE;
    for ( i = 0; i < 4; i++ )
    {
      y0 = corners[i].y       - m_points[1].y;    // vertical dist from corner to dimension line
      y1 = corners[(i+1)%4].y - m_points[1].y;    // vertical dist from next corner to dimension line
      // if they're both above or both below, no intersection of this segment
      if ( (y0 > 0.0 && y1 > 0.0) || (y0 < 0.0 && y1 < 0.0) || y0 == y1 )
        continue;

      // segment intersects dimension line.
      // find x-coord of intersection
      t = y0/(y0-y1);
      xx = (1.0-t)*corners[i].x + t*corners[(i+1)%4].x;
      if ( ON_UNSET_VALUE == xx0 )
      {
        xx0 = xx1 = xx;
      }
      else if ( xx < xx0 )
      {
        xx0 = xx;
      }
      else if ( xx > xx1 )
      {
        xx1 = xx;
      }
    }

    // Nov 4 2009 - Lowell - Added test for no intersection of text with dimension line rr33003
    // Important for user positioned text that's moved out of the way of the dimension line
    if(xx0 != ON_UNSET_VALUE && xx1 != ON_UNSET_VALUE)
    {
      // xx0 is left edge of text rect
      // xx1 is right edge of text rect
      t = arrowwidth + tailwidth;
      if ( x0 + t <= xx0 && xx0 < xx1 && xx1 <= x1 - t )
      {
        // clip line, 2 segments arrows inside
        x[0] = x0;
        x[1] = xx0;
        x[2] = xx1;
        x[3] = x1;
        x[4] = x0;
        x[5] = x1;
        if( dimextension != 0.0)
        {
          x[0] -= dimextension;
          x[3] += dimextension;
        }
        rc = 2;
      }
      else  // 2 segments, put the arrows outside the extension lines
      {
        x[0] = x0;
        x[1] = x0 - t;
        x[2] = x1 + t;
        x[3] = x1;
        x[4] = x0;
        x[5] = x1;
        if( dimextension != 0.0)
        {
          x[0] += dimextension;
          x[3] -= dimextension;
        }
        bInside = false;
        rc = 2;
      }
    }
    else
    {
      // 1 segment, arrows inside
      x[0] = x0;
      x[1] = x1;
      x[2] = x0;
      x[3] = x1;
      x[4] = x0;
      x[5] = x1;
      if( dimextension != 0.0)
      {
        x[0] -= dimextension;
        x[1] += dimextension;
      }
      rc = 1;
    }
  }
  // Above line text
  else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == textdisplay || m_userpositionedtext)
  {
    // 1 segment, arrows inside
    x[0] = x0;
    x[1] = x1;
    x[2] = x0;
    x[3] = x1;
    x[4] = x0;
    x[5] = x1;
    if( dimextension != 0.0)
    {
      x[0] -= dimextension;
      x[1] += dimextension;
    }
    rc = 1;
  }
  // In line text
  else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay)
  {
    // 2 segments, arrows inside
    t = 0.5*(dimwidth - textwidth - 2.0*textgap);
    x[0] = x0;
    x[1] = x0+t;
    x[2] = x1-t;
    x[3] = x1;
    x[4] = x0;
    x[5] = x1;
    if( dimextension != 0.0)
    {
      x[0] -= dimextension;
      x[3] += dimextension;
    }

    rc = 2;
  }

  return rc;
}


const wchar_t* ON_OBSOLETE_V5_DimLinear::DefaultText() { return L"<>"; }




//----- ON_OBSOLETE_V5_DimRadial ------------------------------------------
ON_OBSOLETE_V5_DimRadial::ON_OBSOLETE_V5_DimRadial()
{
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter;
  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine;
  SetTextValue(DefaultDiameterText());
  SetTextFormula(0);
  m_points.Reserve(ON_OBSOLETE_V5_DimRadial::dim_pt_count);
  m_points.SetCount(ON_OBSOLETE_V5_DimRadial::dim_pt_count);
  m_points.Zero();
}

ON_OBSOLETE_V5_DimRadial* ON_OBSOLETE_V5_DimRadial::CreateFromV2RadialDimension(
  const class ON_OBSOLETE_V2_DimRadial& V2_radial_dimension,
  const class ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V5_DimRadial* destination
)
{
  ON_OBSOLETE_V5_DimRadial* V5_radial_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V5_DimRadial();

  V5_radial_dimension->Internal_InitializeFromV2Annotation(V2_radial_dimension,annotation_context);

  return V5_radial_dimension;
}


bool ON_OBSOLETE_V5_DimRadial::IsValid( ON_TextLog* text_log ) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius && m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter  )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimRadial - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius or ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter\n");
    }
    return false;
  }

  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimRadial - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( 4 != m_points.Count() )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimRadial - m_points.Count() = %d (should be 4 or 5)\n", m_points.Count() );
    }
    return false;
  }

  return true;
}


bool ON_OBSOLETE_V5_DimRadial::Write(ON_BinaryArchive& archive) const
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimRadial
  //    V4 did not have a ON_OBSOLETE_V5_DimRadial::Write and simply called
  //    ON_OBSOLETE_V5_Annotation::Write.
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5);
  if ( bInChunk )
  {
    rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk )
      break;

    // To write new fields, increment minor version number
    // and write values here.  Ask Dale Lear for help.

    break;
  }

  if ( bInChunk )
  {
    if (!archive.EndWrite3dmChunk())
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimRadial::Read(ON_BinaryArchive& archive)
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimRadial
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
  if ( bInChunk )
  {
    rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk || minor_version <= 0 )
      break;

    // read future addition here

    break;
  }

  if ( bInChunk )
  {
    if ( !archive.EndRead3dmChunk() )
      rc = false;
  }
  return rc;
}


bool ON_OBSOLETE_V5_DimRadial::GetBBox(
        double* boxmin,
        double* boxmax,
        bool bGrowBox
        ) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  if ( 4 == m_points.Count() )
  {
    ON_3dPointArray P(4);
    ON_2dPoint uv;
    if ( m_userpositionedtext )
    {
      uv = m_points[0]; // point someplace in text
      P.Append( m_plane.PointAt(uv.x,uv.y) );
    }

    P.Append( m_plane.origin ); // + sign at center of dimension

    uv = m_points[1];
    P.Append( m_plane.PointAt(uv.x,uv.y) );

    uv = m_points[2];
    P.Append( m_plane.PointAt(uv.x,uv.y) );

    uv = m_points[3];
    P.Append( m_plane.PointAt(uv.x,uv.y) );
    bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}

bool ON_OBSOLETE_V5_DimRadial::GetTightBoundingBox( 
		ON_BoundingBox& tight_bbox, 
    bool bGrowBox,
		const ON_Xform* xform
    ) const
{
  if ( 4 == m_points.Count() )
  {
    ON_3dPointArray P(4);
    for(int i = 0; i < 4; i++)
      P.Append( m_plane.PointAt(m_points[i].x,m_points[i].y) );

    if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
      bGrowBox = true;
  }
  else if ( bGrowBox && !tight_bbox.IsValid() )
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return (0!=bGrowBox);
}

ON_2dPoint ON_OBSOLETE_V5_DimRadial::Dim2dPoint(
      int point_index
      ) const
{
  ON_2dPoint p2;
  if ( m_points.Count() < dim_pt_count || point_index < 0 )
  {
    p2.x = p2.y = ON_UNSET_VALUE;
  }
  else
  {
    if ( text_pivot_pt == point_index )
    {
      point_index = tail_pt_index;
    }
    if ( point_index < dim_pt_count )
    {
      p2 = m_points[point_index];
    }    
    else
    {
      p2.x = p2.y = ON_UNSET_VALUE;
    }
  }
  return p2;
}

ON_3dPoint ON_OBSOLETE_V5_DimRadial::Dim3dPoint(
      int point_index
      ) const
{
  ON_2dPoint p2 = Dim2dPoint(point_index);
  return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y);
}

// set the plane, center, and point on curve.
// the rest will be set by the Rhino dimension
bool ON_OBSOLETE_V5_DimRadial::CreateFromPoints( 
  ON_3dPoint center, 
  ON_3dPoint arrowtip, 
  ON_3dVector xaxis, 
  ON_3dVector normal,
  double offset_distance)
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter )
    m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius;
  bool rc = false;
  if ( center.IsValid() && arrowtip.IsValid() && normal.IsValid() && !normal.IsZero() && xaxis.IsValid() && !xaxis.IsZero() )
  {
    ON_Plane plane( center, normal);
    double c = xaxis*plane.xaxis;
    double s = xaxis*plane.yaxis;
    if ( c != 0.0 || s != 0.0 )
    {
      if ( c <= 0.0 || s != 0.0 )
      {
        plane.Rotate( s, c, plane.zaxis );
      }
      m_plane = plane;
      ON_2dVector tip;
      if ( m_plane.ClosestPointTo(arrowtip,&tip.x,&tip.y) )
      {
        //double r = tip.Length();
        m_points.SetCapacity(dim_pt_count);
        m_points.SetCount(dim_pt_count);

        m_points[center_pt_index].Set(0.0,0.0);
        m_points[arrow_pt_index] = tip;
        tip.Unitize();

        m_points[knee_pt_index] = m_points[arrow_pt_index] + offset_distance*tip;
        m_points[tail_pt_index] = m_points[knee_pt_index];
        if ( m_points[arrow_pt_index].x < 0.0 )
          m_points[tail_pt_index].x -= offset_distance;
        else
          m_points[tail_pt_index].x += offset_distance;
        m_plane = plane;
        m_userpositionedtext = false;

        rc = true;
      }
    }
  }
  return rc;
}


double ON_OBSOLETE_V5_DimRadial::NumericValue() const
{
  double d = 0.0;
  if ( m_points.Count() >= dim_pt_count )
  {
    d = (m_points[center_pt_index] - m_points[arrow_pt_index]).Length();
    if( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == m_type )
      d *= 2.0;
  }
  return d;
}

int ON_OBSOLETE_V5_DimRadial::StyleIndex() const
{
  return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex();
}

void ON_OBSOLETE_V5_DimRadial::SetStyleIndex( int i)
{
  ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i);
}


const wchar_t* ON_OBSOLETE_V5_DimRadial::DefaultRadiusText()
{
  static wchar_t defstr[4] = { ON_wString::RadiusSymbol, L'<', L'>', 0 };
  return defstr;
}

const wchar_t* ON_OBSOLETE_V5_DimRadial::DefaultDiameterText()
{
  static wchar_t defstr[4] = { ON_wString::DiameterSymbol, L'<', L'>', 0 };
  return defstr;
}


bool ON_OBSOLETE_V5_DimRadial::CreateFromV2( 
    const ON_OBSOLETE_V2_Annotation& v2_ann,
    const ON_3dmAnnotationSettings& settings,
    int dimstyle_index
    )
{
  bool rc = false;
  if( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == v2_ann.m_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == v2_ann.m_type )
  {
    const ON_SimpleArray<ON_2dPoint>& points = v2_ann.Points();
    if ( points.Count() >= 4 )
    {
      m_points.Reserve(4);
      m_points.SetCount(0);
      m_points.Append(4,points.Array());
      m_plane = v2_ann.m_plane;
      m_plane.UpdateEquation();
      SetTextValue(static_cast< const wchar_t* >(v2_ann.UserText()));
      SetTextFormula(0);
      m_userpositionedtext = false;
      m_type = v2_ann.Type();
      m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine;
      SetV5_3dmArchiveDimStyleIndex(dimstyle_index);
      ON_2dVector v = m_points[0];
      if ( !v.IsZero() )
      {
        m_plane.origin = m_plane.PointAt(v.x,v.y);
        m_plane.UpdateEquation();
        v = -v;
        m_points[0].Set(0.0,0.0);
        m_points[1] += v;
        m_points[2] += v;
        m_points[3] += v;
      }

      rc = true;
    }
  }
  return rc;
}


//----- ON_AngularDimension2Extra -----------------------------------------
// Additions to ON_OBSOLETE_V5_DimAngular class

class ON_AngularDimension2Extra : public ON_UserData
{
  ON_OBJECT_DECLARE(ON_AngularDimension2Extra);
public:
  static ON_AngularDimension2Extra* AngularDimensionExtra(ON_OBSOLETE_V5_DimAngular* pDim/*, bool bCreate*/);
  static const ON_AngularDimension2Extra* AngularDimensionExtra(const ON_OBSOLETE_V5_DimAngular* pDim/*, bool bCreate*/);

  ON_AngularDimension2Extra();
  ~ON_AngularDimension2Extra();

  // override virtual ON_Object::Dump function
  void Dump( ON_TextLog& text_log ) const override;

  // override virtual ON_Object::SizeOf function
  unsigned int SizeOf() const override;

  // override virtual ON_Object::Write function
  bool Write(ON_BinaryArchive& binary_archive) const override;

  // override virtual ON_Object::Read function
  bool Read(ON_BinaryArchive& binary_archive) override;

  // override virtual ON_UserData::GetDescription function
  bool GetDescription( ON_wString& description ) override;

  // override virtual ON_UserData::Archive function
  bool Archive() const override;

  // Scale all of the length values
  void Scale( double scale);

  // 
  double DimpointOffset(int index) const;
  void SetDimpointOffset(int index, double offset);

  // offsets from apex of dimension to point from which extension lines start
  // if these are < 0.0, they are ignored
  // Extension lines are drawn from theses points to the Arrow tip points
  // subject to dimexe & dimexo & dimse1 & dimse2
  double m_dimpoint_offset[2];
};

ON_OBJECT_IMPLEMENT(ON_AngularDimension2Extra,ON_UserData,"A68B151F-C778-4a6e-BCB4-23DDD1835677");

ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(ON_OBSOLETE_V5_DimAngular* pDim)
{
  ON_AngularDimension2Extra* pExtra = 0;
  if(pDim)
  {
    pExtra = ON_AngularDimension2Extra::Cast(pDim->GetUserData(ON_CLASS_ID(ON_AngularDimension2Extra)));
    if(pExtra == 0)
    {
      pExtra = new ON_AngularDimension2Extra;
      if( pExtra)
      {
        if(!pDim->AttachUserData(pExtra))
        {
          delete pExtra;
          pExtra = 0;
        }
      }
    }
  }
  return pExtra;
}

const ON_AngularDimension2Extra* ON_AngularDimension2Extra::AngularDimensionExtra(const ON_OBSOLETE_V5_DimAngular* pDim)
{
  return AngularDimensionExtra((ON_OBSOLETE_V5_DimAngular*)pDim);
}

ON_AngularDimension2Extra::ON_AngularDimension2Extra()
{
  m_userdata_uuid = ON_CLASS_ID(ON_AngularDimension2Extra);
  m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata
                                         // The id must be the version 5 id because
                                         // V6 SaveAs V5 needs to work, but SaveAs
                                         // V4 should not write this userdata.
  m_userdata_copycount = 1;

  m_dimpoint_offset[0] = 0;
  m_dimpoint_offset[1] = 0;
}

ON_AngularDimension2Extra::~ON_AngularDimension2Extra()
{
}

void ON_AngularDimension2Extra::Dump( ON_TextLog& text_log ) const
{
  // do nothing
}

unsigned int ON_AngularDimension2Extra::SizeOf() const
{
  unsigned int sz = ON_UserData::SizeOf();
  sz += sizeof(*this) - sizeof(ON_UserData);
  return sz;
}

bool ON_AngularDimension2Extra::Write(ON_BinaryArchive& archive) const
{
  int major_version = 1;
  int minor_version = 0;
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);

  if(rc) rc = archive.WriteDouble(m_dimpoint_offset[0]);
  if(rc) rc = archive.WriteDouble(m_dimpoint_offset[1]);

  if(!archive.EndWrite3dmChunk())
    rc = false;

  return rc;
}

bool ON_AngularDimension2Extra::Read(ON_BinaryArchive& archive)
{
  int major_version = 1;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
  if(major_version != 1)
    rc = false;

  if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[0]);
  if(rc) rc = archive.ReadDouble(&m_dimpoint_offset[1]);

  if ( !archive.EndRead3dmChunk() )
    rc = false;

  return rc;
}

bool ON_AngularDimension2Extra::GetDescription( ON_wString& description)
{
  description = L"Userdata extension of ON_OBSOLETE_V5_DimAngular";
  return true;
}

bool ON_AngularDimension2Extra::Archive() const
{
  // true to write to file
  return true;
}

void ON_AngularDimension2Extra::Scale(double scale)
{
  if( ON_IsValid(scale) && scale > ON_SQRT_EPSILON)
  {
    m_dimpoint_offset[0] *= scale;
    m_dimpoint_offset[1] *= scale;
  }
}

double ON_AngularDimension2Extra::DimpointOffset(int index) const
{
  if(index == 0)
    return m_dimpoint_offset[0];
  if(index == 1)
    return m_dimpoint_offset[1];
  return -1;
}
void ON_AngularDimension2Extra::SetDimpointOffset(int index, double offset)
{
  if(index >= 0 && index <= 1)
    m_dimpoint_offset[index] = offset;
}

//----- ON_OBSOLETE_V5_DimAngular -----------------------------------------
ON_OBSOLETE_V5_DimAngular::ON_OBSOLETE_V5_DimAngular()
  : m_angle(0.0)
  , m_radius(1.0)
{
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular;
  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine;
  SetTextValue(DefaultText());
  SetTextFormula(0);
  m_points.Reserve(ON_OBSOLETE_V5_DimAngular::dim_pt_count);
  m_points.SetCount(ON_OBSOLETE_V5_DimAngular::dim_pt_count);
  m_points.Zero();
}

ON_OBSOLETE_V5_DimAngular* ON_OBSOLETE_V5_DimAngular::CreateFromV2AngularDimension(
  const class ON_OBSOLETE_V2_DimAngular& V2_angular_dimension,
  const class ON_3dmAnnotationContext* annotation_context,
  ON_OBSOLETE_V5_DimAngular* destination
)
{
  ON_OBSOLETE_V5_DimAngular* V5_angular_dimension
    = (nullptr != destination)
    ? destination
    : new ON_OBSOLETE_V5_DimAngular();

  V5_angular_dimension->Internal_InitializeFromV2Annotation(V2_angular_dimension,annotation_context);

  ON_2dVector v0 // point on first line
    = (V2_angular_dimension.m_points.Count() >= 2 && V2_angular_dimension.m_points[0].IsValid())
    ? (V2_angular_dimension.m_points[0] - ON_2dPoint::Origin)
    : ON_2dVector::ZeroVector;
  ON_2dVector v1 // point on second line
    = (V2_angular_dimension.m_points.Count() >= 2 && V2_angular_dimension.m_points[1].IsValid())
    ? (V2_angular_dimension.m_points[1] - ON_2dPoint::Origin)
    : ON_2dVector::ZeroVector;
  ON_2dPoint tp // point on second line
    = (V2_angular_dimension.m_points.Count() >= 3 && V2_angular_dimension.m_points[2].IsValid())
    ? V2_angular_dimension.m_points[2]
    : ON_2dPoint::UnsetPoint;
  double angle = V2_angular_dimension.Radius();
  double radius = V2_angular_dimension.Angle();

  bool bUserPositionedText = (v0.IsNotZero() && v1.IsNotZero() && V5_angular_dimension->m_userpositionedtext && tp.IsValid());
  V5_angular_dimension->m_userpositionedtext = bUserPositionedText;

  if (v0.IsNotZero() && v1.IsNotZero())
  {
    ON_Plane plane = V5_angular_dimension->m_plane;
    for (;;)
    {
      if (v0.x > 0.0 && 0.0 == v0.y)
        break;
      bUserPositionedText = false;
      const double length0 = v0.Length();
      if (!(length0 > 0.0))
        break;
      const ON_3dPoint P0 = plane.PointAt(v0.x, v0.y);
      if (!P0.IsValid())
        break;
      const ON_3dPoint P1 = plane.PointAt(v1.x, v1.y);
      if (!P1.IsValid())
        break;
      plane.xaxis = P0 - plane.origin;
      if (!plane.xaxis.Unitize())
        break;
      plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis);
      if (!plane.yaxis.Unitize())
        break;
      if (!plane.IsValid())
        break;
      ON_2dVector v = v1;
      if (!plane.ClosestPointTo(P1, &v.x, &v.y))
        break;
      v0.x = length0;
      v0.y = 0.0;
      v1 = v;
      V5_angular_dimension->m_plane = plane;
      angle = atan2(v1.y,v1.x);
      if ( angle < 0.0 )
        angle += 2.0*ON_PI;
      radius = 0.5*(v0.Length() + v1.Length());
      break;
    }
  }

  if ( !bUserPositionedText )
  {
    tp.x = radius*cos(0.5*angle);
    tp.y = radius*sin(0.5*angle);
  }

  const ON_2dPoint arcpt( radius*cos(angle/3.0), radius*sin(angle/3.0) );

  V5_angular_dimension->m_points.SetCapacity(4);
  V5_angular_dimension->m_points.SetCount(4);
  V5_angular_dimension->m_points[0] = tp.IsValid() ? tp : ON_2dPoint::Origin;
  V5_angular_dimension->m_points[1] = (v0.IsValid() && v0.IsNotZero()) ? ON_2dPoint(v0) : ON_2dPoint::Origin;
  V5_angular_dimension->m_points[2] = (v1.IsValid() && v1.IsNotZero()) ? ON_2dPoint(v1) : ON_2dPoint::Origin;
  V5_angular_dimension->m_points[3] = arcpt.IsValid() ? arcpt : ON_2dPoint::Origin;
  V5_angular_dimension->m_angle = angle;
  V5_angular_dimension->m_radius = radius;

  V5_angular_dimension->m_userpositionedtext = bUserPositionedText;

  return V5_angular_dimension;
}


bool ON_OBSOLETE_V5_DimAngular::IsValid( ON_TextLog* text_log ) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular\n");
    }
    return false;
  }

  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( 4 != m_points.Count() )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - m_points.Count() = %d (should be 4)\n", m_points.Count() );
    }
    return false;
  }

  if ( !ON_IsValid(m_angle) || m_angle <= 0.0 || m_angle > 2.0*ON_PI )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - bogus m_angle = %g\n",m_angle);
    }
    return false;
  }

  if ( !ON_IsValid(m_radius) || m_radius <= 0.0 )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - bogus m_radius = %g\n",m_radius);
    }
    return false;
  }

  if ( 0.0 == m_points[1].x && 0.0 == m_points[1].y )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[1] = center (should be on start ray).\n");
    }
    return false;
  }

  if ( 0.0 == m_points[2].x && 0.0 == m_points[2].y )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[2] = center (should be on end ray).\n");
    }
    return false;
  }

  if ( 0.0 == m_points[3].x && 0.0 == m_points[3].y )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[3] = center (should be on interior of arc).\n");
    }
    return false;
  }

  double a1 = atan2(m_points[1].y, m_points[1].x);
  double a2 = atan2(m_points[2].y, m_points[2].x);
  double a3 = atan2(m_points[3].y, m_points[3].x);
  if ( a1 < 0.0 )
    a1 += 2.0*ON_PI;
  while ( a2 <= a1 )
    a2 += 2.0*ON_PI;
  while ( a3 < a1 )  // Oct 23 2009 LW changed from a3 <= a1 to allow point at end of arc rr53634
    a3 += 2.0*ON_PI;

  if ( fabs(m_angle - (a2-a1)) > ON_ZERO_TOLERANCE + m_angle*ON_SQRT_EPSILON )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - m_angle = %g != %g = (end angle - start angle)\n",m_angle,a2-a1);
    }
    return false;
  }

  double r = ON_2dVector(m_points[3]).Length();
  if ( fabs(r - m_radius) > ON_ZERO_TOLERANCE + m_radius*ON_SQRT_EPSILON )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - m_radius = %g != %g = |m_point[3])|\n",m_radius,r);
    }
    return false;
  }
  

  if ( a3 > a2 )  // Oct 23 2009 LW changed from a3 >= a2 to allow point at end of arc rr53634
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimAngular - angle dim m_points[3] = not on arc interior.\n");
    }
    return false;
  }

  return true;
}



bool ON_OBSOLETE_V5_DimAngular::GetBBox(
        double* boxmin,
        double* boxmax,
        bool bGrowBox
        ) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  ON_Arc arc;
  if ( GetArc(arc) )
  {
    if ( arc.GetTightBoundingBox(bbox,bGrowBox?true:false,0) )
      bGrowBox = true;    
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}





bool ON_OBSOLETE_V5_DimAngular::GetTightBoundingBox( 
		ON_BoundingBox& tight_bbox, 
    bool bGrowBox,
		const ON_Xform* xform
    ) const
{
  ON_Arc arc;
  if ( GetArc(arc) )
  {
    if ( arc.GetTightBoundingBox(tight_bbox,bGrowBox,xform) )
      bGrowBox = true;    
  }
  else if ( bGrowBox && !tight_bbox.IsValid() )
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return (0!=bGrowBox);

}

ON_2dPoint ON_OBSOLETE_V5_DimAngular::Dim2dPoint( int point_index ) const
{
  ON_2dPoint p2;
  if ( m_points.Count() < dim_pt_count || point_index < 0 )
  {
    p2.x = p2.y = ON_UNSET_VALUE;
  }
  else
  {
    if ( text_pivot_pt == point_index )
    {
      point_index = m_userpositionedtext 
                  ? userpositionedtext_pt_index
                  : arcmid_pt;
    }
    
    if ( point_index < dim_pt_count )
    {
      p2 = m_points[point_index];
    }
    else
    {
      switch(point_index)
      {
      case arcstart_pt:
        p2.x = m_radius;
        p2.y = 0.0;
        break;
      case arcend_pt:
        p2.x = m_radius*cos(m_angle);
        p2.y = m_radius*sin(m_angle);
        break;
      case arccenter_pt:
        p2.x = 0.0;
        p2.y = 0.0;
        break;
      case arcmid_pt:
        p2.x = m_radius*cos(0.5*m_angle);
        p2.y = m_radius*sin(0.5*m_angle);
        break;
      case extension0_pt:
        {
          p2 = m_points[start_pt_index];
          double dp0 = DimpointOffset(0);
          if(dp0 >= 0)
          {
            ON_2dVector v2 = (ON_2dVector)p2;
            v2.Unitize();
            p2 = (ON_2dPoint)v2 * dp0;
          }
        }
        break;
      case extension1_pt:
        {
          p2 = m_points[end_pt_index];
          double dp1 = DimpointOffset(1);
          if(dp1 >= 0)
          {
            ON_2dVector v2 = (ON_2dVector)p2;
            v2.Unitize();
            p2 = (ON_2dPoint)v2 * dp1;
          }
        }
        break;
      default:
        p2.x = p2.y = ON_UNSET_VALUE;
        break;
      }
    }
  }
  return p2;
}

ON_3dPoint ON_OBSOLETE_V5_DimAngular::Dim3dPoint( int point_index ) const
{
  ON_2dPoint p2 = Dim2dPoint(point_index);
  if (ON_UNSET_VALUE == p2.x)
    return ON_3dPoint::UnsetPoint;

  ON_3dPoint p = m_plane.PointAt(p2.x, p2.y);
  return p;

  //return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y);
}


bool ON_OBSOLETE_V5_DimAngular::Write( ON_BinaryArchive& file ) const
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimLinear
  //    V4 did not have a ON_OBSOLETE_V5_DimLinear::Write and simply called
  //    ON_OBSOLETE_V5_Annotation::Write.
  bool rc = false;
  bool bInChunk = (file.Archive3dmVersion() >= 5);
  if ( bInChunk )
  {
    rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Write(file)?true:false;
    if (!rc) break;
    rc = file.WriteDouble( m_angle);
    if (!rc) break;
    rc = file.WriteDouble( m_radius);
    if (!rc) break;
    if ( !bInChunk )
      break;

    // to write new V5 fields, increment the minor
    // version number and write them below

    break;
  }

  if ( bInChunk )
  {
    if (!file.EndWrite3dmChunk())
      rc = false;
  }

  return rc;
}

bool ON_OBSOLETE_V5_DimAngular::Read( ON_BinaryArchive& file )
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_DimAngular
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;
  bool bInChunk = (file.Archive3dmVersion() >= 5 && file.ArchiveOpenNURBSVersion() >= 200710180);
  if ( bInChunk )
  {
    rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Read(file)?true:false;
    if (!rc) break;
    rc = file.ReadDouble( &m_angle);
    if (!rc) break;
    rc = file.ReadDouble( &m_radius);
    if (!rc) break;
    if ( !bInChunk || minor_version <= 0 )
      break;

    // Code to read any new ON_OBSOLETE_V5_DimAngular fields will 
    // go here.

    break;
  }

  if ( bInChunk )
  {
    // To read new ON_OBSOLETE_V5_DimAngular specific additions,
    // examine the minor version number and read the information
    // here.  Please ask Dale Lear for help.

    if ( !file.EndRead3dmChunk() )
      rc = false;
  }
  return rc;
}

static bool VectorAngle( const ON_2dVector& v, double& angle)
{
  if( v.IsTiny())
    return false;

  angle = atan2( v.y, v.x);

  if( angle < 0.0)
    angle += 2.0 * ON_PI;

  if( angle > 2.0 * ON_PI)
    angle -= 2.0 * ON_PI;

  return true;
}

bool ON_OBSOLETE_V5_DimAngular::CreateFromArc( const ON_Arc& arc )
{
  // June 9, 2010 - Lowell - Changed to call CreateFromPoints()
  bool rc = arc.IsValid();
  if (rc)
  {
    ON_3dPoint C = arc.Center();
    ON_3dPoint S = arc.StartPoint();
    ON_3dPoint E = arc.EndPoint();
    ON_3dPoint M = arc.MidPoint();
    ON_3dVector N = arc.Plane().zaxis;

    rc = CreateFromPoints(C, S, E, M, N);
  }

  return rc;
}

bool ON_OBSOLETE_V5_DimAngular::GetArc( ON_Arc& arc ) const
{
  bool rc = false;
  // Jan 25, 2007 - changed min radius from >0 to >ON_SQRT_EPSILON
  // to avoid domain problems trying to use very small arcs later
  if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON
      && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI
      && m_plane.origin.IsValid() 
      && m_plane.xaxis.IsValid() 
      && m_plane.yaxis.IsValid() 
      && m_plane.zaxis.IsValid()
      && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON
      && 4 == m_points.Count()
      )
  {
    ON_3dVector X = m_plane.PointAt( m_points[start_pt_index].x, m_points[start_pt_index].y ) - m_plane.origin;
    if ( fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize() )
    {
      if ( fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON )
      {
        ON_3dVector Y = ON_CrossProduct( m_plane.zaxis, X );
        if ( fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize() )
        {
          arc.plane = m_plane;
          arc.plane.xaxis = X;
          arc.plane.yaxis = Y;
          arc.plane.UpdateEquation();
          arc.SetAngleIntervalRadians( ON_Interval(0.0,m_angle) );
          arc.radius = m_radius;
          rc = true;
        }
      }
    }
  }

  return rc;
}

bool ON_OBSOLETE_V5_DimAngular::GetExtensionLines(ON_Line extensions[2]) const
{
  bool rc = false;

  if ( ON_IsValid(m_radius) && m_radius > ON_SQRT_EPSILON
      && ON_IsValid(m_angle) && m_angle > 0.0 && m_angle <= 2.0*ON_PI
      && m_plane.origin.IsValid() 
      && m_plane.xaxis.IsValid() 
      && m_plane.yaxis.IsValid() 
      && m_plane.zaxis.IsValid()
      && fabs( m_plane.zaxis.Length() - 1.0 ) <= ON_SQRT_EPSILON
      && 4 == m_points.Count()
      )
  {
    const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
    if(pDE != 0)
    {
      double exoffset0 = pDE->DimpointOffset(0);
      double exoffset1 = pDE->DimpointOffset(1);
      ON_3dPoint e00, e01, e10, e11;
      e00 = m_plane.PointAt(m_points[start_pt_index].x, m_points[start_pt_index].y);
      e10 = m_plane.PointAt(m_points[end_pt_index].x, m_points[end_pt_index].y);
      ON_3dVector X = e00 - m_plane.origin;
      ON_3dVector Y = e10 - m_plane.origin;
      if((fabs(X.Length()-1.0) <= ON_SQRT_EPSILON || X.Unitize()) &&
         (fabs(Y.Length()-1.0) <= ON_SQRT_EPSILON || Y.Unitize()))
      {
        if((fabs(X*m_plane.zaxis) <= ON_SQRT_EPSILON) &&
           (fabs(Y*m_plane.zaxis) <= ON_SQRT_EPSILON))
        {
          e00 = m_plane.origin + X * exoffset0;
          e10 = m_plane.origin + Y * exoffset1;
          e01 = m_plane.origin + X * m_radius;
          e11 = m_plane.origin + Y * m_radius;

          extensions[0].from = e00;
          extensions[0].to   = e01;
          extensions[1].from = e10;
          extensions[1].to   = e11;
          rc = true;
        }
      }
    }
  }
  return rc;
}


bool ON_OBSOLETE_V5_DimAngular::CreateFromPoints( 
            const ON_3dPoint& pc, 
            const ON_3dPoint& p0in,
            const ON_3dPoint& p1in,
            ON_3dPoint& arcpt, 
            ON_3dVector& Normal)
{
  ON_3dPoint p0, p1;
  p0 = p0in;
  p1 = p1in;

  ON_Plane plane( pc, Normal);

  ON_2dPoint pa, pp0, pp1;

  if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y))
    return false;

  // rotate so that p0 is on x-axis
  ON_2dVector v0( pp0);
  v0.Unitize();
  if ( v0.IsValid() && v0.IsNotZero() )
    plane.Rotate( v0.y, v0.x, plane.Normal());

  if( !plane.ClosestPointTo( p0, &pp0.x, &pp0.y))
    return false;

  if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y))
    return false;

  if( !plane.ClosestPointTo( p1, &pp1.x, &pp1.y))
    return false;

  double a1 = ON_DBL_QNAN;
  double aa = ON_DBL_QNAN;
  if( !VectorAngle( ON_2dVector( pp1), a1) || !VectorAngle( ON_2dVector( pa), aa))    
    return false;

  if( aa > a1)  // the angle is really the bigger one ( > 180)
  {
    // rotate so that p0 is on x-axis
    v0.Set( pp1.x, pp1.y);
    v0.Unitize();
    plane.Rotate( v0.y, v0.x, plane.Normal());

    if( !plane.ClosestPointTo( arcpt, &pa.x, &pa.y))
      return false;
    if( !plane.ClosestPointTo( p1, &pp0.x, &pp0.y))
      return false;
    if( !plane.ClosestPointTo( p0, &pp1.x, &pp1.y))
      return false;
  }

  VectorAngle( ON_2dVector( pp1), a1);

  SetAngle( a1);
  SetRadius( ON_2dVector( pa).Length());

  ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
  if(pDE != 0)
  {
    double os = ((ON_2dVector)pp0).Length();
    pDE->SetDimpointOffset(0, os);
    os = ((ON_2dVector)pp1).Length();
    pDE->SetDimpointOffset(1, os);
  }

  ReservePoints( 4);
  SetPlane( plane);
  SetPoint( 1, pp0);
  SetPoint( 2, pp1);
  SetPoint( 3, pa);


  return true;
}

void ON_OBSOLETE_V5_DimAngular::SetAngle( double angle)
{
  m_angle = angle;
}

double ON_OBSOLETE_V5_DimAngular::Angle() const
{
  return m_angle;
}

void ON_OBSOLETE_V5_DimAngular::SetRadius( double radius)
{
  m_radius = radius;
}

double ON_OBSOLETE_V5_DimAngular::Radius() const
{
  return m_radius;
}

double ON_OBSOLETE_V5_DimAngular::NumericValue() const
{
  // This returns degrees - if we get another angular unit system, add it
  return (m_angle*180.0/ON_PI);
}

int ON_OBSOLETE_V5_DimAngular::StyleIndex() const
{
  return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex();
}

void ON_OBSOLETE_V5_DimAngular::SetStyleIndex( int i)
{
  ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i);
}

const wchar_t* ON_OBSOLETE_V5_DimAngular::DefaultText()
{
  return L"<>"; // Aug 31, 2009 - Lowell
}





static void OrientRectHelper( ON_2dVector corners[4] )
{
  double twice_area = 0.0;
  ON_2dVector p0, p1;
  int i;
  p1 = corners[3];
  for ( i = 0; i < 4; i++ ) 
  {
    p0 = p1;
    p1 = corners[i];
    twice_area += (p0.x-p1.x)*(p0.y+p1.y);
  }
  if ( twice_area < 0.0 )
  {
    p1 = corners[1];
    corners[1] = corners[3];
    corners[3] = p1;
  }
}

int ON_OBSOLETE_V5_DimAngular::GetDimensionArcSegments(
    ON_OBSOLETE_V5_RECT gdi_text_rect,
    int gdi_height_of_I,
    ON_Xform gdi_to_world,
    const ON_DimStyle& dimstyle,
    double dimscale,
    const ON_Viewport* vp,
    double a[6],
    bool& bInside
    ) const
{
  int rc = 0;

  a[0] = 0.0;
  a[1] = 0.0;

  if ( m_angle <= 0.0 || !ON_IsValid(m_angle) )
    return 0;

  a[1] = m_angle;

  if ( m_radius <= 0.0 || !ON_IsValid(m_radius) || m_angle >= 2.0*ON_PI )
    return 0;

  if ( 0 == gdi_height_of_I )
  {
    // Default to height of Ariel 'I' (this code still works if ON_Font::Constants::AnnotationFontCellHeight != 256)
    gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256;
  }

  if ( 0.0 == dimscale )
  {
    dimscale = 1.0;
  }

  double t;

  ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textdisplay = ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(dimstyle);

  const double text_height_of_I    = dimscale*dimstyle.TextHeight();
  const double textgap             = dimscale*dimstyle.TextGap();
  const double gdi_to_plane_scale  = text_height_of_I/gdi_height_of_I;
  const double textwidth           = fabs(gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left));
  const double arrowwidth          = fabs(dimscale*dimstyle.ArrowSize());
  const double tailwidth           = 0.5*arrowwidth;
  const double dimextension        = fabs(dimscale*dimstyle.DimExtension());
  const double a0 = 0.0;
  const double a1 = m_angle;

  double sin_angle = 0.5*dimextension/m_radius;
  if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
  const double  dimextension_angle = 2.0*asin(sin_angle);

  sin_angle = 0.5*arrowwidth/m_radius;
  if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
  const double arrowangle = 2.0*asin(sin_angle);

  sin_angle = 0.5*tailwidth/m_radius;
  if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
  const double tailangle = 2.0*asin(sin_angle);

  // June 7, 2010 - Lowell - Added some special handling for very small dimensions 
  double arrow_angle = arrowangle + tailangle;
  if(arrow_angle > ON_PI * 0.5)
    arrow_angle = ON_PI * 0.5;
  if ( m_radius <= arrowwidth + tailwidth || m_radius*(a1-a0) < 2.0*(arrowwidth) + tailwidth )
  {
    // arc is tiny with respect to arrowhead size - arrowheads have to be "outside"
    // 2 arc segments, arrowheads outside
    a[0] = a0;
    a[1] = a0 - arrow_angle;
    a[2] = a1 + arrow_angle;
    a[3] = a1;
    a[4] = a0;
    a[5] = a1;
    if( dimextension_angle != 0.0)
    {
      a[0] += dimextension_angle;
      a[3] -= dimextension_angle;
    }
    bInside = false;
    return 2;
  }
  
  if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay && m_radius <= 0.5*(textwidth + 2.0*textgap))
  {
    // 2 arc segments, arrowheads outside
    a[0] = a0;
    a[1] = a0 - arrow_angle;
    a[2] = a1 + arrow_angle;
    a[3] = a1;
    a[4] = a0;
    a[5] = a1;
    if( dimextension_angle != 0.0)
    {
      a[0] += dimextension_angle;
      a[3] -= dimextension_angle;
    }
    bInside = false;
    return 2;
  }

  sin_angle = 0.5*(textwidth+2.0*textgap)/m_radius;
  if ( sin_angle > 1.0 ) sin_angle = 1.0; else if (sin_angle < -1.0) sin_angle = -1.0;
  const double textangle = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay && !m_userpositionedtext)
    ? 2.0*asin(sin_angle)
    : 0.0;

  if ( (a1-a0) <= 2.0*(arrow_angle) + textangle )
  {
    // 2 arc segments, arrowheads outside
    a[0] = a0;
    a[1] = a0 - arrow_angle;
    a[2] = a1 + arrow_angle;
    a[3] = a1;
    a[4] = a0;
    a[5] = a1;
    if( dimextension_angle != 0.0)
    {
      a[0] += dimextension_angle;
      a[3] -= dimextension_angle;
    }
    bInside = false;
    return 2;
  }
  
  if ((ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp) || m_userpositionedtext)
  {
    // use projected rectangle to clip dimension arc
    double aa0, aa1, aa, r0, r1, tt[2];
    ON_3dPoint P, R, c[2];
    ON_2dVector corners[4], N;
    ON_Line ray;
    int i,xi, xrc;
    const ON_Circle circle(ON_xy_plane,1.0);

    ON_3dVector vp_zaxis = (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == textdisplay && vp)
                         ? vp->CameraZ() 
                         : m_plane.zaxis;

    // 30 July 2012 - Lowell - Slightly shrink the text gap value
    // so that text + gap won't intersect the dim line if when text
    // is moved along the dim line. rr110504
    double gdi_gap = fabs(textgap/gdi_to_plane_scale)-12;
    if(gdi_gap < 0.0) gdi_gap = 0.0;

    R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[0].x,&corners[0].y);

    R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.bottom+gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[1].x,&corners[1].y);

    R.Set(gdi_text_rect.right+gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[2].x,&corners[2].y);

    R.Set(gdi_text_rect.left-gdi_gap,gdi_text_rect.top-gdi_gap,0.0);
    ray.from = gdi_to_world*R; ray.to = ray.from + vp_zaxis;
    P = ( ON_Intersect(ray,m_plane,&t) ) ? ray.PointAt(t) : ray.from;
    m_plane.ClosestPointTo(P,&corners[3].x,&corners[3].y);

    // orient projected rect so it is counter-clockwise
    OrientRectHelper(corners);

    aa0 = a0;
    aa1 = a1;
    P.Set(cos(a0)*m_radius,sin(a0)*m_radius,0.0);
    R.Set(cos(a1)*m_radius,sin(a1)*m_radius,0.0);
    for ( i = 0; i < 4; i++ )
    {
      N.x = (corners[i].y-corners[(i+1)%4].y);
      N.y = (corners[(i+1)%4].x-corners[i].x);
      if ( !N.Unitize() )
        continue;
      if ( (P.x - corners[i].x)*N.x + (P.y - corners[i].y)*N.y  < 0.0 )
      {
        // end point not in text box
        aa0 = ON_UNSET_VALUE;
      }
      if ( (R.x - corners[i].x)*N.x + (R.y - corners[i].y)*N.y  < 0.0 )
      {
        // start point not in text box
        aa1 = ON_UNSET_VALUE;
      }
    }

    if ( ON_UNSET_VALUE == aa0 )
      aa0 = aa1;
    else if ( ON_UNSET_VALUE == aa1 )
      aa1 = aa0;

    r1 = corners[3].Length() - m_radius;
    ray.to.x = corners[3].x/m_radius; ray.to.y = corners[3].y/m_radius; 
    ray.to.z = ray.from.z = 0.0;
    for ( i = 0; i < 4; i++ )
    {
      r0 = r1;
      r1 = corners[i].Length() - m_radius;
      ray.from = ray.to;
      ray.to.x = corners[i].x/m_radius; ray.to.y = corners[i].y/m_radius;
      if ( r0 < 0.0 && r1 < 0.0 )
      {
        continue; // ray is inside circle
      }
      if ( r0 > 0.0 && r1 > 0.0 )
      {
        if ( !ray.ClosestPointTo(ON_3dPoint::Origin,&t ) )
          continue;
        R = ray.PointAt(t);
        if ( R.x*R.x + R.y*R.y >= 1.0 )
          continue; // ray is outside circle
      }


      xrc = 0;
      if ( 0.0 == r0 )
        tt[xrc++] = 0.0;

      if ( 0.0 == r1 )
        tt[xrc++] = 1.0;

      if ( 0 == xrc )
        xrc = ON_Intersect(ray,circle,&tt[0],c[0],&tt[1],c[1]);

      if ( xrc < 1 || xrc > 2 )
        continue; // ray does not intersect circle;

      for ( xi = 0; xi < xrc; xi++ )
      {
        if ( tt[xi] < 0.0 || tt[xi] > 1.0 )
          continue; // intersection point not on segment

        P = ray.PointAt(tt[xi]);
        if ( 0.0 == P.x && 0.0 == P.y )
          continue; // bogus

        aa = atan2(P.y,P.x);
        if ( aa < a0 ) aa += 2.0*ON_PI; else if ( aa > a1 ) aa -= 2.0*ON_PI;
        if ( aa < a0 || aa > a1 )
        {
          continue;
        }
        if ( ON_UNSET_VALUE == aa0 )
          aa0 = aa1 = aa;
        else if ( aa < aa0 )
          aa0 = aa;
        else if ( aa > aa1 )
          aa1 = aa;
      }
    }
    if ( ON_UNSET_VALUE != aa0 && ON_UNSET_VALUE != aa1 
        && a0 <= aa0 && aa0 < aa1 && aa1 <= a1 )
    {
      t = arrow_angle;
      if ( aa0 < a0+t && aa1 > a1-t )
      {
        // text box hits both arrowheads
        // 2 arc segments, arrowheads outside
        a[0] = a0;
        a[1] = a0 - arrow_angle;
        a[2] = a1 + arrow_angle;
        a[3] = a1;
        a[4] = a0;
        a[5] = a1;
        if( dimextension_angle != 0.0)
        {
          a[0] += dimextension_angle;
          a[3] -= dimextension_angle;
        }
        bInside = false;
        rc = 2;
      }
      else
      {
        if ( aa0 < a0+t ) aa0 = a0+t;
        if ( aa1 > a1-t ) aa1 = a1-t;
        if ( a0 < aa0 && aa0 < aa1 && aa1 < a1 )
        {
          // clip arc to text rectangle
          // 2 arc segments, arrowheads inside
          a[0] = a0;
          a[1] = aa0;
          a[2] = aa1;
          a[3] = a1;
          a[4] = a0;
          a[5] = a1;
          if( dimextension_angle != 0.0)
          {
            a[0] -= dimextension_angle;
            a[3] += dimextension_angle;
          }
          bInside = true;
          rc = 2;
        }
        else
        {
          // nasty case - don't bother clipping
          // 1 segment, arrows inside
          a[0] = a0;
          a[1] = a1;
          a[2] = a0;
          a[3] = a1;
          a[4] = a0;
          a[5] = a1;
          if( dimextension_angle != 0.0)
          {
            a[0] -= dimextension_angle;
            a[1] += dimextension_angle;
          }
          bInside = true;
          rc = 1;
        }
      }
    }
    else
    {
      // 1 segment, arrows inside
      a[0] = a0;
      a[1] = a1;
      a[2] = a0;
      a[3] = a1;
      a[4] = a0;
      a[5] = a1;
      if( dimextension_angle != 0.0)
      {
        a[0] -= dimextension_angle;
        a[1] += dimextension_angle;
      }
      bInside = true;
      rc = 1;
    }
  }
  else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == textdisplay || m_userpositionedtext)
  {
    // 1 segment, arrows inside
    a[0] = a0;
    a[1] = a1;
    a[2] = a0;
    a[3] = a1;
    a[4] = a0;
    a[5] = a1;
    if( dimextension_angle != 0.0)
    {
      a[0] -= dimextension_angle;
      a[1] += dimextension_angle;
    }
    bInside = true;
    rc = 1;
  }
  else if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine == textdisplay)
  {
    // 2 segments, arrows inside
    t = 0.5*((a1-a0) - textangle);
    a[0] = a0;
    a[1] = a0+t;
    a[2] = a1-t;
    a[3] = a1;
    a[4] = a0;
    a[5] = a1;
    if( dimextension_angle != 0.0)
    {
      a[0] -= dimextension_angle;
      a[3] += dimextension_angle;
    }
    bInside = true;
    rc = 2;
  }

  return rc;
}

double ON_OBSOLETE_V5_DimAngular::DimpointOffset(int index) const
{
  const ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
  if(pDE != 0)
    return pDE->DimpointOffset(index);
  return -1.0;
}
void ON_OBSOLETE_V5_DimAngular::SetDimpointOffset(int index, double offset)
{
  ON_AngularDimension2Extra* pDE = ON_AngularDimension2Extra::AngularDimensionExtra(this);
  if(pDE != 0)
    pDE->SetDimpointOffset(index, offset);
}


//----- ON_OBSOLETE_V5_DimOrdinate -----------------------------------------
ON_OBSOLETE_V5_DimOrdinate::ON_OBSOLETE_V5_DimOrdinate()
{
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate;
  SetTextValue(DefaultText());
  SetTextFormula(0);
  m_direction = -1;  // undetermined direction
  m_points.Reserve(ON_OBSOLETE_V5_DimOrdinate::dim_pt_count);
  m_points.SetCount(ON_OBSOLETE_V5_DimOrdinate::dim_pt_count);
  m_points.Zero();
  m_kink_offset_0 = ON_UNSET_VALUE;
  m_kink_offset_1 = ON_UNSET_VALUE;
}

ON_OBSOLETE_V5_DimOrdinate::~ON_OBSOLETE_V5_DimOrdinate()
{
}

bool ON_OBSOLETE_V5_DimOrdinate::Transform( const ON_Xform& xform )
{
  bool rc = xform.IsIdentity();
  if ( !rc)
  {
    return ON_OBSOLETE_V5_Annotation::Transform(xform);
  }
  return rc;
}

ON_2dPoint ON_OBSOLETE_V5_DimOrdinate::Dim2dPoint( int point_index, double default_offset) const
{
  ON_2dPoint p2( ON_UNSET_VALUE, ON_UNSET_VALUE);
  int dir = m_direction;
  if( dir == -1 && ( point_index == offset_pt_0 || point_index == offset_pt_1))
  {
    if( fabs( m_points[definition_pt_index].y - m_points[leader_end_pt_index].y) > 
        fabs( m_points[definition_pt_index].x - m_points[leader_end_pt_index].x))
      dir = 0;
    else
      dir = 1;
  }

  if( point_index >= 0 && point_index < dim_pt_count && m_points.Count() == dim_pt_count)
  {
    p2 = m_points[point_index];
  }
  else if( point_index == text_pivot_pt)
  {
    // ON_3dPoint::UnsetPoint
  }
  else if( point_index == offset_pt_0)
  {
    double offset;
    if( m_kink_offset_0 == ON_UNSET_VALUE)
      offset = default_offset;
    else
      offset = m_kink_offset_0;

    if( dir == x)
    {
      p2 = m_points[leader_end_pt_index];
      if( p2.y > m_points[definition_pt_index].y)
        p2.y -= offset;
      else
        p2.y += offset;
    }
    else if( dir == y)
    {
      p2 = m_points[leader_end_pt_index];
      if( p2.x > m_points[definition_pt_index].x)
        p2.x -= offset;
      else
        p2.x += offset;
    }
  }
  else if( point_index == offset_pt_1)
  {
    double offset0;
    if( m_kink_offset_0 == ON_UNSET_VALUE)
      offset0 = default_offset;
    else
      offset0 = m_kink_offset_0;

    double offset1;
    if( m_kink_offset_1 == ON_UNSET_VALUE)
      offset1 = default_offset;
    else
      offset1 = m_kink_offset_1;

    if( dir == x)
    {
      p2.x = m_points[definition_pt_index].x;
      if( m_points[leader_end_pt_index].y > m_points[definition_pt_index].y)
        p2.y = m_points[leader_end_pt_index].y - offset0 - offset1;
      else
        p2.y = m_points[leader_end_pt_index].y + offset0 + offset1;
    }
    else if( dir == y)
    {
      p2.y = m_points[definition_pt_index].y;
      if( m_points[leader_end_pt_index].x > m_points[definition_pt_index].x)
        p2.x = m_points[leader_end_pt_index].x - offset0 - offset1;
      else
        p2.x = m_points[leader_end_pt_index].x + offset0 + offset1;
    }
  }
  return p2;
}

ON_3dPoint ON_OBSOLETE_V5_DimOrdinate::Dim3dPoint( int point_index, double default_offset) const
{
  ON_2dPoint p2 = Dim2dPoint(point_index, default_offset);
  return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y);
}

bool ON_OBSOLETE_V5_DimOrdinate::IsValid( ON_TextLog* text_log) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate)
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimOrdinate - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate.\n");
    }
    return false;
  }

  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimOrdinate - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( m_points.Count() != 2 )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_DimOrdinate - m_points.Count() = %d (should be 2).\n",m_points.Count());
    }
    return false;
  }

  return true;

}

bool ON_OBSOLETE_V5_DimOrdinate::Write( ON_BinaryArchive& file ) const
{
  // put the entire ON_OBSOLETE_V5_DimOrdinate in a chunk so we can
  // add fields without breaking the file IO for old product.
  bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
  if (rc)
  {
    rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if (rc)
    {
      // As of 18 October 2007, the following comment is out of date.
      // But, we still need to write this "extra" chunk so we don't
      // break V4 file writing.
      //
      //   The output of ON_OBSOLETE_V5_Annotation::Write must be wrapped
      //   in an additional chunk because it does not put 
      //   itself in a chunk.  If you don't put it in a chunk,
      //   the versioning in the ON_OBSOLETE_V5_Annotation::Write is useless and
      //   will cause serious IO bugs if fields are ever added.
      rc = ON_OBSOLETE_V5_Annotation::Write( file) ? true : false;
      if (!file.EndWrite3dmChunk() )
        rc = false;
    }
    if (rc)
      rc = file.WriteInt( m_direction);
    // kink offsets, ver 1.1 added 2-4-06
    if (rc)
      rc = file.WriteDouble( m_kink_offset_0);
    if (rc)
      rc = file.WriteDouble( m_kink_offset_1);

    // end of ON_OBSOLETE_V5_DimOrdinate chunk
    if (!file.EndWrite3dmChunk() )
      rc = false;
  }

  return rc;
}

bool ON_OBSOLETE_V5_DimOrdinate::Read( ON_BinaryArchive& file )
{
  int major_version=0, minor_version=0;
  bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
  if (rc)
  {
    if ( 1 != major_version )
    {
      rc = false;
    }
    else
    {
      int submajor_version=0, subminor_version=0;

      // subchunk wraps ON_OBSOLETE_V5_Annotation field so this
      // function won't break if ON_OBSOLETE_V5_Annotation changes.
      rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&submajor_version,&subminor_version);
      if (rc)
      {
        if ( 1 != submajor_version )
          rc = false;
        else
        {
          rc = ON_OBSOLETE_V5_Annotation::Read( file) ? true : false;
        }
        if ( !file.EndRead3dmChunk() )
          rc = false;
      }

      if( rc) 
        rc = file.ReadInt( &m_direction);

      if( minor_version > 0)
      {
        if( rc) 
          rc = file.ReadDouble( &m_kink_offset_0);
        if( rc) 
          rc = file.ReadDouble( &m_kink_offset_1);
      }
    }

    if (!file.EndRead3dmChunk())
      rc = false;
  }
  return rc;
}

double ON_OBSOLETE_V5_DimOrdinate::NumericValue() const
{
  if( m_direction == 0)
    return m_points[1].x - m_points[0].x;
  else
    return m_points[1].y - m_points[0].y;
}

int ON_OBSOLETE_V5_DimOrdinate::StyleIndex() const
{
  return ON_OBSOLETE_V5_Annotation::V5_3dmArchiveDimStyleIndex();
}

void ON_OBSOLETE_V5_DimOrdinate::SetStyleIndex( int i)
{
  ON_OBSOLETE_V5_Annotation::SetV5_3dmArchiveDimStyleIndex( i);
}

bool ON_OBSOLETE_V5_DimOrdinate::GetBBox(
  double* boxmin,
  double* boxmax,
  bool bGrowBox
) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  if( m_points.Count() == 2)
  {
    ON_3dPointArray P( 2);

    P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y));
    P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y));
    bGrowBox = P.GetBBox(&bbox.m_min.x, &bbox.m_max.x, bGrowBox);
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}

bool ON_OBSOLETE_V5_DimOrdinate::GetTightBoundingBox( ON_BoundingBox& tight_bbox,
                                                 bool bGrowBox,
                                                 const ON_Xform* xform) const
{
  if( m_points.Count() == 2)
  {
    ON_3dPointArray P(2);

    P.Append( m_plane.PointAt( m_points[0].x, m_points[0].y));
    P.Append( m_plane.PointAt( m_points[1].x, m_points[1].y));

    if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform))
      bGrowBox = true;
  }
  else if( bGrowBox && !tight_bbox.IsValid())
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return( 0 != bGrowBox);
}

int ON_OBSOLETE_V5_DimOrdinate::ImpliedDirection() const
{
  int direction = -1;
  const ON_2dPoint& p0 = m_points[definition_pt_index];
  const ON_2dPoint& p1 = m_points[leader_end_pt_index];
  if( fabs( p1.x - p0.x) <= fabs( p1.y - p0.y))
    direction = 0; // measures along x axis
  else
    direction = 1; // measures along y axis
  
  return direction;
} 

int ON_OBSOLETE_V5_DimOrdinate::Direction() const
{
  return m_direction;
} 

void ON_OBSOLETE_V5_DimOrdinate::SetDirection( int direction)
{
  m_direction = direction;
}

const wchar_t* ON_OBSOLETE_V5_DimOrdinate::DefaultText() { return L"<>"; }

double ON_OBSOLETE_V5_DimOrdinate::KinkOffset( int index) const
{
  if( index == 0)
    return m_kink_offset_0;
  else if( index == 1)
    return m_kink_offset_1;
  else
    return ON_UNSET_VALUE;
}

void ON_OBSOLETE_V5_DimOrdinate::SetKinkOffset( int index, double offset)
{
  if( index == 0)
    m_kink_offset_0 = offset;
  else  if( index == 1)
    m_kink_offset_1 = offset;
}

void ON_OBSOLETE_V5_DimOrdinate::CalcKinkPoints( ON_2dPoint p0, ON_2dPoint p1, 
                                            int direction, double default_offset,
                                            ON_2dPoint& k0, ON_2dPoint& k1) const
{
  double offset0 = KinkOffset( 0);
  double offset1 = KinkOffset( 1);

  // if these haven't been set by dragging the offset points
  // use 2*textheight
  if( offset0 == ON_UNSET_VALUE)
    offset0 = default_offset;
  if( offset1 == ON_UNSET_VALUE)
    offset1 = default_offset;

  if( p0[1-direction] > p1[1-direction])
  {
    offset0 = -offset0;
    offset1 = -offset1;
  }

  //double d = fabs( p0[1-direction] - p1[1-direction]);

  if( direction == 0)
  {
    //if( d - fabs( offset0) > default_offset)
    //{
      k1.x = p0.x;
      k1.y = p1.y - offset0 - offset1;
    //}
    //else
    //{
    //  k1.x = p0.x;
    //  k1.y = p1.y + offset0 - offset1;
    //}

    k0.x = p1.x;
    k0.y = p1.y - offset0;
  }
  else
  {
    //if( d - fabs( offset0) > default_offset)
    //{
      k1.x = p1.x - offset0 - offset1;
      k1.y = p0.y;
    //}
    //else
    //{
    //  k1.x = p1.x + offset0 - offset1;
    //  k1.y = p0.y;
    //}

    k0.x = p1.x - offset0;
    k0.y = p1.y;
  }
}



//----- ON_OBSOLETE_V5_TextObject -----------------------------------------------
ON_OBSOLETE_V5_TextObject::ON_OBSOLETE_V5_TextObject()
{
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock;
  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal;
}

ON_OBSOLETE_V5_TextObject::~ON_OBSOLETE_V5_TextObject()
{
}

bool ON_OBSOLETE_V5_TextObject::IsValid( ON_TextLog* text_log ) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_TextObject - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock\n");
    }
    return false;
  }

  // 05 March 2009 S. Baer
  // Text blocks with no "printable" characters are considered invalid. Any
  // character with a value greater than 32 (the value of space) is "printable."
  int count = m_usertext.Length();
  bool bValidText = false;
  for( int i=0; i<count; i++ )
  {
    wchar_t c = m_usertext[i];
    // all characters <= space are nonprintable
    if( c > L' ' )
    {
      bValidText = true;
      break;
    }
  }
  // 9 Oct 2010 S. Baer
  // With the addition of text formulas, the user text can be 0 length
  if( !bValidText && count<1 )
  {
    const wchar_t* formula = TextFormula();
    if( formula && formula[0] )
      bValidText = true;
  }

  if( !bValidText )
  {
    if( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_TextObject - m_usertext does not contain printable characters.\n");
    }
    return false;
  }


  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_TextObject - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( 0 != m_points.Count() )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_TextObject - m_points.Count() = %d (should be 0)\n", m_points.Count() );
    }
    return false;
  }

  return true;
}


bool ON_OBSOLETE_V5_TextObject::Write(ON_BinaryArchive& archive) const
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_TextObject
  //    V4 did not have a ON_OBSOLETE_V5_TextObject::Write and simply called
  //    ON_OBSOLETE_V5_Annotation::Write.
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5);
  if ( bInChunk )
  {
    rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk )
      break;

    // To write new fields, increment minor version number
    // and write values here.  Ask Dale Lear for help.

    break;
  }

  if ( bInChunk )
  {
    if (!archive.EndWrite3dmChunk())
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_TextObject::Read(ON_BinaryArchive& archive)
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_TextObject
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
  if ( bInChunk )
  {
    rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk || minor_version <= 0 )
      break;

    // read future addition here

    break;
  }

  if ( bInChunk )
  {
    // To read new ON_OBSOLETE_V5_TextObject specific additions,
    // examine the minor version number and read the information
    // here.  Please ask Dale Lear for help.

    if ( !archive.EndRead3dmChunk() )
      rc = false;
  }
  return rc;
}


bool ON_OBSOLETE_V5_TextObject::GetBBox(
        double* boxmin,
        double* boxmax,
        bool bGrowBox
        ) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  if ( 1 == m_points.Count() )
  {
    ON_2dPoint uv = m_points[0];
    bbox.Set( m_plane.PointAt(uv.x,uv.y), bGrowBox );
    bGrowBox = true;
  }
  else if ( 0 == m_points.Count() )
  {
    bbox.Set( m_plane.origin, bGrowBox );
    bGrowBox = true;
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}





bool ON_OBSOLETE_V5_TextObject::GetTightBoundingBox( 
		ON_BoundingBox& tight_bbox, 
    bool bGrowBox,
		const ON_Xform* xform
    ) const
{
  if ( 1 == m_points.Count() )
  {
    ON_3dPointArray P(1);
    P.Append( m_plane.PointAt(m_points[0].x,m_points[0].y) );
    if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
      bGrowBox = true;
  }
  else if ( bGrowBox && !tight_bbox.IsValid() )
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return (0!=bGrowBox);
}

bool ON_OBSOLETE_V5_TextObject::Transform( const ON_Xform& xform )
{
  // Dale Lear - this override fixes RR 11114 by correctly
  //             handling non uniform scaling.
  bool rc = xform.IsIdentity();
  if ( !rc)
  {
    ON_Plane xformed_plane = m_plane;
    rc = xformed_plane.Transform(xform);
    if (rc)
    rc = ON_Geometry::Transform(xform)?true:false;
    if (rc)
    {
      ON_3dPoint P0 = xform*m_plane.origin;
      ON_3dPoint P1 = xform*(m_plane.origin + m_plane.xaxis);
      double s = P0.DistanceTo(P1);
      if ( s <= ON_ZERO_TOLERANCE )
      {
        P1 = xform*(m_plane.origin + m_plane.yaxis);
        s = P0.DistanceTo(P1);
      }
      m_plane = xformed_plane;
      if ( s > ON_ZERO_TOLERANCE && fabs(s-1.0) > ON_SQRT_EPSILON )
      {
        s *= m_textheight;
        if ( s > ON_SQRT_EPSILON )
          m_textheight = s;
      }
    }
  }

  return rc;
}

void ON_OBSOLETE_V5_TextObject::SetJustification( unsigned int justification)
{
  m_justification = justification;
}

unsigned int ON_OBSOLETE_V5_TextObject::Justification() const
{
  return m_justification;
}

bool ON_OBSOLETE_V5_TextObject::DrawTextMask() const
{
  const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false);
  if(pTE)
    return pTE->DrawTextMask();
  else
    return false;
}

void ON_OBSOLETE_V5_TextObject::SetDrawTextMask(bool bDraw)
{
  ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true);
  if(pTE)
    pTE->SetDrawTextMask(bDraw);
}

int ON_OBSOLETE_V5_TextObject::MaskColorSource() const
{
  const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false);
  if(pTE)
    return pTE->MaskColorSource();
  else
    return 0;
}

void ON_OBSOLETE_V5_TextObject::SetMaskColorSource(int source)
{
  ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true);
  if(pTE)
    pTE->SetMaskColorSource(source);
}

ON_Color ON_OBSOLETE_V5_TextObject::MaskColor() const
{
  const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false);
  if(pTE)
    return pTE->MaskColor();
  else
    return 0;
}

void ON_OBSOLETE_V5_TextObject::SetMaskColor(ON_Color color)
{
  ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true);
  if(pTE)
    pTE->SetMaskColor(color);
}

double ON_OBSOLETE_V5_TextObject::MaskOffsetFactor() const
{
  const ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, false);
  if(pTE)
    return pTE->MaskOffsetFactor();
  else
    return 0;
}

void ON_OBSOLETE_V5_TextObject::SetMaskOffsetFactor(double offset)
{
  ON_OBSOLETE_V5_TextExtra* pTE = ON_OBSOLETE_V5_TextExtra::TextExtension(this, true);
  if(pTE)
    pTE->SetMaskOffsetFactor(offset);
}

bool ON_OBSOLETE_V5_TextObject::AnnotativeScaling() const
{
  return m_annotative_scale;
}

void ON_OBSOLETE_V5_TextObject::SetAnnotativeScaling(bool b)
{
  m_annotative_scale = b;
}



//----- ON_OBSOLETE_V5_Leader ------------------------------------------
ON_OBSOLETE_V5_Leader::ON_OBSOLETE_V5_Leader()
{
  m_type = ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader;
  m_textdisplaymode = ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine;
}

ON_OBSOLETE_V5_Leader::~ON_OBSOLETE_V5_Leader()
{
}

bool ON_OBSOLETE_V5_Leader::IsValid( ON_TextLog* text_log ) const
{
  if ( m_type != ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_Leader - m_type !=  ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader\n");
    }
    return false;
  }

  if ( !ON_OBSOLETE_V5_Annotation::IsValid( text_log ))
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_Leader - invalid ON_OBSOLETE_V5_Annotation base class.\n");
    }
    return false;
  }

  if ( m_points.Count() < 2 )
  {
    if ( text_log )
    {
      text_log->Print("ON_OBSOLETE_V5_Leader - m_points.Count() = %d (should be >= 2)\n", m_points.Count() );
    }
    return false;
  }

  return true;
}

bool ON_OBSOLETE_V5_Leader::Write(ON_BinaryArchive& archive) const
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_Leader
  //    V4 did not have a ON_OBSOLETE_V5_Leader::Write and simply called
  //    ON_OBSOLETE_V5_Leader::Write.
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5);
  if ( bInChunk )
  {
    rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Write(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk )
      break;

    // To write new fields, increment minor version number
    // and write values here.  Ask Dale Lear for help.

    break;
  }

  if ( bInChunk )
  {
    if (!archive.EndWrite3dmChunk())
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_Leader::Read(ON_BinaryArchive& archive)
{
  // 18 October 2007 Dale Lear
  //    I added the chunk wrapping so V5 and future versions can
  //    add IO support for information specific to ON_OBSOLETE_V5_Leader
  int major_version = 0;
  int minor_version = 0;
  bool rc = false;
  bool bInChunk = (archive.Archive3dmVersion() >= 5 && archive.ArchiveOpenNURBSVersion() >= 200710180);
  if ( bInChunk )
  {
    rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
    if ( !rc )
      return false;
  }
  else
  {
    rc = true;
  }

  while(rc)
  {
    rc = ON_OBSOLETE_V5_Annotation::Read(archive)?true:false;
    if (!rc) break;
    if ( !bInChunk || minor_version <= 0 )
      break;

    // read future addition here

    break;
  }

  if ( bInChunk )
  {
    if ( !archive.EndRead3dmChunk() )
      rc = false;
  }
  return rc;
}

bool ON_OBSOLETE_V5_Leader::GetBBox(
        double* boxmin,
        double* boxmax,
        bool bGrowBox
        ) const
{
  ON_BoundingBox bbox;
  if ( bGrowBox )
  {
    bbox.m_min.x = boxmin[0]; 
    bbox.m_min.y = boxmin[1]; 
    bbox.m_min.z = boxmin[2];
    bbox.m_max.x = boxmax[0]; 
    bbox.m_max.y = boxmax[1]; 
    bbox.m_max.z = boxmax[2];
    if ( !bbox.IsValid() )
    {
      bbox.Destroy();
      bGrowBox = false;
    }
  }

  const int point_count = m_points.Count();
  if ( point_count > 0 )
  {
    ON_3dPointArray P(point_count);
    int i;
    for ( i = 0; i < point_count; i++ )
    {
      ON_2dPoint uv = m_points[i];
      P.Append( m_plane.PointAt(uv.x,uv.y));
    }
    if ( P.GetBoundingBox(bbox,bGrowBox?true:false) )
      bGrowBox = true;
  }

  if ( bGrowBox )
  {
    boxmin[0] = bbox.m_min.x; 
    boxmin[1] = bbox.m_min.y; 
    boxmin[2] = bbox.m_min.z; 
    boxmax[0] = bbox.m_max.x; 
    boxmax[1] = bbox.m_max.y; 
    boxmax[2] = bbox.m_max.z; 
  }

  return bGrowBox;
}



bool ON_OBSOLETE_V5_Leader::GetTightBoundingBox( 
		ON_BoundingBox& tight_bbox, 
    bool bGrowBox,
		const ON_Xform* xform
    ) const
{
  const int point_count = m_points.Count();
  if ( point_count >= 2 )
  {
    ON_3dPointArray P(point_count);
    int i;
    for ( i = 0; i < point_count; i++ )
    {
      ON_2dPoint uv = m_points[i];
      P.Append( m_plane.PointAt(uv.x,uv.y));
    }
    if ( P.GetTightBoundingBox( tight_bbox, bGrowBox, xform ) )
      bGrowBox = true;
  }
  else if ( bGrowBox && !tight_bbox.IsValid() )
  {
    tight_bbox.Destroy();
    bGrowBox = false;
  }

  return (0!=bGrowBox);
}

ON_2dPoint ON_OBSOLETE_V5_Leader::Dim2dPoint(
      int point_index
      ) const
{
  ON_2dPoint p2;
  int point_count = m_points.Count();
  if ( point_index < 0 || point_count < 1 )
  {
    p2.x = p2.y = ON_UNSET_VALUE;
  }
  else
  {
    switch(point_index)
    {
    case arrow_pt_index:
      p2 = m_points[0];
      break;

    case text_pivot_pt:
    case tail_pt:
      p2 = *m_points.Last();
      break;

    default:
      if ( point_index < point_count )
      {
        p2 = m_points[point_index];
      }
      else
      {
        p2.x = p2.y = ON_UNSET_VALUE;
      }
      break;
    }
  }
  return p2;
}

ON_3dPoint ON_OBSOLETE_V5_Leader::Dim3dPoint(
      int point_index
      ) const
{
  ON_2dPoint p2 = Dim2dPoint(point_index);
  return (ON_UNSET_VALUE == p2.x) ? ON_3dPoint::UnsetPoint : m_plane.PointAt(p2.x,p2.y);
}


void ON_OBSOLETE_V5_Leader::AddPoint( const ON_2dPoint& point )
{
  m_points.Append( point);

}

bool ON_OBSOLETE_V5_Leader::RemovePoint( int idx )
{
  bool rc = true;
  if( idx == -1)  // -1 removes the last point
  {
    m_points.Remove();
  }
  else if( idx >= 0 && idx < m_points.Count())
  {
    m_points.Remove( idx);
  }
  else
  {
    rc = false;
  }

  return rc;
}

// April 22, 2010 Lowell - Added to support right justified text on left pointing leader tails rr64292
bool ON_OBSOLETE_V5_Leader::GetTextDirection( ON_2dVector& text_dir ) const 
{
  bool rc = false;
  const int point_count = m_points.Count();
  if ( point_count < 2 )
  {
    text_dir.Set(-1.0,0.0);
  }
  else
  {
    int i; // 20 June 2011 Fixed textdir for leaders with 2 points. rr86801
    for(i = point_count-1; i >= 1; i--)
    {
      text_dir = m_points[point_count-1] -  m_points[i-1];
      if(text_dir.Unitize())
      {
        rc = true;
        break;
      }
      text_dir.Set(-1.0,0.0);
    }
  }
  return rc;
}

bool ON_OBSOLETE_V5_Leader::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const
{
  bool rc = false;
  const int point_count = m_points.Count();
  if ( point_count < 2 )
  {
    arrowhead_dir.Set(-1.0,0.0);
  }
  else
  {
    int i;
    for ( i = 1; i < point_count; i++ )
    {
      arrowhead_dir = m_points[0] -  m_points[i];
      if ( arrowhead_dir.Unitize() )
      {
        rc = true;
        break;
      }
      arrowhead_dir.Set(-1.0,0.0);
    }
  }
  return rc;
}

bool ON_OBSOLETE_V5_Leader::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const
{
  bool rc = false;
  switch( m_points.Count())
  {
  case 0:
    arrowhead_tip.Set(0.0,0.0);
    break;
  case 1:
    arrowhead_tip = m_points[0];
    break;
  default:
    arrowhead_tip = m_points[0];
    rc = true;
    break;
  }
  return rc;
}



bool ON_OBSOLETE_V5_DimRadial::GetArrowHeadDirection( ON_2dVector& arrowhead_dir ) const
{
  bool rc = false;
  if ( m_points.Count() < 4 )
  {
    arrowhead_dir.Set(-1.0,0.0);
  }
  else
  {
    arrowhead_dir = m_points[1] - m_points[3];
    if ( 0 == (rc=arrowhead_dir.Unitize()) )
    {
      arrowhead_dir = m_points[1] - m_points[2];
      if ( 0 == (rc=arrowhead_dir.Unitize()) )
      {
        arrowhead_dir = m_points[0] - m_points[1];
        rc = arrowhead_dir.Unitize();
      }
    }
  }
  return rc;
}

bool ON_OBSOLETE_V5_DimRadial::GetArrowHeadTip( ON_2dPoint& arrowhead_tip ) const
{
  bool rc = false;
  if ( m_points.Count() >= 2 )
  {
    arrowhead_tip = m_points[1];
    rc = true;
  }
  else
  {
    arrowhead_tip.Set(0.0,0.0);
    rc = false;
  }
  return rc;
}


// class ON_TextDot
//--------------------------------------------------------------------
ON_TextDot::ON_TextDot()
{}

ON_TextDot* ON_TextDot::CreateFromV2AnnotationTextDot(
  const class ON_OBSOLETE_V2_TextDot& V2_text_dot,
  const class ON_3dmAnnotationContext* annotation_context,
  ON_TextDot* destination
)
{
  ON_wString dot_text = V2_text_dot.m_text;
  dot_text.TrimLeft();
  dot_text.TrimRight();
  ON_TextDot* text_dot
    = (nullptr != destination)
    ? destination
    : new ON_TextDot();
  text_dot->SetPrimaryText(dot_text);
  text_dot->SetCenterPoint(V2_text_dot.point);
  return text_dot;
}

ON_TextDot::ON_TextDot(
  ON_3dPoint center_point,
  const wchar_t* primary_text,
  const wchar_t* secondary_text
  )
  : m_center_point(  center_point.IsValid() ? center_point : ON_3dPoint::UnsetPoint )
  , m_primary_text(primary_text)
  , m_secondary_text(secondary_text)
{
}

ON_TextDot::~ON_TextDot()
{}

void ON_TextDot::EmergencyDestroy()
{
  m_center_point = ON_3dPoint::UnsetPoint;
  m_primary_text.EmergencyDestroy();
  m_secondary_text.EmergencyDestroy();
  m_font_face.EmergencyDestroy();
  m_height_in_points = ON_TextDot::DefaultHeightInPoints;
  m_display_bits = 0;
}

bool ON_TextDot::IsValid( 
            ON_TextLog* text_log
            ) const
{
  // 5/6/03 LW made dots with no text valid.
  if ( !m_center_point.IsValid() )
  {
    if ( 0 != text_log )
    {
      text_log->Print("ON_TextDot::CenterPoint() is not valid\n");
    }
    return false;
  }
  return true;
}

void ON_TextDot::Dump( ON_TextLog& log) const
{
  log.Print(L"Center: ");
  log.Print( m_center_point);
  log.Print(L"\n");
  log.Print(L"Primary text: \"%ls\"\n", static_cast<const wchar_t*>(m_primary_text));
  log.Print(L"Secondary text: \"%ls\"\n", static_cast<const wchar_t*>(m_secondary_text));
  log.Print(L"Height in points: %d\n", m_height_in_points);
  log.Print(L"Font face: \"%ls\"\n", static_cast<const wchar_t*>(m_font_face));

}

bool ON_TextDot::Write( ON_BinaryArchive& file) const
{
  bool rc = false;

  for (;;)
  {
    const int minor_version 
      = file.Archive3dmVersion() >= 60
      ? 1
      : 0;
    if (!file.Write3dmChunkVersion(1, minor_version))
      break;

    if (!file.WritePoint(m_center_point))
      break;
    if (!file.WriteInt(m_height_in_points))
      break;
    if (!file.WriteString(m_primary_text))
      break;
    if (!file.WriteString(m_font_face))
      break;

    // June 21, 2015
    //  Dale Lear cleaned this up so runtime bits and file format are independent.
    //  DO NOT modify this display_bits code for future bool values.
    int display_bits = 0;
    if ( AlwaysOnTop() )
      display_bits |= 1;
    if ( Transparent() )
      display_bits |= 2;
    if ( Bold() )
      display_bits |= 4;
    if (Italic() )
      display_bits |= 8;
    if (!file.WriteInt(display_bits))
      break;
    // DO NOT use display bits for future bool values


    if (minor_version >= 1)
    {
      // version 1.1 fields June 21, 2015
      if (!file.WriteString(m_secondary_text))
        break;
    }

    rc = true;
    break;
  }

  return rc;
}

bool ON_TextDot::Read( ON_BinaryArchive& file)
{
  bool rc = false;

  *this = ON_TextDot::Unset;

  int major_version = 0;
  int minor_version = 0;
  while (file.Read3dmChunkVersion(&major_version, &minor_version))
  {
    if (1 != major_version)
      break;

    ON_3dPoint center_point;
    if (!file.ReadPoint(center_point))
      break;
    SetCenterPoint(center_point);

    int height;
    if (!file.ReadInt(&height))
      break;
    SetHeightInPoints(height);

    ON_wString str;
    if (!file.ReadString( str))
      break;
    SetPrimaryText(str);

    if (!file.ReadString( str))
      break;
    SetFontFace(str);
 
    // June 21, 2015
    //  Dale Lear cleaned this up so runtime bits and file format are independent.
    // DO NOT use display bits for future bool values
    int display_bits = 0;
    if (!file.ReadInt( &display_bits))
      break;
    SetAlwaysOnTop( 0 != (1 & display_bits) );
    SetTransparent( 0 != (2 & display_bits) );
    SetBold( 0 != (4 & display_bits) );
    SetItalic( 0 != (8 & display_bits) );
    // DO NOT use display bits for future bool values

    if (minor_version >= 1)
    {
      // version 1.1 fields June 21, 2015
      if (!file.ReadString(str))
        break;
      SetSecondaryText(str);
    }

    rc = true;
    break;
  }

  return rc;
}

ON::object_type ON_TextDot::ObjectType() const
{
  return ON::text_dot;
}

int ON_TextDot::Dimension() const
{
  return 3;
}

bool ON_TextDot::GetBBox( double* box_min, double* box_max, bool grow_box /*= false*/) const
{
  return ON_GetPointListBoundingBox( 3, 0, 1, 3, &m_center_point.x, box_min, box_max, grow_box?true:false );
}

bool ON_TextDot::Transform( const ON_Xform& xform)
{
  TransformUserData( xform);
  return ON_TransformPointList( 3, 0, 1, 3, &m_center_point.x, xform);
}

bool ON_TextDot::IsDeformable() const
{
  return true;
}

bool ON_TextDot::MakeDeformable()
{
  return true;
}

ON_3dPoint ON_TextDot::CenterPoint() const
{
  return m_center_point;
}

void ON_TextDot::SetCenterPoint( ON_3dPoint center_point )
{
  m_center_point = center_point;
}

int ON_TextDot::HeightInPoints() const
{
  // in "points"
  return m_height_in_points;
}

void ON_TextDot::SetHeightInPoints( 
  int height_in_points 
  )
{
  if( height_in_points >= ON_TextDot::MinimumHeightInPoints )
    m_height_in_points = height_in_points;
}

const wchar_t* ON_TextDot::PrimaryText() const 
{
  return static_cast< const wchar_t* >(m_primary_text);
}

const wchar_t* ON_TextDot::SecondaryText() const 
{
  return static_cast< const wchar_t* >(m_secondary_text);
}

static void SetDotText(
  const wchar_t* dot_text,
  bool bCRtoCRLF,
  ON_wString& dot_string
  )
{
  if (nullptr != dot_text)
  {
    // strip leading white space
    while ( dot_text[0] > 0 && dot_text[0] <= 0x20 )
      dot_text++;
  }

  size_t len = ON_wString::Length(dot_text);
    
  // strip trailing white space
  while (len > 0 && dot_text[len-1] <= 0x20 && dot_text[len-1] > 0)
    len--;

  if( len > 0 )
  {
    wchar_t* buffer = (wchar_t*)onmalloc((2 * len + 1)*sizeof(wchar_t));
    size_t j = 0;
    for (size_t i = 0; i < len; i++)
    {
      if (bCRtoCRLF && '\r' == dot_text[i] && '\n' != dot_text[i+1])
      {
        // change \r to \r\n
        buffer[j++] = '\r';
        buffer[j++] = '\n';
      }
      else
      {
        buffer[j++] = dot_text[i];
      }
    }
    buffer[j] = 0;
    dot_string = buffer;
    onfree(buffer);
  }
  else
  {
    dot_string = ON_wString::EmptyString;
  }
}

void ON_TextDot::SetPrimaryText(const wchar_t* primary_dot_text)
{
  SetDotText(primary_dot_text,true,m_primary_text);
}

void ON_TextDot::SetSecondaryText(const wchar_t* secondary_dot_text)
{
  SetDotText(secondary_dot_text,true,m_secondary_text);
}

const wchar_t* ON_TextDot::FontFace() const
{
  return 
    m_font_face.IsEmpty()
    ? ON_TextDot::DefaultFontFace
    : static_cast< const wchar_t* >(m_font_face);
}

void ON_TextDot::SetFontFace( const wchar_t* font_face )
{
  SetDotText(font_face, true, m_font_face);
}

static void SetDisplayBitsFromBool(
  bool b,
  unsigned int bit, 
  unsigned int& display_bits
  )
{
  if (b)
    display_bits |= bit;
  else
    display_bits &= (~bit);
}

static bool DisplayBitsToBool(
  unsigned int display_bits,
  unsigned int bit
  )
{
  return (bit == (display_bits & bit) ? true : false);
}

void ON_TextDot::SetAlwaysOnTop(
  bool bAlwaysOnTop
  )
{
  SetDisplayBitsFromBool(bAlwaysOnTop,1,m_display_bits);
}

bool ON_TextDot::AlwaysOnTop() const
{
  return DisplayBitsToBool(m_display_bits,1);
}

void ON_TextDot::SetTransparent(
  bool bTransparent
  )
{
  SetDisplayBitsFromBool(bTransparent,2,m_display_bits);
}

bool ON_TextDot::Transparent() const
{
  return DisplayBitsToBool(m_display_bits,2);
}

void ON_TextDot::SetBold(
  bool bBold
  )
{
  SetDisplayBitsFromBool(bBold,4,m_display_bits);
}

bool ON_TextDot::Bold() const
{
  return DisplayBitsToBool(m_display_bits,4);
}

void ON_TextDot::SetItalic(bool bItalic)
{
  SetDisplayBitsFromBool(bItalic,8,m_display_bits);
}

bool ON_TextDot::Italic() const
{
  return DisplayBitsToBool(m_display_bits,8);
}

//////// deprecated functions /////////
const ON_3dPoint& ON_TextDot::Point() const
{
  //Perform same functionality as CenterPoint
  return m_center_point;
}
void ON_TextDot::SetPoint(const ON_3dPoint& point)
{
  SetCenterPoint(point);
}
const wchar_t* ON_TextDot::TextString() const
{
  return PrimaryText();
}
void ON_TextDot::SetTextString(const wchar_t* string)
{
  SetPrimaryText(string);
}
////////////////////////////////////////


ON_OBSOLETE_V5_AnnotationText::ON_OBSOLETE_V5_AnnotationText()
{
  memset(&m_rect,0,sizeof(m_rect));
}

ON_OBSOLETE_V5_AnnotationText::~ON_OBSOLETE_V5_AnnotationText()
{
}

ON_OBSOLETE_V5_AnnotationText& ON_OBSOLETE_V5_AnnotationText::operator=(const char* s)
{
  SetText(s);
  return *this;
}

ON_OBSOLETE_V5_AnnotationText& ON_OBSOLETE_V5_AnnotationText::operator=(const wchar_t* s)
{
  SetText(s);
  return *this;
}

void ON_OBSOLETE_V5_AnnotationText::SetText(const char* s)
{
  ON_wString::operator=(s);
  memset(&m_rect,0,sizeof(m_rect));
}

void ON_OBSOLETE_V5_AnnotationText::SetText(const wchar_t* s)
{
  ON_wString::operator=(s);
  memset(&m_rect,0,sizeof(m_rect));
}

bool ON_OBSOLETE_V5_Annotation::GetTextXform( 
      ON_OBSOLETE_V5_RECT gdi_text_rect,
      const ON_Font& font,
      const ON_DimStyle* dimstyle,
      double dimscale,
      const ON_Viewport* vp,
      const ON_Xform* model_xform,
      ON_Xform& xform
      ) const
{
  int gdi_height_of_I = font.FontMetrics().AscentOfI();
  const double textheight = dimstyle ? dimstyle->TextHeight() : m_textheight;
  double textgap =  dimstyle ? dimstyle->TextGap() : 0.0;
  const ON_INTERNAL_OBSOLETE::V5_TextDisplayMode textalignment 
    = dimstyle ? ON_INTERNAL_OBSOLETE::V5TextDisplayModeFromV6DimStyle(*dimstyle) 
    : ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal;
  const ON_3dVector cameraX = (vp) ? vp->CameraX() : m_plane.xaxis;
  const ON_3dVector cameraY = (vp) ? vp->CameraY() : m_plane.yaxis;
  if(dimstyle)
  {
    // - Oct 4, 07 LW Get correct text gap using 
    // multi-line tolerance text since GetTextXform doesn't do that.
    if(
      ( ON_DimStyle::tolerance_format::Deviation == dimstyle->ToleranceFormat() || ON_DimStyle::tolerance_format::Limits == dimstyle->ToleranceFormat() ) 
      &&
      ( Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear || Type() == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned) )
        textgap += textheight * 0.5;
  }
  return GetTextXform( 
      gdi_text_rect,
      gdi_height_of_I,
      textheight, textgap, textalignment,
      dimscale,
      cameraX, cameraY, 
      model_xform,
      xform
      );
}



static bool GetLeaderEndAndDirection( const ON_OBSOLETE_V5_Annotation* pAnn,
                                     ON_2dPoint& E,
                                     ON_2dVector& R )
{
  bool rc = false;

  ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = pAnn->m_type;
  const ON_2dPointArray& ann_m_points = pAnn->m_points;


  R.Set(1.0,0.0); // unit vector points to end
  E.Set(0.0,0.0); // end point

  if ( ann_m_points.Count() >= 4 && (ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type) )
  {
    E = ann_m_points[2]; // end of radial dimension
    R = E - ann_m_points[3];
    if ( !R.Unitize() )
    {
      R = E - ann_m_points[1];
      if ( !R.Unitize() )
      {
        R = E - ann_m_points[0];
        if ( !R.Unitize() )
        {
          R.Set(1.0,0.0);
        }
      }
    }
    rc = true;
  }
  else if ( ann_m_points.Count() >= 2 && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type )
  {
    int i;
    E = *ann_m_points.Last();
    for (i = ann_m_points.Count()-2; i >= 0; i-- )
    {
      R = E - ann_m_points[i];
      if ( R.Unitize() )
      {
        break;
      }
      R.Set(1.0,0.0);
    }
    rc = true;
  }
  else if ( ann_m_points.Count() >= 2 && ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type )
  {
    E = ann_m_points[1];

    int direction = (( ON_OBSOLETE_V5_DimOrdinate*)pAnn)->Direction();
    if( direction == -1)
    {
      if( fabs( ann_m_points[1].x - ann_m_points[0].x) 
       <= fabs( ann_m_points[1].y - ann_m_points[0].y))
        direction = 0;
      else
        direction = 1;
    }

    if( direction == 0)
      R.Set( 0.0, ann_m_points[1].y - ann_m_points[0].y);
    else
      R.Set( ann_m_points[1].x - ann_m_points[0].x, 0.0);

    if( !R.Unitize())
      R.Set(1.0,0.0);

    rc = true;
  }

  return rc;
}

// Oct 30, 07 - LW
// This function should not be used any longer
bool ON_OBSOLETE_V5_Annotation::GetTextXform(
  ON_OBSOLETE_V5_RECT gdi_text_rect,
  int gdi_height_of_I,
  double dimstyle_textheight,
  double dimstyle_textgap,
  ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment,
  double dimscale,
  ON_3dVector cameraX,
  ON_3dVector cameraY,
  ON_Xform& xform
  ) const
{
  ON_ERROR("This function should not be used. Use the version that takes a model transform argument.");
  return false;
}
  //const ON_OBSOLETE_V5_Annotation* ann = this;

  //const ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = ann->m_type;

  //if ( 0 == gdi_height_of_I )
  //{
  //  // Default to height of Ariel 'I'
  //  gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256;
  //}

  //if ( 0.0 == dimscale )
  //{
  //  dimscale = 1.0;
  //}

  //dimstyle_textheight *= dimscale;
  //dimstyle_textgap *= dimscale;

  //double textheight = ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type )
  //                  ? m_textheight*dimscale
  //                  : dimstyle_textheight;
  //if ( 0.0 == textheight )
  //  textheight = 1.0;

  //ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY );
  //if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON )
  //{
  //  cameraZ.Unitize();
  //}

  //// This xform is a scale from Windows gdi coordinates 
  //// to annotation plane coordinates.
  //const double gdi_to_plane_scale = textheight/gdi_height_of_I;
  //ON_Xform gdi_to_plane(1.0);
  //gdi_to_plane.m_xform[0][0] =  gdi_to_plane_scale;
  //gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale;

  //// width and height of text line in Rhino units.
  //const double text_line_width  = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left);
  ////const double text_line_height = gdi_to_plane_scale*(gdi_text_rect.bottom - gdi_text_rect.top);

  //if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type )
  //{
  //  // The orientation of the text is text blocks
  //  // does not depend on the view or text alignment
  //  // settings.  The position and orientation of 
  //  // the text in every other annotation depends on
  //  // the view and text alignment settings.  
  //  //
  //  // It simplifies the code for the rest of the
  //  // annotation settings to quickly deal with text
  //  // blocks here.
  //  ON_Xform plane_to_world(1.0);
  //  plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
  //  xform = plane_to_world*gdi_to_plane;
  //  return true;
  //}


  //// text_position_mode
  ////   1 = linear, aligned, or anglular dimension
  ////       (dimension definition determines center point of text box)
  ////   2 = radial, diameter, leader
  ////       (dimension definition determined end point of text box)
  //int position_style = 0;
  //switch( ann_type )
  //{
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
  //  // dimension definition determines center point of text box
  //  position_style = 1;
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
  //  // dimension definition determines end of text box
  //  position_style = 2;
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing:
  //  break;
  //}


  //// This translation puts the center of the fist line of text at
  //// (0,0) in the annotation's plane.
  //if ( ON::dtHorizontal != dimstyle_textalignment || 1 == position_style )
  //{

  //  gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
  //  gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
  //}
  //gdi_to_plane.m_xform[1][3] = -0.5*textheight;

  //if ( ON::dtHorizontal != dimstyle_textalignment )
  //{
  //  if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) )
  //  {
  //    // Viewing dimension from the backside
  //    ON_Xform flip(1.0);
  //    switch ( position_style )
  //    {
  //    case 1: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular
  //      flip.m_xform[0][0] = -1.0;
  //      flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right;
  //      break;

  //    case 2: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader
  //      flip.m_xform[1][1] = -1.0;
  //      flip.m_xform[1][3] = gdi_text_rect.top + gdi_text_rect.bottom;
  //      break;
  //    }
  //    gdi_to_plane = gdi_to_plane*flip;
  //  }
  //}

  //// text_centering_rotation rotates about the "center".  Angular,
  //// radial, and leader dimensions use this rotation.
  //ON_2dVector text_centering_rotation(1.0,0.0);

  //// text_centering_translation is a small translation deals with
  //// text that is above or to the right of the "center" point.  
  //// It is no larger than dimstyle_gap + 1/2 the size of the
  //// text's bounding box.
  //ON_2dVector text_centering_translation(0.0,0.0);

  //double x, y;

  //if ( ON::dtHorizontal != dimstyle_textalignment )
  //{
  //  if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear  == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == ann_type )
  //  {
  //    if ( ON::dtAboveLine == dimstyle_textalignment )
  //    {
  //      text_centering_translation.y =  0.5*textheight+dimstyle_textgap;
  //    }
  //    y =  ann->m_plane.yaxis*cameraY;
  //    x = -ann->m_plane.yaxis*cameraX;
  //    if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
  //    {
  //      y = x;
  //    }
  //    if ( y < 0.0 )
  //    {
  //      text_centering_translation.Reverse();
  //      text_centering_rotation.Reverse(); // rotate 180 degrees
  //    }
  //  }
  //  else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular == ann_type )
  //  {
  //    // This transform rotates the text in the annotation plane.
  //    const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann);
  //    if ( 0 != angular_dim )
  //    {
  //      double a = 0.5*angular_dim->m_angle;
  //      ON_2dVector R(cos(a),sin(a));
  //      a -= 0.5*ON_PI;
  //      text_centering_rotation.x = cos(a);
  //      text_centering_rotation.y = sin(a);
  //      ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
  //      x = V*cameraX;
  //      y = V*cameraY;
  //      if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
  //      {
  //        y = -x;
  //      }
  //      if ( y < 0.0 )
  //      {
  //        text_centering_rotation.Reverse(); // add another 180 degrees of rotation
  //      }

  //      if ( ON::dtAboveLine == dimstyle_textalignment )
  //      {
  //        y = 0.5*textheight + dimstyle_textgap;
  //        text_centering_translation.x = -y*text_centering_rotation.y;
  //        text_centering_translation.y =  y*text_centering_rotation.x;
  //      }
  //    }
  //  }
  //  else if (    ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type 
  //            || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type 
  //            || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type 
  //            || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type)
  //  {
  //    ON_2dPoint E(0.0,0.0); // end point
  //    ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
  //    GetLeaderEndAndDirection( this, E, R );

  //    text_centering_rotation = R;

  //    text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation;

  //    ON_3dVector V = text_centering_rotation.x*m_plane.xaxis + text_centering_rotation.y*m_plane.yaxis;
  //    x = V*cameraX;
  //    y = V*cameraY;
  //    if ( fabs(x) <= ON_SQRT_EPSILON && fabs(y) > ON_SQRT_EPSILON )
  //    {
  //      x = y;
  //    }
  //    if ( x < 0.0 )
  //    {
  //      text_centering_rotation.Reverse(); // rotate 180 degrees
  //    }
  //  }
  //}

  //ON_Xform text_centering_xform(1.0);
  //text_centering_xform.m_xform[0][0] =  text_centering_rotation.x;
  //text_centering_xform.m_xform[0][1] = -text_centering_rotation.y;
  //text_centering_xform.m_xform[1][0] =  text_centering_rotation.y;
  //text_centering_xform.m_xform[1][1] =  text_centering_rotation.x;
  //// Since the translation happens after the rotation about (0,0),
  //// we can just tack it on here.
  //text_centering_xform.m_xform[0][3] =  text_centering_translation.x;
  //text_centering_xform.m_xform[1][3] =  text_centering_translation.y;

  //// This transform translates the text in the annotation plane
  //// It can be a large translation
  //ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset()
  //switch( ann_type )
  //{
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
  //  if ( m_points.Count() >= ON_OBSOLETE_V5_DimLinear::dim_pt_count )
  //  {
  //    const ON_OBSOLETE_V5_DimLinear* linear_dim = ON_OBSOLETE_V5_DimLinear::Cast(ann);
  //    if ( linear_dim )
  //    {
  //      text_offset_translation = linear_dim->Dim2dPoint(ON_OBSOLETE_V5_DimLinear::text_pivot_pt);
  //    }
  //  }
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
  //  if ( m_points.Count() >= ON_OBSOLETE_V5_DimAngular::dim_pt_count )
  //  {
  //    const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann);
  //    if ( angular_dim )
  //    {
  //      text_offset_translation = angular_dim->Dim2dPoint(ON_OBSOLETE_V5_DimAngular::text_pivot_pt);
  //    }
  //  }
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
  //  if ( m_points.Count() >= ON_OBSOLETE_V5_DimRadial::dim_pt_count )
  //  {
  //    // No user positioned text on radial dimensions.
  //    text_offset_translation = m_points[ON_OBSOLETE_V5_DimRadial::tail_pt_index];
  //  }
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
  //  if ( m_points.Count() > 0 )
  //  {
  //    // No user positioned text on leaders.
  //    text_offset_translation = *m_points.Last();
  //  }
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
  //  if ( m_points.Count() == 2 )
  //  {
  //    // No user positioned text on leaders.
  //    text_offset_translation = m_points[1];
  //  }
  //  break;

  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
  //case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing:
  //  break;
  //}

  //ON_Xform plane_translation(1.0);
  //plane_translation.m_xform[0][3] = text_offset_translation.x;
  //plane_translation.m_xform[1][3] = text_offset_translation.y;

  //// this transform maps a point in the annotation plane to world coordinates
  //ON_Xform plane_to_world(1.0);
  //plane_to_world.Rotation(ON_xy_plane,ann->m_plane);

  //ON_Xform horizonal_xform(1.0);
  //if ( ON::dtHorizontal == dimstyle_textalignment )
  //{
  //  ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y);
  //  horizonal_xform.Rotation( 
  //      fixed_point,
  //      ann->m_plane.xaxis,
  //      ann->m_plane.yaxis,
  //      ann->m_plane.zaxis,
  //      fixed_point,
  //      cameraX,
  //      cameraY,
  //      cameraZ
  //      );

  //  if ( 2 == position_style )
  //  {
  //    // leaders, radial, and diameter
  //    ON_2dPoint E(0.0,0.0); // end point
  //    ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
  //    GetLeaderEndAndDirection( this, E, R );
  //    ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
  //    x = V*cameraX;
  //    y = ( x > -ON_SQRT_EPSILON )
  //      ? dimstyle_textgap
  //      : -(dimstyle_textgap + text_line_width);
  //    V = y*cameraX;
  //    horizonal_xform.m_xform[0][3] += V.x;
  //    horizonal_xform.m_xform[1][3] += V.y;
  //    horizonal_xform.m_xform[2][3] += V.z;
  //  }
  //}

  //ON_Xform gdi_to_world;
  //gdi_to_world = horizonal_xform
  //              * plane_to_world
  //              * plane_translation
  //              * text_centering_xform
  //              * gdi_to_plane;

  //xform = gdi_to_world;

  //return true;
//}

//static bool do_plane_translation = true;
//static bool do_text_centering_xform = true;
//static bool do_text_centering_rotation = true;
//static bool do_text_centering_translation = true;
//static bool do_mirror_flip = true;
//static bool do_flip_x = true;
//static bool do_flip_y = true;

// New function added Oct 30, 07 - LW 
// To use model xform to draw annotation in blocks correctly
bool ON_OBSOLETE_V5_Annotation::GetTextXform( 
      ON_OBSOLETE_V5_RECT gdi_text_rect,
      int gdi_height_of_I,
      double dimstyle_textheight,
      double dimstyle_textgap,
      ON_INTERNAL_OBSOLETE::V5_TextDisplayMode dimstyle_textalignment,
      double dimscale,
      ON_3dVector cameraX,
      ON_3dVector cameraY,
      const ON_Xform* model_xform,
      ON_Xform& xform
      ) const
{
  ON_Xform mxi;
  if( model_xform)
  {
    mxi = model_xform->Inverse();
    cameraX.Transform( mxi);
    cameraY.Transform( mxi);
  }
  const ON_OBSOLETE_V5_Annotation* ann = this;

  const ON_INTERNAL_OBSOLETE::V5_eAnnotationType ann_type = ann->m_type;

  if ( 0 == gdi_height_of_I )
  {
    // Default to height of Ariel 'I'
    gdi_height_of_I = (165*ON_Font::Constants::AnnotationFontCellHeight)/256;
  }

  if ( 0.0 == dimscale )
  {
    dimscale = 1.0;
  }

  dimstyle_textheight *= dimscale;
  dimstyle_textgap *= dimscale;

  double textheight = ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type )
                    ? m_textheight*dimscale
                    : dimstyle_textheight;
  if ( 0.0 == textheight )
    textheight = 1.0;

  ON_3dVector cameraZ = ON_CrossProduct( cameraX, cameraY );
  if ( fabs( 1.0 - cameraZ.Length() ) > ON_SQRT_EPSILON )
  {
    cameraZ.Unitize();
  }

  // This xform is a scale from Windows gdi coordinates 
  // to annotation plane coordinates.
  const double gdi_to_plane_scale = textheight/gdi_height_of_I;
  ON_Xform gdi_to_plane(ON_Xform::IdentityTransformation);
  gdi_to_plane.m_xform[0][0] =  gdi_to_plane_scale;
  gdi_to_plane.m_xform[1][1] = -gdi_to_plane_scale;

  // width and height of text line in Rhino units.
  const double text_line_width  = gdi_to_plane_scale*(gdi_text_rect.right - gdi_text_rect.left);
  //const double text_line_height = gdi_to_plane_scale*(gdi_text_rect.bottom - gdi_text_rect.top);

  if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock == ann_type )
  {
    // The orientation of the text is text blocks
    // does not depend on the view or text alignment
    // settings.  The position and orientation of 
    // the text in every other annotation depends on
    // the view and text alignment settings.  
    //
    // It simplifies the code for the rest of the
    // annotation settings to quickly deal with text
    // blocks here.
    ON_Xform plane_to_world(ON_Xform::IdentityTransformation);
    plane_to_world.Rotation(ON_xy_plane,ann->m_plane);
    xform = plane_to_world*gdi_to_plane;
    return true;
  }

  // text_position_mode
  //   1 = linear, aligned, or anglular dimension
  //       (dimension definition determines center point of text box)
  //   2 = radial, diameter, leader
  //       (dimension definition determined end point of text box)
  int position_style = 0;
  switch( ann_type )
  {
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
    // dimension definition determines center point of text box
    position_style = 1;
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
    if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == dimstyle_textalignment)
      position_style = 1;
    else
      position_style = 2;
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
    // dimension definition determines end of text box
    position_style = 2;
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing:
    break;
  }

  // This translation puts the center of the fist line of text at
  // (0,0) in the annotation's plane.
  if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment || 1 == position_style)
  {
    if((m_justification & tjRight) == tjRight)
      gdi_to_plane.m_xform[0][3] = 0.5*text_line_width;
    else
      gdi_to_plane.m_xform[0][3] = -0.5*text_line_width;
  }
  gdi_to_plane.m_xform[1][3] = -0.5*textheight;

  if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment)
  {
    if ( ((cameraZ*m_plane.zaxis) < -ON_SQRT_EPSILON) )
    {
      // Viewing dimension from the backside
      ON_Xform flip(ON_Xform::IdentityTransformation);
      switch ( position_style )
      {
      case 1: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular
        flip.m_xform[0][0] = -1.0;
        flip.m_xform[0][3] = gdi_text_rect.left + gdi_text_rect.right;
        break;

      case 2: // ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius, ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader
            //flip.m_xform[1][1] = -1.0;
            //flip.m_xform[1][3] = -(gdi_text_rect.top + gdi_text_rect.bottom);
        break;
      }
      gdi_to_plane = gdi_to_plane*flip;
    }
  }

  // text_centering_rotation rotates about the "center".  Angular,
  // radial, and leader dimensions use this rotation.
  ON_2dVector text_centering_rotation(1.0,0.0);

  // text_centering_translation is a small translation deals with
  // text that is above or to the right of the "center" point.  
  // It is no larger than dimstyle_gap + 1/2 the size of the
  // text's bounding box.
  ON_2dVector text_centering_translation(0.0,0.0);
  double x = 1.0, y = 1.0;
  ON_Xform xfs(ON_Xform::IdentityTransformation);

  if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen != dimstyle_textalignment)
  {
    if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear  == ann_type || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned == ann_type )
    {
      if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == dimstyle_textalignment)
      {
        text_centering_translation.y =  0.5*textheight+dimstyle_textgap;
      }
      y =  ann->m_plane.yaxis*cameraY;
      x = -ann->m_plane.yaxis*cameraX;
      if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
      {
        y = x;
      }
      if ( y < 0.0 )
      {
        text_centering_translation = -text_centering_translation;
        text_centering_rotation = -text_centering_rotation; // rotate 180 degrees
      }
    }
    else if ( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular == ann_type )
    {
      // This transform rotates the text in the annotation plane.
      const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann);
      if ( 0 != angular_dim )
      {
        double a = 0.5*angular_dim->m_angle;
        ON_2dVector R(cos(a),sin(a));
        a -= 0.5*ON_PI;
        text_centering_rotation.x = cos(a);
        text_centering_rotation.y = sin(a);
        ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
        x = V*cameraX;
        y = V*cameraY;
        if ( fabs(y) <= ON_SQRT_EPSILON && fabs(x) > ON_SQRT_EPSILON )
        {
          y = -x;
        }
        if ( y < 0.0 )
        {
          text_centering_rotation = -text_centering_rotation; // add another 180 degrees of rotation
        }

        if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == dimstyle_textalignment)
        {
          y = 0.5*textheight + dimstyle_textgap;
          text_centering_translation.x = -y*text_centering_rotation.y;
          text_centering_translation.y =  y*text_centering_rotation.x;
        }
      }
    }
    else if (    ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter == ann_type 
              || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius == ann_type 
              || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader == ann_type 
              || ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate == ann_type)
    {
      // 30 Jan, 2015 - redid this again rh-29493, rh-29540
      ON_2dPoint E(0.0,0.0); // end point
      ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
      GetLeaderEndAndDirection( this, E, R );
      text_centering_rotation = R;
      text_centering_translation = (dimstyle_textgap + 0.5*text_line_width)*text_centering_rotation;

      ON_3dPoint p0 = m_plane.origin;
      ON_3dPoint prx = m_plane.PointAt(R.x, R.y);
      ON_3dPoint pry = m_plane.PointAt(-R.y, R.x);

      ON_3dVector xr = prx - p0;  // 3d direction of text x
      ON_3dVector yr = pry - p0;  // 3d direction of text y

      if (xr * cameraX <= ON_ZERO_TOLERANCE)     // 22 April 2015 - Lowell - Fixed rh-30239
        xfs.m_xform[0][0] = -1.0;
      if (yr * cameraY <= ON_ZERO_TOLERANCE)
        xfs.m_xform[1][1] = -1.0;
    }
  }
  else if(ann_type == ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader)
  {
    if((m_justification & tjRight) == tjRight)
      text_centering_translation.Set(-(dimstyle_textgap + 0.5*text_line_width), 0.0);
    else if((m_justification & tjLeft) == tjLeft)
      text_centering_translation.Set(dimstyle_textgap + 0.5*text_line_width, 0.0);
  }

  ON_Xform text_centering_xform(ON_Xform::IdentityTransformation);
  text_centering_xform.m_xform[0][0] =  text_centering_rotation.x;
  text_centering_xform.m_xform[0][1] = -text_centering_rotation.y;
  text_centering_xform.m_xform[1][0] =  text_centering_rotation.y;
  text_centering_xform.m_xform[1][1] =  text_centering_rotation.x; 

  text_centering_xform = text_centering_xform * xfs;

  // Since the translation happens after the rotation about (0,0),
  // we can just tack it on here.
  text_centering_xform.m_xform[0][3] =  text_centering_translation.x;
  text_centering_xform.m_xform[1][3] =  text_centering_translation.y;

  // This transform translates the text in the annotation plane
  // from the plane origin to the final location of the annotation text
  // It can be a large translation
  ON_2dVector text_offset_translation(0.0,0.0); // CRhinoText::Offset() = text->Offset()
  switch( ann_type )
  {
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
    if ( m_points.Count() >= ON_OBSOLETE_V5_DimLinear::dim_pt_count )
    {
      const ON_OBSOLETE_V5_DimLinear* linear_dim = ON_OBSOLETE_V5_DimLinear::Cast(ann);
      if ( linear_dim )
      {
        text_offset_translation = linear_dim->Dim2dPoint(ON_OBSOLETE_V5_DimLinear::text_pivot_pt);
      }
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
    if ( m_points.Count() >= ON_OBSOLETE_V5_DimAngular::dim_pt_count )
    {
      const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(ann);
      if ( angular_dim )
      {
        text_offset_translation = angular_dim->Dim2dPoint(ON_OBSOLETE_V5_DimAngular::text_pivot_pt);
      }
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
    if ( m_points.Count() >= ON_OBSOLETE_V5_DimRadial::dim_pt_count )
    {
      // No user positioned text on radial dimensions.
      text_offset_translation = m_points[ON_OBSOLETE_V5_DimRadial::tail_pt_index];
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
    if ( m_points.Count() > 0 )
    {
      // No user positioned text on leaders.
      text_offset_translation = *m_points.Last();
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
    if ( m_points.Count() == 2 )
    {
      // No user positioned text on leaders.
      text_offset_translation = m_points[1];
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing:
    break;
  }

  ON_Xform plane_translation(ON_Xform::IdentityTransformation);
  plane_translation.m_xform[0][3] = text_offset_translation.x;
  plane_translation.m_xform[1][3] = text_offset_translation.y;

  // this transform maps a point in the annotation plane to world coordinates
  ON_Xform plane_to_world(ON_Xform::IdentityTransformation);
  plane_to_world.Rotation(ON_xy_plane,ann->m_plane);

  ON_Xform horizonal_xform(ON_Xform::IdentityTransformation);
  if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kHorizontalToScreen == dimstyle_textalignment)
  {
    ON_3dPoint fixed_point = ann->m_plane.PointAt(text_offset_translation.x,text_offset_translation.y);
    horizonal_xform.Rotation( 
        fixed_point,
        ann->m_plane.xaxis,
        ann->m_plane.yaxis,
        ann->m_plane.zaxis,
        fixed_point,
        cameraX,
        cameraY,
        cameraZ
        );

    if ( 2 == position_style )
    {
      // leaders, radial, and diameter
      ON_2dPoint E(0.0,0.0); // end point
      ON_2dVector R(1.0,0.0); // unit vector from penultimate point to end point
      GetLeaderEndAndDirection( this, E, R );
      ON_3dVector V = R.x*m_plane.xaxis + R.y*m_plane.yaxis;
      x = V*cameraX;
      y = ( x > -ON_SQRT_EPSILON )
        ? dimstyle_textgap
        : -(dimstyle_textgap + text_line_width);
      V = y*cameraX;
      horizonal_xform.m_xform[0][3] += V.x;
      horizonal_xform.m_xform[1][3] += V.y;
      horizonal_xform.m_xform[2][3] += V.z;
    }
  }

  ON_Xform gdi_to_world;

  gdi_to_world = horizonal_xform
               * plane_to_world
               * plane_translation
               * text_centering_xform
               * gdi_to_plane;

  xform = gdi_to_world;

  return true;
}

bool ON_OBSOLETE_V5_Annotation::GetTextPoint( ON_2dPoint& text_2d_point ) const
{
  bool rc = false;
  switch ( m_type )
  {
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock:
    text_2d_point.Set(0.0,0.0);
    rc = true;
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned:
    if ( m_userpositionedtext )
    {
      if ( m_points.Count() >= 5 )
      {
        text_2d_point = m_points[4];
        rc = true;
      }
    }
    else if ( m_points.Count() >= 3 )
    {
      text_2d_point.x = 0.5*(m_points[0].x + m_points[2].x);
      text_2d_point.y = m_points[2].y;
      rc = true;
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader:
    if ( m_points.Count() > 0 )
    {
      text_2d_point = *m_points.Last();
      rc = true;
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular:
    {
      const ON_OBSOLETE_V5_DimAngular* angular_dim = ON_OBSOLETE_V5_DimAngular::Cast(this);
      if ( angular_dim )
      {
        if ( m_userpositionedtext )
        {
          if ( m_points.Count() >= 0 )
          {
            text_2d_point = m_points[0];
          }
        }
        else
        {
          text_2d_point.x = angular_dim->m_radius*cos(angular_dim->m_angle);
          text_2d_point.y = angular_dim->m_radius*sin(angular_dim->m_angle);
          rc = true;
        }
      }
    }
    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter:
    // no user positioned text
    if ( m_points.Count() >= 3 )
    {
      text_2d_point = m_points[2];
      rc = true;
    }

    break;

  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimOrdinate:
  case ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtNothing:
    break;
  }
  return rc;
}


////////////////////////////////////////////////////////////
//
// do not copy or export this class definition.
//
class /*NEVER PUT THIS CLASS IN THE SDK*/ ON_AnnotationTextFormula : public ON_UserData
{
#if !defined(BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926)
#error Never copy this class definition or put this definition in a header file!
#endif
  ON_OBJECT_DECLARE(ON_AnnotationTextFormula);
public:
  ON_AnnotationTextFormula();
  ~ON_AnnotationTextFormula();
  // NO! - do not add IO support to this userdata! // bool Write(ON_BinaryArchive&) const;
  // NO! - do not add IO support to this userdata! // bool Read(ON_BinaryArchive&);
  bool GetDescription(ON_wString&) override;
  // NO! - do not add IO support to this userdata! // bool Archive() const; 
  static ON_AnnotationTextFormula* Get(const ON_OBSOLETE_V5_Annotation*);
  static void Set(ON_OBSOLETE_V5_Annotation*,const wchar_t* text_formula);

  ON_wString m_text_formula;
};

#undef BOZO_VACCINE_699FCC4262D4488c9109F1B7A37CE926

ON_OBJECT_IMPLEMENT(ON_AnnotationTextFormula,ON_UserData,"699FCC42-62D4-488c-9109-F1B7A37CE926");

ON_AnnotationTextFormula::~ON_AnnotationTextFormula()
{}

ON_AnnotationTextFormula::ON_AnnotationTextFormula()
{
  m_userdata_uuid = ON_CLASS_ID(ON_AnnotationTextFormula);
  m_application_uuid = ON_opennurbs5_id;
  m_userdata_copycount = 1;
}

bool ON_AnnotationTextFormula::GetDescription( ON_wString& description )
{
  description = "Annotation Text Formula";
  return true;
}

ON_AnnotationTextFormula* ON_AnnotationTextFormula::Get(const ON_OBSOLETE_V5_Annotation* p)
{
  return (0 != p)
         ? ON_AnnotationTextFormula::Cast(p->GetUserData(ON_CLASS_ID(ON_AnnotationTextFormula)))
         : 0;
}

void ON_AnnotationTextFormula::Set(ON_OBSOLETE_V5_Annotation* p,const wchar_t* text_formula)
{
  if ( 0 != p )
  {
    ON_AnnotationTextFormula* tf = Get(p);
    if ( 0 == text_formula || 0 == text_formula[0] )
    {
      if (0 != tf )
        delete tf; 
    }
    else
    {
      if ( 0 == tf )
      {
        tf = new ON_AnnotationTextFormula();
        p->AttachUserData(tf);
      }
      tf->m_text_formula = text_formula;
    }
  }
}
//
// do not copy or export this class definition.
//
////////////////////////////////////////////////////////////

void ON_OBSOLETE_V5_Annotation::SetTextValue( const wchar_t* text_value )
{
  m_usertext = text_value; 
}

const wchar_t* ON_OBSOLETE_V5_Annotation::TextValue() const
{
  return ((const wchar_t*)m_usertext);
}

void ON_OBSOLETE_V5_Annotation::SetTextFormula( const wchar_t* text_formula )
{
  ON_AnnotationTextFormula::Set(this,text_formula);
}

const wchar_t* ON_OBSOLETE_V5_Annotation::TextFormula() const
{
  const ON_AnnotationTextFormula* tf = ON_AnnotationTextFormula::Get(this);
  return (0 != tf) ? ((const wchar_t*)tf->m_text_formula) : TextValue();
}

bool ON_BinaryArchive::Internal_WriteV2AnnotationObject(
  const ON_OBSOLETE_V5_Annotation& V5_annotation,
  const ON_3dmAnnotationContext* annotation_context
)
{
  if (m_3dm_version != 1 && m_3dm_version != 2)
  {
    ON_ERROR("m_3dm_version must be 1 or 2");
    return false;
  }


  const ON_DimStyle* dim_style = nullptr;
  int archive_dim_style_index = V5_annotation.V5_3dmArchiveDimStyleIndex();
  if ( archive_dim_style_index >= 0 && archive_dim_style_index < m_archive_dim_style_table.Count())
    dim_style = m_archive_dim_style_table[archive_dim_style_index];

  if (nullptr == dim_style)
  {
    dim_style = &ArchiveCurrentDimStyle();
    archive_dim_style_index = ArchiveCurrentDimStyleIndex();
  }

  //const int V5_3dm_archive_dim_style_index = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle, dim_style->Id()).Index();

  m_annotation_context.SetReferencedDimStyle(dim_style,nullptr,archive_dim_style_index);
  if (nullptr == annotation_context)
    annotation_context = &m_annotation_context;
  
  ON_OBSOLETE_V2_Annotation* V2_annotation = ON_OBSOLETE_V2_Annotation::CreateFromV5Annotation(
    V5_annotation,
    annotation_context
  );
  bool rc
    = (nullptr != V2_annotation)
    ? Internal_WriteObject(*V2_annotation)
    : Internal_WriteObject(V5_annotation);
  if (nullptr != V2_annotation)
    delete V2_annotation;
  return rc;
}


ON_INTERNAL_OBSOLETE::V5_vertical_alignment ON_INTERNAL_OBSOLETE::V5VerticalAlignmentFromV5Justification(
    unsigned int v5_justification_bits
  )
{
  ON_INTERNAL_OBSOLETE::V5_vertical_alignment valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Bottom;

  const unsigned int v5_valign_mask
    = ON_OBSOLETE_V5_TextObject::eTextJustification::tjBottom
    | ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle
    | ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop;

  if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjTop == (v5_justification_bits & v5_valign_mask))
    valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Top;
  else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjMiddle == (v5_justification_bits & v5_valign_mask))
    valign = ON_INTERNAL_OBSOLETE::V5_vertical_alignment::Middle;

  return valign;
}

ON_INTERNAL_OBSOLETE::V5_horizontal_alignment ON_INTERNAL_OBSOLETE::V5HorizontalAlignmentFromV5Justification(
    unsigned int v5_justification_bits
  )
{
  ON_INTERNAL_OBSOLETE::V5_horizontal_alignment halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Left;

  const unsigned int v5_halign_mask
    = ON_OBSOLETE_V5_TextObject::eTextJustification::tjLeft
    | ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter
    | ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight;

  if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjCenter == (v5_justification_bits & v5_halign_mask))
    halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Center;
  else if (ON_OBSOLETE_V5_TextObject::eTextJustification::tjRight == (v5_justification_bits & v5_halign_mask))
    halign = ON_INTERNAL_OBSOLETE::V5_horizontal_alignment::Right;

  return halign;
}



