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.

750 lines
23 KiB

  1. /*******************/
  2. /** class LIB_ARC **/
  3. /*******************/
  4. #include "fctsys.h"
  5. #include "gr_basic.h"
  6. #include "common.h"
  7. #include "class_drawpanel.h"
  8. #include "plot_common.h"
  9. #include "trigo.h"
  10. #include "general.h"
  11. #include "protos.h"
  12. #include "lib_arc.h"
  13. #include "transform.h"
  14. //! @brief Given three points A B C, compute the circumcenter of the resulting triangle
  15. //! reference: http://en.wikipedia.org/wiki/Circumscribed_circle
  16. //! Coordinates of circumcenter in Cartesian coordinates
  17. static wxPoint calcCenter( const wxPoint& A, const wxPoint& B, const wxPoint& C )
  18. {
  19. double circumCenterX, circumCenterY;
  20. double Ax = (double) A.x;
  21. double Ay = (double) A.y;
  22. double Bx = (double) B.x;
  23. double By = (double) B.y;
  24. double Cx = (double) C.x;
  25. double Cy = (double) C.y;
  26. wxPoint circumCenter;
  27. double D = 2.0 * ( Ax * ( By - Cy ) + Bx * ( Cy - Ay ) + Cx * ( Ay - By ) );
  28. // prevent division / 0
  29. if( fabs( D ) < 1e-7 )
  30. D = 1e-7;
  31. circumCenterX = ( (Ay * Ay + Ax * Ax) * (By - Cy) +
  32. (By * By + Bx * Bx) * (Cy - Ay) +
  33. (Cy * Cy + Cx * Cx) * (Ay - By) ) / D;
  34. circumCenterY = ( (Ay * Ay + Ax * Ax) * (Cx - Bx) +
  35. (By * By + Bx * Bx) * (Ax - Cx) +
  36. (Cy * Cy + Cx * Cx) * (Bx - Ax) ) / D;
  37. circumCenter.x = (int) circumCenterX;
  38. circumCenter.y = (int) circumCenterY;
  39. return circumCenter;
  40. }
  41. LIB_ARC::LIB_ARC( LIB_COMPONENT* aParent ) : LIB_DRAW_ITEM( COMPONENT_ARC_DRAW_TYPE, aParent )
  42. {
  43. m_Radius = 0;
  44. m_t1 = 0;
  45. m_t2 = 0;
  46. m_Width = 0;
  47. m_Fill = NO_FILL;
  48. m_isFillable = true;
  49. m_typeName = _( "Arc" );
  50. m_editState = 0;
  51. m_lastEditState = 0;
  52. }
  53. LIB_ARC::LIB_ARC( const LIB_ARC& aArc ) : LIB_DRAW_ITEM( aArc )
  54. {
  55. m_Radius = aArc.m_Radius;
  56. m_t1 = aArc.m_t1;
  57. m_t2 = aArc.m_t2;
  58. m_Width = aArc.m_Width;
  59. m_Fill = aArc.m_Fill;
  60. m_Pos = aArc.m_Pos;
  61. m_ArcStart = aArc.m_ArcStart;
  62. m_ArcEnd = aArc.m_ArcEnd;
  63. }
  64. /**
  65. * format:
  66. * A centre_posx centre_posy rayon start_angle end_angle unit convert
  67. * fill('N', 'F' ou 'f') startx starty endx endy
  68. */
  69. bool LIB_ARC::Save( FILE* aFile )
  70. {
  71. int x1 = m_t1;
  72. if( x1 > 1800 )
  73. x1 -= 3600;
  74. int x2 = m_t2;
  75. if( x2 > 1800 )
  76. x2 -= 3600;
  77. if( fprintf( aFile, "A %d %d %d %d %d %d %d %d %c %d %d %d %d\n",
  78. m_Pos.x, m_Pos.y, m_Radius, x1, x2, m_Unit, m_Convert, m_Width,
  79. fill_tab[m_Fill], m_ArcStart.x, m_ArcStart.y, m_ArcEnd.x,
  80. m_ArcEnd.y ) < 0 )
  81. return false;
  82. return true;
  83. }
  84. bool LIB_ARC::Load( char* aLine, wxString& aErrorMsg )
  85. {
  86. int startx, starty, endx, endy, cnt;
  87. char tmp[256];
  88. cnt = sscanf( &aLine[2], "%d %d %d %d %d %d %d %d %s %d %d %d %d",
  89. &m_Pos.x, &m_Pos.y, &m_Radius, &m_t1, &m_t2, &m_Unit,
  90. &m_Convert, &m_Width, tmp, &startx, &starty, &endx, &endy );
  91. if( cnt < 8 )
  92. {
  93. aErrorMsg.Printf( _( "arc only had %d parameters of the required 8" ), cnt );
  94. return false;
  95. }
  96. if( tmp[0] == 'F' )
  97. m_Fill = FILLED_SHAPE;
  98. if( tmp[0] == 'f' )
  99. m_Fill = FILLED_WITH_BG_BODYCOLOR;
  100. NORMALIZE_ANGLE( m_t1 );
  101. NORMALIZE_ANGLE( m_t2 );
  102. // Actual Coordinates of arc ends are read from file
  103. if( cnt >= 13 )
  104. {
  105. m_ArcStart.x = startx;
  106. m_ArcStart.y = starty;
  107. m_ArcEnd.x = endx;
  108. m_ArcEnd.y = endy;
  109. }
  110. else
  111. {
  112. // Actual Coordinates of arc ends are not read from file
  113. // (old library), calculate them
  114. m_ArcStart.x = m_Radius;
  115. m_ArcStart.y = 0;
  116. m_ArcEnd.x = m_Radius;
  117. m_ArcEnd.y = 0;
  118. RotatePoint( &m_ArcStart.x, &m_ArcStart.y, -m_t1 );
  119. m_ArcStart.x += m_Pos.x;
  120. m_ArcStart.y += m_Pos.y;
  121. RotatePoint( &m_ArcEnd.x, &m_ArcEnd.y, -m_t2 );
  122. m_ArcEnd.x += m_Pos.x;
  123. m_ArcEnd.y += m_Pos.y;
  124. }
  125. return true;
  126. }
  127. /**
  128. * Function HitTest
  129. * tests if the given wxPoint is within the bounds of this object.
  130. * @param aRefPoint A wxPoint to test in eeschema space
  131. * @return bool - true if a hit, else false
  132. */
  133. bool LIB_ARC::HitTest( const wxPoint& aRefPoint )
  134. {
  135. int mindist = m_Width ? m_Width / 2 : g_DrawDefaultLineThickness / 2;
  136. // Have a minimal tolerance for hit test
  137. if( mindist < MINIMUM_SELECTION_DISTANCE )
  138. mindist = MINIMUM_SELECTION_DISTANCE;
  139. return HitTest( aRefPoint, mindist, DefaultTransform );
  140. }
  141. /** Function HitTest
  142. * @return true if the point aPosRef is near this object
  143. * @param aRefPoint = a wxPoint to test
  144. * @param aThreshold = max distance to this object (usually the half thickness
  145. * of a line)
  146. * @param aTransMat = the transform matrix
  147. */
  148. bool LIB_ARC::HitTest( wxPoint aReferencePoint, int aThreshold, const TRANSFORM& aTransform )
  149. {
  150. // TODO: use aTransMat to calculates parameters
  151. wxPoint relativePosition = aReferencePoint;
  152. NEGATE( relativePosition.y ); // reverse Y axis
  153. int distance = wxRound( EuclideanNorm( TwoPointVector( m_Pos, relativePosition ) ) );
  154. if( abs( distance - m_Radius ) > aThreshold )
  155. return false;
  156. // We are on the circle, ensure we are only on the arc, i.e. between
  157. // m_ArcStart and m_ArcEnd
  158. wxPoint startEndVector = TwoPointVector( m_ArcStart, m_ArcEnd);
  159. wxPoint startRelativePositionVector = TwoPointVector( m_ArcStart, relativePosition );
  160. wxPoint centerStartVector = TwoPointVector( m_Pos, m_ArcStart );
  161. wxPoint centerEndVector = TwoPointVector( m_Pos, m_ArcEnd );
  162. wxPoint centerRelativePositionVector = TwoPointVector( m_Pos, relativePosition );
  163. // Compute the cross product to check if the point is in the sector
  164. int crossProductStart = CrossProduct( centerStartVector, centerRelativePositionVector );
  165. int crossProductEnd = CrossProduct( centerEndVector, centerRelativePositionVector );
  166. // The cross products need to be exchanged, depending on which side the center point
  167. // relative to the start point to end point vector lies
  168. if( CrossProduct( startEndVector, startRelativePositionVector ) < 0 )
  169. {
  170. EXCHG( crossProductStart, crossProductEnd );
  171. }
  172. // When the cross products have a different sign, the point lies in sector
  173. // also check, if the reference is near start or end point
  174. return HitTestPoints( m_ArcStart, relativePosition, MINIMUM_SELECTION_DISTANCE ) ||
  175. HitTestPoints( m_ArcEnd, relativePosition, MINIMUM_SELECTION_DISTANCE ) ||
  176. ( crossProductStart <= 0 && crossProductEnd >= 0 );
  177. }
  178. LIB_DRAW_ITEM* LIB_ARC::DoGenCopy()
  179. {
  180. LIB_ARC* newitem = new LIB_ARC( GetParent() );
  181. newitem->m_Pos = m_Pos;
  182. newitem->m_ArcStart = m_ArcStart;
  183. newitem->m_ArcEnd = m_ArcEnd;
  184. newitem->m_Radius = m_Radius;
  185. newitem->m_t1 = m_t1;
  186. newitem->m_t2 = m_t2;
  187. newitem->m_Width = m_Width;
  188. newitem->m_Unit = m_Unit;
  189. newitem->m_Convert = m_Convert;
  190. newitem->m_Flags = m_Flags;
  191. newitem->m_Fill = m_Fill;
  192. return (LIB_DRAW_ITEM*) newitem;
  193. }
  194. int LIB_ARC::DoCompare( const LIB_DRAW_ITEM& aOther ) const
  195. {
  196. wxASSERT( aOther.Type() == COMPONENT_ARC_DRAW_TYPE );
  197. const LIB_ARC* tmp = ( LIB_ARC* ) &aOther;
  198. if( m_Pos.x != tmp->m_Pos.x )
  199. return m_Pos.x - tmp->m_Pos.x;
  200. if( m_Pos.y != tmp->m_Pos.y )
  201. return m_Pos.y - tmp->m_Pos.y;
  202. if( m_t1 != tmp->m_t1 )
  203. return m_t1 - tmp->m_t1;
  204. if( m_t2 != tmp->m_t2 )
  205. return m_t2 - tmp->m_t2;
  206. return 0;
  207. }
  208. void LIB_ARC::DoOffset( const wxPoint& aOffset )
  209. {
  210. m_Pos += aOffset;
  211. m_ArcStart += aOffset;
  212. m_ArcEnd += aOffset;
  213. }
  214. bool LIB_ARC::DoTestInside( EDA_Rect& aRect ) const
  215. {
  216. return aRect.Inside( m_ArcStart.x, -m_ArcStart.y )
  217. || aRect.Inside( m_ArcEnd.x, -m_ArcEnd.y );
  218. }
  219. void LIB_ARC::DoMove( const wxPoint& aPosition )
  220. {
  221. wxPoint offset = aPosition - m_Pos;
  222. m_Pos = aPosition;
  223. m_ArcStart += offset;
  224. m_ArcEnd += offset;
  225. }
  226. void LIB_ARC::DoMirrorHorizontal( const wxPoint& aCenter )
  227. {
  228. m_Pos.x -= aCenter.x;
  229. m_Pos.x *= -1;
  230. m_Pos.x += aCenter.x;
  231. m_ArcStart.x -= aCenter.x;
  232. m_ArcStart.x *= -1;
  233. m_ArcStart.x += aCenter.x;
  234. m_ArcEnd.x -= aCenter.x;
  235. m_ArcEnd.x *= -1;
  236. m_ArcEnd.x += aCenter.x;
  237. EXCHG( m_ArcStart, m_ArcEnd );
  238. }
  239. void LIB_ARC::DoPlot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  240. const TRANSFORM& aTransform )
  241. {
  242. wxASSERT( aPlotter != NULL );
  243. int t1 = m_t1;
  244. int t2 = m_t2;
  245. wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  246. aTransform.MapAngles( &t1, &t2 );
  247. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  248. {
  249. aPlotter->set_color( ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  250. aPlotter->arc( pos, -t2, -t1, m_Radius, FILLED_SHAPE, 0 );
  251. }
  252. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  253. aPlotter->set_color( ReturnLayerColor( LAYER_DEVICE ) );
  254. aPlotter->arc( pos, -t2, -t1, m_Radius, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  255. }
  256. /** Function GetPenSize
  257. * @return the size of the "pen" that be used to draw or plot this item
  258. */
  259. int LIB_ARC::GetPenSize()
  260. {
  261. return ( m_Width == 0 ) ? g_DrawDefaultLineThickness : m_Width;
  262. }
  263. void LIB_ARC::drawEditGraphics( EDA_Rect* aClipBox, wxDC* aDC, int aColor )
  264. {
  265. // The edit indicators only get drawn when a new arc is being drawn.
  266. if( ( m_Flags & IS_NEW ) == 0 )
  267. return;
  268. // Use the last edit state so when the drawing switches from the end mode to the center
  269. // point mode, the last line between the center points gets erased.
  270. if( m_lastEditState == 1 )
  271. {
  272. GRLine( aClipBox, aDC, m_ArcStart.x, -m_ArcStart.y, m_ArcEnd.x, -m_ArcEnd.y, 0, aColor );
  273. }
  274. else
  275. {
  276. GRDashedLine( aClipBox, aDC, m_ArcStart.x, -m_ArcStart.y, m_Pos.x, -m_Pos.y, 0, aColor );
  277. GRDashedLine( aClipBox, aDC, m_ArcEnd.x, -m_ArcEnd.y, m_Pos.x, -m_Pos.y, 0, aColor );
  278. }
  279. }
  280. void LIB_ARC::drawGraphic( WinEDA_DrawPanel* aPanel, wxDC* aDC, const wxPoint& aOffset,
  281. int aColor, int aDrawMode, void* aData, const TRANSFORM& aTransform )
  282. {
  283. // Don't draw the arc until the end point is selected. Only the edit indicators
  284. // get drawn at this time.
  285. if( ( m_Flags & IS_NEW ) && m_lastEditState == 1 )
  286. return;
  287. wxPoint pos1, pos2, posc;
  288. int color = ReturnLayerColor( LAYER_DEVICE );
  289. if( aColor < 0 ) // Used normal color or selected color
  290. {
  291. if( ( m_Selected & IS_SELECTED ) )
  292. color = g_ItemSelectetColor;
  293. }
  294. else
  295. {
  296. color = aColor;
  297. }
  298. pos1 = aTransform.TransformCoordinate( m_ArcEnd ) + aOffset;
  299. pos2 = aTransform.TransformCoordinate( m_ArcStart ) + aOffset;
  300. posc = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  301. int pt1 = m_t1;
  302. int pt2 = m_t2;
  303. bool swap = aTransform.MapAngles( &pt1, &pt2 );
  304. if( swap )
  305. {
  306. EXCHG( pos1.x, pos2.x );
  307. EXCHG( pos1.y, pos2.y );
  308. }
  309. GRSetDrawMode( aDC, aDrawMode );
  310. FILL_T fill = aData ? NO_FILL : m_Fill;
  311. if( aColor >= 0 )
  312. fill = NO_FILL;
  313. if( fill == FILLED_WITH_BG_BODYCOLOR )
  314. GRFilledArc( &aPanel->m_ClipBox, aDC, posc.x, posc.y, pt1, pt2,
  315. m_Radius, GetPenSize( ),
  316. (m_Flags & IS_MOVED) ? color : ReturnLayerColor( LAYER_DEVICE_BACKGROUND ),
  317. ReturnLayerColor( LAYER_DEVICE_BACKGROUND ) );
  318. else if( fill == FILLED_SHAPE && !aData )
  319. GRFilledArc( &aPanel->m_ClipBox, aDC, posc.x, posc.y, pt1, pt2, m_Radius, color, color );
  320. else
  321. {
  322. #ifdef DRAW_ARC_WITH_ANGLE
  323. GRArc( &aPanel->m_ClipBox, aDC, posc.x, posc.y, pt1, pt2, m_Radius, GetPenSize(), color );
  324. #else
  325. GRArc1( &aPanel->m_ClipBox, aDC, pos1.x, pos1.y, pos2.x, pos2.y,
  326. posc.x, posc.y, GetPenSize(), color );
  327. #endif
  328. }
  329. /* Set to one (1) to draw bounding box around arc to validate bounding box
  330. * calculation. */
  331. #if 0
  332. EDA_Rect bBox = GetBoundingBox();
  333. GRRect( &aPanel->m_ClipBox, aDC, bBox.GetOrigin().x, bBox.GetOrigin().y,
  334. bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
  335. #endif
  336. }
  337. EDA_Rect LIB_ARC::GetBoundingBox()
  338. {
  339. int minX, minY, maxX, maxY, angleStart, angleEnd;
  340. EDA_Rect rect;
  341. wxPoint nullPoint, startPos, endPos, centerPos;
  342. wxPoint normStart = m_ArcStart - m_Pos;
  343. wxPoint normEnd = m_ArcEnd - m_Pos;
  344. if( ( normStart == nullPoint ) || ( normEnd == nullPoint ) || ( m_Radius == 0 ) )
  345. {
  346. wxLogDebug( wxT("Invalid arc drawing definition, center(%d, %d) \
  347. start(%d, %d), end(%d, %d), radius %d" ),
  348. m_Pos.x, m_Pos.y, m_ArcStart.x, m_ArcStart.y, m_ArcEnd.x,
  349. m_ArcEnd.y, m_Radius );
  350. return rect;
  351. }
  352. endPos = DefaultTransform.TransformCoordinate( m_ArcEnd );
  353. startPos = DefaultTransform.TransformCoordinate( m_ArcStart );
  354. centerPos = DefaultTransform.TransformCoordinate( m_Pos );
  355. angleStart = m_t1;
  356. angleEnd = m_t2;
  357. if( DefaultTransform.MapAngles( &angleStart, &angleEnd ) )
  358. {
  359. EXCHG( endPos.x, startPos.x );
  360. EXCHG( endPos.y, startPos.y );
  361. }
  362. /* Start with the start and end point of the arc. */
  363. minX = MIN( startPos.x, endPos.x );
  364. minY = MIN( startPos.y, endPos.y );
  365. maxX = MAX( startPos.x, endPos.x );
  366. maxY = MAX( startPos.y, endPos.y );
  367. /* Zero degrees is a special case. */
  368. if( angleStart == 0 )
  369. maxX = centerPos.x + m_Radius;
  370. /* Arc end angle wrapped passed 360. */
  371. if( angleStart > angleEnd )
  372. angleEnd += 3600;
  373. if( angleStart <= 900 && angleEnd >= 900 ) /* 90 deg */
  374. maxY = centerPos.y + m_Radius;
  375. if( angleStart <= 1800 && angleEnd >= 1800 ) /* 180 deg */
  376. minX = centerPos.x - m_Radius;
  377. if( angleStart <= 2700 && angleEnd >= 2700 ) /* 270 deg */
  378. minY = centerPos.y - m_Radius;
  379. if( angleStart <= 3600 && angleEnd >= 3600 ) /* 0 deg */
  380. maxX = centerPos.x + m_Radius;
  381. rect.SetOrigin( minX, minY );
  382. rect.SetEnd( maxX, maxY );
  383. rect.Inflate( m_Width / 2, m_Width / 2 );
  384. return rect;
  385. }
  386. void LIB_ARC::DisplayInfo( WinEDA_DrawFrame* aFrame )
  387. {
  388. wxString msg;
  389. EDA_Rect bBox = GetBoundingBox();
  390. LIB_DRAW_ITEM::DisplayInfo( aFrame );
  391. msg = ReturnStringFromValue( g_UserUnit, m_Width, EESCHEMA_INTERNAL_UNIT, true );
  392. aFrame->AppendMsgPanel( _( "Line width" ), msg, BLUE );
  393. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  394. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  395. aFrame->AppendMsgPanel( _( "Bounding box" ), msg, BROWN );
  396. }
  397. void LIB_ARC::BeginEdit( int aEditMode, const wxPoint aPosition )
  398. {
  399. wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  400. wxT( "Invalid edit mode for LIB_ARC object." ) );
  401. if( aEditMode == IS_NEW )
  402. {
  403. m_ArcStart = m_ArcEnd = aPosition;
  404. m_editState = m_lastEditState = 1;
  405. }
  406. else if( aEditMode == IS_MOVED )
  407. {
  408. m_initialPos = m_Pos;
  409. m_initialCursorPos = aPosition;
  410. SetEraseLastDrawItem();
  411. }
  412. else
  413. {
  414. // The arc center point has to be rotated with while adjusting the
  415. // start or end point, determine the side of this point and the distance
  416. // from the start / end point
  417. wxPoint middlePoint = wxPoint( (m_ArcStart.x + m_ArcEnd.x) / 2,
  418. (m_ArcStart.y + m_ArcEnd.y) / 2 );
  419. wxPoint centerVector = m_Pos - middlePoint;
  420. wxPoint startEndVector = TwoPointVector( m_ArcStart, m_ArcEnd );
  421. m_editCenterDistance = EuclideanNorm( centerVector );
  422. // Determine on which side is the center point
  423. m_editDirection = CrossProduct( startEndVector, centerVector ) ? 1 : -1;
  424. // Drag either the start, end point or the outline
  425. if( HitTestPoints( m_ArcStart, aPosition, MINIMUM_SELECTION_DISTANCE ) )
  426. {
  427. m_editSelectPoint = START;
  428. }
  429. else if( HitTestPoints( m_ArcEnd, aPosition, MINIMUM_SELECTION_DISTANCE ) )
  430. {
  431. m_editSelectPoint = END;
  432. }
  433. else
  434. m_editSelectPoint = OUTLINE;
  435. m_editState = 0;
  436. SetEraseLastDrawItem();
  437. }
  438. m_Flags = aEditMode;
  439. }
  440. bool LIB_ARC::ContinueEdit( const wxPoint aPosition )
  441. {
  442. wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
  443. wxT( "Bad call to ContinueEdit(). LIB_ARC is not being edited." ) );
  444. if( m_Flags == IS_NEW )
  445. {
  446. if( m_editState == 1 ) // Second position yields the arc segment length.
  447. {
  448. m_ArcEnd = aPosition;
  449. m_editState = 2;
  450. SetEraseLastDrawItem( false );
  451. return true; // Need third position to calculate center point.
  452. }
  453. }
  454. return false;
  455. }
  456. void LIB_ARC::EndEdit( const wxPoint& aPosition, bool aAbort )
  457. {
  458. wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  459. wxT( "Bad call to EndEdit(). LIB_ARC is not being edited." ) );
  460. SetEraseLastDrawItem( false );
  461. m_lastEditState = 0;
  462. m_editState = 0;
  463. m_Flags = 0;
  464. }
  465. void LIB_ARC::calcEdit( const wxPoint& aPosition )
  466. {
  467. if( m_Flags == IS_RESIZED )
  468. {
  469. wxPoint newCenterPoint, startPos, endPos;
  470. // Choose the point of the arc to be adjusted
  471. if( m_editSelectPoint == START )
  472. {
  473. startPos = aPosition;
  474. endPos = m_ArcEnd;
  475. }
  476. else if( m_editSelectPoint == END )
  477. {
  478. endPos = aPosition;
  479. startPos = m_ArcStart;
  480. }
  481. else
  482. {
  483. // Use the cursor for adjusting the arc curvature
  484. startPos = m_ArcStart;
  485. endPos = m_ArcEnd;
  486. wxPoint middlePoint = wxPoint( (startPos.x + endPos.x) / 2,
  487. (startPos.y + endPos.y) / 2 );
  488. // If the distance is too small, use the old center point
  489. // else the new center point is calculated over the three points start/end/cursor
  490. if( DistanceLinePoint( startPos, endPos, aPosition ) > MINIMUM_SELECTION_DISTANCE )
  491. {
  492. newCenterPoint = calcCenter( startPos, aPosition, endPos );
  493. }
  494. else
  495. {
  496. newCenterPoint = m_Pos;
  497. }
  498. // Determine if the arc angle is larger than 180 degrees -> this happens if both
  499. // points (cursor position, center point) lie on the same side of the vector
  500. // start-end
  501. int crossA = CrossProduct( TwoPointVector( startPos, endPos ),
  502. TwoPointVector( endPos, aPosition ) );
  503. int crossB = CrossProduct( TwoPointVector( startPos, endPos ),
  504. TwoPointVector( endPos, newCenterPoint ) );
  505. if( ( crossA < 0 && crossB < 0 ) || ( crossA >= 0 && crossB >= 0 ) )
  506. newCenterPoint = m_Pos;
  507. }
  508. if( m_editSelectPoint == START || m_editSelectPoint == END )
  509. {
  510. // Compute the new center point when the start/end points are modified
  511. wxPoint middlePoint = wxPoint( (startPos.x + endPos.x) / 2,
  512. (startPos.y + endPos.y) / 2 );
  513. wxPoint startEndVector = TwoPointVector( startPos, endPos );
  514. wxPoint perpendicularVector = wxPoint( -startEndVector.y, startEndVector.x );
  515. double lengthPerpendicularVector = EuclideanNorm( perpendicularVector );
  516. // prevent too large values, division / 0
  517. if( lengthPerpendicularVector < 1e-1 )
  518. lengthPerpendicularVector = 1e-1;
  519. perpendicularVector.x = (int) ( (double) perpendicularVector.x *
  520. m_editCenterDistance /
  521. lengthPerpendicularVector ) * m_editDirection;
  522. perpendicularVector.y = (int) ( (double) perpendicularVector.y *
  523. m_editCenterDistance /
  524. lengthPerpendicularVector ) * m_editDirection;
  525. newCenterPoint = middlePoint + perpendicularVector;
  526. m_ArcStart = startPos;
  527. m_ArcEnd = endPos;
  528. }
  529. m_Pos = newCenterPoint;
  530. calcRadiusAngles();
  531. }
  532. else if( m_Flags == IS_NEW )
  533. {
  534. if( m_editState == 1 )
  535. {
  536. m_ArcEnd = aPosition;
  537. }
  538. if( m_editState != m_lastEditState )
  539. m_lastEditState = m_editState;
  540. // Keep the arc center point up to date. Otherwise, there will be edit graphic
  541. // artifacts left behind from the initial draw.
  542. int dx, dy;
  543. int cX, cY;
  544. int angle;
  545. cX = aPosition.x;
  546. cY = aPosition.y;
  547. dx = m_ArcEnd.x - m_ArcStart.x;
  548. dy = m_ArcEnd.y - m_ArcStart.y;
  549. cX -= m_ArcStart.x;
  550. cY -= m_ArcStart.y;
  551. angle = (int) ( atan2( (double) dy, (double) dx ) * 1800 / M_PI );
  552. RotatePoint( &dx, &dy, angle ); /* The segment dx, dy is horizontal
  553. * -> Length = dx, dy = 0 */
  554. RotatePoint( &cX, &cY, angle );
  555. cX = dx / 2; /* cX, cY is on the median segment 0.0 a dx, 0 */
  556. RotatePoint( &cX, &cY, -angle );
  557. cX += m_ArcStart.x;
  558. cY += m_ArcStart.y;
  559. m_Pos.x = cX;
  560. m_Pos.y = cY;
  561. calcRadiusAngles();
  562. SetEraseLastDrawItem();
  563. }
  564. else if( m_Flags == IS_MOVED )
  565. {
  566. Move( m_initialPos + aPosition - m_initialCursorPos );
  567. }
  568. }
  569. void LIB_ARC::calcRadiusAngles()
  570. {
  571. wxPoint centerStartVector = TwoPointVector( m_Pos, m_ArcStart );
  572. wxPoint centerEndVector = TwoPointVector( m_Pos, m_ArcEnd );
  573. m_Radius = wxRound( EuclideanNorm( centerStartVector ) );
  574. m_t1 = (int) ( atan2( (double) centerStartVector.y,
  575. (double) centerStartVector.x ) * 1800 / M_PI );
  576. m_t2 = (int) ( atan2( (double) centerEndVector.y,
  577. (double) centerEndVector.x ) * 1800 / M_PI );
  578. NORMALIZE_ANGLE( m_t1 );
  579. NORMALIZE_ANGLE( m_t2 ); // angles = 0 .. 3600
  580. // Restrict angle to less than 180 to avoid PBS display mirror Trace because it is
  581. // assumed that the arc is less than 180 deg to find orientation after rotate or mirror.
  582. if( (m_t2 - m_t1) > 1800 )
  583. m_t2 -= 3600;
  584. else if( (m_t2 - m_t1) <= -1800 )
  585. m_t2 += 3600;
  586. while( (m_t2 - m_t1) >= 1800 )
  587. {
  588. m_t2--;
  589. m_t1++;
  590. }
  591. while( (m_t1 - m_t2) >= 1800 )
  592. {
  593. m_t2++;
  594. m_t1--;
  595. }
  596. NORMALIZE_ANGLE( m_t1 );
  597. if( !IsMoving() )
  598. NORMALIZE_ANGLE( m_t2 );
  599. }