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.

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