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