Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickellipseshape.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include <algorithm>
7#include <QtMath>
8
9QT_BEGIN_NAMESPACE
10
11namespace {
12inline bool is_equal(qreal a, qreal b, qreal epsilon = DBL_EPSILON)
13{
14 return std::fabs(a - b) <= epsilon;
15}
16
17inline qreal arc_angle(qreal angle)
18{
19 return angle - 90;
20}
21
22inline QVector2D arc_point(QVector2D center, QVector2D radius, qreal angle)
23{
24 return QVector2D(center.x() + radius.x() * qCos(qDegreesToRadians(angle)),
25 center.y() + radius.y() * qSin(qDegreesToRadians(angle)));
26}
27
28// counter-clockwise
29inline QVector2D tangent_ccw(QVector2D radius, qreal angle)
30{
31 return QVector2D(-radius.x() * qSin(qDegreesToRadians(angle)),
32 radius.y() * qCos(qDegreesToRadians(angle)));
33}
34
35inline qreal cross(QVector2D a, QVector2D b)
36{
37 return a.x() * b.y() - a.y() * b.x();
38}
39
40// cross product of two vectors defined by points A->B x C->D
41inline qreal cross(QVector2D a, QVector2D b, QVector2D c, QVector2D d)
42{
43 return cross(b - a, d - c);
44}
45
46qreal angle_between_vectors(QVector2D a, QVector2D b)
47{
48 const QVector2D uA = a.normalized();
49 const QVector2D uB = b.normalized();
50 const qreal angle = qAtan2(cross(uA, uB), QVector2D::dotProduct(uA, uB));
51 if (std::fabs(angle) < FLT_EPSILON)
52 return 0.0f;
53 return angle;
54}
55
56// the intersection point can be calculated as C + T * (D - C) or A + S * (B - A)
57bool lines_intersect(QVector2D a, QVector2D b, QVector2D c, QVector2D d, qreal *s, qreal *t)
58{
59 // lines undefined
60 if ((a.x() == b.x() && a.y() == b.y()) || (c.x() == d.x() && c.y() == d.y()))
61 return false;
62
63 const qreal denom = cross(a, b, c, d);
64
65 // lines are parallel or overlap
66 if (denom == 0)
67 return false;
68
69 if (s != nullptr)
70 *s = cross(c, d, c, a) / denom;
71 if (t != nullptr)
72 *t = cross(a, b, c, a) / denom;
73
74 return true;
75}
76} // namespace
77
78QQuickEllipseShapePrivate::QQuickEllipseShapePrivate() = default;
79
80QQuickEllipseShapePrivate::~QQuickEllipseShapePrivate() = default;
81
82void QQuickEllipseShapePrivate::addLine(QVector2D point)
83{
84 auto line = new QQuickPathLine(path);
85 line->setX(point.x());
86 line->setY(point.y());
87 QQuickPathPrivate::get(path)->appendPathElement(line);
88}
89
90void QQuickEllipseShapePrivate::addArc(QVector2D point, QVector2D arcRadius,
91 QQuickPathArc::ArcDirection dir, bool largeArc)
92{
93 auto arc = new QQuickPathArc(path);
94 arc->setX(point.x());
95 arc->setY(point.y());
96 arc->setRadiusX(arcRadius.x());
97 arc->setRadiusY(arcRadius.y());
98 arc->setDirection(dir);
99 arc->setUseLargeArc(largeArc);
100 QQuickPathPrivate::get(path)->appendPathElement(arc);
101}
102
103qreal QQuickEllipseShapePrivate::getBorderOffset() const
104{
105 if (QQuickEllipseShape::BorderMode::Middle == borderMode)
106 return 0;
107 else if (QQuickEllipseShape::BorderMode::Outside == borderMode)
108 return -path->strokeWidth() * 0.5;
109 return path->strokeWidth() * 0.5f; // inside
110}
111
112void QQuickEllipseShapePrivate::roundCenter(QVector2D center, QVector2D ellipseRadius)
113{
114 const qreal endAngle = arc_angle(startAngle + sweepAngle);
115 const QVector2D endPoint = arc_point(center, ellipseRadius, endAngle);
116 const qreal beginAngle = arc_angle(startAngle);
117 const QVector2D beginPoint = arc_point(center, ellipseRadius, beginAngle);
118
119 const QVector2D AB = endPoint - center;
120 const QVector2D AC = beginPoint - center;
121
122 const qreal a = angle_between_vectors(AB, AC);
123 const qreal halfAngle = std::fabs(a) * 0.5f;
124
125 const qreal maxCornerRadius = (std::min(AB.length(), AC.length()) * 0.5f) * qTan(halfAngle);
126 const qreal corner_radius = std::min(cornerRadius, maxCornerRadius);
127
128 // calculate B and C based on the corner radius
129 const qreal edgeOffset = corner_radius / qTan(halfAngle);
130 const QVector2D B = center + (AB.normalized() * edgeOffset);
131 const QVector2D C = center + (AC.normalized() * edgeOffset);
132
133 // update
134 auto &rc = roundedCorners[RoundedCornerIndex::Center];
135 rc.A = center;
136 rc.B = B;
137 rc.C = C;
138 rc.alpha = a;
139 rc.radius = corner_radius;
140}
141
142void QQuickEllipseShapePrivate::roundBeginEnd(QVector2D center, QVector2D ellipseRadius)
143{
144 qreal deg = 0.0f;
145 const qreal endAngle = startAngle + sweepAngle;
146 bool e_outer = false, b_outer = false, e_inner = false, b_inner = false;
147 while (deg < 45.0f) {
148 deg += 1.0f;
149 if (e_outer && b_outer && e_inner && b_inner)
150 break;
151 if (!b_outer)
152 b_outer = roundOuter(center, ellipseRadius, deg, startAngle, endAngle,
153 RoundedCornerIndex::OuterBegin);
154 if (!e_outer)
155 e_outer = roundOuter(center, ellipseRadius, deg, endAngle, startAngle,
156 RoundedCornerIndex::OuterEnd);
157 if (!e_inner)
158 e_inner = roundInner(center, ellipseRadius, deg, endAngle, startAngle,
159 RoundedCornerIndex::InnerEnd);
160 if (!b_inner)
161 b_inner = roundInner(center, ellipseRadius, deg, startAngle, endAngle,
162 RoundedCornerIndex::InnerBegin);
163 }
164}
165
166bool QQuickEllipseShapePrivate::roundOuter(QVector2D center, QVector2D ellipseRadius, qreal deg,
167 qreal arcAngle1, qreal arcAngle2,
168 RoundedCornerIndex index)
169{
170 bool done = false;
171
172 const qreal arcAngle = arc_angle(arcAngle1);
173 const QVector2D arcPoint = arc_point(center, ellipseRadius, arcAngle);
174
175 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
176
177 const QVector2D B = arc_point(center, ellipseRadius, angle); // point on arc
178
179 // calculate tangent vector
180 const QVector2D uV = tangent_ccw(ellipseRadius, angle).normalized();
181 const QVector2D b1 = B + uV;
182
183 qreal s = 0, t = 0;
184 bool res = lines_intersect(center, arcPoint, B, b1, &s, &t);
185 if (res) {
186 const QVector2D A = center + s * (arcPoint - center);
187
188 const QVector2D AB = B - A;
189 const QVector2D AC = center - A;
190
191 const qreal a = angle_between_vectors(AB, AC);
192 const qreal halfAngle = std::fabs(a) * 0.5;
193 const qreal edgeOffset = AB.length();
194
195 const qreal corner_radius = edgeOffset * qTan(halfAngle);
196
197 // constrain by sweep
198 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
199 const qreal degMax = std::min(sweep, 45.0);
200
201 const QVector2D C = A + AC.normalized() * edgeOffset;
202
203 const qreal ptoc = (arcPoint - C).length();
204 qreal edge = 0;
205 if (innerArcRatio > 0) {
206 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
207 innerArcRatio * ellipseRadius.y());
208 const QVector2D innerArcPoint =
209 arc_point(center, ellipseInnerRadius, arcAngle); // point on the inner arc
210 edge = (arcPoint - innerArcPoint).length();
211 } else {
212 edge = (arcPoint - center).length();
213 }
214
215 const qreal diff = std::fabs(corner_radius - cornerRadius); // closest to target radius
216
217 auto &rc = roundedCorners[index];
218
219 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius) && !(deg > degMax)
220 && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
221
222 // 1st loop or if constraints are met
223 if (rc.radius == 0 || canUpdate)
224 rc.update(diff, A, B, C, a, corner_radius);
225
226 done =
227 // corner radius is bigger or equal to the target radius
228 corner_radius > cornerRadius
229 || is_equal(corner_radius, cornerRadius, 0.01f)
230 // angle used to define point B is bigger than available sweep
231 || deg > degMax
232 || is_equal(deg, degMax, 0.01f)
233 // the corner radius starts to decline (from previously calculated)
234 || (rc.radius != 0 && rc.radius > corner_radius)
235 // point C is beyond the half of the ellipse edge
236 // (closer to the ellipse center or inner arc point)
237 || (ptoc > edge * 0.5f);
238 }
239
240 return done;
241}
242
243bool QQuickEllipseShapePrivate::roundInner(QVector2D center, QVector2D ellipseRadius, qreal deg,
244 qreal arcAngle1, qreal arcAngle2,
245 RoundedCornerIndex index)
246{
247 // make rounding corner bigger and produces smoother result
248 const qreal smoothFactor = 1.5;
249
250 deg *= smoothFactor;
251
252 bool done = false;
253
254 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
255 innerArcRatio * ellipseRadius.y());
256
257 const qreal arcAngle = arc_angle(arcAngle1);
258 const QVector2D innerArcPoint =
259 arc_point(center, ellipseInnerRadius, arcAngle); // point on the inner arc
260
261 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
262
263 const QVector2D B = arc_point(center, ellipseInnerRadius, angle); // point on arc
264
265 // calculate tangent vector
266 const QVector2D uV = tangent_ccw(ellipseInnerRadius, angle).normalized();
267 const QVector2D b1 = B + uV;
268
269 qreal s = 0, t = 0;
270 bool res = lines_intersect(center, innerArcPoint, B, b1, &s, &t);
271
272 if (res) {
273 // hit point
274 const QVector2D A = center + s * (innerArcPoint - center); // point on edge
275
276 const auto arcPoint = arc_point(center, ellipseRadius, arcAngle);
277
278 const QVector2D AB = B - A;
279 const QVector2D AC = A - innerArcPoint;
280
281 const qreal a = angle_between_vectors(AB, AC);
282 const qreal halfAngle = std::fabs(a) * 0.5;
283 const qreal edgeOffset = AB.length();
284
285 const qreal corner_radius = edgeOffset * qTan(halfAngle);
286
287 // constrain by sweep
288 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
289 const qreal degMax = std::min(sweep, 45.0);
290
291 const QVector2D C = A + AC.normalized() * edgeOffset;
292
293 const qreal ptoc = (innerArcPoint - C).length();
294 const qreal edge = (innerArcPoint - arcPoint).length();
295
296 const qreal diff =
297 std::fabs(corner_radius - cornerRadius * smoothFactor); // closest to target radius
298
299 auto &rc = roundedCorners[index];
300
301 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius * smoothFactor)
302 && !(deg > degMax) && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
303
304 // 1st loop or if constraints are met
305 if (rc.radius == 0 || canUpdate)
306 rc.update(diff, A, B, C, a, corner_radius);
307
308 done =
309 // corner radius is bigger or equal to the target radius
310 corner_radius > cornerRadius * smoothFactor
311 || is_equal(corner_radius, cornerRadius * smoothFactor, 0.01f)
312 // angle used to define point B is bigger than available sweep
313 || deg > degMax
314 || is_equal(deg, degMax, 0.01f)
315 // the corner radius starts to decline (from previously calculated)
316 || (rc.radius != 0 && rc.radius > corner_radius)
317 // point C is beyond the half of the ellipse edge
318 // (closer to the inner arc end point)
319 || (ptoc > edge * 0.5f);
320 }
321
322 return done;
323}
324
325void QQuickEllipseShapePrivate::drawCenterCorner()
326{
327 auto &rc = roundedCorners[RoundedCornerIndex::Center];
328 path->setStartX(rc.B.x());
329 path->setStartY(rc.B.y());
330
331 addArc(rc.C, QVector2D(rc.radius, rc.radius),
332 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
333}
334
335void QQuickEllipseShapePrivate::drawInnerEndCorner()
336{
337 auto &rc = roundedCorners[RoundedCornerIndex::InnerEnd];
338
339 addLine(rc.C);
340
341 addArc(rc.B, QVector2D(rc.radius, rc.radius),
342 rc.alpha > 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
343}
344
345void QQuickEllipseShapePrivate::drawInnerBeginCorner()
346{
347 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
348 path->setStartX(rc.B.x());
349 path->setStartY(rc.B.y());
350
351 addArc(rc.C, QVector2D(rc.radius, rc.radius),
352 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
353}
354
355void QQuickEllipseShapePrivate::drawOuterBeginCorner()
356{
357 auto &rc = roundedCorners[RoundedCornerIndex::OuterBegin];
358
359 addLine(rc.C);
360
361 addArc(rc.B, QVector2D(rc.radius, rc.radius),
362 rc.alpha > 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
363}
364
365void QQuickEllipseShapePrivate::drawOuterArcRounded(QVector2D center, QVector2D ellipseRadius)
366{
367 // split outer arc in two parts to avoid issues of the large arc
368 const qreal endAngle = startAngle + sweepAngle;
369
370 const qreal angle = startAngle > endAngle
371 ? arc_angle(startAngle - std::fabs(sweepAngle * 0.5f))
372 : arc_angle(startAngle + std::fabs(sweepAngle * 0.5f));
373 const auto point = arc_point(center, ellipseRadius, angle); // mid point of the arc
374
375 // from begin to mid point
376 addArc(point, ellipseRadius,
377 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
378
379 auto &rc = roundedCorners[RoundedCornerIndex::OuterEnd];
380
381 // from mid point to end rounded corner
382 addArc(rc.B, ellipseRadius,
383 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
384
385 // rounded corner
386 addArc(rc.C, QVector2D(rc.radius, rc.radius),
387 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
388}
389
390void QQuickEllipseShapePrivate::drawInnerArcRounded(QVector2D center, QVector2D ellipseRadius)
391{
392 // split inner arc in two parts to avoid issues of the large arc
393 const qreal endAngle = startAngle + sweepAngle;
394
395 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
396 innerArcRatio * ellipseRadius.y());
397
398 const qreal angle = endAngle > startAngle ? arc_angle(endAngle - std::fabs(sweepAngle * 0.5f))
399 : arc_angle(endAngle + std::fabs(sweepAngle * 0.5f));
400 const auto point = arc_point(center, ellipseInnerRadius, angle); // mid point of the arc
401
402 // from end to mid point
403 addArc(point, ellipseInnerRadius,
404 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
405
406 // from mid point to begin rounded corner
407 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
408 addArc(rc.B, ellipseInnerRadius,
409 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
410}
411
412void QQuickEllipseShapePrivate::drawOuterArc(QVector2D center, QVector2D ellipseRadius)
413{
414 const qreal beginAngle = arc_angle(startAngle);
415 const qreal endAngle = arc_angle(startAngle + sweepAngle);
416
417 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
418 bool isFull = (alpha <= 0.0f || alpha >= 359.0f);
419
420 // QQuickPathArc has some weird behavior when it starts and ends at the same point
421 // leave some gap between the start and the end points in order to avoid it
422 const auto beginPoint = arc_point(center, ellipseRadius, isFull ? 0 : beginAngle);
423 const auto endPoint = arc_point(center, ellipseRadius, isFull ? 359.9 : endAngle);
424
425 path->setStartX(beginPoint.x());
426 path->setStartY(beginPoint.y());
427
428 addArc(endPoint, ellipseRadius,
429 isFull ? QQuickPathArc::Clockwise
430 : (sweepAngle > 0.0f ? QQuickPathArc::Clockwise
431 : QQuickPathArc::Counterclockwise),
432 isFull ? true : alpha > 180.0f);
433}
434
435void QQuickEllipseShapePrivate::drawFullInnerArc(QVector2D center, QVector2D ellipseRadius)
436{
437 const qreal beginAngle = arc_angle(startAngle);
438
439 auto arc = new QQuickPathAngleArc(path);
440 arc->setCenterX(center.x());
441 arc->setCenterY(center.y());
442 arc->setStartAngle(beginAngle);
443 arc->setRadiusX(innerArcRatio * ellipseRadius.x());
444 arc->setRadiusY(innerArcRatio * ellipseRadius.y());
445 arc->setSweepAngle(sweepAngle);
446 QQuickPathPrivate::get(path)->appendPathElement(arc);
447}
448
449void QQuickEllipseShapePrivate::drawWithInnerRadius(QVector2D center, QVector2D ellipseRadius)
450{
451 drawInnerBeginCorner(); // path starts at the begin rounded corner on the inner arc
452
453 drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
454
455 // outer arc connecting begin and end rounded corners
456 drawOuterArcRounded(center, ellipseRadius);
457
458 // path continues to the end rounded corner on the inner arc
459 drawInnerEndCorner();
460
461 // inner arc connecting end and begin rounded corners
462 drawInnerArcRounded(center, ellipseRadius);
463}
464
465void QQuickEllipseShapePrivate::drawWithoutInnerRadius(QVector2D center, QVector2D ellipseRadius)
466{
467 drawCenterCorner(); // path starts at rounded corner of ellipse center
468
469 drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
470
471 // outer arc connecting begin and end rounded corners
472 drawOuterArcRounded(center, ellipseRadius);
473
474 // path ends at the ellipse's center rounded corner
475 const auto &rc = roundedCorners[RoundedCornerIndex::Center];
476 addLine(rc.B);
477}
478
479void QQuickEllipseShapePrivate::updatePath()
480{
481 const qreal borderOffset = getBorderOffset();
482 const QVector2D center =
483 QVector2D(width.valueBypassingBindings(), height.valueBypassingBindings()) * 0.5f;
484 const QVector2D ellipseRadius = center - QVector2D(borderOffset, borderOffset);
485
486 QQuickPathPrivate::get(path)->clearPathElements(QQuickPathPrivate::DeleteElementPolicy::Delete);
487
488 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
489 const bool isFull = alpha >= 359.0;
490
491 if (qFuzzyCompare(alpha, 0))
492 return;
493
494 // just an arc
495 if (qFuzzyCompare(innerArcRatio, 1) || (hideLine && qFuzzyCompare(innerArcRatio, 0))) {
496 drawOuterArc(center, ellipseRadius);
497 return;
498 }
499
500 roundedCorners.reset(); // cleanup old results
501
502 if (innerArcRatio != 0 && isFull) {
503 // this is a donut
504 drawOuterArc(center, ellipseRadius);
505 drawFullInnerArc(center, ellipseRadius);
506 } else if (innerArcRatio != 0 && !isFull) {
507 // this is an outlined arc
508 roundBeginEnd(center, ellipseRadius);
509 drawWithInnerRadius(center, ellipseRadius);
510 } else if (!isFull) {
511 // this is a pie
512 roundCenter(center, ellipseRadius);
513 roundBeginEnd(center, ellipseRadius);
514 drawWithoutInnerRadius(center, ellipseRadius);
515 } else {
516 drawOuterArc(center, ellipseRadius);
517 }
518}
519
520/*!
521 \qmltype EllipseShape
522 \inqmlmodule QtQuick.Shapes.DesignHelpers
523 \brief A shape component that can render an ellipse, an arc, or a pie slice.
524 \since QtQuick 6.10
525
526 The EllipseShape item paints an ellipse, which can be customized to appear
527 as a full ellipse, an arc, or a filled pie slice. Its appearance is
528 controlled by the \l startAngle and \l sweepAngle properties.
529
530 \section1 Basic Ellipse
531 By default, the item renders a full ellipse. The interior is filled with the
532 \l fillColor, and the outline is drawn according to the \l strokeColor, \l
533 strokeWidth, and \l strokeStyle properties.
534
535 \section1 Arc and Pie Slices
536 To create an arc or a pie slice, set the \l startAngle (0-360 degrees) and
537 \l sweepAngle (0-360 degrees) to define the segment of the ellipse to draw.
538
539 \b {Arc Mode}: To create a simple arc (just the outline), set the \l
540 fillColor to \c "transparent". The arc's line style can be customized with
541 \l dashPattern and \l dashOffset.
542
543 \b {Pie Mode}: To create a filled pie slice (a segment connected to the
544 center), simply set the \l fillColor. The outline of the slice will also be
545 stroked.
546
547 \b {Donut Mode}: To create a donut ring (a hollow ellipse), set the
548 \l innerArcRatio to a value between 0.0 and 1.0. This defines the ratio of
549 the inner ellipse's radius to the outer ellipse's radius.
550
551 The area inside the stroke is painted using either a solid fill color,
552 specified using the \l fillColor property, or a gradient, defined using one
553 of the \l ShapeGradient subtypes and set using the \l fillGradient
554 property. If both a color and a gradient are specified, the gradient is
555 used.
556
557 An optional border can be added to an ellipse with its own color and
558 thickness by setting the \l strokeColor and \l strokeWidth properties.
559 Setting the color to \c transparent creates a border without a fill color.
560
561 Ellipse can be drawn with rounded corners using the \l cornerRadius
562 property. The default value of the \l cornerRadius is 10 degrees.
563
564 EllipseShape's default value for \l {QtQuick.Shapes::Shape::preferredRendererType} is
565 \c Shape.CurveRenderer.
566
567 \section1 Example Usage
568
569 \snippet ellipseshape.qml ellipseShape
570
571 \image path-ellipseshape.png
572*/
573QQuickEllipseShape::QQuickEllipseShape(QQuickItem *parent)
574 : QQuickShape(*(new QQuickEllipseShapePrivate), parent)
575{
576 Q_D(QQuickEllipseShape);
577
578 setPreferredRendererType(CurveRenderer);
579
580 setWidth(100);
581 setHeight(100);
582
583 d->path = new QQuickShapePath(this);
584 d->path->setParent(this);
585 d->path->setAsynchronous(true);
586 d->path->setStrokeWidth(4);
587 d->path->setStrokeColor(QColorConstants::Black);
588
589 d->sp.append(d->path);
590 d->path->setParent(this);
591 d->extra.value().resourcesList.append(d->path);
592}
593
594QQuickEllipseShape::~QQuickEllipseShape() = default;
595
596bool QQuickEllipseShape::hideLine() const
597{
598 Q_D(const QQuickEllipseShape);
599 return d->hideLine;
600}
601
602void QQuickEllipseShape::setHideLine(bool hideLine)
603{
604 Q_D(QQuickEllipseShape);
605 if (d->hideLine == hideLine)
606 return;
607 d->hideLine = hideLine;
608 d->updatePath();
609 emit hideLineChanged();
610}
611
612/*!
613 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::sweepAngle
614
615 The angular extent in degrees to be drawn from the \l startAngle.
616
617 If set to positive value, the arc is drawn in clockwise direction.
618 If set to negative value, the arc is drawn in counter-clockwise direction.
619
620 The default value is \c 360.
621*/
622qreal QQuickEllipseShape::sweepAngle() const
623{
624 Q_D(const QQuickEllipseShape);
625 return d->sweepAngle;
626}
627
628void QQuickEllipseShape::setSweepAngle(qreal sweepAngle)
629{
630 Q_D(QQuickEllipseShape);
631 if (qFuzzyCompare(d->sweepAngle, sweepAngle))
632 return;
633 d->sweepAngle = sweepAngle;
634 d->updatePath();
635 emit sweepAngleChanged();
636}
637
638/*!
639 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::startAngle
640
641 The property defines the starting angle in degrees from which to begin
642 drawing the ellipse.
643
644 0 degrees points to the top. Angle increases in clockwise direction.
645
646 The default value is \c 0.
647*/
648qreal QQuickEllipseShape::startAngle() const
649{
650 Q_D(const QQuickEllipseShape);
651 return d->startAngle;
652}
653
654void QQuickEllipseShape::setStartAngle(qreal startAngle)
655{
656 Q_D(QQuickEllipseShape);
657 if (qFuzzyCompare(d->startAngle, startAngle))
658 return;
659 d->startAngle = startAngle;
660 d->updatePath();
661 emit startAngleChanged();
662}
663
664/*!
665 \include shapepath.qdocinc {dashOffset-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
666*/
667
668qreal QQuickEllipseShape::dashOffset() const
669{
670 Q_D(const QQuickEllipseShape);
671 return d->path->dashOffset();
672}
673
674void QQuickEllipseShape::setDashOffset(qreal offset)
675{
676 Q_D(QQuickEllipseShape);
677 if (qFuzzyCompare(d->path->dashOffset(), offset))
678 return;
679 d->path->setDashOffset(offset);
680 d->updatePath();
681 emit dashOffsetChanged();
682}
683
684/*!
685 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::cornerRadius
686
687 Controls the rounding of corners where the radial lines meet the elliptical
688 arcs. For pie segments, this rounds the connection to the outer arc. For
689 donut segments, this also rounds the connections to both inner and outer arcs.
690
691 The default value is \c 10.
692*/
693qreal QQuickEllipseShape::cornerRadius() const
694{
695 Q_D(const QQuickEllipseShape);
696 return d->cornerRadius;
697}
698
699void QQuickEllipseShape::setCornerRadius(qreal cornerRadius)
700{
701 Q_D(QQuickEllipseShape);
702 if (qFuzzyCompare(d->cornerRadius, cornerRadius))
703 return;
704 d->cornerRadius = cornerRadius;
705 d->updatePath();
706 emit cornerRadiusChanged();
707}
708
709/*!
710 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::innerArcRatio
711
712 This property defines the ratio between the inner and outer arcs.
713
714 Value range is between 0.0 and 1.0. Setting the value to 0.0 will cause
715 the inner arc to collapse toward the center, drawing a solid filled
716 ellipse. Setting the value to 1.0 makes the inner arc the same size as the
717 outer ellipse, resulting in just an arc. Values between 0.0 and 1.0 create
718 hollow elliptical rings.
719
720 The default value is \c 0.
721*/
722qreal QQuickEllipseShape::innerArcRatio() const
723{
724 Q_D(const QQuickEllipseShape);
725 return d->innerArcRatio;
726}
727
728void QQuickEllipseShape::setInnerArcRatio(qreal innerArcRatio)
729{
730 Q_D(QQuickEllipseShape);
731 if (qFuzzyCompare(d->innerArcRatio, innerArcRatio))
732 return;
733 d->innerArcRatio = innerArcRatio;
734 d->updatePath();
735 emit innerArcRatioChanged();
736}
737
738/*!
739 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::strokeWidth
740
741 This property holds the stroke width.
742
743 When set to a negative value, no stroking occurs.
744
745 The default value is \c 1.
746*/
747
748qreal QQuickEllipseShape::strokeWidth() const
749{
750 Q_D(const QQuickEllipseShape);
751 return d->path->strokeWidth();
752}
753
754void QQuickEllipseShape::setStrokeWidth(qreal width)
755{
756 Q_D(QQuickEllipseShape);
757 if (qFuzzyCompare(d->path->strokeWidth(), width))
758 return;
759 d->path->setStrokeWidth(width);
760 d->updatePath();
761 emit strokeWidthChanged();
762}
763
764/*!
765 \qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::fillColor
766
767 This property holds the fill color.
768
769 When set to \c transparent, no filling occurs.
770
771 The default value is \c "white".
772
773 \note If either \l fillGradient is set to something other than \c null, it
774 will be used instead of \c fillColor.
775*/
776
777QColor QQuickEllipseShape::fillColor() const
778{
779 Q_D(const QQuickEllipseShape);
780 return d->path->fillColor();
781}
782
783void QQuickEllipseShape::setFillColor(const QColor &color)
784{
785 Q_D(QQuickEllipseShape);
786 d->path->setFillColor(color);
787 d->updatePath();
788 emit fillColorChanged();
789}
790
791/*!
792 \qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::strokeColor
793
794 This property holds the stroking color.
795
796 When set to \c transparent, no stroking occurs.
797
798 The default value is \c "black".
799*/
800
801QColor QQuickEllipseShape::strokeColor() const
802{
803 Q_D(const QQuickEllipseShape);
804 return d->path->strokeColor();
805}
806
807void QQuickEllipseShape::setStrokeColor(const QColor &color)
808{
809 Q_D(QQuickEllipseShape);
810 d->path->setStrokeColor(color);
811 d->updatePath();
812 emit strokeColorChanged();
813}
814
815/*!
816 \include shapepath.qdocinc {capStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
817*/
818
819QQuickShapePath::CapStyle QQuickEllipseShape::capStyle() const
820{
821 Q_D(const QQuickEllipseShape);
822 return d->path->capStyle();
823}
824
825void QQuickEllipseShape::setCapStyle(QQuickShapePath::CapStyle style)
826{
827 Q_D(QQuickEllipseShape);
828 if (d->path->capStyle() == style)
829 return;
830 d->path->setCapStyle(style);
831 d->updatePath();
832 emit capStyleChanged();
833}
834
835/*!
836 \include shapepath.qdocinc {joinStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
837*/
838
839QQuickShapePath::JoinStyle QQuickEllipseShape::joinStyle() const
840{
841 Q_D(const QQuickEllipseShape);
842 return d->path->joinStyle();
843}
844
845void QQuickEllipseShape::setJoinStyle(QQuickShapePath::JoinStyle style)
846{
847 Q_D(QQuickEllipseShape);
848 if (d->path->joinStyle() == style)
849 return;
850 d->path->setJoinStyle(style);
851 d->updatePath();
852 emit joinStyleChanged();
853}
854
855/*!
856 \include shapepath.qdocinc {strokeStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
857*/
858
859QQuickShapePath::StrokeStyle QQuickEllipseShape::strokeStyle() const
860{
861 Q_D(const QQuickEllipseShape);
862 return d->path->strokeStyle();
863}
864
865void QQuickEllipseShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
866{
867 Q_D(QQuickEllipseShape);
868 if (d->path->strokeStyle() == style)
869 return;
870 d->path->setStrokeStyle(style);
871 d->updatePath();
872 emit strokeStyleChanged();
873}
874
875/*!
876 \include shapepath.qdocinc {fillRule-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
877*/
878
879QQuickShapePath::FillRule QQuickEllipseShape::fillRule() const
880{
881 Q_D(const QQuickEllipseShape);
882 return d->path->fillRule();
883}
884
885void QQuickEllipseShape::setFillRule(QQuickShapePath::FillRule fillRule)
886{
887 Q_D(QQuickEllipseShape);
888 if (d->path->fillRule() == fillRule)
889 return;
890 d->path->setFillRule(fillRule);
891 d->updatePath();
892 emit fillRuleChanged();
893}
894
895/*!
896 \include shapepath.qdocinc {dashPattern-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
897*/
898
899QVector<qreal> QQuickEllipseShape::dashPattern() const
900{
901 Q_D(const QQuickEllipseShape);
902 return d->path->dashPattern();
903}
904
905void QQuickEllipseShape::setDashPattern(const QVector<qreal> &array)
906{
907 Q_D(QQuickEllipseShape);
908 d->path->setDashPattern(array);
909 d->updatePath();
910 emit dashPatternChanged();
911}
912
913/*!
914 \qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::EllipseShape::fillGradient
915
916 The fillGradient of the ellipse fill color.
917
918 By default, no fillGradient is enabled and the value is null. In this case, the
919 fill uses a solid color based on the value of \l fillColor.
920
921 When set, \l fillColor is ignored and filling is done using one of the
922 \l ShapeGradient subtypes.
923
924 \note The \l Gradient type cannot be used here. Rather, prefer using one of
925 the advanced subtypes, like \l LinearGradient.
926*/
927QQuickShapeGradient *QQuickEllipseShape::fillGradient() const
928{
929 Q_D(const QQuickEllipseShape);
930 return d->path->fillGradient();
931}
932
933void QQuickEllipseShape::setFillGradient(QQuickShapeGradient *fillGradient)
934{
935 Q_D(QQuickEllipseShape);
936 d->path->setFillGradient(fillGradient);
937 d->updatePath();
938 emit gradientChanged();
939}
940
941void QQuickEllipseShape::resetFillGradient()
942{
943 setFillGradient(nullptr);
944}
945
946/*!
947 \qmlproperty enumeration QtQuick.Shapes.DesignHelpers::EllipseShape::borderMode
948
949 The \l borderMode property determines where the border is drawn along the
950 edge of the ellipse.
951
952 \value EllipseShape.Inside
953 The border is drawn along the inside edge of the item and does not
954 affect the item width.
955
956 This is the default value.
957 \value EllipseShape.Middle
958 The border is drawn over the edge of the item and does not
959 affect the item width.
960 \value EllipseShape.Outside
961 The border is drawn along the outside edge of the item and increases
962 the item width by the value of \l strokeWidth.
963
964 \sa strokeWidth
965*/
966QQuickEllipseShape::BorderMode QQuickEllipseShape::borderMode() const
967{
968 Q_D(const QQuickEllipseShape);
969 return d->borderMode;
970}
971
972void QQuickEllipseShape::setBorderMode(BorderMode borderMode)
973{
974 Q_D(QQuickEllipseShape);
975 if (borderMode == d->borderMode)
976 return;
977 d->borderMode = borderMode;
978 d->updatePath();
979 emit borderModeChanged();
980}
981
982void QQuickEllipseShape::resetBorderMode()
983{
984 setBorderMode(BorderMode::Inside);
985}
986
987void QQuickEllipseShape::itemChange(ItemChange change, const ItemChangeData &value)
988{
989 Q_D(QQuickEllipseShape);
990
991 if (d->path)
992 d->updatePath();
993
994 QQuickItem::itemChange(change, value);
995}
996
997QT_END_NAMESPACE
998
999#include "moc_qquickellipseshape_p.cpp"
qreal angle_between_vectors(QVector2D a, QVector2D b)
qreal cross(QVector2D a, QVector2D b)
qreal arc_angle(qreal angle)
QVector2D tangent_ccw(QVector2D radius, qreal angle)
bool lines_intersect(QVector2D a, QVector2D b, QVector2D c, QVector2D d, qreal *s, qreal *t)
qreal cross(QVector2D a, QVector2D b, QVector2D c, QVector2D d)
bool is_equal(qreal a, qreal b, qreal epsilon=DBL_EPSILON)
QVector2D arc_point(QVector2D center, QVector2D radius, qreal angle)