Files
opennurbs/opennurbs_dimension.cpp
2025-03-12 06:45:55 -07:00

5833 lines
155 KiB
C++

//
// Copyright (c) 1993-2022 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;
}
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 = displaytext + UserText();
if (dimstyle->Prefix().IsNotEmpty() || dimstyle->Suffix().IsNotEmpty())
{
int ci = displaytext.Find(L"<>");
if (ci > -1)
{
ON_wString right;
if (displaytext.Length() > ci + 2)
right = displaytext.Right(displaytext.Length() - ci - 2);
displaytext = displaytext.Left(ci);
displaytext = displaytext + dimstyle->Prefix();
displaytext = displaytext + L"<>";
displaytext = displaytext + dimstyle->Suffix();
displaytext = displaytext + right;
}
}
}
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
{
ON_ERROR("Use ON_Dimension::ArrowFit(const ON_DimStyle* parent_style)");
return ON_Dimension::ForceArrow::Auto;
}
void ON_Dimension::SetForceArrowPosition(ON_Dimension::ForceArrow force)
{
//
ON_ERROR("Use ON_Dimension::SetArrowFit(const ON_DimStyle* parent_style,ON_DimStyle::arrow_fit arrowfit)");
}
ON_Dimension::ForceText ON_Dimension::ForceTextPosition() const
{
ON_ERROR("Use ON_Dimension::TextFit(const ON_DimStyle* parent_style)");
return ON_Dimension::ForceText::Auto;
}
void ON_Dimension::SetForceTextPosition(ON_Dimension::ForceText force)
{
ON_ERROR("Use ON_Dimension::SetTextFit(const ON_DimStyle* parent_style,ON_DimStyle::text_fit textfit)");
}
//--------------------------------
void ON_Dimension::SetForceDimLine(
const ON_DimStyle* parent_style,
bool force_dimline
)
{
parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
bool bCreate = (force_dimline != parent_style->ForceDimLine());
ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
if (nullptr != override_style)
{
override_style->SetForceDimLine(force_dimline);
override_style->SetFieldOverride(ON_DimStyle::field::ForceDimLine, bCreate);
}
}
bool ON_Dimension::ForceDimLine(
const ON_DimStyle* parent_style) const
{
return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ForceDimLine).ForceDimLine();
}
void ON_Dimension::SetTextFit(
const ON_DimStyle* parent_style,
ON_DimStyle::text_fit textfit)
{
parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
bool bCreate = (textfit != parent_style->TextFit());
ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
if (nullptr != override_style)
{
override_style->SetTextFit(textfit);
override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate);
}
}
ON_DimStyle::text_fit ON_Dimension::TextFit(
const ON_DimStyle* parent_style) const
{
return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::TextFit).TextFit();
}
void ON_Dimension::SetArrowFit(
const ON_DimStyle* parent_style,
ON_DimStyle::arrow_fit arrowfit)
{
parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
bool bCreate = (arrowfit != parent_style->ArrowFit());
ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
if (nullptr != override_style)
{
override_style->SetArrowFit(arrowfit);
override_style->SetFieldOverride(ON_DimStyle::field::TextFit, bCreate);
}
}
ON_DimStyle::arrow_fit ON_Dimension::ArrowFit(
const ON_DimStyle* parent_style) const
{
return Internal_StyleForFieldQuery(parent_style, ON_DimStyle::field::ArrowFit).ArrowFit();
}
//----------------------------------------------------------
// 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;
const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle();
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;
const unsigned int legacy_arrow_fit = static_cast<unsigned int>(ArrowFit(&ds));
if (!archive.WriteInt(legacy_arrow_fit))
break;
if (!archive.WriteUuid(m_detail_measured))
break;
if (!archive.WriteDouble(m_distance_scale))
break;
// content_version 1
const unsigned int legacy_text_fit = static_cast<unsigned int>(TextFit(&ds));
if (!archive.WriteInt(legacy_text_fit))
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;
unsigned int legacy_arrow_fit = 0;
unsigned int legacy_text_fit = 0;
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;
if (!archive.ReadInt(&legacy_arrow_fit))
break;
if (!archive.ReadUuid(m_detail_measured))
break;
if (!archive.ReadDouble(&m_distance_scale))
break;
// 24-Sep-2021 Dale Fugier, https://mcneel.myjetbrains.com/youtrack/issue/RH-65605
//if (ON_nil_uuid == m_detail_measured)
// m_distance_scale = 1.0;
if (content_version <= 0)
{
rc = true;
break;
}
// content_version 1
if (!archive.ReadInt(&legacy_text_fit))
break;
rc = true;
break;
}
if (!archive.EndRead3dmChunk())
rc = false;
const unsigned int version_v7_may_8_2019 = ON_VersionNumberConstruct(7, 0, 2019, 5, 8, 0);
if (rc && archive.ArchiveOpenNURBSVersion() < version_v7_may_8_2019 )
{
// may 2019 - "arrow fit" and "text fit" moved from member settings on ON_Dimension
// to settings on ON_DimStyle.
// The file being read is older than the change.
const ON_DimStyle::arrow_fit new_arrow_fit = ON_DimStyle::ArrowFitFromUnsigned((unsigned int)legacy_arrow_fit);
const ON_DimStyle::text_fit new_text_fit = ON_DimStyle::TextFitFromUnsigned((unsigned int)legacy_text_fit);
const ON_DimStyle& ds = archive.ArchiveCurrentDimStyle();
bool bSetArrowFit = (new_arrow_fit != ArrowFit(&ds));
if (bSetArrowFit)
SetArrowFit(&ds, new_arrow_fit);
bool bSetTextFit = (new_text_fit != TextFit(&ds));
if (bSetTextFit)
SetTextFit(&ds, new_text_fit);
}
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
{
ON_3dVector view_x = nullptr == vp ? ON_3dVector::XAxis : vp->CameraX();
ON_3dVector view_y = nullptr == vp ? ON_3dVector::YAxis : vp->CameraY();
ON_3dVector view_z = nullptr == vp ? ON_3dVector::ZAxis : vp->CameraZ();
ON::view_projection projection = vp ? vp->Projection() : ON::view_projection::parallel_view;
bool bDrawForward = dimstyle == nullptr ? false : dimstyle->DrawForward();
return GetTextXform(model_xform, view_x, view_y, view_z, projection, bDrawForward, dimstyle, dimscale, text_xform_out);
}
bool ON_DimLinear::GetTextXform(
const ON_Xform * model_xform,
const ON_3dVector view_x,
const ON_3dVector view_y,
const ON_3dVector view_z,
ON::view_projection projection,
bool bDrawForward,
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 = (projection == ON::view_projection::perspective_view) ? cos(89.0 * ON_DEGREES_TO_RADIANS) : cos(80.001 * ON_DEGREES_TO_RADIANS);
ON_3dPoint text_center = ON_3dPoint::Origin;
// Text starts out approximately centered at origin
ON_3dPoint cp[4];
// 06 Feb 2025 - Jeff: https://mcneel.myjetbrains.com/youtrack/issue/RH-84156
// Don't exit just because the corners don't exist (i.e. no text or zero-length text).
// We still need the transform to get computed.
if (text->Get3dCorners(cp))
{
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();
if (dimstyle->MaskFrameType() != ON_TextMask::MaskFrame::NoFrame)
text_gap += dimstyle->TextMask().MaskBorder(); // RH-71452
text_gap *= 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 };
ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit();
if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
arrowflipped[0] = arrowflipped[1] = true;
ON_DimStyle::text_fit text_fit = dimstyle->TextFit();
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 (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextRight)
{
total_text_width = 0.0;
text_outside = true;
}
//else if (force_text == ON_Dimension::ForceText::Inside)
else if (text_fit == ON_DimStyle::text_fit::TextInside)
{
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 (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
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 && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either
{
arrowflipped[0] = true;
arrowflipped[1] = true;
}
}
//else if (ForceArrow::Auto == force_arrow) // text fits
else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // 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 * 3.0);
if (text_fit == ON_DimStyle::text_fit::TextLeft || text_fit == ON_DimStyle::text_fit::TextHintLeft)
{
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 && !model_xform->IsIdentity())
{
dim_xaxis.Transform(*model_xform);
dim_yaxis.Transform(*model_xform);
dim_zaxis.Transform(*model_xform);
}
ON_3dVector view_xdir = view_x;
ON_3dVector view_ydir = view_y;
ON_3dVector view_zdir = view_z;
// text is in dimension plane, not horizontal to the view
ON_2dVector h_dir = HorizontalDirection();
ON_3dVector text_xdir = dim_xaxis;
ON_3dVector text_ydir = dim_yaxis;
ON_3dVector text_zdir = dim_zaxis;
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);
text_ydir.Rotate(h_angle, dim_zaxis);
}
}
bool flip_x = false;
bool flip_y = false;
CalcTextFlip(
text_xdir, text_ydir, text_zdir,
view_xdir, view_ydir, view_zdir,
model_xform,
fliptol,
flip_x,
flip_y);
if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
{
// Moves the text to AboveLine if that's the alignment mode
double dy = flip_y ? -1.0 : 1.0;
double d = (text_height * 0.5 + text_gap) * dy;
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)
{
ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward
if (flip_x)
{
mxf.Mirror(text_center, ON_3dVector::XAxis);
text_xform_out = text_xform_out * mxf;
}
if (flip_y)
{
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);
}
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 (m_use_default_text_point || fabs(m_user_text_point.y - m_dimline_pt.y) < style->TextGap() * dimscale * 0.75)
{
// 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 is within 3/4 * text gap of default vertical position, draw the extended line
// 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 (ArrowIsFlipped(0) && ArrowIsFlipped(1) && !style->ForceDimLine())
{
// Don't draw dimline between extensions if arrows are flipped
lines[3].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y);
lines[3].to = lines[2].to;
lines[2].to = m_plane.PointAt(0.0, m_dimline_pt.y);
isline[3] = true;
}
else 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)
{
UpdateDimensionText(dimstyle);
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] + cp[2]) / 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();
if (dimstyle->MaskFrameType() != ON_TextMask::MaskFrame::NoFrame)
text_gap += dimstyle->TextMask().MaskBorder(); // RH-71452
text_gap *= dimscale;
bool draw_forward = dimstyle->DrawForward();
ON_2dPoint text_pt_2d = TextPoint();
bool arrowflipped[2] = { false, false };
bool text_outside = false;
ON_DimStyle::arrow_fit arrow_fit = dimstyle->ArrowFit();
if (ON_DimStyle::arrow_fit::ArrowsOutside == arrow_fit)
arrowflipped[0] = arrowflipped[1] = true;
ON_DimStyle::text_fit text_fit = dimstyle->TextFit();
// 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 (text_fit != ON_DimStyle::text_fit::Auto)
total_text_width = 0.0;
else if (0.0 < total_text_width)
total_text_width += text_gap;
if (text_fit != ON_DimStyle::text_fit::Auto &&
text_fit != ON_DimStyle::text_fit::TextInside)
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 && ON_DimStyle::arrow_fit::Auto == arrow_fit) // arrows dont fit either
{
arrowflipped[0] = true;
arrowflipped[1] = true;
}
}
else if (ON_DimStyle::arrow_fit::Auto == arrow_fit) // 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 (text_fit == ON_DimStyle::text_fit::TextLeft)
{
//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 el = style->FixedExtensionLen() * 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;
double el0 = el;
if (m_ext_offset_1 > radius)
{
eo0 = -eo0;
ee0 = -ee0;
el0 = -el0;
}
ON_2dPoint lpt0, lpt1;
if (style->FixedExtensionLenOn())
{
lpt1 = m_vec_1 * (radius + ee0);
lpt0 = lpt1 + (-m_vec_1 * (el0 + ee0));
}
else
{
lpt0 = m_vec_1 * (m_ext_offset_1 + eo0);
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;
double el1 = el;
if (m_ext_offset_2 > radius)
{
eo1 = -eo1;
ee1 = -ee1;
el1 = -el1;
}
ON_2dPoint lpt0, lpt1;
if (style->FixedExtensionLenOn())
{
lpt1 = m_vec_2 * (radius + ee1);
lpt0 = lpt1 + (-m_vec_2 * (el1 + ee1));
}
else
{
lpt0 = m_vec_2 * (m_ext_offset_2 + eo1);
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;
// 6-Jan-2024 Dale Fugier, ON_ZERO_TOLERANCE is too
// small for an angle tolerace. 1e-6 is more than accurate
// but let's start with 1e-8.
//const double atol = ON_ZERO_TOLERANCE;
const double atol = 1e-8;
while (a0 + atol > 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 };
dim_box.Destroy();
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);
}
}
#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();
if (dimstyle->MaskFrameType() != ON_TextMask::MaskFrame::NoFrame)
text_gap += dimstyle->TextMask().MaskBorder(); // RH-71452
text_gap *= 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);
if (ON_DimStyle::TextLocation::AboveDimLine == text_location)
shift.y = text_gap;
if (ON_DimStyle::TextLocation::InDimLine == text_location)
shift.y = -line_height/2.0;
shift.x = text_gap;
shift.x += landing_length;
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);
textpt_xf = textpt_xf * mxf;
}
if (fy)
{
mxf.Mirror(ON_3dPoint::Origin, textplane.yaxis);
textpt_xf = textpt_xf * mxf;
}
text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale);
text_xform_out = textrot_xf * text_xform_out;
text_xform_out = textpt_xf * text_xform_out;
text_xform_out = dimplane_xf * text_xform_out;
}
}
}
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::TextLocation::AboveDimLine == text_location && ON::TextOrientation::InView != dimstyle->DimRadialTextOrientation())
landinglength += text_rect[1].DistanceTo(text_rect[0]); // Add text width to draw under the text
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;
case MeasuredDirection::Unset:
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;
}