filling with gradients seems to work
This commit is contained in:
parent
bf7fe7c8f3
commit
1a08de914a
|
@ -103,8 +103,15 @@ ShadowedArc::ShadowedArc( QQuickItem* parent )
|
||||||
|
|
||||||
//setFillColor( Qt::darkRed );
|
//setFillColor( Qt::darkRed );
|
||||||
|
|
||||||
const QskGradient gradient( Qt::darkRed, Qt::darkYellow );
|
const QskGradientStops stops =
|
||||||
setGradientHint( Arc, gradient );
|
{
|
||||||
|
{ 0.1, Qt::darkRed },
|
||||||
|
{ 0.5, Qt::darkYellow },
|
||||||
|
{ 0.75, Qt::darkBlue },
|
||||||
|
{ 1.0, Qt::darkRed }
|
||||||
|
};
|
||||||
|
|
||||||
|
setGradientHint( Arc, stops );
|
||||||
|
|
||||||
setBorderWidth( 0 );
|
setBorderWidth( 0 );
|
||||||
setBorderColor( Qt::gray );
|
setBorderColor( Qt::gray );
|
||||||
|
|
|
@ -26,73 +26,6 @@ static inline QskVertex::ColoredLine* qskAllocateColoredLines(
|
||||||
return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() );
|
return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() );
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
QskVertex::ArcIterator is slightly more efficient as it increments
|
|
||||||
cos/sin instead of doing the table lookups that are behind qFastCos/qFastSin.
|
|
||||||
For the moment we go with qFastCos/qFastSin, maybe later: TODO ...
|
|
||||||
*/
|
|
||||||
class AngleIterator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AngleIterator( qreal radians1, qreal radians2, int stepCount );
|
|
||||||
|
|
||||||
inline void operator++() { increment(); }
|
|
||||||
void increment();
|
|
||||||
|
|
||||||
inline qreal cos() const { return m_cos; }
|
|
||||||
inline qreal sin() const { return m_sin; }
|
|
||||||
|
|
||||||
inline int step() const { return m_stepIndex; }
|
|
||||||
inline int stepCount() const { return m_stepCount; }
|
|
||||||
inline bool isDone() const { return m_stepIndex >= m_stepCount; }
|
|
||||||
|
|
||||||
inline qreal progress() const { return qreal( m_stepIndex ) / m_stepCount; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
qreal m_cos;
|
|
||||||
qreal m_sin;
|
|
||||||
|
|
||||||
int m_stepIndex;
|
|
||||||
int m_stepCount;
|
|
||||||
|
|
||||||
const qreal m_radians1;
|
|
||||||
const qreal m_radians2;
|
|
||||||
const qreal m_radiansStep;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline AngleIterator::AngleIterator( qreal radians1, qreal radians2, int stepCount )
|
|
||||||
: m_stepIndex( 0 )
|
|
||||||
, m_stepCount( stepCount )
|
|
||||||
, m_radians1( radians1 )
|
|
||||||
, m_radians2( radians2 )
|
|
||||||
, m_radiansStep( ( radians2 - radians1 ) / ( stepCount - 1 ) )
|
|
||||||
{
|
|
||||||
m_cos = qFastCos( radians1 );
|
|
||||||
m_sin = qFastSin( radians1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void AngleIterator::increment()
|
|
||||||
{
|
|
||||||
if ( ++m_stepIndex >= m_stepCount )
|
|
||||||
{
|
|
||||||
if ( m_stepIndex == m_stepCount )
|
|
||||||
{
|
|
||||||
m_cos = qFastCos( m_radians2 );
|
|
||||||
m_sin = qFastSin( m_radians2 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto radians = m_radians1 + m_stepIndex * m_radiansStep;
|
|
||||||
|
|
||||||
m_cos = qFastCos( radians );
|
|
||||||
m_sin = qFastSin( radians );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class EllipseStroker
|
class EllipseStroker
|
||||||
|
@ -109,6 +42,12 @@ namespace
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int arcLineCount() const;
|
int arcLineCount() const;
|
||||||
|
qreal radiansAt( qreal progress ) const;
|
||||||
|
|
||||||
|
void setFillLine( const qreal radians,
|
||||||
|
const QskVertex::Color, QskVertex::ColoredLine& ) const;
|
||||||
|
|
||||||
|
const qreal m_extent;
|
||||||
|
|
||||||
// radius
|
// radius
|
||||||
const qreal m_rx;
|
const qreal m_rx;
|
||||||
|
@ -121,34 +60,33 @@ namespace
|
||||||
const qreal m_radians1;
|
const qreal m_radians1;
|
||||||
const qreal m_radians2;
|
const qreal m_radians2;
|
||||||
|
|
||||||
const qreal m_extent;
|
|
||||||
|
|
||||||
const QskGradient& m_gradient;
|
const QskGradient& m_gradient;
|
||||||
};
|
};
|
||||||
|
|
||||||
EllipseStroker::EllipseStroker( const QRectF& rect,
|
EllipseStroker::EllipseStroker( const QRectF& rect,
|
||||||
const QskArcMetrics& m, qreal borderWidth, const QskGradient& gradient )
|
const QskArcMetrics& m, qreal borderWidth, const QskGradient& gradient )
|
||||||
: m_rx( 0.5 * rect.width() )
|
: m_extent( 0.5 * ( m.thickness() - borderWidth ) )
|
||||||
, m_ry( 0.5 * rect.height() )
|
, m_rx( 0.5 * rect.width() - m_extent )
|
||||||
, m_cx( rect.x() + m_rx )
|
, m_ry( 0.5 * rect.height() - m_extent )
|
||||||
, m_cy( rect.y() + m_ry )
|
, m_cx( rect.center().x() )
|
||||||
|
, m_cy( rect.center().y() )
|
||||||
, m_radians1( qDegreesToRadians( m.startAngle() ) )
|
, m_radians1( qDegreesToRadians( m.startAngle() ) )
|
||||||
, m_radians2( qDegreesToRadians( m.endAngle() ) )
|
, m_radians2( qDegreesToRadians( m.endAngle() ) )
|
||||||
, m_extent( 0.5 * ( m.thickness() - borderWidth ) )
|
|
||||||
, m_gradient( gradient )
|
, m_gradient( gradient )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int EllipseStroker::fillCount() const
|
int EllipseStroker::fillCount() const
|
||||||
{
|
{
|
||||||
return arcLineCount();
|
return arcLineCount() + m_gradient.stepCount() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EllipseStroker::arcLineCount() const
|
int EllipseStroker::arcLineCount() const
|
||||||
{
|
{
|
||||||
// not very sophisticated - TODO ...
|
// not very sophisticated - TODO ...
|
||||||
|
|
||||||
const auto radius = qMax( m_rx, m_ry );
|
const auto radius = qMax( m_rx, m_ry ) + m_extent; // outer border
|
||||||
const auto radians = qAbs( m_radians2 - m_radians1 );
|
const auto radians = qAbs( m_radians2 - m_radians1 );
|
||||||
|
|
||||||
const auto count = qCeil( ( radius * radians ) / 3.0 );
|
const auto count = qCeil( ( radius * radians ) / 3.0 );
|
||||||
|
@ -169,63 +107,86 @@ namespace
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void EllipseStroker::setFillLine( const qreal radians,
|
||||||
|
const QskVertex::Color color, QskVertex::ColoredLine& line ) const
|
||||||
|
{
|
||||||
|
const auto cos = qFastCos( radians );
|
||||||
|
const auto sin = qFastSin( radians );
|
||||||
|
|
||||||
|
/*
|
||||||
|
The inner/outer points are found by shifting orthogonally along the
|
||||||
|
ellipse tangent:
|
||||||
|
|
||||||
|
m = w / h * tan( angle )
|
||||||
|
y = m * x;
|
||||||
|
x² + y² = dt
|
||||||
|
|
||||||
|
=> 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( cos ) )
|
||||||
|
{
|
||||||
|
dx = 0.0;
|
||||||
|
dy = ( sin > 0.0 ) ? m_extent : -m_extent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const qreal m = ( m_rx / m_ry ) * ( sin / cos );
|
||||||
|
const qreal t = m_extent / qSqrt( 1.0 + m * m );
|
||||||
|
|
||||||
|
dx = ( cos >= 0.0 ) ? t : -t;
|
||||||
|
dy = m * dx;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto x = m_cx + m_rx * cos;
|
||||||
|
const auto y = m_cy - m_ry * sin;
|
||||||
|
|
||||||
|
line.setLine( x + dx, y - dy, x - dx, y + dy, color );
|
||||||
|
}
|
||||||
|
|
||||||
int EllipseStroker::setFillLines( QskVertex::ColoredLine* lines ) const
|
int EllipseStroker::setFillLines( QskVertex::ColoredLine* lines ) const
|
||||||
{
|
{
|
||||||
const auto w = m_rx - m_extent;
|
|
||||||
const auto h = m_ry - m_extent;
|
|
||||||
const auto aspectRatio = w / h;
|
|
||||||
|
|
||||||
auto l = lines;
|
auto l = lines;
|
||||||
|
|
||||||
const QskVertex::Color color1 = m_gradient.rgbStart();
|
QskBoxRenderer::GradientIterator it;
|
||||||
const QskVertex::Color color2 = m_gradient.rgbEnd();
|
if ( m_gradient.stepCount() <= 1 )
|
||||||
|
|
||||||
for ( AngleIterator it( m_radians1, m_radians2, arcLineCount() ); !it.isDone(); ++it )
|
|
||||||
{
|
{
|
||||||
/*
|
it.reset( m_gradient.rgbStart(), m_gradient.rgbEnd() );
|
||||||
The inner/outer points are found by shifting orthogonally along the
|
}
|
||||||
ellipse tangent:
|
else
|
||||||
|
{
|
||||||
|
it.reset( m_gradient.stops() );
|
||||||
|
it.advance(); // the first stop is always covered by the contour
|
||||||
|
}
|
||||||
|
|
||||||
m = w / h * tan( angle )
|
const auto stepCount = arcLineCount();
|
||||||
y = m * x;
|
const auto stepSize = ( m_radians2 - m_radians1 ) / ( stepCount - 1 );
|
||||||
x² + y² = dt
|
|
||||||
|
|
||||||
=> x = dt / sqrt( 1.0 + m² );
|
for ( int i = 0; i < stepCount; i++ )
|
||||||
|
{
|
||||||
|
const auto progress = qreal( i ) / stepCount;
|
||||||
|
|
||||||
Note: the angle of the orthogonal vector could
|
for ( ; it.position() < progress; it.advance() )
|
||||||
also be found ( first quadrant ) by:
|
setFillLine( radiansAt( it.position() ), it.color(), *l++ );
|
||||||
|
|
||||||
atan2( tan( angle ), h / w );
|
const auto color = it.colorAt( progress );
|
||||||
*/
|
setFillLine( m_radians1 + i * stepSize, color, *l++ );
|
||||||
|
|
||||||
qreal dx, dy;
|
|
||||||
|
|
||||||
if ( qFuzzyIsNull( it.cos() ) )
|
|
||||||
{
|
|
||||||
dx = 0.0;
|
|
||||||
dy = ( it.sin() > 0.0 ) ? m_extent : -m_extent;
|
|
||||||
}
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline qreal EllipseStroker::radiansAt( qreal progress ) const
|
||||||
|
{
|
||||||
|
return m_radians1 + progress * ( m_radians2 - m_radians1 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QskArcRenderer::isGradientSupported( const QskGradient& gradient )
|
bool QskArcRenderer::isGradientSupported( const QskGradient& gradient )
|
||||||
|
@ -250,9 +211,21 @@ void QskArcRenderer::renderFillGeometry( const QRectF& rect,
|
||||||
if ( lines )
|
if ( lines )
|
||||||
{
|
{
|
||||||
const auto effectiveCount = stroker.setFillLines( lines );
|
const auto effectiveCount = stroker.setFillLines( lines );
|
||||||
if ( lineCount != effectiveCount )
|
if ( effectiveCount > lineCount )
|
||||||
{
|
{
|
||||||
qWarning() << lineCount << effectiveCount;
|
qFatal( "geometry: allocated memory exceeded: %d vs. %d",
|
||||||
|
effectiveCount, lineCount );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( effectiveCount < lineCount )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Gradient or contour lines might be at the same position
|
||||||
|
and we end up with less lines, than expected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for ( int i = effectiveCount; i < lineCount; i++ )
|
||||||
|
lines[i] = lines[i - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue