ADDED: Rounded Rectangles

Fixes https://gitlab.com/kicad/code/kicad/-/issues/4742
This commit is contained in:
Seth Hillbrand 2025-08-29 17:37:04 -07:00
parent e282dca102
commit 8acf5c1a25
38 changed files with 537 additions and 91 deletions

View File

@ -371,6 +371,7 @@ message GraphicRectangleAttributes
{
kiapi.common.types.Vector2 top_left = 1;
kiapi.common.types.Vector2 bottom_right = 2;
kiapi.common.types.Distance corner_radius = 3;
}
message GraphicArcAttributes

View File

@ -35,6 +35,7 @@
#include <geometry/shape_simple.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_rect.h>
#include <geometry/roundrect.h>
#include <geometry/geometry_utils.h>
#include <macros.h>
#include <math/util.h> // for KiROUND
@ -54,6 +55,7 @@ EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
m_hatchingDirty( true ),
m_rectangleHeight( 0 ),
m_rectangleWidth( 0 ),
m_cornerRadius( 0 ),
m_segmentLength( 0 ),
m_editState( 0 ),
m_proxyItem( false )
@ -73,6 +75,7 @@ EDA_SHAPE::EDA_SHAPE( const SHAPE& aShape ) :
m_hatchingDirty( true ),
m_rectangleHeight( 0 ),
m_rectangleWidth( 0 ),
m_cornerRadius( 0 ),
m_segmentLength( 0 ),
m_editState( 0 ),
m_proxyItem( false )
@ -188,6 +191,7 @@ void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
types::GraphicRectangleAttributes* rectangle = shape.mutable_rectangle();
PackVector2( *rectangle->mutable_top_left(), GetStart() );
PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
rectangle->mutable_corner_radius()->set_value_nm( GetCornerRadius() );
break;
}
@ -280,6 +284,7 @@ bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
SetShape( SHAPE_T::RECTANGLE );
SetStart( UnpackVector2( shape.rectangle().top_left() ) );
SetEnd( UnpackVector2( shape.rectangle().bottom_right() ) );
SetCornerRadius( shape.rectangle().corner_radius().value_nm() );
}
else if( shape.has_arc() )
{
@ -432,6 +437,16 @@ int EDA_SHAPE::GetRectangleWidth() const
}
}
int EDA_SHAPE::GetCornerRadius() const
{
return m_cornerRadius;
}
void EDA_SHAPE::SetCornerRadius( int aRadius )
{
m_cornerRadius = aRadius;
}
void EDA_SHAPE::SetLength( const double& aLength )
{
@ -1800,17 +1815,38 @@ std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineCh
case SHAPE_T::RECTANGLE:
{
std::vector<VECTOR2I> pts = GetRectCorners();
if( solidFill )
effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
if( width > 0 || !solidFill )
if( m_cornerRadius > 0 )
{
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
ROUNDRECT rr( SHAPE_RECT( GetStart(), GetRectangleWidth(), GetRectangleHeight() ),
m_cornerRadius );
SHAPE_POLY_SET poly;
rr.TransformToPolygon( poly );
SHAPE_LINE_CHAIN outline = poly.Outline( 0 );
if( solidFill )
effectiveShapes.emplace_back( new SHAPE_SIMPLE( outline ) );
if( width > 0 || !solidFill )
{
for( int i = 0; i < outline.PointCount() - 1; ++i )
effectiveShapes.emplace_back(
new SHAPE_SEGMENT( outline.CPoint( i ), outline.CPoint( i + 1 ), width ) );
}
}
else
{
std::vector<VECTOR2I> pts = GetRectCorners();
if( solidFill )
effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
if( width > 0 || !solidFill )
{
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
}
}
break;
}
@ -2643,6 +2679,12 @@ static struct EDA_SHAPE_DESC
shapeProps )
.SetAvailableFunc( isRectangle );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Corner Radius" ),
&EDA_SHAPE::SetCornerRadius, &EDA_SHAPE::GetCornerRadius,
PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::NOT_A_COORD ),
shapeProps )
.SetAvailableFunc( isRectangle );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
&EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::PT_SIZE ),
shapeProps );

View File

