KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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();
123}
124
125
127{
128 return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
129}
130
131
133{
134 std::optional<VECTOR2I> last;
135
136 if( m_lockedPoints.PointCount() > 0 )
137 {
140 }
141
142 // update the new last segment (was previously
143 // locked in), reusing last constraints
144 if( m_lockedPoints.PointCount() > 0 )
146
147 m_client.OnGeometryChange( *this );
148 return last;
149}
150
151
153{
157
158 m_client.OnGeometryChange( *this );
159}
160
161
162static SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, SHAPE_LINE_CHAIN aLastPoints )
163{
164 if( aLastPoints.PointCount() < 1 )
165 return SHAPE_LINE_CHAIN();
166
167 const VECTOR2I lastPt = aLastPoints.CPoint( -1 );
168 const VECTOR2D endpointD = aEndPoint;
169 const VECTOR2D lineVec = endpointD - lastPt;
170
171 if( aLastPoints.SegmentCount() < 1 )
172 return SHAPE_LINE_CHAIN(
173 std::vector<VECTOR2I>{ lastPt, lastPt + GetVectorSnapped45( lineVec ) } );
174
175 EDA_ANGLE lineA( lineVec );
176 EDA_ANGLE prevA( GetVectorSnapped45( lastPt - aLastPoints.CPoint( -2 ) ) );
177
178 bool vertical = std::abs( lineVec.y ) > std::abs( lineVec.x );
179 bool horizontal = std::abs( lineVec.y ) < std::abs( lineVec.x );
180
181 double angDiff = std::abs( ( lineA - prevA ).Normalize180().AsDegrees() );
182
183 bool bendEnd = ( angDiff < 45 ) || ( angDiff > 90 && angDiff < 135 );
184
185 if( prevA.Normalize90() == ANGLE_45 || prevA.Normalize90() == -ANGLE_45 )
186 bendEnd = !bendEnd;
187
188 VECTOR2D mid = endpointD;
189
190 if( bendEnd )
191 {
192 if( vertical )
193 {
194 if( lineVec.y > 0 )
195 mid = VECTOR2D( lastPt.x, endpointD.y - std::abs( lineVec.x ) );
196 else
197 mid = VECTOR2D( lastPt.x, endpointD.y + std::abs( lineVec.x ) );
198 }
199 else if( horizontal )
200 {
201 if( lineVec.x > 0 )
202 mid = VECTOR2D( endpointD.x - std::abs( lineVec.y ), lastPt.y );
203 else
204 mid = VECTOR2D( endpointD.x + std::abs( lineVec.y ), lastPt.y );
205 }
206 }
207 else
208 {
209 if( vertical )
210 {
211 if( lineVec.y > 0 )
212 mid = VECTOR2D( endpointD.x, lastPt.y + std::abs( lineVec.x ) );
213 else
214 mid = VECTOR2D( endpointD.x, lastPt.y - std::abs( lineVec.x ) );
215 }
216 else if( horizontal )
217 {
218 if( lineVec.x > 0 )
219 mid = VECTOR2D( lastPt.x + std::abs( lineVec.y ), endpointD.y );
220 else
221 mid = VECTOR2D( lastPt.x - std::abs( lineVec.y ), endpointD.y );
222 }
223 }
224
225 const VECTOR2I midInt = { KiROUND( mid.x ), KiROUND( mid.y ) };
226
227 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, midInt, aEndPoint } );
228}
229
230
232{
233 wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
234 const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
235
236 if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
237 {
238 if( m_lockedPoints.PointCount() > 0 )
239 {
242 }
243 }
244 else
245 {
246 // direct segment
247 m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
249 }
250
251 m_client.OnGeometryChange( *this );
252}
EDA_ANGLE Normalize90()
Definition: eda_angle.h:283
"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.
bool IsSelfIntersecting(bool aIncludeLeaderPts) const
Check whether the locked points constitute a self-intersecting outline.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
std::optional< VECTOR2I > DeleteLastCorner()
Remove the last-added point from the polygon.
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.
virtual const VECTOR2I GetPoint(int aIndex) const override
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:436
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:424
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:587