mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-19 23:21:41 +00:00
Unify length calculation between router, board / frame, and DRC
This commit is contained in:
parent
12861345d3
commit
906c24bc6d
common
libs/kimath/include/geometry
pcbnew
board.cppboard.h
drc
length_calculation.cpplength_calculation.hrouter
pns_kicad_iface.cpppns_kicad_iface.hpns_meander_placer_base.cpppns_router.hpns_solid.hpns_topology.cpp
widgets
qa
@ -861,6 +861,8 @@ set( PCB_COMMON_SRCS
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/tools/pcb_editor_conditions.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/tools/pcb_viewer_tools.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/length_calculation.cpp
|
||||
|
||||
widgets/net_selector.cpp
|
||||
)
|
||||
|
||||
|
@ -301,7 +301,7 @@ ADVANCED_CFG::ADVANCED_CFG()
|
||||
|
||||
m_MinimumMarkerSeparationDistance = 0.15;
|
||||
|
||||
m_NetInspectorBulkUpdateOptimisationThreshold = 25;
|
||||
m_NetInspectorBulkUpdateOptimisationThreshold = 100;
|
||||
|
||||
m_ExcludeFromSimulationLineWidth = 25;
|
||||
|
||||
|
@ -409,10 +409,7 @@ public:
|
||||
return m_points[aIndex];
|
||||
}
|
||||
|
||||
const std::vector<VECTOR2I>& CPoints() const
|
||||
{
|
||||
return m_points;
|
||||
}
|
||||
const std::vector<VECTOR2I>& CPoints() const { return m_points; }
|
||||
|
||||
/**
|
||||
* Return the last point in the line chain.
|
||||
|
@ -75,12 +75,11 @@ VECTOR2I BOARD_ITEM::ZeroOffset( 0, 0 );
|
||||
|
||||
BOARD::BOARD() :
|
||||
BOARD_ITEM_CONTAINER( (BOARD_ITEM*) nullptr, PCB_T ), m_LegacyDesignSettingsLoaded( false ),
|
||||
m_LegacyCopperEdgeClearanceLoaded( false ), m_LegacyNetclassesLoaded( false ),
|
||||
m_boardUse( BOARD_USE::NORMAL ), m_timeStamp( 1 ), m_paper( PAGE_INFO::A4 ),
|
||||
m_project( nullptr ), m_userUnits( EDA_UNITS::MM ),
|
||||
m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
|
||||
m_NetInfo( this ), m_embedFonts( false ),
|
||||
m_componentClassManager( std::make_unique<COMPONENT_CLASS_MANAGER>( this ) )
|
||||
m_LegacyCopperEdgeClearanceLoaded( false ), m_LegacyNetclassesLoaded( false ), m_boardUse( BOARD_USE::NORMAL ),
|
||||
m_timeStamp( 1 ), m_paper( PAGE_INFO::A4 ), m_project( nullptr ), m_userUnits( EDA_UNITS::MM ),
|
||||
m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ), m_NetInfo( this ),
|
||||
m_embedFonts( false ), m_componentClassManager( std::make_unique<COMPONENT_CLASS_MANAGER>( this ) ),
|
||||
m_lengthCalc( std::make_unique<LENGTH_CALCULATION>( this ) )
|
||||
{
|
||||
// A too small value do not allow connecting 2 shapes (i.e. segments) not exactly connected
|
||||
// A too large value do not allow safely connecting 2 shapes like very short segments.
|
||||
@ -2399,82 +2398,24 @@ BOARD_STACKUP BOARD::GetStackupOrDefault() const
|
||||
|
||||
std::tuple<int, double, double> BOARD::GetTrackLength( const PCB_TRACK& aTrack ) const
|
||||
{
|
||||
int count = 0;
|
||||
double length = 0.0;
|
||||
double package_length = 0.0;
|
||||
auto connectivity = GetBoard()->GetConnectivity();
|
||||
|
||||
auto connectivity = GetBoard()->GetConnectivity();
|
||||
BOARD_STACKUP& stackup = GetDesignSettings().GetStackupDescriptor();
|
||||
bool useHeight = GetDesignSettings().m_UseHeightForLengthCalcs;
|
||||
std::vector<LENGTH_CALCULATION_ITEM> items;
|
||||
|
||||
for( BOARD_CONNECTED_ITEM* item : connectivity->GetConnectedItems( &aTrack, EXCLUDE_ZONES ) )
|
||||
for( BOARD_CONNECTED_ITEM* boardItem : connectivity->GetConnectedItems( &aTrack, EXCLUDE_ZONES ) )
|
||||
{
|
||||
count++;
|
||||
LENGTH_CALCULATION_ITEM item = GetLengthCalculation()->GetLengthCalculationItem( boardItem );
|
||||
|
||||
if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T && useHeight )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( track );
|
||||
length += stackup.GetLayerDistance( via->TopLayer(), via->BottomLayer() );
|
||||
continue;
|
||||
}
|
||||
else if( track->Type() == PCB_ARC_T )
|
||||
{
|
||||
// Note: we don't apply the clip-to-pad optimization if an arc ends in a pad
|
||||
// Room for future improvement.
|
||||
length += track->GetLength();
|
||||
continue;
|
||||
}
|
||||
|
||||
bool inPad = false;
|
||||
SEG trackSeg( track->GetStart(), track->GetEnd() );
|
||||
double segLen = trackSeg.Length();
|
||||
double segInPadLen = 0;
|
||||
|
||||
for( auto pad_it : connectivity->GetConnectedPads( item ) )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( pad_it );
|
||||
|
||||
bool hitStart = pad->HitTest( track->GetStart(), track->GetWidth() / 2 );
|
||||
bool hitEnd = pad->HitTest( track->GetEnd(), track->GetWidth() / 2 );
|
||||
|
||||
if( hitStart && hitEnd )
|
||||
{
|
||||
inPad = true;
|
||||
break;
|
||||
}
|
||||
else if( hitStart || hitEnd )
|
||||
{
|
||||
VECTOR2I loc;
|
||||
|
||||
// We may not collide even if we passed the bounding-box hit test
|
||||
if( pad->GetEffectivePolygon( track->GetLayer(), ERROR_INSIDE )->Collide( trackSeg, 0, nullptr, &loc ) )
|
||||
{
|
||||
// Part 1: length of the seg to the intersection with the pad poly
|
||||
if( hitStart )
|
||||
trackSeg.A = loc;
|
||||
else
|
||||
trackSeg.B = loc;
|
||||
|
||||
segLen = trackSeg.Length();
|
||||
|
||||
// Part 2: length from the intersection to the pad anchor
|
||||
segInPadLen += ( loc - pad->GetPosition() ).EuclideanNorm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !inPad )
|
||||
length += segLen + segInPadLen;
|
||||
}
|
||||
else if( PAD* pad = dynamic_cast<PAD*>( item ) )
|
||||
{
|
||||
package_length += pad->GetPadToDieLength();
|
||||
}
|
||||
if( item.Type() != LENGTH_CALCULATION_ITEM::TYPE::UNKNOWN )
|
||||
items.push_back( item );
|
||||
}
|
||||
|
||||
return std::make_tuple( count, length, package_length );
|
||||
constexpr PATH_OPTIMISATIONS opts = {
|
||||
.OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
|
||||
};
|
||||
LENGTH_DETAILS details = GetLengthCalculation()->CalculateLengthDetails( items, opts );
|
||||
|
||||
return std::make_tuple( items.size(), details.TrackLength + details.ViaLength, details.PadToDieLength );
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <convert_shape_list_to_polygon.h> // for OUTLINE_ERROR_HANDLER
|
||||
#include <hash.h>
|
||||
#include <layer_ids.h>
|
||||
#include <length_calculation.h>
|
||||
#include <lset.h>
|
||||
#include <netinfo.h>
|
||||
#include <pcb_item_containers.h>
|
||||
@ -1304,6 +1305,11 @@ public:
|
||||
*/
|
||||
void EmbedFonts() override;
|
||||
|
||||
/**
|
||||
* Returns the track length calculator
|
||||
*/
|
||||
LENGTH_CALCULATION* GetLengthCalculation() const { return m_lengthCalc.get(); }
|
||||
|
||||
/**
|
||||
* Gets the component class manager
|
||||
*/
|
||||
@ -1428,6 +1434,8 @@ private:
|
||||
bool m_embedFonts;
|
||||
|
||||
std::unique_ptr<COMPONENT_CLASS_MANAGER> m_componentClassManager;
|
||||
|
||||
std::unique_ptr<LENGTH_CALCULATION> m_lengthCalc;
|
||||
};
|
||||
|
||||
|
||||
|
@ -345,20 +345,25 @@ bool DRC_TEST_PROVIDER_MATCHED_LENGTH::runInternal( bool aDelayReportMode )
|
||||
return true;
|
||||
} );
|
||||
|
||||
LENGTH_CALCULATION* calc = m_board->GetLengthCalculation();
|
||||
|
||||
std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
|
||||
|
||||
for( const std::pair< DRC_RULE* const, std::set<BOARD_CONNECTED_ITEM*> >& it : itemSets )
|
||||
for( const auto& [rule, ruleItems] : itemSets )
|
||||
{
|
||||
std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
|
||||
|
||||
for( BOARD_CONNECTED_ITEM* citem : it.second )
|
||||
netMap[ citem->GetNetCode() ].insert( citem );
|
||||
for( BOARD_CONNECTED_ITEM* item : ruleItems )
|
||||
netMap[item->GetNetCode()].insert( item );
|
||||
|
||||
for( const std::pair< const int, std::set<BOARD_CONNECTED_ITEM*> >& nitem : netMap )
|
||||
for( const auto& [netCode, netItems] : netMap )
|
||||
{
|
||||
std::vector<LENGTH_CALCULATION_ITEM> lengthItems;
|
||||
lengthItems.reserve( netItems.size() );
|
||||
|
||||
CONNECTION ent;
|
||||
ent.items = nitem.second;
|
||||
ent.netcode = nitem.first;
|
||||
ent.items = netItems;
|
||||
ent.netcode = netCode;
|
||||
ent.netname = m_board->GetNetInfo().GetNetItem( ent.netcode )->GetNetname();
|
||||
ent.netinfo = m_board->GetNetInfo().GetNetItem( ent.netcode );
|
||||
|
||||
@ -369,43 +374,24 @@ bool DRC_TEST_PROVIDER_MATCHED_LENGTH::runInternal( bool aDelayReportMode )
|
||||
ent.fromItem = nullptr;
|
||||
ent.toItem = nullptr;
|
||||
|
||||
for( BOARD_CONNECTED_ITEM* citem : nitem.second )
|
||||
for( BOARD_CONNECTED_ITEM* item : netItems )
|
||||
{
|
||||
if( citem->Type() == PCB_VIA_T )
|
||||
{
|
||||
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
|
||||
LENGTH_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( item );
|
||||
|
||||
ent.viaCount++;
|
||||
|
||||
if( bds.m_UseHeightForLengthCalcs )
|
||||
{
|
||||
const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
|
||||
PCB_LAYER_ID topmost;
|
||||
PCB_LAYER_ID bottommost;
|
||||
|
||||
v->GetOutermostConnectedLayers( &topmost, &bottommost );
|
||||
|
||||
if( topmost != UNDEFINED_LAYER && topmost != bottommost )
|
||||
ent.totalVia += stackup.GetLayerDistance( topmost, bottommost );
|
||||
}
|
||||
}
|
||||
else if( citem->Type() == PCB_TRACE_T )
|
||||
{
|
||||
ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
|
||||
}
|
||||
else if ( citem->Type() == PCB_ARC_T )
|
||||
{
|
||||
ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
|
||||
}
|
||||
else if( citem->Type() == PCB_PAD_T )
|
||||
{
|
||||
ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
|
||||
}
|
||||
if( lengthItem.Type() != LENGTH_CALCULATION_ITEM::TYPE::UNKNOWN )
|
||||
lengthItems.emplace_back( lengthItem );
|
||||
}
|
||||
|
||||
constexpr PATH_OPTIMISATIONS opts = {
|
||||
.OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
|
||||
};
|
||||
LENGTH_DETAILS details = calc->CalculateLengthDetails( lengthItems, opts );
|
||||
ent.viaCount = details.NumVias;
|
||||
ent.totalVia = details.ViaLength;
|
||||
ent.totalRoute = static_cast<double>( details.TrackLength );
|
||||
ent.totalPadToDie = details.PadToDieLength;
|
||||
ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
|
||||
ent.matchingRule = it.first;
|
||||
ent.matchingRule = rule;
|
||||
|
||||
// fixme: doesn't seem to work ;-)
|
||||
auto ftPath = ftCache->QueryFromToPath( ent.items );
|
||||
@ -421,7 +407,7 @@ bool DRC_TEST_PROVIDER_MATCHED_LENGTH::runInternal( bool aDelayReportMode )
|
||||
}
|
||||
|
||||
m_report.Add( ent );
|
||||
matches[ it.first ].push_back(ent);
|
||||
matches[rule].push_back( ent );
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,19 +438,14 @@ bool DRC_TEST_PROVIDER_MATCHED_LENGTH::runInternal( bool aDelayReportMode )
|
||||
|
||||
for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
|
||||
{
|
||||
reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
|
||||
"%d matching items, "
|
||||
"total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
|
||||
"vias: %d" ),
|
||||
ent.netname,
|
||||
ent.from,
|
||||
ent.to,
|
||||
(int) ent.items.size(),
|
||||
MessageTextFromValue( ent.total ),
|
||||
MessageTextFromValue( ent.totalRoute ),
|
||||
MessageTextFromValue( ent.totalVia ),
|
||||
MessageTextFromValue( ent.totalPadToDie ),
|
||||
ent.viaCount ) );
|
||||
reportAux( wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
|
||||
"%d matching items, "
|
||||
"total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
|
||||
"vias: %d" ),
|
||||
ent.netname, ent.from, ent.to, static_cast<int>( ent.items.size() ),
|
||||
MessageTextFromValue( ent.total ), MessageTextFromValue( ent.totalRoute ),
|
||||
MessageTextFromValue( ent.totalVia ),
|
||||
MessageTextFromValue( ent.totalPadToDie ), ent.viaCount ) );
|
||||
}
|
||||
|
||||
|
||||
|
556
pcbnew/length_calculation.cpp
Normal file
556
pcbnew/length_calculation.cpp
Normal file
@ -0,0 +1,556 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 <length_calculation.h>
|
||||
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
|
||||
|
||||
void LENGTH_CALCULATION_ITEM::CalculateViaLayers( const BOARD* aBoard )
|
||||
{
|
||||
static std::initializer_list<KICAD_T> traceAndPadTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T };
|
||||
|
||||
PCB_LAYER_ID top_layer = UNDEFINED_LAYER;
|
||||
PCB_LAYER_ID bottom_layer = UNDEFINED_LAYER;
|
||||
|
||||
const LSET layers = aBoard->GetDesignSettings().GetEnabledLayers();
|
||||
|
||||
for( auto layer_it = layers.copper_layers_begin(); layer_it != layers.copper_layers_end(); ++layer_it )
|
||||
{
|
||||
if( aBoard->GetConnectivity()->IsConnectedOnLayer( m_via, *layer_it, traceAndPadTypes ) )
|
||||
{
|
||||
if( top_layer == UNDEFINED_LAYER )
|
||||
top_layer = *layer_it;
|
||||
else
|
||||
bottom_layer = *layer_it;
|
||||
}
|
||||
}
|
||||
|
||||
if( top_layer == UNDEFINED_LAYER )
|
||||
top_layer = m_via->TopLayer();
|
||||
if( bottom_layer == UNDEFINED_LAYER )
|
||||
bottom_layer = m_via->BottomLayer();
|
||||
|
||||
SetLayers( bottom_layer, top_layer );
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::clipLineToPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, PCB_LAYER_ID aLayer, bool aForward )
|
||||
{
|
||||
const int start = aForward ? 0 : aLine.PointCount() - 1;
|
||||
const int delta = aForward ? 1 : -1;
|
||||
|
||||
// Note: we don't apply the clip-to-pad optimization if an arc ends in a pad
|
||||
// Room for future improvement.
|
||||
if( aLine.IsPtOnArc( start ) )
|
||||
return;
|
||||
|
||||
const auto& shape = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
|
||||
|
||||
// Skip the "first" (or last) vertex, we already know it's contained in the pad
|
||||
int clip = start;
|
||||
|
||||
for( int vertex = start + delta; aForward ? vertex < aLine.PointCount() : vertex >= 0; vertex += delta )
|
||||
{
|
||||
SEG seg( aLine.GetPoint( vertex ), aLine.GetPoint( vertex - delta ) );
|
||||
|
||||
bool containsA = shape->Contains( seg.A );
|
||||
bool containsB = shape->Contains( seg.B );
|
||||
|
||||
if( containsA && containsB )
|
||||
{
|
||||
// Whole segment is inside: clip out this segment
|
||||
clip = vertex;
|
||||
}
|
||||
else if( containsB )
|
||||
{
|
||||
// Only one point inside: Find the intersection
|
||||
VECTOR2I loc;
|
||||
|
||||
if( shape->Collide( seg, 0, nullptr, &loc ) )
|
||||
{
|
||||
aLine.Replace( vertex - delta, vertex - delta, loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !aForward && clip < start )
|
||||
aLine.Remove( clip + 1, start );
|
||||
else if( clip > start )
|
||||
aLine.Remove( start, clip - 1 );
|
||||
|
||||
// Now connect the dots
|
||||
aLine.Insert( aForward ? 0 : aLine.PointCount(), aPad->GetPosition() );
|
||||
}
|
||||
|
||||
|
||||
LENGTH_DETAILS LENGTH_CALCULATION::CalculateLengthDetails( std::vector<LENGTH_CALCULATION_ITEM>& aItems,
|
||||
const PATH_OPTIMISATIONS aOptimisations,
|
||||
const PAD* aStartPad, const PAD* aEndPad,
|
||||
const bool aWithLayerLengths ) const
|
||||
{
|
||||
// If this set of items has not been optimised, optimise for shortest electrical path
|
||||
if( aOptimisations.OptimiseViaLayers || aOptimisations.MergeTracks || aOptimisations.MergeTracks )
|
||||
{
|
||||
std::vector<LENGTH_CALCULATION_ITEM*> pads;
|
||||
std::vector<LENGTH_CALCULATION_ITEM*> lines;
|
||||
std::vector<LENGTH_CALCULATION_ITEM*> vias;
|
||||
|
||||
// Map of line endpoints to line objects
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>> linesPositionMap;
|
||||
|
||||
// Map of pad positions to pad objects
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>> padsPositionMap;
|
||||
|
||||
for( LENGTH_CALCULATION_ITEM& item : aItems )
|
||||
{
|
||||
if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::PAD )
|
||||
{
|
||||
pads.emplace_back( &item );
|
||||
padsPositionMap[item.GetPad()->GetPosition()].insert( &item );
|
||||
}
|
||||
else if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::VIA )
|
||||
{
|
||||
vias.emplace_back( &item );
|
||||
}
|
||||
else if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::LINE )
|
||||
{
|
||||
lines.emplace_back( &item );
|
||||
linesPositionMap[item.GetLine().CPoint( 0 )].insert( &item );
|
||||
linesPositionMap[item.GetLine().CLastPoint()].insert( &item );
|
||||
}
|
||||
}
|
||||
|
||||
if( aOptimisations.OptimiseViaLayers )
|
||||
optimiseViaLayers( vias, lines, linesPositionMap, padsPositionMap );
|
||||
|
||||
if( aOptimisations.MergeTracks )
|
||||
mergeLines( lines, linesPositionMap );
|
||||
|
||||
if( aOptimisations.OptimiseTracesInPads )
|
||||
optimiseTracesInPads( pads, lines );
|
||||
}
|
||||
|
||||
LENGTH_DETAILS details;
|
||||
|
||||
if( aWithLayerLengths )
|
||||
details.LayerLengths = std::make_unique<std::map<PCB_LAYER_ID, int64_t>>();
|
||||
|
||||
const bool useHeight = m_board->GetDesignSettings().m_UseHeightForLengthCalcs;
|
||||
|
||||
// If this is a contiguous set of items, check if we have an inferred fanout via at either end. Note that this
|
||||
// condition only arises as a result of how PNS assembles tuning paths - for DRC / net inspector calculations these
|
||||
// fanout vias will be present in the object set and therefore do not need to be inferred
|
||||
if( aOptimisations.InferViaInPad && useHeight )
|
||||
{
|
||||
inferViaInPad( aStartPad, aItems.front(), details );
|
||||
inferViaInPad( aEndPad, aItems.back(), details );
|
||||
}
|
||||
|
||||
// Add stats for each item
|
||||
for( const LENGTH_CALCULATION_ITEM& item : aItems )
|
||||
{
|
||||
// Don't include merged items
|
||||
if( item.GetMergeStatus() == LENGTH_CALCULATION_ITEM::MERGE_STATUS::MERGED_RETIRED
|
||||
|| item.Type() == LENGTH_CALCULATION_ITEM::TYPE::UNKNOWN )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::LINE )
|
||||
{
|
||||
const int64_t length = item.GetLine().Length();
|
||||
|
||||
details.TrackLength += length;
|
||||
|
||||
if( details.LayerLengths )
|
||||
( *details.LayerLengths )[item.GetStartLayer()] += length;
|
||||
}
|
||||
else if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::VIA && useHeight )
|
||||
{
|
||||
const auto [layerStart, layerEnd] = item.GetLayers();
|
||||
details.ViaLength += stackupHeight( layerStart, layerEnd );
|
||||
details.NumVias += 1;
|
||||
}
|
||||
else if( item.Type() == LENGTH_CALCULATION_ITEM::TYPE::PAD )
|
||||
{
|
||||
details.PadToDieLength += item.GetPad()->GetPadToDieLength();
|
||||
details.NumPads += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::inferViaInPad( const PAD* aPad, const LENGTH_CALCULATION_ITEM& aItem,
|
||||
LENGTH_DETAILS& aDetails ) const
|
||||
{
|
||||
if( aPad && aItem.Type() == LENGTH_CALCULATION_ITEM::TYPE::LINE )
|
||||
{
|
||||
const PCB_LAYER_ID startBottomLayer = aItem.GetStartLayer();
|
||||
const LSET padLayers = aPad->Padstack().LayerSet();
|
||||
|
||||
if( !padLayers.Contains( startBottomLayer ) )
|
||||
{
|
||||
// This must be either F_Cu or B_Cu
|
||||
const PCB_LAYER_ID padLayer = padLayers.Contains( F_Cu ) ? F_Cu : B_Cu;
|
||||
|
||||
aDetails.NumVias += 1;
|
||||
aDetails.ViaLength += stackupHeight( startBottomLayer, padLayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int64_t LENGTH_CALCULATION::CalculateLength( std::vector<LENGTH_CALCULATION_ITEM>& aItems,
|
||||
const PATH_OPTIMISATIONS aOptimisations, const PAD* aStartPad,
|
||||
const PAD* aEndPad ) const
|
||||
{
|
||||
return CalculateLengthDetails( aItems, aOptimisations, aStartPad, aEndPad ).TotalLength();
|
||||
}
|
||||
|
||||
|
||||
int LENGTH_CALCULATION::stackupHeight( const PCB_LAYER_ID aFirstLayer, const PCB_LAYER_ID aSecondLayer ) const
|
||||
{
|
||||
if( !m_board || !m_board->GetDesignSettings().m_UseHeightForLengthCalcs )
|
||||
return 0;
|
||||
|
||||
if( m_board->GetDesignSettings().m_HasStackup )
|
||||
{
|
||||
const BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
|
||||
return stackup.GetLayerDistance( aFirstLayer, aSecondLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
BOARD_STACKUP stackup;
|
||||
stackup.BuildDefaultStackupList( &m_board->GetDesignSettings(), m_board->GetCopperLayerCount() );
|
||||
return stackup.GetLayerDistance( aFirstLayer, aSecondLayer );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::mergeLines(
|
||||
std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap )
|
||||
{
|
||||
// Vector of pads, and an associated flag to indicate whether they have been visited by the clustering algorithm
|
||||
std::vector<LENGTH_CALCULATION_ITEM*> pads;
|
||||
|
||||
auto removeFromPositionMap = [&aLinesPositionMap]( LENGTH_CALCULATION_ITEM* line )
|
||||
{
|
||||
aLinesPositionMap[line->GetLine().CPoint( 0 )].erase( line );
|
||||
aLinesPositionMap[line->GetLine().CLastPoint()].erase( line );
|
||||
};
|
||||
|
||||
// Attempts to merge unmerged lines in to aPrimaryLine
|
||||
auto tryMerge = [&removeFromPositionMap, &aLinesPositionMap]( const MERGE_POINT aMergePoint,
|
||||
const VECTOR2I& aMergePos,
|
||||
const LENGTH_CALCULATION_ITEM* aPrimaryItem,
|
||||
SHAPE_LINE_CHAIN& aPrimaryLine, bool* aDidMerge )
|
||||
{
|
||||
const auto startItr = aLinesPositionMap.find( aMergePos );
|
||||
|
||||
if( startItr == aLinesPositionMap.end() )
|
||||
return;
|
||||
|
||||
std::unordered_set<LENGTH_CALCULATION_ITEM*>& startItems = startItr->second;
|
||||
|
||||
if( startItems.size() != 1 )
|
||||
return;
|
||||
|
||||
LENGTH_CALCULATION_ITEM* lineToMerge = *startItems.begin();
|
||||
|
||||
// Don't merge if line is an arc
|
||||
if( !lineToMerge->GetLine().CArcs().empty() )
|
||||
return;
|
||||
|
||||
// Don't merge if lines are on different layers
|
||||
if( aPrimaryItem->GetStartLayer() != lineToMerge->GetStartLayer() )
|
||||
return;
|
||||
|
||||
// Merge the lines
|
||||
lineToMerge->SetMergeStatus( LENGTH_CALCULATION_ITEM::MERGE_STATUS::MERGED_RETIRED );
|
||||
mergeShapeLineChains( aPrimaryLine, lineToMerge->GetLine(), aMergePoint );
|
||||
removeFromPositionMap( lineToMerge );
|
||||
*aDidMerge = true;
|
||||
};
|
||||
|
||||
// Cluster all lines in to contiguous entities
|
||||
for( LENGTH_CALCULATION_ITEM* primaryItem : aLines )
|
||||
{
|
||||
// Don't start with an already merged line
|
||||
if( primaryItem->GetMergeStatus() != LENGTH_CALCULATION_ITEM::MERGE_STATUS::UNMERGED )
|
||||
continue;
|
||||
|
||||
// Remove starting line from the position map
|
||||
removeFromPositionMap( primaryItem );
|
||||
|
||||
SHAPE_LINE_CHAIN& primaryLine = primaryItem->GetLine();
|
||||
|
||||
// Merge all endpoints
|
||||
primaryItem->SetMergeStatus( LENGTH_CALCULATION_ITEM::MERGE_STATUS::MERGED_IN_USE );
|
||||
bool mergeComplete = false;
|
||||
|
||||
while( !mergeComplete )
|
||||
{
|
||||
bool startMerged = false;
|
||||
bool endMerged = false;
|
||||
|
||||
VECTOR2I startPos = primaryLine.CPoint( 0 );
|
||||
VECTOR2I endPos = primaryLine.CLastPoint();
|
||||
|
||||
tryMerge( MERGE_POINT::START, startPos, primaryItem, primaryLine, &startMerged );
|
||||
tryMerge( MERGE_POINT::END, endPos, primaryItem, primaryLine, &endMerged );
|
||||
|
||||
mergeComplete = !startMerged && !endMerged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::mergeShapeLineChains( SHAPE_LINE_CHAIN& aPrimary, const SHAPE_LINE_CHAIN& aSecondary,
|
||||
const MERGE_POINT aMergePoint )
|
||||
{
|
||||
if( aMergePoint == MERGE_POINT::START )
|
||||
{
|
||||
if( aSecondary.GetPoint( 0 ) == aPrimary.GetPoint( 0 ) )
|
||||
{
|
||||
for( auto itr = aSecondary.CPoints().begin() + 1; itr != aSecondary.CPoints().end(); ++itr )
|
||||
aPrimary.Insert( 0, *itr );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxASSERT( aSecondary.CLastPoint() == aPrimary.GetPoint( 0 ) );
|
||||
|
||||
for( auto itr = aSecondary.CPoints().rbegin() + 1; itr != aSecondary.CPoints().rend(); ++itr )
|
||||
aPrimary.Insert( 0, *itr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aSecondary.GetPoint( 0 ) == aPrimary.CLastPoint() )
|
||||
{
|
||||
for( auto itr = aSecondary.CPoints().begin() + 1; itr != aSecondary.CPoints().end(); ++itr )
|
||||
aPrimary.Append( *itr );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxASSERT( aSecondary.CLastPoint() == aPrimary.CLastPoint() );
|
||||
|
||||
for( auto itr = aSecondary.CPoints().rbegin() + 1; itr != aSecondary.CPoints().rend(); ++itr )
|
||||
aPrimary.Append( *itr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::optimiseTracesInPads( const std::vector<LENGTH_CALCULATION_ITEM*>& aPads,
|
||||
const std::vector<LENGTH_CALCULATION_ITEM*>& aLines )
|
||||
{
|
||||
for( LENGTH_CALCULATION_ITEM* padItem : aPads )
|
||||
{
|
||||
PAD* pad = padItem->GetPad();
|
||||
|
||||
for( LENGTH_CALCULATION_ITEM* lineItem : aLines )
|
||||
{
|
||||
// Ignore merged lines
|
||||
if( lineItem->GetMergeStatus() != LENGTH_CALCULATION_ITEM::MERGE_STATUS::MERGED_IN_USE )
|
||||
continue;
|
||||
|
||||
const PCB_LAYER_ID pcbLayer = lineItem->GetStartLayer();
|
||||
SHAPE_LINE_CHAIN& line = lineItem->GetLine();
|
||||
|
||||
OptimiseTraceInPad( line, pad, pcbLayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::optimiseViaLayers(
|
||||
const std::vector<LENGTH_CALCULATION_ITEM*>& aVias, std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap,
|
||||
const std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aPadsPositionMap )
|
||||
{
|
||||
for( LENGTH_CALCULATION_ITEM* via : aVias )
|
||||
{
|
||||
auto lineItr = aLinesPositionMap.find( via->GetVia()->GetPosition() );
|
||||
|
||||
if( lineItr == aLinesPositionMap.end() )
|
||||
continue;
|
||||
|
||||
std::unordered_set<LENGTH_CALCULATION_ITEM*>& connectedLines = lineItr->second;
|
||||
|
||||
if( connectedLines.empty() )
|
||||
{
|
||||
// No connected lines - this via is floating. Set both layers to the same
|
||||
via->SetLayers( via->GetVia()->GetLayer(), via->GetVia()->GetLayer() );
|
||||
}
|
||||
else if( connectedLines.size() == 1 )
|
||||
{
|
||||
// This is either a via stub, or a via-in-pad
|
||||
bool isViaInPad = false;
|
||||
const PCB_LAYER_ID lineLayer = ( *connectedLines.begin() )->GetStartLayer();
|
||||
|
||||
auto padItr = aPadsPositionMap.find( via->GetVia()->GetPosition() );
|
||||
|
||||
if( padItr != aLinesPositionMap.end() )
|
||||
{
|
||||
// This could be a via-in-pad - check for overlapping pads which are not on the line layer
|
||||
const std::unordered_set<LENGTH_CALCULATION_ITEM*>& pads = padItr->second;
|
||||
|
||||
if( pads.size() == 1 )
|
||||
{
|
||||
const LENGTH_CALCULATION_ITEM* padItem = *pads.begin();
|
||||
|
||||
if( !padItem->GetPad()->Padstack().LayerSet().Contains( lineLayer ) )
|
||||
{
|
||||
// This is probably a via-in-pad
|
||||
isViaInPad = true;
|
||||
via->SetLayers( lineLayer, padItem->GetStartLayer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !isViaInPad )
|
||||
{
|
||||
// This is a via stub - make its electrical length 0
|
||||
via->SetLayers( lineLayer, lineLayer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This via has more than one track ending at it. Calculate the connected layer span (which may be shorter
|
||||
// than the overall via span)
|
||||
LSET layers;
|
||||
|
||||
for( const LENGTH_CALCULATION_ITEM* lineItem : connectedLines )
|
||||
layers.set( lineItem->GetStartLayer() );
|
||||
|
||||
LSEQ cuStack = layers.CuStack();
|
||||
|
||||
PCB_LAYER_ID firstLayer = UNDEFINED_LAYER;
|
||||
PCB_LAYER_ID lastLayer = UNDEFINED_LAYER;
|
||||
|
||||
for( PCB_LAYER_ID layer : cuStack )
|
||||
{
|
||||
if( firstLayer == UNDEFINED_LAYER )
|
||||
firstLayer = layer;
|
||||
else
|
||||
lastLayer = layer;
|
||||
}
|
||||
|
||||
if( lastLayer == UNDEFINED_LAYER )
|
||||
via->SetLayers( firstLayer, firstLayer );
|
||||
else
|
||||
via->SetLayers( firstLayer, lastLayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LENGTH_CALCULATION::OptimiseTraceInPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, const PCB_LAYER_ID aPcbLayer )
|
||||
{
|
||||
// Only consider lines which terminate in the pad
|
||||
if( aLine.CPoint( 0 ) != aPad->GetPosition() && aLine.CLastPoint() != aPad->GetPosition() )
|
||||
return;
|
||||
|
||||
if( !aPad->FlashLayer( aPcbLayer ) )
|
||||
return;
|
||||
|
||||
const auto& shape = aPad->GetEffectivePolygon( aPcbLayer, ERROR_INSIDE );
|
||||
|
||||
if( shape->Contains( aLine.CPoint( 0 ) ) )
|
||||
clipLineToPad( aLine, aPad, aPcbLayer, true );
|
||||
else if( shape->Contains( aLine.CPoint( -1 ) ) )
|
||||
clipLineToPad( aLine, aPad, aPcbLayer, false );
|
||||
}
|
||||
|
||||
|
||||
LENGTH_CALCULATION_ITEM LENGTH_CALCULATION::GetLengthCalculationItem( BOARD_CONNECTED_ITEM* aBoardItem ) const
|
||||
{
|
||||
if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( aBoardItem ) )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( track );
|
||||
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetVia( via );
|
||||
item.CalculateViaLayers( m_board );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if( track->Type() == PCB_ARC_T )
|
||||
{
|
||||
PCB_ARC* arcParent = static_cast<PCB_ARC*>( track );
|
||||
SHAPE_ARC shapeArc( arcParent->GetStart(), arcParent->GetMid(), arcParent->GetEnd(),
|
||||
arcParent->GetWidth() );
|
||||
SHAPE_LINE_CHAIN chainArc( shapeArc );
|
||||
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetLine( chainArc );
|
||||
item.SetLayers( track->GetLayer() );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if( track->Type() == PCB_TRACE_T )
|
||||
{
|
||||
std::vector<VECTOR2I> points{ track->GetStart(), track->GetEnd() };
|
||||
SHAPE_LINE_CHAIN shape( points );
|
||||
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetLine( shape );
|
||||
item.SetLayers( track->GetLayer() );
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
else if( PAD* pad = dynamic_cast<PAD*>( aBoardItem ) )
|
||||
{
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetPad( pad );
|
||||
|
||||
LSET& layers = pad->Padstack().LayerSet();
|
||||
PCB_LAYER_ID firstLayer = UNDEFINED_LAYER;
|
||||
PCB_LAYER_ID secondLayer = UNDEFINED_LAYER;
|
||||
|
||||
for( auto itr = layers.copper_layers_begin(); itr != layers.copper_layers_end(); ++itr )
|
||||
{
|
||||
if( firstLayer == UNDEFINED_LAYER )
|
||||
firstLayer = *itr;
|
||||
else
|
||||
secondLayer = *itr;
|
||||
}
|
||||
|
||||
item.SetLayers( firstLayer, secondLayer );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
288
pcbnew/length_calculation.h
Normal file
288
pcbnew/length_calculation.h
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 PCBNEW_LENGTH_CALCULATION_H
|
||||
#define PCBNEW_LENGTH_CALCULATION_H
|
||||
|
||||
#include <board_design_settings.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <layer_ids.h>
|
||||
#include <unordered_set>
|
||||
#include <connectivity/connectivity_data.h>
|
||||
|
||||
class BOARD;
|
||||
|
||||
/**
|
||||
* Lightweight class which holds a pad, via, or a routed trace outline. Proxied objects passed by pointer are not
|
||||
* owned by this container.
|
||||
*/
|
||||
class LENGTH_CALCULATION_ITEM
|
||||
{
|
||||
public:
|
||||
/// The type of routing object this item proxies
|
||||
enum class TYPE
|
||||
{
|
||||
UNKNOWN,
|
||||
PAD,
|
||||
LINE,
|
||||
VIA
|
||||
};
|
||||
|
||||
/// Whether this item is UNMERGED, it has been merged and should be used (MERGED_IN_USE), or it has been merged
|
||||
/// and has been retired from use (MERGED_RETIRED). MERGED_RETIRED essentially means the object has been merged
|
||||
/// in to a MERGED_IN_USE item.
|
||||
enum class MERGE_STATUS
|
||||
{
|
||||
UNMERGED,
|
||||
MERGED_IN_USE,
|
||||
MERGED_RETIRED
|
||||
};
|
||||
|
||||
/// Gets the routing item type
|
||||
TYPE Type() const { return m_type; };
|
||||
|
||||
/// Sets the parent PAD associated with this item
|
||||
void SetPad( PAD* aPad )
|
||||
{
|
||||
m_type = TYPE::PAD;
|
||||
m_pad = aPad;
|
||||
}
|
||||
|
||||
/// Gets the parent PAD associated with this item
|
||||
PAD* GetPad() const { return m_pad; }
|
||||
|
||||
/// Sets the source SHAPE_LINE_CHAIN of this item
|
||||
void SetLine( const SHAPE_LINE_CHAIN& aLine )
|
||||
{
|
||||
m_type = TYPE::LINE;
|
||||
m_line = aLine;
|
||||
}
|
||||
|
||||
/// Gets the SHAPE_LINE_CHAIN associated with this item
|
||||
SHAPE_LINE_CHAIN& GetLine() const { return m_line; }
|
||||
|
||||
/// Sets the VIA associated with this item
|
||||
void SetVia( PCB_VIA* aVia )
|
||||
{
|
||||
m_type = TYPE::VIA;
|
||||
m_via = aVia;
|
||||
}
|
||||
|
||||
/// Gets the VIA associated with this item
|
||||
PCB_VIA* GetVia() const { return m_via; }
|
||||
|
||||
/// Sets the first and last layers associated with this item
|
||||
void SetLayers( const PCB_LAYER_ID aStart, const PCB_LAYER_ID aEnd = PCB_LAYER_ID::UNDEFINED_LAYER )
|
||||
{
|
||||
m_layerStart = aStart;
|
||||
m_layerEnd = aEnd;
|
||||
}
|
||||
|
||||
/// Sets the MERGE_STATUS of this item. MERGED_RETIRED essentially means the object has been merged
|
||||
/// in to a MERGED_IN_USE item.
|
||||
void SetMergeStatus( const MERGE_STATUS aStatus ) { m_mergeStatus = aStatus; }
|
||||
|
||||
/// Gets the MERGE_STATUS of this item
|
||||
MERGE_STATUS GetMergeStatus() const { return m_mergeStatus; }
|
||||
|
||||
/// Gets the upper and lower layers for the proxied item
|
||||
std::tuple<PCB_LAYER_ID, PCB_LAYER_ID> GetLayers() const { return { m_layerStart, m_layerEnd }; }
|
||||
|
||||
/// Gets the start board layer for the proxied item
|
||||
PCB_LAYER_ID GetStartLayer() const { return m_layerStart; }
|
||||
|
||||
/// Gets the end board layer for the proxied item.
|
||||
PCB_LAYER_ID GetEndLayer() const { return m_layerEnd; }
|
||||
|
||||
/// Calculates active via payers for a proxied VIA object
|
||||
void CalculateViaLayers( const BOARD* aBoard );
|
||||
|
||||
protected:
|
||||
/// A proxied PAD object. Set to nullptr if not proxying a PAD.
|
||||
PAD* m_pad{ nullptr };
|
||||
|
||||
/// A proxied SHAPE_LINE_CHAIN object. Line is empty if not proxying a SHAPE_LINE_CHAIN.
|
||||
mutable SHAPE_LINE_CHAIN m_line;
|
||||
|
||||
/// A proxied PVIAAD object. Set to nullptr if not proxying a VIA.
|
||||
PCB_VIA* m_via{ nullptr };
|
||||
|
||||
/// The start board layer for the proxied object
|
||||
PCB_LAYER_ID m_layerStart{ PCB_LAYER_ID::UNDEFINED_LAYER };
|
||||
|
||||
/// The end board layer for the proxied object
|
||||
PCB_LAYER_ID m_layerEnd{ PCB_LAYER_ID::UNDEFINED_LAYER };
|
||||
|
||||
/// Flags whether this item has already been merged with another
|
||||
MERGE_STATUS m_mergeStatus{ MERGE_STATUS::UNMERGED };
|
||||
|
||||
/// The routing object type of the proxied parent
|
||||
TYPE m_type{ TYPE::UNKNOWN };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Holds length measurement result details and statistics
|
||||
*/
|
||||
struct LENGTH_DETAILS
|
||||
{
|
||||
int NumPads{ 0 };
|
||||
int NumVias{ 0 };
|
||||
int ViaLength{ 0 };
|
||||
int64_t TrackLength{ 0 };
|
||||
int PadToDieLength{ 0 };
|
||||
std::unique_ptr<std::map<PCB_LAYER_ID, int64_t>> LayerLengths;
|
||||
|
||||
int64_t TotalLength() const { return ViaLength + TrackLength + PadToDieLength; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Struct to control which optimisations the length calculation code runs on
|
||||
* the given path objects. This is required as some call sites (e.g. PNS) run
|
||||
* their own path optimisation, whereas others (e.g. Net Inspector) do not.
|
||||
*/
|
||||
struct PATH_OPTIMISATIONS
|
||||
{
|
||||
/// Optimise via layers for height calculations, ensuring only the distance
|
||||
/// between routed segments is considered
|
||||
bool OptimiseViaLayers = false;
|
||||
|
||||
/// Merges all contiguous (end-to-end, same layer) tracks
|
||||
bool MergeTracks = false;
|
||||
|
||||
/// Optimises the electrical length of tracks within pads. Note that the track
|
||||
/// must terminate at the trace anchor point to be considered for
|
||||
/// optimisation. Will require MergeTracks if used with a non-contiguous item
|
||||
/// set.
|
||||
bool OptimiseTracesInPads = false;
|
||||
|
||||
/// Determines if there is a via-in-pad present on the board but not in the
|
||||
/// item set. This condition can arise from the PNS meander placer.
|
||||
/// TODO (JJ): This can be fixed in the router
|
||||
bool InferViaInPad = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class which calculates lengths (and associated routing statistics) in a BOARD context
|
||||
*/
|
||||
class LENGTH_CALCULATION
|
||||
{
|
||||
public:
|
||||
/// Construct the calculator in the given BOARD context
|
||||
explicit LENGTH_CALCULATION( BOARD* aBoard ) : m_board( aBoard ) {}
|
||||
|
||||
/**
|
||||
* @brief Calculates the electrical length of the given items
|
||||
* @param aItems is the vector of items making up the route
|
||||
* @param aPathType indicates whether this is an ordered route, or an unordered collection
|
||||
* @param aOptimised indicates whether this has been optimised for electrical length (e.g. clipping within pads)
|
||||
* @param aStartPad is the starting pad of the route
|
||||
* @param aEndPad is the ending pad of the route
|
||||
*/
|
||||
int64_t CalculateLength( std::vector<LENGTH_CALCULATION_ITEM>& aItems, PATH_OPTIMISATIONS aOptimisations,
|
||||
const PAD* aStartPad = nullptr, const PAD* aEndPad = nullptr ) const;
|
||||
|
||||
/**
|
||||
* @brief Calculates the electrical length of the given items
|
||||
* @param aItems is the vector of items making up the route
|
||||
* @param aPathType indicates whether this is an ordered route, or an unordered collection
|
||||
* @param aOptimised indicates whether this has been optimised for electrical length (e.g. clipping within pads)
|
||||
* @param aStartPad is the starting pad of the route
|
||||
* @param aEndPad is the ending pad of the route
|
||||
* @param aWithLayerLengths indicates whether the layer length structure should be populated
|
||||
*/
|
||||
LENGTH_DETAILS CalculateLengthDetails( std::vector<LENGTH_CALCULATION_ITEM>& aItems,
|
||||
PATH_OPTIMISATIONS aOptimisations, const PAD* aStartPad = nullptr,
|
||||
const PAD* aEndPad = nullptr, bool aWithLayerLengths = false ) const;
|
||||
|
||||
/// Optimises the given trace / line to minimise the electrical path length within the given pad
|
||||
static void OptimiseTraceInPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, PCB_LAYER_ID aPcbLayer );
|
||||
|
||||
/// Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM
|
||||
LENGTH_CALCULATION_ITEM GetLengthCalculationItem( BOARD_CONNECTED_ITEM* aBoardItem ) const;
|
||||
|
||||
protected:
|
||||
/// The parent board for all items
|
||||
BOARD* m_board;
|
||||
|
||||
/// Enum to describe whether track merging is attempted from the start or end of a track segment
|
||||
enum class MERGE_POINT
|
||||
{
|
||||
START,
|
||||
END
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the stackup distance between the two given layers.
|
||||
*
|
||||
* Note: Can return 0 if the board design settings disallow stackup height calculations
|
||||
*/
|
||||
int stackupHeight( PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer ) const;
|
||||
|
||||
/**
|
||||
* Optimises the given set of items to minimise the electrical path length. At the moment
|
||||
* only optimises lines attached to pads, future work could optimise paths through pads
|
||||
*
|
||||
* Assumes that any polylines are only connected at either end, and not at midpoints
|
||||
*/
|
||||
static void optimiseTracesInPads( const std::vector<LENGTH_CALCULATION_ITEM*>& aPads,
|
||||
const std::vector<LENGTH_CALCULATION_ITEM*>& aLines );
|
||||
|
||||
/// Clips the given line to the minimal direct electrical length within the pad
|
||||
static void clipLineToPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, PCB_LAYER_ID aLayer, bool aForward = true );
|
||||
|
||||
/**
|
||||
* Optimises the via layers. Ensures that vias that are routed through only on one layer do not count towards total
|
||||
* length calculations.
|
||||
*/
|
||||
static void
|
||||
optimiseViaLayers( const std::vector<LENGTH_CALCULATION_ITEM*>& aVias,
|
||||
std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap,
|
||||
const std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aPadsPositionMap );
|
||||
|
||||
/**
|
||||
* Merges any lines (traces) that are contiguous, on one layer, and with no junctions
|
||||
*/
|
||||
static void mergeLines( std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
|
||||
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap );
|
||||
|
||||
/**
|
||||
* Merges two SHAPE_LINE_CHAINs where there is a shared endpoing.
|
||||
*
|
||||
* aSecondary is merged in to aPrimary
|
||||
*/
|
||||
static void mergeShapeLineChains( SHAPE_LINE_CHAIN& aPrimary, const SHAPE_LINE_CHAIN& aSecondary,
|
||||
MERGE_POINT aMergePoint );
|
||||
|
||||
/**
|
||||
* Infers if there is a via in the given pad. Adds via details to the length details data structure if found.
|
||||
*/
|
||||
void inferViaInPad( const PAD* aPad, const LENGTH_CALCULATION_ITEM& aItem, LENGTH_DETAILS& aDetails ) const;
|
||||
};
|
||||
|
||||
#endif //PCBNEW_LENGTH_CALCULATION_H
|
@ -2322,3 +2322,56 @@ PNS_LAYER_RANGE PNS_KICAD_IFACE_BASE::SetLayersFromPCBNew( PCB_LAYER_ID aStartLa
|
||||
return PNS_LAYER_RANGE( GetPNSLayerFromBoardLayer( aStartLayer ),
|
||||
GetPNSLayerFromBoardLayer( aEndLayer ) );
|
||||
}
|
||||
|
||||
|
||||
long long int PNS_KICAD_IFACE_BASE::CalculateRoutedPathLength( const PNS::ITEM_SET& aLine, const PNS::SOLID* aStartPad,
|
||||
const PNS::SOLID* aEndPad )
|
||||
{
|
||||
std::vector<LENGTH_CALCULATION_ITEM> lengthItems;
|
||||
|
||||
for( int idx = 0; idx < aLine.Size(); idx++ )
|
||||
{
|
||||
const PNS::ITEM* lineItem = aLine[idx];
|
||||
|
||||
if( const PNS::LINE* l = dyn_cast<const PNS::LINE*>( lineItem ) )
|
||||
{
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetLine( l->CLine() );
|
||||
|
||||
const PCB_LAYER_ID layer = GetBoardLayerFromPNSLayer( lineItem->Layer() );
|
||||
item.SetLayers( layer );
|
||||
|
||||
lengthItems.emplace_back( std::move( item ) );
|
||||
}
|
||||
else if( lineItem->OfKind( PNS::ITEM::VIA_T ) && idx > 0 && idx < aLine.Size() - 1 )
|
||||
{
|
||||
const int layerPrev = aLine[idx - 1]->Layer();
|
||||
const int layerNext = aLine[idx + 1]->Layer();
|
||||
const PCB_LAYER_ID pcbLayerPrev = GetBoardLayerFromPNSLayer( layerPrev );
|
||||
const PCB_LAYER_ID pcbLayerNext = GetBoardLayerFromPNSLayer( layerNext );
|
||||
|
||||
if( layerPrev != layerNext )
|
||||
{
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetVia( static_cast<PCB_VIA*>( lineItem->GetSourceItem() ) );
|
||||
item.SetLayers( pcbLayerPrev, pcbLayerNext );
|
||||
lengthItems.emplace_back( std::move( item ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PAD* startPad = nullptr;
|
||||
const PAD* endPad = nullptr;
|
||||
|
||||
if( aStartPad )
|
||||
startPad = static_cast<PAD*>( aStartPad->Parent() );
|
||||
|
||||
if( aEndPad )
|
||||
endPad = static_cast<PAD*>( aEndPad->Parent() );
|
||||
|
||||
constexpr PATH_OPTIMISATIONS opts = {
|
||||
.OptimiseViaLayers = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
|
||||
};
|
||||
const BOARD* board = GetBoard();
|
||||
return board->GetLengthCalculation()->CalculateLength( lengthItems, opts, startPad, endPad );
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ public:
|
||||
|
||||
void SetDebugDecorator( PNS::DEBUG_DECORATOR* aDec );
|
||||
|
||||
long long int CalculateRoutedPathLength( const PNS::ITEM_SET& aLine, const PNS::SOLID* aStartPad,
|
||||
const PNS::SOLID* aEndPad ) override;
|
||||
PCB_LAYER_ID GetBoardLayerFromPNSLayer( int aLayer ) const override;
|
||||
int GetPNSLayerFromBoardLayer( PCB_LAYER_ID aLayer ) const override;
|
||||
|
||||
|
@ -321,60 +321,10 @@ VECTOR2I MEANDER_PLACER_BASE::getSnappedStartPoint( LINKED_ITEM* aStartItem, VEC
|
||||
|
||||
long long int MEANDER_PLACER_BASE::lineLength( const ITEM_SET& aLine, const SOLID* aStartPad, const SOLID* aEndPad ) const
|
||||
{
|
||||
long long int total = 0;
|
||||
|
||||
if( aLine.Empty() )
|
||||
return 0;
|
||||
|
||||
const ITEM* start_item = aLine[0];
|
||||
const ITEM* end_item = aLine[aLine.Size() - 1];
|
||||
bool start_via = false;
|
||||
bool end_via = false;
|
||||
|
||||
|
||||
/**
|
||||
* If there is a start pad but the pad's layers do not overlap the first track layer, then there must be a
|
||||
* fanout via on the line. If there isn't, we still need to have the via back to the pad, so count the distance
|
||||
* in the line tuning
|
||||
*/
|
||||
start_via = aStartPad && ( !aStartPad->LayersOverlap( start_item ) );
|
||||
end_via = aEndPad && ( !aEndPad->LayersOverlap( end_item ) );
|
||||
|
||||
for( int idx = 0; idx < aLine.Size(); idx++ )
|
||||
{
|
||||
const ITEM* item = aLine[idx];
|
||||
|
||||
if( const LINE* l = dyn_cast<const LINE*>( item ) )
|
||||
{
|
||||
total += l->CLine().Length();
|
||||
}
|
||||
else if( item->OfKind( ITEM::VIA_T ) && idx > 0 && idx < aLine.Size() - 1 )
|
||||
{
|
||||
int layerPrev = aLine[idx - 1]->Layer();
|
||||
int layerNext = aLine[idx + 1]->Layer();
|
||||
|
||||
if( layerPrev != layerNext )
|
||||
total += m_router->GetInterface()->StackupHeight( layerPrev, layerNext );
|
||||
}
|
||||
}
|
||||
|
||||
if( start_via )
|
||||
{
|
||||
int layerPrev = aStartPad->Layer();
|
||||
int layerNext = start_item->Layer();
|
||||
|
||||
total += m_router->GetInterface()->StackupHeight( layerPrev, layerNext );
|
||||
}
|
||||
|
||||
if( end_via )
|
||||
{
|
||||
int layerPrev = end_item->Layer();
|
||||
int layerNext = aEndPad->Layer();
|
||||
|
||||
total += m_router->GetInterface()->StackupHeight( layerPrev, layerNext );
|
||||
}
|
||||
|
||||
return total;
|
||||
ROUTER_IFACE* iface = Router()->GetInterface();
|
||||
return iface->CalculateRoutedPathLength( aLine, aStartPad, aEndPad );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -117,6 +117,8 @@ enum DRAG_MODE
|
||||
virtual RULE_RESOLVER* GetRuleResolver() = 0;
|
||||
virtual DEBUG_DECORATOR* GetDebugDecorator() = 0;
|
||||
|
||||
virtual long long int CalculateRoutedPathLength( const ITEM_SET& aLine, const SOLID* aStartPad,
|
||||
const SOLID* aEndPad ) = 0;
|
||||
virtual PCB_LAYER_ID GetBoardLayerFromPNSLayer( int aLayer ) const = 0;
|
||||
virtual int GetPNSLayerFromBoardLayer( PCB_LAYER_ID aLayer ) const = 0;
|
||||
};
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#include "pns_item.h"
|
||||
#include "pns_hole.h"
|
||||
|
||||
namespace PNS {
|
||||
|
||||
|
@ -371,53 +371,7 @@ const ITEM_SET TOPOLOGY::AssembleTuningPath( ROUTER_IFACE* aRouterIface, ITEM* a
|
||||
if( !padA && !padB )
|
||||
return initialPath;
|
||||
|
||||
auto clipLineToPad =
|
||||
[]( SHAPE_LINE_CHAIN& aLine, PAD* aPad, PCB_LAYER_ID aLayer, bool aForward = true )
|
||||
{
|
||||
const auto& shape = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
|
||||
|
||||
int start = aForward ? 0 : aLine.PointCount() - 1;
|
||||
int delta = aForward ? 1 : -1;
|
||||
|
||||
// Skip the "first" (or last) vertex, we already know it's contained in the pad
|
||||
int clip = start;
|
||||
|
||||
for( int vertex = start + delta;
|
||||
aForward ? vertex < aLine.PointCount() : vertex >= 0;
|
||||
vertex += delta )
|
||||
{
|
||||
SEG seg( aLine.GetPoint( vertex ), aLine.GetPoint( vertex - delta ) );
|
||||
|
||||
bool containsA = shape->Contains( seg.A );
|
||||
bool containsB = shape->Contains( seg.B );
|
||||
|
||||
if( containsA && containsB )
|
||||
{
|
||||
// Whole segment is inside: clip out this segment
|
||||
clip = vertex;
|
||||
}
|
||||
else if( containsB )
|
||||
{
|
||||
// Only one point inside: Find the intersection
|
||||
VECTOR2I loc;
|
||||
|
||||
if( shape->Collide( seg, 0, nullptr, &loc ) )
|
||||
{
|
||||
aLine.Replace( vertex - delta, vertex - delta, loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !aForward && clip < start )
|
||||
aLine.Remove( clip + 1, start );
|
||||
else if( clip > start )
|
||||
aLine.Remove( start, clip - 1 );
|
||||
|
||||
// Now connect the dots
|
||||
aLine.Insert( aForward ? 0 : aLine.PointCount(), aPad->GetPosition() );
|
||||
};
|
||||
|
||||
auto processPad = [&]( const JOINT* aJoint, PAD* aPad, int aLayer )
|
||||
auto processPad = [&]( PAD* aPad, int aLayer )
|
||||
{
|
||||
for( int idx = 0; idx < initialPath.Size(); idx++ )
|
||||
{
|
||||
@ -425,32 +379,18 @@ const ITEM_SET TOPOLOGY::AssembleTuningPath( ROUTER_IFACE* aRouterIface, ITEM* a
|
||||
continue;
|
||||
|
||||
LINE* line = static_cast<LINE*>( initialPath[idx] );
|
||||
PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
|
||||
SHAPE_LINE_CHAIN& slc = line->Line();
|
||||
const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
|
||||
|
||||
if( !aPad->FlashLayer( pcbLayer ) )
|
||||
continue;
|
||||
|
||||
const std::vector<VECTOR2I>& points = line->CLine().CPoints();
|
||||
|
||||
if( points.front() != aJoint->Pos() && points.back() != aJoint->Pos() )
|
||||
continue;
|
||||
|
||||
const auto& shape = aPad->GetEffectivePolygon( pcbLayer, ERROR_INSIDE );
|
||||
|
||||
SHAPE_LINE_CHAIN& slc = line->Line();
|
||||
|
||||
if( shape->Contains( slc.CPoint( 0 ) ) )
|
||||
clipLineToPad( slc, aPad, pcbLayer, true );
|
||||
else if( shape->Contains( slc.CPoint( -1 ) ) )
|
||||
clipLineToPad( slc, aPad, pcbLayer, false );
|
||||
LENGTH_CALCULATION::OptimiseTraceInPad( slc, aPad, pcbLayer );
|
||||
}
|
||||
};
|
||||
|
||||
if( padA )
|
||||
processPad( joints.first, padA, joints.first->Layer() );
|
||||
processPad( padA, joints.first->Layer() );
|
||||
|
||||
if( padB )
|
||||
processPad( joints.second, padB, joints.second->Layer() );
|
||||
processPad( padB, joints.second->Layer() );
|
||||
|
||||
return initialPath;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,25 +37,10 @@ class PCB_TRACK;
|
||||
class EDA_COMBINED_MATCHER;
|
||||
|
||||
/**
|
||||
* Net inspection panel for pcbnew
|
||||
* PCB net inspection panel
|
||||
*
|
||||
* Provides a read-only view of net information, such as routed lengths. Data is updated after
|
||||
* every change of board items. Note that there is not always a 1:1 relationship between Nets and
|
||||
* displayed items in the inspector.. This can be the case where there is a constraint which
|
||||
* selects sub-sections of nets, for example consider a netclass used for a fly-by-routing
|
||||
* adddress bus. There could be two constraints, e.g.:
|
||||
* <p>
|
||||
* FROM/TO=IC1-IC2, Netclass=DDR_ADDR, Net=ADDR_0
|
||||
* FROM/TO=IC2-IC3, Netclass=DDR_ADDR, Net=ADDR_0
|
||||
* <p>
|
||||
* In this instance, a single address net within the DDR_ADDR netclass could have three entries in
|
||||
* the inspector, each tracking a different set of net statistics:
|
||||
* <p>
|
||||
* 1. The whole net
|
||||
* 2. IC1-IC2
|
||||
* 3. IC2-IC3
|
||||
* <p>
|
||||
* In this instance, all sub-nets as a result of a constraint will be grouped by the constraint.
|
||||
* Provides a read-only view of net information, such as routed lengths. Data is updated after every change of board
|
||||
* items.
|
||||
*/
|
||||
class PCB_NET_INSPECTOR_PANEL : public NET_INSPECTOR_PANEL, public BOARD_LISTENER
|
||||
{
|
||||
@ -68,50 +53,41 @@ public:
|
||||
*
|
||||
* Called by PCB_EDIT_FRAME after displaying the Board Setup dialog
|
||||
*/
|
||||
virtual void OnParentSetupChanged() override;
|
||||
void OnParentSetupChanged() override;
|
||||
|
||||
/*
|
||||
* BOARD_LISTENER implementation
|
||||
*/
|
||||
virtual void OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
virtual void OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
virtual void OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
virtual void OnBoardItemsRemoved( BOARD& aBoard,
|
||||
std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
virtual void OnBoardNetSettingsChanged( BOARD& aBoard ) override;
|
||||
virtual void OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
virtual void OnBoardItemsChanged( BOARD& aBoard,
|
||||
std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
virtual void OnBoardHighlightNetChanged( BOARD& aBoard ) override;
|
||||
virtual void OnBoardCompositeUpdate( BOARD& aBoard, std::vector<BOARD_ITEM*>& aAddedItems,
|
||||
std::vector<BOARD_ITEM*>& aRemovedItems,
|
||||
std::vector<BOARD_ITEM*>& aChangedItems ) override;
|
||||
/**
|
||||
* Update panel when board is changed
|
||||
*/
|
||||
virtual void OnBoardChanged() override;
|
||||
void OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
void OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
void OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
void OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
void OnBoardNetSettingsChanged( BOARD& aBoard ) override;
|
||||
void OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
|
||||
void OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
|
||||
void OnBoardHighlightNetChanged( BOARD& aBoard ) override;
|
||||
void OnBoardCompositeUpdate( BOARD& aBoard, std::vector<BOARD_ITEM*>& aAddedItems,
|
||||
std::vector<BOARD_ITEM*>& aRemovedItems,
|
||||
std::vector<BOARD_ITEM*>& aChangedItems ) override;
|
||||
|
||||
/**
|
||||
* Prepare the panel when shown in the editor
|
||||
*/
|
||||
virtual void OnShowPanel() override;
|
||||
/// Update panel when board is changed
|
||||
void OnBoardChanged() override;
|
||||
|
||||
/**
|
||||
* Persist the net inspector configuration to project / global settings
|
||||
*/
|
||||
virtual void SaveSettings() override;
|
||||
/// Prepare the panel when shown in the editor
|
||||
void OnShowPanel() override;
|
||||
|
||||
/// Persist the net inspector configuration to project / global settings
|
||||
void SaveSettings() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reloads strings on an application language change
|
||||
*/
|
||||
virtual void OnLanguageChangedImpl() override;
|
||||
/// Reloads strings on an application language change
|
||||
void OnLanguageChangedImpl() override;
|
||||
|
||||
/*
|
||||
* UI events
|
||||
*/
|
||||
virtual void OnSearchTextChanged( wxCommandEvent& event ) override;
|
||||
virtual void OnConfigButton( wxCommandEvent& event ) override;
|
||||
void OnSearchTextChanged( wxCommandEvent& event ) override;
|
||||
void OnConfigButton( wxCommandEvent& event ) override;
|
||||
void OnExpandCollapseRow( wxCommandEvent& event );
|
||||
void OnHeaderContextMenu( wxCommandEvent& event );
|
||||
void OnNetsListContextMenu( wxDataViewEvent& event );
|
||||
@ -119,42 +95,36 @@ protected:
|
||||
void OnColumnSorted( wxDataViewEvent& event );
|
||||
|
||||
private:
|
||||
/// Updates displayed statistics for the given nets
|
||||
void updateNets( const std::vector<NETINFO_ITEM*>& aNets ) const;
|
||||
|
||||
/// Unified handling of added / deleted / modified board items
|
||||
void updateBoardItems( const std::vector<BOARD_ITEM*>& aBoardItems );
|
||||
|
||||
/*
|
||||
* Helper methods for returning fornatted data
|
||||
* Helper methods for returning formatted data
|
||||
*/
|
||||
wxString formatNetCode( const NETINFO_ITEM* aNet ) const;
|
||||
wxString formatNetName( const NETINFO_ITEM* aNet ) const;
|
||||
wxString formatCount( unsigned int aValue ) const;
|
||||
static wxString formatNetCode( const NETINFO_ITEM* aNet );
|
||||
static wxString formatNetName( const NETINFO_ITEM* aNet );
|
||||
static wxString formatCount( unsigned int aValue );
|
||||
wxString formatLength( int64_t aValue ) const;
|
||||
|
||||
/**
|
||||
* Generates a sub-menu for the show / hide columns submenu
|
||||
*/
|
||||
/// Generates a sub-menu for the show / hide columns submenu
|
||||
void generateShowHideColumnMenu( wxMenu* target );
|
||||
|
||||
/**
|
||||
* Filters connectivity items from a board update to remove those not related to
|
||||
* net / track metrics
|
||||
*/
|
||||
/// Fetches an ordered (by NetCode) list of all board connectivity items
|
||||
std::vector<CN_ITEM*> relevantConnectivityItems() const;
|
||||
|
||||
/**
|
||||
* Filter to determine whether a board net should be included in the net inspector
|
||||
*/
|
||||
/// Filter to determine whether a board net should be included in the net inspector
|
||||
bool netFilterMatches( NETINFO_ITEM* aNet, PANEL_NET_INSPECTOR_SETTINGS* cfg = nullptr ) const;
|
||||
|
||||
/**
|
||||
* Updates the stored LIST_ITEMs for a given updated board net item
|
||||
*/
|
||||
void updateNet( NETINFO_ITEM* aNet );
|
||||
|
||||
/**
|
||||
* Calculates the length of a via from the board stackup
|
||||
*/
|
||||
unsigned int calculateViaLength( const PCB_TRACK* ) const;
|
||||
|
||||
/// Rebuilds the net inspector list, removing all previous entries
|
||||
void buildNetsList( bool rebuildColumns = false );
|
||||
|
||||
/// Build the required columns in the net inspector grid
|
||||
void buildColumns();
|
||||
|
||||
/// Set sensible default column widths
|
||||
void setColumnWidths();
|
||||
|
||||
/**
|
||||
@ -162,7 +132,7 @@ private:
|
||||
*
|
||||
* @param cfg the PANEL_NET_INSPECTOR_SETTINGS from which to read column widths
|
||||
*/
|
||||
void adjustListColumnSizes( PANEL_NET_INSPECTOR_SETTINGS* cfg );
|
||||
void adjustListColumnSizes( PANEL_NET_INSPECTOR_SETTINGS* cfg ) const;
|
||||
|
||||
/**
|
||||
* Sets the sort column in the grid to that showing the given model ID column
|
||||
@ -171,7 +141,7 @@ private:
|
||||
* @param sortOrderAsc True for ascending sort, False for descending sort
|
||||
* @returns true if the column was found
|
||||
*/
|
||||
bool restoreSortColumn( int sortingColumnId, bool sortOrderAsc );
|
||||
bool restoreSortColumn( int sortingColumnId, bool sortOrderAsc ) const;
|
||||
|
||||
/**
|
||||
* Fetches the displayed grid view column for the given model column ID
|
||||
@ -179,81 +149,84 @@ private:
|
||||
* @param columnId The ID (from column static IDs enum) to find
|
||||
* @returns Pointer to the wxDataViewColumn, or nullptr if not found
|
||||
*/
|
||||
wxDataViewColumn* getDisplayedColumnForModelField( int columnId );
|
||||
wxDataViewColumn* getDisplayedColumnForModelField( int columnId ) const;
|
||||
|
||||
/**
|
||||
* Generates a CSV report from currently disaplyed data
|
||||
*/
|
||||
/// Generates a CSV report from currently disaplyed data
|
||||
void generateReport();
|
||||
|
||||
/**
|
||||
* Highlight the currently selected net
|
||||
*/
|
||||
/// Highlight the currently selected net
|
||||
void highlightSelectedNets();
|
||||
|
||||
/// Handle an application-level change of units
|
||||
void onUnitsChanged( wxCommandEvent& event );
|
||||
void onSettingsMenu( wxCommandEvent& event );
|
||||
|
||||
/// Handle a net row(s) context menu selection
|
||||
void onContextMenuSelection( wxCommandEvent& event );
|
||||
|
||||
/// Display a new row(s) context menu
|
||||
void onItemContextMenu( wxCommandEvent& event );
|
||||
|
||||
/// Adds a new user-specified net to the board
|
||||
void onAddNet();
|
||||
|
||||
/// Renames a selected net
|
||||
void onRenameSelectedNet();
|
||||
|
||||
/// Deletes a selected net
|
||||
void onDeleteSelectedNet();
|
||||
void onRemoveSelectedGroup();
|
||||
|
||||
/// Adds a custom display grouping of nets
|
||||
void onAddGroup();
|
||||
|
||||
/// Removes a custom display grouping
|
||||
void onRemoveSelectedGroup();
|
||||
|
||||
/// Clears highlighting from nets
|
||||
void onClearHighlighting();
|
||||
|
||||
/**
|
||||
* Container class for a set of net data
|
||||
*/
|
||||
/// Forward declaration: container class for a set of net data
|
||||
class LIST_ITEM;
|
||||
|
||||
/**
|
||||
* Ordered comparison of LIST_ITEMs by net code
|
||||
*/
|
||||
* Calculates the length statistics for each given netcode
|
||||
*
|
||||
* @param aNetCodes is the list of netcodes to calculate statistics for. This must be sorted in ascending netcode order
|
||||
* @param aIncludeZeroPadNets determines whether the results should include nets with no pads
|
||||
* @returns a map of net code to net length detail objects
|
||||
*/
|
||||
std::vector<std::unique_ptr<LIST_ITEM>> calculateNets( const std::vector<NETINFO_ITEM*>& aNetCodes,
|
||||
bool aIncludeZeroPadNets ) const;
|
||||
|
||||
/// Ordered comparison of LIST_ITEMs by net code
|
||||
struct LIST_ITEM_NETCODE_CMP_LESS;
|
||||
|
||||
/**
|
||||
* Ordered comparison of LIST_ITEMs by group number
|
||||
*/
|
||||
/// Ordered comparison of LIST_ITEMs by group number
|
||||
struct LIST_ITEM_GROUP_NUMBER_CMP_LESS;
|
||||
|
||||
using LIST_ITEM_ITER = std::vector<std::unique_ptr<LIST_ITEM>>::iterator;
|
||||
using LIST_ITEM_CONST_ITER = std::vector<std::unique_ptr<LIST_ITEM>>::const_iterator;
|
||||
|
||||
/**
|
||||
* Constructs a LIST_ITEM for storage in the data model from a board net item
|
||||
*/
|
||||
std::unique_ptr<LIST_ITEM> buildNewItem( NETINFO_ITEM* aNet, unsigned int aPadCount,
|
||||
const std::vector<CN_ITEM*>& aCNItems );
|
||||
/// Refreshes displayed data for the given rows
|
||||
void updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow ) const;
|
||||
|
||||
void updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow );
|
||||
/// Parent BOARD
|
||||
BOARD* m_board = nullptr;
|
||||
|
||||
// special zero-netcode item. Unconnected pads etc might use different
|
||||
// (dummy) NETINFO_ITEM. Redirect all of them to this item, which we get
|
||||
// from the board object in buildNetsList.
|
||||
NETINFO_ITEM* m_zero_netitem;
|
||||
|
||||
/*
|
||||
* Current board and parent edit frame
|
||||
*/
|
||||
BOARD* m_brd = nullptr;
|
||||
/// Owning edit frame
|
||||
PCB_EDIT_FRAME* m_frame = nullptr;
|
||||
|
||||
/**
|
||||
* Data model which holds LIST_ITEMs
|
||||
*/
|
||||
/// Data model which holds LIST_ITEMs
|
||||
class DATA_MODEL;
|
||||
|
||||
/*
|
||||
* The bound data model to display
|
||||
*/
|
||||
wxObjectDataPtr<DATA_MODEL> m_data_model;
|
||||
/// The bound data model to display
|
||||
wxObjectDataPtr<DATA_MODEL> m_dataModel;
|
||||
friend DATA_MODEL;
|
||||
|
||||
/*
|
||||
* Status flags set during reporting and net rebuild operations
|
||||
*/
|
||||
bool m_in_reporting = false;
|
||||
bool m_in_build_nets_list = false;
|
||||
bool m_inReporting = false;
|
||||
bool m_inBuildNetsList = false;
|
||||
|
||||
/*
|
||||
* Status flags to indicate whether a board has been loaded in this control's
|
||||
@ -264,41 +237,36 @@ private:
|
||||
* settings calculated from an empty board. We do not save settings until the first
|
||||
* board load operation has occured.
|
||||
*/
|
||||
bool m_board_loaded = false;
|
||||
bool m_board_loading = false;
|
||||
bool m_boardLoaded = false;
|
||||
bool m_boardLoading = false;
|
||||
|
||||
/*
|
||||
* Flags to indicate whether certain events should be disabled during programmatic updates
|
||||
*/
|
||||
bool m_row_expanding = false;
|
||||
bool m_highlighting_nets = false;
|
||||
bool m_rowExpanding = false;
|
||||
bool m_highlightingNets = false;
|
||||
|
||||
/*
|
||||
* Configuration flags - these are all persisted to the project storage
|
||||
*/
|
||||
bool m_filter_by_net_name = true;
|
||||
bool m_filter_by_netclass = true;
|
||||
bool m_show_zero_pad_nets = false;
|
||||
bool m_show_unconnected_nets = false;
|
||||
bool m_group_by_netclass = false;
|
||||
bool m_group_by_constraint = false;
|
||||
|
||||
int m_num_copper_layers = 0;
|
||||
bool m_filterByNetName = true;
|
||||
bool m_filterByNetclass = true;
|
||||
bool m_showZeroPadNets = false;
|
||||
bool m_showUnconnectedNets = false;
|
||||
bool m_groupByNetclass = false;
|
||||
bool m_groupByConstraint = false;
|
||||
|
||||
/// Custom net grouping rules
|
||||
std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>> m_custom_group_rules;
|
||||
|
||||
/**
|
||||
* CSV output control
|
||||
*/
|
||||
/// CSV output control
|
||||
enum class CSV_COLUMN_DESC : int
|
||||
{
|
||||
CSV_NONE = 0,
|
||||
CSV_QUOTE = 1 << 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Column metadata
|
||||
*/
|
||||
/// Column metadata
|
||||
struct COLUMN_DESC
|
||||
{
|
||||
COLUMN_DESC( unsigned aNum, PCB_LAYER_ID aLayer, const wxString& aDisp,
|
||||
@ -324,9 +292,7 @@ private:
|
||||
*/
|
||||
std::vector<COLUMN_DESC> m_columns;
|
||||
|
||||
/*
|
||||
* Column static IDs. Used to refer to columns as use re-ordering can occur.
|
||||
*/
|
||||
/// Column static IDs. Used to refer to columns as user re-ordering can occur
|
||||
enum
|
||||
{
|
||||
COLUMN_NAME = 0,
|
||||
@ -340,9 +306,7 @@ private:
|
||||
COLUMN_LAST_STATIC_COL = COLUMN_PAD_COUNT
|
||||
};
|
||||
|
||||
/*
|
||||
* Popup menu item IDs
|
||||
*/
|
||||
/// Popup menu item IDs
|
||||
enum POPUP_MENU_OPTIONS
|
||||
{
|
||||
ID_ADD_NET = ID_POPUP_MENU_START,
|
||||
|
@ -152,7 +152,7 @@ public:
|
||||
m_via_count -= aValue;
|
||||
}
|
||||
|
||||
uint64_t GetViaLength() const { return m_via_length; }
|
||||
int64_t GetViaLength() const { return m_via_length; }
|
||||
|
||||
bool ViaLengthChanged() const { return m_column_changed[COLUMN_VIA_LENGTH]; }
|
||||
|
||||
@ -174,7 +174,7 @@ public:
|
||||
m_via_length += aValue;
|
||||
}
|
||||
|
||||
void SubViaLength( uint64_t aValue )
|
||||
void SubViaLength( int64_t aValue )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->SubViaLength( aValue );
|
||||
@ -183,9 +183,9 @@ public:
|
||||
m_via_length -= aValue;
|
||||
}
|
||||
|
||||
uint64_t GetBoardWireLength() const
|
||||
int64_t GetBoardWireLength() const
|
||||
{
|
||||
uint64_t retval = 0;
|
||||
int64_t retval = 0;
|
||||
|
||||
for( auto& [layer, length] : m_layer_wire_length )
|
||||
retval += length;
|
||||
@ -193,7 +193,7 @@ public:
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint64_t GetLayerWireLength( PCB_LAYER_ID aLayer ) const
|
||||
int64_t GetLayerWireLength( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
auto it = m_layer_wire_length.find( aLayer );
|
||||
return it != m_layer_wire_length.end() ? it->second : 0;
|
||||
@ -201,7 +201,7 @@ public:
|
||||
|
||||
bool BoardWireLengthChanged() const { return m_column_changed[COLUMN_BOARD_LENGTH]; }
|
||||
|
||||
void SetLayerWireLength( const uint64_t aValue, PCB_LAYER_ID aLayer )
|
||||
void SetLayerWireLength( const int64_t aValue, PCB_LAYER_ID aLayer )
|
||||
{
|
||||
auto it = m_layer_wire_length.find( aLayer );
|
||||
|
||||
@ -219,14 +219,23 @@ public:
|
||||
length = aValue;
|
||||
}
|
||||
|
||||
std::map<PCB_LAYER_ID, uint64_t> GetLayerWireLength() const { return m_layer_wire_length; }
|
||||
std::map<PCB_LAYER_ID, int64_t> GetLayerWireLengths() const { return m_layer_wire_length; }
|
||||
|
||||
void SetLayerWireLength( const std::map<PCB_LAYER_ID, uint64_t>& aValue )
|
||||
void SetLayerWireLengths( const std::map<PCB_LAYER_ID, int64_t>& aValue )
|
||||
{
|
||||
if( m_parent )
|
||||
{
|
||||
for( auto& [oldLayer, oldLength] : m_layer_wire_length )
|
||||
m_parent->SubLayerWireLength( oldLength, oldLayer );
|
||||
|
||||
for( auto& [newLayer, newLength] : aValue )
|
||||
m_parent->AddLayerWireLength( newLength, newLayer );
|
||||
}
|
||||
|
||||
m_layer_wire_length = aValue;
|
||||
}
|
||||
|
||||
void AddLayerWireLength( const uint64_t aValue, PCB_LAYER_ID aLayer )
|
||||
void AddLayerWireLength( const int64_t aValue, PCB_LAYER_ID aLayer )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->AddLayerWireLength( aValue, aLayer );
|
||||
@ -235,7 +244,7 @@ public:
|
||||
m_layer_wire_length[aLayer] += aValue;
|
||||
}
|
||||
|
||||
void SubLayerWireLength( const uint64_t aValue, PCB_LAYER_ID aLayer )
|
||||
void SubLayerWireLength( const int64_t aValue, PCB_LAYER_ID aLayer )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->SubLayerWireLength( aValue, aLayer );
|
||||
@ -244,11 +253,11 @@ public:
|
||||
m_layer_wire_length[aLayer] -= aValue;
|
||||
}
|
||||
|
||||
uint64_t GetPadDieLength() const { return m_pad_die_length; }
|
||||
int64_t GetPadDieLength() const { return m_pad_die_length; }
|
||||
|
||||
bool PadDieLengthChanged() const { return m_column_changed[COLUMN_PAD_DIE_LENGTH]; }
|
||||
|
||||
void SetPadDieLength( uint64_t aValue )
|
||||
void SetPadDieLength( int64_t aValue )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->SetPadDieLength( m_parent->GetPadDieLength() - m_pad_die_length + aValue );
|
||||
@ -257,7 +266,7 @@ public:
|
||||
m_pad_die_length = aValue;
|
||||
}
|
||||
|
||||
void AddPadDieLength( uint64_t aValue )
|
||||
void AddPadDieLength( int64_t aValue )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->AddPadDieLength( aValue );
|
||||
@ -266,7 +275,7 @@ public:
|
||||
m_pad_die_length += aValue;
|
||||
}
|
||||
|
||||
void SubPadDieLength( uint64_t aValue )
|
||||
void SubPadDieLength( int64_t aValue )
|
||||
{
|
||||
if( m_parent )
|
||||
m_parent->SubPadDieLength( aValue );
|
||||
@ -334,10 +343,10 @@ private:
|
||||
NETINFO_ITEM* m_net = nullptr;
|
||||
unsigned int m_pad_count = 0;
|
||||
unsigned int m_via_count = 0;
|
||||
uint64_t m_via_length = 0;
|
||||
uint64_t m_pad_die_length = 0;
|
||||
int64_t m_via_length = 0;
|
||||
int64_t m_pad_die_length = 0;
|
||||
|
||||
std::map<PCB_LAYER_ID, uint64_t> m_layer_wire_length{};
|
||||
std::map<PCB_LAYER_ID, int64_t> m_layer_wire_length{};
|
||||
|
||||
// Dirty bits to record when some attribute has changed, in order to avoid unnecessary sort
|
||||
// operations.
|
||||
@ -514,7 +523,7 @@ public:
|
||||
}
|
||||
|
||||
// Then add any netclass groups required by this item
|
||||
if( m_parent.m_group_by_netclass && !groupMatched )
|
||||
if( m_parent.m_groupByNetclass && !groupMatched )
|
||||
{
|
||||
LIST_ITEM_ITER groups_begin = m_items.begin();
|
||||
LIST_ITEM_ITER groups_end = std::find_if_not( m_items.begin(), m_items.end(),
|
||||
@ -544,7 +553,7 @@ public:
|
||||
return { new_iter };
|
||||
}
|
||||
|
||||
void addItems( std::vector<std::unique_ptr<LIST_ITEM>> aItems )
|
||||
void addItems( std::vector<std::unique_ptr<LIST_ITEM>>& aItems )
|
||||
{
|
||||
m_items.reserve( m_items.size() + aItems.size() );
|
||||
|
||||
@ -569,7 +578,7 @@ public:
|
||||
{
|
||||
ItemChanged( wxDataViewItem( parent ) );
|
||||
|
||||
if( m_parent.m_group_by_netclass && parent != nullptr && parent->ChildrenCount() == 0 )
|
||||
if( m_parent.m_groupByNetclass && parent != nullptr && parent->ChildrenCount() == 0 )
|
||||
{
|
||||
auto p = std::find_if( m_items.begin(), m_items.end(),
|
||||
[&]( std::unique_ptr<LIST_ITEM>& x )
|
||||
@ -604,7 +613,7 @@ public:
|
||||
std::unique_ptr<LIST_ITEM>& group = m_items.emplace_back( std::make_unique<LIST_ITEM>(
|
||||
groupId, rule->GetPattern(), LIST_ITEM::GROUP_TYPE::USER_DEFINED ) );
|
||||
m_custom_group_map[ rule->GetPattern() ] = group.get();
|
||||
group->SetLayerCount( m_parent.m_brd->GetCopperLayerCount() );
|
||||
group->SetLayerCount( m_parent.m_board->GetCopperLayerCount() );
|
||||
ItemAdded( wxDataViewItem( group->Parent() ), wxDataViewItem( group.get() ) );
|
||||
++groupId;
|
||||
}
|
||||
@ -750,7 +759,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
static int compareUInt( uint64_t aValue1, uint64_t aValue2, bool aAsc )
|
||||
static int compareUInt( int64_t aValue1, int64_t aValue2, bool aAsc )
|
||||
{
|
||||
if( aAsc )
|
||||
return aValue1 < aValue2 ? -1 : 1;
|
||||
|
31
qa/data/pcbnew/length_calculations.kicad_dru
Normal file
31
qa/data/pcbnew/length_calculations.kicad_dru
Normal file
@ -0,0 +1,31 @@
|
||||
(version 1)
|
||||
|
||||
(rule "CASE_1"
|
||||
(condition "A.hasNetclass('CASE_1')")
|
||||
(constraint length (min 13.3345mm) (max 13.3555mm))
|
||||
)
|
||||
|
||||
(rule "CASE_2"
|
||||
(condition "A.hasNetclass('CASE_2')")
|
||||
(constraint length (min 14.8795mm) (max 14.8805mm))
|
||||
)
|
||||
|
||||
(rule "CASE_3"
|
||||
(condition "A.hasNetclass('CASE_3')")
|
||||
(constraint length (min 14.8795mm) (max 14.8805mm))
|
||||
)
|
||||
|
||||
(rule "CASE_4"
|
||||
(condition "A.hasNetclass('CASE_4')")
|
||||
(constraint length (min 16.4245mm) (max 16.4250mm))
|
||||
)
|
||||
|
||||
(rule "CASE_5"
|
||||
(condition "A.hasNetclass('CASE_5')")
|
||||
(constraint length (min 13.5470mm) (max 13.5480mm))
|
||||
)
|
||||
|
||||
(rule "CASE_6"
|
||||
(condition "A.hasNetclass('CASE_6')")
|
||||
(constraint length (min 13.3345mm) (max 13.3555mm))
|
||||
)
|
qa/data/pcbnew/length_calculations.kicad_pcb
Normal file
LOADING design file
658
qa/data/pcbnew/length_calculations.kicad_pro
Normal file
658
qa/data/pcbnew/length_calculations.kicad_pro
Normal file
@ -0,0 +1,658 @@
|
||||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.05,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": true,
|
||||
"text_position": 0,
|
||||
"units_format": 0
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.1,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.8,
|
||||
"height": 1.27,
|
||||
"width": 2.54
|
||||
},
|
||||
"silk_line_width": 0.1,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.1,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"min_clearance": 0.5
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
"hole_to_hole": "warning",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.5,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.0,
|
||||
"min_via_annular_width": 0.1,
|
||||
"min_via_diameter": 0.5,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [],
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "length_calculations.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": {
|
||||
"Net-(R1-Pad2)": [
|
||||
"CASE_1"
|
||||
],
|
||||
"Net-(R10-Pad1)": [
|
||||
"CASE_5"
|
||||
],
|
||||
"Net-(R11-Pad2)": [
|
||||
"CASE_6"
|
||||
],
|
||||
"Net-(R3-Pad2)": [
|
||||
"CASE_2"
|
||||
],
|
||||
"Net-(R5-Pad2)": [
|
||||
"CASE_3"
|
||||
],
|
||||
"Net-(R7-Pad2)": [
|
||||
"CASE_4"
|
||||
]
|
||||
},
|
||||
"netclass_patterns": []
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"pos_files": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"svg": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Description",
|
||||
"name": "Description",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "#",
|
||||
"name": "${ITEM_NUMBER}",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from BOM",
|
||||
"name": "${EXCLUDE_FROM_BOM}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from Board",
|
||||
"name": "${EXCLUDE_FROM_BOARD}",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": true,
|
||||
"name": "",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"space_save_all_events": true,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"spice_model_current_sheet_as_root": true,
|
||||
"spice_save_all_currents": false,
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"a4e4d485-017a-4720-8992-147a6425c6a8",
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
}
|
qa/data/pcbnew/length_calculations.kicad_sch
Normal file
LOADING design file
@ -68,6 +68,7 @@ set( QA_PCBNEW_SRCS
|
||||
drc/test_drc_incorrect_text_mirror.cpp
|
||||
drc/test_drc_starved_thermal.cpp
|
||||
drc/test_drc_orientation.cpp
|
||||
drc/test_drc_lengths.cpp
|
||||
|
||||
pcb_io/altium/test_altium_rule_transformer.cpp
|
||||
pcb_io/altium/test_altium_pcblib_import.cpp
|
||||
|
108
qa/tests/pcbnew/drc/test_drc_lengths.cpp
Normal file
108
qa/tests/pcbnew/drc/test_drc_lengths.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <pcbnew_utils/board_test_utils.h>
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcb_marker.h>
|
||||
#include <footprint.h>
|
||||
#include <drc/drc_item.h>
|
||||
#include <settings/settings_manager.h>
|
||||
|
||||
|
||||
struct DRC_REGRESSION_TEST_FIXTURE
|
||||
{
|
||||
DRC_REGRESSION_TEST_FIXTURE() : m_settingsManager( true /* headless */ ) {}
|
||||
|
||||
SETTINGS_MANAGER m_settingsManager;
|
||||
std::unique_ptr<BOARD> m_board;
|
||||
};
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( DRCLengths, DRC_REGRESSION_TEST_FIXTURE )
|
||||
{
|
||||
// Check for minimum copper connection errors
|
||||
|
||||
std::vector<std::pair<wxString, int>> tests = {
|
||||
{ "length_calculations", 0 } // Exclude warnings on unconnected pads
|
||||
};
|
||||
|
||||
for( const std::pair<wxString, int>& test : tests )
|
||||
{
|
||||
KI_TEST::LoadBoard( m_settingsManager, test.first, m_board );
|
||||
KI_TEST::FillZones( m_board.get() );
|
||||
|
||||
std::vector<DRC_ITEM> violations;
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
|
||||
// Disable DRC tests not useful or not handled in this testcase
|
||||
bds.m_DRCSeverities[DRCE_INVALID_OUTLINE] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_UNCONNECTED_ITEMS] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_COPPER_SLIVER] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_STARVED_THERMAL] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_DRILL_OUT_OF_RANGE] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_VIA_DIAMETER] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
// These DRC tests are not useful and do not work because they need a footprint library
|
||||
// associated to the board
|
||||
bds.m_DRCSeverities[DRCE_LIB_FOOTPRINT_ISSUES] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_LIB_FOOTPRINT_MISMATCH] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
bds.m_DRCSeverities[DRCE_DANGLING_VIA] = SEVERITY::RPT_SEVERITY_IGNORE;
|
||||
|
||||
// Ensure that our desired error is fired
|
||||
bds.m_DRCSeverities[DRCE_LENGTH_OUT_OF_RANGE] = SEVERITY::RPT_SEVERITY_ERROR;
|
||||
|
||||
bds.m_DRCEngine->SetViolationHandler(
|
||||
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
|
||||
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
|
||||
{
|
||||
if( bds.GetSeverity( aItem->GetErrorCode() ) == SEVERITY::RPT_SEVERITY_ERROR )
|
||||
violations.push_back( *aItem );
|
||||
} );
|
||||
|
||||
bds.m_DRCEngine->RunTests( EDA_UNITS::MM, true, false );
|
||||
|
||||
if( violations.size() == test.second )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
|
||||
BOOST_TEST_MESSAGE( wxString::Format( "DRC lengths: %s, passed", test.first ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCH );
|
||||
|
||||
std::map<KIID, EDA_ITEM*> itemMap;
|
||||
m_board->FillItemMap( itemMap );
|
||||
|
||||
for( const DRC_ITEM& item : violations )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR, itemMap ) );
|
||||
}
|
||||
|
||||
BOOST_ERROR( wxString::Format( "DRC skew: %s, failed (violations found %d expected %d)", test.first,
|
||||
(int) violations.size(), test.second ) );
|
||||
}
|
||||
}
|
||||
}
|
@ -28,10 +28,12 @@
|
||||
#ifndef __PNS_LOG_VIEWER_FRAME_H
|
||||
#define __PNS_LOG_VIEWER_FRAME_H
|
||||
|
||||
#include <length_calculation.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <pcb_test_frame.h>
|
||||
#include <pcbnew_utils/board_test_utils.h>
|
||||
#include <reporter.h>
|
||||
#include <router/pns_solid.h>
|
||||
|
||||
#include "pns_log_file.h"
|
||||
#include "pns_log_player.h"
|
||||
@ -105,6 +107,58 @@ public:
|
||||
return ( aLayer / 2 ) - 1;
|
||||
}
|
||||
|
||||
long long int CalculateRoutedPathLength( const PNS::ITEM_SET& aLine, const PNS::SOLID* aStartPad,
|
||||
const PNS::SOLID* aEndPad ) override
|
||||
{
|
||||
std::vector<LENGTH_CALCULATION_ITEM> lengthItems;
|
||||
|
||||
for( int idx = 0; idx < aLine.Size(); idx++ )
|
||||
{
|
||||
const PNS::ITEM* lineItem = aLine[idx];
|
||||
|
||||
if( const PNS::LINE* l = dyn_cast<const PNS::LINE*>( lineItem ) )
|
||||
{
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetLine( l->CLine() );
|
||||
|
||||
const PCB_LAYER_ID layer = GetBoardLayerFromPNSLayer( lineItem->Layer() );
|
||||
item.SetLayers( layer );
|
||||
|
||||
lengthItems.emplace_back( std::move( item ) );
|
||||
}
|
||||
else if( lineItem->OfKind( PNS::ITEM::VIA_T ) && idx > 0 && idx < aLine.Size() - 1 )
|
||||
{
|
||||
const int layerPrev = aLine[idx - 1]->Layer();
|
||||
const int layerNext = aLine[idx + 1]->Layer();
|
||||
const PCB_LAYER_ID pcbLayerPrev = GetBoardLayerFromPNSLayer( layerPrev );
|
||||
const PCB_LAYER_ID pcbLayerNext = GetBoardLayerFromPNSLayer( layerNext );
|
||||
|
||||
if( layerPrev != layerNext )
|
||||
{
|
||||
LENGTH_CALCULATION_ITEM item;
|
||||
item.SetVia( static_cast<PCB_VIA*>( lineItem->GetSourceItem() ) );
|
||||
item.SetLayers( pcbLayerPrev, pcbLayerNext );
|
||||
lengthItems.emplace_back( std::move( item ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PAD* startPad = nullptr;
|
||||
const PAD* endPad = nullptr;
|
||||
|
||||
if( aStartPad )
|
||||
startPad = static_cast<PAD*>( aStartPad->Parent() );
|
||||
|
||||
if( aEndPad )
|
||||
endPad = static_cast<PAD*>( aEndPad->Parent() );
|
||||
|
||||
constexpr PATH_OPTIMISATIONS opts = {
|
||||
.OptimiseViaLayers = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
|
||||
};
|
||||
|
||||
return m_board->GetLengthCalculation()->CalculateLength( lengthItems, opts, startPad, endPad );
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BOARD> m_board;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user