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.

482 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 CERN
  5. * Copyright (C) 2019-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Jon Evans <jon@craftyjon.com>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <base_units.h>
  22. #include <lib_pin.h>
  23. #include <sch_symbol.h>
  24. #include <sch_pin.h>
  25. #include <schematic.h>
  26. #include <schematic_settings.h>
  27. #include <sch_sheet_path.h>
  28. #include <sch_edit_frame.h>
  29. #include "string_utils.h"
  30. SCH_PIN::SCH_PIN( LIB_PIN* aLibPin, SCH_SYMBOL* aParentSymbol ) :
  31. SCH_ITEM( aParentSymbol, SCH_PIN_T )
  32. {
  33. m_layer = LAYER_PIN;
  34. m_alt = wxEmptyString;
  35. m_number = aLibPin->GetNumber();
  36. m_libPin = aLibPin;
  37. SetPosition( aLibPin->GetPosition() );
  38. m_isDangling = true;
  39. }
  40. /**
  41. * Create a proxy pin from an alternate pin designation.
  42. * The LIB_PIN data will be filled in when the pin is resolved (see SCH_SYMBOL::UpdatePins).
  43. */
  44. SCH_PIN::SCH_PIN( SCH_SYMBOL* aParentSymbol, const wxString& aNumber, const wxString& aAlt ) :
  45. SCH_ITEM( aParentSymbol, SCH_PIN_T )
  46. {
  47. m_layer = LAYER_PIN;
  48. m_alt = aAlt;
  49. m_number = aNumber;
  50. m_libPin = nullptr;
  51. m_isDangling = true;
  52. }
  53. SCH_PIN::SCH_PIN( const SCH_PIN& aPin ) :
  54. SCH_ITEM( aPin )
  55. {
  56. m_layer = aPin.m_layer;
  57. m_alt = aPin.m_alt;
  58. m_number = aPin.m_number;
  59. m_libPin = aPin.m_libPin;
  60. m_position = aPin.m_position;
  61. m_isDangling = aPin.m_isDangling;
  62. }
  63. SCH_PIN& SCH_PIN::operator=( const SCH_PIN& aPin )
  64. {
  65. SCH_ITEM::operator=( aPin );
  66. m_alt = aPin.m_alt;
  67. m_number = aPin.m_number;
  68. m_libPin = aPin.m_libPin;
  69. m_position = aPin.m_position;
  70. m_isDangling = aPin.m_isDangling;
  71. return *this;
  72. }
  73. wxString SCH_PIN::GetName() const
  74. {
  75. if( !m_alt.IsEmpty() )
  76. return m_alt;
  77. return m_libPin->GetName();
  78. }
  79. wxString SCH_PIN::GetShownName() const
  80. {
  81. wxString name = m_libPin->GetName();
  82. if( !m_alt.IsEmpty() )
  83. name = m_alt;
  84. if( name == wxS( "~" ) )
  85. return wxEmptyString;
  86. else
  87. return name;
  88. }
  89. wxString SCH_PIN::GetShownNumber() const
  90. {
  91. if( m_number == wxS( "~" ) )
  92. return wxEmptyString;
  93. else
  94. return m_number;
  95. }
  96. ELECTRICAL_PINTYPE SCH_PIN::GetType() const
  97. {
  98. if( !m_alt.IsEmpty() )
  99. return m_libPin->GetAlt( m_alt ).m_Type;
  100. return m_libPin->GetType();
  101. }
  102. GRAPHIC_PINSHAPE SCH_PIN::GetShape() const
  103. {
  104. if( !m_alt.IsEmpty() )
  105. return m_libPin->GetAlt( m_alt ).m_Shape;
  106. return m_libPin->GetShape();
  107. }
  108. PIN_ORIENTATION SCH_PIN::GetOrientation() const
  109. {
  110. return m_libPin->GetOrientation();
  111. }
  112. int SCH_PIN::GetLength() const
  113. {
  114. return m_libPin->GetLength();
  115. }
  116. const BOX2I SCH_PIN::ViewBBox() const
  117. {
  118. return GetBoundingBox( false, true, true );
  119. }
  120. void SCH_PIN::ViewGetLayers( int aLayers[], int& aCount ) const
  121. {
  122. aCount = 4;
  123. aLayers[0] = LAYER_DANGLING;
  124. aLayers[1] = LAYER_DEVICE;
  125. aLayers[2] = LAYER_SELECTION_SHADOWS;
  126. aLayers[3] = LAYER_OP_CURRENTS;
  127. }
  128. bool SCH_PIN::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxDat ) const
  129. {
  130. const SCH_SEARCH_DATA& schSearchData =
  131. dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData );
  132. if( !schSearchData.searchAllPins )
  133. return false;
  134. return EDA_ITEM::Matches( GetName(), aSearchData )
  135. || EDA_ITEM::Matches( GetNumber(), aSearchData );
  136. }
  137. bool SCH_PIN::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  138. {
  139. bool isReplaced = false;
  140. /* TODO: waiting on a way to override pins in the schematic...
  141. isReplaced |= EDA_ITEM::Replace( aSearchData, m_name );
  142. isReplaced |= EDA_ITEM::Replace( aSearchData, m_number );
  143. */
  144. return isReplaced;
  145. }
  146. SCH_SYMBOL* SCH_PIN::GetParentSymbol() const
  147. {
  148. return static_cast<SCH_SYMBOL*>( GetParent() );
  149. }
  150. wxString SCH_PIN::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  151. {
  152. LIB_PIN::ALT localStorage;
  153. LIB_PIN::ALT* alt = nullptr;
  154. if( !m_alt.IsEmpty() )
  155. {
  156. localStorage = m_libPin->GetAlt( m_alt );
  157. alt = &localStorage;
  158. }
  159. return wxString::Format( "Symbol %s %s",
  160. UnescapeString( GetParentSymbol()->GetField( REFERENCE_FIELD )->GetText() ),
  161. m_libPin->GetItemDescription( aUnitsProvider, alt ) );
  162. }
  163. void SCH_PIN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  164. {
  165. wxString msg;
  166. aList.emplace_back( _( "Type" ), _( "Pin" ) );
  167. if( m_libPin->GetConvert() == LIB_ITEM::LIB_CONVERT::BASE )
  168. msg = _( "no" );
  169. else if( m_libPin->GetConvert() == LIB_ITEM::LIB_CONVERT::DEMORGAN )
  170. msg = _( "yes" );
  171. else
  172. msg = wxT( "?" );
  173. aList.emplace_back( _( "Converted" ), msg );
  174. aList.emplace_back( _( "Name" ), GetShownName() );
  175. aList.emplace_back( _( "Number" ), GetShownNumber() );
  176. aList.emplace_back( _( "Type" ), ElectricalPinTypeGetText( GetType() ) );
  177. aList.emplace_back( _( "Style" ), PinShapeGetText( GetShape() ) );
  178. aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
  179. aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ), true );
  180. aList.emplace_back( _( "Orientation" ), PinOrientationName( GetOrientation() ) );
  181. SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( aFrame );
  182. SCH_SHEET_PATH* currentSheet = schframe ? &schframe->GetCurrentSheet() : nullptr;
  183. SCH_SYMBOL* symbol = GetParentSymbol();
  184. // Don't use GetShownText(); we want to see the variable references here
  185. aList.emplace_back( symbol->GetRef( currentSheet ),
  186. UnescapeString( symbol->GetField( VALUE_FIELD )->GetText() ) );
  187. #if defined(DEBUG)
  188. if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  189. {
  190. SCH_CONNECTION* conn = Connection();
  191. if( conn )
  192. conn->AppendInfoToMsgPanel( aList );
  193. }
  194. #endif
  195. }
  196. bool SCH_PIN::IsStacked( const SCH_PIN* aPin ) const
  197. {
  198. return m_parent == aPin->GetParent()
  199. && GetTransformedPosition() == aPin->GetTransformedPosition()
  200. && GetName() == aPin->GetName()
  201. && ( ( GetType() == aPin->GetType() ) || ( GetType() == ELECTRICAL_PINTYPE::PT_PASSIVE )
  202. || ( aPin->GetType() == ELECTRICAL_PINTYPE::PT_PASSIVE ) );
  203. }
  204. void SCH_PIN::ClearDefaultNetName( const SCH_SHEET_PATH* aPath )
  205. {
  206. std::lock_guard<std::recursive_mutex> lock( m_netmap_mutex );
  207. if( aPath )
  208. m_net_name_map.erase( *aPath );
  209. else
  210. m_net_name_map.clear();
  211. }
  212. wxString SCH_PIN::GetDefaultNetName( const SCH_SHEET_PATH& aPath, bool aForceNoConnect )
  213. {
  214. // Need to check for parent as power symbol to make sure we aren't dealing
  215. // with legacy global power pins on non-power symbols
  216. if( IsGlobalPower() )
  217. {
  218. if( GetLibPin()->GetParent()->IsPower() )
  219. {
  220. return EscapeString( GetParentSymbol()->GetValueFieldText( true, &aPath, false ),
  221. CTX_NETNAME );
  222. }
  223. else
  224. {
  225. return EscapeString( m_libPin->GetName(), CTX_NETNAME );
  226. }
  227. }
  228. std::lock_guard<std::recursive_mutex> lock( m_netmap_mutex );
  229. auto it = m_net_name_map.find( aPath );
  230. if( it != m_net_name_map.end() )
  231. {
  232. if( it->second.second == aForceNoConnect )
  233. return it->second.first;
  234. }
  235. wxString name = "Net-(";
  236. bool unconnected = false;
  237. if( aForceNoConnect || GetType() == ELECTRICAL_PINTYPE::PT_NC )
  238. {
  239. unconnected = true;
  240. name = ( "unconnected-(" );
  241. }
  242. bool annotated = true;
  243. std::vector<SCH_PIN*> pins = GetParentSymbol()->GetPins( &aPath );
  244. bool has_multiple = false;
  245. for( SCH_PIN* pin : pins )
  246. {
  247. if( pin->GetShownName() == GetShownName()
  248. && pin->GetShownNumber() != GetShownNumber()
  249. && unconnected == ( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC ) )
  250. {
  251. has_multiple = true;
  252. break;
  253. }
  254. }
  255. // Use timestamp for unannotated symbols
  256. if( GetParentSymbol()->GetRef( &aPath, false ).Last() == '?' )
  257. {
  258. name << GetParentSymbol()->m_Uuid.AsString();
  259. name << "-Pad" << m_libPin->GetNumber() << ")";
  260. annotated = false;
  261. }
  262. else if( !m_libPin->GetShownName().IsEmpty()
  263. && m_libPin->GetShownName() != m_libPin->GetShownNumber() )
  264. {
  265. // Pin names might not be unique between different units so we must have the
  266. // unit token in the reference designator
  267. name << GetParentSymbol()->GetRef( &aPath, true );
  268. name << "-" << EscapeString( m_libPin->GetShownName(), CTX_NETNAME );
  269. if( unconnected || has_multiple )
  270. name << "-Pad" << EscapeString( m_libPin->GetShownNumber(), CTX_NETNAME );
  271. name << ")";
  272. }
  273. else
  274. {
  275. // Pin numbers are unique, so we skip the unit token
  276. name << GetParentSymbol()->GetRef( &aPath, false );
  277. name << "-Pad" << EscapeString( m_libPin->GetShownNumber(), CTX_NETNAME ) << ")";
  278. }
  279. if( annotated )
  280. m_net_name_map[ aPath ] = std::make_pair( name, aForceNoConnect );
  281. return name;
  282. }
  283. VECTOR2I SCH_PIN::GetTransformedPosition() const
  284. {
  285. TRANSFORM t = GetParentSymbol()->GetTransform();
  286. return t.TransformCoordinate( GetLocalPosition() ) + GetParentSymbol()->GetPosition();
  287. }
  288. const BOX2I SCH_PIN::GetBoundingBox( bool aIncludeInvisiblePins, bool aIncludeNameAndNumber,
  289. bool aIncludeElectricalType ) const
  290. {
  291. TRANSFORM t = GetParentSymbol()->GetTransform();
  292. BOX2I r = m_libPin->GetBoundingBox( aIncludeInvisiblePins, aIncludeNameAndNumber,
  293. aIncludeElectricalType );
  294. r.RevertYAxis();
  295. r = t.TransformCoordinate( r );
  296. r.Offset( GetParentSymbol()->GetPosition() );
  297. return r;
  298. }
  299. bool SCH_PIN::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  300. {
  301. // When looking for an "exact" hit aAccuracy will be 0 which works poorly if the pin has
  302. // no pin number or name. Give it a floor.
  303. if( Schematic() )
  304. aAccuracy = std::max( aAccuracy, Schematic()->Settings().m_PinSymbolSize / 4 );
  305. BOX2I rect = GetBoundingBox( false, true, m_flags & SHOW_ELEC_TYPE );
  306. return rect.Inflate( aAccuracy ).Contains( aPosition );
  307. }
  308. bool SCH_PIN::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  309. {
  310. BOX2I sel = aRect;
  311. if( aAccuracy )
  312. sel.Inflate( aAccuracy );
  313. if( aContained )
  314. return sel.Contains( GetBoundingBox( false, false, false ) );
  315. return sel.Intersects( GetBoundingBox( false, true, m_flags & SHOW_ELEC_TYPE ) );
  316. }
  317. EDA_ITEM* SCH_PIN::Clone() const
  318. {
  319. return new SCH_PIN( *this );
  320. }
  321. bool SCH_PIN::ConnectionPropagatesTo( const EDA_ITEM* aItem ) const
  322. {
  323. // Reciprocal checking is done in CONNECTION_GRAPH anyway
  324. return !( m_libPin->GetType() == ELECTRICAL_PINTYPE::PT_NC );
  325. }
  326. bool SCH_PIN::operator==( const SCH_ITEM& aOther ) const
  327. {
  328. if( aOther.Type() != SCH_PIN_T )
  329. return false;
  330. const SCH_PIN& other = static_cast<const SCH_PIN&>( aOther );
  331. if( m_number != other.m_number )
  332. return false;
  333. if( m_position != other.m_position )
  334. return false;
  335. return m_libPin == other.m_libPin;
  336. }
  337. double SCH_PIN::Similarity( const SCH_ITEM& aOther ) const
  338. {
  339. if( m_Uuid == aOther.m_Uuid )
  340. return 1.0;
  341. if( aOther.Type() != SCH_PIN_T )
  342. return 0.0;
  343. const SCH_PIN& other = static_cast<const SCH_PIN&>( aOther );
  344. if( m_number != other.m_number )
  345. return 0.0;
  346. if( m_position != other.m_position )
  347. return 0.0;
  348. return m_libPin->Similarity( *other.m_libPin );
  349. }
  350. static struct SCH_PIN_DESC
  351. {
  352. SCH_PIN_DESC()
  353. {
  354. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  355. REGISTER_TYPE( SCH_PIN );
  356. propMgr.InheritsAfter( TYPE_HASH( SCH_PIN ), TYPE_HASH( SCH_ITEM ) );
  357. propMgr.AddProperty( new PROPERTY<SCH_PIN, wxString>( _HKI( "Pin Name" ),
  358. NO_SETTER( SCH_PIN, wxString ), &SCH_PIN::GetName ) );
  359. propMgr.AddProperty( new PROPERTY<SCH_PIN, wxString>( _HKI( "Pin Number" ),
  360. NO_SETTER( SCH_PIN, wxString ), &SCH_PIN::GetNumber ) );
  361. propMgr.AddProperty( new PROPERTY<SCH_PIN, int>( _HKI( "Length" ),
  362. NO_SETTER( SCH_PIN, int ), &SCH_PIN::GetLength, PROPERTY_DISPLAY::PT_SIZE ) );
  363. }
  364. } _SCH_PIN_DESC;