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