initial commit
This commit is contained in:
parent
3fee4907c0
commit
3552c79aec
|
|
@ -0,0 +1 @@
|
|||
#include "QsgNodeUtility.h"
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
#include <QSGNode>
|
||||
#include <type_traits>
|
||||
|
||||
namespace qsg
|
||||
{
|
||||
using NodeType = QSGNode;
|
||||
|
||||
template<typename ... Ts>
|
||||
struct seq;
|
||||
|
||||
template<>
|
||||
struct seq<>
|
||||
{
|
||||
static void append(NodeType* root)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct seq<T>
|
||||
{
|
||||
static void append(NodeType* root)
|
||||
{
|
||||
T::append(root);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename ... Ts>
|
||||
struct seq<T, Ts...>
|
||||
{
|
||||
static void append(NodeType* root)
|
||||
{
|
||||
T::append(root);
|
||||
seq<Ts...>::append(root);
|
||||
}
|
||||
};
|
||||
|
||||
template<int N, typename ... Ts>
|
||||
struct par;
|
||||
|
||||
template<int N, typename T>
|
||||
struct par<N,T>
|
||||
{
|
||||
static void append(NodeType* root)
|
||||
{
|
||||
const auto n = N;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
root->appendChildNode(new T);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<int N, typename T, typename U>
|
||||
struct par<N,T,U>
|
||||
{
|
||||
static void append(NodeType* root)
|
||||
{
|
||||
const auto n = N;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
auto* const t = new T;
|
||||
U::append(t);
|
||||
root->appendChildNode(t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... Ts>
|
||||
struct ensure;
|
||||
|
||||
template<typename Root>
|
||||
struct ensure<Root>
|
||||
{
|
||||
static Q_REQUIRED_RESULT Root* node(NodeType* root = nullptr)
|
||||
{
|
||||
if(root == nullptr)
|
||||
{
|
||||
root = new Root;
|
||||
}
|
||||
return static_cast<Root*>(root);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Root, typename Append>
|
||||
struct ensure<Root, Append>
|
||||
{
|
||||
static Q_REQUIRED_RESULT Root* node(NodeType* root = nullptr)
|
||||
{
|
||||
if(root == nullptr)
|
||||
{
|
||||
root = new Root;
|
||||
Append::append(root);
|
||||
}
|
||||
return static_cast<Root*>(root);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
#include "QskLevelingSensor.h"
|
||||
#include <QskScaleTickmarks.h>
|
||||
|
||||
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 (!qFuzzyCompare(dst, src))
|
||||
{
|
||||
dst = src;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isAxis(const Qt::Axis axis) {
|
||||
return axis == Qt::XAxis || axis == Qt::YAxis || axis == Qt::ZAxis;
|
||||
}
|
||||
}
|
||||
|
||||
QSK_SUBCONTROL(LevelingSensor, OuterDisk)
|
||||
QSK_SUBCONTROL(LevelingSensor, Horizon)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksX)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksXLabels)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksY)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksYLabels)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksZ)
|
||||
QSK_SUBCONTROL(LevelingSensor, TickmarksZLabels)
|
||||
|
||||
#define RETURN_IF_FALSE(expr) if(!(expr)) return;
|
||||
|
||||
LevelingSensor::LevelingSensor(QQuickItem* const parent)
|
||||
: Inherited(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LevelingSensor::setRotation(const QVector3D& degree)
|
||||
{
|
||||
if (m_rotation != degree)
|
||||
{
|
||||
setRotation(Qt::XAxis, degree.x());
|
||||
setRotation(Qt::YAxis, degree.y());
|
||||
setRotation(Qt::ZAxis, degree.z());
|
||||
}
|
||||
}
|
||||
|
||||
void LevelingSensor::setRotation(const Qt::Axis axis, const float degree)
|
||||
{
|
||||
RETURN_IF_FALSE(isAxis(axis));
|
||||
|
||||
if (compareExchange(m_rotation[axis], degree))
|
||||
{
|
||||
update();
|
||||
switch(axis)
|
||||
{
|
||||
case Qt::XAxis: Q_EMIT rotationXChanged(m_rotation[axis]); break;
|
||||
case Qt::YAxis: Q_EMIT rotationYChanged(m_rotation[axis]); break;
|
||||
case Qt::ZAxis: Q_EMIT rotationZChanged(m_rotation[axis]); break;
|
||||
}
|
||||
Q_EMIT rotationChanged(m_rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelingSensor::setTickmarks(const Qt::Axis axis, QskScaleTickmarks tickmarks)
|
||||
{
|
||||
RETURN_IF_FALSE(isAxis(axis));
|
||||
|
||||
m_tickmarks[axis] = std::move(tickmarks);
|
||||
update();
|
||||
}
|
||||
|
||||
void LevelingSensor::setTickmarksLabels(const Qt::Axis axis, TickmarksLabels labels)
|
||||
{
|
||||
RETURN_IF_FALSE(isAxis(axis));
|
||||
|
||||
m_tickmarksLabels[axis] = std::move(labels);
|
||||
update();
|
||||
}
|
||||
|
||||
void LevelingSensor::setAngle(const QVector3D& degree)
|
||||
{
|
||||
if (compareExchange(m_angle, degree))
|
||||
{
|
||||
update();
|
||||
Q_EMIT anglesChanged(m_angle);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelingSensor::setAngle(const Qt::Axis axis, const float degree)
|
||||
{
|
||||
RETURN_IF_FALSE(isAxis(axis));
|
||||
|
||||
if (compareExchange(m_angle[axis], degree))
|
||||
{
|
||||
update();
|
||||
Q_EMIT anglesChanged(m_angle);
|
||||
}
|
||||
}
|
||||
|
||||
const QskScaleTickmarks& LevelingSensor::tickmarks(Qt::Axis axis) const
|
||||
{
|
||||
if (isAxis(axis))
|
||||
{
|
||||
return m_tickmarks[axis];
|
||||
}
|
||||
static const QskScaleTickmarks invalid;
|
||||
return invalid;
|
||||
}
|
||||
|
||||
const LevelingSensor::TickmarksLabels& LevelingSensor::tickmarkLabels(Qt::Axis axis) const
|
||||
{
|
||||
if (isAxis(axis))
|
||||
{
|
||||
return m_tickmarksLabels[axis];
|
||||
}
|
||||
static const LevelingSensor::TickmarksLabels invalid;
|
||||
return invalid;
|
||||
}
|
||||
|
||||
const QVector3D& LevelingSensor::angle() const noexcept
|
||||
{
|
||||
return m_angle;
|
||||
}
|
||||
|
||||
const QVector3D& LevelingSensor::rotation() const noexcept
|
||||
{
|
||||
return m_rotation;
|
||||
}
|
||||
|
||||
#include "moc_QskLevelingSensor.cpp"
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <QskControl.h>
|
||||
#include <QskScaleTickmarks.h>
|
||||
#include <QVector3D>
|
||||
#include <QskAspect.h>
|
||||
|
||||
/// @brief This control can display the pitch, roll and yaw angles
|
||||
/// @note x = pitch, y = yaw, z = roll
|
||||
///
|
||||
/// ^y+
|
||||
/// |
|
||||
/// |
|
||||
/// |
|
||||
/// +------------->x+
|
||||
/// /
|
||||
/// /
|
||||
/// v z+
|
||||
class LevelingSensor : 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 LevelingSensor(QQuickItem* parent = nullptr);
|
||||
public Q_SLOTS:
|
||||
void setRotation(const QVector3D& degree);
|
||||
void setRotation(Qt::Axis axis, float degree);
|
||||
void setTickmarks(Qt::Axis axis, Tickmarks tickmarks);
|
||||
void setTickmarksLabels(Qt::Axis axis, TickmarksLabels labels);
|
||||
void setAngle(const QVector3D& degree);
|
||||
void setAngle(Qt::Axis axis, float degree);
|
||||
signals:
|
||||
void rotationXChanged(qreal degree);
|
||||
void rotationYChanged(qreal degree);
|
||||
void rotationZChanged(qreal degree);
|
||||
void rotationChanged(const QVector3D& degree);
|
||||
void anglesChanged(const QVector3D& degree);
|
||||
public:
|
||||
Q_REQUIRED_RESULT const QVector3D& rotation() const noexcept;
|
||||
Q_REQUIRED_RESULT const Tickmarks& tickmarks(Qt::Axis axis) const;
|
||||
Q_REQUIRED_RESULT const TickmarksLabels& tickmarkLabels(Qt::Axis axis) const;
|
||||
Q_REQUIRED_RESULT const QVector3D& angle() const noexcept;
|
||||
private:
|
||||
/// @brief The sensors rotation per axis: x := roll, y := pitch, z := yaw
|
||||
QVector3D m_rotation;
|
||||
QVector3D m_angle = { 45,45,45 };
|
||||
Tickmarks m_tickmarks[3];
|
||||
TickmarksLabels m_tickmarksLabels[3];
|
||||
};
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
#pragma once
|
||||
|
||||
#include <QSGGeometry>
|
||||
#include <QSGGeometryNode>
|
||||
#include <QSGFlatColorMaterial>
|
||||
|
||||
#include <QFontMetricsF>
|
||||
|
||||
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 |= compare_exchange(m_r1, r1);
|
||||
dirty |= compare_exchange(m_r2, r2);
|
||||
dirty |= compare_exchange(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 LinearTickmarksNode final : public QSGGeometryNode
|
||||
{
|
||||
public:
|
||||
LinearTickmarksNode() : 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)
|
||||
{
|
||||
auto dirty = false;
|
||||
|
||||
if (dirty |= (m_material.color() != color))
|
||||
{
|
||||
m_material.setColor(color);
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
markDirty(QSGNode::DirtyMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto dirty = forceDirty;
|
||||
|
||||
if (dirty |= !qFuzzyCompare(m_geometry.lineWidth(), lineWidth))
|
||||
{
|
||||
m_geometry.setLineWidth(lineWidth);
|
||||
}
|
||||
|
||||
dirty |= m_geometry.vertexCount() != tickmarks.tickCount() * 2;
|
||||
dirty |= compare_exchange(m_tickmarkSize, tickmarkSize);
|
||||
dirty |= compare_exchange(m_scale, scale);
|
||||
dirty |= compare_exchange(m_offset, offset);
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
update(tickmarks);
|
||||
markDirty(QSGNode::DirtyGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void update(const QskScaleTickmarks& tickmarks)
|
||||
{
|
||||
if (m_geometry.vertexCount() != tickmarks.tickCount() * 2)
|
||||
{
|
||||
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 p = m_scale * tick;
|
||||
const auto d = QVector2D(-m_scale.y(), m_scale.x()).normalized();
|
||||
|
||||
const auto p1 = m_tickmarkSize[i] * +1 * d + p + m_offset;
|
||||
const auto p2 = m_tickmarkSize[i] * -1 * d + p + m_offset;
|
||||
|
||||
vertexData[0].set(p1.x(), p1.y());
|
||||
vertexData[1].set(p2.x(), p2.y());
|
||||
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 };
|
||||
};
|
||||
|
||||
class PolygonClipNode final : public QSGClipNode
|
||||
{
|
||||
public:
|
||||
PolygonClipNode() : 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 |= compare_exchange(m_radius, radius);
|
||||
dirty |= compare_exchange(m_cx, cx);
|
||||
dirty |= compare_exchange(m_cy, cy);
|
||||
dirty |= compare_exchange(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::setIsRectangular;
|
||||
using QSGClipNode::setClipRect;
|
||||
|
||||
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 = {})
|
||||
{
|
||||
if (childCount() != labels.count())
|
||||
{
|
||||
removeAllChildNodes();
|
||||
for (const auto& label : qAsConst(labels))
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
#include "QskLevelingSensorSkinlet.h"
|
||||
#include "QskLevelingSensor.h"
|
||||
#include "QskLevelingSensorUtility.h"
|
||||
#include "QskLevelingSensorNodes.h"
|
||||
#include "QsgNodeUtility.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>
|
||||
|
||||
using Q = LevelingSensor;
|
||||
using R = LevelingSensorSkinlet::NodeRole;
|
||||
|
||||
using namespace qsg;
|
||||
|
||||
float LevelingSensorSkinlet::radius2(const QskSkinnable* const skinnable)
|
||||
{
|
||||
// outer radius
|
||||
const auto* const sensor = static_cast<const Q*>(skinnable);
|
||||
const auto contentsRect = sensor->contentsRect();
|
||||
return 0.5f * qMin(contentsRect.width(), contentsRect.height());
|
||||
}
|
||||
|
||||
float LevelingSensorSkinlet::radius1(const QskSkinnable* const skinnable)
|
||||
{
|
||||
const auto scale = skinnable->strutSizeHint(Q::Horizon);
|
||||
return radius2(skinnable) * scale.width();
|
||||
}
|
||||
|
||||
QPointF LevelingSensorSkinlet::center(const QskSkinnable* const skinnable)
|
||||
{
|
||||
const auto* const sensor = static_cast<const Q*>(skinnable);
|
||||
return sensor->contentsRect().center();
|
||||
}
|
||||
|
||||
LevelingSensorSkinlet::LevelingSensorSkinlet(QskSkin* skin)
|
||||
: Inherited(skin)
|
||||
{
|
||||
setNodeRoles({
|
||||
OuterDisk,
|
||||
Horizon,
|
||||
HorizonClip,
|
||||
TickmarksX,
|
||||
TickmarksXLabels,
|
||||
TickmarksY,
|
||||
TickmarksYLabels,
|
||||
TickmarksZ,
|
||||
TickmarksZLabels, });
|
||||
}
|
||||
|
||||
template<LevelingSensorSkinlet::NodeRole>
|
||||
Q_REQUIRED_RESULT QRectF LevelingSensorSkinlet::subControlRect(const LevelingSensor* const sensor,
|
||||
const QRectF& contentsRect) const = delete;
|
||||
|
||||
template<>
|
||||
Q_REQUIRED_RESULT QRectF LevelingSensorSkinlet::subControlRect<R::OuterDisk>(const LevelingSensor* const sensor,
|
||||
const QRectF& contentsRect) const
|
||||
{
|
||||
const auto radius = radius2(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 LevelingSensorSkinlet::subControlRect<R::Horizon>(const LevelingSensor* const sensor,
|
||||
const QRectF& contentsRect) const
|
||||
{
|
||||
const auto scale = sensor->strutSizeHint(Q::Horizon);
|
||||
const auto width = 2 * radius1(sensor) * scale.width();
|
||||
const auto height = width;
|
||||
return {
|
||||
center(sensor).x() - width / 2,
|
||||
center(sensor).y() - height / 2,
|
||||
width,
|
||||
height
|
||||
};
|
||||
}
|
||||
|
||||
QRectF LevelingSensorSkinlet::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<LevelingSensorSkinlet::NodeRole>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const = delete;
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::OuterDisk>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
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 = ensure<QSGTransformNode, par<1, QskBoxNode>>::node(node);
|
||||
auto* const bNode = static_cast<QskBoxNode*>(root->firstChild());
|
||||
|
||||
const auto size = radius2(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 = sensor->arcMetricsHint(subControl).startAngle();
|
||||
|
||||
const auto matrix =
|
||||
matrix_deg(0.0, 0.0, 0.0, cX, cY, 0) *
|
||||
matrix_deg(0.0, 0.0, rZ, 0, 0, 0) *
|
||||
matrix_deg(0.0, 0.0, 0.0, -size, -size, 0);
|
||||
|
||||
root->setMatrix(matrix);
|
||||
return root;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::Horizon>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::Horizon;
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto cX = center(sensor).x();
|
||||
const auto cY = center(sensor).y();
|
||||
const auto rX = sensor->rotation().x();
|
||||
const auto rZ = sensor->arcMetricsHint(subControl).startAngle();
|
||||
const auto dY = 2 * sensor->angle().y();
|
||||
const auto p = qBound(0.0, 0.5 + (-rX / dY), 1.0);
|
||||
|
||||
const auto shape = QskBoxShapeMetrics{ r1 };
|
||||
const auto bmetrics = sensor->boxBorderMetricsHint(subControl);
|
||||
const auto bcolors = sensor->boxBorderColorsHint(subControl);
|
||||
|
||||
auto gradient = sensor->gradientHint(Q::Horizon);
|
||||
gradient.setDirection(QskGradient::Linear);
|
||||
gradient.setLinearDirection(Qt::Vertical);
|
||||
gradient.setStops({
|
||||
{0.0, gradient.startColor()},
|
||||
{p, gradient.startColor()},
|
||||
{p, gradient.endColor()},
|
||||
{1.0, gradient.endColor()}
|
||||
});
|
||||
|
||||
auto* const tNode = ensure<QSGTransformNode, par<1, QskBoxNode>>::node(node);
|
||||
auto* const boxNode = static_cast<QskBoxNode*>(tNode->firstChild());
|
||||
updateBoxNode(sensor, boxNode, { 0, 0, 2 * r1, 2 * r1 }, shape, bmetrics, bcolors, gradient);
|
||||
|
||||
const auto matrix =
|
||||
matrix_deg(0, 0, 0, cX, cY, 0) *
|
||||
matrix_deg(0, 0, rZ, 0, 0, 0) *
|
||||
matrix_deg(0, 0, 0, -r1, -r1, 0);
|
||||
|
||||
tNode->setMatrix(matrix);
|
||||
return tNode;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksX>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksX;
|
||||
const auto color = sensor->color(subControl);
|
||||
const auto scale = sensor->strutSizeHint(subControl);
|
||||
|
||||
const auto cX = center(sensor).x();
|
||||
const auto cY = center(sensor).y();
|
||||
|
||||
const auto rX = sensor->rotation().x();
|
||||
const auto rY = sensor->rotation().y();
|
||||
const auto rZ = sensor->arcMetricsHint(subControl).startAngle();
|
||||
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r3 = r1 * scale.height();
|
||||
|
||||
const auto sX = r1 / sensor->angle().x();
|
||||
const auto sY = r1 / sensor->angle().y();
|
||||
|
||||
const auto tX = static_cast<float>(rY * sX);
|
||||
const auto tY = 0.0; // static_cast<float>(rX * sY);
|
||||
|
||||
auto* const clipping = ensure<PolygonClipNode, par<1, QSGTransformNode, par<1, LinearTickmarksNode>>>::node(node);
|
||||
auto* const transform = static_cast<QSGTransformNode*>(clipping->firstChild());
|
||||
auto* const tickmarks = static_cast<LinearTickmarksNode*>(transform->firstChild());
|
||||
|
||||
auto size = qvariant_cast<QVector3D>(sensor->effectiveSkinHint(subControl)) * r3;
|
||||
|
||||
clipping->setGeometryProperties(r1, cX, cY);
|
||||
|
||||
tickmarks->setMaterialProperties(color);
|
||||
tickmarks->setGeometryProperties(sensor->tickmarks(Qt::XAxis), size, {sX, 0.0f}, {tX, tY});
|
||||
|
||||
const auto matrix = matrix_deg(0, 0, rZ, cX, cY, 0);
|
||||
transform->setMatrix(matrix);
|
||||
return clipping;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksY>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksY;
|
||||
const auto color = sensor->color(subControl);
|
||||
const auto scale = sensor->strutSizeHint(subControl);
|
||||
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r3 = r1 * scale.width();
|
||||
|
||||
const auto rX = 0.00;
|
||||
const auto rY = 0.00;
|
||||
const auto rZ = sensor->rotation().z();
|
||||
|
||||
const auto tX = center(sensor).x();
|
||||
const auto tY = center(sensor).y();
|
||||
const auto tZ = 0.0;
|
||||
|
||||
auto* const cNode = ensure<PolygonClipNode, par<1, QSGTransformNode, par<1, LinearTickmarksNode>>>::node(node);
|
||||
auto* const tNode = static_cast<QSGTransformNode*>(cNode->firstChild());
|
||||
auto* const lNode = static_cast<LinearTickmarksNode*>(tNode->firstChild());
|
||||
|
||||
auto size = qvariant_cast<QVector3D>(sensor->effectiveSkinHint(subControl)) * r3;
|
||||
|
||||
cNode->setGeometryProperties(r1, tX, tY);
|
||||
|
||||
const auto sY = static_cast<float>(r1 / sensor->angle().y());
|
||||
lNode->setMaterialProperties(color);
|
||||
#ifdef USE_FILTERING
|
||||
using TickType = QskScaleTickmarks::TickType;
|
||||
const auto filter = [=](TickType, qreal v){ return rY - r1 / sY <= v && v <= rY + r1 / sY; };
|
||||
lNode->setGeometryProperties(filtered(sensor->tickmarks(Qt::YAxis), filter), size, {0.0f, sY}, {}, 1.0f, true);
|
||||
#else
|
||||
lNode->setGeometryProperties(sensor->tickmarks(Qt::YAxis), size, {0.0f, sY});
|
||||
#endif
|
||||
|
||||
const auto matrix = matrix_deg(rX, rY, rZ, tX, tY, tZ);
|
||||
tNode->setMatrix(matrix);
|
||||
return cNode;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksZ>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksZ;
|
||||
const auto color = sensor->color(subControl);
|
||||
const auto scale = sensor->strutSizeHint(subControl);
|
||||
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r2 = radius2(sensor);
|
||||
const auto r3 = qvariant_cast<QVector3D>(sensor->effectiveSkinHint(subControl)) * (r2 - r1) + QVector3D{r1, r1, r1};
|
||||
|
||||
auto* const transform = ensure<QSGTransformNode, par<1,RadialTickmarksNode>>::node(node);
|
||||
auto* const tickmarksNode = static_cast<RadialTickmarksNode*>(transform->firstChild());
|
||||
tickmarksNode->setMaterialProperties(color);
|
||||
tickmarksNode->setGeometryProperties(sensor->tickmarks(Qt::ZAxis), r1, r3);
|
||||
|
||||
const auto rZ = sensor->arcMetricsHint(subControl).startAngle();
|
||||
const auto tX = center(sensor).x();
|
||||
const auto tY = center(sensor).y();
|
||||
|
||||
const auto matrix = matrix_deg(0.0, 0.0, rZ, tX, tY);
|
||||
transform->setMatrix(matrix);
|
||||
return transform;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksXLabels>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksXLabels;
|
||||
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r3 = static_cast<float>(r1 * sensor->strutSizeHint(Q::TickmarksX).height());
|
||||
const auto sX = r1 / sensor->angle().x();
|
||||
const auto sY = r1 / sensor->angle().y();
|
||||
const auto rZ = sensor->arcMetricsHint(subControl).startAngle();
|
||||
const auto cX = center(sensor).x();
|
||||
const auto cY = center(sensor).y();
|
||||
const auto tX = sensor->rotation().y() * sX;
|
||||
const auto tY = r3;
|
||||
|
||||
auto* const cNode = ensure<PolygonClipNode, par<1, QSGTransformNode, par<1, LinearTickmarksLabelsNode>>>::node(node);
|
||||
auto* const tNode = static_cast<QSGTransformNode*>(cNode->firstChild());
|
||||
auto* const lNode = static_cast<LinearTickmarksLabelsNode*>(tNode->firstChild());
|
||||
tNode->setMatrix(matrix_deg(0.0, 0.0, rZ, cX, cY));
|
||||
cNode->setGeometryProperties(r1, center(sensor).x(), center(sensor).y());
|
||||
lNode->update(sensor, subControl, sensor->tickmarkLabels(Qt::XAxis), { sX , 0.0}, {tX, tY});
|
||||
return cNode;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksYLabels>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksYLabels;
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r3 = static_cast<float>(r1 * sensor->strutSizeHint(Q::TickmarksY).width());
|
||||
const auto cX = static_cast<float>(center(sensor).x());
|
||||
const auto cY = static_cast<float>(center(sensor).y());
|
||||
const auto rZ = sensor->rotation().z();
|
||||
|
||||
auto* const cNode = ensure<PolygonClipNode, par<1, QSGTransformNode, par<1, LinearTickmarksLabelsNode>>>::node(node);
|
||||
auto* const tNode = static_cast<QSGTransformNode*>(cNode->firstChild());
|
||||
auto* const lNode = static_cast<LinearTickmarksLabelsNode*>(tNode->firstChild());
|
||||
cNode->setGeometryProperties(r1, cX, cY);
|
||||
tNode->setMatrix(matrix_deg(0.0, 0.0, 0, cX, cY));
|
||||
lNode->update(sensor, subControl, sensor->tickmarkLabels(Qt::YAxis), { 0.0, r1 / sensor->angle().y() }, {r3, 0.0});
|
||||
return cNode;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::TickmarksZLabels>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto subControl = Q::TickmarksZLabels;
|
||||
auto* const tNode = ensure<QSGTransformNode, par<1,RadialTickmarksLabelsNode>>::node(node);
|
||||
auto* const lNode = static_cast<RadialTickmarksLabelsNode*>(tNode->firstChild());
|
||||
const auto r1 = radius1(sensor);
|
||||
const auto r3 = static_cast<float>(r1 * sensor->strutSizeHint(subControl).width());
|
||||
const auto cX = static_cast<float>(center(sensor).x());
|
||||
const auto cY = static_cast<float>(center(sensor).y());
|
||||
const auto rZ = sensor->arcMetricsHint(subControl).startAngle();
|
||||
lNode->update(sensor, subControl, sensor->tickmarkLabels(Qt::ZAxis), { r3, r3 });
|
||||
tNode->setMatrix(matrix_deg(0.0, 0.0, rZ, cX, cY));
|
||||
return tNode;
|
||||
}
|
||||
|
||||
template<>
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode<R::HorizonClip>(const LevelingSensor* const sensor,
|
||||
const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto cX = center(sensor).x();
|
||||
const auto cY = center(sensor).y();
|
||||
const auto r1 = radius1(sensor);
|
||||
|
||||
auto* const clipNode = ensure<PolygonClipNode>::node(node);
|
||||
clipNode->setGeometryProperties(r1, cX, cY);
|
||||
return clipNode;
|
||||
}
|
||||
|
||||
QSGNode* LevelingSensorSkinlet::updateSubNode(
|
||||
const QskSkinnable* const skinnable, const quint8 nodeRole, QSGNode* const node) const
|
||||
{
|
||||
const auto* const sensor = static_cast<const Q*>(skinnable);
|
||||
|
||||
const auto subControl = [nodeRole, sensor](){
|
||||
switch(static_cast<R>(nodeRole))
|
||||
{
|
||||
case OuterDisk: return Q::OuterDisk;
|
||||
case Horizon: return Q::Horizon;
|
||||
case HorizonClip: return Q::Horizon;
|
||||
case TickmarksX: return Q::TickmarksX;
|
||||
case TickmarksXLabels: return Q::TickmarksXLabels;
|
||||
case TickmarksY: return Q::TickmarksY;
|
||||
case TickmarksYLabels: return Q::TickmarksYLabels;
|
||||
case TickmarksZ: return Q::TickmarksZ;
|
||||
case TickmarksZLabels: return Q::TickmarksZLabels;
|
||||
default: return QskAspect::NoSubcontrol;
|
||||
}
|
||||
}();
|
||||
|
||||
if (qvariant_cast<bool>(sensor->effectiveSkinHint(subControl | QskAspect::Option)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <QskSkinlet.h>
|
||||
#include <QSGNode>
|
||||
|
||||
class LevelingSensor;
|
||||
|
||||
class LevelingSensorSkinlet : public QskSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
using Inherited = QskSkinlet;
|
||||
|
||||
public:
|
||||
enum NodeRole
|
||||
{
|
||||
OuterDisk,
|
||||
Horizon,
|
||||
HorizonClip,
|
||||
TickmarksX,
|
||||
TickmarksXLabels,
|
||||
TickmarksY,
|
||||
TickmarksYLabels,
|
||||
TickmarksZ,
|
||||
TickmarksZLabels,
|
||||
TriangleBar,
|
||||
TickmarksYIndicator,
|
||||
RoleCount
|
||||
};
|
||||
|
||||
Q_INVOKABLE LevelingSensorSkinlet(QskSkin* skin = nullptr);
|
||||
~LevelingSensorSkinlet() override = default;
|
||||
|
||||
/// @returns Returns the inner radius of the @p skinnable
|
||||
static Q_REQUIRED_RESULT float radius2(const QskSkinnable* const skinnable);
|
||||
/// @returns Returns the outer radius of the @p skinnable
|
||||
static Q_REQUIRED_RESULT float radius1(const QskSkinnable* const skinnable);
|
||||
/// @returns Returns the center point of the control
|
||||
static Q_REQUIRED_RESULT 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 LevelingSensor* sensor,
|
||||
const QRectF& contentsRect) const;
|
||||
|
||||
template<NodeRole>
|
||||
Q_REQUIRED_RESULT QSGNode* updateSubNode(const LevelingSensor* sensor,
|
||||
quint8 nodeRole, QSGNode* node) const;
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "QskLevelingSensorUtility.h"
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include <qmath.h>
|
||||
#include <qmatrix4x4.h>
|
||||
|
||||
#include <QskScaleTickmarks.h>
|
||||
|
||||
// create a homogenous transformation matrix
|
||||
inline Q_REQUIRED_RESULT QMatrix4x4 matrix_deg(
|
||||
float rX = 0.0f,
|
||||
float rY = 0.0f,
|
||||
float rZ = 0.0f,
|
||||
float tX = 0.0f,
|
||||
float tY = 0.0f,
|
||||
float tZ = 0.0f
|
||||
)
|
||||
{
|
||||
// Convert rotation angles to radians
|
||||
float rotationX = qDegreesToRadians(rX);
|
||||
float rotationY = qDegreesToRadians(rY);
|
||||
float rotationZ = qDegreesToRadians(rZ);
|
||||
|
||||
// Calculate sin and cos of the rotation angles
|
||||
float cosX = qCos(rotationX);
|
||||
float sinX = qSin(rotationX);
|
||||
float cosY = qCos(rotationY);
|
||||
float sinY = qSin(rotationY);
|
||||
float cosZ = qCos(rotationZ);
|
||||
float sinZ = qSin(rotationZ);
|
||||
|
||||
// Create the transform matrix
|
||||
return QMatrix4x4(
|
||||
cosY * cosZ, sinX * sinY * cosZ - cosX * sinZ, cosX * sinY * cosZ + sinX * sinZ, tX,
|
||||
cosY * sinZ, sinX * sinY * sinZ + cosX * cosZ, cosX * sinY * sinZ - sinX * cosZ, tY,
|
||||
-sinY, sinX * cosY, cosX * cosY, tZ,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool compare_exchange(T& dst, const T& src)
|
||||
{
|
||||
if (dst != src)
|
||||
{
|
||||
dst = src;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool compare_exchange<float>(float& dst, const float& src)
|
||||
{
|
||||
if (!qFuzzyCompare(dst, src))
|
||||
{
|
||||
dst = src;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool compare_exchange<qreal>(qreal& dst, const qreal& src)
|
||||
{
|
||||
if (!qFuzzyCompare(dst, src))
|
||||
{
|
||||
dst = src;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <QSGNode>
|
||||
|
||||
#include "QskLevelingSensorUtility.h"
|
||||
|
||||
#define ASSERT_TRUE(expr) if(!(expr)) { std::cerr << "ASSERT_TRUE(!" << #expr << "):" << __LINE__ << '\n'; return false; }
|
||||
|
||||
class QSGNodeBase : public QSGNode
|
||||
{
|
||||
public:
|
||||
virtual std::string name() const = 0;
|
||||
};
|
||||
|
||||
class QSGNodeA final : public QSGNodeBase{ public: QSGNodeA() { std::cout << name() << '\n'; } std::string name() const override { return "A"; }};
|
||||
class QSGNodeB final : public QSGNodeBase{ public: QSGNodeB() { std::cout << name() << '\n'; } std::string name() const override { return "B"; }};
|
||||
class QSGNodeC final : public QSGNodeBase{ public: QSGNodeC() { std::cout << name() << '\n'; } std::string name() const override { return "C"; }};
|
||||
class QSGNodeD final : public QSGNodeBase{ public: QSGNodeD() { std::cout << name() << '\n'; } std::string name() const override { return "D"; }};
|
||||
class QSGNodeE final : public QSGNodeBase{ public: QSGNodeE() { std::cout << name() << '\n'; } std::string name() const override { return "E"; }};
|
||||
class QSGNodeF final : public QSGNodeBase{ public: QSGNodeF() { std::cout << name() << '\n'; } std::string name() const override { return "F"; }};
|
||||
class QSGNodeG final : public QSGNodeBase{ public: QSGNodeG() { std::cout << name() << '\n'; } std::string name() const override { return "G"; }};
|
||||
class QSGNodeH final : public QSGNodeBase{ public: QSGNodeH() { std::cout << name() << '\n'; } std::string name() const override { return "H"; }};
|
||||
|
||||
using NodeType = QSGNode;
|
||||
|
||||
bool testcase_ensure_node()
|
||||
{
|
||||
NodeType* root = nullptr;
|
||||
ASSERT_TRUE(root = ensure_node<NodeType>());
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
delete root;
|
||||
return true;
|
||||
}
|
||||
|
||||
// seq<par<seq<seq<N<QSGNodeD, 4>>
|
||||
|
||||
/*
|
||||
|
||||
bool testcase_seq()
|
||||
{
|
||||
using namespace qsg;
|
||||
{
|
||||
auto* const root = ensure_node<NodeType>();
|
||||
seq<N<QSGNodeA>, N<QSGNodeB>, N<QSGNodeC>>::append(root);
|
||||
delete root;
|
||||
}
|
||||
{
|
||||
auto* const root = ensure_node<NodeType>();
|
||||
ASSERT_TRUE(root);
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
qsg::seq<>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
delete root;
|
||||
}
|
||||
{
|
||||
auto* const root = ensure_node<NodeType>();
|
||||
ASSERT_TRUE(root);
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
qsg::seq<N<QSGNodeA>>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 1);
|
||||
delete root;
|
||||
}
|
||||
{
|
||||
auto* const root = ensure_node<NodeType>();
|
||||
ASSERT_TRUE(root);
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
qsg::seq<N<QSGNodeA>, N<QSGNodeB>>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 2);
|
||||
delete root;
|
||||
}
|
||||
{
|
||||
// - QSGNode
|
||||
// - QSGNodeA
|
||||
// - QSGNodeB
|
||||
// - QSGNodeC
|
||||
auto* const root = ensure_node<NodeType>();
|
||||
ASSERT_TRUE(root);
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
qsg::seq<N<QSGNodeA>, N<QSGNodeB>, N<QSGNodeC>>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 3);
|
||||
delete root;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
bool testcase_par()
|
||||
{
|
||||
using namespace qsg;
|
||||
{
|
||||
std::cout << __func__ << 0 << '\n';
|
||||
//auto* const root = ensure_node<QSGNode>();
|
||||
//ASSERT_TRUE(root->childCount() == 0);
|
||||
//par<4, QSGNodeA, par<4, QSGNodeB>>::append(root);
|
||||
//ASSERT_TRUE(root->childCount() == 4);
|
||||
//for(int i = 0; i < 4; ++i)
|
||||
//{
|
||||
// ASSERT_TRUE(root->childAtIndex(i)->childCount() == 4);
|
||||
//}
|
||||
//delete root;
|
||||
}
|
||||
{
|
||||
std::cout << __func__ << 1 << '\n';
|
||||
// - QSGNode
|
||||
// - QSGNodeA
|
||||
// - QSGNodeB
|
||||
// - QSGNodeC
|
||||
// - QSGNodeD
|
||||
// - QSGNodeA
|
||||
// - QSGNodeB
|
||||
// - QSGNodeC
|
||||
// - QSGNodeD
|
||||
// - QSGNodeA
|
||||
// - QSGNodeB
|
||||
// - QSGNodeC
|
||||
// - QSGNodeD
|
||||
// - QSGNodeA
|
||||
// - QSGNodeB
|
||||
// - QSGNodeC
|
||||
// - QSGNodeD
|
||||
auto* const root = ensure_node<QSGNode>();
|
||||
ASSERT_TRUE(root->childCount() == 0);
|
||||
par<4, QSGNodeA,
|
||||
seq<par<1, QSGNodeB>,
|
||||
par<1, QSGNodeC>,
|
||||
par<1, QSGNodeD>>>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 4);
|
||||
ASSERT_TRUE(root->childAtIndex(0)->childCount() == 3);
|
||||
ASSERT_TRUE(root->childAtIndex(1)->childCount() == 3);
|
||||
ASSERT_TRUE(root->childAtIndex(2)->childCount() == 3);
|
||||
ASSERT_TRUE(root->childAtIndex(3)->childCount() == 3);
|
||||
}
|
||||
{
|
||||
std::cout << __func__ << 3 << '\n';
|
||||
auto* const root = ensure_node<QSGNode>();
|
||||
seq<
|
||||
par<1, QSGNodeE,
|
||||
seq<
|
||||
par<1, QSGNodeA>,
|
||||
par<1, QSGNodeB>,
|
||||
par<1, QSGNodeC>>>,
|
||||
par<1, QSGNodeF,
|
||||
seq<
|
||||
par<1, QSGNodeB>,
|
||||
par<1, QSGNodeC>,
|
||||
par<1, QSGNodeA>>>,
|
||||
par<1, QSGNodeG,
|
||||
seq<
|
||||
par<1, QSGNodeC>,
|
||||
par<1, QSGNodeA>,
|
||||
par<1, QSGNodeB>>>
|
||||
>::append(root);
|
||||
ASSERT_TRUE(root->childCount() == 3);
|
||||
for(int i = 0; i < root->childCount(); ++i)
|
||||
{
|
||||
ASSERT_TRUE(root->childAtIndex(i)->childCount() == 3);
|
||||
for(int j = 0; j < root->childAtIndex(i)->childAtIndex(i)->childCount(); ++j)
|
||||
{
|
||||
ASSERT_TRUE(root->childAtIndex(i)->childCount() == 0);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeE*>(root->childAtIndex(0)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeA*>(root->childAtIndex(0)->childAtIndex(0)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeB*>(root->childAtIndex(0)->childAtIndex(1)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeC*>(root->childAtIndex(0)->childAtIndex(2)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeF*>(root->childAtIndex(1)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeB*>(root->childAtIndex(1)->childAtIndex(0)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeC*>(root->childAtIndex(1)->childAtIndex(1)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeA*>(root->childAtIndex(1)->childAtIndex(2)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeG*>(root->childAtIndex(2)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeC*>(root->childAtIndex(2)->childAtIndex(0)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeA*>(root->childAtIndex(2)->childAtIndex(1)));
|
||||
ASSERT_TRUE(dynamic_cast<QSGNodeB*>(root->childAtIndex(2)->childAtIndex(2)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
bool result = true;
|
||||
// result &= testcase_ensure_node();
|
||||
// result &= testcase_seq();
|
||||
result &= testcase_par();
|
||||
std::cout << (result ? "ok" : "failed") << std::endl;
|
||||
return result ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Loading…
Reference in New Issue