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 normalize( 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 normalize( 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 {
1422
1423 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1424 {
1425 auto triPoly = aPolySet.TriangulatedPolygon( j );
1426
1427 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1428 {
1429 VECTOR2I a, b, c;
1430 triPoly->GetTriangle( i, a, b, c );
1431 DrawLine( a, b );
1432 DrawLine( b, c );
1433 DrawLine( c, a );
1434 }
1435 }
1436 }
1437}
1438
1439
1440void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
1441{
1442 if( aPolySet.IsTriangulationUpToDate() )
1443 {
1444 drawTriangulatedPolyset( aPolySet, aStrokeTriangulation );
1445 return;
1446 }
1447
1448 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1449 {
1450 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1451 DrawPolygon( outline );
1452 }
1453}
1454
1455
1457{
1458 wxCHECK( aPolygon.PointCount() >= 2, /* void */ );
1459
1460 const int pointCount = aPolygon.SegmentCount() + 1;
1461 std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1462 GLdouble* ptr = points.get();
1463
1464 for( int i = 0; i < pointCount; ++i )
1465 {
1466 const VECTOR2I& p = aPolygon.CPoint( i );
1467 *ptr++ = p.x;
1468 *ptr++ = p.y;
1469 *ptr++ = m_layerDepth;
1470 }
1471
1472 drawPolygon( points.get(), pointCount );
1473}
1474
1475
1476void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1477 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
1478 double aFilterValue )
1479{
1480 std::vector<VECTOR2D> output;
1481 std::vector<VECTOR2D> pointCtrl;
1482
1483 pointCtrl.push_back( aStartPoint );
1484 pointCtrl.push_back( aControlPointA );
1485 pointCtrl.push_back( aControlPointB );
1486 pointCtrl.push_back( aEndPoint );
1487
1488 BEZIER_POLY converter( pointCtrl );
1489 converter.GetPoly( output, aFilterValue );
1490
1491 if( output.size() == 1 )
1492 output.push_back( output.front() );
1493
1494 DrawPolygon( &output[0], output.size() );
1495}
1496
1497
1498void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
1499{
1500 GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
1501
1502 // We have to calculate the pixel size in users units to draw the image.
1503 // m_worldUnitLength is a factor used for converting IU to inches
1504 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
1505 double w = (double) aBitmap.GetSizePixels().x * scale;
1506 double h = (double) aBitmap.GetSizePixels().y * scale;
1507
1508 auto xform = m_currentManager->GetTransformation();
1509
1510 glm::vec4 v0 = xform * glm::vec4( -w / 2, -h / 2, 0.0, 0.0 );
1511 glm::vec4 v1 = xform * glm::vec4( w / 2, h / 2, 0.0, 0.0 );
1512 glm::vec4 trans = xform[3];
1513
1514 auto texture_id = m_bitmapCache->RequestBitmap( &aBitmap );
1515
1516 if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1517 return;
1518
1519 glDepthFunc( GL_ALWAYS );
1520
1521 glAlphaFunc( GL_GREATER, 0.01f );
1522 glEnable( GL_ALPHA_TEST );
1523
1524 glMatrixMode( GL_TEXTURE );
1525 glPushMatrix();
1526 glTranslated( 0.5, 0.5, 0.5 );
1527 glRotated( aBitmap.Rotation().AsDegrees(), 0, 0, 1 );
1528 glTranslated( -0.5, -0.5, -0.5 );
1529
1530 glMatrixMode( GL_MODELVIEW );
1531 glPushMatrix();
1532 glTranslated( trans.x, trans.y, trans.z );
1533
1534 glEnable( GL_TEXTURE_2D );
1535 glActiveTexture( GL_TEXTURE0 );
1536 glBindTexture( GL_TEXTURE_2D, texture_id );
1537
1538 float texStartX = aBitmap.IsMirroredX() ? 1.0 : 0.0;
1539 float texEndX = aBitmap.IsMirroredX() ? 0.0 : 1.0;
1540 float texStartY = aBitmap.IsMirroredY() ? 1.0 : 0.0;
1541 float texEndY = aBitmap.IsMirroredY() ? 0.0 : 1.0;
1542
1543 glBegin( GL_QUADS );
1544 glColor4f( 1.0, 1.0, 1.0, alpha );
1545 glTexCoord2f( texStartX, texStartY );
1546 glVertex3f( v0.x, v0.y, m_layerDepth );
1547 glColor4f( 1.0, 1.0, 1.0, alpha );
1548 glTexCoord2f( texEndX, texStartY);
1549 glVertex3f( v1.x, v0.y, m_layerDepth );
1550 glColor4f( 1.0, 1.0, 1.0, alpha );
1551 glTexCoord2f( texEndX, texEndY);
1552 glVertex3f( v1.x, v1.y, m_layerDepth );
1553 glColor4f( 1.0, 1.0, 1.0, alpha );
1554 glTexCoord2f( texStartX, texEndY);
1555 glVertex3f( v0.x, v1.y, m_layerDepth );
1556 glEnd();
1557
1558 glBindTexture( GL_TEXTURE_2D, 0 );
1559
1560#ifdef DISABLE_BITMAP_CACHE
1561 glDeleteTextures( 1, &texture_id );
1562#endif
1563
1564 glPopMatrix();
1565
1566 glMatrixMode( GL_TEXTURE );
1567 glPopMatrix();
1568 glMatrixMode( GL_MODELVIEW );
1569
1570 glDisable( GL_ALPHA_TEST );
1571
1572 glDepthFunc( GL_LESS );
1573}
1574
1575
1576void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2I& aPosition,
1577 const EDA_ANGLE& aAngle )
1578{
1579 // Fallback to generic impl (which uses the stroke font) on cases we don't handle
1580 if( IsTextMirrored()
1581 || aText.Contains( wxT( "^{" ) )
1582 || aText.Contains( wxT( "_{" ) )
1583 || aText.Contains( wxT( "\n" ) ) )
1584 {
1585 return GAL::BitmapText( aText, aPosition, aAngle );
1586 }
1587
1588 const UTF8 text( aText );
1589 VECTOR2D textSize;
1590 float commonOffset;
1591 std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1592
1593 const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1594 double overbarHeight = textSize.y;
1595
1596 Save();
1597
1599 m_currentManager->Translate( aPosition.x, aPosition.y, m_layerDepth );
1600 m_currentManager->Rotate( aAngle.AsRadians(), 0.0f, 0.0f, -1.0f );
1601
1602 double sx = SCALE * ( m_globalFlipX ? -1.0 : 1.0 );
1603 double sy = SCALE * ( m_globalFlipY ? -1.0 : 1.0 );
1604
1605 m_currentManager->Scale( sx, sy, 0 );
1606 m_currentManager->Translate( 0, -commonOffset, 0 );
1607
1608 switch( GetHorizontalJustify() )
1609 {
1611 Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1612 break;
1613
1615 //if( !IsTextMirrored() )
1616 Translate( VECTOR2D( -textSize.x, 0 ) );
1617 break;
1618
1620 //if( IsTextMirrored() )
1621 //Translate( VECTOR2D( -textSize.x, 0 ) );
1622 break;
1623
1625 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
1626 break;
1627 }
1628
1629 switch( GetVerticalJustify() )
1630 {
1632 break;
1633
1635 Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1636 overbarHeight = 0;
1637 break;
1638
1640 Translate( VECTOR2D( 0, -textSize.y ) );
1641 overbarHeight = -textSize.y / 2.0;
1642 break;
1643
1645 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
1646 break;
1647 }
1648
1649 int overbarLength = 0;
1650 int overbarDepth = -1;
1651 int braceNesting = 0;
1652
1653 auto iterateString =
1654 [&]( const std::function<void( int aOverbarLength, int aOverbarHeight )>& overbarFn,
1655 const std::function<int( unsigned long aChar )>& bitmapCharFn )
1656 {
1657 for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1658 {
1659 wxASSERT_MSG( *chIt != '\n' && *chIt != '\r',
1660 "No support for multiline bitmap text yet" );
1661
1662 if( *chIt == '~' && overbarDepth == -1 )
1663 {
1664 UTF8::uni_iter lookahead = chIt;
1665
1666 if( ++lookahead != end && *lookahead == '{' )
1667 {
1668 chIt = lookahead;
1669 overbarDepth = braceNesting;
1670 braceNesting++;
1671 continue;
1672 }
1673 }
1674 else if( *chIt == '{' )
1675 {
1676 braceNesting++;
1677 }
1678 else if( *chIt == '}' )
1679 {
1680 if( braceNesting > 0 )
1681 braceNesting--;
1682
1683 if( braceNesting == overbarDepth )
1684 {
1685 overbarFn( overbarLength, overbarHeight );
1686 overbarLength = 0;
1687
1688 overbarDepth = -1;
1689 continue;
1690 }
1691 }
1692
1693 if( overbarDepth != -1 )
1694 overbarLength += bitmapCharFn( *chIt );
1695 else
1696 bitmapCharFn( *chIt );
1697 }
1698 };
1699
1700 // First, calculate the amount of characters and overbars to reserve
1701
1702 int charsCount = 0;
1703 int overbarsCount = 0;
1704
1705 iterateString(
1706 [&overbarsCount]( int aOverbarLength, int aOverbarHeight )
1707 {
1708 overbarsCount++;
1709 },
1710 [&charsCount]( unsigned long aChar ) -> int
1711 {
1712 if( aChar != ' ' )
1713 charsCount++;
1714
1715 return 0;
1716 } );
1717
1718 m_currentManager->Reserve( 6 * charsCount + 6 * overbarsCount );
1719
1720 // Now reset the state and actually draw the characters and overbars
1721 overbarLength = 0;
1722 overbarDepth = -1;
1723 braceNesting = 0;
1724
1725 iterateString(
1726 [&]( int aOverbarLength, int aOverbarHeight )
1727 {
1728 drawBitmapOverbar( aOverbarLength, aOverbarHeight, false );
1729 },
1730 [&]( unsigned long aChar ) -> int
1731 {
1732 return drawBitmapChar( aChar, false );
1733 } );
1734
1735 // Handle the case when overbar is active till the end of the drawn text
1736 m_currentManager->Translate( 0, commonOffset, 0 );
1737
1738 if( overbarDepth != -1 && overbarLength > 0 )
1739 drawBitmapOverbar( overbarLength, overbarHeight );
1740
1741 Restore();
1742}
1743
1744
1746{
1749
1751
1752 // sub-pixel lines all render the same
1753 float minorLineWidth = std::fmax( 1.0f,
1755 float majorLineWidth = minorLineWidth * 2.0f;
1756
1757 // Draw the axis and grid
1758 // For the drawing the start points, end points and increments have
1759 // to be calculated in world coordinates
1760 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1762
1763 // Draw axes if desired
1764 if( m_axesEnabled )
1765 {
1766 SetLineWidth( minorLineWidth );
1768
1769 DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1770 DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1771 }
1772
1773 // force flush
1775
1776 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1777 return;
1778
1779 VECTOR2D gridScreenSize = GetVisibleGridSize();
1780
1781 // Compute grid starting and ending indexes to draw grid points on the
1782 // visible screen area
1783 // Note: later any point coordinate will be offset by m_gridOrigin
1784 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1785 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1786 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1787 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1788
1789 // Ensure start coordinate < end coordinate
1790 normalize( gridStartX, gridEndX );
1791 normalize( gridStartY, gridEndY );
1792
1793 // Ensure the grid fills the screen
1794 --gridStartX;
1795 ++gridEndX;
1796 --gridStartY;
1797 ++gridEndY;
1798
1799 glDisable( GL_DEPTH_TEST );
1800 glDisable( GL_TEXTURE_2D );
1801
1803 {
1804 glEnable( GL_STENCIL_TEST );
1805 glStencilFunc( GL_ALWAYS, 1, 1 );
1806 glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1807 glColor4d( 0.0, 0.0, 0.0, 0.0 );
1808 SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1809 }
1810 else
1811 {
1814 }
1815
1817 {
1818 // Vertical positions
1819 for( int j = gridStartY; j <= gridEndY; j++ )
1820 {
1821 bool tickY = ( j % m_gridTick == 0 );
1822 const double posY = j * gridScreenSize.y + m_gridOrigin.y;
1823
1824 // Horizontal positions
1825 for( int i = gridStartX; i <= gridEndX; i++ )
1826 {
1827 bool tickX = ( i % m_gridTick == 0 );
1828 SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1829 auto lineLen = 2.0 * GetLineWidth();
1830 auto posX = i * gridScreenSize.x + m_gridOrigin.x;
1831
1832 DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1833 DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1834 }
1835 }
1836
1838 }
1839 else
1840 {
1841 // Vertical lines
1842 for( int j = gridStartY; j <= gridEndY; j++ )
1843 {
1844 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1845
1846 // If axes are drawn, skip the lines that would cover them
1847 if( m_axesEnabled && y == 0.0 )
1848 continue;
1849
1850 SetLineWidth( ( j % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1851 VECTOR2D a( gridStartX * gridScreenSize.x + m_gridOrigin.x, y );
1852 VECTOR2D b( gridEndX * gridScreenSize.x + m_gridOrigin.x, y );
1853
1854 DrawLine( a, b );
1855 }
1856
1858
1860 {
1861 glStencilFunc( GL_NOTEQUAL, 0, 1 );
1864 }
1865
1866 // Horizontal lines
1867 for( int i = gridStartX; i <= gridEndX; i++ )
1868 {
1869 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1870
1871 // If axes are drawn, skip the lines that would cover them
1872 if( m_axesEnabled && x == 0.0 )
1873 continue;
1874
1875 SetLineWidth( ( i % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1876 VECTOR2D a( x, gridStartY * gridScreenSize.y + m_gridOrigin.y );
1877 VECTOR2D b( x, gridEndY * gridScreenSize.y + m_gridOrigin.y );
1878 DrawLine( a, b );
1879 }
1880
1882
1884 glDisable( GL_STENCIL_TEST );
1885 }
1886
1887 glEnable( GL_DEPTH_TEST );
1888 glEnable( GL_TEXTURE_2D );
1889}
1890
1891
1892void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1893{
1894 m_screenSize = VECTOR2I( aWidth, aHeight );
1895
1896 // Resize framebuffers
1897 const float scaleFactor = GetScaleFactor();
1898 m_compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1900
1901 wxGLCanvas::SetSize( aWidth, aHeight );
1902}
1903
1904
1905bool OPENGL_GAL::Show( bool aShow )
1906{
1907 bool s = wxGLCanvas::Show( aShow );
1908
1909 if( aShow )
1910 wxGLCanvas::Raise();
1911
1912 return s;
1913}
1914
1915
1917{
1918 glFlush();
1919}
1920
1921
1923{
1924 // Clear screen
1926
1927 // NOTE: Black used here instead of m_clearColor; it will be composited later
1928 glClearColor( 0, 0, 0, 1 );
1929 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1930}
1931
1932
1933void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1934{
1935 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1936
1937 matrixData[0] = aTransformation.m_data[0][0];
1938 matrixData[1] = aTransformation.m_data[1][0];
1939 matrixData[2] = aTransformation.m_data[2][0];
1940 matrixData[4] = aTransformation.m_data[0][1];
1941 matrixData[5] = aTransformation.m_data[1][1];
1942 matrixData[6] = aTransformation.m_data[2][1];
1943 matrixData[12] = aTransformation.m_data[0][2];
1944 matrixData[13] = aTransformation.m_data[1][2];
1945 matrixData[14] = aTransformation.m_data[2][2];
1946
1947 glMultMatrixd( matrixData );
1948}
1949
1950
1951void OPENGL_GAL::Rotate( double aAngle )
1952{
1953 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1954}
1955
1956
1957void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1958{
1959 m_currentManager->Translate( aVector.x, aVector.y, 0.0f );
1960}
1961
1962
1963void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1964{
1965 m_currentManager->Scale( aScale.x, aScale.y, 1.0f );
1966}
1967
1968
1970{
1972}
1973
1974
1976{
1978}
1979
1980
1982{
1983 m_isGrouping = true;
1984
1985 std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *m_cachedManager );
1986 int groupNumber = getNewGroupNumber();
1987 m_groups.insert( std::make_pair( groupNumber, newItem ) );
1988
1989 return groupNumber;
1990}
1991
1992
1994{
1996 m_isGrouping = false;
1997}
1998
1999
2000void OPENGL_GAL::DrawGroup( int aGroupNumber )
2001{
2002 auto group = m_groups.find( aGroupNumber );
2003
2004 if( group != m_groups.end() )
2005 m_cachedManager->DrawItem( *group->second );
2006}
2007
2008
2009void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
2010{
2011 auto group = m_groups.find( aGroupNumber );
2012
2013 if( group != m_groups.end() )
2014 m_cachedManager->ChangeItemColor( *group->second, aNewColor );
2015}
2016
2017
2018void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
2019{
2020 auto group = m_groups.find( aGroupNumber );
2021
2022 if( group != m_groups.end() )
2023 m_cachedManager->ChangeItemDepth( *group->second, aDepth );
2024}
2025
2026
2027void OPENGL_GAL::DeleteGroup( int aGroupNumber )
2028{
2029 // Frees memory in the container as well
2030 m_groups.erase( aGroupNumber );
2031}
2032
2033
2035{
2036 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
2037
2038 m_groups.clear();
2039
2040 if( m_isInitialized )
2042}
2043
2044
2046{
2047 switch( aTarget )
2048 {
2049 default:
2054 }
2055
2056 m_currentTarget = aTarget;
2057}
2058
2059
2061{
2062 return m_currentTarget;
2063}
2064
2065
2067{
2068 // Save the current state
2069 unsigned int oldTarget = m_compositor->GetBuffer();
2070
2071 switch( aTarget )
2072 {
2073 // Cached and noncached items are rendered to the same buffer
2074 default:
2075 case TARGET_CACHED:
2076 case TARGET_NONCACHED:
2078 break;
2079
2080 case TARGET_TEMP:
2081 if( m_tempBuffer )
2083 break;
2084
2085 case TARGET_OVERLAY:
2086 if( m_overlayBuffer )
2088 break;
2089 }
2090
2091 if( aTarget != TARGET_OVERLAY )
2093 else if( m_overlayBuffer )
2095
2096 // Restore the previous state
2097 m_compositor->SetBuffer( oldTarget );
2098}
2099
2100
2102{
2103 switch( aTarget )
2104 {
2105 default:
2106 case TARGET_CACHED:
2107 case TARGET_NONCACHED: return true;
2108 case TARGET_OVERLAY: return ( m_overlayBuffer != 0 );
2109 case TARGET_TEMP: return ( m_tempBuffer != 0 );
2110 }
2111}
2112
2113
2115{
2117 if( m_tempBuffer )
2118 {
2121 }
2122}
2123
2124
2126{
2127 if( m_tempBuffer )
2128 {
2129 glBlendEquation( GL_MAX );
2131 glBlendEquation( GL_FUNC_ADD );
2132
2134 }
2135 else
2136 {
2137 // Fall back to imperfect alpha blending on single buffer
2138 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
2140 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
2141 }
2142}
2143
2144
2145bool OPENGL_GAL::SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI )
2146{
2147 // Store the current cursor type and get the wxCursor for it
2148 if( !GAL::SetNativeCursorStyle( aCursor, aHiDPI ) )
2149 return false;
2150
2151 if( aHiDPI )
2153 else
2155
2156 // Update the cursor in the wx control
2157 HIDPI_GL_CANVAS::SetCursor( m_currentwxCursor );
2158
2159 return true;
2160}
2161
2162
2163void OPENGL_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
2164{
2165 aEvent.SetCursor( m_currentwxCursor );
2166}
2167
2168
2169void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
2170{
2171 // Now we should only store the position of the mouse cursor
2172 // The real drawing routines are in blitCursor()
2173 //VECTOR2D screenCursor = m_worldScreenMatrix * aCursorPosition;
2174 //m_cursorPosition = m_screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
2175 m_cursorPosition = aCursorPosition;
2176}
2177
2178
2179void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
2180 const bool aReserve )
2181{
2182 /* Helper drawing: ____--- v3 ^
2183 * ____---- ... \ \
2184 * ____---- ... \ end \
2185 * v1 ____---- ... ____---- \ width
2186 * ---- ...___---- \ \
2187 * \ ___...-- \ v
2188 * \ ____----... ____---- v2
2189 * ---- ... ____----
2190 * start \ ... ____----
2191 * \... ____----
2192 * ----
2193 * v0
2194 * dots mark triangles' hypotenuses
2195 */
2196
2198 * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
2200 * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
2201
2202 VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
2203
2204 if( aReserve )
2205 reserveLineQuads( 1 );
2206
2207 // Line width is maintained by the vertex shader
2209 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2210
2212 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2213
2215 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2216
2218 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2219
2221 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2222
2224 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2225}
2226
2227
2228void OPENGL_GAL::reserveLineQuads( const int aLineCount )
2229{
2230 m_currentManager->Reserve( 6 * aLineCount );
2231}
2232
2233
2234void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2235{
2236 if( m_isFillEnabled )
2237 {
2239 drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
2240 }
2241
2242 if( m_isStrokeEnabled )
2243 {
2245 m_strokeColor.a );
2246 drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
2247 }
2248}
2249
2250
2251void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2252{
2253 Save();
2254
2256 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2257 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2258
2259 /* Draw a triangle that contains the semicircle, then shade it to leave only
2260 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2261 * (if you want to understand more, check the vertex shader source [shader.vert]).
2262 * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
2263 * v2
2264 * /\
2265 * /__\
2266 * v0 //__\\ v1
2267 */
2269 m_currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2270
2272 m_currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2273
2275 m_currentManager->Vertex( 0.0f, aRadius * 2.0f, m_layerDepth ); // v2
2276
2277 Restore();
2278}
2279
2280
2281void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
2282 bool aReserve )
2283{
2284 double outerRadius = aRadius + ( m_lineWidth / 2 );
2285
2286 Save();
2287
2288 if( aReserve )
2290
2291 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2292 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2293
2294 /* Draw a triangle that contains the semicircle, then shade it to leave only
2295 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2296 * (if you want to understand more, check the vertex shader source [shader.vert]), the
2297 * radius and the line width. Shader uses these coordinates to determine if fragments are
2298 * inside the semicircle or not.
2299 * v2
2300 * /\
2301 * /__\
2302 * v0 //__\\ v1
2303 */
2305 m_currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2306
2308 m_currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2309
2311 m_currentManager->Vertex( 0.0f, outerRadius * 2.0f, m_layerDepth ); // v2
2312
2313 Restore();
2314}
2315
2316
2317void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
2318{
2319 if( m_isFillEnabled )
2320 {
2323
2324 // Any non convex polygon needs to be tesselated
2325 // for this purpose the GLU standard functions are used
2327 gluTessBeginPolygon( m_tesselator, &params );
2328 gluTessBeginContour( m_tesselator );
2329
2330 GLdouble* point = aPoints;
2331
2332 for( int i = 0; i < aPointCount; ++i )
2333 {
2334 gluTessVertex( m_tesselator, point, point );
2335 point += 3; // 3 coordinates
2336 }
2337
2338 gluTessEndContour( m_tesselator );
2339 gluTessEndPolygon( m_tesselator );
2340
2341 // Free allocated intersecting points
2342 m_tessIntersects.clear();
2343 }
2344
2345 if( m_isStrokeEnabled )
2346 {
2348 [&]( int idx )
2349 {
2350 return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] );
2351 },
2352 aPointCount );
2353 }
2354}
2355
2356
2357void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
2358 bool aReserve )
2359{
2360 wxCHECK( aPointCount > 0, /* return */ );
2361
2363
2364 if( aPointCount == 1 )
2365 {
2366 drawLineQuad( aPointGetter( 0 ), aPointGetter( 0 ), aReserve );
2367 return;
2368 }
2369
2370 if( aReserve )
2371 {
2372 reserveLineQuads( aPointCount - 1 );
2373 }
2374
2375 for( int i = 1; i < aPointCount; ++i )
2376 {
2377 auto start = aPointGetter( i - 1 );
2378 auto end = aPointGetter( i );
2379
2380 drawLineQuad( start, end, false );
2381 }
2382}
2383
2384
2385void OPENGL_GAL::drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter,
2386 int aPointCount, double aWidth, bool aReserve )
2387{
2388 wxCHECK( aPointCount >= 2, /* return */ );
2389
2391
2392 int vertices = 0;
2393
2394 for( int i = 1; i < aPointCount; ++i )
2395 {
2396 auto start = aPointGetter( i - 1 );
2397 auto end = aPointGetter( i );
2398
2399 VECTOR2D startEndVector = end - start;
2400 double lineLength = startEndVector.EuclideanNorm();
2401
2402 float startx = start.x;
2403 float starty = start.y;
2404 float endx = start.x + lineLength;
2405 float endy = start.y + lineLength;
2406
2407 // Be careful about floating point rounding. As we draw segments in larger and larger
2408 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
2409 // segments. In this case, we need to draw a circle for the minimal segment.
2410 if( startx == endx || starty == endy )
2411 {
2412 vertices += 3; // One circle
2413 continue;
2414 }
2415
2416 if( m_isFillEnabled || aWidth == 1.0 )
2417 {
2418 vertices += 6; // One line
2419 }
2420 else
2421 {
2422 vertices += 6 + 6 + 3 + 3; // Two lines and two half-circles
2423 }
2424 }
2425
2426 m_currentManager->Reserve( vertices );
2427
2428 for( int i = 1; i < aPointCount; ++i )
2429 {
2430 auto start = aPointGetter( i - 1 );
2431 auto end = aPointGetter( i );
2432
2433 drawSegment( start, end, aWidth, false );
2434 }
2435}
2436
2437
2438int OPENGL_GAL::drawBitmapChar( unsigned long aChar, bool aReserve )
2439{
2440 const float TEX_X = font_image.width;
2441 const float TEX_Y = font_image.height;
2442
2443 // handle space
2444 if( aChar == ' ' )
2445 {
2446 const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
2447 wxCHECK( g, 0 );
2448
2449 // Match stroke font as well as possible
2450 double spaceWidth = g->advance * 0.74;
2451
2452 Translate( VECTOR2D( spaceWidth, 0 ) );
2453 return KiROUND( spaceWidth );
2454 }
2455
2456 const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
2457
2458 // If the glyph is not found (happens for many esoteric unicode chars)
2459 // shows a '?' instead.
2460 if( !glyph )
2461 glyph = LookupGlyph( '?' );
2462
2463 if( !glyph ) // Should not happen.
2464 return 0;
2465
2466 const float X = glyph->atlas_x + font_information.smooth_pixels;
2467 const float Y = glyph->atlas_y + font_information.smooth_pixels;
2468 const float XOFF = glyph->minx;
2469
2470 // adjust for height rounding
2471 const float round_adjust = ( glyph->maxy - glyph->miny )
2472 - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
2473 const float top_adjust = font_information.max_y - glyph->maxy;
2474 const float YOFF = round_adjust + top_adjust;
2475 const float W = glyph->atlas_w - font_information.smooth_pixels * 2;
2476 const float H = glyph->atlas_h - font_information.smooth_pixels * 2;
2477 const float B = 0;
2478
2479 if( aReserve )
2481
2482 Translate( VECTOR2D( XOFF, YOFF ) );
2483
2484 /* Glyph:
2485 * v0 v1
2486 * +--+
2487 * | /|
2488 * |/ |
2489 * +--+
2490 * v2 v3
2491 */
2492 m_currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
2493 m_currentManager->Vertex( -B, -B, 0 ); // v0
2494
2495 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2496 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2497
2498 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2499 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2500
2501
2502 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2503 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2504
2505 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2506 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2507
2508 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
2509 m_currentManager->Vertex( W + B, H + B, 0 ); // v3
2510
2511 Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
2512
2513 return glyph->advance;
2514}
2515
2516
2517void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight, bool aReserve )
2518{
2519 // To draw an overbar, simply draw an overbar
2520 const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
2521 wxCHECK( glyph, /* void */ );
2522
2523 const float H = glyph->maxy - glyph->miny;
2524
2525 Save();
2526
2527 Translate( VECTOR2D( -aLength, -aHeight ) );
2528
2529 if( aReserve )
2531
2533
2535
2536 m_currentManager->Vertex( 0, 0, 0 ); // v0
2537 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2538 m_currentManager->Vertex( 0, H, 0 ); // v2
2539
2540 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2541 m_currentManager->Vertex( 0, H, 0 ); // v2
2542 m_currentManager->Vertex( aLength, H, 0 ); // v3
2543
2544 Restore();
2545}
2546
2547
2548std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
2549{
2550 static const FONT_GLYPH_TYPE* defaultGlyph = LookupGlyph( '(' ); // for strange chars
2551
2552 VECTOR2D textSize( 0, 0 );
2553 float commonOffset = std::numeric_limits<float>::max();
2554 float charHeight = font_information.max_y - defaultGlyph->miny;
2555 int overbarDepth = -1;
2556 int braceNesting = 0;
2557
2558 for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
2559 {
2560 if( *chIt == '~' && overbarDepth == -1 )
2561 {
2562 UTF8::uni_iter lookahead = chIt;
2563
2564 if( ++lookahead != end && *lookahead == '{' )
2565 {
2566 chIt = lookahead;
2567 overbarDepth = braceNesting;
2568 braceNesting++;
2569 continue;
2570 }
2571 }
2572 else if( *chIt == '{' )
2573 {
2574 braceNesting++;
2575 }
2576 else if( *chIt == '}' )
2577 {
2578 if( braceNesting > 0 )
2579 braceNesting--;
2580
2581 if( braceNesting == overbarDepth )
2582 {
2583 overbarDepth = -1;
2584 continue;
2585 }
2586 }
2587
2588 const FONT_GLYPH_TYPE* glyph = LookupGlyph( *chIt );
2589
2590 if( !glyph // Not coded in font
2591 || *chIt == '-' || *chIt == '_' ) // Strange size of these 2 chars
2592 {
2593 glyph = defaultGlyph;
2594 }
2595
2596 if( glyph )
2597 textSize.x += glyph->advance;
2598 }
2599
2600 textSize.y = std::max<float>( textSize.y, charHeight );
2601 commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
2602 textSize.y -= commonOffset;
2603
2604 return std::make_pair( textSize, commonOffset );
2605}
2606
2607
2608void OPENGL_GAL::onPaint( wxPaintEvent& aEvent )
2609{
2610 PostPaint( aEvent );
2611}
2612
2613
2614void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
2615{
2616 // Post the mouse event to the event listener registered in constructor, if any
2617 if( m_mouseListener )
2618 wxPostEvent( m_mouseListener, aEvent );
2619}
2620
2621
2622void OPENGL_GAL::skipGestureEvent( wxGestureEvent& aEvent )
2623{
2624 // Post the gesture event to the event listener registered in constructor, if any
2625 if( m_mouseListener )
2626 wxPostEvent( m_mouseListener, aEvent );
2627}
2628
2629
2631{
2632 if( !IsCursorEnabled() )
2633 return;
2634
2636
2637 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
2638
2639 VECTOR2D cursorBegin = m_cursorPosition - cursorSize / ( 2 * m_worldScale );
2640 VECTOR2D cursorEnd = m_cursorPosition + cursorSize / ( 2 * m_worldScale );
2641 VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
2642
2643 const COLOR4D color = getCursorColor();
2644
2645 glActiveTexture( GL_TEXTURE0 );
2646 glDisable( GL_TEXTURE_2D );
2647 glEnable( GL_BLEND );
2648 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
2649
2650 glLineWidth( 1.0 );
2651 glColor4d( color.r, color.g, color.b, color.a );
2652
2653 glBegin( GL_LINES );
2654 glVertex2d( cursorCenter.x, cursorBegin.y );
2655 glVertex2d( cursorCenter.x, cursorEnd.y );
2656
2657 glVertex2d( cursorBegin.x, cursorCenter.y );
2658 glVertex2d( cursorEnd.x, cursorCenter.y );
2659 glEnd();
2660}
2661
2662
2664{
2665 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
2666 wxT( "There are no free slots to store a group" ) );
2667
2668 while( m_groups.find( m_groupCounter ) != m_groups.end() )
2670
2671 return m_groupCounter++;
2672}
2673
2674
2676{
2677#ifndef KICAD_USE_EGL
2678 wxASSERT( IsShownOnScreen() );
2679#endif // KICAD_USE_EGL
2680
2681 wxASSERT_MSG( m_isContextLocked, "This should only be called from within a locked context." );
2682
2683 // Check correct initialization from the constructor
2684 if( m_tesselator == nullptr )
2685 throw std::runtime_error( "Could not create the tesselator" );
2686 GLenum err = glewInit();
2687
2688#ifdef KICAD_USE_EGL
2689 // TODO: better way to check when EGL is ready (init fails at "getString(GL_VERSION)")
2690 for( int i = 0; i < 10; i++ )
2691 {
2692 if( GLEW_OK == err )
2693 break;
2694
2695 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
2696 err = glewInit();
2697 }
2698
2699#endif // KICAD_USE_EGL
2700
2701 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
2702 (const char*) glGetString( GL_VERSION ) );
2703
2704 if( GLEW_OK != err )
2705 throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2706
2707 // Check the OpenGL version (minimum 2.1 is required)
2708 if( !GLEW_VERSION_2_1 )
2709 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2710
2711#if defined( __LINUX__ ) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2712#ifdef DEBUG
2713 if( GLEW_ARB_debug_output )
2714 enableGlDebug( true );
2715#endif
2716#endif
2717
2718 // Framebuffers have to be supported
2719 if( !GLEW_EXT_framebuffer_object )
2720 throw std::runtime_error( "Framebuffer objects are not supported!" );
2721
2722 // Vertex buffer has to be supported
2723 if( !GLEW_ARB_vertex_buffer_object )
2724 throw std::runtime_error( "Vertex buffer objects are not supported!" );
2725
2726 // Prepare shaders
2727 if( !m_shader->IsLinked()
2729 BUILTIN_SHADERS::glsl_kicad_vert ) )
2730 {
2731 throw std::runtime_error( "Cannot compile vertex shader!" );
2732 }
2733
2734 if( !m_shader->IsLinked()
2736 BUILTIN_SHADERS::glsl_kicad_frag ) )
2737 {
2738 throw std::runtime_error( "Cannot compile fragment shader!" );
2739 }
2740
2741 if( !m_shader->IsLinked() && !m_shader->Link() )
2742 throw std::runtime_error( "Cannot link the shaders!" );
2743
2744 // Check if video card supports textures big enough to fit the font atlas
2745 int maxTextureSize;
2746 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2747
2748 if( maxTextureSize < (int) font_image.width || maxTextureSize < (int) font_image.height )
2749 {
2750 // TODO implement software texture scaling
2751 // for bitmap fonts and use a higher resolution texture?
2752 throw std::runtime_error( "Requested texture size is not supported" );
2753 }
2754
2756
2757 m_cachedManager = new VERTEX_MANAGER( true );
2758 m_nonCachedManager = new VERTEX_MANAGER( false );
2759 m_overlayManager = new VERTEX_MANAGER( false );
2760 m_tempManager = new VERTEX_MANAGER( false );
2761
2762 // Make VBOs use shaders
2767
2768 m_isInitialized = true;
2769}
2770
2771
2772// Callback functions for the tesselator. Compare Redbook Chapter 11.
2773void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2774{
2775 GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2776 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2777 VERTEX_MANAGER* vboManager = param->vboManager;
2778
2779 assert( vboManager );
2780 vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2781}
2782
2783
2784void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4],
2785 GLdouble** dataOut, void* aData )
2786{
2787 GLdouble* vertex = new GLdouble[3];
2788 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2789
2790 // Save the pointer so we can delete it later
2791 // Note, we use the default_delete for an array because macOS
2792 // decides to bundle an ancient libc++ that mismatches the C++17 support of clang
2793 param->intersectPoints.emplace_back( vertex, std::default_delete<GLdouble[]>() );
2794
2795 memcpy( vertex, coords, 3 * sizeof( GLdouble ) );
2796
2797 *dataOut = vertex;
2798}
2799
2800
2801void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2802{
2803 // This callback is needed to force GLU tesselator to use triangles only
2804}
2805
2806
2807void CALLBACK ErrorCallback( GLenum aErrorCode )
2808{
2809 //throw std::runtime_error( std::string( "Tessellation error: " ) +
2810 //std::string( (const char*) gluErrorString( aErrorCode ) );
2811}
2812
2813
2814static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2815{
2816 gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, (void( CALLBACK* )()) VertexCallback );
2817 gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, (void( CALLBACK* )()) CombineCallback );
2818 gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, (void( CALLBACK* )()) EdgeCallback );
2819 gluTessCallback( aTesselator, GLU_TESS_ERROR, (void( CALLBACK* )()) ErrorCallback );
2820}
2821
2822void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2823{
2824 m_cachedManager->EnableDepthTest( aEnabled );
2826 m_overlayManager->EnableDepthTest( aEnabled );
2827}
2828
2829
2830inline double round_to_half_pixel( double f, double r )
2831{
2832 return ( ceil( f / r ) - 0.5 ) * r;
2833}
2834
2835
2837{
2839 auto pixelSize = m_worldScale;
2840
2841 // we need -m_lookAtPoint == -k * pixelSize + 0.5 * pixelSize for OpenGL
2842 // meaning m_lookAtPoint = (k-0.5)*pixelSize with integer k
2845
2847}
2848
2849
2850void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2851{
2852 if( aGlyph.IsStroke() )
2853 {
2854 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2855
2856 DrawPolylines( strokeGlyph );
2857 }
2858 else if( aGlyph.IsOutline() )
2859 {
2860 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2861
2864
2865 outlineGlyph.Triangulate(
2866 [&]( const VECTOR2D& aPt1, const VECTOR2D& aPt2, const VECTOR2D& aPt3 )
2867 {
2869
2870 m_currentManager->Vertex( aPt1.x, aPt1.y, m_layerDepth );
2871 m_currentManager->Vertex( aPt2.x, aPt2.y, m_layerDepth );
2872 m_currentManager->Vertex( aPt3.x, aPt3.y, m_layerDepth );
2873 } );
2874 }
2875}
2876
2877
2878void OPENGL_GAL::DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs )
2879{
2880 if( aGlyphs.empty() )
2881 return;
2882
2883 bool allGlyphsAreStroke = true;
2884 bool allGlyphsAreOutline = true;
2885
2886 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2887 {
2888 if( !glyph->IsStroke() )
2889 {
2890 allGlyphsAreStroke = false;
2891 break;
2892 }
2893 }
2894
2895 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2896 {
2897 if( !glyph->IsOutline() )
2898 {
2899 allGlyphsAreOutline = false;
2900 break;
2901 }
2902 }
2903
2904 if( allGlyphsAreStroke )
2905 {
2906 // Optimized path for stroke fonts that pre-reserves line quads.
2907 int lineQuadCount = 0;
2908
2909 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2910 {
2911 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2912
2913 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2914 lineQuadCount += points.size() - 1;
2915 }
2916
2917 reserveLineQuads( lineQuadCount );
2918
2919 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2920 {
2921 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2922
2923 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2924 {
2926 [&]( int idx )
2927 {
2928 return points[idx];
2929 },
2930 points.size(), false );
2931 }
2932 }
2933
2934 return;
2935 }
2936 else if( allGlyphsAreOutline )
2937 {
2938 // Optimized path for outline fonts that pre-reserves glyph triangles.
2939 int triangleCount = 0;
2940
2941 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2942 {
2943 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2944
2945 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2946 {
2948 outlineGlyph.TriangulatedPolygon( i );
2949
2950 triangleCount += polygon->GetTriangleCount();
2951 }
2952 }
2953
2956
2957 m_currentManager->Reserve( 3 * triangleCount );
2958
2959 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2960 {
2961 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2962
2963 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2964 {
2966 outlineGlyph.TriangulatedPolygon( i );
2967
2968 for( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
2969 {
2970 VECTOR2I a, b, c;
2971 polygon->GetTriangle( j, a, b, c );
2972
2976 }
2977 }
2978 }
2979 }
2980 else
2981 {
2982 // Regular path
2983 for( size_t i = 0; i < aGlyphs.size(); i++ )
2984 DrawGlyph( *aGlyphs[i], i, aGlyphs.size() );
2985 }
2986}
int color
Definition: DXF_plotter.cpp:58
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
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:49
const wxImage * GetOriginalImageData() const
Definition: bitmap_base.h:71
VECTOR2I GetSizePixels() const
Definition: bitmap_base.h:107
EDA_ANGLE Rotation() const
Definition: bitmap_base.h:218
bool IsMirroredX() const
Definition: bitmap_base.h:216
bool IsMirroredY() const
Definition: bitmap_base.h:217
KIID GetImageID() const
Definition: bitmap_base.h:76
int GetPPI() const
Definition: bitmap_base.h:118
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.
friend class GAL_SCOPED_ATTRS
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
void normalize(T &a, T &b)
Ensure that the first element is smaller than the second.
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:283
T y
Definition: vector3.h:64
T x
Definition: vector3.h:63
@ BLUE
Definition: color4d.h:56
KICURSOR
Definition: cursors.h:34
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:36
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition: definitions.h:38
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition: definitions.h:40
@ TARGET_CACHED
Main rendering target (cached)
Definition: definitions.h:37
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:39
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)
@ 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,...)
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:691
VECTOR2< double > VECTOR2D
Definition: vector2d.h:690
VECTOR2I ToVECTOR2I(const wxSize &aSize)
Definition: vector2wx.h:30