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
334 int val = aBoard->GetDesignSettings().GetCurrentViaSize();
335
336 if( val <= 0 )
337 val = pcbIUScale.mmToIU( 0.6 );
338
339 return val;
340}
341
343{
344 if( !aBoard )
345 return pcbIUScale.mmToIU( 0.3 );
346
347 int val = aBoard->GetDesignSettings().GetCurrentViaDrill();
348
349 if( val <= 0 )
350 val = pcbIUScale.mmToIU( 0.3 );
351
352 return val;
353}
354
355
357{
358 if( !( GetFlags() & IN_EDIT ) )
359 return false;
360
361 if( !aBoard || !aCommit )
362 return false;
363
364 // Resolve net code from the saved name in case EditStart() was not called
365 // (e.g. programmatic regeneration after load)
366 if( m_netCode == 0 && !m_lastNetName.empty() )
367 {
368 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastNetName ) )
369 m_netCode = net->GetNetCode();
370 }
371
372 if( m_guardedNetCode == 0 && !m_lastGuardedNetName.empty() )
373 {
374 if( NETINFO_ITEM* net = aBoard->FindNet( m_lastGuardedNetName ) )
375 m_guardedNetCode = net->GetNetCode();
376 }
377
378 int detectPitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
379
380 // Avoid accidentally calculating a grid offset change because we just changed layout modes
381 const GRID_CONFIG currentConfig{ m_layout, m_mode, detectPitch };
382 const bool childrenAreOnThisGrid = m_childGridConfig == currentConfig;
383
384 m_childGridConfig = currentConfig;
385
386 // Try to detect origin offset change due to moved via
387 if( usesGridCells() && childrenAreOnThisGrid )
388 {
389 for( BOARD_ITEM* it : GetBoardItems() )
390 {
391 if( it->Type() != PCB_VIA_T )
392 continue;
393
394 VECTOR2I p = static_cast<PCB_VIA*>( it )->GetPosition();
395 VECTOR2I expectedPos = positionForCell( cellForPosition( p ), detectPitch );
396
397 if( expectedPos != p )
398 {
399 int offsetY = ( ( p.y % detectPitch ) + detectPitch ) % detectPitch;
400
401 // The dragged via defines the new grid origin. On a staggered layout an odd
402 // row carries an extra half-pitch x shift, which must be removed before
403 // computing the origin offset or the whole grid re-anchors half a pitch off.
404 // (p.y - offsetY) is an exact multiple of the pitch, so this row index matches
405 // what cellForPosition()/positionForCell() will derive after the re-anchor.
406 int row = ( p.y - offsetY ) / detectPitch;
408 && ( ( row % 2 + 2 ) % 2 != 0 ) ) ? detectPitch / 2 : 0;
409
411 VECTOR2I( ( ( ( p.x - xShift ) % detectPitch ) + detectPitch ) % detectPitch,
412 offsetY );
413 break;
414 }
415 }
416 }
417
418 // Take snapshot of existing vias, we then selectively remove vias only if they should be removed
419 // we want to reduce file churn of KIIDs
420 std::vector<PCB_VIA*> existingVias;
421
422 for( BOARD_ITEM* item : GetBoardItems() )
423 {
424 if( item->Type() == PCB_VIA_T )
425 existingVias.push_back( static_cast<PCB_VIA*>( item ) );
426 }
427
428 bool changed = false;
429
430 auto removeVia =
431 [&]( PCB_VIA* aVia )
432 {
433 RemoveItem( aVia );
434 m_pendingRemovals.insert( aVia );
435 changed = true;
436 };
437
438 if( m_outline.IsEmpty() )
439 {
440 for( PCB_VIA* via : existingVias )
441 removeVia( via );
442
443 m_lastUpdateChangedVias = changed;
444 ClearDirty();
445 return false;
446 }
447
448 int pitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
449
450 if( m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS ) <= 0 )
451 m_viaTemplate->SetWidth( PADSTACK::ALL_LAYERS, defaultViaSize( aBoard ) );
452 if( m_viaTemplate->GetDrillValue() <= 0 )
453 m_viaTemplate->SetDrill( defaultViaDrill( aBoard ) );
454
455 // Compute the set of grid cells where a via fits cleanly (DRC + same-net zone overlap).
456 // TODO, we will use a cache grid
457 std::set<VECTOR2I> placementCells = buildPlacementCells( aBoard );
458
459 // Guard mode currently has to exclude by a proximity heuristic since we aren't using a grid.
460 const int64_t guardExcludeRadiusSq = ( (int64_t) pitch / 4 ) * ( pitch / 4 );
461
462 auto isExcluded =
463 [&]( const VECTOR2I& cell ) -> bool
464 {
465 // Guard vias are walked along the guarded net's envelope, so a stored position
466 // will rarely land exactly on a regenerated via. Match by proximity instead.
468 {
469 for( const VECTOR2I& excluded : m_excludedPositions )
470 {
471 VECTOR2I d = excluded - cell;
472
473 if( (int64_t) d.x * d.x + (int64_t) d.y * d.y < guardExcludeRadiusSq )
474 return true;
475 }
476
477 return false;
478 }
479
480 return usesGridCells() ? m_excludedCells.count( cell ) > 0
481 : m_excludedPositions.count( cell ) > 0;
482 };
483
484 // Get the new list of desired via cells
485 std::map<VECTOR2I, VECTOR2I> desired;
486
487 for( const VECTOR2I& cell : placementCells )
488 {
489 if( !isExcluded( cell ) )
490 desired.emplace( cell, positionForCell( cell, pitch ) );
491 }
492
493 // Determine existing vias we can keep
494 std::map<VECTOR2I, PCB_VIA*> kept;
495
496 for( PCB_VIA* via : existingVias )
497 {
498 VECTOR2I cell = cellForPosition( via->GetPosition() );
499
500 if( desired.count( cell ) && kept.emplace( cell, via ).second )
501 continue;
502
503 removeVia( via );
504 }
505
506 // Vias retired earlier may need to be unretired, this usually occurs when someone
507 // is shrinking the outline and then changes their mind in the same drag
508 std::map<VECTOR2I, PCB_VIA*> resurrectable;
509
511 resurrectable.try_emplace( cellForPosition( via->GetPosition() ), via );
512
513 // Now create a reference via to compare against, we need to see if existing vias need to be nuked
514 // anyway on a property change. In which case we will just regen rather than play games
515 std::unique_ptr<PCB_VIA> ref( static_cast<PCB_VIA*>( m_viaTemplate->Clone() ) );
516 ref->SetParent( aBoard );
517 ref->SetIsFree( true );
518
519 if( m_netCode )
520 ref->SetNetCode( m_netCode );
521
522 auto matchesRef =
523 [&]( const PCB_VIA* aVia ) -> bool
524 {
525 // The padstack carries size, drill, layer span, mask/paste attributes,
526 // backdrill and post-machining. That covers everything the via dialog
527 // can edit except the net and teardrop settings, checked separately.
528 return aVia->GetPosition() == ref->GetPosition()
529 && aVia->GetLayer() == ref->GetLayer()
530 && aVia->GetViaType() == ref->GetViaType()
531 && aVia->Padstack() == ref->Padstack()
532 && aVia->GetNetCode() == ref->GetNetCode()
533 && aVia->GetTeardropParams() == ref->GetTeardropParams();
534 };
535
536 for( const auto& [cell, pt] : desired )
537 {
538 auto it = kept.find( cell );
539
540 if( it != kept.end() )
541 {
542 PCB_VIA* via = it->second;
543
544 ref->SetPosition( pt );
545
546 if( matchesRef( via ) )
547 continue; // unchanged — no commit entry, UUID preserved
548
549 // Just nudge the via
550 ref->SetPosition( via->GetPosition() );
551
552 if( matchesRef( via ) )
553 {
554 aCommit->Modify( via );
555 via->SetPosition( pt );
556 changed = true;
557 continue;
558 }
559
560 removeVia( via );
561 }
562
563 // Resurrect a via retired earlier
564 auto rit = resurrectable.find( cell );
565
566 if( rit != resurrectable.end() )
567 {
568 PCB_VIA* via = rit->second;
569
570 ref->SetPosition( pt );
571
572 if( matchesRef( via ) )
573 {
574 m_pendingRemovals.erase( via );
575 AddItem( via );
576 changed = true;
577 continue;
578 }
579 }
580
581 PCB_VIA* via = static_cast<PCB_VIA*>( m_viaTemplate->Clone() );
582 via->ResetUuidDirect();
583
584 via->SetParent( aBoard );
585 via->SetPosition( pt );
586
587 if( m_netCode )
588 via->SetNetCode( m_netCode );
589
590 // Vias must be marked free to avoid connectivity stealing them away
591 via->SetIsFree( true );
592
593 // If the generator is selected (point editing), make sure to mark new vias
594 // as selected to match
595 if( IsSelected() )
596 via->SetSelected();
597
598 AddItem( via );
599 aCommit->Add( via );
600 changed = true;
601 }
602
603 m_lastUpdateChangedVias = changed;
604 ClearDirty();
605 return true;
606}
607
608
610{
612
613 props.set_iu( "pitch", m_pitch );
614 props.set( "layout", static_cast<int>( m_layout ) );
615 props.set( "mode", static_cast<int>( m_mode ) );
616 props.set( "seed", static_cast<int>( m_seed ) );
617 props.set_iu( "origin_offset_x", m_originOffset.x );
618 props.set_iu( "origin_offset_y", m_originOffset.y );
619
620 // Save the net by name so it survives across sessions (net codes are session-local)
621 if( !m_lastNetName.empty() )
622 props.set( "net_name", m_lastNetName );
623
624 if( !m_lastGuardedNetName.empty() )
625 props.set( "guarded_net_name", m_lastGuardedNetName );
626
627 if( !m_excludedCells.empty() )
628 {
629 std::vector<VECTOR2I> cells( m_excludedCells.begin(), m_excludedCells.end() );
630 props.set( "excluded_grid_cells", wxAny( cells ) );
631 }
632
633 if( !m_excludedPositions.empty() )
634 {
636 for( const VECTOR2I& pos : m_excludedPositions )
637 chain.Append( pos );
638 props.set( "excluded_positions", wxAny( chain ) );
639 }
640
641 // Serialize the single zone outline as a SHAPE_LINE_CHAIN.
642 // The file format natively supports SHAPE_LINE_CHAIN but not SHAPE_POLY_SET directly.
643 if( m_outline.OutlineCount() > 0 )
644 props.set( "outline", wxAny( m_outline.COutline( 0 ) ) );
645
646 return props;
647}
648
649
651{
653
654 aProps.get_to_iu( "pitch", m_pitch );
655
656 if( auto layout = aProps.get_opt<int>( "layout" ) )
657 m_layout = static_cast<PCB_VIA_STITCH_LAYOUT>( *layout );
658
659 if( auto mode = aProps.get_opt<int>( "mode" ) )
660 m_mode = static_cast<PCB_VIA_STITCH_MODE>( *mode );
661
662 if( auto seed = aProps.get_opt<int>( "seed" ) )
663 m_seed = static_cast<uint32_t>( *seed );
664
665 aProps.get_to_iu( "origin_offset_x", m_originOffset.x );
666 aProps.get_to_iu( "origin_offset_y", m_originOffset.y );
667
669
670 // Restore net name; net code is resolved from the board in EditStart()/Update()
671 aProps.get_to( "net_name", m_lastNetName );
672 aProps.get_to( "guarded_net_name", m_lastGuardedNetName );
673
674 m_excludedCells.clear();
675 m_excludedPositions.clear();
676
677
678 if( auto cells = aProps.get_opt<std::vector<VECTOR2I>>( "excluded_grid_cells" ) )
679 m_excludedCells.insert( cells->begin(), cells->end() );
680
681 if( auto chain = aProps.get_opt<SHAPE_LINE_CHAIN>( "excluded_positions" ) )
682 {
683 for( int i = 0; i < chain->PointCount(); ++i )
684 m_excludedPositions.insert( chain->CPoint( i ) );
685 }
686
687 // Restore the single zone outline
688 m_outline.RemoveAllContours();
689
690 if( auto outline = aProps.get_opt<SHAPE_LINE_CHAIN>( "outline" ) )
691 {
692 // A chain parsed from the file is a bare point list; the closed flag is not
693 // serialized and AddOutline() asserts on open chains
694 outline->SetClosed( true );
695 m_outline.AddOutline( *outline );
696 }
697}
698
699
700void PCB_VIA_STITCH::Serialize( google::protobuf::Any& aContainer ) const
701{
702 using namespace kiapi::board::types;
703 using namespace kiapi::common::types;
704
705 ViaStitchArea stitch;
706
707 stitch.mutable_id()->set_value( m_Uuid.AsStdString() );
708 stitch.set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( GetLayer() ) );
709 kiapi::common::PackPolySet( *stitch.mutable_outline(), m_outline );
710 stitch.mutable_pitch()->set_value_nm( pcbIUScale.IUToNm( m_pitch ) );
713
715 {
716 ViaStitchGuardSettings* guard = stitch.mutable_guard_settings();
717
718 if( !m_lastGuardedNetName.empty() )
719 guard->mutable_guarded_net()->set_name( m_lastGuardedNetName.ToUTF8() );
720 }
721 else
722 {
723 ViaStitchGridSettings* grid = stitch.mutable_grid_settings();
724 grid->mutable_origin_offset()->set_x_nm( pcbIUScale.IUToNm( m_originOffset.x ) );
725 grid->mutable_origin_offset()->set_y_nm( pcbIUScale.IUToNm( m_originOffset.y ) );
726 grid->set_seed( m_seed );
727
728 for( const VECTOR2I& cell : m_excludedCells )
729 {
730 GridCell* gridCell = grid->add_excluded_cells();
731 gridCell->set_i( cell.x );
732 gridCell->set_j( cell.y );
733 }
734 }
735
736 if( !m_lastNetName.empty() )
737 stitch.mutable_net()->set_name( m_lastNetName.ToUTF8() );
738
739 for( const VECTOR2I& pos : m_excludedPositions )
740 kiapi::common::PackVector2( *stitch.add_excluded_positions(), pos );
741
742 if( const PCB_VIA* viaTemplate = ViaTemplate() )
743 viaTemplate->Serialize( *stitch.mutable_via_template() );
744
745 stitch.set_locked( IsLocked() ? LockedState::LS_LOCKED : LockedState::LS_UNLOCKED );
746
747 if( const BOARD* board = GetBoard() )
748 stitch.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
749
750 for( EDA_ITEM* member : GetItems() )
751 stitch.add_members()->set_value( member->m_Uuid.AsStdString() );
752
753 kiapi::common::PackCustomProperties( stitch.mutable_custom_properties(), *this );
754 aContainer.PackFrom( stitch );
755}
756
757
758bool PCB_VIA_STITCH::Deserialize( const google::protobuf::Any& aContainer )
759{
760 using namespace kiapi::board::types;
761 using namespace kiapi::common::types;
762
763 ViaStitchArea stitch;
764
765 if( !aContainer.UnpackTo( &stitch ) )
766 return false;
767
768 SetUuidDirect( ::KIID( stitch.id().value() ) );
769 SetLayer( FromProtoEnum<PCB_LAYER_ID>( stitch.layer() ) );
770 m_outline = kiapi::common::UnpackPolySet( stitch.outline() );
771 m_pitch = pcbIUScale.NmToIU( stitch.pitch().value_nm() );
774
775 m_lastNetName = wxString( stitch.net().name().c_str(), wxConvUTF8 );
776 m_lastGuardedNetName.clear();
777 m_netCode = 0;
779
780 m_excludedCells.clear();
781 m_excludedPositions.clear();
782
783 switch( stitch.mode_specific_settings_case() )
784 {
785 case ViaStitchArea::kGridSettings:
786 {
787 const ViaStitchGridSettings& grid = stitch.grid_settings();
788 m_originOffset.x = pcbIUScale.NmToIU( grid.origin_offset().x_nm() );
789 m_originOffset.y = pcbIUScale.NmToIU( grid.origin_offset().y_nm() );
790 m_seed = grid.seed();
791
792 for( const GridCell& cell : grid.excluded_cells() )
793 m_excludedCells.insert( VECTOR2I( cell.i(), cell.j() ) );
794
795 break;
796 }
797
798 case ViaStitchArea::kGuardSettings:
799 m_lastGuardedNetName = wxString( stitch.guard_settings().guarded_net().name().c_str(), wxConvUTF8 );
800 break;
801
802 case ViaStitchArea::MODE_SPECIFIC_SETTINGS_NOT_SET: break;
803 }
804
805 for( const kiapi::common::types::Vector2& pos : stitch.excluded_positions() )
807
808 if( stitch.has_via_template() )
809 {
810 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( this );
811
812 if( !via->Deserialize( stitch.via_template() ) )
813 return false;
814
815 via->SetParent( this );
816 via->SetIsFree( true );
817 m_viaTemplate = std::move( via );
818 }
819
820 SetLocked( stitch.locked() == LockedState::LS_LOCKED );
821 kiapi::common::UnpackCustomProperties( stitch.custom_properties(), *this );
822
823 m_items.clear();
824 m_deserializedItems.clear();
825
826 BOARD* board = GetBoard();
827
828 if( !board )
829 return false;
830
831 for( const kiapi::common::types::KIID& memberId : stitch.members() )
832 {
833 if( EDA_ITEM* item = board->ResolveItem( ::KIID( memberId.value() ), true ) )
834 m_deserializedItems.insert( item );
835 }
836
838
839 return true;
840}
841
842
844{
845 if( !( GetFlags() & IN_EDIT ) )
846 return;
847
848 // The edit session is over, any vias still pending removal can now be committed
849 // for removal.
850 if( aCommit )
851 {
853 aCommit->Remove( via );
854 }
855
856 m_pendingRemovals.clear();
857
859}
860
861
863{
864 if( !( GetFlags() & IN_EDIT ) )
865 return;
866
867 // The commit is being reverted; re-attach retired vias so group membership matches
868 // the pre-edit state they revert to.
870 AddItem( via );
871
872 m_pendingRemovals.clear();
873
875}
876
877
879{
880 if( !m_childContextMenu )
881 {
882 m_childContextMenu = std::make_unique<ACTION_MENU>( true, aTool );
884 }
885
886 return m_childContextMenu.get();
887}
888
889
891{
892 if( !aCommit )
893 return;
894
895 for( BOARD_ITEM* item : GetBoardItems() )
896 aCommit->Remove( item );
897
898 // Retired-but-unpushed vias are detached from the group and would otherwise be
899 // orphaned on the board.
901 aCommit->Remove( via );
902
903 m_pendingRemovals.clear();
904
905 aCommit->Remove( this );
906}
907
908
910{
911 int pitch = m_pitch > 0 ? m_pitch : 1;
912
913 auto roundDiv = []( int a, int b ) -> int
914 {
915 return ( a >= 0 ) ? ( a + b / 2 ) / b : -( ( -a + b / 2 ) / b );
916 };
917
918 // Poisson layout and GUARD mode both place at arbitrary positions, not a grid —
919 // cell index == absolute position.
920 if( !usesGridCells() )
921 return aPos;
922
923 int row = roundDiv( aPos.y - m_originOffset.y, pitch );
924 int xShift = ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( row % 2 + 2 ) % 2 != 0 ) )
925 ? pitch / 2
926 : 0;
927 int col = roundDiv( aPos.x - m_originOffset.x - xShift, pitch );
928
929 return VECTOR2I( col, row );
930}
931
932
933VECTOR2I PCB_VIA_STITCH::positionForCell( const VECTOR2I& aCell, int aPitch ) const
934{
935 if( !usesGridCells() )
936 return aCell;
937
938 int xShift = ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( aCell.y % 2 + 2 ) % 2 != 0 ) )
939 ? aPitch / 2
940 : 0;
941 return VECTOR2I( aCell.x * aPitch + m_originOffset.x + xShift,
942 aCell.y * aPitch + m_originOffset.y );
943}
944
945
946std::set<VECTOR2I> PCB_VIA_STITCH::buildPlacementCells( BOARD* aBoard ) const
947{
948 std::set<VECTOR2I> cells;
949
950 if( !aBoard || m_outline.IsEmpty() )
951 return cells;
952
953 int pitch = m_pitch > 0 ? m_pitch : defaultPitch( aBoard );
954 int viaSize = m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS ) > 0 ? m_viaTemplate->GetWidth( PADSTACK::ALL_LAYERS )
955 : defaultViaSize( aBoard );
956
957 if( GetNetCode() == 0 )
958 return cells;
959
960 DRC_ENGINE* drcEngine = aBoard->GetDesignSettings().m_DRCEngine.get();
961
962 DRC_CACHE_GENERATOR cacheGenerator;
963 cacheGenerator.SetDRCEngine( drcEngine );
964 cacheGenerator.Run();
965
966 // Pre-clip every zone fill to the stitch outline bbox before adding it to perLayerFills.
967 // Without this, a board-wide GND zone would drag in an entire complex polygon when we may
968 // only be stitching part of it.
969 // This also helps us skip same net zones entirely if they aren't in here
970 BOX2I stitchBBox = m_outline.BBox();
971
972 SHAPE_POLY_SET stitchClip;
973 stitchClip.NewOutline();
974 stitchClip.Append( stitchBBox.GetLeft(), stitchBBox.GetTop() );
975 stitchClip.Append( stitchBBox.GetRight(), stitchBBox.GetTop() );
976 stitchClip.Append( stitchBBox.GetRight(), stitchBBox.GetBottom() );
977 stitchClip.Append( stitchBBox.GetLeft(), stitchBBox.GetBottom() );
978
979 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> perLayerFills;
980
981 for( ZONE* zone : collectAllZones( aBoard ) )
982 {
983 if( zone->GetIsRuleArea() )
984 continue;
985
986 if( zone->GetNetCode() != GetNetCode() )
987 continue;
988
989 if( !zone->GetBoundingBox().Intersects( stitchBBox ) )
990 continue;
991
992 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
993 {
994 SHAPE_POLY_SET clipped;
995
997 {
998 clipped = zone->GetBoardOutline();
999 }
1000 else
1001 {
1002 SHAPE_POLY_SET* fill = zone->GetFill( layer );
1003
1004 if( !fill || fill->IsEmpty() )
1005 continue;
1006
1007 clipped = *fill;
1008 }
1009
1010 if( clipped.IsEmpty() )
1011 continue;
1012
1013 clipped.BooleanIntersection( stitchClip );
1014
1015 if( clipped.OutlineCount() == 0 )
1016 continue;
1017
1018 perLayerFills[layer].BooleanAdd( clipped );
1019 }
1020 }
1021
1022 // Extract the via copper layers, the layer stack may contain the mask layers
1023 LSET boardCopperLayers = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
1024 LSET viaCopperLayers = m_viaTemplate->GetLayerSet() & boardCopperLayers;
1025
1026 // Filter down to just those via layers that actually have same-net copper on them.
1027 std::vector<const SHAPE_POLY_SET*> viaLayerFills;
1028
1029 for( PCB_LAYER_ID layer : viaCopperLayers )
1030 {
1031 auto it = perLayerFills.find( layer );
1032
1033 if( it != perLayerFills.end() && !it->second.IsEmpty() )
1034 viaLayerFills.push_back( &it->second );
1035 }
1036
1037 // Is there even enough layers left to stitch?
1038 if( viaLayerFills.size() < 2 )
1039 return cells;
1040
1041 // Now build the region where a via would stitch at least two layers together.
1042 SHAPE_POLY_SET allowedRegion;
1043 SHAPE_POLY_SET coveredSoFar = *viaLayerFills[0];
1044
1045 for( size_t i = 1; i < viaLayerFills.size(); ++i )
1046 {
1047 SHAPE_POLY_SET overlap = *viaLayerFills[i];
1048 overlap.BooleanIntersection( coveredSoFar );
1049
1050 if( !overlap.IsEmpty() )
1051 allowedRegion.BooleanAdd( overlap );
1052
1053 if( i + 1 < viaLayerFills.size() )
1054 coveredSoFar.BooleanAdd( *viaLayerFills[i] );
1055 }
1056
1057 if( allowedRegion.OutlineCount() == 0 )
1058 return cells;
1059
1060 // Now intersect it with the via stitch outline
1061 allowedRegion.BooleanIntersection( m_outline );
1062
1063 if( allowedRegion.OutlineCount() == 0 )
1064 return cells;
1065
1066 // Generate a obstacle mask for via placement
1067 // Rather than DRC-probing every possible placement cell against the r-tree
1068 // to test for collisions. We instead create a keep-out polygon with obstacles
1069 // inflated by the via-radius and clearance we want. The result is now
1070 // we only have to do a point-in-polygon test for placement
1071
1072 const int polyApproxError = pcbIUScale.mmToIU( 0.005 );
1073
1074 // Require the whole via, not just its center, to land on same-net copper inside the
1075 // outline
1076 allowedRegion.Deflate( viaSize / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, polyApproxError );
1077
1078 if( allowedRegion.OutlineCount() == 0 )
1079 return cells;
1080
1081 // Probe used for clearance evaluation; it must carry the stitch net because clearance
1082 // rules can key off the net class.
1083 std::unique_ptr<PCB_VIA> probe( static_cast<PCB_VIA*>( m_viaTemplate->Clone() ) );
1084 probe->SetParent( const_cast<BOARD*>( aBoard ) );
1085
1086 if( m_netCode )
1087 probe->SetNetCode( m_netCode );
1088
1089 const int worstClearance = aBoard->m_DRCMaxClearance;
1090
1091 BOX2I queryBBox = stitchBBox;
1092 queryBBox.Inflate( worstClearance + viaSize );
1093
1094 SHAPE_POLY_SET queryClip;
1095 queryClip.NewOutline();
1096 queryClip.Append( queryBBox.GetLeft(), queryBBox.GetTop() );
1097 queryClip.Append( queryBBox.GetRight(), queryBBox.GetTop() );
1098 queryClip.Append( queryBBox.GetRight(), queryBBox.GetBottom() );
1099 queryClip.Append( queryBBox.GetLeft(), queryBBox.GetBottom() );
1100
1101 SHAPE_POLY_SET obstacles;
1102
1103 int viaDrill = m_viaTemplate->GetDrillValue() > 0 ? m_viaTemplate->GetDrillValue()
1104 : defaultViaDrill( aBoard );
1105
1106 auto evalConstraint =
1107 [&]( DRC_CONSTRAINT_T aType, BOARD_ITEM* aOther, PCB_LAYER_ID aLayer ) -> int
1108 {
1109 if( !drcEngine )
1110 return 0;
1111
1112 DRC_CONSTRAINT constraint =
1113 drcEngine->EvalRules( aType, probe.get(), aOther, aLayer );
1114 return constraint.GetValue().Min();
1115 };
1116
1117 auto isSameNet =
1118 [&]( BOARD_ITEM* aOther ) -> bool
1119 {
1120 BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
1121 return cItem && cItem->GetNetCode() == GetNetCode();
1122 };
1123
1124 // Drilled holes are obstacles as we need to respect the hole to hole constraint
1125 std::set<BOARD_ITEM*> holeSeen;
1126
1127 auto addHoleObstacle =
1128 [&]( BOARD_ITEM* aOther, PCB_LAYER_ID aLayer )
1129 {
1130 if( !aOther->HasHole() || !holeSeen.insert( aOther ).second )
1131 return;
1132
1133 std::shared_ptr<SHAPE_SEGMENT> hole = aOther->GetEffectiveHoleShape();
1134
1135 if( !hole )
1136 return;
1137
1138 int margin = viaDrill / 2
1139 + evalConstraint( HOLE_TO_HOLE_CONSTRAINT, aOther, UNDEFINED_LAYER );
1140
1141 if( !isSameNet( aOther ) )
1142 {
1143 margin = std::max( margin,
1144 viaSize / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT,
1145 aOther, aLayer ) );
1146 }
1147
1148 TransformOvalToPolygon( obstacles, hole->GetSeg().A, hole->GetSeg().B,
1149 hole->GetWidth() + 2 * margin, polyApproxError,
1150 ERROR_OUTSIDE );
1151 };
1152
1153 SHAPE_RECT queryRect( queryBBox.GetPosition(), queryBBox.GetWidth(), queryBBox.GetHeight() );
1154
1155 for( PCB_LAYER_ID layer : viaCopperLayers )
1156 {
1157 std::set<BOARD_ITEM*> seen;
1158
1159 auto enumerate =
1160 [&]( BOARD_ITEM* aOther ) -> bool
1161 {
1162 if( !seen.insert( aOther ).second )
1163 return false;
1164
1165 // Our own children are regenerated along with us and must not block
1166 // their own cells.
1167 if( aOther->GetParentGroup() == static_cast<const EDA_GROUP*>( this ) )
1168 return false;
1169
1170 // Same for children retired during the same edit: they are
1171 // detached from the group but stay on the board until the commit is
1172 // pushed, and their cells must stay placeable so they can resurrect.
1173 if( aOther->Type() == PCB_VIA_T
1174 && m_pendingRemovals.count( static_cast<PCB_VIA*>( aOther ) ) )
1175 {
1176 return false;
1177 }
1178
1179 addHoleObstacle( aOther, layer );
1180
1181 bool sameNet = isSameNet( aOther );
1182
1183 if( aOther->Type() == PCB_PAD_T )
1184 {
1185 // A copper-zone can be placed with thermal reliefs turned off
1186 // We don't want to accidentally place vias on top of that pad
1187 // So process the PAD as a obstacle we need to clear
1188 PAD* pad = static_cast<PAD*>( aOther );
1189
1190 if( !pad->FlashLayer( layer ) )
1191 return false;
1192 }
1193 else if( aOther->Type() != PCB_VIA_T && sameNet )
1194 {
1195 return false;
1196 }
1197
1198 // Our copper against theirs.
1199 int margin = viaSize / 2 + evalConstraint( CLEARANCE_CONSTRAINT, aOther, layer );
1200
1201 // Our drill against their copper (hole clearance), cross-net only.
1202 if( !sameNet )
1203 {
1204 margin = std::max( margin,
1205 viaDrill / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT,
1206 aOther, layer ) );
1207 }
1208
1209 aOther->TransformShapeToPolygon( obstacles, layer, margin, polyApproxError,
1210 ERROR_OUTSIDE );
1211 return false;
1212 };
1213
1214 if( aBoard->m_CopperItemRTreeCache )
1215 {
1216 aBoard->m_CopperItemRTreeCache->CheckColliding( &queryRect, layer, worstClearance,
1217 enumerate );
1218 }
1219 }
1220
1221 // Add NPTH mounting holes which have no copper
1222 for( FOOTPRINT* footprint : aBoard->Footprints() )
1223 {
1224 if( !footprint->GetBoundingBox().Intersects( queryBBox ) )
1225 continue;
1226
1227 for( PAD* pad : footprint->Pads() )
1228 {
1229 if( pad->GetBoundingBox().Intersects( queryBBox ) )
1230 addHoleObstacle( pad, F_Cu );
1231 }
1232 }
1233
1234 obstacles.Simplify();
1235
1236 for( ZONE* zone : collectAllZones( aBoard ) )
1237 {
1238 if( !zone->GetBoundingBox().Intersects( queryBBox ) )
1239 continue;
1240
1241 if( zone->GetIsRuleArea() )
1242 {
1243 // Honor via keepouts (any net).
1244 if( !zone->GetDoNotAllowVias() || !( zone->GetLayerSet() & viaCopperLayers ).any() )
1245 continue;
1246
1247 SHAPE_POLY_SET area = zone->GetBoardOutline();
1248 area.BooleanIntersection( queryClip );
1249
1250 if( !area.IsEmpty() )
1251 {
1253 polyApproxError );
1254 obstacles.BooleanAdd( area );
1255 }
1256
1257 continue;
1258 }
1259
1260 if( zone->GetNetCode() == GetNetCode() )
1261 continue;
1262
1263 // A different-net zone only blocks near its fill outside its own outline.
1264 // A via dropped inside the zone is fine because the refill punches an anti-pad around it.
1265 for( PCB_LAYER_ID layer : LSET( zone->GetLayerSet() & viaCopperLayers ) )
1266 {
1267 SHAPE_POLY_SET* fill = zone->GetFill( layer );
1268
1269 if( !fill || fill->IsEmpty() )
1270 continue;
1271
1272 SHAPE_POLY_SET nearFill = *fill;
1273 nearFill.BooleanIntersection( queryClip );
1274
1275 if( nearFill.IsEmpty() )
1276 continue;
1277
1278 int zoneMargin = std::max(
1279 viaSize / 2 + evalConstraint( CLEARANCE_CONSTRAINT, zone, layer ),
1280 viaDrill / 2 + evalConstraint( HOLE_CLEARANCE_CONSTRAINT, zone, layer ) );
1281
1282 nearFill.Inflate( zoneMargin, CORNER_STRATEGY::ROUND_ALL_CORNERS, polyApproxError );
1283
1284 SHAPE_POLY_SET interior = zone->GetBoardOutline();
1285 interior.BooleanIntersection( queryClip );
1286 nearFill.BooleanSubtract( interior );
1287
1288 if( !nearFill.IsEmpty() )
1289 obstacles.BooleanAdd( nearFill );
1290 }
1291 }
1292
1293 allowedRegion.BooleanSubtract( obstacles );
1294
1295 if( allowedRegion.OutlineCount() == 0 )
1296 return cells;
1297
1298 allowedRegion.BuildBBoxCaches();
1299
1300 BOX2I bbox = allowedRegion.BBox();
1301
1302 auto isValid =
1303 [&]( const VECTOR2I& pt ) -> bool
1304 {
1305 return allowedRegion.Contains( pt, -1, 0, true );
1306 };
1307
1309 {
1310 // Walk the perimeter of each guarded-net item's clearance envelope, dropping
1311 // candidate vias every `pitch` of arc length. The envelope is the item's shape
1312 // inflated by (viaRadius + DRC clearance), so a via dropped on it sits at the
1313 // closest legal distance from the trace
1314 if( m_guardedNetCode == 0 )
1315 return cells;
1316
1317 // Merge every guarded-net item's clearance envelope into one polygon, and their bare
1318 // shapes into another so the sampler can tell facing vias from same-side ones.
1319 SHAPE_POLY_SET mergedEnvelope;
1320 SHAPE_POLY_SET mergedGuarded;
1321
1322 for( PCB_TRACK* track : aBoard->Tracks() )
1323 {
1324 if( track->GetNetCode() != m_guardedNetCode )
1325 continue;
1326
1327 if( !track->GetBoundingBox().Intersects( stitchBBox ) )
1328 continue;
1329
1330 PCB_LAYER_ID layer = track->GetLayer();
1331
1332 if( track->Type() == PCB_VIA_T )
1333 layer = F_Cu; // arbitrary copper layer for clearance evaluation
1334
1335 int requiredClearance = evalConstraint( CLEARANCE_CONSTRAINT, track, layer );
1336
1337 // Safety margin so envelope samples land outside the obstacle mask. The
1338 // guarded items are themselves obstacles, polygonized outward with up to
1339 // polyApproxError of overshoot, so the envelope must sit at least that much
1340 // further out; the extra 2µm absorbs the sample coordinates' integer rounding.
1341 const int safetyMargin = polyApproxError + pcbIUScale.mmToIU( 0.002 );
1342 int margin = viaSize / 2 + requiredClearance + safetyMargin;
1343
1344 SHAPE_POLY_SET envelope;
1345 track->TransformShapeToPolygon( envelope, layer, margin, polyApproxError,
1346 ERROR_OUTSIDE );
1347
1348 mergedEnvelope.BooleanAdd( envelope );
1349
1350 track->TransformShapeToPolygon( mergedGuarded, layer, 0, polyApproxError,
1351 ERROR_OUTSIDE );
1352 }
1353
1354 mergedEnvelope.Simplify();
1355 mergedGuarded.Simplify();
1356
1357 for( const VECTOR2I& pt : SampleGuardEnvelope( mergedEnvelope, mergedGuarded, pitch,
1358 isValid ) )
1359 {
1360 cells.insert( pt );
1361 }
1362
1363 return cells;
1364 }
1365
1367 {
1368 // We tile a one time generated toroidal Poisson pattern
1369 // across the bbox at (pitch * POISSON_TILE_PITCHES) per tile. The tiling is
1370 // anchored to the global (0, 0) origin plus a per-seed sub-tile shift.
1371 // This gives us a deterministic but non-grid distribution
1372 const std::vector<VECTOR2D>& tile = bakedPoissonTile();
1373 const int tileSize = std::max( 1, pitch * POISSON_TILE_PITCHES );
1374
1375 // Per-seed sub-tile origin shift in [0, tileSize).
1376 boost::random::mt19937 seedRng( m_seed );
1377 boost::random::uniform_real_distribution<double> uniform( 0.0, 1.0 );
1378 const double offsetX = uniform( seedRng ) * tileSize;
1379 const double offsetY = uniform( seedRng ) * tileSize;
1380
1381 // Range of tiles that intersect bbox. Tile (tx, ty) covers
1382 // [tx*tileSize + offset, (tx+1)*tileSize + offset).
1383 const int firstTileX = (int) std::floor( ( bbox.GetX() - offsetX ) / (double) tileSize );
1384 const int firstTileY = (int) std::floor( ( bbox.GetY() - offsetY ) / (double) tileSize );
1385 const int lastTileX = (int) std::floor( ( bbox.GetRight() - offsetX ) / (double) tileSize );
1386 const int lastTileY = (int) std::floor( ( bbox.GetBottom() - offsetY ) / (double) tileSize );
1387
1388 for( int ty = firstTileY; ty <= lastTileY; ++ty )
1389 {
1390 for( int tx = firstTileX; tx <= lastTileX; ++tx )
1391 {
1392 for( const VECTOR2D& s : tile )
1393 {
1394 VECTOR2I pt(
1395 (int) std::round( offsetX + ( tx + s.x ) * tileSize ),
1396 (int) std::round( offsetY + ( ty + s.y ) * tileSize ) );
1397
1398 if( !bbox.Contains( pt ) )
1399 continue;
1400
1401 if( isValid( pt ) )
1402 cells.insert( pt ); // Poisson "cell" == absolute position
1403 }
1404 }
1405 }
1406
1407 return cells;
1408 }
1409 else
1410 {
1411 // Anchor the grid to global (0, 0) + m_originOffset.
1412 int startRow = (int) std::floor( double( bbox.GetY() - m_originOffset.y ) / pitch );
1413 int startCol = (int) std::floor( double( bbox.GetX() - m_originOffset.x ) / pitch );
1414 VECTOR2I origin( startCol * pitch + m_originOffset.x, startRow * pitch + m_originOffset.y );
1415
1416 for( int row = startRow, y = origin.y; y <= bbox.GetBottom(); y += pitch, ++row )
1417 {
1418 // Odd rows are shifted right by half the pitch when stagger is enabled.
1419 int xOffset =
1420 ( m_layout == PCB_VIA_STITCH_LAYOUT::STAGGERED && ( ( row % 2 + 2 ) % 2 != 0 ) ) ? pitch / 2 : 0;
1421
1422 for( int col = startCol, x = origin.x + xOffset; x <= bbox.GetRight(); x += pitch, ++col )
1423 {
1424 VECTOR2I pt( x, y );
1425
1426 if( isValid( pt ) )
1427 cells.insert( VECTOR2I( col, row ) );
1428 }
1429 }
1430
1431 return cells;
1432 }
1433}
1434
1435
1437{
1438 std::vector<ZONE*> result;
1439
1440 // A regeneration that added, moved, or removed nothing can't have changed any
1441 // foreign-net anti-pads, so no refill is needed.
1443 return result;
1444
1445 const BOARD* brd = GetBoard();
1446
1447 if( !brd )
1448 return result;
1449
1450 BOX2I myBBox = GetBoundingBox();
1451
1452 // Lets return all intersecting zones with different nets
1453 for( ZONE* zone : collectAllZones( brd ) )
1454 {
1455 if( zone->GetIsRuleArea() )
1456 continue;
1457
1458 if( zone->GetNetCode() == m_netCode )
1459 continue;
1460
1461 if( !zone->GetBoundingBox().Intersects( myBBox ) )
1462 continue;
1463
1464 result.push_back( zone );
1465 }
1466
1467 return result;
1468}
1469
1470
1471void PCB_VIA_STITCH::OnZoneFillChanged( const std::vector<ZONE*>& aZones )
1472{
1473 if( m_netCode == 0 )
1474 return;
1475
1476 BOX2I myBBox = GetBoundingBox();
1477
1478 for( ZONE* zone : aZones )
1479 {
1480 if( zone->GetNetCode() != m_netCode )
1481 continue;
1482
1483 if( !zone->GetBoundingBox().Intersects( myBBox ) )
1484 continue;
1485
1486 MarkDirty();
1487 return;
1488 }
1489}
1490
1491
1493{
1494 BOARD_COMMIT commit( aEditFrame );
1495 GENERATOR_TOOL* genTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
1496
1497 commit.Modify( this );
1498
1499 DIALOG_VIA_STITCH_PROPERTIES dlg( aEditFrame, this );
1500
1501 if( dlg.ShowModal() != wxID_OK )
1502 return;
1503
1504 EditStart( genTool, GetBoard(), &commit );
1505 Update( genTool, GetBoard(), &commit );
1506 EditFinish( genTool, GetBoard(), &commit );
1507
1508 commit.Push( _( "Edit Via Stitching" ) );
1509}
1510
1511
1513{
1514 if( usesGridCells() )
1515 m_excludedCells.insert( cellForPosition( aPos ) );
1516 else
1517 m_excludedPositions.insert( aPos );
1518
1519 MarkDirty();
1520}
1521
1522
1524{
1525 if( usesGridCells() )
1526 m_excludedCells.erase( cellForPosition( aPos ) );
1527 else
1528 m_excludedPositions.erase( aPos );
1529
1530 MarkDirty();
1531}
1532
1533
1535{
1536 m_excludedCells.clear();
1537 m_excludedPositions.clear();
1538 MarkDirty();
1539}
1540
1541
1543{
1544 if( m_netCode == 0 && !m_lastNetName.empty() )
1545 {
1546 if( const BOARD* board = GetBoard() )
1547 {
1548 if( NETINFO_ITEM* net = board->FindNet( m_lastNetName ) )
1549 m_netCode = net->GetNetCode();
1550 }
1551 }
1552
1553 return m_netCode;
1554}
1555
1556
1558{
1559 if( m_guardedNetCode == 0 && !m_lastGuardedNetName.empty() )
1560 {
1561 if( const BOARD* board = GetBoard() )
1562 {
1563 if( NETINFO_ITEM* net = board->FindNet( m_lastGuardedNetName ) )
1564 m_guardedNetCode = net->GetNetCode();
1565 }
1566 }
1567
1568 return m_guardedNetCode;
1569}
1570
1571
1572void PCB_VIA_STITCH::SetNetCode( int aNetCode )
1573{
1574 m_netCode = aNetCode;
1575 if( BOARD* board = GetBoard() )
1576 {
1577 if( NETINFO_ITEM* net = board->FindNet( aNetCode ) )
1578 m_lastNetName = net->GetNetname();
1579 else
1580 m_lastNetName.clear();
1581 }
1582
1583 for( BOARD_ITEM* item : GetBoardItems() )
1584 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
1585 bci->SetNetCode( aNetCode );
1586
1587 MarkDirty();
1588}
1589
1590
1592{
1593 m_guardedNetCode = aNetCode;
1594
1595 if( BOARD* board = GetBoard() )
1596 {
1597 if( NETINFO_ITEM* net = board->FindNet( aNetCode ) )
1598 m_lastGuardedNetName = net->GetNetname();
1599 else
1600 m_lastGuardedNetName.clear();
1601 }
1602
1603 MarkDirty();
1604}
1605
1606
1607std::vector<ZONE*> PCB_VIA_STITCH::collectAllZones( const BOARD* aBoard )
1608{
1609 std::vector<ZONE*> zones;
1610
1611 if( !aBoard )
1612 return zones;
1613
1614 zones.reserve( aBoard->Zones().size() );
1615
1616 for( ZONE* zone : aBoard->Zones() )
1617 zones.push_back( zone );
1618
1619 for( FOOTPRINT* footprint : aBoard->Footprints() )
1620 {
1621 for( ZONE* zone : footprint->Zones() )
1622 zones.push_back( zone );
1623 }
1624
1625 return zones;
1626}
1627
1628
1629std::vector<VECTOR2I>
1631 const SHAPE_POLY_SET& aGuarded, int aPitch,
1632 const std::function<bool( const VECTOR2I& )>& aIsValid )
1633{
1634 std::vector<VECTOR2I> accepted;
1635
1636 if( aPitch <= 0 )
1637 return accepted;
1638
1639 // Keeps adjacent same-side vias from doubling up where the walk rounds a corner or an
1640 // end cap. Arc length will report back a larger than expected value in this case.
1641 const int64_t minDistSq = (int64_t) ( aPitch * 0.7 ) * (int64_t) ( aPitch * 0.7 );
1642
1643 auto farEnough =
1644 [&]( const VECTOR2I& pt ) -> bool
1645 {
1646 for( const VECTOR2I& other : accepted )
1647 {
1648 VECTOR2I d = other - pt;
1649
1650 if( (int64_t) d.x * d.x + (int64_t) d.y * d.y >= minDistSq )
1651 continue;
1652
1653 if( aGuarded.Collide( SEG( pt, other ) ) )
1654 continue;
1655
1656 return false;
1657 }
1658
1659 return true;
1660 };
1661
1662 auto walkChain =
1663 [&]( const SHAPE_LINE_CHAIN& chain )
1664 {
1665 double cursor = 0.0;
1666 double nextSample = aPitch / 2.0;
1667
1668 for( int i = 0; i < chain.SegmentCount(); ++i )
1669 {
1670 SEG seg = chain.CSegment( i );
1671 double segLen = ( VECTOR2D( seg.B ) - VECTOR2D( seg.A ) ).EuclideanNorm();
1672
1673 if( segLen < 1.0 )
1674 continue;
1675
1676 while( nextSample <= cursor + segLen )
1677 {
1678 double t = ( nextSample - cursor ) / segLen;
1679 VECTOR2I pt( (int) std::round( seg.A.x + t * ( seg.B.x - seg.A.x ) ),
1680 (int) std::round( seg.A.y + t * ( seg.B.y - seg.A.y ) ) );
1681
1682 nextSample += aPitch;
1683
1684 if( aIsValid( pt ) && farEnough( pt ) )
1685 accepted.push_back( pt );
1686 }
1687
1688 cursor += segLen;
1689 }
1690 };
1691
1692 for( int o = 0; o < aEnvelope.OutlineCount(); ++o )
1693 walkChain( aEnvelope.COutline( o ) );
1694
1695 return accepted;
1696}
1697
1698
1700{
1702 {
1709
1710 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Size" ),
1712
1713 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Drill" ),
1715
1716 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, int>( _HKI( "Pitch" ),
1718
1720 .Undefined( PCB_VIA_STITCH_LAYOUT::PLAIN )
1721 .Map( PCB_VIA_STITCH_LAYOUT::PLAIN, _HKI( "Plain grid" ) )
1722 .Map( PCB_VIA_STITCH_LAYOUT::STAGGERED, _HKI( "Staggered grid" ) )
1723 .Map( PCB_VIA_STITCH_LAYOUT::POISSON, _HKI( "Poisson disk" ) );
1724
1727 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1728 {
1729 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1730 return stitch->GetMode() == PCB_VIA_STITCH_MODE::STITCH;
1731
1732 return true;
1733 } );
1734
1736 .Undefined( PCB_VIA_STITCH_MODE::STITCH )
1737 .Map( PCB_VIA_STITCH_MODE::STITCH, _HKI( "Stitch" ) )
1738 .Map( PCB_VIA_STITCH_MODE::GUARD, _HKI( "Guard" ) );
1739
1742
1743 propMgr.AddProperty( new PROPERTY<PCB_VIA_STITCH, uint32_t>( _HKI( "Seed" ),
1745 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1746 {
1747 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1748 {
1749 return stitch->GetMode() == PCB_VIA_STITCH_MODE::STITCH
1750 && stitch->GetLayout() == PCB_VIA_STITCH_LAYOUT::POISSON;
1751 }
1752 return true;
1753 } );
1754
1757
1758 propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA_STITCH, int>( _HKI( "Guarded Net" ),
1760 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
1761 {
1762 if( PCB_VIA_STITCH* stitch = dynamic_cast<PCB_VIA_STITCH*>( aItem ) )
1763 return stitch->GetMode() == PCB_VIA_STITCH_MODE::GUARD;
1764
1765 return true;
1766 } );
1767
1768 // The stitch lives on its own GAL layer and the vias are multi-layer configured separately
1769 propMgr.Mask( TYPE_HASH( PCB_VIA_STITCH ), TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) );
1770 }
1772
1773
1776
1777
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,...
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:2995
const ZONES & Zones() const
Definition board.h:468
int GetCopperLayerCount() const
Definition board.cpp:1129
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:1844
int m_DRCMaxClearance
Definition board.h:1867
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1292
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:129
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:792
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:68
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:242
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:275
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.
@ CLEARANCE_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
@ HOLE_TO_HOLE_CONSTRAINT
#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:899
@ 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