This commit is contained in:
Rick Vogel 2023-11-21 03:58:51 -07:00 committed by GitHub
commit 6efb914bfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1538 additions and 1 deletions

View File

@ -6,6 +6,7 @@ add_subdirectory(invoker)
add_subdirectory(shadows)
add_subdirectory(shapes)
add_subdirectory(charts)
add_subdirectory(levelingsensor)
if (BUILD_INPUTCONTEXT)
add_subdirectory(inputpanel)

View File

@ -0,0 +1,11 @@
############################################################################
# QSkinny - Copyright (C) 2016 Uwe Rathmann
# SPDX-License-Identifier: BSD-3-Clause
############################################################################
set(SOURCES
SkinFactory.h SkinFactory.cpp
main.cpp
)
qsk_add_example(levelingsensor ${SOURCES})

View File

@ -0,0 +1,170 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "SkinFactory.h"
#include <QskBoxBorderColors.h>
#include <QskGradient.h>
#include <QskGradientStop.h>
#include <QskPlatform.h>
#include <QskRgbValue.h>
#include <QskSkin.h>
#include <QskSkinHintTableEditor.h>
#include <QskTextLabel.h>
#include <QskLevelingSensor.h>
#include <QskLevelingSensorSkinlet.h>
#include <QskSlider.h>
#include <QskSliderSkinlet.h>
#include <QskTextLabel.h>
#include <QskTextLabelSkinlet.h>
namespace
{
class Skin : public QskSkin
{
template< typename T >
void style( QskSkinHintTableEditor& editor );
template< typename Skinnable, typename Skinlet >
void declareSkinlet()
{
QskSkin::declareSkinlet< Skinnable, Skinlet >();
}
template< typename Skinnable, typename Skinlet >
void declareSkinlet( QskSkinHintTableEditor& editor )
{
QskSkin::declareSkinlet< Skinnable, Skinlet >();
style< Skinnable >( editor );
}
public:
Skin()
{
QskSkinHintTableEditor editor( &hintTable() );
declareSkinlet< QskSlider, QskSliderSkinlet >( editor );
declareSkinlet< QskTextLabel, QskTextLabelSkinlet >();
declareSkinlet< QskLevelingSensor, QskLevelingSensorSkinlet >( editor );
}
};
template<>
void Skin::style< QskSlider >( QskSkinHintTableEditor& editor )
{
using A = QskAspect;
using Q = QskSlider;
const qreal extent = 40;
// Panel
for ( auto variation : { A::Horizontal, A::Vertical } )
{
const auto aspect = Q::Panel | variation;
editor.setMetric( aspect | A::Size, extent );
editor.setBoxBorderMetrics( aspect, 0 );
editor.setBoxShape( aspect, 0 );
editor.setGradient( aspect, QskGradient() );
}
// Groove, Fill
for ( auto variation : { A::Horizontal, A::Vertical } )
{
for ( auto subControl : { Q::Groove, Q::Fill } )
{
const auto aspect = subControl | variation;
editor.setMetric( aspect | A::Size, 0.3 * extent );
editor.setBoxBorderMetrics( aspect, 0 );
editor.setBoxShape( aspect, 0.1 * extent );
}
editor.setGradient( Q::Groove | variation, Qt::lightGray );
editor.setGradient( Q::Fill | variation, Qt::darkGray );
}
// Handle
for ( auto variation : { A::Horizontal, A::Vertical } )
{
const auto aspect = Q::Handle | variation;
editor.setColor( aspect, Qt::black );
editor.setBoxShape( aspect, 20.0, Qt::RelativeSize );
const qreal sz = 0.75 * extent;
editor.setStrutSize( aspect, sz, sz );
}
}
template<>
void Skin::style< QskLevelingSensor >( QskSkinHintTableEditor& editor )
{
using Q = QskLevelingSensor;
static constexpr auto r1 = 0.9;
static constexpr auto r2 = 1.0;
QskGradient gradient{ {
{ 0.5, Qt::lightGray },
{ 0.5, Qt::lightGray },
{ 0.5, Qt::darkGray },
{ 1.0, Qt::darkGray },
} };
gradient.setLinearDirection( Qt::Vertical );
editor.setColor( Q::Background, "dimgray" );
editor.setStrutSize( Q::OuterDisk, { r2, r2 } );
editor.setColor( Q::OuterDisk, Qt::white );
editor.setGradient( Q::Horizon, gradient );
editor.setStrutSize( Q::Horizon, { r1, r1 } );
editor.setColor( Q::TickmarksX, Qt::black );
editor.setStrutSize( Q::TickmarksX, { r1, 0.2 } ); // w %, h %
editor.setHint( Q::TickmarksX, QVector3D{ 0.50, 0.75, 1.0 } ); // %
editor.setAlignment( Q::TickmarksX, Qt::AlignCenter );
editor.setStrutSize( Q::TickmarksXLabels, { r1, 0.15 } ); // w %, h %
editor.setAlignment( Q::TickmarksXLabels, Qt::AlignTop | Qt::AlignHCenter );
editor.setColor( Q::TickmarksY, Qt::black );
editor.setStrutSize( Q::TickmarksY, { 0.1, r1 } ); // w %, h %
editor.setHint( Q::TickmarksY, QVector3D{ 0.50, 0.75, 1.00 } ); // %
editor.setAlignment( Q::TickmarksY, Qt::AlignCenter );
editor.setStrutSize( Q::TickmarksYLabels, { 0.15, r1 } ); // w %, h %
editor.setAlignment( Q::TickmarksYLabels, Qt::AlignCenter );
editor.setColor( Q::TickmarksZ, "silver" );
editor.setStrutSize( Q::TickmarksZ, { 0.90, 0.95 } );
editor.setHint( Q::TickmarksZ, QVector3D{ 0.50, 0.75, 1.00 } ); // %
editor.setStrutSize( Q::TickmarksZLabels, { 0.9, 0.0 } ); // r1 %, r2 %
editor.setAlignment( Q::TickmarksZLabels, Qt::AlignCenter );
}
}
QStringList SkinFactory::skinNames() const
{
return { "Skin" };
}
QskSkin* SkinFactory::createSkin( const QString& skinName )
{
if ( skinName == "Skin" )
return new Skin();
return nullptr;
}
#include "moc_SkinFactory.cpp"

