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.

570 lines
16 KiB

4 years ago
4 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, jp.charras at wanadoo.fr
  5. * Copyright (C) 2004-2022 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_draw_panel.h>
  25. #include <bitmaps.h>
  26. #include <core/mirror.h>
  27. #include <schematic.h>
  28. #include <geometry/shape_segment.h>
  29. #include <sch_bus_entry.h>
  30. #include <sch_edit_frame.h>
  31. #include <sch_junction.h>
  32. #include <sch_line.h>
  33. #include <sch_label.h>
  34. #include <project/net_settings.h>
  35. #include <project/project_file.h>
  36. #include <settings/color_settings.h>
  37. #include <netclass.h>
  38. #include <trigo.h>
  39. #include <board_item.h>
  40. #include <advanced_config.h>
  41. #include <connection_graph.h>
  42. #include "sch_painter.h"
  43. #include "plotters/plotter.h"
  44. SCH_BUS_ENTRY_BASE::SCH_BUS_ENTRY_BASE( KICAD_T aType, const VECTOR2I& pos, bool aFlipY ) :
  45. SCH_ITEM( nullptr, aType )
  46. {
  47. m_pos = pos;
  48. m_size.x = schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE );
  49. m_size.y = schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE );
  50. m_stroke.SetWidth( 0 );
  51. m_stroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT );
  52. m_stroke.SetColor( COLOR4D::UNSPECIFIED );
  53. if( aFlipY )
  54. m_size.y *= -1;
  55. m_isDanglingStart = m_isDanglingEnd = true;
  56. m_lastResolvedWidth = schIUScale.MilsToIU( DEFAULT_WIRE_WIDTH_MILS );
  57. m_lastResolvedLineStyle = PLOT_DASH_TYPE::SOLID;
  58. m_lastResolvedColor = COLOR4D::UNSPECIFIED;
  59. }
  60. SCH_BUS_WIRE_ENTRY::SCH_BUS_WIRE_ENTRY( const VECTOR2I& pos, bool aFlipY ) :
  61. SCH_BUS_ENTRY_BASE( SCH_BUS_WIRE_ENTRY_T, pos, aFlipY )
  62. {
  63. m_layer = LAYER_WIRE;
  64. m_connected_bus_item = nullptr;
  65. m_lastResolvedWidth = schIUScale.MilsToIU( DEFAULT_WIRE_WIDTH_MILS );
  66. m_lastResolvedLineStyle = PLOT_DASH_TYPE::SOLID;
  67. m_lastResolvedColor = COLOR4D::UNSPECIFIED;
  68. }
  69. SCH_BUS_WIRE_ENTRY::SCH_BUS_WIRE_ENTRY( const VECTOR2I& pos, int aQuadrant ) :
  70. SCH_BUS_ENTRY_BASE( SCH_BUS_WIRE_ENTRY_T, pos, false )
  71. {
  72. switch( aQuadrant )
  73. {
  74. case 1: m_size.x *= 1; m_size.y *= -1; break;
  75. case 2: m_size.x *= 1; m_size.y *= 1; break;
  76. case 3: m_size.x *= -1; m_size.y *= 1; break;
  77. case 4: m_size.x *= -1; m_size.y *= -1; break;
  78. default: wxFAIL_MSG( "SCH_BUS_WIRE_ENTRY ctor: unexpected quadrant" );
  79. }
  80. m_layer = LAYER_WIRE;
  81. m_connected_bus_item = nullptr;
  82. m_lastResolvedWidth = schIUScale.MilsToIU( DEFAULT_WIRE_WIDTH_MILS );
  83. m_lastResolvedLineStyle = PLOT_DASH_TYPE::SOLID;
  84. m_lastResolvedColor = COLOR4D::UNSPECIFIED;
  85. }
  86. SCH_BUS_BUS_ENTRY::SCH_BUS_BUS_ENTRY( const VECTOR2I& pos, bool aFlipY ) :
  87. SCH_BUS_ENTRY_BASE( SCH_BUS_BUS_ENTRY_T, pos, aFlipY )
  88. {
  89. m_layer = LAYER_BUS;
  90. m_connected_bus_items[0] = nullptr;
  91. m_connected_bus_items[1] = nullptr;
  92. m_lastResolvedWidth = schIUScale.MilsToIU( DEFAULT_WIRE_WIDTH_MILS );
  93. m_lastResolvedLineStyle = PLOT_DASH_TYPE::SOLID;
  94. m_lastResolvedColor = COLOR4D::UNSPECIFIED;
  95. }
  96. EDA_ITEM* SCH_BUS_WIRE_ENTRY::Clone() const
  97. {
  98. return new SCH_BUS_WIRE_ENTRY( *this );
  99. }
  100. EDA_ITEM* SCH_BUS_BUS_ENTRY::Clone() const
  101. {
  102. return new SCH_BUS_BUS_ENTRY( *this );
  103. }
  104. bool SCH_BUS_ENTRY_BASE::doIsConnected( const VECTOR2I& aPosition ) const
  105. {
  106. return ( m_pos == aPosition || GetEnd() == aPosition );
  107. }
  108. VECTOR2I SCH_BUS_ENTRY_BASE::GetEnd() const
  109. {
  110. return VECTOR2I( m_pos.x + m_size.x, m_pos.y + m_size.y );
  111. }
  112. void SCH_BUS_ENTRY_BASE::SwapData( SCH_ITEM* aItem )
  113. {
  114. SCH_BUS_ENTRY_BASE* item = dynamic_cast<SCH_BUS_ENTRY_BASE*>( aItem );
  115. wxCHECK_RET( item, wxT( "Cannot swap bus entry data with invalid item." ) );
  116. std::swap( m_pos, item->m_pos );
  117. std::swap( m_size, item->m_size );
  118. std::swap( m_stroke, item->m_stroke );
  119. std::swap( m_lastResolvedWidth, item->m_lastResolvedWidth );
  120. std::swap( m_lastResolvedLineStyle, item->m_lastResolvedLineStyle );
  121. std::swap( m_lastResolvedColor, item->m_lastResolvedColor );
  122. }
  123. void SCH_BUS_ENTRY_BASE::ViewGetLayers( int aLayers[], int& aCount ) const
  124. {
  125. aCount = 3;
  126. aLayers[0] = LAYER_DANGLING;
  127. aLayers[1] = Type() == SCH_BUS_BUS_ENTRY_T ? LAYER_BUS : LAYER_WIRE;
  128. aLayers[2] = LAYER_SELECTION_SHADOWS;
  129. }
  130. const BOX2I SCH_BUS_ENTRY_BASE::GetBoundingBox() const
  131. {
  132. BOX2I bbox( m_pos );
  133. bbox.SetEnd( GetEnd() );
  134. bbox.Normalize();
  135. bbox.Inflate( ( GetPenWidth() / 2 ) + 1 );
  136. return bbox;
  137. }
  138. COLOR4D SCH_BUS_ENTRY_BASE::GetBusEntryColor() const
  139. {
  140. if( m_stroke.GetColor() != COLOR4D::UNSPECIFIED )
  141. m_lastResolvedColor = m_stroke.GetColor();
  142. else if( IsConnectable() && !IsConnectivityDirty() )
  143. m_lastResolvedColor = GetEffectiveNetClass()->GetSchematicColor();
  144. return m_lastResolvedColor;
  145. }
  146. void SCH_BUS_ENTRY_BASE::SetPenWidth( int aWidth )
  147. {
  148. m_stroke.SetWidth( aWidth );
  149. m_lastResolvedWidth = aWidth;
  150. }
  151. void SCH_BUS_ENTRY_BASE::SetBusEntryColor( const COLOR4D& aColor )
  152. {
  153. m_stroke.SetColor( aColor );
  154. m_lastResolvedColor = aColor;
  155. }
  156. PLOT_DASH_TYPE SCH_BUS_ENTRY_BASE::GetLineStyle() const
  157. {
  158. if( m_stroke.GetPlotStyle() != PLOT_DASH_TYPE::DEFAULT )
  159. m_lastResolvedLineStyle = m_stroke.GetPlotStyle();
  160. else if( IsConnectable() && !IsConnectivityDirty() )
  161. m_lastResolvedLineStyle = (PLOT_DASH_TYPE) GetEffectiveNetClass()->GetLineStyle();
  162. return m_lastResolvedLineStyle;
  163. }
  164. void SCH_BUS_ENTRY_BASE::SetLineStyle( PLOT_DASH_TYPE aStyle )
  165. {
  166. m_stroke.SetPlotStyle( aStyle );
  167. m_lastResolvedLineStyle = aStyle;
  168. }
  169. int SCH_BUS_WIRE_ENTRY::GetPenWidth() const
  170. {
  171. if( m_stroke.GetWidth() > 0 )
  172. m_lastResolvedWidth = m_stroke.GetWidth();
  173. else if( IsConnectable() && !IsConnectivityDirty() )
  174. m_lastResolvedWidth = GetEffectiveNetClass()->GetWireWidth();
  175. return m_lastResolvedWidth;
  176. }
  177. int SCH_BUS_BUS_ENTRY::GetPenWidth() const
  178. {
  179. if( m_stroke.GetWidth() > 0 )
  180. m_lastResolvedWidth = m_stroke.GetWidth();
  181. else if( IsConnectable() && !IsConnectivityDirty() )
  182. m_lastResolvedWidth = GetEffectiveNetClass()->GetBusWidth();
  183. return m_lastResolvedWidth;
  184. }
  185. void SCH_BUS_WIRE_ENTRY::GetEndPoints( std::vector< DANGLING_END_ITEM >& aItemList )
  186. {
  187. DANGLING_END_ITEM item( WIRE_ENTRY_END, this, m_pos );
  188. aItemList.push_back( item );
  189. DANGLING_END_ITEM item1( WIRE_ENTRY_END, this, GetEnd() );
  190. aItemList.push_back( item1 );
  191. }
  192. void SCH_BUS_BUS_ENTRY::GetEndPoints( std::vector< DANGLING_END_ITEM >& aItemList )
  193. {
  194. DANGLING_END_ITEM item( BUS_ENTRY_END, this, m_pos );
  195. aItemList.push_back( item );
  196. DANGLING_END_ITEM item1( BUS_ENTRY_END, this, GetEnd() );
  197. aItemList.push_back( item1 );
  198. }
  199. void SCH_BUS_ENTRY_BASE::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  200. {
  201. wxDC* DC = aSettings->GetPrintDC();
  202. COLOR4D color = ( GetBusEntryColor() == COLOR4D::UNSPECIFIED ) ?
  203. aSettings->GetLayerColor( m_layer ) : GetBusEntryColor();
  204. VECTOR2I start = m_pos + aOffset;
  205. VECTOR2I end = GetEnd() + aOffset;
  206. int penWidth = ( GetPenWidth() == 0 ) ? aSettings->GetDefaultPenWidth() : GetPenWidth();
  207. if( GetLineStyle() <= PLOT_DASH_TYPE::FIRST_TYPE )
  208. {
  209. GRLine( DC, start.x, start.y, end.x, end.y, penWidth, color );
  210. }
  211. else
  212. {
  213. SHAPE_SEGMENT segment( start, end );
  214. STROKE_PARAMS::Stroke( &segment, GetLineStyle(), penWidth, aSettings,
  215. [&]( const VECTOR2I& a, const VECTOR2I& b )
  216. {
  217. GRLine( DC, a.x, a.y, b.x, b.y, penWidth, color );
  218. } );
  219. }
  220. }
  221. void SCH_BUS_ENTRY_BASE::MirrorVertically( int aCenter )
  222. {
  223. MIRROR( m_pos.y, aCenter );
  224. m_size.y = -m_size.y;
  225. }
  226. void SCH_BUS_ENTRY_BASE::MirrorHorizontally( int aCenter )
  227. {
  228. MIRROR( m_pos.x, aCenter );
  229. m_size.x = -m_size.x;
  230. }
  231. void SCH_BUS_ENTRY_BASE::Rotate( const VECTOR2I& aCenter )
  232. {
  233. RotatePoint( m_pos, aCenter, ANGLE_90 );
  234. RotatePoint( &m_size.x, &m_size.y, ANGLE_90 );
  235. }
  236. bool SCH_BUS_WIRE_ENTRY::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  237. const SCH_SHEET_PATH* aPath )
  238. {
  239. bool previousStateStart = m_isDanglingStart;
  240. bool previousStateEnd = m_isDanglingEnd;
  241. m_isDanglingStart = m_isDanglingEnd = true;
  242. // Store the connection type and state for the start (0) and end (1)
  243. bool has_wire[2] = { false };
  244. bool has_bus[2] = { false };
  245. for( unsigned ii = 0; ii < aItemList.size(); ii++ )
  246. {
  247. DANGLING_END_ITEM& item = aItemList[ii];
  248. if( item.GetItem() == this )
  249. continue;
  250. switch( item.GetType() )
  251. {
  252. case WIRE_END:
  253. if( m_pos == item.GetPosition() )
  254. has_wire[0] = true;
  255. else if( GetEnd() == item.GetPosition() )
  256. has_wire[1] = true;
  257. break;
  258. case BUS_END:
  259. {
  260. // The bus has created 2 DANGLING_END_ITEMs, one per end.
  261. DANGLING_END_ITEM& nextItem = aItemList[++ii];
  262. if( IsPointOnSegment( item.GetPosition(), nextItem.GetPosition(), m_pos ) )
  263. has_bus[0] = true;
  264. else if( IsPointOnSegment( item.GetPosition(), nextItem.GetPosition(), GetEnd() ) )
  265. has_bus[1] = true;
  266. }
  267. break;
  268. default:
  269. break;
  270. }
  271. }
  272. // A bus-wire entry is connected at both ends if it has a bus and a wire on its
  273. // ends. Otherwise, we connect only one end (in the case of a wire-wire or bus-bus)
  274. if( ( has_wire[0] && has_bus[1] ) || ( has_wire[1] && has_bus[0] ) )
  275. m_isDanglingEnd = m_isDanglingStart = false;
  276. else if( has_wire[0] || has_bus[0] )
  277. m_isDanglingStart = false;
  278. else if( has_wire[1] || has_bus[1] )
  279. m_isDanglingEnd = false;
  280. return (previousStateStart != m_isDanglingStart) || (previousStateEnd != m_isDanglingEnd);
  281. }
  282. bool SCH_BUS_BUS_ENTRY::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  283. const SCH_SHEET_PATH* aPath )
  284. {
  285. bool previousStateStart = m_isDanglingStart;
  286. bool previousStateEnd = m_isDanglingEnd;
  287. m_isDanglingStart = m_isDanglingEnd = true;
  288. for( unsigned ii = 0; ii < aItemList.size(); ii++ )
  289. {
  290. DANGLING_END_ITEM& item = aItemList[ii];
  291. if( item.GetItem() == this )
  292. continue;
  293. switch( item.GetType() )
  294. {
  295. case BUS_END:
  296. {
  297. // The bus has created 2 DANGLING_END_ITEMs, one per end.
  298. DANGLING_END_ITEM& nextItem = aItemList[++ii];
  299. if( IsPointOnSegment( item.GetPosition(), nextItem.GetPosition(), m_pos ) )
  300. m_isDanglingStart = false;
  301. if( IsPointOnSegment( item.GetPosition(), nextItem.GetPosition(), GetEnd() ) )
  302. m_isDanglingEnd = false;
  303. }
  304. break;
  305. default:
  306. break;
  307. }
  308. }
  309. return (previousStateStart != m_isDanglingStart) || (previousStateEnd != m_isDanglingEnd);
  310. }
  311. bool SCH_BUS_ENTRY_BASE::IsDangling() const
  312. {
  313. return m_isDanglingStart || m_isDanglingEnd;
  314. }
  315. std::vector<VECTOR2I> SCH_BUS_ENTRY_BASE::GetConnectionPoints() const
  316. {
  317. return { m_pos, GetEnd() };
  318. }
  319. wxString SCH_BUS_WIRE_ENTRY::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  320. {
  321. return wxString( _( "Bus to Wire Entry" ) );
  322. }
  323. wxString SCH_BUS_BUS_ENTRY::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  324. {
  325. return wxString( _( "Bus to Bus Entry" ) );
  326. }
  327. BITMAPS SCH_BUS_WIRE_ENTRY::GetMenuImage() const
  328. {
  329. return BITMAPS::add_line2bus;
  330. }
  331. BITMAPS SCH_BUS_BUS_ENTRY::GetMenuImage() const
  332. {
  333. return BITMAPS::add_bus2bus;
  334. }
  335. bool SCH_BUS_ENTRY_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  336. {
  337. // Insure minimum accuracy
  338. if( aAccuracy == 0 )
  339. aAccuracy = ( GetPenWidth() / 2 ) + 4;
  340. return TestSegmentHit( aPosition, m_pos, GetEnd(), aAccuracy );
  341. }
  342. bool SCH_BUS_ENTRY_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  343. {
  344. BOX2I rect = aRect;
  345. rect.Inflate( aAccuracy );
  346. if( aContained )
  347. return rect.Contains( GetBoundingBox() );
  348. return rect.Intersects( GetBoundingBox() );
  349. }
  350. void SCH_BUS_ENTRY_BASE::Plot( PLOTTER* aPlotter, bool aBackground ) const
  351. {
  352. if( aBackground )
  353. return;
  354. auto* settings = static_cast<KIGFX::SCH_RENDER_SETTINGS*>( aPlotter->RenderSettings() );
  355. COLOR4D color = ( GetBusEntryColor() == COLOR4D::UNSPECIFIED )
  356. ? settings->GetLayerColor( m_layer )
  357. : GetBusEntryColor();
  358. int penWidth = ( GetPenWidth() == 0 ) ? settings->GetDefaultPenWidth() : GetPenWidth();
  359. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  360. aPlotter->SetCurrentLineWidth( penWidth );
  361. aPlotter->SetColor( color );
  362. aPlotter->SetDash( penWidth, GetLineStyle() );
  363. aPlotter->MoveTo( m_pos );
  364. aPlotter->FinishTo( GetEnd() );
  365. aPlotter->SetDash( penWidth, PLOT_DASH_TYPE::SOLID );
  366. }
  367. void SCH_BUS_ENTRY_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
  368. std::vector<MSG_PANEL_ITEM>& aList )
  369. {
  370. wxString msg;
  371. switch( GetLayer() )
  372. {
  373. default:
  374. case LAYER_WIRE: msg = _( "Wire" ); break;
  375. case LAYER_BUS: msg = _( "Bus" ); break;
  376. }
  377. aList.emplace_back( _( "Bus Entry Type" ), msg );
  378. SCH_CONNECTION* conn = nullptr;
  379. if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  380. conn = Connection();
  381. if( conn )
  382. {
  383. conn->AppendInfoToMsgPanel( aList );
  384. if( !conn->IsBus() )
  385. aList.emplace_back( _( "Resolved Netclass" ), GetEffectiveNetClass()->GetName() );
  386. }
  387. }
  388. bool SCH_BUS_ENTRY_BASE::operator <( const SCH_ITEM& aItem ) const
  389. {
  390. if( Type() != aItem.Type() )
  391. return Type() < aItem.Type();
  392. auto symbol = static_cast<const SCH_BUS_ENTRY_BASE*>( &aItem );
  393. if( GetLayer() != symbol->GetLayer() )
  394. return GetLayer() < symbol->GetLayer();
  395. if( GetPosition().x != symbol->GetPosition().x )
  396. return GetPosition().x < symbol->GetPosition().x;
  397. if( GetPosition().y != symbol->GetPosition().y )
  398. return GetPosition().y < symbol->GetPosition().y;
  399. if( GetEnd().x != symbol->GetEnd().x )
  400. return GetEnd().x < symbol->GetEnd().x;
  401. return GetEnd().y < symbol->GetEnd().y;
  402. }
  403. bool SCH_BUS_WIRE_ENTRY::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
  404. {
  405. // Don't generate connections between bus entries and buses, since there is
  406. // a connectivity change at that point (e.g. A[7..0] to A7)
  407. if( ( aItem->Type() == SCH_LINE_T ) &&
  408. ( static_cast<const SCH_LINE*>( aItem )->GetLayer() == LAYER_BUS ) )
  409. {
  410. return false;
  411. }
  412. // Same for bus junctions
  413. if( ( aItem->Type() == SCH_JUNCTION_T ) &&
  414. ( static_cast<const SCH_JUNCTION*>( aItem )->GetLayer() == LAYER_BUS_JUNCTION ) )
  415. {
  416. return false;
  417. }
  418. // Don't generate connections between bus entries and bus labels that happen
  419. // to land at the same point on the bus wire as this bus entry
  420. if( ( aItem->Type() == SCH_LABEL_T ) &&
  421. SCH_CONNECTION::IsBusLabel( static_cast<const SCH_LABEL*>( aItem )->GetText() ) )
  422. {
  423. return false;
  424. }
  425. // Don't generate connections between two bus-wire entries
  426. if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
  427. return false;
  428. return true;
  429. }