Beef up BOX2's API so it can replace EDA_RECT.

This commit is contained in:
Jeff Young 2022-08-30 23:59:30 +01:00
parent 0c8787cbb9
commit c7036ae076
2 changed files with 259 additions and 38 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * 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 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 * Copyright (C) 2013 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
@ -27,13 +27,14 @@
#ifndef __BOX2_H #ifndef __BOX2_H
#define __BOX2_H #define __BOX2_H
#include <math/vector2d.h>
#include <limits> #include <limits>
#include <algorithm> #include <algorithm>
// Needed for the std::optional definition
#include <optional> #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. * A 2D bounding box built on top of an origin point and size vector.
*/ */
@ -41,15 +42,20 @@ template <class Vec>
class BOX2 class BOX2
{ {
public: public:
typedef typename Vec::coord_type coord_type; typedef typename Vec::coord_type coord_type;
typedef typename Vec::extended_type ecoord_type; typedef typename Vec::extended_type ecoord_type;
typedef std::numeric_limits<coord_type> coord_limits; 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) ) : BOX2( const Vec& aPos, const Vec& aSize = Vec(0, 0) ) :
m_Pos( aPos ), m_Pos( aPos ),
m_Size( aSize ) m_Size( aSize ),
m_init( true )
{ {
Normalize(); Normalize();
} }
@ -58,6 +64,7 @@ public:
{ {
m_Pos.x = m_Pos.y = coord_limits::lowest() / 2 + coord_limits::epsilon(); 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_Size.x = m_Size.y = coord_limits::max() - coord_limits::epsilon();
m_init = true;
} }
Vec Centre() const Vec Centre() const
@ -185,29 +192,74 @@ public:
// Compatibility aliases // Compatibility aliases
coord_type GetLeft() const { return GetX(); } coord_type GetLeft() const { return GetX(); }
coord_type GetTop() const { return GetY(); } coord_type GetTop() const { return GetY(); }
void MoveTopTo( coord_type aTop ) { m_Pos.y = aTop; } const Vec GetCenter() const { return Centre(); }
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; } * @return the width or height, whichever is greater.
void SetSize( const Vec& size ) { m_Size = size; } */
void SetSize( coord_type w, coord_type h ) { m_Size.x = w; m_Size.y = h; } int GetSizeMax() const { return ( m_Size.x > m_Size.y ) ? m_Size.x : m_Size.y; }
void Offset( coord_type dx, coord_type dy ) { m_Pos.x += dx; m_Pos.y += dy; }
void Offset( const Vec& offset ) void SetOrigin( const Vec& pos )
{ {
m_Pos.x += offset.x; m_Pos.y += offset.y; 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 )
{
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 ) 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 ) BOX2<Vec> Intersect( const BOX2<Vec>& aRect )
{ {
@ -265,6 +317,78 @@ public:
return BOX2<Vec>( Vec( 0, 0 ), Vec( 0, 0 ) ); 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 const std::string Format() const
{ {
std::stringstream ss; std::stringstream ss;
@ -362,6 +486,18 @@ public:
*/ */
BOX2<Vec>& Merge( const BOX2<Vec>& aRect ) 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 Normalize(); // ensure width and height >= 0
BOX2<Vec> rect = aRect; BOX2<Vec> rect = aRect;
rect.Normalize(); // ensure width and height >= 0 rect.Normalize(); // ensure width and height >= 0
@ -384,6 +520,14 @@ public:
*/ */
BOX2<Vec>& Merge( const Vec& aPoint ) 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 Normalize(); // ensure width and height >= 0
Vec end = GetEnd(); Vec end = GetEnd();
@ -397,6 +541,57 @@ public:
return *this; 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. * Return the area of the rectangle.
* *
@ -479,6 +674,37 @@ public:
return sqrt( SquaredDistance( aBox ) ); 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 bool operator==( const BOX2<Vec>& aOther ) const
{ {
auto t1 ( *this ); auto t1 ( *this );
@ -498,8 +724,10 @@ public:
} }
private: private:
Vec m_Pos; // Rectangle Origin Vec m_Pos; // Rectangle Origin
Vec m_Size; // Rectangle Size Vec m_Size; // Rectangle Size
bool m_init; // Is the rectangle initialized
}; };
/* Default specializations */ /* Default specializations */

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -301,14 +301,7 @@ void PCB_MARKER::SetZoom( double aZoomFactor )
const EDA_RECT PCB_MARKER::GetBoundingBox() const const EDA_RECT PCB_MARKER::GetBoundingBox() const
{ {
EDA_RECT bbox = m_shapeBoundingBox; return GetBoundingBoxMarker();
VECTOR2I pos = m_Pos;
pos.x += int( bbox.GetOrigin().x * MarkerScale() );
pos.y += int( bbox.GetOrigin().y * MarkerScale() );
return EDA_RECT( pos, wxSize( int( bbox.GetWidth() * MarkerScale() ),
int( bbox.GetHeight() * MarkerScale() ) ) );
} }