Sketcher: Snap: initial implementation: - creation of SnapManager class. - Move grid snap to this new class. - Add snap to object. - Add snap at angle.

This commit is contained in:
Paddle
2023-03-15 11:08:54 +01:00
committed by abdullahtahiriyo
parent 21c2eb6014
commit 0f274c2e07
10 changed files with 1526 additions and 73 deletions

View File

@@ -144,6 +144,8 @@ SET(SketcherGui_SRCS
SketchRectangularArrayDialog.cpp
SketcherRegularPolygonDialog.h
SketcherRegularPolygonDialog.cpp
SnapManager.cpp
SnapManager.h
TaskDlgEditSketch.cpp
TaskDlgEditSketch.h
ViewProviderPython.cpp

View File

@@ -995,7 +995,7 @@ public:
updateCheckBox(checkbox, propvalue);
};
updateCheckBox(gridSnap, sketchView->getSnapMode() == SnapMode::SnapToGrid);
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
updateCheckBoxFromProperty(gridAutoSpacing, sketchView->GridAuto);
@@ -1005,10 +1005,6 @@ public:
void languageChange()
{
gridSnap->setText(tr("Grid Snap"));
gridSnap->setToolTip(tr("New points will snap to the nearest grid line.\nPoints must be set closer than a fifth of the grid spacing to a grid line to snap."));
gridSnap->setStatusTip(gridSnap->toolTip());
gridAutoSpacing->setText(tr("Grid Auto Spacing"));
gridAutoSpacing->setToolTip(tr("Resize grid automatically depending on zoom."));
gridAutoSpacing->setStatusTip(gridAutoSpacing->toolTip());
@@ -1020,8 +1016,6 @@ public:
protected:
QWidget* createWidget(QWidget* parent) override
{
gridSnap = new QCheckBox();
gridAutoSpacing = new QCheckBox();
sizeLabel = new QLabel();
@@ -1034,27 +1028,12 @@ protected:
QWidget* gridSizeW = new QWidget(parent);
auto* layout = new QGridLayout(gridSizeW);
layout->addWidget(gridSnap, 0, 0);
layout->addWidget(gridAutoSpacing, 1, 0);
layout->addWidget(sizeLabel, 2, 0);
layout->addWidget(gridSizeBox, 2, 1);
layout->addWidget(gridAutoSpacing, 0, 0, 1, 2);
layout->addWidget(sizeLabel, 1, 0);
layout->addWidget(gridSizeBox, 1, 1);
languageChange();
QObject::connect(gridSnap, &QCheckBox::stateChanged, [this](int state) {
auto* sketchView = getView();
if(sketchView) {
if(state == Qt::Checked) {
sketchView->setSnapMode(SnapMode::SnapToGrid);
}
else {
sketchView->setSnapMode(SnapMode::None);
}
}
});
QObject::connect(gridAutoSpacing, &QCheckBox::stateChanged, [this](int state) {
auto* sketchView = getView();
@@ -1086,7 +1065,6 @@ private:
}
private:
QCheckBox * gridSnap;
QCheckBox * gridAutoSpacing;
QLabel * sizeLabel;
Gui::QuantitySpinBox * gridSizeBox;
@@ -1213,6 +1191,250 @@ bool CmdSketcherGrid::isActive()
return false;
}
/* Snap tool */
class SnapSpaceAction : public QWidgetAction
{
public:
SnapSpaceAction(QObject* parent) : QWidgetAction(parent) {
setEnabled(false);
}
void updateWidget() {
auto* sketchView = getView();
if (sketchView) {
auto updateCheckBox = [](QCheckBox* checkbox, bool value) {
auto checked = checkbox->checkState() == Qt::Checked;
if (value != checked) {
const QSignalBlocker blocker(checkbox);
checkbox->setChecked(value);
}
};
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
updateCheckBox(snapToObjects, hGrp->GetBool("SnapToObjects", true));
updateCheckBox(snapToGrid, hGrp->GetBool("SnapToGrid", false));
snapAngle->setValue(hGrp->GetFloat("SnapAngle", 5.0));
bool snapActivated = hGrp->GetBool("Snap", true);
snapToObjects->setEnabled(snapActivated);
snapToGrid->setEnabled(snapActivated);
angleLabel->setEnabled(snapActivated);
snapAngle->setEnabled(snapActivated);
}
}
void languageChange()
{
snapToObjects->setText(tr("Snap to objects"));
snapToObjects->setToolTip(tr("New points will snap to the currently preselected object. It will also snap to the middle of lines and arcs."));
snapToObjects->setStatusTip(snapToObjects->toolTip());
snapToGrid->setText(tr("Snap to Grid"));
snapToGrid->setToolTip(tr("New points will snap to the nearest grid line.\nPoints must be set closer than a fifth of the grid spacing to a grid line to snap."));
snapToGrid->setStatusTip(snapToGrid->toolTip());
angleLabel->setText(tr("Snap angle"));
snapAngle->setToolTip(tr("Angular step for tools that use 'Snap at Angle' (line for instance). Hold CTRL to enable 'Snap at Angle'. The angle start from the East axis (horizontal right)"));
}
protected:
QWidget* createWidget(QWidget* parent) override
{
snapToObjects = new QCheckBox();
snapToGrid = new QCheckBox();
angleLabel = new QLabel();
snapAngle = new Gui::QuantitySpinBox();
snapAngle->setProperty("unit", QVariant(QStringLiteral("deg")));
snapAngle->setObjectName(QStringLiteral("snapAngle"));
snapAngle->setMaximum(99999999.0);
snapAngle->setMinimum(0);
QWidget* snapW = new QWidget(parent);
auto* layout = new QGridLayout(snapW);
layout->addWidget(snapToGrid, 0, 0, 1, 2);
layout->addWidget(snapToObjects, 1, 0, 1, 2);
layout->addWidget(angleLabel, 2, 0);
layout->addWidget(snapAngle, 2, 1);
languageChange();
QObject::connect(snapToObjects, &QCheckBox::stateChanged, [this](int state) {
auto* sketchView = getView();
if (sketchView) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
hGrp->SetBool("SnapToObjects", state == Qt::Checked);
}
});
QObject::connect(snapToGrid, &QCheckBox::stateChanged, [this](int state) {
auto* sketchView = getView();
if (sketchView) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
hGrp->SetBool("SnapToGrid", state == Qt::Checked);
}
});
QObject::connect(snapAngle, qOverload<double>(&Gui::QuantitySpinBox::valueChanged), [this](double val) {
auto* sketchView = getView();
if (sketchView) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
hGrp->SetFloat("SnapAngle", val);
}
});
return snapW;
}
private:
ViewProviderSketch* getView() {
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (doc) {
return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
}
return nullptr;
}
private:
QCheckBox* snapToObjects;
QCheckBox* snapToGrid;
QLabel* angleLabel;
Gui::QuantitySpinBox* snapAngle;
};
class CmdSketcherSnap : public Gui::Command
{
public:
CmdSketcherSnap();
virtual ~CmdSketcherSnap() {}
virtual const char* className() const
{
return "CmdSketcherSnap";
}
virtual void languageChange();
protected:
virtual void activated(int iMsg);
virtual bool isActive(void);
virtual Gui::Action* createAction(void);
private:
void updateIcon(bool value);
CmdSketcherSnap(const CmdSketcherSnap&) = delete;
CmdSketcherSnap(CmdSketcherSnap&&) = delete;
CmdSketcherSnap& operator= (const CmdSketcherSnap&) = delete;
CmdSketcherSnap& operator= (CmdSketcherSnap&&) = delete;
};
CmdSketcherSnap::CmdSketcherSnap()
: Command("Sketcher_Snap")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Toggle Snap");
sToolTipText = QT_TR_NOOP("Toggle all snapping functionalities. In the menu you can toggle individually 'Snap to Grid', 'Snap to Objects' and further snap settings");
sWhatsThis = "Sketcher_Snap";
sStatusTip = sToolTipText;
eType = 0;
}
void CmdSketcherSnap::updateIcon(bool value)
{
static QIcon active = Gui::BitmapFactory().iconFromTheme("Sketcher_Snap");
static QIcon inactive = Gui::BitmapFactory().iconFromTheme("Sketcher_Snap_Deactivated");
auto* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
pcAction->setIcon(value ? active : inactive);
}
void CmdSketcherSnap::activated(int iMsg)
{
Q_UNUSED(iMsg);
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
bool value = !hGrp->GetBool("Snap", true);
hGrp->SetBool("Snap", value);
updateIcon(value);
//Update the widget :
if (!_pcAction)
return;
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
auto* ssa = static_cast<SnapSpaceAction*>(a[0]);
ssa->updateWidget();
}
Gui::Action* CmdSketcherSnap::createAction()
{
auto* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
pcAction->setExclusive(false);
applyCommandData(this->className(), pcAction);
SnapSpaceAction* ssa = new SnapSpaceAction(pcAction);
pcAction->addAction(ssa);
_pcAction = pcAction;
QObject::connect(pcAction, &Gui::ActionGroup::aboutToShow, [ssa](QMenu* menu) {
Q_UNUSED(menu)
ssa->updateWidget();
});
// set the right pixmap
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
updateIcon(hGrp->GetBool("Snap", true));
return pcAction;
}
void CmdSketcherSnap::languageChange()
{
Command::languageChange();
if (!_pcAction)
return;
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
auto* ssa = static_cast<SnapSpaceAction*>(a[0]);
ssa->languageChange();
}
bool CmdSketcherSnap::isActive()
{
auto* vp = getInactiveHandlerEditModeSketchViewProvider();
if (vp) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
bool value = hGrp->GetBool("Snap", true);
updateIcon(value);
return true;
}
return false;
}
void CreateSketcherCommands()
{
@@ -1230,4 +1452,5 @@ void CreateSketcherCommands()
rcCmdMgr.addCommand(new CmdSketcherMergeSketches());
rcCmdMgr.addCommand(new CmdSketcherViewSection());
rcCmdMgr.addCommand(new CmdSketcherGrid());
rcCmdMgr.addCommand(new CmdSketcherSnap());
}

