diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index ee1780ebc8..08b54834d1 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -1109,6 +1109,50 @@ int SketchObject::fillet(int GeoId1, int GeoId2, return -1; } +int SketchObject::extend(int GeoId, double increment, int endpoint) { + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const std::vector &geomList = getInternalGeometry(); + Part::Geometry *geom = geomList[GeoId]; + int retcode = -1; + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + Part::GeomLineSegment *seg = static_cast(geom); + Base::Vector3d startVec = seg->getStartPoint(); + Base::Vector3d endVec = seg->getEndPoint(); + if (endpoint == start) { + Base::Vector3d newPoint = startVec - endVec; + double scaleFactor = newPoint.Length() + increment; + newPoint.Normalize(); + newPoint.Scale(scaleFactor, scaleFactor, scaleFactor); + newPoint = newPoint + endVec; + retcode = movePoint(GeoId, Sketcher::start, newPoint, false, true); + } else if (endpoint == end) { + Base::Vector3d newPoint = endVec - startVec; + double scaleFactor = newPoint.Length() + increment; + newPoint.Normalize(); + newPoint.Scale(scaleFactor, scaleFactor, scaleFactor); + newPoint = newPoint + startVec; + retcode = movePoint(GeoId, Sketcher::end, newPoint, false, true); + } + } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + Part::GeomArcOfCircle *arc = static_cast(geom); + double startArc, endArc; + arc->getRange(startArc, endArc, true); + if (endpoint == start) { + arc->setRange(startArc - increment, endArc, true); + retcode = 0; + } else if (endpoint == end) { + arc->setRange(startArc, endArc + increment, true); + retcode = 0; + } + } + if (retcode == 0 && noRecomputes) { + solve(); + } + return retcode; +} + int SketchObject::trim(int GeoId, const Base::Vector3d& point) { if (GeoId < 0 || GeoId > getHighestCurveIndex()) diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 177790109e..bed98480e0 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -180,6 +180,9 @@ public: /// trim a curve int trim(int geoId, const Base::Vector3d& point); + /// extend a curve + int extend(int geoId, double increment, int endPoint); + /// 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); /// with default parameters adds a copy of the geometric elements displaced by the displacement vector. diff --git a/src/Mod/Sketcher/App/SketchObjectPy.xml b/src/Mod/Sketcher/App/SketchObjectPy.xml index 717ed48e15..b7ae64f7ea 100644 --- a/src/Mod/Sketcher/App/SketchObjectPy.xml +++ b/src/Mod/Sketcher/App/SketchObjectPy.xml @@ -137,6 +137,11 @@ trim a curve with a given id at a given reference point + + + extend a curve to new start and end positions + + 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 8ebde17e82..94f6f78a07 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -823,6 +823,26 @@ PyObject* SketchObjectPy::trim(PyObject *args) Py_Return; } +PyObject* SketchObjectPy::extend(PyObject *args) +{ + double increment; + int endPoint; + + int GeoId; + + if (PyArg_ParseTuple(args, "idi", &GeoId, &increment, &endPoint)) { + if (this->getSketchObjectPtr()->extend(GeoId, increment, endPoint)) { + std::stringstream str; + str << "Not able to extend geometry with id : (" << GeoId << ") for increment (" << increment << ") and point position (" << endPoint << ")"; + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return 0; + } + } + Py_Return; + PyErr_SetString(PyExc_TypeError, "extend() method accepts:\n" + "-- int,float,int\n"); +} + PyObject* SketchObjectPy::addSymmetric(PyObject *args) { PyObject *pcObj; diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index b6c68f8f44..97c6f68678 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -6083,6 +6083,323 @@ bool CmdSketcherTrimming::isActive(void) // ====================================================================================== +namespace SketcherGui { + class ExtendSelection : public Gui::SelectionFilterGate + { + App::DocumentObject* object; + public: + ExtendSelection(App::DocumentObject* obj) + : Gui::SelectionFilterGate((Gui::SelectionFilter*)0) + , object(obj) + , disabled(false) + {} + + bool allow(App::Document * /*pDoc*/, App::DocumentObject *pObj, const char *sSubName) + { + if (pObj != this->object) + return false; + if (!sSubName || sSubName[0] == '\0') + return false; + if (disabled) + return true; + 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::GeomArcOfCircle::getClassTypeId() + ) + return true; + } + return false; + } + + void setDisabled(bool isDisabled) { + disabled = isDisabled; + } + protected: + bool disabled; + }; +}; + +/* XPM */ +static const char *cursor_extension[]={ +"32 32 3 1", +"+ c white", +"* c red", +". c None", +"......+.........................", +"......+.........................", +"......+.........................", +"......+.........................", +"......+.........................", +"................................", +"+++++...+++++...................", +"................................", +"......+.........................", +"......+.........................", +"......+..................******.", +"......+....................****.", +"......+..................***.**.", +"........................**....*.", +"......................***.......", +".....................***........", +"................................", +"................................", +".................**.............", +"...............***..............", +"..............***...............", +".............**.................", +"................................", +".........***....................", +"........**......................", +".......**.......................", +"....**.*........................", +"...****.........................", +"...****.........................", +"....**..........................", +"................................", +"................................"}; + +class DrawSketchHandlerExtend: public DrawSketchHandler +{ +public: + DrawSketchHandlerExtend() + : Mode(STATUS_SEEK_First) + , EditCurve(2) + , BaseGeoId(-1) + , ExtendFromStart(false) + { + } + virtual ~DrawSketchHandlerExtend() + { + Gui::Selection().rmvSelectionGate(); + } + enum SelectMode { + STATUS_SEEK_First, + STATUS_SEEK_Second, + }; + + virtual void activated(ViewProviderSketch *sketchgui) + { + Q_UNUSED(sketchgui) + Gui::Selection().clearSelection(); + Gui::Selection().rmvSelectionGate(); + filterGate = new ExtendSelection(sketchgui->getObject()); + Gui::Selection().addSelectionGate(filterGate); + setCursor(QPixmap(cursor_extension),7,7); + } + + virtual void mouseMove(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos); + if (Mode == STATUS_SEEK_Second) { + const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geom); + // project point to the existing curve + Base::Vector3d startPoint = lineSeg->getStartPoint(); + Base::Vector3d endPoint = lineSeg->getEndPoint(); + + Base::Vector2d recenteredLine = Base::Vector2d(endPoint.x - startPoint.x, + endPoint.y - startPoint.y); + Base::Vector2d recenteredPoint = Base::Vector2d(onSketchPos.x - startPoint.x, + onSketchPos.y - startPoint.y); + Base::Vector2d projection; + projection.ProjectToLine(recenteredPoint, recenteredLine); + if (recenteredPoint.Length() < recenteredPoint.Distance(recenteredLine)) { + EditCurve[0] = Base::Vector2d(startPoint.x + projection.x, startPoint.y + projection.y); + EditCurve[1] = Base::Vector2d(endPoint.x, endPoint.y); + } else { + EditCurve[0] = Base::Vector2d(startPoint.x, startPoint.y); + EditCurve[1] = Base::Vector2d(startPoint.x + projection.x, startPoint.y + projection.y); + } + ExtendFromStart = (onSketchPos.Distance(EditCurve[0]) < onSketchPos.Distance(EditCurve[1])); + Increment = ExtendFromStart ? projection.Length() : projection.Length() - recenteredLine.Length(); + sketchgui->drawEdit(EditCurve); + + } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geom); + Base::Vector3d center = arc->getCenter(); + double radius = arc->getRadius(); + + double start, end; + arc->getRange(start, end, true); + double arcAngle = end - start; + + Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y); + Base::Vector2d startAngle = Base::Vector2d(cos(start), sin(start)); + Base::Vector2d endAngle = Base::Vector2d(cos(end), sin(end)); + + Base::Vector2d arcHalf = Base::Vector2d(cos(start + arcAngle/ 2.0), sin(start+ arcAngle / 2.0)); + double angleToEndAngle = angle.GetAngle(endAngle); + double angleToStartAngle = angle.GetAngle(startAngle); + + if (arcHalf.GetAngle(angle) > arcAngle / 2) { + double modStartAngle = start; + double modArcAngle = end - start; + if (ExtendFromStart) { + if (crossProduct(angle, startAngle) < 0) { + modStartAngle -= 2*M_PI - angleToStartAngle; + modArcAngle += 2*M_PI - angleToStartAngle; + } else { + modStartAngle -= angleToStartAngle; + modArcAngle += angleToStartAngle; + } + } else { + if (crossProduct(angle, endAngle) >= 0) { + modArcAngle += 2*M_PI - angleToEndAngle; + } else { + modArcAngle += angleToEndAngle; + } + } + for (int i = 0; i < 31; i++) { + double angle = modStartAngle + i * modArcAngle/30.0; + EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle)); + } + Increment = modArcAngle - (end- start); + sketchgui->drawEdit(EditCurve); + } else { + // draw curve anyway to avoid 'stuck' appearance + for (int i = 0; i < 31; i++) { + double angle = start + i * arcAngle/30.0; + EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle)); + } + Increment = 0; + sketchgui->drawEdit(EditCurve); + } + } + if (seekAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) { + renderSuggestConstraintsCursor(SugConstr); + return; + } + } + } + + virtual bool pressButton(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos); + return true; + } + + virtual bool releaseButton(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos); + if (Mode == STATUS_SEEK_First) { + BaseGeoId = sketchgui->getPreselectCurve(); + if (BaseGeoId > -1) { + const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + Mode = STATUS_SEEK_Second; + } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geom); + double start, end; + arc->getRange(start, end, true); + + Base::Vector3d center = arc->getCenter(); + Base::Vector2d angle = Base::Vector2d(onSketchPos.x - center.x, onSketchPos.y - center.y); + double angleToStart = angle.GetAngle(Base::Vector2d(cos(start), sin(start))); + double angleToEnd = angle.GetAngle(Base::Vector2d(cos(end), sin(end))); + ExtendFromStart = (angleToStart < angleToEnd); // move start point if closer to angle than end point + EditCurve.resize(31); + Mode = STATUS_SEEK_Second; + } + filterGate->setDisabled(true); + } + } else if (Mode == STATUS_SEEK_Second) { + try { + Gui::Command::openCommand("Extend edge"); + Gui::Command::doCommand(Gui::Command::Doc, + "App.ActiveDocument.%s.extend(%d, %f, %d)\n", // GeoId, increment, PointPos + sketchgui->getObject()->getNameInDocument(), BaseGeoId, Increment, + ExtendFromStart ? Sketcher::start : Sketcher::end); + Gui::Command::commitCommand(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + bool autoRecompute = hGrp->GetBool("AutoRecompute",false); + if(autoRecompute) + Gui::Command::updateActive(); + + // constrain chosen point + if (SugConstr.size() > 0) { + createAutoConstraints(SugConstr, BaseGeoId, (ExtendFromStart) ? Sketcher::start : Sketcher::end); + SugConstr.clear(); + } + bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); + + if(continuousMode){ + // This code enables the continuous creation mode. + Mode=STATUS_SEEK_First; + filterGate->setDisabled(false); + EditCurve.clear(); + sketchgui->drawEdit(EditCurve); + EditCurve.resize(2); + applyCursor(); + /* this is ok not to call to purgeHandler + * in continuous creation mode because the + * handler is destroyed by the quit() method on pressing the + * right button of the mouse */ + } else{ + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + } + catch (const Base::Exception& e) { + Base::Console().Error("Failed to extend edge: %s\n", e.what()); + Gui::Command::abortCommand(); + } + + } else { // exit extension tool if user clicked on empty space + BaseGeoId = -1; + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + return true; + } + +protected: + SelectMode Mode; + std::vector EditCurve; + int BaseGeoId; + ExtendSelection* filterGate = nullptr; + bool ExtendFromStart; // if true, extend from start, else extend from end (circle only) + double Increment; + std::vector SugConstr; + +private: + int crossProduct(Base::Vector2d &vec1, Base::Vector2d &vec2) { + return vec1.x * vec2.y - vec1.y * vec2.x; + } +}; + +DEF_STD_CMD_A(CmdSketcherExtend); + +//TODO: fix the translations for this +CmdSketcherExtend::CmdSketcherExtend() + : Command("Sketcher_Extend") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Extend edge"); + sToolTipText = QT_TR_NOOP("Extend an edge with respect to the picked position"); + sWhatsThis = "Sketcher_Extend"; + sStatusTip = sToolTipText; + sPixmap = "Sketcher_Extend"; + sAccel = "T,E"; + eType = ForEdit; +} + +void CmdSketcherExtend::activated(int iMsg) +{ + Q_UNUSED(iMsg); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerExtend()); +} + +bool CmdSketcherExtend::isActive(void) +{ + return isCreateGeoActive(getActiveGuiDocument()); +} + + namespace SketcherGui { class ExternalSelection : public Gui::SelectionFilterGate { @@ -7372,6 +7689,7 @@ void CreateSketcherCommandsCreateGeo(void) //rcCmdMgr.addCommand(new CmdSketcherCreateText()); //rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine()); rcCmdMgr.addCommand(new CmdSketcherTrimming()); + rcCmdMgr.addCommand(new CmdSketcherExtend()); 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 2bc9612491..33a6b6c805 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -144,6 +144,7 @@ icons/Sketcher_Element_SelectionTypeInvalid.svg icons/Sketcher_Elliptical_Arc.svg icons/Sketcher_Elliptical_Arc_Constr.svg + icons/Sketcher_Extend.svg icons/Sketcher_External.svg icons/Sketcher_Hyperbolic_Arc.svg icons/Sketcher_Hyperbolic_Arc_Constr.svg diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index e166919ec7..eb3a10dc60 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -894,6 +894,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe << "Sketcher_CreateHexagon" << "Sketcher_CreateFillet" << "Sketcher_Trimming" + << "Sketcher_Extend" << "Sketcher_External" << "Sketcher_ToggleConstruction" /*<< "Sketcher_CreateText"*/ diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index b2ee504a2b..f7e7159e7b 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -190,6 +190,7 @@ inline void SketcherAddWorkbenchGeometries(T& geom){ << "Separator" << "Sketcher_CreateFillet" << "Sketcher_Trimming" + << "Sketcher_Extend" << "Sketcher_External" << "Sketcher_CarbonCopy" << "Sketcher_ToggleConstruction"