Support arcs in Break Track.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/16712
This commit is contained in:
Alex 2024-01-24 05:47:36 +03:00
parent a46d409ec6
commit ed00cb3304
5 changed files with 61 additions and 11 deletions

View File

@ -2,7 +2,7 @@
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013-2017 CERN * Copyright (C) 2013-2017 CERN
* Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2016-2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
@ -767,7 +767,7 @@ bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail
// (now a hull or a walk/shove line cannot collide with the 'owner' of the hull under any circumstances). // (now a hull or a walk/shove line cannot collide with the 'owner' of the hull under any circumstances).
// This, however, introduced a subtle bug. For a row/column/any other 'regular' arrangement // This, however, introduced a subtle bug. For a row/column/any other 'regular' arrangement
// of overlapping hulls (think of pads of a SOP/SOIC chip or a regular via grid), walking around may // of overlapping hulls (think of pads of a SOP/SOIC chip or a regular via grid), walking around may
// produce a new 'head' that is not considered colliding (due to the clearance epsilon), but with // produce a new 'head' that is not considered colliding (due to the clearance epsilon), but with
// its start point inside one of the subsequent hulls to process. // its start point inside one of the subsequent hulls to process.
// We can't have head[0] inside any hull for the algorithm to work - therefore, we now consider the entire // We can't have head[0] inside any hull for the algorithm to work - therefore, we now consider the entire
// 'tail+head' trace when walking around and in case of success, reconstruct the // 'tail+head' trace when walking around and in case of success, reconstruct the
@ -932,7 +932,7 @@ bool LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTai
{ {
newHead.AppendVia( makeVia( newHead.CPoint( -1 ) ) ); newHead.AppendVia( makeVia( newHead.CPoint( -1 ) ) );
PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-new-via" ); PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-new-via" );
} }
SHOVE::SHOVE_STATUS status = m_shove->ShoveLines( newHead ); SHOVE::SHOVE_STATUS status = m_shove->ShoveLines( newHead );
@ -1292,6 +1292,38 @@ bool LINE_PLACER::SplitAdjacentSegments( NODE* aNode, ITEM* aSeg, const VECTOR2I
} }
bool LINE_PLACER::SplitAdjacentArcs( NODE* aNode, ITEM* aArc, const VECTOR2I& aP )
{
if( !aArc )
return false;
if( !aArc->OfKind( ITEM::ARC_T ) )
return false;
const JOINT* jt = aNode->FindJoint( aP, aArc );
if( jt && jt->LinkCount() >= 1 )
return false;
ARC* a_old = static_cast<ARC*>( aArc );
const SHAPE_ARC& o_arc = a_old->Arc();
std::unique_ptr<ARC> a_new[2] = { Clone( *a_old ), Clone( *a_old ) };
a_new[0]->Arc().ConstructFromStartEndCenter( o_arc.GetP0(), aP, o_arc.GetCenter(),
o_arc.IsClockwise(), o_arc.GetWidth() );
a_new[1]->Arc().ConstructFromStartEndCenter( aP, o_arc.GetP1(), o_arc.GetCenter(),
o_arc.IsClockwise(), o_arc.GetWidth() );
aNode->Remove( a_old );
aNode->Add( std::move( a_new[0] ), true );
aNode->Add( std::move( a_new[1] ), true );
return true;
}
bool LINE_PLACER::SetLayer( int aLayer ) bool LINE_PLACER::SetLayer( int aLayer )
{ {
if( m_idle ) if( m_idle )

View File

@ -2,7 +2,7 @@
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013-2017 CERN * Copyright (C) 2013-2017 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2016-2024 KiCad Developers, see AUTHORS.txt for contributors.
* *
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
@ -206,11 +206,17 @@ public:
void GetModifiedNets( std::vector<NET_HANDLE>& aNets ) const override; void GetModifiedNets( std::vector<NET_HANDLE>& aNets ) const override;
/** /**
* Check if point \a aP lies on segment \a aSeg. If so, splits the segment in two, forming a * Snaps the point \a aP to segment \a aSeg. Splits the segment in two, forming a
* joint at \a aP and stores updated topology in node \a aNode. * joint at \a aP and stores updated topology in node \a aNode.
*/ */
bool SplitAdjacentSegments( NODE* aNode, ITEM* aSeg, const VECTOR2I& aP ); bool SplitAdjacentSegments( NODE* aNode, ITEM* aSeg, const VECTOR2I& aP );
/**
* Snaps the point \a aP to arc \a aArc. Splits the arc in two, forming a
* joint at \a aP and stores updated topology in node \a aNode.
*/
bool SplitAdjacentArcs( NODE* aNode, ITEM* aArc, const VECTOR2I& aP );
private: private:
/** /**
* Re-route the current track to point aP. Returns true, when routing has completed * Re-route the current track to point aP. Returns true, when routing has completed

View File

@ -1039,13 +1039,20 @@ void ROUTER::SetInterface( ROUTER_IFACE *aIface )
} }
void ROUTER::BreakSegment( ITEM *aItem, const VECTOR2I& aP ) void ROUTER::BreakSegmentOrArc( ITEM *aItem, const VECTOR2I& aP )
{ {
NODE *node = m_world->Branch(); NODE *node = m_world->Branch();
LINE_PLACER placer( this ); LINE_PLACER placer( this );
if( placer.SplitAdjacentSegments( node, aItem, aP ) ) bool ret = false;
if( aItem->OfKind( ITEM::SEGMENT_T ) )
ret = placer.SplitAdjacentSegments( node, aItem, aP );
else if( aItem->OfKind( ITEM::ARC_T ) )
ret = placer.SplitAdjacentArcs( node, aItem, aP );
if( ret )
{ {
CommitRouting( node ); CommitRouting( node );
} }

View File

@ -150,7 +150,7 @@ public:
bool Finish(); bool Finish();
bool ContinueFromEnd( ITEM** aNewStartItem ); bool ContinueFromEnd( ITEM** aNewStartItem );
bool FixRoute( const VECTOR2I& aP, ITEM* aItem, bool aForceFinish, bool aForceCommit ); bool FixRoute( const VECTOR2I& aP, ITEM* aItem, bool aForceFinish, bool aForceCommit );
void BreakSegment( ITEM *aItem, const VECTOR2I& aP ); void BreakSegmentOrArc( ITEM *aItem, const VECTOR2I& aP );
std::optional<VECTOR2I> UndoLastSegment(); std::optional<VECTOR2I> UndoLastSegment();
void CommitRouting(); void CommitRouting();

View File

@ -1563,8 +1563,11 @@ bool ROUTER_TOOL::RoutingInProgress()
void ROUTER_TOOL::breakTrack() void ROUTER_TOOL::breakTrack()
{ {
if( m_startItem && m_startItem->OfKind( PNS::ITEM::SEGMENT_T ) ) if( !m_startItem )
m_router->BreakSegment( m_startItem, m_startSnapPoint ); return;
if( m_startItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
} }
@ -2501,7 +2504,7 @@ int ROUTER_TOOL::InlineBreakTrack( const TOOL_EVENT& aEvent )
const BOARD_CONNECTED_ITEM* item = const BOARD_CONNECTED_ITEM* item =
static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() ); static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
if( item->Type() != PCB_TRACE_T ) if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
return 0; return 0;
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear ); m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
@ -2516,6 +2519,8 @@ int ROUTER_TOOL::InlineBreakTrack( const TOOL_EVENT& aEvent )
m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() ); m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) ); m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
controls()->ForceCursorPosition( false );
if( toolManager->IsContextMenuActive() ) if( toolManager->IsContextMenuActive() )
{ {
// If we're here from a context menu then we need to get the position of the // If we're here from a context menu then we need to get the position of the