ADDED ability to specify where pad number (& net name) go on custom shaped pads.

Fixes https://gitlab.com/kicad/code/kicad/issues/6627
This commit is contained in:
Jeff Young 2022-09-14 11:34:10 +01:00
parent ecd99633d8
commit 281b9d405a
14 changed files with 205 additions and 35 deletions

View File

@ -55,6 +55,9 @@ EDA_SHAPE::~EDA_SHAPE()
wxString EDA_SHAPE::ShowShape() const
{
if( IsAnnotationProxy() )
return _( "Number Box" );
switch( m_shape )
{
case SHAPE_T::SEGMENT: return _( "Line" );
@ -599,7 +602,10 @@ void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
break;
case SHAPE_T::RECT:
aList.emplace_back( shape, _( "Rectangle" ) );
if( IsAnnotationProxy() )
aList.emplace_back( shape, _( "Pad Number Box" ) );
else
aList.emplace_back( shape, _( "Rectangle" ) );
msg = MessageTextFromValue( units, std::abs( GetEnd().x - GetStart().x ) );
aList.emplace_back( _( "Width" ), msg );
@ -773,7 +779,7 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
case SHAPE_T::RECT:
if( IsFilled() ) // Filled rect hit-test
if( IsAnnotationProxy() || IsFilled() ) // Filled rect hit-test
{
SHAPE_POLY_SET poly;
poly.NewOutline();
@ -783,7 +789,7 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
return poly.Collide( aPosition, maxdist );
}
else // Open rect hit-test
else // Open rect hit-test
{
std::vector<VECTOR2I> pts = GetRectCorners();
@ -1094,7 +1100,7 @@ std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineCh
{
std::vector<VECTOR2I> pts = GetRectCorners();
if( IsFilled() && !aEdgeOnly )
if( ( IsFilled() || IsAnnotationProxy() ) && !aEdgeOnly )
effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
if( width > 0 || !IsFilled() || aEdgeOnly )
@ -1517,7 +1523,7 @@ void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuf
{
std::vector<VECTOR2I> pts = GetRectCorners();
if( IsFilled() )
if( IsFilled() || IsAnnotationProxy() )
{
aCornerBuffer.NewOutline();

View File

@ -130,6 +130,7 @@ generator
grid_origin
group
gr_arc
gr_bbox
gr_circle
gr_curve
gr_line

View File

@ -85,6 +85,9 @@ public:
wxString SHAPE_T_asString() const;
bool IsAnnotationProxy() const { return m_annotationProxy; }
void SetIsAnnotationProxy( bool aIsProxy = true ) { m_annotationProxy = aIsProxy; }
bool IsFilled() const
{
return GetFillMode() != FILL_T::NO_FILL;
@ -376,6 +379,7 @@ protected:
SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape
int m_editState;
bool m_annotationProxy; // A shape storing the position of an annotation
};
#endif // EDA_SHAPE_H

View File

@ -208,6 +208,8 @@ public:
/// Compound assignment operator
VECTOR2<T>& operator*=( const VECTOR2<T>& aVector );
VECTOR2<T>& operator*=( const T& aScalar );
/// Compound assignment operator
VECTOR2<T>& operator+=( const T& aScalar );
@ -336,6 +338,15 @@ VECTOR2<T>& VECTOR2<T>::operator*=( const VECTOR2<T>& aVector )
}
template <class T>
VECTOR2<T>& VECTOR2<T>::operator*=( const T& aScalar )
{
x *= aScalar;
y *= aScalar;
return *this;
}
template <class T>
VECTOR2<T>& VECTOR2<T>::operator+=( const T& aScalar )
{

View File

@ -158,12 +158,34 @@ bool DIALOG_PAD_PRIMITIVES_PROPERTIES::TransferDataToWindow()
m_filledCtrl->Show( true );
break;
case SHAPE_T::RECT:
if( m_shape->IsAnnotationProxy() )
SetTitle( _( "Number Box" ) );
else
SetTitle( _( "Rectangle" ) );
m_startX.SetValue( m_shape->GetStart().x );
m_startY.SetValue( m_shape->GetStart().y );
m_endX.SetValue( m_shape->GetEnd().x );
m_endY.SetValue( m_shape->GetEnd().y );
m_ctrl1X.Show( false, true );
m_ctrl1Y.Show( false, true );
m_ctrl2X.Show( false, true );
m_ctrl2Y.Show( false, true );
m_staticTextPosCtrl1->Show( false );
m_staticTextPosCtrl1->SetSize( 0, 0 );
m_staticTextPosCtrl2->Show( false );
m_staticTextPosCtrl2->SetSize( 0, 0 );
m_radius.Show( false );
m_filledCtrl->Show( false );
break;
case SHAPE_T::POLY:
// polygon has a specific dialog editor. So nothing here
break;
default:
SetTitle( "Unknown basic shape" );
SetTitle( "Unknown Basic Shape" );
break;
}
@ -190,6 +212,7 @@ bool DIALOG_PAD_PRIMITIVES_PROPERTIES::TransferDataFromWindow()
switch( m_shape->GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::RECT:
m_shape->SetStart( VECTOR2I( m_startX.GetValue(), m_startY.GetValue() ) );
m_shape->SetEnd( VECTOR2I( m_endX.GetValue(), m_endY.GetValue() ) );
break;
@ -217,7 +240,6 @@ bool DIALOG_PAD_PRIMITIVES_PROPERTIES::TransferDataFromWindow()
break;
default:
SetTitle( "Unknown basic shape" );
break;
}

View File

@ -706,32 +706,32 @@ void DIALOG_PAD_PROPERTIES::displayPrimitivesList()
{
case SHAPE_T::SEGMENT:
bs_info[0] = _( "Segment" );
bs_info[1] = _( "from" ) + wxS( " " )+ formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "to" ) + wxS( " " )+ formatCoord( m_units, primitive->GetEnd() );
bs_info[1] = _( "from" ) + wxS( " " ) + formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "to" ) + wxS( " " ) + formatCoord( m_units, primitive->GetEnd() );
break;
case SHAPE_T::BEZIER:
bs_info[0] = _( "Bezier" );
bs_info[1] = _( "from" ) + wxS( " " )+ formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "to" ) + wxS( " " )+ formatCoord( m_units, primitive->GetEnd() );
bs_info[1] = _( "from" ) + wxS( " " ) + formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "to" ) + wxS( " " ) + formatCoord( m_units, primitive->GetEnd() );
break;
case SHAPE_T::ARC:
bs_info[0] = _( "Arc" );
bs_info[1] = _( "center" ) + wxS( " " )+ formatCoord( m_units, primitive->GetCenter() );
bs_info[2] = _( "start" ) + wxS( " " )+ formatCoord( m_units, primitive->GetStart() );
bs_info[3] = _( "angle" ) + wxS( " " )+ EDA_UNIT_UTILS::FormatAngle( primitive->GetArcAngle() );
bs_info[1] = _( "center" ) + wxS( " " ) + formatCoord( m_units, primitive->GetCenter() );
bs_info[2] = _( "start" ) + wxS( " " ) + formatCoord( m_units, primitive->GetStart() );
bs_info[3] = _( "angle" ) + wxS( " " ) + EDA_UNIT_UTILS::FormatAngle( primitive->GetArcAngle() );
break;
case SHAPE_T::CIRCLE:
if( primitive->GetWidth() )
bs_info[0] = _( "ring" );
bs_info[0] = _( "Ring" );
else
bs_info[0] = _( "circle" );
bs_info[0] = _( "Circle" );
bs_info[1] = formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "radius" ) + wxS( " " )+ MessageTextFromValue( m_units,
primitive->GetRadius() );
bs_info[1] = _( "at" ) + wxS( " " ) + formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "radius" ) + wxS( " " ) + MessageTextFromValue( m_units,
primitive->GetRadius() );
break;
case SHAPE_T::POLY:
@ -740,6 +740,16 @@ void DIALOG_PAD_PROPERTIES::displayPrimitivesList()
primitive->GetPolyShape().Outline( 0 ).PointCount() );
break;
case SHAPE_T::RECT:
if( primitive->IsAnnotationProxy() )
bs_info[0] = _( "Number box" );
else
bs_info[0] = _( "Rectangle" );
bs_info[1] = _( "from" ) + wxS( " " ) + formatCoord( m_units, primitive->GetStart() );
bs_info[2] = _( "to" ) + wxS( " " ) + formatCoord( m_units, primitive->GetEnd() );
break;
default:
bs_info[0] = _( "Unknown primitive" );
break;
@ -2164,7 +2174,8 @@ void DIALOG_PAD_PROPERTIES::onAddPrimitive( wxCommandEvent& event )
_( "Arc" ),
_( "Bezier" ),
_( "Ring/Circle" ),
_( "Polygon" )
_( "Polygon" ),
_( "Number box" ),
};
int type = wxGetSingleChoiceIndex( _( "Shape type:" ), _( "Add Primitive" ),
@ -2175,10 +2186,14 @@ void DIALOG_PAD_PROPERTIES::onAddPrimitive( wxCommandEvent& event )
return;
SHAPE_T listtype[] = { SHAPE_T::SEGMENT, SHAPE_T::ARC, SHAPE_T::BEZIER, SHAPE_T::CIRCLE,
SHAPE_T::POLY };
SHAPE_T::POLY, SHAPE_T::RECT };
PCB_SHAPE* primitive = new PCB_SHAPE();
primitive->SetShape( listtype[type] );
if( type == arrayDim( shapelist ) - 1 )
primitive->SetIsAnnotationProxy();
primitive->SetStroke( STROKE_PARAMS( m_board->GetDesignSettings().GetLineThickness( F_Cu ),
PLOT_DASH_TYPE::SOLID ) );
primitive->SetFilled( true );

