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
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 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 {
141 }
142
143 // update the new last segment (was previously
144 // locked in), reusing last constraints
145 if( m_lockedPoints.PointCount() > 0 )
147
148 m_client.OnGeometryChange( *this );
149 return last;
150}
151
152
154{
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
231
233{
234 wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
235 const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
236
237 if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
238 {
239 if( m_lockedPoints.PointCount() > 0 )
240 {
243 }
244 }
245 else
246 {
247 // direct segment
248 m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
250 }
251
252 m_client.OnGeometryChange( *this );
253}
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
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.
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.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:400
static SHAPE_LINE_CHAIN build45DegLeader(const VECTOR2I &aEndPoint, const SHAPE_LINE_CHAIN &aLastPoints)
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694