KiCad PCB EDA Suite
Loading...
Searching...
No Matches
zone.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <advanced_config.h>
23#include <bitmaps.h>
25#include <geometry/shape_null.h>
26#include <pcb_edit_frame.h>
27#include <pcb_screen.h>
28#include <board.h>
30#include <lset.h>
31#include <pad.h>
32#include <zone.h>
33#include <footprint.h>
34#include <string_utils.h>
36#include <properties/property.h>
40#include <trigo.h>
41#include <i18n_utility.h>
42#include <mutex>
43#include <magic_enum.hpp>
44
45#include <google/protobuf/any.pb.h>
46#include <api/api_enums.h>
47#include <api/api_utils.h>
48#include <api/api_pcb_utils.h>
49#include <api/board/board_types.pb.h>
50
51
54 m_Poly( nullptr ),
55 m_cornerRadius( 0 ),
56 m_priority( 0 ),
57 m_isRuleArea( false ),
62 m_ZoneClearance( 0 ),
65 m_isFilled( false ),
70 m_hatchGap( 0 ),
74 m_area( 0.0 ),
75 m_outlinearea( 0.0 )
76{
77 m_Poly = new SHAPE_POLY_SET(); // Outlines
78 SetLocalFlags( 0 ); // flags temporary used in zone calculations
79 m_fillVersion = 5; // set the "old" way to build filled polygon areas (< 6.0.x)
80
81 if( GetParentFootprint() )
82 SetIsRuleArea( true ); // Zones living in footprints have the rule area option
83
84 if( aParent->GetBoard() )
85 aParent->GetBoard()->GetDesignSettings().GetDefaultZoneSettings().ExportSetting( *this, false );
86 else
87 ZONE_SETTINGS().ExportSetting( *this, false );
88
89 m_needRefill = false; // True only after edits.
90}
91
92
93ZONE::ZONE( const ZONE& aZone ) :
94 BOARD_CONNECTED_ITEM( aZone ),
95 m_Poly( nullptr )
96{
98}
99
100
101ZONE& ZONE::operator=( const ZONE& aOther )
102{
104
106
107 return *this;
108}
109
110
111void ZONE::CopyFrom( const BOARD_ITEM* aOther )
112{
113 wxCHECK( aOther && aOther->Type() == PCB_ZONE_T, /* void */ );
114 *this = *static_cast<const ZONE*>( aOther );
115}
116
117
119{
120 delete m_Poly;
121
122 if( BOARD* board = GetBoard() )
123 board->IncrementTimeStamp();
124}
125
126
128{
129 // members are expected non initialize in this.
130 // InitDataFromSrcInCopyCtor() is expected to be called only from a copy constructor.
131
132 // Copy only useful EDA_ITEM flags:
133 m_flags = aZone.m_flags;
135
136 // Replace the outlines for aZone outlines.
137 delete m_Poly;
138 m_Poly = new SHAPE_POLY_SET( *aZone.m_Poly );
139
142 m_zoneName = aZone.m_zoneName;
143 m_priority = aZone.m_priority;
148
149 if( aLayer == UNDEFINED_LAYER )
150 SetLayerSet( aZone.GetLayerSet() );
151 else
152 SetLayerSet( { aLayer } );
153
159
161 m_ZoneClearance = aZone.m_ZoneClearance; // clearance value
166
167 m_isFilled = aZone.m_isFilled;
168 m_needRefill = aZone.m_needRefill.load();
170
173
174 m_fillMode = aZone.m_fillMode; // solid vs. hatched
176 m_hatchGap = aZone.m_hatchGap;
183
184 aZone.GetLayerSet().RunOnLayers(
185 [&]( PCB_LAYER_ID layer )
186 {
187 if( aLayer != UNDEFINED_LAYER && aLayer != layer )
188 return;
189
190 std::shared_ptr<SHAPE_POLY_SET> fill = aZone.m_FilledPolysList.at( layer );
191
192 if( fill )
193 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>( *fill );
194 else
195 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>();
196
197 m_filledPolysHash[layer] = aZone.m_filledPolysHash.at( layer );
198 m_insulatedIslands[layer] = aZone.m_insulatedIslands.at( layer );
199 } );
200
202
206
207 SetLocalFlags( aZone.GetLocalFlags() );
208
209 m_netinfo = aZone.m_netinfo;
210 m_area = aZone.m_area;
212
213 // Fresh outline; lock-free bbox cache starts invalid.
214 m_bboxCacheTimeStamp.store( -1, std::memory_order_relaxed );
215}
216
217
219{
220 return new ZONE( *this );
221}
222
223
225{
226 ZONE* clone = new ZONE( BOARD_ITEM::GetParent() );
227 clone->InitDataFromSrcInCopyCtor( *this, aLayer );
228 return clone;
229}
230
231
232BOARD_ITEM* ZONE::Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit ) const
233{
234 BOARD_ITEM* dupe = BOARD_ITEM::Duplicate( addToParentGroup, aCommit );
235
236 if( const BOARD* board = GetBoard() )
237 {
238 ZONE* newZone = static_cast<ZONE*>( dupe );
239
240 // Give the copy its own name so it does not collide with the original (issue 23131)
241 if( !newZone->GetZoneName().IsEmpty() )
242 newZone->SetZoneName( board->GetUniqueZoneName( newZone->GetZoneName(), newZone ) );
243 }
244
245 return dupe;
246}
247
248
249void ZONE::Serialize( google::protobuf::Any& aContainer ) const
250{
251 using namespace kiapi::board;
252 types::Zone zone;
254
255 zone.mutable_id()->set_value( m_Uuid.AsStdString() );
256 PackLayerSet( *zone.mutable_layers(), GetLayerSet() );
257
258 if( m_isRuleArea )
259 zone.set_type( types::ZT_RULE_AREA );
261 zone.set_type( types::ZT_TEARDROP );
262 else if( IsOnCopperLayer() )
263 zone.set_type( types::ZT_COPPER );
264 else
265 zone.set_type( types::ZT_GRAPHICAL );
266
267 kiapi::common::PackPolySet( *zone.mutable_outline(), *m_Poly );
268
269 zone.set_name( m_zoneName.ToUTF8() );
270 zone.set_priority( m_priority );
271 zone.set_filled( m_isFilled );
272
273 if( m_isRuleArea )
274 {
275 types::RuleAreaSettings* ra = zone.mutable_rule_area_settings();
276 ra->set_keepout_copper( m_doNotAllowZoneFills );
277 ra->set_keepout_footprints( m_doNotAllowFootprints );
278 ra->set_keepout_pads( m_doNotAllowPads );
279 ra->set_keepout_tracks( m_doNotAllowTracks );
280 ra->set_keepout_vias( m_doNotAllowVias );
281
282 ra->set_placement_enabled( m_placementAreaEnabled );
283 ra->set_placement_source( m_placementAreaSource.ToUTF8() );
284 ra->set_placement_source_type( ToProtoEnum<PLACEMENT_SOURCE_T,
285 types::PlacementRuleSourceType>( m_placementAreaSourceType ) );
286 }
287 else
288 {
289 types::CopperZoneSettings* cu = zone.mutable_copper_settings();
290 cu->mutable_connection()->set_zone_connection(
292
293 types::ThermalSpokeSettings* thermals = cu->mutable_connection()->mutable_thermal_spokes();
294 thermals->mutable_width()->set_value_nm( m_thermalReliefSpokeWidth );
295 thermals->mutable_gap()->set_value_nm( m_thermalReliefGap );
296 // n.b. zones don't currently have an overall thermal angle override
297
298 cu->mutable_clearance()->set_value_nm( m_ZoneClearance );
299 cu->mutable_min_thickness()->set_value_nm( m_ZoneMinThickness );
300 cu->set_island_mode(
302 cu->set_min_island_area( m_minIslandArea );
304
305 types::HatchFillSettings* hatch = cu->mutable_hatch_settings();
306 hatch->mutable_thickness()->set_value_nm( m_hatchThickness );
307 hatch->mutable_gap()->set_value_nm( m_hatchGap );
308 hatch->mutable_orientation()->set_value_degrees( m_hatchOrientation.AsDegrees() );
309 hatch->set_hatch_smoothing_ratio( m_hatchSmoothingValue );
310 hatch->set_hatch_hole_min_area_ratio( m_hatchHoleMinArea );
311
312 switch( m_hatchBorderAlgorithm )
313 {
314 default:
315 case 0: hatch->set_border_mode( types::ZHFBM_USE_MIN_ZONE_THICKNESS ); break;
316 case 1: hatch->set_border_mode( types::ZHFBM_USE_HATCH_THICKNESS ); break;
317 }
318
319 PackNet( cu->mutable_net() );
320 cu->mutable_teardrop()->set_type(
322
323 types::ThievingFillSettings* thieving = cu->mutable_thieving_settings();
324 thieving->set_pattern(
326 thieving->mutable_element_size()->set_value_nm( m_thievingSettings.element_size );
327 thieving->mutable_gap()->set_value_nm( m_thievingSettings.gap );
328 thieving->mutable_line_width()->set_value_nm( m_thievingSettings.line_width );
329 thieving->set_stagger( m_thievingSettings.stagger );
330 thieving->mutable_orientation()->set_value_degrees(
331 m_thievingSettings.orientation.AsDegrees() );
332 }
333
334 for( const auto& [layer, shape] : m_FilledPolysList )
335 {
336 types::ZoneFilledPolygons* filledLayer = zone.add_filled_polygons();
337 filledLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
338 kiapi::common::PackPolySet( *filledLayer->mutable_shapes(), *shape );
339 }
340
341 for( const auto& [layer, properties] : m_layerProperties )
342 {
343 types::ZoneLayerProperties* layerProperties = zone.add_layer_properties();
344 layerProperties->set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
345
346 if( properties.hatching_offset.has_value() )
347 {
348 PackVector2( *layerProperties->mutable_hatching_offset(),
349 properties.hatching_offset.value() );
350 }
351 }
352
353 zone.mutable_border()->set_style(
355 zone.mutable_border()->mutable_pitch()->set_value_nm( m_borderHatchPitch );
356
357 aContainer.PackFrom( zone );
358}
359
360
361bool ZONE::Deserialize( const google::protobuf::Any& aContainer )
362{
363 using namespace kiapi::board;
364 types::Zone zone;
366
367 if( !aContainer.UnpackTo( &zone ) )
368 return false;
369
370 SetUuidDirect( KIID( zone.id().value() ) );
371 SetLayerSet( UnpackLayerSet( zone.layers() ) );
372 SetAssignedPriority( zone.priority() );
373 SetZoneName( wxString::FromUTF8( zone.name() ) );
374
375 if( zone.type() == types::ZoneType::ZT_RULE_AREA )
376 m_isRuleArea = true;
377
378 if( !m_Poly )
380
381 *m_Poly = kiapi::common::UnpackPolySet( zone.outline() );
382
383 if( m_Poly->OutlineCount() == 0 )
384 return false;
385
386 if( m_isRuleArea )
387 {
388 const types::RuleAreaSettings& ra = zone.rule_area_settings();
389 m_doNotAllowZoneFills = ra.keepout_copper();
390 m_doNotAllowFootprints = ra.keepout_footprints();
391 m_doNotAllowPads = ra.keepout_pads();
392 m_doNotAllowTracks = ra.keepout_tracks();
393 m_doNotAllowVias = ra.keepout_vias();
394
395 m_placementAreaEnabled = ra.placement_enabled();
396 m_placementAreaSource = wxString::FromUTF8( ra.placement_source() );
397 m_placementAreaSourceType = FromProtoEnum<PLACEMENT_SOURCE_T>( ra.placement_source_type() );
398 }
399 else
400 {
401 const types::CopperZoneSettings& cu = zone.copper_settings();
402 m_PadConnection = FromProtoEnum<ZONE_CONNECTION>( cu.connection().zone_connection() );
403 m_thermalReliefSpokeWidth = cu.connection().thermal_spokes().width().value_nm();
404 m_thermalReliefGap = cu.connection().thermal_spokes().gap().value_nm();
405 m_ZoneClearance = cu.clearance().value_nm();
406 m_ZoneMinThickness = cu.min_thickness().value_nm();
408 m_minIslandArea = cu.min_island_area();
409 // Route through SetFillMode so the thieving single-layer / net-less invariants
410 // are enforced on protobuf imports — a direct m_fillMode assignment would leave
411 // the multi-layer set unpacked at line 355 in place for ZFM_COPPER_THIEVING.
412 SetFillMode( FromProtoEnum<ZONE_FILL_MODE>( cu.fill_mode() ) );
413
414 m_hatchThickness = cu.hatch_settings().thickness().value_nm();
415 m_hatchGap = cu.hatch_settings().gap().value_nm();
416 m_hatchOrientation = EDA_ANGLE( cu.hatch_settings().orientation().value_degrees(), DEGREES_T );
417 m_hatchSmoothingValue = cu.hatch_settings().hatch_smoothing_ratio();
418 m_hatchHoleMinArea = cu.hatch_settings().hatch_hole_min_area_ratio();
419
420 switch( cu.hatch_settings().border_mode() )
421 {
422 default:
423 case types::ZHFBM_USE_MIN_ZONE_THICKNESS: m_hatchBorderAlgorithm = 0; break;
424 case types::ZHFBM_USE_HATCH_THICKNESS: m_hatchBorderAlgorithm = 1; break;
425 }
426
427 UnpackNet( cu.net() );
428 m_teardropType = FromProtoEnum<TEARDROP_TYPE>( cu.teardrop().type() );
429
430 if( cu.has_thieving_settings() )
431 {
432 const types::ThievingFillSettings& thieving = cu.thieving_settings();
433 m_thievingSettings.pattern =
434 FromProtoEnum<THIEVING_PATTERN>( thieving.pattern() );
435
436 auto assignIfPositive = []( int aProtoValue, int& aTarget )
437 {
438 if( aProtoValue > 0 )
439 aTarget = aProtoValue;
440 };
441
442 assignIfPositive( thieving.element_size().value_nm(), m_thievingSettings.element_size );
443 assignIfPositive( thieving.gap().value_nm(), m_thievingSettings.gap );
444 assignIfPositive( thieving.line_width().value_nm(), m_thievingSettings.line_width );
445
446 m_thievingSettings.stagger = thieving.stagger();
447 m_thievingSettings.orientation =
448 EDA_ANGLE( thieving.orientation().value_degrees(), DEGREES_T );
449 }
450
451 for( const auto& properties : zone.layer_properties() )
452 {
453 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( properties.layer() );
454
455 ZONE_LAYER_PROPERTIES layerProperties;
456
457 if( properties.has_hatching_offset() )
458 layerProperties.hatching_offset = UnpackVector2( properties.hatching_offset() );
459
460 m_layerProperties[layer] = layerProperties;
461 }
462 }
463
465 m_borderHatchPitch = zone.border().pitch().value_nm();
466
467 if( zone.filled() )
468 {
469 // TODO(JE) check what else has to happen here
470 SetIsFilled( true );
471 SetNeedRefill( false );
472
473 for( const types::ZoneFilledPolygons& fillLayer : zone.filled_polygons() )
474 {
475 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( fillLayer.layer() );
476 SHAPE_POLY_SET shape = kiapi::common::UnpackPolySet( fillLayer.shapes() );
477 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>( shape );
478 }
479 }
480
481 HatchBorder();
482
483 return true;
484}
485
486
487bool ZONE::HigherPriority( const ZONE* aOther ) const
488{
489 // Teardrops are always higher priority than regular zones, so if one zone is a teardrop
490 // and the other is not, then return higher priority as the teardrop
492 return static_cast<int>( m_teardropType ) > static_cast<int>( aOther->m_teardropType );
493
494 if( m_priority != aOther->m_priority )
495 return m_priority > aOther->m_priority;
496
497 return m_Uuid > aOther->m_Uuid;
498}
499
500
501bool ZONE::SameNet( const ZONE* aOther ) const
502{
503 return GetNetCode() == aOther->GetNetCode();
504}
505
506
508{
509 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
510
511 return unFillLocked();
512}
513
514
516{
517 bool change = false;
518
519 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
520 {
521 change |= !pair.second->IsEmpty();
522 m_insulatedIslands[pair.first].clear();
523
524 // Replace the shared_ptr with a new empty object rather than clearing the existing one.
525 // This ensures that any CN_ZONE_LAYERs holding shared_ptr copies still have valid data
526 // (the old, now orphaned SHAPE_POLY_SET) while we get a fresh container.
527 pair.second = std::make_shared<SHAPE_POLY_SET>();
528 }
529
530 m_isFilled = false;
531 m_fillFlags.reset();
532
533 return change;
534}
535
536
538{
539 return HasFlag( COURTYARD_CONFLICT );
540}
541
542
544{
545 if( m_Poly->OutlineCount() == 0 || m_Poly->TotalVertices() == 0 )
546 return VECTOR2I( 0, 0 );
547
548 return GetCornerPosition( 0 );
549}
550
551
553{
554 std::lock_guard<std::mutex> lock( m_layerSetMutex );
555
556 if( m_layerSet.count() == 1 )
557 {
558 // GetFirstLayer would try to acquire the mutex again, so inline its logic here
559 if( m_layerSet.count() == 0 )
560 return UNDEFINED_LAYER;
561
562 const LSEQ uiLayers = m_layerSet.UIOrder();
563
564 if( uiLayers.size() )
565 return uiLayers[0];
566
567 return m_layerSet.Seq()[0];
568 }
569
570 return UNDEFINED_LAYER;
571}
572
573
575{
576 std::lock_guard<std::mutex> lock( m_layerSetMutex );
577
578 if( m_layerSet.count() == 0 )
579 return UNDEFINED_LAYER;
580
581 const LSEQ uiLayers = m_layerSet.UIOrder();
582
583 // This can't use m_layerSet.count() because it's possible to have a zone on
584 // a rescue layer that is not in the UI order.
585 if( uiLayers.size() )
586 return uiLayers[0];
587
588 // If it's not in the UI set at all, just return the first layer in the set.
589 // (we know the count > 0)
590 return m_layerSet.Seq()[0];
591}
592
593
595{
596 std::lock_guard<std::mutex> lock( m_layerSetMutex );
597 return ( m_layerSet & LSET::AllCuMask() ).count() > 0;
598}
599
600
601bool ZONE::SetNetCode( int aNetCode, bool aNoAssert )
602{
603 if( IsCopperThieving() )
604 aNetCode = 0;
605
606 return BOARD_CONNECTED_ITEM::SetNetCode( aNetCode, aNoAssert );
607}
608
609
610void ZONE::SetNet( NETINFO_ITEM* aNetInfo )
611{
612 if( IsCopperThieving() )
613 aNetInfo = nullptr;
614
616}
617
618
620{
621 SetLayerSet( LSET( { aLayer } ) );
622}
623
624
626{
627 if( m_fillMode == aFillMode )
628 return;
629
630 SetNeedRefill( true );
631 m_fillMode = aFillMode;
632
633 // Thieving zones are net-less and single-layer; clamp on transition so a
634 // multi-layer netted POLYGONS zone converted via the property panel or a
635 // load-time SetLayerSet-then-SetFillMode sequence cannot keep stale state.
637 {
640 }
641}
642
643
644void ZONE::SetLayerSet( const LSET& aLayerSet )
645{
646 if( aLayerSet.count() == 0 )
647 return;
648
649 // Thieving zones are single-layer; clamp here so direct callers cannot violate
650 // the invariant. UIOrder().front() matches GetFirstLayer().
651 const LSET effectiveSet = ( IsCopperThieving() && aLayerSet.count() > 1 )
652 ? LSET( { aLayerSet.UIOrder().front() } )
653 : aLayerSet;
654
655 std::scoped_lock lock( m_layerSetMutex, m_filledPolysListMutex );
656
657 if( m_layerSet != effectiveSet )
658 {
659 SetNeedRefill( true );
660
661 unFillLocked();
662
663 m_FilledPolysList.clear();
664 m_filledPolysHash.clear();
665 m_insulatedIslands.clear();
666
667 effectiveSet.RunOnLayers(
668 [&]( PCB_LAYER_ID layer )
669 {
670 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>();
671 m_filledPolysHash[layer] = {};
672 m_insulatedIslands[layer] = {};
673 } );
674
675 std::erase_if( m_layerProperties,
676 [&]( const auto& item )
677 {
678 return !effectiveSet.Contains( item.first );
679 } );
680 }
681
682 m_layerSet = effectiveSet;
683}
684
685
686void ZONE::SetLayerProperties( const std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aOther )
687{
688 m_layerProperties = aOther;
689}
690
691
692std::vector<int> ZONE::ViewGetLayers() const
693{
694 std::lock_guard<std::mutex> lock( m_layerSetMutex );
695
696 std::vector<int> layers;
697 layers.reserve( 2 * m_layerSet.count() + 1 );
698
699 m_layerSet.RunOnLayers(
700 [&]( PCB_LAYER_ID layer )
701 {
702 layers.push_back( layer );
703 layers.push_back( layer + static_cast<int>( LAYER_ZONE_START ) );
704 } );
705
706 layers.push_back( LAYER_CONFLICTS_SHADOW );
707
708 return layers;
709}
710
711
712double ZONE::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
713{
714 if( !aView )
715 return LOD_SHOW;
716
717 if( !aView->IsLayerVisibleCached( LAYER_ZONES ) )
718 return LOD_HIDE;
719
720 if( FOOTPRINT* parentFP = GetParentFootprint() )
721 {
722 const LSET zl = GetLayerSet();
723 bool onFront = ( zl & LSET::FrontMask() ).any();
724 bool onBack = ( zl & LSET::BackMask() ).any();
725
726 if( !onFront && !onBack )
727 {
728 onFront = parentFP->GetLayer() == F_Cu;
729 onBack = parentFP->GetLayer() == B_Cu;
730 }
731
732 const bool frHidden = !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_FR );
733 const bool bkHidden = !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_BK );
734
735 if( onFront && !onBack && frHidden )
736 return LOD_HIDE;
737
738 if( onBack && !onFront && bkHidden )
739 return LOD_HIDE;
740
741 if( onFront && onBack && frHidden && bkHidden )
742 return LOD_HIDE;
743 }
744
745 // Other layers are shown without any conditions
746 return LOD_SHOW;
747}
748
749
750bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer ) const
751{
752 std::lock_guard<std::mutex> lock( m_layerSetMutex );
753 return m_layerSet.test( aLayer );
754}
755
756
758{
759 if( GetParentFootprint() )
760 return GetBoardOutline().BBox();
761
762 return m_Poly->BBox();
763}
764
765
767{
768 if( const BOARD* board = GetBoard() )
769 {
770 // Lock-free fast path, valid while the board timestamp matches what we cached for.
771 // Skips the caches mutex that otherwise serializes every fill worker.
772 if( m_bboxCacheTimeStamp.load( std::memory_order_acquire ) == board->GetTimeStamp() )
773 return m_bboxCache;
774
775 std::unordered_map<const ZONE*, BOX2I>& cache = board->m_ZoneBBoxCache;
776
777 {
778 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
779
780 auto cacheIter = cache.find( this );
781
782 if( cacheIter != cache.end() )
783 return cacheIter->second;
784 }
785
786 BOX2I bbox = computeBoundingBox();
787
788 {
789 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
790 cache[ this ] = bbox;
791 }
792
793 return bbox;
794 }
795
796 return computeBoundingBox();
797}
798
799
801{
802 const BOARD* board = GetBoard();
803 BOX2I bbox = computeBoundingBox();
804
805 if( board )
806 {
807 // Board cache, for callers that read it directly.
808 {
809 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
810 board->m_ZoneBBoxCache[this] = bbox;
811 }
812
813 // Per-zone lock-free copy. Single-threaded per zone, so box-before-timestamp (release)
814 // suffices for the acquiring reader in GetBoundingBox().
815 m_bboxCache = bbox;
816 m_bboxCacheTimeStamp.store( board->GetTimeStamp(), std::memory_order_release );
817 }
818}
819
820
821int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
822{
823 if( aPad->GetLocalThermalGapOverride() == 0 )
824 {
825 if( aSource )
826 *aSource = _( "zone" );
827
828 return m_thermalReliefGap;
829 }
830
831 return aPad->GetLocalThermalGapOverride( aSource );
832
833}
834
835
836void ZONE::SetCornerRadius( unsigned int aRadius )
837{
838 if( m_cornerRadius != aRadius )
839 SetNeedRefill( true );
840
841 m_cornerRadius = aRadius;
842}
843
844
846
847
849{
850 if( !m_filledPolysHash.count( aLayer ) )
851 return g_nullPoly.GetHash();
852 else
853 return m_filledPolysHash.at( aLayer );
854}
855
856
858{
859 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
860
861 if( !m_FilledPolysList.count( aLayer ) )
862 m_filledPolysHash[aLayer] = g_nullPoly.GetHash();
863 else
864 m_filledPolysHash[aLayer] = m_FilledPolysList.at( aLayer )->GetHash();
865}
866
867
872
873
875{
877
878 if( const FOOTPRINT* fp = GetParentFootprint() )
879 {
880 const TRANSFORM_TRS& xform = fp->GetTransform();
881
882 for( auto it = poly.IterateWithHoles(); it; it++ )
883 poly.SetVertex( it.GetIndex(), xform.Apply( *it ) );
884 }
885
886 return poly;
887}
888
889
890bool ZONE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
891{
892 // When looking for an "exact" hit aAccuracy will be 0 which works poorly for very thin
893 // lines. Give it a floor.
894 int accuracy = std::max( aAccuracy, pcbIUScale.mmToIU( 0.1 ) );
895
896 return HitTestForCorner( aPosition, accuracy * 2 ) || HitTestForEdge( aPosition, accuracy );
897}
898
899
900bool ZONE::HitTestForCorner( const VECTOR2I& refPos, int aAccuracy,
901 SHAPE_POLY_SET::VERTEX_INDEX* aCornerHit ) const
902{
903 VECTOR2I libPos = refPos;
904
905 if( const FOOTPRINT* fp = GetParentFootprint() )
906 libPos = fp->GetTransform().InverseApply( refPos );
907
908 return m_Poly->CollideVertex( libPos, aCornerHit, aAccuracy );
909}
910
911
912bool ZONE::HitTestForEdge( const VECTOR2I& refPos, int aAccuracy,
913 SHAPE_POLY_SET::VERTEX_INDEX* aCornerHit ) const
914{
915 VECTOR2I libPos = refPos;
916
917 if( const FOOTPRINT* fp = GetParentFootprint() )
918 libPos = fp->GetTransform().InverseApply( refPos );
919
920 return m_Poly->CollideEdge( libPos, aCornerHit, aAccuracy );
921}
922
923
924bool ZONE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
925{
926 // Calculate bounding box for zone
927 BOX2I bbox = GetBoundingBox();
928 bbox.Normalize();
929
930 BOX2I arect = aRect;
931 arect.Normalize();
932 arect.Inflate( aAccuracy );
933
934 if( aContained )
935 {
936 return arect.Contains( bbox );
937 }
938 else
939 {
940 // Fast test: if aBox is outside the polygon bounding box, rectangles cannot intersect
941 if( !arect.Intersects( bbox ) )
942 return false;
943
944 SHAPE_POLY_SET boardOutline = GetBoardOutline();
945 int count = boardOutline.TotalVertices();
946
947 for( int ii = 0; ii < count; ii++ )
948 {
949 VECTOR2I vertex = boardOutline.CVertex( ii );
950 VECTOR2I vertexNext = boardOutline.CVertex( ( ii + 1 ) % count );
951
952 // Test if the point is within the rect
953 if( arect.Contains( vertex ) )
954 return true;
955
956 // Test if this edge intersects the rect
957 if( arect.Intersects( vertex, vertexNext ) )
958 return true;
959 }
960
961 return false;
962 }
963}
964
965
966bool ZONE::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
967{
968 SHAPE_POLY_SET boardOutline = GetBoardOutline();
969
970 if( aContained )
971 {
972 auto outlineIntersectingSelection = [&]()
973 {
974 for( auto segment = boardOutline.IterateSegments(); segment; segment++ )
975 {
976 if( aPoly.Intersects( *segment ) )
977 return true;
978 }
979
980 return false;
981 };
982
983 // In the case of contained selection, all vertices of the zone outline must be inside
984 // the selection polygon, so we can check only the first vertex.
985 auto vertexInsideSelection = [&]()
986 {
987 return aPoly.PointInside( boardOutline.CVertex( 0 ) );
988 };
989
990 return vertexInsideSelection() && !outlineIntersectingSelection();
991 }
992 else
993 {
994 // Touching selection - check if any segment of the zone contours collides with the
995 // selection shape.
996 for( auto segment = boardOutline.IterateSegmentsWithHoles(); segment; segment++ )
997 {
998 if( aPoly.PointInside( ( *segment ).A ) )
999 return true;
1000
1001 if( aPoly.Intersects( *segment ) )
1002 return true;
1003
1004 // Note: aPoly.Collide() could be used instead of two test above, but it is 3x slower.
1005 }
1006
1007 return false;
1008 }
1009}
1010
1011
1012std::optional<int> ZONE::GetLocalClearance() const
1013{
1014 return m_isRuleArea ? 0 : m_ZoneClearance;
1015}
1016
1017
1018bool ZONE::HitTestFilledArea( PCB_LAYER_ID aLayer, const VECTOR2I& aRefPos, int aAccuracy ) const
1019{
1020 // Rule areas have no filled area, but it's generally nice to treat their interior as if it were
1021 // filled so that people don't have to select them by their outline (which is min-width)
1022 if( GetIsRuleArea() )
1023 {
1024 VECTOR2I libPos = aRefPos;
1025
1026 if( const FOOTPRINT* fp = GetParentFootprint() )
1027 libPos = fp->GetTransform().InverseApply( aRefPos );
1028
1029 return m_Poly->Contains( libPos, -1, aAccuracy );
1030 }
1031
1032 std::shared_ptr<SHAPE_POLY_SET> fillPolys;
1033
1034 {
1035 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1036
1037 if( !m_FilledPolysList.count( aLayer ) )
1038 return false;
1039
1040 fillPolys = m_FilledPolysList.at( aLayer );
1041 }
1042
1043 return fillPolys->Contains( aRefPos, -1, aAccuracy );
1044}
1045
1046
1047bool ZONE::HitTestCutout( const VECTOR2I& aRefPos, int* aOutlineIdx, int* aHoleIdx ) const
1048{
1049 VECTOR2I libPos = aRefPos;
1050
1051 if( const FOOTPRINT* fp = GetParentFootprint() )
1052 libPos = fp->GetTransform().InverseApply( aRefPos );
1053
1054 // Iterate over each outline polygon in the zone and then iterate over
1055 // each hole it has to see if the point is in it.
1056 for( int i = 0; i < m_Poly->OutlineCount(); i++ )
1057 {
1058 for( int j = 0; j < m_Poly->HoleCount( i ); j++ )
1059 {
1060 if( m_Poly->Hole( i, j ).PointInside( libPos ) )
1061 {
1062 if( aOutlineIdx )
1063 *aOutlineIdx = i;
1064
1065 if( aHoleIdx )
1066 *aHoleIdx = j;
1067
1068 return true;
1069 }
1070 }
1071 }
1072
1073 return false;
1074}
1075
1076
1077void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1078{
1079 wxString msg = GetFriendlyName();
1080
1081 aList.emplace_back( _( "Type" ), msg );
1082
1083 if( GetIsRuleArea() )
1084 {
1085 msg.Empty();
1086
1087 if( GetDoNotAllowVias() )
1088 AccumulateDescription( msg, _( "No vias" ) );
1089
1090 if( GetDoNotAllowTracks() )
1091 AccumulateDescription( msg, _( "No tracks" ) );
1092
1093 if( GetDoNotAllowPads() )
1094 AccumulateDescription( msg, _( "No pads" ) );
1095
1097 AccumulateDescription( msg, _( "No zone fills" ) );
1098
1100 AccumulateDescription( msg, _( "No footprints" ) );
1101
1102 if( !msg.IsEmpty() )
1103 aList.emplace_back( _( "Restrictions" ), msg );
1104
1106 aList.emplace_back( _( "Placement source" ), UnescapeString( GetPlacementAreaSource() ) );
1107 }
1108 else if( IsOnCopperLayer() )
1109 {
1110 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1111 {
1112 aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
1113
1114 aList.emplace_back( _( "Resolved Netclass" ),
1115 UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
1116 }
1117
1118 // Display priority level
1119 aList.emplace_back( _( "Priority" ), wxString::Format( wxT( "%d" ), GetAssignedPriority() ) );
1120 }
1121
1122 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1123 {
1124 if( IsLocked() )
1125 aList.emplace_back( _( "Status" ), _( "Locked" ) );
1126 }
1127
1128 LSEQ layers = m_layerSet.Seq();
1129 wxString layerDesc;
1130
1131 if( layers.size() == 1 )
1132 {
1133 layerDesc.Printf( _( "%s" ), GetBoard()->GetLayerName( layers[0] ) );
1134 }
1135 else if (layers.size() == 2 )
1136 {
1137 layerDesc.Printf( _( "%s and %s" ),
1138 GetBoard()->GetLayerName( layers[0] ),
1139 GetBoard()->GetLayerName( layers[1] ) );
1140 }
1141 else if (layers.size() == 3 )
1142 {
1143 layerDesc.Printf( _( "%s, %s and %s" ),
1144 GetBoard()->GetLayerName( layers[0] ),
1145 GetBoard()->GetLayerName( layers[1] ),
1146 GetBoard()->GetLayerName( layers[2] ) );
1147 }
1148 else if( layers.size() > 3 )
1149 {
1150 layerDesc.Printf( _( "%s, %s and %d more" ),
1151 GetBoard()->GetLayerName( layers[0] ),
1152 GetBoard()->GetLayerName( layers[1] ),
1153 static_cast<int>( layers.size() - 2 ) );
1154 }
1155
1156 aList.emplace_back( _( "Layer" ), layerDesc );
1157
1158 if( !m_zoneName.empty() )
1159 aList.emplace_back( _( "Name" ), m_zoneName );
1160
1161 if( !GetIsRuleArea() ) // Show fill mode only for not rule areas
1162 {
1163 switch( m_fillMode )
1164 {
1165 case ZONE_FILL_MODE::POLYGONS: msg = _( "Solid" ); break;
1166 case ZONE_FILL_MODE::HATCH_PATTERN: msg = _( "Hatched" ); break;
1167 default: msg = _( "Unknown" ); break;
1168 }
1169
1170 aList.emplace_back( _( "Fill Mode" ), msg );
1171
1172 aList.emplace_back( _( "Filled Area" ),
1174
1175 wxString source;
1176 int clearance = GetOwnClearance( UNDEFINED_LAYER, &source );
1177
1178 if( !source.IsEmpty() )
1179 {
1180 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ), aFrame->MessageTextFromValue( clearance ) ),
1181 wxString::Format( _( "(from %s)" ), source ) );
1182 }
1183 }
1184
1185 int count = 0;
1186
1187 if( GetIsRuleArea() )
1188 {
1189 double outline_area = CalculateOutlineArea();
1190 aList.emplace_back( _( "Outline Area" ),
1191 aFrame->MessageTextFromValue( outline_area, true, EDA_DATA_TYPE::AREA ) );
1192
1193 const SHAPE_POLY_SET* area_outline = Outline();
1194 count = area_outline->FullPointCount();
1195 }
1196 else if( !m_FilledPolysList.empty() )
1197 {
1198 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& ii: m_FilledPolysList )
1199 count += ii.second->TotalVertices();
1200 }
1201
1202 aList.emplace_back( _( "Corner Count" ), wxString::Format( wxT( "%d" ), count ) );
1203}
1204
1205
1206void ZONE::Move( const VECTOR2I& offset )
1207{
1208 VECTOR2I outlineOffset = offset;
1209
1210 if( const FOOTPRINT* fp = GetParentFootprint() )
1211 {
1212 const TRANSFORM_TRS& xform = fp->GetTransform();
1213 outlineOffset = xform.InverseApply( offset ) - xform.InverseApply( VECTOR2I( 0, 0 ) );
1214 }
1215
1216 m_Poly->Move( outlineOffset );
1217
1218 // Translate existing hatch lines instead of regenerating them. HatchBorder() is expensive
1219 // (O(n*m) segment intersections + point-in-polygon tests) and the hatch pattern is
1220 // invariant under translation.
1221 for( SEG& seg : m_borderHatchLines )
1222 {
1223 seg.A += outlineOffset;
1224 seg.B += outlineOffset;
1225 }
1226
1227 /* move fills */
1228 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1229 pair.second->Move( offset );
1230
1231 /*
1232 * move boundingbox cache
1233 *
1234 * While the cache will get nuked at the conclusion of the operation, we use it for some
1235 * things (such as drawing the parent group) during the move.
1236 */
1237 if( GetBoard() )
1238 {
1239 auto it = GetBoard()->m_ZoneBBoxCache.find( this );
1240
1241 if( it != GetBoard()->m_ZoneBBoxCache.end() )
1242 it->second.Move( offset );
1243 }
1244
1245 // Move doesn't bump the board timestamp, so invalidate the lock-free copy rather than race
1246 // readers by mutating it. GetBoundingBox() falls back to the board cache (moved above).
1247 m_bboxCacheTimeStamp.store( -1, std::memory_order_release );
1248}
1249
1250
1252{
1253 if( GetIsRuleArea() )
1254 return _( "Rule Area" );
1255 else if( IsTeardropArea() )
1256 return _( "Teardrop Area" );
1257 else if( IsOnCopperLayer() )
1258 return _( "Copper Zone" );
1259 else
1260 return _( "Non-copper Zone" );
1261}
1262
1263
1264void ZONE::MoveEdge( const VECTOR2I& offset, int aEdge )
1265{
1266 int next_corner;
1267
1268 if( m_Poly->GetNeighbourIndexes( aEdge, nullptr, &next_corner ) )
1269 {
1270 VECTOR2I libOffset = offset;
1271
1272 if( const FOOTPRINT* fp = GetParentFootprint() )
1273 {
1274 const TRANSFORM_TRS& xform = fp->GetTransform();
1275 libOffset = xform.InverseApply( offset ) - xform.InverseApply( VECTOR2I( 0, 0 ) );
1276 }
1277
1278 m_Poly->SetVertex( aEdge, m_Poly->CVertex( aEdge ) + libOffset );
1279 m_Poly->SetVertex( next_corner, m_Poly->CVertex( next_corner ) + libOffset );
1280 HatchBorder();
1281
1282 SetNeedRefill( true );
1283 }
1284}
1285
1286
1287void ZONE::Rotate( const VECTOR2I& aCentre, const EDA_ANGLE& aAngle )
1288{
1289 VECTOR2I outlineCentre = aCentre;
1290
1291 if( const FOOTPRINT* fp = GetParentFootprint() )
1292 outlineCentre = fp->GetTransform().InverseApply( aCentre );
1293
1294 m_Poly->Rotate( aAngle, outlineCentre );
1295 HatchBorder();
1296
1297 /* rotate filled areas: */
1298 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1299 pair.second->Rotate( aAngle, aCentre );
1300}
1301
1302
1303void ZONE::OnFootprintRescaled( double aRatioX, double aRatioY, double /* aLinearFactor */,
1304 const VECTOR2I& /* aAnchor */, const EDA_ANGLE& /* aParentRotate */ )
1305{
1306 if( aRatioX == 1.0 && aRatioY == 1.0 )
1307 return;
1308
1309 // Zone outline auto-derives from lib storage through the parent transform.
1310 // Just invalidate the fill since geometry-on-screen has changed.
1311 SetNeedRefill( true );
1312 UnFill();
1313}
1314
1315
1316void ZONE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
1317{
1318 Mirror( aCentre, aFlipDirection );
1319
1320 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> fillsCopy;
1321
1322 for( auto& [oldLayer, shapePtr] : m_FilledPolysList )
1323 fillsCopy[oldLayer] = *shapePtr;
1324
1325 std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES> layerPropertiesCopy = m_layerProperties;
1326
1327 LSET flipped;
1328
1329 for( PCB_LAYER_ID layer : GetLayerSet() )
1330 flipped.set( GetBoard()->FlipLayer( layer ) );
1331
1332 SetLayerSet( flipped );
1333
1334 for( auto& [oldLayer, properties] : layerPropertiesCopy )
1335 {
1336 PCB_LAYER_ID newLayer = GetBoard()->FlipLayer( oldLayer );
1337 m_layerProperties[newLayer] = properties;
1338 }
1339
1340 for( auto& [oldLayer, shape] : fillsCopy )
1341 {
1342 PCB_LAYER_ID newLayer = GetBoard()->FlipLayer( oldLayer );
1343 SetFilledPolysList( newLayer, shape );
1344 }
1345}
1346
1347
1348void ZONE::Mirror( const VECTOR2I& aMirrorRef, FLIP_DIRECTION aFlipDirection )
1349{
1350 VECTOR2I outlineRef = aMirrorRef;
1351
1352 if( const FOOTPRINT* fp = GetParentFootprint() )
1353 outlineRef = fp->GetTransform().InverseApply( aMirrorRef );
1354
1355 m_Poly->Mirror( outlineRef, aFlipDirection );
1356
1357 HatchBorder();
1358
1359 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1360 pair.second->Mirror( aMirrorRef, aFlipDirection );
1361}
1362
1363
1364void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx )
1365{
1366 // Ensure the requested cutout is valid
1367 if( m_Poly->OutlineCount() < aOutlineIdx || m_Poly->HoleCount( aOutlineIdx ) < aHoleIdx )
1368 return;
1369
1370 SHAPE_POLY_SET cutPoly( m_Poly->Hole( aOutlineIdx, aHoleIdx ) );
1371
1372 // Add the cutout back to the zone
1373 m_Poly->BooleanAdd( cutPoly );
1374
1375 SetNeedRefill( true );
1376}
1377
1378
1379void ZONE::AddPolygon( const SHAPE_LINE_CHAIN& aPolygon )
1380{
1381 wxASSERT( aPolygon.IsClosed() );
1382
1383 // Add the outline as a new polygon in the polygon set
1384 if( m_Poly->OutlineCount() == 0 )
1385 m_Poly->AddOutline( aPolygon );
1386 else
1387 m_Poly->AddHole( aPolygon );
1388
1389 SetNeedRefill( true );
1390}
1391
1392
1393void ZONE::AddPolygon( std::vector<VECTOR2I>& aPolygon )
1394{
1395 if( aPolygon.empty() )
1396 return;
1397
1398 SHAPE_LINE_CHAIN outline;
1399
1400 // Create an outline and populate it with the points of aPolygon
1401 for( const VECTOR2I& pt : aPolygon )
1402 outline.Append( pt );
1403
1404 outline.SetClosed( true );
1405
1406 AddPolygon( outline );
1407}
1408
1409
1410bool ZONE::AppendCorner( VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication )
1411{
1412 // Ensure the main outline exists:
1413 if( m_Poly->OutlineCount() == 0 )
1414 m_Poly->NewOutline();
1415
1416 // If aHoleIdx >= 0, the corner musty be added to the hole, index aHoleIdx.
1417 // (remember: the index of the first hole is 0)
1418 // Return error if it does not exist.
1419 if( aHoleIdx >= m_Poly->HoleCount( 0 ) )
1420 return false;
1421
1422 m_Poly->Append( aPosition.x, aPosition.y, -1, aHoleIdx, aAllowDuplication );
1423
1424 SetNeedRefill( true );
1425
1426 return true;
1427}
1428
1429
1430wxString ZONE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
1431{
1432 LSEQ layers = m_layerSet.Seq();
1433 wxString layerDesc;
1434
1435 if( layers.size() == 1 )
1436 {
1437 layerDesc.Printf( _( "on %s" ), GetBoard()->GetLayerName( layers[0] ) );
1438 }
1439 else if (layers.size() == 2 )
1440 {
1441 layerDesc.Printf( _( "on %s and %s" ),
1442 GetBoard()->GetLayerName( layers[0] ),
1443 GetBoard()->GetLayerName( layers[1] ) );
1444 }
1445 else if (layers.size() == 3 )
1446 {
1447 layerDesc.Printf( _( "on %s, %s and %s" ),
1448 GetBoard()->GetLayerName( layers[0] ),
1449 GetBoard()->GetLayerName( layers[1] ),
1450 GetBoard()->GetLayerName( layers[2] ) );
1451 }
1452 else if( layers.size() > 3 )
1453 {
1454 layerDesc.Printf( _( "on %s, %s and %zu more" ),
1455 GetBoard()->GetLayerName( layers[0] ),
1456 GetBoard()->GetLayerName( layers[1] ),
1457 layers.size() - 2 );
1458 }
1459
1460 if( GetIsRuleArea() )
1461 {
1462 if( GetZoneName().IsEmpty() )
1463 {
1464 return wxString::Format( _( "Rule Area %s" ),
1465 layerDesc );
1466 }
1467 else
1468 {
1469 return wxString::Format( _( "Rule area '%s' %s" ),
1470 GetZoneName(),
1471 layerDesc );
1472 }
1473 }
1474 else if( IsTeardropArea() )
1475 {
1476 return wxString::Format( _( "Teardrop %s %s" ),
1477 GetNetnameMsg(),
1478 layerDesc );
1479 }
1480 else
1481 {
1482 if( GetZoneName().IsEmpty() )
1483 {
1484 return wxString::Format( _( "Zone %s %s, priority %d" ),
1485 GetNetnameMsg(),
1486 layerDesc,
1488 }
1489 else
1490 {
1491 return wxString::Format( _( "Zone '%s' %s %s, priority %d" ),
1492 GetZoneName(),
1493 GetNetnameMsg(),
1494 layerDesc,
1496 }
1497 }
1498}
1499
1500
1502 int aBorderHatchPitch, bool aRebuildBorderHatch )
1503{
1504 aBorderHatchPitch = std::max( aBorderHatchPitch, pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MINDIST_MM ) );
1505 aBorderHatchPitch = std::min( aBorderHatchPitch, pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MAXDIST_MM ) );
1506 SetBorderHatchPitch( aBorderHatchPitch );
1507 m_borderStyle = aBorderHatchStyle;
1508
1509 if( aRebuildBorderHatch )
1510 HatchBorder();
1511}
1512
1513
1515{
1516 m_borderHatchLines.clear();
1517}
1518
1519
1521{
1522 UnHatchBorder();
1523
1525 || m_borderHatchPitch == 0
1526 || m_Poly->IsEmpty() )
1527 {
1528 return;
1529 }
1530
1531 // set the "length" of hatch lines (the length on horizontal axis)
1532 int hatch_line_len = m_borderHatchPitch; // OK for DIAGONAL_EDGE style
1533
1534 // Calculate spacing between 2 hatch lines
1535 int spacing = m_borderHatchPitch; // OK for DIAGONAL_EDGE style
1536
1538 {
1539 // The spacing is twice the spacing for DIAGONAL_EDGE because one
1540 // full diagonal replaces 2 edge diagonal hatch segments in code
1541 spacing = m_borderHatchPitch * 2;
1542 hatch_line_len = -1; // Use full diagonal hatch line
1543 }
1544
1545 // To have a better look, give a slope depending on the layer
1546 int layer = GetFirstLayer();
1547 std::vector<double> slopes;
1548
1549 if( IsTeardropArea() )
1550 slopes = { 0.7, -0.7 };
1551 else if( layer & 1 )
1552 slopes = { 1 };
1553 else
1554 slopes = { -1 };
1555
1556 m_borderHatchLines = m_Poly->GenerateHatchLines( slopes, spacing, hatch_line_len );
1557}
1558
1559
1560std::vector<SEG> ZONE::GetHatchLines() const
1561{
1562 const FOOTPRINT* fp = GetParentFootprint();
1563
1564 if( !fp )
1565 return m_borderHatchLines;
1566
1567 const TRANSFORM_TRS& xform = fp->GetTransform();
1568 std::vector<SEG> result;
1569 result.reserve( m_borderHatchLines.size() );
1570
1571 for( const SEG& seg : m_borderHatchLines )
1572 result.emplace_back( xform.Apply( seg.A ), xform.Apply( seg.B ) );
1573
1574 return result;
1575}
1576
1577
1579{
1580 return pcbIUScale.mmToIU( ZONE_BORDER_HATCH_DIST_MM );
1581}
1582
1583
1585{
1586 return BITMAPS::add_zone;
1587}
1588
1589
1591{
1592 wxASSERT( aImage->Type() == PCB_ZONE_T );
1593
1594 std::swap( *static_cast<ZONE*>( this ), *static_cast<ZONE*>( aImage) );
1595}
1596
1597
1599{
1600 if( aLayer == UNDEFINED_LAYER )
1601 {
1602 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1603
1604 for( auto& [ layer, poly ] : m_FilledPolysList )
1605 poly->CacheTriangulation( false, aSubmitter );
1606
1607 m_Poly->CacheTriangulation();
1608 }
1609 else
1610 {
1611 // Grab a shared_ptr copy under the lock, then triangulate outside it.
1612 // Each layer's SHAPE_POLY_SET is independent, so concurrent triangulation
1613 // of different layers is safe once we have the shared_ptr.
1614 std::shared_ptr<SHAPE_POLY_SET> poly;
1615
1616 {
1617 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1618
1619 auto it = m_FilledPolysList.find( aLayer );
1620
1621 if( it != m_FilledPolysList.end() )
1622 poly = it->second;
1623 }
1624
1625 if( poly )
1626 poly->CacheTriangulation( false, aSubmitter );
1627 }
1628}
1629
1630
1631bool ZONE::IsIsland( PCB_LAYER_ID aLayer, int aPolyIdx ) const
1632{
1633 if( GetNetCode() < 1 )
1634 return true;
1635
1636 if( !m_insulatedIslands.count( aLayer ) )
1637 return false;
1638
1639 return m_insulatedIslands.at( aLayer ).count( aPolyIdx );
1640}
1641
1642
1643void ZONE::GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE*>* aSameNetCollidingZones,
1644 std::vector<ZONE*>* aOtherNetIntersectingZones ) const
1645{
1646 int epsilon = pcbIUScale.mmToIU( 0.001 );
1647 BOX2I bbox = GetBoundingBox();
1648
1649 bbox.Inflate( epsilon );
1650
1651 for( ZONE* candidate : GetBoard()->Zones() )
1652 {
1653 if( candidate == this )
1654 continue;
1655
1656 if( !candidate->GetLayerSet().test( aLayer ) )
1657 continue;
1658
1659 if( candidate->GetIsRuleArea() || candidate->IsTeardropArea() )
1660 continue;
1661
1662 if( !candidate->GetBoundingBox().Intersects( bbox ) )
1663 continue;
1664
1665 if( candidate->GetNetCode() == GetNetCode() )
1666 {
1667 SHAPE_POLY_SET selfBoard = GetBoardOutline();
1668 SHAPE_POLY_SET candidateBoard = candidate->GetBoardOutline();
1669
1670 if( selfBoard.Collide( &candidateBoard ) )
1671 aSameNetCollidingZones->push_back( candidate );
1672 }
1673 else
1674 {
1675 aOtherNetIntersectingZones->push_back( candidate );
1676 }
1677 }
1678}
1679
1680
1682 SHAPE_POLY_SET* aBoardOutline,
1683 SHAPE_POLY_SET* aSmoothedPolyWithApron ) const
1684{
1685 if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations will not like it ...
1686 return false;
1687
1688 // Processing of arc shapes in zones is not yet supported because Clipper can't do boolean
1689 // operations on them. The poly outline must be converted to segments first.
1690 SHAPE_POLY_SET flattened = GetBoardOutline();
1691 flattened.ClearArcs();
1692
1693 if( GetIsRuleArea() )
1694 {
1695 // We like keepouts just the way they are....
1696 aSmoothedPoly = std::move( flattened );
1697 return true;
1698 }
1699
1700 const BOARD* board = GetBoard();
1701 bool keepExternalFillets = false;
1704
1705 if( IsTeardropArea() )
1706 {
1707 // We use teardrop shapes with no smoothing; these shapes are already optimized
1708 smooth_requested = false;
1709 }
1710
1711 if( board )
1712 keepExternalFillets = board->GetDesignSettings().m_ZoneKeepExternalFillets;
1713
1714 auto smooth =
1715 [&]( SHAPE_POLY_SET& aPoly )
1716 {
1717 if( !smooth_requested )
1718 return;
1719
1720 switch( m_cornerSmoothingType )
1721 {
1723 aPoly = aPoly.Chamfer( (int) m_cornerRadius );
1724 break;
1725
1727 aPoly = aPoly.Fillet( (int) m_cornerRadius, GetMaxError() );
1728 break;
1729
1730 default:
1731 break;
1732 }
1733 };
1734
1735 SHAPE_POLY_SET* maxExtents = &flattened;
1736 SHAPE_POLY_SET withFillets;
1737
1738 aSmoothedPoly = flattened;
1739
1740 // Should external fillets (that is, those applied to concave corners) be kept? While it
1741 // seems safer to never have copper extend outside the zone outline, 5.1.x and prior did
1742 // indeed fill them so we leave the mode available.
1743 if( keepExternalFillets && smooth_requested )
1744 {
1745 withFillets = flattened;
1746 smooth( withFillets );
1747 withFillets.BooleanAdd( flattened );
1748 maxExtents = &withFillets;
1749 }
1750
1751 // We now add in the areas of any same-net, intersecting zones. This keeps us from smoothing
1752 // corners at an intersection (which often produces undesired divots between the intersecting
1753 // zones -- see #2752).
1754 //
1755 // After smoothing, we'll subtract back out everything outside of our zone.
1756 std::vector<ZONE*> sameNetCollidingZones;
1757 std::vector<ZONE*> diffNetIntersectingZones;
1758 GetInteractingZones( aLayer, &sameNetCollidingZones, &diffNetIntersectingZones );
1759
1760 for( ZONE* sameNetZone : sameNetCollidingZones )
1761 {
1762 BOX2I sameNetBoundingBox = sameNetZone->GetBoundingBox();
1763
1764 // Note: a two-pass algorithm could use sameNetZone's actual fill instead of its outline.
1765 // This would obviate the need for the below wrinkles, in addition to fixing both issues
1766 // in #16095.
1767 // (And we wouldn't need to collect all the diffNetIntersectingZones either.)
1768
1769 SHAPE_POLY_SET sameNetPoly = sameNetZone->GetBoardOutline();
1770 sameNetPoly.ClearArcs();
1771
1772 SHAPE_POLY_SET diffNetPoly;
1773
1774 // Of course there's always a wrinkle. The same-net intersecting zone *might* get knocked
1775 // out along the border by a higher-priority, different-net zone. #12797
1776 for( ZONE* diffNetZone : diffNetIntersectingZones )
1777 {
1778 if( diffNetZone->HigherPriority( sameNetZone )
1779 && diffNetZone->GetBoundingBox().Intersects( sameNetBoundingBox ) )
1780 {
1781 SHAPE_POLY_SET diffNetOutline = diffNetZone->GetBoardOutline();
1782 diffNetOutline.ClearArcs();
1783
1784 diffNetPoly.BooleanAdd( diffNetOutline );
1785 }
1786 }
1787
1788 // Second wrinkle. After unioning the higher priority, different net zones together, we
1789 // need to check to see if they completely enclose our zone. If they do, then we need to
1790 // treat the enclosed zone as isolated, not connected to the outer zone. #13915
1791 bool isolated = false;
1792
1793 if( diffNetPoly.OutlineCount() )
1794 {
1796 thisPoly.ClearArcs();
1797
1798 thisPoly.BooleanSubtract( diffNetPoly );
1799 isolated = thisPoly.OutlineCount() == 0;
1800 }
1801
1802 if( !isolated )
1803 aSmoothedPoly.BooleanAdd( sameNetPoly );
1804 }
1805
1806 if( aBoardOutline )
1807 {
1808 SHAPE_POLY_SET boardOutline = aBoardOutline->CloneDropTriangulation();
1809 boardOutline.ClearArcs();
1810
1811 aSmoothedPoly.BooleanIntersection( boardOutline );
1812 }
1813
1814 SHAPE_POLY_SET withSameNetIntersectingZones = aSmoothedPoly.CloneDropTriangulation();
1815
1816 smooth( aSmoothedPoly );
1817
1818 if( aSmoothedPolyWithApron )
1819 {
1820 // The same-net intersecting-zone code above makes sure the corner-smoothing algorithm
1821 // doesn't produce divots. But the min-thickness algorithm applied in fillCopperZone()
1822 // is *also* going to perform a deflate/inflate cycle, again leading to divots. So we
1823 // pre-inflate the contour by the min-thickness within the same-net-intersecting-zones
1824 // envelope.
1825 SHAPE_POLY_SET poly = maxExtents->CloneDropTriangulation();
1827
1828 if( !keepExternalFillets )
1829 poly.BooleanIntersection( withSameNetIntersectingZones );
1830
1831 *aSmoothedPolyWithApron = aSmoothedPoly;
1832 aSmoothedPolyWithApron->BooleanIntersection( poly );
1833 }
1834
1835 aSmoothedPoly.BooleanIntersection( *maxExtents );
1836
1837 return true;
1838}
1839
1840
1842{
1843 m_area = 0.0;
1844
1845 for( const auto& [layer, poly] : m_FilledPolysList )
1846 m_area += poly->Area();
1847
1848 return m_area;
1849}
1850
1851
1853{
1854 m_outlinearea = std::abs( m_Poly->Area() );
1855 return m_outlinearea;
1856}
1857
1858
1860 int aMaxError, ERROR_LOC aErrorLoc,
1861 SHAPE_POLY_SET* aBoardOutline ) const
1862{
1863 // Creates the zone outline polygon (with holes if any)
1864 SHAPE_POLY_SET polybuffer;
1865
1866 // TODO: using GetFirstLayer() means it only works for single-layer zones....
1867 BuildSmoothedPoly( polybuffer, GetFirstLayer(), aBoardOutline );
1868
1869 // Calculate the polygon with clearance
1870 // holes are linked to the main outline, so only one polygon is created.
1871 if( aClearance )
1872 {
1873 if( aErrorLoc == ERROR_OUTSIDE )
1874 aClearance += GetMaxError();
1875
1876 polybuffer.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, GetMaxError() );
1877 }
1878
1879 polybuffer.Fracture();
1880 aBuffer.Append( polybuffer );
1881}
1882
1883
1884std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
1885{
1886 // Rule areas are never filled, so fall back to the outline. DRC relies on this
1887 // to collide tracks, vias and pads against keepout areas.
1888 if( GetIsRuleArea() )
1889 return std::make_shared<SHAPE_POLY_SET>( GetBoardOutline() );
1890
1891 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1892
1893 if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
1894 return std::make_shared<SHAPE_NULL>();
1895 else
1896 return m_FilledPolysList.at( aLayer );
1897}
1898
1899
1900void ZONE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
1901 int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth ) const
1902{
1903 wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for zones." ) );
1904
1905 std::shared_ptr<SHAPE_POLY_SET> fillPolys;
1906
1907 {
1908 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1909
1910 if( !m_FilledPolysList.count( aLayer ) )
1911 return;
1912
1913 fillPolys = m_FilledPolysList.at( aLayer );
1914 }
1915
1916 if( !aClearance )
1917 {
1918 aBuffer.Append( *fillPolys );
1919 return;
1920 }
1921
1922 SHAPE_POLY_SET temp_buf = fillPolys->CloneDropTriangulation();
1923
1924 // Rebuild filled areas only if clearance is not 0
1925 if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
1926 {
1927 if( aErrorLoc == ERROR_OUTSIDE )
1928 aClearance += aError;
1929
1930 temp_buf.InflateWithLinkedHoles( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1931 }
1932
1933 aBuffer.Append( temp_buf );
1934}
1935
1936
1938{
1939 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1940
1941 if( m_FilledPolysList.count( aLayer ) && !m_FilledPolysList.at( aLayer )->IsEmpty() )
1942 aBuffer.Append( *m_FilledPolysList.at( aLayer ) );
1943}
1944
1945
1947{
1948 if( aLayerSet.count() == 0 )
1949 return;
1950
1951 std::scoped_lock lock( m_layerSetMutex, m_filledPolysListMutex );
1952
1953 if( m_layerSet != aLayerSet )
1954 {
1955 aLayerSet.RunOnLayers(
1956 [&]( PCB_LAYER_ID layer )
1957 {
1958 // Only keep layers that are present in the new set
1959 if( !aLayerSet.Contains( layer ) )
1960 {
1961 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>();
1962 m_filledPolysHash[layer] = {};
1963 m_insulatedIslands[layer] = {};
1964 }
1965 } );
1966 }
1967
1968 m_layerSet = aLayerSet;
1969}
1970
1971
1972bool ZONE::operator==( const BOARD_ITEM& aOther ) const
1973{
1974 if( aOther.Type() != Type() )
1975 return false;
1976
1977 const ZONE& other = static_cast<const ZONE&>( aOther );
1978 return *this == other;
1979}
1980
1981
1982bool ZONE::operator==( const ZONE& aOther ) const
1983
1984{
1985 if( aOther.Type() != Type() )
1986 return false;
1987
1988 const ZONE& other = static_cast<const ZONE&>( aOther );
1989
1990 if( GetIsRuleArea() != other.GetIsRuleArea() )
1991 return false;
1992
1993 if( GetIsRuleArea() )
1994 {
1996 return false;
1997
1998 if( GetDoNotAllowTracks() != other.GetDoNotAllowTracks() )
1999 return false;
2000
2001 if( GetDoNotAllowVias() != other.GetDoNotAllowVias() )
2002 return false;
2003
2005 return false;
2006
2007 if( GetDoNotAllowPads() != other.GetDoNotAllowPads() )
2008 return false;
2009
2011 return false;
2012
2014 return false;
2015
2017 return false;
2018 }
2019 else
2020 {
2021 if( GetAssignedPriority() != other.GetAssignedPriority() )
2022 return false;
2023
2024 if( GetMinThickness() != other.GetMinThickness() )
2025 return false;
2026
2028 return false;
2029
2030 if( GetCornerRadius() != other.GetCornerRadius() )
2031 return false;
2032
2033 if( GetTeardropParams() != other.GetTeardropParams() )
2034 return false;
2035 }
2036
2037 if( GetNumCorners() != other.GetNumCorners() )
2038 return false;
2039
2040 for( int ii = 0; ii < GetNumCorners(); ii++ )
2041 {
2042 if( GetCornerPosition( ii ) != other.GetCornerPosition( ii ) )
2043 return false;
2044 }
2045
2046 return true;
2047}
2048
2049
2050double ZONE::Similarity( const BOARD_ITEM& aOther ) const
2051{
2052 if( aOther.Type() != Type() )
2053 return 0.0;
2054
2055 const ZONE& other = static_cast<const ZONE&>( aOther );
2056
2057 if( GetIsRuleArea() != other.GetIsRuleArea() )
2058 return 0.0;
2059
2060 double similarity = 1.0;
2061
2062 if( GetLayerSet() != other.GetLayerSet() )
2063 similarity *= 0.9;
2064
2065 if( GetNetCode() != other.GetNetCode() )
2066 similarity *= 0.9;
2067
2068 if( !GetIsRuleArea() )
2069 {
2070 if( GetAssignedPriority() != other.GetAssignedPriority() )
2071 similarity *= 0.9;
2072
2073 if( GetMinThickness() != other.GetMinThickness() )
2074 similarity *= 0.9;
2075
2077 similarity *= 0.9;
2078
2079 if( GetCornerRadius() != other.GetCornerRadius() )
2080 similarity *= 0.9;
2081
2082 if( GetTeardropParams() != other.GetTeardropParams() )
2083 similarity *= 0.9;
2084 }
2085 else
2086 {
2088 similarity *= 0.9;
2089 if( GetDoNotAllowTracks() != other.GetDoNotAllowTracks() )
2090 similarity *= 0.9;
2091 if( GetDoNotAllowVias() != other.GetDoNotAllowVias() )
2092 similarity *= 0.9;
2094 similarity *= 0.9;
2095 if( GetDoNotAllowPads() != other.GetDoNotAllowPads() )
2096 similarity *= 0.9;
2097 }
2098
2099 std::vector<VECTOR2I> corners;
2100 std::vector<VECTOR2I> otherCorners;
2101 VECTOR2I lastCorner( 0, 0 );
2102
2103 for( int ii = 0; ii < GetNumCorners(); ii++ )
2104 {
2105 corners.push_back( lastCorner - GetCornerPosition( ii ) );
2106 lastCorner = GetCornerPosition( ii );
2107 }
2108
2109 lastCorner = VECTOR2I( 0, 0 );
2110 for( int ii = 0; ii < other.GetNumCorners(); ii++ )
2111 {
2112 otherCorners.push_back( lastCorner - other.GetCornerPosition( ii ) );
2113 lastCorner = other.GetCornerPosition( ii );
2114 }
2115
2116 size_t longest = alg::longest_common_subset( corners, otherCorners );
2117
2118 similarity *= std::pow( 0.9, GetNumCorners() + other.GetNumCorners() - 2 * longest );
2119
2120 return similarity;
2121}
2122
2123
2124static struct ZONE_DESC
2125{
2127 {
2129
2130 if( layerEnum.Choices().GetCount() == 0 )
2131 {
2132 layerEnum.Undefined( UNDEFINED_LAYER );
2133
2134 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
2135 layerEnum.Map( layer, LSET::Name( layer ) );
2136 }
2137
2139
2140 if( zcMap.Choices().GetCount() == 0 )
2141 {
2143 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
2144 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
2145 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
2146 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
2147 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
2148 }
2149
2151
2152 if( zfmMap.Choices().GetCount() == 0 )
2153 {
2155 zfmMap.Map( ZONE_FILL_MODE::POLYGONS, _HKI( "Solid fill" ) )
2156 .Map( ZONE_FILL_MODE::HATCH_PATTERN, _HKI( "Hatch pattern" ) )
2157 .Map( ZONE_FILL_MODE::COPPER_THIEVING, _HKI( "Copper thieving" ) );
2158 }
2159
2161
2162 if( tpMap.Choices().GetCount() == 0 )
2163 {
2165 tpMap.Map( THIEVING_PATTERN::DOTS, _HKI( "Dots" ) )
2166 .Map( THIEVING_PATTERN::SQUARES, _HKI( "Squares" ) )
2167 .Map( THIEVING_PATTERN::HATCH, _HKI( "Hatch" ) );
2168 }
2169
2171
2172 if( irmMap.Choices().GetCount() == 0 )
2173 {
2175 irmMap.Map( ISLAND_REMOVAL_MODE::ALWAYS, _HKI( "Always" ) )
2176 .Map( ISLAND_REMOVAL_MODE::NEVER, _HKI( "Never" ) )
2177 .Map( ISLAND_REMOVAL_MODE::AREA, _HKI( "Below area limit" ) );
2178 }
2179
2181
2182 if( rapstMap.Choices().GetCount() == 0 )
2183 {
2185 rapstMap.Map( PLACEMENT_SOURCE_T::SHEETNAME, _HKI( "Sheet Name" ) )
2186 .Map( PLACEMENT_SOURCE_T::COMPONENT_CLASS, _HKI( "Component Class" ) )
2188 }
2189
2193
2194 // Mask layer and position properties; they aren't useful in current form
2195 auto posX = new PROPERTY<ZONE, int>( _HKI( "Position X" ), NO_SETTER( ZONE, int ),
2196 static_cast<int ( ZONE::* )() const>( &ZONE::GetX ),
2199 posX->SetIsHiddenFromPropertiesManager();
2200
2201 auto posY = new PROPERTY<ZONE, int>( _HKI( "Position Y" ), NO_SETTER( ZONE, int ),
2202 static_cast<int ( ZONE::* )() const>( &ZONE::GetY ),
2205 posY->SetIsHiddenFromPropertiesManager();
2206
2207 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ), posX );
2208 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ), posY );
2209
2210 auto isCopperZone =
2211 []( INSPECTABLE* aItem ) -> bool
2212 {
2213 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2214 return !zone->GetIsRuleArea() && IsCopperLayer( zone->GetFirstLayer() );
2215
2216 return false;
2217 };
2218
2219 // Hide net-bearing and hatch/island properties for copper-thieving zones.
2220 // Same predicate gates both kinds of properties.
2221 auto isNonThievingCopperZone =
2222 []( INSPECTABLE* aItem ) -> bool
2223 {
2224 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2225 {
2226 return !zone->GetIsRuleArea()
2227 && IsCopperLayer( zone->GetFirstLayer() )
2228 && !zone->IsCopperThieving();
2229 }
2230
2231 return false;
2232 };
2233
2234 auto isRuleArea =
2235 []( INSPECTABLE* aItem ) -> bool
2236 {
2237 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2238 return zone->GetIsRuleArea();
2239
2240 return false;
2241 };
2242
2243 auto isHatchedFill =
2244 []( INSPECTABLE* aItem ) -> bool
2245 {
2246 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2247 return zone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN;
2248
2249 return false;
2250 };
2251
2252 auto isThievingFill =
2253 []( INSPECTABLE* aItem ) -> bool
2254 {
2255 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2256 return zone->IsCopperThieving();
2257
2258 return false;
2259 };
2260
2261 auto isThievingHatch =
2262 []( INSPECTABLE* aItem ) -> bool
2263 {
2264 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2265 {
2266 return zone->IsCopperThieving()
2267 && zone->GetThievingPattern() == THIEVING_PATTERN::HATCH;
2268 }
2269
2270 return false;
2271 };
2272
2273 auto isThievingNonHatch =
2274 []( INSPECTABLE* aItem ) -> bool
2275 {
2276 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2277 {
2278 return zone->IsCopperThieving()
2279 && zone->GetThievingPattern() != THIEVING_PATTERN::HATCH;
2280 }
2281
2282 return false;
2283 };
2284
2285 auto isAreaBasedIslandRemoval =
2286 []( INSPECTABLE* aItem ) -> bool
2287 {
2288 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2289 return zone->GetIslandRemovalMode() == ISLAND_REMOVAL_MODE::AREA;
2290
2291 return false;
2292 };
2293
2294 // Visible for thieving zones only; ordinary zones use a layer set, not a single layer.
2295 propMgr.ReplaceProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ),
2298 .SetAvailableFunc( isThievingFill );
2299
2301 isNonThievingCopperZone );
2302 propMgr.OverrideAvailability( TYPE_HASH( ZONE ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net Class" ),
2303 isNonThievingCopperZone );
2304
2307 .SetAvailableFunc( isCopperZone );
2308
2309 propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Name" ),
2311
2312 const wxString groupKeepout = _HKI( "Keepout" );
2313
2314 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Tracks" ),
2316 groupKeepout )
2317 .SetAvailableFunc( isRuleArea );
2318
2319 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Vias" ),
2321 groupKeepout )
2322 .SetAvailableFunc( isRuleArea );
2323
2324 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Pads" ),
2326 groupKeepout )
2327 .SetAvailableFunc( isRuleArea );
2328
2329 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Zone Fills" ),
2331 groupKeepout )
2332 .SetAvailableFunc( isRuleArea );
2333
2334 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Footprints" ),
2336 groupKeepout )
2337 .SetAvailableFunc( isRuleArea );
2338
2339
2340 const wxString groupPlacement = _HKI( "Placement" );
2341
2342 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Enable" ),
2344 groupPlacement )
2345 .SetAvailableFunc( isRuleArea );
2346
2347 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, PLACEMENT_SOURCE_T>( _HKI( "Source Type" ),
2349 groupPlacement )
2350 .SetAvailableFunc( isRuleArea );
2351
2352 propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Source Name" ),
2354 groupPlacement )
2355 .SetAvailableFunc( isRuleArea );
2356
2357
2358 const wxString groupFill = _HKI( "Fill Style" );
2359
2360 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ZONE_FILL_MODE>( _HKI( "Fill Mode" ),
2362 groupFill )
2363 .SetAvailableFunc( isCopperZone );
2364
2365 propMgr.AddProperty( new PROPERTY<ZONE, EDA_ANGLE>( _HKI( "Hatch Orientation" ),
2368 groupFill )
2369 .SetAvailableFunc( isNonThievingCopperZone )
2370 .SetWriteableFunc( isHatchedFill );
2371
2372 auto atLeastMinWidthValidator =
2373 []( const wxAny&& aValue, EDA_ITEM* aZone ) -> VALIDATOR_RESULT
2374 {
2375 int val = aValue.As<int>();
2376 ZONE* zone = dynamic_cast<ZONE*>( aZone );
2377 wxCHECK( zone, std::nullopt );
2378
2379 if( val < zone->GetMinThickness() )
2380 return std::make_unique<VALIDATION_ERROR_MSG>( _( "Cannot be less than zone minimum width" ) );
2381
2382 return std::nullopt;
2383 };
2384
2385 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Hatch Width" ),
2387 groupFill )
2388 .SetAvailableFunc( isNonThievingCopperZone )
2389 .SetWriteableFunc( isHatchedFill )
2390 .SetValidator( atLeastMinWidthValidator );
2391
2392 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Hatch Gap" ),
2394 groupFill )
2395 .SetAvailableFunc( isNonThievingCopperZone )
2396 .SetWriteableFunc( isHatchedFill )
2397 .SetValidator( atLeastMinWidthValidator );
2398
2399 propMgr.AddProperty( new PROPERTY<ZONE, double>( _HKI( "Hatch Minimum Hole Ratio" ),
2401 groupFill )
2402 .SetAvailableFunc( isNonThievingCopperZone )
2403 .SetWriteableFunc( isHatchedFill )
2405
2406 // TODO: Smoothing effort needs to change to enum (in dialog too)
2407 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Smoothing Effort" ),
2409 groupFill )
2410 .SetAvailableFunc( isNonThievingCopperZone )
2411 .SetWriteableFunc( isHatchedFill );
2412
2413 propMgr.AddProperty( new PROPERTY<ZONE, double>( _HKI( "Smoothing Amount" ),
2415 groupFill )
2416 .SetAvailableFunc( isNonThievingCopperZone )
2417 .SetWriteableFunc( isHatchedFill );
2418
2419 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, THIEVING_PATTERN>( _HKI( "Thieving Pattern" ),
2421 groupFill )
2422 .SetAvailableFunc( isThievingFill );
2423
2424 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Element Size" ),
2427 groupFill )
2428 .SetAvailableFunc( isThievingFill )
2429 .SetWriteableFunc( isThievingNonHatch )
2431
2432 // Gap is meaningful for all three patterns: edge-to-edge spacing between
2433 // adjacent stamps for dots/squares, line-to-line edge spacing for hatch.
2434 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Gap" ),
2436 groupFill )
2437 .SetAvailableFunc( isThievingFill )
2439
2440 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Line Width" ),
2443 groupFill )
2444 .SetAvailableFunc( isThievingFill )
2445 .SetWriteableFunc( isThievingHatch )
2447
2448 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Thieving Stagger" ),
2450 groupFill )
2451 .SetAvailableFunc( isThievingFill );
2452
2453 propMgr.AddProperty( new PROPERTY<ZONE, EDA_ANGLE>( _HKI( "Thieving Orientation" ),
2456 groupFill )
2457 .SetAvailableFunc( isThievingFill );
2458
2459 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ISLAND_REMOVAL_MODE>( _HKI( "Remove Islands" ),
2461 groupFill )
2462 .SetAvailableFunc( isNonThievingCopperZone );
2463
2464 propMgr.AddProperty( new PROPERTY<ZONE, long long int>( _HKI( "Minimum Island Area" ),
2466 groupFill )
2467 .SetAvailableFunc( isNonThievingCopperZone )
2468 .SetWriteableFunc( isAreaBasedIslandRemoval );
2469
2470 const wxString groupElectrical = _HKI( "Electrical" );
2471
2472 auto clearance = new PROPERTY<ZONE, std::optional<int>>( _HKI( "Clearance" ),
2474 clearance->SetAvailableFunc( isCopperZone );
2475 constexpr int maxClearance = pcbIUScale.mmToIU( ZONE_CLEARANCE_MAX_VALUE_MM );
2477
2478 auto minWidth = new PROPERTY<ZONE, int>( _HKI( "Minimum Width" ),
2480 minWidth->SetAvailableFunc( isCopperZone );
2481 constexpr int minMinWidth = pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM );
2483
2484 // Pad connections and thermal-relief controls require a net to act on.
2485 // Thieving zones are netless and explicitly use ZONE_CONNECTION::NONE,
2486 // so hide these for them while keeping them visible for solid + hatched zones.
2487 auto padConnections = new PROPERTY_ENUM<ZONE, ZONE_CONNECTION>( _HKI( "Pad Connections" ),
2489 padConnections->SetAvailableFunc( isNonThievingCopperZone );
2490
2491 auto thermalGap = new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Gap" ),
2493 thermalGap->SetAvailableFunc( isNonThievingCopperZone );
2494 thermalGap->SetValidator( PROPERTY_VALIDATORS::PositiveIntValidator );
2495
2496 auto thermalSpokeWidth = new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Spoke Width" ),
2498 thermalSpokeWidth->SetAvailableFunc( isNonThievingCopperZone );
2499 thermalSpokeWidth->SetValidator( atLeastMinWidthValidator );
2500
2501 propMgr.AddProperty( clearance, groupElectrical );
2502 propMgr.AddProperty( minWidth, groupElectrical );
2503 propMgr.AddProperty( padConnections, groupElectrical );
2504 propMgr.AddProperty( thermalGap, groupElectrical );
2505 propMgr.AddProperty( thermalSpokeWidth, groupElectrical );
2506 }
2508
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:47
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BITMAPS
A list of all bitmap identifiers.
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
BOARD_CONNECTED_ITEM(BOARD_ITEM *aParent, KICAD_T idtype)
void PackNet(kiapi::board::types::Net *aProto) const
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
NETINFO_ITEM * m_netinfo
Store all information about the net that item belongs to.
virtual int GetOwnClearance(PCB_LAYER_ID aLayer, wxString *aSource=nullptr) const
Return an item's "own" clearance in internal units.
void UnpackNet(const kiapi::board::types::Net &aProto)
Assigns a net to this item from an API message.
TEARDROP_PARAMETERS & GetTeardropParams()
ZONE_SETTINGS & GetDefaultZoneSettings()
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:83
friend class BOARD
Definition board_item.h:512
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
int GetY() const
Definition board_item.h:122
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
int GetX() const
Definition board_item.h:116
bool IsLocked() const override
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
BOARD_ITEM & operator=(const BOARD_ITEM &aOther)
Definition board_item.h:100
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:231
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
int GetMaxError() const
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayer) const
Definition board.cpp:978
std::unordered_map< const ZONE *, BOX2I > m_ZoneBBoxCache
Definition board.h:1685
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:307
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
const KIID m_Uuid
Definition eda_item.h:531
bool m_forceVisible
Definition eda_item.h:548
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ITEM_FLAGS m_flags
Definition eda_item.h:542
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:156
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:37
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:727
static ENUM_MAP< T > & Instance()
Definition property.h:721
ENUM_MAP & Undefined(T aValue)
Definition property.h:734
wxPGChoices & Choices()
Definition property.h:772
const TRANSFORM_TRS & GetTransform() const
Definition footprint.h:419
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition view_item.h:176
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:181
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
bool IsLayerVisibleCached(int aLayer) const
Definition view.h:437
Definition kiid.h:44
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition lset.cpp:718
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:739
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:725
void RunOnLayers(const std::function< void(PCB_LAYER_ID)> &aFunction) const
Execute a function on each layer of the LSET.
Definition lset.h:263
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Handle the data for a net.
Definition netinfo.h:46
Definition pad.h:61
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:2149
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:262
PROPERTY_BASE & SetWriteableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Definition property.h:287
PROPERTY_BASE & SetValidator(PROPERTY_VALIDATOR_FN &&aValidator)
Definition property.h:349
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void OverrideAvailability(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName, std::function< bool(INSPECTABLE *)> aFunc)
Sets an override availability functor for a base class property of a given derived class.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
static VALIDATOR_RESULT PositiveRatioValidator(const wxAny &&aValue, EDA_ITEM *aItem)
static VALIDATOR_RESULT PositiveIntValidator(const wxAny &&aValue, EDA_ITEM *aItem)
static VALIDATOR_RESULT RangeIntValidator(const wxAny &&aValue, EDA_ITEM *aItem)
Definition seg.h:38
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
bool Intersects(const SEG &aSeg) const
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
ITERATOR IterateWithHoles(int aOutline)
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
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,...
int TotalVertices() const
Return total number of vertices stored in the set.
int FullPointCount() const
Return the number of points in the shape poly set.
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 BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
SEGMENT_ITERATOR IterateSegments(int aFirst, int aLast, bool aIterateHoles=false)
Return an iterator object, for iterating between aFirst and aLast outline, with or without holes (def...
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
SHAPE_POLY_SET CloneDropTriangulation() const
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
std::function< void(std::function< void()>)> TASK_SUBMITTER
Callback that submits a unit of work for asynchronous execution.
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
VECTOR2I InverseApply(const VECTOR2I &aPoint) const
VECTOR2I Apply(const VECTOR2I &aPoint) const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
ZONE_SETTINGS handles zones parameters.
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void SetHatchThickness(int aThickness)
Definition zone.h:326
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, const SHAPE_POLY_SET::TASK_SUBMITTER &aSubmitter={})
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
Definition zone.cpp:1598
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:310
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if a point is near an outline edge or a corner of this zone.
Definition zone.cpp:890
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition zone.cpp:1430
int m_borderHatchPitch
Definition zone.h:1031
wxString m_placementAreaSource
Definition zone.h:952
bool m_isRuleArea
Definition zone.h:945
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:832
void SetLayerProperties(const std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > &aOther)
Definition zone.cpp:686
ZONE & operator=(const ZONE &aOther)
Definition zone.cpp:101
int m_cornerSmoothingType
Definition zone.h:925
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:813
PLACEMENT_SOURCE_T m_placementAreaSourceType
Definition zone.h:951
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1012
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
bool m_doNotAllowVias
Definition zone.h:964
bool UnFill()
Removes the zone filling.
Definition zone.cpp:507
bool GetDoNotAllowVias() const
Definition zone.h:824
void TransformSolidAreasShapesToPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aBuffer) const
Convert solid areas full shapes to polygon set (the full shape is the polygon area with a thick outli...
Definition zone.cpp:1937
void SetCornerRadius(unsigned int aRadius)
Definition zone.cpp:836
ZONE_FILL_MODE m_fillMode
Definition zone.h:995
THIEVING_SETTINGS m_thievingSettings
Definition zone.h:1008
bool m_doNotAllowFootprints
Definition zone.h:967
bool unFillLocked()
Internal implementation of UnFill() that assumes the caller already holds m_filledPolysListMutex.
Definition zone.cpp:515
int m_ZoneMinThickness
Definition zone.h:971
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
Definition zone.cpp:232
int GetThievingLineWidth() const
Definition zone.h:387
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1393
double m_hatchSmoothingValue
Definition zone.h:1003
void SetLocalFlags(int aFlags)
Definition zone.h:416
void TransformSmoothedOutlineToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, SHAPE_POLY_SET *aBoardOutline) const
Convert the outlines shape to a polygon with no holes inflated (optional) by max( aClearanceValue,...
Definition zone.cpp:1859
int m_thermalReliefSpokeWidth
Definition zone.h:993
wxString GetPlacementAreaSource() const
Definition zone.h:818
bool HitTestCutout(const VECTOR2I &aRefPos, int *aOutlineIdx=nullptr, int *aHoleIdx=nullptr) const
Test if the given point is contained within a cutout of the zone.
Definition zone.cpp:1047
EDA_ANGLE m_hatchOrientation
Definition zone.h:998
void Mirror(const VECTOR2I &aMirrorRef, FLIP_DIRECTION aFlipDirection) override
Mirror the outlines relative to a given horizontal axis the layer is not changed.
Definition zone.cpp:1348
std::map< PCB_LAYER_ID, std::set< int > > m_insulatedIslands
For each layer, a set of insulated islands that were not removed.
Definition zone.h:1035
wxString m_zoneName
An optional unique name for this zone, used for identifying it in DRC checking.
Definition zone.h:929
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition zone.cpp:1520
std::map< PCB_LAYER_ID, std::shared_ptr< SHAPE_POLY_SET > > m_FilledPolysList
Definition zone.h:1021
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition zone.cpp:1501
bool GetDoNotAllowPads() const
Definition zone.h:826
const BOX2I GetBoundingBox() const override
Definition zone.cpp:766
void SetMinThickness(int aMinThickness)
Definition zone.h:316
void SetPlacementAreaSource(const wxString &aSource)
Definition zone.h:819
BOX2I computeBoundingBox() const
Compute the bbox from scratch. Shared so the cached value can't diverge from the live one.
Definition zone.cpp:757
void SetThievingOrientation(const EDA_ANGLE &aOrientation)
Definition zone.h:406
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
Definition zone.cpp:712
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:820
double m_outlinearea
Definition zone.h:1038
std::mutex m_filledPolysListMutex
Definition zone.h:1020
void SetThievingPattern(THIEVING_PATTERN aPattern)
Definition zone.h:361
wxString GetFriendlyName() const override
Definition zone.cpp:1251
bool GetDoNotAllowTracks() const
Definition zone.h:825
int GetLocalFlags() const
Definition zone.h:415
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition zone.cpp:218
void SetHatchOrientation(const EDA_ANGLE &aStep)
Definition zone.h:332
void SetThievingElementSize(int aSize)
Definition zone.h:370
void SetHatchSmoothingValue(double aValue)
Definition zone.h:338
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_layerProperties
Definition zone.h:934
bool HitTestForCorner(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a corner.
Definition zone.cpp:900
void SetHatchSmoothingLevel(int aLevel)
Definition zone.h:335
bool m_doNotAllowTracks
Definition zone.h:965
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition zone.h:251
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:552
void SetPlacementAreaSourceType(PLACEMENT_SOURCE_T aType)
Definition zone.h:821
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:619
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:835
LSET m_fillFlags
Definition zone.h:1025
SHAPE_POLY_SET * Outline()
Definition zone.h:418
bool m_doNotAllowPads
Definition zone.h:966
ZONE(BOARD_ITEM_CONTAINER *parent)
Definition zone.cpp:52
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition zone.cpp:249
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
Definition zone.cpp:601
void Move(const VECTOR2I &offset) override
Move the outlines.
Definition zone.cpp:1206
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1631
std::atomic< int > m_bboxCacheTimeStamp
Definition zone.h:1045
bool IsCopperThieving() const
Definition zone.h:349
BOX2I m_bboxCache
Lock-free bbox cache, valid while m_bboxCacheTimeStamp matches the board timestamp.
Definition zone.h:1044
SHAPE_POLY_SET * m_Poly
Outline of the zone.
Definition zone.h:924
TEARDROP_TYPE m_teardropType
Definition zone.h:958
std::map< PCB_LAYER_ID, HASH_128 > m_filledPolysHash
A hash value used in zone filling calculations to see if the filled areas are up to date.
Definition zone.h:1028
~ZONE()
Definition zone.cpp:118
long long int GetMinIslandArea() const
Definition zone.h:838
int m_hatchSmoothingLevel
Definition zone.h:999
void SetThievingStagger(bool aStagger)
Definition zone.h:397
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
Definition zone.cpp:2050
LSET m_layerSet
Definition zone.h:932
void SetIsRuleArea(bool aEnable)
Definition zone.h:814
int m_ZoneClearance
Definition zone.h:970
void CopyFrom(const BOARD_ITEM *aOther) override
Definition zone.cpp:111
void SetThievingGap(int aGap)
Definition zone.h:379
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:831
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:726
bool m_placementAreaEnabled
Placement rule area data.
Definition zone.h:950
const wxString & GetZoneName() const
Definition zone.h:160
void CacheBoundingBox()
Used to preload the zone bounding box cache so we don't have to worry about mutex-locking it each tim...
Definition zone.cpp:800
int GetMinThickness() const
Definition zone.h:315
virtual void swapData(BOARD_ITEM *aImage) override
Definition zone.cpp:1590
bool HitTestForEdge(const VECTOR2I &refPos, int aAccuracy, SHAPE_POLY_SET::VERTEX_INDEX *aCornerHit=nullptr) const
Test if the given VECTOR2I is near a segment defined by 2 corners.
Definition zone.cpp:912
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:874
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition zone.cpp:1364
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition zone.cpp:1287
bool HigherPriority(const ZONE *aOther) const
Definition zone.cpp:487
bool HitTestFilledArea(PCB_LAYER_ID aLayer, const VECTOR2I &aRefPos, int aAccuracy=0) const
Test if the given VECTOR2I is within the bounds of a filled area of this zone.
Definition zone.cpp:1018
void SetIsFilled(bool isFilled)
Definition zone.h:307
std::mutex m_layerSetMutex
Definition zone.h:931
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition zone.cpp:625
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition zone.cpp:1584
int m_hatchGap
Definition zone.h:997
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:312
int GetHatchThickness() const
Definition zone.h:325
double GetHatchHoleMinArea() const
Definition zone.h:340
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:644
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
Definition zone.cpp:1077
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:750
int m_hatchBorderAlgorithm
Definition zone.h:1005
bool GetPlacementAreaEnabled() const
Definition zone.h:815
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:830
bool IsTeardropArea() const
Definition zone.h:788
std::vector< SEG > m_borderHatchLines
Definition zone.h:1032
VECTOR2I GetPosition() const override
Definition zone.cpp:543
int GetThermalReliefSpokeWidth() const
Definition zone.h:259
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
Definition zone.cpp:610
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition zone.cpp:1316
void BuildHashValue(PCB_LAYER_ID aLayer)
Build the hash value of m_FilledPolysList, and store it internally in m_filledPolysHash.
Definition zone.cpp:857
void SetThermalReliefGap(int aThermalReliefGap)
Definition zone.h:240
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:331
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aBoardOutline, SHAPE_POLY_SET *aSmoothedPolyWithApron=nullptr) const
Definition zone.cpp:1681
int m_fillVersion
Definition zone.h:972
const VECTOR2I & GetCornerPosition(int aCornerIndex) const
Definition zone.h:655
bool GetDoNotAllowFootprints() const
Definition zone.h:827
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
double m_hatchHoleMinArea
Definition zone.h:1004
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
bool GetThievingStagger() const
Definition zone.h:396
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:833
void SetBorderHatchPitch(int aPitch)
Definition zone.h:849
void SetThievingLineWidth(int aWidth)
Definition zone.h:388
void GetInteractingZones(PCB_LAYER_ID aLayer, std::vector< ZONE * > *aSameNetCollidingZones, std::vector< ZONE * > *aOtherNetIntersectingZones) const
Some intersecting zones, despite being on the same layer with the same net, cannot be merged due to o...
Definition zone.cpp:1643
void SetLayerSetAndRemoveUnusedFills(const LSET &aLayerSet)
Set the zone to be on the aLayerSet layers and only remove the fill polygons from the unused layers,...
Definition zone.cpp:1946
int GetHatchGap() const
Definition zone.h:328
double CalculateOutlineArea()
Compute the area of the zone outline (not the filled area).
Definition zone.cpp:1852
int GetThievingGap() const
Definition zone.h:378
void SetHatchHoleMinArea(double aPct)
Definition zone.h:341
unsigned m_priority
Definition zone.h:940
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition zone.cpp:361
bool IsConflicting() const
For rule areas which exclude footprints (and therefore participate in courtyard conflicts during move...
Definition zone.cpp:537
bool m_doNotAllowZoneFills
Definition zone.h:963
ISLAND_REMOVAL_MODE m_islandRemovalMode
Definition zone.h:974
std::vector< SEG > GetHatchLines() const
Definition zone.cpp:1560
bool m_isFilled
True when a zone was filled, false after deleting the filled areas.
Definition zone.h:983
double GetHatchSmoothingValue() const
Definition zone.h:337
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition zone.cpp:1410
std::atomic< bool > m_needRefill
False when a zone was refilled, true after changes in zone params.
Definition zone.h:990
THIEVING_PATTERN GetThievingPattern() const
Definition zone.h:360
bool GetDoNotAllowZoneFills() const
Definition zone.h:823
void MoveEdge(const VECTOR2I &offset, int aEdge)
Move the outline Edge.
Definition zone.cpp:1264
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:756
int GetCornerSmoothingType() const
Definition zone.h:752
int m_thermalReliefGap
Definition zone.h:992
int GetThievingElementSize() const
Definition zone.h:369
SHAPE_POLY_SET GetLibraryOutline() const
Definition zone.cpp:868
bool IsOnCopperLayer() const override
Definition zone.cpp:594
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
Definition zone.cpp:1841
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:829
int m_hatchThickness
Definition zone.h:996
void OnFootprintRescaled(double aRatioX, double aRatioY, double aLinearFactor, const VECTOR2I &aAnchor, const EDA_ANGLE &aParentRotate) override
Apply a parent footprint scale to this item.
Definition zone.cpp:1303
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the zone shape to a closed polygon Used in filling zones calculations Circles and arcs are ap...
Definition zone.cpp:1900
void SetAssignedPriority(unsigned aPriority)
Definition zone.h:117
double m_area
Definition zone.h:1037
unsigned int m_cornerRadius
Definition zone.h:926
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition zone.h:313
void SetZoneName(const wxString &aName)
Definition zone.h:161
bool operator==(const ZONE &aOther) const
Definition zone.cpp:1982
void UnHatchBorder()
Clear the zone's hatch.
Definition zone.cpp:1514
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition zone.h:836
EDA_ANGLE GetThievingOrientation() const
Definition zone.h:405
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition zone.h:421
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:574
void SetMinIslandArea(long long int aArea)
Definition zone.h:839
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition zone.cpp:692
HASH_128 GetHashValue(PCB_LAYER_ID aLayer)
Definition zone.cpp:848
ZONE_CONNECTION m_PadConnection
Definition zone.h:969
void InitDataFromSrcInCopyCtor(const ZONE &aZone, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Copy aZone data to me.
Definition zone.cpp:127
int GetThermalReliefGap() const
Definition zone.h:248
void SetHatchGap(int aStep)
Definition zone.h:329
static int GetDefaultHatchPitch()
Definition zone.cpp:1578
void SetPlacementAreaEnabled(bool aEnabled)
Definition zone.h:816
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:615
bool SameNet(const ZONE *aOther) const
Definition zone.cpp:501
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition zone.cpp:1884
ZONE_BORDER_DISPLAY_STYLE m_borderStyle
Definition zone.h:1030
long long int m_minIslandArea
When island removal mode is set to AREA, islands below this area will be removed.
Definition zone.h:980
A type-safe container of any type.
Definition ki_any.h:92
@ ROUND_ALL_CORNERS
All angles are rounded.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ DEGREES_T
Definition eda_angle.h:31
#define PCB_EDIT_FRAME_NAME
#define COURTYARD_CONFLICT
temporary set when moving footprints having courtyard overlapping
@ NONE
Definition eda_shape.h:72
a few functions useful in geometry calculations.
Some functions to handle hotkeys in KiCad.
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:173
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition layer_ids.h:180
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:675
@ LAYER_CONFLICTS_SHADOW
Shadow layer for items flagged conflicting.
Definition layer_ids.h:306
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:291
@ LAYER_ZONE_START
Virtual layers for stacking zones and tracks on a given copper layer.
Definition layer_ids.h:331
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
FLIP_DIRECTION
Definition mirror.h:23
size_t longest_common_subset(const _Container &__c1, const _Container &__c2)
Returns the length of the longest common subset of values between two containers.
Definition kicad_algo.h:182
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
KICOMMON_API void PackPolySet(types::PolySet &aOutput, const SHAPE_POLY_SET &aInput, const EDA_IU_SCALE &aScale)
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 SHAPE_POLY_SET UnpackPolySet(const types::PolySet &aInput, const EDA_IU_SCALE &aScale)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#define _HKI(x)
Definition page_info.cpp:40
#define TYPE_HASH(x)
Definition property.h:74
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:826
#define NO_SETTER(owner, type)
Definition property.h:833
@ PT_DEGREE
Angle expressed in degrees.
Definition property.h:66
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition property.h:65
@ PT_AREA
Area expressed in distance units-squared (mm/inch)
Definition property.h:64
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
#define REGISTER_TYPE(x)
std::optional< std::unique_ptr< VALIDATION_ERROR > > VALIDATOR_RESULT
Null optional means validation succeeded.
const double epsilon
wxString UnescapeString(const wxString &aSource)
void AccumulateDescription(wxString &aDesc, const wxString &aItem)
Utility to build comma separated lists in messages.
A storage class for 128-bit hash value.
Definition hash_128.h:32
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
ZONE_DESC()
Definition zone.cpp:2126
std::optional< VECTOR2I > hatching_offset
TEARDROP_TYPE
define the type of a teardrop: on a via or pad, or a track end
int clearance
const int accuracy
wxString result
Test unit parsing edge cases and error handling.
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
static SHAPE_POLY_SET g_nullPoly
Definition zone.cpp:845
static struct ZONE_DESC _ZONE_DESC
THIEVING_PATTERN
Shape stamped onto the grid for a copper-thieving fill.
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
ZONE_FILL_MODE
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
PLACEMENT_SOURCE_T
#define ZONE_CLEARANCE_MAX_VALUE_MM
Definition zones.h:33
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition zones.h:43
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:48
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47
#define ZONE_BORDER_HATCH_DIST_MM
Definition zones.h:34
#define ZONE_BORDER_HATCH_MINDIST_MM
Definition zones.h:35
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition zones.h:31
#define ZONE_BORDER_HATCH_MAXDIST_MM
Definition zones.h:36