KiCad PCB EDA Suite
opengl_gal.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) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5  * Copyright (C) 2012-2018 Kicad Developers, see AUTHORS.txt for contributors.
6  * Copyright (C) 2013-2017 CERN
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
8  *
9  * Graphics Abstraction Layer (GAL) for OpenGL
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include <gal/opengl/opengl_gal.h>
30 #include <gal/opengl/utils.h>
31 #include <gal/definitions.h>
32 #include <gl_context_mgr.h>
34 #include <text_utils.h>
35 #include <bitmap_base.h>
36 
37 #include <macros.h>
38 
39 #ifdef __WXDEBUG__
40 #include <profile.h>
41 #include <wx/log.h>
42 #endif /* __WXDEBUG__ */
43 
44 #include <limits>
45 #include <functional>
46 using namespace std::placeholders;
47 using namespace KIGFX;
48 
49 // A ugly workaround to avoid serious issues (crashes) when using bitmaps cache
50 // to speedup redraw.
51 // issues arise when using bitmaps in page layout, when the page layout containd bitmaps,
52 // and is common to schematic and board editor,
53 // and the schematic is a hierarchy and when using cross-probing
54 // When the cross probing from pcbnew to eeschema switches to a sheet, the bitmaps cache
55 // becomes broken (in fact the associated texture).
56 // I hope (JPC) it will be fixed later, but a slighty slower refresh is better than a crash
57 #define DISABLE_BITMAP_CACHE
58 
59 // The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
60 // (see ubuntu-font-licence-1.0.txt for details)
61 #include "gl_resources.h"
62 #include "gl_builtin_shaders.h"
63 using namespace KIGFX::BUILTIN_FONT;
64 
65 static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
66 static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
67 
68 wxGLContext* OPENGL_GAL::glMainContext = NULL;
69 int OPENGL_GAL::instanceCounter = 0;
70 GLuint OPENGL_GAL::fontTexture = 0;
71 bool OPENGL_GAL::isBitmapFontLoaded = false;
72 
73 namespace KIGFX {
75 {
76 public:
78  {
79  }
80 
81  ~GL_BITMAP_CACHE();
82 
83  GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
84 
85 private:
86 
88  {
89  GLuint id;
90  int w, h;
91  };
92 
93  GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
94 
95  std::map<const BITMAP_BASE*, CACHED_BITMAP> m_bitmaps;
96 };
97 
98 };
99 
100 
101 GL_BITMAP_CACHE::~GL_BITMAP_CACHE()
102 {
103  for ( auto b = m_bitmaps.begin(); b != m_bitmaps.end(); ++b )
104  glDeleteTextures( 1, &b->second.id );
105 }
106 
107 
108 GLuint GL_BITMAP_CACHE::RequestBitmap( const BITMAP_BASE* aBitmap )
109 {
110  auto it = m_bitmaps.find( aBitmap) ;
111 
112  if ( it != m_bitmaps.end() )
113  {
114  // A bitmap is found in cache bitmap.
115  // Ensure the associated texture is still valide (can be destoyed somewhere)
116  if( glIsTexture( it->second.id ) )
117  return it->second.id;
118 
119  // else if not valid, it will be recreated.
120  }
121 
122  return cacheBitmap( aBitmap );
123 }
124 
125 
126 GLuint GL_BITMAP_CACHE::cacheBitmap( const BITMAP_BASE* aBitmap )
127 {
128  CACHED_BITMAP bmp;
129 
130  bmp.w = aBitmap->GetSizePixels().x;
131  bmp.h = aBitmap->GetSizePixels().y;
132 
133  // The bitmap size needs to be a multiple of 4.
134  // This is easiest to achieve by ensuring that each row
135  // has a multiple of 4 pixels
136  int extra_w = bmp.w % 4;
137 
138  if( extra_w )
139  extra_w = 4 - extra_w;
140 
141  GLuint textureID;
142  glGenTextures(1, &textureID);
143 
144  // make_unique initializes this to 0, so extra pixels are transparent
145  auto buf = std::make_unique<uint8_t[]>( ( bmp.w + extra_w ) * bmp.h * 4 );
146  const wxImage& imgData = *aBitmap->GetImageData();
147 
148  for( int y = 0; y < bmp.h; y++ )
149  {
150  for( int x = 0; x < bmp.w; x++ )
151  {
152  uint8_t *p = buf.get() + ( ( bmp.w + extra_w ) * y + x ) * 4;
153 
154  p[0] = imgData.GetRed( x, y );
155  p[1] = imgData.GetGreen( x, y );
156  p[2] = imgData.GetBlue( x, y );
157 
158  if( imgData.HasAlpha() )
159  p[3] = imgData.GetAlpha( x, y );
160  else if( imgData.HasMask() && p[0] == imgData.GetMaskRed() &&
161  p[1] == imgData.GetMaskGreen() && p[2] == imgData.GetMaskBlue() )
162  p[3] = wxALPHA_TRANSPARENT;
163  else
164  p[3] = wxALPHA_OPAQUE;
165  }
166  }
167 
168  glBindTexture( GL_TEXTURE_2D, textureID );
169  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w + extra_w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf.get() );
170 
171  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
172  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
173 
174  bmp.id = textureID;
175 
176 #ifndef DISABLE_BITMAP_CACHE
177  m_bitmaps[ aBitmap ] = bmp;
178 #endif
179 
180  return textureID;
181 }
182 
183 OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
184  wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
185  const wxString& aName ) :
186  GAL( aDisplayOptions ),
187  HIDPI_GL_CANVAS( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
188  wxEXPAND, aName ),
189  mouseListener( aMouseListener ),
190  paintListener( aPaintListener ),
191  currentManager( nullptr ),
192  cachedManager( nullptr ),
193  nonCachedManager( nullptr ),
194  overlayManager( nullptr ),
195  mainBuffer( 0 ),
196  overlayBuffer( 0 ),
197  isContextLocked( false ),
198  lockClientCookie( 0 )
199 {
200 // IsDisplayAttr() handles WX_GL_{MAJOR,MINOR}_VERSION correctly only in 3.0.4
201 // starting with 3.1.0 one should use wxGLContext::IsOk() (done by GL_CONTEXT_MANAGER)
202 #if wxCHECK_VERSION( 3, 0, 3 ) and !wxCHECK_VERSION( 3, 1, 0 )
203  const int attr[] = { WX_GL_MAJOR_VERSION, 2, WX_GL_MINOR_VERSION, 1, 0 };
204 
205  if( !IsDisplaySupported( attr ) )
206  throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
207 #endif /* wxCHECK_VERSION( 3, 0, 3 ) */
208 
209  if( glMainContext == NULL )
210  {
212 
213  if( !glMainContext )
214  throw std::runtime_error( "Could not create the main OpenGL context" );
215 
217  }
218  else
219  {
221 
222  if( !glPrivContext )
223  throw std::runtime_error( "Could not create a private OpenGL context" );
224  }
225 
226  shader = new SHADER();
227  ++instanceCounter;
228 
229  bitmapCache.reset( new GL_BITMAP_CACHE );
230 
233 
234  // Initialize the flags
235  isFramebufferInitialized = false;
236  isBitmapFontInitialized = false;
237  isInitialized = false;
238  isGrouping = false;
239  groupCounter = 0;
240 
241  // Connecting the event handlers
242  Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
243 
244  // Mouse events are skipped to the parent
245  Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
246  Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
247  Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
248  Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
249  Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
250  Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
251  Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
252  Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
253  Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
254  Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
255  Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
256 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
257  Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
258 #endif
259 #if defined _WIN32 || defined _WIN64
260  Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
261 #endif
262 
263  SetSize( aParent->GetClientSize() );
265 
266  // Grid color settings are different in Cairo and OpenGL
267  SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
268  SetAxesColor( COLOR4D( BLUE ) );
269 
270  // Tesselator initialization
271  tesselator = gluNewTess();
273 
274  if( tesselator == NULL )
275  throw std::runtime_error( "Could not create the tesselator" );
276 
277  gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
278 
280 }
281 
282 
284 {
286 
287  --instanceCounter;
288  glFlush();
289  gluDeleteTess( tesselator );
290  ClearCache();
291 
292  delete compositor;
293 
294  if( isInitialized )
295  {
296  delete cachedManager;
297  delete nonCachedManager;
298  delete overlayManager;
299  }
300 
302 
303  // If it was the main context, then it will be deleted
304  // when the last OpenGL GAL instance is destroyed (a few lines below)
307 
308  delete shader;
309 
310  // Are we destroying the last GAL instance?
311  if( instanceCounter == 0 )
312  {
314 
315  if( isBitmapFontLoaded )
316  {
317  glDeleteTextures( 1, &fontTexture );
318  isBitmapFontLoaded = false;
319  }
320 
323  glMainContext = NULL;
324  }
325 }
326 
327 
329 {
330  bool refresh = false;
331 
333  {
335  isFramebufferInitialized = false;
336  refresh = true;
337  }
338 
340  {
342  refresh = true;
343  }
344 
345  if( super::updatedGalDisplayOptions( aOptions ) || refresh )
346  {
347  Refresh();
348  refresh = true;
349  }
350 
351  return refresh;
352 }
353 
354 
356 {
357  auto matrix = GetScreenWorldMatrix();
358  return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
359 }
360 
361 
363 {
364  auto sf = GetBackingScaleFactor();
365  return VECTOR2D( 2.0 / (double) ( screenSize.x * sf ), 2.0 / (double) ( screenSize.y * sf ) );
366 }
367 
368 
370 {
371 #ifdef __WXDEBUG__
372  PROF_COUNTER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
373 #endif /* __WXDEBUG__ */
374 
375  wxASSERT_MSG( isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
376  "Calling GAL::beginDrawing() directly is not allowed." );
377 
378  wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
379  "Other drawing routines will expect everything to be initialized "
380  "which will not be the case." );
381 
382  if( !isInitialized )
383  init();
384 
385  // Set up the view port
386  glMatrixMode( GL_PROJECTION );
387  glLoadIdentity();
388 
389  // Create the screen transformation (Do the RH-LH conversion here)
390  glOrtho( 0, (GLint) screenSize.x, (GLsizei) screenSize.y, 0, -depthRange.x, -depthRange.y );
391 
393  {
394  // Prepare rendering target buffers
398 
400  }
401 
402  compositor->Begin();
403 
404  // Disable 2D Textures
405  glDisable( GL_TEXTURE_2D );
406 
407  glShadeModel( GL_FLAT );
408 
409  // Enable the depth buffer
410  glEnable( GL_DEPTH_TEST );
411  glDepthFunc( GL_LESS );
412 
413  // Setup blending, required for transparent objects
414  glEnable( GL_BLEND );
415  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
416 
417  glMatrixMode( GL_MODELVIEW );
418 
419  // Set up the world <-> screen transformation
421  GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
422  matrixData[0] = worldScreenMatrix.m_data[0][0];
423  matrixData[1] = worldScreenMatrix.m_data[1][0];
424  matrixData[2] = worldScreenMatrix.m_data[2][0];
425  matrixData[4] = worldScreenMatrix.m_data[0][1];
426  matrixData[5] = worldScreenMatrix.m_data[1][1];
427  matrixData[6] = worldScreenMatrix.m_data[2][1];
428  matrixData[12] = worldScreenMatrix.m_data[0][2];
429  matrixData[13] = worldScreenMatrix.m_data[1][2];
430  matrixData[14] = worldScreenMatrix.m_data[2][2];
431  glLoadMatrixd( matrixData );
432 
433  // Set defaults
436 
437  // Remove all previously stored items
440 
444 
446  {
447  // Keep bitmap font texture always bound to the second texturing unit
448  const GLint FONT_TEXTURE_UNIT = 2;
449 
450  // Either load the font atlas to video memory, or simply bind it to a texture unit
451  if( !isBitmapFontLoaded )
452  {
453  glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
454  glGenTextures( 1, &fontTexture );
455  glBindTexture( GL_TEXTURE_2D, fontTexture );
456  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height,
457  0, GL_RGB, GL_UNSIGNED_BYTE, font_image.pixels );
458  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
459  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
460  checkGlError( "loading bitmap font" );
461 
462  glActiveTexture( GL_TEXTURE0 );
463 
464  isBitmapFontLoaded = true;
465  }
466  else
467  {
468  glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
469  glBindTexture( GL_TEXTURE_2D, fontTexture );
470  glActiveTexture( GL_TEXTURE0 );
471  }
472 
473  // Set shader parameter
474  GLint ufm_fontTexture = shader->AddParameter( "fontTexture" );
475  GLint ufm_fontTextureWidth = shader->AddParameter( "fontTextureWidth" );
476  ufm_worldPixelSize = shader->AddParameter( "worldPixelSize" );
477  ufm_screenPixelSize = shader->AddParameter( "screenPixelSize" );
478  ufm_pixelSizeMultiplier = shader->AddParameter( "pixelSizeMultiplier" );
479 
480  shader->Use();
481  shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
482  shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
483  shader->Deactivate();
484  checkGlError( "setting bitmap font sampler as shader parameter" );
485 
487  }
488 
489  shader->Use();
492  double pixelSizeMultiplier = compositor->GetAntialiasSupersamplingFactor();
493  shader->SetParameter( ufm_pixelSizeMultiplier, (float) pixelSizeMultiplier );
494  shader->Deactivate();
495 
496  // Something betreen BeginDrawing and EndDrawing seems to depend on
497  // this texture unit being active, but it does not assure it itself.
498  glActiveTexture( GL_TEXTURE0 );
499 
500  // Unbind buffers - set compositor for direct drawing
502 
503 #ifdef __WXDEBUG__
504  totalRealTime.Stop();
505  wxLogTrace( "GAL_PROFILE", wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ), totalRealTime.msecs() );
506 #endif /* __WXDEBUG__ */
507 }
508 
509 
511 {
512  wxASSERT_MSG( isContextLocked, "What happened to the context lock?" );
513 
514 #ifdef __WXDEBUG__
515  PROF_COUNTER totalRealTime( "OPENGL_GAL::endDrawing()", true );
516 #endif /* __WXDEBUG__ */
517 
518  // Cached & non-cached containers are rendered to the same buffer
522 
523  // Overlay container is rendered to a different buffer
526 
527  // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
528  glColor4d( 1.0, 1.0, 1.0, 1.0 );
529 
530  // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
533  compositor->Present();
534  blitCursor();
535 
536  SwapBuffers();
537 
538 #ifdef __WXDEBUG__
539  totalRealTime.Stop();
540  wxLogTrace( "GAL_PROFILE", wxT( "OPENGL_GAL::endDrawing(): %.1f ms" ), totalRealTime.msecs() );
541 #endif /* __WXDEBUG__ */
542 }
543 
544 
545 void OPENGL_GAL::lockContext( int aClientCookie )
546 {
547  wxASSERT_MSG( !isContextLocked, "Context already locked." );
548  isContextLocked = true;
549  lockClientCookie = aClientCookie;
550 
552 }
553 
554 
555 void OPENGL_GAL::unlockContext( int aClientCookie )
556 {
557  wxASSERT_MSG( isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
558  "be stacked rather than making separate lock/unlock calls." );
559 
560  wxASSERT_MSG( lockClientCookie == aClientCookie, "Context was locked by a different client. "
561  "Should not be possible with RAII objects." );
562 
563  isContextLocked = false;
564 
566 }
567 
568 
570 {
571  wxASSERT_MSG( isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
572  "Calling this from anywhere else is not allowed." );
573 
574  wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
575  "Other update routines will expect everything to be initialized "
576  "which will not be the case." );
577 
578  if( !isInitialized )
579  init();
580 
581  cachedManager->Map();
582 }
583 
584 
586 {
587  if( !isInitialized )
588  return;
589 
590  cachedManager->Unmap();
591 }
592 
593 
594 void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
595 {
597 
598  drawLineQuad( aStartPoint, aEndPoint );
599 }
600 
601 
602 void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
603  double aWidth )
604 {
605  if( aStartPoint == aEndPoint ) // 0 length segments are just a circle.
606  {
607  DrawCircle( aStartPoint, aWidth/2 );
608  return;
609  }
610 
611  if( isFillEnabled || aWidth == 1.0 )
612  {
614 
615  SetLineWidth( aWidth );
616  drawLineQuad( aStartPoint, aEndPoint );
617  }
618  else
619  {
620  auto startEndVector = aEndPoint - aStartPoint;
621  auto lineAngle = startEndVector.Angle();
622  // Outlined tracks
623  double lineLength = startEndVector.EuclideanNorm();
624 
625  SetLineWidth( 1.0 );
627 
628  Save();
629 
630  currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
631  currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f );
632 
633  drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ),
634  VECTOR2D( lineLength, aWidth / 2.0 ) );
635 
636  drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ),
637  VECTOR2D( lineLength, -aWidth / 2.0 ) );
638 
639  // Draw line caps
640  drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 );
641  drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 );
642 
643  Restore();
644  }
645 }
646 
647 
648 void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
649 {
650  if( isFillEnabled )
651  {
652  currentManager->Reserve( 3 );
654 
655  /* Draw a triangle that contains the circle, then shade it leaving only the circle.
656  * Parameters given to Shader() are indices of the triangle's vertices
657  * (if you want to understand more, check the vertex shader source [shader.vert]).
658  * Shader uses this coordinates to determine if fragments are inside the circle or not.
659  * Does the calculations in the vertex shader now (pixel alignment)
660  * v2
661  * /\
662  * //\\
663  * v0 /_\/_\ v1
664  */
665  currentManager->Shader( SHADER_FILLED_CIRCLE, 1.0, aRadius );
666  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, layerDepth );
667 
668  currentManager->Shader( SHADER_FILLED_CIRCLE, 2.0, aRadius );
669  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, layerDepth );
670 
671  currentManager->Shader( SHADER_FILLED_CIRCLE, 3.0, aRadius );
672  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, layerDepth );
673  }
674  if( isStrokeEnabled )
675  {
676  currentManager->Reserve( 3 );
678 
679  /* Draw a triangle that contains the circle, then shade it leaving only the circle.
680  * Parameters given to Shader() are indices of the triangle's vertices
681  * (if you want to understand more, check the vertex shader source [shader.vert]).
682  * and the line width. Shader uses this coordinates to determine if fragments are
683  * inside the circle or not.
684  * v2
685  * /\
686  * //\\
687  * v0 /_\/_\ v1
688  */
690  currentManager->Vertex( aCenterPoint.x, // v0
691  aCenterPoint.y, layerDepth );
692 
694  currentManager->Vertex( aCenterPoint.x, // v1
695  aCenterPoint.y, layerDepth );
696 
698  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, // v2
699  layerDepth );
700  }
701 }
702 
703 
704 void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
705  double aEndAngle )
706 {
707  if( aRadius <= 0 )
708  return;
709 
710  // Swap the angles, if start angle is greater than end angle
711  SWAP( aStartAngle, >, aEndAngle );
712 
713  const double alphaIncrement = calcAngleStep( aRadius );
714 
715  Save();
716  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
717 
718  if( isFillEnabled )
719  {
720  double alpha;
723 
724  // Triangle fan
725  for( alpha = aStartAngle; ( alpha + alphaIncrement ) < aEndAngle; )
726  {
727  currentManager->Reserve( 3 );
728  currentManager->Vertex( 0.0, 0.0, layerDepth );
729  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
730  alpha += alphaIncrement;
731  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
732  }
733 
734  // The last missing triangle
735  const VECTOR2D endPoint( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
736 
737  currentManager->Reserve( 3 );
738  currentManager->Vertex( 0.0, 0.0, layerDepth );
739  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
740  currentManager->Vertex( endPoint.x, endPoint.y, layerDepth );
741  }
742 
743  if( isStrokeEnabled )
744  {
746 
747  VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
748  double alpha;
749 
750  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
751  {
752  VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
753  DrawLine( p, p_next );
754 
755  p = p_next;
756  }
757 
758  // Draw the last missing part
759  if( alpha != aEndAngle )
760  {
761  VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
762  DrawLine( p, p_last );
763  }
764  }
765 
766  Restore();
767 }
768 
769 
770 void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
771  double aEndAngle, double aWidth )
772 {
773  if( aRadius <= 0 )
774  {
775  // Arcs of zero radius are a circle of aWidth diameter
776  if( aWidth > 0 )
777  DrawCircle( aCenterPoint, aWidth / 2.0 );
778 
779  return;
780  }
781 
782  // Swap the angles, if start angle is greater than end angle
783  SWAP( aStartAngle, >, aEndAngle );
784 
785  const double alphaIncrement = calcAngleStep( aRadius );
786 
787  Save();
788  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
789 
790  if( isStrokeEnabled )
791  {
793 
794  double width = aWidth / 2.0;
795  VECTOR2D startPoint( cos( aStartAngle ) * aRadius,
796  sin( aStartAngle ) * aRadius );
797  VECTOR2D endPoint( cos( aEndAngle ) * aRadius,
798  sin( aEndAngle ) * aRadius );
799 
800  drawStrokedSemiCircle( startPoint, width, aStartAngle + M_PI );
801  drawStrokedSemiCircle( endPoint, width, aEndAngle );
802 
803  VECTOR2D pOuter( cos( aStartAngle ) * ( aRadius + width ),
804  sin( aStartAngle ) * ( aRadius + width ) );
805 
806  VECTOR2D pInner( cos( aStartAngle ) * ( aRadius - width ),
807  sin( aStartAngle ) * ( aRadius - width ) );
808 
809  double alpha;
810 
811  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
812  {
813  VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
814  sin( alpha ) * ( aRadius + width ) );
815  VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
816  sin( alpha ) * ( aRadius - width ) );
817 
818  DrawLine( pOuter, pNextOuter );
819  DrawLine( pInner, pNextInner );
820 
821  pOuter = pNextOuter;
822  pInner = pNextInner;
823  }
824 
825  // Draw the last missing part
826  if( alpha != aEndAngle )
827  {
828  VECTOR2D pLastOuter( cos( aEndAngle ) * ( aRadius + width ),
829  sin( aEndAngle ) * ( aRadius + width ) );
830  VECTOR2D pLastInner( cos( aEndAngle ) * ( aRadius - width ),
831  sin( aEndAngle ) * ( aRadius - width ) );
832 
833  DrawLine( pOuter, pLastOuter );
834  DrawLine( pInner, pLastInner );
835  }
836  }
837 
838  if( isFillEnabled )
839  {
841  SetLineWidth( aWidth );
842 
843  VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
844  double alpha;
845 
846  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
847  {
848  VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
849  DrawLine( p, p_next );
850 
851  p = p_next;
852  }
853 
854  // Draw the last missing part
855  if( alpha != aEndAngle )
856  {
857  VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
858  DrawLine( p, p_last );
859  }
860  }
861 
862  Restore();
863 }
864 
865 
866 void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
867 {
868  // Compute the diagonal points of the rectangle
869  VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
870  VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
871 
872  // Fill the rectangle
873  if( isFillEnabled )
874  {
875  currentManager->Reserve( 6 );
878 
879  currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
880  currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, layerDepth );
881  currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
882 
883  currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
884  currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
885  currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, layerDepth );
886  }
887 
888  // Stroke the outline
889  if( isStrokeEnabled )
890  {
892 
893  std::deque<VECTOR2D> pointList;
894  pointList.push_back( aStartPoint );
895  pointList.push_back( diagonalPointA );
896  pointList.push_back( aEndPoint );
897  pointList.push_back( diagonalPointB );
898  pointList.push_back( aStartPoint );
899  DrawPolyline( pointList );
900  }
901 }
902 
903 
904 void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
905 {
906  drawPolyline( [&](int idx) { return aPointList[idx]; }, aPointList.size() );
907 }
908 
909 
910 void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
911 {
912  drawPolyline( [&](int idx) { return aPointList[idx]; }, aListSize );
913 }
914 
915 
917 {
918  auto numPoints = aLineChain.PointCount();
919 
920  if( aLineChain.IsClosed() )
921  numPoints += 1;
922 
923  drawPolyline( [&](int idx) { return aLineChain.CPoint(idx); }, numPoints );
924 }
925 
926 
927 void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
928 {
929  auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
930  GLdouble* ptr = points.get();
931 
932  for( const VECTOR2D& p : aPointList )
933  {
934  *ptr++ = p.x;
935  *ptr++ = p.y;
936  *ptr++ = layerDepth;
937  }
938 
939  drawPolygon( points.get(), aPointList.size() );
940 }
941 
942 
943 void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
944 {
945  auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
946  GLdouble* target = points.get();
947  const VECTOR2D* src = aPointList;
948 
949  for( int i = 0; i < aListSize; ++i )
950  {
951  *target++ = src->x;
952  *target++ = src->y;
953  *target++ = layerDepth;
954  ++src;
955  }
956 
957  drawPolygon( points.get(), aListSize );
958 }
959 
960 
962 {
965 
966  if( isFillEnabled )
967  {
968  for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
969  {
970  auto triPoly = aPolySet.TriangulatedPolygon( j );
971 
972  for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
973  {
974  VECTOR2I a, b, c;
975  triPoly->GetTriangle( i, a, b, c );
976  currentManager->Vertex( a.x, a.y, layerDepth );
977  currentManager->Vertex( b.x, b.y, layerDepth );
978  currentManager->Vertex( c.x, c.y, layerDepth );
979  }
980  }
981  }
982 
983  if( isStrokeEnabled )
984  {
985  for( int j = 0; j < aPolySet.OutlineCount(); ++j )
986  {
987  const auto& poly = aPolySet.Polygon( j );
988 
989  for( const auto& lc : poly )
990  {
991  DrawPolyline( lc );
992  }
993  }
994  }
995 }
996 
997 
999 {
1000  if ( aPolySet.IsTriangulationUpToDate() )
1001  {
1002  drawTriangulatedPolyset( aPolySet );
1003  return;
1004  }
1005 
1006  for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1007  {
1008  const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1009  DrawPolygon( outline );
1010  }
1011 }
1012 
1013 
1014 
1016 {
1017  if( aPolygon.SegmentCount() == 0 )
1018  return;
1019 
1020  const int pointCount = aPolygon.SegmentCount() + 1;
1021  std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1022  GLdouble* ptr = points.get();
1023 
1024  for( int i = 0; i < pointCount; ++i )
1025  {
1026  const VECTOR2I& p = aPolygon.CPoint( i );
1027  *ptr++ = p.x;
1028  *ptr++ = p.y;
1029  *ptr++ = layerDepth;
1030  }
1031 
1032  drawPolygon( points.get(), pointCount );
1033 }
1034 
1035 
1036 void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1037  const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
1038 {
1039  // FIXME The drawing quality needs to be improved
1040  // FIXME Perhaps choose a quad/triangle strip instead?
1041  // FIXME Brute force method, use a better (recursive?) algorithm
1042 
1043  std::deque<VECTOR2D> pointList;
1044 
1045  double t = 0.0;
1046  double dt = 1.0 / (double) CURVE_POINTS;
1047 
1048  for( int i = 0; i <= CURVE_POINTS; i++ )
1049  {
1050  double omt = 1.0 - t;
1051  double omt2 = omt * omt;
1052  double omt3 = omt * omt2;
1053  double t2 = t * t;
1054  double t3 = t * t2;
1055 
1056  VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA
1057  + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint;
1058 
1059  pointList.push_back( vertex );
1060 
1061  t += dt;
1062  }
1063 
1064  DrawPolyline( pointList );
1065 }
1066 
1067 
1068 void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap )
1069 {
1070  // We have to calculate the pixel size in users units to draw the image.
1071  // worldUnitLength is a factor used for converting IU to inches
1072  double scale = 1.0 / ( aBitmap.GetPPI() * worldUnitLength );
1073  double w = (double) aBitmap.GetSizePixels().x * scale;
1074  double h = (double) aBitmap.GetSizePixels().y * scale;
1075 
1076  auto xform = currentManager->GetTransformation();
1077 
1078  glm::vec4 v0 = xform * glm::vec4( -w/2, -h/2, 0.0, 0.0 );
1079  glm::vec4 v1 = xform * glm::vec4( w/2, h/2, 0.0, 0.0 );
1080  glm::vec4 trans = xform[3];
1081 
1082  auto texture_id = bitmapCache->RequestBitmap( &aBitmap );
1083 
1084  if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1085  return;
1086 
1087  auto oldTarget = GetTarget();
1088 
1089  glPushMatrix();
1090  glTranslated( trans.x, trans.y, trans.z );
1091 
1093  glEnable(GL_TEXTURE_2D);
1094  glActiveTexture( GL_TEXTURE0 );
1095  glBindTexture( GL_TEXTURE_2D, texture_id );
1096 
1097  glBegin( GL_QUADS );
1098  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1099  glTexCoord2f( 0.0, 0.0 );
1100  glVertex3f( v0.x, v0.y, layerDepth );
1101  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1102  glTexCoord2f( 1.0, 0.0 );
1103  glVertex3f( v1.x, v0.y, layerDepth );
1104  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1105  glTexCoord2f( 1.0, 1.0 );
1106  glVertex3f( v1.x, v1.y, layerDepth );
1107  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1108  glTexCoord2f( 0.0, 1.0 );
1109  glVertex3f( v0.x, v1.y, layerDepth );
1110  glEnd();
1111 
1112  SetTarget( oldTarget );
1113  glBindTexture( GL_TEXTURE_2D, 0 );
1114 
1115 #ifdef DISABLE_BITMAP_CACHE
1116  glDeleteTextures( 1, &texture_id );
1117 #endif
1118 
1119  glPopMatrix();
1120 }
1121 
1122 
1123 void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2D& aPosition,
1124  double aRotationAngle )
1125 {
1126  wxASSERT_MSG( !IsTextMirrored(), "No support for mirrored text using bitmap fonts." );
1127 
1128  auto processedText = ProcessOverbars( aText );
1129  const auto& text = processedText.first;
1130  const auto& overbars = processedText.second;
1131 
1132  // Compute text size, so it can be properly justified
1133  VECTOR2D textSize;
1134  float commonOffset;
1135  std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1136 
1137  const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1138  bool overbar = false;
1139 
1140  int overbarLength = 0;
1141  double overbarHeight = textSize.y;
1142 
1143  Save();
1144 
1146  currentManager->Translate( aPosition.x, aPosition.y, layerDepth );
1147  currentManager->Rotate( aRotationAngle, 0.0f, 0.0f, -1.0f );
1148 
1149  double sx = SCALE * ( globalFlipX ? -1.0 : 1.0 );
1150  double sy = SCALE * ( globalFlipY ? -1.0 : 1.0 );
1151 
1152  currentManager->Scale( sx, sy, 0 );
1153  currentManager->Translate( 0, -commonOffset, 0 );
1154 
1155  switch( GetHorizontalJustify() )
1156  {
1158  Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1159  break;
1160 
1162  //if( !IsTextMirrored() )
1163  Translate( VECTOR2D( -textSize.x, 0 ) );
1164  break;
1165 
1166  case GR_TEXT_HJUSTIFY_LEFT:
1167  //if( IsTextMirrored() )
1168  //Translate( VECTOR2D( -textSize.x, 0 ) );
1169  break;
1170  }
1171 
1172  switch( GetVerticalJustify() )
1173  {
1174  case GR_TEXT_VJUSTIFY_TOP:
1175  Translate( VECTOR2D( 0, -textSize.y ) );
1176  overbarHeight = -textSize.y / 2.0;
1177  break;
1178 
1180  Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1181  overbarHeight = 0;
1182  break;
1183 
1185  break;
1186  }
1187 
1188  int i = 0;
1189 
1190  for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1191  {
1192  unsigned int c = *chIt;
1193  wxASSERT_MSG( c != '\n' && c != '\r', wxT( "No support for multiline bitmap text yet" ) );
1194 
1195  // Handle overbar
1196  if( overbars[i] && !overbar )
1197  {
1198  overbar = true; // beginning of an overbar
1199  }
1200  else if( overbar && !overbars[i] )
1201  {
1202  overbar = false; // end of an overbar
1203  drawBitmapOverbar( overbarLength, overbarHeight );
1204  overbarLength = 0;
1205  }
1206 
1207  if( overbar )
1208  overbarLength += drawBitmapChar( c );
1209  else
1210  drawBitmapChar( c );
1211 
1212  ++i;
1213  }
1214 
1215  // Handle the case when overbar is active till the end of the drawn text
1216  currentManager->Translate( 0, commonOffset, 0 );
1217 
1218  if( overbar && overbarLength > 0 )
1219  drawBitmapOverbar( overbarLength, overbarHeight );
1220 
1221  Restore();
1222 }
1223 
1224 
1226 {
1229 
1231 
1232  // sub-pixel lines all render the same
1233  float minorLineWidth =
1234  std::fmax( 1.0f, gridLineWidth ) * getWorldPixelSize() / GetBackingScaleFactor();
1235  float majorLineWidth = minorLineWidth * 2.0f;
1236 
1237  // Draw the axis and grid
1238  // For the drawing the start points, end points and increments have
1239  // to be calculated in world coordinates
1240  VECTOR2D worldStartPoint = screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1241  VECTOR2D worldEndPoint = screenWorldMatrix * VECTOR2D( screenSize );
1242 
1243  // Draw axes if desired
1244  if( axesEnabled )
1245  {
1246  SetLineWidth( minorLineWidth );
1248 
1249  DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1250  DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1251  }
1252 
1253  // force flush
1255 
1256  if( !gridVisibility )
1257  return;
1258 
1259  int gridScreenSizeDense = gridSize.x;
1260  int gridScreenSizeCoarse = KiROUND( gridSize.x * static_cast<double>( gridTick ) );
1261 
1262  double gridThreshold = KiROUND( computeMinGridSpacing() / worldScale );
1263 
1265  gridThreshold *= 2.0;
1266 
1267  // If we cannot display the grid density, scale down by a tick size and
1268  // try again. Eventually, we get some representation of the grid
1269  while( std::min( gridScreenSizeDense, gridScreenSizeCoarse ) <= gridThreshold )
1270  {
1271  gridScreenSizeCoarse *= gridTick;
1272  gridScreenSizeDense *= gridTick;
1273  }
1274 
1275  // Compute grid staring and ending indexes to draw grid points on the
1276  // visible screen area
1277  // Note: later any point coordinate will be offsetted by gridOrigin
1278  int gridStartX = KiROUND( ( worldStartPoint.x - gridOrigin.x ) / gridScreenSizeDense );
1279  int gridEndX = KiROUND( ( worldEndPoint.x - gridOrigin.x ) / gridScreenSizeDense );
1280  int gridStartY = KiROUND( ( worldStartPoint.y - gridOrigin.y ) / gridScreenSizeDense );
1281  int gridEndY = KiROUND( ( worldEndPoint.y - gridOrigin.y ) / gridScreenSizeDense );
1282 
1283  // Ensure start coordinate > end coordinate
1284  SWAP( gridStartX, >, gridEndX );
1285  SWAP( gridStartY, >, gridEndY );
1286 
1287  // Ensure the grid fills the screen
1288  --gridStartX; ++gridEndX;
1289  --gridStartY; ++gridEndY;
1290 
1291  glDisable( GL_DEPTH_TEST );
1292  glDisable( GL_TEXTURE_2D );
1293 
1294  if( gridStyle == GRID_STYLE::DOTS )
1295  {
1296  glEnable( GL_STENCIL_TEST );
1297  glStencilFunc( GL_ALWAYS, 1, 1 );
1298  glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1299  glColor4d( 0.0, 0.0, 0.0, 0.0 );
1300  SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1301  }
1302  else
1303  {
1304  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1306  }
1307 
1309  {
1310 
1311  // Vertical positions
1312  for( int j = gridStartY; j <= gridEndY; j++ )
1313  {
1314  bool tickY = ( j % gridTick == 0 );
1315  int posY = j * gridScreenSizeDense + gridOrigin.y;
1316 
1317  // Horizontal positions
1318  for( int i = gridStartX; i <= gridEndX; i++ )
1319  {
1320  bool tickX = ( i % gridTick == 0 );
1321  SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1322  auto lineLen = 2.0 * GetLineWidth();
1323  auto posX = i * gridScreenSizeDense + gridOrigin.x;
1324 
1325  DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1326  DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1327  }
1328  }
1329 
1331  }
1332  else
1333  {
1334  // Vertical lines
1335  for( int j = gridStartY; j <= gridEndY; j++ )
1336  {
1337  const double y = j * gridScreenSizeDense + gridOrigin.y;
1338 
1339  // If axes are drawn, skip the lines that would cover them
1340  if( axesEnabled && y == 0 )
1341  continue;
1342 
1343  SetLineWidth( ( j % gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1344  VECTOR2D a ( gridStartX * gridScreenSizeDense + gridOrigin.x, y );
1345  VECTOR2D b ( gridEndX * gridScreenSizeDense + gridOrigin.x, y );
1346 
1347  DrawLine( a, b );
1348  }
1349 
1351 
1352  if( gridStyle == GRID_STYLE::DOTS )
1353  {
1354  glStencilFunc( GL_NOTEQUAL, 0, 1 );
1355  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1357  }
1358 
1359  // Horizontal lines
1360  for( int i = gridStartX; i <= gridEndX; i++ )
1361  {
1362  const double x = i * gridScreenSizeDense + gridOrigin.x;
1363 
1364  // If axes are drawn, skip the lines that would cover them
1365  if( axesEnabled && x == 0 )
1366  continue;
1367 
1368  SetLineWidth( ( i % gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1369  VECTOR2D a ( x, gridStartY * gridScreenSizeDense + gridOrigin.y );
1370  VECTOR2D b ( x, gridEndY * gridScreenSizeDense + gridOrigin.y );
1371  DrawLine( a, b );
1372  }
1373 
1375 
1376  if( gridStyle == GRID_STYLE::DOTS )
1377  glDisable( GL_STENCIL_TEST );
1378  }
1379 
1380  glEnable( GL_DEPTH_TEST );
1381  glEnable( GL_TEXTURE_2D );
1382 }
1383 
1384 
1385 void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1386 {
1387  screenSize = VECTOR2I( aWidth, aHeight );
1388 
1389  // Resize framebuffers
1390  const float scaleFactor = GetBackingScaleFactor();
1391  compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1392  isFramebufferInitialized = false;
1393 
1394  wxGLCanvas::SetSize( aWidth, aHeight );
1395 }
1396 
1397 
1398 bool OPENGL_GAL::Show( bool aShow )
1399 {
1400  bool s = wxGLCanvas::Show( aShow );
1401 
1402  if( aShow )
1403  wxGLCanvas::Raise();
1404 
1405  return s;
1406 }
1407 
1408 
1410 {
1411  glFlush();
1412 }
1413 
1414 
1416 {
1417  // Clear screen
1419  // NOTE: Black used here instead of m_clearColor; it will be composited later
1420  glClearColor( 0, 0, 0, 1 );
1421  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1422 }
1423 
1424 
1425 void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1426 {
1427  GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1428 
1429  matrixData[0] = aTransformation.m_data[0][0];
1430  matrixData[1] = aTransformation.m_data[1][0];
1431  matrixData[2] = aTransformation.m_data[2][0];
1432  matrixData[4] = aTransformation.m_data[0][1];
1433  matrixData[5] = aTransformation.m_data[1][1];
1434  matrixData[6] = aTransformation.m_data[2][1];
1435  matrixData[12] = aTransformation.m_data[0][2];
1436  matrixData[13] = aTransformation.m_data[1][2];
1437  matrixData[14] = aTransformation.m_data[2][2];
1438 
1439  glMultMatrixd( matrixData );
1440 }
1441 
1442 
1443 void OPENGL_GAL::Rotate( double aAngle )
1444 {
1445  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1446 }
1447 
1448 
1449 void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1450 {
1451  currentManager->Translate( aVector.x, aVector.y, 0.0f );
1452 }
1453 
1454 
1455 void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1456 {
1457  currentManager->Scale( aScale.x, aScale.y, 0.0f );
1458 }
1459 
1460 
1462 {
1464 }
1465 
1466 
1468 {
1470 }
1471 
1472 
1474 {
1475  isGrouping = true;
1476 
1477  std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *cachedManager );
1478  int groupNumber = getNewGroupNumber();
1479  groups.insert( std::make_pair( groupNumber, newItem ) );
1480 
1481  return groupNumber;
1482 }
1483 
1484 
1486 {
1488  isGrouping = false;
1489 }
1490 
1491 
1492 void OPENGL_GAL::DrawGroup( int aGroupNumber )
1493 {
1494  if( groups[aGroupNumber] )
1495  cachedManager->DrawItem( *groups[aGroupNumber] );
1496 }
1497 
1498 
1499 void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1500 {
1501  if( groups[aGroupNumber] )
1502  cachedManager->ChangeItemColor( *groups[aGroupNumber], aNewColor );
1503 }
1504 
1505 
1506 void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1507 {
1508  if( groups[aGroupNumber] )
1509  cachedManager->ChangeItemDepth( *groups[aGroupNumber], aDepth );
1510 }
1511 
1512 
1513 void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1514 {
1515  // Frees memory in the container as well
1516  groups.erase( aGroupNumber );
1517 }
1518 
1519 
1521 {
1522  bitmapCache.reset( new GL_BITMAP_CACHE );
1523 
1524  groups.clear();
1525 
1526  if( isInitialized )
1527  cachedManager->Clear();
1528 }
1529 
1530 
1532 {
1533  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1534 }
1535 
1536 
1538 {
1539  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1540 }
1541 
1542 
1544 {
1545  switch( aTarget )
1546  {
1547  default:
1548  case TARGET_CACHED:
1550  break;
1551 
1552  case TARGET_NONCACHED:
1554  break;
1555 
1556  case TARGET_OVERLAY:
1558  break;
1559  }
1560 
1561  currentTarget = aTarget;
1562 }
1563 
1564 
1566 {
1567  return currentTarget;
1568 }
1569 
1570 
1572 {
1573  // Save the current state
1574  unsigned int oldTarget = compositor->GetBuffer();
1575 
1576  switch( aTarget )
1577  {
1578  // Cached and noncached items are rendered to the same buffer
1579  default:
1580  case TARGET_CACHED:
1581  case TARGET_NONCACHED:
1583  break;
1584 
1585  case TARGET_OVERLAY:
1587  break;
1588  }
1589 
1590 
1591  if( aTarget != TARGET_OVERLAY )
1593  else
1595 
1596  // Restore the previous state
1597  compositor->SetBuffer( oldTarget );
1598 }
1599 
1600 
1601 void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
1602 {
1603  // Now we should only store the position of the mouse cursor
1604  // The real drawing routines are in blitCursor()
1605  //VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition;
1606  //cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
1607  cursorPosition = aCursorPosition;
1608 }
1609 
1610 
1611 void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1612 {
1613  /* Helper drawing: ____--- v3 ^
1614  * ____---- ... \ \
1615  * ____---- ... \ end \
1616  * v1 ____---- ... ____---- \ width
1617  * ---- ...___---- \ \
1618  * \ ___...-- \ v
1619  * \ ____----... ____---- v2
1620  * ---- ... ____----
1621  * start \ ... ____----
1622  * \... ____----
1623  * ----
1624  * v0
1625  * dots mark triangles' hypotenuses
1626  */
1627 
1628  auto v1 = currentManager->GetTransformation() * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
1629  auto v2 = currentManager->GetTransformation() * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
1630 
1631  VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
1632 
1633  currentManager->Reserve( 6 );
1634 
1635  // Line width is maintained by the vertex shader
1637  currentManager->Vertex( aStartPoint, layerDepth );
1638 
1640  currentManager->Vertex( aStartPoint, layerDepth );
1641 
1643  currentManager->Vertex( aEndPoint, layerDepth );
1644 
1646  currentManager->Vertex( aEndPoint, layerDepth );
1647 
1649  currentManager->Vertex( aEndPoint, layerDepth );
1650 
1652  currentManager->Vertex( aStartPoint, layerDepth );
1653 }
1654 
1655 
1656 void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
1657 {
1658  if( isFillEnabled )
1659  {
1661  drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
1662  }
1663 
1664  if( isStrokeEnabled )
1665  {
1667  drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
1668  }
1669 }
1670 
1671 
1672 void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1673  double aAngle )
1674 {
1675  Save();
1676 
1677  currentManager->Reserve( 3 );
1678  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1679  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1680 
1681  /* Draw a triangle that contains the semicircle, then shade it to leave only
1682  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1683  * (if you want to understand more, check the vertex shader source [shader.vert]).
1684  * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
1685  * v2
1686  * /\
1687  * /__\
1688  * v0 //__\\ v1
1689  */
1691  currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1692 
1694  currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1695 
1697  currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth ); // v2
1698 
1699  Restore();
1700 }
1701 
1702 
1703 void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1704  double aAngle )
1705 {
1706  double outerRadius = aRadius + ( lineWidth / 2 );
1707 
1708  Save();
1709 
1710  currentManager->Reserve( 3 );
1711  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1712  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1713 
1714  /* Draw a triangle that contains the semicircle, then shade it to leave only
1715  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1716  * (if you want to understand more, check the vertex shader source [shader.vert]), the
1717  * radius and the line width. Shader uses these coordinates to determine if fragments are
1718  * inside the semicircle or not.
1719  * v2
1720  * /\
1721  * /__\
1722  * v0 //__\\ v1
1723  */
1725  currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1726 
1728  currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1729 
1731  currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth ); // v2
1732 
1733  Restore();
1734 }
1735 
1736 
1737 void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
1738 {
1739  if( isFillEnabled )
1740  {
1743 
1744  // Any non convex polygon needs to be tesselated
1745  // for this purpose the GLU standard functions are used
1747  gluTessBeginPolygon( tesselator, &params );
1748  gluTessBeginContour( tesselator );
1749 
1750  GLdouble* point = aPoints;
1751 
1752  for( int i = 0; i < aPointCount; ++i )
1753  {
1754  gluTessVertex( tesselator, point, point );
1755  point += 3; // 3 coordinates
1756  }
1757 
1758  gluTessEndContour( tesselator );
1759  gluTessEndPolygon( tesselator );
1760 
1761  // Free allocated intersecting points
1762  tessIntersects.clear();
1763  }
1764 
1765  if( isStrokeEnabled )
1766  {
1767  drawPolyline( [&](int idx) { return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] ); },
1768  aPointCount );
1769  }
1770 }
1771 
1772 
1773 void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D (int)>& aPointGetter, int aPointCount )
1774 {
1775  if( aPointCount < 2 )
1776  return;
1777 
1779  int i;
1780 
1781  for( i = 1; i < aPointCount; ++i )
1782  {
1783  auto start = aPointGetter( i - 1 );
1784  auto end = aPointGetter( i );
1785 
1786  drawLineQuad( start, end );
1787  }
1788 }
1789 
1790 
1791 int OPENGL_GAL::drawBitmapChar( unsigned long aChar )
1792 {
1793  const float TEX_X = font_image.width;
1794  const float TEX_Y = font_image.height;
1795 
1796  // handle space
1797  if( aChar == ' ' )
1798  {
1799  const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
1800  wxASSERT( g );
1801  Translate( VECTOR2D( g->advance, 0 ) );
1802  return g->advance;
1803  }
1804 
1805  const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
1806 
1807  // If the glyph is not found (happens for many esotheric unicode chars)
1808  // shows a '?' instead.
1809  if( !glyph )
1810  glyph = LookupGlyph( '?' );
1811 
1812  if( !glyph ) // Should not happen.
1813  return 0;
1814 
1815  const float X = glyph->atlas_x + font_information.smooth_pixels;
1816  const float Y = glyph->atlas_y + font_information.smooth_pixels;
1817  const float XOFF = glyph->minx;
1818 
1819  // adjust for height rounding
1820  const float round_adjust = ( glyph->maxy - glyph->miny )
1821  - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
1822  const float top_adjust = font_information.max_y - glyph->maxy;
1823  const float YOFF = round_adjust + top_adjust;
1824  const float W = glyph->atlas_w - font_information.smooth_pixels *2;
1825  const float H = glyph->atlas_h - font_information.smooth_pixels *2;
1826  const float B = 0;
1827 
1828  currentManager->Reserve( 6 );
1829  Translate( VECTOR2D( XOFF, YOFF ) );
1830  /* Glyph:
1831  * v0 v1
1832  * +--+
1833  * | /|
1834  * |/ |
1835  * +--+
1836  * v2 v3
1837  */
1838  currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
1839  currentManager->Vertex( -B, -B, 0 ); // v0
1840 
1841  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1842  currentManager->Vertex( W + B, -B, 0 ); // v1
1843 
1844  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1845  currentManager->Vertex( -B, H + B, 0 ); // v2
1846 
1847 
1848  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1849  currentManager->Vertex( W + B, -B, 0 ); // v1
1850 
1851  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1852  currentManager->Vertex( -B, H + B, 0 ); // v2
1853 
1854  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
1855  currentManager->Vertex( W + B, H + B, 0 ); // v3
1856 
1857  Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
1858 
1859  return glyph->advance;
1860 }
1861 
1862 
1863 void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight )
1864 {
1865  // To draw an overbar, simply draw an overbar
1866  const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
1867  wxCHECK( glyph, /* void */ );
1868 
1869  const float H = glyph->maxy - glyph->miny;
1870 
1871  Save();
1872 
1873  Translate( VECTOR2D( -aLength, -aHeight-1.5*H ) );
1874 
1875  currentManager->Reserve( 6 );
1877 
1878  currentManager->Shader( 0 );
1879 
1880  currentManager->Vertex( 0, 0, 0 ); // v0
1881  currentManager->Vertex( aLength, 0, 0 ); // v1
1882  currentManager->Vertex( 0, H, 0 ); // v2
1883 
1884  currentManager->Vertex( aLength, 0, 0 ); // v1
1885  currentManager->Vertex( 0, H, 0 ); // v2
1886  currentManager->Vertex( aLength, H, 0 ); // v3
1887 
1888  Restore();
1889 }
1890 
1891 
1892 std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
1893 {
1894  VECTOR2D textSize( 0, 0 );
1895  float commonOffset = std::numeric_limits<float>::max();
1896  static const auto defaultGlyph = LookupGlyph( '(' ); // for strange chars
1897 
1898  for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
1899  {
1900  unsigned int c = *chIt;
1901 
1902  const FONT_GLYPH_TYPE* glyph = LookupGlyph( c );
1903  // Debug: show not coded char in the atlas
1904  // Be carefull before allowing the assert: it usually crash kicad
1905  // when the assert is made during a paint event.
1906  // wxASSERT_MSG( glyph, wxString::Format( "missing char in font: code 0x%x <%c>", c, c ) );
1907 
1908  if( !glyph || // Not coded in font
1909  c == '-' || c == '_' ) // Strange size of these 2 chars
1910  {
1911  glyph = defaultGlyph;
1912  }
1913 
1914  if( glyph )
1915  {
1916  textSize.x += glyph->advance;
1917  }
1918  }
1919 
1920  textSize.y = std::max<float>( textSize.y, font_information.max_y - defaultGlyph->miny );
1921  commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
1922  textSize.y -= commonOffset;
1923 
1924  return std::make_pair( textSize, commonOffset );
1925 }
1926 
1927 
1928 void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
1929 {
1930  PostPaint();
1931 }
1932 
1933 
1934 void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1935 {
1936  // Post the mouse event to the event listener registered in constructor, if any
1937  if( mouseListener )
1938  wxPostEvent( mouseListener, aEvent );
1939 }
1940 
1941 
1943 {
1944  if( !IsCursorEnabled() )
1945  return;
1946 
1948 
1949  const int cursorSize = fullscreenCursor ? 8000 : 80;
1950 
1951  VECTOR2D cursorBegin = cursorPosition - cursorSize / ( 2 * worldScale );
1952  VECTOR2D cursorEnd = cursorPosition + cursorSize / ( 2 * worldScale );
1953  VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
1954 
1955  const COLOR4D cColor = getCursorColor();
1956  const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a,
1957  cColor.b * cColor.a, 1.0 );
1958 
1959  glActiveTexture( GL_TEXTURE0 );
1960  glDisable( GL_TEXTURE_2D );
1961  glLineWidth( 1.0 );
1962  glColor4d( color.r, color.g, color.b, color.a );
1963 
1964  glBegin( GL_LINES );
1965  glVertex2d( cursorCenter.x, cursorBegin.y );
1966  glVertex2d( cursorCenter.x, cursorEnd.y );
1967 
1968  glVertex2d( cursorBegin.x, cursorCenter.y );
1969  glVertex2d( cursorEnd.x, cursorCenter.y );
1970  glEnd();
1971 }
1972 
1973 
1975 {
1976  wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
1977  wxT( "There are no free slots to store a group" ) );
1978 
1979  while( groups.find( groupCounter ) != groups.end() )
1980  {
1981  groupCounter++;
1982  }
1983 
1984  return groupCounter++;
1985 }
1986 
1987 
1989 {
1990  wxASSERT( IsShownOnScreen() );
1991 
1992  wxASSERT_MSG( isContextLocked, "This should only be called from within a locked context." );
1993 
1994  GLenum err = glewInit();
1995 
1996  if( GLEW_OK != err )
1997  throw std::runtime_error( (const char*) glewGetErrorString( err ) );
1998 
1999  // Check the OpenGL version (minimum 2.1 is required)
2000  if( !GLEW_VERSION_2_1 )
2001  throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2002 
2003 #if defined (__LINUX__) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2004 #ifdef DEBUG
2005  if( GLEW_ARB_debug_output )
2006  enableGlDebug( true );
2007 #endif
2008 #endif
2009 
2010  // Framebuffers have to be supported
2011  if( !GLEW_EXT_framebuffer_object )
2012  throw std::runtime_error( "Framebuffer objects are not supported!" );
2013 
2014  // Vertex buffer has to be supported
2015  if( !GLEW_ARB_vertex_buffer_object )
2016  throw std::runtime_error( "Vertex buffer objects are not supported!" );
2017 
2018  // Prepare shaders
2020  throw std::runtime_error( "Cannot compile vertex shader!" );
2021 
2023  throw std::runtime_error( "Cannot compile fragment shader!" );
2024 
2025  if( !shader->IsLinked() && !shader->Link() )
2026  throw std::runtime_error( "Cannot link the shaders!" );
2027 
2028  // Check if video card supports textures big enough to fit the font atlas
2029  int maxTextureSize;
2030  glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2031 
2032  if( maxTextureSize < (int) font_image.width || maxTextureSize < (int)font_image.height )
2033  {
2034  // TODO implement software texture scaling
2035  // for bitmap fonts and use a higher resolution texture?
2036  throw std::runtime_error( "Requested texture size is not supported" );
2037  }
2038 
2039  cachedManager = new VERTEX_MANAGER( true );
2040  nonCachedManager = new VERTEX_MANAGER( false );
2041  overlayManager = new VERTEX_MANAGER( false );
2042 
2043  // Make VBOs use shaders
2047 
2048  isInitialized = true;
2049 }
2050 
2051 
2052 // ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11
2053 void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2054 {
2055  GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2056  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2057  VERTEX_MANAGER* vboManager = param->vboManager;
2058 
2059  assert( vboManager );
2060  vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2061 }
2062 
2063 
2064 void CALLBACK CombineCallback( GLdouble coords[3],
2065  GLdouble* vertex_data[4],
2066  GLfloat weight[4], GLdouble** dataOut, void* aData )
2067 {
2068  GLdouble* vertex = new GLdouble[3];
2069  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2070 
2071  // Save the pointer so we can delete it later
2072  param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) );
2073 
2074  memcpy( vertex, coords, 3 * sizeof(GLdouble) );
2075 
2076  *dataOut = vertex;
2077 }
2078 
2079 
2080 void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2081 {
2082  // This callback is needed to force GLU tesselator to use triangles only
2083 }
2084 
2085 
2086 void CALLBACK ErrorCallback( GLenum aErrorCode )
2087 {
2088  //throw std::runtime_error( std::string( "Tessellation error: " ) +
2089  //std::string( (const char*) gluErrorString( aErrorCode ) );
2090 }
2091 
2092 
2093 static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2094 {
2095  gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback );
2096  gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback );
2097  gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback );
2098  gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback );
2099 }
2100 
2101 void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2102 {
2103  cachedManager->EnableDepthTest( aEnabled );
2104  nonCachedManager->EnableDepthTest( aEnabled );
2105  overlayManager->EnableDepthTest( aEnabled );
2106 }
2107 
2108 
2109 static double roundr( double f, double r )
2110 {
2111  return floor(f / r + 0.5) * r;
2112 }
2113 
2114 
2116 {
2117  auto pixelSize = worldScale;
2118 
2119  lookAtPoint.x = roundr( lookAtPoint.x, pixelSize );
2120  lookAtPoint.y = roundr( lookAtPoint.y, pixelSize );
2121 
2123 }
2124 
Definition: colors.h:57
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:83
int gridTick
Every tick line gets the double width.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
Class UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion supp...
Definition: utf8.h:73
VERTEX_MANAGER * currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs)
Definition: opengl_gal.h:303
virtual wxSize GetNativePixelSize() const
float GetLineWidth() const
Get the line width.
virtual void beginUpdate() override
Definition: opengl_gal.cpp:569
VECTOR2D cursorPosition
Current cursor position (world coordinates)
bool IsLinked() const
Returns true if shaders are linked correctly.
Definition: shader.h:124
const glm::mat4 & GetTransformation() const
bool axesEnabled
Should the axes be drawn.
virtual void DrawBitmap(const BITMAP_BASE &aBitmap) override
Draw a bitmap image.
int OutlineCount() const
Returns the number of outlines in the set
bool LoadShaderFromStrings(SHADER_TYPE aShaderType, Args &&... aArgs)
Add a shader and compile the shader sources.
Definition: shader.h:99
virtual void DrawBuffer(unsigned int aBufferHandle) override
Function DrawBuffer() draws the selected buffer to the output buffer.
static wxGLContext * glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:291
std::pair< UTF8, std::vector< bool > > ProcessOverbars(const UTF8 &aText)
Processes a text to extract the raw text and overbar flags.
Definition: text_utils.cpp:27
double layerDepth
The actual layer depth.
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:57
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:118
void PushMatrix()
Function PushMatrix() pushes the current transformation matrix stack.
int GetAntialiasSupersamplingFactor() const
unsigned int overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:311
std::deque< boost::shared_array< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:281
virtual void Scale(const VECTOR2D &aScale) override
Scale the context.
const MATRIX3x3D & GetScreenWorldMatrix() const
Get the screen <-> world transformation matrix.
virtual void ClearTarget(RENDER_TARGET aTarget) override
Clears the target for rendering.
bool IsTextMirrored() const
Returns true if text should displayed mirrored.
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
virtual void Translate(const VECTOR2D &aTranslation) override
Translate the context.
virtual void Begin() override
Function Begin() Call this at the beginning of each frame.
bool Reserve(unsigned int aSize)
Function Reserve() allocates space for vertices, so it will be used with subsequent Vertex() calls.
void BeginDrawing() const
Function BeginDrawing() prepares buffers and items to start drawing.
RENDER_TARGET currentTarget
Current rendering target.
Definition: opengl_gal.h:312
bool isBitmapFontInitialized
Is the shader set to use bitmap fonts?
Definition: opengl_gal.h:320
void Use()
Use the shader.
Definition: shader.h:132
void drawPolygon(GLdouble *aPoints, int aPointCount)
Draws a filled polygon.
void DrawItem(const VERTEX_ITEM &aItem) const
Function DrawItem() draws an item to the buffer.
static GLuint fontTexture
Bitmap font texture handle (shared)
Definition: opengl_gal.h:297
GRID_STYLE gridStyle
Grid display style.
void drawLineQuad(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a quad for the line.
VERTEX_MANAGER * overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:306
GAL_DISPLAY_OPTIONS & options
int color
Definition: DXF_plotter.cpp:62
VECTOR2D getScreenPixelSize() const
Definition: opengl_gal.cpp:362
virtual void ComputeWorldScreenMatrix() override
Compute the world <-> screen transformation matrix.
virtual void Restore() override
Restore the context.
void CALLBACK EdgeCallback(GLboolean aEdgeFlag)
void SetParameter(int aParameterNumber, float aValue) const
Set a parameter of the shader.
Definition: shader.cpp:138
virtual void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Changes the depth (Z-axis position) of the group.
wxImage * GetImageData()
Definition: bitmap_base.h:78
std::unique_ptr< GL_BITMAP_CACHE > bitmapCache
Definition: opengl_gal.h:330
virtual void EnableDepthTest(bool aEnabled=false) override
Parameters passed to the GLU tesselator.
bool Vertex(const VERTEX &aVertex)
Function Vertex() adds a vertex with the given coordinates to the currently set item.
static const COLOR4D BLACK
Definition: color4d.h:319
wxGLCanvas wrapper for HiDPI/Retina support.
uni_iter uend() const
Function uend returns a uni_iter initialized to the end of "this" UTF8 byte sequence.
Definition: utf8.h:294
virtual int BeginGroup() override
Begin a group.
static void InitTesselatorCallbacks(GLUtesselator *aTesselator)
virtual void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition: opengl_gal.cpp:927
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
double GetScaleFactor() const
Get the current scale factor.
void Rotate(GLfloat aAngle, GLfloat aX, GLfloat aY, GLfloat aZ)
Function Rotate() multiplies the current matrix by a rotation matrix, so the newly vertices will be r...
bool isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:324
double g
Green component.
Definition: color4d.h:310
Fragment shader.
Definition: shader.h:47
bool Link()
Link the shaders.
Definition: shader.cpp:97
COLOR4D fillColor
The fill color.
VECTOR2D depthRange
Range of the depth.
int checkGlError(const std::string &aInfo, bool aThrow)
Checks if one of recent OpenGL operations has failed.
Definition: utils.cpp:30
MATRIX3x3D screenWorldMatrix
Screen transformation.
SHADER * shader
There is only one shader used for different objects.
Definition: opengl_gal.h:315
bool globalFlipX
Flag for X axis flipping.
const FONT_GLYPH_TYPE * LookupGlyph(unsigned int aCodepoint)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
static const unsigned int DIRECT_RENDERING
int PointCount() const
Function PointCount()
bool isGrouping
Was a group started?
Definition: opengl_gal.h:323
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:45
EDA_TEXT_HJUSTIFY_T GetHorizontalJustify() const
Returns current text horizontal justification setting.
OPENGL_ANTIALIASING_MODE gl_antialiasing_mode
#define abs(a)
Definition: auxiliary.h:84
void UnlockCtx(wxGLContext *aContext)
Function UnlockCtx allows other canvases to bind an OpenGL context.
void enableGlDebug(bool aEnable)
Enables/disables OpenGL driver messages output.
Definition: utils.cpp:140
virtual void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
bool IsTriangulationUpToDate() const
VECTOR2D lookAtPoint
Point to be looked at in world space.
void unlockContext(int aClientCookie) override
Definition: opengl_gal.cpp:555
void CALLBACK VertexCallback(GLvoid *aVertexPtr, void *aData)
virtual void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
VERTEX_MANAGER * nonCachedManager
Container for storing non-cached VERTEX_ITEMs.
Definition: opengl_gal.h:305
double b
Blue component.
Definition: color4d.h:311
Auxiliary rendering target (noncached)
Definition: definitions.h:49
static int instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:293
This file contains miscellaneous commonly used macros and functions.
VERTEX_MANAGER * vboManager
Manager used for storing new vertices.
Definition: opengl_gal.h:278
T m_data[3][3]
Definition: matrix3x3.h:64
double getWorldPixelSize() const
Definition: opengl_gal.cpp:355
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
virtual void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle, double aWidth) override
Draw an arc segment.
Definition: opengl_gal.cpp:770
virtual RENDER_TARGET GetTarget() const override
Gets the currently used target for rendering.
static const int CURVE_POINTS
The number of points for curve approximation.
Definition: opengl_gal.h:289
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:46
virtual void endUpdate() override
Definition: opengl_gal.cpp:585
const VECTOR2D & GetGlyphSize() const
void FinishItem() const
Function FinishItem() does the cleaning after adding an item.
static GL_CONTEXT_MANAGER & Get()
Function Get returns the GL_CONTEXT_MANAGER instance (singleton).
uni_iter ubegin() const
Function ubegin returns a uni_iter initialized to the start of "this" UTF8 byte sequence.
Definition: utf8.h:285
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
virtual void Save() override
Save the context.
void init()
Basic OpenGL initialization.
double a
Alpha component.
Definition: color4d.h:312
void Unmap()
Function Unmap() unmaps vertex buffer.
OPENGL_COMPOSITOR * compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:309
void ChangeItemDepth(const VERTEX_ITEM &aItem, GLfloat aDepth) const
Function ChangeItemDepth() changes the depth of all vertices owned by an item.
bool fullscreenCursor
Shape of the cursor (fullscreen or small cross)
bool isStrokeEnabled
Are the outlines stroked ?
VECTOR2< double > VECTOR2D
Definition: vector2d.h:586
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
GROUPS_MAP groups
Stores informations about VBO objects (groups)
Definition: opengl_gal.h:301
virtual void Present() override
Function Present() Call this to present the output buffer to the screen.
virtual void RestoreScreen() override
Restore the screen contents.
unsigned int mainBuffer
Main rendering target.
Definition: opengl_gal.h:310
virtual bool Show(bool aShow) override
Shows/hides the GAL canvas.
virtual void ClearBuffer(const COLOR4D &aColor) override
Function ClearBuffer() clears the selected buffer (set by the SetBuffer() function).
Class SHAPE_POLY_SET.
virtual void EndGroup() override
End the group.
COLOR4D axesColor
Color of the axes.
virtual void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:476
wxEvtHandler * mouseListener
Definition: opengl_gal.h:294
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Function updatedGalDisplayOptions.
Definition: opengl_gal.cpp:328
virtual void DrawGrid() override
COLOR4D strokeColor
The color of the outlines.
float gridLineWidth
Line width of the grid.
void SetShader(SHADER &aShader) const
Function SetShader() sets a shader program that is going to be used during rendering.
void Translate(GLfloat aX, GLfloat aY, GLfloat aZ)
Function Translate() multiplies the current matrix by a translation matrix, so newly vertices will be...
static const int glAttributes[]
Definition: opengl_gal.cpp:66
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: opengl_gal.cpp:594
double worldUnitLength
The unit length of the world coordinates [inch].
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
void Map()
Function Map() maps vertex buffer.
void onPaint(wxPaintEvent &aEvent)
This is the OnPaint event handler.
int AddParameter(const std::string &aParameterName)
Add a parameter to the parameter queue.
Definition: shader.cpp:125
GLint ufm_screenPixelSize
Definition: opengl_gal.h:327
virtual void endDrawing() override
Definition: opengl_gal.cpp:510
virtual void DrawGroup(int aGroupNumber) override
Draw the stored group.
void Deactivate()
Deactivate the shader and use the default OpenGL program.
Definition: shader.h:141
bool isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:318
virtual void Resize(unsigned int aWidth, unsigned int aHeight) override
Function Resize() clears the state of COMPOSITOR, so it has to be reinitialized again with the new di...
VECTOR2D gridOrigin
The grid origin.
virtual void ClearCache() override
Delete all data created during caching of graphic items.
FONT_IMAGE_TYPE font_image
Definition: gl_resources.cpp:4
unsigned char pixels[1024 *1024 *3]
Definition: gl_resources.h:38
void drawTriangulatedPolyset(const SHAPE_POLY_SET &aPoly)
Draws a set of polygons with a cached triangulation.
Definition: opengl_gal.cpp:961
FONT_INFO_TYPE font_information
Definition: gl_resources.cpp:3
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:306
MATRIX3x3D worldScreenMatrix
World transformation.
Vertex shader.
Definition: shader.h:46
virtual void SetBuffer(unsigned int aBufferHandle) override
Function SetBuffer() sets the selected buffer as the rendering target.
Class SHADER provides the access to the OpenGL shaders.
Definition: shader.h:76
string & err
Definition: json11.cpp:598
void lockContext(int aClientCookie) override
Private: use GAL_CONTEXT_LOCKER RAII object.
Definition: opengl_gal.cpp:545
int SegmentCount() const
Function SegmentCount()
void Color(const COLOR4D &aColor)
Function Color() changes currently used color that will be applied to newly added vertices.
GLUtesselator * tesselator
The tessellator.
Definition: opengl_gal.h:353
Use dots for the grid.
Use small cross instead of dots for the grid.
COLOR4D getCursorColor() const
Gets the actual cursor color to draw.
std::map< const BITMAP_BASE *, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:95
virtual void Rotate(double aAngle) override
Rotate the context.
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Function updatedGalDisplayOptions.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Function LockCtx sets a context as current and prevents other canvases from switching it.
void drawStrokedSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a stroked semicircle.
#define H(x, y, z)
Definition: md5_hash.cpp:17
void PopMatrix()
Function PopMatrix() pops the current transformation matrix stack.
float lineWidth
The line width.
bool isFillEnabled
Is filling of graphic objects enabled ?
void blitCursor()
Blits cursor into the current screen.
VERTEX_MANAGER * cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:304
class uni_iter is a non-mutating iterator that walks through unicode code points in the UTF8 encoded ...
Definition: utf8.h:207
virtual void Initialize() override
Function Reset() performs primary initialiation, necessary to use the object.
void EndDrawing() const
Function EndDrawing() finishes drawing operations.
virtual void DrawPolyline(const std::deque< VECTOR2D > &aPointList) override
Draw a polyline.
Definition: opengl_gal.cpp:904
virtual void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:602
GLint ufm_worldPixelSize
Definition: opengl_gal.h:326
std::deque< boost::shared_array< GLdouble > > tessIntersects
Storage for intersecting points.
Definition: opengl_gal.h:355
virtual void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle) override
Draw an arc.
Definition: opengl_gal.cpp:704
void CALLBACK ErrorCallback(GLenum aErrorCode)
virtual void SaveScreen() override
Save the screen contents.
virtual void SetTarget(RENDER_TARGET aTarget) override
Sets the target for rendering.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
bool isInitialized
Basic initialization flag, has to be done when the window is visible.
Definition: opengl_gal.h:321
COLOR4D gridColor
Color of the grid.
const int scale
void PostPaint()
Function PostPaint posts an event to m_paint_listener.
Definition: opengl_gal.h:253
bool gridVisibility
Should the grid be shown.
void drawSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a semicircle.
Main rendering target (cached)
Definition: definitions.h:48
void EnableDepthTest(bool aEnabled)
Function EnableDepthTest() Enables/disables Z buffer depth test.
#define max(a, b)
Definition: auxiliary.h:86
virtual float GetBackingScaleFactor() const
bool globalFlipY
Flag for Y axis flipping.
double m_scaleFactor
The pixel scale factor (>1 for hi-DPI scaled displays)
EDA_TEXT_VJUSTIFY_T GetVerticalJustify() const
Returns current text vertical justification setting.
Class SHAPE_LINE_CHAIN.
unsigned int groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:302
double worldScale
The scale factor world->screen.
int drawBitmapChar(unsigned long aChar)
Draws a single character using bitmap font.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
VECTOR2D gridSize
The grid size.
unsigned int TriangulatedPolyCount() const
Returns the number of triangulated polygons
size_t i
Definition: json11.cpp:597
void Scale(GLfloat aX, GLfloat aY, GLfloat aZ)
Function Scale() multiplies the current matrix by a scaling matrix, so the newly vertices will be sca...
wxSize GetSizePixels() const
Function GetSizePixels.
Definition: bitmap_base.h:136
virtual void ClearScreen() override
Clear the screen.
VECTOR2I screenSize
Screen size in screen coordinates.
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
unsigned int getNewGroupNumber()
Returns a valid key that can be used as a new group number.
void SetScaleFactor(double aFactor)
Set the canvas scale factor, probably for a hi-DPI display.
wxGLContext * glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:292
virtual void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
Definition: opengl_gal.cpp:866
virtual void Flush() override
Force all remaining objects to be drawn.
virtual unsigned int GetBuffer() const override
Function GetBuffer() returns currently used buffer handle.
virtual void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
virtual unsigned int CreateBuffer() override
Function CreateBuffer() prepares a new buffer that may be used as a rendering target.
void drawPolyline(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount)
Generic way of drawing a polyline stored in different containers.
double msecs() const
Definition: profile.h:144
void DestroyCtx(wxGLContext *aContext)
Function DestroyCtx destroys a managed OpenGL context.
void CALLBACK CombineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut, void *aData)
void Shader(GLfloat aShaderType, GLfloat aParam1=0.0f, GLfloat aParam2=0.0f, GLfloat aParam3=0.0f)
Function Shader() changes currently used shader and its parameters that will be applied to newly adde...
bool IsClosed() const
Function IsClosed()
double r
Red component.
Definition: color4d.h:309
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
void ChangeItemColor(const VERTEX_ITEM &aItem, const COLOR4D &aColor) const
Function ChangeItemColor() changes the color of all vertices owned by an item.
std::pair< VECTOR2D, float > computeBitmapTextSize(const UTF8 &aText) const
Computes a size of text drawn using bitmap font with current text setting applied.
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:46
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=NULL)
Function CreateCtx creates a managed OpenGL context.
virtual void BitmapText(const wxString &aText, const VECTOR2D &aPosition, double aRotationAngle) override
Draws a text using a bitmap font.
static double roundr(double f, double r)
#define CALLBACK
Definition: opengl_gal.h:48
virtual ~OPENGL_GAL()
Definition: opengl_gal.cpp:283
bool IsVisible() const override
Definition: opengl_gal.h:99
bool IsCursorEnabled() const
Returns information about cursor visibility.
virtual void beginDrawing() override
Definition: opengl_gal.cpp:369
GLint ufm_pixelSizeMultiplier
Definition: opengl_gal.h:328
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
void Clear() const
Function Clear() removes all the stored vertices from the container.
virtual void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:648
static bool isBitmapFontLoaded
Is the bitmap font texture loaded?
Definition: opengl_gal.h:319
virtual void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint) override
Draw a cubic bezier spline.
void skipMouseEvent(wxMouseEvent &aEvent)
Skip the mouse event to the parent.
void drawFilledSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a filled semicircle.
int GetPPI() const
Definition: bitmap_base.h:148
virtual void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Changes the color used to draw the group.
#define min(a, b)
Definition: auxiliary.h:85
Class GAL is the abstract interface for drawing on a 2D-surface.
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39
void drawBitmapOverbar(double aLength, double aHeight)
Draws an overbar over the currently drawn text.