737 lines
18 KiB
C++
737 lines
18 KiB
C++
/*
|
|
* KiRouter - a push-and-(sometimes-)shove PCB router
|
|
*
|
|
* Copyright (C) 2013-2014 CERN
|
|
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
|
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <base_units.h> // God forgive me doing this...
|
|
|
|
#include "pns_node.h"
|
|
#include "pns_itemset.h"
|
|
#include "pns_meander.h"
|
|
#include "pns_meander_placer_base.h"
|
|
#include "pns_router.h"
|
|
#include "pns_debug_decorator.h"
|
|
|
|
namespace PNS {
|
|
|
|
const MEANDER_SETTINGS& MEANDER_SHAPE::Settings() const
|
|
{
|
|
return m_placer->MeanderSettings();
|
|
}
|
|
|
|
|
|
const MEANDER_SETTINGS& MEANDERED_LINE::Settings() const
|
|
{
|
|
return m_placer->MeanderSettings();
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::MeanderSegment( const SEG& aBase, bool aSide, int aBaseIndex )
|
|
{
|
|
double base_len = aBase.Length();
|
|
|
|
SHAPE_LINE_CHAIN lc;
|
|
|
|
bool singleSided = Settings().m_singleSided;
|
|
bool side = aSide;
|
|
VECTOR2D dir( aBase.B - aBase.A );
|
|
|
|
if( !m_dual )
|
|
AddCorner( aBase.A );
|
|
|
|
bool turning = false;
|
|
bool started = false;
|
|
|
|
m_last = aBase.A;
|
|
|
|
do
|
|
{
|
|
MEANDER_SHAPE m( m_placer, m_width, m_dual );
|
|
|
|
m.SetBaselineOffset( m_baselineOffset );
|
|
m.SetBaseIndex( aBaseIndex );
|
|
|
|
double thr = (double) m.spacing();
|
|
|
|
bool fail = false;
|
|
double remaining = base_len - ( m_last - aBase.A ).EuclideanNorm();
|
|
|
|
auto addSingleIfFits = [&]()
|
|
{
|
|
fail = true;
|
|
|
|
if( m.Fit( MT_SINGLE, aBase, m_last, side ) )
|
|
{
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
fail = false;
|
|
started = false;
|
|
}
|
|
|
|
if( fail && !singleSided )
|
|
{
|
|
if( m.Fit( MT_SINGLE, aBase, m_last, !side ) )
|
|
{
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
fail = false;
|
|
started = false;
|
|
side = !side;
|
|
}
|
|
}
|
|
};
|
|
|
|
if( remaining < Settings( ).m_step )
|
|
break;
|
|
|
|
if( !singleSided && remaining > 3.0 * thr )
|
|
{
|
|
if( !turning )
|
|
{
|
|
for( int i = 0; i < 2; i++ )
|
|
{
|
|
bool checkSide = ( i == 0 ) ? side : !side;
|
|
|
|
if( m.Fit( MT_CHECK_START, aBase, m_last, checkSide ) )
|
|
{
|
|
turning = true;
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
side = !checkSide;
|
|
started = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !turning )
|
|
addSingleIfFits();
|
|
}
|
|
else
|
|
{
|
|
bool rv = m.Fit( MT_CHECK_FINISH, aBase, m_last, side );
|
|
|
|
if( rv )
|
|
{
|
|
m.Fit( MT_TURN, aBase, m_last, side );
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
side = !side;
|
|
started = true;
|
|
}
|
|
else
|
|
{
|
|
m.Fit( MT_FINISH, aBase, m_last, side );
|
|
started = false;
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
turning = false;
|
|
}
|
|
}
|
|
}
|
|
else if( !singleSided && started )
|
|
{
|
|
bool rv = m.Fit( MT_FINISH, aBase, m_last, side );
|
|
|
|
if( rv )
|
|
AddMeander( new MEANDER_SHAPE( m ) );
|
|
|
|
break;
|
|
|
|
}
|
|
else if( !turning && remaining > thr * 2.0 )
|
|
{
|
|
addSingleIfFits();
|
|
}
|
|
else
|
|
{
|
|
fail = true;
|
|
}
|
|
|
|
remaining = base_len - ( m_last - aBase.A ).EuclideanNorm( );
|
|
|
|
if( remaining < Settings( ).m_step )
|
|
break;
|
|
|
|
if( fail )
|
|
{
|
|
MEANDER_SHAPE tmp( m_placer, m_width, m_dual );
|
|
tmp.SetBaselineOffset( m_baselineOffset );
|
|
tmp.SetBaseIndex( aBaseIndex );
|
|
|
|
int nextP = tmp.spacing() - 2 * tmp.cornerRadius() + Settings().m_step;
|
|
VECTOR2I pn = m_last + dir.Resize( nextP );
|
|
|
|
if( aBase.Contains( pn ) && !m_dual )
|
|
{
|
|
AddCorner( pn );
|
|
} else
|
|
break;
|
|
}
|
|
|
|
|
|
} while( true );
|
|
|
|
if( !m_dual )
|
|
AddCorner( aBase.B );
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
int optCr = (int64_t) spacing() * rPercent / 200;
|
|
int minCr = std::abs( m_baselineOffset );
|
|
int maxCr = std::min( m_amplitude / 2, spacing() / 2 );
|
|
|
|
if( maxCr > minCr )
|
|
return std::clamp( optCr, minCr, maxCr );
|
|
else
|
|
return maxCr;
|
|
}
|
|
|
|
|
|
int MEANDER_SHAPE::spacing( ) const
|
|
{
|
|
if( !m_dual )
|
|
{
|
|
return std::max( m_width + m_placer->Clearance(), Settings().m_spacing );
|
|
}
|
|
else
|
|
{
|
|
int sp = m_width + m_placer->Clearance() + ( 2 * std::abs( m_baselineOffset ) );
|
|
return std::max( sp, Settings().m_spacing );
|
|
}
|
|
}
|
|
|
|
|
|
SHAPE_LINE_CHAIN MEANDER_SHAPE::makeMiterShape( const VECTOR2D& aP, const VECTOR2D& aDir,
|
|
bool aSide )
|
|
{
|
|
SHAPE_LINE_CHAIN lc;
|
|
|
|
if( aDir.EuclideanNorm( ) == 0.0f )
|
|
{
|
|
lc.Append( aP );
|
|
return lc;
|
|
}
|
|
|
|
VECTOR2D dir_u( aDir );
|
|
VECTOR2D dir_v( aDir.Perpendicular( ) );
|
|
VECTOR2D p = aP;
|
|
lc.Append( ( int ) p.x, ( int ) p.y );
|
|
|
|
// fixme: refactor
|
|
switch( m_placer->MeanderSettings().m_cornerStyle )
|
|
{
|
|
case MEANDER_STYLE_ROUND:
|
|
{
|
|
VECTOR2D center = aP + dir_v * ( aSide ? -1.0 : 1.0 );
|
|
|
|
lc.Append( SHAPE_ARC( center, aP, ( aSide ? -ANGLE_90 : ANGLE_90 ) ) );
|
|
}
|
|
break;
|
|
|
|
case MEANDER_STYLE_CHAMFER:
|
|
{
|
|
double radius = (double) aDir.EuclideanNorm();
|
|
double correction = 0;
|
|
|
|
if( m_dual && radius > m_meanCornerRadius )
|
|
correction = (double)( -2 * abs(m_baselineOffset) ) * tan( 22.5 * M_PI / 180.0 );
|
|
|
|
VECTOR2D dir_cu = dir_u.Resize( correction );
|
|
VECTOR2D dir_cv = dir_v.Resize( correction );
|
|
|
|
p = aP - dir_cu;
|
|
lc.Append( ( int ) p.x, ( int ) p.y );
|
|
p = aP + dir_u + (dir_v + dir_cv) * ( aSide ? -1.0 : 1.0 );
|
|
lc.Append( ( int ) p.x, ( int ) p.y );
|
|
}
|
|
break;
|
|
}
|
|
|
|
p = aP + dir_u + dir_v * ( aSide ? -1.0 : 1.0 );
|
|
lc.Append( ( int ) p.x, ( int ) p.y );
|
|
|
|
return lc;
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir )
|
|
{
|
|
m_currentTarget = aTarget;
|
|
m_currentTarget->Clear();
|
|
m_currentTarget->Append( aWhere );
|
|
m_currentDir = aDir;
|
|
m_currentPos = aWhere;
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::forward( int aLength )
|
|
{
|
|
m_currentPos += m_currentDir.Resize( aLength );
|
|
m_currentTarget->Append( m_currentPos );
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::turn( const EDA_ANGLE& aAngle )
|
|
{
|
|
RotatePoint( m_currentDir, aAngle );
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::miter( int aRadius, bool aSide )
|
|
{
|
|
if( aRadius <= 0 )
|
|
{
|
|
turn( aSide ? ANGLE_90 : -ANGLE_90 );
|
|
return;
|
|
}
|
|
|
|
VECTOR2D dir = m_currentDir.Resize( (double) aRadius );
|
|
SHAPE_LINE_CHAIN lc = makeMiterShape( m_currentPos, dir, aSide );
|
|
|
|
m_currentPos = lc.CPoint( -1 );
|
|
turn( aSide ? ANGLE_90 : -ANGLE_90 );
|
|
|
|
m_currentTarget->Append( lc );
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::uShape( int aSides, int aCorner, int aTop )
|
|
{
|
|
forward( aSides );
|
|
miter( aCorner, true );
|
|
forward( aTop );
|
|
miter( aCorner, true );
|
|
forward( aSides );
|
|
}
|
|
|
|
|
|
SHAPE_LINE_CHAIN MEANDER_SHAPE::genMeanderShape( const VECTOR2D& aP, const VECTOR2D& aDir,
|
|
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;
|
|
|
|
VECTOR2D dir_u_b( aDir.Resize( offset ) );
|
|
VECTOR2D dir_v_b( dir_u_b.Perpendicular() );
|
|
|
|
if( 2 * cr > amplitude )
|
|
{
|
|
cr = amplitude / 2;
|
|
}
|
|
|
|
if( 2 * cr > spc )
|
|
{
|
|
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 );
|
|
|
|
switch( aType )
|
|
{
|
|
case MT_EMPTY:
|
|
{
|
|
lc.Append( aP + dir_v_b + aDir );
|
|
break;
|
|
}
|
|
case MT_START:
|
|
{
|
|
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( sCorner, uCorner ) );
|
|
forward( std::abs( offset ) );
|
|
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( turnSide, uCorner, top );
|
|
forward( std::abs( offset ) );
|
|
break;
|
|
}
|
|
|
|
case MT_SINGLE:
|
|
{
|
|
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;
|
|
}
|
|
|
|
if( aSide )
|
|
{
|
|
SEG axis( aP, aP + aDir );
|
|
|
|
lc.Mirror( axis );
|
|
}
|
|
|
|
return lc;
|
|
}
|
|
|
|
|
|
bool MEANDERED_LINE::CheckSelfIntersections( MEANDER_SHAPE* aShape, int aClearance )
|
|
{
|
|
for( int i = m_meanders.size() - 1; i >= 0; i-- )
|
|
{
|
|
MEANDER_SHAPE* m = m_meanders[i];
|
|
|
|
if( m->Type() == MT_EMPTY || m->Type() == MT_CORNER )
|
|
continue;
|
|
|
|
const SEG& b1 = aShape->BaseSegment();
|
|
const SEG& b2 = m->BaseSegment();
|
|
|
|
if( b1.ApproxParallel( b2 ) )
|
|
continue;
|
|
|
|
int n = m->CLine( 0 ).SegmentCount();
|
|
|
|
for( int j = n - 1; j >= 0; j-- )
|
|
{
|
|
if( aShape->CLine( 0 ).Collide( m->CLine( 0 ) .CSegment( j ), aClearance ) )
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MEANDER_SHAPE::Fit( MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide )
|
|
{
|
|
const MEANDER_SETTINGS& st = Settings();
|
|
|
|
bool checkMode = false;
|
|
MEANDER_TYPE prim1, prim2;
|
|
|
|
if( aType == MT_CHECK_START )
|
|
{
|
|
prim1 = MT_START;
|
|
prim2 = MT_TURN;
|
|
checkMode = true;
|
|
}
|
|
else if( aType == MT_CHECK_FINISH )
|
|
{
|
|
prim1 = MT_TURN;
|
|
prim2 = MT_FINISH;
|
|
checkMode = true;
|
|
}
|
|
|
|
if( checkMode )
|
|
{
|
|
MEANDER_SHAPE m1( m_placer, m_width, m_dual );
|
|
MEANDER_SHAPE m2( m_placer, m_width, m_dual );
|
|
|
|
m1.SetBaselineOffset( m_baselineOffset );
|
|
m2.SetBaselineOffset( m_baselineOffset );
|
|
|
|
bool c1 = m1.Fit( prim1, aSeg, aP, aSide );
|
|
bool c2 = false;
|
|
|
|
if( c1 )
|
|
c2 = m2.Fit( prim2, aSeg, m1.End(), !aSide );
|
|
|
|
if( c1 && c2 )
|
|
{
|
|
m_type = prim1;
|
|
m_shapes[0] = m1.m_shapes[0];
|
|
m_shapes[1] = m1.m_shapes[1];
|
|
m_baseSeg =aSeg;
|
|
m_p0 = aP;
|
|
m_side = aSide;
|
|
m_amplitude = m1.Amplitude();
|
|
m_dual = m1.m_dual;
|
|
m_baseSeg = m1.m_baseSeg;
|
|
m_baseIndex = m1.m_baseIndex;
|
|
updateBaseSegment();
|
|
m_baselineOffset = m1.m_baselineOffset;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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, 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, 0 );
|
|
}
|
|
|
|
m_type = aType;
|
|
m_baseSeg = aSeg;
|
|
m_p0 = aP;
|
|
m_side = aSide;
|
|
|
|
updateBaseSegment();
|
|
|
|
if( m_placer->CheckFit( this ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::Recalculate()
|
|
{
|
|
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_baselineOffset );
|
|
|
|
updateBaseSegment();
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::Resize( int aAmpl )
|
|
{
|
|
if( aAmpl < 0 )
|
|
return;
|
|
|
|
m_amplitude = aAmpl;
|
|
|
|
Recalculate();
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::MakeEmpty()
|
|
{
|
|
updateBaseSegment();
|
|
|
|
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, m_dual ? m_baselineOffset : 0 );
|
|
|
|
if( m_dual )
|
|
m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, -m_baselineOffset );
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::AddCorner( const VECTOR2I& aA, const VECTOR2I& aB )
|
|
{
|
|
MEANDER_SHAPE* m = new MEANDER_SHAPE( m_placer, m_width, m_dual );
|
|
|
|
m->MakeCorner( aA, aB );
|
|
m_last = aA;
|
|
|
|
m_meanders.push_back( m );
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::AddArc( const SHAPE_ARC& aArc1, const SHAPE_ARC& aArc2 )
|
|
{
|
|
MEANDER_SHAPE* m = new MEANDER_SHAPE( m_placer, m_width, m_dual );
|
|
|
|
m->MakeArc( aArc1, aArc2 );
|
|
m_last = aArc1.GetP1();
|
|
|
|
m_meanders.push_back( m );
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::AddArcAndPt( const SHAPE_ARC& aArc1, const VECTOR2I& aPt2 )
|
|
{
|
|
SHAPE_ARC arc2( aPt2, aPt2, aPt2, 0 );
|
|
|
|
AddArc( aArc1, arc2 );
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::AddPtAndArc( const VECTOR2I& aPt1, const SHAPE_ARC& aArc2 )
|
|
{
|
|
SHAPE_ARC arc1( aPt1, aPt1, aPt1, 0 );
|
|
|
|
AddArc( arc1, aArc2 );
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::MakeCorner( const VECTOR2I& aP1, const VECTOR2I& aP2 )
|
|
{
|
|
SetType( MT_CORNER );
|
|
m_shapes[0].Clear();
|
|
m_shapes[1].Clear();
|
|
m_shapes[0].Append( aP1 );
|
|
m_shapes[1].Append( aP2 );
|
|
m_clippedBaseSeg.A = aP1;
|
|
m_clippedBaseSeg.B = aP1;
|
|
}
|
|
|
|
|
|
void MEANDER_SHAPE::MakeArc( const SHAPE_ARC& aArc1, const SHAPE_ARC& aArc2 )
|
|
{
|
|
SetType( MT_CORNER );
|
|
m_shapes[0].Clear();
|
|
m_shapes[1].Clear();
|
|
m_shapes[0].Append( aArc1 );
|
|
m_shapes[1].Append( aArc2 );
|
|
m_clippedBaseSeg.A = aArc1.GetP1();
|
|
m_clippedBaseSeg.B = aArc1.GetP1();
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::AddMeander( MEANDER_SHAPE* aShape )
|
|
{
|
|
m_last = aShape->BaseSegment().B;
|
|
m_meanders.push_back( aShape );
|
|
}
|
|
|
|
|
|
void MEANDERED_LINE::Clear()
|
|
{
|
|
for( MEANDER_SHAPE* m : m_meanders )
|
|
{
|
|
delete m;
|
|
}
|
|
|
|
m_meanders.clear( );
|
|
}
|
|
|
|
|
|
int MEANDER_SHAPE::BaselineLength() const
|
|
{
|
|
return m_clippedBaseSeg.Length();
|
|
}
|
|
|
|
|
|
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 )
|
|
{
|
|
VECTOR2I midpA = ( CLine( 0 ).CPoint( 0 ) + CLine( 1 ).CPoint( 0 ) ) / 2;
|
|
VECTOR2I midpB = ( CLine( 0 ).CPoint( -1 ) + CLine( 1 ).CPoint( -1 ) ) / 2;
|
|
|
|
m_clippedBaseSeg.A = m_baseSeg.LineProject( midpA );
|
|
m_clippedBaseSeg.B = m_baseSeg.LineProject( midpB );
|
|
}
|
|
else
|
|
{
|
|
m_clippedBaseSeg.A = m_baseSeg.LineProject( CLine( 0 ).CPoint( 0 ) );
|
|
m_clippedBaseSeg.B = m_baseSeg.LineProject( CLine( 0 ).CPoint( -1 ) );
|
|
}
|
|
}
|
|
|
|
}
|