48bool SegmentsShareEndpoint(
const SEG& aSegA,
const SEG& aSegB )
50 return ( aSegA.
A == aSegB.
A || aSegA.
A == aSegB.
B || aSegA.
B == aSegB.
A || aSegA.
B == aSegB.
B );
54std::pair<VECTOR2I*, VECTOR2I*> GetSharedEndpoints(
SEG& aSegA,
SEG& aSegB )
56 std::pair<VECTOR2I*, VECTOR2I*>
result = {
nullptr,
nullptr };
58 if( aSegA.
A == aSegB.
A )
62 else if( aSegA.
A == aSegB.
B )
66 else if( aSegA.
B == aSegB.
A )
70 else if( aSegA.
B == aSegB.
B )
82 const std::optional<SEG>& aSeg )
86 const bool removed = !aSeg.has_value() || aSeg->Length() == 0;
107 return _(
"Fillet Lines" );
114 return _(
"Unable to fillet the selected lines." );
116 return _(
"Some of the lines could not be filleted." );
130 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
144 auto setIfPointOnSeg =
147 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
152 aPointToSet.
x = aVecToTest.x;
153 aPointToSet.
y = aVecToTest.y;
161 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP0() )
162 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP0() ) )
168 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP1() )
169 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP1() ) )
180 tArc->SetWidth( aLineA.
GetWidth() );
181 tArc->SetLayer( aLineA.
GetLayer() );
182 tArc->SetLocked( aLineA.
IsLocked() );
199 return _(
"Chamfer Lines" );
206 return _(
"Unable to chamfer the selected lines." );
208 return _(
"Some of the lines could not be chamfered." );
225 if( !SegmentsShareEndpoint( seg_a, seg_b ) )
231 std::optional<CHAMFER_RESULT> chamfer_result =
234 if( !chamfer_result )
242 tSegment->SetStart( chamfer_result->m_chamfer.A );
243 tSegment->SetEnd( chamfer_result->m_chamfer.B );
246 tSegment->SetWidth( aLineA.
GetWidth() );
247 tSegment->SetLayer( aLineA.
GetLayer() );
248 tSegment->SetLocked( aLineA.
IsLocked() );
263 return _(
"Dogbone Corners" );
272 msg +=
_(
"Unable to add dogbone corners to the selected lines." );
274 msg +=
_(
"Some of the lines could not have dogbone corners added." );
281 msg +=
_(
"Some of the dogbone corners are too narrow to fit a "
282 "cutter of the specified radius." );
285 msg +=
_(
" Consider enabling the 'Add Slots' option." );
287 msg +=
_(
" Slots were added." );
301 wxLogTrace(
"DOGBONE",
"Skip: zero-length line(s) (A len=%f, B len=%f)",
308 wxLogTrace(
"DOGBONE",
"Skip: board outline unavailable" );
315 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
319 wxLogTrace(
"DOGBONE",
"Skip: segments do not share endpoint" );
326 wxLogTrace(
"DOGBONE",
"Skip: parallel segments" );
339 const VECTOR2I vecA = ( seg_a.
A == corner ? seg_a.
B - corner : seg_a.
A - corner );
340 const VECTOR2I vecB = ( seg_b.
A == corner ? seg_b.
B - corner : seg_b.
A - corner );
345 wxLogTrace(
"DOGBONE",
"Skip: degenerate corner (maxLen==0)" );
350 VECTOR2I bisectorOutward = vecAn + vecBn;
355 wxLogTrace(
"DOGBONE",
"Skip: bisector zero (vectors opposite)" );
361 VECTOR2I sampleDir = ( -bisectorOutward ).Resize( std::min( 1000,
m_params.DogboneRadiusIU ) );
362 VECTOR2I samplePoint = corner + sampleDir;
366 if( !oppositeInside )
368 wxLogTrace(
"DOGBONE",
"Skip: corner not inward (sample outside polygon)" );
372 std::optional<DOGBONE_RESULT> dogbone_result =
375 if( !dogbone_result )
377 wxLogTrace(
"DOGBONE",
"Skip: ComputeDogbone failed (radius=%d slots=%d)",
383 if( dogbone_result->m_small_arc_mouth )
385 wxLogTrace(
"DOGBONE",
"Info: small arc mouth (slots %s)",
386 m_params.AddSlots ?
"enabled" :
"disabled" );
395 const auto copyProps = [&](
PCB_SHAPE& aShape )
397 aShape.SetWidth( aLineA.
GetWidth() );
398 aShape.SetLayer( aLineA.
GetLayer() );
399 aShape.SetLocked( aLineA.
IsLocked() );
404 if( aSeg.Length() == 0 )
408 tSegment->SetStart( aSeg.A );
409 tSegment->SetEnd( aSeg.B );
411 copyProps( *tSegment );
415 tArc->SetArcGeometry( dogbone_result->m_arc.GetP0(), dogbone_result->m_arc.GetArcMid(),
416 dogbone_result->m_arc.GetP1() );
421 addSegment(
SEG{ dogbone_result->m_arc.GetP0(), dogbone_result->m_updated_seg_a->B } );
422 addSegment(
SEG{ dogbone_result->m_arc.GetP1(), dogbone_result->m_updated_seg_b->B } );
429 wxLogTrace(
"DOGBONE",
"Success: dogbone added at (%d,%d)", corner.
x, corner.
y );
443 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: board cast failed" );
450 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: GetBoardPolygonOutlines failed" );
455 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: outline %s", ok ?
"ready" :
"empty" );
462 return _(
"Extend Lines to Meet" );
469 return _(
"Unable to extend the selected lines to meet." );
471 return _(
"Some of the lines could not be extended to meet." );
502 const auto line_extender = [&](
const SEG& aSeg,
PCB_SHAPE& aLine )
505 if( !aSeg.
Contains( *intersection ) )
507 const int dist_start = ( *intersection - aSeg.
A ).EuclideanNorm();
508 const int dist_end = ( *intersection - aSeg.
B ).EuclideanNorm();
510 const VECTOR2I& furthest_pt = ( dist_start < dist_end ) ? aSeg.
B : aSeg.
A;
513 unsigned int edge_padding =
static_cast<unsigned>(
pcbIUScale.mmToIU( 200 ) );
517 aLine.SetStart( furthest_pt );
518 aLine.SetEnd( new_end );
522 line_extender( seg_a, aLineA );
523 line_extender( seg_b, aLineB );
531 std::unique_ptr<SHAPE_POLY_SET> poly;
537 poly = std::make_unique<SHAPE_POLY_SET>( aPcbShape.
GetPolyShape() );
544 poly = std::make_unique<SHAPE_POLY_SET>();
546 const std::vector<VECTOR2I> rect_pts = aPcbShape.
GetRectCorners();
550 for(
const VECTOR2I& pt : rect_pts )
558 poly = std::make_unique<SHAPE_POLY_SET>();
625 std::unique_ptr<PCB_SHAPE> new_poly_shape =
630 new_poly_shape->SetPolyShape( poly_set );
633 new_poly_shape->SetWidth(
m_width );
634 new_poly_shape->SetLayer(
m_layer );
637 handler.
AddNewItem( std::move( new_poly_shape ) );
644 return _(
"Merge Polygons" );
651 return _(
"Unable to merge the selected polygons." );
653 return _(
"Some of the polygons could not be merged." );
671 return _(
"Subtract Polygons" );
678 return _(
"Unable to subtract the selected polygons." );
680 return _(
"Some of the polygons could not be subtracted." );
696 working_polygons = std::move( working_copy );
703 return _(
"Intersect Polygons" );
710 return _(
"Unable to intersect the selected polygons." );
712 return _(
"Some of the polygons could not be intersected." );
736 working_polygons = std::move( working_copy );
743 return _(
"Outset Items" );
749 return _(
"Unable to outset the selected items." );
751 return _(
"Some of the items could not be outset." );
784 if( item_width.has_value() )
794 std::unique_ptr<PCB_SHAPE> new_shape =
797 new_shape->SetPolyShape( new_poly );
798 new_shape->SetLayer( layer );
799 new_shape->SetWidth( width );
810 if( aChain.ArcCount() == 0 )
812 addPolygonalChain( aChain );
816 for(
size_t si = 0; si < aChain.GetSegmentCount(); ++si )
818 const SEG seg = aChain.GetSegment( si );
823 if( aChain.IsArcSegment( si ) )
826 std::unique_ptr<PCB_SHAPE> new_shape =
828 new_shape->SetStart( seg.
A );
829 new_shape->SetEnd( seg.
B );
830 new_shape->SetLayer( layer );
831 new_shape->SetWidth( width );
836 for(
size_t ai = 0; ai < aChain.ArcCount(); ++ai )
843 std::unique_ptr<PCB_SHAPE> new_shape =
846 new_shape->SetLayer( layer );
847 new_shape->SetWidth( width );
855 for(
int oi = 0; oi < aPoly.OutlineCount(); ++oi )
857 addChain( aPoly.Outline( oi ) );
861 const auto addRect = [&](
const SHAPE_RECT& aRect )
863 std::unique_ptr<PCB_SHAPE> new_shape =
866 if( !
m_params.gridRounding.has_value() )
868 new_shape->SetPosition( aRect.GetPosition() );
869 new_shape->SetRectangleWidth( aRect.GetWidth() );
870 new_shape->SetRectangleHeight( aRect.GetHeight() );
877 new_shape->SetRectangleWidth( grid_rect.
GetWidth() );
878 new_shape->SetRectangleHeight( grid_rect.
GetHeight() );
881 new_shape->SetLayer( layer );
882 new_shape->SetWidth( width );
887 const auto addCircle = [&](
const CIRCLE& aCircle )
889 std::unique_ptr<PCB_SHAPE> new_shape =
891 new_shape->SetCenter( aCircle.Center );
892 new_shape->SetRadius( aCircle.Radius );
893 new_shape->SetLayer( layer );
894 new_shape->SetWidth( width );
899 const auto addCircleOrRect = [&](
const CIRCLE& aCircle )
903 addCircle( aCircle );
907 const VECTOR2I rVec{ aCircle.Radius, aCircle.Radius };
908 const SHAPE_RECT rect{ aCircle.Center - rVec, aCircle.Center + rVec };
913 switch( aItem.
Type() )
917 const PAD&
pad =
static_cast<const PAD&
>( aItem );
930 BOX2I box{
pad.GetPosition() - pad_size / 2, pad_size };
931 box.Inflate(
m_params.outsetDistance );
940 radius += std::min( pad_size.
x, pad_size.
y ) / 2;
959 addCircleOrRect(
circle );
984 box.Inflate(
m_params.outsetDistance );
1013 addCircleOrRect(
circle );
1033 chain.Append( seg.
A - ext + perp );
1034 chain.Append( seg.
A - ext - perp );
1035 chain.Append( seg.
B + ext - perp );
1036 chain.Append( seg.
B + ext + perp );
1037 chain.SetClosed(
true );
1059 const SHAPE_ARC inner{ arc.GetCenter(), arc.GetP0() - startNorm,
1060 arc.GetCentralAngle(), 0 };
1061 const SHAPE_ARC outer{ arc.GetCenter(), arc.GetP0() + startNorm,
1062 arc.GetCentralAngle(), 0 };
1065 chain.Append( outer );
1069 if( inner.GetRadius() > 0 )
1071 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
Information pertinent to a Pcbnew printed circuit board.
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Represent basic circle geometry with utility geometry functions.
SHAPE_POLY_SET m_boardOutline
Cached board outline polygons.
std::optional< wxString > GetStatusMessage(int aSegmentCount) const override
Get a status message to show when the routine is complete.
bool EnsureBoardOutline() const
wxString GetCommitDescription() const override
void ProcessLinePair(PCB_SHAPE &aLineA, PCB_SHAPE &aLineB) override
Perform the action on the pair of lines given.
bool m_boardOutlineCached
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.
void SetStart(const VECTOR2I &aStart)
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
std::vector< VECTOR2I > GetRectCorners() const
void SetEnd(const VECTOR2I &aEnd)
int GetRectangleHeight() 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
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) 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.
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.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
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.