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-2023 Kicad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2013-2017 CERN
7 * @author Maciej Suminski <[email protected]>
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// Apple, in their infinite wisdom, has decided to mark OpenGL as deprecated.
30// Luckily we can silence warnings about its deprecation.
31#ifdef __APPLE__
32#define GL_SILENCE_DEPRECATION 1
33#endif
34
35#include <advanced_config.h>
37#include <gal/opengl/utils.h>
38#include <gal/definitions.h>
39#include <gl_context_mgr.h>
41#include <math/vector2wx.h>
42#include <bitmap_base.h>
43#include <bezier_curves.h>
44#include <math/util.h> // for KiROUND
45#include <trace_helpers.h>
46
47#include <wx/frame.h>
48
49#include <macros.h>
51
52#include <profile.h>
53#include <trace_helpers.h>
54
55#include <gl_utils.h>
56
57#include <functional>
58#include <limits>
59#include <memory>
60#include <list>
61using namespace std::placeholders;
62using namespace KIGFX;
63
64//#define DISABLE_BITMAP_CACHE
65
66// The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
67// (see ubuntu-font-licence-1.0.txt for details)
68#include "gl_resources.h"
69#include <glsl_kicad_frag.h>
70#include <glsl_kicad_vert.h>
71using namespace KIGFX::BUILTIN_FONT;
72
73static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
74static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
75
76wxGLContext* OPENGL_GAL::m_glMainContext = nullptr;
77int OPENGL_GAL::m_instanceCounter = 0;
78GLuint OPENGL_GAL::g_fontTexture = 0;
79bool OPENGL_GAL::m_isBitmapFontLoaded = false;
80
81namespace KIGFX
82{
84{
85public:
87 m_cacheSize( 0 )
88 {}
89
91
92 GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
93
94private:
96 {
97 GLuint id;
98 int w, h;
99 size_t size;
100 };
101
102 GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
103
104 const size_t m_cacheMaxElements = 50;
105 const size_t m_cacheMaxSize = 256 * 1024 * 1024;
106
107 std::map<const KIID, CACHED_BITMAP> m_bitmaps;
108 std::list<KIID> m_cacheLru;
110 std::list<GLuint> m_freedTextureIds;
111};
112
113}; // namespace KIGFX
114
115
116GL_BITMAP_CACHE::~GL_BITMAP_CACHE()
117{
118 for( auto& bitmap : m_bitmaps )
119 glDeleteTextures( 1, &bitmap.second.id );
120}
121
122
124{
125#ifndef DISABLE_BITMAP_CACHE
126 auto it = m_bitmaps.find( aBitmap->GetImageID() );
127
128 if( it != m_bitmaps.end() )
129 {
130 // A bitmap is found in cache bitmap. Ensure the associated texture is still valid.
131 if( glIsTexture( it->second.id ) )
132 {
133 return it->second.id;
134 }
135 else
136 {
137 // Delete the invalid bitmap cache and its data
138 glDeleteTextures( 1, &it->second.id );
139 m_freedTextureIds.emplace_back( it->second.id );
140
141 auto listIt = std::find( m_cacheLru.begin(), m_cacheLru.end(), it->first );
142
143 if( listIt != m_cacheLru.end() )
144 m_cacheLru.erase( listIt );
145
146 m_cacheSize -= it->second.size;
147
148 m_bitmaps.erase( it );
149 }
150
151 // the cached bitmap is not valid and deleted, it will be recreated.
152 }
153
154#endif
155 return cacheBitmap( aBitmap );
156}
157
158
160{
161 CACHED_BITMAP bmp;
162
163 const wxImage* imgPtr = aBitmap->GetOriginalImageData();
164
165 if( !imgPtr )
166 return std::numeric_limits< GLuint >::max();
167
168 const wxImage& imgData = *imgPtr;
169
170 bmp.w = imgData.GetSize().x;
171 bmp.h = imgData.GetSize().y;
172
173 // The bitmap size needs to be a multiple of 4.
174 // This is easiest to achieve by ensuring that each row
175 // has a multiple of 4 pixels
176 int extra_w = bmp.w % 4;
177
178 if( extra_w )
179 extra_w = 4 - extra_w;
180
181 GLuint textureID;
182
183 if( m_freedTextureIds.empty() )
184 {
185 glGenTextures( 1, &textureID );
186 }
187 else
188 {
189 textureID = m_freedTextureIds.front();
190 m_freedTextureIds.pop_front();
191 }
192
193 // make_unique initializes this to 0, so extra pixels are transparent
194 bmp.size = ( bmp.w + extra_w ) * bmp.h * 4;
195 auto buf = std::make_unique<uint8_t[]>( bmp.size );
196
197 for( int y = 0; y < bmp.h; y++ )
198 {
199 for( int x = 0; x < bmp.w; x++ )
200 {
201 uint8_t* p = buf.get() + ( ( bmp.w + extra_w ) * y + x ) * 4;
202
203 p[0] = imgData.GetRed( x, y );
204 p[1] = imgData.GetGreen( x, y );
205 p[2] = imgData.GetBlue( x, y );
206
207 if( imgData.HasAlpha() )
208 p[3] = imgData.GetAlpha( x, y );
209 else if( imgData.HasMask() && p[0] == imgData.GetMaskRed()
210 && p[1] == imgData.GetMaskGreen() && p[2] == imgData.GetMaskBlue() )
211 p[3] = wxALPHA_TRANSPARENT;
212 else
213 p[3] = wxALPHA_OPAQUE;
214 }
215 }
216
217 glBindTexture( GL_TEXTURE_2D, textureID );
218 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w + extra_w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
219 buf.get() );
220
221 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
222 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
223
224 bmp.id = textureID;
225
226#ifndef DISABLE_BITMAP_CACHE
227 m_cacheLru.emplace_back( aBitmap->GetImageID() );
228 m_cacheSize += bmp.size;
229 m_bitmaps.emplace( aBitmap->GetImageID(), std::move( bmp ) );
230
231 if( m_cacheLru.size() > m_cacheMaxElements
233 {
234 KIID last = m_cacheLru.front();
235 CACHED_BITMAP& cachedBitmap = m_bitmaps[last];
236
237 m_cacheSize -= cachedBitmap.size;
238 glDeleteTextures( 1, &cachedBitmap.id );
239 m_freedTextureIds.emplace_back( cachedBitmap.id );
240
241 m_bitmaps.erase( last );
242 m_cacheLru.pop_front();
243 }
244#endif
245
246 return textureID;
247}
248
249OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
250 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
251 const wxString& aName ) :
252 GAL( aDisplayOptions ),
253 HIDPI_GL_CANVAS( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
254 wxEXPAND, aName ),
255 m_mouseListener( aMouseListener ),
256 m_paintListener( aPaintListener ),
257 m_currentManager( nullptr ),
258 m_cachedManager( nullptr ),
259 m_nonCachedManager( nullptr ),
260 m_overlayManager( nullptr ),
261 m_tempManager( nullptr ),
262 m_mainBuffer( 0 ),
263 m_overlayBuffer( 0 ),
264 m_tempBuffer( 0 ),
265 m_isContextLocked( false ),
266 m_lockClientCookie( 0 )
267{
268 if( m_glMainContext == nullptr )
269 {
271
273 }
274 else
275 {
277 }
278
279 m_shader = new SHADER();
281
282 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
283
286
287 // Initialize the flags
290 m_isInitialized = false;
291 m_isGrouping = false;
292 m_groupCounter = 0;
293
294 // Connect the native cursor handler
295 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( OPENGL_GAL::onSetNativeCursor ), nullptr,
296 this );
297
298 // Connecting the event handlers
299 Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
300
301 // Mouse events are skipped to the parent
302 Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
303 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
304 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
305 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
306 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
307 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
308 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
309 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
310 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
311 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
312 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
313 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
314 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
315 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
316 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
317 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
318 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
319#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
320 Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
321#endif
322#if defined _WIN32 || defined _WIN64
323 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
324#endif
325
326 SetSize( aParent->GetClientSize() );
328
329 // Grid color settings are different in Cairo and OpenGL
330 SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
332
333 // Tesselator initialization
334 m_tesselator = gluNewTess();
336
337 gluTessProperty( m_tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
338
340
341 // Avoid uninitialized variables:
346 m_swapInterval = 0;
347}
348
349
351{
353
355 glFlush();
356 gluDeleteTess( m_tesselator );
357 ClearCache();
358
359 delete m_compositor;
360
361 if( m_isInitialized )
362 {
363 delete m_cachedManager;
364 delete m_nonCachedManager;
365 delete m_overlayManager;
366 delete m_tempManager;
367 }
368
370
371 // If it was the main context, then it will be deleted
372 // when the last OpenGL GAL instance is destroyed (a few lines below)
375
376 delete m_shader;
377
378 // Are we destroying the last GAL instance?
379 if( m_instanceCounter == 0 )
380 {
382
384 {
385 glDeleteTextures( 1, &g_fontTexture );
386 m_isBitmapFontLoaded = false;
387 }
388
391 m_glMainContext = nullptr;
392 }
393}
394
395
397{
398 wxString retVal = wxEmptyString;
399
400 wxFrame* testFrame = new wxFrame( nullptr, wxID_ANY, wxT( "" ), wxDefaultPosition,
401 wxSize( 1, 1 ), wxFRAME_TOOL_WINDOW | wxNO_BORDER );
402
403 KIGFX::OPENGL_GAL* opengl_gal = nullptr;
404
405 try
406 {
407 opengl_gal = new KIGFX::OPENGL_GAL( aOptions, testFrame );
408
409 testFrame->Raise();
410 testFrame->Show();
411
412 GAL_CONTEXT_LOCKER lock( opengl_gal );
413 opengl_gal->init();
414 }
415 catch( std::runtime_error& err )
416 {
417 //Test failed
418 retVal = wxString( err.what() );
419 }
420
421 delete opengl_gal;
422 delete testFrame;
423
424 return retVal;
425}
426
427
428void OPENGL_GAL::PostPaint( wxPaintEvent& aEvent )
429{
430 // posts an event to m_paint_listener to ask for redraw the canvas.
431 if( m_paintListener )
432 wxPostEvent( m_paintListener, aEvent );
433}
434
435
437{
438 GAL_CONTEXT_LOCKER lock( this );
439
440 bool refresh = false;
441
443 {
446 refresh = true;
447 }
448
450 {
452 refresh = true;
453 }
454
455 if( super::updatedGalDisplayOptions( aOptions ) || refresh )
456 {
457 Refresh();
458 refresh = true;
459 }
460
461 return refresh;
462}
463
464
466{
468 return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
469}
470
471
473{
474 double sf = GetScaleFactor();
475 return VECTOR2D( 2.0 / (double) ( m_screenSize.x * sf ), 2.0 /
476 (double) ( m_screenSize.y * sf ) );
477}
478
479
481{
482#ifdef KICAD_GAL_PROFILE
483 PROF_TIMER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
484#endif /* KICAD_GAL_PROFILE */
485
486 wxASSERT_MSG( m_isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
487 "Calling GAL::beginDrawing() directly is not allowed." );
488
489 wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
490 "Other drawing routines will expect everything to be initialized "
491 "which will not be the case." );
492
493 if( !m_isInitialized )
494 init();
495
496 // Set up the view port
497 glMatrixMode( GL_PROJECTION );
498 glLoadIdentity();
499
500 // Create the screen transformation (Do the RH-LH conversion here)
501 glOrtho( 0, (GLint) m_screenSize.x, (GLsizei) m_screenSize.y, 0,
503
505 {
506 // Prepare rendering target buffers
510 try
511 {
513 }
514 catch( const std::runtime_error& )
515 {
516 wxLogVerbose( "Could not create a framebuffer for overlays.\n" );
517 m_overlayBuffer = 0;
518 }
519
521 }
522
524
525 // Disable 2D Textures
526 glDisable( GL_TEXTURE_2D );
527
528 glShadeModel( GL_FLAT );
529
530 // Enable the depth buffer
531 glEnable( GL_DEPTH_TEST );
532 glDepthFunc( GL_LESS );
533
534 // Setup blending, required for transparent objects
535 glEnable( GL_BLEND );
536 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
537
538 glMatrixMode( GL_MODELVIEW );
539
540 // Set up the world <-> screen transformation
542 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
543 matrixData[0] = m_worldScreenMatrix.m_data[0][0];
544 matrixData[1] = m_worldScreenMatrix.m_data[1][0];
545 matrixData[2] = m_worldScreenMatrix.m_data[2][0];
546 matrixData[4] = m_worldScreenMatrix.m_data[0][1];
547 matrixData[5] = m_worldScreenMatrix.m_data[1][1];
548 matrixData[6] = m_worldScreenMatrix.m_data[2][1];
549 matrixData[12] = m_worldScreenMatrix.m_data[0][2];
550 matrixData[13] = m_worldScreenMatrix.m_data[1][2];
551 matrixData[14] = m_worldScreenMatrix.m_data[2][2];
552 glLoadMatrixd( matrixData );
553
554 // Set defaults
557
558 // Remove all previously stored items
562
567
569 {
570 // Keep bitmap font texture always bound to the second texturing unit
571 const GLint FONT_TEXTURE_UNIT = 2;
572
573 // Either load the font atlas to video memory, or simply bind it to a texture unit
575 {
576 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
577 glGenTextures( 1, &g_fontTexture );
578 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
579 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height, 0, GL_RGB,
580 GL_UNSIGNED_BYTE, font_image.pixels );
581 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
582 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
583 checkGlError( "loading bitmap font", __FILE__, __LINE__ );
584
585 glActiveTexture( GL_TEXTURE0 );
586
588 }
589 else
590 {
591 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
592 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
593 glActiveTexture( GL_TEXTURE0 );
594 }
595
596 // Set shader parameter
597 GLint ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" );
598 GLint ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" );
599 ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" );
600 ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" );
601 ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" );
602 ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" );
603
604 m_shader->Use();
605 m_shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
606 m_shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
608 checkGlError( "setting bitmap font sampler as shader parameter", __FILE__, __LINE__ );
609
611 }
612
613 m_shader->Use();
615 (float) ( getWorldPixelSize() / GetScaleFactor() ) );
616 const VECTOR2D& screenPixelSize = getScreenPixelSize();
617 m_shader->SetParameter( ufm_screenPixelSize, screenPixelSize );
618 double pixelSizeMultiplier = m_compositor->GetAntialiasSupersamplingFactor();
619 m_shader->SetParameter( ufm_pixelSizeMultiplier, (float) pixelSizeMultiplier );
621 renderingOffset.x *= screenPixelSize.x;
622 renderingOffset.y *= screenPixelSize.y;
623 m_shader->SetParameter( ufm_antialiasingOffset, renderingOffset );
625
626 // Something betreen BeginDrawing and EndDrawing seems to depend on
627 // this texture unit being active, but it does not assure it itself.
628 glActiveTexture( GL_TEXTURE0 );
629
630 // Unbind buffers - set compositor for direct drawing
632
633#ifdef KICAD_GAL_PROFILE
634 totalRealTime.Stop();
635 wxLogTrace( traceGalProfile, wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ),
636 totalRealTime.msecs() );
637#endif /* KICAD_GAL_PROFILE */
638}
639
640
642{
643 wxASSERT_MSG( m_isContextLocked, "What happened to the context lock?" );
644
645 PROF_TIMER cntTotal("gl-end-total");
646 PROF_TIMER cntEndCached("gl-end-cached");
647 PROF_TIMER cntEndNoncached("gl-end-noncached");
648 PROF_TIMER cntEndOverlay("gl-end-overlay");
649 PROF_TIMER cntComposite("gl-composite");
650 PROF_TIMER cntSwap("gl-swap");
651
652 cntTotal.Start();
653 // Cached & non-cached containers are rendered to the same buffer
655
656 cntEndNoncached.Start();
658 cntEndNoncached.Stop();
659
660 cntEndCached.Start();
662 cntEndCached.Stop();
663
664 cntEndOverlay.Start();
665 // Overlay container is rendered to a different buffer
666 if( m_overlayBuffer )
668
670 cntEndOverlay.Stop();
671
672 cntComposite.Start();
673 // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
674 glColor4d( 1.0, 1.0, 1.0, 1.0 );
675
676 // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
678
679 if( m_overlayBuffer )
681
683 blitCursor();
684
685 cntComposite.Stop();
686
687 cntSwap.Start();
688 SwapBuffers();
689 cntSwap.Stop();
690
691 cntTotal.Stop();
692
693 KI_TRACE( traceGalProfile, "Timing: %s %s %s %s %s %s\n", cntTotal.to_string(),
694 cntEndCached.to_string(), cntEndNoncached.to_string(), cntEndOverlay.to_string(),
695 cntComposite.to_string(), cntSwap.to_string() );
696}
697
698
699void OPENGL_GAL::LockContext( int aClientCookie )
700{
701 wxASSERT_MSG( !m_isContextLocked, "Context already locked." );
702 m_isContextLocked = true;
703 m_lockClientCookie = aClientCookie;
704
706}
707
708
709void OPENGL_GAL::UnlockContext( int aClientCookie )
710{
711 wxASSERT_MSG( m_isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
712 "be stacked rather than making separate lock/unlock calls." );
713
714 wxASSERT_MSG( m_lockClientCookie == aClientCookie, "Context was locked by a different client. "
715 "Should not be possible with RAII objects." );
716
717 m_isContextLocked = false;
718
720}
721
722
724{
725 wxASSERT_MSG( m_isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
726 "Calling this from anywhere else is not allowed." );
727
728 wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
729 "Other update routines will expect everything to be initialized "
730 "which will not be the case." );
731
732 if( !m_isInitialized )
733 init();
734
736}
737
738
740{
741 if( !m_isInitialized )
742 return;
743
745}
746
747
748void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
749{
751
752 drawLineQuad( aStartPoint, aEndPoint );
753}
754
755
756void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
757 double aWidth )
758{
759 drawSegment( aStartPoint, aEndPoint, aWidth );
760}
761
762
763void OPENGL_GAL::drawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth,
764 bool aReserve )
765{
766 VECTOR2D startEndVector = aEndPoint - aStartPoint;
767 double lineLength = startEndVector.EuclideanNorm();
768
769 float startx = aStartPoint.x;
770 float starty = aStartPoint.y;
771 float endx = aStartPoint.x + lineLength;
772 float endy = aStartPoint.y + lineLength;
773
774 // Be careful about floating point rounding. As we draw segments in larger and larger
775 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
776 // segments. In this case, we need to draw a circle for the minimal segment.
777 if( startx == endx || starty == endy )
778 {
779 drawCircle( aStartPoint, aWidth / 2, aReserve );
780 return;
781 }
782
783 if( m_isFillEnabled || aWidth == 1.0 )
784 {
786
787 SetLineWidth( aWidth );
788 drawLineQuad( aStartPoint, aEndPoint, aReserve );
789 }
790 else
791 {
792 EDA_ANGLE lineAngle( startEndVector );
793 // Outlined tracks
794
795 SetLineWidth( 1.0 );
798
799 Save();
800
801 if( aReserve )
802 m_currentManager->Reserve( 6 + 6 + 3 + 3 ); // Two line quads and two semicircles
803
804 m_currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
805 m_currentManager->Rotate( lineAngle.AsRadians(), 0.0f, 0.0f, 1.0f );
806
807 drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), VECTOR2D( lineLength, aWidth / 2.0 ), false );
808
809 drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), VECTOR2D( lineLength, -aWidth / 2.0 ),
810 false );
811
812 // Draw line caps
813 drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2, false );
814 drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2, false );
815
816 Restore();
817 }
818}
819
820
821void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
822{
823 drawCircle( aCenterPoint, aRadius );
824}
825
826
827void OPENGL_GAL::drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve )
828{
829 if( m_isFillEnabled )
830 {
831 if( aReserve )
833
835
836 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
837 * Parameters given to Shader() are indices of the triangle's vertices
838 * (if you want to understand more, check the vertex shader source [shader.vert]).
839 * Shader uses this coordinates to determine if fragments are inside the circle or not.
840 * Does the calculations in the vertex shader now (pixel alignment)
841 * v2
842 * /\
843 * //\\
844 * v0 /_\/_\ v1
845 */
847 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
848
850 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
851
853 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
854 }
856 {
857 if( aReserve )
859
862
863 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
864 * Parameters given to Shader() are indices of the triangle's vertices
865 * (if you want to understand more, check the vertex shader source [shader.vert]).
866 * and the line width. Shader uses this coordinates to determine if fragments are
867 * inside the circle or not.
868 * v2
869 * /\
870 * //\\
871 * v0 /_\/_\ v1
872 */
874 m_currentManager->Vertex( aCenterPoint.x, // v0
875 aCenterPoint.y, m_layerDepth );
876
878 m_currentManager->Vertex( aCenterPoint.x, // v1
879 aCenterPoint.y, m_layerDepth );
880
882 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, // v2
883 m_layerDepth );
884 }
885}
886
887
888void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
889 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle )
890{
891 if( aRadius <= 0 )
892 return;
893
894 double startAngle = aStartAngle.AsRadians();
895 double endAngle = aEndAngle.AsRadians();
896
897 // Swap the angles, if start angle is greater than end angle
898 SWAP( startAngle, >, endAngle );
899
900 const double alphaIncrement = calcAngleStep( aRadius );
901
902 Save();
903 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
904
905 if( m_isFillEnabled )
906 {
907 double alpha;
910
911 // Triangle fan
912 for( alpha = startAngle; ( alpha + alphaIncrement ) < endAngle; )
913 {
916 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
917 m_layerDepth );
918 alpha += alphaIncrement;
919 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
920 m_layerDepth );
921 }
922
923 // The last missing triangle
924 const VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
925
928 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, m_layerDepth );
929 m_currentManager->Vertex( endPoint.x, endPoint.y, m_layerDepth );
930 }
931
933 {
936
937 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
938 double alpha;
939
940 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
941 {
942 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
943 DrawLine( p, p_next );
944
945 p = p_next;
946 }
947
948 // Draw the last missing part
949 if( alpha != endAngle )
950 {
951 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
952 DrawLine( p, p_last );
953 }
954 }
955
956 Restore();
957}
958
959
960void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
961 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle,
962 double aWidth, double aMaxError )
963{
964 if( aRadius <= 0 )
965 {
966 // Arcs of zero radius are a circle of aWidth diameter
967 if( aWidth > 0 )
968 DrawCircle( aCenterPoint, aWidth / 2.0 );
969
970 return;
971 }
972
973 double startAngle = aStartAngle.AsRadians();
974 double endAngle = aEndAngle.AsRadians();
975
976 // Swap the angles, if start angle is greater than end angle
977 SWAP( startAngle, >, endAngle );
978
979 // Calculate the seg count to approximate the arc with aMaxError or less
980 int segCount360 = GetArcToSegmentCount( aRadius, aMaxError, FULL_CIRCLE );
981 segCount360 = std::max( SEG_PER_CIRCLE_COUNT, segCount360 );
982 double alphaIncrement = 2.0 * M_PI / segCount360;
983
984 // Refinement: Use a segment count multiple of 2, because we have a control point
985 // on the middle of the arc, and the look is better if it is on a segment junction
986 // because there is no approx error
987 int seg_count = KiROUND( ( endAngle - startAngle ) / alphaIncrement );
988
989 if( seg_count % 2 != 0 )
990 seg_count += 1;
991
992 // Recalculate alphaIncrement with a even integer number of segment
993 if( seg_count )
994 alphaIncrement = ( endAngle - startAngle ) / seg_count;
995
996 Save();
997 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
998
1000 {
1002 m_strokeColor.a );
1003
1004 double width = aWidth / 2.0;
1005 VECTOR2D startPoint( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1006 VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1007
1008 drawStrokedSemiCircle( startPoint, width, startAngle + M_PI );
1009 drawStrokedSemiCircle( endPoint, width, endAngle );
1010
1011 VECTOR2D pOuter( cos( startAngle ) * ( aRadius + width ),
1012 sin( startAngle ) * ( aRadius + width ) );
1013
1014 VECTOR2D pInner( cos( startAngle ) * ( aRadius - width ),
1015 sin( startAngle ) * ( aRadius - width ) );
1016
1017 double alpha;
1018
1019 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1020 {
1021 VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
1022 sin( alpha ) * ( aRadius + width ) );
1023 VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
1024 sin( alpha ) * ( aRadius - width ) );
1025
1026 DrawLine( pOuter, pNextOuter );
1027 DrawLine( pInner, pNextInner );
1028
1029 pOuter = pNextOuter;
1030 pInner = pNextInner;
1031 }
1032
1033 // Draw the last missing part
1034 if( alpha != endAngle )
1035 {
1036 VECTOR2D pLastOuter( cos( endAngle ) * ( aRadius + width ),
1037 sin( endAngle ) * ( aRadius + width ) );
1038 VECTOR2D pLastInner( cos( endAngle ) * ( aRadius - width ),
1039 sin( endAngle ) * ( aRadius - width ) );
1040
1041 DrawLine( pOuter, pLastOuter );
1042 DrawLine( pInner, pLastInner );
1043 }
1044 }
1045
1046 if( m_isFillEnabled )
1047 {
1049 SetLineWidth( aWidth );
1050
1051 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1052 double alpha;
1053
1054 int lineCount = 0;
1055
1056 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1057 {
1058 lineCount++;
1059 }
1060
1061 // The last missing part
1062 if( alpha != endAngle )
1063 {
1064 lineCount++;
1065 }
1066
1067 reserveLineQuads( lineCount );
1068
1069 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1070 {
1071 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
1072 drawLineQuad( p, p_next, false );
1073
1074 p = p_next;
1075 }
1076
1077 // Draw the last missing part
1078 if( alpha != endAngle )
1079 {
1080 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1081 drawLineQuad( p, p_last, false );
1082 }
1083 }
1084
1085 Restore();
1086}
1087
1088
1089void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1090{
1091 // Compute the diagonal points of the rectangle
1092 VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
1093 VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
1094
1095 // Fill the rectangle
1096 if( m_isFillEnabled )
1097 {
1101
1102 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1103 m_currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, m_layerDepth );
1104 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1105
1106 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1107 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1108 m_currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, m_layerDepth );
1109 }
1110
1111 // Stroke the outline
1112 if( m_isStrokeEnabled )
1113 {
1115 m_strokeColor.a );
1116
1117 std::deque<VECTOR2D> pointList;
1118 pointList.push_back( aStartPoint );
1119 pointList.push_back( diagonalPointA );
1120 pointList.push_back( aEndPoint );
1121 pointList.push_back( diagonalPointB );
1122 pointList.push_back( aStartPoint );
1123 DrawPolyline( pointList );
1124 }
1125}
1126
1127
1128void OPENGL_GAL::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
1129{
1131 [&]( int idx )
1132 {
1133 return aPointList[idx];
1134 },
1135 aPointList.size(), aWidth );
1136}
1137
1138
1139void OPENGL_GAL::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
1140{
1141 auto numPoints = aLineChain.PointCount();
1142
1143 if( aLineChain.IsClosed() )
1144 numPoints += 1;
1145
1147 [&]( int idx )
1148 {
1149 return aLineChain.CPoint( idx );
1150 },
1151 numPoints, aWidth );
1152}
1153
1154
1155void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
1156{
1158 [&]( int idx )
1159 {
1160 return aPointList[idx];
1161 },
1162 aPointList.size() );
1163}
1164
1165
1166void OPENGL_GAL::DrawPolyline( const std::vector<VECTOR2D>& aPointList )
1167{
1169 [&]( int idx )
1170 {
1171 return aPointList[idx];
1172 },
1173 aPointList.size() );
1174}
1175
1176
1177void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
1178{
1180 [&]( int idx )
1181 {
1182 return aPointList[idx];
1183 },
1184 aListSize );
1185}
1186
1187
1189{
1190 auto numPoints = aLineChain.PointCount();
1191
1192 if( aLineChain.IsClosed() )
1193 numPoints += 1;
1194
1196 [&]( int idx )
1197 {
1198 return aLineChain.CPoint( idx );
1199 },
1200 numPoints );
1201}
1202
1203
1204void OPENGL_GAL::DrawPolylines( const std::vector<std::vector<VECTOR2D>>& aPointList )
1205{
1206 int lineQuadCount = 0;
1207
1208 for( const std::vector<VECTOR2D>& points : aPointList )
1209 lineQuadCount += points.size() - 1;
1210
1211 reserveLineQuads( lineQuadCount );
1212
1213 for( const std::vector<VECTOR2D>& points : aPointList )
1214 {
1216 [&]( int idx )
1217 {
1218 return points[idx];
1219 },
1220 points.size(), false );
1221 }
1222}
1223
1224
1225void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
1226{
1227 wxCHECK( aPointList.size() >= 2, /* void */ );
1228 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
1229 GLdouble* ptr = points.get();
1230
1231 for( const VECTOR2D& p : aPointList )
1232 {
1233 *ptr++ = p.x;
1234 *ptr++ = p.y;
1235 *ptr++ = m_layerDepth;
1236 }
1237
1238 drawPolygon( points.get(), aPointList.size() );
1239}
1240
1241
1242void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
1243{
1244 wxCHECK( aListSize >= 2, /* void */ );
1245 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
1246 GLdouble* target = points.get();
1247 const VECTOR2D* src = aPointList;
1248
1249 for( int i = 0; i < aListSize; ++i )
1250 {
1251 *target++ = src->x;
1252 *target++ = src->y;
1253 *target++ = m_layerDepth;
1254 ++src;
1255 }
1256
1257 drawPolygon( points.get(), aListSize );
1258}
1259
1260
1262 bool aStrokeTriangulation )
1263{
1266
1267 if( m_isFillEnabled )
1268 {
1269 int totalTriangleCount = 0;
1270
1271 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1272 {
1273 auto triPoly = aPolySet.TriangulatedPolygon( j );
1274
1275 totalTriangleCount += triPoly->GetTriangleCount();
1276 }
1277
1278 m_currentManager->Reserve( 3 * totalTriangleCount );
1279
1280 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1281 {
1282 auto triPoly = aPolySet.TriangulatedPolygon( j );
1283
1284 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1285 {
1286 VECTOR2I a, b, c;
1287 triPoly->GetTriangle( i, a, b, c );
1291 }
1292 }
1293 }
1294
1295 if( m_isStrokeEnabled )
1296 {
1297 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1298 {
1299 const auto& poly = aPolySet.Polygon( j );
1300
1301 for( const auto& lc : poly )
1302 {
1303 DrawPolyline( lc );
1304 }
1305 }
1306 }
1307
1308 if( ADVANCED_CFG::GetCfg().m_DrawTriangulationOutlines )
1309 {
1310 aStrokeTriangulation = true;
1311 SetStrokeColor( COLOR4D( 0.0, 1.0, 0.2, 1.0 ) );
1312 }
1313
1314 if( aStrokeTriangulation )
1315 {
1316 COLOR4D oldStrokeColor = m_strokeColor;
1317 double oldLayerDepth = m_layerDepth;
1318
1320
1321 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1322 {
1323 auto triPoly = aPolySet.TriangulatedPolygon( j );
1324
1325 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1326 {
1327 VECTOR2I a, b, c;
1328 triPoly->GetTriangle( i, a, b, c );
1329 DrawLine( a, b );
1330 DrawLine( b, c );
1331 DrawLine( c, a );
1332 }
1333 }
1334
1335 SetStrokeColor( oldStrokeColor );
1336 SetLayerDepth( oldLayerDepth );
1337 }
1338}
1339
1340
1341void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
1342{
1343 if( aPolySet.IsTriangulationUpToDate() )
1344 {
1345 drawTriangulatedPolyset( aPolySet, aStrokeTriangulation );
1346 return;
1347 }
1348
1349 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1350 {
1351 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1352 DrawPolygon( outline );
1353 }
1354}
1355
1356
1358{
1359 wxCHECK( aPolygon.PointCount() >= 2, /* void */ );
1360
1361 const int pointCount = aPolygon.SegmentCount() + 1;
1362 std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1363 GLdouble* ptr = points.get();
1364
1365 for( int i = 0; i < pointCount; ++i )
1366 {
1367 const VECTOR2I& p = aPolygon.CPoint( i );
1368 *ptr++ = p.x;
1369 *ptr++ = p.y;
1370 *ptr++ = m_layerDepth;
1371 }
1372
1373 drawPolygon( points.get(), pointCount );
1374}
1375
1376
1377void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1378 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
1379 double aFilterValue )
1380{
1381 std::vector<VECTOR2D> output;
1382 std::vector<VECTOR2D> pointCtrl;
1383
1384 pointCtrl.push_back( aStartPoint );
1385 pointCtrl.push_back( aControlPointA );
1386 pointCtrl.push_back( aControlPointB );
1387 pointCtrl.push_back( aEndPoint );
1388
1389 BEZIER_POLY converter( pointCtrl );
1390 converter.GetPoly( output, aFilterValue );
1391
1392 DrawPolyline( &output[0], output.size() );
1393}
1394
1395
1396void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
1397{
1398 GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
1399
1400 // We have to calculate the pixel size in users units to draw the image.
1401 // m_worldUnitLength is a factor used for converting IU to inches
1402 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
1403 double w = (double) aBitmap.GetSizePixels().x * scale;
1404 double h = (double) aBitmap.GetSizePixels().y * scale;
1405
1406 auto xform = m_currentManager->GetTransformation();
1407
1408 glm::vec4 v0 = xform * glm::vec4( -w / 2, -h / 2, 0.0, 0.0 );
1409 glm::vec4 v1 = xform * glm::vec4( w / 2, h / 2, 0.0, 0.0 );
1410 glm::vec4 trans = xform[3];
1411
1412 auto texture_id = m_bitmapCache->RequestBitmap( &aBitmap );
1413
1414 if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1415 return;
1416
1417 glMatrixMode( GL_TEXTURE );
1418 glPushMatrix();
1419 glTranslated( 0.5, 0.5, 0.5 );
1420 glRotated( aBitmap.Rotation().AsDegrees(), 0, 0, 1 );
1421 glTranslated( -0.5, -0.5, -0.5 );
1422
1423 glMatrixMode( GL_MODELVIEW );
1424 glPushMatrix();
1425 glTranslated( trans.x, trans.y, trans.z );
1426
1427 glEnable( GL_TEXTURE_2D );
1428 glActiveTexture( GL_TEXTURE0 );
1429 glBindTexture( GL_TEXTURE_2D, texture_id );
1430
1431 float texStartX = aBitmap.IsMirrored() ? 1.0 : 0.0;
1432 float texEndX = aBitmap.IsMirrored() ? 0.0 : 1.0;
1433
1434 glBegin( GL_QUADS );
1435 glColor4f( 1.0, 1.0, 1.0, alpha );
1436 glTexCoord2f( texStartX, 0.0 );
1437 glVertex3f( v0.x, v0.y, m_layerDepth );
1438 glColor4f( 1.0, 1.0, 1.0, alpha );
1439 glTexCoord2f( texEndX, 0.0 );
1440 glVertex3f( v1.x, v0.y, m_layerDepth );
1441 glColor4f( 1.0, 1.0, 1.0, alpha );
1442 glTexCoord2f( texEndX, 1.0 );
1443 glVertex3f( v1.x, v1.y, m_layerDepth );
1444 glColor4f( 1.0, 1.0, 1.0, alpha );
1445 glTexCoord2f( texStartX, 1.0 );
1446 glVertex3f( v0.x, v1.y, m_layerDepth );
1447 glEnd();
1448
1449 glBindTexture( GL_TEXTURE_2D, 0 );
1450
1451#ifdef DISABLE_BITMAP_CACHE
1452 glDeleteTextures( 1, &texture_id );
1453#endif
1454
1455 glPopMatrix();
1456
1457 glMatrixMode( GL_TEXTURE );
1458 glPopMatrix();
1459 glMatrixMode( GL_MODELVIEW );
1460}
1461
1462
1463void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2I& aPosition,
1464 const EDA_ANGLE& aAngle )
1465{
1466 // Fallback to generic impl (which uses the stroke font) on cases we don't handle
1467 if( IsTextMirrored()
1468 || aText.Contains( wxT( "^{" ) )
1469 || aText.Contains( wxT( "_{" ) )
1470 || aText.Contains( wxT( "\n" ) ) )
1471 {
1472 return GAL::BitmapText( aText, aPosition, aAngle );
1473 }
1474
1475 const UTF8 text( aText );
1476 VECTOR2D textSize;
1477 float commonOffset;
1478 std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1479
1480 const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1481 double overbarHeight = textSize.y;
1482
1483 Save();
1484
1486 m_currentManager->Translate( aPosition.x, aPosition.y, m_layerDepth );
1487 m_currentManager->Rotate( aAngle.AsRadians(), 0.0f, 0.0f, -1.0f );
1488
1489 double sx = SCALE * ( m_globalFlipX ? -1.0 : 1.0 );
1490 double sy = SCALE * ( m_globalFlipY ? -1.0 : 1.0 );
1491
1492 m_currentManager->Scale( sx, sy, 0 );
1493 m_currentManager->Translate( 0, -commonOffset, 0 );
1494
1495 switch( GetHorizontalJustify() )
1496 {
1498 Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1499 break;
1500
1502 //if( !IsTextMirrored() )
1503 Translate( VECTOR2D( -textSize.x, 0 ) );
1504 break;
1505
1507 //if( IsTextMirrored() )
1508 //Translate( VECTOR2D( -textSize.x, 0 ) );
1509 break;
1510 }
1511
1512 switch( GetVerticalJustify() )
1513 {
1515 break;
1516
1518 Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1519 overbarHeight = 0;
1520 break;
1521
1523 Translate( VECTOR2D( 0, -textSize.y ) );
1524 overbarHeight = -textSize.y / 2.0;
1525 break;
1526 }
1527
1528 int overbarLength = 0;
1529 int overbarDepth = -1;
1530 int braceNesting = 0;
1531
1532 auto iterateString =
1533 [&]( std::function<void( int aOverbarLength, int aOverbarHeight )> overbarFn,
1534 std::function<int( unsigned long aChar )> bitmapCharFn )
1535 {
1536 for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1537 {
1538 wxASSERT_MSG( *chIt != '\n' && *chIt != '\r',
1539 "No support for multiline bitmap text yet" );
1540
1541 if( *chIt == '~' && overbarDepth == -1 )
1542 {
1543 UTF8::uni_iter lookahead = chIt;
1544
1545 if( ++lookahead != end && *lookahead == '{' )
1546 {
1547 chIt = lookahead;
1548 overbarDepth = braceNesting;
1549 braceNesting++;
1550 continue;
1551 }
1552 }
1553 else if( *chIt == '{' )
1554 {
1555 braceNesting++;
1556 }
1557 else if( *chIt == '}' )
1558 {
1559 if( braceNesting > 0 )
1560 braceNesting--;
1561
1562 if( braceNesting == overbarDepth )
1563 {
1564 overbarFn( overbarLength, overbarHeight );
1565 overbarLength = 0;
1566
1567 overbarDepth = -1;
1568 continue;
1569 }
1570 }
1571
1572 if( overbarDepth != -1 )
1573 overbarLength += bitmapCharFn( *chIt );
1574 else
1575 bitmapCharFn( *chIt );
1576 }
1577 };
1578
1579 // First, calculate the amount of characters and overbars to reserve
1580
1581 int charsCount = 0;
1582 int overbarsCount = 0;
1583
1584 iterateString(
1585 [&overbarsCount]( int aOverbarLength, int aOverbarHeight )
1586 {
1587 overbarsCount++;
1588 },
1589 [&charsCount]( unsigned long aChar ) -> int
1590 {
1591 if( aChar != ' ' )
1592 charsCount++;
1593
1594 return 0;
1595 } );
1596
1597 m_currentManager->Reserve( 6 * charsCount + 6 * overbarsCount );
1598
1599 // Now reset the state and actually draw the characters and overbars
1600 overbarLength = 0;
1601 overbarDepth = -1;
1602 braceNesting = 0;
1603
1604 iterateString(
1605 [&]( int aOverbarLength, int aOverbarHeight )
1606 {
1607 drawBitmapOverbar( aOverbarLength, aOverbarHeight, false );
1608 },
1609 [&]( unsigned long aChar ) -> int
1610 {
1611 return drawBitmapChar( aChar, false );
1612 } );
1613
1614 // Handle the case when overbar is active till the end of the drawn text
1615 m_currentManager->Translate( 0, commonOffset, 0 );
1616
1617 if( overbarDepth != -1 && overbarLength > 0 )
1618 drawBitmapOverbar( overbarLength, overbarHeight );
1619
1620 Restore();
1621}
1622
1623
1625{
1628
1630
1631 // sub-pixel lines all render the same
1632 float minorLineWidth = std::fmax( 1.0f,
1634 float majorLineWidth = minorLineWidth * 2.0f;
1635
1636 // Draw the axis and grid
1637 // For the drawing the start points, end points and increments have
1638 // to be calculated in world coordinates
1639 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1641
1642 // Draw axes if desired
1643 if( m_axesEnabled )
1644 {
1645 SetLineWidth( minorLineWidth );
1647
1648 DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1649 DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1650 }
1651
1652 // force flush
1654
1655 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1656 return;
1657
1658 VECTOR2D gridScreenSize = GetVisibleGridSize();
1659
1660 // Compute grid starting and ending indexes to draw grid points on the
1661 // visible screen area
1662 // Note: later any point coordinate will be offset by m_gridOrigin
1663 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1664 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1665 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1666 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1667
1668 // Ensure start coordinate > end coordinate
1669 SWAP( gridStartX, >, gridEndX );
1670 SWAP( gridStartY, >, gridEndY );
1671
1672 // Ensure the grid fills the screen
1673 --gridStartX;
1674 ++gridEndX;
1675 --gridStartY;
1676 ++gridEndY;
1677
1678 glDisable( GL_DEPTH_TEST );
1679 glDisable( GL_TEXTURE_2D );
1680
1682 {
1683 glEnable( GL_STENCIL_TEST );
1684 glStencilFunc( GL_ALWAYS, 1, 1 );
1685 glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1686 glColor4d( 0.0, 0.0, 0.0, 0.0 );
1687 SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1688 }
1689 else
1690 {
1693 }
1694
1696 {
1697 // Vertical positions
1698 for( int j = gridStartY; j <= gridEndY; j++ )
1699 {
1700 bool tickY = ( j % m_gridTick == 0 );
1701 const double posY = j * gridScreenSize.y + m_gridOrigin.y;
1702
1703 // Horizontal positions
1704 for( int i = gridStartX; i <= gridEndX; i++ )
1705 {
1706 bool tickX = ( i % m_gridTick == 0 );
1707 SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1708 auto lineLen = 2.0 * GetLineWidth();
1709 auto posX = i * gridScreenSize.x + m_gridOrigin.x;
1710
1711 DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1712 DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1713 }
1714 }
1715
1717 }
1718 else
1719 {
1720 // Vertical lines
1721 for( int j = gridStartY; j <= gridEndY; j++ )
1722 {
1723 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1724
1725 // If axes are drawn, skip the lines that would cover them
1726 if( m_axesEnabled && y == 0.0 )
1727 continue;
1728
1729 SetLineWidth( ( j % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1730 VECTOR2D a( gridStartX * gridScreenSize.x + m_gridOrigin.x, y );
1731 VECTOR2D b( gridEndX * gridScreenSize.x + m_gridOrigin.x, y );
1732
1733 DrawLine( a, b );
1734 }
1735
1737
1739 {
1740 glStencilFunc( GL_NOTEQUAL, 0, 1 );
1743 }
1744
1745 // Horizontal lines
1746 for( int i = gridStartX; i <= gridEndX; i++ )
1747 {
1748 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1749
1750 // If axes are drawn, skip the lines that would cover them
1751 if( m_axesEnabled && x == 0.0 )
1752 continue;
1753
1754 SetLineWidth( ( i % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1755 VECTOR2D a( x, gridStartY * gridScreenSize.y + m_gridOrigin.y );
1756 VECTOR2D b( x, gridEndY * gridScreenSize.y + m_gridOrigin.y );
1757 DrawLine( a, b );
1758 }
1759
1761
1763 glDisable( GL_STENCIL_TEST );
1764 }
1765
1766 glEnable( GL_DEPTH_TEST );
1767 glEnable( GL_TEXTURE_2D );
1768}
1769
1770
1771void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1772{
1773 m_screenSize = VECTOR2I( aWidth, aHeight );
1774
1775 // Resize framebuffers
1776 const float scaleFactor = GetScaleFactor();
1777 m_compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1779
1780 wxGLCanvas::SetSize( aWidth, aHeight );
1781}
1782
1783
1784bool OPENGL_GAL::Show( bool aShow )
1785{
1786 bool s = wxGLCanvas::Show( aShow );
1787
1788 if( aShow )
1789 wxGLCanvas::Raise();
1790
1791 return s;
1792}
1793
1794
1796{
1797 glFlush();
1798}
1799
1800
1802{
1803 // Clear screen
1805
1806 // NOTE: Black used here instead of m_clearColor; it will be composited later
1807 glClearColor( 0, 0, 0, 1 );
1808 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1809}
1810
1811
1812void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1813{
1814 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1815
1816 matrixData[0] = aTransformation.m_data[0][0];
1817 matrixData[1] = aTransformation.m_data[1][0];
1818 matrixData[2] = aTransformation.m_data[2][0];
1819 matrixData[4] = aTransformation.m_data[0][1];
1820 matrixData[5] = aTransformation.m_data[1][1];
1821 matrixData[6] = aTransformation.m_data[2][1];
1822 matrixData[12] = aTransformation.m_data[0][2];
1823 matrixData[13] = aTransformation.m_data[1][2];
1824 matrixData[14] = aTransformation.m_data[2][2];
1825
1826 glMultMatrixd( matrixData );
1827}
1828
1829
1830void OPENGL_GAL::Rotate( double aAngle )
1831{
1832 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1833}
1834
1835
1836void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1837{
1838 m_currentManager->Translate( aVector.x, aVector.y, 0.0f );
1839}
1840
1841
1842void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1843{
1844 m_currentManager->Scale( aScale.x, aScale.y, 0.0f );
1845}
1846
1847
1849{
1851}
1852
1853
1855{
1857}
1858
1859
1861{
1862 m_isGrouping = true;
1863
1864 std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *m_cachedManager );
1865 int groupNumber = getNewGroupNumber();
1866 m_groups.insert( std::make_pair( groupNumber, newItem ) );
1867
1868 return groupNumber;
1869}
1870
1871
1873{
1875 m_isGrouping = false;
1876}
1877
1878
1879void OPENGL_GAL::DrawGroup( int aGroupNumber )
1880{
1881 auto group = m_groups.find( aGroupNumber );
1882
1883 if( group != m_groups.end() )
1884 m_cachedManager->DrawItem( *group->second );
1885}
1886
1887
1888void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1889{
1890 auto group = m_groups.find( aGroupNumber );
1891
1892 if( group != m_groups.end() )
1893 m_cachedManager->ChangeItemColor( *group->second, aNewColor );
1894}
1895
1896
1897void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1898{
1899 auto group = m_groups.find( aGroupNumber );
1900
1901 if( group != m_groups.end() )
1902 m_cachedManager->ChangeItemDepth( *group->second, aDepth );
1903}
1904
1905
1906void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1907{
1908 // Frees memory in the container as well
1909 m_groups.erase( aGroupNumber );
1910}
1911
1912
1914{
1915 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
1916
1917 m_groups.clear();
1918
1919 if( m_isInitialized )
1921}
1922
1923
1925{
1926 switch( aTarget )
1927 {
1928 default:
1933 }
1934
1935 m_currentTarget = aTarget;
1936}
1937
1938
1940{
1941 return m_currentTarget;
1942}
1943
1944
1946{
1947 // Save the current state
1948 unsigned int oldTarget = m_compositor->GetBuffer();
1949
1950 switch( aTarget )
1951 {
1952 // Cached and noncached items are rendered to the same buffer
1953 default:
1954 case TARGET_CACHED:
1955 case TARGET_NONCACHED:
1957 break;
1958
1959 case TARGET_TEMP:
1961 break;
1962
1963 case TARGET_OVERLAY:
1964 if( m_overlayBuffer )
1966 break;
1967 }
1968
1969 if( aTarget != TARGET_OVERLAY )
1971 else if( m_overlayBuffer )
1973
1974 // Restore the previous state
1975 m_compositor->SetBuffer( oldTarget );
1976}
1977
1978
1980{
1981 switch( aTarget )
1982 {
1983 default:
1984 case TARGET_TEMP:
1985 case TARGET_CACHED:
1986 case TARGET_NONCACHED: return true;
1987 case TARGET_OVERLAY: return ( m_overlayBuffer != 0 );
1988 }
1989}
1990
1991
1993{
1997}
1998
1999
2001{
2002 glBlendEquation( GL_MAX );
2004 glBlendEquation( GL_FUNC_ADD );
2005
2007}
2008
2009
2011{
2012 // Store the current cursor type and get the wxCursor for it
2013 if( !GAL::SetNativeCursorStyle( aCursor ) )
2014 return false;
2015
2017
2018 // Update the cursor in the wx control
2019 HIDPI_GL_CANVAS::SetCursor( m_currentwxCursor );
2020
2021 return true;
2022}
2023
2024
2025void OPENGL_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
2026{
2027 aEvent.SetCursor( m_currentwxCursor );
2028}
2029
2030
2031void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
2032{
2033 // Now we should only store the position of the mouse cursor
2034 // The real drawing routines are in blitCursor()
2035 //VECTOR2D screenCursor = m_worldScreenMatrix * aCursorPosition;
2036 //m_cursorPosition = m_screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
2037 m_cursorPosition = aCursorPosition;
2038}
2039
2040
2041void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
2042 const bool aReserve )
2043{
2044 /* Helper drawing: ____--- v3 ^
2045 * ____---- ... \ \
2046 * ____---- ... \ end \
2047 * v1 ____---- ... ____---- \ width
2048 * ---- ...___---- \ \
2049 * \ ___...-- \ v
2050 * \ ____----... ____---- v2
2051 * ---- ... ____----
2052 * start \ ... ____----
2053 * \... ____----
2054 * ----
2055 * v0
2056 * dots mark triangles' hypotenuses
2057 */
2058
2060 * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
2062 * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
2063
2064 VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
2065
2066 if( aReserve )
2067 reserveLineQuads( 1 );
2068
2069 // Line width is maintained by the vertex shader
2071 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2072
2074 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2075
2077 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2078
2080 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2081
2083 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2084
2086 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2087}
2088
2089
2090void OPENGL_GAL::reserveLineQuads( const int aLineCount )
2091{
2092 m_currentManager->Reserve( 6 * aLineCount );
2093}
2094
2095
2096void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2097{
2098 if( m_isFillEnabled )
2099 {
2101 drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
2102 }
2103
2104 if( m_isStrokeEnabled )
2105 {
2107 m_strokeColor.a );
2108 drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
2109 }
2110}
2111
2112
2113void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2114{
2115 Save();
2116
2118 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2119 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2120
2121 /* Draw a triangle that contains the semicircle, then shade it to leave only
2122 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2123 * (if you want to understand more, check the vertex shader source [shader.vert]).
2124 * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
2125 * v2
2126 * /\
2127 * /__\
2128 * v0 //__\\ v1
2129 */
2131 m_currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2132
2134 m_currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2135
2137 m_currentManager->Vertex( 0.0f, aRadius * 2.0f, m_layerDepth ); // v2
2138
2139 Restore();
2140}
2141
2142
2143void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
2144 bool aReserve )
2145{
2146 double outerRadius = aRadius + ( m_lineWidth / 2 );
2147
2148 Save();
2149
2150 if( aReserve )
2152
2153 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2154 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2155
2156 /* Draw a triangle that contains the semicircle, then shade it to leave only
2157 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2158 * (if you want to understand more, check the vertex shader source [shader.vert]), the
2159 * radius and the line width. Shader uses these coordinates to determine if fragments are
2160 * inside the semicircle or not.
2161 * v2
2162 * /\
2163 * /__\
2164 * v0 //__\\ v1
2165 */
2167 m_currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2168
2170 m_currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2171
2173 m_currentManager->Vertex( 0.0f, outerRadius * 2.0f, m_layerDepth ); // v2
2174
2175 Restore();
2176}
2177
2178
2179void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
2180{
2181 if( m_isFillEnabled )
2182 {
2185
2186 // Any non convex polygon needs to be tesselated
2187 // for this purpose the GLU standard functions are used
2189 gluTessBeginPolygon( m_tesselator, &params );
2190 gluTessBeginContour( m_tesselator );
2191
2192 GLdouble* point = aPoints;
2193
2194 for( int i = 0; i < aPointCount; ++i )
2195 {
2196 gluTessVertex( m_tesselator, point, point );
2197 point += 3; // 3 coordinates
2198 }
2199
2200 gluTessEndContour( m_tesselator );
2201 gluTessEndPolygon( m_tesselator );
2202
2203 // Free allocated intersecting points
2204 m_tessIntersects.clear();
2205 }
2206
2207 if( m_isStrokeEnabled )
2208 {
2210 [&]( int idx )
2211 {
2212 return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] );
2213 },
2214 aPointCount );
2215 }
2216}
2217
2218
2219void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
2220 bool aReserve )
2221{
2222 wxCHECK( aPointCount > 0, /* return */ );
2223
2225
2226 if( aPointCount == 1 )
2227 {
2228 drawLineQuad( aPointGetter( 0 ), aPointGetter( 0 ), aReserve );
2229 return;
2230 }
2231
2232 if( aReserve )
2233 {
2234 reserveLineQuads( aPointCount - 1 );
2235 }
2236
2237 for( int i = 1; i < aPointCount; ++i )
2238 {
2239 auto start = aPointGetter( i - 1 );
2240 auto end = aPointGetter( i );
2241
2242 drawLineQuad( start, end, false );
2243 }
2244}
2245
2246
2247void OPENGL_GAL::drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter,
2248 int aPointCount, double aWidth, bool aReserve )
2249{
2250 wxCHECK( aPointCount >= 2, /* return */ );
2251
2253
2254 int vertices = 0;
2255
2256 for( int i = 1; i < aPointCount; ++i )
2257 {
2258 auto start = aPointGetter( i - 1 );
2259 auto end = aPointGetter( i );
2260
2261 VECTOR2D startEndVector = start - end;
2262 double lineLength = startEndVector.EuclideanNorm();
2263
2264 float startx = start.x;
2265 float starty = end.y;
2266 float endx = start.x + lineLength;
2267 float endy = end.y + lineLength;
2268
2269 // Be careful about floating point rounding. As we draw segments in larger and larger
2270 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
2271 // segments. In this case, we need to draw a circle for the minimal segment.
2272 if( startx == endx || starty == endy )
2273 {
2274 vertices += 3; // One circle
2275 continue;
2276 }
2277
2278 if( m_isFillEnabled || aWidth == 1.0 )
2279 {
2280 vertices += 6; // One line
2281 }
2282 else
2283 {
2284 vertices += 6 + 6 + 3 + 3; // Two lines and two half-circles
2285 }
2286 }
2287
2288 m_currentManager->Reserve( vertices );
2289
2290 for( int i = 1; i < aPointCount; ++i )
2291 {
2292 auto start = aPointGetter( i - 1 );
2293 auto end = aPointGetter( i );
2294
2295 drawSegment( start, end, aWidth, false );
2296 }
2297}
2298
2299
2300int OPENGL_GAL::drawBitmapChar( unsigned long aChar, bool aReserve )
2301{
2302 const float TEX_X = font_image.width;
2303 const float TEX_Y = font_image.height;
2304
2305 // handle space
2306 if( aChar == ' ' )
2307 {
2308 const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
2309 wxCHECK( g, 0 );
2310
2311 // Match stroke font as well as possible
2312 double spaceWidth = g->advance * 0.74;
2313
2314 Translate( VECTOR2D( spaceWidth, 0 ) );
2315 return KiROUND( spaceWidth );
2316 }
2317
2318 const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
2319
2320 // If the glyph is not found (happens for many esoteric unicode chars)
2321 // shows a '?' instead.
2322 if( !glyph )
2323 glyph = LookupGlyph( '?' );
2324
2325 if( !glyph ) // Should not happen.
2326 return 0;
2327
2328 const float X = glyph->atlas_x + font_information.smooth_pixels;
2329 const float Y = glyph->atlas_y + font_information.smooth_pixels;
2330 const float XOFF = glyph->minx;
2331
2332 // adjust for height rounding
2333 const float round_adjust = ( glyph->maxy - glyph->miny )
2334 - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
2335 const float top_adjust = font_information.max_y - glyph->maxy;
2336 const float YOFF = round_adjust + top_adjust;
2337 const float W = glyph->atlas_w - font_information.smooth_pixels * 2;
2338 const float H = glyph->atlas_h - font_information.smooth_pixels * 2;
2339 const float B = 0;
2340
2341 if( aReserve )
2343
2344 Translate( VECTOR2D( XOFF, YOFF ) );
2345
2346 /* Glyph:
2347 * v0 v1
2348 * +--+
2349 * | /|
2350 * |/ |
2351 * +--+
2352 * v2 v3
2353 */
2354 m_currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
2355 m_currentManager->Vertex( -B, -B, 0 ); // v0
2356
2357 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2358 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2359
2360 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2361 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2362
2363
2364 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2365 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2366
2367 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2368 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2369
2370 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
2371 m_currentManager->Vertex( W + B, H + B, 0 ); // v3
2372
2373 Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
2374
2375 return glyph->advance;
2376}
2377
2378
2379void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight, bool aReserve )
2380{
2381 // To draw an overbar, simply draw an overbar
2382 const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
2383 wxCHECK( glyph, /* void */ );
2384
2385 const float H = glyph->maxy - glyph->miny;
2386
2387 Save();
2388
2389 Translate( VECTOR2D( -aLength, -aHeight ) );
2390
2391 if( aReserve )
2393
2395
2397
2398 m_currentManager->Vertex( 0, 0, 0 ); // v0
2399 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2400 m_currentManager->Vertex( 0, H, 0 ); // v2
2401
2402 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2403 m_currentManager->Vertex( 0, H, 0 ); // v2
2404 m_currentManager->Vertex( aLength, H, 0 ); // v3
2405
2406 Restore();
2407}
2408
2409
2410std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
2411{
2412 static const FONT_GLYPH_TYPE* defaultGlyph = LookupGlyph( '(' ); // for strange chars
2413
2414 VECTOR2D textSize( 0, 0 );
2415 float commonOffset = std::numeric_limits<float>::max();
2416 float charHeight = font_information.max_y - defaultGlyph->miny;
2417 int overbarDepth = -1;
2418 int braceNesting = 0;
2419
2420 for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
2421 {
2422 if( *chIt == '~' && overbarDepth == -1 )
2423 {
2424 UTF8::uni_iter lookahead = chIt;
2425
2426 if( ++lookahead != end && *lookahead == '{' )
2427 {
2428 chIt = lookahead;
2429 overbarDepth = braceNesting;
2430 braceNesting++;
2431 continue;
2432 }
2433 }
2434 else if( *chIt == '{' )
2435 {
2436 braceNesting++;
2437 }
2438 else if( *chIt == '}' )
2439 {
2440 if( braceNesting > 0 )
2441 braceNesting--;
2442
2443 if( braceNesting == overbarDepth )
2444 {
2445 overbarDepth = -1;
2446 continue;
2447 }
2448 }
2449
2450 const FONT_GLYPH_TYPE* glyph = LookupGlyph( *chIt );
2451
2452 if( !glyph // Not coded in font
2453 || *chIt == '-' || *chIt == '_' ) // Strange size of these 2 chars
2454 {
2455 glyph = defaultGlyph;
2456 }
2457
2458 if( glyph )
2459 textSize.x += glyph->advance;
2460 }
2461
2462 textSize.y = std::max<float>( textSize.y, charHeight );
2463 commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
2464 textSize.y -= commonOffset;
2465
2466 return std::make_pair( textSize, commonOffset );
2467}
2468
2469
2470void OPENGL_GAL::onPaint( wxPaintEvent& aEvent )
2471{
2472 PostPaint( aEvent );
2473}
2474
2475
2476void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
2477{
2478 // Post the mouse event to the event listener registered in constructor, if any
2479 if( m_mouseListener )
2480 wxPostEvent( m_mouseListener, aEvent );
2481}
2482
2483
2485{
2486 if( !IsCursorEnabled() )
2487 return;
2488
2490
2491 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
2492
2493 VECTOR2D cursorBegin = m_cursorPosition - cursorSize / ( 2 * m_worldScale );
2494 VECTOR2D cursorEnd = m_cursorPosition + cursorSize / ( 2 * m_worldScale );
2495 VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
2496
2497 const COLOR4D cColor = getCursorColor();
2498 const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a, cColor.b * cColor.a, 1.0 );
2499
2500 glActiveTexture( GL_TEXTURE0 );
2501 glDisable( GL_TEXTURE_2D );
2502 glLineWidth( 1.0 );
2503 glColor4d( color.r, color.g, color.b, color.a );
2504
2505 glBegin( GL_LINES );
2506 glVertex2d( cursorCenter.x, cursorBegin.y );
2507 glVertex2d( cursorCenter.x, cursorEnd.y );
2508
2509 glVertex2d( cursorBegin.x, cursorCenter.y );
2510 glVertex2d( cursorEnd.x, cursorCenter.y );
2511 glEnd();
2512}
2513
2514
2516{
2517 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
2518 wxT( "There are no free slots to store a group" ) );
2519
2520 while( m_groups.find( m_groupCounter ) != m_groups.end() )
2522
2523 return m_groupCounter++;
2524}
2525
2526
2528{
2529 wxASSERT( IsShownOnScreen() );
2530
2531 wxASSERT_MSG( m_isContextLocked, "This should only be called from within a locked context." );
2532
2533// IsDisplayAttr() handles WX_GL_{MAJOR,MINOR}_VERSION correctly only in 3.0.4
2534// starting with 3.1.0 one should use wxGLContext::IsOk() (done by GL_CONTEXT_MANAGER)
2535#if wxCHECK_VERSION( 3, 0, 3 ) and !wxCHECK_VERSION( 3, 1, 0 )
2536 const int attr[] = { WX_GL_MAJOR_VERSION, 2, WX_GL_MINOR_VERSION, 1, 0 };
2537
2538 if( !IsDisplaySupported( attr ) )
2539 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2540#endif /* wxCHECK_VERSION( 3, 0, 3 ) */
2541
2542 // Check correct initialization from the constructor
2543 if( !m_glMainContext )
2544 throw std::runtime_error( "Could not create the main OpenGL context" );
2545
2546 if( !m_glPrivContext )
2547 throw std::runtime_error( "Could not create a private OpenGL context" );
2548
2549 if( m_tesselator == nullptr )
2550 throw std::runtime_error( "Could not create the m_tesselator" );
2551 // End initialization checks
2552
2553 GLenum err = glewInit();
2554
2555 if( GLEW_OK != err )
2556 throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2557
2558 // Check the OpenGL version (minimum 2.1 is required)
2559 if( !GLEW_VERSION_2_1 )
2560 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2561
2562#if defined( __LINUX__ ) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2563#ifdef DEBUG
2564 if( GLEW_ARB_debug_output )
2565 enableGlDebug( true );
2566#endif
2567#endif
2568
2569 // Framebuffers have to be supported
2570 if( !GLEW_EXT_framebuffer_object )
2571 throw std::runtime_error( "Framebuffer objects are not supported!" );
2572
2573 // Vertex buffer has to be supported
2574 if( !GLEW_ARB_vertex_buffer_object )
2575 throw std::runtime_error( "Vertex buffer objects are not supported!" );
2576
2577 // Prepare shaders
2578 if( !m_shader->IsLinked()
2580 BUILTIN_SHADERS::glsl_kicad_vert ) )
2581 {
2582 throw std::runtime_error( "Cannot compile vertex shader!" );
2583 }
2584
2585 if( !m_shader->IsLinked()
2587 BUILTIN_SHADERS::glsl_kicad_frag ) )
2588 {
2589 throw std::runtime_error( "Cannot compile fragment shader!" );
2590 }
2591
2592 if( !m_shader->IsLinked() && !m_shader->Link() )
2593 throw std::runtime_error( "Cannot link the shaders!" );
2594
2595 // Check if video card supports textures big enough to fit the font atlas
2596 int maxTextureSize;
2597 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2598
2599 if( maxTextureSize < (int) font_image.width || maxTextureSize < (int) font_image.height )
2600 {
2601 // TODO implement software texture scaling
2602 // for bitmap fonts and use a higher resolution texture?
2603 throw std::runtime_error( "Requested texture size is not supported" );
2604 }
2605
2607
2608 m_cachedManager = new VERTEX_MANAGER( true );
2609 m_nonCachedManager = new VERTEX_MANAGER( false );
2610 m_overlayManager = new VERTEX_MANAGER( false );
2611 m_tempManager = new VERTEX_MANAGER( false );
2612
2613 // Make VBOs use shaders
2618
2619 m_isInitialized = true;
2620}
2621
2622
2623// Callback functions for the tesselator. Compare Redbook Chapter 11.
2624void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2625{
2626 GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2627 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2628 VERTEX_MANAGER* vboManager = param->vboManager;
2629
2630 assert( vboManager );
2631 vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2632}
2633
2634
2635void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4],
2636 GLdouble** dataOut, void* aData )
2637{
2638 GLdouble* vertex = new GLdouble[3];
2639 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2640
2641 // Save the pointer so we can delete it later
2642 // Note, we use the default_delete for an array because macOS
2643 // decides to bundle an ancient libc++ that mismatches the C++17 support of clang
2644 param->intersectPoints.emplace_back( vertex, std::default_delete<GLdouble[]>() );
2645
2646 memcpy( vertex, coords, 3 * sizeof( GLdouble ) );
2647
2648 *dataOut = vertex;
2649}
2650
2651
2652void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2653{
2654 // This callback is needed to force GLU tesselator to use triangles only
2655}
2656
2657
2658void CALLBACK ErrorCallback( GLenum aErrorCode )
2659{
2660 //throw std::runtime_error( std::string( "Tessellation error: " ) +
2661 //std::string( (const char*) gluErrorString( aErrorCode ) );
2662}
2663
2664
2665static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2666{
2667 gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, (void( CALLBACK* )()) VertexCallback );
2668 gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, (void( CALLBACK* )()) CombineCallback );
2669 gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, (void( CALLBACK* )()) EdgeCallback );
2670 gluTessCallback( aTesselator, GLU_TESS_ERROR, (void( CALLBACK* )()) ErrorCallback );
2671}
2672
2673void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2674{
2675 m_cachedManager->EnableDepthTest( aEnabled );
2677 m_overlayManager->EnableDepthTest( aEnabled );
2678}
2679
2680
2681inline double round_to_half_pixel( double f, double r )
2682{
2683 return ( ceil( f / r ) - 0.5 ) * r;
2684}
2685
2686
2688{
2690 auto pixelSize = m_worldScale;
2691
2692 // we need -m_lookAtPoint == -k * pixelSize + 0.5 * pixelSize for OpenGL
2693 // meaning m_lookAtPoint = (k-0.5)*pixelSize with integer k
2696
2698}
2699
2700
2701void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2702{
2703 if( aGlyph.IsStroke() )
2704 {
2705 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2706
2707 DrawPolylines( strokeGlyph );
2708 }
2709 else if( aGlyph.IsOutline() )
2710 {
2711 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2712
2715
2716 outlineGlyph.Triangulate(
2717 [&]( const VECTOR2D& aPt1, const VECTOR2D& aPt2, const VECTOR2D& aPt3 )
2718 {
2720
2721 m_currentManager->Vertex( aPt1.x, aPt1.y, m_layerDepth );
2722 m_currentManager->Vertex( aPt2.x, aPt2.y, m_layerDepth );
2723 m_currentManager->Vertex( aPt3.x, aPt3.y, m_layerDepth );
2724 } );
2725 }
2726}
2727
2728
2729void OPENGL_GAL::DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs )
2730{
2731 if( aGlyphs.empty() )
2732 return;
2733
2734 bool allGlyphsAreStroke = true;
2735 bool allGlyphsAreOutline = true;
2736
2737 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2738 {
2739 if( !glyph->IsStroke() )
2740 {
2741 allGlyphsAreStroke = false;
2742 break;
2743 }
2744 }
2745
2746 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2747 {
2748 if( !glyph->IsOutline() )
2749 {
2750 allGlyphsAreOutline = false;
2751 break;
2752 }
2753 }
2754
2755 if( allGlyphsAreStroke )
2756 {
2757 // Optimized path for stroke fonts that pre-reserves line quads.
2758 int lineQuadCount = 0;
2759
2760 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2761 {
2762 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2763
2764 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2765 lineQuadCount += points.size() - 1;
2766 }
2767
2768 reserveLineQuads( lineQuadCount );
2769
2770 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2771 {
2772 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2773
2774 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2775 {
2777 [&]( int idx )
2778 {
2779 return points[idx];
2780 },
2781 points.size(), false );
2782 }
2783 }
2784
2785 return;
2786 }
2787 else if( allGlyphsAreOutline )
2788 {
2789 // Optimized path for stroke fonts that pre-reserves glyph triangles.
2790 int triangleCount = 0;
2791
2792 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2793 {
2794 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2795
2796 // Only call CacheTriangulation if it has never been done before. Otherwise we'll hash
2797 // the triangulation to see if it has been edited, and glyphs after creation are read-only.
2798 if( outlineGlyph.TriangulatedPolyCount() == 0 )
2799 const_cast<KIFONT::OUTLINE_GLYPH&>( outlineGlyph ).CacheTriangulation( false );
2800
2801 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2802 {
2804 outlineGlyph.TriangulatedPolygon( i );
2805
2806 triangleCount += polygon->GetTriangleCount();
2807 }
2808 }
2809
2812
2813 m_currentManager->Reserve( 3 * triangleCount );
2814
2815 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2816 {
2817 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2818
2819 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2820 {
2822 outlineGlyph.TriangulatedPolygon( i );
2823
2824 for( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
2825 {
2826 VECTOR2I a, b, c;
2827 polygon->GetTriangle( j, a, b, c );
2828
2832 }
2833 }
2834 }
2835 }
2836 else
2837 {
2838 // Regular path
2839 for( size_t i = 0; i < aGlyphs.size(); i++ )
2840 DrawGlyph( *aGlyphs[i], i, aGlyphs.size() );
2841 }
2842}
int color
Definition: DXF_plotter.cpp:57
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMinSegLen=0, int aMaxSegCount=32)
Convert a Bezier curve to a polygon.
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:52
const wxImage * GetOriginalImageData() const
Definition: bitmap_base.h:74
VECTOR2I GetSizePixels() const
Definition: bitmap_base.h:112
EDA_ANGLE Rotation() const
Definition: bitmap_base.h:214
bool IsMirrored() const
Definition: bitmap_base.h:213
KIID GetImageID() const
Definition: bitmap_base.h:81
int GetPPI() const
Definition: bitmap_base.h:123
static const wxCursor GetCursor(KICURSOR aCursorType)
Definition: cursors.cpp:394
double AsDegrees() const
Definition: eda_angle.h:149
double AsRadians() const
Definition: eda_angle.h:153
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:49
wxGLCanvas wrapper for HiDPI/Retina support.
void SetScaleFactor(double aFactor)
Set the canvas scale factor, probably for a hi-DPI display.
virtual wxSize GetNativePixelSize() const
double GetScaleFactor() const
Get the current scale factor.
virtual bool IsStroke() const
Definition: glyph.h:52
virtual bool IsOutline() const
Definition: glyph.h:51
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:102
double r
Red component.
Definition: color4d.h:372
double g
Green component.
Definition: color4d.h:373
double a
Alpha component.
Definition: color4d.h:375
static const COLOR4D BLACK
Definition: color4d.h:382
double b
Blue component.
Definition: color4d.h:374
OPENGL_ANTIALIASING_MODE gl_antialiasing_mode
Abstract interface for drawing on a 2D-surface.
bool IsTextMirrored() const
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
bool IsCursorEnabled() const
Return information about cursor visibility.
MATRIX3x3D m_worldScreenMatrix
World transformation.
VECTOR2D GetVisibleGridSize() const
Return the visible grid size in x and y directions.
double m_layerDepth
The actual layer depth.
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
bool m_axesEnabled
Should the axes be drawn.
float m_gridLineWidth
Line width of the grid.
VECTOR2I m_screenSize
Screen size in screen coordinates.
GR_TEXT_H_ALIGN_T GetHorizontalJustify() const
VECTOR2D m_depthRange
Range of the depth.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
GRID_STYLE m_gridStyle
Grid display style.
COLOR4D m_axesColor
Color of the axes.
const MATRIX3x3D & GetScreenWorldMatrix() const
Get the screen <-> world transformation matrix.
float m_lineWidth
The line width.
void computeWorldScale()
Compute the scaling factor for the world->screen matrix.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
VECTOR2D m_gridSize
The grid size.
COLOR4D getCursorColor() const
Get the actual cursor color to draw.
COLOR4D m_fillColor
The fill color.
double m_worldUnitLength
The unit length of the world coordinates [inch].
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Handle updating display options.
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
VECTOR2D m_cursorPosition
Current cursor position (world coordinates)
const VECTOR2I & GetGlyphSize() const
int m_gridTick
Every tick line gets the double width.
double m_worldScale
The scale factor world->screen.
virtual bool SetNativeCursorStyle(KICURSOR aCursor)
Set the cursor in the native panel.
VECTOR2D m_gridOrigin
The grid origin.
KICURSOR m_currentNativeCursor
Current cursor.
bool m_globalFlipY
Flag for Y axis flipping.
bool m_fullscreenCursor
Shape of the cursor (fullscreen or small cross)
bool m_isFillEnabled
Is filling of graphic objects enabled ?
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
COLOR4D m_gridColor
Color of the grid.
COLOR4D m_strokeColor
The color of the outlines.
bool m_isStrokeEnabled
Are the outlines stroked ?
GAL_DISPLAY_OPTIONS & m_options
bool m_gridVisibility
Should the grid be shown.
virtual void BitmapText(const wxString &aText, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle)
Draw a text using a bitmap font.
float GetLineWidth() const
Get the line width.
bool m_globalFlipX
Flag for X axis flipping.
GR_TEXT_V_ALIGN_T GetVerticalJustify() const
VECTOR2D m_lookAtPoint
Point to be looked at in world space.
GLuint cacheBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:159
const size_t m_cacheMaxElements
Definition: opengl_gal.cpp:104
GLuint RequestBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:123
std::list< GLuint > m_freedTextureIds
Definition: opengl_gal.cpp:110
const size_t m_cacheMaxSize
Definition: opengl_gal.cpp:105
std::map< const KIID, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:107
std::list< KIID > m_cacheLru
Definition: opengl_gal.cpp:108
VECTOR2D GetAntialiasRenderingOffset() const
virtual void Begin() override
Call this at the beginning of each frame.
static const unsigned int DIRECT_RENDERING
virtual unsigned int GetBuffer() const override
Return currently used buffer handle.
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
virtual void Present() override
Call this to present the output buffer to the screen.
virtual void Initialize() override
Perform primary initialization, necessary to use the object.
virtual void ClearBuffer(const COLOR4D &aColor) override
Clear the selected buffer (set by the SetBuffer() function).
virtual void Resize(unsigned int aWidth, unsigned int aHeight) override
Clear the state of COMPOSITOR, so it has to be reinitialized again with the new dimensions.
virtual void DrawBuffer(unsigned int aBufferHandle) override
Draw the selected buffer to the output buffer.
int GetAntialiasSupersamplingFactor() const
virtual void SetBuffer(unsigned int aBufferHandle) override
Set the selected buffer as the rendering target.
virtual unsigned int CreateBuffer() override
Prepare a new buffer that may be used as a rendering target.
OpenGL implementation of the Graphics Abstraction Layer.
Definition: opengl_gal.h:70
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
void drawPolygon(GLdouble *aPoints, int aPointCount)
Draw a filled polygon.
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
void skipMouseEvent(wxMouseEvent &aEvent)
Skip the mouse event to the parent.
void drawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth, bool aReserve=true)
Internal method for segment drawing.
Definition: opengl_gal.cpp:763
unsigned int m_groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:344
void EndDiffLayer() override
Ends rendering of a differential layer.
VERTEX_MANAGER * m_overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:349
void Scale(const VECTOR2D &aScale) override
Scale the context.
bool m_isInitialized
Basic initialization flag, has to be done when the window is visible.
Definition: opengl_gal.h:367
VERTEX_MANAGER * m_currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs).
Definition: opengl_gal.h:345
void drawCircle(const VECTOR2D &aCenterPoint, double aRadius, bool aReserve=true)
Internal method for circle drawing.
Definition: opengl_gal.cpp:827
std::deque< std::shared_ptr< GLdouble > > m_tessIntersects
Definition: opengl_gal.h:383
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:821
void LockContext(int aClientCookie) override
Use GAL_CONTEXT_LOCKER RAII object unless you know what you're doing.
Definition: opengl_gal.cpp:699
unsigned int m_mainBuffer
Main rendering target.
Definition: opengl_gal.h:354
std::unique_ptr< GL_BITMAP_CACHE > m_bitmapCache
Definition: opengl_gal.h:379
std::pair< VECTOR2D, float > computeBitmapTextSize(const UTF8 &aText) const
Compute a size of text drawn using bitmap font with current text setting applied.
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
void blitCursor()
Blit cursor into the current screen.
static wxString CheckFeatures(GAL_DISPLAY_OPTIONS &aOptions)
Checks OpenGL features.
Definition: opengl_gal.cpp:396
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
void drawBitmapOverbar(double aLength, double aHeight, bool aReserve=true)
Draw an overbar over the currently drawn text.
void EndGroup() override
End the group.
bool m_isBitmapFontInitialized
Is the shader set to use bitmap fonts?
Definition: opengl_gal.h:366
void drawSegmentChain(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount, double aWidth, bool aReserve=true)
Generic way of drawing a chain of segments stored in different containers.
void BitmapText(const wxString &aText, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle) override
Draw a text using a bitmap font.
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Handle updating display options.
Definition: opengl_gal.cpp:436
unsigned int m_overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:355
void PostPaint(wxPaintEvent &aEvent)
Function PostPaint posts an event to m_paint_listener.
Definition: opengl_gal.cpp:428
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle) override
Draw an arc.
Definition: opengl_gal.cpp:888
OPENGL_COMPOSITOR * m_compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:353
VERTEX_MANAGER * m_cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:347
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
void DrawPolyline(const std::deque< VECTOR2D > &aPointList) override
Draw a polyline.
void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint, double aFilterValue=0.0) override
Draw a cubic bezier spline.
void drawFilledSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a filled semicircle.
void onPaint(wxPaintEvent &aEvent)
This is the OnPaint event handler.
void DrawGroup(int aGroupNumber) override
Draw the stored group.
void Restore() override
Restore the context.
GLint ufm_worldPixelSize
Definition: opengl_gal.h:372
void DrawGrid() override
void DrawSegmentChain(const std::vector< VECTOR2D > &aPointList, double aWidth) override
Draw a chain of rounded segments.
void drawStrokedSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle, bool aReserve=true)
Draw a stroked semicircle.
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
void Flush() override
Force all remaining objects to be drawn.
GLint ufm_antialiasingOffset
Definition: opengl_gal.h:375
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
bool IsVisible() const override
Return true if the GAL canvas is visible on the screen.
Definition: opengl_gal.h:109
void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, double aWidth, double aMaxError) override
Draw an arc segment.
Definition: opengl_gal.cpp:960
wxEvtHandler * m_mouseListener
Definition: opengl_gal.h:335
int m_swapInterval
Used to store swap interval information.
Definition: opengl_gal.h:333
void ClearCache() override
Delete all data created during caching of graphic items.
double getWorldPixelSize() const
Definition: opengl_gal.cpp:465
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:756
GROUPS_MAP m_groups
Stores information about VBO objects (groups)
Definition: opengl_gal.h:343
void endUpdate() override
Definition: opengl_gal.cpp:739
void ClearScreen() override
Clear the screen.
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
void drawTriangulatedPolyset(const SHAPE_POLY_SET &aPoly, bool aStrokeTriangulation)
Draw a set of polygons with a cached triangulation.
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
VERTEX_MANAGER * m_nonCachedManager
Container for storing non-cached VERTEX_ITEMs.
Definition: opengl_gal.h:348
int drawBitmapChar(unsigned long aChar, bool aReserve=true)
Draw a single character using bitmap font.
GLUtesselator * m_tesselator
Definition: opengl_gal.h:382
wxEvtHandler * m_paintListener
Definition: opengl_gal.h:336
void StartDiffLayer() override
Begins rendering of a differential layer.
bool m_isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:370
void Save() override
Save the context.
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: opengl_gal.h:377
bool m_isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:364
void ComputeWorldScreenMatrix() override
Compute the world <-> screen transformation matrix.
bool Show(bool aShow) override
Shows/hides the GAL canvas.
static GLuint g_fontTexture
Bitmap font texture handle (shared)
Definition: opengl_gal.h:338
virtual bool HasTarget(RENDER_TARGET aTarget) override
Return true if the target exists.
void reserveLineQuads(const int aLineCount)
Reserves specified number of line quads.
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: opengl_gal.cpp:748
void beginUpdate() override
Definition: opengl_gal.cpp:723
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:567
virtual void DrawGlyph(const KIFONT::GLYPH &aGlyph, int aNth, int aTotal) override
Draw a polygon representing a font glyph.
bool m_isGrouping
Was a group started?
Definition: opengl_gal.h:369
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: opengl_gal.cpp:480
GLint ufm_screenPixelSize
Definition: opengl_gal.h:373
void Rotate(double aAngle) override
Rotate the context.
int BeginGroup() override
Begin a group.
GLint ufm_pixelSizeMultiplier
Definition: opengl_gal.h:374
OPENGL_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("GLCanvas"))
Definition: opengl_gal.cpp:249
VECTOR2D getScreenPixelSize() const
Definition: opengl_gal.cpp:472
bool SetNativeCursorStyle(KICURSOR aCursor) override
Set the cursor in the native panel.
void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0) override
Draw a bitmap image.
void drawPolyline(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount, bool aReserve=true)
Generic way of drawing a polyline stored in different containers.
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
void UnlockContext(int aClientCookie) override
Definition: opengl_gal.cpp:709
void drawSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a semicircle.
void drawLineQuad(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, bool aReserve=true)
Draw a quad for the line.
VERTEX_MANAGER * m_tempManager
Container for storing temp (diff mode) VERTEX_ITEMs.
Definition: opengl_gal.h:350
virtual void DrawGlyphs(const std::vector< std::unique_ptr< KIFONT::GLYPH > > &aGlyphs) override
Draw polygons representing font glyphs.
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
void EnableDepthTest(bool aEnabled=false) override
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: opengl_gal.cpp:641
SHADER * m_shader
There is only one shader used for different objects.
Definition: opengl_gal.h:360
void DrawPolylines(const std::vector< std::vector< VECTOR2D > > &aPointLists) override
Draw multiple polylines.
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: opengl_gal.h:357
wxGLContext * m_glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:332
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
static bool m_isBitmapFontLoaded
Is the bitmap font texture loaded?
Definition: opengl_gal.h:365
static wxGLContext * m_glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:331
unsigned int m_tempBuffer
Temporary rendering target (for diffing etc.)
Definition: opengl_gal.h:356
void init()
Basic OpenGL initialization and feature checks.
static int m_instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:334
Provide the access to the OpenGL shaders.
Definition: shader.h:77
bool IsLinked() const
Return true if shaders are linked correctly.
Definition: shader.h:118
void SetParameter(int aParameterNumber, float aValue) const
Set a parameter of the shader.
Definition: shader.cpp:143
bool Link()
Link the shaders.
Definition: shader.cpp:101
void Use()
Use the shader.
Definition: shader.h:126
bool LoadShaderFromStrings(SHADER_TYPE aShaderType, Args &&... aArgs)
Add a shader and compile the shader sources.
Definition: shader.h:93
int AddParameter(const std::string &aParameterName)
Add a parameter to the parameter queue.
Definition: shader.cpp:130
void Deactivate()
Deactivate the shader and use the default OpenGL program.
Definition: shader.h:135
Class to control vertex container and GPU with possibility of emulating old-style OpenGL 1....
void EndDrawing() const
Finish drawing operations.
bool Vertex(const VERTEX &aVertex)
Add a vertex with the given coordinates to the currently set item.
const glm::mat4 & GetTransformation() const
void Map()
Map vertex buffer.
void Clear() const
Remove all the stored vertices from the container.
void BeginDrawing() const
Prepare buffers and items to start drawing.
void ChangeItemColor(const VERTEX_ITEM &aItem, const COLOR4D &aColor) const
Change the color of all vertices owned by an item.
void Color(const COLOR4D &aColor)
Changes currently used color that will be applied to newly added vertices.
bool Reserve(unsigned int aSize)
Allocate space for vertices, so it will be used with subsequent Vertex() calls.
void FinishItem() const
Clean after adding an item.
void ChangeItemDepth(const VERTEX_ITEM &aItem, GLfloat aDepth) const
Change the depth of all vertices owned by an item.
void Rotate(GLfloat aAngle, GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a rotation matrix, so the newly vertices will be rotated by the given ...
void EnableDepthTest(bool aEnabled)
Enable/disable Z buffer depth test.
void PopMatrix()
Pop the current transformation matrix stack.
void Shader(GLfloat aShaderType, GLfloat aParam1=0.0f, GLfloat aParam2=0.0f, GLfloat aParam3=0.0f)
Change currently used shader and its parameters that will be applied to newly added vertices.
void PushMatrix()
Push the current transformation matrix stack.
void Scale(GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a scaling matrix, so the newly vertices will be scaled by the given fa...
void Unmap()
Unmap vertex buffer.
void SetShader(SHADER &aShader) const
Set a shader program that is going to be used during rendering.
void Translate(GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a translation matrix, so newly vertices will be translated by the give...
void DrawItem(const VERTEX_ITEM &aItem) const
Draw an item to the buffer.
Definition: kiid.h:48
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition: matrix3x3.h:295
T m_data[3][3]
Definition: matrix3x3.h:65
A small class to help profiling.
Definition: profile.h:47
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition: profile.h:86
void Start()
Start or restart the counter.
Definition: profile.h:75
std::string to_string()
Definition: profile.h:153
double msecs(bool aSinceLast=false)
Definition: profile.h:147
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
void GetTriangle(int index, VECTOR2I &a, VECTOR2I &b, VECTOR2I &c) const
Represent a set of closed polygons.
bool IsTriangulationUpToDate() const
POLYGON & Polygon(int aIndex)
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of outlines in the set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
uni_iter is a non-mutating iterator that walks through unicode code points in the UTF8 encoded string...
Definition: utf8.h:204
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:71
uni_iter uend() const
Return a uni_iter initialized to the end of "this" UTF8 byte sequence.
Definition: utf8.h:287
uni_iter ubegin() const
Returns a uni_iter initialized to the start of "this" UTF8 byte sequence.
Definition: utf8.h:279
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
T y
Definition: vector3.h:62
T x
Definition: vector3.h:61
@ BLUE
Definition: color4d.h:54
KICURSOR
Definition: cursors.h:34
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
static constexpr EDA_ANGLE & FULL_CIRCLE
Definition: eda_angle.h:427
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
const wxChar *const traceGalProfile
Flag to enable debug output of GAL performance profiling.
This file contains miscellaneous commonly used macros and functions.
#define H(x, y, z)
Definition: md5_hash.cpp:17
FONT_IMAGE_TYPE font_image
const FONT_GLYPH_TYPE * LookupGlyph(unsigned int aCodepoint)
FONT_INFO_TYPE font_information
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:246
@ SMALL_CROSS
Use small cross instead of dots for the grid.
@ DOTS
Use dots for the grid.
@ SHADER_NONE
Definition: vertex_common.h:47
@ SHADER_LINE_C
Definition: vertex_common.h:53
@ SHADER_LINE_B
Definition: vertex_common.h:52
@ SHADER_FONT
Definition: vertex_common.h:50
@ SHADER_LINE_F
Definition: vertex_common.h:56
@ SHADER_LINE_E
Definition: vertex_common.h:55
@ SHADER_STROKED_CIRCLE
Definition: vertex_common.h:49
@ SHADER_LINE_A
Definition: vertex_common.h:51
@ SHADER_LINE_D
Definition: vertex_common.h:54
@ SHADER_FILLED_CIRCLE
Definition: vertex_common.h:48
@ SHADER_TYPE_VERTEX
Vertex shader.
Definition: shader.h:46
@ SHADER_TYPE_FRAGMENT
Fragment shader.
Definition: shader.h:47
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:47
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition: definitions.h:49
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition: definitions.h:51
@ TARGET_CACHED
Main rendering target (cached)
Definition: definitions.h:48
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
static void InitTesselatorCallbacks(GLUtesselator *aTesselator)
void CALLBACK CombineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut, void *aData)
void CALLBACK VertexCallback(GLvoid *aVertexPtr, void *aData)
void CALLBACK EdgeCallback(GLboolean aEdgeFlag)
void CALLBACK ErrorCallback(GLenum aErrorCode)
double round_to_half_pixel(double f, double r)
static const int glAttributes[]
Definition: opengl_gal.cpp:74
#define SEG_PER_CIRCLE_COUNT
Definition: opengl_gal.h:52
#define CALLBACK
The default number of points for circle approximation.
Definition: opengl_gal.h:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
const int scale
unsigned char pixels[1024 *1024 *3]
Definition: gl_resources.h:38
VERTEX_MANAGER * vboManager
Manager used for storing new vertices.
Definition: opengl_gal.h:321
std::deque< std::shared_ptr< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:324
VECTOR3I v1(5, 5, 5)
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
wxLogTrace helper definitions.
#define KI_TRACE(aWhat,...)
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
void enableGlDebug(bool aEnable)
Enable or disable OpenGL driver messages output.
Definition: utils.cpp:183
int checkGlError(const std::string &aInfo, const char *aFile, int aLine, bool aThrow)
Check if a recent OpenGL operation has failed.
Definition: utils.cpp:45
VECTOR2< double > VECTOR2D
Definition: vector2d.h:589
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590
VECTOR2I ToVECTOR2I(const wxSize &aSize)
Definition: vector2wx.h:30