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 The 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
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
51 m_lockedPoints.Append( m_leaderPts.CPoints()[ m_leaderPts.PointCount() - 2 ] );
52 m_lockedPoints.Append( m_leaderPts.CLastPoint() );
53 }
54 else
55 {
56 // no leader lines, directly add the cursor
57 m_lockedPoints.Append( aPt );
58 }
59
60 // check for self-intersections
62 {
63 m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
64 return false;
65 }
66
67 if( m_lockedPoints.PointCount() > 0 )
69
70 m_client.OnGeometryChange( *this );
71 return true;
72}
73
74
76{
77
78 m_client.OnComplete( *this );
79}
80
81
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 if( m_lockedPoints.PointCount() > 0 )
111 updateTemporaryLines( aPos );
112}
113
114
116{
117 return m_lockedPoints.PointCount() > 0;
118}
119
120
122{
123 return m_lockedPoints.PointCount();
124}
125
126
128{
129 return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
130}
131
132
134{
135 std::optional<VECTOR2I> last;
136
137 if( m_lockedPoints.PointCount() > 0 )
138 {
139 last = m_lockedPoints.GetPoint( m_lockedPoints.PointCount() - 1 );
140 m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
141 }
142
143 // update the new last segment (was previously
144 // locked in), reusing last constraints
145 if( m_lockedPoints.PointCount() > 0 )
146 updateTemporaryLines( m_leaderPts.CLastPoint() );
147
148 m_client.OnGeometryChange( *this );
149 return last;
150}
151
152
154{
155 m_lockedPoints.Clear();
156 m_leaderPts.Clear();
157 m_loopPts.Clear();
158
159 m_client.OnGeometryChange( *this );
160}
161
162
163static SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, const SHAPE_LINE_CHAIN& aLastPoints )
164{
165 if( aLastPoints.PointCount() < 1 )
166 return SHAPE_LINE_CHAIN();
167
168 const VECTOR2I lastPt = aLastPoints.CLastPoint();
169 const VECTOR2D endpointD = aEndPoint;
170 const VECTOR2D lineVec = endpointD - lastPt;
171
172 if( aLastPoints.SegmentCount() < 1 )
173 return SHAPE_LINE_CHAIN(
174 std::vector<VECTOR2I>{ lastPt, lastPt + GetVectorSnapped45( lineVec ) } );
175
176 EDA_ANGLE lineA( lineVec );
177 EDA_ANGLE prevA( GetVectorSnapped45( lastPt - aLastPoints.CPoints()[ aLastPoints.PointCount() - 2 ] ) );
178
179 bool vertical = std::abs( lineVec.y ) > std::abs( lineVec.x );
180 bool horizontal = std::abs( lineVec.y ) < std::abs( lineVec.x );
181
182 double angDiff = std::abs( ( lineA - prevA ).Normalize180().AsDegrees() );
183
184 bool bendEnd = ( angDiff < 45 ) || ( angDiff > 90 && angDiff < 135 );
185
186 if( prevA.Normalize90() == ANGLE_45 || prevA.Normalize90() == -ANGLE_45 )
187 bendEnd = !bendEnd;
188
189 VECTOR2D mid = endpointD;
190
191 if( bendEnd )
192 {
193 if( vertical )
194 {
195 if( lineVec.y > 0 )
196 mid = VECTOR2D( lastPt.x, endpointD.y - std::abs( lineVec.x ) );
197 else
198 mid = VECTOR2D( lastPt.x, endpointD.y + std::abs( lineVec.x ) );
199 }
200 else if( horizontal )
201 {
202 if( lineVec.x > 0 )
203 mid = VECTOR2D( endpointD.x - std::abs( lineVec.y ), lastPt.y );
204 else
205 mid = VECTOR2D( endpointD.x + std::abs( lineVec.y ), lastPt.y );
206 }
207 }
208 else
209 {
210 if( vertical )
211 {
212 if( lineVec.y > 0 )
213 mid = VECTOR2D( endpointD.x, lastPt.y + std::abs( lineVec.x ) );
214 else
215 mid = VECTOR2D( endpointD.x, lastPt.y - std::abs( lineVec.x ) );
216 }
217 else if( horizontal )
218 {
219 if( lineVec.x > 0 )
220 mid = VECTOR2D( lastPt.x + std::abs( lineVec.y ), endpointD.y );
221 else
222 mid = VECTOR2D( lastPt.x - std::abs( lineVec.y ), endpointD.y );
223 }
224 }
225
226 const VECTOR2I midInt = { KiROUND( mid.x ), KiROUND( mid.y ) };
227
228 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, midInt, aEndPoint } );
229}
230
231static SHAPE_LINE_CHAIN build90DegLeader( const VECTOR2I& aEndPoint, const SHAPE_LINE_CHAIN& aLastPoints )
232{
233 if( aLastPoints.PointCount() < 1 )
234 return SHAPE_LINE_CHAIN();
235
236 const VECTOR2I lastPt = aLastPoints.CLastPoint();
237
238 if( lastPt.x == aEndPoint.x || lastPt.y == aEndPoint.y )
239 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, aEndPoint } );
240
241 VECTOR2I mid( aEndPoint.x, lastPt.y );
242 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, mid, aEndPoint } );
243}
244
245
247{
248 wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
249 const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
250
251 if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
252 {
253 if( m_lockedPoints.PointCount() > 0 )
254 {
256 m_loopPts = build45DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
257 }
258 }
259 else if( m_leaderMode == LEADER_MODE::DEG90 || aModifier == LEADER_MODE::DEG90 )
260 {
261 if( m_lockedPoints.PointCount() > 0 )
262 {
264 m_loopPts = build90DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
265 }
266 }
267 else
268 {
269 // direct segment
270 m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
271 m_loopPts.Clear();
272 }
273
274 m_client.OnGeometryChange( *this );
275}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
EDA_ANGLE Normalize90()
Definition eda_angle.h:257
"Listener" interface for a class that wants to be updated about polygon geometry changes
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.
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.
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 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.
const std::vector< VECTOR2I > & CPoints() const
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
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.
LEADER_MODE
The kind of the leader line.
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
static SHAPE_LINE_CHAIN build90DegLeader(const VECTOR2I &aEndPoint, const SHAPE_LINE_CHAIN &aLastPoints)
static SHAPE_LINE_CHAIN build45DegLeader(const VECTOR2I &aEndPoint, const SHAPE_LINE_CHAIN &aLastPoints)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694