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.

447 lines
15 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 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 <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. auto minX = []( const SCH_LINE* l )
  153. {
  154. return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
  155. };
  156. auto maxX = []( const SCH_LINE* l )
  157. {
  158. return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
  159. };
  160. auto minY = []( const SCH_LINE* l )
  161. {
  162. return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
  163. };
  164. auto maxY = []( const SCH_LINE* l )
  165. {
  166. return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
  167. };
  168. // Would be nice to put lines in a canonical form here by swapping
  169. // start <-> end as needed but I don't know what swapping breaks.
  170. while( changed )
  171. {
  172. changed = false;
  173. lines.clear();
  174. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
  175. {
  176. if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
  177. lines.push_back( static_cast<SCH_LINE*>( item ) );
  178. }
  179. // Sort by minimum X position
  180. std::sort( lines.begin(), lines.end(),
  181. [&]( const SCH_LINE* a, const SCH_LINE* b )
  182. {
  183. return minX( a ) < minX( b );
  184. } );
  185. for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
  186. {
  187. SCH_LINE* firstLine = *it1;
  188. if( firstLine->GetEditFlags() & STRUCT_DELETED )
  189. continue;
  190. if( firstLine->IsNull() )
  191. {
  192. remove_item( firstLine );
  193. continue;
  194. }
  195. int firstRightXEdge = maxX( firstLine );
  196. auto it2 = it1;
  197. for( ++it2; it2 != lines.end(); ++it2 )
  198. {
  199. SCH_LINE* secondLine = *it2;
  200. int secondLeftXEdge = minX( secondLine );
  201. // impossible to overlap remaining lines
  202. if( secondLeftXEdge > firstRightXEdge )
  203. break;
  204. // No Y axis overlap
  205. if( !( std::max( minY( firstLine ), minY( secondLine ) )
  206. <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
  207. {
  208. continue;
  209. }
  210. if( secondLine->GetFlags() & STRUCT_DELETED )
  211. continue;
  212. if( !secondLine->IsParallel( firstLine )
  213. || !secondLine->IsStrokeEquivalent( firstLine )
  214. || secondLine->GetLayer() != firstLine->GetLayer() )
  215. {
  216. continue;
  217. }
  218. // Remove identical lines
  219. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  220. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  221. {
  222. remove_item( secondLine );
  223. continue;
  224. }
  225. // See if we can merge an overlap (or two colinear touching segments with
  226. // no junction where they meet).
  227. SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
  228. if( mergedLine != nullptr )
  229. {
  230. remove_item( firstLine );
  231. remove_item( secondLine );
  232. AddToScreen( mergedLine, aScreen );
  233. aCommit->Added( mergedLine, aScreen );
  234. if( firstLine->IsSelected() || secondLine->IsSelected() )
  235. selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
  236. break;
  237. }
  238. }
  239. }
  240. }
  241. }
  242. void SCH_EDIT_FRAME::BreakSegment( SCH_COMMIT* aCommit, SCH_LINE* aSegment, const VECTOR2I& aPoint,
  243. SCH_LINE** aNewSegment, SCH_SCREEN* aScreen )
  244. {
  245. // Save the copy of aSegment before breaking it
  246. aCommit->Modify( aSegment, aScreen );
  247. SCH_LINE* newSegment = aSegment->BreakAt( aPoint );
  248. aSegment->SetFlags( IS_CHANGED | IS_BROKEN );
  249. newSegment->SetFlags( IS_NEW | IS_BROKEN );
  250. AddToScreen( newSegment, aScreen );
  251. aCommit->Added( newSegment, aScreen );
  252. *aNewSegment = newSegment;
  253. }
  254. bool SCH_EDIT_FRAME::BreakSegments( SCH_COMMIT* aCommit, const VECTOR2I& aPos, SCH_SCREEN* aScreen )
  255. {
  256. bool brokenSegments = false;
  257. SCH_LINE* new_line;
  258. for( SCH_LINE* wire : aScreen->GetBusesAndWires( aPos, true ) )
  259. {
  260. BreakSegment( aCommit, wire, aPos, &new_line, aScreen );
  261. brokenSegments = true;
  262. }
  263. return brokenSegments;
  264. }
  265. bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
  266. {
  267. bool brokenSegments = false;
  268. std::set<VECTOR2I> point_set;
  269. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
  270. point_set.insert( item->GetPosition() );
  271. for( SCH_ITEM* item : aScreen->Items().OfType( SCH_BUS_WIRE_ENTRY_T ) )
  272. {
  273. SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  274. point_set.insert( entry->GetPosition() );
  275. point_set.insert( entry->GetEnd() );
  276. }
  277. for( const VECTOR2I& pt : point_set )
  278. {
  279. BreakSegments( aCommit, pt, aScreen );
  280. brokenSegments = true;
  281. }
  282. return brokenSegments;
  283. }
  284. void SCH_EDIT_FRAME::DeleteJunction( SCH_COMMIT* aCommit, SCH_ITEM* aJunction )
  285. {
  286. SCH_SCREEN* screen = GetScreen();
  287. PICKED_ITEMS_LIST undoList;
  288. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  289. aJunction->SetFlags( STRUCT_DELETED );
  290. RemoveFromScreen( aJunction, screen );
  291. aCommit->Removed( aJunction, screen );
  292. /// Note that std::list or similar is required here as we may insert values in the
  293. /// loop below. This will invalidate iterators in a std::vector or std::deque
  294. std::list<SCH_LINE*> lines;
  295. for( SCH_ITEM* item : screen->Items().Overlapping( SCH_LINE_T, aJunction->GetPosition() ) )
  296. {
  297. SCH_LINE* line = static_cast<SCH_LINE*>( item );
  298. if( ( line->IsWire() || line->IsBus() )
  299. && line->IsEndPoint( aJunction->GetPosition() )
  300. && !( line->GetEditFlags() & STRUCT_DELETED ) )
  301. {
  302. lines.push_back( line );
  303. }
  304. }
  305. alg::for_all_pairs( lines.begin(), lines.end(),
  306. [&]( SCH_LINE* firstLine, SCH_LINE* secondLine )
  307. {
  308. if( ( firstLine->GetEditFlags() & STRUCT_DELETED )
  309. || ( secondLine->GetEditFlags() & STRUCT_DELETED )
  310. || !secondLine->IsParallel( firstLine ) )
  311. {
  312. return;
  313. }
  314. // Remove identical lines
  315. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  316. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  317. {
  318. firstLine->SetFlags( STRUCT_DELETED );
  319. return;
  320. }
  321. // Try to merge the remaining lines
  322. if( SCH_LINE* new_line = secondLine->MergeOverlap( screen, firstLine, false ) )
  323. {
  324. firstLine->SetFlags( STRUCT_DELETED );
  325. secondLine->SetFlags( STRUCT_DELETED );
  326. AddToScreen( new_line, screen );
  327. aCommit->Added( new_line, screen );
  328. if( new_line->IsSelected() )
  329. selectionTool->AddItemToSel( new_line, true /*quiet mode*/ );
  330. lines.push_back( new_line );
  331. }
  332. } );
  333. for( SCH_LINE* line : lines )
  334. {
  335. if( line->GetEditFlags() & STRUCT_DELETED )
  336. {
  337. if( line->IsSelected() )
  338. selectionTool->RemoveItemFromSel( line, true /*quiet mode*/ );
  339. RemoveFromScreen( line, screen );
  340. aCommit->Removed( line, screen );
  341. }
  342. }
  343. }
  344. SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen,
  345. const VECTOR2I& aPos )
  346. {
  347. SCH_JUNCTION* junction = new SCH_JUNCTION( aPos );
  348. AddToScreen( junction, aScreen );
  349. aCommit->Added( junction, aScreen );
  350. BreakSegments( aCommit, aPos, aScreen );
  351. return junction;
  352. }