1 /************************************************************************
2  **
3  **  @file   vabstractcubicbezierpath.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   16 3, 2016
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) 2016 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 "vabstractcubicbezierpath.h"
30 #include "vsplinepoint.h"
31 
32 #include <QPainterPath>
33 
34 #include "../vmisc/def.h"
35 #include "../ifc/ifcdef.h"
36 #include "../ifc/exception/vexception.h"
37 #include "vpointf.h"
38 #include "vspline.h"
39 
40 //---------------------------------------------------------------------------------------------------------------------
VAbstractCubicBezierPath(const GOType & type,const quint32 & idObject,const Draw & mode)41 VAbstractCubicBezierPath::VAbstractCubicBezierPath(const GOType &type, const quint32 &idObject, const Draw &mode)
42     : VAbstractBezier(type, idObject, mode)
43 {
44 }
45 
46 //---------------------------------------------------------------------------------------------------------------------
VAbstractCubicBezierPath(const VAbstractCubicBezierPath & curve)47 VAbstractCubicBezierPath::VAbstractCubicBezierPath(const VAbstractCubicBezierPath &curve)
48     : VAbstractBezier(curve)
49 {
50 }
51 
52 //---------------------------------------------------------------------------------------------------------------------
operator =(const VAbstractCubicBezierPath & curve)53 VAbstractCubicBezierPath &VAbstractCubicBezierPath::operator=(const VAbstractCubicBezierPath &curve)
54 {
55     if ( &curve == this )
56     {
57         return *this;
58     }
59     VAbstractBezier::operator=(curve);
60     return *this;
61 }
62 
63 //---------------------------------------------------------------------------------------------------------------------
~VAbstractCubicBezierPath()64 VAbstractCubicBezierPath::~VAbstractCubicBezierPath()
65 {
66 }
67 
68 //---------------------------------------------------------------------------------------------------------------------
69 /**
70  * @brief GetPath return QPainterPath which reprezent spline path.
71  * @return path.
72  */
GetPath() const73 QPainterPath VAbstractCubicBezierPath::GetPath() const
74 {
75     QPainterPath painterPath;
76     for (qint32 i = 1; i <= CountSubSpl(); ++i)
77     {
78         painterPath.addPath(GetSpline(i).GetPath());
79     }
80     return painterPath;
81 }
82 
83 //---------------------------------------------------------------------------------------------------------------------
84 /**
85  * @brief GetPathPoints return list of points what located on path.
86  * @return list.
87  */
GetPoints() const88 QVector<QPointF> VAbstractCubicBezierPath::GetPoints() const
89 {
90     QVector<QPointF> pathPoints;
91     for (qint32 i = 1; i <= CountSubSpl(); ++i)
92     {
93         if (not pathPoints.isEmpty())
94         {
95             pathPoints.removeLast();
96         }
97 
98         pathPoints += GetSpline(i).GetPoints();
99     }
100     return pathPoints;
101 }
102 
103 //---------------------------------------------------------------------------------------------------------------------
104 /**
105  * @brief GetLength return length of spline path.
106  * @return length.
107  */
GetLength() const108 qreal VAbstractCubicBezierPath::GetLength() const
109 {
110     qreal length = 0;
111     for (qint32 i = 1; i <= CountSubSpl(); ++i)
112     {
113         length += GetSpline(i).GetLength();
114     }
115     return length;
116 }
117 
118 //---------------------------------------------------------------------------------------------------------------------
DirectionArrows() const119 QVector<DirectionArrow> VAbstractCubicBezierPath::DirectionArrows() const
120 {
121     QVector<DirectionArrow> arrows;
122     for (qint32 i = 1; i <= CountSubSpl(); ++i)
123     {
124         arrows += GetSpline(i).DirectionArrows();
125     }
126     return arrows;
127 }
128 
129 //---------------------------------------------------------------------------------------------------------------------
Segment(const QPointF & p) const130 int VAbstractCubicBezierPath::Segment(const QPointF &p) const
131 {
132     int index = -1;
133     for (qint32 i = 1; i <= CountSubSpl(); ++i)
134     {
135         const qreal t = GetSpline(i).ParamT(p);
136         if (qFuzzyIsNull(t) || not qFuzzyCompare(t, -1))
137         {
138             index = i;
139             break;
140         }
141     }
142     return index;
143 }
144 
145 //---------------------------------------------------------------------------------------------------------------------
146 /**
147  * @brief CutSplinePath cut spline path into two. This method don't return two spline path. You must create spline
148  * paths by yourself.
149  * Example:
150  * QPointF spl1p2, spl1p3, spl2p2, spl2p3;
151  * qint32 p1 = 0, p2 = 0;
152  * QPointF point = splPath->CutSplinePath(length, p1, p2, spl1p2, spl1p3, spl2p2, spl2p3);
153  *
154  * VSplinePoint splP1 = splPath->at(p1);
155  * VSplinePoint splP2 = splPath->at(p2);
156  * VSpline spl1 = VSpline(splP1.P(), spl1p2, spl1p3, *p, splPath->GetKCurve());
157  * VSpline spl2 = VSpline(*p, spl2p2, spl2p3, splP2.P(), splPath->GetKCurve());
158  * @param length length first spline path.
159  * @param p1 index first spline point in list.
160  * @param p2 index second spline point in list.
161  * @param spl1p2 first control point first spline.
162  * @param spl1p3 second control point first spline.
163  * @param spl2p2 first control point second spline.
164  * @param spl2p3 second control point second spline.
165  * @return cutting point.
166  */
CutSplinePath(qreal length,qint32 & p1,qint32 & p2,QPointF & spl1p2,QPointF & spl1p3,QPointF & spl2p2,QPointF & spl2p3) const167 QPointF VAbstractCubicBezierPath::CutSplinePath(qreal length, qint32 &p1, qint32 &p2, QPointF &spl1p2, QPointF &spl1p3,
168                                                 QPointF &spl2p2, QPointF &spl2p3) const
169 {
170     if (CountSubSpl() < 1)
171     {
172         throw VException(tr("Can't cut this spline"));
173     }
174 
175     //Always need return two spline paths, so we must correct wrong length.
176     const qreal minLength = ToPixel(1, Unit::Mm);
177     qreal fullLength = GetLength();
178 
179     if (fullLength <= minLength)
180     {
181         p1 = p2 = -1;
182         spl1p2 = spl1p3 = spl2p2 = spl2p3 = QPointF();
183         return QPointF();
184     }
185 
186     const qreal maxLength = fullLength - minLength;
187 
188     if (length < minLength)
189     {
190         length = minLength;
191     }
192     else if (length > maxLength)
193     {
194         length = maxLength;
195     }
196 
197     fullLength = 0;
198     for (qint32 i = 1; i <= CountSubSpl(); ++i)
199     {
200         const VSpline spl = GetSpline(i);
201         const qreal splLength = spl.GetLength();
202         fullLength += splLength;
203         if (fullLength > length)
204         {
205             p1 = i-1;
206             p2 = i;
207             const QPointF point = spl.CutSpline(length - (fullLength - splLength), spl1p2, spl1p3, spl2p2, spl2p3);
208 
209             const QVector<VSplinePoint> points = GetSplinePath();
210 
211             if (p1 > 0)
212             {
213                 const VSplinePoint splP1 = points.at(p1);
214                 QLineF line(splP1.P().toQPointF(), spl1p2);
215                 if (qFuzzyIsNull(line.length()))
216                 {
217                     spl1p2.rx() += ToPixel(0.1, Unit::Mm);
218                     QLineF line(splP1.P().toQPointF(), spl1p2);
219                     line.setLength(ToPixel(0.1, Unit::Mm));
220                     line.setAngle(splP1.Angle1()+180);
221                     spl1p2 = line.p2();
222                 }
223             }
224 
225             if (p2 < points.size() - 1)
226             {
227                 const VSplinePoint splP2 = points.at(p2);
228                 QLineF line(splP2.P().toQPointF(), spl2p3);
229                 if (qFuzzyIsNull(line.length()))
230                 {
231                     spl2p3.rx() += ToPixel(0.1, Unit::Mm);
232                     QLineF line(splP2.P().toQPointF(), spl2p3);
233                     line.setAngle(splP2.Angle2()+180);
234                     spl2p3 = line.p2();
235                 }
236             }
237 
238             return point;
239         }
240     }
241     p1 = p2 = -1;
242     spl1p2 = spl1p3 = spl2p2 = spl2p3 = QPointF();
243     return QPointF();
244 }
245 
246 //---------------------------------------------------------------------------------------------------------------------
247 /**
248  * @brief NameForHistory helps to create name for dialog History.
249  * @param toolName first part of name. Like 'Spline path' or 'Cubic Bezier path'.
250  * @return name of curve for history records.
251  */
NameForHistory(const QString & toolName) const252 QString VAbstractCubicBezierPath::NameForHistory(const QString &toolName) const
253 {
254     QString name = toolName;
255     if (CountPoints() > 0)
256     {
257         name += QString(" %1").arg(FirstPoint().name());
258         if (CountSubSpl() >= 1)
259         {
260             name += QString("_%1").arg(LastPoint().name());
261         }
262 
263         if (GetDuplicate() > 0)
264         {
265             name += QString("_%1").arg(GetDuplicate());
266         }
267     }
268 
269     QString alias;
270 
271     if (not GetAliasSuffix().isEmpty())
272     {
273         alias = QString("%1 %2").arg(toolName, GetAliasSuffix());
274     }
275 
276     return not alias.isEmpty() ? QString("%1 (%2)").arg(alias, name) : name;
277 }
278 
279 //---------------------------------------------------------------------------------------------------------------------
CreateName()280 void VAbstractCubicBezierPath::CreateName()
281 {
282     QString name;
283     if (CountPoints() > 0)
284     {
285         name = splPath;
286         name.append(QString("_%1").arg(FirstPoint().name()));
287         if (CountSubSpl() >= 1)
288         {
289             name.append(QString("_%1").arg(LastPoint().name()));
290 
291             if (GetDuplicate() > 0)
292             {
293                 name += QString("_%1").arg(GetDuplicate());
294             }
295         }
296     }
297     setName(name);
298 }
299 
300 //---------------------------------------------------------------------------------------------------------------------
CreateAlias()301 void VAbstractCubicBezierPath::CreateAlias()
302 {
303     const QString aliasSuffix = GetAliasSuffix();
304     if (aliasSuffix.isEmpty())
305     {
306         SetAlias(QString());
307         return;
308     }
309 
310     SetAlias(splPath + '_' + aliasSuffix);
311 }
312