From 54ce01f8d528e818ad730228a1d1dc9c723a7c71 Mon Sep 17 00:00:00 2001 From: Robbert Lagerweij Date: Wed, 31 Jan 2018 22:29:16 +0100 Subject: [PATCH] pcbnew: make the distribute horizonally and vertically tools gap based This changes the distribute horizonally and vertically tools to place items with constant gaps between them. The previous implementation put item centers evenly apart but thereby ignored the relative width and height of the items (i.e. wide items appeared closer together than narrow items). Fixes lp;1745366 https://bugs.launchpad.net/kicad/+bug/1745366 --- pcbnew/tools/placement_tool.cpp | 93 ++++++++++++++++----------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/pcbnew/tools/placement_tool.cpp b/pcbnew/tools/placement_tool.cpp index ae716e4330..72e163f091 100644 --- a/pcbnew/tools/placement_tool.cpp +++ b/pcbnew/tools/placement_tool.cpp @@ -298,17 +298,6 @@ int ALIGN_DISTRIBUTE_TOOL::doAlignRight() return 0; } -static bool compareX( const BOARD_ITEM* aA, const BOARD_ITEM* aB ) -{ - return aA->GetBoundingBox().Centre().x < aB->GetBoundingBox().Centre().x; -} - - -static bool compareY( const BOARD_ITEM* aA, const BOARD_ITEM* aB ) -{ - return aA->GetBoundingBox().Centre().y < aB->GetBoundingBox().Centre().y; -} - int ALIGN_DISTRIBUTE_TOOL::DistributeHorizontally( const TOOL_EVENT& aEvent ) { @@ -320,31 +309,36 @@ int ALIGN_DISTRIBUTE_TOOL::DistributeHorizontally( const TOOL_EVENT& aEvent ) BOARD_COMMIT commit( getEditFrame() ); commit.StageItems( selection, CHT_MODIFY ); - // Prepare a list, so the items can be sorted by their X coordinate - std::vector itemsList; + auto alignMap = GetBoundingBoxesV( selection ); - for( auto item : selection ) - itemsList.push_back( static_cast( item ) ); + // find the last item by reverse sorting + std::sort( alignMap.begin(), alignMap.end(), SortRightmostX ); + const auto maxRight = alignMap.begin()->second.GetRight(); + const auto lastItem = alignMap.begin()->first; - // Sort items by X coordinate - std::sort(itemsList.begin(), itemsList.end(), compareX ); + // sort to get starting order + std::sort( alignMap.begin(), alignMap.end(), SortLeftmostX ); - // Expected X coordinate for the next item (=minX) - int position = itemsList.front()->GetBoundingBox().Centre().x; + auto totalGap = maxRight - alignMap.begin()->second.GetX(); - // X coordinate for the last item - const int maxX = itemsList.back()->GetBoundingBox().Centre().x; - - // Distance between items - const int distance = ( maxX - position ) / ( itemsList.size() - 1 ); - - for( auto item : itemsList ) + for( auto& i : alignMap ) { - int difference = position - item->GetBoundingBox().Centre().x; + totalGap -= i.second.GetWidth(); + } - item->Move( wxPoint( difference, 0 ) ); + const auto itemGap = totalGap / ( alignMap.size() - 1 ); - position += distance; + auto targetX = alignMap.begin()->second.GetX(); + + for( auto& i : alignMap ) + { + // cover the corner case where the last item is wider than the previous item and gap + if( lastItem == i.first ) + continue; + + int difference = targetX - i.second.GetX(); + i.first->Move( wxPoint( difference, 0 ) ); + targetX += ( i.second.GetWidth() + itemGap ); } commit.Push( _( "Distribute horizontally" ) ); @@ -363,31 +357,36 @@ int ALIGN_DISTRIBUTE_TOOL::DistributeVertically( const TOOL_EVENT& aEvent ) BOARD_COMMIT commit( getEditFrame() ); commit.StageItems( selection, CHT_MODIFY ); - // Prepare a list, so the items can be sorted by their Y coordinate - std::vector itemsList; + auto alignMap = GetBoundingBoxesV( selection ); - for( auto item : selection ) - itemsList.push_back( static_cast( item ) ); + // find the last item by reverse sorting + std::sort( alignMap.begin(), alignMap.end(), SortBottommostY ); + const auto maxBottom = alignMap.begin()->second.GetBottom(); + const auto lastItem = alignMap.begin()->first; - // Sort items by Y coordinate - std::sort( itemsList.begin(), itemsList.end(), compareY ); + // sort to get starting order + std::sort( alignMap.begin(), alignMap.end(), SortTopmostY ); - // Expected Y coordinate for the next item (=minY) - int position = (*itemsList.begin())->GetBoundingBox().Centre().y; + auto totalGap = maxBottom - alignMap.begin()->second.GetY(); - // Y coordinate for the last item - const int maxY = (*itemsList.rbegin())->GetBoundingBox().Centre().y; - - // Distance between items - const int distance = ( maxY - position ) / ( itemsList.size() - 1 ); - - for( auto item : itemsList ) + for( auto& i : alignMap ) { - int difference = position - item->GetBoundingBox().Centre().y; + totalGap -= i.second.GetHeight(); + } - item->Move( wxPoint( 0, difference ) ); + const auto itemGap = totalGap / ( alignMap.size() - 1 ); - position += distance; + auto targetY = alignMap.begin()->second.GetY(); + + for( auto& i : alignMap ) + { + // cover the corner case where the last item is wider than the previous item and gap + if( lastItem == i.first ) + continue; + + int difference = targetY - i.second.GetY(); + i.first->Move( wxPoint( 0, difference ) ); + targetY += ( i.second.GetHeight() + itemGap ); } commit.Push( _( "Distribute vertically" ) );