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
qquickstarshape.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// Qt-Security score:significant reason:default
4
7#include <algorithm>
8#include <QtMath>
9#include <cstddef>
10
11QT_BEGIN_NAMESPACE
12
13namespace {
14
15inline qreal arc_angle(qreal angle)
16{
17 return angle - 90;
18}
19
20inline QVector2D arc_point(QVector2D center, QVector2D radius, qreal angle)
21{
22 return QVector2D(center.x() + radius.x() * qCos(qDegreesToRadians(angle)),
23 center.y() + radius.y() * qSin(qDegreesToRadians(angle)));
24}
25
26inline qreal cross(QVector2D a, QVector2D b)
27{
28 return a.x() * b.y() - a.y() * b.x();
29}
30
31qreal angle_between_vectors(QVector2D a, QVector2D b)
32{
33 const QVector2D uA = a.normalized();
34 const QVector2D uB = b.normalized();
35 const qreal angle = qAtan2(cross(uA, uB), QVector2D::dotProduct(uA, uB));
36 if (std::fabs(angle) < FLT_EPSILON)
37 return 0.0f;
38 return angle;
39}
40
41} // namespace
42
43QQuickStarShapePrivate::QQuickStarShapePrivate() = default;
44
45QQuickStarShapePrivate::~QQuickStarShapePrivate() = default;
46
47void QQuickStarShapePrivate::updatePoints()
48{
49 points.clear();
50
51 const qreal rectWidth = width.valueBypassingBindings();
52 const qreal rectHeight = height.valueBypassingBindings();
53
54 const QVector2D center(rectWidth * 0.5, rectHeight * 0.5);
55 const QVector2D radius(rectWidth * 0.5, rectHeight * 0.5);
56 const QVector2D inner_radius = radius * std::min(std::max(ratio, 0.001), 1.0);
57
58 const int numPoints = pointCount * 2;
59 const qreal sliceAngle = (360.0f / numPoints);
60 for (int i = 0; i < numPoints; ++i) {
61 const qreal angle = i * sliceAngle;
62 const auto p = arc_point(center, i % 2 == 0 ? radius : inner_radius, arc_angle(angle));
63 points.emplace_back(std::move(p));
64 }
65}
66
67void QQuickStarShapePrivate::constructPolygonPath()
68{
69 auto *ppath = QQuickShapePathPrivate::get(path);
70
71 path->setStartX(points[0].x());
72 path->setStartY(points[0].y());
73
74 for (const auto &p : points) {
75 auto line = new QQuickPathLine(path);
76 line->setX(p.x());
77 line->setY(p.y());
78 ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
79 }
80
81 auto line = new QQuickPathLine(path);
82 line->setX(points[0].x());
83 line->setY(points[0].y());
84 ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
85
86 path->processPath();
87}
88
89void QQuickStarShapePrivate::constructRoundedPolygonPath()
90{
91 const auto size = points.size();
92
93 auto *ppath = QQuickShapePathPrivate::get(path);
94
95 for (size_t i = 0; i < size; ++i) {
96 const auto &a = points[i];
97 const auto &b = points[(i == 0 ? size : i) - 1];
98 const auto &c = points[(i + 1 == size) ? 0 : (i + 1)];
99
100 const QVector2D ab = b - a;
101 const QVector2D ac = c - a;
102
103 const qreal alpha = angle_between_vectors(ab, ac);
104 const qreal halfAngle = std::fabs(alpha) * 0.5;
105
106 qreal corner_radius = cornerRadius;
107
108 auto edgeOffset = corner_radius / qTan(halfAngle);
109 const qreal edge = std::min(ab.length(), ac.length());
110 if (edgeOffset > edge * 0.5) {
111 edgeOffset = edge * 0.5;
112 corner_radius = edgeOffset * qTan(halfAngle);
113 }
114
115 const auto B = a + ab.normalized() * edgeOffset;
116 const auto C = a + ac.normalized() * edgeOffset;
117
118 if (i == 0) {
119 path->setStartX(B.x());
120 path->setStartY(B.y());
121 } else {
122 auto line = new QQuickPathLine(path);
123 line->setX(B.x());
124 line->setY(B.y());
125 ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
126 }
127
128 auto arc = new QQuickPathArc(path);
129 arc->setX(C.x());
130 arc->setY(C.y());
131 arc->setRadiusX(corner_radius);
132 arc->setRadiusY(corner_radius);
133 arc->setDirection(alpha > 0 ? QQuickPathArc::ArcDirection::Counterclockwise
134 : QQuickPathArc::ArcDirection::Clockwise);
135 ppath->appendPathElement(arc, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
136 }
137
138 auto line = new QQuickPathLine(path);
139 line->setX(path->startX());
140 line->setY(path->startY());
141 ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
142
143 path->processPath();
144}
145
146void QQuickStarShapePrivate::updatePath()
147{
148 QQuickShapePathPrivate::get(path)->clearPathElements(
149 QQuickPathPrivate::DeleteElementPolicy::Delete);
150
151 updatePoints();
152
153 if (qFuzzyCompare(cornerRadius, 0.0))
154 constructPolygonPath();
155 else
156 constructRoundedPolygonPath();
157}
158
159/*!
160 \qmltype StarShape
161 \inqmlmodule QtQuick.Shapes.DesignHelpers
162 \brief A filled star-shaped polygon with an optional border.
163 \since QtQuick 6.10
164
165 A star can be a star shaped stroke, a filling, or a stroke with filling.
166 The \l strokeColor, \l strokeWidth, and \l strokeStyle properties specify
167 the appearance of the outline. The \l dashPattern and \l dashOffset
168 properties specify the appearance of dashed stroke.
169
170 Set the \l pointCount property between 3 and 60 to specify the number of
171 points of the star. Set the \l ratio between 0.1 and 1 to specify the
172 distance of the inner points of the star from the center.
173
174 The area inside the stroke is painted using either a solid fill color, specified using the
175 \l fillColor property, or a gradient, defined using one of the \l ShapeGradient subtypes and set
176 using the \l gradient property. If both a color and a gradient are specified, the gradient is
177 used.
178
179 To create a star with a stroke, set the \l strokeWidth property to a value greater than 0. The
180 \l strokeWidth property specifies the width of the polygon stroke. The default \l pointCount
181 value is 6 and the default \l strokeWidth value is 4. Setting the \l strokeWidth value to a
182 negative value hides the border.
183
184 The \l cornerRadius property specifies whether the star corners are rounded.
185*/
186QQuickStarShape::QQuickStarShape(QQuickItem *parent)
187 : QQuickShape(*(new QQuickStarShapePrivate), parent)
188{
189 Q_D(QQuickStarShape);
190
191 setPreferredRendererType(CurveRenderer);
192
193 setWidth(200);
194 setHeight(200);
195
196 d->path = new QQuickShapePath(this);
197 d->path->setStrokeWidth(1);
198 d->path->setStrokeColor(QColorConstants::Black);
199 d->path->setFillColor(QColorConstants::White);
200
201 d->sp.append(d->path);
202 d->path->setParent(this);
203 d->extra.value().resourcesList.append(d->path);
204
205 connect(d->path, &QQuickShapePath::strokeColorChanged, this, &QQuickStarShape::strokeColorChanged);
206 connect(d->path, &QQuickShapePath::strokeWidthChanged, this, &QQuickStarShape::strokeWidthChanged);
207 connect(d->path, &QQuickShapePath::fillColorChanged, this, &QQuickStarShape::fillColorChanged);
208 connect(d->path, &QQuickShapePath::joinStyleChanged, this, &QQuickStarShape::joinStyleChanged);
209 connect(d->path, &QQuickShapePath::capStyleChanged, this, &QQuickStarShape::capStyleChanged);
210 connect(d->path, &QQuickShapePath::strokeStyleChanged, this, &QQuickStarShape::strokeStyleChanged);
211 connect(d->path, &QQuickShapePath::dashOffsetChanged, this, &QQuickStarShape::dashOffsetChanged);
212 connect(d->path, &QQuickShapePath::dashPatternChanged, this, &QQuickStarShape::dashPatternChanged);
213 connect(d->path, &QQuickShapePath::fillItemChanged, this, &QQuickStarShape::fillItemChanged);
214 connect(d->path, &QQuickShapePath::fillGradientChanged, this, &QQuickStarShape::fillGradientChanged);
215}
216
217QQuickStarShape::~QQuickStarShape() = default;
218
219/*!
220 \include shapepath.qdocinc {dashOffset-property}
221 {QtQuick.Shapes.DesignHelpers::StarShape}
222*/
223
224qreal QQuickStarShape::dashOffset() const
225{
226 Q_D(const QQuickStarShape);
227 return d->path->dashOffset();
228}
229
230void QQuickStarShape::setDashOffset(qreal offset)
231{
232 Q_D(QQuickStarShape);
233 d->path->setDashOffset(offset);
234}
235
236/*!
237 \qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::cornerRadius
238
239 The property controls the rounding of both the star's outer points and
240 inner points.
241
242 The default value is \c 10.
243*/
244
245qreal QQuickStarShape::cornerRadius() const
246{
247 Q_D(const QQuickStarShape);
248 return d->cornerRadius;
249}
250
251void QQuickStarShape::setCornerRadius(qreal radius)
252{
253 Q_D(QQuickStarShape);
254 if (qFuzzyCompare(d->cornerRadius, radius))
255 return;
256 d->cornerRadius = radius;
257 d->updatePath();
258 emit cornerRadiusChanged();
259}
260
261/*!
262 \qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::ratio
263
264 The property defines the distance of the inner points of the star from the
265 center.
266
267 The default value is \c 0.5.
268*/
269
270qreal QQuickStarShape::ratio() const
271{
272 Q_D(const QQuickStarShape);
273 return d->ratio;
274}
275
276void QQuickStarShape::setRatio(qreal ratio)
277{
278 Q_D(QQuickStarShape);
279 if (qFuzzyCompare(d->ratio, ratio))
280 return;
281 d->ratio = ratio;
282 d->updatePath();
283 emit ratioChanged();
284}
285
286/*!
287 \qmlproperty int QtQuick.Shapes.DesignHelpers::StarShape::pointCount
288
289 The property defines the total number of points the star has.
290
291 The default value is \c 6.
292*/
293
294int QQuickStarShape::pointCount() const
295{
296 Q_D(const QQuickStarShape);
297 return d->pointCount;
298}
299
300void QQuickStarShape::setPointCount(int count)
301{
302 Q_D(QQuickStarShape);
303 if (d->pointCount == count)
304 return;
305 d->pointCount = count;
306 d->updatePath();
307 emit pointCountChanged();
308}
309
310/*!
311 \qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::strokeWidth
312
313 This property holds the stroke width.
314
315 When set to a negative value, no stroking occurs.
316
317 The default value is \c 1.
318*/
319
320qreal QQuickStarShape::strokeWidth() const
321{
322 Q_D(const QQuickStarShape);
323 return d->path->strokeWidth();
324}
325
326void QQuickStarShape::setStrokeWidth(qreal width)
327{
328 Q_D(QQuickStarShape);
329 d->path->setStrokeWidth(width);
330}
331
332/*!
333 \qmlproperty color QtQuick.Shapes.DesignHelpers::StarShape::fillColor
334
335 This property holds the fill color.
336
337 When set to \c transparent, no filling occurs.
338
339 The default value is \c "white".
340
341 \note If either \l fillGradient is set to something other than \c null, it
342 will be used instead of \c fillColor.
343*/
344
345QColor QQuickStarShape::fillColor() const
346{
347 Q_D(const QQuickStarShape);
348 return d->path->fillColor();
349}
350
351void QQuickStarShape::setFillColor(const QColor &color)
352{
353 Q_D(QQuickStarShape);
354 d->path->setFillColor(color);
355}
356
357/*!
358 \qmlproperty color QtQuick.Shapes.DesignHelpers::StarShape::strokeColor
359
360 This property holds the stroking color.
361
362 When set to \c transparent, no stroking occurs.
363
364 The default value is \c "black".
365*/
366
367QColor QQuickStarShape::strokeColor() const
368{
369 Q_D(const QQuickStarShape);
370 return d->path->strokeColor();
371}
372
373void QQuickStarShape::setStrokeColor(const QColor &color)
374{
375 Q_D(QQuickStarShape);
376 d->path->setStrokeColor(color);
377}
378
379/*!
380 \include shapepath.qdocinc {capStyle-property}
381 {QtQuick.Shapes.DesignHelpers::StarShape}
382
383 Since a star is drawn, the path forms a loop with no line end points.
384 Therefore, capStyle is only needed when strokeStyle == ShapePath.DashLine
385*/
386
387QQuickShapePath::CapStyle QQuickStarShape::capStyle() const
388{
389 Q_D(const QQuickStarShape);
390 return d->path->capStyle();
391}
392
393void QQuickStarShape::setCapStyle(QQuickShapePath::CapStyle style)
394{
395 Q_D(QQuickStarShape);
396 d->path->setCapStyle(style);
397}
398
399/*!
400 \include shapepath.qdocinc {joinStyle-property}
401 {QtQuick.Shapes.DesignHelpers::StarShape}
402
403 The joinStyle is only meaningful if cornerRadius == 0.
404*/
405
406QQuickShapePath::JoinStyle QQuickStarShape::joinStyle() const
407{
408 Q_D(const QQuickStarShape);
409 return d->path->joinStyle();
410}
411
412void QQuickStarShape::setJoinStyle(QQuickShapePath::JoinStyle style)
413{
414 Q_D(QQuickStarShape);
415 d->path->setJoinStyle(style);
416}
417
418/*!
419 \include shapepath.qdocinc {strokeStyle-property}
420 {QtQuick.Shapes.DesignHelpers::StarShape}
421*/
422
423QQuickShapePath::StrokeStyle QQuickStarShape::strokeStyle() const
424{
425 Q_D(const QQuickStarShape);
426 return d->path->strokeStyle();
427}
428
429void QQuickStarShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
430{
431 Q_D(QQuickStarShape);
432 d->path->setStrokeStyle(style);
433}
434
435/*!
436 \include shapepath.qdocinc {dashPattern-property}
437 {QtQuick.Shapes.DesignHelpers::StarShape}
438*/
439
440QList<qreal> QQuickStarShape::dashPattern() const
441{
442 Q_D(const QQuickStarShape);
443 return d->path->dashPattern();
444}
445
446void QQuickStarShape::setDashPattern(const QList<qreal> &array)
447{
448 Q_D(QQuickStarShape);
449 d->path->setDashPattern(array);
450}
451
452/*!
453 \qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::StarShape::fillGradient
454
455 The fillGradient of the star fill color.
456
457 By default, no fillGradient is enabled and the value is null. In this case, the
458 fill uses a solid color based on the value of \l fillColor.
459
460 When set, \l fillColor is ignored and filling is done using one of the
461 \l ShapeGradient subtypes.
462
463 \note The \l Gradient type cannot be used here. Rather, prefer using one of
464 the advanced subtypes, like \l LinearGradient.
465*/
466QQuickShapeGradient *QQuickStarShape::fillGradient() const
467{
468 Q_D(const QQuickStarShape);
469 return d->path->fillGradient();
470}
471
472void QQuickStarShape::setFillGradient(QQuickShapeGradient *fillGradient)
473{
474 Q_D(QQuickStarShape);
475 d->path->setFillGradient(fillGradient);
476}
477
478void QQuickStarShape::resetFillGradient()
479{
480 setFillGradient(nullptr);
481}
482
483QQuickItem *QQuickStarShape::fillItem() const
484{
485 Q_D(const QQuickStarShape);
486 return d->path->fillItem();
487}
488
489void QQuickStarShape::setFillItem(QQuickItem *newFillItem)
490{
491 Q_D(QQuickStarShape);
492 d->path->setFillItem(newFillItem);
493}
494
495void QQuickStarShape::itemChange(ItemChange change, const ItemChangeData &value)
496{
497 Q_D(QQuickStarShape);
498
499 if (d->path)
500 d->updatePath();
501
502 QQuickItem::itemChange(change, value);
503}
504
505QT_END_NAMESPACE
506
507#include "moc_qquickstarshape_p.cpp"
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:32
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:503
QVector2D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:530
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:502
static constexpr float dotProduct(QVector2D v1, QVector2D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:605