DRC Rule Editor: fix via style matcher, silk conditions, and overlay panels

This commit is contained in:
Damjan Prerad
2026-03-05 14:34:49 +01:00
parent a9cdf8f3ab
commit e2ae384b0f
14 changed files with 329 additions and 347 deletions
@@ -604,6 +604,8 @@ void DIALOG_DRC_RULE_EDITOR::RemoveRule( int aNodeId )
SetModified();
DeleteRuleTreeItem( GetCurrentlySelectedRuleTreeItemData()->GetTreeItemId(), nodeId );
deleteTreeNodeData( nodeId );
SaveRulesToFile();
ClearModified();
}
SetControlsEnabled( true );
@@ -688,8 +690,8 @@ std::vector<RULE_TREE_NODE> DIALOG_DRC_RULE_EDITOR::buildManufacturabilityRuleTr
result.push_back( buildRuleTreeNodeData( "Soldermask", DRC_RULE_EDITOR_ITEM_TYPE::CATEGORY, aParentId ) );
lastParentId = m_nodeId;
result.push_back( buildRuleTreeNodeData( "Minimum soldermask silver", DRC_RULE_EDITOR_ITEM_TYPE::CONSTRAINT,
lastParentId, MINIMUM_SOLDERMASK_SILVER ) );
result.push_back( buildRuleTreeNodeData( "Minimum soldermask sliver", DRC_RULE_EDITOR_ITEM_TYPE::CONSTRAINT,
lastParentId, MINIMUM_SOLDERMASK_SLIVER ) );
result.push_back( buildRuleTreeNodeData( "Soldermask expansion", DRC_RULE_EDITOR_ITEM_TYPE::CONSTRAINT,
lastParentId, SOLDERMASK_EXPANSION ) );
@@ -38,15 +38,11 @@ public:
{
}
explicit DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA( int aId, int aParentId,
double aMinLength,
double aOptLength,
double aMaxLength,
explicit DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA( int aId, int aParentId, double aOptLength, double aTolerance,
const wxString& aRuleName ) :
DRC_RE_BASE_CONSTRAINT_DATA( aId, aParentId, aRuleName ),
m_minLength( aMinLength ),
m_optLength( aOptLength ),
m_maxLength( aMaxLength )
m_tolerance( aTolerance )
{
}
@@ -56,52 +52,43 @@ public:
std::vector<DRC_RE_FIELD_POSITION> GetFieldPositions() const override
{
// Positions measured from constraint_absolute_lenght_2.png (~340x130)
// Format: { xStart, xEnd, yTop, tabOrder }
// Two fields side-by-side, opt_length and tolerance
return {
{ 525 + DRC_RE_OVERLAY_XO, 565 + DRC_RE_OVERLAY_XO, 133 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ), LABEL_POSITION::RIGHT }, // min_length (left arrow)
{ 668 + DRC_RE_OVERLAY_XO, 708 + DRC_RE_OVERLAY_XO, 163 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ), LABEL_POSITION::RIGHT }, // opt_length (bottom center arrow)
{ 820 + DRC_RE_OVERLAY_XO, 860 + DRC_RE_OVERLAY_XO, 190 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_length (right arrow)
{ 80 + DRC_RE_OVERLAY_XO, 120 + DRC_RE_OVERLAY_XO, 115 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // opt_length
{ 160 + DRC_RE_OVERLAY_XO, 200 + DRC_RE_OVERLAY_XO, 115 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // tolerance
};
}
double GetMinimumLength() const { return m_minLength; }
void SetMinimumLength( double aLength ) { m_minLength = aLength; }
double GetOptimumLength() const { return m_optLength; }
void SetOptimumLength( double aLength ) { m_optLength = aLength; }
double GetMaximumLength() const { return m_maxLength; }
double GetTolerance() const { return m_tolerance; }
void SetMaximumLength( double aLength ) { m_maxLength = aLength; }
void SetTolerance( double aTolerance ) { m_tolerance = aTolerance; }
// Computed from opt +/- tolerance
double GetMinimumLength() const { return m_optLength - m_tolerance; }
double GetMaximumLength() const { return m_optLength + m_tolerance; }
VALIDATION_RESULT Validate() const override
{
VALIDATION_RESULT result;
// Validate length values are positive
if( m_minLength <= 0 )
result.AddError( _( "Minimum Length must be greater than 0" ) );
if( m_optLength <= 0 )
result.AddError( _( "Optimum Length must be greater than 0" ) );
if( m_maxLength <= 0 )
result.AddError( _( "Maximum Length must be greater than 0" ) );
if( m_tolerance < 0 )
result.AddError( _( "Tolerance must be greater than or equal to 0" ) );
if( result.isValid )
{
// Validate min <= opt <= max
if( m_minLength > m_optLength )
result.AddError( _( "Minimum Length cannot be greater than Optimum Length" ) );
if( m_optLength > m_maxLength )
result.AddError( _( "Optimum Length cannot be greater than Maximum Length" ) );
if( m_minLength > m_maxLength )
result.AddError( _( "Minimum Length cannot be greater than Maximum Length" ) );
if( GetMinimumLength() <= 0 )
result.AddError( _( "Tolerance is too large: resulting minimum length is not positive" ) );
}
return result;
@@ -119,12 +106,9 @@ public:
if( code.IsEmpty() )
code = wxS( "length" );
wxString clause = wxString::Format(
wxS( "(constraint %s (min %s) (opt %s) (max %s))" ),
code,
formatDistance( m_minLength ),
formatDistance( m_optLength ),
formatDistance( m_maxLength ) );
wxString clause = wxString::Format( wxS( "(constraint %s (min %s) (opt %s) (max %s))" ), code,
formatDistance( GetMinimumLength() ), formatDistance( m_optLength ),
formatDistance( GetMaximumLength() ) );
return { clause };
}
@@ -141,15 +125,13 @@ public:
DRC_RE_BASE_CONSTRAINT_DATA::CopyFrom( source );
m_minLength = source.m_minLength;
m_optLength = source.m_optLength;
m_maxLength = source.m_maxLength;
m_tolerance = source.m_tolerance;
}
private:
double m_minLength{ 0 };
double m_optLength{ 0 };
double m_maxLength{ 0 };
double m_tolerance{ 0 };
};
class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA : public DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA
@@ -162,10 +144,12 @@ public:
std::vector<DRC_RE_FIELD_POSITION> GetFieldPositions() const override
{
return {
{ 527 + DRC_RE_OVERLAY_XO, 567 + DRC_RE_OVERLAY_XO, 133 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ), LABEL_POSITION::RIGHT }, // min_length
{ 670 + DRC_RE_OVERLAY_XO, 710 + DRC_RE_OVERLAY_XO, 162 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ), LABEL_POSITION::RIGHT }, // opt_length
{ 819 + DRC_RE_OVERLAY_XO, 859 + DRC_RE_OVERLAY_XO, 190 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_length
{ 777 + DRC_RE_OVERLAY_XO, 815 + DRC_RE_OVERLAY_XO, -1 + DRC_RE_OVERLAY_YO, 4, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_skew
{ 80 + DRC_RE_OVERLAY_XO, 120 + DRC_RE_OVERLAY_XO, 130 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // opt_length
{ 160 + DRC_RE_OVERLAY_XO, 200 + DRC_RE_OVERLAY_XO, 130 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // tolerance
{ 25 + DRC_RE_OVERLAY_XO, 65 + DRC_RE_OVERLAY_XO, 0 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // max_skew
};
}
@@ -173,14 +157,28 @@ public:
void SetMaxSkew( double aSkew ) { m_maxSkew = aSkew; }
bool GetWithinDiffPairs() const { return m_withinDiffPairs; }
void SetWithinDiffPairs( bool aWithinDiffPairs ) { m_withinDiffPairs = aWithinDiffPairs; }
std::vector<wxString> GetConstraintClauses( const RULE_GENERATION_CONTEXT& aContext ) const override
{
std::vector<wxString> clauses = DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA::GetConstraintClauses( aContext );
if( m_maxSkew > 0 )
{
wxString skewClause = wxString::Format(
wxS( "(constraint skew (max %smm))" ), formatDouble( m_maxSkew ) );
wxString skewClause;
if( m_withinDiffPairs )
{
skewClause = wxString::Format( wxS( "(constraint skew (max %smm) (within_diff_pairs))" ),
formatDouble( m_maxSkew ) );
}
else
{
skewClause = wxString::Format( wxS( "(constraint skew (max %smm))" ), formatDouble( m_maxSkew ) );
}
clauses.push_back( skewClause );
}
@@ -195,6 +193,7 @@ public:
DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA::CopyFrom( source );
m_maxSkew = source.m_maxSkew;
m_withinDiffPairs = source.m_withinDiffPairs;
}
VALIDATION_RESULT Validate() const override
@@ -209,6 +208,7 @@ public:
private:
double m_maxSkew{ 0 };
bool m_withinDiffPairs{ false };
};
#endif // DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA_H_
@@ -31,6 +31,7 @@
#include <widgets/unit_binder.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
#include <dialogs/rule_editor_dialog_base.h>
DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL(
@@ -54,32 +55,36 @@ DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL(
}
}
// Create min length field
auto* minLengthField = AddField<wxTextCtrl>( wxS( "min_length" ), positions[0],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_minLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, minLengthField->GetControl(),
minLengthField->GetLabel(), false, false );
minLengthField->SetUnitBinder( m_minLengthBinder.get() );
minLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create opt length field
auto* optLengthField = AddField<wxTextCtrl>( wxS( "opt_length" ), positions[1],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
auto* optLengthField = AddField<wxTextCtrl>( wxS( "opt_length" ), positions[0], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_optLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, optLengthField->GetControl(),
optLengthField->GetLabel(), false, false );
optLengthField->SetUnitBinder( m_optLengthBinder.get() );
optLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create max length field
auto* maxLengthField = AddField<wxTextCtrl>( wxS( "max_length" ), positions[2],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxLengthField->GetControl(),
maxLengthField->GetLabel(), false, false );
maxLengthField->SetUnitBinder( m_maxLengthBinder.get() );
maxLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create tolerance field
auto* toleranceField = AddField<wxTextCtrl>( wxS( "tolerance" ), positions[1], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_toleranceBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, toleranceField->GetControl(),
toleranceField->GetLabel(), false, false );
toleranceField->SetUnitBinder( m_toleranceBinder.get() );
toleranceField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Add +/- decoration between the two fields (same pattern as via style)
{
const DRC_RE_FIELD_POSITION& optPos = positions[0];
const DRC_RE_FIELD_POSITION& tolPos = positions[1];
int fieldHeight = optLengthField->GetControl()->GetBestSize().GetHeight();
auto* plusMinus = new wxStaticText( this, wxID_ANY, wxS( "\u00B1" ) );
wxSize pmSize = plusMinus->GetBestSize();
wxStaticText* optMmLabel = optLengthField->GetLabel();
int afterOptLabel = optMmLabel->GetPosition().x + optMmLabel->GetBestSize().GetWidth();
int gapMid = ( afterOptLabel + tolPos.xStart ) / 2;
plusMinus->SetPosition(
wxPoint( gapMid - pmSize.GetWidth() / 2, optPos.yTop + ( fieldHeight - pmSize.GetHeight() ) / 2 ) );
}
auto notifyModified = [this]( wxCommandEvent& )
{
@@ -88,9 +93,8 @@ DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL(
dlg->SetModified();
};
minLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
optLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
toleranceField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
auto notifySave = [this]( wxCommandEvent& aEvent )
{
@@ -99,9 +103,8 @@ DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL(
dlg->OnSave( aEvent );
};
minLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
optLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
toleranceField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
// Position all fields and update the panel layout
PositionFields();
@@ -114,10 +117,8 @@ bool DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::TransferDataToWindow()
if( !m_data )
return false;
// Use ChangeDoubleValue to avoid triggering modification events during loading
m_minLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMinimumLength() ) );
m_optLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetOptimumLength() ) );
m_maxLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaximumLength() ) );
m_toleranceBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetTolerance() ) );
return true;
}
@@ -128,9 +129,8 @@ bool DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL::TransferDataFromWindow()
if( !m_data )
return false;
m_data->SetMinimumLength( pcbIUScale.IUTomm( m_minLengthBinder->GetDoubleValue() ) );
m_data->SetOptimumLength( pcbIUScale.IUTomm( m_optLengthBinder->GetDoubleValue() ) );
m_data->SetMaximumLength( pcbIUScale.IUTomm( m_maxLengthBinder->GetDoubleValue() ) );
m_data->SetTolerance( pcbIUScale.IUTomm( m_toleranceBinder->GetDoubleValue() ) );
return true;
}
@@ -60,9 +60,8 @@ private:
UNITS_PROVIDER m_unitsProvider;
std::unique_ptr<UNIT_BINDER> m_minLengthBinder;
std::unique_ptr<UNIT_BINDER> m_optLengthBinder;
std::unique_ptr<UNIT_BINDER> m_maxLengthBinder;
std::unique_ptr<UNIT_BINDER> m_toleranceBinder;
};
#endif // DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL_H
@@ -31,6 +31,8 @@
#include <widgets/unit_binder.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
#include <wx/checkbox.h>
#include <dialogs/rule_editor_dialog_base.h>
DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL(
@@ -54,37 +56,56 @@ DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_MATCHED_LENGTH_DIFF_PAIR_O
}
}
// Create min length field
auto* minLengthField = AddField<wxTextCtrl>( wxS( "min_length" ), positions[0], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_minLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, minLengthField->GetControl(),
minLengthField->GetLabel(), false, false );
minLengthField->SetUnitBinder( m_minLengthBinder.get() );
minLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create opt length field
auto* optLengthField = AddField<wxTextCtrl>( wxS( "opt_length" ), positions[1], wxTE_CENTRE | wxTE_PROCESS_ENTER );
auto* optLengthField = AddField<wxTextCtrl>( wxS( "opt_length" ), positions[0], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_optLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, optLengthField->GetControl(),
optLengthField->GetLabel(), false, false );
optLengthField->SetUnitBinder( m_optLengthBinder.get() );
optLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create max length field
auto* maxLengthField = AddField<wxTextCtrl>( wxS( "max_length" ), positions[2], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxLengthField->GetControl(),
maxLengthField->GetLabel(), false, false );
maxLengthField->SetUnitBinder( m_maxLengthBinder.get() );
maxLengthField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Create tolerance field
auto* toleranceField = AddField<wxTextCtrl>( wxS( "tolerance" ), positions[1], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_toleranceBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, toleranceField->GetControl(),
toleranceField->GetLabel(), false, false );
toleranceField->SetUnitBinder( m_toleranceBinder.get() );
toleranceField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
// Add +/- decoration between opt and tolerance fields
{
const DRC_RE_FIELD_POSITION& optPos = positions[0];
const DRC_RE_FIELD_POSITION& tolPos = positions[1];
int fieldHeight = optLengthField->GetControl()->GetBestSize().GetHeight();
auto* plusMinus = new wxStaticText( this, wxID_ANY, wxS( "\u00B1" ) );
wxSize pmSize = plusMinus->GetBestSize();
wxStaticText* optMmLabel = optLengthField->GetLabel();
int afterOptLabel = optMmLabel->GetPosition().x + optMmLabel->GetBestSize().GetWidth();
int gapMid = ( afterOptLabel + tolPos.xStart ) / 2;
plusMinus->SetPosition(
wxPoint( gapMid - pmSize.GetWidth() / 2, optPos.yTop + ( fieldHeight - pmSize.GetHeight() ) / 2 ) );
}
// Create max skew field
auto* maxSkewField = AddField<wxTextCtrl>( wxS( "max_skew" ), positions[3], wxTE_CENTRE | wxTE_PROCESS_ENTER );
auto* maxSkewField = AddField<wxTextCtrl>( wxS( "max_skew" ), positions[2], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxSkewBinder = std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxSkewField->GetControl(),
maxSkewField->GetLabel(), false, false );
maxSkewField->SetUnitBinder( m_maxSkewBinder.get() );
maxSkewField->GetControl()->SetValidator( VALIDATOR_NUMERIC_CTRL( false, false ) );
{
const DRC_RE_FIELD_POSITION& skewPos = positions[2];
wxStaticText* skewMmLabel = maxSkewField->GetLabel();
int afterSkewLabel = skewMmLabel->GetPosition().x + skewMmLabel->GetBestSize().GetWidth();
int fieldHeight = maxSkewField->GetControl()->GetBestSize().GetHeight();
m_withinDiffPairsCheckbox = new wxCheckBox( this, wxID_ANY, _( "Within diff pairs" ) );
wxSize cbSize = m_withinDiffPairsCheckbox->GetBestSize();
m_withinDiffPairsCheckbox->SetPosition(
wxPoint( afterSkewLabel + 8, skewPos.yTop + ( fieldHeight - cbSize.GetHeight() ) / 2 ) );
}
auto notifyModified = [this]( wxCommandEvent& )
{
RULE_EDITOR_DIALOG_BASE* dlg = RULE_EDITOR_DIALOG_BASE::GetDialog( this );
@@ -92,10 +113,10 @@ DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_MATCHED_LENGTH_DIFF_PAIR_O
dlg->SetModified();
};
minLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
optLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
toleranceField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxSkewField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
m_withinDiffPairsCheckbox->Bind( wxEVT_CHECKBOX, notifyModified );
auto notifySave = [this]( wxCommandEvent& aEvent )
{
@@ -104,10 +125,10 @@ DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_MATCHED_LENGTH_DIFF_PAIR_O
dlg->OnSave( aEvent );
};
minLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
optLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
toleranceField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxSkewField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
m_withinDiffPairsCheckbox->Bind( wxEVT_CHECKBOX, notifySave );
// Position all fields and update the panel layout
PositionFields();
@@ -120,11 +141,10 @@ bool DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::TransferDataToWindow()
if( !m_data )
return false;
// Use ChangeDoubleValue to avoid triggering modification events during loading
m_minLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMinimumLength() ) );
m_optLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetOptimumLength() ) );
m_maxLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaximumLength() ) );
m_toleranceBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetTolerance() ) );
m_maxSkewBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxSkew() ) );
m_withinDiffPairsCheckbox->SetValue( m_data->GetWithinDiffPairs() );
return true;
}
@@ -135,10 +155,10 @@ bool DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::TransferDataFromWindow()
if( !m_data )
return false;
m_data->SetMinimumLength( pcbIUScale.IUTomm( m_minLengthBinder->GetDoubleValue() ) );
m_data->SetOptimumLength( pcbIUScale.IUTomm( m_optLengthBinder->GetDoubleValue() ) );
m_data->SetMaximumLength( pcbIUScale.IUTomm( m_maxLengthBinder->GetDoubleValue() ) );
m_data->SetTolerance( pcbIUScale.IUTomm( m_toleranceBinder->GetDoubleValue() ) );
m_data->SetMaxSkew( pcbIUScale.IUTomm( m_maxSkewBinder->GetDoubleValue() ) );
m_data->SetWithinDiffPairs( m_withinDiffPairsCheckbox->GetValue() );
return true;
}
@@ -34,6 +34,7 @@ enum class EDA_UNITS;
class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA;
class UNIT_BINDER;
class wxCheckBox;
class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL : public DRC_RE_BITMAP_OVERLAY_PANEL
@@ -56,10 +57,11 @@ private:
UNITS_PROVIDER m_unitsProvider;
std::unique_ptr<UNIT_BINDER> m_minLengthBinder;
std::unique_ptr<UNIT_BINDER> m_optLengthBinder;
std::unique_ptr<UNIT_BINDER> m_maxLengthBinder;
std::unique_ptr<UNIT_BINDER> m_toleranceBinder;
std::unique_ptr<UNIT_BINDER> m_maxSkewBinder;
wxCheckBox* m_withinDiffPairsCheckbox;
};
#endif // DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL_H
@@ -71,10 +71,10 @@ void DRC_PANEL_MATCHER::initClaims()
// Priority 90: VIA_STYLE claims via_diameter + hole_size
m_claims.emplace_back(
VIA_STYLE,
std::set<DRC_CONSTRAINT_T>{},
std::set<DRC_CONSTRAINT_T>{ VIA_DIAMETER_CONSTRAINT, HOLE_SIZE_CONSTRAINT },
90 );
VIA_STYLE,
std::set<DRC_CONSTRAINT_T>{ VIA_DIAMETER_CONSTRAINT, HOLE_SIZE_CONSTRAINT },
std::set<DRC_CONSTRAINT_T>{},
90 );
// Priority 80: MINIMUM_TEXT_HEIGHT_THICKNESS claims text_height + text_thickness
m_claims.emplace_back(
@@ -31,6 +31,7 @@
#include <widgets/unit_binder.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
#include <dialogs/rule_editor_dialog_base.h>
@@ -39,14 +39,14 @@ public:
}
explicit DRC_RE_ROUTING_DIFF_PAIR_CONSTRAINT_DATA( int aId, int aParentId, const wxString& aRuleName,
double aMaxUncoupledLength, double aMinWidth,
double aPreferredWidth, double aMaxWidth,
double aMinGap, double aPreferredGap,
double aMaxGap ) :
double aOptWidth, double aWidthTolerance, double aOptGap,
double aGapTolerance, double aMaxUncoupledLength ) :
DRC_RE_BASE_CONSTRAINT_DATA( aId, aParentId, aRuleName ),
m_maxUncoupledLength( aMaxUncoupledLength ), m_minWidth( aMinWidth ),
m_preferredWidth( aPreferredWidth ), m_maxWidth( aMaxWidth ), m_minGap( aMinGap ),
m_preferredGap( aPreferredGap ), m_maxGap( aMaxGap )
m_optWidth( aOptWidth ),
m_widthTolerance( aWidthTolerance ),
m_optGap( aOptGap ),
m_gapTolerance( aGapTolerance ),
m_maxUncoupledLength( aMaxUncoupledLength )
{
}
@@ -56,17 +56,20 @@ public:
std::vector<DRC_RE_FIELD_POSITION> GetFieldPositions() const override
{
// Positions measured from constraint_routing_diff_pair.png bitmap (~570x160)
// Positions measured from constraint_routing_diff_pair.png bitmap (~423x133)
// Format: { xStart, xEnd, yTop, tabOrder }
// TODO: measure actual positions from PNG
return {
{ 162 + DRC_RE_OVERLAY_XO, 202 + DRC_RE_OVERLAY_XO, 39 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ), LABEL_POSITION::RIGHT }, // min_width (left side, top arrow)
{ 302 + DRC_RE_OVERLAY_XO, 342 + DRC_RE_OVERLAY_XO, 39 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ), LABEL_POSITION::RIGHT }, // opt_width (left side, middle)
{ 480 + DRC_RE_OVERLAY_XO, 520 + DRC_RE_OVERLAY_XO, 39 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_width (left side, bottom)
{ 213 + DRC_RE_OVERLAY_XO, 253 + DRC_RE_OVERLAY_XO, 215 + DRC_RE_OVERLAY_YO, 4, wxS( "mm" ), LABEL_POSITION::RIGHT }, // min_gap (center-left, top)
{ 386 + DRC_RE_OVERLAY_XO, 426 + DRC_RE_OVERLAY_XO, 215 + DRC_RE_OVERLAY_YO, 5, wxS( "mm" ), LABEL_POSITION::RIGHT }, // opt_gap (center-left, middle)
{ 600 + DRC_RE_OVERLAY_XO, 640 + DRC_RE_OVERLAY_XO, 215 + DRC_RE_OVERLAY_YO, 6, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_gap (center-left, bottom)
{ 120 + DRC_RE_OVERLAY_XO, 160 + DRC_RE_OVERLAY_XO, 204 + DRC_RE_OVERLAY_YO, 7, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_uncoupled (right side)
{ 798 + DRC_RE_OVERLAY_XO, 838 + DRC_RE_OVERLAY_XO, 168 + DRC_RE_OVERLAY_YO, 8, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_skew (to be added, right side)
{ 0 + DRC_RE_OVERLAY_XO, 40 + DRC_RE_OVERLAY_XO, 135 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // opt_width
{ 80 + DRC_RE_OVERLAY_XO, 120 + DRC_RE_OVERLAY_XO, 135 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // width_tolerance (±)
{ 185 + DRC_RE_OVERLAY_XO, 225 + DRC_RE_OVERLAY_XO, 115 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // opt_gap
{ 265 + DRC_RE_OVERLAY_XO, 305 + DRC_RE_OVERLAY_XO, 115 + DRC_RE_OVERLAY_YO, 4, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // gap_tolerance (±)
{ 95 + DRC_RE_OVERLAY_XO, 135 + DRC_RE_OVERLAY_XO, 10 + DRC_RE_OVERLAY_YO, 5, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // max_uncoupled
};
}
@@ -74,68 +77,32 @@ public:
{
VALIDATION_RESULT result;
bool hasWidth = m_minWidth > 0 || m_preferredWidth > 0 || m_maxWidth > 0;
bool hasWidth = m_optWidth > 0;
if( hasWidth )
{
if( m_minWidth <= 0 )
result.AddError( _( "Minimum Width must be greater than 0" ) );
if( m_widthTolerance < 0 )
result.AddError( _( "Width Tolerance must be greater than or equal to 0" ) );
if( m_preferredWidth <= 0 )
result.AddError( _( "Preferred Width must be greater than 0" ) );
if( m_maxWidth <= 0 )
result.AddError( _( "Maximum Width must be greater than 0" ) );
if( m_widthTolerance >= m_optWidth )
result.AddError( _( "Width Tolerance must be less than Optimum Width" ) );
}
bool hasGap = m_minGap > 0 || m_preferredGap > 0 || m_maxGap > 0;
bool hasGap = m_optGap > 0;
if( hasGap )
{
if( m_minGap <= 0 )
result.AddError( _( "Minimum Gap must be greater than 0" ) );
if( m_gapTolerance < 0 )
result.AddError( _( "Gap Tolerance must be greater than or equal to 0" ) );
if( m_preferredGap <= 0 )
result.AddError( _( "Preferred Gap must be greater than 0" ) );
if( m_maxGap <= 0 )
result.AddError( _( "Maximum Gap must be greater than 0" ) );
if( m_gapTolerance >= m_optGap )
result.AddError( _( "Gap Tolerance must be less than Optimum Gap" ) );
}
if( m_maxUncoupledLength < 0 )
result.AddError( _( "Maximum Uncoupled Length must be greater than or equal to 0" ) );
if( m_maxSkew < 0 )
result.AddError( _( "Maximum Skew must be greater than or equal to 0" ) );
if( result.isValid )
{
if( hasWidth )
{
if( m_minWidth > m_preferredWidth )
result.AddError( _( "Minimum Width cannot be greater than Preferred Width" ) );
if( m_preferredWidth > m_maxWidth )
result.AddError( _( "Preferred Width cannot be greater than Maximum Width" ) );
if( m_minWidth > m_maxWidth )
result.AddError( _( "Minimum Width cannot be greater than Maximum Width" ) );
}
if( hasGap )
{
if( m_minGap > m_preferredGap )
result.AddError( _( "Minimum Gap cannot be greater than Preferred Gap" ) );
if( m_preferredGap > m_maxGap )
result.AddError( _( "Preferred Gap cannot be greater than Maximum Gap" ) );
if( m_minGap > m_maxGap )
result.AddError( _( "Minimum Gap cannot be greater than Maximum Gap" ) );
}
}
if( !hasWidth && !hasGap && m_maxUncoupledLength <= 0 && m_maxSkew <= 0 )
if( !hasWidth && !hasGap && m_maxUncoupledLength <= 0 )
result.AddError( _( "At least one constraint must be specified" ) );
return result;
@@ -150,18 +117,24 @@ public:
std::vector<wxString> clauses;
if( m_minWidth > 0 || m_preferredWidth > 0 || m_maxWidth > 0 )
if( m_optWidth > 0 )
{
double minWidth = m_optWidth - m_widthTolerance;
double maxWidth = m_optWidth + m_widthTolerance;
clauses.push_back( wxString::Format( wxS( "(constraint track_width (min %s) (opt %s) (max %s))" ),
formatDistance( m_minWidth ), formatDistance( m_preferredWidth ),
formatDistance( m_maxWidth ) ) );
formatDistance( minWidth ), formatDistance( m_optWidth ),
formatDistance( maxWidth ) ) );
}
if( m_minGap > 0 || m_preferredGap > 0 || m_maxGap > 0 )
if( m_optGap > 0 )
{
double minGap = m_optGap - m_gapTolerance;
double maxGap = m_optGap + m_gapTolerance;
clauses.push_back( wxString::Format( wxS( "(constraint diff_pair_gap (min %s) (opt %s) (max %s))" ),
formatDistance( m_minGap ), formatDistance( m_preferredGap ),
formatDistance( m_maxGap ) ) );
formatDistance( minGap ), formatDistance( m_optGap ),
formatDistance( maxGap ) ) );
}
if( m_maxUncoupledLength > 0 )
@@ -170,11 +143,6 @@ public:
formatDistance( m_maxUncoupledLength ) ) );
}
if( m_maxSkew > 0 )
{
clauses.push_back( wxString::Format( wxS( "(constraint skew (max %s))" ), formatDistance( m_maxSkew ) ) );
}
return clauses;
}
@@ -183,40 +151,20 @@ public:
return buildRule( aContext, GetConstraintClauses( aContext ) );
}
double GetOptWidth() { return m_optWidth; }
void SetOptWidth( double aOptWidth ) { m_optWidth = aOptWidth; }
double GetWidthTolerance() { return m_widthTolerance; }
void SetWidthTolerance( double aTolerance ) { m_widthTolerance = aTolerance; }
double GetOptGap() { return m_optGap; }
void SetOptGap( double aOptGap ) { m_optGap = aOptGap; }
double GetGapTolerance() { return m_gapTolerance; }
void SetGapTolerance( double aTolerance ) { m_gapTolerance = aTolerance; }
double GetMaxUncoupledLength() { return m_maxUncoupledLength; }
void SetMaxUncoupledLength( double aMaxUncoupledLength )
{
m_maxUncoupledLength = aMaxUncoupledLength;
}
double GetMinWidth() { return m_minWidth; }
void SetMinWidth( double aMinWidth ) { m_minWidth = aMinWidth; }
double GetPreferredWidth() { return m_preferredWidth; }
void SetPreferredWidth( double aPreferredWidth ) { m_preferredWidth = aPreferredWidth; }
double GetMaxWidth() { return m_maxWidth; }
void SetMaxWidth( double aMaxWidth ) { m_maxWidth = aMaxWidth; }
double GetMinGap() { return m_minGap; }
void SetMinGap( double aMinGap ) { m_minGap = aMinGap; }
double GetPreferredGap() { return m_preferredGap; }
void SetPreferredGap( double aPreferredGap ) { m_preferredGap = aPreferredGap; }
double GetMaxGap() { return m_maxGap; }
void SetMaxGap( double aMaxGap ) { m_maxGap = aMaxGap; }
double GetMaxSkew() { return m_maxSkew; }
void SetMaxSkew( double aMaxSkew ) { m_maxSkew = aMaxSkew; }
void SetMaxUncoupledLength( double aMaxUncoupledLength ) { m_maxUncoupledLength = aMaxUncoupledLength; }
void CopyFrom( const ICopyable& aSource ) override
{
@@ -225,25 +173,19 @@ public:
DRC_RE_BASE_CONSTRAINT_DATA::CopyFrom( source );
m_optWidth = source.m_optWidth;
m_widthTolerance = source.m_widthTolerance;
m_optGap = source.m_optGap;
m_gapTolerance = source.m_gapTolerance;
m_maxUncoupledLength = source.m_maxUncoupledLength;
m_minWidth = source.m_minWidth;
m_preferredWidth = source.m_preferredWidth;
m_maxWidth = source.m_maxWidth;
m_minGap = source.m_minGap;
m_preferredGap = source.m_preferredGap;
m_maxGap = source.m_maxGap;
m_maxSkew = source.m_maxSkew;
}
private:
double m_optWidth{ 0 };
double m_widthTolerance{ 0 };
double m_optGap{ 0 };
double m_gapTolerance{ 0 };
double m_maxUncoupledLength{ 0 };
double m_minWidth{ 0 };
double m_preferredWidth{ 0 };
double m_maxWidth{ 0 };
double m_minGap{ 0 };
double m_preferredGap{ 0 };
double m_maxGap{ 0 };
double m_maxSkew{ 0 };
};
#endif // DRC_RE_ROUTING_DIFF_PAIR_CONSTRAINT_DATA_H_
@@ -31,6 +31,7 @@
#include <widgets/unit_binder.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL(
@@ -56,58 +57,63 @@ DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL(
}
}
auto* minWidthField = AddField<wxTextCtrl>( wxS( "min_width" ), positions[0], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_minWidthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, minWidthField->GetControl(),
minWidthField->GetLabel(), false, false );
minWidthField->SetUnitBinder( m_minWidthBinder.get() );
auto* optWidthField = AddField<wxTextCtrl>( wxS( "opt_width" ), positions[0], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_optWidthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, optWidthField->GetControl(),
optWidthField->GetLabel(), false, false );
optWidthField->SetUnitBinder( m_optWidthBinder.get() );
auto* prefWidthField = AddField<wxTextCtrl>( wxS( "pref_width" ), positions[1],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_preferredWidthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, prefWidthField->GetControl(),
prefWidthField->GetLabel(), false, false );
prefWidthField->SetUnitBinder( m_preferredWidthBinder.get() );
auto* widthTolField =
AddField<wxTextCtrl>( wxS( "width_tolerance" ), positions[1], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_widthToleranceBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, widthTolField->GetControl(),
widthTolField->GetLabel(), false, false );
widthTolField->SetUnitBinder( m_widthToleranceBinder.get() );
auto* maxWidthField = AddField<wxTextCtrl>( wxS( "max_width" ), positions[2],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxWidthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxWidthField->GetControl(),
maxWidthField->GetLabel(), false, false );
maxWidthField->SetUnitBinder( m_maxWidthBinder.get() );
auto* optGapField = AddField<wxTextCtrl>( wxS( "opt_gap" ), positions[2], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_optGapBinder = std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, optGapField->GetControl(),
optGapField->GetLabel(), false, false );
optGapField->SetUnitBinder( m_optGapBinder.get() );
// Create gap fields (min/pref/max) — positions[3..5]
auto* minGapField = AddField<wxTextCtrl>( wxS( "min_gap" ), positions[3],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_minGapBinder = std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, minGapField->GetControl(),
minGapField->GetLabel(), false, false );
minGapField->SetUnitBinder( m_minGapBinder.get() );
auto* gapTolField = AddField<wxTextCtrl>( wxS( "gap_tolerance" ), positions[3], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_gapToleranceBinder = std::make_unique<UNIT_BINDER>(
&m_unitsProvider, eventSource, nullptr, gapTolField->GetControl(), gapTolField->GetLabel(), false, false );
gapTolField->SetUnitBinder( m_gapToleranceBinder.get() );
auto* prefGapField = AddField<wxTextCtrl>( wxS( "pref_gap" ), positions[4],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_preferredGapBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, prefGapField->GetControl(),
prefGapField->GetLabel(), false, false );
prefGapField->SetUnitBinder( m_preferredGapBinder.get() );
auto* maxGapField = AddField<wxTextCtrl>( wxS( "max_gap" ), positions[5],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxGapBinder = std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxGapField->GetControl(),
maxGapField->GetLabel(), false, false );
maxGapField->SetUnitBinder( m_maxGapBinder.get() );
// Create max uncoupled length field — positions[6]
auto* maxUncoupledField = AddField<wxTextCtrl>( wxS( "max_uncoupled" ), positions[6],
wxTE_CENTRE | wxTE_PROCESS_ENTER );
auto* maxUncoupledField =
AddField<wxTextCtrl>( wxS( "max_uncoupled" ), positions[4], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxUncoupledLengthBinder =
std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxUncoupledField->GetControl(),
maxUncoupledField->GetLabel(), false, false );
maxUncoupledField->SetUnitBinder( m_maxUncoupledLengthBinder.get() );
auto* maxSkewField = AddField<wxTextCtrl>( wxS( "max_skew" ), positions[7], wxTE_CENTRE | wxTE_PROCESS_ENTER );
m_maxSkewBinder = std::make_unique<UNIT_BINDER>( &m_unitsProvider, eventSource, nullptr, maxSkewField->GetControl(),
maxSkewField->GetLabel(), false, false );
maxSkewField->SetUnitBinder( m_maxSkewBinder.get() );
{
const DRC_RE_FIELD_POSITION& optPos = positions[0];
const DRC_RE_FIELD_POSITION& tolPos = positions[1];
int fieldHeight = optWidthField->GetControl()->GetBestSize().GetHeight();
auto* plusMinus = new wxStaticText( this, wxID_ANY, wxS( "\u00B1" ) );
wxSize pmSize = plusMinus->GetBestSize();
wxStaticText* optMmLabel = optWidthField->GetLabel();
int afterOptLabel = optMmLabel->GetPosition().x + optMmLabel->GetBestSize().GetWidth();
int gapMid = ( afterOptLabel + tolPos.xStart ) / 2;
plusMinus->SetPosition(
wxPoint( gapMid - pmSize.GetWidth() / 2, optPos.yTop + ( fieldHeight - pmSize.GetHeight() ) / 2 ) );
}
{
const DRC_RE_FIELD_POSITION& optPos = positions[2];
const DRC_RE_FIELD_POSITION& tolPos = positions[3];
int fieldHeight = optGapField->GetControl()->GetBestSize().GetHeight();
auto* plusMinus = new wxStaticText( this, wxID_ANY, wxS( "\u00B1" ) );
wxSize pmSize = plusMinus->GetBestSize();
wxStaticText* optMmLabel = optGapField->GetLabel();
int afterOptLabel = optMmLabel->GetPosition().x + optMmLabel->GetBestSize().GetWidth();
int gapMid = ( afterOptLabel + tolPos.xStart ) / 2;
plusMinus->SetPosition(
wxPoint( gapMid - pmSize.GetWidth() / 2, optPos.yTop + ( fieldHeight - pmSize.GetHeight() ) / 2 ) );
}
auto notifyModified = [this]( wxCommandEvent& )
{
@@ -116,14 +122,11 @@ DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL(
dlg->SetModified();
};
minWidthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
prefWidthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxWidthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
minGapField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
prefGapField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxGapField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
optWidthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
widthTolField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
optGapField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
gapTolField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxUncoupledField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxSkewField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
auto notifySave = [this]( wxCommandEvent& aEvent )
{
@@ -132,14 +135,11 @@ DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL(
dlg->OnSave( aEvent );
};
minWidthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
prefWidthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxWidthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
minGapField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
prefGapField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxGapField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
optWidthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
widthTolField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
optGapField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
gapTolField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxUncoupledField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxSkewField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
// Position all fields and update the panel layout
PositionFields();
@@ -154,16 +154,13 @@ bool DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::TransferDataToWindow()
// Convert mm values to internal units and set them in the binders
// Use ChangeDoubleValue to avoid triggering modification events during loading
m_minGapBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMinGap() ) );
m_preferredGapBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetPreferredGap() ) );
m_maxGapBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxGap() ) );
m_optWidthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetOptWidth() ) );
m_widthToleranceBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetWidthTolerance() ) );
m_minWidthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMinWidth() ) );
m_preferredWidthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetPreferredWidth() ) );
m_maxWidthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxWidth() ) );
m_optGapBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetOptGap() ) );
m_gapToleranceBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetGapTolerance() ) );
m_maxUncoupledLengthBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxUncoupledLength() ) );
m_maxSkewBinder->ChangeDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxSkew() ) );
return true;
}
@@ -175,16 +172,13 @@ bool DRC_RE_ROUTING_DIFF_PAIR_OVERLAY_PANEL::TransferDataFromWindow()
return false;
// Read values from binders (in internal units) and convert to mm for storage
m_data->SetMinGap( pcbIUScale.IUTomm( m_minGapBinder->GetDoubleValue() ) );
m_data->SetPreferredGap( pcbIUScale.IUTomm( m_preferredGapBinder->GetDoubleValue() ) );
m_data->SetMaxGap( pcbIUScale.IUTomm( m_maxGapBinder->GetDoubleValue() ) );
m_data->SetOptWidth( pcbIUScale.IUTomm( m_optWidthBinder->GetDoubleValue() ) );
m_data->SetWidthTolerance( pcbIUScale.IUTomm( m_widthToleranceBinder->GetDoubleValue() ) );
m_data->SetMinWidth( pcbIUScale.IUTomm( m_minWidthBinder->GetDoubleValue() ) );
m_data->SetPreferredWidth( pcbIUScale.IUTomm( m_preferredWidthBinder->GetDoubleValue() ) );
m_data->SetMaxWidth( pcbIUScale.IUTomm( m_maxWidthBinder->GetDoubleValue() ) );
m_data->SetOptGap( pcbIUScale.IUTomm( m_optGapBinder->GetDoubleValue() ) );
m_data->SetGapTolerance( pcbIUScale.IUTomm( m_gapToleranceBinder->GetDoubleValue() ) );
m_data->SetMaxUncoupledLength( pcbIUScale.IUTomm( m_maxUncoupledLengthBinder->GetDoubleValue() ) );
m_data->SetMaxSkew( pcbIUScale.IUTomm( m_maxSkewBinder->GetDoubleValue() ) );
return true;
}
@@ -59,14 +59,11 @@ private:
UNITS_PROVIDER m_unitsProvider;
std::unique_ptr<UNIT_BINDER> m_minGapBinder;
std::unique_ptr<UNIT_BINDER> m_preferredGapBinder;
std::unique_ptr<UNIT_BINDER> m_maxGapBinder;
std::unique_ptr<UNIT_BINDER> m_minWidthBinder;
std::unique_ptr<UNIT_BINDER> m_preferredWidthBinder;
std::unique_ptr<UNIT_BINDER> m_maxWidthBinder;
std::unique_ptr<UNIT_BINDER> m_optWidthBinder;
std::unique_ptr<UNIT_BINDER> m_widthToleranceBinder;
std::unique_ptr<UNIT_BINDER> m_optGapBinder;
std::unique_ptr<UNIT_BINDER> m_gapToleranceBinder;
std::unique_ptr<UNIT_BINDER> m_maxUncoupledLengthBinder;
std::unique_ptr<UNIT_BINDER> m_maxSkewBinder;
};
#endif // DRC_RE_RTG_DIFF_PAIR_OVERLAY_PANEL_H
+21 -11
View File
@@ -118,7 +118,8 @@ wxString DRC_RULE_SAVER::generateRuleText( const DRC_RE_LOADED_PANEL_ENTRY& aEnt
wxString ruleText = aEntry.constraintData->GetGeneratedRule();
if( ruleText.IsEmpty() || aEntry.panelType == SILK_TO_SOLDERMASK_CLEARANCE )
if( ruleText.IsEmpty() || aEntry.panelType == SILK_TO_SOLDERMASK_CLEARANCE
|| aEntry.panelType == SILK_TO_SILK_CLEARANCE )
{
RULE_GENERATION_CONTEXT ctx;
ctx.ruleName = aEntry.ruleName;
@@ -131,18 +132,27 @@ wxString DRC_RULE_SAVER::generateRuleText( const DRC_RE_LOADED_PANEL_ENTRY& aEnt
wxString silkCond;
if( aEntry.layerCondition.test( F_SilkS ) && !aEntry.layerCondition.test( B_SilkS ) )
{
silkCond = wxS( "A.Layer == 'F.SilkS' && B.Layer == 'F.Mask'" );
}
silkCond = wxS( "L == 'F.Mask'" );
else if( aEntry.layerCondition.test( B_SilkS ) && !aEntry.layerCondition.test( F_SilkS ) )
{
silkCond = wxS( "A.Layer == 'B.SilkS' && B.Layer == 'B.Mask'" );
}
silkCond = wxS( "L == 'B.Mask'" );
else
{
silkCond = wxS( "(A.Layer == 'F.SilkS' && B.Layer == 'F.Mask') || (A.Layer == 'B.SilkS' && B.Layer == "
"'B.Mask')" );
}
silkCond = wxS( "L == 'F.Mask' || L == 'B.Mask'" );
if( !ctx.conditionExpression.IsEmpty() )
ctx.conditionExpression = silkCond + wxS( " && " ) + ctx.conditionExpression;
else
ctx.conditionExpression = silkCond;
}
else if( aEntry.panelType == SILK_TO_SILK_CLEARANCE )
{
wxString silkCond;
if( aEntry.layerCondition.test( F_SilkS ) && !aEntry.layerCondition.test( B_SilkS ) )
silkCond = wxS( "L == 'F.SilkS'" );
else if( aEntry.layerCondition.test( B_SilkS ) && !aEntry.layerCondition.test( F_SilkS ) )
silkCond = wxS( "L == 'B.SilkS'" );
else
silkCond = wxS( "L == 'F.SilkS' || L == 'B.SilkS'" );
if( !ctx.conditionExpression.IsEmpty() )
ctx.conditionExpression = silkCond + wxS( " && " ) + ctx.conditionExpression;
@@ -82,29 +82,36 @@ public:
{
VALIDATION_RESULT result;
// Validate via diameter values are positive
if( m_minViaDiameter <= 0 )
result.AddError( _( "Minimum Via Diameter must be greater than 0" ) );
bool hasDiameter = m_minViaDiameter > 0 || m_maxViaDiameter > 0;
bool hasHoleSize = m_minViaHoleSize > 0 || m_maxViaHoleSize > 0;
if( m_maxViaDiameter <= 0 )
result.AddError( _( "Maximum Via Diameter must be greater than 0" ) );
// Validate via hole size values are positive
if( m_minViaHoleSize <= 0 )
result.AddError( _( "Minimum Via Hole Size must be greater than 0" ) );
if( m_maxViaHoleSize <= 0 )
result.AddError( _( "Maximum Via Hole Size must be greater than 0" ) );
if( result.isValid )
if( hasDiameter )
{
if( m_minViaDiameter > m_maxViaDiameter )
result.AddError( _( "Minimum Via Diameter cannot be greater than Maximum Via Diameter" ) );
if( m_minViaDiameter <= 0 )
result.AddError( _( "Minimum Via Diameter must be greater than 0" ) );
if( m_minViaHoleSize > m_maxViaHoleSize )
if( m_maxViaDiameter <= 0 )
result.AddError( _( "Maximum Via Diameter must be greater than 0" ) );
if( m_minViaDiameter > 0 && m_maxViaDiameter > 0 && m_minViaDiameter > m_maxViaDiameter )
result.AddError( _( "Minimum Via Diameter cannot be greater than Maximum Via Diameter" ) );
}
if( hasHoleSize )
{
if( m_minViaHoleSize <= 0 )
result.AddError( _( "Minimum Via Hole Size must be greater than 0" ) );
if( m_maxViaHoleSize <= 0 )
result.AddError( _( "Maximum Via Hole Size must be greater than 0" ) );
if( m_minViaHoleSize > 0 && m_maxViaHoleSize > 0 && m_minViaHoleSize > m_maxViaHoleSize )
result.AddError( _( "Minimum Via Hole Size cannot be greater than Maximum Via Hole Size" ) );
}
if( !hasDiameter && !hasHoleSize )
result.AddError( _( "At least one constraint must be specified" ) );
return result;
}
@@ -115,15 +122,23 @@ public:
return formatDouble( aValue ) + wxS( "mm" );
};
wxString diaClause =
wxString::Format( wxS( "(constraint via_diameter (min %s) (max %s))" ),
formatDimension( m_minViaDiameter ), formatDimension( m_maxViaDiameter ) );
std::vector<wxString> clauses;
wxString drillClause =
wxString::Format( wxS( "(constraint hole_size (min %s) (max %s))" ),
formatDimension( m_minViaHoleSize ), formatDimension( m_maxViaHoleSize ) );
if( m_minViaDiameter > 0 && m_maxViaDiameter > 0 )
{
clauses.push_back( wxString::Format( wxS( "(constraint via_diameter (min %s) (max %s))" ),
formatDimension( m_minViaDiameter ),
formatDimension( m_maxViaDiameter ) ) );
}
return { diaClause, drillClause };
if( m_minViaHoleSize > 0 && m_maxViaHoleSize > 0 )
{
clauses.push_back( wxString::Format( wxS( "(constraint hole_size (min %s) (max %s))" ),
formatDimension( m_minViaHoleSize ),
formatDimension( m_maxViaHoleSize ) ) );
}
return clauses;
}
wxString GenerateRule( const RULE_GENERATION_CONTEXT& aContext ) override
+2 -2
View File
@@ -436,7 +436,7 @@ BOOST_AUTO_TEST_CASE( ValidateDiffPairInvalid )
bool foundWidthError = false;
for( const auto& error : result1.errors )
{
if( error.find( "Minimum Width cannot be greater than Maximum Width" ) != std::string::npos )
if( error.find( "Width Tolerance must be less than Optimum Width" ) != std::string::npos )
foundWidthError = true;
}
BOOST_CHECK( foundWidthError );
@@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE( ValidateDiffPairInvalid )
bool foundNegativeError = false;
for( const auto& error : result2.errors )
{
if( error.find( "must be greater than 0" ) != std::string::npos )
if( error.find( "must be greater than or equal to 0" ) != std::string::npos )
foundNegativeError = true;
}
BOOST_CHECK( foundNegativeError );