You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
6.2 KiB

  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. #include <preview_items/polygon_geom_manager.h>
  26. #include <geometry/geometry_utils.h>
  27. #include <geometry/shape_line_chain.h>
  28. POLYGON_GEOM_MANAGER::POLYGON_GEOM_MANAGER( CLIENT& aClient ):
  29. m_client( aClient ),
  30. m_leaderMode( LEADER_MODE::DIRECT ),
  31. m_intersectionsAllowed( true )
  32. {}
  33. bool POLYGON_GEOM_MANAGER::AddPoint( const VECTOR2I& aPt )
  34. {
  35. // if this is the first point, make sure the client is happy
  36. // for us to continue
  37. if( !IsPolygonInProgress() && !m_client.OnFirstPoint( *this ) )
  38. return false;
  39. if( m_leaderPts.PointCount() > 1 )
  40. {
  41. // there are enough leader points - the next
  42. // locked-in point is the end of the first leader
  43. // segment
  44. m_lockedPoints.Append( m_leaderPts.CPoint( 1 ) );
  45. }
  46. else
  47. {
  48. // no leader lines, directly add the cursor
  49. m_lockedPoints.Append( aPt );
  50. }
  51. // check for self-intersections
  52. if( !m_intersectionsAllowed && IsSelfIntersecting( false ) )
  53. {
  54. m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
  55. return false;
  56. }
  57. m_client.OnGeometryChange( *this );
  58. return true;
  59. }
  60. void POLYGON_GEOM_MANAGER::SetFinished()
  61. {
  62. m_client.OnComplete( *this );
  63. }
  64. void POLYGON_GEOM_MANAGER::SetLeaderMode( LEADER_MODE aMode )
  65. {
  66. m_leaderMode = aMode;
  67. }
  68. bool POLYGON_GEOM_MANAGER::IsSelfIntersecting( bool aIncludeLeaderPts ) const
  69. {
  70. SHAPE_LINE_CHAIN pts( m_lockedPoints );
  71. if( aIncludeLeaderPts )
  72. {
  73. for( int i = 0; i < m_leaderPts.PointCount(); ++i )
  74. {
  75. if( m_leaderPts.CPoint( i ) != pts.CPoint( 0 ) )
  76. pts.Append( m_leaderPts.CPoint( i ) );
  77. }
  78. }
  79. // line chain needs to be set as closed for proper checks
  80. pts.SetClosed( true );
  81. return !!pts.SelfIntersecting();
  82. }
  83. void POLYGON_GEOM_MANAGER::SetCursorPosition( const VECTOR2I& aPos )
  84. {
  85. updateLeaderPoints( aPos );
  86. }
  87. bool POLYGON_GEOM_MANAGER::IsPolygonInProgress() const
  88. {
  89. return m_lockedPoints.PointCount() > 0;
  90. }
  91. bool POLYGON_GEOM_MANAGER::NewPointClosesOutline( const VECTOR2I& aPt ) const
  92. {
  93. return m_lockedPoints.PointCount() > 0 && m_lockedPoints.CPoint( 0 ) == aPt;
  94. }
  95. void POLYGON_GEOM_MANAGER::DeleteLastCorner()
  96. {
  97. if( m_lockedPoints.PointCount() > 0 )
  98. m_lockedPoints.Remove( m_lockedPoints.PointCount() - 1 );
  99. // update the new last segment (was previously
  100. // locked in), reusing last constraints
  101. if( m_lockedPoints.PointCount() > 0 )
  102. updateLeaderPoints( m_leaderPts.CLastPoint() );
  103. m_client.OnGeometryChange( *this );
  104. }
  105. void POLYGON_GEOM_MANAGER::Reset()
  106. {
  107. m_lockedPoints.Clear();
  108. m_leaderPts.Clear();
  109. m_client.OnGeometryChange( *this );
  110. }
  111. void POLYGON_GEOM_MANAGER::updateLeaderPoints( const VECTOR2I& aEndPoint, LEADER_MODE aModifier )
  112. {
  113. wxCHECK( m_lockedPoints.PointCount() > 0, /*void*/ );
  114. const VECTOR2I& last_pt = m_lockedPoints.CLastPoint();
  115. if( m_leaderMode == LEADER_MODE::DEG45 || aModifier == LEADER_MODE::DEG45 )
  116. {
  117. const VECTOR2I line_vec( aEndPoint - last_pt );
  118. // get a restricted 45/H/V line from the last fixed point to the cursor
  119. auto new_end = last_pt + GetVectorSnapped45( line_vec );
  120. OPT_VECTOR2I pt = boost::make_optional( false, VECTOR2I() );
  121. if( m_lockedPoints.SegmentCount() > 1 )
  122. {
  123. const VECTOR2I& start_pt = m_lockedPoints.CPoint( 0 );
  124. VECTOR2I completed_vec( start_pt - new_end );
  125. if( completed_vec != GetVectorSnapped45( completed_vec ) )
  126. {
  127. SEG v_first( new_end, VECTOR2I( new_end.x, start_pt.y ) );
  128. SEG h_first( new_end, VECTOR2I( start_pt.x, new_end.y ) );
  129. SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
  130. auto v_hits = m_lockedPoints.Intersect( v_first, intersections );
  131. v_hits += m_lockedPoints.Intersect( SEG( v_first.B, start_pt ), intersections );
  132. pt = v_first.B;
  133. if( v_hits > 0 )
  134. {
  135. intersections.clear();
  136. auto h_hits = m_lockedPoints.Intersect( h_first, intersections );
  137. h_hits += m_lockedPoints.Intersect( SEG( h_first.B, start_pt ), intersections );
  138. if( h_hits < v_hits )
  139. pt = h_first.B;
  140. }
  141. }
  142. }
  143. m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, new_end } );
  144. if( pt )
  145. {
  146. SEG drawn( last_pt, new_end );
  147. SEG completed( new_end, *pt );
  148. /*
  149. * Check for backtracking from the point to intersection. If the snapped path to
  150. * completion is shorter than what the user actually drew, we want to discard the
  151. * drawn point and just use the snapped completion point.
  152. */
  153. if( drawn.Collinear( completed ) && drawn.SquaredLength() > completed.SquaredLength() )
  154. m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, *pt } );
  155. else
  156. m_leaderPts.Append( *pt );
  157. }
  158. }
  159. else
  160. {
  161. // direct segment
  162. m_leaderPts = SHAPE_LINE_CHAIN( { last_pt, aEndPoint } );
  163. }
  164. m_client.OnGeometryChange( *this );
  165. }