Bug Summary

File:librecad/src/lib/engine/document/entities/lc_splinepoints.cpp
Warning:line 1915, 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-607-g330d41cec -D LC_PRERELEASE=true; -D QT_NO_DEBUG -D QT_SVG_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_NETWORK_LIB -D QT_CORE_LIB -I . -I /usr/include -I ../../libraries/lciconengine -I ../../libraries/libdxfrw/src -I ../../libraries/jwwlib/src -I ../../libraries/shapelib/src -I cmd -I lib/actions -I lib/actions/visual_snap -I lib/actions/options -I lib/creation -I lib/debug -I lib/engine -I lib/engine/document -I lib/engine/document/blocks -I lib/engine/document/container -I lib/engine/document/dimstyles -I lib/engine/document/entities -I lib/engine/document/entities/support -I lib/engine/document/fonts -I lib/engine/document/io -I lib/engine/document/layers -I lib/engine/document/patterns -I lib/engine/document/selection -I lib/engine/document/textstyles -I lib/engine/document/ucs -I lib/engine/document/variables -I lib/engine/document/views -I lib/engine/clipboard -I lib/engine/overlays -I lib/engine/overlays/angles_base -I lib/engine/overlays/highlight -I lib/engine/overlays/preview -I lib/engine/overlays/references -I lib/engine/overlays/crosshair -I lib/engine/overlays/info_cursor -I lib/engine/overlays/overlay_box -I lib/engine/overlays/ucs_mark -I lib/engine/undo -I lib/engine/utils -I lib/engine/settings -I lib/fileio -I lib/filters -I lib/generators -I lib/generators/makercamsvg -I lib/generators/layers -I lib/generators/image -I lib/gui -I lib/gui/grid -I lib/gui/render -I lib/gui/render/headless -I lib/gui/render/widget -I lib/information -I lib/math -I lib/modification -I lib/selection -I lib/selection/metaentity -I lib/selection/metaentity/entities -I lib/printing -I lib/properties -I actions -I actions/dock_widgets -I actions/dock_widgets/block -I actions/dock_widgets/entity_info -I actions/dock_widgets/layer -I actions/dock_widgets/library -I actions/dock_widgets/ucs_list -I actions/drawing -I actions/drawing/draw -I actions/drawing/draw/arc -I actions/drawing/draw/circle -I actions/drawing/draw/curve -I actions/drawing/draw/spline -I actions/drawing/draw/dimensions -I actions/drawing/draw/ellipse -I actions/drawing/draw/hatch -I actions/drawing/draw/image -I actions/drawing/draw/line -I actions/drawing/draw/point -I actions/drawing/draw/rect -I actions/drawing/draw/polygon -I actions/drawing/draw/misc -I actions/drawing/draw/line/shapes -I actions/drawing/draw/line/misc -I actions/drawing/draw/line/shapes/rect -I actions/drawing/draw/line/shapes/polygon -I actions/drawing/draw/polyline -I actions/drawing/draw/text -I actions/drawing/edit -I actions/drawing/info -I actions/drawing/pick -I actions/drawing/modify -I actions/drawing/pen -I actions/drawing/rel_zero -I actions/drawing/selection -I actions/drawing/snap -I actions/drawing/zoom -I actions/file -I actions/options -I actions/print_preview -I ui -I ui/action_options -I ui/action_options/circle -I ui/action_options/curve -I ui/action_options/spline -I ui/action_options/dimensions -I ui/action_options/edit -I ui/action_options/image -I ui/action_options/info -I ui/action_options/insert -I ui/action_options/line -I ui/action_options/rect -I ui/action_options/polygon -I ui/action_options/misc -I ui/action_options/modify -I ui/action_options/ellipse -I ui/action_options/other -I ui/action_options/polyline -I ui/action_options/point -I ui/action_options/print_preview -I ui/action_options/selection -I ui/action_options/snap -I ui/action_options/text -I ui/actions -I ui/components -I ui/components/relative_position_assistant -I ui/components/comboboxes -I ui/components/containers -I ui/components/creators -I ui/components/layouts -I ui/components/pen -I ui/components/status_bar -I ui/components/toolbars -I ui/components/utils -I ui/dialogs -I ui/dialogs/actions -I ui/dialogs/actions/modify -I ui/dialogs/actions/quick_selection -I ui/dialogs/modify -I ui/dialogs/entity -I ui/dialogs/creators -I ui/dialogs/file -I ui/dialogs/file/export -I ui/dialogs/file/export/layers -I ui/dialogs/file/export/image -I ui/dialogs/file/export/makercam -I ui/dialogs/main -I ui/dialogs/settings -I ui/dialogs/settings/dimstyles -I ui/dialogs/settings/dimstyles/dimstyle_manager -I ui/dialogs/settings/dimstyles/dimstyle_manager/support -I ui/dialogs/settings/options_device -I ui/dialogs/settings/options_drawing -I ui/dialogs/settings/options_general -I ui/dialogs/settings/options_widget -I ui/dialogs/settings/shortcuts -I ui/dock_widgets -I ui/dock_widgets/block_widget -I ui/dock_widgets/command_line -I ui/dock_widgets/entity_info -I ui/dock_widgets/layer_widget -I ui/dock_widgets/layers_tree -I ui/dock_widgets/library_widget -I ui/dock_widgets/pen_palette -I ui/dock_widgets/pen_wizard -I ui/dock_widgets/property_sheet -I ui/dock_widgets/property_sheet/lib -I ui/dock_widgets/property_sheet/lib/properties -I ui/dock_widgets/property_sheet/lib/view -I ui/dock_widgets/property_sheet/lib/view/edit -I ui/dock_widgets/property_sheet/lib/widgets -I ui/dock_widgets/property_sheet/lib/widgets/sheet -I ui/dock_widgets/property_sheet/metaentity -I ui/dock_widgets/property_sheet/metaentity/entities -I ui/dock_widgets/property_sheet/metaentity/entities/document -I ui/dock_widgets/property_sheet/properties -I ui/dock_widgets/property_sheet/properties/action -I ui/dock_widgets/property_sheet/properties/bool -I ui/dock_widgets/property_sheet/properties/color -I ui/dock_widgets/property_sheet/properties/double -I ui/dock_widgets/property_sheet/properties/enum -I ui/dock_widgets/property_sheet/properties/int -I ui/dock_widgets/property_sheet/properties/layer -I ui/dock_widgets/property_sheet/properties/linetype -I ui/dock_widgets/property_sheet/properties/linewidth -I ui/dock_widgets/property_sheet/properties/rect -I ui/dock_widgets/property_sheet/properties/rsvector -I ui/dock_widgets/property_sheet/properties/string -I ui/dock_widgets/views_list -I ui/dock_widgets/ucs_list -I ui/dock_widgets/workspaces -I ui/dock_widgets/cad -I ui/main -I ui/main/init -I ui/main/persistence -I ui/main/release_check -I ui/main/support -I ui/main/fontviewer -I ui/main/workspaces -I ui/view -I main -I main/console_dxf2pdf -I test -I plugins -I ../res -I ../res/arrows -I ../res/controls -I ../res/dxf -I ../res/gdt -I ../res/icons -I ../res/images -I ../../../Qt/6.9.0/gcc_64/include -I ../../../Qt/6.9.0/gcc_64/include/QtSvg -I ../../../Qt/6.9.0/gcc_64/include/QtPrintSupport -I ../../../Qt/6.9.0/gcc_64/include/QtWidgets -I ../../../Qt/6.9.0/gcc_64/include/QtGui -I ../../../Qt/6.9.0/gcc_64/include/QtNetwork -I ../../../Qt/6.9.0/gcc_64/include/QtCore -I ../../generated/librecad/moc -I ../../generated/librecad/ui -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-08-04-154929-5069-1 -x c++ lib/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 m_minV = RS_Vector(false);
548 m_maxV = RS_Vector(false);
549
550 const size_t n = m_data.controlPoints.size();
551 if (n < 1) {
552 return;
553 }
554
555 RS_Vector vStart(false);
556 RS_Vector vControl(false);
557 RS_Vector vEnd(false);
558
559 if (m_data.closed) {
560 if (n < 3) {
561 return;
562 }
563
564 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
565 vControl = m_data.controlPoints.at(0);
566 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
567 updateQuadExtentUI(vStart, vControl, vEnd);
568
569 for (size_t i = 1; i < n - 1; ++i) {
570 vStart = vEnd;
571 vControl = m_data.controlPoints.at(i);
572 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
573 updateQuadExtentUI(vStart, vControl, vEnd);
574 }
575
576 vStart = vEnd;
577 vControl = m_data.controlPoints.at(n - 1);
578 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
579 updateQuadExtentUI(vStart, vControl, vEnd);
580 }
581 else {
582 vStart = m_data.controlPoints.at(0);
583 m_minV = vStart;
584 m_maxV = vStart;
585
586 if (n < 2) {
587 return;
588 }
589
590 vEnd = m_data.controlPoints.at(1);
591
592 if (n < 3) {
593 m_minV = RS_Vector::minimum(vEnd, m_minV);
594 m_maxV = RS_Vector::maximum(vEnd, m_maxV);
595 return;
596 }
597
598 vControl = vEnd;
599 vEnd = m_data.controlPoints.at(2);
600
601 if (n < 4) {
602 updateQuadExtentUI(vStart, vControl, vEnd);
603 return;
604 }
605
606 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
607 updateQuadExtentUI(vStart, vControl, vEnd);
608
609 for (size_t i = 2; i < n - 2; ++i) {
610 vStart = vEnd;
611 vControl = m_data.controlPoints.at(i);
612 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
613 updateQuadExtentUI(vStart, vControl, vEnd);
614 }
615
616 vStart = vEnd;
617 vControl = m_data.controlPoints.at(n - 2);
618 vEnd = m_data.controlPoints.at(n - 1);
619 updateQuadExtentUI(vStart, vControl, vEnd);
620 }
621 updateLength();
622}
623
624RS_VectorSolutions LC_SplinePoints::getRefPoints() const {
625 if (m_data.cut) {
626 return {{m_data.controlPoints.begin(), m_data.controlPoints.end()}};
627 }
628 return {{m_data.splinePoints.begin(), m_data.splinePoints.end()}};
629}
630
631/** @return Start point of the entity */
632RS_Vector LC_SplinePoints::getStartpoint() const {
633 if (m_data.closed) {
634 return RS_Vector(false);
635 }
636
637 const std::vector<RS_Vector>& pts = getPoints();
638 const size_t iCount = pts.size();
639 if (iCount < 1) {
640 return RS_Vector(false);
641 }
642 return pts.at(0);
643}
644
645/** @return End point of the entity */
646RS_Vector LC_SplinePoints::getEndpoint() const {
647 if (m_data.closed) {
648 return RS_Vector(false);
649 }
650
651 const std::vector<RS_Vector>& pts = getPoints();
652 const size_t iCount = pts.size();
653
654 return (iCount < 1) ? RS_Vector{false} : pts.at(iCount - 1);
655}
656
657RS_Vector LC_SplinePoints::doGetNearestEndpoint(const RS_Vector& coord, double* dist, RS_Entity** entity) const {
658 double minDist = RS_MAXDOUBLE1.0E+10;
659 RS_Vector ret(false);
660 if (!m_data.closed) // no endpoint for closed spline
661 {
662 const RS_Vector vp1(getStartpoint());
663 const RS_Vector vp2(getEndpoint());
664 const double d1 = (coord - vp1).squared();
665 const double d2 = (coord - vp2).squared();
666 if (d1 < d2) {
667 ret = vp1;
668 minDist = std::sqrt(d1);
669 }
670 else {
671 ret = vp2;
672 minDist = std::sqrt(d2);
673 }
674 }
675 if (dist != nullptr) {
676 *dist = minDist;
677 }
678 if (entity != nullptr) {
679 *entity = const_cast<LC_SplinePoints*>(this);
680 }
681 return ret;
682}
683
684// returns true if pvControl is set
685int LC_SplinePoints::getQuadPoints(const int iSeg, RS_Vector* pvStart, RS_Vector* pvControl, RS_Vector* pvEnd) const {
686 const size_t n = m_data.controlPoints.size();
687
688 size_t i1 = iSeg - 1;
689 size_t i2 = iSeg;
690 size_t i3 = iSeg + 1;
691
692 if (m_data.closed) {
693 if (n < 3) {
694 return 0;
695 }
696
697 i1 = (i1 + n - 1) % n;
698 i2--;
699 i3 = (i3 + n - 1) % n;
700
701 *pvStart = (m_data.controlPoints.at(i1) + m_data.controlPoints.at(i2)) / 2.0;
702 *pvControl = m_data.controlPoints.at(i2);
703 *pvEnd = (m_data.controlPoints.at(i2) + m_data.controlPoints.at(i3)) / 2.0;
704 }
705 else {
706 if (iSeg < 1) {
707 return 0;
708 }
709 if (n < 1) {
710 return 0;
711 }
712
713 *pvStart = m_data.controlPoints.at(0);
714
715 if (n < 2) {
716 return 1;
717 }
718
719 *pvEnd = m_data.controlPoints.at(1);
720
721 if (n < 3) {
722 return 2;
723 }
724
725 *pvControl = *pvEnd;
726 *pvEnd = m_data.controlPoints.at(2);
727
728 if (n < 4) {
729 return 3;
730 }
731
732 if (i1 < 1) {
733 *pvStart = m_data.controlPoints.at(0);
734 }
735 else {
736 *pvStart = (m_data.controlPoints.at(i1) + m_data.controlPoints.at(i2)) / 2.0;
737 }
738 *pvControl = m_data.controlPoints.at(i2);
739 if (i3 > n - 2) {
740 *pvEnd = m_data.controlPoints.at(n - 1);
741 }
742 else {
743 *pvEnd = (m_data.controlPoints.at(i2) + m_data.controlPoints.at(i3)) / 2.0;
744 }
745 }
746
747 return 3;
748}
749
750// returns the index to the nearest segment, dt holds the t parameter
751// we will make an extrodrinary exception here and make the index 1-based
752// return values:
753// -1: no segment found
754// 0: segment is one point only
755// >0: index to then non-degenerated segment, depends on closed flag
756int LC_SplinePoints::getNearestQuad(const RS_Vector& coord, double* dist, double* dt) const {
757 size_t n = m_data.controlPoints.size();
758
759 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
760
761 double dDist = 0., dNewDist = 0.;
762 double dRes, dNewRes;
763 int iRes = -1;
764
765 if (m_data.closed) {
8
Assuming field 'closed' is false
9
Taking false branch
766 if (n < 3) {
767 return -1;
768 }
769
770 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
771 vControl = m_data.controlPoints.at(0);
772 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
773
774 dRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
775 iRes = 1;
776
777 for (size_t i = 1; i < n - 1; ++i) {
778 vStart = vEnd;
779 vControl = m_data.controlPoints.at(i);
780 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
781
782 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
783 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
784 iRes = i + 1;
785 }
786 }
787
788 vStart = vEnd;
789 vControl = m_data.controlPoints.at(n - 1);
790 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
791
792 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
793 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
794 iRes = n;
795 }
796 }
797 else {
798 if (n < 1) {
10
Assuming 'n' is >= 1
11
Taking false branch
799 return -1;
800 }
801
802 vStart = m_data.controlPoints.at(0);
803
804 if (n < 2) {
12
Assuming 'n' is < 2
13
Taking true branch
805 if (dist != nullptr) {
14
Taking false branch
806 *dist = (coord - vStart).magnitude();
807 }
808 return 0;
15
Returning without writing to '*dt'
809 }
810
811 vEnd = m_data.controlPoints.at(1);
812
813 if (n < 3) {
814 *dt = getDistToLine(coord, vStart, vEnd, &dDist);
815 if (dist != nullptr) {
816 *dist = std::sqrt(dDist);
817 }
818 return 1;
819 }
820
821 vControl = vEnd;
822 vEnd = m_data.controlPoints.at(2);
823
824 if (n < 4) {
825 *dt = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
826 if (dist != nullptr) {
827 *dist = std::sqrt(dDist);
828 }
829 return 1;
830 }
831
832 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
833
834 dRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dDist);
835 iRes = 1;
836
837 for (size_t i = 2; i < n - 2; i++) {
838 vStart = vEnd;
839 vControl = m_data.controlPoints.at(i);
840 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
841
842 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
843 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
844 iRes = i;
845 }
846 }
847
848 vStart = vEnd;
849 vControl = m_data.controlPoints.at(n - 2);
850 vEnd = m_data.controlPoints.at(n - 1);
851
852 dNewRes = getDistToQuadSquared(coord, vStart, vControl, vEnd, &dNewDist);
853 if (setNewDist(true, dNewDist, dNewRes, &dDist, &dRes)) {
854 iRes = n - 2;
855 }
856 }
857
858 *dt = dRes;
859 if (dist != nullptr) {
860 *dist = std::sqrt(dDist);
861 }
862 return iRes;
863}
864
865RS_Vector LC_SplinePoints::doGetNearestPointOnEntity(const RS_Vector& coord, [[maybe_unused]] bool onEntity, double* dist,
866 RS_Entity** entity) const {
867 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
868
869 double dt = 0.0;
870 const int iQuad = getNearestQuad(coord, dist, &dt);
871
872 if (iQuad < 0) {
873 return vRes;
874 }
875
876 const int n = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
877
878 if (n < 1) {
879 return vRes;
880 }
881
882 if (n < 2) {
883 vRes = vStart;
884 }
885 else if (n < 3) {
886 vRes = vStart * (1.0 - dt) + vEnd * dt;
887 }
888 else {
889 vRes = getQuadAtPoint(vStart, vControl, vEnd, dt);
890 }
891
892 if (entity != nullptr) {
893 *entity = const_cast<LC_SplinePoints*>(this);
894 }
895 return vRes;
896}
897
898double LC_SplinePoints::doGetDistanceToPoint(const RS_Vector& coord, RS_Entity** entity, [[maybe_unused]] RS2::ResolveLevel level,
899 [[maybe_unused]] double solidDist) const {
900 double dDist = RS_MAXDOUBLE1.0E+10;
901 getNearestPointOnEntity(coord, true, &dDist, entity);
902 return dDist;
903}
904
905//RS_Vector LC_SplinePoints::getNearestCenter(const RS_Vector& /*coord*/,
906// double* dist) const
907//{
908// if(dist != nullptr)
909// {
910// *dist = RS_MAXDOUBLE;
911// }
912
913// return RS_Vector(false);
914//}
915
916RS_Vector LC_SplinePoints::getSplinePointAtDist(double dDist, const int iStartSeg, const double dStartT, int* piSeg, double* pdt) const {
917 RS_Vector vRes(false);
918 if (m_data.closed) {
919 return vRes;
920 }
921
922 RS_Vector vStart(false), vControl(false), vEnd(false);
923
924 const size_t n = m_data.controlPoints.size();
925 size_t i = iStartSeg;
926
927 getQuadPoints(i, &vStart, &vControl, &vEnd);
928 double dQuadDist = getQuadLength(vStart, vControl, vEnd, dStartT, 1.0);
929 i++;
930
931 while (dDist > dQuadDist && i < n - 2) {
932 dDist -= dQuadDist;
933 vStart = vEnd;
934 vControl = m_data.controlPoints.at(i);
935 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
936 dQuadDist = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
937 i++;
938 }
939
940 if (dDist > dQuadDist) {
941 dDist -= dQuadDist;
942 vStart = vEnd;
943 vControl = m_data.controlPoints.at(n - 2);
944 vEnd = m_data.controlPoints.at(n - 1);
945 dQuadDist = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
946 i++;
947 }
948
949 if (dDist <= dQuadDist) {
950 double t0{0.0};
951 if (static_cast<size_t>(iStartSeg + 1) == i) {
952 t0 = dStartT;
953 }
954 const double dt = getQuadPointAtDist(vStart, vControl, vEnd, t0, dDist);
955 vRes = getQuadPoint(vStart, vControl, vEnd, dt);
956 *piSeg = i - 1;
957 *pdt = dt;
958 }
959
960 return vRes;
961}
962
963RS_Vector LC_SplinePoints::doGetNearestMiddle(const RS_Vector& coord, double* dist, const int middlePoints) const {
964 if (dist != nullptr) {
965 *dist = RS_MAXDOUBLE1.0E+10;
966 }
967 RS_Vector vStart(false), vControl(false), vEnd(false), vNext(false), vRes(false);
968
969 if (middlePoints < 1) {
970 return vRes;
971 }
972 if (m_data.closed) {
973 return vRes;
974 }
975
976 const size_t n = m_data.controlPoints.size();
977
978 if (n < 1) {
979 return vRes;
980 }
981
982 vStart = m_data.controlPoints.at(0);
983
984 if (n < 2) {
985 if (dist != nullptr) {
986 *dist = (vStart - coord).magnitude();
987 }
988 return vStart;
989 }
990
991 vEnd = m_data.controlPoints.at(1);
992
993 if (n < 3) {
994 return getNearestMiddleLine(vStart, vEnd, coord, dist, middlePoints);
995 }
996
997 double dCurDist = 0., dt = 0.;
998 double dMinDist = RS_MAXDOUBLE1.0E+10;
999 const double dDist = getLength() / (1.0 + middlePoints);
1000
1001 vControl = vEnd;
1002 vEnd = m_data.controlPoints.at(2);
1003
1004 if (n < 4) {
1005 dt = getQuadPointAtDist(vStart, vControl, vEnd, 0.0, dDist);
1006 vRes = getQuadPoint(vStart, vControl, vEnd, dt);
1007 dMinDist = (vRes - coord).magnitude();
1008 for (int j = 1; j < middlePoints; j++) {
1009 dt = getQuadPointAtDist(vStart, vControl, vEnd, dt, dDist);
1010 vNext = getQuadPoint(vStart, vControl, vEnd, dt);
1011 dCurDist = (vNext - coord).magnitude();
1012
1013 if (dCurDist < dMinDist) {
1014 dMinDist = dCurDist;
1015 vRes = vNext;
1016 }
1017 }
1018
1019 if (dist != nullptr) {
1020 *dist = dMinDist;
1021 }
1022 return vRes;
1023 }
1024
1025 int iNext{0};
1026 vRes = getSplinePointAtDist(dDist, 1, 0.0, &iNext, &dt);
1027 if (vRes.valid) {
1028 dMinDist = (vRes - coord).magnitude();
1029 }
1030 int i = 2;
1031 while (vRes.valid && i <= middlePoints) {
1032 vNext = getSplinePointAtDist(dDist, iNext, dt, &iNext, &dt);
1033 dCurDist = (vNext - coord).magnitude();
1034
1035 if (vNext.valid && dCurDist < dMinDist) {
1036 dMinDist = dCurDist;
1037 vRes = vNext;
1038 }
1039 i++;
1040 }
1041
1042 if (dist != nullptr) {
1043 *dist = dMinDist;
1044 }
1045 return vRes;
1046}
1047
1048RS_Vector LC_SplinePoints::doGetNearestDist([[maybe_unused]] double distance, [[maybe_unused]] const RS_Vector& coord, double* dist) const {
1049 printf("getNearestDist\n");
1050 if (dist != nullptr) {
1051 *dist = RS_MAXDOUBLE1.0E+10;
1052 }
1053
1054 return RS_Vector(false);
1055}
1056
1057void LC_SplinePoints::move(const RS_Vector& offset) {
1058 for (auto& v : m_data.splinePoints) {
1059 v.move(offset);
1060 }
1061 for (auto& v : m_data.controlPoints) {
1062 v.move(offset);
1063 }
1064 update();
1065}
1066
1067void LC_SplinePoints::rotate(const RS_Vector& center, const double angle) {
1068 rotate(center, RS_Vector(angle));
1069}
1070
1071void LC_SplinePoints::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
1072 for (auto& v : m_data.splinePoints) {
1073 v.rotate(center, angleVector);
1074 }
1075 for (auto& v : m_data.controlPoints) {
1076 v.rotate(center, angleVector);
1077 }
1078 update();
1079}
1080
1081void LC_SplinePoints::scale(const RS_Vector& center, const RS_Vector& factor) {
1082 for (auto& v : m_data.splinePoints) {
1083 v.scale(center, factor);
1084 }
1085 for (auto& v : m_data.controlPoints) {
1086 v.scale(center, factor);
1087 }
1088 update();
1089}
1090
1091void LC_SplinePoints::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
1092 for (auto& v : m_data.splinePoints) {
1093 v.mirror(axisPoint1, axisPoint2);
1094 }
1095 for (auto& v : m_data.controlPoints) {
1096 v.mirror(axisPoint1, axisPoint2);
1097 }
1098 update();
1099}
1100
1101RS_Entity& LC_SplinePoints::shear(const double k) {
1102 for (auto& v : m_data.splinePoints) {
1103 v.shear(k);
1104 }
1105 for (auto& v : m_data.controlPoints) {
1106 v.shear(k);
1107 }
1108 update();
1109 return *this;
1110}
1111
1112void LC_SplinePoints::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
1113 for (auto& v : m_data.splinePoints) {
1114 // fixme - magic value - replace by constant
1115 if (ref.distanceTo(v) < 1.0e-4) {
1116 v.move(offset);
1117 }
1118 }
1119 for (auto& v : m_data.controlPoints) {
1120 // fixme - magic value - replace by constant
1121 if (ref.distanceTo(v) < 1.0e-4) {
1122 v.move(offset);
1123 }
1124 }
1125 update();
1126}
1127
1128void LC_SplinePoints::revertDirection() {
1129 size_t j = m_data.splinePoints.size() - 1;
1130 for (size_t k = 0; k < m_data.splinePoints.size() / 2; ++k) {
1131 std::swap(m_data.splinePoints[k], m_data.splinePoints[j--]);
1132 }
1133 j = m_data.controlPoints.size() - 1;
1134 for (size_t k = 0; k < m_data.controlPoints.size() / 2; ++k) {
1135 std::swap(m_data.controlPoints[k], m_data.controlPoints[j--]);
1136 }
1137 update();
1138}
1139
1140/**
1141 * @return The reference points of the spline.
1142 */
1143const std::vector<RS_Vector>& LC_SplinePoints::getPoints() const {
1144 if (m_data.cut) {
1145 return m_data.controlPoints;
1146 }
1147 return m_data.splinePoints;
1148}
1149
1150const std::vector<RS_Vector>& LC_SplinePoints::getControlPoints() const {
1151 return m_data.controlPoints;
1152}
1153
1154// fixme - sand - this method is used only for writing spline points... do we really neede a copy of the vector there?
1155std::vector<RS_Vector> LC_SplinePoints::getStrokePoints() const {
1156 const int p1 = getGraphicVariableInt("$SPLINESEGS", 8);
1157 std::vector<RS_Vector> result;
1158 fillStrokePoints(p1, result);
1159 return result;
1160}
1161
1162void LC_SplinePoints::fillStrokePoints(const int splineSegments, std::vector<RS_Vector>& points) const {
1163 size_t iSplines = m_data.controlPoints.size();
1164 if (!m_data.closed) {
1165 iSplines -= 2;
1166 }
1167
1168 RS_Vector vStart(false), vControl(false), vEnd(false);
1169 for (size_t i = 1; i <= iSplines; ++i) {
1170 const int iPts = getQuadPoints(i, &vStart, &vControl, &vEnd);
1171 if (iPts > 2) {
1172 strokeQuad(&points, vStart, vControl, vEnd, splineSegments);
1173 }
1174 else if (iPts > 1) {
1175 points.push_back(vStart);
1176 }
1177 }
1178
1179 if (!m_data.closed && vEnd.valid) {
1180 points.push_back(vEnd);
1181 }
1182}
1183
1184/**
1185 * push_backs the given point to the control points.
1186 */
1187bool LC_SplinePoints::addPoint(const RS_Vector& v) {
1188 if (m_data.cut) {
1189 return false;
1190 }
1191
1192 if (m_data.splinePoints.empty() || (v - m_data.splinePoints.back()).squared() > RS_TOLERANCE21.0e-20) {
1193 m_data.splinePoints.push_back(v);
1194 return true;
1195 }
1196 return false;
1197}
1198
1199/**
1200 * Removes the control point that was last added.
1201 */
1202void LC_SplinePoints::removeLastPoint() {
1203 m_data.splinePoints.pop_back();
1204}
1205
1206void LC_SplinePoints::addControlPoint(const RS_Vector& v) {
1207 m_data.controlPoints.push_back(v);
1208}
1209
1210std::vector<double> getMatrix(const size_t iCount, const bool bClosed, const std::vector<double>& dt) {
1211 if (bClosed && (iCount < 3 || dt.size() != iCount)) {
1212 return {};
1213 }
1214 if (!bClosed && (iCount < 4 || dt.size() != iCount - 2)) {
1215 return {};
1216 }
1217
1218 // closed: iDim = 5*iCount - 6; // n + 2*(n - 1) + 2*(n - 2)
1219 // not closed: iDim = 3*iCount - 8; // (n - 2) + 2*(n - 3)
1220 const int iDim = bClosed ? 5 * iCount - 6 : 3 * iCount - 8;
1221
1222 std::vector<double> dRes(iDim);
1223
1224 if (bClosed) {
1225 double* pdDiag = dRes.data();
1226 double* pdDiag1 = &dRes[iCount];
1227 double* pdDiag2 = &dRes[2 * iCount - 1];
1228 double* pdLastCol1 = &dRes[3 * iCount - 2];
1229 double* pdLastCol2 = &dRes[4 * iCount - 4];
1230
1231 double x1 = (1.0 - dt[0]) * (1.0 - dt[0]) / 2.0;
1232 double x3 = dt[0] * dt[0] / 2.0;
1233 double x2 = x1 + 2.0 * dt[0] * (1.0 - dt[0]) + x3;
1234
1235 pdDiag[0] = std::sqrt(x2);
1236 pdDiag1[0] = x3 / pdDiag[0];
1237 pdLastCol1[0] = x1 / pdDiag[0];
1238
1239 x1 = (1.0 - dt[1]) * (1.0 - dt[1]) / 2.0;
1240 x3 = dt[1] * dt[1] / 2.0;
1241 x2 = x1 + 2.0 * dt[1] * (1.0 - dt[1]) + x3;
1242
1243 pdDiag2[0] = x1 / pdDiag[0];
1244
1245 pdDiag[1] = std::sqrt(x2 - pdDiag1[0] * pdDiag2[0]);
1246 pdDiag1[1] = x3 / pdDiag[1];
1247 pdLastCol1[1] = -pdDiag2[0] * pdLastCol1[0] / pdDiag[1];
1248
1249 for (size_t i = 2; i < iCount - 2; i++) {
1250 x1 = (1.0 - dt[i]) * (1.0 - dt[i]) / 2.0;
1251 x3 = dt[i] * dt[i] / 2.0;
1252 x2 = x1 + 2.0 * dt[i] * (1.0 - dt[i]) + x3;
1253
1254 pdDiag2[i - 1] = x1 / pdDiag[i - 1];
1255
1256 pdDiag[i] = std::sqrt(x2 - pdDiag1[i - 1] * pdDiag2[i - 1]);
1257 pdDiag1[i] = x3 / pdDiag[i];
1258 pdLastCol1[i] = -pdDiag2[i - 1] * pdLastCol1[i - 1] / pdDiag[i];
1259 }
1260 x1 = (1.0 - dt[iCount - 2]) * (1.0 - dt[iCount - 2]) / 2.0;
1261 x3 = dt[iCount - 2] * dt[iCount - 2] / 2.0;
1262 x2 = x1 + 2.0 * dt[iCount - 2] * (1.0 - dt[iCount - 2]) + x3;
1263
1264 pdDiag2[iCount - 3] = x1 / pdDiag[iCount - 3];
1265
1266 pdDiag[iCount - 2] = std::sqrt(x2 - pdDiag1[iCount - 3] * pdDiag2[iCount - 3]);
1267 pdDiag1[iCount - 2] = (x3 - pdDiag2[iCount - 3] * pdLastCol1[iCount - 3]) / pdDiag[iCount - 2];
1268
1269 x1 = (1.0 - dt[iCount - 1]) * (1.0 - dt[iCount - 1]) / 2.0;
1270 x3 = dt[iCount - 1] * dt[iCount - 1] / 2.0;
1271 x2 = x1 + 2.0 * dt[iCount - 1] * (1.0 - dt[iCount - 1]) + x3;
1272
1273 pdLastCol2[0] = x3 / pdDiag[0];
1274 double dLastColSum = pdLastCol1[0] * pdLastCol2[0];
1275 for (size_t i = 1; i < iCount - 2; i++) {
1276 pdLastCol2[i] = -pdLastCol2[i - 1] * pdDiag1[i - 1] / pdDiag[i];
1277 dLastColSum += pdLastCol1[i] * pdLastCol2[i];
1278 }
1279
1280 pdDiag2[iCount - 2] = (x1 - pdDiag1[iCount - 3] * pdLastCol2[iCount - 3]) / pdDiag[iCount - 2];
1281
1282 dLastColSum += pdDiag1[iCount - 2] * pdDiag2[iCount - 2];
1283 pdDiag[iCount - 1] = std::sqrt(x2 - dLastColSum);
1284 }
1285 else {
1286 double* pdDiag = dRes.data();
1287 double* pdDiag1 = &dRes[iCount - 2];
1288 double* pdDiag2 = &dRes[2 * iCount - 5];
1289
1290 double x3 = dt[0] * dt[0] / 2.0;
1291 double x2 = 2.0 * dt[0] * (1.0 - dt[0]) + x3;
1292 pdDiag[0] = std::sqrt(x2);
1293 pdDiag1[0] = x3 / pdDiag[0];
1294
1295 for (size_t i = 1; i < iCount - 3; i++) {
1296 const double x1 = (1.0 - dt[i]) * (1.0 - dt[i]) / 2.0;
1297 x3 = dt[i] * dt[i] / 2.0;
1298 x2 = x1 + 2.0 * dt[i] * (1.0 - dt[i]) + x3;
1299
1300 pdDiag2[i - 1] = x1 / pdDiag[i - 1];
1301 pdDiag[i] = std::sqrt(x2 - pdDiag1[i - 1] * pdDiag2[i - 1]);
1302 pdDiag1[i] = x3 / pdDiag[i];
1303 }
1304
1305 const double x1 = (1.0 - dt[iCount - 3]) * (1.0 - dt[iCount - 3]) / 2.0;
1306 x2 = x1 + 2.0 * dt[iCount - 3] * (1.0 - dt[iCount - 3]);
1307 pdDiag2[iCount - 4] = x1 / pdDiag[iCount - 4];
1308 pdDiag[iCount - 3] = std::sqrt(x2 - pdDiag1[iCount - 4] * pdDiag2[iCount - 4]);
1309 }
1310
1311 return dRes;
1312}
1313
1314void LC_SplinePoints::updateControlPointsUI() {
1315 if (m_data.cut) {
1316 return; // no update after trim operation
1317 }
1318
1319 if (!m_data.useControlPoints) {
1320 m_data.controlPoints.clear();
1321 }
1322
1323 const size_t n = m_data.splinePoints.size();
1324
1325 if (m_data.closed && n < 3) {
1326 if (n > 0) {
1327 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1328 }
1329 if (n > 1) {
1330 m_data.controlPoints.push_back(m_data.splinePoints.at(1));
1331 }
1332 return;
1333 }
1334
1335 if (!m_data.closed && n < 4) {
1336 // use control points directly, reserved for parabola
1337 if (m_data.useControlPoints && m_data.controlPoints.size() == 3) {
1338 return;
1339 }
1340 if (n > 0) {
1341 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1342 }
1343 if (n > 2) {
1344 const RS_Vector vControl = getThreePointsControl(m_data.splinePoints.at(0), m_data.splinePoints.at(1),
1345 m_data.splinePoints.at(2));
1346 if (vControl.valid) {
1347 m_data.controlPoints.push_back(vControl);
1348 }
1349 }
1350 if (n > 1) {
1351 m_data.controlPoints.push_back(m_data.splinePoints.at(n - 1));
1352 }
1353 return;
1354 }
1355
1356 const int iDim = m_data.closed ? n : n - 2;
1357
1358 std::vector<double> dt(iDim);
1359
1360 if (m_data.closed) {
1361 double dl1 = (m_data.splinePoints.at(n - 1) - m_data.splinePoints.at(0)).magnitude();
1362 double dl2 = (m_data.splinePoints.at(1) - m_data.splinePoints.at(0)).magnitude();
1363 dt[0] = dl1 / (dl1 + dl2);
1364 for (int i = 1; i < iDim - 1; i++) {
1365 dl1 = dl2;
1366 dl2 = (m_data.splinePoints.at(i + 1) - m_data.splinePoints.at(i)).magnitude();
1367 dt[i] = dl1 / (dl1 + dl2);
1368 }
1369 dl1 = (m_data.splinePoints.at(n - 1) - m_data.splinePoints.at(n - 2)).magnitude();
1370 dl2 = (m_data.splinePoints.at(0) - m_data.splinePoints.at(n - 1)).magnitude();
1371 dt[iDim - 1] = dl1 / (dl1 + dl2);
1372 }
1373 else {
1374 double dl1 = (m_data.splinePoints.at(1) - m_data.splinePoints.at(0)).magnitude();
1375 double dl2 = (m_data.splinePoints.at(2) - m_data.splinePoints.at(1)).magnitude();
1376 dt[0] = dl1 / (dl1 + dl2 / 2.0);
1377 for (int i = 1; i < iDim - 1; i++) {
1378 dl1 = dl2;
1379 dl2 = (m_data.splinePoints.at(i + 2) - m_data.splinePoints.at(i + 1)).magnitude();
1380 dt[i] = dl1 / (dl1 + dl2);
1381 }
1382 dl1 = dl2;
1383 dl2 = (m_data.splinePoints.at(iDim) - m_data.splinePoints.at(iDim + 1)).magnitude();
1384 dt[iDim - 1] = dl1 / (dl1 + 2.0 * dl2);
1385 }
1386
1387 const std::vector<double> pdMatrix = getMatrix(n, m_data.closed, dt);
1388
1389 if (pdMatrix.empty()) {
1390 return;
1391 }
1392
1393 std::vector<double> dx(iDim);
1394 std::vector<double> dy(iDim);
1395 std::vector<double> dx2(iDim);
1396 std::vector<double> dy2(iDim);
1397
1398 if (m_data.closed) {
1399 const double* pdDiag = pdMatrix.data();
1400 const double* pdDiag1 = &pdMatrix[n];
1401 const double* pdDiag2 = &pdMatrix[2 * n - 1];
1402 const double* pdLastCol1 = &pdMatrix[3 * n - 2];
1403 const double* pdLastCol2 = &pdMatrix[4 * n - 4];
1404
1405 dx[0] = m_data.splinePoints.at(0).x / pdDiag[0];
1406 dy[0] = m_data.splinePoints.at(0).y / pdDiag[0];
1407 for (int i = 1; i < iDim - 1; i++) {
1408 dx[i] = (m_data.splinePoints.at(i).x - pdDiag2[i - 1] * dx[i - 1]) / pdDiag[i];
1409 dy[i] = (m_data.splinePoints.at(i).y - pdDiag2[i - 1] * dy[i - 1]) / pdDiag[i];
1410 }
1411
1412 dx[iDim - 1] = m_data.splinePoints.at(iDim - 1).x - pdDiag2[iDim - 2] * dx[iDim - 2];
1413 dy[iDim - 1] = m_data.splinePoints.at(iDim - 1).y - pdDiag2[iDim - 2] * dy[iDim - 2];
1414 for (int i = 0; i < iDim - 2; i++) {
1415 dx[iDim - 1] -= dx[i] * pdLastCol2[i];
1416 dy[iDim - 1] -= dy[i] * pdLastCol2[i];
1417 }
1418 dx[iDim - 1] /= pdDiag[iDim - 1];
1419 dy[iDim - 1] /= pdDiag[iDim - 1];
1420
1421 dx2[iDim - 1] = dx[iDim - 1] / pdDiag[iDim - 1];
1422 dy2[iDim - 1] = dy[iDim - 1] / pdDiag[iDim - 1];
1423 dx2[iDim - 2] = (dx[iDim - 2] - pdDiag1[iDim - 2] * dx2[iDim - 1]) / pdDiag[iDim - 2];
1424 dy2[iDim - 2] = (dy[iDim - 2] - pdDiag1[iDim - 2] * dy2[iDim - 1]) / pdDiag[iDim - 2];
1425
1426 for (int i = iDim - 3; i >= 0; i--) {
1427 dx2[i] = (dx[i] - pdDiag1[i] * dx2[i + 1] - pdLastCol1[i] * dx2[iDim - 1]) / pdDiag[i];
1428 dy2[i] = (dy[i] - pdDiag1[i] * dy2[i + 1] - pdLastCol1[i] * dy2[iDim - 1]) / pdDiag[i];
1429 }
1430
1431 for (int i = 0; i < iDim; i++) {
1432 m_data.controlPoints.emplace_back(dx2[i], dy2[i]);
1433 }
1434 }
1435 else {
1436 const double* pdDiag = pdMatrix.data();
1437 const double* pdDiag1 = &pdMatrix[n - 2];
1438 const double* pdDiag2 = &pdMatrix[2 * n - 5];
1439
1440 dx[0] = (m_data.splinePoints.at(1).x - m_data.splinePoints.at(0).x * (1.0 - dt[0]) * (1.0 - dt[0])) / pdDiag[0];
1441 dy[0] = (m_data.splinePoints.at(1).y - m_data.splinePoints.at(0).y * (1.0 - dt[0]) * (1.0 - dt[0])) / pdDiag[0];
1442 for (int i = 1; i < iDim - 1; i++) {
1443 dx[i] = (m_data.splinePoints.at(i + 1).x - pdDiag2[i - 1] * dx[i - 1]) / pdDiag[i];
1444 dy[i] = (m_data.splinePoints.at(i + 1).y - pdDiag2[i - 1] * dy[i - 1]) / pdDiag[i];
1445 }
1446 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] *
1447 dx[iDim - 2]) / pdDiag[iDim - 1];
1448 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] *
1449 dy[iDim - 2]) / pdDiag[iDim - 1];
1450
1451 dx2[iDim - 1] = dx[iDim - 1] / pdDiag[iDim - 1];
1452 dy2[iDim - 1] = dy[iDim - 1] / pdDiag[iDim - 1];
1453
1454 for (int i = iDim - 2; i >= 0; i--) {
1455 dx2[i] = (dx[i] - pdDiag1[i] * dx2[i + 1]) / pdDiag[i];
1456 dy2[i] = (dy[i] - pdDiag1[i] * dy2[i + 1]) / pdDiag[i];
1457 }
1458
1459 m_data.controlPoints.push_back(m_data.splinePoints.at(0));
1460 for (int i = 0; i < iDim; i++) {
1461 m_data.controlPoints.emplace_back(dx2[i], dy2[i]);
1462 }
1463 m_data.controlPoints.push_back(m_data.splinePoints.at(n - 1));
1464 }
1465}
1466
1467double getLinePointAtDist(const double dLen, const double t1, const double dDist) {
1468 return t1 + dDist / dLen;
1469}
1470
1471// returns new pattern offset;
1472double drawPatternLine(const std::vector<double>& pdPattern, const int iPattern, double patternOffset, QPainterPath& qPath,
1473 const RS_Vector& x1, const RS_Vector& x2) {
1474 const double dLen = (x2 - x1).magnitude();
1475 if (dLen < RS_TOLERANCE1.0e-10) {
1476 return patternOffset;
1477 }
1478
1479 int i = 0;
1480 double dCurSegLen = 0.0;
1481 double dSegOffs = 0.0;
1482 while (patternOffset > RS_TOLERANCE1.0e-10) {
1483 if (i >= iPattern) {
1484 i = 0;
1485 }
1486 dCurSegLen = std::abs(pdPattern[i++]);
1487 if (patternOffset > dCurSegLen) {
1488 patternOffset -= dCurSegLen;
1489 }
1490 else {
1491 dSegOffs = patternOffset;
1492 patternOffset = 0.0;
1493 }
1494 }
1495 if (i > 0) {
1496 i--;
1497 }
1498
1499 dCurSegLen = std::abs(pdPattern[i]) - dSegOffs;
1500 dSegOffs = 0.0;
1501
1502 double dt1 = 0.0;
1503 double dt2 = 1.0;
1504 double dCurLen = dLen;
1505 if (dCurSegLen < dCurLen) {
1506 // double dt2bak=dt1;
1507 dt2 = getLinePointAtDist(dLen, dt1, dCurSegLen);
1508 dCurLen -= dCurSegLen;
1509 }
1510 else {
1511 dSegOffs = dCurLen;
1512 dCurLen = 0.0;
1513 }
1514
1515 RS_Vector p2 = x1 * (1.0 - dt2) + x2 * dt2;
1516 if (pdPattern[i] < 0) {
1517 qPath.moveTo(QPointF(p2.x, p2.y));
1518 }
1519 else {
1520 qPath.lineTo(QPointF(p2.x, p2.y));
1521 }
1522
1523 i++;
1524 dt1 = dt2;
1525
1526 while (dCurLen > RS_TOLERANCE1.0e-10) {
1527 if (i >= iPattern) {
1528 i = 0;
1529 }
1530
1531 dCurSegLen = std::abs(pdPattern[i]);
1532 if (dCurLen > dCurSegLen) {
1533 dt2 = getLinePointAtDist(dLen, dt1, dCurSegLen);
1534 dCurLen -= dCurSegLen;
1535 }
1536 else {
1537 dt2 = 1.0;
1538 dSegOffs = dCurLen;
1539 dCurLen = 0.0;
1540 }
1541
1542 p2 = x1 * (1.0 - dt2) + x2 * dt2;
1543 if (pdPattern[i] < 0) {
1544 qPath.moveTo(QPointF(p2.x, p2.y));
1545 }
1546 else {
1547 qPath.lineTo(QPointF(p2.x, p2.y));
1548 }
1549
1550 i++;
1551 dt1 = dt2;
1552 }
1553
1554 i--;
1555
1556 while (i > 0) {
1557 dSegOffs += std::abs(pdPattern[--i]);
1558 }
1559 return dSegOffs;
1560}
1561
1562// returns new pattern offset;
1563double drawPatternQuad(const std::vector<double>& pdPattern, const int iPattern, double patternOffset, QPainterPath& qPath,
1564 const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) {
1565 const double dLen = getQuadLength(x1, c1, x2, 0.0, 1.0);
1566 if (dLen < RS_TOLERANCE1.0e-10) {
1567 return patternOffset;
1568 }
1569
1570 int i = 0;
1571 double dCurSegLen = 0.0;
1572 double dSegOffs = 0.0;
1573 while (patternOffset > RS_TOLERANCE1.0e-10) {
1574 if (i >= iPattern) {
1575 i = 0;
1576 }
1577 dCurSegLen = std::abs(pdPattern[i++]);
1578 if (patternOffset > dCurSegLen) {
1579 patternOffset -= dCurSegLen;
1580 }
1581 else {
1582 dSegOffs = patternOffset;
1583 patternOffset = 0.0;
1584 }
1585 }
1586 if (i > 0) {
1587 i--;
1588 }
1589
1590 dCurSegLen = std::abs(pdPattern[i]) - dSegOffs;
1591 dSegOffs = 0.0;
1592
1593 double dt1 = 0.0;
1594 double dt2 = 1.0;
1595 double dCurLen = dLen;
1596 if (dCurSegLen < dCurLen) {
1597 dt2 = getQuadPointAtDist(x1, c1, x2, dt1, dCurSegLen);
1598 dCurLen -= dCurSegLen;
1599 }
1600 else {
1601 dSegOffs = dCurLen;
1602 dCurLen = 0.0;
1603 }
1604
1605 RS_Vector c2;
1606
1607 RS_Vector p2 = getQuadPoint(x1, c1, x2, dt2);
1608 if (pdPattern[i] < 0) {
1609 qPath.moveTo(QPointF(p2.x, p2.y));
1610 }
1611 else {
1612 c2 = getSubQuadControlPoint(x1, c1, x2, dt1, dt2);
1613 qPath.quadTo(QPointF(c2.x, c2.y), QPointF(p2.x, p2.y));
1614 }
1615
1616 i++;
1617 dt1 = dt2;
1618
1619 while (dCurLen > RS_TOLERANCE1.0e-10) {
1620 if (i >= iPattern) {
1621 i = 0;
1622 }
1623
1624 dCurSegLen = std::abs(pdPattern[i]);
1625 if (dCurLen > dCurSegLen) {
1626 dt2 = getQuadPointAtDist(x1, c1, x2, dt1, dCurSegLen);
1627 dCurLen -= dCurSegLen;
1628 }
1629 else {
1630 dt2 = 1.0;
1631 dSegOffs = dCurLen;
1632 dCurLen = 0.0;
1633 }
1634
1635 p2 = getQuadPoint(x1, c1, x2, dt2);
1636 if (pdPattern[i] < 0) {
1637 qPath.moveTo(QPointF(p2.x, p2.y));
1638 }
1639 else {
1640 c2 = getSubQuadControlPoint(x1, c1, x2, dt1, dt2);
1641 qPath.quadTo(QPointF(c2.x, c2.y), QPointF(p2.x, p2.y));
1642 }
1643
1644 i++;
1645 dt1 = dt2;
1646 }
1647
1648 i--;
1649
1650 while (i > 0) {
1651 dSegOffs += std::abs(pdPattern[--i]);
1652 }
1653 return dSegOffs;
1654}
1655
1656void LC_SplinePoints::draw(RS_Painter* painter) {
1657 // Adjust dash offset
1658 painter->updateDashOffset(this);
1659 painter->drawSplinePointsWCS(m_data.controlPoints, m_data.closed);
1660}
1661
1662void LC_SplinePoints::updateLength() {
1663 size_t n = m_data.controlPoints.size();
1664
1665 if (n < 2) {
1666 m_cachedLength = 0;
1667 return;
1668 }
1669
1670 RS_Vector vStart(false), vControl(false), vEnd(false);
1671
1672 //UpdateControlPoints();
1673
1674 double dRes = 0.0;
1675
1676 if (m_data.closed) {
1677 if (n < 3) {
1678 m_cachedLength = 0.0;
1679 return;
1680 }
1681
1682 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1683 vControl = m_data.controlPoints.at(0);
1684 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1685
1686 dRes = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1687
1688 for (size_t i = 1; i < n - 1; i++) {
1689 vStart = vEnd;
1690 vControl = m_data.controlPoints.at(i);
1691 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1692
1693 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1694 }
1695
1696 vStart = vEnd;
1697 vControl = m_data.controlPoints.at(n - 1);
1698 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1699
1700 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1701 }
1702 else {
1703 vStart = m_data.controlPoints.at(0);
1704 vEnd = m_data.controlPoints.at(1);
1705 if (n < 3) {
1706 m_cachedLength = (vEnd - vStart).magnitude();
1707 return;
1708 }
1709
1710 vControl = vEnd;
1711 vEnd = m_data.controlPoints.at(2);
1712 if (n < 4) {
1713 m_cachedLength = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1714 return;
1715 }
1716
1717 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
1718
1719 dRes = getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1720
1721 for (size_t i = 2; i < n - 2; i++) {
1722 vStart = vEnd;
1723 vControl = m_data.controlPoints.at(i);
1724 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1725
1726 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1727 }
1728
1729 vStart = vEnd;
1730 vControl = m_data.controlPoints.at(n - 2);
1731 vEnd = m_data.controlPoints.at(n - 1);
1732
1733 dRes += getQuadLength(vStart, vControl, vEnd, 0.0, 1.0);
1734 }
1735
1736 m_cachedLength = dRes;
1737}
1738
1739double LC_SplinePoints::getDirection1() const {
1740 const size_t n = m_data.controlPoints.size();
1741
1742 if (n < 2) {
1743 return 0.0;
1744 }
1745
1746 RS_Vector vStart, vEnd;
1747
1748 if (m_data.closed) {
1749 if (n < 3) {
1750 return 0.0;
1751 }
1752 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1753 vEnd = m_data.controlPoints.at(0);
1754 }
1755 else {
1756 vStart = m_data.controlPoints.at(0);
1757 vEnd = m_data.controlPoints.at(1);
1758 }
1759
1760 return vStart.angleTo(vEnd);
1761}
1762
1763double LC_SplinePoints::getDirection2() const {
1764 const size_t n = m_data.controlPoints.size();
1765
1766 if (n < 2) {
1767 return 0.0;
1768 }
1769
1770 RS_Vector vStart, vEnd;
1771
1772 if (m_data.closed) {
1773 if (n < 3) {
1774 return 0.0;
1775 }
1776 vStart = m_data.controlPoints.at(n - 1);
1777 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1778 }
1779 else {
1780 vStart = m_data.controlPoints.at(n - 2);
1781 vEnd = m_data.controlPoints.at(n - 1);
1782 }
1783
1784 return vEnd.angleTo(vStart);
1785}
1786
1787RS_VectorSolutions LC_SplinePoints::getTangentPoint(const RS_Vector& point) const {
1788 RS_VectorSolutions ret;
1789 size_t n = m_data.controlPoints.size();
1790
1791 if (n < 3) {
1792 return ret;
1793 }
1794
1795 RS_Vector vStart(false), vControl(false), vEnd(false);
1796
1797 if (m_data.closed) {
1798 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1799 vControl = m_data.controlPoints.at(0);
1800 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1801
1802 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1803
1804 for (size_t i = 1; i < n - 1; i++) {
1805 vStart = vEnd;
1806 vControl = m_data.controlPoints.at(i);
1807 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1808
1809 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1810 }
1811
1812 vStart = vEnd;
1813 vControl = m_data.controlPoints.at(n - 1);
1814 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1815
1816 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1817 }
1818 else {
1819 vStart = m_data.controlPoints.at(0);
1820 vControl = m_data.controlPoints.at(1);
1821 vEnd = m_data.controlPoints.at(2);
1822 if (n < 4) {
1823 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1824 return ret;
1825 }
1826
1827 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
1828
1829 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1830
1831 for (size_t i = 2; i < n - 2; i++) {
1832 vStart = vEnd;
1833 vControl = m_data.controlPoints.at(i);
1834 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1835
1836 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1837 }
1838
1839 vStart = vEnd;
1840 vControl = m_data.controlPoints.at(n - 2);
1841 vEnd = m_data.controlPoints.at(n - 1);
1842
1843 addQuadTangentPoints(&ret, point, vStart, vControl, vEnd);
1844 }
1845
1846 return ret;
1847}
1848
1849RS_Vector LC_SplinePoints::getTangentDirection(const RS_Vector& point) const {
1850 const size_t n = m_data.controlPoints.size();
1851
1852 RS_Vector vStart(false), vControl(false), vEnd(false), vRes(false);
1853
1854 if (n < 2) {
1855 return vStart;
1856 }
1857
1858 double dt = 0.0;
1859 const int iQuad = getNearestQuad(point, nullptr, &dt);
1860 if (iQuad < 0) {
1861 return vStart;
1862 }
1863
1864 const int i = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
1865
1866 if (i < 2) {
1867 return vStart;
1868 }
1869 if (i < 3) {
1870 vRes = vEnd - vStart;
1871 }
1872 else {
1873 vRes = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
1874 }
1875
1876 return vRes;
1877}
1878
1879LC_SplinePointsData gddLineOffset(const RS_Vector& vx1, const RS_Vector& vx2, const double distance) {
1880 LC_SplinePointsData ret(false, false);
1881
1882 double dDist = (vx2 - vx1).magnitude();
1883
1884 if (dDist < RS_TOLERANCE1.0e-10) {
1885 return ret;
1886 }
1887
1888 dDist = distance / dDist;
1889
1890 ret.splinePoints.emplace_back(vx1.x - dDist * (vx2.y - vx1.y), vx1.y + dDist * (vx2.x - vx1.x));
1891 ret.splinePoints.emplace_back(vx2.x - dDist * (vx2.y - vx1.y), vx2.y + dDist * (vx2.x - vx1.x));
1892 return ret;
1893}
1894
1895bool LC_SplinePoints::offsetCut(const RS_Vector& coord, const double& distance) {
1896 size_t n = m_data.controlPoints.size();
1897 if (n < 2) {
4
Assuming 'n' is >= 2
5
Taking false branch
1898 return false;
1899 }
1900
1901 double dt;
6
'dt' declared without an initial value
1902 int iQuad = getNearestQuad(coord, nullptr, &dt);
7
Calling 'LC_SplinePoints::getNearestQuad'
16
Returning from 'LC_SplinePoints::getNearestQuad'
1903 if (iQuad
16.1
'iQuad' is >= 0
< 0) {
17
Taking false branch
1904 return false;
1905 }
1906
1907 RS_Vector vStart(false), vEnd(false), vControl(false);
1908 RS_Vector vPoint(false), vTan(false);
1909
1910 if (getQuadPoints(iQuad, &vStart, &vControl, &vEnd)) {
18
Taking false branch
1911 vPoint = getQuadAtPoint(vStart, vControl, vEnd, dt);
1912 vTan = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
1913 }
1914 else {
1915 vPoint = vEnd * (1.0 - dt) - vStart * dt;
19
The right operand of '-' is a garbage value
1916 vTan = vEnd - vStart;
1917 }
1918
1919 double dDist = distance;
1920 if ((coord.x - vPoint.x) * vTan.y - (coord.y - vPoint.y) * vTan.x > 0) {
1921 dDist *= -1.0;
1922 }
1923
1924 LC_SplinePointsData spd(m_data.closed, false);
1925
1926 if (m_data.closed) {
1927 if (n < 3) {
1928 return false;
1929 }
1930
1931 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
1932 vControl = m_data.controlPoints.at(0);
1933 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
1934
1935 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1936 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1937 if (vTan.valid) {
1938 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1939 }
1940
1941 for (size_t i = 1; i < n - 1; i++) {
1942 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
1943 vControl = m_data.controlPoints.at(i);
1944 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
1945
1946 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1947 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1948 if (vTan.valid) {
1949 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1950 }
1951 }
1952
1953 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1954 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1955 if (vTan.valid) {
1956 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1957 }
1958 }
1959 else {
1960 vStart = m_data.controlPoints.at(0);
1961 vEnd = m_data.controlPoints.at(1);
1962
1963 if (n < 3) {
1964 spd = gddLineOffset(vStart, vEnd, dDist);
1965 bool bRes = spd.splinePoints.size() > 0;
1966 if (bRes) {
1967 m_data = spd;
1968 update();
1969 m_data.cut = true;
1970 }
1971 return bRes;
1972 }
1973
1974 vControl = vEnd;
1975 vEnd = m_data.controlPoints.at(2);
1976 if (n < 4) {
1977 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
1978 if (vTan.valid) {
1979 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
1980 }
1981
1982 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
1983 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
1984 if (vTan.valid) {
1985 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
1986 }
1987
1988 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
1989 if (vTan.valid) {
1990 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
1991 }
1992
1993 m_data = spd;
1994 update();
1995 m_data.cut = true;
1996 return true;
1997 }
1998
1999 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2000
2001 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2002 if (vTan.valid) {
2003 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2004 }
2005
2006 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2007 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2008 if (vTan.valid) {
2009 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2010 }
2011
2012 for (size_t i = 2; i < n - 2; i++) {
2013 vStart = vEnd;
2014 vControl = m_data.controlPoints.at(i);
2015 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2016
2017 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2018 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2019 if (vTan.valid) {
2020 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2021 }
2022 }
2023
2024 vStart = vEnd;
2025 vControl = m_data.controlPoints.at(n - 2);
2026 vEnd = m_data.controlPoints.at(n - 1);
2027
2028 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2029 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2030 if (vTan.valid) {
2031 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2032 }
2033
2034 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2035 if (vTan.valid) {
2036 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2037 }
2038 }
2039 m_data = spd;
2040 update();
2041 m_data.cut = true;
2042 return true;
2043}
2044
2045bool LC_SplinePoints::offsetSpline(const RS_Vector& coord, const double& distance) {
2046 size_t iPoints = m_data.splinePoints.size();
2047 size_t n = m_data.controlPoints.size();
2048
2049 if (iPoints < 2) {
2050 return false;
2051 }
2052 if (n < 2) {
2053 return false;
2054 }
2055
2056 double dt;
2057 int iQuad = getNearestQuad(coord, nullptr, &dt);
2058 if (iQuad < 0) {
2059 return false;
2060 }
2061
2062 RS_Vector vStart(false), vEnd(false), vControl(false);
2063 RS_Vector vPoint(false), vTan(false);
2064
2065 if (getQuadPoints(iQuad, &vStart, &vControl, &vEnd)) {
2066 vPoint = getQuadAtPoint(vStart, vControl, vEnd, dt);
2067 vTan = getQuadDirAtPoint(vStart, vControl, vEnd, dt);
2068 }
2069 else {
2070 vPoint = vEnd * (1.0 - dt) - vStart * dt;
2071 vTan = vEnd - vStart;
2072 }
2073
2074 double dDist = distance;
2075 if ((coord.x - vPoint.x) * vTan.y - (coord.y - vPoint.y) * vTan.x > 0) {
2076 dDist *= -1.0;
2077 }
2078
2079 LC_SplinePointsData spd(m_data.closed, m_data.cut);
2080
2081 double dl1, dl2;
2082
2083 if (m_data.closed) {
2084 if (n < 3) {
2085 return false;
2086 }
2087
2088 vPoint = m_data.splinePoints.at(0);
2089
2090 dl1 = (m_data.splinePoints.at(iPoints - 1) - vPoint).magnitude();
2091 dl2 = (m_data.splinePoints.at(1) - vPoint).magnitude();
2092 dt = dl1 / (dl1 + dl2);
2093
2094 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2095 vControl = m_data.controlPoints.at(0);
2096 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2097
2098 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2099 if (vTan.valid) {
2100 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2101 }
2102
2103 for (size_t i = 1; i < n - 1; i++) {
2104 vPoint = m_data.splinePoints.at(i);
2105
2106 dl1 = dl2;
2107 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2108 dt = dl1 / (dl1 + dl2);
2109
2110 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2111 vControl = m_data.controlPoints.at(i);
2112 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2113
2114 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2115
2116 if (vTan.valid) {
2117 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2118 }
2119 }
2120
2121 vPoint = m_data.splinePoints.at(iPoints - 1);
2122 dl1 = (vPoint - m_data.splinePoints.at(iPoints - 2)).magnitude();
2123 dl2 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2124 dt = dl1 / (dl1 + dl2);
2125
2126 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2127 if (vTan.valid) {
2128 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2129 }
2130 }
2131 else {
2132 vStart = m_data.controlPoints.at(0);
2133 vEnd = m_data.controlPoints.at(1);
2134
2135 if (n < 3) {
2136 spd = gddLineOffset(vStart, vEnd, dDist);
2137 bool bRes = !spd.splinePoints.empty();
2138 if (bRes) {
2139 m_data = spd;
2140 }
2141 return bRes;
2142 }
2143
2144 vPoint = m_data.splinePoints.at(1);
2145
2146 vControl = vEnd;
2147 vEnd = m_data.controlPoints.at(2);
2148 if (n < 4) {
2149 dl1 = (vPoint - vStart).magnitude();
2150 dl2 = (vEnd - vPoint).magnitude();
2151 dt = dl1 / (dl1 + dl2);
2152
2153 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2154 if (vTan.valid) {
2155 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2156 }
2157
2158 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2159 if (vTan.valid) {
2160 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2161 }
2162
2163 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2164 if (vTan.valid) {
2165 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2166 }
2167
2168 m_data = spd;
2169 return true;
2170 }
2171
2172 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2173
2174 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2175 if (vTan.valid) {
2176 spd.splinePoints.emplace_back(vStart.x - dDist * vTan.y, vStart.y + dDist * vTan.x);
2177 }
2178
2179 dl1 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2180 dl2 = (m_data.splinePoints.at(2) - vPoint).magnitude();
2181 dt = dl1 / (dl1 + dl2 / 2.0);
2182
2183 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2184 if (vTan.valid) {
2185 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2186 }
2187
2188 for (size_t i = 2; i < n - 2; i++) {
2189 vPoint = m_data.splinePoints.at(i);
2190
2191 dl1 = dl2;
2192 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2193 dt = dl1 / (dl1 + dl2);
2194
2195 vStart = vEnd;
2196 vControl = m_data.controlPoints.at(i);
2197 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2198
2199 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2200 if (vTan.valid) {
2201 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2202 }
2203 }
2204
2205 vPoint = m_data.splinePoints.at(n - 2);
2206
2207 dl1 = dl2;
2208 dl2 = (vPoint - m_data.splinePoints.at(n - 1)).magnitude();
2209 dt = dl1 / (dl1 + 2.0 * dl2);
2210
2211 vStart = vEnd;
2212 vControl = m_data.controlPoints.at(n - 2);
2213 vEnd = m_data.controlPoints.at(n - 1);
2214
2215 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2216 if (vTan.valid) {
2217 spd.splinePoints.emplace_back(vPoint.x - dDist * vTan.y, vPoint.y + dDist * vTan.x);
2218 }
2219
2220 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2221 if (vTan.valid) {
2222 spd.splinePoints.emplace_back(vEnd.x - dDist * vTan.y, vEnd.y + dDist * vTan.x);
2223 }
2224 }
2225 m_data = spd;
2226 return true;
2227}
2228
2229bool LC_SplinePoints::offset(const RS_Vector& coord, const double distance) {
2230 if (m_data.cut) {
1
Assuming field 'cut' is true
2
Taking true branch
2231 return offsetCut(coord, distance);
3
Calling 'LC_SplinePoints::offsetCut'
2232 }
2233 return offsetSpline(coord, distance);
2234}
2235
2236std::vector<RS_Entity*> addLineOffsets(const RS_Vector& vx1, const RS_Vector& vx2, const double& distance) {
2237 std::vector<RS_Entity*> ret(0, nullptr);
2238
2239 double dDist = (vx2 - vx1).magnitude();
2240
2241 if (dDist < RS_TOLERANCE1.0e-10) {
2242 ret.push_back(new RS_Circle(nullptr, {vx1, distance}));
2243 return ret;
2244 }
2245
2246 const LC_SplinePointsData spd1(false, false);
2247 const LC_SplinePointsData spd2(false, false);
2248
2249 auto* sp1 = new LC_SplinePoints(nullptr, spd1);
2250 auto* sp2 = new LC_SplinePoints(nullptr, spd2);
2251
2252 dDist = distance / dDist;
2253
2254 sp1->addPoint(RS_Vector(vx1.x - dDist * (vx2.y - vx1.y), vx1.y + dDist * (vx2.x - vx1.x)));
2255 sp2->addPoint(RS_Vector(vx1.x + dDist * (vx2.y - vx1.y), vx1.y - dDist * (vx2.x - vx1.x)));
2256
2257 sp1->addPoint(RS_Vector(vx2.x - dDist * (vx2.y - vx1.y), vx2.y + dDist * (vx2.x - vx1.x)));
2258 sp2->addPoint(RS_Vector(vx2.x + dDist * (vx2.y - vx1.y), vx2.y - dDist * (vx2.x - vx1.x)));
2259
2260 ret.push_back(sp1);
2261 ret.push_back(sp2);
2262 return ret;
2263}
2264
2265std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSidesSpline(const double& distance) const {
2266 std::vector<RS_Entity*> ret(0, nullptr);
2267
2268 size_t iPoints = m_data.splinePoints.size();
2269 size_t n = m_data.controlPoints.size();
2270
2271 if (iPoints < 1) {
2272 return ret;
2273 }
2274 if (n < 1) {
2275 return ret;
2276 }
2277
2278 LC_SplinePointsData spd1(m_data.closed, false);
2279 LC_SplinePointsData spd2(m_data.closed, false);
2280
2281 LC_SplinePoints *sp1, *sp2;
2282
2283 RS_Vector vStart(false), vEnd(false), vControl(false);
2284 RS_Vector vPoint(false), vTan(false);
2285
2286 double dt, dl1, dl2;
2287
2288 if (m_data.closed) {
2289 if (n < 3) {
2290 return ret;
2291 }
2292
2293 sp1 = new LC_SplinePoints(nullptr, spd1);
2294 sp2 = new LC_SplinePoints(nullptr, spd2);
2295
2296 vPoint = m_data.splinePoints.at(0);
2297
2298 dl1 = (m_data.splinePoints.at(iPoints - 1) - vPoint).magnitude();
2299 dl2 = (m_data.splinePoints.at(1) - vPoint).magnitude();
2300 dt = dl1 / (dl1 + dl2);
2301
2302 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2303 vControl = m_data.controlPoints.at(0);
2304 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2305
2306 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2307 if (vTan.valid) {
2308 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2309 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2310 }
2311
2312 for (size_t i = 1; i < n - 1; i++) {
2313 vPoint = m_data.splinePoints.at(i);
2314
2315 dl1 = dl2;
2316 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2317 dt = dl1 / (dl1 + dl2);
2318
2319 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2320 vControl = m_data.controlPoints.at(i);
2321 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2322
2323 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2324
2325 if (vTan.valid) {
2326 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2327 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2328 }
2329 }
2330
2331 vPoint = m_data.splinePoints.at(iPoints - 1);
2332 dl1 = (vPoint - m_data.splinePoints.at(iPoints - 2)).magnitude();
2333 dl2 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2334 dt = dl1 / (dl1 + dl2);
2335
2336 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2337 if (vTan.valid) {
2338 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2339 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2340 }
2341 }
2342 else {
2343 vStart = m_data.controlPoints.at(0);
2344 if (n < 2) {
2345 ret.push_back(new RS_Circle(nullptr, {vStart, distance}));
2346 return ret;
2347 }
2348
2349 vEnd = m_data.controlPoints.at(1);
2350 if (n < 3) {
2351 return addLineOffsets(vStart, vEnd, distance);
2352 }
2353
2354 vPoint = m_data.splinePoints.at(1);
2355
2356 vControl = vEnd;
2357 vEnd = m_data.controlPoints.at(2);
2358
2359 if (n < 4) {
2360 dl1 = (vPoint - vStart).magnitude();
2361 dl2 = (vEnd - vPoint).magnitude();
2362 dt = dl1 / (dl1 + dl2);
2363
2364 sp1 = new LC_SplinePoints(nullptr, spd1);
2365 sp2 = new LC_SplinePoints(nullptr, spd2);
2366
2367 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2368 if (vTan.valid) {
2369 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2370 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2371 }
2372
2373 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2374 if (vTan.valid) {
2375 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2376 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2377 }
2378
2379 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2380 if (vTan.valid) {
2381 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2382 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2383 }
2384
2385 ret.push_back(sp1);
2386 ret.push_back(sp2);
2387 return ret;
2388 }
2389
2390 sp1 = new LC_SplinePoints(nullptr, spd1);
2391 sp2 = new LC_SplinePoints(nullptr, spd2);
2392
2393 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2394
2395 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2396 if (vTan.valid) {
2397 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2398 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2399 }
2400
2401 dl1 = (vPoint - m_data.splinePoints.at(0)).magnitude();
2402 dl2 = (m_data.splinePoints.at(2) - vPoint).magnitude();
2403 dt = dl1 / (dl1 + dl2 / 2.0);
2404
2405 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2406 if (vTan.valid) {
2407 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2408 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2409 }
2410
2411 for (size_t i = 2; i < n - 2; i++) {
2412 vPoint = m_data.splinePoints.at(i);
2413
2414 dl1 = dl2;
2415 dl2 = (m_data.splinePoints.at(i + 1) - vPoint).magnitude();
2416 dt = dl1 / (dl1 + dl2);
2417
2418 vStart = vEnd;
2419 vControl = m_data.controlPoints.at(i);
2420 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2421
2422 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2423 if (vTan.valid) {
2424 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2425 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2426 }
2427 }
2428
2429 vPoint = m_data.splinePoints.at(n - 2);
2430
2431 dl1 = dl2;
2432 dl2 = (vPoint - m_data.splinePoints.at(n - 1)).magnitude();
2433 dt = dl1 / (dl1 + 2.0 * dl2);
2434
2435 vStart = vEnd;
2436 vControl = m_data.controlPoints.at(n - 2);
2437 vEnd = m_data.controlPoints.at(n - 1);
2438
2439 vTan = getQuadDir(vStart, vControl, vEnd, dt);
2440 if (vTan.valid) {
2441 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2442 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2443 }
2444
2445 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2446 if (vTan.valid) {
2447 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2448 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2449 }
2450 }
2451
2452 ret.push_back(sp1);
2453 ret.push_back(sp2);
2454 return ret;
2455}
2456
2457std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSidesCut(const double& distance) const {
2458 std::vector<RS_Entity*> ret(0, nullptr);
2459
2460 size_t n = m_data.controlPoints.size();
2461
2462 if (n < 1) {
2463 return ret;
2464 }
2465
2466 LC_SplinePointsData spd1(m_data.closed, false);
2467 LC_SplinePointsData spd2(m_data.closed, false);
2468
2469 LC_SplinePoints *sp1, *sp2;
2470
2471 RS_Vector vStart(false), vEnd(false), vControl(false);
2472 RS_Vector vPoint(false), vTan(false);
2473
2474 if (m_data.closed) {
2475 if (n < 3) {
2476 return ret;
2477 }
2478
2479 sp1 = new LC_SplinePoints(nullptr, spd1);
2480 sp2 = new LC_SplinePoints(nullptr, spd2);
2481
2482 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2483 vControl = m_data.controlPoints.at(0);
2484 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2485
2486 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2487 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2488 if (vTan.valid) {
2489 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2490 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2491 }
2492
2493 for (size_t i = 1; i < n - 1; i++) {
2494 vStart = (m_data.controlPoints.at(i - 1) + m_data.controlPoints.at(i)) / 2.0;
2495 vControl = m_data.controlPoints.at(i);
2496 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2497
2498 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2499 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2500 if (vTan.valid) {
2501 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2502 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2503 }
2504 }
2505
2506 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2507 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2508 if (vTan.valid) {
2509 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2510 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2511 }
2512 }
2513 else {
2514 vStart = m_data.controlPoints.at(0);
2515 if (n < 2) {
2516 ret.push_back(new RS_Circle(nullptr, RS_CircleData(vStart, distance)));
2517 return ret;
2518 }
2519
2520 vEnd = m_data.controlPoints.at(1);
2521 if (n < 3) {
2522 ret = addLineOffsets(vStart, vEnd, distance);
2523 sp1 = static_cast<LC_SplinePoints*>(ret[0]);
2524 sp1->update();
2525 sp1->m_data.cut = true;
2526 sp2 = static_cast<LC_SplinePoints*>(ret[1]);
2527 sp2->update();
2528 sp2->m_data.cut = true;
2529 return ret;
2530 }
2531
2532 vControl = vEnd;
2533 vEnd = m_data.controlPoints.at(2);
2534
2535 if (n < 4) {
2536 sp1 = new LC_SplinePoints(nullptr, spd1);
2537 sp2 = new LC_SplinePoints(nullptr, spd2);
2538
2539 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2540 if (vTan.valid) {
2541 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2542 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2543 }
2544
2545 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2546 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2547 if (vTan.valid) {
2548 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2549 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2550 }
2551
2552 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2553 if (vTan.valid) {
2554 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2555 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2556 }
2557
2558 sp1->update();
2559 sp1->m_data.cut = true;
2560 sp2->update();
2561 sp2->m_data.cut = true;
2562
2563 ret.push_back(sp1);
2564 ret.push_back(sp2);
2565 return ret;
2566 }
2567
2568 sp1 = new LC_SplinePoints(nullptr, spd1);
2569 sp2 = new LC_SplinePoints(nullptr, spd2);
2570
2571 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2572
2573 vTan = getQuadDir(vStart, vControl, vEnd, 0.0);
2574 if (vTan.valid) {
2575 sp1->addPoint(RS_Vector(vStart.x - distance * vTan.y, vStart.y + distance * vTan.x));
2576 sp2->addPoint(RS_Vector(vStart.x + distance * vTan.y, vStart.y - distance * vTan.x));
2577 }
2578
2579 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2580 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2581 if (vTan.valid) {
2582 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2583 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2584 }
2585
2586 for (size_t i = 2; i < n - 2; i++) {
2587 vStart = vEnd;
2588 vControl = m_data.controlPoints.at(i);
2589 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2590
2591 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2592 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2593 if (vTan.valid) {
2594 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2595 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2596 }
2597 }
2598
2599 vStart = vEnd;
2600 vControl = m_data.controlPoints.at(n - 2);
2601 vEnd = m_data.controlPoints.at(n - 1);
2602
2603 vPoint = getQuadAtPoint(vStart, vControl, vEnd, 0.5);
2604 vTan = getQuadDir(vStart, vControl, vEnd, 0.5);
2605 if (vTan.valid) {
2606 sp1->addPoint(RS_Vector(vPoint.x - distance * vTan.y, vPoint.y + distance * vTan.x));
2607 sp2->addPoint(RS_Vector(vPoint.x + distance * vTan.y, vPoint.y - distance * vTan.x));
2608 }
2609
2610 vTan = getQuadDir(vStart, vControl, vEnd, 1.0);
2611 if (vTan.valid) {
2612 sp1->addPoint(RS_Vector(vEnd.x - distance * vTan.y, vEnd.y + distance * vTan.x));
2613 sp2->addPoint(RS_Vector(vEnd.x + distance * vTan.y, vEnd.y - distance * vTan.x));
2614 }
2615 }
2616
2617 sp1->update();
2618 sp1->m_data.cut = true;
2619 sp2->update();
2620 sp2->m_data.cut = true;
2621
2622 ret.push_back(sp1);
2623 ret.push_back(sp2);
2624 return ret;
2625}
2626
2627std::vector<RS_Entity*> LC_SplinePoints::offsetTwoSides(const double distance) const {
2628 if (m_data.cut) {
2629 return offsetTwoSidesCut(distance);
2630 }
2631 return offsetTwoSidesSpline(distance);
2632}
2633
2634/**
2635 * Dumps the spline's m_data to stdout.
2636 */
2637std::ostream& operator <<(std::ostream& os, const LC_SplinePoints& l) {
2638 os << " SplinePoints: " << l.getData() << "\n";
2639 return os;
2640}
2641
2642RS_VectorSolutions getLineLineIntersect(const RS_Vector& vStart, const RS_Vector& vEnd, const RS_Vector& vx1, const RS_Vector& vx2) {
2643 RS_VectorSolutions ret;
2644
2645 const RS_Vector x1 = vx2 - vx1;
2646 const RS_Vector x2 = vStart - vEnd;
2647 RS_Vector x3 = vStart - vx1;
2648
2649 const double dDet = x1.x * x2.y - x1.y * x2.x;
2650 if (std::abs(dDet) < RS_TOLERANCE1.0e-10) {
2651 return ret;
2652 }
2653
2654 double dt = (x2.y * x3.x - x2.x * x3.y) / dDet;
2655 const double ds = (-x1.y * x3.x + x1.x * x3.y) / dDet;
2656
2657 if (dt < -RS_TOLERANCE1.0e-10) {
2658 return ret;
2659 }
2660 if (ds < -RS_TOLERANCE1.0e-10) {
2661 return ret;
2662 }
2663 if (dt > 1.0 + RS_TOLERANCE1.0e-10) {
2664 return ret;
2665 }
2666 if (ds > 1.0 + RS_TOLERANCE1.0e-10) {
2667 return ret;
2668 }
2669
2670 if (dt < 0.0) {
2671 dt = 0.0;
2672 }
2673 if (dt > 1.0) {
2674 dt = 1.0;
2675 }
2676
2677 x3 = vx1 * (1.0 - dt) + vx2 * dt;
2678
2679 ret.push_back(x3);
2680
2681 return ret;
2682}
2683
2684RS_VectorSolutions LC_SplinePoints::getLineIntersect(const RS_Vector& x1, const RS_Vector& x2) const {
2685 RS_VectorSolutions ret;
2686
2687 size_t n = m_data.controlPoints.size();
2688 if (n < 2) {
2689 return ret;
2690 }
2691
2692 RS_Vector vStart(false), vEnd(false), vControl(false);
2693
2694 if (m_data.closed) {
2695 if (n < 3) {
2696 return ret;
2697 }
2698
2699 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2700 vControl = m_data.controlPoints.at(0);
2701 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2702
2703 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2704
2705 for (size_t i = 1; i < n - 1; i++) {
2706 vStart = vEnd;
2707 vControl = m_data.controlPoints.at(i);
2708 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2709
2710 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2711 }
2712
2713 vStart = vEnd;
2714 vControl = m_data.controlPoints.at(n - 1);
2715 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2716
2717 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2718 }
2719 else {
2720 vStart = m_data.controlPoints.at(0);
2721 vEnd = m_data.controlPoints.at(1);
2722 if (n < 3) {
2723 return getLineLineIntersect(x1, x2, vStart, vEnd);
2724 }
2725
2726 vControl = vEnd;
2727 vEnd = m_data.controlPoints.at(2);
2728 if (n < 4) {
2729 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2730 return ret;
2731 }
2732
2733 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2734 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2735
2736 for (size_t i = 2; i < n - 2; i++) {
2737 vStart = vEnd;
2738 vControl = m_data.controlPoints.at(i);
2739 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2740
2741 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2742 }
2743
2744 vStart = vEnd;
2745 vControl = m_data.controlPoints.at(n - 2);
2746 vEnd = m_data.controlPoints.at(n - 1);
2747
2748 addLineQuadIntersect(&ret, x1, x2, vStart, vControl, vEnd);
2749 }
2750
2751 return ret;
2752}
2753
2754void addQuadQuadIntersect(RS_VectorSolutions* pVS, const RS_Vector& vStart, const RS_Vector& vControl, const RS_Vector& vEnd,
2755 const RS_Vector& vx1, const RS_Vector& vc1, const RS_Vector& vx2) {
2756 //avoid intersection if there's no intersection between lines
2757 //TODO, avoid O(N^2) complexity
2758 //tangential direction along (start, control, end)
2759 const std::array<RS_Line, 2> lines0{{{vStart, vControl}, {vEnd, vControl}}};
2760
2761 //tangential direction along (start, control, end)
2762 const std::array<RS_Line, 2> lines1{{{vx1, vc1}, {vx2, vc1}}};
2763
2764 //if lines0, lines1 do not overlap, there's no intersection
2765 bool overlap = false;
2766 for (const auto& l0 : lines0) {
2767 for (const auto& l1 : lines1) {
2768 if (RS_Information::getIntersection(&l0, &l1, true).size()) {
2769 overlap = true;
2770 break;
2771 }
2772 }
2773 if (overlap) {
2774 break;
2775 }
2776 }
2777 if (!overlap) {
2778 //if there's no overlap, return now
2779 return;
2780 }
2781
2782 const RS_Vector va0 = vStart;
2783 const RS_Vector va1 = (vControl - vStart) * 2.0;
2784 const RS_Vector va2 = vEnd - vControl * 2.0 + vStart;
2785
2786 const RS_Vector vb0 = vx1;
2787 const RS_Vector vb1 = (vc1 - vx1) * 2.0;
2788 const RS_Vector vb2 = vx2 - vc1 * 2.0 + vx1;
2789
2790 std::vector<double> a1(0, 0.), b1(0, 0.);
2791 a1.push_back(va2.x);
2792 b1.push_back(va2.y);
2793 a1.push_back(0.0);
2794 b1.push_back(0.0);
2795 a1.push_back(-vb2.x);
2796 b1.push_back(-vb2.y);
2797 a1.push_back(va1.x);
2798 b1.push_back(va1.y);
2799 a1.push_back(-vb1.x);
2800 b1.push_back(-vb1.y);
2801 a1.push_back(va0.x - vb0.x);
2802 b1.push_back(va0.y - vb0.y);
2803
2804 std::vector<std::vector<double>> m(0);
2805 m.push_back(a1);
2806 m.push_back(b1);
2807
2808 const RS_VectorSolutions& pvRes = RS_Math::simultaneousQuadraticSolverFull(m);
2809
2810 for (RS_Vector vSol : pvRes) {
2811 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) {
2812 if (vSol.x < 0.0) {
2813 vSol.x = 0.0;
2814 }
2815 if (vSol.x > 1.0) {
2816 vSol.x = 1.0;
2817 }
2818 pVS->push_back(getQuadPoint(vStart, vControl, vEnd, vSol.x));
2819 }
2820 }
2821}
2822
2823void LC_SplinePoints::addQuadIntersect(RS_VectorSolutions* sol, const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) const {
2824 size_t n = m_data.controlPoints.size();
2825 if (n < 2) {
2826 return;
2827 }
2828
2829 RS_Vector vStart(false), vEnd(false), vControl(false);
2830
2831 if (m_data.closed) {
2832 if (n < 3) {
2833 return;
2834 }
2835
2836 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2837 vControl = m_data.controlPoints.at(0);
2838 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2839
2840 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2841
2842 for (size_t i = 1; i < n - 1; i++) {
2843 vStart = vEnd;
2844 vControl = m_data.controlPoints.at(i);
2845 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2846
2847 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2848 }
2849
2850 vStart = vEnd;
2851 vControl = m_data.controlPoints.at(n - 1);
2852 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2853
2854 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2855 }
2856 else {
2857 vStart = m_data.controlPoints.at(0);
2858 vEnd = m_data.controlPoints.at(1);
2859 if (n < 3) {
2860 addLineQuadIntersect(sol, vStart, vEnd, x1, c1, x2);
2861 return;
2862 }
2863
2864 vControl = vEnd;
2865 vEnd = m_data.controlPoints.at(2);
2866 if (n < 4) {
2867 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2868 return;
2869 }
2870
2871 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2872 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2873
2874 for (size_t i = 2; i < n - 2; i++) {
2875 vStart = vEnd;
2876 vControl = m_data.controlPoints.at(i);
2877 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2878
2879 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2880 }
2881
2882 vStart = vEnd;
2883 vControl = m_data.controlPoints.at(n - 2);
2884 vEnd = m_data.controlPoints.at(n - 1);
2885
2886 addQuadQuadIntersect(sol, vStart, vControl, vEnd, x1, c1, x2);
2887 }
2888}
2889
2890RS_VectorSolutions LC_SplinePoints::getSplinePointsIntersect(LC_SplinePoints* l1) const {
2891 RS_VectorSolutions ret;
2892
2893 size_t n = m_data.controlPoints.size();
2894 if (n < 2) {
2895 return ret;
2896 }
2897
2898 RS_Vector vStart(false), vEnd(false), vControl(false);
2899
2900 if (m_data.closed) {
2901 if (n < 3) {
2902 return ret;
2903 }
2904
2905 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2906 vControl = m_data.controlPoints.at(0);
2907 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
2908
2909 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2910
2911 for (size_t i = 1; i < n - 1; i++) {
2912 vStart = vEnd;
2913 vControl = m_data.controlPoints.at(i);
2914 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2915
2916 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2917 }
2918
2919 vStart = vEnd;
2920 vControl = m_data.controlPoints.at(n - 1);
2921 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
2922
2923 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2924 }
2925 else {
2926 vStart = m_data.controlPoints.at(0);
2927 vEnd = m_data.controlPoints.at(1);
2928 if (n < 3) {
2929 return l1->getLineIntersect(vStart, vEnd);
2930 }
2931
2932 vControl = vEnd;
2933 vEnd = m_data.controlPoints.at(2);
2934 if (n < 4) {
2935 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2936 return ret;
2937 }
2938
2939 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
2940 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2941
2942 for (size_t i = 2; i < n - 2; i++) {
2943 vStart = vEnd;
2944 vControl = m_data.controlPoints.at(i);
2945 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
2946
2947 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2948 }
2949
2950 vStart = vEnd;
2951 vControl = m_data.controlPoints.at(n - 2);
2952 vEnd = m_data.controlPoints.at(n - 1);
2953
2954 l1->addQuadIntersect(&ret, vStart, vControl, vEnd);
2955 }
2956
2957 return ret;
2958}
2959
2960RS_VectorSolutions getQuadraticLineIntersect(const std::vector<double>& dQuadCoefs, const RS_Vector& vx1, const RS_Vector& vx2) {
2961 RS_VectorSolutions ret;
2962 if (dQuadCoefs.size() < 3) {
2963 return ret;
2964 }
2965
2966 const RS_Vector x1 = vx2 - vx1;
2967
2968 double a0 = 0.0;
2969 double a1 = 0.0;
2970 double a2 = 0.0;
2971
2972 if (dQuadCoefs.size() > 3) {
2973 a2 = dQuadCoefs[0] * x1.x * x1.x + dQuadCoefs[1] * x1.x * x1.y + dQuadCoefs[2] * x1.y * x1.y;
2974 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) +
2975 dQuadCoefs[3] * x1.x + dQuadCoefs[4] * x1.y;
2976 a0 = dQuadCoefs[0] * vx1.x * vx1.x + dQuadCoefs[1] * vx1.x * vx1.y + dQuadCoefs[2] * vx1.y * vx1.y + dQuadCoefs[3] * vx1.x +
2977 dQuadCoefs[4] * vx1.y + dQuadCoefs[5];
2978 }
2979 else {
2980 a1 = dQuadCoefs[0] * x1.x + dQuadCoefs[1] * x1.y;
2981 a0 = dQuadCoefs[0] * vx1.x + dQuadCoefs[1] * vx1.y + dQuadCoefs[2];
2982 }
2983
2984 std::vector<double> dSol(0, 0.);
2985
2986 if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
2987 std::vector<double> dCoefs(2, 0.);
2988 dCoefs.push_back(a1 / a2);
2989 dCoefs.push_back(a0 / a2);
2990 dSol = RS_Math::quadraticSolver(dCoefs);
2991 }
2992 else if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
2993 dSol.push_back(-a0 / a1);
2994 }
2995
2996 for (double& d : dSol) {
2997 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
2998 d = qBound(0.0, d, 1.0);
2999 ret.push_back(vx1 * (1.0 - d) + vx2 * d);
3000 }
3001 }
3002
3003 return ret;
3004}
3005
3006void addQuadraticQuadIntersect(RS_VectorSolutions* pVS, const std::vector<double>& dQuadCoefs, const RS_Vector& vx1, const RS_Vector& vc1,
3007 const RS_Vector& vx2) {
3008 if (dQuadCoefs.size() < 3) {
3009 return;
3010 }
3011
3012 const RS_Vector x1 = vx2 - vc1 * 2.0 + vx1;
3013 const RS_Vector x2 = vc1 - vx1;
3014
3015 double a0 = 0.0;
3016 double a1 = 0.0;
3017 double a2 = 0.0;
3018 double a3 = 0.0;
3019 double a4 = 0.0;
3020
3021 if (dQuadCoefs.size() > 3) {
3022 a4 = dQuadCoefs[0] * x1.x * x1.x + dQuadCoefs[1] * x1.x * x1.y + dQuadCoefs[2] * x1.y * x1.y;
3023 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;
3024 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) +
3025 dQuadCoefs[2] * (2.0 * x1.y * vx1.y + 4.0 * x2.y * x2.y) + dQuadCoefs[3] * x1.x + dQuadCoefs[4] * x1.y;
3026 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) +
3027 dQuadCoefs[3] * x2.x + dQuadCoefs[4] * x2.y);
3028 a0 = dQuadCoefs[0] * vx1.x * vx1.x + dQuadCoefs[1] * vx1.x * vx1.y + dQuadCoefs[2] * vx1.y * vx1.y + dQuadCoefs[3] * vx1.x +
3029 dQuadCoefs[4] * vx1.y + dQuadCoefs[5];
3030 }
3031 else {
3032 a2 = dQuadCoefs[0] * x1.x + dQuadCoefs[1] * x1.y;
3033 a1 = 2.0 * (dQuadCoefs[0] * x2.x + dQuadCoefs[1] * x2.y);
3034 a0 = dQuadCoefs[0] * vx1.x + dQuadCoefs[1] * vx1.y + dQuadCoefs[2];
3035 }
3036
3037 std::vector<double> dSol(0, 0.);
3038 std::vector<double> dCoefs(0, 0.);
3039
3040 if (std::abs(a4) > RS_TOLERANCE1.0e-10) {
3041 dCoefs.push_back(a3 / a4);
3042 dCoefs.push_back(a2 / a4);
3043 dCoefs.push_back(a1 / a4);
3044 dCoefs.push_back(a0 / a4);
3045 dSol = RS_Math::quarticSolver(dCoefs);
3046 }
3047 else if (std::abs(a3) > RS_TOLERANCE1.0e-10) {
3048 dCoefs.push_back(a2 / a3);
3049 dCoefs.push_back(a1 / a3);
3050 dCoefs.push_back(a0 / a3);
3051 dSol = RS_Math::cubicSolver(dCoefs);
3052 }
3053 else if (std::abs(a2) > RS_TOLERANCE1.0e-10) {
3054 dCoefs.push_back(a1 / a2);
3055 dCoefs.push_back(a0 / a2);
3056 dSol = RS_Math::quadraticSolver(dCoefs);
3057 }
3058 else if (std::abs(a1) > RS_TOLERANCE1.0e-10) {
3059 dSol.push_back(-a0 / a1);
3060 }
3061
3062 for (double& d : dSol) {
3063 if (d > -RS_TOLERANCE1.0e-10 && d < 1.0 + RS_TOLERANCE1.0e-10) {
3064 if (d < 0.0) {
3065 d = 0.0;
3066 }
3067 if (d > 1.0) {
3068 d = 1.0;
3069 }
3070 pVS->push_back(getQuadAtPoint(vx1, vc1, vx2, d));
3071 }
3072 }
3073}
3074
3075RS_VectorSolutions LC_SplinePoints::getQuadraticIntersect(const RS_Entity* e1) const {
3076 RS_VectorSolutions ret;
3077
3078 size_t n = m_data.controlPoints.size();
3079 if (n < 2) {
3080 return ret;
3081 }
3082
3083 LC_Quadratic lcQuad = e1->getQuadratic();
3084 std::vector<double> dQuadCoefs = lcQuad.getCoefficients();
3085
3086 RS_Vector vStart(false), vEnd(false), vControl(false);
3087
3088 if (m_data.closed) {
3089 if (n < 3) {
3090 return ret;
3091 }
3092
3093 vStart = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
3094 vControl = m_data.controlPoints.at(0);
3095 vEnd = (m_data.controlPoints.at(0) + m_data.controlPoints.at(1)) / 2.0;
3096
3097 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3098
3099 for (size_t i = 1; i < n - 1; i++) {
3100 vStart = vEnd;
3101 vControl = m_data.controlPoints.at(i);
3102 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
3103
3104 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3105 }
3106
3107 vStart = vEnd;
3108 vControl = m_data.controlPoints.at(n - 1);
3109 vEnd = (m_data.controlPoints.at(n - 1) + m_data.controlPoints.at(0)) / 2.0;
3110
3111 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3112 }
3113 else {
3114 vStart = m_data.controlPoints.at(0);
3115 vEnd = m_data.controlPoints.at(1);
3116 if (n < 3) {
3117 return getQuadraticLineIntersect(dQuadCoefs, vStart, vEnd);
3118 }
3119
3120 vControl = vEnd;
3121 vEnd = m_data.controlPoints.at(2);
3122 if (n < 4) {
3123 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3124 return ret;
3125 }
3126
3127 vEnd = (m_data.controlPoints.at(1) + m_data.controlPoints.at(2)) / 2.0;
3128 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3129
3130 for (size_t i = 2; i < n - 2; i++) {
3131 vStart = vEnd;
3132 vControl = m_data.controlPoints.at(i);
3133 vEnd = (m_data.controlPoints.at(i) + m_data.controlPoints.at(i + 1)) / 2.0;
3134
3135 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3136 }
3137
3138 vStart = vEnd;
3139 vControl = m_data.controlPoints.at(n - 2);
3140 vEnd = m_data.controlPoints.at(n - 1);
3141
3142 addQuadraticQuadIntersect(&ret, dQuadCoefs, vStart, vControl, vEnd);
3143 }
3144
3145 return ret;
3146}
3147
3148RS_VectorSolutions LC_SplinePoints::getIntersection(const RS_Entity* e1, const RS_Entity* e2) {
3149 if (e2 == nullptr || (e1 != nullptr && e1->rtti() != RS2::EntitySplinePoints)) {
3150 std::swap(e1, e2);
3151 }
3152 if (e1 == nullptr || e1->rtti() != RS2::EntitySplinePoints) {
3153 return {};
3154 }
3155
3156 const auto spline = static_cast<LC_SplinePoints*>(const_cast<RS_Entity*>(e1));
3157
3158 switch (e2->rtti()) {
3159 case RS2::EntityLine:
3160 return {spline->getLineIntersect(e2->getStartpoint(), e2->getEndpoint())};
3161 case RS2::EntitySplinePoints:
3162 return {spline->getSplinePointsIntersect(static_cast<LC_SplinePoints*>(const_cast<RS_Entity*>(e2)))};
3163 default:
3164 return {spline->getQuadraticIntersect(e2)};
3165 }
3166}
3167
3168RS2::EntityType LC_SplinePoints::rtti() const {
3169 return RS2::EntitySplinePoints;
3170}
3171
3172/** @return false */
3173bool LC_SplinePoints::isEdge() const {
3174 return true;
3175}
3176
3177/** @return Copy of m_data that defines the spline. */
3178const LC_SplinePointsData& LC_SplinePoints::getData() const {
3179 return m_data;
3180}
3181
3182/** @return Copy of m_data that defines the spline. */
3183LC_SplinePointsData& LC_SplinePoints::getData() {
3184 return m_data;
3185}
3186
3187/** @return Number of control points. */
3188size_t LC_SplinePoints::getNumberOfControlPoints() const {
3189 return m_data.controlPoints.size();
3190}
3191
3192/**
3193* @retval true if the spline is closed.
3194* @retval false otherwise.
3195*/
3196bool LC_SplinePoints::isClosed() const {
3197 return m_data.closed;
3198}
3199
3200/**
3201* Sets the closed flag of this spline.
3202*/
3203void LC_SplinePoints::setClosed(const bool c) {
3204 m_data.closed = c;
3205 update();
3206}
3207
3208/*void LC_SplinePoints::trimStartpoint(const RS_Vector& pos)
3209{
3210}
3211
3212void LC_SplinePoints::trimEndpoint(const RS_Vector& pos)
3213{
3214}*/
3215
3216LC_SplinePoints* LC_SplinePoints::cut(const RS_Vector& pos) {
3217 LC_SplinePoints* ret = nullptr;
3218
3219 double dt;
3220 const int iQuad = getNearestQuad(pos, nullptr, &dt);
3221 if (iQuad < 1) {
3222 return ret;
3223 }
3224
3225 RS_Vector vStart(false);
3226 RS_Vector vControl(false);
3227 RS_Vector vEnd(false);
3228
3229 const int iPts = getQuadPoints(iQuad, &vStart, &vControl, &vEnd);
3230 if (iPts < 2) {
3231 return ret;
3232 }
3233
3234 RS_Vector vPoint(false);
3235 if (iPts < 3) {
3236 vPoint = vStart * (1.0 - dt) + vEnd * dt;
3237 }
3238 else {
3239 vPoint = getQuadPoint(vStart, vControl, vEnd, dt);
3240 }
3241
3242 const size_t n = m_data.controlPoints.size();
3243
3244 RS_Vector vNewControl(false);
3245 if (m_data.closed) {
3246 // if the spline is closed, we must delete splinePoints, add the pos
3247 // as start and end point and reorder control points. We must return
3248 // nullptr since there will still be only one spline
3249 for (int i = 0; i < iQuad - 1; i++) {
3250 vNewControl = m_data.controlPoints.front();
3251 m_data.controlPoints.erase(m_data.controlPoints.begin());
3252 m_data.controlPoints.push_back(vNewControl);
3253 }
3254
3255 if (iPts > 2) {
3256 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, 0.0, dt);
3257 m_data.controlPoints.push_back(vNewControl);
3258
3259 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, dt, 1.0);
3260 m_data.controlPoints.front() = vNewControl;
3261 }
3262 m_data.controlPoints.push_back(vPoint);
3263 m_data.controlPoints.insert(m_data.controlPoints.begin(), vPoint);
3264
3265 m_data.closed = false;
3266 m_data.cut = true;
3267 }
3268 else {
3269 LC_SplinePointsData newData(false, true);
3270 for (size_t i = iQuad + 1; i < n; i++) {
3271 newData.controlPoints.push_back(m_data.controlPoints.at(iQuad + 1));
3272 m_data.controlPoints.erase(m_data.controlPoints.begin() + iQuad + 1);
3273 }
3274
3275 if (iPts > 2) {
3276 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, 0.0, dt);
3277 m_data.controlPoints[iQuad] = vNewControl;
3278
3279 vNewControl = getSubQuadControlPoint(vStart, vControl, vEnd, dt, 1.0);
3280 newData.controlPoints.insert(newData.controlPoints.begin(), vNewControl);
3281 }
3282 m_data.controlPoints.push_back(vPoint);
3283 newData.controlPoints.insert(newData.controlPoints.begin(), vPoint);
3284
3285 ret = new LC_SplinePoints(m_parent, newData);
3286
3287 m_data.cut = true;
3288 }
3289
3290 return ret;
3291}
3292
3293QPolygonF LC_SplinePoints::getBoundingRect(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2) {
3294 QPolygonF ret;
3295 ret << QPointF(x1.x, x1.y);
3296 //find t for tangent in parallel with x2 - x1
3297 const RS_Vector pt = (x1 - c1 * 2. + x2) * 2.;
3298 const RS_Vector pl = (x1 - x2);
3299 const double determinant = pt.x * pl.y - pt.y * pl.x;
3300 if (std::abs(determinant) < RS_TOLERANCE151.5e-15) {
3301 //bezier is a straight line
3302 ret << QPointF(x2.x, x2.y) << ret.front();
3303 return ret;
3304 }
3305 const RS_Vector pc = (x1 - c1) * 2.;
3306 const double t = (pc.x * pl.y - pc.y * pl.x) / determinant;
3307 const double tr = 1. - t;
3308 //offset from x1 to the extreme point
3309 const RS_Vector pext = x1 * (tr * tr - 1) + c1 * (2. * t * tr) + x2 * (t * t);
3310 //perpendicular offset from x1 to the extreme point, the component of the offset perpendicular to x1-x2
3311 const RS_Vector dp = pext - pl * (pext.dotP(pl) / pl.squared());
3312 RS_Vector v1 = x1 + dp;
3313 ret << QPointF(v1.x, v1.y);
3314 v1 = x2 + dp;
3315 ret << QPointF(v1.x, v1.y);
3316 ret << ret.front();
3317 return ret;
3318}
3319
3320/**
3321 * @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
3322 * Contour Area = \oint x dy
3323 * @return line integral \oint x dy along the spline entity
3324 * @author Dongxu Li
3325 */
3326double LC_SplinePoints::areaLineIntegral() const {
3327 size_t n = m_data.controlPoints.size();
3328 if (n < 2) {
3329 return 0.0;
3330 }
3331
3332 auto quadAreaIntegral = [](const RS_Vector& p0, const RS_Vector& p1, const RS_Vector& p2) -> double {
3333 const double x0 = p0.x;
3334 const double y0 = p0.y;
3335 const double x1 = p1.x;
3336 const double y1 = p1.y;
3337 const double x2 = p2.x;
3338 const double y2 = p2.y;
3339
3340 const double A = 2.0 * (y1 - y0);
3341 const double B = 2.0 * (y2 - 2.0 * y1 + y0);
3342 const double C = x0;
3343 const double D = 2.0 * (x1 - x0);
3344 const double E = x0 - 2.0 * x1 + x2;
3345
3346 const double int1 = C * A;
3347 const double int2 = (C * B + D * A) / 2.0;
3348 const double int3 = (D * B + E * A) / 3.0;
3349 const double int4 = E * B / 4.0;
3350
3351 return int1 + int2 + int3 + int4;
3352 };
3353
3354 double res = 0.0;
3355 RS_Vector vStart, vControl, vEnd;
3356
3357 if (!m_data.closed) {
3358 if (n == 2) {
3359 RS_Vector s = getStartpoint();
3360 RS_Vector e = getEndpoint();
3361 return (s.x + e.x) / 2.0 * (e.y - s.y);
3362 }
3363
3364 // n >= 3
3365 vStart = m_data.controlPoints[0];
3366 vControl = m_data.controlPoints[1];
3367 if (n == 3) {
3368 vEnd = m_data.controlPoints[2];
3369 return quadAreaIntegral(vStart, vControl, vEnd);
3370 }
3371
3372 vEnd = (m_data.controlPoints[1] + m_data.controlPoints[2]) / 2.0;
3373 res += quadAreaIntegral(vStart, vControl, vEnd);
3374
3375 for (size_t i = 2; i < n - 2; ++i) {
3376 vStart = vEnd;
3377 vControl = m_data.controlPoints[i];
3378 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i + 1]) / 2.0;
3379 res += quadAreaIntegral(vStart, vControl, vEnd);
3380 }
3381
3382 vStart = vEnd;
3383 vControl = m_data.controlPoints[n - 2];
3384 vEnd = m_data.controlPoints[n - 1];
3385 res += quadAreaIntegral(vStart, vControl, vEnd);
3386 }
3387 else {
3388 // closed, n >= 3
3389 if (n < 3) {
3390 return 0.0;
3391 }
3392
3393 vStart = (m_data.controlPoints[n - 1] + m_data.controlPoints[0]) / 2.0;
3394 vControl = m_data.controlPoints[0];
3395 vEnd = (m_data.controlPoints[0] + m_data.controlPoints[1]) / 2.0;
3396 res += quadAreaIntegral(vStart, vControl, vEnd);
3397
3398 for (size_t i = 1; i < n - 1; ++i) {
3399 vStart = vEnd;
3400 vControl = m_data.controlPoints[i];
3401 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i + 1]) / 2.0;
3402 res += quadAreaIntegral(vStart, vControl, vEnd);
3403 }
3404
3405 vStart = vEnd;
3406 vControl = m_data.controlPoints[n - 1];
3407 vEnd = (m_data.controlPoints[n - 1] + m_data.controlPoints[0]) / 2.0;
3408 res += quadAreaIntegral(vStart, vControl, vEnd);
3409 }
3410
3411 return res;
3412}
3413
3414LC_SecondMoment LC_SplinePoints::secondMomentLineIntegral() const {
3415 // 5-point Gauss-Legendre quadrature on [0,1] — exact for polynomial degree ≤ 9.
3416 // Each quadratic Bézier segment has degree-7 integrands (x³y'/3 etc.),
3417 // so this gives the exact result per segment.
3418 static constexpr double t5[] = {
3419 0.04691007703067, 0.23076534494716, 0.5,
3420 0.76923465505284, 0.95308992296933
3421 };
3422 static constexpr double w5[] = {
3423 0.11846344252810, 0.23931433524969, 0.28444444444444,
3424 0.23931433524969, 0.11846344252810
3425 };
3426
3427 // Helper: integrate second moments over one quadratic Bézier segment P0,P1,P2
3428 auto segMoment = [&](const RS_Vector& p0, const RS_Vector& p1, const RS_Vector& p2) -> LC_SecondMoment {
3429 LC_SecondMoment m;
3430 for (int i = 0; i < 5; ++i) {
3431 const double t = t5[i];
3432 const double mt = 1.0 - t;
3433 const double x = mt*mt*p0.x + 2.0*mt*t*p1.x + t*t*p2.x;
3434 const double y = mt*mt*p0.y + 2.0*mt*t*p1.y + t*t*p2.y;
3435 const double dxdt = 2.0*(mt*(p1.x-p0.x) + t*(p2.x-p1.x));
3436 const double dydt = 2.0*(mt*(p1.y-p0.y) + t*(p2.y-p1.y));
3437 m.ixx += w5[i] * x*x*x / 3.0 * dydt;
3438 m.iyy += w5[i] * (-y*y*y / 3.0) * dxdt;
3439 m.ixy += w5[i] * x*x * y / 2.0 * dydt;
3440 }
3441 return m;
3442 };
3443
3444 // Replicate the segment decomposition from areaLineIntegral()
3445 size_t n = m_data.controlPoints.size();
3446 if (n < 2) {
3447 return {};
3448 }
3449
3450 LC_SecondMoment res;
3451 RS_Vector vStart, vControl, vEnd;
3452
3453 if (!m_data.closed) {
3454 if (n == 2) {
3455 // Degenerate: straight line
3456 RS_Vector s = getStartpoint();
3457 RS_Vector e = getEndpoint();
3458 RS_Line line(nullptr, RS_LineData{s, e});
3459 return line.secondMomentLineIntegral();
3460 }
3461 vStart = m_data.controlPoints[0];
3462 vControl = m_data.controlPoints[1];
3463 if (n == 3) {
3464 vEnd = m_data.controlPoints[2];
3465 return segMoment(vStart, vControl, vEnd);
3466 }
3467 vEnd = (m_data.controlPoints[1] + m_data.controlPoints[2]) / 2.0;
3468 res += segMoment(vStart, vControl, vEnd);
3469
3470 for (size_t i = 2; i < n - 2; ++i) {
3471 vStart = vEnd;
3472 vControl = m_data.controlPoints[i];
3473 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i+1]) / 2.0;
3474 res += segMoment(vStart, vControl, vEnd);
3475 }
3476 vStart = vEnd;
3477 vControl = m_data.controlPoints[n-2];
3478 vEnd = m_data.controlPoints[n-1];
3479 res += segMoment(vStart, vControl, vEnd);
3480 } else {
3481 if (n < 3) {
3482 return {};
3483 }
3484 vStart = (m_data.controlPoints[n-1] + m_data.controlPoints[0]) / 2.0;
3485 vControl = m_data.controlPoints[0];
3486 vEnd = (m_data.controlPoints[0] + m_data.controlPoints[1]) / 2.0;
3487 res += segMoment(vStart, vControl, vEnd);
3488
3489 for (size_t i = 1; i < n - 1; ++i) {
3490 vStart = vEnd;
3491 vControl = m_data.controlPoints[i];
3492 vEnd = (m_data.controlPoints[i] + m_data.controlPoints[i+1]) / 2.0;
3493 res += segMoment(vStart, vControl, vEnd);
3494 }
3495 vStart = vEnd;
3496 vControl = m_data.controlPoints[n-1];
3497 vEnd = (m_data.controlPoints[n-1] + m_data.controlPoints[0]) / 2.0;
3498 res += segMoment(vStart, vControl, vEnd);
3499 }
3500 return res;
3501}