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 )
85 const bool removed = !aSeg.has_value() || aSeg->Length() == 0;
106 return _(
"Fillet Lines" );
113 return _(
"Unable to fillet the selected lines." );
115 return _(
"Some of the lines could not be filleted." );
129 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
143 auto setIfPointOnSeg =
146 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
151 aPointToSet.
x = aVecToTest.x;
152 aPointToSet.
y = aVecToTest.y;
160 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP0() )
161 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP0() ) )
167 if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.
GetP1() )
168 && !setIfPointOnSeg( t2newPoint, seg_b, sArc.
GetP1() ) )
179 tArc->SetWidth( aLineA.
GetWidth() );
180 tArc->SetLayer( aLineA.
GetLayer() );
181 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 ) )
233 if( !chamfer_result )
241 tSegment->SetStart( chamfer_result->m_chamfer.A );
242 tSegment->SetEnd( chamfer_result->m_chamfer.B );
245 tSegment->SetWidth( aLineA.
GetWidth() );
246 tSegment->SetLayer( aLineA.
GetLayer() );
247 tSegment->SetLocked( aLineA.
IsLocked() );
262 return _(
"Dogbone Corners" );
271 msg +=
_(
"Unable to add dogbone corners to the selected lines." );
273 msg +=
_(
"Some of the lines could not have dogbone corners added." );
280 msg +=
_(
"Some of the dogbone corners are too narrow to fit a cutter of the specified radius." );
283 msg +=
_(
" Consider enabling the 'Add Slots' option." );
285 msg +=
_(
" Slots were added." );
299 wxLogTrace(
"DOGBONE",
"Skip: zero-length line(s) (A len=%f, B len=%f)",
306 wxLogTrace(
"DOGBONE",
"Skip: board outline unavailable" );
313 auto [a_pt, b_pt] = GetSharedEndpoints( seg_a, seg_b );
317 wxLogTrace(
"DOGBONE",
"Skip: segments do not share endpoint" );
324 wxLogTrace(
"DOGBONE",
"Skip: parallel segments" );
337 const VECTOR2I vecA = ( seg_a.
A == corner ? seg_a.
B - corner : seg_a.
A - corner );
338 const VECTOR2I vecB = ( seg_b.
A == corner ? seg_b.
B - corner : seg_b.
A - corner );
344 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)" );
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)",
m_params.AddSlots ?
"enabled" :
"disabled" );
394 const auto copyProps = [&](
PCB_SHAPE& aShape )
396 aShape.SetWidth( aLineA.
GetWidth() );
397 aShape.SetLayer( aLineA.
GetLayer() );
398 aShape.SetLocked( aLineA.
IsLocked() );
402 [&](
const SEG& aSeg )
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 );
444 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: board cast failed" );
451 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: GetBoardPolygonOutlines failed" );
456 wxLogTrace(
"DOGBONE",
"EnsureBoardOutline: outline %s", ok ?
"ready" :
"empty" );
463 return _(
"Extend Lines to Meet" );
470 return _(
"Unable to extend the selected lines to meet." );
472 return _(
"Some of the lines could not be extended to meet." );
503 const auto line_extender =
507 if( !aSeg.
Contains( *intersection ) )
509 const int dist_start = ( *intersection - aSeg.
A ).EuclideanNorm();
510 const int dist_end = ( *intersection - aSeg.
B ).EuclideanNorm();
512 const VECTOR2I& furthest_pt = ( dist_start < dist_end ) ? aSeg.
B : aSeg.
A;
515 unsigned int edge_padding =
static_cast<unsigned>(
pcbIUScale.mmToIU( 200 ) );
519 aLine.SetStart( furthest_pt );
520 aLine.SetEnd( new_end );
524 line_extender( seg_a, aLineA );
525 line_extender( seg_b, aLineB );
533 std::unique_ptr<SHAPE_POLY_SET> poly;
538 poly = std::make_unique<SHAPE_POLY_SET>( aPcbShape.
GetPolyShape() );
545 poly = std::make_unique<SHAPE_POLY_SET>();
547 const std::vector<VECTOR2I> rect_pts = aPcbShape.
GetRectCorners();
551 for(
const VECTOR2I& pt : rect_pts )
559 poly = std::make_unique<SHAPE_POLY_SET>();
625 std::unique_ptr<PCB_SHAPE> new_poly_shape = std::make_unique<PCB_SHAPE>(
GetBoard(),
SHAPE_T::POLY );
629 new_poly_shape->SetPolyShape( poly_set );
632 new_poly_shape->SetWidth(
m_width );
633 new_poly_shape->SetLayer(
m_layer );
636 handler.
AddNewItem( std::move( new_poly_shape ) );
643 return _(
"Merge Polygons" );
650 return _(
"Unable to merge the selected polygons." );
652 return _(
"Some of the polygons could not be merged." );
670 return _(
"Subtract Polygons" );
677 return _(
"Unable to subtract the selected polygons." );
679 return _(
"Some of the polygons could not be subtracted." );
695 working_polygons = std::move( working_copy );
702 return _(
"Intersect Polygons" );
709 return _(
"Unable to intersect the selected polygons." );
711 return _(
"Some of the polygons could not be intersected." );
735 working_polygons = std::move( working_copy );
742 return _(
"Outset Items" );
748 return _(
"Unable to outset the selected items." );
750 return _(
"Some of the items could not be outset." );
782 if( item_width.has_value() )
788 const auto addPolygonalChain =
795 new_shape->SetPolyShape( new_poly );
796 new_shape->SetLayer( layer );
797 new_shape->SetWidth( width );
804 const auto addChain =
809 if( aChain.ArcCount() == 0 )
811 addPolygonalChain( aChain );
815 for(
size_t si = 0; si < aChain.GetSegmentCount(); ++si )
817 const SEG seg = aChain.GetSegment( si );
822 if( aChain.IsArcSegment( si ) )
826 new_shape->SetStart( seg.
A );
827 new_shape->SetEnd( seg.
B );
828 new_shape->SetLayer( layer );
829 new_shape->SetWidth( width );
834 for(
size_t ai = 0; ai < aChain.ArcCount(); ++ai )
843 new_shape->SetLayer( layer );
844 new_shape->SetWidth( width );
853 for(
int oi = 0; oi < aPoly.OutlineCount(); ++oi )
855 addChain( aPoly.Outline( oi ) );
864 if( !
m_params.gridRounding.has_value() )
866 new_shape->SetPosition( aRect.GetPosition() );
867 new_shape->SetRectangleWidth( aRect.GetWidth() );
868 new_shape->SetRectangleHeight( aRect.GetHeight() );
874 new_shape->SetRectangleWidth( grid_rect.
GetWidth() );
875 new_shape->SetRectangleHeight( grid_rect.
GetHeight() );
878 new_shape->SetLayer( layer );
879 new_shape->SetWidth( width );
884 const auto addCircle =
885 [&](
const CIRCLE& aCircle )
888 new_shape->SetCenter( aCircle.Center );
889 new_shape->SetRadius( aCircle.Radius );
890 new_shape->SetLayer( layer );
891 new_shape->SetWidth( width );
896 const auto addCircleOrRect =
897 [&](
const CIRCLE& aCircle )
901 addCircle( aCircle );
905 const VECTOR2I rVec{ aCircle.Radius, aCircle.Radius };
906 const SHAPE_RECT rect{ aCircle.Center - rVec, aCircle.Center + rVec };
911 switch( aItem.
Type() )
915 const PAD&
pad =
static_cast<const PAD&
>( aItem );
928 BOX2I box{
pad.GetPosition() - pad_size / 2, pad_size };
929 box.Inflate(
m_params.outsetDistance );
931 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
942 radius += std::min( pad_size.
x, pad_size.
y ) / 2;
968 addCircleOrRect(
circle );
993 box.Inflate(
m_params.outsetDistance );
995 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
1007 cornerRadius = std::max( cornerRadius +
m_params.outsetDistance, 0 );
1009 if(
m_params.gridRounding.has_value() )
1012 if( cornerRadius > 0 )
1017 addChain( poly.
Outline( 0 ) );
1032 if( newRadius <= 0 )
1039 addCircleOrRect(
circle );
1066 chain.Append( seg.
A - ext + perp );
1067 chain.Append( seg.
A - ext - perp );
1068 chain.Append( seg.
B + ext - perp );
1069 chain.Append( seg.
B + ext + perp );
1070 chain.SetClosed(
true );
1092 const SHAPE_ARC inner{ arc.GetCenter(), arc.GetP0() - startNorm,
1093 arc.GetCentralAngle(), 0 };
1094 const SHAPE_ARC outer{ arc.GetCenter(), arc.GetP0() + startNorm,
1095 arc.GetCentralAngle(), 0 };
1098 chain.Append( outer );
1102 if( inner.GetRadius() > 0 )
1104 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, bool aInferOutlineIfNecessary, 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
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
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.