57bool lines_intersect(QVector2D a, QVector2D b, QVector2D c, QVector2D d, qreal *s, qreal *t)
60 if ((a.x() == b.x() && a.y() == b.y()) || (c.x() == d.x() && c.y() == d.y()))
63 const qreal denom = cross(a, b, c, d);
70 *s = cross(c, d, c, a) / denom;
72 *t = cross(a, b, c, a) / denom;
90void QQuickEllipseShapePrivate::addArc(QVector2D point, QVector2D arcRadius,
91 QQuickPathArc::ArcDirection dir,
bool largeArc)
93 auto arc =
new QQuickPathArc(path);
96 arc->setRadiusX(arcRadius.x());
97 arc->setRadiusY(arcRadius.y());
98 arc->setDirection(dir);
99 arc->setUseLargeArc(largeArc);
100 QQuickPathPrivate::get(path)->appendPathElement(arc);
112void QQuickEllipseShapePrivate::roundCenter(QVector2D center, QVector2D ellipseRadius)
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);
119 const QVector2D AB = endPoint - center;
120 const QVector2D AC = beginPoint - center;
122 const qreal a = angle_between_vectors(AB, AC);
123 const qreal halfAngle = std::fabs(a) * 0.5f;
125 const qreal maxCornerRadius = (std::min(AB.length(), AC.length()) * 0.5f) * qTan(halfAngle);
126 const qreal corner_radius = std::min(cornerRadius, maxCornerRadius);
129 const qreal edgeOffset = corner_radius / qTan(halfAngle);
130 const QVector2D B = center + (AB.normalized() * edgeOffset);
131 const QVector2D C = center + (AC.normalized() * edgeOffset);
134 auto &rc = roundedCorners[RoundedCornerIndex::Center];
139 rc.radius = corner_radius;
142void QQuickEllipseShapePrivate::roundBeginEnd(QVector2D center, QVector2D ellipseRadius)
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) {
149 if (e_outer && b_outer && e_inner && b_inner)
152 b_outer = roundOuter(center, ellipseRadius, deg, startAngle, endAngle,
153 RoundedCornerIndex::OuterBegin);
155 e_outer = roundOuter(center, ellipseRadius, deg, endAngle, startAngle,
156 RoundedCornerIndex::OuterEnd);
158 e_inner = roundInner(center, ellipseRadius, deg, endAngle, startAngle,
159 RoundedCornerIndex::InnerEnd);
161 b_inner = roundInner(center, ellipseRadius, deg, startAngle, endAngle,
162 RoundedCornerIndex::InnerBegin);
166bool QQuickEllipseShapePrivate::roundOuter(QVector2D center, QVector2D ellipseRadius, qreal deg,
167 qreal arcAngle1, qreal arcAngle2,
168 RoundedCornerIndex index)
172 const qreal arcAngle = arc_angle(arcAngle1);
173 const QVector2D arcPoint = arc_point(center, ellipseRadius, arcAngle);
175 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
177 const QVector2D B = arc_point(center, ellipseRadius, angle);
180 const QVector2D uV = tangent_ccw(ellipseRadius, angle).normalized();
181 const QVector2D b1 = B + uV;
184 bool res = lines_intersect(center, arcPoint, B, b1, &s, &t);
186 const QVector2D A = center + s * (arcPoint - center);
188 const QVector2D AB = B - A;
189 const QVector2D AC = center - A;
191 const qreal a = angle_between_vectors(AB, AC);
192 const qreal halfAngle = std::fabs(a) * 0.5;
193 const qreal edgeOffset = AB.length();
195 const qreal corner_radius = edgeOffset * qTan(halfAngle);
198 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
199 const qreal degMax = std::min(sweep, 45.0);
201 const QVector2D C = A + AC.normalized() * edgeOffset;
203 const qreal ptoc = (arcPoint - C).length();
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);
210 edge = (arcPoint - innerArcPoint).length();
212 edge = (arcPoint - center).length();
215 const qreal diff = std::fabs(corner_radius - cornerRadius);
217 auto &rc = roundedCorners[index];
219 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius) && !(deg > degMax)
220 && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
223 if (rc.radius == 0 || canUpdate)
224 rc.update(diff, A, B, C, a, corner_radius);
228 corner_radius > cornerRadius
229 || is_equal(corner_radius, cornerRadius, 0.01f)
232 || is_equal(deg, degMax, 0.01f)
234 || (rc.radius != 0 && rc.radius > corner_radius)
237 || (ptoc > edge * 0.5f);
243bool QQuickEllipseShapePrivate::roundInner(QVector2D center, QVector2D ellipseRadius, qreal deg,
244 qreal arcAngle1, qreal arcAngle2,
245 RoundedCornerIndex index)
248 const qreal smoothFactor = 1.5;
254 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
255 innerArcRatio * ellipseRadius.y());
257 const qreal arcAngle = arc_angle(arcAngle1);
258 const QVector2D innerArcPoint =
259 arc_point(center, ellipseInnerRadius, arcAngle);
261 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
263 const QVector2D B = arc_point(center, ellipseInnerRadius, angle);
266 const QVector2D uV = tangent_ccw(ellipseInnerRadius, angle).normalized();
267 const QVector2D b1 = B + uV;
270 bool res = lines_intersect(center, innerArcPoint, B, b1, &s, &t);
274 const QVector2D A = center + s * (innerArcPoint - center);
276 const auto arcPoint = arc_point(center, ellipseRadius, arcAngle);
278 const QVector2D AB = B - A;
279 const QVector2D AC = A - innerArcPoint;
281 const qreal a = angle_between_vectors(AB, AC);
282 const qreal halfAngle = std::fabs(a) * 0.5;
283 const qreal edgeOffset = AB.length();
285 const qreal corner_radius = edgeOffset * qTan(halfAngle);
288 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
289 const qreal degMax = std::min(sweep, 45.0);
291 const QVector2D C = A + AC.normalized() * edgeOffset;
293 const qreal ptoc = (innerArcPoint - C).length();
294 const qreal edge = (innerArcPoint - arcPoint).length();
297 std::fabs(corner_radius - cornerRadius * smoothFactor);
299 auto &rc = roundedCorners[index];
301 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius * smoothFactor)
302 && !(deg > degMax) && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
305 if (rc.radius == 0 || canUpdate)
306 rc.update(diff, A, B, C, a, corner_radius);
310 corner_radius > cornerRadius * smoothFactor
311 || is_equal(corner_radius, cornerRadius * smoothFactor, 0.01f)
314 || is_equal(deg, degMax, 0.01f)
316 || (rc.radius != 0 && rc.radius > corner_radius)
319 || (ptoc > edge * 0.5f);
325void QQuickEllipseShapePrivate::drawCenterCorner()
327 auto &rc = roundedCorners[RoundedCornerIndex::Center];
328 path->setStartX(rc.B.x());
329 path->setStartY(rc.B.y());
331 addArc(rc.C, QVector2D(rc.radius, rc.radius),
332 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
345void QQuickEllipseShapePrivate::drawInnerBeginCorner()
347 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
348 path->setStartX(rc.B.x());
349 path->setStartY(rc.B.y());
351 addArc(rc.C, QVector2D(rc.radius, rc.radius),
352 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
365void QQuickEllipseShapePrivate::drawOuterArcRounded(QVector2D center, QVector2D ellipseRadius)
368 const qreal endAngle = startAngle + sweepAngle;
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);
376 addArc(point, ellipseRadius,
377 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
379 auto &rc = roundedCorners[RoundedCornerIndex::OuterEnd];
382 addArc(rc.B, ellipseRadius,
383 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
386 addArc(rc.C, QVector2D(rc.radius, rc.radius),
387 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
390void QQuickEllipseShapePrivate::drawInnerArcRounded(QVector2D center, QVector2D ellipseRadius)
393 const qreal endAngle = startAngle + sweepAngle;
395 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
396 innerArcRatio * ellipseRadius.y());
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);
403 addArc(point, ellipseInnerRadius,
404 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
407 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
408 addArc(rc.B, ellipseInnerRadius,
409 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
412void QQuickEllipseShapePrivate::drawOuterArc(QVector2D center, QVector2D ellipseRadius)
414 const qreal beginAngle = arc_angle(startAngle);
415 const qreal endAngle = arc_angle(startAngle + sweepAngle);
417 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
418 bool isFull = (alpha <= 0.0f || alpha >= 359.0f);
422 const auto beginPoint = arc_point(center, ellipseRadius, isFull ? 0 : beginAngle);
423 const auto endPoint = arc_point(center, ellipseRadius, isFull ? 359.9 : endAngle);
425 path->setStartX(beginPoint.x());
426 path->setStartY(beginPoint.y());
428 addArc(endPoint, ellipseRadius,
429 isFull ? QQuickPathArc::Clockwise
430 : (sweepAngle > 0.0f ? QQuickPathArc::Clockwise
431 : QQuickPathArc::Counterclockwise),
432 isFull ?
true : alpha > 180.0f);
435void QQuickEllipseShapePrivate::drawFullInnerArc(QVector2D center, QVector2D ellipseRadius)
437 const qreal beginAngle = arc_angle(startAngle);
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);
479void QQuickEllipseShapePrivate::updatePath()
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);
486 QQuickPathPrivate::get(path)->clearPathElements(QQuickPathPrivate::DeleteElementPolicy::Delete);
488 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
489 const bool isFull = alpha >= 359.0;
491 if (qFuzzyCompare(alpha, 0))
495 if (qFuzzyCompare(innerArcRatio, 1) || (hideLine && qFuzzyCompare(innerArcRatio, 0))) {
496 drawOuterArc(center, ellipseRadius);
500 roundedCorners.reset();
502 if (innerArcRatio != 0 && isFull) {
504 drawOuterArc(center, ellipseRadius);
505 drawFullInnerArc(center, ellipseRadius);
506 }
else if (innerArcRatio != 0 && !isFull) {
508 roundBeginEnd(center, ellipseRadius);
509 drawWithInnerRadius(center, ellipseRadius);
510 }
else if (!isFull) {
512 roundCenter(center, ellipseRadius);
513 roundBeginEnd(center, ellipseRadius);
514 drawWithoutInnerRadius(center, ellipseRadius);
516 drawOuterArc(center, ellipseRadius);
573QQuickEllipseShape::QQuickEllipseShape(QQuickItem *parent)
574 : QQuickShape(*(
new QQuickEllipseShapePrivate), parent)
576 Q_D(QQuickEllipseShape);
578 setPreferredRendererType(CurveRenderer);
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);
589 d->sp.append(d->path);
590 d->path->setParent(
this);
591 d->extra.value().resourcesList.append(d->path);