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