@ -29,6 +29,7 @@
#include <macros.h>
#include <string_utils.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_rect.h>
#include <trigo.h>
#include <fmt/core.h>
@ -446,10 +447,21 @@ void DXF_PLOTTER::SetColor( const COLOR4D& color )
}
void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius )
{
wxASSERT( m_outputFile );
if( aCornerRadius > 0 )
{
BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
box.Normalize();
SHAPE_RECT rect( box );
rect.SetRadius( aCornerRadius );
PlotPoly( rect.Outline(), fill, width, nullptr );
return;
}
if( p1 != p2 )
{
MoveTo( p1 );
@ -605,6 +617,22 @@ void DXF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFi
}
void DXF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill, int aWidth,
void* aData )
{
std::vector<VECTOR2I> cornerList;
cornerList.reserve( aCornerList.PointCount() );
for( int ii = 0; ii < aCornerList.PointCount(); ii++ )
cornerList.emplace_back( aCornerList.CPoint( ii ) );
if( aCornerList.IsClosed() && cornerList.front() != cornerList.back() )
cornerList.emplace_back( aCornerList.CPoint( 0 ) );
PlotPoly( cornerList, aFill, aWidth, aData );
}
void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
{
wxASSERT( m_outputFile );
@ -735,17 +763,17 @@ void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width,
{
VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
offsetp1.x += width;
offsetp1.y += width;
offsetp2.x -= width;
offsetp2.y -= width;
Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
}
else
{
Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
}
}

View File

@ -25,6 +25,7 @@
#include <string_utils.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_rect.h>
#include <macros.h>
#include <math/util.h> // for KiROUND
#include <trigo.h>
@ -846,8 +847,19 @@ void GERBER_PLOTTER::PenTo( const VECTOR2I& aPos, char plume )
}
void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
void GERBER_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius )
{
if( aCornerRadius > 0 )
{
BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
box.Normalize();
SHAPE_RECT rect( box );
rect.SetRadius( aCornerRadius );
PlotPoly( rect.Outline(), fill, width, nullptr );
return;
}
std::vector<VECTOR2I> cornerList;
cornerList.reserve( 5 );

View File

@ -51,6 +51,7 @@
#include <fmt/ranges.h>
#include <plotters/plotters_pslike.h>
#include <geometry/shape_rect.h>
std::string PDF_PLOTTER::encodeStringForPlotter( const wxString& aText )
@ -227,7 +228,8 @@ void PDF_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
}
void PDF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
void PDF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius )
{
wxASSERT( m_workFile );
@ -236,6 +238,16 @@ void PDF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int
SetCurrentLineWidth( width );
if( aCornerRadius > 0 )
{
BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
box.Normalize();
SHAPE_RECT rect( box );
rect.SetRadius( aCornerRadius );
PlotPoly( rect.Outline(), fill, width, nullptr );
return;
}
VECTOR2I size = p2 - p1;
if( size.x == 0 && size.y == 0 )
@ -429,6 +441,22 @@ void PDF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFi
}
void PDF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill, int aWidth,
void* aData )
{
std::vector<VECTOR2I> cornerList;
cornerList.reserve( aCornerList.PointCount() );
for( int ii = 0; ii < aCornerList.PointCount(); ii++ )
cornerList.emplace_back( aCornerList.CPoint( ii ) );
if( aCornerList.IsClosed() && cornerList.front() != cornerList.back() )
cornerList.emplace_back( aCornerList.CPoint( 0 ) );
PlotPoly( cornerList, aFill, aWidth, aData );
}
void PDF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
{
wxASSERT( m_workFile );

View File

@ -30,6 +30,7 @@
#include <convert_basic_shapes_to_polygon.h>
#include <macros.h>
#include <math/util.h> // for KiROUND
#include <geometry/shape_rect.h>
#include <string_utils.h>
#include <trigo.h>
#include <fmt/format.h>
@ -457,13 +458,24 @@ void PS_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
}
void PS_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
void PS_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius )
{
SetCurrentLineWidth( width );
if( fill == FILL_T::NO_FILL && GetCurrentLineWidth() <= 0 )
return;
if( aCornerRadius > 0 )
{
BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
box.Normalize();
SHAPE_RECT rect( box );
rect.SetRadius( aCornerRadius );
PlotPoly( rect.Outline(), fill, width, nullptr );
return;
}
VECTOR2D p1_dev = userToDeviceCoordinates( p1 );
VECTOR2D p2_dev = userToDeviceCoordinates( p2 );
@ -556,6 +568,22 @@ void PS_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFil
}
void PS_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill, int aWidth,
void* aData )
{
std::vector<VECTOR2I> cornerList;
cornerList.reserve( aCornerList.PointCount() );
for( int ii = 0; ii < aCornerList.PointCount(); ii++ )
cornerList.emplace_back( aCornerList.CPoint( ii ) );
if( aCornerList.IsClosed() && cornerList.front() != cornerList.back() )
cornerList.emplace_back( aCornerList.CPoint( 0 ) );
PlotPoly( cornerList, aFill, aWidth, aData );
}
void PS_PLOTTER::PlotImage( const wxImage& aImage, const VECTOR2I& aPos, double aScaleFactor )
{
VECTOR2I pix_size; // size of the bitmap in pixels

View File

@ -372,7 +372,8 @@ void SVG_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
}
void SVG_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width )
void SVG_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius )
{
BOX2I rect( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
rect.Normalize();
@ -412,7 +413,7 @@ void SVG_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int
rect_dev.GetPosition().y,
rect_dev.GetSize().x,
rect_dev.GetSize().y,
0.0 /* radius of rounded corners */ );
userToDeviceSize( aCornerRadius ) );
}
}