View File

@@ -106,6 +106,8 @@
<file>icons/general/Sketcher_ViewSketch.svg</file>
<file>icons/general/Sketcher_GridToggle.svg</file>
<file>icons/general/Sketcher_GridToggle_Deactivated.svg</file>
<file>icons/general/Sketcher_Snap.svg</file>
<file>icons/general/Sketcher_Snap_Deactivated.svg</file>
</qresource>
<qresource>
<file>icons/geometry/Sketcher_AlterFillet.svg</file>

View File

@@ -0,0 +1,386 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64px"
height="64px"
id="svg3364"
version="1.1"
sodipodi:docname="Sketcher_Snap.svg"
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview55"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
objecttolerance="10.0"
gridtolerance="10.0"
guidetolerance="10.0"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="11.765625"
inkscape:cx="39.861886"
inkscape:cy="27.580345"
inkscape:window-width="1725"
inkscape:window-height="1013"
inkscape:window-x="1213"
inkscape:window-y="293"
inkscape:window-maximized="0"
inkscape:current-layer="svg3364" />
<defs
id="defs3366">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient2571"
gradientUnits="userSpaceOnUse"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3352"
gradientUnits="userSpaceOnUse"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<linearGradient
id="linearGradient3593">
<stop
style="stop-color:#c8e0f9;stop-opacity:1"
offset="0"
id="stop3595" />
<stop
style="stop-color:#637dca;stop-opacity:1"
offset="1"
id="stop3597" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3354"
gradientUnits="userSpaceOnUse"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient3369"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3372"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3375"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient3380"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
cx="320.44025"
cy="113.23357"
fx="320.44025"
fy="113.23357"
r="19.571428" />
<linearGradient
xlink:href="#linearGradient3864"
id="linearGradient3914"
x1="6.94525"
y1="36.838673"
x2="48.691113"
y2="36.838673"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
<radialGradient
xlink:href="#linearGradient3794"
id="radialGradient3800"
cx="1"
cy="45"
fx="1"
fy="45"
r="41"
gradientTransform="matrix(0.93348213,-2.2905276e-8,0,0.28687573,0.06651751,32.090592)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#000000;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
gradientTransform="translate(0,-9)"
xlink:href="#linearGradient3777"
id="linearGradient3783"
x1="53.896763"
y1="51.179787"
x2="48"
y2="32"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3777">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop3779" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop3781" />
</linearGradient>
<linearGradient
gradientTransform="translate(22,-17)"
xlink:href="#linearGradient3767"
id="linearGradient3773"
x1="22.116516"
y1="55.717518"
x2="19"
y2="33"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3767">
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="0"
id="stop3769" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop3771" />
</linearGradient>
<linearGradient
gradientTransform="translate(-2,-11)"
xlink:href="#linearGradient3777-6"
id="linearGradient3783-3"
x1="53.896763"
y1="51.179787"
x2="48"
y2="32"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3777-6">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop3779-7" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop3781-5" />
</linearGradient>
<linearGradient
y2="32"
x2="48"
y1="51.179787"
x1="53.896763"
gradientTransform="translate(-24,-13)"
gradientUnits="userSpaceOnUse"
id="linearGradient3066"
xlink:href="#linearGradient3777-6" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
gradientUnits="userSpaceOnUse"
id="linearGradient3323-4"
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6" />
<linearGradient
id="linearGradient3836-9-3-7-2-7-7-2-6">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8-5-4-4-6-4-4-61" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1-6-0-5-1-0-5-8" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6"
id="linearGradient898"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
x1="-18"
y1="18"
x2="-22"
y2="5" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
gradientUnits="userSpaceOnUse"
id="linearGradient3323-3"
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6" />
</defs>
<metadata
id="metadata3369">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:date>2011-10-10</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Section.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.3798139,32.218121 H 61.620186"
id="path985-9" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.2324867,51.127858 H 61.472858"
id="path985-6" />
<path
style="fill:none;stroke:#4ce642;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 12.221742,61.620185 V 2.3798148"
id="path985-92" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 31.131479,61.620186 V 2.3798143"
id="path985-9-8" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 50.041216,61.620185 V 2.3798148"
id="path985-6-2" />
<path
style="fill:none;stroke:#4ce642;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.2324859,13.308385 H 61.472857"
id="path985" />
<g
transform="rotate(63.326802,46.528624,-2.5779449)"
id="g3529-7">
<path
id="path3491-7"
d="M 56,8 V 32"
style="fill:none;stroke:#172a04;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3491-4-7"
d="M 56,8 V 32"
style="fill:none;stroke:#73d216;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3491-4-1-3"
d="M 55,8 V 32"
style="fill:none;stroke:#8ae234;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.35922387,0.71507028,-0.71505986,0.35921864,53.759702,19.45672)"
id="g3797-7-2-9-5-4-9-5-96">
<path
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-6-9-4-1-2-4-1-3"
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
<path
style="fill:url(#linearGradient898);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-7-0-1-8-7-3-8-7-7"
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
</g>
<g
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.35922387,0.71507028,-0.71505986,0.35921864,32.313746,30.230346)"
id="g3797-7-2-9-5-4-9-5-96-5">
<path
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-6-9-4-1-2-4-1-3-7"
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
<path
style="fill:url(#linearGradient3323-4);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-7-0-1-8-7-3-8-7-7-5"
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
</g>
<g
id="g4273"
transform="translate(-89.528258,0.54055521)">
<path
style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 108.96148,19.97344 c 4.28933,11.161025 8.9416,20.384136 14.87384,32.552457 l 5.94954,-11.134131 12.749,12.154051 c 3.39402,0.638464 5.31688,-1.489816 4.75963,-4.504649 l -13.00398,-11.814076 8.49933,-6.629483 z"
id="path1052"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:#d3d7cf;fill-opacity:1;stroke:#ffffff;stroke-width:1.285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 112.59215,23.233162 c 3.72959,10.062766 6.25342,13.924184 11.41153,24.895126 l 5.26419,-9.586262 13.8638,13.109482 c 1.42608,0.530564 2.84842,-0.286358 2.07091,-2.088013 l -13.80942,-12.277572 7.29489,-5.92068 z"
id="path1052-8"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,377 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64px"
height="64px"
id="svg3364"
version="1.1"
sodipodi:docname="Sketcher_Snap_Deactivated.svg"
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview55"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
objecttolerance="10.0"
gridtolerance="10.0"
guidetolerance="10.0"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="5.8828125"
inkscape:cx="56.180611"
inkscape:cy="32.63745"
inkscape:window-width="1720"
inkscape:window-height="1112"
inkscape:window-x="743"
inkscape:window-y="334"
inkscape:window-maximized="0"
inkscape:current-layer="svg3364" />
<defs
id="defs3366">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient2571"
gradientUnits="userSpaceOnUse"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3352"
gradientUnits="userSpaceOnUse"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<linearGradient
id="linearGradient3593">
<stop
style="stop-color:#c8e0f9;stop-opacity:1"
offset="0"
id="stop3595" />
<stop
style="stop-color:#637dca;stop-opacity:1"
offset="1"
id="stop3597" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3354"
gradientUnits="userSpaceOnUse"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient3369"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3372"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3593"
id="radialGradient3375"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428" />
<radialGradient
xlink:href="#linearGradient3864"
id="radialGradient3380"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
cx="320.44025"
cy="113.23357"
fx="320.44025"
fy="113.23357"
r="19.571428" />
<linearGradient
xlink:href="#linearGradient3864"
id="linearGradient3914"
x1="6.94525"
y1="36.838673"
x2="48.691113"
y2="36.838673"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
<radialGradient
xlink:href="#linearGradient3794"
id="radialGradient3800"
cx="1"
cy="45"
fx="1"
fy="45"
r="41"
gradientTransform="matrix(0.93348213,-2.2905276e-8,0,0.28687573,0.06651751,32.090592)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#000000;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
gradientTransform="translate(0,-9)"
xlink:href="#linearGradient3777"
id="linearGradient3783"
x1="53.896763"
y1="51.179787"
x2="48"
y2="32"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3777">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop3779" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop3781" />
</linearGradient>
<linearGradient
gradientTransform="translate(22,-17)"
xlink:href="#linearGradient3767"
id="linearGradient3773"
x1="22.116516"
y1="55.717518"
x2="19"
y2="33"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3767">
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="0"
id="stop3769" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop3771" />
</linearGradient>
<linearGradient
gradientTransform="translate(-2,-11)"
xlink:href="#linearGradient3777-6"
id="linearGradient3783-3"
x1="53.896763"
y1="51.179787"
x2="48"
y2="32"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3777-6">
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="0"
id="stop3779-7" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop3781-5" />
</linearGradient>
<linearGradient
y2="32"
x2="48"
y1="51.179787"
x1="53.896763"
gradientTransform="translate(-24,-13)"
gradientUnits="userSpaceOnUse"
id="linearGradient3066"
xlink:href="#linearGradient3777-6" />
<linearGradient
y2="5"
x2="-22"
y1="18"
x1="-18"
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
gradientUnits="userSpaceOnUse"
id="linearGradient3323-3"
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-13" />
<linearGradient
id="linearGradient3836-9-3-7-2-7-7-2-13">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3838-8-5-4-4-6-4-4-8" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3840-1-6-0-5-1-0-5-9" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-13"
id="linearGradient1092"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
x1="-18"
y1="18"
x2="-22"
y2="5" />
</defs>
<metadata
id="metadata3369">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:date>2011-10-10</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Section.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.3798139,32.218121 H 61.620186"
id="path985-9" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.2324867,51.127858 H 61.472858"
id="path985-6" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 12.221742,61.620185 V 2.3798148"
id="path985-92" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 31.131479,61.620186 V 2.3798143"
id="path985-9-8" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 50.041216,61.620185 V 2.3798148"
id="path985-6-2" />
<path
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
d="M 2.2324859,13.308385 H 61.472857"
id="path985" />
<g
id="g4273"
transform="translate(-85.253531,6.1052318)">
<path
style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 108.96148,19.97344 c 4.28933,11.161025 8.9416,20.384136 14.87384,32.552457 l 5.94954,-11.134131 12.749,12.154051 c 3.39402,0.638464 5.31688,-1.489816 4.75963,-4.504649 l -13.00398,-11.814076 8.49933,-6.629483 z"
id="path1052"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:#d3d7cf;fill-opacity:1;stroke:#ffffff;stroke-width:1.285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 112.59215,23.233162 c 3.72959,10.062766 6.25342,13.924184 11.41153,24.895126 l 5.26419,-9.586262 13.8638,13.109482 c 1.42608,0.530564 2.84842,-0.286358 2.07091,-2.088013 l -13.80942,-12.277572 7.29489,-5.92068 z"
id="path1052-8"
sodipodi:nodetypes="cccccccc" />
</g>
<g
transform="rotate(63.436651,45.794909,-8.4224816)"
id="g3529-4">
<path
id="path3491-6"
d="M 56,8 V 32"
style="fill:none;stroke:#2e3436;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3491-4-07"
d="M 56,8 V 32"
style="fill:none;stroke:#d3d7cf;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3491-4-1-17"
d="M 55,8 V 32"
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.35785226,0.71575769,-0.71574725,0.35784704,48.084512,16.896405)"
id="g3797-7-2-9-5-4-9-5-4">
<path
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-6-9-4-1-2-4-1-4"
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
<path
style="fill:url(#linearGradient1092);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-7-0-1-8-7-3-8-7-6"
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
</g>
<g
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.35785226,0.71575769,-0.71574725,0.35784704,26.61794,27.628894)"
id="g3797-7-2-9-5-4-9-5-4-0">
<path
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-6-9-4-1-2-4-1-4-1"
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
<path
style="fill:url(#linearGradient3323-3);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4250-7-0-1-8-7-3-8-7-6-6"
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,364 @@
/***************************************************************************
* Copyright (c) 2023 Pierre-Louis Boyer <pierrelouis.boyer@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QApplication>
#endif // #ifndef _PreComp_
#include <Mod/Sketcher/App/SketchObject.h>
#include "SnapManager.h"
#include "ViewProviderSketch.h"
using namespace SketcherGui;
using namespace Sketcher;
/************************************ Attorney *******************************************/
inline int ViewProviderSketchSnapAttorney::getPreselectPoint(const ViewProviderSketch& vp)
{
return vp.getPreselectPoint();
}
inline int ViewProviderSketchSnapAttorney::getPreselectCross(const ViewProviderSketch& vp)
{
return vp.getPreselectCross();
}
inline int ViewProviderSketchSnapAttorney::getPreselectCurve(const ViewProviderSketch& vp)
{
return vp.getPreselectCurve();
}
/**************************** ParameterObserver nested class *****************************/
SnapManager::ParameterObserver::ParameterObserver(SnapManager& client) : client(client)
{
initParameters();
subscribeToParameters();
}
SnapManager::ParameterObserver::~ParameterObserver()
{
unsubscribeToParameters();
}
void SnapManager::ParameterObserver::initParameters()
{
// static map to avoid substantial if/else branching
//
// key->first => String of parameter,
// key->second => Update function to be called for the parameter,
str2updatefunction = {
{"Snap",
[this](const std::string& param) {updateSnapParameter(param); }},
{"SnapToObjects",
[this](const std::string& param) {updateSnapToObjectParameter(param); }},
{"SnapToGrid",
[this](const std::string& param) {updateSnapToGridParameter(param); }},
{"SnapAngle",
[this](const std::string& param) {updateSnapAngleParameter(param); }},
};
for (auto& val : str2updatefunction) {
auto string = val.first;
auto function = val.second;
function(string);
}
}
void SnapManager::ParameterObserver::updateSnapParameter(const std::string& parametername)
{
ParameterGrp::handle hGrp = getParameterGrpHandle();
client.snapRequested = hGrp->GetBool(parametername.c_str(), true);
}
void SnapManager::ParameterObserver::updateSnapToObjectParameter(const std::string& parametername)
{
ParameterGrp::handle hGrp = getParameterGrpHandle();
client.snapToObjectsRequested = hGrp->GetBool(parametername.c_str(), true);
}
void SnapManager::ParameterObserver::updateSnapToGridParameter(const std::string& parametername)
{
ParameterGrp::handle hGrp = getParameterGrpHandle();
client.snapToGridRequested = hGrp->GetBool(parametername.c_str(), false);
}
void SnapManager::ParameterObserver::updateSnapAngleParameter(const std::string& parametername)
{
ParameterGrp::handle hGrp = getParameterGrpHandle();
client.snapAngle = fmod(hGrp->GetFloat(parametername.c_str(), 5.) * M_PI / 180, 2 * M_PI);
}
void SnapManager::ParameterObserver::subscribeToParameters()
{
try {
ParameterGrp::handle hGrp = getParameterGrpHandle();
hGrp->Attach(this);
}
catch (const Base::ValueError& e) { // ensure that if parameter strings are not well-formed, the exception is not propagated
Base::Console().Error("SnapManager: Malformed parameter string: %s\n", e.what());
}
}
void SnapManager::ParameterObserver::unsubscribeToParameters()
{
try {
ParameterGrp::handle hGrp = getParameterGrpHandle();
hGrp->Detach(this);
}
catch (const Base::ValueError& e) {// ensure that if parameter strings are not well-formed, the program is not terminated when calling the noexcept destructor.
Base::Console().Error("SnapManager: Malformed parameter string: %s\n", e.what());
}
}
void SnapManager::ParameterObserver::OnChange(Base::Subject<const char*>& rCaller, const char* sReason)
{
(void)rCaller;
auto key = str2updatefunction.find(sReason);
if (key != str2updatefunction.end()) {
auto string = key->first;
auto function = key->second;
function(string);
}
}
ParameterGrp::handle SnapManager::ParameterObserver::getParameterGrpHandle()
{
return App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
}
//**************************** SnapManager class ******************************
SnapManager::SnapManager(ViewProviderSketch &vp):viewProvider(vp), angleSnapEnabled(false), referencePoint(Base::Vector2d(0.,0.)), lastMouseAngle(0.0)
{
// Create parameter observer and initialise watched parameters
pObserver = std::make_unique<SnapManager::ParameterObserver>(*this);
}
SnapManager::~SnapManager() {}
bool SnapManager::snap(double& x, double& y)
{
if (!snapRequested)
{
return false;
}
//In order of priority :
// 1 - Snap at an angle
if (angleSnapEnabled && QApplication::keyboardModifiers() == Qt::ControlModifier) {
return snapAtAngle(x, y);
}
else {
lastMouseAngle = 0.0;
}
// 2 - Snap to objects
if (snapToObjectsRequested
&& snapToObject(x, y)) {
return true;
}
// 3 - Snap to grid
if (snapToGridRequested /*&& viewProvider.ShowGrid.getValue() */ ) { //Snap to grid is enabled even if the grid is not visible.
return snapToGrid(x, y);
}
return false;
}
bool SnapManager::snapAtAngle(double& x, double& y)
{
Base::Vector2d pointToOverride(x, y);
double length = (pointToOverride - referencePoint).Length();
double angle1 = (pointToOverride - referencePoint).Angle();
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI;
lastMouseAngle = abs(angle1 - lastMouseAngle) < abs(angle2 - lastMouseAngle) ? angle1 : angle2;
double angle = round(lastMouseAngle / snapAngle) * snapAngle;
pointToOverride = referencePoint + length * Base::Vector2d(cos(angle), sin(angle));
x = pointToOverride.x;
y = pointToOverride.y;
return true;
}
bool SnapManager::snapToObject(double& x, double& y)
{
Sketcher::SketchObject* Obj = viewProvider.getSketchObject();
int geoId = GeoEnum::GeoUndef;
Sketcher::PointPos posId = Sketcher::PointPos::none;
int VtId = ViewProviderSketchSnapAttorney::getPreselectPoint(viewProvider);
int CrsId = ViewProviderSketchSnapAttorney::getPreselectCross(viewProvider);
int CrvId = ViewProviderSketchSnapAttorney::getPreselectCurve(viewProvider);
if (CrsId == 0 || VtId >= 0) {
if (CrsId == 0) {
geoId = Sketcher::GeoEnum::RtPnt;
posId = Sketcher::PointPos::start;
}
else if (VtId >= 0) {
Obj->getGeoVertexIndex(VtId, geoId, posId);
}
x = Obj->getPoint(geoId, posId).x;
y = Obj->getPoint(geoId, posId).y;
return true;
}
else if (CrsId == 1) { //H_Axis
y = 0;
return true;
}
else if (CrsId == 2) { //V_Axis
x = 0;
return true;
}
else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) { //Curves
const Part::Geometry* geo = Obj->getGeometry(CrvId);
Base::Vector3d pointToOverride(x, y, 0.);
double pointParam = 0.0;
auto curve = dynamic_cast<const Part::GeomCurve*>(geo);
if (curve) {
try {
curve->closestParameter(pointToOverride, pointParam);
pointToOverride = curve->pointAtParameter(pointParam);
}
catch (Base::CADKernelError& e) {
e.ReportException();
return false;
}
//If it is a line, then we check if we need to snap to the middle.
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment* line = static_cast<const Part::GeomLineSegment*>(geo);
snapToLineMiddle(pointToOverride, line);
}
//If it is an arc, then we check if we need to snap to the middle (not the center).
if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(geo);
snapToArcMiddle(pointToOverride, arc);
}
x = pointToOverride.x;
y = pointToOverride.y;
return true;
}
}
return false;
}
bool SnapManager::snapToGrid(double& x, double& y)
{
// Snap Tolerance in pixels
const double snapTol = viewProvider.getGridSize() / 5;
double tmpX = x, tmpY = y;
viewProvider.getClosestGridPoint(tmpX, tmpY);
bool snapped = false;
// Check if x within snap tolerance
if (x < tmpX + snapTol && x > tmpX - snapTol) {
x = tmpX; // Snap X Mouse Position
snapped = true;
}
// Check if y within snap tolerance
if (y < tmpY + snapTol && y > tmpY - snapTol) {
y = tmpY; // Snap Y Mouse Position
snapped = true;
}
return snapped;
}
bool SnapManager::snapToLineMiddle(Base::Vector3d& pointToOverride, const Part::GeomLineSegment* line)
{
Base::Vector3d startPoint = line->getStartPoint();
Base::Vector3d endPoint = line->getEndPoint();
Base::Vector3d midPoint = (startPoint + endPoint) / 2;
//Check if we are at middle of the line and if so snap to it.
if ((pointToOverride - midPoint).Length() < (endPoint - startPoint).Length() * 0.05) {
pointToOverride = midPoint;
return true;
}
return false;
}
bool SnapManager::snapToArcMiddle(Base::Vector3d& pointToOverride, const Part::GeomArcOfCircle* arc)
{
Base::Vector3d centerPoint = arc->getCenter();
Base::Vector3d startVec = (arc->getStartPoint() - centerPoint);
Base::Vector3d middleVec = startVec + (arc->getEndPoint() - centerPoint);
/* Handle the case of arc angle = 180 */
if (middleVec.Length() < Precision::Confusion()) {
middleVec.x = startVec.y;
middleVec.y = -startVec.x;
}
else {
middleVec = middleVec / middleVec.Length() * arc->getRadius();
}
Base::Vector2d mVec = Base::Vector2d(middleVec.x, middleVec.y);
Base::Vector3d pointVec = pointToOverride - centerPoint;
Base::Vector2d pVec = Base::Vector2d(pointVec.x, pointVec.y);
double u, v;
arc->getRange(u, v, true);
if (v < u)
v += 2 * M_PI;
double angle = v - u;
int revert = angle < M_PI ? 1 : -1;
/*To know if we are close to the middle of the arc, we are going to compare the angle of the
* (mouse cursor - center) to the angle of the middle of the arc. If it's less than 10% of the arc angle, then we snap.
*/
if (fabs(pVec.Angle() - (revert * mVec).Angle()) < 0.10 * angle) {
pointToOverride = centerPoint + middleVec * revert;
return true;
}
return false;
}

