Bug Summary

File:librecad/src/lib/engine/document/entities/lc_splinepoints.cpp
Warning:line 2071, column 30
The right operand of '-' is a garbage value

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 lc_splinepoints.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-670-g0ffb38d79 -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 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-09-15-234850-5000-1 -x c++ lib/engine/document/entities/lc_splinepoints.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) 2014 Dongxu Li (dongxuli2011@gmail.com)
7** Copyright (C) 2014 Pevel Krejcir (pavel@pamsoft.cz)
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22**********************************************************************/
23
24#include "lc_splinepoints.h"
25
26#include <QPainterPath>
27
28#include "lc_quadratic.h"
29#include "rs_circle.h"
30#include "rs_information.h"
31#include "rs_line.h"
32#include "rs_math.h"
33#include "rs_painter.h"
34
35namespace {
36 RS_Vector getQuadPoint(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double dt) {
37 return x1 * (1.0 - dt) * (1.0 - dt) + c1 * 2.0 * dt * (1.0 - dt) + x2 * dt * dt;
38 }
39
40 void strokeQuad(std::vector<RS_Vector>* plist, const RS_Vector& vx1, const RS_Vector& vc1, const RS_Vector& vx2, const int iSeg) {
41 if (iSeg < 1) {
42 plist->push_back(vx1);
43 return;
44 }
45
46 for (int i = 0; i < iSeg; i++) {
47 const double doubleI = i;
48 const double dt = doubleI / iSeg;
49 RS_Vector x = getQuadPoint(vx1, vc1, vx2, dt);
50 plist->push_back(x);
51 }
52 }
53
54 RS_Vector getQuadDir(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double dt) {
55 const RS_Vector vStart = c1 - x1;
56 const RS_Vector vEnd = x2 - c1;
57 RS_Vector vRes(false);
58
59 double dDist = (vEnd - vStart).squared();
60 if (dDist > RS_TOLERANCE21.0e-20) {
61 vRes = vStart * (1.0 - dt) + vEnd * dt;
62 dDist = vRes.magnitude();
63 if (dDist < RS_TOLERANCE1.0e-10) {
64 return RS_Vector(false);
65 }
66
67 return vRes / dDist;
68 }
69
70 dDist = (x2 - x1).magnitude();
71 if (dDist > RS_TOLERANCE1.0e-10) {
72 return (x2 - x1) / dDist;
73 }
74
75 return vRes;
76 }
77
78 RS_Vector getSubQuadControlPoint(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double dt1, const double dt2) {
79 return x1 * (1.0 - dt1) * (1.0 - dt2) + c1 * dt1 * (1.0 - dt2) + c1 * dt2 * (1.0 - dt1) + x2 * dt1 * dt2;
80 }
81
82 double lenInt(const double x) {
83 // Issue #1610 : when x is negative and much smaller than -1E5, sqrt(1+x*x) is very close to -x, so
84 // brute force evaluation of x+y loses significant digits quickly when the absolute value of a negative
85 // x gets larger. when x+y is evaluated to be negative, log(x+y) will become meaningless.
86 // The solution, for negative x, when x+y may lose significant digits, evaluating log(x+y) as
87 // log( y + x ) = log ((y^2 - x^2)/(y - x)) = - log(y - x)
88 const double y = std::sqrt(1 + x * x);
89 if (std::signbit(x)) {
90 return x * y - std::log(y - x);
91 }
92 return x * y + std::log(y + x);
93 }
94
95 double getQuadLength(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double t1, const double t2) {
96 const RS_Vector xh1 = (c1 - x1) * 2.0;
97 const RS_Vector xh2 = (x2 - c1) * 2.0;
98 const RS_Vector xd1 = xh2 - xh1;
99 const RS_Vector xd2 = xh1;
100
101 const double dx1 = xd1.squared();
102 double dx2 = xd2.squared();
103 const double dx12 = xd1.x * xd2.x + xd1.y * xd2.y;
104 const double dDet = dx1 * dx2 - dx12 * dx12; // always >= 0 from Schwarz inequality
105
106 double dRes = 0.0;
107
108 if (dDet > RS_TOLERANCE1.0e-10) {
109 const double dA = std::sqrt(dDet);
110 const double v1 = (dx1 * t1 + dx12) / dA;
111 const double v2 = (dx1 * t2 + dx12) / dA;
112 dRes = (lenInt(v2) - lenInt(v1)) * dDet / 2.0 / dx1 / std::sqrt(dx1);
113 }
114 else {
115 if (dx1 < RS_TOLERANCE1.0e-10) {
116 dRes = std::sqrt(dx2) * (t2 - t1);
117 }
118 else {
119 dx2 = std::sqrt(dx1);
120 dRes = (t2 - t1) * (dx2 * (t2 + t1) / 2.0 + dx12 / dx2);
121 }
122 }
123
124 return dRes;
125 }
126
127 double getQuadLengthDeriv(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double t2) {
128 const RS_Vector xh1 = (c1 - x1) * 2.0;
129 const RS_Vector xh2 = (x2 - c1) * 2.0;
130 const RS_Vector xd1 = xh2 - xh1;
131 const RS_Vector xd2 = xh1;
132
133 const double dx1 = xd1.squared();
134 double dx2 = xd2.squared();
135 const double dx12 = xd1.x * xd2.x + xd1.y * xd2.y;
136 const double dDet = dx1 * dx2 - dx12 * dx12; // always >= 0 from Schwarz inequality
137
138 double dRes = 0.0;
139
140 if (dDet > RS_TOLERANCE1.0e-10) {
141 const double dA = std::sqrt(dDet);
142 const double v2 = (dx1 * t2 + dx12) / dA;
143 const double v3 = v2 * v2;
144 const double v4 = 1.0 + v3;
145 const double v5 = std::sqrt(v4);
146 dRes = ((v2 + v5) / (v4 + v2 * v5) + (2.0 * v3 + 1.0) / v5) * dA / 2.0 / std::sqrt(dx1);
147 }
148 else {
149 if (dx1 < RS_TOLERANCE1.0e-10) {
150 dRes = std::sqrt(dx2);
151 }
152 else {
153 dx2 = std::sqrt(dx1);
154 dRes = dx2 * t2 + dx12 / dx2;
155 }
156 }
157
158 return dRes;
159 }
160
161 double getQuadPointAtDist(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double t1, const double dDist) {
162 const RS_Vector xh1 = (c1 - x1) * 2.0;
163 const RS_Vector xh2 = (x2 - c1) * 2.0;
164 const RS_Vector xd1 = xh2 - xh1;
165 const RS_Vector xd2 = xh1;
166
167 const double dx1 = xd1.squared();
168 double dx2 = xd2.squared();
169 const double dx12 = xd1.x * xd2.x + xd1.y * xd2.y;
170 const double dDet = dx1 * dx2 - dx12 * dx12; // always >= 0 from Schwarz inequality
171
172 double dRes = RS_MAXDOUBLE1.0E+10;
173
174 std::vector<double> dCoefs(0, 0.);
175 std::vector<double> dSol(0, 0.);
176
177 if (dDet > RS_TOLERANCE1.0e-10) {
178 const double dA = std::sqrt(dDet);
179 const double v1 = (dx1 * t1 + dx12) / dA;
180 //double v2 = (dx1*t2 + dx12)/dA;
181 //dDist = (LenInt(v2) - LenInt(v1))*dDet/2.0/dx1/std::sqrt(dx1);
182 //LenInt(v2) = 2.0*dx1*std::sqrt(dx1)*dDist/dDet + LenInt(v1);
183 const double dB = 2.0 * dx1 * std::sqrt(dx1) * dDist / dDet + lenInt(v1);
184
185 dCoefs.push_back(0.0);
186 dCoefs.push_back(0.0);
187 dCoefs.push_back(2.0 * dB);
188 dCoefs.push_back(-dB * dB);
189 dSol = RS_Math::quarticSolver(dCoefs);
190
191 dRes = t1;
192 double a1 = 0;
193 for (const double& d : dSol) {
194 const double a0 = (d * dA - dx12) / dx1;
195 const double a2 = getQuadLength(x1, c1, x2, t1, a0);
196 if (std::abs(dDist - a2) < std::abs(dDist - a1)) {
197 a1 = a2;
198 dRes = a0;
199 }
200 }
201
202 // we believe we are pretty close to the solution at the moment
203 // so we only perform three iterations
204 for (int i = 0; i < 3; i++) {
205 a1 = getQuadLength(x1, c1, x2, t1, dRes) - dDist;
206 const double a2 = getQuadLengthDeriv(x1, c1, x2, dRes);
207 if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
208 dRes -= a1 / a2;
209 }
210 }
211 }
212 else {
213 if (dx1 < RS_TOLERANCE1.0e-10) {
214 if (dx2 > RS_TOLERANCE1.0e-10) {
215 dRes = t1 + dDist / std::sqrt(dx2);
216 }
217 }
218 else {
219 dx2 = std::sqrt(dx1);
220 //dRes = (t2 - t1)*(dx2*(t2 + t1)/2.0 + dx12/dx2);
221
222 const double a0 = dx2 / 2.0;
223 double a1 = dx12 / dx2;
224 double a2 = -dDist - a0 * t1 * t1 - a1 * t1;
225
226 dCoefs.push_back(a1 / a0);
227 dCoefs.push_back(a2 / a0);
228 dSol = RS_Math::quadraticSolver(dCoefs);
229
230 if (dSol.size() > 0) {
231 dRes = dSol[0];
232 if (dSol.size() > 1) {
233 a1 = getQuadLength(x1, c1, x2, t1, dRes);
234 a2 = getQuadLength(x1, c1, x2, t1, dSol[1]);
235 if (std::abs(dDist - a2) < std::abs(dDist - a1)) {
236 dRes = dSol[1];
237 }
238 }
239 }
240 }
241 }
242
243 return dRes;
244 }
245
246 RS_Vector getThreePointsControl(const RS_Vector& x1, const RS_Vector& x2, const RS_Vector& x3) {
247 const double dl1 = (x2 - x1).magnitude();
248 const double dl2 = (x3 - x2).magnitude();
249 const double dt = dl1 / (dl1 + dl2);
250
251 if (dt < RS_TOLERANCE1.0e-10 || dt > 1.0 - RS_TOLERANCE1.0e-10) {
252 return RS_Vector{false};
253 }
254
255 const RS_Vector vRes = (x2 - x1 * (1.0 - dt) * (1.0 - dt) - x3 * dt * dt) / dt / (1 - dt) / 2.0;
256 return vRes;
257 }
258
259 double getDistToLine(const RS_Vector& coord, const RS_Vector& x1, const RS_Vector& x2, double* dist) {
260 const double ddet = (x2 - x1).squared();
261 if (ddet < RS_TOLERANCE1.0e-10) {
262 *dist = (coord - x1).magnitude();
263 return 0.0;
264 }
265
266 const double dt = ((coord.x - x1.x) * (x2.x - x1.x) + (coord.y - x1.y) * (x2.y - x1.y)) / ddet;
267
268 if (dt < RS_TOLERANCE1.0e-10) {
269 *dist = (coord - x1).magnitude();
270 return 0.0;
271 }
272
273 if (dt > 1.0 - RS_TOLERANCE1.0e-10) {
274 *dist = (coord - x2).magnitude();
275 return 1.0;
276 }
277
278 const RS_Vector vRes = x1 * (1.0 - dt) + x2 * dt;
279 *dist = (coord - vRes).magnitude();
280 return dt;
281 }
282
283 RS_Vector getQuadAtPoint(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double dt) {
284 const RS_Vector vRes = x1 * (1.0 - dt) * (1.0 - dt) + c1 * 2.0 * dt * (1.0 - dt) + x2 * dt * dt;
285 return vRes;
286 }
287
288 RS_Vector getQuadDirAtPoint(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, const double dt) {
289 const RS_Vector vRes = (c1 - x1) * (1.0 - dt) + (x2 - c1) * dt;
290 return vRes;
291 }
292
293 double getDistToQuadAtPointSquared(const RS_Vector& coord, const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2,
294 const double dt) {
295 if (dt < RS_TOLERANCE1.0e-10) {
296 return (coord - x1).squared();
297 }
298
299 if (dt > 1.0 - RS_TOLERANCE1.0e-10) {
300 return (coord - x2).squared();
301 }
302
303 const RS_Vector vx = getQuadAtPoint(x1, c1, x2, dt);
304 return (coord - vx).squared();
305 }
306
307 // returns true if the new distance was smaller than previous one
308 bool setNewDist(const bool bResSet, const double dNewDist, const double dNewT, double* pdDist, double* pdt) {
309 bool bRes = false;
310 if (bResSet) {
311 if (dNewDist < *pdDist) {
312 *pdDist = dNewDist;
313 *pdt = dNewT;
314 bRes = true;
315 }
316 }
317 else {
318 *pdDist = dNewDist;
319 *pdt = dNewT;
320 bRes = true;
321 }
322 return bRes;
323 }
324
325 double getDistToQuadSquared(const RS_Vector& coord, const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2, double* dist) {
326 const double a1 = (x2.x - 2.0 * c1.x + x1.x) * (x2.x - 2.0 * c1.x + x1.x) + (x2.y - 2.0 * c1.y + x1.y) * (x2.y - 2.0 * c1.y + x1.y);
327 const double a2 = 3.0 * ((c1.x - x1.x) * (x2.x - 2.0 * c1.x + x1.x) + (c1.y - x1.y) * (x2.y - 2.0 * c1.y + x1.y));
328 const double a3 = 2.0 * (c1.x - x1.x) * (c1.x - x1.x) + (x1.x - coord.x) * (x2.x - 2.0 * c1.x + x1.x) + 2.0 * (c1.y - x1.y) * (c1.y
329 - x1.y) + (x1.y - coord.y) * (x2.y - 2.0 * c1.y + x1.y);
330 const double a4 = (x1.x - coord.x) * (c1.x - x1.x) + (x1.y - coord.y) * (c1.y - x1.y);
331
332 std::vector<double> dCoefs(0, 0.);
333 std::vector<double> dSol(0, 0.);
334
335 if (std::abs(a1) > RS_TOLERANCE1.0e-10) // solve as cubic
336 {
337 dCoefs.push_back(a2 / a1);
338 dCoefs.push_back(a3 / a1);
339 dCoefs.push_back(a4 / a1);
340 dSol = RS_Math::cubicSolver(dCoefs);
341 }
342 else if (std::abs(a2) > RS_TOLERANCE1.0e-10) // solve as quadratic
343 {
344 dCoefs.push_back(a3 / a2);
345 dCoefs.push_back(a4 / a2);
346 dSol = RS_Math::quadraticSolver(dCoefs);
347 }
348 else if (std::abs(a3) > RS_TOLERANCE1.0e-10) // solve as linear
349 {
350 dSol.push_back(-a4 / a3);
351 }
352 else {
353 return -1.0;
354 }
355
356 bool bResSet = false;
357 double dDist = 0., dNewDist;
358 double dRes = 0.;
359 for (const double& dSolValue : dSol) {
360 dNewDist = getDistToQuadAtPointSquared(coord, x1, c1, x2, dSolValue);
361 setNewDist(bResSet, dNewDist, dSolValue, &dDist, &dRes);
362 bResSet = true;
363 }
364
365 dNewDist = (coord - x1).squared();
366 setNewDist(bResSet, dNewDist, 0.0, &dDist, &dRes);
367
368 dNewDist = (coord - x2).squared();
369 setNewDist(bResSet, dNewDist, 1.0, &dDist, &dRes);
370
371 *dist = dDist;
372
373 return dRes;
374 }
375
376 RS_Vector getNearestMiddleLine(const RS_Vector& x1, const RS_Vector& x2, const RS_Vector& coord, double* dist, const int middlePoints) {
377 double dt = 1.0 / (1.0 + middlePoints);
378 RS_Vector vRes = x1 * (1.0 - dt) + x2 * dt;
379 double dMinDist = (vRes - coord).magnitude();
380 double dCurDist = 0.;
381
382 for (int i = 1; i < middlePoints; i++) {
383 dt = (1.0 + i) / (1.0 + middlePoints);
384 RS_Vector vMiddle = x1 * (1.0 - dt) + x2 * dt;
385 dCurDist = (vMiddle - coord).magnitude();
386
387 if (dCurDist < dMinDist) {
388 dMinDist = dCurDist;
389 vRes = vMiddle;
390 }
391 }
392
393 if (dist != nullptr) {
394 *dist = dMinDist;
395 }
396 return vRes;
397 }
398
399 void addQuadTangentPoints(RS_VectorSolutions* pVS, const RS_Vector& point, const RS_Vector& x1, const RS_Vector& c1,
400 const RS_Vector& x2) {
401 const RS_Vector vx1 = x2 - c1 * 2.0 + x1;
402 const RS_Vector vx2 = c1 - x1;
403 const RS_Vector vx3 = x1 - point;
404
405 const double a1 = vx2.x * vx1.y - vx2.y * vx1.x;
406 const double a2 = vx3.x * vx1.y - vx3.y * vx1.x;
407 const double a3 = vx3.x * vx2.y - vx3.y * vx2.x;
408
409 std::vector<double> dSol(0, 0.);
410
411 if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
412 std::vector<double> dCoefs(0, 0.);
413
414 dCoefs.push_back(a2 / a1);
415 dCoefs.push_back(a3 / a1);
416 dSol = RS_Math::quadraticSolver(dCoefs);
417 }
418 else if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
419 dSol.push_back(-a3 / a2);
420 }
421
422 for (double& d : dSol) {
423 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
424 if (d < 0.0) {
425 d = 0.0;
426 }
427 if (d > 1.0) {
428 d = 1.0;
429 }
430 pVS->push_back(getQuadPoint(x1, c1, x2, d));
431 }
432 }
433 }
434
435 void addLineQuadIntersect(RS_VectorSolutions* pVS, const RS_Vector& vStart, const RS_Vector& vEnd, const RS_Vector& vx1,
436 const RS_Vector& vc1, const RS_Vector& vx2) {
437 RS_Vector x1 = vx2 - vc1 * 2.0 + vx1;
438 const RS_Vector x2 = vc1 - vx1;
439 const RS_Vector x3 = vx1 - vStart;
440 const RS_Vector x4 = vEnd - vStart;
441
442 const double a1 = x1.x * x4.y - x1.y * x4.x;
443 const double a2 = 2.0 * (x2.x * x4.y - x2.y * x4.x);
444 const double a3 = x3.x * x4.y - x3.y * x4.x;
445
446 std::vector<double> dSol(0, 0.);
447
448 if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
449 std::vector<double> dCoefs(0, 0.);
450
451 dCoefs.push_back(a2 / a1);
452 dCoefs.push_back(a3 / a1);
453 dSol = RS_Math::quadraticSolver(dCoefs);
454 }
455 else if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
456 dSol.push_back(-a3 / a2);
457 }
458
459 double ds = 0.;
460
461 for (double& d : dSol) {
462 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
463 if (d < 0.0) {
464 d = 0.0;
465 }
466 if (d > 1.0) {
467 d = 1.0;
468 }
469
470 ds = -1.0;
471 x1 = getQuadAtPoint(vx1, vc1, vx2, d);
472 if (std::abs(x4.x) > RS_TOLERANCE1.0e-10) {
473 ds = (x1.x - vStart.x) / x4.x;
474 }
475 else if (std::abs(x4.y) > RS_TOLERANCE1.0e-10) {
476 ds = (x1.y - vStart.y) / x4.y;
477 }
478
479 if (ds > -RS_TOLERANCE1.0e-10 && ds < 1.0 + RS_TOLERANCE1.0e-10) {
480 pVS->push_back(x1);
481 }
482 }
483 }
484 }
485}
486
487LC_SplinePointsData::LC_SplinePointsData(const bool closed, const bool cut) : closed{closed}, cut{cut} {
488}
489
490std::ostream& operator <<(std::ostream& os, const LC_SplinePointsData& ld) {
491 os << "( closed: " << ld.closed << ")";
492 return os;
493}
494
495// RS_SplinePoints
496
497/**
498 * Constructor.
499 */
500LC_SplinePoints::LC_SplinePoints(RS_EntityContainer* parent, LC_SplinePointsData d) : LC_CachedLengthEntity(parent), m_data(std::move(d)) {
501 if (!m_data.useControlPoints) {
502 updateControlPointsUI();
503 }
504
505 LC_SplinePoints::calculateBorders();
506}
507
508RS_Entity* LC_SplinePoints::clone() const {
509 auto* l = new LC_SplinePoints(*this);
510 return l;
511}
512
513void LC_SplinePoints::update() {
514 updateControlPointsUI();
515 calculateBorders();
516}
517
518void LC_SplinePoints::updateQuadExtentUI(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) {
519 RS_Vector locMinV = RS_Vector::minimum(x1, x2);
520 RS_Vector locMaxV = RS_Vector::maximum(x1, x2);
521
522 const RS_Vector vDer = x2 - c1 * 2.0 + x1;
523
524 if (std::abs(vDer.x) > RS_TOLERANCE1.0e-10) {
525 const double dt = (x1.x - c1.x) / vDer.x;
526 if (dt > RS_TOLERANCE1.0e-10 && dt < 1.0 - RS_TOLERANCE1.0e-10) {
527 const double dx = x1.x * (1.0 - dt) * (1.0 - dt) + 2.0 * c1.x * dt * (1.0 - dt) + x2.x * dt * dt;
528 locMinV.x = std::min(locMinV.x, dx);
529 locMaxV.x = std::max(locMaxV.x, dx);
530 }
531 }
532
533 if (std::abs(vDer.y) > RS_TOLERANCE1.0e-10) {
534 const double dt = (x1.y - c1.y) / vDer.y;
535 if (dt > RS_TOLERANCE1.0e-10 && dt < 1.0 - RS_TOLERANCE1.0e-10) {
536 const double dy = x1.y * (1.0 - dt) * (1.0 - dt) + 2.0 * c1.y * dt * (1.0 - dt) + x2.y * dt * dt;
537 locMinV.y = std::min(locMinV.y, dy);
538 locMaxV.y = std::max(locMaxV.y, dy);
539 }
540 }
541
542 m_minV = RS_Vector::minimum(locMinV, m_minV);
543 m_maxV = RS_Vector::maximum(locMaxV, m_maxV);
544}
545
546void LC_SplinePoints::calculateBorders() {
547 // the length does not depend on the borders; computed last, it was skipped by the early returns below
548 updateLength();
549 m_minV = RS_Vector(false);
550 m_maxV = RS_Vector(false);
551
552 const size_t n = m_data.controlPoints.size();
553 if (n < 1) {
554 return;
555 }
556
557 RS_Vector vStart(false);
558 RS_Vector vControl(false);
559 RS_Vector vEnd(false);
560
561 if (m_data.closed) {
562 if (n < 3) {
563 return;
564 }
565
566 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
567 vControl = m_data.controlPoints.at(0);
568 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
569 updateQuadExtentUI(vStart, vControl, vEnd);
570
571 for (size_t i = 1; i < n - 1; ++i) {
572 vStart = vEnd;
573 vControl = m_data.controlPoints.at(i);
574 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
575 updateQuadExtentUI(vStart, vControl, vEnd);
576 }
577
578 vStart = vEnd;
579 vControl = m_data.controlPoints.at(n - 1);
580 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
581 updateQuadExtentUI(vStart, vControl, vEnd);
582 }
583 else {
584 vStart = m_data.controlPoints.at(0);
585 m_minV = vStart;
586 m_maxV = vStart;
587
588 if (n < 2) {
589 return;
590 }
591
592 vEnd = m_data.controlPoints.at(1);
593
594 if (n < 3) {
595 m_minV = RS_Vector::minimum(vEnd, m_minV);
596 m_maxV = RS_Vector::maximum(vEnd, m_maxV);
597 return;
598 }
599
600 vControl = vEnd;
601 vEnd = m_data.controlPoints.at(2);
602
603 if (n < 4) {
604 updateQuadExtentUI(vStart, vControl, vEnd);
605 return;
606 }
607
608 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
609 updateQuadExtentUI(vStart, vControl, vEnd);
610
611 for (size_t i = 2; i < n - 2; ++i) {
612 vStart = vEnd;
613 vControl = m_data.controlPoints.at(i);
614 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
615 updateQuadExtentUI(vStart, vControl, vEnd);
616 }
617
618 vStart = vEnd;
619 vControl = m_data.controlPoints.at(n - 2);
620 vEnd = m_data.controlPoints.at(n - 1);
621 updateQuadExtentUI(vStart, vControl, vEnd);
622 }
623}
624
625RS_VectorSolutions LC_SplinePoints::getRefPoints() const {
626 if (m_data.cut) {
627 return {{m_data.controlPoints.begin(), m_data.controlPoints.end()}};
628 }
629 return {{m_data.splinePoints.begin(), m_data.splinePoints.end()}};
630}
631
632/** @return Start point of the entity */
633RS_Vector LC_SplinePoints::getStartpoint() const {
634 if (m_data.closed) {
635 return RS_Vector(false);
636 }
637
638 const std::vector<RS_Vector>& pts = getPoints();
639 const size_t iCount = pts.size();
640 if (iCount < 1) {
641 return RS_Vector(false);
642 }
643 return pts.at(0);
644}
645
646/** @return End point of the entity */
647RS_Vector LC_SplinePoints::getEndpoint() const {
648 if (m_data.closed) {
649 return RS_Vector(false);
650 }
651
652 const std::vector<RS_Vector>& pts = getPoints();
653 const size_t iCount = pts.size();
654
655 return (iCount < 1) ? RS_Vector{false} : pts.at(iCount - 1);
656}
657
658RS_Vector LC_SplinePoints::doGetNearestEndpoint(const RS_Vector& coord, double* dist, RS_Entity** entity) const {
659 double minDist = RS_MAXDOUBLE1.0E+10;
660 RS_Vector ret(false);
661 if (!m_data.closed) // no endpoint for closed spline
662 {
663 const RS_Vector vp1(getStartpoint());
664 const RS_Vector vp2(getEndpoint());
665 const double d1 = (coord - vp1).squared();
666 const double d2 = (coord - vp2).squared();
667 if (d1 < d2) {
668 ret = vp1;
669 minDist = std::sqrt(d1);
670 }
671 else {
672 ret = vp2;
673 minDist = std::sqrt(d2);
674 }
675 }
676 if (dist != nullptr) {
677 *dist = minDist;
678 }
679 if (entity != nullptr) {
680 *entity = const_cast<LC_SplinePoints*>(this);
681 }
682 return ret;
683}
684
685// returns true if pvControl is set
686int LC_SplinePoints::getQuadPoints(const int iSeg, RS_Vector* pvStart, RS_Vector* pvControl, RS_Vector* pvEnd) const {
687 const size_t n = m_data.controlPoints.size();
688
689 size_t i1 = iSeg - 1;
690 size_t i2 = iSeg;
691 size_t i3 = iSeg + 1;
692
693 if (m_data.closed) {
694 if (n < 3) {
695 return 0;
696 }
697
698 i1 = (i1 + n - 1) % n;
699 i2--;
700 i3 = (i3 + n - 1) % n;
701
702 *pvStart = (m_data.controlPoints.at(i1) + m_data.controlPoints.at(i2)) / 2.0;
703 *pvControl = m_data.controlPoints.at(i2);
704 *pvEnd = (m_data.controlPoints.at(i2) + m_data.controlPoints.at(i3)) / 2.0;
705 }
706 else {
707 if (iSeg < 1) {
708 return 0;
709 }
710 if (n < 1) {
711 return 0;
712 }
713
714 *pvStart = m_data.controlPoints.at(0);
715
716 if (n < 2) {
717 return 1;
718 }
719
720 *pvEnd = m_data.controlPoints.at(1);
721
722 if (n < 3) {
723 return 2;
724 }
725
726 *pvControl = *pvEnd;
727 *pvEnd = m_data.controlPoints.at(2);
728
729 if (n < 4) {
730 return 3;
731 }
732
733 if (i1 < 1) {
734 *pvStart = m_data.controlPoints.at(0);
735 }
736 else {
737 *pvStart = (m_data.controlPoints.at(i1) + m_data.controlPoints.at(i2)) / 2.0;
738 }
739 *pvControl = m_data.controlPoints.at(i2);
740 if (i3 > n - 2) {
741 *pvEnd = m_data.controlPoints.at(n - 1);
742 }
743 else {
744 *pvEnd = (m_data.controlPoints.at(i2) + m_data.controlPoints.at(i3)) / 2.0;
745 }
746 }
747
748 return 3;
749}
750
751// returns the index to the nearest segment, dt holds the t parameter
752// we will make an extrodrinary exception here and make the index 1-based
753// return values:
754// -1: no segment found
755// 0: segment is one point only
756// >0: index to then non-degenerated segment, depends on closed flag
757int LC_SplinePoints::getNearestQuad(const RS_Vector& coord, double* dist, double* dt) const {
758 size_t n = m_data.controlPoints.size();
759
760 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
761
762 double dDist = 0., dNewDist = 0.;
763 double dRes, dNewRes;
764 int iRes = -1;
765
766 if (m_data.closed) {
10
Assuming field 'closed' is false
11
Taking false branch
767 if (n < 3) {
768 return -1;
769 }
770
771 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
772 vControl = m_data.controlPoints.at(0);
773 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
774
775 dRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
776 iRes = 1;
777
778 for (size_t i = 1; i < n - 1; ++i) {
779 vStart = vEnd;
780 vControl = m_data.controlPoints.at(i);
781 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
782
783 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
784 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
785 iRes = i + 1;
786 }
787 }
788
789 vStart = vEnd;
790 vControl = m_data.controlPoints.at(n - 1);
791 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
792
793 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
794 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
795 iRes = n;
796 }
797 }
798 else {
799 if (n < 1) {
12
Assuming 'n' is >= 1
13
Taking false branch
800 return -1;
801 }
802
803 vStart = m_data.controlPoints.at(0);
804
805 if (n < 2) {
14
Assuming 'n' is < 2
15
Taking true branch
806 if (dist != nullptr) {
16
Taking false branch
807 *dist = (coord - vStart).magnitude();
808 }
809 return 0;
17
Returning without writing to '*dt'
810 }
811
812 vEnd = m_data.controlPoints.at(1);
813
814 if (n < 3) {
815 *dt = getDistToLine(coord, vStart, vEnd, &dDist);
816 if (dist != nullptr) {
817 *dist = std::sqrt(dDist);
818 }
819 return 1;
820 }
821
822 vControl = vEnd;
823 vEnd = m_data.controlPoints.at(2);
824
825 if (n < 4) {
826 *dt = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
827 if (dist != nullptr) {
828 *dist = std::sqrt(dDist);
829 }
830 return 1;
831 }
832
833 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
834
835 dRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
836 iRes = 1;
837
838 for (size_t i = 2; i < n - 2; i++) {
839 vStart = vEnd;
840 vControl = m_data.controlPoints.at(i);
841 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
842
843 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
844 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
845 iRes = i;
846 }
847 }
848
849 vStart = vEnd;
850 vControl = m_data.controlPoints.at(n - 2);
851 vEnd = m_data.controlPoints.at(n - 1);
852
853 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
854 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
855 iRes = n - 2;
856 }
857 }
858
859 *dt = dRes;
860 if (dist != nullptr) {
861 *dist = std::sqrt(dDist);
862 }
863 return iRes;
864}
865
866RS_Vector LC_SplinePoints::doGetNearestPointOnEntity(const RS_Vector& coord, [[maybe_unused]] bool onEntity, double* dist,
867 RS_Entity** entity) const {
868 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
869
870 double dt = 0.0;
871 const int iQuad = getNearestQuad(coord, dist, &dt);
872
873 if (iQuad < 0) {
874 return vRes;
875 }
876
877 const int n = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
878
879 if (n < 1) {
880 return vRes;
881 }
882
883 if (n < 2) {
884 vRes = vStart;
885 }
886 else if (n < 3) {
887 vRes = vStart * (1.0 - dt) + vEnd * dt;
888 }
889 else {
890 vRes = getQuadAtPoint(vStart, vControl, vEnd, dt);
891 }
892
893 if (entity != nullptr) {
894 *entity = const_cast<LC_SplinePoints*>(this);
895 }
896 return vRes;
897}
898
899double LC_SplinePoints::doGetDistanceToPoint(const RS_Vector& coord, RS_Entity** entity, [[maybe_unused]] RS2::ResolveLevel level,
900 [[maybe_unused]] double solidDist) const {
901 double dDist = RS_MAXDOUBLE1.0E+10;
902 getNearestPointOnEntity(coord, true, &dDist, entity);
903 return dDist;
904}
905
906//RS_Vector LC_SplinePoints::getNearestCenter(const RS_Vector& /*coord*/,
907// double* dist) const
908//{
909// if(dist != nullptr)
910// {
911// *dist = RS_MAXDOUBLE;
912// }
913
914// return RS_Vector(false);
915//}
916
917RS_Vector LC_SplinePoints::getSplinePointAtDist(double dDist, const int iStartSeg, const double dStartT, int* piSeg, double* pdt) const {
918 RS_Vector vRes(false);
919 if (m_data.closed) {
920 return vRes;
921 }
922
923 RS_Vector vStart(false), vControl(false), vEnd(false);
924
925 const size_t n = m_data.controlPoints.size();
926 size_t i = iStartSeg;
927
928 getQuadPoints(i, &vStart, &vControl, &vEnd);
929 double dQuadDist = getQuadLength(vStart, vControl, vEnd, dStartT, 1.0);
930 i++;
931
932 while (dDist > dQuadDist && i < n - 2) {
933 dDist -= dQuadDist;
934 vStart = vEnd;
935 vControl = m_data.controlPoints.at(i);
936 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
937 dQuadDist = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
938 i++;
939 }
940
941 if (dDist > dQuadDist) {
942 dDist -= dQuadDist;
943 vStart = vEnd;
944 vControl = m_data.controlPoints.at(n - 2);
945 vEnd = m_data.controlPoints.at(n - 1);
946 dQuadDist = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
947 i++;
948 }
949
950 if (dDist <= dQuadDist) {
951 double t0{0.0};
952 if (static_cast<size_t>(iStartSeg + 1) == i) {
953 t0 = dStartT;
954 }
955 const double dt = getQuadPointAtDist(vStart, vControl, vEnd, t0, dDist);
956 vRes = getQuadPoint(vStart, vControl, vEnd, dt);
957 *piSeg = i - 1;
958 *pdt = dt;
959 }
960
961 return vRes;
962}
963
964RS_Vector LC_SplinePoints::doGetNearestMiddle(const RS_Vector& coord, double* dist, const int middlePoints) const {
965 if (dist != nullptr) {
966 *dist = RS_MAXDOUBLE1.0E+10;
967 }
968 RS_Vector vStart(false), vControl(false), vEnd(false), vNext(false), vRes(false);
969
970 if (middlePoints < 1) {
971 return vRes;
972 }
973 if (m_data.closed) {
974 return vRes;
975 }
976
977 const size_t n = m_data.controlPoints.size();
978
979 if (n < 1) {
980 return vRes;
981 }
982
983 vStart = m_data.controlPoints.at(0);
984
985 if (n < 2) {
986 if (dist != nullptr) {
987 *dist = (vStart - coord).magnitude();
988 }
989 return vStart;
990 }
991
992 vEnd = m_data.controlPoints.at(1);
993
994 if (n < 3) {
995 return getNearestMiddleLine(vStart, vEnd, coord, dist, middlePoints);
996 }
997
998 double dCurDist = 0., dt = 0.;
999 double dMinDist = RS_MAXDOUBLE1.0E+10;
1000 const double dDist = getLength() / (1.0 + middlePoints);
1001
1002 vControl = vEnd;
1003 vEnd = m_data.controlPoints.at(2);
1004
1005 if (n < 4) {
1006 dt = getQuadPointAtDist(vStart, vControl, vEnd, 0.0, dDist);
1007 vRes = getQuadPoint(vStart, vControl, vEnd, dt);
1008 dMinDist = (vRes - coord).magnitude();
1009 for (int j = 1; j < middlePoints; j++) {
1010 dt = getQuadPointAtDist(vStart, vControl, vEnd, dt, dDist);
1011 vNext = getQuadPoint(vStart, vControl, vEnd, dt);
1012 dCurDist = (vNext - coord).magnitude();
1013
1014 if (dCurDist < dMinDist) {
1015 dMinDist = dCurDist;
1016 vRes = vNext;
1017 }
1018 }
1019
1020 if (dist != nullptr) {
1021 *dist = dMinDist;
1022 }
1023 return vRes;
1024 }
1025
1026 int iNext{0};
1027 vRes = getSplinePointAtDist(dDist, 1, 0.0, &iNext, &dt);
1028 if (vRes.valid) {
1029 dMinDist = (vRes - coord).magnitude();
1030 }
1031 int i = 2;
1032 while (vRes.valid && i <= middlePoints) {
1033 vNext = getSplinePointAtDist(dDist, iNext, dt, &iNext, &dt);
1034 dCurDist = (vNext - coord).magnitude();
1035
1036 if (vNext.valid && dCurDist < dMinDist) {
1037 dMinDist = dCurDist;
1038 vRes = vNext;
1039 }
1040 i++;
1041 }
1042
1043 if (dist != nullptr) {
1044 *dist = dMinDist;
1045 }
1046 return vRes;
1047}
1048
1049RS_Vector LC_SplinePoints::doGetNearestDist([[maybe_unused]] double distance, [[maybe_unused]] const RS_Vector& coord, double* dist) const {
1050 printf("getNearestDist\n");
1051 if (dist != nullptr) {
1052 *dist = RS_MAXDOUBLE1.0E+10;
1053 }
1054
1055 return RS_Vector(false);
1056}
1057
1058void LC_SplinePoints::move(const RS_Vector& offset) {
1059 for (auto& v : m_data.splinePoints) {
1060 v.move(offset);
1061 }
1062 for (auto& v : m_data.controlPoints) {
1063 v.move(offset);
1064 }
1065 update();
1066}
1067
1068void LC_SplinePoints::rotate(const RS_Vector& center, const double angle) {
1069 rotate(center, RS_Vector(angle));
1070}
1071
1072void LC_SplinePoints::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
1073 for (auto& v : m_data.splinePoints) {
1074 v.rotate(center, angleVector);
1075 }
1076 for (auto& v : m_data.controlPoints) {
1077 v.rotate(center, angleVector);
1078 }
1079 update();
1080}
1081
1082void LC_SplinePoints::scale(const RS_Vector& center, const RS_Vector& factor) {
1083 for (auto& v : m_data.splinePoints) {
1084 v.scale(center, factor);
1085 }
1086 for (auto& v : m_data.controlPoints) {
1087 v.scale(center, factor);
1088 }
1089 update();
1090}
1091
1092void LC_SplinePoints::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
1093 for (auto& v : m_data.splinePoints) {
1094 v.mirror(axisPoint1, axisPoint2);
1095 }
1096 for (auto& v : m_data.controlPoints) {
1097 v.mirror(axisPoint1, axisPoint2);
1098 }
1099 update();
1100}
1101
1102RS_Entity& LC_SplinePoints::shear(const double k) {
1103 for (auto& v : m_data.splinePoints) {
1104 v.shear(k);
1105 }
1106 for (auto& v : m_data.controlPoints) {
1107 v.shear(k);
1108 }
1109 update();
1110 return *this;
1111}
1112
1113void LC_SplinePoints::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
1114 for (auto& v : m_data.splinePoints) {
1115 // fixme - magic value - replace by constant
1116 if (ref.distanceTo(v) < 1.0e-4) {
1117 v.move(offset);
1118 }
1119 }
1120 for (auto& v : m_data.controlPoints) {
1121 // fixme - magic value - replace by constant
1122 if (ref.distanceTo(v) < 1.0e-4) {
1123 v.move(offset);
1124 }
1125 }
1126 update();
1127}
1128
1129void LC_SplinePoints::revertDirection() {
1130 size_t j = m_data.splinePoints.size() - 1;
1131 for (size_t k = 0; k < m_data.splinePoints.size() / 2; ++k) {
1132 std::swap(m_data.splinePoints[k], m_data.splinePoints[j--]);
1133 }
1134 j = m_data.controlPoints.size() - 1;
1135 for (size_t k = 0; k < m_data.controlPoints.size() / 2; ++k) {
1136 std::swap(m_data.controlPoints[k], m_data.controlPoints[j--]);
1137 }
1138 update();
1139}
1140
1141/**
1142 * @return The reference points of the spline.
1143 */
1144const std::vector<RS_Vector>& LC_SplinePoints::getPoints() const {
1145 if (m_data.cut) {
1146 return m_data.controlPoints;
1147 }
1148 return m_data.splinePoints;
1149}
1150
1151const std::vector<RS_Vector>& LC_SplinePoints::getControlPoints() const {
1152 return m_data.controlPoints;
1153}
1154
1155// fixme - sand - this method is used only for writing spline points... do we really neede a copy of the vector there?
1156std::vector<RS_Vector> LC_SplinePoints::getStrokePoints() const {
1157 const int p1 = getGraphicVariableInt("$SPLINESEGS", 8);
1158 std::vector<RS_Vector> result;
1159 fillStrokePoints(p1, result);
1160 return result;
1161}
1162
1163void LC_SplinePoints::fillStrokePoints(const int splineSegments, std::vector<RS_Vector>& points) const {
1164 size_t iSplines = m_data.controlPoints.size();
1165 if (!m_data.closed) {
1166 iSplines -= 2;
1167 }
1168
1169 RS_Vector vStart(false), vControl(false), vEnd(false);
1170 for (size_t i = 1; i <= iSplines; ++i) {
1171 const int iPts = getQuadPoints(i, &vStart, &vControl, &vEnd);
1172 if (iPts > 2) {
1173 strokeQuad(&points, vStart, vControl, vEnd, splineSegments);
1174 }
1175 else if (iPts > 1) {
1176 points.push_back(vStart);
1177 }
1178 }
1179
1180 if (!m_data.closed && vEnd.valid) {
1181 points.push_back(vEnd);
1182 }
1183}
1184
1185/**
1186 * push_backs the given point to the control points.
1187 */
1188bool LC_SplinePoints::addPoint(const RS_Vector& v) {
1189 if (m_data.cut) {
1190 return false;
1191 }
1192
1193 if (m_data.splinePoints.empty() || (v - m_data.splinePoints.back()).squared() > RS_TOLERANCE21.0e-20) {
1194 m_data.splinePoints.push_back(v);
1195 return true;
1196 }
1197 return false;
1198}
1199
1200/**
1201 * Removes the control point that was last added.
1202 */
1203void LC_SplinePoints::removeLastPoint() {
1204 m_data.splinePoints.pop_back();
1205}
1206
1207void LC_SplinePoints::addControlPoint(const RS_Vector& v) {
1208 m_data.controlPoints.push_back(v);
1209}
1210
1211std::vector<double> getMatrix(const size_t iCount, const bool bClosed, const std::vector<double>& dt) {
1212 if (bClosed && (iCount < 3 || dt.size() != iCount)) {
1213 return {};
1214 }
1215 if (!bClosed && (iCount < 4 || dt.size() != iCount - 2)) {
1216 return {};
1217 }
1218
1219 // closed: iDim = 5*iCount - 6; // n + 2*(n - 1) + 2*(n - 2)
1220 // not closed: iDim = 3*iCount - 8; // (n - 2) + 2*(n - 3)
1221 const int iDim = bClosed ? 5 * iCount - 6 : 3 * iCount - 8;
1222
1223 std::vector<double> dRes(iDim);
1224
1225 if (bClosed) {
1226 double* pdDiag = dRes.data();
1227 double* pdDiag1 = &dRes[iCount];
1228 double* pdDiag2 = &dRes[2 * iCount - 1];
1229 double* pdLastCol1 = &dRes[3 * iCount - 2];
1230 double* pdLastCol2 = &dRes[4 * iCount - 4];
1231
1232 double x1 = (1.0 - dt[0]) * (1.0 - dt[0]) / 2.0;
1233 double x3 = dt[0] * dt[0] / 2.0;
1234 double x2 = x1 + 2.0 * dt[0] * (1.0 - dt[0]) + x3;
1235
1236 pdDiag[0] = std::sqrt(x2);
1237 pdDiag1[0] = x3 / pdDiag[0];
1238 pdLastCol1[0] = x1 / pdDiag[0];
1239
1240 x1 = (1.0 - dt[1]) * (1.0 - dt[1]) / 2.0;
1241 x3 = dt[1] * dt[1] / 2.0;
1242 x2 = x1 + 2.0 * dt[1] * (1.0 - dt[1]) + x3;
1243
1244 pdDiag2[0] = x1 / pdDiag[0];
1245
1246 pdDiag[1] = std::sqrt(x2 - pdDiag1[0] * pdDiag2[0]);
1247 pdDiag1[1] = x3 / pdDiag[1];
1248 pdLastCol1[1] = -pdDiag2[0] * pdLastCol1[0] / pdDiag[1];
1249
1250 for (size_t i = 2; i < iCount - 2; i++) {
1251 x1 = (1.0 - dt[i]) * (1.0 - dt[i]) / 2.0;
1252 x3 = dt[i] * dt[i] / 2.0;
1253 x2 = x1 + 2.0 * dt[i] * (1.0 - dt[i]) + x3;
1254
1255 pdDiag2[i - 1] = x1 / pdDiag[i - 1];
1256
1257 pdDiag[i] = std::sqrt(x2 - pdDiag1[i - 1] * pdDiag2[i - 1]);
1258 pdDiag1[i] = x3 / pdDiag[i];
1259 pdLastCol1[i] = -pdDiag2[i - 1] * pdLastCol1[i - 1] / pdDiag[i];
1260 }
1261 x1 = (1.0 - dt[iCount - 2]) * (1.0 - dt[iCount - 2]) / 2.0;
1262 x3 = dt[iCount - 2] * dt[iCount - 2] / 2.0;
1263 x2 = x1 + 2.0 * dt[iCount - 2] * (1.0 - dt[iCount - 2]) + x3;
1264
1265 pdDiag2[iCount - 3] = x1 / pdDiag[iCount - 3];
1266
1267 pdDiag[iCount - 2] = std::sqrt(x2 - pdDiag1[iCount - 3] * pdDiag2[iCount - 3]);
1268 pdDiag1[iCount - 2] = (x3 - pdDiag2[iCount - 3] * pdLastCol1[iCount - 3]) / pdDiag[iCount - 2];
1269
1270 x1 = (1.0 - dt[iCount - 1]) * (1.0 - dt[iCount - 1]) / 2.0;
1271 x3 = dt[iCount - 1] * dt[iCount - 1] / 2.0;
1272 x2 = x1 + 2.0 * dt[iCount - 1] * (1.0 - dt[iCount - 1]) + x3;
1273
1274 pdLastCol2[0] = x3 / pdDiag[0];
1275 double dLastColSum = pdLastCol1[0] * pdLastCol2[0];
1276 for (size_t i = 1; i < iCount - 2; i++) {
1277 pdLastCol2[i] = -pdLastCol2[i - 1] * pdDiag1[i - 1] / pdDiag[i];
1278 dLastColSum += pdLastCol1[i] * pdLastCol2[i];
1279 }
1280
1281 pdDiag2[iCount - 2] = (x1 - pdDiag1[iCount - 3] * pdLastCol2[iCount - 3]) / pdDiag[iCount - 2];
1282
1283 dLastColSum += pdDiag1[iCount - 2] * pdDiag2[iCount - 2];
1284 pdDiag[iCount - 1] = std::sqrt(x2 - dLastColSum);
1285 }
1286 else {
1287 double* pdDiag = dRes.data();
1288 double* pdDiag1 = &dRes[iCount - 2];
1289 double* pdDiag2 = &dRes[2 * iCount - 5];
1290
1291 double x3 = dt[0] * dt[0] / 2.0;
1292 double x2 = 2.0 * dt[0] * (1.0 - dt[0]) + x3;
1293 pdDiag[0] = std::sqrt(x2);
1294 pdDiag1[0] = x3 / pdDiag[0];
1295
1296 for (size_t i = 1; i < iCount - 3; i++) {
1297 const double x1 = (1.0 - dt[i]) * (1.0 - dt[i]) / 2.0;
1298 x3 = dt[i] * dt[i] / 2.0;
1299 x2 = x1 + 2.0 * dt[i] * (1.0 - dt[i]) + x3;
1300
1301 pdDiag2[i - 1] = x1 / pdDiag[i - 1];
1302 pdDiag[i] = std::sqrt(x2 - pdDiag1[i - 1] * pdDiag2[i - 1]);
1303 pdDiag1[i] = x3 / pdDiag[i];
1304 }
1305
1306 const double x1 = (1.0 - dt[iCount - 3]) * (1.0 - dt[iCount - 3]) / 2.0;
1307 x2 = x1 + 2.0 * dt[iCount - 3] * (1.0 - dt[iCount - 3]);
1308 pdDiag2[iCount - 4] = x1 / pdDiag[iCount - 4];
1309 pdDiag[iCount - 3] = std::sqrt(x2 - pdDiag1[iCount - 4] * pdDiag2[iCount - 4]);
1310 }
1311
1312 return dRes;
1313}
1314
1315void LC_SplinePoints::updateControlPointsUI() {
1316 if (m_data.cut) {
1317 return; // no update after trim operation
1318 }
1319
1320 if (!m_data.useControlPoints) {
1321 m_data.controlPoints.clear();
1322 }
1323
1324 const size_t n = m_data.splinePoints.size();
1325
1326 if (m_data.closed && n < 3) {
1327 if (n > 0) {
1328 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1329 }
1330 if (n > 1) {
1331 m_data.controlPoints.push_back(m_data.splinePoints.at(1));
1332 }
1333 return;
1334 }
1335
1336 if (!m_data.closed && n < 4) {
1337 // use control points directly, reserved for parabola
1338 if (m_data.useControlPoints && m_data.controlPoints.size() == 3) {
1339 return;
1340 }
1341 if (n > 0) {
1342 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1343 }
1344 if (n > 2) {
1345 const RS_Vector vControl = getThreePointsControl(m_data.splinePoints.at(0), m_data.splinePoints.at(1),
1346 m_data.splinePoints.at(2));
1347 if (vControl.valid) {
1348 m_data.controlPoints.push_back(vControl);
1349 }
1350 }
1351 if (n > 1) {
1352 m_data.controlPoints.push_back(m_data.splinePoints.at(n - 1));
1353 }
1354 return;
1355 }
1356
1357 const int iDim = m_data.closed ? n : n - 2;
1358
1359 std::vector<double> dt(iDim);
1360
1361 if (m_data.closed) {
1362 double dl1 = (m_data.splinePoints.at(n - 1) - m_data.splinePoints.at(0)).magnitude();
1363 double dl2 = (m_data.splinePoints.at(1) - m_data.splinePoints.at(0)).magnitude();
1364 dt[0] = dl1 / (dl1 + dl2);
1365 for (int i = 1; i < iDim - 1; i++) {
1366 dl1 = dl2;
1367 dl2 = (m_data.splinePoints.at(i + 1) - m_data.splinePoints.at(i)).magnitude();
1368 dt[i] = dl1 / (dl1 + dl2);
1369 }
1370 dl1 = (m_data.splinePoints.at(n - 1) - m_data.splinePoints.at(n - 2)).magnitude();
1371 dl2 = (m_data.splinePoints.at(0) - m_data.splinePoints.at(n - 1)).magnitude();
1372 dt[iDim - 1] = dl1 / (dl1 + dl2);
1373 }
1374 else {
1375 double dl1 = (m_data.splinePoints.at(1) - m_data.splinePoints.at(0)).magnitude();
1376 double dl2 = (m_data.splinePoints.at(2) - m_data.splinePoints.at(1)).magnitude();
1377 dt[0] = dl1 / (dl1 + dl2 / 2.0);
1378 for (int i = 1; i < iDim - 1; i++) {
1379 dl1 = dl2;
1380 dl2 = (m_data.splinePoints.at(i + 2) - m_data.splinePoints.at(i + 1)).magnitude();
1381 dt[i] = dl1 / (dl1 + dl2);
1382 }
1383 dl1 = dl2;
1384 dl2 = (m_data.splinePoints.at(iDim) - m_data.splinePoints.at(iDim + 1)).magnitude();
1385 dt[iDim - 1] = dl1 / (dl1 + 2.0 * dl2);
1386 }
1387
1388 const std::vector<double> pdMatrix = getMatrix(n, m_data.closed, dt);
1389
1390 if (pdMatrix.empty()) {
1391 return;
1392 }
1393
1394 std::vector<double> dx(iDim);
1395 std::vector<double> dy(iDim);
1396 std::vector<double> dx2(iDim);
1397 std::vector<double> dy2(iDim);
1398
1399 if (m_data.closed) {
1400 const double* pdDiag = pdMatrix.data();
1401 const double* pdDiag1 = &pdMatrix[n];
1402 const double* pdDiag2 = &pdMatrix[2 * n - 1];
1403 const double* pdLastCol1 = &pdMatrix[3 * n - 2];
1404 const double* pdLastCol2 = &pdMatrix[4 * n - 4];
1405
1406 dx[0] = m_data.splinePoints.at(0).x / pdDiag[0];
1407 dy[0] = m_data.splinePoints.at(0).y / pdDiag[0];
1408 for (int i = 1; i < iDim - 1; i++) {
1409 dx[i] = (m_data.splinePoints.at(i).x - pdDiag2[i - 1] * dx[i - 1]) / pdDiag[i];
1410 dy[i] = (m_data.splinePoints.at(i).y - pdDiag2[i - 1] * dy[i - 1]) / pdDiag[i];
1411 }
1412
1413 dx[iDim - 1] = m_data.splinePoints.at(iDim - 1).x - pdDiag2[iDim - 2] * dx[iDim - 2];
1414 dy[iDim - 1] = m_data.splinePoints.at(iDim - 1).y - pdDiag2[iDim - 2] * dy[iDim - 2];
1415 for (int i = 0; i < iDim - 2; i++) {
1416 dx[iDim - 1] -= dx[i] * pdLastCol2[i];
1417 dy[iDim - 1] -= dy[i] * pdLastCol2[i];
1418 }
1419 dx[iDim - 1] /= pdDiag[iDim - 1];
1420 dy[iDim - 1] /= pdDiag[iDim - 1];
1421
1422 dx2[iDim - 1] = dx[iDim - 1] / pdDiag[iDim - 1];
1423 dy2[iDim - 1] = dy[iDim - 1] / pdDiag[iDim - 1];
1424 dx2[iDim - 2] = (dx[iDim - 2] - pdDiag1[iDim - 2] * dx2[iDim - 1]) / pdDiag[iDim - 2];
1425 dy2[iDim - 2] = (dy[iDim - 2] - pdDiag1[iDim - 2] * dy2[iDim - 1]) / pdDiag[iDim - 2];
1426
1427 for (int i = iDim - 3; i >= 0; i--) {
1428 dx2[i] = (dx[i] - pdDiag1[i] * dx2[i + 1] - pdLastCol1[i] * dx2[iDim - 1]) / pdDiag[i];
1429 dy2[i] = (dy[i] - pdDiag1[i] * dy2[i + 1] - pdLastCol1[i] * dy2[iDim - 1]) / pdDiag[i];
1430 }
1431
1432 for (int i = 0; i < iDim; i++) {
1433 m_data.controlPoints.emplace_back(dx2[i], dy2[i]);
1434 }
1435 }
1436 else {
1437 const double* pdDiag = pdMatrix.data();
1438 const double* pdDiag1 = &pdMatrix[n - 2];
1439 const double* pdDiag2 = &pdMatrix[2 * n - 5];
1440
1441 dx[0] = (m_data.splinePoints.at(1).x - m_data.splinePoints.at(0).x * (1.0 - dt[0]) * (1.0 - dt[0])) / pdDiag[0];
1442 dy[0] = (m_data.splinePoints.at(1).y - m_data.splinePoints.at(0).y * (1.0 - dt[0]) * (1.0 - dt[0])) / pdDiag[0];
1443 for (int i = 1; i < iDim - 1; i++) {
1444 dx[i] = (m_data.splinePoints.at(i + 1).x - pdDiag2[i - 1] * dx[i - 1]) / pdDiag[i];
1445 dy[i] = (m_data.splinePoints.at(i + 1).y - pdDiag2[i - 1] * dy[i - 1]) / pdDiag[i];
1446 }
1447 dx[iDim - 1] = ((m_data.splinePoints.at(iDim).x - m_data.splinePoints.at(iDim + 1).x * dt[n - 3] * dt[n - 3]) - pdDiag2[iDim - 2] *
1448 dx[iDim - 2]) / pdDiag[iDim - 1];
1449 dy[iDim - 1] = ((m_data.splinePoints.at(iDim).y - m_data.splinePoints.at(iDim + 1).y * dt[n - 3] * dt[n - 3]) - pdDiag2[iDim - 2] *
1450 dy[iDim - 2]) / pdDiag[iDim - 1];
1451
1452 dx2[iDim - 1] = dx[iDim - 1] / pdDiag[iDim - 1];
1453 dy2[iDim - 1] = dy[iDim - 1] / pdDiag[iDim - 1];
1454
1455 for (int i = iDim - 2; i >= 0; i--) {
1456 dx2[i] = (dx[i] - pdDiag1[i] * dx2[i + 1]) / pdDiag[i];
1457 dy2[i] = (dy[i] - pdDiag1[i] * dy2[i + 1]) / pdDiag[i];
1458 }
1459
1460 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1461 for (int i = 0; i < iDim; i++) {
1462 m_data.controlPoints.emplace_back(dx2[i], dy2[i]);
1463 }
1464 m_data.controlPoints.push_back(m_data.splinePoints.at(n - 1));
1465 }
1466}
1467
1468double getLinePointAtDist(const double dLen, const double t1, const double dDist) {
1469 return t1 + dDist / dLen;
1470}
1471
1472// returns new pattern offset;
1473double drawPatternLine(const std::vector<double>& pdPattern, const int iPattern, double patternOffset, QPainterPath& qPath,
1474 const RS_Vector& x1, const RS_Vector& x2) {
1475 const double dLen = (x2 - x1).magnitude();
1476 if (dLen < RS_TOLERANCE1.0e-10) {
1477 return patternOffset;
1478 }
1479
1480 int i = 0;
1481 double dCurSegLen = 0.0;
1482 double dSegOffs = 0.0;
1483 while (patternOffset > RS_TOLERANCE1.0e-10) {
1484 if (i >= iPattern) {
1485 i = 0;
1486 }
1487 dCurSegLen = std::abs(pdPattern[i++]);
1488 if (patternOffset > dCurSegLen) {
1489 patternOffset -= dCurSegLen;
1490 }
1491 else {
1492 dSegOffs = patternOffset;
1493 patternOffset = 0.0;
1494 }
1495 }
1496 if (i > 0) {
1497 i--;
1498 }
1499
1500 dCurSegLen = std::abs(pdPattern[i]) - dSegOffs;
1501 dSegOffs = 0.0;
1502
1503 double dt1 = 0.0;
1504 double dt2 = 1.0;
1505 double dCurLen = dLen;
1506 if (dCurSegLen < dCurLen) {
1507 // double dt2bak=dt1;
1508 dt2 = getLinePointAtDist(dLen, dt1, dCurSegLen);
1509 dCurLen -= dCurSegLen;
1510 }
1511 else {
1512 dSegOffs = dCurLen;
1513 dCurLen = 0.0;
1514 }
1515
1516 RS_Vector p2 = x1 * (1.0 - dt2) + x2 * dt2;
1517 if (pdPattern[i] < 0) {
1518 qPath.moveTo(QPointF(p2.x, p2.y));
1519 }
1520 else {
1521 qPath.lineTo(QPointF(p2.x, p2.y));
1522 }
1523
1524 i++;
1525 dt1 = dt2;
1526
1527 while (dCurLen > RS_TOLERANCE1.0e-10) {
1528 if (i >= iPattern) {
1529 i = 0;
1530 }
1531
1532 dCurSegLen = std::abs(pdPattern[i]);
1533 if (dCurLen > dCurSegLen) {
1534 dt2 = getLinePointAtDist(dLen, dt1, dCurSegLen);
1535 dCurLen -= dCurSegLen;
1536 }
1537 else {
1538 dt2 = 1.0;
1539 dSegOffs = dCurLen;
1540 dCurLen = 0.0;
1541 }
1542
1543 p2 = x1 * (1.0 - dt2) + x2 * dt2;
1544 if (pdPattern[i] < 0) {
1545 qPath.moveTo(QPointF(p2.x, p2.y));
1546 }
1547 else {
1548 qPath.lineTo(QPointF(p2.x, p2.y));
1549 }
1550
1551 i++;
1552 dt1 = dt2;
1553 }
1554
1555 i--;
1556
1557 while (i > 0) {
1558 dSegOffs += std::abs(pdPattern[--i]);
1559 }
1560 return dSegOffs;
1561}
1562
1563// returns new pattern offset;
1564double drawPatternQuad(const std::vector<double>& pdPattern, const int iPattern, double patternOffset, QPainterPath& qPath,
1565 const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) {
1566 const double dLen = getQuadLength(x1, c1, x2, 0.0, 1.0);
1567 if (dLen < RS_TOLERANCE1.0e-10) {
1568 return patternOffset;
1569 }
1570
1571 int i = 0;
1572 double dCurSegLen = 0.0;
1573 double dSegOffs = 0.0;
1574 while (patternOffset > RS_TOLERANCE1.0e-10) {
1575 if (i >= iPattern) {
1576 i = 0;
1577 }
1578 dCurSegLen = std::abs(pdPattern[i++]);
1579 if (patternOffset > dCurSegLen) {
1580 patternOffset -= dCurSegLen;
1581 }
1582 else {
1583 dSegOffs = patternOffset;
1584 patternOffset = 0.0;
1585 }
1586 }
1587 if (i > 0) {
1588 i--;
1589 }
1590
1591 dCurSegLen = std::abs(pdPattern[i]) - dSegOffs;
1592 dSegOffs = 0.0;
1593
1594 double dt1 = 0.0;
1595 double dt2 = 1.0;
1596 double dCurLen = dLen;
1597 if (dCurSegLen < dCurLen) {
1598 dt2 = getQuadPointAtDist(x1, c1, x2, dt1, dCurSegLen);
1599 dCurLen -= dCurSegLen;
1600 }
1601 else {
1602 dSegOffs = dCurLen;
1603 dCurLen = 0.0;
1604 }
1605
1606 RS_Vector c2;
1607
1608 RS_Vector p2 = getQuadPoint(x1, c1, x2, dt2);
1609 if (pdPattern[i] < 0) {
1610 qPath.moveTo(QPointF(p2.x, p2.y));
1611 }
1612 else {
1613 c2 = getSubQuadControlPoint(x1, c1, x2, dt1, dt2);
1614 qPath.quadTo(QPointF(c2.x, c2.y), QPointF(p2.x, p2.y));
1615 }
1616
1617 i++;
1618 dt1 = dt2;
1619
1620 while (dCurLen > RS_TOLERANCE1.0e-10) {
1621 if (i >= iPattern) {
1622 i = 0;
1623 }
1624
1625 dCurSegLen = std::abs(pdPattern[i]);
1626 if (dCurLen > dCurSegLen) {
1627 dt2 = getQuadPointAtDist(x1, c1, x2, dt1, dCurSegLen);
1628 dCurLen -= dCurSegLen;
1629 }
1630 else {
1631 dt2 = 1.0;
1632 dSegOffs = dCurLen;
1633 dCurLen = 0.0;
1634 }
1635
1636 p2 = getQuadPoint(x1, c1, x2, dt2);
1637 if (pdPattern[i] < 0) {
1638 qPath.moveTo(QPointF(p2.x, p2.y));
1639 }
1640 else {
1641 c2 = getSubQuadControlPoint(x1, c1, x2, dt1, dt2);
1642 qPath.quadTo(QPointF(c2.x, c2.y), QPointF(p2.x, p2.y));
1643 }
1644
1645 i++;
1646 dt1 = dt2;
1647 }
1648
1649 i--;
1650
1651 while (i > 0) {
1652 dSegOffs += std::abs(pdPattern[--i]);
1653 }
1654 return dSegOffs;
1655}
1656
1657void LC_SplinePoints::draw(RS_Painter* painter) {
1658 // Adjust dash offset
1659 painter->updateDashOffset(this);
1660 painter->drawSplinePointsWCS(m_data.controlPoints, m_data.closed);
1661}
1662
1663void LC_SplinePoints::updateLength() {
1664 size_t n = m_data.controlPoints.size();
1665
1666 if (n < 2) {
1667 m_cachedLength = 0;
1668 return;
1669 }
1670
1671 RS_Vector vStart(false), vControl(false), vEnd(false);
1672
1673 //UpdateControlPoints();
1674
1675 double dRes = 0.0;
1676
1677 if (m_data.closed) {
1678 if (n < 3) {
1679 m_cachedLength = 0.0;
1680 return;
1681 }
1682
1683 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1684 vControl = m_data.controlPoints.at(0);
1685 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1686
1687 dRes = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1688
1689 for (size_t i = 1; i < n - 1; i++) {
1690 vStart = vEnd;
1691 vControl = m_data.controlPoints.at(i);
1692 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1693
1694 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1695 }
1696
1697 vStart = vEnd;
1698 vControl = m_data.controlPoints.at(n - 1);
1699 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1700
1701 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1702 }
1703 else {
1704 vStart = m_data.controlPoints.at(0);
1705 vEnd = m_data.controlPoints.at(1);
1706 if (n < 3) {
1707 m_cachedLength = (vEnd - vStart).magnitude();
1708 return;
1709 }
1710
1711 vControl = vEnd;
1712 vEnd = m_data.controlPoints.at(2);
1713 if (n < 4) {
1714 m_cachedLength = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1715 return;
1716 }
1717
1718 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
1719
1720 dRes = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1721
1722 for (size_t i = 2; i < n - 2; i++) {
1723 vStart = vEnd;
1724 vControl = m_data.controlPoints.at(i);
1725 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1726
1727 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1728 }
1729
1730 vStart = vEnd;
1731 vControl = m_data.controlPoints.at(n - 2);
1732 vEnd = m_data.controlPoints.at(n - 1);
1733
1734 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1735 }
1736
1737 m_cachedLength = dRes;
1738}
1739
1740double LC_SplinePoints::getDirection1() const {
1741 const size_t n = m_data.controlPoints.size();
1742
1743 if (n < 2) {
1744 return 0.0;
1745 }
1746
1747 RS_Vector vStart, vEnd;
1748
1749 if (m_data.closed) {
1750 if (n < 3) {
1751 return 0.0;
1752 }
1753 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1754 vEnd = m_data.controlPoints.at(0);
1755 }
1756 else {
1757 vStart = m_data.controlPoints.at(0);
1758 vEnd = m_data.controlPoints.at(1);
1759 }
1760
1761 return vStart.angleTo(vEnd);
1762}
1763
1764double LC_SplinePoints::getDirection2() const {
1765 const size_t n = m_data.controlPoints.size();
1766
1767 if (n < 2) {
1768 return 0.0;
1769 }
1770
1771 RS_Vector vStart, vEnd;
1772
1773 if (m_data.closed) {
1774 if (n < 3) {
1775 return 0.0;
1776 }
1777 vStart = m_data.controlPoints.at(n - 1);
1778 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1779 }
1780 else {
1781 vStart = m_data.controlPoints.at(n - 2);
1782 vEnd = m_data.controlPoints.at(n - 1);
1783 }
1784
1785 return vEnd.angleTo(vStart);
1786}
1787
1788RS_VectorSolutions LC_SplinePoints::getTangentPoint(const RS_Vector& point) const {
1789 RS_VectorSolutions ret;
1790 size_t n = m_data.controlPoints.size();
1791
1792 if (n < 3) {
1793 return ret;
1794 }
1795
1796 RS_Vector vStart(false), vControl(false), vEnd(false);
1797
1798 if (m_data.closed) {
1799 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1800 vControl = m_data.controlPoints.at(0);
1801 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1802
1803 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1804
1805 for (size_t i = 1; i < n - 1; i++) {
1806 vStart = vEnd;
1807 vControl = m_data.controlPoints.at(i);
1808 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1809
1810 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1811 }
1812
1813 vStart = vEnd;
1814 vControl = m_data.controlPoints.at(n - 1);
1815 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1816
1817 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1818 }
1819 else {
1820 vStart = m_data.controlPoints.at(0);
1821 vControl = m_data.controlPoints.at(1);
1822 vEnd = m_data.controlPoints.at(2);
1823 if (n < 4) {
1824 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1825 return ret;
1826 }
1827
1828 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
1829
1830 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1831
1832 for (size_t i = 2; i < n - 2; i++) {
1833 vStart = vEnd;
1834 vControl = m_data.controlPoints.at(i);
1835 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1836
1837 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1838 }
1839
1840 vStart = vEnd;
1841 vControl = m_data.controlPoints.at(n - 2);
1842 vEnd = m_data.controlPoints.at(n - 1);
1843
1844 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1845 }
1846
1847 return ret;
1848}
1849
1850RS_Vector LC_SplinePoints::getTangentDirection(const RS_Vector& point) const {
1851 const size_t n = m_data.controlPoints.size();
1852
1853 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
1854
1855 if (n < 2) {
1856 return vStart;
1857 }
1858
1859 double dt = 0.0;
1860 const int iQuad = getNearestQuad(point, nullptr, &dt);
1861 if (iQuad < 0) {
1862 return vStart;
1863 }
1864
1865 const int i = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
1866
1867 if (i < 2) {
1868 return vStart;
1869 }
1870 if (i < 3) {
1871 vRes = vEnd - vStart;
1872 }
1873 else {
1874 vRes = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
1875 }
1876
1877 return vRes;
1878}
1879
1880LC_SplinePointsData gddLineOffset(const RS_Vector& vx1, const RS_Vector& vx2, const double distance) {
1881 LC_SplinePointsData ret(false, false);
1882
1883 double dDist = (vx2 - vx1).magnitude();
1884
1885 if (dDist < RS_TOLERANCE1.0e-10) {
1886 return ret;
1887 }
1888
1889 dDist = distance / dDist;
1890
1891 ret.splinePoints.emplace_back(vx1.x - dDist * (vx2.y - vx1.y), vx1.y + dDist * (vx2.x - vx1.x));
1892 ret.splinePoints.emplace_back(vx2.x - dDist * (vx2.y - vx1.y), vx2.y + dDist * (vx2.x - vx1.x));
1893 return ret;
1894}
1895
1896bool LC_SplinePoints::offsetCut(const RS_Vector& coord, const double& distance) {
1897 size_t n = m_data.controlPoints.size();
1898 if (n < 2) {
1899 return false;
1900 }
1901
1902 double dt;
1903 int iQuad = getNearestQuad(coord, nullptr, &dt);
1904 if (iQuad < 0) {
1905 return false;
1906 }
1907
1908 RS_Vector vStart(false), vEnd(false), vControl(false);
1909 RS_Vector vPoint(false), vTan(false);
1910
1911 if (getQuadPoints(iQuad, &vStart, &vControl, &vEnd)) {
1912 vPoint = getQuadAtPoint(vStart, vControl, vEnd, dt);
1913 vTan = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
1914 }
1915 else {
1916 vPoint = vEnd * (1.0 - dt) - vStart * dt;
1917 vTan = vEnd - vStart;
1918 }
1919
1920 double dDist = distance;
1921 if ((coord.x - vPoint.x) * vTan.y - (coord.y - vPoint.y) * vTan.x > 0) {
1922 dDist *= -1.0;
1923 }
1924
1925 LC_SplinePointsData spd(m_data.closed, false);
1926
1927 if (m_data.closed) {
1928 if (n < 3) {
1929 return false;
1930 }
1931
1932 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1933 vControl = m_data.controlPoints.at(0);
1934 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1935
1936 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1937 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1938 if (vTan.valid) {
1939 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1940 }
1941
1942 for (size_t i = 1; i < n - 1; i++) {
1943 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
1944 vControl = m_data.controlPoints.at(i);
1945 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1946
1947 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1948 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1949 if (vTan.valid) {
1950 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1951 }
1952 }
1953
1954 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1955 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1956 if (vTan.valid) {
1957 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1958 }
1959 }
1960 else {
1961 vStart = m_data.controlPoints.at(0);
1962 vEnd = m_data.controlPoints.at(1);
1963
1964 if (n < 3) {
1965 spd = gddLineOffset(vStart, vEnd, dDist);
1966 bool bRes = spd.splinePoints.size() > 0;
1967 if (bRes) {
1968 m_data = spd;
1969 update();
1970 m_data.cut = true;
1971 }
1972 return bRes;
1973 }
1974
1975 vControl = vEnd;
1976 vEnd = m_data.controlPoints.at(2);
1977 if (n < 4) {
1978 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
1979 if (vTan.valid) {
1980 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
1981 }
1982
1983 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1984 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1985 if (vTan.valid) {
1986 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1987 }
1988
1989 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
1990 if (vTan.valid) {
1991 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
1992 }
1993
1994 m_data = spd;
1995 update();
1996 m_data.cut = true;
1997 return true;
1998 }
1999
2000 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2001
2002 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2003 if (vTan.valid) {
2004 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2005 }
2006
2007 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2008 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2009 if (vTan.valid) {
2010 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2011 }
2012
2013 for (size_t i = 2; i < n - 2; i++) {
2014 vStart = vEnd;
2015 vControl = m_data.controlPoints.at(i);
2016 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2017
2018 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2019 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2020 if (vTan.valid) {
2021 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2022 }
2023 }
2024
2025 vStart = vEnd;
2026 vControl = m_data.controlPoints.at(n - 2);
2027 vEnd = m_data.controlPoints.at(n - 1);
2028
2029 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2030 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2031 if (vTan.valid) {
2032 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2033 }
2034
2035 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2036 if (vTan.valid) {
2037 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2038 }
2039 }
2040 m_data = spd;
2041 update();
2042 m_data.cut = true;
2043 return true;
2044}
2045
2046bool LC_SplinePoints::offsetSpline(const RS_Vector& coord, const double& distance) {
2047 size_t iPoints = m_data.splinePoints.size();
2048 size_t n = m_data.controlPoints.size();
2049
2050 if (iPoints < 2) {
4
Assuming 'iPoints' is >= 2
5
Taking false branch
2051 return false;
2052 }
2053 if (n < 2) {
6
Assuming 'n' is >= 2
7
Taking false branch
2054 return false;
2055 }
2056
2057 double dt;
8
'dt' declared without an initial value
2058 int iQuad = getNearestQuad(coord, nullptr, &dt);
9
Calling 'LC_SplinePoints::getNearestQuad'
18
Returning from 'LC_SplinePoints::getNearestQuad'
2059 if (iQuad
18.1
'iQuad' is >= 0
< 0) {
19
Taking false branch
2060 return false;
2061 }
2062
2063 RS_Vector vStart(false), vEnd(false), vControl(false);
2064 RS_Vector vPoint(false), vTan(false);
2065
2066 if (getQuadPoints(iQuad, &vStart, &vControl, &vEnd)) {
20
Taking false branch
2067 vPoint = getQuadAtPoint(vStart, vControl, vEnd, dt);
2068 vTan = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
2069 }
2070 else {
2071 vPoint = vEnd * (1.0 - dt) - vStart * dt;
21
The right operand of '-' is a garbage value
2072 vTan = vEnd - vStart;
2073 }
2074
2075 double dDist = distance;
2076 if ((coord.x - vPoint.x) * vTan.y - (coord.y - vPoint.y) * vTan.x > 0) {
2077 dDist *= -1.0;
2078 }
2079
2080 LC_SplinePointsData spd(m_data.closed, m_data.cut);
2081
2082 double dl1, dl2;
2083
2084 if (m_data.closed) {
2085 if (n < 3) {
2086 return false;
2087 }
2088
2089 vPoint = m_data.splinePoints.at(0);
2090
2091 dl1 = (m_data.splinePoints.at(iPoints - 1) - vPoint).magnitude();
2092 dl2 = (m_data.splinePoints.at(1) - vPoint).magnitude();
2093 dt = dl1 / (dl1 + dl2);
2094
2095 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2096 vControl = m_data.controlPoints.at(0);
2097 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2098
2099 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2100 if (vTan.valid) {
2101 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2102 }
2103
2104 for (size_t i = 1; i < n - 1; i++) {
2105 vPoint = m_data.splinePoints.at(i);
2106
2107 dl1 = dl2;
2108 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2109 dt = dl1 / (dl1 + dl2);
2110
2111 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2112 vControl = m_data.controlPoints.at(i);
2113 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2114
2115 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2116
2117 if (vTan.valid) {
2118 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2119 }
2120 }
2121
2122 vPoint = m_data.splinePoints.at(iPoints - 1);
2123 dl1 = (vPoint - m_data.splinePoints.at(iPoints - 2)).magnitude();
2124 dl2 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2125 dt = dl1 / (dl1 + dl2);
2126
2127 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2128 if (vTan.valid) {
2129 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2130 }
2131 }
2132 else {
2133 vStart = m_data.controlPoints.at(0);
2134 vEnd = m_data.controlPoints.at(1);
2135
2136 if (n < 3) {
2137 spd = gddLineOffset(vStart, vEnd, dDist);
2138 bool bRes = !spd.splinePoints.empty();
2139 if (bRes) {
2140 m_data = spd;
2141 }
2142 return bRes;
2143 }
2144
2145 vPoint = m_data.splinePoints.at(1);
2146
2147 vControl = vEnd;
2148 vEnd = m_data.controlPoints.at(2);
2149 if (n < 4) {
2150 dl1 = (vPoint - vStart).magnitude();
2151 dl2 = (vEnd - vPoint).magnitude();
2152 dt = dl1 / (dl1 + dl2);
2153
2154 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2155 if (vTan.valid) {
2156 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2157 }
2158
2159 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2160 if (vTan.valid) {
2161 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2162 }
2163
2164 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2165 if (vTan.valid) {
2166 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2167 }
2168
2169 m_data = spd;
2170 return true;
2171 }
2172
2173 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2174
2175 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2176 if (vTan.valid) {
2177 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2178 }
2179
2180 dl1 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2181 dl2 = (m_data.splinePoints.at(2) - vPoint).magnitude();
2182 dt = dl1 / (dl1 + dl2 / 2.0);
2183
2184 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2185 if (vTan.valid) {
2186 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2187 }
2188
2189 for (size_t i = 2; i < n - 2; i++) {
2190 vPoint = m_data.splinePoints.at(i);
2191
2192 dl1 = dl2;
2193 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2194 dt = dl1 / (dl1 + dl2);
2195
2196 vStart = vEnd;
2197 vControl = m_data.controlPoints.at(i);
2198 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2199
2200 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2201 if (vTan.valid) {
2202 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2203 }
2204 }
2205
2206 vPoint = m_data.splinePoints.at(n - 2);
2207
2208 dl1 = dl2;
2209 dl2 = (vPoint - m_data.splinePoints.at(n - 1)).magnitude();
2210 dt = dl1 / (dl1 + 2.0 * dl2);
2211
2212 vStart = vEnd;
2213 vControl = m_data.controlPoints.at(n - 2);
2214 vEnd = m_data.controlPoints.at(n - 1);
2215
2216 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2217 if (vTan.valid) {
2218 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2219 }
2220
2221 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2222 if (vTan.valid) {
2223 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2224 }
2225 }
2226 m_data = spd;
2227 return true;
2228}
2229
2230bool LC_SplinePoints::offset(const RS_Vector& coord, const double distance) {
2231 if (m_data.cut) {
1
Assuming field 'cut' is false
2
Taking false branch
2232 return offsetCut(coord, distance);
2233 }
2234 // offsetSpline() keeps only the spline points: rebuild the control points, borders and length
2235 const bool offsetDone = offsetSpline(coord, distance);
3
Calling 'LC_SplinePoints::offsetSpline'
2236 update();
2237 return offsetDone;
2238}
2239
2240std::vector<RS_Entity*> addLineOffsets(const RS_Vector& vx1, const RS_Vector& vx2, const double& distance) {
2241 std::vector<RS_Entity*> ret(0, nullptr);
2242
2243 double dDist = (vx2 - vx1).magnitude();
2244
2245 if (dDist < RS_TOLERANCE1.0e-10) {
2246 ret.push_back(new RS_Circle(nullptr, {vx1, distance}));
2247 return ret;
2248 }
2249
2250 const LC_SplinePointsData spd1(false, false);
2251 const LC_SplinePointsData spd2(false, false);
2252
2253 auto* sp1 = new LC_SplinePoints(nullptr, spd1);
2254 auto* sp2 = new LC_SplinePoints(nullptr, spd2);
2255
2256 dDist = distance / dDist;
2257
2258 sp1->addPoint(RS_Vector(vx1.x - dDist * (vx2.y - vx1.y), vx1.y + dDist * (vx2.x - vx1.x)));
2259 sp2->addPoint(RS_Vector(vx1.x + dDist * (vx2.y - vx1.y), vx1.y - dDist * (vx2.x - vx1.x)));
2260
2261 sp1->addPoint(RS_Vector(vx2.x - dDist * (vx2.y - vx1.y), vx2.y + dDist * (vx2.x - vx1.x)));
2262 sp2->addPoint(RS_Vector(vx2.x + dDist * (vx2.y - vx1.y), vx2.y - dDist * (vx2.x - vx1.x)));
2263
2264 ret.push_back(sp1);
2265 ret.push_back(sp2);
2266 return ret;
2267}
2268
2269std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSidesSpline(const double& distance) const {
2270 std::vector<RS_Entity*> ret(0, nullptr);
2271
2272 size_t iPoints = m_data.splinePoints.size();
2273 size_t n = m_data.controlPoints.size();
2274
2275 if (iPoints < 1) {
2276 return ret;
2277 }
2278 if (n < 1) {
2279 return ret;
2280 }
2281
2282 LC_SplinePointsData spd1(m_data.closed, false);
2283 LC_SplinePointsData spd2(m_data.closed, false);
2284
2285 LC_SplinePoints *sp1, *sp2;
2286
2287 RS_Vector vStart(false), vEnd(false), vControl(false);
2288 RS_Vector vPoint(false), vTan(false);
2289
2290 double dt, dl1, dl2;
2291
2292 if (m_data.closed) {
2293 if (n < 3) {
2294 return ret;
2295 }
2296
2297 sp1 = new LC_SplinePoints(nullptr, spd1);
2298 sp2 = new LC_SplinePoints(nullptr, spd2);
2299
2300 vPoint = m_data.splinePoints.at(0);
2301
2302 dl1 = (m_data.splinePoints.at(iPoints - 1) - vPoint).magnitude();
2303 dl2 = (m_data.splinePoints.at(1) - vPoint).magnitude();
2304 dt = dl1 / (dl1 + dl2);
2305
2306 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2307 vControl = m_data.controlPoints.at(0);
2308 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2309
2310 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2311 if (vTan.valid) {
2312 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2313 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2314 }
2315
2316 for (size_t i = 1; i < n - 1; i++) {
2317 vPoint = m_data.splinePoints.at(i);
2318
2319 dl1 = dl2;
2320 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2321 dt = dl1 / (dl1 + dl2);
2322
2323 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2324 vControl = m_data.controlPoints.at(i);
2325 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2326
2327 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2328
2329 if (vTan.valid) {
2330 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2331 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2332 }
2333 }
2334
2335 vPoint = m_data.splinePoints.at(iPoints - 1);
2336 dl1 = (vPoint - m_data.splinePoints.at(iPoints - 2)).magnitude();
2337 dl2 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2338 dt = dl1 / (dl1 + dl2);
2339
2340 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2341 if (vTan.valid) {
2342 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2343 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2344 }
2345 }
2346 else {
2347 vStart = m_data.controlPoints.at(0);
2348 if (n < 2) {
2349 ret.push_back(new RS_Circle(nullptr, {vStart, distance}));
2350 return ret;
2351 }
2352
2353 vEnd = m_data.controlPoints.at(1);
2354 if (n < 3) {
2355 return addLineOffsets(vStart, vEnd, distance);
2356 }
2357
2358 vPoint = m_data.splinePoints.at(1);
2359
2360 vControl = vEnd;
2361 vEnd = m_data.controlPoints.at(2);
2362
2363 if (n < 4) {
2364 dl1 = (vPoint - vStart).magnitude();
2365 dl2 = (vEnd - vPoint).magnitude();
2366 dt = dl1 / (dl1 + dl2);
2367
2368 sp1 = new LC_SplinePoints(nullptr, spd1);
2369 sp2 = new LC_SplinePoints(nullptr, spd2);
2370
2371 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2372 if (vTan.valid) {
2373 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2374 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2375 }
2376
2377 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2378 if (vTan.valid) {
2379 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2380 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2381 }
2382
2383 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2384 if (vTan.valid) {
2385 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2386 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2387 }
2388
2389 ret.push_back(sp1);
2390 ret.push_back(sp2);
2391 return ret;
2392 }
2393
2394 sp1 = new LC_SplinePoints(nullptr, spd1);
2395 sp2 = new LC_SplinePoints(nullptr, spd2);
2396
2397 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2398
2399 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2400 if (vTan.valid) {
2401 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2402 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2403 }
2404
2405 dl1 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2406 dl2 = (m_data.splinePoints.at(2) - vPoint).magnitude();
2407 dt = dl1 / (dl1 + dl2 / 2.0);
2408
2409 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2410 if (vTan.valid) {
2411 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2412 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2413 }
2414
2415 for (size_t i = 2; i < n - 2; i++) {
2416 vPoint = m_data.splinePoints.at(i);
2417
2418 dl1 = dl2;
2419 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2420 dt = dl1 / (dl1 + dl2);
2421
2422 vStart = vEnd;
2423 vControl = m_data.controlPoints.at(i);
2424 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2425
2426 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2427 if (vTan.valid) {
2428 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2429 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2430 }
2431 }
2432
2433 vPoint = m_data.splinePoints.at(n - 2);
2434
2435 dl1 = dl2;
2436 dl2 = (vPoint - m_data.splinePoints.at(n - 1)).magnitude();
2437 dt = dl1 / (dl1 + 2.0 * dl2);
2438
2439 vStart = vEnd;
2440 vControl = m_data.controlPoints.at(n - 2);
2441 vEnd = m_data.controlPoints.at(n - 1);
2442
2443 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2444 if (vTan.valid) {
2445 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2446 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2447 }
2448
2449 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2450 if (vTan.valid) {
2451 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2452 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2453 }
2454 }
2455
2456 ret.push_back(sp1);
2457 ret.push_back(sp2);
2458 return ret;
2459}
2460
2461std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSidesCut(const double& distance) const {
2462 std::vector<RS_Entity*> ret(0, nullptr);
2463
2464 size_t n = m_data.controlPoints.size();
2465
2466 if (n < 1) {
2467 return ret;
2468 }
2469
2470 LC_SplinePointsData spd1(m_data.closed, false);
2471 LC_SplinePointsData spd2(m_data.closed, false);
2472
2473 LC_SplinePoints *sp1, *sp2;
2474
2475 RS_Vector vStart(false), vEnd(false), vControl(false);
2476 RS_Vector vPoint(false), vTan(false);
2477
2478 if (m_data.closed) {
2479 if (n < 3) {
2480 return ret;
2481 }
2482
2483 sp1 = new LC_SplinePoints(nullptr, spd1);
2484 sp2 = new LC_SplinePoints(nullptr, spd2);
2485
2486 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2487 vControl = m_data.controlPoints.at(0);
2488 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2489
2490 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2491 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2492 if (vTan.valid) {
2493 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2494 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2495 }
2496
2497 for (size_t i = 1; i < n - 1; i++) {
2498 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2499 vControl = m_data.controlPoints.at(i);
2500 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2501
2502 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2503 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2504 if (vTan.valid) {
2505 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2506 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2507 }
2508 }
2509
2510 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2511 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2512 if (vTan.valid) {
2513 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2514 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2515 }
2516 }
2517 else {
2518 vStart = m_data.controlPoints.at(0);
2519 if (n < 2) {
2520 ret.push_back(new RS_Circle(nullptr, RS_CircleData(vStart, distance)));
2521 return ret;
2522 }
2523
2524 vEnd = m_data.controlPoints.at(1);
2525 if (n < 3) {
2526 ret = addLineOffsets(vStart, vEnd, distance);
2527 sp1 = static_cast<LC_SplinePoints*>(ret[0]);
2528 sp1->update();
2529 sp1->m_data.cut = true;
2530 sp2 = static_cast<LC_SplinePoints*>(ret[1]);
2531 sp2->update();
2532 sp2->m_data.cut = true;
2533 return ret;
2534 }
2535
2536 vControl = vEnd;
2537 vEnd = m_data.controlPoints.at(2);
2538
2539 if (n < 4) {
2540 sp1 = new LC_SplinePoints(nullptr, spd1);
2541 sp2 = new LC_SplinePoints(nullptr, spd2);
2542
2543 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2544 if (vTan.valid) {
2545 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2546 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2547 }
2548
2549 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2550 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2551 if (vTan.valid) {
2552 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2553 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2554 }
2555
2556 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2557 if (vTan.valid) {
2558 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2559 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2560 }
2561
2562 sp1->update();
2563 sp1->m_data.cut = true;
2564 sp2->update();
2565 sp2->m_data.cut = true;
2566
2567 ret.push_back(sp1);
2568 ret.push_back(sp2);
2569 return ret;
2570 }
2571
2572 sp1 = new LC_SplinePoints(nullptr, spd1);
2573 sp2 = new LC_SplinePoints(nullptr, spd2);
2574
2575 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2576
2577 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2578 if (vTan.valid) {
2579 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2580 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2581 }
2582
2583 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2584 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2585 if (vTan.valid) {
2586 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2587 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2588 }
2589
2590 for (size_t i = 2; i < n - 2; i++) {
2591 vStart = vEnd;
2592 vControl = m_data.controlPoints.at(i);
2593 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2594
2595 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2596 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2597 if (vTan.valid) {
2598 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2599 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2600 }
2601 }
2602
2603 vStart = vEnd;
2604 vControl = m_data.controlPoints.at(n - 2);
2605 vEnd = m_data.controlPoints.at(n - 1);
2606
2607 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2608 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2609 if (vTan.valid) {
2610 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2611 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2612 }
2613
2614 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2615 if (vTan.valid) {
2616 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2617 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2618 }
2619 }
2620
2621 sp1->update();
2622 sp1->m_data.cut = true;
2623 sp2->update();
2624 sp2->m_data.cut = true;
2625
2626 ret.push_back(sp1);
2627 ret.push_back(sp2);
2628 return ret;
2629}
2630
2631std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSides(const double distance) const {
2632 if (m_data.cut) {
2633 return offsetTwoSidesCut(distance);
2634 }
2635 return offsetTwoSidesSpline(distance);
2636}
2637
2638/**
2639 * Dumps the spline's m_data to stdout.
2640 */
2641std::ostream& operator <<(std::ostream& os, const LC_SplinePoints& l) {
2642 os << " SplinePoints: " << l.getData() << "\n";
2643 return os;
2644}
2645
2646RS_VectorSolutions getLineLineIntersect(const RS_Vector& vStart, const RS_Vector& vEnd, const RS_Vector& vx1, const RS_Vector& vx2) {
2647 RS_VectorSolutions ret;
2648
2649 const RS_Vector x1 = vx2 - vx1;
2650 const RS_Vector x2 = vStart - vEnd;
2651 RS_Vector x3 = vStart - vx1;
2652
2653 const double dDet = x1.x * x2.y - x1.y * x2.x;
2654 if (std::abs(dDet) < RS_TOLERANCE1.0e-10) {
2655 return ret;
2656 }
2657
2658 double dt = (x2.y * x3.x - x2.x * x3.y) / dDet;
2659 const double ds = (-x1.y * x3.x + x1.x * x3.y) / dDet;
2660
2661 if (dt < -RS_TOLERANCE1.0e-10) {
2662 return ret;
2663 }
2664 if (ds < -RS_TOLERANCE1.0e-10) {
2665 return ret;
2666 }
2667 if (dt > 1.0 + RS_TOLERANCE1.0e-10) {
2668 return ret;
2669 }
2670 if (ds > 1.0 + RS_TOLERANCE1.0e-10) {
2671 return ret;
2672 }
2673
2674 if (dt < 0.0) {
2675 dt = 0.0;
2676 }
2677 if (dt > 1.0) {
2678 dt = 1.0;
2679 }
2680
2681 x3 = vx1 * (1.0 - dt) + vx2 * dt;
2682
2683 ret.push_back(x3);
2684
2685 return ret;
2686}
2687
2688RS_VectorSolutions LC_SplinePoints::getLineIntersect(const RS_Vector& x1, const RS_Vector& x2) const {
2689 RS_VectorSolutions ret;
2690
2691 size_t n = m_data.controlPoints.size();
2692 if (n < 2) {
2693 return ret;
2694 }
2695
2696 RS_Vector vStart(false), vEnd(false), vControl(false);
2697
2698 if (m_data.closed) {
2699 if (n < 3) {
2700 return ret;
2701 }
2702
2703 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2704 vControl = m_data.controlPoints.at(0);
2705 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2706
2707 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2708
2709 for (size_t i = 1; i < n - 1; i++) {
2710 vStart = vEnd;
2711 vControl = m_data.controlPoints.at(i);
2712 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2713
2714 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2715 }
2716
2717 vStart = vEnd;
2718 vControl = m_data.controlPoints.at(n - 1);
2719 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2720
2721 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2722 }
2723 else {
2724 vStart = m_data.controlPoints.at(0);
2725 vEnd = m_data.controlPoints.at(1);
2726 if (n < 3) {
2727 return getLineLineIntersect(x1, x2, vStart, vEnd);
2728 }
2729
2730 vControl = vEnd;
2731 vEnd = m_data.controlPoints.at(2);
2732 if (n < 4) {
2733 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2734 return ret;
2735 }
2736
2737 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2738 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2739
2740 for (size_t i = 2; i < n - 2; i++) {
2741 vStart = vEnd;
2742 vControl = m_data.controlPoints.at(i);
2743 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2744
2745 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2746 }
2747
2748 vStart = vEnd;
2749 vControl = m_data.controlPoints.at(n - 2);
2750 vEnd = m_data.controlPoints.at(n - 1);
2751
2752 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2753 }
2754
2755 return ret;
2756}
2757
2758void addQuadQuadIntersect(RS_VectorSolutions* pVS, const RS_Vector& vStart, const RS_Vector& vControl, const RS_Vector& vEnd,
2759 const RS_Vector& vx1, const RS_Vector& vc1, const RS_Vector& vx2) {
2760 //avoid intersection if there's no intersection between lines
2761 //TODO, avoid O(N^2) complexity
2762 //tangential direction along (start, control, end)
2763 const std::array<RS_Line, 2> lines0{{{vStart, vControl}, {vEnd, vControl}}};
2764
2765 //tangential direction along (start, control, end)
2766 const std::array<RS_Line, 2> lines1{{{vx1, vc1}, {vx2, vc1}}};
2767
2768 //if lines0, lines1 do not overlap, there's no intersection
2769 bool overlap = false;
2770 for (const auto& l0 : lines0) {
2771 for (const auto& l1 : lines1) {
2772 if (RS_Information::getIntersection(&l0, &l1, true).size()) {
2773 overlap = true;
2774 break;
2775 }
2776 }
2777 if (overlap) {
2778 break;
2779 }
2780 }
2781 if (!overlap) {
2782 //if there's no overlap, return now
2783 return;
2784 }
2785
2786 const RS_Vector va0 = vStart;
2787 const RS_Vector va1 = (vControl - vStart) * 2.0;
2788 const RS_Vector va2 = vEnd - vControl * 2.0 + vStart;
2789
2790 const RS_Vector vb0 = vx1;
2791 const RS_Vector vb1 = (vc1 - vx1) * 2.0;
2792 const RS_Vector vb2 = vx2 - vc1 * 2.0 + vx1;
2793
2794 std::vector<double> a1(0, 0.), b1(0, 0.);
2795 a1.push_back(va2.x);
2796 b1.push_back(va2.y);
2797 a1.push_back(0.0);
2798 b1.push_back(0.0);
2799 a1.push_back(-vb2.x);
2800 b1.push_back(-vb2.y);
2801 a1.push_back(va1.x);
2802 b1.push_back(va1.y);
2803 a1.push_back(-vb1.x);
2804 b1.push_back(-vb1.y);
2805 a1.push_back(va0.x - vb0.x);
2806 b1.push_back(va0.y - vb0.y);
2807
2808 std::vector<std::vector<double>> m(0);
2809 m.push_back(a1);
2810 m.push_back(b1);
2811
2812 const RS_VectorSolutions& pvRes = RS_Math::simultaneousQuadraticSolverFull(m);
2813
2814 for (RS_Vector vSol : pvRes) {
2815 if (vSol.x > -RS_TOLERANCE1.0e-10 && vSol.x < 1.0 + RS_TOLERANCE1.0e-10 && vSol.y > -RS_TOLERANCE1.0e-10 && vSol.y < 1.0 + RS_TOLERANCE1.0e-10) {
2816 if (vSol.x < 0.0) {
2817 vSol.x = 0.0;
2818 }
2819 if (vSol.x > 1.0) {
2820 vSol.x = 1.0;
2821 }
2822 pVS->push_back(getQuadPoint(vStart, vControl, vEnd, vSol.x));
2823 }
2824 }
2825}
2826
2827void LC_SplinePoints::addQuadIntersect(RS_VectorSolutions* sol, const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) const {
2828 size_t n = m_data.controlPoints.size();
2829 if (n < 2) {
2830 return;
2831 }
2832
2833 RS_Vector vStart(false), vEnd(false), vControl(false);
2834
2835 if (m_data.closed) {
2836 if (n < 3) {
2837 return;
2838 }
2839
2840 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2841 vControl = m_data.controlPoints.at(0);
2842 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2843
2844 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2845
2846 for (size_t i = 1; i < n - 1; i++) {
2847 vStart = vEnd;
2848 vControl = m_data.controlPoints.at(i);
2849 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2850
2851 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2852 }
2853
2854 vStart = vEnd;
2855 vControl = m_data.controlPoints.at(n - 1);
2856 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2857
2858 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2859 }
2860 else {
2861 vStart = m_data.controlPoints.at(0);
2862 vEnd = m_data.controlPoints.at(1);
2863 if (n < 3) {
2864 addLineQuadIntersect(sol, vStart, vEnd, x1, c1, x2);
2865 return;
2866 }
2867
2868 vControl = vEnd;
2869 vEnd = m_data.controlPoints.at(2);
2870 if (n < 4) {
2871 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2872 return;
2873 }
2874
2875 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2876 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2877
2878 for (size_t i = 2; i < n - 2; i++) {
2879 vStart = vEnd;
2880 vControl = m_data.controlPoints.at(i);
2881 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2882
2883 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2884 }
2885
2886 vStart = vEnd;
2887 vControl = m_data.controlPoints.at(n - 2);
2888 vEnd = m_data.controlPoints.at(n - 1);
2889
2890 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2891 }
2892}
2893
2894RS_VectorSolutions LC_SplinePoints::getSplinePointsIntersect(LC_SplinePoints* l1) const {
2895 RS_VectorSolutions ret;
2896
2897 size_t n = m_data.controlPoints.size();
2898 if (n < 2) {
2899 return ret;
2900 }
2901
2902 RS_Vector vStart(false), vEnd(false), vControl(false);
2903
2904 if (m_data.closed) {
2905 if (n < 3) {
2906 return ret;
2907 }
2908
2909 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2910 vControl = m_data.controlPoints.at(0);
2911 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2912
2913 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2914
2915 for (size_t i = 1; i < n - 1; i++) {
2916 vStart = vEnd;
2917 vControl = m_data.controlPoints.at(i);
2918 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2919
2920 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2921 }
2922
2923 vStart = vEnd;
2924 vControl = m_data.controlPoints.at(n - 1);
2925 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2926
2927 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2928 }
2929 else {
2930 vStart = m_data.controlPoints.at(0);
2931 vEnd = m_data.controlPoints.at(1);
2932 if (n < 3) {
2933 return l1->getLineIntersect(vStart, vEnd);
2934 }
2935
2936 vControl = vEnd;
2937 vEnd = m_data.controlPoints.at(2);
2938 if (n < 4) {
2939 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2940 return ret;
2941 }
2942
2943 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2944 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2945
2946 for (size_t i = 2; i < n - 2; i++) {
2947 vStart = vEnd;
2948 vControl = m_data.controlPoints.at(i);
2949 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2950
2951 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2952 }
2953
2954 vStart = vEnd;
2955 vControl = m_data.controlPoints.at(n - 2);
2956 vEnd = m_data.controlPoints.at(n - 1);
2957
2958 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2959 }
2960
2961 return ret;
2962}
2963
2964RS_VectorSolutions getQuadraticLineIntersect(const std::vector<double>& dQuadCoefs, const RS_Vector& vx1, const RS_Vector& vx2) {
2965 RS_VectorSolutions ret;
2966 if (dQuadCoefs.size() < 3) {
2967 return ret;
2968 }
2969
2970 const RS_Vector x1 = vx2 - vx1;
2971
2972 double a0 = 0.0;
2973 double a1 = 0.0;
2974 double a2 = 0.0;
2975
2976 if (dQuadCoefs.size() > 3) {
2977 a2 = dQuadCoefs[0] * x1.x * x1.x + dQuadCoefs[1] * x1.x * x1.y + dQuadCoefs[2] * x1.y * x1.y;
2978 a1 = 2.0 * (dQuadCoefs[0] * x1.x * vx1.x + dQuadCoefs[2] * x1.y * vx1.y) + dQuadCoefs[1] * (x1.x * vx1.y + x1.y * vx1.x) +
2979 dQuadCoefs[3] * x1.x + dQuadCoefs[4] * x1.y;
2980 a0 = dQuadCoefs[0] * vx1.x * vx1.x + dQuadCoefs[1] * vx1.x * vx1.y + dQuadCoefs[2] * vx1.y * vx1.y + dQuadCoefs[3] * vx1.x +
2981 dQuadCoefs[4] * vx1.y + dQuadCoefs[5];
2982 }
2983 else {
2984 a1 = dQuadCoefs[0] * x1.x + dQuadCoefs[1] * x1.y;
2985 a0 = dQuadCoefs[0] * vx1.x + dQuadCoefs[1] * vx1.y + dQuadCoefs[2];
2986 }
2987
2988 std::vector<double> dSol(0, 0.);
2989
2990 if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
2991 std::vector<double> dCoefs(2, 0.);
2992 dCoefs.push_back(a1 / a2);
2993 dCoefs.push_back(a0 / a2);
2994 dSol = RS_Math::quadraticSolver(dCoefs);
2995 }
2996 else if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
2997 dSol.push_back(-a0 / a1);
2998 }
2999
3000 for (double& d : dSol) {
3001 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
3002 d = qBound(0.0, d, 1.0);
3003 ret.push_back(vx1 * (1.0 - d) + vx2 * d);
3004 }
3005 }
3006
3007 return ret;
3008}
3009
3010void addQuadraticQuadIntersect(RS_VectorSolutions* pVS, const std::vector<double>& dQuadCoefs, const RS_Vector& vx1, const RS_Vector& vc1,
3011 const RS_Vector& vx2) {
3012 if (dQuadCoefs.size() < 3) {
3013 return;
3014 }
3015
3016 const RS_Vector x1 = vx2 - vc1 * 2.0 + vx1;
3017 const RS_Vector x2 = vc1 - vx1;
3018
3019 double a0 = 0.0;
3020 double a1 = 0.0;
3021 double a2 = 0.0;
3022 double a3 = 0.0;
3023 double a4 = 0.0;
3024
3025 if (dQuadCoefs.size() > 3) {
3026 a4 = dQuadCoefs[0] * x1.x * x1.x + dQuadCoefs[1] * x1.x * x1.y + dQuadCoefs[2] * x1.y * x1.y;
3027 a3 = 4.0 * dQuadCoefs[0] * x1.x * x2.x + 2.0 * dQuadCoefs[1] * (x1.x * x2.y + x1.y * x2.x) + 4.0 * dQuadCoefs[2] * x1.y * x2.y;
3028 a2 = dQuadCoefs[0] * (2.0 * x1.x * vx1.x + 4.0 * x2.x * x2.x) + dQuadCoefs[1] * (x1.x * vx1.y + x1.y * vx1.x + 4.0 * x2.x * x2.y) +
3029 dQuadCoefs[2] * (2.0 * x1.y * vx1.y + 4.0 * x2.y * x2.y) + dQuadCoefs[3] * x1.x + dQuadCoefs[4] * x1.y;
3030 a1 = 4.0 * (dQuadCoefs[0] * x2.x * vx1.x + dQuadCoefs[2] * x2.y * vx1.y) + 2.0 * (dQuadCoefs[1] * (x2.x * vx1.y + x2.y * vx1.x) +
3031 dQuadCoefs[3] * x2.x + dQuadCoefs[4] * x2.y);
3032 a0 = dQuadCoefs[0] * vx1.x * vx1.x + dQuadCoefs[1] * vx1.x * vx1.y + dQuadCoefs[2] * vx1.y * vx1.y + dQuadCoefs[3] * vx1.x +
3033 dQuadCoefs[4] * vx1.y + dQuadCoefs[5];
3034 }
3035 else {
3036 a2 = dQuadCoefs[0] * x1.x + dQuadCoefs[1] * x1.y;
3037 a1 = 2.0 * (dQuadCoefs[0] * x2.x + dQuadCoefs[1] * x2.y);
3038 a0 = dQuadCoefs[0] * vx1.x + dQuadCoefs[1] * vx1.y + dQuadCoefs[2];
3039 }
3040
3041 std::vector<double> dSol(0, 0.);
3042 std::vector<double> dCoefs(0, 0.);
3043
3044 if (std::abs(a4) > RS_TOLERANCE1.0e-10) {
3045 dCoefs.push_back(a3 / a4);
3046 dCoefs.push_back(a2 / a4);
3047 dCoefs.push_back(a1 / a4);
3048 dCoefs.push_back(a0 / a4);
3049 dSol = RS_Math::quarticSolver(dCoefs);
3050 }
3051 else if (std::abs(a3) > RS_TOLERANCE1.0e-10) {
3052 dCoefs.push_back(a2 / a3);
3053 dCoefs.push_back(a1 / a3);
3054 dCoefs.push_back(a0 / a3);
3055 dSol = RS_Math::cubicSolver(dCoefs);
3056 }
3057 else if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
3058 dCoefs.push_back(a1 / a2);
3059 dCoefs.push_back(a0 / a2);
3060 dSol = RS_Math::quadraticSolver(dCoefs);
3061 }
3062 else if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
3063 dSol.push_back(-a0 / a1);
3064 }
3065
3066 for (double& d : dSol) {
3067 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
3068 if (d < 0.0) {
3069 d = 0.0;
3070 }
3071 if (d > 1.0) {
3072 d = 1.0;
3073 }
3074 pVS->push_back(getQuadAtPoint(vx1, vc1, vx2, d));
3075 }
3076 }
3077}
3078
3079RS_VectorSolutions LC_SplinePoints::getQuadraticIntersect(const RS_Entity* e1) const {
3080 RS_VectorSolutions ret;
3081
3082 size_t n = m_data.controlPoints.size();
3083 if (n < 2) {
3084 return ret;
3085 }
3086
3087 LC_Quadratic lcQuad = e1->getQuadratic();
3088 std::vector<double> dQuadCoefs = lcQuad.getCoefficients();
3089
3090 RS_Vector vStart(false), vEnd(false), vControl(false);
3091
3092 if (m_data.closed) {
3093 if (n < 3) {
3094 return ret;
3095 }
3096
3097 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
3098 vControl = m_data.controlPoints.at(0);
3099 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
3100
3101 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3102
3103 for (size_t i = 1; i < n - 1; i++) {
3104 vStart = vEnd;
3105 vControl = m_data.controlPoints.at(i);
3106 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
3107
3108 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3109 }
3110
3111 vStart = vEnd;
3112 vControl = m_data.controlPoints.at(n - 1);
3113 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
3114
3115 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3116 }
3117 else {
3118 vStart = m_data.controlPoints.at(0);
3119 vEnd = m_data.controlPoints.at(1);
3120 if (n < 3) {
3121 return getQuadraticLineIntersect(dQuadCoefs, vStart, vEnd);
3122 }
3123
3124 vControl = vEnd;
3125 vEnd = m_data.controlPoints.at(2);
3126 if (n < 4) {
3127 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3128 return ret;
3129 }
3130
3131 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
3132 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3133
3134 for (size_t i = 2; i < n - 2; i++) {
3135 vStart = vEnd;
3136 vControl = m_data.controlPoints.at(i);
3137 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
3138
3139 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3140 }
3141
3142 vStart = vEnd;
3143 vControl = m_data.controlPoints.at(n - 2);
3144 vEnd = m_data.controlPoints.at(n - 1);
3145
3146 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3147 }
3148
3149 return ret;
3150}
3151
3152RS_VectorSolutions LC_SplinePoints::getIntersection(const RS_Entity* e1, const RS_Entity* e2) {
3153 if (e2 == nullptr || (e1 != nullptr && e1->rtti() != RS2::EntitySplinePoints)) {
3154 std::swap(e1, e2);
3155 }
3156 if (e1 == nullptr || e1->rtti() != RS2::EntitySplinePoints) {
3157 return {};
3158 }
3159
3160 const auto spline = static_cast<LC_SplinePoints*>(const_cast<RS_Entity*>(e1));
3161
3162 switch (e2->rtti()) {
3163 case RS2::EntityLine:
3164 return {spline->getLineIntersect(e2->getStartpoint(), e2->getEndpoint())};
3165 case RS2::EntitySplinePoints:
3166 return {spline->getSplinePointsIntersect(static_cast<LC_SplinePoints*>(const_cast<RS_Entity*>(e2)))};
3167 default:
3168 return {spline->getQuadraticIntersect(e2)};
3169 }
3170}
3171
3172RS2::EntityType LC_SplinePoints::rtti() const {
3173 return RS2::EntitySplinePoints;
3174}
3175
3176/** @return false */
3177bool LC_SplinePoints::isEdge() const {
3178 return true;
3179}
3180
3181/** @return Copy of m_data that defines the spline. */
3182const LC_SplinePointsData& LC_SplinePoints::getData() const {
3183 return m_data;
3184}
3185
3186/** @return Copy of m_data that defines the spline. */
3187LC_SplinePointsData& LC_SplinePoints::getData() {
3188 return m_data;
3189}
3190
3191/** @return Number of control points. */
3192size_t LC_SplinePoints::getNumberOfControlPoints() const {
3193 return m_data.controlPoints.size();
3194}
3195
3196/**
3197* @retval true if the spline is closed.
3198* @retval false otherwise.
3199*/
3200bool LC_SplinePoints::isClosed() const {
3201 return m_data.closed;
3202}
3203
3204/**
3205* Sets the closed flag of this spline.
3206*/
3207void LC_SplinePoints::setClosed(const bool c) {
3208 m_data.closed = c;
3209 update();
3210}
3211
3212/*void LC_SplinePoints::trimStartpoint(const RS_Vector& pos)
3213{
3214}
3215
3216void LC_SplinePoints::trimEndpoint(const RS_Vector& pos)
3217{
3218}*/
3219
3220LC_SplinePoints* LC_SplinePoints::cut(const RS_Vector& pos) {
3221 LC_SplinePoints* ret = nullptr;
3222
3223 double dt;
3224 const int iQuad = getNearestQuad(pos, nullptr, &dt);
3225 if (iQuad < 1) {
3226 return ret;
3227 }
3228
3229 RS_Vector vStart(false);
3230 RS_Vector vControl(false);
3231 RS_Vector vEnd(false);
3232
3233 const int iPts = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
3234 if (iPts < 2) {
3235 return ret;
3236 }
3237
3238 RS_Vector vPoint(false);
3239 if (iPts < 3) {
3240 vPoint = vStart * (1.0 - dt) + vEnd * dt;
3241 }
3242 else {
3243 vPoint = getQuadPoint(vStart, vControl, vEnd, dt);
3244 }
3245
3246 const size_t n = m_data.controlPoints.size();
3247
3248 RS_Vector vNewControl(false);
3249 if (m_data.closed) {
3250 // if the spline is closed, we must delete splinePoints, add the pos
3251 // as start and end point and reorder control points. We must return
3252 // nullptr since there will still be only one spline
3253 for (int i = 0; i < iQuad - 1; i++) {
3254 vNewControl = m_data.controlPoints.front();
3255 m_data.controlPoints.erase(m_data.controlPoints.begin());
3256 m_data.controlPoints.push_back(vNewControl);
3257 }
3258
3259 if (iPts > 2) {
3260 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, 0.0, dt);
3261 m_data.controlPoints.push_back(vNewControl);
3262
3263 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, dt, 1.0);
3264 m_data.controlPoints.front() = vNewControl;
3265 }
3266 m_data.controlPoints.push_back(vPoint);
3267 m_data.controlPoints.insert(m_data.controlPoints.begin(), vPoint);
3268
3269 m_data.closed = false;
3270 m_data.cut = true;
3271 }
3272 else {
3273 LC_SplinePointsData newData(false, true);
3274 for (size_t i = iQuad + 1; i < n; i++) {
3275 newData.controlPoints.push_back(m_data.controlPoints.at(iQuad + 1));
3276 m_data.controlPoints.erase(m_data.controlPoints.begin() + iQuad + 1);
3277 }
3278
3279 if (iPts > 2) {
3280 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, 0.0, dt);
3281 m_data.controlPoints[iQuad] = vNewControl;
3282
3283 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, dt, 1.0);
3284 newData.controlPoints.insert(newData.controlPoints.begin(), vNewControl);
3285 }
3286 m_data.controlPoints.push_back(vPoint);
3287 newData.controlPoints.insert(newData.controlPoints.begin(), vPoint);
3288
3289 ret = new LC_SplinePoints(m_parent, newData);
3290
3291 m_data.cut = true;
3292 }
3293
3294 return ret;
3295}
3296
3297QPolygonF LC_SplinePoints::getBoundingRect(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) {
3298 QPolygonF ret;
3299 ret << QPointF(x1.x, x1.y);
3300 //find t for tangent in parallel with x2 - x1
3301 const RS_Vector pt = (x1 - c1 * 2. + x2) * 2.;
3302 const RS_Vector pl = (x1 - x2);
3303 const double determinant = pt.x * pl.y - pt.y * pl.x;
3304 if (std::abs(determinant) < RS_TOLERANCE151.5e-15) {
3305 //bezier is a straight line
3306 ret << QPointF(x2.x, x2.y) << ret.front();
3307 return ret;
3308 }
3309 const RS_Vector pc = (x1 - c1) * 2.;
3310 const double t = (pc.x * pl.y - pc.y * pl.x) / determinant;
3311 const double tr = 1. - t;
3312 //offset from x1 to the extreme point
3313 const RS_Vector pext = x1 * (tr * tr - 1) + c1 * (2. * t * tr) + x2 * (t * t);
3314 //perpendicular offset from x1 to the extreme point, the component of the offset perpendicular to x1-x2
3315 const RS_Vector dp = pext - pl * (pext.dotP(pl) / pl.squared());
3316 RS_Vector v1 = x1 + dp;
3317 ret << QPointF(v1.x, v1.y);
3318 v1 = x2 + dp;
3319 ret << QPointF(v1.x, v1.y);
3320 ret << ret.front();
3321 return ret;
3322}
3323
3324/**
3325 * @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
3326 * Contour Area = \oint x dy
3327 * @return line integral \oint x dy along the spline entity
3328 * @author Dongxu Li
3329 */
3330double LC_SplinePoints::areaLineIntegral() const {
3331 size_t n = m_data.controlPoints.size();
3332 if (n < 2) {
3333 return 0.0;
3334 }
3335
3336 auto quadAreaIntegral = [](const RS_Vector& p0, const RS_Vector& p1, const RS_Vector& p2) -> double {
3337 const double x0 = p0.x;
3338 const double y0 = p0.y;
3339 const double x1 = p1.x;
3340 const double y1 = p1.y;
3341 const double x2 = p2.x;
3342 const double y2 = p2.y;
3343
3344 const double A = 2.0 * (y1 - y0);
3345 const double B = 2.0 * (y2 - 2.0 * y1 + y0);
3346 const double C = x0;
3347 const double D = 2.0 * (x1 - x0);
3348 const double E = x0 - 2.0 * x1 + x2;
3349
3350 const double int1 = C * A;
3351 const double int2 = (C * B + D * A) / 2.0;
3352 const double int3 = (D * B + E * A) / 3.0;
3353 const double int4 = E * B / 4.0;
3354
3355 return int1 + int2 + int3 + int4;
3356 };
3357
3358 double res = 0.0;
3359 RS_Vector vStart, vControl, vEnd;
3360
3361 if (!m_data.closed) {
3362 if (n == 2) {
3363 RS_Vector s = getStartpoint();
3364 RS_Vector e = getEndpoint();
3365 return (s.x + e.x) / 2.0 * (e.y - s.y);
3366 }
3367
3368 // n >= 3
3369 vStart = m_data.controlPoints[0];
3370 vControl = m_data.controlPoints[1];
3371 if (n == 3) {
3372 vEnd = m_data.controlPoints[2];
3373 return quadAreaIntegral(vStart, vControl, vEnd);
3374 }
3375
3376 vEnd = (m_data.controlPoints[1] + m_data.controlPoints[2]) / 2.0;
3377 res += quadAreaIntegral(vStart, vControl, vEnd);
3378
3379 for (size_t i = 2; i < n - 2; ++i) {
3380 vStart = vEnd;
3381 vControl = m_data.controlPoints[i];
3382 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i + 1]) / 2.0;
3383 res += quadAreaIntegral(vStart, vControl, vEnd);
3384 }
3385
3386 vStart = vEnd;
3387 vControl = m_data.controlPoints[n - 2];
3388 vEnd = m_data.controlPoints[n - 1];
3389 res += quadAreaIntegral(vStart, vControl, vEnd);
3390 }
3391 else {
3392 // closed, n >= 3
3393 if (n < 3) {
3394 return 0.0;
3395 }
3396
3397 vStart = (m_data.controlPoints[n - 1] + m_data.controlPoints[0]) / 2.0;
3398 vControl = m_data.controlPoints[0];
3399 vEnd = (m_data.controlPoints[0] + m_data.controlPoints[1]) / 2.0;
3400 res += quadAreaIntegral(vStart, vControl, vEnd);
3401
3402 for (size_t i = 1; i < n - 1; ++i) {
3403 vStart = vEnd;
3404 vControl = m_data.controlPoints[i];
3405 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i + 1]) / 2.0;
3406 res += quadAreaIntegral(vStart, vControl, vEnd);
3407 }
3408
3409 vStart = vEnd;
3410 vControl = m_data.controlPoints[n - 1];
3411 vEnd = (m_data.controlPoints[n - 1] + m_data.controlPoints[0]) / 2.0;
3412 res += quadAreaIntegral(vStart, vControl, vEnd);
3413 }
3414
3415 return res;
3416}
3417
3418LC_SecondMoment LC_SplinePoints::secondMomentLineIntegral() const {
3419 // 5-point Gauss-Legendre quadrature on [0,1] — exact for polynomial degree ≤ 9.
3420 // Each quadratic Bézier segment has degree-7 integrands (x³y'/3 etc.),
3421 // so this gives the exact result per segment.
3422 static constexpr double t5[] = {
3423 0.04691007703067, 0.23076534494716, 0.5,
3424 0.76923465505284, 0.95308992296933
3425 };
3426 static constexpr double w5[] = {
3427 0.11846344252810, 0.23931433524969, 0.28444444444444,
3428 0.23931433524969, 0.11846344252810
3429 };
3430
3431 // Helper: integrate second moments over one quadratic Bézier segment P0,P1,P2
3432 auto segMoment = [&](const RS_Vector& p0, const RS_Vector& p1, const RS_Vector& p2) -> LC_SecondMoment {
3433 LC_SecondMoment m;
3434 for (int i = 0; i < 5; ++i) {
3435 const double t = t5[i];
3436 const double mt = 1.0 - t;
3437 const double x = mt*mt*p0.x + 2.0*mt*t*p1.x + t*t*p2.x;
3438 const double y = mt*mt*p0.y + 2.0*mt*t*p1.y + t*t*p2.y;
3439 const double dxdt = 2.0*(mt*(p1.x-p0.x) + t*(p2.x-p1.x));
3440 const double dydt = 2.0*(mt*(p1.y-p0.y) + t*(p2.y-p1.y));
3441 m.ixx += w5[i] * x*x*x / 3.0 * dydt;
3442 m.iyy += w5[i] * (-y*y*y / 3.0) * dxdt;
3443 m.ixy += w5[i] * x*x * y / 2.0 * dydt;
3444 }
3445 return m;
3446 };
3447
3448 // Replicate the segment decomposition from areaLineIntegral()
3449 size_t n = m_data.controlPoints.size();
3450 if (n < 2) {
3451 return {};
3452 }
3453
3454 LC_SecondMoment res;
3455 RS_Vector vStart, vControl, vEnd;
3456
3457 if (!m_data.closed) {
3458 if (n == 2) {
3459 // Degenerate: straight line
3460 RS_Vector s = getStartpoint();
3461 RS_Vector e = getEndpoint();
3462 RS_Line line(nullptr, RS_LineData{s, e});
3463 return line.secondMomentLineIntegral();
3464 }
3465 vStart = m_data.controlPoints[0];
3466 vControl = m_data.controlPoints[1];
3467 if (n == 3) {
3468 vEnd = m_data.controlPoints[2];
3469 return segMoment(vStart, vControl, vEnd);
3470 }
3471 vEnd = (m_data.controlPoints[1] + m_data.controlPoints[2]) / 2.0;
3472 res += segMoment(vStart, vControl, vEnd);
3473
3474 for (size_t i = 2; i < n - 2; ++i) {
3475 vStart = vEnd;
3476 vControl = m_data.controlPoints[i];
3477 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i+1]) / 2.0;
3478 res += segMoment(vStart, vControl, vEnd);
3479 }
3480 vStart = vEnd;
3481 vControl = m_data.controlPoints[n-2];
3482 vEnd = m_data.controlPoints[n-1];
3483 res += segMoment(vStart, vControl, vEnd);
3484 } else {
3485 if (n < 3) {
3486 return {};
3487 }
3488 vStart = (m_data.controlPoints[n-1] + m_data.controlPoints[0]) / 2.0;
3489 vControl = m_data.controlPoints[0];
3490 vEnd = (m_data.controlPoints[0] + m_data.controlPoints[1]) / 2.0;
3491 res += segMoment(vStart, vControl, vEnd);
3492
3493 for (size_t i = 1; i < n - 1; ++i) {
3494 vStart = vEnd;
3495 vControl = m_data.controlPoints[i];
3496 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i+1]) / 2.0;
3497 res += segMoment(vStart, vControl, vEnd);
3498 }
3499 vStart = vEnd;
3500 vControl = m_data.controlPoints[n-1];
3501 vEnd = (m_data.controlPoints[n-1] + m_data.controlPoints[0]) / 2.0;
3502 res += segMoment(vStart, vControl, vEnd);
3503 }
3504 return res;
3505}