/* // Copyright (c) 1993-2017 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 ON_OBJECT_IMPLEMENT(ON_Leader, ON_Annotation, "945BF594-6FF9-4F5C-BFC0-B3AF528F29D2"); void ON_Leader::Internal_Destroy() { if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } } void ON_Leader::Internal_CopyFrom(const ON_Leader& src) { if (nullptr != src.m_curve) m_curve = new ON_NurbsCurve(*src.m_curve); m_points = src.m_points; m_text_point = src.m_text_point; } ON_Leader::ON_Leader() : ON_Annotation(ON::AnnotationType::Leader) , m_curve(nullptr) , m_text_point(ON_2dPoint::UnsetPoint) { } ON_Leader::~ON_Leader() { Internal_Destroy(); } ON_Leader::ON_Leader(const ON_Leader& src) : ON_Annotation(src) { if (this != &src) { Internal_CopyFrom(src); } } ON_Leader& ON_Leader::operator=(const ON_Leader& src) { if (this != &src) { Internal_Destroy(); ON_Annotation::operator=(src); Internal_CopyFrom(src); } return *this; } bool ON_Leader::IsValid(ON_TextLog* log) const { return true; } void ON_Leader::Dump(ON_TextLog& log) const { } bool ON_Leader::Write(ON_BinaryArchive& archive) const { const int chunk_version = 1; if (!archive.BeginWrite3dmAnonymousChunk(chunk_version)) return false; bool rc = false; for (;;) { if (false == ON_Annotation::Internal_WriteAnnotation(archive)) break; if (!archive.WriteArray(m_points)) break; rc = true; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_Leader::Read(ON_BinaryArchive& archive) { *this = ON_Leader::Empty; int chunk_version = 0; if (!archive.BeginRead3dmAnonymousChunk(&chunk_version)) return false; bool rc = false; for (;;) { if (chunk_version < 1) break; if (false == ON_Annotation::Internal_ReadAnnotation(archive)) break; if (!archive.ReadArray(m_points)) break; rc = true; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } ON::object_type ON_Leader::ObjectType() const { return ON::annotation_object; } int ON_Leader::Dimension() const { return 3; } bool ON_Leader::GetBBox( // returns true if successful double* bbox_min, // boxmin[dim] double* bbox_max, // boxmax[dim] bool grow // true means grow box ) const { // Even though this doesn't apply view info properly, it has a side-effect of creating runs // so that text query functions will work properly. // I'm adding it back here to make Opennurbs ONx model support for reading and not displaying // files work better wrt text queries return GetAnnotationBoundingBox(nullptr, nullptr, 1.0, bbox_min, bbox_max, grow ? true : false); } bool ON_Leader::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_Leader::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; const ON_TextContent* text = Text(); if (nullptr == text) return true; ON_2dVector tail_dir = TailDirection(dimstyle); 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 = ON_CrossProduct(view_x, view_y); ON_Plane objectplane = Plane(); ON::TextHorizontalAlignment halign = dimstyle->LeaderTextHorizontalAlignment(); ON::TextVerticalAlignment valign = dimstyle->LeaderTextVerticalAlignment(); if (ON::TextHorizontalAlignment::Auto == halign) { double xdotx = objectplane.xaxis * view_x; if (tail_dir.x < -0.00001) xdotx = -xdotx; if (xdotx > -0.00001) halign = ON::TextHorizontalAlignment::Left; else halign = ON::TextHorizontalAlignment::Right; } ON::TextHorizontalAlignment last_halign = text->RuntimeHorizontalAlignment(); text->SetRuntimeHorizontalAlignment(halign); if (last_halign != halign) { const_cast(text)->SetAlignment(halign, valign); } if (DimStyleTextPositionPropertiesHash() != dimstyle->TextPositionPropertiesHash()) { ON_wString rtfstr = text->RtfText(); const_cast(text)->Create( rtfstr, ON::AnnotationType::Leader, dimstyle, text->TextIsWrapped(), text->FormattingRectangleWidth(), text->TextRotationRadians()); const_cast(text)->SetAlignment(halign, valign); } // Find center of scaled text double textblock_width = 0.0; double textblock_height = 0.0; double line_height = 1.0; ON_2dPoint text_corners[4]; ON_2dPoint text_center; ON_2dPoint text_pt(0.0, 0.0); if (text->Get2dCorners(text_corners)) // Gets unscaled 3d size { text_center = (text_corners[0] + text_corners[2]) / 2.0; textblock_width = (text_corners[1].x - text_corners[0].x); textblock_height = (text_corners[3].y - text_corners[0].y); line_height = dimstyle->TextHeight(); ON_2dVector text_shift; text_shift.x = 0.0; text_shift.y = 0.0; // LeaderAttachStyle - Vertical alignment of text with leader text point ON::TextVerticalAlignment attach = dimstyle->LeaderTextVerticalAlignment(); switch (attach) { case ON::TextVerticalAlignment::Top: text_shift.y = -textblock_height / 2.0; break; case ON::TextVerticalAlignment::MiddleOfTop: text_shift.y = -(textblock_height / 2.0) + (line_height / 2.0); break; case ON::TextVerticalAlignment::BottomOfTop: text_shift.y = -(textblock_height / 2.0) + line_height; break; case ON::TextVerticalAlignment::Middle: text_shift.y = 0.0; break; case ON::TextVerticalAlignment::MiddleOfBottom: text_shift.y = (textblock_height / 2.0) - (line_height / 2.0); break; case ON::TextVerticalAlignment::Bottom: text_shift.y = textblock_height / 2.0; break; case ON::TextVerticalAlignment::BottomOfBoundingBox: text_shift.y = (textblock_height / 2.0) + (dimstyle->TextGap()); /*(line_height / 10.0);*/ break; } // 2d Point at center of text but without vertical alignment shift ON_2dPoint text_pt2(0.0, 0.0); if (0 < m_points.Count()) text_pt2 = m_points[m_points.Count() - 1]; double landing_length = 0.0; if (dimstyle->LeaderHasLanding()) landing_length = dimstyle->LeaderLandingLength(); double text_gap = dimstyle->TextGap(); if (ON_TextMask::MaskFrame::RectFrame == dimstyle->MaskFrameType()) text_gap = dimstyle->TextMask().MaskBorder(); double x_offset = dimscale * (landing_length + text_gap + textblock_width / 2.0); text_pt2 = text_pt2 + (tail_dir * x_offset); // Move from Origin to leader plane const ON_Plane& leaderplane = Plane(); ON_2dVector text_dir(1.0, 0.0); // Horizontal to cplane - ON_DimStyle::ContentAngleStyle::Horizontal ON_DimStyle::ContentAngleStyle textangle_style = dimstyle->LeaderContentAngleStyle(); if (ON_DimStyle::ContentAngleStyle::Aligned == textangle_style) { text_dir = tail_dir; if (text_dir.x < 0.0) text_dir = -text_dir; } else if (ON_DimStyle::ContentAngleStyle::Rotated == textangle_style) { text_dir = tail_dir; // Already has rotation included } if (!text_dir.Unitize()) text_dir.Set(1.0, 0.0); ON_Xform textscale_xf(ON_Xform::DiagonalTransformation(dimscale)); ON_Xform textcenter_xf(ON_Xform::IdentityTransformation); // Centers text block at origin ON_Xform textrotation_xf(ON_Xform::IdentityTransformation); // Text rotation around origin ON_Xform world_to_leader_xf(ON_Xform::IdentityTransformation); // WCS plane to leader plane rotation world_to_leader_xf.Rotation(ON_Plane::World_xy, leaderplane); // Rotate text from starting text plane (wcs) to leader plane ON_Xform leader_to_text_pt_xf(ON_Xform::IdentityTransformation); // Leader plane to text point translation leader_to_text_pt_xf = ON_Xform::TranslationTransformation(text_pt2); textrotation_xf.Rotation(text_dir.y, text_dir.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); textcenter_xf.m_xform[0][3] = -text_center.x; textcenter_xf.m_xform[1][3] = -text_center.y + text_shift.y; if (ON::TextOrientation::InView == dimstyle->LeaderTextOrientation()) { const ON_Plane& ldrplane = Plane(); ON_3dPoint text_point_3d = Plane().PointAt(text_pt2.x, text_pt2.y); textrotation_xf.Rotation(text_point_3d, ldrplane.xaxis, ldrplane.yaxis, ldrplane.zaxis, text_point_3d, view_x, view_y, view_z); text_xform_out = textscale_xf * textcenter_xf; text_xform_out = leader_to_text_pt_xf * text_xform_out; text_xform_out = world_to_leader_xf * text_xform_out; text_xform_out = textrotation_xf * text_xform_out; return true; } else if (dimstyle->DrawForward()) { if (dimstyle->DrawForward()) { // Check if the text is right-reading by comparing // text plane x and y, rotated by text rotation angle, // to view right and up ON_3dVector text_xdir = leaderplane.xaxis; ON_3dVector text_ydir = leaderplane.yaxis; ON_3dVector text_zdir = leaderplane.zaxis; if (text_zdir * view_z < 0.0) { ON_Xform xfr = textrotation_xf.Inverse(); text_xdir.Transform(xfr); text_ydir.Transform(xfr); text_zdir.Transform(xfr); } else { text_xdir.Transform(textrotation_xf); text_ydir.Transform(textrotation_xf); text_zdir.Transform(textrotation_xf); } if (nullptr != model_xform) { text_xdir.Transform(*model_xform); text_ydir.Transform(*model_xform); text_zdir.Transform(*model_xform); } bool flip_x = false; bool flip_y = false; const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(88.0 * ON_DEGREES_TO_RADIANS); CalcTextFlip( text_xdir, text_ydir, text_zdir, view_x, view_y, view_z, model_xform, fliptol, flip_x, flip_y); ON_Xform mxf; if (flip_x) { mxf.Mirror(ON_3dPoint::Origin, ON_3dVector::XAxis); textscale_xf = textscale_xf * mxf; } if (flip_y) { mxf.Mirror(ON_3dPoint::Origin, ON_3dVector::YAxis); textscale_xf = textscale_xf * mxf; } } } text_xform_out = textscale_xf * textcenter_xf; text_xform_out = textrotation_xf * text_xform_out; text_xform_out = leader_to_text_pt_xf * text_xform_out; text_xform_out = world_to_leader_xf * text_xform_out; } return true; } // ON_Annotation override bool ON_Leader::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_SHA1_Hash hash = Internal_GetBBox_InputHash( vp, dimstyle, dimscale, m_text_point, m_points.UnsignedCount(), m_points.Array() ); if (Internal_GetBBox_Begin(hash, boxmin, boxmax, bGrow)) return true; if (nullptr == boxmin || nullptr == boxmax) return false; ON_BoundingBox bbox; Internal_GetBBox_TextGlyphBox( vp, dimstyle, dimscale, bbox ); const ON_Curve* curve = Curve(dimstyle); if (nullptr != curve) { ON_BoundingBox curve_box; curve->GetTightBoundingBox(curve_box); bbox.Union(curve_box); } return Internal_GetBBox_End(bbox, hash, boxmin, boxmax, bGrow); } bool ON_Leader::Transform(const ON_Xform& xform) { bool rc = ON_Geometry::Transform(xform); if (rc) { if (xform.IsTranslation()) rc = m_plane.Transform(xform); else { int cnt = m_points.Count(); ON_3dPointArray pts(cnt+1); for (int i = 0; i < cnt; i++) { pts.AppendNew() = m_plane.PointAt(m_points[i].x, m_points[i].y); pts[i].Transform(xform); } m_text_point = ON_3dPoint::UnsetPoint; rc = m_plane.Transform(xform); for (int i = 0; i < cnt; i++) m_plane.ClosestPointTo(pts[i], &m_points[i].x, &m_points[i].y); } } if (rc && nullptr != m_curve && !m_curve->Transform(xform)) { delete m_curve; m_curve = nullptr; } return rc; } // returns the base point and width grip using the current alignments bool ON_Leader::GetTextGripPoints( ON_2dPoint& base, ON_2dPoint& width, const ON_DimStyle* dimstyle, double dimscale) const { const ON_TextContent* text = Text(); if (nullptr == text) return false; ON_3dPoint q[4]; if (!text->Get3dCorners(q)) return false; ON_2dVector taildir = TailDirection(dimstyle); ON_3dPoint wp3 = (taildir.x < 0.0) ? (q[0] + q[3]) / 2.0 : (q[1] + q[2]) / 2.0; ON_3dPoint bp3 = (taildir.x < 0.0) ? (q[1] + q[2]) / 2.0 : (q[0] + q[3]) / 2.0; ON_Xform xform; GetTextXform(nullptr, dimstyle, dimscale, xform); bp3.Transform(xform); wp3.Transform(xform); Plane().ClosestPointTo(bp3, &base.x, &base.y); Plane().ClosestPointTo(wp3, &width.x, &width.y); return true; } //bool ON_Leader::Explode( // const ON_DimStyle* dimstyle, // ON_SimpleArray object_parts) const //{ // bool rc = true; // if (nullptr == dimstyle) // dimstyle = &ON_DimStyle::Default; // double text_scale = dimstyle->DimScale(); // // const ON_TextContent* text = Text(); // if (nullptr != text) // { // ON_3dVector view_x(1.0, 0.0, 0.0); // ON_3dVector view_y(0.0, 1.0, 0.0); // ON_Xform text_xf(1.0); // GetTextXform(text_xf, dimstyle, text_scale, view_x, view_y, false); // ON_Text* textobj = new ON_Text; // if (nullptr != textobj) // { // // Explode to text object, not to curves // ON_Plane plane(ON_xy_plane); // plane.Transform(text_xf); // ON::TextHorizontalAlignment halign = ON::TextHorizontalAlignment::Left; // ON::TextVerticalAlignment valign = ON::TextVerticalAlignment::Top; // text->GetAlignment(halign, valign); // if (textobj->Create(text->RtfText(), dimstyle, plane, halign, valign, 0.0, 0.0)) // { // object_parts.Append(textobj); // } // else // { // delete textobj; // textobj = nullptr; // } // } // } // const ON_NurbsCurve* curve = Curve(dimstyle); // if (nullptr != curve) // { // ON_NurbsCurve* curvecpy = curve->Duplicate(); // if (nullptr != curvecpy) // { // object_parts.Append(curvecpy); // rc = true; // } // } // // if (dimstyle->LeaderHasLanding()) // { // ON_Line line; // LandingLine3d(dimstyle, text_scale, line); // ON_LineCurve* landing = new ON_LineCurve(line); // if (nullptr != landing) // { // object_parts.Append(landing); // } // } // ON_Arrowhead::arrow_type arrowtype = dimstyle->LeaderArrowType(); // if (ON_Arrowhead::arrow_type::NoArrow != arrowtype) // { // ON_Xform arrow_xf(1.0); // ON_2dVector uv(1.0, 0.0); // // if (1 < PointCount()) // { // ON_2dPoint c; // Point2d(1, c); // uv.x = c.x; uv.y = c.y; // } // // if (uv.Unitize()) // { // double arrow_scale = dimstyle->LeaderArrowSize() * text_scale; // ON_Xform xfr, xfs; // arrow_xf.Rotation(ON_xy_plane, Plane()); // xfs.Scale(arrow_scale, arrow_scale, arrow_scale); // xfr.Rotation(uv.y, uv.x, ON_3dVector::ZAxis, ON_3dPoint::Origin); // arrow_xf = arrow_xf * xfs; // arrow_xf = arrow_xf * xfr; // } // // //CRhinoAnnotationDrawing::ExplodeOnArrowhead(arrowtype, dimstyle->LeaderArrowBlockId(), arrow_xf, out_curves, out_objects); // } // // return rc; // //} bool ON_Leader::Create( const wchar_t* leader_text, const ON_DimStyle* dimstyle, int point_count, const ON_3dPoint* points, const ON_Plane& plane, bool bWrapped, double rect_width ) { if (point_count < 2) return false; InvalidateTextPoint(); dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle); SetDimensionStyleId(*dimstyle); SetPlane(plane); SetPoints3d(point_count, points); ON_TextContent* text = nullptr; if (nullptr != leader_text) { text = new ON_TextContent; if (text->Create(leader_text, Type(), dimstyle, bWrapped, rect_width, 0.0)) SetText(text); else { delete text; text = 0; } } return true; } void ON_Leader::DeleteCurve() const { if (nullptr != m_curve) delete m_curve; m_curve = nullptr; } const ON_NurbsCurve* ON_Leader::Curve(const ON_DimStyle* dimstyle) const { ON_DimStyle::leader_curve_type curvetype = ON_DimStyle::leader_curve_type::Polyline; if (nullptr != dimstyle) curvetype = dimstyle->LeaderCurveType(); if (curvetype == ON_DimStyle::leader_curve_type::None) { if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; const_cast(this)->ClearBoundingBox(); } return nullptr; } if (nullptr != m_curve && ((curvetype == ON_DimStyle::leader_curve_type::Polyline && m_curve->Order() != 2) || (curvetype == ON_DimStyle::leader_curve_type::Spline && m_curve->Order() == 2 && m_points.Count() > 2))) { delete m_curve; m_curve = nullptr; } if (nullptr == m_curve) { // Make a 3d curve to cache for display & picking if (m_points.Count() > 1) { int order; int pointcount = m_points.Count(); ON_3dPointArray points(pointcount); for (int i = 0; i < m_points.Count(); i++) points.AppendNew() = m_plane.PointAt(m_points[i].x, m_points[i].y); if (curvetype == ON_DimStyle::leader_curve_type::Spline) { order = 4; if (points.Count() < 4) order = points.Count(); } else { order = 2; } ON_NurbsCurve* nc = new ON_NurbsCurve(3, false, order, pointcount); if (0 != nc && 0 == m_curve) { double k = 0.0; int ki; for (ki = 0; ki < order - 1; ki++) nc->m_knot[ki] = k; for (int i = 0; i < pointcount - order + 1; i++) { k += points[i].DistanceTo(points[i + 1]); nc->m_knot[ki++] = k; } if (k > ON_SQRT_EPSILON) // min curve length { for (int i = 0; i < order - 2; i++) nc->m_knot[ki++] = k; for (int i = 0; i < pointcount; i++) nc->SetCV(i, points[i]); } else { delete nc; nc = nullptr; } m_curve = nc; } const_cast(this)->ClearBoundingBox(); } } return m_curve; } void ON_Leader::SetPlane(ON_Plane plane) { if (Plane() == plane) return; ON_Annotation::SetPlane(plane); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } ClearBoundingBox(); } bool ON_Leader::LandingLine2d( const ON_DimStyle* dimstyle, double dimscale, ON_Line& line) const { if (nullptr == dimstyle) return false; int pointcount = PointCount(); if (0 == pointcount) return false; ON_2dVector taildir = TailDirection(dimstyle); double landing_length = dimstyle->LeaderLandingLength(); if (landing_length < ON_SQRT_EPSILON) { line.from = m_points[pointcount - 1]; line.to = line.from; } if (dimstyle->LeaderContentAngleStyle() == ON_DimStyle::ContentAngleStyle::Horizontal) { if (taildir.x < 0.0) taildir.x = -1.0; else taildir.x = 1.0; taildir.y = 0.0; } line.from = m_points[pointcount - 1]; line.to = line.from + (landing_length * dimscale) * taildir; return true; } bool ON_Leader::LandingLine3d( const ON_DimStyle* dimstyle, double dimscale, ON_Line& line) const { ON_Line line2d; if (!LandingLine2d(dimstyle, dimscale, line2d)) return false; line.from = m_plane.PointAt(line2d.from.x, line2d.from.y); line.to = m_plane.PointAt(line2d.to.x, line2d.to.y); return true; } // Tests if the text is going to the right or left and // Changes text alignment (justification) if necessary // This really changes the TextRun offsets and destroys the Text bbox // Only changes horizontal alignment void ON_Leader::UpdateTextAlignment(ON_2dVector angle) { return; //if (nullptr == m_text) // return; //ON::TextHorizontalAlignment ha; // //if (0.0 > LeaderContentAngleStyle().x) // ha = ON::TextHorizontalAlignment::Right; //else // ha = ON::TextHorizontalAlignment::Left; // //ON::TextHorizontalAlignment current_h; //ON::TextVerticalAlignment current_v; //m_text->GetAlignment(current_h, current_v); //if (ha != current_h) //{ // m_text->SetAlignment(ha, current_v); // m_text->ClearBoundingBox(); //} } ON_2dVector ON_Leader::TailDirection(const ON_DimStyle* dimstyle) const { ON_2dVector dir = ON_2dVector::XAxis; int pointcount = m_points.Count(); if (2 <= pointcount) { dir = m_points[pointcount - 1] - m_points[pointcount - 2]; // This works for Aligned if (nullptr != dimstyle) { if (ON_DimStyle::ContentAngleStyle::Horizontal == dimstyle->LeaderContentAngleStyle()) { if (0.0 > dir.x) // going to the left dir.Set(-1.0, 0.0); else dir.Set(1.0, 0.0); } else if (ON_DimStyle::ContentAngleStyle::Rotated == dimstyle->LeaderContentAngleStyle()) { double r = dimstyle->LeaderContentAngleRadians(); if (fabs(r) > ON_SQRT_EPSILON) { dir.x = cos(r); dir.y = sin(r); } else { dir.Set(1.0, 0.0); } } } dir.Unitize(); } return dir; } void ON_Leader::InvalidateTextPoint() { m_text_point = ON_3dPoint::UnsetPoint; } ON_2dPointArray& ON_Leader::Points2d() { return m_points; } const ON_2dPointArray& ON_Leader::Points2d() const { return m_points; } void ON_Leader::SetPoints2d(int count, const ON_2dPoint* points) { m_points.Empty(); m_points.Append(count, points); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); } void ON_Leader::SetPoints3d(int count, const ON_3dPoint* points) { m_points.Empty(); ON_2dPoint uv; for (int i = 0; i < count; i++) { if(m_plane.ClosestPointTo(points[i], &uv.x, &uv.y)) m_points.Append(uv); } if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); } void ON_Leader::InsertPoint2d(int atidx, ON_2dPoint point) { m_points.Insert(atidx, point); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); } void ON_Leader::InsertPoint3d(int atidx, ON_3dPoint point) { ON_2dPoint p2; if(m_plane.ClosestPointTo(point, &p2.x, &p2.y)) m_points.Insert(atidx, p2); } void ON_Leader::RemovePoint(int idx) { m_points.Remove(idx); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); } void ON_Leader::AppendPoint2d(ON_2dPoint point) { m_points.Append(point); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); } bool ON_Leader::AppendPoint3d(ON_3dPoint point) { ON_2dPoint uv; if (m_plane.ClosestPointTo(point, &uv.x, &uv.y)) { m_points.Append(uv); if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); return true; } return false; } bool ON_Leader::SetPoint2d(int idx, ON_2dPoint point) { if (idx >= 0 && idx < m_points.Count()) { m_points[idx] = point; if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); return true; } return false; } bool ON_Leader::SetPoint3d(int idx, ON_3dPoint point) { if (idx >= 0 && idx < m_points.Count()) { ON_2dPoint uv; if (m_plane.ClosestPointTo(point, &uv.x, &uv.y)) { m_points[idx] = uv; if (nullptr != m_curve) { delete m_curve; m_curve = nullptr; } InvalidateTextPoint(); return true; } } return false; } bool ON_Leader::Point2d(int idx, ON_2dPoint& point) const { if (idx >= 0 && idx < m_points.Count()) { point = m_points[idx]; return true; } return false; } bool ON_Leader::Point3d(int idx, ON_3dPoint& point) const { if (idx >= 0 && idx < m_points.Count()) { point = m_plane.PointAt(m_points[idx].x, m_points[idx].y); return true; } return false; } ON__UINT32 ON_Leader::PointCount() const { return m_points.Count(); } bool ON_Leader::GetTextPoint2d( const ON_DimStyle* dimstyle, double leaderscale, ON_2dPoint& point) const { //if (m_text_point.IsUnsetPoint()) { if (!const_cast(this)->UpdateTextPosition(dimstyle, leaderscale)) return false; } point = m_text_point; return m_text_point.IsValid(); } //bool ON_Leader::GetTextPoint3d(ON_3dPoint& point) const //{ // //if (m_text_point.IsUnsetPoint()) // { // if (!const_cast(this)->UpdateTextPosition()) // return false; // } // point = m_plane.PointAt(m_text_point.x, m_text_point.y); // return point.IsValid(); //} // sets m_text_point and m_text_angle from m_points // m_text_point is the point near the end of the leader tail // where content attaches bool ON_Leader::UpdateTextPosition( const ON_DimStyle* dimstyle, double leaderscale) { if (nullptr == dimstyle) return false; ON_2dVector text_dir; ON_2dPoint attachpt(ON_2dPoint::Origin); ON_2dPoint from(ON_2dPoint::Origin); ON_Line ll; if (LandingLine2d(dimstyle, leaderscale, ll)) // from ponit is end of landing line { text_dir = ll.Direction(); if (text_dir.Unitize()) from = ll.to; } else // No landing line - from point is last leader point { text_dir = TailDirection(dimstyle); if (0 < m_points.Count()) from = m_points[m_points.Count() - 1]; } attachpt = from + (text_dir * dimstyle->TextGap() * leaderscale); if (attachpt.IsValid()) { if (attachpt != m_text_point) ClearBoundingBox(); m_text_point = attachpt; return true; } else return false; }