radial arcs added
This commit is contained in:
parent
929b7a43fd
commit
ac68b66b4e
|
@ -9,153 +9,10 @@
|
|||
|
||||
#include <QskIntervalF.h>
|
||||
#include <QskArcMetrics.h>
|
||||
#include <QskArcNode.h>
|
||||
#include <QskArcRenderNode.h>
|
||||
|
||||
#include <qpainterpath.h>
|
||||
#include <qmath.h>
|
||||
|
||||
#define PAINTED_NODE 0
|
||||
|
||||
#if PAINTED_NODE
|
||||
|
||||
/*
|
||||
This is a fallback implementation for a user who is using an outdated
|
||||
version of QSkinny where only shaders for linear gradients are available.
|
||||
*/
|
||||
#include <QskPaintedNode.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
#include <qpen.h>
|
||||
#include <qbrush.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
QConicalGradient qskQConicalGradient(
|
||||
const QskGradientStops& stops, qreal startAngle, qreal spanAngle )
|
||||
{
|
||||
QskGradientStops scaledStops;
|
||||
scaledStops.reserve( stops.size() );
|
||||
|
||||
const auto ratio = qAbs( spanAngle ) / 360.0;
|
||||
|
||||
if ( spanAngle > 0.0 )
|
||||
{
|
||||
for ( auto it = stops.cbegin(); it != stops.cend(); ++it )
|
||||
scaledStops += { ratio * it->position(), it->color() };
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( auto it = stops.crbegin(); it != stops.crend(); ++it )
|
||||
scaledStops += { 1.0 - ratio * it->position(), it->color() };
|
||||
}
|
||||
|
||||
QConicalGradient qGradient( QPointF(), startAngle );
|
||||
qGradient.setStops( qskToQGradientStops( scaledStops ) );
|
||||
|
||||
return qGradient;
|
||||
}
|
||||
|
||||
class PaintedArcNode : public QskPaintedNode
|
||||
{
|
||||
public:
|
||||
void setArcData( const QRectF&, const QskArcMetrics&,
|
||||
qreal, const QColor&, const QskGradient&, QQuickWindow* );
|
||||
|
||||
protected:
|
||||
void paint( QPainter*, const QSize&, const void* nodeData ) override;
|
||||
QskHashValue hash( const void* nodeData ) const override;
|
||||
|
||||
private:
|
||||
QskHashValue arcHash( const QRectF&, const QskArcMetrics&,
|
||||
qreal, const QColor&, const QskGradient& ) const;
|
||||
|
||||
QBrush fillBrush( const QskGradient&, const QRectF&, qreal, qreal ) const;
|
||||
|
||||
struct ArcData
|
||||
{
|
||||
QPointF translation;
|
||||
QPen pen;
|
||||
QBrush brush;
|
||||
QPainterPath path;
|
||||
|
||||
QskHashValue hash;
|
||||
};
|
||||
};
|
||||
|
||||
void PaintedArcNode::setArcData(
|
||||
const QRectF& rect, const QskArcMetrics& metrics,
|
||||
qreal borderWidth, const QColor& borderColor,
|
||||
const QskGradient& gradient, QQuickWindow* window )
|
||||
{
|
||||
const auto hash = arcHash( rect, metrics, borderWidth, borderColor, gradient );
|
||||
|
||||
const auto brush = fillBrush( gradient, rect,
|
||||
metrics.startAngle(), metrics.spanAngle() );
|
||||
|
||||
QPen pen( borderColor, borderWidth );
|
||||
if ( borderWidth <= 0.0 )
|
||||
pen.setStyle( Qt::NoPen );
|
||||
|
||||
const auto path = metrics.painterPath( rect );
|
||||
const auto r = path.controlPointRect();
|
||||
|
||||
const ArcData arcData { r.topLeft(), pen, brush, path, hash };
|
||||
update( window, r, QSizeF(), &arcData );
|
||||
}
|
||||
|
||||
void PaintedArcNode::paint( QPainter* painter, const QSize&, const void* nodeData )
|
||||
{
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
|
||||
painter->setRenderHint( QPainter::Antialiasing, true );
|
||||
painter->translate( -arcData->translation );
|
||||
painter->setPen( arcData->pen );
|
||||
painter->setBrush( arcData->brush );
|
||||
painter->drawPath( arcData->path );
|
||||
}
|
||||
|
||||
QskHashValue PaintedArcNode::hash( const void* nodeData ) const
|
||||
{
|
||||
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
|
||||
return arcData->hash;
|
||||
}
|
||||
|
||||
QBrush PaintedArcNode::fillBrush( const QskGradient& gradient,
|
||||
const QRectF& rect, qreal startAngle, qreal spanAngle ) const
|
||||
{
|
||||
const auto qGradient = qskQConicalGradient(
|
||||
gradient.stops(), startAngle, spanAngle );
|
||||
|
||||
const qreal sz = qMax( rect.width(), rect.height() );
|
||||
const qreal sx = rect.width() / sz;
|
||||
const qreal sy = rect.height() / sz;
|
||||
|
||||
QTransform t;
|
||||
t.scale( sx, sy );
|
||||
t.translate( rect.center().x() / sx, rect.center().y() / sy );
|
||||
|
||||
QBrush brush( qGradient );
|
||||
brush.setTransform( t );
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
inline QskHashValue PaintedArcNode::arcHash(
|
||||
const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth,
|
||||
const QColor& borderColor, const QskGradient& gradient ) const
|
||||
{
|
||||
auto hash = metrics.hash( 6753 );
|
||||
hash = qHashBits( &rect, sizeof( rect ), hash );
|
||||
hash = qHash( borderWidth, hash );
|
||||
hash = qHash( borderColor.rgba(), hash );
|
||||
hash = gradient.hash( hash );
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PAINTED_NODE
|
||||
|
||||
namespace
|
||||
{
|
||||
inline QskArcMetrics segmentMetrics(
|
||||
|
@ -334,38 +191,16 @@ QSGNode* CircularChartSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
QSGNode* CircularChartSkinlet::updateArcSegmentNode( const QskSkinnable* skinnable,
|
||||
QSGNode* CircularChartSkinlet::updateArcSegmentNode( const QskSkinnable*,
|
||||
QSGNode* node, qreal borderWidth, const QColor& borderColor,
|
||||
const QskGradient& gradient, const QskArcMetrics& metrics ) const
|
||||
{
|
||||
#if PAINTED_NODE
|
||||
auto arcNode = static_cast< PaintedArcNode* >( node );
|
||||
auto arcNode = static_cast< QskArcRenderNode* >( node );
|
||||
if ( arcNode == nullptr )
|
||||
arcNode = new PaintedArcNode();
|
||||
arcNode = new QskArcRenderNode();
|
||||
|
||||
const auto chart = static_cast< const CircularChart* >( skinnable );
|
||||
|
||||
arcNode->setArcData( m_data->closedArcRect, metrics,
|
||||
borderWidth, borderColor, gradient, chart->window() );
|
||||
#else
|
||||
Q_UNUSED( skinnable )
|
||||
|
||||
auto fillGradient = gradient;
|
||||
|
||||
if ( fillGradient.type() == QskGradient::Stops )
|
||||
{
|
||||
fillGradient.setStretchMode( QskGradient::StretchToSize );
|
||||
fillGradient.setConicDirection( 0.5, 0.5,
|
||||
metrics.startAngle(), metrics.spanAngle() );
|
||||
}
|
||||
|
||||
auto arcNode = static_cast< QskArcNode* >( node );
|
||||
if ( arcNode == nullptr )
|
||||
arcNode = new QskArcNode();
|
||||
|
||||
arcNode->setArcData( m_data->closedArcRect, metrics,
|
||||
borderWidth, borderColor, fillGradient );
|
||||
#endif
|
||||
arcNode->updateNode( m_data->closedArcRect, metrics, true,
|
||||
borderWidth, borderColor, gradient );
|
||||
|
||||
return arcNode;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,8 @@ namespace
|
|||
{ 0.75, Qt::darkBlue },
|
||||
{ 0.75, Qt::darkRed }
|
||||
};
|
||||
#else
|
||||
#endif
|
||||
#if 0
|
||||
const QskGradientStops stops =
|
||||
{
|
||||
{ 0.1, Qt::darkRed },
|
||||
|
@ -80,6 +81,16 @@ namespace
|
|||
{ 1.0, Qt::darkRed }
|
||||
};
|
||||
#endif
|
||||
#if 1
|
||||
QskGradientStops stops;
|
||||
for ( int i = 0; i < 100; i++ )
|
||||
{
|
||||
const auto pos = i * 1.0 / 100;
|
||||
|
||||
stops += { pos, ( i % 2 ) ? Qt::darkRed : Qt::darkBlue };
|
||||
stops += { pos, ( i % 2 ) ? Qt::darkBlue : Qt::darkRed };
|
||||
}
|
||||
#endif
|
||||
|
||||
setFillGradient( stops );
|
||||
}
|
||||
|
|
|
@ -21,6 +21,83 @@ static void qskRegisterArcMetrics()
|
|||
|
||||
Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics )
|
||||
|
||||
static inline QPainterPath qskRadialPathPath(
|
||||
const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width )
|
||||
{
|
||||
const auto sz = qMin( rect.width(), rect.height() );
|
||||
|
||||
const auto tx = width * rect.width() / sz;
|
||||
const auto ty = width * rect.height() / sz;
|
||||
|
||||
const auto innerRect = rect.adjusted( tx, ty, -tx, -ty );
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
if ( innerRect.isEmpty() )
|
||||
{
|
||||
if ( qAbs( spanAngle ) >= 360.0 )
|
||||
{
|
||||
path.addEllipse( rect );
|
||||
}
|
||||
else
|
||||
{
|
||||
// pie
|
||||
path.arcMoveTo( rect, startAngle );
|
||||
path.arcTo( rect, startAngle, spanAngle );
|
||||
path.lineTo( rect.center() );
|
||||
path.closeSubpath();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( qAbs( spanAngle ) >= 360.0 )
|
||||
{
|
||||
path.addEllipse( rect );
|
||||
|
||||
QPainterPath innerPath;
|
||||
innerPath.addEllipse( innerRect );
|
||||
path -= innerPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
We need the end point of the inner arc to add the line that connects
|
||||
the inner/outer arcs. As QPainterPath does not offer such a method
|
||||
we insert a dummy arcMoveTo and grab the calculated position.
|
||||
*/
|
||||
path.arcMoveTo( innerRect, startAngle + spanAngle );
|
||||
const auto pos = path.currentPosition();
|
||||
|
||||
path.arcMoveTo( rect, startAngle ); // replaces the dummy arcMoveTo above
|
||||
path.arcTo( rect, startAngle, spanAngle );
|
||||
|
||||
path.lineTo( pos );
|
||||
path.arcTo( innerRect, startAngle + spanAngle, -spanAngle );
|
||||
|
||||
path.closeSubpath();
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static inline QPainterPath qskOrthogonalPath(
|
||||
const QRectF& rect, qreal startAngle, qreal spanAngle, qreal width )
|
||||
{
|
||||
const auto t2 = 0.5 * width;
|
||||
const auto r = rect.adjusted( t2, t2, -t2, -t2 );
|
||||
|
||||
QPainterPath arcPath;
|
||||
arcPath.arcMoveTo( r, startAngle );
|
||||
arcPath.arcTo( r, startAngle, spanAngle );
|
||||
|
||||
QPainterPathStroker stroker;
|
||||
stroker.setCapStyle( Qt::FlatCap );
|
||||
stroker.setWidth( width );
|
||||
|
||||
return stroker.createStroke( arcPath );
|
||||
}
|
||||
|
||||
static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio )
|
||||
{
|
||||
return from + ( to - from ) * ratio;
|
||||
|
@ -97,6 +174,11 @@ QVariant QskArcMetrics::interpolate(
|
|||
return QVariant::fromValue( from.interpolated( to, progress ) );
|
||||
}
|
||||
|
||||
QskArcMetrics QskArcMetrics::toAbsolute( const QSizeF& size ) const noexcept
|
||||
{
|
||||
return toAbsolute( 0.5 * size.width(), 0.5 * size.height() );
|
||||
}
|
||||
|
||||
QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY ) const noexcept
|
||||
{
|
||||
if ( radiusX < 0.0 )
|
||||
|
@ -117,59 +199,30 @@ QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept
|
|||
return QskArcMetrics( m_startAngle, m_spanAngle, t, Qt::AbsoluteSize );
|
||||
}
|
||||
|
||||
QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const
|
||||
QPainterPath QskArcMetrics::painterPath( const QRectF& rect, bool radial ) const
|
||||
{
|
||||
qreal t = m_thickness;
|
||||
|
||||
if ( m_relativeSize )
|
||||
{
|
||||
const auto sz = qMin( ellipseRect.width(), ellipseRect.height() );
|
||||
t = qskEffectiveThickness( 0.5 * sz, t );
|
||||
}
|
||||
|
||||
if ( t <= 0.0 || qFuzzyIsNull( m_spanAngle ) )
|
||||
return QPainterPath();
|
||||
|
||||
#if 0
|
||||
/*
|
||||
We do not have a proper solution for situations, where
|
||||
the width leads to overlapping upper/lower or left/right parts.
|
||||
*/
|
||||
|
||||
const auto innerRect = ellipseRect.adjusted( t, t, -t, -t );
|
||||
if ( innerRect.isEmpty() )
|
||||
{
|
||||
QPainterPath path;
|
||||
|
||||
if ( qAbs( m_spanAngle ) >= 360.0 )
|
||||
{
|
||||
path.addEllipse( ellipseRect );
|
||||
}
|
||||
else
|
||||
{
|
||||
// pie
|
||||
path.arcMoveTo( ellipseRect, m_startAngle );
|
||||
path.arcTo( ellipseRect, m_startAngle, m_spanAngle );
|
||||
path.lineTo( ellipseRect.center() );
|
||||
path.closeSubpath();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto t2 = 0.5 * t;
|
||||
const auto r = ellipseRect.adjusted( t2, t2, -t2, -t2 );
|
||||
|
||||
QPainterPath path;
|
||||
path.arcMoveTo( r, m_startAngle ); // replaces the dummy arcMoveTo above
|
||||
path.arcTo( r, m_startAngle, m_spanAngle );
|
||||
|
||||
QPainterPathStroker stroker;
|
||||
stroker.setCapStyle( Qt::FlatCap );
|
||||
stroker.setWidth( t );
|
||||
if ( !qFuzzyIsNull( m_spanAngle ) )
|
||||
{
|
||||
qreal t = m_thickness;
|
||||
|
||||
return stroker.createStroke( path );
|
||||
if ( m_relativeSize )
|
||||
{
|
||||
const auto sz = qMin( rect.width(), rect.height() );
|
||||
t = qskEffectiveThickness( 0.5 * sz, t );
|
||||
}
|
||||
|
||||
if ( t > 0.0 )
|
||||
{
|
||||
if ( radial )
|
||||
path = qskRadialPathPath( rect, m_startAngle, m_spanAngle, t );
|
||||
else
|
||||
path = qskOrthogonalPath( rect, m_startAngle, m_spanAngle, t );
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
QRectF QskArcMetrics::boundingRect( const QRectF& ellipseRect ) const
|
||||
|
|
|
@ -58,10 +58,26 @@ class QSK_EXPORT QskArcMetrics
|
|||
QskArcMetrics interpolated( const QskArcMetrics&,
|
||||
qreal value ) const noexcept;
|
||||
|
||||
QskArcMetrics toAbsolute( const QSizeF& ) const noexcept;
|
||||
QskArcMetrics toAbsolute( qreal radiusX, qreal radiusY ) const noexcept;
|
||||
QskArcMetrics toAbsolute( qreal radius ) const noexcept;
|
||||
|
||||
QPainterPath painterPath( const QRectF& ellipseRect ) const;
|
||||
/*
|
||||
The arc is interpolated by pairs of points, where one point is on
|
||||
the outer and the other on the inner side of the arc. The length between
|
||||
these points depends on the thickness.
|
||||
|
||||
When radial is set the inner point lies on the line between the outer point
|
||||
and the center of the arc. This corresponds to the lines of a conic gradient.
|
||||
|
||||
Otherwise the line between the inner and outer point is orthogonal to the
|
||||
tangent at the point in the middle of the arc. This is how the width
|
||||
of the pen is expanded by QPainter::drawArc.
|
||||
|
||||
Note, that the radial flag is irrelevant for circular arcs as the tangent
|
||||
is always orthogonal to any point on the circle.
|
||||
*/
|
||||
QPainterPath painterPath( const QRectF& ellipseRect, bool radial = false ) const;
|
||||
|
||||
QRectF boundingRect( const QRectF& ellipseRect ) const;
|
||||
QSizeF boundingSize( const QSizeF& ellipseSize ) const;
|
||||
|
|
|
@ -56,20 +56,6 @@ static inline QskGradient qskEffectiveGradient(
|
|||
return gradient;
|
||||
}
|
||||
|
||||
static inline QskArcMetrics qskEffectiveMetrics(
|
||||
const QskArcMetrics& metrics, const QRectF& rect )
|
||||
{
|
||||
if ( metrics.sizeMode() == Qt::RelativeSize )
|
||||
{
|
||||
const auto rx = 0.5 * rect.width();
|
||||
const auto ry = 0.5 * rect.height();
|
||||
|
||||
return metrics.toAbsolute( rx, ry );
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
static inline QRectF qskEffectiveRect(
|
||||
const QRectF& rect, const qreal borderWidth )
|
||||
{
|
||||
|
@ -112,7 +98,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
const qreal borderWidth, const QColor& borderColor, const QskGradient& gradient,
|
||||
const QColor& shadowColor, const QskShadowMetrics& shadowMetrics )
|
||||
{
|
||||
const auto metricsArc = qskEffectiveMetrics( arcMetrics, rect );
|
||||
const auto metricsArc = arcMetrics.toAbsolute( rect.size() );
|
||||
|
||||
auto shadowNode = static_cast< QskArcShadowNode* >(
|
||||
QskSGNode::findChildNode( this, ShadowRole ) );
|
||||
|
@ -136,7 +122,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
return;
|
||||
}
|
||||
|
||||
const auto isFillNodeVisible = gradient.isVisible() && !metricsArc.isNull();
|
||||
const auto isFillNodeVisible = gradient.isVisible();
|
||||
const auto isStrokeNodeVisible = ( borderWidth > 0.0 ) && ( borderColor.alpha() > 0 );
|
||||
const auto isShadowNodeVisible = isFillNodeVisible &&
|
||||
shadowColor.isValid() && ( shadowColor.alpha() > 0.0 );
|
||||
|
@ -184,7 +170,8 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
QskSGNode::setNodeRole( arcNode, ArcRole );
|
||||
}
|
||||
|
||||
arcNode->updateNode( arcRect, metricsArc, gradient );
|
||||
arcNode->updateNode( arcRect, metricsArc, false,
|
||||
borderWidth, QColor(), gradient );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -6,18 +6,14 @@
|
|||
#ifndef QSK_ARC_NODE_H
|
||||
#define QSK_ARC_NODE_H
|
||||
|
||||
#include "QskShapeNode.h"
|
||||
#include "QskGlobal.h"
|
||||
#include <qsgnode.h>
|
||||
|
||||
class QskArcMetrics;
|
||||
class QskGradient;
|
||||
class QskShadowMetrics;
|
||||
|
||||
/*
|
||||
For the moment a QPainterPath/QskShapeNode.
|
||||
But we can do better by creatig vertex lists manually
|
||||
like what is done by the box renderer. TODO ...
|
||||
*/
|
||||
class QSK_EXPORT QskArcNode : public QskShapeNode
|
||||
class QSK_EXPORT QskArcNode : public QSGNode
|
||||
{
|
||||
public:
|
||||
QskArcNode();
|
||||
|
|
|
@ -52,21 +52,23 @@ QskArcRenderNode::QskArcRenderNode()
|
|||
void QskArcRenderNode::updateNode( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, const QskGradient& gradient )
|
||||
{
|
||||
updateNode( rect, metrics, 0.0, QColor(), gradient );
|
||||
updateNode( rect, metrics, false, 0.0, QColor(), gradient );
|
||||
}
|
||||
|
||||
void QskArcRenderNode::updateNode( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, qreal borderWidth, const QColor& borderColor )
|
||||
{
|
||||
updateNode( rect, metrics, borderWidth, borderColor, QskGradient() );
|
||||
updateNode( rect, metrics, false, borderWidth, borderColor, QskGradient() );
|
||||
}
|
||||
|
||||
void QskArcRenderNode::updateNode(
|
||||
const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth,
|
||||
const QColor& borderColor, const QskGradient& gradient )
|
||||
const QRectF& rect, const QskArcMetrics& arcMetrics, bool radial,
|
||||
qreal borderWidth, const QColor& borderColor, const QskGradient& gradient )
|
||||
{
|
||||
Q_D( QskArcRenderNode );
|
||||
|
||||
const auto metrics = arcMetrics.toAbsolute( rect.size() );
|
||||
|
||||
bool visible = !( rect.isEmpty() || metrics.isNull() );
|
||||
if ( visible )
|
||||
{
|
||||
|
@ -93,21 +95,22 @@ void QskArcRenderNode::updateNode(
|
|||
hash = qHash( borderColor.rgba(), hash );
|
||||
hash = metrics.hash( hash );
|
||||
hash = gradient.hash( hash );
|
||||
hash = qHash( radial, hash );
|
||||
|
||||
if ( hash != d->hash )
|
||||
{
|
||||
d->hash = hash;
|
||||
|
||||
#if 0
|
||||
if ( borderWidth > 0.0 )
|
||||
{
|
||||
QskArcRenderer::renderBorder(
|
||||
rect, metrics, borderWidth, borderColor, *geometry() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QskArcRenderer::renderFillGeometry(
|
||||
rect, metrics, borderWidth, gradient, *geometry() );
|
||||
rect, metrics, borderWidth, radial, borderColor, *geometry() );
|
||||
}
|
||||
#endif
|
||||
|
||||
QskArcRenderer::renderFillGeometry(
|
||||
rect, metrics, radial, borderWidth, gradient, *geometry() );
|
||||
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
|
|
|
@ -25,8 +25,8 @@ class QSK_EXPORT QskArcRenderNode : public QSGGeometryNode
|
|||
void updateNode( const QRectF&, const QskArcMetrics&, qreal borderWidth,
|
||||
const QColor& borderColor );
|
||||
|
||||
void updateNode( const QRectF&, const QskArcMetrics&, qreal borderWidth,
|
||||
const QColor& borderColor, const QskGradient& );
|
||||
void updateNode( const QRectF&, const QskArcMetrics&, bool radial,
|
||||
qreal borderWidth, const QColor& borderColor, const QskGradient& );
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE( QskArcRenderNode )
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace
|
|||
class EllipseStroker
|
||||
{
|
||||
public:
|
||||
EllipseStroker( const QRectF&, const QskArcMetrics&,
|
||||
EllipseStroker( const QRectF&, const QskArcMetrics&, bool orthogonal,
|
||||
qreal borderWidth, const QskGradient& );
|
||||
|
||||
int fillCount() const;
|
||||
|
@ -44,10 +44,14 @@ namespace
|
|||
int arcLineCount() const;
|
||||
qreal radiansAt( qreal progress ) const;
|
||||
|
||||
void setFillLine( const qreal radians,
|
||||
void setOrthogonalLine( const qreal radians,
|
||||
const QskVertex::Color, QskVertex::ColoredLine& ) const;
|
||||
|
||||
void setRadialLine( const qreal radians,
|
||||
const QskVertex::Color, QskVertex::ColoredLine& ) const;
|
||||
|
||||
const qreal m_extent;
|
||||
const bool m_orthogonal;
|
||||
|
||||
// radius
|
||||
const qreal m_rx;
|
||||
|
@ -60,13 +64,14 @@ namespace
|
|||
const qreal m_radians1;
|
||||
const qreal m_radians2;
|
||||
|
||||
|
||||
const QskGradient& m_gradient;
|
||||
};
|
||||
|
||||
EllipseStroker::EllipseStroker( const QRectF& rect,
|
||||
const QskArcMetrics& m, qreal borderWidth, const QskGradient& gradient )
|
||||
const QskArcMetrics& m, bool orthogonal, qreal borderWidth,
|
||||
const QskGradient& gradient )
|
||||
: m_extent( 0.5 * ( m.thickness() - borderWidth ) )
|
||||
, m_orthogonal( orthogonal )
|
||||
, m_rx( 0.5 * rect.width() - m_extent )
|
||||
, m_ry( 0.5 * rect.height() - m_extent )
|
||||
, m_cx( rect.center().x() )
|
||||
|
@ -107,7 +112,7 @@ namespace
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline void EllipseStroker::setFillLine( const qreal radians,
|
||||
inline void EllipseStroker::setOrthogonalLine( const qreal radians,
|
||||
const QskVertex::Color color, QskVertex::ColoredLine& line ) const
|
||||
{
|
||||
const auto cos = qFastCos( radians );
|
||||
|
@ -151,6 +156,34 @@ namespace
|
|||
line.setLine( x + dx, y - dy, x - dx, y + dy, color );
|
||||
}
|
||||
|
||||
inline void EllipseStroker::setRadialLine( const qreal radians,
|
||||
const QskVertex::Color color, QskVertex::ColoredLine& line ) const
|
||||
{
|
||||
const auto cos = qFastCos( radians );
|
||||
const auto sin = qFastSin( radians );
|
||||
|
||||
const auto t = 2.0 * m_extent;
|
||||
|
||||
const auto w = 2.0 * m_rx + t;
|
||||
const auto h = 2.0 * m_ry + t;
|
||||
|
||||
const auto x1 = 0.5 * w * cos;
|
||||
const auto y1 = 0.5 * h * sin;
|
||||
|
||||
const auto sz = qMin( w, h );
|
||||
|
||||
const auto tx = t * w / sz;
|
||||
const auto ty = t * h / sz;
|
||||
|
||||
const auto w2 = w - 2 * tx;
|
||||
const auto h2 = h - 2 * ty;
|
||||
|
||||
const auto x2 = 0.5 * w2 * cos;
|
||||
const auto y2 = 0.5 * h2 * sin;
|
||||
|
||||
line.setLine( m_cx + x1, m_cy - y1, m_cx + x2, m_cy - y2, color );
|
||||
}
|
||||
|
||||
int EllipseStroker::setFillLines( QskVertex::ColoredLine* lines ) const
|
||||
{
|
||||
auto l = lines;
|
||||
|
@ -177,12 +210,20 @@ namespace
|
|||
|
||||
while( !it.isDone() && ( it.position() < progress ) )
|
||||
{
|
||||
setFillLine( radiansAt( it.position() ), it.color(), *l++ );
|
||||
if ( m_orthogonal )
|
||||
setOrthogonalLine( radiansAt( it.position() ), it.color(), *l++ );
|
||||
else
|
||||
setRadialLine( radiansAt( it.position() ), it.color(), *l++ );
|
||||
|
||||
it.advance();
|
||||
}
|
||||
|
||||
const auto color = it.colorAt( progress );
|
||||
setFillLine( m_radians1 + i * stepSize, color, *l++ );
|
||||
|
||||
if ( m_orthogonal )
|
||||
setOrthogonalLine( m_radians1 + i * stepSize, color, *l++ );
|
||||
else
|
||||
setRadialLine( m_radians1 + i * stepSize, color, *l++ );
|
||||
}
|
||||
|
||||
return l - lines;
|
||||
|
@ -203,12 +244,16 @@ bool QskArcRenderer::isGradientSupported( const QskGradient& gradient )
|
|||
}
|
||||
|
||||
void QskArcRenderer::renderFillGeometry( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, qreal borderWidth,
|
||||
const QskArcMetrics& metrics, bool radial, qreal borderWidth,
|
||||
const QskGradient& gradient, QSGGeometry& geometry )
|
||||
{
|
||||
#if 1
|
||||
// TODO ...
|
||||
borderWidth = 0.0;
|
||||
#endif
|
||||
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
||||
|
||||
EllipseStroker stroker( rect, metrics, borderWidth, gradient );
|
||||
EllipseStroker stroker( rect, metrics, !radial, borderWidth, gradient );
|
||||
|
||||
const auto lineCount = stroker.fillCount();
|
||||
|
||||
|
@ -236,11 +281,11 @@ void QskArcRenderer::renderFillGeometry( const QRectF& rect,
|
|||
}
|
||||
|
||||
void QskArcRenderer::renderBorder( const QRectF& rect, const QskArcMetrics& metrics,
|
||||
qreal borderWidth, const QColor& borderColor, QSGGeometry& geometry )
|
||||
bool radial, qreal borderWidth, const QColor& borderColor, QSGGeometry& geometry )
|
||||
{
|
||||
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
||||
|
||||
EllipseStroker stroker( rect, metrics, borderWidth, QskGradient() );
|
||||
EllipseStroker stroker( rect, metrics, !radial, borderWidth, QskGradient() );
|
||||
|
||||
const auto lineCount = stroker.borderCount();
|
||||
|
||||
|
@ -254,3 +299,24 @@ void QskArcRenderer::renderBorder( const QRectF& rect, const QskArcMetrics& metr
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QskArcRenderer::renderBorderGeometry( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, bool radial, qreal borderWidth, QSGGeometry& geometry )
|
||||
{
|
||||
Q_UNUSED( rect );
|
||||
Q_UNUSED( metrics );
|
||||
Q_UNUSED( radial );
|
||||
Q_UNUSED( borderWidth );
|
||||
Q_UNUSED( geometry );
|
||||
}
|
||||
|
||||
void QskArcRenderer::renderFillGeometry( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, bool radial, qreal borderWidth, QSGGeometry& geometry )
|
||||
{
|
||||
Q_UNUSED( rect );
|
||||
Q_UNUSED( metrics );
|
||||
Q_UNUSED( radial );
|
||||
Q_UNUSED( borderWidth );
|
||||
Q_UNUSED( geometry );
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ namespace QskArcRenderer
|
|||
*/
|
||||
|
||||
QSK_EXPORT void renderBorderGeometry( const QRectF&,
|
||||
const QskArcMetrics&, qreal borderWidth, QSGGeometry& );
|
||||
const QskArcMetrics&, bool radial, qreal borderWidth, QSGGeometry& );
|
||||
|
||||
QSK_EXPORT void renderFillGeometry( const QRectF&,
|
||||
const QskArcMetrics&, qreal borderWidth, QSGGeometry& );
|
||||
const QskArcMetrics&, bool radial, qreal borderWidth, QSGGeometry& );
|
||||
|
||||
/*
|
||||
Filling the geometry with color information:
|
||||
|
@ -38,10 +38,10 @@ namespace QskArcRenderer
|
|||
QSK_EXPORT bool isGradientSupported( const QskGradient& );
|
||||
|
||||
QSK_EXPORT void renderBorder( const QRectF&, const QskArcMetrics&,
|
||||
qreal borderWidth, const QColor& borderColor, QSGGeometry& );
|
||||
bool radial, qreal borderWidth, const QColor& borderColor, QSGGeometry& );
|
||||
|
||||
QSK_EXPORT void renderFillGeometry( const QRectF&, const QskArcMetrics&,
|
||||
qreal borderWidth, const QskGradient&, QSGGeometry& );
|
||||
bool radial, qreal borderWidth, const QskGradient&, QSGGeometry& );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue