Commit e5deafb4 authored by Tomasz Włostowski's avatar Tomasz Włostowski

bulk improvements for selection and edit tools (GAL) disambiguatin heuristics...

bulk improvements for selection and edit tools (GAL) disambiguatin heuristics and smarter grid alignment
parent a46a92f9
...@@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho ...@@ -58,8 +58,27 @@ TOOL_ACTION COMMON_ACTIONS::findDummy( "pcbnew.Find.Dummy", // only block the ho
TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove", TOOL_ACTION COMMON_ACTIONS::findMove( "pcbnew.InteractiveSelection.FindMove",
AS_GLOBAL, 'T'); AS_GLOBAL, 'T');
// Edit tool actions // Edit tool actions
TOOL_ACTION COMMON_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.editFootprintInFpEditor",
AS_CONTEXT, MD_CTRL + 'E',
"Open in Footprint Editor",
"Opens the selected footprint in the Footprint Editor" );
TOOL_ACTION COMMON_ACTIONS::copyPadToSettings ( "pcbnew.InteractiveEdit.copyPadToSettings",
AS_CONTEXT, 0,
"Copy pad settings to Current Settings",
"Copies the properties of selected pad to the current template pad settings." );
TOOL_ACTION COMMON_ACTIONS::copySettingsToPads ( "pcbnew.InteractiveEdit.copySettingsToPads",
AS_CONTEXT, 0,
"Copy Current Settings to pads",
"Copies the current template pad settings to the selected pad(s)." );
TOOL_ACTION COMMON_ACTIONS::globalEditPads ( "pcbnew.InteractiveEdit.globalPadEdit",
AS_CONTEXT, 0,
"Global Pad Edition",
"Changes pad properties globally." );
TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit", TOOL_ACTION COMMON_ACTIONS::editActivate( "pcbnew.InteractiveEdit",
AS_GLOBAL, 'M', AS_GLOBAL, 'M',
"Move", "Moves the selected item(s)", AF_ACTIVATE ); "Move", "Moves the selected item(s)", AF_ACTIVATE );
......
...@@ -236,6 +236,13 @@ public: ...@@ -236,6 +236,13 @@ public:
/// Blocks CTRL+F, it is handled by wxWidgets /// Blocks CTRL+F, it is handled by wxWidgets
static TOOL_ACTION findDummy; static TOOL_ACTION findDummy;
static TOOL_ACTION editFootprintInFpEditor;
static TOOL_ACTION copyPadToSettings;
static TOOL_ACTION copySettingsToPads;
static TOOL_ACTION globalEditPads;
/** /**
* Function TranslateLegacyId() * Function TranslateLegacyId()
* Translates legacy tool ids to the corresponding TOOL_ACTION name. * Translates legacy tool ids to the corresponding TOOL_ACTION name.
......
This diff is collapsed.
...@@ -147,10 +147,25 @@ private: ...@@ -147,10 +147,25 @@ private:
///> If there are no items currently selected, it tries to choose the item that is under ///> If there are no items currently selected, it tries to choose the item that is under
///> the cursor or displays a disambiguation menu if there are multpile items. ///> the cursor or displays a disambiguation menu if there are multpile items.
bool makeSelection( const SELECTION& aSelection ); bool hoverSelection( const SELECTION& aSelection, bool aSanitize = true );
///> Updates view with the changes in the list. ///> Updates view with the changes in the list.
void processChanges( const PICKED_ITEMS_LIST* aList ); void processChanges( const PICKED_ITEMS_LIST* aList );
int editFootprintInFpEditor( const TOOL_EVENT& aEvent );
bool invokeInlineRouter();
template <class T> T* uniqueSelected()
{
const SELECTION& selection = m_selectionTool->GetSelection();
if(selection.items.GetCount() > 1)
return NULL;
BOARD_ITEM *item = selection.Item<BOARD_ITEM>( 0 );
return dyn_cast<T*> (item);
}
}; };
#endif #endif
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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 <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <wxPcbStruct.h>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <class_zone.h>
#include <class_draw_panel_gal.h>
#include <view/view_controls.h>
#include <gal/graphics_abstraction_layer.h>
#include <geometry/shape_line_chain.h>
#include "grid_helper.h"
GRID_HELPER::GRID_HELPER ( PCB_BASE_FRAME *aFrame ) :
m_frame ( aFrame )
{
}
GRID_HELPER::~GRID_HELPER ()
{
}
void GRID_HELPER::SetGrid ( int aSize )
{
assert ( false );
}
void GRID_HELPER::SetOrigin ( const VECTOR2I& aOrigin )
{
}
VECTOR2I GRID_HELPER::GetGrid ()
{
PCB_SCREEN *screen = m_frame->GetScreen();
const wxRealPoint& size = screen->GetGridSize();
return VECTOR2I ( KiROUND ( size.x ), KiROUND ( size.y ) );
}
VECTOR2I GRID_HELPER::GetOrigin ()
{
return VECTOR2I ( 0, 0 );
}
void GRID_HELPER::SetAuxAxes ( bool aEnable, const VECTOR2I aOrigin, bool aEnableDiagonal)
{
if( aEnable )
m_auxAxis = aOrigin;
else
m_auxAxis = boost::optional <VECTOR2I> ();
m_diagonalAuxAxesEnable = aEnable;
}
VECTOR2I GRID_HELPER::Align ( const VECTOR2I& aPoint )
{
const VECTOR2D gridOffset ( GetOrigin () );
const VECTOR2D gridSize ( GetGrid() );
VECTOR2I nearest ( round( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
round( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
if ( !m_auxAxis )
return nearest;
if ( std::abs ( m_auxAxis->x - aPoint.x) < std::abs ( nearest.x - aPoint.x ) )
nearest.x = m_auxAxis->x;
if ( std::abs ( m_auxAxis->y - aPoint.y) < std::abs ( nearest.y - aPoint.y ) )
nearest.y = m_auxAxis->y;
return nearest;
}
VECTOR2I GRID_HELPER::BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM *aItem )
{
clearAnchors();
computeAnchors( aItem, aMousePos );
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
double lineSnapMinCornerDistance = 50.0 / worldScale;
ANCHOR* nearestOutline = nearestAnchor ( aMousePos, OUTLINE, LSET::AllLayersMask() );
ANCHOR* nearestCorner = nearestAnchor ( aMousePos, CORNER, LSET::AllLayersMask() );
ANCHOR* nearestOrigin = nearestAnchor ( aMousePos, ORIGIN, LSET::AllLayersMask() );
ANCHOR* best = NULL;
double minDist = std::numeric_limits<double>::max();
if (nearestOrigin)
{
minDist = nearestOrigin->Distance(aMousePos);
best = nearestOrigin;
}
if (nearestCorner)
{
double dist = nearestCorner->Distance(aMousePos);
if (dist < minDist)
{
minDist = dist;
best = nearestCorner;
}
}
if (nearestOutline)
{
double dist = nearestOutline->Distance(aMousePos);
if (minDist > lineSnapMinCornerDistance && dist < minDist)
best = nearestOutline;
}
return best ? best->pos : aMousePos;
}
std::set<BOARD_ITEM *> GRID_HELPER::queryVisible ( const BOX2I& aArea )
{
std::set <BOARD_ITEM *> items;
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
m_frame->GetGalCanvas()->GetView()->Query( aArea, selectedItems ); // Get the list of selected items
for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
{
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
if( item->ViewIsVisible() )
items.insert ( item );
}
return items;
}
VECTOR2I GRID_HELPER::BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM *aDraggedItem )
{
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
int snapRange = (int) (100.0 / worldScale);
BOX2I bb ( VECTOR2I ( aOrigin.x - snapRange / 2, aOrigin.y - snapRange/2) , VECTOR2I (snapRange, snapRange) );
clearAnchors();
BOOST_FOREACH ( BOARD_ITEM *item, queryVisible ( bb ) )
{
computeAnchors(item, aOrigin);
}
LSET layers ( aDraggedItem->GetLayer() );
ANCHOR *nearest = nearestAnchor ( aOrigin, CORNER | SNAPPABLE, layers );
VECTOR2I nearestGrid = Align ( aOrigin );
double gridDist = (nearestGrid - aOrigin).EuclideanNorm();
if (nearest)
{
double snapDist = nearest->Distance ( aOrigin );
if(nearest && snapDist < gridDist)
return nearest->pos;
}
return nearestGrid;
}
void GRID_HELPER::computeAnchors ( BOARD_ITEM *aItem, const VECTOR2I& aRefPos )
{
VECTOR2I origin;
switch ( aItem->Type() )
{
case PCB_MODULE_T:
{
MODULE *mod = static_cast <MODULE *> (aItem);
addAnchor ( mod->GetPosition(), ORIGIN | SNAPPABLE, mod );
for (D_PAD *pad = mod->Pads(); pad; pad = pad->Next() )
addAnchor ( pad->GetPosition(), CORNER | SNAPPABLE, pad );
break;
}
case PCB_MODULE_EDGE_T:
case PCB_LINE_T:
{
DRAWSEGMENT *dseg = static_cast <DRAWSEGMENT*> (aItem);
VECTOR2I start = dseg->GetStart();
VECTOR2I end = dseg->GetEnd();
//LAYER_ID layer = dseg->GetLayer();
switch( dseg->GetShape() )
{
case S_CIRCLE:
{
int r = (start - end).EuclideanNorm();
addAnchor ( start, ORIGIN | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( -r, 0 ) , OUTLINE | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( r, 0 ) , OUTLINE | SNAPPABLE, dseg );
addAnchor ( start + VECTOR2I ( 0, -r ) , OUTLINE | SNAPPABLE, dseg);
addAnchor ( start + VECTOR2I ( 0, r ) , OUTLINE | SNAPPABLE, dseg );
break;
}
case S_ARC:
{
origin = dseg->GetCenter();
addAnchor ( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg );
addAnchor ( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg );
addAnchor ( origin, ORIGIN | SNAPPABLE, dseg );
break;
}
case S_SEGMENT:
{
origin.x = start.x + ( start.x - end.x ) / 2;
origin.y = start.y + ( start.y - end.y ) / 2;
addAnchor ( start, CORNER | SNAPPABLE, dseg );
addAnchor ( end, CORNER | SNAPPABLE, dseg );
addAnchor ( origin, ORIGIN, dseg );
break;
}
default:
{
origin = dseg->GetStart();
addAnchor ( origin, ORIGIN | SNAPPABLE, dseg );
break;
}
}
break;
}
case PCB_TRACE_T:
{
TRACK *track = static_cast <TRACK*> (aItem);
VECTOR2I start = track->GetStart();
VECTOR2I end = track->GetEnd();
origin.x = start.x + ( start.x - end.x ) / 2;
origin.y = start.y + ( start.y - end.y ) / 2;
addAnchor ( start, CORNER | SNAPPABLE, track );
addAnchor ( end, CORNER | SNAPPABLE, track );
addAnchor ( origin, ORIGIN, track);
break;
}
case PCB_ZONE_AREA_T:
{
const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
int cornersCount = outline->GetCornersCount();
SHAPE_LINE_CHAIN lc;
lc.SetClosed ( true );
for( int i = 0; i < cornersCount; ++i )
{
const VECTOR2I p ( outline->GetPos( i ) );
addAnchor ( p, CORNER, aItem );
lc.Append ( p );
}
addAnchor( lc.NearestPoint ( aRefPos ), OUTLINE, aItem );
break;
}
case PCB_MODULE_TEXT_T:
case PCB_TEXT_T:
addAnchor ( aItem->GetPosition(), ORIGIN, aItem );
default:
break;
}
}
GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor ( VECTOR2I aPos, int aFlags, LSET aMatchLayers )
{
double minDist = std::numeric_limits<double>::max();
ANCHOR *best = NULL;
BOOST_FOREACH( ANCHOR& a, m_anchors )
{
if ( !aMatchLayers [ a.item->GetLayer() ] )
continue;
if ( ( aFlags & a.flags ) != aFlags )
continue;
double dist = a.Distance(aPos);
if(dist < minDist)
{
minDist = dist;
best = &a;
}
}
return best;
}
\ No newline at end of file
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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 __GRID_HELPER_H
#define __GRID_HELPER_H
#include <vector>
#include <math/vector2d.h>
#include <boost/optional.hpp>
#include <layers_id_colors_and_visibility.h>
class PCB_BASE_FRAME;
class GRID_HELPER {
public:
GRID_HELPER ( PCB_BASE_FRAME *aFrame );
~GRID_HELPER ();
void SetGrid ( int aSize );
void SetOrigin ( const VECTOR2I& aOrigin );
VECTOR2I GetGrid ();
VECTOR2I GetOrigin ();
void SetAuxAxes ( bool aEnable, const VECTOR2I aOrigin = VECTOR2I(0, 0), bool aEnableDiagonal = false );
VECTOR2I Align ( const VECTOR2I& aPoint );
VECTOR2I BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM *aItem );
VECTOR2I BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM *aDraggedItem );
private:
enum ANCHOR_FLAGS {
CORNER = 0x1,
OUTLINE = 0x2,
SNAPPABLE = 0x4,
ORIGIN = 0x8
};
struct ANCHOR
{
ANCHOR ( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM *aItem = NULL ):
pos (aPos), flags (aFlags), item (aItem) {} ;
VECTOR2I pos;
int flags;
BOARD_ITEM *item;
double Distance ( const VECTOR2I& aP )
{
return (aP - pos).EuclideanNorm();
}
bool CanSnapItem ( const BOARD_ITEM *aItem );
};
std::vector<ANCHOR> m_anchors;
std::set<BOARD_ITEM *> queryVisible ( const BOX2I& aArea );
void addAnchor( VECTOR2I aPos, int aFlags = CORNER | SNAPPABLE, BOARD_ITEM *aItem = NULL )
{
m_anchors.push_back( ANCHOR( aPos, aFlags, aItem ) );
}
ANCHOR* nearestAnchor ( VECTOR2I aPos, int aFlags, LSET aMatchLayers );
void computeAnchors ( BOARD_ITEM *aItem, const VECTOR2I& aRefPos );
void clearAnchors ()
{
m_anchors.clear();
}
PCB_BASE_FRAME* m_frame;
boost::optional<VECTOR2I> m_auxAxis;
bool m_diagonalAuxAxesEnable;
};
#endif
This diff is collapsed.
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2015 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch> * @author Maciej Suminski <maciej.suminski@cern.ch>
* *
...@@ -78,6 +78,13 @@ private: ...@@ -78,6 +78,13 @@ private:
friend class SELECTION_TOOL; friend class SELECTION_TOOL;
}; };
enum SELECTION_LOCK_FLAGS
{
SELECTION_UNLOCKED = 0,
SELECTION_LOCK_OVERRIDE = 1,
SELECTION_LOCKED = 2
};
/** /**
* Class SELECTION_TOOL * Class SELECTION_TOOL
* *
...@@ -149,7 +156,7 @@ public: ...@@ -149,7 +156,7 @@ public:
} }
///> Checks if the user has agreed to modify locked items for the given selection. ///> Checks if the user has agreed to modify locked items for the given selection.
bool CheckLock(); SELECTION_LOCK_FLAGS CheckLock();
///> Select a single item under cursor event handler. ///> Select a single item under cursor event handler.
int CursorSelection( const TOOL_EVENT& aEvent ); int CursorSelection( const TOOL_EVENT& aEvent );
...@@ -157,6 +164,10 @@ public: ...@@ -157,6 +164,10 @@ public:
///> Clear current selection event handler. ///> Clear current selection event handler.
int ClearSelection( const TOOL_EVENT& aEvent ); int ClearSelection( const TOOL_EVENT& aEvent );
///> Makes sure a group selection does not contain items that would cause
///> conflicts when moving/rotating together (e.g. a footprint and one of the same footprint's pads)
bool SanitizeSelection( );
///> Item selection event handler. ///> Item selection event handler.
int SelectItem( const TOOL_EVENT& aEvent ); int SelectItem( const TOOL_EVENT& aEvent );
...@@ -183,7 +194,7 @@ private: ...@@ -183,7 +194,7 @@ private:
* a menu is shown, otherise function finishes without selecting anything. * a menu is shown, otherise function finishes without selecting anything.
* @return True if an item was selected, false otherwise. * @return True if an item was selected, false otherwise.
*/ */
bool selectCursor( const VECTOR2I& aWhere, bool aAllowDisambiguation = true ); bool selectCursor( const VECTOR2I& aWhere, bool aOnDrag = false);
/** /**
* Function selectMultiple() * Function selectMultiple()
...@@ -291,14 +302,12 @@ private: ...@@ -291,14 +302,12 @@ private:
void highlightNet( const VECTOR2I& aPoint ); void highlightNet( const VECTOR2I& aPoint );
/** /**
* Function prefer() * Function guessSelectionCandidates()
* Checks if collector's list contains only single entry of asked types. If so, it returns it. * Tries to guess best selection candidates in case multiple items are clicked, by
* doing some braindead heuristics.
* @param aCollector is the collector that has a list of items to be queried. * @param aCollector is the collector that has a list of items to be queried.
* @param aTypes is the list of searched/preferred types.
* @return Pointer to the preferred item, if there is only one entry of given type or NULL
* if there are more entries or no entries at all.
*/ */
BOARD_ITEM* prefer( GENERAL_COLLECTOR& aCollector, const KICAD_T aTypes[] ) const; void guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) const;
/** /**
* Function generateMenu() * Function generateMenu()
......
#include <io_mgr.h>
#include <tool/tool_manager.h>
#include <tools/selection_tool.h>
#include <tools/edit_tool.h>
#include <tools/drawing_tool.h>
#include <tools/point_editor.h>
#include <tools/pcbnew_control.h>
#include <tools/pcb_editor_control.h>
#include <tools/placement_tool.h>
#include <tools/common_actions.h>
#include <router/router_tool.h>
#include <router/length_tuner_tool.h>
void registerAllTools ( TOOL_MANAGER *aToolManager )
{
aToolManager->RegisterTool( new SELECTION_TOOL );
aToolManager->RegisterTool( new ROUTER_TOOL );
aToolManager->RegisterTool( new LENGTH_TUNER_TOOL );
aToolManager->RegisterTool( new EDIT_TOOL );
aToolManager->RegisterTool( new DRAWING_TOOL );
aToolManager->RegisterTool( new POINT_EDITOR );
aToolManager->RegisterTool( new PCBNEW_CONTROL );
aToolManager->RegisterTool( new PCB_EDITOR_CONTROL );
aToolManager->RegisterTool( new PLACEMENT_TOOL );
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment