Bug Summary

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