KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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>
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#include <core/thread_pool.h>
52
53#include <core/profile.h>
54#include <trace_helpers.h>
55
56#include <gal/opengl/gl_utils.h>
57
58#include <functional>
59#include <limits>
60#include <memory>
61#include <list>
62using namespace std::placeholders;
63using namespace KIGFX;
64
65//#define DISABLE_BITMAP_CACHE
66
67// The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
68// (see ubuntu-font-licence-1.0.txt for details)
69#include "gl_resources.h"
70#include <glsl_kicad_frag.h>
71#include <glsl_kicad_vert.h>
72using namespace KIGFX::BUILTIN_FONT;
73
74static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
75static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
76
77wxGLContext* OPENGL_GAL::m_glMainContext = nullptr;
81
82namespace KIGFX
83{
85{
86public:
88 m_cacheSize( 0 )
89 {}
90
92
93 GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
94
95private:
97 {
98 GLuint id;
99 int w, h;
100 size_t size;
101 };
102
103 GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
104
105 const size_t m_cacheMaxElements = 50;
106 const size_t m_cacheMaxSize = 256 * 1024 * 1024;
107
108 std::map<const KIID, CACHED_BITMAP> m_bitmaps;
109 std::list<KIID> m_cacheLru;
111 std::list<GLuint> m_freedTextureIds;
112};
113
114}; // namespace KIGFX
115
116
118{
119 for( auto& bitmap : m_bitmaps )
120 glDeleteTextures( 1, &bitmap.second.id );
121}
122
123
125{
126#ifndef DISABLE_BITMAP_CACHE
127 auto it = m_bitmaps.find( aBitmap->GetImageID() );
128
129 if( it != m_bitmaps.end() )
130 {
131 // A bitmap is found in cache bitmap. Ensure the associated texture is still valid.
132 if( glIsTexture( it->second.id ) )
133 {
134 return it->second.id;
135 }
136 else
137 {
138 // Delete the invalid bitmap cache and its data
139 glDeleteTextures( 1, &it->second.id );
140 m_freedTextureIds.emplace_back( it->second.id );
141
142 auto listIt = std::find( m_cacheLru.begin(), m_cacheLru.end(), it->first );
143
144 if( listIt != m_cacheLru.end() )
145 m_cacheLru.erase( listIt );
146
147 m_cacheSize -= it->second.size;
148
149 m_bitmaps.erase( it );
150 }
151
152 // the cached bitmap is not valid and deleted, it will be recreated.
153 }
154
155#endif
156 return cacheBitmap( aBitmap );
157}
158
159
161{
162 CACHED_BITMAP bmp;
163
164 const wxImage* imgPtr = aBitmap->GetOriginalImageData();
165
166 if( !imgPtr )
167 return std::numeric_limits< GLuint >::max();
168
169 const wxImage& imgData = *imgPtr;
170
171 bmp.w = imgData.GetSize().x;
172 bmp.h = imgData.GetSize().y;
173
174 GLuint textureID;
175
176 if( m_freedTextureIds.empty() )
177 {
178 glGenTextures( 1, &textureID );
179 }
180 else
181 {
182 textureID = m_freedTextureIds.front();
183 m_freedTextureIds.pop_front();
184 }
185
186 bmp.size = bmp.w * bmp.h * 4;
187 auto buf = std::make_unique<uint8_t[]>( bmp.size );
188
189 for( int y = 0; y < bmp.h; y++ )
190 {
191 for( int x = 0; x < bmp.w; x++ )
192 {
193 uint8_t* p = buf.get() + ( bmp.w * y + x ) * 4;
194
195 p[0] = imgData.GetRed( x, y );
196 p[1] = imgData.GetGreen( x, y );
197 p[2] = imgData.GetBlue( x, y );
198
199 if( imgData.HasAlpha() )
200 p[3] = imgData.GetAlpha( x, y );
201 else if( imgData.HasMask() && p[0] == imgData.GetMaskRed()
202 && p[1] == imgData.GetMaskGreen() && p[2] == imgData.GetMaskBlue() )
203 p[3] = wxALPHA_TRANSPARENT;
204 else
205 p[3] = wxALPHA_OPAQUE;
206 }
207 }
208
209 glBindTexture( GL_TEXTURE_2D, textureID );
210 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
211 buf.get() );
212
213 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
214 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
215
216 bmp.id = textureID;
217
218#ifndef DISABLE_BITMAP_CACHE
219 m_cacheLru.emplace_back( aBitmap->GetImageID() );
220 m_cacheSize += bmp.size;
221 m_bitmaps.emplace( aBitmap->GetImageID(), std::move( bmp ) );
222
223 if( m_cacheLru.size() > m_cacheMaxElements
225 {
226 KIID last = m_cacheLru.front();
227 CACHED_BITMAP& cachedBitmap = m_bitmaps[last];
228
229 m_cacheSize -= cachedBitmap.size;
230 glDeleteTextures( 1, &cachedBitmap.id );
231 m_freedTextureIds.emplace_back( cachedBitmap.id );
232
233 m_bitmaps.erase( last );
234 m_cacheLru.pop_front();
235 }
236#endif
237
238 return textureID;
239}
240
242 wxWindow* aParent,
243 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
244 const wxString& aName ) :
245 GAL( aDisplayOptions ),
246 HIDPI_GL_CANVAS( aVcSettings, aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition,
247 wxDefaultSize,
248 wxEXPAND, aName ),
249 m_mouseListener( aMouseListener ),
250 m_paintListener( aPaintListener ),
251 m_currentManager( nullptr ),
252 m_cachedManager( nullptr ),
253 m_nonCachedManager( nullptr ),
254 m_overlayManager( nullptr ),
255 m_tempManager( nullptr ),
256 m_mainBuffer( 0 ),
257 m_overlayBuffer( 0 ),
258 m_tempBuffer( 0 ),
259 m_isContextLocked( false ),
260 m_lockClientCookie( 0 )
261{
262 if( m_glMainContext == nullptr )
263 {
265
266 if( !m_glMainContext )
267 throw std::runtime_error( "Could not create the main OpenGL context" );
268
270 }
271 else
272 {
274
275 if( !m_glPrivContext )
276 throw std::runtime_error( "Could not create a private OpenGL context" );
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 Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
320#if defined _WIN32 || defined _WIN64
321 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
322#endif
323
324 SetSize( aParent->GetClientSize() );
326
327 // Grid color settings are different in Cairo and OpenGL
328 SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
330
331 // Tesselator initialization
332 m_tesselator = gluNewTess();
334
335 gluTessProperty( m_tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
336
338
339 // Avoid uninitialized variables:
344 m_swapInterval = 0;
345}
346
347
349{
351
353 glFlush();
354 gluDeleteTess( m_tesselator );
355 ClearCache();
356
357 delete m_compositor;
358
359 if( m_isInitialized )
360 {
361 delete m_cachedManager;
362 delete m_nonCachedManager;
363 delete m_overlayManager;
364 delete m_tempManager;
365 }
366
368
369 // If it was the main context, then it will be deleted
370 // when the last OpenGL GAL instance is destroyed (a few lines below)
373
374 delete m_shader;
375
376 // Are we destroying the last GAL instance?
377 if( m_instanceCounter == 0 )
378 {
380
382 {
383 glDeleteTextures( 1, &g_fontTexture );
384 m_isBitmapFontLoaded = false;
385 }
386
389 m_glMainContext = nullptr;
390 }
391}
392
393
395{
396 wxString retVal = wxEmptyString;
397
398 wxFrame* testFrame = new wxFrame( nullptr, wxID_ANY, wxT( "" ), wxDefaultPosition,
399 wxSize( 1, 1 ), wxFRAME_TOOL_WINDOW | wxNO_BORDER );
400
401 KIGFX::OPENGL_GAL* opengl_gal = nullptr;
402
403 try
404 {
406 opengl_gal = new KIGFX::OPENGL_GAL( dummy, aOptions, testFrame );
407
408 testFrame->Raise();
409 testFrame->Show();
410
411 GAL_CONTEXT_LOCKER lock( opengl_gal );
412 opengl_gal->init();
413 }
414 catch( std::runtime_error& err )
415 {
416 //Test failed
417 retVal = wxString( err.what() );
418 }
419
420 delete opengl_gal;
421 delete testFrame;
422
423 return retVal;
424}
425
426
427void OPENGL_GAL::PostPaint( wxPaintEvent& aEvent )
428{
429 // posts an event to m_paint_listener to ask for redraw the canvas.
430 if( m_paintListener )
431 wxPostEvent( m_paintListener, aEvent );
432}
433
434
436{
437 GAL_CONTEXT_LOCKER lock( this );
438
439 bool refresh = false;
440
442 {
445 refresh = true;
446 }
447
449 {
451 refresh = true;
452 }
453
454 if( super::updatedGalDisplayOptions( aOptions ) || refresh )
455 {
456 Refresh();
457 refresh = true;
458 }
459
460 return refresh;
461}
462
463
465{
467 return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
468}
469
470
472{
473 double sf = GetScaleFactor();
474 return VECTOR2D( 2.0 / (double) ( m_screenSize.x * sf ), 2.0 /
475 (double) ( m_screenSize.y * sf ) );
476}
477
478
480{
481#ifdef KICAD_GAL_PROFILE
482 PROF_TIMER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
483#endif /* KICAD_GAL_PROFILE */
484
485 wxASSERT_MSG( m_isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
486 "Calling GAL::beginDrawing() directly is not allowed." );
487
488 wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
489 "Other drawing routines will expect everything to be initialized "
490 "which will not be the case." );
491
492 if( !m_isInitialized )
493 init();
494
495 // Set up the view port
496 glMatrixMode( GL_PROJECTION );
497 glLoadIdentity();
498
499 // Create the screen transformation (Do the RH-LH conversion here)
500 glOrtho( 0, (GLint) m_screenSize.x, (GLsizei) m_screenSize.y, 0,
502
504 {
505 // Prepare rendering target buffers
508 try
509 {
511 }
512 catch( const std::runtime_error& )
513 {
514 wxLogVerbose( "Could not create a framebuffer for diff mode blending.\n" );
515 m_tempBuffer = 0;
516 }
517 try
518 {
520 }
521 catch( const std::runtime_error& )
522 {
523 wxLogVerbose( "Could not create a framebuffer for overlays.\n" );
524 m_overlayBuffer = 0;
525 }
526
528 }
529
531
532 // Disable 2D Textures
533 glDisable( GL_TEXTURE_2D );
534
535 glShadeModel( GL_FLAT );
536
537 // Enable the depth buffer
538 glEnable( GL_DEPTH_TEST );
539 glDepthFunc( GL_LESS );
540
541 // Setup blending, required for transparent objects
542 glEnable( GL_BLEND );
543 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
544
545 glMatrixMode( GL_MODELVIEW );
546
547 // Set up the world <-> screen transformation
549 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
550 matrixData[0] = m_worldScreenMatrix.m_data[0][0];
551 matrixData[1] = m_worldScreenMatrix.m_data[1][0];
552 matrixData[2] = m_worldScreenMatrix.m_data[2][0];
553 matrixData[4] = m_worldScreenMatrix.m_data[0][1];
554 matrixData[5] = m_worldScreenMatrix.m_data[1][1];
555 matrixData[6] = m_worldScreenMatrix.m_data[2][1];
556 matrixData[12] = m_worldScreenMatrix.m_data[0][2];
557 matrixData[13] = m_worldScreenMatrix.m_data[1][2];
558 matrixData[14] = m_worldScreenMatrix.m_data[2][2];
559 glLoadMatrixd( matrixData );
560
561 // Set defaults
564
565 // Remove all previously stored items
569
574
576 {
577 // Keep bitmap font texture always bound to the second texturing unit
578 const GLint FONT_TEXTURE_UNIT = 2;
579
580 // Either load the font atlas to video memory, or simply bind it to a texture unit
582 {
583 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
584 glGenTextures( 1, &g_fontTexture );
585 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
586 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height, 0, GL_RGB,
587 GL_UNSIGNED_BYTE, font_image.pixels );
588 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
589 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
590 checkGlError( "loading bitmap font", __FILE__, __LINE__ );
591
592 glActiveTexture( GL_TEXTURE0 );
593
595 }
596 else
597 {
598 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
599 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
600 glActiveTexture( GL_TEXTURE0 );
601 }
602
603 // Set shader parameter
604 GLint ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" );
605 GLint ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" );
606 ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" );
607 ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" );
608 ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" );
609 ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" );
610
611 m_shader->Use();
612 m_shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
613 m_shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
615 checkGlError( "setting bitmap font sampler as shader parameter", __FILE__, __LINE__ );
616
618 }
619
620 m_shader->Use();
622 (float) ( getWorldPixelSize() / GetScaleFactor() ) );
623 const VECTOR2D& screenPixelSize = getScreenPixelSize();
624 m_shader->SetParameter( ufm_screenPixelSize, screenPixelSize );
625 double pixelSizeMultiplier = m_compositor->GetAntialiasSupersamplingFactor();
626 m_shader->SetParameter( ufm_pixelSizeMultiplier, (float) pixelSizeMultiplier );
628 renderingOffset.x *= screenPixelSize.x;
629 renderingOffset.y *= screenPixelSize.y;
630 m_shader->SetParameter( ufm_antialiasingOffset, renderingOffset );
632
633 // Something betreen BeginDrawing and EndDrawing seems to depend on
634 // this texture unit being active, but it does not assure it itself.
635 glActiveTexture( GL_TEXTURE0 );
636
637 // Unbind buffers - set compositor for direct drawing
639
640#ifdef KICAD_GAL_PROFILE
641 totalRealTime.Stop();
642 wxLogTrace( traceGalProfile, wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ),
643 totalRealTime.msecs() );
644#endif /* KICAD_GAL_PROFILE */
645}
646
647
649{
650 wxASSERT_MSG( m_isContextLocked, "What happened to the context lock?" );
651
652 PROF_TIMER cntTotal("gl-end-total");
653 PROF_TIMER cntEndCached("gl-end-cached");
654 PROF_TIMER cntEndNoncached("gl-end-noncached");
655 PROF_TIMER cntEndOverlay("gl-end-overlay");
656 PROF_TIMER cntComposite("gl-composite");
657 PROF_TIMER cntSwap("gl-swap");
658
659 cntTotal.Start();
660 // Cached & non-cached containers are rendered to the same buffer
662
663 cntEndNoncached.Start();
665 cntEndNoncached.Stop();
666
667 cntEndCached.Start();
669 cntEndCached.Stop();
670
671 cntEndOverlay.Start();
672 // Overlay container is rendered to a different buffer
673 if( m_overlayBuffer )
675
677 cntEndOverlay.Stop();
678
679 cntComposite.Start();
680 // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
681 glColor4d( 1.0, 1.0, 1.0, 1.0 );
682
683 // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
685
686 if( m_overlayBuffer )
688
690 blitCursor();
691
692 cntComposite.Stop();
693
694 cntSwap.Start();
695 SwapBuffers();
696 cntSwap.Stop();
697
698 cntTotal.Stop();
699
700 KI_TRACE( traceGalProfile, "Timing: %s %s %s %s %s %s\n", cntTotal.to_string(),
701 cntEndCached.to_string(), cntEndNoncached.to_string(), cntEndOverlay.to_string(),
702 cntComposite.to_string(), cntSwap.to_string() );
703}
704
705
706void OPENGL_GAL::LockContext( int aClientCookie )
707{
708 wxASSERT_MSG( !m_isContextLocked, "Context already locked." );
709 m_isContextLocked = true;
710 m_lockClientCookie = aClientCookie;
711
713}
714
715
716void OPENGL_GAL::UnlockContext( int aClientCookie )
717{
718 wxASSERT_MSG( m_isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
719 "be stacked rather than making separate lock/unlock calls." );
720
721 wxASSERT_MSG( m_lockClientCookie == aClientCookie, "Context was locked by a different client. "
722 "Should not be possible with RAII objects." );
723
724 m_isContextLocked = false;
725
727}
728
729
731{
732 wxASSERT_MSG( m_isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
733 "Calling this from anywhere else is not allowed." );
734
735 wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
736 "Other update routines will expect everything to be initialized "
737 "which will not be the case." );
738
739 if( !m_isInitialized )
740 init();
741
743}
744
745
747{
748 if( !m_isInitialized )
749 return;
750
752}
753
754
755void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
756{
758
759 drawLineQuad( aStartPoint, aEndPoint );
760}
761
762
763void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
764 double aWidth )
765{
766 drawSegment( aStartPoint, aEndPoint, aWidth );
767}
768
769
770void OPENGL_GAL::drawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth,
771 bool aReserve )
772{
773 VECTOR2D startEndVector = aEndPoint - aStartPoint;
774 double lineLength = startEndVector.EuclideanNorm();
775
776 float startx = aStartPoint.x;
777 float starty = aStartPoint.y;
778 float endx = aStartPoint.x + lineLength;
779 float endy = aStartPoint.y + lineLength;
780
781 // Be careful about floating point rounding. As we draw segments in larger and larger
782 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
783 // segments. In this case, we need to draw a circle for the minimal segment.
784 if( startx == endx || starty == endy )
785 {
786 drawCircle( aStartPoint, aWidth / 2, aReserve );
787 return;
788 }
789
790 if( m_isFillEnabled || aWidth == 1.0 )
791 {
793
794 SetLineWidth( aWidth );
795 drawLineQuad( aStartPoint, aEndPoint, aReserve );
796 }
797 else
798 {
799 EDA_ANGLE lineAngle( startEndVector );
800 // Outlined tracks
801
802 SetLineWidth( 1.0 );
805
806 Save();
807
808 if( aReserve )
809 m_currentManager->Reserve( 6 + 6 + 3 + 3 ); // Two line quads and two semicircles
810
811 m_currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
812 m_currentManager->Rotate( lineAngle.AsRadians(), 0.0f, 0.0f, 1.0f );
813
814 drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), VECTOR2D( lineLength, aWidth / 2.0 ), false );
815
816 drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), VECTOR2D( lineLength, -aWidth / 2.0 ),
817 false );
818
819 // Draw line caps
820 drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2, false );
821 drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2, false );
822
823 Restore();
824 }
825}
826
827
828void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
829{
830 drawCircle( aCenterPoint, aRadius );
831}
832
833
834void OPENGL_GAL::drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve )
835{
836 if( m_isFillEnabled )
837 {
838 if( aReserve )
840
842
843 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
844 * Parameters given to Shader() are indices of the triangle's vertices
845 * (if you want to understand more, check the vertex shader source [shader.vert]).
846 * Shader uses this coordinates to determine if fragments are inside the circle or not.
847 * Does the calculations in the vertex shader now (pixel alignment)
848 * v2
849 * /\
850 * //\\
851 * v0 /_\/_\ v1
852 */
854 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
855
857 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
858
860 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
861 }
863 {
864 if( aReserve )
866
869
870 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
871 * Parameters given to Shader() are indices of the triangle's vertices
872 * (if you want to understand more, check the vertex shader source [shader.vert]).
873 * and the line width. Shader uses this coordinates to determine if fragments are
874 * inside the circle or not.
875 * v2
876 * /\
877 * //\\
878 * v0 /_\/_\ v1
879 */
881 m_currentManager->Vertex( aCenterPoint.x, // v0
882 aCenterPoint.y, m_layerDepth );
883
885 m_currentManager->Vertex( aCenterPoint.x, // v1
886 aCenterPoint.y, m_layerDepth );
887
889 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, // v2
890 m_layerDepth );
891 }
892}
893
894
895void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
896 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle )
897{
898 if( aRadius <= 0 )
899 return;
900
901 double startAngle = aStartAngle.AsRadians();
902 double endAngle = startAngle + aAngle.AsRadians();
903
904 // Normalize arc angles
905 SWAP( startAngle, >, endAngle );
906
907 const double alphaIncrement = calcAngleStep( aRadius );
908
909 Save();
910 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
911
912 if( m_isFillEnabled )
913 {
914 double alpha;
917
918 // Triangle fan
919 for( alpha = startAngle; ( alpha + alphaIncrement ) < endAngle; )
920 {
923 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
924 m_layerDepth );
925 alpha += alphaIncrement;
926 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
927 m_layerDepth );
928 }
929
930 // The last missing triangle
931 const VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
932
935 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, m_layerDepth );
936 m_currentManager->Vertex( endPoint.x, endPoint.y, m_layerDepth );
937 }
938
940 {
943
944 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
945 double alpha;
946
947 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
948 {
949 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
950 DrawLine( p, p_next );
951
952 p = p_next;
953 }
954
955 // Draw the last missing part
956 if( alpha != endAngle )
957 {
958 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
959 DrawLine( p, p_last );
960 }
961 }
962
963 Restore();
964}
965
966
967void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
968 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
969 double aWidth, double aMaxError )
970{
971 if( aRadius <= 0 )
972 {
973 // Arcs of zero radius are a circle of aWidth diameter
974 if( aWidth > 0 )
975 DrawCircle( aCenterPoint, aWidth / 2.0 );
976
977 return;
978 }
979
980 double startAngle = aStartAngle.AsRadians();
981 double endAngle = startAngle + aAngle.AsRadians();
982
983 // Swap the angles, if start angle is greater than end angle
984 SWAP( startAngle, >, endAngle );
985
986 // Calculate the seg count to approximate the arc with aMaxError or less
987 int segCount360 = GetArcToSegmentCount( aRadius, aMaxError, FULL_CIRCLE );
988 segCount360 = std::max( SEG_PER_CIRCLE_COUNT, segCount360 );
989 double alphaIncrement = 2.0 * M_PI / segCount360;
990
991 // Refinement: Use a segment count multiple of 2, because we have a control point
992 // on the middle of the arc, and the look is better if it is on a segment junction
993 // because there is no approx error
994 int seg_count = KiROUND( ( endAngle - startAngle ) / alphaIncrement );
995
996 if( seg_count % 2 != 0 )
997 seg_count += 1;
998
999 // Recalculate alphaIncrement with a even integer number of segment
1000 if( seg_count )
1001 alphaIncrement = ( endAngle - startAngle ) / seg_count;
1002
1003 Save();
1004 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
1005
1006 if( m_isStrokeEnabled )
1007 {
1009 m_strokeColor.a );
1010
1011 double width = aWidth / 2.0;
1012 VECTOR2D startPoint( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1013 VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1014
1015 drawStrokedSemiCircle( startPoint, width, startAngle + M_PI );
1016 drawStrokedSemiCircle( endPoint, width, endAngle );
1017
1018 VECTOR2D pOuter( cos( startAngle ) * ( aRadius + width ),
1019 sin( startAngle ) * ( aRadius + width ) );
1020
1021 VECTOR2D pInner( cos( startAngle ) * ( aRadius - width ),
1022 sin( startAngle ) * ( aRadius - width ) );
1023
1024 double alpha;
1025
1026 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1027 {
1028 VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
1029 sin( alpha ) * ( aRadius + width ) );
1030 VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
1031 sin( alpha ) * ( aRadius - width ) );
1032
1033 DrawLine( pOuter, pNextOuter );
1034 DrawLine( pInner, pNextInner );
1035
1036 pOuter = pNextOuter;
1037 pInner = pNextInner;
1038 }
1039
1040 // Draw the last missing part
1041 if( alpha != endAngle )
1042 {
1043 VECTOR2D pLastOuter( cos( endAngle ) * ( aRadius + width ),
1044 sin( endAngle ) * ( aRadius + width ) );
1045 VECTOR2D pLastInner( cos( endAngle ) * ( aRadius - width ),
1046 sin( endAngle ) * ( aRadius - width ) );
1047
1048 DrawLine( pOuter, pLastOuter );
1049 DrawLine( pInner, pLastInner );
1050 }
1051 }
1052
1053 if( m_isFillEnabled )
1054 {
1056 SetLineWidth( aWidth );
1057
1058 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1059 double alpha;
1060
1061 int lineCount = 0;
1062
1063 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1064 {
1065 lineCount++;
1066 }
1067
1068 // The last missing part
1069 if( alpha != endAngle )
1070 {
1071 lineCount++;
1072 }
1073
1074 reserveLineQuads( lineCount );
1075
1076 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1077 {
1078 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
1079 drawLineQuad( p, p_next, false );
1080
1081 p = p_next;
1082 }
1083
1084 // Draw the last missing part
1085 if( alpha != endAngle )
1086 {
1087 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1088 drawLineQuad( p, p_last, false );
1089 }
1090 }
1091
1092 Restore();
1093}
1094
1095
1096void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1097{
1098 // Compute the diagonal points of the rectangle
1099 VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
1100 VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
1101
1102 // Fill the rectangle
1103 if( m_isFillEnabled )
1104 {
1108
1109 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1110 m_currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, m_layerDepth );
1111 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1112
1113 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1114 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1115 m_currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, m_layerDepth );
1116 }
1117
1118 // Stroke the outline
1119 if( m_isStrokeEnabled )
1120 {
1122 m_strokeColor.a );
1123
1124 std::deque<VECTOR2D> pointList;
1125 pointList.push_back( aStartPoint );
1126 pointList.push_back( diagonalPointA );
1127 pointList.push_back( aEndPoint );
1128 pointList.push_back( diagonalPointB );
1129 pointList.push_back( aStartPoint );
1130 DrawPolyline( pointList );
1131 }
1132}
1133
1134
1135void OPENGL_GAL::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
1136{
1138 [&]( int idx )
1139 {
1140 return aPointList[idx];
1141 },
1142 aPointList.size(), aWidth );
1143}
1144
1145
1146void OPENGL_GAL::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
1147{
1148 auto numPoints = aLineChain.PointCount();
1149
1150 if( aLineChain.IsClosed() )
1151 numPoints += 1;
1152
1154 [&]( int idx )
1155 {
1156 return aLineChain.CPoint( idx );
1157 },
1158 numPoints, aWidth );
1159}
1160
1161
1162void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
1163{
1165 [&]( int idx )
1166 {
1167 return aPointList[idx];
1168 },
1169 aPointList.size() );
1170}
1171
1172
1173void OPENGL_GAL::DrawPolyline( const std::vector<VECTOR2D>& aPointList )
1174{
1176 [&]( int idx )
1177 {
1178 return aPointList[idx];
1179 },
1180 aPointList.size() );
1181}
1182
1183
1184void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
1185{
1187 [&]( int idx )
1188 {
1189 return aPointList[idx];
1190 },
1191 aListSize );
1192}
1193
1194
1196{
1197 auto numPoints = aLineChain.PointCount();
1198
1199 if( aLineChain.IsClosed() )
1200 numPoints += 1;
1201
1203 [&]( int idx )
1204 {
1205 return aLineChain.CPoint( idx );
1206 },
1207 numPoints );
1208}
1209
1210
1211void OPENGL_GAL::DrawPolylines( const std::vector<std::vector<VECTOR2D>>& aPointList )
1212{
1213 int lineQuadCount = 0;
1214
1215 for( const std::vector<VECTOR2D>& points : aPointList )
1216 lineQuadCount += points.size() - 1;
1217
1218 reserveLineQuads( lineQuadCount );
1219
1220 for( const std::vector<VECTOR2D>& points : aPointList )
1221 {
1223 [&]( int idx )
1224 {
1225 return points[idx];
1226 },
1227 points.size(), false );
1228 }
1229}
1230
1231
1232void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
1233{
1234 wxCHECK( aPointList.size() >= 2, /* void */ );
1235 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
1236 GLdouble* ptr = points.get();
1237
1238 for( const VECTOR2D& p : aPointList )
1239 {
1240 *ptr++ = p.x;
1241 *ptr++ = p.y;
1242 *ptr++ = m_layerDepth;
1243 }
1244
1245 drawPolygon( points.get(), aPointList.size() );
1246}
1247
1248
1249void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
1250{
1251 wxCHECK( aListSize >= 2, /* void */ );
1252 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
1253 GLdouble* target = points.get();
1254 const VECTOR2D* src = aPointList;
1255
1256 for( int i = 0; i < aListSize; ++i )
1257 {
1258 *target++ = src->x;
1259 *target++ = src->y;
1260 *target++ = m_layerDepth;
1261 ++src;
1262 }
1263
1264 drawPolygon( points.get(), aListSize );
1265}
1266
1267
1269 bool aStrokeTriangulation )
1270{
1273
1274 if( m_isFillEnabled )
1275 {
1276 int totalTriangleCount = 0;
1277
1278 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1279 {
1280 auto triPoly = aPolySet.TriangulatedPolygon( j );
1281
1282 totalTriangleCount += triPoly->GetTriangleCount();
1283 }
1284
1285 m_currentManager->Reserve( 3 * totalTriangleCount );
1286
1287 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1288 {
1289 auto triPoly = aPolySet.TriangulatedPolygon( j );
1290
1291 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1292 {
1293 VECTOR2I a, b, c;
1294 triPoly->GetTriangle( i, a, b, c );
1298 }
1299 }
1300 }
1301
1302 if( m_isStrokeEnabled )
1303 {
1304 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1305 {
1306 const auto& poly = aPolySet.Polygon( j );
1307
1308 for( const auto& lc : poly )
1309 {
1310 DrawPolyline( lc );
1311 }
1312 }
1313 }
1314
1315 if( ADVANCED_CFG::GetCfg().m_DrawTriangulationOutlines )
1316 {
1317 aStrokeTriangulation = true;
1318 SetStrokeColor( COLOR4D( 0.0, 1.0, 0.2, 1.0 ) );
1319 }
1320
1321 if( aStrokeTriangulation )
1322 {
1323 COLOR4D oldStrokeColor = m_strokeColor;
1324 double oldLayerDepth = m_layerDepth;
1325
1327
1328 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1329 {
1330 auto triPoly = aPolySet.TriangulatedPolygon( j );
1331
1332 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1333 {
1334 VECTOR2I a, b, c;
1335 triPoly->GetTriangle( i, a, b, c );
1336 DrawLine( a, b );
1337 DrawLine( b, c );
1338 DrawLine( c, a );
1339 }
1340 }
1341
1342 SetStrokeColor( oldStrokeColor );
1343 SetLayerDepth( oldLayerDepth );
1344 }
1345}
1346
1347
1348void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
1349{
1350 if( aPolySet.IsTriangulationUpToDate() )
1351 {
1352 drawTriangulatedPolyset( aPolySet, aStrokeTriangulation );
1353 return;
1354 }
1355
1356 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1357 {
1358 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1359 DrawPolygon( outline );
1360 }
1361}
1362
1363
1365{
1366 wxCHECK( aPolygon.PointCount() >= 2, /* void */ );
1367
1368 const int pointCount = aPolygon.SegmentCount() + 1;
1369 std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1370 GLdouble* ptr = points.get();
1371
1372 for( int i = 0; i < pointCount; ++i )
1373 {
1374 const VECTOR2I& p = aPolygon.CPoint( i );
1375 *ptr++ = p.x;
1376 *ptr++ = p.y;
1377 *ptr++ = m_layerDepth;
1378 }
1379
1380 drawPolygon( points.get(), pointCount );
1381}
1382
1383
1384void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1385 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
1386 double aFilterValue )
1387{
1388 std::vector<VECTOR2D> output;
1389 std::vector<VECTOR2D> pointCtrl;
1390
1391 pointCtrl.push_back( aStartPoint );
1392 pointCtrl.push_back( aControlPointA );
1393 pointCtrl.push_back( aControlPointB );
1394 pointCtrl.push_back( aEndPoint );
1395
1396 BEZIER_POLY converter( pointCtrl );
1397 converter.GetPoly( output, aFilterValue );
1398
1399 DrawPolygon( &output[0], output.size() );
1400}
1401
1402
1403void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
1404{
1405 GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
1406
1407 // We have to calculate the pixel size in users units to draw the image.
1408 // m_worldUnitLength is a factor used for converting IU to inches
1409 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
1410 double w = (double) aBitmap.GetSizePixels().x * scale;
1411 double h = (double) aBitmap.GetSizePixels().y * scale;
1412
1413 auto xform = m_currentManager->GetTransformation();
1414
1415 glm::vec4 v0 = xform * glm::vec4( -w / 2, -h / 2, 0.0, 0.0 );
1416 glm::vec4 v1 = xform * glm::vec4( w / 2, h / 2, 0.0, 0.0 );
1417 glm::vec4 trans = xform[3];
1418
1419 auto texture_id = m_bitmapCache->RequestBitmap( &aBitmap );
1420
1421 if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1422 return;
1423
1424 glDisable( GL_DEPTH_TEST );
1425
1426 glMatrixMode( GL_TEXTURE );
1427 glPushMatrix();
1428 glTranslated( 0.5, 0.5, 0.5 );
1429 glRotated( aBitmap.Rotation().AsDegrees(), 0, 0, 1 );
1430 glTranslated( -0.5, -0.5, -0.5 );
1431
1432 glMatrixMode( GL_MODELVIEW );
1433 glPushMatrix();
1434 glTranslated( trans.x, trans.y, trans.z );
1435
1436 glEnable( GL_TEXTURE_2D );
1437 glActiveTexture( GL_TEXTURE0 );
1438 glBindTexture( GL_TEXTURE_2D, texture_id );
1439
1440 float texStartX = aBitmap.IsMirroredX() ? 1.0 : 0.0;
1441 float texEndX = aBitmap.IsMirroredX() ? 0.0 : 1.0;
1442 float texStartY = aBitmap.IsMirroredY() ? 1.0 : 0.0;
1443 float texEndY = aBitmap.IsMirroredY() ? 0.0 : 1.0;
1444
1445 glBegin( GL_QUADS );
1446 glColor4f( 1.0, 1.0, 1.0, alpha );
1447 glTexCoord2f( texStartX, texStartY );
1448 glVertex3f( v0.x, v0.y, m_layerDepth );
1449 glColor4f( 1.0, 1.0, 1.0, alpha );
1450 glTexCoord2f( texEndX, texStartY);
1451 glVertex3f( v1.x, v0.y, m_layerDepth );
1452 glColor4f( 1.0, 1.0, 1.0, alpha );
1453 glTexCoord2f( texEndX, texEndY);
1454 glVertex3f( v1.x, v1.y, m_layerDepth );
1455 glColor4f( 1.0, 1.0, 1.0, alpha );
1456 glTexCoord2f( texStartX, texEndY);
1457 glVertex3f( v0.x, v1.y, m_layerDepth );
1458 glEnd();
1459
1460 glBindTexture( GL_TEXTURE_2D, 0 );
1461
1462#ifdef DISABLE_BITMAP_CACHE
1463 glDeleteTextures( 1, &texture_id );
1464#endif
1465
1466 glPopMatrix();
1467
1468 glMatrixMode( GL_TEXTURE );
1469 glPopMatrix();
1470 glMatrixMode( GL_MODELVIEW );
1471
1472 glEnable( GL_DEPTH_TEST );
1473}
1474
1475
1476void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2I& aPosition,
1477 const EDA_ANGLE& aAngle )
1478{
1479 // Fallback to generic impl (which uses the stroke font) on cases we don't handle
1480 if( IsTextMirrored()
1481 || aText.Contains( wxT( "^{" ) )
1482 || aText.Contains( wxT( "_{" ) )
1483 || aText.Contains( wxT( "\n" ) ) )
1484 {
1485 return GAL::BitmapText( aText, aPosition, aAngle );
1486 }
1487
1488 const UTF8 text( aText );
1489 VECTOR2D textSize;
1490 float commonOffset;
1491 std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1492
1493 const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1494 double overbarHeight = textSize.y;
1495
1496 Save();
1497
1499 m_currentManager->Translate( aPosition.x, aPosition.y, m_layerDepth );
1500 m_currentManager->Rotate( aAngle.AsRadians(), 0.0f, 0.0f, -1.0f );
1501
1502 double sx = SCALE * ( m_globalFlipX ? -1.0 : 1.0 );
1503 double sy = SCALE * ( m_globalFlipY ? -1.0 : 1.0 );
1504
1505 m_currentManager->Scale( sx, sy, 0 );
1506 m_currentManager->Translate( 0, -commonOffset, 0 );
1507
1508 switch( GetHorizontalJustify() )
1509 {
1511 Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1512 break;
1513
1515 //if( !IsTextMirrored() )
1516 Translate( VECTOR2D( -textSize.x, 0 ) );
1517 break;
1518
1520 //if( IsTextMirrored() )
1521 //Translate( VECTOR2D( -textSize.x, 0 ) );
1522 break;
1523 }
1524
1525 switch( GetVerticalJustify() )
1526 {
1528 break;
1529
1531 Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1532 overbarHeight = 0;
1533 break;
1534
1536 Translate( VECTOR2D( 0, -textSize.y ) );
1537 overbarHeight = -textSize.y / 2.0;
1538 break;
1539 }
1540
1541 int overbarLength = 0;
1542 int overbarDepth = -1;
1543 int braceNesting = 0;
1544
1545 auto iterateString =
1546 [&]( std::function<void( int aOverbarLength, int aOverbarHeight )> overbarFn,
1547 std::function<int( unsigned long aChar )> bitmapCharFn )
1548 {
1549 for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1550 {
1551 wxASSERT_MSG( *chIt != '\n' && *chIt != '\r',
1552 "No support for multiline bitmap text yet" );
1553
1554 if( *chIt == '~' && overbarDepth == -1 )
1555 {
1556 UTF8::uni_iter lookahead = chIt;
1557
1558 if( ++lookahead != end && *lookahead == '{' )
1559 {
1560 chIt = lookahead;
1561 overbarDepth = braceNesting;
1562 braceNesting++;
1563 continue;
1564 }
1565 }
1566 else if( *chIt == '{' )
1567 {
1568 braceNesting++;
1569 }
1570 else if( *chIt == '}' )
1571 {
1572 if( braceNesting > 0 )
1573 braceNesting--;
1574
1575 if( braceNesting == overbarDepth )
1576 {
1577 overbarFn( overbarLength, overbarHeight );
1578 overbarLength = 0;
1579
1580 overbarDepth = -1;
1581 continue;
1582 }
1583 }
1584
1585 if( overbarDepth != -1 )
1586 overbarLength += bitmapCharFn( *chIt );
1587 else
1588 bitmapCharFn( *chIt );
1589 }
1590 };
1591
1592 // First, calculate the amount of characters and overbars to reserve
1593
1594 int charsCount = 0;
1595 int overbarsCount = 0;
1596
1597 iterateString(
1598 [&overbarsCount]( int aOverbarLength, int aOverbarHeight )
1599 {
1600 overbarsCount++;
1601 },
1602 [&charsCount]( unsigned long aChar ) -> int
1603 {
1604 if( aChar != ' ' )
1605 charsCount++;
1606
1607 return 0;
1608 } );
1609
1610 m_currentManager->Reserve( 6 * charsCount + 6 * overbarsCount );
1611
1612 // Now reset the state and actually draw the characters and overbars
1613 overbarLength = 0;
1614 overbarDepth = -1;
1615 braceNesting = 0;
1616
1617 iterateString(
1618 [&]( int aOverbarLength, int aOverbarHeight )
1619 {
1620 drawBitmapOverbar( aOverbarLength, aOverbarHeight, false );
1621 },
1622 [&]( unsigned long aChar ) -> int
1623 {
1624 return drawBitmapChar( aChar, false );
1625 } );
1626
1627 // Handle the case when overbar is active till the end of the drawn text
1628 m_currentManager->Translate( 0, commonOffset, 0 );
1629
1630 if( overbarDepth != -1 && overbarLength > 0 )
1631 drawBitmapOverbar( overbarLength, overbarHeight );
1632
1633 Restore();
1634}
1635
1636
1638{
1641
1643
1644 // sub-pixel lines all render the same
1645 float minorLineWidth = std::fmax( 1.0f,
1647 float majorLineWidth = minorLineWidth * 2.0f;
1648
1649 // Draw the axis and grid
1650 // For the drawing the start points, end points and increments have
1651 // to be calculated in world coordinates
1652 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1654
1655 // Draw axes if desired
1656 if( m_axesEnabled )
1657 {
1658 SetLineWidth( minorLineWidth );
1660
1661 DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1662 DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1663 }
1664
1665 // force flush
1667
1668 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1669 return;
1670
1671 VECTOR2D gridScreenSize = GetVisibleGridSize();
1672
1673 // Compute grid starting and ending indexes to draw grid points on the
1674 // visible screen area
1675 // Note: later any point coordinate will be offset by m_gridOrigin
1676 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1677 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1678 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1679 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1680
1681 // Ensure start coordinate > end coordinate
1682 SWAP( gridStartX, >, gridEndX );
1683 SWAP( gridStartY, >, gridEndY );
1684
1685 // Ensure the grid fills the screen
1686 --gridStartX;
1687 ++gridEndX;
1688 --gridStartY;
1689 ++gridEndY;
1690
1691 glDisable( GL_DEPTH_TEST );
1692 glDisable( GL_TEXTURE_2D );
1693
1695 {
1696 glEnable( GL_STENCIL_TEST );
1697 glStencilFunc( GL_ALWAYS, 1, 1 );
1698 glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1699 glColor4d( 0.0, 0.0, 0.0, 0.0 );
1700 SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1701 }
1702 else
1703 {
1706 }
1707
1709 {
1710 // Vertical positions
1711 for( int j = gridStartY; j <= gridEndY; j++ )
1712 {
1713 bool tickY = ( j % m_gridTick == 0 );
1714 const double posY = j * gridScreenSize.y + m_gridOrigin.y;
1715
1716 // Horizontal positions
1717 for( int i = gridStartX; i <= gridEndX; i++ )
1718 {
1719 bool tickX = ( i % m_gridTick == 0 );
1720 SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1721 auto lineLen = 2.0 * GetLineWidth();
1722 auto posX = i * gridScreenSize.x + m_gridOrigin.x;
1723
1724 DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1725 DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1726 }
1727 }
1728
1730 }
1731 else
1732 {
1733 // Vertical lines
1734 for( int j = gridStartY; j <= gridEndY; j++ )
1735 {
1736 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1737
1738 // If axes are drawn, skip the lines that would cover them
1739 if( m_axesEnabled && y == 0.0 )
1740 continue;
1741
1742 SetLineWidth( ( j % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1743 VECTOR2D a( gridStartX * gridScreenSize.x + m_gridOrigin.x, y );
1744 VECTOR2D b( gridEndX * gridScreenSize.x + m_gridOrigin.x, y );
1745
1746 DrawLine( a, b );
1747 }
1748
1750
1752 {
1753 glStencilFunc( GL_NOTEQUAL, 0, 1 );
1756 }
1757
1758 // Horizontal lines
1759 for( int i = gridStartX; i <= gridEndX; i++ )
1760 {
1761 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1762
1763 // If axes are drawn, skip the lines that would cover them
1764 if( m_axesEnabled && x == 0.0 )
1765 continue;
1766
1767 SetLineWidth( ( i % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1768 VECTOR2D a( x, gridStartY * gridScreenSize.y + m_gridOrigin.y );
1769 VECTOR2D b( x, gridEndY * gridScreenSize.y + m_gridOrigin.y );
1770 DrawLine( a, b );
1771 }
1772
1774
1776 glDisable( GL_STENCIL_TEST );
1777 }
1778
1779 glEnable( GL_DEPTH_TEST );
1780 glEnable( GL_TEXTURE_2D );
1781}
1782
1783
1784void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1785{
1786 m_screenSize = VECTOR2I( aWidth, aHeight );
1787
1788 // Resize framebuffers
1789 const float scaleFactor = GetScaleFactor();
1790 m_compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1792
1793 wxGLCanvas::SetSize( aWidth, aHeight );
1794}
1795
1796
1797bool OPENGL_GAL::Show( bool aShow )
1798{
1799 bool s = wxGLCanvas::Show( aShow );
1800
1801 if( aShow )
1802 wxGLCanvas::Raise();
1803
1804 return s;
1805}
1806
1807
1809{
1810 glFlush();
1811}
1812
1813
1815{
1816 // Clear screen
1818
1819 // NOTE: Black used here instead of m_clearColor; it will be composited later
1820 glClearColor( 0, 0, 0, 1 );
1821 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1822}
1823
1824
1825void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1826{
1827 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1828
1829 matrixData[0] = aTransformation.m_data[0][0];
1830 matrixData[1] = aTransformation.m_data[1][0];
1831 matrixData[2] = aTransformation.m_data[2][0];
1832 matrixData[4] = aTransformation.m_data[0][1];
1833 matrixData[5] = aTransformation.m_data[1][1];
1834 matrixData[6] = aTransformation.m_data[2][1];
1835 matrixData[12] = aTransformation.m_data[0][2];
1836 matrixData[13] = aTransformation.m_data[1][2];
1837 matrixData[14] = aTransformation.m_data[2][2];
1838
1839 glMultMatrixd( matrixData );
1840}
1841
1842
1843void OPENGL_GAL::Rotate( double aAngle )
1844{
1845 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1846}
1847
1848
1849void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1850{
1851 m_currentManager->Translate( aVector.x, aVector.y, 0.0f );
1852}
1853
1854
1855void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1856{
1857 m_currentManager->Scale( aScale.x, aScale.y, 0.0f );
1858}
1859
1860
1862{
1864}
1865
1866
1868{
1870}
1871
1872
1874{
1875 m_isGrouping = true;
1876
1877 std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *m_cachedManager );
1878 int groupNumber = getNewGroupNumber();
1879 m_groups.insert( std::make_pair( groupNumber, newItem ) );
1880
1881 return groupNumber;
1882}
1883
1884
1886{
1888 m_isGrouping = false;
1889}
1890
1891
1892void OPENGL_GAL::DrawGroup( int aGroupNumber )
1893{
1894 auto group = m_groups.find( aGroupNumber );
1895
1896 if( group != m_groups.end() )
1897 m_cachedManager->DrawItem( *group->second );
1898}
1899
1900
1901void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1902{
1903 auto group = m_groups.find( aGroupNumber );
1904
1905 if( group != m_groups.end() )
1906 m_cachedManager->ChangeItemColor( *group->second, aNewColor );
1907}
1908
1909
1910void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1911{
1912 auto group = m_groups.find( aGroupNumber );
1913
1914 if( group != m_groups.end() )
1915 m_cachedManager->ChangeItemDepth( *group->second, aDepth );
1916}
1917
1918
1919void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1920{
1921 // Frees memory in the container as well
1922 m_groups.erase( aGroupNumber );
1923}
1924
1925
1927{
1928 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
1929
1930 m_groups.clear();
1931
1932 if( m_isInitialized )
1934}
1935
1936
1938{
1939 switch( aTarget )
1940 {
1941 default:
1946 }
1947
1948 m_currentTarget = aTarget;
1949}
1950
1951
1953{
1954 return m_currentTarget;
1955}
1956
1957
1959{
1960 // Save the current state
1961 unsigned int oldTarget = m_compositor->GetBuffer();
1962
1963 switch( aTarget )
1964 {
1965 // Cached and noncached items are rendered to the same buffer
1966 default:
1967 case TARGET_CACHED:
1968 case TARGET_NONCACHED:
1970 break;
1971
1972 case TARGET_TEMP:
1973 if( m_tempBuffer )
1975 break;
1976
1977 case TARGET_OVERLAY:
1978 if( m_overlayBuffer )
1980 break;
1981 }
1982
1983 if( aTarget != TARGET_OVERLAY )
1985 else if( m_overlayBuffer )
1987
1988 // Restore the previous state
1989 m_compositor->SetBuffer( oldTarget );
1990}
1991
1992
1994{
1995 switch( aTarget )
1996 {
1997 default:
1998 case TARGET_CACHED:
1999 case TARGET_NONCACHED: return true;
2000 case TARGET_OVERLAY: return ( m_overlayBuffer != 0 );
2001 case TARGET_TEMP: return ( m_tempBuffer != 0 );
2002 }
2003}
2004
2005
2007{
2009 if( m_tempBuffer )
2010 {
2013 }
2014}
2015
2016
2018{
2019 if( m_tempBuffer )
2020 {
2021 glBlendEquation( GL_MAX );
2023 glBlendEquation( GL_FUNC_ADD );
2024
2026 }
2027 else
2028 {
2029 // Fall back to imperfect alpha blending on single buffer
2030 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
2032 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
2033 }
2034}
2035
2036
2038{
2039 // Store the current cursor type and get the wxCursor for it
2040 if( !GAL::SetNativeCursorStyle( aCursor ) )
2041 return false;
2042
2044
2045 // Update the cursor in the wx control
2046 HIDPI_GL_CANVAS::SetCursor( m_currentwxCursor );
2047
2048 return true;
2049}
2050
2051
2052void OPENGL_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
2053{
2054 aEvent.SetCursor( m_currentwxCursor );
2055}
2056
2057
2058void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
2059{
2060 // Now we should only store the position of the mouse cursor
2061 // The real drawing routines are in blitCursor()
2062 //VECTOR2D screenCursor = m_worldScreenMatrix * aCursorPosition;
2063 //m_cursorPosition = m_screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
2064 m_cursorPosition = aCursorPosition;
2065}
2066
2067
2068void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
2069 const bool aReserve )
2070{
2071 /* Helper drawing: ____--- v3 ^
2072 * ____---- ... \ \
2073 * ____---- ... \ end \
2074 * v1 ____---- ... ____---- \ width
2075 * ---- ...___---- \ \
2076 * \ ___...-- \ v
2077 * \ ____----... ____---- v2
2078 * ---- ... ____----
2079 * start \ ... ____----
2080 * \... ____----
2081 * ----
2082 * v0
2083 * dots mark triangles' hypotenuses
2084 */
2085
2087 * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
2089 * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
2090
2091 VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
2092
2093 if( aReserve )
2094 reserveLineQuads( 1 );
2095
2096 // Line width is maintained by the vertex shader
2098 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2099
2101 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2102
2104 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2105
2107 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2108
2110 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2111
2113 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2114}
2115
2116
2117void OPENGL_GAL::reserveLineQuads( const int aLineCount )
2118{
2119 m_currentManager->Reserve( 6 * aLineCount );
2120}
2121
2122
2123void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2124{
2125 if( m_isFillEnabled )
2126 {
2128 drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
2129 }
2130
2131 if( m_isStrokeEnabled )
2132 {
2134 m_strokeColor.a );
2135 drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
2136 }
2137}
2138
2139
2140void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2141{
2142 Save();
2143
2145 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2146 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2147
2148 /* Draw a triangle that contains the semicircle, then shade it to leave only
2149 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2150 * (if you want to understand more, check the vertex shader source [shader.vert]).
2151 * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
2152 * v2
2153 * /\
2154 * /__\
2155 * v0 //__\\ v1
2156 */
2158 m_currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2159
2161 m_currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2162
2164 m_currentManager->Vertex( 0.0f, aRadius * 2.0f, m_layerDepth ); // v2
2165
2166 Restore();
2167}
2168
2169
2170void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
2171 bool aReserve )
2172{
2173 double outerRadius = aRadius + ( m_lineWidth / 2 );
2174
2175 Save();
2176
2177 if( aReserve )
2179
2180 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2181 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2182
2183 /* Draw a triangle that contains the semicircle, then shade it to leave only
2184 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2185 * (if you want to understand more, check the vertex shader source [shader.vert]), the
2186 * radius and the line width. Shader uses these coordinates to determine if fragments are
2187 * inside the semicircle or not.
2188 * v2
2189 * /\
2190 * /__\
2191 * v0 //__\\ v1
2192 */
2194 m_currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2195
2197 m_currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2198
2200 m_currentManager->Vertex( 0.0f, outerRadius * 2.0f, m_layerDepth ); // v2
2201
2202 Restore();
2203}
2204
2205
2206void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
2207{
2208 if( m_isFillEnabled )
2209 {
2212
2213 // Any non convex polygon needs to be tesselated
2214 // for this purpose the GLU standard functions are used
2216 gluTessBeginPolygon( m_tesselator, &params );
2217 gluTessBeginContour( m_tesselator );
2218
2219 GLdouble* point = aPoints;
2220
2221 for( int i = 0; i < aPointCount; ++i )
2222 {
2223 gluTessVertex( m_tesselator, point, point );
2224 point += 3; // 3 coordinates
2225 }
2226
2227 gluTessEndContour( m_tesselator );
2228 gluTessEndPolygon( m_tesselator );
2229
2230 // Free allocated intersecting points
2231 m_tessIntersects.clear();
2232 }
2233
2234 if( m_isStrokeEnabled )
2235 {
2237 [&]( int idx )
2238 {
2239 return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] );
2240 },
2241 aPointCount );
2242 }
2243}
2244
2245
2246void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
2247 bool aReserve )
2248{
2249 wxCHECK( aPointCount > 0, /* return */ );
2250
2252
2253 if( aPointCount == 1 )
2254 {
2255 drawLineQuad( aPointGetter( 0 ), aPointGetter( 0 ), aReserve );
2256 return;
2257 }
2258
2259 if( aReserve )
2260 {
2261 reserveLineQuads( aPointCount - 1 );
2262 }
2263
2264 for( int i = 1; i < aPointCount; ++i )
2265 {
2266 auto start = aPointGetter( i - 1 );
2267 auto end = aPointGetter( i );
2268
2269 drawLineQuad( start, end, false );
2270 }
2271}
2272
2273
2274void OPENGL_GAL::drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter,
2275 int aPointCount, double aWidth, bool aReserve )
2276{
2277 wxCHECK( aPointCount >= 2, /* return */ );
2278
2280
2281 int vertices = 0;
2282
2283 for( int i = 1; i < aPointCount; ++i )
2284 {
2285 auto start = aPointGetter( i - 1 );
2286 auto end = aPointGetter( i );
2287
2288 VECTOR2D startEndVector = end - start;
2289 double lineLength = startEndVector.EuclideanNorm();
2290
2291 float startx = start.x;
2292 float starty = start.y;
2293 float endx = start.x + lineLength;
2294 float endy = start.y + lineLength;
2295
2296 // Be careful about floating point rounding. As we draw segments in larger and larger
2297 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
2298 // segments. In this case, we need to draw a circle for the minimal segment.
2299 if( startx == endx || starty == endy )
2300 {
2301 vertices += 3; // One circle
2302 continue;
2303 }
2304
2305 if( m_isFillEnabled || aWidth == 1.0 )
2306 {
2307 vertices += 6; // One line
2308 }
2309 else
2310 {
2311 vertices += 6 + 6 + 3 + 3; // Two lines and two half-circles
2312 }
2313 }
2314
2315 m_currentManager->Reserve( vertices );
2316
2317 for( int i = 1; i < aPointCount; ++i )
2318 {
2319 auto start = aPointGetter( i - 1 );
2320 auto end = aPointGetter( i );
2321
2322 drawSegment( start, end, aWidth, false );
2323 }
2324}
2325
2326
2327int OPENGL_GAL::drawBitmapChar( unsigned long aChar, bool aReserve )
2328{
2329 const float TEX_X = font_image.width;
2330 const float TEX_Y = font_image.height;
2331
2332 // handle space
2333 if( aChar == ' ' )
2334 {
2335 const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
2336 wxCHECK( g, 0 );
2337
2338 // Match stroke font as well as possible
2339 double spaceWidth = g->advance * 0.74;
2340
2341 Translate( VECTOR2D( spaceWidth, 0 ) );
2342 return KiROUND( spaceWidth );
2343 }
2344
2345 const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
2346
2347 // If the glyph is not found (happens for many esoteric unicode chars)
2348 // shows a '?' instead.
2349 if( !glyph )
2350 glyph = LookupGlyph( '?' );
2351
2352 if( !glyph ) // Should not happen.
2353 return 0;
2354
2355 const float X = glyph->atlas_x + font_information.smooth_pixels;
2356 const float Y = glyph->atlas_y + font_information.smooth_pixels;
2357 const float XOFF = glyph->minx;
2358
2359 // adjust for height rounding
2360 const float round_adjust = ( glyph->maxy - glyph->miny )
2361 - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
2362 const float top_adjust = font_information.max_y - glyph->maxy;
2363 const float YOFF = round_adjust + top_adjust;
2364 const float W = glyph->atlas_w - font_information.smooth_pixels * 2;
2365 const float H = glyph->atlas_h - font_information.smooth_pixels * 2;
2366 const float B = 0;
2367
2368 if( aReserve )
2370
2371 Translate( VECTOR2D( XOFF, YOFF ) );
2372
2373 /* Glyph:
2374 * v0 v1
2375 * +--+
2376 * | /|
2377 * |/ |
2378 * +--+
2379 * v2 v3
2380 */
2381 m_currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
2382 m_currentManager->Vertex( -B, -B, 0 ); // v0
2383
2384 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2385 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2386
2387 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2388 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2389
2390
2391 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2392 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2393
2394 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2395 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2396
2397 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
2398 m_currentManager->Vertex( W + B, H + B, 0 ); // v3
2399
2400 Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
2401
2402 return glyph->advance;
2403}
2404
2405
2406void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight, bool aReserve )
2407{
2408 // To draw an overbar, simply draw an overbar
2409 const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
2410 wxCHECK( glyph, /* void */ );
2411
2412 const float H = glyph->maxy - glyph->miny;
2413
2414 Save();
2415
2416 Translate( VECTOR2D( -aLength, -aHeight ) );
2417
2418 if( aReserve )
2420
2422
2424
2425 m_currentManager->Vertex( 0, 0, 0 ); // v0
2426 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2427 m_currentManager->Vertex( 0, H, 0 ); // v2
2428
2429 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2430 m_currentManager->Vertex( 0, H, 0 ); // v2
2431 m_currentManager->Vertex( aLength, H, 0 ); // v3
2432
2433 Restore();
2434}
2435
2436
2437std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
2438{
2439 static const FONT_GLYPH_TYPE* defaultGlyph = LookupGlyph( '(' ); // for strange chars
2440
2441 VECTOR2D textSize( 0, 0 );
2442 float commonOffset = std::numeric_limits<float>::max();
2443 float charHeight = font_information.max_y - defaultGlyph->miny;
2444 int overbarDepth = -1;
2445 int braceNesting = 0;
2446
2447 for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
2448 {
2449 if( *chIt == '~' && overbarDepth == -1 )
2450 {
2451 UTF8::uni_iter lookahead = chIt;
2452
2453 if( ++lookahead != end && *lookahead == '{' )
2454 {
2455 chIt = lookahead;
2456 overbarDepth = braceNesting;
2457 braceNesting++;
2458 continue;
2459 }
2460 }
2461 else if( *chIt == '{' )
2462 {
2463 braceNesting++;
2464 }
2465 else if( *chIt == '}' )
2466 {
2467 if( braceNesting > 0 )
2468 braceNesting--;
2469
2470 if( braceNesting == overbarDepth )
2471 {
2472 overbarDepth = -1;
2473 continue;
2474 }
2475 }
2476
2477 const FONT_GLYPH_TYPE* glyph = LookupGlyph( *chIt );
2478
2479 if( !glyph // Not coded in font
2480 || *chIt == '-' || *chIt == '_' ) // Strange size of these 2 chars
2481 {
2482 glyph = defaultGlyph;
2483 }
2484
2485 if( glyph )
2486 textSize.x += glyph->advance;
2487 }
2488
2489 textSize.y = std::max<float>( textSize.y, charHeight );
2490 commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
2491 textSize.y -= commonOffset;
2492
2493 return std::make_pair( textSize, commonOffset );
2494}
2495
2496
2497void OPENGL_GAL::onPaint( wxPaintEvent& aEvent )
2498{
2499 PostPaint( aEvent );
2500}
2501
2502
2503void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
2504{
2505 // Post the mouse event to the event listener registered in constructor, if any
2506 if( m_mouseListener )
2507 wxPostEvent( m_mouseListener, aEvent );
2508}
2509
2510
2512{
2513 if( !IsCursorEnabled() )
2514 return;
2515
2517
2518 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
2519
2520 VECTOR2D cursorBegin = m_cursorPosition - cursorSize / ( 2 * m_worldScale );
2521 VECTOR2D cursorEnd = m_cursorPosition + cursorSize / ( 2 * m_worldScale );
2522 VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
2523
2524 const COLOR4D cColor = getCursorColor();
2525 const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a, cColor.b * cColor.a, 1.0 );
2526
2527 glActiveTexture( GL_TEXTURE0 );
2528 glDisable( GL_TEXTURE_2D );
2529 glLineWidth( 1.0 );
2530 glColor4d( color.r, color.g, color.b, color.a );
2531
2532 glBegin( GL_LINES );
2533 glVertex2d( cursorCenter.x, cursorBegin.y );
2534 glVertex2d( cursorCenter.x, cursorEnd.y );
2535
2536 glVertex2d( cursorBegin.x, cursorCenter.y );
2537 glVertex2d( cursorEnd.x, cursorCenter.y );
2538 glEnd();
2539}
2540
2541
2543{
2544 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
2545 wxT( "There are no free slots to store a group" ) );
2546
2547 while( m_groups.find( m_groupCounter ) != m_groups.end() )
2549
2550 return m_groupCounter++;
2551}
2552
2553
2555{
2556#ifndef KICAD_USE_EGL
2557 wxASSERT( IsShownOnScreen() );
2558#endif // KICAD_USE_EGL
2559
2560 wxASSERT_MSG( m_isContextLocked, "This should only be called from within a locked context." );
2561
2562 // Check correct initialization from the constructor
2563 if( m_tesselator == nullptr )
2564 throw std::runtime_error( "Could not create the tesselator" );
2565 GLenum err = glewInit();
2566
2567#ifdef KICAD_USE_EGL
2568 // TODO: better way to check when EGL is ready (init fails at "getString(GL_VERSION)")
2569 for( int i = 0; i < 10; i++ )
2570 {
2571 if( GLEW_OK == err )
2572 break;
2573
2574 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
2575 err = glewInit();
2576 }
2577
2578#endif // KICAD_USE_EGL
2579
2580 if( GLEW_OK != err )
2581 throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2582
2583 // Check the OpenGL version (minimum 2.1 is required)
2584 if( !GLEW_VERSION_2_1 )
2585 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2586
2587#if defined( __LINUX__ ) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2588#ifdef DEBUG
2589 if( GLEW_ARB_debug_output )
2590 enableGlDebug( true );
2591#endif
2592#endif
2593
2594 // Framebuffers have to be supported
2595 if( !GLEW_EXT_framebuffer_object )
2596 throw std::runtime_error( "Framebuffer objects are not supported!" );
2597
2598 // Vertex buffer has to be supported
2599 if( !GLEW_ARB_vertex_buffer_object )
2600 throw std::runtime_error( "Vertex buffer objects are not supported!" );
2601
2602 // Prepare shaders
2603 if( !m_shader->IsLinked()
2605 BUILTIN_SHADERS::glsl_kicad_vert ) )
2606 {
2607 throw std::runtime_error( "Cannot compile vertex shader!" );
2608 }
2609
2610 if( !m_shader->IsLinked()
2612 BUILTIN_SHADERS::glsl_kicad_frag ) )
2613 {
2614 throw std::runtime_error( "Cannot compile fragment shader!" );
2615 }
2616
2617 if( !m_shader->IsLinked() && !m_shader->Link() )
2618 throw std::runtime_error( "Cannot link the shaders!" );
2619
2620 // Check if video card supports textures big enough to fit the font atlas
2621 int maxTextureSize;
2622 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2623
2624 if( maxTextureSize < (int) font_image.width || maxTextureSize < (int) font_image.height )
2625 {
2626 // TODO implement software texture scaling
2627 // for bitmap fonts and use a higher resolution texture?
2628 throw std::runtime_error( "Requested texture size is not supported" );
2629 }
2630
2632
2633 m_cachedManager = new VERTEX_MANAGER( true );
2634 m_nonCachedManager = new VERTEX_MANAGER( false );
2635 m_overlayManager = new VERTEX_MANAGER( false );
2636 m_tempManager = new VERTEX_MANAGER( false );
2637
2638 // Make VBOs use shaders
2643
2644 m_isInitialized = true;
2645}
2646
2647
2648// Callback functions for the tesselator. Compare Redbook Chapter 11.
2649void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2650{
2651 GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2652 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2653 VERTEX_MANAGER* vboManager = param->vboManager;
2654
2655 assert( vboManager );
2656 vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2657}
2658
2659
2660void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4],
2661 GLdouble** dataOut, void* aData )
2662{
2663 GLdouble* vertex = new GLdouble[3];
2664 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2665
2666 // Save the pointer so we can delete it later
2667 // Note, we use the default_delete for an array because macOS
2668 // decides to bundle an ancient libc++ that mismatches the C++17 support of clang
2669 param->intersectPoints.emplace_back( vertex, std::default_delete<GLdouble[]>() );
2670
2671 memcpy( vertex, coords, 3 * sizeof( GLdouble ) );
2672
2673 *dataOut = vertex;
2674}
2675
2676
2677void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2678{
2679 // This callback is needed to force GLU tesselator to use triangles only
2680}
2681
2682
2683void CALLBACK ErrorCallback( GLenum aErrorCode )
2684{
2685 //throw std::runtime_error( std::string( "Tessellation error: " ) +
2686 //std::string( (const char*) gluErrorString( aErrorCode ) );
2687}
2688
2689
2690static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2691{
2692 gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, (void( CALLBACK* )()) VertexCallback );
2693 gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, (void( CALLBACK* )()) CombineCallback );
2694 gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, (void( CALLBACK* )()) EdgeCallback );
2695 gluTessCallback( aTesselator, GLU_TESS_ERROR, (void( CALLBACK* )()) ErrorCallback );
2696}
2697
2698void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2699{
2700 m_cachedManager->EnableDepthTest( aEnabled );
2702 m_overlayManager->EnableDepthTest( aEnabled );
2703}
2704
2705
2706inline double round_to_half_pixel( double f, double r )
2707{
2708 return ( ceil( f / r ) - 0.5 ) * r;
2709}
2710
2711
2713{
2715 auto pixelSize = m_worldScale;
2716
2717 // we need -m_lookAtPoint == -k * pixelSize + 0.5 * pixelSize for OpenGL
2718 // meaning m_lookAtPoint = (k-0.5)*pixelSize with integer k
2721
2723}
2724
2725
2726void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2727{
2728 if( aGlyph.IsStroke() )
2729 {
2730 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2731
2732 DrawPolylines( strokeGlyph );
2733 }
2734 else if( aGlyph.IsOutline() )
2735 {
2736 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2737
2740
2741 outlineGlyph.Triangulate(
2742 [&]( const VECTOR2D& aPt1, const VECTOR2D& aPt2, const VECTOR2D& aPt3 )
2743 {
2745
2746 m_currentManager->Vertex( aPt1.x, aPt1.y, m_layerDepth );
2747 m_currentManager->Vertex( aPt2.x, aPt2.y, m_layerDepth );
2748 m_currentManager->Vertex( aPt3.x, aPt3.y, m_layerDepth );
2749 } );
2750 }
2751}
2752
2753
2754void OPENGL_GAL::DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs )
2755{
2756 if( aGlyphs.empty() )
2757 return;
2758
2759 bool allGlyphsAreStroke = true;
2760 bool allGlyphsAreOutline = true;
2761
2762 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2763 {
2764 if( !glyph->IsStroke() )
2765 {
2766 allGlyphsAreStroke = false;
2767 break;
2768 }
2769 }
2770
2771 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2772 {
2773 if( !glyph->IsOutline() )
2774 {
2775 allGlyphsAreOutline = false;
2776 break;
2777 }
2778 }
2779
2780 if( allGlyphsAreStroke )
2781 {
2782 // Optimized path for stroke fonts that pre-reserves line quads.
2783 int lineQuadCount = 0;
2784
2785 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2786 {
2787 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2788
2789 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2790 lineQuadCount += points.size() - 1;
2791 }
2792
2793 reserveLineQuads( lineQuadCount );
2794
2795 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2796 {
2797 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2798
2799 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2800 {
2802 [&]( int idx )
2803 {
2804 return points[idx];
2805 },
2806 points.size(), false );
2807 }
2808 }
2809
2810 return;
2811 }
2812 else if( allGlyphsAreOutline )
2813 {
2814 // Optimized path for stroke fonts that pre-reserves glyph triangles.
2815 int triangleCount = 0;
2816
2817 if( aGlyphs.size() > 0 )
2818 {
2820
2821 tp.push_loop( aGlyphs.size(),
2822 [&]( const int a, const int b)
2823 {
2824 for( int ii = a; ii < b; ++ii )
2825 {
2826 auto glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( aGlyphs.at( ii ).get() );
2827
2828 // Only call CacheTriangulation() if it has never been done before.
2829 // Otherwise we'll hash the triangulation to see if it has been edited,
2830 // and all our glpyh editing ops update the triangulation anyway.
2831 if( glyph->TriangulatedPolyCount() == 0 )
2832 glyph->CacheTriangulation( false );
2833 }
2834 } );
2835 tp.wait_for_tasks();
2836 }
2837
2838 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2839 {
2840 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2841
2842 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2843 {
2845 outlineGlyph.TriangulatedPolygon( i );
2846
2847 triangleCount += polygon->GetTriangleCount();
2848 }
2849 }
2850
2853
2854 m_currentManager->Reserve( 3 * triangleCount );
2855
2856 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2857 {
2858 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2859
2860 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2861 {
2863 outlineGlyph.TriangulatedPolygon( i );
2864
2865 for( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
2866 {
2867 VECTOR2I a, b, c;
2868 polygon->GetTriangle( j, a, b, c );
2869
2873 }
2874 }
2875 }
2876 }
2877 else
2878 {
2879 // Regular path
2880 for( size_t i = 0; i < aGlyphs.size(); i++ )
2881 DrawGlyph( *aGlyphs[i], i, aGlyphs.size() );
2882 }
2883}
int color
Definition: DXF_plotter.cpp:58
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:48
const wxImage * GetOriginalImageData() const
Definition: bitmap_base.h:70
VECTOR2I GetSizePixels() const
Definition: bitmap_base.h:106
EDA_ANGLE Rotation() const
Definition: bitmap_base.h:212
bool IsMirroredX() const
Definition: bitmap_base.h:210
bool IsMirroredY() const
Definition: bitmap_base.h:211
KIID GetImageID() const
Definition: bitmap_base.h:75
int GetPPI() const
Definition: bitmap_base.h:117
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:57
virtual bool IsOutline() const
Definition: glyph.h:56
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:376
double g
Green component.
Definition: color4d.h:377
double a
Alpha component.
Definition: color4d.h:379
static const COLOR4D BLACK
Definition: color4d.h:386
double b
Blue component.
Definition: color4d.h:378
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:160
const size_t m_cacheMaxElements
Definition: opengl_gal.cpp:105
GLuint RequestBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:124
std::list< GLuint > m_freedTextureIds
Definition: opengl_gal.cpp:111
const size_t m_cacheMaxSize
Definition: opengl_gal.cpp:106
std::map< const KIID, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:108
std::list< KIID > m_cacheLru
Definition: opengl_gal.cpp:109
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:71
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:770
unsigned int m_groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:346
void EndDiffLayer() override
Ends rendering of a differential layer.
VERTEX_MANAGER * m_overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:351
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:369
VERTEX_MANAGER * m_currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs).
Definition: opengl_gal.h:347
void drawCircle(const VECTOR2D &aCenterPoint, double aRadius, bool aReserve=true)
Internal method for circle drawing.
Definition: opengl_gal.cpp:834
std::deque< std::shared_ptr< GLdouble > > m_tessIntersects
Definition: opengl_gal.h:385
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:828
void LockContext(int aClientCookie) override
Use GAL_CONTEXT_LOCKER RAII object unless you know what you're doing.
Definition: opengl_gal.cpp:706
unsigned int m_mainBuffer
Main rendering target.
Definition: opengl_gal.h:356
std::unique_ptr< GL_BITMAP_CACHE > m_bitmapCache
Definition: opengl_gal.h:381
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:394
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:368
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 DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aWidth, double aMaxError) override
Draw an arc segment.
Definition: opengl_gal.cpp:967
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:435
unsigned int m_overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:357
void PostPaint(wxPaintEvent &aEvent)
Function PostPaint posts an event to m_paint_listener.
Definition: opengl_gal.cpp:427
OPENGL_COMPOSITOR * m_compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:355
VERTEX_MANAGER * m_cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:349
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:374
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:377
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:111
wxEvtHandler * m_mouseListener
Definition: opengl_gal.h:337
int m_swapInterval
Used to store swap interval information.
Definition: opengl_gal.h:335
void ClearCache() override
Delete all data created during caching of graphic items.
double getWorldPixelSize() const
Definition: opengl_gal.cpp:464
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:763
GROUPS_MAP m_groups
Stores information about VBO objects (groups)
Definition: opengl_gal.h:345
void endUpdate() override
Definition: opengl_gal.cpp:746
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:350
int drawBitmapChar(unsigned long aChar, bool aReserve=true)
Draw a single character using bitmap font.
GLUtesselator * m_tesselator
Definition: opengl_gal.h:384
wxEvtHandler * m_paintListener
Definition: opengl_gal.h:338
void StartDiffLayer() override
Begins rendering of a differential layer.
bool m_isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:372
void Save() override
Save the context.
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: opengl_gal.h:379
bool m_isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:366
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:340
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:755
OPENGL_GAL(const KIGFX::VC_SETTINGS &aVcSettings, GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("GLCanvas"))
Definition: opengl_gal.cpp:241
void beginUpdate() override
Definition: opengl_gal.cpp:730
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:569
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:371
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: opengl_gal.cpp:479
GLint ufm_screenPixelSize
Definition: opengl_gal.h:375
void Rotate(double aAngle) override
Rotate the context.
int BeginGroup() override
Begin a group.
GLint ufm_pixelSizeMultiplier
Definition: opengl_gal.h:376
VECTOR2D getScreenPixelSize() const
Definition: opengl_gal.cpp:471
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:716
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:352
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:648
SHADER * m_shader
There is only one shader used for different objects.
Definition: opengl_gal.h:362
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle) override
Draw an arc.
Definition: opengl_gal.cpp:895
void DrawPolylines(const std::vector< std::vector< VECTOR2D > > &aPointLists) override
Draw multiple polylines.
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: opengl_gal.h:359
wxGLContext * m_glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:334
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:367
static wxGLContext * m_glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:333
unsigned int m_tempBuffer
Temporary rendering target (for diffing etc.)
Definition: opengl_gal.h:358
void init()
Basic OpenGL initialization and feature checks.
static int m_instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:336
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:49
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)
Return the aIndex-th subpolygon in the set.
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of triangulated polygons.
int OutlineCount() const
Return the number of outlines in the set.
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:63
T x
Definition: vector3.h:62
@ BLUE
Definition: color4d.h:56
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:435
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:247
static LIB_SYMBOL * dummy()
Used when a LIB_SYMBOL is not found in library to draw a dummy shape.
@ 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:426
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:75
#define SEG_PER_CIRCLE_COUNT
Definition: opengl_gal.h:53
#define CALLBACK
The default number of points for circle approximation.
Definition: opengl_gal.h:49
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:323
std::deque< std::shared_ptr< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:326
Structure to keep VIEW_CONTROLS settings for easy store/restore operations.
Definition: view_controls.h:43
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
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
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:587
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588
VECTOR2I ToVECTOR2I(const wxSize &aSize)
Definition: vector2wx.h:30