KiCad PCB EDA Suite
polygon_geom_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2017-2022 Kicad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23#include <limits>
24#include <vector>
25
27
30
31
33 m_client( aClient ),
34 m_leaderMode( LEADER_MODE::DIRECT ),
35 m_intersectionsAllowed( true )
36{}
37
38
40{
41 // if this is the first point, make sure the client is happy
42 // for us to continue
43 if( !IsPolygonInProgress() && !m_client.OnFirstPoint( *this ) )
44 return false;
45
46 if( m_leaderPts.PointCount() > 1 )
47 {
48 // there are enough leader points - the next
49 // locked-in point is the end of the last leader
50 // segment
53 }
54 else
55 {
56 // no leader lines, directly add the cursor
58 }
59
60 // check for self-intersections
62 {
64 return false;
65 }
66
67 if( m_lockedPoints.PointCount() > 0 )
69
71 return true;
72}
73
74
76{
77
78 m_client.OnComplete( *this );
79}
80
81
83{
84 m_leaderMode = aMode;
85}
86
87
88bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const
89{
91
92 if( aIncludeLeaderPts )
93 {
94 for( int i = 0; i < m_leaderPts.PointCount(); ++i )
95 {
96 if( m_leaderPts.CPoint( i ) != pts.CPoint( 0 ) )
97 pts.Append( m_leaderPts.CPoint( i ) );
98 }
99 }
100
101 // line chain needs to be set as closed for proper checks
102 pts.SetClosed( true );
103
104 return !!pts.SelfIntersecting();
105}
106
107
109{
110 updateTemporaryLines( aPos );
111}
112
113
115{
116 return m_lockedPoints.PointCount() > 0;
117}
118
119
121{
122 return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
123}
124
125
127{
128 if( m_lockedPoints.PointCount() > 0 )
130
131 // update the new last segment (was previously
132 // locked in), reusing last constraints
133 if( m_lockedPoints.PointCount() > 0 )
135
136 m_client.OnGeometryChange( *this );
137}
138
139
141{
145
146 m_client.OnGeometryChange( *this );
147}
148
149
150static SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, SHAPE_LINE_CHAIN aLastPoints )
151{
152 if( aLastPoints.PointCount() < 1 )
153 return SHAPE_LINE_CHAIN();
154
155 const VECTOR2I lastPt = aLastPoints.CPoint( -1 );
156 const VECTOR2D endpointD = aEndPoint;
157 const VECTOR2D lineVec = endpointD - lastPt;
158
159 if( aLastPoints.SegmentCount() < 1 )
160 return SHAPE_LINE_CHAIN(
161 std::vector<VECTOR2I>{ lastPt, lastPt + GetVectorSnapped45( lineVec ) } );
162
163 EDA_ANGLE lineA( lineVec );
164 EDA_ANGLE prevA( GetVectorSnapped45( lastPt - aLastPoints.CPoint( -2 ) ) );
165
166 bool vertical = std::abs( lineVec.y ) > std::abs( lineVec.x );
167 bool horizontal = std::abs( lineVec.y ) < std::abs( lineVec.x );
168
169 double angDiff = std::abs( ( lineA - prevA ).Normalize180().AsDegrees() );
170
171 bool bendEnd = ( angDiff < 45 ) || ( angDiff > 90 && angDiff < 135 );
172
173 if( prevA.Normalize90() == ANGLE_45 || prevA.Normalize90() == -ANGLE_45 )
174 bendEnd = !bendEnd;
175
176 VECTOR2D mid = endpointD;
177
178 if( bendEnd )
179 {
180 if( vertical )
181 {
182 if( lineVec.y > 0 )
183 mid = VECTOR2D( lastPt.x, endpointD.y - std::abs( lineVec.x ) );
184 else
185 mid = VECTOR2D( lastPt.x, endpointD.y + std::abs( lineVec.x ) );
186 }
187 else if( horizontal )
188 {
189 if( lineVec.x > 0 )
190 mid = VECTOR2D( endpointD.x - std::abs( lineVec.y ), lastPt.y );
191 else
192 mid = VECTOR2D( endpointD.x + std::abs( lineVec.y ), lastPt.y );
193 }
194 }
195 else
196 {
197 if( vertical )
198 {
199 if( lineVec.y > 0 )
200 mid = VECTOR2D( endpointD.x, lastPt.y + std::abs( lineVec.x ) );
201 else
202 mid = VECTOR2D( endpointD.x, lastPt.y - std::abs( lineVec.x ) );
203 }
204 else if( horizontal )
205 {
206 if( lineVec.x > 0 )
207 mid = VECTOR2D( lastPt.x + std::abs( lineVec.y ), endpointD.y );
208 else
209 mid = VECTOR2D( lastPt.x - std::abs( lineVec.y ), endpointD.y );
210 }
211 }
212
213 const VECTOR2I midInt = { KiROUND( mid.x ), KiROUND( mid.y ) };
214
215 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, midInt, aEndPoint } );
216}
217
218
220{
221 wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
222 const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
223
224 if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
225 {
226 if( m_lockedPoints.PointCount() > 0 )
227 {
230 }
231 }
232 else
233 {
234 // direct segment
235 m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
237 }
238
239 m_client.OnGeometryChange( *this );
240}
EDA_ANGLE Normalize90()
Definition: eda_angle.h:277
"Listener" interface for a class that wants to be updated about polygon geometry changes
virtual void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr)=0
Called when the polygon is complete.
virtual bool OnFirstPoint(POLYGON_GEOM_MANAGER &aMgr)=0
Called before the first point is added - clients can do initialization here, and can veto the start o...
virtual void OnComplete(const POLYGON_GEOM_MANAGER &aMgr)=0
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void updateTemporaryLines(const VECTOR2I &aEndPoint, LEADER_MODE aModifier=LEADER_MODE::DIRECT)
Update the leader and loop lines points based on a new endpoint (probably a cursor position)
CLIENT & m_client
The current mode of the leader line.
POLYGON_GEOM_MANAGER(CLIENT &aClient)
SHAPE_LINE_CHAIN m_leaderPts
Points between the cursor and start point.
LEADER_MODE
The kind of the leader line.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
void DeleteLastCorner()
Remove the last-added point from the polygon.
bool IsSelfIntersecting(bool aIncludeLeaderPts) const
Check whether the locked points constitute a self-intersecting outline.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
SHAPE_LINE_CHAIN m_loopPts
void SetFinished()
Mark the polygon finished and update the client.
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
LEADER_MODE m_leaderMode
Flag enabling self-intersecting polygons.
void Reset()
Clear the manager state and start again.
SHAPE_LINE_CHAIN m_lockedPoints
Points in the temporary "leader" line(s)
bool m_intersectionsAllowed
Point that have been "locked in".
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
const std::optional< INTERSECTION > SelfIntersecting() const
Check if the line chain is self-intersecting.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Clear()
Remove all points from the line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
static constexpr EDA_ANGLE & ANGLE_45
Definition: eda_angle.h:430
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
static SHAPE_LINE_CHAIN build45DegLeader(const VECTOR2I &aEndPoint, SHAPE_LINE_CHAIN aLastPoints)
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< double > VECTOR2D
Definition: vector2d.h:589