|
|
|
@ -2,7 +2,7 @@ |
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application. |
|
|
|
* |
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> |
|
|
|
* Copyright (C) 2012-2021 Kicad Developers, see AUTHORS.txt for contributors. |
|
|
|
* Copyright (C) 2012-2022 Kicad Developers, see AUTHORS.txt for contributors. |
|
|
|
* Copyright (C) 2013 CERN |
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> |
|
|
|
* |
|
|
|
@ -27,13 +27,14 @@ |
|
|
|
#ifndef __BOX2_H |
|
|
|
#define __BOX2_H |
|
|
|
|
|
|
|
#include <math/vector2d.h> |
|
|
|
#include <limits> |
|
|
|
#include <algorithm> |
|
|
|
|
|
|
|
// Needed for the std::optional definition |
|
|
|
#include <optional> |
|
|
|
|
|
|
|
#include <math/vector2d.h> |
|
|
|
#include <geometry/eda_angle.h> |
|
|
|
#include <trigo.h> |
|
|
|
|
|
|
|
/** |
|
|
|
* A 2D bounding box built on top of an origin point and size vector. |
|
|
|
*/ |
|
|
|
@ -41,15 +42,20 @@ template <class Vec> |
|
|
|
class BOX2 |
|
|
|
{ |
|
|
|
public: |
|
|
|
typedef typename Vec::coord_type coord_type; |
|
|
|
typedef typename Vec::extended_type ecoord_type; |
|
|
|
typedef std::numeric_limits<coord_type> coord_limits; |
|
|
|
typedef typename Vec::coord_type coord_type; |
|
|
|
typedef typename Vec::extended_type ecoord_type; |
|
|
|
typedef std::numeric_limits<coord_type> coord_limits; |
|
|
|
|
|
|
|
BOX2() {}; |
|
|
|
BOX2() : |
|
|
|
m_Pos( 0, 0 ), |
|
|
|
m_Size( 0, 0 ), |
|
|
|
m_init( false ) |
|
|
|
{}; |
|
|
|
|
|
|
|
BOX2( const Vec& aPos, const Vec& aSize = Vec(0, 0) ) : |
|
|
|
m_Pos( aPos ), |
|
|
|
m_Size( aSize ) |
|
|
|
m_Size( aSize ), |
|
|
|
m_init( true ) |
|
|
|
{ |
|
|
|
Normalize(); |
|
|
|
} |
|
|
|
@ -58,6 +64,7 @@ public: |
|
|
|
{ |
|
|
|
m_Pos.x = m_Pos.y = coord_limits::lowest() / 2 + coord_limits::epsilon(); |
|
|
|
m_Size.x = m_Size.y = coord_limits::max() - coord_limits::epsilon(); |
|
|
|
m_init = true; |
|
|
|
} |
|
|
|
|
|
|
|
Vec Centre() const |
|
|
|
@ -185,29 +192,74 @@ public: |
|
|
|
// Compatibility aliases |
|
|
|
coord_type GetLeft() const { return GetX(); } |
|
|
|
coord_type GetTop() const { return GetY(); } |
|
|
|
void MoveTopTo( coord_type aTop ) { m_Pos.y = aTop; } |
|
|
|
void MoveBottomTo( coord_type aBottom ) { m_Size.y = aBottom - m_Pos.y; } |
|
|
|
void MoveLeftTo( coord_type aLeft ) { m_Pos.x = aLeft; } |
|
|
|
void MoveRightTo( coord_type aRight ) { m_Size.x = aRight - m_Pos.x; } |
|
|
|
|
|
|
|
void SetOrigin( const Vec& pos ) { m_Pos = pos; } |
|
|
|
void SetOrigin( coord_type x, coord_type y ) { m_Pos.x = x; m_Pos.y = y; } |
|
|
|
void SetSize( const Vec& size ) { m_Size = size; } |
|
|
|
void SetSize( coord_type w, coord_type h ) { m_Size.x = w; m_Size.y = h; } |
|
|
|
void Offset( coord_type dx, coord_type dy ) { m_Pos.x += dx; m_Pos.y += dy; } |
|
|
|
const Vec GetCenter() const { return Centre(); } |
|
|
|
|
|
|
|
/** |
|
|
|
* @return the width or height, whichever is greater. |
|
|
|
*/ |
|
|
|
int GetSizeMax() const { return ( m_Size.x > m_Size.y ) ? m_Size.x : m_Size.y; } |
|
|
|
|
|
|
|
void SetOrigin( const Vec& pos ) |
|
|
|
{ |
|
|
|
m_Pos = pos; |
|
|
|
m_init = true; |
|
|
|
} |
|
|
|
|
|
|
|
void SetOrigin( coord_type x, coord_type y ) |
|
|
|
{ |
|
|
|
SetOrigin( Vec( x, y ) ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetSize( const Vec& size ) |
|
|
|
{ |
|
|
|
m_Size = size; |
|
|
|
m_init = true; |
|
|
|
} |
|
|
|
|
|
|
|
void SetSize( coord_type w, coord_type h ) |
|
|
|
{ |
|
|
|
SetSize( Vec( w, h ) ); |
|
|
|
} |
|
|
|
|
|
|
|
void Offset( coord_type dx, coord_type dy ) |
|
|
|
{ |
|
|
|
m_Pos.x += dx; |
|
|
|
m_Pos.y += dy; |
|
|
|
} |
|
|
|
|
|
|
|
void Offset( const Vec& offset ) |
|
|
|
{ |
|
|
|
m_Pos.x += offset.x; m_Pos.y += offset.y; |
|
|
|
Offset( offset.x, offset.y ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetX( coord_type val ) |
|
|
|
{ |
|
|
|
SetOrigin( val, m_Pos.y ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetY( coord_type val ) |
|
|
|
{ |
|
|
|
SetOrigin( m_Pos.x, val ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetWidth( coord_type val ) |
|
|
|
{ |
|
|
|
SetSize( val, m_Size.y ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetHeight( coord_type val ) |
|
|
|
{ |
|
|
|
SetSize( m_Size.x, val ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetEnd( coord_type x, coord_type y ) |
|
|
|
{ |
|
|
|
SetEnd( Vec( x, y ) ); |
|
|
|
} |
|
|
|
|
|
|
|
void SetX( coord_type val ) { m_Pos.x = val; } |
|
|
|
void SetY( coord_type val ) { m_Pos.y = val; } |
|
|
|
void SetWidth( coord_type val ) { m_Size.x = val; } |
|
|
|
void SetHeight( coord_type val ) { m_Size.y = val; } |
|
|
|
void SetEnd( coord_type x, coord_type y ) { SetEnd( Vec( x, y ) ); } |
|
|
|
void SetEnd( const Vec& pos ) |
|
|
|
{ |
|
|
|
m_Size.x = pos.x - m_Pos.x; m_Size.y = pos.y - m_Pos.y; |
|
|
|
SetSize( pos - m_Pos ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -243,7 +295,7 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Return the intersection of this with another rectangle. |
|
|
|
* @return true if this rectangle intersects \a aRect. |
|
|
|
*/ |
|
|
|
BOX2<Vec> Intersect( const BOX2<Vec>& aRect ) |
|
|
|
{ |
|
|
|
@ -265,6 +317,78 @@ public: |
|
|
|
return BOX2<Vec>( Vec( 0, 0 ), Vec( 0, 0 ) ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @return true if this rectangle intersects a line from \a aPoint1 to \a aPoint2 |
|
|
|
*/ |
|
|
|
bool Intersects( const Vec& aPoint1, const Vec& aPoint2 ) const |
|
|
|
{ |
|
|
|
Vec point2, point4; |
|
|
|
|
|
|
|
if( Contains( aPoint1 ) || Contains( aPoint2 ) ) |
|
|
|
return true; |
|
|
|
|
|
|
|
point2.x = GetEnd().x; |
|
|
|
point2.y = GetOrigin().y; |
|
|
|
point4.x = GetOrigin().x; |
|
|
|
point4.y = GetEnd().y; |
|
|
|
|
|
|
|
//Only need to test 3 sides since a straight line can't enter and exit on same side |
|
|
|
if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin(), point2 ) ) |
|
|
|
return true; |
|
|
|
|
|
|
|
if( SegmentIntersectsSegment( aPoint1, aPoint2, point2, GetEnd() ) ) |
|
|
|
return true; |
|
|
|
|
|
|
|
if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd(), point4 ) ) |
|
|
|
return true; |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @return true if this rectangle intersects the circle defined by \a aCenter and \a aRadius. |
|
|
|
*/ |
|
|
|
bool IntersectsCircle( const Vec& aCenter, const int aRadius ) const |
|
|
|
{ |
|
|
|
if( !m_init ) |
|
|
|
return false; |
|
|
|
|
|
|
|
Vec closest = ClosestPointTo( aCenter ); |
|
|
|
|
|
|
|
double dx = static_cast<double>( aCenter.x ) - closest.x; |
|
|
|
double dy = static_cast<double>( aCenter.y ) - closest.y; |
|
|
|
|
|
|
|
double r = static_cast<double>( aRadius ); |
|
|
|
|
|
|
|
return ( dx * dx + dy * dy ) <= ( r * r ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @return true if this rectangle intersects the edge of a circle defined by \a aCenter |
|
|
|
* and \a aRadius. |
|
|
|
*/ |
|
|
|
bool IntersectsCircleEdge( const Vec& aCenter, const int aRadius, const int aWidth ) const |
|
|
|
{ |
|
|
|
if( !m_init ) |
|
|
|
return false; |
|
|
|
|
|
|
|
BOX2<Vec> me( *this ); |
|
|
|
me.Normalize(); // ensure size is >= 0 |
|
|
|
|
|
|
|
// Test if the circle intersects at all |
|
|
|
if( !IntersectsCircle( aCenter, aRadius + aWidth / 2 ) ) |
|
|
|
return false; |
|
|
|
|
|
|
|
Vec farpt = FarthestPointTo( aCenter ); |
|
|
|
// Farthest point must be further than the inside of the line |
|
|
|
double fx = (double) farpt.x; |
|
|
|
double fy = (double) farpt.y; |
|
|
|
|
|
|
|
double r = (double) aRadius - (double) aWidth / 2; |
|
|
|
|
|
|
|
return ( fx * fx + fy * fy ) > ( r * r ); |
|
|
|
} |
|
|
|
|
|
|
|
const std::string Format() const |
|
|
|
{ |
|
|
|
std::stringstream ss; |
|
|
|
@ -362,6 +486,18 @@ public: |
|
|
|
*/ |
|
|
|
BOX2<Vec>& Merge( const BOX2<Vec>& aRect ) |
|
|
|
{ |
|
|
|
if( !m_init ) |
|
|
|
{ |
|
|
|
if( aRect.m_init ) |
|
|
|
{ |
|
|
|
m_Pos = aRect.GetPosition(); |
|
|
|
m_Size = aRect.GetSize(); |
|
|
|
m_init = true; |
|
|
|
} |
|
|
|
|
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
Normalize(); // ensure width and height >= 0 |
|
|
|
BOX2<Vec> rect = aRect; |
|
|
|
rect.Normalize(); // ensure width and height >= 0 |
|
|
|
@ -384,6 +520,14 @@ public: |
|
|
|
*/ |
|
|
|
BOX2<Vec>& Merge( const Vec& aPoint ) |
|
|
|
{ |
|
|
|
if( !m_init ) |
|
|
|
{ |
|
|
|
m_Pos = aPoint; |
|
|
|
m_Size = VECTOR2I( 0, 0 ); |
|
|
|
m_init = true; |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
Normalize(); // ensure width and height >= 0 |
|
|
|
|
|
|
|
Vec end = GetEnd(); |
|
|
|
@ -397,6 +541,57 @@ public: |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Useful to calculate bounding box of rotated items, when rotation is not cardinal. |
|
|
|
* |
|
|
|
* @return the bounding box of this, after rotation. |
|
|
|
*/ |
|
|
|
const BOX2<Vec> GetBoundingBoxRotated( const VECTOR2I& aRotCenter, |
|
|
|
const EDA_ANGLE& aAngle ) const |
|
|
|
{ |
|
|
|
VECTOR2I corners[4]; |
|
|
|
|
|
|
|
// Build the corners list |
|
|
|
corners[0] = GetOrigin(); |
|
|
|
corners[2] = GetEnd(); |
|
|
|
corners[1].x = corners[0].x; |
|
|
|
corners[1].y = corners[2].y; |
|
|
|
corners[3].x = corners[2].x; |
|
|
|
corners[3].y = corners[0].y; |
|
|
|
|
|
|
|
// Rotate all corners, to find the bounding box |
|
|
|
for( int ii = 0; ii < 4; ii++ ) |
|
|
|
RotatePoint( corners[ii], aRotCenter, aAngle ); |
|
|
|
|
|
|
|
// Find the corners bounding box |
|
|
|
VECTOR2I start = corners[0]; |
|
|
|
VECTOR2I end = corners[0]; |
|
|
|
|
|
|
|
for( int ii = 1; ii < 4; ii++ ) |
|
|
|
{ |
|
|
|
start.x = std::min( start.x, corners[ii].x ); |
|
|
|
start.y = std::min( start.y, corners[ii].y ); |
|
|
|
end.x = std::max( end.x, corners[ii].x ); |
|
|
|
end.y = std::max( end.y, corners[ii].y ); |
|
|
|
} |
|
|
|
|
|
|
|
BOX2<Vec> bbox; |
|
|
|
bbox.SetOrigin( start ); |
|
|
|
bbox.SetEnd( end ); |
|
|
|
|
|
|
|
return bbox; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Mirror the rectangle from the X axis (negate Y pos and size). |
|
|
|
*/ |
|
|
|
void RevertYAxis() |
|
|
|
{ |
|
|
|
m_Pos.y = -m_Pos.y; |
|
|
|
m_Size.y = -m_Size.y; |
|
|
|
Normalize(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Return the area of the rectangle. |
|
|
|
* |
|
|
|
@ -479,6 +674,37 @@ public: |
|
|
|
return sqrt( SquaredDistance( aBox ) ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Return the point in this rect that is closest to the provided point |
|
|
|
*/ |
|
|
|
const Vec ClosestPointTo( const Vec& aPoint ) const |
|
|
|
{ |
|
|
|
BOX2<Vec> me( *this ); |
|
|
|
|
|
|
|
me.Normalize(); // ensure size is >= 0 |
|
|
|
|
|
|
|
// Determine closest point to the circle centre within this rect |
|
|
|
coord_type nx = std::max( me.GetLeft(), std::min( aPoint.x, me.GetRight() ) ); |
|
|
|
coord_type ny = std::max( me.GetTop(), std::min( aPoint.y, me.GetBottom() ) ); |
|
|
|
|
|
|
|
return Vec( nx, ny ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Return the point in this rect that is farthest from the provided point |
|
|
|
*/ |
|
|
|
const Vec FarthestPointTo( const Vec& aPoint ) const |
|
|
|
{ |
|
|
|
BOX2<Vec> me( *this ); |
|
|
|
|
|
|
|
me.Normalize(); // ensure size is >= 0 |
|
|
|
|
|
|
|
coord_type fx = std::max( std::abs( aPoint.x - me.GetLeft() ), std::abs( aPoint.x - me.GetRight() ) ); |
|
|
|
coord_type fy = std::max( std::abs( aPoint.y - me.GetTop() ), std::abs( aPoint.y - me.GetBottom() ) ); |
|
|
|
|
|
|
|
return Vec( fx, fy ); |
|
|
|
} |
|
|
|
|
|
|
|
bool operator==( const BOX2<Vec>& aOther ) const |
|
|
|
{ |
|
|
|
auto t1 ( *this ); |
|
|
|
@ -498,8 +724,10 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
Vec m_Pos; // Rectangle Origin |
|
|
|
Vec m_Size; // Rectangle Size |
|
|
|
Vec m_Pos; // Rectangle Origin |
|
|
|
Vec m_Size; // Rectangle Size |
|
|
|
|
|
|
|
bool m_init; // Is the rectangle initialized |
|
|
|
}; |
|
|
|
|
|
|
|
/* Default specializations */ |
|
|
|
|