From 3d2b1aaf90e974e967a9673359f7e6ff3ceb6f5c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Dec 2022 19:11:56 +0300 Subject: [PATCH] Improve H/V/45 deg mode when drawing zones and polygons. --- common/preview_items/polygon_geom_manager.cpp | 136 +++++++++++------- common/preview_items/polygon_item.cpp | 10 +- include/preview_items/polygon_geom_manager.h | 20 ++- include/preview_items/polygon_item.h | 11 +- pcbnew/tools/zone_create_helper.cpp | 15 +- 5 files changed, 123 insertions(+), 69 deletions(-) diff --git a/common/preview_items/polygon_geom_manager.cpp b/common/preview_items/polygon_geom_manager.cpp index 9b4aa67f5a..bb87e11774 100644 --- a/common/preview_items/polygon_geom_manager.cpp +++ b/common/preview_items/polygon_geom_manager.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KICAD, a free EDA CAD application. * - * Copyright (C) 2017-2020 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2022 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 @@ -46,9 +46,10 @@ bool POLYGON_GEOM_MANAGER::AddPoint( const VECTOR2I& aPt ) if( m_leaderPts.PointCount() > 1 ) { // there are enough leader points - the next - // locked-in point is the end of the first leader + // locked-in point is the end of the last leader // segment - m_lockedPoints.Append( m_leaderPts.CPoint( 1 ) ); + m_lockedPoints.Append( m_leaderPts.CPoint( -2 ) ); + m_lockedPoints.Append( m_leaderPts.CPoint( -1 ) ); } else { @@ -63,6 +64,9 @@ bool POLYGON_GEOM_MANAGER::AddPoint( const VECTOR2I& aPt ) return false; } + if( m_lockedPoints.PointCount() > 0 ) + updateTemporaryLines( aPt ); + m_client.OnGeometryChange( *this ); return true; } @@ -103,7 +107,7 @@ bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const void POLYGON_GEOM_MANAGER::SetCursorPosition( const VECTOR2I& aPos ) { - updateLeaderPoints( aPos ); + updateTemporaryLines( aPos ); } @@ -127,7 +131,7 @@ void POLYGON_GEOM_MANAGER::DeleteLastCorner() // update the new last segment (was previously // locked in), reusing last constraints if( m_lockedPoints.PointCount() > 0 ) - updateLeaderPoints( m_leaderPts.CLastPoint() ); + updateTemporaryLines( m_leaderPts.CLastPoint() ); m_client.OnGeometryChange( *this ); } @@ -142,67 +146,93 @@ void POLYGON_GEOM_MANAGER::Reset() } -void POLYGON_GEOM_MANAGER::updateLeaderPoints( const VECTOR2I& aEndPoint, LEADER_MODE aModifier ) +SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, SHAPE_LINE_CHAIN aLastPoints ) +{ + if( aLastPoints.PointCount() < 1 ) + return SHAPE_LINE_CHAIN(); + + const VECTOR2I lastPt = aLastPoints.CPoint( -1 ); + const VECTOR2D endpointD = aEndPoint; + const VECTOR2D lineVec = endpointD - lastPt; + + if( aLastPoints.SegmentCount() < 1 ) + return SHAPE_LINE_CHAIN( + std::vector{ lastPt, lastPt + GetVectorSnapped45( lineVec ) } ); + + EDA_ANGLE lineA( lineVec ); + EDA_ANGLE prevA( GetVectorSnapped45( lastPt - aLastPoints.CPoint( -2 ) ) ); + + bool vertical = std::abs( lineVec.y ) > std::abs( lineVec.x ); + bool horizontal = std::abs( lineVec.y ) < std::abs( lineVec.x ); + + double angDiff = std::abs( ( lineA - prevA ).Normalize180().AsDegrees() ); + + bool bendEnd = ( angDiff < 45 ) || ( angDiff > 90 && angDiff < 135 ); + + if( prevA.Normalize90() == ANGLE_45 || prevA.Normalize90() == -ANGLE_45 ) + bendEnd = !bendEnd; + + VECTOR2D mid = endpointD; + + if( bendEnd ) + { + if( vertical ) + { + if( lineVec.y > 0 ) + mid = VECTOR2D( lastPt.x, endpointD.y - std::abs( lineVec.x ) ); + else + mid = VECTOR2D( lastPt.x, endpointD.y + std::abs( lineVec.x ) ); + } + else if( horizontal ) + { + if( lineVec.x > 0 ) + mid = VECTOR2D( endpointD.x - std::abs( lineVec.y ), lastPt.y ); + else + mid = VECTOR2D( endpointD.x + std::abs( lineVec.y ), lastPt.y ); + } + } + else + { + if( vertical ) + { + if( lineVec.y > 0 ) + mid = VECTOR2D( endpointD.x, lastPt.y + std::abs( lineVec.x ) ); + else + mid = VECTOR2D( endpointD.x, lastPt.y - std::abs( lineVec.x ) ); + } + else if( horizontal ) + { + if( lineVec.x > 0 ) + mid = VECTOR2D( lastPt.x + std::abs( lineVec.y ), endpointD.y ); + else + mid = VECTOR2D( lastPt.x - std::abs( lineVec.y ), endpointD.y ); + } + } + + const VECTOR2I midInt = { KiROUND( mid.x ), KiROUND( mid.y ) }; + + return SHAPE_LINE_CHAIN( std::vector{ lastPt, midInt, aEndPoint } ); +} + + +void POLYGON_GEOM_MANAGER::updateTemporaryLines( const VECTOR2I& aEndPoint, LEADER_MODE aModifier ) { wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ ); const VECTOR2I& last_pt = m_lockedPoints.CLastPoint(); if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 ) { - const VECTOR2I line_vec( aEndPoint - last_pt ); - // get a restricted 45/H/V line from the last fixed point to the cursor - auto new_end = last_pt + GetVectorSnapped45( line_vec ); - OPT_VECTOR2I pt; - - if( m_lockedPoints.SegmentCount() > 1 ) + if( m_lockedPoints.PointCount() > 0 ) { - const VECTOR2I& start_pt = m_lockedPoints.CPoint( 0 ); - VECTOR2I completed_vec( start_pt - new_end ); - - if( completed_vec != GetVectorSnapped45( completed_vec ) ) - { - SEG v_first( new_end, VECTOR2I( new_end.x, start_pt.y ) ); - SEG h_first( new_end, VECTOR2I( start_pt.x, new_end.y ) ); - - SHAPE_LINE_CHAIN::INTERSECTIONS intersections; - auto v_hits = m_lockedPoints.Intersect( v_first, intersections ); - v_hits += m_lockedPoints.Intersect( SEG( v_first.B, start_pt ), intersections ); - pt = v_first.B; - - if( v_hits > 0 ) - { - intersections.clear(); - auto h_hits = m_lockedPoints.Intersect( h_first, intersections ); - h_hits += m_lockedPoints.Intersect( SEG( h_first.B, start_pt ), intersections ); - - if( h_hits < v_hits ) - pt = h_first.B; - } - } - } - - m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, new_end } ); - - if( pt ) - { - SEG drawn( last_pt, new_end ); - SEG completed( new_end, *pt ); - - /* - * Check for backtracking from the point to intersection. If the snapped path to - * completion is shorter than what the user actually drew, we want to discard the - * drawn point and just use the snapped completion point. - */ - if( drawn.Collinear( completed ) && drawn.SquaredLength() > completed.SquaredLength() ) - m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, *pt } ); - else - m_leaderPts.Append( *pt ); + m_leaderPts = build45DegLeader( aEndPoint, m_lockedPoints ); + m_loopPts = build45DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse(); } } else { // direct segment m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } ); + m_loopPts.Clear(); } m_client.OnGeometryChange( *this ); diff --git a/common/preview_items/polygon_item.cpp b/common/preview_items/polygon_item.cpp index 6f751f4d3f..a38c7b7130 100644 --- a/common/preview_items/polygon_item.cpp +++ b/common/preview_items/polygon_item.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KICAD, a free EDA CAD application. * - * Copyright (C) 2017 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2022 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 @@ -36,10 +36,11 @@ POLYGON_ITEM::POLYGON_ITEM() : void POLYGON_ITEM::SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, - const SHAPE_LINE_CHAIN& aLeaderPts ) + const SHAPE_LINE_CHAIN& aLeaderPts, const SHAPE_LINE_CHAIN& aLoopPts ) { m_lockedChain = aLockedInPts; m_leaderChain = aLeaderPts; + m_loopChain = aLoopPts; m_polyfill.RemoveAllContours(); m_polyfill.NewOutline(); @@ -49,6 +50,9 @@ void POLYGON_ITEM::SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, for( int i = 0; i < aLeaderPts.PointCount(); ++i ) m_polyfill.Append( aLeaderPts.CPoint( i ) ); + + for( int i = 0; i < aLoopPts.PointCount(); ++i ) + m_polyfill.Append( aLoopPts.CPoint( i ) ); } @@ -70,6 +74,8 @@ void POLYGON_ITEM::drawPreviewShape( KIGFX::VIEW* aView ) const gal.DrawPolyline( m_leaderChain ); } + gal.SetIsStroke( false ); + for( int j = 0; j < m_polyfill.OutlineCount(); ++j ) { const SHAPE_LINE_CHAIN& outline = m_polyfill.COutline( j ); diff --git a/include/preview_items/polygon_geom_manager.h b/include/preview_items/polygon_geom_manager.h index 9f73fa7173..4325a9755c 100644 --- a/include/preview_items/polygon_geom_manager.h +++ b/include/preview_items/polygon_geom_manager.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KICAD, a free EDA CAD application. * - * Copyright (C) 2017-2021 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2022 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 @@ -174,14 +174,23 @@ public: return m_leaderPts; } + /** + * Get the points from the current cursor position + * to the polygon start point + */ + const SHAPE_LINE_CHAIN& GetLoopLinePoints() const + { + return m_loopPts; + } + private: /** - * Update the leader line points based on a new endpoint (probably + * Update the leader and loop lines points based on a new endpoint (probably * a cursor position) */ - void updateLeaderPoints( const VECTOR2I& aEndPoint, - LEADER_MODE aModifier = LEADER_MODE::DIRECT ); + void updateTemporaryLines( const VECTOR2I& aEndPoint, + LEADER_MODE aModifier = LEADER_MODE::DIRECT ); ///< The "user" of the polygon data that is informed when the geometry changes CLIENT& m_client; @@ -197,6 +206,9 @@ private: ///< Points in the temporary "leader" line(s) SHAPE_LINE_CHAIN m_leaderPts; + + ///< Points between the cursor and start point + SHAPE_LINE_CHAIN m_loopPts; }; #endif // PREVIEW_POLYGON_GEOM_MANAGER__H_ diff --git a/include/preview_items/polygon_item.h b/include/preview_items/polygon_item.h index ab24c5b949..b5e7376871 100644 --- a/include/preview_items/polygon_item.h +++ b/include/preview_items/polygon_item.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KICAD, a free EDA CAD application. * - * Copyright (C) 2017-2021 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2022 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 @@ -56,16 +56,17 @@ public: * @param aLockedInPts - the "fixed points" of the outline * @param aLeaderPts - the lines from the last fixed point to * another point, eg the cursor. + * @param aLoopPts - the lines from the cursor to the start point. */ - void SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, - const SHAPE_LINE_CHAIN& aLeaderPts ); + void SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, const SHAPE_LINE_CHAIN& aLeaderPts, + const SHAPE_LINE_CHAIN& aLoopPts ); private: ///< Draw rectangle and center line onto GAL void drawPreviewShape( KIGFX::VIEW* aView ) const override; - ///< complete polyline of locked in and leader points - SHAPE_LINE_CHAIN m_lockedChain, m_leaderChain; + ///< complete polyline of locked in, leader and looping points + SHAPE_LINE_CHAIN m_lockedChain, m_leaderChain, m_loopChain; ///< polygon fill SHAPE_POLY_SET m_polyfill; diff --git a/pcbnew/tools/zone_create_helper.cpp b/pcbnew/tools/zone_create_helper.cpp index 96bfef6d8c..b383ed038d 100644 --- a/pcbnew/tools/zone_create_helper.cpp +++ b/pcbnew/tools/zone_create_helper.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2017-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2017-2022 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 @@ -320,7 +320,8 @@ bool ZONE_CREATE_HELPER::OnFirstPoint( POLYGON_GEOM_MANAGER& aMgr ) void ZONE_CREATE_HELPER::OnGeometryChange( const POLYGON_GEOM_MANAGER& aMgr ) { // send the points to the preview item - m_previewItem.SetPoints( aMgr.GetLockedInPoints(), aMgr.GetLeaderLinePoints() ); + m_previewItem.SetPoints( aMgr.GetLockedInPoints(), aMgr.GetLeaderLinePoints(), + aMgr.GetLoopLinePoints() ); m_parentView.Update( &m_previewItem, KIGFX::GEOMETRY ); } @@ -349,9 +350,13 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr ) // 45 constraint if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 ) { - const auto& pts = aMgr.GetLeaderLinePoints(); - for( int i = 1; i < pts.PointCount(); i++ ) - outline->Append( pts.CPoint( i ) ); + const SHAPE_LINE_CHAIN leaderPts = aMgr.GetLeaderLinePoints(); + for( int i = 1; i < leaderPts.PointCount(); i++ ) + outline->Append( leaderPts.CPoint( i ) ); + + const SHAPE_LINE_CHAIN loopPts = aMgr.GetLoopLinePoints(); + for( int i = 1; i < loopPts.PointCount(); i++ ) + outline->Append( loopPts.CPoint( i ) ); } outline->Outline( 0 ).SetClosed( true );