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.

558 lines
15 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 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 <macros.h>
  26. #include <plotters/plotter.h>
  27. #include <base_units.h>
  28. #include <widgets/msgpanel.h>
  29. #include <bitmaps.h>
  30. #include <eda_draw_frame.h>
  31. #include <general.h>
  32. #include <schematic.h>
  33. #include <sch_shape.h>
  34. #include "plotters/plotter.h"
  35. SCH_SHAPE::SCH_SHAPE( SHAPE_T aShape, int aLineWidth, FILL_T aFillType, KICAD_T aType ) :
  36. SCH_ITEM( nullptr, aType ),
  37. EDA_SHAPE( aShape, aLineWidth, aFillType )
  38. {
  39. SetLayer( LAYER_NOTES );
  40. }
  41. EDA_ITEM* SCH_SHAPE::Clone() const
  42. {
  43. return new SCH_SHAPE( *this );
  44. }
  45. void SCH_SHAPE::SwapData( SCH_ITEM* aItem )
  46. {
  47. SCH_ITEM::SwapFlags( aItem );
  48. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
  49. EDA_SHAPE::SwapShape( shape );
  50. std::swap( m_layer, shape->m_layer );
  51. }
  52. void SCH_SHAPE::SetStroke( const STROKE_PARAMS& aStroke )
  53. {
  54. m_stroke = aStroke;
  55. }
  56. void SCH_SHAPE::Move( const VECTOR2I& aOffset )
  57. {
  58. move( aOffset );
  59. }
  60. void SCH_SHAPE::Normalize()
  61. {
  62. if( GetShape() == SHAPE_T::RECTANGLE )
  63. {
  64. VECTOR2I size = GetEnd() - GetPosition();
  65. if( size.y < 0 )
  66. {
  67. SetStartY( GetStartY() + size.y );
  68. SetEndY( GetStartY() - size.y );
  69. }
  70. if( size.x < 0 )
  71. {
  72. SetStartX( GetStartX() + size.x );
  73. SetEndX( GetStartX() - size.x );
  74. }
  75. }
  76. }
  77. void SCH_SHAPE::MirrorHorizontally( int aCenter )
  78. {
  79. flip( VECTOR2I( aCenter, 0 ), true );
  80. }
  81. void SCH_SHAPE::MirrorVertically( int aCenter )
  82. {
  83. flip( VECTOR2I( 0, aCenter ), false );
  84. }
  85. void SCH_SHAPE::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
  86. {
  87. rotate( aCenter, aRotateCCW ? ANGLE_270 : ANGLE_90 );
  88. }
  89. void SCH_SHAPE::Plot( PLOTTER* aPlotter, bool aBackground,
  90. const SCH_PLOT_SETTINGS& aPlotSettings ) const
  91. {
  92. int pen_size = GetPenWidth();
  93. if( pen_size > 0 )
  94. pen_size = std::max( pen_size, aPlotter->RenderSettings()->GetMinPenWidth() );
  95. static std::vector<VECTOR2I> cornerList;
  96. if( GetShape() == SHAPE_T::POLY )
  97. {
  98. cornerList.clear();
  99. for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() )
  100. cornerList.push_back( pt );
  101. }
  102. if( aBackground )
  103. {
  104. if( !aPlotter->GetColorMode() )
  105. return;
  106. if( IsFilled() )
  107. {
  108. if( GetFillColor() != COLOR4D::UNSPECIFIED )
  109. aPlotter->SetColor( GetFillColor() );
  110. else
  111. aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_NOTES ) );
  112. switch( GetShape() )
  113. {
  114. case SHAPE_T::ARC:
  115. aPlotter->Arc( GetStart(), GetArcMid(), GetEnd(), m_fill, 0 );
  116. break;
  117. case SHAPE_T::CIRCLE:
  118. aPlotter->Circle( getCenter(), GetRadius() * 2, m_fill, 0 );
  119. break;
  120. case SHAPE_T::RECTANGLE:
  121. aPlotter->Rect( GetStart(), GetEnd(), m_fill, 0 );
  122. break;
  123. case SHAPE_T::POLY:
  124. aPlotter->PlotPoly( cornerList, m_fill, 0 );
  125. break;
  126. case SHAPE_T::BEZIER:
  127. aPlotter->PlotPoly( m_bezierPoints, m_fill, 0 );
  128. break;
  129. default:
  130. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  131. }
  132. }
  133. }
  134. else /* if( aForeground ) */
  135. {
  136. if( aPlotter->GetColorMode() && GetStroke().GetColor() != COLOR4D::UNSPECIFIED )
  137. aPlotter->SetColor( GetStroke().GetColor() );
  138. else
  139. aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_NOTES ) );
  140. aPlotter->SetCurrentLineWidth( pen_size );
  141. aPlotter->SetDash( pen_size, GetEffectiveLineStyle() );
  142. switch( GetShape() )
  143. {
  144. case SHAPE_T::ARC:
  145. aPlotter->Arc( GetStart(), GetArcMid(), GetEnd(), FILL_T::NO_FILL, pen_size );
  146. break;
  147. case SHAPE_T::CIRCLE:
  148. aPlotter->Circle( getCenter(), GetRadius() * 2, FILL_T::NO_FILL, pen_size );
  149. break;
  150. case SHAPE_T::RECTANGLE:
  151. aPlotter->Rect( GetStart(), GetEnd(), FILL_T::NO_FILL, pen_size );
  152. break;
  153. case SHAPE_T::POLY:
  154. aPlotter->PlotPoly( cornerList, FILL_T::NO_FILL, pen_size );
  155. break;
  156. case SHAPE_T::BEZIER:
  157. aPlotter->PlotPoly( m_bezierPoints, FILL_T::NO_FILL, pen_size );
  158. break;
  159. default:
  160. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  161. }
  162. aPlotter->SetDash( pen_size, LINE_STYLE::SOLID );
  163. }
  164. }
  165. int SCH_SHAPE::GetPenWidth() const
  166. {
  167. if( m_stroke.GetWidth() > 0 )
  168. return m_stroke.GetWidth();
  169. // Historically 0 meant "default width" and negative numbers meant "don't stroke".
  170. if( GetWidth() < 0 )
  171. return 0;
  172. SCHEMATIC* schematic = Schematic();
  173. if( schematic )
  174. return schematic->Settings().m_DefaultLineWidth;
  175. return schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
  176. }
  177. void SCH_SHAPE::PrintBackground( const SCH_RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  178. {
  179. wxDC* DC = aSettings->GetPrintDC();
  180. COLOR4D color;
  181. unsigned ptCount = 0;
  182. VECTOR2I* buffer = nullptr;
  183. if( GetShape() == SHAPE_T::POLY )
  184. {
  185. SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
  186. ptCount = poly.GetPointCount();
  187. buffer = new VECTOR2I[ptCount];
  188. for( unsigned ii = 0; ii < ptCount; ++ii )
  189. buffer[ii] = poly.CPoint( ii );
  190. }
  191. else if( GetShape() == SHAPE_T::BEZIER )
  192. {
  193. ptCount = m_bezierPoints.size();
  194. buffer = new VECTOR2I[ptCount];
  195. for( size_t ii = 0; ii < ptCount; ++ii )
  196. buffer[ii] = m_bezierPoints[ii];
  197. }
  198. if( GetFillMode() == FILL_T::FILLED_WITH_COLOR )
  199. {
  200. if( GetFillColor() == COLOR4D::UNSPECIFIED )
  201. color = aSettings->GetLayerColor( LAYER_NOTES );
  202. else
  203. color = GetFillColor();
  204. switch( GetShape() )
  205. {
  206. case SHAPE_T::ARC:
  207. GRFilledArc( DC, GetEnd(), GetStart(), getCenter(), 0, color, color );
  208. break;
  209. case SHAPE_T::CIRCLE:
  210. GRFilledCircle( DC, GetStart(), GetRadius(), 0, color, color );
  211. break;
  212. case SHAPE_T::RECTANGLE:
  213. GRFilledRect( DC, GetStart(), GetEnd(), 0, color, color );
  214. break;
  215. case SHAPE_T::POLY:
  216. GRPoly( DC, ptCount, buffer, true, 0, color, color );
  217. break;
  218. case SHAPE_T::BEZIER:
  219. GRPoly( DC, ptCount, buffer, true, 0, color, color );
  220. break;
  221. default:
  222. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  223. }
  224. }
  225. delete[] buffer;
  226. }
  227. void SCH_SHAPE::Print( const SCH_RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  228. {
  229. int penWidth = GetPenWidth();
  230. wxDC* DC = aSettings->GetPrintDC();
  231. COLOR4D color = GetStroke().GetColor();
  232. if( color == COLOR4D::UNSPECIFIED )
  233. color = aSettings->GetLayerColor( LAYER_NOTES );
  234. COLOR4D bg = aSettings->GetBackgroundColor();
  235. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  236. bg = COLOR4D::WHITE;
  237. unsigned ptCount = 0;
  238. VECTOR2I* buffer = nullptr;
  239. if( GetShape() == SHAPE_T::POLY )
  240. {
  241. SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
  242. ptCount = poly.GetPointCount();
  243. buffer = new VECTOR2I[ptCount];
  244. for( unsigned ii = 0; ii < ptCount; ++ii )
  245. buffer[ii] = poly.CPoint( ii );
  246. }
  247. else if( GetShape() == SHAPE_T::BEZIER )
  248. {
  249. ptCount = m_bezierPoints.size();
  250. buffer = new VECTOR2I[ptCount];
  251. for( size_t ii = 0; ii < ptCount; ++ii )
  252. buffer[ii] = m_bezierPoints[ii];
  253. }
  254. COLOR4D fillColor = COLOR4D::UNSPECIFIED;
  255. if( GetFillMode() == FILL_T::FILLED_SHAPE )
  256. fillColor = color;
  257. else if( GetFillMode() == FILL_T::FILLED_WITH_COLOR )
  258. fillColor = GetFillColor();
  259. if( fillColor != COLOR4D::UNSPECIFIED )
  260. {
  261. switch( GetShape() )
  262. {
  263. case SHAPE_T::ARC:
  264. GRFilledArc( DC, GetEnd(), GetStart(), getCenter(), 0, fillColor, fillColor );
  265. break;
  266. case SHAPE_T::CIRCLE:
  267. GRFilledCircle( DC, GetStart(), GetRadius(), 0, fillColor, fillColor );
  268. break;
  269. case SHAPE_T::RECTANGLE:
  270. GRFilledRect( DC, GetStart(), GetEnd(), 0, fillColor, fillColor );
  271. break;
  272. case SHAPE_T::POLY:
  273. GRPoly( DC, ptCount, buffer, true, 0, fillColor, fillColor );
  274. break;
  275. case SHAPE_T::BEZIER:
  276. GRPoly( DC, ptCount, buffer, true, 0, fillColor, fillColor );
  277. break;
  278. default:
  279. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  280. }
  281. }
  282. else
  283. {
  284. penWidth = std::max( penWidth, aSettings->GetMinPenWidth() );
  285. }
  286. if( penWidth > 0 )
  287. {
  288. if( GetEffectiveLineStyle() == LINE_STYLE::SOLID )
  289. {
  290. switch( GetShape() )
  291. {
  292. case SHAPE_T::ARC:
  293. GRArc( DC, GetEnd(), GetStart(), getCenter(), penWidth, color );
  294. break;
  295. case SHAPE_T::CIRCLE:
  296. GRCircle( DC, GetStart(), GetRadius(), penWidth, color );
  297. break;
  298. case SHAPE_T::RECTANGLE:
  299. GRRect( DC, GetStart(), GetEnd(), penWidth, color );
  300. break;
  301. case SHAPE_T::POLY:
  302. GRPoly( DC, ptCount, buffer, false, penWidth, color, color );
  303. break;
  304. case SHAPE_T::BEZIER:
  305. GRPoly( DC, ptCount, buffer, false, penWidth, color, color );
  306. break;
  307. default:
  308. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  309. }
  310. }
  311. else
  312. {
  313. std::vector<SHAPE*> shapes = MakeEffectiveShapes( true );
  314. for( SHAPE* shape : shapes )
  315. {
  316. STROKE_PARAMS::Stroke( shape, GetEffectiveLineStyle(), penWidth, aSettings,
  317. [&]( const VECTOR2I& a, const VECTOR2I& b )
  318. {
  319. GRLine( DC, a.x, a.y, b.x, b.y, penWidth, color );
  320. } );
  321. }
  322. for( SHAPE* shape : shapes )
  323. delete shape;
  324. }
  325. }
  326. delete[] buffer;
  327. }
  328. void SCH_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  329. {
  330. SCH_ITEM::GetMsgPanelInfo( aFrame, aList );
  331. ShapeGetMsgPanelInfo( aFrame, aList );
  332. }
  333. wxString SCH_SHAPE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  334. {
  335. switch( GetShape() )
  336. {
  337. case SHAPE_T::ARC:
  338. return wxString::Format( _( "Arc, radius %s" ),
  339. aUnitsProvider->MessageTextFromValue( GetRadius() ) );
  340. case SHAPE_T::CIRCLE:
  341. return wxString::Format( _( "Circle, radius %s" ),
  342. aUnitsProvider->MessageTextFromValue( GetRadius() ) );
  343. case SHAPE_T::RECTANGLE:
  344. return wxString::Format( _( "Rectangle, width %s height %s" ),
  345. aUnitsProvider->MessageTextFromValue( std::abs( m_start.x - m_end.x ) ),
  346. aUnitsProvider->MessageTextFromValue( std::abs( m_start.y - m_end.y ) ) );
  347. case SHAPE_T::POLY:
  348. return wxString::Format( _( "Polyline, %d points" ),
  349. int( m_poly.Outline( 0 ).GetPointCount() ) );
  350. case SHAPE_T::BEZIER:
  351. return wxString::Format( _( "Bezier Curve, %d points" ),
  352. int( m_bezierPoints.size() ) );
  353. default:
  354. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  355. return wxEmptyString;
  356. }
  357. }
  358. BITMAPS SCH_SHAPE::GetMenuImage() const
  359. {
  360. switch( GetShape() )
  361. {
  362. case SHAPE_T::SEGMENT: return BITMAPS::add_line;
  363. case SHAPE_T::ARC: return BITMAPS::add_arc;
  364. case SHAPE_T::CIRCLE: return BITMAPS::add_circle;
  365. case SHAPE_T::RECTANGLE: return BITMAPS::add_rectangle;
  366. case SHAPE_T::POLY: return BITMAPS::add_graphical_segments;
  367. case SHAPE_T::BEZIER: return BITMAPS::add_bezier;
  368. default:
  369. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  370. return BITMAPS::question_mark;
  371. }
  372. }
  373. void SCH_SHAPE::ViewGetLayers( int aLayers[], int& aCount ) const
  374. {
  375. aCount = 3;
  376. aLayers[0] = LAYER_NOTES;
  377. aLayers[1] = LAYER_NOTES_BACKGROUND;
  378. aLayers[2] = LAYER_SELECTION_SHADOWS;
  379. }
  380. void SCH_SHAPE::AddPoint( const VECTOR2I& aPosition )
  381. {
  382. if( GetShape() == SHAPE_T::POLY )
  383. {
  384. if( m_poly.IsEmpty() )
  385. m_poly.NewOutline();
  386. m_poly.Outline( 0 ).Append( aPosition, true );
  387. }
  388. else
  389. {
  390. UNIMPLEMENTED_FOR( SHAPE_T_asString() );
  391. }
  392. }
  393. bool SCH_SHAPE::operator==( const SCH_ITEM& aOther ) const
  394. {
  395. if( aOther.Type() != Type() )
  396. return false;
  397. const SCH_SHAPE& other = static_cast<const SCH_SHAPE&>( aOther );
  398. return EDA_SHAPE::operator==( other );
  399. }
  400. double SCH_SHAPE::Similarity( const SCH_ITEM& aOther ) const
  401. {
  402. if( aOther.Type() != Type() )
  403. return 0.0;
  404. const SCH_SHAPE& other = static_cast<const SCH_SHAPE&>( aOther );
  405. double similarity = EDA_SHAPE::Similarity( other );
  406. return similarity;
  407. }
  408. static struct SCH_SHAPE_DESC
  409. {
  410. SCH_SHAPE_DESC()
  411. {
  412. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  413. REGISTER_TYPE( SCH_SHAPE );
  414. propMgr.AddTypeCast( new TYPE_CAST<SCH_SHAPE, SCH_ITEM> );
  415. propMgr.AddTypeCast( new TYPE_CAST<SCH_SHAPE, EDA_SHAPE> );
  416. propMgr.InheritsAfter( TYPE_HASH( SCH_SHAPE ), TYPE_HASH( SCH_ITEM ) );
  417. propMgr.InheritsAfter( TYPE_HASH( SCH_SHAPE ), TYPE_HASH( EDA_SHAPE ) );
  418. // Only polygons have meaningful Position properties.
  419. // On other shapes, these are duplicates of the Start properties.
  420. auto isPolygon =
  421. []( INSPECTABLE* aItem ) -> bool
  422. {
  423. if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( aItem ) )
  424. return shape->GetShape() == SHAPE_T::POLY;
  425. return false;
  426. };
  427. propMgr.OverrideAvailability( TYPE_HASH( SCH_SHAPE ), TYPE_HASH( SCH_ITEM ),
  428. _HKI( "Position X" ), isPolygon );
  429. propMgr.OverrideAvailability( TYPE_HASH( SCH_SHAPE ), TYPE_HASH( SCH_ITEM ),
  430. _HKI( "Position Y" ), isPolygon );
  431. }
  432. } _SCH_SHAPE_DESC;