6#include "QskArcMetrics.h"
7#include "QskFunctions.h"
9#include <qhashfunctions.h>
10#include <qpainterpath.h>
13static void qskRegisterArcMetrics()
15 qRegisterMetaType< QskArcMetrics >();
17#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
18 QMetaType::registerEqualsComparator< QskArcMetrics >();
22Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics )
24static inline QPainterPath qskRadialPathPath(
25 const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width )
27 const auto sz = qMin( rect.width(), rect.height() );
29 const auto tx = width * rect.width() / sz;
30 const auto ty = width * rect.height() / sz;
32 const auto innerRect = rect.adjusted( tx, ty, -tx, -ty );
36 if ( innerRect.isEmpty() )
38 if ( qAbs( spanAngle ) >= 360.0 )
40 path.addEllipse( rect );
45 path.arcMoveTo( rect, startAngle );
46 path.arcTo( rect, startAngle, spanAngle );
47 path.lineTo( rect.center() );
53 if ( qAbs( spanAngle ) >= 360.0 )
55 path.addEllipse( rect );
57 QPainterPath innerPath;
58 innerPath.addEllipse( innerRect );
68 path.arcMoveTo( innerRect, startAngle + spanAngle );
69 const auto pos = path.currentPosition();
71 path.arcMoveTo( rect, startAngle );
72 path.arcTo( rect, startAngle, spanAngle );
75 path.arcTo( innerRect, startAngle + spanAngle, -spanAngle );
84static inline QPainterPath qskOrthogonalPath(
85 const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width )
87 const auto t2 = 0.5 * width;
88 const auto r = rect.adjusted( t2, t2, -t2, -t2 );
91 arcPath.arcMoveTo( r, startAngle );
92 arcPath.arcTo( r, startAngle, spanAngle );
94 QPainterPathStroker stroker;
95 stroker.setCapStyle( Qt::FlatCap );
96 stroker.setWidth( width );
98 return stroker.createStroke( arcPath );
101static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio )
103 return from + ( to - from ) * ratio;
106static inline qreal qskEffectiveThickness( qreal radius, qreal percentage )
108 return qMax( radius, 0.0 ) * qBound( 0.0, percentage, 100.0 ) / 100.0;
111void QskArcMetrics::setThickness( qreal thickness )
noexcept
113 m_thickness = thickness;
116void QskArcMetrics::setStartAngle( qreal startAngle )
noexcept
118 m_startAngle = qskConstrainedDegrees( startAngle );
121void QskArcMetrics::setSpanAngle( qreal spanAngle )
noexcept
123 m_spanAngle = qBound( -360.0, spanAngle, 360.0 );
126void QskArcMetrics::setSizeMode( Qt::SizeMode sizeMode )
noexcept
128 m_relativeSize = ( sizeMode == Qt::RelativeSize );
131bool QskArcMetrics::isClosed()
const
133 return qAbs( m_spanAngle ) >= 360.0;
136bool QskArcMetrics::containsAngle( qreal angle )
const
138 angle = qskConstrainedDegrees( angle );
140 if ( m_spanAngle >= 0.0 )
142 if ( angle < m_startAngle )
145 return ( angle >= m_startAngle ) && ( angle <= m_startAngle + m_spanAngle );
149 if ( angle > m_startAngle )
152 return ( angle <= m_startAngle ) && ( angle >= m_startAngle + m_spanAngle );
159 if ( ( *
this == to ) || ( m_relativeSize != to.m_relativeSize ) )
162 const qreal thickness = qskInterpolated( m_thickness, to.m_thickness, ratio );
164 const qreal s1 = qskInterpolated( m_startAngle, to.m_startAngle, ratio );
165 const qreal s2 = qskInterpolated( endAngle(), to.endAngle(), ratio );
170QVariant QskArcMetrics::interpolate(
174 return QVariant::fromValue( from.interpolated( to, progress ) );
177QskArcMetrics QskArcMetrics::toAbsolute(
const QSizeF& size )
const noexcept
179 return toAbsolute( 0.5 * size.width(), 0.5 * size.height() );
182QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY )
const noexcept
185 return toAbsolute( radiusY );
188 return toAbsolute( radiusX );
190 return toAbsolute( qMin( radiusX, radiusY ) );
193QskArcMetrics QskArcMetrics::toAbsolute( qreal radius )
const noexcept
195 if ( !m_relativeSize )
198 const qreal t = qskEffectiveThickness( radius, m_thickness );
199 return QskArcMetrics( m_startAngle, m_spanAngle, t, Qt::AbsoluteSize );
202QPainterPath QskArcMetrics::painterPath(
const QRectF& rect,
bool radial )
const
206 if ( !qFuzzyIsNull( m_spanAngle ) )
208 qreal t = m_thickness;
210 if ( m_relativeSize )
212 const auto sz = qMin( rect.width(), rect.height() );
213 t = qskEffectiveThickness( 0.5 * sz, t );
219 path = qskRadialPathPath( rect, m_startAngle, m_spanAngle, t );
221 path = qskOrthogonalPath( rect, m_startAngle, m_spanAngle, t );
228QRectF QskArcMetrics::boundingRect(
const QRectF& ellipseRect )
const
230 if ( qFuzzyIsNull( m_spanAngle ) )
231 return QRectF( ellipseRect.center(), QSizeF() );
233 if ( qAbs( m_spanAngle ) >= 360.0 )
236 return painterPath( ellipseRect ).controlPointRect();
239QSizeF QskArcMetrics::boundingSize(
const QSizeF& ellipseSize )
const
241 if ( qFuzzyIsNull( m_spanAngle ) )
244 if ( qAbs( m_spanAngle ) >= 360.0 )
247 const QRectF r( 0.0, 0.0, ellipseSize.width(), ellipseSize.height() );
248 return painterPath( r ).controlPointRect().size();
251QskHashValue QskArcMetrics::hash( QskHashValue seed )
const noexcept
253 auto hash = qHash( m_thickness, seed );
254 hash = qHash( m_startAngle, hash );
255 hash = qHash( m_spanAngle, hash );
257 return qHash( m_relativeSize, hash );
260#ifndef QT_NO_DEBUG_STREAM
264QDebug operator<<( QDebug debug,
const QskArcMetrics& metrics )
266 QDebugStateSaver saver( debug );
269 debug <<
"QskArcMetrics" <<
'(';
270 debug << metrics.thickness() <<
',' << metrics.sizeMode();
271 debug <<
",[" << metrics.startAngle() <<
',' << metrics.spanAngle() <<
']';
279#include "moc_QskArcMetrics.cpp"