mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-21 00:21:25 +00:00
Pcbnew: Snap to graphic/track intersections
Relates-To: https://gitlab.com/kicad/code/kicad/-/issues/2329
This commit is contained in:
parent
6c5c9fe89a
commit
1fb2d7fe26
common/preview_items
libs
pcbnew/tools
qa/tests/libs/kimath/geometry
@ -110,6 +110,20 @@ static void DrawQuadrantPointIcon( GAL& aGal, const VECTOR2I& aPosition, int aSi
|
||||
EDA_ANGLE( 140, EDA_ANGLE_T::DEGREES_T ) );
|
||||
}
|
||||
|
||||
static void DrawIntersectionIcon( GAL& aGal, const VECTOR2I& aPosition, int aSize )
|
||||
{
|
||||
const int nodeRadius = aSize / 8;
|
||||
|
||||
DrawSnapNode( aGal, aPosition, nodeRadius );
|
||||
|
||||
// Slightly squashed X shape
|
||||
VECTOR2I xLeg = VECTOR2I( aSize / 2, aSize / 3 );
|
||||
|
||||
aGal.DrawLine( aPosition - xLeg, aPosition + xLeg );
|
||||
xLeg.y = -xLeg.y;
|
||||
aGal.DrawLine( aPosition - xLeg, aPosition + xLeg );
|
||||
}
|
||||
|
||||
|
||||
void SNAP_INDICATOR::ViewDraw( int, VIEW* aView ) const
|
||||
{
|
||||
@ -155,4 +169,8 @@ void SNAP_INDICATOR::ViewDraw( int, VIEW* aView ) const
|
||||
{
|
||||
DrawQuadrantPointIcon( gal, typeIconPos, size );
|
||||
}
|
||||
else if( m_snapTypes & POINT_TYPE::PT_INTERSECTION )
|
||||
{
|
||||
DrawIntersectionIcon( gal, typeIconPos, size );
|
||||
}
|
||||
}
|
||||
|
47
libs/core/include/core/type_helpers.h
Normal file
47
libs/core/include/core/type_helpers.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief A type that is always false.
|
||||
*
|
||||
* Useful for static_asserts, when 'false' is no good as the
|
||||
* compiler likes to believe that the assert could pass in
|
||||
* some cases:
|
||||
*
|
||||
* if constexpr ( something<T>() )
|
||||
* {
|
||||
* // Say we know that this branch must always be chosen.
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // Even if the above if's are made such this branch is never chosen
|
||||
* // compilers may still complain if this were to use a literal 'false'.
|
||||
* static_assert( always_false<T>::value, "This should never happen" );
|
||||
* }
|
||||
*/
|
||||
template <typename T>
|
||||
struct always_false : std::false_type
|
||||
{
|
||||
};
|
@ -19,6 +19,7 @@ set( KIMATH_SRCS
|
||||
src/geometry/convex_hull.cpp
|
||||
src/geometry/direction_45.cpp
|
||||
src/geometry/geometry_utils.cpp
|
||||
src/geometry/intersection.cpp
|
||||
src/geometry/oval.cpp
|
||||
src/geometry/seg.cpp
|
||||
src/geometry/shape.cpp
|
||||
|
73
libs/kimath/include/geometry/intersection.h
Normal file
73
libs/kimath/include/geometry/intersection.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
class SEG;
|
||||
class CIRCLE;
|
||||
class SHAPE_ARC;
|
||||
class SHAPE_RECT;
|
||||
|
||||
/**
|
||||
* A variant type that can hold any of the supported geometry types
|
||||
* for intersection calculations.
|
||||
*/
|
||||
using INTERSECTABLE_GEOM = std::variant<SEG, CIRCLE, SHAPE_ARC, SHAPE_RECT>;
|
||||
|
||||
/**
|
||||
* A visitor that visits INTERSECTABLE_GEOM variant objects with another
|
||||
* (which is held as state: m_otherGeometry).
|
||||
*
|
||||
* This provides a unified way to intersect any supported geometry with
|
||||
* any other supported geometry.
|
||||
*/
|
||||
struct INTERSECTION_VISITOR
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aOtherGeometry The other geometry to intersect the visited geometry with.
|
||||
* @param aIntersections A vector to store the intersections in. Does not have to
|
||||
* be empty, the visitor will append to it.
|
||||
*/
|
||||
INTERSECTION_VISITOR( const INTERSECTABLE_GEOM& aOtherGeometry,
|
||||
std::vector<VECTOR2I>& aIntersections );
|
||||
|
||||
/*
|
||||
* One of these operator() overloads will be called by std::visit
|
||||
* as needed to visit (i.e. intersect) the geometry with the (stored)
|
||||
* other geometry.
|
||||
*/
|
||||
void operator()( const SEG& aSeg ) const;
|
||||
void operator()( const CIRCLE& aCircle ) const;
|
||||
void operator()( const SHAPE_ARC& aArc ) const;
|
||||
void operator()( const SHAPE_RECT& aArc ) const;
|
||||
|
||||
private:
|
||||
const INTERSECTABLE_GEOM& m_otherGeometry;
|
||||
std::vector<VECTOR2I>& m_intersections;
|
||||
};
|
@ -61,6 +61,10 @@ enum POINT_TYPE
|
||||
* (you may want to infer PT_END from this)
|
||||
*/
|
||||
PT_CORNER = 1 << 4,
|
||||
/**
|
||||
* The point is an intersection of two (or more) items.
|
||||
*/
|
||||
PT_INTERSECTION = 1 << 5,
|
||||
};
|
||||
|
||||
struct TYPED_POINT2I
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <math/vector2d.h> // for VECTOR2I
|
||||
#include <geometry/eda_angle.h>
|
||||
|
||||
class CIRCLE;
|
||||
class SHAPE_LINE_CHAIN;
|
||||
|
||||
class SHAPE_ARC : public SHAPE
|
||||
@ -142,9 +143,18 @@ public:
|
||||
int IntersectLine( const SEG& aSeg, std::vector<VECTOR2I>* aIpsBuffer ) const;
|
||||
|
||||
/**
|
||||
* Find intersection points between this arc and aArc. Ignores arc width.
|
||||
* Find intersection points between this arc and a CIRCLE. Ignores arc width.
|
||||
*
|
||||
* @param aSeg
|
||||
* @param aCircle Circle to intersect against
|
||||
* @param aIpsBuffer Buffer to store the resulting intersection points (if any)
|
||||
* @return Number of intersection points found
|
||||
*/
|
||||
int Intersect( const CIRCLE& aArc, std::vector<VECTOR2I>* aIpsBuffer ) const;
|
||||
|
||||
/**
|
||||
* Find intersection points between this arc and another arc. Ignores arc width.
|
||||
*
|
||||
* @param aArc Arc to intersect against
|
||||
* @param aIpsBuffer Buffer to store the resulting intersection points (if any)
|
||||
* @return Number of intersection points found
|
||||
*/
|
||||
|
@ -76,6 +76,16 @@ public:
|
||||
m_h( aH )
|
||||
{}
|
||||
|
||||
/**
|
||||
* Create by two corners.
|
||||
*/
|
||||
SHAPE_RECT( const VECTOR2I& aP0, const VECTOR2I& aP1 ) :
|
||||
SHAPE( SH_RECT ),
|
||||
m_p0( aP0 ),
|
||||
m_w( aP1.x - aP0.x ),
|
||||
m_h( aP1.y - aP0.y )
|
||||
{}
|
||||
|
||||
SHAPE_RECT( const SHAPE_RECT& aOther ) :
|
||||
SHAPE( SH_RECT ),
|
||||
m_p0( aOther.m_p0 ),
|
||||
|
233
libs/kimath/src/geometry/intersection.cpp
Normal file
233
libs/kimath/src/geometry/intersection.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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 "geometry/intersection.h"
|
||||
|
||||
#include <core/type_helpers.h>
|
||||
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/circle.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
|
||||
/*
|
||||
* Helper functions that dispatch to the correct intersection function
|
||||
* in one of the geometry classes.
|
||||
*/
|
||||
namespace
|
||||
{
|
||||
|
||||
void findIntersections( const SEG& aSegA, const SEG& aSegB, std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
const OPT_VECTOR2I intersection = aSegA.Intersect( aSegB );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const SEG& aSeg, const CIRCLE& aCircle,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
|
||||
|
||||
aIntersections.insert( aIntersections.end(), intersections.begin(), intersections.end() );
|
||||
}
|
||||
|
||||
void findIntersections( const SEG& aSeg, const SHAPE_ARC& aArc,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections;
|
||||
aArc.IntersectLine( aSeg, &intersections );
|
||||
|
||||
// Find only the intersections that are within the segment
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
if( aSeg.Contains( intersection ) )
|
||||
{
|
||||
aIntersections.emplace_back( intersection );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void findIntersections( const CIRCLE& aCircleA, const CIRCLE& aCircleB,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections = aCircleA.Intersect( aCircleB );
|
||||
aIntersections.insert( aIntersections.end(), intersections.begin(), intersections.end() );
|
||||
}
|
||||
|
||||
void findIntersections( const CIRCLE& aCircle, const SHAPE_ARC& aArc,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
aArc.Intersect( aCircle, &aIntersections );
|
||||
}
|
||||
|
||||
void findIntersections( const SHAPE_ARC& aArcA, const SHAPE_ARC& aArcB,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
aArcA.Intersect( aArcB, &aIntersections );
|
||||
}
|
||||
|
||||
|
||||
std::vector<SEG> RectToSegs( const SHAPE_RECT& aRect )
|
||||
{
|
||||
const VECTOR2I corner = aRect.GetPosition();
|
||||
const int w = aRect.GetWidth();
|
||||
const int h = aRect.GetHeight();
|
||||
|
||||
return {
|
||||
SEG( corner, { corner + VECTOR2I( w, 0 ) } ),
|
||||
SEG( { corner + VECTOR2I( w, 0 ) }, { corner + VECTOR2I( w, h ) } ),
|
||||
SEG( { corner + VECTOR2I( w, h ) }, { corner + VECTOR2I( 0, h ) } ),
|
||||
SEG( { corner + VECTOR2I( 0, h ) }, corner ),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
INTERSECTION_VISITOR::INTERSECTION_VISITOR( const INTERSECTABLE_GEOM& aOtherGeometry,
|
||||
std::vector<VECTOR2I>& aIntersections ) :
|
||||
m_otherGeometry( aOtherGeometry ), m_intersections( aIntersections )
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* The operator() functions are the entry points for the visitor.
|
||||
*
|
||||
* Dispatch to the correct function based on the type of the "otherGeometry"
|
||||
* which is held as state. This is also where the order of the parameters is
|
||||
* determined, which avoids having to define a 'reverse' function for each
|
||||
* intersection type.
|
||||
*/
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const SEG& aSeg ) const
|
||||
{
|
||||
// Dispatch to the correct function
|
||||
return std::visit(
|
||||
[&]( const auto& otherGeom )
|
||||
{
|
||||
using OtherGeomType = std::decay_t<decltype( otherGeom )>;
|
||||
|
||||
if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
{
|
||||
// Seg-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aSeg, aRectSeg, m_intersections );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In all other segment comparisons, the SEG is the first argument
|
||||
findIntersections( aSeg, otherGeom, m_intersections );
|
||||
}
|
||||
},
|
||||
m_otherGeometry );
|
||||
}
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const CIRCLE& aCircle ) const
|
||||
{
|
||||
// Dispatch to the correct function
|
||||
return std::visit(
|
||||
[&]( const auto& otherGeom )
|
||||
{
|
||||
using OtherGeomType = std::decay_t<decltype( otherGeom )>;
|
||||
// Dispatch in the correct order
|
||||
if constexpr( std::is_same_v<OtherGeomType, SEG>
|
||||
|| std::is_same_v<OtherGeomType, CIRCLE> )
|
||||
{
|
||||
// Seg-Circle, Circle-Circle
|
||||
findIntersections( otherGeom, aCircle, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_ARC> )
|
||||
{
|
||||
// Circle-Arc
|
||||
findIntersections( aCircle, otherGeom, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
{
|
||||
// Circle-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aCircle, m_intersections );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( always_false<OtherGeomType>::value,
|
||||
"Unhandled other geometry type" );
|
||||
}
|
||||
},
|
||||
m_otherGeometry );
|
||||
}
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const SHAPE_ARC& aArc ) const
|
||||
{
|
||||
// Dispatch to the correct function
|
||||
return std::visit(
|
||||
[&]( const auto& otherGeom )
|
||||
{
|
||||
using OtherGeomType = std::decay_t<decltype( otherGeom )>;
|
||||
// Dispatch in the correct order
|
||||
if constexpr( std::is_same_v<OtherGeomType, SEG>
|
||||
|| std::is_same_v<OtherGeomType, CIRCLE>
|
||||
|| std::is_same_v<OtherGeomType, SHAPE_ARC> )
|
||||
{
|
||||
// Seg-Arc, Circle-Arc, Arc-Arc
|
||||
findIntersections( otherGeom, aArc, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
{
|
||||
// Arc-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aArc, m_intersections );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( always_false<OtherGeomType>::value,
|
||||
"Unhandled other geometry type" );
|
||||
}
|
||||
},
|
||||
m_otherGeometry );
|
||||
};
|
||||
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const SHAPE_RECT& aRect ) const
|
||||
{
|
||||
// Defer to the SEG visitor repeatedly
|
||||
// Note - in some cases, points can be repeated in the intersection list
|
||||
// if that's an issue, both directions of the visitor can be implemented
|
||||
// to take care of that.
|
||||
const std::vector<SEG> segs = RectToSegs( aRect );
|
||||
|
||||
for( const SEG& seg : segs )
|
||||
{
|
||||
( *this )( seg );
|
||||
}
|
||||
};
|
@ -301,7 +301,25 @@ int SHAPE_ARC::IntersectLine( const SEG& aSeg, std::vector<VECTOR2I>* aIpsBuffer
|
||||
|
||||
std::vector<VECTOR2I> intersections = circ.IntersectLine( aSeg );
|
||||
|
||||
size_t originalSize = aIpsBuffer->size();
|
||||
const size_t originalSize = aIpsBuffer->size();
|
||||
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
if( sliceContainsPoint( intersection ) )
|
||||
aIpsBuffer->push_back( intersection );
|
||||
}
|
||||
|
||||
return aIpsBuffer->size() - originalSize;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_ARC::Intersect( const CIRCLE& aCircle, std::vector<VECTOR2I>* aIpsBuffer ) const
|
||||
{
|
||||
CIRCLE thiscirc( GetCenter(), GetRadius() );
|
||||
|
||||
std::vector<VECTOR2I> intersections = thiscirc.Intersect( aCircle );
|
||||
|
||||
const size_t originalSize = aIpsBuffer->size();
|
||||
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
@ -320,7 +338,7 @@ int SHAPE_ARC::Intersect( const SHAPE_ARC& aArc, std::vector<VECTOR2I>* aIpsBuff
|
||||
|
||||
std::vector<VECTOR2I> intersections = thiscirc.Intersect( othercirc );
|
||||
|
||||
size_t originalSize = aIpsBuffer->size();
|
||||
const size_t originalSize = aIpsBuffer->size();
|
||||
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <pcb_track.h>
|
||||
#include <zone.h>
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <geometry/intersection.h>
|
||||
#include <geometry/oval.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
@ -195,7 +196,7 @@ VECTOR2I PCB_GRID_HELPER::AlignToNearestPad( const VECTOR2I& aMousePos, std::deq
|
||||
clearAnchors();
|
||||
|
||||
for( BOARD_ITEM* item : aPads )
|
||||
computeAnchors( item, aMousePos, true );
|
||||
computeAnchors( item, aMousePos, true, nullptr );
|
||||
|
||||
double minDist = std::numeric_limits<double>::max();
|
||||
ANCHOR* nearestOrigin = nullptr;
|
||||
@ -230,8 +231,7 @@ VECTOR2I PCB_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos,
|
||||
{
|
||||
clearAnchors();
|
||||
|
||||
for( BOARD_ITEM* item : aItems )
|
||||
computeAnchors( item, aMousePos, true, aSelectionFilter );
|
||||
computeAnchors( aItems, aMousePos, true, aSelectionFilter );
|
||||
|
||||
double worldScale = m_toolMgr->GetView()->GetGAL()->GetWorldScale();
|
||||
double lineSnapMinCornerDistance = 50.0 / worldScale;
|
||||
@ -315,8 +315,8 @@ VECTOR2I PCB_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& a
|
||||
|
||||
clearAnchors();
|
||||
|
||||
for( BOARD_ITEM* item : queryVisible( bb, aSkip ) )
|
||||
computeAnchors( item, aOrigin );
|
||||
const std::vector<BOARD_ITEM*> visibleItems = queryVisible( bb, aSkip );
|
||||
computeAnchors( visibleItems, aOrigin, false, nullptr );
|
||||
|
||||
ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayers );
|
||||
VECTOR2I nearestGrid = Align( aOrigin, aGrid );
|
||||
@ -480,8 +480,8 @@ VECTOR2D PCB_GRID_HELPER::GetGridSize( GRID_HELPER_GRIDS aGrid ) const
|
||||
}
|
||||
|
||||
|
||||
std::set<BOARD_ITEM*> PCB_GRID_HELPER::queryVisible( const BOX2I& aArea,
|
||||
const std::vector<BOARD_ITEM*>& aSkip ) const
|
||||
std::vector<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;
|
||||
@ -538,7 +538,127 @@ std::set<BOARD_ITEM*> PCB_GRID_HELPER::queryVisible( const BOX2I& aArea,
|
||||
for( BOARD_ITEM* item : aSkip )
|
||||
skipItem( item );
|
||||
|
||||
return items;
|
||||
return {items.begin(), items.end()};
|
||||
}
|
||||
|
||||
|
||||
struct PCB_INTERSECTABLE
|
||||
{
|
||||
BOARD_ITEM* Item;
|
||||
INTERSECTABLE_GEOM Geometry;
|
||||
|
||||
// Clang wants this constructor
|
||||
PCB_INTERSECTABLE( BOARD_ITEM* aItem, INTERSECTABLE_GEOM aSeg ) :
|
||||
Item( aItem ), Geometry( std::move( aSeg ) )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void PCB_GRID_HELPER::computeAnchors( const std::vector<BOARD_ITEM*>& aItems,
|
||||
const VECTOR2I& aRefPos, bool aFrom,
|
||||
const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter )
|
||||
{
|
||||
std::vector<PCB_INTERSECTABLE> intersectables;
|
||||
|
||||
// This c/should come from a more granular snap filter
|
||||
const bool computeIntersections = true;
|
||||
const bool excludeGraphics = aSelectionFilter && !aSelectionFilter->graphics;
|
||||
const bool excludeTracks = aSelectionFilter && !aSelectionFilter->tracks;
|
||||
|
||||
for( BOARD_ITEM* item : aItems )
|
||||
{
|
||||
// First, add all the key points of the item itself
|
||||
computeAnchors( item, aRefPos, aFrom, aSelectionFilter );
|
||||
|
||||
// If we are computing intersections, construct the relevant intersectables
|
||||
if( computeIntersections )
|
||||
{
|
||||
if( !excludeGraphics && item->Type() == PCB_SHAPE_T )
|
||||
{
|
||||
PCB_SHAPE& shape = static_cast<PCB_SHAPE&>( *item );
|
||||
|
||||
switch( shape.GetShape() )
|
||||
{
|
||||
case SHAPE_T::SEGMENT:
|
||||
{
|
||||
intersectables.emplace_back( &shape, SEG{ shape.GetStart(), shape.GetEnd() } );
|
||||
break;
|
||||
}
|
||||
case SHAPE_T::CIRCLE:
|
||||
{
|
||||
intersectables.emplace_back( &shape,
|
||||
CIRCLE{ shape.GetCenter(), shape.GetRadius() } );
|
||||
break;
|
||||
}
|
||||
case SHAPE_T::ARC:
|
||||
{
|
||||
intersectables.emplace_back(
|
||||
&shape,
|
||||
SHAPE_ARC{ shape.GetStart(), shape.GetArcMid(), shape.GetEnd(), 0 } );
|
||||
break;
|
||||
}
|
||||
case SHAPE_T::RECTANGLE:
|
||||
{
|
||||
intersectables.emplace_back( &shape,
|
||||
SHAPE_RECT{ shape.GetStart(), shape.GetEnd() } );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Ignore other shapes
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( !excludeTracks )
|
||||
{
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_TRACE_T:
|
||||
{
|
||||
PCB_TRACK& track = static_cast<PCB_TRACK&>( *item );
|
||||
|
||||
intersectables.emplace_back( &track, SEG{ track.GetStart(), track.GetEnd() } );
|
||||
break;
|
||||
}
|
||||
case PCB_ARC_T:
|
||||
{
|
||||
PCB_ARC& arc = static_cast<PCB_ARC&>( *item );
|
||||
|
||||
intersectables.emplace_back(
|
||||
&arc, SHAPE_ARC{ arc.GetStart(), arc.GetMid(), arc.GetEnd(), 0 } );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Ignore other items
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, add all the intersections between the items
|
||||
// This is obviously quadratic, so performance may be a concern for large selections
|
||||
// But, so far up to ~20k comparisons seems not to be an issue with run times in the ms range
|
||||
|
||||
for( size_t ii = 0; ii < intersectables.size(); ++ii )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections;
|
||||
const PCB_INTERSECTABLE& intersectableA = intersectables[ii];
|
||||
|
||||
const INTERSECTION_VISITOR visitor{ intersectableA.Geometry, intersections };
|
||||
|
||||
for( size_t jj = ii + 1; jj < intersectables.size(); ++jj )
|
||||
{
|
||||
const PCB_INTERSECTABLE& intersectableB = intersectables[jj];
|
||||
std::visit( visitor, intersectableB.Geometry );
|
||||
}
|
||||
|
||||
// For each intersection, add an intersection snap anchor
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
addAnchor( intersection, SNAPPABLE, intersectableA.Item, POINT_TYPE::PT_INTERSECTION );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1045,7 +1165,7 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
|
||||
for( BOARD_ITEM* item : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
|
||||
{
|
||||
if( checkVisibility( item ) )
|
||||
computeAnchors( item, aRefPos, aFrom );
|
||||
computeAnchors( item, aRefPos, aFrom, nullptr );
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -95,11 +95,20 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
std::set<BOARD_ITEM*> queryVisible( const BOX2I& aArea,
|
||||
const std::vector<BOARD_ITEM*>& aSkip ) const;
|
||||
std::vector<BOARD_ITEM*> queryVisible( const BOX2I& aArea,
|
||||
const std::vector<BOARD_ITEM*>& aSkip ) const;
|
||||
|
||||
ANCHOR* nearestAnchor( const VECTOR2I& aPos, int aFlags, LSET aMatchLayers );
|
||||
|
||||
|
||||
/**
|
||||
* computeAnchors inserts the local anchor points in to the grid helper for the specified
|
||||
* container of board items, including points implied by intersections or other relationships
|
||||
* between the items.
|
||||
*/
|
||||
void computeAnchors( const std::vector<BOARD_ITEM*>& aItems, const VECTOR2I& aRefPos,
|
||||
bool aFrom, const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter );
|
||||
|
||||
/**
|
||||
* computeAnchors inserts the local anchor points in to the grid helper for the specified
|
||||
* board item, given the reference point and the direction of use for the point.
|
||||
@ -108,8 +117,8 @@ private:
|
||||
* @param aRefPos The point for which to compute the anchors (if used by the component)
|
||||
* @param aFrom Is this for an anchor that is designating a source point (aFrom=true) or not
|
||||
*/
|
||||
void computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom = false,
|
||||
const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter = nullptr );
|
||||
void computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos, bool aFrom,
|
||||
const PCB_SELECTION_FILTER_OPTIONS* aSelectionFilter );
|
||||
|
||||
private:
|
||||
MAGNETIC_SETTINGS* m_magneticSettings;
|
||||
|
@ -26,6 +26,7 @@ std::string toString( const POINT_TYPE& aType )
|
||||
case PT_MID: return "PT_MID";
|
||||
case PT_QUADRANT: return "PT_QUADRANT";
|
||||
case PT_CORNER: return "PT_CORNER";
|
||||
case PT_INTERSECTION: return "PT_INTERSECTION";
|
||||
default: return "Unknown POINT_TYPE: " + std::to_string( (int) aType );
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user