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 <stambaughw@verizon.net>
6  * Copyright (C) 1992-2018 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 /********************************/
23 /* Low level graphics routines */
24 /********************************/
25 
26 
27 #include <gr_basic.h>
28 #include <trigo.h>
29 #include <eda_item.h>
30 #include <base_screen.h>
31 #include <bezier_curves.h>
32 #include <math_for_graphics.h>
33 #include <wx/graphics.h>
35 #include <math/util.h> // for KiROUND
36 
37 #include <algorithm>
38 
39 static const bool FILLED = true;
40 static const bool NOT_FILLED = false;
41 
42 /* Important Note:
43  * These drawing functions clip draw item before send these items to wxDC draw
44  * functions. For guy who asks why i did it, see a sample of problems encountered
45  * when pixels
46  * coordinates overflow 16 bits values:
47  * http://trac.wxwidgets.org/ticket/10446
48  * Problems can be found under Windows **and** Linux (mainly when drawing arcs)
49  * (mainly at low zoom values (2, 1 or 0.5), in Pcbnew)
50  * some of these problems could be now fixed in recent distributions.
51  *
52  * Currently (feb 2009) there are overflow problems when drawing solid (filled)
53  * polygons under linux without clipping
54  *
55  * So before removing clipping functions, be aware these bug (they are not in
56  * KiCad or wxWidgets) are fixed by testing how are drawn complex lines arcs
57  * and solid polygons under Windows and Linux and remember users can have old
58  * versions with bugs
59  */
60 
61 
62 /* Definitions for enabling and disabling debugging features in gr_basic.cpp.
63  * Please remember to set these back to 0 before making LAUNCHPAD commits.
64  */
65 #define DEBUG_DUMP_CLIP_ERROR_COORDS 0 // Set to 1 to dump clip algorithm errors.
66 #define DEBUG_DUMP_CLIP_COORDS 0 // Set to 1 to dump clipped coordinates.
67 
68 
69 // For draw mode = XOR GR_XOR or GR_NXOR by background color
71 
72 
73 static void ClipAndDrawPoly( EDA_RECT* ClipBox, wxDC* DC, const wxPoint* Points, int n );
74 
75 /* These functions are used by corresponding functions
76  * ( GRSCircle is called by GRCircle for instance) after mapping coordinates
77  * from user units to screen units(pixels coordinates)
78  */
79 static void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1,
80  int x2, int y2, int aWidth, COLOR4D aColor,
81  wxPenStyle aStyle = wxPENSTYLE_SOLID );
82 
83 
84 
86 static bool s_ForceBlackPen; /* if true: draws in black instead of
87  * color for printing. */
88 static int xcliplo = 0,
89  ycliplo = 0,
90  xcliphi = 2000,
91  ycliphi = 2000;
92 
93 static COLOR4D s_DC_lastcolor( 0, 0, 0, 0 );
94 static COLOR4D s_DC_lastbrushcolor( 0, 0, 0, 0 );
95 static bool s_DC_lastbrushfill = false;
96 static wxDC* s_DC_lastDC = NULL;
97 
98 static void WinClipAndDrawLine( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width )
99 {
100  GRLastMoveToX = x2;
101  GRLastMoveToY = y2;
102 
103  if( ClipBox )
104  {
105  EDA_RECT clipbox(*ClipBox);
106  clipbox.Inflate(width/2);
107  if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
108  return;
109  }
110 
111  DC->DrawLine( x1, y1, x2, y2 );
112 }
113 
114 
115 /* Forcing a reset of the current pen.
116  * Must be called after changing the graphical device before any trace.
117  */
118 void GRResetPenAndBrush( wxDC* DC )
119 {
120  GRSetBrush( DC, BLACK ); // Force no fill
121  s_DC_lastbrushcolor = COLOR4D::UNSPECIFIED;
122  s_DC_lastcolor = COLOR4D::UNSPECIFIED;
123  s_DC_lastDC = NULL;
124 }
125 
126 
131 void GRSetColorPen( wxDC* DC, COLOR4D Color, int width, wxPenStyle style )
132 {
133  wxDash dots[2] = { 1, 3 };
134  // Under OSX and while printing when wxPen is set to 0, renderer follows the request drawing
135  // nothing & in the bitmap world the minimum is enough to light a pixel, in vectorial one not
136  if( width <= 1 )
137  width = DC->DeviceToLogicalXRel( 1 );
138 
139  if( s_ForceBlackPen )
140  Color = COLOR4D::BLACK;
141 
142  const wxPen& curr_pen = DC->GetPen();
143 
144  if( !curr_pen.IsOk() || curr_pen.GetColour() != Color.ToColour()
145  || curr_pen.GetWidth() != width
146  || curr_pen.GetStyle() != style )
147  {
148  wxPen pen;
149  pen.SetColour( Color.ToColour() );
150  if( style == wxPENSTYLE_DOT )
151  {
152  style = wxPENSTYLE_USER_DASH;
153  pen.SetDashes( 2, dots );
154  }
155  pen.SetWidth( width );
156  pen.SetStyle( style );
157  DC->SetPen( pen );
158  }
159  else
160  // Should be not needed, but on Linux, in printing process
161  // the curr pen settings needs to be sometimes re-initialized
162  // Clearly, this is due to a bug, related to SetBrush(),
163  // but we have to live with it, at least on wxWidgets 3.0
164  DC->SetPen( curr_pen );
165 }
166 
167 
168 void GRSetBrush( wxDC* DC, COLOR4D Color, bool fill )
169 {
170  if( s_ForceBlackPen )
171  Color = COLOR4D::BLACK;
172 
173  if( s_DC_lastbrushcolor != Color
174  || s_DC_lastbrushfill != fill
175  || s_DC_lastDC != DC )
176  {
177  wxBrush brush;
178 
179  brush.SetColour( Color.ToColour() );
180 
181  if( fill )
182  brush.SetStyle( wxBRUSHSTYLE_SOLID );
183  else
184  brush.SetStyle( wxBRUSHSTYLE_TRANSPARENT );
185 
186  DC->SetBrush( brush );
187 
188  s_DC_lastbrushcolor = Color;
189  s_DC_lastbrushfill = fill;
190  s_DC_lastDC = DC;
191  }
192 }
193 
194 
199 void GRForceBlackPen( bool flagforce )
200 {
201  s_ForceBlackPen = flagforce;
202 }
203 
204 
210 {
211  return s_ForceBlackPen;
212 }
213 
214 
215 void GRPutPixel( EDA_RECT* ClipBox, wxDC* DC, int x, int y, COLOR4D Color )
216 {
217  if( ClipBox && !ClipBox->Contains( x, y ) )
218  return;
219 
220  GRSetColorPen( DC, Color );
221  DC->DrawPoint( x, y );
222 }
223 
224 
225 /*
226  * Draw a line, in object space.
227  */
228 void GRLine( EDA_RECT* ClipBox,
229  wxDC* DC,
230  int x1,
231  int y1,
232  int x2,
233  int y2,
234  int width,
235  COLOR4D Color,
236  wxPenStyle aStyle)
237 {
238  GRSetColorPen( DC, Color, width, aStyle );
239  WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width );
240  GRLastMoveToX = x2;
241  GRLastMoveToY = y2;
242 }
243 
244 
245 void GRLine( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor, wxPenStyle aStyle )
246 {
247  GRLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, aColor, aStyle );
248 }
249 
250 
251 /*
252  * Move to a new position, in object space.
253  */
254 void GRMoveTo( int x, int y )
255 {
256  GRLastMoveToX = x;
257  GRLastMoveToY = y;
258 }
259 
260 
261 /*
262  * Draw line to a new position, in object space.
263  */
264 void GRLineTo( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int width, COLOR4D Color )
265 {
266  GRLine( ClipBox, DC, GRLastMoveToX, GRLastMoveToY, x, y, width, Color );
267 }
268 
269 
270 
271 
282 void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector<wxPoint>& aLines,
283  int aWidth, COLOR4D aColor )
284 {
285  if( aLines.empty() )
286  return;
287 
288  GRSetColorPen( aDC, aColor, aWidth );
289 
290  if( aClipBox )
291  aClipBox->Inflate( aWidth / 2 );
292 
293  for( unsigned i = 0; i < aLines.size(); i += 2 )
294  {
295  int x1 = aLines[i].x;
296  int y1 = aLines[i].y;
297  int x2 = aLines[i + 1].x;
298  int y2 = aLines[i + 1].y;
299  if( ( aClipBox == NULL ) || !ClipLine( aClipBox, x1, y1, x2, y2 ) )
300  aDC->DrawLine( x1, y1, x2, y2 );
301  }
302 
303  GRMoveTo( aLines[aLines.size() - 1].x, aLines[aLines.size() - 1].y );
304 
305  if( aClipBox )
306  aClipBox->Inflate(-aWidth/2);
307 }
308 
309 // Draw the outline of a thick segment wih rounded ends
310 void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
311  int width, int aPenSize, COLOR4D Color )
312 {
313  GRLastMoveToX = x2;
314  GRLastMoveToY = y2;
315 
316  if( ClipBox )
317  {
318  EDA_RECT clipbox(*ClipBox);
319  clipbox.Inflate(width/2);
320 
321  if( ClipLine( &clipbox, x1, y1, x2, y2 ) )
322  return;
323  }
324 
325 
326  if( width <= 2 ) /* single line or 2 pixels */
327  {
328  GRSetColorPen( DC, Color, width );
329  DC->DrawLine( x1, y1, x2, y2 );
330  return;
331  }
332 
333  GRSetBrush( DC, Color, NOT_FILLED );
334  GRSetColorPen( DC, Color, aPenSize );
335 
336  int radius = (width + 1) >> 1;
337  int dx = x2 - x1;
338  int dy = y2 - y1;
339  double angle = -ArcTangente( dy, dx );
340  wxPoint start;
341  wxPoint end;
342  wxPoint org( x1, y1);
343  int len = (int) hypot( dx, dy );
344 
345  // We know if the DC is mirrored, to draw arcs
346  int slx = DC->DeviceToLogicalX( 1 ) - DC->DeviceToLogicalX( 0 );
347  int sly = DC->DeviceToLogicalY( 1 ) - DC->DeviceToLogicalY( 0 );
348  bool mirrored = (slx > 0 && sly < 0) || (slx < 0 && sly > 0);
349 
350  // first edge
351  start.x = 0;
352  start.y = radius;
353  end.x = len;
354  end.y = radius;
355  RotatePoint( &start, angle);
356  RotatePoint( &end, angle);
357 
358  start += org;
359  end += org;
360 
361  DC->DrawLine( start, end );
362 
363  // first rounded end
364  end.x = 0;
365  end.y = -radius;
366  RotatePoint( &end, angle);
367  end += org;
368 
369  if( !mirrored )
370  DC->DrawArc( end, start, org );
371  else
372  DC->DrawArc( start, end, org );
373 
374 
375  // second edge
376  start.x = len;
377  start.y = -radius;
378  RotatePoint( &start, angle);
379  start += org;
380 
381  DC->DrawLine( start, end );
382 
383  // second rounded end
384  end.x = len;
385  end.y = radius;
386  RotatePoint( &end, angle);
387  end += org;
388 
389  if( !mirrored )
390  DC->DrawArc( end.x, end.y, start.x, start.y, x2, y2 );
391  else
392  DC->DrawArc( start.x, start.y, end.x, end.y, x2, y2 );
393 }
394 
395 
396 void GRCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
397  int width, COLOR4D Color )
398 {
399  GRCSegm( ClipBox, DC, x1, y1, x2, y2, width, 0, Color );
400 }
401 
402 
403 void GRCSegm( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd,
404  int aWidth, COLOR4D aColor )
405 {
406  GRCSegm( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth, 0, aColor );
407 }
408 
409 
410 /*
411  * Draw segment (full) with rounded ends in object space (real coords.).
412  */
413 void GRFillCSegm( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
414  int width, COLOR4D Color )
415 {
416  GRSetColorPen( DC, Color, width );
417  WinClipAndDrawLine( ClipBox, DC, x1, y1, x2, y2, width );
418 }
419 
420 
421 void GRFilledSegment( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd,
422  int aWidth, COLOR4D aColor )
423 {
424  GRSetColorPen( aDC, aColor, aWidth );
425  WinClipAndDrawLine( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aWidth );
426 }
427 
428 
429 static bool IsGRSPolyDrawable( EDA_RECT* ClipBox, int n, const wxPoint* Points )
430 {
431  if( !ClipBox )
432  return true;
433 
434  if( n <= 0 )
435  return false;
436 
437  int Xmin, Xmax, Ymin, Ymax;
438 
439  Xmin = Xmax = Points[0].x;
440  Ymin = Ymax = Points[0].y;
441 
442  for( int ii = 1; ii < n; ii++ ) // calculate rectangle
443  {
444  Xmin = std::min( Xmin, Points[ii].x );
445  Xmax = std::max( Xmax, Points[ii].x );
446  Ymin = std::min( Ymin, Points[ii].y );
447  Ymax = std::max( Ymax, Points[ii].y );
448  }
449 
450  xcliplo = ClipBox->GetX();
451  ycliplo = ClipBox->GetY();
452  xcliphi = ClipBox->GetRight();
453  ycliphi = ClipBox->GetBottom();
454 
455  if( Xmax < xcliplo )
456  return false;
457  if( Xmin > xcliphi )
458  return false;
459  if( Ymax < ycliplo )
460  return false;
461  if( Ymin > ycliphi )
462  return false;
463 
464  return true;
465 }
466 
467 
468 /*
469  * Draw a new polyline and fill it if Fill, in screen space.
470  */
471 static void GRSPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill,
472  int width, COLOR4D Color, COLOR4D BgColor )
473 {
474  if( !IsGRSPolyDrawable( ClipBox, n, Points ) )
475  return;
476 
477  if( Fill && ( n > 2 ) )
478  {
479  GRSetBrush( DC, BgColor, FILLED );
480  GRSetColorPen( DC, Color, width );
481 
482  /* clip before send the filled polygon to wxDC, because under linux
483  * (GTK?) polygons having large coordinates are incorrectly drawn
484  * (integer overflow in coordinates, I am guessing)
485  */
486  ClipAndDrawPoly( ClipBox, DC, Points, n );
487  }
488  else
489  {
490 
491  GRMoveTo( Points[0].x, Points[0].y );
492  for( int i = 1; i < n; ++i )
493  {
494  GRLineTo( ClipBox, DC, Points[i].x, Points[i].y, width, Color );
495  }
496  }
497 }
498 
499 
500 /*
501  * Draw a new closed polyline and fill it if Fill, in screen space.
502  */
503 static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC, int aPointCount, const wxPoint* aPoints,
504  bool aFill, int aWidth, COLOR4D aColor, COLOR4D aBgColor )
505 {
506  if( !IsGRSPolyDrawable( aClipBox, aPointCount, aPoints ) )
507  return;
508 
509  if( aFill && ( aPointCount > 2 ) )
510  {
511  GRLastMoveToX = aPoints[aPointCount - 1].x;
512  GRLastMoveToY = aPoints[aPointCount - 1].y;
513  GRSetBrush( aDC, aBgColor, FILLED );
514  GRSetColorPen( aDC, aColor, aWidth );
515  ClipAndDrawPoly( aClipBox, aDC, aPoints, aPointCount );
516  }
517  else
518  {
519 
520  GRMoveTo( aPoints[0].x, aPoints[0].y );
521  for( int i = 1; i < aPointCount; ++i )
522  {
523  GRLineTo( aClipBox, aDC, aPoints[i].x, aPoints[i].y, aWidth, aColor );
524  }
525 
526  int lastpt = aPointCount - 1;
527 
528  // Close the polygon
529  if( aPoints[lastpt] != aPoints[0] )
530  {
531  GRLineTo( aClipBox, aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor );
532  }
533  }
534 }
535 
536 
537 /*
538  * Draw a new polyline and fill it if Fill, in drawing space.
539  */
540 void GRPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill, int width,
541  COLOR4D Color, COLOR4D BgColor )
542 {
543  GRSPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor );
544 }
545 
546 
547 /*
548  * Draw a closed polyline and fill it if Fill, in object space.
549  */
550 void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill,
551  COLOR4D Color, COLOR4D BgColor )
552 {
553  GRClosedPoly( ClipBox, DC, n, Points, Fill, 0, Color, BgColor );
554 }
555 
556 
557 void GRClosedPoly( EDA_RECT* ClipBox, wxDC* DC, int n, const wxPoint* Points, bool Fill, int width,
558  COLOR4D Color, COLOR4D BgColor )
559 {
560  GRSClosedPoly( ClipBox, DC, n, Points, Fill, width, Color, BgColor );
561 }
562 
563 
564 static bool clipCircle( EDA_RECT* aClipBox, int xc, int yc, int r, int aWidth )
565 {
566  // Clip circles that are outside the ClipBox.
567  if( aClipBox )
568  {
569  int x0, y0, xm, ym;
570  x0 = aClipBox->GetX();
571  y0 = aClipBox->GetY();
572  xm = aClipBox->GetRight();
573  ym = aClipBox->GetBottom();
574 
575  r += aWidth;
576 
577  if( xc < ( x0 - r ) )
578  return true;
579 
580  if( yc < ( y0 - r ) )
581  return true;
582 
583  if( xc > ( r + xm ) )
584  return true;
585 
586  if( yc > ( r + ym ) )
587  return true;
588  }
589 
590  return false;
591 }
592 
593 
594 void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, int r, int width, COLOR4D Color )
595 {
596  if( clipCircle( ClipBox, xc, yc, r, width ) || r <= 0 )
597  return;
598 
599  GRSetBrush( DC, Color, NOT_FILLED );
600  GRSetColorPen( DC, Color, width );
601  DC->DrawEllipse( xc - r, yc - r, r + r, r + r );
602 }
603 
604 
605 void GRCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r, COLOR4D Color )
606 {
607  GRCircle( ClipBox, DC, x, y, r, 0, Color );
608 }
609 
610 
611 void GRCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, int aWidth, COLOR4D aColor )
612 {
613  GRCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, aWidth, aColor );
614 }
615 
616 
617 void GRFilledCircle( EDA_RECT* ClipBox, wxDC* DC, int x, int y, int r,
618  int width, COLOR4D Color, COLOR4D BgColor )
619 {
620  if( clipCircle( ClipBox, x, y, r, width ) || r <= 0 )
621  return;
622 
623  GRSetBrush( DC, BgColor, FILLED );
624  GRSetColorPen( DC, Color, width );
625  DC->DrawEllipse( x - r, y - r, r + r, r + r );
626 }
627 
628 
629 void GRFilledCircle( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aPos, int aRadius, COLOR4D aColor )
630 {
631  GRFilledCircle( aClipBox, aDC, aPos.x, aPos.y, aRadius, 0, aColor, aColor );
632 }
633 
634 
635 /*
636  * Draw an arc in user space.
637  */
638 void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
639  int xc, int yc, COLOR4D Color )
640 {
641  GRArc1( ClipBox, DC, x1, y1, x2, y2, xc, yc, 0, Color );
642 }
643 
644 
645 /*
646  * Draw an arc, width = width in user space.
647  */
648 void GRArc1( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
649  int xc, int yc, int width, COLOR4D Color )
650 {
651  /* Clip arcs off screen. */
652  if( ClipBox )
653  {
654  int x0, y0, xm, ym, r;
655  x0 = ClipBox->GetX();
656  y0 = ClipBox->GetY();
657  xm = ClipBox->GetRight();
658  ym = ClipBox->GetBottom();
659  r = KiROUND( Distance( x1, y1, xc, yc ) );
660  if( xc < ( x0 - r ) )
661  return;
662  if( yc < ( y0 - r ) )
663  return;
664  if( xc > ( r + xm ) )
665  return;
666  if( yc > ( r + ym ) )
667  return;
668  }
669 
670  GRSetBrush( DC, Color );
671  GRSetColorPen( DC, Color, width );
672  DC->DrawArc( x1, y1, x2, y2, xc, yc );
673 }
674 
675 
676 void GRArc1( EDA_RECT* aClipBox, wxDC* aDC, wxPoint aStart, wxPoint aEnd,
677  wxPoint aCenter, int aWidth, COLOR4D aColor )
678 {
679  GRArc1( aClipBox, aDC, aStart.x, aStart.y, aEnd.x, aEnd.y, aCenter.x, aCenter.y,
680  aWidth, aColor );
681 }
682 
683 
684 /*
685  * Draw a filled arc in drawing space.
686  */
687 void GRFilledArc( EDA_RECT* ClipBox,
688  wxDC* DC,
689  int x,
690  int y,
691  double StAngle,
692  double EndAngle,
693  int r,
694  int width,
695  COLOR4D Color,
696  COLOR4D BgColor )
697 {
698  int x1, y1, x2, y2;
699 
700  /* Clip arcs off screen */
701  if( ClipBox )
702  {
703  int x0, y0, xm, ym;
704  x0 = ClipBox->GetX();
705  y0 = ClipBox->GetY();
706  xm = ClipBox->GetRight();
707  ym = ClipBox->GetBottom();
708 
709  if( x < ( x0 - r - 1 ) )
710  return;
711 
712  if( y < ( y0 - r - 1 ) )
713  return;
714 
715  if( x > ( r + xm + 1 ) )
716  return;
717 
718  if( y > ( r + ym + 1 ) )
719  return;
720  }
721 
722  x1 = r;
723  y1 = 0;
724  RotatePoint( &x1, &y1, EndAngle );
725 
726  x2 = r;
727  y2 = 0;
728  RotatePoint( &x2, &y2, StAngle );
729 
730  GRSetBrush( DC, BgColor, FILLED );
731  GRSetColorPen( DC, Color, width );
732  DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y );
733 }
734 
735 
736 void GRFilledArc( EDA_RECT* ClipBox, wxDC* DC, int x, int y,
737  double StAngle, double EndAngle, int r,
738  COLOR4D Color, COLOR4D BgColor )
739 {
740  GRFilledArc( ClipBox, DC, x, y, StAngle, EndAngle, r, 0, Color, BgColor );
741 }
742 
743 
744 /*
745  * Draw an arc in drawing space.
746  */
747 void GRArc( EDA_RECT* ClipBox, wxDC* DC, int xc, int yc, double StAngle,
748  double EndAngle, int r, COLOR4D Color )
749 {
750  int x1, y1, x2, y2;
751 
752  /* Clip arcs off screen */
753  if( ClipBox )
754  {
755  int radius = r + 1;
756  int x0, y0, xm, ym, x, y;
757  x0 = ClipBox->GetX();
758  y0 = ClipBox->GetY();
759  xm = ClipBox->GetRight();
760  ym = ClipBox->GetBottom();
761  x = xc;
762  y = yc;
763 
764  if( x < ( x0 - radius ) )
765  return;
766  if( y < ( y0 - radius ) )
767  return;
768  if( x > ( xm + radius ) )
769  return;
770  if( y > ( ym + radius ) )
771  return;
772  }
773 
774  x1 = r;
775  y1 = 0;
776  RotatePoint( &x1, &y1, EndAngle );
777 
778  x2 = r;
779  y2 = 0;
780  RotatePoint( &x2, &y2, StAngle );
781 
782  GRSetBrush( DC, Color, NOT_FILLED );
783  GRSetColorPen( DC, Color );
784  DC->DrawArc( xc + x1, yc - y1, xc + x2, yc - y2, xc, yc );
785 }
786 
787 
788 /*
789  * Draw an arc with width = width in drawing space.
790  */
791 void GRArc( EDA_RECT* ClipBox,
792  wxDC* DC,
793  int x,
794  int y,
795  double StAngle,
796  double EndAngle,
797  int r,
798  int width,
799  COLOR4D Color )
800 {
801  int x1, y1, x2, y2;
802 
803  /* Clip arcs off screen. */
804  if( ClipBox )
805  {
806  int x0, y0, xm, ym;
807  x0 = ClipBox->GetX();
808  y0 = ClipBox->GetY();
809  xm = ClipBox->GetRight();
810  ym = ClipBox->GetBottom();
811 
812  if( x < ( x0 - r - width ) )
813  return;
814 
815  if( y < ( y0 - r - width ) )
816  return;
817 
818  if( x > ( r + xm + width ) )
819  return;
820 
821  if( y > ( r + ym + width ) )
822  return;
823  }
824 
825  x1 = r;
826  y1 = 0;
827  RotatePoint( &x1, &y1, EndAngle );
828 
829  x2 = r;
830  y2 = 0;
831  RotatePoint( &x2, &y2, StAngle );
832 
833  GRSetBrush( DC, Color );
834  GRSetColorPen( DC, Color, width );
835  DC->DrawArc( x + x1, y - y1, x + x2, y - y2, x, y );
836 }
837 
838 
839 /*
840  * Draw a rectangle in drawing space.
841  */
842 void GRRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2, COLOR4D aColor )
843 {
844  GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor );
845 }
846 
847 
848 void GRRectPs( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, COLOR4D aColor, wxPenStyle aStyle )
849 {
850  int x1 = aRect.GetX();
851  int y1 = aRect.GetY();
852  int x2 = aRect.GetRight();
853  int y2 = aRect.GetBottom();
854 
855  GRSRect( aClipBox, aDC, x1, y1, x2, y2, 0, aColor, aStyle );
856 }
857 
858 
859 /*
860  * Draw a rectangle (thick lines) in drawing space.
861  */
862 void GRRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color )
863 {
864  GRSRect( ClipBox, DC, x1, y1, x2, y2, width, Color );
865 }
866 
867 
868 void GRRect( EDA_RECT* aClipBox, wxDC* aDC, const EDA_RECT& aRect, int aWidth, COLOR4D aColor )
869 {
870  int x1 = aRect.GetX();
871  int y1 = aRect.GetY();
872  int x2 = aRect.GetRight();
873  int y2 = aRect.GetBottom();
874 
875  GRSRect( aClipBox, aDC, x1, y1, x2, y2, aWidth, aColor );
876 }
877 
878 
879 /*
880  * Draw a rectangle (filled with AreaColor) in drawing space.
881  */
882 void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
883  COLOR4D Color, COLOR4D BgColor )
884 {
885  GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, 0, Color, BgColor );
886 }
887 
888 
889 /*
890  * Draw a rectangle (filled with AreaColor) in drawing space.
891  */
892 void GRFilledRect( EDA_RECT* ClipBox, wxDC* DC, int x1, int y1, int x2, int y2,
893  int width, COLOR4D Color, COLOR4D BgColor )
894 {
895  GRSFilledRect( ClipBox, DC, x1, y1, x2, y2, width, Color, BgColor );
896 }
897 
898 
899 /*
900  * Draw a rectangle in screen space.
901  */
902 
903 void GRSRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2,
904  int aWidth, COLOR4D aColor, wxPenStyle aStyle )
905 {
906  wxPoint points[5];
907  points[0] = wxPoint(x1, y1);
908  points[1] = wxPoint(x1, y2);
909  points[2] = wxPoint(x2, y2);
910  points[3] = wxPoint(x2, y1);
911  points[4] = points[0];
912  GRSClosedPoly( aClipBox, aDC, 5, points, NOT_FILLED, aWidth,
913  aColor, aColor );
914 }
915 
916 
917 void GRSFilledRect( EDA_RECT* aClipBox, wxDC* aDC, int x1, int y1, int x2, int y2,
918  int aWidth, COLOR4D aColor, COLOR4D aBgColor )
919 {
920  wxPoint points[5];
921  points[0] = wxPoint(x1, y1);
922  points[1] = wxPoint(x1, y2);
923  points[2] = wxPoint(x2, y2);
924  points[3] = wxPoint(x2, y1);
925  points[4] = points[0];
926 
927  GRSetBrush( aDC, aBgColor, FILLED );
928  GRSetColorPen( aDC, aBgColor, aWidth );
929 
930  if( aClipBox && (aWidth > 0) )
931  {
932  EDA_RECT clipbox(*aClipBox);
933  clipbox.Inflate(aWidth);
934  ClipAndDrawPoly(&clipbox, aDC, points, 5); // polygon approach is more accurate
935  }
936  else
937  ClipAndDrawPoly(aClipBox, aDC, points, 5 );
938 }
939 
950 /* Note: aClipBox == NULL is legal, so if aClipBox == NULL,
951  * the polygon is drawn, but not clipped
952  */
953 #include <SutherlandHodgmanClipPoly.h>
954 
955 void ClipAndDrawPoly( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint* Points, int n )
956 {
957  if( aClipBox == NULL )
958  {
959  aDC->DrawPolygon( n, Points );
960  return;
961  }
962 
963  // A clip box exists: clip and draw the polygon.
964  static std::vector<wxPoint> clippedPolygon;
965  static pointVector inputPolygon, outputPolygon;
966 
967  inputPolygon.clear();
968  outputPolygon.clear();
969  clippedPolygon.clear();
970 
971  for( int ii = 0; ii < n; ii++ )
972  inputPolygon.push_back( PointF( (REAL) Points[ii].x, (REAL) Points[ii].y ) );
973 
974  RectF window( (REAL) aClipBox->GetX(), (REAL) aClipBox->GetY(),
975  (REAL) aClipBox->GetWidth(), (REAL) aClipBox->GetHeight() );
976 
977  SutherlandHodgman sh( window );
978  sh.Clip( inputPolygon, outputPolygon );
979 
980  for( cpointIterator cit = outputPolygon.begin(); cit != outputPolygon.end(); ++cit )
981  {
982  clippedPolygon.emplace_back( KiROUND( cit->X ), KiROUND( cit->Y ) );
983  }
984 
985  if( clippedPolygon.size() )
986  aDC->DrawPolygon( clippedPolygon.size(), &clippedPolygon[0] );
987 }
988 
989 
990 void GRBezier( EDA_RECT* aClipBox, wxDC* aDC,
991  std::vector<wxPoint>& aPoint,
992  int aWidth, COLOR4D aColor )
993 {
994  std::vector<wxPoint> output;
995 
996  BEZIER_POLY converter( aPoint );
997  converter.GetPoly( output, aWidth );
998 
999  GRPoly( aClipBox, aDC, output.size(), &output[0], false, aWidth, aColor, aColor );
1000 }
1001 
1002 
1003 void GRDrawAnchor( EDA_RECT *aClipBox, wxDC *aDC, int x, int y,
1004  int aSize, COLOR4D aColor )
1005 {
1006  int anchor_size = aDC->DeviceToLogicalXRel( aSize );
1007 
1008  GRLine( aClipBox, aDC,
1009  x - anchor_size, y,
1010  x + anchor_size, y, 0, aColor );
1011  GRLine( aClipBox, aDC,
1012  x, y - anchor_size,
1013  x, y + anchor_size, 0, aColor );
1014 }
1015 
1016 
1017 void GRDrawWrappedText( wxDC& aDC, wxString const& aText )
1018 {
1019  wxStringTokenizer tokenizer( aText, " " );
1020  wxSize const dc_size = aDC.GetSize();
1021  wxSize const margin = aDC.GetTextExtent( " " );
1022  std::vector<wxString> lines;
1023  wxString line_accumulator;
1024  int total_height = 0;
1025 
1026  while( tokenizer.HasMoreTokens() )
1027  {
1028  wxString word = tokenizer.GetNextToken();
1029  wxSize linesize = aDC.GetTextExtent( line_accumulator + " " + word );
1030 
1031  if( linesize.x >= dc_size.x - margin.x && !line_accumulator.IsEmpty() )
1032  {
1033  lines.push_back( line_accumulator );
1034  line_accumulator = word;
1035  }
1036  else
1037  {
1038  line_accumulator += " ";
1039  line_accumulator += word;
1040  }
1041  }
1042 
1043  if( !line_accumulator.IsEmpty() )
1044  {
1045  lines.push_back( line_accumulator );
1046  }
1047 
1048  for( auto const& line: lines )
1049  {
1050  wxSize linesize = aDC.GetTextExtent( line );
1051  total_height += linesize.y;
1052  }
1053 
1054  int top = ( dc_size.y - total_height ) / 2;
1055  int pos = top;
1056 
1057  for( auto const& line: lines )
1058  {
1059  wxSize linesize = aDC.GetTextExtent( line );
1060  aDC.DrawText( line, ( dc_size.x - linesize.x ) / 2, pos );
1061  pos += linesize.y;
1062  }
1063 }
GR_DRAWMODE g_XorMode
Definition: gr_basic.cpp:70
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:118
static int ycliplo
Definition: gr_basic.cpp:89
static const bool FILLED
Definition: gr_basic.cpp:39
void GRDrawWrappedText(wxDC &aDC, wxString const &aText)
Draw text centered on a wxDC with wrapping.
Definition: gr_basic.cpp:1017
static int ycliphi
Definition: gr_basic.cpp:91
void GRSFilledRect(EDA_RECT *aClipBox, wxDC *aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, COLOR4D aBgColor)
Definition: gr_basic.cpp:917
int GetX() const
Definition: eda_rect.h:111
static bool clipCircle(EDA_RECT *aClipBox, int xc, int yc, int r, int aWidth)
Definition: gr_basic.cpp:564
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Converts Bezier curve to a polygon.
void GRPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, int width, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:540
void GRBezier(EDA_RECT *aClipBox, wxDC *aDC, std::vector< wxPoint > &aPoint, int aWidth, COLOR4D aColor)
Draw cubic (4 points: start control1, control2, end) bezier curve.
Definition: gr_basic.cpp:990
void GRFilledRect(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:882
static void GRSPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, int width, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:471
int GetWidth() const
Definition: eda_rect.h:119
void GRCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, int aPenSize, COLOR4D Color)
Definition: gr_basic.cpp:310
void GRDrawAnchor(EDA_RECT *aClipBox, wxDC *aDC, int x, int y, int aSize, COLOR4D aColor)
Definition: gr_basic.cpp:1003
Definition: color4d.h:45
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
void GRFilledCircle(EDA_RECT *ClipBox, wxDC *DC, int x, int y, int r, int width, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:617
static int xcliphi
Definition: gr_basic.cpp:90
static int GRLastMoveToX
Definition: gr_basic.cpp:85
bool Contains(const wxPoint &aPoint) const
Function Contains.
Definition: eda_rect.cpp:57
bool ClipLine(const EDA_RECT *aClipBox, int &x1, int &y1, int &x2, int &y2)
Test if any part of a line falls within the bounds of a rectangle.
int GetBottom() const
Definition: eda_rect.h:124
void GRLineTo(EDA_RECT *ClipBox, wxDC *DC, int x, int y, int width, COLOR4D Color)
Definition: gr_basic.cpp:264
static int xcliplo
Definition: gr_basic.cpp:88
static const bool NOT_FILLED
Definition: gr_basic.cpp:40
void GRRect(EDA_RECT *aClipBox, wxDC *aDC, int x1, int y1, int x2, int y2, COLOR4D aColor)
Definition: gr_basic.cpp:842
static int GRLastMoveToY
Definition: gr_basic.cpp:85
#define NULL
void GRForceBlackPen(bool flagforce)
Function GRForceBlackPen.
Definition: gr_basic.cpp:199
GR_DRAWMODE
Drawmode. Compositing mode plus a flag or two.
Definition: gr_basic.h:39
void GRFillCSegm(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color)
Definition: gr_basic.cpp:413
static void WinClipAndDrawLine(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width)
Definition: gr_basic.cpp:98
static wxDC * s_DC_lastDC
Definition: gr_basic.cpp:96
void GRArc(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, double StAngle, double EndAngle, int r, COLOR4D Color)
Definition: gr_basic.cpp:747
int GetRight() const
Definition: eda_rect.h:121
static void GRSClosedPoly(EDA_RECT *aClipBox, wxDC *aDC, int aPointCount, const wxPoint *aPoints, bool aFill, int aWidth, COLOR4D aColor, COLOR4D aBgColor)
Definition: gr_basic.cpp:503
a few functions useful in geometry calculations.
static bool IsGRSPolyDrawable(EDA_RECT *ClipBox, int n, const wxPoint *Points)
Definition: gr_basic.cpp:429
void GRLine(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color, wxPenStyle aStyle)
Definition: gr_basic.cpp:228
int GetHeight() const
Definition: eda_rect.h:120
static COLOR4D s_DC_lastbrushcolor(0, 0, 0, 0)
static COLOR4D s_DC_lastcolor(0, 0, 0, 0)
void GRFilledArc(EDA_RECT *ClipBox, wxDC *DC, int x, int y, double StAngle, double EndAngle, int r, int width, COLOR4D Color, COLOR4D BgColor)
Definition: gr_basic.cpp:687
void GRSetBrush(wxDC *DC, COLOR4D Color, bool fill)
Definition: gr_basic.cpp:168
void GRPutPixel(EDA_RECT *ClipBox, wxDC *DC, int x, int y, COLOR4D Color)
Definition: gr_basic.cpp:215
void GRMoveTo(int x, int y)
Definition: gr_basic.cpp:254
static void ClipAndDrawPoly(EDA_RECT *ClipBox, wxDC *DC, const wxPoint *Points, int n)
Function ClipAndDrawPoly Used to clip a polygon and draw it as Filled Polygon uses the Sutherland and...
Definition: gr_basic.cpp:955
Bezier curves to polygon converter.
Definition: bezier_curves.h:35
void GRFilledSegment(EDA_RECT *aClipBox, wxDC *aDC, wxPoint aStart, wxPoint aEnd, int aWidth, COLOR4D aColor)
Definition: gr_basic.cpp:421
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void GRCircle(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, int r, int width, COLOR4D Color)
Definition: gr_basic.cpp:594
void GRSetColorPen(wxDC *DC, COLOR4D Color, int width, wxPenStyle style)
Function GRSetColorPen sets a pen style, width, color, and alpha into the given device context.
Definition: gr_basic.cpp:131
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:68
int GetY() const
Definition: eda_rect.h:112
void GRLineArray(EDA_RECT *aClipBox, wxDC *aDC, std::vector< wxPoint > &aLines, int aWidth, COLOR4D aColor)
Function GRLineArray draws an array of lines (not a polygon).
Definition: gr_basic.cpp:282
static void GRSRect(EDA_RECT *aClipBox, wxDC *aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, wxPenStyle aStyle=wxPENSTYLE_SOLID)
Definition: gr_basic.cpp:903
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:162
bool GetGRForceBlackPenState(void)
Function GetGRForceBlackPenState.
Definition: gr_basic.cpp:209
BASE_SCREEN class implementation.
void GRClosedPoly(EDA_RECT *ClipBox, wxDC *DC, int n, const wxPoint *Points, bool Fill, COLOR4D Color, COLOR4D BgColor)
Function GRClosedPoly draws a closed polygon onto the drawing context aDC and optionally fills and/or...
Definition: gr_basic.cpp:550
static bool s_DC_lastbrushfill
Definition: gr_basic.cpp:95
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:363
static bool s_ForceBlackPen
Definition: gr_basic.cpp:86
void GRRectPs(EDA_RECT *aClipBox, wxDC *aDC, const EDA_RECT &aRect, COLOR4D aColor, wxPenStyle aStyle)
Definition: gr_basic.cpp:848
void GRArc1(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int xc, int yc, COLOR4D Color)
Definition: gr_basic.cpp:638
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:100