text: ﻿// Canvas widget - view over infinite virtual space.
  //
  // Canvas allows you to draw your widgets anywhere over infinite space and provide
  // view over it with support for panning and scaling.
  //
  // When you enter a canvas ImGui is moved to virtual space which mean:
  //   - ImGui::GetCursorScreenPos() return (0, 0) and which correspond to top left corner
  //     of the canvas on the screen (this can be changed usign CanvasView()).
  //   - Mouse input is brought to canvas space, so widgets works as usual.
  //   - Everything you draw with ImDrawList will be in virtual space.
  //
  // By default origin point is on top left corner of canvas widget. It can be
  // changed with call to CanvasView() where you can specify what part of space
  // should be viewed by setting viewport origin point and scale. Current state
  // can be queried with CanvasViewOrigin() and CanvasViewScale().
  //
  // Viewport size is controlled by 'size' parameter in BeginCanvas(). You can query
  // it using CanvasContentMin/Max/Size functions. They are useful if you to not specify
  // canvas size in which case all free space is used.
  //
  // Bounds of visible region of infinite space can be queried using CanvasViewMin/Max/Size
  // functions. Everything that is drawn outside of this region will be clipped
  // as usual in ImGui.
  //
  // While drawing inside canvas you can translate position from world (usual ImGui space)
  // to virtual space and back usign CanvasFromWorld()/CanvasToWorld().
  //
  // Canvas can be nested in each other (they are regular widgets after all). There
  // is a way to transform position between current and parent canvas with
  // CanvasFromParent()/CanvasToParent().
  //
  // Sometimes in more elaborate scenarios you want to move out canvas virtual space,
  // do something and came back. You can do that with SuspendCanvas() and ResumeCanvas().
  //
  // Note:
  //     It is not valid to call canvas API outside of BeginCanvas() / EndCanvas() scope.
  //
  // LICENSE
  //   This software is dual-licensed to the public domain and under the following
  //   license: you are granted a perpetual, irrevocable license to copy, modify,
  //   publish, and distribute this file as you see fit.
  //
  // CREDITS
  //   Written by Michal Cichon
  # ifndef __IMGUI_EX_CANVAS_H__
  # define __IMGUI_EX_CANVAS_H__
  # pragma once
  
  # include <imgui.h>
  # include <imgui_internal.h> // ImRect, ImFloor
  
  namespace ImGuiEx {
  
  struct CanvasView
  {
      ImVec2 Origin;
      float  Scale  = 1.0f;
      float  InvScale = 1.0f;
  
      CanvasView() = default;
      CanvasView(const ImVec2& origin, float scale)
          : Origin(origin)
          , Scale(scale)
          , InvScale(scale ? 1.0f / scale : 0.0f)
      {
      }
  
      void Set(const ImVec2& origin, float scale)
      {
          *this = CanvasView(origin, scale);
      }
  };
  
  // Canvas widget represent view over infinite plane.
  //
  // It acts like a child window without scroll bars with
  // ability to zoom to specific part of canvas plane.
  //
  // Widgets are clipped according to current view exactly
  // same way ImGui do. To avoid `missing widgets` artifacts first
  // setup visible region with SetView() then draw content.
  //
  // Everything drawn with ImDrawList betwen calls to Begin()/End()
  // will be drawn on canvas plane. This behavior can be suspended
  // by calling Suspend() and resumed by calling Resume().
  //
  // Warning:
  //     Please do not interleave canvas with use of channel splitter.
  //     Keep channel splitter contained inside canvas or always
  //     call canvas functions from same channel.
  struct Canvas
  {
      // Begins drawing content of canvas plane.
      //
      // When false is returned that mean canvas is not visible to the
      // user can drawing should be skipped and End() not called.
      // When true is returned drawing must be ended with call to End().
      //
      // If any size component is equal to zero or less canvas will
      // automatically expand to all available area on that axis.
      // So (0, 300) will take horizontal space and have height
      // of 300 points. (0, 0) will take all remaining space of
      // the window.
      //
      // You can query size of the canvas while it is being drawn
      // by calling Rect().
      bool Begin(const char* id, const ImVec2& size);
      bool Begin(ImGuiID id, const ImVec2& size);
  
      // Ends interaction with canvas plane.
      //
      // Must be called only when Begin() retuned true.
      void End();
  
      // Sets visible region of canvas plane.
      //
      // Origin is an offset of infinite plane origin from top left
      // corner of the canvas.
      //
      // Scale greater than 1 make canvas content be bigger, less than 1 smaller.
      void SetView(const ImVec2& origin, float scale);
      void SetView(const CanvasView& view);
  
      // Centers view over specific point on canvas plane.
      //
      // View will be centered on specific point by changing origin
      // but not scale.
      void CenterView(const ImVec2& canvasPoint);
  
      // Calculates view over specific point on canvas plane.
      CanvasView CalcCenterView(const ImVec2& canvasPoint) const;
  
      // Centers view over specific rectangle on canvas plane.
      //
      // Whole rectangle will fit in canvas view. This will affect both
      // origin and scale.
      void CenterView(const ImRect& canvasRect);
  
      // Calculates view over specific rectangle on canvas plane.
      CanvasView CalcCenterView(const ImRect& canvasRect) const;
  
      // Suspends canvas by returning to normal ImGui transformation space.
      // While suspended UI will not be drawn on canvas plane.
      //
      // Calls to Suspend()/Resume() are symetrical. Each call to Suspend()
      // must be matched with call to Resume().
      void Suspend();
      void Resume();
  
      // Transforms point from canvas plane to ImGui.
      ImVec2 FromLocal(const ImVec2& point) const;
      ImVec2 FromLocal(const ImVec2& point, const CanvasView& view) const;
  
      // Transforms vector from canvas plant to ImGui.
      ImVec2 FromLocalV(const ImVec2& vector) const;
      ImVec2 FromLocalV(const ImVec2& vector, const CanvasView& view) const;
  
      // Transforms point from ImGui to canvas plane.
      ImVec2 ToLocal(const ImVec2& point) const;
      ImVec2 ToLocal(const ImVec2& point, const CanvasView& view) const;
  
      // Transforms vector from ImGui to canvas plane.
      ImVec2 ToLocalV(const ImVec2& vector) const;
      ImVec2 ToLocalV(const ImVec2& vector, const CanvasView& view) const;
  
      // Returns widget bounds.
      //
      // Note:
      //     Rect is valid after call to Begin().
      const ImRect& Rect() const { return m_WidgetRect; }
  
      // Returns visible region on canvas plane (in canvas plane coordinates).
      const ImRect& ViewRect() const { return m_ViewRect; }
  
      // Calculates visible region for view.
      ImRect CalcViewRect(const CanvasView& view) const;
  
      // Returns current view.
      const CanvasView& View() const { return m_View; }
  
      // Returns origin of the view.
      //
      // Origin is an offset of infinite plane origin from top left
      // corner of the canvas.
      const ImVec2& ViewOrigin()  const { return m_View.Origin; }
  
      // Returns scale of the view.
      float ViewScale() const { return m_View.Scale; }
  
      // Returns true if canvas is suspended.
      //
      // See: Suspend()/Resume()
      bool IsSuspended() const { return m_SuspendCounter > 0; }
  
  private:
  # define IMGUI_EX_CANVAS_DEFERED() 0
  
  # if IMGUI_EX_CANVAS_DEFERED()
      struct Range
      {
          int BeginVertexIndex = 0;
          int EndVertexIndex   = 0;
          int BeginComandIndex = 0;
          int EndCommandIndex  = 0;
      };
  # endif
  
      void UpdateViewTransformPosition();
  
      void SaveInputState();
      void RestoreInputState();
  
      void SaveViewportState();
      void RestoreViewportState();
  
      void EnterLocalSpace();
      void LeaveLocalSpace();
  
      bool m_InBeginEnd = false;
  
      ImVec2 m_WidgetPosition;
      ImVec2 m_WidgetSize;
      ImRect m_WidgetRect;
  
      ImDrawList* m_DrawList = nullptr;
      int m_ExpectedChannel = 0;
  
  # if IMGUI_EX_CANVAS_DEFERED()
      ImVector<Range> m_Ranges;
      Range* m_CurrentRange = nullptr;
  # endif
  
      int m_DrawListCommadBufferSize = 0;
      int m_DrawListStartVertexIndex = 0;
  
      CanvasView  m_View;
      ImRect      m_ViewRect;
  
      ImVec2 m_ViewTransformPosition;
  
      int m_SuspendCounter = 0;
  
      float m_LastFringeScale = 1.0f;
  
      ImVec2 m_MousePosBackup;
      ImVec2 m_MousePosPrevBackup;
      ImVec2 m_MouseClickedPosBackup[IM_ARRAYSIZE(ImGuiIO::MouseClickedPos)];
      ImVec2 m_WindowCursorMaxBackup;
  
  # if defined(IMGUI_HAS_VIEWPORT)
      ImVec2 m_ViewportPosBackup;
      ImVec2 m_ViewportSizeBackup;
  # endif
  };
  
  } // namespace ImGuiEx
  
  # endif // __IMGUI_EX_CANVAS_H__