1 /************************************************************************
2 **
3 ** @file varc.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 "varc.h"
30
31 #include <QLineF>
32 #include <QPointF>
33
34 #include "../vmisc/def.h"
35 #include "../vmisc/vmath.h"
36 #include "../vmisc/compatibility.h"
37 #include "../ifc/ifcdef.h"
38 #include "vabstractcurve.h"
39 #include "varc_p.h"
40 #include "vspline.h"
41
42 //---------------------------------------------------------------------------------------------------------------------
43 /**
44 * @brief VArc default constructor.
45 */
VArc()46 VArc::VArc ()
47 : VAbstractArc(GOType::Arc),
48 d (new VArcData)
49 {}
50
51 //---------------------------------------------------------------------------------------------------------------------
52 /**
53 * @brief VArc constructor.
54 * @param center center point.
55 * @param radius arc radius.
56 * @param f1 start angle (degree).
57 * @param f2 end angle (degree).
58 */
VArc(const VPointF & center,qreal radius,const QString & formulaRadius,qreal f1,const QString & formulaF1,qreal f2,const QString & formulaF2,quint32 idObject,Draw mode)59 VArc::VArc (const VPointF ¢er, qreal radius, const QString &formulaRadius, qreal f1, const QString &formulaF1,
60 qreal f2, const QString &formulaF2, quint32 idObject, Draw mode)
61 : VAbstractArc(GOType::Arc, center, f1, formulaF1, f2, formulaF2, idObject, mode),
62 d (new VArcData(radius, formulaRadius))
63 {
64 CreateName();
65 }
66
67 //---------------------------------------------------------------------------------------------------------------------
VArc(const VPointF & center,qreal radius,qreal f1,qreal f2)68 VArc::VArc(const VPointF ¢er, qreal radius, qreal f1, qreal f2)
69 : VAbstractArc(GOType::Arc, center, f1, f2, NULL_ID, Draw::Calculation),
70 d (new VArcData(radius))
71 {
72 CreateName();
73 }
74
75 //---------------------------------------------------------------------------------------------------------------------
VArc(qreal length,const QString & formulaLength,const VPointF & center,qreal radius,const QString & formulaRadius,qreal f1,const QString & formulaF1,quint32 idObject,Draw mode)76 VArc::VArc(qreal length, const QString &formulaLength, const VPointF ¢er, qreal radius,
77 const QString &formulaRadius, qreal f1, const QString &formulaF1, quint32 idObject, Draw mode)
78 : VAbstractArc(GOType::Arc, formulaLength, center, f1, formulaF1, idObject, mode),
79 d (new VArcData(radius, formulaRadius))
80 {
81 CreateName();
82 FindF2(length);
83 }
84
85 //---------------------------------------------------------------------------------------------------------------------
VArc(qreal length,const VPointF & center,qreal radius,qreal f1)86 VArc::VArc(qreal length, const VPointF ¢er, qreal radius, qreal f1)
87 : VAbstractArc(GOType::Arc, center, f1, NULL_ID, Draw::Calculation),
88 d (new VArcData(radius))
89 {
90 CreateName();
91 FindF2(length);
92 }
93
94 //---------------------------------------------------------------------------------------------------------------------
95 /**
96 * @brief VArc copy constructor
97 * @param arc arc
98 */
VArc(const VArc & arc)99 VArc::VArc(const VArc &arc)
100 : VAbstractArc(arc), d (arc.d)
101 {}
102
103 //---------------------------------------------------------------------------------------------------------------------
104 /**
105 * @brief operator = assignment operator
106 * @param arc arc
107 * @return arc
108 */
operator =(const VArc & arc)109 VArc &VArc::operator =(const VArc &arc)
110 {
111 if ( &arc == this )
112 {
113 return *this;
114 }
115 VAbstractArc::operator=(arc);
116 d = arc.d;
117 return *this;
118 }
119
120 #ifdef Q_COMPILER_RVALUE_REFS
121 //---------------------------------------------------------------------------------------------------------------------
VArc(VArc && arc)122 VArc::VArc(VArc &&arc) Q_DECL_NOTHROW
123 : VAbstractArc(arc), d (arc.d)
124 {}
125
126 //---------------------------------------------------------------------------------------------------------------------
operator =(VArc && arc)127 VArc &VArc::operator=(VArc &&arc) Q_DECL_NOTHROW
128 {
129 VAbstractArc::operator=(arc);
130 std::swap(d, arc.d);
131 return *this;
132 }
133 #endif
134
135 //---------------------------------------------------------------------------------------------------------------------
Rotate(const QPointF & originPoint,qreal degrees,const QString & prefix) const136 VArc VArc::Rotate(const QPointF &originPoint, qreal degrees, const QString &prefix) const
137 {
138 const VPointF center = GetCenter().Rotate(originPoint, degrees);
139
140 const QPointF p1 = VPointF::RotatePF(originPoint, GetP1(), degrees);
141 const QPointF p2 = VPointF::RotatePF(originPoint, GetP2(), degrees);
142
143 const qreal f1 = QLineF(static_cast<QPointF>(center), p1).angle();
144 const qreal f2 = QLineF(static_cast<QPointF>(center), p2).angle();
145
146 VArc arc(center, GetRadius(), f1, f2);
147 arc.setName(name() + prefix);
148
149 if (not GetAliasSuffix().isEmpty())
150 {
151 arc.SetAliasSuffix(GetAliasSuffix() + prefix);
152 }
153
154 arc.SetColor(GetColor());
155 arc.SetPenStyle(GetPenStyle());
156 arc.SetFlipped(IsFlipped());
157 arc.SetApproximationScale(GetApproximationScale());
158 return arc;
159 }
160
161 //---------------------------------------------------------------------------------------------------------------------
Flip(const QLineF & axis,const QString & prefix) const162 VArc VArc::Flip(const QLineF &axis, const QString &prefix) const
163 {
164 const VPointF center = GetCenter().Flip(axis);
165
166 const QPointF p1 = VPointF::FlipPF(axis, GetP1());
167 const QPointF p2 = VPointF::FlipPF(axis, GetP2());
168
169 const qreal f1 = QLineF(static_cast<QPointF>(center), p1).angle();
170 const qreal f2 = QLineF(static_cast<QPointF>(center), p2).angle();
171
172 VArc arc(center, GetRadius(), f1, f2);
173 arc.setName(name() + prefix);
174
175 if (not GetAliasSuffix().isEmpty())
176 {
177 arc.SetAliasSuffix(GetAliasSuffix() + prefix);
178 }
179
180 arc.SetColor(GetColor());
181 arc.SetPenStyle(GetPenStyle());
182 arc.SetFlipped(not IsFlipped());
183 arc.SetApproximationScale(GetApproximationScale());
184 return arc;
185 }
186
187 //---------------------------------------------------------------------------------------------------------------------
Move(qreal length,qreal angle,const QString & prefix) const188 VArc VArc::Move(qreal length, qreal angle, const QString &prefix) const
189 {
190 const VPointF center = GetCenter().Move(length, angle);
191
192 const QPointF p1 = VPointF::MovePF(GetP1(), length, angle);
193 const QPointF p2 = VPointF::MovePF(GetP2(), length, angle);
194
195 const qreal f1 = QLineF(static_cast<QPointF>(center), p1).angle();
196 const qreal f2 = QLineF(static_cast<QPointF>(center), p2).angle();
197
198 VArc arc(center, GetRadius(), f1, f2);
199 arc.setName(name() + prefix);
200
201 if (not GetAliasSuffix().isEmpty())
202 {
203 arc.SetAliasSuffix(GetAliasSuffix() + prefix);
204 }
205
206 arc.SetColor(GetColor());
207 arc.SetPenStyle(GetPenStyle());
208 arc.SetFlipped(IsFlipped());
209 arc.SetApproximationScale(GetApproximationScale());
210 return arc;
211 }
212
213 //---------------------------------------------------------------------------------------------------------------------
~VArc()214 VArc::~VArc()
215 {}
216
217 //---------------------------------------------------------------------------------------------------------------------
218 /**
219 * @brief GetLength return arc length.
220 * @return length.
221 */
GetLength() const222 qreal VArc::GetLength() const
223 {
224 qreal length = d->radius * qDegreesToRadians(AngleArc());
225 if (IsFlipped())
226 {
227 length *= -1;
228 }
229
230 return length;
231 }
232
233 //---------------------------------------------------------------------------------------------------------------------
234 /**
235 * @brief GetP1 return point associated with start angle.
236 * @return point.
237 */
GetP1() const238 QPointF VArc::GetP1() const
239 {
240 QPointF p1 ( GetCenter().x () + d->radius, GetCenter().y () );
241 QLineF centerP1(static_cast<QPointF>(GetCenter()), p1);
242 centerP1.setAngle(GetStartAngle());
243 return centerP1.p2();
244 }
245
246 //---------------------------------------------------------------------------------------------------------------------
247 /**
248 * @brief GetP2 return point associated with end angle.
249 * @return точку point.
250 */
GetP2() const251 QPointF VArc::GetP2 () const
252 {
253 QPointF p2 ( GetCenter().x () + d->radius, GetCenter().y () );
254 QLineF centerP2(static_cast<QPointF>(GetCenter()), p2);
255 centerP2.setAngle(GetEndAngle());
256 return centerP2.p2();
257 }
258
259 //---------------------------------------------------------------------------------------------------------------------
260 /**
261 * @brief GetPoints return list of points needed for drawing arc.
262 * @return list of points
263 */
GetPoints() const264 QVector<QPointF> VArc::GetPoints() const
265 {
266 if (qFuzzyIsNull(GetRadius()))
267 {
268 return {GetCenter().toQPointF()};
269 }
270
271 QVector<QPointF> points;
272 QVector<qreal> sectionAngle;
273
274 QPointF pStart = IsFlipped() ? GetP2() : GetP1();
275
276 {
277 qreal angle = AngleArc();
278
279 if (qFuzzyIsNull(angle))
280 {
281 points.append(pStart);
282 return points;
283 }
284
285 if (angle > 360 || angle < 0)
286 {// Filter incorect value
287 QLineF dummy(0,0, 100, 0);
288 dummy.setAngle(angle);
289 angle = dummy.angle();
290 }
291
292 const qreal angleInterpolation = 45; //degree
293 const int sections = qFloor(angle / angleInterpolation);
294 for (int i = 0; i < sections; ++i)
295 {
296 sectionAngle.append(angleInterpolation);
297 }
298
299 const qreal tail = angle - sections * angleInterpolation;
300 if (tail > 0)
301 {
302 sectionAngle.append(tail);
303 }
304 }
305
306 for (int i = 0; i < sectionAngle.size(); ++i)
307 {
308 const qreal lDistance = GetRadius() * 4.0/3.0 * qTan(qDegreesToRadians(sectionAngle.at(i)) * 0.25);
309
310 const QPointF center = static_cast<QPointF>(GetCenter());
311
312 QLineF lineP1P2(pStart, center);
313 lineP1P2.setAngle(lineP1P2.angle() - 90.0);
314 lineP1P2.setLength(lDistance);
315
316 QLineF lineP4P3(center, pStart);
317 lineP4P3.setAngle(lineP4P3.angle() + sectionAngle.at(i));
318 lineP4P3.setLength(GetRadius());//in case of computing error
319 lineP4P3 = QLineF(lineP4P3.p2(), center);
320 lineP4P3.setAngle(lineP4P3.angle() + 90.0);
321 lineP4P3.setLength(lDistance);
322
323 VSpline spl(VPointF(pStart), lineP1P2.p2(), lineP4P3.p2(), VPointF(lineP4P3.p1()), 1.0);
324 spl.SetApproximationScale(GetApproximationScale());
325 QVector<QPointF> splPoints = spl.GetPoints();
326 if (not splPoints.isEmpty() && i != sectionAngle.size() - 1)
327 {
328 splPoints.removeLast();
329 }
330
331 points << splPoints;
332 pStart = lineP4P3.p1();
333 }
334 return IsFlipped() ? Reverse(points) : points;
335 }
336
337 //---------------------------------------------------------------------------------------------------------------------
338 /**
339 * @brief CutArc cut arc into two arcs.
340 * @param length length first arc.
341 * @param arc1 first arc.
342 * @param arc2 second arc.
343 * @return point cutting
344 */
CutArc(qreal length,VArc & arc1,VArc & arc2) const345 QPointF VArc::CutArc(qreal length, VArc &arc1, VArc &arc2) const
346 {
347 //Always need return two arcs, so we must correct wrong length.
348 const qreal fullLength = GetLength();
349
350 if (qAbs(fullLength) < ToPixel(2, Unit::Mm))
351 {
352 arc1 = VArc();
353 arc2 = VArc();
354 return QPointF();
355 }
356
357 QLineF line(static_cast<QPointF>(GetCenter()), GetP1());
358
359 if (not IsFlipped())
360 {
361 if (length < 0)
362 {
363 length = fullLength + length;
364 }
365 length = qBound(ToPixel(1, Unit::Mm), length, fullLength - ToPixel(1, Unit::Mm));
366
367 line.setAngle(line.angle() + qRadiansToDegrees(length/d->radius));
368 }
369 else
370 {
371 if (length > 0)
372 {
373 length = fullLength + length;
374 }
375 length = qBound(fullLength + ToPixel(1, Unit::Mm), length, ToPixel(-1, Unit::Mm));
376
377 line.setAngle(line.angle() - qRadiansToDegrees(qAbs(length)/d->radius));
378 }
379
380 arc1 = VArc (GetCenter(), d->radius, d->formulaRadius, GetStartAngle(), GetFormulaF1(), line.angle(),
381 QString().setNum(line.angle()), getIdObject(), getMode());
382 arc1.SetApproximationScale(GetApproximationScale());
383 arc1.SetFlipped(IsFlipped());
384
385 arc2 = VArc (GetCenter(), d->radius, d->formulaRadius, line.angle(), QString().setNum(line.angle()), GetEndAngle(),
386 GetFormulaF2(), getIdObject(), getMode());
387 arc2.SetApproximationScale(GetApproximationScale());
388 arc2.SetFlipped(IsFlipped());
389 return line.p2();
390 }
391
392
393 //---------------------------------------------------------------------------------------------------------------------
CutArc(qreal length) const394 QPointF VArc::CutArc(qreal length) const
395 {
396 VArc arc1;
397 VArc arc2;
398 return this->CutArc(length, arc1, arc2);
399 }
400
401 //---------------------------------------------------------------------------------------------------------------------
CreateName()402 void VArc::CreateName()
403 {
404 QString name = ARC_ + this->GetCenter().name();
405
406 if (getMode() == Draw::Modeling && getIdObject() != NULL_ID)
407 {
408 name += QString("_%1").arg(getIdObject());
409 }
410 else if (VAbstractCurve::id() != NULL_ID)
411 {
412 name += QString("_%1").arg(VAbstractCurve::id());
413 }
414
415 if (GetDuplicate() > 0)
416 {
417 name += QString("_%1").arg(GetDuplicate());
418 }
419
420 setName(name);
421 }
422
423 //---------------------------------------------------------------------------------------------------------------------
CreateAlias()424 void VArc::CreateAlias()
425 {
426 const QString aliasSuffix = GetAliasSuffix();
427 if (aliasSuffix.isEmpty())
428 {
429 SetAlias(QString());
430 return;
431 }
432
433 SetAlias(ARC_ + aliasSuffix);
434 }
435
436 //---------------------------------------------------------------------------------------------------------------------
FindF2(qreal length)437 void VArc::FindF2(qreal length)
438 {
439 SetFlipped(length < 0);
440
441 if (length >= MaxLength())
442 {
443 length = MaxLength();
444 }
445
446 qreal arcAngle = qAbs(qRadiansToDegrees(length/d->radius));
447
448 if (IsFlipped())
449 {
450 arcAngle = arcAngle * -1;
451 }
452
453 QLineF startAngle(0, 0, 100, 0);
454 startAngle.setAngle(GetStartAngle() + arcAngle);// We use QLineF just because it is easy way correct angle value
455
456 SetFormulaF2(QString().number(startAngle.angle()), startAngle.angle());
457 }
458
459 //---------------------------------------------------------------------------------------------------------------------
MaxLength() const460 qreal VArc::MaxLength() const
461 {
462 return M_2PI*d->radius;
463 }
464
465 //---------------------------------------------------------------------------------------------------------------------
466 /**
467 * @brief GetRadius return arc radius.
468 * @return radius.
469 */
GetFormulaRadius() const470 QString VArc::GetFormulaRadius() const
471 {
472 return d->formulaRadius;
473 }
474
475 //---------------------------------------------------------------------------------------------------------------------
SetFormulaRadius(const QString & formula,qreal value)476 void VArc::SetFormulaRadius(const QString &formula, qreal value)
477 {
478 d->formulaRadius = formula;
479 d->radius = value;
480 }
481
482 //---------------------------------------------------------------------------------------------------------------------
483 /**
484 * @brief GetRadius return formula for radius.
485 * @return string with formula.
486 */
GetRadius() const487 qreal VArc::GetRadius() const
488 {
489 return d->radius;
490 }
491