2015-02-18 00:10:20 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 CERN
|
2019-01-29 14:04:12 +00:00
|
|
|
* Copyright (C) 2018-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
2015-02-18 00:10:20 +00:00
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
|
2016-06-29 10:23:11 +00:00
|
|
|
#include <functional>
|
|
|
|
using namespace std::placeholders;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-01-29 20:58:58 +00:00
|
|
|
#include <pcb_edit_frame.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
#include <class_board.h>
|
2018-10-05 03:23:05 +00:00
|
|
|
#include <class_dimension.h>
|
|
|
|
#include <class_draw_panel_gal.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
#include <class_edge_mod.h>
|
2018-10-05 03:23:05 +00:00
|
|
|
#include <class_module.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
#include <class_zone.h>
|
|
|
|
|
2019-10-10 13:33:21 +00:00
|
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
|
|
#include <geometry/shape_line_chain.h>
|
|
|
|
#include <math/vector2d.h>
|
2018-10-06 16:31:00 +00:00
|
|
|
#include <painter.h>
|
2016-12-02 17:58:12 +00:00
|
|
|
#include <view/view.h>
|
2015-02-18 00:10:20 +00:00
|
|
|
#include <view/view_controls.h>
|
|
|
|
|
|
|
|
#include "grid_helper.h"
|
|
|
|
|
2017-08-02 10:16:59 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
GRID_HELPER::GRID_HELPER( PCB_BASE_FRAME* aFrame ) :
|
|
|
|
m_frame( aFrame )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-03-14 11:50:39 +00:00
|
|
|
m_diagonalAuxAxesEnable = true;
|
2018-10-04 00:15:54 +00:00
|
|
|
m_enableSnap = true;
|
2018-10-05 03:42:58 +00:00
|
|
|
m_enableGrid = true;
|
2018-10-04 00:15:54 +00:00
|
|
|
m_snapSize = 100;
|
2019-10-10 13:33:21 +00:00
|
|
|
m_snapItem = nullptr;
|
|
|
|
|
2017-08-02 10:16:59 +00:00
|
|
|
KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView();
|
|
|
|
|
|
|
|
m_viewAxis.SetSize( 20000 );
|
|
|
|
m_viewAxis.SetStyle( KIGFX::ORIGIN_VIEWITEM::CROSS );
|
|
|
|
m_viewAxis.SetColor( COLOR4D( 1.0, 1.0, 1.0, 0.4 ) );
|
|
|
|
m_viewAxis.SetDrawAtZero( true );
|
|
|
|
view->Add( &m_viewAxis );
|
|
|
|
view->SetVisible( &m_viewAxis, false );
|
|
|
|
|
|
|
|
m_viewSnapPoint.SetStyle( KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS );
|
|
|
|
m_viewSnapPoint.SetColor( COLOR4D( 1.0, 1.0, 1.0, 1.0 ) );
|
|
|
|
m_viewSnapPoint.SetDrawAtZero( true );
|
|
|
|
view->Add( &m_viewSnapPoint );
|
|
|
|
view->SetVisible( &m_viewSnapPoint, false );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
GRID_HELPER::~GRID_HELPER()
|
|
|
|
{
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
void GRID_HELPER::SetGrid( int aSize )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
assert( false );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
|
|
|
void GRID_HELPER::SetOrigin( const VECTOR2I& aOrigin )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-06-30 12:08:03 +00:00
|
|
|
assert( false );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
VECTOR2I GRID_HELPER::GetGrid() const
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
PCB_SCREEN* screen = m_frame->GetScreen();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
const wxRealPoint& size = screen->GetGridSize();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
return VECTOR2I( KiROUND( size.x ), KiROUND( size.y ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
VECTOR2I GRID_HELPER::GetOrigin() const
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-06-30 12:08:03 +00:00
|
|
|
return VECTOR2I( m_frame->GetGridOrigin() );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
void GRID_HELPER::SetAuxAxes( bool aEnable, const VECTOR2I& aOrigin, bool aEnableDiagonal )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2017-08-02 10:16:59 +00:00
|
|
|
KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView();
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( aEnable )
|
2017-08-02 10:16:59 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
m_auxAxis = aOrigin;
|
2017-08-02 10:16:59 +00:00
|
|
|
m_viewAxis.SetPosition( aOrigin );
|
|
|
|
view->SetVisible( &m_viewAxis, true );
|
|
|
|
}
|
2015-02-18 16:53:46 +00:00
|
|
|
else
|
2017-08-02 10:16:59 +00:00
|
|
|
{
|
2017-11-01 11:14:16 +00:00
|
|
|
m_auxAxis = OPT<VECTOR2I>();
|
2017-08-02 10:16:59 +00:00
|
|
|
view->SetVisible( &m_viewAxis, false );
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
m_diagonalAuxAxesEnable = aEnable;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
VECTOR2I GRID_HELPER::Align( const VECTOR2I& aPoint ) const
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2018-10-05 03:42:58 +00:00
|
|
|
if( !m_enableGrid )
|
|
|
|
return aPoint;
|
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
const VECTOR2D gridOffset( GetOrigin() );
|
2015-02-18 16:53:46 +00:00
|
|
|
const VECTOR2D gridSize( GetGrid() );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-17 08:26:48 +00:00
|
|
|
VECTOR2I nearest( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
|
|
|
|
KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( !m_auxAxis )
|
|
|
|
return nearest;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-07 16:36:32 +00:00
|
|
|
if( std::abs( m_auxAxis->x - aPoint.x ) < std::abs( nearest.x - aPoint.x ) )
|
2015-02-18 16:53:46 +00:00
|
|
|
nearest.x = m_auxAxis->x;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-07-07 16:36:32 +00:00
|
|
|
if( std::abs( m_auxAxis->y - aPoint.y ) < std::abs( nearest.y - aPoint.y ) )
|
2015-02-18 16:53:46 +00:00
|
|
|
nearest.y = m_auxAxis->y;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
return nearest;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
VECTOR2I GRID_HELPER::AlignToSegment( const VECTOR2I& aPoint, const SEG& aSeg )
|
2015-11-03 16:19:42 +00:00
|
|
|
{
|
|
|
|
OPT_VECTOR2I pts[6];
|
|
|
|
|
2018-10-04 00:15:54 +00:00
|
|
|
if( !m_enableSnap )
|
|
|
|
return aPoint;
|
|
|
|
|
2015-11-03 16:19:42 +00:00
|
|
|
const VECTOR2D gridOffset( GetOrigin() );
|
|
|
|
const VECTOR2D gridSize( GetGrid() );
|
|
|
|
|
|
|
|
VECTOR2I nearest( KiROUND( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x,
|
|
|
|
KiROUND( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y );
|
|
|
|
|
|
|
|
pts[0] = aSeg.A;
|
|
|
|
pts[1] = aSeg.B;
|
2019-01-25 15:12:04 +00:00
|
|
|
pts[2] = aSeg.IntersectLines( SEG( nearest + VECTOR2I( -1, 1 ), nearest + VECTOR2I( 1, -1 ) ) );
|
|
|
|
pts[3] = aSeg.IntersectLines( SEG( nearest + VECTOR2I( -1, -1 ), nearest + VECTOR2I( 1, 1 ) ) );
|
2015-11-03 16:19:42 +00:00
|
|
|
|
|
|
|
int min_d = std::numeric_limits<int>::max();
|
|
|
|
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
|
|
{
|
|
|
|
if( pts[i] && aSeg.Contains( *pts[i] ) )
|
|
|
|
{
|
|
|
|
int d = (*pts[i] - aPoint).EuclideanNorm();
|
|
|
|
|
|
|
|
if( d < min_d )
|
|
|
|
{
|
|
|
|
min_d = d;
|
|
|
|
nearest = *pts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
VECTOR2I GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, BOARD_ITEM* aItem )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
clearAnchors();
|
2019-02-03 03:20:53 +00:00
|
|
|
computeAnchors( aItem, aMousePos, true );
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
|
|
|
|
double lineSnapMinCornerDistance = 50.0 / worldScale;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
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 );
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( dist < minDist )
|
|
|
|
{
|
|
|
|
minDist = dist;
|
|
|
|
best = nearestCorner;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( nearestOutline )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
double dist = nearestOutline->Distance( aMousePos );
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
if( minDist > lineSnapMinCornerDistance && dist < minDist )
|
|
|
|
best = nearestOutline;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return best ? best->pos : aMousePos;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2018-11-16 18:01:54 +00:00
|
|
|
std::set<BOARD_ITEM*> GRID_HELPER::queryVisible( const BOX2I& aArea,
|
|
|
|
const std::vector<BOARD_ITEM*> aSkip ) const
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
std::set<BOARD_ITEM*> items;
|
2015-02-18 00:10:20 +00:00
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
|
|
|
|
2016-12-02 17:58:12 +00:00
|
|
|
auto view = m_frame->GetGalCanvas()->GetView();
|
2018-10-06 16:31:00 +00:00
|
|
|
auto activeLayers = view->GetPainter()->GetSettings()->GetActiveLayers();
|
|
|
|
bool isHighContrast = view->GetPainter()->GetSettings()->GetHighContrast();
|
|
|
|
view->Query( aArea, selectedItems );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-10-06 16:31:00 +00:00
|
|
|
for( auto it : selectedItems )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2018-10-06 16:31:00 +00:00
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
|
2016-12-09 11:04:32 +00:00
|
|
|
|
2018-10-07 02:36:49 +00:00
|
|
|
// The item must be visible and on an active layer
|
2019-10-10 13:33:21 +00:00
|
|
|
if( view->IsVisible( item ) && ( !isHighContrast || activeLayers.count( it.second ) )
|
|
|
|
&& item->ViewGetLOD( it.second, view ) < view->GetScale() )
|
2015-02-18 16:53:46 +00:00
|
|
|
items.insert ( item );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-16 18:01:54 +00:00
|
|
|
|
|
|
|
for( auto ii : aSkip )
|
|
|
|
items.erase( ii );
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem )
|
2018-10-04 00:15:54 +00:00
|
|
|
{
|
|
|
|
LSET layers;
|
2018-11-16 18:01:54 +00:00
|
|
|
std::vector<BOARD_ITEM*> item;
|
2018-10-04 00:15:54 +00:00
|
|
|
|
|
|
|
if( aDraggedItem )
|
2018-11-16 18:01:54 +00:00
|
|
|
{
|
2018-10-04 05:45:10 +00:00
|
|
|
layers = aDraggedItem->GetLayerSet();
|
2018-11-16 18:01:54 +00:00
|
|
|
item.push_back( aDraggedItem );
|
|
|
|
}
|
2018-10-04 00:15:54 +00:00
|
|
|
else
|
|
|
|
layers = LSET::AllLayersMask();
|
|
|
|
|
2018-11-16 18:01:54 +00:00
|
|
|
return BestSnapAnchor( aOrigin, layers, item );
|
2018-10-04 00:15:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-16 18:01:54 +00:00
|
|
|
VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers,
|
|
|
|
const std::vector<BOARD_ITEM*> aSkip )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
|
2018-10-04 00:15:54 +00:00
|
|
|
int snapRange = (int) ( m_snapSize / worldScale );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange / 2 ), VECTOR2I( snapRange, snapRange ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
clearAnchors();
|
|
|
|
|
2018-11-16 18:01:54 +00:00
|
|
|
for( BOARD_ITEM* item : queryVisible( bb, aSkip ) )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
computeAnchors( item, aOrigin );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 19:03:31 +00:00
|
|
|
ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayers );
|
2015-02-18 16:53:46 +00:00
|
|
|
VECTOR2I nearestGrid = Align( aOrigin );
|
|
|
|
double gridDist = ( nearestGrid - aOrigin ).EuclideanNorm();
|
2017-04-20 14:37:36 +00:00
|
|
|
|
2018-10-04 00:15:54 +00:00
|
|
|
if( nearest && m_enableSnap )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
double snapDist = nearest->Distance( aOrigin );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-10-07 06:21:55 +00:00
|
|
|
if( !m_enableGrid || snapDist <= gridDist )
|
2017-08-02 10:16:59 +00:00
|
|
|
{
|
|
|
|
m_viewSnapPoint.SetPosition( nearest->pos );
|
2018-11-09 14:11:44 +00:00
|
|
|
|
|
|
|
if( m_frame->GetGalCanvas()->GetView()->IsVisible( &m_viewSnapPoint ) )
|
|
|
|
m_frame->GetGalCanvas()->GetView()->Update( &m_viewSnapPoint, KIGFX::GEOMETRY);
|
|
|
|
else
|
|
|
|
m_frame->GetGalCanvas()->GetView()->SetVisible( &m_viewSnapPoint, true );
|
|
|
|
|
2018-11-24 15:58:33 +00:00
|
|
|
m_snapItem = nearest;
|
2017-04-20 14:37:36 +00:00
|
|
|
return nearest->pos;
|
2017-08-02 10:16:59 +00:00
|
|
|
}
|
2015-06-30 12:08:03 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-11-24 15:58:33 +00:00
|
|
|
m_snapItem = nullptr;
|
2017-08-02 10:16:59 +00:00
|
|
|
m_frame->GetGalCanvas()->GetView()->SetVisible( &m_viewSnapPoint, false );
|
2015-06-30 12:08:03 +00:00
|
|
|
return nearestGrid;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2018-11-24 15:58:33 +00:00
|
|
|
BOARD_ITEM* GRID_HELPER::GetSnapped( void ) const
|
|
|
|
{
|
|
|
|
if( !m_snapItem )
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return m_snapItem->item;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-03 03:20:53 +00:00
|
|
|
void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, const bool aFrom )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
VECTOR2I origin;
|
2019-10-10 13:33:21 +00:00
|
|
|
auto view = m_frame->GetGalCanvas()->GetView();
|
|
|
|
auto activeLayers = view->GetPainter()->GetSettings()->GetActiveLayers();
|
|
|
|
bool isHighContrast = view->GetPainter()->GetSettings()->GetHighContrast();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
switch( aItem->Type() )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
case PCB_MODULE_T:
|
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
MODULE* mod = static_cast<MODULE*>( aItem );
|
|
|
|
|
2017-04-25 09:06:24 +00:00
|
|
|
for( auto pad : mod->Pads() )
|
2017-07-27 09:13:25 +00:00
|
|
|
{
|
2019-10-10 13:33:21 +00:00
|
|
|
// Getting pads from the module requires re-checking that the pad is shown
|
|
|
|
if( ( aFrom || m_frame->Settings().m_magneticPads == CAPTURE_ALWAYS )
|
|
|
|
&& pad->GetBoundingBox().Contains( wxPoint( aRefPos.x, aRefPos.y ) )
|
|
|
|
&& view->IsVisible( pad )
|
|
|
|
&& ( !isHighContrast || activeLayers.count( pad->GetLayer() ) )
|
|
|
|
&& pad->ViewGetLOD( pad->GetLayer(), view ) < view->GetScale() )
|
2017-07-27 09:13:25 +00:00
|
|
|
{
|
|
|
|
addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-07-27 09:13:25 +00:00
|
|
|
// if the cursor is not over a pad, then drag the module by its origin
|
|
|
|
addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-11 17:06:44 +00:00
|
|
|
case PCB_PAD_T:
|
|
|
|
{
|
2019-02-03 03:20:53 +00:00
|
|
|
if( aFrom || m_frame->Settings().m_magneticPads == CAPTURE_ALWAYS )
|
2019-01-29 14:04:12 +00:00
|
|
|
{
|
|
|
|
D_PAD* pad = static_cast<D_PAD*>( aItem );
|
|
|
|
addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad );
|
|
|
|
}
|
|
|
|
|
2015-03-11 17:06:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
case PCB_MODULE_EDGE_T:
|
|
|
|
case PCB_LINE_T:
|
|
|
|
{
|
2019-01-29 14:04:12 +00:00
|
|
|
if( !m_frame->Settings().m_magneticGraphics )
|
|
|
|
break;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
DRAWSEGMENT* dseg = static_cast<DRAWSEGMENT*>( aItem );
|
2015-02-18 00:10:20 +00:00
|
|
|
VECTOR2I start = dseg->GetStart();
|
|
|
|
VECTOR2I end = dseg->GetEnd();
|
|
|
|
|
|
|
|
switch( dseg->GetShape() )
|
|
|
|
{
|
|
|
|
case S_CIRCLE:
|
|
|
|
{
|
2015-02-18 16:53:46 +00:00
|
|
|
int r = ( start - end ).EuclideanNorm();
|
|
|
|
|
|
|
|
addAnchor( start, ORIGIN | SNAPPABLE, dseg );
|
2015-07-07 16:36:32 +00:00
|
|
|
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 );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case S_ARC:
|
|
|
|
origin = dseg->GetCenter();
|
2015-02-18 16:53:46 +00:00
|
|
|
addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
|
|
|
|
break;
|
2018-10-05 03:23:05 +00:00
|
|
|
|
|
|
|
case S_RECT:
|
|
|
|
addAnchor( start, CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( VECTOR2I( end.x, start.y ), CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( VECTOR2I( start.x, end.y ), CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( end, CORNER | SNAPPABLE, dseg );
|
|
|
|
break;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
case S_SEGMENT:
|
|
|
|
origin.x = start.x + ( start.x - end.x ) / 2;
|
|
|
|
origin.y = start.y + ( start.y - end.y ) / 2;
|
2015-02-18 16:53:46 +00:00
|
|
|
addAnchor( start, CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( end, CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( origin, ORIGIN, dseg );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
|
2017-10-19 21:14:01 +00:00
|
|
|
case S_POLYGON:
|
2018-01-24 13:22:43 +00:00
|
|
|
for( const auto& p : dseg->BuildPolyPointsList() )
|
2017-12-18 17:24:25 +00:00
|
|
|
addAnchor( p, CORNER | SNAPPABLE, dseg );
|
2018-10-05 03:23:05 +00:00
|
|
|
|
2017-10-19 21:14:01 +00:00
|
|
|
break;
|
|
|
|
|
2018-10-05 03:23:05 +00:00
|
|
|
case S_CURVE:
|
|
|
|
addAnchor( start, CORNER | SNAPPABLE, dseg );
|
|
|
|
addAnchor( end, CORNER | SNAPPABLE, dseg );
|
|
|
|
//Fallthrough
|
2015-02-18 00:10:20 +00:00
|
|
|
default:
|
|
|
|
origin = dseg->GetStart();
|
2015-02-18 16:53:46 +00:00
|
|
|
addAnchor( origin, ORIGIN | SNAPPABLE, dseg );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
{
|
2019-02-03 03:20:53 +00:00
|
|
|
if( aFrom || m_frame->Settings().m_magneticTracks == CAPTURE_ALWAYS )
|
2019-01-29 14:04:12 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-10-05 03:23:05 +00:00
|
|
|
case PCB_MARKER_T:
|
|
|
|
case PCB_TARGET_T:
|
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem );
|
2015-07-07 16:36:32 +00:00
|
|
|
break;
|
|
|
|
|
2019-01-29 14:04:12 +00:00
|
|
|
case PCB_VIA_T:
|
|
|
|
{
|
2019-02-03 03:20:53 +00:00
|
|
|
if( aFrom || m_frame->Settings().m_magneticTracks == CAPTURE_ALWAYS )
|
2019-01-29 14:04:12 +00:00
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
case PCB_ZONE_AREA_T:
|
|
|
|
{
|
2017-03-07 12:06:00 +00:00
|
|
|
const SHAPE_POLY_SET* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline();
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
SHAPE_LINE_CHAIN lc;
|
2015-02-18 16:53:46 +00:00
|
|
|
lc.SetClosed( true );
|
|
|
|
|
2017-03-07 12:06:00 +00:00
|
|
|
for( auto iter = outline->CIterateWithHoles(); iter; iter++ )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2017-03-07 12:06:00 +00:00
|
|
|
addAnchor( *iter, CORNER, aItem );
|
|
|
|
lc.Append( *iter );
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-10-05 03:23:05 +00:00
|
|
|
case PCB_DIMENSION_T:
|
|
|
|
{
|
|
|
|
const DIMENSION* dim = static_cast<const DIMENSION*>( aItem );
|
2018-10-06 16:29:19 +00:00
|
|
|
addAnchor( dim->m_crossBarF, CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->m_crossBarO, CORNER | SNAPPABLE, aItem );
|
2018-10-05 03:23:05 +00:00
|
|
|
addAnchor( dim->m_featureLineGO, CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->m_featureLineDO, CORNER | SNAPPABLE, aItem );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
case PCB_MODULE_TEXT_T:
|
|
|
|
case PCB_TEXT_T:
|
2015-02-18 16:53:46 +00:00
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN, aItem );
|
2018-10-05 03:23:05 +00:00
|
|
|
break;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2018-10-05 03:23:05 +00:00
|
|
|
default:
|
|
|
|
break;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-30 12:08:03 +00:00
|
|
|
GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags, LSET aMatchLayers )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
2017-04-20 14:37:36 +00:00
|
|
|
double minDist = std::numeric_limits<double>::max();
|
|
|
|
ANCHOR* best = NULL;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
for( ANCHOR& a : m_anchors )
|
|
|
|
{
|
2018-10-05 03:41:17 +00:00
|
|
|
if( ( aMatchLayers & a.item->GetLayerSet() ) == 0 )
|
2017-04-20 14:37:36 +00:00
|
|
|
continue;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
if( ( aFlags & a.flags ) != aFlags )
|
|
|
|
continue;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
double dist = a.Distance( aPos );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
if( dist < minDist )
|
|
|
|
{
|
|
|
|
minDist = dist;
|
|
|
|
best = &a;
|
|
|
|
}
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
return best;
|
2015-02-18 16:53:46 +00:00
|
|
|
}
|