qskinny/src/nodes/QskLinesNode.cpp

450 lines
12 KiB
C++
Raw Normal View History

2023-05-17 12:21:40 +00:00
/******************************************************************************
2024-01-17 13:31:45 +00:00
* QSkinny - Copyright (C) The authors
2023-05-17 12:21:40 +00:00
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskLinesNode.h"
#include "QskVertex.h"
#include "QskStippleMetrics.h"
2023-05-25 13:03:48 +00:00
#include "QskStippledLineRenderer.h"
2023-05-17 12:21:40 +00:00
#include "QskSGNode.h"
2024-09-24 08:14:26 +00:00
#include "QskRgbValue.h"
2023-05-17 12:21:40 +00:00
#include <qtransform.h>
2023-05-25 13:03:48 +00:00
#include <qline.h>
2023-05-17 12:21:40 +00:00
namespace
{
inline qreal mapX( const QTransform& t, qreal x )
{
return t.dx() + t.m11() * x;
}
inline qreal mapY( const QTransform& t, qreal y )
{
return t.dy() + t.m22() * y;
}
2023-05-25 13:03:48 +00:00
class Renderer : public QskStippledLineRenderer
2023-05-17 12:21:40 +00:00
{
public:
2023-05-25 13:03:48 +00:00
inline Renderer( const QskStippleMetrics& metrics )
: QskStippledLineRenderer( metrics )
2023-05-17 12:21:40 +00:00
{
}
2023-05-25 13:03:48 +00:00
inline QSGGeometry::Point2D* addDashes( QSGGeometry::Point2D* points,
2023-05-17 12:21:40 +00:00
qreal x1, qreal y1, qreal x2, qreal y2 )
{
m_points = points;
2023-05-25 13:03:48 +00:00
renderLine( x1, y1, x2, y2 );
return m_points;
}
private:
void renderDash( qreal x1, qreal y1, qreal x2, qreal y2 ) override
{
m_points++->set( x1, y1 );
m_points++->set( x2, y2 );
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
QSGGeometry::Point2D* m_points;
};
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
static QSGGeometry::Point2D* qskAddDashes( const QTransform& transform,
int count, const QLineF* lines, const QskStippleMetrics& metrics,
QSGGeometry::Point2D* points )
{
if ( count <= 0 )
return points;
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
const bool doTransform = !transform.isIdentity();
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
Renderer renderer( metrics );
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
for ( int i = 0; i < count; i++ )
{
auto p1 = lines[i].p1();
auto p2 = lines[i].p2();
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
if ( doTransform )
2023-05-17 12:21:40 +00:00
{
2023-05-25 13:03:48 +00:00
p1 = transform.map( p1 );
p2 = transform.map( p2 );
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
points = renderer.addDashes( points, p1.x(), p1.y(), p2.x(), p2.y() );
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
return points;
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
static QSGGeometry::Point2D* qskAddLines( const QTransform& transform,
int count, const QLineF* lines, QSGGeometry::Point2D* points )
{
if ( count <= 0 )
return points;
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
const bool doTransform = !transform.isIdentity();
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
auto vlines = reinterpret_cast< QskVertex::Line* >( points );
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
for ( int i = 0; i < count; i++ )
{
auto p1 = lines[i].p1();
auto p2 = lines[i].p2();
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
if ( doTransform )
2023-05-17 12:21:40 +00:00
{
2023-05-25 13:03:48 +00:00
p1 = transform.map( p1 );
p2 = transform.map( p2 );
2023-05-17 12:21:40 +00:00
}
2023-05-25 13:03:48 +00:00
vlines++->setLine( p1.x(), p1.y(), p2.x(), p2.y() );
}
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
return reinterpret_cast< QSGGeometry::Point2D* >( vlines );
2023-05-17 12:21:40 +00:00
}
QskLinesNode::QskLinesNode()
{
}
QskLinesNode::~QskLinesNode()
{
}
2023-05-25 13:03:48 +00:00
void QskLinesNode::updateRect( const QColor& color,
2023-05-17 14:46:25 +00:00
qreal lineWidth, const QskStippleMetrics& stippleMetrics,
const QTransform& transform, const QRectF& rect )
{
// using QVarLengthArray instead. TODO ...
2023-05-25 13:03:48 +00:00
updateGrid( color, lineWidth, stippleMetrics, transform,
2023-05-17 14:46:25 +00:00
rect, { rect.left(), rect.right() }, { rect.top(), rect.bottom() } );
}
void QskLinesNode::updateLine( const QColor& color,
qreal lineWidth, const QPointF& p1, const QPointF& p2 )
{
updateLine( color, lineWidth, QskStippleMetrics(), QTransform(), p1, p2 );
}
2023-05-25 13:03:48 +00:00
void QskLinesNode::updateLine( const QColor& color,
qreal lineWidth, const QskStippleMetrics& stippleMetrics,
const QTransform& transform, const QPointF& p1, const QPointF& p2 )
{
if ( p1 == p2 )
{
updateLines( color, lineWidth, stippleMetrics, transform, 0, nullptr );
}
else
{
const QLineF line( p1, p2 );
updateLines( color, lineWidth, stippleMetrics, transform, 1, &line );
}
}
void QskLinesNode::updateLines( const QColor& color,
qreal lineWidth, const QVector< QLineF >& lines )
{
updateLines( color, lineWidth, QskStippleMetrics(),
QTransform(), lines.count(), lines.constData() );
}
2023-05-25 13:03:48 +00:00
void QskLinesNode::updateLines( const QColor& color,
qreal lineWidth, const QskStippleMetrics& stippleMetrics,
const QTransform& transform, const QVector< QLineF >& lines )
{
updateLines( color, lineWidth, stippleMetrics,
transform, lines.count(), lines.constData() );
}
2023-05-17 14:46:25 +00:00
void QskLinesNode::updateLines( const QColor& color,
2023-05-25 13:03:48 +00:00
qreal lineWidth, const QskStippleMetrics& stippleMetrics,
const QTransform& transform, int count, const QLineF* lines )
{
2024-09-24 08:14:26 +00:00
if ( !stippleMetrics.isValid() || !QskRgb::isVisible( color ) || count == 0 )
2023-05-25 13:03:48 +00:00
{
QskSGNode::resetGeometry( this );
return;
}
QskHashValue hash = 9784;
hash = stippleMetrics.hash( hash );
hash = qHash( transform, hash );
hash = qHashBits( lines, count * sizeof( QLineF ) );
if ( hash != m_hash )
2023-05-25 13:03:48 +00:00
{
m_hash = hash;
2023-05-25 13:03:48 +00:00
updateGeometry( stippleMetrics, transform, count, lines );
geometry()->markVertexDataDirty();
2023-05-25 13:03:48 +00:00
markDirty( QSGNode::DirtyGeometry );
}
setLineWidth( lineWidth );
setColor( color );
2023-05-25 13:03:48 +00:00
}
void QskLinesNode::updateGrid( const QColor& color,
2023-05-17 14:46:25 +00:00
qreal lineWidth, const QskStippleMetrics& stippleMetrics,
const QTransform& transform, const QRectF& rect,
const QVector< qreal >& xValues, const QVector< qreal >& yValues )
2023-05-17 12:21:40 +00:00
{
2024-09-24 08:14:26 +00:00
if ( !stippleMetrics.isValid() || !QskRgb::isVisible( color ) )
2023-05-17 12:21:40 +00:00
{
2023-05-25 13:03:48 +00:00
QskSGNode::resetGeometry( this );
return;
2023-05-17 12:21:40 +00:00
}
QskHashValue hash = 9784;
hash = stippleMetrics.hash( hash );
hash = qHash( transform, hash );
2023-05-17 14:46:25 +00:00
hash = qHashBits( &rect, sizeof( QRectF ), hash );
2023-05-17 12:21:40 +00:00
hash = qHash( xValues, hash );
hash = qHash( yValues, hash );
if ( hash != m_hash )
2023-05-17 12:21:40 +00:00
{
m_hash = hash;
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
updateGeometry( stippleMetrics, transform, rect, xValues, yValues );
2023-05-17 12:21:40 +00:00
geometry()->markVertexDataDirty();
2023-05-17 12:21:40 +00:00
markDirty( QSGNode::DirtyGeometry );
}
setLineWidth( lineWidth );
setColor( color );
2023-05-25 13:03:48 +00:00
}
void QskLinesNode::updateGeometry( const QskStippleMetrics& stippleMetrics,
const QTransform& transform, int count, const QLineF* lines )
{
auto& geom = *geometry();
2023-05-25 13:03:48 +00:00
QSGGeometry::Point2D* points = nullptr;
if ( stippleMetrics.isSolid() )
{
using namespace QskVertex;
geom.allocate( 2 * count );
points = geom.vertexDataAsPoint2D();
points = qskAddLines( transform, count, lines, points );
}
else
{
const bool doTransform = !transform.isIdentity();
Renderer renderer( stippleMetrics );
int lineCount = 0;
for ( int i = 0; i < count; i++ )
{
auto p1 = lines[i].p1();
auto p2 = lines[i].p2();
if ( doTransform )
{
p1 = transform.map( p1 );
p2 = transform.map( p2 );
}
lineCount += renderer.dashCount( p1, p2 );
}
geometry()->allocate( 2 * lineCount );
points = geometry()->vertexDataAsPoint2D();
2023-05-25 13:03:48 +00:00
points = qskAddDashes( transform,
count, lines, stippleMetrics, points );
}
Q_ASSERT( geom.vertexCount() == ( points - geom.vertexDataAsPoint2D() ) );
2023-05-17 12:21:40 +00:00
}
void QskLinesNode::updateGeometry(
2023-05-17 14:46:25 +00:00
const QskStippleMetrics& stippleMetrics,
const QTransform& transform, const QRectF& rect,
const QVector< qreal >& xValues, const QVector< qreal >& yValues )
2023-05-17 12:21:40 +00:00
{
auto& geom = *geometry();
2023-05-17 14:46:25 +00:00
const auto y1 = mapY( transform, rect.top() );
const auto y2 = mapY( transform, rect.bottom() );
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
const auto x1 = mapX( transform, rect.left() );
const auto x2 = mapX( transform, rect.right() );
QSGGeometry::Point2D* points = nullptr;
2023-05-17 12:21:40 +00:00
if ( stippleMetrics.isSolid() )
{
using namespace QskVertex;
2023-05-17 14:46:25 +00:00
geom.allocate( 2 * ( xValues.count() + yValues.count() ) );
points = geom.vertexDataAsPoint2D();
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
points = setSolidLines( Qt::Vertical, y1, y2,
transform, xValues.count(), xValues.constData(), points );
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
points = setSolidLines( Qt::Horizontal, x1, x2,
transform, yValues.count(), yValues.constData(), points );
2023-05-17 12:21:40 +00:00
}
else
{
2023-05-25 13:03:48 +00:00
Renderer renderer( stippleMetrics );
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
const auto countX = renderer.dashCount( 0.0, y1, 0.0, y2 );
const auto countY = renderer.dashCount( x1, 0.0, x2, 0.0 );
const auto count = xValues.count() * countX + yValues.count() * countY;
2023-05-17 12:21:40 +00:00
geometry()->allocate( 2 * count );
points = geometry()->vertexDataAsPoint2D();
2023-05-17 14:46:25 +00:00
points = setStippledLines( Qt::Vertical, y1, y2,
transform, xValues.count(), xValues.constData(),
stippleMetrics, points );
points = setStippledLines( Qt::Horizontal, x1, x2,
transform, yValues.count(), yValues.constData(),
stippleMetrics, points );
}
Q_ASSERT( geom.vertexCount() == ( points - geom.vertexDataAsPoint2D() ) );
}
QSGGeometry::Point2D* QskLinesNode::setStippledLines(
Qt::Orientation orientation, qreal v1, qreal v2,
const QTransform& transform, int count, const qreal* values,
const QskStippleMetrics& stippleMetrics, QSGGeometry::Point2D* points ) const
{
if ( count <= 0 )
return points;
2023-05-25 13:03:48 +00:00
Renderer renderer( stippleMetrics );
2023-05-17 14:46:25 +00:00
// Calculating the dashes for the first line
const auto line0 = points;
int dashCount = 0;
if ( orientation == Qt::Vertical )
{
const auto x = mapX( transform, values[0] );
2023-05-17 14:46:25 +00:00
2023-05-25 13:03:48 +00:00
points = renderer.addDashes( points, x, v1, x, v2 );
2023-05-17 14:46:25 +00:00
dashCount = points - line0;
}
else
{
const auto y = mapY( transform, values[0] );
2023-05-17 12:21:40 +00:00
2023-05-25 13:03:48 +00:00
points = renderer.addDashes( points, v1, y, v2, y );
2023-05-17 14:46:25 +00:00
dashCount = points - line0;
}
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
// all other dashes are translations of the dashes of the first line
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
if ( orientation == Qt::Vertical )
{
for ( int i = 1; i < count; i++ )
2023-05-17 12:21:40 +00:00
{
const auto x = mapX( transform, values[i] );
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
for ( int j = 0; j < dashCount; j++ )
points++->set( x, line0[j].y );
}
}
else
{
for ( int i = 1; i < count; i++ )
{
const auto y = mapY( transform, values[i] );
2023-05-17 14:46:25 +00:00
for ( int j = 0; j < dashCount; j++ )
points++->set( line0[j].x, y );
2023-05-17 12:21:40 +00:00
}
2023-05-17 14:46:25 +00:00
}
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
return points;
}
2023-05-17 12:21:40 +00:00
2023-05-17 14:46:25 +00:00
QSGGeometry::Point2D* QskLinesNode::setSolidLines(
Qt::Orientation orientation, qreal v1, qreal v2,
const QTransform& transform, int count, const qreal* values,
QSGGeometry::Point2D* points ) const
{
if ( count <= 0 )
return points;
auto lines = reinterpret_cast< QskVertex::Line* >( points );
if ( orientation == Qt::Vertical )
{
for ( int i = 0; i < count; i++ )
2023-05-17 12:21:40 +00:00
{
const auto x = mapX( transform, values[i] );
2023-05-17 14:46:25 +00:00
lines++->setVLine( x, v1, v2 );
}
}
else
{
for ( int i = 0; i < count; i++ )
{
const auto y = mapY( transform, values[i] );
2023-05-17 14:46:25 +00:00
lines++->setHLine( v1, v2, y );
2023-05-17 12:21:40 +00:00
}
}
2023-05-17 14:46:25 +00:00
return reinterpret_cast< QSGGeometry::Point2D* >( lines );
2023-05-17 12:21:40 +00:00
}
2023-11-15 10:47:56 +00:00
void QskLinesNode::updatePolygon( const QColor& color, qreal lineWidth,
const QTransform& transform, const QPolygonF& polygon )
{
2024-09-24 08:14:26 +00:00
if ( polygon.isEmpty() || !QskRgb::isVisible( color ) )
2023-11-15 10:47:56 +00:00
{
QskSGNode::resetGeometry( this );
return;
}
if ( true ) // for the moment we always update the geometry. TODO ...
{
geometry()->allocate( polygon.count() + 1 );
2023-11-15 10:47:56 +00:00
auto points = geometry()->vertexDataAsPoint2D();
2023-11-15 10:47:56 +00:00
if ( transform.isIdentity() )
{
for ( int i = 0; i < polygon.count(); i++ )
{
const auto& pos = polygon[i];
points[i].set( pos.x(), pos.y() );
}
}
else
{
for ( int i = 0; i < polygon.count(); i++ )
{
const auto pos = transform.map( polygon[i] );
points[i].set( pos.x(), pos.y() );
}
}
points[ polygon.count() ] = points[0];
geometry()->markVertexDataDirty();
2023-11-15 10:47:56 +00:00
markDirty( QSGNode::DirtyGeometry );
}
setLineWidth( lineWidth );
setColor( color );
2023-11-15 10:47:56 +00:00
}