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.

1432 lines
48 KiB

2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright The 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. #include "sch_point_editor.h"
  25. #include <functional>
  26. using namespace std::placeholders;
  27. #include <ee_grid_helper.h>
  28. #include <tool/tool_manager.h>
  29. #include <sch_commit.h>
  30. #include <view/view_controls.h>
  31. #include <gal/graphics_abstraction_layer.h>
  32. #include <geometry/seg.h>
  33. #include <geometry/shape_utils.h>
  34. #include <tool/point_editor_behavior.h>
  35. #include <tools/sch_actions.h>
  36. #include <tools/sch_selection_tool.h>
  37. #include <sch_edit_frame.h>
  38. #include <sch_line.h>
  39. #include <sch_bitmap.h>
  40. #include <sch_sheet.h>
  41. #include <sch_textbox.h>
  42. #include <sch_table.h>
  43. #include <symbol_editor/symbol_editor_settings.h>
  44. static const std::vector<KICAD_T> pointEditorTypes = { SCH_SHAPE_T,
  45. SCH_RULE_AREA_T,
  46. SCH_TEXTBOX_T,
  47. SCH_TABLECELL_T,
  48. SCH_SHEET_T,
  49. SCH_ITEM_LOCATE_GRAPHIC_LINE_T,
  50. SCH_BITMAP_T };
  51. // Few constants to avoid using bare numbers for point indices
  52. enum ARC_POINTS
  53. {
  54. ARC_START, ARC_END, ARC_CENTER
  55. };
  56. enum RECTANGLE_POINTS
  57. {
  58. RECT_TOPLEFT, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT, RECT_CENTER
  59. };
  60. enum RECTANGLE_LINES
  61. {
  62. RECT_TOP, RECT_RIGHT, RECT_BOT, RECT_LEFT
  63. };
  64. enum REFIMAGE_POINTS
  65. {
  66. REFIMG_ORIGIN = RECT_BOTRIGHT + 1
  67. };
  68. enum TABLECELL_POINTS
  69. {
  70. COL_WIDTH, ROW_HEIGHT
  71. };
  72. enum LINE_POINTS
  73. {
  74. LINE_START, LINE_END
  75. };
  76. class LINE_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  77. {
  78. public:
  79. LINE_POINT_EDIT_BEHAVIOR( SCH_LINE& aLine, SCH_SCREEN& aScreen ) :
  80. m_line( aLine ),
  81. m_screen( aScreen )
  82. {
  83. }
  84. void MakePoints( EDIT_POINTS& aPoints ) override
  85. {
  86. std::pair<EDA_ITEM*, int> connectedStart = { nullptr, STARTPOINT };
  87. std::pair<EDA_ITEM*, int> connectedEnd = { nullptr, STARTPOINT };
  88. for( SCH_ITEM* test : m_screen.Items().OfType( SCH_LINE_T ) )
  89. {
  90. if( test->GetLayer() != LAYER_NOTES )
  91. continue;
  92. if( test == &m_line )
  93. continue;
  94. SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
  95. if( testLine->GetStartPoint() == m_line.GetStartPoint() )
  96. {
  97. connectedStart = { testLine, STARTPOINT };
  98. }
  99. else if( testLine->GetEndPoint() == m_line.GetStartPoint() )
  100. {
  101. connectedStart = { testLine, ENDPOINT };
  102. }
  103. else if( testLine->GetStartPoint() == m_line.GetEndPoint() )
  104. {
  105. connectedEnd = { testLine, STARTPOINT };
  106. }
  107. else if( testLine->GetEndPoint() == m_line.GetEndPoint() )
  108. {
  109. connectedEnd = { testLine, ENDPOINT };
  110. }
  111. }
  112. aPoints.AddPoint( m_line.GetStartPoint(), connectedStart );
  113. aPoints.AddPoint( m_line.GetEndPoint(), connectedEnd );
  114. }
  115. void UpdatePoints( EDIT_POINTS& aPoints ) override
  116. {
  117. aPoints.Point( LINE_START ).SetPosition( m_line.GetStartPoint() );
  118. aPoints.Point( LINE_END ).SetPosition( m_line.GetEndPoint() );
  119. }
  120. void UpdateItem( const EDIT_POINT& aEditedPoints, EDIT_POINTS& aPoints, COMMIT& aCommit,
  121. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  122. {
  123. m_line.SetStartPoint( aPoints.Point( LINE_START ).GetPosition() );
  124. m_line.SetEndPoint( aPoints.Point( LINE_END ).GetPosition() );
  125. std::pair<EDA_ITEM*, int> connected = aPoints.Point( LINE_START ).GetConnected();
  126. if( connected.first )
  127. {
  128. aCommit.Modify( connected.first, &m_screen );
  129. aUpdatedItems.push_back( connected.first );
  130. if( connected.second == STARTPOINT )
  131. static_cast<SCH_LINE*>( connected.first )->SetStartPoint( m_line.GetStartPoint() );
  132. else if( connected.second == ENDPOINT )
  133. static_cast<SCH_LINE*>( connected.first )->SetEndPoint( m_line.GetStartPoint() );
  134. }
  135. connected = aPoints.Point( LINE_END ).GetConnected();
  136. if( connected.first )
  137. {
  138. aCommit.Modify( connected.first, &m_screen );
  139. aUpdatedItems.push_back( connected.first );
  140. if( connected.second == STARTPOINT )
  141. static_cast<SCH_LINE*>( connected.first )->SetStartPoint( m_line.GetEndPoint() );
  142. else if( connected.second == ENDPOINT )
  143. static_cast<SCH_LINE*>( connected.first )->SetEndPoint( m_line.GetEndPoint() );
  144. }
  145. }
  146. private:
  147. SCH_LINE& m_line;
  148. SCH_SCREEN& m_screen;
  149. };
  150. class ARC_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  151. {
  152. public:
  153. ARC_POINT_EDIT_BEHAVIOR( SCH_SHAPE& aArc ) :
  154. m_arc( aArc )
  155. {
  156. wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
  157. }
  158. void MakePoints( EDIT_POINTS& aPoints ) override
  159. {
  160. aPoints.AddPoint( m_arc.GetStart() );
  161. aPoints.AddPoint( m_arc.GetEnd() );
  162. aPoints.AddPoint( m_arc.GetPosition() );
  163. aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
  164. aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
  165. }
  166. void UpdatePoints( EDIT_POINTS& aPoints ) override
  167. {
  168. aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
  169. aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
  170. aPoints.Point( ARC_CENTER ).SetPosition( m_arc.GetPosition() );
  171. }
  172. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  173. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  174. {
  175. if( isModified( aEditedPoint, aPoints.Point( ARC_START ) ) )
  176. {
  177. m_arc.SetEditState( 2 );
  178. m_arc.CalcEdit( aPoints.Point( ARC_START ).GetPosition() );
  179. }
  180. else if( isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
  181. {
  182. m_arc.SetEditState( 3 );
  183. m_arc.CalcEdit( aPoints.Point( ARC_END ).GetPosition() );
  184. }
  185. else if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
  186. {
  187. m_arc.SetEditState( 4 );
  188. m_arc.CalcEdit( aPoints.Point( ARC_CENTER ).GetPosition() );
  189. }
  190. aUpdatedItems.push_back( &m_arc );
  191. }
  192. private:
  193. SCH_SHAPE& m_arc;
  194. };
  195. class BITMAP_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  196. {
  197. public:
  198. BITMAP_POINT_EDIT_BEHAVIOR( SCH_BITMAP& aBitmap ) :
  199. m_bitmap( aBitmap )
  200. {}
  201. void MakePoints( EDIT_POINTS& aPoints ) override
  202. {
  203. const REFERENCE_IMAGE& refImage = m_bitmap.GetReferenceImage();
  204. const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
  205. const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
  206. aPoints.AddPoint( topLeft );
  207. aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
  208. aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
  209. aPoints.AddPoint( botRight );
  210. aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
  211. }
  212. void UpdatePoints( EDIT_POINTS& aPoints ) override
  213. {
  214. const REFERENCE_IMAGE& refImage = m_bitmap.GetReferenceImage();
  215. const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
  216. const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
  217. aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
  218. aPoints.Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
  219. aPoints.Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
  220. aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
  221. aPoints.Point( REFIMG_ORIGIN )
  222. .SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
  223. }
  224. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  225. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  226. {
  227. REFERENCE_IMAGE& refImg = m_bitmap.GetReferenceImage();
  228. const VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
  229. const VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
  230. const VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
  231. const VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
  232. const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
  233. if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
  234. {
  235. // Moving the transform origin
  236. // As the other points didn't move, we can get the image extent from them
  237. const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
  238. refImg.SetTransformOriginOffset( newOffset );
  239. }
  240. else
  241. {
  242. const VECTOR2I oldOrigin = refImg.GetPosition() + refImg.GetTransformOriginOffset();
  243. const VECTOR2I oldSize = refImg.GetSize();
  244. const VECTOR2I pos = refImg.GetPosition();
  245. OPT_VECTOR2I newCorner;
  246. VECTOR2I oldCorner = pos;
  247. if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) ) )
  248. {
  249. newCorner = topLeft;
  250. oldCorner -= oldSize / 2;
  251. }
  252. else if( isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) ) )
  253. {
  254. newCorner = topRight;
  255. oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
  256. }
  257. else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
  258. {
  259. newCorner = botLeft;
  260. oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
  261. }
  262. else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) ) )
  263. {
  264. newCorner = botRight;
  265. oldCorner += oldSize / 2;
  266. }
  267. if( newCorner )
  268. {
  269. // Turn in the respective vectors from the origin
  270. *newCorner -= xfrmOrigin;
  271. oldCorner -= oldOrigin;
  272. // If we tried to cross the origin, clamp it to stop it
  273. if( sign( newCorner->x ) != sign( oldCorner.x )
  274. || sign( newCorner->y ) != sign( oldCorner.y ) )
  275. {
  276. *newCorner = VECTOR2I( 0, 0 );
  277. }
  278. const double newLength = newCorner->EuclideanNorm();
  279. const double oldLength = oldCorner.EuclideanNorm();
  280. double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
  281. // Clamp the scaling to a minimum of 50 mils
  282. VECTOR2I newSize = oldSize * ratio;
  283. double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
  284. double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
  285. ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
  286. // Also handles the origin offset
  287. refImg.SetImageScale( refImg.GetImageScale() * ratio );
  288. }
  289. }
  290. aUpdatedItems.push_back( &m_bitmap );
  291. }
  292. private:
  293. SCH_BITMAP& m_bitmap;
  294. };
  295. class SCH_TABLECELL_POINT_EDIT_BEHAVIOR : public EDA_TABLECELL_POINT_EDIT_BEHAVIOR
  296. {
  297. public:
  298. SCH_TABLECELL_POINT_EDIT_BEHAVIOR( SCH_TABLECELL& aCell, SCH_SCREEN& aScreen ) :
  299. EDA_TABLECELL_POINT_EDIT_BEHAVIOR( aCell ),
  300. m_cell( aCell ),
  301. m_screen( aScreen )
  302. {
  303. }
  304. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  305. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  306. {
  307. SCH_TABLE& table = static_cast<SCH_TABLE&>( *m_cell.GetParent() );
  308. bool rotated = !m_cell.GetTextAngle().IsHorizontal();
  309. aCommit.Modify( &table, &m_screen );
  310. aUpdatedItems.push_back( &table );
  311. if( rotated )
  312. {
  313. if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
  314. {
  315. m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
  316. int colWidth = std::abs( m_cell.GetRectangleHeight() );
  317. for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
  318. colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
  319. table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
  320. }
  321. else if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
  322. {
  323. m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
  324. int rowHeight = m_cell.GetRectangleWidth();
  325. for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
  326. rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
  327. table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
  328. }
  329. }
  330. else
  331. {
  332. if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
  333. {
  334. m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
  335. int colWidth = m_cell.GetRectangleWidth();
  336. for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
  337. colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
  338. table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
  339. }
  340. else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
  341. {
  342. m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
  343. int rowHeight = m_cell.GetRectangleHeight();
  344. for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
  345. rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
  346. table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
  347. }
  348. }
  349. table.Normalize();
  350. }
  351. private:
  352. SCH_TABLECELL& m_cell;
  353. SCH_SCREEN& m_screen;
  354. };
  355. class RECTANGLE_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  356. {
  357. public:
  358. RECTANGLE_POINT_EDIT_BEHAVIOR( SCH_SHAPE& aRect, EDA_DRAW_FRAME& aFrame ) :
  359. m_rect( aRect ),
  360. m_frame( aFrame )
  361. {
  362. }
  363. static void MakePoints( SCH_SHAPE& aRect, EDIT_POINTS& aPoints )
  364. {
  365. VECTOR2I topLeft = aRect.GetPosition();
  366. VECTOR2I botRight = aRect.GetEnd();
  367. aPoints.AddPoint( topLeft );
  368. aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
  369. aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
  370. aPoints.AddPoint( botRight );
  371. aPoints.AddPoint( aRect.GetCenter() );
  372. aPoints.AddLine( aPoints.Point( RECT_TOPLEFT ), aPoints.Point( RECT_TOPRIGHT ) );
  373. aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
  374. aPoints.AddLine( aPoints.Point( RECT_TOPRIGHT ), aPoints.Point( RECT_BOTRIGHT ) );
  375. aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
  376. aPoints.AddLine( aPoints.Point( RECT_BOTRIGHT ), aPoints.Point( RECT_BOTLEFT ) );
  377. aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
  378. aPoints.AddLine( aPoints.Point( RECT_BOTLEFT ), aPoints.Point( RECT_TOPLEFT ) );
  379. aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
  380. }
  381. static void UpdatePoints( SCH_SHAPE& aRect, EDIT_POINTS& aPoints )
  382. {
  383. VECTOR2I topLeft = aRect.GetPosition();
  384. VECTOR2I botRight = aRect.GetEnd();
  385. aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
  386. aPoints.Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
  387. aPoints.Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
  388. aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
  389. aPoints.Point( RECT_CENTER ).SetPosition( aRect.GetCenter() );
  390. }
  391. /**
  392. * Update the coordinates of 4 corners of a rectangle, according to constraints
  393. * and the moved corner
  394. * @param minWidth is the minimal width constraint
  395. * @param minHeight is the minimal height constraint
  396. * @param topLeft is the RECT_TOPLEFT to constraint
  397. * @param topRight is the RECT_TOPRIGHT to constraint
  398. * @param botLeft is the RECT_BOTLEFT to constraint
  399. * @param botRight is the RECT_BOTRIGHT to constraint
  400. */
  401. static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aPoints,
  402. int minWidth, int minHeight, VECTOR2I& topLeft, VECTOR2I& topRight,
  403. VECTOR2I& botLeft, VECTOR2I& botRight )
  404. {
  405. if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) ) )
  406. {
  407. // pin edited point within opposite corner
  408. topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
  409. topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
  410. // push edited point edges to adjacent corners
  411. topRight.y = topLeft.y;
  412. botLeft.x = topLeft.x;
  413. }
  414. else if( isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) ) )
  415. {
  416. // pin edited point within opposite corner
  417. topRight.x = std::max( topRight.x, botLeft.x + minWidth );
  418. topRight.y = std::min( topRight.y, botLeft.y - minHeight );
  419. // push edited point edges to adjacent corners
  420. topLeft.y = topRight.y;
  421. botRight.x = topRight.x;
  422. }
  423. else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
  424. {
  425. // pin edited point within opposite corner
  426. botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
  427. botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
  428. // push edited point edges to adjacent corners
  429. botRight.y = botLeft.y;
  430. topLeft.x = botLeft.x;
  431. }
  432. else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) ) )
  433. {
  434. // pin edited point within opposite corner
  435. botRight.x = std::max( botRight.x, topLeft.x + minWidth );
  436. botRight.y = std::max( botRight.y, topLeft.y + minHeight );
  437. // push edited point edges to adjacent corners
  438. botLeft.y = botRight.y;
  439. topRight.x = botRight.x;
  440. }
  441. else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
  442. {
  443. topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
  444. }
  445. else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
  446. {
  447. topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
  448. }
  449. else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
  450. {
  451. botRight.y = std::max( botRight.y, topLeft.y + minHeight );
  452. }
  453. else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
  454. {
  455. botRight.x = std::max( botRight.x, topLeft.x + minWidth );
  456. }
  457. }
  458. static void UpdateItem( SCH_SHAPE& aRect, const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
  459. {
  460. VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
  461. VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
  462. VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
  463. VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
  464. PinEditedCorner( aEditedPoint, aPoints, schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ),
  465. topLeft, topRight, botLeft, botRight );
  466. if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
  467. || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
  468. || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
  469. || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
  470. {
  471. aRect.SetPosition( topLeft );
  472. aRect.SetEnd( botRight );
  473. }
  474. else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
  475. {
  476. aRect.SetStartY( topLeft.y );
  477. }
  478. else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
  479. {
  480. aRect.SetStartX( topLeft.x );
  481. }
  482. else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
  483. {
  484. aRect.SetEndY( botRight.y );
  485. }
  486. else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
  487. {
  488. aRect.SetEndX( botRight.x );
  489. }
  490. for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
  491. {
  492. if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
  493. {
  494. aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
  495. }
  496. }
  497. }
  498. void MakePoints( EDIT_POINTS& aPoints ) override
  499. {
  500. m_rect.Normalize();
  501. MakePoints( m_rect, aPoints );
  502. }
  503. void UpdatePoints( EDIT_POINTS& aPoints ) override
  504. {
  505. m_rect.Normalize();
  506. UpdatePoints( m_rect, aPoints );
  507. }
  508. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  509. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  510. {
  511. VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
  512. VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
  513. VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
  514. VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
  515. PinEditedCorner( aEditedPoint, aPoints, schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ),
  516. topLeft, topRight, botLeft, botRight );
  517. const BOX2I oldBox = BOX2I::ByCorners( m_rect.GetStart(), m_rect.GetEnd() );
  518. std::vector<SEG> oldSegs;
  519. std::vector<VECTOR2I> moveVecs;
  520. if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
  521. || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
  522. || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
  523. || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
  524. {
  525. // Corner drags don't update pins. Not only is it an escape hatch to avoid
  526. // moving pins, it also avoids tricky problems when the pins "fall off"
  527. // the ends of one of the two segments and get either left behind or
  528. // "swept up" into the corner.
  529. m_rect.SetPosition( topLeft );
  530. m_rect.SetEnd( botRight );
  531. }
  532. else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
  533. {
  534. VECTOR2I moveVec = aPoints.Point( RECT_CENTER ).GetPosition() - oldBox.GetCenter();
  535. m_rect.Move( moveVec );
  536. }
  537. else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
  538. {
  539. oldSegs = KIGEOM::GetSegsInDirection( oldBox, DIRECTION_45::Directions::N );
  540. moveVecs.emplace_back( 0, topLeft.y - oldBox.GetTop() );
  541. m_rect.SetStartY( topLeft.y );
  542. }
  543. else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
  544. {
  545. oldSegs = KIGEOM::GetSegsInDirection( oldBox, DIRECTION_45::Directions::W );
  546. moveVecs.emplace_back( topLeft.x - oldBox.GetLeft(), 0 );
  547. m_rect.SetStartX( topLeft.x );
  548. }
  549. else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
  550. {
  551. oldSegs = KIGEOM::GetSegsInDirection( oldBox, DIRECTION_45::Directions::S );
  552. moveVecs.emplace_back( 0, botRight.y - oldBox.GetBottom() );
  553. m_rect.SetEndY( botRight.y );
  554. }
  555. else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
  556. {
  557. oldSegs = KIGEOM::GetSegsInDirection( oldBox, DIRECTION_45::Directions::E );
  558. moveVecs.emplace_back( botRight.x - oldBox.GetRight(), 0 );
  559. m_rect.SetEndX( botRight.x );
  560. }
  561. dragPinsOnEdge( oldSegs, moveVecs, m_rect.GetUnit(), aCommit, aUpdatedItems );
  562. for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
  563. {
  564. if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
  565. {
  566. aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
  567. }
  568. }
  569. }
  570. private:
  571. void dragPinsOnEdge( const std::vector<SEG>& aOldEdges, const std::vector<VECTOR2I>& aMoveVecs,
  572. int aEdgeUnit, COMMIT& aCommit,
  573. std::vector<EDA_ITEM*>& aUpdatedItems ) const
  574. {
  575. wxCHECK( aOldEdges.size() == aMoveVecs.size(), /* void */ );
  576. // This only make sense in the symbol editor
  577. if( !m_frame.IsType( FRAME_SCH_SYMBOL_EDITOR ) )
  578. return;
  579. SYMBOL_EDIT_FRAME& editor = static_cast<SYMBOL_EDIT_FRAME&>( m_frame );
  580. // And only if the setting is enabled
  581. if( !editor.GetSettings()->m_dragPinsAlongWithEdges )
  582. return;
  583. // Adjuting pins on a different unit to a unit-limited shape
  584. // seems suspect.
  585. wxCHECK( aEdgeUnit == 0 || aEdgeUnit == editor.GetUnit(), /* void */ );
  586. /*
  587. * Get a list of pins on a line segment
  588. */
  589. const auto getPinsOnSeg =
  590. []( LIB_SYMBOL& aSymbol, int aUnit, const SEG& aSeg,
  591. bool aIncludeEnds ) -> std::vector<SCH_PIN*>
  592. {
  593. std::vector<SCH_PIN*> pins;
  594. for( SCH_PIN* pin : aSymbol.GetPins( aUnit, 0 ) )
  595. {
  596. // Figure out if the pin "connects" to the line
  597. const VECTOR2I pinRootPos = pin->GetPinRoot();
  598. if( aSeg.Contains( pinRootPos ) )
  599. {
  600. if( aIncludeEnds || ( pinRootPos != aSeg.A && pinRootPos != aSeg.B ) )
  601. {
  602. pins.push_back( pin );
  603. }
  604. }
  605. }
  606. return pins;
  607. };
  608. LIB_SYMBOL* const symbol = editor.GetCurSymbol();
  609. for( std::size_t i = 0; i < aOldEdges.size(); ++i )
  610. {
  611. if( aMoveVecs[i] == VECTOR2I( 0, 0 ) || !symbol )
  612. continue;
  613. const std::vector<SCH_PIN*> pins = getPinsOnSeg( *symbol, aEdgeUnit, aOldEdges[i],
  614. false );
  615. for( SCH_PIN* pin : pins )
  616. {
  617. aCommit.Modify( pin );
  618. aUpdatedItems.push_back( pin );
  619. // Move the pin
  620. pin->Move( aMoveVecs[i] );
  621. }
  622. }
  623. }
  624. private:
  625. SCH_SHAPE& m_rect;
  626. EDA_DRAW_FRAME& m_frame;
  627. };
  628. class TEXTBOX_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  629. {
  630. public:
  631. TEXTBOX_POINT_EDIT_BEHAVIOR( SCH_TEXTBOX& aTextbox ) :
  632. m_textbox( aTextbox )
  633. {}
  634. void MakePoints( EDIT_POINTS& aPoints ) override
  635. {
  636. m_textbox.Normalize();
  637. RECTANGLE_POINT_EDIT_BEHAVIOR::MakePoints( m_textbox, aPoints );
  638. }
  639. void UpdatePoints( EDIT_POINTS& aPoints ) override
  640. {
  641. // point editor works only with rectangles having width and height > 0
  642. // Some symbols can have rectangles with width or height < 0
  643. // So normalize the size:
  644. m_textbox.Normalize();
  645. RECTANGLE_POINT_EDIT_BEHAVIOR::UpdatePoints( m_textbox, aPoints );
  646. }
  647. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  648. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  649. {
  650. RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( m_textbox, aEditedPoint, aPoints );
  651. m_textbox.ClearRenderCache();
  652. }
  653. private:
  654. SCH_TEXTBOX& m_textbox;
  655. };
  656. class SHEET_POINT_EDIT_BEHAVIOR : public POINT_EDIT_BEHAVIOR
  657. {
  658. public:
  659. SHEET_POINT_EDIT_BEHAVIOR( SCH_SHEET& aSheet ) :
  660. m_sheet( aSheet )
  661. {}
  662. void MakePoints( EDIT_POINTS& aPoints ) override
  663. {
  664. VECTOR2I topLeft = m_sheet.GetPosition();
  665. VECTOR2I botRight = m_sheet.GetPosition() + m_sheet.GetSize();
  666. aPoints.AddPoint( topLeft );
  667. aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
  668. aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
  669. aPoints.AddPoint( botRight );
  670. aPoints.AddLine( aPoints.Point( RECT_TOPLEFT ), aPoints.Point( RECT_TOPRIGHT ) );
  671. aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
  672. aPoints.AddLine( aPoints.Point( RECT_TOPRIGHT ), aPoints.Point( RECT_BOTRIGHT ) );
  673. aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
  674. aPoints.AddLine( aPoints.Point( RECT_BOTRIGHT ), aPoints.Point( RECT_BOTLEFT ) );
  675. aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
  676. aPoints.AddLine( aPoints.Point( RECT_BOTLEFT ), aPoints.Point( RECT_TOPLEFT ) );
  677. aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
  678. }
  679. void UpdatePoints( EDIT_POINTS& aPoints ) override
  680. {
  681. VECTOR2I topLeft = m_sheet.GetPosition();
  682. VECTOR2I botRight = m_sheet.GetPosition() + m_sheet.GetSize();
  683. aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
  684. aPoints.Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
  685. aPoints.Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
  686. aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
  687. }
  688. void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
  689. std::vector<EDA_ITEM*>& aUpdatedItems ) override
  690. {
  691. VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
  692. VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
  693. VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
  694. VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
  695. VECTOR2I sheetNewPos = m_sheet.GetPosition();
  696. VECTOR2I sheetNewSize = m_sheet.GetSize();
  697. bool editedTopRight = isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) );
  698. bool editedBotLeft = isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) );
  699. bool editedBotRight = isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) );
  700. if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
  701. editedTopRight = true;
  702. else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
  703. editedBotLeft = true;
  704. RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner(
  705. aEditedPoint, aPoints, m_sheet.GetMinWidth( editedTopRight || editedBotRight ),
  706. m_sheet.GetMinHeight( editedBotLeft || editedBotRight ), topLeft, topRight, botLeft,
  707. botRight );
  708. if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
  709. || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
  710. || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
  711. || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
  712. {
  713. sheetNewPos = topLeft;
  714. sheetNewSize = VECTOR2I( botRight.x - topLeft.x, botRight.y - topLeft.y );
  715. }
  716. else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
  717. {
  718. sheetNewPos = VECTOR2I( m_sheet.GetPosition().x, topLeft.y );
  719. sheetNewSize = VECTOR2I( m_sheet.GetSize().x, botRight.y - topLeft.y );
  720. }
  721. else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
  722. {
  723. sheetNewPos = VECTOR2I( topLeft.x, m_sheet.GetPosition().y );
  724. sheetNewSize = VECTOR2I( botRight.x - topLeft.x, m_sheet.GetSize().y );
  725. }
  726. else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
  727. {
  728. sheetNewSize = VECTOR2I( m_sheet.GetSize().x, botRight.y - topLeft.y );
  729. }
  730. else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
  731. {
  732. sheetNewSize = VECTOR2I( botRight.x - topLeft.x, m_sheet.GetSize().y );
  733. }
  734. for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
  735. {
  736. if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
  737. {
  738. aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
  739. }
  740. }
  741. if( m_sheet.GetPosition() != sheetNewPos )
  742. m_sheet.SetPositionIgnoringPins( sheetNewPos );
  743. if( m_sheet.GetSize() != sheetNewSize )
  744. m_sheet.Resize( sheetNewSize );
  745. }
  746. private:
  747. SCH_SHEET& m_sheet;
  748. };
  749. void SCH_POINT_EDITOR::makePointsAndBehavior( EDA_ITEM* aItem )
  750. {
  751. m_editBehavior = nullptr;
  752. m_editPoints = std::make_shared<EDIT_POINTS>( aItem );
  753. if( !aItem )
  754. return;
  755. // Generate list of edit points based on the item type
  756. switch( aItem->Type() )
  757. {
  758. case SCH_SHAPE_T:
  759. {
  760. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
  761. switch( shape->GetShape() )
  762. {
  763. case SHAPE_T::ARC:
  764. m_editBehavior = std::make_unique<ARC_POINT_EDIT_BEHAVIOR>( *shape );
  765. break;
  766. case SHAPE_T::CIRCLE:
  767. m_editBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
  768. break;
  769. case SHAPE_T::RECTANGLE:
  770. m_editBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape, *m_frame );
  771. break;
  772. case SHAPE_T::POLY:
  773. m_editBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
  774. break;
  775. case SHAPE_T::BEZIER:
  776. m_editBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape );
  777. break;
  778. default:
  779. UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
  780. }
  781. break;
  782. }
  783. case SCH_RULE_AREA_T:
  784. {
  785. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
  786. // Implemented directly as a polygon
  787. m_editBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
  788. break;
  789. }
  790. case SCH_TEXTBOX_T:
  791. {
  792. SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( aItem );
  793. m_editBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( *textbox );
  794. break;
  795. }
  796. case SCH_TABLECELL_T:
  797. {
  798. SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( aItem );
  799. m_editBehavior = std::make_unique<SCH_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell, *m_frame->GetScreen() );
  800. break;
  801. }
  802. case SCH_SHEET_T:
  803. {
  804. SCH_SHEET& sheet = static_cast<SCH_SHEET&>( *aItem );
  805. m_editBehavior = std::make_unique<SHEET_POINT_EDIT_BEHAVIOR>( sheet );
  806. break;
  807. }
  808. case SCH_BITMAP_T:
  809. {
  810. SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *aItem );
  811. m_editBehavior = std::make_unique<BITMAP_POINT_EDIT_BEHAVIOR>( bitmap );
  812. break;
  813. }
  814. case SCH_LINE_T:
  815. {
  816. SCH_LINE& line = static_cast<SCH_LINE&>( *aItem );
  817. m_editBehavior = std::make_unique<LINE_POINT_EDIT_BEHAVIOR>( line, *m_frame->GetScreen() );
  818. break;
  819. }
  820. default:
  821. {
  822. m_editPoints.reset();
  823. break;
  824. }
  825. }
  826. // If we got a behavior, generate the points
  827. if( m_editBehavior )
  828. {
  829. wxCHECK( m_editPoints, /* void */ );
  830. m_editBehavior->MakePoints( *m_editPoints );
  831. }
  832. }
  833. SCH_POINT_EDITOR::SCH_POINT_EDITOR() :
  834. SCH_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.PointEditor" ),
  835. m_editedPoint( nullptr ),
  836. m_inPointEditor( false )
  837. {
  838. }
  839. void SCH_POINT_EDITOR::Reset( RESET_REASON aReason )
  840. {
  841. SCH_TOOL_BASE::Reset( aReason );
  842. m_editPoints.reset();
  843. m_editedPoint = nullptr;
  844. }
  845. bool SCH_POINT_EDITOR::Init()
  846. {
  847. SCH_TOOL_BASE::Init();
  848. auto& menu = m_selectionTool->GetToolMenu().GetMenu();
  849. menu.AddItem( SCH_ACTIONS::pointEditorAddCorner,
  850. std::bind( &SCH_POINT_EDITOR::addCornerCondition, this, _1 ) );
  851. menu.AddItem( SCH_ACTIONS::pointEditorRemoveCorner,
  852. std::bind( &SCH_POINT_EDITOR::removeCornerCondition, this, _1 ) );
  853. return true;
  854. }
  855. int SCH_POINT_EDITOR::clearEditedPoints( const TOOL_EVENT& aEvent )
  856. {
  857. setEditedPoint( nullptr );
  858. return 0;
  859. }
  860. void SCH_POINT_EDITOR::updateEditedPoint( const TOOL_EVENT& aEvent )
  861. {
  862. EDIT_POINT* point = m_editedPoint;
  863. if( !m_editPoints )
  864. {
  865. point = nullptr;
  866. }
  867. else if( aEvent.IsMotion() )
  868. {
  869. point = m_editPoints->FindPoint( aEvent.Position(), getView() );
  870. }
  871. else if( aEvent.IsDrag( BUT_LEFT ) )
  872. {
  873. point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
  874. }
  875. else
  876. {
  877. point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition( false ), getView() );
  878. }
  879. if( m_editedPoint != point )
  880. setEditedPoint( point );
  881. }
  882. int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
  883. {
  884. if( !m_selectionTool )
  885. return 0;
  886. if( m_inPointEditor )
  887. return 0;
  888. REENTRANCY_GUARD guard( &m_inPointEditor );
  889. if( m_isSymbolEditor )
  890. {
  891. SYMBOL_EDIT_FRAME* editor = getEditFrame<SYMBOL_EDIT_FRAME>();
  892. if( !editor->IsSymbolEditable() || editor->IsSymbolAlias() )
  893. return 0;
  894. }
  895. const SCH_SELECTION& selection = m_selectionTool->GetSelection();
  896. if( selection.Size() != 1 || !selection.Front()->IsType( pointEditorTypes ) )
  897. return 0;
  898. // Wait till drawing tool is done
  899. if( selection.Front()->IsNew() )
  900. return 0;
  901. Activate();
  902. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  903. EE_GRID_HELPER* grid = new EE_GRID_HELPER( m_toolMgr );
  904. VECTOR2I cursorPos;
  905. KIGFX::VIEW* view = getView();
  906. EDA_ITEM* item = selection.Front();
  907. SCH_COMMIT commit( m_toolMgr );
  908. controls->ShowCursor( true );
  909. makePointsAndBehavior( item );
  910. view->Add( m_editPoints.get() );
  911. setEditedPoint( nullptr );
  912. updateEditedPoint( aEvent );
  913. bool inDrag = false;
  914. // Main loop: keep receiving events
  915. while( TOOL_EVENT* evt = Wait() )
  916. {
  917. if( grid )
  918. {
  919. grid->SetSnap( !evt->Modifier( MD_SHIFT ) );
  920. grid->SetUseGrid( getView()->GetGAL()->GetGridSnapping()
  921. && !evt->DisableGridSnapping() );
  922. }
  923. else
  924. {
  925. // This check is based on the assumption that the grid object must be valid.
  926. // If this assumption is wrong, please fix the code above.
  927. wxCHECK( false, 0 );
  928. }
  929. if( !m_editPoints || evt->IsSelectionEvent() )
  930. break;
  931. if ( !inDrag )
  932. updateEditedPoint( *evt );
  933. if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
  934. {
  935. if( !inDrag )
  936. {
  937. commit.Modify( m_editPoints->GetParent(), m_frame->GetScreen() );
  938. if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
  939. {
  940. shape->SetFlags( IS_MOVING );
  941. shape->SetHatchingDirty();
  942. shape->UpdateHatching();
  943. }
  944. inDrag = true;
  945. }
  946. bool snap = !evt->DisableGridSnapping();
  947. cursorPos = grid->Align( controls->GetMousePosition(),
  948. GRID_HELPER_GRIDS::GRID_GRAPHICS );
  949. controls->ForceCursorPosition( true, cursorPos );
  950. m_editedPoint->SetPosition( controls->GetCursorPosition( snap ) );
  951. updateParentItem( snap, commit );
  952. updatePoints();
  953. }
  954. else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
  955. {
  956. if( !commit.Empty() )
  957. commit.Push( _( "Move Point" ) );
  958. controls->SetAutoPan( false );
  959. if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
  960. {
  961. shape->ClearFlags( IS_MOVING );
  962. shape->SetHatchingDirty();
  963. shape->UpdateHatching();
  964. }
  965. inDrag = false;
  966. }
  967. else if( evt->IsCancelInteractive() || evt->IsActivate() )
  968. {
  969. if( inDrag ) // Restore the last change
  970. {
  971. // Currently we are manually managing the lifetime of the grid
  972. // helpers because there is a bug in the tool stack that adds
  973. // the point editor again when commit.Revert() rebuilds the selection.
  974. // We remove this grid here so the its destructor is called before it
  975. // is added again.
  976. if( grid )
  977. {
  978. delete grid;
  979. grid = nullptr;
  980. }
  981. commit.Revert();
  982. inDrag = false;
  983. break;
  984. }
  985. else if( evt->IsCancelInteractive() )
  986. {
  987. break;
  988. }
  989. if( evt->IsActivate() )
  990. break;
  991. }
  992. else
  993. {
  994. evt->SetPassEvent();
  995. }
  996. controls->SetAutoPan( inDrag );
  997. controls->CaptureCursor( inDrag );
  998. }
  999. if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
  1000. {
  1001. shape->ClearFlags( IS_MOVING );
  1002. shape->SetHatchingDirty();
  1003. shape->UpdateHatching();
  1004. }
  1005. controls->SetAutoPan( false );
  1006. controls->CaptureCursor( false );
  1007. setEditedPoint( nullptr );
  1008. if( m_editPoints )
  1009. {
  1010. view->Remove( m_editPoints.get() );
  1011. m_editPoints.reset();
  1012. m_frame->GetCanvas()->Refresh();
  1013. }
  1014. delete grid;
  1015. return 0;
  1016. }
  1017. void SCH_POINT_EDITOR::updateParentItem( bool aSnapToGrid, SCH_COMMIT& aCommit ) const
  1018. {
  1019. EDA_ITEM* item = m_editPoints->GetParent();
  1020. if( !item )
  1021. return;
  1022. if( !m_editBehavior )
  1023. return;
  1024. std::vector<EDA_ITEM*> updatedItems;
  1025. m_editBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
  1026. for( EDA_ITEM* updatedItem : updatedItems )
  1027. updateItem( updatedItem, true );
  1028. m_frame->SetMsgPanel( item );
  1029. }
  1030. void SCH_POINT_EDITOR::updatePoints()
  1031. {
  1032. if( !m_editPoints || !m_editBehavior )
  1033. return;
  1034. m_editBehavior->UpdatePoints( *m_editPoints );
  1035. getView()->Update( m_editPoints.get() );
  1036. }
  1037. void SCH_POINT_EDITOR::setEditedPoint( EDIT_POINT* aPoint )
  1038. {
  1039. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  1040. if( aPoint )
  1041. {
  1042. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1043. controls->ForceCursorPosition( true, aPoint->GetPosition() );
  1044. controls->ShowCursor( true );
  1045. }
  1046. else
  1047. {
  1048. if( m_frame->ToolStackIsEmpty() )
  1049. controls->ShowCursor( false );
  1050. controls->ForceCursorPosition( false );
  1051. }
  1052. m_editedPoint = aPoint;
  1053. }
  1054. bool SCH_POINT_EDITOR::removeCornerCondition( const SELECTION& )
  1055. {
  1056. bool isRuleArea = false;
  1057. if( m_editPoints )
  1058. isRuleArea = m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T;
  1059. if( !m_editPoints || !m_editedPoint
  1060. || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T || isRuleArea ) )
  1061. {
  1062. return false;
  1063. }
  1064. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
  1065. if( shape->GetPolyShape().IsEmpty() )
  1066. return false;
  1067. SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
  1068. if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
  1069. return false;
  1070. if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
  1071. return false;
  1072. for( const VECTOR2I& pt : poly.CPoints() )
  1073. {
  1074. if( pt == m_editedPoint->GetPosition() )
  1075. return true;
  1076. }
  1077. return false;
  1078. }
  1079. bool SCH_POINT_EDITOR::addCornerCondition( const SELECTION& )
  1080. {
  1081. if( !m_editPoints
  1082. || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
  1083. || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
  1084. {
  1085. return false;
  1086. }
  1087. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
  1088. if( shape->GetShape() != SHAPE_T::POLY )
  1089. return false;
  1090. VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
  1091. double threshold = getView()->ToWorld( EDIT_POINT::POINT_SIZE );
  1092. return shape->HitTest( cursorPos, (int) threshold );
  1093. }
  1094. int SCH_POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
  1095. {
  1096. if( !m_editPoints
  1097. || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
  1098. || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
  1099. {
  1100. return 0;
  1101. }
  1102. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
  1103. SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
  1104. SCH_COMMIT commit( m_toolMgr );
  1105. commit.Modify( shape, m_frame->GetScreen() );
  1106. VECTOR2I cursor = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
  1107. int currentMinDistance = INT_MAX;
  1108. int closestLineStart = 0;
  1109. unsigned numPoints = poly.GetPointCount();
  1110. if( !shape->IsClosed() )
  1111. numPoints -= 1;
  1112. for( unsigned i = 0; i < numPoints; ++i )
  1113. {
  1114. SEG seg = poly.GetSegment( i );
  1115. int distance = seg.Distance( cursor );
  1116. if( distance < currentMinDistance )
  1117. {
  1118. currentMinDistance = distance;
  1119. closestLineStart = i;
  1120. }
  1121. }
  1122. poly.Insert( closestLineStart + 1, cursor );
  1123. updateItem( shape, true );
  1124. updatePoints();
  1125. commit.Push( _( "Add Corner" ) );
  1126. return 0;
  1127. }
  1128. int SCH_POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
  1129. {
  1130. if( !m_editPoints || !m_editedPoint
  1131. || !m_editPoints->GetParent()->IsType( { SCH_SHAPE_T, SCH_RULE_AREA_T } ) )
  1132. {
  1133. return 0;
  1134. }
  1135. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
  1136. SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
  1137. SCH_COMMIT commit( m_toolMgr );
  1138. if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
  1139. return 0;
  1140. if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
  1141. return 0;
  1142. commit.Modify( shape, m_frame->GetScreen() );
  1143. int idx = getEditedPointIndex();
  1144. int last = (int) poly.GetPointCount() - 1;
  1145. if( idx == 0 && poly.GetPoint( 0 ) == poly.GetPoint( last ) )
  1146. {
  1147. poly.Remove( idx );
  1148. poly.SetPoint( last-1, poly.GetPoint( 0 ) );
  1149. }
  1150. else
  1151. {
  1152. poly.Remove( idx );
  1153. }
  1154. shape->SetHatchingDirty();
  1155. setEditedPoint( nullptr );
  1156. updateItem( shape, true );
  1157. updatePoints();
  1158. commit.Push( _( "Remove Corner" ) );
  1159. return 0;
  1160. }
  1161. int SCH_POINT_EDITOR::modifiedSelection( const TOOL_EVENT& aEvent )
  1162. {
  1163. updatePoints();
  1164. return 0;
  1165. }
  1166. void SCH_POINT_EDITOR::setTransitions()
  1167. {
  1168. Go( &SCH_POINT_EDITOR::Main, EVENTS::PointSelectedEvent );
  1169. Go( &SCH_POINT_EDITOR::Main, EVENTS::SelectedEvent );
  1170. Go( &SCH_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
  1171. Go( &SCH_POINT_EDITOR::addCorner, SCH_ACTIONS::pointEditorAddCorner.MakeEvent() );
  1172. Go( &SCH_POINT_EDITOR::removeCorner, SCH_ACTIONS::pointEditorRemoveCorner.MakeEvent() );
  1173. Go( &SCH_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
  1174. Go( &SCH_POINT_EDITOR::clearEditedPoints, EVENTS::ClearedEvent );
  1175. }