View File

@ -0,0 +1,17 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#pragma once
#include <QskSkinFactory.h>
class SkinFactory : public QskSkinFactory
{
Q_OBJECT
public:
QStringList skinNames() const override;
QskSkin* createSkin( const QString& ) override;
};

View File

@ -0,0 +1,213 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "SkinFactory.h"
#include <SkinnyShortcut.h>
#include <QskObjectCounter.h>
#include <QskSkinManager.h>
#include <QskWindow.h>
#include <QskIntervalF.h>
#include <QskLevelingSensor.h>
#include <QskLinearBox.h>
#include <QskScaleTickmarks.h>
#include <QskSlider.h>
#include <QskTextLabel.h>
#include <QGuiApplication>
#include <QQuickWindow>
namespace
{
void updateTickmarks( const Qt::Axis axis, const QskIntervalF& intervalA,
const QskIntervalF& intervalB, QskLevelingSensor* const sensor )
{
QskScaleTickmarks tickmarks;
QVector< qreal > major;
QVector< qreal > medium;
QVector< qreal > minor;
for ( const auto& interval : { intervalA, intervalB } )
{
for ( int deg = ( int ) interval.lowerBound(); deg < ( int ) interval.upperBound();
++deg )
{
if ( deg % 45 == 0 )
{
major << deg;
}
if ( deg % 45 != 0 && deg % 5 == 0 )
{
medium << deg;
}
if ( deg % 45 != 0 && deg % 5 != 0 )
{
minor << deg;
}
}
}
tickmarks.setMajorTicks( major );
tickmarks.setMediumTicks( medium );
tickmarks.setMinorTicks( minor );
sensor->setTickmarks( axis, tickmarks );
}
void updateTickmarksLabels( const Qt::Axis axis, const QskIntervalF& intervalA,
const QskIntervalF& intervalB, int step, QskLevelingSensor* const sensor )
{
QskLevelingSensor::TickmarksLabels labels;
for ( const auto& interval : { intervalA, intervalB } )
{
for ( int deg = ( ( int ) interval.lowerBound() / step ) * step;
deg < ( int ) interval.upperBound(); deg += step )
{
labels << QskLevelingSensor::TickmarksLabels::value_type{ ( float ) deg,
QString( deg > 0 ? "+" : "" ) + QString::number( deg ) };
}
}
sensor->setTickmarksLabels( axis, labels );
}
Q_REQUIRED_RESULT QskSlider* makeAngleSlider( const Qt::Axis axis,
QskLevelingSensor* const sensor, double min = 0, double max = 90, QQuickItem* const parent = nullptr)
{
auto* const slider = new QskSlider( Qt::Horizontal, parent );
slider->setMinimum( min );
slider->setMaximum( max );
slider->setValue( sensor->angles()[ axis ] );
QObject::connect(slider, &QskSlider::valueChanged, sensor, [axis, sensor](const qreal v){
auto angles = sensor->angles();
angles[axis] = v;
sensor->setAngle(angles);
});
return slider;
}
Q_REQUIRED_RESULT QskSlider* makeTickmarksSlider( const Qt::Axis axis,
QskLevelingSensor* const sensor, int min, int max,
std::function< QskIntervalF( qreal ) > intervalA,
std::function< QskIntervalF( qreal ) > intervalB, QQuickItem* const parent = nullptr )
{
auto* const slider = new QskSlider( Qt::Horizontal, parent );
slider->setMinimum( min );
slider->setMaximum( max );
QObject::connect( slider, &QskSlider::valueChanged, sensor, [ = ]( const qreal degree ) {
updateTickmarks( axis, intervalA( degree ), intervalB( degree ), sensor );
updateTickmarksLabels( axis, intervalA( degree ), intervalB( degree ), 10, sensor );
} );
return slider;
}
Q_REQUIRED_RESULT QskSlider* makeRotationSlider( const Qt::Axis axis,
QskLevelingSensor* const sensor, const QskAspect::Subcontrol subControl,
QQuickItem* const parent = nullptr )
{
auto* const slider = new QskSlider( Qt::Horizontal, parent );
slider->setMinimum( -360 );
slider->setMaximum( +360 );
QObject::connect( sensor, &QskLevelingSensor::subControlRotationChanged, slider,
[ = ]( const QskAspect::Subcontrol control, const QVector3D& degree ) {
if ( control == subControl )
{
slider->setValue( degree[ axis ] );
}
} );
QObject::connect( slider, &QskSlider::valueChanged, sensor, [ = ]( const qreal degree ) {
auto d = sensor->subControlRotation( subControl );
d[ axis ] = degree;
sensor->setSubControlRotation( subControl, d );
} );
return slider;
}
class Window : public QskWindow
{
public:
Window()
{
auto* const root = new QskLinearBox( Qt::Horizontal, contentItem() );
root->setSpacing( 8 );
root->setMargins( 8 );
auto* const left = new QskLinearBox( Qt::Vertical, root );
auto* const right = new QskLinearBox( Qt::Vertical, root );
auto* const sensor = new QskLevelingSensor( left );
auto linearIntervalA = []( const qreal degree ) -> QskIntervalF {
return { -degree, +degree };
};
auto linearIntervalB = []( const qreal /*degree*/ ) -> QskIntervalF { return {}; };
auto radialIntervalA = []( const qreal degree ) -> QskIntervalF {
return { -degree, +degree };
};
auto radialIntervalB = []( const qreal degree ) -> QskIntervalF {
return { 180 - degree, 180 + degree };
};
( void ) new QskTextLabel( "Angles XYZ", right );
(void) makeAngleSlider(Qt::XAxis, sensor, 0, 45, right);
(void) makeAngleSlider(Qt::YAxis, sensor, 0, 45, right);
(void) makeAngleSlider(Qt::ZAxis, sensor, 0, 45, right);
( void ) new QskTextLabel( "Tickmarks XXZ", right );
auto* const sliderTickmarksX = makeTickmarksSlider(
Qt::XAxis, sensor, 0, 90, linearIntervalA, linearIntervalB, right );
auto* const sliderTickmarksY = makeTickmarksSlider(
Qt::YAxis, sensor, 0, 90, linearIntervalA, linearIntervalB, right );
auto* const sliderTickmarksZ = makeTickmarksSlider(
Qt::ZAxis, sensor, 0, 90, radialIntervalA, radialIntervalB, right );
( void ) new QskTextLabel( "Rotation X Plane", right );
( void ) makeRotationSlider( Qt::XAxis, sensor, QskLevelingSensor::TickmarksX, right );
( void ) makeRotationSlider( Qt::YAxis, sensor, QskLevelingSensor::TickmarksX, right );
( void ) makeRotationSlider( Qt::ZAxis, sensor, QskLevelingSensor::TickmarksX, right );
( void ) new QskTextLabel( "Rotation Y Plane", right );
( void ) makeRotationSlider( Qt::XAxis, sensor, QskLevelingSensor::TickmarksY, right );
( void ) makeRotationSlider( Qt::YAxis, sensor, QskLevelingSensor::TickmarksY, right );
( void ) makeRotationSlider( Qt::ZAxis, sensor, QskLevelingSensor::TickmarksY, right );
( void ) new QskTextLabel( "Rotation Z Plane", right );
( void ) makeRotationSlider( Qt::XAxis, sensor, QskLevelingSensor::TickmarksZ, right );
( void ) makeRotationSlider( Qt::YAxis, sensor, QskLevelingSensor::TickmarksZ, right );
( void ) makeRotationSlider( Qt::ZAxis, sensor, QskLevelingSensor::TickmarksZ, right );
( void ) new QskTextLabel( "Horizon", right );
( void ) makeRotationSlider( Qt::XAxis, sensor, QskLevelingSensor::Horizon, right );
( void ) makeRotationSlider( Qt::YAxis, sensor, QskLevelingSensor::Horizon, right );
( void ) makeRotationSlider( Qt::ZAxis, sensor, QskLevelingSensor::Horizon, right );
sliderTickmarksX->setValue( 15 );
sliderTickmarksY->setValue( 15 );
sliderTickmarksZ->setValue( 30 );
}
};
}
int main( int argc, char** argv )
{
#ifdef ITEM_STATISTICS
QskObjectCounter counter( true );
#endif
qskSkinManager->setPluginPaths( QStringList() ); // no skin plugins
qskSkinManager->registerFactory( QStringLiteral( "sample" ), new SkinFactory() );
QGuiApplication app( argc, argv );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
Window window;
window.showMaximized();
return app.exec();
}

