From b3fe5bba2811fa6c6ed45c9c9ded9b7806e93938 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 12 Mar 2024 11:45:08 +0100 Subject: [PATCH] Sketcher: Chamfer tool (and fillet refactor) --- src/Gui/PreferencePackTemplates/Shortcuts.cfg | 2 +- src/Mod/Sketcher/App/SketchObject.cpp | 228 ++++---- src/Mod/Sketcher/App/SketchObject.h | 11 +- src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 26 +- src/Mod/Sketcher/Gui/CommandCreateGeo.cpp | 158 ++---- .../Sketcher/Gui/DrawSketchHandlerFillet.h | 486 ++++++++++------ src/Mod/Sketcher/Gui/Resources/Sketcher.qrc | 3 + .../icons/geometry/Sketcher_CreateChamfer.svg | 537 ++++++++++++++++++ .../Sketcher_Pointer_Create_Chamfer.svg | 159 ++++++ .../Sketcher_Pointer_Create_PointChamfer.svg | 145 +++++ src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 3 +- src/Mod/Sketcher/Gui/Workbench.cpp | 4 +- 12 files changed, 1342 insertions(+), 420 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_CreateChamfer.svg create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Chamfer.svg create mode 100644 src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg diff --git a/src/Gui/PreferencePackTemplates/Shortcuts.cfg b/src/Gui/PreferencePackTemplates/Shortcuts.cfg index 48572b9e34..c82e3fd307 100644 --- a/src/Gui/PreferencePackTemplates/Shortcuts.cfg +++ b/src/Gui/PreferencePackTemplates/Shortcuts.cfg @@ -548,7 +548,7 @@ G, B, P G, B, O G, Y - G, F, P + G, F, C G, M G, R G, V diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 8312902534..ecb6c9abe6 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -2065,7 +2065,7 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG return 0; } -int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bool createCorner) +int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bool createCorner, bool chamfer) { if (GeoId < 0 || GeoId > getHighestCurveIndex()) return -1; @@ -2081,12 +2081,12 @@ int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bo const Part::Geometry* geo2 = getGeometry(GeoIdList[1]); if (geo1->is() && geo2->is()) { - const Part::GeomLineSegment* lineSeg1 = static_cast(geo1); - const Part::GeomLineSegment* lineSeg2 = static_cast(geo2); + auto* lineSeg1 = static_cast(geo1); + auto* lineSeg2 = static_cast(geo2); Base::Vector3d midPnt1 = (lineSeg1->getStartPoint() + lineSeg1->getEndPoint()) / 2; Base::Vector3d midPnt2 = (lineSeg2->getStartPoint() + lineSeg2->getEndPoint()) / 2; - return fillet(GeoIdList[0], GeoIdList[1], midPnt1, midPnt2, radius, trim, createCorner); + return fillet(GeoIdList[0], GeoIdList[1], midPnt1, midPnt2, radius, trim, createCorner, chamfer); } } @@ -2094,11 +2094,11 @@ int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim, bo } int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, - const Base::Vector3d& refPnt2, double radius, bool trim, bool createCorner) + const Base::Vector3d& refPnt2, double radius, bool trim, bool createCorner, bool chamfer) { - if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() || GeoId2 < 0 - || GeoId2 > getHighestCurveIndex()) + if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() || GeoId2 < 0 || GeoId2 > getHighestCurveIndex()) { return -1; + } // If either of the two input lines are locked, don't try to trim since it won't work anyway const Part::Geometry* geo1 = getGeometry(GeoId1); @@ -2107,14 +2107,19 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, trim = false; } - if (geo1->is() - && geo2->is()) { - const Part::GeomLineSegment* lineSeg1 = static_cast(geo1); - const Part::GeomLineSegment* lineSeg2 = static_cast(geo2); + Base::Vector3d p1, p2; + PointPos PosId1 = PointPos::none; + PointPos PosId2 = PointPos::none; + int filletId; + + if (geo1->is() && geo2->is()) { + auto* lineSeg1 = static_cast(geo1); + auto* lineSeg2 = static_cast(geo2); Base::Vector3d filletCenter; - if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, filletCenter)) + if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, filletCenter)) { return -1; + } Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint(); Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint(); @@ -2123,27 +2128,27 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, Base::Vector3d intersection, dist1, dist2; // create arc from known parameters and lines - int filletId; std::unique_ptr arc( Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius)); - if (!arc) + if (!arc) { return -1; + } // calculate intersection and distances before we invalidate lineSeg1 and lineSeg2 if (!find2DLinesIntersection(lineSeg1, lineSeg2, intersection)) { return -1; } + p1 = arc->getStartPoint(); + p2 = arc->getEndPoint(); + dist1.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true) - intersection, dir1); dist2.ProjectToLine(arc->getStartPoint(/*emulateCCW=*/true) - intersection, dir2); - Part::Geometry* newgeo = arc.get(); - filletId = addGeometry(newgeo); + filletId = addGeometry(arc.get()); if (trim) { - PointPos PosId1 = - (filletCenter - intersection) * dir1 > 0 ? PointPos::start : PointPos::end; - PointPos PosId2 = - (filletCenter - intersection) * dir2 > 0 ? PointPos::start : PointPos::end; + PosId1 = (filletCenter - intersection) * dir1 > 0 ? PointPos::start : PointPos::end; + PosId2 = (filletCenter - intersection) * dir2 > 0 ? PointPos::start : PointPos::end; if (createCorner) { transferFilletConstraints(GeoId1, PosId1, GeoId2, PosId2); @@ -2182,16 +2187,8 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, addConstraint(std::move(tangent1)); addConstraint(std::move(tangent2)); } - - // if we do not have a recompute after the geometry creation, the sketch must be solved to - // update the DoF of the solver - if (noRecomputes) - solve(); - - return 0; } - else if (geo1->isDerivedFrom() - && geo2->isDerivedFrom()) { + else if (geo1->isDerivedFrom() && geo2->isDerivedFrom()) { auto distancetorefpoints = [](Base::Vector3d ip1, Base::Vector3d ip2, Base::Vector3d ref1, Base::Vector3d ref2) { @@ -2284,40 +2281,35 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, // look for coincident constraints between curves, take the coincident closest to the // refpoints - Sketcher::PointPos curve1PosId = Sketcher::PointPos::none; - Sketcher::PointPos curve2PosId = Sketcher::PointPos::none; - double dist = INFINITY; const std::vector& constraints = this->Constraints.getValues(); - for (std::vector::const_iterator it = constraints.begin(); - it != constraints.end(); - ++it) { - if ((*it)->Type == Sketcher::Coincident || (*it)->Type == Sketcher::Perpendicular - || (*it)->Type == Sketcher::Tangent) { - if ((*it)->First == GeoId1 && (*it)->Second == GeoId2 - && (*it)->FirstPos != Sketcher::PointPos::none - && (*it)->SecondPos != Sketcher::PointPos::none) { - Base::Vector3d tmpp1 = getPoint((*it)->First, (*it)->FirstPos); - Base::Vector3d tmpp2 = getPoint((*it)->Second, (*it)->SecondPos); + for (auto& constr : constraints) { + if (constr->Type == Sketcher::Coincident || constr->Type == Sketcher::Perpendicular + || constr->Type == Sketcher::Tangent) { + if (constr->First == GeoId1 && constr->Second == GeoId2 + && constr->FirstPos != PointPos::none + && constr->SecondPos != PointPos::none) { + Base::Vector3d tmpp1 = getPoint(constr->First, constr->FirstPos); + Base::Vector3d tmpp2 = getPoint(constr->Second, constr->SecondPos); double tmpdist = distancetorefpoints(tmpp1, tmpp2, refPnt1, refPnt2); if (tmpdist < dist) { - curve1PosId = (*it)->FirstPos; - curve2PosId = (*it)->SecondPos; + PosId1 = constr->FirstPos; + PosId2 = constr->SecondPos; dist = tmpdist; interpoints = std::make_pair(tmpp1, tmpp2); } } - else if ((*it)->First == GeoId2 && (*it)->Second == GeoId1 - && (*it)->FirstPos != Sketcher::PointPos::none - && (*it)->SecondPos != Sketcher::PointPos::none) { - Base::Vector3d tmpp2 = getPoint((*it)->First, (*it)->FirstPos); - Base::Vector3d tmpp1 = getPoint((*it)->Second, (*it)->SecondPos); + else if (constr->First == GeoId2 && constr->Second == GeoId1 + && constr->FirstPos != PointPos::none + && constr->SecondPos != PointPos::none) { + Base::Vector3d tmpp2 = getPoint(constr->First, constr->FirstPos); + Base::Vector3d tmpp1 = getPoint(constr->Second, constr->SecondPos); double tmpdist = distancetorefpoints(tmpp1, tmpp2, refPnt1, refPnt2); if (tmpdist < dist) { - curve2PosId = (*it)->FirstPos; - curve1PosId = (*it)->SecondPos; + PosId2 = constr->FirstPos; + PosId1 = constr->SecondPos; dist = tmpdist; interpoints = std::make_pair(tmpp1, tmpp2); } @@ -2325,15 +2317,13 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, } } - if (curve1PosId == Sketcher::PointPos::none) { + if (PosId1 == PointPos::none) { // no coincident was found, try basis curve intersection if GeomTrimmedCurve - if (geo1->isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId()) - && geo2->isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId())) { + if (geo1->isDerivedFrom() + && geo2->isDerivedFrom()) { - const Part::GeomTrimmedCurve* tcurve1 = - static_cast(geo1); - const Part::GeomTrimmedCurve* tcurve2 = - static_cast(geo2); + auto* tcurve1 =static_cast(geo1); + auto* tcurve2 = static_cast(geo2); try { if (!tcurve1->intersectBasisCurves(tcurve2, points)) @@ -2350,11 +2340,13 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, int res = selectintersection(points, interpoints, refPnt1, refPnt2); - if (res != 0) + if (res != 0) { return res; + } } - else + else { return -1;// not a GeomTrimmedCurve and no coincident point. + } } // Now that we know where the curves intersect, get the parameters in the curves of those @@ -2363,8 +2355,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, double intparam2; try { - if (!curve1->closestParameter(interpoints.first, intparam1)) + if (!curve1->closestParameter(interpoints.first, intparam1)) { return -1; + } } catch (Base::CADKernelError& e) { e.ReportException(); @@ -2374,8 +2367,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, } try { - if (!curve2->closestParameter(interpoints.second, intparam2)) + if (!curve2->closestParameter(interpoints.second, intparam2)) { return -1; + } } catch (Base::CADKernelError& e) { e.ReportException(); @@ -2439,8 +2433,7 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, / det, 0); - radius = - ((refp1 - normalintersect).Length() + (refp2 - normalintersect).Length()) / 2; + radius = ((refp1 - normalintersect).Length() + (refp2 - normalintersect).Length()) / 2; } catch (const Base::Exception&) { radius = ref21.Length();// fall-back to simplest estimation. @@ -2543,8 +2536,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, int res = selectintersection(offsetintersectionpoints, filletcenterpoint, refPnt1, refPnt2); - if (res != 0) + if (res != 0) { return res; + } #ifdef DEBUG Base::Console().Log( @@ -2555,8 +2549,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, double refoparam2; try { - if (!curve1->closestParameter(filletcenterpoint.first, refoparam1)) + if (!curve1->closestParameter(filletcenterpoint.first, refoparam1)) { return -1; + } } catch (Base::CADKernelError& e) { e.ReportException(); @@ -2564,8 +2559,9 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, } try { - if (!curve2->closestParameter(filletcenterpoint.second, refoparam2)) + if (!curve2->closestParameter(filletcenterpoint.second, refoparam2)) { return -1; + } } catch (Base::CADKernelError& e) { e.ReportException(); @@ -2599,25 +2595,29 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, endAngle = startAngle + range; - if (endAngle < startAngle) + if (endAngle < startAngle) { std::swap(startAngle, endAngle); + } - if (endAngle > 2 * M_PI) + if (endAngle > 2 * M_PI) { endAngle -= 2 * M_PI; + } - if (startAngle < 0) + if (startAngle < 0) { endAngle += 2 * M_PI; + } // Create Arc Segment - Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle(); + auto* arc = new Part::GeomArcOfCircle(); arc->setRadius(radDir1.Length()); arc->setCenter(filletcenterpoint.first); arc->setRange(startAngle, endAngle, /*emulateCCWXY=*/true); + p1 = arc->getStartPoint(); + p2 = arc->getEndPoint(); + // add arc to sketch geometry - int filletId; - Part::Geometry* newgeo = arc; - filletId = addGeometry(newgeo); + filletId = addGeometry(arc); if (filletId < 0) { delete arc; return -1; @@ -2639,27 +2639,27 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, // b) we used the basis curve intersection - if (curve1PosId == Sketcher::PointPos::none) { - curve1PosId = selectend(intparam1, refoparam1, spc1); - curve2PosId = selectend(intparam2, refoparam2, spc2); + if (PosId1 == Sketcher::PointPos::none) { + PosId1 = selectend(intparam1, refoparam1, spc1); + PosId2 = selectend(intparam2, refoparam2, spc2); } - delConstraintOnPoint(GeoId1, curve1PosId, false); - delConstraintOnPoint(GeoId2, curve2PosId, false); + delConstraintOnPoint(GeoId1, PosId1, false); + delConstraintOnPoint(GeoId2, PosId2, false); - Sketcher::Constraint* tangent1 = new Sketcher::Constraint(); - Sketcher::Constraint* tangent2 = new Sketcher::Constraint(); + auto* tangent1 = new Sketcher::Constraint(); + auto* tangent2 = new Sketcher::Constraint(); tangent1->Type = Sketcher::Tangent; tangent1->First = GeoId1; - tangent1->FirstPos = curve1PosId; + tangent1->FirstPos = PosId1; tangent1->Second = filletId; tangent2->Type = Sketcher::Tangent; tangent2->First = GeoId2; - tangent2->FirstPos = curve2PosId; + tangent2->FirstPos = PosId2; tangent2->Second = filletId; double dist1 = (refp1 - arc->getStartPoint(true)).Length(); @@ -2670,14 +2670,14 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, if (dist1 < dist2) { tangent1->SecondPos = PointPos::start; tangent2->SecondPos = PointPos::end; - movePoint(GeoId1, curve1PosId, arc->getStartPoint(true), false, true); - movePoint(GeoId2, curve2PosId, arc->getEndPoint(true), false, true); + movePoint(GeoId1, PosId1, arc->getStartPoint(true), false, true); + movePoint(GeoId2, PosId2, arc->getEndPoint(true), false, true); } else { tangent1->SecondPos = PointPos::end; tangent2->SecondPos = PointPos::start; - movePoint(GeoId1, curve1PosId, arc->getEndPoint(true), false, true); - movePoint(GeoId2, curve2PosId, arc->getStartPoint(true), false, true); + movePoint(GeoId1, PosId1, arc->getEndPoint(true), false, true); + movePoint(GeoId2, PosId2, arc->getStartPoint(true), false, true); } addConstraint(tangent1); @@ -2692,15 +2692,53 @@ int SketchObject::fillet(int GeoId1, int GeoId2, const Base::Vector3d& refPnt1, #ifdef DEBUG Base::Console().Log("\n\nEND OF FILLET DEBUG\n\n"); #endif - - // if we do not have a recompute after the geometry creation, the sketch must be solved to - // update the DoF of the solver - if (noRecomputes) - solve(); - - return 0; } - return -1; + else { + return -1; + } + + if (chamfer) { + auto line = std::make_unique(); + line->setPoints(p1, p2); + int lineGeoId = addGeometry(line.get()); + + auto coinc1 = std::make_unique(); + auto coinc2 = std::make_unique(); + + coinc1->Type = Sketcher::Coincident; + coinc1->First = lineGeoId; + coinc1->FirstPos = PointPos::start; + + coinc2->Type = Sketcher::Coincident; + coinc2->First = lineGeoId; + coinc2->FirstPos = PointPos::end; + + if (trim) { + coinc1->Second = GeoId1; + coinc1->SecondPos = PosId1; + coinc2->Second = GeoId2; + coinc2->SecondPos = PosId2; + } + else { + coinc1->Second = filletId; + coinc1->SecondPos = PointPos::start; + coinc2->Second = filletId; + coinc2->SecondPos = PointPos::end; + } + + addConstraint(std::move(coinc1)); + addConstraint(std::move(coinc2)); + + setConstruction(filletId, true); + } + + // if we do not have a recompute after the geometry creation, the sketch must be solved to + // update the DoF of the solver + if (noRecomputes) { + solve(); + } + + return 0; } int SketchObject::extend(int GeoId, double increment, PointPos endpoint) diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index d0f3b99ba1..5d748bdadb 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -323,8 +323,12 @@ public: \param createCorner - keep geoId/pos as a Point and keep as many constraints as possible \retval - 0 on success, -1 on failure */ - int - fillet(int geoId, PointPos pos, double radius, bool trim = true, bool preserveCorner = false); + int fillet(int geoId, + PointPos pos, + double radius, + bool trim = true, + bool preserveCorner = false, + bool chamfer = false); /*! \brief More general form of fillet \param geoId1, geoId2 - geoId for two lines (which don't necessarily have to coincide) @@ -340,7 +344,8 @@ public: const Base::Vector3d& refPnt2, double radius, bool trim = true, - bool createCorner = false); + bool createCorner = false, + bool chamfer = false); /// trim a curve int trim(int geoId, const Base::Vector3d& point); diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 631e90b8f8..54d2eac944 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -1196,11 +1196,12 @@ PyObject* SketchObjectPy::fillet(PyObject* args) int geoId1, geoId2, posId1; int trim = true; PyObject* createCorner = Py_False; + PyObject* chamfer = Py_False; double radius; // Two Lines, radius if (PyArg_ParseTuple(args, - "iiO!O!d|iO!", + "iiO!O!d|iO!O!", &geoId1, &geoId2, &(Base::VectorPy::Type), @@ -1210,15 +1211,23 @@ PyObject* SketchObjectPy::fillet(PyObject* args) &radius, &trim, &PyBool_Type, - &createCorner)) { + &createCorner, + &PyBool_Type, + &chamfer)) { // The i for &trim should probably have been a bool like &createCorner, but we'll leave it // an int for backward compatibility (and because python will accept a bool there anyway) Base::Vector3d v1 = static_cast(pcObj1)->value(); Base::Vector3d v2 = static_cast(pcObj2)->value(); - if (this->getSketchObjectPtr() - ->fillet(geoId1, geoId2, v1, v2, radius, trim, Base::asBoolean(createCorner))) { + if (this->getSketchObjectPtr()->fillet(geoId1, + geoId2, + v1, + v2, + radius, + trim, + Base::asBoolean(createCorner), + Base::asBoolean(chamfer))) { std::stringstream str; str << "Not able to fillet curves with ids : (" << geoId1 << ", " << geoId2 << ") and points (" << v1.x << ", " << v1.y << ", " << v1.z << ") & " @@ -1232,18 +1241,21 @@ PyObject* SketchObjectPy::fillet(PyObject* args) PyErr_Clear(); // Point, radius if (PyArg_ParseTuple(args, - "iid|iO!", + "iid|iO!O!", &geoId1, &posId1, &radius, &trim, &PyBool_Type, - &createCorner)) { + &createCorner, + &PyBool_Type, + &chamfer)) { if (this->getSketchObjectPtr()->fillet(geoId1, static_cast(posId1), radius, trim, - Base::asBoolean(createCorner))) { + Base::asBoolean(createCorner), + Base::asBoolean(chamfer))) { std::stringstream str; str << "Not able to fillet point with ( geoId: " << geoId1 << ", PointPos: " << posId1 << " )"; diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 5d5fb442d6..2b66c0874d 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -1431,8 +1431,9 @@ CmdSketcherCreateFillet::CmdSketcherCreateFillet() void CmdSketcherCreateFillet::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), - new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet)); + ActivateHandler( + getActiveGuiDocument(), + new DrawSketchHandlerFillet(ConstructionMethods::FilletConstructionMethod::Fillet)); } bool CmdSketcherCreateFillet::isActive() @@ -1442,149 +1443,62 @@ bool CmdSketcherCreateFillet::isActive() // ====================================================================================== -DEF_STD_CMD_A(CmdSketcherCreatePointFillet) +DEF_STD_CMD_A(CmdSketcherCreateChamfer) -CmdSketcherCreatePointFillet::CmdSketcherCreatePointFillet() - : Command("Sketcher_CreatePointFillet") +CmdSketcherCreateChamfer::CmdSketcherCreateChamfer() + : Command("Sketcher_CreateChamfer") { sAppModule = "Sketcher"; sGroup = "Sketcher"; - sMenuText = QT_TR_NOOP("Create corner-preserving fillet"); - sToolTipText = QT_TR_NOOP("Fillet that preserves intersection point and most constraints"); - sWhatsThis = "Sketcher_CreatePointFillet"; + sMenuText = QT_TR_NOOP("Create chamfer"); + sToolTipText = QT_TR_NOOP("Create a chamfer between two lines or at a coincident point"); + sWhatsThis = "Sketcher_CreateChamfer"; sStatusTip = sToolTipText; - sPixmap = "Sketcher_CreatePointFillet"; - sAccel = "G, F, P"; + sPixmap = "Sketcher_CreateChamfer"; + sAccel = "G, F, C"; eType = ForEdit; } -void CmdSketcherCreatePointFillet::activated(int iMsg) +void CmdSketcherCreateChamfer::activated(int iMsg) { Q_UNUSED(iMsg); ActivateHandler( getActiveGuiDocument(), - new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet)); + new DrawSketchHandlerFillet(ConstructionMethods::FilletConstructionMethod::Chamfer)); } -bool CmdSketcherCreatePointFillet::isActive() +bool CmdSketcherCreateChamfer::isActive() { return isCommandActive(getActiveGuiDocument()); } -/// @brief Macro that declares a new sketcher command class 'CmdSketcherCompCreateFillets' -DEF_STD_CMD_ACLU(CmdSketcherCompCreateFillets) -/** - * @brief ctor - */ -CmdSketcherCompCreateFillets::CmdSketcherCompCreateFillets() - : Command("Sketcher_CompCreateFillets") +class CmdSketcherCompCreateFillets: public Gui::GroupCommand { - sAppModule = "Sketcher"; - sGroup = "Sketcher"; - sMenuText = QT_TR_NOOP("Create fillet"); - sToolTipText = QT_TR_NOOP("Create a fillet between two lines"); - sWhatsThis = "Sketcher_CompCreateFillets"; - sStatusTip = sToolTipText; - eType = ForEdit; -} +public: + CmdSketcherCompCreateFillets() + : GroupCommand("Sketcher_CompCreateFillets") + { + sAppModule = "Sketcher"; + sGroup = "Sketcher"; + sMenuText = QT_TR_NOOP("Create fillet or chamfer"); + sToolTipText = QT_TR_NOOP("Create a fillet or chamfer between two lines"); + sWhatsThis = "Sketcher_CompCreateFillets"; + sStatusTip = sToolTipText; + eType = ForEdit; -/** - * @brief Instantiates the fillet handler when the fillet command activated - * @param int iMsg - */ -void CmdSketcherCompCreateFillets::activated(int iMsg) -{ - if (iMsg == 0) { - ActivateHandler(getActiveGuiDocument(), - new DrawSketchHandlerFillet(DrawSketchHandlerFillet::SimpleFillet)); - } - else if (iMsg == 1) { - ActivateHandler( - getActiveGuiDocument(), - new DrawSketchHandlerFillet(DrawSketchHandlerFillet::ConstraintPreservingFillet)); - } - else { - return; + setCheckable(false); + + addCommand("Sketcher_CreateFillet"); + addCommand("Sketcher_CreateChamfer"); } - // Since the default icon is reset when enabling/disabling the command we have - // to explicitly set the icon of the used command. - Gui::ActionGroup* pcAction = qobject_cast(_pcAction); - QList a = pcAction->actions(); - - assert(iMsg < a.size()); - pcAction->setIcon(a[iMsg]->icon()); -} - -Gui::Action* CmdSketcherCompCreateFillets::createAction() -{ - Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); - pcAction->setDropDownMenu(true); - applyCommandData(this->className(), pcAction); - - QAction* oldFillet = pcAction->addAction(QString()); - oldFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet")); - - QAction* pointFillet = pcAction->addAction(QString()); - pointFillet->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet")); - - _pcAction = pcAction; - languageChange(); - - pcAction->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet")); - int defaultId = 1; - pcAction->setProperty("defaultAction", QVariant(defaultId)); - - return pcAction; -} - -void CmdSketcherCompCreateFillets::updateAction(int mode) -{ - Q_UNUSED(mode); - Gui::ActionGroup* pcAction = qobject_cast(getAction()); - if (!pcAction) { - return; + const char* className() const override + { + return "CmdSketcherCompCreateFillets"; } +}; - QList a = pcAction->actions(); - int index = pcAction->property("defaultAction").toInt(); - a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet")); - a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet")); - getAction()->setIcon(a[index]->icon()); -} - -void CmdSketcherCompCreateFillets::languageChange() -{ - Command::languageChange(); - - if (!_pcAction) { - return; - } - Gui::ActionGroup* pcAction = qobject_cast(_pcAction); - QList a = pcAction->actions(); - - QAction* oldFillet = a[0]; - oldFillet->setText(QApplication::translate("CmdSketcherCompCreateFillets", "Sketch fillet")); - oldFillet->setToolTip( - QApplication::translate("Sketcher_CreateFillet", "Creates a radius between two lines")); - oldFillet->setStatusTip( - QApplication::translate("Sketcher_CreateFillet", "Creates a radius between two lines")); - QAction* pointFillet = a[1]; - pointFillet->setText( - QApplication::translate("CmdSketcherCompCreateFillets", "Corner-preserving sketch fillet")); - pointFillet->setToolTip( - QApplication::translate("Sketcher_CreatePointFillet", - "Fillet that preserves constraints and intersection point")); - pointFillet->setStatusTip( - QApplication::translate("Sketcher_CreatePointFillet", - "Fillet that preserves constraints and intersection point")); -} - -bool CmdSketcherCompCreateFillets::isActive() -{ - return isCommandActive(getActiveGuiDocument()); -} // ====================================================================================== @@ -2323,9 +2237,9 @@ void CreateSketcherCommandsCreateGeo() rcCmdMgr.addCommand(new CmdSketcherCreateSlot()); rcCmdMgr.addCommand(new CmdSketcherCreateArcSlot()); rcCmdMgr.addCommand(new CmdSketcherCompSlot()); - rcCmdMgr.addCommand(new CmdSketcherCompCreateFillets()); rcCmdMgr.addCommand(new CmdSketcherCreateFillet()); - rcCmdMgr.addCommand(new CmdSketcherCreatePointFillet()); + rcCmdMgr.addCommand(new CmdSketcherCreateChamfer()); + rcCmdMgr.addCommand(new CmdSketcherCompCreateFillets()); // rcCmdMgr.addCommand(new CmdSketcherCreateText()); // rcCmdMgr.addCommand(new CmdSketcherCreateDraftLine()); rcCmdMgr.addCommand(new CmdSketcherTrimming()); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h index b6c5aa1366..0fd4e12442 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerFillet.h @@ -35,6 +35,8 @@ #include "Utils.h" #include "ViewProviderSketch.h" +using namespace Sketcher; + namespace SketcherGui { @@ -86,80 +88,106 @@ public: }; -class DrawSketchHandlerFillet: public DrawSketchHandler -{ -public: - enum FilletType - { - SimpleFillet, - ConstraintPreservingFillet - }; +class DrawSketchHandlerFillet; - explicit DrawSketchHandlerFillet(FilletType filletType) - : filletType(filletType) - , Mode(STATUS_SEEK_First) - , firstCurve(0) +namespace ConstructionMethods +{ + +enum class FilletConstructionMethod +{ + Fillet, + Chamfer, + End // Must be the last one +}; + +} + +using DSHFilletController = + DrawSketchDefaultWidgetController, // NOLINT + /*WidgetParametersT =*/WidgetParameters<0, 0>, // NOLINT + /*WidgetCheckboxesT =*/WidgetCheckboxes<1, 1>, // NOLINT + /*WidgetComboboxesT =*/WidgetComboboxes<1, 1>, // NOLINT + ConstructionMethods::FilletConstructionMethod, + /*bool PFirstComboboxIsConstructionMethod =*/true>; + +using DSHFilletControllerBase = DSHFilletController::ControllerBase; + +using DrawSketchHandlerFilletBase = DrawSketchControllableHandler; + +class DrawSketchHandlerFillet: public DrawSketchHandlerFilletBase +{ + friend DSHFilletController; + friend DSHFilletControllerBase; + +public: + explicit DrawSketchHandlerFillet(ConstructionMethod constrMethod = ConstructionMethod::Fillet) + : DrawSketchHandlerFilletBase(constrMethod) + , preserveCorner(true) + , vtId(-1) + , geoId1(GeoEnum::GeoUndef) + , geoId2(GeoEnum::GeoUndef) {} ~DrawSketchHandlerFillet() override { Gui::Selection().rmvSelectionGate(); } - enum SelectMode +private: + void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override { - STATUS_SEEK_First, - STATUS_SEEK_Second - }; - - void mouseMove(Base::Vector2d onSketchPos) override - { - Q_UNUSED(onSketchPos); + switch (state()) { + case SelectMode::SeekFirst: { + vtId = getPreselectPoint(); + geoId1 = getPreselectCurve(); + firstPos = onSketchPos; + } break; + case SelectMode::SeekSecond: { + geoId2 = getPreselectCurve(); + secondPos = onSketchPos; + } break; + default: + break; + } } - bool pressButton(Base::Vector2d onSketchPos) override + void executeCommands() override { - Q_UNUSED(onSketchPos); - return true; - } + SketchObject* obj = sketchgui->getSketchObject(); - bool releaseButton(Base::Vector2d onSketchPos) override - { bool construction = false; - int VtId = getPreselectPoint(); - if (Mode == STATUS_SEEK_First && VtId != -1) { + bool isChamfer = constructionMethod() == ConstructionMethod::Chamfer; + + if (vtId != -1) { int GeoId; - Sketcher::PointPos PosId = Sketcher::PointPos::none; - sketchgui->getSketchObject()->getGeoVertexIndex(VtId, GeoId, PosId); - const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(GeoId); - if (geom->is() - && (PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end)) { + PointPos PosId = PointPos::none; + obj->getGeoVertexIndex(vtId, GeoId, PosId); + const Part::Geometry* geom = obj->getGeometry(GeoId); + if (isLineSegment(*geom) && (PosId == PointPos::start || PosId == PointPos::end)) { // guess fillet radius double radius = -1; std::vector GeoIdList; std::vector PosIdList; - sketchgui->getSketchObject()->getDirectlyCoincidentPoints(GeoId, - PosId, - GeoIdList, - PosIdList); + obj->getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList); if (GeoIdList.size() == 2 && GeoIdList[0] >= 0 && GeoIdList[1] >= 0) { - const Part::Geometry* geom1 = - sketchgui->getSketchObject()->getGeometry(GeoIdList[0]); - const Part::Geometry* geom2 = - sketchgui->getSketchObject()->getGeometry(GeoIdList[1]); - construction = Sketcher::GeometryFacade::getConstruction(geom1) - && Sketcher::GeometryFacade::getConstruction(geom2); - if (geom1->is() && geom2->is()) { - const Part::GeomLineSegment* lineSeg1 = - static_cast(geom1); - const Part::GeomLineSegment* lineSeg2 = - static_cast(geom2); - Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint(); - Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint(); - if (PosIdList[0] == Sketcher::PointPos::end) { + const Part::Geometry* geo1 = obj->getGeometry(GeoIdList[0]); + const Part::Geometry* geo2 = obj->getGeometry(GeoIdList[1]); + + construction = GeometryFacade::getConstruction(geo1) + && GeometryFacade::getConstruction(geo2); + + if (isLineSegment(*geo1) && isLineSegment(*geo2)) { + auto* line1 = static_cast(geo1); + auto* line2 = static_cast(geo2); + Base::Vector3d dir1 = line1->getEndPoint() - line1->getStartPoint(); + Base::Vector3d dir2 = line2->getEndPoint() - line2->getStartPoint(); + if (PosIdList[0] == PointPos::end) { dir1 *= -1; } - if (PosIdList[1] == Sketcher::PointPos::end) { + if (PosIdList[1] == PointPos::end) { dir2 *= -1; } double l1 = dir1.Length(); @@ -169,55 +197,197 @@ public: } } if (radius < 0) { - return false; + return; } - int currentgeoid = getHighestCurveIndex(); + int filletGeoId = getHighestCurveIndex() + (isChamfer ? 2 : 1); // create fillet at point try { - bool pointFillet = (filletType == 1); Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); - Gui::cmdAppObjectArgs(sketchgui->getObject(), - "fillet(%d,%d,%f,%s,%s)", + Gui::cmdAppObjectArgs(obj, + "fillet(%d,%d,%f,%s,%s,%s)", GeoId, static_cast(PosId), radius, "True", - pointFillet ? "True" : "False"); + preserveCorner ? "True" : "False", + isChamfer ? "True" : "False"); if (construction) { - Gui::cmdAppObjectArgs(sketchgui->getObject(), - "toggleConstruction(%d) ", - currentgeoid + 1); + Gui::cmdAppObjectArgs(obj, "toggleConstruction(%d) ", filletGeoId); } Gui::Command::commitCommand(); } catch (const Base::Exception& e) { Gui::NotifyUserError( - sketchgui->getObject(), + obj, QT_TRANSLATE_NOOP("Notifications", "Failed to create fillet"), e.what()); Gui::Command::abortCommand(); } - tryAutoRecomputeIfNotSolve( - static_cast(sketchgui->getObject())); + tryAutoRecomputeIfNotSolve(obj); } - return true; } - int GeoId = getPreselectCurve(); - if (GeoId > -1) { - const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(GeoId); - if (geom->isDerivedFrom()) { - if (Mode == STATUS_SEEK_First) { - firstCurve = GeoId; - firstPos = onSketchPos; - Mode = STATUS_SEEK_Second; - // add the line to the selection + else { + Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f); + Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f); + + const Part::Geometry* geo1 = obj->getGeometry(geoId1); + const Part::Geometry* geo2 = obj->getGeometry(geoId2); + + construction = + GeometryFacade::getConstruction(geo1) && GeometryFacade::getConstruction(geo2); + + double radius = 0; + + if (isLineSegment(*geo1) && isLineSegment(*geo2)) { + // guess fillet radius + auto* line1 = static_cast(geo1); + auto* line2 = static_cast(geo2); + + radius = Part::suggestFilletRadius(line1, line2, refPnt1, refPnt2); + if (radius < 0) { + return; + } + } + + int filletGeoId = getHighestCurveIndex() + (isChamfer ? 2 : 1); + + // create fillet between lines + try { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); + Gui::cmdAppObjectArgs( + obj, + "fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s,%s)", + geoId1, + geoId2, + firstPos.x, + firstPos.y, + secondPos.x, + secondPos.y, + radius, + "True", + preserveCorner ? "True" : "False", + isChamfer ? "True" : "False"); + Gui::Command::commitCommand(); + } + catch (const Base::CADKernelError& e) { + if (e.getTranslatable()) { + Gui::TranslatedUserError(sketchgui, + QObject::tr("CAD Kernel Error"), + QObject::tr(e.getMessage().c_str())); + } + Gui::Selection().clearSelection(); + Gui::Command::abortCommand(); + } + catch (const Base::ValueError& e) { + Gui::TranslatedUserError(sketchgui, + QObject::tr("Value Error"), + QObject::tr(e.getMessage().c_str())); + Gui::Selection().clearSelection(); + Gui::Command::abortCommand(); + } + + tryAutoRecompute(obj); + + if (construction) { + Gui::cmdAppObjectArgs(obj, "toggleConstruction(%d) ", filletGeoId); + } + + + Gui::Selection().clearSelection(); + } + } + + std::string getToolName() const override + { + return "DSH_Fillet"; + } + + QString getCrosshairCursorSVGName() const override + { + Gui::Selection().rmvSelectionGate(); + Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject())); + + if (constructionMethod() == DrawSketchHandlerFillet::ConstructionMethod::Fillet) { + if (preserveCorner) { + return QString::fromLatin1("Sketcher_Pointer_Create_PointFillet"); + } + else { + return QString::fromLatin1("Sketcher_Pointer_Create_Fillet"); + } + } + else { + if (preserveCorner) { + return QString::fromLatin1("Sketcher_Pointer_Create_PointChamfer"); + } + else { + return QString::fromLatin1("Sketcher_Pointer_Create_Chamfer"); + } + } + } + + std::unique_ptr createWidget() const override + { + return std::make_unique(); + } + + bool isWidgetVisible() const override + { + return true; + }; + + QPixmap getToolIcon() const override + { + return Gui::BitmapFactory().pixmap("Sketcher_CreateFillet"); + } + + QString getToolWidgetText() const override + { + return QString(QObject::tr("Fillet/Chamfer parameters")); + } + + bool canGoToNextMode() override + { + if (state() == SelectMode::SeekFirst) { + if (vtId != -1) { + return true; + } + if (geoId1 >= 0) { + const Part::Geometry* geo = sketchgui->getSketchObject()->getGeometry(geoId1); + if (geo->isDerivedFrom()) { + return true; + } + } + } + + if (state() == SelectMode::SeekSecond) { + if (geoId2 >= 0) { + const Part::Geometry* geo = sketchgui->getSketchObject()->getGeometry(geoId2); + if (geo->isDerivedFrom()) { + return true; + } + } + } + + return false; + } + + void onButtonPressed(Base::Vector2d onSketchPos) override + { + this->updateDataAndDrawToPosition(onSketchPos); + if (canGoToNextMode()) { + if (state() == SelectMode::SeekFirst) { + if (vtId != -1) { + setState(SelectMode::End); + } + else { + // add the edge to the selection std::stringstream ss; - ss << "Edge" << firstCurve + 1; + ss << "Edge" << geoId1 + 1; Gui::Selection().addSelection( sketchgui->getSketchObject()->getDocument()->getName(), sketchgui->getSketchObject()->getNameInDocument(), @@ -225,128 +395,66 @@ public: onSketchPos.x, onSketchPos.y, 0.f); - } - else if (Mode == STATUS_SEEK_Second) { - int secondCurve = GeoId; - Base::Vector2d secondPos = onSketchPos; - - Base::Vector3d refPnt1(firstPos.x, firstPos.y, 0.f); - Base::Vector3d refPnt2(secondPos.x, secondPos.y, 0.f); - - const Part::Geometry* geom1 = - sketchgui->getSketchObject()->getGeometry(firstCurve); - - double radius = 0; - - if (geom->is() && geom1->is()) { - // guess fillet radius - const Part::GeomLineSegment* lineSeg1 = - static_cast( - sketchgui->getSketchObject()->getGeometry(firstCurve)); - const Part::GeomLineSegment* lineSeg2 = - static_cast( - sketchgui->getSketchObject()->getGeometry(secondCurve)); - - radius = Part::suggestFilletRadius(lineSeg1, lineSeg2, refPnt1, refPnt2); - if (radius < 0) { - return false; - } - - construction = Sketcher::GeometryFacade::getConstruction(lineSeg1) - && Sketcher::GeometryFacade::getConstruction(lineSeg2); - } - else { // other supported curves - const Part::Geometry* geo1 = static_cast( - sketchgui->getSketchObject()->getGeometry(firstCurve)); - const Part::Geometry* geo2 = static_cast( - sketchgui->getSketchObject()->getGeometry(secondCurve)); - - construction = Sketcher::GeometryFacade::getConstruction(geo1) - && Sketcher::GeometryFacade::getConstruction(geo2); - } - - - int currentgeoid = getHighestCurveIndex(); - - // create fillet between lines - try { - bool pointFillet = (filletType == 1); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create fillet")); - Gui::cmdAppObjectArgs( - sketchgui->getObject(), - "fillet(%d,%d,App.Vector(%f,%f,0),App.Vector(%f,%f,0),%f,%s,%s)", - firstCurve, - secondCurve, - firstPos.x, - firstPos.y, - secondPos.x, - secondPos.y, - radius, - "True", - pointFillet ? "True" : "False"); - Gui::Command::commitCommand(); - } - catch (const Base::CADKernelError& e) { - if (e.getTranslatable()) { - Gui::TranslatedUserError(sketchgui, - QObject::tr("CAD Kernel Error"), - QObject::tr(e.getMessage().c_str())); - } - Gui::Selection().clearSelection(); - Gui::Command::abortCommand(); - Mode = STATUS_SEEK_First; - } - catch (const Base::ValueError& e) { - Gui::TranslatedUserError(sketchgui, - QObject::tr("Value Error"), - QObject::tr(e.getMessage().c_str())); - Gui::Selection().clearSelection(); - Gui::Command::abortCommand(); - Mode = STATUS_SEEK_First; - } - - tryAutoRecompute(static_cast(sketchgui->getObject())); - - if (construction) { - Gui::cmdAppObjectArgs(sketchgui->getObject(), - "toggleConstruction(%d) ", - currentgeoid + 1); - } - - - Gui::Selection().clearSelection(); - Mode = STATUS_SEEK_First; + moveToNextMode(); } } + else { + moveToNextMode(); + } } - - if (VtId < 0 && GeoId < 0) { // exit the fillet tool if the user clicked on empty space - sketchgui - ->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider - } - - return true; } + private: - QString getCrosshairCursorSVGName() const override - { - Gui::Selection().rmvSelectionGate(); - Gui::Selection().addSelectionGate(new FilletSelection(sketchgui->getObject())); - if (filletType == SimpleFillet) { - return QString::fromLatin1("Sketcher_Pointer_Create_Fillet"); - } - else { - return QString::fromLatin1("Sketcher_Pointer_Create_PointFillet"); - } + bool preserveCorner; + int vtId, geoId1, geoId2; + Base::Vector2d firstPos, secondPos; +}; + +template<> +void DSHFilletController::configureToolWidget() +{ + if (!init) { // Code to be executed only upon initialisation + QStringList names = {QStringLiteral("Fillet"), QStringLiteral("Chamfer")}; + toolWidget->setComboboxElements(WCombobox::FirstCombo, names); + + toolWidget->setComboboxItemIcon( + WCombobox::FirstCombo, + 0, + Gui::BitmapFactory().iconFromTheme("Sketcher_CreateFillet")); + toolWidget->setComboboxItemIcon( + WCombobox::FirstCombo, + 1, + Gui::BitmapFactory().iconFromTheme("Sketcher_CreateChamfer")); + + toolWidget->setCheckboxLabel( + WCheckbox::FirstBox, + QApplication::translate("TaskSketcherTool_c1_fillet", "Preserve corner (U)")); + toolWidget->setCheckboxToolTip( + WCheckbox::FirstBox, + QApplication::translate("TaskSketcherTool_c1_fillet", + "Preserves intersection point and most constraints")); + + toolWidget->setCheckboxIcon( + WCheckbox::FirstBox, + Gui::BitmapFactory().iconFromTheme("Sketcher_CreatePointFillet")); + } + syncCheckboxToHandler(WCheckbox::FirstBox, handler->preserveCorner); +} + +template<> +void DSHFilletController::adaptDrawingToCheckboxChange(int checkboxindex, bool value) +{ + Q_UNUSED(checkboxindex); + + switch (checkboxindex) { + case WCheckbox::FirstBox: + handler->preserveCorner = value; + break; } -protected: - int filletType; - SelectMode Mode; - int firstCurve; - Base::Vector2d firstPos; -}; + handler->updateCursor(); +} } // namespace SketcherGui diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 936b246e64..6572d92280 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -139,6 +139,7 @@ icons/geometry/Sketcher_CreateBSplineByInterpolation_Constr.svg icons/geometry/Sketcher_Create_Periodic_BSplineByInterpolation.svg icons/geometry/Sketcher_Create_Periodic_BSplineByInterpolation_Constr.svg + icons/geometry/Sketcher_CreateChamfer.svg icons/geometry/Sketcher_CreateCircle.svg icons/geometry/Sketcher_CreateCircle_Constr.svg icons/geometry/Sketcher_CreateEllipse_3points.svg @@ -229,6 +230,7 @@ icons/pointers/Sketcher_Pointer_Create_BSpline.svg icons/pointers/Sketcher_Pointer_Create_BSplineByInterpolation.svg icons/pointers/Sketcher_Pointer_Create_Circle.svg + icons/pointers/Sketcher_Pointer_Create_Chamfer.svg icons/pointers/Sketcher_Pointer_Create_EllipseByCenter.svg icons/pointers/Sketcher_Pointer_Create_Ellipse_3points.svg icons/pointers/Sketcher_Pointer_Create_Fillet.svg @@ -238,6 +240,7 @@ icons/pointers/Sketcher_Pointer_Create_Periodic_BSpline.svg icons/pointers/Sketcher_Pointer_Create_Periodic_BSplineByInterpolation.svg icons/pointers/Sketcher_Pointer_Create_Point.svg + icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg icons/pointers/Sketcher_Pointer_Create_PointFillet.svg icons/pointers/Sketcher_Pointer_Create_Translate.svg icons/pointers/Sketcher_Pointer_Create_Offset.svg diff --git a/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_CreateChamfer.svg b/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_CreateChamfer.svg new file mode 100644 index 0000000000..d2032be3c9 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/geometry/Sketcher_CreateChamfer.svg @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + [maxwxyz] + + + https://www.freecad.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreateArc.svg + + + FreeCAD LGPL2+ + + + 2023-12-19 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Chamfer.svg b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Chamfer.svg new file mode 100644 index 0000000000..6ed3ce1ce3 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_Chamfer.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg new file mode 100644 index 0000000000..6f1587fb76 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/pointers/Sketcher_Pointer_Create_PointChamfer.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 09e656f69f..8bcacb0dcf 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -4161,7 +4161,8 @@ void ViewProviderSketch::generateContextMenu() << "Separator" << "Sketcher_ToggleConstruction" << "Separator" - << "Sketcher_CreatePointFillet" + << "Sketcher_CreateFillet" + << "Sketcher_CreateChamfer" << "Sketcher_Trimming" << "Sketcher_Extend" << "Separator" diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index e507189983..7ef3c44373 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -375,8 +375,8 @@ void SketcherAddWorkspaceFillets(T& geom); template<> inline void SketcherAddWorkspaceFillets(Gui::MenuItem& geom) { - geom << "Sketcher_CreatePointFillet" - << "Sketcher_CreateFillet"; + geom << "Sketcher_CreateFillet" + << "Sketcher_CreateChamfer"; } template<>