working on arcs
This commit is contained in:
parent
7f2fbc7bd0
commit
73dd618626
|
@ -25,13 +25,6 @@ static inline qreal qskInterpolated( qreal from, qreal to, qreal ratio )
|
||||||
return from + ( to - from ) * ratio;
|
return from + ( to - from ) * ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline qreal qskAbsoluted( qreal length, qreal percentage )
|
|
||||||
{
|
|
||||||
// 100% means -> 0.5 of length
|
|
||||||
percentage = qBound( 0.0, percentage, 100.0 );
|
|
||||||
return percentage / 100.0 * 0.5 * length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskArcMetrics::setThickness( qreal thickness ) noexcept
|
void QskArcMetrics::setThickness( qreal thickness ) noexcept
|
||||||
{
|
{
|
||||||
m_thickness = thickness;
|
m_thickness = thickness;
|
||||||
|
@ -93,32 +86,33 @@ QVariant QskArcMetrics::interpolate(
|
||||||
return QVariant::fromValue( from.interpolated( to, progress ) );
|
return QVariant::fromValue( from.interpolated( to, progress ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskArcMetrics QskArcMetrics::toAbsolute( const QSizeF& size ) const noexcept
|
QskArcMetrics QskArcMetrics::toAbsolute( qreal radiusX, qreal radiusY ) const noexcept
|
||||||
{
|
{
|
||||||
if ( size.width() < 0.0 )
|
if ( radiusX < 0.0 )
|
||||||
return toAbsolute( size.height() );
|
return toAbsolute( radiusY );
|
||||||
|
|
||||||
if ( size.height() < 0.0 )
|
if ( radiusY < 0.0 )
|
||||||
return toAbsolute( size.width() );
|
return toAbsolute( radiusX );
|
||||||
|
|
||||||
return toAbsolute( qMin( size.width(), size.height() ) );
|
return toAbsolute( qMin( radiusX, radiusY ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskArcMetrics QskArcMetrics::toAbsolute( qreal size ) const noexcept
|
QskArcMetrics QskArcMetrics::toAbsolute( qreal radius ) const noexcept
|
||||||
{
|
{
|
||||||
if ( m_sizeMode != Qt::RelativeSize )
|
if ( m_sizeMode != Qt::RelativeSize )
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
QskArcMetrics absoluted = *this;
|
QskArcMetrics m = *this;
|
||||||
|
|
||||||
if ( size <= 0.0 )
|
if ( radius < 0.0 )
|
||||||
absoluted.m_thickness = 0.0;
|
radius = 0.0;
|
||||||
else
|
|
||||||
absoluted.m_thickness = qskAbsoluted( size, absoluted.m_thickness );
|
|
||||||
|
|
||||||
absoluted.m_sizeMode = Qt::AbsoluteSize;
|
const auto ratio = qBound( 0.0, m.m_thickness, 100.0 ) / 100.0;
|
||||||
|
|
||||||
return absoluted;
|
m.m_thickness = radius * ratio;
|
||||||
|
m.m_sizeMode = Qt::AbsoluteSize;
|
||||||
|
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept
|
QskHashValue QskArcMetrics::hash( QskHashValue seed ) const noexcept
|
||||||
|
|
|
@ -56,8 +56,8 @@ class QSK_EXPORT QskArcMetrics
|
||||||
QskArcMetrics interpolated( const QskArcMetrics&,
|
QskArcMetrics interpolated( const QskArcMetrics&,
|
||||||
qreal value ) const noexcept;
|
qreal value ) const noexcept;
|
||||||
|
|
||||||
QskArcMetrics toAbsolute( const QSizeF& ) const noexcept;
|
QskArcMetrics toAbsolute( qreal radiusX, qreal radiusY ) const noexcept;
|
||||||
QskArcMetrics toAbsolute( qreal ) const noexcept;
|
QskArcMetrics toAbsolute( qreal radius ) const noexcept;
|
||||||
|
|
||||||
QskHashValue hash( QskHashValue seed = 0 ) const noexcept;
|
QskHashValue hash( QskHashValue seed = 0 ) const noexcept;
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,10 @@ static inline QSGNode* qskUpdateArcNode(
|
||||||
if ( rect.isEmpty() )
|
if ( rect.isEmpty() )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const auto absoluteMetrics = metrics.toAbsolute( rect.size() );
|
const auto rx = 0.5 * rect.width();
|
||||||
|
const auto ry = 0.5 * rect.height();
|
||||||
|
|
||||||
|
const auto absoluteMetrics = metrics.toAbsolute( rx, ry );
|
||||||
|
|
||||||
if ( !qskIsArcVisible( absoluteMetrics, fillGradient ) )
|
if ( !qskIsArcVisible( absoluteMetrics, fillGradient ) )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -11,31 +11,29 @@
|
||||||
|
|
||||||
#include <qpainterpath.h>
|
#include <qpainterpath.h>
|
||||||
|
|
||||||
static inline QskGradient effectiveGradient( const QRectF& rect,
|
#define LINEAR_GRADIENT_HACK 1
|
||||||
const QskArcMetrics& metrics, const QskGradient& gradient )
|
|
||||||
|
#if LINEAR_GRADIENT_HACK
|
||||||
|
|
||||||
|
static inline QskGradient buildGradient( QskGradient::Type type,
|
||||||
|
const QRectF& rect, const QskArcMetrics& metrics,
|
||||||
|
const QskGradientStops& stops )
|
||||||
{
|
{
|
||||||
if ( gradient.isMonochrome() )
|
|
||||||
return gradient;
|
|
||||||
|
|
||||||
bool isRadial = false;
|
|
||||||
|
|
||||||
if ( gradient.type() == QskGradient::Linear )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Horizontal is interpreted as conic ( in direction of the arc ),
|
|
||||||
while Vertical means radial ( inner to outer border )
|
|
||||||
*/
|
|
||||||
isRadial = gradient.linearDirection().isVertical();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto g = gradient;
|
|
||||||
g.setStretchMode( QskGradient::NoStretch );
|
|
||||||
|
|
||||||
const auto center = rect.center();
|
const auto center = rect.center();
|
||||||
|
|
||||||
if( isRadial )
|
QskGradient gradient;
|
||||||
|
gradient.setStretchMode( QskGradient::NoStretch );
|
||||||
|
|
||||||
|
if ( type == QskGradient::Conic )
|
||||||
{
|
{
|
||||||
g.setRadialDirection( center.x(), center.y(),
|
gradient.setConicDirection(
|
||||||
|
center.x(), center.y(), metrics.startAngle() );
|
||||||
|
|
||||||
|
gradient.setStops( stops );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gradient.setRadialDirection( center.x(), center.y(),
|
||||||
rect.width(), rect.height() );
|
rect.width(), rect.height() );
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -47,24 +45,58 @@ static inline QskGradient effectiveGradient( const QRectF& rect,
|
||||||
const auto radius = 0.5 * qMin( rect.width(), rect.height() );
|
const auto radius = 0.5 * qMin( rect.width(), rect.height() );
|
||||||
const auto t = metrics.thickness() / radius;
|
const auto t = metrics.thickness() / radius;
|
||||||
|
|
||||||
QskGradientStops stops;
|
QskGradientStops scaledStops;
|
||||||
stops.reserve( gradient.stops().size() );
|
scaledStops.reserve( stops.size() );
|
||||||
|
|
||||||
for ( const auto& stop : gradient.stops() )
|
for ( const auto& stop : stops )
|
||||||
{
|
{
|
||||||
const auto pos = 0.5 - t * ( 0.75 - stop.position() );
|
const auto pos = 0.5 - t * ( 0.75 - stop.position() );
|
||||||
stops += QskGradientStop( pos, stop.color() );
|
scaledStops += QskGradientStop( pos, stop.color() );
|
||||||
}
|
}
|
||||||
|
|
||||||
g.setStops( stops );
|
gradient.setStops( scaledStops );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline QskGradient effectiveGradient( const QRectF& rect,
|
||||||
|
const QskArcMetrics& metrics, const QskGradient& gradient )
|
||||||
|
{
|
||||||
|
if ( !gradient.isMonochrome() )
|
||||||
{
|
{
|
||||||
g.setConicDirection( center.x(), center.y(), metrics.startAngle() );
|
if ( gradient.type() == QskGradient::Stops )
|
||||||
|
{
|
||||||
|
const QskConicDirection dir(
|
||||||
|
rect.center(), metrics.startAngle() );
|
||||||
|
#if 0
|
||||||
|
dir.setSpanAngle( metrics.spanAngle() ); // what is "expected" ??
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QskGradient g( gradient.stops() );
|
||||||
|
g.setStretchMode( QskGradient::NoStretch );
|
||||||
|
g.setConicDirection( dir );
|
||||||
|
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINEAR_GRADIENT_HACK
|
||||||
|
if ( gradient.type() == QskGradient::Linear )
|
||||||
|
{
|
||||||
|
// to keep the iotdashboard working: to be removed
|
||||||
|
|
||||||
|
const auto type = gradient.linearDirection().isHorizontal()
|
||||||
|
? QskGradient::Conic : QskGradient::Radial;
|
||||||
|
|
||||||
|
return buildGradient( type, rect, metrics, gradient.stops() );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return g;
|
return gradient;
|
||||||
}
|
}
|
||||||
|
|
||||||
QskArcNode::QskArcNode()
|
QskArcNode::QskArcNode()
|
||||||
|
@ -78,22 +110,8 @@ QskArcNode::~QskArcNode()
|
||||||
void QskArcNode::setArcData( const QRectF& rect,
|
void QskArcNode::setArcData( const QRectF& rect,
|
||||||
const QskArcMetrics& metrics, const QskGradient& gradient )
|
const QskArcMetrics& metrics, const QskGradient& gradient )
|
||||||
{
|
{
|
||||||
#if 1
|
|
||||||
/*
|
|
||||||
Translating linear gradients into conic or radial gradients.
|
|
||||||
This code is a leftover from situations, where only linear
|
|
||||||
gradients had been available. Once the iotdashboard example
|
|
||||||
has been adjusted we will remove this code TODO ...
|
|
||||||
*/
|
|
||||||
const auto g = effectiveGradient( rect, metrics, gradient );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
For the moment using a QPainterPath/QskShapeNode.
|
|
||||||
But we can do better by creatig vertex lists manually
|
|
||||||
like what is done by the box renderer. TODO ...
|
|
||||||
*/
|
|
||||||
|
|
||||||
const auto path = QskArcRenderer::arcPath( rect, metrics );
|
const auto path = QskArcRenderer::arcPath( rect, metrics );
|
||||||
updateNode( path, QTransform(), rect, g );
|
|
||||||
|
updateNode( path, QTransform(), rect,
|
||||||
|
effectiveGradient( rect, metrics, gradient ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
class QskArcMetrics;
|
class QskArcMetrics;
|
||||||
class QskGradient;
|
class QskGradient;
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 QskShapeNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -9,10 +9,13 @@
|
||||||
#include <qpainterpath.h>
|
#include <qpainterpath.h>
|
||||||
#include <qrect.h>
|
#include <qrect.h>
|
||||||
|
|
||||||
QPainterPath QskArcRenderer::arcPath(
|
static inline QPainterPath qskArcPath(
|
||||||
const QRectF& rect, const QskArcMetrics& metrics )
|
const QRectF& rect, const QskArcMetrics& metrics )
|
||||||
{
|
{
|
||||||
const auto m = metrics.toAbsolute( rect.size() );
|
const auto rx = 0.5 * rect.width();
|
||||||
|
const auto ry = 0.5 * rect.height();
|
||||||
|
|
||||||
|
const auto m = metrics.toAbsolute( rx, ry );
|
||||||
|
|
||||||
const qreal t2 = 0.5 * m.thickness();
|
const qreal t2 = 0.5 * m.thickness();
|
||||||
const auto r = rect.adjusted( t2, t2, -t2, -t2 );
|
const auto r = rect.adjusted( t2, t2, -t2, -t2 );
|
||||||
|
@ -27,3 +30,59 @@ QPainterPath QskArcRenderer::arcPath(
|
||||||
|
|
||||||
return stroker.createStroke( path );
|
return stroker.createStroke( path );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline QRectF qskArcRect(
|
||||||
|
const QRectF& rect, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
return qskArcPath( rect, metrics ).controlPointRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath QskArcRenderer::arcPath(
|
||||||
|
qreal radius, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
const QRectF r( 0.0, 0.0, 2 * radius, 2 * radius );
|
||||||
|
return qskArcPath( r, metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath QskArcRenderer::arcPath(
|
||||||
|
const QSizeF& diameters, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() );
|
||||||
|
return qskArcPath( r, metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath QskArcRenderer::arcPath(
|
||||||
|
const QRectF& rect, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
return qskArcPath( rect, metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF QskArcRenderer::arcRect( qreal radius, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
const qreal d = 2.0 * radius;
|
||||||
|
return qskArcRect( QRectF( 0.0, 0.0, d, d ), metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF QskArcRenderer::arcRect( const QSizeF& diameters, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() );
|
||||||
|
return qskArcRect( r, metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF QskArcRenderer::arcRect( const QRectF& rect, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
return qskArcRect( rect, metrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF QskArcRenderer::arcSize(
|
||||||
|
const QSizeF& diameters, const QskArcMetrics& metrics )
|
||||||
|
{
|
||||||
|
if ( qFuzzyIsNull( metrics.spanAngle() ) )
|
||||||
|
return QSizeF();
|
||||||
|
|
||||||
|
if ( qAbs( metrics.spanAngle() ) >= 360.0 )
|
||||||
|
return diameters;
|
||||||
|
|
||||||
|
const QRectF r( 0.0, 0.0, diameters.width(), diameters.height() );
|
||||||
|
return qskArcRect( r, metrics ).size();
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,22 @@ class QskArcMetrics;
|
||||||
|
|
||||||
class QPainterPath;
|
class QPainterPath;
|
||||||
class QRectF;
|
class QRectF;
|
||||||
|
class QSizeF;
|
||||||
|
|
||||||
namespace QskArcRenderer
|
namespace QskArcRenderer
|
||||||
{
|
{
|
||||||
|
// radius
|
||||||
|
QSK_EXPORT QPainterPath arcPath( qreal radius, const QskArcMetrics& );
|
||||||
|
QSK_EXPORT QRectF arcRect( qreal radius, const QskArcMetrics& );
|
||||||
|
|
||||||
|
// diameter
|
||||||
|
QSK_EXPORT QPainterPath arcPath( const QSizeF&, const QskArcMetrics& );
|
||||||
|
QSK_EXPORT QSizeF arcSize( const QSizeF&, const QskArcMetrics& );
|
||||||
|
QSK_EXPORT QRectF arcRect( const QSizeF&, const QskArcMetrics& );
|
||||||
|
|
||||||
|
// bounding rectangle
|
||||||
QSK_EXPORT QPainterPath arcPath( const QRectF&, const QskArcMetrics& );
|
QSK_EXPORT QPainterPath arcPath( const QRectF&, const QskArcMetrics& );
|
||||||
|
QSK_EXPORT QRectF arcRect( const QRectF&, const QskArcMetrics& );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue