/* $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

#include "opennurbs_internal_defines.h"

ON_VIRTUAL_OBJECT_IMPLEMENT(ON_Dimension, ON_Annotation, "EE6571FE-1596-4D5B-BD6D-7072B0643986");
ON_OBJECT_IMPLEMENT(ON_DimLinear, ON_Dimension, "E550882B-F44D-4154-A1EF-6E50CBBBF543");
ON_OBJECT_IMPLEMENT(ON_DimAngular, ON_Dimension, "D417786B-F6CD-4F12-9E1F-063F414DBEB6");
ON_OBJECT_IMPLEMENT(ON_DimRadial, ON_Dimension, "FC749C2F-4C00-41FD-9840-26D94F047AD3");
ON_OBJECT_IMPLEMENT(ON_DimOrdinate, ON_Dimension, "03124828-4C9B-4D28-9A82-664DDDE7A14F");
ON_OBJECT_IMPLEMENT(ON_Centermark, ON_Dimension, "D46767BA-7E8F-4D9D-9A92-66050219A5B9");


ON_Dimension::ON_Dimension(ON::AnnotationType annotation_type)
  : ON_Annotation(annotation_type)
{}

ON_Dimension::~ON_Dimension()
{
  Internal_Destroy();
}

ON_Dimension::ON_Dimension( const ON_Dimension& src )
  : ON_Annotation(src)
{
  Internal_CopyFrom(src);
}

ON_Dimension& ON_Dimension::operator=(
  const ON_Dimension& src
  )
{
  if (this != &src)
  {
    Internal_Destroy();
    ON_Annotation::operator=(src);
    Internal_CopyFrom(src);
  }
  return *this;
}

void ON_Dimension::Internal_Destroy()
{
  m_user_text.Destroy();
}

void ON_Dimension::Internal_CopyFrom(const ON_Dimension& src)
{
  // m_text_rotation NOT used in 6.0 - m_text_rotation = src.m_text_rotation;
  m_use_default_text_point = src.m_use_default_text_point;
  m_user_text_point = src.m_user_text_point;
  m_user_text = src.m_user_text;
  m_distance_scale = src.m_distance_scale;
  m_detail_measured = src.m_detail_measured;
  m_flip_arrow_1 = src.m_flip_arrow_1;
  m_flip_arrow_2 = src.m_flip_arrow_2;
  m_force_arrows = src.m_force_arrows;
  m_force_textpos = src.m_force_textpos;
}

bool ON_Dimension::IsValid(ON_TextLog* text_log) const
{
  return true;
}

ON_2dPoint ON_Dimension::TextPoint() const
{
  if (m_use_default_text_point)
    return DefaultTextPoint();
  else
    return m_user_text_point;
}

void ON_Dimension::Set2dTextPoint(const ON_2dPoint& textpoint)
{
  SetUseDefaultTextPoint(false);
  m_user_text_point = textpoint;
}

ON_2dPoint ON_Dimension::DefaultTextPoint() const
{
  return ON_2dPoint::Origin;
}

bool ON_Dimension::UseDefaultTextPoint() const
{
  return m_use_default_text_point;
}

void ON_Dimension::SetUseDefaultTextPoint(bool usedefault)
{
  m_use_default_text_point = usedefault;
}

const wchar_t* ON_Dimension::UserText() const
{
  return static_cast< const wchar_t* >(m_user_text);
}

void ON_Dimension::SetUserText(const wchar_t* text)
{
  if (nullptr == text)
    return;

  if (m_user_text.CompareOrdinal(text, false))
  {
    if (0 == *text)
      m_user_text = L"<>";
    else
      m_user_text = text;
    m_plain_user_text.Empty();
  }
}

const wchar_t* ON_Dimension::PlainUserText() const
{
  if (m_plain_user_text.IsEmpty())
  {
    ON_TextContent tc;
    tc.Create(UserText(), Type(), &ON_DimStyle::Default);
    m_plain_user_text = tc.PlainText();
  }
  return static_cast< const wchar_t* >(m_plain_user_text);
}


// Add to natural rotation
double ON_Dimension::TextRotation() const
{
  // This V5 function should have been removed from the 6.0 SDK.
  // It returned some angle in radians and it doesn't do anything in V6. It was almost always zero.
  // Text rotation is handled completely differently in V5 an V6.
  return 0.0;
}

void ON_Dimension::SetTextRotation(double ignored_rotation_radians)
{
  // This V5 function and m_text_rotation should have been removed from the 6.0 SDK.
  // Text rotation is handled completely differently in V5 an V6.
  // m_text_rotation = remainder(rotation_radians, (2.0 * ON_PI));
  return;
}

bool ON_Dimension::GetTextRect(ON_3dPoint text_rect[4]) const
{
  const ON_TextContent* text = Text();
  if (nullptr != text)
  {
    ON_BoundingBox text_box;
    if (text->GetTightBoundingBox(text_box))
    {
      ON_3dPoint text_point( TextPoint());
      text_rect[0].Set(text_box.m_min.x, text_box.m_min.y, 0.0);
      text_rect[1].Set(text_box.m_max.x, text_box.m_min.y, 0.0);
      text_rect[2].Set(text_box.m_max.x, text_box.m_max.y, 0.0);
      text_rect[3].Set(text_box.m_min.x, text_box.m_max.y, 0.0);
      return true;
    }
  }
  return false;
}

ON_TextContent* ON_Dimension::RebuildDimensionText(
  ON::LengthUnitSystem units_in,
  const ON_DimStyle* dimstyle,
  bool expandanglebrackets
) const
{
  if (Type() == ON::AnnotationType::CenterMark)
    return nullptr;

  ON_wString displaytext;

  if (expandanglebrackets)
  {
    if (!GetDistanceDisplayText(units_in, dimstyle, displaytext))
      return nullptr;
  }
  else
  {
    displaytext = UserText();
  }

  ON_TextContent* newtext = new ON_TextContent;
  if (nullptr != newtext)
  {
    bool wrapped = m_text ? m_text->TextIsWrapped() : false;
    double rect_width = m_text ? m_text->FormattingRectangleWidth() : 0.0;
    double rotation = m_text ? m_text->TextRotationRadians() : 0.0;
    if (newtext->Create(displaytext, Type(), dimstyle,wrapped, rect_width, rotation))
    {
#ifdef _DEBUG
      newtext->IsValid();
#endif
    }
  }
  return newtext;
}

bool ON_Dimension::UpdateDimensionText(
  ON::LengthUnitSystem units_in,
  const ON_DimStyle* dimstyle
) const
{
  if (Type() == ON::AnnotationType::CenterMark)
    return false;

  ON_TextContent* newtext = RebuildDimensionText(units_in, dimstyle,  true);
  if (nullptr != newtext)
  {
    SetText(newtext);
    return true;
  }
  return false;
}

bool ON_Dimension::GetDistanceDisplayText(
  ON::LengthUnitSystem units_in,
  const ON_DimStyle* dimstyle,
  ON_wString& displaytext) const
{
  if (Type() == ON::AnnotationType::CenterMark)
    return false;

  if (nullptr == dimstyle)
    return false;

  double measurement = Measurement();
  const wchar_t* user_text = UserText();
  ON_TextContent::FormatDistanceMeasurement(measurement, units_in, dimstyle, user_text, displaytext);
  return true;
}



bool ON_Dimension::ArrowIsFlipped(int i) const
{
  if (i == 0)
    return m_flip_arrow_1;
  else
    return m_flip_arrow_2;
}

void ON_Dimension::FlipArrow(int i, bool flip) const
{
  if (i == 0)
    m_flip_arrow_1 = flip;
  else
    m_flip_arrow_2 = flip;
}

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

void ON_Dimension::SetDistanceScale(double distance_scale) const
{
  m_distance_scale = distance_scale;
}

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

void ON_Dimension::SetDetailMeasured(ON_UUID uuid)
{
  m_detail_measured = uuid;
}

ON_Dimension::ForceArrow ON_Dimension::ForceArrowPosition() const
{
  return m_force_arrows;
}

void ON_Dimension::SetForceArrowPosition(ON_Dimension::ForceArrow force)
{
  m_force_arrows = force;
}

ON_Dimension::ForceText ON_Dimension::ForceTextPosition() const
{
  return m_force_textpos;
}

void ON_Dimension::SetForceTextPosition(ON_Dimension::ForceText force)
{
  m_force_textpos = force;
}

//----------------------------------------------------------
// Class ON_DimLinear

ON_DimLinear::ON_DimLinear()
  : ON_Dimension(ON::AnnotationType::Rotated)
{}



ON_Dimension::ForceArrow ON_Dimension::ForceArrowFromUnsigned(
  unsigned int force_arrow_as_unsigned)
{
  switch (force_arrow_as_unsigned)
  {
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Auto);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Inside);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceArrow::Outside);
  }
  ON_ERROR("Invalid type_as_unsigned parameter.");
  return (ON_Dimension::ForceArrow::Auto);
}

ON_Dimension::ForceText ON_Dimension::ForceTextFromUnsigned(
    unsigned int force_text_as_unsigned)
{
  switch (force_text_as_unsigned)
  {
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Auto);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Inside);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Right);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Left);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::HintRight);
    ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::HintLeft);
  }
  ON_ERROR("Invalid type_as_unsigned parameter.");
  return (ON_Dimension::ForceText::Auto);
}

bool ON_Dimension::Internal_WriteDimension(
  ON_BinaryArchive& archive
) const
{
  // content_version = 1 added m_force_textpos
  const int content_version = 1;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Annotation::Internal_WriteAnnotation(archive))
      break;

    if (!archive.WriteString(m_user_text))
      break;
    if (!archive.WriteDouble(0.0)) // OBSOLETE m_text_rotation
      break;
    if (!archive.WriteBool(m_use_default_text_point))
      break;
    if (!archive.WritePoint(m_user_text_point))
      break;
    if (!archive.WriteBool(m_flip_arrow_1))
      break;
    if (!archive.WriteBool(m_flip_arrow_2))
      break;
    unsigned int u = static_cast<unsigned int>(m_force_arrows);
    if (!archive.WriteInt(u))
      break;
    if (!archive.WriteUuid(m_detail_measured))
      break;
    if (!archive.WriteDouble(m_distance_scale))
      break;

    // content_version 1
    const unsigned int force_textpos_as_unsigned = static_cast<unsigned int>(m_force_textpos);
    if (!archive.WriteInt(force_textpos_as_unsigned))
      break;

    rc = true;
    break;
  }

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

  return rc;
}

bool ON_Dimension::Internal_ReadDimension(
  ON_BinaryArchive& archive
)
{
  // This is a helper function called by liner and angular annotation classes.
  // "this" has already been set to default values before this function is called.
  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Annotation::Internal_ReadAnnotation(archive))
      break;

    if (!archive.ReadString(m_user_text))
      break;
    double obsolete_text_rotation = 0.0;
    if (!archive.ReadDouble(&obsolete_text_rotation))
      break;
    if (!archive.ReadBool(&m_use_default_text_point))
      break;
    if (!archive.ReadPoint(m_user_text_point))
      break;
    if (!archive.ReadBool(&m_flip_arrow_1))
      break;
    if (!archive.ReadBool(&m_flip_arrow_2))
      break;
    unsigned int u = static_cast<unsigned int>(m_force_arrows);
    if (!archive.ReadInt(&u))
      break;
    m_force_arrows = ON_Dimension::ForceArrowFromUnsigned(u);
    if (!archive.ReadUuid(m_detail_measured))
      break;
    if (!archive.ReadDouble(&m_distance_scale))
      break;

    if (content_version <= 0)
    {
      rc = true;
      break;
    }

    // content_version 1
    unsigned int force_textpos_as_unsigned = static_cast<unsigned int>(m_force_textpos);
    if (!archive.ReadInt(&force_textpos_as_unsigned))
      break;
    m_force_textpos = ON_Dimension::ForceTextFromUnsigned(force_textpos_as_unsigned);

    rc = true;
    break;
  }

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

  return rc;
}


bool ON_DimLinear::Write(
  ON_BinaryArchive& archive
  ) const 
{
  const int content_version = 0;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Dimension::Internal_WriteDimension(archive))
      break;
    if (!archive.WritePoint(m_def_pt_2))
      break;
    if (!archive.WritePoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimLinear::Read(
  ON_BinaryArchive& archive
  ) 
{
  *this = ON_DimLinear::Empty;

  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Dimension::Internal_ReadDimension(archive))
      break;
    if (!archive.ReadPoint(m_def_pt_2))
      break;
    if (!archive.ReadPoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimLinear::Transform(const ON_Xform& xform)
{
  bool rc = xform.IsIdentity();
  if (!rc)
  {
    rc = true;
    bool scaling = false;
    ON_3dVector v = m_plane.xaxis;
    v.Transform(xform);
    if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
      scaling = true;
    else
    {
      v = m_plane.yaxis;
      v.Transform(xform);
      if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
        scaling = true;
      else
      {
        v = m_plane.zaxis;
        v.Transform(xform);
        if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
          scaling = true;
      }
    }

    if (rc)
    {
      if (scaling)
      {
        ON_3dPoint defpt2_0(ON_3dPoint::UnsetPoint);
        ON_3dPoint dimlinept_0(ON_3dPoint::UnsetPoint);
        ON_3dPoint textpt_0(ON_3dPoint::UnsetPoint);
        if (Get3dPoints(nullptr, &defpt2_0, nullptr, nullptr, &dimlinept_0, &textpt_0))
        {
          ON_2dPoint defpt2(ON_2dPoint::NanPoint), dimlinept(ON_2dPoint::NanPoint), textpt(ON_2dPoint::NanPoint);
          rc = m_plane.Transform(xform);
          defpt2_0.Transform(xform);
          dimlinept_0.Transform(xform);
          if (!UseDefaultTextPoint())
            textpt_0.Transform(xform);
          if (rc && !m_plane.ClosestPointTo(defpt2_0, &defpt2.x, &defpt2.y))
            rc = false;
          else if (rc && !m_plane.ClosestPointTo(dimlinept_0, &dimlinept.x, &dimlinept.y))
            rc = false;
          else if (rc && !UseDefaultTextPoint() && !m_plane.ClosestPointTo(textpt_0, &textpt.x, &textpt.y))
            rc = false;
          if (rc)
          {
            Set2dDefPoint2(defpt2);
            Set2dDimlinePoint(dimlinept);
            if (!UseDefaultTextPoint())
              Set2dTextPoint(textpt);
          }
        }
      }
      else
        rc = m_plane.Transform(xform);
    }
    if (rc)
      ON_Geometry::Transform(xform);
  }
  return rc;
}

bool ON_DimLinear::GetTextXform(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out);
}


bool ON_DimLinear::GetTextXform(
  const ON_Xform* model_xform,
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  bool rc = false;
  if (nullptr == dimstyle)
    return false;

  // This gets the display text that's already on the dimension
  const ON_TextContent* text = Text();
  if (nullptr == text)
    return false;

  // See if the text needs remade because of some change in some property that
  // would change its appearance
  if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash())
  {
    ON_wString rtfstr = text->RtfText();
    ON::AnnotationType annotation_type = this->Type();
    bool wrapped = text->TextIsWrapped();
    double width = text->FormattingRectangleWidth();
    double rot = text->TextRotationRadians();
    const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot);
  }

  double text_width = 0.0;
  double text_height = 0.0;
  double text_gap = 0.0;
  double text_angle = 0.0; // in radians - deviation from horizontal ccw
  
  const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation();
  const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation();
  const ON_DimStyle::ContentAngleStyle text_angle_style = dimstyle->DimTextAngleStyle();

  ON_Xform text_to_dimplane(1.0);         // Text plane (world xy) to dimension plane rotation
  ON_Xform dimplane_to_textpoint(1.0);        // Dimension plane to text point translation
  ON_Xform text_rotation(1.0);          // Text rotation around text plane origin point

  // The amount past vertical where text flips to the other orientation
  const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.0*ON_PI / 180.0);

  ON_3dPoint text_center = ON_3dPoint::Origin;
  // Text starts out approximately centered at origin
  ON_3dPoint cp[4];
  if (!text->Get3dCorners(cp))
    return false;

  text_center = (cp[0] + cp[2]) / 2.0;
  text_width = (cp[1].x - cp[0].x) * dimscale;
  text_height = (cp[3].y - cp[0].y) * dimscale;

  text_gap = dimstyle->TextGap() * dimscale;

  if (dimstyle->Alternate() && dimstyle->AlternateBelow())
    text_height = -2.0 * text_gap;

  text_xform_out = ON_Xform::IdentityTransformation;
  text_to_dimplane.Rotation(ON_Plane::World_xy, Plane());     // Rotate text from starting text plane to dimension plane
  bool draw_forward = dimstyle->DrawForward();

#pragma region ArrowAndTextFitting
  // See if arrows and text will all fit inside extension lines
  // or what has to be moved outside
  bool arrowflipped[2] = { false, false };
  //{ ArrowIsFlipped(0), ArrowIsFlipped(1) };  // manual override
  ON_Dimension::ForceArrow force_arrow = ForceArrowPosition();
  if (ForceArrow::Outside == force_arrow)
    arrowflipped[0] = arrowflipped[1] = true;

  ON_Dimension::ForceText force_text = ForceTextPosition();

  bool text_outside = false;
  double dist = Measurement();
  // Display scale for detail viewports when page space dimension measures model space
  if (ON_nil_uuid != DetailMeasured())
  {
    double dist_scale = DistanceScale();
    if (dist_scale != 1.0 && dist_scale > 0.0)
      dist /= dist_scale;
  }

  // V6_Dimstyle Arrow1 & Arrow2
  double asz = dimstyle->ArrowSize() * dimscale;

  double total_text_width = (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) ? text_height : text_width;

  if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::Right)
  {
    total_text_width = 0.0;
    text_outside = true;
  }
  else if (force_text == ON_Dimension::ForceText::Inside)
  {
    total_text_width = 0.0;
    text_outside = false;
  }
  else if (0.0 < total_text_width)
    total_text_width += text_gap;

  static double arrow_width_factor = 1.1;
  double total_arrow_width = asz * arrow_width_factor * 2;
  if (ForceArrow::Outside == force_arrow)
    total_arrow_width = 0.0;

  if (total_arrow_width + total_text_width > dist)     // arrows + text dont fit
  {
    // Try to leave text inside and move arrows outside
    if (total_text_width > dist)   // text doesnt fit
    {
      // move text outside
      text_outside = true;
      if (total_arrow_width > dist && ForceArrow::Auto == force_arrow)  // arrows dont fit either
      {
        arrowflipped[0] = true;
        arrowflipped[1] = true;
      }
    }
    else if (ForceArrow::Auto == force_arrow)       // text fits
    {
      // flip arrows
      arrowflipped[0] = true;
      arrowflipped[1] = true;
    }
  }

  FlipArrow(0, arrowflipped[0]);
  FlipArrow(1, arrowflipped[1]);

  // This returns the midpoint of the dimension line in 2d coordinates
  ON_2dPoint text_pt = TextPoint();
  if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint())
  {
    // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width
    double x = text_width * 0.5 + text_gap;
    if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::HintLeft)
    {
      if (arrowflipped[0]) 
        x += (asz * arrow_width_factor);
      text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint1() : ArrowPoint2();
      text_pt.x -= x;
    }
    else  // right or auto
    {
      if (arrowflipped[1])
        x += asz * (arrow_width_factor);
      text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint2() : ArrowPoint1();
      text_pt.x += x;
    }
  }

#pragma endregion ArrowAndTextFitting

  ON_3dVector dim_xaxis = Plane().xaxis;
  ON_3dVector dim_yaxis = Plane().yaxis;
  ON_3dVector dim_zaxis = Plane().zaxis;
  if (nullptr != model_xform)
  {
    dim_xaxis.Transform(*model_xform);
    dim_yaxis.Transform(*model_xform);
    dim_zaxis.Transform(*model_xform);
  }

  ON_3dVector view_xdir = ON_3dVector::XAxis;
  ON_3dVector view_ydir = ON_3dVector::YAxis;
  ON_3dVector view_zdir = ON_3dVector::ZAxis;
  if (nullptr != vp)
  {
    view_xdir = vp->CameraX();
    view_ydir = vp->CameraY();
    view_zdir = vp->CameraZ();
  }

  // text is in dimension plane, not horizontal to the view
  ON_3dVector text_xdir = dim_xaxis;
  ON_2dVector h_dir = HorizontalDirection();
  if (ON::TextOrientation::InPlane == text_orientation)
  {
    if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style)
    {
      // Rotation angle = 0 means the text is horizontal
      text_angle = 0.0; //TextRotation();
    }
    else if (ON_DimStyle::ContentAngleStyle::Aligned == text_angle_style)
    {
      text_angle = 0.0;
    }
    if (ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style)
    {
      double h_angle = atan2(h_dir.y, h_dir.x);
      text_angle += h_angle;
      text_xdir.Rotate(h_angle, dim_zaxis);
    }
  }

  double XoX = dim_xaxis * view_xdir;
  double XoY = dim_xaxis * view_ydir;
  double YoX = dim_yaxis * view_xdir;
  double YoY = dim_yaxis * view_ydir;
  bool from_the_back = (view_zdir * dim_zaxis < 0.0);
  if (nullptr != model_xform && model_xform->Determinant() < 0.0)
    from_the_back = !from_the_back;

  double upsign = 1.0;

  // This part shifts text to the correct side of the dimension line
  if (fabs(XoX) > fabs(XoY)) // more horizontal
  {
    if (YoY > 0.0)
      upsign = 1.0;
    else
      upsign = -1.0;
  }
  else  // more vertical
  {
    if (from_the_back)
    {
      if (YoX < 0.0)
      {
        if (XoX < fliptol)
          upsign = 1.0;
        else
          upsign = -1.0;
      }
      else
      {
        if (XoX > -fliptol)
          upsign = -1.0;
        else
          upsign = 1.0;
      }
    }
    else
    {
      if (YoX > 0.0)
      {
        if (XoX > fliptol)
          upsign = 1.0;
        else
          upsign = -1.0;
      }
      else
      {
        if (XoX < -fliptol)
          upsign = -1.0;
        else
          upsign = 1.0;
      }
    }
  }

  if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
  {
    // Moves the text to AboveLine if that's the alignment mode
    double d = (text_height * 0.5 + text_gap) * upsign;
    //if (from_the_back)
    //  d = -d;
    text_pt.y += d;
  }

  ON_3dPoint text_point_3d = Plane().PointAt(text_pt.x, text_pt.y);  // 3d text point
  dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - Plane().origin);  // Move from dimplane origin to text point

  text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);      // dimscale

  if (1.0e-2 < fabs(text_angle))  // There's a rotation angle change of more than 1/100 radian (~1/2 degree)
  {
    text_rotation.Rotation(text_angle, ON_3dVector::ZAxis, ON_3dPoint::Origin);

    if (ON::TextOrientation::InView != text_orientation)
      text_xform_out = text_rotation * text_xform_out;                  // text rotation
  }
  text_xform_out = text_to_dimplane * text_xform_out;                 // text plane to dim plane
  text_xform_out = dimplane_to_textpoint * text_xform_out;                // dimension plane to text point

  
  if (ON::TextOrientation::InView == text_orientation)  // Draw dimension horizontal to view
  {
    if (nullptr != model_xform)
    {
      ON_Xform xf(*model_xform);
      xf.Invert();
      view_xdir.Transform(xf);
      view_ydir.Transform(xf);
      view_zdir.Transform(xf);
    }

    ON_Xform tp2sxf;        // Text point to view plane rotation
    tp2sxf.Rotation(text_point_3d, Plane().xaxis, Plane().yaxis, Plane().zaxis, text_point_3d, view_xdir, view_ydir, view_zdir);
    text_xform_out = tp2sxf * text_xform_out;
  }
  else if (draw_forward)
  {
    bool fx = false;
    bool fy = false;
    if (from_the_back)
      upsign = -upsign;
    fx = upsign < 0.0;
    if (from_the_back)
      fy = !fx;
    else
      fy = fx;

    ON_Xform mxf;  // Mirror xform for backwards text to adjust DrawForward
    if (fx)
    {
      mxf.Mirror(text_center, ON_3dVector::XAxis);
      text_xform_out = text_xform_out * mxf;
    }
    if (fy)
    {
      mxf.Mirror(text_center, ON_3dVector::YAxis);
      text_xform_out = text_xform_out * mxf;
    }
  }
  return rc;
}

bool ON_DimLinear::GetBBox(double* bmin, double* bmax, bool grow) const
{
  const ON_DimStyle* dimstyle = nullptr;
  return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow?true:false);
}


class StylePointer
{
public:
  ~StylePointer()
  {
    if (m_delete_style && nullptr != m_style)
    {
      delete m_style;
      m_style = nullptr;
      m_delete_style = false;
    }
  }
  const ON_DimStyle* m_style = nullptr;
  bool m_delete_style = false;
};

bool ON_DimLinear::GetAnnotationBoundingBox(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  double* boxmin,
  double* boxmax,
  bool bGrow
) const
{
  if (nullptr == dimstyle)
    dimstyle = &ON_DimStyle::Default;

  const ON_2dPoint hash_points[] = { 
    m_def_pt_2,
    m_dimline_pt
  };

  const ON_SHA1_Hash hash = Internal_GetBBox_InputHash(
    vp, 
    dimstyle, 
    dimscale, 
    m_user_text_point,
    (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])),
    hash_points
  );

  if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow))
    return true;

  if (nullptr == boxmin || nullptr == boxmax)
    return false;

  ON_Xform text_xform;
  GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform);

  ON_BoundingBox dim_box;

  const ON_TextContent* text = Text();
  ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin };

  if (nullptr != text && text->GetTightBoundingBox(dim_box))
  {
    text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0);
    text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0);
    text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0);
    text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0);
    for (int i = 0; i < 4; i++)
      text_rect[i].Transform(text_xform);  // Text + gap bounding rect

    for (int i = 0; i < 4; i++)
    {
      dim_box.Set(text_rect[i], 0 < i ? true : false);
    }
  }

  // Get non-text display geometry for the dimension
#define dimlinecount 4
  bool dimlines[dimlinecount] = { false, false, false, false };
  ON_Line lines[dimlinecount];
  if (GetDisplayLines(vp, dimstyle, dimscale, text_rect, lines, dimlines, dimlinecount))
  {
    for (int i = 0; i < dimlinecount; i++)
    {
      if (dimlines[i])
      {
        dim_box.Set(lines[i].from, true);
        dim_box.Set(lines[i].to, true);
      }
    }
  }
#undef dimlinecount
  
  double points[12];
  if(Get3dPoints(
      (ON_3dPoint*)(&points[0]),   // defpt1
      (ON_3dPoint*)(&points[3]),   // defpt2
      (ON_3dPoint*)(&points[6]),   // arrowpt1
      (ON_3dPoint*)(&points[9]),   // arrowpt2
      nullptr,  // dimlinept
      nullptr)) // textpt
    dim_box.Set(3, 0, 4, 3, points, true);


  // Include arrows
  bool arrowflipped[2] = { ArrowIsFlipped(0), ArrowIsFlipped(1) };
  double scale = dimstyle->ArrowSize() * dimscale;

  for (int ai = 0; ai < 2; ai++)
  {
    if (0 == ai && dimstyle->SuppressArrow1())
      continue;
    if (1 == ai && dimstyle->SuppressArrow2())
      continue;

    ON_Xform arrow_xform(1.0);
    GetArrowXform(ai, scale, arrowflipped[ai], false, arrow_xform);

    ON_Arrowhead::arrow_type arrowtype = (0 == ai) ? dimstyle->ArrowType1() : dimstyle->ArrowType2();
    ON_UUID arrow_block_id = (0 == ai) ? dimstyle->ArrowBlockId1() : dimstyle->ArrowBlockId2();
    ON_Arrowhead::GetArrowheadBoundingBox(arrowtype, arrow_block_id, arrow_xform, dim_box, true);
  }

  return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow);
}

bool ON_DimLinear::IsValidLinearDimensionType(
  ON::AnnotationType annotation_type
)
{
  return (
    ON::AnnotationType::Aligned == annotation_type
    || ON::AnnotationType::Rotated == annotation_type
    );
}

bool ON_DimLinear::SetLinearDimensionType(
  ON::AnnotationType linear_dimension_type
)
{
  if ( !ON_DimLinear::IsValidLinearDimensionType(linear_dimension_type) )
  {
    ON_ERROR("Invalid linear_dimension_type parameter.");
    return false;
  }

  m_annotation_type = linear_dimension_type;

  return true;
}



// Returns unadjusted distance in entity plane coordinates
double ON_DimLinear::Measurement() const
{
  if (!m_def_pt_2.IsValid())
    return 0.0;
  if (ON_2dPoint::UnsetPoint == m_def_pt_2)
    return 0.0;
  double d = fabs(m_def_pt_2.x);
  if (DistanceScale() != 1.0)
    d *= DistanceScale();
  return d;
}

// Returns midpoint of dimline
ON_2dPoint ON_DimLinear::DefaultTextPoint() const
{
  return ON_2dPoint(m_def_pt_2.x / 2.0, m_dimline_pt.y);
}

ON_2dPoint ON_DimLinear::DefPoint1() const
{
  return ON_2dPoint(0.0, 0.0);
}

ON_2dPoint ON_DimLinear::DefPoint2() const
{
  return m_def_pt_2;
}

ON_2dPoint ON_DimLinear::DimlinePoint() const
{
  return m_dimline_pt;
}

void ON_DimLinear::Set2dDimlinePoint(ON_2dPoint pt)
{
  m_dimline_pt = pt;
}

void ON_DimLinear::Set3dDimlinePoint(ON_3dPoint pt)
{
  ON_2dPoint p;
  if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
    Set2dDimlinePoint(p);
}

ON_2dPoint ON_DimLinear::ArrowPoint1() const
{
  return ON_2dPoint(0.0, m_dimline_pt.y);
}

ON_2dPoint ON_DimLinear::ArrowPoint2() const
{
  return ON_2dPoint(m_def_pt_2.x, m_dimline_pt.y);
}


bool ON_DimLinear::Get3dPoints(
  ON_3dPoint* defpt1, ON_3dPoint* defpt2,
  ON_3dPoint* arrowpt1, ON_3dPoint* arrowpt2,
  ON_3dPoint* dimline, ON_3dPoint* textpt) const
{
  bool rc = true;
  if (nullptr != defpt1)
    *defpt1 = m_plane.origin;

  if (nullptr != defpt2)
  {
    if (ON_3dPoint::UnsetPoint != m_def_pt_2)
      *defpt2 = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y);
    else
    {
      *defpt2 = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != dimline)
  {
    if (ON_3dPoint::UnsetPoint != m_dimline_pt)
      *dimline = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y);
    else
    {
      *dimline = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }
  
  if (nullptr != arrowpt1)
  {
    if (ON_3dPoint::UnsetPoint != m_dimline_pt)
      *arrowpt1 = m_plane.PointAt(0.0, m_dimline_pt.y);
    else
    {
      *arrowpt1 = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != arrowpt2)
  {
    if (ON_3dPoint::UnsetPoint != m_def_pt_2 && ON_3dPoint::UnsetPoint != m_dimline_pt)
      *arrowpt2 = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y);
    else
    {
      *arrowpt2 = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != textpt)
  {
    ON_2dPoint textpt2d = ON_2dPoint::UnsetPoint;
    if (m_use_default_text_point)
      textpt2d = DefaultTextPoint();
    else if (ON_3dPoint::UnsetPoint != m_user_text_point)
        textpt2d = m_user_text_point;

    if (ON_3dPoint::UnsetPoint != textpt2d)
      *textpt = m_plane.PointAt(textpt2d.x, textpt2d.y);
    else
    {
      *textpt = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }
  return rc;
}

ON_DimLinear* ON_DimLinear::CreateAligned(
  ON_3dPoint extension_point0,
    ON_3dPoint extension_point1,
    ON_3dPoint dimension_line_point,
    ON_3dVector plane_normal,
    ON_UUID style_id,
    ON_DimLinear* destination
)
{
  if (nullptr != destination)
    *destination = ON_DimLinear::Empty;

  const ON_Line ext_line(extension_point0, extension_point1);
  if (false == ext_line.IsValid())
    return nullptr;
  ON_Plane plane;
  plane.xaxis = ext_line.Tangent();
  if (false == plane.xaxis.IsUnitVector())
    return nullptr;
  plane.zaxis = plane_normal;
  if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize())
    return nullptr;
  plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis);
  if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize())
    return nullptr;
  plane.origin = extension_point0;
  plane.UpdateEquation();
  if (false == plane.IsValid())
  {
    plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis);
    plane.zaxis.Unitize();
    plane.UpdateEquation();
    if (false == plane.IsValid())
      return nullptr;
  }

  ON_Line dim_line = ext_line;
  double t = 0.5;
  if (dimension_line_point.IsValid())
  {
    ON_3dPoint p = plane.ClosestPointTo(dimension_line_point);
    ON_Line l(p, p + ext_line.Direction());
    ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1));
    if (l2.IsValid() && fabs(1.0 - l2.Tangent()*plane.xaxis) <= 1e-4)
      dim_line = l2;
    double s = ON_UNSET_VALUE;
    dim_line.ClosestPointTo(dimension_line_point, &s);
    if (s >= 0.0 && s <= 1.0)
      t = s;
    else if (s < 0.0)
      t = 0.0;
    else if (s > 1.0)
      t = 1.0;
  }

  ON_DimLinear* dim_linear
    = (nullptr != destination)
    ? destination
    : new ON_DimLinear();

  ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane);
  if (false == dim_linear->Create(ON::AnnotationType::Aligned, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(t)) )
  {
    if (nullptr != destination)
      *destination = ON_DimLinear::Empty;
    else
      delete dim_linear;
    dim_linear = nullptr;
  }

  return dim_linear;
}

ON_DimLinear* ON_DimLinear::CreateRotated(
    ON_3dPoint extension_point0,
    ON_3dPoint extension_point1,
    ON_Line dimension_line,
    ON_3dVector plane_normal,
    ON_UUID style_id,
    ON_DimLinear* destination
)
{
  if (nullptr != destination)
    *destination = ON_DimLinear::Empty;

  const ON_Line ext_line(extension_point0, extension_point1);
  if (false == ext_line.IsValid())
    return nullptr;
  ON_Plane plane;
  plane.xaxis = ext_line.Tangent();
  if (false == plane.xaxis.IsUnitVector())
    return nullptr;
  plane.zaxis = plane_normal;
  if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize())
    return nullptr;
  plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis);
  if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize())
    return nullptr;
  plane.origin = extension_point0;
  plane.UpdateEquation();
  if (false == plane.IsValid())
  {
    plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis);
    plane.zaxis.Unitize();
    plane.UpdateEquation();
    if (false == plane.IsValid())
      return nullptr;
  }

  ON_Line dim_line = ext_line;
  double rot_angle = 0.0;
  if (dimension_line.IsValid())
  {
    ON_Line l(plane.ClosestPointTo(dimension_line.from),plane.ClosestPointTo(dimension_line.to));
    if (l.IsValid())
    {
      ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1));
      if (l2.IsValid() && fabs(l2.Tangent()*plane.zaxis) <= 1e-4)
      {
        dim_line = l2;
        double x = l2.Tangent()*plane.xaxis;
        double y = l2.Tangent()*plane.yaxis;
        double a = -atan2(y, x);
        if (a < -ON_PI)
          a += 2.0*ON_PI;
        else if (a > ON_PI)
          a -= 2.0*ON_PI;
        if (-ON_PI <= a && a <= ON_PI)
          rot_angle = a;
      }
    }
  }

  ON_DimLinear* dim_linear
    = (nullptr != destination)
    ? destination
    : new ON_DimLinear();

  ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane);

  if (false == dim_linear->Create(ON::AnnotationType::Rotated, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(0.5), rot_angle) )
  {
    if (nullptr != destination)
      *destination = ON_DimLinear::Empty;
    else
      delete dim_linear;
    dim_linear = nullptr;
  }

  return dim_linear;
}

// Projects all points to the input plane
bool ON_DimLinear::Create(
  ON::AnnotationType dim_type,
  const ON_UUID style_id,
  const ON_Plane& plane,
  const ON_3dVector& ref_horizontal,
  const ON_3dPoint& def_pt_1,
  const ON_3dPoint& def_pt_2,
  const ON_3dPoint& dimline_pt,
  double rotation_in_plane
  )
{
  m_dimstyle_id = style_id;
  if (ON_nil_uuid == m_dimstyle_id)
    return true;

  if ( !ON_DimLinear::IsValidLinearDimensionType(dim_type) )
  {
    ON_ERROR("Invalid dim_type parameter.");
    return false;
  }

  if (!plane.IsValid() || !def_pt_1.IsValid() || !def_pt_2.IsValid() || !dimline_pt.IsValid() || !ON_IsValid(rotation_in_plane))
    return false;

  bool rc = SetLinearDimensionType(dim_type);

  m_plane = plane;

  if (0.0 != rotation_in_plane)
    rc = m_plane.Rotate(-rotation_in_plane, m_plane.zaxis);

  if (rc)
  {
    m_plane.origin = plane.ClosestPointTo(def_pt_1);
    rc = m_plane.ClosestPointTo(def_pt_2, &m_def_pt_2.x, &m_def_pt_2.y);
  }
  if (rc)
    rc = m_plane.ClosestPointTo(dimline_pt, &m_dimline_pt.x, &m_dimline_pt.y);

  if (rc)
  {
    ON_2dVector horiz;
    ON_3dPoint hp = m_plane.origin + ref_horizontal;
    rc = m_plane.ClosestPointTo(hp, &horiz.x, &horiz.y);
    if (rc)
      SetHorizontalDirection(horiz);
  }

  return rc;
}

void ON_DimLinear::Set2dDefPoint1(ON_2dPoint pt)
{
  // Move plane origin to pt
  ON_3dPoint p = m_plane.PointAt(pt.x, pt.y);
  Set3dDefPoint1(p);
}

void ON_DimLinear::Set2dDefPoint2(ON_2dPoint pt)
{
  if ( ON::AnnotationType::Aligned == Type() )
  {
    // Rotate around DefPoint1
    ON_2dVector xdir = pt;
    if (!xdir.Unitize())
      return;
    double r = atan2(pt.y, pt.x);
    m_plane.Rotate(r, m_plane.zaxis);
    pt.Rotate(-r, ON_2dPoint(0.0, 0.0));
  }
  m_def_pt_2 = pt;
}

void ON_DimLinear::Set3dDefPoint1(ON_3dPoint pt)
{
  ON_2dPoint p;
  if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
  {
    if (ON::AnnotationType::Aligned == Type())
    {
      ON_2dVector xdir = m_def_pt_2 - p;
      if (!xdir.Unitize())
        return;
      m_plane.origin = pt;
      m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis);
    }
    else //DimensionType::Rotated
    {
      m_plane.origin = pt;
      m_dimline_pt.x -= p.x;
      m_dimline_pt.y -= p.y;
      m_def_pt_2.x -= p.x;
      m_def_pt_2.y -= p.y;
    }
  }
}

void ON_DimLinear::Set3dDefPoint2(ON_3dPoint pt)
{
  if (ON::AnnotationType::Aligned == Type())
  {
    ON_2dPoint p;
    if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
    {
      // Rotate around DefPoint1
      ON_2dVector xdir = p;
      if (!xdir.Unitize())
        return;
      m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis);
    }
  }
  else
    m_plane.ClosestPointTo(pt, &m_def_pt_2.x, &m_def_pt_2.y);
}

static int ClipLineToTextRect(
  const ON_Viewport* vp,
  const ON_Line dimline,
  const ON_3dPoint text_rect[4],
  ON_Line linesegs[2])
{
  ON_PlaneEquation cam_plane_eq;
  ON_3dPoint cam_loc = ON_3dPoint::Origin;
  ON_3dVector cam_dir = ON_3dVector::ZAxis;

  if (nullptr == vp)
  {
    cam_loc = ON_3dPoint::Origin;
    ON_3dVector xdir = text_rect[1] - text_rect[0]; xdir.Unitize();
    ON_3dVector ydir = text_rect[3] - text_rect[0]; ydir.Unitize();
    cam_dir = ON_CrossProduct(xdir, ydir);
    cam_dir = cam_dir * 100.0;
    cam_loc = cam_loc + cam_dir;
    cam_dir = cam_dir + cam_dir;
  }
  else
  {
    cam_loc = vp->CameraLocation();
    cam_dir = -vp->CameraDirection();
  }
  cam_plane_eq.Create(cam_loc, cam_dir);

  ON_PlaneEquation frust_plane_eq[4];
  ON_3dVector v0, v1;
  v0 = text_rect[3] - cam_loc;
  if (!v0.Unitize())
    return 0;

  bool bTextRectBackwards = false;
  {
    ON_3dVector a = text_rect[2] - text_rect[0];
    ON_3dVector b = text_rect[3] - text_rect[1];
    ON_3dVector c = ON_CrossProduct(a, b);
    if ((c * cam_dir) < 0.0)
      bTextRectBackwards = true;
  }

  for (int i = 0; i < 4; i++)
  {
    v1 = text_rect[i] - cam_loc;
    if (!v1.Unitize())
      return 0;
    // Makes normals facing out of frustum
    ON_3dVector z = ON_CrossProduct(v0, v1);
    if (bTextRectBackwards)
      z = -z;
    if (!frust_plane_eq[i].Create(cam_loc, z))
      return 0;
    v0 = v1;
  }

  double s[4];
  int intcount = 0;
  for (int i = 0; i < 4; i++)
  {
    double t;
    if (ON_Intersect(dimline, frust_plane_eq[i], &t) && t >= 0.0 && t <= 1.0)
    {
      ON_3dPoint p = dimline.PointAt(t);
      double d = cam_plane_eq.ValueAt(p);
      if (0.0 < d)
        continue;  // intersection behind camera

      bool inside = true;
      // Test if intersection is behind the other 3 planes
      // If not, it's outside the frustum
      for (int j = 1; j < 4; j++)
      {
        // eval other planes at p
        d = frust_plane_eq[(i + j) % 4].ValueAt(p);
        if (0.0 < d)  // intersection is outside frustum
        {
          inside = false;
          break;
        }
      }
      if (inside)
      {
        s[intcount] = t;
        intcount++;
      }
    }
  }

  bool end_1_inside = true;
  bool end_2_inside = true;

  if (0.0 < cam_plane_eq.ValueAt(dimline.from))
    end_1_inside = false;  // Dimline from point behind camera
  else for (int i = 0; i < 4; i++)
  {
    if (frust_plane_eq[i].ValueAt(dimline.from) > 0.0)
    {
      end_1_inside = false;
      break;
    }
  }

  if (0.0 < cam_plane_eq.ValueAt(dimline.to))
    end_2_inside = false;
  else for (int i = 0; i < 4; i++)
  {
    if (frust_plane_eq[i].ValueAt(dimline.to) > 0.0)
    {
      end_2_inside = false;
      break;
    }
  }

  if (end_1_inside && end_2_inside)
    return 0;

  if (0 == intcount)
  {
    // no intersection with frustum - return the whole line
    linesegs[0] = dimline;
    return 1;
  }

  double max_s = s[0];
  double min_s = s[0];
  for (int i = 1; i < intcount; i++)
  {
    if (s[i] > max_s)   max_s = s[i];
    if (s[i] < min_s)   min_s = s[i];
  }
  int segcount = 0;
  if (!end_1_inside && intcount > 0)
  {
    linesegs[segcount].from = dimline.from;
    linesegs[segcount].to = dimline.PointAt(min_s);
    segcount++;
  }
  if (!end_2_inside && intcount > 0)
  {
    linesegs[segcount].from = dimline.PointAt(max_s);
    linesegs[segcount].to = dimline.to;
    segcount++;
  }
  return segcount;
}

static int ClipArcToTextRect(
  const ON_Viewport* vp,
  const ON_Arc dimarc,
  const ON_3dPoint text_rect[4],
  ON_Arc arcsegs[2])
{
  if (nullptr == vp)
    return 0;

  ON_3dPoint cam_loc = vp->CameraLocation();
  ON_PlaneEquation cam_plane_eq;
  ON_3dVector cam_dir = -vp->CameraDirection();
  cam_plane_eq.Create(cam_loc, cam_dir);

  ON_Plane frust_plane[4];
  ON_3dVector v0, v1;
  v0 = text_rect[3] - cam_loc;
  if (!v0.Unitize())
    return 0;

  for (int i = 0; i < 4; i++)
  {
    if (!frust_plane[i].CreateFromPoints(cam_loc, text_rect[i], text_rect[(i + 1) % 4]))
      return 0;
  }

  bool bTextRectBackwards = false;
  {
    ON_3dVector a = text_rect[2] - text_rect[0];
    ON_3dVector b = text_rect[3] - text_rect[1];
    ON_3dVector c = ON_CrossProduct(a, b);
    if ((c * cam_dir) < 0.0)
      bTextRectBackwards = true;
  }

  for (int i = 0; i < 4; i++)
  {
    v1 = text_rect[i] - cam_loc;
    if (!v1.Unitize())
      return 0;
    // Makes normals facing out of frustum
    ON_3dVector z = ON_CrossProduct(v0, v1);
    z.Unitize();
    if (bTextRectBackwards)
      z = -z;
    if(!frust_plane[i].CreateFromNormal(cam_loc, z))
      return 0;
    v0 = v1;
  }

  double s[8];
  int intcount = 0;
  for (int i = 0; i < 4; i++)
  {
    ON_3dPoint p[2];
    int icount = ON_Intersect(frust_plane[i], dimarc, p[0], p[1]);
    if (icount > 2)
      icount = 2;
    for (int ip = 0; ip < icount; ip++)
    {
      double d = cam_plane_eq.ValueAt(p[ip]);
      if (0.0 < d)
        continue;  // intersection behind camera

      bool inside = true;
      // Test if intersection is behind the other 3 planes
      // If not, it's outside the frustum
      for (int j = 1; j < 4; j++)
      {
        // eval other planes at p
        d = frust_plane[(i + j) % 4].plane_equation.ValueAt(p[ip]);
        if (0.0 < d)  // intersection is outside frustum
        {
          inside = false;
          break;
        }
      }
      if (inside)
      {
        dimarc.ClosestPointTo(p[ip], &s[intcount]);
        intcount++;
        if (intcount > 7)
          intcount = 7;
      }
    }
  }

  bool end_1_inside = true;
  bool end_2_inside = true;
  ON_3dPoint end1 = dimarc.StartPoint();
  ON_3dPoint end2 = dimarc.EndPoint();

  if (0.0 < cam_plane_eq.ValueAt(end1))
    end_1_inside = false;  // Dimline from point behind camera
  else for (int i = 0; i < 4; i++)
  {
    if (frust_plane[i].plane_equation.ValueAt(end1) > 0.0)
    {
      end_1_inside = false;
      break;
    }
  }

  if (0.0 < cam_plane_eq.ValueAt(end2))
    end_2_inside = false;
  else for (int i = 0; i < 4; i++)
  {
    if (frust_plane[i].plane_equation.ValueAt(end2) > 0.0)
    {
      end_2_inside = false;
      break;
    }
  }

  if (0 == intcount) // no intersection with frustum
  {
    if (end_1_inside && end_2_inside)
      return 0;
    else if (!end_1_inside && !end_2_inside)
    {
      arcsegs[0] = dimarc;
      return 1;
    }
  }

  double max_s = s[0];
  double min_s = s[0];
  for (int i = 1; i < intcount; i++)
  {
    if (s[i] > max_s)   max_s = s[i];
    if (s[i] < min_s)   min_s = s[i];
  }
  int segcount = 0;
  ON_Interval domain = dimarc.Domain();
  if (!end_1_inside && intcount > 0)
  {
    arcsegs[segcount] = dimarc;
    arcsegs[segcount].Trim(ON_Interval(domain[0], min_s));
    segcount++;
  }
  if (!end_2_inside && intcount > 0)
  {
    arcsegs[segcount] = dimarc;
    arcsegs[segcount].Trim(ON_Interval(max_s, domain[1]));
    segcount++;
  }
  return segcount;
}


bool ON_DimLinear::GetDisplayLines(
  const ON_Viewport* vp,
  const ON_DimStyle* style,
  double dimscale,
  ON_3dPoint text_rect[4],
  ON_Line lines[4],
  bool isline[4],
  int maxlines
) const
{
  if (maxlines != 4)
  {
    ON_ERROR("Wrong linecount calling ON_DimLinear::GetDisplayLines.\n");
    return false;
  }
  if (nullptr == style)
    return false;

  //ON_BoundingBox text_box;
  //const ON_TextContent* text = Text();
  //if (nullptr != text)
  //  text->GetTightBoundingBox(text_box);

  double eo = style->ExtOffset() * dimscale;
  double ee = style->ExtExtension() * dimscale;
  double el = style->FixedExtensionLen() * dimscale;
  if (!style->SuppressExtension1())
  {
    double o = eo;
    double e = ee;
    double l = el;
    if (m_dimline_pt.y < 0.0)
    {
      o = -o;
      e = -e;
      l = -l;
    }
    if (style->FixedExtensionLenOn())
      lines[0].from = m_plane.PointAt(0.0, m_dimline_pt.y - l);
    else
      lines[0].from = m_plane.PointAt(0.0, o);

    lines[0].to = m_plane.PointAt(0.0, m_dimline_pt.y + e);
    isline[0] = true;
  }
  else
    isline[0] = false;

  if (!style->SuppressExtension2())
  {
    double o = eo;
    double e = ee;
    double l = el;
    if (m_dimline_pt.y < m_def_pt_2.y)
    {
      o = -o;
      e = -e;
      l = -l;
    }
    if (style->FixedExtensionLenOn())
      lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y - l);
    else
      lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y + o);
    lines[1].to = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y + e);
    isline[1] = true;
  }
  else
    isline[1] = false;

  const ON_DimStyle::TextLocation text_location = style->DimTextLocation();
  const ON::TextOrientation text_orientation = style->DimTextOrientation();
  const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle();


  double de[2] = { style->DimExtension() * dimscale, style->DimExtension() * dimscale };
  if (fabs(de[0]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(0))
    de[0] = style->ArrowSize() * dimscale * 1.5;
  if (fabs(de[1]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(1))
    de[1] = style->ArrowSize() * dimscale * 1.5;

  if (m_def_pt_2.x < 0.0)
  {
    double d = de[0];
    de[0] = -de[1];
    de[1] = -d;
  }

  lines[2].from = m_plane.PointAt(-de[0], m_dimline_pt.y);
  lines[2].to = m_plane.PointAt(m_def_pt_2.x + de[1], m_dimline_pt.y);
  isline[2] = true;
  isline[3] = false;

  if (UseDefaultTextPoint() && ON_DimStyle::TextLocation::InDimLine != text_location)
  {
    // If the dimline is under the text, and the text extends past the end of the dimline,
    // make the dim line as long as the text if the text is offset sideways from the 
    // extension lines.
    // If the text overlaps the extensions in both directions, it is centered and the
    // dimension line will hang out just a little each way, so don't do it in that case
    double t0, t1;
    lines[2].ClosestPointTo(text_rect[0], &t0);
    lines[2].ClosestPointTo(text_rect[1], &t1);
    if (fabs(t0 - t1) > 0.00001) // if text rect has some width
    {
      if (t0 > t1)
      {
        double t = t0; t0 = t1; t1 = t;
      }
      ON_Line l = lines[2];
      if (t0 < 0.0 && t1 < 1.0)
        l.from = lines[2].PointAt(t0);
      if (t1 > 1.0 && t0 > 0.0)
        l.to = lines[2].PointAt(t1);
      lines[2] = l;
    }
  }

  if (
    ON_DimStyle::TextLocation::InDimLine == text_location
    || ON::TextOrientation::InView == text_orientation
    || ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style
    ) // Means line has to be clipped around text
  {
    if (text_rect[0].DistanceTo(text_rect[2]) > ON_SQRT_EPSILON)
    {
      ON_Line dimline(lines[2]);
      ON_Line line_segs[2];

      int seg_count = ClipLineToTextRect(vp, dimline, text_rect, line_segs);
      if (0 == seg_count)
        isline[2] = false;
      else
      {
        if (seg_count > 0)
          lines[2] = line_segs[0];
        if (seg_count > 1)
        {
          lines[3] = line_segs[1];
          isline[3] = true;
        }
      }
    }
  }
  return true;
}

void ON_DimLinear::GetArrowXform(
  int which_end,
  double scale,
  bool arrowflipped,
  bool from_the_back,
  ON_Xform& arrow_xform_out) const
{
  ON_Xform xf, xfs, xfr;
  if (0 != which_end)
    which_end = 1;
  const ON_Plane& plane = Plane();
  ON_2dPoint ap = (which_end == 0) ? ArrowPoint1() : ArrowPoint2();
  xf.Rotation(ON_xy_plane, plane);
  ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0);
  xf = xf * xft;
  double rotang = 0.0;
  bool flip = false;
  if (arrowflipped != (which_end == 0))
    flip = !flip;
  if (from_the_back)
    flip = !flip;
  ON_2dVector v = DefPoint1() - DefPoint2();
  if (0.0 < (v * ON_2dVector::XAxis))
    flip = !flip;
  if (flip)
    rotang += ON_PI;
  rotang = fmod(rotang, (2.0 * ON_PI));
  if (ON_ZERO_TOLERANCE > fabs(rotang))
    rotang = 0.0;
  if (0.0 != rotang)
  {
    xfr.Rotation(rotang, ON_3dVector::ZAxis, ON_3dPoint::Origin);
    xf = xf * xfr;
  }
  xfs = ON_Xform::DiagonalTransformation(from_the_back ? -scale : scale, scale, scale);
  xf = xf * xfs;
  arrow_xform_out = xf;
}

//----------------------------------------------------------
// Class ON_DimAngular

ON_DimAngular::ON_DimAngular()
  : ON_Dimension(ON::AnnotationType::Angular)
{}

bool ON_DimAngular::IsValidAngularDimensionType(
  ON::AnnotationType annotation_type
)
{
  return (
    ON::AnnotationType::Angular == annotation_type ||
    ON::AnnotationType::Angular3pt == annotation_type
    );
}

bool ON_DimAngular::SetAngularDimensionType(
  ON::AnnotationType angular_dimension_type
)
{
  if (!ON_DimAngular::IsValidAngularDimensionType(angular_dimension_type))
  {
    ON_ERROR("Invalid angular_dimension_type parameter.");
    return false;
  }

  m_annotation_type = angular_dimension_type;

  return true;
}




bool ON_DimAngular::Write(
  ON_BinaryArchive& archive
  ) const
{
  const int content_version = 0;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Dimension::Internal_WriteDimension(archive))
      break;
    if (!archive.WriteVector(m_vec_1))
      break;
    if (!archive.WriteVector(m_vec_2))
      break;
    if (!archive.WriteDouble(m_ext_offset_1))
      break;
    if (!archive.WriteDouble(m_ext_offset_2))
      break;
    if (!archive.WritePoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimAngular::Read(
  ON_BinaryArchive& archive
  ) 
{
  *this = ON_DimAngular::Empty;

  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Dimension::Internal_ReadDimension(archive))
      break;
    if (!archive.ReadVector(m_vec_1))
      break;
    if (!archive.ReadVector(m_vec_2))
      break;
    if (!archive.ReadDouble(&m_ext_offset_1))
      break;
    if (!archive.ReadDouble(&m_ext_offset_2))
      break;
    if (!archive.ReadPoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimAngular::Transform(const ON_Xform& xform)
{
  bool rc = xform.IsIdentity();
  if (!rc)
  {
    rc = true;
    bool scaling = false;
    ON_3dVector v = m_plane.xaxis;
    v.Transform(xform);
    if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
      scaling = true;
    else
    {
      v = m_plane.yaxis;
      v.Transform(xform);
      if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
        scaling = true;
      else
      {
        v = m_plane.zaxis;
        v.Transform(xform);
        if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
          scaling = true;
      }
    }

    if (scaling)
    {
      ON_3dPoint extpt1(ON_3dPoint::NanPoint);
      ON_3dPoint extpt2(ON_3dPoint::NanPoint);
      ON_3dPoint arrowpt1(ON_3dPoint::NanPoint);
      ON_3dPoint arrowpt2(ON_3dPoint::NanPoint);
      ON_3dPoint dimlinept(ON_3dPoint::NanPoint);
      ON_3dPoint textpt(ON_3dPoint::NanPoint);
      rc = Get3dPoints(nullptr, &extpt1, &extpt2, &arrowpt1, &arrowpt2, &dimlinept, &textpt);
      if (rc)
      {
        rc = m_plane.Transform(xform);
        extpt1.Transform(xform);
        extpt2.Transform(xform);
        arrowpt1.Transform(xform);
        arrowpt2.Transform(xform);
        dimlinept.Transform(xform);

        AdjustFromPoints(m_plane, extpt1, extpt2, arrowpt1, arrowpt2, dimlinept);
        if (!UseDefaultTextPoint())
        {
          textpt.Transform(xform);
          SetUserTextPoint(textpt);
        }
      }
    }
    else
      rc = m_plane.Transform(xform);

    if (rc)
      ON_Geometry::Transform(xform);
  }
  return rc;
}

bool ON_DimAngular::Create(
  const ON_DimStyle* dim_style,
  ON_Arc arc,
  double offset
)
{
  ON_DimStyle local_dim_style;
  if (IsOverrideStylePointer(dim_style))
  {
    // make a local copy, because *this = ON_DimAngular::Empty will delete dim_style.
    local_dim_style = *dim_style;
    dim_style = &local_dim_style;
  }
  *this = ON_DimAngular::Empty;

  if (false == arc.IsValid())
    return false;

  const double arc_radius = arc.Radius();
  if (false == (arc_radius > 0.0))
    return false;

  if (nullptr == dim_style)
    dim_style = &ON_DimStyle::Default;

  ON_DimStyle* override_style = nullptr;
  ON_UUID dimstyle_id = dim_style->ParentIdIsNotNil() ? dim_style->ParentId() : dim_style->Id();
  if (ON_nil_uuid == dimstyle_id)
    dimstyle_id = ON_DimStyle::Default.Id();

  if (dim_style->IsOverrideDimStyleCandidate(dim_style->ParentId(), true, nullptr))
  {
    override_style = new ON_DimStyle(*dim_style);
    override_style->SetParentId(dimstyle_id);
  }

  *this = ON_DimAngular::Empty;
  m_dimstyle_id = dimstyle_id;

  SetPlane(arc.Plane());
  if (nullptr != override_style)
    SetOverrideDimensionStyle(override_style);

  double dim_radius = arc_radius;
  if (ON_IS_VALID(offset) && offset > -(1.0 - ON_SQRT_EPSILON)*arc_radius)
    dim_radius += offset;

  SetAngularDimensionType(
    dim_radius != arc_radius
    ? ON::AnnotationType::Angular
    : ON::AnnotationType::Angular3pt
  );

  const ON_Interval arc_angle = arc.DomainRadians();
  for (int i = 0; i < 2; i++)
  {
    ON_2dVector& vec = (i == 0) ? m_vec_1 : m_vec_2;
    const double a = arc_angle[i];
    vec.x = cos(a);
    vec.y = sin(a);
  }
  double a = arc_angle.ParameterAt(1.0 / 3.0);
  m_dimline_pt.x = dim_radius*cos(a);
  m_dimline_pt.y = dim_radius*sin(a);
  m_ext_offset_1 = arc.Radius();
  m_ext_offset_2 = arc.Radius();

  return true;
}

bool ON_DimAngular::Create(
  const ON_DimStyle* dim_style,
  ON_Line line1,
  ON_3dPoint point_on_line1,
  ON_Line line2,
  ON_3dPoint point_on_line2,
  ON_3dPoint point_on_angular_dimension_arc,
  bool bSetExtensionPoints
)
{
  ON_DimStyle local_dim_style;
  if (IsOverrideStylePointer(dim_style))
  {
    // make a local copy, because *this = ON_DimAngular::Empty will delete dim_style.
    local_dim_style = *dim_style;
    dim_style = &local_dim_style;
  }
  *this = ON_DimAngular::Empty;

  if (false == point_on_angular_dimension_arc.IsValid())
    return false;

  ON_3dVector dir1 = line1.Tangent();
  ON_3dVector dir2 = line2.Tangent();
  if (false == dir1.IsUnitVector())
    return false;
  if (false == dir2.IsUnitVector())
    return false;

  ON_3dPoint center = ON_3dPoint::UnsetPoint;
  ON_3dVector normal = ON_3dVector::ZeroVector;
  bool bColinear = false;
  for (;;)
  {
    normal = ON_CrossProduct(dir1, dir2);
    if ( false == normal.IsValid() )
      return false;
    if (false == normal.IsTiny() && normal.Unitize())
    {
      double t1 = ON_UNSET_VALUE;
      double t2 = ON_UNSET_VALUE;
      if (ON_Intersect(line1, line2, &t1, &t2) && ON_IsValid(t1) && ON_IsValid(t2))
      {
        const ON_3dPoint c1 = line1.PointAt(t1);
        if (false == c1.IsValid())
          return false;
        const ON_3dPoint c2 = line2.PointAt(t2);
        if (false == c2.IsValid())
          return false;
        const double h1 = (point_on_angular_dimension_arc - c1)*normal;
        const double h2 = (point_on_angular_dimension_arc - c2)*normal;
        if (h1 <= h2)
        {
          center = c1 + h1*normal;
          if (center.IsValid())
            break;
          return false;
        }
        if (h2 < h1)
        {
          center = c2 + h2*normal;
          if (center.IsValid())
            break;
          return false;
        }
        // NANs
        return false;
      }
    }

    // if lines are colinear and point_on_arc is not on the line, we have a semi circle (180 degrees)
    normal = ON_3dVector::ZeroVector;
    ON_3dPoint q1 = line1.ClosestPointTo(point_on_angular_dimension_arc);
    ON_3dPoint q2 = line2.ClosestPointTo(point_on_angular_dimension_arc);
    double d1 = point_on_angular_dimension_arc.DistanceTo(q1);
    double d2 = point_on_angular_dimension_arc.DistanceTo(q2);
    if (fabs(d1 - d2) <= ON_SQRT_EPSILON*(d1 + d2))
    {
      // lines are colinear
      bColinear = true;
      if (d1 >= d2)
      {
        center = q1;
        normal = ON_CrossProduct(dir1, point_on_angular_dimension_arc - center);
      }
      else
      {
        center = q2;
        normal = ON_CrossProduct(dir2, point_on_angular_dimension_arc - center);
      }
      if (false == normal.IsTiny() &&  normal.Unitize() )
        break;
    }

    // lines are not colinear or point_on_arc is on the line.
    return false;
  }

  // project lines to arc's plane
  for (int i = 0; i < 2; i++)
  {
    ON_3dVector& dir = (0 == i) ? dir1 : dir2;
    ON_Line& line = (0 == i) ? line1 : line2;
    const double hfrom = (line.from - point_on_angular_dimension_arc)*normal;
    const double hto = (line.to - point_on_angular_dimension_arc)*normal;
    ON_Line l1(line.from + hfrom*normal, line.to + hto*normal);
    const double h1from = (l1.from - point_on_angular_dimension_arc)*normal;
    const double h1to = (l1.to - point_on_angular_dimension_arc)*normal;
    if (fabs(h1from) < fabs(hfrom))
      line.from = l1.from;
    if (fabs(h1to) < fabs(hto))
      line.to = l1.to;
    if (false == line.IsValid())
      return false;
    dir = line.Tangent();
    if (false == dir.IsUnitVector())
      return false;
    if (!(fabs(dir*normal) < 1.0e-5))
      return false;
  }

  for (int i = 0; i < 2; i++)
  {
    ON_3dVector& dir = (0 == i) ? dir1 : dir2;
    const ON_Line& line = (0 == i) ? line1 : line2;
    ON_3dPoint& point_on_line = (0 == i) ? point_on_line1 : point_on_line2;
    ON_3dPoint p = line.PointAt(0.5);
    if (point_on_line.IsValid())
    {
      ON_3dPoint q = line.ClosestPointTo(point_on_line);
      if (q.IsValid())
        p = q;
    }
    if ((p - center)*dir < 0.0)
    {
      dir = -dir;
      normal = -normal;
    }
  }

  ON_Circle circle;
  if ( false == circle.plane.CreateFromNormal(center, normal) )
    return false;
  circle.plane.xaxis = dir1;
  circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector();
  if (false == circle.IsValid())
    return false;

  circle.radius = point_on_angular_dimension_arc.DistanceTo(circle.plane.origin);
  if (false == (circle.radius > 0.0) || false == circle.IsValid() )
    return false;



  ON_3dPoint arc_end1 = ON_3dPoint::UnsetPoint;
  ON_3dPoint arc_end2 = ON_3dPoint::UnsetPoint;
  for (int i = 0; i < 2; i++)
  {
    ON_Line& line = (0 == i) ? line1 : line2;
    ON_3dPoint& point_on_line = (0 == i) ? point_on_line1 : point_on_line2;
    ON_3dPoint& arc_end = (0 == i) ? arc_end1 : arc_end2;
    double t0, t1;
    ON_3dPoint p0, p1;
    if (2 != ON_Intersect(line, circle, &t0, p0, &t1, p1))
      return false;

    double d0 = ON_DBL_QNAN;
    double d1 = ON_DBL_QNAN;
    if (point_on_line.IsValid())
    {
      double t;
      if (false == line.ClosestPointTo(point_on_line, &t))
        return false;
      d0 = fabs(t0 - t);
      d1 = fabs(t1 - t);
      ON_3dPoint q = line.PointAt(t);
      if (point_on_line.DistanceTo(q) > ON_SQRT_EPSILON*line.Length() )
        point_on_line = q;
    }
    else
    {
      d0 = fabs(t0 - 0.5);
      d1 = fabs(t1 - 0.5);
    }
    if (d0 <= d1)
      arc_end = p0;
    else if (d1 < d0)
      arc_end = p1;
    else
      return false; // NAN
  }

  circle.plane.xaxis = (arc_end1 - center).UnitVector();
  circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector();
  if (false == circle.IsValid())
    return false;
  
  ON_2dVector a, b;
  if (false == circle.plane.ClosestPointTo(point_on_angular_dimension_arc, &a.x, &a.y))
    return false;
  if (false == circle.plane.ClosestPointTo(arc_end2, &b.x, &b.y))
    return false;
  double alpha = atan2(a.y, a.x);
  double beta = atan2(b.y, b.x);
  if (alpha < 0.0)
  {
    alpha += 2.0*ON_PI;
    if (alpha < 0.0)
      alpha = 0.0;
  }
  if (beta < 0.0)
  {
    beta += 2.0*ON_PI;
    if (beta < 0.0)
      beta = 0.0;
  }
  if (alpha > beta)
  {
    beta = 2.0*ON_PI - beta;
    if (false == (beta > 0.0 && beta < 2.0*ON_PI))
      return false;
    circle.plane.xaxis = (arc_end2 - center).UnitVector();
    circle.plane.yaxis = ON_CrossProduct(normal, circle.plane.xaxis).UnitVector();
    if (false == circle.IsValid())
      return false;
  }

  for (;;)
  {
    ON_Arc arc;
    if (false == arc.Create(circle, ON_Interval(0.0, beta)) || false == arc.IsValid())
      return false;

    if (false == Create(dim_style, arc, ON_UNSET_VALUE))
      break;
    
    SetAngularDimensionType(ON::AnnotationType::Angular);

    if (bSetExtensionPoints)
    {
      for (int i = 0; i < 2; i++)
      {
        const ON_3dPoint point_on_line = (i == 0) ? point_on_line1 : point_on_line2;
        if (false == point_on_line.IsValid())
          continue;

        const ON_2dVector vec = (i == 0) ? m_vec_1 : m_vec_2;
        double& ext_offset = (i == 0) ? m_ext_offset_1 : m_ext_offset_2;
        ON_2dVector e;
        if (circle.plane.ClosestPointTo(point_on_line, &e.x, &e.y))
        {
          double d = e*vec;
          if (d > 0.0 && fabs(d - arc.Radius()) > 0.001*arc.Radius())
            ext_offset = d;
        }
      }
    }
    return true;
  }

  *this = ON_DimAngular::Empty;
  return false;
}

// Creates dimension with extension lines starting at plane origin (arc center)
bool ON_DimAngular::Create(
  const ON_UUID style_id,
  const ON_Plane& plane,
  const ON_3dVector& ref_horizontal,
  const ON_3dPoint& center_pt,
  const ON_3dPoint& extension_pt1,    // point on first extension line
  const ON_3dPoint& extension_pt2,    // point on second extension line
  const ON_3dPoint& dimline_pt        // point on dimension arc
)
{
  m_dimstyle_id = style_id;
  SetAngularDimensionType(ON::AnnotationType::Angular3pt);
  //if (ON_nil_uuid == m_dimstyle_id)
  //  return true;

  bool rc = AdjustFromPoints(plane, center_pt, extension_pt1, extension_pt2, dimline_pt);
  SetAngularDimensionType(ON::AnnotationType::Angular3pt);
  SetHorizontalDirection( ON_2dVector(ref_horizontal) );
  return rc;
}

bool ON_DimAngular::Create(
  const ON_UUID style_id,
  const ON_Plane& plane,
  const ON_3dVector& ref_horizontal,
  const ON_3dPoint& extension_pt1,      // start of first extension line
  const ON_3dPoint& extension_pt2,      // start of second extension line
  const ON_3dPoint& direction_pt1,      // point on first extension vector
  const ON_3dPoint& direction_pt2,      // point on second extension vector
  const ON_3dPoint& dimline_pt          // point on dimension line
)
{
  m_dimstyle_id = style_id;
  //if (ON_nil_uuid == m_dimstyle_id)
  //  return true;

  bool rc = AdjustFromPoints(plane, extension_pt1, extension_pt2, direction_pt1, direction_pt2, dimline_pt);
  SetAngularDimensionType(ON::AnnotationType::Angular);
  SetHorizontalDirection( ON_2dVector(ref_horizontal) );
  return rc;
}

static bool VectorAngle(ON_2dVector v, double& angle)
{
  if (v.IsTiny())
    return false;
  v.Unitize();
  angle = atan2(v.y, v.x);
  while (angle < 0.0)
    angle += 2.0 * ON_PI;
  while (angle >= 2.0 * ON_PI)
    angle -= 2.0 * ON_PI;
  return true;
}

bool ON_DimAngular::FindAngleVertex(
  ON_Line lines[2],
  ON_3dPoint pickpoints[2],
  const ON_Plane& plane,
  ON_3dPoint& centerpoint_out)
{
  // Intersect lines to get centerpoint
  double a, b;
  if (ON_IntersectLineLine(lines[0], lines[1], &a, &b, 0.01, false))
  {
    centerpoint_out = lines[0].PointAt(a);
    return true;
  }
  else
  {
    // If the lines don't intersect, project to getpoint cplane and intersect
    ON_3dPoint from0, to0, from1, to1;
    from0 = plane.ClosestPointTo(lines[0].from);
    to0 = plane.ClosestPointTo(lines[0].to);
    from1 = plane.ClosestPointTo(lines[1].from);
    to1 = plane.ClosestPointTo(lines[1].to);
    ON_Line l0(from0, to0);
    ON_Line l1(from1, to1);
    if (ON_IntersectLineLine(l0, l1, &a, &b, 0.01, false))
    {
      centerpoint_out = lines[0].PointAt(a);
      return true;
    }
    else  // no line intersection, check for collinear
    {
      ON_3dVector v0 = l0.Direction();
      ON_3dVector v1 = l1.Direction();
      int isp = v0.IsParallelTo(v1);
      if (isp != 0) // Lines are parallel
      {
        ON_3dPoint pon = l0.ClosestPointTo(l1.from);
        if (pon.DistanceTo(l1.from) > ON_ZERO_TOLERANCE) // lines aren't collinear
        {
          return false;
        }
        else
        {
          // use endpoints of lines if they meet at a point, otherwise, 1/2 way between pickpoints
          centerpoint_out = (pickpoints[0] + pickpoints[1]) / 2.0;
          if (l0.from.DistanceTo(l1.from) < ON_ZERO_TOLERANCE || l0.from.DistanceTo(l1.to) < ON_ZERO_TOLERANCE)
            centerpoint_out = l0.from;
          else if (l0.to.DistanceTo(l1.from) < ON_ZERO_TOLERANCE || l0.to.DistanceTo(l1.to) < ON_ZERO_TOLERANCE)
            centerpoint_out = l0.to;
          return true;
        }
      }
    }
  }
  return false;
}


bool ON_DimAngular::AdjustFromPoints(
  const ON_Plane& plane,
  const ON_3dPoint& center_pt,
  const ON_3dPoint& extension_pt1,
  const ON_3dPoint& extension_pt2,
  const ON_3dPoint& dimline_pt
  )
{
  if (center_pt.DistanceTo(dimline_pt) < ON_ZERO_TOLERANCE)
    return false;

  ON_2dPoint def1, def2, dimline;
  ON_2dVector v;
  m_plane = plane;
  m_plane.origin = plane.ClosestPointTo(center_pt);
  if (!m_plane.ClosestPointTo(extension_pt1, &def1.x, &def1.y))
    return false;
  v = def1;
  if (v.Unitize())
  {
    m_plane.Rotate(v.y, v.x, plane.Normal());
    m_plane.ClosestPointTo(extension_pt1, &def1.x, &def1.y);
  }
  m_plane.ClosestPointTo(extension_pt2, &def2.x, &def2.y);
  m_plane.ClosestPointTo(dimline_pt, &dimline.x, &dimline.y);

  double a1 = ON_DBL_QNAN;
  double a2 = ON_DBL_QNAN;
  double ad = ON_DBL_QNAN;
  if (VectorAngle(ON_2dVector(def1), a1) && VectorAngle(ON_2dVector(def2), a2) && VectorAngle(ON_2dVector(dimline), ad))
  {
    if (ad > a2) // Swap the order of the points to get dimlinept between them
    {
      m_plane = plane;
      m_plane.origin = plane.ClosestPointTo(center_pt);
      if (!m_plane.ClosestPointTo(extension_pt2, &def1.x, &def1.y))
        return false;
      v = def1;
      if (v.Unitize())
      {
        m_plane.Rotate(v.y, v.x, plane.Normal());
        m_plane.ClosestPointTo(extension_pt2, &def1.x, &def1.y);
      }
      m_plane.ClosestPointTo(extension_pt1, &def2.x, &def2.y);
      m_plane.ClosestPointTo(dimline_pt, &dimline.x, &dimline.y);
    }
  }
  double offset1 = ((ON_2dVector)def1).Length();
  if (offset1 > ON_SQRT_EPSILON)
  {
    m_vec_1 = def1;
    m_vec_1.Unitize();
  }
  double offset2 = ((ON_2dVector)def2).Length();
  if (offset2 > ON_SQRT_EPSILON)
  {
    m_vec_2 = def2;
    m_vec_2.Unitize();
  }

  VectorAngle(m_vec_2, a2);
  ON_2dVector vd(m_vec_1);
  vd.Rotate(a2 / 3.0);
  double r = ((ON_2dVector)dimline).Length();
  m_dimline_pt = ON_2dPoint(vd * r);
  m_ext_offset_1 = offset1;
  m_ext_offset_2 = offset2;

  ClearText();

  return true;
}

bool ON_DimAngular::AdjustFromPoints(
  const ON_Plane& plane,
  const ON_3dPoint& extension_pt1,      // start of first extension line
  const ON_3dPoint& extension_pt2,      // start of second extension line
  const ON_3dPoint& direction_pt1,      // point on first extension vector
  const ON_3dPoint& direction_pt2,      // point on second extension vector
  const ON_3dPoint& dimline_pt          // point on dimension line
)
{
  if (!plane.IsValid() || !extension_pt1.IsValid() || !extension_pt2.IsValid() || !direction_pt1.IsValid() || !direction_pt2.IsValid() || !dimline_pt.IsValid())
    return false;

  ON_3dPoint pe1on = plane.ClosestPointTo(extension_pt1);
  ON_3dPoint pe2on = plane.ClosestPointTo(extension_pt2);
  ON_3dPoint pd1on = plane.ClosestPointTo(direction_pt1);
  ON_3dPoint pd2on = plane.ClosestPointTo(direction_pt2);

  if (pe1on.DistanceTo(pd1on) < ON_ZERO_TOLERANCE || pe2on.DistanceTo(pd2on) < ON_ZERO_TOLERANCE)
    return false;

  ON_3dPoint center_point = ON_3dPoint::Origin;

  ON_Line lines[2] = { ON_Line(pe1on, pd1on), ON_Line(pe2on, pd2on) };
  ON_3dPoint pickpoints[2] = { pe1on, pe2on };
  if (!ON_DimAngular::FindAngleVertex(lines, pickpoints, plane, center_point))
    return false;

  ON_3dPoint pd1 = pe1on;
  ON_3dPoint pd2 = pe2on;
  if (center_point.DistanceTo(pe1on) < ON_SQRT_EPSILON)
    pd1 = pd1on;
  if (center_point.DistanceTo(pe2on) < ON_SQRT_EPSILON)
    pd2 = pd2on;

  if (AdjustFromPoints(plane, center_point, pd1, pd2, dimline_pt))
  {
    m_ext_offset_1 = center_point.DistanceTo(pe1on);
    m_ext_offset_2 = center_point.DistanceTo(pe2on);
    return true;
  }
  return false;
}

bool ON_DimAngular::GetBBox(double* bmin, double* bmax, bool grow) const
{
  const ON_DimStyle* dimstyle = nullptr;
  return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow?true:false);
}

bool ON_DimAngular::GetAnnotationBoundingBox(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  double* boxmin,
  double* boxmax,
  bool bGrow
) const
{
  if (nullptr == dimstyle)
    dimstyle = &ON_DimStyle::Default;

  const ON_2dPoint hash_points[] = {
    ON_2dPoint(m_vec_1),
    ON_2dPoint(m_vec_2), 
    m_dimline_pt,
    ON_2dPoint(m_ext_offset_1,m_ext_offset_2) 
  };

  const ON_SHA1_Hash hash = Internal_GetBBox_InputHash(
    vp, 
    dimstyle, 
    dimscale, 
    m_user_text_point,
    (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])),
    hash_points
  );

  if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow))
    return true;

  if (nullptr == boxmin || nullptr == boxmax)
    return false;
  
  ON_3dVector view_xdir = ON_3dVector::XAxis;
  ON_3dVector view_ydir = ON_3dVector::YAxis;
  if (nullptr != vp)
  {
    view_xdir = vp->CameraX();
    view_ydir = vp->CameraY();
  }
  ON_Xform text_xform;
  GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform);

  ON_BoundingBox dim_box;

  const ON_TextContent* text = Text();
  ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin };

  if (nullptr != text && text->GetTightBoundingBox(dim_box))
  {
    text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0);
    text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0);
    text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0);
    text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0);
    for (int i = 0; i < 4; i++)
      text_rect[i].Transform(text_xform);  // Text + gap bounding rect
  }

  dim_box.Destroy();
  for (int i = 0; i < 4; i++)
  {
    dim_box.Set(text_rect[i], 0 < i ? true : false);
  }


  bool dimlines[2] = { false, false };
  ON_Line lines[2];
  bool dimarcs[2] = { false, false };
  ON_Arc arcs[2];
  if (nullptr != dimstyle && GetDisplayLines(vp, dimstyle, dimscale, text_rect, lines, dimlines, arcs, dimarcs, 2, 2))
  {
    for (int i = 0; i < 2; i++)
    {
      if (dimlines[i])
      {
        dim_box.Set(lines[i].from, true);
        dim_box.Set(lines[i].to, true);
      }
    }
    for (int i = 0; i < 2; i++)
    {
      if (dimarcs[i])
      {
        arcs[i].GetTightBoundingBox(dim_box, true);
      }
    }
  }

  double points[21];
  if(Get3dPoints((ON_3dPoint*)(&points[0]), (ON_3dPoint*)(&points[3]), (ON_3dPoint*)(&points[6]), (ON_3dPoint*)(&points[9]),
                 (ON_3dPoint*)(&points[12]), (ON_3dPoint*)(&points[15]), (ON_3dPoint*)(&points[18])))
    dim_box.Set(3, 0, 6, 3, points + 3, true);

  return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow);
}

bool ON_DimAngular::GetTextXform(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out);
}

bool ON_DimAngular::GetTextXform(
  const ON_Xform* model_xform,
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  if (nullptr == dimstyle)
    return false;

  // This gets the display text that's already on the dimension
  const ON_TextContent* text = Text();
  if (nullptr == text)
    return false;

  // See if the text needs remade because of some change in some property that
  // would change its appearance
  if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash())
  {
    ON_wString rtfstr = text->RtfText();
    ON::AnnotationType annotation_type = this->Type();
    bool wrapped = text->TextIsWrapped();
    double width = text->FormattingRectangleWidth();
    double rot = text->TextRotationRadians();
    const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot);
  }

  double text_width = 0.0;
  double text_height = 0.0;
  double text_gap = 0.0;
  double text_angle = 0.0;      // User set rotation - add to natural rotation
  double dim_text_angle_2d = 0.0;

  const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation();
  const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation();
  const ON_DimStyle::ContentAngleStyle text_angle_style = dimstyle->DimTextAngleStyle();


  ON_Xform t2dxf(1.0);         // Text plane to dimension plane rotation
  ON_Xform d2tpxf(1.0);        // Dimension plane to text point translation
  ON_Xform trxf(1.0);          // Text rotation around text plane origin point

  const ON_Plane& textplane = ON_xy_plane;
  const ON_Plane& dimplane = Plane();
  ON_3dPoint text_center = ON_3dPoint::Origin;

  ON_3dPoint cp[4];
  if (!text->Get3dCorners(cp))
    return false;

  text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0;
  text_width = (cp[1].x - cp[0].x) * dimscale;
  text_height = (cp[3].y - cp[0].y) * dimscale;

  text_xform_out = ON_Xform::IdentityTransformation;
  t2dxf.Rotation(textplane, dimplane);     // Rotate text from starting text plane (world xy) to dimension plane
  text_gap = dimstyle->TextGap() * dimscale;
  bool draw_forward = dimstyle->DrawForward();

  ON_2dPoint text_pt_2d = TextPoint();

  bool arrowflipped[2] = { false, false };
  bool text_outside = false;
  ON_Dimension::ForceText force_text = ForceTextPosition();
  ON_Dimension::ForceArrow force_arrow = ForceArrowPosition();
  if (ForceArrow::Outside == force_arrow)
    arrowflipped[0] = arrowflipped[1] = true;
  else if (ForceArrow::Inside == force_arrow)
    arrowflipped[0] = arrowflipped[1] = false;
  //else if (ON_PI < Measurement())  // No flipping 
  //  arrowflipped[0] = arrowflipped[1] = false;
  //else // Flipping arrows won't happen on more than half-circle angles
  {
    // See if arrows and text will all fit inside extension lines
    // or what has to be moved outside
    double asz = dimstyle->ArrowSize() * dimscale;
    double dist = Radius() * Measurement();

    double total_text_width = text_width;
    if (force_text != ON_Dimension::ForceText::Auto)
      total_text_width = 0.0;
    else if (0.0 < total_text_width)
      total_text_width += text_gap;

    if (force_text != ON_Dimension::ForceText::Auto &&
      force_text != ON_Dimension::ForceText::Inside)
      text_outside = true;

    static double arrow_width_factor = 1.5;
    double total_arrow_width = asz * arrow_width_factor * 2;  // min arrow tail space is asz/2

    if (arrowflipped[0])
      total_arrow_width -= (asz * arrow_width_factor);
    if (arrowflipped[1])
      total_arrow_width -= (asz * arrow_width_factor);

    if (total_arrow_width + total_text_width > dist)     // arrows + text dont fit
    {
      if (total_text_width > dist)   // text doesnt fit
      {
        // move text outside
        text_outside = true;
        if (total_arrow_width > dist && ForceArrow::Auto == force_arrow)  // arrows dont fit either
        {
          arrowflipped[0] = true;
          arrowflipped[1] = true;
        }
      }
      else if (ForceArrow::Auto == force_arrow)      // text fits
      {
        // flip arrows
        arrowflipped[0] = true;
        arrowflipped[1] = true;
      }
    }

    // Dimension's ON_TextContent display text is stored at wcs origin coords until it is drawn
    // text_xform positions text at the 3d point and rotation to draw
    if (fabs(text_pt_2d.x) < ON_SQRT_EPSILON && fabs(text_pt_2d.y) < ON_SQRT_EPSILON)
      text_pt_2d.Set(0.0, 0.0);

    if (text_outside && ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style && UseDefaultTextPoint())
    {
      double radius = Radius();
      // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width
      double x = text_width * 0.5 + text_gap;
      if (force_text == ON_Dimension::ForceText::Left)
      {
        //if (arrowflipped[0])
          x += 1.5 * asz * arrow_width_factor;
        text_pt_2d = ArrowPoint1();
        if (0.0 < radius)
        {
          double d_ang = x / radius;
          text_pt_2d.Rotate(-d_ang, ON_2dPoint::Origin);
        }
      }
      else
      {
        //if (arrowflipped[1])
          x += asz * arrow_width_factor;
        text_pt_2d = ArrowPoint2();
        if (0.0 < radius)
        {
          double d_ang = x / radius;
          text_pt_2d.Rotate(d_ang, ON_2dPoint::Origin);
        }
      }
    }
  }
  FlipArrow(0, arrowflipped[0]);
  FlipArrow(1, arrowflipped[1]);

  if (ON::TextOrientation::InPlane == text_orientation)
  {
    if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style)
    {
      // Rotation angle = 0 means the text is horizontal
      text_angle = 0.0; //TextRotation(); Was some gooffy 0 to 1 property from V5. Not degrees or radians
    }
    else if (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style)
    {
      text_angle = 0.0;
    }
    if (ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style)
    {
      ON_2dVector h = HorizontalDirection();
      double h_angle = atan2(h.y, h.x);
      text_angle += h_angle;
    }
  }
  
  // Moves the text to AboveLine if that's the mode
  ON_3dPoint text_point_3d = dimplane.PointAt(text_pt_2d.x, text_pt_2d.y);  // 3d text point
  ON_3dVector text_up_dir = text_point_3d - dimplane.origin;
  text_up_dir.Unitize();
  ON_3dVector text_right_dir = ON_CrossProduct(text_up_dir, dimplane.zaxis);
  ON_2dVector text_pt_dir_2d = text_pt_2d;  // From center toward text point
  text_pt_dir_2d.Unitize();
  if (ON::TextOrientation::InPlane == text_orientation &&
    ON_DimStyle::ContentAngleStyle::Horizontal != text_angle_style)
    dim_text_angle_2d = atan2(text_pt_dir_2d.y, text_pt_dir_2d.x) - (ON_PI / 2.0);  // How the dimension would rotate it
  else
    dim_text_angle_2d = 0.0;

  ON_Xform xfWorld2Cam;
  ON_3dVector text_right_in_view = text_right_dir;
  ON_3dVector text_z_in_view = dimplane.zaxis;
  if (nullptr != vp)
  {
    vp->GetXform(ON::coordinate_system::world_cs, ON::coordinate_system::camera_cs, xfWorld2Cam);
    text_right_in_view.Transform(xfWorld2Cam);
    text_z_in_view.Transform(xfWorld2Cam);
  }
  bool x_to_the_right = (text_right_in_view * ON_3dVector::XAxis) > -ON_SQRT_EPSILON;
  bool fromfront = text_z_in_view * ON_3dVector::ZAxis > 0.0;

  if (ON_DimStyle::TextLocation::AboveDimLine == text_location )
  {
    double d = text_height * 0.5 + text_gap;
    if (x_to_the_right != fromfront)
      d = -d;

    // Moves the text to AboveLine if that's the alignment mode
    text_pt_2d = text_pt_2d + (text_pt_dir_2d * d);
  }
  text_point_3d = dimplane.PointAt(text_pt_2d.x, text_pt_2d.y);
  d2tpxf = ON_Xform::TranslationTransformation(text_point_3d - dimplane.origin);                // Move text from dimplane origin to text point

  if (1.0e-2 < fabs(text_angle + dim_text_angle_2d))  // There's a rotation angle change of more than 1/100 radian (~1/2 degree)
    trxf.Rotation(text_angle + dim_text_angle_2d, ON_3dVector::ZAxis, ON_3dPoint::Origin); // hopefully dim_text_angle_2d already adjusts to horizontal

  text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);      // dimscale
  
  text_xform_out = trxf * text_xform_out;                  // text rotation
  text_xform_out = t2dxf * text_xform_out;                 // text plane to dim plane
  text_xform_out = d2tpxf * text_xform_out;                // dimension plane to text point

  ON_3dVector view_xdir = ON_3dVector::XAxis;
  ON_3dVector view_ydir = ON_3dVector::YAxis;
  ON_3dVector view_zdir = ON_3dVector::ZAxis;
  if (nullptr != vp)
  {
    view_xdir = vp->CameraX();
    view_ydir = vp->CameraY();
    view_zdir = vp->CameraZ();
  }
  ON_3dVector dim_xdir = Plane().xaxis;
  ON_3dVector dim_ydir = Plane().yaxis;
  if (nullptr != model_xform)
  {
    dim_xdir.Transform(*model_xform);
    dim_ydir.Transform(*model_xform);
  }

  if (ON::TextOrientation::InView == text_orientation)  // Draw dimension horizontal to view
  {

    ON_Xform tp2sxf;        // Text point to view plane rotation
    tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir);
    text_xform_out = tp2sxf * text_xform_out;
  }
  else if (draw_forward)
  {
    ON_3dVector text_right_dir_local(1.0, 0.0, 0.0);
    text_right_dir_local.Transform(text_xform_out);
    if (nullptr != model_xform)
      text_right_dir_local.Transform(*model_xform);
    if (text_right_dir_local.Unitize())
    {
      ON_3dVector zdir = ON_CrossProduct(dim_xdir, dim_ydir);
      ON_3dVector text_up_dir_local = ON_CrossProduct(zdir, text_right_dir_local);
      bool fx = (0.0 > view_xdir * text_right_dir_local);
      bool fy = (0.0 > view_ydir * text_up_dir_local);

      ON_Xform mxf;  // Mirror xform for backwards text to adjust DrawForward
      if (fx)
      {
        mxf.Mirror(text_center, ON_3dVector::XAxis);
        text_xform_out = text_xform_out * mxf;
      }
      if (fy)
      {
        mxf.Mirror(text_center, ON_3dVector::YAxis);
        text_xform_out = text_xform_out * mxf;
      }
    }
  }
  return true;
}


bool ON_DimAngular::UpdateDimensionText(const ON_DimStyle* dimstyle) const
{
  ON_wString displaytext;

  if (!GetAngleDisplayText(dimstyle, displaytext))
    return false;

  ON_TextContent* newtext = new ON_TextContent;
  if (nullptr != newtext)
  {
    bool wrapped = m_text ? m_text->TextIsWrapped() : false;
    double rect_width = m_text ? m_text->FormattingRectangleWidth() : 0.0;
    double rotation = m_text ? m_text->TextRotationRadians() : 0.0;
    if (newtext->Create(displaytext, Type(), dimstyle,   wrapped, rect_width, rotation))
    {
#ifdef _DEBUG
      newtext->IsValid();
#endif
      SetText(newtext);
      return true;
    }
  }
  return false;
}

bool ON_DimAngular::GetAngleDisplayText(
  const ON_DimStyle* dimstyle,
  ON_wString& displaytext) const
{
  if (nullptr == dimstyle)
    return false;

  double measurement = Measurement();
  const wchar_t* user_text = UserText();
  ON_TextContent::FormatAngleMeasurement(measurement, dimstyle, user_text, displaytext);
  return true;
}




double ON_DimAngular::Radius() const
{
  return ((ON_2dVector)m_dimline_pt).Length();
}

bool ON_DimAngular::GetAngles(double* start_ang, double* end_ang, double* mid_ang) const
{
  if (nullptr == start_ang || nullptr == end_ang)
    return false;
  //ON_2dVector vm = m_dimline_pt;
  bool rc = false;
  {
    *start_ang = atan2(m_vec_1.y, m_vec_1.x);
    *end_ang = atan2(m_vec_2.y, m_vec_2.x);
    rc = true;
  }

  if (rc && nullptr != mid_ang)
  {
    ON_2dVector vm = m_dimline_pt;
    if (vm.Unitize())
    {
      *mid_ang = atan2(vm.y, vm.x);
      rc = true;
    }
    else
      rc = false;
  }
  return rc;
}

// Returns angle between |defpt1 - centerpt| and |defpt2 - centerpt| in radians
double ON_DimAngular::Measurement() const
{
  double a1 = 0.0, a2 = 0.0, am = 0.0;
  GetAngles(&a1, &a2, &am);
  double a = 0.0;
  if(ON_ZERO_TOLERANCE > fabs(a1))
    a1 = 0.0;
  else
  {
    a2 -= a1;
    am -= a1;
    a1 = 0.0;
  }

  if(a2 < 0.0)
    a2 += 2.0 * ON_PI;
  if(am < 0.0)
    am += 2.0 * ON_PI;

  if(am > a1)
  {
    if(am < a2)
      a = a2 - a1;
    else
      a = a2;
  }
  return a;
}

// Returns midpoint of dimline
ON_2dPoint ON_DimAngular::DefaultTextPoint() const
{
  ON_2dPoint tp(0.0, 0.0);
  double a1 = 0.0, a2 = 0.0, am = 0.0;
  if (GetAngles(&a1, &a2, &am))
  {
    if (a2 < 0.0)
      a2 += ON_PI * 2.0;
    double a0 = a2 - a1;
    ON_2dPoint c = CenterPoint();
    ON_2dPoint d = DimlinePoint();
    double r = c.DistanceTo(d);
    double a = a0 * 0.5;
    double cosa0 = cos(a);
    double sina0 = sin(a);
    tp.x = r * cosa0;
    tp.y = r * sina0;
  }
  return tp;
}

ON_2dPoint ON_DimAngular::CenterPoint() const
{
  return ON_2dPoint::Origin;
}

ON_2dPoint ON_DimAngular::DefPoint1() const
{
  return m_vec_1 * m_ext_offset_1;
}

ON_2dPoint ON_DimAngular::DefPoint2() const
{
  return m_vec_2 * m_ext_offset_2;
}

ON_2dPoint ON_DimAngular::DimlinePoint() const
{
  return m_dimline_pt;
}

ON_2dPoint ON_DimAngular::ArrowPoint1() const
{
    return m_vec_1  * Radius();
}

ON_2dPoint ON_DimAngular::ArrowPoint2() const
{
  return m_vec_2  * Radius();
}

ON_2dPoint ON_DimAngular::UserTextPoint() const
{
  return m_user_text_point;
}

void ON_DimAngular::SetUserTextPoint(const ON_3dPoint& point)
{
  ON_2dPoint p;
  if (m_plane.ClosestPointTo(point, &p.x, &p.y))
    m_user_text_point = p;
}

ON_2dVector ON_DimAngular::ExtDir1() const
{
  return m_vec_1;
}

ON_2dVector ON_DimAngular::ExtDir2() const
{
  return m_vec_2;
}

void ON_DimAngular::SetExtDir1(const ON_2dVector& dir1)
{
  m_vec_1 = dir1;
}

void ON_DimAngular::SetExtDir2(const ON_2dVector& dir2)
{
  m_vec_2 = dir2;
}

void ON_DimAngular::Set2dCenterPoint(ON_2dPoint pt)
{
  // Move plane origin to pt
  ON_2dVector v(-pt.x, -pt.y);
  ON_3dPoint p = m_plane.PointAt(pt.x, pt.y);
  m_plane.origin = p;
  m_plane.UpdateEquation();
  m_dimline_pt = m_dimline_pt + v;
}

void ON_DimAngular::Set2dDefPoint1(ON_2dPoint pt)
{
  // Rotate plane to keep first line on x axis
  ON_2dVector xdir = pt;
  double r = xdir.Length();
  if (!xdir.Unitize())
    return;

  if (fabs((xdir * ON_2dVector::XAxis) - 1.0) > ON_SQRT_EPSILON)
  {
    m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis);
    m_vec_2.Rotate(-xdir.y, xdir.x);
    m_dimline_pt.Rotate(-xdir.y, xdir.x, ON_2dPoint::Origin);
    m_ext_offset_1 = r;
  }
}

void ON_DimAngular::Set2dDefPoint2(ON_2dPoint pt)
{
  ON_2dVector xdir = pt;
  if (xdir.Unitize())
    m_vec_2 = xdir;
}

void ON_DimAngular::Set2dDimlinePoint(ON_2dPoint pt)
{
  m_dimline_pt = pt;
}

//void ON_DimAngular::Set3dCenterPoint(ON_3dPoint pt)
//{
//  m_plane.origin = pt;
//}
//
//void ON_DimAngular::Set3dDefPoint1(ON_3dPoint pt)
//{
//  ON_3dVector x = pt - m_plane.origin;
//  ON_3dVector z = m_plane.zaxis;
//  if (0.999 < x * z) // x & z nearly parallel
//    z = ON_CrossProduct(x, m_plane.yaxis);
//  if (z.Unitize())
//  {
//    ON_3dVector y = ON_CrossProduct(z, x);
//    if (y.Unitize())
//      m_plane.CreateFromFrame(m_plane.origin, x, y);
//  }
//}
//
//void ON_DimAngular::Set3dDefPoint2(ON_3dPoint pt)
//{
//  ON_3dVector y = pt - m_plane.origin;
//  ON_3dVector x = m_plane.xaxis;
//  if (0.99998 > x * y) // x & v not parallel
//  {
//    ON_3dVector z = ON_CrossProduct(x, y);
//    if (z.Unitize())
//    {
//      y = ON_CrossProduct(z, x);
//      if (y.Unitize())
//        m_plane.CreateFromFrame(m_plane.origin, x, y);
//    }
//  }
//  double u, v;
//  if (m_plane.ClosestPointTo(pt, &u, &v))
//    m_def_pt_2.Set(u, v);
//}
//
//void ON_DimAngular::Set3dDimlinePoint(ON_3dPoint pt)
//{
//  ON_2dPoint p;
//  if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
//    Set2dDimlinePoint(p);
//}

bool ON_DimAngular::Get3dPoints(
  ON_3dPoint* center,
  ON_3dPoint* defpt1, ON_3dPoint* defpt2,
  ON_3dPoint* arrowpt1, ON_3dPoint* arrowpt2,
  ON_3dPoint* dimline, ON_3dPoint* textpt) const
{
  bool rc = true;
  if (nullptr != center)
    *center = m_plane.origin;

  if (nullptr != defpt1)
  {
    ON_2dPoint p1 = m_vec_1 * m_ext_offset_1;
    *defpt1 = m_plane.PointAt(p1.x, p1.y);
  }
  if (nullptr != defpt2)
  {
    ON_2dPoint p2 = m_vec_2 * m_ext_offset_2;
    *defpt2 = m_plane.PointAt(p2.x, p2.y);
  }

  if (nullptr != dimline)
  {
    if(ON_3dPoint::UnsetPoint != m_dimline_pt)
    *dimline = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y);
    else
    {
      *dimline = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != arrowpt1)
  {
    ON_2dPoint p = ArrowPoint1();
    if (ON_2dPoint::UnsetPoint != p)
      *arrowpt1 = m_plane.PointAt(p.x, p.y);
    else
    {
      *arrowpt1 = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != arrowpt2)
  {
    ON_2dPoint p = ArrowPoint2();
    if (ON_2dPoint::UnsetPoint != p)
      *arrowpt2 = m_plane.PointAt(p.x, p.y);
    else
    {
      *arrowpt2 = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != textpt)
  {
    ON_2dPoint textpt2d = ON_2dPoint::UnsetPoint;
    if (m_use_default_text_point)
    {
      textpt2d = DefaultTextPoint();
    }
    else
    {
      if (ON_3dPoint::UnsetPoint != m_user_text_point)
        textpt2d = m_user_text_point;
    }

    if (ON_3dPoint::UnsetPoint != textpt2d)
      *textpt = m_plane.PointAt(textpt2d.x, textpt2d.y);
    else
    {
      *textpt = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }
  return rc;
}

bool ON_DimAngular::GetDisplayLines(
  const ON_Viewport* vp,
  const ON_DimStyle* style,
  double dimscale,
  const ON_3dPoint text_rect[4],
  ON_Line lines[2],
  bool isline[2],
  ON_Arc arcs[2],
  bool isarc[2],
  int maxlines,
  int maxarcs) const
{
  if (maxlines != 2 || maxarcs != 2)
  {
    ON_ERROR("Wrong linecount calling ON_DimAngular::GetDisplayLines.\n");
    return false;
  }
  if (nullptr == style)
    return false;

  isline[0] = isline[1] = isarc[0] = isarc[1] = false;
  double eo = style->ExtOffset() * dimscale;
  double ee = style->ExtExtension() * dimscale;
  double radius = Radius();
  if (1.0e-8 > radius)
    return false;

  ON_2dVector v0 = m_vec_1 * m_ext_offset_1;
  v0.Unitize();
  ON_2dPoint arcpt_0 = m_vec_1 * radius;

  ON_2dVector v1 = m_vec_2 * m_ext_offset_2;
  v1.Unitize();
  ON_2dPoint arcpt_1 = m_vec_2 * radius;

  if (!style->SuppressExtension1())
  {
    double eo0 = eo;
    double ee0 = ee;
    if (m_ext_offset_1 > radius)
    {
      eo0 = -eo0;
      ee0 = -ee0;
    }
    ON_2dPoint lpt0 = m_vec_1  * (m_ext_offset_1 + eo0);
    ON_2dPoint lpt1 = m_vec_1  * (radius + ee0);

    lines[0].from = m_plane.PointAt(lpt0.x, lpt0.y);
    lines[0].to   = m_plane.PointAt(lpt1.x, lpt1.y);
    isline[0] = true;
  }

  if (!style->SuppressExtension2())
  {
    double eo1 = eo;
    double ee1 = ee;
    if (m_ext_offset_2 > radius)
    {
      eo1 = -eo1;
      ee1 = -ee1;
    }
    ON_2dPoint lpt0 = m_vec_2  * (m_ext_offset_2 + eo1);
    ON_2dPoint lpt1 = m_vec_2  * (radius + ee1);
    lines[1].from = m_plane.PointAt(lpt0.x, lpt0.y);
    lines[1].to   = m_plane.PointAt(lpt1.x, lpt1.y);
    isline[1] = true;
  }

  ON_Circle c(ON_xy_plane, radius);
  double a0 = 0.0, a1 = 0.0;
  if (c.ClosestPointTo(ON_3dPoint(arcpt_0), &a0) &&
      c.ClosestPointTo(ON_3dPoint(arcpt_1), &a1))
  {
    double dim_ext[2] = { style->DimExtension() * dimscale, style->DimExtension() * dimscale };
    if (fabs(dim_ext[0]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(0))
      dim_ext[0] = style->ArrowSize() * dimscale * 1.5;
    if (fabs(dim_ext[1]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(1))
      dim_ext[1] = style->ArrowSize() * dimscale * 1.5;

    double dim_ext_ang[2] = { 0.0, 0.0 };
    if (0.0 < dim_ext[0])
      dim_ext_ang[0] = dim_ext[0] / radius;
    if (0.0 < dim_ext[1])
      dim_ext_ang[1] = dim_ext[1] / radius;

    while (a0 + ON_ZERO_TOLERANCE > ON_PI * 2.0)
      a0 -= ON_PI * 2.0;
    a0 -= dim_ext_ang[0];
    a1 += dim_ext_ang[1];
    if (arcs[0].Create(c, ON_Interval(a0, a1)))
    {
      ON_Xform xf;
      xf.Rotation(ON_xy_plane, m_plane);
      arcs[0].Transform(xf);
      isarc[0] = true;
    }
  }

  // This part clips dimarc to textbox except when text is above line
  //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_mode = style->TextAlignment();
  const ON_DimStyle::TextLocation text_location = style->DimTextLocation();
  const ON::TextOrientation text_orientation = style->DimTextOrientation();
  const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle();

  //if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine != text_mode)
  if (
    ON_DimStyle::TextLocation::InDimLine == text_location
    || ON::TextOrientation::InView == text_orientation
    || ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style
    )
  {
    if (text_rect[0].DistanceTo(text_rect[2]) > ON_SQRT_EPSILON)
    {
      ON_Arc dimarc(arcs[0]);
      ON_Arc arc_segs[2];

      if (dimarc.IsValid())
      {
        int seg_count = ClipArcToTextRect(vp, dimarc, text_rect, arc_segs);
        if (0 == seg_count)
          isarc[0] = false;
        else
        {
          if (seg_count > 0)
            arcs[0] = arc_segs[0];
          if (seg_count > 1)
          {
            arcs[1] = arc_segs[1];
            isarc[1] = true;
          }
        }
      }
    }
  }

  return true;
}

void ON_DimAngular::GetArrowXform(
  int which_end,
  double arrowlength,
  bool arrowflipped,
  bool from_the_back,
  ON_Xform& arrow_xform_out) const
{
  ON_Xform xf(1.0), xfp, xfs, xfr;
  if (0 != which_end)
    which_end = 1;
  ON_2dPoint ap = (0 == which_end) ? ArrowPoint1() : ArrowPoint2();
  xfp.Rotation(ON_xy_plane, Plane());
  ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0);
  double rotang = ON_PI / 2.0;
  if (1 == which_end)
  {
    ON_2dVector v = ap;
    v.Unitize();
    rotang = atan2(v.y, v.x);
    rotang += ON_PI / 2.0;
  }
  if (from_the_back)
    rotang += ON_PI;
  if (arrowflipped != (which_end == 0))
    rotang += ON_PI;
  while (rotang >= (2.0 * ON_PI))
    rotang -= (2.0 * ON_PI);
  while (rotang < 0.0)
    rotang += (2.0 * ON_PI);

  // little adjustment so dimension arc leaves arrow in the middle
  double f = (arrowlength * 0.5) / Radius();
  if (f > 1.0) f = 1.0;
  double adjust = asin(f);
  if (which_end == 1)
    adjust = -adjust;
  if (ArrowIsFlipped(which_end))
    adjust = -adjust;

  xfr.Rotation(rotang+adjust, ON_3dVector::ZAxis, ON_3dPoint::Origin);
  xf = xft * xfr;
  xf = xfp * xf;
  xfs = ON_Xform::DiagonalTransformation(from_the_back ? -arrowlength : arrowlength, arrowlength, arrowlength);
  arrow_xform_out = xf * xfs;
}

bool ON_DimAngular::UpdateDimensionText(
  ON::LengthUnitSystem units,
  const ON_DimStyle* dimstyle) const
{
  return false;
}

bool ON_DimAngular::GetDistanceDisplayText(
  ON::LengthUnitSystem units,
  const ON_DimStyle* dimstyle,
  ON_wString& displaytext) const
{
  return false;
}


//----------------------------------------------------------
// Class ON_DimRadial

ON_DimRadial::ON_DimRadial()
  : ON_Dimension(ON::AnnotationType::Radius)
{}

bool ON_DimRadial::IsValidRadialDimensionType(
  ON::AnnotationType annotation_type
)
{
  return (
    ON::AnnotationType::Radius == annotation_type
    || ON::AnnotationType::Diameter == annotation_type
    );
}

bool ON_DimRadial::SetRadialDimensionType(
  ON::AnnotationType radial_dimension_type
)
{
  if ( false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type) )
  {
    ON_ERROR("Invalid radial_dimension_type parameter.");
    return false;
  }

  m_annotation_type = radial_dimension_type;
  ON_wString usertext
    = (ON::AnnotationType::Diameter == m_annotation_type)
    ? ON_wString::DiameterSymbol
    : ON_wString::RadiusSymbol;

  usertext += "<>";

  SetUserText(usertext);

  return true;
}




bool ON_DimRadial::Write(
  ON_BinaryArchive& archive
  ) const
{
  const int content_version = 0;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Dimension::Internal_WriteDimension(archive))
      break;
    if (!archive.WritePoint(m_radius_pt))
      break;
    if (!archive.WritePoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimRadial::Read(
  ON_BinaryArchive& archive
  )
{
  *this = ON_DimRadial::Empty;

  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Dimension::Internal_ReadDimension(archive))
      break;
    if (!archive.ReadPoint(m_radius_pt))
      break;
    if (!archive.ReadPoint(m_dimline_pt))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimRadial::Transform(const ON_Xform& xform)
{
  bool rc = xform.IsIdentity();
  if (!rc)
  {
    rc = true;
    bool scaling = false;
    ON_3dVector v = m_plane.xaxis;
    v.Transform(xform);
    if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
      scaling = true;
    else
    {
      v = m_plane.yaxis;
      v.Transform(xform);
      if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
        scaling = true;
      else
      {
        v = m_plane.zaxis;
        v.Transform(xform);
        if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
          scaling = true;
      }
    }

    if (scaling)
    {
      ON_3dPoint radius_pt_0(ON_3dPoint::UnsetPoint);
      ON_3dPoint dimline_pt_0(ON_3dPoint::UnsetPoint);
      if (Get3dPoints(nullptr, &radius_pt_0, &dimline_pt_0, nullptr))
      {
        ON_2dPoint radius_pt(ON_2dPoint::NanPoint), dimline_pt(ON_2dPoint::NanPoint);
        rc = m_plane.Transform(xform);
        radius_pt_0.Transform(xform);
        dimline_pt_0.Transform(xform);
        if (rc && !m_plane.ClosestPointTo(radius_pt_0, &radius_pt.x, &radius_pt.y))
          rc = false;
        else if (rc && !m_plane.ClosestPointTo(dimline_pt_0, &dimline_pt.x, &dimline_pt.y))
          rc = false;
        if (rc)
        {
          Set2dRadiusPoint(radius_pt);
          Set2dDimlinePoint(dimline_pt);
        }
      }
    }
    else
      rc = m_plane.Transform(xform);

    if (rc)
      ON_Geometry::Transform(xform);
  }
  return rc;
}

bool ON_DimRadial::Create(
  ON::AnnotationType radial_dimension_type,
  const ON_UUID style_id,
  const ON_Plane& plane,
  const ON_3dPoint& center_pt,
  const ON_3dPoint& radius_pt,
  const ON_3dPoint& dimline_pt)
{
  m_dimstyle_id = style_id;
  if (ON_nil_uuid == m_dimstyle_id)
    return true; // .NET dialog hack

  if (false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type))
  {
    ON_ERROR("Invalid radial_dimension_type parameter.");
    return false;
  }

  if (!plane.IsValid() || !center_pt.IsValid() || !center_pt.IsValid() || !radius_pt.IsValid() || !dimline_pt.IsValid())
    return false;

  bool rc = SetRadialDimensionType(radial_dimension_type);
  m_plane = plane;

  if (rc)
  {
    double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0;
    ON_3dPoint radius_pton;
    ON_3dPoint dimline_pton;
    ON_3dVector v1;
    ON_3dVector v2;

    m_plane.origin = plane.ClosestPointTo(center_pt);
    rc = m_plane.ClosestPointTo(radius_pt, &x1, &y1);
    if (rc)
    {
      rc = m_plane.ClosestPointTo(dimline_pt, &x2, &y2);
      if (rc)
      {
        radius_pton = m_plane.PointAt(x1, y1);
        dimline_pton = m_plane.PointAt(x2, y2);
        v1 = radius_pton - m_plane.origin;
        v2 = dimline_pton - m_plane.origin;
        rc = v1.Unitize() && v2.Unitize();
      }
    }
    if (rc)
    {
      m_radius_pt.Set(x1, y1);
      m_dimline_pt.Set(x2, y2);
    }
  }
  return rc;
}

bool ON_DimRadial::AdjustFromPoints(
  const ON_Plane& plane,
  const ON_3dPoint& center_pt,
  const ON_3dPoint& radius_pt,
  const ON_3dPoint& dimline_pt
  )
{
  ON_2dPoint center_pt2d(ON_2dPoint::Origin), radius_pt2d(ON_2dPoint::Origin), dimline_pt2d(ON_2dPoint::Origin);
  m_plane = plane;
  m_plane.origin = plane.ClosestPointTo(center_pt);
  //if (!plane.ClosestPointTo(center_pt, &c_pt.x, &c_pt.y))
  //  return false;
  if (!plane.ClosestPointTo(radius_pt, &radius_pt2d.x, &radius_pt2d.y))
    return false;
  if (!plane.ClosestPointTo(dimline_pt, &dimline_pt2d.x, &dimline_pt2d.y))
    return false;

  ON_2dVector arrow_dir = radius_pt2d - center_pt2d;
  ON_2dVector drag_dir = dimline_pt2d - center_pt2d;
  double drag_dist = drag_dir.Length();
  if (!arrow_dir.Unitize() || !drag_dir.Unitize())
    return false;

  if (fabs(center_pt2d.y - radius_pt2d.y) < ON_SQRT_EPSILON)
  {
    // radial line is horizontal - skip intersecting with dimline
    dimline_pt2d = arrow_dir * drag_dist;
  }

  //m_plane = plane;
  m_radius_pt.Set(radius_pt2d.x, radius_pt2d.y);
  m_dimline_pt.Set(dimline_pt2d.x, dimline_pt2d.y);
  
  return true;
}

bool ON_DimRadial::GetBBox(double* bmin, double* bmax, bool grow) const
{
  const ON_DimStyle* dimstyle = nullptr;
  return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow ? true : false);
}

bool ON_DimRadial::GetAnnotationBoundingBox(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  double* boxmin,
  double* boxmax,
  bool bGrow
) const
{
  if (nullptr == dimstyle)
    dimstyle = &ON_DimStyle::Default;

  const ON_2dPoint hash_points[] = {
    ON_2dPoint(m_radius_pt),
    ON_2dPoint(m_dimline_pt)
  };

  const ON_SHA1_Hash hash = Internal_GetBBox_InputHash(
    vp, 
    dimstyle, 
    dimscale, 
    m_user_text_point,
    (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])),
    hash_points
  );

  if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow))
    return true;

  if (nullptr == boxmin || nullptr == boxmax)
    return false;

  ON_Xform text_xform;
  GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform);

  ON_BoundingBox dim_box;

  const ON_TextContent* text = Text();
  ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin };

  if (nullptr != text && text->GetTightBoundingBox(dim_box))
  {
    text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0);
    text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0);
    text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0);
    text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0);
    for (int i = 0; i < 4; i++)
      text_rect[i].Transform(text_xform);  // Text + gap bounding rect
  }

  dim_box.Destroy();
  for (int i = 0; i < 4; i++)
  {
    dim_box.Set(text_rect[i], 0 < i ? true : false);
  }

#define dimlinecount 9
  bool dimlines[dimlinecount] = { false, false, false, false, false, false, false, false, false };
  ON_Line lines[dimlinecount];
  if (GetDisplayLines(dimstyle, dimscale, text_rect, lines, dimlines, dimlinecount))
  {
    for (int i = 0; i < dimlinecount; i++)
    {
      if (dimlines[i])
      {
        dim_box.Set(lines[i].from, true);
        dim_box.Set(lines[i].to, true);
      }
    }
  }
#undef dimlinecount

  return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow);
}

bool ON_DimRadial::GetTextXform(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out);
}

bool ON_DimRadial::GetTextXform(
  const ON_Xform* model_xform,
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  const ON_TextContent* text = Text();
  if (nullptr == text)
    return false;
  if (nullptr == dimstyle)
    return false;

  // See if the text needs remade because of some change in some property that
  // would change its appearance
  if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash())
  {
    ON_wString rtfstr = text->RtfText();
    ON::AnnotationType annotation_type = this->Type();
    bool wrapped = text->TextIsWrapped();
    double width = text->FormattingRectangleWidth();
    double rot = text->TextRotationRadians();
    const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot);
  }

  ON_3dPoint text_center = ON_3dPoint::Origin;

  ON_3dPoint cp[4];
  if (!text->Get3dCorners(cp))
    return false;

  double text_width = 0.0;
  double text_height = 0.0;
  double line_height = 0.0;
  double text_gap = 0.0;
  double landing_length = 0.0;

  const ON::TextOrientation text_orientation = dimstyle->DimRadialTextOrientation();
  const ON_DimStyle::ContentAngleStyle text_alignment = dimstyle->DimRadialTextAngleStyle();
  const ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment();
  //const ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment();

  // Always move text to InLine if it's going to be horizontal and in the view plane
  const ON_DimStyle::TextLocation text_location =   
    (ON::TextOrientation::InView == text_orientation)
    ? ON_DimStyle::TextLocation::InDimLine
    : dimstyle->DimRadialTextLocation();

  const ON_Plane& textplane = ON_xy_plane;
  bool draw_forward = dimstyle->DrawForward();

  ON_Xform dimplane_xf(1.0);

  dimplane_xf.Rotation(textplane, Plane());     // Rotate text from world xy to dimension plane

  ON_Xform textpt_xf(1.0);           // Dimension plane to text point translation
  ON_Xform textrot_xf(1.0);          // Text rotation around text plane origin point
  ON_Xform textscale_xf(1.0);
  
  text_center = (cp[0] * dimscale + cp[2] * dimscale) / 2.0;
  text_width = (cp[1].x - cp[0].x) * dimscale;
  text_height = (cp[3].y - cp[0].y) * dimscale;
  line_height = dimstyle->TextHeight() * dimscale;
  text_gap = dimstyle->TextGap() * dimscale;
  landing_length = dimstyle->LeaderLandingLength() * dimscale;

  ON_2dPoint dimline_pt = DimlinePoint();
  ON_2dPoint radius_pt = RadiusPoint();
  ON_2dPoint center_pt(0.0, 0.0);
  ON_2dPoint kink_pt(ON_2dPoint::UnsetPoint);
  ON_2dVector radius_vector = radius_pt;
  if (!radius_vector.Unitize())
    return false;
  if (fabs(dimline_pt.x) < ON_SQRT_EPSILON)
    dimline_pt.x = 0.0;
  if (fabs(dimline_pt.y) < ON_SQRT_EPSILON)
    dimline_pt.y = 0.0;
  if (fabs(radius_pt.x) < ON_SQRT_EPSILON)
    radius_pt.x = 0.0;
  if (fabs(radius_pt.y) < ON_SQRT_EPSILON)
    radius_pt.y = 0.0;
  ON_2dVector tail_dir(1.0, 0.0);

  kink_pt = KneePoint();

  ON_3dVector dim_xdir = Plane().xaxis;
  ON_3dVector dim_ydir = Plane().yaxis;
  if (nullptr != model_xform)
  {
    dim_xdir.Transform(*model_xform);
    dim_ydir.Transform(*model_xform);
  }

  ON_3dVector view_x = ON_3dVector::XAxis;
  ON_3dVector view_y = ON_3dVector::YAxis;
  ON_3dVector view_z = ON_3dVector::ZAxis;
  if (nullptr != vp)
  {
    view_x = vp->CameraX();
    view_y = vp->CameraY();
    view_z = vp->CameraZ();
  }

  // Text is horizontal in CPlane, not view
  if (ON_DimStyle::ContentAngleStyle::Horizontal == text_alignment &&
      ON_2dPoint::UnsetPoint != kink_pt)
  {
    if (fabs(dimline_pt.x - kink_pt.x) < ON_SQRT_EPSILON)
    {
      // kink at dimlinept
      if (dimline_pt.x - radius_pt.x > -ON_SQRT_EPSILON)
        tail_dir.Set(1.0, 0.0);
      else
        tail_dir.Set(-1.0, 0.0);
    }
    else
    {
      if (dimline_pt.x - kink_pt.x > -ON_SQRT_EPSILON)
        tail_dir.Set(1.0, 0.0);
      else
        tail_dir.Set(-1.0, 0.0);
    }
  }
  // Text is aligned with last leader segment
  else if (ON_DimStyle::ContentAngleStyle::Aligned == text_alignment) // && no kink point
  {
    double d = ((ON_2dVector)dimline_pt).Length();
    if (((ON_2dVector)dimline_pt) * ((ON_2dVector)radius_pt) < 0.0)
      d = -d;  // text point is on the other side of center from arrow point
    dimline_pt = radius_vector * d;  // With no kink, adjust dimline point to line up with radius point
    tail_dir = dimline_pt - radius_pt;
    if (ON_SQRT_EPSILON > tail_dir.Length() || !tail_dir.Unitize())
      tail_dir = radius_pt - center_pt;
  }
  if (dimline_pt.DistanceTo(DimlinePoint()) > ON_SQRT_EPSILON)
    const_cast<ON_DimRadial*>(this)->Set2dDimlinePoint(dimline_pt);

  if (!tail_dir.Unitize())
    return false;

  // Text position adjustment
  ON_2dVector shift(0.0, 0.0);
  shift.y = -line_height / 2.0;
  if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
    shift.y = text_gap + text_height;

  shift.x = text_gap;
  shift.x += landing_length;
  //shift.x += text_width / 2.0;

  if (-ON_SQRT_EPSILON > tail_dir.x)  // text to left
  {
    switch (halign)
    {
    default:
    case ON::TextHorizontalAlignment::Left:
      shift.x += text_width;
      break;
    case ON::TextHorizontalAlignment::Right:
      break;
    case ON::TextHorizontalAlignment::Center:
      shift.x += text_width / 2.0;
      break;
    }
    shift.y = -shift.y;
  }
  else
  {
    switch (halign)
    {
    default:
    case ON::TextHorizontalAlignment::Left:
      break;
    case ON::TextHorizontalAlignment::Right:
      shift.x += text_width;
      break;
    case ON::TextHorizontalAlignment::Center:
      shift.x += text_width / 2.0;
      break;
    }
  }

  if (dim_ydir * view_y < 0.0)
    shift.y = -shift.y;

  shift.Rotate(tail_dir.y, tail_dir.x);

  textpt_xf = ON_Xform::TranslationTransformation( ON_3dVector(dimline_pt + shift) );
  if (-ON_SQRT_EPSILON > tail_dir.x)  // text to left
    textrot_xf.Rotation(-tail_dir.y, -tail_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin);
  else
    textrot_xf.Rotation(tail_dir.y, tail_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin);

  text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);

  // Text is horizontal to view
  if (ON::TextOrientation::InView != text_orientation)
    text_xform_out = textrot_xf * text_xform_out;
  text_xform_out = textpt_xf * text_xform_out;
  text_xform_out = dimplane_xf * text_xform_out;

  ON_3dPoint text_point_3d = Plane().PointAt(dimline_pt.x + shift.x, dimline_pt.y + shift.y);

  // Text is horizontal to view
  if (ON::TextOrientation::InView == text_orientation)
  {
    ON_Xform tp2sxf;        // Text point to view plane rotation
    tp2sxf.Rotation(text_point_3d, Plane().xaxis, Plane().yaxis, Plane().zaxis, text_point_3d, view_x, view_y, view_z);
    text_xform_out = tp2sxf * text_xform_out;
  }
  else
    if (draw_forward)
  {
    // Check if the text is right-reading
    ON_3dVector text_right_dir(1.0, 0.0, 0.0);
    text_right_dir.Transform(text_xform_out);
    if(nullptr != model_xform)
      text_right_dir.Transform(*model_xform);
    if (text_right_dir.Unitize())
    {
      ON_3dVector zdir = ON_CrossProduct(dim_xdir, dim_ydir);
      ON_3dVector text_up_dir = ON_CrossProduct(zdir, text_right_dir);
      bool fx = (0.0 > view_x * text_right_dir);
      bool fy = (0.0 > view_y * text_up_dir);
      if (fx || fy)
      {
        ON_Xform mxf;  // Mirror xform for backwards text
        if (fx)
        {
          mxf.Mirror(text_center, textplane.xaxis);
          text_xform_out = text_xform_out * mxf;
        }
        if (fy)
        {
          mxf.Mirror(ON_3dPoint::Origin, textplane.yaxis);
          text_xform_out = text_xform_out * mxf;
        }
      }
    }
  }
  return true;
}

double ON_DimRadial::Measurement() const
{
  double l = 0.0;
  if (m_radius_pt.IsValid())
  {
    l = ((ON_2dVector)m_radius_pt).Length();
    if ( ON::AnnotationType::Diameter == Type() )
      l *= 2.0;
    if (DistanceScale() != 1.0)
      l *= DistanceScale();
  }
  return l;
}

ON_2dPoint ON_DimRadial::DefaultTextPoint() const
{
  return m_dimline_pt;
}

ON_2dPoint ON_DimRadial::CenterPoint() const
{
  return ON_2dPoint::Origin;
}

ON_2dPoint ON_DimRadial::RadiusPoint() const
{
  return m_radius_pt;
}

ON_2dPoint ON_DimRadial::DimlinePoint() const
{
  return m_dimline_pt;
}

ON_2dPoint ON_DimRadial::KneePoint() const
{
  ON_2dPoint kpt = ON_2dPoint::UnsetPoint;
  if (ON_2dPoint::UnsetPoint != m_radius_pt && ON_2dPoint::UnsetPoint != m_dimline_pt)
  {
    kpt.Set(m_radius_pt.x, m_dimline_pt.y);

    if (ON_SQRT_EPSILON > fabs(m_radius_pt.x) || 0.01 < fabs(m_radius_pt.y / m_radius_pt.x))      // radial isn't horizontal
    {
      double x = m_radius_pt.x;
      if (m_radius_pt.y != 0.0)
        x = m_radius_pt.x * m_dimline_pt.y / m_radius_pt.y;
      kpt.Set(x, m_dimline_pt.y);
    }
  }
  return kpt;
}

void ON_DimRadial::Set2dCenterPoint(ON_2dPoint pt)
{
  // Move plane origin to pt
  if (pt.IsValid())
  {
    ON_2dVector v(-pt.x, -pt.y);
    m_plane.origin = m_plane.PointAt(pt.x, pt.y);
    m_radius_pt = m_radius_pt + v;
    m_dimline_pt = m_dimline_pt + v;
  }
}

void ON_DimRadial::Set2dRadiusPoint(ON_2dPoint pt)
{
  if (pt.IsValid())
  {
    m_radius_pt = pt;
  }
}

void ON_DimRadial::Set2dDimlinePoint(ON_2dPoint pt)
{
  if (pt.IsValid())
  {
    m_dimline_pt = pt;
  }
}

void ON_DimRadial::Set3dCenterPoint(ON_3dPoint pt)
{
  // This moves the whole dimension
  if (pt.IsValid())
  {
    m_plane.origin = pt;
  }
}

void ON_DimRadial::Set3dRadiusPoint(ON_3dPoint pt)
{
  if (pt.IsValid())
  {
    ON_2dPoint p;
    if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
      Set2dRadiusPoint(p);
  }
}

void ON_DimRadial::Set3dDimlinePoint(ON_3dPoint pt)
{
  if (pt.IsValid())
  {
    ON_2dPoint p;
    if (m_plane.ClosestPointTo(pt, &p.x, &p.y))
      Set2dDimlinePoint(p);
  }
}

bool ON_DimRadial::Get3dPoints(
  ON_3dPoint* center_pt,
  ON_3dPoint* radius_pt,
  ON_3dPoint* dimline_pt,
  ON_3dPoint* knee_pt) const
{
  bool rc = true;
  if (nullptr != center_pt)
    *center_pt = m_plane.origin;

  if (nullptr != radius_pt)
  {
    if (ON_2dPoint::UnsetPoint != m_radius_pt)
      *radius_pt = m_plane.PointAt(m_radius_pt.x, m_radius_pt.y);
    else
    {
      *radius_pt = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != dimline_pt)
  {
    if (ON_2dPoint::UnsetPoint != m_dimline_pt)
      *dimline_pt = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y);
    else
    {
      *dimline_pt = ON_3dPoint::UnsetPoint;
      rc = false;
    }
  }

  if (nullptr != knee_pt)
  {
    *knee_pt = ON_3dPoint::UnsetPoint;
    ON_2dPoint kpt = KneePoint();
    if (ON_2dPoint::UnsetPoint != kpt)
      *knee_pt = m_plane.PointAt(kpt.x, kpt.y);
    else
      rc = false;
  }
  return rc;
}

bool ON_DimRadial::GetDisplayLines(
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_3dPoint text_rect[4],
  ON_Line lines[9],
  bool isline[9],
  int maxlines) const
{
  if (maxlines != 9)
    return false;
  if (nullptr == dimstyle)
    return false;

  isline[0] = isline[1] = isline[2] = isline[3] = 
    isline[4] = isline[5] = isline[6] = isline[7] = isline[8] = false;

  ON_2dPoint center = CenterPoint();
  const ON_Plane& plane = Plane();
  ON_3dVector landing_dir(plane.xaxis);

  ON_DimStyle::ContentAngleStyle alignment = dimstyle->DimRadialTextAngleStyle();

  ON_2dPoint centerpt2d = CenterPoint();
  ON_2dPoint radiuspt2d = RadiusPoint();
  ON_2dPoint kneept2d = KneePoint();
  ON_2dPoint dimlinept2d = DimlinePoint();
  ON_2dVector taildir2d(1.0, 0.0);


  if (ON_DimStyle::ContentAngleStyle::Horizontal == alignment &&
    ON_2dPoint::UnsetPoint != kneept2d)
  {
    if (fabs(dimlinept2d.x - kneept2d.x) < ON_SQRT_EPSILON)
    {
      // kink at dimlinept
      if (dimlinept2d.x - radiuspt2d.x > -ON_SQRT_EPSILON)
        taildir2d.Set(1.0, 0.0);
      else
        taildir2d.Set(-1.0, 0.0);
    }
    else
    {
      if (dimlinept2d.x - kneept2d.x > -ON_SQRT_EPSILON)
        taildir2d.Set(1.0, 0.0);
      else
        taildir2d.Set(-1.0, 0.0);
    }

    lines[0].from = plane.PointAt(radiuspt2d.x, radiuspt2d.y);
    lines[0].to = plane.PointAt(kneept2d.x, kneept2d.y);
    isline[0] = lines[0].Length() > ON_SQRT_EPSILON;
    lines[1].from = plane.PointAt(kneept2d.x, kneept2d.y);
    lines[1].to = plane.PointAt(dimlinept2d.x, dimlinept2d.y);
    isline[1] = lines[1].Length() > ON_SQRT_EPSILON;
  }
  else if (ON_DimStyle::ContentAngleStyle::Aligned == alignment) // && no kink point
  {
    double d = ((ON_2dVector)dimlinept2d).Length();
    ON_2dVector rv = radiuspt2d;
    if (((ON_2dVector)dimlinept2d) * ((ON_2dVector)radiuspt2d) < 0.0)
      d = -d;
    if(rv.Unitize())
      dimlinept2d = rv * d;  // With no kink, adjust dimline point to line up with radius point
    taildir2d = dimlinept2d - radiuspt2d;
    if (ON_SQRT_EPSILON > taildir2d.Length() || !taildir2d.Unitize())
      taildir2d = radiuspt2d - centerpt2d;
    lines[0].from = plane.PointAt(radiuspt2d.x, radiuspt2d.y);
    lines[0].to = plane.PointAt(dimlinept2d.x, dimlinept2d.y);
    isline[0] = lines[0].Length() > ON_SQRT_EPSILON;
  }

  ON_3dPoint p = plane.PointAt(taildir2d.x, taildir2d.y);
  landing_dir = p - plane.origin;

  if (landing_dir.Unitize())
  {
    double landinglength = dimstyle->LeaderLandingLength() * dimscale;
    ON_3dPoint kink_point = plane.PointAt(kneept2d.x, kneept2d.y);
    ON_3dPoint dimline_point = plane.PointAt(dimlinept2d.x, dimlinept2d.y);

    const ON_DimStyle::TextLocation text_location = dimstyle->DimRadialTextLocation();

    if (
      (ON_DimStyle::ContentAngleStyle::Horizontal == alignment && ON_2dPoint::UnsetPoint != kneept2d) 
      || ON_DimStyle::TextLocation::AboveDimLine == text_location
      )
    {
      if (!dimstyle->LeaderHasLanding() && fabs(dimline_point.x - kink_point.x) < ON_ZERO_TOLERANCE)
        landinglength = dimstyle->TextHeight() * dimscale;
    }

    if(ON_DimStyle::TextLocation::AboveDimLine == text_location && ON::TextOrientation::InView != dimstyle->DimRadialTextOrientation())
      landinglength += text_rect[1].DistanceTo(text_rect[0]);

    if (0.0 < landinglength)
    {
      lines[2].from = plane.PointAt(dimlinept2d.x, dimlinept2d.y);
      lines[2].to = lines[2].from + (landing_dir * landinglength);
      isline[2] = true;
    }
  }

  double centermarksize = dimstyle->CenterMark() * dimscale;
  ON_DimStyle::centermark_style centermarkstyle = dimstyle->CenterMarkStyle();
  if (ON_DimStyle::centermark_style::None != centermarkstyle && ON_SQRT_EPSILON < centermarksize)
  {
    double radius = centerpt2d.DistanceTo(radiuspt2d);
    return ON_Dimension::GetCentermarkDisplay(plane, center, centermarksize, radius, centermarkstyle, &lines[3], &isline[3], 6);
  }
  else
    return true;
}

void ON_DimRadial::GetArrowXform(
  double scale,
  ON_Xform& arrow_xform_out) const
{
  ON_Xform arrow_xf, xfs, xfr;
  ON_2dPoint ap = RadiusPoint();
  arrow_xf.Rotation(ON_xy_plane, Plane());
  ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0);
  arrow_xf = arrow_xf * xft;
  double rotang = ON_PI;

  double ra = 0.0;
  ON_2dPoint rp = RadiusPoint();
  ON_2dPoint kp = KneePoint();
  ON_2dVector kv = kp - rp;
  if (((ON_2dVector)rp).Unitize())
  {
    ra = atan2(rp.y, rp.x);
    if (kv.Unitize())
    {
      if (0.0 > kv * ((ON_2dVector)rp))
        ra += ON_PI;
    }
    rotang += ra;
  }
  double pi2 = ON_PI * 2.0;
  while (pi2 <= rotang)
    rotang -= pi2;
  while (0 > rotang)
    rotang += pi2;

  if (ON_ZERO_TOLERANCE > fabs(rotang))
    rotang = 0.0;
  if (0.0 != rotang)
  {
    xfr.Rotation(rotang, ON_3dVector::ZAxis, ON_3dPoint::Origin);
    arrow_xf = arrow_xf * xfr;
  }
  xfs = ON_Xform::DiagonalTransformation(scale, scale, scale);
  arrow_xform_out = arrow_xf * xfs;
}


//----------------------------------------------------------
// Class ON_DimOrdinate

ON_DimOrdinate::ON_DimOrdinate()
  : ON_Dimension(ON::AnnotationType::Ordinate)
{}

ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::MeasuredDirectionFromUnsigned(
  unsigned int measured_direction_as_unsigned
)
{
  switch (measured_direction_as_unsigned)
  {
  ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Unset);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Xaxis);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_DimOrdinate::MeasuredDirection::Yaxis);
  }

  ON_ERROR("Invalid measured_direction_as_unsigned value.");

  return ON_DimOrdinate::Empty.m_direction;
}

bool ON_DimOrdinate::Write(
  ON_BinaryArchive& archive
  ) const
{
  const int content_version = 0;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Dimension::Internal_WriteDimension(archive))
      break;
    const unsigned int u = static_cast<unsigned char>(m_direction);
    if (!archive.WriteInt(u))
      break;
    if (!archive.WritePoint(m_def_pt))
      break;
    if (!archive.WritePoint(m_ldr_pt))
      break;
    if (!archive.WriteDouble(m_kink_offset_1))
      break;
    if (!archive.WriteDouble(m_kink_offset_2))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimOrdinate::Read(
  ON_BinaryArchive& archive
  )
{
  *this = ON_DimOrdinate::Empty;

  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Dimension::Internal_ReadDimension(archive))
      break;

    unsigned int u = static_cast<unsigned char>(m_direction);
    if (!archive.ReadInt(&u))
      break;
    m_direction = ON_DimOrdinate::MeasuredDirectionFromUnsigned(u);

    if (!archive.ReadPoint(m_def_pt))
      break;
    if (!archive.ReadPoint(m_ldr_pt))
      break;
    if (!archive.ReadDouble(&m_kink_offset_1))
      break;
    if (!archive.ReadDouble(&m_kink_offset_2))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_DimOrdinate::Transform(const ON_Xform& xform)
{
  bool rc = xform.IsIdentity();
  double xscale = 1.0, yscale = 1.0;
  if (!rc)
  {
    rc = true;
    bool scaling = false;
    ON_3dVector v = m_plane.xaxis;
    v.Transform(xform);
    if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
    {
      scaling = true;
      xscale = v.Length();
    }
    else
    {
      v = m_plane.yaxis;
      v.Transform(xform);
      if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
      {
        scaling = true;
        yscale = v.Length();
      }
      else
      {
        v = m_plane.zaxis;
        v.Transform(xform);
        if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON)
          scaling = true;
      }
    }

    if (scaling)
    {
      ON_3dPoint base_pt(ON_3dPoint::UnsetPoint);
      ON_3dPoint def_pt(ON_3dPoint::UnsetPoint);
      ON_3dPoint ldr_pt(ON_3dPoint::UnsetPoint);
      ON_3dPoint kink_pt1(ON_3dPoint::UnsetPoint);
      ON_3dPoint kink_pt2(ON_3dPoint::UnsetPoint);
      Get3dPoints(&base_pt, &def_pt, &ldr_pt, &kink_pt1, &kink_pt2);
      rc = m_plane.Transform(xform);
      def_pt.Transform(xform);
      ldr_pt.Transform(xform);
      ON_2dPoint def_pt_2d(ON_2dPoint::NanPoint), ldr_pt_2d(ON_2dPoint::NanPoint);
      if (rc && !m_plane.ClosestPointTo(def_pt, &def_pt_2d.x, &def_pt_2d.y))
        rc = false;
      else if (rc && !m_plane.ClosestPointTo(ldr_pt, &ldr_pt_2d.x, &ldr_pt_2d.y))
        rc = false;
      if (rc)
      {
        if (MeasuredDirection::Xaxis == GetMeasuredDirection())
        {
          if (ON_SQRT_EPSILON > fabs(def_pt_2d.x - ldr_pt_2d.x))
            ldr_pt_2d.x = def_pt_2d.x;
          else if (1.0 != yscale)
          {
            if (ON_UNSET_VALUE != m_kink_offset_1)
              m_kink_offset_1 *= yscale;
            if (ON_UNSET_VALUE != m_kink_offset_2)
              m_kink_offset_2 *= yscale;
          }
        }
        else if (MeasuredDirection::Yaxis == GetMeasuredDirection())
        {
          if (ON_SQRT_EPSILON > fabs(def_pt_2d.y - ldr_pt_2d.y))
            ldr_pt_2d.y = def_pt_2d.y;
          else if (1.0 != xscale)
          {
            if (ON_UNSET_VALUE != m_kink_offset_1)
              m_kink_offset_1 *= xscale;
            if (ON_UNSET_VALUE != m_kink_offset_2)
              m_kink_offset_2 *= xscale;
          }
        }
        Set2dDefPt(def_pt_2d);
        Set2dLeaderPt(ldr_pt_2d);
      }
    }
    else
      rc = m_plane.Transform(xform);

    if (rc)
      ON_Geometry::Transform(xform);
  }
  return rc;
}

bool ON_DimOrdinate::GetBBox(double* boxmin, double* boxmax, bool grow) const  // overrides ON_Geometry::GetBBox()
{
  const ON_DimStyle* dimstyle = nullptr;
  return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, boxmin, boxmax, grow ? true : false);
}

bool ON_DimOrdinate::GetAnnotationBoundingBox(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  double* boxmin,
  double* boxmax,
  bool bGrow
) const
{
  if (nullptr == dimstyle)
    dimstyle = &ON_DimStyle::Default;

  ON_2dPoint hash_points[] = {
    ON_2dPoint(m_def_pt),
    ON_2dPoint(m_ldr_pt), 
    ON_2dPoint(m_kink_offset_1,m_kink_offset_2),
    ON_2dPoint((double)static_cast<char>(m_direction),0.0)
  };

  const ON_SHA1_Hash hash = Internal_GetBBox_InputHash(
    vp, 
    dimstyle, 
    dimscale, 
    m_user_text_point,
    (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])),
    hash_points
  );

  if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow))
    return true;

  if (nullptr == boxmin || nullptr == boxmax)
    return false;

  ON_BoundingBox dbox;

  const ON_TextContent* text = Text();
  if (nullptr != text)
  {
    dbox = text->TextContentBoundingBox();
    ON_3dVector view_xdir = ON_3dVector::XAxis;
    ON_3dVector view_ydir = ON_3dVector::YAxis;
    if (nullptr != vp)
    {
      view_xdir = vp->CameraX();
      view_ydir = vp->CameraY();
    }
    ON_Xform textxform;
    GetTextXform(nullptr, vp, dimstyle, dimscale, textxform);
    dbox.Transform(textxform);
  }

  double points[12];
  const int defpt = 0, ldrpt = 3, kink1 = 6, kink2 = 9;
  Get3dPoints(nullptr, (ON_3dPoint*)(&points[defpt]), (ON_3dPoint*)(&points[ldrpt]), (ON_3dPoint*)(&points[kink1]), (ON_3dPoint*)(&points[kink2]));
  dbox.Set(3, 0, 4, 3, points, true);

  return Internal_GetBBox_End(dbox, hash, boxmin, boxmax, bGrow);
}

// Gets transform for dimension text from ON_xy_plane to 3d display location
bool ON_DimOrdinate::GetTextXform(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{
  return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out);
}

bool ON_DimOrdinate::GetTextXform(
  const ON_Xform* model_xform,
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Xform& text_xform_out
) const
{

  // This gets the display text that's already on the dimension
  // If its not updated correctly for the view, the wrong size will be used
  const ON_TextContent* text = Text();
  if (nullptr == text)
    return false;
  if (nullptr == dimstyle)
    return false;

  // See if the text needs remade because of some change in some property that
  // would change its appearance
  if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash())
  {
    ON_wString rtfstr = text->RtfText();
    ON::AnnotationType annotation_type = this->Type();
    bool wrapped = text->TextIsWrapped();
    double width = text->FormattingRectangleWidth();
    double rot = text->TextRotationRadians();
    const_cast<ON_TextContent*>(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot);
  }

  double text_width = 0.0;
  double text_height = 0.0;
  double text_gap = 0.0;
  double text_angle = 0.0;
  const ON_Plane& textplane = ON_xy_plane;
  bool draw_forward = dimstyle->DrawForward();

  const ON_Plane& dimplane = Plane();
  ON_3dPoint text_center = ON_3dPoint::Origin;

  ON_3dPoint cp[4];
  if (!text->Get3dCorners(cp))
    return false;

  text_center = (cp[0] + cp[2]) / 2.0;
  text_width = (cp[1].x - cp[0].x) * dimscale;
  text_height = (cp[3].y - cp[0].y) * dimscale;
  text_gap = dimstyle->TextGap() * dimscale;

  text_xform_out = ON_Xform::IdentityTransformation;
  ON_Xform dimplane_xf;
  dimplane_xf.Rotation(textplane, dimplane);     // Rotate text from world xy to dimension plane
  ON_Xform textpt_xf(1.0);           // Dimension plane to text point translation 
  ON_Xform textrot_xf(1.0);          // Text rotation around text plane origin point
  ON_Xform textscale_xf(dimscale);
  ON_Xform textshift_xf(1.0);

  ON_2dPoint defpt = DefPt();
  ON_2dPoint ldrpt = LeaderPt();

  MeasuredDirection direction = GetMeasuredDirection();
  if (MeasuredDirection::Unset == direction)
    direction = MeasuredDirection::Xaxis;

  ON_2dVector shift(text_width / 2.0 + text_gap, 0.0);
  ON_3dVector tail_dir;

  if (MeasuredDirection::Xaxis == direction) // Tail direction is vertical
  {
    tail_dir.Set(0.0, 1.0, 0.0);
    if (ldrpt.y < defpt.y)   // tail direction is down
    {
      tail_dir.y = -1.0;
      shift.x = -shift.x;
    }
    text_angle = ON_PI / 2.0; // rotate 90 for vertical tail direction
  }
  else  // Measures y axis and tail direction is horizontal
  {
    tail_dir.Set(1.0, 0.0, 0.0);
    if (ldrpt.x < defpt.x)  // tail direction is left
    {
      tail_dir.x = -1.0;
      shift.x = -shift.x;
    }
  }

  ON_Xform xfWorld2Cam;
  ON_3dVector W2CX = dimplane.xaxis;
  ON_3dVector W2CY = dimplane.yaxis;
  ON_3dVector W2CZ = dimplane.zaxis;
  if (nullptr != vp)
  {
    vp->GetXform(ON::coordinate_system::world_cs, ON::coordinate_system::camera_cs, xfWorld2Cam);
    W2CX.Transform(xfWorld2Cam);
    W2CY.Transform(xfWorld2Cam);
    W2CZ.Transform(xfWorld2Cam);
    if (nullptr != model_xform)
    {
      W2CX.Transform(*model_xform);
      W2CY.Transform(*model_xform);
      W2CZ.Transform(*model_xform);
    }
  }
  bool xright = (W2CX * ON_3dVector::XAxis) > -ON_SQRT_EPSILON;
  bool yup = (W2CY * ON_3dVector::YAxis) > -ON_SQRT_EPSILON;
  
  textrot_xf.Rotation(text_angle, ON_3dVector::ZAxis, ON_3dPoint::Origin);

  const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation();
  const ON::TextOrientation text_orientation = dimstyle->DimTextOrientation();
  if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
  {
    shift.y = text_height / 2.0 + text_gap;
    if (!xright && direction == MeasuredDirection::Xaxis)
      shift.y = -shift.y;
    if (!yup && direction == MeasuredDirection::Yaxis)
      shift.y = -shift.y;
  }
  textshift_xf = ON_Xform::TranslationTransformation(shift.x, shift.y, 0.0); // small shift around text point
  textpt_xf = ON_Xform::TranslationTransformation(ldrpt.x, ldrpt.y, 0.0);    // base point to text point

  text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);      // dimscale
  text_xform_out = textshift_xf * text_xform_out;          // dimension plane to text point
  text_xform_out = textrot_xf * text_xform_out;            // text rotation
  text_xform_out = textpt_xf * text_xform_out;             // dimension plane to text point
  text_xform_out = dimplane_xf * text_xform_out;           // text plane to dim plane

  ON_3dVector view_xdir = ON_3dVector::XAxis;
  ON_3dVector view_ydir = ON_3dVector::YAxis;
  ON_3dVector view_zdir = ON_3dVector::ZAxis;
  if (nullptr != vp)
  {
    view_xdir = vp->CameraX();
    view_ydir = vp->CameraY();
    view_zdir = vp->CameraZ();
  }

  if (ON::TextOrientation::InView == text_orientation)  // Draw dimension horizontal to view
  {

    ON_Xform tp2sxf;        // Text point to view plane rotation
    ON_3dPoint text_point_3d = dimplane.PointAt(ldrpt.x, ldrpt.y);
    tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir);
    text_xform_out = tp2sxf * text_xform_out;
  }
  else if (draw_forward)
  {
    ON_3dVector text_right_dir(1.0, 0.0, 0.0);
    text_right_dir.Transform(text_xform_out);
    if (nullptr != model_xform)
      text_right_dir.Transform(*model_xform);
    ON_3dVector text_up_dir(0.0, 1.0, 0.0);
    text_up_dir.Transform(text_xform_out);
    if (nullptr != model_xform)
      text_up_dir.Transform(*model_xform);
    if (text_right_dir.Unitize() && text_up_dir.Unitize())
    {
      bool fx = false;
      bool fy = false;
      if (direction == MeasuredDirection::Xaxis)
      {
        fx = (view_ydir * text_right_dir) < 0.0;
        fy = (view_xdir * text_up_dir) > 0.0;
      }
      else
      {
        fx = (view_xdir * text_right_dir) < 0.0;
        fy = (view_ydir * text_up_dir) < 0.0;
      }

      ON_Xform mxf;  // Mirror xform for backwards text to adjust DrawForward
      if (fx)
      {
        mxf.Mirror(text_center, ON_3dVector::XAxis);
        text_xform_out = text_xform_out * mxf;
      }
      if (fy)
      {
        mxf.Mirror(text_center, ON_3dVector::YAxis);
        text_xform_out = text_xform_out * mxf;
      }
    }
  }
  return true;
}

bool ON_DimOrdinate::Create(
  const ON_UUID style_id,
  const ON_Plane& plane,
  MeasuredDirection direction,
  const ON_3dPoint& basept,
  const ON_3dPoint& defpt,
  const ON_3dPoint& ldrpt,
  double kinkoffset1,
  double kinkoffset2)
{
  bool rc = true;
  m_dimstyle_id = style_id;
  if (ON_nil_uuid == m_dimstyle_id)
    rc = true;
  if (!plane.IsValid() || !basept.IsValid() || !defpt.IsValid() || !ldrpt.IsValid())
    return false;

  m_plane = plane;

  ON_2dPoint def_pton(ON_2dPoint::NanPoint);
  ON_2dPoint ldr_pton(ON_2dPoint::NanPoint);

  m_plane.origin = plane.ClosestPointTo(basept);
  rc = m_plane.ClosestPointTo(defpt, &def_pton.x, &def_pton.y);
  if (rc)
  {
    rc = m_plane.ClosestPointTo(ldrpt, &ldr_pton.x, &ldr_pton.y);
    if (rc)
    {
      Set2dDefPt(def_pton);
      Set2dLeaderPt(ldr_pton);
      SetKinkOffset1(kinkoffset1);
      SetKinkOffset2(kinkoffset2);
    }
  }
  return rc;
}

bool ON_DimOrdinate::AdjustFromPoints(
  const ON_Plane& base_plane,
  MeasuredDirection direction,
  const ON_3dPoint& basept,
  const ON_3dPoint& defpt,
  const ON_3dPoint& ldrpt,
  double kinkoffset1,
  double kinkoffset2)
{
  ON_2dPoint base_pt(ON_3dPoint::Origin), def_pt(ON_3dPoint::Origin), ldr_pt(ON_3dPoint::Origin);
  ON_Plane plane = base_plane;
  plane.origin = basept;

  if (!plane.ClosestPointTo(defpt, &def_pt.x, &def_pt.y))
    return false;
  if (!plane.ClosestPointTo(ldrpt, &ldr_pt.x, &ldr_pt.y))
    return false;

  if (MeasuredDirection::Xaxis == GetMeasuredDirection())
  {
    if (ON_SQRT_EPSILON > fabs(def_pt.x - ldr_pt.x))
      ldr_pt.x = def_pt.x;
  }
  else if (MeasuredDirection::Yaxis == GetMeasuredDirection())
  {
    if (ON_SQRT_EPSILON > fabs(def_pt.y - ldr_pt.y))
      ldr_pt.y = def_pt.y;
  }
  SetPlane(plane);
  Set2dDefPt(def_pt);
  Set2dLeaderPt(ldr_pt);
  SetMeasuredDirection(direction);
  SetKinkOffset1(kinkoffset1);
  SetKinkOffset2(kinkoffset2);

  return true;
}

ON_2dPoint ON_DimOrdinate::DefPt() const
{
  return m_def_pt;
}

ON_2dPoint ON_DimOrdinate::LeaderPt() const
{
  return m_ldr_pt;
}

ON_2dPoint ON_DimOrdinate::KinkPt1() const
{
  ON_2dPoint kink1(m_def_pt);
  ON_2dPoint kink2(m_ldr_pt);
  CalcKinkPoints(m_def_pt, m_ldr_pt, MeasuredDirection(), 1.0, kink1, kink2);
  return kink1;
}

ON_2dPoint ON_DimOrdinate::KinkPt2() const
{
  ON_2dPoint kink1(m_def_pt);
  ON_2dPoint kink2(m_ldr_pt);
  CalcKinkPoints(m_def_pt, m_ldr_pt, MeasuredDirection(), 1.0, kink1, kink2);
  return kink2;
}
double ON_DimOrdinate::KinkOffset1() const
{
  return m_kink_offset_1;
}

double ON_DimOrdinate::KinkOffset2() const
{
  return m_kink_offset_2;
}

void ON_DimOrdinate::Set2dDefPt(ON_2dPoint pt)
{
  if (pt.IsValid())
    m_def_pt = pt;
}

void ON_DimOrdinate::Set2dLeaderPt(ON_2dPoint pt)
{
  if (pt.IsValid())
    m_ldr_pt = pt;
}

void ON_DimOrdinate::SetKinkOffset1(double d)
{
  if (ON_IsValid(d))
    m_kink_offset_1 = d;
}

void ON_DimOrdinate::SetKinkOffset2(double d)
{
  if (ON_IsValid(d))
    m_kink_offset_2 = d;
}

void ON_DimOrdinate::Set3dBasePoint(ON_3dPoint pt)
{
  ON_2dVector p2;
  if (m_plane.ClosestPointTo(pt, &p2.x, &p2.y))
  {
    m_def_pt = m_def_pt - p2;
    m_ldr_pt = m_ldr_pt - p2;
    m_plane.origin = pt;
  }
}

void ON_DimOrdinate::Set3dDefPt(ON_3dPoint pt)
{
  double x, y;
  if (m_plane.ClosestPointTo(pt, &x, &y))
    m_def_pt.Set(x, y);
}

void ON_DimOrdinate::Set3dLeaderPt(ON_3dPoint pt)
{
  double x, y;
  if (m_plane.ClosestPointTo(pt, &x, &y))
    m_ldr_pt.Set(x, y);
}

ON_3dPoint ON_DimOrdinate::Get3dBasePoint() const
{
  return m_plane.origin;
}

ON_3dPoint ON_DimOrdinate::Get3dDefPt() const
{
  ON_3dPoint p = m_plane.PointAt(m_def_pt.x, m_def_pt.y);
  return p;
}

ON_3dPoint ON_DimOrdinate::Get3dLeaderPt() const
{
  ON_3dPoint p = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y);
  return p;
}

ON_3dPoint ON_DimOrdinate::Get3dKinkPt1(double default_kink_offset) const
{
  ON_3dPoint p(ON_3dPoint::Origin);
  double ko1 = m_kink_offset_1;
  double ko2 = m_kink_offset_2;
  if (!ON_IsValid(ko1))
    ko1 = default_kink_offset;
  if (!ON_IsValid(ko2))
    ko2 = default_kink_offset;
  MeasuredDirection direction = MeasuredDirection();
  if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y)
      ||
      (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x))
  {
    ko1 = -ko1;
    ko2 = -ko2;
  }
  if (MeasuredDirection::Xaxis == direction)
    p = m_plane.PointAt(m_def_pt.x, m_ldr_pt.y - ko1 - ko2);
  else if (MeasuredDirection::Yaxis == direction)
    p = m_plane.PointAt(m_ldr_pt.x - ko1 - ko2, m_def_pt.y);
  return p;
}

ON_3dPoint ON_DimOrdinate::Get3dKinkPt2(double default_kink_offset) const
{
  ON_3dPoint p(ON_3dPoint::Origin);
  double ko1 = m_kink_offset_1;
  if (!ON_IsValid(ko1))
    ko1 = default_kink_offset;
  MeasuredDirection direction = GetMeasuredDirection();
  if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y)
      ||
      (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x))
  {
    ko1 = -ko1;
  }
  if (MeasuredDirection::Xaxis == direction)
    p = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y - ko1);
  else if (MeasuredDirection::Yaxis == direction)
    p = m_plane.PointAt(m_ldr_pt.x - ko1, m_ldr_pt.y);
  return p;
}

bool ON_DimOrdinate::Get3dPoints(
  ON_3dPoint* base,
  ON_3dPoint* def1,
  ON_3dPoint* def2,
  ON_3dPoint* kink1,
  ON_3dPoint* kink2,
  double default_kink_offset) const
{
  if (nullptr == base && nullptr == def1 && nullptr == def2 && nullptr == kink1 && nullptr == kink2)
    return false;
  if (nullptr != base)
    *base = m_plane.origin;
  if (nullptr != def1)
    *def1 = m_plane.PointAt(m_def_pt.x, m_def_pt.y);
  if (nullptr != def2)
    *def2 = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y);
  if (nullptr != kink1 || nullptr != kink2)
  {
    double ko1 = m_kink_offset_1;
    double ko2 = m_kink_offset_2;
    if (!ON_IsValid(ko1))
      ko1 = default_kink_offset;
    if (!ON_IsValid(ko2))
      ko2 = default_kink_offset;
    MeasuredDirection direction = GetMeasuredDirection();
    if ((MeasuredDirection::Xaxis == direction && m_ldr_pt.y < m_def_pt.y)
        ||
        (MeasuredDirection::Yaxis == direction && m_ldr_pt.x < m_def_pt.x))
    {
      ko1 = -ko1;
      ko2 = -ko2;
    }
    if (nullptr != kink1)
    {
      if (MeasuredDirection::Xaxis == direction)
        *kink1 = m_plane.PointAt(m_def_pt.x, m_ldr_pt.y - ko1 - ko2);
      else if (MeasuredDirection::Yaxis == direction)
        *kink1 = m_plane.PointAt(m_ldr_pt.x - ko1 - ko2, m_def_pt.y);
    }
    if (nullptr != kink2)
    {
      if (MeasuredDirection::Xaxis == direction)
        *kink2 = m_plane.PointAt(m_ldr_pt.x, m_ldr_pt.y - ko1);
      else if (MeasuredDirection::Yaxis == direction)
        *kink2 = m_plane.PointAt(m_ldr_pt.x - ko1, m_ldr_pt.y);
    }
  }
  return true;  
}

bool ON_DimOrdinate::GetDisplayLines(
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_3dPoint text_rect[4],
  ON_Line lines[3],
  bool isline[3],
  int maxlines) const
{
  if (3 != maxlines)
  {
    ON_ERROR("Wrong linecount calling ON_DimOrdinate::GetDisplayLines.\n");
    return false;
  }
  ON_3dPoint defpt, ldrpt, kink1, kink2, frompt;
  Get3dPoints(nullptr, &defpt, &ldrpt, &kink1, &kink2);
  frompt = defpt;
  double eodist = dimstyle->ExtOffset() * dimscale;
  ON_3dVector eodir = kink1 - defpt;
  if (eodir.Unitize())
  {
    frompt = defpt + (eodir * eodist);
  }

  lines[0].from = frompt;
  lines[0].to = kink1;
  if (ON_SQRT_EPSILON < lines[0].Length())
    isline[0] = true;
  else
    isline[0] = false;

  lines[1].from = kink1;
  lines[1].to = kink2;
  if (ON_SQRT_EPSILON < lines[1].Length())
    isline[1] = true;
  else
    isline[1] = false;

  lines[2].from = kink2;
  lines[2].to = ldrpt;

  //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_alignment_mode = dimstyle->TextAlignment();
  const ON_DimStyle::TextLocation text_location = dimstyle->DimTextLocation();

  //if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine == text_alignment_mode)
  if (ON_DimStyle::TextLocation::AboveDimLine == text_location )
  {
    ON_2dPoint defpt2d = DefPt();
    ON_2dPoint ldrpt2d = LeaderPt();

    if (ON_DimOrdinate::MeasuredDirection::Xaxis == GetMeasuredDirection())
    {
      double text_width = fabs(text_rect[1].y - text_rect[0].y);
      if (ldrpt2d.y > defpt2d.y)
        lines[2].to = Plane().PointAt(ldrpt2d.x, ldrpt2d.y + text_width);
      else
        lines[2].to = Plane().PointAt(ldrpt2d.x, ldrpt2d.y - text_width);
    }
    else
    {
      double text_width = fabs(text_rect[1].x - text_rect[0].x);
      if (ldrpt2d.x > defpt2d.x)
        lines[2].to = Plane().PointAt(ldrpt2d.x + text_width, ldrpt2d.y);
      else
        lines[2].to = Plane().PointAt(ldrpt2d.x - text_width, ldrpt2d.y);
    }
  }
  if (ON_SQRT_EPSILON < lines[2].Length())
    isline[2] = true;
  else
    isline[2] = false;
  return true;
}

bool ON_DimOrdinate::CalcKinkPoints(
  ON_2dPoint defpt,
  ON_2dPoint ldrpt,
  MeasuredDirection direction,
  double default_kink_offset,
  ON_2dPoint& kinkpt1_out,
  ON_2dPoint& kinkpt2_out) const
{
  bool rc = false;
  if (MeasuredDirection::Unset == direction)
    direction = ImpliedDirection(defpt, ldrpt);
  if (MeasuredDirection::Unset == direction)
    return false;

  double offset1 = KinkOffset1();
  double offset2 = KinkOffset2();

  // if these haven't been set by dragging the offset points
  // default distance - 2 * textheight
  if (offset1 == ON_UNSET_VALUE)
  {
    offset1 = default_kink_offset;
  }
  if (offset2 == ON_UNSET_VALUE)
  {
    offset2 = default_kink_offset;
  }
  ((ON_DimOrdinate*)this)->SetKinkOffset1(offset1);
  ((ON_DimOrdinate*)this)->SetKinkOffset2(offset2);
  if (direction == MeasuredDirection::Xaxis)
  {
    if (defpt.y > ldrpt.y)
    {
      offset1 = -offset1;
      offset2 = -offset2;
    }
    kinkpt1_out.x = ldrpt.x;
    kinkpt1_out.y = ldrpt.y - offset1;
    kinkpt2_out.x = defpt.x;
    kinkpt2_out.y = ldrpt.y - offset1 - offset2;
    rc = true;
  }
  else if (direction == MeasuredDirection::Yaxis)
  {
    if (defpt.x > ldrpt.x)
    {
      offset1 = -offset1;
      offset2 = -offset2;
    }
    kinkpt1_out.y = ldrpt.y;
    kinkpt1_out.x = ldrpt.x - offset1;
    kinkpt2_out.y = defpt.y;
    kinkpt2_out.x = ldrpt.x - offset1 - offset2;
    rc = true;
  }
  return rc;
}

ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::ImpliedDirection(
  ON_2dPoint defpt,
  ON_2dPoint ldrpt
  ) const
{
  MeasuredDirection direction = MeasuredDirection::Unset;
  if (fabs(ldrpt.x - defpt.x) <= fabs(ldrpt.y - defpt.y))
    direction = MeasuredDirection::Xaxis; // measures along x axis
  else
    direction = MeasuredDirection::Yaxis; // measures along y axis

  return direction;
}

ON_DimOrdinate::MeasuredDirection ON_DimOrdinate::GetMeasuredDirection() const
{
  if (MeasuredDirection::Unset == m_direction)
    return ImpliedDirection(m_def_pt, m_ldr_pt);
  else
    return m_direction;
}

void ON_DimOrdinate::SetMeasuredDirection(MeasuredDirection direction)
{
  m_direction = direction;
}

double ON_DimOrdinate::Measurement() const
{
  double m = 0.0;
  switch (GetMeasuredDirection())
  {
  case MeasuredDirection::Xaxis:
      m = m_def_pt.x;
    break;
  case MeasuredDirection::Yaxis:
      m = m_def_pt.y;
    break;
  }
  if (DistanceScale() != 1.0)
    m *= DistanceScale();

  return fabs(m);
}


//----------------------------------------------------------
// Class ON_Centermark

ON_Centermark::ON_Centermark()
  : ON_Dimension(ON::AnnotationType::CenterMark)
{}

double ON_Centermark::Measurement() const
{
  return 0.0;
}

bool ON_Centermark::Write(
  ON_BinaryArchive& archive
  ) const
{
  const int content_version = 0;
  if (false == archive.BeginWrite3dmAnonymousChunk(content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (!ON_Dimension::Internal_WriteDimension(archive))
      break;
    if (!archive.WriteDouble(m_radius))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_Centermark::Read(
  ON_BinaryArchive& archive
  )
{
  *this = ON_Centermark::Empty;

  int content_version = -1;
  if (false == archive.BeginRead3dmAnonymousChunk(&content_version))
    return false;

  bool rc = false;
  for (;;)
  {
    if (content_version < 0)
      break;
    if (!ON_Dimension::Internal_ReadDimension(archive))
      break;
    if (!archive.ReadDouble(&m_radius))
      break;
    rc = true;
    break;
  }

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

  return rc;
}

bool ON_Centermark::Transform(const ON_Xform& xform)
{
  bool rc = xform.IsIdentity();
  if (!rc)
  {
    rc = m_plane.Transform(xform);
    if (rc)
      ON_Geometry::Transform(xform);
  }
  return rc;
}

bool ON_Centermark::GetTextXform(
  const ON_Viewport*,
  const ON_DimStyle*,
  double,
  ON_Xform&
) const
{
  return true;
}

bool ON_Centermark::Create(
  const ON_UUID style_id,
  const ON_Plane& plane,
  const ON_3dPoint& center_pt,
  const double radius)
{
  bool rc = true;
  m_dimstyle_id = style_id;
  if (ON_nil_uuid == m_dimstyle_id)
    rc = true;
  if (!plane.IsValid() || !center_pt.IsValid() || !center_pt.IsValid())
    return false;
  m_plane = plane;
  if (rc)
  {
    m_plane.origin = plane.ClosestPointTo(center_pt);
    m_radius = radius;
  }
  return rc;
}

bool ON_Centermark::AdjustFromPoints(
  const ON_Plane& plane,
  const ON_3dPoint& center_pt
  )
{
  m_plane = plane;
  m_plane.origin = center_pt;
  return true;
}

bool ON_Centermark::GetBBox(double* bmin, double* bmax, bool grow) const
{
  const ON_DimStyle* dimstyle = nullptr;
  return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow ? true : false);
}

bool ON_Centermark::GetAnnotationBoundingBox(
  const ON_Viewport* vp,
  const ON_DimStyle* dimstyle,
  double dimscale,
  double* boxmin,
  double* boxmax,
  bool bGrow
) const
{
  if (nullptr == dimstyle)
    dimstyle = &ON_DimStyle::Default;

  const ON_2dPoint hash_points[] = {
    ON_2dPoint(m_radius,0.0)
  };

  const ON_SHA1_Hash hash = Internal_GetBBox_InputHash(
    vp, 
    dimstyle, 
    dimscale, 
    m_user_text_point,
    (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])),
    hash_points
  );

  if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow))
    return true;

  if (nullptr == boxmin || nullptr == boxmax)
    return false;

  ON_BoundingBox dbox;

#define dimlinecount 6
  ON_Line lines[dimlinecount];
  bool isline[dimlinecount] = { false };
  if (GetDisplayLines(dimstyle, dimscale, lines, isline, dimlinecount))
  {
    for (int i = 0; i < dimlinecount; i++)
    {
      if (isline[i])
      {
        dbox.Set(lines[i].from, true);
        dbox.Set(lines[i].to, true);
      }
    }
  }
#undef dimlinecount

  return Internal_GetBBox_End(dbox, hash, boxmin, boxmax, bGrow);
}

ON_2dPoint ON_Centermark::CenterPoint() const
{
  return ON_2dPoint::Origin;
}

void ON_Centermark::Set2dCenterPoint(ON_2dPoint pt)
{
  if (pt.IsValid())
    m_plane.origin = m_plane.PointAt(pt.x, pt.y);
}

void ON_Centermark::Set3dCenterPoint(ON_3dPoint pt)
{
  if (pt.IsValid())
    m_plane.origin = pt;
}

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

void ON_Centermark::SetRadius(double radius)
{
  if (!(radius > ON_UNSET_VALUE && radius < ON_UNSET_POSITIVE_VALUE))
  {
    ON_ERROR("Invalid radius parameter in ON_Centermark::SetRadius().");
    return;
  }
  m_radius = radius;
}

bool ON_Dimension::GetCentermarkSnapPoints(
  const ON_Plane& plane,
  const ON_2dPoint center,
  double marksize,
  double radius,
  ON_DimStyle::centermark_style style,
  ON_3dPoint points[13],
  bool ispoint[13])
{
  for (int i = 0; i < 13; i++)
    ispoint[i] = false;
  ON_Line lines[6];
  bool isline[6] = { false,false,false,false,false,false };
  const int dimlinecount = 6;
  if (GetCentermarkDisplay(plane, center, marksize, radius, style, lines, isline, dimlinecount))
  {
    points[0] = plane.origin;
    ispoint[0] = true;
    for (int j = 0; j < dimlinecount; j++)
    {
      if (isline[j])
      {
        for (int i = 0; i < 2; i++)
        {
          int t = 1 + (2 * j) + i;
          points[t] = i ? lines[j].from : lines[j].to;
          ispoint[t] = true;
        }
      }
    }
    return true;
  }
  return false;
}

bool ON_Dimension::GetCentermarkDisplay(
  const ON_Plane& plane,
  const ON_2dPoint center,
  double marksize,
  double radius,
  ON_DimStyle::centermark_style style,
  ON_Line lines[6],
  bool isline[6],
  int maxlines)
{
  if (ON_DimStyle::centermark_style::None != style)
  {
    if (maxlines < 2)
    {
      ON_ERROR("Wrong line count calling ON_Dimension::GetCentermarkDisplay()\n");
      return false;
    }
    lines[0].from = plane.PointAt(center.x - marksize, center.y);
    lines[0].to   = plane.PointAt(center.x + marksize, center.y);
    lines[1].from = plane.PointAt(center.x, center.y - marksize);
    lines[1].to   = plane.PointAt(center.x, center.y + marksize);
    isline[0] = isline[1] = true;

    if (ON_DimStyle::centermark_style::MarkAndLines == style)
    {
      if (maxlines != 6)
      {
        ON_ERROR("Wrong line count calling ON_Dimension::GetCentermarkDisplay()\n");
        return false;
      }

      lines[2].from = plane.PointAt(center.x + 2.0 * marksize, center.y);
      lines[2].to   = plane.PointAt(center.x + radius + marksize, center.y);
      lines[3].from = plane.PointAt(center.x, center.y + 2.0 * marksize);
      lines[3].to   = plane.PointAt(center.x, center.y + radius + marksize);
      lines[4].from = plane.PointAt(center.x - 2.0 * marksize, center.y);
      lines[4].to   = plane.PointAt(center.x - radius - marksize, center.y);
      lines[5].from = plane.PointAt(center.x, center.y - 2.0 * marksize);
      lines[5].to   = plane.PointAt(center.x, center.y - radius - marksize);
      isline[2] = isline[3] = isline[4] = isline[5] = true;
    }
  }
  return true;
}

bool ON_Centermark::GetDisplayLines(
  const ON_DimStyle* dimstyle,
  double dimscale,
  ON_Line lines[6],
  bool isline[6], 
  int maxlines) const
{
  if (maxlines != 6)
  {
    ON_ERROR("Wrong linecount calling ON_Centermark::GetDisplayLines.\n");
    return false;
  }
  if (nullptr == dimstyle)
    return false;
  isline[0] = isline[1] = isline[2] = isline[3] = isline[4] = isline[5] = false;

  if (ON_DimStyle::centermark_style::None == dimstyle->CenterMarkStyle())
    return true;

  ON_2dPoint center = CenterPoint();

  const ON_Plane& plane = Plane();
  double centermarksize = dimstyle->CenterMark() * dimscale;
  double radius = Radius();
  ON_DimStyle::centermark_style style = dimstyle->CenterMarkStyle();
  if (ON_SQRT_EPSILON < centermarksize)
    return ON_Dimension::GetCentermarkDisplay(plane, center, centermarksize, radius, style, lines, isline, 6);
  else
    return true;
}

