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 || gridSize.x == 0 || gridSize.y == 0 )
1257  return;
1258 
1259  VECTOR2D gridScreenSize( gridSize );
1260 
1261  double gridThreshold = computeMinGridSpacing() / worldScale;
1262 
1264  gridThreshold *= 2.0;
1265 
1266  // If we cannot display the grid density, scale down by a tick size and
1267  // try again. Eventually, we get some representation of the grid
1268  while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1269  {
1270  gridScreenSize = gridScreenSize * static_cast<double>( gridTick );
1271  }
1272 
1273  // Compute grid starting and ending indexes to draw grid points on the
1274  // visible screen area
1275  // Note: later any point coordinate will be offsetted by gridOrigin
1276  int gridStartX = KiROUND( ( worldStartPoint.x - gridOrigin.x ) / gridScreenSize.x );
1277  int gridEndX = KiROUND( ( worldEndPoint.x - gridOrigin.x ) / gridScreenSize.x );
1278  int gridStartY = KiROUND( ( worldStartPoint.y - gridOrigin.y ) / gridScreenSize.y );
1279  int gridEndY = KiROUND( ( worldEndPoint.y - gridOrigin.y ) / gridScreenSize.y );
1280 
1281  // Ensure start coordinate > end coordinate
1282  SWAP( gridStartX, >, gridEndX );
1283  SWAP( gridStartY, >, gridEndY );
1284 
1285  // Ensure the grid fills the screen
1286  --gridStartX; ++gridEndX;
1287  --gridStartY; ++gridEndY;
1288 
1289  glDisable( GL_DEPTH_TEST );
1290  glDisable( GL_TEXTURE_2D );
1291 
1292  if( gridStyle == GRID_STYLE::DOTS )
1293  {
1294  glEnable( GL_STENCIL_TEST );
1295  glStencilFunc( GL_ALWAYS, 1, 1 );
1296  glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1297  glColor4d( 0.0, 0.0, 0.0, 0.0 );
1298  SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1299  }
1300  else
1301  {
1302  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1304  }
1305 
1307  {
1308 
1309  // Vertical positions
1310  for( int j = gridStartY; j <= gridEndY; j++ )
1311  {
1312  bool tickY = ( j % gridTick == 0 );
1313  const double posY = j * gridScreenSize.y + gridOrigin.y;
1314 
1315  // Horizontal positions
1316  for( int i = gridStartX; i <= gridEndX; i++ )
1317  {
1318  bool tickX = ( i % gridTick == 0 );
1319  SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1320  auto lineLen = 2.0 * GetLineWidth();
1321  auto posX = i * gridScreenSize.x + gridOrigin.x;
1322 
1323  DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1324  DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1325  }
1326  }
1327 
1329  }
1330  else
1331  {
1332  // Vertical lines
1333  for( int j = gridStartY; j <= gridEndY; j++ )
1334  {
1335  const double y = j * gridScreenSize.y + gridOrigin.y;
1336 
1337  // If axes are drawn, skip the lines that would cover them
1338  if( axesEnabled && y == 0.0 )
1339  continue;
1340 
1341  SetLineWidth( ( j % gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1342  VECTOR2D a ( gridStartX * gridScreenSize.x + gridOrigin.x, y );
1343  VECTOR2D b ( gridEndX * gridScreenSize.x + gridOrigin.x, y );
1344 
1345  DrawLine( a, b );
1346  }
1347 
1349 
1350  if( gridStyle == GRID_STYLE::DOTS )
1351  {
1352  glStencilFunc( GL_NOTEQUAL, 0, 1 );
1353  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1355  }
1356 
1357  // Horizontal lines
1358  for( int i = gridStartX; i <= gridEndX; i++ )
1359  {
1360  const double x = i * gridScreenSize.x + gridOrigin.x;
1361 
1362  // If axes are drawn, skip the lines that would cover them
1363  if( axesEnabled && x == 0.0 )
1364  continue;
1365 
1366  SetLineWidth( ( i % gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1367  VECTOR2D a ( x, gridStartY * gridScreenSize.y + gridOrigin.y );
1368  VECTOR2D b ( x, gridEndY * gridScreenSize.y + gridOrigin.y );
1369  DrawLine( a, b );
1370  }
1371 
1373 
1374  if( gridStyle == GRID_STYLE::DOTS )
1375  glDisable( GL_STENCIL_TEST );
1376  }
1377 
1378  glEnable( GL_DEPTH_TEST );
1379  glEnable( GL_TEXTURE_2D );
1380 }
1381 
1382 
1383 void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1384 {
1385  screenSize = VECTOR2I( aWidth, aHeight );
1386 
1387  // Resize framebuffers
1388  const float scaleFactor = GetBackingScaleFactor();
1389  compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1390  isFramebufferInitialized = false;
1391 
1392  wxGLCanvas::SetSize( aWidth, aHeight );
1393 }
1394 
1395 
1396 bool OPENGL_GAL::Show( bool aShow )
1397 {
1398  bool s = wxGLCanvas::Show( aShow );
1399 
1400  if( aShow )
1401  wxGLCanvas::Raise();
1402 
1403  return s;
1404 }
1405 
1406 
1408 {
1409  glFlush();
1410 }
1411 
1412 
1414 {
1415  // Clear screen
1417  // NOTE: Black used here instead of m_clearColor; it will be composited later
1418  glClearColor( 0, 0, 0, 1 );
1419  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1420 }
1421 
1422 
1423 void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1424 {
1425  GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1426 
1427  matrixData[0] = aTransformation.m_data[0][0];
1428  matrixData[1] = aTransformation.m_data[1][0];
1429  matrixData[2] = aTransformation.m_data[2][0];
1430  matrixData[4] = aTransformation.m_data[0][1];
1431  matrixData[5] = aTransformation.m_data[1][1];
1432  matrixData[6] = aTransformation.m_data[2][1];
1433  matrixData[12] = aTransformation.m_data[0][2];
1434  matrixData[13] = aTransformation.m_data[1][2];
1435  matrixData[14] = aTransformation.m_data[2][2];
1436 
1437  glMultMatrixd( matrixData );
1438 }
1439 
1440 
1441 void OPENGL_GAL::Rotate( double aAngle )
1442 {
1443  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1444 }
1445 
1446 
1447 void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1448 {
1449  currentManager->Translate( aVector.x, aVector.y, 0.0f );
1450 }
1451 
1452 
1453 void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1454 {
1455  currentManager->Scale( aScale.x, aScale.y, 0.0f );
1456 }
1457 
1458 
1460 {
1462 }
1463 
1464 
1466 {
1468 }
1469 
1470 
1472 {
1473  isGrouping = true;
1474 
1475  std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *cachedManager );
1476  int groupNumber = getNewGroupNumber();
1477  groups.insert( std::make_pair( groupNumber, newItem ) );
1478 
1479  return groupNumber;
1480 }
1481 
1482 
1484 {
1486  isGrouping = false;
1487 }
1488 
1489 
1490 void OPENGL_GAL::DrawGroup( int aGroupNumber )
1491 {
1492  if( groups[aGroupNumber] )
1493  cachedManager->DrawItem( *groups[aGroupNumber] );
1494 }
1495 
1496 
1497 void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1498 {
1499  if( groups[aGroupNumber] )
1500  cachedManager->ChangeItemColor( *groups[aGroupNumber], aNewColor );
1501 }
1502 
1503 
1504 void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1505 {
1506  if( groups[aGroupNumber] )
1507  cachedManager->ChangeItemDepth( *groups[aGroupNumber], aDepth );
1508 }
1509 
1510 
1511 void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1512 {
1513  // Frees memory in the container as well
1514  groups.erase( aGroupNumber );
1515 }
1516 
1517 
1519 {
1520  bitmapCache.reset( new GL_BITMAP_CACHE );
1521 
1522  groups.clear();
1523 
1524  if( isInitialized )
1525  cachedManager->Clear();
1526 }
1527 
1528 
1530 {
1531  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1532 }
1533 
1534 
1536 {
1537  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1538 }
1539 
1540 
1542 {
1543  switch( aTarget )
1544  {
1545  default:
1546  case TARGET_CACHED:
1548  break;
1549 
1550  case TARGET_NONCACHED:
1552  break;
1553 
1554  case TARGET_OVERLAY:
1556  break;
1557  }
1558 
1559  currentTarget = aTarget;
1560 }
1561 
1562 
1564 {
1565  return currentTarget;
1566 }
1567 
1568 
1570 {
1571  // Save the current state
1572  unsigned int oldTarget = compositor->GetBuffer();
1573 
1574  switch( aTarget )
1575  {
1576  // Cached and noncached items are rendered to the same buffer
1577  default:
1578  case TARGET_CACHED:
1579  case TARGET_NONCACHED:
1581  break;
1582 
1583  case TARGET_OVERLAY:
1585  break;
1586  }
1587 
1588 
1589  if( aTarget != TARGET_OVERLAY )
1591  else
1593 
1594  // Restore the previous state
1595  compositor->SetBuffer( oldTarget );
1596 }
1597 
1598 
1599 void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
1600 {
1601  // Now we should only store the position of the mouse cursor
1602  // The real drawing routines are in blitCursor()
1603  //VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition;
1604  //cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
1605  cursorPosition = aCursorPosition;
1606 }
1607 
1608 
1609 void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1610 {
1611  /* Helper drawing: ____--- v3 ^
1612  * ____---- ... \ \
1613  * ____---- ... \ end \
1614  * v1 ____---- ... ____---- \ width
1615  * ---- ...___---- \ \
1616  * \ ___...-- \ v
1617  * \ ____----... ____---- v2
1618  * ---- ... ____----
1619  * start \ ... ____----
1620  * \... ____----
1621  * ----
1622  * v0
1623  * dots mark triangles' hypotenuses
1624  */
1625 
1626  auto v1 = currentManager->GetTransformation() * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
1627  auto v2 = currentManager->GetTransformation() * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
1628 
1629  VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
1630 
1631  currentManager->Reserve( 6 );
1632 
1633  // Line width is maintained by the vertex shader
1635  currentManager->Vertex( aStartPoint, layerDepth );
1636 
1638  currentManager->Vertex( aStartPoint, layerDepth );
1639 
1641  currentManager->Vertex( aEndPoint, layerDepth );
1642 
1644  currentManager->Vertex( aEndPoint, layerDepth );
1645 
1647  currentManager->Vertex( aEndPoint, layerDepth );
1648 
1650  currentManager->Vertex( aStartPoint, layerDepth );
1651 }
1652 
1653 
1654 void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
1655 {
1656  if( isFillEnabled )
1657  {
1659  drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
1660  }
1661 
1662  if( isStrokeEnabled )
1663  {
1665  drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
1666  }
1667 }
1668 
1669 
1670 void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1671  double aAngle )
1672 {
1673  Save();
1674 
1675  currentManager->Reserve( 3 );
1676  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1677  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1678 
1679  /* Draw a triangle that contains the semicircle, then shade it to leave only
1680  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1681  * (if you want to understand more, check the vertex shader source [shader.vert]).
1682  * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
1683  * v2
1684  * /\
1685  * /__\
1686  * v0 //__\\ v1
1687  */
1689  currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1690 
1692  currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1693 
1695  currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth ); // v2
1696 
1697  Restore();
1698 }
1699 
1700 
1701 void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1702  double aAngle )
1703 {
1704  double outerRadius = aRadius + ( lineWidth / 2 );
1705 
1706  Save();
1707 
1708  currentManager->Reserve( 3 );
1709  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1710  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1711 
1712  /* Draw a triangle that contains the semicircle, then shade it to leave only
1713  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1714  * (if you want to understand more, check the vertex shader source [shader.vert]), the
1715  * radius and the line width. Shader uses these coordinates to determine if fragments are
1716  * inside the semicircle or not.
1717  * v2
1718  * /\
1719  * /__\
1720  * v0 //__\\ v1
1721  */
1723  currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1724 
1726  currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1727 
1729  currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth ); // v2
1730 
1731  Restore();
1732 }
1733 
1734 
1735 void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
1736 {
1737  if( isFillEnabled )
1738  {
1741 
1742  // Any non convex polygon needs to be tesselated
1743  // for this purpose the GLU standard functions are used
1745  gluTessBeginPolygon( tesselator, &params );
1746  gluTessBeginContour( tesselator );
1747 
1748  GLdouble* point = aPoints;
1749 
1750  for( int i = 0; i < aPointCount; ++i )
1751  {
1752  gluTessVertex( tesselator, point, point );
1753  point += 3; // 3 coordinates
1754  }
1755 
1756  gluTessEndContour( tesselator );
1757  gluTessEndPolygon( tesselator );
1758 
1759  // Free allocated intersecting points
1760  tessIntersects.clear();
1761  }
1762 
1763  if( isStrokeEnabled )
1764  {
1765  drawPolyline( [&](int idx) { return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] ); },
1766  aPointCount );
1767  }
1768 }
1769 
1770 
1771 void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D (int)>& aPointGetter, int aPointCount )
1772 {
1773  if( aPointCount < 2 )
1774  return;
1775 
1777  int i;
1778 
1779  for( i = 1; i < aPointCount; ++i )
1780  {
1781  auto start = aPointGetter( i - 1 );
1782  auto end = aPointGetter( i );
1783 
1784  drawLineQuad( start, end );
1785  }
1786 }
1787 
1788 
1789 int OPENGL_GAL::drawBitmapChar( unsigned long aChar )
1790 {
1791  const float TEX_X = font_image.width;
1792  const float TEX_Y = font_image.height;
1793 
1794  // handle space
1795  if( aChar == ' ' )
1796  {
1797  const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
1798  wxASSERT( g );
1799  Translate( VECTOR2D( g->advance, 0 ) );
1800  return g->advance;
1801  }
1802 
1803  const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
1804 
1805  // If the glyph is not found (happens for many esotheric unicode chars)
1806  // shows a '?' instead.
1807  if( !glyph )
1808  glyph = LookupGlyph( '?' );
1809 
1810  if( !glyph ) // Should not happen.
1811  return 0;
1812 
1813  const float X = glyph->atlas_x + font_information.smooth_pixels;
1814  const float Y = glyph->atlas_y + font_information.smooth_pixels;
1815  const float XOFF = glyph->minx;
1816 
1817  // adjust for height rounding
1818  const float round_adjust = ( glyph->maxy - glyph->miny )
1819  - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
1820  const float top_adjust = font_information.max_y - glyph->maxy;
1821  const float YOFF = round_adjust + top_adjust;
1822  const float W = glyph->atlas_w - font_information.smooth_pixels *2;
1823  const float H = glyph->atlas_h - font_information.smooth_pixels *2;
1824  const float B = 0;
1825 
1826  currentManager->Reserve( 6 );
1827  Translate( VECTOR2D( XOFF, YOFF ) );
1828  /* Glyph:
1829  * v0 v1
1830  * +--+
1831  * | /|
1832  * |/ |
1833  * +--+
1834  * v2 v3
1835  */
1836  currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
1837  currentManager->Vertex( -B, -B, 0 ); // v0
1838 
1839  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1840  currentManager->Vertex( W + B, -B, 0 ); // v1
1841 
1842  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1843  currentManager->Vertex( -B, H + B, 0 ); // v2
1844 
1845 
1846  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1847  currentManager->Vertex( W + B, -B, 0 ); // v1
1848 
1849  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1850  currentManager->Vertex( -B, H + B, 0 ); // v2
1851 
1852  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
1853  currentManager->Vertex( W + B, H + B, 0 ); // v3
1854 
1855  Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
1856 
1857  return glyph->advance;
1858 }
1859 
1860 
1861 void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight )
1862 {
1863  // To draw an overbar, simply draw an overbar
1864  const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
1865  wxCHECK( glyph, /* void */ );
1866 
1867  const float H = glyph->maxy - glyph->miny;
1868 
1869  Save();
1870 
1871  Translate( VECTOR2D( -aLength, -aHeight-1.5*H ) );
1872 
1873  currentManager->Reserve( 6 );
1875 
1876  currentManager->Shader( 0 );
1877 
1878  currentManager->Vertex( 0, 0, 0 ); // v0
1879  currentManager->Vertex( aLength, 0, 0 ); // v1
1880  currentManager->Vertex( 0, H, 0 ); // v2
1881 
1882  currentManager->Vertex( aLength, 0, 0 ); // v1
1883  currentManager->Vertex( 0, H, 0 ); // v2
1884  currentManager->Vertex( aLength, H, 0 ); // v3
1885 
1886  Restore();
1887 }
1888 
1889 
1890 std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
1891 {
1892  VECTOR2D textSize( 0, 0 );
1893  float commonOffset = std::numeric_limits<float>::max();
1894  static const auto defaultGlyph = LookupGlyph( '(' ); // for strange chars
1895 
1896  for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
1897  {
1898  unsigned int c = *chIt;
1899 
1900  const FONT_GLYPH_TYPE* glyph = LookupGlyph( c );
1901  // Debug: show not coded char in the atlas
1902  // Be carefull before allowing the assert: it usually crash kicad
1903  // when the assert is made during a paint event.
1904  // wxASSERT_MSG( glyph, wxString::Format( "missing char in font: code 0x%x <%c>", c, c ) );
1905 
1906  if( !glyph || // Not coded in font
1907  c == '-' || c == '_' ) // Strange size of these 2 chars
1908  {
1909  glyph = defaultGlyph;
1910  }
1911 
1912  if( glyph )
1913  {
1914  textSize.x += glyph->advance;
1915  }
1916  }
1917 
1918  textSize.y = std::max<float>( textSize.y, font_information.max_y - defaultGlyph->miny );
1919  commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
1920  textSize.y -= commonOffset;
1921 
1922  return std::make_pair( textSize, commonOffset );
1923 }
1924 
1925 
1926 void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
1927 {
1928  PostPaint();
1929 }
1930 
1931 
1932 void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1933 {
1934  // Post the mouse event to the event listener registered in constructor, if any
1935  if( mouseListener )
1936  wxPostEvent( mouseListener, aEvent );
1937 }
1938 
1939 
1941 {
1942  if( !IsCursorEnabled() )
1943  return;
1944 
1946 
1947  const int cursorSize = fullscreenCursor ? 8000 : 80;
1948 
1949  VECTOR2D cursorBegin = cursorPosition - cursorSize / ( 2 * worldScale );
1950  VECTOR2D cursorEnd = cursorPosition + cursorSize / ( 2 * worldScale );
1951  VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
1952 
1953  const COLOR4D cColor = getCursorColor();
1954  const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a,
1955  cColor.b * cColor.a, 1.0 );
1956 
1957  glActiveTexture( GL_TEXTURE0 );
1958  glDisable( GL_TEXTURE_2D );
1959  glLineWidth( 1.0 );
1960  glColor4d( color.r, color.g, color.b, color.a );
1961 
1962  glBegin( GL_LINES );
1963  glVertex2d( cursorCenter.x, cursorBegin.y );
1964  glVertex2d( cursorCenter.x, cursorEnd.y );
1965 
1966  glVertex2d( cursorBegin.x, cursorCenter.y );
1967  glVertex2d( cursorEnd.x, cursorCenter.y );
1968  glEnd();
1969 }
1970 
1971 
1973 {
1974  wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
1975  wxT( "There are no free slots to store a group" ) );
1976 
1977  while( groups.find( groupCounter ) != groups.end() )
1978  {
1979  groupCounter++;
1980  }
1981 
1982  return groupCounter++;
1983 }
1984 
1985 
1987 {
1988  wxASSERT( IsShownOnScreen() );
1989 
1990  wxASSERT_MSG( isContextLocked, "This should only be called from within a locked context." );
1991 
1992  GLenum err = glewInit();
1993 
1994  if( GLEW_OK != err )
1995  throw std::runtime_error( (const char*) glewGetErrorString( err ) );
1996 
1997  // Check the OpenGL version (minimum 2.1 is required)
1998  if( !GLEW_VERSION_2_1 )
1999  throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2000 
2001 #if defined (__LINUX__) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2002 #ifdef DEBUG
2003  if( GLEW_ARB_debug_output )
2004  enableGlDebug( true );
2005 #endif
2006 #endif
2007 
2008  // Framebuffers have to be supported
2009  if( !GLEW_EXT_framebuffer_object )
2010  throw std::runtime_error( "Framebuffer objects are not supported!" );
2011 
2012  // Vertex buffer has to be supported
2013  if( !GLEW_ARB_vertex_buffer_object )
2014  throw std::runtime_error( "Vertex buffer objects are not supported!" );
2015 
2016  // Prepare shaders
2018  throw std::runtime_error( "Cannot compile vertex shader!" );
2019 
2021  throw std::runtime_error( "Cannot compile fragment shader!" );
2022 
2023  if( !shader->IsLinked() && !shader->Link() )
2024  throw std::runtime_error( "Cannot link the shaders!" );
2025 
2026  // Check if video card supports textures big enough to fit the font atlas
2027  int maxTextureSize;
2028  glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2029 
2030  if( maxTextureSize < (int) font_image.width || maxTextureSize < (int)font_image.height )
2031  {
2032  // TODO implement software texture scaling
2033  // for bitmap fonts and use a higher resolution texture?
2034  throw std::runtime_error( "Requested texture size is not supported" );
2035  }
2036 
2037  cachedManager = new VERTEX_MANAGER( true );
2038  nonCachedManager = new VERTEX_MANAGER( false );
2039  overlayManager = new VERTEX_MANAGER( false );
2040 
2041  // Make VBOs use shaders
2045 
2046  isInitialized = true;
2047 }
2048 
2049 
2050 // ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11
2051 void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2052 {
2053  GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2054  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2055  VERTEX_MANAGER* vboManager = param->vboManager;
2056 
2057  assert( vboManager );
2058  vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2059 }
2060 
2061 
2062 void CALLBACK CombineCallback( GLdouble coords[3],
2063  GLdouble* vertex_data[4],
2064  GLfloat weight[4], GLdouble** dataOut, void* aData )
2065 {
2066  GLdouble* vertex = new GLdouble[3];
2067  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2068 
2069  // Save the pointer so we can delete it later
2070  param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) );
2071 
2072  memcpy( vertex, coords, 3 * sizeof(GLdouble) );
2073 
2074  *dataOut = vertex;
2075 }
2076 
2077 
2078 void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2079 {
2080  // This callback is needed to force GLU tesselator to use triangles only
2081 }
2082 
2083 
2084 void CALLBACK ErrorCallback( GLenum aErrorCode )
2085 {
2086  //throw std::runtime_error( std::string( "Tessellation error: " ) +
2087  //std::string( (const char*) gluErrorString( aErrorCode ) );
2088 }
2089 
2090 
2091 static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2092 {
2093  gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback );
2094  gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback );
2095  gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback );
2096  gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback );
2097 }
2098 
2099 void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2100 {
2101  cachedManager->EnableDepthTest( aEnabled );
2102  nonCachedManager->EnableDepthTest( aEnabled );
2103  overlayManager->EnableDepthTest( aEnabled );
2104 }
2105 
2106 
2107 static double roundr( double f, double r )
2108 {
2109  return floor(f / r + 0.5) * r;
2110 }
2111 
2112 
2114 {
2115  auto pixelSize = worldScale;
2116 
2117  lookAtPoint.x = roundr( lookAtPoint.x, pixelSize );
2118  lookAtPoint.y = roundr( lookAtPoint.y, pixelSize );
2119 
2121 }
2122 
Definition: colors.h:57
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
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.
double msecs(bool aSinceLast=false)
Definition: profile.h:143
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:83
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:311
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:302
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:44
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:303
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:51
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:304
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:141
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.
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:301
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:153
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.