// // 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 . // //////////////////////////////////////////////////////////////// #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(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(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(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]; 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(); 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); } class StylePointer { public: ~StylePointer() { if (m_delete_style && nullptr != m_style) { delete m_style; m_style = nullptr; m_delete_style = false; } } const ON_DimStyle* m_style = nullptr; bool m_delete_style = false; }; bool ON_DimLinear::GetAnnotationBoundingBox( const ON_Viewport* vp, const ON_DimStyle* dimstyle, double dimscale, double* boxmin, double* boxmax, bool bGrow ) const { if (nullptr == dimstyle) dimstyle = &ON_DimStyle::Default; const ON_2dPoint hash_points[] = { m_def_pt_2, m_dimline_pt }; const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( vp, dimstyle, dimscale, m_user_text_point, (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), hash_points ); if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) return true; if (nullptr == boxmin || nullptr == boxmax) return false; ON_Xform text_xform; GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform); ON_BoundingBox dim_box; const ON_TextContent* text = Text(); ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; if (nullptr != text && text->GetTightBoundingBox(dim_box)) { text_rect[0].Set(dim_box.m_min.x, dim_box.m_min.y, 0.0); text_rect[1].Set(dim_box.m_max.x, dim_box.m_min.y, 0.0); text_rect[2].Set(dim_box.m_max.x, dim_box.m_max.y, 0.0); text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); for (int i = 0; i < 4; i++) text_rect[i].Transform(text_xform); // Text + gap bounding rect for (int i = 0; i < 4; i++) { dim_box.Set(text_rect[i], 0 < i ? true : false); } } // Get non-text display geometry for the dimension #define dimlinecount 4 bool dimlines[dimlinecount] = { false, false, false, false }; ON_Line lines[dimlinecount]; if (GetDisplayLines(vp, dimstyle, dimscale, text_rect, lines, dimlines, dimlinecount)) { for (int i = 0; i < dimlinecount; i++) { if (dimlines[i]) { dim_box.Set(lines[i].from, true); dim_box.Set(lines[i].to, true); } } } #undef dimlinecount double points[12]; if(Get3dPoints( (ON_3dPoint*)(&points[0]), // defpt1 (ON_3dPoint*)(&points[3]), // defpt2 (ON_3dPoint*)(&points[6]), // arrowpt1 (ON_3dPoint*)(&points[9]), // arrowpt2 nullptr, // dimlinept nullptr)) // textpt dim_box.Set(3, 0, 4, 3, points, true); // Include arrows bool arrowflipped[2] = { ArrowIsFlipped(0), ArrowIsFlipped(1) }; double scale = dimstyle->ArrowSize() * dimscale; for (int ai = 0; ai < 2; ai++) { if (0 == ai && dimstyle->SuppressArrow1()) continue; if (1 == ai && dimstyle->SuppressArrow2()) continue; ON_Xform arrow_xform(1.0); GetArrowXform(ai, scale, arrowflipped[ai], false, arrow_xform); ON_Arrowhead::arrow_type arrowtype = (0 == ai) ? dimstyle->ArrowType1() : dimstyle->ArrowType2(); ON_UUID arrow_block_id = (0 == ai) ? dimstyle->ArrowBlockId1() : dimstyle->ArrowBlockId2(); ON_Arrowhead::GetArrowheadBoundingBox(arrowtype, arrow_block_id, arrow_xform, dim_box, true); } return Internal_GetBBox_End(dim_box, hash, boxmin, boxmax, bGrow); } bool ON_DimLinear::IsValidLinearDimensionType( ON::AnnotationType annotation_type ) { return ( ON::AnnotationType::Aligned == annotation_type || ON::AnnotationType::Rotated == annotation_type ); } bool ON_DimLinear::SetLinearDimensionType( ON::AnnotationType linear_dimension_type ) { if ( !ON_DimLinear::IsValidLinearDimensionType(linear_dimension_type) ) { ON_ERROR("Invalid linear_dimension_type parameter."); return false; } m_annotation_type = linear_dimension_type; return true; } // Returns unadjusted distance in entity plane coordinates double ON_DimLinear::Measurement() const { if (!m_def_pt_2.IsValid()) return 0.0; if (ON_2dPoint::UnsetPoint == m_def_pt_2) return 0.0; double d = fabs(m_def_pt_2.x); if (DistanceScale() != 1.0) d *= DistanceScale(); return d; } // Returns midpoint of dimline ON_2dPoint ON_DimLinear::DefaultTextPoint() const { return ON_2dPoint(m_def_pt_2.x / 2.0, m_dimline_pt.y); } ON_2dPoint ON_DimLinear::DefPoint1() const { return ON_2dPoint(0.0, 0.0); } ON_2dPoint ON_DimLinear::DefPoint2() const { return m_def_pt_2; } ON_2dPoint ON_DimLinear::DimlinePoint() const { return m_dimline_pt; } void ON_DimLinear::Set2dDimlinePoint(ON_2dPoint pt) { m_dimline_pt = pt; } void ON_DimLinear::Set3dDimlinePoint(ON_3dPoint pt) { ON_2dPoint p; if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) Set2dDimlinePoint(p); } ON_2dPoint ON_DimLinear::ArrowPoint1() const { return ON_2dPoint(0.0, m_dimline_pt.y); } ON_2dPoint ON_DimLinear::ArrowPoint2() const { return ON_2dPoint(m_def_pt_2.x, m_dimline_pt.y); } bool ON_DimLinear::Get3dPoints( ON_3dPoint* defpt1, ON_3dPoint* defpt2, ON_3dPoint* arrowpt1, ON_3dPoint* arrowpt2, ON_3dPoint* dimline, ON_3dPoint* textpt) const { bool rc = true; if (nullptr != defpt1) *defpt1 = m_plane.origin; if (nullptr != defpt2) { if (ON_3dPoint::UnsetPoint != m_def_pt_2) *defpt2 = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y); else { *defpt2 = ON_3dPoint::UnsetPoint; rc = false; } } if (nullptr != dimline) { if (ON_3dPoint::UnsetPoint != m_dimline_pt) *dimline = m_plane.PointAt(m_dimline_pt.x, m_dimline_pt.y); else { *dimline = ON_3dPoint::UnsetPoint; rc = false; } } if (nullptr != arrowpt1) { if (ON_3dPoint::UnsetPoint != m_dimline_pt) *arrowpt1 = m_plane.PointAt(0.0, m_dimline_pt.y); else { *arrowpt1 = ON_3dPoint::UnsetPoint; rc = false; } } if (nullptr != arrowpt2) { if (ON_3dPoint::UnsetPoint != m_def_pt_2 && ON_3dPoint::UnsetPoint != m_dimline_pt) *arrowpt2 = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y); else { *arrowpt2 = ON_3dPoint::UnsetPoint; rc = false; } } if (nullptr != textpt) { ON_2dPoint textpt2d = ON_2dPoint::UnsetPoint; if (m_use_default_text_point) textpt2d = DefaultTextPoint(); else if (ON_3dPoint::UnsetPoint != m_user_text_point) textpt2d = m_user_text_point; if (ON_3dPoint::UnsetPoint != textpt2d) *textpt = m_plane.PointAt(textpt2d.x, textpt2d.y); else { *textpt = ON_3dPoint::UnsetPoint; rc = false; } } return rc; } ON_DimLinear* ON_DimLinear::CreateAligned( ON_3dPoint extension_point0, ON_3dPoint extension_point1, ON_3dPoint dimension_line_point, ON_3dVector plane_normal, ON_UUID style_id, ON_DimLinear* destination ) { if (nullptr != destination) *destination = ON_DimLinear::Empty; const ON_Line ext_line(extension_point0, extension_point1); if (false == ext_line.IsValid()) return nullptr; ON_Plane plane; plane.xaxis = ext_line.Tangent(); if (false == plane.xaxis.IsUnitVector()) return nullptr; plane.zaxis = plane_normal; if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize()) return nullptr; plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize()) return nullptr; plane.origin = extension_point0; plane.UpdateEquation(); if (false == plane.IsValid()) { plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis); plane.zaxis.Unitize(); plane.UpdateEquation(); if (false == plane.IsValid()) return nullptr; } ON_Line dim_line = ext_line; double t = 0.5; if (dimension_line_point.IsValid()) { ON_3dPoint p = plane.ClosestPointTo(dimension_line_point); ON_Line l(p, p + ext_line.Direction()); ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1)); if (l2.IsValid() && fabs(1.0 - l2.Tangent()*plane.xaxis) <= 1e-4) dim_line = l2; double s = ON_UNSET_VALUE; dim_line.ClosestPointTo(dimension_line_point, &s); if (s >= 0.0 && s <= 1.0) t = s; else if (s < 0.0) t = 0.0; else if (s > 1.0) t = 1.0; } ON_DimLinear* dim_linear = (nullptr != destination) ? destination : new ON_DimLinear(); ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane); if (false == dim_linear->Create(ON::AnnotationType::Aligned, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(t)) ) { if (nullptr != destination) *destination = ON_DimLinear::Empty; else delete dim_linear; dim_linear = nullptr; } return dim_linear; } ON_DimLinear* ON_DimLinear::CreateRotated( ON_3dPoint extension_point0, ON_3dPoint extension_point1, ON_Line dimension_line, ON_3dVector plane_normal, ON_UUID style_id, ON_DimLinear* destination ) { if (nullptr != destination) *destination = ON_DimLinear::Empty; const ON_Line ext_line(extension_point0, extension_point1); if (false == ext_line.IsValid()) return nullptr; ON_Plane plane; plane.xaxis = ext_line.Tangent(); if (false == plane.xaxis.IsUnitVector()) return nullptr; plane.zaxis = plane_normal; if (false == plane.zaxis.IsUnitVector() && false == plane.zaxis.Unitize()) return nullptr; plane.yaxis = ON_CrossProduct(plane.zaxis, plane.xaxis); if (false == plane.yaxis.IsUnitVector() && false == plane.yaxis.Unitize()) return nullptr; plane.origin = extension_point0; plane.UpdateEquation(); if (false == plane.IsValid()) { plane.zaxis = ON_CrossProduct(plane.xaxis, plane.yaxis); plane.zaxis.Unitize(); plane.UpdateEquation(); if (false == plane.IsValid()) return nullptr; } ON_Line dim_line = ext_line; double rot_angle = 0.0; if (dimension_line.IsValid()) { ON_Line l(plane.ClosestPointTo(dimension_line.from),plane.ClosestPointTo(dimension_line.to)); if (l.IsValid()) { ON_Line l2(l.ClosestPointTo(extension_point0), l.ClosestPointTo(extension_point1)); if (l2.IsValid() && fabs(l2.Tangent()*plane.zaxis) <= 1e-4) { dim_line = l2; double x = l2.Tangent()*plane.xaxis; double y = l2.Tangent()*plane.yaxis; double a = -atan2(y, x); if (a < -ON_PI) a += 2.0*ON_PI; else if (a > ON_PI) a -= 2.0*ON_PI; if (-ON_PI <= a && a <= ON_PI) rot_angle = a; } } } ON_DimLinear* dim_linear = (nullptr != destination) ? destination : new ON_DimLinear(); ON_3dVector horizontal = ON_Annotation::GetDefaultHorizontal(plane); if (false == dim_linear->Create(ON::AnnotationType::Rotated, style_id, plane, horizontal, extension_point0, extension_point1, dim_line.PointAt(0.5), rot_angle) ) { if (nullptr != destination) *destination = ON_DimLinear::Empty; else delete dim_linear; dim_linear = nullptr; } return dim_linear; } // Projects all points to the input plane bool ON_DimLinear::Create( ON::AnnotationType dim_type, const ON_UUID style_id, const ON_Plane& plane, const ON_3dVector& ref_horizontal, const ON_3dPoint& def_pt_1, const ON_3dPoint& def_pt_2, const ON_3dPoint& dimline_pt, double rotation_in_plane ) { m_dimstyle_id = style_id; if (ON_nil_uuid == m_dimstyle_id) return true; if ( !ON_DimLinear::IsValidLinearDimensionType(dim_type) ) { ON_ERROR("Invalid dim_type parameter."); return false; } if (!plane.IsValid() || !def_pt_1.IsValid() || !def_pt_2.IsValid() || !dimline_pt.IsValid() || !ON_IsValid(rotation_in_plane)) return false; bool rc = SetLinearDimensionType(dim_type); m_plane = plane; if (0.0 != rotation_in_plane) rc = m_plane.Rotate(-rotation_in_plane, m_plane.zaxis); if (rc) { m_plane.origin = plane.ClosestPointTo(def_pt_1); rc = m_plane.ClosestPointTo(def_pt_2, &m_def_pt_2.x, &m_def_pt_2.y); } if (rc) rc = m_plane.ClosestPointTo(dimline_pt, &m_dimline_pt.x, &m_dimline_pt.y); if (rc) { ON_2dVector horiz; ON_3dPoint hp = m_plane.origin + ref_horizontal; rc = m_plane.ClosestPointTo(hp, &horiz.x, &horiz.y); if (rc) SetHorizontalDirection(horiz); } return rc; } void ON_DimLinear::Set2dDefPoint1(ON_2dPoint pt) { // Move plane origin to pt ON_3dPoint p = m_plane.PointAt(pt.x, pt.y); Set3dDefPoint1(p); } void ON_DimLinear::Set2dDefPoint2(ON_2dPoint pt) { if ( ON::AnnotationType::Aligned == Type() ) { // Rotate around DefPoint1 ON_2dVector xdir = pt; if (!xdir.Unitize()) return; double r = atan2(pt.y, pt.x); m_plane.Rotate(r, m_plane.zaxis); pt.Rotate(-r, ON_2dPoint(0.0, 0.0)); } m_def_pt_2 = pt; } void ON_DimLinear::Set3dDefPoint1(ON_3dPoint pt) { ON_2dPoint p; if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) { if (ON::AnnotationType::Aligned == Type()) { ON_2dVector xdir = m_def_pt_2 - p; if (!xdir.Unitize()) return; m_plane.origin = pt; m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis); } else //DimensionType::Rotated { m_plane.origin = pt; m_dimline_pt.x -= p.x; m_dimline_pt.y -= p.y; m_def_pt_2.x -= p.x; m_def_pt_2.y -= p.y; } } } void ON_DimLinear::Set3dDefPoint2(ON_3dPoint pt) { if (ON::AnnotationType::Aligned == Type()) { ON_2dPoint p; if (m_plane.ClosestPointTo(pt, &p.x, &p.y)) { // Rotate around DefPoint1 ON_2dVector xdir = p; if (!xdir.Unitize()) return; m_plane.Rotate(xdir.y, xdir.x, m_plane.zaxis); } } else m_plane.ClosestPointTo(pt, &m_def_pt_2.x, &m_def_pt_2.y); } static int ClipLineToTextRect( const ON_Viewport* vp, const ON_Line dimline, const ON_3dPoint text_rect[4], ON_Line linesegs[2]) { ON_PlaneEquation cam_plane_eq; ON_3dPoint cam_loc = ON_3dPoint::Origin; ON_3dVector cam_dir = ON_3dVector::ZAxis; if (nullptr == vp) { cam_loc = ON_3dPoint::Origin; ON_3dVector xdir = text_rect[1] - text_rect[0]; xdir.Unitize(); ON_3dVector ydir = text_rect[3] - text_rect[0]; ydir.Unitize(); cam_dir = ON_CrossProduct(xdir, ydir); cam_dir = cam_dir * 100.0; cam_loc = cam_loc + cam_dir; cam_dir = cam_dir + cam_dir; } else { cam_loc = vp->CameraLocation(); cam_dir = -vp->CameraDirection(); } cam_plane_eq.Create(cam_loc, cam_dir); ON_PlaneEquation frust_plane_eq[4]; ON_3dVector v0, v1; v0 = text_rect[3] - cam_loc; if (!v0.Unitize()) return 0; bool bTextRectBackwards = false; { ON_3dVector a = text_rect[2] - text_rect[0]; ON_3dVector b = text_rect[3] - text_rect[1]; ON_3dVector c = ON_CrossProduct(a, b); if ((c * cam_dir) < 0.0) bTextRectBackwards = true; } for (int i = 0; i < 4; i++) { v1 = text_rect[i] - cam_loc; if (!v1.Unitize()) return 0; // Makes normals facing out of frustum ON_3dVector z = ON_CrossProduct(v0, v1); if (bTextRectBackwards) z = -z; if (!frust_plane_eq[i].Create(cam_loc, z)) return 0; v0 = v1; } double s[4]; int intcount = 0; for (int i = 0; i < 4; i++) { double t; if (ON_Intersect(dimline, frust_plane_eq[i], &t) && t >= 0.0 && t <= 1.0) { ON_3dPoint p = dimline.PointAt(t); double d = cam_plane_eq.ValueAt(p); if (0.0 < d) continue; // intersection behind camera bool inside = true; // Test if intersection is behind the other 3 planes // If not, it's outside the frustum for (int j = 1; j < 4; j++) { // eval other planes at p d = frust_plane_eq[(i + j) % 4].ValueAt(p); if (0.0 < d) // intersection is outside frustum { inside = false; break; } } if (inside) { s[intcount] = t; intcount++; } } } bool end_1_inside = true; bool end_2_inside = true; if (0.0 < cam_plane_eq.ValueAt(dimline.from)) end_1_inside = false; // Dimline from point behind camera else for (int i = 0; i < 4; i++) { if (frust_plane_eq[i].ValueAt(dimline.from) > 0.0) { end_1_inside = false; break; } } if (0.0 < cam_plane_eq.ValueAt(dimline.to)) end_2_inside = false; else for (int i = 0; i < 4; i++) { if (frust_plane_eq[i].ValueAt(dimline.to) > 0.0) { end_2_inside = false; break; } } if (end_1_inside && end_2_inside) return 0; if (0 == intcount) { // no intersection with frustum - return the whole line linesegs[0] = dimline; return 1; } double max_s = s[0]; double min_s = s[0]; for (int i = 1; i < intcount; i++) { if (s[i] > max_s) max_s = s[i]; if (s[i] < min_s) min_s = s[i]; } int segcount = 0; if (!end_1_inside && intcount > 0) { linesegs[segcount].from = dimline.from; linesegs[segcount].to = dimline.PointAt(min_s); segcount++; } if (!end_2_inside && intcount > 0) { linesegs[segcount].from = dimline.PointAt(max_s); linesegs[segcount].to = dimline.to; segcount++; } return segcount; } static int ClipArcToTextRect( const ON_Viewport* vp, const ON_Arc dimarc, const ON_3dPoint text_rect[4], ON_Arc arcsegs[2]) { if (nullptr == vp) return 0; ON_3dPoint cam_loc = vp->CameraLocation(); ON_PlaneEquation cam_plane_eq; ON_3dVector cam_dir = -vp->CameraDirection(); cam_plane_eq.Create(cam_loc, cam_dir); ON_Plane frust_plane[4]; ON_3dVector v0, v1; v0 = text_rect[3] - cam_loc; if (!v0.Unitize()) return 0; for (int i = 0; i < 4; i++) { if (!frust_plane[i].CreateFromPoints(cam_loc, text_rect[i], text_rect[(i + 1) % 4])) return 0; } bool bTextRectBackwards = false; { ON_3dVector a = text_rect[2] - text_rect[0]; ON_3dVector b = text_rect[3] - text_rect[1]; ON_3dVector c = ON_CrossProduct(a, b); if ((c * cam_dir) < 0.0) bTextRectBackwards = true; } for (int i = 0; i < 4; i++) { v1 = text_rect[i] - cam_loc; if (!v1.Unitize()) return 0; // Makes normals facing out of frustum ON_3dVector z = ON_CrossProduct(v0, v1); z.Unitize(); if (bTextRectBackwards) z = -z; if(!frust_plane[i].CreateFromNormal(cam_loc, z)) return 0; v0 = v1; } double s[8]; int intcount = 0; for (int i = 0; i < 4; i++) { ON_3dPoint p[2]; int icount = ON_Intersect(frust_plane[i], dimarc, p[0], p[1]); if (icount > 2) icount = 2; for (int ip = 0; ip < icount; ip++) { double d = cam_plane_eq.ValueAt(p[ip]); if (0.0 < d) continue; // intersection behind camera bool inside = true; // Test if intersection is behind the other 3 planes // If not, it's outside the frustum for (int j = 1; j < 4; j++) { // eval other planes at p d = frust_plane[(i + j) % 4].plane_equation.ValueAt(p[ip]); if (0.0 < d) // intersection is outside frustum { inside = false; break; } } if (inside) { dimarc.ClosestPointTo(p[ip], &s[intcount]); intcount++; if (intcount > 7) intcount = 7; } } } bool end_1_inside = true; bool end_2_inside = true; ON_3dPoint end1 = dimarc.StartPoint(); ON_3dPoint end2 = dimarc.EndPoint(); if (0.0 < cam_plane_eq.ValueAt(end1)) end_1_inside = false; // Dimline from point behind camera else for (int i = 0; i < 4; i++) { if (frust_plane[i].plane_equation.ValueAt(end1) > 0.0) { end_1_inside = false; break; } } if (0.0 < cam_plane_eq.ValueAt(end2)) end_2_inside = false; else for (int i = 0; i < 4; i++) { if (frust_plane[i].plane_equation.ValueAt(end2) > 0.0) { end_2_inside = false; break; } } if (0 == intcount) // no intersection with frustum { if (end_1_inside && end_2_inside) return 0; else if (!end_1_inside && !end_2_inside) { arcsegs[0] = dimarc; return 1; } } double max_s = s[0]; double min_s = s[0]; for (int i = 1; i < intcount; i++) { if (s[i] > max_s) max_s = s[i]; if (s[i] < min_s) min_s = s[i]; } int segcount = 0; ON_Interval domain = dimarc.Domain(); if (!end_1_inside && intcount > 0) { arcsegs[segcount] = dimarc; arcsegs[segcount].Trim(ON_Interval(domain[0], min_s)); segcount++; } if (!end_2_inside && intcount > 0) { arcsegs[segcount] = dimarc; arcsegs[segcount].Trim(ON_Interval(max_s, domain[1])); segcount++; } return segcount; } bool ON_DimLinear::GetDisplayLines( const ON_Viewport* vp, const ON_DimStyle* style, double dimscale, ON_3dPoint text_rect[4], ON_Line lines[4], bool isline[4], int maxlines ) const { if (maxlines != 4) { ON_ERROR("Wrong linecount calling ON_DimLinear::GetDisplayLines.\n"); return false; } if (nullptr == style) return false; //ON_BoundingBox text_box; //const ON_TextContent* text = Text(); //if (nullptr != text) // text->GetTightBoundingBox(text_box); double eo = style->ExtOffset() * dimscale; double ee = style->ExtExtension() * dimscale; double el = style->FixedExtensionLen() * dimscale; if (!style->SuppressExtension1()) { double o = eo; double e = ee; double l = el; if (m_dimline_pt.y < 0.0) { o = -o; e = -e; l = -l; } if (style->FixedExtensionLenOn()) lines[0].from = m_plane.PointAt(0.0, m_dimline_pt.y - l); else lines[0].from = m_plane.PointAt(0.0, o); lines[0].to = m_plane.PointAt(0.0, m_dimline_pt.y + e); isline[0] = true; } else isline[0] = false; if (!style->SuppressExtension2()) { double o = eo; double e = ee; double l = el; if (m_dimline_pt.y < m_def_pt_2.y) { o = -o; e = -e; l = -l; } if (style->FixedExtensionLenOn()) lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y - l); else lines[1].from = m_plane.PointAt(m_def_pt_2.x, m_def_pt_2.y + o); lines[1].to = m_plane.PointAt(m_def_pt_2.x, m_dimline_pt.y + e); isline[1] = true; } else isline[1] = false; const ON_DimStyle::TextLocation text_location = style->DimTextLocation(); const ON::TextOrientation text_orientation = style->DimTextOrientation(); const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle(); double de[2] = { style->DimExtension() * dimscale, style->DimExtension() * dimscale }; if (fabs(de[0]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(0)) de[0] = style->ArrowSize() * dimscale * 1.5; if (fabs(de[1]) < ON_ZERO_TOLERANCE && ArrowIsFlipped(1)) de[1] = style->ArrowSize() * dimscale * 1.5; if (m_def_pt_2.x < 0.0) { double d = de[0]; de[0] = -de[1]; de[1] = -d; } lines[2].from = m_plane.PointAt(-de[0], m_dimline_pt.y); lines[2].to = m_plane.PointAt(m_def_pt_2.x + de[1], m_dimline_pt.y); isline[2] = true; isline[3] = false; if (/*UseDefaultTextPoint() &&*/ ON_DimStyle::TextLocation::InDimLine != text_location) { if (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(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; while (a0 + ON_ZERO_TOLERANCE > ON_PI * 2.0) a0 -= ON_PI * 2.0; a0 -= dim_ext_ang[0]; a1 += dim_ext_ang[1]; if (arcs[0].Create(c, ON_Interval(a0, a1))) { ON_Xform xf; xf.Rotation(ON_xy_plane, m_plane); arcs[0].Transform(xf); isarc[0] = true; } } // This part clips dimarc to textbox except when text is above line //ON_INTERNAL_OBSOLETE::V5_TextDisplayMode text_mode = style->TextAlignment(); const ON_DimStyle::TextLocation text_location = style->DimTextLocation(); const ON::TextOrientation text_orientation = style->DimTextOrientation(); const ON_DimStyle::ContentAngleStyle text_angle_style = style->DimTextAngleStyle(); //if (ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine != text_mode) if ( ON_DimStyle::TextLocation::InDimLine == text_location || ON::TextOrientation::InView == text_orientation || ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style ) { if (text_rect[0].DistanceTo(text_rect[2]) > ON_SQRT_EPSILON) { ON_Arc dimarc(arcs[0]); ON_Arc arc_segs[2]; if (dimarc.IsValid()) { int seg_count = ClipArcToTextRect(vp, dimarc, text_rect, arc_segs); if (0 == seg_count) isarc[0] = false; else { if (seg_count > 0) arcs[0] = arc_segs[0]; if (seg_count > 1) { arcs[1] = arc_segs[1]; isarc[1] = true; } } } } } return true; } void ON_DimAngular::GetArrowXform( int which_end, double arrowlength, bool arrowflipped, bool from_the_back, ON_Xform& arrow_xform_out) const { ON_Xform xf(1.0), xfp, xfs, xfr; if (0 != which_end) which_end = 1; ON_2dPoint ap = (0 == which_end) ? ArrowPoint1() : ArrowPoint2(); xfp.Rotation(ON_xy_plane, Plane()); ON_Xform xft = ON_Xform::TranslationTransformation(ap.x, ap.y, 0.0); double rotang = ON_PI / 2.0; if (1 == which_end) { ON_2dVector v = ap; v.Unitize(); rotang = atan2(v.y, v.x); rotang += ON_PI / 2.0; } if (from_the_back) rotang += ON_PI; if (arrowflipped != (which_end == 0)) rotang += ON_PI; while (rotang >= (2.0 * ON_PI)) rotang -= (2.0 * ON_PI); while (rotang < 0.0) rotang += (2.0 * ON_PI); // little adjustment so dimension arc leaves arrow in the middle double f = (arrowlength * 0.5) / Radius(); if (f > 1.0) f = 1.0; double adjust = asin(f); if (which_end == 1) adjust = -adjust; if (ArrowIsFlipped(which_end)) adjust = -adjust; xfr.Rotation(rotang+adjust, ON_3dVector::ZAxis, ON_3dPoint::Origin); xf = xft * xfr; xf = xfp * xf; xfs = ON_Xform::DiagonalTransformation(from_the_back ? -arrowlength : arrowlength, arrowlength, arrowlength); arrow_xform_out = xf * xfs; } bool ON_DimAngular::UpdateDimensionText( ON::LengthUnitSystem units, const ON_DimStyle* dimstyle) const { return false; } bool ON_DimAngular::GetDistanceDisplayText( ON::LengthUnitSystem units, const ON_DimStyle* dimstyle, ON_wString& displaytext) const { return false; } //---------------------------------------------------------- // Class ON_DimRadial ON_DimRadial::ON_DimRadial() : ON_Dimension(ON::AnnotationType::Radius) {} bool ON_DimRadial::IsValidRadialDimensionType( ON::AnnotationType annotation_type ) { return ( ON::AnnotationType::Radius == annotation_type || ON::AnnotationType::Diameter == annotation_type ); } bool ON_DimRadial::SetRadialDimensionType( ON::AnnotationType radial_dimension_type ) { if ( false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type) ) { ON_ERROR("Invalid radial_dimension_type parameter."); return false; } m_annotation_type = radial_dimension_type; ON_wString usertext = (ON::AnnotationType::Diameter == m_annotation_type) ? ON_wString::DiameterSymbol : ON_wString::RadiusSymbol; usertext += "<>"; SetUserText(usertext); return true; } bool ON_DimRadial::Write( ON_BinaryArchive& archive ) const { const int content_version = 0; if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) return false; bool rc = false; for (;;) { if (!ON_Dimension::Internal_WriteDimension(archive)) break; if (!archive.WritePoint(m_radius_pt)) break; if (!archive.WritePoint(m_dimline_pt)) break; rc = true; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_DimRadial::Read( ON_BinaryArchive& archive ) { *this = ON_DimRadial::Empty; int content_version = -1; if (false == archive.BeginRead3dmAnonymousChunk(&content_version)) return false; bool rc = false; for (;;) { if (content_version < 0) break; if (!ON_Dimension::Internal_ReadDimension(archive)) break; if (!archive.ReadPoint(m_radius_pt)) break; if (!archive.ReadPoint(m_dimline_pt)) break; rc = true; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_DimRadial::Transform(const ON_Xform& xform) { bool rc = xform.IsIdentity(); if (!rc) { rc = true; bool scaling = false; ON_3dVector v = m_plane.xaxis; v.Transform(xform); if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) scaling = true; else { v = m_plane.yaxis; v.Transform(xform); if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) scaling = true; else { v = m_plane.zaxis; v.Transform(xform); if (fabs(1.0 - v.Length()) > ON_SQRT_EPSILON) scaling = true; } } if (scaling) { ON_3dPoint radius_pt_0(ON_3dPoint::UnsetPoint); ON_3dPoint dimline_pt_0(ON_3dPoint::UnsetPoint); if (Get3dPoints(nullptr, &radius_pt_0, &dimline_pt_0, nullptr)) { ON_2dPoint radius_pt(ON_2dPoint::NanPoint), dimline_pt(ON_2dPoint::NanPoint); rc = m_plane.Transform(xform); radius_pt_0.Transform(xform); dimline_pt_0.Transform(xform); if (rc && !m_plane.ClosestPointTo(radius_pt_0, &radius_pt.x, &radius_pt.y)) rc = false; else if (rc && !m_plane.ClosestPointTo(dimline_pt_0, &dimline_pt.x, &dimline_pt.y)) rc = false; if (rc) { Set2dRadiusPoint(radius_pt); Set2dDimlinePoint(dimline_pt); } } } else rc = m_plane.Transform(xform); if (rc) ON_Geometry::Transform(xform); } return rc; } bool ON_DimRadial::Create( ON::AnnotationType radial_dimension_type, const ON_UUID style_id, const ON_Plane& plane, const ON_3dPoint& center_pt, const ON_3dPoint& radius_pt, const ON_3dPoint& dimline_pt) { m_dimstyle_id = style_id; if (ON_nil_uuid == m_dimstyle_id) return true; // .NET dialog hack if (false == ON_DimRadial::IsValidRadialDimensionType(radial_dimension_type)) { ON_ERROR("Invalid radial_dimension_type parameter."); return false; } if (!plane.IsValid() || !center_pt.IsValid() || !center_pt.IsValid() || !radius_pt.IsValid() || !dimline_pt.IsValid()) return false; bool rc = SetRadialDimensionType(radial_dimension_type); m_plane = plane; if (rc) { double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0; ON_3dPoint radius_pton; ON_3dPoint dimline_pton; ON_3dVector v1; ON_3dVector v2; m_plane.origin = plane.ClosestPointTo(center_pt); rc = m_plane.ClosestPointTo(radius_pt, &x1, &y1); if (rc) { rc = m_plane.ClosestPointTo(dimline_pt, &x2, &y2); if (rc) { radius_pton = m_plane.PointAt(x1, y1); dimline_pton = m_plane.PointAt(x2, y2); v1 = radius_pton - m_plane.origin; v2 = dimline_pton - m_plane.origin; rc = v1.Unitize() && v2.Unitize(); } } if (rc) { m_radius_pt.Set(x1, y1); m_dimline_pt.Set(x2, y2); } } return rc; } bool ON_DimRadial::AdjustFromPoints( const ON_Plane& plane, const ON_3dPoint& center_pt, const ON_3dPoint& radius_pt, const ON_3dPoint& dimline_pt ) { ON_2dPoint center_pt2d(ON_2dPoint::Origin), radius_pt2d(ON_2dPoint::Origin), dimline_pt2d(ON_2dPoint::Origin); m_plane = plane; m_plane.origin = plane.ClosestPointTo(center_pt); //if (!plane.ClosestPointTo(center_pt, &c_pt.x, &c_pt.y)) // return false; if (!plane.ClosestPointTo(radius_pt, &radius_pt2d.x, &radius_pt2d.y)) return false; if (!plane.ClosestPointTo(dimline_pt, &dimline_pt2d.x, &dimline_pt2d.y)) return false; ON_2dVector arrow_dir = radius_pt2d - center_pt2d; ON_2dVector drag_dir = dimline_pt2d - center_pt2d; double drag_dist = drag_dir.Length(); if (!arrow_dir.Unitize() || !drag_dir.Unitize()) return false; if (fabs(center_pt2d.y - radius_pt2d.y) < ON_SQRT_EPSILON) { // radial line is horizontal - skip intersecting with dimline dimline_pt2d = arrow_dir * drag_dist; } //m_plane = plane; m_radius_pt.Set(radius_pt2d.x, radius_pt2d.y); m_dimline_pt.Set(dimline_pt2d.x, dimline_pt2d.y); return true; } bool ON_DimRadial::GetBBox(double* bmin, double* bmax, bool grow) const { const ON_DimStyle* dimstyle = nullptr; return GetAnnotationBoundingBox(nullptr, dimstyle, 1.0, bmin, bmax, grow ? true : false); } bool ON_DimRadial::GetAnnotationBoundingBox( const ON_Viewport* vp, const ON_DimStyle* dimstyle, double dimscale, double* boxmin, double* boxmax, bool bGrow ) const { if (nullptr == dimstyle) dimstyle = &ON_DimStyle::Default; const ON_2dPoint hash_points[] = { ON_2dPoint(m_radius_pt), ON_2dPoint(m_dimline_pt) }; const ON_SHA1_Hash hash = Internal_GetBBox_InputHash( vp, dimstyle, dimscale, m_user_text_point, (unsigned int)(sizeof(hash_points)/sizeof(hash_points[0])), hash_points ); if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) return true; if (nullptr == boxmin || nullptr == boxmax) return false; ON_Xform text_xform; GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform); ON_BoundingBox dim_box; const ON_TextContent* text = Text(); ON_3dPoint text_rect[4] = { ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin, ON_3dPoint::Origin }; 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(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(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(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(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(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(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; }