View File

@@ -0,0 +1,125 @@
/***************************************************************************
* Copyright (c) 2023 Pierre-Louis Boyer <pierrelouis.boyer@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
* SnapManager initially funded by the Open Toolchain Foundation *
***************************************************************************/
#ifndef SKETCHERGUI_SnapManager_H
#define SKETCHERGUI_SnapManager_H
#include <App/Application.h>
namespace SketcherGui {
class ViewProviderSketch;
class ViewProviderSketchSnapAttorney {
private:
static inline int getPreselectPoint(const ViewProviderSketch& vp);
static inline int getPreselectCross(const ViewProviderSketch& vp);
static inline int getPreselectCurve(const ViewProviderSketch& vp);
friend class SnapManager;
};
/* This class is used to manage the overriding of mouse pointer coordinates in Sketcher
* (in Edit-Mode) depending on the situation. Those situations are in priority order :
* 1 - Snap at angle: For tools like Slot, Arc, Line, Ellipse, this enables to constrain the angle at steps of 5° (or customized angle).
* This is useful to make features at a certain angle (45° for example)
* 2 - Snap to object: This snaps the mouse pointer onto objects.
* 3 - Snap to grid: This snaps the mouse pointer on the grid.
*/
class SnapManager
{
/** @brief Class for monitoring changes in parameters affecting Snapping
* @details
*
* This nested class is a helper responsible for attaching to the parameters relevant for
* SnapManager, initialising the SnapManager to the current configuration
* and handle in real time any change to their values.
*/
class ParameterObserver : public ParameterGrp::ObserverType
{
public:
explicit ParameterObserver(SnapManager& client);
~ParameterObserver() override;
void subscribeToParameters();
void unsubscribeToParameters();
/** Observer for parameter group. */
void OnChange(Base::Subject<const char*>& rCaller, const char* sReason) override;
private:
void initParameters();
void updateSnapParameter(const std::string& parametername);
void updateSnapToObjectParameter(const std::string& parametername);
void updateSnapToGridParameter(const std::string& parametername);
void updateSnapAngleParameter(const std::string& parametername);
static ParameterGrp::handle getParameterGrpHandle();
private:
std::map<std::string, std::function<void(const std::string&)>> str2updatefunction;
SnapManager& client;
};
public:
explicit SnapManager(ViewProviderSketch &vp);
~SnapManager();
bool snap(double& x, double& y);
bool snapAtAngle(double& x, double& y);
bool snapToObject(double& x, double& y);
bool snapToGrid(double& x, double& y);
bool snapToLineMiddle(Base::Vector3d& pointToOverride, const Part::GeomLineSegment* line);
bool snapToArcMiddle(Base::Vector3d& pointToOverride, const Part::GeomArcOfCircle* arc);
bool angleSnapEnabled;
Base::Vector2d referencePoint;
private:
double snapAngle;
double lastMouseAngle;
bool snapRequested;
bool snapToObjectsRequested;
bool snapToGridRequested;
/// Reference to ViewProviderSketch in order to access the public and the Attorney Interface
ViewProviderSketch & viewProvider;
/// Observer to track all the needed parameters.
std::unique_ptr<SnapManager::ParameterObserver> pObserver;
};
} // namespace SketcherGui
#endif // SKETCHERGUI_SnapManager_H

