Skip to content

Commit

Permalink
Allow QgsRectangle constructors to bypass the automatic normalization
Browse files Browse the repository at this point in the history
step, when they know in advance they are already normalized

This step isn't free, and can add up when many rectangles are
constructed
  • Loading branch information
nyalldawson committed Feb 23, 2021
1 parent 885387f commit d007901
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 37 deletions.
34 changes: 23 additions & 11 deletions python/core/auto_generated/geometry/qgsrectangle.sip.in
Expand Up @@ -29,19 +29,27 @@ Examples are storing a layer extent or the current view extent of a map

QgsRectangle(); // optimised constructor for null rectangle - no need to call normalize here

explicit QgsRectangle( double xMin, double yMin = 0, double xMax = 0, double yMax = 0 ) /HoldGIL/;
explicit QgsRectangle( double xMin, double yMin = 0, double xMax = 0, double yMax = 0, bool normalize = true ) /HoldGIL/;
%Docstring
Constructor
Constructs a QgsRectangle from a set of x and y minimum and maximum coordinates.

The rectangle will be normalized after creation. Since QGIS 3.20, if ``normalize`` is ``False`` then
the normalization step will not be applied automatically.
%End

QgsRectangle( const QgsPointXY &p1, const QgsPointXY &p2 ) /HoldGIL/;
QgsRectangle( const QgsPointXY &p1, const QgsPointXY &p2, bool normalize = true ) /HoldGIL/;
%Docstring
Construct a rectangle from two points. The rectangle is normalized after construction.
Construct a rectangle from two points.

The rectangle is normalized after construction. Since QGIS 3.20, if ``normalize`` is ``False`` then
the normalization step will not be applied automatically.
%End

QgsRectangle( const QRectF &qRectF ) /HoldGIL/;
%Docstring
Construct a rectangle from a QRectF. The rectangle is normalized after construction.
Construct a rectangle from a QRectF.

The rectangle is NOT normalized after construction.
%End

QgsRectangle( const QgsRectangle &other ) /HoldGIL/;
Expand All @@ -67,16 +75,20 @@ and ``width`` and ``height``.
.. versionadded:: 3.0
%End

void set( const QgsPointXY &p1, const QgsPointXY &p2 );
void set( const QgsPointXY &p1, const QgsPointXY &p2, bool normalize = true );
%Docstring
Sets the rectangle from two :py:class:`QgsPoints`. The rectangle is
normalised after construction.
Sets the rectangle from two :py:class:`QgsPoints`.

The rectangle is normalised after construction. Since QGIS 3.20, if ``normalize`` is ``False`` then
the normalization step will not be applied automatically.
%End

void set( double xMin, double yMin, double xMax, double yMax );
void set( double xMin, double yMin, double xMax, double yMax, bool normalize = true );
%Docstring
Sets the rectangle from four points. The rectangle is
normalised after construction.
Sets the rectangle from four points.

The rectangle is normalised after construction. Since QGIS 3.20, if ``normalize`` is ``False`` then
the normalization step will not be applied automatically.
%End

void setXMinimum( double x ) /HoldGIL/;
Expand Down
51 changes: 36 additions & 15 deletions src/core/geometry/qgsrectangle.h
Expand Up @@ -45,23 +45,38 @@ class CORE_EXPORT QgsRectangle
//! Constructor for a null rectangle
QgsRectangle() = default; // optimised constructor for null rectangle - no need to call normalize here

//! Constructor
explicit QgsRectangle( double xMin, double yMin = 0, double xMax = 0, double yMax = 0 ) SIP_HOLDGIL
/**
* Constructs a QgsRectangle from a set of x and y minimum and maximum coordinates.
*
* The rectangle will be normalized after creation. Since QGIS 3.20, if \a normalize is FALSE then
* the normalization step will not be applied automatically.
*/
explicit QgsRectangle( double xMin, double yMin = 0, double xMax = 0, double yMax = 0, bool normalize = true ) SIP_HOLDGIL
: mXmin( xMin )
, mYmin( yMin )
, mXmax( xMax )
, mYmax( yMax )
{
normalize();
if ( normalize )
QgsRectangle::normalize();
}

