| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | #include <App/Document.h>
|
| | #include <Base/Console.h>
|
| | #include <Base/Parameter.h>
|
| | #include <Base/Tools.h>
|
| |
|
| | #include "DrawViewPart.h"
|
| | #include "DrawPage.h"
|
| | #include "DrawLeaderLine.h"
|
| | #include "DrawLeaderLinePy.h"
|
| | #include "ArrowPropEnum.h"
|
| | #include "DrawView.h"
|
| | #include "Preferences.h"
|
| | #include "DrawUtil.h"
|
| |
|
| |
|
| | using namespace TechDraw;
|
| | using DU = DrawUtil;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | PROPERTY_SOURCE(TechDraw::DrawLeaderLine, TechDraw::DrawView)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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));
|
| |
|
| | EndSymbol.setEnums(ArrowPropEnum::ArrowTypeEnums);
|
| | ADD_PROPERTY(EndSymbol, (NoEnd));
|
| |
|
| | 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.");
|
| |
|
| |
|
| | 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;
|
| | }
|
| |
|
| | const App::DocumentObject* docObj = getBaseObject();
|
| | if (docObj && docObj->isTouched()) {
|
| | return 1;
|
| | }
|
| |
|
| | if (WayPoints.isTouched()) {
|
| | return 1;
|
| | }
|
| |
|
| | return DrawView::mustExecute();
|
| | }
|
| |
|
| | App::DocumentObjectExecReturn *DrawLeaderLine::execute()
|
| | {
|
| |
|
| | 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
|
| | {
|
| |
|
| | DrawView* parent = getBaseView();
|
| | if (!parent) {
|
| | return 1.0;
|
| | }
|
| | return parent->getScale();
|
| | }
|
| |
|
| | double DrawLeaderLine::getScale() const
|
| | {
|
| |
|
| | if (!Scalable.getValue()) {
|
| | return 1.0;
|
| | }
|
| |
|
| | return getBaseScale();
|
| | }
|
| |
|
| | Base::Vector3d DrawLeaderLine::getAttachPoint()
|
| | {
|
| | return Base::Vector3d(
|
| | X.getValue(),
|
| | Y.getValue(),
|
| | 0.0
|
| | );
|
| | }
|
| |
|
| |
|
| |
|
| | 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;
|
| | }
|
| |
|
| |
|
| |
|
| | 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;
|
| | }
|
| |
|
| |
|
| |
|
| | 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();
|
| | }
|
| |
|
| |
|
| | Base::Vector3d DrawLeaderLine::getKinkPoint() const
|
| | {
|
| | std::vector<Base::Vector3d> wp = getTransformedWayPoints();
|
| | if (wp.size() > 1) {
|
| | return wp.rbegin()[1];
|
| | }
|
| |
|
| | return Base::Vector3d();
|
| | }
|
| |
|
| |
|
| | Base::Vector3d DrawLeaderLine::getTailPoint() const
|
| | {
|
| | std::vector<Base::Vector3d> wp = getTransformedWayPoints();
|
| | if (!wp.empty()) {
|
| | return wp.rbegin()[0];
|
| | }
|
| |
|
| | return Base::Vector3d();
|
| | }
|
| |
|
| |
|
| | Base::Vector3d DrawLeaderLine::lastSegmentDirection() const
|
| | {
|
| | std::vector<Base::Vector3d> wp = getTransformedWayPoints();
|
| | if (wp.empty()) {
|
| | return Base::Vector3d(1,0,0);
|
| | }
|
| |
|
| |
|
| | auto tailPoint = DU::invertY(wp.rbegin()[0]);
|
| | auto kinkPoint = DU::invertY(wp.rbegin()[1]);
|
| | auto direction = kinkPoint - tailPoint;
|
| | direction = DU::invertY(direction);
|
| | direction.Normalize();
|
| | return direction;
|
| | }
|
| |
|
| |
|
| |
|
| | 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 {};
|
| | }
|
| |
|
| |
|
| | 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());
|
| |
|
| |
|
| |
|
| | App::DocumentObject* obj = parent->getDocument()->getObject(leaderName.c_str());
|
| | if (!obj || !obj->isDerivedFrom<TechDraw::DrawLeaderLine>()) {
|
| | throw Base::RuntimeError("DrawLeaderLine::makeLeader - new object not found");
|
| | }
|
| |
|
| |
|
| | 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;
|
| | leaderFeature->setPosition(leaderPagePos.x, leaderPagePos.y, force);
|
| |
|
| |
|
| | std::vector<Base::Vector3d> pageDeltas;
|
| | for (auto& point : pagePoints) {
|
| | auto temp = point - pagePoints.front();
|
| | pageDeltas.emplace_back(temp);
|
| | }
|
| |
|
| |
|
| | auto leaderPoints = leaderFeature->makeCanonicalPoints(pageDeltas);
|
| |
|
| | 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;
|
| | }
|
| |
|
| |
|
| |
|
| | std::vector<Base::Vector3d> DrawLeaderLine::getScaledAndRotatedPoints(bool doScale, bool doRotate) const
|
| | {
|
| | auto dvp = getBaseView();
|
| | if (!dvp) {
|
| |
|
| |
|
| | 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) {
|
| |
|
| | newPoint.RotateZ(rotationRad);
|
| | }
|
| | result.push_back(DU::invertY(newPoint));
|
| | }
|
| |
|
| | return result;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | 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;
|
| | }
|
| |
|
| |
|
| | 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;
|
| | }
|
| |
|
| |
|
| | bool DrawLeaderLine::isParentReady() const
|
| | {
|
| | TechDraw::DrawView* parent = getBaseView();
|
| | auto dvp = dynamic_cast<TechDraw::DrawViewPart*>(parent);
|
| | if (!parent || (dvp && !dvp->hasGeometry())) {
|
| |
|
| |
|
| | 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())) {
|
| |
|
| | PythonObject = Py::Object(new DrawLeaderLinePy(this), true);
|
| | }
|
| | return Py::new_reference_to(PythonObject);
|
| | }
|
| |
|
| |
|
| |
|
| | namespace App {
|
| |
|
| | PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawLeaderLinePython, TechDraw::DrawLeaderLine)
|
| | template<> const char* TechDraw::DrawLeaderLinePython::getViewProviderName() const {
|
| | return "TechDrawGui::ViewProviderLeader";
|
| | }
|
| |
|
| |
|
| |
|
| | template class TechDrawExport FeaturePythonT<TechDraw::DrawLeaderLine>;
|
| | }
|
| |
|
| |
|