1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "cubicsegment.h"
27 
28 #include <qmath.h>
29 #include <QtDebug>
30 
31 
32 namespace QmlDesigner {
33 
34 CubicSegment::CubicSegment() = default;
35 
create()36 CubicSegment CubicSegment::create()
37 {
38     CubicSegment cubicSegment;
39     cubicSegment.d = new CubicSegmentData;
40 
41     return cubicSegment;
42 }
43 
setModelNode(const ModelNode & modelNode)44 void CubicSegment::setModelNode(const ModelNode &modelNode)
45 {
46     d->modelNode = modelNode;
47 }
48 
modelNode() const49 ModelNode CubicSegment::modelNode() const
50 {
51     return d->modelNode;
52 }
53 
setFirstControlPoint(const ControlPoint & firstControlPoint)54 void CubicSegment::setFirstControlPoint(const ControlPoint &firstControlPoint)
55 {
56     d->firstControllPoint = firstControlPoint;
57 }
58 
setFirstControlPoint(double x,double y)59 void CubicSegment::setFirstControlPoint(double x, double y)
60 {
61     d->firstControllPoint.setX(x);
62     d->firstControllPoint.setY(y);
63 }
64 
setFirstControlPoint(const QPointF & coordiante)65 void CubicSegment::setFirstControlPoint(const QPointF &coordiante)
66 {
67     d->firstControllPoint.setCoordinate(coordiante);
68 }
69 
setSecondControlPoint(const ControlPoint & secondControlPoint)70 void CubicSegment::setSecondControlPoint(const ControlPoint &secondControlPoint)
71 {
72     d->secondControllPoint = secondControlPoint;
73     d->secondControllPoint.setPathElementModelNode(d->modelNode);
74     d->secondControllPoint.setPointType(FirstControlPoint);
75 }
76 
setSecondControlPoint(double x,double y)77 void CubicSegment::setSecondControlPoint(double x, double y)
78 {
79     d->secondControllPoint.setX(x);
80     d->secondControllPoint.setY(y);
81     d->secondControllPoint.setPathElementModelNode(d->modelNode);
82     d->secondControllPoint.setPointType(FirstControlPoint);
83 }
84 
setSecondControlPoint(const QPointF & coordiante)85 void CubicSegment::setSecondControlPoint(const QPointF &coordiante)
86 {
87     d->secondControllPoint.setCoordinate(coordiante);
88     d->secondControllPoint.setPathElementModelNode(d->modelNode);
89     d->secondControllPoint.setPointType(FirstControlPoint);
90 }
91 
setThirdControlPoint(const ControlPoint & thirdControlPoint)92 void CubicSegment::setThirdControlPoint(const ControlPoint &thirdControlPoint)
93 {
94     d->thirdControllPoint = thirdControlPoint;
95     d->thirdControllPoint.setPathElementModelNode(d->modelNode);
96     d->thirdControllPoint.setPointType(SecondControlPoint);
97 }
98 
setThirdControlPoint(double x,double y)99 void CubicSegment::setThirdControlPoint(double x, double y)
100 {
101     d->thirdControllPoint.setX(x);
102     d->thirdControllPoint.setY(y);
103     d->thirdControllPoint.setPathElementModelNode(d->modelNode);
104     d->thirdControllPoint.setPointType(SecondControlPoint);
105 }
106 
setThirdControlPoint(const QPointF & coordiante)107 void CubicSegment::setThirdControlPoint(const QPointF &coordiante)
108 {
109     d->thirdControllPoint.setCoordinate(coordiante);
110     d->thirdControllPoint.setPathElementModelNode(d->modelNode);
111     d->thirdControllPoint.setPointType(SecondControlPoint);
112 }
113 
setFourthControlPoint(const ControlPoint & fourthControlPoint)114 void CubicSegment::setFourthControlPoint(const ControlPoint &fourthControlPoint)
115 {
116     d->fourthControllPoint = fourthControlPoint;
117     d->fourthControllPoint.setPathElementModelNode(d->modelNode);
118     d->fourthControllPoint.setPointType(EndPoint);
119 }
120 
setFourthControlPoint(double x,double y)121 void CubicSegment::setFourthControlPoint(double x, double y)
122 {
123     d->fourthControllPoint.setX(x);
124     d->fourthControllPoint.setY(y);
125     d->fourthControllPoint.setPathElementModelNode(d->modelNode);
126     d->fourthControllPoint.setPointType(EndPoint);
127 }
128 
setFourthControlPoint(const QPointF & coordiante)129 void CubicSegment::setFourthControlPoint(const QPointF &coordiante)
130 {
131     d->fourthControllPoint.setCoordinate(coordiante);
132     d->fourthControllPoint.setPathElementModelNode(d->modelNode);
133     d->fourthControllPoint.setPointType(EndPoint);
134 }
135 
setAttributes(const QMap<QString,QVariant> & attributes)136 void CubicSegment::setAttributes(const QMap<QString, QVariant> &attributes)
137 {
138     d->attributes = attributes;
139 }
140 
setPercent(double percent)141 void CubicSegment::setPercent(double percent)
142 {
143     d->percent = percent;
144 }
145 
firstControlPoint() const146 ControlPoint CubicSegment::firstControlPoint() const
147 {
148     return d->firstControllPoint;
149 }
150 
secondControlPoint() const151 ControlPoint CubicSegment::secondControlPoint() const
152 {
153     return d->secondControllPoint;
154 }
155 
thirdControlPoint() const156 ControlPoint CubicSegment::thirdControlPoint() const
157 {
158     return d->thirdControllPoint;
159 }
160 
fourthControlPoint() const161 ControlPoint CubicSegment::fourthControlPoint() const
162 {
163     return d->fourthControllPoint;
164 }
165 
attributes() const166 const QMap<QString, QVariant> CubicSegment::attributes() const
167 {
168     return d->attributes;
169 }
170 
percent() const171 double CubicSegment::percent() const
172 {
173     return d->percent;
174 }
175 
controlPoints() const176 QList<ControlPoint> CubicSegment::controlPoints() const
177 {
178     QList<ControlPoint> controlPointList;
179 
180     controlPointList.reserve(4);
181 
182     controlPointList.append(firstControlPoint());
183     controlPointList.append(secondControlPoint());
184     controlPointList.append(thirdControlPoint());
185     controlPointList.append(fourthControlPoint());
186 
187     return controlPointList;
188 }
189 
firstControlX() const190 double CubicSegment::firstControlX() const
191 {
192     return firstControlPoint().coordinate().x();
193 }
194 
firstControlY() const195 double CubicSegment::firstControlY() const
196 {
197     return firstControlPoint().coordinate().y();
198 }
199 
secondControlX() const200 double CubicSegment::secondControlX() const
201 {
202     return secondControlPoint().coordinate().x();
203 }
204 
secondControlY() const205 double CubicSegment::secondControlY() const
206 {
207     return secondControlPoint().coordinate().y();
208 }
209 
thirdControlX() const210 double CubicSegment::thirdControlX() const
211 {
212     return thirdControlPoint().coordinate().x();
213 }
214 
thirdControlY() const215 double CubicSegment::thirdControlY() const
216 {
217     return thirdControlPoint().coordinate().y();
218 }
219 
fourthControlX() const220 double CubicSegment::fourthControlX() const
221 {
222     return fourthControlPoint().coordinate().x();
223 }
224 
fourthControlY() const225 double CubicSegment::fourthControlY() const
226 {
227     return fourthControlPoint().coordinate().y();
228 }
229 
quadraticControlX() const230 double CubicSegment::quadraticControlX() const
231 {
232     return -0.25 * firstControlX() + 0.75 * secondControlX() + 0.75 * thirdControlX() - 0.25 * fourthControlX();
233 }
234 
quadraticControlY() const235 double CubicSegment::quadraticControlY() const
236 {
237     return -0.25 * firstControlY() + 0.75 * secondControlY() + 0.75 * thirdControlY() - 0.25 * fourthControlY();
238 }
239 
isValid() const240 bool CubicSegment::isValid() const
241 {
242     return d.data();
243 }
244 
canBeConvertedToLine() const245 bool CubicSegment::canBeConvertedToLine() const
246 {
247     return canBeConvertedToQuad()
248             && qFuzzyIsNull(((3. * d->firstControllPoint.coordinate())
249                              - (6. * d->secondControllPoint.coordinate())
250                              + (3. * d->thirdControllPoint.coordinate())).manhattanLength());;
251 }
252 
canBeConvertedToQuad() const253 bool CubicSegment::canBeConvertedToQuad() const
254 {
255     return qFuzzyIsNull(((3. * d->secondControllPoint.coordinate())
256                          - (3 * d->thirdControllPoint.coordinate())
257                          + d->fourthControllPoint.coordinate()
258                          - d->firstControllPoint.coordinate()).manhattanLength());
259 }
260 
sample(double t) const261 QPointF CubicSegment::sample(double t) const
262 {
263     return qPow(1.-t, 3.) * firstControlPoint().coordinate()
264             + 3 * qPow(1.-t, 2.) * t * secondControlPoint().coordinate()
265             + 3 * qPow(t, 2.) * (1. - t) * thirdControlPoint().coordinate()
266             + qPow(t, 3.) * fourthControlPoint().coordinate();
267 }
268 
minimumDistance(const QPointF & pickPoint,double & tReturnValue) const269 double CubicSegment::minimumDistance(const QPointF &pickPoint, double &tReturnValue) const
270 {
271     double actualMinimumDistance = 10000000.;
272     for (double t = 0.0; t <= 1.0; t += 0.1) {
273         QPointF samplePoint = sample(t);
274         QPointF distanceVector = pickPoint - samplePoint;
275         if (distanceVector.manhattanLength() < actualMinimumDistance) {
276             actualMinimumDistance = distanceVector.manhattanLength();
277             tReturnValue = t;
278         }
279     }
280 
281     return actualMinimumDistance;
282 }
283 
interpolatedPoint(double t,const QPointF & firstPoint,const QPointF & secondPoint)284 static QPointF interpolatedPoint(double t, const QPointF &firstPoint, const QPointF &secondPoint)
285 {
286     return (secondPoint - firstPoint) * t + firstPoint;
287 }
288 
split(double t)289 QPair<CubicSegment, CubicSegment> CubicSegment::split(double t)
290 {
291     // first pass
292     QPointF secondPointFirstSegment = interpolatedPoint(t, firstControlPoint().coordinate(), secondControlPoint().coordinate());
293     QPointF firstIntermediatPoint = interpolatedPoint(t, secondControlPoint().coordinate(), thirdControlPoint().coordinate());
294     QPointF thirdPointSecondSegment = interpolatedPoint(t, thirdControlPoint().coordinate(), fourthControlPoint().coordinate());
295 
296     // second pass
297     QPointF thirdPointFirstSegment = interpolatedPoint(t, secondPointFirstSegment, firstIntermediatPoint);
298     QPointF secondPointSecondSegment = interpolatedPoint(t, firstIntermediatPoint, thirdPointSecondSegment);
299 
300     // third pass
301     QPointF midPoint = interpolatedPoint(t, thirdPointFirstSegment, secondPointSecondSegment);
302     ControlPoint midControlPoint(midPoint);
303 
304 
305     CubicSegment firstCubicSegment = CubicSegment::create();
306     firstCubicSegment.setFirstControlPoint(firstControlPoint().coordinate());
307     firstCubicSegment.setSecondControlPoint(secondPointFirstSegment);
308     firstCubicSegment.setThirdControlPoint(thirdPointFirstSegment);
309     firstCubicSegment.setFourthControlPoint(midControlPoint);
310 
311     CubicSegment secondCubicSegment =  CubicSegment::create();
312     secondCubicSegment.setFirstControlPoint(midControlPoint);
313     secondCubicSegment.setSecondControlPoint(secondPointSecondSegment);
314     secondCubicSegment.setThirdControlPoint(thirdPointSecondSegment);
315     secondCubicSegment.setFourthControlPoint(fourthControlPoint().coordinate());
316 
317     qDebug() << firstCubicSegment << secondCubicSegment;
318 
319     return {firstCubicSegment, secondCubicSegment};
320 }
321 
makeStraightLine()322 void CubicSegment::makeStraightLine()
323 {
324     QPointF lineVector = fourthControlPoint().coordinate() - firstControlPoint().coordinate();
325     QPointF newSecondControlPoint = firstControlPoint().coordinate() + (lineVector * 0.3);
326     QPointF newThirdControlPoint = fourthControlPoint().coordinate() - (lineVector * 0.3);
327     setSecondControlPoint(newSecondControlPoint);
328     setThirdControlPoint(newThirdControlPoint);
329 }
330 
updateModelNode()331 void CubicSegment::updateModelNode()
332 {
333     firstControlPoint().updateModelNode();
334     secondControlPoint().updateModelNode();
335     thirdControlPoint().updateModelNode();
336     fourthControlPoint().updateModelNode();
337 }
338 
CubicSegmentData()339 CubicSegmentData::CubicSegmentData()
340     : firstControllPoint(0., 0.),
341       secondControllPoint(0., 0.),
342       thirdControllPoint(0., 0.),
343       fourthControllPoint(0., 0.),
344       percent(-1.0)
345 {
346 }
347 
operator ==(const CubicSegment & firstCubicSegment,const CubicSegment & secondCubicSegment)348 bool operator ==(const CubicSegment& firstCubicSegment, const CubicSegment& secondCubicSegment)
349 {
350     return firstCubicSegment.d.data() == secondCubicSegment.d.data();
351 }
352 
operator <<(QDebug debug,const CubicSegment & cubicSegment)353 QDebug operator<<(QDebug debug, const CubicSegment &cubicSegment)
354 {
355     if (cubicSegment.isValid()) {
356         debug.nospace() << "CubicSegment("
357                 << cubicSegment.firstControlPoint() << ", "
358                 << cubicSegment.secondControlPoint() << ", "
359                 << cubicSegment.thirdControlPoint() << ", "
360                 << cubicSegment.fourthControlPoint() << ')';
361     } else {
362         debug.nospace() << "CubicSegment(invalid)";
363     }
364 
365     return debug.space();
366 }
367 } // namespace QmlDesigner
368