KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_via_stitch.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <pcb_via_stitch.h>
25
27#include <pcb_base_edit_frame.h>
28#include <tool/tool_manager.h>
30#include <board_commit.h>
31
32#include <cmath>
33#include <map>
34#include <vector>
35
36#include <boost/random/mersenne_twister.hpp>
37#include <boost/random/uniform_real_distribution.hpp>
38
40#include <geometry/seg.h>
42#include <geometry/shape_rect.h>
44#include <gal/painter.h>
45#include <tool/action_menu.h>
46#include <tools/pcb_actions.h>
47#include <view/view.h>
48#include <zone.h>
49#include <pad.h>
50#include <api/api_enums.h>
51#include <api/api_utils.h>
52#include <api/board/board_types.pb.h>
53#include <google/protobuf/any.pb.h>
54
55#include <pcb_track.h>
56#include <board.h>
59#include <footprint.h>
61#include <drc/drc_engine.h>
62#include <drc/drc_rtree.h>
63#include <drc/drc_rule.h>
64#include <properties/property.h>
67
68const wxString PCB_VIA_STITCH::DISPLAY_NAME = _HKI( "Via Stitching" );
69const wxString PCB_VIA_STITCH::GENERATOR_TYPE = wxS( "via_stitch" );
70
71
72const std::vector<VECTOR2D>& PCB_VIA_STITCH::bakedPoissonTile()
73{
74 static const std::vector<VECTOR2D> samples = POISSON_DISK::ToroidalUnitTile(
76 return samples;
77}
78
79
81 PCB_GENERATOR_POLY( aParent, F_Cu ),
82 m_viaTemplate( std::make_unique<PCB_VIA>( this ) )
83{
86
87
88 m_viaTemplate->SetIsFree( true );
89}
90
91
93 PCB_GENERATOR_POLY( aOther ),
94 m_pitch( aOther.m_pitch ),
95 m_layout( aOther.m_layout ),
96 m_mode( aOther.m_mode ),
97 m_seed( aOther.m_seed ),
98 m_viaTemplate( std::make_unique<PCB_VIA>( *aOther.m_viaTemplate ) ),
100 m_netCode( aOther.m_netCode ),
107{
108 // m_childContextMenu is intentionally not copied
109}
110
111
112PCB_VIA_STITCH::PCB_VIA_STITCH( PCB_VIA_STITCH&& aOther ) noexcept = default;
113
115{
116 if( this != &aOther )
117 {
119 m_pitch = aOther.m_pitch;
120 m_layout = aOther.m_layout;
121 m_mode = aOther.m_mode;
122 m_seed = aOther.m_seed;
123 m_viaTemplate = std::make_unique<PCB_VIA>( *aOther.m_viaTemplate );
125 m_netCode = aOther.m_netCode;
132 // m_childContextMenu is intentionally not copied.
133 m_childContextMenu.reset();
134 }
135 return *this;
136}
137
138PCB_VIA_STITCH& PCB_VIA_STITCH::operator=( PCB_VIA_STITCH&& aOther ) noexcept = default;
139
141
142
144{
145 return m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS );
146}
147
148
150{
151 m_viaTemplate->SetWidth( PADSTACK::ALL_LAYERS, aVal );
152 MarkDirty();
153}
154
155
157{
158 int d = m_viaTemplate->GetDrillValue();
159 return d > 0 ? d : 0;
160}
161
162
164{
165 m_viaTemplate->SetDrill( aVal );
166 MarkDirty();
167}
168
169
170std::vector<std::pair<wxString, const BOARD_ITEM*>> PCB_VIA_STITCH::GetTemplateItems() const
171{
172 return { { wxS( "via" ), m_viaTemplate.get() } };
173}
174
175
176void PCB_VIA_STITCH::SetTemplateItem( const wxString& aName, std::unique_ptr<BOARD_ITEM> aItem )
177{
178 if( aName != wxS( "via" ) || !aItem || aItem->Type() != PCB_VIA_T )
179 return;
180
181 m_viaTemplate.reset( static_cast<PCB_VIA*>( aItem.release() ) );
182 m_viaTemplate->SetParent( this );
183 m_viaTemplate->SetIsFree( true );
184}
185
186void PCB_VIA_STITCH::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
187{
188 if( m_outline.OutlineCount() == 0 )
189 return;
190
191 // Only paint on the outline layer. ViewGetLayers() also lists LAYER_VIAS /
192 // LAYER_VIA_HOLES / LAYER_ANCHOR for the produced vias' rendering, but those are
193 // handled by the via children themselves; we don't want to redraw the outline
194 // multiple times per frame.
195 if( aLayer != LAYER_VIA_STITCHING )
196 return;
197
198 KIGFX::GAL* gal = aView->GetGAL();
199
200 // We are drawing perpendicular ticks along the outline to indicate the stitch zone
201 // It kind of looks like a sewing pattern with the ticks, so it fits.
202 KIGFX::RENDER_SETTINGS* settings = aView->GetPainter()->GetSettings();
203 const KIGFX::COLOR4D borderColor = settings->GetColor( this, LAYER_VIA_STITCHING );
204
205 const SHAPE_LINE_CHAIN& outline = m_outline.COutline( 0 );
206 const int n = outline.PointCount();
207
208 gal->SetIsFill( false );
209 gal->SetIsStroke( true );
210 gal->SetStrokeColor( borderColor );
211 gal->SetLineWidth( pcbIUScale.mmToIU( 0.1 ) );
212 gal->DrawPolyline( outline );
213
214 const double tickSpacing = pcbIUScale.mmToIU( 2.0 );
215 const double tickLength = pcbIUScale.mmToIU( 0.5 );
216
217 // Approximate centroid — used to resolve which perpendicular direction is inward
218 // regardless of polygon winding order.
219 VECTOR2D centroid( 0.0, 0.0 );
220 for( int i = 0; i < n; ++i )
221 {
222 centroid.x += outline.CPoint( i ).x;
223 centroid.y += outline.CPoint( i ).y;
224 }
225 centroid.x /= n;
226 centroid.y /= n;
227
228 gal->SetLineWidth( 1 ); // hairline ticks
229
230 for( int i = 0; i < n; ++i )
231 {
232 const VECTOR2D p1( outline.CPoint( i ) );
233 const VECTOR2D p2( outline.CPoint( ( i + 1 ) % n ) );
234
235 const VECTOR2D edgeVec = p2 - p1;
236 const double edgeLen = edgeVec.EuclideanNorm();
237
238 if( edgeLen < 1.0 )
239 continue;
240
241 const VECTOR2D edgeDir = edgeVec / edgeLen;
242
243 // Pick the perpendicular that points toward the centroid
244 const VECTOR2D edgeMid = ( p1 + p2 ) / 2.0;
245 const VECTOR2D perp = VECTOR2D( -edgeDir.y, edgeDir.x );
246 const VECTOR2D toCentr = centroid - edgeMid;
247 const bool perpToward = ( perp.x * toCentr.x + perp.y * toCentr.y >= 0.0 );
248 const VECTOR2D inward = perpToward ? perp : VECTOR2D( -perp.x, -perp.y );
249
250 for( double t = tickSpacing; t < edgeLen; t += tickSpacing )
251 {
252 const VECTOR2D base = p1 + edgeDir * t;
253 gal->DrawLine( base, base + inward * tickLength );
254 }
255 }
256}
257
258
260{
262
263 // An empty outline has no vertex 0 (e.g. a freshly constructed generator before the
264 // outline is set); fall back to the anchor origin rather than throwing.
265 if( !m_outline.GetRelativeIndices( 0, &index ) )
266 return m_origin;
267
268 return m_outline.CVertex( index );
269}
270
271
273{
274 if( m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS ) <= 0 )
275 m_viaTemplate->SetWidth( PADSTACK::ALL_LAYERS, defaultViaSize( aBoard ) );
276
277 if( m_viaTemplate->GetDrillValue() <= 0 )
278 m_viaTemplate->SetDrill( defaultViaDrill( aBoard ) );
279
280 if( m_pitch <= 0 )
281 {
282 m_pitch = defaultPitch( aBoard );
283
285 }
286}
287
288
290{
291 if( aCommit )
292 {
293 if( IsNew() )
294 aCommit->Add( this );
295 else
296 aCommit->Modify( this );
297 }
298
299 // Initialize property defaults on first edit
300 InitializeDefaults( aBoard );
301
302 // Resolve net code from saved name (SetProperties has no board reference)
303 if( m_netCode == 0 && !m_lastNetName.empty() && aBoard )
304 {
305 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastNetName ) )
306 m_netCode = net->GetNetCode();
307 }
308
309 if( m_guardedNetCode == 0 && !m_lastGuardedNetName.empty() && aBoard )
310 {
311 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastGuardedNetName ) )
312 m_guardedNetCode = net->GetNetCode();
313 }
314
315 SetFlags( IN_EDIT );
316}
317
318
320{
321 int mm2 = pcbIUScale.mmToIU( 2.0 );
322 int viaSize = m_viaTemplate->GetWidth( PADSTACK::TEMP_ALL_LAYERS ) > 0 ? m_viaTemplate->GetWidth( PADSTACK::TEMP_ALL_LAYERS )
323 : defaultViaSize( aBoard );
324 int pitch = std::max( mm2, viaSize * 2 );
325 return pitch;
326}
327
328
330{
331 if( !aBoard )
332 return pcbIUScale.mmToIU( 0.6 );
333
335 int val = ds.GetCurrentViaSize();
336 if( val <= 0 )
337 val = pcbIUScale.mmToIU( 0.6 );
338 return val;
339}
340
342{
343 if( !aBoard )
344 return pcbIUScale.mmToIU( 0.3 );
345
347 int val = ds.GetCurrentViaDrill();
348 if( val <= 0 )
349 val = pcbIUScale.mmToIU( 0.3 );
350 return val;
351}
352
353
355{
356 if( !( GetFlags() & IN_EDIT ) )
357 return false;
358
359 if( !aBoard || !aCommit )
360 return false;
361
362 // Resolve net code from the saved name in case EditStart() was not called
363 // (e.g. programmatic regeneration after load)
364 if( m_netCode == 0 && !m_lastNetName.empty() )
365 {
366 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastNetName ) )
367 m_netCode = net->GetNetCode();
368 }
369
370 if( m_guardedNetCode == 0 && !m_lastGuardedNetName.empty() )
371 {
372 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastGuardedNetName ) )
373 m_guardedNetCode = net->GetNetCode();
374 }
375
376 int detectPitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
377
378 // Avoid accidentally calculating a grid offset change because we just changed layout modes
379 const GRID_CONFIG currentConfig{ m_layout, m_mode, detectPitch };
380 const bool childrenAreOnThisGrid = m_childGridConfig == currentConfig;
381
382 m_childGridConfig = currentConfig;
383
384 // Try to detect origin offset change due to moved via
385 if( usesGridCells() && childrenAreOnThisGrid )
386 {
387 for( BOARD_ITEM* it : GetBoardItems() )
388 {
389 if( it->Type() != PCB_VIA_T )
390 continue;
391
392 VECTOR2I p = static_cast<PCB_VIA*>( it )->GetPosition();
393 VECTOR2I expectedPos = positionForCell( cellForPosition( p ), detectPitch );
394
395 if( expectedPos != p )
396 {
397 int offsetY = ( ( p.y % detectPitch ) + detectPitch ) % detectPitch;
398
399 // The dragged via defines the new grid origin. On a staggered layout an odd
400 // row carries an extra half-pitch x shift, which must be removed before
401 // computing the origin offset or the whole grid re-anchors half a pitch off.
402 // (p.y - offsetY) is an exact multiple of the pitch, so this row index matches
403 // what cellForPosition()/positionForCell() will derive after the re-anchor.
404 int row = ( p.y - offsetY ) / detectPitch;
406 && ( ( row % 2 + 2 ) % 2 != 0 ) ) ? detectPitch / 2 : 0;
407
409 VECTOR2I( ( ( ( p.x - xShift ) % detectPitch ) + detectPitch ) % detectPitch,
410 offsetY );
411 break;
412 }
413 }
414 }
415
416 // Take snapshot of existing vias, we then selectively remove vias only if they should be removed
417 // we want to reduce file churn of KIIDs
418 std::vector<PCB_VIA*> existingVias;
419
420 for( BOARD_ITEM* item : GetBoardItems() )
421 {
422 if( item->Type() == PCB_VIA_T )
423 existingVias.push_back( static_cast<PCB_VIA*>( item ) );
424 }
425
426 bool changed = false;
427
428 auto removeVia =
429 [&]( PCB_VIA* aVia )
430 {
431 RemoveItem( aVia );
432 m_pendingRemovals.insert( aVia );
433 changed = true;
434 };
435
436 if( m_outline.IsEmpty() )
437 {
438 for( PCB_VIA* via : existingVias )
439 removeVia( via );
440
441 m_lastUpdateChangedVias = changed;
442 ClearDirty();
443 return false;
444 }
445
446 int pitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
447
448 if( m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS ) <= 0 )
449 m_viaTemplate->SetWidth( PADSTACK::ALL_LAYERS, defaultViaSize( aBoard ) );
450 if( m_viaTemplate->GetDrillValue() <= 0 )
451 m_viaTemplate->SetDrill( defaultViaDrill( aBoard ) );
452
453 // Compute the set of grid cells where a via fits cleanly (DRC + same-net zone overlap).
454 // TODO, we will use a cache grid
455 std::set<VECTOR2I> placementCells = buildPlacementCells( aBoard );
456
457 // Guard mode currently has to exclude by a proximity heuristic since we aren't using a grid.
458 const int64_t guardExcludeRadiusSq = ( (int64_t) pitch / 4 ) * ( pitch / 4 );
459
460 auto isExcluded =
461 [&]( const VECTOR2I& cell ) -> bool
462 {
463 // Guard vias are walked along the guarded net's envelope, so a stored position
464 // will rarely land exactly on a regenerated via. Match by proximity instead.
466 {
467 for( const VECTOR2I& excluded : m_excludedPositions )
468 {
469 VECTOR2I d = excluded - cell;
470
471 if( (int64_t) d.x * d.x + (int64_t) d.y * d.y < guardExcludeRadiusSq )
472 return true;
473 }
474
475 return false;
476 }
477
478 return usesGridCells() ? m_excludedCells.count( cell ) > 0
479 : m_excludedPositions.count( cell ) > 0;
480 };
481
482 // Get the new list of desired via cells
483 std::map<VECTOR2I, VECTOR2I> desired;
484
485 for( const VECTOR2I& cell : placementCells )
486 {
487 if( !isExcluded( cell ) )
488 desired.emplace( cell, positionForCell( cell, pitch ) );
489 }
490
491 // Determine existing vias we can keep
492 std::map<VECTOR2I, PCB_VIA*> kept;
493
494 for( PCB_VIA* via : existingVias )
495 {
496 VECTOR2I cell = cellForPosition( via->GetPosition() );
497
498 if( desired.count( cell ) && kept.emplace( cell, via ).second )
499 continue;
500
501 removeVia( via );
502 }
503
504 // Vias retired earlier may need to be unretired, this usually occurs when someone
505 // is shrinking the outline and then changes their mind in the same drag
506 std::map<VECTOR2I, PCB_VIA*> resurrectable;
507
509 resurrectable.try_emplace( cellForPosition( via->GetPosition() ), via );
510
511 // Now create a reference via to compare against, we need to see if existing vias need to be nuked
512 // anyway on a property change. In which case we will just regen rather than play games
513 std::unique_ptr<PCB_VIA> ref( static_cast<PCB_VIA*>( m_viaTemplate->Clone() ) );
514 ref->SetParent( aBoard );
515 ref->SetIsFree( true );
516
517 if( m_netCode )
518 ref->SetNetCode( m_netCode );
519
520 auto matchesRef =
521 [&]( const PCB_VIA* aVia ) -> bool
522 {
523 // The padstack carries size, drill, layer span, mask/paste attributes,
524 // backdrill and post-machining. That covers everything the via dialog
525 // can edit except the net and teardrop settings, checked separately.
526 return aVia->GetPosition() == ref->GetPosition()
527 && aVia->GetLayer() == ref->GetLayer()
528 && aVia->GetViaType() == ref->GetViaType()
529 && aVia->Padstack() == ref->Padstack()
530 && aVia->GetNetCode() == ref->GetNetCode()
531 && aVia->GetTeardropParams() == ref->GetTeardropParams();
532 };
533
534 for( const auto& [cell, pt] : desired )
535 {
536 auto it = kept.find( cell );
537
538 if( it != kept.end() )
539 {
540 PCB_VIA* via = it->second;
541
542 ref->SetPosition( pt );
543
544 if( matchesRef( via ) )
545 continue; // unchanged — no commit entry, UUID preserved
546
547 // Just nudge the via
548 ref->SetPosition( via->GetPosition() );
549
550 if( matchesRef( via ) )
551 {
552 aCommit->Modify( via );
553 via->SetPosition( pt );
554 changed = true;
555 continue;
556 }
557
558 removeVia( via );
559 }
560
561 // Resurrect a via retired earlier
562 auto rit = resurrectable.find( cell );
563
564 if( rit != resurrectable.end() )
565 {
566 PCB_VIA* via = rit->second;
567
568 ref->SetPosition( pt );
569
570 if( matchesRef( via ) )
571 {
572 m_pendingRemovals.erase( via );
573 AddItem( via );
574 changed = true;
575 continue;
576 }
577 }
578
579 PCB_VIA* via = static_cast<PCB_VIA*>( m_viaTemplate->Clone() );
580 via->ResetUuidDirect();
581
582 via->SetParent( aBoard );
583 via->SetPosition( pt );
584
585 if( m_netCode )
586 via->SetNetCode( m_netCode );
587
588 // Vias must be marked free to avoid connectivity stealing them away
589 via->SetIsFree( true );
590
591 // If the generator is selected (point editing), make sure to mark new vias
592 // as selected to match
593 if( IsSelected() )
594 via->SetSelected();
595
596 AddItem( via );
597 aCommit->Add( via );
598 changed = true;
599 }
600
601 m_lastUpdateChangedVias = changed;
602 ClearDirty();
603 return true;
604}
605
606
608{
610
611 props.set_iu( "pitch", m_pitch );
612 props.set( "layout", static_cast<int>( m_layout ) );
613 props.set( "mode", static_cast<int>( m_mode ) );
614 props.set( "seed", static_cast<int>( m_seed ) );
615 props.set_iu( "origin_offset_x", m_originOffset.x );
616 props.set_iu( "origin_offset_y", m_originOffset.y );
617
618 // Save the net by name so it survives across sessions (net codes are session-local)
619 if( !m_lastNetName.empty() )
620 props.set( "net_name", m_lastNetName );
621
622 if( !m_lastGuardedNetName.empty() )
623 props.set( "guarded_net_name", m_lastGuardedNetName );
624
625 if( !m_excludedCells.empty() )
626 {
627 std::vector<VECTOR2I> cells( m_excludedCells.begin(), m_excludedCells.end() );
628 props.set( "excluded_grid_cells", wxAny( cells ) );
629 }
630
631 if( !m_excludedPositions.empty() )
632 {
634 for( const VECTOR2I& pos : m_excludedPositions )
635 chain.Append( pos );
636 props.set( "excluded_positions", wxAny( chain ) );
637 }
638
639 // Serialize the single zone outline as a SHAPE_LINE_CHAIN.
640 // The file format natively supports SHAPE_LINE_CHAIN but not SHAPE_POLY_SET directly.
641 if( m_outline.OutlineCount() > 0 )
642 props.set( "outline", wxAny( m_outline.COutline( 0 ) ) );
643
644 return props;
645}
646
647
649{
651
652 aProps.get_to_iu( "pitch", m_pitch );
653
654 if( auto layout = aProps.get_opt<int>( "layout" ) )
655 m_layout = static_cast<PCB_VIA_STITCH_LAYOUT>( *layout );
656
657 if( auto mode = aProps.get_opt<int>( "mode" ) )
658 m_mode = static_cast<PCB_VIA_STITCH_MODE>( *mode );
659
660 if( auto seed = aProps.get_opt<int>( "seed" ) )
661 m_seed = static_cast<uint32_t>( *seed );
662
663 aProps.get_to_iu( "origin_offset_x", m_originOffset.x );
664 aProps.get_to_iu( "origin_offset_y", m_originOffset.y );
665
667
668 // Restore net name; net code is resolved from the board in EditStart()/Update()
669 aProps.get_to( "net_name", m_lastNetName );
670 aProps.get_to( "guarded_net_name", m_lastGuardedNetName );
671
672 m_excludedCells.clear();
673 m_excludedPositions.clear();
674
675
676 if( auto cells = aProps.get_opt<std::vector<VECTOR2I>>( "excluded_grid_cells" ) )
677 m_excludedCells.insert( cells->begin(), cells->end() );
678
679 if( auto chain = aProps.get_opt<SHAPE_LINE_CHAIN>( "excluded_positions" ) )
680 {
681 for( int i = 0; i < chain->PointCount(); ++i )
682 m_excludedPositions.insert( chain->CPoint( i ) );
683 }
684
685 // Restore the single zone outline
686 m_outline.RemoveAllContours();
687
688 if( auto outline = aProps.get_opt<SHAPE_LINE_CHAIN>( "outline" ) )
689 {
690 // A chain parsed from the file is a bare point list; the closed flag is not
691 // serialized and AddOutline() asserts on open chains
692 outline->SetClosed( true );
693 m_outline.AddOutline( *outline );
694 }
695}
696
697
698void PCB_VIA_STITCH::Serialize( google::protobuf::Any& aContainer ) const
699{
700 using namespace kiapi::board::types;
701 using namespace kiapi::common::types;
702
703 ViaStitchArea stitch;
704
705 stitch.mutable_id()->set_value( m_Uuid.AsStdString() );
706 stitch.set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( GetLayer() ) );
707 kiapi::common::PackPolySet( *stitch.mutable_outline(), m_outline );
708 stitch.mutable_pitch()->set_value_nm( pcbIUScale.IUToNm( m_pitch ) );
711
713 {
714 ViaStitchGuardSettings* guard = stitch.mutable_guard_settings();
715
716 if( !m_lastGuardedNetName.empty() )
717 guard->mutable_guarded_net()->set_name( m_lastGuardedNetName.ToUTF8() );
718 }
719 else
720 {
721 ViaStitchGridSettings* grid = stitch.mutable_grid_settings();
722 grid->mutable_origin_offset()->set_x_nm( pcbIUScale.IUToNm( m_originOffset.x ) );
723 grid->mutable_origin_offset()->set_y_nm( pcbIUScale.IUToNm( m_originOffset.y ) );
724 grid->set_seed( m_seed );
725
726 for( const VECTOR2I& cell : m_excludedCells )
727 {
728 GridCell* gridCell = grid->add_excluded_cells();
729 gridCell->set_i( cell.x );
730 gridCell->set_j( cell.y );
731 }
732 }
733
734 if( !m_lastNetName.empty() )
735 stitch.mutable_net()->set_name( m_lastNetName.ToUTF8() );
736
737 for( const VECTOR2I& pos : m_excludedPositions )
738 kiapi::common::PackVector2( *stitch.add_excluded_positions(), pos );
739
740 if( const PCB_VIA* viaTemplate = ViaTemplate() )
741 viaTemplate->Serialize( *stitch.mutable_via_template() );
742
743 stitch.set_locked( IsLocked() ? LockedState::LS_LOCKED : LockedState::LS_UNLOCKED );
744
745 if( const BOARD* board = GetBoard() )
746 stitch.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
747
748 for( EDA_ITEM* member : GetItems() )
749 stitch.add_members()->set_value( member->m_Uuid.AsStdString() );
750
751 kiapi::common::PackCustomProperties( stitch.mutable_custom_properties(), *this );
752 aContainer.PackFrom( stitch );
753}
754
755
756bool PCB_VIA_STITCH::Deserialize( const google::protobuf::Any& aContainer )
757{
758 using namespace kiapi::board::types;
759 using namespace kiapi::common::types;
760
761 ViaStitchArea stitch;
762
763 if( !aContainer.UnpackTo( &stitch ) )
764 return false;
765
766 SetUuidDirect( ::KIID( stitch.id().value() ) );
767 SetLayer( FromProtoEnum<PCB_LAYER_ID>( stitch.layer() ) );
768 m_outline = kiapi::common::UnpackPolySet( stitch.outline() );
769 m_pitch = pcbIUScale.NmToIU( stitch.pitch().value_nm() );
772
773 m_lastNetName = wxString( stitch.net().name().c_str(), wxConvUTF8 );
774 m_lastGuardedNetName.clear();
775 m_netCode = 0;
777
778 m_excludedCells.clear();
779 m_excludedPositions.clear();
780
781 switch( stitch.mode_specific_settings_case() )
782 {
783 case ViaStitchArea::kGridSettings:
784 {
785 const ViaStitchGridSettings& grid = stitch.grid_settings();
786 m_originOffset.x = pcbIUScale.NmToIU( grid.origin_offset().x_nm() );
787 m_originOffset.y = pcbIUScale.NmToIU( grid.origin_offset().y_nm() );
788 m_seed = grid.seed();
789
790 for( const GridCell& cell : grid.excluded_cells() )
791 m_excludedCells.insert( VECTOR2I( cell.i(), cell.j() ) );
792
793 break;
794 }
795
796 case ViaStitchArea::kGuardSettings:
797 m_lastGuardedNetName = wxString( stitch.guard_settings().guarded_net().name().c_str(), wxConvUTF8 );
798 break;
799
800 case ViaStitchArea::MODE_SPECIFIC_SETTINGS_NOT_SET: break;
801 }
802
803 for( const kiapi::common::types::Vector2& pos : stitch.excluded_positions() )
805
806 if( stitch.has_via_template() )
807 {
808 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( this );
809
810 if( !via->Deserialize( stitch.via_template() ) )
811 return false;
812
813 via->SetParent( this );
814 via->SetIsFree( true );
815 m_viaTemplate = std::move( via );
816 }
817
818 SetLocked( stitch.locked() == LockedState::LS_LOCKED );
819 kiapi::common::UnpackCustomProperties( stitch.custom_properties(), *this );
820
821 m_items.clear();
822 m_deserializedItems.clear();
823
824 BOARD* board = GetBoard();
825
826 if( !board )
827 return false;
828
829 for( const kiapi::common::types::KIID& memberId : stitch.members() )
830 {
831 if( EDA_ITEM* item = board->ResolveItem( ::KIID( memberId.value() ), true ) )
832 m_deserializedItems.insert( item );
833 }
834
836
837 return true;
838}
839
840
842{
843 if( !( GetFlags() & IN_EDIT ) )
844 return;
845
846 // The edit session is over, any vias still pending removal can now be committed
847 // for removal.
848 if( aCommit )
849 {
851 aCommit->Remove( via );
852 }
853
854 m_pendingRemovals.clear();
855
857}
858
859
861{
862 if( !( GetFlags() & IN_EDIT ) )
863 return;
864
865 // The commit is being reverted; re-attach retired vias so group membership matches
866 // the pre-edit state they revert to.
868 AddItem( via );
869
870 m_pendingRemovals.clear();
871
873}
874
875
877{
878 if( !m_childContextMenu )
879 {
880 m_childContextMenu = std::make_unique<ACTION_MENU>( true, aTool );
882 }
883
884 return m_childContextMenu.get();
885}
886
887
889{
890 if( !aCommit )
891 return;
892
893 for( BOARD_ITEM* item : GetBoardItems() )
894 aCommit->Remove( item );
895
896 // Retired-but-unpushed vias are detached from the group and would otherwise be
897 // orphaned on the board.
899 aCommit->Remove( via );
900
901 m_pendingRemovals.clear();
902
903 aCommit->Remove( this );
904}
905
906
908{
909 int pitch = m_pitch > 0 ? m_pitch : 1;
910
911 auto roundDiv = []( int a, int b ) -> int
912 {
913 return ( a >= 0 ) ? ( a + b / 2 ) / b : -( ( -a + b / 2 ) / b );
914 };
915
916 // Poisson layout and GUARD mode both place at arbitrary positions, not a grid —
917 // cell index == absolute position.
918 if( !usesGridCells() )
919 return aPos;
920
921 int row = roundDiv( aPos.y - m_originOffset.y, pitch );
922 int xShift = ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( row % 2 + 2 ) % 2 != 0 ) )
923 ? pitch / 2
924 : 0;
925 int col = roundDiv( aPos.x - m_originOffset.x - xShift, pitch );
926
927 return VECTOR2I( col, row );
928}
929
930
931VECTOR2I PCB_VIA_STITCH::positionForCell( const VECTOR2I& aCell, int aPitch ) const
932{
933 if( !usesGridCells() )
934 return aCell;
935
936 int xShift = ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( aCell.y % 2 + 2 ) % 2 != 0 ) )
937 ? aPitch / 2
938 : 0;
939 return VECTOR2I( aCell.x * aPitch + m_originOffset.x + xShift,
940 aCell.y * aPitch + m_originOffset.y );
941}
942
943
944std::set<VECTOR2I> PCB_VIA_STITCH::buildPlacementCells( BOARD* aBoard ) const
945{
946 std::set<VECTOR2I> cells;
947
948 if( !aBoard || m_outline.IsEmpty() )
949 return cells;
950
951 int pitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
952 int viaSize = m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS ) > 0 ? m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS )
953 : defaultViaSize( aBoard );
954
955 if( GetNetCode() == 0 )
956 return cells;
957
958 DRC_ENGINE* drcEngine = aBoard->GetDesignSettings().m_DRCEngine.get();
959
960 DRC_CACHE_GENERATOR cacheGenerator;
961 cacheGenerator.SetDRCEngine( drcEngine );
962 cacheGenerator.Run();
963
964 // Pre-clip every zone fill to the stitch outline bbox before adding it to perLayerFills.
965 // Without this, a board-wide GND zone would drag in an entire complex polygon when we may
966 // only be stitching part of it.
967 // This also helps us skip same net zones entirely if they aren't in here
968 BOX2I stitchBBox = m_outline.BBox();
969
970 SHAPE_POLY_SET stitchClip;
971 stitchClip.NewOutline();
972 stitchClip.Append( stitchBBox.GetLeft(), stitchBBox.GetTop() );
973 stitchClip.Append( stitchBBox.GetRight(), stitchBBox.GetTop() );
974 stitchClip.Append( stitchBBox.GetRight(), stitchBBox.GetBottom() );
975 stitchClip.Append( stitchBBox.GetLeft(), stitchBBox.GetBottom() );
976
977 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> perLayerFills;
978
979 for( ZONE* zone : collectAllZones( aBoard ) )
980 {
981 if( zone->GetIsRuleArea() )
982 continue;
983
984 if( zone->GetNetCode() != GetNetCode() )
985 continue;
986
987 if( !zone->GetBoundingBox().Intersects( stitchBBox ) )
988 continue;
989
990 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
991 {
992 SHAPE_POLY_SET clipped;
993
995 {
996 clipped = zone->GetBoardOutline();
997 }
998 else
999 {
1000 SHAPE_POLY_SET* fill = zone->GetFill( layer );
1001
1002 if( !fill || fill->IsEmpty() )
1003 continue;
1004
1005 clipped = *fill;
1006 }
1007
1008 if( clipped.IsEmpty() )
1009 continue;
1010
1011 clipped.BooleanIntersection( stitchClip );
1012
1013 if( clipped.OutlineCount() == 0 )
1014 continue;
1015
1016 perLayerFills[layer].BooleanAdd( clipped );
1017 }
1018 }
1019
1020 // Extract the via copper layers, the layer stack may contain the mask layers
1021 LSET boardCopperLayers = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
1022 LSET viaCopperLayers = m_viaTemplate->GetLayerSet() & boardCopperLayers;
1023
1024 // Filter down to just those via layers that actually have same-net copper on them.
1025 std::vector<const SHAPE_POLY_SET*> viaLayerFills;
1026
1027 for( PCB_LAYER_ID layer : viaCopperLayers )
1028 {
1029 auto it = perLayerFills.find( layer );
1030
1031 if( it != perLayerFills.end() && !it->second.IsEmpty() )
1032 viaLayerFills.push_back( &it->second );
1033 }
1034
1035 // Is there even enough layers left to stitch?
1036 if( viaLayerFills.size() < 2 )
1037 return cells;
1038
1039 // Now build the region where a via would stitch at least two layers together.
1040 SHAPE_POLY_SET allowedRegion;
1041 SHAPE_POLY_SET coveredSoFar = *viaLayerFills[0];
1042
1043 for( size_t i = 1; i < viaLayerFills.size(); ++i )
1044 {
1045 SHAPE_POLY_SET overlap = *viaLayerFills[i];
1046 overlap.BooleanIntersection( coveredSoFar );
1047
1048 if( !overlap.IsEmpty() )
1049 allowedRegion.BooleanAdd( overlap );
1050
1051 if( i + 1 < viaLayerFills.size() )
1052 coveredSoFar.BooleanAdd( *viaLayerFills[i] );
1053 }
1054
1055 if( allowedRegion.OutlineCount() == 0 )
1056 return cells;
1057
1058 // Now intersect it with the via stitch outline
1059 allowedRegion.BooleanIntersection( m_outline );
1060
1061 if( allowedRegion.OutlineCount() == 0 )
1062 return cells;
1063
1064 // Generate a obstacle mask for via placement
1065 // Rather than DRC-probing every possible placement cell against the r-tree
1066 // to test for collisions. We instead create a keep-out polygon with obstacles
1067 // inflated by the via-radius and clearance we want. The result is now
1068 // we only have to do a point-in-polygon test for placement
1069
1070 const int polyApproxError = pcbIUScale.mmToIU( 0.005 );
1071
1072 // Require the whole via, not just its center, to land on same-net copper inside the
1073 // outline
1074 allowedRegion.Deflate( viaSize / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, polyApproxError );
1075
1076 if( allowedRegion.OutlineCount() == 0 )
1077 return cells;
1078
1079 // Probe used for clearance evaluation; it must carry the stitch net because clearance
1080 // rules can key off the net class.
1081 std::unique_ptr<PCB_VIA> probe( static_cast<PCB_VIA*>( m_viaTemplate->Clone() ) );
1082 probe->SetParent( const_cast<BOARD*>( aBoard ) );
1083
1084 if( m_netCode )
1085 probe->SetNetCode( m_netCode );
1086
1087 const int worstClearance = aBoard->m_DRCMaxClearance;
1088
1089 BOX2I queryBBox = stitchBBox;
1090 queryBBox.Inflate( worstClearance + viaSize );
1091
1092 SHAPE_POLY_SET queryClip;
1093 queryClip.NewOutline();
1094 queryClip.Append( queryBBox.GetLeft(), queryBBox.GetTop() );
1095 queryClip.Append( queryBBox.GetRight(), queryBBox.GetTop() );
1096 queryClip.Append( queryBBox.GetRight(), queryBBox.GetBottom() );
1097 queryClip.Append( queryBBox.GetLeft(), queryBBox.GetBottom() );
1098
1099 SHAPE_POLY_SET obstacles;
1100
1101 int viaDrill = m_viaTemplate->GetDrillValue() > 0 ? m_viaTemplate->GetDrillValue()
1102 : defaultViaDrill( aBoard );
1103
1104 auto evalConstraint =
1105 [&]( DRC_CONSTRAINT_T aType, BOARD_ITEM* aOther, PCB_LAYER_ID aLayer ) -> int
1106 {
1107 if( !drcEngine )
1108 return 0;
1109
1110 DRC_CONSTRAINT constraint =
1111 drcEngine->EvalRules( aType, probe.get(), aOther, aLayer );
1112 return constraint.GetValue().Min();
1113 };
1114
1115 auto isSameNet =
1116 [&]( BOARD_ITEM* aOther ) -> bool
1117 {
1118 BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
1119 return cItem && cItem->GetNetCode() == GetNetCode();
1120 };
1121
1122 // Drilled holes are obstacles as we need to respect the hole to hole constraint
1123 std::set<BOARD_ITEM*> holeSeen;
1124
1125 auto addHoleObstacle =
1126 [&]( BOARD_ITEM* aOther, PCB_LAYER_ID aLayer )
1127 {
1128 if( !aOther->HasHole() || !holeSeen.insert( aOther ).second )
1129 return;
1130
1131 std::shared_ptr<SHAPE_SEGMENT> hole = aOther->GetEffectiveHoleShape();
1132
1133 if( !hole )
1134 return;
1135
1136 int margin = viaDrill / 2
1137 + evalConstraint( HOLE_TO_HOLE_CONSTRAINT, aOther, UNDEFINED_LAYER );
1138
1139 if( !isSameNet( aOther ) )
1140 {
1141 margin = std::max( margin,
1142 viaSize / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT,
1143 aOther, aLayer ) );
1144 }
1145
1146 TransformOvalToPolygon( obstacles, hole->GetSeg().A, hole->GetSeg().B,
1147 hole->GetWidth() + 2 * margin, polyApproxError,
1148 ERROR_OUTSIDE );
1149 };
1150
1151 SHAPE_RECT queryRect( queryBBox.GetPosition(), queryBBox.GetWidth(), queryBBox.GetHeight() );
1152
1153 for( PCB_LAYER_ID layer : viaCopperLayers )
1154 {
1155 std::set<BOARD_ITEM*> seen;
1156
1157 auto enumerate =
1158 [&]( BOARD_ITEM* aOther ) -> bool
1159 {
1160 if( !seen.insert( aOther ).second )
1161 return false;
1162
1163 // Our own children are regenerated along with us and must not block
1164 // their own cells.
1165 if( aOther->GetParentGroup() == static_cast<const EDA_GROUP*>( this ) )
1166 return false;
1167
1168 // Same for children retired during the same edit: they are
1169 // detached from the group but stay on the board until the commit is
1170 // pushed, and their cells must stay placeable so they can resurrect.
1171 if( aOther->Type() == PCB_VIA_T
1172 && m_pendingRemovals.count( static_cast<PCB_VIA*>( aOther ) ) )
1173 {
1174 return false;
1175 }
1176
1177 addHoleObstacle( aOther, layer );
1178
1179 bool sameNet = isSameNet( aOther );
1180
1181 if( aOther->Type() == PCB_PAD_T )
1182 {
1183 // A copper-zone can be placed with thermal reliefs turned off
1184 // We don't want to accidentally place vias on top of that pad
1185 // So process the PAD as a obstacle we need to clear
1186 PAD* pad = static_cast<PAD*>( aOther );
1187
1188 if( !pad->FlashLayer( layer ) )
1189 return false;
1190 }
1191 else if( aOther->Type() != PCB_VIA_T && sameNet )
1192 {
1193 return false;
1194 }
1195
1196 // Our copper against theirs.
1197 int margin = viaSize / 2 + evalConstraint( CLEARANCE_CONSTRAINT, aOther, layer );
1198
1199 // Our drill against their copper (hole clearance), cross-net only.
1200 if( !sameNet )
1201 {
1202 margin = std::max( margin,
1203 viaDrill / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT,
1204 aOther, layer ) );
1205 }
1206
1207 aOther->TransformShapeToPolygon( obstacles, layer, margin, polyApproxError,
1208 ERROR_OUTSIDE );
1209 return false;
1210 };
1211
1212 if( aBoard->m_CopperItemRTreeCache )
1213 {
1214 aBoard->m_CopperItemRTreeCache->CheckColliding( &queryRect, layer, worstClearance,
1215 enumerate );
1216 }
1217 }
1218
1219 // Add NPTH mounting holes which have no copper
1220 for( FOOTPRINT* footprint : aBoard->Footprints() )
1221 {
1222 if( !footprint->GetBoundingBox().Intersects( queryBBox ) )
1223 continue;
1224
1225 for( PAD* pad : footprint->Pads() )
1226 {
1227 if( pad->GetBoundingBox().Intersects( queryBBox ) )
1228 addHoleObstacle( pad, F_Cu );
1229 }
1230 }
1231
1232 obstacles.Simplify();
1233
1234 for( ZONE* zone : collectAllZones( aBoard ) )
1235 {
1236 if( !zone->GetBoundingBox().Intersects( queryBBox ) )
1237 continue;
1238
1239 if( zone->GetIsRuleArea() )
1240 {
1241 // Honor via keepouts (any net).
1242 if( !zone->GetDoNotAllowVias() || !( zone->GetLayerSet() & viaCopperLayers ).any() )
1243 continue;
1244
1245 SHAPE_POLY_SET area = zone->GetBoardOutline();
1246 area.BooleanIntersection( queryClip );
1247
1248 if( !area.IsEmpty() )
1249 {
1251 polyApproxError );
1252 obstacles.BooleanAdd( area );
1253 }
1254
1255 continue;
1256 }
1257
1258 if( zone->GetNetCode() == GetNetCode() )
1259 continue;
1260
1261 // A different-net zone only blocks near its fill outside its own outline.
1262 // A via dropped inside the zone is fine because the refill punches an anti-pad around it.
1263 for( PCB_LAYER_ID layer : LSET( zone->GetLayerSet() & viaCopperLayers ) )
1264 {
1265 SHAPE_POLY_SET* fill = zone->GetFill( layer );
1266
1267 if( !fill || fill->IsEmpty() )
1268 continue;
1269
1270 SHAPE_POLY_SET nearFill = *fill;
1271 nearFill.BooleanIntersection( queryClip );
1272
1273 if( nearFill.IsEmpty() )
1274 continue;
1275
1276 int zoneMargin = std::max(
1277 viaSize / 2 + evalConstraint( CLEARANCE_CONSTRAINT, zone, layer ),
1278 viaDrill / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT, zone, layer ) );
1279
1280 nearFill.Inflate( zoneMargin, CORNER_STRATEGY::ROUND_ALL_CORNERS, polyApproxError );
1281
1282 SHAPE_POLY_SET interior = zone->GetBoardOutline();
1283 interior.BooleanIntersection( queryClip );
1284 nearFill.BooleanSubtract( interior );
1285
1286 if( !nearFill.IsEmpty() )
1287 obstacles.BooleanAdd( nearFill );
1288 }
1289 }
1290
1291 allowedRegion.BooleanSubtract( obstacles );
1292
1293 if( allowedRegion.OutlineCount() == 0 )
1294 return cells;
1295
1296 allowedRegion.BuildBBoxCaches();
1297
1298 BOX2I bbox = allowedRegion.BBox();
1299
1300 auto isValid =
1301 [&]( const VECTOR2I& pt ) -> bool
1302 {
1303 return allowedRegion.Contains( pt, -1, 0, true );
1304 };
1305
1307 {
1308 // Walk the perimeter of each guarded-net item's clearance envelope, dropping
1309 // candidate vias every `pitch` of arc length. The envelope is the item's shape
1310 // inflated by (viaRadius + DRC clearance), so a via dropped on it sits at the
1311 // closest legal distance from the trace
1312 if( m_guardedNetCode == 0 )
1313 return cells;
1314
1315 // Merge every guarded-net item's clearance envelope into one polygon, and their bare
1316 // shapes into another so the sampler can tell facing vias from same-side ones.
1317 SHAPE_POLY_SET mergedEnvelope;
1318 SHAPE_POLY_SET mergedGuarded;
1319
1320 for( PCB_TRACK* track : aBoard->Tracks() )
1321 {
1322 if( track->GetNetCode() != m_guardedNetCode )
1323 continue;
1324
1325 if( !track->GetBoundingBox().Intersects( stitchBBox ) )
1326 continue;
1327
1328 PCB_LAYER_ID layer = track->GetLayer();
1329
1330 if( track->Type() == PCB_VIA_T )
1331 layer = F_Cu; // arbitrary copper layer for clearance evaluation
1332
1333 int requiredClearance = evalConstraint( CLEARANCE_CONSTRAINT, track, layer );
1334
1335 // Safety margin so envelope samples land outside the obstacle mask. The
1336 // guarded items are themselves obstacles, polygonized outward with up to
1337 // polyApproxError of overshoot, so the envelope must sit at least that much
1338 // further out; the extra 2µm absorbs the sample coordinates' integer rounding.
1339 const int safetyMargin = polyApproxError + pcbIUScale.mmToIU( 0.002 );
1340 int margin = viaSize / 2 + requiredClearance + safetyMargin;
1341
1342 SHAPE_POLY_SET envelope;
1343 track->TransformShapeToPolygon( envelope, layer, margin, polyApproxError,
1344 ERROR_OUTSIDE );
1345
1346 mergedEnvelope.BooleanAdd( envelope );
1347
1348 track->TransformShapeToPolygon( mergedGuarded, layer, 0, polyApproxError,
1349 ERROR_OUTSIDE );
1350 }
1351
1352 mergedEnvelope.Simplify();
1353 mergedGuarded.Simplify();
1354
1355 for( const VECTOR2I& pt : SampleGuardEnvelope( mergedEnvelope, mergedGuarded, pitch,
1356 isValid ) )
1357 {
1358 cells.insert( pt );
1359 }
1360
1361 return cells;
1362 }
1363
1365 {
1366 // We tile a one time generated toroidal Poisson pattern
1367 // across the bbox at (pitch * POISSON_TILE_PITCHES) per tile. The tiling is
1368 // anchored to the global (0, 0) origin plus a per-seed sub-tile shift.
1369 // This gives us a deterministic but non-grid distribution
1370 const std::vector<VECTOR2D>& tile = bakedPoissonTile();
1371 const int tileSize = std::max( 1, pitch * POISSON_TILE_PITCHES );
1372
1373 // Per-seed sub-tile origin shift in [0, tileSize).
1374 boost::random::mt19937 seedRng( m_seed );
1375 boost::random::uniform_real_distribution<double> uniform( 0.0, 1.0 );
1376 const double offsetX = uniform( seedRng ) * tileSize;
1377 const double offsetY = uniform( seedRng ) * tileSize;
1378
1379 // Range of tiles that intersect bbox. Tile (tx, ty) covers
1380 // [tx*tileSize + offset, (tx+1)*tileSize + offset).
1381 const int firstTileX = (int) std::floor( ( bbox.GetX() - offsetX ) / (double) tileSize );
1382 const int firstTileY = (int) std::floor( ( bbox.GetY() - offsetY ) / (double) tileSize );
1383 const int lastTileX = (int) std::floor( ( bbox.GetRight() - offsetX ) / (double) tileSize );
1384 const int lastTileY = (int) std::floor( ( bbox.GetBottom() - offsetY ) / (double) tileSize );
1385
1386 for( int ty = firstTileY; ty <= lastTileY; ++ty )
1387 {
1388 for( int tx = firstTileX; tx <= lastTileX; ++tx )
1389 {
1390 for( const VECTOR2D& s : tile )
1391 {
1392 VECTOR2I pt(
1393 (int) std::round( offsetX + ( tx + s.x ) * tileSize ),
1394 (int) std::round( offsetY + ( ty + s.y ) * tileSize ) );
1395
1396 if( !bbox.Contains( pt ) )
1397 continue;
1398
1399 if( isValid( pt ) )
1400 cells.insert( pt ); // Poisson "cell" == absolute position
1401 }
1402 }
1403 }
1404
1405 return cells;
1406 }
1407 else
1408 {
1409 // Anchor the grid to global (0, 0) + m_originOffset.
1410 int startRow = (int) std::floor( double( bbox.GetY() - m_originOffset.y ) / pitch );
1411 int startCol = (int) std::floor( double( bbox.GetX() - m_originOffset.x ) / pitch );
1412 VECTOR2I origin( startCol * pitch + m_originOffset.x, startRow * pitch + m_originOffset.y );
1413
1414 for( int row = startRow, y = origin.y; y <= bbox.GetBottom(); y += pitch, ++row )
1415 {
1416 // Odd rows are shifted right by half the pitch when stagger is enabled.
1417 int xOffset =
1418 ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( row % 2 + 2 ) % 2 != 0 ) ) ? pitch / 2 : 0;
1419
1420 for( int col = startCol, x = origin.x + xOffset; x <= bbox.GetRight(); x += pitch, ++col )
1421 {
1422 VECTOR2I pt( x, y );
1423
1424 if( isValid( pt ) )
1425 cells.insert( VECTOR2I( col, row ) );
1426 }
1427 }
1428
1429 return cells;
1430 }
1431}
1432
1433
1435{
1436 std::vector<ZONE*> result;
1437
1438 // A regeneration that added, moved, or removed nothing can't have changed any
1439 // foreign-net anti-pads, so no refill is needed.
1441 return result;
1442
1443 const BOARD* brd = GetBoard();
1444
1445 if( !brd )
1446 return result;
1447
1448 BOX2I myBBox = GetBoundingBox();
1449
1450 // Lets return all intersecting zones with different nets
1451 for( ZONE* zone : collectAllZones( brd ) )
1452 {
1453 if( zone->GetIsRuleArea() )
1454 continue;
1455
1456 if( zone->GetNetCode() == m_netCode )
1457 continue;
1458
1459 if( !zone->GetBoundingBox().Intersects( myBBox ) )
1460 continue;
1461
1462 result.push_back( zone );
1463 }
1464
1465 return result;
1466}
1467
1468
1469void PCB_VIA_STITCH::OnZoneFillChanged( const std::vector<ZONE*>& aZones )
1470{
1471 if( m_netCode == 0 )
1472 return;
1473
1474 BOX2I myBBox = GetBoundingBox();
1475
1476 for( ZONE* zone : aZones )
1477 {
1478 if( zone->GetNetCode() != m_netCode )
1479 continue;
1480
1481 if( !zone->GetBoundingBox().Intersects( myBBox ) )
1482 continue;
1483
1484 MarkDirty();
1485 return;
1486 }
1487}
1488
1489
1491{
1492 BOARD_COMMIT commit( aEditFrame );
1493 GENERATOR_TOOL* genTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
1494
1495 commit.Modify( this );
1496
1497 DIALOG_VIA_STITCH_PROPERTIES dlg( aEditFrame, this );
1498
1499 if( dlg.ShowModal() != wxID_OK )
1500 return;
1501
1502 EditStart( genTool, GetBoard(), &commit );
1503 Update( genTool, GetBoard(), &commit );
1504 EditFinish( genTool, GetBoard(), &commit );
1505
1506 commit.Push( _( "Edit Via Stitching" ) );
1507}
1508
1509
1511{
1512 if( usesGridCells() )
1513 m_excludedCells.insert( cellForPosition( aPos ) );
1514 else
1515 m_excludedPositions.insert( aPos );
1516
1517 MarkDirty();
1518}
1519
1520
1522{
1523 if( usesGridCells() )
1524 m_excludedCells.erase( cellForPosition( aPos ) );
1525 else
1526 m_excludedPositions.erase( aPos );
1527
1528 MarkDirty();
1529}
1530
1531
1533{
1534 m_excludedCells.clear();
1535 m_excludedPositions.clear();
1536 MarkDirty();
1537}
1538
1539
1541{
1542 if( m_netCode == 0 && !m_lastNetName.empty() )
1543 {
1544 if( const BOARD* board = GetBoard() )
1545 {
1546 if( NETINFO_ITEM* net = board->FindNet( m_lastNetName ) )
1547 m_netCode = net->GetNetCode();
1548 }
1549 }
1550
1551 return m_netCode;
1552}
1553
1554
1556{
1557 if( m_guardedNetCode == 0 && !m_lastGuardedNetName.empty() )
1558 {
1559 if( const BOARD* board = GetBoard() )
1560 {
1561 if( NETINFO_ITEM* net = board->FindNet( m_lastGuardedNetName ) )
1562 m_guardedNetCode = net->GetNetCode();
1563 }
1564 }
1565
1566 return m_guardedNetCode;
1567}
1568
1569
1570void PCB_VIA_STITCH::SetNetCode( int aNetCode )
1571{
1572 m_netCode = aNetCode;
1573 if( BOARD* board = GetBoard() )
1574 {
1575 if( NETINFO_ITEM* net = board->FindNet( aNetCode ) )
1576 m_lastNetName = net->GetNetname();
1577 else
1578 m_lastNetName.clear();
1579 }
1580
1581 for( BOARD_ITEM* item : GetBoardItems() )
1582 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
1583 bci->SetNetCode( aNetCode );
1584
1585 MarkDirty();
1586}
1587
1588
1590{
1591 m_guardedNetCode = aNetCode;
1592
1593 if( BOARD* board = GetBoard() )
1594 {
1595 if( NETINFO_ITEM* net = board->FindNet( aNetCode ) )
1596 m_lastGuardedNetName = net->GetNetname();
1597 else
1598 m_lastGuardedNetName.clear();
1599 }
1600
1601 MarkDirty();
1602}
1603
1604
1605std::vector<ZONE*> PCB_VIA_STITCH::collectAllZones( const BOARD* aBoard )
1606{
1607 std::vector<ZONE*> zones;
1608
1609 if( !aBoard )
1610 return zones;
1611
1612 zones.reserve( aBoard->Zones().size() );
1613
1614 for( ZONE* zone : aBoard->Zones() )
1615 zones.push_back( zone );
1616
1617 for( FOOTPRINT* footprint : aBoard->Footprints() )
1618 {
1619 for( ZONE* zone : footprint->Zones() )
1620 zones.push_back( zone );
1621 }
1622
1623 return zones;
1624}
1625
1626
1627std::vector<VECTOR2I>
1629 const SHAPE_POLY_SET& aGuarded, int aPitch,
1630 const std::function<bool( const VECTOR2I& )>& aIsValid )
1631{
1632 std::vector<VECTOR2I> accepted;
1633
1634 if( aPitch <= 0 )
1635 return accepted;
1636
1637 // Keeps adjacent same-side vias from doubling up where the walk rounds a corner or an
1638 // end cap. Arc length will report back a larger than expected value in this case.
1639 const int64_t minDistSq = (int64_t) ( aPitch * 0.7 ) * (int64_t) ( aPitch * 0.7 );
1640
1641 auto farEnough =
1642 [&]( const VECTOR2I& pt ) -> bool
1643 {
1644 for( const VECTOR2I& other : accepted )
1645 {
1646 VECTOR2I d = other - pt;
1647
1648 if( (int64_t) d.x * d.x + (int64_t) d.y * d.y >= minDistSq )
1649 continue;
1650
1651 if( aGuarded.Collide( SEG( pt, other ) ) )
1652 continue;
1653
1654 return false;
1655 }
1656
1657 return true;
1658 };
1659
1660 auto walkChain =
1661 [&]( const SHAPE_LINE_CHAIN& chain )
1662 {
1663 double cursor = 0.0;
1664 double nextSample = aPitch / 2.0;
1665
1666 for( int i = 0; i < chain.SegmentCount(); ++i )
1667 {
1668 SEG seg = chain.CSegment( i );
1669 double segLen = ( VECTOR2D( seg.B ) - VECTOR2D( seg.A ) ).EuclideanNorm();
1670
1671 if( segLen < 1.0 )
1672 continue;
1673
1674 while( nextSample <= cursor + segLen )
1675 {
1676 double t = ( nextSample - cursor ) / segLen;
1677 VECTOR2I pt( (int) std::round( seg.A.x + t * ( seg.B.x - seg.A.x ) ),
1678 (int) std::round( seg.A.y + t * ( seg.B.y - seg.A.y ) ) );
1679
1680 nextSample += aPitch;
1681
1682 if( aIsValid( pt ) && farEnough( pt ) )
1683 accepted.push_back( pt );
1684 }
1685
1686 cursor += segLen;
1687 }
1688 };
1689
1690 for( int o = 0; o < aEnvelope.OutlineCount(); ++o )
1691 walkChain( aEnvelope.COutline( o ) );
1692
1693 return accepted;
1694}
1695
1696
1698{
1700 {
1707
1708 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Size" ),
1710
1711 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Drill" ),
1713
1714 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Pitch" ),
1716
1718 .Undefined( PCB_VIA_STITCH_LAYOUT::PLAIN )
1719 .Map( PCB_VIA_STITCH_LAYOUT::PLAIN, _HKI( "Plain grid" ) )
1720 .Map( PCB_VIA_STITCH_LAYOUT::STAGGERED, _HKI( "Staggered grid" ) )
1721 .Map( PCB_VIA_STITCH_LAYOUT::POISSON, _HKI( "Poisson disk" ) );
1722
1725 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1726 {
1727 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1728 return stitch->GetMode() == PCB_VIA_STITCH_MODE::STITCH;
1729
1730 return true;
1731 } );
1732
1734 .Undefined( PCB_VIA_STITCH_MODE::STITCH )
1735 .Map( PCB_VIA_STITCH_MODE::STITCH, _HKI( "Stitch" ) )
1736 .Map( PCB_VIA_STITCH_MODE::GUARD, _HKI( "Guard" ) );
1737
1740
1741 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, uint32_t>( _HKI( "Seed" ),
1743 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1744 {
1745 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1746 {
1747 return stitch->GetMode() == PCB_VIA_STITCH_MODE::STITCH
1748 && stitch->GetLayout() == PCB_VIA_STITCH_LAYOUT::POISSON;
1749 }
1750 return true;
1751 } );
1752
1755
1756 propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA_STITCH, int>( _HKI( "Guarded Net" ),
1758 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1759 {
1760 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1761 return stitch->GetMode() == PCB_VIA_STITCH_MODE::GUARD;
1762
1763 return true;
1764 } );
1765
1766 // The stitch lives on its own GAL layer and the vias are multi-layer configured separately
1767 propMgr.Mask( TYPE_HASH( PCB_VIA_STITCH ), TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) );
1768 }
1770
1771
1774
1775
int index
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
@ ERROR_OUTSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
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.
bool IsLocked() const override
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
BOARD_ITEM & operator=(const BOARD_ITEM &aOther)
Definition board_item.h:103
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:3001
const ZONES & Zones() const
Definition board.h:468
int GetCopperLayerCount() const
Definition board.cpp:1138
const FOOTPRINTS & Footprints() const
Definition board.h:464
const TRACKS & Tracks() const
Definition board.h:462
std::shared_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition board.h:1842
int m_DRCMaxClearance
Definition board.h:1865
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
constexpr const Vec & GetPosition() const
Definition box2.h:208
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 size_type GetWidth() const
Definition box2.h:211
constexpr coord_type GetX() const
Definition box2.h:204
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
int ShowModal() override
Modal properties editor for a PCB_VIA_STITCH generator.
virtual bool Run() override
Discard the board's run-time DRC caches and regenerate them from scratch.
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:204
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
bool CheckColliding(SHAPE *aRefShape, PCB_LAYER_ID aTargetLayer, int aClearance=0, std::function< bool(BOARD_ITEM *)> aFilter=nullptr) const
Definition drc_rtree.h:192
void SetDRCEngine(DRC_ENGINE *engine)
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
wxString m_name
Definition eda_group.h:106
std::unordered_set< EDA_ITEM * > m_items
Definition eda_group.h:105
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
void RemoveItem(EDA_ITEM *aItem)
Remove item from group.
Definition eda_group.cpp:77
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
std::unordered_set< EDA_ITEM * > m_deserializedItems
Definition eda_group.h:109
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
bool IsSelected() const
Definition eda_item.h:134
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
bool IsNew() const
Definition eda_item.h:131
static ENUM_MAP< T > & Instance()
Definition property.h:783
Handle actions specific to filling copper zones.
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
Abstract interface for drawing on a 2D-surface.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void DrawPolyline(const std::deque< VECTOR2D > &aPointList)
Draw a polyline.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
virtual COLOR4D GetColor(const VIEW_ITEM *aItem, int aLayer) const =0
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:209
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:227
Definition kiid.h:46
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
T Min() const
Definition minoptmax.h:29
Handle the data for a net.
Definition netinfo.h:50
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
static constexpr PCB_LAYER_ID TEMP_ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:176
Definition pad.h:61
static TOOL_ACTION excludeStitchVia
Exclude selected stitching vias from their parent via-stitch generator.
Common, abstract interface for edit frames.
PCB_GENERATOR_POLY(BOARD_ITEM *aParent, PCB_LAYER_ID aLayer)
SHAPE_POLY_SET m_outline
virtual void SetProperties(const STRING_ANY_MAP &aProps)
wxString m_generatorType
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
VECTOR2I m_origin
virtual const STRING_ANY_MAP GetProperties() const
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
void SetLocked(bool aLocked) override
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override final
Draw the parts of the object belonging to layer aLayer.
PCB_VIA_STITCH_MODE m_mode
int GetViaSize() const
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
~PCB_VIA_STITCH() override
static std::vector< VECTOR2I > SampleGuardEnvelope(const SHAPE_POLY_SET &aEnvelope, const SHAPE_POLY_SET &aGuarded, int aPitch, const std::function< bool(const VECTOR2I &)> &aIsValid)
Determine via positions around the perimeter of a guard envelope.
PCB_VIA * ViaTemplate()
void EditCancel(GENERATOR_TOOL *, BOARD *, BOARD_COMMIT *) override
void SetTemplateItem(const wxString &aName, std::unique_ptr< BOARD_ITEM > aItem) override
Insert a template item (from board file loading)
int GetViaDrill() const
std::set< VECTOR2I > m_excludedPositions
void SetGuardedNetCode(int aNetCode)
int GetNetCode() const
void SetMode(PCB_VIA_STITCH_MODE aVal)
void EditStart(GENERATOR_TOOL *, BOARD *, BOARD_COMMIT *) override
bool usesGridCells() const
PCB_VIA_STITCH_LAYOUT GetLayout() const
std::vector< ZONE * > GetZonesNeedingRefillAfterUpdate() const override
List of zones that cross out stitch zone, they'll need to be refilled for the punches.
void SetViaSize(int aVal)
std::unique_ptr< PCB_VIA > m_viaTemplate
void SetPitch(int aVal)
PCB_VIA_STITCH & operator=(const PCB_VIA_STITCH &aOther)
std::vector< std::pair< wxString, const BOARD_ITEM * > > GetTemplateItems() const override
Named template items used by the generator.
PCB_VIA_STITCH(BOARD_ITEM *aParent=nullptr)
std::unique_ptr< ACTION_MENU > m_childContextMenu
static constexpr uint32_t POISSON_TILE_SEED
Fixed seed for baking the toroidal Poisson pattern.
int defaultPitch(BOARD *aBoard) const
void ShowPropertiesDialog(PCB_BASE_EDIT_FRAME *aEditFrame) override
void ClearAllExclusions()
Drop all manual exclusions.
VECTOR2I cellForPosition(const VECTOR2I &aPos) const
Convert an absolute board position to a (col, row) grid cell index.
void ClearExclusion(const VECTOR2I &aPos)
Remove a board position from the exclusion list for the current layout/mode.
ACTION_MENU * GetChildContextMenu(TOOL_INTERACTIVE *aTool) const override
Get a context menu when interacting with a generator child.
const STRING_ANY_MAP GetProperties() const override
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
void SetViaDrill(int aVal)
PCB_VIA_STITCH_MODE GetMode() const
std::set< VECTOR2I > buildPlacementCells(BOARD *aBoard) const
Walk the placement grid, run DRC clearance tests, and return the set of (col, row) cells where a via ...
static std::vector< ZONE * > collectAllZones(const BOARD *aBoard)
Grabs all zones we need to consider for via placement.
static const std::vector< VECTOR2D > & bakedPoissonTile()
Helper method to cache the poisson tile we pattern.
VECTOR2I GetPosition() const override
static constexpr int POISSON_TILE_PITCHES
Tile size for the POISSON layout, in units of pitch.
VECTOR2I m_originOffset
bool Update(GENERATOR_TOOL *, BOARD *, BOARD_COMMIT *) override
int defaultViaSize(BOARD *aBoard) const
int GetGuardedNetCode() const
Net being guarded in GUARD mode.
void SetNetCode(int aNetCode)
static const wxString GENERATOR_TYPE
int GetPitch() const
void EditFinish(GENERATOR_TOOL *, BOARD *, BOARD_COMMIT *) override
int defaultViaDrill(BOARD *aBoard) const
std::set< PCB_VIA * > m_pendingRemovals
void Remove(GENERATOR_TOOL *, BOARD *, BOARD_COMMIT *) override
static const wxString DISPLAY_NAME
wxString m_lastGuardedNetName
uint32_t GetSeed() const
void SetProperties(const STRING_ANY_MAP &aProps) override
void SetLayout(PCB_VIA_STITCH_LAYOUT aVal)
PCB_VIA_STITCH_LAYOUT m_layout
void ExcludePosition(const VECTOR2I &aPos)
Add a board position to the exclusion set so the next Update() skips it.
void OnZoneFillChanged(const std::vector< ZONE * > &aZones) override
Callback to be informed zones have changed.
wxString m_lastNetName
void SetSeed(uint32_t aVal)
GRID_CONFIG m_childGridConfig
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
void InitializeDefaults(BOARD *aBoard)
Fill in any unset via-template and pitch values from the board's design settings.
std::set< VECTOR2I > m_excludedCells
VECTOR2I positionForCell(const VECTOR2I &aCell, int aPitch) const
Convert a (col, row) grid cell index to an absolute board position, including any stagger shift on od...
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:263
Provide class metadata.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
void Mask(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName)
Sets a base class property as masked in a derived class.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
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)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
int OutlineCount() const
Return the number of outlines in the set.
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
A name/value tuple with unique names and wxAny values.
void set_iu(const std::string &aKey, const T &aVar)
bool get_to(const std::string &aKey, T &aVar) const
std::optional< T > get_opt(const std::string &aKey) const
void set(const std::string &aKey, const T &aVar)
bool get_to_iu(const std::string &aKey, T &aVar) const
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
T * GetTool()
Return the tool of given type or nullptr if there is no such tool registered.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
@ ROUND_ALL_CORNERS
All angles are rounded.
DRC_CONSTRAINT_T
Definition drc_rule.h:49
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:54
#define _(s)
#define IN_EDIT
Item currently edited.
@ LAYER_VIA_STITCHING
Outline of via stitching generators.
Definition layer_ids.h:326
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
std::vector< VECTOR2D > ToroidalUnitTile(double aMinDist, uint32_t aSeed)
Bridson's "Fast Poisson Disk Sampling in Arbitrary Dimensions" with toroidal boundary conditions on t...
KICOMMON_API void PackPolySet(types::PolySet &aOutput, const SHAPE_POLY_SET &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackCustomProperties(google::protobuf::RepeatedPtrField< types::CustomProperty > *aOutput, const EDA_ITEM &aItem)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void UnpackCustomProperties(const google::protobuf::RepeatedPtrField< types::CustomProperty > &aInput, EDA_ITEM &aItem)
KICOMMON_API SHAPE_POLY_SET UnpackPolySet(const types::PolySet &aInput, const EDA_IU_SCALE &aScale)
STL namespace.
#define _HKI(x)
Definition page_info.cpp:40
static GENERATORS_MGR::REGISTER< PCB_TUNING_PATTERN > registerMe
static struct PCB_VIA_STITCH_DESC _PCB_VIA_STITCH_DESC
PCB_VIA_STITCH_MODE
@ STITCH
Fill the outline with vias with a pattern.
@ GUARD
Place vias to guard a net contained within.
PCB_VIA_STITCH_LAYOUT
@ POISSON
Tiled poisson distribution.
@ STAGGERED
Odd rows shifted by half the pitch.
@ PLAIN
Regular row/column grid.
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
#define ENUM_TO_WXANY(type)
Definition property.h:890
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
@ PT_NET
Net selection property.
Definition property.h:70
#define REGISTER_TYPE(x)
Helper macro to map type hashes to names.
Static helper to register a generator.
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
const SHAPE_LINE_CHAIN chain
wxString result
Test unit parsing edge cases and error handling.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707