qskinny/playground/plots/QskPlotCorridorSkinlet.cpp

262 lines
7.6 KiB
C++
Raw Normal View History

2023-11-28 13:36:47 +01:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskPlotCorridorSkinlet.h"
#include "QskPlotCorridor.h"
#include "QskPlotCorridorData.h"
#include <QskSGNode.h>
#include <QskVertex.h>
#include <qvector.h>
#include <qsgvertexcolormaterial.h>
namespace
{
class GeometryNode : public QSGGeometryNode
{
protected:
GeometryNode()
: m_geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 )
{
setGeometry( &m_geometry );
setMaterial( &m_material );
}
private:
QSGGeometry m_geometry;
QSGVertexColorMaterial m_material;
};
class CorridorNode : public GeometryNode
{
public:
void updateCorridor( const QskPlotCorridorData* data,
const QskPlotCorridorSample& sample1, int index1,
const QskPlotCorridorSample& sample2, int index2,
const QColor& color )
{
using namespace QskVertex;
const Color vertexColor( color );
geometry()->setDrawingMode( QSGGeometry::DrawTriangleStrip );
auto line = allocateLines< ColoredLine >( *geometry(), index2 - index1 + 1 );
line++->setLine( sample1.value, sample1.boundary.lowerBound(),
sample1.value, sample1.boundary.upperBound(), vertexColor );
for ( int i = index1 + 1; i < index2; i++ )
{
const auto sample = data->sampleAt( i );
line++->setLine( sample.value, sample.boundary.lowerBound(),
sample.value, sample.boundary.upperBound(), vertexColor );
}
line++->setLine( sample2.value, sample2.boundary.lowerBound(),
sample2.value, sample2.boundary.upperBound(), vertexColor );
markDirty( QSGNode::DirtyGeometry );
}
};
class BorderNode : public GeometryNode
{
public:
void updateBorder( const QskPlotCorridorData* data,
const QskPlotCorridorSample& sample1, int index1,
const QskPlotCorridorSample& sample2, int index2,
quint8 nodeRole, const QColor& color, qreal lineWidth )
{
auto& geometry = *this->geometry();
geometry.setDrawingMode( QSGGeometry::DrawLineStrip );
const float lineWidthF = lineWidth;
if( lineWidthF != geometry.lineWidth() )
geometry.setLineWidth( lineWidthF );
const QskVertex::Color c( color );
geometry.allocate( index2 - index1 + 1 );
auto p = geometry.vertexDataAsColoredPoint2D();
if( nodeRole == QskPlotCorridorSkinlet::LowerBoundRole )
{
p++->set( sample1.value, sample1.boundary.lowerBound(),
c.r, c.g, c.b, c.a );
for ( int i = index1 + 1; i < index2; i++ )
{
const auto sample = data->sampleAt( i );
p++->set( sample.value, sample.boundary.lowerBound(),
c.r, c.g, c.b, c.a );
}
p++->set( sample2.value, sample2.boundary.lowerBound(),
c.r, c.g, c.b, c.a );
}
else
{
p++->set( sample1.value, sample1.boundary.upperBound(),
c.r, c.g, c.b, c.a );
for ( int i = index1 + 1; i < index2; i++ )
{
const auto sample = data->sampleAt( i );
p++->set( sample.value, sample.boundary.upperBound(),
c.r, c.g, c.b, c.a );
}
p++->set( sample2.value, sample2.boundary.upperBound(),
c.r, c.g, c.b, c.a );
}
markDirty( QSGNode::DirtyGeometry );
}
};
}
class QskPlotCorridorSkinlet::PrivateData
{
public:
int index1, index2;
QskPlotCorridorSample sample1, sample2;
};
QskPlotCorridorSkinlet::QskPlotCorridorSkinlet( QskSkin* skin )
: Inherited( skin )
, m_data( new PrivateData )
{
setNodeRoles( { CorridorRole, LowerBoundRole, UpperBoundRole } );
}
QskPlotCorridorSkinlet::~QskPlotCorridorSkinlet()
{
}
void QskPlotCorridorSkinlet::updateNode(
QskSkinnable* skinnable, QSGNode* parent ) const
{
/*
As clipping is the same for borders and corridor
we do it only once here
*/
auto corridor = static_cast< const QskPlotCorridor* >( skinnable );
const auto data = corridor->data();
m_data->index1 = m_data->index2 = -1;
const auto n = data->count();
if ( data && n > 0 )
{
auto& s1 = m_data->sample1;
auto& s2 = m_data->sample2;
s1 = data->sampleAt( 0 );
s2 = data->sampleAt( n - 1 );
const auto scaleRect = corridor->scaleRect();
const qreal x1 = scaleRect.left();
const qreal x2 = scaleRect.right();
if ( !( x1 > s2.value || x2 < s1.value ) )
{
m_data->index1 = 0;
m_data->index2 = n - 1;
const int index1 = data->upperIndex( x1 );
if ( index1 > 0 )
{
m_data->index1 = index1 - 1;
s1 = data->interpolatedSample( x1 );
}
const int index2 = data->upperIndex( x2 );
if ( index2 > 0 )
{
m_data->index2 = index2;
s2 = data->interpolatedSample( x2 );
}
}
}
Inherited::updateNode( skinnable, parent );
}
QSGNode* QskPlotCorridorSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
if ( m_data->index2 < 0 )
return nullptr;
if ( nodeRole == CorridorRole )
return updateCorridorNode( skinnable, node );
else
return updateBorderNode( skinnable, nodeRole, node );
}
QSGNode* QskPlotCorridorSkinlet::updateCorridorNode(
const QskSkinnable* skinnable, QSGNode* node ) const
{
using Q = QskPlotCorridor;
auto corridor = static_cast< const QskPlotCorridor* >( skinnable );
const auto color = corridor->color( Q::Corridor );
if ( !color.isValid() || color.alpha() == 0 )
return nullptr;
const auto corridorData = corridor->data();
if ( corridorData->count() == 0 )
return nullptr;
auto corridorNode = QskSGNode::ensureNode< CorridorNode >( node );
corridorNode->updateCorridor( corridorData,
m_data->sample1, m_data->index1, m_data->sample2, m_data->index2, color );
return corridorNode;
}
QSGNode* QskPlotCorridorSkinlet::updateBorderNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
using Q = QskPlotCorridor;
using A = QskAspect;
auto corridor = static_cast< const QskPlotCorridor* >( skinnable );
const auto corridorData = corridor->data();
if ( corridorData->count() == 0 )
return nullptr;
QColor color;
if( nodeRole == QskPlotCorridorSkinlet::LowerBoundRole )
color = corridor->color( Q::Border | A::Lower );
else
color = corridor->color( Q::Border | A::Upper );
if ( !color.isValid() || color.alpha() == 0 )
return nullptr;
auto lineWidth = corridor->metric( Q::Border | A::Size );
lineWidth = qMax( lineWidth, 0.0 );
auto borderNode = QskSGNode::ensureNode< BorderNode >( node );
borderNode->updateBorder( corridorData,
m_data->sample1, m_data->index1, m_data->sample2, m_data->index2,
nodeRole, color, lineWidth );
return borderNode;
}
#include "moc_QskPlotCorridorSkinlet.cpp"