View File

@ -440,6 +440,22 @@ list(APPEND SOURCES
inputpanel/QskVirtualKeyboard.cpp
)
list(APPEND HEADERS
controls/QskLevelingSensor.h
controls/QskLevelingSensorSkinlet.h
)
list(APPEND PRIVATE_HEADERS
controls/private/QskSGNodeUtility.h
controls/private/QskLevelingSensorNodes.h
controls/private/QskLevelingSensorUtility.h
)
list(APPEND SOURCES
controls/private/QskLevelingSensor.cpp
controls/private/QskLevelingSensorSkinlet.cpp
)
if(ENABLE_PINYIN)
list(APPEND HEADERS inputpanel/QskPinyinTextPredictor.h)
list(APPEND SOURCES inputpanel/QskPinyinTextPredictor.cpp)
@ -499,6 +515,7 @@ set_target_properties(${target}
list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_LIST_DIR}/")
set_target_properties(${target} PROPERTIES PUBLIC_HEADER "${HEADERS}")
set_target_properties(${target} PROPERTIES
VERSION ${CMAKE_PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} )

View File

@ -0,0 +1,49 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_LEVELING_SENSOR_H
#define QSK_LEVELING_SENSOR_H
#include <QskControl.h>
#include <QskScaleTickmarks.h>
#include <memory>
class QSK_EXPORT QskLevelingSensor : public QskControl
{
Q_OBJECT
using Inherited = QskControl;
public:
QSK_SUBCONTROLS( OuterDisk, Horizon, TickmarksX, TickmarksXLabels, TickmarksY, TickmarksYLabels,
TickmarksZ, TickmarksZLabels )
using Tickmarks = QskScaleTickmarks;
using TickmarksLabels = QVector< QPair< qreal, QString > >;
explicit QskLevelingSensor( QQuickItem* parent = nullptr );
~QskLevelingSensor();
public Q_SLOTS:
void setTickmarks( Qt::Axis axis, Tickmarks tickmarks );
void setTickmarksLabels( Qt::Axis axis, TickmarksLabels labels );
void setAngle( const QVector3D& degrees );
void setSubControlRotation( QskAspect::Subcontrol subControl, const QVector3D& degrees );
Q_SIGNALS:
void anglesChanged( const QVector3D& degree );
void subControlRotationChanged( QskAspect::Subcontrol subControl, const QVector3D& degrees );
public:
Q_REQUIRED_RESULT Tickmarks tickmarks( Qt::Axis axis ) const;
Q_REQUIRED_RESULT TickmarksLabels tickmarkLabels( Qt::Axis axis ) const;
Q_REQUIRED_RESULT QVector3D angles() const;
Q_REQUIRED_RESULT QVector3D subControlRotation( QskAspect::Subcontrol subControl ) const;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -0,0 +1,60 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_LEVELING_SENSOR_SKINLET_H
#define QSK_LEVELING_SENSOR_SKINLET_H
#include <QSGNode>
#include <QskSkinlet.h>
class QskLevelingSensor;
class QSK_EXPORT QskLevelingSensorSkinlet : public QskSkinlet
{
Q_GADGET
using Inherited = QskSkinlet;
public:
enum NodeRole
{
OuterDisk,
Horizon,
HorizonClip,
TickmarksX,
TickmarksXLabels,
TickmarksY,
TickmarksYLabels,
TickmarksZ,
TickmarksZLabels,
TriangleBar,
TickmarksYIndicator,
RoleCount
};
Q_INVOKABLE QskLevelingSensorSkinlet( QskSkin* skin = nullptr );
~QskLevelingSensorSkinlet() override = default;
Q_REQUIRED_RESULT static float outerRadius( const QskSkinnable* const skinnable );
Q_REQUIRED_RESULT static float innerRadius( const QskSkinnable* const skinnable );
Q_REQUIRED_RESULT static QPointF center( const QskSkinnable* const skinnable );
protected:
Q_REQUIRED_RESULT QRectF subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const override;
Q_REQUIRED_RESULT QSGNode* updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override;
template< NodeRole >
Q_REQUIRED_RESULT QRectF subControlRect(
const QskLevelingSensor* sensor, const QRectF& contentsRect ) const;
template< NodeRole >
Q_REQUIRED_RESULT QSGNode* updateSubNode(
const QskLevelingSensor* sensor, quint8 nodeRole, QSGNode* node ) const;
};
#endif

View File

@ -0,0 +1,141 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include <QskFunctions.h>
#include <QskLevelingSensor.h>
#include <QskScaleTickmarks.h>
#include <type_traits>
#include <unordered_map>
#include <QVector2D>
#include <QVector3D>
namespace
{
template< typename T >
bool compareExchange( T& dst, const T& src )
{
if ( dst != src )
{
dst = src;
return true;
}
return false;
}
template<>
bool compareExchange< float >( float& dst, const float& src )
{
if ( !qskFuzzyCompare( dst, src ) )
{
dst = src;
return true;
}
return false;
}
template<>
bool compareExchange< QVector3D >( QVector3D& dst, const QVector3D& src )
{
auto dirty = false;
dirty |= compareExchange( dst[ Qt::XAxis ], src[ Qt::XAxis ] );
dirty |= compareExchange( dst[ Qt::YAxis ], src[ Qt::YAxis ] );
dirty |= compareExchange( dst[ Qt::ZAxis ], src[ Qt::ZAxis ] );
return dirty;
}
inline bool isAxis( const Qt::Axis axis )
{
return axis == Qt::XAxis || axis == Qt::YAxis || axis == Qt::ZAxis;
}
}
QSK_SUBCONTROL( QskLevelingSensor, OuterDisk )
QSK_SUBCONTROL( QskLevelingSensor, Horizon )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksX )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksXLabels )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksY )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksYLabels )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksZ )
QSK_SUBCONTROL( QskLevelingSensor, TickmarksZLabels )
#define RETURN_IF_FALSE( expr ) \
if ( !( expr ) ) \
return;
class QskLevelingSensor::PrivateData
{
public:
QVector3D m_angles = { 45, 45, 45 };
Tickmarks m_tickmarks[ 3 ];
TickmarksLabels m_tickmarksLabels[ 3 ];
std::unordered_map< QskAspect::Subcontrol, QVector3D > m_subControlRotation;
};
QskLevelingSensor::QskLevelingSensor( QQuickItem* const parent )
: Inherited( parent )
, m_data( new QskLevelingSensor::PrivateData )
{
}
QskLevelingSensor::~QskLevelingSensor() = default;
void QskLevelingSensor::setTickmarks( const Qt::Axis axis, QskScaleTickmarks tickmarks )
{
RETURN_IF_FALSE( isAxis( axis ) );
m_data->m_tickmarks[ axis ] = std::move( tickmarks );
update();
}
void QskLevelingSensor::setTickmarksLabels( const Qt::Axis axis, TickmarksLabels labels )
{
RETURN_IF_FALSE( isAxis( axis ) );
m_data->m_tickmarksLabels[ axis ] = std::move( labels );
update();
}
void QskLevelingSensor::setAngle( const QVector3D& degrees )
{
if ( compareExchange( m_data->m_angles, degrees ) )
{
update();
Q_EMIT anglesChanged( m_data->m_angles );
}
}
QskScaleTickmarks QskLevelingSensor::tickmarks( const Qt::Axis axis ) const
{
return isAxis( axis ) ? m_data->m_tickmarks[ axis ] : QskScaleTickmarks{};
}
QskLevelingSensor::TickmarksLabels QskLevelingSensor::tickmarkLabels( const Qt::Axis axis ) const
{
return isAxis( axis ) ? m_data->m_tickmarksLabels[ axis ]
: QskLevelingSensor::TickmarksLabels{};
}
QVector3D QskLevelingSensor::angles() const
{
return m_data->m_angles;
}
QVector3D QskLevelingSensor::subControlRotation( const QskAspect::Subcontrol subControl ) const
{
const auto found = m_data->m_subControlRotation.find( subControl );
return found != m_data->m_subControlRotation.end() ? found->second : QVector3D{};
}
void QskLevelingSensor::setSubControlRotation(
const QskAspect::Subcontrol subControl, const QVector3D& degrees )
{
if ( compareExchange( m_data->m_subControlRotation[ subControl ], degrees ) )
{
update();
Q_EMIT subControlRotationChanged( subControl, degrees );
}
}
#include "moc_QskLevelingSensor.cpp"