View File

@ -267,7 +267,7 @@ void PLOTTER::PlotImage( const wxImage& aImage, const VECTOR2I& aPos, double aSc
end.x += size.x;
end.y += size.y;
Rect( start, end, FILL_T::NO_FILL, USE_DEFAULT_LINE_WIDTH );
Rect( start, end, FILL_T::NO_FILL, USE_DEFAULT_LINE_WIDTH, 0 );
}
@ -584,7 +584,7 @@ void PLOTTER::ThickArc( const EDA_SHAPE& aArcShape, void* aData, int aWidth )
void PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
{
Rect( p1, p2, FILL_T::NO_FILL, width );
Rect( p1, p2, FILL_T::NO_FILL, width, 0 );
}

View File

@ -312,7 +312,7 @@ void EDIT_POINTS::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
};
for( const EDIT_POINT& point : m_points )
drawPoint( point );
drawPoint( point, point.DrawCircle() );
for( const EDIT_LINE& line : m_lines )
{

View File

@ -54,7 +54,8 @@
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20240819 // Embedded Files - Update hash algorithm to Murmur3
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20241209 // Private flags for SCH_FIELDs
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20250318 // ~ no longer means empty text
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20250324 // Jumper pin groups
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20250324 // Jumper pin groups
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20250829 // Rounded Rectangles
/**
* Schematic file version.
@ -124,4 +125,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250425 // uuids for tables
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250513 // Groups can have design block lib_id
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250610 // DNP, etc. flags for rule areas
#define SEXPR_SCHEMATIC_FILE_VERSION 20250827 // Custom body styles
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250827 // Custom body styles
#define SEXPR_SCHEMATIC_FILE_VERSION 20250829 // Rounded Rectangles

View File

@ -269,6 +269,9 @@ void formatRect( OUTPUTFORMATTER* aFormatter, EDA_SHAPE* aRect, bool aIsPrivate,
aIsPrivate ? "private" : "",
formatIU( aRect->GetStart(), aInvertY ).c_str(),
formatIU( aRect->GetEnd(), aInvertY ).c_str() );
if( aRect->GetCornerRadius() > 0 )
aFormatter->Print( "(radius %s)",
formatIU( aRect->GetCornerRadius() ).c_str() );
aStroke.Format( aFormatter, schIUScale );
formatFill( aFormatter, aFillMode, aFillColor );

View File

@ -1895,6 +1895,11 @@ SCH_SHAPE* SCH_IO_KICAD_SEXPR_PARSER::parseSymbolRectangle()
NeedRIGHT();
break;
case T_radius:
rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
NeedRIGHT();
break;
case T_stroke:
parseStroke( stroke );
rectangle->SetStroke( stroke );
@ -4186,6 +4191,11 @@ SCH_SHAPE* SCH_IO_KICAD_SEXPR_PARSER::parseSchRectangle()
NeedRIGHT();
break;
case T_radius:
rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
NeedRIGHT();
break;
case T_stroke:
parseStroke( stroke );
rectangle->SetStroke( stroke );

View File

@ -32,6 +32,8 @@
#include <callback_gal.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_rect.h>
#include <geometry/roundrect.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_utils.h>
#include <gr_text.h>
#include <sch_pin.h>
@ -1588,7 +1590,20 @@ void SCH_PAINTER::draw( const SCH_SHAPE* aShape, int aLayer, bool aDimmed )
break;
case SHAPE_T::RECTANGLE:
m_gal->DrawRectangle( shape->GetPosition(), shape->GetEnd() );
if( shape->GetCornerRadius() > 0 )
{
ROUNDRECT rr( SHAPE_RECT( shape->GetPosition(),
shape->GetRectangleWidth(),
shape->GetRectangleHeight() ),
shape->GetCornerRadius() );
SHAPE_POLY_SET poly;
rr.TransformToPolygon( poly );
m_gal->DrawPolygon( poly );
}
else
{
m_gal->DrawRectangle( shape->GetPosition(), shape->GetEnd() );
}
break;
case SHAPE_T::POLY:

View File

@ -462,7 +462,7 @@ bool SCH_PLOTTER::plotOneSheetPS( const wxString& aFileName, SCH_SCREEN* aScreen
VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0, 0 );
}
if( aPlotOpts.m_plotDrawingSheet )
@ -639,7 +639,7 @@ bool SCH_PLOTTER::plotOneSheetSVG( const wxString& aFileName, SCH_SCREEN* aScree
VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0, 0 );
}
if( aPlotOpts.m_plotDrawingSheet )

View File

@ -274,7 +274,7 @@ void SCH_SHAPE::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS&
break;
case SHAPE_T::RECTANGLE:
aPlotter->Rect( start, end, fill, pen_size );
aPlotter->Rect( start, end, fill, pen_size, GetCornerRadius() );
break;
case SHAPE_T::POLY:

View File

@ -1222,14 +1222,14 @@ void SCH_SHEET::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS&
if( aBackground && backgroundColor.a > 0.0 )
{
aPlotter->SetColor( backgroundColor );
aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::FILLED_SHAPE, 1 );
aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::FILLED_SHAPE, 1, 0 );
}
else
{
aPlotter->SetColor( borderColor );
int penWidth = GetEffectivePenWidth( getRenderSettings( aPlotter ) );
aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::NO_FILL, penWidth );
aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::NO_FILL, penWidth, 0 );
}
// Make the sheet object a clickable hyperlink (e.g. for PDF plotter)

View File

@ -24,6 +24,7 @@
#include "sch_point_editor.h"
#include <algorithm>
#include <ee_grid_helper.h>
#include <tool/tool_manager.h>
#include <sch_commit.h>
@ -61,7 +62,7 @@ enum ARC_POINTS
enum RECTANGLE_POINTS
{
RECT_TOPLEFT, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT, RECT_CENTER
RECT_TOPLEFT, RECT_RADIUS, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT, RECT_CENTER
};
@ -391,6 +392,8 @@ public:
VECTOR2I botRight = aRect.GetEnd();
aPoints.AddPoint( topLeft );
aPoints.AddPoint( VECTOR2I( botRight.x - aRect.GetCornerRadius(), topLeft.y ) );
aPoints.Point( RECT_RADIUS ).SetDrawCircle();
aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
aPoints.AddPoint( botRight );
@ -412,6 +415,7 @@ public:
VECTOR2I botRight = aRect.GetEnd();
aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
aPoints.Point( RECT_RADIUS ).SetPosition( VECTOR2I( botRight.x - aRect.GetCornerRadius(), topLeft.y ) );
aPoints.Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
aPoints.Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
@ -578,6 +582,16 @@ public:
VECTOR2I moveVec = aPoints.Point( RECT_CENTER ).GetPosition() - oldBox.GetCenter();
m_rect.Move( moveVec );
}
else if( isModified( aEditedPoint, aPoints.Point( RECT_RADIUS ) ) )
{
int width = std::abs( botRight.x - topLeft.x );
int height = std::abs( botRight.y - topLeft.y );
int maxRadius = std::min( width, height ) / 2;
int x = aPoints.Point( RECT_RADIUS ).GetX();
x = std::clamp( x, botRight.x - maxRadius, botRight.x );
aPoints.Point( RECT_RADIUS ).SetPosition( VECTOR2I( x, topLeft.y ) );
m_rect.SetCornerRadius( botRight.x - x );
}
else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
{
oldSegs = KIGEOM::GetSegsInDirection( oldBox, DIRECTION_45::Directions::N );

View File

@ -391,6 +391,9 @@ public:
void SetRectangle( const long long int& aHeight, const long long int& aWidth );
void SetCornerRadius( int aRadius );
int GetCornerRadius() const;
void SetSegmentAngle( const EDA_ANGLE& aAngle );
bool IsClockwiseArc() const;
@ -496,6 +499,7 @@ protected:
long long int m_rectangleHeight;
long long int m_rectangleWidth;
int m_cornerRadius;
double m_segmentLength;
EDA_ANGLE m_segmentAngle;

View File

@ -228,7 +228,8 @@ public:
int GetPlotterArcHighDef() const { return m_IUsPerDecimil * 2; }
// Low level primitives
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) = 0;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) = 0;
virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width ) = 0;
virtual void Arc( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd,

View File

@ -84,7 +84,8 @@ public:
/**
* DXF rectangle: fill not supported.
*/
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) override;
/**
* DXF circle: full functionality; it even does 'fills' drawing a
@ -107,6 +108,9 @@ public:
virtual void PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill,
int aWidth, void* aData = nullptr ) override;
virtual void PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill,
int aWidth, void* aData = nullptr ) override;
virtual void ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int width,
void* aData ) override;

View File

@ -60,7 +60,8 @@ public:
double aScale, bool aMirror ) override;
// Basic plot primitives
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) override;
virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width ) override;
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth ) override;

View File

@ -185,7 +185,8 @@ public:
virtual void SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil,
double aScale, bool aMirror ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) override;
virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width ) override;
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth ) override;
@ -223,6 +224,9 @@ public:
const KIFONT::METRICS& aFontMetrics,
void* aData = nullptr ) override;
virtual void PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill,
int aWidth, void* aData = nullptr ) override;
protected:
virtual void emitSetRGBColor( double r, double g, double b, double a ) override;
@ -326,7 +330,8 @@ public:
/**
* Rectangles in PDF. Supported by the native operator.
*/
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) override;
/**
* Circle drawing for PDF. They're approximated by curves, but fill is supported
@ -346,6 +351,9 @@ public:
virtual void PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill,
int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override;
virtual void PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill,
int aWidth = USE_DEFAULT_LINE_WIDTH, void* aData = nullptr ) override;
virtual void PenTo( const VECTOR2I& pos, char plume ) override;
virtual void Text( const VECTOR2I& aPos,
@ -572,7 +580,8 @@ public:
virtual void SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil, double aScale,
bool aMirror ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width ) override;
virtual void Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
int aCornerRadius = 0 ) override;
virtual void Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int width ) override;
virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill,

View File

@ -54,6 +54,7 @@ public:
m_position( aPoint ),
m_isActive( false ),
m_isHover( false ),
m_drawCircle( false ),
m_gridConstraint( SNAP_TO_GRID ),
m_snapConstraint( OBJECT_LAYERS ),
m_connected( aConnected )
@ -175,6 +176,9 @@ public:
bool IsHover() const { return m_isHover; }
void SetHover( bool aHover = true ) { m_isHover = aHover; }
bool DrawCircle() const { return m_drawCircle; }
void SetDrawCircle( bool aDrawCircle = true ) { m_drawCircle = aDrawCircle; }
GRID_CONSTRAINT_TYPE GetGridConstraint() const { return m_gridConstraint; }
void SetGridConstraint( GRID_CONSTRAINT_TYPE aConstraint ) { m_gridConstraint = aConstraint; }
@ -201,6 +205,7 @@ private:
VECTOR2I m_position; ///< Position of EDIT_POINT.
bool m_isActive; ///< True if this point is being manipulated.
bool m_isHover; ///< True if this point is being hovered over.
bool m_drawCircle; ///< True if the point is drawn circular.
GRID_CONSTRAINT_TYPE m_gridConstraint; ///< Describe the grid snapping behavior.
SNAP_CONSTRAINT_TYPE m_snapConstraint; ///< Describe the object snapping behavior.

View File

@ -30,6 +30,7 @@
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h>
#include <math/box2.h>
#include <math/vector2d.h>
#include <trigo.h>
@ -43,7 +44,8 @@ public:
SHAPE_RECT() :
SHAPE( SH_RECT ),
m_w( 0 ),
m_h( 0 )
m_h( 0 ),
m_radius( 0 )
{}
/**
@ -53,7 +55,8 @@ public:
SHAPE( SH_RECT ),
m_p0( aBox.GetPosition() ),
m_w( aBox.GetWidth() ),
m_h( aBox.GetHeight() )
m_h( aBox.GetHeight() ),
m_radius( 0 )
{}
/**
@ -63,7 +66,8 @@ public:
SHAPE( SH_RECT ),
m_p0( aX0, aY0 ),
m_w( aW ),
m_h( aH )
m_h( aH ),
m_radius( 0 )
{}
/**
@ -73,7 +77,8 @@ public:
SHAPE( SH_RECT ),
m_p0( aP0 ),
m_w( aW ),
m_h( aH )
m_h( aH ),
m_radius( 0 )
{}
/**
@ -83,14 +88,16 @@ public:
SHAPE( SH_RECT ),
m_p0( aP0 ),
m_w( aP1.x - aP0.x ),
m_h( aP1.y - aP0.y )
m_h( aP1.y - aP0.y ),
m_radius( 0 )
{}
SHAPE_RECT( const SHAPE_RECT& aOther ) :
SHAPE( SH_RECT ),
m_p0( aOther.m_p0 ),
m_w( aOther.m_w ),
m_h( aOther.m_h )
m_h( aOther.m_h ),
m_radius( aOther.m_radius )
{};
SHAPE* Clone() const override
@ -112,11 +119,13 @@ public:
*/
SHAPE_RECT GetInflated( int aOffset ) const
{
return SHAPE_RECT{
SHAPE_RECT r{
m_p0 - VECTOR2I( aOffset, aOffset ),
m_w + 2 * aOffset,
m_h + 2 * aOffset,
};
r.SetRadius( m_radius + aOffset );
return r;
}
/**
@ -186,6 +195,19 @@ public:
return m_h;
}
/**
* @return the corner radius of the rectangle.
*/
int GetRadius() const
{
return m_radius;
}
void SetRadius( int aRadius )
{
m_radius = aRadius;
}
void Move( const VECTOR2I& aVector ) override
{
m_p0 += aVector;
@ -209,17 +231,7 @@ public:
return true;
}
const SHAPE_LINE_CHAIN Outline() const
{
SHAPE_LINE_CHAIN rv;
rv.Append( m_p0 );
rv.Append( m_p0.x, m_p0.y + m_h );
rv.Append( m_p0.x + m_w, m_p0.y + m_h );
rv.Append( m_p0.x + m_w, m_p0.y );
rv.Append( m_p0 );
rv.SetClosed( true );
return rv;
}
const SHAPE_LINE_CHAIN Outline() const;
virtual const std::string Format( bool aCplusPlus = true ) const override;
@ -230,6 +242,7 @@ private:
VECTOR2I m_p0; ///< Top-left corner
int m_w; ///< Width
int m_h; ///< Height
int m_radius; ///< Corner radius
};
#endif // __SHAPE_RECT_H

