From 9a9bc596ab732956bb4cc77c39d983e7231c2c41 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 12 Nov 2021 16:09:04 +0100 Subject: [PATCH] add tickmarks node --- examples/iotdashboard/LightDisplaySkinlet.cpp | 25 +++- examples/iotdashboard/Skin.cpp | 4 +- examples/iotdashboard/iotdashboard.pro | 6 +- .../nodes/RadialTickmarksNode.cpp | 129 ++++++++++++++++++ .../iotdashboard/nodes/RadialTickmarksNode.h | 27 ++++ 5 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 examples/iotdashboard/nodes/RadialTickmarksNode.cpp create mode 100644 examples/iotdashboard/nodes/RadialTickmarksNode.h diff --git a/examples/iotdashboard/LightDisplaySkinlet.cpp b/examples/iotdashboard/LightDisplaySkinlet.cpp index ecee7be7..ca551f23 100644 --- a/examples/iotdashboard/LightDisplaySkinlet.cpp +++ b/examples/iotdashboard/LightDisplaySkinlet.cpp @@ -7,6 +7,7 @@ #include "LightDisplay.h" #include "nodes/BoxShadowNode.h" +#include "nodes/RadialTickmarksNode.h" #include #include @@ -30,6 +31,7 @@ QRectF LightDisplaySkinlet::subControlRect( const QskSkinnable* skinnable, { auto* display = static_cast< const LightDisplay* >( skinnable ); QRectF rect = contentsRect; + const qreal ticksSpacing = 4; // space between the ticks and the arc if( subControl == LightDisplay::Groove || subControl == LightDisplay::Panel @@ -37,7 +39,7 @@ QRectF LightDisplaySkinlet::subControlRect( const QskSkinnable* skinnable, { QSizeF textSize = textLabelsSize( display ); QskArcMetrics arcMetrics = display->arcMetricsHint( LightDisplay::ColdAndWarmArc ); - const qreal ticksWidth = display->metric( LightDisplay::Tickmarks ); + const qreal ticksWidth = display->arcMetricsHint( LightDisplay::Tickmarks ).width() + ticksSpacing; const qreal x = textSize.width() + arcMetrics.width() + ticksWidth; const qreal w = contentsRect.width() - ( 2 * ( textSize.width() + arcMetrics.width() + ticksWidth ) ); @@ -61,8 +63,8 @@ QRectF LightDisplaySkinlet::subControlRect( const QskSkinnable* skinnable, { const QRectF arcRect = subControlRect( skinnable, contentsRect, LightDisplay::ColdAndWarmArc ); - auto tickWidth = display->metric( LightDisplay::Tickmarks ); - auto rect = arcRect.marginsAdded( { tickWidth, tickWidth, tickWidth, tickWidth } ); + const qreal ticksWidth = display->arcMetricsHint( LightDisplay::Tickmarks ).width() + ticksSpacing; + const QRectF rect = arcRect.marginsAdded( { ticksWidth, ticksWidth, ticksWidth, ticksWidth } ); return rect; } else if( subControl == LightDisplay::LeftLabel ) @@ -151,7 +153,22 @@ QSGNode* LightDisplaySkinlet::updateSubNode( } case TickmarksRole: { - return nullptr; + auto ticksNode = static_cast< RadialTickmarksNode* >( node ); + if ( ticksNode == nullptr ) + ticksNode = new RadialTickmarksNode(); + + QColor color = display->color( LightDisplay::Tickmarks ); + QRectF ticksRect = display->subControlRect( LightDisplay::Tickmarks ); + QskArcMetrics arcMetrics = display->arcMetricsHint( LightDisplay::Tickmarks ); + QskIntervalF boundaries = display->boundaries(); + QskScaleTickmarks tickmarks; + tickmarks.setMajorTicks( {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180 } ); + int tickLineWidth = display->metric( LightDisplay::Tickmarks ); + + ticksNode->update( color, ticksRect, arcMetrics, boundaries, + tickmarks, tickLineWidth, Qt::Horizontal ); + + return ticksNode; } case ValueTextRole: { diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 2546f110..8856a7af 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -192,7 +192,9 @@ void Skin::initHints( const Palette& palette ) { 1.0, Qt::black } } ); ed.setGradient( LightDisplay::ColdAndWarmArc, coldGradient ); - ed.setMetric( LightDisplay::Tickmarks, 4.69 ); + ed.setMetric( LightDisplay::Tickmarks, 1 ); + ed.setArcMetrics( LightDisplay::Tickmarks, { 4.69, 0, 180 * 16 } ); + ed.setColor( LightDisplay::Tickmarks, 0x55929CB2 ); ed.setFontRole( LightDisplay::ValueText, QskSkin::LargeFont ); ed.setColor( LightDisplay::ValueText, "#929cb2" ); diff --git a/examples/iotdashboard/iotdashboard.pro b/examples/iotdashboard/iotdashboard.pro index 5f99e0e3..1b6a87b7 100644 --- a/examples/iotdashboard/iotdashboard.pro +++ b/examples/iotdashboard/iotdashboard.pro @@ -29,9 +29,10 @@ SOURCES += \ main.cpp \ SOURCES += \ + nodes/BoxShadowNode.cpp \ nodes/DiagramDataNode.cpp \ nodes/DiagramSegmentsNode.cpp \ - nodes/BoxShadowNode.cpp + nodes/RadialTickmarksNode.cpp HEADERS += \ Box.h \ @@ -59,9 +60,10 @@ HEADERS += \ UsageDiagram.h HEADERS += \ + nodes/BoxShadowNode.h \ nodes/DiagramDataNode.h \ nodes/DiagramSegmentsNode.h \ - nodes/BoxShadowNode.h + nodes/RadialTickmarksNode.h RESOURCES += \ images.qrc \ diff --git a/examples/iotdashboard/nodes/RadialTickmarksNode.cpp b/examples/iotdashboard/nodes/RadialTickmarksNode.cpp new file mode 100644 index 00000000..35a78f40 --- /dev/null +++ b/examples/iotdashboard/nodes/RadialTickmarksNode.cpp @@ -0,0 +1,129 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "RadialTickmarksNode.h" + +#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::MinorTick ? 0.85 : 1.0 ); +} + +class RadialTickmarksNodePrivate final : public QSGGeometryNodePrivate +{ + public: + RadialTickmarksNodePrivate() + : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) + { +#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) + geometry.setDrawingMode( QSGGeometry::DrawLines ); +#else + geometry.setDrawingMode( GL_LINES ); +#endif + geometry.setVertexDataPattern( QSGGeometry::StaticPattern ); + } + + QSGGeometry geometry; + QSGFlatColorMaterial material; + + QskIntervalF boundaries; + QskScaleTickmarks tickmarks; + + QRectF rect; + int lineWidth = 0; + + uint hash = 0; +}; + +RadialTickmarksNode::RadialTickmarksNode() + : QSGGeometryNode( *new RadialTickmarksNodePrivate ) +{ + Q_D( RadialTickmarksNode ); + + setGeometry( &d->geometry ); + setMaterial( &d->material ); +} + +RadialTickmarksNode::~RadialTickmarksNode() +{ +} + +void RadialTickmarksNode::update(const QColor& color, const QRectF& rect, + const QskArcMetrics &arcMetrics, const QskIntervalF& boundaries, + const QskScaleTickmarks& tickmarks, int lineWidth, + Qt::Orientation /*orientation*/ ) +{ + Q_D( RadialTickmarksNode ); + + if( lineWidth != d->lineWidth ) + { + d->lineWidth = lineWidth; + d->geometry.setLineWidth( lineWidth ); + + markDirty( QSGNode::DirtyGeometry ); + } + + const uint hash = tickmarks.hash( 17435 ); + + if( ( hash != d->hash ) || ( rect != d->rect ) ) + { + d->hash = hash; + d->rect = rect; + + d->geometry.allocate( tickmarks.tickCount() * 2 ); + auto vertexData = d->geometry.vertexDataAsPoint2D(); + + const auto center = rect.center(); + const auto radius = 0.5 * rect.width(); + const auto needleRadius = radius - arcMetrics.width(); + + using TM = QskScaleTickmarks; + + for( int i = TM::MinorTick; i <= TM::MajorTick; i++ ) + { + const auto tickType = static_cast< TM::TickType >( i ); + const auto ticks = tickmarks.ticks( tickType ); + + const auto startAngle = arcMetrics.startAngle(); + const auto endAngle = startAngle + arcMetrics.spanAngle(); + + for( const auto tick : ticks ) + { + const qreal ratio = ( tick - startAngle ) / ( endAngle - startAngle ); + const qreal angle = ratio * ( endAngle - startAngle ); + + const qreal cos = qFastCos( qDegreesToRadians( angle ) ); + const qreal sin = qFastSin( qDegreesToRadians( angle ) ); + + const auto xStart = center.x() - radius * cos; + const auto yStart = center.y() - radius * sin; + + const auto xEnd = center.x() - needleRadius * cos; + const auto yEnd = center.y() - needleRadius * sin; + + vertexData[ 0 ].set( xStart, yStart ); + vertexData[ 1 ].set( xEnd, yEnd ); + + vertexData += 2; + } + } + + d->geometry.markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); + } + + if ( color != d->material.color() ) + { + d->material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } +} diff --git a/examples/iotdashboard/nodes/RadialTickmarksNode.h b/examples/iotdashboard/nodes/RadialTickmarksNode.h new file mode 100644 index 00000000..27fc82a9 --- /dev/null +++ b/examples/iotdashboard/nodes/RadialTickmarksNode.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +class RadialTickmarksNodePrivate; + +class RadialTickmarksNode : public QSGGeometryNode +{ + public: + RadialTickmarksNode(); + ~RadialTickmarksNode() override; + + void update( const QColor&, const QRectF&, const QskArcMetrics&, + const QskIntervalF&, const QskScaleTickmarks&, int, Qt::Orientation ); + + private: + Q_DECLARE_PRIVATE( RadialTickmarksNode ) +};