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.

362 lines
9.3 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2019 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 <fctsys.h>
  24. #include <gr_basic.h>
  25. #include <macros.h>
  26. #include <sch_draw_panel.h>
  27. #include <plotter.h>
  28. #include <trigo.h>
  29. #include <bezier_curves.h>
  30. #include <base_units.h>
  31. #include <msgpanel.h>
  32. #include <general.h>
  33. #include <lib_bezier.h>
  34. #include <transform.h>
  35. LIB_BEZIER::LIB_BEZIER( LIB_PART* aParent ) :
  36. LIB_ITEM( LIB_BEZIER_T, aParent )
  37. {
  38. m_Fill = NO_FILL;
  39. m_Width = 0;
  40. m_isFillable = true;
  41. }
  42. EDA_ITEM* LIB_BEZIER::Clone() const
  43. {
  44. return new LIB_BEZIER( *this );
  45. }
  46. int LIB_BEZIER::compare( const LIB_ITEM& aOther ) const
  47. {
  48. wxASSERT( aOther.Type() == LIB_BEZIER_T );
  49. const LIB_BEZIER* tmp = ( LIB_BEZIER* ) &aOther;
  50. if( m_BezierPoints.size() != tmp->m_BezierPoints.size() )
  51. return m_BezierPoints.size() - tmp->m_BezierPoints.size();
  52. for( size_t i = 0; i < m_BezierPoints.size(); i++ )
  53. {
  54. if( m_BezierPoints[i].x != tmp->m_BezierPoints[i].x )
  55. return m_BezierPoints[i].x - tmp->m_BezierPoints[i].x;
  56. if( m_BezierPoints[i].y != tmp->m_BezierPoints[i].y )
  57. return m_BezierPoints[i].y - tmp->m_BezierPoints[i].y;
  58. }
  59. return 0;
  60. }
  61. void LIB_BEZIER::Offset( const wxPoint& aOffset )
  62. {
  63. size_t i;
  64. for( i = 0; i < m_BezierPoints.size(); i++ )
  65. m_BezierPoints[i] += aOffset;
  66. for( i = 0; i < m_PolyPoints.size(); i++ )
  67. m_PolyPoints[i] += aOffset;
  68. }
  69. bool LIB_BEZIER::Inside( EDA_RECT& aRect ) const
  70. {
  71. for( size_t i = 0; i < m_PolyPoints.size(); i++ )
  72. {
  73. if( aRect.Contains( m_PolyPoints[i].x, -m_PolyPoints[i].y ) )
  74. return true;
  75. }
  76. return false;
  77. }
  78. void LIB_BEZIER::MoveTo( const wxPoint& aPosition )
  79. {
  80. if ( !m_PolyPoints.size() )
  81. m_PolyPoints.emplace_back(0, 0 );
  82. Offset( aPosition - m_PolyPoints[ 0 ] );
  83. }
  84. const wxPoint LIB_BEZIER::GetOffset() const
  85. {
  86. if ( !m_PolyPoints.size() )
  87. return wxPoint(0, 0);
  88. return m_PolyPoints[0];
  89. }
  90. void LIB_BEZIER::MirrorHorizontal( const wxPoint& aCenter )
  91. {
  92. size_t i, imax = m_PolyPoints.size();
  93. for( i = 0; i < imax; i++ )
  94. {
  95. m_PolyPoints[i].x -= aCenter.x;
  96. m_PolyPoints[i].x *= -1;
  97. m_PolyPoints[i].x += aCenter.x;
  98. }
  99. imax = m_BezierPoints.size();
  100. for( i = 0; i < imax; i++ )
  101. {
  102. m_BezierPoints[i].x -= aCenter.x;
  103. m_BezierPoints[i].x *= -1;
  104. m_BezierPoints[i].x += aCenter.x;
  105. }
  106. }
  107. void LIB_BEZIER::MirrorVertical( const wxPoint& aCenter )
  108. {
  109. size_t i, imax = m_PolyPoints.size();
  110. for( i = 0; i < imax; i++ )
  111. {
  112. m_PolyPoints[i].y -= aCenter.y;
  113. m_PolyPoints[i].y *= -1;
  114. m_PolyPoints[i].y += aCenter.y;
  115. }
  116. imax = m_BezierPoints.size();
  117. for( i = 0; i < imax; i++ )
  118. {
  119. m_BezierPoints[i].y -= aCenter.y;
  120. m_BezierPoints[i].y *= -1;
  121. m_BezierPoints[i].y += aCenter.y;
  122. }
  123. }
  124. void LIB_BEZIER::Rotate( const wxPoint& aCenter, bool aRotateCCW )
  125. {
  126. int rot_angle = aRotateCCW ? -900 : 900;
  127. for( wxPoint& point : m_PolyPoints )
  128. RotatePoint( &point, aCenter, rot_angle );
  129. for( wxPoint& bezierPoint : m_BezierPoints )
  130. RotatePoint( &bezierPoint, aCenter, rot_angle );
  131. }
  132. void LIB_BEZIER::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  133. const TRANSFORM& aTransform )
  134. {
  135. wxASSERT( aPlotter != NULL );
  136. static std::vector< wxPoint > cornerList;
  137. cornerList.clear();
  138. for( wxPoint pos : m_PolyPoints )
  139. {
  140. pos = aTransform.TransformCoordinate( pos ) + aOffset;
  141. cornerList.push_back( pos );
  142. }
  143. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  144. {
  145. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  146. aPlotter->PlotPoly( cornerList, FILLED_WITH_BG_BODYCOLOR, 0 );
  147. }
  148. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  149. auto pen_size = GetPenSize();
  150. if( !already_filled || pen_size > 0 )
  151. {
  152. pen_size = std::max( 0, pen_size );
  153. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE ) );
  154. aPlotter->PlotPoly( cornerList, already_filled ? NO_FILL : m_Fill, pen_size );
  155. }
  156. }
  157. int LIB_BEZIER::GetPenSize() const
  158. {
  159. if( m_Width > 0 )
  160. return m_Width;
  161. if( m_Width == 0 )
  162. return GetDefaultLineThickness();
  163. return -1; // a value to use a minimal pen size
  164. }
  165. void LIB_BEZIER::print( wxDC* aDC, const wxPoint& aOffset, void* aData,
  166. const TRANSFORM& aTransform )
  167. {
  168. std::vector<wxPoint> PolyPointsTraslated;
  169. COLOR4D color = GetLayerColor( LAYER_DEVICE );
  170. COLOR4D bgColor = GetLayerColor( LAYER_DEVICE_BACKGROUND );
  171. BEZIER_POLY converter( m_BezierPoints );
  172. converter.GetPoly( m_PolyPoints );
  173. PolyPointsTraslated.clear();
  174. for( wxPoint& point : m_PolyPoints )
  175. PolyPointsTraslated.push_back( aTransform.TransformCoordinate( point ) + aOffset );
  176. FILL_T fill = aData ? NO_FILL : m_Fill;
  177. if( fill == FILLED_WITH_BG_BODYCOLOR )
  178. {
  179. GRPoly( nullptr, aDC, m_PolyPoints.size(), &PolyPointsTraslated[0], true, GetPenSize(),
  180. bgColor, bgColor );
  181. }
  182. else if( fill == FILLED_SHAPE )
  183. {
  184. GRPoly( nullptr, aDC, m_PolyPoints.size(), &PolyPointsTraslated[0], true, GetPenSize(),
  185. color, color );
  186. }
  187. else
  188. {
  189. GRPoly( nullptr, aDC, m_PolyPoints.size(), &PolyPointsTraslated[0], false, GetPenSize(),
  190. color, color );
  191. }
  192. }
  193. bool LIB_BEZIER::HitTest( const wxPoint& aRefPos, int aAccuracy ) const
  194. {
  195. int mindist = std::max( aAccuracy + GetPenSize() / 2,
  196. Mils2iu( MINIMUM_SELECTION_DISTANCE ) );
  197. wxPoint start, end;
  198. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  199. {
  200. start = DefaultTransform.TransformCoordinate( m_PolyPoints[ii - 1] );
  201. end = DefaultTransform.TransformCoordinate( m_PolyPoints[ii] );
  202. if ( TestSegmentHit( aRefPos, start, end, mindist ) )
  203. return true;
  204. }
  205. return false;
  206. }
  207. bool LIB_BEZIER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  208. {
  209. if( m_Flags & ( STRUCT_DELETED | SKIP_STRUCT ) )
  210. return false;
  211. EDA_RECT sel = aRect;
  212. if ( aAccuracy )
  213. sel.Inflate( aAccuracy );
  214. if( aContained )
  215. return sel.Contains( GetBoundingBox() );
  216. // Fast test: if aRect is outside the polygon bounding box, rectangles cannot intersect
  217. if( !sel.Intersects( GetBoundingBox() ) )
  218. return false;
  219. // Account for the width of the line
  220. sel.Inflate( GetWidth() / 2 );
  221. unsigned count = m_BezierPoints.size();
  222. for( unsigned ii = 1; ii < count; ii++ )
  223. {
  224. wxPoint vertex = DefaultTransform.TransformCoordinate( m_BezierPoints[ii-1] );
  225. wxPoint vertexNext = DefaultTransform.TransformCoordinate( m_BezierPoints[ii] );
  226. // Test if the point is within aRect
  227. if( sel.Contains( vertex ) )
  228. return true;
  229. // Test if this edge intersects aRect
  230. if( sel.Intersects( vertex, vertexNext ) )
  231. return true;
  232. }
  233. return false;
  234. }
  235. const EDA_RECT LIB_BEZIER::GetBoundingBox() const
  236. {
  237. EDA_RECT rect;
  238. int xmin, xmax, ymin, ymax;
  239. if( !GetCornerCount() )
  240. return rect;
  241. xmin = xmax = m_PolyPoints[0].x;
  242. ymin = ymax = m_PolyPoints[0].y;
  243. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  244. {
  245. xmin = std::min( xmin, m_PolyPoints[ii].x );
  246. xmax = std::max( xmax, m_PolyPoints[ii].x );
  247. ymin = std::min( ymin, m_PolyPoints[ii].y );
  248. ymax = std::max( ymax, m_PolyPoints[ii].y );
  249. }
  250. rect.SetOrigin( xmin, ymin );
  251. rect.SetEnd( xmax, ymax );
  252. rect.Inflate( ( GetPenSize()+1 ) / 2 );
  253. rect.RevertYAxis();
  254. return rect;
  255. }
  256. void LIB_BEZIER::GetMsgPanelInfo( EDA_UNITS aUnits, std::vector<MSG_PANEL_ITEM>& aList )
  257. {
  258. wxString msg;
  259. EDA_RECT bBox = GetBoundingBox();
  260. LIB_ITEM::GetMsgPanelInfo( aUnits, aList );
  261. msg = MessageTextFromValue( aUnits, m_Width, true );
  262. aList.emplace_back( _( "Line Width" ), msg, BLUE );
  263. msg.Printf( wxT( "(%d, %d, %d, %d)" ),
  264. bBox.GetOrigin().x,
  265. bBox.GetOrigin().y,
  266. bBox.GetEnd().x,
  267. bBox.GetEnd().y );
  268. aList.emplace_back( _( "Bounding Box" ), msg, BROWN );
  269. }
  270. wxPoint LIB_BEZIER::GetPosition() const
  271. {
  272. if( !m_PolyPoints.size() )
  273. return wxPoint(0, 0);
  274. return m_PolyPoints[0];
  275. }