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.

422 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2012 KiCad Developers, see change_log.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 <plot_common.h>
  31. #include <trigo.h>
  32. #include <wxstruct.h>
  33. #include <bezier_curves.h>
  34. #include <richio.h>
  35. #include <base_units.h>
  36. #include <general.h>
  37. #include <protos.h>
  38. #include <lib_bezier.h>
  39. #include <transform.h>
  40. LIB_BEZIER::LIB_BEZIER( LIB_COMPONENT* aParent ) :
  41. LIB_ITEM( LIB_BEZIER_T, aParent )
  42. {
  43. m_Fill = NO_FILL;
  44. m_Width = 0;
  45. m_isFillable = true;
  46. m_typeName = _( "Bezier" );
  47. }
  48. bool LIB_BEZIER::Save( OUTPUTFORMATTER& aFormatter )
  49. {
  50. int ccount = GetCornerCount();
  51. aFormatter.Print( 0, "B %d %d %d %d", ccount, m_Unit, m_Convert, m_Width );
  52. for( unsigned i = 0; i < GetCornerCount(); i++ )
  53. {
  54. aFormatter.Print( 0, " %d %d", m_BezierPoints[i].x, m_BezierPoints[i].y );
  55. }
  56. aFormatter.Print( 0, " %c\n", fill_tab[m_Fill] );
  57. return true;
  58. }
  59. bool LIB_BEZIER::Load( LINE_READER& aLineReader, wxString& aErrorMsg )
  60. {
  61. char* p;
  62. int i, ccount = 0;
  63. wxPoint pt;
  64. char* line = (char*) aLineReader;
  65. i = sscanf( line + 2, "%d %d %d %d", &ccount, &m_Unit, &m_Convert, &m_Width );
  66. if( i !=4 )
  67. {
  68. aErrorMsg.Printf( _( "Bezier only had %d parameters of the required 4" ), i );
  69. return false;
  70. }
  71. if( ccount <= 0 )
  72. {
  73. aErrorMsg.Printf( _( "Bezier count parameter %d is invalid" ), ccount );
  74. return false;
  75. }
  76. p = strtok( line + 2, " \t\n" );
  77. p = strtok( NULL, " \t\n" );
  78. p = strtok( NULL, " \t\n" );
  79. p = strtok( NULL, " \t\n" );
  80. for( i = 0; i < ccount; i++ )
  81. {
  82. wxPoint point;
  83. p = strtok( NULL, " \t\n" );
  84. if( sscanf( p, "%d", &pt.x ) != 1 )
  85. {
  86. aErrorMsg.Printf( _( "Bezier point %d X position not defined" ), i );
  87. return false;
  88. }
  89. p = strtok( NULL, " \t\n" );
  90. if( sscanf( p, "%d", &pt.y ) != 1 )
  91. {
  92. aErrorMsg.Printf( _( "Bezier point %d Y position not defined" ), i );
  93. return false;
  94. }
  95. m_BezierPoints.push_back( pt );
  96. }
  97. m_Fill = NO_FILL;
  98. if( ( p = strtok( NULL, " \t\n" ) ) != NULL )
  99. {
  100. if( p[0] == 'F' )
  101. m_Fill = FILLED_SHAPE;
  102. if( p[0] == 'f' )
  103. m_Fill = FILLED_WITH_BG_BODYCOLOR;
  104. }
  105. return true;
  106. }
  107. EDA_ITEM* LIB_BEZIER::Clone() const
  108. {
  109. return new LIB_BEZIER( *this );
  110. }
  111. int LIB_BEZIER::compare( const LIB_ITEM& aOther ) const
  112. {
  113. wxASSERT( aOther.Type() == LIB_BEZIER_T );
  114. const LIB_BEZIER* tmp = ( LIB_BEZIER* ) &aOther;
  115. if( m_BezierPoints.size() != tmp->m_BezierPoints.size() )
  116. return m_BezierPoints.size() - tmp->m_BezierPoints.size();
  117. for( size_t i = 0; i < m_BezierPoints.size(); i++ )
  118. {
  119. if( m_BezierPoints[i].x != tmp->m_BezierPoints[i].x )
  120. return m_BezierPoints[i].x - tmp->m_BezierPoints[i].x;
  121. if( m_BezierPoints[i].y != tmp->m_BezierPoints[i].y )
  122. return m_BezierPoints[i].y - tmp->m_BezierPoints[i].y;
  123. }
  124. return 0;
  125. }
  126. void LIB_BEZIER::SetOffset( const wxPoint& aOffset )
  127. {
  128. size_t i;
  129. for( i = 0; i < m_BezierPoints.size(); i++ )
  130. m_BezierPoints[i] += aOffset;
  131. for( i = 0; i < m_PolyPoints.size(); i++ )
  132. m_PolyPoints[i] += aOffset;
  133. }
  134. bool LIB_BEZIER::Inside( EDA_RECT& aRect ) const
  135. {
  136. for( size_t i = 0; i < m_PolyPoints.size(); i++ )
  137. {
  138. if( aRect.Contains( m_PolyPoints[i].x, -m_PolyPoints[i].y ) )
  139. return true;
  140. }
  141. return false;
  142. }
  143. void LIB_BEZIER::Move( const wxPoint& aPosition )
  144. {
  145. SetOffset( aPosition - m_PolyPoints[0] );
  146. }
  147. void LIB_BEZIER::MirrorHorizontal( const wxPoint& aCenter )
  148. {
  149. size_t i, imax = m_PolyPoints.size();
  150. for( i = 0; i < imax; i++ )
  151. {
  152. m_PolyPoints[i].x -= aCenter.x;
  153. m_PolyPoints[i].x *= -1;
  154. m_PolyPoints[i].x += aCenter.x;
  155. }
  156. imax = m_BezierPoints.size();
  157. for( i = 0; i < imax; i++ )
  158. {
  159. m_BezierPoints[i].x -= aCenter.x;
  160. m_BezierPoints[i].x *= -1;
  161. m_BezierPoints[i].x += aCenter.x;
  162. }
  163. }
  164. void LIB_BEZIER::MirrorVertical( const wxPoint& aCenter )
  165. {
  166. size_t i, imax = m_PolyPoints.size();
  167. for( i = 0; i < imax; i++ )
  168. {
  169. m_PolyPoints[i].y -= aCenter.y;
  170. m_PolyPoints[i].y *= -1;
  171. m_PolyPoints[i].y += aCenter.y;
  172. }
  173. imax = m_BezierPoints.size();
  174. for( i = 0; i < imax; i++ )
  175. {
  176. m_BezierPoints[i].y -= aCenter.y;
  177. m_BezierPoints[i].y *= -1;
  178. m_BezierPoints[i].y += aCenter.y;
  179. }
  180. }
  181. void LIB_BEZIER::Rotate( const wxPoint& aCenter, bool aRotateCCW )
  182. {
  183. int rot_angle = aRotateCCW ? -900 : 900;
  184. size_t i, imax = m_PolyPoints.size();
  185. for( i = 0; i < imax; i++ )
  186. {
  187. RotatePoint( &m_PolyPoints[i], aCenter, rot_angle );
  188. }
  189. imax = m_BezierPoints.size();
  190. for( i = 0; i < imax; i++ )
  191. {
  192. RotatePoint( &m_BezierPoints[i], aCenter, rot_angle );
  193. }
  194. }
  195. void LIB_BEZIER::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  196. const TRANSFORM& aTransform )
  197. {
  198. wxASSERT( aPlotter != NULL );
  199. static std::vector< wxPoint > cornerList;
  200. cornerList.clear();
  201. for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
  202. {
  203. wxPoint pos = m_PolyPoints[ii];
  204. pos = aTransform.TransformCoordinate( pos ) + aOffset;
  205. cornerList.push_back( pos );
  206. }
  207. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  208. {
  209. aPlotter->SetColor( ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  210. aPlotter->PlotPoly( cornerList, FILLED_WITH_BG_BODYCOLOR, 0 );
  211. }
  212. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  213. aPlotter->SetColor( ReturnLayerColor( LAYER_DEVICE ) );
  214. aPlotter->PlotPoly( cornerList, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  215. }
  216. int LIB_BEZIER::GetPenSize() const
  217. {
  218. return ( m_Width == 0 ) ? GetDefaultLineThickness() : m_Width;
  219. }
  220. void LIB_BEZIER::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset,
  221. EDA_COLOR_T aColor, GR_DRAWMODE aDrawMode, void* aData,
  222. const TRANSFORM& aTransform )
  223. {
  224. wxPoint pos1;
  225. std::vector<wxPoint> PolyPointsTraslated;
  226. EDA_COLOR_T color = ReturnLayerColor( LAYER_DEVICE );
  227. m_PolyPoints = Bezier2Poly( m_BezierPoints[0],
  228. m_BezierPoints[1],
  229. m_BezierPoints[2],
  230. m_BezierPoints[3] );
  231. PolyPointsTraslated.clear();
  232. for( unsigned int i = 0; i < m_PolyPoints.size() ; i++ )
  233. PolyPointsTraslated.push_back( aTransform.TransformCoordinate( m_PolyPoints[i] ) +
  234. aOffset );
  235. if( aColor < 0 ) // Used normal color or selected color
  236. {
  237. if( IsSelected() )
  238. color = GetItemSelectedColor();
  239. }
  240. else
  241. {
  242. color = aColor;
  243. }
  244. FILL_T fill = aData ? NO_FILL : m_Fill;
  245. if( aColor >= 0 )
  246. fill = NO_FILL;
  247. GRSetDrawMode( aDC, aDrawMode );
  248. if( fill == FILLED_WITH_BG_BODYCOLOR )
  249. GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
  250. &PolyPointsTraslated[0], 1, GetPenSize(),
  251. (m_Flags & IS_MOVED) ? color : ReturnLayerColor( LAYER_DEVICE_BACKGROUND ),
  252. ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  253. else if( fill == FILLED_SHAPE )
  254. GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
  255. &PolyPointsTraslated[0], 1, GetPenSize(), color, color );
  256. else
  257. GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
  258. &PolyPointsTraslated[0], 0, GetPenSize(), color, color );
  259. /* Set to one (1) to draw bounding box around bezier curve to validate
  260. * bounding box calculation. */
  261. #if 0
  262. EDA_RECT bBox = GetBoundingBox();
  263. bBox.Inflate( m_Thickness + 1, m_Thickness + 1 );
  264. GRRect( aPanel->GetClipBox(), aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  265. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  266. #endif
  267. }
  268. bool LIB_BEZIER::HitTest( const wxPoint& aRefPos )
  269. {
  270. int mindist = GetPenSize() / 2;
  271. // Have a minimal tolerance for hit test
  272. if ( mindist < MINIMUM_SELECTION_DISTANCE )
  273. mindist = MINIMUM_SELECTION_DISTANCE;
  274. return HitTest( aRefPos, mindist, DefaultTransform );
  275. }
  276. bool LIB_BEZIER::HitTest( wxPoint aPosRef, int aThreshold, const TRANSFORM& aTransform )
  277. {
  278. wxPoint ref, start, end;
  279. if( aThreshold < 0 )
  280. aThreshold = GetPenSize() / 2;
  281. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  282. {
  283. start = aTransform.TransformCoordinate( m_PolyPoints[ii - 1] );
  284. end = aTransform.TransformCoordinate( m_PolyPoints[ii] );
  285. if ( TestSegmentHit( aPosRef, start, end, aThreshold ) )
  286. return true;
  287. }
  288. return false;
  289. }
  290. EDA_RECT LIB_BEZIER::GetBoundingBox() const
  291. {
  292. EDA_RECT rect;
  293. int xmin, xmax, ymin, ymax;
  294. if( !GetCornerCount() )
  295. return rect;
  296. xmin = xmax = m_PolyPoints[0].x;
  297. ymin = ymax = m_PolyPoints[0].y;
  298. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  299. {
  300. xmin = std::min( xmin, m_PolyPoints[ii].x );
  301. xmax = std::max( xmax, m_PolyPoints[ii].x );
  302. ymin = std::min( ymin, m_PolyPoints[ii].y );
  303. ymax = std::max( ymax, m_PolyPoints[ii].y );
  304. }
  305. rect.SetOrigin( xmin, - ymin );
  306. rect.SetEnd( xmax, - ymax );
  307. rect.Inflate( m_Width / 2 );
  308. return rect;
  309. }
  310. void LIB_BEZIER::DisplayInfo( EDA_DRAW_FRAME* aFrame )
  311. {
  312. wxString msg;
  313. EDA_RECT bBox = GetBoundingBox();
  314. LIB_ITEM::DisplayInfo( aFrame );
  315. msg = ReturnStringFromValue( g_UserUnit, m_Width, true );
  316. aFrame->AppendMsgPanel( _( "Line width" ), msg, BLUE );
  317. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  318. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  319. aFrame->AppendMsgPanel( _( "Bounding box" ), msg, BROWN );
  320. }