LCOV - code coverage report
Current view: top level - ezlibs/ezGL - canvas.hpp (source / functions) Coverage Total Hit
Test: Coverage (llvm-cov → lcov → genhtml) Lines: 0.0 % 174 0
Test Date: 2025-09-16 22:55:37 Functions: 0.0 % 28 0
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : #pragma once
       2                 :             : 
       3                 :             : /*
       4                 :             : MIT License
       5                 :             : 
       6                 :             : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
       7                 :             : 
       8                 :             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :             : of this software and associated documentation files (the "Software"), to deal
      10                 :             : in the Software without restriction, including without limitation the rights
      11                 :             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :             : copies of the Software, and to permit persons to whom the Software is
      13                 :             : furnished to do so, subject to the following conditions:
      14                 :             : 
      15                 :             : The above copyright notice and this permission notice shall be included in all
      16                 :             : copies or substantial portions of the Software.
      17                 :             : 
      18                 :             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24                 :             : SOFTWARE.
      25                 :             : */
      26                 :             : 
      27                 :             : // ezGL is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
      28                 :             : 
      29                 :             : #include <map>
      30                 :             : #include <string>
      31                 :             : #include <memory>
      32                 :             : #include "../ezScreen.hpp"
      33                 :             : 
      34                 :             : namespace ez {
      35                 :             : namespace gl {
      36                 :             : 
      37                 :             : class Canvas {
      38                 :             : public:
      39                 :             :     struct MouseDatas {
      40                 :             :         ez::fvec2 displayRect;
      41                 :             :         ez::fvec2 mousePos;
      42                 :             :         float mouseWheel{};
      43                 :             :         bool isPanButtonDown{};
      44                 :             :     };
      45                 :             :     struct Transform {
      46                 :             :         ez::fvec2 origin;
      47                 :             :         float scale{1.0f};  // px / unit� monde
      48                 :             :         float invScale{1.0f};
      49                 :             :     };
      50                 :             :     struct UniformTransform {
      51                 :             :         ez::fvec2 uScale{1.0f};
      52                 :             :         ez::fvec2 uOffset;
      53                 :             :     };
      54                 :             : 
      55                 :             : 
      56                 :             : private:
      57                 :             :     bool m_isRenderingActive{true};
      58                 :             :     ez::fvec4 m_clearColor;
      59                 :             : 
      60                 :             :     ez::fvec4 m_displayRect;
      61                 :             :     ez::gl::FBOPipeLinePtr mp_fboPipeline;
      62                 :             :     ez::fvec2 m_fboSize;
      63                 :             : 
      64                 :             :     MouseDatas m_mouseDatas;
      65                 :             :     Transform m_transform;
      66                 :             : 
      67                 :             :     // Uniforms NDC pour le shader (uScale/uOffset)
      68                 :             :     UniformTransform m_uniformTransform;
      69                 :             : 
      70                 :             :     // Souris (interne)
      71                 :             :     ez::fvec2 m_lastMousePos;
      72                 :             :     bool m_isPanning{false};
      73                 :             : 
      74                 :             : public:
      75                 :           0 :     bool init(const ez::ivec4& vDisplayRect) {
      76                 :           0 :         bool ret = true;
      77                 :           0 :         m_displayRect = vDisplayRect;
      78                 :           0 :         mp_fboPipeline = ez::gl::FBOPipeLine::create(vDisplayRect.z, vDisplayRect.w, 1, false, false);
      79                 :           0 :         ret &= (mp_fboPipeline != nullptr);
      80                 :           0 :         m_fboSize = m_displayRect.zw();
      81                 :           0 :         return ret;
      82                 :           0 :     }
      83                 :           0 :     void unit() {}
      84                 :           0 :     void startFrame(const MouseDatas& vMouseDatas) {
      85                 :           0 :         if (m_mouseActions(vMouseDatas)) {
      86                 :           0 :             m_computeTransform();
      87                 :           0 :         }
      88                 :           0 :     }
      89                 :             : 
      90                 :           0 :     void endFrame() {}
      91                 :             : 
      92                 :           0 :     bool resize(const ez::ivec4& vNewDisplayRect) {
      93                 :           0 :         if (mp_fboPipeline->resize(vNewDisplayRect.z, vNewDisplayRect.w)) {
      94                 :           0 :             m_displayRect = vNewDisplayRect;
      95                 :           0 :             if (!m_fboSize.emptyOR()) {
      96                 :           0 :                 ez::fvec2 rescale = m_displayRect.zw() / m_fboSize;
      97                 :           0 :                 m_transform.origin *= rescale;
      98                 :           0 :                 m_transform.scale *= ez::mini(rescale.x, rescale.y);
      99                 :           0 :             }
     100                 :           0 :             m_fboSize = vNewDisplayRect.zw();
     101                 :           0 :             m_computeTransform();
     102                 :           0 :             return true;
     103                 :           0 :         }
     104                 :           0 :         return false;
     105                 :           0 :     }
     106                 :             : 
     107                 :           0 :     ez::fvec2 worldToLocal(const ez::fvec2& vWorldPos) const { 
     108                 :           0 :         return (vWorldPos - m_transform.origin) * m_transform.invScale;
     109                 :           0 :     }
     110                 :             : 
     111                 :           0 :     ez::fvec2 localToWorld(const ez::fvec2& vCanvasPos) const { 
     112                 :           0 :        return vCanvasPos * m_transform.scale + m_transform.origin; 
     113                 :           0 :     }
     114                 :             : 
     115                 :           0 :     bool startOffscreenRender() {
     116                 :           0 :         if (m_isRenderingActive) {
     117                 :           0 :             clearBuffers(m_clearColor);
     118                 :           0 :             if (mp_fboPipeline->bind()) {
     119                 :           0 :                 mp_fboPipeline->selectBuffers();
     120                 :           0 :                 return true;
     121                 :           0 :             }
     122                 :           0 :         }
     123                 :           0 :         return false;
     124                 :           0 :     }
     125                 :           0 :     void endOffscreenRender() { mp_fboPipeline->unbind(); }
     126                 :             : 
     127                 :           0 :     void blitOnScreen() {
     128                 :           0 :         auto fbo_ptr = mp_fboPipeline->getFrontFBO().lock();
     129                 :           0 :         if (fbo_ptr != nullptr) {
     130                 :           0 :             fbo_ptr->blitOnScreen(
     131                 :           0 :                 m_displayRect.x,  //
     132                 :           0 :                 m_displayRect.y,  //
     133                 :           0 :                 m_displayRect.z,  //
     134                 :           0 :                 m_displayRect.w,  //
     135                 :           0 :                 0,
     136                 :           0 :                 GL_COLOR_BUFFER_BIT,
     137                 :           0 :                 GL_NEAREST);
     138                 :           0 :         }
     139                 :           0 :     }
     140                 :           0 :     void clearBuffers(const ez::fvec4& vColorPtr) { mp_fboPipeline->clearBuffer({vColorPtr.x, vColorPtr.y, vColorPtr.z, vColorPtr.w}); }
     141                 :           0 :     bool drawUI() {
     142                 :           0 : #ifdef IMGUI_API
     143                 :           0 : #ifdef _DEBUG
     144                 :           0 :         if (ImGui::CollapsingHeader("Debug##Renderer")) {
     145                 :           0 :             ImGui::Text("Origin Px : %f,%f", m_transform.origin.x, m_transform.origin.y);
     146                 :           0 :             ImGui::Text("FBO Size : %f,%f", m_fboSize.x, m_fboSize.y);
     147                 :           0 :             ImGui::Text("Scale : %f", m_transform.scale);
     148                 :           0 :             ImGui::Text("Sclae inv : %f", m_transform.invScale);
     149                 :           0 :             ImGui::DragFloat2("Offset", &m_uniformTransform.uOffset.x);
     150                 :           0 :             ImGui::DragFloat2("Scale", &m_uniformTransform.uScale.x);
     151                 :           0 :         }
     152                 :           0 : #endif
     153                 :           0 : #endif
     154                 :           0 :         return false;
     155                 :           0 :     }
     156                 :             : 
     157                 :           0 :     void updateTransform() { m_computeTransform(); }
     158                 :             : 
     159                 :           0 :     void fitToContent(const ez::fAABB& vBoundingBox) {
     160                 :           0 :         m_computeFitToContent(  //
     161                 :           0 :             vBoundingBox.lowerBound,
     162                 :           0 :             vBoundingBox.upperBound,
     163                 :           0 :             m_fboSize,
     164                 :           0 :             m_transform.origin,
     165                 :           0 :             m_transform.scale,
     166                 :           0 :             m_transform.invScale);
     167                 :           0 :         m_computeTransform();
     168                 :           0 :     }
     169                 :             : 
     170                 :           0 :     ez::fvec4& getBackgroundColorRef() { return m_clearColor; }
     171                 :           0 :     const ez::fvec4& getBackgroundColor() const { return m_clearColor; }
     172                 :             :     
     173                 :           0 :     MouseDatas& getMouseDatasRef() { return m_mouseDatas; }
     174                 :           0 :     const MouseDatas& getMouseDatas() const { return m_mouseDatas; }
     175                 :             :     
     176                 :           0 :     Transform& getTransfomRef() { return m_transform; }
     177                 :           0 :     const Transform& getTransfom() const { return m_transform; }
     178                 :             : 
     179                 :           0 :     UniformTransform& getUniformTransfomRef() { return m_uniformTransform; }
     180                 :           0 :     const UniformTransform& getUniformTransfom() const { return m_uniformTransform; }
     181                 :             : 
     182                 :             : private:
     183                 :           0 :     bool m_mouseActions(const MouseDatas& vMouseDatas) {
     184                 :           0 :         bool ret = false;
     185                 :           0 :         const float steps = vMouseDatas.mouseWheel;
     186                 :           0 :         if (fabsf(steps) > 0.0001f) {
     187                 :           0 :             const float factor = powf(1.1f, steps);
     188                 :           0 :             m_applyZoomAtMouse(factor, vMouseDatas.mousePos);
     189                 :           0 :             ret = true;
     190                 :           0 :         }
     191                 :           0 :         if (vMouseDatas.isPanButtonDown) {
     192                 :           0 :             const ez::fvec2 delta = vMouseDatas.mousePos - m_lastMousePos;
     193                 :           0 :             m_isPanning = true;
     194                 :           0 :             m_applyPanDrag(delta);
     195                 :           0 :             ret = true;
     196                 :           0 :         } else {
     197                 :           0 :             m_isPanning = false;
     198                 :           0 :         }
     199                 :           0 :         m_lastMousePos = vMouseDatas.mousePos;
     200                 :           0 :         return ret;
     201                 :           0 :     }
     202                 :             : 
     203                 :           0 :     void m_setScale(float v) {
     204                 :           0 :         m_transform.scale = v;
     205                 :           0 :         m_transform.invScale = (v != 0.0f) ? (1.0f / v) : 0.0f;
     206                 :           0 :     }
     207                 :             : 
     208                 :           0 :     void m_applyPanDrag(const ez::fvec2& dragPx) {
     209                 :           0 :         m_transform.origin.x += dragPx.x;
     210                 :           0 :         m_transform.origin.y += dragPx.y;
     211                 :           0 :     }
     212                 :             : 
     213                 :           0 :     void m_applyZoomAtMouse(float factor, const ez::fvec2& mousePx) {
     214                 :           0 :         if (factor <= 0.0f) {
     215                 :           0 :             return;
     216                 :           0 :         }
     217                 :           0 :         float newScale = m_transform.scale * factor;
     218                 :           0 :         newScale = std::max(1e-6f, std::min(newScale, 1e6f));
     219                 :           0 :         const float applied = (m_transform.scale > 0.0f) ? (newScale / m_transform.scale) : 1.0f;
     220                 :           0 :         if (applied == 1.0f) {
     221                 :           0 :             return;
     222                 :           0 :         }
     223                 :           0 :         m_transform.origin.x = m_transform.origin.x + (1.0f - applied) * (mousePx.x - m_transform.origin.x);
     224                 :           0 :         m_transform.origin.y = m_transform.origin.y + (1.0f - applied) * (mousePx.y - m_transform.origin.y);
     225                 :           0 :         m_setScale(newScale);
     226                 :           0 :     }
     227                 :             : 
     228                 :             :     // computation of uScale/uOffset (NDC) for
     229                 :             :     // gl_Position = vec4(aPos * uScale + uOffset, 0, 1)
     230                 :           0 :     void m_computeTransform() {
     231                 :           0 :         const float VW = m_fboSize.x;
     232                 :           0 :         const float VH = m_fboSize.y;
     233                 :           0 :         if (VW <= 0.0f || VH <= 0.0f) {
     234                 :           0 :             m_transform = {};
     235                 :           0 :             return;
     236                 :           0 :         }
     237                 :           0 :         m_setScale(m_transform.scale);
     238                 :           0 :         m_uniformTransform.uScale.x = (2.0f * m_transform.scale) / VW;
     239                 :           0 :         m_uniformTransform.uOffset.x = (2.0f * m_transform.origin.x) / VW - 1.0f;
     240                 :           0 :         // Y-down (framebuffer in top-left, classic UI)
     241                 :           0 :         m_uniformTransform.uScale.y = -(2.0f * m_transform.scale) / VH;
     242                 :           0 :         m_uniformTransform.uOffset.y = 1.0f - (2.0f * m_transform.origin.y) / VH;
     243                 :           0 :     }
     244                 :             : 
     245                 :             :     // Fit "contain" d'un AABB monde dans un framebuffer en pixels.
     246                 :             :     // Mapping utilis� : screen = world * scale + originPx
     247                 :             :     void m_computeFitToContent(
     248                 :             :         const ez::fvec2& vWorldMin,
     249                 :             :         const ez::fvec2& vWorldMax,
     250                 :             :         const ez::fvec2& vFramebufferSizePx,
     251                 :             :         ez::fvec2& voOriginPx,
     252                 :             :         float& voScale,
     253                 :           0 :         float& voInvScale) {
     254                 :           0 :         const float W = vWorldMax.x - vWorldMin.x;
     255                 :           0 :         const float H = vWorldMax.y - vWorldMin.y;
     256                 :           0 :         const float VW = vFramebufferSizePx.x;
     257                 :           0 :         const float VH = vFramebufferSizePx.y;
     258                 :           0 : 
     259                 :           0 :         if (W <= 0.0f || H <= 0.0f || VW <= 0.0f || VH <= 0.0f) {
     260                 :           0 :             voOriginPx = {0.0f, 0.0f};
     261                 :           0 :             voScale = 1.0f;
     262                 :           0 :             voInvScale = 1.0f;
     263                 :           0 :             return;
     264                 :           0 :         }
     265                 :           0 : 
     266                 :           0 :         const float sx = VW / W;
     267                 :           0 :         const float sy = VH / H;
     268                 :           0 :         const float s = (sx < sy) ? sx : sy;
     269                 :           0 : 
     270                 :           0 :         const float padX = 0.5f * (VW - W * s);
     271                 :           0 :         const float padY = 0.5f * (VH - H * s);
     272                 :           0 : 
     273                 :           0 :         voOriginPx.x = padX - vWorldMin.x * s;
     274                 :           0 :         voOriginPx.y = padY - vWorldMin.y * s;
     275                 :           0 :         voScale = s;
     276                 :           0 :         voInvScale = (s != 0.0f) ? (1.0f / s) : 0.0f;
     277                 :           0 :     }
     278                 :             : };
     279                 :             : 
     280                 :             : }  // namespace gl
     281                 :             : }  // namespace ez
        

Generated by: LCOV version 2.0-1