mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-21 00:21:25 +00:00
Snapping: Add construction geometry snapping
This is a pretty major rework of the snapping system. The GRID_HELPERs now have a separate CONSTRUCTION_MANAGER which handles some of the state involving "construction geometry". This is fed with 'extended' geometry (e.g. "infinite" lines from segments) for use in generating things like intersection points. It also handles adding this geoemtry to a GAL view item (CONSTRUCTION_GEOM) for display to the user. The process is: * A TOOL creates a GRID_HELPER * Optionally, it pre-loads a "persistent" batch of construction geometry (e.g. for an item's original position) * The grid helper finds useful snap 'anchors' as before, including those involving the construction items. * Other items on the board can be 'activated' by snapping to one of their main points. Then, if it has construction geometry, it will be added to the display. At most 2 items of this kind of geometry are shown, plus the original item, to reduce avoid too much clutter. The dashed snap lines state machine is also handled in the CONSTRUCTION_MANAGER and displayed in the CONSTRUCTION_GEOM item.
This commit is contained in:
parent
72e48aed52
commit
b2be0d39bd
common
eeschema/tools
include
libs/kimath
pcbnew/tools
qa/tests/libs/kimath
@ -457,7 +457,9 @@ set( COMMON_PREVIEW_ITEMS_SRCS
|
||||
preview_items/arc_assistant.cpp
|
||||
preview_items/arc_geom_manager.cpp
|
||||
preview_items/centreline_rect_item.cpp
|
||||
preview_items/construction_geom.cpp
|
||||
preview_items/draw_context.cpp
|
||||
preview_items/item_drawing_utils.cpp
|
||||
preview_items/polygon_geom_manager.cpp
|
||||
preview_items/polygon_item.cpp
|
||||
preview_items/preview_utils.cpp
|
||||
@ -603,7 +605,7 @@ set( COMMON_SRCS
|
||||
tool/action_toolbar.cpp
|
||||
tool/actions.cpp
|
||||
tool/common_control.cpp
|
||||
tool/library_editor_control.cpp
|
||||
tool/construction_manager.cpp
|
||||
tool/common_tools.cpp
|
||||
tool/conditional_menu.cpp
|
||||
tool/edit_constraints.cpp
|
||||
@ -612,6 +614,7 @@ set( COMMON_SRCS
|
||||
tool/embed_tool.cpp
|
||||
tool/grid_helper.cpp
|
||||
tool/grid_menu.cpp
|
||||
tool/library_editor_control.cpp
|
||||
tool/picker_tool.cpp
|
||||
tool/properties_tool.cpp
|
||||
tool/selection.cpp
|
||||
|
161
common/preview_items/construction_geom.cpp
Normal file
161
common/preview_items/construction_geom.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 "preview_items/construction_geom.h"
|
||||
|
||||
#include <layer_ids.h>
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <geometry/shape_utils.h>
|
||||
#include <preview_items/item_drawing_utils.h>
|
||||
#include <view/view.h>
|
||||
|
||||
using namespace KIGFX;
|
||||
|
||||
|
||||
CONSTRUCTION_GEOM::CONSTRUCTION_GEOM() :
|
||||
EDA_ITEM( nullptr, NOT_USED ), // Never added to a BOARD/SCHEMATIC so it needs no type
|
||||
m_color( COLOR4D::WHITE ), m_persistentColor( COLOR4D::WHITE )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CONSTRUCTION_GEOM::AddDrawable( const DRAWABLE& aItem, bool aPersistent )
|
||||
{
|
||||
m_drawables.push_back( { aItem, aPersistent } );
|
||||
}
|
||||
|
||||
void CONSTRUCTION_GEOM::ClearDrawables()
|
||||
{
|
||||
m_drawables.clear();
|
||||
}
|
||||
|
||||
const BOX2I CONSTRUCTION_GEOM::ViewBBox() const
|
||||
{
|
||||
// We could be a bit more circumspect here, but much of the time the
|
||||
// enxtended lines go across the whole screen anyway
|
||||
BOX2I bbox;
|
||||
bbox.SetMaximum();
|
||||
return bbox;
|
||||
}
|
||||
|
||||
void CONSTRUCTION_GEOM::ViewDraw( int aLayer, VIEW* aView ) const
|
||||
{
|
||||
GAL& gal = *aView->GetGAL();
|
||||
|
||||
gal.SetIsFill( false );
|
||||
gal.SetIsStroke( true );
|
||||
gal.SetLineWidth( 1 );
|
||||
|
||||
BOX2D viewportD = aView->GetViewport();
|
||||
BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
|
||||
|
||||
const bool haveSnapLine = m_snapLine && m_snapLine->Length() != 0;
|
||||
|
||||
// Avoid fighting with the snap line
|
||||
const auto drawLineIfNotAlsoSnapLine = [&]( const SEG& aLine )
|
||||
{
|
||||
if( !haveSnapLine || !aLine.ApproxCollinear( *m_snapLine, 1 ) )
|
||||
{
|
||||
gal.DrawLine( aLine.A, aLine.B );
|
||||
}
|
||||
};
|
||||
|
||||
// Draw all the items
|
||||
for( const DRAWABLE_INFO& drawable : m_drawables )
|
||||
{
|
||||
gal.SetStrokeColor( drawable.IsPersistent ? m_persistentColor : m_color );
|
||||
|
||||
std::visit(
|
||||
[&]( const auto& visited )
|
||||
{
|
||||
using ItemType = std::decay_t<decltype( visited )>;
|
||||
|
||||
if constexpr( std::is_same_v<ItemType, LINE> )
|
||||
{
|
||||
// Extend the segment to the viewport boundary
|
||||
std::optional<SEG> segToBoundary =
|
||||
KIGEOM::ClipLineToBox( visited, viewport );
|
||||
|
||||
if( segToBoundary )
|
||||
{
|
||||
drawLineIfNotAlsoSnapLine( *segToBoundary );
|
||||
}
|
||||
}
|
||||
else if constexpr( std::is_same_v<ItemType, HALF_LINE> )
|
||||
{
|
||||
// Extend the ray to the viewport boundary
|
||||
std::optional<SEG> segToBoundary =
|
||||
KIGEOM::ClipHalfLineToBox( visited, viewport );
|
||||
|
||||
if( segToBoundary )
|
||||
{
|
||||
drawLineIfNotAlsoSnapLine( *segToBoundary );
|
||||
}
|
||||
}
|
||||
else if constexpr( std::is_same_v<ItemType, SEG> )
|
||||
{
|
||||
drawLineIfNotAlsoSnapLine( visited );
|
||||
}
|
||||
else if constexpr( std::is_same_v<ItemType, CIRCLE> )
|
||||
{
|
||||
gal.DrawCircle( visited.Center, visited.Radius );
|
||||
}
|
||||
else if constexpr( std::is_same_v<ItemType, SHAPE_ARC> )
|
||||
{
|
||||
gal.DrawArc( visited.GetCenter(), visited.GetRadius(),
|
||||
visited.GetStartAngle(), visited.GetCentralAngle() );
|
||||
}
|
||||
else if constexpr( std::is_same_v<ItemType, VECTOR2I> )
|
||||
{
|
||||
KIGFX::DrawCross( gal, visited, aView->ToWorld( 16 ) );
|
||||
}
|
||||
},
|
||||
drawable.Item );
|
||||
}
|
||||
|
||||
if( haveSnapLine )
|
||||
{
|
||||
gal.SetStrokeColor( m_persistentColor );
|
||||
|
||||
const int dashSizeBasis = aView->ToWorld( 12 );
|
||||
const int snapOriginMarkerSize = aView->ToWorld( 16 );
|
||||
// Avoid clash with the snap marker if very close
|
||||
const int omitStartMarkerIfWithinLength = aView->ToWorld( 8 );
|
||||
|
||||
// The line itself
|
||||
KIGFX::DrawDashedLine( gal, *m_snapLine, dashSizeBasis );
|
||||
|
||||
// The line origin marker if the line is long enough
|
||||
if( m_snapLine->A.Distance( m_snapLine->B ) > omitStartMarkerIfWithinLength )
|
||||
{
|
||||
KIGFX::DrawCross( gal, m_snapLine->A, snapOriginMarkerSize );
|
||||
gal.DrawCircle( m_snapLine->A, snapOriginMarkerSize / 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CONSTRUCTION_GEOM::ViewGetLayers( int aLayers[], int& aCount ) const
|
||||
{
|
||||
aLayers[0] = LAYER_GP_OVERLAY;
|
||||
aCount = 1;
|
||||
}
|
76
common/preview_items/item_drawing_utils.cpp
Normal file
76
common/preview_items/item_drawing_utils.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "preview_items/item_drawing_utils.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <geometry/seg.h>
|
||||
|
||||
using namespace KIGFX;
|
||||
|
||||
void KIGFX::DrawCross( GAL& aGal, const VECTOR2I& aPosition, int aSize )
|
||||
{
|
||||
const int size = aSize / 2;
|
||||
aGal.DrawLine( aPosition - VECTOR2I( size, 0 ), aPosition + VECTOR2I( size, 0 ) );
|
||||
aGal.DrawLine( aPosition - VECTOR2I( 0, size ), aPosition + VECTOR2I( 0, size ) );
|
||||
}
|
||||
|
||||
|
||||
void KIGFX::DrawDashedLine( GAL& aGal, const SEG& aSeg, double aDashSize )
|
||||
{
|
||||
const std::array<double, 2> strokes = { aDashSize, aDashSize / 2 };
|
||||
const double dashCycleLen = strokes[0] + strokes[1];
|
||||
// The dash cycle length must be at least 1 pixel.
|
||||
wxASSERT( dashCycleLen * aGal.GetWorldScale() > 1 );
|
||||
|
||||
const BOX2I clip = BOX2I::ByCorners( aSeg.A, aSeg.B );
|
||||
|
||||
const double theta = atan2( aSeg.B.y - aSeg.A.y, aSeg.B.x - aSeg.A.x );
|
||||
|
||||
const VECTOR2D cycleVec{
|
||||
dashCycleLen * cos( theta ),
|
||||
dashCycleLen * sin( theta ),
|
||||
};
|
||||
const VECTOR2D dashVec{
|
||||
strokes[0] * cos( theta ),
|
||||
strokes[0] * sin( theta ),
|
||||
};
|
||||
|
||||
unsigned cyclei = 0;
|
||||
while( true )
|
||||
{
|
||||
const VECTOR2D dashStart = aSeg.A + cycleVec * cyclei;
|
||||
const VECTOR2D dashEnd = dashStart + dashVec;
|
||||
|
||||
// Drawing each segment can be done rounded to ints.
|
||||
SEG dashSeg{ KiROUND( dashStart ), KiROUND( dashEnd ) };
|
||||
|
||||
if( ClipLine( &clip, dashSeg.A.x, dashSeg.A.y, dashSeg.B.x, dashSeg.B.y ) )
|
||||
break;
|
||||
|
||||
aGal.DrawLine( dashSeg.A, dashSeg.B );
|
||||
++cyclei;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 20204 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* 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
|
||||
@ -21,7 +21,7 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <preview_items/snap_indicator.h>
|
||||
#include "preview_items/snap_indicator.h"
|
||||
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
|
||||
@ -124,6 +124,16 @@ static void DrawIntersectionIcon( GAL& aGal, const VECTOR2I& aPosition, int aSiz
|
||||
aGal.DrawLine( aPosition - xLeg, aPosition + xLeg );
|
||||
}
|
||||
|
||||
static void DrawOnElementIcon( GAL& aGal, const VECTOR2I& aPosition, int aSize )
|
||||
{
|
||||
const int nodeRadius = aSize / 8;
|
||||
|
||||
// A bit like midpoint by off to one side
|
||||
DrawSnapNode( aGal, aPosition + VECTOR2I( aSize / 4, 0 ), nodeRadius );
|
||||
|
||||
aGal.DrawLine( aPosition - VECTOR2I( aSize / 2, 0 ), aPosition + VECTOR2I( aSize / 2, 0 ) );
|
||||
}
|
||||
|
||||
|
||||
void SNAP_INDICATOR::ViewDraw( int, VIEW* aView ) const
|
||||
{
|
||||
@ -134,19 +144,9 @@ void SNAP_INDICATOR::ViewDraw( int, VIEW* aView ) const
|
||||
|
||||
gal.SetFillColor( m_color );
|
||||
|
||||
const auto scaleSize = [&]( double aSize ) -> int
|
||||
{
|
||||
return aView->ToWorld( aSize );
|
||||
};
|
||||
|
||||
const auto scaleVec = [&]( const VECTOR2I& aVec ) -> VECTOR2I
|
||||
{
|
||||
return aView->ToWorld( aVec, false );
|
||||
};
|
||||
|
||||
// Put the icon near the x-line, so it doesn't overlap with the ruler helpers
|
||||
const VECTOR2I typeIconPos = m_position + scaleVec( { 24, 10 } );
|
||||
const int size = scaleSize( 16 );
|
||||
const VECTOR2I typeIconPos = m_position + aView->ToWorld( { 24, 10 }, false );
|
||||
const int size = aView->ToWorld( 16 );
|
||||
|
||||
// For now, choose the first type that is set
|
||||
if( m_snapTypes & POINT_TYPE::PT_CORNER )
|
||||
@ -173,4 +173,8 @@ void SNAP_INDICATOR::ViewDraw( int, VIEW* aView ) const
|
||||
{
|
||||
DrawIntersectionIcon( gal, typeIconPos, size );
|
||||
}
|
||||
else if( m_snapTypes & POINT_TYPE::PT_ON_ELEMENT )
|
||||
{
|
||||
DrawOnElementIcon( gal, typeIconPos, size );
|
||||
}
|
||||
}
|
||||
|
300
common/tool/construction_manager.cpp
Normal file
300
common/tool/construction_manager.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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 <tool/construction_manager.h>
|
||||
|
||||
|
||||
CONSTRUCTION_MANAGER::CONSTRUCTION_MANAGER( KIGFX::CONSTRUCTION_GEOM& aHelper ) :
|
||||
m_constructionGeomPreview( aHelper )
|
||||
{
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::updateView()
|
||||
{
|
||||
if( m_updateCallback )
|
||||
{
|
||||
bool showAnything = m_persistentConstructionBatch || !m_temporaryConstructionBatches.empty()
|
||||
|| ( m_snapLineOrigin && m_snapLineEnd );
|
||||
|
||||
m_updateCallback( showAnything );
|
||||
}
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::AddConstructionItems( CONSTRUCTION_ITEM_BATCH aBatch,
|
||||
bool aIsPersistent )
|
||||
{
|
||||
if( aIsPersistent )
|
||||
{
|
||||
// We only keep one previous persistent batch for the moment
|
||||
m_persistentConstructionBatch = std::move( aBatch );
|
||||
}
|
||||
else
|
||||
{
|
||||
bool anyNewItems = false;
|
||||
for( CONSTRUCTION_ITEM& item : aBatch )
|
||||
{
|
||||
if( m_involvedItems.count( item.Item ) == 0 )
|
||||
{
|
||||
anyNewItems = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no new items involved, don't bother adding the batch
|
||||
if( !anyNewItems )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We only keep up to one previous temporary batch and the current one
|
||||
// we could make this a setting if we want to keep more, but it gets cluttered
|
||||
const int maxTempItems = 2;
|
||||
|
||||
while( m_temporaryConstructionBatches.size() >= maxTempItems )
|
||||
{
|
||||
m_temporaryConstructionBatches.pop_front();
|
||||
}
|
||||
|
||||
m_temporaryConstructionBatches.emplace_back( std::move( aBatch ) );
|
||||
}
|
||||
|
||||
// Refresh what items are drawn
|
||||
|
||||
m_constructionGeomPreview.ClearDrawables();
|
||||
m_involvedItems.clear();
|
||||
|
||||
const auto addBatchItems = [&]( const CONSTRUCTION_ITEM_BATCH& aBatchToAdd, bool aPersistent )
|
||||
{
|
||||
for( const CONSTRUCTION_ITEM& item : aBatchToAdd )
|
||||
{
|
||||
// Only show the item if it's not already involved
|
||||
// (avoid double-drawing the same item)
|
||||
if( m_involvedItems.count( item.Item ) == 0 )
|
||||
{
|
||||
m_involvedItems.insert( item.Item );
|
||||
|
||||
for( const KIGFX::CONSTRUCTION_GEOM::DRAWABLE& construction : item.Constructions )
|
||||
{
|
||||
m_constructionGeomPreview.AddDrawable( construction, aPersistent );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if( m_persistentConstructionBatch )
|
||||
{
|
||||
addBatchItems( *m_persistentConstructionBatch, true );
|
||||
}
|
||||
|
||||
for( const CONSTRUCTION_ITEM_BATCH& batch : m_temporaryConstructionBatches )
|
||||
{
|
||||
addBatchItems( batch, false );
|
||||
}
|
||||
|
||||
updateView();
|
||||
}
|
||||
|
||||
bool CONSTRUCTION_MANAGER::InvolvesAllGivenRealItems( const std::vector<EDA_ITEM*>& aItems ) const
|
||||
{
|
||||
for( EDA_ITEM* item : aItems )
|
||||
{
|
||||
// Null items (i.e. construction items) are always considered involved
|
||||
if( item && m_involvedItems.count( item ) == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::SetSnapLineOrigin( const VECTOR2I& aOrigin )
|
||||
{
|
||||
// Setting the origin clears the snap line as the end point is no longer valid
|
||||
ClearSnapLine();
|
||||
m_snapLineOrigin = aOrigin;
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::SetSnapLineEnd( const OPT_VECTOR2I& aSnapEnd )
|
||||
{
|
||||
if( m_snapLineOrigin && aSnapEnd != m_snapLineEnd )
|
||||
{
|
||||
m_snapLineEnd = aSnapEnd;
|
||||
|
||||
if( m_snapLineEnd )
|
||||
m_constructionGeomPreview.SetSnapLine( SEG{ *m_snapLineOrigin, *m_snapLineEnd } );
|
||||
else
|
||||
m_constructionGeomPreview.ClearSnapLine();
|
||||
|
||||
updateView();
|
||||
}
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::ClearSnapLine()
|
||||
{
|
||||
m_snapLineOrigin.reset();
|
||||
m_snapLineEnd.reset();
|
||||
m_constructionGeomPreview.ClearSnapLine();
|
||||
updateView();
|
||||
}
|
||||
|
||||
void CONSTRUCTION_MANAGER::SetSnappedAnchor( const VECTOR2I& aAnchorPos )
|
||||
{
|
||||
if( m_snapLineOrigin )
|
||||
{
|
||||
if( aAnchorPos.x == m_snapLineOrigin->x || aAnchorPos.y == m_snapLineOrigin->y )
|
||||
{
|
||||
SetSnapLineEnd( aAnchorPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Snapped to something that is not the snap line origin, so
|
||||
// this anchor is now the new snap line origin
|
||||
SetSnapLineOrigin( aAnchorPos );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's no snap line, start one
|
||||
m_snapLineOrigin = aAnchorPos;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH>
|
||||
CONSTRUCTION_MANAGER::GetConstructionItems() const
|
||||
{
|
||||
std::vector<CONSTRUCTION_ITEM_BATCH> batches;
|
||||
|
||||
if( m_persistentConstructionBatch )
|
||||
{
|
||||
batches.push_back( *m_persistentConstructionBatch );
|
||||
}
|
||||
|
||||
for( const CONSTRUCTION_ITEM_BATCH& batch : m_temporaryConstructionBatches )
|
||||
{
|
||||
batches.push_back( batch );
|
||||
}
|
||||
|
||||
if( m_snapLineOrigin )
|
||||
{
|
||||
CONSTRUCTION_ITEM_BATCH batch;
|
||||
|
||||
CONSTRUCTION_ITEM& snapPointItem = batch.emplace_back( CONSTRUCTION_ITEM{
|
||||
SOURCE::FROM_SNAP_LINE,
|
||||
nullptr,
|
||||
{},
|
||||
} );
|
||||
|
||||
snapPointItem.Constructions.push_back(
|
||||
LINE{ *m_snapLineOrigin, *m_snapLineOrigin + VECTOR2I( 100000, 0 ) } );
|
||||
snapPointItem.Constructions.push_back(
|
||||
LINE{ *m_snapLineOrigin, *m_snapLineOrigin + VECTOR2I( 0, 100000 ) } );
|
||||
|
||||
batches.push_back( std::move( batch ) );
|
||||
}
|
||||
|
||||
return batches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the cursor has moved far enough away from the snap line origin to escape snapping
|
||||
* in the X direction.
|
||||
*
|
||||
* This is defined as within aEscapeRange of the snap line origin, and within aLongRangeEscapeAngle
|
||||
* of the vertical line passing through the snap line origin.
|
||||
*/
|
||||
static bool pointHasEscapedSnapLineX( const VECTOR2I& aCursor, const VECTOR2I& aSnapLineOrigin,
|
||||
int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle )
|
||||
{
|
||||
if( std::abs( aCursor.x - aSnapLineOrigin.x ) < aEscapeRange )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EDA_ANGLE angle = EDA_ANGLE( aCursor - aSnapLineOrigin ) + EDA_ANGLE( 90, DEGREES_T );
|
||||
return std::abs( angle.Normalize90() ) > aLongRangeEscapeAngle;
|
||||
}
|
||||
|
||||
/**
|
||||
* As above, but for the Y direction.
|
||||
*/
|
||||
static bool pointHasEscapedSnapLineY( const VECTOR2I& aCursor, const VECTOR2I& aSnapLineOrigin,
|
||||
int aEscapeRange, EDA_ANGLE aLongRangeEscapeAngle )
|
||||
{
|
||||
if( std::abs( aCursor.y - aSnapLineOrigin.y ) < aEscapeRange )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EDA_ANGLE angle = EDA_ANGLE( aCursor - aSnapLineOrigin );
|
||||
return std::abs( angle.Normalize90() ) > aLongRangeEscapeAngle;
|
||||
}
|
||||
|
||||
|
||||
OPT_VECTOR2I CONSTRUCTION_MANAGER::GetNearestSnapLinePoint( const VECTOR2I& aCursor,
|
||||
const VECTOR2I& aNearestGrid,
|
||||
std::optional<int> aDistToNearest,
|
||||
int aSnapRange ) const
|
||||
{
|
||||
// return std::nullopt;
|
||||
if( m_snapLineOrigin )
|
||||
{
|
||||
bool snapLine = false;
|
||||
VECTOR2I bestSnapPoint = aNearestGrid;
|
||||
|
||||
// If there's no snap anchor, or it's too far away, prefer the grid
|
||||
const bool gridBetterThanNearest = !aDistToNearest || *aDistToNearest > aSnapRange;
|
||||
|
||||
// The escape range is how far you go before the snap line is de-activated.
|
||||
// Make this a bit more forgiving than the snap range, as you can easily cancel
|
||||
// deliberately with a mouse move.
|
||||
// These are both a bit arbitrary, and can be adjusted as preferred
|
||||
const int escapeRange = 2 * aSnapRange;
|
||||
const EDA_ANGLE longRangeEscapeAngle( 3, DEGREES_T );
|
||||
|
||||
const bool escapedX = pointHasEscapedSnapLineX( aCursor, *m_snapLineOrigin, escapeRange,
|
||||
longRangeEscapeAngle );
|
||||
const bool escapedY = pointHasEscapedSnapLineY( aCursor, *m_snapLineOrigin, escapeRange,
|
||||
longRangeEscapeAngle );
|
||||
|
||||
/// Allows de-snapping from the line if you are closer to another snap point
|
||||
/// Or if you have moved far enough away from the line
|
||||
if( !escapedX && gridBetterThanNearest )
|
||||
{
|
||||
bestSnapPoint.x = m_snapLineOrigin->x;
|
||||
snapLine = true;
|
||||
}
|
||||
|
||||
if( !escapedY && gridBetterThanNearest )
|
||||
{
|
||||
bestSnapPoint.y = m_snapLineOrigin->y;
|
||||
snapLine = true;
|
||||
}
|
||||
|
||||
if( snapLine )
|
||||
{
|
||||
return bestSnapPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
@ -21,32 +21,78 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "tool/grid_helper.h"
|
||||
|
||||
#include <functional>
|
||||
using namespace std::placeholders;
|
||||
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <gal/painter.h>
|
||||
#include <math/util.h> // for KiROUND
|
||||
#include <math/vector2d.h>
|
||||
#include <render_settings.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <view/view.h>
|
||||
#include <tool/grid_helper.h>
|
||||
#include <settings/app_settings.h>
|
||||
|
||||
|
||||
GRID_HELPER::GRID_HELPER( TOOL_MANAGER* aToolMgr ) :
|
||||
m_toolMgr( aToolMgr )
|
||||
GRID_HELPER::GRID_HELPER( TOOL_MANAGER* aToolMgr, int aConstructionLayer ) :
|
||||
m_toolMgr( aToolMgr ), m_constructionManager( m_constructionGeomPreview )
|
||||
{
|
||||
m_maskTypes = ALL;
|
||||
m_enableSnap = true;
|
||||
m_enableSnapLine = true;
|
||||
m_enableGrid = true;
|
||||
m_snapItem = std::nullopt;
|
||||
|
||||
KIGFX::VIEW* view = m_toolMgr->GetView();
|
||||
KIGFX::RENDER_SETTINGS* settings = view->GetPainter()->GetSettings();
|
||||
|
||||
const KIGFX::COLOR4D constructionColour = settings->GetLayerColor( aConstructionLayer );
|
||||
m_constructionGeomPreview.SetPersistentColor( constructionColour );
|
||||
m_constructionGeomPreview.SetColor( constructionColour.WithAlpha( 0.7 ) );
|
||||
|
||||
view->Add( &m_constructionGeomPreview );
|
||||
view->SetVisible( &m_constructionGeomPreview, false );
|
||||
|
||||
m_constructionManager.SetUpdateCallback(
|
||||
[view, this]( bool aAnythingShown )
|
||||
{
|
||||
const bool currentlyVisible = view->IsVisible( &m_constructionGeomPreview );
|
||||
|
||||
if( currentlyVisible && aAnythingShown )
|
||||
{
|
||||
view->Update( &m_constructionGeomPreview, KIGFX::GEOMETRY );
|
||||
}
|
||||
else
|
||||
{
|
||||
view->SetVisible( &m_constructionGeomPreview, aAnythingShown );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
GRID_HELPER::~GRID_HELPER()
|
||||
{
|
||||
KIGFX::VIEW* view = m_toolMgr->GetView();
|
||||
view->Remove( &m_constructionGeomPreview );
|
||||
}
|
||||
|
||||
|
||||
void GRID_HELPER::showConstructionGeometry( bool aShow )
|
||||
{
|
||||
m_toolMgr->GetView()->SetVisible( &m_constructionGeomPreview, aShow );
|
||||
}
|
||||
|
||||
|
||||
void GRID_HELPER::updateSnapPoint( const TYPED_POINT2I& aPoint )
|
||||
{
|
||||
m_viewSnapPoint.SetPosition( aPoint.m_point );
|
||||
m_viewSnapPoint.SetSnapTypes( aPoint.m_types );
|
||||
|
||||
if( m_toolMgr->GetView()->IsVisible( &m_viewSnapPoint ) )
|
||||
m_toolMgr->GetView()->Update( &m_viewSnapPoint, KIGFX::GEOMETRY );
|
||||
else
|
||||
m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, true );
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
|
||||
EE_GRID_HELPER::EE_GRID_HELPER( TOOL_MANAGER* aToolMgr ) :
|
||||
GRID_HELPER( aToolMgr )
|
||||
GRID_HELPER( aToolMgr, LAYER_SCHEMATIC_ANCHOR )
|
||||
{
|
||||
KIGFX::VIEW* view = m_toolMgr->GetView();
|
||||
|
||||
@ -55,12 +55,6 @@ EE_GRID_HELPER::EE_GRID_HELPER( TOOL_MANAGER* aToolMgr ) :
|
||||
m_viewSnapPoint.SetDrawAtZero( true );
|
||||
view->Add( &m_viewSnapPoint );
|
||||
view->SetVisible( &m_viewSnapPoint, false );
|
||||
|
||||
m_viewSnapLine.SetStyle( KIGFX::ORIGIN_VIEWITEM::DASH_LINE );
|
||||
m_viewSnapLine.SetColor( COLOR4D( 0.33, 0.55, 0.95, 1.0 ) );
|
||||
m_viewSnapLine.SetDrawAtZero( true );
|
||||
view->Add( &m_viewSnapLine );
|
||||
view->SetVisible( &m_viewSnapLine, false );
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +64,6 @@ EE_GRID_HELPER::~EE_GRID_HELPER()
|
||||
|
||||
view->Remove( &m_viewAxis );
|
||||
view->Remove( &m_viewSnapPoint );
|
||||
view->Remove( &m_viewSnapLine );
|
||||
}
|
||||
|
||||
|
||||
@ -167,19 +160,24 @@ VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, GRID_HELPER_GR
|
||||
ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aGrid );
|
||||
VECTOR2I nearestGrid = Align( aOrigin, aGrid );
|
||||
|
||||
if( m_enableSnapLine && m_snapItem && m_skipPoint != VECTOR2I( m_viewSnapLine.GetPosition() ) )
|
||||
showConstructionGeometry( m_enableSnap );
|
||||
|
||||
std::optional<VECTOR2I> snapLineOrigin = getConstructionManager().GetSnapLineOrigin();
|
||||
|
||||
if( m_enableSnapLine && m_snapItem && snapLineOrigin.has_value()
|
||||
&& m_skipPoint != *snapLineOrigin )
|
||||
{
|
||||
if( std::abs( m_viewSnapLine.GetPosition().x - aOrigin.x ) < snapDist.x )
|
||||
if( std::abs( snapLineOrigin->x - aOrigin.x ) < snapDist.x )
|
||||
{
|
||||
pt.x = m_viewSnapLine.GetPosition().x;
|
||||
snapDist.x = std::abs( m_viewSnapLine.GetPosition().x - aOrigin.x );
|
||||
pt.x = snapLineOrigin->x;
|
||||
snapDist.x = std::abs( pt.x - aOrigin.x );
|
||||
snapLineX = true;
|
||||
}
|
||||
|
||||
if( std::abs( m_viewSnapLine.GetPosition().y - aOrigin.y ) < snapDist.y )
|
||||
if( std::abs( snapLineOrigin->y - aOrigin.y ) < snapDist.y )
|
||||
{
|
||||
pt.y = m_viewSnapLine.GetPosition().y;
|
||||
snapDist.y = std::abs( m_viewSnapLine.GetPosition().y - aOrigin.y );
|
||||
pt.y = snapLineOrigin->y;
|
||||
snapDist.y = std::abs( pt.y - aOrigin.y );
|
||||
snapLineY = true;
|
||||
}
|
||||
|
||||
@ -218,7 +216,8 @@ VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, GRID_HELPER_GR
|
||||
snapPoint = true;
|
||||
}
|
||||
|
||||
snapLineX = snapLineY = false;
|
||||
snapLineX = false;
|
||||
snapLineY = false;
|
||||
gridChecked = true;
|
||||
}
|
||||
|
||||
@ -227,20 +226,14 @@ VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, GRID_HELPER_GR
|
||||
|
||||
if( snapLineX || snapLineY )
|
||||
{
|
||||
m_viewSnapLine.SetEndPosition( pt );
|
||||
|
||||
if( m_toolMgr->GetView()->IsVisible( &m_viewSnapLine ) )
|
||||
m_toolMgr->GetView()->Update( &m_viewSnapLine, KIGFX::GEOMETRY );
|
||||
else
|
||||
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, true );
|
||||
getConstructionManager().SetSnapLineEnd( pt );
|
||||
}
|
||||
else if( snapPoint )
|
||||
{
|
||||
m_snapItem = *nearest;
|
||||
m_viewSnapPoint.SetPosition( pt );
|
||||
m_viewSnapLine.SetPosition( pt );
|
||||
|
||||
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, false );
|
||||
getConstructionManager().SetSnapLineOrigin( pt );
|
||||
|
||||
if( m_toolMgr->GetView()->IsVisible( &m_viewSnapPoint ) )
|
||||
m_toolMgr->GetView()->Update( &m_viewSnapPoint, KIGFX::GEOMETRY);
|
||||
@ -249,8 +242,8 @@ VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, GRID_HELPER_GR
|
||||
}
|
||||
else
|
||||
{
|
||||
getConstructionManager().ClearSnapLine();
|
||||
m_toolMgr->GetView()->SetVisible( &m_viewSnapPoint, false );
|
||||
m_toolMgr->GetView()->SetVisible( &m_viewSnapLine, false );
|
||||
}
|
||||
|
||||
return pt;
|
||||
@ -309,7 +302,10 @@ SCH_ITEM* EE_GRID_HELPER::GetSnapped() const
|
||||
if( !m_snapItem )
|
||||
return nullptr;
|
||||
|
||||
return static_cast<SCH_ITEM*>( m_snapItem->item );
|
||||
if( m_snapItem->items.empty() )
|
||||
return nullptr;
|
||||
|
||||
return static_cast<SCH_ITEM*>( m_snapItem->items[0] );
|
||||
}
|
||||
|
||||
|
||||
@ -511,15 +507,20 @@ EE_GRID_HELPER::ANCHOR* EE_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int
|
||||
|
||||
for( ANCHOR& a : m_anchors )
|
||||
{
|
||||
SCH_ITEM* item = static_cast<SCH_ITEM*>( a.item );
|
||||
|
||||
if( ( aFlags & a.flags ) != aFlags )
|
||||
continue;
|
||||
|
||||
if( aGrid == GRID_CONNECTABLE && !item->IsConnectable() )
|
||||
continue;
|
||||
else if( aGrid == GRID_GRAPHICS && item->IsConnectable() )
|
||||
continue;
|
||||
// A "virtual" anchor with no real items associated shouldn't be filtered out
|
||||
if( !a.items.empty() )
|
||||
{
|
||||
// Filter using the first item
|
||||
SCH_ITEM* item = static_cast<SCH_ITEM*>( a.items[0] );
|
||||
|
||||
if( aGrid == GRID_CONNECTABLE && !item->IsConnectable() )
|
||||
continue;
|
||||
else if( aGrid == GRID_GRAPHICS && item->IsConnectable() )
|
||||
continue;
|
||||
}
|
||||
|
||||
double dist = a.Distance( aPos );
|
||||
|
||||
|
84
include/preview_items/construction_geom.h
Normal file
84
include/preview_items/construction_geom.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 <eda_item.h>
|
||||
#include <gal/color4d.h>
|
||||
|
||||
#include <geometry/circle.h>
|
||||
#include <geometry/half_line.h>
|
||||
#include <geometry/line.h>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
|
||||
/**
|
||||
* Shows construction geometry for things like line extensions, arc centers, etc.
|
||||
*/
|
||||
class CONSTRUCTION_GEOM : public EDA_ITEM
|
||||
{
|
||||
public:
|
||||
// Supported items
|
||||
using DRAWABLE = std::variant<SEG, LINE, HALF_LINE, CIRCLE, SHAPE_ARC, VECTOR2I>;
|
||||
|
||||
CONSTRUCTION_GEOM();
|
||||
|
||||
wxString GetClass() const override { return wxT( "CONSTRUCTION_GEOM" ); }
|
||||
|
||||
const BOX2I ViewBBox() const override;
|
||||
|
||||
void ViewDraw( int aLayer, VIEW* aView ) const override;
|
||||
|
||||
void ViewGetLayers( int aLayers[], int& aCount ) const override;
|
||||
|
||||
void SetColor( const COLOR4D& aColor ) { m_color = aColor; }
|
||||
void SetPersistentColor( const COLOR4D& aColor ) { m_persistentColor = aColor; }
|
||||
|
||||
void AddDrawable( const DRAWABLE& aItem, bool aIsPersistent );
|
||||
void ClearDrawables();
|
||||
|
||||
void SetSnapLine( const SEG& aLine ) { m_snapLine = aLine; }
|
||||
void ClearSnapLine() { m_snapLine.reset(); }
|
||||
|
||||
private:
|
||||
COLOR4D m_color;
|
||||
COLOR4D m_persistentColor;
|
||||
struct DRAWABLE_INFO
|
||||
{
|
||||
DRAWABLE Item;
|
||||
bool IsPersistent;
|
||||
};
|
||||
|
||||
// The items to draw
|
||||
std::vector<DRAWABLE_INFO> m_drawables;
|
||||
|
||||
// The snap line to draw
|
||||
std::optional<SEG> m_snapLine;
|
||||
};
|
||||
|
||||
} // namespace KIGFX
|
57
include/preview_items/item_drawing_utils.h
Normal file
57
include/preview_items/item_drawing_utils.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 <gal/graphics_abstraction_layer.h>
|
||||
#include <math/vector2d.h>
|
||||
|
||||
/**
|
||||
* @file item_drawing_utils.h
|
||||
*
|
||||
* Utility functions for drawing compound items (i.e. items that
|
||||
* need more than one GAL Draw call to render) the might be used be more
|
||||
* than one VIEW_ITEM Draw function.
|
||||
*/
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
/**
|
||||
* Draw a cross at a given position.
|
||||
*
|
||||
* @param aGal The graphics abstraction layer to draw with.
|
||||
* @param aPosition The position to draw the cross at.
|
||||
* @param aSize The size of the cross.
|
||||
*/
|
||||
void DrawCross( GAL& aGal, const VECTOR2I& aPosition, int aSize );
|
||||
|
||||
/**
|
||||
* Draw a dashed line.
|
||||
*
|
||||
* @param aGal The graphics abstraction layer to draw with.
|
||||
* @param aSeg The line to draw.
|
||||
* @param aDashSize The size of the dashes.
|
||||
*/
|
||||
void DrawDashedLine( GAL& aGal, const SEG& aSeg, double aDashSize );
|
||||
|
||||
} // namespace KIGFX
|
179
include/tool/construction_manager.h
Normal file
179
include/tool/construction_manager.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* @author Maciej Suminski <maciej.suminski@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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include <preview_items/construction_geom.h>
|
||||
|
||||
class EDA_ITEM;
|
||||
|
||||
/**
|
||||
* A class that mananges "construction" objects and geometry.
|
||||
*
|
||||
* Probably only used by GRID_HELPERs, but it's neater to keep it separate,
|
||||
* as there's quite a bit of state to manage.
|
||||
*/
|
||||
class CONSTRUCTION_MANAGER
|
||||
{
|
||||
public:
|
||||
CONSTRUCTION_MANAGER( KIGFX::CONSTRUCTION_GEOM& aHelper );
|
||||
|
||||
/**
|
||||
* Items to be used for the construction of "virtual" anchors, for example, when snapping to
|
||||
* a point involving an _extension_ of an existing line or arc.
|
||||
*
|
||||
* One item can have multiple construction items (e.g. an arc can have a circle and centre point).
|
||||
*/
|
||||
|
||||
enum class SOURCE
|
||||
{
|
||||
FROM_ITEMS,
|
||||
FROM_SNAP_LINE,
|
||||
};
|
||||
|
||||
struct CONSTRUCTION_ITEM
|
||||
{
|
||||
SOURCE Source;
|
||||
EDA_ITEM* Item;
|
||||
std::vector<KIGFX::CONSTRUCTION_GEOM::DRAWABLE> Constructions;
|
||||
};
|
||||
|
||||
// A single batch of construction items. Once batch contains all the items (and associated
|
||||
// construction geometry) that should be shown for one point of interest.
|
||||
// More than one batch may be shown on the screen at the same time.
|
||||
using CONSTRUCTION_ITEM_BATCH = std::vector<CONSTRUCTION_ITEM>;
|
||||
|
||||
/**
|
||||
* Add a batch of construction items to the helper.
|
||||
*
|
||||
* @param aBatch The batch of construction items to add.
|
||||
* @param aIsPersistent If true, the batch is considered "persistent" and will always be shown
|
||||
* (and it will replace any previous persistent batch).
|
||||
* If false, the batch is temporary and may be pushed out by other batches.
|
||||
*/
|
||||
void AddConstructionItems( CONSTRUCTION_ITEM_BATCH aBatch, bool aIsPersistent );
|
||||
|
||||
/**
|
||||
* Check if all 'real' (non-null = constructed) the items in the batch are in the list of items
|
||||
* currently 'involved' in an active construction.
|
||||
*/
|
||||
bool InvolvesAllGivenRealItems( const std::vector<EDA_ITEM*>& aItems ) const;
|
||||
|
||||
/**
|
||||
* Set the reference-only points - these are points that are not snapped to, but can still
|
||||
* be used for connection to the snap line.
|
||||
*/
|
||||
void SetReferenceOnlyPoints( std::vector<VECTOR2I> aPoints )
|
||||
{
|
||||
m_referenceOnlyPoints = std::move( aPoints );
|
||||
}
|
||||
|
||||
const std::vector<VECTOR2I>& GetReferenceOnlyPoints() const { return m_referenceOnlyPoints; }
|
||||
|
||||
/**
|
||||
* The snap point is a special point that is located at the last point the cursor
|
||||
* snapped to. If it is set, the construction manager may add extra construction
|
||||
* geometry to the helper extending from the snap point origin to the cursor,
|
||||
* which is the 'snap line'.
|
||||
*/
|
||||
void SetSnapLineOrigin( const VECTOR2I& aOrigin );
|
||||
|
||||
/**
|
||||
* Set the end point of the snap line.
|
||||
*
|
||||
* Passing std::nullopt will unset the end point, but keep the origin.
|
||||
*/
|
||||
void SetSnapLineEnd( const OPT_VECTOR2I& aSnapPoint );
|
||||
|
||||
/**
|
||||
* Clear the snap line origin and end points.
|
||||
*/
|
||||
void ClearSnapLine();
|
||||
|
||||
std::optional<VECTOR2I> GetSnapLineOrigin() const { return m_snapLineOrigin; }
|
||||
|
||||
/**
|
||||
* Inform the construction manager that an anchor snap is wanted.
|
||||
*
|
||||
* This will also update the snap line if appropriate.
|
||||
*/
|
||||
void SetSnappedAnchor( const VECTOR2I& aAnchorPos );
|
||||
|
||||
// Get the list of additional geometry items that should be considered
|
||||
std::vector<CONSTRUCTION_ITEM_BATCH> GetConstructionItems() const;
|
||||
|
||||
/**
|
||||
* If the snap line is active, return the best snap point that is closest to the cursor
|
||||
*
|
||||
* If there's no active snap line, return std::nullopt.
|
||||
*
|
||||
* If there's a snap very near, use that otherwise, use the grid point.
|
||||
* With this point, snap to it on an H/V axis.
|
||||
*
|
||||
* Then, if there's a grid point near, snap to it on an H/V axis
|
||||
*
|
||||
* @param aCursor The cursor position
|
||||
* @param aNearestGrid The nearest grid point to the cursor
|
||||
* @param aDistToNearest The distance to the nearest non-grid snap point, if any
|
||||
* @param snapRange The snap range
|
||||
*/
|
||||
OPT_VECTOR2I GetNearestSnapLinePoint( const VECTOR2I& aCursor, const VECTOR2I& aNearestGrid,
|
||||
std::optional<int> aDistToNearest, int snapRange ) const;
|
||||
|
||||
using GFX_UPDATE_CALLBACK = std::function<void( bool )>;
|
||||
/**
|
||||
* Set the callback to call when the construction geometry changes and a view may need updating.
|
||||
*/
|
||||
void SetUpdateCallback( GFX_UPDATE_CALLBACK aCallback ) { m_updateCallback = aCallback; }
|
||||
|
||||
private:
|
||||
void updateView();
|
||||
|
||||
// An (external) construction helper view item, that this manager adds/removes
|
||||
// construction objects to/from.
|
||||
KIGFX::CONSTRUCTION_GEOM& m_constructionGeomPreview;
|
||||
|
||||
// Within one "operation", there is one set of construction items that are
|
||||
// "persistent", and are always shown. Usually the original item and any
|
||||
// extensions.
|
||||
std::optional<CONSTRUCTION_ITEM_BATCH> m_persistentConstructionBatch;
|
||||
|
||||
// Temporary construction items are added and removed as needed
|
||||
std::deque<CONSTRUCTION_ITEM_BATCH> m_temporaryConstructionBatches;
|
||||
|
||||
// Set of all items for which construction geometry has been added
|
||||
std::set<EDA_ITEM*> m_involvedItems;
|
||||
|
||||
std::vector<VECTOR2I> m_referenceOnlyPoints;
|
||||
|
||||
// If a snap point is "active", extra construction geometry is added to the helper
|
||||
// extending from the snap point to the cursor.
|
||||
OPT_VECTOR2I m_snapLineOrigin;
|
||||
OPT_VECTOR2I m_snapLineEnd;
|
||||
|
||||
GFX_UPDATE_CALLBACK m_updateCallback;
|
||||
};
|
@ -29,6 +29,8 @@
|
||||
#include <geometry/point_types.h>
|
||||
#include <math/vector2d.h>
|
||||
#include <preview_items/snap_indicator.h>
|
||||
#include <preview_items/construction_geom.h>
|
||||
#include <tool/construction_manager.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tool/selection.h>
|
||||
#include <origin_viewitem.h>
|
||||
@ -50,7 +52,7 @@ enum GRID_HELPER_GRIDS : int
|
||||
class GRID_HELPER
|
||||
{
|
||||
public:
|
||||
GRID_HELPER( TOOL_MANAGER* aToolMgr );
|
||||
GRID_HELPER( TOOL_MANAGER* aToolMgr, int aConstructionLayer );
|
||||
virtual ~GRID_HELPER();
|
||||
|
||||
VECTOR2I GetGrid() const;
|
||||
@ -127,7 +129,11 @@ public:
|
||||
ORIGIN = 8,
|
||||
VERTICAL = 16,
|
||||
HORIZONTAL = 32,
|
||||
ALL = CORNER | OUTLINE | SNAPPABLE | ORIGIN | VERTICAL | HORIZONTAL
|
||||
// This anchor comes from 'constructed' geometry (e.g. an intersection
|
||||
// with something else), and not from some intrinsic point of an item
|
||||
// (e.g. an endpoint)
|
||||
CONSTRUCTED = 64,
|
||||
ALL = CORNER | OUTLINE | SNAPPABLE | ORIGIN | VERTICAL | HORIZONTAL | CONSTRUCTED
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -143,27 +149,40 @@ protected:
|
||||
* @param aPointTypes The point types that this anchor represents in geometric terms
|
||||
* @param aItem The item to which the anchor belongs
|
||||
*/
|
||||
ANCHOR( const VECTOR2I& aPos, int aFlags, int aPointTypes, EDA_ITEM* aItem ) :
|
||||
pos( aPos ), flags( aFlags ), pointTypes( aPointTypes ), item( aItem )
|
||||
ANCHOR( const VECTOR2I& aPos, int aFlags, int aPointTypes, std::vector<EDA_ITEM*> aItems ) :
|
||||
pos( aPos ), flags( aFlags ), pointTypes( aPointTypes ),
|
||||
items( std::move( aItems ) )
|
||||
{
|
||||
}
|
||||
|
||||
VECTOR2I pos;
|
||||
int flags;
|
||||
int pointTypes;
|
||||
EDA_ITEM* item;
|
||||
// Items that are associated with this anchor (can be more than one, e.g. for an intersection)
|
||||
std::vector<EDA_ITEM*> items;
|
||||
|
||||
double Distance( const VECTOR2I& aP ) const
|
||||
{
|
||||
return VECTOR2D( (double) aP.x - pos.x, (double) aP.y - pos.y ).EuclideanNorm();
|
||||
}
|
||||
|
||||
bool InvolvesItem( const EDA_ITEM& aItem ) const
|
||||
{
|
||||
return std::find( items.begin(), items.end(), &aItem ) != items.end();
|
||||
}
|
||||
};
|
||||
|
||||
void addAnchor( const VECTOR2I& aPos, int aFlags, EDA_ITEM* aItem,
|
||||
int aPointTypes = POINT_TYPE::PT_NONE )
|
||||
{
|
||||
addAnchor( aPos, aFlags, std::vector<EDA_ITEM*>{ aItem }, aPointTypes );
|
||||
}
|
||||
|
||||
void addAnchor( const VECTOR2I& aPos, int aFlags, std::vector<EDA_ITEM*> aItems,
|
||||
int aPointTypes )
|
||||
{
|
||||
if( ( aFlags & m_maskTypes ) == aFlags )
|
||||
m_anchors.emplace_back( ANCHOR( aPos, aFlags, aPointTypes, aItem ) );
|
||||
m_anchors.emplace_back( ANCHOR( aPos, aFlags, aPointTypes, std::move( aItems ) ) );
|
||||
}
|
||||
|
||||
void clearAnchors()
|
||||
@ -181,6 +200,12 @@ protected:
|
||||
const VECTOR2I& aOffset ) const;
|
||||
|
||||
protected:
|
||||
void showConstructionGeometry( bool aShow );
|
||||
|
||||
CONSTRUCTION_MANAGER& getConstructionManager() { return m_constructionManager; }
|
||||
|
||||
void updateSnapPoint( const TYPED_POINT2I& aPoint );
|
||||
|
||||
std::vector<ANCHOR> m_anchors;
|
||||
|
||||
TOOL_MANAGER* m_toolMgr;
|
||||
@ -196,8 +221,17 @@ protected:
|
||||
VECTOR2I m_skipPoint; // When drawing a line, we avoid snapping to the
|
||||
// source point
|
||||
KIGFX::SNAP_INDICATOR m_viewSnapPoint;
|
||||
KIGFX::ORIGIN_VIEWITEM m_viewSnapLine;
|
||||
KIGFX::ORIGIN_VIEWITEM m_viewAxis;
|
||||
|
||||
private:
|
||||
// Construction helper - this is what actually shows construction geometry
|
||||
// (if any) on the canvas.
|
||||
KIGFX::CONSTRUCTION_GEOM m_constructionGeomPreview;
|
||||
|
||||
// Construction manager - this is what manages the construction geometry
|
||||
// and deals with updating the construction helper as well as keeping
|
||||
// track of what geometry is "active" for construction purposes.
|
||||
CONSTRUCTION_MANAGER m_constructionManager;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,19 +19,23 @@ set( KIMATH_SRCS
|
||||
src/geometry/convex_hull.cpp
|
||||
src/geometry/direction_45.cpp
|
||||
src/geometry/geometry_utils.cpp
|
||||
src/geometry/half_line.cpp
|
||||
src/geometry/intersection.cpp
|
||||
src/geometry/line.cpp
|
||||
src/geometry/nearest.cpp
|
||||
src/geometry/oval.cpp
|
||||
src/geometry/seg.cpp
|
||||
src/geometry/shape.cpp
|
||||
src/geometry/shape_arc.cpp
|
||||
src/geometry/shape_collisions.cpp
|
||||
src/geometry/shape_compound.cpp
|
||||
src/geometry/shape_file_io.cpp
|
||||
src/geometry/shape_line_chain.cpp
|
||||
src/geometry/shape_poly_set.cpp
|
||||
src/geometry/shape_rect.cpp
|
||||
src/geometry/shape_compound.cpp
|
||||
src/geometry/shape_segment.cpp
|
||||
src/geometry/vector_utils.cpp
|
||||
src/geometry/shape_utils.cpp
|
||||
src/geometry/vertex_set.cpp
|
||||
|
||||
|
||||
|
@ -38,6 +38,8 @@ public:
|
||||
|
||||
CIRCLE( const CIRCLE& aOther );
|
||||
|
||||
bool operator==( const CIRCLE& aOther ) const = default;
|
||||
|
||||
/**
|
||||
* Construct this circle such that it is tangent to the given segments and passes through the
|
||||
* given point, generating the solution which can be used to fillet both segments.
|
||||
|
95
libs/kimath/include/geometry/half_line.h
Normal file
95
libs/kimath/include/geometry/half_line.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 <optional>
|
||||
|
||||
#include <geometry/seg.h>
|
||||
#include <math/box2.h>
|
||||
|
||||
/*
|
||||
* A geometric half-line of infinite length, starting at a given point and extending infinitely.
|
||||
* A.k.a. a ray.
|
||||
*
|
||||
* In terms of geometric ops, a SEG would probably do in most cases, as it
|
||||
* has the same definition, but a separate class is more explicit and also
|
||||
* allows compile-time reasoning about the meaning of the object through
|
||||
* the type system.
|
||||
*/
|
||||
class HALF_LINE
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a ray from a segment - the ray will start at the segment's A point and
|
||||
* extend infinitely in the direction of the segment, passing through its B point.
|
||||
*/
|
||||
HALF_LINE( const SEG& aSeg ) : m_seg( aSeg ) {}
|
||||
|
||||
HALF_LINE( const VECTOR2I& aStart, const VECTOR2I& aOtherContainedPoint ) :
|
||||
m_seg( aStart, aOtherContainedPoint )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start point of the ray.
|
||||
*/
|
||||
const VECTOR2I& GetStart() const { return m_seg.A; }
|
||||
|
||||
/**
|
||||
* Get one (of the infinite number) of points that the ray passes through.
|
||||
*/
|
||||
const VECTOR2I& GetContainedPoint() const { return m_seg.B; }
|
||||
|
||||
bool Contains( const VECTOR2I& aPoint ) const;
|
||||
|
||||
OPT_VECTOR2I Intersect( const SEG& aSeg ) const;
|
||||
|
||||
OPT_VECTOR2I Intersect( const HALF_LINE& aOther ) const;
|
||||
|
||||
/**
|
||||
* Get the nearest point on the ray to the given point.
|
||||
*
|
||||
* This will be the start point of the ray for half the 2D plane.
|
||||
*/
|
||||
VECTOR2I NearestPoint( const VECTOR2I& aPoint ) const;
|
||||
|
||||
/**
|
||||
* Based on the ray being identically defined. TODO: this is not geoemetrical equality?!
|
||||
*/
|
||||
bool operator==( const HALF_LINE& aOther ) const { return m_seg == aOther.m_seg; }
|
||||
|
||||
/**
|
||||
* Gets the (one of the infinite number of) segments that the ray passes through.
|
||||
*
|
||||
* The segment's A point is the start of the ray, and the B point is on the ray.
|
||||
*/
|
||||
const SEG& GetContainedSeg() const { return m_seg; }
|
||||
|
||||
private:
|
||||
/// Internally, we can represent a just a segment that the ray passes through
|
||||
SEG m_seg;
|
||||
};
|
||||
|
||||
|
||||
std::optional<SEG> ClipHalfLineToBox( const HALF_LINE& aRay, const BOX2I& aBox );
|
@ -27,17 +27,20 @@
|
||||
#include <vector>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <math/box2.h>
|
||||
|
||||
class SEG;
|
||||
class CIRCLE;
|
||||
class SHAPE_ARC;
|
||||
class SHAPE_RECT;
|
||||
#include <geometry/circle.h>
|
||||
#include <geometry/half_line.h>
|
||||
#include <geometry/line.h>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
|
||||
/**
|
||||
* 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>;
|
||||
using INTERSECTABLE_GEOM = std::variant<LINE, HALF_LINE, SEG, CIRCLE, SHAPE_ARC, BOX2I>;
|
||||
|
||||
/**
|
||||
* A visitor that visits INTERSECTABLE_GEOM variant objects with another
|
||||
@ -63,9 +66,11 @@ public:
|
||||
* other geometry.
|
||||
*/
|
||||
void operator()( const SEG& aSeg ) const;
|
||||
void operator()( const LINE& aLine ) const;
|
||||
void operator()( const HALF_LINE& aLine ) const;
|
||||
void operator()( const CIRCLE& aCircle ) const;
|
||||
void operator()( const SHAPE_ARC& aArc ) const;
|
||||
void operator()( const SHAPE_RECT& aArc ) const;
|
||||
void operator()( const BOX2I& aArc ) const;
|
||||
|
||||
private:
|
||||
const INTERSECTABLE_GEOM& m_otherGeometry;
|
||||
|
67
libs/kimath/include/geometry/line.h
Normal file
67
libs/kimath/include/geometry/line.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 <geometry/seg.h>
|
||||
|
||||
/*
|
||||
* A geometric line of infinite length.
|
||||
*
|
||||
* In terms of geometric ops, a SEG would probably do as it has the same definition,
|
||||
* but a separate class is more explicit and also allows compile-time
|
||||
* reasoning about the meaning of the object through the type system.
|
||||
*/
|
||||
class LINE
|
||||
{
|
||||
public:
|
||||
using ecoord = VECTOR2I::extended_type;
|
||||
|
||||
LINE( const SEG& aSeg ) : m_seg( aSeg ) {}
|
||||
|
||||
LINE( const VECTOR2I& aStart, const VECTOR2I& aEnd ) : m_seg( aStart, aEnd ) {}
|
||||
|
||||
bool operator==( const LINE& aOther ) const { return m_seg == aOther.m_seg; }
|
||||
|
||||
/**
|
||||
* Gets the (one of the infinite number of) segments that the line passes through.
|
||||
*/
|
||||
const SEG& GetContainedSeg() const { return m_seg; }
|
||||
|
||||
OPT_VECTOR2I Intersect( const SEG& aOther ) const;
|
||||
OPT_VECTOR2I Intersect( const LINE& aOther ) const;
|
||||
|
||||
/**
|
||||
* Gets the distance from the line to the given point.
|
||||
*/
|
||||
int Distance( const VECTOR2I& aPoint ) const;
|
||||
|
||||
/**
|
||||
* Gets the nearest point on the line to the given point.
|
||||
*/
|
||||
VECTOR2I NearestPoint( const VECTOR2I& aPoint ) const;
|
||||
|
||||
private:
|
||||
/// Internally, we can represent a just a segment that the line passes through
|
||||
SEG m_seg;
|
||||
};
|
59
libs/kimath/include/geometry/nearest.h
Normal file
59
libs/kimath/include/geometry/nearest.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <math/box2.h>
|
||||
|
||||
#include <geometry/circle.h>
|
||||
#include <geometry/half_line.h>
|
||||
#include <geometry/line.h>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
|
||||
|
||||
/**
|
||||
* A variant type that can hold any of the supported geometry types for
|
||||
* nearest point calculations.
|
||||
*/
|
||||
using NEARABLE_GEOM = std::variant<LINE, HALF_LINE, SEG, CIRCLE, SHAPE_ARC, BOX2I, VECTOR2I>;
|
||||
|
||||
/**
|
||||
* Get the nearest point on a geometry to a given point.
|
||||
*/
|
||||
VECTOR2I GetNearestPoint( const NEARABLE_GEOM& aGeom, const VECTOR2I& aPt );
|
||||
|
||||
/**
|
||||
* Get the nearest point on any of a list of geometries to a given point.
|
||||
*
|
||||
* @param aGeoms The geometries to check.
|
||||
* @param aPt The point to find the nearest point to.
|
||||
*
|
||||
* @return The nearest point on any of the geometries to the given point (or std::nullopt if
|
||||
* no geometries were provided).
|
||||
*/
|
||||
OPT_VECTOR2I GetNearestPoint( const std::vector<NEARABLE_GEOM>& aGeoms, const VECTOR2I& aPt );
|
@ -65,15 +65,21 @@ enum POINT_TYPE
|
||||
* The point is an intersection of two (or more) items.
|
||||
*/
|
||||
PT_INTERSECTION = 1 << 5,
|
||||
/**
|
||||
* The point is somewhere on another element, but not some specific point.
|
||||
* (you can infer this from some other point types)
|
||||
*/
|
||||
PT_ON_ELEMENT = 1 << 6,
|
||||
};
|
||||
|
||||
struct TYPED_POINT2I
|
||||
{
|
||||
VECTOR2I m_point;
|
||||
POINT_TYPE m_types;
|
||||
VECTOR2I m_point;
|
||||
// Bitwise OR of POINT_TYPE values
|
||||
int m_types;
|
||||
|
||||
// Clang needs this apparently
|
||||
TYPED_POINT2I( const VECTOR2I& aVec, POINT_TYPE aTypes ) : m_point( aVec ), m_types( aTypes ) {}
|
||||
TYPED_POINT2I( const VECTOR2I& aVec, int aTypes ) : m_point( aVec ), m_types( aTypes ) {}
|
||||
|
||||
bool operator==( const TYPED_POINT2I& ) const = default;
|
||||
};
|
||||
|
72
libs/kimath/include/geometry/shape_utils.h
Normal file
72
libs/kimath/include/geometry/shape_utils.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* @file geometry/shape_utils.h
|
||||
*
|
||||
* @brief Utility functions for working with shapes.
|
||||
*
|
||||
* These are free functions to avoid bloating the shape classes with functions
|
||||
* that only need to be used in a few places and can just use the public
|
||||
* interfaces.
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <math/box2.h>
|
||||
|
||||
class HALF_LINE;
|
||||
class LINE;
|
||||
class SEG;
|
||||
|
||||
namespace KIGEOM
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a SEG such that the start point is smaller or equal
|
||||
* in x and y compared to the end point.
|
||||
*/
|
||||
SEG NormalisedSeg( const SEG& aSeg );
|
||||
|
||||
/**
|
||||
* Decompose a BOX2 into four segments.
|
||||
*
|
||||
* Segments are returned in the order: Top, Right, Bottom, Left.
|
||||
*/
|
||||
std::array<SEG, 4> BoxToSegs( const BOX2I& aBox );
|
||||
|
||||
/**
|
||||
* Get the segment of a half-line that is inside a box, if any.
|
||||
*/
|
||||
std::optional<SEG> ClipHalfLineToBox( const HALF_LINE& aRay, const BOX2I& aBox );
|
||||
|
||||
/**
|
||||
* Get the segment of a line that is inside a box, if any.
|
||||
*/
|
||||
std::optional<SEG> ClipLineToBox( const LINE& aLine, const BOX2I& aBox );
|
||||
|
||||
} // namespace KIGEOM
|
@ -28,7 +28,7 @@ namespace KIGEOM
|
||||
* @file vector_utils.h
|
||||
*
|
||||
* Supplemental functions for working with vectors and
|
||||
* objects that interact with vectors.
|
||||
* simple objects that interact with vectors.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -82,6 +82,7 @@ bool PointProjectsOntoSegment( const VECTOR2I& aPoint, const SEG& aSeg );
|
||||
/**
|
||||
* Get the ratio of the vector to a point from the segment's start,
|
||||
* compared to the segment's length.
|
||||
*
|
||||
* /--- aPoint
|
||||
* A<---+-------->B <-- Length L
|
||||
* | |
|
||||
|
@ -67,6 +67,11 @@ public:
|
||||
Normalize();
|
||||
}
|
||||
|
||||
static BOX2<Vec> ByCorners( const Vec& aCorner1, const Vec& aCorner2 )
|
||||
{
|
||||
return BOX2( aCorner1, aCorner2 - aCorner1 );
|
||||
}
|
||||
|
||||
void SetMaximum()
|
||||
{
|
||||
if constexpr( std::is_floating_point<coord_type>() )
|
||||
|
@ -214,6 +214,10 @@ public:
|
||||
*/
|
||||
double Distance( const VECTOR2<extended_type>& aVector ) const;
|
||||
|
||||
/**
|
||||
* Compute the squared distance between two vectors.
|
||||
*/
|
||||
constexpr extended_type SquaredDistance( const VECTOR2<T>& aVector ) const;
|
||||
|
||||
// Operators
|
||||
|
||||
@ -556,6 +560,15 @@ double VECTOR2<T>::Distance( const VECTOR2<extended_type>& aVector ) const
|
||||
return diff.EuclideanNorm();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr typename VECTOR2<T>::extended_type
|
||||
VECTOR2<T>::SquaredDistance( const VECTOR2<T>& aVector ) const
|
||||
{
|
||||
const extended_type dx = (extended_type) x - aVector.x;
|
||||
const extended_type dy = (extended_type) y - aVector.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
bool VECTOR2<T>::operator<( const VECTOR2<T>& aVector ) const
|
||||
|
131
libs/kimath/src/geometry/half_line.cpp
Normal file
131
libs/kimath/src/geometry/half_line.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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/half_line.h"
|
||||
|
||||
#include <math/box2.h>
|
||||
|
||||
#include <geometry/shape_utils.h>
|
||||
|
||||
|
||||
/**
|
||||
* Check if two vectors point into the same quadrant.
|
||||
*/
|
||||
bool VectorsInSameQuadrant( const VECTOR2I& aA, const VECTOR2I& aB )
|
||||
{
|
||||
// The sign of the x and y components of the vectors must be the same
|
||||
return ( ( aA.x >= 0 ) == ( aB.x >= 0 ) ) && ( ( aA.y >= 0 ) == ( aB.y >= 0 ) );
|
||||
}
|
||||
|
||||
|
||||
bool HALF_LINE::Contains( const VECTOR2I& aPoint ) const
|
||||
{
|
||||
// Check that the point is on the right side of the ray from
|
||||
// the start point
|
||||
// This is quick, so we can do it first
|
||||
if( !VectorsInSameQuadrant( m_seg.B - m_seg.A, aPoint - m_seg.A ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the point is within a distance of 1 from the
|
||||
// infinite line of the ray
|
||||
return m_seg.LineDistance( aPoint ) <= 1;
|
||||
}
|
||||
|
||||
|
||||
OPT_VECTOR2I HALF_LINE::Intersect( const SEG& aSeg ) const
|
||||
{
|
||||
// Intsersection of two infinite lines
|
||||
const SEG seg = GetContainedSeg();
|
||||
OPT_VECTOR2I intersection = aSeg.Intersect( seg, false, true );
|
||||
|
||||
// Reject parallel lines
|
||||
if( !intersection )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check that the intersection is on the right side of the
|
||||
// ray's start point (i.e. equal quadrants)
|
||||
if( !VectorsInSameQuadrant( m_seg.B - m_seg.A, *intersection - m_seg.A ) )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check that the intersection is not somewhere past the end
|
||||
// of the segment
|
||||
if( !aSeg.Contains( *intersection ) )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
OPT_VECTOR2I HALF_LINE::Intersect( const HALF_LINE& aOther ) const
|
||||
{
|
||||
// Intsersection of two infinite lines
|
||||
const SEG otherSeg = aOther.GetContainedSeg();
|
||||
OPT_VECTOR2I intersection = m_seg.Intersect( otherSeg, false, true );
|
||||
|
||||
// Reject parallel lines
|
||||
if( !intersection )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check that the intersection is on the right side of both
|
||||
// rays' start points (i.e. equal quadrants)
|
||||
if( !VectorsInSameQuadrant( m_seg.B - m_seg.A, *intersection - m_seg.A )
|
||||
|| !VectorsInSameQuadrant( aOther.m_seg.B - aOther.m_seg.A,
|
||||
*intersection - aOther.m_seg.A ) )
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
VECTOR2I HALF_LINE::NearestPoint( const VECTOR2I& aPoint ) const
|
||||
{
|
||||
// Same as the SEG implementation, but without the early return
|
||||
// if the point isn't on the segment.
|
||||
|
||||
// Inlined for performance reasons
|
||||
VECTOR2L d( m_seg.B.x - m_seg.A.x, m_seg.B.y - m_seg.A.y );
|
||||
SEG::ecoord l_squared( d.x * d.x + d.y * d.y );
|
||||
|
||||
if( l_squared == 0 )
|
||||
return m_seg.A;
|
||||
|
||||
SEG::ecoord t = d.Dot( aPoint - m_seg.A );
|
||||
|
||||
if( t < 0 )
|
||||
return m_seg.A;
|
||||
|
||||
SEG::ecoord xp = rescale( t, (SEG::ecoord) d.x, l_squared );
|
||||
SEG::ecoord yp = rescale( t, (SEG::ecoord) d.y, l_squared );
|
||||
|
||||
return VECTOR2<SEG::ecoord>( m_seg.A.x + xp, m_seg.A.y + yp );
|
||||
}
|
@ -25,10 +25,7 @@
|
||||
|
||||
#include <core/type_helpers.h>
|
||||
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/circle.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
#include <geometry/shape_utils.h>
|
||||
|
||||
/*
|
||||
* Helper functions that dispatch to the correct intersection function
|
||||
@ -47,6 +44,27 @@ void findIntersections( const SEG& aSegA, const SEG& aSegB, std::vector<VECTOR2I
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const SEG& aSeg, const LINE& aLine, std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
OPT_VECTOR2I intersection = aLine.Intersect( aSeg );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const SEG& aSeg, const HALF_LINE& aHalfLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
OPT_VECTOR2I intersection = aHalfLine.Intersect( aSeg );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const SEG& aSeg, const CIRCLE& aCircle,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
@ -71,6 +89,68 @@ void findIntersections( const SEG& aSeg, const SHAPE_ARC& aArc,
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const LINE& aLineA, const LINE& aLineB,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
OPT_VECTOR2I intersection = aLineA.Intersect( aLineB );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const LINE& aLine, const HALF_LINE& aHalfLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
// Intersect as two infinite lines
|
||||
OPT_VECTOR2I intersection =
|
||||
aHalfLine.GetContainedSeg().Intersect( aLine.GetContainedSeg(), false, true );
|
||||
|
||||
// No intersection at all (parallel, or passes on the other side of the start point)
|
||||
if( !intersection )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( aHalfLine.Contains( *intersection ) )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const HALF_LINE& aHalfLineA, const HALF_LINE& aHalfLineB,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
OPT_VECTOR2I intersection = aHalfLineA.Intersect( aHalfLineB );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
aIntersections.push_back( *intersection );
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const CIRCLE& aCircle, const LINE& aLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections = aCircle.IntersectLine( aLine.GetContainedSeg() );
|
||||
|
||||
aIntersections.insert( aIntersections.end(), intersections.begin(), intersections.end() );
|
||||
}
|
||||
|
||||
void findIntersections( const CIRCLE& aCircle, const HALF_LINE& aHalfLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections = aCircle.IntersectLine( aHalfLine.GetContainedSeg() );
|
||||
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
if( aHalfLine.Contains( intersection ) )
|
||||
{
|
||||
aIntersections.push_back( intersection );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findIntersections( const CIRCLE& aCircleA, const CIRCLE& aCircleB,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
@ -91,19 +171,28 @@ void findIntersections( const SHAPE_ARC& aArcA, const SHAPE_ARC& aArcB,
|
||||
aArcA.Intersect( aArcB, &aIntersections );
|
||||
}
|
||||
|
||||
|
||||
std::vector<SEG> RectToSegs( const SHAPE_RECT& aRect )
|
||||
void findIntersections( const SHAPE_ARC& aArc, const LINE& aLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
const VECTOR2I corner = aRect.GetPosition();
|
||||
const int w = aRect.GetWidth();
|
||||
const int h = aRect.GetHeight();
|
||||
std::vector<VECTOR2I> intersections;
|
||||
aArc.IntersectLine( aLine.GetContainedSeg(), &intersections );
|
||||
|
||||
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 ),
|
||||
};
|
||||
aIntersections.insert( aIntersections.end(), intersections.begin(), intersections.end() );
|
||||
}
|
||||
|
||||
void findIntersections( const SHAPE_ARC& aArc, const HALF_LINE& aHalfLine,
|
||||
std::vector<VECTOR2I>& aIntersections )
|
||||
{
|
||||
std::vector<VECTOR2I> intersections;
|
||||
aArc.IntersectLine( aHalfLine.GetContainedSeg(), &intersections );
|
||||
|
||||
for( const VECTOR2I& intersection : intersections )
|
||||
{
|
||||
if( aHalfLine.Contains( intersection ) )
|
||||
{
|
||||
aIntersections.push_back( intersection );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -132,10 +221,10 @@ void INTERSECTION_VISITOR::operator()( const SEG& aSeg ) const
|
||||
{
|
||||
using OtherGeomType = std::decay_t<decltype( otherGeom )>;
|
||||
|
||||
if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
if constexpr( std::is_same_v<OtherGeomType, BOX2I> )
|
||||
{
|
||||
// Seg-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
for( const SEG& aRectSeg : KIGEOM::BoxToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aSeg, aRectSeg, m_intersections );
|
||||
}
|
||||
@ -149,6 +238,82 @@ void INTERSECTION_VISITOR::operator()( const SEG& aSeg ) const
|
||||
m_otherGeometry );
|
||||
}
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const LINE& aLine ) 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, LINE>
|
||||
|| std::is_same_v<OtherGeomType, CIRCLE>
|
||||
|| std::is_same_v<OtherGeomType, SHAPE_ARC> )
|
||||
{
|
||||
// Seg-Line, Line-Line, Circle-Line, Arc-Line
|
||||
findIntersections( otherGeom, aLine, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, HALF_LINE> )
|
||||
{
|
||||
// Line-HalfLine
|
||||
findIntersections( aLine, otherGeom, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, BOX2I> )
|
||||
{
|
||||
// Line-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : KIGEOM::BoxToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aLine, m_intersections );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( always_false<OtherGeomType>::value,
|
||||
"Unhandled other geometry type" );
|
||||
}
|
||||
},
|
||||
m_otherGeometry );
|
||||
};
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const HALF_LINE& aHalfLine ) 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, HALF_LINE>
|
||||
|| std::is_same_v<OtherGeomType, CIRCLE>
|
||||
|| std::is_same_v<OtherGeomType, SHAPE_ARC> )
|
||||
{
|
||||
// Seg-HalfLine, HalfLine-HalfLine, Circle-HalfLine, Arc-HalfLine
|
||||
findIntersections( otherGeom, aHalfLine, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, LINE> )
|
||||
{
|
||||
// Line-HalfLine
|
||||
findIntersections( otherGeom, aHalfLine, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, BOX2I> )
|
||||
{
|
||||
// HalfLine-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : KIGEOM::BoxToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aHalfLine, m_intersections );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( always_false<OtherGeomType>::value,
|
||||
"Unhandled other geometry type" );
|
||||
}
|
||||
},
|
||||
m_otherGeometry );
|
||||
};
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const CIRCLE& aCircle ) const
|
||||
{
|
||||
// Dispatch to the correct function
|
||||
@ -163,15 +328,17 @@ void INTERSECTION_VISITOR::operator()( const CIRCLE& aCircle ) const
|
||||
// Seg-Circle, Circle-Circle
|
||||
findIntersections( otherGeom, aCircle, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_ARC> )
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_ARC>
|
||||
|| std::is_same_v<OtherGeomType, LINE>
|
||||
|| std::is_same_v<OtherGeomType, HALF_LINE> )
|
||||
{
|
||||
// Circle-Arc
|
||||
// Circle-Arc, Circle-Line, Circle-HalfLine
|
||||
findIntersections( aCircle, otherGeom, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
else if constexpr( std::is_same_v<OtherGeomType, BOX2I> )
|
||||
{
|
||||
// Circle-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
for( const SEG& aRectSeg : KIGEOM::BoxToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aCircle, m_intersections );
|
||||
}
|
||||
@ -200,10 +367,16 @@ void INTERSECTION_VISITOR::operator()( const SHAPE_ARC& aArc ) const
|
||||
// Seg-Arc, Circle-Arc, Arc-Arc
|
||||
findIntersections( otherGeom, aArc, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, SHAPE_RECT> )
|
||||
else if constexpr( std::is_same_v<OtherGeomType, LINE>
|
||||
|| std::is_same_v<OtherGeomType, HALF_LINE> )
|
||||
{
|
||||
// Arc-Line, Arc-HalfLine
|
||||
findIntersections( aArc, otherGeom, m_intersections );
|
||||
}
|
||||
else if constexpr( std::is_same_v<OtherGeomType, BOX2I> )
|
||||
{
|
||||
// Arc-Rect via decomposition into segments
|
||||
for( const SEG& aRectSeg : RectToSegs( otherGeom ) )
|
||||
for( const SEG& aRectSeg : KIGEOM::BoxToSegs( otherGeom ) )
|
||||
{
|
||||
findIntersections( aRectSeg, aArc, m_intersections );
|
||||
}
|
||||
@ -218,13 +391,13 @@ void INTERSECTION_VISITOR::operator()( const SHAPE_ARC& aArc ) const
|
||||
};
|
||||
|
||||
|
||||
void INTERSECTION_VISITOR::operator()( const SHAPE_RECT& aRect ) const
|
||||
void INTERSECTION_VISITOR::operator()( const BOX2I& 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 );
|
||||
const std::array<SEG, 4> segs = KIGEOM::BoxToSegs( aRect );
|
||||
|
||||
for( const SEG& seg : segs )
|
||||
{
|
||||
|
70
libs/kimath/src/geometry/line.cpp
Normal file
70
libs/kimath/src/geometry/line.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <geometry/line.h>
|
||||
|
||||
OPT_VECTOR2I LINE::Intersect( const SEG& aSeg ) const
|
||||
{
|
||||
// intersect as two lines
|
||||
OPT_VECTOR2I intersection = aSeg.Intersect( m_seg, false, true );
|
||||
|
||||
if( intersection )
|
||||
{
|
||||
// Not parallel.
|
||||
// That was two lines, but we need to check if the intersection is on
|
||||
// the requested segment
|
||||
if( aSeg.Contains( *intersection ) )
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
OPT_VECTOR2I LINE::Intersect( const LINE& aOther ) const
|
||||
{
|
||||
// Defer to the SEG implementation
|
||||
return aOther.m_seg.Intersect( m_seg, false, true );
|
||||
}
|
||||
|
||||
int LINE::Distance( const VECTOR2I& aPoint ) const
|
||||
{
|
||||
// Just defer to the SEG implementation
|
||||
return m_seg.LineDistance( aPoint );
|
||||
}
|
||||
|
||||
VECTOR2I LINE::NearestPoint( const VECTOR2I& aPoint ) const
|
||||
{
|
||||
// Same as the SEG implementation, but without the early return
|
||||
// if the point isn't on the segment.
|
||||
|
||||
// Inlined for performance reasons
|
||||
VECTOR2L d( m_seg.B.x - m_seg.A.x, m_seg.B.y - m_seg.A.y );
|
||||
ecoord l_squared( d.x * d.x + d.y * d.y );
|
||||
|
||||
if( l_squared == 0 )
|
||||
return m_seg.A;
|
||||
|
||||
ecoord t = d.Dot( aPoint - m_seg.A );
|
||||
|
||||
ecoord xp = rescale( t, (ecoord) d.x, l_squared );
|
||||
ecoord yp = rescale( t, (ecoord) d.y, l_squared );
|
||||
|
||||
return VECTOR2<ecoord>( m_seg.A.x + xp, m_seg.A.y + yp );
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user