KiCad PCB EDA Suite
Loading...
Searching...
No Matches
teardrop.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2021 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26#include <confirm.h>
27
29#include <pcb_track.h>
30#include <pad.h>
31#include <zone_filler.h>
32#include <board_commit.h>
33
35#include <teardrop/teardrop.h>
36#include <drc/drc_rtree.h>
38#include <geometry/rtree.h>
40#include <bezier_curves.h>
41
42#include <wx/log.h>
43
44// The first priority level of a teardrop area (arbitrary value)
45#define MAGIC_TEARDROP_ZONE_ID 30000
46
47
49 m_board( aBoard ),
50 m_toolManager( aToolManager )
51{
53 m_tolerance = 0;
54}
55
56
58 std::vector<VECTOR2I>& aPoints, PCB_TRACK* aTrack ) const
59{
60 ZONE* teardrop = new ZONE( m_board );
61
62 // teardrop settings are the last zone settings used by a zone dialog.
63 // override them by default.
65
66 // Add zone properties (priority will be fixed later)
67 teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
68 : TEARDROP_TYPE::TD_TRACKEND );
69 teardrop->SetLayer( aTrack->GetLayer() );
70 teardrop->SetNetCode( aTrack->GetNetCode() );
71 teardrop->SetLocalClearance( 0 );
72 teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
73 teardrop->SetPadConnection( ZONE_CONNECTION::FULL );
74 teardrop->SetIsFilled( false );
75 teardrop->SetZoneName( aTeardropVariant == TD_TYPE_PADVIA ? MAGIC_TEARDROP_PADVIA_NAME
77 teardrop->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
78 teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL,
79 pcbIUScale.mmToIU( 0.1 ), true );
80
81 SHAPE_POLY_SET* outline = teardrop->Outline();
82 outline->NewOutline();
83
84 for( const VECTOR2I& pt: aPoints )
85 outline->Append( pt.x, pt.y );
86
87 // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
88 // fill is the same as its outline.
89 teardrop->SetFilledPolysList( aTrack->GetLayer(), *teardrop->Outline() );
90 teardrop->SetIsFilled( true );
91
92 // Used in priority calculations:
93 teardrop->CalculateFilledArea();
94
95 return teardrop;
96}
97
98
100 const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
101 const std::set<PCB_TRACK*>* dirtyTracks )
102{
103 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
104 std::vector<ZONE*> stale_teardrops;
105
106 for( ZONE* zone : m_board->Zones() )
107 {
108 if( zone->IsTeardropArea() )
109 {
110 bool stale = false;
111
112 std::vector<PAD*> connectedPads;
113 std::vector<PCB_VIA*> connectedVias;
114
115 connectivity->GetConnectedPadsAndVias( zone, &connectedPads, &connectedVias );
116
117 for( PAD* pad : connectedPads )
118 {
119 if( alg::contains( *dirtyPadsAndVias, pad ) )
120 {
121 stale = true;
122 break;
123 }
124 }
125
126 if( !stale )
127 {
128 for( PCB_VIA* via : connectedVias )
129 {
130 if( alg::contains( *dirtyPadsAndVias, via ) )
131 {
132 stale = true;
133 break;
134 }
135 }
136 }
137
138 if( stale )
139 stale_teardrops.push_back( zone );
140 }
141 }
142
143 for( ZONE* td : stale_teardrops )
144 {
145 m_board->Remove( td, REMOVE_MODE::BULK );
146 aCommit.Removed( td );
147 }
148}
149
150
152 const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
153 const std::set<PCB_TRACK*>* dirtyTracks,
154 bool aForceFullUpdate )
155{
156 if( m_board->LegacyTeardrops() )
157 return;
158
159 // Init parameters:
160 m_tolerance = pcbIUScale.mmToIU( 0.01 );
161
163
164 // Old teardrops must be removed, to ensure a clean teardrop rebuild
165 if( aForceFullUpdate )
166 {
167 std::vector<ZONE*> teardrops;
168
169 for( ZONE* zone : m_board->Zones() )
170 {
171 if( zone->IsTeardropArea() )
172 teardrops.push_back( zone );
173 }
174
175 for( ZONE* td : teardrops )
176 {
177 m_board->Remove( td, REMOVE_MODE::BULK );
178 aCommit.Removed( td );
179 }
180 }
181
182 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
183
184 for( PCB_TRACK* track : m_board->Tracks() )
185 {
186 if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
187 continue;
188
189 std::vector<PAD*> connectedPads;
190 std::vector<PCB_VIA*> connectedVias;
191
192 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
193
194 bool forceUpdate = aForceFullUpdate || alg::contains( *dirtyTracks, track );
195
196 for( PAD* pad : connectedPads )
197 {
198 if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, pad ) )
199 continue;
200
201 if( pad->GetShape() == PAD_SHAPE::CUSTOM )
202 // A teardrop shape cannot be built
203 continue;
204
205 TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
206 int annularWidth = std::min( pad->GetSize().x, pad->GetSize().y );
207
208 if( !tdParams.m_Enabled )
209 continue;
210
211 // Ensure a teardrop shape can be built: track width must be < teardrop width and
212 // filter width
213 if( track->GetWidth() >= tdParams.m_TdMaxWidth
214 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
215 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
216 {
217 continue;
218 }
219
220 if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
221 // The track is entirely inside the pad; cannot create a teardrop
222 continue;
223
224 // Skip case where pad and the track are within a copper zone with the same net
225 // (and the pad can be connected to the zone)
226 if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
227 continue;
228
229 std::vector<VECTOR2I> points;
230
231 if( computeTeardropPolygon( tdParams, points, track, pad, pad->GetPosition() ) )
232 {
233 ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
234 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
235 m_createdTdList.push_back( new_teardrop );
236
237 aCommit.Added( new_teardrop );
238 }
239 }
240
241 for( PCB_VIA* via : connectedVias )
242 {
243 if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, via ) )
244 continue;
245
246 TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
247 int annularWidth = via->GetWidth();
248
249 if( !tdParams.m_Enabled )
250 continue;
251
252 // Ensure a teardrop shape can be built: track width must be < teardrop width and
253 // filter width
254 if( track->GetWidth() >= tdParams.m_TdMaxWidth
255 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
256 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
257 {
258 continue;
259 }
260
261 if( via->HitTest( track->GetStart() ) && via->HitTest( track->GetEnd() ) )
262 // The track is entirely inside the via; cannot create a teardrop
263 continue;
264
265 std::vector<VECTOR2I> points;
266
267 if( computeTeardropPolygon( tdParams, points, track, via, via->GetPosition() ) )
268 {
269 ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
270 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
271 m_createdTdList.push_back( new_teardrop );
272
273 aCommit.Added( new_teardrop );
274 }
275 }
276 }
277
278 if( ( aForceFullUpdate || !dirtyTracks->empty() )
280 {
281 AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate );
282 }
283
284 // Now set priority of teardrops now all teardrops are added
286}
287
288
290{
291 std::vector<ZONE*> stale_teardrops;
292
293 for( ZONE* zone : m_board->Zones() )
294 {
295 if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
296 stale_teardrops.push_back( zone );
297 }
298
299 for( ZONE* td : stale_teardrops )
300 {
301 m_board->Remove( td, REMOVE_MODE::BULK );
302 aCommit.Removed( td );
303 }
304}
305
306
308{
309 // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
310 // to know the zone layer of a teardrop
311
312 int priority_base = MAGIC_TEARDROP_ZONE_ID;
313
314 // The sort function to sort by increasing copper layers. Group by layers.
315 // For same layers sort by decreasing areas
316 struct
317 {
318 bool operator()(ZONE* a, ZONE* b) const
319 {
320 if( a->GetFirstLayer() == b->GetFirstLayer() )
321 return a->GetOutlineArea() > b->GetOutlineArea();
322
323 return a->GetFirstLayer() < b->GetFirstLayer();
324 }
325 } compareLess;
326
327 for( ZONE* td: m_createdTdList )
328 td->CalculateOutlineArea();
329
330 std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
331
332 int curr_layer = -1;
333
334 for( ZONE* td: m_createdTdList )
335 {
336 if( td->GetFirstLayer() != curr_layer )
337 {
338 curr_layer = td->GetFirstLayer();
339 priority_base = MAGIC_TEARDROP_ZONE_ID;
340 }
341
342 td->SetAssignedPriority( priority_base++ );
343 }
344}
345
346
348 const std::set<PCB_TRACK*>* aTracks,
349 bool aForceFullUpdate )
350{
351 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
353
354 // Explore groups (a group is a set of tracks on the same layer and the same net):
355 for( auto& grp : m_trackLookupList.GetBuffer() )
356 {
357 int layer, netcode;
358 TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
359
360 std::vector<PCB_TRACK*>* sublist = grp.second;
361
362 if( sublist->size() <= 1 ) // We need at least 2 track segments
363 continue;
364
365 // The sort function to sort by increasing track widths
366 struct
367 {
368 bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
369 { return a->GetWidth() < b->GetWidth(); }
370 } compareLess;
371
372 std::sort( sublist->begin(), sublist->end(), compareLess );
373 int min_width = sublist->front()->GetWidth();
374 int max_width = sublist->back()->GetWidth();
375
376 // Skip groups having the same track thickness
377 if( max_width == min_width )
378 continue;
379
380 for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
381 {
382 PCB_TRACK* track = (*sublist)[ii];
383 int track_len = (int) track->GetLength();
384 bool track_needs_update = aForceFullUpdate || alg::contains( *aTracks, track );
385 min_width = track->GetWidth();
386
387 // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
388 params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
389 const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
390 min_width = KiROUND( min_width * th );
391
392 for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
393 {
394 // Search candidates with thickness > curr thickness
395 PCB_TRACK* candidate = (*sublist)[jj];
396
397 if( min_width >= candidate->GetWidth() )
398 continue;
399
400 // Cannot build a teardrop on a too short track segment.
401 // The min len is > candidate radius
402 if( track_len <= candidate->GetWidth() /2 )
403 continue;
404
405 // Now test end to end connection:
406 EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
407 // 0, STARTPOINT, ENDPOINT
408
409 VECTOR2I pos = candidate->GetStart();
410 match_points = track->IsPointOnEnds( pos, m_tolerance );
411
412 if( !match_points )
413 {
414 pos = candidate->GetEnd();
415 match_points = track->IsPointOnEnds( pos, m_tolerance );
416 }
417
418 if( !match_points )
419 continue;
420
421 if( !track_needs_update && alg::contains( *aTracks, candidate ) )
422 continue;
423
424 // Pads/vias have priority for teardrops; ensure there isn't one at our position
425 bool existingPadOrVia = false;
426 std::vector<PAD*> connectedPads;
427 std::vector<PCB_VIA*> connectedVias;
428
429 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
430
431 for( PAD* pad : connectedPads )
432 {
433 if( pad->HitTest( pos ) )
434 existingPadOrVia = true;
435 }
436
437 for( PCB_VIA* via : connectedVias )
438 {
439 if( via->HitTest( pos ) )
440 existingPadOrVia = true;
441 }
442
443 if( existingPadOrVia )
444 continue;
445
446 std::vector<VECTOR2I> points;
447
448 if( computeTeardropPolygon( params, points, track, candidate, pos ) )
449 {
450 ZONE* new_teardrop = createTeardrop( TD_TYPE_TRACKEND, points, track );
451 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
452 m_createdTdList.push_back( new_teardrop );
453
454 aCommit.Added( new_teardrop );
455 }
456 }
457 }
458 }
459}
460
461
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
TEARDROP_PARAMETERS_LIST * GetTeadropParamsList()
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:226
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:281
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:882
const ZONES & Zones() const
Definition: board.h:326
const TRACKS & Tracks() const
Definition: board.h:320
bool LegacyTeardrops() const
Definition: board.h:1243
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:797
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1020
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:459
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:86
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:98
Definition: pad.h:59
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:693
int GetWidth() const
Definition: pcb_track.h:107
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
Definition: pcb_track.cpp:615
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
BOARD * m_board
Definition: teardrop.h:236
bool computeTeardropPolygon(const TEARDROP_PARAMETERS &aParams, std::vector< VECTOR2I > &aCorners, PCB_TRACK *aTrack, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos) const
Compute all teardrop points of the polygon shape.
TEARDROP_MANAGER(BOARD *aBoard, TOOL_MANAGER *aToolManager)
Definition: teardrop.cpp:48
void UpdateTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks, bool aForceFullUpdate=false)
Update teardrops on a list of items.
Definition: teardrop.cpp:151
void setTeardropPriorities()
Set priority of created teardrops.
Definition: teardrop.cpp:307
void AddTeardropsOnTracks(BOARD_COMMIT &aCommit, const std::set< PCB_TRACK * > *aTracks, bool aForceFullUpdate=false)
Add teardrop on tracks of different sizes connected by their end.
Definition: teardrop.cpp:347
TRACK_BUFFER m_trackLookupList
Definition: teardrop.h:241
TEARDROP_PARAMETERS_LIST * m_prmsList
Definition: teardrop.h:238
std::vector< ZONE * > m_createdTdList
Definition: teardrop.h:242
ZONE * createTeardrop(TEARDROP_VARIANT aTeardropVariant, std::vector< VECTOR2I > &aPoints, PCB_TRACK *aTrack) const
Creates a teardrop (a ZONE item) from its polygonal shape, track netcode and layer.
Definition: teardrop.cpp:57
void DeleteTrackToTrackTeardrops(BOARD_COMMIT &aCommit)
Definition: teardrop.cpp:289
bool areItemsInSameZone(BOARD_ITEM *aPadOrVia, PCB_TRACK *aTrack) const
static int GetWidth(BOARD_ITEM *aItem)
void RemoveTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks)
Remove teardrops connected to any dirty pads, vias or tracks.
Definition: teardrop.cpp:99
TEARDROP_PARAMETERS * GetParameters(TARGET_TD aTdType)
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
Master controller class:
Definition: tool_manager.h:57
const std::map< int, std::vector< PCB_TRACK * > * > & GetBuffer() const
Definition: teardrop.h:62
static void GetNetcodeAndLayerFromIndex(int aIdx, int *aLayer, int *aNetcode)
Definition: teardrop.h:64
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
static const ZONE_SETTINGS & GetDefaultSettings()
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
double GetOutlineArea()
This area is cached from the most recent call to CalculateOutlineArea().
Definition: zone.h:243
void SetLocalClearance(std::optional< int > aClearance)
Definition: zone.h:154
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:875
void SetMinThickness(int aMinThickness)
Definition: zone.h:270
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:261
SHAPE_POLY_SET * Outline()
Definition: zone.h:336
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:636
void SetIsFilled(bool isFilled)
Definition: zone.h:261
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
Definition: zone.cpp:1283
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:267
void SetZoneName(const wxString &aName)
Definition: zone.h:132
void SetTeardropAreaType(TEARDROP_TYPE aType)
Set the type of teardrop if the zone is a teardrop area for non teardrop area, the type must be TEARD...
Definition: zone.h:700
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition: zone.h:725
PCB_LAYER_ID GetFirstLayer() const
Definition: zone.cpp:246
This file is part of the common library.
std::uint32_t EDA_ITEM_FLAGS
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
#define MAGIC_TEARDROP_ZONE_ID
Definition: teardrop.cpp:45
#define MAGIC_TEARDROP_TRACK_NAME
Definition: teardrop.h:38
#define MAGIC_TEARDROP_PADVIA_NAME
Definition: teardrop.h:37
@ TARGET_TRACK
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85