1 /************************************************************************
2  **
3  **  @file   vsplinepath.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   November 15, 2013
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2013-2015 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "vsplinepath.h"
30 
31 #include <QJsonArray>
32 #include <QJsonObject>
33 #include <QPoint>
34 
35 #include "../ifc/exception/vexception.h"
36 #include "../vmisc/vmath.h"
37 #include "../vmisc/compatibility.h"
38 #include "vabstractcurve.h"
39 #include "vsplinepath_p.h"
40 
41 //---------------------------------------------------------------------------------------------------------------------
42 /**
43  * @brief VSplinePath constructor.
44  * @param idObject parent id.
45  * @param mode mode creation spline path.
46  */
VSplinePath(quint32 idObject,Draw mode)47 VSplinePath::VSplinePath(quint32 idObject, Draw mode)
48     : VAbstractCubicBezierPath(GOType::SplinePath, idObject, mode),
49       d(new VSplinePathData())
50 {}
51 
52 //---------------------------------------------------------------------------------------------------------------------
VSplinePath(const QVector<VFSplinePoint> & points,qreal kCurve,quint32 idObject,Draw mode)53 VSplinePath::VSplinePath(const QVector<VFSplinePoint> &points, qreal kCurve, quint32 idObject, Draw mode)
54     : VAbstractCubicBezierPath(GOType::SplinePath, idObject, mode),
55       d(new VSplinePathData())
56 {
57     if (points.size() < 3)
58     {
59         return;
60     }
61 
62     QVector<VSplinePoint> newPoints(points.size());
63     for (qint32 i = 1; i <= points.size()-1; ++i)
64     {
65         const VFSplinePoint &p1 = points.at(i-1);
66         const VFSplinePoint &p2 = points.at(i);
67         VSpline spl(p1.P(), p2.P(), p1.Angle2(), p2.Angle1(), p1.KAsm2(), p2.KAsm1(), kCurve);
68 
69         newPoints[i-1].SetP(p1.P());
70         newPoints[i-1].SetAngle2(p1.Angle2(), spl.GetStartAngleFormula());
71         newPoints[i-1].SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula());
72 
73         newPoints[i].SetP(p2.P());
74         newPoints[i].SetAngle1(p2.Angle1(), spl.GetEndAngleFormula());
75         newPoints[i].SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula());
76     }
77 
78     d->path = newPoints;
79     CreateName();
80 }
81 
82 //---------------------------------------------------------------------------------------------------------------------
VSplinePath(const QVector<VSplinePoint> & points,quint32 idObject,Draw mode)83 VSplinePath::VSplinePath(const QVector<VSplinePoint> &points, quint32 idObject, Draw mode)
84     : VAbstractCubicBezierPath(GOType::SplinePath, idObject, mode),
85       d(new VSplinePathData())
86 {
87     if (points.isEmpty())
88     {
89         return;
90     }
91 
92     d->path = points;
93     CreateName();
94 }
95 
96 //---------------------------------------------------------------------------------------------------------------------
97 /**
98  * @brief VSplinePath copy constructor.
99  * @param splPath spline path.
100  */
VSplinePath(const VSplinePath & splPath)101 VSplinePath::VSplinePath(const VSplinePath &splPath)
102     : VAbstractCubicBezierPath(splPath),
103       d(splPath.d)
104 {}
105 
106 //---------------------------------------------------------------------------------------------------------------------
Rotate(const QPointF & originPoint,qreal degrees,const QString & prefix) const107 VSplinePath VSplinePath::Rotate(const QPointF &originPoint, qreal degrees, const QString &prefix) const
108 {
109     QVector<VSplinePoint> newPoints(CountPoints());
110     for (qint32 i = 1; i <= CountSubSpl(); ++i)
111     {
112         const VSpline spl = GetSpline(i).Rotate(originPoint, degrees);
113 
114         newPoints[i-1].SetP(spl.GetP1());
115         newPoints[i-1].SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula());
116         newPoints[i-1].SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula());
117 
118         newPoints[i].SetP(spl.GetP4());
119         newPoints[i].SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula());
120         newPoints[i].SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula());
121     }
122 
123     VSplinePath splPath(newPoints);
124     splPath.setName(name() + prefix);
125 
126     if (not GetAliasSuffix().isEmpty())
127     {
128         splPath.SetAliasSuffix(GetAliasSuffix() + prefix);
129     }
130 
131     splPath.SetColor(GetColor());
132     splPath.SetPenStyle(GetPenStyle());
133     splPath.SetApproximationScale(GetApproximationScale());
134     return splPath;
135 }
136 
137 //---------------------------------------------------------------------------------------------------------------------
Flip(const QLineF & axis,const QString & prefix) const138 VSplinePath VSplinePath::Flip(const QLineF &axis, const QString &prefix) const
139 {
140     QVector<VSplinePoint> newPoints(CountPoints());
141     for (qint32 i = 1; i <= CountSubSpl(); ++i)
142     {
143         const VSpline spl = GetSpline(i).Flip(axis);
144 
145         newPoints[i-1].SetP(spl.GetP1());
146         newPoints[i-1].SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula());
147         newPoints[i-1].SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula());
148 
149         newPoints[i].SetP(spl.GetP4());
150         newPoints[i].SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula());
151         newPoints[i].SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula());
152     }
153 
154     VSplinePath splPath(newPoints);
155     splPath.setName(name() + prefix);
156 
157     if (not GetAliasSuffix().isEmpty())
158     {
159         splPath.SetAliasSuffix(GetAliasSuffix() + prefix);
160     }
161 
162     splPath.SetColor(GetColor());
163     splPath.SetPenStyle(GetPenStyle());
164     splPath.SetApproximationScale(GetApproximationScale());
165     return splPath;
166 }
167 
168 //---------------------------------------------------------------------------------------------------------------------
Move(qreal length,qreal angle,const QString & prefix) const169 VSplinePath VSplinePath::Move(qreal length, qreal angle, const QString &prefix) const
170 {
171     QVector<VSplinePoint> newPoints(CountPoints());
172     for (qint32 i = 1; i <= CountSubSpl(); ++i)
173     {
174         const VSpline spl = GetSpline(i).Move(length, angle);
175 
176         newPoints[i-1].SetP(spl.GetP1());
177         newPoints[i-1].SetAngle2(spl.GetStartAngle(), spl.GetStartAngleFormula());
178         newPoints[i-1].SetLength2(spl.GetC1Length(), spl.GetC1LengthFormula());
179 
180         newPoints[i].SetP(spl.GetP4());
181         newPoints[i].SetAngle1(spl.GetEndAngle(), spl.GetEndAngleFormula());
182         newPoints[i].SetLength1(spl.GetC2Length(), spl.GetC2LengthFormula());
183     }
184 
185     VSplinePath splPath(newPoints);
186     splPath.setName(name() + prefix);
187 
188     if (not GetAliasSuffix().isEmpty())
189     {
190         splPath.SetAliasSuffix(GetAliasSuffix() + prefix);
191     }
192 
193     splPath.SetColor(GetColor());
194     splPath.SetPenStyle(GetPenStyle());
195     splPath.SetApproximationScale(GetApproximationScale());
196     return splPath;
197 }
198 
199 //---------------------------------------------------------------------------------------------------------------------
~VSplinePath()200 VSplinePath::~VSplinePath()
201 {}
202 
203 //---------------------------------------------------------------------------------------------------------------------
204 /**
205  * @brief append add point in the end of list points.
206  * @param point new point.
207  */
append(const VSplinePoint & point)208 void VSplinePath::append(const VSplinePoint &point)
209 {
210     if (d->path.size() > 0 && static_cast<QPointF>(d->path.last().P()) == static_cast<QPointF>(point.P())) //-V807
211     {
212         return;
213     }
214 
215     d->path.append(point);
216     CreateName();
217 }
218 
219 //---------------------------------------------------------------------------------------------------------------------
220 /**
221  * @brief CountSubSpl return count of simple splines.
222  * @return count.
223  */
CountSubSpl() const224 qint32 VSplinePath::CountSubSpl() const
225 {
226     if (d->path.isEmpty())
227     {
228         return 0;
229     }
230     else
231     {
232         return d->path.size() - 1;
233     }
234 }
235 
236 //---------------------------------------------------------------------------------------------------------------------
237 /**
238  * @brief GetSpline return spline by index.
239  * @param index index spline in spline path.
240  * @return spline
241  */
GetSpline(qint32 index) const242 VSpline VSplinePath::GetSpline(qint32 index) const
243 {
244     if (CountPoints()<1)
245     {
246         throw VException(tr("Not enough points to create the spline."));
247     }
248 
249     if (index < 1 || index > CountSubSpl())
250     {
251         throw VException(tr("This spline does not exist."));
252     }
253 
254     const VSplinePoint &p1 = d->path.at(index-1);
255     const VSplinePoint &p2 = d->path.at(index);
256     VSpline spl(p1.P(), p2.P(), p1.Angle2(), p1.Angle2Formula(), p2.Angle1(), p2.Angle1Formula(), p1.Length2(),
257                 p1.Length2Formula(), p2.Length1(), p2.Length1Formula(), 1);
258     spl.SetApproximationScale(GetApproximationScale());
259     return spl;
260 }
261 
262 //---------------------------------------------------------------------------------------------------------------------
263 /**
264  * @brief UpdatePoint update spline point in list.
265  * @param indexSpline spline index in list.
266  * @param pos position point in spline.
267  * @param point point.
268  */
UpdatePoint(qint32 indexSpline,const SplinePointPosition & pos,const VSplinePoint & point)269 void VSplinePath::UpdatePoint(qint32 indexSpline, const SplinePointPosition &pos, const VSplinePoint &point)
270 {
271     if (indexSpline < 1 || indexSpline > CountSubSpl())
272     {
273         throw VException(tr("This spline does not exist."));
274     }
275     if (pos == SplinePointPosition::FirstPoint)
276     {
277         d->path[indexSpline-1] = point;
278     }
279     else
280     {
281         d->path[indexSpline] = point;
282     }
283 }
284 
285 //---------------------------------------------------------------------------------------------------------------------
286 /**
287  * @brief GetSplinePoint return spline point from list.
288  * @param indexSpline spline index in list.
289  * @param pos position point in spline.
290  * @return spline point.
291  */
GetSplinePoint(qint32 indexSpline,SplinePointPosition pos) const292 VSplinePoint VSplinePath::GetSplinePoint(qint32 indexSpline, SplinePointPosition pos) const
293 {
294     if (indexSpline < 1 || indexSpline > CountSubSpl())
295     {
296         throw VException(tr("This spline does not exist."));
297     }
298     if (pos == SplinePointPosition::FirstPoint)
299     {
300         return d->path.at(indexSpline-1);
301     }
302     else
303     {
304         return d->path.at(indexSpline);
305     }
306 }
307 
308 //---------------------------------------------------------------------------------------------------------------------
309 /**
310  * @brief operator = assignment operator.
311  * @param path spline path.
312  * @return spline path.
313  */
operator =(const VSplinePath & path)314 VSplinePath &VSplinePath::operator =(const VSplinePath &path)
315 {
316     if ( &path == this )
317     {
318         return *this;
319     }
320     VAbstractCubicBezierPath::operator=(path);
321     d = path.d;
322     return *this;
323 }
324 
325 #ifdef Q_COMPILER_RVALUE_REFS
326 //---------------------------------------------------------------------------------------------------------------------
VSplinePath(const VSplinePath && splPath)327 VSplinePath::VSplinePath(const VSplinePath &&splPath) Q_DECL_NOTHROW
328     : VAbstractCubicBezierPath(splPath),
329       d(splPath.d)
330 {}
331 
332 //---------------------------------------------------------------------------------------------------------------------
operator =(VSplinePath && path)333 VSplinePath &VSplinePath::operator=(VSplinePath &&path) Q_DECL_NOTHROW
334 {
335     VAbstractCubicBezierPath::operator=(path);
336     std::swap(d, path.d);
337     return *this;
338 }
339 #endif
340 
341 //---------------------------------------------------------------------------------------------------------------------
342 /**
343  * @brief operator [] return spline point by index.
344  * @param indx index in list.
345  * @return spline point.
346  */
operator [](int indx)347 VSplinePoint & VSplinePath::operator[](int indx)
348 {
349     return d->path[indx];
350 }
351 
352 //---------------------------------------------------------------------------------------------------------------------
353 /**
354  * @brief at return spline point by index.
355  * @param indx index in list.
356  * @return spline point.
357  */
at(int indx) const358 const VSplinePoint &VSplinePath::at(int indx) const
359 {
360     return d->path[indx];
361 }
362 
363 //---------------------------------------------------------------------------------------------------------------------
ToJson() const364 QJsonObject VSplinePath::ToJson() const
365 {
366     QJsonObject object = VAbstractCubicBezierPath::ToJson();
367     object[QLatin1String("aScale")] = GetApproximationScale();
368 
369     QJsonArray nodesArray;
370     for (auto &node: d->path)
371     {
372         nodesArray.append(node.ToJson());
373     }
374     object[QLatin1String("nodes")] = nodesArray;
375 
376     return object;
377 }
378 
379 //---------------------------------------------------------------------------------------------------------------------
GetStartAngle() const380 qreal VSplinePath::GetStartAngle() const
381 {
382     return CountPoints() > 0 ? ConstFirst (GetSplinePath()).Angle2() : 0;
383 }
384 
385 //---------------------------------------------------------------------------------------------------------------------
GetEndAngle() const386 qreal VSplinePath::GetEndAngle() const
387 {
388     if (CountPoints() > 0)
389     {
390         return ConstLast(GetSplinePath()).Angle1();
391     }
392     else
393     {
394         return 0;
395     }
396 }
397 
398 //---------------------------------------------------------------------------------------------------------------------
GetC1Length() const399 qreal VSplinePath::GetC1Length() const
400 {
401     return CountPoints() > 0 ? ConstFirst (GetSplinePath()).Length2() : 0;
402 }
403 
404 //---------------------------------------------------------------------------------------------------------------------
GetC2Length() const405 qreal VSplinePath::GetC2Length() const
406 {
407     return CountPoints() > 0 ? ConstFirst (GetSplinePath()).Length1() : 0;
408 }
409 
410 //---------------------------------------------------------------------------------------------------------------------
FirstPoint() const411 VPointF VSplinePath::FirstPoint() const
412 {
413     return not d->path.isEmpty() ? ConstFirst (d->path).P() : VPointF();
414 }
415 
416 //---------------------------------------------------------------------------------------------------------------------
LastPoint() const417 VPointF VSplinePath::LastPoint() const
418 {
419     const qint32 count = CountSubSpl();
420     return count >= 1 ? d->path.at(count).P() :// Take last point of the last real spline
421                         VPointF();
422 }
423 
424 //---------------------------------------------------------------------------------------------------------------------
425 /**
426  * @brief CountPoints return count of points.
427  * @return count.
428  */
CountPoints() const429 qint32 VSplinePath::CountPoints() const
430 {
431     return d->path.size();
432 }
433 
434 //---------------------------------------------------------------------------------------------------------------------
435 /**
436  * @brief GetSplinePath return list with spline points.
437  * @return list.
438  */
GetSplinePath() const439 QVector<VSplinePoint> VSplinePath::GetSplinePath() const
440 {
441     return d->path;
442 }
443 
444 //---------------------------------------------------------------------------------------------------------------------
GetFSplinePath() const445 QVector<VFSplinePoint> VSplinePath::GetFSplinePath() const
446 {
447     QVector<VFSplinePoint> points;
448     points.reserve(d->path.size());
449 
450     for (qint32 i = 1; i <= CountSubSpl(); ++i)
451     {
452         const VSplinePoint &p1 = d->path.at(i-1);
453         const VSplinePoint &p2 = d->path.at(i);
454         VSpline spl(p1.P(), p2.P(), p1.Angle2(), p1.Angle2Formula(), p2.Angle1(), p2.Angle1Formula(), p1.Length2(),
455                     p1.Length2Formula(), p2.Length1(), p2.Length1Formula(), 1);
456 
457         points[i-1].SetP(p1.P());
458         points[i-1].SetAngle2(p1.Angle2());
459         points[i-1].SetKAsm2(spl.GetKasm1());
460 
461         points[i].SetP(p2.P());
462         points[i].SetAngle1(p2.Angle1());
463         points[i].SetKAsm1(spl.GetKasm2());
464     }
465 
466     return points;
467 }
468 
469 //---------------------------------------------------------------------------------------------------------------------
470 /**
471  * @brief Clear clear list of points.
472  */
Clear()473 void VSplinePath::Clear()
474 {
475     d->path.clear();
476     SetDuplicate(0);
477 }
478