//! Construct a rectangle from two points. The rectangle is normalized after construction.
QgsRectangle( const QgsPointXY &p1, const QgsPointXY &p2 ) SIP_HOLDGIL
/**
* Construct a rectangle from two points.
*
* The rectangle is normalized after construction. Since QGIS 3.20, if \a normalize is FALSE then
* the normalization step will not be applied automatically.
*/
QgsRectangle( const QgsPointXY &p1, const QgsPointXY &p2, bool normalize = true ) SIP_HOLDGIL
{
set( p1, p2 );
set( p1, p2, normalize );
}

//! Construct a rectangle from a QRectF. The rectangle is normalized after construction.
/**
* Construct a rectangle from a QRectF.
*
* The rectangle is NOT normalized after construction.
*/
QgsRectangle( const QRectF &qRectF ) SIP_HOLDGIL
{
mXmin = qRectF.topLeft().x();
Expand Down Expand Up @@ -99,29 +114,35 @@ class CORE_EXPORT QgsRectangle
static QgsRectangle fromCenterAndSize( QgsPointXY center, double width, double height );

/**
* Sets the rectangle from two QgsPoints. The rectangle is
* normalised after construction.
* Sets the rectangle from two QgsPoints.
*
* The rectangle is normalised after construction. Since QGIS 3.20, if \a normalize is FALSE then
* the normalization step will not be applied automatically.
*/
void set( const QgsPointXY &p1, const QgsPointXY &p2 )
void set( const QgsPointXY &p1, const QgsPointXY &p2, bool normalize = true )
{
mXmin = p1.x();
mXmax = p2.x();
mYmin = p1.y();
mYmax = p2.y();
normalize();
if ( normalize )
QgsRectangle::normalize();
}

/**
* Sets the rectangle from four points. The rectangle is
* normalised after construction.
* Sets the rectangle from four points.
*
* The rectangle is normalised after construction. Since QGIS 3.20, if \a normalize is FALSE then
* the normalization step will not be applied automatically.
*/
void set( double xMin, double yMin, double xMax, double yMax )
void set( double xMin, double yMin, double xMax, double yMax, bool normalize = true )
{
mXmin = xMin;
mYmin = yMin;
mXmax = xMax;
mYmax = yMax;
normalize();
if ( normalize )
QgsRectangle::normalize();
}

/**
Expand Down
14 changes: 3 additions & 11 deletions src/gui/qgsrubberband.cpp
Expand Up @@ -615,17 +615,9 @@ void QgsRubberBand::updateRect()
{
QgsPointXY p( point.x() + mTranslationOffsetX, point.y() + mTranslationOffsetY );
p = m2p.transform( p );
QgsRectangle rect( p.x() - w, p.y() - w, p.x() + w, p.y() + w );

if ( r.isEmpty() )
{
// Get rectangle of the first point
r = rect;
}
else
{
r.combineExtentWith( rect );
}
// no need to normalize the rectangle -- we know it is already normal
QgsRectangle rect( p.x() - w, p.y() - w, p.x() + w, p.y() + w, false );
r.combineExtentWith( rect );
}
}

Expand Down
86 changes: 86 additions & 0 deletions tests/src/core/testqgsrectangle.cpp
Expand Up @@ -27,6 +27,10 @@ class TestQgsRectangle: public QObject
private slots:
void isEmpty();
void fromWkt();
void constructor();
void constructorTwoPoints();
void set();
void setXY();
void fromCenter();
void manipulate();
void regression6194();
Expand Down Expand Up @@ -82,6 +86,88 @@ void TestQgsRectangle::fromWkt()
QVERIFY( QgsRectangle::fromWkt( QStringLiteral( "POLYGON((0 0,1 0,1 1,0 1))" ) ).isEmpty() );
}

void TestQgsRectangle::constructor()
{
QgsRectangle r( 1, 2, 13, 14 );
QCOMPARE( r.xMinimum(), 1.0 );
QCOMPARE( r.xMaximum(), 13.0 );
QCOMPARE( r.yMinimum(), 2.0 );
QCOMPARE( r.yMaximum(), 14.0 );

// auto normalized
QgsRectangle r2( 13, 14, 1, 2 );
QCOMPARE( r2.xMinimum(), 1.0 );
QCOMPARE( r2.xMaximum(), 13.0 );
QCOMPARE( r2.yMinimum(), 2.0 );
QCOMPARE( r2.yMaximum(), 14.0 );

// no normalization
QgsRectangle r3( 13, 14, 1, 2, false );
QCOMPARE( r3.xMinimum(), 13.0 );
QCOMPARE( r3.xMaximum(), 1.0 );
QCOMPARE( r3.yMinimum(), 14.0 );
QCOMPARE( r3.yMaximum(), 2.0 );
}

void TestQgsRectangle::constructorTwoPoints()
{
QgsRectangle r( QgsPointXY( 1, 2 ), QgsPointXY( 13, 14 ) );
QCOMPARE( r.xMinimum(), 1.0 );
QCOMPARE( r.xMaximum(), 13.0 );
QCOMPARE( r.yMinimum(), 2.0 );
QCOMPARE( r.yMaximum(), 14.0 );

// auto normalized
QgsRectangle r2( QgsPointXY( 13, 14 ), QgsPointXY( 1, 2 ) );
QCOMPARE( r2.xMinimum(), 1.0 );
QCOMPARE( r2.xMaximum(), 13.0 );
QCOMPARE( r2.yMinimum(), 2.0 );
QCOMPARE( r2.yMaximum(), 14.0 );

// no normalization
QgsRectangle r3( QgsPointXY( 13, 14 ), QgsPointXY( 1, 2 ), false );
QCOMPARE( r3.xMinimum(), 13.0 );
QCOMPARE( r3.xMaximum(), 1.0 );
QCOMPARE( r3.yMinimum(), 14.0 );
QCOMPARE( r3.yMaximum(), 2.0 );
}

void TestQgsRectangle::set()
{
QgsRectangle r( QgsPointXY( 1, 2 ), QgsPointXY( 13, 14 ) );
// auto normalized
r.set( QgsPointXY( 13, 14 ), QgsPointXY( 1, 2 ) );
QCOMPARE( r.xMinimum(), 1.0 );
QCOMPARE( r.xMaximum(), 13.0 );
QCOMPARE( r.yMinimum(), 2.0 );
QCOMPARE( r.yMaximum(), 14.0 );

// no normalization
r.set( QgsPointXY( 13, 14 ), QgsPointXY( 1, 2 ), false );
QCOMPARE( r.xMinimum(), 13.0 );
QCOMPARE( r.xMaximum(), 1.0 );
QCOMPARE( r.yMinimum(), 14.0 );
QCOMPARE( r.yMaximum(), 2.0 );
}

void TestQgsRectangle::setXY()
{
QgsRectangle r( QgsPointXY( 111, 112 ), QgsPointXY( 113, 114 ) );
// auto normalized
r.set( 13, 14, 1, 2 );
QCOMPARE( r.xMinimum(), 1.0 );
QCOMPARE( r.xMaximum(), 13.0 );
QCOMPARE( r.yMinimum(), 2.0 );
QCOMPARE( r.yMaximum(), 14.0 );

// no normalization
r.set( 13, 14, 1, 2, false );
QCOMPARE( r.xMinimum(), 13.0 );
QCOMPARE( r.xMaximum(), 1.0 );
QCOMPARE( r.yMinimum(), 14.0 );
QCOMPARE( r.yMaximum(), 2.0 );
}

void TestQgsRectangle::fromCenter()
{
QgsRectangle rect = QgsRectangle::fromCenterAndSize( QgsPointXY( 12, 21 ), 20, 40 );
Expand Down

0 comments on commit d007901

Please sign in to comment.