diff --git a/include/board_design_settings.h b/include/board_design_settings.h index dafc8c70c7..298861f5f8 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -244,12 +244,19 @@ public: std::map m_DRCSeverities; // Map from DRCErrorCode to SEVERITY std::set m_DrcExclusions; - // Option to handle filled polygons in zones: - // the "legacy" option is using thick outlines around filled polygons: give the best shape - // the "new" option is using only filled polygons (no outline: give the faster redraw time - // moreover when exporting zone filled areas, the excatct shape is exported. - // the legacy option can really create redraw time issues for large boards. - bool m_ZoneUseNoOutlineInFill; + /* + * Option to select different fill algorithms. + * There are currenly two supported values: + * 5: + * - Use thick outlines around filled polygons (gives smoothest shape but at the expense + * of processing time and slight infidelity when exporting) + * - Use zone outline when knocking out higher-priority zones (just wrong, but mimics + * legacy behaviour. + * 6: + * - No thick outline. + * - Use filled areas when knocking out higher-priority zones. + */ + int m_ZoneFillVersion; // When smoothing the zone's outline there's the question of external fillets (that is, those // applied to concave corners). While it seems safer to never have copper extend outside the diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index e5a8f6c820..15e2920eb5 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -162,7 +162,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std: m_DRCSeverities[ DRCE_NET_CONFLICT ] = RPT_SEVERITY_WARNING; m_MaxError = ARC_HIGH_DEF; - m_ZoneUseNoOutlineInFill = true; // Use new algo by default to fill zones + m_ZoneFillVersion = 6; // Use new algo by default to fill zones m_ZoneKeepExternalFillets = false; // Use new algo by default. Legacy boards might // want to set it to true for old algo.... @@ -584,8 +584,17 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std: m_params.emplace_back( new PARAM_SCALED( "rules.max_error", &m_MaxError, ARC_HIGH_DEF, Millimeter2iu( 0.0001 ), Millimeter2iu( 1.0 ), MM_PER_IU ) ); - m_params.emplace_back( new PARAM( "zones_use_no_outline", - &m_ZoneUseNoOutlineInFill, true ) ); + // TODO: replace with zones_fill_version parameter and migrate zones_use_no_outline? + m_params.emplace_back( new PARAM_LAMBDA( "zones_use_no_outline", + [this]() -> bool + { + return m_ZoneFillVersion >= 6; + }, + [this]( bool aVal ) + { + m_ZoneFillVersion = aVal ? 6 : 5; + }, + 6 ) ); m_params.emplace_back( new PARAM( "zones_allow_external_fillets", &m_ZoneKeepExternalFillets, false ) ); @@ -639,7 +648,7 @@ void BOARD_DESIGN_SETTINGS::initFromOther( const BOARD_DESIGN_SETTINGS& aOther ) m_HoleToHoleMin = aOther.m_HoleToHoleMin; m_DRCSeverities = aOther.m_DRCSeverities; m_DrcExclusions = aOther.m_DrcExclusions; - m_ZoneUseNoOutlineInFill = aOther.m_ZoneUseNoOutlineInFill; + m_ZoneFillVersion = aOther.m_ZoneFillVersion; m_ZoneKeepExternalFillets= aOther.m_ZoneKeepExternalFillets; m_MaxError = aOther.m_MaxError; m_SolderMaskMargin = aOther.m_SolderMaskMargin; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 571cf0f20f..60a7747e03 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -43,8 +43,8 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent, bool aInModule ) m_area( 0.0 ) { m_CornerSelection = nullptr; // no corner is selected - m_IsFilled = false; // fill status : true when the zone is filled - m_FillMode = ZONE_FILL_MODE::POLYGONS; + m_isFilled = false; // fill status : true when the zone is filled + m_fillMode = ZONE_FILL_MODE::POLYGONS; m_borderStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; m_borderHatchPitch = GetDefaultHatchPitch(); m_hv45 = false; @@ -67,7 +67,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent, bool aInModule ) m_cornerRadius = 0; SetLocalFlags( 0 ); // flags tempoarry used in zone calculations m_Poly = new SHAPE_POLY_SET(); // Outlines - m_FilledPolysUseThickness = true; // set the "old" way to build filled polygon areas (before 6.0.x) + m_fillVersion = 6; // set the "old" way to build filled polygon areas (before 6.0.x) m_islandRemovalMode = ISLAND_REMOVAL_MODE::ALWAYS; aParent->GetZoneSettings().ExportSetting( *this ); @@ -131,17 +131,17 @@ void ZONE_CONTAINER::InitDataFromSrcInCopyCtor( const ZONE_CONTAINER& aZone ) m_PadConnection = aZone.m_PadConnection; m_ZoneClearance = aZone.m_ZoneClearance; // clearance value m_ZoneMinThickness = aZone.m_ZoneMinThickness; - m_FilledPolysUseThickness = aZone.m_FilledPolysUseThickness; + m_fillVersion = aZone.m_fillVersion; m_islandRemovalMode = aZone.m_islandRemovalMode; m_minIslandArea = aZone.m_minIslandArea; - m_IsFilled = aZone.m_IsFilled; - m_needRefill = aZone.m_needRefill; + m_isFilled = aZone.m_isFilled; + m_needRefill = aZone.m_needRefill; - m_ThermalReliefGap = aZone.m_ThermalReliefGap; - m_ThermalReliefCopperBridge = aZone.m_ThermalReliefCopperBridge; + m_thermalReliefGap = aZone.m_thermalReliefGap; + m_thermalReliefSpokeWidth = aZone.m_thermalReliefSpokeWidth; - m_FillMode = aZone.m_FillMode; // solid vs. hatched + m_fillMode = aZone.m_fillMode; // solid vs. hatched m_hatchThickness = aZone.m_hatchThickness; m_hatchGap = aZone.m_hatchGap; m_hatchOrientation = aZone.m_hatchOrientation; @@ -198,7 +198,8 @@ bool ZONE_CONTAINER::UnFill() pair.second.clear(); } - m_IsFilled = false; + m_isFilled = false; + m_fillFlags.clear(); return change; } @@ -331,7 +332,7 @@ int ZONE_CONTAINER::GetThermalReliefGap( D_PAD* aPad, wxString* aSource ) const if( aSource ) *aSource = _( "zone" ); - return m_ThermalReliefGap; + return m_thermalReliefGap; } return aPad->GetEffectiveThermalGap( aSource ); @@ -339,14 +340,14 @@ int ZONE_CONTAINER::GetThermalReliefGap( D_PAD* aPad, wxString* aSource ) const } -int ZONE_CONTAINER::GetThermalReliefCopperBridge( D_PAD* aPad, wxString* aSource ) const +int ZONE_CONTAINER::GetThermalReliefSpokeWidth( D_PAD* aPad, wxString* aSource ) const { if( aPad->GetEffectiveThermalSpokeWidth() == 0 ) { if( aSource ) *aSource = _( "zone" ); - return m_ThermalReliefCopperBridge; + return m_thermalReliefSpokeWidth; } return aPad->GetEffectiveThermalSpokeWidth( aSource ); @@ -600,7 +601,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector( _( "Thermal Spoke Width" ), - &ZONE_CONTAINER::SetThermalReliefCopperBridge, &ZONE_CONTAINER::GetThermalReliefCopperBridge, - PROPERTY_DISPLAY::DISTANCE ) ); + &ZONE_CONTAINER::SetThermalReliefSpokeWidth, &ZONE_CONTAINER::GetThermalReliefSpokeWidth, + PROPERTY_DISPLAY::DISTANCE ) ); } } _ZONE_CONTAINER_DESC; diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index b3661d4a4a..d86cdcf13a 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -152,28 +152,28 @@ public: unsigned int ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override; - void SetFillMode( ZONE_FILL_MODE aFillMode ) { m_FillMode = aFillMode; } - ZONE_FILL_MODE GetFillMode() const { return m_FillMode; } + void SetFillMode( ZONE_FILL_MODE aFillMode ) { m_fillMode = aFillMode; } + ZONE_FILL_MODE GetFillMode() const { return m_fillMode; } void SetThermalReliefGap( int aThermalReliefGap ) { - if( m_ThermalReliefGap != aThermalReliefGap ) + if( m_thermalReliefGap != aThermalReliefGap ) SetNeedRefill( true ); - m_ThermalReliefGap = aThermalReliefGap; + m_thermalReliefGap = aThermalReliefGap; } - int GetThermalReliefGap() const { return m_ThermalReliefGap; } + int GetThermalReliefGap() const { return m_thermalReliefGap; } int GetThermalReliefGap( D_PAD* aPad, wxString* aSource = nullptr ) const; - void SetThermalReliefCopperBridge( int aThermalReliefCopperBridge ) + void SetThermalReliefSpokeWidth( int aThermalReliefSpokeWidth ) { - if( m_ThermalReliefCopperBridge != aThermalReliefCopperBridge ) + if( m_thermalReliefSpokeWidth != aThermalReliefSpokeWidth ) SetNeedRefill( true ); - m_ThermalReliefCopperBridge = aThermalReliefCopperBridge; + m_thermalReliefSpokeWidth = aThermalReliefSpokeWidth; } - int GetThermalReliefCopperBridge() const { return m_ThermalReliefCopperBridge; } - int GetThermalReliefCopperBridge( D_PAD* aPad, wxString* aSource = nullptr ) const; + int GetThermalReliefSpokeWidth() const { return m_thermalReliefSpokeWidth; } + int GetThermalReliefSpokeWidth( D_PAD* aPad, wxString* aSource = nullptr ) const; /** * Compute the area currently occupied by the zone fill. @@ -198,8 +198,14 @@ public: return m_lock; } - bool IsFilled() const { return m_IsFilled; } - void SetIsFilled( bool isFilled ) { m_IsFilled = isFilled; } + int GetFillFlag( PCB_LAYER_ID aLayer ) + { + return m_fillFlags.count( aLayer ) ? m_fillFlags[ aLayer ] : false; + } + void SetFillFlag( PCB_LAYER_ID aLayer, bool aFlag ) { m_fillFlags[ aLayer ] = aFlag; } + + bool IsFilled() const { return m_isFilled; } + void SetIsFilled( bool isFilled ) { m_isFilled = isFilled; } bool NeedRefill() const { return m_needRefill; } void SetNeedRefill( bool aNeedRefill ) { m_needRefill = aNeedRefill; } @@ -348,17 +354,17 @@ public: int aError = ARC_HIGH_DEF ) const; /** - * Function TransformOutlinesShapeWithClearanceToPolygon + * Function TransformSmoothedOutlineWithClearanceToPolygon * Convert the outlines shape to a polygon with no holes * inflated (optional) by max( aClearanceValue, the zone clearance) * (holes are linked to external outline by overlapping segments) * Used in filling zones calculations * Circles (vias) and arcs (ends of tracks) are approximated by segments * @param aCornerBuffer = a buffer to store the polygon - * @param aMinClearanceValue = the min clearance around outlines + * @param aClearance = the min clearance around outlines */ - void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, - int aMinClearanceValue ) const; + void TransformSmoothedOutlineWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, + int aClearance ) const; /** * Function TransformShapeWithClearanceToPolygon @@ -666,9 +672,10 @@ public: unsigned int GetCornerRadius() const { return m_cornerRadius; } - bool GetFilledPolysUseThickness() const { return m_FilledPolysUseThickness; } - void SetFilledPolysUseThickness( bool aOption ) { m_FilledPolysUseThickness = aOption; } + bool GetFilledPolysUseThickness() const { return m_fillVersion == 5; } + int GetFillVersion() const { return m_fillVersion; } + void SetFillVersion( int aVersion ) { m_fillVersion = aVersion; } /** * Remove a cutout from the zone. @@ -842,39 +849,36 @@ protected: bool m_doNotAllowFootprints; ZONE_CONNECTION m_PadConnection; - int m_ZoneClearance; ///< Clearance value in internal units. - int m_ZoneMinThickness; ///< Minimum thickness value in filled areas. - bool m_FilledPolysUseThickness; ///< outline of filled polygons have thickness. - + int m_ZoneClearance; // Clearance value in internal units. + int m_ZoneMinThickness; // Minimum thickness value in filled areas. + int m_fillVersion; // See BOARD_DESIGN_SETTINGS for version + // differences. ISLAND_REMOVAL_MODE m_islandRemovalMode; /** * When island removal mode is set to AREA, islands below this area will be removed. * If this value is negative, all islands will be removed. */ - long long int m_minIslandArea; + long long int m_minIslandArea; /** True when a zone was filled, false after deleting the filled areas. */ - bool m_IsFilled; + bool m_isFilled; /** False when a zone was refilled, true after changes in zone params. * m_needRefill = false does not imply filled areas are up to date, just * the zone was refilled after edition, and does not need refilling */ - bool m_needRefill; + bool m_needRefill; - ///< Width of the gap in thermal reliefs. - int m_ThermalReliefGap; - - ///< Width of the copper bridge in thermal reliefs. - int m_ThermalReliefCopperBridge; + int m_thermalReliefGap; // Width of the gap in thermal reliefs. + int m_thermalReliefSpokeWidth; // Width of the copper bridge in thermal reliefs. /** How to fill areas: * ZONE_FILL_MODE::POLYGONS => use solid polygons * ZONE_FILL_MODE::HATCH_PATTERN => use a grid pattern as shape */ - ZONE_FILL_MODE m_FillMode; + ZONE_FILL_MODE m_fillMode; int m_hatchThickness; // thickness of lines (if 0 -> solid shape) int m_hatchGap; // gap between lines (0 -> solid shape double m_hatchOrientation; // orientation in degrees of grid lines @@ -890,8 +894,7 @@ protected: /// The index of the corner being moved or nullptr if no corner is selected. SHAPE_POLY_SET::VERTEX_INDEX* m_CornerSelection; - /// Variable used in polygon calculations. - int m_localFlgs; + int m_localFlgs; // Variable used in polygon calculations. /** Segments used to fill the zone (#m_FillMode ==1 ), when fill zone by segment is used. * In this case the segments have #m_ZoneMinThickness width. @@ -909,6 +912,9 @@ protected: std::map m_FilledPolysList; std::map m_RawPolysList; + /// A temp variable used while filling + std::map m_fillFlags; + /// A hash value used in zone filling calculations to see if the filled areas are up to date std::map m_filledPolysHash; diff --git a/pcbnew/dialogs/dialog_copper_zones.cpp b/pcbnew/dialogs/dialog_copper_zones.cpp index 0b548664ee..ec48ba3340 100644 --- a/pcbnew/dialogs/dialog_copper_zones.cpp +++ b/pcbnew/dialogs/dialog_copper_zones.cpp @@ -177,7 +177,7 @@ bool DIALOG_COPPER_ZONE::TransferDataToWindow() // Do not enable/disable antipad clearance and spoke width. They might be needed if // a module or pad overrides the zone to specify a thermal connection. m_antipadClearance.SetValue( m_settings.m_ThermalReliefGap ); - m_spokeWidth.SetValue( m_settings.m_ThermalReliefCopperBridge ); + m_spokeWidth.SetValue( m_settings.m_ThermalReliefSpokeWidth ); m_islandThreshold.SetDataType( EDA_DATA_TYPE::AREA ); m_islandThreshold.SetDoubleValue( static_cast( m_settings.GetMinIslandArea() ) ); @@ -395,9 +395,9 @@ bool DIALOG_COPPER_ZONE::AcceptOptions( bool aUseExportableSetupOnly ) m_settings.m_Zone_45_Only = m_constrainOutline->GetValue(); m_settings.m_ThermalReliefGap = m_antipadClearance.GetValue(); - m_settings.m_ThermalReliefCopperBridge = m_spokeWidth.GetValue(); + m_settings.m_ThermalReliefSpokeWidth = m_spokeWidth.GetValue(); - if( m_settings.m_ThermalReliefCopperBridge < m_settings.m_ZoneMinThickness ) + if( m_settings.m_ThermalReliefSpokeWidth < m_settings.m_ZoneMinThickness ) { DisplayError( this, _( "Thermal spoke width cannot be smaller than the minimum width." ) ); return false; @@ -406,7 +406,7 @@ bool DIALOG_COPPER_ZONE::AcceptOptions( bool aUseExportableSetupOnly ) cfg->m_Zones.clearance = Iu2Mils( m_settings.m_ZoneClearance ); cfg->m_Zones.min_thickness = Iu2Mils( m_settings.m_ZoneMinThickness ); cfg->m_Zones.thermal_relief_gap = Iu2Mils( m_settings.m_ThermalReliefGap ); - cfg->m_Zones.thermal_relief_copper_width = Iu2Mils( m_settings.m_ThermalReliefCopperBridge ); + cfg->m_Zones.thermal_relief_copper_width = Iu2Mils( m_settings.m_ThermalReliefSpokeWidth ); m_settings.SetIslandRemovalMode( static_cast( m_cbRemoveIslands->GetSelection() ) ); diff --git a/pcbnew/dialogs/panel_setup_feature_constraints.cpp b/pcbnew/dialogs/panel_setup_feature_constraints.cpp index 6a8e05593c..e556c469f5 100644 --- a/pcbnew/dialogs/panel_setup_feature_constraints.cpp +++ b/pcbnew/dialogs/panel_setup_feature_constraints.cpp @@ -64,8 +64,8 @@ bool PANEL_SETUP_FEATURE_CONSTRAINTS::TransferDataToWindow() m_maxError.SetValue( m_BrdSettings->m_MaxError ); - m_rbOutlinePolygonFastest->SetValue( m_BrdSettings->m_ZoneUseNoOutlineInFill ); - m_rbOutlinePolygonBestQ->SetValue( !m_BrdSettings->m_ZoneUseNoOutlineInFill ); + m_rbOutlinePolygonFastest->SetValue( m_BrdSettings->m_ZoneFillVersion == 6 ); + m_rbOutlinePolygonBestQ->SetValue( m_BrdSettings->m_ZoneFillVersion == 5 ); m_allowExternalFilletsOpt->SetValue( m_BrdSettings->m_ZoneKeepExternalFillets ); m_minClearance.SetValue( m_BrdSettings->m_MinClearance ); @@ -113,7 +113,7 @@ bool PANEL_SETUP_FEATURE_CONSTRAINTS::TransferDataFromWindow() m_BrdSettings->m_MaxError = Clamp( IU_PER_MM * MINIMUM_ERROR_SIZE_MM, m_maxError.GetValue(), IU_PER_MM * MAXIMUM_ERROR_SIZE_MM ); - m_BrdSettings->m_ZoneUseNoOutlineInFill = m_rbOutlinePolygonFastest->GetValue(); + m_BrdSettings->m_ZoneFillVersion = m_rbOutlinePolygonFastest->GetValue() ? 6 : 5; m_BrdSettings->m_ZoneKeepExternalFillets = m_allowExternalFilletsOpt->GetValue(); m_BrdSettings->m_MinClearance = m_minClearance.GetValue(); diff --git a/pcbnew/dialogs/panel_setup_feature_constraints_base.cpp b/pcbnew/dialogs/panel_setup_feature_constraints_base.cpp index 67f7ee317d..330613ee00 100644 --- a/pcbnew/dialogs/panel_setup_feature_constraints_base.cpp +++ b/pcbnew/dialogs/panel_setup_feature_constraints_base.cpp @@ -87,7 +87,7 @@ PANEL_SETUP_FEATURE_CONSTRAINTS_BASE::PANEL_SETUP_FEATURE_CONSTRAINTS_BASE( wxWi m_bitmapInfo = new wxStaticBitmap( this, wxID_ANY, wxArtProvider::GetBitmap( wxART_INFORMATION, wxART_OTHER ), wxDefaultPosition, wxDefaultSize, 0 ); bSizer8->Add( m_bitmapInfo, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - m_stCircleToPolyWarning = new wxStaticText( this, wxID_ANY, _("Value < %s can be time consumming\nwhen filling zones."), wxDefaultPosition, wxDefaultSize, 0 ); + m_stCircleToPolyWarning = new wxStaticText( this, wxID_ANY, _("Value < %s can be time consuming when \nfilling zones."), wxDefaultPosition, wxDefaultSize, 0 ); m_stCircleToPolyWarning->Wrap( -1 ); m_stCircleToPolyWarning->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) ); @@ -119,11 +119,15 @@ PANEL_SETUP_FEATURE_CONSTRAINTS_BASE::PANEL_SETUP_FEATURE_CONSTRAINTS_BASE( wxWi wxBoxSizer* bSizerOutlinesOpts; bSizerOutlinesOpts = new wxBoxSizer( wxVERTICAL ); - m_rbOutlinePolygonBestQ = new wxRadioButton( this, wxID_ANY, _("Stroked outlines (legacy)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbOutlinePolygonBestQ = new wxRadioButton( this, wxID_ANY, _("Mimic legacy behavior"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbOutlinePolygonBestQ->SetToolTip( _("Produces a slightly smoother outline at the expense of performance, some export fidelity issues, and overly agressive higher-priority zone knockouts.") ); + bSizerOutlinesOpts->Add( m_rbOutlinePolygonBestQ, 0, wxALL, 4 ); m_rbOutlinePolygonFastest = new wxRadioButton( this, wxID_ANY, _("Smoothed polygons (best performance)"), wxDefaultPosition, wxDefaultSize, 0 ); m_rbOutlinePolygonFastest->SetValue( true ); + m_rbOutlinePolygonFastest->SetToolTip( _("Better performance, exact export fidelity, and more complete filling near higher-priority zones.") ); + bSizerOutlinesOpts->Add( m_rbOutlinePolygonFastest, 0, wxALL, 4 ); diff --git a/pcbnew/dialogs/panel_setup_feature_constraints_base.fbp b/pcbnew/dialogs/panel_setup_feature_constraints_base.fbp index 8507fcf370..fd88335024 100644 --- a/pcbnew/dialogs/panel_setup_feature_constraints_base.fbp +++ b/pcbnew/dialogs/panel_setup_feature_constraints_base.fbp @@ -837,7 +837,7 @@ 0 0 wxID_ANY - Value < %s can be time consumming when filling zones. + Value < %s can be time consuming when filling zones. 0 0 @@ -1106,7 +1106,7 @@ 0 0 wxID_ANY - Stroked outlines (legacy) + Mimic legacy behavior 0 @@ -1127,7 +1127,7 @@ ; ; forward_declare 0 - + Produces a slightly smoother outline at the expense of performance, some export fidelity issues, and overly agressive higher-priority zone knockouts. wxFILTER_NONE wxDefaultValidator @@ -1192,7 +1192,7 @@ ; ; forward_declare 0 - + Better performance, exact export fidelity, and more complete filling near higher-priority zones. wxFILTER_NONE wxDefaultValidator diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index e172acce62..fe3832bad5 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -1357,7 +1357,7 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode ) // (i.e. width of spoke is half of the smaller side of an smd pad) // This is a basic workaround zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm - zone->SetThermalReliefCopperBridge( p.width.ToPcbUnits() + 50000 ); + zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 ); } int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority; diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 87b05f3bf7..231c1bccf7 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -1776,8 +1776,8 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( aNestLevel+1, "(min_thickness %s)", FormatInternalUnits( aZone->GetMinThickness() ).c_str() ); - // write it only if V 6.O version option is not used (i.e. do not write if the - // "legacy" algorithm is used) + // write it only if V 6.O version option is used (i.e. do not write if the "legacy" + // algorithm is used) if( !aZone->GetFilledPolysUseThickness() ) m_out->Print( 0, " (filled_areas_thickness no)" ); @@ -1805,7 +1805,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const m_out->Print( 0, " (thermal_gap %s) (thermal_bridge_width %s)", FormatInternalUnits( aZone->GetThermalReliefGap() ).c_str(), - FormatInternalUnits( aZone->GetThermalReliefCopperBridge() ).c_str() ); + FormatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() ); if( aZone->GetCornerSmoothingType() != ZONE_SETTINGS::SMOOTHING_NONE ) { diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 67ea887ff5..3432b99bbc 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -2610,7 +2610,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER() zc->SetIsFilled( fillstate == 'S' ); zc->SetThermalReliefGap( thermalReliefGap ); - zc->SetThermalReliefCopperBridge( thermalReliefCopperBridge ); + zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge ); } else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 4f41d580d5..1b30727f7a 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -1821,8 +1821,8 @@ void PCB_PARSER::parseSetup() NeedRIGHT(); break; - case T_filled_areas_thickness: - designSettings.m_ZoneUseNoOutlineInFill = not parseBool(); + case T_filled_areas_thickness: // Note: legacy (early 5.99) token + designSettings.m_ZoneFillVersion = parseBool() ? 5 : 6; m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; @@ -4353,7 +4353,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent ) break; case T_filled_areas_thickness: - zone->SetFilledPolysUseThickness( parseBool() ); + zone->SetFillVersion( parseBool() ? 5 : 6 ); NeedRIGHT(); break; @@ -4454,8 +4454,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent ) NeedRIGHT(); break; - case T_thermal_bridge_width: - zone->SetThermalReliefCopperBridge( parseBoardUnits( T_thermal_bridge_width ) ); + case T_thermal_bridge_width:zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width )); NeedRIGHT(); break; diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 0ed5e8848b..8a77a07ad4 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -492,6 +492,8 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, for( ZONE_CONTAINER* zone : aBoard->Zones() ) { + int outlineThickness = zone->GetFilledPolysUseThickness() ? zone->GetMinThickness() : 0; + for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() ) { auto pair = std::make_pair( layer, zone ); @@ -529,17 +531,12 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, if( candidate->GetNetCode() != zone->GetNetCode() ) continue; - // Merging zones of the same net can be done only for areas - // having compatible settings for drawings: - // use or not outline thickness, and if using outline thickness, - // having the same thickness - // because after merging only one outline thickness is used - if( candidate->GetFilledPolysUseThickness() != zone->GetFilledPolysUseThickness() ) - // Should not happens, because usually the same option is used for filling - continue; + // Merging zones of the same net can be done only for areas having compatible + // settings for filling as the merged zone can only have a single setting. + int candidateOutlineThickness = candidate->GetFilledPolysUseThickness() ? + candidate->GetMinThickness() : 0; - if( zone->GetFilledPolysUseThickness() - && ( candidate->GetMinThickness() != zone->GetMinThickness() ) ) + if( candidateOutlineThickness != outlineThickness ) continue; plotted.insert( candidate_pair ); @@ -867,9 +864,9 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, continue; // add shapes inflated by aMinThickness/2 in areas - zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin ); + zone->TransformSmoothedOutlineWithClearanceToPolygon( areas, inflate + zone_margin ); // add shapes with their exact mask layer size in initialPolys - zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin ); + zone->TransformSmoothedOutlineWithClearanceToPolygon( initialPolys, zone_margin ); } int maxError = aBoard->GetDesignSettings().m_MaxError; diff --git a/pcbnew/plugins/altium/altium_pcb.cpp b/pcbnew/plugins/altium/altium_pcb.cpp index 76f31b3d81..497fac92b9 100644 --- a/pcbnew/plugins/altium/altium_pcb.cpp +++ b/pcbnew/plugins/altium/altium_pcb.cpp @@ -1311,7 +1311,7 @@ void ALTIUM_PCB::ParsePolygons6Data( } // TODO: correct variables? - zone->SetThermalReliefCopperBridge( + zone->SetThermalReliefSpokeWidth( polygonConnectRule->polygonconnectReliefconductorwidth ); zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth ); diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp index b6b8fc8a7b..1e225772fb 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp @@ -1098,8 +1098,8 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadTemplates() if( csTemplate.Pouring.ThermalReliefOnPads ) { zone->SetThermalReliefGap( getKiCadLength( csTemplate.Pouring.ClearanceWidth ) ); - zone->SetThermalReliefCopperBridge( getKiCadLength( - getCopperCode( csTemplate.Pouring.ReliefCopperCodeID ).CopperWidth ) ); + zone->SetThermalReliefSpokeWidth( getKiCadLength( + getCopperCode( csTemplate.Pouring.ReliefCopperCodeID ).CopperWidth )); zone->SetPadConnection( ZONE_CONNECTION::THERMAL ); } else diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 4ff459e161..a1a145cdd5 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -88,11 +88,12 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck, std::vector> toFill; std::vector islandsList; - auto connectivity = m_board->GetConnectivity(); - bool filledPolyWithOutline = not m_board->GetDesignSettings().m_ZoneUseNoOutlineInFill; - + std::shared_ptr connectivity = m_board->GetConnectivity(); std::unique_lock lock( connectivity->GetLock(), std::try_to_lock ); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + int worstClearance = bds.GetBiggestClearanceValue(); + if( !lock ) return false; @@ -141,25 +142,90 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck, // Remove existing fill first to prevent drawing invalid polygons // on some platforms zone->UnFill(); + + zone->SetFillVersion( bds.m_ZoneFillVersion ); } std::atomic nextItem( 0 ); size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), - aZones.size() ); + toFill.size() ); std::vector> returns( parallelThreadCount ); auto fill_lambda = [&]( PROGRESS_REPORTER* aReporter ) -> size_t { - size_t num = 0; + std::deque> deferred; - for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ ) + size_t num = 0; + PCB_LAYER_ID layer = UNDEFINED_LAYER; + ZONE_CONTAINER* zone = nullptr; + + while( true ) { - PCB_LAYER_ID layer = toFill[i].second; - ZONE_CONTAINER* zone = toFill[i].first; + size_t i = nextItem++; - zone->SetFilledPolysUseThickness( filledPolyWithOutline ); + if( i < toFill.size() ) + { + layer = toFill[i].second; + zone = toFill[i].first; + } + else if( !deferred.empty() ) + { + layer = deferred.front().second; + zone = deferred.front().first; + deferred.pop_front(); + } + else + { + // all done + break; + } + EDA_RECT zone_boundingbox = zone->GetBoundingBox(); + bool canFill = true; + + zone_boundingbox.Inflate( worstClearance ); + + // Check for any fill dependencies. If our zone needs to be clipped by + // another zone then we can't fill until that zone is filled. + for( ZONE_CONTAINER* candidate : m_board->GetZoneList( true ) ) + { + // We don't care about keepouts; even if they exlclude copper pours + // the exclusion is by outline, not by filled area. + if( candidate->GetIsKeepout() ) + continue; + + // If the zones share no common layers + if( !candidate->GetLayerSet().test( layer ) ) + continue; + + if( candidate->GetPriority() <= zone->GetPriority() ) + continue; + + // Same-net zones always use outline to produce predictable results + if( candidate->GetNetCode() == zone->GetNetCode() ) + continue; + + // A higher priority zone is found: if we intersect and it's not + // filled yet then we have to wait. + if( zone_boundingbox.Intersects( candidate->GetBoundingBox() ) + && !candidate->GetFillFlag( layer ) ) + { + canFill = false; + break; + } + } + + if( !canFill ) + { + // This isn't ideal from a load-balancing standpoint as another thread + // cannot pick up this thread's deferred zones. However a better + // solution would require a significantly more complex thread-safe queue. + deferred.push_back( { zone, layer } ); + continue; + } + + // Now we're ready to fill. SHAPE_POLY_SET rawPolys, finalPolys; fillSingleZone( zone, layer, rawPolys, finalPolys ); @@ -167,7 +233,7 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck, zone->SetRawPolysList( layer, rawPolys ); zone->SetFilledPolysList( layer, finalPolys ); - zone->SetIsFilled( true ); + zone->SetFillFlag( layer, true ); if( m_progressReporter ) { @@ -222,6 +288,15 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck, if( m_progressReporter && m_progressReporter->IsCancelled() ) return false; + for( ZONE_CONTAINER* zone : aZones ) + { + // Keepout zones are not filled + if( zone->GetIsKeepout() ) + continue; + + zone->SetIsFilled( true ); + } + // Re-add not connected zones to list, connectivity->FindIsolatedCopperIslands() // populates islandsList only with zones having a net for( ZONE_CONTAINER* zone : aZones ) @@ -748,7 +823,6 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA // for( ZONE_CONTAINER* zone : m_board->GetZoneList( true ) ) { - // If the zones share no common layers if( !zone->GetLayerSet().test( aLayer ) ) continue; @@ -764,15 +838,28 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA if( item_boundingbox.Intersects( zone_boundingbox ) ) { - // Add the zone outline area. Don't use any clearance for keepouts, or for zones - // with the same net (they will be connected but will honor their own clearance, - // thermal connections, etc.). - int gap = 0; + if( zone->GetIsKeepout() || aZone->GetNetCode() == zone->GetNetCode() ) + { + // Keepouts and same-net zones use outline with no clearance + zone->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, 0 ); + } + else + { + int gap = aZone->GetClearance( aLayer, zone ); - if( !zone->GetIsKeepout() && aZone->GetNetCode() != zone->GetNetCode() ) - gap = aZone->GetClearance( aLayer, zone ); - - zone->TransformOutlinesShapeWithClearanceToPolygon( aHoles, gap ); + if( bds.m_ZoneFillVersion == 5 ) + { + // 5.x used outline with clearance + zone->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, gap ); + } + else + { + // 6.0 uses filled areas with clearance + SHAPE_POLY_SET knockout; + zone->TransformShapeWithClearanceToPolygon( knockout, aLayer, gap ); + aHoles.Append( knockout ); + } + } } } @@ -1050,7 +1137,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone, PCB_LAYER_ID int thermalReliefGap = aZone->GetThermalReliefGap( pad ); // Calculate thermal bridge half width - int spoke_w = aZone->GetThermalReliefCopperBridge( pad ); + int spoke_w = aZone->GetThermalReliefSpokeWidth( pad ); // Avoid spoke_w bigger than the smaller pad size, because // it is not possible to create stubs bigger than the pad. // Possible refinement: have a separate size for vertical and horizontal stubs diff --git a/pcbnew/zone_settings.cpp b/pcbnew/zone_settings.cpp index 662af86e08..36b8f2c220 100644 --- a/pcbnew/zone_settings.cpp +++ b/pcbnew/zone_settings.cpp @@ -68,9 +68,9 @@ ZONE_SETTINGS::ZONE_SETTINGS() // thickness of the gap in thermal reliefs: m_ThermalReliefGap = Mils2iu( ZONE_THERMAL_RELIEF_GAP_MIL ); // thickness of the copper bridge in thermal reliefs: - m_ThermalReliefCopperBridge = Mils2iu( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL ); + m_ThermalReliefSpokeWidth = Mils2iu( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL ); - m_PadConnection = ZONE_CONNECTION::THERMAL; // How pads are covered by copper in zone + m_padConnection = ZONE_CONNECTION::THERMAL; // How pads are covered by copper in zone m_Zone_45_Only = false; @@ -104,10 +104,10 @@ ZONE_SETTINGS& ZONE_SETTINGS::operator << ( const ZONE_CONTAINER& aSource ) m_HatchHoleMinArea = aSource.GetHatchHoleMinArea(); m_NetcodeSelection = aSource.GetNetCode(); m_Name = aSource.GetZoneName(); - m_ZoneBorderDisplayStyle = aSource.GetHatchStyle(); + m_ZoneBorderDisplayStyle = aSource.GetHatchStyle(); m_ThermalReliefGap = aSource.GetThermalReliefGap(); - m_ThermalReliefCopperBridge = aSource.GetThermalReliefCopperBridge(); - m_PadConnection = aSource.GetPadConnection(); + m_ThermalReliefSpokeWidth = aSource.GetThermalReliefSpokeWidth(); + m_padConnection = aSource.GetPadConnection(); m_cornerSmoothingType = aSource.GetCornerSmoothingType(); m_cornerRadius = aSource.GetCornerRadius(); m_isKeepout = aSource.GetIsKeepout(); @@ -139,8 +139,8 @@ void ZONE_SETTINGS::ExportSetting( ZONE_CONTAINER& aTarget, bool aFullExport ) c aTarget.SetHatchBorderAlgorithm( m_HatchBorderAlgorithm ); aTarget.SetHatchHoleMinArea( m_HatchHoleMinArea ); aTarget.SetThermalReliefGap( m_ThermalReliefGap ); - aTarget.SetThermalReliefCopperBridge( m_ThermalReliefCopperBridge ); - aTarget.SetPadConnection( m_PadConnection ); + aTarget.SetThermalReliefSpokeWidth( m_ThermalReliefSpokeWidth ); + aTarget.SetPadConnection( m_padConnection ); aTarget.SetCornerSmoothingType( m_cornerSmoothingType ); aTarget.SetCornerRadius( m_cornerRadius ); aTarget.SetIsKeepout( GetIsKeepout() ); diff --git a/pcbnew/zone_settings.h b/pcbnew/zone_settings.h index a826ee23c9..8a1c04c4b6 100644 --- a/pcbnew/zone_settings.h +++ b/pcbnew/zone_settings.h @@ -76,52 +76,50 @@ public: SMOOTHING_LAST // sentinel }; - int m_ZonePriority; ///< Priority (0 ... N) of the zone + int m_ZonePriority; // Priority (0 ... N) of the zone - ZONE_FILL_MODE m_FillMode; - int m_ZoneClearance; // Minimal clearance value - int m_ZoneMinThickness; // Min thickness value in filled areas - int m_HatchThickness; // HatchBorder thickness of lines (if 0 -> solid shape) - int m_HatchGap; // HatchBorder clearance between lines (0 -> solid shape) - double m_HatchOrientation; // HatchBorder orientation of grid lines in degrees - int m_HatchSmoothingLevel; // HatchBorder smoothing type, similar to corner smoothing type - // 0 = no smoothing, 1 = fillet, >= 2 = arc - double m_HatchSmoothingValue; // HatchBorder chamfer/fillet size as a ratio of hole size - double m_HatchHoleMinArea; // min size before holes are dropped (ratio) - int m_HatchBorderAlgorithm; // 0 = use min zone thickness + ZONE_FILL_MODE m_FillMode; + int m_ZoneClearance; // Minimal clearance value + int m_ZoneMinThickness; // Min thickness value in filled areas + int m_HatchThickness; // HatchBorder thickness of lines (if 0 -> solid shape) + int m_HatchGap; // HatchBorder clearance between lines (0 -> solid shape) + double m_HatchOrientation; // HatchBorder orientation of grid lines in degrees + int m_HatchSmoothingLevel; // HatchBorder smoothing type, similar to corner smoothing type + // 0 = no smoothing, 1 = fillet, >= 2 = arc + double m_HatchSmoothingValue; // HatchBorder chamfer/fillet size as a ratio of hole size + double m_HatchHoleMinArea; // min size before holes are dropped (ratio) + int m_HatchBorderAlgorithm; // 0 = use min zone thickness - int m_NetcodeSelection; ///< Net code selection for the current zone + int m_NetcodeSelection; // Net code selection for the current zone - wxString m_Name; ///< Unique name for the current zone (can be blank) + wxString m_Name; // Unique name for the current zone (can be blank) - LSET m_Layers; ///< Layers that this zone exists on + LSET m_Layers; // Layers that this zone exists on /// Option to show the zone area (outlines only, short hatches or full hatches ZONE_BORDER_DISPLAY_STYLE m_ZoneBorderDisplayStyle; - long m_ThermalReliefGap; ///< thickness of the gap in thermal reliefs - long m_ThermalReliefCopperBridge; ///< thickness of the copper bridge in thermal reliefs + long m_ThermalReliefGap; // thickness of the gap in thermal reliefs + long m_ThermalReliefSpokeWidth; // thickness of the copper bridge in thermal reliefs - bool m_Zone_45_Only; + bool m_Zone_45_Only; private: - int m_cornerSmoothingType; ///< Corner smoothing type - unsigned int m_cornerRadius; ///< Corner chamfer distance / fillet radius - ZONE_CONNECTION m_PadConnection; + int m_cornerSmoothingType; // Corner smoothing type + unsigned int m_cornerRadius; // Corner chamfer distance / fillet radius + ZONE_CONNECTION m_padConnection; - /* A zone outline can be a keepout zone. - * It will be never filled, and DRC should test for pads, tracks and vias + /* + * Keepout zones and keepout flags. + * Note that DRC rules can set keepouts on zones whether they're a keepout or not. */ - bool m_isKeepout; + bool m_isKeepout; - /* For keepout zones only: - * what is not allowed inside the keepout ( pads, tracks and vias ) - */ - bool m_keepoutDoNotAllowCopperPour; - bool m_keepoutDoNotAllowVias; - bool m_keepoutDoNotAllowTracks; - bool m_keepoutDoNotAllowPads; - bool m_keepoutDoNotAllowFootprints; + bool m_keepoutDoNotAllowCopperPour; + bool m_keepoutDoNotAllowVias; + bool m_keepoutDoNotAllowTracks; + bool m_keepoutDoNotAllowPads; + bool m_keepoutDoNotAllowFootprints; ISLAND_REMOVAL_MODE m_removeIslands; long long int m_minIslandArea; @@ -168,12 +166,12 @@ public: ZONE_CONNECTION GetPadConnection() const { - return m_PadConnection; + return m_padConnection; } void SetPadConnection( ZONE_CONNECTION aPadConnection ) { - m_PadConnection = aPadConnection; + m_padConnection = aPadConnection; } /** diff --git a/pcbnew/zones_functions_for_undo_redo.cpp b/pcbnew/zones_functions_for_undo_redo.cpp index 68882c45cd..9187cb52db 100644 --- a/pcbnew/zones_functions_for_undo_redo.cpp +++ b/pcbnew/zones_functions_for_undo_redo.cpp @@ -99,16 +99,16 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare ) if( m_ZoneMinThickness != aZoneToCompare.GetMinThickness() ) return false; - if( m_FillMode != aZoneToCompare.GetFillMode() ) + if( m_fillMode != aZoneToCompare.GetFillMode() ) return false; if( m_PadConnection != aZoneToCompare.m_PadConnection ) return false; - if( m_ThermalReliefGap != aZoneToCompare.m_ThermalReliefGap ) + if( m_thermalReliefGap != aZoneToCompare.m_thermalReliefGap ) return false; - if( m_ThermalReliefCopperBridge != aZoneToCompare.m_ThermalReliefCopperBridge ) + if( m_thermalReliefSpokeWidth != aZoneToCompare.m_thermalReliefSpokeWidth ) return false; if( m_zoneName != aZoneToCompare.m_zoneName ) diff --git a/pcbnew/zones_test_and_combine_areas.cpp b/pcbnew/zones_test_and_combine_areas.cpp index 90bae36ed5..3c4062d634 100644 --- a/pcbnew/zones_test_and_combine_areas.cpp +++ b/pcbnew/zones_test_and_combine_areas.cpp @@ -175,7 +175,7 @@ bool BOARD::TestAreaIntersections( ZONE_CONTAINER* area_to_test ) if( area_to_test->GetThermalReliefGap() != area2->GetThermalReliefGap() ) continue; - if( area_to_test->GetThermalReliefCopperBridge() != area2->GetThermalReliefCopperBridge() ) + if( area_to_test->GetThermalReliefSpokeWidth() != area2->GetThermalReliefSpokeWidth() ) continue; if( area_to_test->GetLocalClearance() != area2->GetLocalClearance() )