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