Geom: use SHAPE_SEGMENT for OVAL

OVAL didn't do anything SHAPE_SEGMENT couldn't already do.
This commit is contained in:
John Beard 2025-09-02 16:57:21 +08:00
parent 60a5c8b742
commit 1e272ca21b
7 changed files with 66 additions and 119 deletions

View File

@ -25,86 +25,22 @@
#include <vector>
#include <math/box2.h>
#include <math/vector2d.h>
#include <geometry/approximation.h>
#include <geometry/eda_angle.h>
#include <geometry/point_types.h>
#include <geometry/seg.h>
class SHAPE_LINE_CHAIN;
#include <geometry/shape_segment.h>
/**
* Class that represents an oval shape (rectangle with semicircular end caps)
* @file Utility functions for ovals (oblongs/stadiums)
*
* This is not a full-blown SHAPE (yet), but can be used for some simple
* purposes, as well as for type-based logic.
* An "oval" is represented by SHAPE_SEGMENT, but these functions
* aren't required for most users of SHAPE_SEGMENT.
*/
class OVAL
{
public:
/**
* Create an oval from the segment joining the centers of the semicircles
* and the diameter of the semicircles.
*/
OVAL( const SEG& aSeg, int aWidth );
/**
* Create an oval from the overall size, the center of the oval, and the rotation.
*
* The shorter dimension is the width of the semicircles.
*/
OVAL( const VECTOR2I& aOverallSize, const VECTOR2I& aCenter, const EDA_ANGLE& aRotation );
/**
* Get the bounding box of the oval
*/
BOX2I BBox( int aClearance ) const;
/**
* Get the width of the oval (diameter of the semicircles)
*/
int GetWidth() const { return m_width; }
/**
* Get the overall length of the oval from endcap tip to endcap tip
*/
int GetLength() const { return m_seg.Length() + m_width; }
/**
* Get the side length of the oval (=length between the centers of the semicircles)
*/
int GetSideLength() const { return m_seg.Length(); }
/**
* Get the center point of the oval.
*/
VECTOR2I GetCenter() const { return m_seg.Center(); }
/**
* Get the central segment of the oval
*
* (Endpoint are the centers of the semicircles)
*/
const SEG& GetSegment() const { return m_seg; }
/**
* Get the angle of the oval's central segment.
*
* The direction is aligned with the segment start/end
*/
EDA_ANGLE GetAngle() const { return EDA_ANGLE( m_seg.B - m_seg.A ); }
private:
SEG m_seg;
int m_width;
};
class SHAPE_LINE_CHAIN;
namespace KIGEOM
{
SHAPE_LINE_CHAIN ConvertToChain( const OVAL& aOval );
SHAPE_LINE_CHAIN ConvertToChain( const SHAPE_SEGMENT& aOval );
enum OVAL_KEY_POINTS
@ -129,12 +65,11 @@ using OVAL_KEY_POINT_FLAGS = unsigned int;
* - The tips of the end caps
* - The extreme cardinal points of the whole oval (if rotated non-cardinally)
*
* @param aOvalSize - The size of the oval (overall length and width)
* @param aRotation - The rotation of the oval
* @param aOval - The oval to get the points from
* @param aFlags - The flags indicating which points to return
*
* @return std::vector<TYPED_POINT2I> - The list of points and their geomtrical types
*/
std::vector<TYPED_POINT2I> GetOvalKeyPoints( const OVAL& aOval, OVAL_KEY_POINT_FLAGS aFlags );
std::vector<TYPED_POINT2I> GetOvalKeyPoints( const SHAPE_SEGMENT& aOval, OVAL_KEY_POINT_FLAGS aFlags );
} // namespace KIGEOM
} // namespace KIGEOM

View File

