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