41bool SegmentsShareEndpoint(
const SEG& aSegA,
const SEG& aSegB )
43 return ( aSegA.
A == aSegB.
A || aSegA.
A == aSegB.
B || aSegA.
B == aSegB.
A || aSegA.
B == aSegB.
B );
47std::pair<VECTOR2I*, VECTOR2I*> GetSharedEndpoints(
SEG& aSegA,
SEG& aSegB )
49 std::pair<VECTOR2I*, VECTOR2I*>
result = {
nullptr,
nullptr };
51 if( aSegA.
A == aSegB.
A )
55 else if( aSegA.
A == aSegB.
B )
59 else if( aSegA.
B == aSegB.
A )
63 else if( aSegA.
B == aSegB.
B )
78 const bool removed = !aSeg.has_value() || aSeg->Length() == 0;
99 return _(
"Fillet Lines" );
106 return _(
"Unable to fillet the selected lines." );
108 return _(
"Some of the lines could not be filleted." );
122 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
136 auto setIfPointOnSeg =
139 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
144 aPointToSet.
x = aVecToTest.x;
145 aPointToSet.
y = aVecToTest.y;
153 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP0() )
154 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP0() ) )
160 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP1() )
161 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP1() ) )
172 tArc->SetWidth( aLineA.
GetWidth() );
173 tArc->SetLayer( aLineA.
GetLayer() );
174 tArc->SetLocked( aLineA.
IsLocked() );
192 return _(
"Chamfer Lines" );
199 return _(
"Unable to chamfer the selected lines." );
201 return _(
"Some of the lines could not be chamfered." );
218 if( !SegmentsShareEndpoint( seg_a, seg_b ) )
226 if( !chamfer_result )
234 tSegment->SetStart( chamfer_result->m_chamfer.A );
235 tSegment->SetEnd( chamfer_result->m_chamfer.B );
238 tSegment->SetWidth( aLineA.
GetWidth() );
239 tSegment->SetLayer( aLineA.
GetLayer() );
240 tSegment->SetLocked( aLineA.
IsLocked() );
255 return _(
"Dogbone Corners" );
264 msg +=
_(
"Unable to add dogbone corners to the selected lines." );
266 msg +=
_(
"Some of the lines could not have dogbone corners added." );
273 msg +=
_(
"Some of the dogbone corners are too narrow to fit a cutter of the specified radius." );
276 msg +=
_(
" Consider enabling the 'Add Slots' option." );
278 msg +=
_(
" Slots were added." );
296 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
311 if( !dogbone_result )
317 if( dogbone_result->m_small_arc_mouth )
327 const auto copyProps = [&](
PCB_SHAPE& aShape )
329 aShape.SetWidth( aLineA.
GetWidth() );
330 aShape.SetLayer( aLineA.
GetLayer() );
331 aShape.SetLocked( aLineA.
IsLocked() );
335 [&](
const SEG& aSeg )
337 if( aSeg.Length() == 0 )
341 tSegment->SetStart( aSeg.A );
342 tSegment->SetEnd( aSeg.B );
344 copyProps( *tSegment );
348 tArc->SetArcGeometry( dogbone_result->m_arc.GetP0(), dogbone_result->m_arc.GetArcMid(),
349 dogbone_result->m_arc.GetP1() );
354 addSegment(
SEG{ dogbone_result->m_arc.GetP0(), dogbone_result->m_updated_seg_a->B } );
355 addSegment(
SEG{ dogbone_result->m_arc.GetP1(), dogbone_result->m_updated_seg_b->B } );
368 return _(
"Extend Lines to Meet" );
375 return _(
"Unable to extend the selected lines to meet." );
377 return _(
"Some of the lines could not be extended to meet." );
408 const auto line_extender =
412 if( !aSeg.
Contains( *intersection ) )
414 const int dist_start = ( *intersection - aSeg.
A ).EuclideanNorm();
415 const int dist_end = ( *intersection - aSeg.
B ).EuclideanNorm();
417 const VECTOR2I& furthest_pt = ( dist_start < dist_end ) ? aSeg.
B : aSeg.
A;
420 unsigned int edge_padding =
static_cast<unsigned>(
pcbIUScale.mmToIU( 200 ) );
424 aLine.SetStart( furthest_pt );
425 aLine.SetEnd( new_end );
429 line_extender( seg_a, aLineA );
430 line_extender( seg_b, aLineB );
438 std::unique_ptr<SHAPE_POLY_SET> poly;
443 poly = std::make_unique<SHAPE_POLY_SET>( aPcbShape.
GetPolyShape() );
450 poly = std::make_unique<SHAPE_POLY_SET>();
452 const std::vector<VECTOR2I> rect_pts = aPcbShape.
GetRectCorners();
456 for(
const VECTOR2I& pt : rect_pts )
464 poly = std::make_unique<SHAPE_POLY_SET>();
530 std::unique_ptr<PCB_SHAPE> new_poly_shape = std::make_unique<PCB_SHAPE>(
GetBoard(),
SHAPE_T::POLY );
534 new_poly_shape->SetPolyShape( poly_set );
537 new_poly_shape->SetWidth(
m_width );
538 new_poly_shape->SetLayer(
m_layer );
541 handler.
AddNewItem( std::move( new_poly_shape ) );
548 return _(
"Merge Polygons" );
555 return _(
"Unable to merge the selected polygons." );
557 return _(
"Some of the polygons could not be merged." );
575 return _(
"Subtract Polygons" );
582 return _(
"Unable to subtract the selected polygons." );
584 return _(
"Some of the polygons could not be subtracted." );
600 working_polygons = std::move( working_copy );
607 return _(
"Intersect Polygons" );
614 return _(
"Unable to intersect the selected polygons." );
616 return _(
"Some of the polygons could not be intersected." );
640 working_polygons = std::move( working_copy );
647 return _(
"Outset Items" );
653 return _(
"Unable to outset the selected items." );
655 return _(
"Some of the items could not be outset." );
687 if( item_width.has_value() )
693 const auto addPolygonalChain =
700 new_shape->SetPolyShape( new_poly );
701 new_shape->SetLayer( layer );
702 new_shape->SetWidth( width );
709 const auto addChain =
714 if( aChain.ArcCount() == 0 )
716 addPolygonalChain( aChain );
720 for(
size_t si = 0; si < aChain.GetSegmentCount(); ++si )
722 const SEG seg = aChain.GetSegment( si );
727 if( aChain.IsArcSegment( si ) )
731 new_shape->SetStart( seg.
A );
732 new_shape->SetEnd( seg.
B );
733 new_shape->SetLayer( layer );
734 new_shape->SetWidth( width );
739 for(
size_t ai = 0; ai < aChain.ArcCount(); ++ai )
748 new_shape->SetLayer( layer );
749 new_shape->SetWidth( width );
758 for(
int oi = 0; oi < aPoly.OutlineCount(); ++oi )
760 addChain( aPoly.Outline( oi ) );
769 if( !
m_params.gridRounding.has_value() )
771 new_shape->SetPosition( aRect.GetPosition() );
772 new_shape->SetRectangleWidth( aRect.GetWidth() );
773 new_shape->SetRectangleHeight( aRect.GetHeight() );
779 new_shape->SetRectangleWidth( grid_rect.
GetWidth() );
780 new_shape->SetRectangleHeight( grid_rect.
GetHeight() );
783 new_shape->SetLayer( layer );
784 new_shape->SetWidth( width );
789 const auto addCircle =
790 [&](
const CIRCLE& aCircle )
793 new_shape->SetCenter( aCircle.Center );
794 new_shape->SetRadius( aCircle.Radius );
795 new_shape->SetLayer( layer );
796 new_shape->SetWidth( width );
801 const auto addCircleOrRect =
802 [&](
const CIRCLE& aCircle )
806 addCircle( aCircle );
810 const VECTOR2I rVec{ aCircle.Radius, aCircle.Radius };
811 const SHAPE_RECT rect{ aCircle.Center - rVec, aCircle.Center + rVec };
816 switch( aItem.
Type() )
820 const PAD&
pad =
static_cast<const PAD&
>( aItem );
833 BOX2I box{
pad.GetPosition() - pad_size / 2, pad_size };
834 box.Inflate(
m_params.outsetDistance );
836 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
847 radius += std::min( pad_size.
x, pad_size.
y ) / 2;
873 addCircleOrRect(
circle );
898 box.Inflate(
m_params.outsetDistance );
900 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
912 cornerRadius = std::max( cornerRadius +
m_params.outsetDistance, 0 );
914 if(
m_params.gridRounding.has_value() )
917 if( cornerRadius > 0 )
944 addCircleOrRect(
circle );
971 chain.Append( seg.
A - ext + perp );
972 chain.Append( seg.
A - ext - perp );
973 chain.Append( seg.
B + ext - perp );
974 chain.Append( seg.
B + ext + perp );
975 chain.SetClosed(
true );
997 const SHAPE_ARC inner{ arc.GetCenter(), arc.GetP0() - startNorm,
998 arc.GetCentralAngle(), 0 };
999 const SHAPE_ARC outer{ arc.GetCenter(), arc.GetP0() + startNorm,
1000 arc.GetCentralAngle(), 0 };
1003 chain.Append( outer );
1007 if( inner.GetRadius() > 0 )
1009 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...
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
bool IsLocked() const override
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
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 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
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
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)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
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()
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
std::optional< VECTOR2I > OPT_VECTOR2I
Utility functions for working with shapes.
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.