From 90ad4dcc05c5b010efdd63caa491f4cd7cb1ea0d Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 4 Dec 2022 07:52:21 +0300 Subject: [PATCH] router: Various bugfixes in length tuning meandering - When fine-tuning, keep adjust to the original baseline length - Enforce a minimum amplitude based on settings, DP gap and track width - Use binary search to fine-tune amplitude if the length is not trivial - Account for inequalities in balancing - Don't add the meander if adding it with min. amplitude will make the tuning worse - Keep a distance after meandering end to prevent overlaps in DP Fixes https://gitlab.com/kicad/code/kicad/issues/9267 Fixes https://gitlab.com/kicad/code/kicad/issues/1776 --- pcbnew/router/pns_meander.cpp | 131 ++++++++++++------ pcbnew/router/pns_meander.h | 28 +++- pcbnew/router/pns_meander_placer_base.cpp | 158 +++++++++++++++++----- 3 files changed, 245 insertions(+), 72 deletions(-) diff --git a/pcbnew/router/pns_meander.cpp b/pcbnew/router/pns_meander.cpp index 300f008c54..40799a07ee 100644 --- a/pcbnew/router/pns_meander.cpp +++ b/pcbnew/router/pns_meander.cpp @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2014 CERN - * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -127,6 +127,7 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde { m.Fit( MT_TURN, aBase, m_last, side ); AddMeander( new MEANDER_SHAPE( m ) ); + side = !side; started = true; } else @@ -136,8 +137,6 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde AddMeander( new MEANDER_SHAPE( m ) ); turning = false; } - - side = !side; } } else if( !singleSided && started ) @@ -188,12 +187,33 @@ void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseInde } +int MEANDER_SHAPE::MinAmplitude() const +{ + int minAmplitude = Settings().m_minAmplitude; + + // DP meanders don't really support smaller amplitudes + minAmplitude = std::max( minAmplitude, std::abs( m_baselineOffset ) * 2 ); + + // The path length won't be correct with very small arcs + if( m_placer->MeanderSettings().m_cornerStyle == MEANDER_STYLE_ROUND ) + minAmplitude = std::max( minAmplitude, m_width + std::abs( m_baselineOffset ) * 2 ); + + return minAmplitude; +} + + int MEANDER_SHAPE::cornerRadius() const { // TODO: fix diff-pair meandering so we can use non-100% radii int rPercent = m_dual ? 100 : Settings().m_cornerRadiusPercentage; - return (int64_t) spacing() * rPercent / 200; + int optCr = (int64_t) spacing() * rPercent / 200; + int minCr = std::abs( m_baselineOffset ); + int maxCr = std::min( m_amplitude / 2, spacing() / 2 ); + + int cr = std::clamp( optCr, minCr, maxCr ); + + return cr; } @@ -316,12 +336,14 @@ void MEANDER_SHAPE::uShape( int aSides, int aCorner, int aTop ) SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTOR2D& aDir, - bool aSide, MEANDER_TYPE aType, int aAmpl, + bool aSide, MEANDER_TYPE aType, int aBaselineOffset ) { int cr = cornerRadius(); int offset = aBaselineOffset; int spc = spacing(); + int amplitude = m_amplitude; + int targetBaseLen = m_targetBaseLen; if( aSide ) offset *= -1; @@ -329,9 +351,9 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO VECTOR2D dir_u_b( aDir.Resize( offset ) ); VECTOR2D dir_v_b( dir_u_b.Perpendicular() ); - if( 2 * cr > aAmpl ) + if( 2 * cr > amplitude ) { - cr = aAmpl / 2; + cr = amplitude / 2; } if( 2 * cr > spc ) @@ -339,8 +361,19 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO cr = spc / 2; } + if( cr - offset < 0 ) + { + cr = offset; + } + m_meanCornerRadius = cr; + int sCorner = cr - offset; + int uCorner = cr + offset; + int startSide = amplitude - 2 * cr + std::abs( offset ); + int turnSide = amplitude - cr; + int top = spc - 2 * cr; + SHAPE_LINE_CHAIN lc; start( &lc, aP + dir_v_b, aDir ); @@ -354,45 +387,62 @@ SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTO } case MT_START: { - miter( cr - offset, false ); - uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); - forward( std::min( cr - offset, cr + offset ) ); + if( targetBaseLen ) + top = std::max( top, targetBaseLen - sCorner - uCorner * 2 + offset ); + + miter( sCorner, false ); + uShape( startSide, uCorner, top ); + forward( std::min( sCorner, uCorner ) ); forward( std::abs( offset ) ); break; } case MT_FINISH: { + if( targetBaseLen ) + top = std::max( top, targetBaseLen - cr - spc ); + start( &lc, aP - dir_u_b, aDir ); turn( -ANGLE_90 ); - forward( std::min( cr - offset, cr + offset ) ); + forward( std::min( sCorner, uCorner ) ); forward( std::abs( offset ) ); - uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); - miter( cr - offset, false ); + uShape( startSide, uCorner, top ); + miter( sCorner, false ); + + if( targetBaseLen >= spc + cr ) + lc.Append( aP + dir_v_b + aDir.Resize( targetBaseLen ) ); + else + lc.Append( aP + dir_v_b + aDir.Resize( 2 * spc - cr ) ); + break; } case MT_TURN: { + if( targetBaseLen ) + top = std::max( top, targetBaseLen - uCorner * 2 + offset * 2 ); + start( &lc, aP - dir_u_b, aDir ); turn( -ANGLE_90 ); forward( std::abs( offset ) ); - uShape( aAmpl - cr, cr + offset, spc - 2 * cr ); + uShape( turnSide, uCorner, top ); forward( std::abs( offset ) ); break; } case MT_SINGLE: { - miter( cr - offset, false ); - uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); - miter( cr - offset, false ); + if( targetBaseLen ) + top = std::max( top, ( targetBaseLen - sCorner * 2 - uCorner * 2 ) / 2 ); + + miter( sCorner, false ); + uShape( startSide, uCorner, top ); + miter( sCorner, false ); lc.Append( aP + dir_v_b + aDir.Resize( 2 * spc ) ); break; } - default: - break; + default: break; } if( aSide ) @@ -490,34 +540,27 @@ bool MEANDER_SHAPE::Fit( MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP } } - int minAmpl = st.m_minAmplitude; - int maxAmpl = st.m_maxAmplitude; - - if( m_dual ) - { - minAmpl = std::max( minAmpl, 2 * std::abs( m_baselineOffset ) ); - maxAmpl = std::max( maxAmpl, 2 * std::abs( m_baselineOffset ) ); - } + int minAmpl = MinAmplitude(); + int maxAmpl = std::max( st.m_maxAmplitude, minAmpl ); for( int ampl = maxAmpl; ampl >= minAmpl; ampl -= st.m_step ) { + m_amplitude = ampl; + if( m_dual ) { - m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, - m_baselineOffset ); - m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, - -m_baselineOffset ); + m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, m_baselineOffset ); + m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, -m_baselineOffset ); } else { - m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, 0 ); + m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, 0 ); } m_type = aType; m_baseSeg = aSeg; m_p0 = aP; m_side = aSide; - m_amplitude = ampl; updateBaseSegment(); @@ -531,12 +574,12 @@ bool MEANDER_SHAPE::Fit( MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP void MEANDER_SHAPE::Recalculate() { - m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, + m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_dual ? m_baselineOffset : 0 ); if( m_dual ) m_shapes[1] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, - m_amplitude, -m_baselineOffset ); + -m_baselineOffset ); updateBaseSegment(); } @@ -560,11 +603,12 @@ void MEANDER_SHAPE::MakeEmpty() VECTOR2I dir = m_clippedBaseSeg.B - m_clippedBaseSeg.A; m_type = MT_EMPTY; + m_amplitude = 0; - m_shapes[0] = genMeanderShape( m_p0, dir, m_side, m_type, 0, m_dual ? m_baselineOffset : 0 ); + m_shapes[0] = genMeanderShape( m_p0, dir, m_side, m_type, m_dual ? m_baselineOffset : 0 ); if( m_dual ) - m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, 0, -m_baselineOffset ); + m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, -m_baselineOffset ); } @@ -654,12 +698,23 @@ int MEANDER_SHAPE::BaselineLength() const } -int MEANDER_SHAPE::MaxTunableLength() const +long long int MEANDER_SHAPE::CurrentLength() const { return CLine( 0 ).Length(); } +long long int MEANDER_SHAPE::MinTunableLength() const +{ + MEANDER_SHAPE copy = *this; + + copy.SetTargetBaselineLength( BaselineLength() ); + copy.Resize( copy.MinAmplitude() ); + + return copy.CurrentLength(); +} + + void MEANDER_SHAPE::updateBaseSegment( ) { if( m_dual ) diff --git a/pcbnew/router/pns_meander.h b/pcbnew/router/pns_meander.h index 52358a1230..b09e870f41 100644 --- a/pcbnew/router/pns_meander.h +++ b/pcbnew/router/pns_meander.h @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2015 CERN - * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * * @author Tomasz Wlostowski * @@ -129,6 +129,7 @@ public: // Do not leave uninitialized members, and keep static analyzer quiet: m_type = MT_SINGLE; m_amplitude = 0; + m_targetBaseLen = 0; m_side = false; m_baseIndex = 0; m_currentTarget = nullptr; @@ -273,7 +274,17 @@ public: /** * @return the length of the fitted line chain. */ - int MaxTunableLength() const; + long long int CurrentLength() const; + + /** + * @return the minumum tunable length according to settings. + */ + long long int MinTunableLength() const; + + /** + * @return the minumum possible amplitude according to settings. + */ + int MinAmplitude() const; /** * @return the current meandering settings. @@ -299,6 +310,14 @@ public: m_baselineOffset = aOffset; } + /** + * Sets the target length of the baseline. When resizing, the meander will try to + * fit the baseline length into the specified value. + * + * @param aLength the minimum baseline length. + */ + void SetTargetBaselineLength( int aLength ) { m_targetBaseLen = aLength; } + private: friend class MEANDERED_LINE; @@ -322,7 +341,7 @@ private: ///< Produce a meander shape of given type. SHAPE_LINE_CHAIN genMeanderShape( const VECTOR2D& aP, const VECTOR2D& aDir, bool aSide, - MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0 ); + MEANDER_TYPE aType, int aBaselineOffset = 0 ); ///< Recalculate the clipped baseline after the parameters of the meander have been changed. void updateBaseSegment(); @@ -354,6 +373,9 @@ private: ///< Average radius of meander corners (for correction of DP meanders). int m_meanCornerRadius; + ///< Minimum length of the base segment to target when resizing. + int m_targetBaseLen; + ///< First point of the meandered line. VECTOR2I m_p0; diff --git a/pcbnew/router/pns_meander_placer_base.cpp b/pcbnew/router/pns_meander_placer_base.cpp index 6c88fae9f5..07338fc9ad 100644 --- a/pcbnew/router/pns_meander_placer_base.cpp +++ b/pcbnew/router/pns_meander_placer_base.cpp @@ -2,7 +2,7 @@ * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2015 CERN - * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it @@ -27,6 +27,8 @@ namespace PNS { +const int LENGTH_TARGET_TOLERANCE = 20; + MEANDER_PLACER_BASE::MEANDER_PLACER_BASE( ROUTER* aRouter ) : PLACEMENT_ALGO( aRouter ) { @@ -131,32 +133,112 @@ void MEANDER_PLACER_BASE::cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, const V } +int findAmplitudeBinarySearch( MEANDER_SHAPE& aCopy, int targetLength, int minAmp, int maxAmp ) +{ + if( minAmp == maxAmp ) + return maxAmp; + + aCopy.Resize( minAmp ); + int minLen = aCopy.CurrentLength(); + + aCopy.Resize( maxAmp ); + int maxLen = aCopy.CurrentLength(); + + if( minLen > targetLength ) + return 0; + + if( maxLen < targetLength ) + return 0; + + int minError = minLen - targetLength; + int maxError = maxLen - targetLength; + + if( std::abs( minError ) < LENGTH_TARGET_TOLERANCE + || std::abs( maxError ) < LENGTH_TARGET_TOLERANCE ) + { + return std::abs( minError ) < std::abs( maxError ) ? minAmp : maxAmp; + } + else + { + int left = + findAmplitudeBinarySearch( aCopy, targetLength, minAmp, ( minAmp + maxAmp ) / 2 ); + + if( left ) + return left; + + int right = + findAmplitudeBinarySearch( aCopy, targetLength, ( minAmp + maxAmp ) / 2, maxAmp ); + + if( right ) + return right; + } + + return 0; +} + + +int findAmplitudeForLength( MEANDER_SHAPE* m, int targetLength, int minAmp, int maxAmp ) +{ + MEANDER_SHAPE copy = *m; + + // Try to keep the same baseline length + copy.SetTargetBaselineLength( m->BaselineLength() ); + + long long initialGuess = m->Amplitude() - ( m->CurrentLength() - targetLength ) / 2; + + if( initialGuess >= minAmp && initialGuess <= maxAmp ) + { + copy.Resize( minAmp ); + + if( std::abs( copy.CurrentLength() - targetLength ) < LENGTH_TARGET_TOLERANCE ) + return initialGuess; + } + + // The length is non-trivial, use binary search + return findAmplitudeBinarySearch( copy, targetLength, minAmp, maxAmp ); +} + + void MEANDER_PLACER_BASE::tuneLineLength( MEANDERED_LINE& aTuned, long long int aElongation ) { - long long int remaining = aElongation; - bool finished = false; + long long int maxElongation = 0; + long long int minElongation = 0; + bool finished = false; for( MEANDER_SHAPE* m : aTuned.Meanders() ) { if( m->Type() != MT_CORNER && m->Type() != MT_ARC ) { - if( remaining >= 0 ) - remaining -= m->MaxTunableLength() - m->BaselineLength(); + MEANDER_SHAPE end = *m; + MEANDER_TYPE endType; - if( remaining < 0 ) + if( m->Type() == MT_START || m->Type() == MT_SINGLE ) + endType = MT_SINGLE; + else + endType = MT_FINISH; + + end.SetType( endType ); + end.Recalculate(); + + long long int maxEndElongation = end.CurrentLength() - end.BaselineLength(); + + if( maxElongation + maxEndElongation > aElongation ) { if( !finished ) { - MEANDER_TYPE newType; - - if( m->Type() == MT_START || m->Type() == MT_SINGLE ) - newType = MT_SINGLE; - else - newType = MT_FINISH; - - m->SetType( newType ); + m->SetType( endType ); m->Recalculate(); + if( endType == MT_SINGLE ) + { + // Check if we need to fit this meander + long long int endMinElongation = + ( m->MinTunableLength() - m->BaselineLength() ); + + if( minElongation + endMinElongation >= aElongation ) + m->MakeEmpty(); + } + finished = true; } else @@ -164,38 +246,52 @@ void MEANDER_PLACER_BASE::tuneLineLength( MEANDERED_LINE& aTuned, long long int m->MakeEmpty(); } } + + maxElongation += m->CurrentLength() - m->BaselineLength(); + minElongation += m->MinTunableLength() - m->BaselineLength(); } } - remaining = aElongation; - int meanderCount = 0; + long long int remainingElongation = aElongation; + int meanderCount = 0; for( MEANDER_SHAPE* m : aTuned.Meanders() ) { if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY ) { - if(remaining >= 0) - { - remaining -= m->MaxTunableLength() - m->BaselineLength(); - meanderCount ++; - } + remainingElongation -= m->CurrentLength() - m->BaselineLength(); + meanderCount++; } } - long long int balance = 0; + long long int lenReductionLeft = -remainingElongation; + int meandersLeft = meanderCount; - if( meanderCount ) - balance = -remaining / meanderCount; + if( lenReductionLeft < 0 || !meandersLeft ) + return; - if( balance >= 0 ) + for( MEANDER_SHAPE* m : aTuned.Meanders() ) { - for( MEANDER_SHAPE* m : aTuned.Meanders() ) + if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY ) { - if( m->Type() != MT_CORNER && m->Type() != MT_ARC && m->Type() != MT_EMPTY ) - { - m->Resize( std::max( m->Amplitude() - balance / 2, - (long long int) m_settings.m_minAmplitude ) ); - } + long long int lenReductionHere = lenReductionLeft / meandersLeft; + long long int initialLen = m->CurrentLength(); + int minAmpl = m->MinAmplitude(); + + int amp = findAmplitudeForLength( m, initialLen - lenReductionHere, minAmpl, + m->Amplitude() ); + + if( amp < minAmpl ) + amp = minAmpl; + + m->SetTargetBaselineLength( m->BaselineLength() ); + m->Resize( amp ); + + lenReductionLeft -= initialLen - m->CurrentLength(); + meandersLeft--; + + if( !meandersLeft ) + break; } } }