KiCad PCB EDA Suite
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) 2022 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
34#include "teardrop.h"
38#include <bezier_curves.h>
39
40#include <wx/log.h>
41
42// The first priority level of a teardrop area (arbitrary value)
43#define MAGIC_TEARDROP_ZONE_ID 30000
44
45
47{
48 m_board = aBoard;
50 m_tolerance = 0;
51}
52
53
54// Build a zone teardrop
55static ZONE_SETTINGS s_default_settings; // Use zone default settings for teardrop
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.
64 s_default_settings.ExportSetting( *teardrop, false );
65
66 // Add zone properties (priority will be fixed later)
67 teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ?
70 teardrop->SetLayer( aTrack->GetLayer() );
71 teardrop->SetNetCode( aTrack->GetNetCode() );
72 teardrop->SetLocalClearance( 0 );
73 teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
75 teardrop->SetIsFilled( false );
76 teardrop->SetZoneName( aTeardropVariant == TD_TYPE_PADVIA ?
80 SHAPE_POLY_SET* outline = teardrop->Outline();
81 outline->NewOutline();
82
83 for( VECTOR2I pt: aPoints )
84 outline->Append(pt.x, pt.y);
85
86 // Used in priority calculations:
87 teardrop->CalculateFilledArea();
88
89 return teardrop;
90}
91
92
93int TEARDROP_MANAGER::SetTeardrops( BOARD_COMMIT* aCommitter, bool aFollowTracks )
94{
95 // Init parameters:
97
98 int count = 0; // Number of created teardrop
99
100 // Old teardrops must be removed, to ensure a clean teardrop rebuild
101 int removed_cnt = RemoveTeardrops( aCommitter, false );
102
103 // get vias, PAD_ATTRIB_PTH and others if aIncludeNotDrilled == true
104 // (custom pads are not collected)
105 std::vector< VIAPAD > viapad_list;
106
108 collectVias( viapad_list );
109
112
113 TRACK_BUFFER trackLookupList;
114
115 if( aFollowTracks )
116 {
117 // Build the track list (only straight lines)
118 for( PCB_TRACK* track: m_board->Tracks() )
119 {
120 if( track->Type() == PCB_TRACE_T )
121 {
122 int netcode = track->GetNetCode();
123 int layer = track->GetLayer();
124 trackLookupList.AddTrack( track, layer, netcode );
125 }
126 }
127 }
128
129
130 std::vector< ZONE*> teardrops;
131 collectTeardrops( teardrops );
132
133 for( PCB_TRACK* track : m_board->Tracks() )
134 {
135 if( track->Type() != PCB_TRACE_T )
136 continue;
137
138 // Search for a padvia connected to track, with one end inside and one end outside
139 // if both track ends are inside or outside, one cannot build a teadrop
140 for( VIAPAD& viapad: viapad_list )
141 {
142 // Pad and track must be on the same layer
143 if( !viapad.IsOnLayer( track->GetLayer() ) )
144 continue;
145
146 bool start_in_pad = viapad.m_Parent->HitTest( track->GetStart() );
147 bool end_in_pad = viapad.m_Parent->HitTest( track->GetEnd() );
148
149 if( end_in_pad == start_in_pad )
150 // the track is inside or outside the via pad. Cannot create a teardrop
151 continue;
152
153 // A pointer to one of available m_Parameters items
154 TEARDROP_PARAMETERS* currParams;
155
156 if( viapad.m_IsRound )
157 currParams = m_prmsList->GetParameters( TARGET_ROUND );
158 else
159 currParams = m_prmsList->GetParameters( TARGET_RECT );
160
161 // Ensure a teardrop shape can be built:
162 // The track width must be < teardrop height
163 if( track->GetWidth() >= currParams->m_TdMaxHeight
164 || track->GetWidth() >= viapad.m_Width * currParams->m_HeightRatio )
165 continue;
166
167 // Ensure also it is not filtered by a too high track->GetWidth()/viapad.m_Width ratio
168 if( track->GetWidth() >= viapad.m_Width * currParams->m_WidthtoSizeFilterRatio )
169 continue;
170
171 // Skip case where pad/via and the track is within a copper zone with the same net
172 // (and the pad can be connected by the zone thermal relief )
173 if( !m_prmsList->m_TdOnPadsInZones && isViaAndTrackInSameZone( viapad, track ) )
174 continue;
175
176 std::vector<VECTOR2I> points;
177 bool success = computeTeardropPolygonPoints( currParams, points, track, viapad,
178 aFollowTracks, trackLookupList );
179
180 if( success )
181 {
182 ZONE* new_teardrop = createTeardrop( TD_TYPE_PADVIA, points, track );
183 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
184 m_createdTdList.push_back( new_teardrop );
185
186 if( aCommitter )
187 aCommitter->Added( new_teardrop );
188
189 count += 1;
190 }
191 }
192 }
193
194 int track2trackCount = 0;
195
197 track2trackCount = addTeardropsOnTracks( aCommitter );
198
199 // Now set priority of teardrops now all teardrops are added
201
202 // Fill teardrop shapes. This is a rough calculation, just to show a filled
203 // shape on screen, but most of time this is a good shape.
204 // Exact shapes can be calculated only on a full zone refill, **much more** time consuming
205 if( m_createdTdList.size() )
206 {
207 int epsilon = pcbIUScale.mmToIU( 0.001 );
208
209 for( ZONE* zone: m_createdTdList )
210 {
211 int half_min_width = zone->GetMinThickness() / 2;
212 int numSegs = GetArcToSegmentCount( half_min_width, pcbIUScale.mmToIU( 0.005 ), FULL_CIRCLE );
213 SHAPE_POLY_SET filledPolys = *zone->Outline();
214
215 filledPolys.Deflate( half_min_width - epsilon, numSegs );
216
217 // Re-inflate after pruning of areas that don't meet minimum-width criteria
218 if( half_min_width - epsilon > epsilon )
219 filledPolys.Inflate( half_min_width - epsilon, numSegs );
220
221 zone->SetFilledPolysList( zone->GetFirstLayer(), filledPolys );
222 }
223 }
224
225 if( count || removed_cnt || track2trackCount )
226 {
227 if( aCommitter )
228 aCommitter->Push( _( "Add teardrops" ) );
229
230 // Note:
231 // Refill zones can be made only with clean data, especially connectivity data,
232 // therefore only after changes are pushed to avoid crashes in some cases
233 }
234
235 return count + track2trackCount;
236}
237
238
240{
241 // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
242 // to know the zone layer of a teardrop
243
244 int priority_base = MAGIC_TEARDROP_ZONE_ID;
245
246 // The sort function to sort by increasing copper layers. Group by layers.
247 // For same layers sort by decreasing areas
248 struct
249 {
250 bool operator()(ZONE* a, ZONE* b) const
251 {
252 if( a->GetFirstLayer() == b->GetFirstLayer() )
253 return a->GetOutlineArea() > b->GetOutlineArea();
254
255 return a->GetFirstLayer() < b->GetFirstLayer();
256 }
257 } compareLess;
258
259 for( ZONE* td: m_createdTdList )
260 td->CalculateOutlineArea();
261
262 std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
263
264 int curr_layer = -1;
265
266 for( ZONE* td: m_createdTdList )
267 {
268 if( td->GetFirstLayer() != curr_layer )
269 {
270 curr_layer = td->GetFirstLayer();
271 priority_base = MAGIC_TEARDROP_ZONE_ID;
272 }
273
274 td->SetAssignedPriority( priority_base++ );
275 }
276}
277
278
280{
281 TRACK_BUFFER trackLookupList;
282 int count = 0;
283
284 // Build the track list (only straight lines)
285 for( PCB_TRACK* track: m_board->Tracks() )
286 {
287 if( track->Type() == PCB_TRACE_T )
288 {
289 int netcode = track->GetNetCode();
290 int layer = track->GetLayer();
291 trackLookupList.AddTrack( track, layer, netcode );
292 }
293 }
294
295 // get vias and pads (custom pads are not collected). We do not create a track to track
296 // teardrop inside a pad or via area
297 std::vector< VIAPAD > viapad_list;
298 collectVias( viapad_list );
299 collectPadsCandidate( viapad_list, true, true, true );
300
302
303 // Explore groups (a group is a set of tracks on the same layer and the same net):
304 for( auto grp : trackLookupList.GetBuffer() )
305 {
306 int layer, netcode;
307 TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
308
309 std::vector<PCB_TRACK*>* sublist = grp.second;
310
311 if( sublist->size() <= 1 ) // We need at least 2 track segments
312 continue;
313
314 // The sort function to sort by increasing track widths
315 struct
316 {
317 bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
318 { return a->GetWidth() < b->GetWidth(); }
319 } compareLess;
320
321 std::sort( sublist->begin(), sublist->end(), compareLess );
322 int min_width = sublist->front()->GetWidth();
323 int max_width = sublist->back()->GetWidth();
324
325 // Skip groups having the same track thickness
326 if( max_width == min_width )
327 continue;
328
329 for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
330 {
331 PCB_TRACK* track = (*sublist)[ii];
332 int track_len = track->GetLength();
333 min_width = track->GetWidth();
334
335 // to avoid creating a teardrop between 2 tracks having similar widths
336 // give a threshold
337 const double th = currParams->m_WidthtoSizeFilterRatio > 0.1 ?
338 1.0 / currParams->m_WidthtoSizeFilterRatio
339 : 10.0;
340 min_width = min_width * th;
341
342 for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
343 {
344 // Search candidates with thickness > curr thickness
345 PCB_TRACK* candidate = (*sublist)[jj];
346
347 if( min_width >= candidate->GetWidth() )
348 continue;
349
350 // Cannot build a teardrop on a too short track segment.
351 // The min len is > candidate radius
352 if( track_len <= candidate->GetWidth() /2 )
353 continue;
354
355 // Now test end to end connection:
356 EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
357 // 0, STARTPOINT, ENDPOINT
358
359 VECTOR2I roundshape_pos = candidate->GetStart();
360 ENDPOINT_T endPointCandidate = ENDPOINT_START;
361 match_points = track->IsPointOnEnds( roundshape_pos, m_tolerance );
362
363 if( !match_points )
364 {
365 roundshape_pos = candidate->GetEnd();
366 match_points = track->IsPointOnEnds( roundshape_pos, m_tolerance );
367 endPointCandidate = ENDPOINT_END;
368 }
369
370 // Ensure a pad or via is not on test_pos point before creating a teardrop
371 // at this location
372 for( VIAPAD& viapad : viapad_list )
373 {
374 if( viapad.IsOnLayer( track->GetLayer() )
375 && viapad.m_Parent->HitTest( roundshape_pos, 0 ) )
376 {
377 match_points = 0;
378 break;
379 }
380 }
381
382 if( match_points )
383 {
384 VIAPAD viatrack( candidate, endPointCandidate );
385 std::vector<VECTOR2I> points;
386 bool success = computeTeardropPolygonPoints( currParams,
387 points, track, viatrack,
388 false, trackLookupList );
389
390 if( success )
391 {
392 ZONE* new_teardrop = createTeardrop( TD_TYPE_TRACKEND, points, track );
393 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
394 m_createdTdList.push_back( new_teardrop );
395
396 if( aCommitter )
397 aCommitter->Added( new_teardrop );
398
399 count += 1;
400 }
401 }
402 }
403 }
404 }
405
406 return count;
407}
408
409
410int TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT* aCommitter, bool aCommitAfterRemove )
411{
412 int count = 0;
413 std::vector< ZONE*> teardrops;
414
415 collectTeardrops( teardrops );
416
417 for( ZONE* teardrop : teardrops )
418 {
419 m_board->Remove( teardrop, REMOVE_MODE::BULK );
420
421 if( aCommitter )
422 aCommitter->Removed( teardrop );
423
424 count += 1;
425 }
426
427 if( count )
428 {
429 ZONE_FILLER filler( m_board, aCommitter );
430 (void)filler.Fill( m_board->Zones() );
431
432 if( aCommitter && aCommitAfterRemove )
433 aCommitter->Push( _( "Remove teardrops" ), SKIP_CONNECTIVITY );
434
436 }
437
438 return count;
439}
440
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
#define SKIP_CONNECTIVITY
Definition: board_commit.h:42
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
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:180
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:711
ZONES & Zones()
Definition: board.h:313
TRACKS & Tracks()
Definition: board.h:304
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:643
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:820
void BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:162
COMMIT & Added(EDA_ITEM *aItem)
Remove a new item from the model.
Definition: commit.h:84
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
The main frame for Pcbnew.
virtual double GetLength() const
Function GetLength returns the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:285
int GetWidth() const
Definition: pcb_track.h:106
const VECTOR2I & GetStart() const
Definition: pcb_track.h:112
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:109
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Function IsPointOnEnds returns STARTPOINT if point if near (dist = min_dist) start point,...
Definition: pcb_track.cpp:207
Represent a set of closed polygons.
void Deflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
void Inflate(int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
SHAPE_LINE_CHAIN & Outline(int aIndex)
int NewOutline()
Creates a new hole in a given outline.
BOARD * m_board
Definition: teardrop.h:249
TEARDROP_MANAGER(BOARD *aBoard, PCB_EDIT_FRAME *aFrame)
Definition: teardrop.cpp:46
void collectTeardrops(std::vector< ZONE * > &aList) const
Build a list of all teardrops on the current board.
void collectVias(std::vector< VIAPAD > &aList) const
Collect and build the list of all vias from the given board.
void collectPadsCandidate(std::vector< VIAPAD > &aList, bool aDrilledViaPad, bool aRoundShapesOnly, bool aIncludeNotDrilled) const
Build a list of pads candidate for teardrops from the given board Pads with no net are not candidate ...
bool isViaAndTrackInSameZone(VIAPAD &aVia, PCB_TRACK *aTrack) const
int SetTeardrops(BOARD_COMMIT *aCommitter, bool aFollowTracks=true)
Set teardrops on a teardrop free board.
Definition: teardrop.cpp:93
void setTeardropPriorities()
Set priority of created teardrops.
Definition: teardrop.cpp:239
int RemoveTeardrops(BOARD_COMMIT *aCommitter, bool aCommitAfterRemove)
Remove all teardrops.
Definition: teardrop.cpp:410
bool computeTeardropPolygonPoints(TEARDROP_PARAMETERS *aCurrParams, std::vector< VECTOR2I > &aCorners, PCB_TRACK *aTrack, VIAPAD &aVia, bool aFollowTracks, TRACK_BUFFER &aTrackLookupList) const
Compute all teardrop points of the polygon shape.
TEARDROP_PARAMETERS_LIST * m_prmsList
Definition: teardrop.h:250
std::vector< ZONE * > m_createdTdList
Definition: teardrop.h:251
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
int addTeardropsOnTracks(BOARD_COMMIT *aCommitter)
Add teardrop on tracks of different sizes connected by their end.
Definition: teardrop.cpp:279
bool m_TargetPadsWithNoHole
True to create teardrops for pads without holes (SMD and others.
bool m_TargetViasPads
True to create teardrops for vias and pads with holes.
bool m_UseRoundShapesOnly
True to create teardrops for round shapes only.
bool m_TargetTrack2Track
True to create teardrops at the end of a track connected to the end of another track having a differe...
bool m_TdOnPadsInZones
Pads inside a zone of the same net do not have teardrop added.
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_HeightRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxHeight
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....
void AddTrack(PCB_TRACK *aTrack, int aLayer, int aNetcode)
Add a track in buffer, in space grouping tracks having the same netcode and the same layer.
static void GetNetcodeAndLayerFromIndex(int aIdx, int *aLayer, int *aNetcode)
Definition: teardrop.h:279
std::map< int, std::vector< PCB_TRACK * > * > & GetBuffer()
Definition: teardrop.h:277
bool Fill(std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Definition: zone_filler.cpp:89
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:70
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
double GetOutlineArea()
This area is cached from the most recent call to CalculateOutlineArea().
Definition: zone.h:225
void SetMinThickness(int aMinThickness)
Definition: zone.h:252
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:257
SHAPE_POLY_SET * Outline()
Definition: zone.h:312
void SetIsFilled(bool isFilled)
Definition: zone.h:243
void SetLocalClearance(int aClearance)
Definition: zone.h:153
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
Definition: zone.cpp:1193
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:249
void SetZoneName(const wxString &aName)
Definition: zone.h:125
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:687
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition: zone.h:712
PCB_LAYER_ID GetFirstLayer() const
Definition: zone.cpp:242
This file is part of the common library.
#define _(s)
static constexpr EDA_ANGLE & FULL_CIRCLE
Definition: eda_angle.h:410
std::uint32_t EDA_ITEM_FLAGS
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
ENDPOINT_T
Definition: pcb_track.h:55
@ ENDPOINT_END
Definition: pcb_track.h:57
@ ENDPOINT_START
Definition: pcb_track.h:56
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
Stores info of a pad, via or track end to build a teardrop.
Definition: teardrop.h:45
static ZONE_SETTINGS s_default_settings
Definition: teardrop.cpp:55
#define MAGIC_TEARDROP_ZONE_ID
Definition: teardrop.cpp:43
#define MAGIC_TEARDROP_TRACK_NAME
Definition: teardrop.h:37
#define MAGIC_TEARDROP_PADVIA_NAME
Definition: teardrop.h:36
@ TARGET_ROUND
@ TARGET_RECT
@ TARGET_TRACK
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101
@ FULL
pads are covered by copper