2023-07-10 13:54:53 +00:00
|
|
|
#pragma once
|
|
|
|
|
2023-07-20 14:12:54 +00:00
|
|
|
#include "QskLevelingSensorUtility.h"
|
|
|
|
|
2023-07-26 15:39:13 +00:00
|
|
|
#include <QskScaleTickmarks.h>
|
|
|
|
#include <QskAspect.h>
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
#include <QSGFlatColorMaterial>
|
2023-07-10 13:54:53 +00:00
|
|
|
#include <QSGGeometry>
|
|
|
|
#include <QSGGeometryNode>
|
|
|
|
|
|
|
|
#include <QFontMetricsF>
|
2023-07-20 14:12:54 +00:00
|
|
|
#include <qmath.h>
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-26 15:39:13 +00:00
|
|
|
class QskSkinnable;
|
|
|
|
|
2023-07-10 13:54:53 +00:00
|
|
|
class RadialTickmarksNode final : public QSGGeometryNode
|
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
RadialTickmarksNode()
|
|
|
|
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.setDrawingMode( QSGGeometry::DrawLines );
|
|
|
|
m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
setGeometry( &m_geometry );
|
|
|
|
setMaterial( &m_material );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void setMaterialProperties( const QColor& color )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( m_material.color() != color )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_material.setColor( color );
|
|
|
|
markDirty( QSGNode::DirtyMaterial );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void setGeometryProperties( const QskScaleTickmarks& tickmarks, const qreal r1 = 0.0,
|
|
|
|
const QVector3D& r2 = { 0.5, 0.75, 1.0 }, float lineWidth = 1.0 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
auto dirty = false;
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty |= ( m_geometry.lineWidth() != lineWidth ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.setLineWidth( lineWidth );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
dirty |= compareExchange( m_r1, r1 );
|
|
|
|
dirty |= compareExchange( m_r2, r2 );
|
|
|
|
dirty |= compareExchange( m_tickmarksHash, tickmarks.hash() );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
update( tickmarks );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
private:
|
|
|
|
void update( const QskScaleTickmarks& tickmarks )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( m_geometry.vertexCount() != tickmarks.tickCount() )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.allocate( tickmarks.tickCount() * 2 );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto* vertexData = m_geometry.vertexDataAsPoint2D();
|
|
|
|
|
|
|
|
using T = QskScaleTickmarks::TickType;
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( auto type : { T::MinorTick, T::MediumTick, T::MajorTick } )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( const auto tick : tickmarks.ticks( type ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto i = static_cast< int >( type );
|
|
|
|
const auto angleRad = qDegreesToRadians( tick );
|
|
|
|
const auto x1 = qFastCos( angleRad ) * m_r1;
|
|
|
|
const auto y1 = qFastSin( angleRad ) * m_r1;
|
|
|
|
const auto x2 = qFastCos( angleRad ) * m_r2[ i ];
|
|
|
|
const auto y2 = qFastSin( angleRad ) * m_r2[ i ];
|
|
|
|
|
|
|
|
vertexData[ 0 ].set( x1, y1 );
|
|
|
|
vertexData[ 1 ].set( x2, y2 );
|
2023-07-10 13:54:53 +00:00
|
|
|
vertexData += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_geometry.markVertexDataDirty();
|
2023-07-25 13:08:00 +00:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QSGGeometry m_geometry;
|
|
|
|
QSGFlatColorMaterial m_material;
|
|
|
|
qreal m_r1 = 0.0;
|
|
|
|
QVector3D m_r2 = { 0.5, 0.75, 1.0 };
|
|
|
|
QskHashValue m_tickmarksHash{ 0 };
|
|
|
|
};
|
|
|
|
|
|
|
|
class LinearTickmarksNode final : public QSGGeometryNode
|
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
LinearTickmarksNode()
|
|
|
|
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.setDrawingMode( QSGGeometry::DrawLines );
|
|
|
|
m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
setGeometry( &m_geometry );
|
|
|
|
setMaterial( &m_material );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void setMaterialProperties( const QColor& color )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
auto dirty = false;
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty |= ( m_material.color() != color ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_material.setColor( color );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
markDirty( QSGNode::DirtyMaterial );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void setGeometryProperties( const QskScaleTickmarks& tickmarks, const QVector3D& tickmarkSize,
|
|
|
|
const QVector2D& scale = { 1.0, 0.0f }, const QVector2D& offset = {},
|
|
|
|
const float lineWidth = 1.0f, const bool forceDirty = false )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
auto dirty = forceDirty;
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty |= !qFuzzyCompare( m_geometry.lineWidth(), lineWidth ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.setLineWidth( lineWidth );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dirty |= m_geometry.vertexCount() != tickmarks.tickCount() * 2;
|
2023-07-25 13:08:00 +00:00
|
|
|
dirty |= compareExchange( m_tickmarkSize, tickmarkSize );
|
|
|
|
dirty |= compareExchange( m_scale, scale );
|
|
|
|
dirty |= compareExchange( m_offset, offset );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
update( tickmarks );
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
private:
|
|
|
|
void update( const QskScaleTickmarks& tickmarks )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( m_geometry.vertexCount() != tickmarks.tickCount() * 2 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.allocate( tickmarks.tickCount() * 2 );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto* vertexData = m_geometry.vertexDataAsPoint2D();
|
|
|
|
|
|
|
|
using T = QskScaleTickmarks::TickType;
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( auto type : { T::MinorTick, T::MediumTick, T::MajorTick } )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( const auto tick : tickmarks.ticks( type ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto i = static_cast< int >( type );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
|
|
|
const auto p = m_scale * tick;
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto d = QVector2D( -m_scale.y(), m_scale.x() ).normalized();
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto p1 = m_tickmarkSize[ i ] * +1 * d + p + m_offset;
|
|
|
|
const auto p2 = m_tickmarkSize[ i ] * -1 * d + p + m_offset;
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
vertexData[ 0 ].set( p1.x(), p1.y() );
|
|
|
|
vertexData[ 1 ].set( p2.x(), p2.y() );
|
2023-07-10 13:54:53 +00:00
|
|
|
vertexData += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_geometry.markVertexDataDirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGGeometry m_geometry;
|
|
|
|
QSGFlatColorMaterial m_material;
|
|
|
|
QVector2D m_scale = { 1.0f, 0.0f };
|
|
|
|
QVector2D m_offset = { 0.0f, 0.0f };
|
|
|
|
QVector3D m_tickmarkSize = { 1.0, 2.0, 4.0 };
|
|
|
|
};
|
|
|
|
|
2023-07-24 07:39:32 +00:00
|
|
|
class RadialClipNode final : public QSGClipNode
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
RadialClipNode()
|
|
|
|
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.setVertexDataPattern( QSGGeometry::DynamicPattern );
|
|
|
|
m_geometry.setDrawingMode( QSGGeometry::DrawTriangleFan );
|
|
|
|
setGeometry( &m_geometry );
|
|
|
|
setIsRectangular( false );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void setGeometryProperties( const qreal radius = 1.0, const qreal cx = 0.0,
|
|
|
|
const qreal cy = 0.0, const int count = 360 )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
auto dirty = false;
|
2023-07-25 13:08:00 +00:00
|
|
|
dirty |= compareExchange( m_radius, radius );
|
|
|
|
dirty |= compareExchange( m_cx, cx );
|
|
|
|
dirty |= compareExchange( m_cy, cy );
|
|
|
|
dirty |= compareExchange( m_count, count );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( dirty )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
private:
|
2023-07-10 13:54:53 +00:00
|
|
|
void update()
|
|
|
|
{
|
|
|
|
const auto step = 2.0 * M_PI / m_count;
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
if ( m_geometry.vertexCount() != m_count )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
m_geometry.allocate( m_count );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto* vertices = m_geometry.vertexDataAsPoint2D();
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( int i = 0; i < m_count; ++i )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
vertices[ i ].x = qFastCos( i * step ) * m_radius + m_cx;
|
|
|
|
vertices[ i ].y = qFastSin( i * step ) * m_radius + m_cy;
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_geometry.markVertexDataDirty();
|
2023-07-25 13:08:00 +00:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
using QSGClipNode::setClipRect;
|
2023-07-25 13:08:00 +00:00
|
|
|
using QSGClipNode::setIsRectangular;
|
2023-07-10 13:54:53 +00:00
|
|
|
|
|
|
|
QSGGeometry m_geometry;
|
|
|
|
qreal m_radius = 1.0;
|
|
|
|
qreal m_cx = 0;
|
|
|
|
qreal m_cy = 0;
|
|
|
|
int m_count = 360;
|
|
|
|
};
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
template< typename CRTP >
|
2023-07-10 13:54:53 +00:00
|
|
|
struct TickmarksLabelsNode : public QSGNode
|
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
return static_cast< const CRTP* >( this )->value( v, s, o );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
void update( const QskSkinnable* const skinnable, const QskAspect::Subcontrol subControl,
|
|
|
|
const QVector< QPair< double, QString > >& labels, const QVector2D& scale = { 1.0, 0.0 },
|
|
|
|
const QVector2D& offset = {} )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-24 08:08:53 +00:00
|
|
|
const auto count = labels.count();
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( int i = childCount(); i > count; --i )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
removeChildNode( lastChild() );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
for ( int i = childCount(); i < count; ++i )
|
2023-07-24 08:08:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
appendChildNode( new QskTextNode );
|
|
|
|
}
|
2023-07-24 08:08:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
const QFontMetricsF metrics( skinnable->effectiveFont( subControl ) );
|
|
|
|
const auto h = skinnable->effectiveFontHeight( subControl );
|
|
|
|
const auto a = skinnable->alignmentHint( subControl );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
auto* textNode = static_cast< QskTextNode* >( firstChild() );
|
|
|
|
for ( const auto& label : qAsConst( labels ) )
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto v = value( { ( float ) label.first, ( float ) label.first }, scale, offset );
|
2023-07-10 13:54:53 +00:00
|
|
|
auto x = v.x();
|
|
|
|
auto y = v.y();
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
const auto w = metrics.horizontalAdvance( label.second );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
x -= a.testFlag( Qt::AlignRight ) ? w : 0;
|
|
|
|
x -= a.testFlag( Qt::AlignHCenter ) ? w / 2 : 0;
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
y -= a.testFlag( Qt::AlignBottom ) ? h : 0;
|
|
|
|
y -= a.testFlag( Qt::AlignVCenter ) ? h / 2 : 0;
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
QskSkinlet::updateTextNode(
|
|
|
|
skinnable, textNode, { x, y, w, h }, a, label.second, subControl );
|
2023-07-10 13:54:53 +00:00
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
textNode = static_cast< QskTextNode* >( textNode->nextSibling() );
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
struct LinearTickmarksLabelsNode final : public TickmarksLabelsNode< LinearTickmarksLabelsNode >
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
return v * s + o;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-07-25 13:08:00 +00:00
|
|
|
struct RadialTickmarksLabelsNode final : public TickmarksLabelsNode< RadialTickmarksLabelsNode >
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
2023-07-25 13:08:00 +00:00
|
|
|
public:
|
|
|
|
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
|
2023-07-10 13:54:53 +00:00
|
|
|
{
|
|
|
|
return QVector2D{
|
2023-07-25 13:08:00 +00:00
|
|
|
( float ) qFastCos( qDegreesToRadians( v.x() ) ),
|
|
|
|
( float ) qFastSin( qDegreesToRadians( v.y() ) )
|
|
|
|
} * s + o;
|
2023-07-10 13:54:53 +00:00
|
|
|
}
|
|
|
|
};
|