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.

432 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004 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. #include <fctsys.h>
  25. #include <gr_basic.h>
  26. #include <macros.h>
  27. #include <sch_draw_panel.h>
  28. #include <trigo.h>
  29. #include <common.h>
  30. #include <richio.h>
  31. #include <plotter.h>
  32. #include <bitmaps.h>
  33. #include <eeschema_config.h>
  34. #include <general.h>
  35. #include <sch_bus_entry.h>
  36. #include <sch_line.h>
  37. #include <sch_text.h>
  38. SCH_BUS_ENTRY_BASE::SCH_BUS_ENTRY_BASE( KICAD_T aType, const wxPoint& pos, char shape ) :
  39. SCH_ITEM( NULL, aType )
  40. {
  41. m_pos = pos;
  42. m_size.x = Mils2iu( 100 );
  43. m_size.y = Mils2iu( 100 );
  44. if( shape == '/' )
  45. m_size.y *= -1;
  46. m_isDanglingStart = m_isDanglingEnd = true;
  47. }
  48. SCH_BUS_WIRE_ENTRY::SCH_BUS_WIRE_ENTRY( const wxPoint& pos, char shape ) :
  49. SCH_BUS_ENTRY_BASE( SCH_BUS_WIRE_ENTRY_T, pos, shape )
  50. {
  51. m_Layer = LAYER_WIRE;
  52. m_connected_bus_item = nullptr;
  53. }
  54. SCH_BUS_BUS_ENTRY::SCH_BUS_BUS_ENTRY( const wxPoint& pos, char shape ) :
  55. SCH_BUS_ENTRY_BASE( SCH_BUS_BUS_ENTRY_T, pos, shape )
  56. {
  57. m_Layer = LAYER_BUS;
  58. m_connected_bus_items[0] = nullptr;
  59. m_connected_bus_items[1] = nullptr;
  60. }
  61. EDA_ITEM* SCH_BUS_WIRE_ENTRY::Clone() const
  62. {
  63. return new SCH_BUS_WIRE_ENTRY( *this );
  64. }
  65. EDA_ITEM* SCH_BUS_BUS_ENTRY::Clone() const
  66. {
  67. return new SCH_BUS_BUS_ENTRY( *this );
  68. }
  69. bool SCH_BUS_ENTRY_BASE::doIsConnected( const wxPoint& aPosition ) const
  70. {
  71. return ( m_pos == aPosition || m_End() == aPosition );
  72. }
  73. wxPoint SCH_BUS_ENTRY_BASE::m_End() const
  74. {
  75. return wxPoint( m_pos.x + m_size.x, m_pos.y + m_size.y );
  76. }
  77. void SCH_BUS_ENTRY_BASE::SwapData( SCH_ITEM* aItem )
  78. {
  79. SCH_BUS_ENTRY_BASE* item = dynamic_cast<SCH_BUS_ENTRY_BASE*>( aItem );
  80. wxCHECK_RET( item, wxT( "Cannot swap bus entry data with invalid item." ) );
  81. std::swap( m_pos, item->m_pos );
  82. std::swap( m_size, item->m_size );
  83. }
  84. void SCH_BUS_ENTRY_BASE::ViewGetLayers( int aLayers[], int& aCount ) const
  85. {
  86. aCount = 2;
  87. aLayers[0] = Type() == SCH_BUS_BUS_ENTRY_T ? LAYER_BUS : LAYER_WIRE;
  88. aLayers[1] = LAYER_SELECTION_SHADOWS;
  89. }
  90. const EDA_RECT SCH_BUS_ENTRY_BASE::GetBoundingBox() const
  91. {
  92. EDA_RECT box;
  93. box.SetOrigin( m_pos );
  94. box.SetEnd( m_End() );
  95. box.Normalize();
  96. box.Inflate( GetPenSize() / 2 );
  97. return box;
  98. }
  99. int SCH_BUS_WIRE_ENTRY::GetPenSize() const
  100. {
  101. return GetDefaultWireThickness();
  102. }
  103. int SCH_BUS_BUS_ENTRY::GetPenSize() const
  104. {
  105. return GetDefaultBusThickness();
  106. }
  107. void SCH_BUS_WIRE_ENTRY::GetEndPoints( std::vector< DANGLING_END_ITEM >& aItemList )
  108. {
  109. DANGLING_END_ITEM item( WIRE_ENTRY_END, this, m_pos );
  110. aItemList.push_back( item );
  111. DANGLING_END_ITEM item1( WIRE_ENTRY_END, this, m_End() );
  112. aItemList.push_back( item1 );
  113. }
  114. void SCH_BUS_BUS_ENTRY::GetEndPoints( std::vector< DANGLING_END_ITEM >& aItemList )
  115. {
  116. DANGLING_END_ITEM item( BUS_ENTRY_END, this, m_pos );
  117. aItemList.push_back( item );
  118. DANGLING_END_ITEM item1( BUS_ENTRY_END, this, m_End() );
  119. aItemList.push_back( item1 );
  120. }
  121. void SCH_BUS_ENTRY_BASE::Print( wxDC* aDC, const wxPoint& aOffset )
  122. {
  123. COLOR4D color = GetLayerColor( m_Layer );
  124. GRLine( nullptr, aDC, m_pos.x + aOffset.x, m_pos.y + aOffset.y, m_End().x + aOffset.x,
  125. m_End().y + aOffset.y, GetPenSize(), color );
  126. }
  127. void SCH_BUS_ENTRY_BASE::MirrorX( int aXaxis_position )
  128. {
  129. MIRROR( m_pos.y, aXaxis_position );
  130. m_size.y = -m_size.y;
  131. }
  132. void SCH_BUS_ENTRY_BASE::MirrorY( int aYaxis_position )
  133. {
  134. MIRROR( m_pos.x, aYaxis_position );
  135. m_size.x = -m_size.x;
  136. }
  137. void SCH_BUS_ENTRY_BASE::Rotate( wxPoint aPosition )
  138. {
  139. RotatePoint( &m_pos, aPosition, 900 );
  140. RotatePoint( &m_size.x, &m_size.y, 900 );
  141. }
  142. bool SCH_BUS_WIRE_ENTRY::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  143. const SCH_SHEET_PATH* aPath )
  144. {
  145. bool previousStateStart = m_isDanglingStart;
  146. bool previousStateEnd = m_isDanglingEnd;
  147. m_isDanglingStart = m_isDanglingEnd = true;
  148. // Wires and buses are stored in the list as a pair, start and end. This
  149. // variable holds the start position from one iteration so it can be used
  150. // when the end position is found.
  151. wxPoint seg_start;
  152. // Store the connection type and state for the start (0) and end (1)
  153. bool has_wire[2] = { false };
  154. bool has_bus[2] = { false };
  155. for( DANGLING_END_ITEM& each_item : aItemList )
  156. {
  157. if( each_item.GetItem() == this )
  158. continue;
  159. switch( each_item.GetType() )
  160. {
  161. case WIRE_START_END:
  162. case WIRE_END_END:
  163. if( m_pos == each_item.GetPosition() )
  164. has_wire[0] = true;
  165. else if( m_End() == each_item.GetPosition() )
  166. has_wire[1] = true;
  167. break;
  168. case BUS_START_END:
  169. seg_start = each_item.GetPosition();
  170. break;
  171. case BUS_END_END:
  172. if( IsPointOnSegment( seg_start, each_item.GetPosition(), m_pos ) )
  173. has_bus[0] = true;
  174. else if( IsPointOnSegment( seg_start, each_item.GetPosition(), m_End() ) )
  175. has_bus[1] = true;
  176. break;
  177. default:
  178. break;
  179. }
  180. }
  181. // A bus-wire entry is connected at both ends if it has a bus and a wire on its
  182. // ends. Otherwise, we connect only one end (in the case of a wire-wire or bus-bus)
  183. if( ( has_wire[0] && has_bus[1] ) || ( has_wire[1] && has_bus[0] ) )
  184. m_isDanglingEnd = m_isDanglingStart = false;
  185. else if( has_wire[0] || has_bus[0] )
  186. m_isDanglingStart = false;
  187. else if( has_wire[1] || has_bus[1] )
  188. m_isDanglingEnd = false;
  189. return (previousStateStart != m_isDanglingStart) || (previousStateEnd != m_isDanglingEnd);
  190. }
  191. bool SCH_BUS_BUS_ENTRY::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  192. const SCH_SHEET_PATH* aPath )
  193. {
  194. bool previousStateStart = m_isDanglingStart;
  195. bool previousStateEnd = m_isDanglingEnd;
  196. m_isDanglingStart = m_isDanglingEnd = true;
  197. // Wires and buses are stored in the list as a pair, start and end. This
  198. // variable holds the start position from one iteration so it can be used
  199. // when the end position is found.
  200. wxPoint seg_start;
  201. for( DANGLING_END_ITEM& each_item : aItemList )
  202. {
  203. if( each_item.GetItem() == this )
  204. continue;
  205. switch( each_item.GetType() )
  206. {
  207. case BUS_START_END:
  208. seg_start = each_item.GetPosition();
  209. break;
  210. case BUS_END_END:
  211. if( IsPointOnSegment( seg_start, each_item.GetPosition(), m_pos ) )
  212. m_isDanglingStart = false;
  213. if( IsPointOnSegment( seg_start, each_item.GetPosition(), m_End() ) )
  214. m_isDanglingEnd = false;
  215. break;
  216. default:
  217. break;
  218. }
  219. }
  220. return (previousStateStart != m_isDanglingStart) || (previousStateEnd != m_isDanglingEnd);
  221. }
  222. bool SCH_BUS_ENTRY_BASE::IsDangling() const
  223. {
  224. return m_isDanglingStart || m_isDanglingEnd;
  225. }
  226. void SCH_BUS_ENTRY_BASE::GetConnectionPoints( std::vector< wxPoint >& aPoints ) const
  227. {
  228. aPoints.push_back( m_pos );
  229. aPoints.push_back( m_End() );
  230. }
  231. wxString SCH_BUS_WIRE_ENTRY::GetSelectMenuText( EDA_UNITS aUnits ) const
  232. {
  233. return wxString( _( "Bus to Wire Entry" ) );
  234. }
  235. wxString SCH_BUS_BUS_ENTRY::GetSelectMenuText( EDA_UNITS aUnits ) const
  236. {
  237. return wxString( _( "Bus to Bus Entry" ) );
  238. }
  239. BITMAP_DEF SCH_BUS_WIRE_ENTRY::GetMenuImage() const
  240. {
  241. return add_line2bus_xpm;
  242. }
  243. BITMAP_DEF SCH_BUS_BUS_ENTRY::GetMenuImage() const
  244. {
  245. return add_bus2bus_xpm;
  246. }
  247. bool SCH_BUS_ENTRY_BASE::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  248. {
  249. // Insure minimum accuracy
  250. if( aAccuracy == 0 )
  251. aAccuracy = ( GetPenSize() / 2 ) + 4;
  252. return TestSegmentHit( aPosition, m_pos, m_End(), aAccuracy );
  253. }
  254. bool SCH_BUS_ENTRY_BASE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  255. {
  256. EDA_RECT rect = aRect;
  257. rect.Inflate( aAccuracy );
  258. if( aContained )
  259. return rect.Contains( GetBoundingBox() );
  260. return rect.Intersects( GetBoundingBox() );
  261. }
  262. void SCH_BUS_ENTRY_BASE::Plot( PLOTTER* aPlotter )
  263. {
  264. aPlotter->SetCurrentLineWidth( GetPenSize() );
  265. aPlotter->SetColor( GetLayerColor( GetLayer() ) );
  266. aPlotter->MoveTo( m_pos );
  267. aPlotter->FinishTo( m_End() );
  268. }
  269. void SCH_BUS_ENTRY_BASE::SetBusEntryShape( char aShape )
  270. {
  271. switch( aShape )
  272. {
  273. case '\\':
  274. if( m_size.y < 0 )
  275. m_size.y = -m_size.y;
  276. break;
  277. case '/':
  278. if( m_size.y > 0 )
  279. m_size.y = -m_size.y;
  280. break;
  281. }
  282. }
  283. char SCH_BUS_ENTRY_BASE::GetBusEntryShape() const
  284. {
  285. if( GetSize().y < 0 )
  286. return '/';
  287. else
  288. return '\\';
  289. }
  290. void SCH_BUS_ENTRY_BASE::GetMsgPanelInfo( EDA_UNITS aUnits, MSG_PANEL_ITEMS& aList )
  291. {
  292. wxString msg;
  293. switch( GetLayer() )
  294. {
  295. default:
  296. case LAYER_WIRE: msg = _( "Wire" ); break;
  297. case LAYER_BUS: msg = _( "Bus" ); break;
  298. }
  299. aList.push_back( MSG_PANEL_ITEM( _( "Bus Entry Type" ), msg, DARKCYAN ) );
  300. if( auto conn = Connection( *g_CurrentSheet ) )
  301. {
  302. #if defined(DEBUG)
  303. conn->AppendDebugInfoToMsgPanel( aList );
  304. #else
  305. conn->AppendInfoToMsgPanel( aList );
  306. #endif
  307. }
  308. }
  309. bool SCH_BUS_WIRE_ENTRY::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
  310. {
  311. // Don't generate connections between bus entries and buses, since there is
  312. // a connectivity change at that point (e.g. A[7..0] to A7)
  313. if( ( aItem->Type() == SCH_LINE_T ) &&
  314. ( static_cast<const SCH_LINE*>( aItem )->GetLayer() == LAYER_BUS ) )
  315. {
  316. return false;
  317. }
  318. // Don't generate connections between bus entries and bus labels that happen
  319. // to land at the same point on the bus wire as this bus entry
  320. if( ( aItem->Type() == SCH_LABEL_T ) &&
  321. SCH_CONNECTION::IsBusLabel( static_cast<const SCH_LABEL*>( aItem )->GetText() ) )
  322. {
  323. return false;
  324. }
  325. // Don't generate connections between two bus-wire entries
  326. if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
  327. return false;
  328. return true;
  329. }