| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #ifndef SKETCHERGUI_DrawSketchHandlerExtend_H
|
| | #define SKETCHERGUI_DrawSketchHandlerExtend_H
|
| |
|
| | #include <Gui/Notifications.h>
|
| | #include <Gui/Selection/SelectionFilter.h>
|
| | #include <Gui/Command.h>
|
| | #include <Gui/CommandT.h>
|
| |
|
| | #include <Mod/Sketcher/App/SketchObject.h>
|
| |
|
| | #include "DrawSketchHandler.h"
|
| | #include "GeometryCreationMode.h"
|
| | #include "Utils.h"
|
| | #include "ViewProviderSketch.h"
|
| | #include "SnapManager.h"
|
| |
|
| |
|
| | namespace SketcherGui
|
| | {
|
| |
|
| | extern GeometryCreationMode geometryCreationMode;
|
| |
|
| | class ExtendSelection: public Gui::SelectionFilterGate
|
| | {
|
| | App::DocumentObject* object;
|
| |
|
| | public:
|
| | explicit ExtendSelection(App::DocumentObject* obj)
|
| | : Gui::SelectionFilterGate(nullPointer())
|
| | , object(obj)
|
| | , disabled(false)
|
| | {}
|
| |
|
| | bool allow(App::Document* , App::DocumentObject* pObj, const char* sSubName) override
|
| | {
|
| | if (pObj != this->object) {
|
| | return false;
|
| | }
|
| | if (Base::Tools::isNullOrEmpty(sSubName)) {
|
| | 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<Sketcher::SketchObject*>(object);
|
| | const Part::Geometry* geom = Sketch->getGeometry(GeoId);
|
| | if (geom->is<Part::GeomLineSegment>() || geom->is<Part::GeomArcOfCircle>()) {
|
| | return true;
|
| | }
|
| | }
|
| | return false;
|
| | }
|
| |
|
| | void setDisabled(bool isDisabled)
|
| | {
|
| | disabled = isDisabled;
|
| | }
|
| |
|
| | protected:
|
| | bool disabled;
|
| | };
|
| |
|
| |
|
| | class DrawSketchHandlerExtend: public DrawSketchHandler
|
| | {
|
| | Q_DECLARE_TR_FUNCTIONS(SketcherGui::DrawSketchHandlerExtend)
|
| |
|
| | public:
|
| | DrawSketchHandlerExtend()
|
| | : Mode(STATUS_SEEK_First)
|
| | , EditCurve(2)
|
| | , BaseGeoId(-1)
|
| | , ExtendFromStart(false)
|
| | , SavedExtendFromStart(false)
|
| | , Increment(0)
|
| | {}
|
| |
|
| | ~DrawSketchHandlerExtend() override
|
| | {
|
| | Gui::Selection().rmvSelectionGate();
|
| | }
|
| | enum SelectMode
|
| | {
|
| | STATUS_SEEK_First,
|
| | STATUS_SEEK_Second,
|
| | };
|
| |
|
| | void mouseMove(SnapManager::SnapHandle snapHandle) override
|
| | {
|
| | Base::Vector2d onSketchPos = snapHandle.compute();
|
| |
|
| | using std::numbers::pi;
|
| |
|
| | if (Mode == STATUS_SEEK_Second) {
|
| | const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
| | if (geom->is<Part::GeomLineSegment>()) {
|
| | const Part::GeomLineSegment* lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
| |
|
| | Base::Vector3d start3d = lineSeg->getStartPoint();
|
| | Base::Vector3d end3d = lineSeg->getEndPoint();
|
| |
|
| | Base::Vector2d startPoint = Base::Vector2d(start3d.x, start3d.y);
|
| | Base::Vector2d endPoint = Base::Vector2d(end3d.x, end3d.y);
|
| | Base::Vector2d recenteredLine = endPoint - startPoint;
|
| | Base::Vector2d recenteredPoint = onSketchPos - startPoint;
|
| | Base::Vector2d projection;
|
| | projection.ProjectToLine(recenteredPoint, recenteredLine);
|
| | if (recenteredPoint.Length() < recenteredPoint.Distance(recenteredLine)) {
|
| | EditCurve[0] = startPoint + projection;
|
| | EditCurve[1] = endPoint;
|
| | }
|
| | else {
|
| | EditCurve[0] = startPoint;
|
| | EditCurve[1] = startPoint + projection;
|
| | }
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | bool inCurve
|
| | = (projection.Length() < recenteredLine.Length()
|
| | && projection.GetAngle(recenteredLine) < 0.1);
|
| |
|
| |
|
| |
|
| | if (inCurve) {
|
| | Increment = SavedExtendFromStart ? -1 * projection.Length()
|
| | : projection.Length() - recenteredLine.Length();
|
| | ExtendFromStart = SavedExtendFromStart;
|
| | }
|
| | else {
|
| | ExtendFromStart = onSketchPos.Distance(startPoint)
|
| | < onSketchPos.Distance(endPoint);
|
| | Increment = ExtendFromStart ? projection.Length()
|
| | : projection.Length() - recenteredLine.Length();
|
| | }
|
| | drawEdit(EditCurve);
|
| | }
|
| | else if (geom->is<Part::GeomArcOfCircle>()) {
|
| | const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(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);
|
| |
|
| |
|
| | double modStartAngle = start;
|
| | double modArcAngle = end - start;
|
| | bool outOfArc = arcHalf.GetAngle(angle) * 2.0 > arcAngle;
|
| | if (ExtendFromStart) {
|
| | bool isCCWFromStart = crossProduct(angle, startAngle) < 0;
|
| | if (outOfArc) {
|
| | if (isCCWFromStart) {
|
| | modStartAngle -= 2 * pi - angleToStartAngle;
|
| | modArcAngle += 2 * pi - angleToStartAngle;
|
| | }
|
| | else {
|
| | modStartAngle -= angleToStartAngle;
|
| | modArcAngle += angleToStartAngle;
|
| | }
|
| | }
|
| | else {
|
| | if (isCCWFromStart) {
|
| | modStartAngle += angleToStartAngle;
|
| | modArcAngle -= angleToStartAngle;
|
| | }
|
| | else {
|
| | modStartAngle += 2 * pi - angleToStartAngle;
|
| | modArcAngle -= 2 * pi - angleToStartAngle;
|
| | }
|
| | }
|
| | }
|
| | else {
|
| | bool isCWFromEnd = crossProduct(angle, endAngle) >= 0;
|
| | if (outOfArc) {
|
| | if (isCWFromEnd) {
|
| | modArcAngle += 2 * pi - angleToEndAngle;
|
| | }
|
| | else {
|
| | modArcAngle += angleToEndAngle;
|
| | }
|
| | }
|
| | else {
|
| | if (isCWFromEnd) {
|
| | modArcAngle -= angleToEndAngle;
|
| | }
|
| | else {
|
| | modArcAngle -= 2 * pi - angleToEndAngle;
|
| | }
|
| | }
|
| | }
|
| | Increment = modArcAngle - (end - start);
|
| | 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)
|
| | );
|
| | }
|
| | drawEdit(EditCurve);
|
| | }
|
| | int curveId = getPreselectCurve();
|
| | if (BaseGeoId != curveId) {
|
| | seekAndRenderAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f, 0.f));
|
| | }
|
| | }
|
| | }
|
| |
|
| | bool pressButton(Base::Vector2d onSketchPos) override
|
| | {
|
| | Q_UNUSED(onSketchPos);
|
| | return true;
|
| | }
|
| |
|
| | bool releaseButton(Base::Vector2d onSketchPos) override
|
| | {
|
| | Q_UNUSED(onSketchPos);
|
| | if (Mode == STATUS_SEEK_First) {
|
| | BaseGeoId = getPreselectCurve();
|
| | if (BaseGeoId > -1) {
|
| | const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId);
|
| | if (geom->is<Part::GeomLineSegment>()) {
|
| | const Part::GeomLineSegment* seg = static_cast<const Part::GeomLineSegment*>(geom);
|
| | Base::Vector3d start3d = seg->getStartPoint();
|
| | Base::Vector3d end3d = seg->getEndPoint();
|
| | Base::Vector2d start = Base::Vector2d(start3d.x, start3d.y);
|
| | Base::Vector2d end = Base::Vector2d(end3d.x, end3d.y);
|
| | SavedExtendFromStart = (onSketchPos.Distance(start) < onSketchPos.Distance(end));
|
| | ExtendFromStart = SavedExtendFromStart;
|
| | Mode = STATUS_SEEK_Second;
|
| | }
|
| | else if (geom->is<Part::GeomArcOfCircle>()) {
|
| | const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(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);
|
| |
|
| | EditCurve.resize(31);
|
| | Mode = STATUS_SEEK_Second;
|
| | }
|
| | filterGate->setDisabled(true);
|
| | }
|
| | }
|
| | else if (Mode == STATUS_SEEK_Second) {
|
| | try {
|
| | Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend edge"));
|
| | Gui::cmdAppObjectArgs(
|
| | sketchgui->getObject(),
|
| | "extend(%d, %f, %d)\n",
|
| | BaseGeoId,
|
| | Increment,
|
| | ExtendFromStart ? static_cast<int>(Sketcher::PointPos::start)
|
| | : static_cast<int>(Sketcher::PointPos::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();
|
| | }
|
| |
|
| |
|
| | if (!SugConstr.empty()) {
|
| | createAutoConstraints(
|
| | SugConstr,
|
| | BaseGeoId,
|
| | (ExtendFromStart) ? Sketcher::PointPos::start : Sketcher::PointPos::end
|
| | );
|
| | SugConstr.clear();
|
| | }
|
| | bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true);
|
| |
|
| | if (continuousMode) {
|
| |
|
| | Mode = STATUS_SEEK_First;
|
| | filterGate->setDisabled(false);
|
| | EditCurve.clear();
|
| | drawEdit(EditCurve);
|
| | EditCurve.resize(2);
|
| | applyCursor();
|
| | |
| | |
| | |
| |
|
| | }
|
| | else {
|
| | sketchgui->purgeHandler();
|
| |
|
| | }
|
| | }
|
| | catch (const Base::Exception&) {
|
| | Gui::NotifyError(
|
| | sketchgui,
|
| | QT_TRANSLATE_NOOP("Notifications", "Error"),
|
| | QT_TRANSLATE_NOOP("Notifications", "Failed to extend edge")
|
| | );
|
| | Gui::Command::abortCommand();
|
| | }
|
| | }
|
| | else {
|
| | BaseGeoId = -1;
|
| | sketchgui->purgeHandler();
|
| | }
|
| |
|
| | updateHint();
|
| | return true;
|
| | }
|
| |
|
| | private:
|
| | void activated() override
|
| | {
|
| | Gui::Selection().clearSelection();
|
| | Gui::Selection().rmvSelectionGate();
|
| | filterGate = new ExtendSelection(sketchgui->getObject());
|
| | Gui::Selection().addSelectionGate(filterGate);
|
| | }
|
| |
|
| | QString getCrosshairCursorSVGName() const override
|
| | {
|
| | return QStringLiteral("Sketcher_Pointer_Extension");
|
| | }
|
| |
|
| | protected:
|
| | SelectMode Mode;
|
| | std::vector<Base::Vector2d> EditCurve;
|
| | int BaseGeoId;
|
| | ExtendSelection* filterGate = nullptr;
|
| | bool ExtendFromStart;
|
| | bool SavedExtendFromStart;
|
| | double Increment;
|
| | std::vector<AutoConstraint> SugConstr;
|
| |
|
| | public:
|
| | std::list<Gui::InputHint> getToolHints() const override
|
| | {
|
| | using enum Gui::InputHint::UserInput;
|
| |
|
| | return Gui::lookupHints<SelectMode>(
|
| | Mode,
|
| | {
|
| | {.state = STATUS_SEEK_First,
|
| | .hints =
|
| | {
|
| | {tr("%1 pick edge to extend", "Sketcher Extend: hint"), {MouseLeft}},
|
| | }},
|
| | {.state = STATUS_SEEK_Second,
|
| | .hints =
|
| | {
|
| | {tr("%1 set extension length", "Sketcher Extend: hint"), {MouseLeft}},
|
| | }},
|
| | });
|
| | }
|
| |
|
| | private:
|
| | int crossProduct(Base::Vector2d& vec1, Base::Vector2d& vec2)
|
| | {
|
| | return vec1.x * vec2.y - vec1.y * vec2.x;
|
| | }
|
| | };
|
| |
|
| | }
|
| |
|
| | #endif
|
| |
|