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, see <https://www.gnu.org/licenses/>.
18 */
19#include <limits>
20#include <vector>
21
23
26
27
33
34
36{
37 // if this is the first point, make sure the client is happy
38 // for us to continue
39 if( !IsPolygonInProgress() && !m_client.OnFirstPoint( *this ) )
40 return false;
41
42 if( m_leaderPts.PointCount() > 1 )
43 {
44 // there are enough leader points - the next
45 // locked-in point is the end of the last leader
46 // segment
47 m_lockedPoints.Append( m_leaderPts.CPoints()[ m_leaderPts.PointCount() - 2 ] );
48 m_lockedPoints.Append( m_leaderPts.CLastPoint() );
49 }
50 else
51 {
52 // no leader lines, directly add the cursor
53 m_lockedPoints.Append( aPt );
54 }
55
56 // check for self-intersections
58 {
59 m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
60 return false;
61 }
62
63 if( m_lockedPoints.PointCount() > 0 )
65
66 m_client.OnGeometryChange( *this );
67 return true;
68}
69
70
72{
73
74 m_client.OnComplete( *this );
75}
76
77
82
83
84bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const
85{
87
88 if( aIncludeLeaderPts )
89 {
90 for( int i = 0; i < m_leaderPts.PointCount(); ++i )
91 {
92 if( m_leaderPts.CPoint( i ) != pts.CPoint( 0 ) )
93 pts.Append( m_leaderPts.CPoint( i ) );
94 }
95 }
96
97 // line chain needs to be set as closed for proper checks
98 pts.SetClosed( true );
99
100 return !!pts.SelfIntersecting();
101}
102
103
105{
106 if( m_lockedPoints.PointCount() > 0 )
107 updateTemporaryLines( aPos );
108}
109
110
112{
113 return m_lockedPoints.PointCount() > 0;
114}
115
116
118{
119 return m_lockedPoints.PointCount();
120}
121
122
124{
125 return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
126}
127
128
130{
131 std::optional<VECTOR2I> last;
132
133 if( m_lockedPoints.PointCount() > 0 )
134 {
135 last = m_lockedPoints.GetPoint( m_lockedPoints.PointCount() - 1 );
136 m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
137 }
138
139 // update the new last segment (was previously
140 // locked in), reusing last constraints
141 if( m_lockedPoints.PointCount() > 0 )
142 updateTemporaryLines( m_leaderPts.CLastPoint() );
143
144 m_client.OnGeometryChange( *this );
145 return last;
146}
147
148
150{
151 m_lockedPoints.Clear();
152 m_leaderPts.Clear();
153 m_loopPts.Clear();
154
155 m_client.OnGeometryChange( *this );
156}
157
158
159static SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, const SHAPE_LINE_CHAIN& aLastPoints )
160{
161 if( aLastPoints.PointCount() < 1 )
162 return SHAPE_LINE_CHAIN();
163
164 const VECTOR2I lastPt = aLastPoints.CLastPoint();
165 const VECTOR2D endpointD = aEndPoint;
166 const VECTOR2D lineVec = endpointD - lastPt;
167
168 if( aLastPoints.SegmentCount() < 1 )
169 return SHAPE_LINE_CHAIN(
170 std::vector<VECTOR2I>{ lastPt, lastPt + GetVectorSnapped45( lineVec ) } );
171
172 EDA_ANGLE lineA( lineVec );
173 EDA_ANGLE prevA( GetVectorSnapped45( lastPt - aLastPoints.CPoints()[ aLastPoints.PointCount() - 2 ] ) );
174
175 bool vertical = std::abs( lineVec.y ) > std::abs( lineVec.x );
176 bool horizontal = std::abs( lineVec.y ) < std::abs( lineVec.x );
177
178 double angDiff = std::abs( ( lineA - prevA ).Normalize180().AsDegrees() );
179
180 bool bendEnd = ( angDiff < 45 ) || ( angDiff > 90 && angDiff < 135 );
181
182 if( prevA.Normalize90() == ANGLE_45 || prevA.Normalize90() == -ANGLE_45 )
183 bendEnd = !bendEnd;
184
185 VECTOR2D mid = endpointD;
186
187 if( bendEnd )
188 {
189 if( vertical )
190 {
191 if( lineVec.y > 0 )
192 mid = VECTOR2D( lastPt.x, endpointD.y - std::abs( lineVec.x ) );
193 else
194 mid = VECTOR2D( lastPt.x, endpointD.y + std::abs( lineVec.x ) );
195 }
196 else if( horizontal )
197 {
198 if( lineVec.x > 0 )
199 mid = VECTOR2D( endpointD.x - std::abs( lineVec.y ), lastPt.y );
200 else
201 mid = VECTOR2D( endpointD.x + std::abs( lineVec.y ), lastPt.y );
202 }
203 }
204 else
205 {
206 if( vertical )
207 {
208 if( lineVec.y > 0 )
209 mid = VECTOR2D( endpointD.x, lastPt.y + std::abs( lineVec.x ) );
210 else
211 mid = VECTOR2D( endpointD.x, lastPt.y - std::abs( lineVec.x ) );
212 }
213 else if( horizontal )
214 {
215 if( lineVec.x > 0 )
216 mid = VECTOR2D( lastPt.x + std::abs( lineVec.y ), endpointD.y );
217 else
218 mid = VECTOR2D( lastPt.x - std::abs( lineVec.y ), endpointD.y );
219 }
220 }
221
222 const VECTOR2I midInt = KiROUND( mid.x, mid.y );
223
224 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, midInt, aEndPoint } );
225}
226
227static SHAPE_LINE_CHAIN build90DegLeader( const VECTOR2I& aEndPoint, const SHAPE_LINE_CHAIN& aLastPoints )
228{
229 if( aLastPoints.PointCount() < 1 )
230 return SHAPE_LINE_CHAIN();
231
232 const VECTOR2I lastPt = aLastPoints.CLastPoint();
233
234 if( lastPt.x == aEndPoint.x || lastPt.y == aEndPoint.y )
235 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, aEndPoint } );
236
237 VECTOR2I mid( aEndPoint.x, lastPt.y );
238 return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, mid, aEndPoint } );
239}
240
241
243{
244 wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
245 const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
246
247 if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
248 {
249 if( m_lockedPoints.PointCount() > 0 )
250 {
252 m_loopPts = build45DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
253 }
254 }
255 else if( m_leaderMode == LEADER_MODE::DEG90 || aModifier == LEADER_MODE::DEG90 )
256 {
257 if( m_lockedPoints.PointCount() > 0 )
258 {
260 m_loopPts = build90DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
261 }
262 }
263 else
264 {
265 // direct segment
266 m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
267 m_loopPts.Clear();
268 }
269
270 m_client.OnGeometryChange( *this );
271}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
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:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682