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.

436 lines
12 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
17 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
4 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 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) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * 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 <gr_basic.h>
  22. #include <trigo.h>
  23. #include <eda_item.h>
  24. #include <wx/graphics.h>
  25. #include <math/vector2wx.h>
  26. #include <algorithm>
  27. static const bool FILLED = true;
  28. static const bool NOT_FILLED = false;
  29. // For draw mode = XOR GR_XOR or GR_NXOR by background color
  30. GR_DRAWMODE g_XorMode = GR_NXOR;
  31. /* These functions are used by corresponding functions
  32. * ( GRSCircle is called by GRCircle for instance) after mapping coordinates
  33. * from user units to screen units(pixels coordinates)
  34. */
  35. static void GRSRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor );
  36. /**/
  37. static int GRLastMoveToX, GRLastMoveToY;
  38. static bool s_ForceBlackPen; /* if true: draws in black instead of
  39. * color for printing. */
  40. static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 );
  41. static bool s_DC_lastbrushfill = false;
  42. static wxDC* s_DC_lastDC = nullptr;
  43. static void vector2IwxDrawPolygon( wxDC* aDC, const VECTOR2I* Points, int n )
  44. {
  45. wxPoint* points = new wxPoint[n];
  46. for( int i = 0; i < n; i++ )
  47. points[i] = wxPoint( Points[i].x, Points[i].y );
  48. aDC->DrawPolygon( n, points );
  49. delete[] points;
  50. }
  51. static void winDrawLine( wxDC* DC, int x1, int y1, int x2, int y2, int width )
  52. {
  53. GRLastMoveToX = x2;
  54. GRLastMoveToY = y2;
  55. DC->DrawLine( x1, y1, x2, y2 );
  56. }
  57. void GRResetPenAndBrush( wxDC* DC )
  58. {
  59. GRSetBrush( DC, BLACK ); // Force no fill
  60. s_DC_lastbrushcolor = COLOR4D::UNSPECIFIED;
  61. s_DC_lastDC = nullptr;
  62. }
  63. void GRSetColorPen( wxDC* DC, const COLOR4D& Color, int width, wxPenStyle style )
  64. {
  65. COLOR4D color = Color;
  66. wxDash dots[2] = { 1, 3 };
  67. // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing
  68. // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not
  69. if( width <= 1 && DC->GetBrush().GetStyle() != wxBRUSHSTYLE_SOLID )
  70. width = DC->DeviceToLogicalXRel( 1 );
  71. if( s_ForceBlackPen )
  72. color = COLOR4D::BLACK;
  73. // wxWidgets will enforce a minimum pen width when printing, so we have to make the pen
  74. // transparent when we don't want the object stroked.
  75. if( width == 0 )
  76. {
  77. color = COLOR4D::UNSPECIFIED;
  78. style = wxPENSTYLE_TRANSPARENT;
  79. }
  80. const wxPen& curr_pen = DC->GetPen();
  81. if( !curr_pen.IsOk() || curr_pen.GetColour() != color.ToColour()
  82. || curr_pen.GetWidth() != width || curr_pen.GetStyle() != style )
  83. {
  84. wxPen pen;
  85. pen.SetColour( color.ToColour() );
  86. if( style == wxPENSTYLE_DOT )
  87. {
  88. style = wxPENSTYLE_USER_DASH;
  89. pen.SetDashes( 2, dots );
  90. }
  91. pen.SetWidth( width );
  92. pen.SetStyle( style );
  93. DC->SetPen( pen );
  94. }
  95. else
  96. {
  97. // Should be not needed, but on Linux, in printing process
  98. // the curr pen settings needs to be sometimes re-initialized
  99. // Clearly, this is due to a bug, related to SetBrush(),
  100. // but we have to live with it, at least on wxWidgets 3.0
  101. DC->SetPen( curr_pen );
  102. }
  103. }
  104. void GRSetBrush( wxDC* DC, const COLOR4D& Color, bool fill )
  105. {
  106. COLOR4D color = Color;
  107. if( s_ForceBlackPen )
  108. color = COLOR4D::BLACK;
  109. if( s_DC_lastbrushcolor != color || s_DC_lastbrushfill != fill || s_DC_lastDC != DC )
  110. {
  111. wxBrush brush;
  112. brush.SetColour( color.ToColour() );
  113. if( fill )
  114. brush.SetStyle( wxBRUSHSTYLE_SOLID );
  115. else
  116. brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT );
  117. DC->SetBrush( brush );
  118. s_DC_lastbrushcolor = color;
  119. s_DC_lastbrushfill = fill;
  120. s_DC_lastDC = DC;
  121. }
  122. }
  123. void GRForceBlackPen( bool flagforce )
  124. {
  125. s_ForceBlackPen = flagforce;
  126. }
  127. bool GetGRForceBlackPenState( void )
  128. {
  129. return s_ForceBlackPen;
  130. }
  131. void GRLine( wxDC* DC, int x1, int y1, int x2, int y2, int width, const COLOR4D& Color,
  132. wxPenStyle aStyle)
  133. {
  134. GRSetColorPen( DC, Color, width, aStyle );
  135. winDrawLine( DC, x1, y1, x2, y2, width );
  136. GRLastMoveToX = x2;
  137. GRLastMoveToY = y2;
  138. }
  139. void GRLine( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
  140. const COLOR4D& aColor, wxPenStyle aStyle )
  141. {
  142. GRLine( aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aStyle );
  143. }
  144. void GRMoveTo( int x, int y )
  145. {
  146. GRLastMoveToX = x;
  147. GRLastMoveToY = y;
  148. }
  149. void GRLineTo( wxDC* DC, int x, int y, int width, const COLOR4D& Color )
  150. {
  151. GRLine( DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color );
  152. }
  153. void GRCSegm( wxDC* DC, const VECTOR2I& A, const VECTOR2I& B, int width, const COLOR4D& Color )
  154. {
  155. GRLastMoveToX = B.x;
  156. GRLastMoveToY = B.y;
  157. if( width <= 2 ) /* single line or 2 pixels */
  158. {
  159. GRSetColorPen( DC, Color, width );
  160. DC->DrawLine( A.x, A.y, B.x, B.y );
  161. return;
  162. }
  163. GRSetBrush( DC, Color, NOT_FILLED );
  164. GRSetColorPen( DC, Color, 0 );
  165. int radius = ( width + 1 ) >> 1;
  166. int dx = B.x - A.x;
  167. int dy = B.y - A.y;
  168. EDA_ANGLE angle( VECTOR2I( dx, dy ) );
  169. angle = -angle;
  170. VECTOR2I start;
  171. VECTOR2I end;
  172. VECTOR2I org( A.x, A.y );
  173. int len = (int) hypot( dx, dy );
  174. // We know if the DC is mirrored, to draw arcs
  175. int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 );
  176. int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 );
  177. bool mirrored = ( slx > 0 && sly < 0 ) || ( slx < 0 && sly > 0 );
  178. // first edge
  179. start.x = 0;
  180. start.y = radius;
  181. end.x = len;
  182. end.y = radius;
  183. RotatePoint( start, angle );
  184. RotatePoint( end, angle );
  185. start += org;
  186. end += org;
  187. DC->DrawLine( ToWxPoint( start ), ToWxPoint( end ) );
  188. // first rounded end
  189. end.x = 0;
  190. end.y = -radius;
  191. RotatePoint( end, angle );
  192. end += org;
  193. if( !mirrored )
  194. DC->DrawArc( ToWxPoint(end ), ToWxPoint(start ), ToWxPoint(org ) );
  195. else
  196. DC->DrawArc( ToWxPoint(start ), ToWxPoint(end ), ToWxPoint(org ) );
  197. // second edge
  198. start.x = len;
  199. start.y = -radius;
  200. RotatePoint( start, angle );
  201. start += org;
  202. DC->DrawLine( ToWxPoint( start ), ToWxPoint( end ) );
  203. // second rounded end
  204. end.x = len;
  205. end.y = radius;
  206. RotatePoint( end, angle);
  207. end += org;
  208. if( !mirrored )
  209. DC->DrawArc( end.x, end.y, start.x, start.y, B.x, B.y );
  210. else
  211. DC->DrawArc( start.x, start.y, end.x, end.y, B.x, B.y );
  212. }
  213. void GRFilledSegment( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
  214. const COLOR4D& aColor )
  215. {
  216. GRSetColorPen( aDC, aColor, aWidth );
  217. winDrawLine( aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth );
  218. }
  219. /**
  220. * Draw a new polyline and fill it if Fill, in screen space.
  221. */
  222. static void GRSPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, int width,
  223. const COLOR4D& Color, const COLOR4D& BgColor )
  224. {
  225. if( Fill && ( n > 2 ) )
  226. {
  227. GRSetBrush( DC, BgColor, FILLED );
  228. GRSetColorPen( DC, Color, width );
  229. vector2IwxDrawPolygon( DC, Points, n );
  230. }
  231. else
  232. {
  233. GRMoveTo( Points[0].x, Points[0].y );
  234. for( int i = 1; i < n; ++i )
  235. GRLineTo( DC, Points[i].x, Points[i].y, width, Color );
  236. }
  237. }
  238. /**
  239. * Draw a new closed polyline and fill it if Fill, in screen space.
  240. */
  241. static void GRSClosedPoly( wxDC* aDC, int aPointCount, const VECTOR2I* aPoints, bool aFill,
  242. int aWidth, const COLOR4D& aColor, const COLOR4D& aBgColor )
  243. {
  244. if( aFill && ( aPointCount > 2 ) )
  245. {
  246. GRLastMoveToX = aPoints[aPointCount - 1].x;
  247. GRLastMoveToY = aPoints[aPointCount - 1].y;
  248. GRSetBrush( aDC, aBgColor, FILLED );
  249. GRSetColorPen( aDC, aColor, aWidth );
  250. vector2IwxDrawPolygon( aDC, aPoints, aPointCount );
  251. }
  252. else
  253. {
  254. GRMoveTo( aPoints[0].x, aPoints[0].y );
  255. for( int i = 1; i < aPointCount; ++i )
  256. GRLineTo( aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor );
  257. int lastpt = aPointCount - 1;
  258. // Close the polygon
  259. if( aPoints[lastpt] != aPoints[0] )
  260. GRLineTo( aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor );
  261. }
  262. }
  263. /**
  264. * Draw a new polyline and fill it if Fill, in drawing space.
  265. */
  266. void GRPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, int width, const COLOR4D& Color,
  267. const COLOR4D& BgColor )
  268. {
  269. GRSPoly( DC, n, Points, Fill, width, Color, BgColor );
  270. }
  271. /**
  272. * Draw a closed polyline and fill it if Fill, in object space.
  273. */
  274. void GRClosedPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, const COLOR4D& Color )
  275. {
  276. GRSClosedPoly( DC, n, Points, Fill, 0, Color, Color );
  277. }
  278. void GRCircle( wxDC* aDC, const VECTOR2I& aPos, int aRadius, int aWidth, const COLOR4D& aColor )
  279. {
  280. GRSetBrush( aDC, aColor, NOT_FILLED );
  281. GRSetColorPen( aDC, aColor, aWidth );
  282. // Draw two arcs here to make a circle. Unfortunately, the printerDC doesn't handle
  283. // transparent brushes when used with circles. It does work for for arcs, however
  284. aDC->DrawArc(aPos.x + aRadius, aPos.y, aPos.x - aRadius, aPos.y, aPos.x, aPos.y );
  285. aDC->DrawArc(aPos.x - aRadius, aPos.y, aPos.x + aRadius, aPos.y, aPos.x, aPos.y );
  286. }
  287. void GRFilledCircle( wxDC* aDC, const VECTOR2I& aPos, int aRadius, int aWidth,
  288. const COLOR4D& aStrokeColor, const COLOR4D& aFillColor )
  289. {
  290. GRSetBrush( aDC, aFillColor, FILLED );
  291. GRSetColorPen( aDC, aStrokeColor, aWidth );
  292. aDC->DrawEllipse( aPos.x - aRadius, aPos.y - aRadius, 2 * aRadius, 2 * aRadius );
  293. }
  294. void GRArc( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
  295. int aWidth, const COLOR4D& aColor )
  296. {
  297. GRSetBrush( aDC, aColor );
  298. GRSetColorPen( aDC, aColor, aWidth );
  299. aDC->DrawArc( aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y );
  300. }
  301. void GRFilledArc( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
  302. int width, const COLOR4D& Color, const COLOR4D& BgColor )
  303. {
  304. GRSetBrush( DC, BgColor, FILLED );
  305. GRSetColorPen( DC, Color, width );
  306. DC->DrawArc( aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y );
  307. }
  308. void GRRect( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
  309. const COLOR4D& aColor )
  310. {
  311. GRSRect( DC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor );
  312. }
  313. void GRFilledRect( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
  314. const COLOR4D& aColor, const COLOR4D& aBgColor )
  315. {
  316. GRSFilledRect( DC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aBgColor );
  317. }
  318. void GRSRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor )
  319. {
  320. VECTOR2I points[5];
  321. points[0] = VECTOR2I( x1, y1 );
  322. points[1] = VECTOR2I( x1, y2 );
  323. points[2] = VECTOR2I( x2, y2 );
  324. points[3] = VECTOR2I( x2, y1 );
  325. points[4] = points[0];
  326. GRSClosedPoly( aDC, 5, points, NOT_FILLED, aWidth, aColor, aColor );
  327. }
  328. void GRSFilledRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor,
  329. const COLOR4D& aBgColor )
  330. {
  331. VECTOR2I points[5];
  332. points[0] = VECTOR2I( x1, y1 );
  333. points[1] = VECTOR2I( x1, y2 );
  334. points[2] = VECTOR2I( x2, y2 );
  335. points[3] = VECTOR2I( x2, y1 );
  336. points[4] = points[0];
  337. GRSetBrush( aDC, aBgColor, FILLED );
  338. GRSetColorPen( aDC, aBgColor, aWidth );
  339. vector2IwxDrawPolygon( aDC, points, 5 );
  340. }