View File

@ -26,6 +26,7 @@
#include <geometry/shape_rect.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/roundrect.h>
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
{
@ -114,6 +115,8 @@ const std::string SHAPE_RECT::Format( bool aCplusPlus ) const
ss << m_w;
ss << ", ";
ss << m_h;
ss << ", ";
ss << m_radius;
ss << ");";
return ss.str();
@ -123,6 +126,13 @@ const std::string SHAPE_RECT::Format( bool aCplusPlus ) const
void SHAPE_RECT::TransformToPolygon( SHAPE_POLY_SET& aBuffer, int aError,
ERROR_LOC aErrorLoc ) const
{
if( m_radius > 0 )
{
ROUNDRECT rr( *this, m_radius );
rr.TransformToPolygon( aBuffer );
return;
}
int idx = aBuffer.NewOutline();
SHAPE_LINE_CHAIN& outline = aBuffer.Outline( idx );
@ -132,3 +142,23 @@ void SHAPE_RECT::TransformToPolygon( SHAPE_POLY_SET& aBuffer, int aError,
outline.Append( { m_p0.x, m_p0.y + m_h } );
outline.SetClosed( true );
}
const SHAPE_LINE_CHAIN SHAPE_RECT::Outline() const
{
if( m_radius > 0 )
{
SHAPE_POLY_SET poly;
ROUNDRECT rr( *this, m_radius );
rr.TransformToPolygon( poly );
return poly.Outline( 0 );
}
SHAPE_LINE_CHAIN rv;
rv.Append( m_p0 );
rv.Append( m_p0.x, m_p0.y + m_h );
rv.Append( m_p0.x + m_w, m_p0.y + m_h );
rv.Append( m_p0.x + m_w, m_p0.y );
rv.Append( m_p0 );
rv.SetClosed( true );
return rv;
}

View File

@ -964,6 +964,9 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_SHAPE* aShape ) const
prefix.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
if( aShape->GetCornerRadius() > 0 )
m_out->Print( " (radius %s)", formatInternalUnits( aShape->GetCornerRadius() ).c_str() );
break;
case SHAPE_T::CIRCLE:
@ -1801,6 +1804,9 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
m_out->Print( "(gr_rect (start %s) (end %s)",
formatInternalUnits( primitive->GetStart() ).c_str(),
formatInternalUnits( primitive->GetEnd() ).c_str() );
if( primitive->GetCornerRadius() > 0 )
m_out->Print( " (radius %s)", formatInternalUnits( primitive->GetCornerRadius() ).c_str() );
}
break;

