KiCad PCB EDA Suite
gr_basic.cpp
Go to the documentation of this file.
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 <[email protected]>
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
22#include <gr_basic.h>
23#include <trigo.h>
24#include <eda_item.h>
25#include <wx/graphics.h>
26
27#include <algorithm>
28
29static const bool FILLED = true;
30static const bool NOT_FILLED = false;
31
32// For draw mode = XOR GR_XOR or GR_NXOR by background color
34
35
36/* These functions are used by corresponding functions
37 * ( GRSCircle is called by GRCircle for instance) after mapping coordinates
38 * from user units to screen units(pixels coordinates)
39 */
40static void GRSRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor );
41
42
43
45static bool s_ForceBlackPen; /* if true: draws in black instead of
46 * color for printing. */
47static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 );
48static bool s_DC_lastbrushfill = false;
49static wxDC* s_DC_lastDC = nullptr;
50
51
52static void vector2IwxDrawPolygon( wxDC* aDC, const VECTOR2I* Points, int n )
53{
54 wxPoint* points = new wxPoint[n];
55
56 for( int i = 0; i < n; i++ )
57 points[i] = wxPoint( Points[i].x, Points[i].y );
58
59 aDC->DrawPolygon( n, points );
60 delete[] points;
61}
62
63
64static void winDrawLine( wxDC* DC, int x1, int y1, int x2, int y2, int width )
65{
66 GRLastMoveToX = x2;
67 GRLastMoveToY = y2;
68 DC->DrawLine( x1, y1, x2, y2 );
69}
70
71
72void GRResetPenAndBrush( wxDC* DC )
73{
74 GRSetBrush( DC, BLACK ); // Force no fill
75 s_DC_lastbrushcolor = COLOR4D::UNSPECIFIED;
76 s_DC_lastDC = nullptr;
77}
78
79
80void GRSetColorPen( wxDC* DC, const COLOR4D& Color, int width, wxPenStyle style )
81{
82 COLOR4D color = Color;
83
84 wxDash dots[2] = { 1, 3 };
85
86 // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing
87 // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not
88 if( width <= 1 && DC->GetBrush().GetStyle() != wxBRUSHSTYLE_SOLID )
89 width = DC->DeviceToLogicalXRel( 1 );
90
91 if( s_ForceBlackPen )
93
94 const wxPen& curr_pen = DC->GetPen();
95
96 if( !curr_pen.IsOk() || curr_pen.GetColour() != color.ToColour()
97 || curr_pen.GetWidth() != width || curr_pen.GetStyle() != style )
98 {
99 wxPen pen;
100 pen.SetColour( color.ToColour() );
101
102 if( style == wxPENSTYLE_DOT )
103 {
104 style = wxPENSTYLE_USER_DASH;
105 pen.SetDashes( 2, dots );
106 }
107
108 pen.SetWidth( width );
109 pen.SetStyle( style );
110 DC->SetPen( pen );
111 }
112 else
113 {
114 // Should be not needed, but on Linux, in printing process
115 // the curr pen settings needs to be sometimes re-initialized
116 // Clearly, this is due to a bug, related to SetBrush(),
117 // but we have to live with it, at least on wxWidgets 3.0
118 DC->SetPen( curr_pen );
119 }
120}
121
122
123void GRSetBrush( wxDC* DC, const COLOR4D& Color, bool fill )
124{
125 COLOR4D color = Color;
126
127 if( s_ForceBlackPen )
129
130 if( s_DC_lastbrushcolor != color || s_DC_lastbrushfill != fill || s_DC_lastDC != DC )
131 {
132 wxBrush brush;
133
134 brush.SetColour( color.ToColour() );
135
136 if( fill )
137 brush.SetStyle( wxBRUSHSTYLE_SOLID );
138 else
139 brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT );
140
141 DC->SetBrush( brush );
142
144 s_DC_lastbrushfill = fill;
145 s_DC_lastDC = DC;
146 }
147}
148
149
150void GRForceBlackPen( bool flagforce )
151{
152 s_ForceBlackPen = flagforce;
153}
154
155
157{
158 return s_ForceBlackPen;
159}
160
161
162void GRLine( wxDC* DC, int x1, int y1, int x2, int y2, int width, const COLOR4D& Color,
163 wxPenStyle aStyle)
164{
165 GRSetColorPen( DC, Color, width, aStyle );
166 winDrawLine( DC, x1, y1, x2, y2, width );
167 GRLastMoveToX = x2;
168 GRLastMoveToY = y2;
169}
170
171
172void GRLine( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
173 const COLOR4D& aColor, wxPenStyle aStyle )
174{
175 GRLine( aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aStyle );
176}
177
178
179void GRMoveTo( int x, int y )
180{
181 GRLastMoveToX = x;
182 GRLastMoveToY = y;
183}
184
185
186void GRLineTo( wxDC* DC, int x, int y, int width, const COLOR4D& Color )
187{
188 GRLine( DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color );
189}
190
191
192void GRCSegm( wxDC* DC, const VECTOR2I& A, const VECTOR2I& B, int width, const COLOR4D& Color )
193{
194 GRLastMoveToX = B.x;
195 GRLastMoveToY = B.y;
196
197 if( width <= 2 ) /* single line or 2 pixels */
198 {
199 GRSetColorPen( DC, Color, width );
200 DC->DrawLine( A.x, A.y, B.x, B.y );
201 return;
202 }
203
204 GRSetBrush( DC, Color, NOT_FILLED );
205 GRSetColorPen( DC, Color, 0 );
206
207 int radius = ( width + 1 ) >> 1;
208 int dx = B.x - A.x;
209 int dy = B.y - A.y;
210 EDA_ANGLE angle( VECTOR2I( dx, dy ) );
211
212 angle = -angle;
213
214 VECTOR2I start;
215 VECTOR2I end;
216 VECTOR2I org( A.x, A.y );
217 int len = (int) hypot( dx, dy );
218
219 // We know if the DC is mirrored, to draw arcs
220 int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 );
221 int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 );
222 bool mirrored = ( slx > 0 && sly < 0 ) || ( slx < 0 && sly > 0 );
223
224 // first edge
225 start.x = 0;
226 start.y = radius;
227 end.x = len;
228 end.y = radius;
229 RotatePoint( start, angle );
230 RotatePoint( end, angle );
231
232 start += org;
233 end += org;
234
235 DC->DrawLine( (wxPoint) start, (wxPoint) end );
236
237 // first rounded end
238 end.x = 0;
239 end.y = -radius;
240 RotatePoint( end, angle );
241 end += org;
242
243 if( !mirrored )
244 DC->DrawArc( (wxPoint) end, (wxPoint) start, (wxPoint) org );
245 else
246 DC->DrawArc( (wxPoint) start, (wxPoint) end, (wxPoint) org );
247
248 // second edge
249 start.x = len;
250 start.y = -radius;
251 RotatePoint( start, angle );
252 start += org;
253
254 DC->DrawLine( (wxPoint) start, (wxPoint) end );
255
256 // second rounded end
257 end.x = len;
258 end.y = radius;
259 RotatePoint( end, angle);
260 end += org;
261
262 if( !mirrored )
263 DC->DrawArc( end.x, end.y, start.x, start.y, B.x, B.y );
264 else
265 DC->DrawArc( start.x, start.y, end.x, end.y, B.x, B.y );
266}
267
268
269void GRFilledSegment( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
270 const COLOR4D& aColor )
271{
272 GRSetColorPen( aDC, aColor, aWidth );
273 winDrawLine( aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth );
274}
275
279static void GRSPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, int width,
280 const COLOR4D& Color, const COLOR4D& BgColor )
281{
282 if( Fill && ( n > 2 ) )
283 {
284 GRSetBrush( DC, BgColor, FILLED );
285 GRSetColorPen( DC, Color, width );
286
287 vector2IwxDrawPolygon( DC, Points, n );
288 }
289 else
290 {
291 GRMoveTo( Points[0].x, Points[0].y );
292
293 for( int i = 1; i < n; ++i )
294 GRLineTo( DC, Points[i].x, Points[i].y, width, Color );
295 }
296}
297
298
302static void GRSClosedPoly( wxDC* aDC, int aPointCount, const VECTOR2I* aPoints, bool aFill,
303 int aWidth, const COLOR4D& aColor, const COLOR4D& aBgColor )
304{
305 if( aFill && ( aPointCount > 2 ) )
306 {
307 GRLastMoveToX = aPoints[aPointCount - 1].x;
308 GRLastMoveToY = aPoints[aPointCount - 1].y;
309 GRSetBrush( aDC, aBgColor, FILLED );
310 GRSetColorPen( aDC, aColor, aWidth );
311 vector2IwxDrawPolygon( aDC, aPoints, aPointCount );
312 }
313 else
314 {
315 GRMoveTo( aPoints[0].x, aPoints[0].y );
316
317 for( int i = 1; i < aPointCount; ++i )
318 GRLineTo( aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor );
319
320 int lastpt = aPointCount - 1;
321
322 // Close the polygon
323 if( aPoints[lastpt] != aPoints[0] )
324 GRLineTo( aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor );
325 }
326}
327
328
332void GRPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, int width, const COLOR4D& Color,
333 const COLOR4D& BgColor )
334{
335 GRSPoly( DC, n, Points, Fill, width, Color, BgColor );
336}
337
338
342void GRClosedPoly( wxDC* DC, int n, const VECTOR2I* Points, bool Fill, const COLOR4D& Color )
343{
344 GRSClosedPoly( DC, n, Points, Fill, 0, Color, Color );
345}
346
347
348void GRCircle( wxDC* aDC, const VECTOR2I& aPos, int aRadius, int aWidth, const COLOR4D& aColor )
349{
350 GRSetBrush( aDC, aColor, NOT_FILLED );
351 GRSetColorPen( aDC, aColor, aWidth );
352
353 // Draw two arcs here to make a circle. Unfortunately, the printerDC doesn't handle
354 // transparent brushes when used with circles. It does work for for arcs, however
355 aDC->DrawArc(aPos.x + aRadius, aPos.y, aPos.x - aRadius, aPos.y, aPos.x, aPos.y );
356 aDC->DrawArc(aPos.x - aRadius, aPos.y, aPos.x + aRadius, aPos.y, aPos.x, aPos.y );
357}
358
359
360void GRFilledCircle( wxDC* aDC, const VECTOR2I& aPos, int aRadius, int aWidth,
361 const COLOR4D& aStrokeColor, const COLOR4D& aFillColor )
362{
363 GRSetBrush( aDC, aFillColor, FILLED );
364 GRSetColorPen( aDC, aStrokeColor, aWidth );
365 aDC->DrawEllipse( aPos.x - aRadius, aPos.y - aRadius, 2 * aRadius, 2 * aRadius );
366}
367
368
369void GRArc( wxDC* aDC, const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
370 int aWidth, const COLOR4D& aColor )
371{
372 GRSetBrush( aDC, aColor );
373 GRSetColorPen( aDC, aColor, aWidth );
374 aDC->DrawArc( aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y );
375}
376
377
378void GRFilledArc( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
379 int width, const COLOR4D& Color, const COLOR4D& BgColor )
380{
381 GRSetBrush( DC, BgColor, FILLED );
382 GRSetColorPen( DC, Color, width );
383 DC->DrawArc( aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y );
384}
385
386
387void GRRect( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
388 const COLOR4D& aColor )
389{
390 GRSRect( DC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor );
391}
392
393
394void GRFilledRect( wxDC* DC, const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth,
395 const COLOR4D& aColor, const COLOR4D& aBgColor )
396{
397 GRSFilledRect( DC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aBgColor );
398}
399
400
401void GRSRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor )
402{
403 VECTOR2I points[5];
404 points[0] = VECTOR2I( x1, y1 );
405 points[1] = VECTOR2I( x1, y2 );
406 points[2] = VECTOR2I( x2, y2 );
407 points[3] = VECTOR2I( x2, y1 );
408 points[4] = points[0];
409 GRSClosedPoly( aDC, 5, points, NOT_FILLED, aWidth, aColor, aColor );
410}
411
412
413void GRSFilledRect( wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D& aColor,
414 const COLOR4D& aBgColor )
415{
416 VECTOR2I points[5];
417 points[0] = VECTOR2I( x1, y1 );
418 points[1] = VECTOR2I( x1, y2 );
419 points[2] = VECTOR2I( x2, y2 );
420 points[3] = VECTOR2I( x2, y1 );
421 points[4] = points[0];
422
423 GRSetBrush( aDC, aBgColor, FILLED );
424 GRSetColorPen( aDC, aBgColor, aWidth );
425
426 vector2IwxDrawPolygon( aDC, points, 5 );
427}
int color
Definition: DXF_plotter.cpp:57
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
@ BLACK
Definition: color4d.h:44
void GRForceBlackPen(bool flagforce)
Definition: gr_basic.cpp:150
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:72
void GRLineTo(wxDC *DC, int x, int y, int width, const COLOR4D &Color)
Definition: gr_basic.cpp:186
static int GRLastMoveToY
Definition: gr_basic.cpp:44
static void GRSRect(wxDC *aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D &aColor)
Definition: gr_basic.cpp:401
void GRRect(wxDC *DC, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, const COLOR4D &aColor)
Definition: gr_basic.cpp:387
void GRCSegm(wxDC *DC, const VECTOR2I &A, const VECTOR2I &B, int width, const COLOR4D &Color)
Definition: gr_basic.cpp:192
static void winDrawLine(wxDC *DC, int x1, int y1, int x2, int y2, int width)
Definition: gr_basic.cpp:64
static bool s_ForceBlackPen
Definition: gr_basic.cpp:45
static void vector2IwxDrawPolygon(wxDC *aDC, const VECTOR2I *Points, int n)
Definition: gr_basic.cpp:52
static int GRLastMoveToX
Definition: gr_basic.cpp:44
static const bool NOT_FILLED
Definition: gr_basic.cpp:30
static bool s_DC_lastbrushfill
Definition: gr_basic.cpp:48
void GRCircle(wxDC *aDC, const VECTOR2I &aPos, int aRadius, int aWidth, const COLOR4D &aColor)
Definition: gr_basic.cpp:348
void GRFilledSegment(wxDC *aDC, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, const COLOR4D &aColor)
Definition: gr_basic.cpp:269
void GRFilledArc(wxDC *DC, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, int width, const COLOR4D &Color, const COLOR4D &BgColor)
Definition: gr_basic.cpp:378
void GRSetColorPen(wxDC *DC, const COLOR4D &Color, int width, wxPenStyle style)
Definition: gr_basic.cpp:80
void GRSetBrush(wxDC *DC, const COLOR4D &Color, bool fill)
Definition: gr_basic.cpp:123
static void GRSPoly(wxDC *DC, int n, const VECTOR2I *Points, bool Fill, int width, const COLOR4D &Color, const COLOR4D &BgColor)
Draw a new polyline and fill it if Fill, in screen space.
Definition: gr_basic.cpp:279
void GRMoveTo(int x, int y)
Definition: gr_basic.cpp:179
static void GRSClosedPoly(wxDC *aDC, int aPointCount, const VECTOR2I *aPoints, bool aFill, int aWidth, const COLOR4D &aColor, const COLOR4D &aBgColor)
Draw a new closed polyline and fill it if Fill, in screen space.
Definition: gr_basic.cpp:302
void GRLine(wxDC *DC, int x1, int y1, int x2, int y2, int width, const COLOR4D &Color, wxPenStyle aStyle)
Definition: gr_basic.cpp:162
static COLOR4D s_DC_lastbrushcolor(0, 0, 0, 0)
static const bool FILLED
Definition: gr_basic.cpp:29
GR_DRAWMODE g_XorMode
Definition: gr_basic.cpp:33
void GRSFilledRect(wxDC *aDC, int x1, int y1, int x2, int y2, int aWidth, const COLOR4D &aColor, const COLOR4D &aBgColor)
Definition: gr_basic.cpp:413
void GRClosedPoly(wxDC *DC, int n, const VECTOR2I *Points, bool Fill, const COLOR4D &Color)
Draw a closed polyline and fill it if Fill, in object space.
Definition: gr_basic.cpp:342
static wxDC * s_DC_lastDC
Definition: gr_basic.cpp:49
void GRFilledRect(wxDC *DC, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, const COLOR4D &aColor, const COLOR4D &aBgColor)
Definition: gr_basic.cpp:394
void GRArc(wxDC *aDC, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, int aWidth, const COLOR4D &aColor)
Definition: gr_basic.cpp:369
void GRPoly(wxDC *DC, int n, const VECTOR2I *Points, bool Fill, int width, const COLOR4D &Color, const COLOR4D &BgColor)
Draw a new polyline and fill it if Fill, in drawing space.
Definition: gr_basic.cpp:332
bool GetGRForceBlackPenState(void)
Definition: gr_basic.cpp:156
void GRFilledCircle(wxDC *aDC, const VECTOR2I &aPos, int aRadius, int aWidth, const COLOR4D &aStrokeColor, const COLOR4D &aFillColor)
Draw a circle onto the drawing context aDC centered at the user coordinates (x,y).
Definition: gr_basic.cpp:360
GR_DRAWMODE
Drawmode. Compositing mode plus a flag or two.
Definition: gr_basic.h:35
@ GR_NXOR
Definition: gr_basic.h:39
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618