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