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-2020 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 first leader
50  // segment
52  }
53  else
54  {
55  // no leader lines, directly add the cursor
56  m_lockedPoints.Append( aPt );
57  }
58 
59  // check for self-intersections
61  {
63  return false;
64  }
65 
66  m_client.OnGeometryChange( *this );
67  return true;
68 }
69 
70 
72 {
73 
74  m_client.OnComplete( *this );
75 }
76 
77 
79 {
80  m_leaderMode = aMode;
81 }
82 
83 
84 bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const
85 {
86  auto pts( m_lockedPoints );
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  updateLeaderPoints( aPos );
107 }
108 
109 
111 {
112  return m_lockedPoints.PointCount() > 0;
113 }
114 
115 
117 {
118  return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
119 }
120 
121 
123 {
124  if( m_lockedPoints.PointCount() > 0 )
126 
127  // update the new last segment (was previously
128  // locked in), reusing last constraints
129  if( m_lockedPoints.PointCount() > 0 )
131 
132  m_client.OnGeometryChange( *this );
133 }
134 
135 
137 {
139  m_leaderPts.Clear();
140 
141  m_client.OnGeometryChange( *this );
142 }
143 
144 
146 {
147  wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
148  const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
149 
150  if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
151  {
152  const VECTOR2I line_vec( aEndPoint - last_pt );
153  // get a restricted 45/H/V line from the last fixed point to the cursor
154  auto new_end = last_pt + GetVectorSnapped45( line_vec );
155  OPT_VECTOR2I pt = boost::make_optional( false, VECTOR2I() );
156 
157  if( m_lockedPoints.SegmentCount() > 1 )
158  {
159  const VECTOR2I& start_pt = m_lockedPoints.CPoint( 0 );
160  VECTOR2I completed_vec( start_pt - new_end );
161 
162  if( completed_vec != GetVectorSnapped45( completed_vec ) )
163  {
164  SEG v_first( new_end, VECTOR2I( new_end.x, start_pt.y ) );
165  SEG h_first( new_end, VECTOR2I( start_pt.x, new_end.y ) );
166 
167  SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
168  auto v_hits = m_lockedPoints.Intersect( v_first, intersections );
169  v_hits += m_lockedPoints.Intersect( SEG( v_first.B, start_pt ), intersections );
170  pt = v_first.B;
171 
172  if( v_hits > 0 )
173  {
174  intersections.clear();
175  auto h_hits = m_lockedPoints.Intersect( h_first, intersections );
176  h_hits += m_lockedPoints.Intersect( SEG( h_first.B, start_pt ), intersections );
177 
178  if( h_hits < v_hits )
179  pt = h_first.B;
180  }
181  }
182  }
183 
184  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, new_end } );
185 
186  if( pt )
187  {
188  SEG drawn( last_pt, new_end );
189  SEG completed( new_end, *pt );
190 
191  /*
192  * Check for backtracking from the point to intersection. If the snapped path to
193  * completion is shorter than what the user actually drew, we want to discard the
194  * drawn point and just use the snapped completion point.
195  */
196  if( drawn.Collinear( completed ) && drawn.SquaredLength() > completed.SquaredLength() )
197  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, *pt } );
198  else
199  m_leaderPts.Append( *pt );
200  }
201  }
202  else
203  {
204  // direct segment
205  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
206  }
207 
208  m_client.OnGeometryChange( *this );
209 }
virtual void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr)=0
Sent when the polygon geometry changes
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Function Intersect()
std::vector< INTERSECTION > INTERSECTIONS
SHAPE_LINE_CHAIN m_lockedPoints
Point that have been "locked in"
LEADER_MODE
The kind of the leader line.
POLYGON_GEOM_MANAGER(CLIENT &aClient)
ecoord SquaredLength() const
Definition: seg.h:324
LEADER_MODE m_leaderMode
The current mode of the leader line
CLIENT & m_client
The "user" of the polygon data that is informed when the geometry changes
bool NewPointClosesOutline(const VECTOR2I &aPt) const
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
int PointCount() const
Function PointCount()
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
const VECTOR2I & CPoint(int aIndex) const
Function Point()
Unconstrained point-to-point
"Listener" interface for a class that wants to be updated about polygon geometry changes
OPT< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:37
virtual bool OnFirstPoint(POLYGON_GEOM_MANAGER &aMgr)=0
Called before the first point is added - clients can do initialisation here, and can veto the start o...
bool IsSelfIntersecting(bool aIncludeLeaderPts) const
Checks whether the locked points constitute a self-intersecting outline.
a few functions useful in geometry calculations.
void Remove(int aStartIndex, int aEndIndex)
Function Remove()
int SegmentCount() const
Function SegmentCount()
void DeleteLastCorner()
Remove the last-added point from the polygon.
bool Collinear(const SEG &aSeg) const
Function Collinear()
Definition: seg.h:243
Definition: seg.h:39
bool m_intersectionsAllowed
Flag enabling self-intersecting polygons
SHAPE_LINE_CHAIN.
void Reset()
Clear the manager state and start again.
const VECTOR2I & CLastPoint() const
Returns the last point in the line chain.
void Clear()
Function Clear() Removes all points from the line chain.
virtual void OnComplete(const POLYGON_GEOM_MANAGER &aMgr)=0
Called when the polygon is complete
void updateLeaderPoints(const VECTOR2I &aEndPoint, LEADER_MODE aModifier=LEADER_MODE::DIRECT)
Update the leader line points based on a new endpoint (probably a cursor position)
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
void SetFinished()
Mark the polygon finished and update the client.
SHAPE_LINE_CHAIN m_leaderPts
Points in the temporary "leader" line(s)
VECTOR2I B
Definition: seg.h:48