View File

@ -186,7 +186,8 @@ class PCB_IO_KICAD_SEXPR; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20250513 // Groups can have design block lib_id
//#define SEXPR_BOARD_FILE_VERSION 20250801 // (island) -> (island yes/no)
//#define SEXPR_BOARD_FILE_VERSION 20250811 // press-fit pad fabr prop support
#define SEXPR_BOARD_FILE_VERSION 20250818 // Support for custom layer counts in footprints
//#define SEXPR_BOARD_FILE_VERSION 20250818 // Support for custom layer counts in footprints
#define SEXPR_BOARD_FILE_VERSION 20250829 // Support Rounded Rectangles
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting

View File

@ -3192,6 +3192,11 @@ PCB_SHAPE* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_SHAPE( BOARD_ITEM* aParent )
NeedRIGHT();
break;
case T_radius:
shape->SetCornerRadius( parseBoardUnits( "corner radius" ) );
NeedRIGHT();
break;
case T_stroke:
{
STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );

View File

@ -63,6 +63,8 @@
#include <geometry/geometry_utils.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_poly_set.h>
#include <geometry/roundrect.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_simple.h>
#include <geometry/shape_circle.h>
@ -1973,50 +1975,97 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
case SHAPE_T::RECTANGLE:
{
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
if( aShape->GetCornerRadius() > 0 )
{
ROUNDRECT rr( SHAPE_RECT( aShape->GetStart(), aShape->GetRectangleWidth(),
aShape->GetRectangleHeight() ),
aShape->GetCornerRadius() );
SHAPE_POLY_SET poly;
rr.TransformToPolygon( poly );
SHAPE_LINE_CHAIN outline = poly.Outline( 0 );
if( aShape->IsProxyItem() )
{
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
m_gal->DrawLine( pts[0], pts[1] );
m_gal->DrawLine( pts[1], pts[2] );
m_gal->DrawLine( pts[2], pts[3] );
m_gal->DrawLine( pts[3], pts[0] );
m_gal->DrawLine( pts[0], pts[2] );
m_gal->DrawLine( pts[1], pts[3] );
}
else if( outline_mode )
{
m_gal->DrawSegment( pts[0], pts[1], thickness );
m_gal->DrawSegment( pts[1], pts[2], thickness );
m_gal->DrawSegment( pts[2], pts[3], thickness );
m_gal->DrawSegment( pts[3], pts[0], thickness );
if( aShape->IsProxyItem() )
{
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
m_gal->DrawPolygon( outline );
}
else if( outline_mode )
{
m_gal->DrawSegmentChain( outline, thickness );
}
else
{
m_gal->SetIsFill( true );
m_gal->SetIsStroke( false );
if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
{
m_gal->DrawSegmentChain( outline, thickness );
}
if( isSolidFill )
{
if( thickness < 0 )
{
SHAPE_POLY_SET deflated_shape = outline;
deflated_shape.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
m_gal->DrawPolygon( deflated_shape );
}
else
{
m_gal->DrawPolygon( outline );
}
}
}
}
else
{
m_gal->SetIsFill( true );
m_gal->SetIsStroke( false );
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
if( aShape->IsProxyItem() )
{
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
m_gal->DrawLine( pts[0], pts[1] );
m_gal->DrawLine( pts[1], pts[2] );
m_gal->DrawLine( pts[2], pts[3] );
m_gal->DrawLine( pts[3], pts[0] );
m_gal->DrawLine( pts[0], pts[2] );
m_gal->DrawLine( pts[1], pts[3] );
}
else if( outline_mode )
{
m_gal->DrawSegment( pts[0], pts[1], thickness );
m_gal->DrawSegment( pts[1], pts[2], thickness );
m_gal->DrawSegment( pts[2], pts[3], thickness );
m_gal->DrawSegment( pts[3], pts[0], thickness );
}
if( isSolidFill )
else
{
SHAPE_POLY_SET poly;
poly.NewOutline();
m_gal->SetIsFill( true );
m_gal->SetIsStroke( false );
for( const VECTOR2I& pt : pts )
poly.Append( pt );
if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
{
m_gal->DrawSegment( pts[0], pts[1], thickness );
m_gal->DrawSegment( pts[1], pts[2], thickness );
m_gal->DrawSegment( pts[2], pts[3], thickness );
m_gal->DrawSegment( pts[3], pts[0], thickness );
}
if( thickness < 0 )
poly.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
if( isSolidFill )
{
SHAPE_POLY_SET poly;
poly.NewOutline();
m_gal->DrawPolygon( poly );
for( const VECTOR2I& pt : pts )
poly.Append( pt );
if( thickness < 0 )
poly.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
m_maxError );
m_gal->DrawPolygon( poly );
}
}
}

View File

@ -1180,7 +1180,7 @@ static void FillNegativeKnockout( PLOTTER *aPlotter, const BOX2I &aBbbox )
BOX2I area = aBbbox;
area.Inflate( margin );
aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_T::FILLED_SHAPE, 0 );
aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_T::FILLED_SHAPE, 0, 0 );
aPlotter->SetColor( BLACK );
}