View File

@@ -65,6 +65,7 @@
#include "DrawSketchHandler.h"
#include "EditDatumDialog.h"
#include "EditModeCoinManager.h"
#include "SnapManager.h"
#include "TaskDlgEditSketch.h"
#include "TaskSketcherValidation.h"
#include "Utils.h"
@@ -304,6 +305,7 @@ ViewProviderSketch::ViewProviderSketch()
Mode(STATUS_NONE),
listener(nullptr),
editCoinManager(nullptr),
snapManager(nullptr),
pObserver(std::make_unique<ViewProviderSketch::ParameterObserver>(*this)),
sketchHandler(nullptr),
viewOrientationFactor(1)
@@ -551,36 +553,11 @@ bool ViewProviderSketch::keyPressed(bool pressed, int key)
return true; // handle all other key events
}
void ViewProviderSketch::setSnapMode(SnapMode mode)
void ViewProviderSketch::setAngleSnapping(bool enable, Base::Vector2d referencePoint)
{
snapMode = mode; // to be redirected to SnapManager
}
SnapMode ViewProviderSketch::getSnapMode() const
{
return snapMode; // to be redirected to SnapManager
}
void ViewProviderSketch::snapToGrid(double &x, double &y) // Paddle, when resolving this conflict, make sure to use the function in ViewProviderGridExtension
{
if (snapMode == SnapMode::SnapToGrid && ShowGrid.getValue()) {
// Snap Tolerance in pixels
const double snapTol = getGridSize() / 5;
double tmpX = x, tmpY = y;
getClosestGridPoint(tmpX, tmpY);
// Check if x within snap tolerance
if (x < tmpX + snapTol && x > tmpX - snapTol) {
x = tmpX; // Snap X Mouse Position
}
// Check if y within snap tolerance
if (y < tmpY + snapTol && y > tmpY - snapTol) {
y = tmpY; // Snap Y Mouse Position
}
}
assert(snapManager);
snapManager->angleSnapEnabled = enable;
snapManager->referencePoint = referencePoint;
}
void ViewProviderSketch::getProjectingLine(const SbVec2s& pnt, const Gui::View3DInventorViewer *viewer, SbLine& line) const
@@ -679,7 +656,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
try {
getCoordsOnSketchPlane(pos,normal,x,y);
snapToGrid(x, y);
snapManager->snap(x, y);
}
catch (const Base::ZeroDivisionError&) {
return false;
@@ -1148,7 +1125,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
double x,y;
try {
getCoordsOnSketchPlane(line.getPosition(),line.getDirection(),x,y);
snapToGrid(x, y);
snapManager->snap(x, y);
}
catch (const Base::ZeroDivisionError&) {
return false;
@@ -1275,7 +1252,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
SbLine line2;
getProjectingLine(DoubleClick::prvCursorPos, viewer, line2);
getCoordsOnSketchPlane(line2.getPosition(),line2.getDirection(),drag.xInit,drag.yInit);
snapToGrid(drag.xInit, drag.yInit);
snapManager->snap(drag.xInit, drag.yInit);
} else {
drag.resetVector();
}
@@ -2877,6 +2854,7 @@ bool ViewProviderSketch::setEdit(int ModNum)
preselection.reset();
selection.reset();
editCoinManager = std::make_unique<EditModeCoinManager>(*this);
snapManager = std::make_unique<SnapManager>(*this);
auto editDoc = Gui::Application::Instance->editDocument();
App::DocumentObject *editObj = getSketchObject();
@@ -3123,6 +3101,7 @@ void ViewProviderSketch::unsetEdit(int ModNum)
deactivateHandler();
editCoinManager = nullptr;
snapManager = nullptr;
preselection.reset();
selection.reset();
this->detachSelection();

View File

@@ -85,15 +85,9 @@ namespace Sketcher {
namespace SketcherGui {
class EditModeCoinManager;
class SnapManager;
class DrawSketchHandler;
enum class SnapMode { // to be moved to SnapManager
None,
SnapToObject,
SnapToAngle,
SnapToGrid,
};
using GeoList = Sketcher::GeoList;
using GeoListFacade = Sketcher::GeoListFacade;
@@ -484,8 +478,10 @@ public:
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
//@}
void setSnapMode(SnapMode mode);
SnapMode getSnapMode() const;
/** @name Toggle angle snapping and set the reference point */
//@{
/// Toggle angle snapping and set the reference point
void setAngleSnapping(bool enable, Base::Vector2d referencePoint = Base::Vector2d(0., 0.));
/** @name Access to Sketch and Solver objects */
//@{
@@ -566,6 +562,7 @@ public:
//@{
friend class ViewProviderSketchDrawSketchHandlerAttorney;
friend class ViewProviderSketchCoinAttorney;
friend class ViewProviderSketchSnapAttorney;
friend class ViewProviderSketchShortcutListenerAttorney;
//@}
protected:
@@ -660,9 +657,6 @@ private:
/** @name miscelanea utilities */
//@{
/// snap points x,y (mouse coordinates) onto grid if enabled
void snapToGrid(double &x, double &y);
/// moves a selected constraint
void moveConstraint(int constNum, const Base::Vector2d &toPos);
@@ -784,6 +778,8 @@ private:
std::unique_ptr<EditModeCoinManager> editCoinManager;
std::unique_ptr<SnapManager> snapManager;
std::unique_ptr<ViewProviderSketch::ParameterObserver> pObserver;
std::unique_ptr<DrawSketchHandler> sketchHandler;
@@ -792,8 +788,6 @@ private:
SoNodeSensor cameraSensor;
int viewOrientationFactor; // stores if sketch viewed from front or back
SnapMode snapMode = SnapMode::None; // temporary - to be moved to SnapManager
};
} // namespace PartGui

View File

@@ -188,7 +188,8 @@ inline void SketcherAddWorkbenchSketchEditModeActions(Gui::ToolBarItem& sketch)
sketch << "Sketcher_LeaveSketch"
<< "Sketcher_ViewSketch"
<< "Sketcher_ViewSection"
<< "Sketcher_Grid";
<< "Sketcher_Grid"
<< "Sketcher_Snap";
}
template <typename T>