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.
*
* 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<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*/ );
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 );

View File

@ -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 );

View File

@ -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_

View File

@ -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;

View File

@ -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 );