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