Seamly2D
Code documentation
vellipticalarc.cpp
Go to the documentation of this file.
1 /************************************************************************
2  **
3  ** @file vellipticalarc.cpp
4  ** @author Valentina Zhuravska <zhuravska19(at)gmail.com>
5  ** @date February 1, 2016
6  **
7  ** @brief
8  ** @copyright
9  ** This source code is part of the Valentine project, a pattern making
10  ** program, whose allow create and modeling patterns of clothing.
11  ** Copyright (C) 2013-2015 Seamly2D project
12  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
13  **
14  ** Seamly2D 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  ** Seamly2D 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 Seamly2D. If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "vellipticalarc.h"
30 
31 #include <QLineF>
32 #include <QPoint>
33 #include <QPainterPath>
34 
35 #include "../vmisc/def.h"
36 #include "../vmisc/vmath.h"
37 #include "../ifc/ifcdef.h"
38 #include "../vmisc/vabstractapplication.h"
39 #include "vabstractcurve.h"
40 #include "vellipticalarc_p.h"
41 #include "vspline.h"
42 
43 #ifdef Q_COMPILER_RVALUE_REFS
44 VEllipticalArc &VEllipticalArc::operator=(VEllipticalArc &&arc) Q_DECL_NOTHROW { Swap(arc); return *this; }
45 #endif
46 
47 void VEllipticalArc::Swap(VEllipticalArc &arc) Q_DECL_NOTHROW
48 { VAbstractArc::Swap(arc); std::swap(d, arc.d); }
49 
50 //---------------------------------------------------------------------------------------------------------------------
51 /**
52  * @brief VEllipticalArc default constructor.
53  */
56 {}
57 
58 //---------------------------------------------------------------------------------------------------------------------
59 /**
60  * @brief VEllipticalArc constructor.
61  * @param center center point.
62  * @param radius1 arc major radius.
63  * @param radius2 arc minor radius.
64  * @param f1 start angle (degree).
65  * @param f2 end angle (degree).
66  */
67 VEllipticalArc::VEllipticalArc (const VPointF &center, qreal radius1, qreal radius2, const QString &formulaRadius1,
68  const QString &formulaRadius2, qreal f1, const QString &formulaF1, qreal f2,
69  const QString &formulaF2, qreal rotationAngle, const QString &formulaRotationAngle,
70  quint32 idObject, Draw mode)
71  : VAbstractArc(GOType::EllipticalArc, center, f1, formulaF1, f2, formulaF2, idObject, mode),
72  d (new VEllipticalArcData(radius1, radius2, formulaRadius1, formulaRadius2, rotationAngle, formulaRotationAngle))
73 {
74  CreateName();
75 }
76 
77 //---------------------------------------------------------------------------------------------------------------------
78 VEllipticalArc::VEllipticalArc(const VPointF &center, qreal radius1, qreal radius2, qreal f1, qreal f2,
79  qreal rotationAngle)
81  d (new VEllipticalArcData(radius1, radius2, rotationAngle))
82 {
83  CreateName();
84 }
85 
86 //---------------------------------------------------------------------------------------------------------------------
87 VEllipticalArc::VEllipticalArc(qreal length, const QString &formulaLength, const VPointF &center, qreal radius1,
88  qreal radius2, const QString &formulaRadius1, const QString &formulaRadius2, qreal f1,
89  const QString &formulaF1, qreal rotationAngle, const QString &formulaRotationAngle,
90  quint32 idObject, Draw mode)
91  : VAbstractArc(GOType::EllipticalArc, formulaLength, center, f1, formulaF1, idObject, mode),
92  d (new VEllipticalArcData(radius1, radius2, formulaRadius1, formulaRadius2, rotationAngle, formulaRotationAngle))
93 {
94  CreateName();
95  FindF2(length);
96 }
97 
98 //---------------------------------------------------------------------------------------------------------------------
99 VEllipticalArc::VEllipticalArc(qreal length, const VPointF &center, qreal radius1, qreal radius2, qreal f1,
100  qreal rotationAngle)
102  d (new VEllipticalArcData(radius1, radius2, rotationAngle))
103 {
104  CreateName();
105  FindF2(length);
106 }
107 
108 //---------------------------------------------------------------------------------------------------------------------
109 /**
110  * @brief VEllipticalArc copy constructor
111  * @param arc arc
112  */
114  : VAbstractArc(arc), d (arc.d)
115 {}
116 
117 //---------------------------------------------------------------------------------------------------------------------
118 /**
119  * @brief operator = assignment operator
120  * @param arc arc
121  * @return arc
122  */
124 {
125  if ( &arc == this )
126  {
127  return *this;
128  }
130  d = arc.d;
131  return *this;
132 }
133 
134 //---------------------------------------------------------------------------------------------------------------------
135 VEllipticalArc VEllipticalArc::Rotate(QPointF originPoint, qreal degrees, const QString &prefix) const
136 {
137  originPoint = d->m_transform.inverted().map(originPoint);
138 
139  QTransform t = d->m_transform;
140  t.translate(originPoint.x(), originPoint.y());
141  t.rotate(IsFlipped() ? degrees : -degrees);
142  t.translate(-originPoint.x(), -originPoint.y());
143 
146  elArc.setName(name() + prefix);
147  elArc.setLineColor(getLineColor());
148  elArc.SetPenStyle(GetPenStyle());
149  elArc.setLineWeight(getLineWeight());
150  elArc.SetFlipped(IsFlipped());
151  elArc.setTransform(t);
152  return elArc;
153 }
154 
155 //---------------------------------------------------------------------------------------------------------------------
156 VEllipticalArc VEllipticalArc::Flip(const QLineF &axis, const QString &prefix) const
157 {
160  elArc.setName(name() + prefix);
161  elArc.setLineColor(getLineColor());
162  elArc.SetPenStyle(GetPenStyle());
163  elArc.setLineWeight(getLineWeight());
164  elArc.SetFlipped(not IsFlipped());
165  elArc.setTransform(d->m_transform * VGObject::flipTransform(d->m_transform.inverted().map(axis)));
166  return elArc;
167 }
168 
169 //---------------------------------------------------------------------------------------------------------------------
170 VEllipticalArc VEllipticalArc::Move(qreal length, qreal angle, const QString &prefix) const
171 {
172  const VPointF oldCenter = VAbstractArc::GetCenter();
173  const VPointF center = oldCenter.Move(length, angle);
174 
175  const QPointF position = d->m_transform.inverted().map(center.toQPointF()) -
176  d->m_transform.inverted().map(oldCenter.toQPointF());
177 
178  QTransform t = d->m_transform;
179  t.translate(position.x(), position.y());
180 
183  elArc.setName(name() + prefix);
184  elArc.setLineColor(getLineColor());
185  elArc.SetPenStyle(GetPenStyle());
186  elArc.setLineWeight(getLineWeight());
187  elArc.SetFlipped(IsFlipped());
188  elArc.setTransform(t);
189  return elArc;
190 }
191 
192 //---------------------------------------------------------------------------------------------------------------------
194 {}
195 
196 //---------------------------------------------------------------------------------------------------------------------
197 /**
198  * @brief GetLength return arc length.
199  * @return length.
200  */
202 {
203  qreal length = PathLength(getPoints());
204 
205  if (IsFlipped())
206  {
207  length = length * -1;
208  }
209 
210  return length;
211 }
212 
213 //---------------------------------------------------------------------------------------------------------------------
214 /**
215  * @brief GetP1 return point associated with start angle.
216  * @return point.
217  */
218 QPointF VEllipticalArc::GetP1() const
219 {
221 }
222 
223 //---------------------------------------------------------------------------------------------------------------------
224 /**
225  * @brief GetP2 return point associated with end angle.
226  * @return point.
227  */
228 QPointF VEllipticalArc::GetP2 () const
229 {
231 }
232 
233 //---------------------------------------------------------------------------------------------------------------------
235 {
236  return d->m_transform;
237 }
238 
239 //---------------------------------------------------------------------------------------------------------------------
240 void VEllipticalArc::setTransform(const QTransform &matrix, bool combine)
241 {
242  d->m_transform = combine ? d->m_transform * matrix : matrix;
243 }
244 
245 //---------------------------------------------------------------------------------------------------------------------
247 {
248  VPointF center = VAbstractArc::GetCenter();
249  const QPointF p = d->m_transform.map(center.toQPointF());
250  center.setX(p.x());
251  center.setY(p.y());
252  return center;
253 }
254 
255 
256 //---------------------------------------------------------------------------------------------------------------------
257 /**
258  * @brief GetPoint return point associated with angle.
259  * @return point.
260  */
261  //---------------------------------------------------------------------------------------------------------------------
262 QPointF VEllipticalArc::getPoint(qreal angle) const
263 {
264  if (qFuzzyIsNull(GetRadius1()) && qFuzzyIsNull(GetRadius2()))
265  {
266  return GetCenter().toQPointF();
267  }
268  QLineF line(0, 0, 100, 0);
269  line.setAngle(angle);
270 
271  const qreal a = not qFuzzyIsNull(GetRadius1()) ? line.p2().x() / GetRadius1() : 0;
272  const qreal b = not qFuzzyIsNull(GetRadius2()) ? line.p2().y() / GetRadius2() : 0;
273  const qreal k = qSqrt(a*a + b*b);
274 
275  if (qFuzzyIsNull(k))
276  {
277  return GetCenter().toQPointF();
278  }
279 
280  QPointF p(line.p2().x() / k, line.p2().y() / k);
281  QLineF line2(QPointF(), p);
282  SCASSERT(VFuzzyComparePossibleNulls(line2.angle(), line.angle()))
283 
284  line2.setAngle(line2.angle() + GetRotationAngle());
285  return line2.p2() + VAbstractArc::GetCenter().toQPointF();
286 }
287 
288 //---------------------------------------------------------------------------------------------------------------------
289 /**
290  * @brief GetPoints return list of points needed for drawing arc.
291  * @return list of points
292  */
294 {
295  const QPointF center = VAbstractArc::GetCenter().toQPointF();
296  QRectF box(center.x() - d->radius1, center.y() - d->radius2, d->radius1*2, d->radius2*2);
297 
298  QLineF startLine(center.x(), center.y(), center.x() + d->radius1, center.y());
299  QLineF endLine = startLine;
300 
301  startLine.setAngle(VAbstractArc::GetStartAngle());
302  endLine.setAngle(getRealEndAngle());
303  qreal sweepAngle = startLine.angleTo(endLine);
304 
305  if (qFuzzyIsNull(sweepAngle))
306  {
307  sweepAngle = 360;
308  }
309 
310  QPainterPath path;
311  path.moveTo(GetP1());
312  path.arcTo(box, VAbstractArc::GetStartAngle(), sweepAngle);
313  path.moveTo(GetP2());
314 
315  QTransform t = d->m_transform;
316  t.translate(center.x(), center.y());
317  t.rotate(-GetRotationAngle());
318  t.translate(-center.x(), -center.y());
319 
320  path = t.map(path);
321 
322  QPolygonF polygon;
323  const QList<QPolygonF> subpath = path.toSubpathPolygons();
324  if (not subpath.isEmpty())
325  {
326  polygon = path.toSubpathPolygons().first();
327  if (not polygon.isEmpty() && not VFuzzyComparePoints(GetP1(), polygon.first()))
328  {
329  polygon.removeFirst(); // remove point (0;0)
330  }
331  }
332 
333  return static_cast<QVector<QPointF>>(polygon);
334 }
335 
336 //---------------------------------------------------------------------------------------------------------------------
338 {
339  return QLineF(GetCenter().toQPointF(), GetP1()).angle() - GetRotationAngle();
340 }
341 
342 //---------------------------------------------------------------------------------------------------------------------
344 {
345  return QLineF(GetCenter().toQPointF(), GetP2()).angle() - GetRotationAngle();
346 }
347 
348 //---------------------------------------------------------------------------------------------------------------------
349 /**
350  * @brief CutArc cut arc into two arcs.
351  * @param length length first arc.
352  * @param arc1 first arc.
353  * @param arc2 second arc.
354  * @return point cutting
355  */
356 QPointF VEllipticalArc::CutArc(const qreal &length, VEllipticalArc &arc1, VEllipticalArc &arc2) const
357 {
358  //Always need return two arcs, so we must correct wrong length.
359  qreal len = 0;
360  const qreal minLength = ToPixel(1, Unit::Mm);
361  const qreal fullLength = GetLength();
362 
363  if (fullLength <= minLength)
364  {
365  arc1 = VEllipticalArc();
366  arc2 = VEllipticalArc();
367  return QPointF();
368  }
369 
370  const qreal maxLength = fullLength - minLength;
371 
372  if (length < minLength)
373  {
374  len = minLength;
375  }
376  else if (length > maxLength)
377  {
378  len = maxLength;
379  }
380  else
381  {
382  len = length;
383  }
384 
385  // the first arc has given length and startAngle just like in the origin arc
386  arc1 = VEllipticalArc (len, QString().setNum(length), GetCenter(), d->radius1, d->radius2,
387  d->formulaRadius1, d->formulaRadius2, GetStartAngle(), GetFormulaF1(), d->rotationAngle,
389  // the second arc has startAngle just like endAngle of the first arc
390  // and it has endAngle just like endAngle of the origin arc
391  arc2 = VEllipticalArc (GetCenter(), d->radius1, d->radius2, d->formulaRadius1, d->formulaRadius2,
392  arc1.GetEndAngle(), arc1.GetFormulaF2(), GetEndAngle(), GetFormulaF2(), d->rotationAngle,
394  return arc1.GetP1();
395 }
396 
397 
398 //---------------------------------------------------------------------------------------------------------------------
399 QPointF VEllipticalArc::CutArc(const qreal &length) const
400 {
401  VEllipticalArc arc1;
402  VEllipticalArc arc2;
403  return this->CutArc(length, arc1, arc2);
404 }
405 
406 //---------------------------------------------------------------------------------------------------------------------
408 {
409  QString name = ELARC_ + QString("%1").arg(this->GetCenter().name());
410 
411  if (VAbstractCurve::id() != NULL_ID)
412  {
413  name += QString("_%1").arg(VAbstractCurve::id());
414  }
415 
416  if (GetDuplicate() > 0)
417  {
418  name += QString("_%1").arg(GetDuplicate());
419  }
420 
421  setName(name);
422 }
423 
424 //---------------------------------------------------------------------------------------------------------------------
425 void VEllipticalArc::FindF2(qreal length)
426 {
427  qreal gap = 180;
428  if (length < 0)
429  {
430  SetFlipped(true);
431  gap = -gap;
432  }
433  while (length > MaxLength())
434  {
435  length = MaxLength();
436  }
437 
438  // We need to calculate the second angle
439  // first approximation of angle between start and end angles
440 
441  QLineF radius1(GetCenter().x(), GetCenter().y(), GetCenter().x() + d->radius1, GetCenter().y());
442  radius1.setAngle(GetStartAngle());
443  radius1.setAngle(radius1.angle() + gap);
444  qreal endAngle = radius1.angle();
445 
446  // we need to set the end angle, because we want to use GetLength()
447  SetFormulaF2(QString::number(endAngle), endAngle);
448 
449  qreal bezLength = GetLength(); // first approximation of length
450 
451  const qreal eps = ToPixel(0.001, Unit::Mm);
452 
453  while (qAbs(bezLength - length) > eps)
454  {
455  gap = gap/2;
456  if (gap < 0.0001)
457  {
458  break;
459  }
460  if (bezLength > length)
461  { // we selected too big end angle
462  radius1.setAngle(endAngle - qAbs(gap));
463  }
464  else
465  { // we selected too little end angle
466  radius1.setAngle(endAngle + qAbs(gap));
467  }
468  endAngle = radius1.angle();
469  // we need to set d->f2, because we use it when we calculate GetLength
470  SetFormulaF2(QString::number(endAngle), endAngle);
471  bezLength = GetLength();
472  }
473  SetFormulaLength(QString::number(qApp->fromPixel(bezLength)));
474 }
475 
476 //---------------------------------------------------------------------------------------------------------------------
478 {
479  const qreal h = qPow(d->radius1 - d->radius2, 2) / qPow(d->radius1 + d->radius2, 2);
480  const qreal ellipseLength = M_PI * (d->radius1 + d->radius2) * (1+3*h/(10+qSqrt(4-3*h)));
481  return ellipseLength;
482 }
483 
484 //---------------------------------------------------------------------------------------------------------------------
485 /**
486  * @brief GetFormulaRadius1 return formula for major radius.
487  * @return radius.
488  */
490 {
491  return d->formulaRadius1;
492 }
493 
494 //---------------------------------------------------------------------------------------------------------------------
495 /**
496  * @brief GetFormulaRadius2 return formula for minor radius.
497  * @return radius.
498  */
500 {
501  return d->formulaRadius2;
502 }
503 
504 //---------------------------------------------------------------------------------------------------------------------
505 /**
506  * @brief GetFormulaRotationAngle return formula for rotation angle.
507  * @return rotationAngle.
508  */
510 {
511  return d->formulaRotationAngle;
512 }
513 
514 //---------------------------------------------------------------------------------------------------------------------
515 void VEllipticalArc::SetFormulaRadius1(const QString &formula, qreal value)
516 {
517  d->formulaRadius1 = formula;
518  d->radius1 = value;
519 }
520 
521 //---------------------------------------------------------------------------------------------------------------------
522 void VEllipticalArc::SetFormulaRadius2(const QString &formula, qreal value)
523 {
524  d->formulaRadius2 = formula;
525  d->radius2 = value;
526 }
527 
528 //---------------------------------------------------------------------------------------------------------------------
529 void VEllipticalArc::SetFormulaRotationAngle(const QString &formula, qreal value)
530 {
531  d->formulaRotationAngle = formula;
532  d->rotationAngle = value;
533 }
534 
535 //---------------------------------------------------------------------------------------------------------------------
536 /**
537  * @brief GetRadius1 return elliptical arc major radius.
538  * @return string with formula.
539  */
541 {
542  return d->radius1;
543 }
544 
545 //---------------------------------------------------------------------------------------------------------------------
546 /**
547  * @brief GetRadius2 return elliptical arc minor radius.
548  * @return string with formula.
549  */
551 {
552  return d->radius2;
553 }
554 
555 //---------------------------------------------------------------------------------------------------------------------
556 /**
557  * @brief GetRotationAngle return rotation angle.
558  * @return rotationAngle.
559  */
561 {
562  return d->rotationAngle;
563 }
564 
565 //---------------------------------------------------------------------------------------------------------------------
567 {
569 
570  if (qFuzzyIsNull(endAngle) ||
571  VFuzzyComparePossibleNulls(endAngle, 90) ||
572  VFuzzyComparePossibleNulls(endAngle, 180) ||
573  VFuzzyComparePossibleNulls(endAngle, 270) ||
574  VFuzzyComparePossibleNulls(endAngle, 360))
575  {
576  return endAngle;
577  }
578 
579  endAngle = qRadiansToDegrees(qAtan2(d->radius1 * qSin(qDegreesToRadians(endAngle)),
580  d->radius2 * qCos(qDegreesToRadians(endAngle))));
581 
582  return endAngle;
583 }
void Swap(VAbstractArc &arc) Q_DECL_NOTHROW
virtual VPointF GetCenter() const
QString GetFormulaF1() const
VAbstractArc & operator=(const VAbstractArc &arc)
QString GetFormulaF2() const
void SetFormulaF2(const QString &formula, qreal value)
void SetFormulaLength(const QString &formula, qreal value)
virtual qreal GetEndAngle() const Q_DECL_OVERRIDE
virtual qreal GetStartAngle() const Q_DECL_OVERRIDE
bool IsFlipped() const
void SetFlipped(bool value)
QString getLineWeight() const
getLineWeight return weight of the lines
static qreal PathLength(const QVector< QPointF > &path)
void SetPenStyle(const QString &penStyle)
void setLineColor(const QString &color)
void setLineWeight(const QString &lineWeight)
setLineWeight set weight of the lines
QString GetPenStyle() const
quint32 GetDuplicate() const
QString getLineColor() const
VEllipticalArc Flip(const QLineF &axis, const QString &prefix=QString()) const
qreal GetRadius2() const
GetRadius2 return elliptical arc minor radius.
QString GetFormulaRadius2() const
GetFormulaRadius2 return formula for minor radius.
VEllipticalArc Rotate(QPointF originPoint, qreal degrees, const QString &prefix=QString()) const
VEllipticalArc & operator=(const VEllipticalArc &arc)
operator = assignment operator
virtual qreal GetStartAngle() const Q_DECL_OVERRIDE
static qreal normalizeAngle(qreal angle)
QPointF GetP1() const
GetP1 return point associated with start angle.
VEllipticalArc Move(qreal length, qreal angle, const QString &prefix=QString()) const
QString GetFormulaRadius1() const
GetFormulaRadius1 return formula for major radius.
virtual VPointF GetCenter() const Q_DECL_OVERRIDE
void SetFormulaRadius1(const QString &formula, qreal value)
VEllipticalArc()
VEllipticalArc default constructor.
virtual ~VEllipticalArc() Q_DECL_OVERRIDE
QPointF CutArc(const qreal &length, VEllipticalArc &arc1, VEllipticalArc &arc2) const
CutArc cut arc into two arcs.
qreal GetRadius1() const
GetRadius1 return elliptical arc major radius.
QSharedDataPointer< VEllipticalArcData > d
virtual QVector< QPointF > getPoints() const Q_DECL_OVERRIDE
GetPoints return list of points needed for drawing arc.
virtual void FindF2(qreal length) Q_DECL_OVERRIDE
void setTransform(const QTransform &matrix, bool combine=false)
QString GetFormulaRotationAngle() const
GetFormulaRotationAngle return formula for rotation angle.
void SetFormulaRotationAngle(const QString &formula, qreal value)
virtual qreal GetEndAngle() const Q_DECL_OVERRIDE
QPointF getPoint(qreal angle) const
GetPoint return point associated with angle.
QPointF GetP2() const
GetP2 return point associated with end angle.
qreal MaxLength() const
qreal GetRotationAngle() const
GetRotationAngle return rotation angle.
QTransform getTransform() const
virtual qreal GetLength() const Q_DECL_OVERRIDE
GetLength return arc length.
void SetFormulaRadius2(const QString &formula, qreal value)
qreal getRealEndAngle() const
void Swap(VEllipticalArc &arc) Q_DECL_NOTHROW
virtual void CreateName() Q_DECL_OVERRIDE
virtual QString name() const
name return name graphical object.
Definition: vgobject.cpp:148
static QTransform flipTransform(const QLineF &axis)
Definition: vgobject.cpp:591
quint32 getIdObject() const
getIdObject return parent id.
Definition: vgobject.cpp:128
void setName(const QString &name)
setName set name graphical object.
Definition: vgobject.cpp:158
Draw getMode() const
getMode return mode creation.
Definition: vgobject.cpp:168
quint32 id() const
id return id object.
Definition: vgobject.cpp:205
The VPointF class keep data of point.
Definition: vpointf.h:75
void setX(const qreal &value)
setX set x coordinate
Definition: vpointf.cpp:233
void setY(const qreal &value)
setY set y coordinate
Definition: vpointf.cpp:253
QPointF toQPointF() const
Definition: vpointf.cpp:213
VPointF Move(qreal length, qreal angle, const QString &prefix=QString()) const
Definition: vpointf.cpp:164
double ToPixel(double val, const Unit &unit)
Definition: def.cpp:231
#define SCASSERT(cond)
Definition: def.h:317
static Q_REQUIRED_RESULT bool VFuzzyComparePossibleNulls(double p1, double p2)
Definition: def.h:490
static Q_REQUIRED_RESULT bool VFuzzyComparePoints(const QPointF &p1, const QPointF &p2, qreal accuracy=accuracyPointOnLine)
Definition: def.h:484
#define ELARC_
Definition: ifcdef.h:240
#define NULL_ID
Definition: ifcdef.h:76
#define qApp
Definition: vapplication.h:67
GOType
Definition: vgeometrydef.h:56
@ EllipticalArc
Draw
Definition: vgeometrydef.h:55
@ Calculation