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.

568 lines
15 KiB

Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
18 years ago
18 years ago
18 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, jaen-pierre.charras@gipsa-lab.inpg.com
  5. * Copyright (C) 1992-2011 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. /**
  25. * @file base_struct.cpp
  26. * @brief Implementation of EDA_ITEM base class for KiCad.
  27. */
  28. #include <fctsys.h>
  29. #include <trigo.h>
  30. #include <common.h>
  31. #include <macros.h>
  32. #include <kicad_string.h>
  33. #include <wxstruct.h>
  34. #include <class_drawpanel.h>
  35. #include <class_base_screen.h>
  36. #include "../eeschema/dialogs/dialog_schematic_find.h"
  37. const wxString traceFindReplace( wxT( "KicadFindReplace" ) );
  38. enum textbox {
  39. ID_TEXTBOX_LIST = 8010
  40. };
  41. EDA_ITEM::EDA_ITEM( EDA_ITEM* parent, KICAD_T idType )
  42. {
  43. InitVars();
  44. m_StructType = idType;
  45. m_Parent = parent;
  46. }
  47. EDA_ITEM::EDA_ITEM( KICAD_T idType )
  48. {
  49. InitVars();
  50. m_StructType = idType;
  51. }
  52. EDA_ITEM::EDA_ITEM( const EDA_ITEM& base )
  53. {
  54. InitVars();
  55. m_StructType = base.m_StructType;
  56. m_Parent = base.m_Parent;
  57. m_Flags = base.m_Flags;
  58. // A copy of an item cannot have the same time stamp as the original item.
  59. SetTimeStamp( GetNewTimeStamp() );
  60. m_Status = base.m_Status;
  61. }
  62. void EDA_ITEM::InitVars()
  63. {
  64. m_StructType = TYPE_NOT_INIT;
  65. Pnext = NULL; // Linked list: Link (next struct)
  66. Pback = NULL; // Linked list: Link (previous struct)
  67. m_Parent = NULL; // Linked list: Link (parent struct)
  68. m_List = NULL; // I am not on any list yet
  69. m_Image = NULL; // Link to an image copy for undelete or abort command
  70. m_Flags = 0; // flags for editions and other
  71. SetTimeStamp( 0 ); // Time stamp used for logical links
  72. m_Status = 0;
  73. m_forceVisible = false; // true to override the visibility setting of the item.
  74. }
  75. void EDA_ITEM::SetModified()
  76. {
  77. SetFlags( IS_CHANGED );
  78. // If this a child object, then the parent modification state also needs to be set.
  79. if( m_Parent )
  80. m_Parent->SetModified();
  81. }
  82. EDA_ITEM* EDA_ITEM::Clone() const
  83. {
  84. wxCHECK_MSG( false, NULL, wxT( "Clone not implemented in derived class " ) + GetClass() +
  85. wxT( ". Bad programmer!" ) );
  86. }
  87. SEARCH_RESULT EDA_ITEM::IterateForward( EDA_ITEM* listStart,
  88. INSPECTOR* inspector,
  89. const void* testData,
  90. const KICAD_T scanTypes[] )
  91. {
  92. EDA_ITEM* p = listStart;
  93. for( ; p; p = p->Pnext )
  94. {
  95. if( SEARCH_QUIT == p->Visit( inspector, testData, scanTypes ) )
  96. return SEARCH_QUIT;
  97. }
  98. return SEARCH_CONTINUE;
  99. }
  100. // see base_struct.h
  101. // many classes inherit this method, be careful:
  102. SEARCH_RESULT EDA_ITEM::Visit( INSPECTOR* inspector, const void* testData,
  103. const KICAD_T scanTypes[] )
  104. {
  105. KICAD_T stype;
  106. #if 0 && defined(DEBUG)
  107. std::cout << GetClass().mb_str() << ' ';
  108. #endif
  109. for( const KICAD_T* p = scanTypes; (stype = *p) != EOT; ++p )
  110. {
  111. // If caller wants to inspect my type
  112. if( stype == Type() )
  113. {
  114. if( SEARCH_QUIT == inspector->Inspect( this, testData ) )
  115. return SEARCH_QUIT;
  116. break;
  117. }
  118. }
  119. return SEARCH_CONTINUE;
  120. }
  121. wxString EDA_ITEM::GetSelectMenuText() const
  122. {
  123. wxFAIL_MSG( wxT( "GetSelectMenuText() was not overridden for schematic item type " ) +
  124. GetClass() );
  125. return wxString( wxT( "Undefined menu text for " ) + GetClass() );
  126. }
  127. bool EDA_ITEM::Matches( const wxString& aText, wxFindReplaceData& aSearchData )
  128. {
  129. wxString text = aText;
  130. wxString searchText = aSearchData.GetFindString();
  131. // Don't match if searching for replaceable item and the item doesn't support text replace.
  132. if( (aSearchData.GetFlags() & FR_SEARCH_REPLACE) && !IsReplaceable() )
  133. return false;
  134. if( aSearchData.GetFlags() & wxFR_WHOLEWORD )
  135. return aText.IsSameAs( searchText, aSearchData.GetFlags() & wxFR_MATCHCASE );
  136. if( aSearchData.GetFlags() & FR_MATCH_WILDCARD )
  137. {
  138. if( aSearchData.GetFlags() & wxFR_MATCHCASE )
  139. return text.Matches( searchText );
  140. return text.MakeUpper().Matches( searchText.MakeUpper() );
  141. }
  142. if( aSearchData.GetFlags() & wxFR_MATCHCASE )
  143. return aText.Find( searchText ) != wxNOT_FOUND;
  144. return text.MakeUpper().Find( searchText.MakeUpper() ) != wxNOT_FOUND;
  145. }
  146. bool EDA_ITEM::Replace( wxFindReplaceData& aSearchData, wxString& aText )
  147. {
  148. wxCHECK_MSG( IsReplaceable(), false,
  149. wxT( "Attempt to replace text in <" ) + GetClass() + wxT( "> item." ) );
  150. wxString searchString = (aSearchData.GetFlags() & wxFR_MATCHCASE) ? aText : aText.Upper();
  151. int result = searchString.Find( (aSearchData.GetFlags() & wxFR_MATCHCASE) ?
  152. aSearchData.GetFindString() :
  153. aSearchData.GetFindString().Upper() );
  154. if( result == wxNOT_FOUND )
  155. return false;
  156. wxString prefix = aText.Left( result );
  157. wxString suffix;
  158. if( aSearchData.GetFindString().length() + result < aText.length() )
  159. suffix = aText.Right( aText.length() - ( aSearchData.GetFindString().length() + result ) );
  160. wxLogTrace( traceFindReplace, wxT( "Replacing '%s', prefix '%s', replace '%s', suffix '%s'." ),
  161. GetChars( aText ), GetChars( prefix ), GetChars( aSearchData.GetReplaceString() ),
  162. GetChars( suffix ) );
  163. aText = prefix + aSearchData.GetReplaceString() + suffix;
  164. return true;
  165. }
  166. bool EDA_ITEM::operator<( const EDA_ITEM& aItem ) const
  167. {
  168. wxFAIL_MSG( wxString::Format( wxT( "Less than operator not defined for item type %s." ),
  169. GetChars( GetClass() ) ) );
  170. return false;
  171. }
  172. EDA_ITEM& EDA_ITEM::operator=( const EDA_ITEM& aItem )
  173. {
  174. if( &aItem != this )
  175. {
  176. m_Image = aItem.m_Image;
  177. m_StructType = aItem.m_StructType;
  178. m_Parent = aItem.m_Parent;
  179. m_Flags = aItem.m_Flags;
  180. m_TimeStamp = aItem.m_TimeStamp;
  181. m_Status = aItem.m_Status;
  182. m_forceVisible = aItem.m_forceVisible;
  183. }
  184. return *this;
  185. }
  186. const BOX2I EDA_ITEM::ViewBBox() const
  187. {
  188. // Basic fallback
  189. return BOX2I( VECTOR2I( GetBoundingBox().GetOrigin() ),
  190. VECTOR2I( GetBoundingBox().GetSize() ) );
  191. }
  192. void EDA_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
  193. {
  194. // Basic fallback
  195. aCount = 1;
  196. aLayers[0] = 0;
  197. }
  198. #if defined(DEBUG)
  199. // A function that should have been in wxWidgets
  200. std::ostream& operator<<( std::ostream& out, const wxSize& size )
  201. {
  202. out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
  203. return out;
  204. }
  205. // A function that should have been in wxWidgets
  206. std::ostream& operator<<( std::ostream& out, const wxPoint& pt )
  207. {
  208. out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
  209. return out;
  210. }
  211. void EDA_ITEM::ShowDummy( std::ostream& os ) const
  212. {
  213. // XML output:
  214. wxString s = GetClass();
  215. os << '<' << s.Lower().mb_str() << ">"
  216. << " Need ::Show() override for this class "
  217. << "</" << s.Lower().mb_str() << ">\n";
  218. }
  219. std::ostream& EDA_ITEM::NestedSpace( int nestLevel, std::ostream& os )
  220. {
  221. for( int i = 0; i<nestLevel; ++i )
  222. os << " ";
  223. // number of spaces here controls indent per nest level
  224. return os;
  225. }
  226. #endif
  227. /******************/
  228. /* Class EDA_RECT */
  229. /******************/
  230. void EDA_RECT::Normalize()
  231. {
  232. if( m_Size.y < 0 )
  233. {
  234. m_Size.y = -m_Size.y;
  235. m_Pos.y -= m_Size.y;
  236. }
  237. if( m_Size.x < 0 )
  238. {
  239. m_Size.x = -m_Size.x;
  240. m_Pos.x -= m_Size.x;
  241. }
  242. }
  243. void EDA_RECT::Move( const wxPoint& aMoveVector )
  244. {
  245. m_Pos += aMoveVector;
  246. }
  247. bool EDA_RECT::Contains( const wxPoint& aPoint ) const
  248. {
  249. wxPoint rel_pos = aPoint - m_Pos;
  250. wxSize size = m_Size;
  251. if( size.x < 0 )
  252. {
  253. size.x = -size.x;
  254. rel_pos.x += size.x;
  255. }
  256. if( size.y < 0 )
  257. {
  258. size.y = -size.y;
  259. rel_pos.y += size.y;
  260. }
  261. return (rel_pos.x >= 0) && (rel_pos.y >= 0) && ( rel_pos.y <= size.y) && ( rel_pos.x <= size.x);
  262. }
  263. /*
  264. * return true if aRect is inside me (or on boundaries)
  265. */
  266. bool EDA_RECT::Contains( const EDA_RECT& aRect ) const
  267. {
  268. return Contains( aRect.GetOrigin() ) && Contains( aRect.GetEnd() );
  269. }
  270. /* Intersects
  271. * test for a common area between segment and rect.
  272. * return true if at least a common point is found
  273. */
  274. bool EDA_RECT::Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2 ) const
  275. {
  276. wxPoint point2, point4;
  277. if( Contains( aPoint1 ) || Contains( aPoint2 ) )
  278. return true;
  279. point2.x = GetEnd().x;
  280. point2.y = GetOrigin().y;
  281. point4.x = GetOrigin().x;
  282. point4.y = GetEnd().y;
  283. //Only need to test 3 sides since a straight line cant enter and exit on same side
  284. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetOrigin() , point2 ) )
  285. return true;
  286. if( SegmentIntersectsSegment( aPoint1, aPoint2, point2 , GetEnd() ) )
  287. return true;
  288. if( SegmentIntersectsSegment( aPoint1, aPoint2, GetEnd() , point4 ) )
  289. return true;
  290. return false;
  291. }
  292. /* Intersects
  293. * test for a common area between 2 rect.
  294. * return true if at least a common point is found
  295. */
  296. bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
  297. {
  298. // this logic taken from wxWidgets' geometry.cpp file:
  299. bool rc;
  300. EDA_RECT me(*this);
  301. EDA_RECT rect(aRect);
  302. me.Normalize(); // ensure size is >= 0
  303. rect.Normalize(); // ensure size is >= 0
  304. // calculate the left common area coordinate:
  305. int left = std::max( me.m_Pos.x, rect.m_Pos.x );
  306. // calculate the right common area coordinate:
  307. int right = std::min( me.m_Pos.x + me.m_Size.x, rect.m_Pos.x + rect.m_Size.x );
  308. // calculate the upper common area coordinate:
  309. int top = std::max( me.m_Pos.y, aRect.m_Pos.y );
  310. // calculate the lower common area coordinate:
  311. int bottom = std::min( me.m_Pos.y + me.m_Size.y, rect.m_Pos.y + rect.m_Size.y );
  312. // if a common area exists, it must have a positive (null accepted) size
  313. if( left <= right && top <= bottom )
  314. rc = true;
  315. else
  316. rc = false;
  317. return rc;
  318. }
  319. EDA_RECT& EDA_RECT::Inflate( int aDelta )
  320. {
  321. Inflate( aDelta, aDelta );
  322. return *this;
  323. }
  324. EDA_RECT& EDA_RECT::Inflate( wxCoord dx, wxCoord dy )
  325. {
  326. if( m_Size.x >= 0 )
  327. {
  328. if( m_Size.x < -2 * dx )
  329. {
  330. // Don't allow deflate to eat more width than we have,
  331. m_Pos.x += m_Size.x / 2;
  332. m_Size.x = 0;
  333. }
  334. else
  335. {
  336. // The inflate is valid.
  337. m_Pos.x -= dx;
  338. m_Size.x += 2 * dx;
  339. }
  340. }
  341. else // size.x < 0:
  342. {
  343. if( m_Size.x > -2 * dx )
  344. {
  345. // Don't allow deflate to eat more width than we have,
  346. m_Pos.x -= m_Size.x / 2;
  347. m_Size.x = 0;
  348. }
  349. else
  350. {
  351. // The inflate is valid.
  352. m_Pos.x += dx;
  353. m_Size.x -= 2 * dx; // m_Size.x <0: inflate when dx > 0
  354. }
  355. }
  356. if( m_Size.y >= 0 )
  357. {
  358. if( m_Size.y < -2 * dy )
  359. {
  360. // Don't allow deflate to eat more height than we have,
  361. m_Pos.y += m_Size.y / 2;
  362. m_Size.y = 0;
  363. }
  364. else
  365. {
  366. // The inflate is valid.
  367. m_Pos.y -= dy;
  368. m_Size.y += 2 * dy;
  369. }
  370. }
  371. else // size.y < 0:
  372. {
  373. if( m_Size.y > 2 * dy )
  374. {
  375. // Don't allow deflate to eat more height than we have,
  376. m_Pos.y -= m_Size.y / 2;
  377. m_Size.y = 0;
  378. }
  379. else
  380. {
  381. // The inflate is valid.
  382. m_Pos.y += dy;
  383. m_Size.y -= 2 * dy; // m_Size.y <0: inflate when dy > 0
  384. }
  385. }
  386. return *this;
  387. }
  388. void EDA_RECT::Merge( const EDA_RECT& aRect )
  389. {
  390. Normalize(); // ensure width and height >= 0
  391. EDA_RECT rect = aRect;
  392. rect.Normalize(); // ensure width and height >= 0
  393. wxPoint end = GetEnd();
  394. wxPoint rect_end = rect.GetEnd();
  395. // Change origin and size in order to contain the given rect
  396. m_Pos.x = std::min( m_Pos.x, rect.m_Pos.x );
  397. m_Pos.y = std::min( m_Pos.y, rect.m_Pos.y );
  398. end.x = std::max( end.x, rect_end.x );
  399. end.y = std::max( end.y, rect_end.y );
  400. SetEnd( end );
  401. }
  402. void EDA_RECT::Merge( const wxPoint& aPoint )
  403. {
  404. Normalize(); // ensure width and height >= 0
  405. wxPoint end = GetEnd();
  406. // Change origin and size in order to contain the given rect
  407. m_Pos.x = std::min( m_Pos.x, aPoint.x );
  408. m_Pos.y = std::min( m_Pos.y, aPoint.y );
  409. end.x = std::max( end.x, aPoint.x );
  410. end.y = std::max( end.y, aPoint.y );
  411. SetEnd( end );
  412. }
  413. double EDA_RECT::GetArea() const
  414. {
  415. return (double) GetWidth() * (double) GetHeight();
  416. }
  417. /* Calculate the bounding box of this, when rotated
  418. */
  419. const EDA_RECT EDA_RECT::GetBoundingBoxRotated( wxPoint aRotCenter, double aAngle )
  420. {
  421. wxPoint corners[4];
  422. // Build the corners list
  423. corners[0] = GetOrigin();
  424. corners[2] = GetEnd();
  425. corners[1].x = corners[0].x;
  426. corners[1].y = corners[2].y;
  427. corners[3].x = corners[2].x;
  428. corners[3].y = corners[0].y;
  429. // Rotate all corners, to find the bounding box
  430. for( int ii = 0; ii < 4; ii ++ )
  431. RotatePoint( &corners[ii], aRotCenter, aAngle );
  432. // Find the corners bounding box
  433. wxPoint start = corners[0];
  434. wxPoint end = corners[0];
  435. for( int ii = 1; ii < 4; ii ++ )
  436. {
  437. start.x = std::min( start.x, corners[ii].x);
  438. start.y = std::min( start.y, corners[ii].y);
  439. end.x = std::max( end.x, corners[ii].x);
  440. end.y = std::max( end.y, corners[ii].y);
  441. }
  442. EDA_RECT bbox;
  443. bbox.SetOrigin( start );
  444. bbox.SetEnd( end );
  445. return bbox;
  446. }