View File

@ -0,0 +1,238 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_LEVELING_SENSOR_NODES_H
#define QSK_LEVELING_SENSOR_NODES_H
#include "QskLevelingSensorUtility.h"
#include <QskAspect.h>
#include <QskScaleTickmarks.h>
#include <QskSkinlet.h>
#include <QskSkinnable.h>
#include <QskTextNode.h>
#include <QskTextOptions.h>
#include <QSGFlatColorMaterial>
#include <QSGGeometry>
#include <QSGGeometryNode>
#include <QFontMetricsF>
#include <qmath.h>
class RadialTickmarksNode final : public QSGGeometryNode
{
public:
RadialTickmarksNode()
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
{
m_geometry.setDrawingMode( QSGGeometry::DrawLines );
m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
setGeometry( &m_geometry );
setMaterial( &m_material );
}
void setMaterialProperties( const QColor& color )
{
if ( m_material.color() != color )
{
m_material.setColor( color );
markDirty( QSGNode::DirtyMaterial );
}
}
void setGeometryProperties( const QskScaleTickmarks& tickmarks, const qreal r1 = 0.0,
const QVector3D& r2 = { 0.5, 0.75, 1.0 }, float lineWidth = 1.0 )
{
auto dirty = false;
if ( dirty |= ( m_geometry.lineWidth() != lineWidth ) )
{
m_geometry.setLineWidth( lineWidth );
}
dirty |= compareExchange( m_r1, r1 );
dirty |= compareExchange( m_r2, r2 );
dirty |= compareExchange( m_tickmarksHash, tickmarks.hash() );
if ( dirty )
{
update( tickmarks );
}
}
private:
void update( const QskScaleTickmarks& tickmarks )
{
if ( m_geometry.vertexCount() != tickmarks.tickCount() )
{
m_geometry.allocate( tickmarks.tickCount() * 2 );
}
auto* vertexData = m_geometry.vertexDataAsPoint2D();
using T = QskScaleTickmarks::TickType;
for ( auto type : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
for ( const auto tick : tickmarks.ticks( type ) )
{
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 );
vertexData += 2;
}
}
m_geometry.markVertexDataDirty();
markDirty( QSGNode::DirtyGeometry );
}
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 RadialClipNode final : public QSGClipNode
{
public:
RadialClipNode()
: m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
{
m_geometry.setVertexDataPattern( QSGGeometry::DynamicPattern );
m_geometry.setDrawingMode( QSGGeometry::DrawTriangleFan );
setGeometry( &m_geometry );
setIsRectangular( false );
}
void setGeometryProperties( const qreal radius = 1.0, const qreal cx = 0.0,
const qreal cy = 0.0, const int count = 360 )
{
auto dirty = false;
dirty |= compareExchange( m_radius, radius );
dirty |= compareExchange( m_cx, cx );
dirty |= compareExchange( m_cy, cy );
dirty |= compareExchange( m_count, count );
if ( dirty )
{
update();
}
}
private:
void update()
{
const auto step = 2.0 * M_PI / m_count;
if ( m_geometry.vertexCount() != m_count )
{
m_geometry.allocate( m_count );
}
auto* vertices = m_geometry.vertexDataAsPoint2D();
for ( int i = 0; i < m_count; ++i )
{
vertices[ i ].x = qFastCos( i * step ) * m_radius + m_cx;
vertices[ i ].y = qFastSin( i * step ) * m_radius + m_cy;
}
m_geometry.markVertexDataDirty();
markDirty( QSGNode::DirtyGeometry );
}
using QSGClipNode::setClipRect;
using QSGClipNode::setIsRectangular;
QSGGeometry m_geometry;
qreal m_radius = 1.0;
qreal m_cx = 0;
qreal m_cy = 0;
int m_count = 360;
};
template< typename CRTP >
struct TickmarksLabelsNode : public QSGNode
{
public:
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
{
return static_cast< const CRTP* >( this )->value( v, s, o );
}
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 = {} )
{
const auto count = labels.count();
for ( int i = childCount(); i > count; --i )
{
removeChildNode( lastChild() );
}
for ( int i = childCount(); i < count; ++i )
{
appendChildNode( new QskTextNode );
}
const QFontMetricsF metrics( skinnable->effectiveFont( subControl ) );
const auto h = skinnable->effectiveFontHeight( subControl );
const auto a = skinnable->alignmentHint( subControl );
auto* textNode = static_cast< QskTextNode* >( firstChild() );
for ( const auto& label : qAsConst( labels ) )
{
const auto v = value( { ( float ) label.first, ( float ) label.first }, scale, offset );
auto x = v.x();
auto y = v.y();
const auto w = metrics.horizontalAdvance( label.second );
x -= a.testFlag( Qt::AlignRight ) ? w : 0;
x -= a.testFlag( Qt::AlignHCenter ) ? w / 2 : 0;
y -= a.testFlag( Qt::AlignBottom ) ? h : 0;
y -= a.testFlag( Qt::AlignVCenter ) ? h / 2 : 0;
QskSkinlet::updateTextNode(
skinnable, textNode, { x, y, w, h }, a, label.second, subControl );
textNode = static_cast< QskTextNode* >( textNode->nextSibling() );
}
}
};
struct LinearTickmarksLabelsNode final : public TickmarksLabelsNode< LinearTickmarksLabelsNode >
{
public:
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
{
return v * s + o;
}
};
struct RadialTickmarksLabelsNode final : public TickmarksLabelsNode< RadialTickmarksLabelsNode >
{
public:
QVector2D value( const QVector2D& v, const QVector2D& s, const QVector2D& o ) const
{
return QVector2D{
( float ) qFastCos( qDegreesToRadians( v.x() ) ),
( float ) qFastSin( qDegreesToRadians( v.y() ) )
} * s + o;
}
};
#endif

