kicad/pcbnew/zone_manager/panel_zone_properties.cpp

321 lines
12 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Ethan Chien <liangtie.qian@gmail.com>
* Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "panel_zone_properties.h"
#include "zone_manager/panel_zone_properties_base.h"
#include "zone_manager/zones_container.h"
#include <wx/radiobut.h>
#include <kiface_base.h>
#include <confirm.h>
#include <pcb_edit_frame.h>
#include <pcbnew_settings.h>
#include <wx/string.h>
#include <zones.h>
#include <widgets/unit_binder.h>
#include <zone.h>
#include <pad.h>
#include <board.h>
#include <trigo.h>
#include <eda_pattern_match.h>
#include <dialog_copper_zones_base.h>
#include <string_utils.h>
wxDEFINE_EVENT( EVT_ZONE_NAME_UPDATE, wxCommandEvent );
PANEL_ZONE_PROPERTIES::PANEL_ZONE_PROPERTIES( wxWindow* aParent, PCB_BASE_FRAME* aPCB_FRAME,
ZONES_CONTAINER& aZoneContainer ) :
PANEL_ZONE_PROPERTIES_BASE( aParent ),
m_ZoneContainer( aZoneContainer ), m_PCB_Frame( aPCB_FRAME ),
m_cornerSmoothingType( ZONE_SETTINGS::SMOOTHING_UNDEFINED ),
m_outlineHatchPitch( aPCB_FRAME, m_stBorderHatchPitchText, m_outlineHatchPitchCtrl,
m_outlineHatchUnits ),
m_cornerRadius( aPCB_FRAME, m_cornerRadiusLabel, m_cornerRadiusCtrl, m_cornerRadiusUnits ),
m_clearance( aPCB_FRAME, m_clearanceLabel, m_clearanceCtrl, m_clearanceUnits ),
m_minThickness( aPCB_FRAME, m_minWidthLabel, m_minWidthCtrl, m_minWidthUnits ),
m_antipadClearance( aPCB_FRAME, m_antipadLabel, m_antipadCtrl, m_antipadUnits ),
m_spokeWidth( aPCB_FRAME, m_spokeWidthLabel, m_spokeWidthCtrl, m_spokeWidthUnits ),
m_gridStyleRotation( aPCB_FRAME, m_staticTextGrindOrient, m_tcGridStyleOrientation,
m_staticTextRotUnits ),
m_gridStyleThickness( aPCB_FRAME, m_staticTextStyleThickness, m_tcGridStyleThickness,
m_GridStyleThicknessUnits ),
m_gridStyleGap( aPCB_FRAME, m_staticTextGridGap, m_tcGridStyleGap, m_GridStyleGapUnits ),
m_islandThreshold( aPCB_FRAME, m_islandThresholdLabel, m_tcIslandThreshold,
m_islandThresholdUnits ),
m_isTeardrop()
{
m_cbRemoveIslands->Bind( wxEVT_CHOICE,
[&]( wxCommandEvent& )
{
// Area mode is index 2
m_islandThreshold.Enable( m_cbRemoveIslands->GetSelection() == 2 );
} );
}
void PANEL_ZONE_PROPERTIES::ActivateSelectedZone( ZONE* aZone )
{
if( m_settings )
TransferZoneSettingsFromWindow();
m_settings = m_ZoneContainer.GetZoneSettings( aZone );
m_isTeardrop = m_settings->m_TeardropType != TEARDROP_TYPE::TD_NONE;
TransferZoneSettingsToWindow();
}
void PANEL_ZONE_PROPERTIES::OnUserConfirmChange()
{
TransferZoneSettingsFromWindow();
}
bool PANEL_ZONE_PROPERTIES::TransferZoneSettingsToWindow()
{
if( !m_settings )
return false;
m_cbLocked->SetValue( m_settings->m_Locked );
m_cornerSmoothingChoice->SetSelection( m_settings->GetCornerSmoothingType() );
m_cornerRadius.SetValue( m_settings->GetCornerRadius() );
if( m_isTeardrop ) // outlines are never smoothed: they have already the right shape
{
m_cornerSmoothingChoice->SetSelection( 0 );
m_cornerSmoothingChoice->Enable( false );
m_cornerRadius.SetValue( 0 );
m_cornerRadius.Enable( false );
}
switch( m_settings->m_ZoneBorderDisplayStyle )
{
case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: m_OutlineDisplayCtrl->SetSelection( 0 ); break;
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: m_OutlineDisplayCtrl->SetSelection( 1 ); break;
case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: m_OutlineDisplayCtrl->SetSelection( 2 ); break;
case ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER: break;
}
m_outlineHatchPitch.SetValue( m_settings->m_BorderHatchPitch );
m_clearance.SetValue( m_settings->m_ZoneClearance );
m_minThickness.SetValue( m_settings->m_ZoneMinThickness );
switch( m_settings->GetPadConnection() )
{
default:
case ZONE_CONNECTION::THERMAL: m_PadInZoneOpt->SetSelection( 1 ); break;
case ZONE_CONNECTION::THT_THERMAL: m_PadInZoneOpt->SetSelection( 2 ); break;
case ZONE_CONNECTION::NONE: m_PadInZoneOpt->SetSelection( 3 ); break;
case ZONE_CONNECTION::FULL: m_PadInZoneOpt->SetSelection( 0 ); break;
}
if( m_isTeardrop )
{
m_PadInZoneOpt->SetSelection( 0 );
m_PadInZoneOpt->Enable( false );
}
// Do not enable/disable antipad clearance and spoke width. They might be needed if
// a footprint or pad overrides the zone to specify a thermal connection.
m_antipadClearance.SetValue( m_settings->m_ThermalReliefGap );
m_spokeWidth.SetValue( m_settings->m_ThermalReliefSpokeWidth );
m_islandThreshold.SetDataType( EDA_DATA_TYPE::AREA );
m_islandThreshold.SetDoubleValue( static_cast<double>( m_settings->GetMinIslandArea() ) );
m_cbRemoveIslands->SetSelection( static_cast<int>( m_settings->GetIslandRemovalMode() ) );
m_islandThreshold.Enable( m_settings->GetIslandRemovalMode() == ISLAND_REMOVAL_MODE::AREA );
if( !m_isTeardrop && m_settings->m_FillMode == ZONE_FILL_MODE::HATCH_PATTERN )
m_GridStyleCtrl->SetSelection( 1 );
else
m_GridStyleCtrl->SetSelection( 0 );
m_GridStyleCtrl->Enable( !m_isTeardrop );
m_gridStyleRotation.SetUnits( EDA_UNITS::DEGREES );
m_gridStyleRotation.SetAngleValue( m_settings->m_HatchOrientation );
m_gridStyleThickness.SetValue( m_settings->m_HatchThickness );
m_gridStyleGap.SetValue( m_settings->m_HatchGap );
m_spinCtrlSmoothLevel->SetValue( m_settings->m_HatchSmoothingLevel );
m_spinCtrlSmoothValue->SetValue( m_settings->m_HatchSmoothingValue );
m_tcZoneName->SetValue( m_settings->m_Name );
// Enable/Disable some widgets
wxCommandEvent aEvent;
OnStyleSelection( aEvent );
Fit();
return true;
}
void PANEL_ZONE_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& )
{
if( m_cornerSmoothingType != m_cornerSmoothingChoice->GetSelection() )
{
m_cornerSmoothingType = m_cornerSmoothingChoice->GetSelection();
if( m_cornerSmoothingChoice->GetSelection() == ZONE_SETTINGS::SMOOTHING_CHAMFER )
m_cornerRadiusLabel->SetLabel( _( "Chamfer distance:" ) );
else
m_cornerRadiusLabel->SetLabel( _( "Fillet radius:" ) );
}
m_cornerRadiusCtrl->Enable( m_cornerSmoothingType > ZONE_SETTINGS::SMOOTHING_NONE );
}
void PANEL_ZONE_PROPERTIES::OnRemoveIslandsSelection( wxCommandEvent& aEvent )
{
m_islandThreshold.Enable( m_cbRemoveIslands->GetSelection() == 2 );
}
void PANEL_ZONE_PROPERTIES::OnZoneNameChanged( wxCommandEvent& aEvent )
{
wxCommandEvent* evt = new wxCommandEvent( EVT_ZONE_NAME_UPDATE );
evt->SetString( m_tcZoneName->GetValue() );
wxQueueEvent( m_parent, evt );
}
bool PANEL_ZONE_PROPERTIES::TransferZoneSettingsFromWindow()
{
if( !m_settings )
return false;
if( m_GridStyleCtrl->GetSelection() > 0 )
m_settings->m_FillMode = ZONE_FILL_MODE::HATCH_PATTERN;
else
m_settings->m_FillMode = ZONE_FILL_MODE::POLYGONS;
if( !AcceptOptions() )
return false;
m_settings->m_HatchOrientation = m_gridStyleRotation.GetAngleValue();
m_settings->m_HatchThickness = m_gridStyleThickness.GetValue();
m_settings->m_HatchGap = m_gridStyleGap.GetValue();
m_settings->m_HatchSmoothingLevel = m_spinCtrlSmoothLevel->GetValue();
m_settings->m_HatchSmoothingValue = m_spinCtrlSmoothValue->GetValue();
return true;
}
bool PANEL_ZONE_PROPERTIES::AcceptOptions( bool aUseExportableSetupOnly )
{
if( !m_clearance.Validate( 0, pcbIUScale.mmToIU( ZONE_CLEARANCE_MAX_VALUE_MM ) ) )
return false;
if( !m_minThickness.Validate( pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM ), INT_MAX ) )
return false;
if( !m_cornerRadius.Validate( 0, INT_MAX ) )
return false;
if( !m_spokeWidth.Validate( 0, INT_MAX ) )
return false;
m_gridStyleRotation.SetValue( NormalizeAngle180( m_gridStyleRotation.GetValue() ) );
if( m_settings->m_FillMode == ZONE_FILL_MODE::HATCH_PATTERN )
{
int minThickness = m_minThickness.GetValue();
if( !m_gridStyleThickness.Validate( minThickness, INT_MAX ) )
return false;
if( !m_gridStyleGap.Validate( minThickness, INT_MAX ) )
return false;
}
switch( m_PadInZoneOpt->GetSelection() )
{
case 3: m_settings->SetPadConnection( ZONE_CONNECTION::NONE ); break;
case 2: m_settings->SetPadConnection( ZONE_CONNECTION::THT_THERMAL ); break;
case 1: m_settings->SetPadConnection( ZONE_CONNECTION::THERMAL ); break;
case 0: m_settings->SetPadConnection( ZONE_CONNECTION::FULL ); break;
}
switch( m_OutlineDisplayCtrl->GetSelection() )
{
case 0: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
case 1: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
case 2: m_settings->m_ZoneBorderDisplayStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
}
if( !m_outlineHatchPitch.Validate( pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MINDIST_MM ),
pcbIUScale.mmToIU( ZONE_BORDER_HATCH_MAXDIST_MM ) ) )
return false;
m_settings->m_BorderHatchPitch = m_outlineHatchPitch.GetValue();
m_settings->m_ZoneClearance = m_clearance.GetValue();
m_settings->m_ZoneMinThickness = m_minThickness.GetValue();
m_settings->SetCornerSmoothingType( m_cornerSmoothingChoice->GetSelection() );
m_settings->SetCornerRadius( m_settings->GetCornerSmoothingType()
== ZONE_SETTINGS::SMOOTHING_NONE
? 0
: m_cornerRadius.GetValue() );
m_settings->m_Locked = m_cbLocked->GetValue();
m_settings->m_ThermalReliefGap = m_antipadClearance.GetValue();
m_settings->m_ThermalReliefSpokeWidth = m_spokeWidth.GetValue();
if( m_settings->m_ThermalReliefSpokeWidth < m_settings->m_ZoneMinThickness )
{
DisplayError( this, _( "Thermal spoke width cannot be smaller than the minimum width." ) );
return false;
}
m_settings->SetIslandRemovalMode( (ISLAND_REMOVAL_MODE) m_cbRemoveIslands->GetSelection() );
m_settings->SetMinIslandArea( m_islandThreshold.GetValue() );
// If we use only exportable to others zones parameters, exit here:
if( aUseExportableSetupOnly )
return true;
m_settings->m_Name = m_tcZoneName->GetValue();
return true;
}
void PANEL_ZONE_PROPERTIES::OnStyleSelection( wxCommandEvent& aEvent )
{
bool enable = m_GridStyleCtrl->GetSelection() >= 1;
m_tcGridStyleThickness->Enable( enable );
m_tcGridStyleGap->Enable( enable );
m_tcGridStyleOrientation->Enable( enable );
m_spinCtrlSmoothLevel->Enable( enable );
m_spinCtrlSmoothValue->Enable( enable );
}