diff --git a/src/common/QskArcMetrics.cpp b/src/common/QskArcMetrics.cpp index 6981b45c..70eeb12f 100644 --- a/src/common/QskArcMetrics.cpp +++ b/src/common/QskArcMetrics.cpp @@ -25,13 +25,6 @@ static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio ) return from + ( to - from ) * ratio; } -static inline qreal qskAbsoluted( qreal length, qreal percentage ) -{ - // 100% means -> 0.5 of length - percentage = qBound( 0.0, percentage, 100.0 ); - return percentage / 100.0 * 0.5 * length; -} - void QskArcMetrics::setThickness( qreal thickness ) noexcept { m_thickness = thickness; @@ -93,32 +86,33 @@ QVariant QskArcMetrics::interpolate( return QVariant::fromValue( from.interpolated( to, progress ) ); } -QskArcMetrics QskArcMetrics::toAbsolute( const QSizeF& size ) const noexcept +QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY ) const noexcept { - if ( size.width() < 0.0 ) - return toAbsolute( size.height() ); + if ( radiusX < 0.0 ) + return toAbsolute( radiusY ); - if ( size.height() < 0.0 ) - return toAbsolute( size.width() ); + if ( radiusY < 0.0 ) + return toAbsolute( radiusX ); - return toAbsolute( qMin( size.width(), size.height() ) ); + return toAbsolute( qMin( radiusX, radiusY ) ); } -QskArcMetrics QskArcMetrics::toAbsolute( qreal size ) const noexcept +QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept { if ( m_sizeMode != Qt::RelativeSize ) return *this; - QskArcMetrics absoluted = *this; + QskArcMetrics m = *this; - if ( size <= 0.0 ) - absoluted.m_thickness = 0.0; - else - absoluted.m_thickness = qskAbsoluted( size, absoluted.m_thickness ); + if ( radius < 0.0 ) + radius = 0.0; - absoluted.m_sizeMode = Qt::AbsoluteSize; + const auto ratio = qBound( 0.0, m.m_thickness, 100.0 ) / 100.0; - return absoluted; + m.m_thickness = radius * ratio; + m.m_sizeMode = Qt::AbsoluteSize; + + return m; } QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept diff --git a/src/common/QskArcMetrics.h b/src/common/QskArcMetrics.h index ee087475..ecd94326 100644 --- a/src/common/QskArcMetrics.h +++ b/src/common/QskArcMetrics.h @@ -56,8 +56,8 @@ class QSK_EXPORT QskArcMetrics QskArcMetrics interpolated( const QskArcMetrics&, qreal value ) const noexcept; - QskArcMetrics toAbsolute( const QSizeF& ) const noexcept; - QskArcMetrics toAbsolute( qreal ) const noexcept; + QskArcMetrics toAbsolute( qreal radiusX, qreal radiusY ) const noexcept; + QskArcMetrics toAbsolute( qreal radius ) const noexcept; QskHashValue hash( QskHashValue seed = 0 ) const noexcept; diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 0b35c590..b57387be 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -210,7 +210,10 @@ static inline QSGNode* qskUpdateArcNode( if ( rect.isEmpty() ) return nullptr; - const auto absoluteMetrics = metrics.toAbsolute( rect.size() ); + const auto rx = 0.5 * rect.width(); + const auto ry = 0.5 * rect.height(); + + const auto absoluteMetrics = metrics.toAbsolute( rx, ry ); if ( !qskIsArcVisible( absoluteMetrics, fillGradient ) ) return nullptr; diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index a9259ed9..fee133a2 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -11,31 +11,29 @@ #include -static inline QskGradient effectiveGradient( const QRectF& rect, - const QskArcMetrics& metrics, const QskGradient& gradient ) +#define LINEAR_GRADIENT_HACK 1 + +#if LINEAR_GRADIENT_HACK + +static inline QskGradient buildGradient( QskGradient::Type type, + const QRectF& rect, const QskArcMetrics& metrics, + const QskGradientStops& stops ) { - if ( gradient.isMonochrome() ) - return gradient; - - bool isRadial = false; - - if ( gradient.type() == QskGradient::Linear ) - { - /* - Horizontal is interpreted as conic ( in direction of the arc ), - while Vertical means radial ( inner to outer border ) - */ - isRadial = gradient.linearDirection().isVertical(); - } - - auto g = gradient; - g.setStretchMode( QskGradient::NoStretch ); - const auto center = rect.center(); - if( isRadial ) + QskGradient gradient; + gradient.setStretchMode( QskGradient::NoStretch ); + + if ( type == QskGradient::Conic ) { - g.setRadialDirection( center.x(), center.y(), + gradient.setConicDirection( + center.x(), center.y(), metrics.startAngle() ); + + gradient.setStops( stops ); + } + else + { + gradient.setRadialDirection( center.x(), center.y(), rect.width(), rect.height() ); { @@ -47,24 +45,58 @@ static inline QskGradient effectiveGradient( const QRectF& rect, const auto radius = 0.5 * qMin( rect.width(), rect.height() ); const auto t = metrics.thickness() / radius; - QskGradientStops stops; - stops.reserve( gradient.stops().size() ); + QskGradientStops scaledStops; + scaledStops.reserve( stops.size() ); - for ( const auto& stop : gradient.stops() ) + for ( const auto& stop : stops ) { const auto pos = 0.5 - t * ( 0.75 - stop.position() ); - stops += QskGradientStop( pos, stop.color() ); + scaledStops += QskGradientStop( pos, stop.color() ); } - g.setStops( stops ); + gradient.setStops( scaledStops ); } } - else + + return gradient; +} + +#endif + +static inline QskGradient effectiveGradient( const QRectF& rect, + const QskArcMetrics& metrics, const QskGradient& gradient ) +{ + if ( !gradient.isMonochrome() ) { - g.setConicDirection( center.x(), center.y(), metrics.startAngle() ); + if ( gradient.type() == QskGradient::Stops ) + { + const QskConicDirection dir( + rect.center(), metrics.startAngle() ); +#if 0 + dir.setSpanAngle( metrics.spanAngle() ); // what is "expected" ?? +#endif + + QskGradient g( gradient.stops() ); + g.setStretchMode( QskGradient::NoStretch ); + g.setConicDirection( dir ); + + return g; + } + +#if LINEAR_GRADIENT_HACK + if ( gradient.type() == QskGradient::Linear ) + { + // to keep the iotdashboard working: to be removed + + const auto type = gradient.linearDirection().isHorizontal() + ? QskGradient::Conic : QskGradient::Radial; + + return buildGradient( type, rect, metrics, gradient.stops() ); + } +#endif } - return g; + return gradient; } QskArcNode::QskArcNode() @@ -78,22 +110,8 @@ QskArcNode::~QskArcNode() void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics, const QskGradient& gradient ) { -#if 1 - /* - Translating linear gradients into conic or radial gradients. - This code is a leftover from situations, where only linear - gradients had been available. Once the iotdashboard example - has been adjusted we will remove this code TODO ... - */ - const auto g = effectiveGradient( rect, metrics, gradient ); -#endif - - /* - For the moment using a QPainterPath/QskShapeNode. - But we can do better by creatig vertex lists manually - like what is done by the box renderer. TODO ... - */ - const auto path = QskArcRenderer::arcPath( rect, metrics ); - updateNode( path, QTransform(), rect, g ); + + updateNode( path, QTransform(), rect, + effectiveGradient( rect, metrics, gradient ) ); } diff --git a/src/nodes/QskArcNode.h b/src/nodes/QskArcNode.h index feb7f341..a74a3b96 100644 --- a/src/nodes/QskArcNode.h +++ b/src/nodes/QskArcNode.h @@ -11,6 +11,11 @@ class QskArcMetrics; class QskGradient; +/* + For the moment a QPainterPath/QskShapeNode. + But we can do better by creatig vertex lists manually + like what is done by the box renderer. TODO ... + */ class QSK_EXPORT QskArcNode : public QskShapeNode { public: diff --git a/src/nodes/QskArcRenderer.cpp b/src/nodes/QskArcRenderer.cpp index 88f61c23..cc9a2af5 100644 --- a/src/nodes/QskArcRenderer.cpp +++ b/src/nodes/QskArcRenderer.cpp @@ -9,10 +9,13 @@ #include #include -QPainterPath QskArcRenderer::arcPath( +static inline QPainterPath qskArcPath( const QRectF& rect, const QskArcMetrics& metrics ) { - const auto m = metrics.toAbsolute( rect.size() ); + const auto rx = 0.5 * rect.width(); + const auto ry = 0.5 * rect.height(); + + const auto m = metrics.toAbsolute( rx, ry ); const qreal t2 = 0.5 * m.thickness(); const auto r = rect.adjusted( t2, t2, -t2, -t2 ); @@ -27,3 +30,59 @@ QPainterPath QskArcRenderer::arcPath( return stroker.createStroke( path ); } + +static inline QRectF qskArcRect( + const QRectF& rect, const QskArcMetrics& metrics ) +{ + return qskArcPath( rect, metrics ).controlPointRect(); +} + +QPainterPath QskArcRenderer::arcPath( + qreal radius, const QskArcMetrics& metrics ) +{ + const QRectF r( 0.0, 0.0, 2 * radius, 2 * radius ); + return qskArcPath( r, metrics ); +} + +QPainterPath QskArcRenderer::arcPath( + const QSizeF& diameters, const QskArcMetrics& metrics ) +{ + const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() ); + return qskArcPath( r, metrics ); +} + +QPainterPath QskArcRenderer::arcPath( + const QRectF& rect, const QskArcMetrics& metrics ) +{ + return qskArcPath( rect, metrics ); +} + +QRectF QskArcRenderer::arcRect( qreal radius, const QskArcMetrics& metrics ) +{ + const qreal d = 2.0 * radius; + return qskArcRect( QRectF( 0.0, 0.0, d, d ), metrics ); +} + +QRectF QskArcRenderer::arcRect( const QSizeF& diameters, const QskArcMetrics& metrics ) +{ + const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() ); + return qskArcRect( r, metrics ); +} + +QRectF QskArcRenderer::arcRect( const QRectF& rect, const QskArcMetrics& metrics ) +{ + return qskArcRect( rect, metrics ); +} + +QSizeF QskArcRenderer::arcSize( + const QSizeF& diameters, const QskArcMetrics& metrics ) +{ + if ( qFuzzyIsNull( metrics.spanAngle() ) ) + return QSizeF(); + + if ( qAbs( metrics.spanAngle() ) >= 360.0 ) + return diameters; + + const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() ); + return qskArcRect( r, metrics ).size(); +} diff --git a/src/nodes/QskArcRenderer.h b/src/nodes/QskArcRenderer.h index f54b0061..d4bca494 100644 --- a/src/nodes/QskArcRenderer.h +++ b/src/nodes/QskArcRenderer.h @@ -12,10 +12,22 @@ class QskArcMetrics; class QPainterPath; class QRectF; +class QSizeF; namespace QskArcRenderer { + // radius + QSK_EXPORT QPainterPath arcPath( qreal radius, const QskArcMetrics& ); + QSK_EXPORT QRectF arcRect( qreal radius, const QskArcMetrics& ); + + // diameter + QSK_EXPORT QPainterPath arcPath( const QSizeF&, const QskArcMetrics& ); + QSK_EXPORT QSizeF arcSize( const QSizeF&, const QskArcMetrics& ); + QSK_EXPORT QRectF arcRect( const QSizeF&, const QskArcMetrics& ); + + // bounding rectangle QSK_EXPORT QPainterPath arcPath( const QRectF&, const QskArcMetrics& ); + QSK_EXPORT QRectF arcRect( const QRectF&, const QskArcMetrics& ); }; #endif