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.

480 lines
15 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
  5. * Copyright (C) 2004-2021 KiCad Developers, see change_log.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 <core/kicad_algo.h>
  25. #include <eeschema_id.h>
  26. #include <general.h>
  27. #include <lib_item.h>
  28. #include <sch_bus_entry.h>
  29. #include <sch_symbol.h>
  30. #include <sch_edit_frame.h>
  31. #include <sch_junction.h>
  32. #include <sch_line.h>
  33. #include <sch_no_connect.h>
  34. #include <sch_screen.h>
  35. #include <sch_sheet.h>
  36. #include <sch_view.h>
  37. #include <tool/tool_manager.h>
  38. #include <tools/ee_actions.h>
  39. #include <tools/ee_selection_tool.h>
  40. #include <trigo.h>
  41. std::vector<wxPoint> SCH_EDIT_FRAME::GetSchematicConnections()
  42. {
  43. std::vector<wxPoint> retval;
  44. for( SCH_ITEM* item : GetScreen()->Items() )
  45. {
  46. // Avoid items that are changing
  47. if( !( item->GetEditFlags() & ( IS_DRAGGED | IS_MOVED | IS_DELETED ) ) )
  48. {
  49. std::vector<wxPoint> pts = item->GetConnectionPoints();
  50. retval.insert( retval.end(), pts.begin(), pts.end() );
  51. }
  52. }
  53. // We always have some overlapping connection points. Drop duplicates here
  54. std::sort( retval.begin(), retval.end(),
  55. []( const wxPoint& a, const wxPoint& b ) -> bool
  56. { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
  57. retval.erase(
  58. std::unique( retval.begin(), retval.end() ), retval.end() );
  59. return retval;
  60. }
  61. void SCH_EDIT_FRAME::TestDanglingEnds()
  62. {
  63. std::function<void( SCH_ITEM* )> changeHandler =
  64. [&]( SCH_ITEM* aChangedItem ) -> void
  65. {
  66. GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT );
  67. };
  68. GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
  69. }
  70. bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd )
  71. {
  72. SCH_SCREEN* screen = GetScreen();
  73. bool retval = false;
  74. std::vector<SCH_LINE*> wires;
  75. EDA_RECT bb( aStart, wxSize( 1, 1 ) );
  76. bb.Merge( aEnd );
  77. if( aStart == aEnd )
  78. return retval;
  79. // We cannot modify the RTree while iterating, so push the possible
  80. // wires into a separate structure.
  81. for( EDA_ITEM* item : screen->Items().Overlapping( bb ) )
  82. {
  83. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  84. if( item->Type() == SCH_LINE_T && line->GetLayer() == LAYER_WIRE )
  85. wires.push_back( line );
  86. }
  87. for( SCH_LINE* line : wires )
  88. {
  89. // Don't remove wires that are already deleted or are currently being dragged
  90. if( line->GetEditFlags() & ( STRUCT_DELETED | IS_DRAGGED | IS_MOVED | SKIP_STRUCT ) )
  91. continue;
  92. if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
  93. !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
  94. {
  95. continue;
  96. }
  97. // Don't remove entire wires
  98. if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
  99. || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
  100. {
  101. continue;
  102. }
  103. // Step 1: break the segment on one end. return_line remains line if not broken.
  104. // Ensure that *line points to the segment containing aEnd
  105. SCH_LINE* return_line = line;
  106. BreakSegment( line, aStart, &return_line );
  107. if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
  108. line = return_line;
  109. // Step 2: break the remaining segment. return_line remains line if not broken.
  110. // Ensure that *line _also_ contains aStart. This is our overlapping segment
  111. BreakSegment( line, aEnd, &return_line );
  112. if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
  113. line = return_line;
  114. SaveCopyInUndoList( screen, line, UNDO_REDO::DELETED, true );
  115. RemoveFromScreen( line, screen );
  116. retval = true;
  117. }
  118. return retval;
  119. }
  120. bool SCH_EDIT_FRAME::SchematicCleanUp( SCH_SCREEN* aScreen )
  121. {
  122. PICKED_ITEMS_LIST itemList;
  123. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  124. std::vector<SCH_ITEM*> deletedItems;
  125. std::vector<SCH_LINE*> lines;
  126. std::vector<SCH_JUNCTION*> junctions;
  127. std::vector<SCH_NO_CONNECT*> ncs;
  128. bool changed = true;
  129. if( aScreen == nullptr )
  130. aScreen = GetScreen();
  131. auto remove_item = [&]( SCH_ITEM* aItem ) -> void
  132. {
  133. changed = true;
  134. aItem->SetFlags( STRUCT_DELETED );
  135. itemList.PushItem( ITEM_PICKER( aScreen, aItem, UNDO_REDO::DELETED ) );
  136. deletedItems.push_back( aItem );
  137. };
  138. BreakSegmentsOnJunctions( aScreen );
  139. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
  140. {
  141. if( !aScreen->IsJunctionNeeded( item->GetPosition() ) )
  142. remove_item( item );
  143. else
  144. junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
  145. }
  146. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
  147. {
  148. ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
  149. }
  150. alg::for_all_pairs( junctions.begin(), junctions.end(),
  151. [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
  152. {
  153. if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
  154. || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
  155. {
  156. return;
  157. }
  158. if( aFirst->GetPosition() == aSecond->GetPosition() )
  159. remove_item( aSecond );
  160. } );
  161. alg::for_all_pairs( ncs.begin(), ncs.end(),
  162. [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
  163. {
  164. if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
  165. || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
  166. {
  167. return;
  168. }
  169. if( aFirst->GetPosition() == aSecond->GetPosition() )
  170. remove_item( aSecond );
  171. } );
  172. while( changed )
  173. {
  174. changed = false;
  175. lines.clear();
  176. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
  177. {
  178. if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
  179. lines.push_back( static_cast<SCH_LINE*>( item ) );
  180. }
  181. for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
  182. {
  183. SCH_LINE* firstLine = *it1;
  184. if( firstLine->GetEditFlags() & STRUCT_DELETED )
  185. continue;
  186. if( firstLine->IsNull() )
  187. {
  188. remove_item( firstLine );
  189. continue;
  190. }
  191. auto it2 = it1;
  192. for( ++it2; it2 != lines.end(); ++it2 )
  193. {
  194. SCH_LINE* secondLine = *it2;
  195. if( secondLine->GetFlags() & STRUCT_DELETED )
  196. continue;
  197. if( !secondLine->IsParallel( firstLine )
  198. || !secondLine->IsStrokeEquivalent( firstLine )
  199. || secondLine->GetLayer() != firstLine->GetLayer() )
  200. {
  201. continue;
  202. }
  203. // Remove identical lines
  204. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  205. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  206. {
  207. remove_item( secondLine );
  208. continue;
  209. }
  210. // See if we can merge an overlap (or two colinear touching segments with
  211. // no junction where they meet).
  212. SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
  213. if( mergedLine != nullptr )
  214. {
  215. remove_item( firstLine );
  216. remove_item( secondLine );
  217. itemList.PushItem( ITEM_PICKER( aScreen, mergedLine, UNDO_REDO::NEWITEM ) );
  218. AddToScreen( mergedLine, aScreen );
  219. if( firstLine->IsSelected() )
  220. selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
  221. break;
  222. }
  223. }
  224. }
  225. }
  226. for( SCH_ITEM* item : deletedItems )
  227. {
  228. if( item->IsSelected() )
  229. selectionTool->RemoveItemFromSel( item, true /*quiet mode*/ );
  230. RemoveFromScreen( item, aScreen );
  231. }
  232. if( itemList.GetCount() )
  233. SaveCopyInUndoList( itemList, UNDO_REDO::DELETED, true );
  234. return itemList.GetCount() > 0;
  235. }
  236. bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint,
  237. SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
  238. {
  239. if( aScreen == nullptr )
  240. aScreen = GetScreen();
  241. SCH_LINE* newSegment = new SCH_LINE( *aSegment );
  242. newSegment->SetStartPoint( aPoint );
  243. AddToScreen( newSegment, aScreen );
  244. SaveCopyInUndoList( aScreen, newSegment, UNDO_REDO::NEWITEM, true );
  245. SaveCopyInUndoList( aScreen, aSegment, UNDO_REDO::CHANGED, true );
  246. UpdateItem( aSegment );
  247. aSegment->SetEndPoint( aPoint );
  248. if( aNewSegment )
  249. *aNewSegment = newSegment;
  250. return true;
  251. }
  252. bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, SCH_SCREEN* aScreen )
  253. {
  254. static const KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  255. if( aScreen == nullptr )
  256. aScreen = GetScreen();
  257. bool brokenSegments = false;
  258. std::vector<SCH_LINE*> wires;
  259. for( SCH_ITEM* item : aScreen->Items().Overlapping( SCH_LINE_T, aPoint ) )
  260. {
  261. if( item->IsType( wiresAndBuses ) )
  262. {
  263. SCH_LINE* wire = static_cast<SCH_LINE*>( item );
  264. if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), aPoint )
  265. && !wire->IsEndPoint( aPoint ) )
  266. {
  267. wires.push_back( wire );
  268. }
  269. }
  270. }
  271. for( SCH_LINE* wire : wires )
  272. brokenSegments |= BreakSegment( wire, aPoint, NULL, aScreen );
  273. return brokenSegments;
  274. }
  275. bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( SCH_SCREEN* aScreen )
  276. {
  277. if( aScreen == nullptr )
  278. aScreen = GetScreen();
  279. bool brokenSegments = false;
  280. std::set<wxPoint> point_set;
  281. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
  282. point_set.insert( item->GetPosition() );
  283. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
  284. {
  285. SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  286. point_set.insert( entry->GetPosition() );
  287. point_set.insert( entry->GetEnd() );
  288. }
  289. for( const wxPoint& pt : point_set )
  290. brokenSegments |= BreakSegments( pt, aScreen );
  291. return brokenSegments;
  292. }
  293. void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
  294. {
  295. SCH_SCREEN* screen = GetScreen();
  296. PICKED_ITEMS_LIST undoList;
  297. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  298. KICAD_T wiresAndBuses[] = { SCH_LINE_LOCATE_WIRE_T, SCH_LINE_LOCATE_BUS_T, EOT };
  299. auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
  300. {
  301. aItem->SetFlags( STRUCT_DELETED );
  302. undoList.PushItem( ITEM_PICKER( screen, aItem, UNDO_REDO::DELETED ) );
  303. };
  304. remove_item( aJunction );
  305. RemoveFromScreen( aJunction, screen );
  306. /// Note that std::list or similar is required here as we may insert values in the
  307. /// loop below. This will invalidate iterators in a std::vector or std::deque
  308. std::list<SCH_LINE*> lines;
  309. for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
  310. {
  311. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  312. if( line->IsType( wiresAndBuses ) && line->IsEndPoint( aJunction->GetPosition() )
  313. && !( line->GetEditFlags() & STRUCT_DELETED ) )
  314. lines.push_back( line );
  315. }
  316. alg::for_all_pairs( lines.begin(), lines.end(),
  317. [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
  318. {
  319. if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
  320. || ( secondLine->GetEditFlags() & STRUCT_DELETED )
  321. || !secondLine->IsParallel( firstLine ) )
  322. {
  323. return;
  324. }
  325. // Remove identical lines
  326. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  327. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  328. {
  329. remove_item( firstLine );
  330. return;
  331. }
  332. // Try to merge the remaining lines
  333. if( SCH_LINE* line = secondLine->MergeOverlap( screen, firstLine, false ) )
  334. {
  335. remove_item( firstLine );
  336. remove_item( secondLine );
  337. undoList.PushItem( ITEM_PICKER( screen, line, UNDO_REDO::NEWITEM ) );
  338. AddToScreen( line, screen );
  339. if( line->IsSelected() )
  340. selectionTool->AddItemToSel( line, true /*quiet mode*/ );
  341. lines.push_back( line );
  342. }
  343. } );
  344. SaveCopyInUndoList( undoList, UNDO_REDO::DELETED, aAppend );
  345. for( SCH_LINE* line : lines )
  346. {
  347. if( line->GetEditFlags() & STRUCT_DELETED )
  348. {
  349. if( line->IsSelected() )
  350. selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
  351. RemoveFromScreen( line, screen );
  352. }
  353. }
  354. }
  355. SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( SCH_SCREEN* aScreen, const wxPoint& aPos,
  356. bool aUndoAppend, bool aFinal )
  357. {
  358. SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
  359. AddToScreen( junction, aScreen );
  360. SaveCopyInUndoList( aScreen, junction, UNDO_REDO::NEWITEM, aUndoAppend );
  361. BreakSegments( aPos );
  362. if( aFinal )
  363. {
  364. m_toolManager->PostEvent( EVENTS::SelectedItemsModified );
  365. TestDanglingEnds();
  366. OnModify();
  367. KIGFX::SCH_VIEW* view = GetCanvas()->GetView();
  368. view->ClearPreview();
  369. view->ShowPreview( false );
  370. view->ClearHiddenFlags();
  371. }
  372. return junction;
  373. }