58bool lines_intersect(QVector2D a, QVector2D b, QVector2D c, QVector2D d, qreal *s, qreal *t)
61 if ((a.x() == b.x() && a.y() == b.y()) || (c.x() == d.x() && c.y() == d.y()))
64 const qreal denom = cross(a, b, c, d);
71 *s = cross(c, d, c, a) / denom;
73 *t = cross(a, b, c, a) / denom;
91void QQuickEllipseShapePrivate::addArc(QVector2D point, QVector2D arcRadius,
92 QQuickPathArc::ArcDirection dir,
bool largeArc)
94 auto arc =
new QQuickPathArc(path);
97 arc->setRadiusX(arcRadius.x());
98 arc->setRadiusY(arcRadius.y());
99 arc->setDirection(dir);
100 arc->setUseLargeArc(largeArc);
101 QQuickPathPrivate::get(path)->appendPathElement(arc);
113void QQuickEllipseShapePrivate::roundCenter(QVector2D center, QVector2D ellipseRadius)
115 const qreal endAngle = arc_angle(startAngle + sweepAngle);
116 const QVector2D endPoint = arc_point(center, ellipseRadius, endAngle);
117 const qreal beginAngle = arc_angle(startAngle);
118 const QVector2D beginPoint = arc_point(center, ellipseRadius, beginAngle);
120 const QVector2D AB = endPoint - center;
121 const QVector2D AC = beginPoint - center;
123 const qreal a = angle_between_vectors(AB, AC);
124 const qreal halfAngle = std::fabs(a) * 0.5f;
126 const qreal maxCornerRadius = (std::min(AB.length(), AC.length()) * 0.5f) * qTan(halfAngle);
127 const qreal corner_radius = std::min(cornerRadius, maxCornerRadius);
130 const qreal edgeOffset = corner_radius / qTan(halfAngle);
131 const QVector2D B = center + (AB.normalized() * edgeOffset);
132 const QVector2D C = center + (AC.normalized() * edgeOffset);
135 auto &rc = roundedCorners[RoundedCornerIndex::Center];
140 rc.radius = corner_radius;
143void QQuickEllipseShapePrivate::roundBeginEnd(QVector2D center, QVector2D ellipseRadius)
146 const qreal endAngle = startAngle + sweepAngle;
147 bool e_outer =
false, b_outer =
false, e_inner =
false, b_inner =
false;
148 while (deg < 45.0f) {
150 if (e_outer && b_outer && e_inner && b_inner)
153 b_outer = roundOuter(center, ellipseRadius, deg, startAngle, endAngle,
154 RoundedCornerIndex::OuterBegin);
156 e_outer = roundOuter(center, ellipseRadius, deg, endAngle, startAngle,
157 RoundedCornerIndex::OuterEnd);
159 e_inner = roundInner(center, ellipseRadius, deg, endAngle, startAngle,
160 RoundedCornerIndex::InnerEnd);
162 b_inner = roundInner(center, ellipseRadius, deg, startAngle, endAngle,
163 RoundedCornerIndex::InnerBegin);
167bool QQuickEllipseShapePrivate::roundOuter(QVector2D center, QVector2D ellipseRadius, qreal deg,
168 qreal arcAngle1, qreal arcAngle2,
169 RoundedCornerIndex index)
173 const qreal arcAngle = arc_angle(arcAngle1);
174 const QVector2D arcPoint = arc_point(center, ellipseRadius, arcAngle);
176 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
178 const QVector2D B = arc_point(center, ellipseRadius, angle);
181 const QVector2D uV = tangent_ccw(ellipseRadius, angle).normalized();
182 const QVector2D b1 = B + uV;
185 bool res = lines_intersect(center, arcPoint, B, b1, &s, &t);
187 const QVector2D A = center + s * (arcPoint - center);
189 const QVector2D AB = B - A;
190 const QVector2D AC = center - A;
192 const qreal a = angle_between_vectors(AB, AC);
193 const qreal halfAngle = std::fabs(a) * 0.5;
194 const qreal edgeOffset = AB.length();
196 const qreal corner_radius = edgeOffset * qTan(halfAngle);
199 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
200 const qreal degMax = std::min(sweep, 45.0);
202 const QVector2D C = A + AC.normalized() * edgeOffset;
204 const qreal ptoc = (arcPoint - C).length();
206 if (innerArcRatio > 0) {
207 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
208 innerArcRatio * ellipseRadius.y());
209 const QVector2D innerArcPoint =
210 arc_point(center, ellipseInnerRadius, arcAngle);
211 edge = (arcPoint - innerArcPoint).length();
213 edge = (arcPoint - center).length();
216 const qreal diff = std::fabs(corner_radius - cornerRadius);
218 auto &rc = roundedCorners[index];
220 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius) && !(deg > degMax)
221 && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
224 if (rc.radius == 0 || canUpdate)
225 rc.update(diff, A, B, C, a, corner_radius);
229 corner_radius > cornerRadius
230 || is_equal(corner_radius, cornerRadius, 0.01f)
233 || is_equal(deg, degMax, 0.01f)
235 || (rc.radius != 0 && rc.radius > corner_radius)
238 || (ptoc > edge * 0.5f);
244bool QQuickEllipseShapePrivate::roundInner(QVector2D center, QVector2D ellipseRadius, qreal deg,
245 qreal arcAngle1, qreal arcAngle2,
246 RoundedCornerIndex index)
249 const qreal smoothFactor = 1.5;
255 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
256 innerArcRatio * ellipseRadius.y());
258 const qreal arcAngle = arc_angle(arcAngle1);
259 const QVector2D innerArcPoint =
260 arc_point(center, ellipseInnerRadius, arcAngle);
262 const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
264 const QVector2D B = arc_point(center, ellipseInnerRadius, angle);
267 const QVector2D uV = tangent_ccw(ellipseInnerRadius, angle).normalized();
268 const QVector2D b1 = B + uV;
271 bool res = lines_intersect(center, innerArcPoint, B, b1, &s, &t);
275 const QVector2D A = center + s * (innerArcPoint - center);
277 const auto arcPoint = arc_point(center, ellipseRadius, arcAngle);
279 const QVector2D AB = B - A;
280 const QVector2D AC = A - innerArcPoint;
282 const qreal a = angle_between_vectors(AB, AC);
283 const qreal halfAngle = std::fabs(a) * 0.5;
284 const qreal edgeOffset = AB.length();
286 const qreal corner_radius = edgeOffset * qTan(halfAngle);
289 const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
290 const qreal degMax = std::min(sweep, 45.0);
292 const QVector2D C = A + AC.normalized() * edgeOffset;
294 const qreal ptoc = (innerArcPoint - C).length();
295 const qreal edge = (innerArcPoint - arcPoint).length();
298 std::fabs(corner_radius - cornerRadius * smoothFactor);
300 auto &rc = roundedCorners[index];
302 bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius * smoothFactor)
303 && !(deg > degMax) && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
306 if (rc.radius == 0 || canUpdate)
307 rc.update(diff, A, B, C, a, corner_radius);
311 corner_radius > cornerRadius * smoothFactor
312 || is_equal(corner_radius, cornerRadius * smoothFactor, 0.01f)
315 || is_equal(deg, degMax, 0.01f)
317 || (rc.radius != 0 && rc.radius > corner_radius)
320 || (ptoc > edge * 0.5f);
326void QQuickEllipseShapePrivate::drawCenterCorner()
328 auto &rc = roundedCorners[RoundedCornerIndex::Center];
329 path->setStartX(rc.B.x());
330 path->setStartY(rc.B.y());
332 addArc(rc.C, QVector2D(rc.radius, rc.radius),
333 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
346void QQuickEllipseShapePrivate::drawInnerBeginCorner()
348 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
349 path->setStartX(rc.B.x());
350 path->setStartY(rc.B.y());
352 addArc(rc.C, QVector2D(rc.radius, rc.radius),
353 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
366void QQuickEllipseShapePrivate::drawOuterArcRounded(QVector2D center, QVector2D ellipseRadius)
369 const qreal endAngle = startAngle + sweepAngle;
371 const qreal angle = startAngle > endAngle
372 ? arc_angle(startAngle - std::fabs(sweepAngle * 0.5f))
373 : arc_angle(startAngle + std::fabs(sweepAngle * 0.5f));
374 const auto point = arc_point(center, ellipseRadius, angle);
377 addArc(point, ellipseRadius,
378 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
380 auto &rc = roundedCorners[RoundedCornerIndex::OuterEnd];
383 addArc(rc.B, ellipseRadius,
384 sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
387 addArc(rc.C, QVector2D(rc.radius, rc.radius),
388 rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
391void QQuickEllipseShapePrivate::drawInnerArcRounded(QVector2D center, QVector2D ellipseRadius)
394 const qreal endAngle = startAngle + sweepAngle;
396 const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
397 innerArcRatio * ellipseRadius.y());
399 const qreal angle = endAngle > startAngle ? arc_angle(endAngle - std::fabs(sweepAngle * 0.5f))
400 : arc_angle(endAngle + std::fabs(sweepAngle * 0.5f));
401 const auto point = arc_point(center, ellipseInnerRadius, angle);
404 addArc(point, ellipseInnerRadius,
405 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
408 auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
409 addArc(rc.B, ellipseInnerRadius,
410 sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
413void QQuickEllipseShapePrivate::drawOuterArc(QVector2D center, QVector2D ellipseRadius)
415 const qreal beginAngle = arc_angle(startAngle);
416 const qreal endAngle = arc_angle(startAngle + sweepAngle);
418 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
419 bool isFull = (alpha <= 0.0f || alpha >= 359.0f);
423 const auto beginPoint = arc_point(center, ellipseRadius, isFull ? 0 : beginAngle);
424 const auto endPoint = arc_point(center, ellipseRadius, isFull ? 359.9 : endAngle);
426 path->setStartX(beginPoint.x());
427 path->setStartY(beginPoint.y());
429 addArc(endPoint, ellipseRadius,
430 isFull ? QQuickPathArc::Clockwise
431 : (sweepAngle > 0.0f ? QQuickPathArc::Clockwise
432 : QQuickPathArc::Counterclockwise),
433 isFull ?
true : alpha > 180.0f);
436 if (qFuzzyCompare(innerArcRatio, 1)) {
437 addArc(beginPoint, ellipseRadius,
438 isFull ? QQuickPathArc::Counterclockwise
439 : (sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise
440 : QQuickPathArc::Clockwise),
441 isFull ?
true : alpha > 180.0f);
445void QQuickEllipseShapePrivate::drawFullInnerArc(QVector2D center, QVector2D ellipseRadius)
447 const qreal beginAngle = arc_angle(startAngle);
449 auto arc =
new QQuickPathAngleArc(path);
450 arc->setCenterX(center.x());
451 arc->setCenterY(center.y());
452 arc->setStartAngle(beginAngle);
453 arc->setRadiusX(innerArcRatio * ellipseRadius.x());
454 arc->setRadiusY(innerArcRatio * ellipseRadius.y());
455 arc->setSweepAngle(sweepAngle);
456 QQuickPathPrivate::get(path)->appendPathElement(arc);
489void QQuickEllipseShapePrivate::updatePath()
491 const qreal borderOffset = getBorderOffset();
492 const QVector2D center =
493 QVector2D(width.valueBypassingBindings(), height.valueBypassingBindings()) * 0.5f;
494 const QVector2D ellipseRadius = center - QVector2D(borderOffset, borderOffset);
496 QQuickPathPrivate::get(path)->clearPathElements(QQuickPathPrivate::DeleteElementPolicy::Delete);
498 const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
499 const bool isFull = alpha >= 359.0;
501 if (qFuzzyCompare(alpha, 0))
505 if (qFuzzyCompare(innerArcRatio, 1)) {
506 drawOuterArc(center, ellipseRadius);
510 roundedCorners.reset();
512 if (innerArcRatio != 0 && isFull) {
514 drawOuterArc(center, ellipseRadius);
515 drawFullInnerArc(center, ellipseRadius);
516 }
else if (innerArcRatio != 0 && !isFull) {
518 roundBeginEnd(center, ellipseRadius);
519 drawWithInnerRadius(center, ellipseRadius);
520 }
else if (!isFull) {
522 roundCenter(center, ellipseRadius);
523 roundBeginEnd(center, ellipseRadius);
524 drawWithoutInnerRadius(center, ellipseRadius);
526 drawOuterArc(center, ellipseRadius);
583QQuickEllipseShape::QQuickEllipseShape(QQuickItem *parent)
584 : QQuickShape(*(
new QQuickEllipseShapePrivate), parent)
586 Q_D(QQuickEllipseShape);
588 setPreferredRendererType(CurveRenderer);
593 d->path =
new QQuickShapePath(
this);
594 d->path->setParent(
this);
595 d->path->setStrokeWidth(1);
596 d->path->setStrokeColor(QColorConstants::Black);
597 d->path->setFillColor(QColorConstants::White);
599 d->sp.append(d->path);
600 d->path->setParent(
this);
601 d->extra.value().resourcesList.append(d->path);
603 connect(d->path, &QQuickShapePath::strokeColorChanged,
this, &QQuickEllipseShape::strokeColorChanged);
604 connect(d->path, &QQuickShapePath::strokeWidthChanged,
this, &QQuickEllipseShape::strokeWidthChanged);
605 connect(d->path, &QQuickShapePath::fillColorChanged,
this, &QQuickEllipseShape::fillColorChanged);
606 connect(d->path, &QQuickShapePath::joinStyleChanged,
this, &QQuickEllipseShape::joinStyleChanged);
607 connect(d->path, &QQuickShapePath::capStyleChanged,
this, &QQuickEllipseShape::capStyleChanged);
608 connect(d->path, &QQuickShapePath::strokeStyleChanged,
this, &QQuickEllipseShape::strokeStyleChanged);
609 connect(d->path, &QQuickShapePath::dashOffsetChanged,
this, &QQuickEllipseShape::dashOffsetChanged);
610 connect(d->path, &QQuickShapePath::dashPatternChanged,
this, &QQuickEllipseShape::dashPatternChanged);
611 connect(d->path, &QQuickShapePath::fillItemChanged,
this, &QQuickEllipseShape::fillItemChanged);
612 connect(d->path, &QQuickShapePath::fillGradientChanged,
this, &QQuickEllipseShape::fillGradientChanged);
613 connect(d->path, &QQuickShapePath::fillRuleChanged,
this, &QQuickEllipseShape::fillRuleChanged);