QskArcRenderer using the normal vector of the tangents for expanding
the arc to the desired thickness. This matches the result of what QPainter::drawArc does. However our implementation is much simpler as we do not convert the arc into a sequence of bezier curves finally running into code that has to deal with random QPainterPath element lists.
This commit is contained in:
parent
a327084c3f
commit
bc066c8103
|
@ -27,7 +27,7 @@ CircularChart::CircularChart( QQuickItem* parentItem )
|
|||
setGradientHint( Panel, QskGradient() );
|
||||
setBoxBorderMetricsHint( Panel, 0 );
|
||||
|
||||
setArcMetricsHint( Arc, { 90.0, -360.0, 100.0, Qt::RelativeSize, true } );
|
||||
setArcMetricsHint( Arc, { 90.0, -360.0, 100.0, Qt::RelativeSize } );
|
||||
|
||||
setGradientHint( Arc, QskRgb::toTransparent( QskRgb::LightGray, 100 ) );
|
||||
setColor( Arc | QskAspect::Border, QskRgb::LightGray );
|
||||
|
|
|
@ -106,7 +106,7 @@ ShadowedArc::ShadowedArc( QQuickItem* parent )
|
|||
setBorderWidth( 0 );
|
||||
setBorderColor( Qt::gray );
|
||||
|
||||
setShadowColor( Qt::black );
|
||||
setShadowColor( QColor() );
|
||||
setShadowMetrics( { 0, 0, QPointF( 0, 0 ), Qt::AbsoluteSize } );
|
||||
}
|
||||
|
||||
|
|
|
@ -51,11 +51,6 @@ void QskArcMetrics::setSizeMode( Qt::SizeMode sizeMode ) noexcept
|
|||
m_relativeSize = ( sizeMode == Qt::RelativeSize );
|
||||
}
|
||||
|
||||
void QskArcMetrics::setProportional( bool on ) noexcept
|
||||
{
|
||||
m_proportional = on;
|
||||
}
|
||||
|
||||
bool QskArcMetrics::isClosed() const
|
||||
{
|
||||
return qAbs( m_spanAngle ) >= 360.0;
|
||||
|
@ -92,7 +87,7 @@ QskArcMetrics QskArcMetrics::interpolated(
|
|||
const qreal s1 = qskInterpolated( m_startAngle, to.m_startAngle, ratio );
|
||||
const qreal s2 = qskInterpolated( endAngle(), to.endAngle(), ratio );
|
||||
|
||||
return QskArcMetrics( s1, s2 - s1, thickness, sizeMode(), to.isProportional() );
|
||||
return QskArcMetrics( s1, s2 - s1, thickness, sizeMode() );
|
||||
}
|
||||
|
||||
QVariant QskArcMetrics::interpolate(
|
||||
|
@ -119,8 +114,7 @@ QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept
|
|||
return *this;
|
||||
|
||||
const qreal t = qskEffectiveThickness( radius, m_thickness );
|
||||
return QskArcMetrics( m_startAngle, m_spanAngle, t,
|
||||
Qt::AbsoluteSize, m_proportional );
|
||||
return QskArcMetrics( m_startAngle, m_spanAngle, t, Qt::AbsoluteSize );
|
||||
}
|
||||
|
||||
QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const
|
||||
|
@ -136,18 +130,7 @@ QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const
|
|||
if ( t <= 0.0 || qFuzzyIsNull( m_spanAngle ) )
|
||||
return QPainterPath();
|
||||
|
||||
auto tx = t;
|
||||
auto ty = t;
|
||||
|
||||
if ( m_proportional )
|
||||
{
|
||||
const auto sz = qMin( ellipseRect.width(), ellipseRect.height() );
|
||||
|
||||
tx *= ellipseRect.width() / sz;
|
||||
ty *= ellipseRect.height() / sz;
|
||||
}
|
||||
|
||||
const auto innerRect = ellipseRect.adjusted( tx, ty, -tx, -ty );
|
||||
const auto innerRect = ellipseRect.adjusted( t, t, -t, -t );
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
|
@ -168,32 +151,18 @@ QPainterPath QskArcMetrics::painterPath( const QRectF& ellipseRect ) const
|
|||
}
|
||||
else
|
||||
{
|
||||
if ( qAbs( m_spanAngle ) >= 360.0 )
|
||||
{
|
||||
path.addEllipse( ellipseRect );
|
||||
const auto t2 = 0.5 * t;
|
||||
const auto r = ellipseRect.adjusted( t2, t2, -t2, -t2 );
|
||||
|
||||
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, m_startAngle + m_spanAngle );
|
||||
const auto pos = path.currentPosition();
|
||||
QPainterPath arcPath;
|
||||
arcPath.arcMoveTo( r, m_startAngle ); // replaces the dummy arcMoveTo above
|
||||
arcPath.arcTo( r, m_startAngle, m_spanAngle );
|
||||
|
||||
path.arcMoveTo( ellipseRect, m_startAngle ); // replaces the dummy arcMoveTo above
|
||||
path.arcTo( ellipseRect, m_startAngle, m_spanAngle );
|
||||
QPainterPathStroker stroker;
|
||||
stroker.setCapStyle( Qt::FlatCap );
|
||||
stroker.setWidth( t );
|
||||
|
||||
path.lineTo( pos );
|
||||
path.arcTo( innerRect, m_startAngle + m_spanAngle, -m_spanAngle );
|
||||
|
||||
path.closeSubpath();
|
||||
}
|
||||
path = stroker.createStroke( arcPath );
|
||||
}
|
||||
|
||||
return path;
|
||||
|
@ -227,9 +196,8 @@ QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept
|
|||
auto hash = qHash( m_thickness, seed );
|
||||
hash = qHash( m_startAngle, hash );
|
||||
hash = qHash( m_spanAngle, hash );
|
||||
hash = qHash( m_relativeSize, hash );
|
||||
|
||||
return qHash( m_proportional, hash );
|
||||
return qHash( m_relativeSize, hash );
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
|
@ -242,8 +210,7 @@ QDebug operator<<( QDebug debug, const QskArcMetrics& metrics )
|
|||
debug.nospace();
|
||||
|
||||
debug << "QskArcMetrics" << '(';
|
||||
debug << metrics.thickness() << ',' << metrics.sizeMode() << ','
|
||||
<< metrics.isProportional();
|
||||
debug << metrics.thickness() << ',' << metrics.sizeMode();
|
||||
debug << ",[" << metrics.startAngle() << ',' << metrics.spanAngle() << ']';
|
||||
debug << ')';
|
||||
|
||||
|
|
|
@ -22,16 +22,15 @@ class QSK_EXPORT QskArcMetrics
|
|||
|
||||
Q_PROPERTY( qreal thickness READ thickness WRITE setThickness )
|
||||
Q_PROPERTY( Qt::SizeMode sizeMode READ sizeMode WRITE setSizeMode )
|
||||
Q_PROPERTY( bool proportional READ isProportional WRITE setProportional )
|
||||
|
||||
public:
|
||||
constexpr QskArcMetrics() noexcept = default;
|
||||
|
||||
constexpr QskArcMetrics( qreal thickness,
|
||||
Qt::SizeMode = Qt::AbsoluteSize, bool proportional = false ) noexcept;
|
||||
Qt::SizeMode = Qt::AbsoluteSize ) noexcept;
|
||||
|
||||
constexpr QskArcMetrics( qreal startAngle, qreal spanAngle, qreal thickness,
|
||||
Qt::SizeMode = Qt::AbsoluteSize, bool proportional = false ) noexcept;
|
||||
Qt::SizeMode = Qt::AbsoluteSize ) noexcept;
|
||||
|
||||
bool operator==( const QskArcMetrics& ) const noexcept;
|
||||
bool operator!=( const QskArcMetrics& ) const noexcept;
|
||||
|
@ -53,19 +52,6 @@ class QSK_EXPORT QskArcMetrics
|
|||
void setThickness( qreal ) noexcept;
|
||||
constexpr qreal thickness() const noexcept;
|
||||
|
||||
/*
|
||||
A proportional arc scales the thickness of the arc according to the
|
||||
aspect ratio of the target rectangle. F.e when having a 20x10 rectangle
|
||||
the thickness in west/east direction is doubled, while for a
|
||||
10x20 rectangle the thickness in north/south direction is doubled.
|
||||
This matches the lines that result from a filling with a conic gradient.
|
||||
|
||||
A non proportional arc will have a fixed thickness regardless of
|
||||
the aspect ratio.
|
||||
*/
|
||||
void setProportional( bool ) noexcept;
|
||||
constexpr bool isProportional() const noexcept;
|
||||
|
||||
void setSizeMode( Qt::SizeMode ) noexcept;
|
||||
constexpr Qt::SizeMode sizeMode() const noexcept;
|
||||
|
||||
|
@ -92,22 +78,20 @@ class QSK_EXPORT QskArcMetrics
|
|||
qreal m_thickness = 0.0;
|
||||
|
||||
bool m_relativeSize = false;
|
||||
bool m_proportional = false;
|
||||
};
|
||||
|
||||
inline constexpr QskArcMetrics::QskArcMetrics(
|
||||
qreal thickness, Qt::SizeMode sizeMode, bool proportional ) noexcept
|
||||
: QskArcMetrics( 0.0, 360.0, thickness, sizeMode, proportional )
|
||||
qreal thickness, Qt::SizeMode sizeMode ) noexcept
|
||||
: QskArcMetrics( 0.0, 360.0, thickness, sizeMode )
|
||||
{
|
||||
}
|
||||
|
||||
inline constexpr QskArcMetrics::QskArcMetrics( qreal startAngle, qreal spanAngle,
|
||||
qreal thickness, Qt::SizeMode sizeMode, bool proportional ) noexcept
|
||||
qreal thickness, Qt::SizeMode sizeMode ) noexcept
|
||||
: m_startAngle( startAngle )
|
||||
, m_spanAngle( spanAngle )
|
||||
, m_thickness( thickness )
|
||||
, m_relativeSize( sizeMode == Qt::RelativeSize )
|
||||
, m_proportional( proportional )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -117,8 +101,7 @@ inline bool QskArcMetrics::operator==(
|
|||
return qskFuzzyCompare( m_thickness, other.m_thickness )
|
||||
&& qskFuzzyCompare( m_startAngle, other.m_startAngle )
|
||||
&& qskFuzzyCompare( m_spanAngle, other.m_spanAngle )
|
||||
&& ( m_relativeSize == other.m_relativeSize )
|
||||
&& ( m_proportional == other.m_proportional );
|
||||
&& ( m_relativeSize == other.m_relativeSize );
|
||||
}
|
||||
|
||||
inline bool QskArcMetrics::operator!=(
|
||||
|
@ -162,11 +145,6 @@ inline constexpr Qt::SizeMode QskArcMetrics::sizeMode() const noexcept
|
|||
return m_relativeSize ? Qt::RelativeSize : Qt::AbsoluteSize;
|
||||
}
|
||||
|
||||
inline constexpr bool QskArcMetrics::isProportional() const noexcept
|
||||
{
|
||||
return m_proportional;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
|
||||
class QDebug;
|
||||
|
|
|
@ -16,15 +16,21 @@
|
|||
|
||||
#include <qpainterpath.h>
|
||||
|
||||
#define ARC_RENDERER
|
||||
// #define ARC_BORDER_NODE
|
||||
#define ARC_FILL_NODE
|
||||
|
||||
#ifdef ARC_RENDERER
|
||||
#ifdef ARC_BORDER_NODE
|
||||
using BorderNode = QskArcRenderNode;
|
||||
#else
|
||||
#include <qpen.h>
|
||||
using BorderNode = QskStrokeNode;
|
||||
#endif
|
||||
|
||||
#ifdef ARC_FILL_NODE
|
||||
using FillNode = QskArcRenderNode;
|
||||
#else
|
||||
using FillNode = QskShapeNode;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -114,7 +120,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
auto shadowNode = static_cast< QskArcShadowNode* >(
|
||||
QskSGNode::findChildNode( this, ShadowRole ) );
|
||||
|
||||
auto fillNode = static_cast< QskShapeNode* >(
|
||||
auto fillNode = static_cast< FillNode* >(
|
||||
QskSGNode::findChildNode( this, FillRole ) );
|
||||
|
||||
auto borderNode = static_cast< BorderNode* >(
|
||||
|
@ -168,11 +174,15 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
{
|
||||
if ( fillNode == nullptr )
|
||||
{
|
||||
fillNode = new QskShapeNode;
|
||||
fillNode = new FillNode;
|
||||
QskSGNode::setNodeRole( fillNode, FillRole );
|
||||
}
|
||||
|
||||
#ifdef ARC_FILL_NODE
|
||||
fillNode->updateNode( arcRect, metricsArc, gradient );
|
||||
#else
|
||||
fillNode->updateNode( path, QTransform(), arcRect, gradient );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -188,9 +198,8 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
|
|||
QskSGNode::setNodeRole( borderNode, BorderRole );
|
||||
}
|
||||
|
||||
#ifdef ARC_RENDERER
|
||||
borderNode->updateNode( arcRect, metricsArc, borderWidth,
|
||||
borderColor, gradient );
|
||||
#ifdef ARC_BORDER_NODE
|
||||
borderNode->updateNode( arcRect, metricsArc, borderWidth, borderColor );
|
||||
#else
|
||||
QPen pen( borderColor, borderWidth );
|
||||
pen.setCapStyle( Qt::FlatCap );
|
||||
|
|
|
@ -49,6 +49,18 @@ QskArcRenderNode::QskArcRenderNode()
|
|||
setFlag( QSGNode::OwnsMaterial, false );
|
||||
}
|
||||
|
||||
void QskArcRenderNode::updateNode( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, const QskGradient& gradient )
|
||||
{
|
||||
updateNode( rect, metrics, 0.0, QColor(), gradient );
|
||||
}
|
||||
|
||||
void QskArcRenderNode::updateNode( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, qreal borderWidth, const QColor& borderColor )
|
||||
{
|
||||
updateNode( rect, metrics, borderWidth, borderColor, QskGradient() );
|
||||
}
|
||||
|
||||
void QskArcRenderNode::updateNode(
|
||||
const QRectF& rect, const QskArcMetrics& metrics, qreal borderWidth,
|
||||
const QColor& borderColor, const QskGradient& gradient )
|
||||
|
@ -86,8 +98,16 @@ void QskArcRenderNode::updateNode(
|
|||
{
|
||||
d->hash = hash;
|
||||
|
||||
if ( borderWidth > 0.0 )
|
||||
{
|
||||
QskArcRenderer::renderBorder(
|
||||
rect, metrics, borderWidth, borderColor, *geometry() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QskArcRenderer::renderFillGeometry(
|
||||
rect, metrics, *geometry() );
|
||||
}
|
||||
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
|
|
|
@ -21,6 +21,10 @@ class QSK_EXPORT QskArcRenderNode : public QSGGeometryNode
|
|||
public:
|
||||
QskArcRenderNode();
|
||||
|
||||
void updateNode( const QRectF&, const QskArcMetrics&, const QskGradient& );
|
||||
void updateNode( const QRectF&, const QskArcMetrics&, qreal borderWidth,
|
||||
const QColor& borderColor );
|
||||
|
||||
void updateNode( const QRectF&, const QskArcMetrics&, qreal borderWidth,
|
||||
const QColor& borderColor, const QskGradient& );
|
||||
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
#include "QskVertex.h"
|
||||
|
||||
#include <qsggeometry.h>
|
||||
|
||||
#if 1
|
||||
#include <qdebug.h>
|
||||
#endif
|
||||
|
||||
static inline QskVertex::Line* qskAllocateLines(
|
||||
QSGGeometry& geometry, int lineCount )
|
||||
|
@ -28,36 +25,6 @@ static inline QskVertex::ColoredLine* qskAllocateColoredLines(
|
|||
return reinterpret_cast< QskVertex::ColoredLine* >( geometry.vertexData() );
|
||||
}
|
||||
|
||||
static inline int qskApproximatedCircumference( const QRectF& rect )
|
||||
{
|
||||
const qreal a = rect.width();
|
||||
const qreal b = rect.height();
|
||||
|
||||
const auto ratio = a / b;
|
||||
if ( ratio > 0.9 || ratio < 1.1 )
|
||||
return std::max( a, b ) * 2.0 * M_PI; // circle
|
||||
|
||||
// Srinivasa Ramanujan: https://en.wikipedia.org/wiki/Ellipse#Circumference
|
||||
|
||||
const qreal d1 = ( a - b );
|
||||
const qreal d2 = ( a + b );
|
||||
const qreal h = ( d1 * d1 ) / ( d2 * d2 );
|
||||
|
||||
return M_PI * d2 * ( 1.0 + ( 3 * h / ( 10 + sqrt( 4 - 3 * h ) ) ) );
|
||||
}
|
||||
|
||||
static inline int qskStepCount( const QRectF& rect )
|
||||
{
|
||||
#if 0
|
||||
const auto dist = 3.0;
|
||||
#else
|
||||
const auto dist = 20.0;
|
||||
#endif
|
||||
|
||||
const int length = qskApproximatedCircumference( rect );
|
||||
return std::max( 3, qCeil( length / dist ) );
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class AngleIterator
|
||||
|
@ -73,14 +40,13 @@ namespace
|
|||
|
||||
inline int step() const { return m_stepIndex; }
|
||||
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; }
|
||||
|
||||
private:
|
||||
double m_cos;
|
||||
double m_sin;
|
||||
|
||||
int m_stepIndex;
|
||||
|
||||
int m_stepCount;
|
||||
|
||||
const double m_radians1;
|
||||
|
@ -93,7 +59,7 @@ namespace
|
|||
, m_stepCount( stepCount )
|
||||
, m_radians1( radians1 )
|
||||
, m_radians2( radians2 )
|
||||
, m_radiansStep( ( radians2 - radians1 ) / stepCount )
|
||||
, m_radiansStep( ( radians2 - radians1 ) / ( stepCount - 1 ) )
|
||||
{
|
||||
m_cos = qFastCos( radians1 );
|
||||
m_sin = qFastSin( radians1 );
|
||||
|
@ -130,15 +96,11 @@ namespace
|
|||
int borderCount() const;
|
||||
|
||||
int setBorderLines( QskVertex::ColoredLine*, const QskVertex::Color ) const;
|
||||
int setBorderLines( QskVertex::Line* ) const;
|
||||
int setFillLines( QskVertex::ColoredLine*, const QskVertex::Color ) const;
|
||||
|
||||
private:
|
||||
int arcLineCount() const;
|
||||
|
||||
void setArcLines( QskVertex::ColoredLine*, int lineCount,
|
||||
const QPointF&, const QSizeF&,
|
||||
const qreal radians1, const qreal radians2,
|
||||
qreal arcWidth, const QskVertex::Color ) const;
|
||||
int arcLineCount( qreal radians = 2.0 * M_PI ) const;
|
||||
QLineF fillLineAt( qreal x, qreal y, qreal sin, qreal cos ) const;
|
||||
|
||||
const QRectF& m_rect;
|
||||
const QskArcMetrics& m_metrics;
|
||||
|
@ -156,17 +118,35 @@ namespace
|
|||
|
||||
int Stroker::fillCount() const
|
||||
{
|
||||
return 0; // TODO
|
||||
int n = 0;
|
||||
|
||||
qreal radians1 = qDegreesToRadians( m_metrics.startAngle() );
|
||||
qreal radians2 = qDegreesToRadians( m_metrics.endAngle() );
|
||||
|
||||
if ( radians2 < radians1 )
|
||||
qSwap( radians1, radians2 );
|
||||
|
||||
for ( auto r = qFloor( radians1 / M_PI_2 ) * M_PI_2;
|
||||
r < radians2; r += M_PI_2 )
|
||||
{
|
||||
const auto r1 = qMax( r, radians1 );
|
||||
const auto r2 = qMin( r + M_PI_2, radians2 );
|
||||
|
||||
n += arcLineCount( r2 - r1 );
|
||||
}
|
||||
|
||||
int Stroker::arcLineCount() const
|
||||
{
|
||||
if ( m_metrics.isNull() )
|
||||
return 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
int n = qskStepCount( m_rect );
|
||||
if ( !m_metrics.isClosed() )
|
||||
n = qCeil( n * qAbs( m_metrics.spanAngle() ) / 360.0 );
|
||||
int Stroker::arcLineCount( const qreal radians ) const
|
||||
{
|
||||
// not very sophisticated - TODO ...
|
||||
|
||||
const auto ratio = qAbs( radians ) / ( 2.0 * M_PI );
|
||||
|
||||
int n = ( m_rect.width() + m_rect.height() ) * M_PI_2;
|
||||
n = qBound( 3, n, 80 );
|
||||
n = qCeil( n * ratio );
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -176,122 +156,115 @@ namespace
|
|||
if ( m_metrics.isNull() )
|
||||
return 0;
|
||||
|
||||
return 2 * arcLineCount() + 1;
|
||||
}
|
||||
|
||||
void Stroker::setArcLines( QskVertex::ColoredLine* lines, int lineCount,
|
||||
const QPointF& center, const QSizeF& size,
|
||||
const qreal radians1, const qreal radians2,
|
||||
qreal arcWidth, const QskVertex::Color color ) const
|
||||
{
|
||||
const auto w1 = size.width();
|
||||
const auto h1 = size.height();
|
||||
|
||||
const auto w2 = w1 - arcWidth;
|
||||
const auto h2 = h1 - arcWidth;
|
||||
|
||||
auto l = lines;
|
||||
|
||||
for ( AngleIterator it( radians1, radians2, lineCount - 1 ); !it.isDone(); ++it )
|
||||
{
|
||||
const auto x1 = center.x() + w1 * it.cos();
|
||||
const auto x2 = center.x() + w2 * it.cos();
|
||||
|
||||
const auto y1 = center.y() + h1 * it.sin();
|
||||
const auto y2 = center.y() + h2 * it.sin();
|
||||
|
||||
l++->setLine( x1, y1, x2, y2, color );
|
||||
}
|
||||
|
||||
if ( l - lines != lineCount )
|
||||
qWarning() << lineCount << "->" << l - lines;
|
||||
Q_ASSERT( l - lines == lineCount );
|
||||
}
|
||||
|
||||
int Stroker::setBorderLines( QskVertex::Line* ) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Stroker::setBorderLines( QskVertex::ColoredLine* lines,
|
||||
const QskVertex::Color color ) const
|
||||
{
|
||||
Q_UNUSED( lines );
|
||||
Q_UNUSED( color );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Stroker::setFillLines( QskVertex::ColoredLine* lines,
|
||||
const QskVertex::Color color ) const
|
||||
{
|
||||
qreal radians1 = qDegreesToRadians( m_metrics.startAngle() );
|
||||
qreal radians2 = qDegreesToRadians( m_metrics.endAngle() );
|
||||
|
||||
if ( m_metrics.spanAngle() < 0.0 )
|
||||
std::swap( radians1, radians2 );
|
||||
|
||||
const qreal w = 0.5 * ( m_rect.width() - m_metrics.thickness() );
|
||||
const qreal h = 0.5 * ( m_rect.height() - m_metrics.thickness() );
|
||||
|
||||
const auto center = m_rect.center();
|
||||
|
||||
const qreal radians1 = qDegreesToRadians( m_metrics.startAngle() );
|
||||
const qreal radians2 = qDegreesToRadians( m_metrics.endAngle() );
|
||||
auto l = lines;
|
||||
|
||||
const int n = arcLineCount();
|
||||
|
||||
auto size = 0.5 * m_rect.size();
|
||||
|
||||
setArcLines( lines, n, center, size,
|
||||
radians1, radians2, m_borderWidth, color );
|
||||
|
||||
const bool stretched = true;
|
||||
|
||||
if ( !stretched )
|
||||
for ( auto r = qFloor( radians1 / M_PI_2 ) * M_PI_2;
|
||||
r < radians2; r += M_PI_2 )
|
||||
{
|
||||
size.rwidth() -= m_metrics.thickness() - m_borderWidth;
|
||||
size.rheight() -= m_metrics.thickness() - m_borderWidth;
|
||||
}
|
||||
else
|
||||
const auto r1 = qMax( r, radians1 );
|
||||
const auto r2 = qMin( r + M_PI_2, radians2 );
|
||||
|
||||
const auto lineCount = arcLineCount( r2 - r1 );
|
||||
|
||||
for ( AngleIterator it( r1, r2, lineCount ); !it.isDone(); ++it )
|
||||
{
|
||||
qreal tx = m_metrics.thickness();
|
||||
qreal ty = m_metrics.thickness();
|
||||
const auto line = fillLineAt( w, h, it.cos(), it.sin() );
|
||||
|
||||
const qreal ratio = m_rect.width() / m_rect.height();
|
||||
|
||||
if ( ratio >= 1.0 )
|
||||
tx *= ratio;
|
||||
else
|
||||
ty /= ratio;
|
||||
|
||||
size.rwidth() -= tx;
|
||||
size.rheight() -= ty;
|
||||
}
|
||||
|
||||
setArcLines( lines + n + 1, n, center, size,
|
||||
radians2, radians1, m_borderWidth, color );
|
||||
|
||||
lines[n] = { lines[n - 1].p2, lines[n + 1].p1 };
|
||||
|
||||
return 2 * n + 1;
|
||||
l++->setLine( center.x() + line.x1(), center.y() - line.y1(),
|
||||
center.x() + line.x2(), center.y() - line.y2(), color );
|
||||
}
|
||||
}
|
||||
|
||||
void QskArcRenderer::renderBorderGeometry( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, qreal borderWidth, QSGGeometry& geometry )
|
||||
{
|
||||
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
||||
|
||||
Stroker stroker( rect, metrics, borderWidth );
|
||||
|
||||
const auto lineCount = stroker.borderCount();
|
||||
|
||||
const auto lines = qskAllocateLines( geometry, lineCount );
|
||||
if ( lines )
|
||||
{
|
||||
const auto effectiveCount = stroker.setBorderLines( lines );
|
||||
if ( lineCount != effectiveCount )
|
||||
{
|
||||
qWarning() << lineCount << effectiveCount;
|
||||
return l - lines;
|
||||
}
|
||||
|
||||
inline qreal sqr( qreal x )
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
QLineF Stroker::fillLineAt( qreal w, qreal h, qreal cos, qreal sin ) const
|
||||
{
|
||||
const auto x = w * cos;
|
||||
const auto y = h * sin;
|
||||
|
||||
/*
|
||||
The inner/outer points are found by shifting along the
|
||||
normal vector of the tangent at the ellipse point.
|
||||
*/
|
||||
|
||||
const auto t = 0.5 * m_metrics.thickness();
|
||||
|
||||
if ( qFuzzyIsNull( sin ) )
|
||||
{
|
||||
const qreal dx = cos * t;
|
||||
return QLineF( x + dx, y, x - dx, y );
|
||||
}
|
||||
else if ( qFuzzyIsNull( cos ) )
|
||||
{
|
||||
const qreal dy = sin * t;
|
||||
return QLineF( x, y + dy, x, y - dy );
|
||||
}
|
||||
|
||||
const qreal m = qSqrt( w * w - x * x ) * ( w / h ) / x;
|
||||
const auto dt = t * qSqrt( ( 1.0 / ( 1.0 + m * m ) ) );
|
||||
|
||||
const qreal dx = ( x >= 0 ) ? dt : -dt;
|
||||
const qreal dy = m * ( ( y >= 0 ) ? dx : -dx );
|
||||
|
||||
const auto x1 = x + dx;
|
||||
const auto y1 = y + dy;
|
||||
|
||||
const auto x2 = x - dx;
|
||||
const auto y2 = y - dy;
|
||||
|
||||
return QLineF( x1, y1, x2, y2 );
|
||||
}
|
||||
}
|
||||
|
||||
void QskArcRenderer::renderFillGeometry( const QRectF& rect,
|
||||
const QskArcMetrics& metrics, qreal borderWidth, QSGGeometry& geometry )
|
||||
{
|
||||
Q_UNUSED( rect );
|
||||
Q_UNUSED( geometry );
|
||||
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
|
||||
|
||||
Stroker stroker( rect, metrics, borderWidth );
|
||||
|
||||
const auto lines = qskAllocateColoredLines( geometry, stroker.fillCount() );
|
||||
const auto lineCount = stroker.fillCount();
|
||||
|
||||
const auto lines = qskAllocateColoredLines( geometry, lineCount );
|
||||
if ( lines )
|
||||
{
|
||||
// TODO
|
||||
const auto effectiveCount = stroker.setFillLines( lines, QColor( Qt::darkRed ) );
|
||||
if ( lineCount != effectiveCount )
|
||||
{
|
||||
qWarning() << lineCount << effectiveCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue