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 // add reverse arc to hide fill color
435 if (qFuzzyCompare(innerArcRatio, 1)) {
436 addArc(beginPoint, ellipseRadius,
437 isFull ? QQuickPathArc::Counterclockwise
438 : (sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise
439 : QQuickPathArc::Clockwise),
440 isFull ? true : alpha > 180.0f);
441 }
442}
443
444void QQuickEllipseShapePrivate::drawFullInnerArc(QVector2D center, QVector2D ellipseRadius)
445{
446 const qreal beginAngle = arc_angle(startAngle);
447
448 auto arc = new QQuickPathAngleArc(path);
449 arc->setCenterX(center.x());
450 arc->setCenterY(center.y());
451 arc->setStartAngle(beginAngle);
452 arc->setRadiusX(innerArcRatio * ellipseRadius.x());
453 arc->setRadiusY(innerArcRatio * ellipseRadius.y());
454 arc->setSweepAngle(sweepAngle);
455 QQuickPathPrivate::get(path)->appendPathElement(arc);
456}
457
458void QQuickEllipseShapePrivate::drawWithInnerRadius(QVector2D center, QVector2D ellipseRadius)
459{
460 drawInnerBeginCorner(); // path starts at the begin rounded corner on the inner arc
461
462 drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
463
464 // outer arc connecting begin and end rounded corners
465 drawOuterArcRounded(center, ellipseRadius);
466
467 // path continues to the end rounded corner on the inner arc
468 drawInnerEndCorner();
469
470 // inner arc connecting end and begin rounded corners
471 drawInnerArcRounded(center, ellipseRadius);
472}
473
474void QQuickEllipseShapePrivate::drawWithoutInnerRadius(QVector2D center, QVector2D ellipseRadius)
475{
476 drawCenterCorner(); // path starts at rounded corner of ellipse center
477
478 drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
479
480 // outer arc connecting begin and end rounded corners
481 drawOuterArcRounded(center, ellipseRadius);
482
483 // path ends at the ellipse's center rounded corner
484 const auto &rc = roundedCorners[RoundedCornerIndex::Center];
485 addLine(rc.B);
486}
487
488void QQuickEllipseShapePrivate::updatePath()
489{
490 const qreal borderOffset = getBorderOffset();
491 const QVector2D center =
492 QVector2D(width.valueBypassingBindings(), height.valueBypassingBindings()) * 0.5f;
493 const QVector2D ellipseRadius = center - QVector2D(borderOffset, borderOffset);
494
495 QQuickPathPrivate::get(path)->clearPathElements(QQuickPathPrivate::DeleteElementPolicy::Delete);
496
497 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
498 const bool isFull = alpha >= 359.0;
499
500 if (qFuzzyCompare(alpha, 0))
501 return;
502
503 // just an arc
504 if (qFuzzyCompare(innerArcRatio, 1)) {
505 drawOuterArc(center, ellipseRadius);
506 return;
507 }
508
509 roundedCorners.reset(); // cleanup old results
510
511 if (innerArcRatio != 0 && isFull) {
512 // this is a donut
513 drawOuterArc(center, ellipseRadius);
514 drawFullInnerArc(center, ellipseRadius);
515 } else if (innerArcRatio != 0 && !isFull) {
516 // this is an outlined arc
517 roundBeginEnd(center, ellipseRadius);
518 drawWithInnerRadius(center, ellipseRadius);
519 } else if (!isFull) {
520 // this is a pie
521 roundCenter(center, ellipseRadius);
522 roundBeginEnd(center, ellipseRadius);
523 drawWithoutInnerRadius(center, ellipseRadius);
524 } else {
525 drawOuterArc(center, ellipseRadius);
526 }
527}
528
529/*!
530 \qmltype EllipseShape
531 \inqmlmodule QtQuick.Shapes.DesignHelpers
532 \brief A shape component that can render an ellipse, an arc, or a pie slice.
533 \since QtQuick 6.10
534
535 The EllipseShape item paints an ellipse, which can be customized to appear
536 as a full ellipse, an arc, or a filled pie slice. Its appearance is
537 controlled by the \l startAngle and \l sweepAngle properties.
538
539 \section1 Basic Ellipse
540 By default, the item renders a full ellipse. The interior is filled with the
541 \l fillColor, and the outline is drawn according to the \l strokeColor, \l
542 strokeWidth, and \l strokeStyle properties.
543
544 \section1 Arc and Pie Slices
545 To create an arc or a pie slice, set the \l startAngle (0-360 degrees) and
546 \l sweepAngle (0-360 degrees) to define the segment of the ellipse to draw.
547
548 \b {Arc Mode}: To create a simple arc (just the outline), set the \l
549 fillColor to \c "transparent". The arc's line style can be customized with
550 \l dashPattern and \l dashOffset.
551
552 \b {Pie Mode}: To create a filled pie slice (a segment connected to the
553 center), simply set the \l fillColor. The outline of the slice will also be
554 stroked.
555
556 \b {Donut Mode}: To create a donut ring (a hollow ellipse), set the
557 \l innerArcRatio to a value between 0.0 and 1.0. This defines the ratio of
558 the inner ellipse's radius to the outer ellipse's radius.
559
560 The area inside the stroke is painted using either a solid fill color,
561 specified using the \l fillColor property, or a gradient, defined using one
562 of the \l ShapeGradient subtypes and set using the \l fillGradient
563 property. If both a color and a gradient are specified, the gradient is
564 used.
565
566 An optional border can be added to an ellipse with its own color and
567 thickness by setting the \l strokeColor and \l strokeWidth properties.
568 Setting the color to \c transparent creates a border without a fill color.
569
570 Ellipse can be drawn with rounded corners using the \l cornerRadius
571 property. The default value of the \l cornerRadius is 10 degrees.
572
573 EllipseShape's default value for \l {QtQuick.Shapes::Shape::preferredRendererType} is
574 \c Shape.CurveRenderer.
575
576 \section1 Example Usage
577
578 \snippet ellipseshape.qml ellipseShape
579
580 \image path-ellipseshape.png
581*/
582QQuickEllipseShape::QQuickEllipseShape(QQuickItem *parent)
583 : QQuickShape(*(new QQuickEllipseShapePrivate), parent)
584{
585 Q_D(QQuickEllipseShape);
586
587 setPreferredRendererType(CurveRenderer);
588
589 setWidth(200);
590 setHeight(200);
591
592 d->path = new QQuickShapePath(this);
593 d->path->setParent(this);
594 d->path->setAsynchronous(true);
595 d->path->setStrokeWidth(1);
596 d->path->setStrokeColor(QColorConstants::Black);
597 d->path->setFillColor(QColorConstants::White);
598
599 d->sp.append(d->path);
600 d->path->setParent(this);
601 d->extra.value().resourcesList.append(d->path);
602}
603
604QQuickEllipseShape::~QQuickEllipseShape() = default;
605
606/*!
607 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::sweepAngle
608
609 The angular extent in degrees to be drawn from the \l startAngle.
610
611 If set to positive value, the arc is drawn in clockwise direction.
612 If set to negative value, the arc is drawn in counter-clockwise direction.
613
614 The default value is \c 360.
615*/
616qreal QQuickEllipseShape::sweepAngle() const
617{
618 Q_D(const QQuickEllipseShape);
619 return d->sweepAngle;
620}
621
622void QQuickEllipseShape::setSweepAngle(qreal sweepAngle)
623{
624 Q_D(QQuickEllipseShape);
625 if (qFuzzyCompare(d->sweepAngle, sweepAngle))
626 return;
627 d->sweepAngle = sweepAngle;
628 d->updatePath();
629 emit sweepAngleChanged();
630}
631
632/*!
633 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::startAngle
634
635 The property defines the starting angle in degrees from which to begin
636 drawing the ellipse.
637
638 0 degrees points to the top. Angle increases in clockwise direction.
639
640 The default value is \c 0.
641*/
642qreal QQuickEllipseShape::startAngle() const
643{
644 Q_D(const QQuickEllipseShape);
645 return d->startAngle;
646}
647
648void QQuickEllipseShape::setStartAngle(qreal startAngle)
649{
650 Q_D(QQuickEllipseShape);
651 if (qFuzzyCompare(d->startAngle, startAngle))
652 return;
653 d->startAngle = startAngle;
654 d->updatePath();
655 emit startAngleChanged();
656}
657
658/*!
659 \include shapepath.qdocinc {dashOffset-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
660*/
661
662qreal QQuickEllipseShape::dashOffset() const
663{
664 Q_D(const QQuickEllipseShape);
665 return d->path->dashOffset();
666}
667
668void QQuickEllipseShape::setDashOffset(qreal offset)
669{
670 Q_D(QQuickEllipseShape);
671 if (qFuzzyCompare(d->path->dashOffset(), offset))
672 return;
673 d->path->setDashOffset(offset);
674 d->updatePath();
675 emit dashOffsetChanged();
676}
677
678/*!
679 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::cornerRadius
680
681 Controls the rounding of corners where the radial lines meet the elliptical
682 arcs. For pie segments, this rounds the connection to the outer arc. For
683 donut segments, this also rounds the connections to both inner and outer arcs.
684
685 The default value is \c 10.
686*/
687qreal QQuickEllipseShape::cornerRadius() const
688{
689 Q_D(const QQuickEllipseShape);
690 return d->cornerRadius;
691}
692
693void QQuickEllipseShape::setCornerRadius(qreal cornerRadius)
694{
695 Q_D(QQuickEllipseShape);
696 if (qFuzzyCompare(d->cornerRadius, cornerRadius))
697 return;
698 d->cornerRadius = cornerRadius;
699 d->updatePath();
700 emit cornerRadiusChanged();
701}
702
703/*!
704 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::innerArcRatio
705
706 This property defines the ratio between the inner and outer arcs.
707
708 Value range is between 0.0 and 1.0. Setting the value to 0.0 will cause
709 the inner arc to collapse toward the center, drawing a solid filled
710 ellipse. Setting the value to 1.0 makes the inner arc the same size as the
711 outer ellipse, resulting in just an arc. Values between 0.0 and 1.0 create
712 hollow elliptical rings.
713
714 The default value is \c 0.
715*/
716qreal QQuickEllipseShape::innerArcRatio() const
717{
718 Q_D(const QQuickEllipseShape);
719 return d->innerArcRatio;
720}
721
722void QQuickEllipseShape::setInnerArcRatio(qreal innerArcRatio)
723{
724 Q_D(QQuickEllipseShape);
725 if (qFuzzyCompare(d->innerArcRatio, innerArcRatio))
726 return;
727 d->innerArcRatio = innerArcRatio;
728 d->updatePath();
729 emit innerArcRatioChanged();
730}
731
732/*!
733 \qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::strokeWidth
734
735 This property holds the stroke width.
736
737 When set to a negative value, no stroking occurs.
738
739 The default value is \c 1.
740*/
741
742qreal QQuickEllipseShape::strokeWidth() const
743{
744 Q_D(const QQuickEllipseShape);
745 return d->path->strokeWidth();
746}
747
748void QQuickEllipseShape::setStrokeWidth(qreal width)
749{
750 Q_D(QQuickEllipseShape);
751 if (qFuzzyCompare(d->path->strokeWidth(), width))
752 return;
753 d->path->setStrokeWidth(width);
754 d->updatePath();
755 emit strokeWidthChanged();
756}
757
758/*!
759 \qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::fillColor
760
761 This property holds the fill color.
762
763 When set to \c transparent, no filling occurs.
764
765 The default value is \c "white".
766
767 \note If either \l fillGradient is set to something other than \c null, it
768 will be used instead of \c fillColor.
769*/
770
771QColor QQuickEllipseShape::fillColor() const
772{
773 Q_D(const QQuickEllipseShape);
774 return d->path->fillColor();
775}
776
777void QQuickEllipseShape::setFillColor(const QColor &color)
778{
779 Q_D(QQuickEllipseShape);
780 d->path->setFillColor(color);
781 d->updatePath();
782 emit fillColorChanged();
783}
784
785/*!
786 \qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::strokeColor
787
788 This property holds the stroking color.
789
790 When set to \c transparent, no stroking occurs.
791
792 The default value is \c "black".
793*/
794
795QColor QQuickEllipseShape::strokeColor() const
796{
797 Q_D(const QQuickEllipseShape);
798 return d->path->strokeColor();
799}
800
801void QQuickEllipseShape::setStrokeColor(const QColor &color)
802{
803 Q_D(QQuickEllipseShape);
804 d->path->setStrokeColor(color);
805 d->updatePath();
806 emit strokeColorChanged();
807}
808
809/*!
810 \include shapepath.qdocinc {capStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
811*/
812
813QQuickShapePath::CapStyle QQuickEllipseShape::capStyle() const
814{
815 Q_D(const QQuickEllipseShape);
816 return d->path->capStyle();
817}
818
819void QQuickEllipseShape::setCapStyle(QQuickShapePath::CapStyle style)
820{
821 Q_D(QQuickEllipseShape);
822 if (d->path->capStyle() == style)
823 return;
824 d->path->setCapStyle(style);
825 d->updatePath();
826 emit capStyleChanged();
827}
828
829/*!
830 \include shapepath.qdocinc {joinStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
831*/
832
833QQuickShapePath::JoinStyle QQuickEllipseShape::joinStyle() const
834{
835 Q_D(const QQuickEllipseShape);
836 return d->path->joinStyle();
837}
838
839void QQuickEllipseShape::setJoinStyle(QQuickShapePath::JoinStyle style)
840{
841 Q_D(QQuickEllipseShape);
842 if (d->path->joinStyle() == style)
843 return;
844 d->path->setJoinStyle(style);
845 d->updatePath();
846 emit joinStyleChanged();
847}
848
849/*!
850 \include shapepath.qdocinc {strokeStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
851*/
852
853QQuickShapePath::StrokeStyle QQuickEllipseShape::strokeStyle() const
854{
855 Q_D(const QQuickEllipseShape);
856 return d->path->strokeStyle();
857}
858
859void QQuickEllipseShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
860{
861 Q_D(QQuickEllipseShape);
862 if (d->path->strokeStyle() == style)
863 return;
864 d->path->setStrokeStyle(style);
865 d->updatePath();
866 emit strokeStyleChanged();
867}
868
869/*!
870 \include shapepath.qdocinc {fillRule-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
871*/
872
873QQuickShapePath::FillRule QQuickEllipseShape::fillRule() const
874{
875 Q_D(const QQuickEllipseShape);
876 return d->path->fillRule();
877}
878
879void QQuickEllipseShape::setFillRule(QQuickShapePath::FillRule fillRule)
880{
881 Q_D(QQuickEllipseShape);
882 if (d->path->fillRule() == fillRule)
883 return;
884 d->path->setFillRule(fillRule);
885 d->updatePath();
886 emit fillRuleChanged();
887}
888
889/*!
890 \include shapepath.qdocinc {dashPattern-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
891*/
892
893QList<qreal> QQuickEllipseShape::dashPattern() const
894{
895 Q_D(const QQuickEllipseShape);
896 return d->path->dashPattern();
897}
898
899void QQuickEllipseShape::setDashPattern(const QList<qreal> &array)
900{
901 Q_D(QQuickEllipseShape);
902 d->path->setDashPattern(array);
903 d->updatePath();
904 emit dashPatternChanged();
905}
906
907/*!
908 \qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::EllipseShape::fillGradient
909
910 The fillGradient of the ellipse fill color.
911
912 By default, no fillGradient is enabled and the value is null. In this case, the
913 fill uses a solid color based on the value of \l fillColor.
914
915 When set, \l fillColor is ignored and filling is done using one of the
916 \l ShapeGradient subtypes.
917
918 \note The \l Gradient type cannot be used here. Rather, prefer using one of
919 the advanced subtypes, like \l LinearGradient.
920*/
921QQuickShapeGradient *QQuickEllipseShape::fillGradient() const
922{
923 Q_D(const QQuickEllipseShape);
924 return d->path->fillGradient();
925}
926
927void QQuickEllipseShape::setFillGradient(QQuickShapeGradient *fillGradient)
928{
929 Q_D(QQuickEllipseShape);
930 d->path->setFillGradient(fillGradient);
931 d->updatePath();
932 emit gradientChanged();
933}
934
935void QQuickEllipseShape::resetFillGradient()
936{
937 setFillGradient(nullptr);
938}
939
940/*!
941 \qmlproperty enumeration QtQuick.Shapes.DesignHelpers::EllipseShape::borderMode
942
943 The \l borderMode property determines where the border is drawn along the
944 edge of the ellipse.
945
946 \value EllipseShape.Inside
947 The border is drawn along the inside edge of the item and does not
948 affect the item width.
949
950 This is the default value.
951 \value EllipseShape.Middle
952 The border is drawn over the edge of the item and does not
953 affect the item width.
954 \value EllipseShape.Outside
955 The border is drawn along the outside edge of the item and increases
956 the item width by the value of \l strokeWidth.
957
958 \sa strokeWidth
959*/
960QQuickEllipseShape::BorderMode QQuickEllipseShape::borderMode() const
961{
962 Q_D(const QQuickEllipseShape);
963 return d->borderMode;
964}
965
966void QQuickEllipseShape::setBorderMode(BorderMode borderMode)
967{
968 Q_D(QQuickEllipseShape);
969 if (borderMode == d->borderMode)
970 return;
971 d->borderMode = borderMode;
972 d->updatePath();
973 emit borderModeChanged();
974}
975
976void QQuickEllipseShape::resetBorderMode()
977{
978 setBorderMode(BorderMode::Inside);
979}
980
981void QQuickEllipseShape::itemChange(ItemChange change, const ItemChangeData &value)
982{
983 Q_D(QQuickEllipseShape);
984
985 if (d->path)
986 d->updatePath();
987
988 QQuickItem::itemChange(change, value);
989}
990
991QT_END_NAMESPACE
992
993#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)