Improve H/V/45 deg mode when drawing zones and polygons.

This commit is contained in:
Alex 2022-12-09 19:11:56 +03:00
parent 945361ca65
commit 3d2b1aaf90
5 changed files with 123 additions and 69 deletions

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KICAD, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 ) if( m_leaderPts.PointCount() > 1 )
{ {
// there are enough leader points - the next // 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 // segment
m_lockedPoints.Append( m_leaderPts.CPoint( 1 ) ); m_lockedPoints.Append( m_leaderPts.CPoint( -2 ) );
m_lockedPoints.Append( m_leaderPts.CPoint( -1 ) );
} }
else else
{ {
@ -63,6 +64,9 @@ bool POLYGON_GEOM_MANAGER::AddPoint( const VECTOR2I& aPt )
return false; return false;
} }
if( m_lockedPoints.PointCount() > 0 )
updateTemporaryLines( aPt );
m_client.OnGeometryChange( *this ); m_client.OnGeometryChange( *this );
return true; return true;
} }
@ -103,7 +107,7 @@ bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const
void POLYGON_GEOM_MANAGER::SetCursorPosition( const VECTOR2I& aPos ) 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 // update the new last segment (was previously
// locked in), reusing last constraints // locked in), reusing last constraints
if( m_lockedPoints.PointCount() > 0 ) if( m_lockedPoints.PointCount() > 0 )
updateLeaderPoints( m_leaderPts.CLastPoint() ); updateTemporaryLines( m_leaderPts.CLastPoint() );
m_client.OnGeometryChange( *this ); 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<VECTOR2I>{ 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<VECTOR2I>{ lastPt, midInt, aEndPoint } );
}
void POLYGON_GEOM_MANAGER::updateTemporaryLines( const VECTOR2I& aEndPoint, LEADER_MODE aModifier )
{ {
wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ ); wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
const VECTOR2I& last_pt = m_lockedPoints.CLastPoint(); const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 ) if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
{ {
const VECTOR2I line_vec( aEndPoint - last_pt ); if( m_lockedPoints.PointCount() > 0 )
// 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 )
{ {
const VECTOR2I& start_pt = m_lockedPoints.CPoint( 0 ); m_leaderPts = build45DegLeader( aEndPoint, m_lockedPoints );
VECTOR2I completed_vec( start_pt - new_end ); m_loopPts = build45DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
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 );
} }
} }
else else
{ {
// direct segment // direct segment
m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } ); m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
m_loopPts.Clear();
} }
m_client.OnGeometryChange( *this ); m_client.OnGeometryChange( *this );

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KICAD, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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, 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_lockedChain = aLockedInPts;
m_leaderChain = aLeaderPts; m_leaderChain = aLeaderPts;
m_loopChain = aLoopPts;
m_polyfill.RemoveAllContours(); m_polyfill.RemoveAllContours();
m_polyfill.NewOutline(); m_polyfill.NewOutline();
@ -49,6 +50,9 @@ void POLYGON_ITEM::SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts,
for( int i = 0; i < aLeaderPts.PointCount(); ++i ) for( int i = 0; i < aLeaderPts.PointCount(); ++i )
m_polyfill.Append( aLeaderPts.CPoint( 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.DrawPolyline( m_leaderChain );
} }
gal.SetIsStroke( false );
for( int j = 0; j < m_polyfill.OutlineCount(); ++j ) for( int j = 0; j < m_polyfill.OutlineCount(); ++j )
{ {
const SHAPE_LINE_CHAIN& outline = m_polyfill.COutline( j ); const SHAPE_LINE_CHAIN& outline = m_polyfill.COutline( j );

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KICAD, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -174,14 +174,23 @@ public:
return m_leaderPts; 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: 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) * a cursor position)
*/ */
void updateLeaderPoints( const VECTOR2I& aEndPoint, void updateTemporaryLines( const VECTOR2I& aEndPoint,
LEADER_MODE aModifier = LEADER_MODE::DIRECT ); LEADER_MODE aModifier = LEADER_MODE::DIRECT );
///< The "user" of the polygon data that is informed when the geometry changes ///< The "user" of the polygon data that is informed when the geometry changes
CLIENT& m_client; CLIENT& m_client;
@ -197,6 +206,9 @@ private:
///< Points in the temporary "leader" line(s) ///< Points in the temporary "leader" line(s)
SHAPE_LINE_CHAIN m_leaderPts; SHAPE_LINE_CHAIN m_leaderPts;
///< Points between the cursor and start point
SHAPE_LINE_CHAIN m_loopPts;
}; };
#endif // PREVIEW_POLYGON_GEOM_MANAGER__H_ #endif // PREVIEW_POLYGON_GEOM_MANAGER__H_

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KICAD, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 aLockedInPts - the "fixed points" of the outline
* @param aLeaderPts - the lines from the last fixed point to * @param aLeaderPts - the lines from the last fixed point to
* another point, eg the cursor. * another point, eg the cursor.
* @param aLoopPts - the lines from the cursor to the start point.
*/ */
void SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, void SetPoints( const SHAPE_LINE_CHAIN& aLockedInPts, const SHAPE_LINE_CHAIN& aLeaderPts,
const SHAPE_LINE_CHAIN& aLeaderPts ); const SHAPE_LINE_CHAIN& aLoopPts );
private: private:
///< Draw rectangle and center line onto GAL ///< Draw rectangle and center line onto GAL
void drawPreviewShape( KIGFX::VIEW* aView ) const override; void drawPreviewShape( KIGFX::VIEW* aView ) const override;
///< complete polyline of locked in and leader points ///< complete polyline of locked in, leader and looping points
SHAPE_LINE_CHAIN m_lockedChain, m_leaderChain; SHAPE_LINE_CHAIN m_lockedChain, m_leaderChain, m_loopChain;
///< polygon fill ///< polygon fill
SHAPE_POLY_SET m_polyfill; SHAPE_POLY_SET m_polyfill;

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 ) void ZONE_CREATE_HELPER::OnGeometryChange( const POLYGON_GEOM_MANAGER& aMgr )
{ {
// send the points to the preview item // 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 ); m_parentView.Update( &m_previewItem, KIGFX::GEOMETRY );
} }
@ -349,9 +350,13 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
// 45 constraint // 45 constraint
if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 ) if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 )
{ {
const auto& pts = aMgr.GetLeaderLinePoints(); const SHAPE_LINE_CHAIN leaderPts = aMgr.GetLeaderLinePoints();
for( int i = 1; i < pts.PointCount(); i++ ) for( int i = 1; i < leaderPts.PointCount(); i++ )
outline->Append( pts.CPoint( 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 ); outline->Outline( 0 ).SetClosed( true );