Download src/Mod/TechDraw/App/DrawLeaderLine.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DrawLeaderLine.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/TechDraw/App/DrawLeaderLine.cpp
-
curl -L -o DrawLeaderLine.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/TechDraw/App/DrawLeaderLine.cpp
18.5 kB
| /*************************************************************************** | |
| * Copyright (c) 2019 WandererFan <wandererfan@gmail.com> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library is distributed in the hope that it will be useful, * | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| * GNU Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| //! DrawLeaderLine - a class for storing leader line attributes and providing methods to apply transformations on leader geometry. | |
| //! Waypoints are to be stored as displacements from the first Waypoint in printed page coordinates (mm, conventional | |
| //! X and Y axes, (0, 0) at lower left). The first Waypoint is set to (0,0) after the displacements are calculated. | |
| //! The leader's X,Y position is relative to the parent's origin. The X,Y position is unrotated and unscaled. | |
| using namespace TechDraw; | |
| using DU = DrawUtil; | |
| //=========================================================================== | |
| // DrawLeaderLine - Base class for drawing leader based features | |
| //=========================================================================== | |
| PROPERTY_SOURCE(TechDraw::DrawLeaderLine, TechDraw::DrawView) | |
| //TODO: share this between DrawViewBalloon, DrawLeaderLine, QGIArrow, Prefs, etc | |
| //const char* DrawLeaderLine::ArrowTypeEnums[]= { | |
| // "FILLED_ARROW", | |
| // "OPEN_ARROW", | |
| // "TICK", | |
| // "DOT", | |
| // "OPEN_CIRCLE", | |
| // "FORK", | |
| // "FILLED_TRIANGLE", | |
| // "NONE" | |
| // NULL}; | |
| //const char* DrawLeaderLine::ArrowTypeEnums2[]= { | |
| // "FILLED_ARROW", | |
| // "OPEN_ARROW", | |
| // "TICK", | |
| // "DOT", | |
| // "OPEN_CIRCLE", | |
| // "FORK", | |
| // "FILLED_TRIANGLE", | |
| // "NONE" | |
| // NULL}; | |
| DrawLeaderLine::DrawLeaderLine() | |
| { | |
| static const char *group = "Leader"; | |
| constexpr long int FilledArrow{0l}; | |
| constexpr long int NoEnd{7l}; | |
| ADD_PROPERTY_TYPE(LeaderParent, (nullptr), group, (App::PropertyType)(App::Prop_None), | |
| "View to which this leader is attached"); | |
| LeaderParent.setScope(App::LinkScope::Global); | |
| ADD_PROPERTY_TYPE(WayPoints, (Base::Vector3d()) ,group, App::Prop_None, | |
| "Intermediate points for Leader line"); | |
| StartSymbol.setEnums(ArrowPropEnum::ArrowTypeEnums); | |
| ADD_PROPERTY(StartSymbol, (FilledArrow)); //filled arrow | |
| EndSymbol.setEnums(ArrowPropEnum::ArrowTypeEnums); | |
| ADD_PROPERTY(EndSymbol, (NoEnd)); //no symbol | |
| ADD_PROPERTY_TYPE(Scalable ,(false), group, App::Prop_None, "Scale line with LeaderParent"); | |
| ADD_PROPERTY_TYPE(AutoHorizontal ,(getDefAuto()), group, App::Prop_None, "Forces last line segment to be horizontal"); | |
| ADD_PROPERTY_TYPE(RotatesWithParent ,(true), group, App::Prop_None, | |
| "If true, leader rotates around parent. If false, only first segment of leader changes with parent rotation."); | |
| //hide the DrawView properties that don't apply to Leader | |
| ScaleType.setStatus(App::Property::ReadOnly, true); | |
| ScaleType.setStatus(App::Property::Hidden, true); | |
| Scale.setStatus(App::Property::ReadOnly, true); | |
| Scale.setStatus(App::Property::Hidden, true); | |
| Rotation.setStatus(App::Property::ReadOnly, true); | |
| Rotation.setStatus(App::Property::Hidden, true); | |
| Caption.setStatus(App::Property::Hidden, true); | |
| LockPosition.setValue(true); | |
| LockPosition.setStatus(App::Property::Hidden, true); | |
| } | |
| short DrawLeaderLine::mustExecute() const | |
| { | |
| if (!isRestoring() && LeaderParent.isTouched()) { | |
| return 1; // Property changed | |
| } | |
| const App::DocumentObject* docObj = getBaseObject(); | |
| if (docObj && docObj->isTouched()) { | |
| return 1; // Object property points to is touched | |
| } | |
| if (WayPoints.isTouched()) { | |
| return 1; | |
| } | |
| return DrawView::mustExecute(); | |
| } | |
| App::DocumentObjectExecReturn *DrawLeaderLine::execute() | |
| { | |
| // Base::Console().message("DLL::execute()\n"); | |
| if (!keepUpdated()) { | |
| return App::DocumentObject::StdReturn; | |
| } | |
| overrideKeepUpdated(false); | |
| return DrawView::execute(); | |
| } | |
| DrawView* DrawLeaderLine::getBaseView() const | |
| { | |
| App::DocumentObject* baseObj = LeaderParent.getValue(); | |
| if (!baseObj) { | |
| return nullptr; | |
| } | |
| auto cast = freecad_cast<DrawView*>(baseObj); | |
| return cast; | |
| } | |
| App::DocumentObject* DrawLeaderLine::getBaseObject() const | |
| { | |
| return getBaseView(); | |
| } | |
| bool DrawLeaderLine::keepUpdated() | |
| { | |
| DrawView* view = getBaseView(); | |
| if (!view) { | |
| return false; | |
| } | |
| return view->keepUpdated(); | |
| } | |
| double DrawLeaderLine::getBaseScale() const | |
| { | |
| // Base::Console().message("DLL::getBaseScale()\n"); | |
| DrawView* parent = getBaseView(); | |
| if (!parent) { | |
| return 1.0; | |
| } | |
| return parent->getScale(); | |
| } | |
| double DrawLeaderLine::getScale() const | |
| { | |
| // Base::Console().message("DLL::getScale()\n"); | |
| if (!Scalable.getValue()) { | |
| return 1.0; | |
| } | |
| return getBaseScale(); | |
| } | |
| Base::Vector3d DrawLeaderLine::getAttachPoint() | |
| { | |
| return Base::Vector3d( | |
| X.getValue(), | |
| Y.getValue(), | |
| 0.0 | |
| ); | |
| } | |
| //! unit agnostic conversion of last segment to horizontal. need to do this at drawing time otherwise | |
| //! we just realign the canonical form. | |
| std::vector<Base::Vector3d> DrawLeaderLine::horizLastSegment(const std::vector<Base::Vector3d>& inDeltas, double rotationDeg) | |
| { | |
| Base::Vector3d stdX{1, 0, 0}; | |
| std::vector<Base::Vector3d> wp = inDeltas; | |
| if (wp.size() > 1) { | |
| size_t iLast = wp.size() - 1; | |
| size_t iPen = wp.size() - 2; | |
| Base::Vector3d last = wp.at(iLast); | |
| Base::Vector3d penUlt = wp.at(iPen); | |
| auto lastSeg = DU::invertY(last - penUlt); | |
| auto lastSegLong = lastSeg.Length(); | |
| auto lastSegRotated = lastSeg; | |
| lastSegRotated.RotateZ(Base::toRadians(rotationDeg)); | |
| auto lastSegRotatedUnit = lastSegRotated; | |
| lastSegRotatedUnit.Normalize(); | |
| auto dot = lastSegRotatedUnit.Dot(stdX); | |
| auto newLast = penUlt + stdX * lastSegLong; | |
| if (dot < 0) { | |
| newLast = penUlt - stdX * lastSegLong; | |
| } | |
| wp.at(iLast) = newLast; | |
| } | |
| return wp; | |
| } | |
| //! returns the waypoints with scale, rotation and horizontal last applied. | |
| std::vector<Base::Vector3d> DrawLeaderLine::getTransformedWayPoints() const | |
| { | |
| auto doScale = Scalable.getValue(); | |
| auto doRotate = RotatesWithParent.getValue(); | |
| auto vPoints = getScaledAndRotatedPoints(doScale, doRotate); | |
| if (AutoHorizontal.getValue()) { | |
| vPoints = DrawLeaderLine::horizLastSegment(vPoints, getBaseView()->Rotation.getValue()); | |
| } | |
| return vPoints; | |
| } | |
| //! returns the mid point of last segment. used by leader decorators like weld symbol. | |
| //! the returned point is unscaled and unrotated. | |
| Base::Vector3d DrawLeaderLine::getTileOrigin() const | |
| { | |
| std::vector<Base::Vector3d> wp = getTransformedWayPoints(); | |
| if (wp.size() > 1) { | |
| Base::Vector3d last = wp.rbegin()[0]; | |
| Base::Vector3d second = wp.rbegin()[1]; | |
| return (last + second) / 2; | |
| } | |
| return Base::Vector3d(); | |
| } | |
| //! returns start of last line segment | |
| Base::Vector3d DrawLeaderLine::getKinkPoint() const | |
| { | |
| std::vector<Base::Vector3d> wp = getTransformedWayPoints(); | |
| if (wp.size() > 1) { | |
| return wp.rbegin()[1]; // second point from end | |
| } | |
| return Base::Vector3d(); | |
| } | |
| //end of last line segment | |
| Base::Vector3d DrawLeaderLine::getTailPoint() const | |
| { | |
| std::vector<Base::Vector3d> wp = getTransformedWayPoints(); | |
| if (!wp.empty()) { | |
| return wp.rbegin()[0]; // Last | |
| } | |
| return Base::Vector3d(); | |
| } | |
| //! returns the (transformed) direction of the last segment of the leader line | |
| Base::Vector3d DrawLeaderLine::lastSegmentDirection() const | |
| { | |
| std::vector<Base::Vector3d> wp = getTransformedWayPoints(); | |
| if (wp.empty()) { | |
| return Base::Vector3d(1,0,0); | |
| } | |
| // this direction is in conventional coords? Y+ up? | |
| // vertical segment will be small negative Y - large negative Y -> a positive Y but we want a negative Y | |
| auto tailPoint = DU::invertY(wp.rbegin()[0]); | |
| auto kinkPoint = DU::invertY(wp.rbegin()[1]); | |
| auto direction = kinkPoint - tailPoint; // from kink to tail | |
| direction = DU::invertY(direction); | |
| direction.Normalize(); | |
| return direction; | |
| } | |
| //! create a new leader feature from parameters. Used by python method makeLeader. | |
| //! pagePoints are in mm from bottom left of page. | |
| DrawLeaderLine* DrawLeaderLine::makeLeader(DrawViewPart* parent, std::vector<Base::Vector3d> pagePoints, int iStartSymbol, int iEndSymbol) | |
| { | |
| Base::Console().message("DLL::makeLeader(%s, %d, %d, %d)\n", parent->getNameInDocument(), pagePoints.size(), iStartSymbol, iEndSymbol); | |
| if (pagePoints.size() < 2) { | |
| Base::Console().message("DLL::makeLeader - not enough pagePoints\n"); | |
| return {}; | |
| } | |
| // this is +/- the same code as in TaskLeaderLine::createLeaderFeature() | |
| const std::string objectName{"LeaderLine"}; | |
| const std::string leaderType = "TechDraw::DrawLeaderLine"; | |
| std::string leaderName = parent->getDocument()->getUniqueObjectName(objectName.c_str()); | |
| std::string pageName = parent->findParentPage()->getNameInDocument(); | |
| std::string parentName = parent->getNameInDocument(); | |
| Base::Interpreter().runStringArg("App.activeDocument().addObject('%s', '%s')", | |
| leaderType.c_str(), leaderName.c_str()); | |
| Base::Interpreter().runStringArg("App.activeDocument().%s.translateLabel('DrawLeaderLine', 'LeaderLine', '%s')", | |
| leaderName.c_str(), leaderName.c_str()); | |
| Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)", | |
| pageName.c_str(), leaderName.c_str()); | |
| Base::Interpreter().runStringArg("App.activeDocument().%s.LeaderParent = App.activeDocument().%s", | |
| leaderName.c_str(), parentName.c_str()); | |
| // we assume here that the caller will handle AutoHorizontal, Scalable and Rotatable as required | |
| App::DocumentObject* obj = parent->getDocument()->getObject(leaderName.c_str()); | |
| if (!obj || !obj->isDerivedFrom<TechDraw::DrawLeaderLine>()) { | |
| throw Base::RuntimeError("DrawLeaderLine::makeLeader - new object not found"); | |
| } | |
| // set leader x,y position | |
| auto leaderFeature = static_cast<TechDraw::DrawLeaderLine*>(obj); | |
| Base::Vector3d parentPagePos{ parent->X.getValue(), parent->Y.getValue(), 0.0}; | |
| Base::Vector3d leaderPagePos = pagePoints.front() - parentPagePos; | |
| bool force = true; // update position even though leaders default to locked. | |
| leaderFeature->setPosition(leaderPagePos.x, leaderPagePos.y, force); | |
| // page positions to deltas | |
| std::vector<Base::Vector3d> pageDeltas; | |
| for (auto& point : pagePoints) { | |
| auto temp = point - pagePoints.front(); | |
| pageDeltas.emplace_back(temp); | |
| } | |
| // deltas to unscaled, unrotated form | |
| auto leaderPoints = leaderFeature->makeCanonicalPoints(pageDeltas); | |
| // invert the canonical points | |
| std::vector<Base::Vector3d> inverted; | |
| inverted.reserve(leaderPoints.size()); | |
| for (auto& point : leaderPoints) { | |
| inverted.push_back(DU::invertY(point)); | |
| } | |
| leaderFeature->WayPoints.setValues(inverted); | |
| leaderFeature->StartSymbol.setValue(iStartSymbol); | |
| leaderFeature->EndSymbol.setValue(iEndSymbol); | |
| parent->touch(); | |
| return leaderFeature; | |
| } | |
| //! return scaled and rotated copies of the WayPoints (page position deltas from 1st point) | |
| //! used by QGILL. | |
| std::vector<Base::Vector3d> DrawLeaderLine::getScaledAndRotatedPoints(bool doScale, bool doRotate) const | |
| { | |
| auto dvp = getBaseView(); | |
| if (!dvp) { | |
| // document is restoring? | |
| // Base::Console().message("DLL::getScaledAndRotatedPoints - no DV\n"); | |
| return {}; | |
| } | |
| double scale{1.0}; | |
| if (Scalable.getValue() && doScale) { | |
| scale = dvp->getScale(); | |
| } | |
| double rotationRad{0.0}; | |
| if (doRotate) { | |
| rotationRad = dvp->Rotation.getValue() * std::numbers::pi / DegreesHalfCircle; | |
| } | |
| std::vector<Base::Vector3d> pointsAll = WayPoints.getValues(); | |
| std::vector<Base::Vector3d> result; | |
| for (auto& point : pointsAll) { | |
| Base::Vector3d newPoint = DU::invertY(point * scale); | |
| if (rotationRad != 0.0) { | |
| // the waypoints use conventional coords | |
| newPoint.RotateZ(rotationRad); | |
| } | |
| result.push_back(DU::invertY(newPoint)); | |
| } | |
| return result; | |
| } | |
| //! return unscaled and unrotated versions of the input points. input is expected to in mm (Rez::appX()), | |
| //! and conventional Y axis (+ up) | |
| //! used by QGILL. | |
| std::vector<Base::Vector3d> | |
| DrawLeaderLine::makeCanonicalPoints(const std::vector<Base::Vector3d>& inPoints, | |
| bool doScale, | |
| bool doRotate) const | |
| { | |
| auto dvp = getBaseView(); | |
| double scale{1.0}; | |
| if (Scalable.getValue() && doScale) { | |
| scale = dvp->getScale(); | |
| } | |
| double rotationRad{0.0}; | |
| if (doRotate) { | |
| rotationRad = - dvp->Rotation.getValue() * std::numbers::pi / DegreesHalfCircle; | |
| } | |
| std::vector<Base::Vector3d> result; | |
| for (auto& point : inPoints) { | |
| Base::Vector3d newPoint = point / scale; | |
| if (rotationRad != 0.0) { | |
| newPoint.RotateZ(rotationRad); | |
| } | |
| result.push_back(newPoint); | |
| } | |
| return result; | |
| } | |
| //! as makeCanonicalPoints, but accepts and returns inverted points | |
| std::vector<Base::Vector3d> | |
| DrawLeaderLine::makeCanonicalPointsInverted(const std::vector<Base::Vector3d>& inPoints, | |
| bool doScale, | |
| bool doRotate) const | |
| { | |
| std::vector<Base::Vector3d> conventionalPoints; | |
| conventionalPoints.reserve(inPoints.size()); | |
| for (auto& point : inPoints) { | |
| conventionalPoints.push_back(DU::invertY(point)); | |
| } | |
| auto conventionalCanon = makeCanonicalPoints(conventionalPoints, doScale, doRotate); | |
| std::vector<Base::Vector3d> invertedPoints; | |
| invertedPoints.reserve(inPoints.size()); | |
| for (auto& point : conventionalCanon) { | |
| invertedPoints.push_back(DU::invertY(point)); | |
| } | |
| return invertedPoints; | |
| } | |
| //! returns true if parent exists. if parent is a DVP it must have geometry. | |
| bool DrawLeaderLine::isParentReady() const | |
| { | |
| TechDraw::DrawView* parent = getBaseView(); | |
| auto dvp = dynamic_cast<TechDraw::DrawViewPart*>(parent); | |
| if (!parent || (dvp && !dvp->hasGeometry())) { | |
| // still restoring or | |
| // we are attached to a dvp that has no geometry, so don't bother trying to draw yet | |
| Base::Console().message("DLL:: - no parent or geometry\n"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool DrawLeaderLine::getDefAuto() const | |
| { | |
| return Preferences::getPreferenceGroup("LeaderLine")->GetBool("AutoHorizontal", true); | |
| } | |
| void DrawLeaderLine::dumpWaypoints(const std::vector<Base::Vector3d> &points, const std::string &label) | |
| { | |
| Base::Console().message("DLL::dumpWaypoints - %s\n", label.c_str()); | |
| for (auto& p : points) { | |
| Base::Console().message(">>>> a point: %s\n", DU::formatVector(p).c_str()); | |
| } | |
| } | |
| PyObject *DrawLeaderLine::getPyObject() | |
| { | |
| if (PythonObject.is(Py::_None())) { | |
| // ref counter is set to 1 | |
| PythonObject = Py::Object(new DrawLeaderLinePy(this), true); | |
| } | |
| return Py::new_reference_to(PythonObject); | |
| } | |
| // Python Drawing feature --------------------------------------------------------- | |
| namespace App { | |
| /// @cond DOXERR | |
| PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawLeaderLinePython, TechDraw::DrawLeaderLine) | |
| template<> const char* TechDraw::DrawLeaderLinePython::getViewProviderName() const { | |
| return "TechDrawGui::ViewProviderLeader"; | |
| } | |
| /// @endcond | |
| // explicit template instantiation | |
| template class TechDrawExport FeaturePythonT<TechDraw::DrawLeaderLine>; | |
| } | |