diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6ff444ae..556a859d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ list(APPEND HEADERS common/QskGradient.h common/QskGradientDirection.h common/QskGradientStop.h + common/QskGraduationMetrics.h common/QskHctColor.h common/QskIntervalF.h common/QskLabelData.h @@ -48,6 +49,7 @@ list(APPEND SOURCES common/QskGradient.cpp common/QskGradientDirection.cpp common/QskGradientStop.cpp + common/QskGraduationMetrics.cpp common/QskHctColor.cpp common/QskIntervalF.cpp common/QskLabelData.cpp diff --git a/src/common/QskAspect.h b/src/common/QskAspect.h index 4c20c13f0..7b67eb364 100644 --- a/src/common/QskAspect.h +++ b/src/common/QskAspect.h @@ -72,7 +72,9 @@ class QSK_EXPORT QskAspect Shadow, Shape, - Border + Border, + + Graduation }; Q_ENUM( Primitive ) diff --git a/src/common/QskGraduationMetrics.cpp b/src/common/QskGraduationMetrics.cpp new file mode 100644 index 000000000..a24350f07 --- /dev/null +++ b/src/common/QskGraduationMetrics.cpp @@ -0,0 +1,58 @@ +#include "QskGraduationMetrics.h" + +#include + +static void qskRegisterGraduationMetrics() +{ + qRegisterMetaType< QskGraduationMetrics >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskGraduationMetrics >(); +#endif +} + +Q_CONSTRUCTOR_FUNCTION( qskRegisterGraduationMetrics ) + +static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio ) +{ + return from + ( to - from ) * ratio; +} + +QskGraduationMetrics QskGraduationMetrics::interpolated( + const QskGraduationMetrics& to, const qreal ratio ) const noexcept +{ + if ( ( *this == to ) ) + return to; + + return { qskInterpolated( m_tickLengths[0], to.m_tickLengths[0], ratio ), + qskInterpolated( m_tickLengths[1], to.m_tickLengths[1], ratio ), + qskInterpolated( m_tickLengths[2], to.m_tickLengths[2], ratio ) }; +} + +QVariant QskGraduationMetrics::interpolate( + const QskGraduationMetrics& from, const QskGraduationMetrics& to, const qreal progress ) +{ + return QVariant::fromValue( from.interpolated( to, progress ) ); +} + +#ifndef QT_NO_DEBUG_STREAM + +#include + +QDebug operator<<( QDebug debug, const QskGraduationMetrics& metrics ) +{ + const char s[] = ", "; + + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << "Graduation"; + debug << '('; + debug << metrics.minorTickLength() << s << metrics.mediumTickLength() + << s << metrics.majorTickLength(); + debug << ')'; + + return debug; +} + +#endif diff --git a/src/common/QskGraduationMetrics.h b/src/common/QskGraduationMetrics.h new file mode 100644 index 000000000..9343492f4 --- /dev/null +++ b/src/common/QskGraduationMetrics.h @@ -0,0 +1,146 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_GRADUATION_METRICS_H +#define QSK_GRADUATION_METRICS_H + +#include "QskScaleTickmarks.h" +#include "QskFunctions.h" +#include +#include + +class QSK_EXPORT QskGraduationMetrics +{ + Q_GADGET + Q_PROPERTY( qreal majorTickLength READ majorTickLength WRITE setMajorTickLength ) + Q_PROPERTY( qreal mediumTickLength READ mediumTickLength WRITE setMediumTickLength ) + Q_PROPERTY( qreal minorTickLength READ minorTickLength WRITE setMinorTickLength ) + + public: + using TickType = QskScaleTickmarks::TickType; + + constexpr QskGraduationMetrics() noexcept = default; + constexpr QskGraduationMetrics( qreal minorTickLength, + qreal mediumTickLength, qreal majorTickLength ) noexcept; + constexpr QskGraduationMetrics( const QskGraduationMetrics& ) noexcept = default; + constexpr QskGraduationMetrics( QskGraduationMetrics&& ) noexcept = default; + + constexpr QskGraduationMetrics& operator=( const QskGraduationMetrics& ) noexcept = default; + constexpr QskGraduationMetrics& operator=( QskGraduationMetrics&& ) noexcept = default; + + [[nodiscard]] constexpr bool operator==( const QskGraduationMetrics& rhs ) const noexcept; + [[nodiscard]] constexpr bool operator!=( const QskGraduationMetrics& rhs ) const noexcept; + + constexpr void setTickLength( TickType, qreal ) noexcept; + [[nodiscard]] constexpr qreal tickLength( TickType ) const noexcept; + + constexpr void setMinorTickLength( qreal ) noexcept; + [[nodiscard]] constexpr qreal minorTickLength() const noexcept; + + constexpr void setMediumTickLength( qreal ) noexcept; + [[nodiscard]] constexpr qreal mediumTickLength() const noexcept; + + constexpr void setMajorTickLength( qreal ) noexcept; + [[nodiscard]] constexpr qreal majorTickLength() const noexcept; + + [[nodiscard]] QskGraduationMetrics interpolated( + const QskGraduationMetrics&, qreal progress ) const noexcept; + + [[nodiscard]] static QVariant interpolate( + const QskGraduationMetrics&, const QskGraduationMetrics&, qreal progress ); + + [[nodiscard]] QskHashValue hash( QskHashValue seed = 0 ) const noexcept; + + private: + static inline constexpr qreal constrainedLength( qreal length ) + { + return std::max( 0.0, length ); + } + + qreal m_tickLengths[3] = {}; +}; + +inline constexpr QskGraduationMetrics::QskGraduationMetrics( + qreal minorTickLength, qreal mediumTickLength, qreal majorTickLength ) noexcept + : m_tickLengths{ constrainedLength( minorTickLength ), + constrainedLength( mediumTickLength ), constrainedLength( majorTickLength ) } +{ +} + +inline constexpr qreal QskGraduationMetrics::majorTickLength() const noexcept +{ + return tickLength( QskScaleTickmarks::MajorTick ); +} + +inline constexpr qreal QskGraduationMetrics::mediumTickLength() const noexcept +{ + return tickLength( QskScaleTickmarks::MediumTick ); +} + +inline constexpr qreal QskGraduationMetrics::minorTickLength() const noexcept +{ + return tickLength( QskScaleTickmarks::MinorTick ); +} + +inline constexpr void QskGraduationMetrics::setMajorTickLength( qreal length ) noexcept +{ + setTickLength( QskScaleTickmarks::MajorTick, length ); +} + +inline constexpr void QskGraduationMetrics::setMediumTickLength( qreal length ) noexcept +{ + setTickLength( QskScaleTickmarks::MediumTick, length ); +} + +inline constexpr void QskGraduationMetrics::setMinorTickLength( qreal length ) noexcept +{ + setTickLength( QskScaleTickmarks::MinorTick, length ); +} + +inline constexpr bool QskGraduationMetrics::operator==( + const QskGraduationMetrics& other ) const noexcept +{ + return qskFuzzyCompare( m_tickLengths[0], other.m_tickLengths[0] ) && + qskFuzzyCompare( m_tickLengths[1], other.m_tickLengths[1] ) && + qskFuzzyCompare( m_tickLengths[2], other.m_tickLengths[2] ); +} + +inline constexpr bool QskGraduationMetrics::operator!=( + const QskGraduationMetrics& rhs ) const noexcept +{ + return !( *this == rhs ); +} + +inline constexpr qreal QskGraduationMetrics::tickLength( + const QskScaleTickmarks::TickType type ) const noexcept +{ + return m_tickLengths[ type ]; +} + +inline constexpr void QskGraduationMetrics::setTickLength( + TickType type, qreal length ) noexcept +{ + m_tickLengths[ type ] = constrainedLength( length ); +} + +inline QskHashValue QskGraduationMetrics::hash( const QskHashValue seed ) const noexcept +{ + auto hash = qHash( m_tickLengths[0], seed ); + hash = qHash( m_tickLengths[1], hash ); + hash = qHash( m_tickLengths[2], hash ); + return hash; +} + +#ifndef QT_NO_DEBUG_STREAM + +class QDebug; +QSK_EXPORT QDebug operator<<( QDebug, const QskGraduationMetrics& ); + +#endif + +Q_DECLARE_TYPEINFO( QskGraduationMetrics, Q_MOVABLE_TYPE ); +Q_DECLARE_METATYPE( QskGraduationMetrics ) + +#endif diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index dc1967724..61b8a57cb 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -10,6 +10,7 @@ #include "QskBoxShapeMetrics.h" #include "QskShadowMetrics.h" #include "QskStippleMetrics.h" +#include "QskGraduationMetrics.h" #include "QskColorFilter.h" #include "QskGradient.h" #include "QskMargins.h" @@ -49,6 +50,7 @@ static void qskRegisterInterpolator() qRegisterAnimationInterpolator< QskShadowMetrics >( QskShadowMetrics::interpolate ); qRegisterAnimationInterpolator< QskStippleMetrics >( QskStippleMetrics::interpolate ); qRegisterAnimationInterpolator< QskArcMetrics >( QskArcMetrics::interpolate ); + qRegisterAnimationInterpolator< QskGraduationMetrics >( QskGraduationMetrics::interpolate ); } Q_CONSTRUCTOR_FUNCTION( qskRegisterInterpolator ) diff --git a/src/nodes/QskScaleRenderer.cpp b/src/nodes/QskScaleRenderer.cpp index 51877069f..16c3dfabe 100644 --- a/src/nodes/QskScaleRenderer.cpp +++ b/src/nodes/QskScaleRenderer.cpp @@ -7,6 +7,7 @@ #include "QskScaleTickmarks.h" #include "QskSkinlet.h" #include "QskSGNode.h" +#include "QskGraduationMetrics.h" #include "QskTickmarksNode.h" #include "QskTextOptions.h" #include "QskTextColors.h" @@ -230,7 +231,7 @@ QSGNode* QskScaleRenderer::updateTicksNode( ticksNode->update( m_data->tickColor, rect, m_data->boundaries, m_data->tickmarks, tickWidth, m_data->orientation, - m_data->alignment ); + m_data->alignment, {}); return ticksNode; } diff --git a/src/nodes/QskTickmarksNode.cpp b/src/nodes/QskTickmarksNode.cpp index 11604d2cb..a67df9325 100644 --- a/src/nodes/QskTickmarksNode.cpp +++ b/src/nodes/QskTickmarksNode.cpp @@ -1,48 +1,17 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + #include "QskTickmarksNode.h" #include "QskScaleTickmarks.h" +#include "QskGraduationMetrics.h" -#include -#include -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -static constexpr inline qreal qskTickFactor( QskScaleTickmarks::TickType type ) -{ - using TM = QskScaleTickmarks; - return type == TM::MinorTick ? 0.7 : ( type == TM::MediumTick ? 0.85 : 1.0 ); -} - -class QskTickmarksNodePrivate final : public QSGGeometryNodePrivate -{ - public: - QskTickmarksNodePrivate() - : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) - { - geometry.setDrawingMode( QSGGeometry::DrawLines ); - } - - QSGGeometry geometry; - QSGFlatColorMaterial material; - - QskIntervalF boundaries; - QskScaleTickmarks tickmarks; - - QRectF rect; - int lineWidth = 0; - - QskHashValue hash = 0; -}; +#include +#include QskTickmarksNode::QskTickmarksNode() - : QSGGeometryNode( *new QskTickmarksNodePrivate ) { - Q_D( QskTickmarksNode ); - - setGeometry( &d->geometry ); - setMaterial( &d->material ); } QskTickmarksNode::~QskTickmarksNode() @@ -52,27 +21,24 @@ QskTickmarksNode::~QskTickmarksNode() void QskTickmarksNode::update( const QColor& color, const QRectF& rect, const QskIntervalF& boundaries, const QskScaleTickmarks& tickmarks, - int lineWidth, Qt::Orientation orientation, Qt::Alignment alignment ) + int lineWidth, Qt::Orientation orientation, Qt::Alignment alignment, + const QskGraduationMetrics& graduationMetrics ) { - Q_D( QskTickmarksNode ); + setLineWidth( lineWidth ); - if( lineWidth != d->lineWidth ) - { - d->lineWidth = lineWidth; - d->geometry.setLineWidth( lineWidth ); + auto hash = tickmarks.hash( 17435 ); + hash = graduationMetrics.hash( hash ); + hash = qHashBits( &boundaries, sizeof( boundaries ), hash ); + hash = qHashBits( &rect, sizeof( rect ), hash ); + hash = qHash( orientation, hash ); + hash = qHash( alignment, hash ); - markDirty( QSGNode::DirtyGeometry ); - } - - const auto hash = tickmarks.hash( 17435 ); - - if( ( hash != d->hash ) || ( rect != d->rect ) ) + if ( hash != m_hash ) { - d->hash = hash; - d->rect = rect; + m_hash = hash; - d->geometry.allocate( tickmarks.tickCount() * 2 ); - auto vertexData = d->geometry.vertexDataAsPoint2D(); + geometry()->allocate( tickmarks.tickCount() * 2 ); + auto vertexData = geometry()->vertexDataAsPoint2D(); const qreal min = boundaries.lowerBound(); const qreal range = boundaries.width(); @@ -82,12 +48,13 @@ void QskTickmarksNode::update( for( int i = TM::MinorTick; i <= TM::MajorTick; i++ ) { const auto tickType = static_cast< TM::TickType >( i ); + const auto ticks = tickmarks.ticks( tickType ); + const float len = graduationMetrics.tickLength( tickType ); if ( orientation == Qt::Horizontal ) { const qreal ratio = rect.width() / range; - const float len = rect.height() * qskTickFactor( tickType ); for( const auto tick : ticks ) { @@ -120,7 +87,6 @@ void QskTickmarksNode::update( else { const qreal ratio = rect.height() / range; - const float len = rect.width() * qskTickFactor( tickType ); for( const auto tick : ticks ) { @@ -152,13 +118,9 @@ void QskTickmarksNode::update( } } - d->geometry.markVertexDataDirty(); + geometry()->markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); } - if ( color != d->material.color() ) - { - d->material.setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } + setColor( color ); } diff --git a/src/nodes/QskTickmarksNode.h b/src/nodes/QskTickmarksNode.h index 7afdab7c1..40a927bc9 100644 --- a/src/nodes/QskTickmarksNode.h +++ b/src/nodes/QskTickmarksNode.h @@ -6,19 +6,14 @@ #ifndef QSK_TICKMARKS_NODE_H #define QSK_TICKMARKS_NODE_H -#include "QskGlobal.h" +#include "QskBasicLinesNode.h" -#include -#include - -class QColor; -class QRectF; class QskIntervalF; class QskScaleTickmarks; +class QskGraduationMetrics; +class QRectF; -class QskTickmarksNodePrivate; - -class QSK_EXPORT QskTickmarksNode : public QSGGeometryNode +class QSK_EXPORT QskTickmarksNode : public QskBasicLinesNode { public: QskTickmarksNode(); @@ -26,10 +21,10 @@ class QSK_EXPORT QskTickmarksNode : public QSGGeometryNode void update(const QColor&, const QRectF&, const QskIntervalF&, const QskScaleTickmarks&, int tickLineWidth, Qt::Orientation, - Qt::Alignment ); + Qt::Alignment, const QskGraduationMetrics& ); private: - Q_DECLARE_PRIVATE( QskTickmarksNode ) + QskHashValue m_hash = 0; }; #endif