@ -54,6 +54,8 @@ public:
m_width( aWidth )
{};
static SHAPE_SEGMENT BySizeAndCenter( const VECTOR2I& aSize, const VECTOR2I& aCenter, const EDA_ANGLE& aRotation );
~SHAPE_SEGMENT() {};
SHAPE* Clone() const override
@ -140,6 +142,24 @@ public:
return m_width;
}
/**
* Get the total length of the segment, from tip to tip.
*/
int GetTotalLength() const
{
return m_seg.Length() + m_width;
}
VECTOR2I GetCenter() const
{
return m_seg.Center();
}
EDA_ANGLE GetAngle() const
{
return EDA_ANGLE( m_seg.B - m_seg.A );
}
bool IsSolid() const override
{
return true;

View File

@ -27,48 +27,14 @@
#include <trigo.h> // for RotatePoint
#include <geometry/shape_arc.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_line_chain.h>
using namespace KIGEOM;
OVAL::OVAL( const SEG& aSeg, int aWidth ) : m_seg( aSeg ), m_width( aWidth )
SHAPE_LINE_CHAIN KIGEOM::ConvertToChain( const SHAPE_SEGMENT& aOval )
{
// A negative width is meaningless
wxASSERT( aWidth > 0 );
}
OVAL::OVAL( const VECTOR2I& aOverallSize, const VECTOR2I& aCenter, const EDA_ANGLE& aRotation )
{
VECTOR2I segVec{};
// Find the major axis, without endcaps
if( aOverallSize.x > aOverallSize.y )
segVec.x = ( aOverallSize.x - aOverallSize.y );
else
segVec.y = ( aOverallSize.y - aOverallSize.x );
RotatePoint( segVec, aRotation );
m_seg = SEG( aCenter - segVec / 2, aCenter + segVec / 2 );
m_width = std::min( aOverallSize.x, aOverallSize.y );
}
BOX2I OVAL::BBox( int aClearance ) const
{
const int rad = m_width / 2 + aClearance;
const VECTOR2I& topleft = LexicographicalMin( m_seg.A, m_seg.B );
const VECTOR2I& bottomright = LexicographicalMax( m_seg.A, m_seg.B );
return BOX2I::ByCorners( topleft - VECTOR2I( rad, rad ), bottomright + VECTOR2I( rad, rad ) );
}
SHAPE_LINE_CHAIN KIGEOM::ConvertToChain( const OVAL& aOval )
{
const SEG& seg = aOval.GetSegment();
const SEG& seg = aOval.GetSeg();
const VECTOR2I perp = GetRotated( seg.B - seg.A, ANGLE_90 ).Resize( aOval.GetWidth() / 2 );
SHAPE_LINE_CHAIN chain;
@ -81,11 +47,10 @@ SHAPE_LINE_CHAIN KIGEOM::ConvertToChain( const OVAL& aOval )
}
std::vector<TYPED_POINT2I> KIGEOM::GetOvalKeyPoints( const OVAL& aOval,
OVAL_KEY_POINT_FLAGS aFlags )
std::vector<TYPED_POINT2I> KIGEOM::GetOvalKeyPoints( const SHAPE_SEGMENT& aOval, OVAL_KEY_POINT_FLAGS aFlags )
{
const int half_width = aOval.GetWidth() / 2;
const int half_len = aOval.GetLength() / 2;
const int half_len = aOval.GetTotalLength() / 2;
const EDA_ANGLE rotation = aOval.GetAngle() - ANGLE_90;
// Points on a non-rotated pad at the origin, long-axis is y
@ -194,4 +159,4 @@ std::vector<TYPED_POINT2I> KIGEOM::GetOvalKeyPoints( const OVAL& aOval,
}
return pts;
}
}

View File

@ -29,6 +29,31 @@
#include <geometry/shape_circle.h>
#include <convert_basic_shapes_to_polygon.h>
SHAPE_SEGMENT SHAPE_SEGMENT::BySizeAndCenter( const VECTOR2I& aOverallSize, const VECTOR2I& aCenter,
const EDA_ANGLE& aRotation )
{
VECTOR2I segVec{ 0, 0 };
int width;
// Find the major axis, without endcaps
if( aOverallSize.x > aOverallSize.y )
{
width = aOverallSize.y;
segVec.x = aOverallSize.x - width;
}
else
{
width = aOverallSize.x;
segVec.y = aOverallSize.y - width;
}
RotatePoint( segVec, aRotation );
return SHAPE_SEGMENT( aCenter - segVec / 2, aCenter + segVec / 2, width );
}
const std::string SHAPE_SEGMENT::Format( bool aCplusPlus ) const
{
std::stringstream ss;

View File

@ -933,7 +933,7 @@ void OUTSET_ROUTINE::ProcessItem( BOARD_ITEM& aItem )
if( m_params.roundCorners )
{
const OVAL oval( seg, m_params.outsetDistance * 2 );
const SHAPE_SEGMENT oval( seg, m_params.outsetDistance * 2 );
addChain( KIGEOM::ConvertToChain( oval ) );
}
else

View File

@ -1204,7 +1204,8 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
}
case PAD_SHAPE::OVAL:
{
const OVAL oval( aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() );
const SHAPE_SEGMENT oval = SHAPE_SEGMENT::BySizeAndCenter(
aPad->GetSize( aLayer ), aPad->GetPosition(), aPad->GetOrientation() );
for( const TYPED_POINT2I& pt : KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags ) )
addAnchor( pt.m_point, OUTLINE | SNAPPABLE, aPad, pt.m_types );
@ -1281,7 +1282,8 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
// For now there's no way to have an off-angle hole, so this is the
// same as the pad. In future, this may not be true:
// https://gitlab.com/kicad/code/kicad/-/issues/4124
const OVAL oval( hole_size, hole_pos, aPad->GetOrientation() );
const SHAPE_SEGMENT oval =
SHAPE_SEGMENT::BySizeAndCenter( hole_size, hole_pos, aPad->GetOrientation() );
snap_pts = KIGEOM::GetOvalKeyPoints( oval, ovalKeyPointFlags );
}

View File

@ -59,7 +59,7 @@ BOOST_AUTO_TEST_SUITE( Oval )
struct OVAL_POINTS_TEST_CASE
{
OVAL m_oval;
SHAPE_SEGMENT m_oval;
std::vector<TYPED_POINT2I> m_expected_points;
};