View File

@ -0,0 +1,492 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskLevelingSensorNodes.h"
#include "QskLevelingSensorUtility.h"
#include "QskSGNodeUtility.h"
#include <QskLevelingSensor.h>
#include <QskLevelingSensorSkinlet.h>
#include <QskArcMetrics.h>
#include <QskBoxBorderColors.h>
#include <QskBoxBorderMetrics.h>
#include <QskBoxNode.h>
#include <QskBoxShapeMetrics.h>
#include <QskGradient.h>
#include <QskGraphic.h>
#include <QskScaleEngine.h>
#include <QskScaleTickmarks.h>
#include <QskTextColors.h>
#include <QskTextNode.h>
#include <QskTextOptions.h>
#include <QskTickmarksNode.h>
#include <QFontMetrics>
#include <QSGClipNode>
#include <QSGFlatColorMaterial>
#include <QSGGeometryNode>
#include <qmath.h>
#include <array>
#include <optional>
#include <utility>
namespace
{
template< typename T >
struct State;
template<>
struct State< QskLevelingSensor >
{
qreal r1 = 0.0;
qreal r2 = 0.0;
qreal cX = 0.0;
qreal cY = 0.0;
qreal sX = 0.0;
qreal sY = 0.0;
qreal sZ = 0.0;
explicit State( const QskLevelingSensor* const sensor )
: r1( QskLevelingSensorSkinlet::innerRadius( sensor ) )
, r2( QskLevelingSensorSkinlet::outerRadius( sensor ) )
, cX( QskLevelingSensorSkinlet::center( sensor ).x() )
, cY( QskLevelingSensorSkinlet::center( sensor ).y() )
, sX( r1 / sensor->angles().x() )
, sY( r1 / sensor->angles().y() )
, sZ( r1 / sensor->angles().z() )
{
}
Q_REQUIRED_RESULT QVector2D center() const noexcept
{
return { ( float ) cX, ( float ) cY };
}
Q_REQUIRED_RESULT QVector3D scale() const noexcept
{
return { ( float ) sX, ( float ) sY, ( float ) sZ };
}
};
template<>
struct State< QskAspect::Subcontrol > : State< QskLevelingSensor >
{
qreal rX = 0.0;
qreal rY = 0.0;
qreal rZ = 0.0;
qreal tX = 0.0;
qreal tY = 0.0;
qreal tZ = 0.0;
State( const QskLevelingSensor* const sensor, QskAspect::Subcontrol subcontrol )
: State< QskLevelingSensor >( sensor )
, rX( sensor->subControlRotation( subcontrol ).x() )
, rY( sensor->subControlRotation( subcontrol ).y() )
, rZ( sensor->subControlRotation( subcontrol ).z() )
, tX( rY * sX )
, tY( rX * sY )
, tZ( 0.0 )
{
}
Q_REQUIRED_RESULT QVector3D translation() const noexcept
{
return { ( float ) tX, ( float ) tY, ( float ) tZ };
}
Q_REQUIRED_RESULT QMatrix4x4 matrix() const noexcept
{
return matrix_deg( rZ, cX, cY );
}
};
Q_REQUIRED_RESULT std::optional< std::pair< qreal, qreal > > minmax(
const QVector< qreal >& tickmarks )
{
if ( tickmarks.empty() )
{
return {};
}
const auto [ pmin, pmax ] = std::minmax_element( tickmarks.begin(), tickmarks.end() );
return std::make_pair( *pmin, *pmax );
}
Q_REQUIRED_RESULT std::optional< std::pair< qreal, qreal > > minmax(
const QskScaleTickmarks& tickmarks )
{
if ( tickmarks.tickCount() == 0 )
{
return {};
}
const auto majorTicks = tickmarks.majorTicks();
const auto mediumTicks = tickmarks.mediumTicks();
const auto minorTicks = tickmarks.minorTicks();
const auto item = !majorTicks.empty() ? majorTicks[ 0 ]
: !mediumTicks.empty() ? mediumTicks[ 0 ]
: !minorTicks.empty() ? minorTicks[ 0 ]
: std::numeric_limits< qreal >::quiet_NaN();
Q_ASSERT_X( !std::isnan( item ), __func__, "NaN should not be possible!" );
std::array< qreal, 6 > local = { item };
if ( const auto opt = minmax( tickmarks.majorTicks() ) )
{
local[ 0 ] = opt->first;
local[ 1 ] = opt->second;
}
if ( const auto opt = minmax( tickmarks.mediumTicks() ) )
{
local[ 2 ] = opt->first;
local[ 3 ] = opt->second;
}
if ( const auto opt = minmax( tickmarks.minorTicks() ) )
{
local[ 4 ] = opt->first;
local[ 5 ] = opt->second;
}
const auto [ pmin, pmax ] = std::minmax_element( local.begin(), local.end() );
return std::make_pair( *pmin, *pmax );
}
}
using Q = QskLevelingSensor;
using R = QskLevelingSensorSkinlet::NodeRole;
using namespace QskSGNode;
template< typename Root, typename... Children >
Q_REQUIRED_RESULT inline Root* ensureNodes( QSGNode* root = nullptr )
{
return ensureNodes< AppendMode::Recursive, Root, Children... >( root );
}
float QskLevelingSensorSkinlet::outerRadius( const QskSkinnable* const skinnable )
{
const auto* const sensor = static_cast< const Q* >( skinnable );
const auto contentsRect = sensor->contentsRect();
return 0.5f * qMin( contentsRect.width(), contentsRect.height() );
}
float QskLevelingSensorSkinlet::innerRadius( const QskSkinnable* const skinnable )
{
const auto scale = skinnable->strutSizeHint( Q::Horizon );
return outerRadius( skinnable ) * scale.width();
}
QPointF QskLevelingSensorSkinlet::center( const QskSkinnable* const skinnable )
{
const auto* const sensor = static_cast< const Q* >( skinnable );
return sensor->contentsRect().center();
}
QskLevelingSensorSkinlet::QskLevelingSensorSkinlet( QskSkin* skin )
: Inherited( skin )
{
setNodeRoles( {
OuterDisk,
Horizon,
HorizonClip,
TickmarksX,
TickmarksXLabels,
TickmarksY,
TickmarksYLabels,
TickmarksZ,
TickmarksZLabels,
} );
}
template<>
Q_REQUIRED_RESULT QRectF QskLevelingSensorSkinlet::subControlRect< R::OuterDisk >(
const QskLevelingSensor* const sensor, const QRectF& contentsRect ) const
{
const auto radius = outerRadius( sensor );
const auto scale = sensor->strutSizeHint( Q::OuterDisk );
const auto width = 2 * radius * scale.width();
const auto height = width;
const auto x = contentsRect.center().x() - width / 2;
const auto y = contentsRect.center().y() - height / 2;
return { x, y, width, height };
}
template<>
Q_REQUIRED_RESULT QRectF QskLevelingSensorSkinlet::subControlRect< R::Horizon >(
const QskLevelingSensor* const sensor, const QRectF& contentsRect ) const
{
Q_UNUSED( contentsRect )
const auto scale = sensor->strutSizeHint( Q::Horizon );
const auto width = 2 * innerRadius( sensor ) * scale.width();
const auto height = width;
return { center( sensor ).x() - width / 2, center( sensor ).y() - height / 2, width, height };
}
QRectF QskLevelingSensorSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
const auto* const sensor = static_cast< const Q* >( skinnable );
if ( subControl == Q::OuterDisk )
{
return subControlRect< OuterDisk >( sensor, contentsRect );
}
if ( subControl == Q::Horizon )
{
return subControlRect< Horizon >( sensor, contentsRect );
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::OuterDisk >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::OuterDisk;
const auto contentsRect = sensor->contentsRect();
const auto boxRect = subControlRect< OuterDisk >( sensor, contentsRect );
const auto boxShapeMetrics = QskBoxShapeMetrics{ boxRect.width() / 2 };
const auto boxBorderMetrics = sensor->boxBorderMetricsHint( subControl );
const auto boxBorderColors = sensor->boxBorderColorsHint( subControl );
const auto boxGradient = sensor->gradientHint( subControl );
auto* const root = ensureNodes< QSGTransformNode, QskBoxNode >( node );
auto* const bNode = static_cast< QskBoxNode* >( root->firstChild() );
const auto size = outerRadius( sensor ) * sensor->strutSizeHint( Q::OuterDisk ).width();
updateBoxNode( sensor, bNode, { 0, 0, 2 * size, 2 * size }, boxShapeMetrics, boxBorderMetrics,
boxBorderColors, boxGradient );
const auto cX = center( sensor ).x();
const auto cY = center( sensor ).y();
const auto rZ = 0.0;
const auto matrix =
matrix_deg( 0.0, cX, cY ) * matrix_deg( rZ, 0, 0 ) * matrix_deg( 0.0, -size, -size );
root->setMatrix( matrix );
return root;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::Horizon >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::Horizon;
const State< QskAspect::Subcontrol > state( sensor, subControl );
const auto dY = 2 * sensor->angles().y();
const auto pY = qBound( 0.0, 0.5 + ( -state.rX / dY ), 1.0 );
const auto shape = QskBoxShapeMetrics{ state.r1 };
const auto metrics = sensor->boxBorderMetricsHint( subControl );
const auto colors = sensor->boxBorderColorsHint( subControl );
auto gradient = sensor->gradientHint( Q::Horizon );
gradient.setDirection( QskGradient::Linear );
gradient.setLinearDirection( Qt::Vertical );
gradient.setStops( { { 0.0, gradient.startColor() }, { pY, gradient.startColor() },
{ pY, gradient.endColor() }, { 1.0, gradient.endColor() } } );
auto* const tNode = ensureNodes< QSGTransformNode, QskBoxNode >( node );
auto* const boxNode = static_cast< QskBoxNode* >( tNode->firstChild() );
updateBoxNode(
sensor, boxNode, { 0, 0, 2 * state.r1, 2 * state.r1 }, shape, metrics, colors, gradient );
const auto matrix = matrix_deg( 0, state.cX, state.cY ) * matrix_deg( state.rZ, 0, 0 ) *
matrix_deg( 0, -state.r1, -state.r1 );
tNode->setMatrix( matrix );
return tNode;
}
Q_REQUIRED_RESULT QSGNode* updateLinearTickmarksNode( const QskLevelingSensor* const sensor,
const QskAspect::Subcontrol subControl, const QskScaleTickmarks& tickmarks,
const Qt::Orientation orientation, QSGNode* const node )
{
const auto state = State< QskAspect::Subcontrol >( sensor, subControl );
const auto color = sensor->color( subControl );
const auto scale = sensor->strutSizeHint( subControl );
const auto width = state.r1 * scale.width();
const auto height = state.r1 * scale.height();
const auto alignment = sensor->alignmentHint( subControl );
const auto opt = minmax( tickmarks );
const auto min = opt ? opt->first : 0.0;
const auto max = opt ? opt->second : 0.0;
const auto interval = QskIntervalF{ min, max };
const auto rect =
orientation == Qt::Horizontal
? QRectF{ QPointF{ min * state.sX, -height }, QPointF{ max * state.sX, +height } }
: QRectF{ QPointF{ -width, min * state.sY }, QPointF{ +width, max * state.sY } };
const auto translation = QPointF{ state.tX + state.cX, state.tY + state.cY };
auto* const cNode = ensureNodes< RadialClipNode, QSGTransformNode, QskTickmarksNode >( node );
auto* const tNode = static_cast< QSGTransformNode* >( cNode->firstChild() );
auto* const qNode = static_cast< QskTickmarksNode* >( tNode->firstChild() );
cNode->setGeometryProperties( state.r1, state.cX, state.cY );
tNode->setMatrix( matrix_deg( state.rZ, translation.x(), translation.y() ) );
qNode->update( color, rect, interval, tickmarks, 1, orientation, alignment );
return cNode;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksX >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
return updateLinearTickmarksNode(
sensor, Q::TickmarksX, sensor->tickmarks( Qt::XAxis ), Qt::Horizontal, node );
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksY >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
return updateLinearTickmarksNode(
sensor, Q::TickmarksY, sensor->tickmarks( Qt::YAxis ), Qt::Vertical, node );
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksZ >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::TickmarksZ;
const State< QskAspect::Subcontrol > state( sensor, subControl );
const auto color = sensor->color( subControl );
const auto r3 = qvariant_cast< QVector3D >( sensor->effectiveSkinHint( subControl ) ) *
( state.r2 - state.r1 ) +
QVector3D{ ( float ) state.r1, ( float ) state.r1, ( float ) state.r1 };
auto* const transform = ensureNodes< QSGTransformNode, RadialTickmarksNode >( node );
auto* const tickmarksNode = static_cast< RadialTickmarksNode* >( transform->firstChild() );
tickmarksNode->setMaterialProperties( color );
tickmarksNode->setGeometryProperties( sensor->tickmarks( Qt::ZAxis ), state.r1, r3 );
const auto matrix = matrix_deg( state.rZ, state.cX, state.cY );
transform->setMatrix( matrix );
return transform;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksXLabels >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::TickmarksXLabels;
const State< QskAspect::Subcontrol > state( sensor, subControl );
const auto r3 = state.r1 * sensor->strutSizeHint( Q::TickmarksX ).height();
const auto dX = qFastCos( qDegreesToRadians( 90 + state.rZ ) ) * r3;
const auto dY = qFastSin( qDegreesToRadians( 90 + state.rZ ) ) * r3;
auto* const cNode =
ensureNodes< RadialClipNode, QSGTransformNode, LinearTickmarksLabelsNode >( node );
auto* const tNode = static_cast< QSGTransformNode* >( cNode->firstChild() );
auto* const lNode = static_cast< LinearTickmarksLabelsNode* >( tNode->firstChild() );
cNode->setGeometryProperties( state.r1, state.cX, state.cY );
tNode->setMatrix( matrix_deg( state.rZ, state.cX + state.tX + dX, state.cY + state.tY + dY ) );
lNode->update(
sensor, subControl, sensor->tickmarkLabels( Qt::XAxis ), { state.scale().x(), 0.0 } );
return cNode;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksYLabels >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::TickmarksYLabels;
const State< QskAspect::Subcontrol > state( sensor, subControl );
const auto r3 = state.r1 * sensor->strutSizeHint( Q::TickmarksY ).width();
const auto dX = qFastCos( qDegreesToRadians( state.rZ ) ) * r3;
const auto dY = qFastSin( qDegreesToRadians( state.rZ ) ) * r3;
auto* const cNode =
ensureNodes< RadialClipNode, QSGTransformNode, LinearTickmarksLabelsNode >( node );
auto* const tNode = static_cast< QSGTransformNode* >( cNode->firstChild() );
auto* const lNode = static_cast< LinearTickmarksLabelsNode* >( tNode->firstChild() );
cNode->setGeometryProperties( state.r1, state.cX, state.cY );
tNode->setMatrix( matrix_deg( state.rZ, state.cX + state.tX + dX, state.cY + state.tY + dY ) );
lNode->update(
sensor, subControl, sensor->tickmarkLabels( Qt::YAxis ), { 0.0, state.scale().y() } );
return cNode;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::TickmarksZLabels >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto subControl = Q::TickmarksZLabels;
const State< QskAspect::Subcontrol > state( sensor, subControl );
auto* const tNode = ensureNodes< QSGTransformNode, RadialTickmarksLabelsNode >( node );
auto* const lNode = static_cast< RadialTickmarksLabelsNode* >( tNode->firstChild() );
const auto r3 = static_cast< float >( state.r1 * sensor->strutSizeHint( subControl ).width() );
lNode->update( sensor, subControl, sensor->tickmarkLabels( Qt::ZAxis ), { r3, r3 } );
tNode->setMatrix( state.matrix() );
return tNode;
}
template<>
QSGNode* QskLevelingSensorSkinlet::updateSubNode< R::HorizonClip >(
const QskLevelingSensor* const sensor, const quint8 nodeRole, QSGNode* const node ) const
{
Q_UNUSED( nodeRole )
const auto cX = center( sensor ).x();
const auto cY = center( sensor ).y();
const auto r1 = innerRadius( sensor );
auto* const clipNode = ensureNodes< RadialClipNode >( node );
clipNode->setGeometryProperties( r1, cX, cY );
return clipNode;
}
QSGNode* QskLevelingSensorSkinlet::updateSubNode(
const QskSkinnable* const skinnable, const quint8 nodeRole, QSGNode* const node ) const
{
const auto* const sensor = static_cast< const Q* >( skinnable );
switch ( static_cast< R >( nodeRole ) )
{
case OuterDisk:
return updateSubNode< OuterDisk >( sensor, nodeRole, node );
case Horizon:
return updateSubNode< Horizon >( sensor, nodeRole, node );
case HorizonClip:
return updateSubNode< HorizonClip >( sensor, nodeRole, node );
case TickmarksX:
return updateSubNode< TickmarksX >( sensor, nodeRole, node );
case TickmarksXLabels:
return updateSubNode< TickmarksXLabels >( sensor, nodeRole, node );
case TickmarksY:
return updateSubNode< TickmarksY >( sensor, nodeRole, node );
case TickmarksYLabels:
return updateSubNode< TickmarksYLabels >( sensor, nodeRole, node );
case TickmarksZ:
return updateSubNode< TickmarksZ >( sensor, nodeRole, node );
case TickmarksZLabels:
return updateSubNode< TickmarksZLabels >( sensor, nodeRole, node );
default:
return Inherited::updateSubNode( sensor, nodeRole, node );
}
}
#include "moc_QskLevelingSensorSkinlet.cpp"

