working towards gradients

This commit is contained in:
Uwe Rathmann 2024-06-04 13:26:49 +02:00
parent 36f088a263
commit bf7fe7c8f3
2 changed files with 69 additions and 79 deletions

View File

@ -103,7 +103,7 @@ ShadowedArc::ShadowedArc( QQuickItem* parent )
//setFillColor( Qt::darkRed ); //setFillColor( Qt::darkRed );
const QskGradient gradient( Qt::darkRed, Qt::darkBlue ); const QskGradient gradient( Qt::darkRed, Qt::darkYellow );
setGradientHint( Arc, gradient ); setGradientHint( Arc, gradient );
setBorderWidth( 0 ); setBorderWidth( 0 );

View File

@ -7,6 +7,7 @@
#include "QskArcMetrics.h" #include "QskArcMetrics.h"
#include "QskGradient.h" #include "QskGradient.h"
#include "QskVertex.h" #include "QskVertex.h"
#include "QskBoxColorMap.h"
#include <qsggeometry.h> #include <qsggeometry.h>
#include <qdebug.h> #include <qdebug.h>
@ -40,23 +41,25 @@ namespace
inline void operator++() { increment(); } inline void operator++() { increment(); }
void increment(); void increment();
inline double cos() const { return m_cos; } inline qreal cos() const { return m_cos; }
inline double sin() const { return m_sin; } inline qreal sin() const { return m_sin; }
inline int step() const { return m_stepIndex; } inline int step() const { return m_stepIndex; }
inline int stepCount() const { return m_stepCount; } inline int stepCount() const { return m_stepCount; }
inline bool isDone() const { return m_stepIndex >= m_stepCount; } inline bool isDone() const { return m_stepIndex >= m_stepCount; }
inline qreal progress() const { return qreal( m_stepIndex ) / m_stepCount; }
private: private:
double m_cos; qreal m_cos;
double m_sin; qreal m_sin;
int m_stepIndex; int m_stepIndex;
int m_stepCount; int m_stepCount;
const double m_radians1; const qreal m_radians1;
const double m_radians2; const qreal m_radians2;
const double m_radiansStep; const qreal m_radiansStep;
}; };
inline AngleIterator::AngleIterator( qreal radians1, qreal radians2, int stepCount ) inline AngleIterator::AngleIterator( qreal radians1, qreal radians2, int stepCount )
@ -95,16 +98,17 @@ namespace
class EllipseStroker class EllipseStroker
{ {
public: public:
EllipseStroker( const QRectF&, const QskArcMetrics&, qreal borderWidth ); EllipseStroker( const QRectF&, const QskArcMetrics&,
qreal borderWidth, const QskGradient& );
int fillCount() const; int fillCount() const;
int borderCount() const; int borderCount() const;
int setBorderLines( QskVertex::ColoredLine*, const QskVertex::Color ) const; int setBorderLines( QskVertex::ColoredLine*, const QskVertex::Color ) const;
int setFillLines( QskVertex::ColoredLine*, const QskVertex::Color ) const; int setFillLines( QskVertex::ColoredLine* ) const;
private: private:
int arcLineCount( qreal radians ) const; int arcLineCount() const;
// radius // radius
const qreal m_rx; const qreal m_rx;
@ -116,50 +120,39 @@ namespace
const qreal m_radians1; const qreal m_radians1;
const qreal m_radians2; const qreal m_radians2;
const bool m_inverted;
const qreal m_extent; const qreal m_extent;
const QskGradient& m_gradient;
}; };
EllipseStroker::EllipseStroker( const QRectF& rect, EllipseStroker::EllipseStroker( const QRectF& rect,
const QskArcMetrics& m, qreal borderWidth ) const QskArcMetrics& m, qreal borderWidth, const QskGradient& gradient )
: m_rx( 0.5 * rect.width() ) : m_rx( 0.5 * rect.width() )
, m_ry( 0.5 * rect.height() ) , m_ry( 0.5 * rect.height() )
, m_cx( rect.x() + m_rx ) , m_cx( rect.x() + m_rx )
, m_cy( rect.y() + m_ry ) , m_cy( rect.y() + m_ry )
, m_radians1( qDegreesToRadians( qMin( m.startAngle(), m.endAngle() ) ) ) , m_radians1( qDegreesToRadians( m.startAngle() ) )
, m_radians2( qDegreesToRadians( qMax( m.startAngle(), m.endAngle() ) ) ) , m_radians2( qDegreesToRadians( m.endAngle() ) )
, m_inverted( m.spanAngle() >= 0.0 )
, m_extent( 0.5 * ( m.thickness() - borderWidth ) ) , m_extent( 0.5 * ( m.thickness() - borderWidth ) )
, m_gradient( gradient )
{ {
} }
int EllipseStroker::fillCount() const int EllipseStroker::fillCount() const
{ {
int n = 0; return arcLineCount();
for ( auto r = qFloor( m_radians1 / M_PI_2 ) * M_PI_2;
r < m_radians2; r += M_PI_2 )
{
const auto r1 = qMax( r, m_radians1 );
const auto r2 = qMin( r + M_PI_2, m_radians2 );
n += arcLineCount( r2 - r1 );
}
return n;
} }
int EllipseStroker::arcLineCount( qreal radians ) const int EllipseStroker::arcLineCount() const
{ {
Q_ASSERT( radians >= 0.0 );
// not very sophisticated - TODO ... // not very sophisticated - TODO ...
const auto radius = qMax( m_rx, m_ry ); const auto radius = qMax( m_rx, m_ry );
const auto radians = qAbs( m_radians2 - m_radians1 );
const auto count = qCeil( ( radius * radians ) / 3.0 ); const auto count = qCeil( ( radius * radians ) / 3.0 );
return qBound( 3, count, 40 ); return qBound( 3, count, 160 );
} }
int EllipseStroker::borderCount() const int EllipseStroker::borderCount() const
@ -176,8 +169,7 @@ namespace
return 0; return 0;
} }
int EllipseStroker::setFillLines( QskVertex::ColoredLine* lines, int EllipseStroker::setFillLines( QskVertex::ColoredLine* lines ) const
const QskVertex::Color color ) const
{ {
const auto w = m_rx - m_extent; const auto w = m_rx - m_extent;
const auto h = m_ry - m_extent; const auto h = m_ry - m_extent;
@ -185,51 +177,51 @@ namespace
auto l = lines; auto l = lines;
for ( auto r = qFloor( m_radians1 / M_PI_2 ) * M_PI_2; const QskVertex::Color color1 = m_gradient.rgbStart();
r < m_radians2; r += M_PI_2 ) const QskVertex::Color color2 = m_gradient.rgbEnd();
for ( AngleIterator it( m_radians1, m_radians2, arcLineCount() ); !it.isDone(); ++it )
{ {
const auto r1 = qMax( r, m_radians1 ); /*
const auto r2 = qMin( r + M_PI_2, m_radians2 ); The inner/outer points are found by shifting orthogonally along the
ellipse tangent:
const auto lineCount = arcLineCount( r2 - r1 ); m = w / h * tan( angle )
y = m * x;
x² + y² = dt
for ( AngleIterator it( r1, r2, lineCount ); !it.isDone(); ++it ) => x = dt / sqrt( 1.0 + m² );
Note: the angle of the orthogonal vector could
also be found ( first quadrant ) by:
atan2( tan( angle ), h / w );
*/
qreal dx, dy;
if ( qFuzzyIsNull( it.cos() ) )
{ {
qreal dx, dy; dx = 0.0;
dy = ( it.sin() > 0.0 ) ? m_extent : -m_extent;
/*
The inner/outer points are found by shifting along the
normal vector of the tangent at the ellipse point.
*/
if ( qFuzzyIsNull( it.cos() ) )
{
dx = 0.0;
dy = ( it.sin() > 0.0 ) ? m_extent : -m_extent;
}
else
{
// expanding orthogonally to the ellipse tangent
/*
m = w / h * tan( angle )
y = m * x;
x² + y² = dt
=> x = dt / sqrt( 1.0 + m² );
*/
const qreal m = aspectRatio * ( it.sin() / it.cos() );
const qreal t = m_extent / qSqrt( 1.0 + m * m );
dx = ( it.cos() >= 0.0 ) ? t : -t;
dy = m * dx;
}
const auto x = m_cx + w * it.cos();
const auto y = m_cy - h * it.sin();
l++->setLine( x + dx, y - dy, x - dx, y + dy, color );
} }
else
{
const qreal m = aspectRatio * ( it.sin() / it.cos() );
const qreal t = m_extent / qSqrt( 1.0 + m * m );
dx = ( it.cos() >= 0.0 ) ? t : -t;
dy = m * dx;
}
const auto x = m_cx + w * it.cos();
const auto y = m_cy - h * it.sin();
auto color = color1;
if ( color1 != color2 )
color = color.interpolatedTo( color2, it.progress() );
l++->setLine( x + dx, y - dy, x - dx, y + dy, color );
} }
return l - lines; return l - lines;
@ -250,16 +242,14 @@ void QskArcRenderer::renderFillGeometry( const QRectF& rect,
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
EllipseStroker stroker( rect, metrics, borderWidth ); EllipseStroker stroker( rect, metrics, borderWidth, gradient );
const auto lineCount = stroker.fillCount(); const auto lineCount = stroker.fillCount();
const auto lines = qskAllocateColoredLines( geometry, lineCount ); const auto lines = qskAllocateColoredLines( geometry, lineCount );
if ( lines ) if ( lines )
{ {
const QskVertex::Color color = gradient.rgbStart(); const auto effectiveCount = stroker.setFillLines( lines );
const auto effectiveCount = stroker.setFillLines( lines, color );
if ( lineCount != effectiveCount ) if ( lineCount != effectiveCount )
{ {
qWarning() << lineCount << effectiveCount; qWarning() << lineCount << effectiveCount;
@ -272,7 +262,7 @@ void QskArcRenderer::renderBorder( const QRectF& rect, const QskArcMetrics& metr
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
EllipseStroker stroker( rect, metrics, borderWidth ); EllipseStroker stroker( rect, metrics, borderWidth, QskGradient() );
const auto lineCount = stroker.borderCount(); const auto lineCount = stroker.borderCount();