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 &center, 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 &center, 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 &center, 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 &center, 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