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
|
2023-03-04 23:15:43 +00:00
|
|
|
* Copyright (C) 2018-2023 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>
|
2021-06-11 16:59:28 +00:00
|
|
|
#include <pcb_dimension.h>
|
2023-03-30 11:49:23 +00:00
|
|
|
#include <pcb_shape.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <footprint.h>
|
2021-06-06 19:03:10 +00:00
|
|
|
#include <pad.h>
|
2021-06-03 17:03:25 +00:00
|
|
|
#include <pcb_group.h>
|
2021-06-11 21:07:02 +00:00
|
|
|
#include <pcb_track.h>
|
2020-11-11 23:05:59 +00:00
|
|
|
#include <zone.h>
|
2020-09-29 03:59:44 +00:00
|
|
|
#include <geometry/shape_circle.h>
|
2019-10-10 13:33:21 +00:00
|
|
|
#include <geometry/shape_line_chain.h>
|
2020-09-29 03:59:44 +00:00
|
|
|
#include <geometry/shape_rect.h>
|
|
|
|
#include <geometry/shape_segment.h>
|
|
|
|
#include <geometry/shape_simple.h>
|
2020-04-24 23:44:09 +00:00
|
|
|
#include <macros.h>
|
2020-09-29 03:59:44 +00:00
|
|
|
#include <math/util.h> // for KiROUND
|
2018-10-06 16:31:00 +00:00
|
|
|
#include <painter.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <pcbnew_settings.h>
|
2020-09-29 03:59:44 +00:00
|
|
|
#include <tool/tool_manager.h>
|
2021-01-06 00:07:29 +00:00
|
|
|
#include <tools/pcb_tool_base.h>
|
2016-12-02 17:58:12 +00:00
|
|
|
#include <view/view.h>
|
2021-01-16 23:17:32 +00:00
|
|
|
#include "pcb_grid_helper.h"
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-08-02 10:16:59 +00:00
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
PCB_GRID_HELPER::PCB_GRID_HELPER( TOOL_MANAGER* aToolMgr, MAGNETIC_SETTINGS* aMagneticSettings ) :
|
|
|
|
GRID_HELPER( aToolMgr ),
|
2020-06-13 12:33:29 +00:00
|
|
|
m_magneticSettings( aMagneticSettings )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2020-12-19 13:44:07 +00:00
|
|
|
KIGFX::VIEW* view = m_toolMgr->GetView();
|
|
|
|
KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
|
|
|
|
KIGFX::COLOR4D auxItemsColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
|
|
|
|
KIGFX::COLOR4D umbilicalColor = settings->GetLayerColor( LAYER_ANCHOR );
|
2017-08-02 10:16:59 +00:00
|
|
|
|
|
|
|
m_viewAxis.SetSize( 20000 );
|
|
|
|
m_viewAxis.SetStyle( KIGFX::ORIGIN_VIEWITEM::CROSS );
|
2020-12-19 13:44:07 +00:00
|
|
|
m_viewAxis.SetColor( auxItemsColor.WithAlpha( 0.4 ) );
|
2017-08-02 10:16:59 +00:00
|
|
|
m_viewAxis.SetDrawAtZero( true );
|
|
|
|
view->Add( &m_viewAxis );
|
|
|
|
view->SetVisible( &m_viewAxis, false );
|
|
|
|
|
|
|
|
m_viewSnapPoint.SetStyle( KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS );
|
2020-12-19 13:44:07 +00:00
|
|
|
m_viewSnapPoint.SetColor( auxItemsColor );
|
2017-08-02 10:16:59 +00:00
|
|
|
m_viewSnapPoint.SetDrawAtZero( true );
|
|
|
|
view->Add( &m_viewSnapPoint );
|
|
|
|
view->SetVisible( &m_viewSnapPoint, false );
|
2020-01-19 04:42:10 +00:00
|
|
|
|
|
|
|
m_viewSnapLine.SetStyle( KIGFX::ORIGIN_VIEWITEM::DASH_LINE );
|
2020-12-19 13:44:07 +00:00
|
|
|
m_viewSnapLine.SetColor( umbilicalColor );
|
2020-01-19 04:42:10 +00:00
|
|
|
m_viewSnapLine.SetDrawAtZero( true );
|
|
|
|
view->Add( &m_viewSnapLine );
|
|
|
|
view->SetVisible( &m_viewSnapLine, false );
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::AlignToSegment( const VECTOR2I& aPoint, const SEG& aSeg )
|
2015-11-03 16:19:42 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
const int c_gridSnapEpsilon_sq = 4;
|
2021-02-11 23:05:46 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
VECTOR2I aligned = Align( aPoint );
|
2018-10-04 00:15:54 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
if( !m_enableSnap )
|
|
|
|
return aligned;
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
std::vector<VECTOR2I> points;
|
2021-07-26 16:01:22 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
const SEG testSegments[] = { SEG( aligned, aligned + VECTOR2( 1, 0 ) ),
|
|
|
|
SEG( aligned, aligned + VECTOR2( 0, 1 ) ),
|
|
|
|
SEG( aligned, aligned + VECTOR2( 1, 1 ) ),
|
|
|
|
SEG( aligned, aligned + VECTOR2( 1, -1 ) ) };
|
2021-07-26 16:01:22 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
for( const SEG& seg : testSegments )
|
|
|
|
{
|
|
|
|
OPT_VECTOR2I vec = aSeg.IntersectLines( seg );
|
2021-07-26 16:01:22 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
if( vec && aSeg.SquaredDistance( *vec ) <= c_gridSnapEpsilon_sq )
|
2022-11-19 22:51:27 +00:00
|
|
|
points.push_back( *vec );
|
|
|
|
}
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
VECTOR2I nearest = aligned;
|
|
|
|
SEG::ecoord min_d_sq = VECTOR2I::ECOORD_MAX;
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
// Snap by distance between pointer and endpoints
|
|
|
|
for( const VECTOR2I& pt : { aSeg.A, aSeg.B } )
|
2015-11-03 16:19:42 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
|
2022-11-19 22:51:27 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
if( d_sq < min_d_sq )
|
2015-11-03 16:19:42 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
min_d_sq = d_sq;
|
2022-11-19 22:51:27 +00:00
|
|
|
nearest = pt;
|
|
|
|
}
|
|
|
|
}
|
2015-11-03 16:19:42 +00:00
|
|
|
|
2022-11-19 22:51:27 +00:00
|
|
|
// Snap by distance between aligned cursor and intersections
|
|
|
|
for( const VECTOR2I& pt : points )
|
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
|
2022-11-19 22:51:27 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
if( d_sq < min_d_sq )
|
2022-11-19 22:51:27 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
min_d_sq = d_sq;
|
2022-11-19 22:51:27 +00:00
|
|
|
nearest = pt;
|
2015-11-03 16:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::AlignToArc( const VECTOR2I& aPoint, const SHAPE_ARC& aArc )
|
2019-05-17 00:13:21 +00:00
|
|
|
{
|
2022-11-19 22:54:11 +00:00
|
|
|
VECTOR2I aligned = Align( aPoint );
|
|
|
|
|
2019-05-17 00:13:21 +00:00
|
|
|
if( !m_enableSnap )
|
2022-11-19 22:54:11 +00:00
|
|
|
return aligned;
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-11-19 22:54:11 +00:00
|
|
|
std::vector<VECTOR2I> points;
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-11-19 22:54:11 +00:00
|
|
|
aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 0 ) ), &points );
|
|
|
|
aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 0, 1 ) ), &points );
|
|
|
|
aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, 1 ) ), &points );
|
|
|
|
aArc.IntersectLine( SEG( aligned, aligned + VECTOR2( 1, -1 ) ), &points );
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
VECTOR2I nearest = aligned;
|
|
|
|
SEG::ecoord min_d_sq = VECTOR2I::ECOORD_MAX;
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-11-19 22:54:11 +00:00
|
|
|
// Snap by distance between pointer and endpoints
|
2022-08-15 19:07:16 +00:00
|
|
|
for( const VECTOR2I& pt : { aArc.GetP0(), aArc.GetP1() } )
|
2019-05-17 00:13:21 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
SEG::ecoord d_sq = ( pt - aPoint ).SquaredEuclideanNorm();
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
if( d_sq < min_d_sq )
|
2019-05-17 00:13:21 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
min_d_sq = d_sq;
|
2019-05-17 00:13:21 +00:00
|
|
|
nearest = pt;
|
|
|
|
}
|
2022-11-19 22:54:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Snap by distance between aligned cursor and intersections
|
|
|
|
for( const VECTOR2I& pt : points )
|
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
SEG::ecoord d_sq = ( pt - aligned ).SquaredEuclideanNorm();
|
2022-11-19 22:54:11 +00:00
|
|
|
|
2022-11-30 05:18:10 +00:00
|
|
|
if( d_sq < min_d_sq )
|
2022-11-19 22:54:11 +00:00
|
|
|
{
|
2022-11-30 05:18:10 +00:00
|
|
|
min_d_sq = d_sq;
|
2022-11-19 22:54:11 +00:00
|
|
|
nearest = pt;
|
|
|
|
}
|
2019-05-17 00:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-04 01:16:51 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::AlignToNearestPad( const VECTOR2I& aMousePos, PADS& aPads )
|
|
|
|
{
|
|
|
|
clearAnchors();
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : aPads )
|
|
|
|
computeAnchors( item, aMousePos, true );
|
|
|
|
|
|
|
|
double minDist = std::numeric_limits<double>::max();
|
|
|
|
ANCHOR* nearestOrigin = nullptr;
|
|
|
|
|
|
|
|
for( ANCHOR& a : m_anchors )
|
|
|
|
{
|
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( a.item );
|
|
|
|
|
|
|
|
if( ( ORIGIN & a.flags ) != ORIGIN )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( !item->HitTest( aMousePos ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
double dist = a.Distance( aMousePos );
|
|
|
|
|
|
|
|
if( dist < minDist )
|
|
|
|
{
|
|
|
|
minDist = dist;
|
|
|
|
nearestOrigin = &a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearestOrigin ? nearestOrigin->pos : aMousePos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos,
|
2022-11-08 16:26:42 +00:00
|
|
|
std::vector<BOARD_ITEM*>& aItems,
|
2023-08-24 14:13:59 +00:00
|
|
|
GRID_HELPER_GRIDS aGrid,
|
2022-11-08 16:26:42 +00:00
|
|
|
const SELECTION_FILTER_OPTIONS* aSelectionFilter )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
|
|
|
clearAnchors();
|
2019-08-21 04:31:11 +00:00
|
|
|
|
2020-06-13 12:33:29 +00:00
|
|
|
for( BOARD_ITEM* item : aItems )
|
2022-11-08 16:26:42 +00:00
|
|
|
computeAnchors( item, aMousePos, true, aSelectionFilter );
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2020-06-12 10:58:56 +00:00
|
|
|
double worldScale = m_toolMgr->GetView()->GetGAL()->GetWorldScale();
|
2015-02-18 00:10:20 +00:00
|
|
|
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() );
|
2021-07-19 23:56:05 +00:00
|
|
|
ANCHOR* best = nullptr;
|
2015-02-18 16:53:46 +00:00
|
|
|
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
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aReferenceItem,
|
|
|
|
GRID_HELPER_GRIDS aGrid )
|
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
|
|
|
|
2021-01-29 18:13:31 +00:00
|
|
|
if( aReferenceItem )
|
2018-11-16 18:01:54 +00:00
|
|
|
{
|
2021-01-29 18:13:31 +00:00
|
|
|
layers = aReferenceItem->GetLayerSet();
|
|
|
|
item.push_back( aReferenceItem );
|
2018-11-16 18:01:54 +00:00
|
|
|
}
|
2018-10-04 00:15:54 +00:00
|
|
|
else
|
2021-05-25 20:55:57 +00:00
|
|
|
{
|
2018-10-04 00:15:54 +00:00
|
|
|
layers = LSET::AllLayersMask();
|
2021-05-25 20:55:57 +00:00
|
|
|
}
|
2018-10-04 00:15:54 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
return BestSnapAnchor( aOrigin, layers, aGrid, item );
|
2018-10-04 00:15:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers,
|
2023-08-24 14:13:59 +00:00
|
|
|
GRID_HELPER_GRIDS aGrid,
|
2021-01-16 23:17:32 +00:00
|
|
|
const std::vector<BOARD_ITEM*>& aSkip )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2021-01-15 17:29:47 +00:00
|
|
|
// Tuning constant: snap radius in screen space
|
|
|
|
const int snapSize = 25;
|
|
|
|
|
|
|
|
// Snapping distance is in screen space, clamped to the current grid to ensure that the grid
|
|
|
|
// points that are visible can always be snapped to.
|
|
|
|
// see https://gitlab.com/kicad/code/kicad/-/issues/5638
|
|
|
|
// see https://gitlab.com/kicad/code/kicad/-/issues/7125
|
2022-12-22 01:45:31 +00:00
|
|
|
// see https://gitlab.com/kicad/code/kicad/-/issues/12303
|
2023-08-11 15:58:33 +00:00
|
|
|
double snapScale = snapSize / m_toolMgr->GetView()->GetGAL()->GetWorldScale();
|
|
|
|
// warning: GetVisibleGrid().x sometimes returns a value > INT_MAX. Intermediate calculation
|
|
|
|
// needs double.
|
|
|
|
int snapRange = KiROUND( m_enableGrid ? std::min( snapScale, GetVisibleGrid().x ) : snapScale );
|
2022-12-22 01:45:31 +00:00
|
|
|
int snapDist = snapRange;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2022-02-11 16:42:52 +00:00
|
|
|
//Respect limits of coordinates representation
|
|
|
|
BOX2I bb;
|
2022-10-26 18:39:44 +00:00
|
|
|
bb.SetOrigin( GetClampedCoords<double, int>( VECTOR2D( aOrigin ) - snapRange / 2.0 ) );
|
|
|
|
bb.SetEnd( GetClampedCoords<double, int>( VECTOR2D( aOrigin ) + snapRange / 2.0 ) );
|
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 16:53:46 +00:00
|
|
|
computeAnchors( item, aOrigin );
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2020-06-13 12:33:29 +00:00
|
|
|
ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayers );
|
2023-08-24 14:13:59 +00:00
|
|
|
VECTOR2I nearestGrid = Align( aOrigin, aGrid );
|
2017-04-20 14:37:36 +00:00
|
|
|
|
2020-09-11 17:33:12 +00:00
|
|
|
if( nearest )
|
|
|
|
snapDist = nearest->Distance( aOrigin );
|
2018-11-09 14:11:44 +00:00
|
|
|
|
2020-09-11 17:33:12 +00:00
|
|
|
// Existing snap lines need priority over new snaps
|
|
|
|
if( m_snapItem && m_enableSnapLine && m_enableSnap )
|
2020-01-19 04:42:10 +00:00
|
|
|
{
|
|
|
|
bool snapLine = false;
|
2020-09-11 17:33:12 +00:00
|
|
|
int x_dist = std::abs( m_viewSnapLine.GetPosition().x - aOrigin.x );
|
|
|
|
int y_dist = std::abs( m_viewSnapLine.GetPosition().y - aOrigin.y );
|
2020-01-19 04:42:10 +00:00
|
|
|
|
2020-09-11 17:33:12 +00:00
|
|
|
/// Allows de-snapping from the line if you are closer to another snap point
|
2020-09-29 01:37:48 +00:00
|
|
|
if( x_dist < snapRange && ( !nearest || snapDist > snapRange ) )
|
2020-01-19 04:42:10 +00:00
|
|
|
{
|
|
|
|
nearestGrid.x = m_viewSnapLine.GetPosition().x;
|
|
|
|
snapLine = true;
|
|
|
|
}
|
|
|
|
|
2020-09-29 01:37:48 +00:00
|
|
|
if( y_dist < snapRange && ( !nearest || snapDist > snapRange ) )
|
2020-01-19 04:42:10 +00:00
|
|
|
{
|
|
|
|
nearestGrid.y = m_viewSnapLine.GetPosition().y;
|
|
|
|
snapLine = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( snapLine && m_skipPoint != VECTOR2I( m_viewSnapLine.GetPosition() ) )
|
|
|
|
{
|
|
|
|
m_viewSnapLine.SetEndPosition( nearestGrid );
|
|
|
|
|
2020-06-12 10:58:56 +00:00
|
|
|
if( m_toolMgr->GetView()->IsVisible( &m_viewSnapLine ) )
|
|
|
|
m_toolMgr->GetView()->Update( &m_viewSnapLine, KIGFX::GEOMETRY );
|
2020-01-19 04:42:10 +00:00
|
|
|
else
|
2020-06-12 10:58:56 +00:00
|
|
|
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, true );
|
2020-01-19 04:42:10 +00:00
|
|
|
|
|
|
|
return nearestGrid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-11 17:33:12 +00:00
|
|
|
if( nearest && m_enableSnap )
|
|
|
|
{
|
|
|
|
if( nearest->Distance( aOrigin ) <= snapRange )
|
|
|
|
{
|
2023-02-19 03:40:07 +00:00
|
|
|
m_viewSnapPoint.SetPosition( nearest->pos );
|
|
|
|
m_viewSnapLine.SetPosition( nearest->pos );
|
2020-09-11 17:33:12 +00:00
|
|
|
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, false );
|
|
|
|
|
|
|
|
if( m_toolMgr->GetView()->IsVisible( &m_viewSnapPoint ) )
|
|
|
|
m_toolMgr->GetView()->Update( &m_viewSnapPoint, KIGFX::GEOMETRY);
|
|
|
|
else
|
|
|
|
m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, true );
|
|
|
|
|
|
|
|
m_snapItem = nearest;
|
|
|
|
return nearest->pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-24 15:58:33 +00:00
|
|
|
m_snapItem = nullptr;
|
2020-06-12 10:58:56 +00:00
|
|
|
m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
|
|
|
|
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, 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
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
BOARD_ITEM* PCB_GRID_HELPER::GetSnapped() const
|
2018-11-24 15:58:33 +00:00
|
|
|
{
|
|
|
|
if( !m_snapItem )
|
|
|
|
return nullptr;
|
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
return static_cast<BOARD_ITEM*>( m_snapItem->item );
|
2018-11-24 15:58:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
GRID_HELPER_GRIDS PCB_GRID_HELPER::GetItemGrid( const EDA_ITEM* aItem ) const
|
|
|
|
{
|
|
|
|
if( !aItem )
|
|
|
|
return GRID_CURRENT;
|
|
|
|
|
|
|
|
switch( aItem->Type() )
|
|
|
|
{
|
|
|
|
case PCB_FOOTPRINT_T:
|
|
|
|
case PCB_PAD_T:
|
|
|
|
return GRID_CONNECTABLE;
|
|
|
|
|
|
|
|
case PCB_TEXT_T:
|
|
|
|
case PCB_FIELD_T:
|
|
|
|
return GRID_TEXT;
|
|
|
|
|
|
|
|
case PCB_SHAPE_T:
|
|
|
|
case PCB_DIMENSION_T:
|
|
|
|
case PCB_BITMAP_T:
|
|
|
|
case PCB_TEXTBOX_T:
|
|
|
|
return GRID_GRAPHICS;
|
|
|
|
|
|
|
|
case PCB_TRACE_T:
|
|
|
|
case PCB_ARC_T:
|
|
|
|
return GRID_WIRES;
|
|
|
|
|
|
|
|
case PCB_VIA_T:
|
|
|
|
return GRID_VIAS;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return GRID_CURRENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VECTOR2D PCB_GRID_HELPER::GetGridSize( GRID_HELPER_GRIDS aGrid ) const
|
|
|
|
{
|
|
|
|
const GRID_SETTINGS& grid = m_toolMgr->GetSettings()->m_Window.grid;
|
|
|
|
|
|
|
|
VECTOR2D g = m_toolMgr->GetView()->GetGAL()->GetGridSize();
|
|
|
|
|
|
|
|
if( !grid.overrides_enabled )
|
|
|
|
return g;
|
|
|
|
|
|
|
|
switch( aGrid )
|
|
|
|
{
|
|
|
|
case GRID_CONNECTABLE:
|
2023-08-26 12:29:24 +00:00
|
|
|
if( grid.override_connected )
|
|
|
|
{
|
2023-08-24 14:13:59 +00:00
|
|
|
g.x = g.y = EDA_UNIT_UTILS::UI::DoubleValueFromString(
|
2023-08-26 12:29:24 +00:00
|
|
|
pcbIUScale, EDA_UNITS::MILLIMETRES, grid.override_connected_size );
|
|
|
|
}
|
2023-08-24 14:13:59 +00:00
|
|
|
|
|
|
|
break;
|
2023-08-26 12:29:24 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
case GRID_WIRES:
|
|
|
|
if( grid.override_wires )
|
2023-08-26 12:29:24 +00:00
|
|
|
{
|
2023-08-24 14:13:59 +00:00
|
|
|
g.x = g.y = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES,
|
|
|
|
grid.override_wires_size );
|
2023-08-26 12:29:24 +00:00
|
|
|
}
|
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
break;
|
2023-08-26 12:29:24 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
case GRID_VIAS:
|
|
|
|
if( grid.override_vias )
|
2023-08-26 12:29:24 +00:00
|
|
|
{
|
2023-08-24 14:13:59 +00:00
|
|
|
g.x = g.y = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES,
|
|
|
|
grid.override_vias_size );
|
2023-08-26 12:29:24 +00:00
|
|
|
}
|
2023-08-24 14:13:59 +00:00
|
|
|
|
|
|
|
break;
|
2023-08-26 12:29:24 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
case GRID_TEXT:
|
|
|
|
if( grid.override_text )
|
2023-08-26 12:29:24 +00:00
|
|
|
{
|
2023-08-24 14:13:59 +00:00
|
|
|
g.x = g.y = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES,
|
|
|
|
grid.override_text_size );
|
2023-08-26 12:29:24 +00:00
|
|
|
}
|
2023-08-24 14:13:59 +00:00
|
|
|
|
|
|
|
break;
|
2023-08-26 12:29:24 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
case GRID_GRAPHICS:
|
|
|
|
if( grid.override_graphics )
|
2023-08-26 12:29:24 +00:00
|
|
|
{
|
2023-08-24 14:13:59 +00:00
|
|
|
g.x = g.y = EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES,
|
|
|
|
grid.override_graphics_size );
|
2023-08-26 12:29:24 +00:00
|
|
|
}
|
2023-08-24 14:13:59 +00:00
|
|
|
|
|
|
|
break;
|
2023-08-26 12:29:24 +00:00
|
|
|
|
2023-08-24 14:13:59 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
std::set<BOARD_ITEM*> PCB_GRID_HELPER::queryVisible( const BOX2I& aArea,
|
|
|
|
const std::vector<BOARD_ITEM*>& aSkip ) const
|
|
|
|
{
|
|
|
|
std::set<BOARD_ITEM*> items;
|
|
|
|
std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
|
|
|
|
|
2023-08-19 19:55:42 +00:00
|
|
|
PCB_TOOL_BASE* currentTool = static_cast<PCB_TOOL_BASE*>( m_toolMgr->GetCurrentTool() );
|
2022-12-12 23:37:46 +00:00
|
|
|
KIGFX::VIEW* view = m_toolMgr->GetView();
|
|
|
|
RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
|
|
|
|
const std::set<int>& activeLayers = settings->GetHighContrastLayers();
|
|
|
|
bool isHighContrast = settings->GetHighContrast();
|
2021-01-16 23:17:32 +00:00
|
|
|
|
|
|
|
view->Query( aArea, selectedItems );
|
|
|
|
|
2023-08-19 19:55:42 +00:00
|
|
|
for( const auto& [ viewItem, layer ] : selectedItems )
|
2021-01-16 23:17:32 +00:00
|
|
|
{
|
2023-08-19 19:55:42 +00:00
|
|
|
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( viewItem );
|
2021-01-16 23:17:32 +00:00
|
|
|
|
2023-08-19 19:55:42 +00:00
|
|
|
if( currentTool->IsFootprintEditor() )
|
2021-01-16 23:17:32 +00:00
|
|
|
{
|
2023-08-19 19:55:42 +00:00
|
|
|
// If we are in the footprint editor, don't use the footprint itself
|
|
|
|
if( boardItem->Type() == PCB_FOOTPRINT_T )
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If we are not in the footprint editor, don't use footprint-editor-private items
|
|
|
|
if( FOOTPRINT* parentFP = boardItem->GetParentFootprint() )
|
|
|
|
{
|
|
|
|
if( IsPcbLayer( layer ) && parentFP->GetPrivateLayers().test( layer ) )
|
|
|
|
continue;
|
|
|
|
}
|
2021-01-16 23:17:32 +00:00
|
|
|
}
|
|
|
|
|
2023-08-19 19:55:42 +00:00
|
|
|
// The boardItem must be visible and on an active layer
|
|
|
|
if( view->IsVisible( boardItem )
|
|
|
|
&& ( !isHighContrast || activeLayers.count( layer ) )
|
|
|
|
&& boardItem->ViewGetLOD( layer, view ) < view->GetScale() )
|
2021-01-16 23:17:32 +00:00
|
|
|
{
|
2023-08-19 19:55:42 +00:00
|
|
|
items.insert ( boardItem );
|
2021-01-16 23:17:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-29 12:04:53 +00:00
|
|
|
std::function<void( BOARD_ITEM* )> skipItem =
|
|
|
|
[&]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
items.erase( aItem );
|
|
|
|
|
|
|
|
if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aItem ) )
|
|
|
|
{
|
|
|
|
footprint->RunOnChildren(
|
|
|
|
[&]( BOARD_ITEM* aChild )
|
|
|
|
{
|
|
|
|
skipItem( aChild );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( aItem ) )
|
|
|
|
{
|
|
|
|
group->RunOnChildren(
|
|
|
|
[&]( BOARD_ITEM* aChild )
|
|
|
|
{
|
|
|
|
skipItem( aChild );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : aSkip )
|
|
|
|
skipItem( item );
|
2021-01-16 23:17:32 +00:00
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
|
|
|
|
const SELECTION_FILTER_OPTIONS* aSelectionFilter )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2022-12-12 23:37:46 +00:00
|
|
|
KIGFX::VIEW* view = m_toolMgr->GetView();
|
|
|
|
RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
|
|
|
|
const std::set<int>& activeLayers = settings->GetHighContrastLayers();
|
|
|
|
bool isHighContrast = settings->GetHighContrast();
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2020-09-29 01:03:19 +00:00
|
|
|
auto handlePadShape =
|
2020-11-12 22:30:02 +00:00
|
|
|
[&]( PAD* aPad )
|
2020-09-29 01:03:19 +00:00
|
|
|
{
|
2021-01-08 03:44:28 +00:00
|
|
|
addAnchor( aPad->GetPosition(), ORIGIN | SNAPPABLE, aPad );
|
|
|
|
|
|
|
|
/// If we are getting a drag point, we don't want to center the edge of pads
|
|
|
|
if( aFrom )
|
|
|
|
return;
|
2020-09-29 01:03:19 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
switch( aPad->GetShape() )
|
2020-09-29 01:03:19 +00:00
|
|
|
{
|
2022-10-10 17:10:15 +00:00
|
|
|
case PAD_SHAPE::CIRCLE:
|
|
|
|
{
|
2022-10-10 18:15:40 +00:00
|
|
|
int r = aPad->GetSizeX() / 2;
|
2022-10-10 17:10:15 +00:00
|
|
|
VECTOR2I center = aPad->ShapePos();
|
|
|
|
|
|
|
|
addAnchor( center + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( center + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( center + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( center + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, aPad );
|
|
|
|
break;
|
|
|
|
}
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
case PAD_SHAPE::OVAL:
|
|
|
|
{
|
|
|
|
VECTOR2I pos = aPad->ShapePos();
|
|
|
|
VECTOR2I half_size = aPad->GetSize() / 2;
|
|
|
|
int half_width = std::min( half_size.x, half_size.y );
|
|
|
|
VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
|
|
|
|
|
|
|
|
RotatePoint( half_len, aPad->GetOrientation() );
|
|
|
|
|
|
|
|
VECTOR2I a( pos - half_len );
|
|
|
|
VECTOR2I b( pos + half_len );
|
|
|
|
VECTOR2I normal = b - a;
|
|
|
|
normal.Resize( half_width );
|
|
|
|
RotatePoint( normal, ANGLE_90 );
|
|
|
|
|
|
|
|
addAnchor( a + normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( a - normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( b + normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( b - normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( pos + normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( pos - normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
|
|
|
|
RotatePoint( normal, -ANGLE_90 );
|
|
|
|
|
|
|
|
addAnchor( a - normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( b + normal, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
break;
|
|
|
|
}
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2023-06-02 09:10:48 +00:00
|
|
|
case PAD_SHAPE::RECTANGLE:
|
2022-10-10 17:10:15 +00:00
|
|
|
case PAD_SHAPE::TRAPEZOID:
|
|
|
|
case PAD_SHAPE::ROUNDRECT:
|
|
|
|
case PAD_SHAPE::CHAMFERED_RECT:
|
|
|
|
{
|
|
|
|
VECTOR2I half_size( aPad->GetSize() / 2 );
|
|
|
|
VECTOR2I trap_delta( 0, 0 );
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
if( aPad->GetShape() == PAD_SHAPE::TRAPEZOID )
|
|
|
|
trap_delta = aPad->GetDelta() / 2;
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
SHAPE_LINE_CHAIN corners;
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
|
|
|
|
corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
|
|
|
|
corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
|
|
|
|
corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
|
|
|
|
corners.SetClosed( true );
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
corners.Rotate( aPad->GetOrientation() );
|
|
|
|
corners.Move( aPad->ShapePos() );
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
for( size_t ii = 0; ii < corners.GetSegmentCount(); ++ii )
|
2020-09-29 03:59:44 +00:00
|
|
|
{
|
2022-10-10 17:10:15 +00:00
|
|
|
const SEG& seg = corners.GetSegment( ii );
|
|
|
|
addAnchor( seg.A, OUTLINE | SNAPPABLE, aPad );
|
|
|
|
addAnchor( seg.Center(), OUTLINE | SNAPPABLE, aPad );
|
2020-09-29 01:03:19 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
if( ii == corners.GetSegmentCount() - 1 )
|
|
|
|
addAnchor( seg.B, OUTLINE | SNAPPABLE, aPad );
|
2020-09-29 03:59:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
const std::shared_ptr<SHAPE_POLY_SET>& outline = aPad->GetEffectivePolygon();
|
2020-09-29 03:59:44 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
if( !outline->IsEmpty() )
|
|
|
|
{
|
|
|
|
for( const VECTOR2I& pt : outline->Outline( 0 ).CPoints() )
|
|
|
|
addAnchor( pt, OUTLINE | SNAPPABLE, aPad );
|
2020-09-29 03:59:44 +00:00
|
|
|
}
|
2020-09-29 01:03:19 +00:00
|
|
|
|
2022-10-10 17:10:15 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-09-29 01:03:19 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
auto handleShape =
|
|
|
|
[&]( PCB_SHAPE* shape )
|
|
|
|
{
|
|
|
|
VECTOR2I start = shape->GetStart();
|
|
|
|
VECTOR2I end = shape->GetEnd();
|
|
|
|
|
|
|
|
switch( shape->GetShape() )
|
|
|
|
{
|
|
|
|
case SHAPE_T::CIRCLE:
|
|
|
|
{
|
|
|
|
int r = ( start - end ).EuclideanNorm();
|
|
|
|
|
|
|
|
addAnchor( start, ORIGIN | SNAPPABLE, shape );
|
|
|
|
addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, shape );
|
|
|
|
addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, shape );
|
|
|
|
addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, shape );
|
|
|
|
addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, shape );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHAPE_T::ARC:
|
|
|
|
addAnchor( shape->GetStart(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( shape->GetEnd(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( shape->GetArcMid(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( shape->GetCenter(), ORIGIN | SNAPPABLE, shape );
|
|
|
|
break;
|
|
|
|
|
2023-07-24 16:07:56 +00:00
|
|
|
case SHAPE_T::RECTANGLE:
|
2022-11-08 16:26:42 +00:00
|
|
|
{
|
|
|
|
VECTOR2I point2( end.x, start.y );
|
|
|
|
VECTOR2I point3( start.x, end.y );
|
|
|
|
SEG first( start, point2 );
|
|
|
|
SEG second( point2, end );
|
|
|
|
SEG third( end, point3 );
|
|
|
|
SEG fourth( point3, start );
|
|
|
|
|
|
|
|
addAnchor( first.A, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( first.Center(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( second.A, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( second.Center(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( third.A, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( third.Center(), CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( fourth.A, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( fourth.Center(), CORNER | SNAPPABLE, shape );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHAPE_T::SEGMENT:
|
|
|
|
addAnchor( start, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( end, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( shape->GetCenter(), CORNER | SNAPPABLE, shape );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SHAPE_T::POLY:
|
|
|
|
{
|
|
|
|
SHAPE_LINE_CHAIN lc;
|
|
|
|
lc.SetClosed( true );
|
|
|
|
std::vector<VECTOR2I> poly;
|
|
|
|
shape->DupPolyPointsList( poly );
|
|
|
|
|
|
|
|
for( const VECTOR2I& p : poly )
|
|
|
|
{
|
|
|
|
addAnchor( p, CORNER | SNAPPABLE, shape );
|
|
|
|
lc.Append( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHAPE_T::BEZIER:
|
|
|
|
addAnchor( start, CORNER | SNAPPABLE, shape );
|
|
|
|
addAnchor( end, CORNER | SNAPPABLE, shape );
|
|
|
|
KI_FALLTHROUGH;
|
|
|
|
|
|
|
|
default:
|
|
|
|
addAnchor( shape->GetPosition(), ORIGIN | SNAPPABLE, shape );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
switch( aItem->Type() )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2020-11-13 12:21:02 +00:00
|
|
|
case PCB_FOOTPRINT_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
|
2015-02-18 16:53:46 +00:00
|
|
|
|
2020-11-13 12:21:02 +00:00
|
|
|
for( PAD* pad : footprint->Pads() )
|
2017-07-27 09:13:25 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom )
|
|
|
|
{
|
|
|
|
if( aSelectionFilter && !aSelectionFilter->pads )
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
|
|
|
|
continue;
|
|
|
|
}
|
2022-02-23 23:47:55 +00:00
|
|
|
|
|
|
|
if( !view->IsVisible( pad ) || !pad->GetBoundingBox().Contains( aRefPos ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Getting pads from a footprint requires re-checking that the pads are shown
|
|
|
|
bool onActiveLayer = !isHighContrast;
|
|
|
|
bool isLODVisible = false;
|
|
|
|
|
|
|
|
for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
|
2017-07-27 09:13:25 +00:00
|
|
|
{
|
2022-02-23 23:47:55 +00:00
|
|
|
if( !onActiveLayer && activeLayers.count( layer ) )
|
|
|
|
onActiveLayer = true;
|
|
|
|
|
|
|
|
if( !isLODVisible && pad->ViewGetLOD( layer, view ) < view->GetScale() )
|
|
|
|
isLODVisible = true;
|
|
|
|
|
|
|
|
if( onActiveLayer && isLODVisible )
|
|
|
|
{
|
|
|
|
handlePadShape( pad );
|
|
|
|
break;
|
|
|
|
}
|
2017-07-27 09:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->footprints )
|
|
|
|
break;
|
|
|
|
|
2020-11-13 12:21:02 +00:00
|
|
|
// if the cursor is not over a pad, then drag the footprint by its origin
|
2021-02-01 20:02:24 +00:00
|
|
|
VECTOR2I position = footprint->GetPosition();
|
|
|
|
addAnchor( position, ORIGIN | SNAPPABLE, footprint );
|
|
|
|
|
|
|
|
// Add the footprint center point if it is markedly different from the origin
|
2021-02-26 13:49:40 +00:00
|
|
|
VECTOR2I center = footprint->GetBoundingBox( false, false ).Centre();
|
2021-02-01 20:02:24 +00:00
|
|
|
VECTOR2I grid( GetGrid() );
|
|
|
|
|
|
|
|
if( ( center - position ).SquaredEuclideanNorm() > grid.SquaredEuclideanNorm() )
|
2021-02-26 13:49:40 +00:00
|
|
|
addAnchor( center, ORIGIN | SNAPPABLE, footprint );
|
2021-02-01 20:02:24 +00:00
|
|
|
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-11 17:06:44 +00:00
|
|
|
case PCB_PAD_T:
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom )
|
2019-01-29 14:04:12 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aSelectionFilter && !aSelectionFilter->pads )
|
|
|
|
break;
|
2019-01-29 14:04:12 +00:00
|
|
|
}
|
2022-11-08 16:26:42 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_magneticSettings->pads != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
handlePadShape( static_cast<PAD*>( aItem ) );
|
2019-01-29 14:04:12 +00:00
|
|
|
|
2015-03-11 17:06:44 +00:00
|
|
|
break;
|
|
|
|
|
2022-01-30 10:52:52 +00:00
|
|
|
case PCB_TEXTBOX_T:
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom )
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aSelectionFilter && !aSelectionFilter->text )
|
2018-10-05 03:23:05 +00:00
|
|
|
break;
|
2022-11-08 16:26:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !m_magneticSettings->graphics )
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
2022-11-08 16:26:42 +00:00
|
|
|
}
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
handleShape( static_cast<PCB_SHAPE*>( aItem ) );
|
|
|
|
break;
|
2018-10-05 03:23:05 +00:00
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
case PCB_SHAPE_T:
|
|
|
|
if( aFrom )
|
|
|
|
{
|
|
|
|
if( aSelectionFilter && !aSelectionFilter->graphics )
|
2017-10-19 21:14:01 +00:00
|
|
|
break;
|
2022-11-08 16:26:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !m_magneticSettings->graphics )
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-11-08 16:26:42 +00:00
|
|
|
|
|
|
|
handleShape( static_cast<PCB_SHAPE*>( aItem ) );
|
2015-02-18 00:10:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
case PCB_ARC_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom )
|
2019-01-29 14:04:12 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aSelectionFilter && !aSelectionFilter->tracks )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
|
|
|
|
break;
|
2019-01-29 14:04:12 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 16:26:42 +00:00
|
|
|
PCB_TRACK* track = static_cast<PCB_TRACK*>( aItem );
|
|
|
|
|
|
|
|
addAnchor( track->GetStart(), CORNER | SNAPPABLE, track );
|
|
|
|
addAnchor( track->GetEnd(), CORNER | SNAPPABLE, track );
|
|
|
|
addAnchor( track->GetCenter(), 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:
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom )
|
|
|
|
{
|
|
|
|
if( aSelectionFilter && !aSelectionFilter->vias )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_magneticSettings->tracks != MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
addAnchor( aItem->GetPosition(), ORIGIN | CORNER | SNAPPABLE, aItem );
|
2019-01-29 14:04:12 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
case PCB_ZONE_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->zones )
|
|
|
|
break;
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
const SHAPE_POLY_SET* outline = static_cast<const ZONE*>( 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
|
|
|
{
|
2022-10-05 19:01:55 +00:00
|
|
|
addAnchor( *iter, CORNER | SNAPPABLE, aItem );
|
2017-03-07 12:06:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_ALIGNED_T:
|
2020-09-22 02:32:40 +00:00
|
|
|
case PCB_DIM_ORTHOGONAL_T:
|
2018-10-05 03:23:05 +00:00
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
|
|
|
|
break;
|
|
|
|
|
2021-06-11 16:59:28 +00:00
|
|
|
const PCB_DIM_ALIGNED* dim = static_cast<const PCB_DIM_ALIGNED*>( aItem );
|
2020-09-09 03:17:08 +00:00
|
|
|
addAnchor( dim->GetCrossbarStart(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->GetCrossbarEnd(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
|
2018-10-05 03:23:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-17 00:54:58 +00:00
|
|
|
case PCB_DIM_CENTER_T:
|
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
|
|
|
|
break;
|
|
|
|
|
2021-06-11 16:59:28 +00:00
|
|
|
const PCB_DIM_CENTER* dim = static_cast<const PCB_DIM_CENTER*>( aItem );
|
2020-09-17 00:54:58 +00:00
|
|
|
addAnchor( dim->GetStart(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( dim->GetEnd(), CORNER | SNAPPABLE, aItem );
|
|
|
|
|
|
|
|
VECTOR2I start( dim->GetStart() );
|
|
|
|
VECTOR2I radial( dim->GetEnd() - dim->GetStart() );
|
|
|
|
|
|
|
|
for( int i = 0; i < 2; i++ )
|
|
|
|
{
|
2022-01-20 23:08:48 +00:00
|
|
|
RotatePoint( radial, -ANGLE_90 );
|
2020-09-17 00:54:58 +00:00
|
|
|
addAnchor( start + radial, CORNER | SNAPPABLE, aItem );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-07-13 18:46:33 +00:00
|
|
|
case PCB_DIM_RADIAL_T:
|
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
|
|
|
|
break;
|
|
|
|
|
2021-07-13 18:46:33 +00:00
|
|
|
const PCB_DIM_RADIAL* radialDim = static_cast<const PCB_DIM_RADIAL*>( aItem );
|
|
|
|
addAnchor( radialDim->GetStart(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( radialDim->GetEnd(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( radialDim->GetKnee(), CORNER | SNAPPABLE, aItem );
|
2023-03-04 23:15:43 +00:00
|
|
|
addAnchor( radialDim->GetTextPos(), CORNER | SNAPPABLE, aItem );
|
2021-07-13 18:46:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_LEADER_T:
|
|
|
|
{
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->dimensions )
|
|
|
|
break;
|
|
|
|
|
2021-06-11 16:59:28 +00:00
|
|
|
const PCB_DIM_LEADER* leader = static_cast<const PCB_DIM_LEADER*>( aItem );
|
2020-09-12 20:09:40 +00:00
|
|
|
addAnchor( leader->GetStart(), CORNER | SNAPPABLE, aItem );
|
|
|
|
addAnchor( leader->GetEnd(), CORNER | SNAPPABLE, aItem );
|
2023-03-04 23:15:43 +00:00
|
|
|
addAnchor( leader->GetTextPos(), CORNER | SNAPPABLE, aItem );
|
2020-09-12 20:09:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-06-06 15:09:34 +00:00
|
|
|
case PCB_FIELD_T:
|
2015-02-18 00:10:20 +00:00
|
|
|
case PCB_TEXT_T:
|
2022-11-08 16:26:42 +00:00
|
|
|
if( aFrom && aSelectionFilter && !aSelectionFilter->text )
|
|
|
|
break;
|
|
|
|
|
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
|
|
|
|
2021-04-08 22:41:35 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
{
|
|
|
|
const PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem );
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : group->GetItems() )
|
|
|
|
computeAnchors( item, aRefPos, aFrom );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-10-05 03:23:05 +00:00
|
|
|
default:
|
|
|
|
break;
|
2015-02-18 00:10:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-16 23:17:32 +00:00
|
|
|
PCB_GRID_HELPER::ANCHOR* PCB_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags,
|
|
|
|
LSET aMatchLayers )
|
2015-02-18 16:53:46 +00:00
|
|
|
{
|
2020-06-13 12:33:29 +00:00
|
|
|
double minDist = std::numeric_limits<double>::max();
|
2021-07-19 23:56:05 +00:00
|
|
|
ANCHOR* best = nullptr;
|
2015-02-18 00:10:20 +00:00
|
|
|
|
2017-04-20 14:37:36 +00:00
|
|
|
for( ANCHOR& a : m_anchors )
|
|
|
|
{
|
2021-01-16 23:17:32 +00:00
|
|
|
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( a.item );
|
|
|
|
|
2023-08-17 13:25:26 +00:00
|
|
|
if( !m_magneticSettings->allLayers && ( ( aMatchLayers & 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
|
|
|
}
|