View File

@ -29,6 +29,7 @@
#include <geometry/shape_circle.h>
#include <geometry/shape_line_chain.h> // for SHAPE_LINE_CHAIN
#include <geometry/shape_poly_set.h> // for SHAPE_POLY_SET, SHAPE_P...
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
#include <string_utils.h>
#include <macros.h>
@ -1081,19 +1082,28 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
case SHAPE_T::RECTANGLE:
{
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
int radius = aShape->GetCornerRadius();
if( m_plotter->GetPlotterType() == PLOT_FORMAT::DXF && GetDXFPlotMode() == SKETCH )
if( radius == 0 && m_plotter->GetPlotterType() == PLOT_FORMAT::DXF &&
GetDXFPlotMode() == SKETCH )
{
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
m_plotter->ThickRect( pts[0], pts[2], thickness, getMetadata() );
}
else
{
SHAPE_POLY_SET poly;
BOX2I box( aShape->GetStart(), VECTOR2I( aShape->GetEnd().x - aShape->GetStart().x,
aShape->GetEnd().y - aShape->GetStart().y ) );
box.Normalize();
SHAPE_RECT rect( box );
rect.SetRadius( radius );
SHAPE_LINE_CHAIN outline = rect.Outline();
SHAPE_POLY_SET poly;
poly.NewOutline();
for( const VECTOR2I& pt : pts )
poly.Append( pt );
for( int ii = 0; ii < outline.PointCount() - 1; ++ii )
poly.Append( outline.CPoint( ii ) );
if( margin < 0 )
poly.Inflate( margin / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, aShape->GetMaxError() );

View File

@ -25,6 +25,7 @@
#include <functional>
#include <memory>
#include <algorithm>
using namespace std::placeholders;
#include <advanced_config.h>
@ -68,6 +69,7 @@ const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
enum RECT_POINTS
{
RECT_TOP_LEFT,
RECT_RADIUS,
RECT_TOP_RIGHT,
RECT_BOT_RIGHT,
RECT_BOT_LEFT,
@ -137,6 +139,8 @@ public:
std::swap( topLeft.y, botRight.y );
aPoints.AddPoint( topLeft );
aPoints.AddPoint( VECTOR2I( botRight.x - aRectangle.GetCornerRadius(), topLeft.y ) );
aPoints.Point( RECT_RADIUS ).SetDrawCircle();
aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
aPoints.AddPoint( botRight );
aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
@ -203,6 +207,16 @@ public:
aPoints.Point( RECT_CENTER ).GetPosition() - aRectangle.GetCenter();
aRectangle.Move( moveVector );
}
else if( isModified( aEditedPoint, aPoints.Point( RECT_RADIUS ) ) )
{
int width = std::abs( botRight.x - topLeft.x );
int height = std::abs( botRight.y - topLeft.y );
int maxRadius = std::min( width, height ) / 2;
int x = aPoints.Point( RECT_RADIUS ).GetX();
x = std::clamp( x, botRight.x - maxRadius, botRight.x );
aPoints.Point( RECT_RADIUS ).SetPosition( x, topLeft.y );
aRectangle.SetCornerRadius( botRight.x - x );
}
else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
{
setTop( topLeft.y );
@ -246,6 +260,7 @@ public:
std::swap( topLeft.y, botRight.y );
aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
aPoints.Point( RECT_RADIUS ).SetPosition( botRight.x - aRectangle.GetCornerRadius(), topLeft.y );
aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );

View File

@ -80,6 +80,7 @@ set( QA_EESCHEMA_SRCS
test_incremental_netlister.cpp
test_legacy_power_symbols.cpp
test_sch_commit.cpp
test_shape_corner_radius.cpp
test_sch_group.cpp
test_pin_numbers.cpp
test_sch_netclass.cpp

View File

@ -0,0 +1,36 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/unit_test.hpp>
#include <sch_shape.h>
BOOST_AUTO_TEST_CASE( SCHShapeCornerRadius )
{
SCH_SHAPE shape( SHAPE_T::RECTANGLE, LAYER_NOTES );
shape.SetPosition( VECTOR2I( 0, 0 ) );
shape.SetEnd( VECTOR2I( 100, 100 ) );
shape.SetCornerRadius( 20 );
BOOST_CHECK_EQUAL( shape.GetCornerRadius(), 20 );
}

View File

@ -51,6 +51,7 @@ set( QA_KIMATH_SRCS
geometry/test_shape_line_chain.cpp
geometry/test_shape_line_chain_collision.cpp
geometry/test_vector_utils.cpp
geometry/test_shape_rect_corner.cpp
math/test_box2.cpp
math/test_matrix3x3.cpp

View File

@ -0,0 +1,34 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/unit_test.hpp>
#include <geometry/shape_rect.h>
BOOST_AUTO_TEST_CASE( ShapeRectCornerRadius )
{
SHAPE_RECT rect( VECTOR2I( 0, 0 ), VECTOR2I( 10, 10 ) );
rect.SetRadius( 2 );
BOOST_CHECK_EQUAL( rect.GetRadius(), 2 );
}

View File

@ -48,6 +48,7 @@ set( QA_PCBNEW_SRCS
test_prettifier.cpp
test_libeval_compiler.cpp
test_reference_image_load.cpp
test_shape_corner_radius.cpp
test_pcb_grid_helper.cpp
test_save_load.cpp
test_tracks_cleaner.cpp

View File

@ -0,0 +1,36 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/unit_test.hpp>
#include <pcb_shape.h>
BOOST_AUTO_TEST_CASE( PCBShapeCornerRadius )
{
PCB_SHAPE shape( nullptr, SHAPE_T::RECTANGLE );
shape.SetStart( VECTOR2I( 0, 0 ) );
shape.SetEnd( VECTOR2I( 1000, 1000 ) );
shape.SetCornerRadius( 200 );
BOOST_CHECK_EQUAL( shape.GetCornerRadius(), 200 );
}