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  if( m_leaderPts.CPoint( i ) != pts.CPoint( 0 ) )
92  pts.Append( m_leaderPts.CPoint( i ) );
93  }
94 
95  // line chain needs to be set as closed for proper checks
96  pts.SetClosed( true );
97 
98  return !!pts.SelfIntersecting();
99 }
100 
101 
103 {
104  updateLeaderPoints( aPos );
105 }
106 
107 
109 {
110  return m_lockedPoints.PointCount() > 0;
111 }
112 
113 
115 {
116  return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
117 }
118 
119 
121 {
122  if( m_lockedPoints.PointCount() > 0 )
123  {
125  }
126 
127  // update the new last segment (was previously
128  // locked in), reusing last constraints
129  if( m_lockedPoints.PointCount() > 0 )
130  {
132  }
133 
134  m_client.OnGeometryChange( *this );
135 }
136 
137 
139 {
141  m_leaderPts.Clear();
142 
143  m_client.OnGeometryChange( *this );
144 }
145 
146 
148 {
149  wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
150  const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
151 
152  if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
153  {
154  const VECTOR2I line_vec( aEndPoint - last_pt );
155  // get a restricted 45/H/V line from the last fixed point to the cursor
156  auto new_end = last_pt + GetVectorSnapped45( line_vec );
157  OPT_VECTOR2I pt = boost::make_optional( false, VECTOR2I() );
158 
159  if( m_lockedPoints.SegmentCount() > 1 )
160  {
161  const VECTOR2I& start_pt = m_lockedPoints.CPoint( 0 );
162  VECTOR2I completed_vec( start_pt - new_end );
163 
164  if( completed_vec != GetVectorSnapped45( completed_vec ) )
165  {
166  SEG v_first( new_end, VECTOR2I( new_end.x, start_pt.y ) );
167  SEG h_first( new_end, VECTOR2I( start_pt.x, new_end.y ) );
168 
169  SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
170  auto v_hits = m_lockedPoints.Intersect( v_first, intersections );
171  v_hits += m_lockedPoints.Intersect( SEG( v_first.B, start_pt ), intersections );
172  pt = v_first.B;
173 
174  if( v_hits > 0 )
175  {
176  intersections.clear();
177  auto h_hits = m_lockedPoints.Intersect( h_first, intersections );
178  h_hits += m_lockedPoints.Intersect( SEG( h_first.B, start_pt ), intersections );
179 
180  if( h_hits < v_hits )
181  pt = h_first.B;
182  }
183  }
184  }
185 
186  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, new_end } );
187 
188  if( pt )
189  {
190  // This checks for backtracking from the point to intersection
191  if( SEG( last_pt, new_end ).Collinear( SEG( new_end, *pt ) ) )
192  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, *pt } );
193  else
194  m_leaderPts.Append( *pt );
195  }
196  }
197  else
198  {
199  // direct segment
200  m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
201  }
202 
203  m_client.OnGeometryChange( *this );
204 }
virtual void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr)=0
Sent when the polygon geometry changes
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)
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.
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.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec)
Snap a vector onto the nearest 0, 45 or 90 degree line.
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