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
214
216{
217 return new ZONE( *this );
218}
219
220
222{
223 ZONE* clone = new ZONE( BOARD_ITEM::GetParent() );
224 clone->InitDataFromSrcInCopyCtor( *this, aLayer );
225 return clone;
226}
227
228
229void ZONE::Serialize( google::protobuf::Any& aContainer ) const
230{
231 using namespace kiapi::board;
232 types::Zone zone;
234
235 zone.mutable_id()->set_value( m_Uuid.AsStdString() );
236 PackLayerSet( *zone.mutable_layers(), GetLayerSet() );
237
238 if( m_isRuleArea )
239 zone.set_type( types::ZT_RULE_AREA );
241 zone.set_type( types::ZT_TEARDROP );
242 else if( IsOnCopperLayer() )
243 zone.set_type( types::ZT_COPPER );
244 else
245 zone.set_type( types::ZT_GRAPHICAL );
246
247 kiapi::common::PackPolySet( *zone.mutable_outline(), *m_Poly );
248
249 zone.set_name( m_zoneName.ToUTF8() );
250 zone.set_priority( m_priority );
251 zone.set_filled( m_isFilled );
252
253 if( m_isRuleArea )
254 {
255 types::RuleAreaSettings* ra = zone.mutable_rule_area_settings();
256 ra->set_keepout_copper( m_doNotAllowZoneFills );
257 ra->set_keepout_footprints( m_doNotAllowFootprints );
258 ra->set_keepout_pads( m_doNotAllowPads );
259 ra->set_keepout_tracks( m_doNotAllowTracks );
260 ra->set_keepout_vias( m_doNotAllowVias );
261
262 ra->set_placement_enabled( m_placementAreaEnabled );
263 ra->set_placement_source( m_placementAreaSource.ToUTF8() );
264 ra->set_placement_source_type( ToProtoEnum<PLACEMENT_SOURCE_T,
265 types::PlacementRuleSourceType>( m_placementAreaSourceType ) );
266 }
267 else
268 {
269 types::CopperZoneSettings* cu = zone.mutable_copper_settings();
270 cu->mutable_connection()->set_zone_connection(
272
273 types::ThermalSpokeSettings* thermals = cu->mutable_connection()->mutable_thermal_spokes();
274 thermals->mutable_width()->set_value_nm( m_thermalReliefSpokeWidth );
275 thermals->mutable_gap()->set_value_nm( m_thermalReliefGap );
276 // n.b. zones don't currently have an overall thermal angle override
277
278 cu->mutable_clearance()->set_value_nm( m_ZoneClearance );
279 cu->mutable_min_thickness()->set_value_nm( m_ZoneMinThickness );
280 cu->set_island_mode(
282 cu->set_min_island_area( m_minIslandArea );
284
285 types::HatchFillSettings* hatch = cu->mutable_hatch_settings();
286 hatch->mutable_thickness()->set_value_nm( m_hatchThickness );
287 hatch->mutable_gap()->set_value_nm( m_hatchGap );
288 hatch->mutable_orientation()->set_value_degrees( m_hatchOrientation.AsDegrees() );
289 hatch->set_hatch_smoothing_ratio( m_hatchSmoothingValue );
290 hatch->set_hatch_hole_min_area_ratio( m_hatchHoleMinArea );
291
292 switch( m_hatchBorderAlgorithm )
293 {
294 default:
295 case 0: hatch->set_border_mode( types::ZHFBM_USE_MIN_ZONE_THICKNESS ); break;
296 case 1: hatch->set_border_mode( types::ZHFBM_USE_HATCH_THICKNESS ); break;
297 }
298
299 PackNet( cu->mutable_net() );
300 cu->mutable_teardrop()->set_type(
302
303 types::ThievingFillSettings* thieving = cu->mutable_thieving_settings();
304 thieving->set_pattern(
306 thieving->mutable_element_size()->set_value_nm( m_thievingSettings.element_size );
307 thieving->mutable_gap()->set_value_nm( m_thievingSettings.gap );
308 thieving->mutable_line_width()->set_value_nm( m_thievingSettings.line_width );
309 thieving->set_stagger( m_thievingSettings.stagger );
310 thieving->mutable_orientation()->set_value_degrees(
311 m_thievingSettings.orientation.AsDegrees() );
312 }
313
314 for( const auto& [layer, shape] : m_FilledPolysList )
315 {
316 types::ZoneFilledPolygons* filledLayer = zone.add_filled_polygons();
317 filledLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
318 kiapi::common::PackPolySet( *filledLayer->mutable_shapes(), *shape );
319 }
320
321 for( const auto& [layer, properties] : m_layerProperties )
322 {
323 types::ZoneLayerProperties* layerProperties = zone.add_layer_properties();
324 layerProperties->set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
325
326 if( properties.hatching_offset.has_value() )
327 {
328 PackVector2( *layerProperties->mutable_hatching_offset(),
329 properties.hatching_offset.value() );
330 }
331 }
332
333 zone.mutable_border()->set_style(
335 zone.mutable_border()->mutable_pitch()->set_value_nm( m_borderHatchPitch );
336
337 aContainer.PackFrom( zone );
338}
339
340
341bool ZONE::Deserialize( const google::protobuf::Any& aContainer )
342{
343 using namespace kiapi::board;
344 types::Zone zone;
346
347 if( !aContainer.UnpackTo( &zone ) )
348 return false;
349
350 SetUuidDirect( KIID( zone.id().value() ) );
351 SetLayerSet( UnpackLayerSet( zone.layers() ) );
352 SetAssignedPriority( zone.priority() );
353 SetZoneName( wxString::FromUTF8( zone.name() ) );
354
355 if( zone.type() == types::ZoneType::ZT_RULE_AREA )
356 m_isRuleArea = true;
357
358 if( !m_Poly )
360
361 *m_Poly = kiapi::common::UnpackPolySet( zone.outline() );
362
363 if( m_Poly->OutlineCount() == 0 )
364 return false;
365
366 if( m_isRuleArea )
367 {
368 const types::RuleAreaSettings& ra = zone.rule_area_settings();
369 m_doNotAllowZoneFills = ra.keepout_copper();
370 m_doNotAllowFootprints = ra.keepout_footprints();
371 m_doNotAllowPads = ra.keepout_pads();
372 m_doNotAllowTracks = ra.keepout_tracks();
373 m_doNotAllowVias = ra.keepout_vias();
374
375 m_placementAreaEnabled = ra.placement_enabled();
376 m_placementAreaSource = wxString::FromUTF8( ra.placement_source() );
377 m_placementAreaSourceType = FromProtoEnum<PLACEMENT_SOURCE_T>( ra.placement_source_type() );
378 }
379 else
380 {
381 const types::CopperZoneSettings& cu = zone.copper_settings();
382 m_PadConnection = FromProtoEnum<ZONE_CONNECTION>( cu.connection().zone_connection() );
383 m_thermalReliefSpokeWidth = cu.connection().thermal_spokes().width().value_nm();
384 m_thermalReliefGap = cu.connection().thermal_spokes().gap().value_nm();
385 m_ZoneClearance = cu.clearance().value_nm();
386 m_ZoneMinThickness = cu.min_thickness().value_nm();
388 m_minIslandArea = cu.min_island_area();
389 // Route through SetFillMode so the thieving single-layer / net-less invariants
390 // are enforced on protobuf imports — a direct m_fillMode assignment would leave
391 // the multi-layer set unpacked at line 355 in place for ZFM_COPPER_THIEVING.
392 SetFillMode( FromProtoEnum<ZONE_FILL_MODE>( cu.fill_mode() ) );
393
394 m_hatchThickness = cu.hatch_settings().thickness().value_nm();
395 m_hatchGap = cu.hatch_settings().gap().value_nm();
396 m_hatchOrientation = EDA_ANGLE( cu.hatch_settings().orientation().value_degrees(), DEGREES_T );
397 m_hatchSmoothingValue = cu.hatch_settings().hatch_smoothing_ratio();
398 m_hatchHoleMinArea = cu.hatch_settings().hatch_hole_min_area_ratio();
399
400 switch( cu.hatch_settings().border_mode() )
401 {
402 default:
403 case types::ZHFBM_USE_MIN_ZONE_THICKNESS: m_hatchBorderAlgorithm = 0; break;
404 case types::ZHFBM_USE_HATCH_THICKNESS: m_hatchBorderAlgorithm = 1; break;
405 }
406
407 UnpackNet( cu.net() );
408 m_teardropType = FromProtoEnum<TEARDROP_TYPE>( cu.teardrop().type() );
409
410 if( cu.has_thieving_settings() )
411 {
412 const types::ThievingFillSettings& thieving = cu.thieving_settings();
413 m_thievingSettings.pattern =
414 FromProtoEnum<THIEVING_PATTERN>( thieving.pattern() );
415
416 auto assignIfPositive = []( int aProtoValue, int& aTarget )
417 {
418 if( aProtoValue > 0 )
419 aTarget = aProtoValue;
420 };
421
422 assignIfPositive( thieving.element_size().value_nm(), m_thievingSettings.element_size );
423 assignIfPositive( thieving.gap().value_nm(), m_thievingSettings.gap );
424 assignIfPositive( thieving.line_width().value_nm(), m_thievingSettings.line_width );
425
426 m_thievingSettings.stagger = thieving.stagger();
427 m_thievingSettings.orientation =
428 EDA_ANGLE( thieving.orientation().value_degrees(), DEGREES_T );
429 }
430
431 for( const auto& properties : zone.layer_properties() )
432 {
433 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( properties.layer() );
434
435 ZONE_LAYER_PROPERTIES layerProperties;
436
437 if( properties.has_hatching_offset() )
438 layerProperties.hatching_offset = UnpackVector2( properties.hatching_offset() );
439
440 m_layerProperties[layer] = layerProperties;
441 }
442 }
443
445 m_borderHatchPitch = zone.border().pitch().value_nm();
446
447 if( zone.filled() )
448 {
449 // TODO(JE) check what else has to happen here
450 SetIsFilled( true );
451 SetNeedRefill( false );
452
453 for( const types::ZoneFilledPolygons& fillLayer : zone.filled_polygons() )
454 {
455 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID>( fillLayer.layer() );
456 SHAPE_POLY_SET shape = kiapi::common::UnpackPolySet( fillLayer.shapes() );
457 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>( shape );
458 }
459 }
460
461 HatchBorder();
462
463 return true;
464}
465
466
467bool ZONE::HigherPriority( const ZONE* aOther ) const
468{
469 // Teardrops are always higher priority than regular zones, so if one zone is a teardrop
470 // and the other is not, then return higher priority as the teardrop
472 return static_cast<int>( m_teardropType ) > static_cast<int>( aOther->m_teardropType );
473
474 if( m_priority != aOther->m_priority )
475 return m_priority > aOther->m_priority;
476
477 return m_Uuid > aOther->m_Uuid;
478}
479
480
481bool ZONE::SameNet( const ZONE* aOther ) const
482{
483 return GetNetCode() == aOther->GetNetCode();
484}
485
486
488{
489 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
490
491 return unFillLocked();
492}
493
494
496{
497 bool change = false;
498
499 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
500 {
501 change |= !pair.second->IsEmpty();
502 m_insulatedIslands[pair.first].clear();
503
504 // Replace the shared_ptr with a new empty object rather than clearing the existing one.
505 // This ensures that any CN_ZONE_LAYERs holding shared_ptr copies still have valid data
506 // (the old, now orphaned SHAPE_POLY_SET) while we get a fresh container.
507 pair.second = std::make_shared<SHAPE_POLY_SET>();
508 }
509
510 m_isFilled = false;
511 m_fillFlags.reset();
512
513 return change;
514}
515
516
518{
519 return HasFlag( COURTYARD_CONFLICT );
520}
521
522
524{
525 if( m_Poly->OutlineCount() == 0 || m_Poly->TotalVertices() == 0 )
526 return VECTOR2I( 0, 0 );
527
528 return GetCornerPosition( 0 );
529}
530
531
533{
534 std::lock_guard<std::mutex> lock( m_layerSetMutex );
535
536 if( m_layerSet.count() == 1 )
537 {
538 // GetFirstLayer would try to acquire the mutex again, so inline its logic here
539 if( m_layerSet.count() == 0 )
540 return UNDEFINED_LAYER;
541
542 const LSEQ uiLayers = m_layerSet.UIOrder();
543
544 if( uiLayers.size() )
545 return uiLayers[0];
546
547 return m_layerSet.Seq()[0];
548 }
549
550 return UNDEFINED_LAYER;
551}
552
553
555{
556 std::lock_guard<std::mutex> lock( m_layerSetMutex );
557
558 if( m_layerSet.count() == 0 )
559 return UNDEFINED_LAYER;
560
561 const LSEQ uiLayers = m_layerSet.UIOrder();
562
563 // This can't use m_layerSet.count() because it's possible to have a zone on
564 // a rescue layer that is not in the UI order.
565 if( uiLayers.size() )
566 return uiLayers[0];
567
568 // If it's not in the UI set at all, just return the first layer in the set.
569 // (we know the count > 0)
570 return m_layerSet.Seq()[0];
571}
572
573
575{
576 std::lock_guard<std::mutex> lock( m_layerSetMutex );
577 return ( m_layerSet & LSET::AllCuMask() ).count() > 0;
578}
579
580
581bool ZONE::SetNetCode( int aNetCode, bool aNoAssert )
582{
583 if( IsCopperThieving() )
584 aNetCode = 0;
585
586 return BOARD_CONNECTED_ITEM::SetNetCode( aNetCode, aNoAssert );
587}
588
589
590void ZONE::SetNet( NETINFO_ITEM* aNetInfo )
591{
592 if( IsCopperThieving() )
593 aNetInfo = nullptr;
594
596}
597
598
600{
601 SetLayerSet( LSET( { aLayer } ) );
602}
603
604
606{
607 if( m_fillMode == aFillMode )
608 return;
609
610 SetNeedRefill( true );
611 m_fillMode = aFillMode;
612
613 // Thieving zones are net-less and single-layer; clamp on transition so a
614 // multi-layer netted POLYGONS zone converted via the property panel or a
615 // load-time SetLayerSet-then-SetFillMode sequence cannot keep stale state.
617 {
620 }
621}
622
623
624void ZONE::SetLayerSet( const LSET& aLayerSet )
625{
626 if( aLayerSet.count() == 0 )
627 return;
628
629 // Thieving zones are single-layer; clamp here so direct callers cannot violate
630 // the invariant. UIOrder().front() matches GetFirstLayer().
631 const LSET effectiveSet = ( IsCopperThieving() && aLayerSet.count() > 1 )
632 ? LSET( { aLayerSet.UIOrder().front() } )
633 : aLayerSet;
634
635 std::scoped_lock lock( m_layerSetMutex, m_filledPolysListMutex );
636
637 if( m_layerSet != effectiveSet )
638 {
639 SetNeedRefill( true );
640
641 unFillLocked();
642
643 m_FilledPolysList.clear();
644 m_filledPolysHash.clear();
645 m_insulatedIslands.clear();
646
647 effectiveSet.RunOnLayers(
648 [&]( PCB_LAYER_ID layer )
649 {
650 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>();
651 m_filledPolysHash[layer] = {};
652 m_insulatedIslands[layer] = {};
653 } );
654
655 std::erase_if( m_layerProperties,
656 [&]( const auto& item )
657 {
658 return !effectiveSet.Contains( item.first );
659 } );
660 }
661
662 m_layerSet = effectiveSet;
663}
664
665
666void ZONE::SetLayerProperties( const std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aOther )
667{
668 m_layerProperties = aOther;
669}
670
671
672std::vector<int> ZONE::ViewGetLayers() const
673{
674 std::lock_guard<std::mutex> lock( m_layerSetMutex );
675
676 std::vector<int> layers;
677 layers.reserve( 2 * m_layerSet.count() + 1 );
678
679 m_layerSet.RunOnLayers(
680 [&]( PCB_LAYER_ID layer )
681 {
682 layers.push_back( layer );
683 layers.push_back( layer + static_cast<int>( LAYER_ZONE_START ) );
684 } );
685
686 layers.push_back( LAYER_CONFLICTS_SHADOW );
687
688 return layers;
689}
690
691
692double ZONE::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
693{
694 if( !aView )
695 return LOD_SHOW;
696
697 if( !aView->IsLayerVisibleCached( LAYER_ZONES ) )
698 return LOD_HIDE;
699
700 if( FOOTPRINT* parentFP = GetParentFootprint() )
701 {
702 const LSET zl = GetLayerSet();
703 bool onFront = ( zl & LSET::FrontMask() ).any();
704 bool onBack = ( zl & LSET::BackMask() ).any();
705
706 if( !onFront && !onBack )
707 {
708 onFront = parentFP->GetLayer() == F_Cu;
709 onBack = parentFP->GetLayer() == B_Cu;
710 }
711
712 const bool frHidden = !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_FR );
713 const bool bkHidden = !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_BK );
714
715 if( onFront && !onBack && frHidden )
716 return LOD_HIDE;
717
718 if( onBack && !onFront && bkHidden )
719 return LOD_HIDE;
720
721 if( onFront && onBack && frHidden && bkHidden )
722 return LOD_HIDE;
723 }
724
725 // Other layers are shown without any conditions
726 return LOD_SHOW;
727}
728
729
730bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer ) const
731{
732 std::lock_guard<std::mutex> lock( m_layerSetMutex );
733 return m_layerSet.test( aLayer );
734}
735
736
738{
739 auto computeBBox = [&]() -> BOX2I
740 {
741 if( GetParentFootprint() )
742 return GetBoardOutline().BBox();
743
744 return m_Poly->BBox();
745 };
746
747 if( const BOARD* board = GetBoard() )
748 {
749 std::unordered_map<const ZONE*, BOX2I>& cache = board->m_ZoneBBoxCache;
750
751 {
752 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
753
754 auto cacheIter = cache.find( this );
755
756 if( cacheIter != cache.end() )
757 return cacheIter->second;
758 }
759
760 BOX2I bbox = computeBBox();
761
762 {
763 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
764 cache[ this ] = bbox;
765 }
766
767 return bbox;
768 }
769
770 return computeBBox();
771}
772
773
775{
776 // GetBoundingBox() will cache it for us, and there's no sense duplicating the somewhat tricky
777 // locking code.
779}
780
781
782int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
783{
784 if( aPad->GetLocalThermalGapOverride() == 0 )
785 {
786 if( aSource )
787 *aSource = _( "zone" );
788
789 return m_thermalReliefGap;
790 }
791
792 return aPad->GetLocalThermalGapOverride( aSource );
793
794}
795
796
797void ZONE::SetCornerRadius( unsigned int aRadius )
798{
799 if( m_cornerRadius != aRadius )
800 SetNeedRefill( true );
801
802 m_cornerRadius = aRadius;
803}
804
805
807
808
810{
811 if( !m_filledPolysHash.count( aLayer ) )
812 return g_nullPoly.GetHash();
813 else
814 return m_filledPolysHash.at( aLayer );
815}
816
817
819{
820 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
821
822 if( !m_FilledPolysList.count( aLayer ) )
823 m_filledPolysHash[aLayer] = g_nullPoly.GetHash();
824 else
825 m_filledPolysHash[aLayer] = m_FilledPolysList.at( aLayer )->GetHash();
826}
827
828
833
834
836{
838
839 if( const FOOTPRINT* fp = GetParentFootprint() )
840 {
841 const TRANSFORM_TRS& xform = fp->GetTransform();
842
843 for( auto it = poly.IterateWithHoles(); it; it++ )
844 poly.SetVertex( it.GetIndex(), xform.Apply( *it ) );
845 }
846
847 return poly;
848}
849
850
851bool ZONE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
852{
853 // When looking for an "exact" hit aAccuracy will be 0 which works poorly for very thin
854 // lines. Give it a floor.
855 int accuracy = std::max( aAccuracy, pcbIUScale.mmToIU( 0.1 ) );
856
857 return HitTestForCorner( aPosition, accuracy * 2 ) || HitTestForEdge( aPosition, accuracy );
858}
859
860
861bool ZONE::HitTestForCorner( const VECTOR2I& refPos, int aAccuracy,
862 SHAPE_POLY_SET::VERTEX_INDEX* aCornerHit ) const
863{
864 VECTOR2I libPos = refPos;
865
866 if( const FOOTPRINT* fp = GetParentFootprint() )
867 libPos = fp->GetTransform().InverseApply( refPos );
868
869 return m_Poly->CollideVertex( libPos, aCornerHit, aAccuracy );
870}
871
872
873bool ZONE::HitTestForEdge( const VECTOR2I& refPos, int aAccuracy,
874 SHAPE_POLY_SET::VERTEX_INDEX* aCornerHit ) const
875{
876 VECTOR2I libPos = refPos;
877
878 if( const FOOTPRINT* fp = GetParentFootprint() )
879 libPos = fp->GetTransform().InverseApply( refPos );
880
881 return m_Poly->CollideEdge( libPos, aCornerHit, aAccuracy );
882}
883
884
885bool ZONE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
886{
887 // Calculate bounding box for zone
888 BOX2I bbox = GetBoundingBox();
889 bbox.Normalize();
890
891 BOX2I arect = aRect;
892 arect.Normalize();
893 arect.Inflate( aAccuracy );
894
895 if( aContained )
896 {
897 return arect.Contains( bbox );
898 }
899 else
900 {
901 // Fast test: if aBox is outside the polygon bounding box, rectangles cannot intersect
902 if( !arect.Intersects( bbox ) )
903 return false;
904
905 SHAPE_POLY_SET boardOutline = GetBoardOutline();
906 int count = boardOutline.TotalVertices();
907
908 for( int ii = 0; ii < count; ii++ )
909 {
910 VECTOR2I vertex = boardOutline.CVertex( ii );
911 VECTOR2I vertexNext = boardOutline.CVertex( ( ii + 1 ) % count );
912
913 // Test if the point is within the rect
914 if( arect.Contains( vertex ) )
915 return true;
916
917 // Test if this edge intersects the rect
918 if( arect.Intersects( vertex, vertexNext ) )
919 return true;
920 }
921
922 return false;
923 }
924}
925
926
927bool ZONE::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
928{
929 SHAPE_POLY_SET boardOutline = GetBoardOutline();
930
931 if( aContained )
932 {
933 auto outlineIntersectingSelection = [&]()
934 {
935 for( auto segment = boardOutline.IterateSegments(); segment; segment++ )
936 {
937 if( aPoly.Intersects( *segment ) )
938 return true;
939 }
940
941 return false;
942 };
943
944 // In the case of contained selection, all vertices of the zone outline must be inside
945 // the selection polygon, so we can check only the first vertex.
946 auto vertexInsideSelection = [&]()
947 {
948 return aPoly.PointInside( boardOutline.CVertex( 0 ) );
949 };
950
951 return vertexInsideSelection() && !outlineIntersectingSelection();
952 }
953 else
954 {
955 // Touching selection - check if any segment of the zone contours collides with the
956 // selection shape.
957 for( auto segment = boardOutline.IterateSegmentsWithHoles(); segment; segment++ )
958 {
959 if( aPoly.PointInside( ( *segment ).A ) )
960 return true;
961
962 if( aPoly.Intersects( *segment ) )
963 return true;
964
965 // Note: aPoly.Collide() could be used instead of two test above, but it is 3x slower.
966 }
967
968 return false;
969 }
970}
971
972
973std::optional<int> ZONE::GetLocalClearance() const
974{
975 return m_isRuleArea ? 0 : m_ZoneClearance;
976}
977
978
979bool ZONE::HitTestFilledArea( PCB_LAYER_ID aLayer, const VECTOR2I& aRefPos, int aAccuracy ) const
980{
981 // Rule areas have no filled area, but it's generally nice to treat their interior as if it were
982 // filled so that people don't have to select them by their outline (which is min-width)
983 if( GetIsRuleArea() )
984 {
985 VECTOR2I libPos = aRefPos;
986
987 if( const FOOTPRINT* fp = GetParentFootprint() )
988 libPos = fp->GetTransform().InverseApply( aRefPos );
989
990 return m_Poly->Contains( libPos, -1, aAccuracy );
991 }
992
993 std::shared_ptr<SHAPE_POLY_SET> fillPolys;
994
995 {
996 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
997
998 if( !m_FilledPolysList.count( aLayer ) )
999 return false;
1000
1001 fillPolys = m_FilledPolysList.at( aLayer );
1002 }
1003
1004 return fillPolys->Contains( aRefPos, -1, aAccuracy );
1005}
1006
1007
1008bool ZONE::HitTestCutout( const VECTOR2I& aRefPos, int* aOutlineIdx, int* aHoleIdx ) const
1009{
1010 VECTOR2I libPos = aRefPos;
1011
1012 if( const FOOTPRINT* fp = GetParentFootprint() )
1013 libPos = fp->GetTransform().InverseApply( aRefPos );
1014
1015 // Iterate over each outline polygon in the zone and then iterate over
1016 // each hole it has to see if the point is in it.
1017 for( int i = 0; i < m_Poly->OutlineCount(); i++ )
1018 {
1019 for( int j = 0; j < m_Poly->HoleCount( i ); j++ )
1020 {
1021 if( m_Poly->Hole( i, j ).PointInside( libPos ) )
1022 {
1023 if( aOutlineIdx )
1024 *aOutlineIdx = i;
1025
1026 if( aHoleIdx )
1027 *aHoleIdx = j;
1028
1029 return true;
1030 }
1031 }
1032 }
1033
1034 return false;
1035}
1036
1037
1038void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1039{
1040 wxString msg = GetFriendlyName();
1041
1042 aList.emplace_back( _( "Type" ), msg );
1043
1044 if( GetIsRuleArea() )
1045 {
1046 msg.Empty();
1047
1048 if( GetDoNotAllowVias() )
1049 AccumulateDescription( msg, _( "No vias" ) );
1050
1051 if( GetDoNotAllowTracks() )
1052 AccumulateDescription( msg, _( "No tracks" ) );
1053
1054 if( GetDoNotAllowPads() )
1055 AccumulateDescription( msg, _( "No pads" ) );
1056
1058 AccumulateDescription( msg, _( "No zone fills" ) );
1059
1061 AccumulateDescription( msg, _( "No footprints" ) );
1062
1063 if( !msg.IsEmpty() )
1064 aList.emplace_back( _( "Restrictions" ), msg );
1065
1067 aList.emplace_back( _( "Placement source" ), UnescapeString( GetPlacementAreaSource() ) );
1068 }
1069 else if( IsOnCopperLayer() )
1070 {
1071 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1072 {
1073 aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
1074
1075 aList.emplace_back( _( "Resolved Netclass" ),
1076 UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
1077 }
1078
1079 // Display priority level
1080 aList.emplace_back( _( "Priority" ), wxString::Format( wxT( "%d" ), GetAssignedPriority() ) );
1081 }
1082
1083 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1084 {
1085 if( IsLocked() )
1086 aList.emplace_back( _( "Status" ), _( "Locked" ) );
1087 }
1088
1089 LSEQ layers = m_layerSet.Seq();
1090 wxString layerDesc;
1091
1092 if( layers.size() == 1 )
1093 {
1094 layerDesc.Printf( _( "%s" ), GetBoard()->GetLayerName( layers[0] ) );
1095 }
1096 else if (layers.size() == 2 )
1097 {
1098 layerDesc.Printf( _( "%s and %s" ),
1099 GetBoard()->GetLayerName( layers[0] ),
1100 GetBoard()->GetLayerName( layers[1] ) );
1101 }
1102 else if (layers.size() == 3 )
1103 {
1104 layerDesc.Printf( _( "%s, %s and %s" ),
1105 GetBoard()->GetLayerName( layers[0] ),
1106 GetBoard()->GetLayerName( layers[1] ),
1107 GetBoard()->GetLayerName( layers[2] ) );
1108 }
1109 else if( layers.size() > 3 )
1110 {
1111 layerDesc.Printf( _( "%s, %s and %d more" ),
1112 GetBoard()->GetLayerName( layers[0] ),
1113 GetBoard()->GetLayerName( layers[1] ),
1114 static_cast<int>( layers.size() - 2 ) );
1115 }
1116
1117 aList.emplace_back( _( "Layer" ), layerDesc );
1118
1119 if( !m_zoneName.empty() )
1120 aList.emplace_back( _( "Name" ), m_zoneName );
1121
1122 if( !GetIsRuleArea() ) // Show fill mode only for not rule areas
1123 {
1124 switch( m_fillMode )
1125 {
1126 case ZONE_FILL_MODE::POLYGONS: msg = _( "Solid" ); break;
1127 case ZONE_FILL_MODE::HATCH_PATTERN: msg = _( "Hatched" ); break;
1128 default: msg = _( "Unknown" ); break;
1129 }
1130
1131 aList.emplace_back( _( "Fill Mode" ), msg );
1132
1133 aList.emplace_back( _( "Filled Area" ),
1135
1136 wxString source;
1137 int clearance = GetOwnClearance( UNDEFINED_LAYER, &source );
1138
1139 if( !source.IsEmpty() )
1140 {
1141 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ), aFrame->MessageTextFromValue( clearance ) ),
1142 wxString::Format( _( "(from %s)" ), source ) );
1143 }
1144 }
1145
1146 int count = 0;
1147
1148 if( GetIsRuleArea() )
1149 {
1150 double outline_area = CalculateOutlineArea();
1151 aList.emplace_back( _( "Outline Area" ),
1152 aFrame->MessageTextFromValue( outline_area, true, EDA_DATA_TYPE::AREA ) );
1153
1154 const SHAPE_POLY_SET* area_outline = Outline();
1155 count = area_outline->FullPointCount();
1156 }
1157 else if( !m_FilledPolysList.empty() )
1158 {
1159 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& ii: m_FilledPolysList )
1160 count += ii.second->TotalVertices();
1161 }
1162
1163 aList.emplace_back( _( "Corner Count" ), wxString::Format( wxT( "%d" ), count ) );
1164}
1165
1166
1167void ZONE::Move( const VECTOR2I& offset )
1168{
1169 VECTOR2I outlineOffset = offset;
1170
1171 if( const FOOTPRINT* fp = GetParentFootprint() )
1172 {
1173 const TRANSFORM_TRS& xform = fp->GetTransform();
1174 outlineOffset = xform.InverseApply( offset ) - xform.InverseApply( VECTOR2I( 0, 0 ) );
1175 }
1176
1177 m_Poly->Move( outlineOffset );
1178
1179 // Translate existing hatch lines instead of regenerating them. HatchBorder() is expensive
1180 // (O(n*m) segment intersections + point-in-polygon tests) and the hatch pattern is
1181 // invariant under translation.
1182 for( SEG& seg : m_borderHatchLines )
1183 {
1184 seg.A += outlineOffset;
1185 seg.B += outlineOffset;
1186 }
1187
1188 /* move fills */
1189 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1190 pair.second->Move( offset );
1191
1192 /*
1193 * move boundingbox cache
1194 *
1195 * While the cache will get nuked at the conclusion of the operation, we use it for some
1196 * things (such as drawing the parent group) during the move.
1197 */
1198 if( GetBoard() )
1199 {
1200 auto it = GetBoard()->m_ZoneBBoxCache.find( this );
1201
1202 if( it != GetBoard()->m_ZoneBBoxCache.end() )
1203 it->second.Move( offset );
1204 }
1205}
1206
1207
1209{
1210 if( GetIsRuleArea() )
1211 return _( "Rule Area" );
1212 else if( IsTeardropArea() )
1213 return _( "Teardrop Area" );
1214 else if( IsOnCopperLayer() )
1215 return _( "Copper Zone" );
1216 else
1217 return _( "Non-copper Zone" );
1218}
1219
1220
1221void ZONE::MoveEdge( const VECTOR2I& offset, int aEdge )
1222{
1223 int next_corner;
1224
1225 if( m_Poly->GetNeighbourIndexes( aEdge, nullptr, &next_corner ) )
1226 {
1227 VECTOR2I libOffset = offset;
1228
1229 if( const FOOTPRINT* fp = GetParentFootprint() )
1230 {
1231 const TRANSFORM_TRS& xform = fp->GetTransform();
1232 libOffset = xform.InverseApply( offset ) - xform.InverseApply( VECTOR2I( 0, 0 ) );
1233 }
1234
1235 m_Poly->SetVertex( aEdge, m_Poly->CVertex( aEdge ) + libOffset );
1236 m_Poly->SetVertex( next_corner, m_Poly->CVertex( next_corner ) + libOffset );
1237 HatchBorder();
1238
1239 SetNeedRefill( true );
1240 }
1241}
1242
1243
1244void ZONE::Rotate( const VECTOR2I& aCentre, const EDA_ANGLE& aAngle )
1245{
1246 VECTOR2I outlineCentre = aCentre;
1247
1248 if( const FOOTPRINT* fp = GetParentFootprint() )
1249 outlineCentre = fp->GetTransform().InverseApply( aCentre );
1250
1251 m_Poly->Rotate( aAngle, outlineCentre );
1252 HatchBorder();
1253
1254 /* rotate filled areas: */
1255 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1256 pair.second->Rotate( aAngle, aCentre );
1257}
1258
1259
1260void ZONE::OnFootprintRescaled( double aRatioX, double aRatioY, double /* aLinearFactor */,
1261 const VECTOR2I& /* aAnchor */, const EDA_ANGLE& /* aParentRotate */ )
1262{
1263 if( aRatioX == 1.0 && aRatioY == 1.0 )
1264 return;
1265
1266 // Zone outline auto-derives from lib storage through the parent transform.
1267 // Just invalidate the fill since geometry-on-screen has changed.
1268 SetNeedRefill( true );
1269 UnFill();
1270}
1271
1272
1273void ZONE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
1274{
1275 Mirror( aCentre, aFlipDirection );
1276
1277 std::map<PCB_LAYER_ID, SHAPE_POLY_SET> fillsCopy;
1278
1279 for( auto& [oldLayer, shapePtr] : m_FilledPolysList )
1280 fillsCopy[oldLayer] = *shapePtr;
1281
1282 std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES> layerPropertiesCopy = m_layerProperties;
1283
1284 LSET flipped;
1285
1286 for( PCB_LAYER_ID layer : GetLayerSet() )
1287 flipped.set( GetBoard()->FlipLayer( layer ) );
1288
1289 SetLayerSet( flipped );
1290
1291 for( auto& [oldLayer, properties] : layerPropertiesCopy )
1292 {
1293 PCB_LAYER_ID newLayer = GetBoard()->FlipLayer( oldLayer );
1294 m_layerProperties[newLayer] = properties;
1295 }
1296
1297 for( auto& [oldLayer, shape] : fillsCopy )
1298 {
1299 PCB_LAYER_ID newLayer = GetBoard()->FlipLayer( oldLayer );
1300 SetFilledPolysList( newLayer, shape );
1301 }
1302}
1303
1304
1305void ZONE::Mirror( const VECTOR2I& aMirrorRef, FLIP_DIRECTION aFlipDirection )
1306{
1307 VECTOR2I outlineRef = aMirrorRef;
1308
1309 if( const FOOTPRINT* fp = GetParentFootprint() )
1310 outlineRef = fp->GetTransform().InverseApply( aMirrorRef );
1311
1312 m_Poly->Mirror( outlineRef, aFlipDirection );
1313
1314 HatchBorder();
1315
1316 for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
1317 pair.second->Mirror( aMirrorRef, aFlipDirection );
1318}
1319
1320
1321void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx )
1322{
1323 // Ensure the requested cutout is valid
1324 if( m_Poly->OutlineCount() < aOutlineIdx || m_Poly->HoleCount( aOutlineIdx ) < aHoleIdx )
1325 return;
1326
1327 SHAPE_POLY_SET cutPoly( m_Poly->Hole( aOutlineIdx, aHoleIdx ) );
1328
1329 // Add the cutout back to the zone
1330 m_Poly->BooleanAdd( cutPoly );
1331
1332 SetNeedRefill( true );
1333}
1334
1335
1336void ZONE::AddPolygon( const SHAPE_LINE_CHAIN& aPolygon )
1337{
1338 wxASSERT( aPolygon.IsClosed() );
1339
1340 // Add the outline as a new polygon in the polygon set
1341 if( m_Poly->OutlineCount() == 0 )
1342 m_Poly->AddOutline( aPolygon );
1343 else
1344 m_Poly->AddHole( aPolygon );
1345
1346 SetNeedRefill( true );
1347}
1348
1349
1350void ZONE::AddPolygon( std::vector<VECTOR2I>& aPolygon )
1351{
1352 if( aPolygon.empty() )
1353 return;
1354
1355 SHAPE_LINE_CHAIN outline;
1356
1357 // Create an outline and populate it with the points of aPolygon
1358 for( const VECTOR2I& pt : aPolygon )
1359 outline.Append( pt );
1360
1361 outline.SetClosed( true );
1362
1363 AddPolygon( outline );
1364}
1365
1366
1367bool ZONE::AppendCorner( VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication )
1368{
1369 // Ensure the main outline exists:
1370 if( m_Poly->OutlineCount() == 0 )
1371 m_Poly->NewOutline();
1372
1373 // If aHoleIdx >= 0, the corner musty be added to the hole, index aHoleIdx.
1374 // (remember: the index of the first hole is 0)
1375 // Return error if it does not exist.
1376 if( aHoleIdx >= m_Poly->HoleCount( 0 ) )
1377 return false;
1378
1379 m_Poly->Append( aPosition.x, aPosition.y, -1, aHoleIdx, aAllowDuplication );
1380
1381 SetNeedRefill( true );
1382
1383 return true;
1384}
1385
1386
1387wxString ZONE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
1388{
1389 LSEQ layers = m_layerSet.Seq();
1390 wxString layerDesc;
1391
1392 if( layers.size() == 1 )
1393 {
1394 layerDesc.Printf( _( "on %s" ), GetBoard()->GetLayerName( layers[0] ) );
1395 }
1396 else if (layers.size() == 2 )
1397 {
1398 layerDesc.Printf( _( "on %s and %s" ),
1399 GetBoard()->GetLayerName( layers[0] ),
1400 GetBoard()->GetLayerName( layers[1] ) );
1401 }
1402 else if (layers.size() == 3 )
1403 {
1404 layerDesc.Printf( _( "on %s, %s and %s" ),
1405 GetBoard()->GetLayerName( layers[0] ),
1406 GetBoard()->GetLayerName( layers[1] ),
1407 GetBoard()->GetLayerName( layers[2] ) );
1408 }
1409 else if( layers.size() > 3 )
1410 {
1411 layerDesc.Printf( _( "on %s, %s and %zu more" ),
1412 GetBoard()->GetLayerName( layers[0] ),
1413 GetBoard()->GetLayerName( layers[1] ),
1414 layers.size() - 2 );
1415 }
1416
1417 if( GetIsRuleArea() )
1418 {
1419 if( GetZoneName().IsEmpty() )
1420 {
1421 return wxString::Format( _( "Rule Area %s" ),
1422 layerDesc );
1423 }
1424 else
1425 {
1426 return wxString::Format( _( "Rule area '%s' %s" ),
1427 GetZoneName(),
1428 layerDesc );
1429 }
1430 }
1431 else if( IsTeardropArea() )
1432 {
1433 return wxString::Format( _( "Teardrop %s %s" ),
1434 GetNetnameMsg(),
1435 layerDesc );
1436 }
1437 else
1438 {
1439 if( GetZoneName().IsEmpty() )
1440 {
1441 return wxString::Format( _( "Zone %s %s, priority %d" ),
1442 GetNetnameMsg(),
1443 layerDesc,
1445 }
1446 else
1447 {
1448 return wxString::Format( _( "Zone '%s' %s %s, priority %d" ),
1449 GetZoneName(),
1450 GetNetnameMsg(),
1451 layerDesc,
1453 }
1454 }
1455}
1456
1457
1459 int aBorderHatchPitch, bool aRebuildBorderHatch )
1460{
1461 aBorderHatchPitch = std::max( aBorderHatchPitch, pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MINDIST_MM ) );
1462 aBorderHatchPitch = std::min( aBorderHatchPitch, pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MAXDIST_MM ) );
1463 SetBorderHatchPitch( aBorderHatchPitch );
1464 m_borderStyle = aBorderHatchStyle;
1465
1466 if( aRebuildBorderHatch )
1467 HatchBorder();
1468}
1469
1470
1472{
1473 m_borderHatchLines.clear();
1474}
1475
1476
1478{
1479 UnHatchBorder();
1480
1482 || m_borderHatchPitch == 0
1483 || m_Poly->IsEmpty() )
1484 {
1485 return;
1486 }
1487
1488 // set the "length" of hatch lines (the length on horizontal axis)
1489 int hatch_line_len = m_borderHatchPitch; // OK for DIAGONAL_EDGE style
1490
1491 // Calculate spacing between 2 hatch lines
1492 int spacing = m_borderHatchPitch; // OK for DIAGONAL_EDGE style
1493
1495 {
1496 // The spacing is twice the spacing for DIAGONAL_EDGE because one
1497 // full diagonal replaces 2 edge diagonal hatch segments in code
1498 spacing = m_borderHatchPitch * 2;
1499 hatch_line_len = -1; // Use full diagonal hatch line
1500 }
1501
1502 // To have a better look, give a slope depending on the layer
1503 int layer = GetFirstLayer();
1504 std::vector<double> slopes;
1505
1506 if( IsTeardropArea() )
1507 slopes = { 0.7, -0.7 };
1508 else if( layer & 1 )
1509 slopes = { 1 };
1510 else
1511 slopes = { -1 };
1512
1513 m_borderHatchLines = m_Poly->GenerateHatchLines( slopes, spacing, hatch_line_len );
1514}
1515
1516
1517std::vector<SEG> ZONE::GetHatchLines() const
1518{
1519 const FOOTPRINT* fp = GetParentFootprint();
1520
1521 if( !fp )
1522 return m_borderHatchLines;
1523
1524 const TRANSFORM_TRS& xform = fp->GetTransform();
1525 std::vector<SEG> result;
1526 result.reserve( m_borderHatchLines.size() );
1527
1528 for( const SEG& seg : m_borderHatchLines )
1529 result.emplace_back( xform.Apply( seg.A ), xform.Apply( seg.B ) );
1530
1531 return result;
1532}
1533
1534
1536{
1537 return pcbIUScale.mmToIU( ZONE_BORDER_HATCH_DIST_MM );
1538}
1539
1540
1542{
1543 return BITMAPS::add_zone;
1544}
1545
1546
1548{
1549 wxASSERT( aImage->Type() == PCB_ZONE_T );
1550
1551 std::swap( *static_cast<ZONE*>( this ), *static_cast<ZONE*>( aImage) );
1552}
1553
1554
1556{
1557 if( aLayer == UNDEFINED_LAYER )
1558 {
1559 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1560
1561 for( auto& [ layer, poly ] : m_FilledPolysList )
1562 poly->CacheTriangulation( false, aSubmitter );
1563
1564 m_Poly->CacheTriangulation();
1565 }
1566 else
1567 {
1568 // Grab a shared_ptr copy under the lock, then triangulate outside it.
1569 // Each layer's SHAPE_POLY_SET is independent, so concurrent triangulation
1570 // of different layers is safe once we have the shared_ptr.
1571 std::shared_ptr<SHAPE_POLY_SET> poly;
1572
1573 {
1574 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1575
1576 auto it = m_FilledPolysList.find( aLayer );
1577
1578 if( it != m_FilledPolysList.end() )
1579 poly = it->second;
1580 }
1581
1582 if( poly )
1583 poly->CacheTriangulation( false, aSubmitter );
1584 }
1585}
1586
1587
1588bool ZONE::IsIsland( PCB_LAYER_ID aLayer, int aPolyIdx ) const
1589{
1590 if( GetNetCode() < 1 )
1591 return true;
1592
1593 if( !m_insulatedIslands.count( aLayer ) )
1594 return false;
1595
1596 return m_insulatedIslands.at( aLayer ).count( aPolyIdx );
1597}
1598
1599
1600void ZONE::GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE*>* aSameNetCollidingZones,
1601 std::vector<ZONE*>* aOtherNetIntersectingZones ) const
1602{
1603 int epsilon = pcbIUScale.mmToIU( 0.001 );
1604 BOX2I bbox = GetBoundingBox();
1605
1606 bbox.Inflate( epsilon );
1607
1608 for( ZONE* candidate : GetBoard()->Zones() )
1609 {
1610 if( candidate == this )
1611 continue;
1612
1613 if( !candidate->GetLayerSet().test( aLayer ) )
1614 continue;
1615
1616 if( candidate->GetIsRuleArea() || candidate->IsTeardropArea() )
1617 continue;
1618
1619 if( !candidate->GetBoundingBox().Intersects( bbox ) )
1620 continue;
1621
1622 if( candidate->GetNetCode() == GetNetCode() )
1623 {
1624 SHAPE_POLY_SET selfBoard = GetBoardOutline();
1625 SHAPE_POLY_SET candidateBoard = candidate->GetBoardOutline();
1626
1627 if( selfBoard.Collide( &candidateBoard ) )
1628 aSameNetCollidingZones->push_back( candidate );
1629 }
1630 else
1631 {
1632 aOtherNetIntersectingZones->push_back( candidate );
1633 }
1634 }
1635}
1636
1637
1639 SHAPE_POLY_SET* aBoardOutline,
1640 SHAPE_POLY_SET* aSmoothedPolyWithApron ) const
1641{
1642 if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations will not like it ...
1643 return false;
1644
1645 // Processing of arc shapes in zones is not yet supported because Clipper can't do boolean
1646 // operations on them. The poly outline must be converted to segments first.
1647 SHAPE_POLY_SET flattened = GetBoardOutline();
1648 flattened.ClearArcs();
1649
1650 if( GetIsRuleArea() )
1651 {
1652 // We like keepouts just the way they are....
1653 aSmoothedPoly = std::move( flattened );
1654 return true;
1655 }
1656
1657 const BOARD* board = GetBoard();
1658 bool keepExternalFillets = false;
1661
1662 if( IsTeardropArea() )
1663 {
1664 // We use teardrop shapes with no smoothing; these shapes are already optimized
1665 smooth_requested = false;
1666 }
1667
1668 if( board )
1669 keepExternalFillets = board->GetDesignSettings().m_ZoneKeepExternalFillets;
1670
1671 auto smooth =
1672 [&]( SHAPE_POLY_SET& aPoly )
1673 {
1674 if( !smooth_requested )
1675 return;
1676
1677 switch( m_cornerSmoothingType )
1678 {
1680 aPoly = aPoly.Chamfer( (int) m_cornerRadius );
1681 break;
1682
1684 aPoly = aPoly.Fillet( (int) m_cornerRadius, GetMaxError() );
1685 break;
1686
1687 default:
1688 break;
1689 }
1690 };
1691
1692 SHAPE_POLY_SET* maxExtents = &flattened;
1693 SHAPE_POLY_SET withFillets;
1694
1695 aSmoothedPoly = flattened;
1696
1697 // Should external fillets (that is, those applied to concave corners) be kept? While it
1698 // seems safer to never have copper extend outside the zone outline, 5.1.x and prior did
1699 // indeed fill them so we leave the mode available.
1700 if( keepExternalFillets && smooth_requested )
1701 {
1702 withFillets = flattened;
1703 smooth( withFillets );
1704 withFillets.BooleanAdd( flattened );
1705 maxExtents = &withFillets;
1706 }
1707
1708 // We now add in the areas of any same-net, intersecting zones. This keeps us from smoothing
1709 // corners at an intersection (which often produces undesired divots between the intersecting
1710 // zones -- see #2752).
1711 //
1712 // After smoothing, we'll subtract back out everything outside of our zone.
1713 std::vector<ZONE*> sameNetCollidingZones;
1714 std::vector<ZONE*> diffNetIntersectingZones;
1715 GetInteractingZones( aLayer, &sameNetCollidingZones, &diffNetIntersectingZones );
1716
1717 for( ZONE* sameNetZone : sameNetCollidingZones )
1718 {
1719 BOX2I sameNetBoundingBox = sameNetZone->GetBoundingBox();
1720
1721 // Note: a two-pass algorithm could use sameNetZone's actual fill instead of its outline.
1722 // This would obviate the need for the below wrinkles, in addition to fixing both issues
1723 // in #16095.
1724 // (And we wouldn't need to collect all the diffNetIntersectingZones either.)
1725
1726 SHAPE_POLY_SET sameNetPoly = sameNetZone->GetBoardOutline();
1727 sameNetPoly.ClearArcs();
1728
1729 SHAPE_POLY_SET diffNetPoly;
1730
1731 // Of course there's always a wrinkle. The same-net intersecting zone *might* get knocked
1732 // out along the border by a higher-priority, different-net zone. #12797
1733 for( ZONE* diffNetZone : diffNetIntersectingZones )
1734 {
1735 if( diffNetZone->HigherPriority( sameNetZone )
1736 && diffNetZone->GetBoundingBox().Intersects( sameNetBoundingBox ) )
1737 {
1738 SHAPE_POLY_SET diffNetOutline = diffNetZone->GetBoardOutline();
1739 diffNetOutline.ClearArcs();
1740
1741 diffNetPoly.BooleanAdd( diffNetOutline );
1742 }
1743 }
1744
1745 // Second wrinkle. After unioning the higher priority, different net zones together, we
1746 // need to check to see if they completely enclose our zone. If they do, then we need to
1747 // treat the enclosed zone as isolated, not connected to the outer zone. #13915
1748 bool isolated = false;
1749
1750 if( diffNetPoly.OutlineCount() )
1751 {
1753 thisPoly.ClearArcs();
1754
1755 thisPoly.BooleanSubtract( diffNetPoly );
1756 isolated = thisPoly.OutlineCount() == 0;
1757 }
1758
1759 if( !isolated )
1760 aSmoothedPoly.BooleanAdd( sameNetPoly );
1761 }
1762
1763 if( aBoardOutline )
1764 {
1765 SHAPE_POLY_SET boardOutline = aBoardOutline->CloneDropTriangulation();
1766 boardOutline.ClearArcs();
1767
1768 aSmoothedPoly.BooleanIntersection( boardOutline );
1769 }
1770
1771 SHAPE_POLY_SET withSameNetIntersectingZones = aSmoothedPoly.CloneDropTriangulation();
1772
1773 smooth( aSmoothedPoly );
1774
1775 if( aSmoothedPolyWithApron )
1776 {
1777 // The same-net intersecting-zone code above makes sure the corner-smoothing algorithm
1778 // doesn't produce divots. But the min-thickness algorithm applied in fillCopperZone()
1779 // is *also* going to perform a deflate/inflate cycle, again leading to divots. So we
1780 // pre-inflate the contour by the min-thickness within the same-net-intersecting-zones
1781 // envelope.
1782 SHAPE_POLY_SET poly = maxExtents->CloneDropTriangulation();
1784
1785 if( !keepExternalFillets )
1786 poly.BooleanIntersection( withSameNetIntersectingZones );
1787
1788 *aSmoothedPolyWithApron = aSmoothedPoly;
1789 aSmoothedPolyWithApron->BooleanIntersection( poly );
1790 }
1791
1792 aSmoothedPoly.BooleanIntersection( *maxExtents );
1793
1794 return true;
1795}
1796
1797
1799{
1800 m_area = 0.0;
1801
1802 for( const auto& [layer, poly] : m_FilledPolysList )
1803 m_area += poly->Area();
1804
1805 return m_area;
1806}
1807
1808
1810{
1811 m_outlinearea = std::abs( m_Poly->Area() );
1812 return m_outlinearea;
1813}
1814
1815
1817 int aMaxError, ERROR_LOC aErrorLoc,
1818 SHAPE_POLY_SET* aBoardOutline ) const
1819{
1820 // Creates the zone outline polygon (with holes if any)
1821 SHAPE_POLY_SET polybuffer;
1822
1823 // TODO: using GetFirstLayer() means it only works for single-layer zones....
1824 BuildSmoothedPoly( polybuffer, GetFirstLayer(), aBoardOutline );
1825
1826 // Calculate the polygon with clearance
1827 // holes are linked to the main outline, so only one polygon is created.
1828 if( aClearance )
1829 {
1830 if( aErrorLoc == ERROR_OUTSIDE )
1831 aClearance += GetMaxError();
1832
1833 polybuffer.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, GetMaxError() );
1834 }
1835
1836 polybuffer.Fracture();
1837 aBuffer.Append( polybuffer );
1838}
1839
1840
1841std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
1842{
1843 // Rule areas are never filled, so fall back to the outline. DRC relies on this
1844 // to collide tracks, vias and pads against keepout areas.
1845 if( GetIsRuleArea() )
1846 return std::make_shared<SHAPE_POLY_SET>( GetBoardOutline() );
1847
1848 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1849
1850 if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
1851 return std::make_shared<SHAPE_NULL>();
1852 else
1853 return m_FilledPolysList.at( aLayer );
1854}
1855
1856
1857void ZONE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
1858 int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth ) const
1859{
1860 wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for zones." ) );
1861
1862 std::shared_ptr<SHAPE_POLY_SET> fillPolys;
1863
1864 {
1865 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1866
1867 if( !m_FilledPolysList.count( aLayer ) )
1868 return;
1869
1870 fillPolys = m_FilledPolysList.at( aLayer );
1871 }
1872
1873 if( !aClearance )
1874 {
1875 aBuffer.Append( *fillPolys );
1876 return;
1877 }
1878
1879 SHAPE_POLY_SET temp_buf = fillPolys->CloneDropTriangulation();
1880
1881 // Rebuild filled areas only if clearance is not 0
1882 if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
1883 {
1884 if( aErrorLoc == ERROR_OUTSIDE )
1885 aClearance += aError;
1886
1887 temp_buf.InflateWithLinkedHoles( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1888 }
1889
1890 aBuffer.Append( temp_buf );
1891}
1892
1893
1895{
1896 std::lock_guard<std::mutex> lock( m_filledPolysListMutex );
1897
1898 if( m_FilledPolysList.count( aLayer ) && !m_FilledPolysList.at( aLayer )->IsEmpty() )
1899 aBuffer.Append( *m_FilledPolysList.at( aLayer ) );
1900}
1901
1902
1904{
1905 if( aLayerSet.count() == 0 )
1906 return;
1907
1908 std::scoped_lock lock( m_layerSetMutex, m_filledPolysListMutex );
1909
1910 if( m_layerSet != aLayerSet )
1911 {
1912 aLayerSet.RunOnLayers(
1913 [&]( PCB_LAYER_ID layer )
1914 {
1915 // Only keep layers that are present in the new set
1916 if( !aLayerSet.Contains( layer ) )
1917 {
1918 m_FilledPolysList[layer] = std::make_shared<SHAPE_POLY_SET>();
1919 m_filledPolysHash[layer] = {};
1920 m_insulatedIslands[layer] = {};
1921 }
1922 } );
1923 }
1924
1925 m_layerSet = aLayerSet;
1926}
1927
1928
1929bool ZONE::operator==( const BOARD_ITEM& aOther ) const
1930{
1931 if( aOther.Type() != Type() )
1932 return false;
1933
1934 const ZONE& other = static_cast<const ZONE&>( aOther );
1935 return *this == other;
1936}
1937
1938
1939bool ZONE::operator==( const ZONE& aOther ) const
1940
1941{
1942 if( aOther.Type() != Type() )
1943 return false;
1944
1945 const ZONE& other = static_cast<const ZONE&>( aOther );
1946
1947 if( GetIsRuleArea() != other.GetIsRuleArea() )
1948 return false;
1949
1950 if( GetIsRuleArea() )
1951 {
1953 return false;
1954
1955 if( GetDoNotAllowTracks() != other.GetDoNotAllowTracks() )
1956 return false;
1957
1958 if( GetDoNotAllowVias() != other.GetDoNotAllowVias() )
1959 return false;
1960
1962 return false;
1963
1964 if( GetDoNotAllowPads() != other.GetDoNotAllowPads() )
1965 return false;
1966
1968 return false;
1969
1971 return false;
1972
1974 return false;
1975 }
1976 else
1977 {
1978 if( GetAssignedPriority() != other.GetAssignedPriority() )
1979 return false;
1980
1981 if( GetMinThickness() != other.GetMinThickness() )
1982 return false;
1983
1985 return false;
1986
1987 if( GetCornerRadius() != other.GetCornerRadius() )
1988 return false;
1989
1990 if( GetTeardropParams() != other.GetTeardropParams() )
1991 return false;
1992 }
1993
1994 if( GetNumCorners() != other.GetNumCorners() )
1995 return false;
1996
1997 for( int ii = 0; ii < GetNumCorners(); ii++ )
1998 {
1999 if( GetCornerPosition( ii ) != other.GetCornerPosition( ii ) )
2000 return false;
2001 }
2002
2003 return true;
2004}
2005
2006
2007double ZONE::Similarity( const BOARD_ITEM& aOther ) const
2008{
2009 if( aOther.Type() != Type() )
2010 return 0.0;
2011
2012 const ZONE& other = static_cast<const ZONE&>( aOther );
2013
2014 if( GetIsRuleArea() != other.GetIsRuleArea() )
2015 return 0.0;
2016
2017 double similarity = 1.0;
2018
2019 if( GetLayerSet() != other.GetLayerSet() )
2020 similarity *= 0.9;
2021
2022 if( GetNetCode() != other.GetNetCode() )
2023 similarity *= 0.9;
2024
2025 if( !GetIsRuleArea() )
2026 {
2027 if( GetAssignedPriority() != other.GetAssignedPriority() )
2028 similarity *= 0.9;
2029
2030 if( GetMinThickness() != other.GetMinThickness() )
2031 similarity *= 0.9;
2032
2034 similarity *= 0.9;
2035
2036 if( GetCornerRadius() != other.GetCornerRadius() )
2037 similarity *= 0.9;
2038
2039 if( GetTeardropParams() != other.GetTeardropParams() )
2040 similarity *= 0.9;
2041 }
2042 else
2043 {
2045 similarity *= 0.9;
2046 if( GetDoNotAllowTracks() != other.GetDoNotAllowTracks() )
2047 similarity *= 0.9;
2048 if( GetDoNotAllowVias() != other.GetDoNotAllowVias() )
2049 similarity *= 0.9;
2051 similarity *= 0.9;
2052 if( GetDoNotAllowPads() != other.GetDoNotAllowPads() )
2053 similarity *= 0.9;
2054 }
2055
2056 std::vector<VECTOR2I> corners;
2057 std::vector<VECTOR2I> otherCorners;
2058 VECTOR2I lastCorner( 0, 0 );
2059
2060 for( int ii = 0; ii < GetNumCorners(); ii++ )
2061 {
2062 corners.push_back( lastCorner - GetCornerPosition( ii ) );
2063 lastCorner = GetCornerPosition( ii );
2064 }
2065
2066 lastCorner = VECTOR2I( 0, 0 );
2067 for( int ii = 0; ii < other.GetNumCorners(); ii++ )
2068 {
2069 otherCorners.push_back( lastCorner - other.GetCornerPosition( ii ) );
2070 lastCorner = other.GetCornerPosition( ii );
2071 }
2072
2073 size_t longest = alg::longest_common_subset( corners, otherCorners );
2074
2075 similarity *= std::pow( 0.9, GetNumCorners() + other.GetNumCorners() - 2 * longest );
2076
2077 return similarity;
2078}
2079
2080
2081static struct ZONE_DESC
2082{
2084 {
2086
2087 if( layerEnum.Choices().GetCount() == 0 )
2088 {
2089 layerEnum.Undefined( UNDEFINED_LAYER );
2090
2091 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
2092 layerEnum.Map( layer, LSET::Name( layer ) );
2093 }
2094
2096
2097 if( zcMap.Choices().GetCount() == 0 )
2098 {
2100 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
2101 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
2102 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
2103 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
2104 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
2105 }
2106
2108
2109 if( zfmMap.Choices().GetCount() == 0 )
2110 {
2112 zfmMap.Map( ZONE_FILL_MODE::POLYGONS, _HKI( "Solid fill" ) )
2113 .Map( ZONE_FILL_MODE::HATCH_PATTERN, _HKI( "Hatch pattern" ) )
2114 .Map( ZONE_FILL_MODE::COPPER_THIEVING, _HKI( "Copper thieving" ) );
2115 }
2116
2118
2119 if( tpMap.Choices().GetCount() == 0 )
2120 {
2122 tpMap.Map( THIEVING_PATTERN::DOTS, _HKI( "Dots" ) )
2123 .Map( THIEVING_PATTERN::SQUARES, _HKI( "Squares" ) )
2124 .Map( THIEVING_PATTERN::HATCH, _HKI( "Hatch" ) );
2125 }
2126
2128
2129 if( irmMap.Choices().GetCount() == 0 )
2130 {
2132 irmMap.Map( ISLAND_REMOVAL_MODE::ALWAYS, _HKI( "Always" ) )
2133 .Map( ISLAND_REMOVAL_MODE::NEVER, _HKI( "Never" ) )
2134 .Map( ISLAND_REMOVAL_MODE::AREA, _HKI( "Below area limit" ) );
2135 }
2136
2138
2139 if( rapstMap.Choices().GetCount() == 0 )
2140 {
2142 rapstMap.Map( PLACEMENT_SOURCE_T::SHEETNAME, _HKI( "Sheet Name" ) )
2143 .Map( PLACEMENT_SOURCE_T::COMPONENT_CLASS, _HKI( "Component Class" ) )
2145 }
2146
2150
2151 // Mask layer and position properties; they aren't useful in current form
2152 auto posX = new PROPERTY<ZONE, int>( _HKI( "Position X" ), NO_SETTER( ZONE, int ),
2153 static_cast<int ( ZONE::* )() const>( &ZONE::GetX ),
2156 posX->SetIsHiddenFromPropertiesManager();
2157
2158 auto posY = new PROPERTY<ZONE, int>( _HKI( "Position Y" ), NO_SETTER( ZONE, int ),
2159 static_cast<int ( ZONE::* )() const>( &ZONE::GetY ),
2162 posY->SetIsHiddenFromPropertiesManager();
2163
2164 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ), posX );
2165 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ), posY );
2166
2167 auto isCopperZone =
2168 []( INSPECTABLE* aItem ) -> bool
2169 {
2170 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2171 return !zone->GetIsRuleArea() && IsCopperLayer( zone->GetFirstLayer() );
2172
2173 return false;
2174 };
2175
2176 // Hide net-bearing and hatch/island properties for copper-thieving zones.
2177 // Same predicate gates both kinds of properties.
2178 auto isNonThievingCopperZone =
2179 []( INSPECTABLE* aItem ) -> bool
2180 {
2181 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2182 {
2183 return !zone->GetIsRuleArea()
2184 && IsCopperLayer( zone->GetFirstLayer() )
2185 && !zone->IsCopperThieving();
2186 }
2187
2188 return false;
2189 };
2190
2191 auto isRuleArea =
2192 []( INSPECTABLE* aItem ) -> bool
2193 {
2194 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2195 return zone->GetIsRuleArea();
2196
2197 return false;
2198 };
2199
2200 auto isHatchedFill =
2201 []( INSPECTABLE* aItem ) -> bool
2202 {
2203 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2204 return zone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN;
2205
2206 return false;
2207 };
2208
2209 auto isThievingFill =
2210 []( INSPECTABLE* aItem ) -> bool
2211 {
2212 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2213 return zone->IsCopperThieving();
2214
2215 return false;
2216 };
2217
2218 auto isThievingHatch =
2219 []( INSPECTABLE* aItem ) -> bool
2220 {
2221 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2222 {
2223 return zone->IsCopperThieving()
2224 && zone->GetThievingPattern() == THIEVING_PATTERN::HATCH;
2225 }
2226
2227 return false;
2228 };
2229
2230 auto isThievingNonHatch =
2231 []( INSPECTABLE* aItem ) -> bool
2232 {
2233 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2234 {
2235 return zone->IsCopperThieving()
2236 && zone->GetThievingPattern() != THIEVING_PATTERN::HATCH;
2237 }
2238
2239 return false;
2240 };
2241
2242 auto isAreaBasedIslandRemoval =
2243 []( INSPECTABLE* aItem ) -> bool
2244 {
2245 if( ZONE* zone = dynamic_cast<ZONE*>( aItem ) )
2246 return zone->GetIslandRemovalMode() == ISLAND_REMOVAL_MODE::AREA;
2247
2248 return false;
2249 };
2250
2251 // Visible for thieving zones only; ordinary zones use a layer set, not a single layer.
2252 propMgr.ReplaceProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ),
2255 .SetAvailableFunc( isThievingFill );
2256
2258 isNonThievingCopperZone );
2259 propMgr.OverrideAvailability( TYPE_HASH( ZONE ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net Class" ),
2260 isNonThievingCopperZone );
2261
2264 .SetAvailableFunc( isCopperZone );
2265
2266 propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Name" ),
2268
2269 const wxString groupKeepout = _HKI( "Keepout" );
2270
2271 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Tracks" ),
2273 groupKeepout )
2274 .SetAvailableFunc( isRuleArea );
2275
2276 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Vias" ),
2278 groupKeepout )
2279 .SetAvailableFunc( isRuleArea );
2280
2281 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Pads" ),
2283 groupKeepout )
2284 .SetAvailableFunc( isRuleArea );
2285
2286 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Zone Fills" ),
2288 groupKeepout )
2289 .SetAvailableFunc( isRuleArea );
2290
2291 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Keep Out Footprints" ),
2293 groupKeepout )
2294 .SetAvailableFunc( isRuleArea );
2295
2296
2297 const wxString groupPlacement = _HKI( "Placement" );
2298
2299 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Enable" ),
2301 groupPlacement )
2302 .SetAvailableFunc( isRuleArea );
2303
2304 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, PLACEMENT_SOURCE_T>( _HKI( "Source Type" ),
2306 groupPlacement )
2307 .SetAvailableFunc( isRuleArea );
2308
2309 propMgr.AddProperty( new PROPERTY<ZONE, wxString>( _HKI( "Source Name" ),
2311 groupPlacement )
2312 .SetAvailableFunc( isRuleArea );
2313
2314
2315 const wxString groupFill = _HKI( "Fill Style" );
2316
2317 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ZONE_FILL_MODE>( _HKI( "Fill Mode" ),
2319 groupFill )
2320 .SetAvailableFunc( isCopperZone );
2321
2322 propMgr.AddProperty( new PROPERTY<ZONE, EDA_ANGLE>( _HKI( "Hatch Orientation" ),
2325 groupFill )
2326 .SetAvailableFunc( isNonThievingCopperZone )
2327 .SetWriteableFunc( isHatchedFill );
2328
2329 auto atLeastMinWidthValidator =
2330 []( const wxAny&& aValue, EDA_ITEM* aZone ) -> VALIDATOR_RESULT
2331 {
2332 int val = aValue.As<int>();
2333 ZONE* zone = dynamic_cast<ZONE*>( aZone );
2334 wxCHECK( zone, std::nullopt );
2335
2336 if( val < zone->GetMinThickness() )
2337 return std::make_unique<VALIDATION_ERROR_MSG>( _( "Cannot be less than zone minimum width" ) );
2338
2339 return std::nullopt;
2340 };
2341
2342 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Hatch Width" ),
2344 groupFill )
2345 .SetAvailableFunc( isNonThievingCopperZone )
2346 .SetWriteableFunc( isHatchedFill )
2347 .SetValidator( atLeastMinWidthValidator );
2348
2349 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Hatch Gap" ),
2351 groupFill )
2352 .SetAvailableFunc( isNonThievingCopperZone )
2353 .SetWriteableFunc( isHatchedFill )
2354 .SetValidator( atLeastMinWidthValidator );
2355
2356 propMgr.AddProperty( new PROPERTY<ZONE, double>( _HKI( "Hatch Minimum Hole Ratio" ),
2358 groupFill )
2359 .SetAvailableFunc( isNonThievingCopperZone )
2360 .SetWriteableFunc( isHatchedFill )
2362
2363 // TODO: Smoothing effort needs to change to enum (in dialog too)
2364 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Smoothing Effort" ),
2366 groupFill )
2367 .SetAvailableFunc( isNonThievingCopperZone )
2368 .SetWriteableFunc( isHatchedFill );
2369
2370 propMgr.AddProperty( new PROPERTY<ZONE, double>( _HKI( "Smoothing Amount" ),
2372 groupFill )
2373 .SetAvailableFunc( isNonThievingCopperZone )
2374 .SetWriteableFunc( isHatchedFill );
2375
2376 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, THIEVING_PATTERN>( _HKI( "Thieving Pattern" ),
2378 groupFill )
2379 .SetAvailableFunc( isThievingFill );
2380
2381 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Element Size" ),
2384 groupFill )
2385 .SetAvailableFunc( isThievingFill )
2386 .SetWriteableFunc( isThievingNonHatch )
2388
2389 // Gap is meaningful for all three patterns: edge-to-edge spacing between
2390 // adjacent stamps for dots/squares, line-to-line edge spacing for hatch.
2391 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Gap" ),
2393 groupFill )
2394 .SetAvailableFunc( isThievingFill )
2396
2397 propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thieving Line Width" ),
2400 groupFill )
2401 .SetAvailableFunc( isThievingFill )
2402 .SetWriteableFunc( isThievingHatch )
2404
2405 propMgr.AddProperty( new PROPERTY<ZONE, bool>( _HKI( "Thieving Stagger" ),
2407 groupFill )
2408 .SetAvailableFunc( isThievingFill );
2409
2410 propMgr.AddProperty( new PROPERTY<ZONE, EDA_ANGLE>( _HKI( "Thieving Orientation" ),
2413 groupFill )
2414 .SetAvailableFunc( isThievingFill );
2415
2416 propMgr.AddProperty( new PROPERTY_ENUM<ZONE, ISLAND_REMOVAL_MODE>( _HKI( "Remove Islands" ),
2418 groupFill )
2419 .SetAvailableFunc( isNonThievingCopperZone );
2420
2421 propMgr.AddProperty( new PROPERTY<ZONE, long long int>( _HKI( "Minimum Island Area" ),
2423 groupFill )
2424 .SetAvailableFunc( isNonThievingCopperZone )
2425 .SetWriteableFunc( isAreaBasedIslandRemoval );
2426
2427 const wxString groupElectrical = _HKI( "Electrical" );
2428
2429 auto clearance = new PROPERTY<ZONE, std::optional<int>>( _HKI( "Clearance" ),
2431 clearance->SetAvailableFunc( isCopperZone );
2432 constexpr int maxClearance = pcbIUScale.mmToIU( ZONE_CLEARANCE_MAX_VALUE_MM );
2434
2435 auto minWidth = new PROPERTY<ZONE, int>( _HKI( "Minimum Width" ),
2437 minWidth->SetAvailableFunc( isCopperZone );
2438 constexpr int minMinWidth = pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM );
2440
2441 // Pad connections and thermal-relief controls require a net to act on.
2442 // Thieving zones are netless and explicitly use ZONE_CONNECTION::NONE,
2443 // so hide these for them while keeping them visible for solid + hatched zones.
2444 auto padConnections = new PROPERTY_ENUM<ZONE, ZONE_CONNECTION>( _HKI( "Pad Connections" ),
2446 padConnections->SetAvailableFunc( isNonThievingCopperZone );
2447
2448 auto thermalGap = new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Gap" ),
2450 thermalGap->SetAvailableFunc( isNonThievingCopperZone );
2451 thermalGap->SetValidator( PROPERTY_VALIDATORS::PositiveIntValidator );
2452
2453 auto thermalSpokeWidth = new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Spoke Width" ),
2455 thermalSpokeWidth->SetAvailableFunc( isNonThievingCopperZone );
2456 thermalSpokeWidth->SetValidator( atLeastMinWidthValidator );
2457
2458 propMgr.AddProperty( clearance, groupElectrical );
2459 propMgr.AddProperty( minWidth, groupElectrical );
2460 propMgr.AddProperty( padConnections, groupElectrical );
2461 propMgr.AddProperty( thermalGap, groupElectrical );
2462 propMgr.AddProperty( thermalSpokeWidth, groupElectrical );
2463 }
2465
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
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:1677
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:1555
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:851
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition zone.cpp:1387
int m_borderHatchPitch
Definition zone.h:1029
wxString m_placementAreaSource
Definition zone.h:950
bool m_isRuleArea
Definition zone.h:943
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:830
void SetLayerProperties(const std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > &aOther)
Definition zone.cpp:666
ZONE & operator=(const ZONE &aOther)
Definition zone.cpp:101
int m_cornerSmoothingType
Definition zone.h:923
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:811
PLACEMENT_SOURCE_T m_placementAreaSourceType
Definition zone.h:949
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:973
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
bool m_doNotAllowVias
Definition zone.h:962
bool UnFill()
Removes the zone filling.
Definition zone.cpp:487
bool GetDoNotAllowVias() const
Definition zone.h:822
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:1894
void SetCornerRadius(unsigned int aRadius)
Definition zone.cpp:797
ZONE_FILL_MODE m_fillMode
Definition zone.h:993
THIEVING_SETTINGS m_thievingSettings
Definition zone.h:1006
bool m_doNotAllowFootprints
Definition zone.h:965
bool unFillLocked()
Internal implementation of UnFill() that assumes the caller already holds m_filledPolysListMutex.
Definition zone.cpp:495
int m_ZoneMinThickness
Definition zone.h:969
int GetThievingLineWidth() const
Definition zone.h:387
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1350
double m_hatchSmoothingValue
Definition zone.h:1001
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:1816
int m_thermalReliefSpokeWidth
Definition zone.h:991
wxString GetPlacementAreaSource() const
Definition zone.h:816
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:1008
EDA_ANGLE m_hatchOrientation
Definition zone.h:996
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:1305
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:1033
wxString m_zoneName
An optional unique name for this zone, used for identifying it in DRC checking.
Definition zone.h:927
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition zone.cpp:1477
std::map< PCB_LAYER_ID, std::shared_ptr< SHAPE_POLY_SET > > m_FilledPolysList
Definition zone.h:1019
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
Definition zone.cpp:1458
bool GetDoNotAllowPads() const
Definition zone.h:824
const BOX2I GetBoundingBox() const override
Definition zone.cpp:737
void SetMinThickness(int aMinThickness)
Definition zone.h:316
void SetPlacementAreaSource(const wxString &aSource)
Definition zone.h:817
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:692
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:818
double m_outlinearea
Definition zone.h:1036
std::mutex m_filledPolysListMutex
Definition zone.h:1018
void SetThievingPattern(THIEVING_PATTERN aPattern)
Definition zone.h:361
wxString GetFriendlyName() const override
Definition zone.cpp:1208
bool GetDoNotAllowTracks() const
Definition zone.h:823
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:215
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:932
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:861
void SetHatchSmoothingLevel(int aLevel)
Definition zone.h:335
bool m_doNotAllowTracks
Definition zone.h:963
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:532
void SetPlacementAreaSourceType(PLACEMENT_SOURCE_T aType)
Definition zone.h:819
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:599
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:833
LSET m_fillFlags
Definition zone.h:1023
SHAPE_POLY_SET * Outline()
Definition zone.h:418
bool m_doNotAllowPads
Definition zone.h:964
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:229
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:581
void Move(const VECTOR2I &offset) override
Move the outlines.
Definition zone.cpp:1167
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1588
bool IsCopperThieving() const
Definition zone.h:349
SHAPE_POLY_SET * m_Poly
Outline of the zone.
Definition zone.h:922
TEARDROP_TYPE m_teardropType
Definition zone.h:956
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:1026
~ZONE()
Definition zone.cpp:118
long long int GetMinIslandArea() const
Definition zone.h:836
int m_hatchSmoothingLevel
Definition zone.h:997
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:2007
LSET m_layerSet
Definition zone.h:930
void SetIsRuleArea(bool aEnable)
Definition zone.h:812
int m_ZoneClearance
Definition zone.h:968
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:829
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:948
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:774
int GetMinThickness() const
Definition zone.h:315
virtual void swapData(BOARD_ITEM *aImage) override
Definition zone.cpp:1547
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:873
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:835
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition zone.cpp:1321
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition zone.cpp:1244
bool HigherPriority(const ZONE *aOther) const
Definition zone.cpp:467
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:979
void SetIsFilled(bool isFilled)
Definition zone.h:307
std::mutex m_layerSetMutex
Definition zone.h:929
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition zone.cpp:605
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition zone.cpp:1541
int m_hatchGap
Definition zone.h:995
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:624
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:1038
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:730
int m_hatchBorderAlgorithm
Definition zone.h:1003
bool GetPlacementAreaEnabled() const
Definition zone.h:813
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:828
bool IsTeardropArea() const
Definition zone.h:786
std::vector< SEG > m_borderHatchLines
Definition zone.h:1030
VECTOR2I GetPosition() const override
Definition zone.cpp:523
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:590
virtual void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition zone.cpp:1273
void BuildHashValue(PCB_LAYER_ID aLayer)
Build the hash value of m_FilledPolysList, and store it internally in m_filledPolysHash.
Definition zone.cpp:818
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:1638
int m_fillVersion
Definition zone.h:970
const VECTOR2I & GetCornerPosition(int aCornerIndex) const
Definition zone.h:655
bool GetDoNotAllowFootprints() const
Definition zone.h:825
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
double m_hatchHoleMinArea
Definition zone.h:1002
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:831
void SetBorderHatchPitch(int aPitch)
Definition zone.h:847
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:1600
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:1903
int GetHatchGap() const
Definition zone.h:328
double CalculateOutlineArea()
Compute the area of the zone outline (not the filled area).
Definition zone.cpp:1809
int GetThievingGap() const
Definition zone.h:378
void SetHatchHoleMinArea(double aPct)
Definition zone.h:341
unsigned m_priority
Definition zone.h:938
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition zone.cpp:341
bool IsConflicting() const
For rule areas which exclude footprints (and therefore participate in courtyard conflicts during move...
Definition zone.cpp:517
bool m_doNotAllowZoneFills
Definition zone.h:961
ISLAND_REMOVAL_MODE m_islandRemovalMode
Definition zone.h:972
std::vector< SEG > GetHatchLines() const
Definition zone.cpp:1517
bool m_isFilled
True when a zone was filled, false after deleting the filled areas.
Definition zone.h:981
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:1367
std::atomic< bool > m_needRefill
False when a zone was refilled, true after changes in zone params.
Definition zone.h:988
THIEVING_PATTERN GetThievingPattern() const
Definition zone.h:360
bool GetDoNotAllowZoneFills() const
Definition zone.h:821
void MoveEdge(const VECTOR2I &offset, int aEdge)
Move the outline Edge.
Definition zone.cpp:1221
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:990
int GetThievingElementSize() const
Definition zone.h:369
SHAPE_POLY_SET GetLibraryOutline() const
Definition zone.cpp:829
bool IsOnCopperLayer() const override
Definition zone.cpp:574
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
Definition zone.cpp:1798
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:827
int m_hatchThickness
Definition zone.h:994
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:1260
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:1857
void SetAssignedPriority(unsigned aPriority)
Definition zone.h:117
double m_area
Definition zone.h:1035
unsigned int m_cornerRadius
Definition zone.h:924
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:1939
void UnHatchBorder()
Clear the zone's hatch.
Definition zone.cpp:1471
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition zone.h:834
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:554
void SetMinIslandArea(long long int aArea)
Definition zone.h:837
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition zone.cpp:672
HASH_128 GetHashValue(PCB_LAYER_ID aLayer)
Definition zone.cpp:809
ZONE_CONNECTION m_PadConnection
Definition zone.h:967
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:1535
void SetPlacementAreaEnabled(bool aEnabled)
Definition zone.h:814
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:481
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:1841
ZONE_BORDER_DISPLAY_STYLE m_borderStyle
Definition zone.h:1028
long long int m_minIslandArea
When island removal mode is set to AREA, islands below this area will be removed.
Definition zone.h:978
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:2083
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:806
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