43bool SegmentsShareEndpoint(
const SEG& aSegA,
const SEG& aSegB )
45 return ( aSegA.
A == aSegB.
A || aSegA.
A == aSegB.
B || aSegA.
B == aSegB.
A || aSegA.
B == aSegB.
B );
49std::pair<VECTOR2I*, VECTOR2I*> GetSharedEndpoints(
SEG& aSegA,
SEG& aSegB )
51 std::pair<VECTOR2I*, VECTOR2I*>
result = {
nullptr,
nullptr };
53 if( aSegA.
A == aSegB.
A )
57 else if( aSegA.
A == aSegB.
B )
61 else if( aSegA.
B == aSegB.
A )
65 else if( aSegA.
B == aSegB.
B )
80 const bool removed = !aSeg.has_value() || aSeg->Length() == 0;
113 constraints.insert( constraints.end(), footprint->Constraints().begin(), footprint->Constraints().end() );
120 std::vector<CONSTRAINT_MEMBER>& members = constraint->Members();
122 if( members.size() != 2 )
126 if( !( ( members[0].m_item == aCornerA.
m_item && members[1].m_item == aCornerB.
m_item )
127 || ( members[0].m_item == aCornerB.
m_item && members[1].m_item == aCornerA.
m_item ) ) )
133 members = { aCornerA, aInsertedForA };
147 return _(
"Fillet Lines" );
154 return _(
"Unable to fillet the selected lines." );
156 return _(
"Some of the lines could not be filleted." );
170 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
184 auto setIfPointOnSeg =
187 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
192 aPointToSet.
x = aVecToTest.x;
193 aPointToSet.
y = aVecToTest.y;
201 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP0() )
202 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP0() ) )
208 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP1() )
209 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP1() ) )
220 tArc->SetWidth( aLineA.
GetWidth() );
221 tArc->SetLayer( aLineA.
GetLayer() );
222 tArc->SetLocked( aLineA.
IsLocked() );
226 const KIID arcId = tArc->m_Uuid;
227 const VECTOR2I arcStart = tArc->GetStart();
241 if( !removedA && !removedB )
247 { arcId, arcForB } );
256 return _(
"Chamfer Lines" );
263 return _(
"Unable to chamfer the selected lines." );
265 return _(
"Some of the lines could not be chamfered." );
282 if( !SegmentsShareEndpoint( seg_a, seg_b ) )
290 if( !chamfer_result )
298 tSegment->SetStart( chamfer_result->m_chamfer.A );
299 tSegment->SetEnd( chamfer_result->m_chamfer.B );
302 tSegment->SetWidth( aLineA.
GetWidth() );
303 tSegment->SetLayer( aLineA.
GetLayer() );
304 tSegment->SetLocked( aLineA.
IsLocked() );
308 const KIID chamferId = tSegment->m_Uuid;
315 if( !removedA && !removedB )
328 return _(
"Dogbone Corners" );
337 msg +=
_(
"Unable to add dogbone corners to the selected lines." );
339 msg +=
_(
"Some of the lines could not have dogbone corners added." );
346 msg +=
_(
"Some of the dogbone corners are too narrow to fit a cutter of the specified radius." );
349 msg +=
_(
" Consider enabling the 'Add Slots' option." );
351 msg +=
_(
" Slots were added." );
369 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
384 if( !dogbone_result )
390 if( dogbone_result->m_small_arc_mouth )
400 const auto copyProps = [&](
PCB_SHAPE& aShape )
402 aShape.SetWidth( aLineA.
GetWidth() );
403 aShape.SetLayer( aLineA.
GetLayer() );
404 aShape.SetLocked( aLineA.
IsLocked() );
407 const auto addSegment = [&](
const SEG& aSeg ) -> std::optional<KIID>
409 if( aSeg.Length() == 0 )
413 tSegment->SetStart( aSeg.A );
414 tSegment->SetEnd( aSeg.B );
416 copyProps( *tSegment );
418 const KIID id = tSegment->m_Uuid;
424 tArc->SetArcGeometry( dogbone_result->m_arc.GetP0(), dogbone_result->m_arc.GetArcMid(),
425 dogbone_result->m_arc.GetP1() );
430 const std::optional<KIID> slotA =
431 addSegment(
SEG{ dogbone_result->m_arc.GetP0(), dogbone_result->m_updated_seg_a->B } );
432 const std::optional<KIID> slotB =
433 addSegment(
SEG{ dogbone_result->m_arc.GetP1(), dogbone_result->m_updated_seg_b->B } );
435 const KIID arcId = tArc->m_Uuid;
442 if( !removedA && !removedB )
460 return _(
"Extend Lines to Meet" );
467 return _(
"Unable to extend the selected lines to meet." );
469 return _(
"Some of the lines could not be extended to meet." );
500 const auto line_extender =
504 if( !aSeg.
Contains( *intersection ) )
506 const int dist_start = ( *intersection - aSeg.
A ).EuclideanNorm();
507 const int dist_end = ( *intersection - aSeg.
B ).EuclideanNorm();
509 const VECTOR2I& furthest_pt = ( dist_start < dist_end ) ? aSeg.
B : aSeg.
A;
512 unsigned int edge_padding =
static_cast<unsigned>(
pcbIUScale.mmToIU( 200 ) );
516 aLine.SetStart( furthest_pt );
517 aLine.SetEnd( new_end );
521 line_extender( seg_a, aLineA );
522 line_extender( seg_b, aLineB );
530 std::unique_ptr<SHAPE_POLY_SET> poly;
535 poly = std::make_unique<SHAPE_POLY_SET>( aPcbShape.
GetPolyShape() );
542 poly = std::make_unique<SHAPE_POLY_SET>();
544 const std::vector<VECTOR2I> rect_pts = aPcbShape.
GetRectCorners();
548 for(
const VECTOR2I& pt : rect_pts )
556 poly = std::make_unique<SHAPE_POLY_SET>();
622 std::unique_ptr<PCB_SHAPE> new_poly_shape = std::make_unique<PCB_SHAPE>(
GetBoard(),
SHAPE_T::POLY );
626 new_poly_shape->SetPolyShape( poly_set );
629 new_poly_shape->SetWidth(
m_width );
630 new_poly_shape->SetLayer(
m_layer );
633 handler.
AddNewItem( std::move( new_poly_shape ) );
640 return _(
"Merge Polygons" );
647 return _(
"Unable to merge the selected polygons." );
649 return _(
"Some of the polygons could not be merged." );
667 return _(
"Subtract Polygons" );
674 return _(
"Unable to subtract the selected polygons." );
676 return _(
"Some of the polygons could not be subtracted." );
692 working_polygons = std::move( working_copy );
699 return _(
"Intersect Polygons" );
706 return _(
"Unable to intersect the selected polygons." );
708 return _(
"Some of the polygons could not be intersected." );
732 working_polygons = std::move( working_copy );
739 return _(
"Outset Items" );
745 return _(
"Unable to outset the selected items." );
747 return _(
"Some of the items could not be outset." );
779 if( item_width.has_value() )
785 const auto addPolygonalChain =
792 new_shape->SetPolyShape( new_poly );
793 new_shape->SetLayer( targetLayer );
794 new_shape->SetWidth( width );
801 const auto addChain =
806 if( aChain.ArcCount() == 0 )
808 addPolygonalChain( aChain );
812 for(
size_t si = 0; si < aChain.GetSegmentCount(); ++si )
814 const SEG seg = aChain.GetSegment( si );
819 if( aChain.IsArcSegment( si ) )
823 new_shape->SetStart( seg.
A );
824 new_shape->SetEnd( seg.
B );
825 new_shape->SetLayer( targetLayer );
826 new_shape->SetWidth( width );
831 for(
size_t ai = 0; ai < aChain.ArcCount(); ++ai )
840 new_shape->SetLayer( targetLayer );
841 new_shape->SetWidth( width );
850 for(
int oi = 0; oi < aPoly.OutlineCount(); ++oi )
852 addChain( aPoly.Outline( oi ) );
861 if( !
m_params.gridRounding.has_value() )
863 new_shape->SetPosition( aRect.GetPosition() );
864 new_shape->SetRectangleWidth( aRect.GetWidth() );
865 new_shape->SetRectangleHeight( aRect.GetHeight() );
871 new_shape->SetRectangleWidth( grid_rect.
GetWidth() );
872 new_shape->SetRectangleHeight( grid_rect.
GetHeight() );
875 new_shape->SetLayer( targetLayer );
876 new_shape->SetWidth( width );
881 const auto addCircle =
882 [&](
const CIRCLE& aCircle )
885 new_shape->SetCenter( aCircle.Center );
886 new_shape->SetRadius( aCircle.Radius );
887 new_shape->SetLayer( targetLayer );
888 new_shape->SetWidth( width );
893 const auto addCircleOrRect =
894 [&](
const CIRCLE& aCircle )
898 addCircle( aCircle );
902 const VECTOR2I rVec{ aCircle.Radius, aCircle.Radius };
903 const SHAPE_RECT rect{ aCircle.Center - rVec, aCircle.Center + rVec };
908 switch( aItem.
Type() )
912 const PAD&
pad =
static_cast<const PAD&
>( aItem );
914 pad.Padstack().ForEachUniqueLayer(
918 targetLayer = aLayer;
930 BOX2I box{
pad.GetPosition() - pad_size / 2, pad_size };
931 box.Offset(
pad.GetOffset( aLayer ) );
932 box.Inflate(
m_params.outsetDistance );
934 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
943 radius +=
pad.GetRoundRectCornerRadius( aLayer );
945 radius += std::min( pad_size.
x, pad_size.
y ) / 2;
971 addCircleOrRect(
circle );
997 box.Inflate(
m_params.outsetDistance );
999 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
1011 cornerRadius = std::max( cornerRadius +
m_params.outsetDistance, 0 );
1013 if(
m_params.gridRounding.has_value() )
1016 if( cornerRadius > 0 )
1021 addChain( poly.
Outline( 0 ) );
1036 if( newRadius <= 0 )
1043 addCircleOrRect(
circle );
1070 chain.Append( seg.
A - ext + perp );
1071 chain.Append( seg.
A - ext - perp );
1072 chain.Append( seg.
B + ext - perp );
1073 chain.Append( seg.
B + ext + perp );
1074 chain.SetClosed(
true );
1096 const SHAPE_ARC inner{ arc.GetCenter(), arc.GetP0() - startNorm,
1097 arc.GetCentralAngle(), 0 };
1098 const SHAPE_ARC outer{ arc.GetCenter(), arc.GetP0() + startNorm,
1099 arc.GetCentralAngle(), 0 };
1102 chain.Append( outer );
1106 if( inner.GetRadius() > 0 )
1108 chain.Append( inner.Reversed() );
constexpr EDA_IU_SCALE pcbIUScale
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
const FOOTPRINTS & Footprints() const
const CONSTRAINTS & Constraints() const
Geometric constraints (#2329) owned by this board.
Represent basic circle geometry with utility geometry functions.
std::optional< wxString > GetStatusMessage(int aSegmentCount) const override
Get a status message to show when the routine is complete.
wxString GetCommitDescription() const override
void ProcessLinePair(PCB_SHAPE &aLineA, PCB_SHAPE &aLineB) override
Perform the action on the pair of lines given.
bool IsHorizontal() const
KICAD_T Type() const
Returns the type of object.
EDA_ANGLE GetArcAngle() const
FILL_T GetFillMode() const
int GetRectangleWidth() const
SHAPE_POLY_SET & GetPolyShape()
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
std::vector< VECTOR2I > GetRectCorners() const
int GetRectangleHeight() const
int GetCornerRadius() const
Handlers for receiving changes from the tool.
virtual void MarkItemModified(BOARD_ITEM &aItem)=0
Report that the tool has modified an item on the board.
virtual void DeleteItem(BOARD_ITEM &aItem)=0
Report that the tool has deleted an item on the board.
virtual void AddNewItem(std::unique_ptr< BOARD_ITEM > aItem)=0
Report that the tools wants to add a new item to the board.
unsigned GetFailures() const
void SplitCornerConstraint(const CONSTRAINT_MEMBER &aCornerA, const CONSTRAINT_MEMBER &aCornerB, const CONSTRAINT_MEMBER &aInsertedForA, const CONSTRAINT_MEMBER &aInsertedForB)
Hold each line to a shape just inserted at their shared corner, in place of the coincident constraint...
void AddFailure()
Mark that one of the actions failed.
unsigned GetSuccesses() const
void AddSuccess()
Mark that one of the actions succeeded.
bool ModifyLineOrDeleteIfZeroLength(PCB_SHAPE &aItem, const std::optional< SEG > &aSeg)
Helper function useful for multiple tools: modify a line or delete it if it has zero length.
BOARD_ITEM * GetBoard() const
The BOARD used when creating new shapes.
CHANGE_HANDLER & GetHandler()
Access the handler for making changes to the board.
std::optional< wxString > GetStatusMessage(int aSegmentCount) const override
Get a status message to show when the routine is complete.
wxString GetCommitDescription() const override
void ProcessLinePair(PCB_SHAPE &aLineA, PCB_SHAPE &aLineB) override
Perform the action on the pair of lines given.
const CHAMFER_PARAMS m_chamferParams
wxString GetCommitDescription() const override
std::optional< wxString > GetStatusMessage(int aSegmentCount) const override
Get a status message to show when the routine is complete.
void ProcessLinePair(PCB_SHAPE &aLineA, PCB_SHAPE &aLineB) override
Perform the action on the pair of lines given.
std::optional< wxString > GetStatusMessage(int aSegmentCount) const override
Get a status message to show when the routine is complete.
wxString GetCommitDescription() const override
void ProcessLinePair(PCB_SHAPE &aLineA, PCB_SHAPE &aLineB) override
Perform the action on the pair of lines given.
void ProcessItem(BOARD_ITEM &aItem)
const PARAMETERS m_params
wxString GetCommitDescription() const override
std::optional< wxString > GetStatusMessage() const
A geometric constraint between board items (issue #2329).
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
int GetWidth() const override
void SetEnd(const VECTOR2I &aEnd) override
void SetStart(const VECTOR2I &aStart) override
VECTOR2I GetPosition() const override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
SHAPE_POLY_SET m_workingPolygons
This can be disjoint, which will be fixed at the end.
void Finalize()
Clear up any outstanding work.
SHAPE_POLY_SET & GetWorkingPolygons()
void ProcessShape(PCB_SHAPE &aPcbShape)
virtual bool ProcessSubsequentPolygon(const SHAPE_POLY_SET &aPolygon)=0
std::optional< wxString > GetStatusMessage() const override
Get a status message to show when the routine is complete.
wxString GetCommitDescription() const override
bool ProcessSubsequentPolygon(const SHAPE_POLY_SET &aPolygon) override
bool ProcessSubsequentPolygon(const SHAPE_POLY_SET &aPolygon) override
wxString GetCommitDescription() const override
std::optional< wxString > GetStatusMessage() const override
Get a status message to show when the routine is complete.
wxString GetCommitDescription() const override
std::optional< wxString > GetStatusMessage() const override
Get a status message to show when the routine is complete.
bool ProcessSubsequentPolygon(const SHAPE_POLY_SET &aPolygon) override
A round rectangle shape, based on a rectangle and a radius.
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError) const
Get the polygonal representation of the roundrect.
bool Intersects(const SEG &aSeg) const
int Length() const
Return the length (this).
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
bool Contains(const SEG &aSeg) const
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
const VECTOR2I & GetArcMid() const
const VECTOR2I & GetP1() const
const VECTOR2I & GetP0() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
int GetWidth() const override
const VECTOR2I & GetPosition() const
const VECTOR2I GetSize() const
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
This file is part of the common library.
std::optional< CHAMFER_RESULT > ComputeChamferPoints(const SEG &aSegA, const SEG &aSegB, const CHAMFER_PARAMS &aChamferParams)
Compute the chamfer points for a given line pair and chamfer parameters.
std::optional< DOGBONE_RESULT > ComputeDogbone(const SEG &aSegA, const SEG &aSegB, int aDogboneRadius, bool aAddSlots)
Compute the dogbone geometry for a given line pair and dogbone parameters.
static constexpr EDA_ANGLE ANGLE_90
static constexpr EDA_ANGLE FULL_CIRCLE
static constexpr EDA_ANGLE ANGLE_180
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamp a vector to values that can be negated, respecting numeric limits of coordinates data type with...
static SHAPE_RECT GetRectRoundedToGridOutwards(const SHAPE_RECT &aRect, int aGridSize)
PCB_LAYER_ID
A quick note on layer IDs:
SHAPE_LINE_CHAIN ConvertToChain(const SHAPE_SEGMENT &aOval)
VECTOR2I RoundNW(const VECTOR2I &aVec, int aGridSize)
Round a vector to the nearest grid point in the NW direction.
VECTOR2I RoundSE(const VECTOR2I &aVec, int aGridSize)
Round a vector to the nearest grid point in the SE direction.
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
CONSTRAINT_ANCHOR
Which feature of a referenced board item participates in a constraint.
@ START
First endpoint of a segment or arc.
@ END
Second endpoint of a segment or arc.
@ COINCIDENT
Two points are made to coincide.
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
std::optional< VECTOR2I > OPT_VECTOR2I
Utility functions for working with shapes.
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
One participant in a constraint: a referenced board item plus the feature of that item that participa...
KIID m_item
Referenced board item, usually a PCB_SHAPE.
CONSTRAINT_ANCHOR m_anchor
Which feature of that item participates.
int m_index
Vertex ordinal; only meaningful for the VERTEX anchor.
const SHAPE_LINE_CHAIN chain
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_PAD_T
class PAD, a pad in a footprint
VECTOR2< int32_t > VECTOR2I
Supplemental functions for working with vectors and simple objects that interact with vectors.