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