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
204 m_createdTdList.push_back( new_teardrop );
205
206 // The next teardrop has to see this one, or two of them flare into the same gap.
207 BOX2I bbox = new_teardrop->Outline()->BBox();
208 const int bmin[2] = { bbox.GetX(), bbox.GetY() };
209 const int bmax[2] = { bbox.GetRight(), bbox.GetBottom() };
210 m_createdTdRTree[aSourceTrack->GetLayer()].Insert( bmin, bmax, new_teardrop );
211
212 aCommit.Added( new_teardrop );
213
214 if( aSourceTrack->HasSolderMask() && IsExternalCopperLayer( aSourceTrack->GetLayer() ) )
215 {
216 ZONE* new_teardrop_mask = createTeardropMask( aTeardropVariant, aPoints, aSourceTrack,
217 aUuid );
218 m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
219 aCommit.Added( new_teardrop_mask );
220 }
221}
222
223
225 const TEARDROP_PARAMETERS& aParams,
226 TEARDROP_MANAGER::TEARDROP_VARIANT aTeardropVariant,
227 PCB_TRACK* aTrack, PCB_TRACK* aSourceTrack,
228 BOARD_ITEM* aCandidate, const VECTOR2I& aPos,
229 const KIID& aUuid )
230{
231 std::vector<VECTOR2I> points;
232
233 if( computeFittedTeardropPolygon( aParams, points, aTrack, aSourceTrack, aCandidate, aPos ) )
234 {
235 createAndAddTeardropWithMask( aCommit, aTeardropVariant, points, aSourceTrack, aUuid );
236 return true;
237 }
238
239 return false;
240}
241
242
244 std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
245 std::set<PCB_TRACK*>* dirtyTracks,
246 const std::vector<BOARD_ITEM*>* dirtyCopper )
247{
248 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
249
250 struct TEARDROP_ANCHORS
251 {
252 std::vector<PAD*> pads;
253 std::vector<PCB_VIA*> vias;
254 std::vector<PCB_TRACK*> tracks;
255 };
256
257 std::vector<ZONE*> masks;
258 std::vector<ZONE*> copperTeardrops;
259 std::map<ZONE*, TEARDROP_ANCHORS> anchors;
260
261 for( ZONE* zone : m_board->Zones() )
262 {
263 if( !zone->IsTeardropArea() )
264 continue;
265
266 // Connectivity knows nothing of a mask layer, so a mask teardrop is never stale on its
267 // own. It goes when the copper it covers goes.
268 if( !zone->IsOnCopperLayer() )
269 {
270 masks.push_back( zone );
271 continue;
272 }
273
274 copperTeardrops.push_back( zone );
275
276 TEARDROP_ANCHORS& zoneAnchors = anchors[zone];
277
278 connectivity->GetConnectedPadsAndVias( zone, &zoneAnchors.pads, &zoneAnchors.vias );
279 zoneAnchors.tracks = connectivity->GetConnectedTracks( zone );
280 }
281
282 // A footprint move pushes every copper descendant, and the test below runs against the whole
283 // list once per teardrop. PCB_ARC rebuilds a SHAPE_ARC every time it is asked for its box.
284 struct DIRTY_COPPER
285 {
286 BOX2I bbox;
287 int netcode;
288 };
289
290 std::map<PCB_LAYER_ID, std::vector<DIRTY_COPPER>> dirtyCopperByLayer;
291
292 if( dirtyCopper )
293 {
294 for( BOARD_ITEM* item : *dirtyCopper )
295 {
296 DIRTY_COPPER entry = { item->GetBoundingBox(), copperNetcode( item ) };
297
298 for( PCB_LAYER_ID layer : item->GetLayerSet().CuStack() )
299 dirtyCopperByLayer[layer].push_back( entry );
300 }
301 }
302
303 int maxClearance = m_board->GetMaxClearanceValue();
304
305 // A width fitted to the neighbours goes stale when one of them moves, though the teardrop
306 // anchors on neither. Pre- and post-edit geometry both count, so moving away counts too.
307 auto foreignNeighbourMoved =
308 [&]( ZONE* zone ) -> bool
309 {
310 PCB_LAYER_ID layer = zone->GetFirstLayer();
311
312 auto it = dirtyCopperByLayer.find( layer );
313
314 if( it == dirtyCopperByLayer.end() )
315 return false;
316
317 // The fit resolves clearance per pair, so no one number bounds the neighbourhood.
318 // Take the widest anything can demand; over-retiring only costs a rebuild.
319 BOX2I reach = zone->GetBoundingBox();
320
321 reach.Inflate( maxClearance );
322
323 for( const DIRTY_COPPER& item : it->second )
324 {
325 // Net 0 is "no net", not a net that every unassigned item shares.
326 if( zone->GetNetCode() > 0 && item.netcode == zone->GetNetCode() )
327 continue;
328
329 if( reach.Intersects( item.bbox ) )
330 return true;
331 }
332
333 return false;
334 };
335
336 std::unordered_set<BOARD_ITEM*> dirtyPadViaSet( dirtyPadsAndVias->begin(),
337 dirtyPadsAndVias->end() );
338
339 auto isStale =
340 [&]( const TEARDROP_ANCHORS& zoneAnchors )
341 {
342 auto anchorDirty = [&]( BOARD_ITEM* aItem )
343 {
344 return dirtyPadViaSet.count( aItem ) > 0;
345 };
346
347 return std::any_of( zoneAnchors.pads.begin(), zoneAnchors.pads.end(),
348 anchorDirty )
349 || std::any_of( zoneAnchors.vias.begin(), zoneAnchors.vias.end(),
350 anchorDirty )
351 || std::any_of( zoneAnchors.tracks.begin(), zoneAnchors.tracks.end(),
352 [&]( PCB_TRACK* aTrack )
353 {
354 return dirtyTracks->contains( aTrack );
355 } );
356 };
357
358 // Dirty the anchors first, or the rebuild passes these teardrops by and they are lost.
359 // Doing it here also lets the staleness pass below see the lists UpdateTeardrops() will.
360 for( ZONE* zone : copperTeardrops )
361 {
362 if( !foreignNeighbourMoved( zone ) )
363 continue;
364
365 const TEARDROP_ANCHORS& zoneAnchors = anchors[zone];
366
367 for( PAD* pad : zoneAnchors.pads )
368 {
369 if( dirtyPadViaSet.insert( pad ).second )
370 dirtyPadsAndVias->push_back( pad );
371 }
372
373 for( PCB_VIA* via : zoneAnchors.vias )
374 {
375 if( dirtyPadViaSet.insert( via ).second )
376 dirtyPadsAndVias->push_back( via );
377 }
378
379 for( PCB_TRACK* track : zoneAnchors.tracks )
380 dirtyTracks->insert( track );
381 }
382
383 std::map<PCB_LAYER_ID, std::vector<ZONE*>> survivingCopper;
384 std::unordered_map<KIID, bool> maskSurvives;
385
386 for( ZONE* zone : copperTeardrops )
387 {
388 bool stale = isStale( anchors[zone] );
389
390 // A slot spans both UUIDs, so the pairing is exact rather than guessed from geometry.
391 maskSurvives[maskUuidFor( zone->m_Uuid )] = !stale;
392
393 if( stale )
394 zone->SetFlags( STRUCT_DELETED );
395 else
396 survivingCopper[zone->GetFirstLayer()].push_back( zone );
397 }
398
399 for( ZONE* mask : masks )
400 {
401 bool covers;
402
403 if( auto it = maskSurvives.find( mask->m_Uuid ); it != maskSurvives.end() )
404 {
405 covers = it->second;
406 }
407 else
408 {
409 // A mask predating the UUID spacing pairs with nothing, so fall back to concentricity
410 // (the expansion can be negative). Erring towards a spare mask, not a lost opening.
411 PCB_LAYER_ID copperLayer = mask->GetFirstLayer() == F_Mask ? F_Cu : B_Cu;
412 BOX2I maskBBox = mask->Outline()->BBox();
413
414 covers = false;
415
416 for( ZONE* copper : survivingCopper[copperLayer] )
417 {
418 BOX2I copperBBox = copper->GetBoundingBox();
419
420 if( maskBBox.Contains( copperBBox.GetCenter() )
421 && copperBBox.Contains( maskBBox.GetCenter() ) )
422 {
423 covers = true;
424 break;
425 }
426 }
427 }
428
429 if( !covers )
430 mask->SetFlags( STRUCT_DELETED );
431 }
432
433 m_board->BulkRemoveStaleTeardrops( aCommit );
434}
435
436
438 const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
439 const std::set<PCB_TRACK*>* dirtyTracks,
440 bool aForceFullUpdate )
441{
442 if( m_board->LegacyTeardrops() )
443 return;
444
445 // Init parameters:
446 m_tolerance = pcbIUScale.mmToIU( 0.01 );
447
448 // Old teardrops must be removed, to ensure a clean teardrop rebuild. Before the caches are
449 // built, or they index zones that are no longer on the board.
450 if( aForceFullUpdate )
451 {
452 for( ZONE* zone : m_board->Zones() )
453 {
454 if( zone->IsTeardropArea() )
455 zone->SetFlags( STRUCT_DELETED );
456 }
457
458 m_board->BulkRemoveStaleTeardrops( aCommit );
459 }
460
462
463 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
464 std::unordered_set<BOARD_ITEM*> dirtyPadViaSet;
465
466 if( dirtyPadsAndVias )
467 dirtyPadViaSet.insert( dirtyPadsAndVias->begin(), dirtyPadsAndVias->end() );
468
469 for( PCB_TRACK* track : m_board->Tracks() )
470 {
471 if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
472 continue;
473
474 std::vector<PAD*> connectedPads;
475 std::vector<PCB_VIA*> connectedVias;
476
477 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
478
479 bool forceUpdate = aForceFullUpdate || dirtyTracks->contains( track );
480
481 for( PAD* pad : connectedPads )
482 {
483 if( !forceUpdate && !dirtyPadViaSet.count( pad ) )
484 continue;
485
486 TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
487 VECTOR2I padSize = pad->GetSize( track->GetLayer() );
488 int annularWidth = std::min( padSize.x, padSize.y );
489
490 if( !tdParams.m_Enabled )
491 continue;
492
493 // Ensure a teardrop shape can be built: track width must be < teardrop width and
494 // filter width. A max width of 0 means no limit, not "nothing fits".
495 if( ( tdParams.m_TdMaxWidth > 0 && track->GetWidth() >= tdParams.m_TdMaxWidth )
496 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
497 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
498 {
499 continue;
500 }
501
502 bool startHitsPad = pad->HitTest( track->GetStart(), 0, track->GetLayer() );
503 bool endHitsPad = pad->HitTest( track->GetEnd(), 0, track->GetLayer() );
504
505 // The track is entirely inside the pad; cannot create a teardrop
506 if( startHitsPad && endHitsPad )
507 continue;
508
509 // Reject tangential grazes, but keep short radial entries.
510 if( startHitsPad != endHitsPad
511 && computeChordThroughShape( track, pad, track->GetLayer(),
512 startHitsPad ? track->GetStart() : track->GetEnd() )
513 < track->GetWidth() )
514 {
515 continue;
516 }
517
518 // Skip case where pad and the track are within a copper zone with the same net
519 // (and the pad can be connected to the zone)
520 if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
521 continue;
522
523 // A track crossing the pad earns one teardrop per side, not one over the whole track.
524 if( !startHitsPad && !endHitsPad && track->HitTest( pad->GetPosition() ) )
525 {
526 PCB_TRACK stub( m_board );
527
528 buildCrossingStub( stub, track, pad->GetPosition() );
529
530 stub.SetStart( track->GetEnd() );
532 track, pad, pad->GetPosition(),
533 teardropUuid( track, pad, 0 ) );
534 stub.SetStart( track->GetStart() );
536 track, pad, pad->GetPosition(),
537 teardropUuid( track, pad, 1 ) );
538 }
539 else
540 {
542 track, pad, pad->GetPosition(),
543 teardropUuid( track, pad, 0 ) );
544 }
545 }
546
547 for( PCB_VIA* via : connectedVias )
548 {
549 if( !forceUpdate && !dirtyPadViaSet.count( via ) )
550 continue;
551
552 TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
553 int annularWidth = via->GetWidth( track->GetLayer() );
554
555 if( !tdParams.m_Enabled )
556 continue;
557
558 // Ensure a teardrop shape can be built: track width must be < teardrop width and
559 // filter width. A max width of 0 means no limit, not "nothing fits".
560 if( ( tdParams.m_TdMaxWidth > 0 && track->GetWidth() >= tdParams.m_TdMaxWidth )
561 || track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
562 || track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
563 {
564 continue;
565 }
566
567 bool startHitsVia = via->HitTest( track->GetStart() );
568 bool endHitsVia = via->HitTest( track->GetEnd() );
569
570 // The track is entirely inside the via; cannot create a teardrop
571 if( startHitsVia && endHitsVia )
572 continue;
573
574 // Reject tangential grazes, but keep short radial entries.
575 if( startHitsVia != endHitsVia
576 && computeChordThroughShape( track, via, track->GetLayer(),
577 startHitsVia ? track->GetStart() : track->GetEnd() )
578 < track->GetWidth() )
579 {
580 continue;
581 }
582
583 // As for pads, a track that merely crosses the via earns a teardrop on each side.
584 if( !startHitsVia && !endHitsVia && track->HitTest( via->GetPosition() ) )
585 {
586 PCB_TRACK stub( m_board );
587
588 buildCrossingStub( stub, track, via->GetPosition() );
589
590 stub.SetStart( track->GetEnd() );
592 track, via, via->GetPosition(),
593 teardropUuid( track, via, 0 ) );
594 stub.SetStart( track->GetStart() );
596 track, via, via->GetPosition(),
597 teardropUuid( track, via, 1 ) );
598 }
599 else
600 {
602 track, via, via->GetPosition(),
603 teardropUuid( track, via, 0 ) );
604 }
605 }
606 }
607
608 if( ( aForceFullUpdate || !dirtyTracks->empty() )
609 && m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
610 {
611 AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate, false );
612 }
613
614 // Now set priority of teardrops now all teardrops are added
616}
617
618
620{
621 for( ZONE* zone : m_board->Zones() )
622 {
623 if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
624 zone->SetFlags( STRUCT_DELETED );
625 }
626
627 m_board->BulkRemoveStaleTeardrops( aCommit );
628}
629
630
632{
633 // Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
634 // to know the zone layer of a teardrop
635
636 unsigned priority_base = MAGIC_TEARDROP_ZONE_ID;
637
638 // The sort function to sort by increasing copper layers. Group by layers.
639 // For same layers sort by decreasing areas
640 struct
641 {
642 bool operator()(ZONE* a, ZONE* b) const
643 {
644 if( a->GetFirstLayer() == b->GetFirstLayer() )
645 {
646 if( a->GetOutlineArea() != b->GetOutlineArea() )
647 return a->GetOutlineArea() > b->GetOutlineArea();
648 return a->m_Uuid < b->m_Uuid; // stable tiebreak
649 }
650 return a->GetFirstLayer() < b->GetFirstLayer();
651
652 }
653 } compareLess;
654
655 for( ZONE* td: m_createdTdList )
656 td->CalculateOutlineArea();
657
658 std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
659
660 // Survivors of an incremental update keep their priorities, and equal-priority zones of
661 // different nets do not clear each other, so hand out what the layer still has free.
662 std::set<ZONE*> created( m_createdTdList.begin(), m_createdTdList.end() );
663 std::map<int, std::set<unsigned>> taken;
664
665 for( ZONE* zone : m_board->Zones() )
666 {
667 if( zone->IsTeardropArea() && !created.count( zone ) )
668 taken[zone->GetFirstLayer()].insert( zone->GetAssignedPriority() );
669 }
670
671 int curr_layer = -1;
672
673 for( ZONE* td: m_createdTdList )
674 {
675 if( td->GetFirstLayer() != curr_layer )
676 {
677 curr_layer = td->GetFirstLayer();
678 priority_base = MAGIC_TEARDROP_ZONE_ID;
679 }
680
681 const std::set<unsigned>& layerTaken = taken[curr_layer];
682
683 while( layerTaken.count( priority_base )
684 && priority_base < std::numeric_limits<unsigned>::max() )
685 {
686 priority_base++;
687 }
688
689 td->SetAssignedPriority( priority_base );
690
691 if( priority_base < std::numeric_limits<unsigned>::max() )
692 priority_base++;
693 }
694}
695
696
698 const std::set<PCB_TRACK*>* aTracks,
699 bool aForceFullUpdate, bool aSetPriorities )
700{
701 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
702 TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
703
704 // Explore groups (a group is a set of tracks on the same layer and the same net):
705 for( auto& grp : m_trackLookupList.GetBuffer() )
706 {
707 int layer, netcode;
708 TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
709
710 std::vector<PCB_TRACK*>* sublist = &grp.second;
711
712 if( sublist->size() <= 1 ) // We need at least 2 track segments
713 continue;
714
715 // The sort function to sort by increasing track widths
716 struct
717 {
718 bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
719 { return a->GetWidth() < b->GetWidth(); }
720 } compareLess;
721
722 std::sort( sublist->begin(), sublist->end(), compareLess );
723 int min_width = sublist->front()->GetWidth();
724 int max_width = sublist->back()->GetWidth();
725
726 // Skip groups having the same track thickness
727 if( max_width == min_width )
728 continue;
729
730 for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
731 {
732 PCB_TRACK* track = (*sublist)[ii];
733 int track_len = (int) track->GetLength();
734 bool track_needs_update = aForceFullUpdate || aTracks->contains( track );
735 min_width = track->GetWidth();
736
737 // to avoid creating a teardrop between 2 tracks having similar widths give a threshold
738 params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
739 const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
740 min_width = KiROUND( min_width * th );
741
742 for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
743 {
744 // Search candidates with thickness > curr thickness
745 PCB_TRACK* candidate = (*sublist)[jj];
746
747 if( min_width >= candidate->GetWidth() )
748 continue;
749
750 // Cannot build a teardrop on a too short track segment.
751 // The min len is > candidate radius
752 if( track_len <= candidate->GetWidth() /2 )
753 continue;
754
755 // Now test end to end connection:
756 EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
757 // 0, STARTPOINT, ENDPOINT
758
759 VECTOR2I pos = candidate->GetStart();
760 match_points = track->IsPointOnEnds( pos, m_tolerance );
761
762 if( !match_points )
763 {
764 pos = candidate->GetEnd();
765 match_points = track->IsPointOnEnds( pos, m_tolerance );
766 }
767
768 if( !match_points )
769 continue;
770
771 // An untouched pair's teardrop was not removed as stale, so building another
772 // would duplicate it, and its UUID, on every edit elsewhere on the board.
773 if( !track_needs_update && !aTracks->contains( candidate ) )
774 continue;
775
776 // Pads/vias have priority for teardrops; ensure there isn't one at our position
777 bool existingPadOrVia = false;
778 std::vector<PAD*> connectedPads;
779 std::vector<PCB_VIA*> connectedVias;
780
781 connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
782
783 for( PAD* pad : connectedPads )
784 {
785 if( pad->HitTest( pos ) )
786 existingPadOrVia = true;
787 }
788
789 for( PCB_VIA* via : connectedVias )
790 {
791 if( via->HitTest( pos ) )
792 existingPadOrVia = true;
793 }
794
795 if( existingPadOrVia )
796 continue;
797
799 track, candidate, pos,
800 teardropUuid( track, candidate, 0 ) );
801 }
802 }
803 }
804
805 // The global edit dialog calls this directly, and a teardrop left at the default priority is
806 // outranked by every pour. UpdateTeardrops() has more to add, so it numbers them itself.
807 if( aSetPriorities )
809}
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:85
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
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 coord_type GetY() const
Definition box2.h:205
constexpr coord_type GetX() const
Definition box2.h:204
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
constexpr coord_type GetBottom() const
Definition box2.h:219
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:300
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.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
BOARD * m_board
Definition teardrop.h:334
std::map< PCB_LAYER_ID, KIRTREE::DYNAMIC_RTREE< ZONE *, int, 2 > > m_createdTdRTree
The teardrops of this pass by layer, since m_copperRTree cannot take inserts once built.
Definition teardrop.h:343
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
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:437
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:243
void setTeardropPriorities()
Set priority of created teardrops.
Definition teardrop.cpp:631
TRACK_BUFFER m_trackLookupList
Definition teardrop.h:339
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:697
TEARDROP_PARAMETERS_LIST * m_prmsList
Definition teardrop.h:336
std::vector< ZONE * > m_createdTdList
Definition teardrop.h:340
void DeleteTrackToTrackTeardrops(BOARD_COMMIT &aCommit)
Definition teardrop.cpp:619
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:224
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.
friend class TEARDROP_PARAMETERS
Definition teardrop.h:88
TOOL_MANAGER * m_toolManager
Definition teardrop.h:335
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:53
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:1595
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:608
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:675
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:657
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:1972
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:862
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:630
This file is part of the common library.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
#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:752
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