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.

337 lines
9.0 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2017 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 <class_drawpanel.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. SetOffset( aPosition - m_PolyPoints[0] );
  84. }
  85. void LIB_BEZIER::MirrorHorizontal( const wxPoint& aCenter )
  86. {
  87. size_t i, imax = m_PolyPoints.size();
  88. for( i = 0; i < imax; i++ )
  89. {
  90. m_PolyPoints[i].x -= aCenter.x;
  91. m_PolyPoints[i].x *= -1;
  92. m_PolyPoints[i].x += aCenter.x;
  93. }
  94. imax = m_BezierPoints.size();
  95. for( i = 0; i < imax; i++ )
  96. {
  97. m_BezierPoints[i].x -= aCenter.x;
  98. m_BezierPoints[i].x *= -1;
  99. m_BezierPoints[i].x += aCenter.x;
  100. }
  101. }
  102. void LIB_BEZIER::MirrorVertical( const wxPoint& aCenter )
  103. {
  104. size_t i, imax = m_PolyPoints.size();
  105. for( i = 0; i < imax; i++ )
  106. {
  107. m_PolyPoints[i].y -= aCenter.y;
  108. m_PolyPoints[i].y *= -1;
  109. m_PolyPoints[i].y += aCenter.y;
  110. }
  111. imax = m_BezierPoints.size();
  112. for( i = 0; i < imax; i++ )
  113. {
  114. m_BezierPoints[i].y -= aCenter.y;
  115. m_BezierPoints[i].y *= -1;
  116. m_BezierPoints[i].y += aCenter.y;
  117. }
  118. }
  119. void LIB_BEZIER::Rotate( const wxPoint& aCenter, bool aRotateCCW )
  120. {
  121. int rot_angle = aRotateCCW ? -900 : 900;
  122. size_t i, imax = m_PolyPoints.size();
  123. for( i = 0; i < imax; i++ )
  124. {
  125. RotatePoint( &m_PolyPoints[i], aCenter, rot_angle );
  126. }
  127. imax = m_BezierPoints.size();
  128. for( i = 0; i < imax; i++ )
  129. {
  130. RotatePoint( &m_BezierPoints[i], aCenter, rot_angle );
  131. }
  132. }
  133. void LIB_BEZIER::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  134. const TRANSFORM& aTransform )
  135. {
  136. wxASSERT( aPlotter != NULL );
  137. static std::vector< wxPoint > cornerList;
  138. cornerList.clear();
  139. for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
  140. {
  141. wxPoint pos = m_PolyPoints[ii];
  142. pos = aTransform.TransformCoordinate( pos ) + aOffset;
  143. cornerList.push_back( pos );
  144. }
  145. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  146. {
  147. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  148. aPlotter->PlotPoly( cornerList, FILLED_WITH_BG_BODYCOLOR, 0 );
  149. }
  150. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  151. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE ) );
  152. aPlotter->PlotPoly( cornerList, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  153. }
  154. int LIB_BEZIER::GetPenSize() const
  155. {
  156. return ( m_Width == 0 ) ? GetDefaultLineThickness() : m_Width;
  157. }
  158. void LIB_BEZIER::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset,
  159. COLOR4D aColor, GR_DRAWMODE aDrawMode, void* aData,
  160. const TRANSFORM& aTransform )
  161. {
  162. std::vector<wxPoint> PolyPointsTraslated;
  163. COLOR4D color = GetLayerColor( LAYER_DEVICE );
  164. BEZIER_POLY converter( m_BezierPoints );
  165. converter.GetPoly( m_PolyPoints );
  166. PolyPointsTraslated.clear();
  167. for( unsigned int i = 0; i < m_PolyPoints.size() ; i++ )
  168. PolyPointsTraslated.push_back( aTransform.TransformCoordinate( m_PolyPoints[i] ) +
  169. aOffset );
  170. if( aColor == COLOR4D::UNSPECIFIED ) // Used normal color or selected color
  171. {
  172. if( IsSelected() )
  173. color = GetItemSelectedColor();
  174. }
  175. else
  176. {
  177. color = aColor;
  178. }
  179. FILL_T fill = aData ? NO_FILL : m_Fill;
  180. if( aColor != COLOR4D::UNSPECIFIED )
  181. fill = NO_FILL;
  182. GRSetDrawMode( aDC, aDrawMode );
  183. EDA_RECT* const clipbox = aPanel? aPanel->GetClipBox() : NULL;
  184. if( fill == FILLED_WITH_BG_BODYCOLOR )
  185. GRPoly( clipbox, aDC, m_PolyPoints.size(),
  186. &PolyPointsTraslated[0], 1, GetPenSize(),
  187. (m_Flags & IS_MOVED) ? color : GetLayerColor( LAYER_DEVICE_BACKGROUND ),
  188. GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  189. else if( fill == FILLED_SHAPE )
  190. GRPoly( clipbox, aDC, m_PolyPoints.size(),
  191. &PolyPointsTraslated[0], 1, GetPenSize(), color, color );
  192. else
  193. GRPoly( clipbox, aDC, m_PolyPoints.size(),
  194. &PolyPointsTraslated[0], 0, GetPenSize(), color, color );
  195. /* Set to one (1) to draw bounding box around bezier curve to validate
  196. * bounding box calculation. */
  197. #if 0
  198. EDA_RECT bBox = GetBoundingBox();
  199. GRRect( aPanel->GetClipBox(), aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  200. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  201. #endif
  202. }
  203. bool LIB_BEZIER::HitTest( const wxPoint& aRefPos ) const
  204. {
  205. int mindist = GetPenSize() / 2;
  206. // Have a minimal tolerance for hit test
  207. if ( mindist < MINIMUM_SELECTION_DISTANCE )
  208. mindist = MINIMUM_SELECTION_DISTANCE;
  209. return HitTest( aRefPos, mindist, DefaultTransform );
  210. }
  211. bool LIB_BEZIER::HitTest( const wxPoint &aPosRef, int aThreshold, const TRANSFORM& aTransform ) const
  212. {
  213. wxPoint start, end;
  214. if( aThreshold < 0 )
  215. aThreshold = GetPenSize() / 2;
  216. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  217. {
  218. start = aTransform.TransformCoordinate( m_PolyPoints[ii - 1] );
  219. end = aTransform.TransformCoordinate( m_PolyPoints[ii] );
  220. if ( TestSegmentHit( aPosRef, start, end, aThreshold ) )
  221. return true;
  222. }
  223. return false;
  224. }
  225. const EDA_RECT LIB_BEZIER::GetBoundingBox() const
  226. {
  227. EDA_RECT rect;
  228. int xmin, xmax, ymin, ymax;
  229. if( !GetCornerCount() )
  230. return rect;
  231. xmin = xmax = m_PolyPoints[0].x;
  232. ymin = ymax = m_PolyPoints[0].y;
  233. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  234. {
  235. xmin = std::min( xmin, m_PolyPoints[ii].x );
  236. xmax = std::max( xmax, m_PolyPoints[ii].x );
  237. ymin = std::min( ymin, m_PolyPoints[ii].y );
  238. ymax = std::max( ymax, m_PolyPoints[ii].y );
  239. }
  240. rect.SetOrigin( xmin, ymin );
  241. rect.SetEnd( xmax, ymax );
  242. rect.Inflate( ( GetPenSize()+1 ) / 2 );
  243. rect.RevertYAxis();
  244. return rect;
  245. }
  246. void LIB_BEZIER::GetMsgPanelInfo( EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM >& aList )
  247. {
  248. wxString msg;
  249. EDA_RECT bBox = GetBoundingBox();
  250. LIB_ITEM::GetMsgPanelInfo( aUnits, aList );
  251. msg = MessageTextFromValue( aUnits, m_Width, true );
  252. aList.push_back( MSG_PANEL_ITEM( _( "Line Width" ), msg, BLUE ) );
  253. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  254. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  255. aList.push_back( MSG_PANEL_ITEM( _( "Bounding Box" ), msg, BROWN ) );
  256. }