Prevent creation of invalid polyline / polygon

This commit is contained in:
Yoann Quenach de Quivillic 2024-10-20 21:53:09 +02:00 committed by Nyall Dawson
parent c354ed5000
commit ca2688a977
12 changed files with 81 additions and 5 deletions

View File

@ -111,6 +111,14 @@ Deselects any selected nodes.
virtual double estimatedFrameBleed() const; virtual double estimatedFrameBleed() const;
virtual bool isValid() const;
%Docstring
Can be reimplemented in subclasses. Typically a polyline is valid if it has at least 2 distinct nodes, while
a polygon is valid if it has at least 3 distinct nodes.
.. versionadded:: 3.40
%End
protected: protected:
QgsLayoutNodesItem( QgsLayout *layout ); QgsLayoutNodesItem( QgsLayout *layout );

View File

@ -53,6 +53,8 @@ The caller takes responsibility for deleting the returned object.
virtual QgsGeometry clipPath() const; virtual QgsGeometry clipPath() const;
virtual bool isValid() const;
QgsFillSymbol *symbol(); QgsFillSymbol *symbol();
%Docstring %Docstring

View File

@ -55,6 +55,8 @@ The caller takes responsibility for deleting the returned object.
virtual QPainterPath shape() const; virtual QPainterPath shape() const;
virtual bool isValid() const;
QgsLineSymbol *symbol(); QgsLineSymbol *symbol();
%Docstring %Docstring

View File

@ -111,6 +111,14 @@ Deselects any selected nodes.
virtual double estimatedFrameBleed() const; virtual double estimatedFrameBleed() const;
virtual bool isValid() const;
%Docstring
Can be reimplemented in subclasses. Typically a polyline is valid if it has at least 2 distinct nodes, while
a polygon is valid if it has at least 3 distinct nodes.
.. versionadded:: 3.40
%End
protected: protected:
QgsLayoutNodesItem( QgsLayout *layout ); QgsLayoutNodesItem( QgsLayout *layout );

View File

@ -53,6 +53,8 @@ The caller takes responsibility for deleting the returned object.
virtual QgsGeometry clipPath() const; virtual QgsGeometry clipPath() const;
virtual bool isValid() const;
QgsFillSymbol *symbol(); QgsFillSymbol *symbol();
%Docstring %Docstring

View File

@ -55,6 +55,8 @@ The caller takes responsibility for deleting the returned object.
virtual QPainterPath shape() const; virtual QPainterPath shape() const;
virtual bool isValid() const;
QgsLineSymbol *symbol(); QgsLineSymbol *symbol();
%Docstring %Docstring

View File

@ -113,6 +113,15 @@ class CORE_EXPORT QgsLayoutNodesItem: public QgsLayoutItem
// rather than the item's pen // rather than the item's pen
double estimatedFrameBleed() const override; double estimatedFrameBleed() const override;
/**
* Can be reimplemented in subclasses. Typically a polyline is valid if it has at least 2 distinct nodes, while
* a polygon is valid if it has at least 3 distinct nodes.
*
* \since QGIS 3.40
*/
virtual bool isValid() const { return true; };
protected: protected:
/** /**

View File

@ -129,6 +129,23 @@ QgsGeometry QgsLayoutItemPolygon::clipPath() const
return QgsGeometry::fromQPolygonF( path ); return QgsGeometry::fromQPolygonF( path );
} }
bool QgsLayoutItemPolygon::isValid() const
{
QList<QPointF> uniquePoints;
int seen = 0;
for ( QPointF point : mPolygon )
{
if ( !uniquePoints.contains( point ) )
{
uniquePoints.append( point );
if ( ++seen > 2 )
return true;
}
}
return false;
}
QgsFillSymbol *QgsLayoutItemPolygon::symbol() QgsFillSymbol *QgsLayoutItemPolygon::symbol()
{ {
return mPolygonStyleSymbol.get(); return mPolygonStyleSymbol.get();

View File

@ -58,6 +58,7 @@ class CORE_EXPORT QgsLayoutItemPolygon: public QgsLayoutNodesItem
bool accept( QgsStyleEntityVisitorInterface *visitor ) const override; bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
QgsLayoutItem::Flags itemFlags() const override; QgsLayoutItem::Flags itemFlags() const override;
QgsGeometry clipPath() const override; QgsGeometry clipPath() const override;
bool isValid() const override;
/** /**
* Returns the fill symbol used to draw the shape. * Returns the fill symbol used to draw the shape.

View File

@ -344,6 +344,22 @@ QPainterPath QgsLayoutItemPolyline::shape() const
return strokedOutline; return strokedOutline;
} }
bool QgsLayoutItemPolyline::isValid() const
{
QList<QPointF> uniquePoints;
int seen = 0;
for (QPointF point: mPolygon)
{
if( !uniquePoints.contains( point ) )
{
uniquePoints.append( point );
if (++seen > 1)
return true;
}
}
return false;
}
QgsLineSymbol *QgsLayoutItemPolyline::symbol() QgsLineSymbol *QgsLayoutItemPolyline::symbol()
{ {
return mPolylineStyleSymbol.get(); return mPolylineStyleSymbol.get();

View File

@ -66,6 +66,7 @@ class CORE_EXPORT QgsLayoutItemPolyline: public QgsLayoutNodesItem
QIcon icon() const override; QIcon icon() const override;
QString displayName() const override; QString displayName() const override;
QPainterPath shape() const override; QPainterPath shape() const override;
bool isValid() const override;
/** /**
* Returns the line symbol used to draw the shape. * Returns the line symbol used to draw the shape.

View File

@ -74,7 +74,15 @@ void QgsLayoutViewToolAddNodeItem::layoutPressEvent( QgsLayoutViewMouseEvent *ev
return; return;
if ( QgsLayoutNodesItem *nodesItem = qobject_cast< QgsLayoutNodesItem * >( item ) ) if ( QgsLayoutNodesItem *nodesItem = qobject_cast< QgsLayoutNodesItem * >( item ) )
{
nodesItem->setNodes( mPolygon ); nodesItem->setNodes( mPolygon );
if ( !nodesItem->isValid() )
{
nodesItem->deleteLater();
mRubberBand.reset();
return;
}
}
layout()->addLayoutItem( item ); layout()->addLayoutItem( item );
layout()->setSelectedItem( item ); layout()->setSelectedItem( item );