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.

709 lines
21 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file lib_arc.cpp
  26. */
  27. #include <fctsys.h>
  28. #include <gr_basic.h>
  29. #include <macros.h>
  30. #include <sch_draw_panel.h>
  31. #include <plotter.h>
  32. #include <trigo.h>
  33. #include <base_units.h>
  34. #include <msgpanel.h>
  35. #include <bitmaps.h>
  36. #include <general.h>
  37. #include <lib_arc.h>
  38. #include <transform.h>
  39. // Helper function
  40. static inline wxPoint twoPointVector( const wxPoint &startPoint, const wxPoint &endPoint )
  41. {
  42. return endPoint - startPoint;
  43. }
  44. //! @brief Given three points A B C, compute the circumcenter of the resulting triangle
  45. //! reference: http://en.wikipedia.org/wiki/Circumscribed_circle
  46. //! Coordinates of circumcenter in Cartesian coordinates
  47. static wxPoint calcCenter( const wxPoint& A, const wxPoint& B, const wxPoint& C )
  48. {
  49. double circumCenterX, circumCenterY;
  50. double Ax = (double) A.x;
  51. double Ay = (double) A.y;
  52. double Bx = (double) B.x;
  53. double By = (double) B.y;
  54. double Cx = (double) C.x;
  55. double Cy = (double) C.y;
  56. wxPoint circumCenter;
  57. double D = 2.0 * ( Ax * ( By - Cy ) + Bx * ( Cy - Ay ) + Cx * ( Ay - By ) );
  58. // prevent division / 0
  59. if( fabs( D ) < 1e-7 )
  60. D = 1e-7;
  61. circumCenterX = ( (Ay * Ay + Ax * Ax) * (By - Cy) +
  62. (By * By + Bx * Bx) * (Cy - Ay) +
  63. (Cy * Cy + Cx * Cx) * (Ay - By) ) / D;
  64. circumCenterY = ( (Ay * Ay + Ax * Ax) * (Cx - Bx) +
  65. (By * By + Bx * Bx) * (Ax - Cx) +
  66. (Cy * Cy + Cx * Cx) * (Bx - Ax) ) / D;
  67. circumCenter.x = (int) circumCenterX;
  68. circumCenter.y = (int) circumCenterY;
  69. return circumCenter;
  70. }
  71. LIB_ARC::LIB_ARC( LIB_PART* aParent ) : LIB_ITEM( LIB_ARC_T, aParent )
  72. {
  73. m_Radius = 0;
  74. m_t1 = 0;
  75. m_t2 = 0;
  76. m_Width = 0;
  77. m_Fill = NO_FILL;
  78. m_isFillable = true;
  79. m_editState = 0;
  80. m_lastEditState = 0;
  81. m_editCenterDistance = 0.0;
  82. m_editSelectPoint = ARC_STATUS_START;
  83. m_editDirection = 0;
  84. }
  85. bool LIB_ARC::HitTest( const wxPoint& aRefPoint ) const
  86. {
  87. int mindist = GetPenSize() / 2;
  88. // Have a minimal tolerance for hit test
  89. if( mindist < MINIMUM_SELECTION_DISTANCE )
  90. mindist = MINIMUM_SELECTION_DISTANCE;
  91. return HitTest( aRefPoint, mindist, DefaultTransform );
  92. }
  93. bool LIB_ARC::HitTest( const wxPoint &aPosition, int aThreshold, const TRANSFORM& aTransform ) const
  94. {
  95. if( aThreshold < 0 )
  96. aThreshold = GetPenSize() / 2;
  97. // TODO: use aTransMat to calculates parameters
  98. wxPoint relativePosition = aPosition;
  99. relativePosition.y = -relativePosition.y; // reverse Y axis
  100. int distance = KiROUND( GetLineLength( m_Pos, relativePosition ) );
  101. if( abs( distance - m_Radius ) > aThreshold )
  102. return false;
  103. // We are on the circle, ensure we are only on the arc, i.e. between
  104. // m_ArcStart and m_ArcEnd
  105. wxPoint startEndVector = twoPointVector( m_ArcStart, m_ArcEnd);
  106. wxPoint startRelativePositionVector = twoPointVector( m_ArcStart, relativePosition );
  107. wxPoint centerStartVector = twoPointVector( m_Pos, m_ArcStart );
  108. wxPoint centerEndVector = twoPointVector( m_Pos, m_ArcEnd );
  109. wxPoint centerRelativePositionVector = twoPointVector( m_Pos, relativePosition );
  110. // Compute the cross product to check if the point is in the sector
  111. double crossProductStart = CrossProduct( centerStartVector, centerRelativePositionVector );
  112. double crossProductEnd = CrossProduct( centerEndVector, centerRelativePositionVector );
  113. // The cross products need to be exchanged, depending on which side the center point
  114. // relative to the start point to end point vector lies
  115. if( CrossProduct( startEndVector, startRelativePositionVector ) < 0 )
  116. {
  117. std::swap( crossProductStart, crossProductEnd );
  118. }
  119. // When the cross products have a different sign, the point lies in sector
  120. // also check, if the reference is near start or end point
  121. return HitTestPoints( m_ArcStart, relativePosition, MINIMUM_SELECTION_DISTANCE ) ||
  122. HitTestPoints( m_ArcEnd, relativePosition, MINIMUM_SELECTION_DISTANCE ) ||
  123. ( crossProductStart <= 0 && crossProductEnd >= 0 );
  124. }
  125. EDA_ITEM* LIB_ARC::Clone() const
  126. {
  127. return new LIB_ARC( *this );
  128. }
  129. int LIB_ARC::compare( const LIB_ITEM& aOther ) const
  130. {
  131. wxASSERT( aOther.Type() == LIB_ARC_T );
  132. const LIB_ARC* tmp = ( LIB_ARC* ) &aOther;
  133. if( m_Pos.x != tmp->m_Pos.x )
  134. return m_Pos.x - tmp->m_Pos.x;
  135. if( m_Pos.y != tmp->m_Pos.y )
  136. return m_Pos.y - tmp->m_Pos.y;
  137. if( m_t1 != tmp->m_t1 )
  138. return m_t1 - tmp->m_t1;
  139. if( m_t2 != tmp->m_t2 )
  140. return m_t2 - tmp->m_t2;
  141. return 0;
  142. }
  143. void LIB_ARC::SetOffset( const wxPoint& aOffset )
  144. {
  145. m_Pos += aOffset;
  146. m_ArcStart += aOffset;
  147. m_ArcEnd += aOffset;
  148. }
  149. bool LIB_ARC::Inside( EDA_RECT& aRect ) const
  150. {
  151. return aRect.Contains( m_ArcStart.x, -m_ArcStart.y )
  152. || aRect.Contains( m_ArcEnd.x, -m_ArcEnd.y );
  153. }
  154. void LIB_ARC::Move( const wxPoint& aPosition )
  155. {
  156. wxPoint offset = aPosition - m_Pos;
  157. m_Pos = aPosition;
  158. m_ArcStart += offset;
  159. m_ArcEnd += offset;
  160. }
  161. void LIB_ARC::MirrorHorizontal( const wxPoint& aCenter )
  162. {
  163. m_Pos.x -= aCenter.x;
  164. m_Pos.x *= -1;
  165. m_Pos.x += aCenter.x;
  166. m_ArcStart.x -= aCenter.x;
  167. m_ArcStart.x *= -1;
  168. m_ArcStart.x += aCenter.x;
  169. m_ArcEnd.x -= aCenter.x;
  170. m_ArcEnd.x *= -1;
  171. m_ArcEnd.x += aCenter.x;
  172. std::swap( m_ArcStart, m_ArcEnd );
  173. std::swap( m_t1, m_t2 );
  174. m_t1 = 1800 - m_t1;
  175. m_t2 = 1800 - m_t2;
  176. if( m_t1 > 3600 || m_t2 > 3600 )
  177. {
  178. m_t1 -= 3600;
  179. m_t2 -= 3600;
  180. }
  181. else if( m_t1 < -3600 || m_t2 < -3600 )
  182. {
  183. m_t1 += 3600;
  184. m_t2 += 3600;
  185. }
  186. }
  187. void LIB_ARC::MirrorVertical( const wxPoint& aCenter )
  188. {
  189. m_Pos.y -= aCenter.y;
  190. m_Pos.y *= -1;
  191. m_Pos.y += aCenter.y;
  192. m_ArcStart.y -= aCenter.y;
  193. m_ArcStart.y *= -1;
  194. m_ArcStart.y += aCenter.y;
  195. m_ArcEnd.y -= aCenter.y;
  196. m_ArcEnd.y *= -1;
  197. m_ArcEnd.y += aCenter.y;
  198. std::swap( m_ArcStart, m_ArcEnd );
  199. std::swap( m_t1, m_t2 );
  200. m_t1 = - m_t1;
  201. m_t2 = - m_t2;
  202. if( m_t1 > 3600 || m_t2 > 3600 )
  203. {
  204. m_t1 -= 3600;
  205. m_t2 -= 3600;
  206. }
  207. else if( m_t1 < -3600 || m_t2 < -3600 )
  208. {
  209. m_t1 += 3600;
  210. m_t2 += 3600;
  211. }
  212. }
  213. void LIB_ARC::Rotate( const wxPoint& aCenter, bool aRotateCCW )
  214. {
  215. int rot_angle = aRotateCCW ? -900 : 900;
  216. RotatePoint( &m_Pos, aCenter, rot_angle );
  217. RotatePoint( &m_ArcStart, aCenter, rot_angle );
  218. RotatePoint( &m_ArcEnd, aCenter, rot_angle );
  219. m_t1 -= rot_angle;
  220. m_t2 -= rot_angle;
  221. if( m_t1 > 3600 || m_t2 > 3600 )
  222. {
  223. m_t1 -= 3600;
  224. m_t2 -= 3600;
  225. }
  226. else if( m_t1 < -3600 || m_t2 < -3600 )
  227. {
  228. m_t1 += 3600;
  229. m_t2 += 3600;
  230. }
  231. }
  232. void LIB_ARC::Plot( PLOTTER* aPlotter, const wxPoint& aOffset, bool aFill,
  233. const TRANSFORM& aTransform )
  234. {
  235. wxASSERT( aPlotter != NULL );
  236. int t1 = m_t1;
  237. int t2 = m_t2;
  238. wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  239. aTransform.MapAngles( &t1, &t2 );
  240. if( aFill && m_Fill == FILLED_WITH_BG_BODYCOLOR )
  241. {
  242. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
  243. aPlotter->Arc( pos, -t2, -t1, m_Radius, FILLED_WITH_BG_BODYCOLOR, 0 );
  244. }
  245. bool already_filled = m_Fill == FILLED_WITH_BG_BODYCOLOR;
  246. auto pen_size = GetPenSize();
  247. if( !already_filled || pen_size > 0 )
  248. {
  249. pen_size = std::max( 0, pen_size );
  250. aPlotter->SetColor( GetLayerColor( LAYER_DEVICE ) );
  251. aPlotter->Arc( pos, -t2, -t1, m_Radius, already_filled ? NO_FILL : m_Fill, GetPenSize() );
  252. }
  253. }
  254. int LIB_ARC::GetPenSize() const
  255. {
  256. if( m_Width > 0 )
  257. return m_Width;
  258. if( m_Width == 0 )
  259. return GetDefaultLineThickness();
  260. return -1; // a value to use a minimal pen size
  261. }
  262. void LIB_ARC::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset, void* aData,
  263. const TRANSFORM& aTransform )
  264. {
  265. // Don't draw the arc until the end point is selected. Only the edit indicators
  266. // get drawn at this time.
  267. if( IsNew() && m_lastEditState == 1 )
  268. return;
  269. wxPoint pos1, pos2, posc;
  270. COLOR4D color = GetLayerColor( LAYER_DEVICE );
  271. COLOR4D bgColor = GetLayerColor( LAYER_DEVICE_BACKGROUND );
  272. pos1 = aTransform.TransformCoordinate( m_ArcEnd ) + aOffset;
  273. pos2 = aTransform.TransformCoordinate( m_ArcStart ) + aOffset;
  274. posc = aTransform.TransformCoordinate( m_Pos ) + aOffset;
  275. int pt1 = m_t1;
  276. int pt2 = m_t2;
  277. bool swap = aTransform.MapAngles( &pt1, &pt2 );
  278. if( swap )
  279. {
  280. std::swap( pos1.x, pos2.x );
  281. std::swap( pos1.y, pos2.y );
  282. }
  283. FILL_T fill = aData ? NO_FILL : m_Fill;
  284. EDA_RECT* const clipbox = aPanel? aPanel->GetClipBox() : NULL;
  285. if( fill == FILLED_WITH_BG_BODYCOLOR )
  286. {
  287. GRFilledArc( clipbox, aDC, posc.x, posc.y, pt1, pt2, m_Radius, GetPenSize( ),
  288. bgColor, bgColor );
  289. }
  290. else if( fill == FILLED_SHAPE && !aData )
  291. {
  292. GRFilledArc( clipbox, aDC, posc.x, posc.y, pt1, pt2, m_Radius,
  293. color, color );
  294. }
  295. else
  296. {
  297. GRArc1( clipbox, aDC, pos1.x, pos1.y, pos2.x, pos2.y, posc.x, posc.y, GetPenSize(),
  298. color );
  299. }
  300. }
  301. const EDA_RECT LIB_ARC::GetBoundingBox() const
  302. {
  303. int minX, minY, maxX, maxY, angleStart, angleEnd;
  304. EDA_RECT rect;
  305. wxPoint nullPoint, startPos, endPos, centerPos;
  306. wxPoint normStart = m_ArcStart - m_Pos;
  307. wxPoint normEnd = m_ArcEnd - m_Pos;
  308. if( ( normStart == nullPoint ) || ( normEnd == nullPoint ) || ( m_Radius == 0 ) )
  309. {
  310. wxLogDebug( wxT("Invalid arc drawing definition, center(%d, %d) \
  311. start(%d, %d), end(%d, %d), radius %d" ),
  312. m_Pos.x, m_Pos.y, m_ArcStart.x, m_ArcStart.y, m_ArcEnd.x,
  313. m_ArcEnd.y, m_Radius );
  314. return rect;
  315. }
  316. endPos = DefaultTransform.TransformCoordinate( m_ArcEnd );
  317. startPos = DefaultTransform.TransformCoordinate( m_ArcStart );
  318. centerPos = DefaultTransform.TransformCoordinate( m_Pos );
  319. angleStart = m_t1;
  320. angleEnd = m_t2;
  321. if( DefaultTransform.MapAngles( &angleStart, &angleEnd ) )
  322. {
  323. std::swap( endPos.x, startPos.x );
  324. std::swap( endPos.y, startPos.y );
  325. }
  326. /* Start with the start and end point of the arc. */
  327. minX = std::min( startPos.x, endPos.x );
  328. minY = std::min( startPos.y, endPos.y );
  329. maxX = std::max( startPos.x, endPos.x );
  330. maxY = std::max( startPos.y, endPos.y );
  331. /* Zero degrees is a special case. */
  332. if( angleStart == 0 )
  333. maxX = centerPos.x + m_Radius;
  334. /* Arc end angle wrapped passed 360. */
  335. if( angleStart > angleEnd )
  336. angleEnd += 3600;
  337. if( angleStart <= 900 && angleEnd >= 900 ) /* 90 deg */
  338. maxY = centerPos.y + m_Radius;
  339. if( angleStart <= 1800 && angleEnd >= 1800 ) /* 180 deg */
  340. minX = centerPos.x - m_Radius;
  341. if( angleStart <= 2700 && angleEnd >= 2700 ) /* 270 deg */
  342. minY = centerPos.y - m_Radius;
  343. if( angleStart <= 3600 && angleEnd >= 3600 ) /* 0 deg */
  344. maxX = centerPos.x + m_Radius;
  345. rect.SetOrigin( minX, minY );
  346. rect.SetEnd( maxX, maxY );
  347. rect.Inflate( ( GetPenSize()+1 ) / 2 );
  348. return rect;
  349. }
  350. void LIB_ARC::GetMsgPanelInfo( EDA_UNITS_T aUnits, std::vector< MSG_PANEL_ITEM >& aList )
  351. {
  352. wxString msg;
  353. EDA_RECT bBox = GetBoundingBox();
  354. LIB_ITEM::GetMsgPanelInfo( aUnits, aList );
  355. msg = MessageTextFromValue( aUnits, m_Width, true );
  356. aList.push_back( MSG_PANEL_ITEM( _( "Line Width" ), msg, BLUE ) );
  357. msg.Printf( wxT( "(%d, %d, %d, %d)" ), bBox.GetOrigin().x,
  358. bBox.GetOrigin().y, bBox.GetEnd().x, bBox.GetEnd().y );
  359. aList.push_back( MSG_PANEL_ITEM( _( "Bounding Box" ), msg, BROWN ) );
  360. }
  361. wxString LIB_ARC::GetSelectMenuText( EDA_UNITS_T aUnits ) const
  362. {
  363. return wxString::Format( _( "Arc center (%s, %s), radius %s" ),
  364. MessageTextFromValue( aUnits, m_Pos.x ),
  365. MessageTextFromValue( aUnits, m_Pos.y ),
  366. MessageTextFromValue( aUnits, m_Radius ) );
  367. }
  368. BITMAP_DEF LIB_ARC::GetMenuImage() const
  369. {
  370. return add_arc_xpm;
  371. }
  372. void LIB_ARC::BeginEdit( STATUS_FLAGS aEditMode, const wxPoint aPosition )
  373. {
  374. wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  375. wxT( "Invalid edit mode for LIB_ARC object." ) );
  376. if( aEditMode == IS_NEW )
  377. {
  378. m_ArcStart = m_ArcEnd = aPosition;
  379. m_editState = m_lastEditState = 1;
  380. }
  381. else if( aEditMode == IS_MOVED )
  382. {
  383. m_initialPos = m_Pos;
  384. m_initialCursorPos = aPosition;
  385. }
  386. else
  387. {
  388. // The arc center point has to be rotated with while adjusting the
  389. // start or end point, determine the side of this point and the distance
  390. // from the start / end point
  391. wxPoint middlePoint = wxPoint( (m_ArcStart.x + m_ArcEnd.x) / 2,
  392. (m_ArcStart.y + m_ArcEnd.y) / 2 );
  393. wxPoint centerVector = m_Pos - middlePoint;
  394. wxPoint startEndVector = twoPointVector( m_ArcStart, m_ArcEnd );
  395. m_editCenterDistance = EuclideanNorm( centerVector );
  396. // Determine on which side is the center point
  397. m_editDirection = CrossProduct( startEndVector, centerVector ) ? 1 : -1;
  398. // Drag either the start, end point or the outline
  399. if( HitTestPoints( m_ArcStart, aPosition, MINIMUM_SELECTION_DISTANCE ) )
  400. {
  401. m_editSelectPoint = ARC_STATUS_START;
  402. }
  403. else if( HitTestPoints( m_ArcEnd, aPosition, MINIMUM_SELECTION_DISTANCE ) )
  404. {
  405. m_editSelectPoint = ARC_STATUS_END;
  406. }
  407. else
  408. {
  409. m_editSelectPoint = ARC_STATUS_OUTLINE;
  410. }
  411. m_editState = 0;
  412. }
  413. m_Flags = aEditMode;
  414. }
  415. bool LIB_ARC::ContinueEdit( const wxPoint aPosition )
  416. {
  417. wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0, false,
  418. wxT( "Bad call to ContinueEdit(). LIB_ARC is not being edited." ) );
  419. if( m_Flags == IS_NEW )
  420. {
  421. if( m_editState == 1 ) // Second position yields the arc segment length.
  422. {
  423. m_ArcEnd = aPosition;
  424. m_editState = 2;
  425. return true; // Need third position to calculate center point.
  426. }
  427. }
  428. return false;
  429. }
  430. void LIB_ARC::EndEdit( const wxPoint& aPosition, bool aAbort )
  431. {
  432. wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED | IS_RESIZED ) ) != 0,
  433. wxT( "Bad call to EndEdit(). LIB_ARC is not being edited." ) );
  434. m_lastEditState = 0;
  435. m_editState = 0;
  436. m_Flags = 0;
  437. }
  438. void LIB_ARC::CalcEdit( const wxPoint& aPosition )
  439. {
  440. if( m_Flags == IS_RESIZED )
  441. {
  442. wxPoint newCenterPoint, startPos, endPos;
  443. // Choose the point of the arc to be adjusted
  444. if( m_editSelectPoint == ARC_STATUS_START )
  445. {
  446. startPos = aPosition;
  447. endPos = m_ArcEnd;
  448. }
  449. else if( m_editSelectPoint == ARC_STATUS_END )
  450. {
  451. endPos = aPosition;
  452. startPos = m_ArcStart;
  453. }
  454. else
  455. {
  456. // Use the cursor for adjusting the arc curvature
  457. startPos = m_ArcStart;
  458. endPos = m_ArcEnd;
  459. // If the distance is too small, use the old center point
  460. // else the new center point is calculated over the three points start/end/cursor
  461. if( DistanceLinePoint( startPos, endPos, aPosition ) > MINIMUM_SELECTION_DISTANCE )
  462. {
  463. newCenterPoint = calcCenter( startPos, aPosition, endPos );
  464. }
  465. else
  466. {
  467. newCenterPoint = m_Pos;
  468. }
  469. // Determine if the arc angle is larger than 180 degrees -> this happens if both
  470. // points (cursor position, center point) lie on the same side of the vector
  471. // start-end
  472. double crossA = CrossProduct( twoPointVector( startPos, endPos ),
  473. twoPointVector( endPos, aPosition ) );
  474. double crossB = CrossProduct( twoPointVector( startPos, endPos ),
  475. twoPointVector( endPos, newCenterPoint ) );
  476. if( ( crossA < 0 && crossB < 0 ) || ( crossA >= 0 && crossB >= 0 ) )
  477. newCenterPoint = m_Pos;
  478. }
  479. if( m_editSelectPoint == ARC_STATUS_START || m_editSelectPoint == ARC_STATUS_END )
  480. {
  481. // Compute the new center point when the start/end points are modified
  482. wxPoint middlePoint = wxPoint( (startPos.x + endPos.x) / 2,
  483. (startPos.y + endPos.y) / 2 );
  484. wxPoint startEndVector = twoPointVector( startPos, endPos );
  485. wxPoint perpendicularVector = wxPoint( -startEndVector.y, startEndVector.x );
  486. double lengthPerpendicularVector = EuclideanNorm( perpendicularVector );
  487. // prevent too large values, division / 0
  488. if( lengthPerpendicularVector < 1e-1 )
  489. lengthPerpendicularVector = 1e-1;
  490. perpendicularVector.x = (int) ( (double) perpendicularVector.x *
  491. m_editCenterDistance /
  492. lengthPerpendicularVector ) * m_editDirection;
  493. perpendicularVector.y = (int) ( (double) perpendicularVector.y *
  494. m_editCenterDistance /
  495. lengthPerpendicularVector ) * m_editDirection;
  496. newCenterPoint = middlePoint + perpendicularVector;
  497. m_ArcStart = startPos;
  498. m_ArcEnd = endPos;
  499. }
  500. m_Pos = newCenterPoint;
  501. CalcRadiusAngles();
  502. }
  503. else if( m_Flags == IS_NEW )
  504. {
  505. if( m_editState == 1 )
  506. {
  507. m_ArcEnd = aPosition;
  508. }
  509. if( m_editState != m_lastEditState )
  510. m_lastEditState = m_editState;
  511. // Keep the arc center point up to date. Otherwise, there will be edit graphic
  512. // artifacts left behind from the initial draw.
  513. int dx, dy;
  514. int cX, cY;
  515. double angle;
  516. cX = aPosition.x;
  517. cY = aPosition.y;
  518. dx = m_ArcEnd.x - m_ArcStart.x;
  519. dy = m_ArcEnd.y - m_ArcStart.y;
  520. cX -= m_ArcStart.x;
  521. cY -= m_ArcStart.y;
  522. angle = ArcTangente( dy, dx );
  523. RotatePoint( &dx, &dy, angle ); /* The segment dx, dy is horizontal
  524. * -> Length = dx, dy = 0 */
  525. RotatePoint( &cX, &cY, angle );
  526. cX = dx / 2; /* cX, cY is on the median segment 0.0 a dx, 0 */
  527. RotatePoint( &cX, &cY, -angle );
  528. cX += m_ArcStart.x;
  529. cY += m_ArcStart.y;
  530. m_Pos.x = cX;
  531. m_Pos.y = cY;
  532. CalcRadiusAngles();
  533. }
  534. else if( m_Flags == IS_MOVED )
  535. {
  536. Move( m_initialPos + aPosition - m_initialCursorPos );
  537. }
  538. }
  539. void LIB_ARC::CalcRadiusAngles()
  540. {
  541. wxPoint centerStartVector = twoPointVector( m_Pos, m_ArcStart );
  542. wxPoint centerEndVector = twoPointVector( m_Pos, m_ArcEnd );
  543. m_Radius = KiROUND( EuclideanNorm( centerStartVector ) );
  544. // Angles in eeschema are still integers
  545. m_t1 = KiROUND( ArcTangente( centerStartVector.y, centerStartVector.x ) );
  546. m_t2 = KiROUND( ArcTangente( centerEndVector.y, centerEndVector.x ) );
  547. NORMALIZE_ANGLE_POS( m_t1 );
  548. NORMALIZE_ANGLE_POS( m_t2 ); // angles = 0 .. 3600
  549. // Restrict angle to less than 180 to avoid PBS display mirror Trace because it is
  550. // assumed that the arc is less than 180 deg to find orientation after rotate or mirror.
  551. if( (m_t2 - m_t1) > 1800 )
  552. m_t2 -= 3600;
  553. else if( (m_t2 - m_t1) <= -1800 )
  554. m_t2 += 3600;
  555. while( (m_t2 - m_t1) >= 1800 )
  556. {
  557. m_t2--;
  558. m_t1++;
  559. }
  560. while( (m_t1 - m_t2) >= 1800 )
  561. {
  562. m_t2++;
  563. m_t1--;
  564. }
  565. NORMALIZE_ANGLE_POS( m_t1 );
  566. if( !IsMoving() )
  567. NORMALIZE_ANGLE_POS( m_t2 );
  568. }