View File

@ -0,0 +1,81 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_LEVELING_SENSOR_UTILITY_H
#define QSK_LEVELING_SENSOR_UTILITY_H
#include <qmath.h>
#include <qmatrix4x4.h>
#include <qtransform.h>
#include <QskFunctions.h>
#include <QskScaleTickmarks.h>
Q_REQUIRED_RESULT inline QMatrix4x4 matrix_deg( float rZ = 0.0f, float tX = 0.0f, float tY = 0.0f )
{
QTransform transform;
transform.translate( tX, tY );
transform.rotate( rZ, Qt::ZAxis );
return transform;
}
template< typename T >
Q_REQUIRED_RESULT inline bool compareExchange( T& dst, const T& src )
{
if ( dst != src )
{
dst = src;
return true;
}
return false;
}
template<>
Q_REQUIRED_RESULT inline bool compareExchange< float >( float& dst, const float& src )
{
if ( !qskFuzzyCompare( dst, src ) )
{
dst = src;
return true;
}
return false;
}
template<>
Q_REQUIRED_RESULT inline bool compareExchange< qreal >( qreal& dst, const qreal& src )
{
if ( !qskFuzzyCompare( dst, src ) )
{
dst = src;
return true;
}
return false;
}
Q_REQUIRED_RESULT inline QskScaleTickmarks filtered( const QskScaleTickmarks& tickmarks,
const std::function< bool( QskScaleTickmarks::TickType, qreal ) >& predicate )
{
QskScaleTickmarks result;
QVector< qreal > ticks[ 3 ];
using T = QskScaleTickmarks::TickType;
for ( auto type : { T::MinorTick, T::MediumTick, T::MajorTick } )
{
for ( const auto tick : tickmarks.ticks( type ) )
{
if ( predicate( type, tick ) )
{
ticks[ type ] << tick;
}
}
}
result.setMinorTicks( ticks[ QskScaleTickmarks::MinorTick ] );
result.setMediumTicks( ticks[ QskScaleTickmarks::MediumTick ] );
result.setMajorTicks( ticks[ QskScaleTickmarks::MajorTick ] );
return result;
}
#endif

View File

@ -0,0 +1,47 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_SGNODE_UTILITY_H
#define QSK_SGNODE_UTILITY_H
#include <QSGNode>
namespace QskSGNode
{
enum AppendMode
{
Sequential,
Recursive
};
template< AppendMode mode, typename Root, typename... Children >
Q_REQUIRED_RESULT inline Root* ensureNodes( QSGNode* root = nullptr )
{
if ( root == nullptr )
{
root = new Root;
}
if constexpr ( mode == Recursive )
{
QSGNode* current = root;
Q_UNUSED( current )
(
[ &current ]( QSGNode* const child ) mutable {
current->appendChildNode( child );
current = child;
}( new Children ),
... );
}
else
{
( root->appendChildNode( new Children ), ... );
}
return static_cast< Root* >( root );
}
}
#endif