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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "teardrop/teardrop.h"
22
23#include <confirm.h>
24
26#include <pcb_track.h>
27#include <pad.h>
28#include <zone_filler.h>
29#include <board_commit.h>
30
32#include <drc/drc_rtree.h>
35#include <bezier_curves.h>
36
37#include <algorithm>
38#include <limits>
39#include <unordered_map>
40#include <unordered_set>
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 m_copperIndexed( false )
52{
53 m_prmsList = m_board->GetDesignSettings().GetTeadropParamsList();
54 m_tolerance = 0;
55}
56
57
58KIID TEARDROP_MANAGER::teardropUuid( const PCB_TRACK* aTrack, const BOARD_ITEM* aCandidate,
59 int aSlot )
60{
61 // Deriving the UUID from the pair keeps teardrop ordering stable across save/load. A
62 // crossing track yields two from one pair, so each takes a slot, and a slot spans two UUIDs.
63 KIID uuid = KIID::Combine( aTrack->m_Uuid, aCandidate->m_Uuid );
64
65 for( int ii = 0; ii < 2 * aSlot; ++ii )
66 uuid.Increment();
67
68 return uuid;
69}
70
71
73{
74 KIID uuid = aCopperUuid;
75 uuid.Increment();
76
77 return uuid;
78}
79
80
81void TEARDROP_MANAGER::buildCrossingStub( PCB_TRACK& aStub, const PCB_TRACK* aTrack, const VECTOR2I& aCrossedPos )
82{
83 // Copying aTrack would slice a PCB_ARC while the copy kept PCB_ARC_T, so the arc branches
84 // would read a mid-point that is not there.
85 aStub.SetLayer( aTrack->GetLayer() );
86 aStub.SetWidth( aTrack->GetWidth() );
87 aStub.SetNet( aTrack->GetNet() );
88 aStub.SetHasSolderMask( aTrack->HasSolderMask() );
90
91 // Aim the stub along the track centreline. The crossed centre can sit anywhere within the
92 // track width, and the teardrop follows the stub.
93 if( aTrack->Type() == PCB_ARC_T )
94 {
95 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
96 SHAPE_ARC shape( arc->GetStart(), arc->GetMid(), arc->GetEnd(), 0 );
97
98 aStub.SetEnd( shape.NearestPoint( aCrossedPos ) );
99 }
100 else
101 {
102 aStub.SetEnd( SEG( aTrack->GetStart(), aTrack->GetEnd() ).NearestPoint( aCrossedPos ) );
103 }
104}
105
106
108 std::vector<VECTOR2I>& aPoints, PCB_TRACK* aSourceTrack,
109 const KIID& aUuid ) const
110{
111 ZONE* teardrop = new ZONE( m_board );
112
113 teardrop->SetUuidDirect( aUuid );
114
115 // Pristine rather than the board's, so nothing the user set up for a pour (hatch fill,
116 // rule area, locking) leaks into a teardrop.
118
119 // Add zone properties (priority will be fixed later)
120 teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
122 teardrop->SetLayer( aSourceTrack->GetLayer() );
123 teardrop->SetNetCode( aSourceTrack->GetNetCode(), /* aNoAssert */ true );
124 teardrop->SetLocalClearance( 0 );
125 teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
127 teardrop->SetIsFilled( false );
130
131 SHAPE_POLY_SET* outline = teardrop->Outline();
132 outline->NewOutline();
133
134 for( const VECTOR2I& pt: aPoints )
135 outline->Append( pt.x, pt.y );
136
137 // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
138 // fill is the same as its outline.
139 teardrop->SetFilledPolysList( aSourceTrack->GetLayer(), *teardrop->Outline() );
140 teardrop->SetIsFilled( true );
141
142 // Used in priority calculations:
143 teardrop->CalculateFilledArea();
144
145 return teardrop;
146}
147
148
150 std::vector<VECTOR2I>& aPoints,
151 PCB_TRACK* aSourceTrack, const KIID& aUuid ) const
152{
153 ZONE* teardrop = new ZONE( m_board );
154
155 // The second UUID of the slot, so the mask differs from the copper it covers.
156 teardrop->SetUuidDirect( maskUuidFor( aUuid ) );
157
158 // As for the copper teardrop. The ZONE constructor imports the board's zone defaults,
159 // which follow the last pour the user set up.
161
162 teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
164 teardrop->SetLayer( aSourceTrack->GetLayer() == F_Cu ? F_Mask : B_Mask );
165 teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
166 teardrop->SetIsFilled( false );
169
170 SHAPE_POLY_SET* outline = teardrop->Outline();
171 outline->NewOutline();
172
173 for( const VECTOR2I& pt: aPoints )
174 outline->Append( pt.x, pt.y );
175
176 if( int expansion = aSourceTrack->GetSolderMaskExpansion() )
177 {
178 // The zone-min-thickness deflate/reinflate is going to round corners, so it's more
179 // efficient to allow acute corners on the solder mask expansion here, and delegate the
180 // rounding to the deflate/reinflate.
181 teardrop->SetMinThickness( std::max( teardrop->GetMinThickness(), expansion ) );
182
184 m_board->GetDesignSettings().m_MaxError );
185 }
186
187 // Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
188 // fill is the same as its outline.
189 teardrop->SetFilledPolysList( teardrop->GetLayer(), *teardrop->Outline() );
190 teardrop->SetIsFilled( true );
191
192 return teardrop;
193}
194
195
197 TEARDROP_VARIANT aTeardropVariant,
198 std::vector<VECTOR2I>& aPoints,
199 PCB_TRACK* aSourceTrack, const KIID& aUuid )
200{
201 ZONE* new_teardrop = createTeardrop( aTeardropVariant, aPoints, aSourceTrack, aUuid );
202 m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
203 m_createdTdList.push_back( new_teardrop );
204
205 // The next teardrop has to see this one, or two of them flare into the same gap.
207 m_copperRTree.Insert( new_teardrop, new_teardrop->GetLayer(), CLEARANCE_CONSTRAINT );
208
209 aCommit.Added( new_teardrop );
210
211 if( aSourceTrack->HasSolderMask() && IsExternalCopperLayer( aSourceTrack->GetLayer() ) )
212 {
213 ZONE* new_teardrop_mask = createTeardropMask( aTeardropVariant, aPoints, aSourceTrack,
214 aUuid );
215 m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
216 aCommit.Added( new_teardrop_mask );
217 }
218}
219
220
222 const TEARDROP_PARAMETERS& aParams,
223 TEARDROP_MANAGER::TEARDROP_VARIANT aTeardropVariant,
224 PCB_TRACK* aTrack, PCB_TRACK* aSourceTrack,
225 BOARD_ITEM* aCandidate, const VECTOR2I& aPos,
226 const KIID& aUuid )
227{
228 std::vector<VECTOR2I> points;
229
230 if( computeFittedTeardropPolygon( aParams, points, aTrack, aSourceTrack, aCandidate, aPos ) )
231 {
232 createAndAddTeardropWithMask( aCommit, aTeardropVariant, points, aSourceTrack, aUuid );
233 return true;
234 }
235
236 return false;
237}
238
239
241 std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
242 std::set<PCB_TRACK*>* dirtyTracks,
243 const std::vector<BOARD_ITEM*>* dirtyCopper )
244{
245 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
246
247 struct TEARDROP_ANCHORS
248 {
249 std::vector<PAD*> pads;
250 std::vector<PCB_VIA*> vias;
251 std::vector<PCB_TRACK*> tracks;
252 };
253
254 std::vector<ZONE*> masks;
255 std::vector<ZONE*> copperTeardrops;
256 std::map<ZONE*, TEARDROP_ANCHORS> anchors;
257
258 for( ZONE* zone : m_board->Zones() )
259 {
260 if( !zone->IsTeardropArea() )
261 continue;
262
263 // Connectivity knows nothing of a mask layer, so a mask teardrop is never stale on its
264 // own. It goes when the copper it covers goes.
265 if( !zone->IsOnCopperLayer() )
266 {
267 masks.push_back( zone );
268 continue;
269 }
270
271 copperTeardrops.push_back( zone );
272
273 TEARDROP_ANCHORS& zoneAnchors = anchors[zone];
274
275 connectivity->GetConnectedPadsAndVias( zone, &zoneAnchors.pads, &zoneAnchors.vias );
276 zoneAnchors.tracks = connectivity->GetConnectedTracks( zone );
277 }
278
279 // A footprint move pushes every copper descendant, and the test below runs against the whole
280 // list once per teardrop. PCB_ARC rebuilds a SHAPE_ARC every time it is asked for its box.
281 struct DIRTY_COPPER
282 {
283 BOX2I bbox;
284 int netcode;
285 };
286
287 std::map<PCB_LAYER_ID, std::vector<DIRTY_COPPER>> dirtyCopperByLayer;
288
289 if( dirtyCopper )
290 {
291 for( BOARD_ITEM* item : *dirtyCopper )
292 {
293 DIRTY_COPPER entry = { item->GetBoundingBox(), copperNetcode( item ) };
294
295 for( PCB_LAYER_ID layer : item->GetLayerSet().CuStack() )
296 dirtyCopperByLayer[layer].push_back( entry );
297 }
298 }
299
300 int maxClearance = m_board->GetMaxClearanceValue();
301
302 // A width fitted to the neighbours goes stale when one of them moves, though the teardrop
303 // anchors on neither. Pre- and post-edit geometry both count, so moving away counts too.
304 auto foreignNeighbourMoved =
305 [&]( ZONE* zone ) -> bool
306 {
307 PCB_LAYER_ID layer = zone->GetFirstLayer();
308
309 auto it = dirtyCopperByLayer.find( layer );
310
311 if( it == dirtyCopperByLayer.end() )
312 return false;
313
314 // The fit resolves clearance per pair, so no one number bounds the neighbourhood.
315 // Take the widest anything can demand; over-retiring only costs a rebuild.
316 BOX2I reach = zone->GetBoundingBox();
317
318 reach.Inflate( maxClearance );
319
320 for( const DIRTY_COPPER& item : it->second )
321 {
322 // Net 0 is "no net", not a net that every unassigned item shares.
323 if( zone->GetNetCode() > 0 && item.netcode == zone->GetNetCode() )
324 continue;
325
326 if( reach.Intersects( item.bbox ) )
327 return true;
328 }
329
330 return false;
331 };
332
333 std::unordered_set<BOARD_ITEM*> dirtyPadViaSet( dirtyPadsAndVias->begin(),
334 dirtyPadsAndVias->end() );
335
336 auto isStale =
337 [&]( const TEARDROP_ANCHORS& zoneAnchors )
338 {
339 auto anchorDirty = [&]( BOARD_ITEM* aItem )
340 {
341 return dirtyPadViaSet.count( aItem ) > 0;
342 };
343
344 return std::any_of( zoneAnchors.pads.begin(), zoneAnchors.pads.end(),
345 anchorDirty )
346 || std::any_of( zoneAnchors.vias.begin(), zoneAnchors.vias.end(),
347 anchorDirty )
348 || std::any_of( zoneAnchors.tracks.begin(), zoneAnchors.tracks.end(),
349 [&]( PCB_TRACK* aTrack )
350 {
351 return dirtyTracks->contains( aTrack );
352 } );
353 };
354
355 // Dirty the anchors first, or the rebuild passes these teardrops by and they are lost.
356 // Doing it here also lets the staleness pass below see the lists UpdateTeardrops() will.
357 for( ZONE* zone : copperTeardrops )
358 {
359 if( !foreignNeighbourMoved( zone ) )
360 continue;
361
362 const TEARDROP_ANCHORS& zoneAnchors = anchors[zone];
363
364 for( PAD* pad : zoneAnchors.pads )
365 {
366 if( dirtyPadViaSet.insert( pad ).second )
367 dirtyPadsAndVias->push_back( pad );
368 }
369
370 for( PCB_VIA* via : zoneAnchors.vias )
371 {
372 if( dirtyPadViaSet.insert( via ).second )
373 dirtyPadsAndVias->push_back( via );
374 }
375
376 for( PCB_TRACK* track : zoneAnchors.tracks )
377 dirtyTracks->insert( track );
378 }
379
380 std::map<PCB_LAYER_ID, std::vector<ZONE*>> survivingCopper;
381 std::unordered_map<KIID, bool> maskSurvives;
382
383 for( ZONE* zone : copperTeardrops )
384 {
385 bool stale = isStale( anchors[zone] );
386
387 // A slot spans both UUIDs, so the pairing is exact rather than guessed from geometry.
388 maskSurvives[maskUuidFor( zone->m_Uuid )] = !stale;
389
390 if( stale )
391 zone->SetFlags( STRUCT_DELETED );
392 else
393 survivingCopper[zone->GetFirstLayer()].push_back( zone );
394 }
395
396 for( ZONE* mask : masks )
397 {
398 bool covers;
399
400 if( auto it = maskSurvives.find( mask->m_Uuid ); it != maskSurvives.end() )
401 {
402 covers = it->second;
403 }
404 else
405 {
406 // A mask predating the UUID spacing pairs with nothing, so fall back to concentricity
407 // (the expansion can be negative). Erring towards a spare mask, not a lost opening.
408 PCB_LAYER_ID copperLayer = mask->GetFirstLayer() == F_Mask ? F_Cu : B_Cu;
409 BOX2I maskBBox = mask->Outline()->BBox();
410
411 covers = false;
412
413 for( ZONE* copper : survivingCopper[copperLayer] )
414 {
415 BOX2I copperBBox = copper->GetBoundingBox();
416
417 if( maskBBox.Contains( copperBBox.GetCenter() )
418 && copperBBox.Contains( maskBBox.GetCenter() ) )
419 {
420 covers = true;
421 break;
422 }
423 }
424 }
425
426 if( !covers )
427 mask->SetFlags( STRUCT_DELETED );
428 }
429
430 m_board->BulkRemoveStaleTeardrops( aCommit );
431}
432
433
435 const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
436 const std::set<PCB_TRACK*>* dirtyTracks,
437 bool aForceFullUpdate )
438{
439 if( m_board->LegacyTeardrops() )
440 return;
441
442 // Init parameters:
443 m_tolerance = pcbIUScale.mmToIU( 0.01 );
444
445 // Old teardrops must be removed, to ensure a clean teardrop rebuild. Before the caches are
446 // built, or they index zones that are no longer on the board.
447 if( aForceFullUpdate )
448 {
449 for( ZONE* zone : m_board->Zones() )
450 {
451 if( zone->IsTeardropArea() )
452 zone->SetFlags( STRUCT_DELETED );
453 }
454
455 m_board->BulkRemoveStaleTeardrops( aCommit );
456 }
457
459
460 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
461 std::unordered_set<BOARD_ITEM*> dirtyPadViaSet;
462
463 if( dirtyPadsAndVias )
464 dirtyPadViaSet.insert( dirtyPadsAndVias->begin(), dirtyPadsAndVias->end() );
465
466 for( PCB_TRACK* track : m_board->Tracks() )
467 {
468 if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
469 continue;
470
471 std::vector<PAD*> connectedPads;
472 std::vector<PCB_VIA*> connectedVias;
473
474 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
475
476 bool forceUpdate = aForceFullUpdate || dirtyTracks->contains( track );
477
478 for( PAD* pad : connectedPads )
479 {
480 if( !forceUpdate && !dirtyPadViaSet.count( pad ) )
481 continue;
482
483 TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
484 VECTOR2I padSize = pad->GetSize( track->GetLayer() );
485 int annularWidth = std::min( padSize.x, padSize.y );
486
487 if( !tdParams.m_Enabled )
488 continue;
489
490 // Ensure a teardrop shape can be built: track width must be < teardrop width and
491 // filter width. A max width of 0 means no limit, not "nothing fits".
492 if( ( tdParams.m_TdMaxWidth > 0 && track->GetWidth() >= tdParams.m_TdMaxWidth )
493 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
494 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
495 {
496 continue;
497 }
498
499 bool startHitsPad = pad->HitTest( track->GetStart(), 0, track->GetLayer() );
500 bool endHitsPad = pad->HitTest( track->GetEnd(), 0, track->GetLayer() );
501
502 // The track is entirely inside the pad; cannot create a teardrop
503 if( startHitsPad && endHitsPad )
504 continue;
505
506 // Reject tangential grazes, but keep short radial entries.
507 if( startHitsPad != endHitsPad
508 && computeChordThroughShape( track, pad, track->GetLayer(),
509 startHitsPad ? track->GetStart() : track->GetEnd() )
510 < track->GetWidth() )
511 {
512 continue;
513 }
514
515 // Skip case where pad and the track are within a copper zone with the same net
516 // (and the pad can be connected to the zone)
517 if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
518 continue;
519
520 // A track crossing the pad earns one teardrop per side, not one over the whole track.
521 if( !startHitsPad && !endHitsPad && track->HitTest( pad->GetPosition() ) )
522 {
523 PCB_TRACK stub( m_board );
524
525 buildCrossingStub( stub, track, pad->GetPosition() );
526
527 stub.SetStart( track->GetEnd() );
529 track, pad, pad->GetPosition(),
530 teardropUuid( track, pad, 0 ) );
531 stub.SetStart( track->GetStart() );
533 track, pad, pad->GetPosition(),
534 teardropUuid( track, pad, 1 ) );
535 }
536 else
537 {
539 track, pad, pad->GetPosition(),
540 teardropUuid( track, pad, 0 ) );
541 }
542 }
543
544 for( PCB_VIA* via : connectedVias )
545 {
546 if( !forceUpdate && !dirtyPadViaSet.count( via ) )
547 continue;
548
549 TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
550 int annularWidth = via->GetWidth( track->GetLayer() );
551
552 if( !tdParams.m_Enabled )
553 continue;
554
555 // Ensure a teardrop shape can be built: track width must be < teardrop width and
556 // filter width. A max width of 0 means no limit, not "nothing fits".
557 if( ( tdParams.m_TdMaxWidth > 0 && track->GetWidth() >= tdParams.m_TdMaxWidth )
558 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
559 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
560 {
561 continue;
562 }
563
564 bool startHitsVia = via->HitTest( track->GetStart() );
565 bool endHitsVia = via->HitTest( track->GetEnd() );
566
567 // The track is entirely inside the via; cannot create a teardrop
568 if( startHitsVia && endHitsVia )
569 continue;
570
571 // Reject tangential grazes, but keep short radial entries.
572 if( startHitsVia != endHitsVia
573 && computeChordThroughShape( track, via, track->GetLayer(),
574 startHitsVia ? track->GetStart() : track->GetEnd() )
575 < track->GetWidth() )
576 {
577 continue;
578 }
579
580 // As for pads, a track that merely crosses the via earns a teardrop on each side.
581 if( !startHitsVia && !endHitsVia && track->HitTest( via->GetPosition() ) )
582 {
583 PCB_TRACK stub( m_board );
584
585 buildCrossingStub( stub, track, via->GetPosition() );
586
587 stub.SetStart( track->GetEnd() );
589 track, via, via->GetPosition(),
590 teardropUuid( track, via, 0 ) );
591 stub.SetStart( track->GetStart() );
593 track, via, via->GetPosition(),
594 teardropUuid( track, via, 1 ) );
595 }
596 else
597 {
599 track, via, via->GetPosition(),
600 teardropUuid( track, via, 0 ) );
601 }
602 }
603 }
604
605 if( ( aForceFullUpdate || !dirtyTracks->empty() )
606 && m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
607 {
608 AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate, false );
609 }
610
611 // Now set priority of teardrops now all teardrops are added
613}
614
615
617{
618 for( ZONE* zone : m_board->Zones() )
619 {
620 if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
621 zone->SetFlags( STRUCT_DELETED );
622 }
623
624 m_board->BulkRemoveStaleTeardrops( aCommit );
625}
626
627
629{
630 // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
631 // to know the zone layer of a teardrop
632
633 unsigned priority_base = MAGIC_TEARDROP_ZONE_ID;
634
635 // The sort function to sort by increasing copper layers. Group by layers.
636 // For same layers sort by decreasing areas
637 struct
638 {
639 bool operator()(ZONE* a, ZONE* b) const
640 {
641 if( a->GetFirstLayer() == b->GetFirstLayer() )
642 {
643 if( a->GetOutlineArea() != b->GetOutlineArea() )
644 return a->GetOutlineArea() > b->GetOutlineArea();
645 return a->m_Uuid < b->m_Uuid; // stable tiebreak
646 }
647 return a->GetFirstLayer() < b->GetFirstLayer();
648
649 }
650 } compareLess;
651
652 for( ZONE* td: m_createdTdList )
653 td->CalculateOutlineArea();
654
655 std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
656
657 // Survivors of an incremental update keep their priorities, and equal-priority zones of
658 // different nets do not clear each other, so hand out what the layer still has free.
659 std::set<ZONE*> created( m_createdTdList.begin(), m_createdTdList.end() );
660 std::map<int, std::set<unsigned>> taken;
661
662 for( ZONE* zone : m_board->Zones() )
663 {
664 if( zone->IsTeardropArea() && !created.count( zone ) )
665 taken[zone->GetFirstLayer()].insert( zone->GetAssignedPriority() );
666 }
667
668 int curr_layer = -1;
669
670 for( ZONE* td: m_createdTdList )
671 {
672 if( td->GetFirstLayer() != curr_layer )
673 {
674 curr_layer = td->GetFirstLayer();
675 priority_base = MAGIC_TEARDROP_ZONE_ID;
676 }
677
678 const std::set<unsigned>& layerTaken = taken[curr_layer];
679
680 while( layerTaken.count( priority_base )
681 && priority_base < std::numeric_limits<unsigned>::max() )
682 {
683 priority_base++;
684 }
685
686 td->SetAssignedPriority( priority_base );
687
688 if( priority_base < std::numeric_limits<unsigned>::max() )
689 priority_base++;
690 }
691}
692
693
695 const std::set<PCB_TRACK*>* aTracks,
696 bool aForceFullUpdate, bool aSetPriorities )
697{
698 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
699 TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
700
701 // Explore groups (a group is a set of tracks on the same layer and the same net):
702 for( auto& grp : m_trackLookupList.GetBuffer() )
703 {
704 int layer, netcode;
705 TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
706
707 std::vector<PCB_TRACK*>* sublist = &grp.second;
708
709 if( sublist->size() <= 1 ) // We need at least 2 track segments
710 continue;
711
712 // The sort function to sort by increasing track widths
713 struct
714 {
715 bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
716 { return a->GetWidth() < b->GetWidth(); }
717 } compareLess;
718
719 std::sort( sublist->begin(), sublist->end(), compareLess );
720 int min_width = sublist->front()->GetWidth();
721 int max_width = sublist->back()->GetWidth();
722
723 // Skip groups having the same track thickness
724 if( max_width == min_width )
725 continue;
726
727 for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
728 {
729 PCB_TRACK* track = (*sublist)[ii];
730 int track_len = (int) track->GetLength();
731 bool track_needs_update = aForceFullUpdate || aTracks->contains( track );
732 min_width = track->GetWidth();
733
734 // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
735 params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
736 const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
737 min_width = KiROUND( min_width * th );
738
739 for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
740 {
741 // Search candidates with thickness > curr thickness
742 PCB_TRACK* candidate = (*sublist)[jj];
743
744 if( min_width >= candidate->GetWidth() )
745 continue;
746
747 // Cannot build a teardrop on a too short track segment.
748 // The min len is > candidate radius
749 if( track_len <= candidate->GetWidth() /2 )
750 continue;
751
752 // Now test end to end connection:
753 EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
754 // 0, STARTPOINT, ENDPOINT
755
756 VECTOR2I pos = candidate->GetStart();
757 match_points = track->IsPointOnEnds( pos, m_tolerance );
758
759 if( !match_points )
760 {
761 pos = candidate->GetEnd();
762 match_points = track->IsPointOnEnds( pos, m_tolerance );
763 }
764
765 if( !match_points )
766 continue;
767
768 // An untouched pair's teardrop was not removed as stale, so building another
769 // would duplicate it, and its UUID, on every edit elsewhere on the board.
770 if( !track_needs_update && !aTracks->contains( candidate ) )
771 continue;
772
773 // Pads/vias have priority for teardrops; ensure there isn't one at our position
774 bool existingPadOrVia = false;
775 std::vector<PAD*> connectedPads;
776 std::vector<PCB_VIA*> connectedVias;
777
778 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
779
780 for( PAD* pad : connectedPads )
781 {
782 if( pad->HitTest( pos ) )
783 existingPadOrVia = true;
784 }
785
786 for( PCB_VIA* via : connectedVias )
787 {
788 if( via->HitTest( pos ) )
789 existingPadOrVia = true;
790 }
791
792 if( existingPadOrVia )
793 continue;
794
796 track, candidate, pos,
797 teardropUuid( track, candidate, 0 ) );
798 }
799 }
800 }
801
802 // The global edit dialog calls this directly, and a teardrop left at the default priority is
803 // outranked by every pour. UpdateTeardrops() has more to add, so it numbers them itself.
804 if( aSetPriorities )
806}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
NETINFO_ITEM * GetNet() const
Return NETINFO_ITEM object for a given item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition commit.h:80
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Definition kiid.h:46
static KIID Combine(const KIID &aFirst, const KIID &aSecond)
Creates a deterministic KIID from two input KIIDs by XORing their underlying UUIDs.
Definition kiid.cpp:314
void Increment()
Generates a deterministic replacement for a given ID.
Definition kiid.cpp:299
Definition pad.h:61
const VECTOR2I & GetMid() const
Definition pcb_track.h:295
int GetSolderMaskExpansion() const
void SetHasSolderMask(bool aVal)
Definition pcb_track.h:121
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
bool HasSolderMask() const
Definition pcb_track.h:122
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pcb_track.h:124
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_track.h:125
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
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...
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
virtual int GetWidth() const
Definition pcb_track.h:92
Definition seg.h:38
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Represent a set of closed polygons.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
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:333
ZONE * createTeardropMask(TEARDROP_VARIANT aTeardropVariant, std::vector< VECTOR2I > &aPoints, PCB_TRACK *aSourceTrack, const KIID &aUuid) const
Definition teardrop.cpp:149
static int GetWidth(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
static int copperNetcode(const BOARD_ITEM *aItem)
TEARDROP_MANAGER(BOARD *aBoard, TOOL_MANAGER *aToolManager)
Definition teardrop.cpp:48
DRC_RTREE m_copperRTree
Every copper item plus the teardrops built so far, to keep teardrops off other nets.
Definition teardrop.h:342
static KIID teardropUuid(const PCB_TRACK *aTrack, const BOARD_ITEM *aCandidate, int aSlot)
Build the UUID a teardrop is created with.
Definition teardrop.cpp:58
static KIID maskUuidFor(const KIID &aCopperUuid)
Build the UUID of the mask sibling of aCopperUuid.
Definition teardrop.cpp:72
ZONE * createTeardrop(TEARDROP_VARIANT aTeardropVariant, std::vector< VECTOR2I > &aPoints, PCB_TRACK *aSourceTrack, const KIID &aUuid) const
Creates a teardrop (a ZONE item) from its polygonal shape, track netcode and layer.
Definition teardrop.cpp:107
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:434
void RemoveTeardrops(BOARD_COMMIT &aCommit, std::vector< BOARD_ITEM * > *dirtyPadsAndVias, std::set< PCB_TRACK * > *dirtyTracks, const std::vector< BOARD_ITEM * > *dirtyCopper=nullptr)
Remove teardrops on dirty pads, vias or tracks, and any whose neighbouring copper moved,...
Definition teardrop.cpp:240
void setTeardropPriorities()
Set priority of created teardrops.
Definition teardrop.cpp:628
TRACK_BUFFER m_trackLookupList
Definition teardrop.h:338
void AddTeardropsOnTracks(BOARD_COMMIT &aCommit, const std::set< PCB_TRACK * > *aTracks, bool aForceFullUpdate=false, bool aSetPriorities=true)
Add teardrop on tracks of different sizes connected by their end.
Definition teardrop.cpp:694
TEARDROP_PARAMETERS_LIST * m_prmsList
Definition teardrop.h:335
std::vector< ZONE * > m_createdTdList
Definition teardrop.h:339
void DeleteTrackToTrackTeardrops(BOARD_COMMIT &aCommit)
Definition teardrop.cpp:616
bool areItemsInSameZone(BOARD_ITEM *aPadOrVia, PCB_TRACK *aTrack) const
bool tryCreateTrackTeardrop(BOARD_COMMIT &aCommit, const TEARDROP_PARAMETERS &aParams, TEARDROP_VARIANT aTeardropVariant, PCB_TRACK *aTrack, PCB_TRACK *aSourceTrack, BOARD_ITEM *aCandidate, const VECTOR2I &aPos, const KIID &aUuid)
Attempts to create a track-to-track teardrop.
Definition teardrop.cpp:221
bool computeFittedTeardropPolygon(const TEARDROP_PARAMETERS &aParams, std::vector< VECTOR2I > &aPoints, PCB_TRACK *aTrack, PCB_TRACK *aSourceTrack, BOARD_ITEM *aOther, const VECTOR2I &aOtherPos) const
Widen a teardrop as far as the surrounding copper allows.
void ensureCopperIndex() const
Build the copper collision index, deferred so a commit with no teardrop candidate never pays for it.
friend class TEARDROP_PARAMETERS
Definition teardrop.h:87
TOOL_MANAGER * m_toolManager
Definition teardrop.h:334
void createAndAddTeardropWithMask(BOARD_COMMIT &aCommit, TEARDROP_VARIANT aTeardropVariant, std::vector< VECTOR2I > &aPoints, PCB_TRACK *aSourceTrack, const KIID &aUuid)
Creates and adds a teardrop with optional mask to the board.
Definition teardrop.cpp:196
int computeChordThroughShape(PCB_TRACK *aTrack, BOARD_ITEM *aOther, PCB_LAYER_ID aLayer, const VECTOR2I &aInsidePoint) const
Return the centerline chord length through aOther's copper span at aInsidePoint.
static void buildCrossingStub(PCB_TRACK &aStub, const PCB_TRACK *aTrack, const VECTOR2I &aCrossedPos)
Set aStub up as the segment from one end of aTrack to the point of aTrack nearest aCrossedPos,...
Definition teardrop.cpp:81
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.
bool m_Enabled
Flag to enable teardrops.
Master controller class:
static void GetNetcodeAndLayerFromIndex(int aIdx, int *aLayer, int *aNetcode)
Definition teardrop.h:52
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Copy settings to a given zone.
static const ZONE_SETTINGS & GetDefaultSettings()
Handle a list of polygons defining a copper zone.
Definition zone.h:70
double GetOutlineArea()
This area is cached from the most recent call to CalculateOutlineArea().
Definition zone.h:289
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
void SetMinThickness(int aMinThickness)
Definition zone.h:316
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuildBorderHatch)
Set all hatch parameters for the zone.
Definition zone.cpp:1563
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:573
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:640
SHAPE_POLY_SET * Outline()
Definition zone.h:421
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
Definition zone.cpp:622
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:746
int GetMinThickness() const
Definition zone.h:315
void SetIsFilled(bool isFilled)
Definition zone.h:307
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
Definition zone.cpp:1937
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition zone.h:313
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:813
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition zone.h:855
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:595
This file is part of the common library.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
#define STRUCT_DELETED
flag indication structures to be erased
std::uint32_t EDA_ITEM_FLAGS
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_Cu
Definition layer_ids.h:60
#define MAGIC_TEARDROP_ZONE_ID
Definition teardrop.cpp:45
@ TARGET_TRACK
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
@ FULL
pads are covered by copper
Definition zones.h:47