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-2021 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 <math_for_graphics.h>
26 #include <wx/graphics.h>
27 #include <geometry/geometry_utils.h>
28 #include <math/util.h> // for KiROUND
29
30 #include <algorithm>
31
32 static const bool FILLED = true;
33 static const bool NOT_FILLED = false;
34
35 /* Important Note:
36 * These drawing functions clip draw item before send these items to wxDC draw
37 * functions. For guy who asks why i did it, see a sample of problems encountered
38 * when pixels
39 * coordinates overflow 16 bits values:
40 * http://trac.wxwidgets.org/ticket/10446
41 * Problems can be found under Windows **and** Linux (mainly when drawing arcs)
42 * (mainly at low zoom values (2, 1 or 0.5), in Pcbnew)
43 * some of these problems could be now fixed in recent distributions.
44 *
45 * Currently (feb 2009) there are overflow problems when drawing solid (filled)
46 * polygons under linux without clipping
47 *
48 * So before removing clipping functions, be aware these bug (they are not in
49 * KiCad or wxWidgets) are fixed by testing how are drawn complex lines arcs
50 * and solid polygons under Windows and Linux and remember users can have old
51 * versions with bugs
52 */
53
54
55 /* Definitions for enabling and disabling debugging features in gr_basic.cpp.
56 * Please remember to set these back to 0 before making LAUNCHPAD commits.
57 */
58 #define DEBUG_DUMP_CLIP_ERROR_COORDS 0 // Set to 1 to dump clip algorithm errors.
59 #define DEBUG_DUMP_CLIP_COORDS 0 // Set to 1 to dump clipped coordinates.
60
61
62 // For draw mode = XOR GR_XOR or GR_NXOR by background color
63 GR_DRAWMODE g_XorMode = GR_NXOR;
64
65
66 static void ClipAndDrawPoly( EDA_RECT* ClipBox, wxDC* DC, const wxPoint* Points, int n );
67
68 /* These functions are used by corresponding functions
69 * ( GRSCircle is called by GRCircle for instance) after mapping coordinates
70 * from user units to screen units(pixels coordinates)
71 */
72 static void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1,
73 int x2, int y2, int aWidth, const COLOR4D& aColor );
74
75 /**/
76
77 static int GRLastMoveToX, GRLastMoveToY;
78 static bool s_ForceBlackPen; /* if true: draws in black instead of
79 * color for printing. */
80 static int xcliplo = 0,
81 ycliplo = 0,
82 xcliphi = 2000,
83 ycliphi = 2000;
84
85 static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 );
86 static bool s_DC_lastbrushfill = false;
87 static wxDC* s_DC_lastDC = nullptr;
88
WinClipAndDrawLine(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width)89 static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
90 int width )
91 {
92 GRLastMoveToX = x2;
93 GRLastMoveToY = y2;
94
95 if( ClipBox )
96 {
97 EDA_RECT clipbox(*ClipBox);
98 clipbox.Inflate(width/2);
99 if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
100 return;
101 }
102
103 DC->DrawLine( x1, y1, x2, y2 );
104 }
105
106
GRResetPenAndBrush(wxDC * DC)107 void GRResetPenAndBrush( wxDC* DC )
108 {
109 GRSetBrush( DC, BLACK ); // Force no fill
110 s_DC_lastbrushcolor = COLOR4D::UNSPECIFIED;
111 s_DC_lastDC = nullptr;
112 }
113
114
GRSetColorPen(wxDC * DC,const COLOR4D & Color,int width,wxPenStyle style)115 void GRSetColorPen( wxDC* DC, const COLOR4D& Color, int width, wxPenStyle style )
116 {
117 COLOR4D color = Color;
118
119 wxDash dots[2] = { 1, 3 };
120
121 // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing
122 // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not
123 if( width <= 1 )
124 width = DC->DeviceToLogicalXRel( 1 );
125
126 if( s_ForceBlackPen )
127 color = COLOR4D::BLACK;
128
129 const wxPen& curr_pen = DC->GetPen();
130
131 if( !curr_pen.IsOk() || curr_pen.GetColour() != color.ToColour()
132 || curr_pen.GetWidth() != width || curr_pen.GetStyle() != style )
133 {
134 wxPen pen;
135 pen.SetColour( color.ToColour() );
136
137 if( style == wxPENSTYLE_DOT )
138 {
139 style = wxPENSTYLE_USER_DASH;
140 pen.SetDashes( 2, dots );
141 }
142
143 pen.SetWidth( width );
144 pen.SetStyle( style );
145 DC->SetPen( pen );
146 }
147 else
148 {
149 // Should be not needed, but on Linux, in printing process
150 // the curr pen settings needs to be sometimes re-initialized
151 // Clearly, this is due to a bug, related to SetBrush(),
152 // but we have to live with it, at least on wxWidgets 3.0
153 DC->SetPen( curr_pen );
154 }
155 }
156
157
GRSetBrush(wxDC * DC,const COLOR4D & Color,bool fill)158 void GRSetBrush( wxDC* DC, const COLOR4D& Color, bool fill )
159 {
160 COLOR4D color = Color;
161
162 if( s_ForceBlackPen )
163 color = COLOR4D::BLACK;
164
165 if( s_DC_lastbrushcolor != color || s_DC_lastbrushfill != fill || s_DC_lastDC != DC )
166 {
167 wxBrush brush;
168
169 brush.SetColour( color.ToColour() );
170
171 if( fill )
172 brush.SetStyle( wxBRUSHSTYLE_SOLID );
173 else
174 brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT );
175
176 DC->SetBrush( brush );
177
178 s_DC_lastbrushcolor = color;
179 s_DC_lastbrushfill = fill;
180 s_DC_lastDC = DC;
181 }
182 }
183
184
GRForceBlackPen(bool flagforce)185 void GRForceBlackPen( bool flagforce )
186 {
187 s_ForceBlackPen = flagforce;
188 }
189
190
GetGRForceBlackPenState(void)191 bool GetGRForceBlackPenState( void )
192 {
193 return s_ForceBlackPen;
194 }
195
196
GRLine(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,const COLOR4D & Color,wxPenStyle aStyle)197 void GRLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
198 const COLOR4D& Color, wxPenStyle aStyle)
199 {
200 GRSetColorPen( DC, Color, width, aStyle );
201 WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width );
202 GRLastMoveToX = x2;
203 GRLastMoveToY = y2;
204 }
205
206
GRLine(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aStart,const wxPoint & aEnd,int aWidth,const COLOR4D & aColor,wxPenStyle aStyle)207 void GRLine( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
208 const COLOR4D& aColor, wxPenStyle aStyle )
209 {
210 GRLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aStyle );
211 }
212
213
GRMoveTo(int x,int y)214 void GRMoveTo( int x, int y )
215 {
216 GRLastMoveToX = x;
217 GRLastMoveToY = y;
218 }
219
220
GRLineTo(EDA_RECT * ClipBox,wxDC * DC,int x,int y,int width,const COLOR4D & Color)221 void GRLineTo( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int width, const COLOR4D& Color )
222 {
223 GRLine( ClipBox, DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color );
224 }
225
226
GRCSegm(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,int aPenSize,const COLOR4D & Color)227 void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
228 int aPenSize, const COLOR4D& Color )
229 {
230 GRLastMoveToX = x2;
231 GRLastMoveToY = y2;
232
233 if( ClipBox )
234 {
235 EDA_RECT clipbox( *ClipBox );
236 clipbox.Inflate( width / 2 );
237
238 if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
239 return;
240 }
241
242 if( width <= 2 ) /* single line or 2 pixels */
243 {
244 GRSetColorPen( DC, Color, width );
245 DC->DrawLine( x1, y1, x2, y2 );
246 return;
247 }
248
249 GRSetBrush( DC, Color, NOT_FILLED );
250 GRSetColorPen( DC, Color, aPenSize );
251
252 int radius = ( width + 1 ) >> 1;
253 int dx = x2 - x1;
254 int dy = y2 - y1;
255 double angle = -ArcTangente( dy, dx );
256 wxPoint start;
257 wxPoint end;
258 wxPoint org( x1, y1 );
259 int len = (int) hypot( dx, dy );
260
261 // We know if the DC is mirrored, to draw arcs
262 int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 );
263 int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 );
264 bool mirrored = ( slx > 0 && sly < 0 ) || ( slx < 0 && sly > 0 );
265
266 // first edge
267 start.x = 0;
268 start.y = radius;
269 end.x = len;
270 end.y = radius;
271 RotatePoint( &start, angle );
272 RotatePoint( &end, angle );
273
274 start += org;
275 end += org;
276
277 DC->DrawLine( start, end );
278
279 // first rounded end
280 end.x = 0;
281 end.y = -radius;
282 RotatePoint( &end, angle );
283 end += org;
284
285 if( !mirrored )
286 DC->DrawArc( end, start, org );
287 else
288 DC->DrawArc( start, end, org );
289
290 // second edge
291 start.x = len;
292 start.y = -radius;
293 RotatePoint( &start, angle );
294 start += org;
295
296 DC->DrawLine( start, end );
297
298 // second rounded end
299 end.x = len;
300 end.y = radius;
301 RotatePoint( &end, angle);
302 end += org;
303
304 if( !mirrored )
305 DC->DrawArc( end.x, end.y, start.x, start.y, x2, y2 );
306 else
307 DC->DrawArc( start.x, start.y, end.x, end.y, x2, y2 );
308 }
309
310
GRCSegm(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,const COLOR4D & Color)311 void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
312 const COLOR4D& Color )
313 {
314 GRCSegm( ClipBox, DC, x1, y1, x2, y2, width, 0, Color );
315 }
316
317
GRCSegm(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aStart,const wxPoint & aEnd,int aWidth,const COLOR4D & aColor)318 void GRCSegm( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
319 const COLOR4D& aColor )
320 {
321 GRCSegm( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, 0, aColor );
322 }
323
324
GRFillCSegm(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,const COLOR4D & Color)325 void GRFillCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
326 const COLOR4D& Color )
327 {
328 GRSetColorPen( DC, Color, width );
329 WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width );
330 }
331
332
GRFilledSegment(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aStart,const wxPoint & aEnd,int aWidth,const COLOR4D & aColor)333 void GRFilledSegment( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aStart, const wxPoint& aEnd,
334 int aWidth, const COLOR4D& aColor )
335 {
336 GRSetColorPen( aDC, aColor, aWidth );
337 WinClipAndDrawLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth );
338 }
339
IsGRSPolyDrawable(EDA_RECT * ClipBox,int n,const wxPoint * Points)340 static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, const wxPoint* Points )
341 {
342 if( !ClipBox )
343 return true;
344
345 if( n <= 0 )
346 return false;
347
348 int Xmin, Xmax, Ymin, Ymax;
349
350 Xmin = Xmax = Points[0].x;
351 Ymin = Ymax = Points[0].y;
352
353 for( int ii = 1; ii < n; ii++ ) // calculate rectangle
354 {
355 Xmin = std::min( Xmin, Points[ii].x );
356 Xmax = std::max( Xmax, Points[ii].x );
357 Ymin = std::min( Ymin, Points[ii].y );
358 Ymax = std::max( Ymax, Points[ii].y );
359 }
360
361 xcliplo = ClipBox->GetX();
362 ycliplo = ClipBox->GetY();
363 xcliphi = ClipBox->GetRight();
364 ycliphi = ClipBox->GetBottom();
365
366 if( Xmax < xcliplo )
367 return false;
368 if( Xmin > xcliphi )
369 return false;
370 if( Ymax < ycliplo )
371 return false;
372 if( Ymin > ycliphi )
373 return false;
374
375 return true;
376 }
377
378
379 /**
380 * Draw a new polyline and fill it if Fill, in screen space.
381 */
GRSPoly(EDA_RECT * ClipBox,wxDC * DC,int n,const wxPoint * Points,bool Fill,int width,const COLOR4D & Color,const COLOR4D & BgColor)382 static void GRSPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill,
383 int width, const COLOR4D& Color, const COLOR4D& BgColor )
384 {
385 if( !IsGRSPolyDrawable( ClipBox, n, Points ) )
386 return;
387
388 if( Fill && ( n > 2 ) )
389 {
390 GRSetBrush( DC, BgColor, FILLED );
391 GRSetColorPen( DC, Color, width );
392
393 /* clip before send the filled polygon to wxDC, because under linux
394 * (GTK?) polygons having large coordinates are incorrectly drawn
395 * (integer overflow in coordinates, I am guessing)
396 */
397 ClipAndDrawPoly( ClipBox, DC, Points, n );
398 }
399 else
400 {
401
402 GRMoveTo( Points[0].x, Points[0].y );
403
404 for( int i = 1; i < n; ++i )
405 {
406 GRLineTo( ClipBox, DC, Points[i].x, Points[i].y, width, Color );
407 }
408 }
409 }
410
411
412 /**
413 * Draw a new closed polyline and fill it if Fill, in screen space.
414 */
GRSClosedPoly(EDA_RECT * aClipBox,wxDC * aDC,int aPointCount,const wxPoint * aPoints,bool aFill,int aWidth,const COLOR4D & aColor,const COLOR4D & aBgColor)415 static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC, int aPointCount, const wxPoint* aPoints,
416 bool aFill, int aWidth, const COLOR4D& aColor, const COLOR4D& aBgColor )
417 {
418 if( !IsGRSPolyDrawable( aClipBox, aPointCount, aPoints ) )
419 return;
420
421 if( aFill && ( aPointCount > 2 ) )
422 {
423 GRLastMoveToX = aPoints[aPointCount - 1].x;
424 GRLastMoveToY = aPoints[aPointCount - 1].y;
425 GRSetBrush( aDC, aBgColor, FILLED );
426 GRSetColorPen( aDC, aColor, aWidth );
427 ClipAndDrawPoly( aClipBox, aDC, aPoints, aPointCount );
428 }
429 else
430 {
431
432 GRMoveTo( aPoints[0].x, aPoints[0].y );
433
434 for( int i = 1; i < aPointCount; ++i )
435 {
436 GRLineTo( aClipBox, aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor );
437 }
438
439 int lastpt = aPointCount - 1;
440
441 // Close the polygon
442 if( aPoints[lastpt] != aPoints[0] )
443 {
444 GRLineTo( aClipBox, aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor );
445 }
446 }
447 }
448
449
450 /**
451 * Draw a new polyline and fill it if Fill, in drawing space.
452 */
GRPoly(EDA_RECT * ClipBox,wxDC * DC,int n,const wxPoint * Points,bool Fill,int width,const COLOR4D & Color,const COLOR4D & BgColor)453 void GRPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill, int width,
454 const COLOR4D& Color, const COLOR4D& BgColor )
455 {
456 GRSPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor );
457 }
458
459
460 /**
461 * Draw a closed polyline and fill it if Fill, in object space.
462 */
GRClosedPoly(EDA_RECT * ClipBox,wxDC * DC,int n,const wxPoint * Points,bool Fill,const COLOR4D & Color,const COLOR4D & BgColor)463 void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill,
464 const COLOR4D& Color, const COLOR4D& BgColor )
465 {
466 GRClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor );
467 }
468
469
GRClosedPoly(EDA_RECT * ClipBox,wxDC * DC,int n,const wxPoint * Points,bool Fill,int width,const COLOR4D & Color,const COLOR4D & BgColor)470 void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill, int width,
471 const COLOR4D& Color, const COLOR4D& BgColor )
472 {
473 GRSClosedPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor );
474 }
475
476
clipCircle(EDA_RECT * aClipBox,int xc,int yc,int r,int aWidth)477 static bool clipCircle( EDA_RECT* aClipBox, int xc, int yc, int r, int aWidth )
478 {
479 // Clip circles that are outside the ClipBox.
480 if( aClipBox )
481 {
482 int x0, y0, xm, ym;
483 x0 = aClipBox->GetX();
484 y0 = aClipBox->GetY();
485 xm = aClipBox->GetRight();
486 ym = aClipBox->GetBottom();
487
488 r += aWidth;
489
490 if( xc < ( x0 - r ) )
491 return true;
492
493 if( yc < ( y0 - r ) )
494 return true;
495
496 if( xc > ( r + xm ) )
497 return true;
498
499 if( yc > ( r + ym ) )
500 return true;
501 }
502
503 return false;
504 }
505
506
GRCircle(EDA_RECT * ClipBox,wxDC * DC,int xc,int yc,int r,int width,const COLOR4D & Color)507 void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, int r, int width, const COLOR4D& Color )
508 {
509 if( clipCircle( ClipBox, xc, yc, r, width ) || r <= 0 )
510 return;
511
512 GRSetBrush( DC, Color, NOT_FILLED );
513 GRSetColorPen( DC, Color, width );
514 DC->DrawEllipse( xc - r, yc - r, r + r, r + r );
515 }
516
517
GRCircle(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aPos,int aRadius,int aWidth,const COLOR4D & aColor)518 void GRCircle( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aPos, int aRadius, int aWidth,
519 const COLOR4D& aColor )
520 {
521 GRCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, aWidth, aColor );
522 }
523
524
GRFilledCircle(EDA_RECT * ClipBox,wxDC * DC,int x,int y,int r,int width,const COLOR4D & Color,const COLOR4D & BgColor)525 void GRFilledCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, int width,
526 const COLOR4D& Color, const COLOR4D& BgColor )
527 {
528 if( clipCircle( ClipBox, x, y, r, width ) || r <= 0 )
529 return;
530
531 GRSetBrush( DC, BgColor, FILLED );
532 GRSetColorPen( DC, Color, width );
533 DC->DrawEllipse( x - r, y - r, r + r, r + r );
534 }
535
536
GRFilledCircle(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aPos,int aRadius,const COLOR4D & aColor)537 void GRFilledCircle( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aPos, int aRadius,
538 const COLOR4D& aColor )
539 {
540 GRFilledCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, 0, aColor, aColor );
541 }
542
543
GRArc1(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int xc,int yc,int width,const COLOR4D & Color)544 void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int xc, int yc,
545 int width, const COLOR4D& Color )
546 {
547 /* Clip arcs off screen. */
548 if( ClipBox )
549 {
550 int x0, y0, xm, ym, r;
551 x0 = ClipBox->GetX();
552 y0 = ClipBox->GetY();
553 xm = ClipBox->GetRight();
554 ym = ClipBox->GetBottom();
555 r = KiROUND( Distance( x1, y1, xc, yc ) );
556
557 if( xc < ( x0 - r ) )
558 return;
559
560 if( yc < ( y0 - r ) )
561 return;
562
563 if( xc > ( r + xm ) )
564 return;
565
566 if( yc > ( r + ym ) )
567 return;
568 }
569
570 GRSetBrush( DC, Color );
571 GRSetColorPen( DC, Color, width );
572 DC->DrawArc( x1, y1, x2, y2, xc, yc );
573 }
574
575
GRArc1(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint & aStart,const wxPoint & aEnd,const wxPoint & aCenter,int aWidth,const COLOR4D & aColor)576 void GRArc1( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aStart, const wxPoint& aEnd,
577 const wxPoint& aCenter, int aWidth, const COLOR4D& aColor )
578 {
579 GRArc1( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y,
580 aWidth, aColor );
581 }
582
583
GRFilledArc1(EDA_RECT * ClipBox,wxDC * DC,const wxPoint & aStart,const wxPoint & aEnd,const wxPoint & aCenter,int width,const COLOR4D & Color,const COLOR4D & BgColor)584 void GRFilledArc1( EDA_RECT* ClipBox, wxDC* DC, const wxPoint& aStart, const wxPoint& aEnd,
585 const wxPoint& aCenter, int width, const COLOR4D& Color, const COLOR4D& BgColor )
586 {
587 /* Clip arcs off screen. */
588 if( ClipBox )
589 {
590 int x0, y0, xm, ym, r;
591 x0 = ClipBox->GetX();
592 y0 = ClipBox->GetY();
593 xm = ClipBox->GetRight();
594 ym = ClipBox->GetBottom();
595 r = KiROUND( Distance( aStart.x, aStart.y, aCenter.x, aCenter.y ) );
596
597 if( aCenter.x < ( x0 - r ) )
598 return;
599
600 if( aCenter.y < ( y0 - r ) )
601 return;
602
603 if( aCenter.x > ( r + xm ) )
604 return;
605
606 if( aCenter.y > ( r + ym ) )
607 return;
608 }
609
610 GRSetBrush( DC, BgColor, FILLED );
611 GRSetColorPen( DC, Color, width );
612 DC->DrawArc( aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y );
613 }
614
615
GRArc(EDA_RECT * ClipBox,wxDC * DC,int xc,int yc,double StAngle,double EndAngle,int r,const COLOR4D & Color)616 void GRArc( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, double StAngle, double EndAngle, int r,
617 const COLOR4D& Color )
618 {
619 int x1, y1, x2, y2;
620
621 /* Clip arcs off screen */
622 if( ClipBox )
623 {
624 int radius = r + 1;
625 int x0, y0, xm, ym, x, y;
626 x0 = ClipBox->GetX();
627 y0 = ClipBox->GetY();
628 xm = ClipBox->GetRight();
629 ym = ClipBox->GetBottom();
630 x = xc;
631 y = yc;
632
633 if( x < ( x0 - radius ) )
634 return;
635
636 if( y < ( y0 - radius ) )
637 return;
638
639 if( x > ( xm + radius ) )
640 return;
641
642 if( y > ( ym + radius ) )
643 return;
644 }
645
646 x1 = r;
647 y1 = 0;
648 RotatePoint( &x1, &y1, EndAngle );
649
650 x2 = r;
651 y2 = 0;
652 RotatePoint( &x2, &y2, StAngle );
653
654 GRSetBrush( DC, Color, NOT_FILLED );
655 GRSetColorPen( DC, Color );
656 DC->DrawArc( xc + x1, yc - y1, xc + x2, yc - y2, xc, yc );
657 }
658
659
GRArc(EDA_RECT * ClipBox,wxDC * DC,int x,int y,double StAngle,double EndAngle,int r,int width,const COLOR4D & Color)660 void GRArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y, double StAngle, double EndAngle, int r,
661 int width, const COLOR4D& Color )
662 {
663 int x1, y1, x2, y2;
664
665 /* Clip arcs off screen. */
666 if( ClipBox )
667 {
668 int x0, y0, xm, ym;
669 x0 = ClipBox->GetX();
670 y0 = ClipBox->GetY();
671 xm = ClipBox->GetRight();
672 ym = ClipBox->GetBottom();
673
674 if( x < ( x0 - r - width ) )
675 return;
676
677 if( y < ( y0 - r - width ) )
678 return;
679
680 if( x > ( r + xm + width ) )
681 return;
682
683 if( y > ( r + ym + width ) )
684 return;
685 }
686
687 x1 = r;
688 y1 = 0;
689 RotatePoint( &x1, &y1, EndAngle );
690
691 x2 = r;
692 y2 = 0;
693 RotatePoint( &x2, &y2, StAngle );
694
695 GRSetBrush( DC, Color );
696 GRSetColorPen( DC, Color, width );
697 DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y );
698 }
699
700
GRRect(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,const COLOR4D & Color)701 void GRRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
702 const COLOR4D& Color )
703 {
704 GRSRect( ClipBox, DC, x1, y1, x2, y2, width, Color );
705 }
706
707
GRFilledRect(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,const COLOR4D & Color,const COLOR4D & BgColor)708 void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
709 const COLOR4D& Color, const COLOR4D& BgColor )
710 {
711 GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, 0, Color, BgColor );
712 }
713
714
GRFilledRect(EDA_RECT * ClipBox,wxDC * DC,int x1,int y1,int x2,int y2,int width,const COLOR4D & Color,const COLOR4D & BgColor)715 void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width,
716 const COLOR4D& Color, const COLOR4D& BgColor )
717 {
718 GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, width, Color, BgColor );
719 }
720
721
GRSRect(EDA_RECT * aClipBox,wxDC * aDC,int x1,int y1,int x2,int y2,int aWidth,const COLOR4D & aColor)722 void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth,
723 const COLOR4D& aColor )
724 {
725 wxPoint points[5];
726 points[0] = wxPoint( x1, y1 );
727 points[1] = wxPoint( x1, y2 );
728 points[2] = wxPoint( x2, y2 );
729 points[3] = wxPoint( x2, y1 );
730 points[4] = points[0];
731 GRSClosedPoly( aClipBox, aDC, 5, points, NOT_FILLED, aWidth, aColor, aColor );
732 }
733
734
GRSFilledRect(EDA_RECT * aClipBox,wxDC * aDC,int x1,int y1,int x2,int y2,int aWidth,const COLOR4D & aColor,const COLOR4D & aBgColor)735 void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, int aWidth,
736 const COLOR4D& aColor, const COLOR4D& aBgColor )
737 {
738 wxPoint points[5];
739 points[0] = wxPoint( x1, y1 );
740 points[1] = wxPoint( x1, y2 );
741 points[2] = wxPoint( x2, y2 );
742 points[3] = wxPoint( x2, y1 );
743 points[4] = points[0];
744
745 GRSetBrush( aDC, aBgColor, FILLED );
746 GRSetColorPen( aDC, aBgColor, aWidth );
747
748 if( aClipBox && ( aWidth > 0 ) )
749 {
750 EDA_RECT clipbox( *aClipBox );
751 clipbox.Inflate( aWidth );
752 ClipAndDrawPoly( &clipbox, aDC, points, 5 ); // polygon approach is more accurate
753 }
754 else
755 {
756 ClipAndDrawPoly(aClipBox, aDC, points, 5 );
757 }
758 }
759
760 /**
761 * Used to clip a polygon and draw it as Filled Polygon.
762 *
763 * Uses the Sutherland and Hodgman algo to clip the given poly against a rectangle. This
764 * rectangle is the drawing area this is useful under Linux (2009) because filled polygons
765 * are incorrectly drawn if they have too large coordinates (seems due to integer overflows
766 * in calculations). Could be removed in some years, if become unnecessary.
767 */
768
769 #include <SutherlandHodgmanClipPoly.h>
770
ClipAndDrawPoly(EDA_RECT * aClipBox,wxDC * aDC,const wxPoint * Points,int n)771 void ClipAndDrawPoly( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint* Points, int n )
772 {
773 if( aClipBox == nullptr )
774 {
775 aDC->DrawPolygon( n, Points );
776 return;
777 }
778
779 // A clip box exists: clip and draw the polygon.
780 static std::vector<wxPoint> clippedPolygon;
781 static pointVector inputPolygon, outputPolygon;
782
783 inputPolygon.clear();
784 outputPolygon.clear();
785 clippedPolygon.clear();
786
787 for( int ii = 0; ii < n; ii++ )
788 inputPolygon.push_back( PointF( (REAL) Points[ii].x, (REAL) Points[ii].y ) );
789
790 RectF window( (REAL) aClipBox->GetX(), (REAL) aClipBox->GetY(),
791 (REAL) aClipBox->GetWidth(), (REAL) aClipBox->GetHeight() );
792
793 SutherlandHodgman sh( window );
794 sh.Clip( inputPolygon, outputPolygon );
795
796 for( cpointIterator cit = outputPolygon.begin(); cit != outputPolygon.end(); ++cit )
797 {
798 clippedPolygon.emplace_back( KiROUND( cit->X ), KiROUND( cit->Y ) );
799 }
800
801 if( clippedPolygon.size() )
802 aDC->DrawPolygon( clippedPolygon.size(), &clippedPolygon[0] );
803 }
804
805
806