qskinny/src/controls/LevelingSensor/QskLevelingSensorSkinlet.cpp

416 lines
16 KiB
C++
Raw Normal View History

2023-07-10 13:54:53 +00:00
#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"