View File

@ -292,6 +292,11 @@ public:
void AddPrimitiveCurve( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCtrl1,
const VECTOR2I& aCtrl2, int aThickness );
/**
* Has meaning only for custom shape pads. Allows one to specify the box in which to place
* the pad number and/or net name (if they are being displayed).
*/
void AddPrimitiveAnnotationBox( const VECTOR2I& aStart, const VECTOR2I& aEnd );
bool GetBestAnchorPosition( VECTOR2I& aPos );
@ -723,7 +728,7 @@ private:
* Editing definitions of primitives for custom pad shapes. In local coordinates relative
* to m_Pos (NOT shapePos) at orient 0.
*/
std::vector<std::shared_ptr<PCB_SHAPE>> m_editPrimitives;
std::vector<std::shared_ptr<PCB_SHAPE>> m_editPrimitives;
// Must be set to true to force rebuild shapes to draw (after geometry change for instance)
mutable bool m_shapesDirty;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2019 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
* modify it under the terms of the GNU General Public License
@ -136,7 +136,7 @@ void PAD::AddPrimitiveCircle( const VECTOR2I& aCenter, int aRadius, int aThickne
void PAD::AddPrimitiveRect( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aThickness,
bool aFilled)
{
PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T:: RECT );
PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::RECT );
item->SetFilled( aFilled );
item->SetStart( aStart );
item->SetEnd( aEnd );
@ -147,6 +147,20 @@ void PAD::AddPrimitiveRect( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aT
}
void PAD::AddPrimitiveAnnotationBox( const VECTOR2I& aStart, const VECTOR2I& aEnd )
{
PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::RECT );
item->SetIsAnnotationProxy();
item->SetFilled( false );
item->SetStart( aStart );
item->SetEnd( aEnd );
item->SetStroke( STROKE_PARAMS( 1, PLOT_DASH_TYPE::SOLID ) );
item->SetParent( this );
m_editPrimitives.emplace_back( item );
SetDirty();
}
void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
{
// clear old list
@ -195,8 +209,11 @@ void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
{
primitive->TransformShapeWithClearanceToPolygon( polyset, UNDEFINED_LAYER, 0, aError,
aErrorLoc );
if( !primitive->IsAnnotationProxy() )
{
primitive->TransformShapeWithClearanceToPolygon( polyset, UNDEFINED_LAYER, 0, aError,
aErrorLoc );
}
}
polyset.Simplify( SHAPE_POLY_SET::PM_FAST );

View File

@ -1017,6 +1017,50 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
VECTOR2D position = padBBox.Centre();
VECTOR2D padsize = VECTOR2D( padBBox.GetSize() );
if( aPad->GetFlags() & ENTERED )
{
FOOTPRINT* fp = static_cast<FOOTPRINT*>( aPad->GetParentFootprint() );
// Find the number box
for( const BOARD_ITEM* aItem : fp->GraphicalItems() )
{
if( aItem->Type() == PCB_FP_SHAPE_T )
{
const FP_SHAPE* shape = static_cast<const FP_SHAPE*>( aItem );
if( shape->IsAnnotationProxy() )
{
position = shape->GetCenter();
padsize = shape->GetBotRight() - shape->GetTopLeft();
// We normally draw a bit outside the pad, but this will be somewhat
// unexpected when the user has drawn a box.
padsize *= 0.9;
break;
}
}
}
}
else if( aPad->GetShape() == PAD_SHAPE::CUSTOM )
{
// See if we have a number box
for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
{
if( primitive->IsAnnotationProxy() )
{
position = aPad->GetPosition() + primitive->GetCenter();
padsize = primitive->GetBotRight() - primitive->GetTopLeft();
// We normally draw a bit outside the pad, but this will be somewhat
// unexpected when the user has drawn a box.
padsize *= 0.9;
break;
}
}
}
if( aPad->GetShape() != PAD_SHAPE::CUSTOM )
{
// Don't allow a 45° rotation to bloat a pad's bounding box unnecessarily
@ -1486,7 +1530,16 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
{
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
if( outline_mode )
if( aShape->IsAnnotationProxy() )
{
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 );
m_gal->DrawSegment( pts[0], pts[2], thickness );
m_gal->DrawSegment( pts[1], pts[3], thickness );
}
else if( outline_mode )
{
m_gal->DrawSegment( pts[0], pts[1], thickness );
m_gal->DrawSegment( pts[1], pts[2], thickness );

View File

@ -2462,7 +2462,8 @@ void PCB_PARSER::parseNETCLASS()
PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
{
wxCHECK_MSG( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
CurTok() == T_gr_rect || CurTok() == T_gr_line || CurTok() == T_gr_poly, nullptr,
CurTok() == T_gr_rect || CurTok() == T_gr_bbox || CurTok() == T_gr_line ||
CurTok() == T_gr_poly, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
T token;
@ -2603,6 +2604,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
NeedRIGHT();
break;
case T_gr_bbox:
case T_gr_rect:
shape->SetShape( SHAPE_T::RECT );
token = NextTok();
@ -2704,7 +2706,7 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
}
default:
Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, or gr_rect" );
Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, gr_rect or gr_bbox" );
}
bool foundFill = false;
@ -4980,6 +4982,10 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
dummyShape->GetWidth(), dummyShape->IsFilled() );
break;
case T_gr_bbox:
dummyShape = parsePCB_SHAPE();
pad->AddPrimitiveAnnotationBox( dummyShape->GetStart(), dummyShape->GetEnd() );
break;
case T_gr_poly:
dummyShape = parsePCB_SHAPE();
@ -4995,7 +5001,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
break;
default:
Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect or gr_poly" );
Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect, gr_bbox or gr_poly" );
break;
}

View File

@ -1764,9 +1764,18 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
break;
case SHAPE_T::RECT:
m_out->Print( nested_level, "(gr_rect (start %s) (end %s)",
FormatInternalUnits( primitive->GetStart() ).c_str(),
FormatInternalUnits( primitive->GetEnd() ).c_str() );
if( primitive->IsAnnotationProxy() )
{
m_out->Print( nested_level, "(gr_bbox (start %s) (end %s)",
FormatInternalUnits( primitive->GetStart() ).c_str(),
FormatInternalUnits( primitive->GetEnd() ).c_str() );
}
else
{
m_out->Print( nested_level, "(gr_rect (start %s) (end %s)",
FormatInternalUnits( primitive->GetStart() ).c_str(),
FormatInternalUnits( primitive->GetEnd() ).c_str() );
}
break;
case SHAPE_T::ARC:

View File

@ -126,7 +126,8 @@ class SHAPE_LINE_CHAIN;
//#define SEXPR_BOARD_FILE_VERSION 20220609 // Add teardrop keywords to identify teardrop zones
//#define SEXPR_BOARD_FILE_VERSION 20220621 // Add Image support
//#define SEXPR_BOARD_FILE_VERSION 20220815 // Add allow-soldermask-bridges-in-FPs flag
#define SEXPR_BOARD_FILE_VERSION 20220818 // First-class storage for net-ties
//#define SEXPR_BOARD_FILE_VERSION 20220818 // First-class storage for net-ties
#define SEXPR_BOARD_FILE_VERSION 20220914 // Number boxes for custom-shape pads
#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

@ -644,6 +644,7 @@ PCB_LAYER_ID PAD_TOOL::explodePad( PAD* aPad )
FP_SHAPE* shape = new FP_SHAPE( board()->GetFirstFootprint() );
shape->SetShape( primitive->GetShape() );
shape->SetIsAnnotationProxy( primitive->IsAnnotationProxy());
shape->SetFilled( primitive->IsFilled() );
shape->SetStroke( primitive->GetStroke() );
@ -687,6 +688,7 @@ PCB_LAYER_ID PAD_TOOL::explodePad( PAD* aPad )
aPad->SetShape( aPad->GetAnchorPadShape() );
aPad->DeletePrimitivesList();
aPad->SetFlags( ENTERED );
m_editPad = aPad->m_Uuid;
}
@ -721,6 +723,9 @@ void PAD_TOOL::recombinePad( PAD* aPad )
if( shape->GetLayer() != aLayer )
continue;
if( shape->IsAnnotationProxy() ) // Pad number (and net name) box
return (FP_SHAPE*) item;
SHAPE_POLY_SET drawPoly;
shape->TransformShapeWithClearanceToPolygon( drawPoly, aLayer, 0, maxError,
ERROR_INSIDE );
@ -821,12 +826,15 @@ void PAD_TOOL::recombinePad( PAD* aPad )
pcbShape->Move( - aPad->GetPosition() );
pcbShape->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
pcbShape->SetIsAnnotationProxy( fpShape->IsAnnotationProxy());
aPad->AddPrimitive( pcbShape );
fpShape->SetFlags( STRUCT_DELETED );
commit.Remove( fpShape );
}
aPad->ClearFlags( ENTERED );
commit.Push( _( "Recombine pads" ) );
}

View File

@ -46,6 +46,7 @@ using namespace std::placeholders;
#include <pcb_textbox.h>
#include <pad.h>
#include <zone.h>
#include <footprint.h>
#include <connectivity/connectivity_data.h>
#include <progress_reporter.h>
@ -1269,10 +1270,21 @@ void PCB_POINT_EDITOR::updateItem() const
break;
}
// Update relative coordinates for footprint shapes
if( FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( item ) )
{
// Update relative coordinates for footprint shapes
fpShape->SetLocalCoord();
if( fpShape->IsAnnotationProxy() )
{
for( PAD* pad : fpShape->GetParentFootprint()->Pads() )
{
if( pad->GetFlags() & ENTERED )
view()->Update( pad );
}
}
}
// Nuke outline font render caches
if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
textBox->ClearRenderCache();