more details around shapes

This commit is contained in:
Uwe Rathmann 2022-09-29 16:50:46 +02:00
parent 634ca3bed3
commit f798f2228c
5 changed files with 138 additions and 49 deletions

View File

@ -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 );
parentNode->appendChildNode( borderNode );
}
borderNode->updateNode( path, m_pen );
const auto transform = ::transformForRects( pathRect, rect );
if ( borderNode->parent() != parentNode )
parentNode->appendChildNode( borderNode );
const auto scaledPath = transform.map( m_path );
borderNode->updateNode( scaledPath, pen );
}
else
{
delete borderNode;
}
}
QPainterPath ShapeItem::scaledPath( const QRectF& rect ) const
{
// does not center properly. TODO
const auto pw = 2 * m_pen.width();
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 );
}
#include "moc_ShapeItem.cpp"

View File

@ -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;

View File

@ -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 );

View File

@ -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 );
}

View File

@ -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 )