Bug Summary

File:librecad/src/lib/actions/rs_snapper.cpp
Warning:line 1213, column 30
Although the value stored to 'showSnapIndicator' is used in the enclosing expression, the value is never actually read from 'showSnapIndicator'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name rs_snapper.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -resource-dir /usr/lib/llvm-21/lib/clang/21 -D _REENTRANT -D DWGSUPPORT -D MUPARSER_STATIC -D LC_WITH_OPENTYPE=0 -D QC_APPDIR=librecad -D LC_VERSION=2.2.2_alpha1-853-ge2a8c2931 -D LC_PRERELEASE=true; -D QT_NO_DEBUG -D QT_SVG_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_NETWORK_LIB -D QT_CORE_LIB -I . -I /usr/include -I ../../libraries/lciconengine -I ../../libraries/libdxfrw/src -I ../../libraries/libdxfrw/src/intern -I ../../libraries/jwwlib/src -I ../../libraries/shapelib/src -I ../../libraries/qtbidi -I cmd -I lib/actions -I lib/actions/visual_snap -I lib/actions/options -I lib/creation -I lib/debug -I lib/engine -I lib/engine/document -I lib/engine/document/blocks -I lib/engine/document/container -I lib/engine/document/dimstyles -I lib/engine/document/entities -I lib/engine/document/entities/support -I lib/engine/document/fonts -I lib/engine/document/io -I lib/engine/document/layers -I lib/engine/document/linetypes -I lib/engine/document/patterns -I lib/engine/document/selection -I lib/engine/document/textstyles -I lib/engine/document/ucs -I lib/engine/document/variables -I lib/engine/document/views -I lib/engine/clipboard -I lib/engine/overlays -I lib/engine/overlays/angles_base -I lib/engine/overlays/highlight -I lib/engine/overlays/preview -I lib/engine/overlays/references -I lib/engine/overlays/crosshair -I lib/engine/overlays/info_cursor -I lib/engine/overlays/overlay_box -I lib/engine/overlays/ucs_mark -I lib/engine/undo -I lib/engine/utils -I lib/engine/settings -I lib/fileio -I lib/filters -I lib/generators -I lib/generators/makercamsvg -I lib/generators/layers -I lib/generators/image -I lib/gui -I lib/gui/grid -I lib/gui/render -I lib/gui/render/headless -I lib/gui/render/widget -I lib/information -I lib/math -I lib/modification -I lib/selection -I lib/selection/metaentity -I lib/selection/metaentity/entities -I lib/printing -I lib/properties -I actions -I actions/dock_widgets -I actions/dock_widgets/block -I actions/dock_widgets/entity_info -I actions/dock_widgets/layer -I actions/dock_widgets/library -I actions/dock_widgets/ucs_list -I actions/drawing -I actions/drawing/draw -I actions/drawing/draw/arc -I actions/drawing/draw/circle -I actions/drawing/draw/curve -I actions/drawing/draw/spline -I actions/drawing/draw/dimensions -I actions/drawing/draw/ellipse -I actions/drawing/draw/hatch -I actions/drawing/draw/image -I actions/drawing/draw/line -I actions/drawing/draw/point -I actions/drawing/draw/rect -I actions/drawing/draw/polygon -I actions/drawing/draw/misc -I actions/drawing/draw/line/shapes -I actions/drawing/draw/line/misc -I actions/drawing/draw/line/shapes/rect -I actions/drawing/draw/line/shapes/polygon -I actions/drawing/draw/polyline -I actions/drawing/draw/text -I actions/drawing/edit -I actions/drawing/info -I actions/drawing/pick -I actions/drawing/modify -I actions/drawing/pen -I actions/drawing/rel_zero -I actions/drawing/selection -I actions/drawing/snap -I actions/drawing/zoom -I actions/file -I actions/options -I actions/print_preview -I ui -I ui/action_options -I ui/action_options/circle -I ui/action_options/curve -I ui/action_options/spline -I ui/action_options/dimensions -I ui/action_options/edit -I ui/action_options/image -I ui/action_options/info -I ui/action_options/insert -I ui/action_options/line -I ui/action_options/rect -I ui/action_options/polygon -I ui/action_options/misc -I ui/action_options/modify -I ui/action_options/ellipse -I ui/action_options/other -I ui/action_options/polyline -I ui/action_options/point -I ui/action_options/print_preview -I ui/action_options/selection -I ui/action_options/snap -I ui/action_options/text -I ui/actions -I ui/components -I ui/components/relative_position_assistant -I ui/components/comboboxes -I ui/components/containers -I ui/components/creators -I ui/components/layouts -I ui/components/pen -I ui/components/status_bar -I ui/components/toolbars -I ui/components/utils -I ui/dialogs -I ui/dialogs/actions -I ui/dialogs/actions/modify -I ui/dialogs/actions/quick_selection -I ui/dialogs/modify -I ui/dialogs/entity -I ui/dialogs/creators -I ui/dialogs/file -I ui/dialogs/file/export -I ui/dialogs/file/export/layers -I ui/dialogs/file/export/image -I ui/dialogs/file/export/makercam -I ui/dialogs/main -I ui/dialogs/settings -I ui/dialogs/settings/dimstyles -I ui/dialogs/settings/dimstyles/dimstyle_manager -I ui/dialogs/settings/dimstyles/dimstyle_manager/support -I ui/dialogs/settings/options_device -I ui/dialogs/settings/options_drawing -I ui/dialogs/settings/options_general -I ui/dialogs/settings/options_widget -I ui/dialogs/settings/shortcuts -I ui/dock_widgets -I ui/dock_widgets/block_widget -I ui/dock_widgets/command_line -I ui/dock_widgets/entity_info -I ui/dock_widgets/layer_widget -I ui/dock_widgets/layers_tree -I ui/dock_widgets/library_widget -I ui/dock_widgets/pen_palette -I ui/dock_widgets/pen_wizard -I ui/dock_widgets/property_sheet -I ui/dock_widgets/property_sheet/lib -I ui/dock_widgets/property_sheet/lib/properties -I ui/dock_widgets/property_sheet/lib/view -I ui/dock_widgets/property_sheet/lib/view/edit -I ui/dock_widgets/property_sheet/lib/widgets -I ui/dock_widgets/property_sheet/lib/widgets/sheet -I ui/dock_widgets/property_sheet/metaentity -I ui/dock_widgets/property_sheet/metaentity/entities -I ui/dock_widgets/property_sheet/metaentity/entities/document -I ui/dock_widgets/property_sheet/properties -I ui/dock_widgets/property_sheet/properties/action -I ui/dock_widgets/property_sheet/properties/bool -I ui/dock_widgets/property_sheet/properties/color -I ui/dock_widgets/property_sheet/properties/double -I ui/dock_widgets/property_sheet/properties/enum -I ui/dock_widgets/property_sheet/properties/int -I ui/dock_widgets/property_sheet/properties/layer -I ui/dock_widgets/property_sheet/properties/linetype -I ui/dock_widgets/property_sheet/properties/linewidth -I ui/dock_widgets/property_sheet/properties/rect -I ui/dock_widgets/property_sheet/properties/rsvector -I ui/dock_widgets/property_sheet/properties/string -I ui/dock_widgets/views_list -I ui/dock_widgets/ucs_list -I ui/dock_widgets/workspaces -I ui/dock_widgets/cad -I ui/main -I ui/main/init -I ui/main/persistence -I ui/main/release_check -I ui/main/support -I ui/main/fontviewer -I ui/main/workspaces -I ui/view -I main -I main/console_dxf2pdf -I test -I plugins -I ../res -I ../res/arrows -I ../res/controls -I ../res/dxf -I ../res/gdt -I ../res/icons -I ../res/images -I ../../../Qt/6.9.0/gcc_64/include -I ../../../Qt/6.9.0/gcc_64/include/QtSvg -I ../../../Qt/6.9.0/gcc_64/include/QtPrintSupport -I ../../../Qt/6.9.0/gcc_64/include/QtWidgets -I ../../../Qt/6.9.0/gcc_64/include/QtGui -I ../../../Qt/6.9.0/gcc_64/include/QtNetwork -I ../../../Qt/6.9.0/gcc_64/include/QtCore -I ../../generated/librecad/moc -I ../../generated/librecad/ui -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/x86_64-linux-gnu/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-11-06-031751-4837-1 -x c++ lib/actions/rs_snapper.cpp
1/****************************************************************************
2**
3** This file is part of the LibreCAD project, a 2D CAD program
4**
5** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7**
8**
9** This file may be distributed and/or modified under the terms of the
10** GNU General Public License version 2 as published by the Free Software
11** Foundation and appearing in the file gpl-2.0.txt included in the
12** packaging of this file.
13**
14** This program is distributed in the hope that it will be useful,
15** but WITHOUT ANY WARRANTY; without even the implied warranty of
16** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17** GNU General Public License for more details.
18**
19** You should have received a copy of the GNU General Public License
20** along with this program; if not, write to the Free Software
21** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22**
23** This copyright notice MUST APPEAR in all copies of the script!
24**
25**********************************************************************/
26
27#include "rs_snapper.h"
28
29#include <algorithm>
30#include <cmath>
31#include <vector>
32
33#include <QMouseEvent>
34
35#include "lc_actioncontext.h"
36#include "lc_containertraverser.h"
37#include "lc_crosshair.h"
38#include "lc_cursoroverlayinfo.h"
39#include "lc_defaults.h"
40#include "lc_graphicviewport.h"
41#include "lc_linemath.h"
42#include "lc_overlayentitiescontainer.h"
43#include "rs_debug.h"
44#include "rs_graphic.h"
45#include "rs_graphicview.h"
46#include "rs_grid.h"
47#include "rs_line.h"
48#include "rs_pen.h"
49#include "rs_settings.h"
50#include "rs_vector.h"
51#include "lc_visual_snap_manager.h"
52
53namespace {
54 // whether a floating point is positive by tolerance
55 bool isPositive(const double x) {
56 return x > RS_TOLERANCE1.0e-10;
57 }
58
59 // A size vector is valid with a positive size
60 bool isSizeValid(const RS_Vector& sizeVector) {
61 return isPositive(sizeVector.x) || isPositive(sizeVector.y);
62 }
63
64 // The valid size magnitude
65 double getValidSize(const RS_Vector& sizeVector) {
66 return std::hypot(std::max(sizeVector.x, RS_TOLERANCE1.0e-10), std::max(sizeVector.y, RS_TOLERANCE1.0e-10));
67 }
68
69 constexpr int DEFAULT_CATCH_ENTITY_RANGE_PX = 32;
70
71 bool isContainerTarget(const RS2::EntityType type) {
72 switch (type) {
73 case RS2::EntityPolyline:
74 case RS2::EntityContainer:
75 case RS2::EntitySpline:
76 return true;
77 default:
78 return false;
79 }
80 }
81
82 bool matchesRequestedType(const RS_Entity& entity, const RS2::EntityType type) {
83 if (entity.rtti() == type) {
84 return true;
85 }
86 if (!isContainerTarget(type)) {
87 return false;
88 }
89 for (const RS_EntityContainer* parent = entity.getParent(); parent != nullptr; parent = parent->getParent()) {
90 if (parent->rtti() == type) {
91 return true;
92 }
93 }
94 return false;
95 }
96}
97
98/**
99 * Disable all snapping.
100 *
101 * This effectively puts the object into free snap mode.
102 *
103 * @returns A reference to itself.
104 */
105const RS_SnapMode& RS_SnapMode::clear() {
106 *this = RS_SnapMode{};
107
108 return *this;
109}
110
111bool RS_SnapMode::operator ==(const RS_SnapMode& rhs) const {
112 return snapIntersection == rhs.snapIntersection && snapOnEntity == rhs.snapOnEntity && snapCenter == rhs.snapCenter && snapDistance ==
113 rhs.snapDistance && snapMiddle == rhs.snapMiddle && snapEndpoint == rhs.snapEndpoint && snapGrid == rhs.snapGrid && snapFree == rhs.
114 snapFree && restriction == rhs.restriction && snapAngle == rhs.snapAngle;
115}
116
117bool RS_SnapMode::operator !=(const RS_SnapMode& rhs) const {
118 return !this->operator ==(rhs);
119}
120
121/**
122 * snap mode to a flag integer
123 */
124uint RS_SnapMode::toInt(const RS_SnapMode& s) {
125 uint ret{0};
126
127 if (s.snapIntersection) {
128 ret |= RS_SnapMode::SnapIntersection;
129 }
130 if (s.snapOnEntity) {
131 ret |= RS_SnapMode::SnapOnEntity;
132 }
133 if (s.snapCenter) {
134 ret |= RS_SnapMode::SnapCenter;
135 }
136 if (s.snapDistance) {
137 ret |= RS_SnapMode::SnapDistance;
138 }
139 if (s.snapMiddle) {
140 ret |= RS_SnapMode::SnapMiddle;
141 }
142 if (s.snapEndpoint) {
143 ret |= RS_SnapMode::SnapEndpoint;
144 }
145 if (s.snapGrid) {
146 ret |= RS_SnapMode::SnapGrid;
147 }
148 if (s.snapFree) {
149 ret |= RS_SnapMode::SnapFree;
150 }
151 if (s.snapAngle) {
152 ret |= RS_SnapMode::SnapAngle;
153 }
154
155 switch (s.restriction) {
156 case RS2::RestrictHorizontal:
157 ret |= RS_SnapMode::RestrictHorizontal;
158 break;
159 case RS2::RestrictVertical:
160 ret |= RS_SnapMode::RestrictVertical;
161 break;
162 case RS2::RestrictOrthogonal:
163 ret |= RS_SnapMode::RestrictOrthogonal;
164 break;
165 default:
166 break;
167 }
168
169 if (s.snapVisual) {
170 ret |= RS_SnapMode::SnapVisual;
171 }
172 if (s.snapVisualSurvive) {
173 ret |= RS_SnapMode::SnapVisualSurvive;
174 }
175
176 return ret;
177}
178
179/**
180 * integer flag to snapMode
181 */
182RS_SnapMode RS_SnapMode::fromInt(const unsigned int ret) {
183 RS_SnapMode s;
184
185 if (RS_SnapMode::SnapIntersection & ret) {
186 s.snapIntersection = true;
187 }
188 if (RS_SnapMode::SnapOnEntity & ret) {
189 s.snapOnEntity = true;
190 }
191 if (RS_SnapMode::SnapCenter & ret) {
192 s.snapCenter = true;
193 }
194 if (RS_SnapMode::SnapDistance & ret) {
195 s.snapDistance = true;
196 }
197 if (RS_SnapMode::SnapMiddle & ret) {
198 s.snapMiddle = true;
199 }
200 if (RS_SnapMode::SnapEndpoint & ret) {
201 s.snapEndpoint = true;
202 }
203 if (RS_SnapMode::SnapGrid & ret) {
204 s.snapGrid = true;
205 }
206 if (RS_SnapMode::SnapFree & ret) {
207 s.snapFree = true;
208 }
209 if (RS_SnapMode::SnapAngle & ret) {
210 s.snapAngle = true;
211 }
212
213 switch (RS_SnapMode::RestrictOrthogonal & ret) {
214 case RS_SnapMode::RestrictHorizontal:
215 s.restriction = RS2::RestrictHorizontal;
216 break;
217 case RS_SnapMode::RestrictVertical:
218 s.restriction = RS2::RestrictVertical;
219 break;
220 case RS_SnapMode::RestrictOrthogonal:
221 s.restriction = RS2::RestrictOrthogonal;
222 break;
223 default:
224 s.restriction = RS2::RestrictNothing;
225 break;
226 }
227
228 if (RS_SnapMode::SnapVisual & ret) {
229 s.snapVisual = true;
230 }
231
232 if (RS_SnapMode::SnapVisualSurvive & ret) {
233 s.snapVisualSurvive = true;
234 }
235 return s;
236}
237
238/**
239 * Methods and structs for class RS_Snapper
240 */
241struct RS_Snapper::Indicator {
242 bool drawLines = false;
243 int lines_Type = 0;
244 RS_Pen lines_Pen;
245
246 bool drawShape = false;
247 int shape_Type = 0;
248 RS_Pen shape_Pen;
249
250 int pointType = LC_DEFAULTS_PDMode(0|96);
251 int pointSize = LC_DEFAULTS_PDSize(- 2.0);
252};
253
254namespace {
255 constexpr double DEFAULT_SNAP_ANGLE_STEP = RS_Math::deg2rad(15.0);
256 constexpr double DEFAULT_SOFT_SNAP_SENSITIVITY = RS_Math::deg2rad(3.0);
257 constexpr double MAX_SOFT_SNAP_FRACTION_OF_STEP = 0.45;
258}
259
260/**
261 * Constructor.
262 */
263RS_Snapper::RS_Snapper(LC_ActionContext* actionContext, QObject* parent) :
264 QObject(parent), m_document(actionContext->getDocument()), m_graphicView(actionContext->getGraphicView()),
265 m_actionContext(actionContext), m_infoCursorOverlayData{std::make_unique<LC_InfoCursorData>()},
266 m_visualSnapManager{std::make_unique<LC_VisualSnapManager>(this)}, m_impData{std::make_unique<ImpData>()},
267 m_snapIndicator{std::make_unique<Indicator>()}, m_snapToAngleStep{DEFAULT_SNAP_ANGLE_STEP},
268 m_softSnapSensitivityRad{DEFAULT_SOFT_SNAP_SENSITIVITY} {
269 Q_ASSERT(m_document != nullptr)static_cast<void>(false && (m_document != nullptr
))
;
270 Q_ASSERT(m_graphicView != nullptr)static_cast<void>(false && (m_graphicView != nullptr
))
;
271 m_viewport = m_graphicView->getViewPort();
272 m_visualSnapManager->setViewport(m_viewport);
273 m_formatter = m_viewport->getFormatter();
274 m_infoCursorOverlayPrefs = m_graphicView->getInfoCursorOverlayPreferences();
275}
276
277RS_Snapper::~RS_Snapper() = default;
278
279/**
280 * Initialize (called by all constructors)
281 */
282void RS_Snapper::init() {
283 m_snapMode = m_graphicView->getDefaultSnapMode();
284 m_keyEntity = nullptr;
285 m_impData->snapSpot = RS_Vector{false};
286 m_impData->snapCoord = RS_Vector{false};
287 m_snapDistance = 1.0;
288 initSettings();
289}
290
291void RS_Snapper::initSettings() {
292 initFromSettings();
293
294 RS_Graphic* graphic = m_graphicView->getGraphic();
295 if (graphic != nullptr) {
296 initFromGraphic(graphic);
297 }
298}
299
300void RS_Snapper::initFromSettings() {
301 LC_GROUPRS_Settings::instance()->beginGroup("Appearance");
302 {
303 const int snapIndicatorLineWidth = static_cast<RS2::LineType>(LC_GET_INTRS_Settings::instance()->readInt("indicator_lines_line_width", 1));
304 m_snapIndicator->drawLines = LC_GET_BOOLRS_Settings::instance()->readBool("indicator_lines_state", true);
305 if (m_snapIndicator->drawLines) {
306 m_snapIndicator->lines_Type = LC_GET_INTRS_Settings::instance()->readInt("indicator_lines_type", 0);
307 const auto snapIndicatorLineType = static_cast<RS2::LineType>(LC_GET_INTRS_Settings::instance()->readInt("indicator_lines_line_type", RS2::DashLine));
308 const QString snapColorLines = LC_GET_ONE_STRRS_Settings::instance()->readStrSingle("Colors", "snap_indicator_lines", RS_Settings::SNAP_INDICATOR_LINES);
309 m_snapIndicator->lines_Pen = RS_Pen(RS_Color(snapColorLines), RS2::Width00, snapIndicatorLineType);
310 m_snapIndicator->lines_Pen.setScreenWidth(snapIndicatorLineWidth);
311 }
312 else {
313 m_snapIndicator->lines_Type = LC_Crosshair::NoLines;
314 }
315
316 m_snapIndicator->drawShape = LC_GET_BOOLRS_Settings::instance()->readBool("indicator_shape_state", true);
317 if (m_snapIndicator->drawShape) {
318 m_snapIndicator->shape_Type = LC_GET_INTRS_Settings::instance()->readInt("indicator_shape_type", 0);
319 const QString snapColor = LC_GET_ONE_STRRS_Settings::instance()->readStrSingle("Colors", "snap_indicator", RS_Settings::SNAP_INDICATOR);
320 m_snapIndicator->shape_Pen = RS_Pen(RS_Color(snapColor), RS2::Width00, RS2::SolidLine);
321 m_snapIndicator->shape_Pen.setScreenWidth(snapIndicatorLineWidth);
322 }
323 else {
324 m_snapIndicator->shape_Type = LC_Crosshair::NoShape;
325 }
326
327 m_ignoreSnapToGridIfNoGrid = LC_GET_BOOLRS_Settings::instance()->readBool("SnapGridIgnoreIfNoGrid", false);
328 }
329 LC_GROUP_ENDRS_Settings::instance()->endGroup();
330
331 LC_GROUPRS_Settings::instance()->beginGroup("Snap");
332 {
333 m_distanceBeforeSwitchToFreeSnap = LC_GET_INTRS_Settings::instance()->readInt("AdvSnapOnEntitySwitchToFreeDistance", 500) / 100.0;
334 m_catchEntityGuiRange = LC_GET_INTRS_Settings::instance()->readInt("AdvSnapEntityCatchRange", DEFAULT_CATCH_ENTITY_RANGE_PX);
335 m_minGridCellSnapFactor = LC_GET_INTRS_Settings::instance()->readInt("AdvSnapGridCellSnapFactor", 25) / 100.0;
336 m_angleSnapSnapToGridLinesIfGrid = LC_GET_BOOLRS_Settings::instance()->readBool("AngleSnapToLinesIfGrid", true);
337 }
338 LC_GROUP_ENDRS_Settings::instance()->endGroup();
339
340 m_visualSnapManager->updateOptions();
341}
342
343void RS_Snapper::updateSnapAngleStep() {
344 const int stepType = LC_GET_ONE_INTRS_Settings::instance()->readIntSingle("Defaults", "AngleSnapStep", 3);
345 double snapStepDegrees;
346 switch (stepType) {
347 case 0:
348 snapStepDegrees = 1.0;
349 break;
350 case 1:
351 snapStepDegrees = 3.0;
352 break;
353 case 2:
354 snapStepDegrees = 5.0;
355 break;
356 case 3:
357 snapStepDegrees = 10.0;
358 break;
359 case 4:
360 snapStepDegrees = 15.0;
361 break;
362 case 5:
363 snapStepDegrees = 18.0;
364 break;
365 case 6:
366 snapStepDegrees = 22.5;
367 break;
368 case 7:
369 snapStepDegrees = 30.0;
370 break;
371 case 8:
372 snapStepDegrees = 45.0;
373 break;
374 case 9:
375 snapStepDegrees = 90.0;
376 break;
377 default:
378 snapStepDegrees = 15.0;
379 break;
380 }
381 m_snapToAngleStep = RS_Math::deg2rad(snapStepDegrees);
382
383 bool ok = false;
384 const double softSnapSensitivityDegrees = LC_GET_ONE_STRRS_Settings::instance()->readStrSingle("Defaults", "SoftSnapSensitivityAngle", "3.0").toDouble(&ok);
385 m_softSnapEnabled = LC_GET_ONE_BOOLRS_Settings::instance()->readBoolSingle("Defaults", "SoftSnapEnabled", false);
386 const double maximumSoftSnapSensitivity = RS_Math::rad2deg(m_snapToAngleStep)
387 * MAX_SOFT_SNAP_FRACTION_OF_STEP;
388 const double requestedSoftSnapSensitivity = ok && softSnapSensitivityDegrees >= 0.1
389 ? softSnapSensitivityDegrees
390 : RS_Math::rad2deg(DEFAULT_SOFT_SNAP_SENSITIVITY);
391 m_softSnapSensitivityRad = RS_Math::deg2rad(std::min(std::clamp(requestedSoftSnapSensitivity, 0.1, 45.0),
392 maximumSoftSnapSensitivity));
393}
394
395void RS_Snapper::initFromGraphic(RS_Graphic* graphic) {
396 if (graphic != nullptr) {
397 m_snapIndicator->pointType = graphic->getVariableInt("$PDMODE", LC_DEFAULTS_PDMode(0|96));
398 m_snapIndicator->pointSize = graphic->getVariableInt("$PDSIZE", LC_DEFAULTS_PDSize(- 2.0));
399 }
400}
401
402bool RS_Snapper::isInVisualSnapStatus([[maybe_unused]] int status) {
403 return false;
404}
405
406bool RS_Snapper::isClearVisualSnapByRMB() const {
407 return m_visualSnapManager->isClearVisualSnapByRMB();
408}
409
410void RS_Snapper::onVisualSnapEntityRegistered([[maybe_unused]] RS_Entity* point) {
411}
412
413bool RS_Snapper::isVisualSnapApplicable() {
414 return m_snapMode.snapVisual;
415}
416
417void RS_Snapper::finish() {
418 m_finished = true;
419 deleteSnapper();
420 deleteInfoCursor();
421 clearVisualSnap();
422 m_graphicView->hideRelativeInputWidget();
423}
424
425void RS_Snapper::setSnapMode(const RS_SnapMode& snapMode) {
426 this->m_snapMode = snapMode;
427 m_actionContext->requestSnapDistOptions(&m_snapDistance, snapMode.snapDistance);
428 m_actionContext->requestSnapMiddleOptions(&m_middlePoints, snapMode.snapMiddle);
429}
430
431const RS_SnapMode* RS_Snapper::getSnapMode() const {
432 return &m_snapMode;
433}
434
435RS_SnapMode* RS_Snapper::getSnapMode() {
436 return &m_snapMode;
437}
438
439//get current mouse coordinates
440RS_Vector RS_Snapper::snapFree(const QMouseEvent* e) const {
441 if (e == nullptr) {
442 RS_DEBUGRS_Debug::instance()->print(RS_Debug::D_WARNING, "RS_Snapper::snapFree: event is nullptr");
443 return RS_Vector(false);
444 }
445 m_impData->update(toGraph(e), RS2::SnapType::FREE);
446 m_impData->snapCoord = m_impData->snapSpot;
447 m_snapIndicator->drawLines = true;
448 return m_impData->snapCoord;
449}
450
451void RS_Snapper::snapEndpoint(const RS_Vector& mouseCoord, double& ds2Min) const {
452 if (m_snapMode.snapEndpoint) {
453 RS_Entity* endpointEntity = nullptr;
454 const RS_Vector snap = snapEndpoint(mouseCoord, &endpointEntity);
455 const double ds2 = mouseCoord.squaredTo(snap);
456
457 if (snap.valid && ds2 < ds2Min) {
458 ds2Min = ds2;
459 m_impData->update(snap, RS2::SnapType::ENDPOINT, endpointEntity);
460 }
461 }
462}
463
464void RS_Snapper::snapCenter(const RS_Vector& mouseCoord, double& ds2Min) const {
465 if (m_snapMode.snapCenter) {
466 RS_Entity* centerEntity = nullptr;
467 const RS_Vector snap = snapCenter(mouseCoord, &centerEntity);
468 const double ds2 = mouseCoord.squaredTo(snap);
469 if (ds2 < ds2Min) {
470 ds2Min = ds2;
471 m_impData->update(snap, RS2::SnapType::CENTER, centerEntity);
472 }
473 }
474}
475
476void RS_Snapper::snapMiddle(const RS_Vector& mouseCoord, double& ds2Min) {
477 if (m_snapMode.snapMiddle) {
478 //this is still brutal force
479 //todo: accept value from widget QG_SnapMiddleOptions
480
481 m_actionContext->requestSnapMiddleOptions(&m_middlePoints, m_snapMode.snapMiddle);
482 const RS_Vector snap = snapMiddle(mouseCoord);
483 const double ds2 = mouseCoord.squaredTo(snap);
484 if (ds2 < ds2Min) {
485 ds2Min = ds2;
486 m_impData->update(snap, RS2::SnapType::MIDDLE);
487 }
488 }
489}
490
491void RS_Snapper::snapDistance(const RS_Vector& mouseCoord, double& ds2Min) {
492 if (m_snapMode.snapDistance) {
493 //this is still brutal force
494 //todo: accept value from widget QG_SnapDistOptions
495 m_actionContext->requestSnapDistOptions(&m_snapDistance, m_snapMode.snapDistance);
496 const RS_Vector snap = snapDist(mouseCoord);
497 const double ds2 = mouseCoord.squaredTo(snap);
498 if (ds2 < ds2Min) {
499 ds2Min = ds2;
500 m_impData->update(snap, RS2::SnapType::DISTANCE);
501 }
502 }
503}
504
505void RS_Snapper::snapIntersection(const RS_Vector& mouseCoord, double& ds2Min) const {
506 m_impData->entityOther = nullptr;
507 if (m_snapMode.snapIntersection) {
508 RS_Entity* entity1 = nullptr;
509 RS_Entity* entity2 = nullptr;
510 const RS_Vector snap = snapIntersection(mouseCoord, &entity1, &entity2);
511 const double ds2 = mouseCoord.squaredTo(snap);
512 if (ds2 < ds2Min) {
513 ds2Min = ds2;
514 m_impData->update(snap, RS2::SnapType::INTERSECTION, entity1, entity2);
515 }
516 }
517}
518
519void RS_Snapper::snapOnEntity(const RS_Vector& mouseCoord, double& ds2Min) {
520 if (m_snapMode.snapOnEntity && m_impData->snapSpot.distanceTo(mouseCoord) > m_distanceBeforeSwitchToFreeSnap) {
521 RS_Entity* entity = nullptr;
522 const RS_Vector snap = snapOnEntity(mouseCoord, &entity);
523 const double ds2 = mouseCoord.squaredTo(snap);
524 if (ds2 < ds2Min) {
525 ds2Min = ds2;
526 m_impData->update(snap, RS2::SnapType::ENTITY, entity);
527 }
528 }
529}
530
531void RS_Snapper::snapGrid(const RS_Vector& mouseCoord, double ds2Min) const {
532 if (isSnapToGrid()) {
533 const RS_Vector snap = snapGrid(mouseCoord);
534 const double ds2 = mouseCoord.squaredTo(snap);
535 if (ds2 < ds2Min) {
536 m_impData->update(snap, RS2::SnapType::GRID);
537 }
538 }
539}
540
541RS_Vector RS_Snapper::snapVisualRayOrLine(const RS_Vector& mouseCoord, const RS_Entity* entity, double& dist) const {
542 RS_Vector snap = entity->getNearestPointOnEntity(mouseCoord, true, &dist);
543 if (isSnapToGrid()) {
544 const RS_Vector startPoint = entity->getStartpoint();
545 const RS_Vector endPoint = entity->getEndpoint();
546 snap = snapGrid(snap, startPoint, endPoint);
547 m_impData->restrictedSnapType = RS2::SnapType::GRID;
548 }
549 return snap;
550}
551
552bool RS_Snapper::snapVisual(const RS_Vector& mouseCoord, RS_Entity** restrictingEntity, RS2::VisualSnapGuideEntityType& restrictingType) {
553 bool visualSnapFound = false;
554
555 // double range = getCatchDistance(getSnapRange(), m_catchEntityGuiRange);
556 const double range = getSnapRange();
557 m_visualSnapManager->setSnapRange(range);
558 if (isVisualSnapApplicable() && hasVisualSnap()) {
559 LC_VisualSnapSolution* visualSnapSolution = m_visualSnapManager->solveVisualSnap(mouseCoord);
560 visualSnapSolution->restrictedPoint.valid = false;
561
562 if (visualSnapSolution != nullptr) {
563 if (visualSnapSolution->hasFoundSnapPoint()) {
564 // exact snap found, it's either intersection or vertex point
565 const RS_Vector snap = visualSnapSolution->foundSnapPoint;
566 m_impData->update(snap, RS2::SnapType::VISUAL_SNAP);
567 m_impData->snapCoord = snap;
568 m_impData->visualSnapType = visualSnapSolution->foundSnapPointInfo;
569 const RS2::SnapType originalSnapType = visualSnapSolution->restrictedOriginalSnapType;
570 if (originalSnapType != RS2::SnapType::NO_SNAP) {
571 m_impData->restrictedSnapType = originalSnapType;
572 }
573 visualSnapFound = true;
574 }
575 else {
576 if (!visualSnapSolution->guidingEntities.empty()) {
577 const int size = visualSnapSolution->guidingEntities.size();
578 // LC_ERR << "VSnap entities size: " << size;
579 LC_VisualSnapEntityHolder holder;
580 if (size == 1) {
581 holder = visualSnapSolution->guidingEntities.front();
582 }
583 else {
584 // find closest entity
585 double minDist = RS_MAXDOUBLE1.0E+10;
586 for (const auto& h : visualSnapSolution->guidingEntities) {
587 const auto e = h.entity;
588 double dist;
589 e->getNearestPointOnEntity(mouseCoord, true, &dist);
590 // LC_ERR << "VSNAP_nearest_dist: " << dist;
591 // LC_ERR << "holder.snapEntityType: " << h.snapEntityType;
592 if (dist < minDist) {
593 minDist = dist;
594 holder = h;
595 }
596 }
597 if (minDist > range) {
598 return false; // otherwise, it will be more than strict snap to nearest line...
599 }
600 }
601 const auto snapEntityType = holder.snapEntityType;
602 // LC_ERR << "VSnap - snapped type : " << snapEntityType;
603 double distance = RS_MAXDOUBLE1.0E+10;
604 const RS_Entity* guideEntity = holder.entity;
605 switch (snapEntityType) {
606 case RS2::VSNAP_LINE_VERTEX_VERTICAL:
607 case RS2::VSNAP_LINE_VERTEX_HORIZONTAL:
608 case RS2::VSNAP_LINE_RESTR_VERTICAL:
609 case RS2::VSNAP_LINE_RESTR_HORIZONTAL:
610 case RS2::VSNAP_POINT_RELATIVE_VERTICAL_DX:
611 case RS2::VSNAP_POINT_RELATIVE_HORIZONTAL_DY: {
612 restrictingType = snapEntityType;
613 *restrictingEntity = holder.entity;
614 break;
615 }
616 case RS2::VSNAP_LINE_ENDPOINT_TANGENT:
617 case RS2::VSNAP_LINE_ENDPOINT_NORMAL:
618 case RS2::VSNAP_LINE_TANGENT1:
619 case RS2::VSNAP_LINE_TANGENT2:
620 case RS2::VSNAP_LINE_RAY:
621 case RS2::VSNAP_LINE_ENDPOINT_ANGLE_STEP:
622 case RS2::VSNAP_LINE_VERTEX_ANGLE_STEP:
623 case RS2::VSNAP_LINE_VERTEX_VERTEX:
624 case RS2::VSNAP_LINE_VERTEX_ORTHO:
625 case RS2::VSNAP_LINE_DISTANCE_TANGENT2:
626 case RS2::VSNAP_LINE_VERTEX_VERTEX_ORTHO:
627 case RS2::VSNAP_POINT_RELATIVE_ANGLE_RAY:
628 case RS2::VSNAP_POINT_RELATIVE_NORMAL: {
629 double distanceToRay;
630 RS_Vector snap = guideEntity->getNearestPointOnEntity(mouseCoord, true, &distanceToRay);
631 snapEndpoint(mouseCoord, distance);
632 snapIntersection(mouseCoord, distance);
633 snapCenter(mouseCoord, distance);
634
635 if (distance > range) {
636 if (distanceToRay < range) {
637 if (isSnapToGrid()) {
638 const RS_Vector startPoint = guideEntity->getStartpoint();
639 const RS_Vector endPoint = guideEntity->getEndpoint();
640 snap = snapGrid(snap, startPoint, endPoint);
641 m_impData->restrictedSnapType = RS2::SnapType::GRID;
642 }
643 else {
644 // LC_ERR << "VS RAY: ";
645 }
646
647 m_impData->update(snap, RS2::SnapType::VISUAL_SNAP);
648 m_impData->snapCoord = snap;
649 RS2::LC_VisualSnapIntersectionInfo snapInfo;
650 snapInfo.entity1 = RS2::VisualSnapGuideEntityType::VSNAP_NONE;
651 snapInfo.entity2 = snapEntityType;
652 if (snapEntityType == RS2::VSNAP_LINE_VERTEX_ANGLE_STEP || snapEntityType ==
653 RS2::VSNAP_LINE_ENDPOINT_ANGLE_STEP) {
654 snapInfo.rayAngle2 = holder.wcsRayAngleRad;
655 }
656 m_impData->visualSnapType = snapInfo;
657 visualSnapFound = true;
658 // LC_ERR << "VS POINT ON RAY: " << snap;
659 }
660 }
661 else {
662 // visualSnapFound = true;
663 // m_impData->snapCoord = m_impData->snapSpot;
664 visualSnapFound = false;
665 // LC_ERR << "VS POINT FOUND: " << m_impData->snapCoord;
666 }
667 break;
668 }
669 default: {
670 const RS_Entity* ent = holder.entity;
671 RS_Vector snap(false);
672 snap = ent->getNearestPointOnEntity(mouseCoord, true, &distance);
673 if (distance < range) {
674 m_impData->update(snap, RS2::SnapType::VISUAL_SNAP);
675 m_impData->snapCoord = snap;
676 RS2::LC_VisualSnapIntersectionInfo snapInfo;
677 snapInfo.entity1 = RS2::VisualSnapGuideEntityType::VSNAP_NONE;
678 snapInfo.entity2 = snapEntityType;
679 if (snapEntityType == RS2::VSNAP_LINE_VERTEX_ANGLE_STEP || snapEntityType ==
680 RS2::VSNAP_LINE_ENDPOINT_ANGLE_STEP) {
681 snapInfo.rayAngle2 = holder.wcsRayAngleRad;
682 }
683 m_impData->visualSnapType = snapInfo;
684 visualSnapFound = true;
685 }
686 }
687 }
688 }
689 }
690 }
691 }
692 return visualSnapFound;
693}
694
695/**
696 * Snap to a coordinate in the drawing using the current snap mode.
697 *
698 * @param e A mouse event.
699 * @return The coordinates of the point or an invalid vector.
700 */
701RS_Vector RS_Snapper::snapPoint(const QMouseEvent* e) {
702 m_impData->restrictedSnapType = RS2::NO_SNAP;
703 m_impData->update(RS_Vector(false), RS2::SnapType::NO_SNAP);
704 m_impData->restriction = RS2::RestrictNothing;
705 m_impData->snapCoord = RS_Vector(false);
706 m_impData->visualSnapType.entity1 = RS2::VSNAP_NONE;
707 m_impData->visualSnapType.entity2 = RS2::VSNAP_NONE;
708
709 if (e == nullptr) {
710 RS_DEBUGRS_Debug::instance()->print(RS_Debug::D_WARNING, "RS_Snapper::snapPoint: event is nullptr");
711 return m_impData->snapSpot;
712 }
713
714 const RS_Vector mouseCoord = toGraph(e);
715 double ds2Min = RS_MAXDOUBLE1.0E+10 * RS_MAXDOUBLE1.0E+10;
716
717 RS_Entity* restrictingEntity{nullptr};
718 RS2::VisualSnapGuideEntityType restrictingEntityType;
719
720 const bool visualSnapFound = snapVisual(mouseCoord, &restrictingEntity, restrictingEntityType);
721
722 if (!visualSnapFound) {
723 snapEndpoint(mouseCoord, ds2Min);
724 snapCenter(mouseCoord, ds2Min);
725 snapMiddle(mouseCoord, ds2Min);
726 snapDistance(mouseCoord, ds2Min);
727 snapIntersection(mouseCoord, ds2Min);
728 snapOnEntity(mouseCoord, ds2Min);
729 snapGrid(mouseCoord, ds2Min);
730
731 if (!m_impData->snapSpot.valid) {
732 m_impData->snapSpot = mouseCoord; //default to snapFree
733 m_impData->snapType = RS2::SnapType::FREE;
734 }
735 else {
736 //retreat to snapFree when distance is more than quarter grid
737 // issue #1631: snapFree issues: defines getSnapFree as the minimum graph distance to allow SnapFree
738 if (m_snapMode.snapFree) {
739 const double snapRange = getSnapRange();
740 // compare the current graph distance to the closest snap point to the minimum snapping free distance
741 if ((mouseCoord - m_impData->snapSpot).magnitude() >= snapRange) {
742 m_impData->snapSpot = mouseCoord;
743 m_impData->snapType = RS2::SnapType::FREE;
744 }
745 }
746 }
747
748 // handle snap restrictions that can be activated in addition
749 // to the ones above:
750 //apply restriction
751 RS_Vector vpv;
752 RS_Vector vph;
753 if (m_snapMode.restriction != RS2::RestrictNothing) {
754 const RS_Vector rz = m_viewport->getRelativeZero();
755 if (m_viewport->hasUCS()) {
756 const RS_Vector ucsRZ = m_viewport->toUCS(rz);
757 const RS_Vector ucsSnap = m_viewport->toUCS(m_impData->snapSpot);
758 vpv = m_viewport->toWorld(RS_Vector(ucsRZ.x, ucsSnap.y));
759 vph = m_viewport->toWorld(RS_Vector(ucsSnap.x, ucsRZ.y));
760 }
761 else {
762 vpv = RS_Vector(rz.x, m_impData->snapSpot.y);
763 vph = RS_Vector(m_impData->snapSpot.x, rz.y);
764 }
765 }
766
767 switch (m_snapMode.restriction) {
768 case RS2::RestrictOrthogonal: {
769 m_impData->snapCoord = (mouseCoord.distanceTo(vpv) < mouseCoord.distanceTo(vph)) ? vpv : vph;
770 m_impData->restriction = RS2::RestrictOrthogonal;
771 break;
772 }
773 case RS2::RestrictHorizontal: {
774 m_impData->snapCoord = vph;
775 m_impData->restriction = RS2::RestrictHorizontal;
776 break;
777 }
778 case RS2::RestrictVertical: {
779 m_impData->snapCoord = vpv;
780 m_impData->restriction = RS2::RestrictVertical;
781 break;
782 }
783 //case RS2::RestrictNothing:
784 default: {
785 m_impData->snapCoord = m_impData->snapSpot;
786 m_impData->restriction = RS2::RestrictNothing;
787 break;
788 }
789 }
790 }
791 if (restrictingEntity != nullptr) {
792 LC_VisualSnapSolution* visualSnapSolution = m_visualSnapManager->getCurrentSolution();
793 visualSnapSolution->restrictedPoint = m_impData->snapSpot;
794 visualSnapSolution->restrictedOriginalSnapType = m_impData->snapType;
795 m_impData->restrictedSnapType = m_impData->snapType;
796 m_impData->snapType = RS2::SnapType::VISUAL_SNAP;
797 RS2::LC_VisualSnapIntersectionInfo snapInfo;
798 snapInfo.entity1 = RS2::VisualSnapGuideEntityType::VSNAP_NONE;
799 snapInfo.entity2 = restrictingEntityType;
800 m_impData->visualSnapType = snapInfo;
801
802 if (restrictingEntityType == RS2::VSNAP_LINE_VERTEX_VERTICAL || restrictingEntityType == RS2::VSNAP_POINT_RELATIVE_VERTICAL_DX || restrictingEntityType == RS2::VSNAP_LINE_RESTR_VERTICAL) {
803 m_impData->snapSpot = restrictVertical(restrictingEntity->getStartpoint(), m_impData->snapSpot);
804 m_impData->snapCoord = m_impData->snapSpot;
805 }
806 else if (restrictingEntityType == RS2::VSNAP_LINE_VERTEX_HORIZONTAL || restrictingEntityType ==
807 RS2::VSNAP_POINT_RELATIVE_HORIZONTAL_DY || restrictingEntityType == RS2::VSNAP_LINE_RESTR_HORIZONTAL) {
808 m_impData->snapSpot = restrictHorizontal(restrictingEntity->getStartpoint(), m_impData->snapSpot);
809 m_impData->snapCoord = m_impData->snapSpot;
810 }
811 visualSnapSolution->foundSnapPoint = m_impData->snapSpot;
812 }
813
814 setSnapPoint(m_impData->snapSpot, false);
815
816 return m_impData->snapCoord;
817}
818
819/**manually set snapPoint*/
820RS_Vector RS_Snapper::setSnapPoint(const RS_Vector& coord, const bool setSpot) {
821 if (coord.valid) {
822 m_impData->snapSpot = coord;
823 if (setSpot) {
824 m_impData->snapCoord = coord;
825 }
826 // fixme - sand - it seems that the code below is meaning for preview only?
827 updateCoordinateWidgetByRelZero(m_impData->snapCoord);
828 drawSnapper();
829 drawInfoCursor();
830 }
831 return coord;
832}
833
834double RS_Snapper::getSnapRange() const {
835 // issue #1631: redefine this method to the minimum graph distance to allow "Snap Free"
836 // When the closest of any other snapping point is beyond this distance, free snapping is used.
837
838 double minGraph = RS_MAXDOUBLE1.0E+10;
839 double minGrid = RS_MAXDOUBLE1.0E+10;
840 double minSize = RS_MAXDOUBLE1.0E+10;
841 if (m_graphicView != nullptr) {
842 minGraph = toGraphDX(DEFAULT_CATCH_ENTITY_RANGE_PX);
843 // if grid is on, less than one quarter of the cell vector
844 // if (viewport->isGridOn()) {
845 // todo - sand - check whether it's correct apply this check only if "Snap to Grid" is enabled
846 if (m_viewport->isGridOn() && m_snapMode.snapGrid) {
847 const RS_Grid* grid = m_viewport->getGrid();
848 const RS_Vector& cellVector = grid->getCellVector();
849 minGrid = cellVector.magnitude() * m_minGridCellSnapFactor;
850 }
851 }
852 if (isSizeValid(m_document->getSize())) {
853 // The size bounding box
854 minSize = getValidSize(m_document->getSize());
855 }
856 if (std::min(minGraph, minGrid) < 0.99 * RS_MAXDOUBLE1.0E+10) {
857 return std::min(minGraph, minGrid);
858 }
859 if (minSize < 0.99 * RS_MAXDOUBLE1.0E+10) {
860 return minSize;
861 }
862 // shouldn't happen: no graphicview or a valid size
863 // Allow free snapping by returning the floating point tolerance
864 return RS_TOLERANCE1.0e-10;
865}
866
867/**
868 * Snaps to a free coordinate.
869 *
870 * @param coord The mouse coordinate.
871 * @return The coordinates of the point or an invalid vector.
872 */
873RS_Vector RS_Snapper::snapFree(const RS_Vector& coord) {
874 m_keyEntity = nullptr;
875 return coord;
876}
877
878/**
879 * Snaps to the closest endpoint.
880 *
881 * @param coord The mouse coordinate.
882 * @return The coordinates of the point or an invalid vector.
883 */
884RS_Vector RS_Snapper::snapEndpoint(const RS_Vector& coord, RS_Entity** entity) const {
885 const RS_Vector vec = m_document->getNearestEndpoint(coord, entity, nullptr);
886 return vec;
887}
888
889/**
890 * Snaps to a grid point.
891 *
892 * @param coord The mouse coordinate.
893 * @return The coordinates of the point or an invalid vector.
894 */
895RS_Vector RS_Snapper::snapGrid(const RS_Vector& coord) const {
896 // RS_DEBUG->print("RS_Snapper::snapGrid begin");
897
898 // std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
899 // std::cout<<" mouse: = "<<coord<<std::endl;
900 // std::cout<<" snapGrid: = "<<graphicView->getGrid()->snapGrid(coord)<<std::endl;
901 return m_viewport->snapGrid(coord);
902}
903
904RS_Vector RS_Snapper::snapGrid(const RS_Vector& coord, const RS_Vector& rayStart, const RS_Vector& rayEnd) const {
905 return m_viewport->snapGrid(coord, rayStart, rayEnd);
906}
907
908// fixme - note 1) similarly to ray, actually any entity may be used with snap to grid if it will be possisble to find
909// intersection point points of constuction lines with this grid. However, for UCS - it's necessary to transform the entity
910// accordingly (or, alternativealy, transform a grid cell.. ).
911// Fixme - note 2) it might be practical add method that returns grid cell - and do transformation in viewport
912RS_Vector RS_Snapper::snapGrid(const RS_Vector& coord, RS_Entity* entity) const {
913 return m_viewport->snapGrid(coord, entity);
914}
915
916/**
917 * Snaps to a point on an entity.
918 *
919 * @param coord The mouse coordinate.
920 * @return The coordinates of the point or an invalid vector.
921 */
922RS_Vector RS_Snapper::snapOnEntity(const RS_Vector& coord, RS_Entity** entity) {
923 // Text is skipped at every depth: points on glyphs are never snap targets, and measuring
924 // them would walk every glyph (issue #2804)
925 RS_Entity* nearest = nullptr;
926 if (m_snapMode.snapFree) {
927 // A point beyond the snap range gives way to free snapping anyway, and must not leave a key
928 // entity behind: Delete Free acts on the key entity.
929 RS_EntityContainer nearby(nullptr, false);
930 m_document->appendNearby(coord, getSnapRange(), nearby);
931 nearby.getDistanceToPoint(coord, &nearest, RS2::ResolveAllButTexts);
932 }
933 else {
934 // the drawing skips every entity that lies farther away than the nearest one found so far
935 m_document->getDistanceToPoint(coord, &nearest, RS2::ResolveAllButTexts);
936 }
937
938 RS_Vector point(false);
939 m_keyEntity = nullptr;
940 // what RS_EntityContainer::getNearestPointOnEntity() refuses to snap to
941 const RS_EntityContainer* parent = nearest != nullptr ? nearest->getParent() : nullptr;
942 if (nearest != nullptr && (parent == nullptr || !parent->ignoredSnap())) {
943 RS_Entity* snapEntity = nullptr;
944 point = nearest->getNearestPointOnEntity(coord, true, nullptr, &snapEntity);
945 if (point.valid) {
946 m_keyEntity = snapEntity != nullptr ? snapEntity : nearest;
947 }
948 }
949 *entity = m_keyEntity;
950 return point;
951}
952
953/**
954 * Snaps to the closest center.
955 *
956 * @param coord The mouse coordinate.
957 * @return The coordinates of the point or an invalid vector.
958 */
959RS_Vector RS_Snapper::snapCenter(const RS_Vector& coord, RS_Entity** centerEntity) const {
960 const RS_Vector vec = m_document->getNearestCenter(coord, nullptr, centerEntity);
961 return vec;
962}
963
964/**
965 * Snaps to the closest middle.
966 *
967 * @param coord The mouse coordinate.
968 * @return The coordinates of the point or an invalid vector.
969 */
970RS_Vector RS_Snapper::snapMiddle(const RS_Vector& coord) const {
971 //std::cout<<"RS_Snapper::snapMiddle(): middlePoints="<<middlePoints<<std::endl;
972 return m_document->getNearestMiddle(coord, nullptr, m_middlePoints);
973}
974
975int RS_Snapper::getSnapMiddlePoints() const {
976 return m_middlePoints;
977}
978
979double RS_Snapper::getSnapDistance() const {
980 return m_snapDistance;
981}
982
983/**
984 * Snaps to the closest point with a given distance to the endpoint.
985 *
986 * @param coord The mouse coordinate.
987 * @return The coordinates of the point or an invalid vector.
988 */
989RS_Vector RS_Snapper::snapDist(const RS_Vector& coord) const {
990 //std::cout<<" RS_Snapper::snapDist(RS_Vector coord): distance="<<distance<<std::endl;
991 const RS_Vector vec = m_document->getNearestDist(m_snapDistance, coord, nullptr);
992 return vec;
993}
994
995/**
996 * Snaps to the closest intersection point.
997 *
998 * @param coord The mouse coordinate.
999 * @return The coordinates of the point or an invalid vector.
1000 */
1001RS_Vector RS_Snapper::snapIntersection(const RS_Vector& coord, [[maybe_unused]] RS_Entity** entity,
1002 [[maybe_unused]] RS_Entity** otherEntity) const {
1003 const RS_Vector vec = m_document->getNearestIntersection(coord, nullptr);
1004 return vec;
1005}
1006
1007/**
1008 * 'Corrects' the given coordinates to 0, 90, 180, 270 degrees relative to
1009 * the current relative zero point.
1010 *
1011 * @param coord The uncorrected coordinates.
1012 * @return The corrected coordinates.
1013 */
1014RS_Vector RS_Snapper::restrictOrthogonal(const RS_Vector& coord) const {
1015 const RS_Vector rz = m_viewport->getRelativeZero();
1016 RS_Vector ret(coord);
1017
1018 const auto retx = RS_Vector(rz.x, ret.y);
1019 const auto rety = RS_Vector(ret.x, rz.y);
1020
1021 if (retx.distanceTo(ret) > rety.distanceTo(ret)) {
1022 ret = rety;
1023 }
1024 else {
1025 ret = retx;
1026 }
1027
1028 return ret;
1029}
1030
1031/**
1032 * 'Corrects' the given coordinates to 0, 180 degrees relative to
1033 * the current relative zero point.
1034 *
1035 * @param coord The uncorrected coordinates.
1036 * @return The corrected coordinates.
1037 */
1038
1039RS_Vector RS_Snapper::restrictHorizontal(const RS_Vector& coord) const {
1040 return m_viewport->restrictHorizontal(m_viewport->getRelativeZero(), coord);
1041}
1042
1043/**
1044 * 'Corrects' the given coordinates to 90, 270 degrees relative to
1045 * the current relative zero point.
1046 *
1047 * @param coord The uncorrected coordinates.
1048 * @return The corrected coordinates.
1049 */
1050RS_Vector RS_Snapper::restrictVertical(const RS_Vector& coord) const {
1051 return m_viewport->restrictVertical(m_viewport->getRelativeZero(), coord);
1052}
1053
1054RS_Vector RS_Snapper::restrictVertical(const RS_Vector& base, const RS_Vector& coord) const {
1055 return m_viewport->restrictVertical(base, coord);
1056}
1057
1058RS_Vector RS_Snapper::restrictHorizontal(const RS_Vector& base, const RS_Vector& coord) const {
1059 return m_viewport->restrictHorizontal(base, coord);
1060}
1061
1062RS_Vector RS_Snapper::restrictAngle(const RS_Vector& basePoint, const RS_Vector& snap, const double angle) const {
1063 const double realAngle = toWorldAngle(angle);
1064 const RS_Vector infiniteTickEndPoint = basePoint.relative(10.0, realAngle);
1065 const RS_Vector pointOnInfiniteTick = LC_LineMath::getNearestPointOnInfiniteLine(snap, basePoint, infiniteTickEndPoint);
1066
1067 const RS_Vector possibleEndPoint = pointOnInfiniteTick;
1068 return possibleEndPoint;
1069}
1070
1071/**
1072 * Catches an entity which is close to the given position 'pos'.
1073 *
1074 * @param pos A graphic coordinate.
1075 * @param level The level of resolving for iterating through the entity
1076 * container
1077 * @return Pointer to the entity or nullptr.
1078 */
1079RS_Entity* RS_Snapper::catchEntity(const RS_Vector& pos, const RS2::ResolveLevel level, double* distance) const {
1080 // set default distance for points inside solids
1081 double dist(0.);
1082 RS_Entity* entity = m_document->getNearestEntity(pos, &dist, level);
1083 if (distance != nullptr) {
1084 *distance = dist;
1085 }
1086 if (entity != nullptr && dist <= getCatchDistance(getSnapRange(), m_catchEntityGuiRange)) {
1087 RS_DEBUGRS_Debug::instance()->print("RS_Snapper::catchEntity: found");
1088 return entity;
1089 }
1090 RS_DEBUGRS_Debug::instance()->print("RS_Snapper::catchEntity: not found");
1091 return nullptr;
1092}
1093
1094/**
1095 * Catches an entity which is close to the given position 'pos'.
1096 *
1097 * @param pos A graphic coordinate.
1098 * @param enType
1099 * @param level The level of resolving for iterating through the entity
1100 * container
1101 * @enType, only search for a particular entity type
1102 * @return Pointer to the entity or nullptr.
1103 */
1104RS_Entity* RS_Snapper::catchEntity(const RS_Vector& pos, const RS2::EntityType enType, const RS2::ResolveLevel level) const {
1105 return catchEntity(pos, EntityTypeList{enType}, level);
1106}
1107
1108/**
1109 * Catches an entity which is close to the mouse cursor.
1110 *
1111 * @param e A mouse event.
1112 * @param level The level of resolving for iterating through the entity
1113 * container
1114 * @return Pointer to the entity or nullptr.
1115 */
1116RS_Entity* RS_Snapper::catchEntity(const QMouseEvent* e, const RS2::ResolveLevel level) const {
1117 RS_Entity* entity = catchEntity(toGraph(e), level);
1118 return entity;
1119}
1120
1121/**
1122 * Catches an entity which is close to the mouse cursor.
1123 *
1124 * @param e A mouse event.
1125 * @param enType
1126 * @param level The level of resolving for iterating through the entity
1127 * container
1128 * @enType, only search for a particular entity type
1129 * @return Pointer to the entity or nullptr.
1130 */
1131RS_Entity* RS_Snapper::catchEntity(const QMouseEvent* e, const RS2::EntityType enType, const RS2::ResolveLevel level) const {
1132 return catchEntity(toGraph(e), enType, level);
1133}
1134
1135RS_Entity* RS_Snapper::catchEntity(const QMouseEvent* e, const EntityTypeList& enTypeList, const RS2::ResolveLevel level) const {
1136 const RS_Vector coord = toGraph(e);
1137 return catchEntity(coord, enTypeList, level);
1138}
1139
1140RS_Entity* RS_Snapper::catchEntity(const RS_Vector& pos, const EntityTypeList& enTypeList, const RS2::ResolveLevel level) const {
1141 if (enTypeList.empty()) {
1142 return catchEntity(pos, level);
1143 }
1144
1145 const double catchDistance = getCatchDistance(getSnapRange(), m_catchEntityGuiRange);
1146 RS_EntityContainer nearby(nullptr, false);
1147 m_document->appendNearby(pos, catchDistance, nearby);
1148 // Text and MText stay whole: their glyphs are generated geometry that no action may pick or
1149 // edit, and walking them on every mouse move froze Fillet (issue #2804)
1150 const RS2::ResolveLevel candidateLevel = level == RS2::ResolveAll ? RS2::ResolveAllButTexts : level;
1151 const std::vector<RS_Entity*> candidates = lc::LC_ContainerTraverser{nearby, candidateLevel}.entities();
1152
1153 // The matches of each type follow those of the types listed before it, so an exact tie goes to
1154 // the type listed last and, within a type, to the entity drawn last, as when each type was
1155 // caught on its own.
1156 RS_EntityContainer matches(nullptr, false);
1157 for (const RS2::EntityType type : enTypeList) {
1158 for (RS_Entity* candidate : candidates) {
1159 if (candidate->isVisible() && matchesRequestedType(*candidate, type)) {
1160 matches.push_back(candidate);
1161 }
1162 }
1163 }
1164 double dist = 0.;
1165 RS_Entity* entity = matches.getNearestEntity(pos, &dist, RS2::ResolveNone);
1166 return entity != nullptr && dist <= catchDistance ? entity : nullptr;
1167}
1168
1169void RS_Snapper::suspend() {
1170 m_impData->snapSpot = RS_Vector{false};
1171 m_impData->snapCoord = RS_Vector{false};
1172 suspendRelativeInputWidget();
1173}
1174
1175void RS_Snapper::resume() {
1176 drawSnapper();
1177 initSettings();
1178 // fixme - review/rework this, load from settings as other overlays action(??)
1179 m_infoCursorOverlayPrefs = m_graphicView->getInfoCursorOverlayPreferences();
1180 resumeRelativeInputWidget();
1181}
1182
1183/**
1184 * Hides the snapper options. Default implementation does nothing.
1185 */
1186void RS_Snapper::hideSnapOptions() const {
1187 m_actionContext->hideSnapOptions();
1188}
1189
1190/**
1191 * Deletes the snapper from the screen.
1192 */
1193void RS_Snapper::deleteSnapper() const {
1194 if (m_graphicView != nullptr && !m_graphicView->isCleanUp()) {
1195 m_viewport->clearOverlayDrawablesContainer(RS2::Snapper);
1196 m_graphicView->redraw(RS2::RedrawOverlay); // redraw will happen in the mouse movement event
1197 }
1198}
1199
1200void RS_Snapper::deleteInfoCursor() const {
1201 if (m_graphicView != nullptr && !m_graphicView->isCleanUp()) {
1202 m_viewport->clearOverlayDrawablesContainer(RS2::InfoCursor);
1203 m_graphicView->redraw(RS2::RedrawOverlay); // redraw will happen in the mouse movement event
1204 }
1205}
1206
1207/**
1208 * creates the snap indicator
1209 */
1210void RS_Snapper::drawSnapper() {
1211 LC_OverlayDrawablesContainer* snapperOverlay = m_viewport->getOverlaysDrawablesContainer(RS2::Snapper);
1212 snapperOverlay->clear();
1213 bool showSnapIndicator = showSnapIndicator = isSnapExpected(); // LibreCAD#2520
Although the value stored to 'showSnapIndicator' is used in the enclosing expression, the value is never actually read from 'showSnapIndicator'
1214 if (!m_finished && m_impData->snapSpot.valid && showSnapIndicator) {
1215 if (m_snapIndicator->drawLines || m_snapIndicator->drawShape) {
1216 auto* crosshair = new LC_Crosshair(m_impData->snapCoord, m_snapIndicator->shape_Type, m_snapIndicator->lines_Type,
1217 m_snapIndicator->lines_Pen, m_snapIndicator->pointSize, m_snapIndicator->pointType);
1218 crosshair->setShapesPen(m_snapIndicator->shape_Pen);
1219 snapperOverlay->add(crosshair);
1220 }
1221 }
1222 m_graphicView->redraw(RS2::RedrawOverlay); // redraw will happen in the mouse movement event
1223}
1224
1225LC_OverlayInfoCursor* RS_Snapper::obtainInfoCursor() const {
1226 const auto overlayContainer = m_viewport->getOverlaysDrawablesContainer(RS2::InfoCursor);
1227 LC_OverlayInfoCursor* result = nullptr;
1228 // fixme - this is not absolutely safe if someone put another cursor to overlay container! Rework later!!
1229 const auto entity = overlayContainer->first(); // note - this is not absolutely safe if someone put another cursor to overlay container!
1230 result = dynamic_cast<LC_OverlayInfoCursor*>(entity);
1231 if (result == nullptr && m_infoCursorOverlayPrefs != nullptr) {
1232 result = new LC_OverlayInfoCursor(m_impData->snapCoord, &m_infoCursorOverlayPrefs->options);
1233 overlayContainer->add(result);
1234 }
1235 return result;
1236}
1237
1238void RS_Snapper::drawInfoCursor() {
1239 const auto overlayContainer = m_viewport->getOverlaysDrawablesContainer(RS2::InfoCursor);
1240 if (m_infoCursorOverlayPrefs != nullptr && m_infoCursorOverlayPrefs->enabled) {
1241 // fixme - this is not absolutely safe if someone put another cursor to overlay container! Rework later!!
1242 const auto entity = overlayContainer->first();
1243 auto* infoCursor = dynamic_cast<LC_OverlayInfoCursor*>(entity);
1244 if (infoCursor == nullptr) {
1245 infoCursor = new LC_OverlayInfoCursor(m_impData->snapCoord, &m_infoCursorOverlayPrefs->options);
1246 overlayContainer->add(infoCursor);
1247 }
1248 else {
1249 infoCursor->setOptions(&m_infoCursorOverlayPrefs->options);
1250 infoCursor->setPos(m_impData->snapCoord);
1251 }
1252 const auto prefs = getInfoCursorOverlayPrefs();
1253 if (prefs->showSnapType) {
1254 if (m_graphicView->isInRelativePointInput()) {
1255 m_infoCursorOverlayData->setZone2("");
1256 }
1257 else {
1258 QString snapName = getCurrentSnapName();
1259 QString restrictionName;
1260 if (m_impData->snapType == RS2::ANGLE || m_impData->snapType == RS2::ANGLE_REL || m_impData->snapType == RS2::ANGLE_ON_ENTITY) {
1261 const double ucsAbsSnapAngle = m_impData->angle;
1262 const double ucsBasisAngle = m_formatter->toUCSBasisAngleFromUCS(ucsAbsSnapAngle);
1263 restrictionName = formatAngleRaw(ucsBasisAngle);
1264 }
1265 else {
1266 restrictionName = getCurrentRestrictionName();
1267 }
1268 if (!restrictionName.isEmpty()) {
1269 snapName = snapName + (prefs->multiLine ? "\n" : " | ") + restrictionName;
1270 }
1271 m_infoCursorOverlayData->setZone2(snapName);
1272 }
1273 }
1274 else {
1275 m_infoCursorOverlayData->setZone2("");
1276 }
1277 infoCursor->setZonesData(m_infoCursorOverlayData.get());
1278 }
1279 m_graphicView->redraw(RS2::RedrawOverlay);
1280}
1281
1282QString RS_Snapper::getRestrictionName(const int restriction) {
1283 switch (restriction) {
1284 case RS2::RestrictVertical:
1285 return tr("Vertical");
1286 case RS2::RestrictHorizontal:
1287 return tr("Horizontal");
1288 case RS2::RestrictOrthogonal:
1289 return tr("Orthogonal");
1290 default:
1291 return "";
1292 }
1293}
1294
1295QString RS_Snapper::getCurrentRestrictionName() {
1296 if (m_impData->snapType == RS2::VISUAL_SNAP) {
1297 if (m_impData->visualSnapType.entity1 == RS2::VSNAP_NONE) {
1298 const auto secondEntityType = m_impData->visualSnapType.entity2;
1299 if (secondEntityType == RS2::VSNAP_LINE_VERTEX_VERTICAL) {
1300 return getSnapName(m_impData->restrictedSnapType);
1301 }
1302 if (secondEntityType == RS2::VSNAP_LINE_VERTEX_HORIZONTAL) {
1303 return getSnapName(m_impData->restrictedSnapType);
1304 }
1305 if (secondEntityType == RS2::VSNAP_NONE) {
1306 return getSnapName(m_impData->restrictedSnapType);
1307 }
1308 if (secondEntityType == RS2::VSNAP_POINT_RELATIVE_VERTICAL_DX) {
1309 return getSnapName(m_impData->restrictedSnapType);
1310 }
1311 if (secondEntityType == RS2::VSNAP_POINT_RELATIVE_HORIZONTAL_DY) {
1312 return getSnapName(m_impData->restrictedSnapType);
1313 }
1314 }
1315 return "";
1316 }
1317 else {
1318 return getRestrictionName(m_impData->restriction);
1319 }
1320}
1321
1322QString RS_Snapper::getCurrentSnapName() const {
1323 const RS2::SnapType snapType = m_impData->snapType;
1324 QString msg = getSnapName(snapType);
1325 if (snapType == RS2::SnapType::VISUAL_SNAP) {
1326 const auto pointType = m_impData->visualSnapType;
1327 if (pointType.entity1 == RS2::VSNAP_NONE) {
1328 // this is point
1329 msg = msg + " (" + getVisualSnapName(pointType.entity2, pointType.rayAngle2) + ")";
1330 }
1331 else {
1332 // this is intersection
1333 msg = msg + " (" + getVisualSnapName(pointType.entity1, pointType.rayAngle1) + " x " + getVisualSnapName(
1334 pointType.entity2, pointType.rayAngle2) + ")";
1335 }
1336 }
1337 return msg;
1338}
1339
1340QString RS_Snapper::getSnapName(RS2::SnapType snapType) const {
1341 switch (snapType) {
1342 case RS2::GRID:
1343 return tr("Grid");
1344 case RS2::ENTITY:
1345 return tr("Entity");
1346 case RS2::ENDPOINT:
1347 return tr("Endpoint");
1348 case RS2::INTERSECTION:
1349 return tr("Intersection");
1350 case RS2::MIDDLE:
1351 return tr("Middle");
1352 case RS2::DISTANCE:
1353 return tr("Distance");
1354 case RS2::CENTER:
1355 return tr("Center");
1356 case RS2::ANGLE:
1357 return tr("Angle");
1358 case RS2::ANGLE_REL:
1359 return tr("Angle Relative");
1360 case RS2::ANGLE_ON_ENTITY:
1361 return tr("Angle (on Entity)");
1362 case RS2::VISUAL_SNAP: {
1363 QString msg = tr("Visual");
1364 return msg;
1365 }
1366 case RS2::FREE:
1367 return tr("Free");
1368 default:
1369 return "";
1370 }
1371}
1372
1373QString RS_Snapper::getVisualSnapName(const RS2::VisualSnapGuideEntityType entityType, double rayAngle) const {
1374 switch (entityType) {
1375 case RS2::VSNAP_NONE: {
1376 return tr("None", "visual snap");
1377 }
1378 case RS2::VSNAP_LINE_VERTEX_HORIZONTAL: {
1379 return tr("Horizontal", "visual snap");
1380 }
1381 case RS2::VSNAP_LINE_VERTEX_ANGLE_STEP: {
1382 QString name = tr("Angle Ray");
1383 const double wcsRayAngle = rayAngle;
1384 if (wcsRayAngle > 0) {
1385 const double ucsAbsRayAngle = toUCSBasisAngle(wcsRayAngle);
1386 const double ucsBasisAngle = m_formatter->toUCSBasisAngleFromUCS(ucsAbsRayAngle);
1387 QString angleName = formatAngleRaw(ucsBasisAngle);
1388 name = name + " " + angleName;
1389 }
1390 return name;
1391 }
1392 case RS2::VSNAP_LINE_ENDPOINT_ANGLE_STEP: {
1393 QString name = tr("Relative Angle Ray");
1394 const double wcsRayAngle = rayAngle;
1395 if (wcsRayAngle > 0) {
1396 const double ucsAbsRayAngle = toUCSBasisAngle(wcsRayAngle);
1397 const double ucsBasisAngle = m_formatter->toUCSBasisAngleFromUCS(ucsAbsRayAngle);
1398 QString angleName = formatAngleRaw(ucsBasisAngle);
1399 name = name + " " + angleName;
1400 }
1401 return name;
1402 }
1403 case RS2::VSNAP_LINE_VERTEX_VERTICAL: {
1404 return tr("Vertical", "visual snap");
1405 }
1406 case RS2::VSNAP_LINE_RAY: {
1407 return tr("Line ray");
1408 }
1409 case RS2::VSNAP_LINE_VERTEX_VERTEX: {
1410 return tr("Point-Point");
1411 }
1412 case RS2::VSNAP_LINE_VERTEX_VERTEX_ORTHO: {
1413 return tr("Point-Point Orthogonal");
1414 }
1415 case RS2::VSNAP_LINE_ENDPOINT_TANGENT: {
1416 return tr("Endpoint Tangential");
1417 }
1418 case RS2::VSNAP_LINE_ENDPOINT_NORMAL: {
1419 return tr("Endpoint Normal");
1420 }
1421 case RS2::VSNAP_LINE_TANGENT1: {
1422 return tr("Tangential One");
1423 }
1424 case RS2::VSNAP_LINE_TANGENT2: {
1425 return tr("Tangential Two");
1426 }
1427 case RS2::VSNAP_POINT_MIDDLE: {
1428 return tr("Middle", "visual snap");
1429 }
1430 case RS2::VSNAP_LINE_VERTEX_ORTHO: {
1431 return tr("Orthogonal", "visual snap");
1432 }
1433 case RS2::VSNAP_POINT_DISTANCE_EXPLICIT: {
1434 return tr("Distance (Explicit)");
1435 }
1436 case RS2::VSNAP_LINE_DISTANCE_TANGENT2: {
1437 return tr("Tangential Distance (Explicit)");
1438 }
1439 case RS2::VSNAP_POINT_DISTANCE_VERTEX: {
1440 return tr("Distance (Point)");
1441 }
1442 case RS2::VSNAP_DOC_ENTITY: {
1443 return tr("Entity", "visual snap");
1444 }
1445 case RS2::VSNAP_POINT_RELATIVE_NORMAL: {
1446 return tr("Relative Normal", "visual snap");
1447 }
1448 case RS2::VSNAP_POINT_RELATIVE_DISTANCE: {
1449 return tr("Relative Distance", "visual snap");
1450 }
1451 case RS2::VSNAP_POINT_RELATIVE_VERTICAL_DX: {
1452 return tr("Relative X", "visual snap");
1453 }
1454 case RS2::VSNAP_POINT_RELATIVE_HORIZONTAL_DY: {
1455 return tr("Relative Y", "visual snap");
1456 }
1457 case RS2::VSNAP_POINT_RELATIVE_ANGLE_RAY: {
1458 return tr("Relative Angle", "visual snap");
1459 }
1460 case RS2::VSNAP_LINE_RESTR_HORIZONTAL: {
1461 return tr("Restriction Horizontal", "visual snap");
1462 }
1463 case RS2::VSNAP_LINE_RESTR_VERTICAL: {
1464 return tr("Restriction Vertical", "visual snap");
1465 }
1466 default:
1467 return "";
1468 }
1469}
1470
1471bool RS_Snapper::isSnapToGrid() const {
1472 bool result = m_snapMode.snapGrid;
1473 if (result) {
1474 if (m_ignoreSnapToGridIfNoGrid) {
1475 result = m_viewport->isGridOn();
1476 }
1477 }
1478 return result;
1479}
1480
1481bool RS_Snapper::isLastSnapFree() const {
1482 return m_impData->snapType == RS2::SnapType::FREE;
1483}
1484
1485RS_Vector RS_Snapper::snapToRelativeAngle(const double baseAngle, const RS_Vector& currentCoord, const RS_Vector& referenceCoord,
1486 const double angularResolution) {
1487 if (m_snapMode.restriction != RS2::RestrictNothing || isSnapToGrid()) {
1488 return currentCoord;
1489 }
1490
1491 const double wcsAngleRaw = referenceCoord.angleTo(currentCoord);
1492 const double ucsAngleRaw = toUCSAngle(wcsAngleRaw);
1493 const double ucsAngleSnapped = ucsAngleRaw - std::remainder(ucsAngleRaw, angularResolution);
1494 const double wcsAngleSnappedAbsolute = toWorldAngle(ucsAngleSnapped);
1495
1496 const double wcsAngleSnapped = wcsAngleSnappedAbsolute + baseAngle; // add base angle, so snap is relative
1497
1498 RS_Vector res = RS_Vector::polar(referenceCoord.distanceTo(currentCoord), wcsAngleSnapped);
1499 res += referenceCoord;
1500
1501 if (m_snapMode.snapOnEntity) {
1502 const RS_Vector t = m_document->getNearestVirtualIntersection(res, wcsAngleSnapped, nullptr);
1503 m_impData->snapSpot = t;
1504 m_impData->snapType = (t == res) ? RS2::SnapType::ANGLE_REL : RS2::SnapType::ANGLE_ON_ENTITY;
1505 m_impData->angle = ucsAngleSnapped;
1506 setSnapPoint(m_impData->snapSpot, true);
1507 return t;
1508 }
1509 m_impData->snapType = RS2::SnapType::ANGLE_REL;
1510 m_impData->angle = ucsAngleSnapped;
1511 setSnapPoint(res, true);
1512 return res;
1513}
1514
1515// fixme - snap to angle - rwork
1516RS_Vector RS_Snapper::snapToAngle(const RS_Vector& currentCoord, const RS_Vector& referenceCoord, const double angularResolution) {
1517 if (m_snapMode.restriction != RS2::RestrictNothing || isSnapToGrid()) {
1518 return currentCoord;
1519 }
1520 return doSnapToAngle(currentCoord, referenceCoord, angularResolution);
1521}
1522
1523RS_Vector RS_Snapper::obtainEndPointForAngleSnap(const RS_Vector& currentCoord, const RS_Vector& referenceCoord,
1524 const double angularResolution, double& wcsAngleSnapped, double& ucsAngleSnapped) const {
1525 const double wcsAngleRaw = referenceCoord.angleTo(currentCoord);
1526 const double ucsAngleAbs = toUCSAngle(wcsAngleRaw);
1527
1528 // double ucsAngle = ucsAngleAbs - this->m_anglesBase;
1529 // fixme - fmt - review
1530 const double ucsAngle = m_formatter->toUCSBasisAngleFromUCS(ucsAngleAbs);
1531
1532 ucsAngleSnapped = ucsAngleAbs - remainder(ucsAngle, angularResolution);
1533 wcsAngleSnapped = toWorldAngle(ucsAngleSnapped);
1534
1535 RS_Vector res = RS_Vector::polar(referenceCoord.distanceTo(currentCoord), wcsAngleSnapped);
1536 res += referenceCoord;
1537 return res;
1538}
1539
1540RS_Vector RS_Snapper::doSnapToAngle(const RS_Vector& currentCoord, const RS_Vector& referenceCoord, const double angularResolution) {
1541 double wcsAngleSnapped;
1542 double ucsAngleSnapped;
1543 const RS_Vector res = obtainEndPointForAngleSnap(currentCoord, referenceCoord, angularResolution, wcsAngleSnapped, ucsAngleSnapped);
1544
1545 if (m_snapMode.snapOnEntity) {
1546 const RS_Vector t = m_document->getNearestVirtualIntersection(res, wcsAngleSnapped, nullptr);
1547 m_impData->snapSpot = t;
1548 m_impData->snapType = (t == res) ? RS2::ANGLE : RS2::ANGLE_ON_ENTITY;
1549 m_impData->angle = ucsAngleSnapped;
1550 setSnapPoint(m_impData->snapSpot, true);
1551 return t;
1552 }
1553 m_impData->snapType = RS2::ANGLE;
1554 m_impData->angle = ucsAngleSnapped;
1555 setSnapPoint(res, true);
1556 return res;
1557}
1558
1559RS_Vector RS_Snapper::toGraph(const QMouseEvent* e) const {
1560 const QPointF& pointF = e->position();
1561 const RS_Vector result = m_viewport->toWorldFromUi(pointF.x(), pointF.y());
1562 return result;
1563}
1564
1565double RS_Snapper::toGuiDX(const double wcsDX) const {
1566 return m_viewport->toGuiDX(wcsDX);
1567}
1568
1569double RS_Snapper::toGraphDX(const int wcsDX) const {
1570 return m_viewport->toUcsDX(wcsDX);
1571}
1572
1573const RS_Vector& RS_Snapper::getRelativeZero() const {
1574 return m_viewport->getRelativeZero();
1575}
1576
1577void RS_Snapper::refreshBySettings() {
1578 initFromSettings();
1579 updateSnapAngleStep();
1580 m_visualSnapManager->invalidateSolution();
1581}
1582
1583void RS_Snapper::lockVisualSnap(bool performLock) const {
1584 m_visualSnapManager->lockData(performLock);
1585}
1586
1587void RS_Snapper::updateCoordinateWidgetFormat() const {
1588 m_actionContext->updateCoordinateWidget(toWorld(RS_Vector(0.0, 0.0)), toWorld(RS_Vector(0.0, 0.0)), true);
1589}
1590
1591void RS_Snapper::updateCoordinateWidget(const RS_Vector& abs, const RS_Vector& rel) const {
1592 if (m_infoCursorOverlayPrefs->enabled) {
1593 preparePositionsInfoCursorOverlay(abs, rel);
1594 }
1595 m_actionContext->updateCoordinateWidget(abs, rel, false);
1596}
1597
1598void RS_Snapper::updateCoordinateWidgetByRelZero(const RS_Vector& abs) const {
1599 const RS_Vector& relative = abs - m_viewport->getRelativeZero();
1600 if (m_infoCursorOverlayPrefs->enabled) {
1601 preparePositionsInfoCursorOverlay(abs, relative);
1602 }
1603 m_actionContext->updateCoordinateWidget(abs, relative, false);
1604}
1605
1606LC_InfoCursorOverlayPrefs* RS_Snapper::getInfoCursorOverlayPrefs() const {
1607 return m_infoCursorOverlayPrefs;
1608}
1609
1610bool RS_Snapper::isInfoCursorForModificationEnabled() const {
1611 return m_infoCursorOverlayPrefs->enabled && m_infoCursorOverlayPrefs->showEntityInfoOnModification;
1612}
1613
1614void RS_Snapper::preparePositionsInfoCursorOverlay(const RS_Vector& abs, const RS_Vector& relative) const {
1615 const LC_InfoCursorOverlayPrefs* prefs = getInfoCursorOverlayPrefs();
1616
1617 QString coordAbs = "";
1618 QString coordPolar = "";
1619 if (prefs != nullptr && (prefs->showAbsolutePosition || prefs->showRelativePositionDistAngle || prefs->showRelativePositionDeltas)) {
1620 const RS_Graphic* graphic = m_graphicView->getGraphic();
1621 if (graphic != nullptr) {
1622 const bool showLabels = prefs->showLabels;
1623 if (prefs->showAbsolutePosition) {
1624 const RS_Vector ucs = toUCS(abs);
1625 const QString absX = (showLabels ? "X: " : "") + formatLinear(ucs.x);
1626 const QString absY = (showLabels ? "Y: " : "") + formatLinear(ucs.y);
1627 coordAbs = absX + (prefs->multiLine ? "\n" : showLabels ? " " : " , ") + absY;
1628 }
1629
1630 const bool hasUCS = m_viewport->hasUCS();
1631 if (prefs->showAbsolutePositionWCS && hasUCS) {
1632 const QString absX = (showLabels ? "WX: " : "W") + formatLinear(abs.x);
1633 const QString absY = (showLabels ? "WY: " : "") + formatLinear(abs.y);
1634
1635 const QString coordAbsWCS = absX + (prefs->multiLine ? "\n" : showLabels ? " " : " , ") + absY;
1636
1637 if (coordAbs.isEmpty()) {
1638 coordAbs = coordAbsWCS;
1639 }
1640 else {
1641 coordAbs = coordAbs + "\n" + coordAbsWCS;
1642 }
1643 }
1644
1645 RS_Vector relativeToUse;
1646 if (hasUCS) {
1647 relativeToUse = m_viewport->toUCSDelta(relative);
1648 }
1649 else {
1650 relativeToUse = relative;
1651 }
1652
1653 if (prefs->showRelativePositionDistAngle) {
1654 const QString lenStr = (showLabels ? tr("Dist: ") : "@ ") + formatLinear(relativeToUse.magnitude());
1655 // as we're in ucs coordinates there, use raw formatAngle instead of method
1656
1657 const double relativeAngle = relativeToUse.angle();
1658 const double ucsBasisAngle = ucsAbsToBasisAngle(relativeAngle);
1659 const QString angleStr = (showLabels ? tr("Angle: ") : "< ") + formatAngleRaw(ucsBasisAngle);
1660
1661 coordPolar = lenStr + (prefs->multiLine ? "\n" : showLabels ? " " : " ") + angleStr;
1662 }
1663 if (prefs->showRelativePositionDeltas) {
1664 const QString lenStr = (showLabels ? tr("dX: ") : "@ ") + formatLinear(relativeToUse.x);
1665 const QString angleStr = (showLabels ? tr("dY: ") : "") + formatLinear(relativeToUse.y);
1666
1667 const QString coordDeltas = lenStr + (prefs->multiLine ? "\n" : showLabels ? " " : " , ") + angleStr;
1668 if (coordPolar.isEmpty()) {
1669 coordPolar = coordDeltas;
1670 }
1671 else {
1672 coordPolar = coordPolar + "\n" + coordDeltas;
1673 }
1674 }
1675 }
1676 }
1677
1678 m_infoCursorOverlayData->setZone1(coordAbs);
1679 m_infoCursorOverlayData->setZone3(coordPolar);
1680}
1681
1682void RS_Snapper::invalidateSnapSpot() const {
1683 m_impData->snapSpot.valid = false;
1684}
1685
1686QString RS_Snapper::formatLinear(const double value) const {
1687 return m_formatter->formatLinear(value);
1688}
1689
1690QString RS_Snapper::formatWCSAngle(const double wcsAngle) const {
1691 return m_formatter->formatWCSAngle(wcsAngle);
1692}
1693
1694QString RS_Snapper::formatAngleRaw(const double angle) const {
1695 return m_formatter->formatRawAngle(angle);
1696}
1697
1698// fixme - ucs- move to coordinate mapper?
1699QString RS_Snapper::formatVector(const RS_Vector& value) const {
1700 double x, y;
1701 if (m_viewport->hasUCS()) {
1702 const RS_Vector ucsValue = m_viewport->toUCS(value);
1703 x = ucsValue.x;
1704 y = ucsValue.y;
1705 }
1706 else {
1707 x = value.x;
1708 y = value.y;
1709 }
1710 return formatLinear(x).append(" , ").append(formatLinear(y));
1711}
1712
1713QString RS_Snapper::formatVectorWCS(const RS_Vector& value) const {
1714 return QString("W ").append(formatLinear(value.x)).append(" , ").append(formatLinear(value.y));
1715}
1716
1717QString RS_Snapper::formatRelative(const RS_Vector& value) const {
1718 double x, y;
1719 m_viewport->toUCSDelta(value, x, y);
1720 return QString("@ ").append(formatLinear(x)).append(" , ").append(formatLinear(y));
1721}
1722
1723QString RS_Snapper::formatPolar(const RS_Vector& value) const {
1724 return formatLinear(value.magnitude()).append(" < ").append(formatWCSAngle(value.angle()));
1725}
1726
1727QString RS_Snapper::formatRelativePolar(const RS_Vector& wcsAngle) const {
1728 return QString("@ ").append(formatLinear(wcsAngle.magnitude())).append(" < ").append(formatWCSAngle(wcsAngle.angle()));
1729}
1730
1731void RS_Snapper::forceUpdateInfoCursor(const RS_Vector& pos) const {
1732 LC_OverlayInfoCursor* infoCursor = obtainInfoCursor();
1733 if (infoCursor != nullptr) {
1734 infoCursor->setPos(pos);
1735 infoCursor->setZonesData(m_infoCursorOverlayData.get());
1736 }
1737}
1738
1739double RS_Snapper::toWorldAngle(const double ucsAbsAngle) const {
1740 return m_viewport->toWorldAngle(ucsAbsAngle);
1741}
1742
1743double RS_Snapper::toWorldAngleDegrees(const double ucsAbsAngleDegrees) const {
1744 return m_viewport->toWorldAngleDegrees(ucsAbsAngleDegrees);
1745}
1746
1747double RS_Snapper::toUCSAngle(const double wcsAngle) const {
1748 return m_viewport->toUCSAngle(wcsAngle);
1749}
1750
1751double RS_Snapper::ucsAbsToBasisAngle(const double ucsAbsAngle) const {
1752 return m_viewport->toBasisUCSAngle(ucsAbsAngle);
1753}
1754
1755double RS_Snapper::ucsBasisToAbsAngle(const double ucsRelAngle) const {
1756 return m_viewport->toAbsUCSAngle(ucsRelAngle);
1757}
1758
1759double RS_Snapper::adjustRelativeAngleSignByBasis(const double relativeAngle) const {
1760 return m_formatter->adjustRelativeAngleSignByBasis(relativeAngle);
1761}
1762
1763double RS_Snapper::toUCSBasisAngleDegrees(const double wcsAngle) const {
1764 return m_formatter->toUCSBasisAngleDegrees(wcsAngle);
1765}
1766
1767double RS_Snapper::toWorldAngleFromUCSBasisDegrees(const double ucsBasisAngleDegrees) const {
1768 return m_formatter->toWorldAngleFromUCSBasisDegrees(ucsBasisAngleDegrees);
1769}
1770
1771double RS_Snapper::toWorldAngleFromUCSBasis(const double ucsBasisAngle) const {
1772 return m_formatter->toWorldAngleFromUCSBasis(ucsBasisAngle);
1773}
1774
1775double RS_Snapper::toUCSBasisAngle(const double wcsAngle) const {
1776 return m_formatter->toUCSBasisAngle(wcsAngle);
1777}
1778
1779RS_Vector RS_Snapper::toWorld(const RS_Vector& ucsPos) const {
1780 return m_viewport->toWorld(ucsPos);
1781}
1782
1783RS_Vector RS_Snapper::toUCS(const RS_Vector& worldPos) const {
1784 return m_viewport->toUCS(worldPos);
1785}
1786
1787RS_Vector RS_Snapper::toWorldDelta(const RS_Vector& ucsDelta) const {
1788 return m_viewport->toWorldDelta(ucsDelta);
1789}
1790
1791RS_Vector RS_Snapper::toUCSDelta(const RS_Vector& worldDelta) const {
1792 return m_viewport->toUCSDelta(worldDelta);
1793}
1794
1795// todo - sand - ucs - move to coordinates mapper?
1796void RS_Snapper::calcRectCorners(const RS_Vector& worldCorner1, const RS_Vector& worldCorner3, RS_Vector& worldCorner2,
1797 RS_Vector& worldCorner4) const {
1798 const RS_Vector ucsCorner1 = toUCS(worldCorner1);
1799 const RS_Vector ucsCorner3 = toUCS(worldCorner3);
1800 const auto ucsCorner2 = RS_Vector(ucsCorner1.x, ucsCorner3.y);
1801 const auto ucsCorner4 = RS_Vector(ucsCorner3.x, ucsCorner1.y);
1802 worldCorner2 = toWorld(ucsCorner2);
1803 worldCorner4 = toWorld(ucsCorner4);
1804}
1805
1806bool RS_Snapper::hasNonDefaultAnglesBasis() const {
1807 return m_formatter->hasNonDefaultAnglesBasis();
1808}
1809
1810void RS_Snapper::clearVisualSnap() const {
1811 m_visualSnapManager->clear();
1812}
1813
1814void RS_Snapper::stopVisualSnap() const {
1815 clearVisualSnap();
1816}
1817
1818void RS_Snapper::removePreviousVisualSnapAddition() {
1819 m_visualSnapManager->removeLastAddition();
1820 onVisualSnapSolutionRefresh();
1821}
1822
1823bool RS_Snapper::hasVisualSnap(bool ignoreLastSnapData) const {
1824 return m_visualSnapManager->hasVisualSnap(ignoreLastSnapData);
1825}
1826
1827// get catching entity distance in graph distance
1828double RS_Snapper::getCatchDistance(const double catchDistance, const int catchEntityGuiRange) const {
1829 return (m_graphicView != nullptr) ? std::min(catchDistance, toGraphDX(catchEntityGuiRange)) : catchDistance;
1830}
1831
1832// fixme - sand - review
1833void RS_Snapper::enableCoordinateInput() const {
1834 m_graphicView->enableCoordinateInput();
1835}
1836
1837void RS_Snapper::disableCoordinateInput() const {
1838 m_graphicView->disableCoordinateInput();
1839}
1840
1841void RS_Snapper::redraw(const RS2::RedrawMethod method) const {
1842 // fixme - sand - ucs - decide how it's better to invoke redraw
1843 // viewport->requestRedraw(method);
1844 m_graphicView->redraw(method);
1845}
1846
1847void RS_Snapper::redrawImmediately(const RS2::RedrawMethod method) const {
1848 m_graphicView->redraw(method, true);
1849}
1850
1851void RS_Snapper::redrawDrawing() const {
1852 redraw(RS2::RedrawMethod::RedrawDrawing);
1853}
1854
1855void RS_Snapper::redrawAll() const {
1856 redraw(RS2::RedrawMethod::RedrawAll);
1857}
1858
1859// fixme **********************************************************************
1860// fixme - snap angle and grid enabled!!!!
1861// fixme **********************************************************************
1862
1863// Fixme
1864// options for visual snap
1865// 1) which rays should be created
1866// 2) creating not only normal/tangental, but also interval rays (as in angle snap?)