DRC Rule Editor: Add skew field to matched length diff pair constr.

The matched length diff pair panel had skew data in the constraint class
but no UI field to display or edit it. Added a dedicated overlay panel
with min/opt/max length plus max skew fields, a new bitmap showing a
diff pair with length arrows and skew indicator.
This commit is contained in:
Damjan Prerad
2026-02-28 23:22:15 +01:00
parent 76ac4883b4
commit c753d6cd34
14 changed files with 956 additions and 4 deletions
+6
View File
@@ -5036,6 +5036,9 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::constraint_hole_to_hole_distance].emplace_back( BITMAPS::constraint_hole_to_hole_distance, wxT( "constraint_hole_to_hole_distance.png" ), -1, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_length].emplace_back( BITMAPS::constraint_length, wxT( "constraint_length.png" ), -1, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_matched_length_diff_pair].emplace_back( BITMAPS::constraint_matched_length_diff_pair, wxT( "constraint_matched_length_diff_pair.png" ), -1, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_matched_length_diff_pair_v2].emplace_back(
BITMAPS::constraint_matched_length_diff_pair_v2, wxT( "constraint_matched_length_diff_pair_v2.png" ), -1,
wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_maximum_allowed_deviation].emplace_back( BITMAPS::constraint_maximum_allowed_deviation, wxT( "constraint_maximum_allowed_deviation.png" ), -1, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_maximum_via_count].emplace_back( BITMAPS::constraint_maximum_via_count, wxT( "constraint_maximum_via_count.png" ), -1, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::constraint_minimum_angular_ring].emplace_back( BITMAPS::constraint_minimum_angular_ring, wxT( "constraint_minimum_angular_ring.png" ), -1, wxT( "light" ) );
@@ -5071,6 +5074,9 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::constraint_hole_to_hole_distance].emplace_back( BITMAPS::constraint_hole_to_hole_distance, wxT( "constraint_hole_to_hole_distance_dark.png" ), -1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_length].emplace_back( BITMAPS::constraint_length, wxT( "constraint_length_dark.png" ), -1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_matched_length_diff_pair].emplace_back( BITMAPS::constraint_matched_length_diff_pair, wxT( "constraint_matched_length_diff_pair_dark.png" ), -1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_matched_length_diff_pair_v2].emplace_back(
BITMAPS::constraint_matched_length_diff_pair_v2, wxT( "constraint_matched_length_diff_pair_v2_dark.png" ),
-1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_maximum_allowed_deviation].emplace_back( BITMAPS::constraint_maximum_allowed_deviation, wxT( "constraint_maximum_allowed_deviation_dark.png" ), -1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_maximum_via_count].emplace_back( BITMAPS::constraint_maximum_via_count, wxT( "constraint_maximum_via_count_dark.png" ), -1, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::constraint_minimum_angular_ring].emplace_back( BITMAPS::constraint_minimum_angular_ring, wxT( "constraint_minimum_angular_ring_dark.png" ), -1, wxT( "dark" ) );
+1
View File
@@ -717,6 +717,7 @@ enum class BITMAPS : unsigned int
constraint_maximum_allowed_deviation,
constraint_minimum_angular_ring,
constraint_matched_length_diff_pair,
constraint_matched_length_diff_pair_v2,
constraint_routing_diff_pair,
constraint_routing_width,
constraint_clearance,
+1
View File
@@ -333,6 +333,7 @@ set( PCBNEW_DRC_SRCS
drc/rule_editor/drc_re_rtg_diff_pair_overlay_panel.cpp
drc/rule_editor/drc_re_min_txt_ht_th_overlay_panel.cpp
drc/rule_editor/drc_re_abs_length_two_overlay_panel.cpp
drc/rule_editor/drc_re_matched_length_diff_pair_overlay_panel.cpp
drc/rule_editor/drc_re_via_style_overlay_panel.cpp
drc/rule_editor/drc_re_numeric_input_overlay_panel.cpp
drc/rule_editor/drc_re_bool_input_overlay_panel.cpp
@@ -157,14 +157,19 @@ class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA : public DRC_RE_ABSOLUTE_L
public:
using DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA::DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA;
BITMAPS GetOverlayBitmap() const override { return BITMAPS::constraint_matched_length_diff_pair; }
BITMAPS GetOverlayBitmap() const override { return BITMAPS::constraint_matched_length_diff_pair_v2; }
std::vector<DRC_RE_FIELD_POSITION> GetFieldPositions() const override
{
return {
{ 335 + DRC_RE_OVERLAY_XO, 365 + DRC_RE_OVERLAY_XO, 33 + DRC_RE_OVERLAY_YO, 1, wxS( "mm" ), LABEL_POSITION::RIGHT }, // min_length
{ 335 + DRC_RE_OVERLAY_XO, 365 + DRC_RE_OVERLAY_XO, 103 + DRC_RE_OVERLAY_YO, 2, wxS( "mm" ), LABEL_POSITION::RIGHT }, // opt_length
{ 335 + DRC_RE_OVERLAY_XO, 365 + DRC_RE_OVERLAY_XO, 173 + DRC_RE_OVERLAY_YO, 3, wxS( "mm" ), LABEL_POSITION::RIGHT }, // max_length
{ 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
{ 817 + DRC_RE_OVERLAY_XO, 857 + 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, -3 + DRC_RE_OVERLAY_YO, 4, wxS( "mm" ),
LABEL_POSITION::RIGHT }, // max_skew
};
}
@@ -196,6 +201,16 @@ public:
m_maxSkew = source.m_maxSkew;
}
VALIDATION_RESULT Validate() const override
{
VALIDATION_RESULT result = DRC_RE_ABSOLUTE_LENGTH_TWO_CONSTRAINT_DATA::Validate();
if( m_maxSkew < 0 )
result.AddError( _( "Maximum Skew must be greater than or equal to 0" ) );
return result;
}
private:
double m_maxSkew{ 0 };
};
@@ -0,0 +1,172 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "drc_re_matched_length_diff_pair_overlay_panel.h"
#include "drc_re_abs_length_two_constraint_data.h"
#include "drc_rule_editor_utils.h"
#include "drc_re_validator_numeric_ctrl.h"
#include <base_units.h>
#include <eda_base_frame.h>
#include <widgets/unit_binder.h>
#include <wx/textctrl.h>
#include <dialogs/rule_editor_dialog_base.h>
DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL(
wxWindow* aParent, DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA* aData, EDA_UNITS aUnits ) :
DRC_RE_BITMAP_OVERLAY_PANEL( aParent ),
m_data( aData ),
m_unitsProvider( pcbIUScale, aUnits )
{
SetBackgroundBitmap( aData->GetOverlayBitmap() );
std::vector<DRC_RE_FIELD_POSITION> positions = m_data->GetFieldPositions();
wxWindow* eventSource = nullptr;
for( wxWindow* win = aParent; win; win = win->GetParent() )
{
if( dynamic_cast<EDA_BASE_FRAME*>( win ) )
{
eventSource = win;
break;
}
}
// 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 );
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 max skew field
auto* maxSkewField = AddField<wxTextCtrl>( wxS( "max_skew" ), positions[3], 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 ) );
auto notifyModified = [this]( wxCommandEvent& )
{
RULE_EDITOR_DIALOG_BASE* dlg = RULE_EDITOR_DIALOG_BASE::GetDialog( this );
if( dlg )
dlg->SetModified();
};
minLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
optLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxLengthField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
maxSkewField->GetControl()->Bind( wxEVT_TEXT, notifyModified );
auto notifySave = [this]( wxCommandEvent& aEvent )
{
RULE_EDITOR_DIALOG_BASE* dlg = RULE_EDITOR_DIALOG_BASE::GetDialog( this );
if( dlg )
dlg->OnSave( aEvent );
};
minLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
optLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxLengthField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
maxSkewField->GetControl()->Bind( wxEVT_TEXT_ENTER, notifySave );
// Position all fields and update the panel layout
PositionFields();
TransferDataToWindow();
}
bool DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::TransferDataToWindow()
{
if( !m_data )
return false;
m_minLengthBinder->SetDoubleValue( pcbIUScale.mmToIU( m_data->GetMinimumLength() ) );
m_optLengthBinder->SetDoubleValue( pcbIUScale.mmToIU( m_data->GetOptimumLength() ) );
m_maxLengthBinder->SetDoubleValue( pcbIUScale.mmToIU( m_data->GetMaximumLength() ) );
m_maxSkewBinder->SetDoubleValue( pcbIUScale.mmToIU( m_data->GetMaxSkew() ) );
return true;
}
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->SetMaxSkew( pcbIUScale.IUTomm( m_maxSkewBinder->GetDoubleValue() ) );
return true;
}
bool DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::ValidateInputs( int* aErrorCount, wxString* aValidationMessage )
{
TransferDataFromWindow();
VALIDATION_RESULT result = m_data->Validate();
if( !result.isValid )
{
*aErrorCount = result.errors.size();
for( size_t i = 0; i < result.errors.size(); i++ )
*aValidationMessage += DRC_RULE_EDITOR_UTILS::FormatErrorMessage( i + 1, result.errors[i] );
return false;
}
return true;
}
wxString DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL::GenerateRule( const RULE_GENERATION_CONTEXT& aContext )
{
if( !m_data )
return wxEmptyString;
return m_data->GenerateRule( aContext );
}
@@ -0,0 +1,65 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL_H
#define DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL_H
#include <memory>
#include <units_provider.h>
#include "drc_re_bitmap_overlay_panel.h"
enum class EDA_UNITS;
class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA;
class UNIT_BINDER;
class DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL : public DRC_RE_BITMAP_OVERLAY_PANEL
{
public:
DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL( wxWindow* aParent,
DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA* aData,
EDA_UNITS aUnits );
~DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL() override = default;
bool TransferDataToWindow() override;
bool TransferDataFromWindow() override;
bool ValidateInputs( int* aErrorCount, wxString* aValidationMessage ) override;
wxString GenerateRule( const RULE_GENERATION_CONTEXT& aContext ) override;
private:
DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA* m_data;
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_maxSkewBinder;
};
#endif // DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL_H
@@ -61,6 +61,7 @@
#include "drc_re_numeric_input_overlay_panel.h"
#include "drc_re_bool_input_overlay_panel.h"
#include "drc_re_allowed_orientation_overlay_panel.h"
#include "drc_re_matched_length_diff_pair_overlay_panel.h"
#include "drc_re_vias_under_smd_overlay_panel.h"
#include "drc_re_permitted_layers_overlay_panel.h"
@@ -382,6 +383,11 @@ PANEL_DRC_RULE_EDITOR::getConstraintPanel( wxWindow* aParent, const DRC_RULE_EDI
aParent, dynamic_pointer_cast<DRC_RE_CUSTOM_RULE_CONSTRAINT_DATA>( m_constraintData ) );
case MATCHED_LENGTH_DIFF_PAIR:
return new DRC_RE_MATCHED_LENGTH_DIFF_PAIR_OVERLAY_PANEL(
aParent,
dynamic_pointer_cast<DRC_RE_MATCHED_LENGTH_DIFF_PAIR_CONSTRAINT_DATA>( m_constraintData ).get(),
units );
case ABSOLUTE_LENGTH:
return new DRC_RE_ABS_LENGTH_TWO_OVERLAY_PANEL(
aParent,
+1
View File
@@ -730,6 +730,7 @@ set( BMAPS_CONSTRAINTS
hole-to-hole-distance
length
matched-length-diff-pair
matched-length-diff-pair-v2
maximum-allowed-deviation
maximum-via-count
minimum-angular-ring
@@ -52,6 +52,7 @@ CONSTRAINT_FILES = {
'solderpaste-expansion.svg': 'solderpaste-expansion.svg',
'maximum_allowed_deviation.svg': 'maximum-allowed-deviation.svg',
'matched-lenght-diff-pair.svg': 'matched-length-diff-pair.svg',
"matched-length-diff-pair-v2.svg": "matched-length-diff-pair-v2.svg",
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="850"
height="260"
viewBox="0 0 850 260"
version="1.1"
id="svg14"
sodipodi:docname="constraint_matched_length_diff_pair_meander.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.89263174"
inkscape:cx="430.74874"
inkscape:cy="138.91507"
inkscape:window-width="1512"
inkscape:window-height="839"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="0"
inkscape:current-layer="svg14" />
<defs
id="defs2">
<!-- Inward-pointing arrowheads for dimension lines -->
<marker
id="ah-start"
markerWidth="10"
markerHeight="6"
refX="0"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="0,1 10,4 0,7"
fill="#B0B0B0"
id="polygon1" />
</marker>
<marker
id="ah-end"
markerWidth="10"
markerHeight="6"
refX="10"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="10,1 0,4 10,7"
fill="#B0B0B0"
id="polygon2" />
</marker>
</defs>
<!-- ===== PADS ===== -->
<!-- Left pads (round, through-hole style) -->
<circle
cx="30"
cy="75"
r="10"
fill="#5b9bd5"
id="circle2" />
<circle
cx="30"
cy="125"
r="10"
fill="#5B9BD5"
id="circle3" />
<!-- Right pads (rectangular, SMD style) -->
<rect
x="794.49725"
y="91.655205"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect3" />
<rect
x="747.22162"
y="112.6461"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect4" />
<!-- ===== DIFF PAIR TRACES ===== -->
<!-- Coupled differential pair with constant spacing (16px center-to-center).
At turns: outer trace R=30, inner trace R=14.
Channel pitch = 44px. 8 channels of meander.
y_top=38 (top turn centers), y_bot=108 (bottom turn centers).
Traces converge from pad spacing (50px) to pair spacing (16px) at entry/exit. -->
<!-- Trace P (top/outer at top turns, inner at bottom turns) -->
<path
d="M 40,75 H 70 C 82.150264,75 92,65.150264 92,53 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c -0.2766,6.92805 5.7148,13.10725 13.44679,13.10725 C 173.17878,121.10725 180,114.92805 180,108 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.2766,9.6941 7.37443,13.66046 15.10642,13.66046 7.73199,0 13.44679,-5.07278 12.89358,-13.66046 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.20009,8.07518 5.86783,15.01317 13.59982,15.01317 7.73199,0 14.60027,-6.33772 14.40018,-15.01317 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 29.62284,13.435726 30,29.999987 l 1.00545,44.157924 c -0.22012,13.430703 7.15554,21.866686 22,21.999996 h 357"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path4"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- Trace N (bottom/inner at top turns, outer at bottom turns) -->
<path
d="m 40,125 h 46 c 12.150264,0 22,-9.84974 22,-22 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,17.73041 12.32504,31.68282 28.89358,31.68282 C 181.46212,139.68282 196,129.32627 196,108 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,18.83683 14.53788,31.40622 31.10642,31.40622 16.56854,0 29.44679,-11.18637 28.89358,-31.40622 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0,16.64651 12.83119,31.62797 29.39973,31.62797 16.56854,0 31.20054,-12.78046 30.60027,-31.62797 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 59.802222 c -0.0434,13.047588 13.94904,26.944898 29.14694,26.548058 L 747,125"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path5"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- ===== LENGTH DIMENSION ARROWS (below traces) ===== -->
<!-- Common left extension line (dashed, from pad area down to arrows) -->
<line
x1="20.16864"
y1="140"
x2="20.16864"
y2="204.44565"
stroke="#707070"
stroke-width="0.7"
stroke-dasharray="3, 3"
id="line5" />
<!-- Min length: x=30 to x=285, y=190 -->
<line
x1="524.33136"
y1="138.8214"
x2="524.33136"
y2="154.8214"
stroke="#808080"
stroke-width="0.7"
id="line6" />
<line
x1="30.978367"
y1="147.80708"
x2="513.7215"
y2="147.80708"
stroke="#b0b0b0"
stroke-width="2.06386"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line7"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(290, 183) -->
<!-- Opt length: x=30 to x=475, y=210 -->
<line
x1="666.53131"
y1="167.05452"
x2="666.53131"
y2="183.05452"
stroke="#808080"
stroke-width="0.7"
id="line8" />
<line
x1="30.206526"
y1="174.81697"
x2="655.6615"
y2="174.81697"
stroke="#b0b0b0"
stroke-width="1.77832"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line9"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(480, 203) -->
<!-- Max length: x=30 to x=700, y=230 -->
<line
x1="819.27112"
y1="194.58308"
x2="819.27112"
y2="210.58308"
stroke="#808080"
stroke-width="0.7"
id="line10"
style="stroke-width:1.123;stroke-dasharray:none" />
<line
x1="30.613073"
y1="203.02974"
x2="808.76239"
y2="203.02974"
stroke="#b0b0b0"
stroke-width="1.61654"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line11"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(705, 223) -->
<!-- ===== SKEW DIMENSION ARROW (right side) ===== -->
<!-- Brackets the extra meander section of trace N -->
<line
x1="819.00098"
y1="25.205948"
x2="819.00098"
y2="106.14098"
stroke="#808080"
stroke-width="0.971719"
id="line12" />
<line
x1="771.53912"
y1="25.091061"
x2="771.53912"
y2="128.11938"
stroke="#808080"
stroke-width="1.09636"
id="line13" />
<line
x1="781.61816"
y1="30.48731"
x2="808.73627"
y2="30.48731"
stroke="#b0b0b0"
stroke-width="1.4277"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line14"
style="stroke-width:1.0841;stroke-dasharray:none;marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(773, 161) -->
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="850"
height="260"
viewBox="0 0 850 260"
version="1.1"
id="svg14"
sodipodi:docname="constraint_matched_length_diff_pair_meander.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#ded3dd"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.89263174"
inkscape:cx="430.74874"
inkscape:cy="138.91507"
inkscape:window-width="1512"
inkscape:window-height="839"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="0"
inkscape:current-layer="svg14" />
<defs
id="defs2">
<!-- Inward-pointing arrowheads for dimension lines -->
<marker
id="ah-start"
markerWidth="10"
markerHeight="6"
refX="0"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="0,1 10,4 0,7"
fill="#B0B0B0"
id="polygon1" />
</marker>
<marker
id="ah-end"
markerWidth="10"
markerHeight="6"
refX="10"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="10,1 0,4 10,7"
fill="#B0B0B0"
id="polygon2" />
</marker>
</defs>
<!-- ===== PADS ===== -->
<!-- Left pads (round, through-hole style) -->
<circle
cx="30"
cy="75"
r="10"
fill="#5b9bd5"
id="circle2" />
<circle
cx="30"
cy="125"
r="10"
fill="#5B9BD5"
id="circle3" />
<!-- Right pads (rectangular, SMD style) -->
<rect
x="794.49725"
y="91.655205"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect3" />
<rect
x="747.22162"
y="112.6461"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect4" />
<!-- ===== DIFF PAIR TRACES ===== -->
<!-- Coupled differential pair with constant spacing (16px center-to-center).
At turns: outer trace R=30, inner trace R=14.
Channel pitch = 44px. 8 channels of meander.
y_top=38 (top turn centers), y_bot=108 (bottom turn centers).
Traces converge from pad spacing (50px) to pair spacing (16px) at entry/exit. -->
<!-- Trace P (top/outer at top turns, inner at bottom turns) -->
<path
d="M 40,75 H 70 C 82.150264,75 92,65.150264 92,53 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c -0.2766,6.92805 5.7148,13.10725 13.44679,13.10725 C 173.17878,121.10725 180,114.92805 180,108 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.2766,9.6941 7.37443,13.66046 15.10642,13.66046 7.73199,0 13.44679,-5.07278 12.89358,-13.66046 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.20009,8.07518 5.86783,15.01317 13.59982,15.01317 7.73199,0 14.60027,-6.33772 14.40018,-15.01317 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 29.62284,13.435726 30,29.999987 l 1.00545,44.157924 c -0.22012,13.430703 7.15554,21.866686 22,21.999996 h 357"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path4"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- Trace N (bottom/inner at top turns, outer at bottom turns) -->
<path
d="m 40,125 h 46 c 12.150264,0 22,-9.84974 22,-22 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,17.73041 12.32504,31.68282 28.89358,31.68282 C 181.46212,139.68282 196,129.32627 196,108 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,18.83683 14.53788,31.40622 31.10642,31.40622 16.56854,0 29.44679,-11.18637 28.89358,-31.40622 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0,16.64651 12.83119,31.62797 29.39973,31.62797 16.56854,0 31.20054,-12.78046 30.60027,-31.62797 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 59.802222 c -0.0434,13.047588 13.94904,26.944898 29.14694,26.548058 L 747,125"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path5"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- ===== LENGTH DIMENSION ARROWS (below traces) ===== -->
<!-- Common left extension line (dashed, from pad area down to arrows) -->
<line
x1="20.16864"
y1="140"
x2="20.16864"
y2="204.44565"
stroke="#707070"
stroke-width="0.7"
stroke-dasharray="3, 3"
id="line5" />
<!-- Min length: x=30 to x=285, y=190 -->
<line
x1="524.33136"
y1="138.8214"
x2="524.33136"
y2="154.8214"
stroke="#808080"
stroke-width="0.7"
id="line6" />
<line
x1="30.978367"
y1="147.80708"
x2="513.7215"
y2="147.80708"
stroke="#b0b0b0"
stroke-width="2.06386"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line7"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(290, 183) -->
<!-- Opt length: x=30 to x=475, y=210 -->
<line
x1="666.53131"
y1="167.05452"
x2="666.53131"
y2="183.05452"
stroke="#808080"
stroke-width="0.7"
id="line8" />
<line
x1="30.206526"
y1="174.81697"
x2="655.6615"
y2="174.81697"
stroke="#b0b0b0"
stroke-width="1.77832"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line9"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(480, 203) -->
<!-- Max length: x=30 to x=700, y=230 -->
<line
x1="819.27112"
y1="194.58308"
x2="819.27112"
y2="210.58308"
stroke="#808080"
stroke-width="0.7"
id="line10"
style="stroke-width:1.123;stroke-dasharray:none" />
<line
x1="30.613073"
y1="203.02974"
x2="808.76239"
y2="203.02974"
stroke="#b0b0b0"
stroke-width="1.61654"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line11"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(705, 223) -->
<!-- ===== SKEW DIMENSION ARROW (right side) ===== -->
<!-- Brackets the extra meander section of trace N -->
<line
x1="819.00098"
y1="25.205948"
x2="819.00098"
y2="106.14098"
stroke="#808080"
stroke-width="0.971719"
id="line12" />
<line
x1="771.53912"
y1="25.091061"
x2="771.53912"
y2="128.11938"
stroke="#808080"
stroke-width="1.09636"
id="line13" />
<line
x1="781.61816"
y1="30.48731"
x2="808.73627"
y2="30.48731"
stroke="#b0b0b0"
stroke-width="1.4277"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line14"
style="stroke-width:1.0841;stroke-dasharray:none;marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(773, 161) -->
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="850"
height="260"
viewBox="0 0 850 260"
version="1.1"
id="svg14"
sodipodi:docname="constraint_matched_length_diff_pair_meander.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.89263174"
inkscape:cx="430.74874"
inkscape:cy="138.91507"
inkscape:window-width="1512"
inkscape:window-height="839"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="0"
inkscape:current-layer="svg14" />
<defs
id="defs2">
<!-- Inward-pointing arrowheads for dimension lines -->
<marker
id="ah-start"
markerWidth="10"
markerHeight="6"
refX="0"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="0,1 10,4 0,7"
fill="#B0B0B0"
id="polygon1" />
</marker>
<marker
id="ah-end"
markerWidth="10"
markerHeight="6"
refX="10"
refY="4"
orient="auto"
markerUnits="userSpaceOnUse"
viewBox="0 0 10 6"
preserveAspectRatio="xMidYMid">
<polygon
points="10,1 0,4 10,7"
fill="#B0B0B0"
id="polygon2" />
</marker>
</defs>
<!-- ===== PADS ===== -->
<!-- Left pads (round, through-hole style) -->
<circle
cx="30"
cy="75"
r="10"
fill="#5b9bd5"
id="circle2" />
<circle
cx="30"
cy="125"
r="10"
fill="#5B9BD5"
id="circle3" />
<!-- Right pads (rectangular, SMD style) -->
<rect
x="794.49725"
y="91.655205"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect3" />
<rect
x="747.22162"
y="112.6461"
width="24"
height="24"
rx="3"
fill="#5b9bd5"
id="rect4" />
<!-- ===== DIFF PAIR TRACES ===== -->
<!-- Coupled differential pair with constant spacing (16px center-to-center).
At turns: outer trace R=30, inner trace R=14.
Channel pitch = 44px. 8 channels of meander.
y_top=38 (top turn centers), y_bot=108 (bottom turn centers).
Traces converge from pad spacing (50px) to pair spacing (16px) at entry/exit. -->
<!-- Trace P (top/outer at top turns, inner at bottom turns) -->
<path
d="M 40,75 H 70 C 82.150264,75 92,65.150264 92,53 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c -0.2766,6.92805 5.7148,13.10725 13.44679,13.10725 C 173.17878,121.10725 180,114.92805 180,108 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.2766,9.6941 7.37443,13.66046 15.10642,13.66046 7.73199,0 13.44679,-5.07278 12.89358,-13.66046 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 30,13.431433 30,29.999987 v 70 c 0.20009,8.07518 5.86783,15.01317 13.59982,15.01317 7.73199,0 14.60027,-6.33772 14.40018,-15.01317 V 38 c 0,-16.568554 13.43146,-29.999987 30,-29.999987 16.56854,0 29.62284,13.435726 30,29.999987 l 1.00545,44.157924 c -0.22012,13.430703 7.15554,21.866686 22,21.999996 h 357"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path4"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- Trace N (bottom/inner at top turns, outer at bottom turns) -->
<path
d="m 40,125 h 46 c 12.150264,0 22,-9.84974 22,-22 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,17.73041 12.32504,31.68282 28.89358,31.68282 C 181.46212,139.68282 196,129.32627 196,108 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0.2766,18.83683 14.53788,31.40622 31.10642,31.40622 16.56854,0 29.44679,-11.18637 28.89358,-31.40622 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 70 c 0,16.64651 12.83119,31.62797 29.39973,31.62797 16.56854,0 31.20054,-12.78046 30.60027,-31.62797 V 38 c 0,-7.731992 6.26801,-13.999994 14,-13.999994 7.73199,0 14,6.268002 14,13.999994 v 59.802222 c -0.0434,13.047588 13.94904,26.944898 29.14694,26.548058 L 747,125"
stroke="#5b9bd5"
stroke-width="7"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
id="path5"
sodipodi:nodetypes="cssssscscssscscssscscsssccc" />
<!-- ===== LENGTH DIMENSION ARROWS (below traces) ===== -->
<!-- Common left extension line (dashed, from pad area down to arrows) -->
<line
x1="20.16864"
y1="140"
x2="20.16864"
y2="204.44565"
stroke="#707070"
stroke-width="0.7"
stroke-dasharray="3, 3"
id="line5" />
<!-- Min length: x=30 to x=285, y=190 -->
<line
x1="524.33136"
y1="138.8214"
x2="524.33136"
y2="154.8214"
stroke="#808080"
stroke-width="0.7"
id="line6" />
<line
x1="30.978367"
y1="147.80708"
x2="513.7215"
y2="147.80708"
stroke="#b0b0b0"
stroke-width="2.06386"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line7"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(290, 183) -->
<!-- Opt length: x=30 to x=475, y=210 -->
<line
x1="666.53131"
y1="167.05452"
x2="666.53131"
y2="183.05452"
stroke="#808080"
stroke-width="0.7"
id="line8" />
<line
x1="30.206526"
y1="174.81697"
x2="655.6615"
y2="174.81697"
stroke="#b0b0b0"
stroke-width="1.77832"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line9"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(480, 203) -->
<!-- Max length: x=30 to x=700, y=230 -->
<line
x1="819.27112"
y1="194.58308"
x2="819.27112"
y2="210.58308"
stroke="#808080"
stroke-width="0.7"
id="line10"
style="stroke-width:1.123;stroke-dasharray:none" />
<line
x1="30.613073"
y1="203.02974"
x2="808.76239"
y2="203.02974"
stroke="#b0b0b0"
stroke-width="1.61654"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line11"
style="marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(705, 223) -->
<!-- ===== SKEW DIMENSION ARROW (right side) ===== -->
<!-- Brackets the extra meander section of trace N -->
<line
x1="819.00098"
y1="25.205948"
x2="819.00098"
y2="106.14098"
stroke="#808080"
stroke-width="0.971719"
id="line12" />
<line
x1="771.53912"
y1="25.091061"
x2="771.53912"
y2="128.11938"
stroke="#808080"
stroke-width="1.09636"
id="line13" />
<line
x1="781.61816"
y1="30.48731"
x2="808.73627"
y2="30.48731"
stroke="#b0b0b0"
stroke-width="1.4277"
marker-start="url(#ah-start)"
marker-end="url(#ah-end)"
id="line14"
style="stroke-width:1.0841;stroke-dasharray:none;marker-start:url(#ah-end);marker-end:url(#ah-start)" />
<!-- Field position: ~(773, 161) -->
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB