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.

405 lines
13 KiB

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-2023 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 <general.h>
  26. #include <sch_bus_entry.h>
  27. #include <sch_edit_frame.h>
  28. #include <sch_junction.h>
  29. #include <sch_line.h>
  30. #include <sch_no_connect.h>
  31. #include <sch_screen.h>
  32. #include <sch_view.h>
  33. #include <sch_commit.h>
  34. #include <tool/tool_manager.h>
  35. #include <tools/ee_actions.h>
  36. #include <tools/ee_selection_tool.h>
  37. #include <trigo.h>
  38. void SCH_EDIT_FRAME::TestDanglingEnds()
  39. {
  40. std::function<void( SCH_ITEM* )> changeHandler =
  41. [&]( SCH_ITEM* aChangedItem ) -> void
  42. {
  43. GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT );
  44. };
  45. GetScreen()->TestDanglingEnds( nullptr, &changeHandler );
  46. }
  47. bool SCH_EDIT_FRAME::TrimWire( SCH_COMMIT* aCommit, const VECTOR2I& aStart, const VECTOR2I& aEnd )
  48. {
  49. if( aStart == aEnd )
  50. return false;
  51. SCH_SCREEN* screen = GetScreen();
  52. std::vector<SCH_LINE*> wires;
  53. BOX2I bb( aStart );
  54. bb.Merge( aEnd );
  55. // We cannot modify the RTree while iterating, so push the possible
  56. // wires into a separate structure.
  57. for( EDA_ITEM* item : screen->Items().Overlapping( bb ) )
  58. {
  59. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  60. if( item->Type() == SCH_LINE_T && line->GetLayer() == LAYER_WIRE )
  61. wires.push_back( line );
  62. }
  63. for( SCH_LINE* line : wires )
  64. {
  65. // Don't remove wires that are already deleted or are currently being dragged
  66. if( line->GetEditFlags() & ( STRUCT_DELETED | IS_MOVING | SKIP_STRUCT ) )
  67. continue;
  68. if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
  69. !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
  70. {
  71. continue;
  72. }
  73. // Don't remove entire wires
  74. if( ( line->GetStartPoint() == aStart && line->GetEndPoint() == aEnd )
  75. || ( line->GetStartPoint() == aEnd && line->GetEndPoint() == aStart ) )
  76. {
  77. continue;
  78. }
  79. // Step 1: break the segment on one end.
  80. // Ensure that *line points to the segment containing aEnd
  81. SCH_LINE* new_line;
  82. BreakSegment( aCommit, line, aStart, &new_line, screen );
  83. if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aEnd ) )
  84. line = new_line;
  85. // Step 2: break the remaining segment.
  86. // Ensure that *line _also_ contains aStart. This is our overlapping segment
  87. BreakSegment( aCommit, line, aEnd, &new_line, screen );
  88. if( IsPointOnSegment( new_line->GetStartPoint(), new_line->GetEndPoint(), aStart ) )
  89. line = new_line;
  90. RemoveFromScreen( line, screen );
  91. aCommit->Removed( line, screen );
  92. return true;
  93. }
  94. return false;
  95. }
  96. void SCH_EDIT_FRAME::SchematicCleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
  97. {
  98. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  99. std::vector<SCH_LINE*> lines;
  100. std::vector<SCH_JUNCTION*> junctions;
  101. std::vector<SCH_NO_CONNECT*> ncs;
  102. std::vector<SCH_ITEM*> items_to_remove;
  103. bool changed = true;
  104. if( aScreen == nullptr )
  105. aScreen = GetScreen();
  106. auto remove_item = [&]( SCH_ITEM* aItem ) -> void
  107. {
  108. changed = true;
  109. if( !( aItem->GetFlags() & STRUCT_DELETED ) )
  110. {
  111. aItem->SetFlags( STRUCT_DELETED );
  112. if( aItem->IsSelected() )
  113. selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
  114. RemoveFromScreen( aItem, aScreen );
  115. aCommit->Removed( aItem, aScreen );
  116. }
  117. };
  118. BreakSegmentsOnJunctions( aCommit, aScreen );
  119. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
  120. {
  121. if( !aScreen->IsExplicitJunction( item->GetPosition() ) )
  122. items_to_remove.push_back( item );
  123. else
  124. junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
  125. }
  126. for( SCH_ITEM* item : items_to_remove )
  127. remove_item( item );
  128. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
  129. ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
  130. alg::for_all_pairs( junctions.begin(), junctions.end(),
  131. [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
  132. {
  133. if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
  134. || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
  135. {
  136. return;
  137. }
  138. if( aFirst->GetPosition() == aSecond->GetPosition() )
  139. remove_item( aSecond );
  140. } );
  141. alg::for_all_pairs( ncs.begin(), ncs.end(),
  142. [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
  143. {
  144. if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
  145. || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
  146. {
  147. return;
  148. }
  149. if( aFirst->GetPosition() == aSecond->GetPosition() )
  150. remove_item( aSecond );
  151. } );
  152. while( changed )
  153. {
  154. changed = false;
  155. lines.clear();
  156. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
  157. {
  158. if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
  159. lines.push_back( static_cast<SCH_LINE*>( item ) );
  160. }
  161. for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
  162. {
  163. SCH_LINE* firstLine = *it1;
  164. if( firstLine->GetEditFlags() & STRUCT_DELETED )
  165. continue;
  166. if( firstLine->IsNull() )
  167. {
  168. remove_item( firstLine );
  169. continue;
  170. }
  171. auto it2 = it1;
  172. for( ++it2; it2 != lines.end(); ++it2 )
  173. {
  174. SCH_LINE* secondLine = *it2;
  175. if( secondLine->GetFlags() & STRUCT_DELETED )
  176. continue;
  177. if( !secondLine->IsParallel( firstLine )
  178. || !secondLine->IsStrokeEquivalent( firstLine )
  179. || secondLine->GetLayer() != firstLine->GetLayer() )
  180. {
  181. continue;
  182. }
  183. // Remove identical lines
  184. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  185. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  186. {
  187. remove_item( secondLine );
  188. continue;
  189. }
  190. // See if we can merge an overlap (or two colinear touching segments with
  191. // no junction where they meet).
  192. SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
  193. if( mergedLine != nullptr )
  194. {
  195. remove_item( firstLine );
  196. remove_item( secondLine );
  197. AddToScreen( mergedLine, aScreen );
  198. aCommit->Added( mergedLine, aScreen );
  199. if( firstLine->IsSelected() || secondLine->IsSelected() )
  200. selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
  201. break;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. void SCH_EDIT_FRAME::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
  208. SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
  209. {
  210. // Save the copy of aSegment before breaking it
  211. aCommit->Modify( aSegment, aScreen );
  212. SCH_LINE* newSegment = aSegment->BreakAt( aPoint );
  213. aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
  214. newSegment->SetFlags( IS_NEW | IS_BROKEN );
  215. AddToScreen( newSegment, aScreen );
  216. aCommit->Added( newSegment, aScreen );
  217. *aNewSegment = newSegment;
  218. }
  219. bool SCH_EDIT_FRAME::BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPos, SCH_SCREEN* aScreen )
  220. {
  221. bool brokenSegments = false;
  222. SCH_LINE* new_line;
  223. for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
  224. {
  225. BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
  226. brokenSegments = true;
  227. }
  228. return brokenSegments;
  229. }
  230. bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
  231. {
  232. bool brokenSegments = false;
  233. std::set<VECTOR2I> point_set;
  234. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
  235. point_set.insert( item->GetPosition() );
  236. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
  237. {
  238. SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  239. point_set.insert( entry->GetPosition() );
  240. point_set.insert( entry->GetEnd() );
  241. }
  242. for( const VECTOR2I& pt : point_set )
  243. {
  244. BreakSegments( aCommit, pt, aScreen );
  245. brokenSegments = true;
  246. }
  247. return brokenSegments;
  248. }
  249. void SCH_EDIT_FRAME::DeleteJunction( SCH_COMMIT* aCommit, SCH_ITEM* aJunction )
  250. {
  251. SCH_SCREEN* screen = GetScreen();
  252. PICKED_ITEMS_LIST undoList;
  253. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  254. aJunction->SetFlags( STRUCT_DELETED );
  255. RemoveFromScreen( aJunction, screen );
  256. aCommit->Removed( aJunction, screen );
  257. /// Note that std::list or similar is required here as we may insert values in the
  258. /// loop below. This will invalidate iterators in a std::vector or std::deque
  259. std::list<SCH_LINE*> lines;
  260. for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
  261. {
  262. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  263. if( line->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } )
  264. && line->IsEndPoint( aJunction->GetPosition() )
  265. && !( line->GetEditFlags() & STRUCT_DELETED ) )
  266. {
  267. lines.push_back( line );
  268. }
  269. }
  270. alg::for_all_pairs( lines.begin(), lines.end(),
  271. [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
  272. {
  273. if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
  274. || ( secondLine->GetEditFlags() & STRUCT_DELETED )
  275. || !secondLine->IsParallel( firstLine ) )
  276. {
  277. return;
  278. }
  279. // Remove identical lines
  280. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  281. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  282. {
  283. firstLine->SetFlags( STRUCT_DELETED );
  284. return;
  285. }
  286. // Try to merge the remaining lines
  287. if( SCH_LINE* new_line = secondLine->MergeOverlap( screen, firstLine, false ) )
  288. {
  289. firstLine->SetFlags( STRUCT_DELETED );
  290. secondLine->SetFlags( STRUCT_DELETED );
  291. AddToScreen( new_line, screen );
  292. aCommit->Added( new_line, screen );
  293. if( new_line->IsSelected() )
  294. selectionTool->AddItemToSel( new_line, true /*quiet mode*/ );
  295. lines.push_back( new_line );
  296. }
  297. } );
  298. for( SCH_LINE* line : lines )
  299. {
  300. if( line->GetEditFlags() & STRUCT_DELETED )
  301. {
  302. if( line->IsSelected() )
  303. selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
  304. RemoveFromScreen( line, screen );
  305. aCommit->Removed( line, screen );
  306. }
  307. }
  308. }
  309. SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen,
  310. const VECTOR2I& aPos )
  311. {
  312. SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
  313. AddToScreen( junction, aScreen );
  314. aCommit->Added( junction, aScreen );
  315. BreakSegments( aCommit, aPos, aScreen );
  316. return junction;
  317. }