more details around shapes
This commit is contained in:
parent
634ca3bed3
commit
f798f2228c
@ -9,6 +9,38 @@
|
||||
#include <QskShapeNode.h>
|
||||
#include <QskSGNode.h>
|
||||
|
||||
static inline QTransform transformForRects( const QRectF& r1, const QRectF& r2 )
|
||||
{
|
||||
return QTransform::fromTranslate( -r1.x(), -r1.y() )
|
||||
* QTransform::fromScale( r2.width() / r1.width(), r2.height() / r1.height() )
|
||||
* QTransform::fromTranslate( r2.x(), r2.y() );
|
||||
}
|
||||
|
||||
static inline bool isVisible( const QColor& color )
|
||||
{
|
||||
return color.isValid() && ( color.alpha() > 0 );
|
||||
}
|
||||
|
||||
static inline bool isVisible( const QPen& pen )
|
||||
{
|
||||
return ( pen.style() != Qt::NoPen ) && isVisible( pen.color() );
|
||||
}
|
||||
|
||||
static inline QPen cosmeticPen( const QPen& pen, const QSizeF& size1, const QSizeF& size2 )
|
||||
{
|
||||
if ( pen.isCosmetic() || pen.widthF() <= 0.0 || size2.isEmpty() )
|
||||
return pen;
|
||||
|
||||
const auto f = qMin( size1.width() / size2.width(),
|
||||
size1.height() / size2.height() );
|
||||
|
||||
auto newPen = pen;
|
||||
newPen.setWidthF( pen.widthF() * f );
|
||||
newPen.setCosmetic( true );
|
||||
|
||||
return newPen;
|
||||
}
|
||||
|
||||
ShapeItem::ShapeItem( QQuickItem* parent )
|
||||
: QskControl( parent )
|
||||
{
|
||||
@ -44,6 +76,16 @@ void ShapeItem::setGradient( const QColor& c1, const QColor& c2 )
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeItem::setGradient( QGradient::Preset preset )
|
||||
{
|
||||
const auto stops = QGradient( preset ).stops();
|
||||
if ( !stops.isEmpty() )
|
||||
{
|
||||
// gradients with more than 2 clors do not work TODO ...
|
||||
setGradient( stops.first().second, stops.last().second );
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeItem::setPath( const QPainterPath& path )
|
||||
{
|
||||
if ( path != m_path )
|
||||
@ -67,28 +109,43 @@ void ShapeItem::updateNode( QSGNode* parentNode )
|
||||
};
|
||||
|
||||
const auto rect = contentsRect();
|
||||
|
||||
/*
|
||||
The triangulators in the nodes are able to do transformations
|
||||
on the fly. TODO ...
|
||||
*/
|
||||
const auto path = scaledPath( rect );
|
||||
const auto pathRect = m_path.controlPointRect();
|
||||
|
||||
auto fillNode = static_cast< QskShapeNode* >(
|
||||
QskSGNode::findChildNode( parentNode, FillRole ) );
|
||||
|
||||
if ( path.isEmpty() || rect.isEmpty() )
|
||||
auto borderNode = static_cast< QskStrokeNode* >(
|
||||
QskSGNode::findChildNode( parentNode, BorderRole ) );
|
||||
|
||||
if ( rect.isEmpty() || pathRect.isEmpty() )
|
||||
{
|
||||
delete fillNode;
|
||||
delete borderNode;
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
const auto pen = ::cosmeticPen( m_pen, rect.size(), pathRect.size() );
|
||||
|
||||
if ( ::isVisible( m_fillColor[0] ) || ::isVisible( m_fillColor[1] ) )
|
||||
{
|
||||
if ( fillNode == nullptr )
|
||||
{
|
||||
fillNode = new QskShapeNode;
|
||||
QskSGNode::setNodeRole( fillNode, FillRole );
|
||||
|
||||
parentNode->prependChildNode( fillNode );
|
||||
}
|
||||
|
||||
auto fillRect = rect;
|
||||
if ( pen.style() != Qt::NoPen )
|
||||
{
|
||||
const auto pw2 = 0.5 * pen.widthF();
|
||||
fillRect.adjust( pw2, pw2, -pw2, -pw2 );
|
||||
}
|
||||
|
||||
const auto transform = ::transformForRects( pathRect, fillRect );
|
||||
|
||||
if ( m_fillColor[0] != m_fillColor[1] )
|
||||
{
|
||||
QLinearGradient gradient;
|
||||
@ -97,51 +154,52 @@ void ShapeItem::updateNode( QSGNode* parentNode )
|
||||
gradient.setColorAt( 0.0, m_fillColor[0] );
|
||||
gradient.setColorAt( 1.0, m_fillColor[1] );
|
||||
|
||||
fillNode->updateNode( path, &gradient );
|
||||
fillNode->updateNode( m_path, transform, &gradient );
|
||||
}
|
||||
else
|
||||
{
|
||||
fillNode->updateNode( path, m_fillColor[0] );
|
||||
fillNode->updateNode( m_path, transform, m_fillColor[0] );
|
||||
}
|
||||
|
||||
if ( fillNode->parent() != parentNode )
|
||||
parentNode->prependChildNode( fillNode );
|
||||
}
|
||||
|
||||
auto borderNode = static_cast< QskStrokeNode* >(
|
||||
QskSGNode::findChildNode( parentNode, BorderRole ) );
|
||||
|
||||
if ( path.isEmpty() || rect.isEmpty() )
|
||||
{
|
||||
delete borderNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete fillNode;
|
||||
}
|
||||
|
||||
if ( ::isVisible( pen ) )
|
||||
{
|
||||
if ( pen.widthF() > 1.0 )
|
||||
{
|
||||
if ( !( pen.isSolid() && pen.color().alpha() == 255 ) )
|
||||
{
|
||||
/*
|
||||
We might end up with overlapping parts
|
||||
at corners with angles < 180°
|
||||
|
||||
What about translating the stroke into
|
||||
a path ( QPainterPathStroker ) and using
|
||||
a QskShapeNode then. TODO ...
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if ( borderNode == nullptr )
|
||||
{
|
||||
borderNode = new QskStrokeNode;
|
||||
QskSGNode::setNodeRole( borderNode, BorderRole );
|
||||
}
|
||||
|
||||
borderNode->updateNode( path, m_pen );
|
||||
|
||||
if ( borderNode->parent() != parentNode )
|
||||
parentNode->appendChildNode( borderNode );
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath ShapeItem::scaledPath( const QRectF& rect ) const
|
||||
{
|
||||
// does not center properly. TODO
|
||||
const auto pw = 2 * m_pen.width();
|
||||
const auto transform = ::transformForRects( pathRect, rect );
|
||||
|
||||
const auto pathRect = m_path.controlPointRect();
|
||||
const auto r = rect.adjusted( pw, pw, -pw, -pw );
|
||||
|
||||
auto transform = QTransform::fromTranslate( r.left(), r.top() );
|
||||
transform.scale( r.width() / pathRect.width(), r.height() / pathRect.height() );
|
||||
|
||||
return transform.map( m_path );
|
||||
const auto scaledPath = transform.map( m_path );
|
||||
borderNode->updateNode( scaledPath, pen );
|
||||
}
|
||||
else
|
||||
{
|
||||
delete borderNode;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_ShapeItem.cpp"
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <QskControl.h>
|
||||
#include <QPen>
|
||||
#include <QPainterPath>
|
||||
#include <QGradient>
|
||||
|
||||
class ShapeItem : public QskControl
|
||||
{
|
||||
@ -21,6 +22,7 @@ class ShapeItem : public QskControl
|
||||
QPen pen() const;
|
||||
|
||||
void setGradient( const QColor&, const QColor& );
|
||||
void setGradient( QGradient::Preset );
|
||||
|
||||
void setPath( const QPainterPath& );
|
||||
QPainterPath path() const;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <QskObjectCounter.h>
|
||||
#include <QskWindow.h>
|
||||
#include <QskRgbValue.h>
|
||||
|
||||
#include <SkinnyShortcut.h>
|
||||
#include <SkinnyShapeFactory.h>
|
||||
@ -18,6 +19,29 @@ namespace
|
||||
{
|
||||
return SkinnyShapeFactory::shapePath( shape, QSizeF( 50, 50 ) );
|
||||
}
|
||||
|
||||
class Pen : public QPen
|
||||
{
|
||||
public:
|
||||
Pen( const QColor& color )
|
||||
: QPen( color )
|
||||
{
|
||||
setCosmetic( true );
|
||||
setWidth( isCosmetic() ? 8 : 2 );
|
||||
|
||||
setJoinStyle( Qt::MiterJoin );
|
||||
|
||||
//setStyle( Qt::NoPen );
|
||||
//setStyle( Qt::DashLine );
|
||||
|
||||
//setAlpha( 100 );
|
||||
}
|
||||
|
||||
void setAlpha( int alpha )
|
||||
{
|
||||
setColor( QskRgb::toTransparent( color(), alpha ) );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
@ -36,8 +60,9 @@ int main( int argc, char* argv[] )
|
||||
auto shapeItem = new ShapeItem();
|
||||
|
||||
shapeItem->setPath( path( SkinnyShapeFactory::Hexagon ) );
|
||||
shapeItem->setPen( QPen( Qt::darkCyan, 20 ) );
|
||||
shapeItem->setGradient( Qt::red, Qt::blue );
|
||||
|
||||
shapeItem->setPen( Pen( QColorConstants::Svg::indigo ) );
|
||||
shapeItem->setGradient( QGradient::PhoenixStart );
|
||||
|
||||
window.addItem( shapeItem );
|
||||
window.resize( 600, 600 );
|
||||
|
@ -15,9 +15,10 @@ QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qtriangulator_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
static void qskUpdateGeometry( const QPainterPath& path, QSGGeometry& geometry )
|
||||
static void qskUpdateGeometry( const QPainterPath& path,
|
||||
const QTransform& transform, QSGGeometry& geometry )
|
||||
{
|
||||
const auto ts = qTriangulate( path, QTransform(), 1, false );
|
||||
const auto ts = qTriangulate( path, transform, 1, false );
|
||||
|
||||
#if 1
|
||||
geometry.allocate( ts.vertices.size(), ts.indices.size() );
|
||||
@ -118,7 +119,8 @@ QskShapeNode::QskShapeNode()
|
||||
setFlag( QSGNode::OwnsMaterial, true );
|
||||
}
|
||||
|
||||
void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color )
|
||||
void QskShapeNode::updateNode( const QPainterPath& path,
|
||||
const QTransform& transform, const QColor& color )
|
||||
{
|
||||
Q_D( QskShapeNode );
|
||||
|
||||
@ -130,7 +132,7 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color )
|
||||
|
||||
if ( true ) // For the moment we always update the geometry. TODO ...
|
||||
{
|
||||
qskUpdateGeometry( path, d->geometry );
|
||||
qskUpdateGeometry( path, transform, d->geometry );
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
||||
|
||||
@ -150,7 +152,8 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color )
|
||||
}
|
||||
}
|
||||
|
||||
void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradient )
|
||||
void QskShapeNode::updateNode( const QPainterPath& path,
|
||||
const QTransform& transform, const QGradient* gradient )
|
||||
{
|
||||
if ( path.isEmpty() || !qskIsGradientVisible( gradient ) )
|
||||
{
|
||||
@ -160,7 +163,7 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradie
|
||||
|
||||
if ( qskIsGradientMonochrome( gradient ) )
|
||||
{
|
||||
updateNode( path, gradient->stops().first().first );
|
||||
updateNode( path, transform, gradient->stops().first().first );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -168,7 +171,7 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradie
|
||||
|
||||
if ( true ) // For the moment we always update the geometry. TODO ...
|
||||
{
|
||||
qskUpdateGeometry( path, d->geometry );
|
||||
qskUpdateGeometry( path, transform, d->geometry );
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
||||
|
||||
@ -182,3 +185,4 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradie
|
||||
if ( gradientMaterial->updateGradient( gradient ) )
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ class QSK_EXPORT QskShapeNode : public QSGGeometryNode
|
||||
public:
|
||||
QskShapeNode();
|
||||
|
||||
void updateNode( const QPainterPath&, const QGradient* );
|
||||
void updateNode( const QPainterPath&, const QColor& );
|
||||
void updateNode( const QPainterPath&, const QTransform&, const QGradient* );
|
||||
void updateNode( const QPainterPath&, const QTransform&, const QColor& );
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE( QskShapeNode )
|
||||
|
Loading…
x
Reference in New Issue
Block a user