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.

523 lines
14 KiB

  1. /***************************************************/
  2. /* class and functions to handle a graphic segment */
  3. /****************************************************/
  4. #include "fctsys.h"
  5. #include "wxstruct.h"
  6. #include "gr_basic.h"
  7. #include "bezier_curves.h"
  8. #include "common.h"
  9. #include "class_drawpanel.h"
  10. #include "kicad_string.h"
  11. #include "colors_selection.h"
  12. #include "pcbnew.h"
  13. #include "class_board_design_settings.h"
  14. #include "trigo.h"
  15. #include "protos.h"
  16. /* DRAWSEGMENT: constructor */
  17. DRAWSEGMENT::DRAWSEGMENT( BOARD_ITEM* aParent, KICAD_T idtype ) :
  18. BOARD_ITEM( aParent, idtype )
  19. {
  20. m_Width = m_Flags = m_Shape = m_Type = m_Angle = 0;
  21. }
  22. /* destructor */
  23. DRAWSEGMENT:: ~DRAWSEGMENT()
  24. {
  25. }
  26. /*******************************************/
  27. void DRAWSEGMENT::Copy( DRAWSEGMENT* source )
  28. /*******************************************/
  29. {
  30. m_Type = source->m_Type;
  31. m_Layer = source->m_Layer;
  32. m_Width = source->m_Width;
  33. m_Start = source->m_Start;
  34. m_End = source->m_End;
  35. m_Shape = source->m_Shape;
  36. m_Angle = source->m_Angle;
  37. m_TimeStamp = source->m_TimeStamp;
  38. m_BezierC1 = source->m_BezierC1;
  39. m_BezierC2 = source->m_BezierC1;
  40. }
  41. /**
  42. * Function Rotate
  43. * Rotate this object.
  44. * @param const wxPoint& aRotCentre - the rotation point.
  45. * @param aAngle - the rotation angle in 0.1 degree.
  46. */
  47. void DRAWSEGMENT::Rotate(const wxPoint& aRotCentre, int aAngle)
  48. {
  49. RotatePoint( &m_Start, aRotCentre, aAngle );
  50. RotatePoint( &m_End, aRotCentre, aAngle );
  51. }
  52. /**
  53. * Function Flip
  54. * Flip this object, i.e. change the board side for this object
  55. * @param const wxPoint& aCentre - the rotation point.
  56. */
  57. void DRAWSEGMENT::Flip(const wxPoint& aCentre )
  58. {
  59. m_Start.y = aCentre.y - (m_Start.y - aCentre.y);
  60. m_End.y = aCentre.y - (m_End.y - aCentre.y);
  61. if( m_Shape == S_ARC )
  62. {
  63. NEGATE( m_Angle );
  64. }
  65. SetLayer( ChangeSideNumLayer( GetLayer() ) );
  66. }
  67. bool DRAWSEGMENT::Save( FILE* aFile ) const
  68. {
  69. if( GetState( DELETED ) )
  70. return true;
  71. bool rc = false;
  72. if( fprintf( aFile, "$DRAWSEGMENT\n" ) != sizeof("$DRAWSEGMENT\n") - 1 )
  73. goto out;
  74. fprintf( aFile, "Po %d %d %d %d %d %d\n",
  75. m_Shape,
  76. m_Start.x, m_Start.y,
  77. m_End.x, m_End.y, m_Width );
  78. if( m_Type != S_CURVE) {
  79. fprintf( aFile, "De %d %d %d %lX %X\n",
  80. m_Layer, m_Type, m_Angle,
  81. m_TimeStamp, ReturnStatus() );
  82. } else {
  83. fprintf( aFile, "De %d %d %d %lX %X %d %d %d %d\n",
  84. m_Layer, m_Type, m_Angle,
  85. m_TimeStamp, ReturnStatus(),
  86. m_BezierC1.x,m_BezierC1.y,
  87. m_BezierC2.x,m_BezierC2.y);
  88. }
  89. if( fprintf( aFile, "$EndDRAWSEGMENT\n" ) != sizeof("$EndDRAWSEGMENT\n") - 1 )
  90. goto out;
  91. rc = true;
  92. out:
  93. return rc;
  94. }
  95. /******************************************************************/
  96. bool DRAWSEGMENT::ReadDrawSegmentDescr( FILE* File, int* LineNum )
  97. /******************************************************************/
  98. /* Read a DRAWSEGMENT from a file
  99. */
  100. {
  101. char Line[2048];
  102. while( GetLine( File, Line, LineNum ) != NULL )
  103. {
  104. if( strnicmp( Line, "$End", 4 ) == 0 )
  105. return TRUE; /* End of description */
  106. if( Line[0] == 'P' )
  107. {
  108. sscanf( Line + 2, " %d %d %d %d %d %d",
  109. &m_Shape, &m_Start.x, &m_Start.y,
  110. &m_End.x, &m_End.y, &m_Width );
  111. if( m_Width < 0 )
  112. m_Width = 0;
  113. }
  114. if( Line[0] == 'D' )
  115. {
  116. int status;
  117. char* token=0;
  118. token = strtok(Line," ");
  119. for(int i=0; (token = strtok(NULL," ")) != NULL; i++){
  120. switch(i){
  121. case 0:
  122. sscanf(token,"%d",&m_Layer);
  123. break;
  124. case 1:
  125. sscanf(token,"%d",&m_Type);
  126. break;
  127. case 2:
  128. sscanf(token,"%d",&m_Angle);
  129. break;
  130. case 3:
  131. sscanf(token,"%lX",&m_TimeStamp);
  132. break;
  133. case 4:
  134. sscanf(token,"%X",&status);
  135. break;
  136. /* Bezier Control Points*/
  137. case 5:
  138. sscanf(token,"%d",&m_BezierC1.x);
  139. break;
  140. case 6:
  141. sscanf(token,"%d",&m_BezierC1.y);
  142. break;
  143. case 7:
  144. sscanf(token,"%d",&m_BezierC2.x);
  145. break;
  146. case 8:
  147. sscanf(token,"%d",&m_BezierC2.y);
  148. break;
  149. default:
  150. break;
  151. }
  152. }
  153. if( m_Layer < FIRST_NO_COPPER_LAYER )
  154. m_Layer = FIRST_NO_COPPER_LAYER;
  155. if( m_Layer > LAST_NO_COPPER_LAYER )
  156. m_Layer = LAST_NO_COPPER_LAYER;
  157. SetState( status, ON );
  158. }
  159. }
  160. return FALSE;
  161. }
  162. wxPoint DRAWSEGMENT::GetStart() const
  163. {
  164. switch( m_Shape )
  165. {
  166. case S_ARC:
  167. return m_End; // the start of the arc is held in field m_End, center point is in m_Start.
  168. case S_SEGMENT:
  169. default:
  170. return m_Start;
  171. }
  172. }
  173. wxPoint DRAWSEGMENT::GetEnd() const
  174. {
  175. wxPoint center; // center point of the arc
  176. wxPoint start; // start of arc
  177. switch( m_Shape )
  178. {
  179. case S_ARC:
  180. // rotate the starting point of the arc, given by m_End, through the
  181. // angle m_Angle to get the ending point of the arc.
  182. center = m_Start; // center point of the arc
  183. start = m_End; // start of arc
  184. RotatePoint( &start.x, &start.y, center.x, center.y, -m_Angle );
  185. return start; // after rotation, the end of the arc.
  186. break;
  187. case S_SEGMENT:
  188. default:
  189. return m_End;
  190. }
  191. }
  192. void DRAWSEGMENT::Draw( WinEDA_DrawPanel* panel, wxDC* DC,
  193. int draw_mode, const wxPoint& notUsed )
  194. {
  195. int ux0, uy0, dx, dy;
  196. int l_piste;
  197. int color, mode;
  198. int rayon;
  199. BOARD * brd = GetBoard( );
  200. if( brd->IsLayerVisible( GetLayer() ) == false )
  201. return;
  202. color = brd->GetLayerColor(GetLayer());
  203. GRSetDrawMode( DC, draw_mode );
  204. l_piste = m_Width >> 1; /* half trace width */
  205. ux0 = m_Start.x;
  206. uy0 = m_Start.y;
  207. dx = m_End.x;
  208. dy = m_End.y;
  209. mode = DisplayOpt.DisplayDrawItems;
  210. if( m_Flags & FORCE_SKETCH )
  211. mode = SKETCH;
  212. #ifdef USE_WX_ZOOM
  213. if( l_piste < DC->DeviceToLogicalXRel( L_MIN_DESSIN ) )
  214. #else
  215. if( l_piste < panel->GetScreen()->Unscale( L_MIN_DESSIN ) )
  216. #endif
  217. mode = FILAIRE;
  218. switch( m_Shape )
  219. {
  220. case S_CIRCLE:
  221. rayon = (int) hypot( (double) (dx - ux0), (double) (dy - uy0) );
  222. if( mode == FILAIRE )
  223. {
  224. GRCircle( &panel->m_ClipBox, DC, ux0, uy0, rayon, color );
  225. }
  226. else if( mode == SKETCH )
  227. {
  228. GRCircle( &panel->m_ClipBox, DC, ux0, uy0, rayon - l_piste, color );
  229. GRCircle( &panel->m_ClipBox, DC, ux0, uy0, rayon + l_piste, color );
  230. }
  231. else
  232. {
  233. GRCircle( &panel->m_ClipBox, DC, ux0, uy0, rayon, m_Width, color );
  234. }
  235. break;
  236. case S_ARC:
  237. int StAngle, EndAngle;
  238. rayon = (int) hypot( (double) (dx - ux0), (double) (dy - uy0) );
  239. StAngle = (int) ArcTangente( dy - uy0, dx - ux0 );
  240. EndAngle = StAngle + m_Angle;
  241. if ( ! panel->m_PrintIsMirrored)
  242. {
  243. if( StAngle > EndAngle )
  244. EXCHG( StAngle, EndAngle );
  245. }
  246. else // Mirrored mode: arc orientation is reversed
  247. {
  248. if( StAngle < EndAngle )
  249. EXCHG( StAngle, EndAngle );
  250. }
  251. if( mode == FILAIRE )
  252. GRArc( &panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle,
  253. rayon, color );
  254. else if( mode == SKETCH )
  255. {
  256. GRArc( &panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle,
  257. rayon - l_piste, color );
  258. GRArc( &panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle,
  259. rayon + l_piste, color );
  260. }
  261. else
  262. {
  263. GRArc( &panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle,
  264. rayon, m_Width, color );
  265. }
  266. break;
  267. case S_CURVE:
  268. m_BezierPoints = Bezier2Poly(m_Start,m_BezierC1, m_BezierC2, m_End);
  269. for (unsigned int i=1; i < m_BezierPoints.size(); i++) {
  270. if( mode == FILAIRE )
  271. GRLine( &panel->m_ClipBox, DC,
  272. m_BezierPoints[i].x, m_BezierPoints[i].y,
  273. m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, 0,
  274. color );
  275. else if( mode == SKETCH )
  276. {
  277. GRCSegm( &panel->m_ClipBox, DC,
  278. m_BezierPoints[i].x, m_BezierPoints[i].y,
  279. m_BezierPoints[i-1].x, m_BezierPoints[i-1].y,
  280. m_Width, color );
  281. }
  282. else
  283. {
  284. GRFillCSegm( &panel->m_ClipBox, DC,
  285. m_BezierPoints[i].x, m_BezierPoints[i].y,
  286. m_BezierPoints[i-1].x, m_BezierPoints[i-1].y,
  287. m_Width, color );
  288. }
  289. }
  290. break;
  291. default:
  292. if( mode == FILAIRE )
  293. GRLine( &panel->m_ClipBox, DC, ux0, uy0, dx, dy, 0, color );
  294. else if( mode == SKETCH )
  295. {
  296. GRCSegm( &panel->m_ClipBox, DC, ux0, uy0, dx, dy,
  297. m_Width, color );
  298. }
  299. else
  300. {
  301. GRFillCSegm( &panel->m_ClipBox, DC, ux0, uy0, dx, dy,
  302. m_Width, color );
  303. }
  304. break;
  305. }
  306. }
  307. // see pcbstruct.h
  308. void DRAWSEGMENT::DisplayInfo( WinEDA_DrawFrame* frame )
  309. {
  310. int itype;
  311. wxString msg;
  312. wxString coords;
  313. BOARD* board = (BOARD*) m_Parent;
  314. wxASSERT( board );
  315. frame->ClearMsgPanel();
  316. itype = m_Type & 0x0F;
  317. msg = wxT( "DRAWING" );
  318. frame->AppendMsgPanel( _( "Type" ), msg, DARKCYAN );
  319. wxString shape = _( "Shape" );
  320. switch( m_Shape ) {
  321. case S_CIRCLE:
  322. frame->AppendMsgPanel( shape, _( "Circle" ), RED );
  323. break;
  324. case S_ARC:
  325. frame->AppendMsgPanel( shape, _( "Arc" ), RED );
  326. msg.Printf( wxT( "%.1f" ), (double)m_Angle/10 );
  327. frame->AppendMsgPanel( _("Angle"), msg, RED );
  328. break;
  329. case S_CURVE:
  330. frame->AppendMsgPanel( shape, _( "Curve" ), RED );
  331. break;
  332. default:
  333. frame->AppendMsgPanel( shape, _( "Segment" ), RED );
  334. }
  335. wxString start;
  336. start << GetStart();
  337. wxString end;
  338. end << GetEnd();
  339. frame->AppendMsgPanel( start, end, DARKGREEN );
  340. frame->AppendMsgPanel( _( "Layer" ),
  341. board->GetLayerName( m_Layer ), DARKBROWN );
  342. valeur_param( (unsigned) m_Width, msg );
  343. frame->AppendMsgPanel( _( "Width" ), msg, DARKCYAN );
  344. }
  345. /**
  346. * Function HitTest
  347. * tests if the given wxPoint is within the bounds of this object.
  348. * @param ref_pos A wxPoint to test
  349. * @return bool - true if a hit, else false
  350. */
  351. bool DRAWSEGMENT::HitTest( const wxPoint& ref_pos )
  352. {
  353. int ux0 = m_Start.x;
  354. int uy0 = m_Start.y;
  355. /* Calculate coordinates with ux0, uy0 = origin. */
  356. int dx = m_End.x - ux0;
  357. int dy = m_End.y - uy0;
  358. int spot_cX = ref_pos.x - ux0;
  359. int spot_cY = ref_pos.y - uy0;
  360. switch(m_Shape){
  361. case S_CIRCLE:
  362. case S_ARC:
  363. int rayon, dist, stAngle, endAngle, mouseAngle;
  364. rayon = (int) hypot( (double) (dx), (double) (dy) );
  365. dist = (int) hypot( (double) (spot_cX), (double) (spot_cY) );
  366. if( abs( rayon - dist ) <= ( m_Width / 2 ) )
  367. {
  368. if( m_Shape == S_CIRCLE )
  369. return true;
  370. mouseAngle = (int) ArcTangente( spot_cY, spot_cX );
  371. stAngle = (int) ArcTangente( dy, dx );
  372. endAngle = stAngle + m_Angle;
  373. if( endAngle > 3600 )
  374. {
  375. stAngle -= 3600;
  376. endAngle -= 3600;
  377. }
  378. if( mouseAngle >= stAngle && mouseAngle <= endAngle )
  379. return true;
  380. }
  381. break;
  382. case S_CURVE:
  383. for( unsigned int i= 1; i < m_BezierPoints.size(); i++)
  384. {
  385. if( TestSegmentHit( ref_pos,m_BezierPoints[i-1],
  386. m_BezierPoints[i-1], m_Width / 2 ) )
  387. return true;
  388. }
  389. break;
  390. default:
  391. if( DistanceTest( m_Width / 2, dx, dy, spot_cX, spot_cY ) )
  392. return true;
  393. }
  394. return false;
  395. }
  396. /**
  397. * Function HitTest (overlayed)
  398. * tests if the given EDA_Rect intersect this object.
  399. * For now, an ending point must be inside this rect.
  400. * @param refArea : the given EDA_Rect
  401. * @return bool - true if a hit, else false
  402. */
  403. bool DRAWSEGMENT::HitTest( EDA_Rect& refArea )
  404. {
  405. if( refArea.Inside( m_Start ) )
  406. return true;
  407. if( refArea.Inside( m_End ) )
  408. return true;
  409. return false;
  410. }
  411. #if defined(DEBUG)
  412. /**
  413. * Function Show
  414. * is used to output the object tree, currently for debugging only.
  415. * @param nestLevel An aid to prettier tree indenting, and is the level
  416. * of nesting of this object within the overall tree.
  417. * @param os The ostream& to output to.
  418. */
  419. void DRAWSEGMENT::Show( int nestLevel, std::ostream& os )
  420. {
  421. NestedSpace( nestLevel, os ) << '<' << GetClass().Lower().mb_str() <<
  422. " shape=\"" << m_Shape << '"' <<
  423. /*
  424. " layer=\"" << GetLayer() << '"' <<
  425. " width=\"" << m_Width << '"' <<
  426. " angle=\"" << m_Angle << '"' << // Used only for Arcs: Arc angle in 1/10 deg
  427. */
  428. '>' <<
  429. "<start" << m_Start << "/>" <<
  430. "<end" << m_End << "/>"
  431. "<GetStart" << GetStart() << "/>" <<
  432. "<GetEnd" << GetEnd() << "/>"
  433. ;
  434. os << "</" << GetClass().Lower().mb_str() << ">\n";
  435. }
  436. #endif