diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0403a369..f93b7b48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,7 +95,6 @@ list(APPEND SOURCES list(APPEND HEADERS nodes/QskArcNode.h - nodes/QskArcRenderer.h nodes/QskBoxNode.h nodes/QskBoxClipNode.h nodes/QskBoxFillNode.h @@ -125,7 +124,6 @@ list(APPEND HEADERS list(APPEND SOURCES nodes/QskArcNode.cpp - nodes/QskArcRenderer.cpp nodes/QskBoxNode.cpp nodes/QskBoxClipNode.cpp nodes/QskBoxFillNode.cpp diff --git a/src/common/QskArcMetrics.cpp b/src/common/QskArcMetrics.cpp index bc6fb549..938a0ab9 100644 --- a/src/common/QskArcMetrics.cpp +++ b/src/common/QskArcMetrics.cpp @@ -7,6 +7,7 @@ #include "QskFunctions.h" #include +#include #include static void qskRegisterArcMetrics() @@ -25,6 +26,11 @@ static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio ) return from + ( to - from ) * ratio; } +static inline qreal qskEffectiveThickness( qreal radius, qreal percentage ) +{ + return qMax( radius, 0.0 ) * qBound( 0.0, percentage, 100.0 ) / 100.0; +} + void QskArcMetrics::setThickness( qreal thickness ) noexcept { m_thickness = thickness; @@ -107,17 +113,83 @@ QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept if ( m_sizeMode != Qt::RelativeSize ) return *this; - QskArcMetrics m = *this; + const qreal t = qskEffectiveThickness( radius, m_thickness ); + return QskArcMetrics( m_startAngle, m_spanAngle, t, Qt::AbsoluteSize ); +} - if ( radius < 0.0 ) - radius = 0.0; +QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const +{ + /* + We might want to have no connecting line between inner and + outer border. F.e. for 360° arcs. TODO ... + */ + const auto sz = qMin( ellipseRect.width(), ellipseRect.height() ); - const auto ratio = qBound( 0.0, m.m_thickness, 100.0 ) / 100.0; + qreal t = m_thickness; + if ( m_sizeMode == Qt::RelativeSize ) + t = qskEffectiveThickness( 0.5 * sz, t ); - m.m_thickness = radius * ratio; - m.m_sizeMode = Qt::AbsoluteSize; + if ( t <= 0.0 || qFuzzyIsNull( m_spanAngle ) ) + return QPainterPath(); - return m; + const auto tx = t * ellipseRect.width() / sz; + const auto ty = t * ellipseRect.height() / sz; + + const auto innerRect = ellipseRect.adjusted( tx, ty, -tx, -ty ); + + QPainterPath path; + + if ( innerRect.isEmpty() ) + { + // a pie + + path.arcMoveTo( ellipseRect, m_startAngle ); + path.arcTo( ellipseRect, m_startAngle, m_spanAngle ); + path.lineTo( ellipseRect.center() ); + } + else + { + /* + We need the end point of the inner arc to add the line that connects + the inner/outer arcs. As QPainterPath does not offer such a method + we insert a dummy arcMoveTo and grab the calculated position. + */ + path.arcMoveTo( innerRect, m_startAngle + m_spanAngle ); + const auto pos = path.currentPosition(); + + path.arcMoveTo( ellipseRect, m_startAngle ); // replaces the dummy arcMoveTo above + path.arcTo( ellipseRect, m_startAngle, m_spanAngle ); + + path.lineTo( pos ); + path.arcTo( innerRect, m_startAngle + m_spanAngle, -m_spanAngle ); + } + + path.closeSubpath(); + + return path; +} + +QRectF QskArcMetrics::boundingRect( const QRectF& ellipseRect ) const +{ + if ( qFuzzyIsNull( m_spanAngle ) ) + return QRectF( ellipseRect.center(), QSizeF() ); + + if ( qAbs( m_spanAngle ) >= 360.0 ) + return ellipseRect; + + return painterPath( ellipseRect ).controlPointRect(); +} + +QSizeF QskArcMetrics::boundingSize( const QSizeF& ellipseSize ) const +{ + if ( qFuzzyIsNull( m_spanAngle ) ) + return QSizeF(); + + if ( qAbs( m_spanAngle ) >= 360.0 ) + return ellipseSize; + + const QRectF r( 0.0, 0.0, ellipseSize.width(), ellipseSize.height() ); + return painterPath( r ).controlPointRect().size(); } QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept diff --git a/src/common/QskArcMetrics.h b/src/common/QskArcMetrics.h index 8e3fef11..6197f3a9 100644 --- a/src/common/QskArcMetrics.h +++ b/src/common/QskArcMetrics.h @@ -11,6 +11,7 @@ #include class QVariant; +class QPainterPath; class QSK_EXPORT QskArcMetrics { @@ -60,6 +61,11 @@ class QSK_EXPORT QskArcMetrics QskArcMetrics toAbsolute( qreal radiusX, qreal radiusY ) const noexcept; QskArcMetrics toAbsolute( qreal radius ) const noexcept; + QPainterPath painterPath( const QRectF& ellipseRect ) const; + + QRectF boundingRect( const QRectF& ellipseRect ) const; + QSizeF boundingSize( const QSizeF& ellipseSize ) const; + QskHashValue hash( QskHashValue seed = 0 ) const noexcept; static QVariant interpolate( const QskArcMetrics&, diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index 39dfbf12..a58a72df 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -4,7 +4,6 @@ *****************************************************************************/ #include "QskArcNode.h" -#include "QskArcRenderer.h" #include "QskArcMetrics.h" #include "QskMargins.h" #include "QskGradient.h" @@ -96,7 +95,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics return; } - const auto path = QskArcRenderer::arcPath( arcRect, metrics ); + const auto path = metrics.painterPath( arcRect ); if ( gradient.isVisible() && !metrics.isNull() ) { diff --git a/src/nodes/QskArcRenderer.cpp b/src/nodes/QskArcRenderer.cpp deleted file mode 100644 index 6d88a18b..00000000 --- a/src/nodes/QskArcRenderer.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskArcRenderer.h" -#include "QskArcMetrics.h" - -#include -#include - -static inline QPainterPath qskArcPath( - const QRectF& rect, const QskArcMetrics& metrics ) -{ - const auto sz = qMin( rect.width(), rect.height() ); - if ( sz <= 0.0 ) - return QPainterPath(); - - const auto m = metrics.toAbsolute( 0.5 * sz ); - - const auto tx = m.thickness() * rect.width() / sz; - const auto ty = m.thickness() * rect.height() / sz; - - const auto innerRect = rect.adjusted( tx, ty, -tx, -ty ); - - const auto angle = m.startAngle(); - const auto span = m.spanAngle(); - - QPainterPath path; - - /* - We need the end point of the inner arc to add the line that connects - the inner/outer arcs. As QPainterPath does not offer such a method - we insert a dummy arcMoveTo and grab the calculated position. - */ - path.arcMoveTo( innerRect, angle + span ); - const auto pos = path.currentPosition(); - - path.arcMoveTo( rect, angle ); // replaces the dummy arcMoveTo above - path.arcTo( rect, angle, span ); - - path.lineTo( pos ); - path.arcTo( innerRect, angle + span, -span ); - - path.closeSubpath(); - - return 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 deleted file mode 100644 index d4bca494..00000000 --- a/src/nodes/QskArcRenderer.h +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_ARC_RENDERER_H -#define QSK_ARC_RENDERER_H - -#include "QskGlobal.h" - -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