From a7d83b5e15a6641178682fa8d629d35d4f9c86cf Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Tue, 9 Feb 2021 23:17:40 +0100 Subject: [PATCH] Sketcher - Add new Split Edge action --- src/Mod/Part/App/Geometry.cpp | 2 +- src/Mod/Part/App/Geometry.h | 2 +- src/Mod/Sketcher/App/SketchObject.cpp | 356 +++++++++- src/Mod/Sketcher/App/SketchObject.h | 12 +- src/Mod/Sketcher/App/SketchObjectPy.xml | 5 + src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 21 +- src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 120 ++++ src/Mod/Sketcher/Gui/Resources/Sketcher.qrc | 2 + .../icons/geometry/Sketcher_Split.svg | 671 ++++++++++++++++++ .../pointers/Sketcher_Pointer_Splitting.svg | 98 +++ src/Mod/Sketcher/Gui/Workbench.cpp | 1 + 11 files changed, 1276 insertions(+), 14 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_Split.svg create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Splitting.svg diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp index e2dc02e7bb..e6d6396ca9 100644 --- a/src/Mod/Part/App/Geometry.cpp +++ b/src/Mod/Part/App/Geometry.cpp @@ -684,7 +684,7 @@ bool GeomCurve::normalAt(double u, Base::Vector3d& dir) const return false; } -bool GeomCurve::intersect( GeomCurve * c, +bool GeomCurve::intersect( const GeomCurve *c, std::vector>& points, double tol) const { diff --git a/src/Mod/Part/App/Geometry.h b/src/Mod/Part/App/Geometry.h index fea71fbbbc..4cbd45c540 100644 --- a/src/Mod/Part/App/Geometry.h +++ b/src/Mod/Part/App/Geometry.h @@ -197,7 +197,7 @@ public: double curvatureAt(double u) const; double length(double u, double v) const; bool normalAt(double u, Base::Vector3d& dir) const; - bool intersect(GeomCurve * c, + bool intersect(const GeomCurve *c, std::vector>& points, double tol = Precision::Confusion()) const; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 9fc6e73921..e671585ecf 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -1647,7 +1647,7 @@ void SketchObject::transferFilletConstraints(int geoId1, PointPos posId1, int ge this->Constraints.setValues(std::move(newConstraints)); } -int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId) +int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId, bool tangencyHolds) { Base::StateLocker lock(managedoperation, true); // no need to check input data validity as this is an sketchobject managed operation. @@ -1659,15 +1659,17 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId) && !(toGeoId < 0 && vals[i]->Second <0) ) { - // Nothing guarantees that a tangent can be freely transferred to another coincident point, as - // the transfer destination edge most likely won't be intended to be tangent. However, if it is - // an end to end point tangency, the user expects it to be substituted by a coincidence constraint. std::unique_ptr constNew(newVals[i]->clone()); constNew->First = toGeoId; constNew->FirstPos = toPosId; - if(vals[i]->Type == Sketcher::Tangent || vals[i]->Type == Sketcher::Perpendicular){ - constNew->Type = Sketcher::Coincident; + // If not explicitly confirmed, nothing guarantees that a tangent can be freely transferred to another coincident + // point, as the transfer destination edge most likely won't be intended to be tangent. However, if it is + // an end to end point tangency, the user expects it to be substituted by a coincidence constraint. + if (vals[i]->Type == Sketcher::Tangent || vals[i]->Type == Sketcher::Perpendicular) { + if (!tangencyHolds) { + constNew->Type = Sketcher::Coincident; + } } // With respect to angle constraints, if it is a DeepSOIC style angle constraint (segment+segment+point), // then no problem arises as the segments are PosId=none. In this case there is no call to this function. @@ -1690,11 +1692,13 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG std::unique_ptr constNew(newVals[i]->clone()); constNew->Second = toGeoId; constNew->SecondPos = toPosId; - // Nothing guarantees that a tangent can be freely transferred to another coincident point, as - // the transfer destination edge most likely won't be intended to be tangent. However, if it is + // If not explicitly confirmed, nothing guarantees that a tangent can be freely transferred to another coincident + // point, as the transfer destination edge most likely won't be intended to be tangent. However, if it is // an end to end point tangency, the user expects it to be substituted by a coincidence constraint. - if(vals[i]->Type == Sketcher::Tangent || vals[i]->Type == Sketcher::Perpendicular) { - constNew->Type = Sketcher::Coincident; + if (vals[i]->Type == Sketcher::Tangent || vals[i]->Type == Sketcher::Perpendicular) { + if (!tangencyHolds) { + constNew->Type = Sketcher::Coincident; + } } else if(vals[i]->Type == Sketcher::Angle) { continue; @@ -1713,6 +1717,19 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG return 0; } +void SketchObject::swapInvolvedGeometry(Constraint *constraint, int fromGeoId, int toGeoId) +{ + if (constraint->First == fromGeoId) { + constraint->First = toGeoId; + } + if (constraint->Second == fromGeoId) { + constraint->Second = toGeoId; + } + if (constraint->Third == fromGeoId) { + constraint->Third = toGeoId; + } +} + int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bool createCorner) { if (GeoId < 0 || GeoId > getHighestCurveIndex()) @@ -2964,6 +2981,312 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) return -1; } +int SketchObject::split(int GeoId, const Base::Vector3d &point) +{ + // No need to check input data validity as this is an sketchobject managed operation + + Base::StateLocker lock(managedoperation, true); + + if (GeoId < 0 || GeoId > getHighestCurveIndex()) { + return -1; + } + + const Part::Geometry *geo = getGeometry(GeoId); + std::vector newGeometries; + std::vector newIds; + std::vector newConstraints; + bool ok = false; + + Base::Vector3d startPoint, endPoint, splitPoint; + double radius, startAngle, endAngle, splitAngle; + unsigned int longestPart = 0; + + do { + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSegm = static_cast(geo); + + startPoint = lineSegm->getStartPoint(); + endPoint = lineSegm->getEndPoint(); + splitPoint = point.Perpendicular(startPoint, endPoint - startPoint); + if ((endPoint - splitPoint).Length() > (splitPoint - startPoint).Length()) { + longestPart = 1; + } + + Part::GeomLineSegment *newLine = static_cast(lineSegm->copy()); + newGeometries.push_back(newLine); + + newLine->setPoints(startPoint, splitPoint); + int newId = addGeometry(newLine); + if (newId < 0) { + continue; + } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(geo)); + + newLine = static_cast(lineSegm->copy()); + newGeometries.push_back(newLine); + + newLine->setPoints(splitPoint, endPoint); + newId = addGeometry(newLine); + if (newId < 0) { + continue; + } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(geo)); + + Constraint *joint = new Constraint(); + joint->Type = Coincident; + joint->First = newIds[0]; + joint->FirstPos = end; + joint->Second = newIds[1]; + joint->SecondPos = start; + newConstraints.push_back(joint); + + transferConstraints(GeoId, start, newIds[0], start, true); + transferConstraints(GeoId, end, newIds[1], end, true); + ok = true; + } + else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo); + + Base::Vector3d center(circle->getLocation()); + Base::Vector3d dir(point - center); + radius = circle->getRadius(); + + splitAngle = atan2(dir.y, dir.x); + startAngle = splitAngle; + endAngle = splitAngle + M_PI*2.0; + + splitPoint = Base::Vector3d(center.x + radius*cos(splitAngle), center.y + radius*sin(splitAngle)); + startPoint = splitPoint; + endPoint = splitPoint; + + Part::GeomArcOfCircle *arc = new Part::GeomArcOfCircle(); + newGeometries.push_back(arc); + + arc->setLocation(center); + arc->setRadius(radius); + arc->setRange(startAngle, endAngle, false); + int arcId = addGeometry(arc); + if (arcId < 0) { + continue; + } + newIds.push_back(arcId); + setConstruction(arcId, GeometryFacade::getConstruction(geo)); + + transferConstraints(GeoId, mid, arcId, mid); + ok = true; + } + else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo); + + startPoint = arc->getStartPoint(); + endPoint = arc->getEndPoint(); + + Base::Vector3d center(arc->getLocation()); + radius = arc->getRadius(); + arc->getRange(startAngle, endAngle, false); + + Base::Vector3d dir(point - center); + splitAngle = atan2(dir.y, dir.x); + if (splitAngle < startAngle) { + splitAngle += M_PI*2.0; + } + if (endAngle - splitAngle > splitAngle - startAngle) { + longestPart = 1; + } + + splitPoint = Base::Vector3d(center.x + radius*cos(splitAngle), center.y + radius*sin(splitAngle)); + startPoint = splitPoint; + endPoint = splitPoint; + + Part::GeomArcOfCircle *newArc = static_cast(arc->copy()); + newGeometries.push_back(newArc); + + newArc->setRange(startAngle, splitAngle, false); + int newId = addGeometry(newArc); + if (newId < 0) { + continue; + } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(geo)); + + newArc = static_cast(arc->copy()); + newGeometries.push_back(newArc); + + newArc->setRange(splitAngle, endAngle, false); + newId = addGeometry(newArc); + if (newId < 0) { + continue; + } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(geo)); + + Constraint *joint = new Constraint(); + joint->Type = Coincident; + joint->First = newIds[0]; + joint->FirstPos = end; + joint->Second = newIds[1]; + joint->SecondPos = start; + newConstraints.push_back(joint); + + joint = new Constraint(); + joint->Type = Coincident; + joint->First = newIds[0]; + joint->FirstPos = mid; + joint->Second = newIds[1]; + joint->SecondPos = mid; + newConstraints.push_back(joint); + + transferConstraints(GeoId, start, newIds[0], start, true); + transferConstraints(GeoId, mid, newIds[0], mid); + transferConstraints(GeoId, end, newIds[1], end, true); + ok = true; + } + } + while (false); + + if (ok) { + std::vector oldConstraints; + getAppliedConstraints(GeoId, oldConstraints); + + for (unsigned int i = 0; i < oldConstraints.size(); ++i) { + + Constraint *con = this->Constraints.getValues()[oldConstraints[i]]; + int conId = con->First; + PointPos conPos = con->FirstPos; + if (conId == GeoId) { + conId = con->Second; + conPos = con->SecondPos; + } + + bool transferToAll = false; + switch (con->Type) { + case Horizontal: + case Vertical: + case Parallel: { + transferToAll = geo->getTypeId() == Part::GeomLineSegment::getClassTypeId(); + break; + } + case Tangent: + case Perpendicular: { + unsigned int initial = 0; + unsigned int limit = newIds.size(); + + if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::Geometry *conGeo = getGeometry(conId); + if (conGeo && conGeo->isDerivedFrom(Part::GeomCurve::getClassTypeId())) { + std::vector> intersections; + bool intersects[2]; + + intersects[0] = static_cast(newGeometries[0])-> + intersect(static_cast(conGeo), intersections); + intersects[1] = static_cast(newGeometries[1])-> + intersect(static_cast(conGeo), intersections); + + initial = longestPart; + if (intersects[0] != intersects[1]) { + initial = intersects[1] ? 1 : 0; + } + limit = initial + 1; + } + } + + for (unsigned int i = initial; i < limit; ++i) { + Constraint *trans = con->copy(); + swapInvolvedGeometry(trans, GeoId, newIds[i]); + newConstraints.push_back(trans); + } + break; + } + case Distance: + case DistanceX: + case DistanceY: + case PointOnObject: { + if (con->FirstPos == none && con->SecondPos == none) { + Constraint *dist = con->copy(); + dist->First = newIds[0]; + dist->FirstPos = start; + dist->Second = newIds[1]; + dist->SecondPos = end; + newConstraints.push_back(dist); + } + else { + Constraint *trans = con->copy(); + trans->First = conId; + trans->FirstPos = conPos; + trans->SecondPos = none; + + Base::Vector3d conPoint(getPoint(conId, conPos)); + int targetId = newIds[0]; + + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + Base::Vector3d projPoint(conPoint.Perpendicular(startPoint, endPoint - startPoint)); + Base::Vector3d splitDir = splitPoint - startPoint; + if ((projPoint - startPoint)*splitDir > splitDir*splitDir) { + targetId = newIds[1]; + } + } + else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + Base::Vector3d conDir(conPoint - static_cast(geo)->getLocation()); + double conAngle = atan2(conDir.y, conDir.x); + if (conAngle < startAngle) { + conAngle += M_PI*2.0; + } + if (conAngle > splitAngle) { + targetId = newIds[1]; + } + } + trans->Second = targetId; + + newConstraints.push_back(trans); + } + break; + } + case Radius: + case Diameter: + case Equal: { + transferToAll = geo->getTypeId() == Part::GeomCircle::getClassTypeId() + || geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId(); + break; + } + default: + // Release other constraints + break; + } + + if (transferToAll) { + for (unsigned int i = 0; i < newIds.size(); ++i) { + Constraint *trans = con->copy(); + swapInvolvedGeometry(trans, GeoId, newIds[i]); + newConstraints.push_back(trans); + } + } + } + + if (noRecomputes) { + solve(); + } + + delConstraints(oldConstraints); + addConstraints(newConstraints); + } + + for (std::vector::iterator it = newGeometries.begin(); it != newGeometries.end(); ++it) { + delete *it; + } + for (std::vector::iterator it = newConstraints.begin(); it != newConstraints.end(); ++it) { + delete *it; + } + + if (ok) { + delGeometry(GeoId); + return 0; + } + + return -1; +} + bool SketchObject::isExternalAllowed(App::Document *pDoc, App::DocumentObject *pObj, eReasonList* rsn) const { if (rsn) @@ -6826,6 +7149,19 @@ bool SketchObject::arePointsCoincident(int GeoId1, PointPos PosId1, return false; } +void SketchObject::getAppliedConstraints(int GeoId, std::vector &constraintList) +{ + const std::vector &constraints = this->Constraints.getValues(); + int i = 0; + + for (std::vector::const_iterator it = constraints.begin(); it != constraints.end(); ++it) { + if ((*it)->First == GeoId || (*it)->Second == GeoId || (*it)->Third == GeoId) { + constraintList.push_back(i); + } + ++i; + } +} + void SketchObject::appendConflictMsg(const std::vector &conflicting, std::string &msg) { appendConstraintsMsg(conflicting, diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 462692f2df..2d1def3f9f 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -157,7 +157,12 @@ public: /// Deletes all constraints referencing an external geometry int delConstraintsToExternal(); /// transfers all constraints of a point to a new point - int transferConstraints(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId); + int transferConstraints(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId, + bool tangencyHolds = false); + + /// swaps original GeoId for a new one + void swapInvolvedGeometry(Constraint *constraint, int fromGeoId, int toGeoId); + /// Carbon copy another sketch geometry and constraints int carbonCopy(App::DocumentObject * pObj, bool construction = true); /// add an external geometry reference @@ -282,6 +287,8 @@ public: int trim(int geoId, const Base::Vector3d& point); /// extend a curve int extend(int geoId, double increment, int endPoint); + /// split a curve + int split(int geoId, const Base::Vector3d &point); /// adds symmetric geometric elements with respect to the refGeoId (line or point) int addSymmetric(const std::vector &geoIdList, int refGeoId, Sketcher::PointPos refPosId=Sketcher::none); @@ -358,6 +365,9 @@ public: void getDirectlyCoincidentPoints(int VertexId, std::vector &GeoIdList, std::vector &PosIdList); bool arePointsCoincident(int GeoId1, PointPos PosId1, int GeoId2, PointPos PosId2); + /// fetches all constraints involving given GeoId + void getAppliedConstraints(int GeoId, std::vector &constraintList); + /// generates a warning message about constraint conflicts and appends it to the given message static void appendConflictMsg(const std::vector &conflicting, std::string &msg); /// generates a warning message about redundant constraints and appends it to the given message diff --git a/src/Mod/Sketcher/App/SketchObjectPy.xml b/src/Mod/Sketcher/App/SketchObjectPy.xml index 11f340481c..20f993e6a4 100644 --- a/src/Mod/Sketcher/App/SketchObjectPy.xml +++ b/src/Mod/Sketcher/App/SketchObjectPy.xml @@ -217,6 +217,11 @@ If there is no such constraint an exception is raised. extend a curve to new start and end positions + + + split a curve with a given id at a given reference point + + add a symmetric geometric objects to the sketch with respect to a reference point or line diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 4600c9e248..1162db047e 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -570,7 +570,7 @@ PyObject* SketchObjectPy::delConstraintOnPoint(PyObject *args) if (!PyArg_ParseTuple(args, "i|i", &Index, &pos)) return 0; - if (pos>=0 && pos<3) { // Sketcher::none Sketcher::mid + if (pos>=0 && pos<=3) { // Sketcher::none Sketcher::mid if (this->getSketchObjectPtr()->delConstraintOnPoint(Index,(Sketcher::PointPos)pos)) { std::stringstream str; str << "Not able to delete a constraint on point with the given index: " << Index @@ -1120,6 +1120,25 @@ PyObject* SketchObjectPy::extend(PyObject *args) return 0; } +PyObject* SketchObjectPy::split(PyObject *args) +{ + PyObject *pcObj; + int GeoId; + + if (!PyArg_ParseTuple(args, "iO!", &GeoId, &(Base::VectorPy::Type), &pcObj)) + return 0; + + Base::Vector3d v1 = static_cast(pcObj)->value(); + if (this->getSketchObjectPtr()->split(GeoId,v1)) { + std::stringstream str; + str << "Not able to split curve with the given index: " << GeoId; + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return 0; + } + + Py_Return; +} + PyObject* SketchObjectPy::addSymmetric(PyObject *args) { PyObject *pcObj; diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 25a6be8ae6..e2f28175b1 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -5904,6 +5904,125 @@ bool CmdSketcherExtend::isActive(void) } +// ====================================================================================== + +namespace SketcherGui { + class SplittingSelection : public Gui::SelectionFilterGate + { + App::DocumentObject* object; + public: + SplittingSelection(App::DocumentObject* obj) + : Gui::SelectionFilterGate((Gui::SelectionFilter*)0), object(obj) + {} + + bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName) + { + if (pObj != this->object) + return false; + if (!sSubName || sSubName[0] == '\0') + return false; + std::string element(sSubName); + if (element.substr(0,4) == "Edge") { + int GeoId = std::atoi(element.substr(4,4000).c_str()) - 1; + Sketcher::SketchObject *Sketch = static_cast(object); + const Part::Geometry *geom = Sketch->getGeometry(GeoId); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() + || geom->getTypeId() == Part::GeomCircle::getClassTypeId() + || geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + return true; + } + } + return false; + } + }; +} + +class DrawSketchHandlerSplitting: public DrawSketchHandler +{ +public: + DrawSketchHandlerSplitting() {} + virtual ~DrawSketchHandlerSplitting() + { + Gui::Selection().rmvSelectionGate(); + } + + virtual void activated(ViewProviderSketch *sketchgui) + { + Gui::Selection().clearSelection(); + Gui::Selection().rmvSelectionGate(); + Gui::Selection().addSelectionGate(new SplittingSelection(sketchgui->getObject())); + setCrosshairCursor("Sketcher_Pointer_Splitting"); + } + + virtual void mouseMove(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos); + } + + virtual bool pressButton(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos); + return true; + } + + virtual bool releaseButton(Base::Vector2d onSketchPos) + { + int GeoId = sketchgui->getPreselectCurve(); + if (GeoId >= 0) { + const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() + || geom->getTypeId() == Part::GeomCircle::getClassTypeId() + || geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + try { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Split edge")); + Gui::cmdAppObjectArgs(sketchgui->getObject(), "split(%d,App.Vector(%f,%f,0))", + GeoId, onSketchPos.x, onSketchPos.y); + Gui::Command::commitCommand(); + tryAutoRecompute(static_cast(sketchgui->getObject())); + } + catch (const Base::Exception& e) { + Base::Console().Error("Failed to split edge: %s\n", e.what()); + Gui::Command::abortCommand(); + } + } + } + else { + sketchgui->purgeHandler(); + } + + return true; + } +}; + +DEF_STD_CMD_A(CmdSketcherSplit) + +//TODO: fix the translations for this +CmdSketcherSplit::CmdSketcherSplit() + : Command("Sketcher_Split") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Split edge"); + sToolTipText = QT_TR_NOOP("Splits an edge into two while preserving constraints"); + sWhatsThis = "Sketcher_Split"; + sStatusTip = sToolTipText; + sPixmap = "Sketcher_Split"; + sAccel = "T,S"; + eType = ForEdit; +} + +void CmdSketcherSplit::activated(int iMsg) +{ + Q_UNUSED(iMsg); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerSplitting()); +} + +bool CmdSketcherSplit::isActive(void) +{ + return isCreateGeoActive(getActiveGuiDocument()); +} + + namespace SketcherGui { class ExternalSelection : public Gui::SelectionFilterGate { @@ -7061,6 +7180,7 @@ void CreateSketcherCommandsCreateGeo(void) //rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine()); rcCmdMgr.addCommand(new CmdSketcherTrimming()); rcCmdMgr.addCommand(new CmdSketcherExtend()); + rcCmdMgr.addCommand(new CmdSketcherSplit()); rcCmdMgr.addCommand(new CmdSketcherExternal()); rcCmdMgr.addCommand(new CmdSketcherCarbonCopy()); } diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 4dcefcaaf7..2b19b852cd 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -161,6 +161,7 @@ icons/geometry/Sketcher_CreateTriangle_Constr.svg icons/geometry/Sketcher_Extend.svg icons/geometry/Sketcher_External.svg + icons/geometry/Sketcher_Split.svg icons/geometry/Sketcher_ToggleConstruction.svg icons/geometry/Sketcher_Trimming.svg @@ -195,6 +196,7 @@ icons/pointers/Sketcher_Pointer_External.svg icons/pointers/Sketcher_Pointer_Regular_Polygon.svg icons/pointers/Sketcher_Pointer_Slot.svg + icons/pointers/Sketcher_Pointer_Splitting.svg icons/pointers/Sketcher_Pointer_Trimming.svg diff --git a/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_Split.svg b/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_Split.svg new file mode 100644 index 0000000000..65f04b8eae --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_Split.svg @@ -0,0 +1,671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + [wmayer] + + + + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateLine.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Splitting.svg b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Splitting.svg new file mode 100644 index 0000000000..e0b739dc07 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Splitting.svg @@ -0,0 +1,98 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index 4b0abff114..c85a405f0e 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -265,6 +265,7 @@ inline void SketcherAddWorkbenchGeometries(T& geom) SketcherAddWorkspaceFillets(geom); geom << "Sketcher_Trimming" << "Sketcher_Extend" + << "Sketcher_Split" << "Sketcher_External" << "Sketcher_CarbonCopy" << "Sketcher_ToggleConstruction"