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.

569 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
  5. * Copyright (C) 2008-2011 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 2004-2011 KiCad Developers, see change_log.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file lib_polyline.cpp
  27. */
  28. #include "fctsys.h"
  29. #include "gr_basic.h"
  30. #include "macros.h"
  31. #include "class_drawpanel.h"
  32. #include "plot_common.h"
  33. #include "trigo.h"
  34. #include "wxstruct.h"
  35. #include "richio.h"
  36. #include "general.h"
  37. #include "protos.h"
  38. #include "lib_polyline.h"
  39. #include "transform.h"
  40. #include <boost/foreach.hpp>
  41. LIB_POLYLINE::LIB_POLYLINE( LIB_COMPONENT* aParent ) :
  42. LIB_ITEM( LIB_POLYLINE_T, aParent )
  43. {
  44. m_Fill = NO_FILL;
  45. m_Width = 0;
  46. m_isFillable = true;
  47. m_typeName = _( "PolyLine" );
  48. }
  49. LIB_POLYLINE::LIB_POLYLINE( const LIB_POLYLINE& polyline ) :
  50. LIB_ITEM( polyline )
  51. {
  52. m_PolyPoints = polyline.m_PolyPoints; // Vector copy
  53. m_Width = polyline.m_Width;
  54. }
  55. bool LIB_POLYLINE::Save( OUTPUTFORMATTER& aFormatter )
  56. {
  57. int ccount = GetCornerCount();
  58. aFormatter.Print( 0, "P %d %d %d %d", ccount, m_Unit, m_Convert, m_Width );
  59. for( unsigned i = 0; i < GetCornerCount(); i++ )
  60. {
  61. aFormatter.Print( 0, " %d %d", m_PolyPoints[i].x, m_PolyPoints[i].y );
  62. }
  63. aFormatter.Print( 0, " %c\n", fill_tab[m_Fill] );
  64. return true;
  65. }
  66. bool LIB_POLYLINE::Load( LINE_READER& aLineReader, wxString& aErrorMsg )
  67. {
  68. char* p;
  69. int i, ccount = 0;
  70. wxPoint pt;
  71. char* line = (char*) aLineReader;
  72. i = sscanf( line + 2, "%d %d %d %d", &ccount, &m_Unit, &m_Convert, &m_Width );
  73. m_Fill = NO_FILL;
  74. if( i < 4 )
  75. {
  76. aErrorMsg.Printf( _( "polyline only had %d parameters of the required 4" ), i );
  77. return false;
  78. }
  79. if( ccount <= 0 )
  80. {
  81. aErrorMsg.Printf( _( "polyline count parameter %d is invalid" ), ccount );
  82. return false;
  83. }
  84. p = strtok( line + 2, " \t\n" );
  85. p = strtok( NULL, " \t\n" );
  86. p = strtok( NULL, " \t\n" );
  87. p = strtok( NULL, " \t\n" );
  88. for( i = 0; i < ccount; i++ )
  89. {
  90. wxPoint point;
  91. p = strtok( NULL, " \t\n" );
  92. if( p == NULL || sscanf( p, "%d", &pt.x ) != 1 )
  93. {
  94. aErrorMsg.Printf( _( "polyline point %d X position not defined" ), i );
  95. return false;
  96. }
  97. p = strtok( NULL, " \t\n" );
  98. if( p == NULL || sscanf( p, "%d", &pt.y ) != 1 )
  99. {
  100. aErrorMsg.Printf( _( "polyline point %d Y position not defined" ), i );
  101. return false;
  102. }
  103. AddPoint( pt );
  104. }
  105. if( ( p = strtok( NULL, " \t\n" ) ) != NULL )
  106. {
  107. if( p[0] == 'F' )
  108. m_Fill = FILLED_SHAPE;
  109. if( p[0] == 'f' )
  110. m_Fill = FILLED_WITH_BG_BODYCOLOR;
  111. }
  112. return true;
  113. }
  114. EDA_ITEM* LIB_POLYLINE::doClone() const
  115. {
  116. return new LIB_POLYLINE( *this );
  117. }
  118. int LIB_POLYLINE::DoCompare( const LIB_ITEM& aOther ) const
  119. {
  120. wxASSERT( aOther.Type() == LIB_POLYLINE_T );
  121. const LIB_POLYLINE* tmp = (LIB_POLYLINE*) &aOther;
  122. if( m_PolyPoints.size() != tmp->m_PolyPoints.size() )
  123. return m_PolyPoints.size() - tmp->m_PolyPoints.size();
  124. for( size_t i = 0; i < m_PolyPoints.size(); i++ )
  125. {
  126. if( m_PolyPoints[i].x != tmp->m_PolyPoints[i].x )
  127. return m_PolyPoints[i].x - tmp->m_PolyPoints[i].x;
  128. if( m_PolyPoints[i].y != tmp->m_PolyPoints[i].y )
  129. return m_PolyPoints[i].y - tmp->m_PolyPoints[i].y;
  130. }
  131. return 0;
  132. }
  133. void LIB_POLYLINE::DoOffset( const wxPoint& aOffset )
  134. {
  135. for( size_t i = 0; i < m_PolyPoints.size(); i++ )
  136. m_PolyPoints[i] += aOffset;
  137. }
  138. bool LIB_POLYLINE::DoTestInside( EDA_RECT& aRect ) const
  139. {
  140. for( size_t i = 0; i < m_PolyPoints.size(); i++ )
  141. {
  142. if( aRect.Contains( m_PolyPoints[i].x, -m_PolyPoints[i].y ) )
  143. return true;
  144. }
  145. return false;
  146. }
  147. void LIB_POLYLINE::DoMove( const wxPoint& aPosition )
  148. {
  149. DoOffset( aPosition - m_PolyPoints[0] );
  150. }
  151. void LIB_POLYLINE::DoMirrorHorizontal( const wxPoint& aCenter )
  152. {
  153. size_t i, imax = m_PolyPoints.size();
  154. for( i = 0; i < imax; i++ )
  155. {
  156. m_PolyPoints[i].x -= aCenter.x;
  157. m_PolyPoints[i].x *= -1;
  158. m_PolyPoints[i].x += aCenter.x;
  159. }
  160. }
  161. void LIB_POLYLINE::DoMirrorVertical( const wxPoint& aCenter )
  162. {
  163. size_t i, imax = m_PolyPoints.size();
  164. for( i = 0; i < imax; i++ )
  165. {
  166. m_PolyPoints[i].y -= aCenter.y;
  167. m_PolyPoints[i].y *= -1;
  168. m_PolyPoints[i].y += aCenter.y;
  169. }
  170. }
  171. void LIB_POLYLINE::DoRotate( const wxPoint& aCenter, bool aRotateCCW )
  172. {
  173. int rot_angle = aRotateCCW ? -900 : 900;
  174. size_t i, imax = m_PolyPoints.size();
  175. for( i = 0; i < imax; i++ )
  176. {
  177. RotatePoint( &m_PolyPoints[i], aCenter, rot_angle );
  178. }
  179. }
  180. void LIB_POLYLINE::DoPlot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  181. const TRANSFORM& aTransform )
  182. {
  183. wxASSERT( aPlotter != NULL );
  184. static std::vector< wxPoint > cornerList;
  185. cornerList.clear();
  186. for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
  187. {
  188. wxPoint pos = m_PolyPoints[ii];
  189. pos = aTransform.TransformCoordinate( pos ) + aOffset;
  190. cornerList.push_back( pos );
  191. }
  192. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  193. {
  194. aPlotter->set_color( ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  195. aPlotter->PlotPoly( cornerList, FILLED_WITH_BG_BODYCOLOR, 0 );
  196. aFill = false; // body is now filled, do not fill it later.
  197. }
  198. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  199. aPlotter->set_color( ReturnLayerColor( LAYER_DEVICE ) );
  200. aPlotter->PlotPoly( cornerList, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  201. }
  202. void LIB_POLYLINE::AddPoint( const wxPoint& point )
  203. {
  204. m_PolyPoints.push_back( point );
  205. }
  206. int LIB_POLYLINE::GetPenSize() const
  207. {
  208. return ( m_Width == 0 ) ? g_DrawDefaultLineThickness : m_Width;
  209. }
  210. void LIB_POLYLINE::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset,
  211. int aColor, int aDrawMode, void* aData,
  212. const TRANSFORM& aTransform )
  213. {
  214. wxPoint pos1;
  215. int color = ReturnLayerColor( LAYER_DEVICE );
  216. wxPoint* buffer = NULL;
  217. if( aColor < 0 ) // Used normal color or selected color
  218. {
  219. if( m_Selected & IS_SELECTED )
  220. color = g_ItemSelectetColor;
  221. }
  222. else
  223. {
  224. color = aColor;
  225. }
  226. buffer = new wxPoint[ m_PolyPoints.size() ];
  227. for( unsigned ii = 0; ii < m_PolyPoints.size(); ii++ )
  228. {
  229. buffer[ii] = aTransform.TransformCoordinate( m_PolyPoints[ii] ) + aOffset;
  230. }
  231. FILL_T fill = aData ? NO_FILL : m_Fill;
  232. if( aColor >= 0 )
  233. fill = NO_FILL;
  234. GRSetDrawMode( aDC, aDrawMode );
  235. if( fill == FILLED_WITH_BG_BODYCOLOR )
  236. GRPoly( &aPanel->m_ClipBox, aDC, m_PolyPoints.size(), buffer, 1, GetPenSize(),
  237. (m_Flags & IS_MOVED) ? color : ReturnLayerColor( LAYER_DEVICE_BACKGROUND ),
  238. ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  239. else if( fill == FILLED_SHAPE )
  240. GRPoly( &aPanel->m_ClipBox, aDC, m_PolyPoints.size(), buffer, 1, GetPenSize(),
  241. color, color );
  242. else
  243. GRPoly( &aPanel->m_ClipBox, aDC, m_PolyPoints.size(), buffer, 0, GetPenSize(),
  244. color, color );
  245. delete[] buffer;
  246. /* Set to one (1) to draw bounding box around polyline to validate
  247. * bounding box calculation. */
  248. #if 0
  249. EDA_RECT bBox = GetBoundingBox();
  250. bBox.Inflate( m_Thickness + 1, m_Thickness + 1 );
  251. GRRect( &aPanel->m_ClipBox, aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  252. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  253. #endif
  254. }
  255. bool LIB_POLYLINE::HitTest( const wxPoint& aPosition )
  256. {
  257. int mindist = GetPenSize() / 2;
  258. // Have a minimal tolerance for hit test
  259. if( mindist < MINIMUM_SELECTION_DISTANCE )
  260. mindist = MINIMUM_SELECTION_DISTANCE;
  261. return HitTest( aPosition, mindist, DefaultTransform );
  262. }
  263. bool LIB_POLYLINE::HitTest( wxPoint aPosition, int aThreshold, const TRANSFORM& aTransform )
  264. {
  265. wxPoint ref, start, end;
  266. if( aThreshold < 0 )
  267. aThreshold = GetPenSize() / 2;
  268. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  269. {
  270. start = aTransform.TransformCoordinate( m_PolyPoints[ii - 1] );
  271. end = aTransform.TransformCoordinate( m_PolyPoints[ii] );
  272. if( TestSegmentHit( aPosition, start, end, aThreshold ) )
  273. return true;
  274. }
  275. return false;
  276. }
  277. EDA_RECT LIB_POLYLINE::GetBoundingBox() const
  278. {
  279. EDA_RECT rect;
  280. int xmin, xmax, ymin, ymax;
  281. xmin = xmax = m_PolyPoints[0].x;
  282. ymin = ymax = m_PolyPoints[0].y;
  283. for( unsigned ii = 1; ii < GetCornerCount(); ii++ )
  284. {
  285. xmin = MIN( xmin, m_PolyPoints[ii].x );
  286. xmax = MAX( xmax, m_PolyPoints[ii].x );
  287. ymin = MIN( ymin, m_PolyPoints[ii].y );
  288. ymax = MAX( ymax, m_PolyPoints[ii].y );
  289. }
  290. rect.SetOrigin( xmin, ymin * -1 );
  291. rect.SetEnd( xmax, ymax * -1 );
  292. rect.Inflate( m_Width / 2, m_Width / 2 );
  293. return rect;
  294. }
  295. void LIB_POLYLINE::DeleteSegment( const wxPoint aPosition )
  296. {
  297. // First segment is kept, only its end point is changed
  298. while( GetCornerCount() > 2 )
  299. {
  300. m_PolyPoints.pop_back();
  301. if( m_PolyPoints[ GetCornerCount() - 1 ] != aPosition )
  302. {
  303. m_PolyPoints[ GetCornerCount() - 1 ] = aPosition;
  304. break;
  305. }
  306. }
  307. }
  308. void LIB_POLYLINE::DisplayInfo( EDA_DRAW_FRAME* aFrame )
  309. {
  310. wxString msg;
  311. EDA_RECT bBox = GetBoundingBox();
  312. LIB_ITEM::DisplayInfo( aFrame );
  313. msg = ReturnStringFromValue( g_UserUnit, m_Width, EESCHEMA_INTERNAL_UNIT, true );
  314. aFrame->AppendMsgPanel( _( "Line width" ), msg, BLUE );
  315. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  316. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  317. aFrame->AppendMsgPanel( _( "Bounding box" ), msg, BROWN );
  318. }
  319. wxString LIB_POLYLINE::GetSelectMenuText() const
  320. {
  321. return wxString::Format( _( "Polyline at (%s, %s) with %u points" ),
  322. GetChars( CoordinateToString( m_PolyPoints[0].x,
  323. EESCHEMA_INTERNAL_UNIT ) ),
  324. GetChars( CoordinateToString( m_PolyPoints[0].y,
  325. EESCHEMA_INTERNAL_UNIT ) ),
  326. m_PolyPoints.size() );
  327. }
  328. void LIB_POLYLINE::BeginEdit( int aEditMode, const wxPoint aPosition )
  329. {
  330. wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  331. wxT( "Invalid edit mode for LIB_POLYLINE object." ) );
  332. if( aEditMode == IS_NEW )
  333. {
  334. m_PolyPoints.push_back( aPosition ); // Start point of first segment.
  335. m_PolyPoints.push_back( aPosition ); // End point of first segment.
  336. }
  337. else if( aEditMode == IS_RESIZED )
  338. {
  339. // Drag one edge point of the polyline
  340. // Find the nearest edge point to be dragged
  341. wxPoint startPoint = m_PolyPoints[0];
  342. // Begin with the first list point as nearest point
  343. int index = 0;
  344. m_ModifyIndex = 0;
  345. m_initialPos = startPoint;
  346. // First distance is the current minimum distance
  347. int distanceMin = (aPosition - startPoint).x * (aPosition - startPoint).x
  348. + (aPosition - startPoint).y * (aPosition - startPoint).y;
  349. wxPoint prevPoint = startPoint;
  350. // Find the right index of the point to be dragged
  351. BOOST_FOREACH( wxPoint point, m_PolyPoints )
  352. {
  353. int distancePoint = (aPosition - point).x * (aPosition - point).x +
  354. (aPosition - point).y * (aPosition - point).y;
  355. if( distancePoint < distanceMin )
  356. {
  357. // Save point.
  358. m_initialPos = point;
  359. m_ModifyIndex = index;
  360. distanceMin = distancePoint;
  361. }
  362. // check middle of an edge
  363. wxPoint offset = ( aPosition + aPosition - point - prevPoint );
  364. distancePoint = ( offset.x * offset.x + offset.y * offset.y ) / 4 + 1;
  365. if( distancePoint < distanceMin )
  366. {
  367. // Save point.
  368. m_initialPos = point;
  369. m_ModifyIndex = -index; // negative indicates new vertex is to be inserted
  370. distanceMin = distancePoint;
  371. }
  372. prevPoint = point;
  373. index++;
  374. }
  375. SetEraseLastDrawItem();
  376. }
  377. else if( aEditMode == IS_MOVED )
  378. {
  379. m_initialCursorPos = aPosition;
  380. m_initialPos = m_PolyPoints[0];
  381. SetEraseLastDrawItem();
  382. }
  383. m_Flags = aEditMode;
  384. }
  385. bool LIB_POLYLINE::ContinueEdit( const wxPoint aPosition )
  386. {
  387. wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
  388. wxT( "Bad call to ContinueEdit(). LIB_POLYLINE is not being edited." ) );
  389. if( m_Flags == IS_NEW )
  390. {
  391. // do not add zero length segments
  392. if( m_PolyPoints[m_PolyPoints.size() - 2] != m_PolyPoints.back() )
  393. m_PolyPoints.push_back( aPosition );
  394. return true;
  395. }
  396. return false;
  397. }
  398. void LIB_POLYLINE::EndEdit( const wxPoint& aPosition, bool aAbort )
  399. {
  400. wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  401. wxT( "Bad call to EndEdit(). LIB_POLYLINE is not being edited." ) );
  402. // do not include last point twice
  403. if( m_Flags == IS_NEW && 2 < m_PolyPoints.size() )
  404. {
  405. if( m_PolyPoints[ m_PolyPoints.size() - 2 ] == m_PolyPoints.back() )
  406. m_PolyPoints.pop_back();
  407. }
  408. if( (m_Flags == IS_RESIZED) && (m_PolyPoints.size() > 2) ) // do not delete last two points... keep it alive
  409. {
  410. if( ( m_ModifyIndex > 0 && m_PolyPoints[ m_ModifyIndex ] ==
  411. m_PolyPoints[ m_ModifyIndex - 1 ] )
  412. || ( m_ModifyIndex < (int) m_PolyPoints.size() - 1
  413. && m_PolyPoints[ m_ModifyIndex ] == m_PolyPoints[ m_ModifyIndex + 1 ] ) )
  414. {
  415. m_PolyPoints.erase( m_PolyPoints.begin() + m_ModifyIndex ); // delete a point on this
  416. }
  417. }
  418. m_Flags = 0;
  419. SetEraseLastDrawItem( false );
  420. }
  421. void LIB_POLYLINE::calcEdit( const wxPoint& aPosition )
  422. {
  423. if( m_Flags == IS_NEW )
  424. {
  425. m_PolyPoints[ GetCornerCount() - 1 ] = aPosition;
  426. SetEraseLastDrawItem();
  427. }
  428. else if( m_Flags == IS_RESIZED )
  429. {
  430. if( m_ModifyIndex < 0 ) // negative indicates new vertex is to be inserted
  431. {
  432. m_ModifyIndex = -m_ModifyIndex;
  433. m_PolyPoints.insert( m_PolyPoints.begin() + m_ModifyIndex, aPosition );
  434. }
  435. m_PolyPoints[ m_ModifyIndex ] = aPosition;
  436. }
  437. else if( m_Flags == IS_MOVED )
  438. {
  439. Move( m_initialPos + aPosition - m_initialCursorPos );
  440. }
  441. }