From 544867cf2ac291648d0be7768c2eca46e2a83167 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 14 Nov 2019 16:26:05 +0100 Subject: [PATCH] ADD: layer stack manager: support of multilayer dielectric between 2 copper layers This feature is useful for advanced microwave applications. This is the last missing feature in the board stackup management. --- common/pcb.keywords | 1 + .../board_stackup_reporter.cpp | 94 +- .../class_board_stackup.cpp | 187 ++- .../class_board_stackup.h | 71 +- .../panel_board_stackup.cpp | 1080 ++++++++++------- .../panel_board_stackup.h | 43 +- pcbnew/exporters/gerber_jobfile_writer.cpp | 253 ++-- pcbnew/pcb_parser.cpp | 120 +- 8 files changed, 1092 insertions(+), 757 deletions(-) diff --git a/common/pcb.keywords b/common/pcb.keywords index 81f780d525..6f9de73d81 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -25,6 +25,7 @@ # These are the keywords for the Pcbnew s-expression file format. add_net +addsublayer allowed anchor angle diff --git a/pcbnew/board_stackup_manager/board_stackup_reporter.cpp b/pcbnew/board_stackup_manager/board_stackup_reporter.cpp index 79576c8ce1..db46364870 100644 --- a/pcbnew/board_stackup_manager/board_stackup_reporter.cpp +++ b/pcbnew/board_stackup_manager/board_stackup_reporter.cpp @@ -42,58 +42,76 @@ wxString BuildStackupReport( BOARD_STACKUP& aStackup, EDA_UNITS_T aUnits ) wxString report; wxString txt; - int row = 0; LOCALE_IO toggle; // toggles on the C locale to write floating values, then off. - for( const auto item : aStackup.GetList() ) + for( const BOARD_STACKUP_ITEM* item : aStackup.GetList() ) { // Skip stackup items useless for the current board if( !item->IsEnabled() ) - { - row++; continue; - } - txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(), item->GetTypeName() ); + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + { + wxString sublayer_text; + + if( item->GetSublayersCount() ) + sublayer_text.Printf( "\n sublayer \"1/%d\"", item->GetSublayersCount() ); + + txt.Printf( "layer \"%s\" type \"%s\"%s", + item->FormatDielectricLayerName(), + item->GetTypeName(), sublayer_text ); + } + else + txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(), + item->GetTypeName() ); + report << txt; - if( item->IsMaterialEditable() ) - { - txt.Printf( " Material \"%s\"", item->GetMaterial() ); - report << txt; - } - - if( item->HasEpsilonRValue() ) - { - txt.Printf( " EpsilonR %s", item->FormatEpsilonR() ); - report << txt; - } - - if( item->HasLossTangentValue() ) - { - txt.Printf( " LossTg %s", item->FormatLossTangent() ); - report << txt; - } - - if( item->IsThicknessEditable() ) - { - txt.Printf( " Thickness %s", - StringFromValue( aUnits, item->GetThickness(), true, true ) ); - report << txt; - - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() ) - { - txt.Printf( " Locked" ); - report << txt; - } - } - if( item->IsColorEditable() ) { txt.Printf( " Color \"%s\"", item->GetColor() ); report << txt; } - row++; + + for( int idx = 0; idx < item->GetSublayersCount(); idx++ ) + { + if( idx ) // not printed for the main (first) layer. + { + txt.Printf( "\n sublayer \"%d/%d\"", idx+1, item->GetSublayersCount() ); + report << txt; + } + + if( item->IsThicknessEditable() ) + { + txt.Printf( " Thickness %s", + StringFromValue( aUnits, item->GetThickness( idx ), true, true ) ); + report << txt; + + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) ) + { + txt.Printf( " Locked" ); + report << txt; + } + } + + if( item->IsMaterialEditable() ) + { + txt.Printf( " Material \"%s\"", item->GetMaterial( idx ) ); + report << txt; + } + + if( item->HasEpsilonRValue() ) + { + txt.Printf( " EpsilonR %s", item->FormatEpsilonR( idx ) ); + report << txt; + } + + if( item->HasLossTangentValue() ) + { + txt.Printf( " LossTg %s", item->FormatLossTangent( idx ) ); + report << txt; + } + } report << '\n'; } diff --git a/pcbnew/board_stackup_manager/class_board_stackup.cpp b/pcbnew/board_stackup_manager/class_board_stackup.cpp index b1d047aa90..05ddda14c6 100644 --- a/pcbnew/board_stackup_manager/class_board_stackup.cpp +++ b/pcbnew/board_stackup_manager/class_board_stackup.cpp @@ -35,6 +35,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType ) m_DielectricPrmsList.emplace_back( item_prms ); m_LayerId = UNDEFINED_LAYER; m_Type = aType; + SetDielectricLayerId( 1 ); SetEnabled( true ); // Initialize parameters to a usual value for allowed types: @@ -48,7 +49,6 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType ) case BS_ITEM_TYPE_DIELECTRIC: m_TypeName = KEY_CORE; // or prepreg SetMaterial( "FR4" ); // or other dielectric name - SetDielectricLayerId( 1 ); SetLossTangent( 0.02 ); // for FR4 SetEpsilonR( 4.5 ); // for FR4 break; @@ -81,6 +81,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType ) BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther ) { m_LayerId = aOther.m_LayerId; + m_DielectricLayerId = aOther.m_DielectricLayerId; m_Type = aOther.m_Type; m_enabled = aOther.m_enabled; m_DielectricPrmsList = aOther.m_DielectricPrmsList; @@ -90,6 +91,28 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther ) } +void BOARD_STACKUP_ITEM::AddDielectricPrms( int aDielectricPrmsIdx ) +{ + // add a DIELECTRIC_PRMS item to m_DielectricPrmsList + DIELECTRIC_PRMS new_prms; + + m_DielectricPrmsList.emplace( m_DielectricPrmsList.begin() + aDielectricPrmsIdx, + new_prms ); +} + + +void BOARD_STACKUP_ITEM::RemoveDielectricPrms( int aDielectricPrmsIdx ) +{ + // Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList if possible + + if( GetSublayersCount() < 2 || aDielectricPrmsIdx < 0 || aDielectricPrmsIdx >= GetSublayersCount() ) + return; + + m_DielectricPrmsList.erase( m_DielectricPrmsList.begin() + aDielectricPrmsIdx ); +} + + + int BOARD_STACKUP_ITEM::GetCopperDefaultThickness() { // A reasonable thickness for copper layers: @@ -104,38 +127,42 @@ int BOARD_STACKUP_ITEM::GetMaskDefaultThickness() } // Getters: -int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) +int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) const { + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness; } -double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) +double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) const { + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent; } -double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) +double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) const { + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR; } -bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) +bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) const { + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked; } -int BOARD_STACKUP_ITEM::GetDielectricLayerId( int aDielectricSubLayer ) +wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) const { - return m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId; -} + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); - -wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) -{ return m_DielectricPrmsList[aDielectricSubLayer].m_Material; } @@ -143,41 +170,50 @@ wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) // Setters: void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer ) { - m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness; + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + + if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ) + m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness; } void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer ) { - m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg; + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + + if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ) + m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg; } void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer ) { - m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon; + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + + if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ) + m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon; } void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer ) { - m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked; -} + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); - -void BOARD_STACKUP_ITEM::SetDielectricLayerId( int aLayerId, int aDielectricSubLayer ) -{ - m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId = aLayerId; + if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ) + m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked; } void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer ) { - m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName; + wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ); + + if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() ) + m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName; } -bool BOARD_STACKUP_ITEM::HasEpsilonRValue() +bool BOARD_STACKUP_ITEM::HasEpsilonRValue() const { return m_Type == BS_ITEM_TYPE_DIELECTRIC || m_Type == BS_ITEM_TYPE_SOLDERMASK @@ -186,23 +222,21 @@ bool BOARD_STACKUP_ITEM::HasEpsilonRValue() }; -bool BOARD_STACKUP_ITEM::HasLossTangentValue() +bool BOARD_STACKUP_ITEM::HasLossTangentValue() const { return m_Type == BS_ITEM_TYPE_DIELECTRIC - || m_Type == BS_ITEM_TYPE_SOLDERMASK - //|| m_Type == BS_ITEM_TYPE_SILKSCREEN - ; + || m_Type == BS_ITEM_TYPE_SOLDERMASK; }; -bool BOARD_STACKUP_ITEM::HasMaterialValue() +bool BOARD_STACKUP_ITEM::HasMaterialValue( int aDielectricSubLayer ) const { // return true if the material is specified - return IsMaterialEditable() && IsPrmSpecified( GetMaterial() ); + return IsMaterialEditable() && IsPrmSpecified( GetMaterial( aDielectricSubLayer ) ); } -bool BOARD_STACKUP_ITEM::IsMaterialEditable() +bool BOARD_STACKUP_ITEM::IsMaterialEditable() const { // The material is editable only for dielectric return m_Type == BS_ITEM_TYPE_DIELECTRIC || @@ -211,13 +245,13 @@ bool BOARD_STACKUP_ITEM::IsMaterialEditable() } -bool BOARD_STACKUP_ITEM::IsColorEditable() +bool BOARD_STACKUP_ITEM::IsColorEditable() const { return m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SILKSCREEN; } -bool BOARD_STACKUP_ITEM::IsThicknessEditable() +bool BOARD_STACKUP_ITEM::IsThicknessEditable() const { switch( m_Type ) { @@ -244,25 +278,25 @@ bool BOARD_STACKUP_ITEM::IsThicknessEditable() } -wxString BOARD_STACKUP_ITEM::FormatEpsilonR() +wxString BOARD_STACKUP_ITEM::FormatEpsilonR( int aDielectricSubLayer ) const { // return a wxString to print/display Epsilon R wxString txt; - txt.Printf( "%.1f", GetEpsilonR() ); + txt.Printf( "%.1f", GetEpsilonR( aDielectricSubLayer ) ); return txt; } -wxString BOARD_STACKUP_ITEM::FormatLossTangent() +wxString BOARD_STACKUP_ITEM::FormatLossTangent( int aDielectricSubLayer ) const { // return a wxString to print/display Loss Tangent wxString txt; - txt.Printf( "%g", GetLossTangent() ); + txt.Printf( "%g", GetLossTangent( aDielectricSubLayer ) ); return txt; } -wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() +wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() const { // return a wxString to print/display a dielectriv name wxString lname; @@ -364,17 +398,31 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings ) BOARD_STACKUP stackup; stackup.BuildDefaultStackupList( aSettings ); - // First test for removed layers: - for( BOARD_STACKUP_ITEM* old_item: m_list ) + // First, find removed layers: + for( BOARD_STACKUP_ITEM* curr_item: m_list ) { bool found = false; for( BOARD_STACKUP_ITEM* item: stackup.GetList() ) { - if( item->GetBrdLayerId() == old_item->GetBrdLayerId() ) + if( curr_item->GetBrdLayerId() != UNDEFINED_LAYER ) { - found = true; - break; + if( item->GetBrdLayerId() == curr_item->GetBrdLayerId() ) + { + found = true; + break; + } + } + else // curr_item = dielectric layer + { + if( item->GetBrdLayerId() != UNDEFINED_LAYER ) + continue; + + if( item->GetDielectricLayerId() == curr_item->GetDielectricLayerId() ) + { + found = true; + break; + } } } @@ -403,6 +451,10 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings ) } else // dielectric layer: see m_DielectricLayerId for identification { + // Compare dielectric layer with dielectric layer + if( initial_item->GetBrdLayerId() != UNDEFINED_LAYER ) + continue; + if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() ) { *item = *initial_item; @@ -413,7 +465,9 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings ) } if( !found ) + { change = true; + } } // Transfer other stackup settings from aSettings @@ -586,31 +640,40 @@ void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter, aFormatter->Quotew( layer_name ).c_str(), aFormatter->Quotew( item->GetTypeName() ).c_str() ); - if( item->IsThicknessEditable() ) - { - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() ) - aFormatter->Print( 0, " (thickness %s locked)", - FormatInternalUnits( item->GetThickness() ).c_str() ); - else - aFormatter->Print( 0, " (thickness %s)", - FormatInternalUnits( item->GetThickness() ).c_str() ); - } - - if( item->HasMaterialValue() ) - aFormatter->Print( 0, " (material %s)", - aFormatter->Quotew( item->GetMaterial() ).c_str() ); - - if( item->HasEpsilonRValue() && item->HasMaterialValue() ) - aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR() ); - - if( item->HasLossTangentValue() && item->HasMaterialValue() ) - aFormatter->Print( 0, " (loss_tangent %s)", - Double2Str(item->GetLossTangent() ).c_str() ); - if( item->IsColorEditable() && IsPrmSpecified( item->GetColor() ) ) aFormatter->Print( 0, " (color %s)", aFormatter->Quotew( item->GetColor() ).c_str() ); + for( int idx = 0; idx < item->GetSublayersCount(); idx++ ) + { + if( idx ) // not for the main (first) layer. + { + aFormatter->Print( 0, "\n" ); + aFormatter->Print( nest_level+1, "addsublayer" ); + } + + if( item->IsThicknessEditable() ) + { + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) ) + aFormatter->Print( 0, " (thickness %s locked)", + FormatInternalUnits( item->GetThickness( idx ) ).c_str() ); + else + aFormatter->Print( 0, " (thickness %s)", + FormatInternalUnits( item->GetThickness( idx ) ).c_str() ); + } + + if( item->HasMaterialValue( idx ) ) + aFormatter->Print( 0, " (material %s)", + aFormatter->Quotew( item->GetMaterial( idx ) ).c_str() ); + + if( item->HasEpsilonRValue() && item->HasMaterialValue( idx ) ) + aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR( idx ) ); + + if( item->HasLossTangentValue() && item->HasMaterialValue( idx ) ) + aFormatter->Print( 0, " (loss_tangent %s)", + Double2Str(item->GetLossTangent( idx ) ).c_str() ); + } + aFormatter->Print( 0, ")\n" ); } diff --git a/pcbnew/board_stackup_manager/class_board_stackup.h b/pcbnew/board_stackup_manager/class_board_stackup.h index b93f3dfc41..6b164eb9b4 100644 --- a/pcbnew/board_stackup_manager/class_board_stackup.h +++ b/pcbnew/board_stackup_manager/class_board_stackup.h @@ -58,15 +58,13 @@ enum BS_EDGE_CONNECTOR_CONSTRAINTS /** - * A helper class to manage a dielectric parameters + * A helper class to manage a dielectric layer set of parameters */ class DIELECTRIC_PRMS { friend class BOARD_STACKUP_ITEM; private: - int m_DielectricLayerId;/// the "layer" id for dielectric layers, - /// from 1 (top) to 31 (bottom) wxString m_Material; /// type of material (for dielectric and solder mask) int m_Thickness; /// the physical layer thickness in internal units bool m_ThicknessLocked; /// true for dielectric layers with a fixed thickness @@ -76,7 +74,6 @@ private: public: DIELECTRIC_PRMS() : - m_DielectricLayerId(-1), m_Thickness(0), m_ThicknessLocked( false ), m_EpsilonR( 1.0 ), m_LossTangent( 0.0 ) {} @@ -102,6 +99,9 @@ private: PCB_LAYER_ID m_LayerId; /// the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask) /// and UNDEFINED_LAYER (-1) for dielectic layers that are not /// really layers for the board editor + int m_DielectricLayerId;/// the "layer" id for dielectric layers, + /// from 1 (top) to 31 (bottom) + /// (only 31 dielectric layers for 32 copper layers) /// List of dielectric parameters /// usually only one item, but in complex (microwave) boards, one can have /// more than one dielectic layer between 2 copper layers, and therfore @@ -112,25 +112,40 @@ private: /// false to ignore it. Mainly used in dialog stackup editor. public: + /** + * add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList + * all values are set to default + * @param aDielectricPrmsIdx is a index in m_DielectricPrmsList + * the new item will be inserted at this position + */ + void AddDielectricPrms( int aDielectricPrmsIdx ); + + /** + * Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList + * @param aDielectricPrmsIdx is the index of the parameters set + * to remove in m_DielectricPrmsList + */ + void RemoveDielectricPrms( int aDielectricPrmsIdx ); + /// @return true if the layer has a meaningfull Epsilon R parameter /// namely dielectric layers: dielectric and solder mask - bool HasEpsilonRValue(); + bool HasEpsilonRValue() const; /// @return true if the layer has a meaningfull Dielectric Loss parameter /// namely dielectric layers: dielectric and solder mask - bool HasLossTangentValue(); + bool HasLossTangentValue() const; /// @return true if the material is specified - bool HasMaterialValue(); + bool HasMaterialValue( int aDielectricSubLayer = 0 ) const; /// @return true if the material is editable - bool IsMaterialEditable(); + bool IsMaterialEditable() const; /// @return true if the color is editable - bool IsColorEditable(); + bool IsColorEditable() const; /// @return true if Thickness is editable - bool IsThicknessEditable(); + bool IsThicknessEditable() const; /// @return a reasonable default value for a copper layer thickness static int GetCopperDefaultThickness(); @@ -138,30 +153,34 @@ public: /// @return a reasonable default value for a solder mask layer thickness static int GetMaskDefaultThickness(); + /// @return a the number of sublayers in a dielectric layer. + /// the count is >= 1 (there is at least one layer) + int GetSublayersCount() const { return m_DielectricPrmsList.size(); } + /// @return a wxString to print/display Epsilon R - wxString FormatEpsilonR(); + wxString FormatEpsilonR( int aDielectricSubLayer = 0 ) const; /// @return a wxString to print/display Loss Tangent - wxString FormatLossTangent(); + wxString FormatLossTangent( int aDielectricSubLayer = 0 ) const; /// @return a wxString to print/display a dielectric name - wxString FormatDielectricLayerName(); + wxString FormatDielectricLayerName() const; // Getters: - bool IsEnabled() {return m_enabled; } + bool IsEnabled() const {return m_enabled; } - BOARD_STACKUP_ITEM_TYPE GetType() { return m_Type; } - PCB_LAYER_ID GetBrdLayerId() { return m_LayerId; } - wxString GetColor(){ return m_Color; } - wxString GetLayerName() { return m_LayerName; } - wxString GetTypeName() { return m_TypeName; } + BOARD_STACKUP_ITEM_TYPE GetType() const { return m_Type; } + PCB_LAYER_ID GetBrdLayerId() const { return m_LayerId; } + wxString GetColor() const { return m_Color; } + wxString GetLayerName() const { return m_LayerName; } + wxString GetTypeName() const { return m_TypeName; } + int GetDielectricLayerId() const { return m_DielectricLayerId; } - int GetThickness( int aDielectricSubLayer = 0 ); - bool IsThicknessLocked( int aDielectricSubLayer = 0 ); - double GetEpsilonR( int aDielectricSubLayer = 0 ); - double GetLossTangent( int aDielectricSubLayer = 0 ); - int GetDielectricLayerId( int aDielectricSubLayer = 0 ); - wxString GetMaterial( int aDielectricSubLayer = 0 ); + int GetThickness( int aDielectricSubLayer = 0 ) const; + bool IsThicknessLocked( int aDielectricSubLayer = 0 ) const; + double GetEpsilonR( int aDielectricSubLayer = 0 ) const; + double GetLossTangent( int aDielectricSubLayer = 0 ) const; + wxString GetMaterial( int aDielectricSubLayer = 0 ) const; // Setters: void SetEnabled( bool aEnable) { m_enabled = aEnable; } @@ -169,12 +188,12 @@ public: void SetColor( const wxString& aColorName ){ m_Color = aColorName; } void SetLayerName( const wxString& aName ) { m_LayerName = aName; } void SetTypeName( const wxString& aName ) { m_TypeName = aName; } + void SetDielectricLayerId( int aLayerId ) { m_DielectricLayerId = aLayerId; } void SetThickness( int aThickness, int aDielectricSubLayer = 0 ); void SetThicknessLocked( bool aLocked, int aDielectricSubLayer = 0 ); void SetEpsilonR( double aEpsilon, int aDielectricSubLayer = 0 ); void SetLossTangent( double aTg, int aDielectricSubLayer = 0 ); - void SetDielectricLayerId( int aLayerId, int aDielectricSubLayer = 0 ); void SetMaterial( const wxString& aName, int aDielectricSubLayer = 0 ); }; diff --git a/pcbnew/board_stackup_manager/panel_board_stackup.cpp b/pcbnew/board_stackup_manager/panel_board_stackup.cpp index 11aece6737..0c5e17eeb9 100644 --- a/pcbnew/board_stackup_manager/panel_board_stackup.cpp +++ b/pcbnew/board_stackup_manager/panel_board_stackup.cpp @@ -38,15 +38,17 @@ #include #include #include "dialog_dielectric_list_manager.h" +#include // Some wx widget ID to know what widget has fired a event: -#define ID_INCREMENT 128 // space between 2 ID type. Bigger than the layer count max +#define ID_INCREMENT 256 // space between 2 ID type. Bigger than the layer count max // The actual widget IDs are the base id + the row index. // they are used in events to know the row index of the control that fired the event enum WIDGETS_IDS { - ID_ITEM_MATERIAL = 10000, // Be sure it is higher than other IDs used in the board setup dialog + ID_ITEM_MATERIAL = 10000, // Be sure it is higher than other IDs + // used in the board setup dialog ID_ITEM_THICKNESS = ID_ITEM_MATERIAL + ID_INCREMENT, ID_ITEM_THICKNESS_LOCKED = ID_ITEM_THICKNESS + ID_INCREMENT, ID_ITEM_COLOR = ID_ITEM_THICKNESS_LOCKED + ID_INCREMENT, @@ -100,12 +102,12 @@ PANEL_SETUP_BOARD_STACKUP::PANEL_SETUP_BOARD_STACKUP( PAGED_DIALOG* aParent, PCB m_thicknessCtrl->SetMinSize( m_numericTextCtrlSize ); m_tcCTValue->SetMinSize( m_numericTextCtrlSize ); - buildLayerStackPanel(); - synchronizeWithBoard( true ); + // Prepare dielectric layer type: layer type keyword is "core" or "prepreg" + m_core_prepreg_choice.Add( _( "Core" ) ); + m_core_prepreg_choice.Add( _( "PrePreg" ) ); - // Currently: disable not yet usable buttons: - m_buttonAddDielectricLayer->Hide(); - m_buttonRemoveDielectricLayer->Hide(); + buildLayerStackPanel( true ); + synchronizeWithBoard( true ); } @@ -148,53 +150,30 @@ void PANEL_SETUP_BOARD_STACKUP::onAddDielectricLayer( wxCommandEvent& event ) { // Build Dielectric layers list: wxArrayString d_list; + std::vector rows; // indexes of row values for each selectable item + int row = -1; for( BOARD_STACKUP_ROW_UI_ITEM& item : m_rowUiItemsList ) { + row++; + if( !item.m_isEnabled ) continue; - if( item.m_Item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - d_list.Add( item.m_LayerName->GetLabel() ); - } + BOARD_STACKUP_ITEM* brd_stackup_item = item.m_Item; - // Show list - int index = wxGetSingleChoiceIndex( wxEmptyString, _("Dielectric Layers List"), - d_list); - - if( index < 0 ) - return; -} - - -void PANEL_SETUP_BOARD_STACKUP::onRemoveDielectricLayer( wxCommandEvent& event ) -{ - // Build deletable Dielectric layers list. - // A layer can be deleted if there are 2 (or more) dielectric layers - // between 2 copper layers - wxArrayString d_list; - wxString previousD_Layer; - int dielectric_layer_count = 0; - - for( auto item : m_stackup.GetList() ) - { - if( !item->IsEnabled() ) - continue; - - if( item->GetType() == BS_ITEM_TYPE_COPPER ) - dielectric_layer_count = 0; - else if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + if( brd_stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - dielectric_layer_count++; + if( brd_stackup_item->GetSublayersCount() > 1 ) + { + d_list.Add( wxString::Format( _( "Layer \"%s\" (sublayer %d/%d)" ), + brd_stackup_item->FormatDielectricLayerName(), + item.m_SubItem+1, brd_stackup_item->GetSublayersCount() ) ); + } + else + d_list.Add( brd_stackup_item->FormatDielectricLayerName() ); - if( dielectric_layer_count == 1 ) - previousD_Layer = item->FormatDielectricLayerName(); - - if( dielectric_layer_count == 2 ) - d_list.Add( previousD_Layer ); - - if( dielectric_layer_count >= 2 ) - d_list.Add( item->FormatDielectricLayerName() ); + rows.push_back( row ); } } @@ -204,28 +183,77 @@ void PANEL_SETUP_BOARD_STACKUP::onRemoveDielectricLayer( wxCommandEvent& event ) if( index < 0 ) return; + + row = rows[index]; + + BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[row].m_Item; + int new_sublayer = m_rowUiItemsList[row].m_SubItem; + + // Insert a new item after the selected item + brd_stackup_item->AddDielectricPrms( new_sublayer+1 ); + + rebuildLayerStackPanel(); +} + + +void PANEL_SETUP_BOARD_STACKUP::onRemoveDielectricLayer( wxCommandEvent& event ) +{ + // Build deletable Dielectric layers list. + // A layer can be deleted if there are 2 (or more) dielectric sub-layers + // between 2 copper layers + wxArrayString d_list; + std::vector rows; // indexes of row values for each selectable item + + int row = -1; + + for( auto item : m_stackup.GetList() ) + { + row++; + + if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC ) + continue; + + if( item->GetSublayersCount() <= 1 ) + continue; + + for( int ii = 0; ii < item->GetSublayersCount(); ii++ ) + { + d_list.Add( wxString::Format( "Layer \"%s\" sublayer %d/%d", + item->FormatDielectricLayerName(), ii+1, item->GetSublayersCount() ) ); + + rows.push_back( row ); + } + } + + // Show list + int index = wxGetSingleChoiceIndex( wxEmptyString, _("Dielectric Layers List"), + d_list ); + + if( index < 0 ) + return; + + row = rows[index]; + + BOARD_STACKUP_ITEM* brd_stackup_item = m_rowUiItemsList[row].m_Item; + int sublayer = m_rowUiItemsList[row].m_SubItem; + + // Insert a new item after the selected item + brd_stackup_item->RemoveDielectricPrms( sublayer ); + + rebuildLayerStackPanel(); } void PANEL_SETUP_BOARD_STACKUP::onRemoveDielUI( wxUpdateUIEvent& event ) { // The m_buttonRemoveDielectricLayer wxButton is enabled only if a dielectric - // layer can be removed, i.e. if 2 (or more) dielectric layers are found - // between 3 copper layers - int dielectric_layer_count = 0; - + // layer can be removed, i.e. if dielectric layers have sublayers for( auto item : m_stackup.GetList() ) { - if( !item->IsEnabled() ) - continue; + if( !item->IsEnabled() || item->GetType() != BS_ITEM_TYPE_DIELECTRIC ) + continue; - if( item->GetType() == BS_ITEM_TYPE_COPPER ) - dielectric_layer_count = 0; - - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - dielectric_layer_count++; - - if( dielectric_layer_count >= 2 ) + if( item->GetSublayersCount() > 1 ) { m_buttonRemoveDielectricLayer->Enable( true ); return; @@ -256,10 +284,10 @@ void PANEL_SETUP_BOARD_STACKUP::onExportToClipboard( wxCommandEvent& event ) wxColor PANEL_SETUP_BOARD_STACKUP::GetSelectedColor( int aRow ) const { - wxBitmapComboBox* choice = static_cast( m_rowUiItemsList[aRow].m_ColorCtrl ); + wxBitmapComboBox* choice = dynamic_cast( m_rowUiItemsList[aRow].m_ColorCtrl ); wxASSERT( choice ); - int idx = choice->GetSelection(); + int idx = choice ? choice->GetSelection() : 0; if( idx != GetColorUserDefinedListIdx() ) // a standard color is selected return GetColorStandardList()[idx].m_Color; @@ -272,17 +300,25 @@ void PANEL_SETUP_BOARD_STACKUP::onUpdateThicknessValue( wxUpdateUIEvent& event ) { int thickness = 0; - for( auto item : m_stackup.GetList() ) + for( BOARD_STACKUP_ROW_UI_ITEM& ui_item : m_rowUiItemsList ) { - if( item->IsThicknessEditable() && item->IsEnabled() ) - thickness += item->GetThickness(); + BOARD_STACKUP_ITEM* item = ui_item.m_Item; + + if( !item->IsThicknessEditable() || !ui_item.m_isEnabled ) + continue; + + wxTextCtrl* textCtrl = static_cast( ui_item.m_ThicknessCtrl ); + wxString txt = textCtrl->GetValue(); + + int item_thickness = ValueFromString( m_frame->GetUserUnits(), txt, true ); + thickness += item_thickness; } m_tcCTValue->SetValue( StringFromValue( m_units, thickness, true, true ) ); } -int PANEL_SETUP_BOARD_STACKUP::GetPcbTickness() +int PANEL_SETUP_BOARD_STACKUP::GetPcbThickness() { return ValueFromString( m_units, m_thicknessCtrl->GetValue(), true ); } @@ -292,12 +328,6 @@ void PANEL_SETUP_BOARD_STACKUP::synchronizeWithBoard( bool aFullSync ) { BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor(); - // Calculate copper layer count from m_enabledLayers, and *do not use* brd_stackup - // for that, because it is not necessary up to date - // (for instance after modifying the layer count from the panel layers in dialog) - LSET copperMask = m_enabledLayers & ( LSET::ExternalCuMask() | LSET::InternalCuMask() ); - int copperLayersCount = copperMask.count(); - if( aFullSync ) { int thickness = m_brdSettings->GetBoardThickness(); @@ -327,133 +357,128 @@ void PANEL_SETUP_BOARD_STACKUP::synchronizeWithBoard( bool aFullSync ) int row = 0; - for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() ) + for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item : m_rowUiItemsList ) { - BOARD_STACKUP_ROW_UI_ITEM& ui_row_item = m_rowUiItemsList[row]; - BOARD_STACKUP_ITEM* brd_stack_item = nullptr; + BOARD_STACKUP_ITEM* item = ui_row_item.m_Item; + int sub_item = ui_row_item.m_SubItem; - // test for existing stackup items in board: - for( BOARD_STACKUP_ITEM* brd_item : brd_stackup.GetList() ) + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - { - // Compare only BS_ITEM_TYPE_DIELECTRIC items - if( brd_item->GetType() != BS_ITEM_TYPE_DIELECTRIC ) - continue; + wxChoice* choice = dynamic_cast( ui_row_item.m_LayerTypeCtrl ); - if( item->GetDielectricLayerId() == brd_item->GetDielectricLayerId() ) - brd_stack_item = brd_item; - } - else if( item->GetBrdLayerId() == brd_item->GetBrdLayerId() ) - brd_stack_item = brd_item; - - if( brd_stack_item ) - break; + if( choice ) + choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 ); } - // Update panel stackup info if needed. If the board stackup item is not found - // just refresh the default values - if( brd_stack_item != nullptr && aFullSync ) + if( item->IsMaterialEditable() ) { - *item = *brd_stack_item; + wxTextCtrl* matName = dynamic_cast( ui_row_item.m_MaterialCtrl ); - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + if( matName ) { - wxChoice* choice = dynamic_cast( ui_row_item.m_LayerTypeCtrl ); - - if( choice ) - choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 ); - } - - if( item->IsMaterialEditable() ) - { - wxTextCtrl* matName = dynamic_cast( ui_row_item.m_MaterialCtrl ); - - if( matName ) - { - if( IsPrmSpecified( item->GetMaterial() ) ) - matName->SetValue( item->GetMaterial() ); - else - matName->SetValue( wxGetTranslation( NotSpecifiedPrm() ) ); - } - } - - if( item->IsThicknessEditable() ) - { - wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_ThicknessCtrl ); - - if( textCtrl ) - textCtrl->SetValue( StringFromValue( m_units, item->GetThickness(), true, true ) ); - - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - { - wxCheckBox* cb_box = dynamic_cast ( ui_row_item.m_ThicknessLockCtrl ); - - if( cb_box ) - cb_box->SetValue( item->IsThicknessLocked() ); - } - } - - if( item->IsColorEditable() ) - { - auto bm_combo = dynamic_cast( ui_row_item.m_ColorCtrl ); - int color_idx = 0; - - if( item->GetColor().StartsWith( "#" ) ) // User defined color - { - wxColour color( item->GetColor() ); - m_UserColors[row] = color; - color_idx = GetColorUserDefinedListIdx(); - - if( bm_combo ) // Update user color shown in the wxBitmapComboBox - { - bm_combo->SetString( color_idx, color.GetAsString( wxC2S_HTML_SYNTAX ) ); - wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y ); - LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D(), COLOR4D( color ) ); - bm_combo->SetItemBitmap( color_idx, layerbmp ); - } - } + if( IsPrmSpecified( item->GetMaterial( sub_item ) ) ) + matName->SetValue( item->GetMaterial( sub_item ) ); else - { - const FAB_LAYER_COLOR* color_list = GetColorStandardList(); - - for( int ii = 0; ii < GetColorStandardListCount(); ii++ ) - { - if( color_list[ii].m_ColorName == item->GetColor() ) - { - color_idx = ii; - break; - } - } - } - - if( bm_combo ) - bm_combo->SetSelection( color_idx ); - } - - if( item->HasEpsilonRValue() ) - { - wxString txt; - txt.Printf( "%.1f", item->GetEpsilonR() ); - wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_EpsilonCtrl ); - - if( textCtrl ) - textCtrl->SetValue( txt ); - } - - if( item->HasLossTangentValue() ) - { - wxString txt; - txt.Printf( "%g", item->GetLossTangent() ); - wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_LossTgCtrl ); - - if( textCtrl ) - textCtrl->SetValue( txt ); + matName->SetValue( wxGetTranslation( NotSpecifiedPrm() ) ); } } - // Now enable/disable stackup items, according to the m_enabledLayers config + if( item->IsThicknessEditable() ) + { + wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_ThicknessCtrl ); + + if( textCtrl ) + textCtrl->SetValue( StringFromValue( m_units, + item->GetThickness( sub_item ), true, true ) ); + + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + { + wxCheckBox* cb_box = dynamic_cast ( ui_row_item.m_ThicknessLockCtrl ); + + if( cb_box ) + cb_box->SetValue( item->IsThicknessLocked( sub_item ) ); + } + } + + if( item->IsColorEditable() ) + { + auto bm_combo = dynamic_cast( ui_row_item.m_ColorCtrl ); + int color_idx = 0; + + if( item->GetColor().StartsWith( "#" ) ) // User defined color + { + wxColour color( item->GetColor() ); + m_UserColors[row] = color; + color_idx = GetColorUserDefinedListIdx(); + + if( bm_combo ) // Update user color shown in the wxBitmapComboBox + { + bm_combo->SetString( color_idx, color.GetAsString( wxC2S_HTML_SYNTAX ) ); + wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y ); + LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D(), COLOR4D( color ) ); + bm_combo->SetItemBitmap( color_idx, layerbmp ); + } + } + else + { + const FAB_LAYER_COLOR* color_list = GetColorStandardList(); + + for( int ii = 0; ii < GetColorStandardListCount(); ii++ ) + { + if( color_list[ii].m_ColorName == item->GetColor() ) + { + color_idx = ii; + break; + } + } + } + + if( bm_combo ) + bm_combo->SetSelection( color_idx ); + } + + if( item->HasEpsilonRValue() ) + { + wxString txt; + txt.Printf( "%.1f", item->GetEpsilonR( sub_item ) ); + wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_EpsilonCtrl ); + + if( textCtrl ) + textCtrl->SetValue( txt ); + } + + if( item->HasLossTangentValue() ) + { + wxString txt; + txt.Printf( "%g", item->GetLossTangent( sub_item ) ); + wxTextCtrl* textCtrl = dynamic_cast( ui_row_item.m_LossTgCtrl ); + + if( textCtrl ) + textCtrl->SetValue( txt ); + } + } + + // Now enable/disable stackup items, according to the m_enabledLayers config + showOnlyActiveLayers(); + + updateIconColor(); +} + + +void PANEL_SETUP_BOARD_STACKUP::showOnlyActiveLayers() +{ + + // Now enable/disable stackup items, according to the m_enabledLayers config + // Calculate copper layer count from m_enabledLayers, and *do not use* brd_stackup + // for that, because it is not necessary up to date + // (for instance after modifying the layer count from the panel layers in dialog) + LSET copperMask = m_enabledLayers & ( LSET::ExternalCuMask() | LSET::InternalCuMask() ); + int copperLayersCount = copperMask.count(); + + for( BOARD_STACKUP_ROW_UI_ITEM& ui_row_item: m_rowUiItemsList ) + { bool show_item; + BOARD_STACKUP_ITEM* item = ui_row_item.m_Item; if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) // the m_DielectricLayerId is not a copper layer id, it is a dielectric idx from 1 @@ -479,11 +504,7 @@ void PANEL_SETUP_BOARD_STACKUP::synchronizeWithBoard( bool aFullSync ) ui_row_item.m_ColorCtrl->Show( show_item ); ui_row_item.m_EpsilonCtrl->Show( show_item ); ui_row_item.m_LossTgCtrl->Show( show_item ); - - row++; } - - updateIconColor(); } @@ -528,45 +549,43 @@ wxControl* PANEL_SETUP_BOARD_STACKUP::addSpacer() } -void PANEL_SETUP_BOARD_STACKUP::buildLayerStackPanel() +BOARD_STACKUP_ROW_UI_ITEM PANEL_SETUP_BOARD_STACKUP::createRowData( int aRow, + BOARD_STACKUP_ITEM* aStackupItem, int aSublayerIdx ) { - // for dielectric: layer type keyword is "core" - m_core_prepreg_choice.Add( _( "Core" ) ); - // for dielectric: layer type keyword is "prepreg" - m_core_prepreg_choice.Add( _( "PrePreg" ) ); + wxASSERT( aStackupItem ); + wxASSERT( aSublayerIdx >= 0 && aSublayerIdx < aStackupItem->GetSublayersCount() ); - // Build a full stackup for the dialog, with a active copper layer count - // = current board layer count to calculate a reasonable default - // dielectric thickness, for board having no stackup initalized: - m_stackup.BuildDefaultStackupList( nullptr, m_brdSettings->GetCopperLayerCount() ); - - int row = 0; + BOARD_STACKUP_ROW_UI_ITEM ui_row_item( aStackupItem, aSublayerIdx ); + BOARD_STACKUP_ITEM* item = aStackupItem; + int row = aRow; const FAB_LAYER_COLOR* color_list = GetColorStandardList(); - for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() ) + // Add color swatch icon. The color will be updated later, + // when all widgets are initialized + wxStaticBitmap* bitmap = new wxStaticBitmap( m_scGridWin, wxID_ANY, wxNullBitmap ); + m_fgGridSizer->Add( bitmap, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT ); + ui_row_item.m_Icon = bitmap; + + ui_row_item.m_isEnabled = true; + + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - BOARD_STACKUP_ROW_UI_ITEM ui_row_item( item ); + wxString lname = item->FormatDielectricLayerName(); - bool show_item = true;//false; - ui_row_item.m_isEnabled = true; - - // Reserve room in m_UserColors to store usercolor definition - m_UserColors.push_back( color_list[GetColorUserDefinedListIdx()].m_Color ); - - // Add color swatch icon. The color will be updated later, - // when all widgets are initialized - wxStaticBitmap* bitmap = new wxStaticBitmap( m_scGridWin, wxID_ANY, wxNullBitmap ); - m_fgGridSizer->Add( bitmap, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT ); - ui_row_item.m_Icon = bitmap; - - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + if( item->GetSublayersCount() > 1 ) { - wxString lname = item->FormatDielectricLayerName(); - wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname ); - m_fgGridSizer->Add( st_text, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - ui_row_item.m_LayerName = st_text; + lname << " (" << aSublayerIdx+1 << "/" << item->GetSublayersCount() << ")"; + } + wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname ); + m_fgGridSizer->Add( st_text, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + ui_row_item.m_LayerName = st_text; + + // For a dielectric layer, the layer type choice is not for each sublayer, + // only for the first (aSublayerIdx = 0), and is common to all sublayers + if( aSublayerIdx == 0 ) + { wxChoice* choice = new wxChoice( m_scGridWin, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_core_prepreg_choice ); choice->SetSelection( item->GetTypeName() == KEY_CORE ? 0 : 1 ); @@ -575,139 +594,268 @@ void PANEL_SETUP_BOARD_STACKUP::buildLayerStackPanel() ui_row_item.m_LayerTypeCtrl = choice; } else + ui_row_item.m_LayerTypeCtrl = addSpacer(); + } + else + { + item->SetLayerName( m_board->GetLayerName( item->GetBrdLayerId() ) ); + wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, item->GetLayerName() ); + m_fgGridSizer->Add( st_text, 0, wxALL|wxALIGN_CENTER_VERTICAL, 1 ); + st_text->Show( true ); + ui_row_item.m_LayerName = st_text; + + wxString lname; + + if( item->GetTypeName() == KEY_COPPER ) + lname = _( "Copper" ); + else + lname = wxGetTranslation( item->GetTypeName() ); + + st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname ); + m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + ui_row_item.m_LayerTypeCtrl = st_text; + } + + if( item->IsMaterialEditable() ) + { + wxString matName = item->GetMaterial( aSublayerIdx ); + addMaterialChooser( ID_ITEM_MATERIAL+row, &matName, ui_row_item ); + } + else + { + ui_row_item.m_MaterialCtrl = addSpacer(); + } + + if( item->IsThicknessEditable() ) + { + wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, ID_ITEM_THICKNESS+row ); + textCtrl->SetMinSize( m_numericTextCtrlSize ); + textCtrl->SetValue( StringFromValue( m_units, item->GetThickness( aSublayerIdx ), + true, true ) ); + m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + m_controlItemsList.push_back( textCtrl ); + textCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, + wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ), + NULL, this ); + ui_row_item.m_ThicknessCtrl = textCtrl; + + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - item->SetLayerName( m_board->GetLayerName( item->GetBrdLayerId() ) ); - wxStaticText* st_text = new wxStaticText( m_scGridWin, wxID_ANY, item->GetLayerName() ); - m_fgGridSizer->Add( st_text, 0, wxALL|wxALIGN_CENTER_VERTICAL, 1 ); - st_text->Show( show_item ); - ui_row_item.m_LayerName = st_text; - - wxString lname; - - if( item->GetTypeName() == KEY_COPPER ) - lname = _( "Copper" ); - else - lname = wxGetTranslation( item->GetTypeName() ); - - st_text = new wxStaticText( m_scGridWin, wxID_ANY, lname ); - m_fgGridSizer->Add( st_text, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - ui_row_item.m_LayerTypeCtrl = st_text; - } - - if( item->IsMaterialEditable() ) - { - wxString matName = item->GetMaterial(); - addMaterialChooser( ID_ITEM_MATERIAL+row, &matName, ui_row_item ); + wxCheckBox* cb_box = new wxCheckBox( m_scGridWin, ID_ITEM_THICKNESS_LOCKED+row, + wxEmptyString ); + cb_box->SetValue( item->IsThicknessLocked( aSublayerIdx ) ); + m_fgGridSizer->Add( cb_box, 0, wxALIGN_CENTER_VERTICAL, 2 ); + ui_row_item.m_ThicknessLockCtrl = cb_box; } else { - ui_row_item.m_MaterialCtrl = addSpacer(); - } - - if( item->IsThicknessEditable() ) - { - wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, ID_ITEM_THICKNESS+row ); - textCtrl->SetMinSize( m_numericTextCtrlSize ); - textCtrl->SetValue( StringFromValue( m_units, item->GetThickness(), true, true ) ); - m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - m_controlItemsList.push_back( textCtrl ); - textCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, - wxCommandEventHandler( PANEL_SETUP_BOARD_STACKUP::onThicknessChange ), - NULL, this ); - ui_row_item.m_ThicknessCtrl = textCtrl; - - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - { - wxCheckBox* cb_box = new wxCheckBox( m_scGridWin, ID_ITEM_THICKNESS_LOCKED+row, - wxEmptyString ); - cb_box->SetValue( item->IsThicknessLocked() ); - m_fgGridSizer->Add( cb_box, 0, wxALIGN_CENTER_VERTICAL, 2 ); - ui_row_item.m_ThicknessLockCtrl = cb_box; - } - else - { - ui_row_item.m_ThicknessLockCtrl = addSpacer(); - } - } - else - { - ui_row_item.m_ThicknessCtrl = addSpacer(); ui_row_item.m_ThicknessLockCtrl = addSpacer(); } + } + else + { + ui_row_item.m_ThicknessCtrl = addSpacer(); + ui_row_item.m_ThicknessLockCtrl = addSpacer(); + } - if( item->IsColorEditable() ) + if( item->IsColorEditable() ) + { + int color_idx = 0; + + if( item->GetColor().StartsWith( "#" ) ) // User defined color { - int color_idx = 0; - - if( item->GetColor().StartsWith( "#" ) ) // User defined color + wxColour color( item->GetColor() ); + m_UserColors[row] = color; + color_idx = GetColorUserDefinedListIdx(); + } + else + { + for( int ii = 0; ii < GetColorStandardListCount(); ii++ ) { - wxColour color( item->GetColor() ); - m_UserColors[row] = color; - color_idx = GetColorUserDefinedListIdx(); - } - else - { - for( int ii = 0; ii < GetColorStandardListCount(); ii++ ) + if( color_list[ii].m_ColorName == item->GetColor() ) { - if( color_list[ii].m_ColorName == item->GetColor() ) + color_idx = ii; + break; + } + } + } + + wxBitmapComboBox* bm_combo = createBmComboBox( item, row ); + m_colorComboSize.y = bm_combo->GetSize().y; + bm_combo->SetMinSize( m_colorComboSize ); + m_fgGridSizer->Add( bm_combo, 0, wxEXPAND|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + bm_combo->SetSelection( color_idx ); + ui_row_item.m_ColorCtrl = bm_combo; + } + else + { + ui_row_item.m_ColorCtrl = addSpacer(); + } + + if( item->HasEpsilonRValue() ) + { + wxString txt; + txt.Printf( "%.1f", item->GetEpsilonR( aSublayerIdx ) ); + wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString, + wxDefaultPosition, m_numericFieldsSize ); + textCtrl->SetValue( txt ); + m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + ui_row_item.m_EpsilonCtrl = textCtrl; + } + else + { + ui_row_item.m_EpsilonCtrl = addSpacer(); + } + + if( item->HasLossTangentValue() ) + { + wxString txt; + txt.Printf( "%g", item->GetLossTangent( aSublayerIdx ) ); + wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString, + wxDefaultPosition, m_numericFieldsSize ); + textCtrl->SetValue( txt ); + m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); + ui_row_item.m_LossTgCtrl = textCtrl; + } + else + { + ui_row_item.m_LossTgCtrl = addSpacer(); + } + + return ui_row_item; +} + + +void PANEL_SETUP_BOARD_STACKUP::rebuildLayerStackPanel() +{ + // Rebuild the stackup for the dialog, after dielectric parameters list is modified + // (added/removed): + + // First, delete all ui objects, because wxID values will be no longer valid for many widgets + disconnectEvents(); + m_controlItemsList.clear(); + + // Delete widgets (handled by the wxPanel parent) + for( BOARD_STACKUP_ROW_UI_ITEM ui_item: m_rowUiItemsList ) + { + wxSizer* sizerMat = ui_item.m_MaterialCtrl->GetSizer(); + delete ui_item.m_Icon; // Color icon in first column (column 1) + delete ui_item.m_LayerName; // string shown in column 2 + delete ui_item.m_LayerTypeCtrl; // control shown in column 3 + delete ui_item.m_MaterialCtrl; // control shown in column 4, with m_MaterialButt + delete ui_item.m_MaterialButt; // control shown in column 4, with m_MaterialCtrl + delete ui_item.m_ThicknessCtrl; // control shown in column 5 + delete ui_item.m_ThicknessLockCtrl;// control shown in column 6 + delete ui_item.m_ColorCtrl; // control shown in column 7 + delete ui_item.m_EpsilonCtrl; // control shown in column 8 + delete ui_item.m_LossTgCtrl; // control shown in column 9 + delete sizerMat; + } + + m_rowUiItemsList.clear(); + m_UserColors.clear(); + + // In order to recreate a clean grid layer list, we have to delete and + // recreate the sizer m_fgGridSizer (just deleting items in this size is not enough) + // therefore we also have to add the "old" title items to the newly recreated m_fgGridSizer: + delete m_fgGridSizer; + m_fgGridSizer = new wxFlexGridSizer( 0, 9, 0, 2 ); + m_fgGridSizer->SetFlexibleDirection( wxHORIZONTAL ); + m_fgGridSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + m_scGridWin->SetSizer( m_fgGridSizer ); + + // Re-add "old" title items: + m_fgGridSizer->Add( m_staticTextLayer, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_staticTextType, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_staticTextLayerId, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_fgGridSizer->Add( m_staticTextMaterial, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_staticTextThickness, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_bitmapLockThickness, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_fgGridSizer->Add( m_staticTextColor, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_staticTextEpsilonR, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + m_fgGridSizer->Add( m_staticTextLossTg, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 2 ); + + + // Now, rebuild the widget list from the new m_stackup items: + buildLayerStackPanel( false ); + + // Now enable/disable stackup items, according to the m_enabledLayers config + showOnlyActiveLayers(); + + m_scGridWin->Layout(); +} + + +void PANEL_SETUP_BOARD_STACKUP::buildLayerStackPanel( bool aCreatedInitialStackup ) +{ + wxWindowUpdateLocker locker( m_scGridWin ); + + // Build a full stackup for the dialog, with a active copper layer count + // = current board layer count to calculate a reasonable default stackup: + if( aCreatedInitialStackup ) + { + // Creates a full BOARD_STACKUP with 32 copper layers. + // extra layers will be hiden later. + // but if the number of layer is changed in the dialog, the corresponding + // widgets will be available with their previous values. + m_stackup.BuildDefaultStackupList( nullptr, m_brdSettings->GetCopperLayerCount() ); + BOARD_STACKUP& brd_stackup = m_brdSettings->GetStackupDescriptor(); + + // Now initialize all stackup items to the board values, when exist + for( BOARD_STACKUP_ITEM* item: m_stackup.GetList() ) + { + // Search for board settings: + for( BOARD_STACKUP_ITEM* board_item: brd_stackup.GetList() ) + { + if( item->GetBrdLayerId() != UNDEFINED_LAYER ) + { + if( item->GetBrdLayerId() == board_item->GetBrdLayerId() ) { - color_idx = ii; + *item = *board_item; + break; + } + } + else // dielectric layer: see m_DielectricLayerId for identification + { + // Compare dielectric layer with dielectric layer + if( board_item->GetBrdLayerId() != UNDEFINED_LAYER ) + continue; + + if( item->GetDielectricLayerId() == board_item->GetDielectricLayerId() ) + { + *item = *board_item; break; } } } - - wxBitmapComboBox* bm_combo = createBmComboBox( item, row ); - m_colorComboSize.y = bm_combo->GetSize().y; - bm_combo->SetMinSize( m_colorComboSize ); - m_fgGridSizer->Add( bm_combo, 0, wxEXPAND|wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - bm_combo->SetSelection( color_idx ); - ui_row_item.m_ColorCtrl = bm_combo; } - else - { - ui_row_item.m_ColorCtrl = addSpacer(); - } - - if( item->HasEpsilonRValue() ) - { - wxString txt; - txt.Printf( "%.1f", item->GetEpsilonR() ); - wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString, - wxDefaultPosition, m_numericFieldsSize ); - textCtrl->SetValue( txt ); - m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - ui_row_item.m_EpsilonCtrl = textCtrl; - } - else - { - ui_row_item.m_EpsilonCtrl = addSpacer(); - } - - if( item->HasLossTangentValue() ) - { - wxString txt; - txt.Printf( "%g", item->GetLossTangent() ); - wxTextCtrl* textCtrl = new wxTextCtrl( m_scGridWin, wxID_ANY, wxEmptyString, - wxDefaultPosition, m_numericFieldsSize ); - textCtrl->SetValue( txt ); - m_fgGridSizer->Add( textCtrl, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 2 ); - ui_row_item.m_LossTgCtrl = textCtrl; - } - else - { - ui_row_item.m_LossTgCtrl = addSpacer(); - } - - m_rowUiItemsList.push_back( ui_row_item ); - - row++; } - // Get the translated list of choices and init m_choiceFinish - wxArrayString finish_list = GetCopperFinishStandardList( true ); - m_choiceFinish->Append( finish_list ); - m_choiceFinish->SetSelection( 0 ); // Will be correctly set later + const FAB_LAYER_COLOR* color_list = GetColorStandardList(); + + int row = 0; + + for( BOARD_STACKUP_ITEM* item : m_stackup.GetList() ) + { + for( int sub_idx = 0; sub_idx < item->GetSublayersCount(); sub_idx++ ) + { + // Reserve room in m_UserColors to store usercolor definition + m_UserColors.push_back( color_list[GetColorUserDefinedListIdx()].m_Color ); + + BOARD_STACKUP_ROW_UI_ITEM ui_row_item = createRowData( row, item, sub_idx ); + m_rowUiItemsList.emplace_back( ui_row_item ); + + row++; + } + } + + if( aCreatedInitialStackup ) + { + // Get the translated list of choices and init m_choiceFinish + wxArrayString finish_list = GetCopperFinishStandardList( true ); + m_choiceFinish->Append( finish_list ); + m_choiceFinish->SetSelection( 0 ); // Will be correctly set later + } updateIconColor(); m_scGridWin->Layout(); @@ -721,8 +869,6 @@ bool PANEL_SETUP_BOARD_STACKUP::transferDataFromUIToStackup() // First, verify the list of layers currently in stackup: // if it does not mach the list of layers set in PANEL_SETUP_LAYERS // prompt the user to update the stackup - - // the current enabled layers in PANEL_SETUP_LAYERS LSET layersList = m_panelLayers->GetUILayerMask() & BOARD_STACKUP::StackupAllowedBrdLayers(); if( m_enabledLayers != layersList ) @@ -733,74 +879,40 @@ bool PANEL_SETUP_BOARD_STACKUP::transferDataFromUIToStackup() // The board thickness and the thickness from stackup settings should be compatible // so verify that compatibility - int pcbTickness = GetPcbTickness() ; + int pcbTickness = GetPcbThickness() ; int stackup_thickness = 0; - bool thickness_error = false; // Set to true if a negative thickness value is found - for( auto item : m_stackup.GetList() ) - { - if( item->IsThicknessEditable() && item->IsEnabled() ) - { - stackup_thickness += item->GetThickness(); - - if( item->GetThickness() < 0 ) - thickness_error = true; - } - } - - if( thickness_error ) - { - wxMessageBox( _( "A layer thickness is < 0. Fix it" ) ); - return false; - } - - int delta = std::abs( stackup_thickness - pcbTickness ); - double relative_error = (double)delta/pcbTickness; - - // warn user if relative_error > 0.01 - const double relative_error_max = 0.01; - - if( relative_error > relative_error_max ) - { - wxString msg; - msg.Printf( _( "Board thickness %s differs from stackup thickness %s\n" - "Allowed max error %s" ), - StringFromValue( m_units, pcbTickness, true, true ), - StringFromValue( m_units, stackup_thickness, true, true ), - StringFromValue( m_units, KiROUND( relative_error_max*pcbTickness), - true, true ) ); - - wxMessageBox( msg ); - return false; - } - - - int row = 0; wxString txt; wxString error_msg; bool success = true; double value; + int row = 0; - for( auto item : m_stackup.GetList() ) + for( BOARD_STACKUP_ROW_UI_ITEM& ui_item : m_rowUiItemsList ) { // Skip stackup items useless for the current board - if( !item->IsEnabled() ) - { - row++; + if( !ui_item.m_isEnabled ) continue; - } - item->SetLayerName( m_rowUiItemsList[row].m_LayerName->GetLabel() ); + BOARD_STACKUP_ITEM* item = ui_item.m_Item; + int sub_item = ui_item.m_SubItem; + + // Add sub layer if there is a new sub layer: + while( item->GetSublayersCount() <= sub_item ) + item->AddDielectricPrms( item->GetSublayersCount() ); + + if( sub_item == 0 ) // Name only main layer + item->SetLayerName( ui_item.m_LayerName->GetLabel() ); if( item->HasEpsilonRValue() ) { - wxTextCtrl* textCtrl = static_cast( m_rowUiItemsList[row].m_EpsilonCtrl ); + wxTextCtrl* textCtrl = static_cast( ui_item.m_EpsilonCtrl ); txt = textCtrl->GetValue(); if( txt.ToDouble( &value ) && value >= 0.0 ) - item->SetEpsilonR( value ); + item->SetEpsilonR( value, sub_item ); else if( txt.ToCDouble( &value ) && value >= 0.0 ) - item->SetEpsilonR( value ); + item->SetEpsilonR( value, sub_item ); else { success = false; @@ -810,13 +922,13 @@ bool PANEL_SETUP_BOARD_STACKUP::transferDataFromUIToStackup() if( item->HasLossTangentValue() ) { - wxTextCtrl* textCtrl = static_cast( m_rowUiItemsList[row].m_LossTgCtrl ); + wxTextCtrl* textCtrl = static_cast( ui_item.m_LossTgCtrl ); txt = textCtrl->GetValue(); if( txt.ToDouble( &value ) && value >= 0.0 ) - item->SetLossTangent( value ); + item->SetLossTangent( value, sub_item ); else if( txt.ToCDouble( &value ) && value >= 0.0 ) - item->SetLossTangent( value ); + item->SetLossTangent( value, sub_item ); else { success = false; @@ -828,49 +940,64 @@ bool PANEL_SETUP_BOARD_STACKUP::transferDataFromUIToStackup() if( item->IsMaterialEditable() ) { - wxTextCtrl* textCtrl = static_cast( m_rowUiItemsList[row].m_MaterialCtrl ); - item->SetMaterial( textCtrl->GetValue() ); + wxTextCtrl* textCtrl = static_cast( ui_item.m_MaterialCtrl ); + item->SetMaterial( textCtrl->GetValue(), sub_item ); // Ensure the not specified mat name is the keyword, not its translation // to avoid any issue is the language setting changes - if( !IsPrmSpecified( item->GetMaterial() ) ) - item->SetMaterial( NotSpecifiedPrm() ); + if( !IsPrmSpecified( item->GetMaterial( sub_item ) ) ) + item->SetMaterial( NotSpecifiedPrm(), sub_item ); } if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - // Choice is Core or Prepreg: - wxChoice* choice = static_cast( m_rowUiItemsList[row].m_LayerTypeCtrl ); + // Choice is Core or Prepreg. Sublayers have no choice: + wxChoice* choice = dynamic_cast( ui_item.m_LayerTypeCtrl ); - int idx = choice->GetSelection(); + if( choice ) + { + int idx = choice->GetSelection(); - if( idx == 0 ) - item->SetTypeName( KEY_CORE ); - else - item->SetTypeName( KEY_PREPREG ); + if( idx == 0 ) + item->SetTypeName( KEY_CORE ); + else + item->SetTypeName( KEY_PREPREG ); + } } if( item->IsThicknessEditable() ) { - wxTextCtrl* textCtrl = static_cast( m_rowUiItemsList[row].m_ThicknessCtrl ); + wxTextCtrl* textCtrl = static_cast( ui_item.m_ThicknessCtrl ); txt = textCtrl->GetValue(); - item->SetThickness( ValueFromString( m_frame->GetUserUnits(), txt, true ) ); + int new_thickness = ValueFromString( m_frame->GetUserUnits(), txt, true ); + item->SetThickness( new_thickness, sub_item ); + stackup_thickness += new_thickness; + + if( new_thickness < 0 ) + { + success = false; + + if( !error_msg.IsEmpty() ) + error_msg << "\n"; + + error_msg << _( "A layer thickness is < 0. Fix it" ); + } if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { // Dielectric thickness layer can have a locked thickness: wxCheckBox* cb_box = static_cast - ( m_rowUiItemsList[row].m_ThicknessLockCtrl ); - item->SetThicknessLocked( cb_box && cb_box->GetValue() ); + ( ui_item.m_ThicknessLockCtrl ); + item->SetThicknessLocked( cb_box && cb_box->GetValue(), sub_item ); } } - if( item->IsColorEditable() ) + if( sub_item == 0 && item->IsColorEditable() ) { const FAB_LAYER_COLOR* color_list = GetColorStandardList(); - wxBitmapComboBox* choice = static_cast( m_rowUiItemsList[row].m_ColorCtrl ); + wxBitmapComboBox* choice = static_cast( ui_item.m_ColorCtrl ); int idx = choice->GetSelection(); if( idx == GetColorUserDefinedListIdx() ) @@ -878,9 +1005,33 @@ bool PANEL_SETUP_BOARD_STACKUP::transferDataFromUIToStackup() else item->SetColor( color_list[idx].m_ColorName ); } + row++; } + int delta = std::abs( stackup_thickness - pcbTickness ); + double relative_error = (double)delta/pcbTickness; + const double relative_error_max = 0.01; + + // warn user if relative_error > 0.01 + if( relative_error > relative_error_max ) + { + wxString msg; + msg.Printf( _( "Board thickness %s differs from stackup thickness %s\n" + "Allowed max error %s" ), + StringFromValue( m_units, pcbTickness, true, true ), + StringFromValue( m_units, stackup_thickness, true, true ), + StringFromValue( m_units, KiROUND( relative_error_max*pcbTickness), + true, true ) ); + + if( !error_msg.IsEmpty() ) + error_msg << "\n"; + + error_msg << msg; + + success = false; + } + if( !success ) { wxMessageBox( error_msg, _( "Errors" ) ); @@ -918,10 +1069,10 @@ bool PANEL_SETUP_BOARD_STACKUP::TransferDataFromWindow() for( auto item : m_stackup.GetList() ) { if( item->IsEnabled() ) - brd_stackup.Add( new BOARD_STACKUP_ITEM( *item ) ); + brd_stackup.Add( new BOARD_STACKUP_ITEM( *item ) ); } - m_brdSettings->SetBoardThickness( GetPcbTickness() ); + m_brdSettings->SetBoardThickness( GetPcbThickness() ); m_brdSettings->m_HasStackup = true; return true; @@ -940,8 +1091,7 @@ void PANEL_SETUP_BOARD_STACKUP::ImportSettingsFrom( BOARD* aBoard ) m_brdSettings = savedSettings; m_board = savedBrd; - Layout(); - Refresh(); + rebuildLayerStackPanel(); } @@ -952,6 +1102,7 @@ void PANEL_SETUP_BOARD_STACKUP::OnLayersOptionsChanged( LSET aNewLayerSet ) // rebuild the panel // the current enabled layers in PANEL_SETUP_LAYERS + // Note: the number of layer can change, but not the layers properties LSET layersList = m_panelLayers->GetUILayerMask() & BOARD_STACKUP::StackupAllowedBrdLayers(); if( m_enabledLayers != layersList ) @@ -972,36 +1123,34 @@ void PANEL_SETUP_BOARD_STACKUP::onCalculateDielectricThickness( wxCommandEvent& int thickness = 0; int fixed_thickness_cnt = 0; bool thickness_error = false; // True if a locked thickness value in list is < 0 + int dielectricCount = 0; - int row = 0; - for( auto item : m_stackup.GetList() ) + for( BOARD_STACKUP_ROW_UI_ITEM& ui_item : m_rowUiItemsList ) { - if( !item->IsThicknessEditable() || !item->IsEnabled() ) - { - row++; + BOARD_STACKUP_ITEM* item = ui_item.m_Item; + int sublayer_idx = ui_item.m_SubItem; + + if( !item->IsThicknessEditable() || !ui_item.m_isEnabled ) continue; - } if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) { - wxCheckBox* checkBox = static_cast( m_rowUiItemsList[row].m_ThicknessLockCtrl ); + dielectricCount++; + + wxCheckBox* checkBox = static_cast( ui_item.m_ThicknessLockCtrl ); if( !checkBox->GetValue() ) // Only not locked dielectric thickness can be modified - { - row++; continue; - } else { fixed_thickness_cnt++; - if( item->GetThickness() < 0 ) + if( item->GetThickness( sublayer_idx ) < 0 ) thickness_error = true; } } - thickness += item->GetThickness(); - row++; + thickness += item->GetThickness( sublayer_idx ); } if( thickness_error ) @@ -1011,12 +1160,9 @@ void PANEL_SETUP_BOARD_STACKUP::onCalculateDielectricThickness( wxCommandEvent& return; } - LSET copperMask = m_enabledLayers & ( LSET::ExternalCuMask() | LSET::InternalCuMask() ); - int copperLayersCount = copperMask.count(); - // the number of adjustable dielectric layers must obvioulsly be > 0 // So verify the user has at least one dielectric layer free - int adjustableDielectricCount = copperLayersCount - 1 - fixed_thickness_cnt; + int adjustableDielectricCount = dielectricCount - fixed_thickness_cnt; if( adjustableDielectricCount <= 0 ) { @@ -1025,7 +1171,7 @@ void PANEL_SETUP_BOARD_STACKUP::onCalculateDielectricThickness( wxCommandEvent& return; } - int dielectric_thickness = GetPcbTickness() - thickness; + int dielectric_thickness = GetPcbThickness() - thickness; if( dielectric_thickness <= 0 ) // fixed thickness is too big: cannot calculate free thickness { @@ -1037,22 +1183,23 @@ void PANEL_SETUP_BOARD_STACKUP::onCalculateDielectricThickness( wxCommandEvent& dielectric_thickness /= adjustableDielectricCount; // Update items thickness, and the values displayed on screen - row = 0; - for( auto item : m_stackup.GetList() ) + for( BOARD_STACKUP_ROW_UI_ITEM& ui_item : m_rowUiItemsList ) { - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsEnabled() ) + BOARD_STACKUP_ITEM* item = ui_item.m_Item; + int sublayer_idx = ui_item.m_SubItem; + + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && ui_item.m_isEnabled ) { - wxCheckBox* checkBox = static_cast( m_rowUiItemsList[row].m_ThicknessLockCtrl ); + wxCheckBox* checkBox = static_cast( ui_item.m_ThicknessLockCtrl ); if( !checkBox->GetValue() ) // Not locked thickness: can be modified { - item->SetThickness( dielectric_thickness ); - wxTextCtrl* textCtrl = static_cast( m_rowUiItemsList[row].m_ThicknessCtrl ); - textCtrl->SetValue( StringFromValue( m_units, item->GetThickness(), true, true ) ); + item->SetThickness( dielectric_thickness, sublayer_idx ); + wxTextCtrl* textCtrl = static_cast( ui_item.m_ThicknessCtrl ); + textCtrl->SetValue( StringFromValue( m_units, item->GetThickness( sublayer_idx ), + true, true ) ); } } - - row++; } } @@ -1109,23 +1256,27 @@ void PANEL_SETUP_BOARD_STACKUP::onMaterialChange( wxCommandEvent& event ) else continue; - int idx = mat_list->FindSubstrate( item->GetMaterial(), - item->GetEpsilonR(), - item->GetLossTangent() ); - - if( idx < 0 && !item->GetMaterial().IsEmpty() ) + for( int ii = 0; ii < item->GetSublayersCount(); ii++ ) { - // This material is not in list: add it - DIELECTRIC_SUBSTRATE new_mat; - new_mat.m_Name = item->GetMaterial(); - new_mat.m_EpsilonR = item->GetEpsilonR(); - new_mat.m_LossTangent = item->GetLossTangent(); - mat_list->AppendSubstrate( new_mat ); + int idx = mat_list->FindSubstrate( item->GetMaterial( ii ), + item->GetEpsilonR( ii ), + item->GetLossTangent( ii ) ); + + if( idx < 0 && !item->GetMaterial().IsEmpty() ) + { + // This material is not in list: add it + DIELECTRIC_SUBSTRATE new_mat; + new_mat.m_Name = item->GetMaterial( ii ); + new_mat.m_EpsilonR = item->GetEpsilonR( ii ); + new_mat.m_LossTangent = item->GetLossTangent( ii ); + mat_list->AppendSubstrate( new_mat ); + } } } int row = event.GetId() - ID_ITEM_MATERIAL; - BOARD_STACKUP_ITEM* item = GetStackupItem( row ); + BOARD_STACKUP_ITEM* item = m_rowUiItemsList[row].m_Item; + int sub_item = m_rowUiItemsList[row].m_SubItem; DIELECTRIC_SUBSTRATE_LIST* item_mat_list = nullptr; switch( item->GetType() ) @@ -1158,13 +1309,13 @@ void PANEL_SETUP_BOARD_STACKUP::onMaterialChange( wxCommandEvent& event ) return; // Update Name, Epsilon R and Loss tg - item->SetMaterial( substrate.m_Name ); - item->SetEpsilonR( substrate.m_EpsilonR ); - item->SetLossTangent( substrate.m_LossTangent ); + item->SetMaterial( substrate.m_Name, sub_item ); + item->SetEpsilonR( substrate.m_EpsilonR, sub_item ); + item->SetLossTangent( substrate.m_LossTangent, sub_item ); wxTextCtrl* textCtrl; textCtrl = static_cast( m_rowUiItemsList[row].m_MaterialCtrl ); - textCtrl->SetValue( item->GetMaterial() ); + textCtrl->SetValue( item->GetMaterial( sub_item ) ); // some layers have a material choice but not EpsilonR ctrl if( item->HasEpsilonRValue() ) @@ -1172,7 +1323,7 @@ void PANEL_SETUP_BOARD_STACKUP::onMaterialChange( wxCommandEvent& event ) textCtrl = dynamic_cast( m_rowUiItemsList[row].m_EpsilonCtrl ); if( textCtrl ) - textCtrl->SetValue( item->FormatEpsilonR() ); + textCtrl->SetValue( item->FormatEpsilonR( sub_item ) ); } // some layers have a material choice but not loss tg ctrl @@ -1181,7 +1332,7 @@ void PANEL_SETUP_BOARD_STACKUP::onMaterialChange( wxCommandEvent& event ) textCtrl = dynamic_cast( m_rowUiItemsList[row].m_LossTgCtrl ); if( textCtrl ) - textCtrl->SetValue( item->FormatLossTangent() ); + textCtrl->SetValue( item->FormatLossTangent( sub_item ) ); } } @@ -1192,22 +1343,35 @@ void PANEL_SETUP_BOARD_STACKUP::onThicknessChange( wxCommandEvent& event ) wxString value = event.GetString(); BOARD_STACKUP_ITEM* item = GetStackupItem( row ); - item->SetThickness( ValueFromString( m_frame->GetUserUnits(), value, true ) ); + int idx = GetSublayerId( row ); + + item->SetThickness( ValueFromString( m_frame->GetUserUnits(), value, true ), idx ); } -BOARD_STACKUP_ITEM* PANEL_SETUP_BOARD_STACKUP::GetStackupItem( int aIndex ) +BOARD_STACKUP_ITEM* PANEL_SETUP_BOARD_STACKUP::GetStackupItem( int aRow ) { - return m_stackup.GetStackupLayer( aIndex ); + return m_rowUiItemsList[aRow].m_Item; +} + + +int PANEL_SETUP_BOARD_STACKUP::GetSublayerId( int aRow ) +{ + return m_rowUiItemsList[aRow].m_SubItem; } wxColor PANEL_SETUP_BOARD_STACKUP::getColorIconItem( int aRow ) { - BOARD_STACKUP_ITEM* layer = GetStackupItem( aRow ); + BOARD_STACKUP_ITEM* st_item = dynamic_cast( GetStackupItem( aRow ) ); + + wxASSERT( st_item ); wxColor color; - switch( layer->GetType() ) + if( ! st_item ) + return color; + + switch( st_item->GetType() ) { case BS_ITEM_TYPE_COPPER: color = copperColor; diff --git a/pcbnew/board_stackup_manager/panel_board_stackup.h b/pcbnew/board_stackup_manager/panel_board_stackup.h index b5ede324b1..6b27573931 100644 --- a/pcbnew/board_stackup_manager/panel_board_stackup.h +++ b/pcbnew/board_stackup_manager/panel_board_stackup.h @@ -49,6 +49,9 @@ class PANEL_SETUP_LAYERS; struct BOARD_STACKUP_ROW_UI_ITEM { BOARD_STACKUP_ITEM* m_Item; // The BOARD_STACKUP_ITEM managed by this BOARD_STACKUP_ROW_UI_ITEM + int m_SubItem; // For multilayer dielectric, the index in sublayer list. + // Must be >= 0 and < m_Item sublayer count. Used only for dielectic + // 0 is the base list of parameters (always existing) bool m_isEnabled; // True if the row is in board // false if not (this row is not shown on the panel) wxStaticBitmap* m_Icon; // Color icon in first column (column 1) @@ -62,8 +65,8 @@ struct BOARD_STACKUP_ROW_UI_ITEM wxControl* m_EpsilonCtrl; // control shown in column 8 wxControl* m_LossTgCtrl; // control shown in column 9 - BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem ) : - m_Item( aItem ), + BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem, int aSubItem = 1 ) : + m_Item( aItem ), m_SubItem( aSubItem ), m_isEnabled( true ), m_Icon( nullptr ), m_LayerName( nullptr ), m_LayerTypeCtrl( nullptr ), m_MaterialCtrl( nullptr ),m_MaterialButt( nullptr ), @@ -92,13 +95,16 @@ public: */ void OnLayersOptionsChanged( LSET aNewLayerSet ); - BOARD_STACKUP_ITEM* GetStackupItem( int aIndex ); + /// @return the BOARD_STACKUP_ITEM managed by the row aRow + BOARD_STACKUP_ITEM* GetStackupItem( int aRow ); + /// @return the BOARD_STACKUP_ITEM sublayermanaged by the row aRow + int GetSublayerId( int aRow ); /// Return the color currently selected for the row aRow wxColor GetSelectedColor( int aRow ) const; BOARD_STACKUP& GetStackup() { return m_stackup; } - int GetPcbTickness(); + int GetPcbThickness(); // Called by wxWidgets: transfer current settings stored in m_stackup to the board bool TransferDataFromWindow() override; @@ -106,6 +112,18 @@ public: std::vector m_UserColors; // the list of user colors for each grid row // other colors are defined colors, and are not stored private: + /** Creates a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem. + * @return a BOARD_STACKUP_ROW_UI_ITEM filled with corresponding widgets + * @param aRow is the row index in the row list + * @param aStackupItem is the stackup item controlled by the created + * BOARD_STACKUP_ROW_UI_ITEM. + * @param aSublayerIdx is used only for BS_ITEM_TYPE_DIELECTRIC stackup items. + * this is the index of the sublayer to used inside aStackupItem + * (from 0 to sub layer count - 1) + */ + BOARD_STACKUP_ROW_UI_ITEM createRowData( int aRow, BOARD_STACKUP_ITEM* aStackupItem, + int aSublayerIdx ); + /** add a Spacer in m_fgGridSizer when a empty cell is needed */ wxControl* addSpacer(); @@ -124,20 +142,31 @@ private: * all copper layers and all tech layers that are supported by the stackup * items not in the current board stackup will be not shown, but they are * existing in list + * @param aCreatedInitialStackup = true to create a initial stackup list for the dialog + * false to build the stackup panel from the existing stackup list. */ - void buildLayerStackPanel(); + void buildLayerStackPanel( bool aCreatedInitialStackup ); - /** Show or do not show items in m_fgGridSizer according to the stackup of the + /** Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the * current board and optionally update the stackup params (thickness, color ... ) * @param aFullSync = true to update stackup params, false to only update the list * of shown items */ void synchronizeWithBoard( bool aFullSync ); + /** Show or do not show items in m_fgGridSizer according to the stackup of the + * current board. + * The panel stackup stores all posible layers (because the number of layers is set + * from an other panel), but only some of them must be actually shown on screen + */ + void showOnlyActiveLayers(); + /** Populate m_fgGridSizer with items to handle stackup parameters * If previous items are in list, remove old items + * New prms are added + * must be called after adding or deleting a dielectric parameter set */ - void RebuildLayerStackPanel(); + void rebuildLayerStackPanel(); /** Transfer current UI settings to m_stackup but not to the board */ diff --git a/pcbnew/exporters/gerber_jobfile_writer.cpp b/pcbnew/exporters/gerber_jobfile_writer.cpp index eec21cb4f6..d294d34567 100644 --- a/pcbnew/exporters/gerber_jobfile_writer.cpp +++ b/pcbnew/exporters/gerber_jobfile_writer.cpp @@ -614,150 +614,173 @@ void GERBER_JOBFILE_WRITER::addJSONMaterialStackup() for( int ii = 0; ii < brd_stackup.GetCount(); ++ii ) { BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii ); - double thickness = item->GetThickness()*m_conversionUnits; // layer thickness is always in mm - wxString layer_type; - std::string layer_name; // for comment - switch( item->GetType() ) + int sub_layer_count = item->GetType() == BS_ITEM_TYPE_DIELECTRIC + ? item->GetSublayersCount() : 1; + + for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ ) { - case BS_ITEM_TYPE_COPPER: - layer_type = "Copper"; - layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) ); - last_copper_layer = item->GetBrdLayerId(); - break; + // layer thickness is always in mm + double thickness = item->GetThickness( sub_idx )*m_conversionUnits; + wxString layer_type; + std::string layer_name; // for comment - case BS_ITEM_TYPE_SILKSCREEN: - layer_type = "Legend"; - layer_name = formatStringFromUTF32( item->GetTypeName() ); - break; - - case BS_ITEM_TYPE_SOLDERMASK: - layer_type = "SolderMask"; - layer_name = formatStringFromUTF32( item->GetTypeName() ); - break; - - case BS_ITEM_TYPE_SOLDERPASTE: - layer_type = "SolderPaste"; - layer_name = formatStringFromUTF32( item->GetTypeName() ); - break; - - case BS_ITEM_TYPE_DIELECTRIC: - layer_type = "Dielectric"; - // The option core or prepreg is not added here, as it creates constraints - // in build process, not necessary wanted. - layer_name = formatStringFromUTF32( wxString::Format( "dielectric layer %d", - item->GetDielectricLayerId() ) ); - break; - - default: - break; - } - - openBlock(); - addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) ); - - if( item->IsColorEditable() && uptodate ) - { - if( IsPrmSpecified( item->GetColor() ) ) + switch( item->GetType() ) { - wxString colorName = item->GetColor(); + case BS_ITEM_TYPE_COPPER: + layer_type = "Copper"; + layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) ); + last_copper_layer = item->GetBrdLayerId(); + break; - if( colorName.StartsWith( "#" ) ) // This is a user defined color. + case BS_ITEM_TYPE_SILKSCREEN: + layer_type = "Legend"; + layer_name = formatStringFromUTF32( item->GetTypeName() ); + break; + + case BS_ITEM_TYPE_SOLDERMASK: + layer_type = "SolderMask"; + layer_name = formatStringFromUTF32( item->GetTypeName() ); + break; + + case BS_ITEM_TYPE_SOLDERPASTE: + layer_type = "SolderPaste"; + layer_name = formatStringFromUTF32( item->GetTypeName() ); + break; + + case BS_ITEM_TYPE_DIELECTRIC: + layer_type = "Dielectric"; + // The option core or prepreg is not added here, as it creates constraints + // in build process, not necessary wanted. + if( sub_layer_count > 1 ) { - // In job file a color can be given by its RGB values (0...255) - wxColor color( colorName ); - colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() ); + layer_name = formatStringFromUTF32( + wxString::Format( "dielectric layer %d - %d/%d", + item->GetDielectricLayerId(), + sub_idx+1, sub_layer_count ) ); } + else + layer_name = formatStringFromUTF32( + wxString::Format( "dielectric layer %d", + item->GetDielectricLayerId() ) ); + break; - addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) ); + default: + break; } - } - if( item->IsThicknessEditable() && uptodate ) - addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) ); + openBlock(); + addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) ); - if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) - { - if( item->HasMaterialValue() ) + if( item->IsColorEditable() && uptodate ) { - addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) ); - - // These constrains are only written if the board has impedance controlled tracks. - // If the board is not impedance controlled, they are useless. - // Do not add constrains that create more expensive boards. - if( brd_stackup.m_HasDielectricConstrains ) + if( IsPrmSpecified( item->GetColor() ) ) { - // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not - // a possible value - if( item->GetEpsilonR() > 1.0 ) - addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n", - item->FormatEpsilonR() ) ); + wxString colorName = item->GetColor(); - // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not - // a possible value - if( item->GetLossTangent() > 0.0 ) - addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", - item->FormatLossTangent() ) ); + if( colorName.StartsWith( "#" ) ) // This is a user defined color. + { + // In job file a color can be given by its RGB values (0...255) + wxColor color( colorName ); + colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() ); + } + + addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) ); } } - PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1); + if( item->IsThicknessEditable() && uptodate ) + addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) ); - // If the next_copper_layer is the last copper layer, the next layer id is B_Cu - if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 ) - next_copper_layer = B_Cu; + if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) + { + if( item->HasMaterialValue() ) + { + addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", + item->GetMaterial( sub_idx ) ) ); + // These constrains are only written if the board has impedance controlled tracks. + // If the board is not impedance controlled, they are useless. + // Do not add constrains that create more expensive boards. + if( brd_stackup.m_HasDielectricConstrains ) + { + // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not + // a possible value + if( item->GetEpsilonR() > 1.0 ) + addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n", + item->FormatEpsilonR( sub_idx ) ) ); - addJSONObject( wxString::Format( "\"Name\": \"%s/%s\",\n", + // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not + // a possible value + if( item->GetLossTangent() > 0.0 ) + addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", + item->FormatLossTangent( sub_idx ) ) ); + } + } + + PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1); + + // If the next_copper_layer is the last copper layer, the next layer id is B_Cu + if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 ) + next_copper_layer = B_Cu; + + wxString subLayerName; + + if( sub_layer_count > 1 ) + subLayerName.Printf( " (%d/%d)",sub_idx+1, sub_layer_count ); + + addJSONObject( wxString::Format( "\"Name\": \"%s/%s%s\",\n", + formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ), + formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ), + subLayerName ) + ); + + // Add a comment ("Notes"): + wxString note = "\"Notes\": "; + + note << wxString::Format( " \"Type: %s", layer_name.c_str() ); + + note << wxString::Format( " (from %s to %s)\"\n", formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ), - formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) ) - ); + formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) ); - // Add a comment ("Notes"): - wxString note = "\"Notes\": "; - - note << wxString::Format( " \"Type: %s", layer_name.c_str() ); - - note << wxString::Format( " (from %s to %s)\"\n", - formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ), - formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) ); - - addJSONObject( note ); - } - else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN ) - { - if( item->HasMaterialValue() ) + addJSONObject( note ); + } + else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN ) { - addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) ); - - // These constrains are only written if the board has impedance controlled tracks. - // If the board is not impedance controlled, they are useless. - // Do not add constrains that create more expensive boards. - if( brd_stackup.m_HasDielectricConstrains ) + if( item->HasMaterialValue() ) { - // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not - // a possible value - if( item->GetEpsilonR() > 1.0 ) - addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n", - item->FormatEpsilonR() ) ); + addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) ); - // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not - // a possible value - if( item->GetLossTangent() > 0.0 ) - addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", - item->FormatLossTangent() ) ); + // These constrains are only written if the board has impedance controlled tracks. + // If the board is not impedance controlled, they are useless. + // Do not add constrains that create more expensive boards. + if( brd_stackup.m_HasDielectricConstrains ) + { + // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not + // a possible value + if( item->GetEpsilonR() > 1.0 ) + addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n", + item->FormatEpsilonR() ) ); + + // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not + // a possible value + if( item->GetLossTangent() > 0.0 ) + addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", + item->FormatLossTangent() ) ); + } } + + addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) ); + } + else + { + addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) ); } - addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) ); + removeJSONSepararator(); + closeBlockWithSep(); } - else - { - addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) ); - } - - removeJSONSepararator(); - closeBlockWithSep(); } removeJSONSepararator(); diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 5920e01e80..60af80a628 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -1075,72 +1075,90 @@ void PCB_PARSER::parseBoardStackup() else Expecting( "layer_name" ); - // Dielectric thickness can be locked (for impedance controled layers) - bool thickness_locked = false; + bool has_next_sublayer = true; + int sublayer_idx = 0; // the index of dielectric sub layers + // sublayer 0 is always existing (main sublayer) - for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + while( has_next_sublayer ) { - if( token == T_LEFT ) + has_next_sublayer = false; + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) { - token = NextTok(); - - switch( token ) + if( token == T_addsublayer ) { - case T_type: - NeedSYMBOL(); - item->SetTypeName( FromUTF8() ); - NeedRIGHT(); + has_next_sublayer = true; break; + } - case T_thickness: - item->SetThickness( parseBoardUnits( T_thickness ) ); + if( token == T_LEFT ) + { token = NextTok(); - if( token == T_LEFT ) + switch( token ) + { + case T_type: + NeedSYMBOL(); + item->SetTypeName( FromUTF8() ); + NeedRIGHT(); break; - if( token == T_locked ) - { - thickness_locked = true; + case T_thickness: + item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx ); + token = NextTok(); + + if( token == T_LEFT ) + break; + + if( token == T_locked ) + { + // Dielectric thickness can be locked (for impedance controled layers) + if( type == BS_ITEM_TYPE_DIELECTRIC ) + item->SetThicknessLocked( true, sublayer_idx ); + + NeedRIGHT(); + } + break; + + case T_material: + NeedSYMBOL(); + item->SetMaterial( FromUTF8(), sublayer_idx ); NeedRIGHT(); + break; + + case T_epsilon_r: + NextTok(); + item->SetEpsilonR( parseDouble(), sublayer_idx ); + NeedRIGHT(); + break; + + case T_loss_tangent: + NextTok(); + item->SetLossTangent( parseDouble(), sublayer_idx ); + NeedRIGHT(); + break; + + case T_color: + NeedSYMBOL(); + item->SetColor( FromUTF8() ); + NeedRIGHT(); + break; + + default: + // Currently, skip this item if not defined, because the stackup def + // is a moving target + //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" ); + skipCurrent(); } - break; - - case T_material: - NeedSYMBOL(); - item->SetMaterial( FromUTF8() ); - NeedRIGHT(); - break; - - case T_epsilon_r: - NextTok(); - item->SetEpsilonR( parseDouble() ); - NeedRIGHT(); - break; - - case T_loss_tangent: - NextTok(); - item->SetLossTangent( parseDouble() ); - NeedRIGHT(); - break; - - case T_color: - NeedSYMBOL(); - item->SetColor( FromUTF8() ); - NeedRIGHT(); - break; - - default: - // Currently, skip this item if not defined, because the stackup def - // is a moving target - //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" ); - skipCurrent(); } } - } - if( type == BS_ITEM_TYPE_DIELECTRIC && thickness_locked ) - item->SetThicknessLocked( true ); + if( has_next_sublayer ) // Prepare reading the next sublayer description + { + sublayer_idx++; + item->AddDielectricPrms( sublayer_idx ); + } + } } if( token != T_RIGHT )