2017-10-17 17:29:02 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskBoxRenderer.h"
|
|
|
|
#include "QskBoxRendererColorMap.h"
|
|
|
|
#include "QskGradient.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskVertex.h"
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qmath.h>
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using namespace QskVertex;
|
|
|
|
|
|
|
|
class ContourLine
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-10-17 17:29:02 +02:00
|
|
|
inline void setLine( qreal x1, qreal y1, qreal value1,
|
|
|
|
qreal x2, qreal y2, qreal value2 )
|
|
|
|
{
|
|
|
|
p1.x = x1;
|
|
|
|
p1.y = y1;
|
|
|
|
p1.v = value1;
|
|
|
|
|
|
|
|
p2.x = x2;
|
|
|
|
p2.y = y2;
|
|
|
|
p2.v = value2;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QPointF pointAt( qreal value ) const
|
|
|
|
{
|
|
|
|
const qreal r = ( value - p1.v ) / ( p2.v - p1.v );
|
|
|
|
return QPointF( p1.x + r * ( p2.x - p1.x ), p1.y + r * ( p2.y - p1.y ) );
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
qreal x, y, v;
|
|
|
|
} p1, p2;
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class ValueCurve
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-10-17 17:29:02 +02:00
|
|
|
ValueCurve( const QskBoxRenderer::Metrics& m )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The slopes of the value line and those for the fill lines.
|
|
|
|
*/
|
|
|
|
const qreal mv = -( m.innerQuad.width / m.innerQuad.height );
|
|
|
|
const qreal md = m.innerQuad.height / m.innerQuad.width;
|
|
|
|
|
|
|
|
/*
|
|
|
|
first we find the point, where the tangent line
|
|
|
|
with the slope of md touches the top left border
|
|
|
|
of the ellipse around the top left corner point
|
|
|
|
*/
|
|
|
|
|
|
|
|
qreal xt, yt;
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto& c = m.corner[ Qt::TopLeftCorner ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
const qreal k = c.radiusInnerY / c.radiusInnerX * md;
|
|
|
|
const qreal u = ::sqrt( 1.0 + k * k );
|
|
|
|
|
|
|
|
const qreal dx = c.radiusInnerX / u;
|
|
|
|
const qreal dy = ::sqrt( 1.0 - 1.0 / ( u * u ) ) * c.radiusInnerY;
|
|
|
|
|
|
|
|
xt = c.centerX - dx;
|
|
|
|
yt = c.centerY - dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
the cutting point between the tangent and the diagonal of the
|
|
|
|
fill rectangle is the origin of our line, where we iterate
|
|
|
|
the values along.
|
|
|
|
*/
|
|
|
|
|
|
|
|
qreal left, top, right;
|
|
|
|
{
|
|
|
|
const auto& r = m.innerQuad;
|
|
|
|
|
|
|
|
left = ( yt - r.top - mv * xt + md * r.left ) / ( md - mv );
|
|
|
|
|
|
|
|
const qreal dx = left - r.left;
|
|
|
|
|
|
|
|
top = r.top + md * dx;
|
|
|
|
right = r.right - dx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Finally we can precaclculate the coefficients needed for
|
|
|
|
evaluating points in valueAt.
|
|
|
|
*/
|
|
|
|
|
|
|
|
{
|
|
|
|
const qreal k = mv + 1.0 / mv;
|
|
|
|
const qreal r = top + left / mv;
|
|
|
|
const qreal w = right - left;
|
|
|
|
|
|
|
|
m_coeff_0 = ( r / k - left ) / w;
|
|
|
|
m_coeff_y = -1.0 / ( k * w );
|
|
|
|
m_coeff_x = -mv * m_coeff_y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline qreal valueAt( qreal x, qreal y ) const
|
|
|
|
{
|
|
|
|
// The value, where the perpendicular runs through x,y
|
|
|
|
|
|
|
|
return m_coeff_0 + x * m_coeff_x + y * m_coeff_y;
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
qreal m_coeff_0, m_coeff_x, m_coeff_y;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ContourIterator
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
ContourIterator()
|
|
|
|
: m_clockwise( true )
|
|
|
|
, m_isLeading( true )
|
|
|
|
, m_isDone( false )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void setup( const QskBoxRenderer::Metrics& metrics,
|
|
|
|
bool isLeading, bool clockwise,
|
|
|
|
qreal cos, qreal cosStep, qreal sin, qreal sinStep,
|
|
|
|
qreal x1, qreal y1, qreal v1, qreal x2, qreal y2, qreal v2 )
|
|
|
|
{
|
|
|
|
m_cos = cos;
|
|
|
|
m_sin = sin;
|
|
|
|
|
|
|
|
m_cosStep = cosStep;
|
|
|
|
m_sinStep = sinStep;
|
|
|
|
|
|
|
|
m_stepInv1 = m_sinStep / m_cosStep;
|
|
|
|
m_stepInv2 = m_cosStep + m_sinStep * m_stepInv1;
|
|
|
|
|
|
|
|
m_contourLine.setLine( x1, y1, v1, x2, y2, v2 );
|
|
|
|
|
|
|
|
m_clockwise = clockwise;
|
|
|
|
m_isLeading = isLeading;
|
|
|
|
m_isDone = false;
|
|
|
|
|
|
|
|
m_corner = Qt::TopLeftCorner;
|
|
|
|
|
|
|
|
if ( clockwise )
|
|
|
|
{
|
|
|
|
if ( x2 >= metrics.corner[ Qt::TopRightCorner ].centerX )
|
|
|
|
setCorner( Qt::TopRightCorner, metrics );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( y2 >= metrics.corner[ Qt::BottomLeftCorner ].centerY )
|
|
|
|
setCorner( Qt::BottomLeftCorner, metrics );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isClockwise() const { return m_clockwise; }
|
|
|
|
inline bool isDone() const { return m_isDone; }
|
|
|
|
inline qreal value() const { return m_contourLine.p2.v; }
|
|
|
|
|
|
|
|
inline const ContourLine& contourLine() const { return m_contourLine; }
|
|
|
|
|
|
|
|
inline void advance( const QskBoxRenderer::Metrics& metrics, const ValueCurve& curve )
|
|
|
|
{
|
|
|
|
if ( m_isDone )
|
|
|
|
return;
|
|
|
|
|
|
|
|
using namespace Qt;
|
|
|
|
|
|
|
|
const auto& corners = metrics.corner;
|
|
|
|
const auto& c = corners[ m_corner ];
|
|
|
|
|
|
|
|
m_contourLine.p1 = m_contourLine.p2;
|
|
|
|
|
|
|
|
auto& p = m_contourLine.p2;
|
|
|
|
|
|
|
|
if ( m_clockwise )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( m_corner )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
case TopLeftCorner:
|
|
|
|
{
|
|
|
|
if ( p.x >= c.centerX )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
p.x = corners[ TopRightCorner ].centerX;
|
2017-10-17 17:29:02 +02:00
|
|
|
p.y = metrics.innerQuad.top;
|
|
|
|
|
|
|
|
setCorner( TopRightCorner, metrics );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
decrement();
|
|
|
|
p.x = c.centerX - m_cos * c.radiusInnerX;
|
|
|
|
p.y = c.centerY - m_sin * c.radiusInnerY;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( p.x >= corners[ TopRightCorner ].centerX )
|
2017-10-17 17:29:02 +02:00
|
|
|
setCorner( TopRightCorner, metrics );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TopRightCorner:
|
|
|
|
{
|
|
|
|
if ( p.y >= c.centerY )
|
|
|
|
{
|
|
|
|
p.x = metrics.innerQuad.right;
|
2018-08-03 08:15:28 +02:00
|
|
|
p.y = corners[ BottomRightCorner ].centerY;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
setCorner( BottomRightCorner, metrics );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
increment();
|
|
|
|
p.x = c.centerX + m_cos * c.radiusInnerX;
|
|
|
|
p.y = c.centerY - m_sin * c.radiusInnerY;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( p.y >= corners[ BottomRightCorner ].centerY )
|
2017-10-17 17:29:02 +02:00
|
|
|
setCorner( BottomRightCorner, metrics );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BottomRightCorner:
|
|
|
|
{
|
|
|
|
// we are bottom/right
|
|
|
|
increment();
|
|
|
|
|
|
|
|
p.x = c.centerX + m_cos * c.radiusInnerX;
|
|
|
|
p.y = c.centerY + m_sin * c.radiusInnerY;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
Q_ASSERT( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( m_corner )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
case TopLeftCorner:
|
|
|
|
{
|
|
|
|
if ( p.y >= c.centerY )
|
|
|
|
{
|
|
|
|
p.x = metrics.innerQuad.left;
|
2018-08-03 08:15:28 +02:00
|
|
|
p.y = corners[ BottomLeftCorner ].centerY;
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
setCorner( BottomLeftCorner, metrics );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
increment();
|
|
|
|
p.x = c.centerX - m_cos * c.radiusInnerX;
|
|
|
|
p.y = c.centerY - m_sin * c.radiusInnerY;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( p.y >= corners[ BottomLeftCorner ].centerY )
|
2017-10-17 17:29:02 +02:00
|
|
|
setCorner( BottomLeftCorner, metrics );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BottomLeftCorner:
|
|
|
|
{
|
|
|
|
if ( p.x >= c.centerX )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
p.x = corners[ BottomRightCorner ].centerX;
|
2017-10-17 17:29:02 +02:00
|
|
|
p.y = metrics.innerQuad.bottom;
|
|
|
|
|
|
|
|
setCorner( BottomRightCorner, metrics );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
increment();
|
|
|
|
p.x = c.centerX - m_cos * c.radiusInnerX;
|
|
|
|
p.y = c.centerY + m_sin * c.radiusInnerY;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( p.x >= corners[ BottomRightCorner ].centerX )
|
2017-10-17 17:29:02 +02:00
|
|
|
setCorner( BottomRightCorner, metrics );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BottomRightCorner:
|
|
|
|
{
|
|
|
|
increment();
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
p.x = c.centerX + m_cos * c.radiusInnerX;
|
2017-10-17 17:29:02 +02:00
|
|
|
p.y = c.centerY + m_sin * c.radiusInnerY;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
Q_ASSERT( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p.v = curve.valueAt( p.x, p.y );
|
|
|
|
|
|
|
|
if ( ( p.v > 0.5 ) && ( p.v < m_contourLine.p1.v ) )
|
|
|
|
{
|
|
|
|
p = m_contourLine.p1;
|
|
|
|
m_isDone = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
static constexpr qreal m_eps = 1e-4;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
inline void setCorner(
|
|
|
|
Qt::Corner corner, const QskBoxRenderer::Metrics& metrics )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
m_corner = corner;
|
|
|
|
const auto& c = metrics.corner[ corner ];
|
|
|
|
|
|
|
|
const double angleStep = M_PI_2 / c.stepCount;
|
|
|
|
|
|
|
|
m_cosStep = qFastCos( angleStep );
|
|
|
|
m_sinStep = qFastSin( angleStep );
|
|
|
|
m_stepInv1 = m_sinStep / m_cosStep;
|
|
|
|
m_stepInv2 = m_cosStep + m_sinStep * m_stepInv1;
|
|
|
|
|
2017-10-25 10:41:34 +02:00
|
|
|
bool horizontal;
|
|
|
|
if ( corner == Qt::TopRightCorner || corner == Qt::BottomLeftCorner )
|
|
|
|
horizontal = !m_clockwise;
|
|
|
|
else
|
|
|
|
horizontal = m_clockwise;
|
|
|
|
|
2017-10-17 17:29:02 +02:00
|
|
|
if ( horizontal )
|
|
|
|
{
|
|
|
|
m_cos = 1.0;
|
|
|
|
m_sin = 0.0;
|
|
|
|
|
|
|
|
m_sinStep = -m_sinStep;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_cos = 0.0;
|
|
|
|
m_sin = 1.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void increment()
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
We are running into this assertions when closing the filling
|
|
|
|
at the bottom right corner for rectangles with small width/height
|
|
|
|
ratio. Seems to be no problem, but has to be understood. TODO ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
Q_ASSERT( m_sinStep < 0.0 || m_cos < 1.0 );
|
|
|
|
Q_ASSERT( m_sinStep > 0.0 || m_cos > 0.0 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const double cos0 = m_cos;
|
|
|
|
|
|
|
|
m_cos = m_cos * m_cosStep + m_sin * m_sinStep;
|
|
|
|
m_sin = m_sin * m_cosStep - cos0 * m_sinStep;
|
|
|
|
|
|
|
|
if ( m_sinStep < 0.0 )
|
|
|
|
{
|
|
|
|
if ( m_cos < m_eps )
|
|
|
|
m_cos = 0.0;
|
|
|
|
|
|
|
|
if ( m_sin > 1.0 - m_eps )
|
|
|
|
m_sin = 1.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( m_cos > 1.0 - m_eps )
|
|
|
|
m_cos = 1.0;
|
|
|
|
|
|
|
|
if ( m_sin < m_eps )
|
|
|
|
m_sin = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void decrement()
|
|
|
|
{
|
|
|
|
m_cos = ( m_cos - m_stepInv1 * m_sin ) / m_stepInv2;
|
|
|
|
if ( std::abs( m_cos ) < m_eps )
|
|
|
|
m_cos = 0.0;
|
|
|
|
|
|
|
|
m_sin = m_sin / m_cosStep + m_stepInv1 * m_cos;
|
|
|
|
if ( std::abs( m_sin ) < m_eps )
|
|
|
|
m_sin = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool m_clockwise;
|
|
|
|
bool m_isLeading;
|
|
|
|
bool m_isDone;
|
|
|
|
|
|
|
|
qreal m_cos, m_cosStep;
|
|
|
|
qreal m_sin, m_sinStep;
|
|
|
|
qreal m_stepInv1, m_stepInv2;
|
|
|
|
|
|
|
|
ContourLine m_contourLine;
|
|
|
|
Qt::Corner m_corner;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OutlineIterator
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-10-17 17:29:02 +02:00
|
|
|
OutlineIterator( const QskBoxRenderer::Metrics& metrics,
|
2018-08-03 08:15:28 +02:00
|
|
|
const ValueCurve& curve, bool clockwise )
|
|
|
|
: m_metrics( metrics )
|
|
|
|
, m_curve( curve )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto& c = metrics.corner[ Qt::TopLeftCorner ];
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
#if 1
|
|
|
|
// This does not need to be done twice !!!
|
|
|
|
#endif
|
|
|
|
const double angleStep = M_PI_2 / c.stepCount;
|
|
|
|
const qreal cosStep = qFastCos( angleStep );
|
|
|
|
const qreal sinStep = qFastSin( angleStep );
|
|
|
|
|
|
|
|
/*
|
|
|
|
Initialize the iterators to start with the
|
|
|
|
minimal value, what is somewhere around the top left corner
|
|
|
|
when having a gradient going from top/left to bottom/right.
|
|
|
|
*/
|
|
|
|
qreal cos1 = 0.0;
|
|
|
|
qreal sin1 = 1.0;
|
|
|
|
|
|
|
|
qreal x1 = c.centerX;
|
|
|
|
qreal y1 = metrics.innerQuad.top;
|
|
|
|
qreal v1 = m_curve.valueAt( x1, y1 );
|
|
|
|
|
|
|
|
for ( int step = 1;; step++ )
|
|
|
|
{
|
|
|
|
const qreal cos2 = cos1 * cosStep + sin1 * sinStep;
|
|
|
|
const qreal sin2 = sin1 * cosStep - cos1 * sinStep;
|
|
|
|
|
|
|
|
const qreal x2 = c.centerX - c.radiusInnerX * cos2;
|
|
|
|
const qreal y2 = c.centerY - c.radiusInnerY * sin2;
|
|
|
|
const qreal v2 = m_curve.valueAt( x2, y2 );
|
|
|
|
|
|
|
|
if ( v2 >= v1 || step >= c.stepCount )
|
|
|
|
{
|
|
|
|
if ( clockwise )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 0 ].setup( metrics, true, true,
|
2017-10-17 17:29:02 +02:00
|
|
|
cos1, cosStep, sin1, sinStep, x2, y2, v2, x1, y1, v1 );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 1 ].setup( metrics, false, false,
|
2017-10-17 17:29:02 +02:00
|
|
|
cos2, cosStep, sin2, sinStep, x1, y1, v1, x2, y2, v2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 0 ].setup( metrics, true, false,
|
2017-10-17 17:29:02 +02:00
|
|
|
cos2, cosStep, sin2, sinStep, x1, y1, v1, x2, y2, v2 );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 1 ].setup( metrics, false, true,
|
2017-10-17 17:29:02 +02:00
|
|
|
cos1, cosStep, sin1, sinStep, x2, y2, v2, x1, y1, v1 );
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
while ( !m_iterator[ 1 ].isDone() &&
|
2019-01-04 13:42:16 +01:00
|
|
|
( m_iterator[ 0 ].value() > m_iterator[ 1 ].value() ) )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 1 ].advance( metrics, m_curve );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cos1 = cos2;
|
|
|
|
sin1 = sin2;
|
|
|
|
|
|
|
|
x1 = x2;
|
|
|
|
y1 = y2;
|
|
|
|
v1 = v2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void advance()
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 0 ].advance( m_metrics, m_curve );
|
2017-10-17 17:29:02 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( !m_iterator[ 0 ].isDone() )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
adjusting the counter vertex until its top value is above
|
|
|
|
the value of the border. Then our value is between
|
|
|
|
the values of the counter vertex and we find out counter
|
|
|
|
border point by cutting the counter vertex.
|
|
|
|
*/
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
while ( !m_iterator[ 1 ].isDone() &&
|
2019-01-04 13:42:16 +01:00
|
|
|
( m_iterator[ 0 ].value() > m_iterator[ 1 ].value() ) )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
m_iterator[ 1 ].advance( m_metrics, m_curve );
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isDone() const
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
return m_iterator[ 0 ].isDone();
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline qreal value() const
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
return m_iterator[ 0 ].value();
|
2017-10-17 17:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void setLineAt( qreal value, Color color, ColoredLine* line )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto& contourLine1 = m_iterator[ 0 ].contourLine();
|
|
|
|
const auto& contourLine2 = m_iterator[ 1 ].contourLine();
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
const QPointF pos = contourLine1.pointAt( value );
|
|
|
|
const QPointF cPos = contourLine2.pointAt( value );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( m_iterator[ 0 ].isClockwise() )
|
2017-10-17 17:29:02 +02:00
|
|
|
setLine( cPos.x(), cPos.y(), pos.x(), pos.y(), color, line );
|
|
|
|
else
|
|
|
|
setLine( pos.x(), pos.y(), cPos.x(), cPos.y(), color, line );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void setLine( Color color, ColoredLine* line )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto& contourLine1 = m_iterator[ 0 ].contourLine();
|
|
|
|
const auto& contourLine2 = m_iterator[ 1 ].contourLine();
|
2017-10-17 17:29:02 +02:00
|
|
|
|
|
|
|
const QPointF cPos = contourLine2.pointAt( contourLine1.p2.v );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( m_iterator[ 0 ].isClockwise() )
|
2017-10-17 17:29:02 +02:00
|
|
|
setLine( cPos.x(), cPos.y(), contourLine1.p2.x, contourLine1.p2.y, color, line );
|
|
|
|
else
|
|
|
|
setLine( contourLine1.p2.x, contourLine1.p2.y, cPos.x(), cPos.y(), color, line );
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
|
|
|
|
Color color, ColoredLine* line )
|
|
|
|
{
|
|
|
|
line->setLine( x1, y1, x2, y2, color );
|
|
|
|
}
|
|
|
|
|
|
|
|
const QskBoxRenderer::Metrics& m_metrics;
|
|
|
|
const ValueCurve& m_curve;
|
|
|
|
|
|
|
|
/*
|
|
|
|
The first iterator for running along the left or right
|
|
|
|
half of the ellipse. The other one is for finding the
|
|
|
|
corresponing point at the other side. */
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
ContourIterator m_iterator[ 2 ];
|
2017-10-17 17:29:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class DRectellipseIterator
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
DRectellipseIterator(
|
|
|
|
const QskBoxRenderer::Metrics& metrics, const ValueCurve& curve )
|
|
|
|
: m_left( metrics, curve, false )
|
|
|
|
, m_right( metrics, curve, true )
|
2017-10-17 17:29:02 +02:00
|
|
|
{
|
|
|
|
m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right;
|
|
|
|
}
|
|
|
|
|
|
|
|
template< class ColorIterator >
|
|
|
|
inline void setGradientLine( const ColorIterator& it, ColoredLine* line )
|
|
|
|
{
|
|
|
|
m_next->setLineAt( it.value(), it.color(), line );
|
|
|
|
}
|
|
|
|
|
|
|
|
template< class ColorIterator >
|
|
|
|
inline void setContourLine( const ColorIterator& it, ColoredLine* line )
|
|
|
|
{
|
|
|
|
m_next->setLine( it.colorAt( m_next->value() ), line );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline qreal value() const
|
|
|
|
{
|
|
|
|
return m_next->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool advance()
|
|
|
|
{
|
|
|
|
m_next->advance();
|
|
|
|
m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right;
|
|
|
|
|
|
|
|
return !m_next->isDone();
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-10-17 17:29:02 +02:00
|
|
|
OutlineIterator m_left, m_right;
|
|
|
|
OutlineIterator* m_next;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskBoxRenderer::renderDiagonalFill( const QskBoxRenderer::Metrics& metrics,
|
|
|
|
const QskGradient& gradient, int fillLineCount, QskVertex::ColoredLine* lines )
|
|
|
|
{
|
|
|
|
const ValueCurve curve( metrics );
|
|
|
|
|
|
|
|
DRectellipseIterator it( metrics, curve );
|
|
|
|
auto line = QskVertex::fillOrdered( it, 0.0, 1.0, gradient, lines );
|
|
|
|
|
|
|
|
/*
|
|
|
|
There are a couple of reasons, why less points have been rendered
|
|
|
|
than expected: f.e the positions of the gradient lines are
|
|
|
|
calculated from a diagonal, where the start/endpoints
|
|
|
|
might be a little outside of the contour.
|
|
|
|
|
|
|
|
As effects like this one are hard to precalculate we simply
|
|
|
|
insert dummy lines to match the allocated memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while ( line - lines < fillLineCount )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
line[ 0 ] = line[ -1 ];
|
2017-10-17 17:29:02 +02:00
|
|
|
line++;
|
|
|
|
}
|
|
|
|
}
|