<commit_before>#pragma once

#include <taichi/math/math.h>
#include <taichi/system/timer.h>
#include <ctime>
#include <numeric>

#if defined(TC_PLATFORM_LINUX)
#define TC_GUI_X11
#endif

#if defined(TC_PLATFORM_WINDOWS)
#define TC_GUI_WIN32
#endif

#if defined(TC_PLATFORM_OSX)
#define TC_GUI_COCOA
#include <objc/objc.h>
#endif

TC_NAMESPACE_BEGIN

// https://color.adobe.com/sea-waves-color-theme-11521801/edit/?copy=true&base=2&rule=Custom&selected=1&name=Copy%20of%20sea%20waves&mode=hsv&rgbvalues=0.009800000000000008,0.32993205279993043,0.49,0.006600000000000006,0.5184304355999791,0.66,0.007700000000000007,0.7445879672002016,0.77,0.6643,0.91,0.8608604914000435,0.8827,0.97,0.9059801164000182&swatchOrder=0,1,2,3,4
TC_FORCE_INLINE Vector4 color_from_hex(uint32 c) {
  return Vector4(c / 65536, c / 256 % 256, c % 256, 255) * (1 / 255.0_f);
}

#if (false)
constexpr uint32 text_color = 0x02547D;
constexpr uint32 widget_bg = 0x02BEC4;
constexpr uint32 widget_hover = 0xA9E8DC;
constexpr uint32 slider_bar_color = text_color;
constexpr uint32 slider_circle_color = 0x0284A8;
#else
constexpr uint32 text_color = 0x111111;
constexpr uint32 widget_bg = 0xAAAAAA;
constexpr uint32 widget_hover = 0xCCCCCC;
constexpr uint32 slider_bar_color = 0x333333;
constexpr uint32 slider_circle_color = 0x555555;
#endif

class Canvas {
  struct Context {
    Vector4 _color;
    real _radius;
  };

 public:
  Context context;

  Canvas &color(Vector4 val) {
    context._color = val;
    return *this;
  }

  TC_FORCE_INLINE Canvas &color(real r, real g, real b, real a = 1) {
    context._color = Vector4(r, g, b, a);
    return *this;
  }

  TC_FORCE_INLINE Canvas &color(int r, int g, int b, int a = 255) {
    context._color = (1.0_f / 255) * Vector4(r, g, b, a);
    return *this;
  }

  TC_FORCE_INLINE Canvas &radius(real radius) {
    context._radius = radius;
    return *this;
  }

  struct Line {
    Canvas &canvas;
    Vector4 _color;
    real _radius;
    int n_vertices;
    bool finished;
    static Vector2 vertices[128];  // TODO: ...

    TC_FORCE_INLINE Line(Canvas &canvas)
        : canvas(canvas),
          _color(canvas.context._color),
          _radius(canvas.context._radius) {
      n_vertices = 0;
      finished = false;
    }

    TC_FORCE_INLINE Line(Canvas &canvas, Vector2 a, Vector2 b) : Line(canvas) {
      push(a);
      push(b);
    }

    TC_FORCE_INLINE Line(Canvas &canvas, Vector2 a, Vector2 b, Vector2 c)
        : Line(canvas) {
      push(a);
      push(b);
      push(c);
    }

    TC_FORCE_INLINE Line(Canvas &canvas,
                         Vector2 a,
                         Vector2 b,
                         Vector2 c,
                         Vector2 d)
        : Line(canvas) {
      push(a);
      push(b);
      push(c);
      push(d);
    }

    TC_FORCE_INLINE void push(Vector2 vec) {
      vertices[n_vertices++] = vec;
    }

    TC_FORCE_INLINE Line &path(Vector2 a) {
      push(a);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b) {
      push(a);
      push(b);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b, Vector2 c) {
      push(a);
      push(b);
      push(c);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
      push(a);
      push(b);
      push(c);
      push(d);
      return *this;
    }

    TC_FORCE_INLINE Line &close() {
      TC_ASSERT(n_vertices > 0);
      push(vertices[0]);
      return *this;
    }

    TC_FORCE_INLINE Line &color(Vector4 color) {
      _color = color;
      return *this;
    }

    TC_FORCE_INLINE Line &color(int c) {
      return color(c / 65536, c / 256 % 256, c % 256, 255);
    }

    TC_FORCE_INLINE Line &color(real r, real g, real b, real a = 1) {
      _color = Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Line &color(int r, int g, int b, int a = 255) {
      _color = (1.0_f / 255) * Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Line &width(real width) {
      _radius = width * 0.5;
      return *this;
    }

    TC_FORCE_INLINE Line &radius(real radius) {
      _radius = radius;
      return *this;
    }

    // TODO: end style e.g. arrow

    void stroke(Vector2 a, Vector2 b) {
      // TODO: accelerate
      auto a_i = (a + Vector2(0.5_f)).template cast<int>();
      auto b_i = (b + Vector2(0.5_f)).template cast<int>();
      auto radius_i = (int)std::ceil(_radius + 0.5_f);
      auto range_lower = Vector2i(std::min(a_i.x, b_i.x) - radius_i,
                                  std::min(a_i.y, b_i.y) - radius_i);
      range_lower(0) = std::max(0, range_lower(0));
      range_lower(1) = std::max(0, range_lower(1));
      auto range_higher = Vector2i(std::max(a_i.x, b_i.x) + radius_i,
                                   std::max(a_i.y, b_i.y) + radius_i);
      range_higher(0) = std::min(canvas.img.get_width(), range_higher(0));
      range_higher(1) = std::min(canvas.img.get_height(), range_higher(1));
      auto direction = normalized(b - a);
      auto l = length(b - a);
      auto tangent = Vector2(-direction.y, direction.x);
      for (int i = range_lower.x; i <= range_higher.x; i++) {
        for (int j = range_lower.y; j <= range_higher.y; j++) {
          auto pixel_coord = Vector2(i + 0.5_f, j + 0.5_f) - a;
          auto u = dot(tangent, pixel_coord);
          auto v = dot(direction, pixel_coord);
          if (v > 0) {
            v = std::max(0.0_f, v - l);
          }
          real dist = length(Vector2(u, v));
          auto alpha = _color.w * clamp(_radius - dist);
          auto &dest = canvas.img[Vector2i(i, j)];
          dest = lerp(alpha, dest, _color);
        }
      }
    }

    void finish() {
      TC_ASSERT(!finished);
      finished = true;
      for (int i = 0; i + 1 < n_vertices; i++) {
        stroke(canvas.transform(vertices[i]),
               canvas.transform(vertices[i + 1]));
      }
    }
  };

  struct Circle {
    Canvas &canvas;
    Vector2 _center;
    Vector4 _color;
    real _radius;
    bool finished;

    TC_FORCE_INLINE Circle(Canvas &canvas, Vector2 center)
        : canvas(canvas),
          _center(center),
          _color(canvas.context._color),
          _radius(canvas.context._radius) {
      finished = false;
    }

    TC_FORCE_INLINE Circle &color(Vector4 color) {
      _color = color;
      return *this;
    }

    TC_FORCE_INLINE Circle &color(real r, real g, real b, real a = 1) {
      _color = Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Circle &color(int r, int g, int b, int a = 255) {
      _color = (1.0_f / 255) * Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Circle &color(int c) {
      return color(c / 65536, c / 256 % 256, c % 256, 255);
    }

    TC_FORCE_INLINE Circle &radius(real radius) {
      _radius = radius;
      return *this;
    }

    void finish() {
      TC_ASSERT(finished == false);
      finished = true;
      auto center = canvas.transform(_center);
      auto const canvas_width = canvas.img.get_width();
      auto const canvas_height = canvas.img.get_height();
      const auto r = _radius;
      int i_lower = std::max(0, (int)std::ceil(center(0) - r));
      int j_lower = std::max(0, (int)std::ceil(center(1) - r));
      int i_higher = std::min((int)std::floor(center(0) + r), canvas_width - 1);
      int j_higher =
          std::min((int)std::floor(center(1) + r), canvas_height - 1);
      const auto w = _color.w;
      for (int i = i_lower; i <= i_higher; i++) {
        for (int j = j_lower; j <= j_higher; j++) {
          real dist = length(center - Vector2(i, j));
          auto alpha = w * clamp(r - dist);
          auto &dest = canvas.img[Vector2i(i, j)];
          dest = lerp(alpha, dest, _color);
        }
      }
    }

    TC_FORCE_INLINE ~Circle() {
      if (!finished)
        finish();
    }
  };

 public:
  Array2D<Vector4> &img;
  Matrix3 transform_matrix;

  Canvas(Array2D<Vector4> &img) : img(img) {
    transform_matrix = Matrix3(Vector3(img.get_res().cast<real>(), 1.0_f));
  }

  TC_FORCE_INLINE Vector2 transform(Vector2 x) const {
    return Vector2(transform_matrix * Vector3(x, 1.0_f));
  }

  std::vector<Circle> circles;
  std::vector<Line> lines;

  Circle &circle(Vector2 center) {
    circles.emplace_back(*this, center);
    return circles.back();
  }

  Circle &circle(real x, real y) {
    circles.emplace_back(*this, Vector2(x, y));
    return circles.back();
  }

  void circles_batched(int n,
                       std::size_t x,
                       uint32 color_single,
                       std::size_t color_array,
                       real radius_single,
                       std::size_t radius_array);

  Line &path(real xa, real ya, real xb, real yb) {
    return path(Vector2(xa, ya), Vector2(xb, yb));
  }

  Line &path() {
    lines.emplace_back(*this);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b) {
    lines.emplace_back(*this);
    lines.back().path(a, b);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b, Vector2 c) {
    lines.emplace_back(*this, a, b, c);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
    lines.emplace_back(*this, a, b, c, d);
    return lines.back();
  }

  Line &rect(Vector2 a, Vector2 b) {
    lines.emplace_back(*this, a, Vector2(a.x, b.y), b, Vector2(b.x, a.y));
    return lines.back();
  }

  void line(Vector2 start, Vector2 end, Vector4 color) {
    // convert to screen space
    start = transform(start);
    end = transform(end);
    real len = length(end - start);
    int samples = (int)len * 2 + 4;
    for (int i = 0; i < samples; i++) {
      real alpha = (1.0_f / (samples - 1)) * i;
      Vector2i coord = (lerp(alpha, start, end)).floor().cast<int>();
      if (img.inside(coord)) {
        img[coord] = color;
      }
    }
  }

  void triangle(Vector2 a, Vector2 b, Vector2 c, Vector4 color) {
    a = transform(a);
    b = transform(b);
    c = transform(c);

    // real points[3] = {a, b, c};
    // std::sort(points, points + 3, [](const Vector2 &a, const Vector2 &b) {
    //  return a.y < b.y;
    //});
    Vector2 limits[2];
    limits[0].x = min(a.x, min(b.x, c.x));
    limits[0].y = min(a.y, min(b.y, c.y));
    limits[1].x = max(a.x, max(b.x, c.x));
    limits[1].y = max(a.y, max(b.y, c.y));
    for (int i = (int)std::floor(limits[0].x); i < (int)std::ceil(limits[1].x);
         i++) {
      for (int j = (int)std::floor(limits[0].y);
           j < (int)std::ceil(limits[1].y); j++) {
        Vector2 pixel(i + 0.5_f, j + 0.5_f);
        bool inside_a = cross(pixel - a, b - a) <= 0;
        bool inside_b = cross(pixel - b, c - b) <= 0;
        bool inside_c = cross(pixel - c, a - c) <= 0;
        if (inside_a && inside_b && inside_c && img.inside(i, j)) {
          img[i][j] = color;
        }
      }
    }
  }

  void text(const std::string &str,
            Vector2 position,
            real size,
            Vector4 color) {
    position = transform(position);
#if defined(TC_AMALGAMATED)
    auto ttf_path = std::string("");  // use amalgamated font
#else
    std::string root_dir = get_repo_dir();
    auto ttf_path = root_dir + std::string("/assets/fonts/go/Go-Regular.ttf");
#endif
    img.write_text(ttf_path, str, size, position.x, position.y, color);
  }

  void clear(Vector4 color) {
    circles.clear();
    lines.clear();
    img.reset(color);
  }

  void clear(int c) {
    img.reset((1.0_f / 255) * Vector4(c / 65536, c / 256 % 256, c % 256, 255));
  }

  ~Canvas() {
  }

  void set_idendity_transform_matrix() {
    transform_matrix = Matrix3(1);
  }
};

#if defined(TC_GUI_X11)

class CXImage;

class GUIBaseX11 {
 public:
  void *display;
  void *visual;
  unsigned long window;
  CXImage *img;
};

using GUIBase = GUIBaseX11;

#endif

#if defined(TC_GUI_WIN32)
class GUIBaseWin32 {
 public:
  HWND hwnd;
  HDC hdc;
  COLORREF *data;
  HDC src;
  HBITMAP bitmap;
};

using GUIBase = GUIBaseWin32;
#endif

#if defined(TC_GUI_COCOA)
class GUIBaseCocoa {
 public:
  id window, view;
  std::size_t img_data_length;
  std::vector<uint8_t> img_data;
};

using GUIBase = GUIBaseCocoa;
#endif

class GUI : public GUIBase {
 public:
  std::string window_name;
  int width, height;
  int frame_id = 0;
  const int fps = 60;
  float64 start_time;
  Array2D<Vector4> buffer;
  std::vector<real> last_frame_interval;
  std::unique_ptr<Canvas> canvas;
  float64 last_frame_time;
  bool key_pressed;
  std::vector<std::string> log_entries;
  Vector2i cursor_pos;
  bool button_status[3];
  int widget_height;

  void set_mouse_pos(int x, int y) {
    cursor_pos = Vector2i(x, y);
  }<commit_msg>gui/win32.cpp fix 4<commit_after>#pragma once

#include <taichi/math/math.h>
#include <taichi/system/timer.h>
#include <ctime>
#include <numeric>

#if defined(TC_PLATFORM_LINUX)
#define TC_GUI_X11
#endif

#if defined(TC_PLATFORM_WINDOWS)
#define TC_GUI_WIN32
#endif

#if defined(TC_PLATFORM_OSX)
#define TC_GUI_COCOA
#include <objc/objc.h>
#endif

TC_NAMESPACE_BEGIN

// https://color.adobe.com/sea-waves-color-theme-11521801/edit/?copy=true&base=2&rule=Custom&selected=1&name=Copy%20of%20sea%20waves&mode=hsv&rgbvalues=0.009800000000000008,0.32993205279993043,0.49,0.006600000000000006,0.5184304355999791,0.66,0.007700000000000007,0.7445879672002016,0.77,0.6643,0.91,0.8608604914000435,0.8827,0.97,0.9059801164000182&swatchOrder=0,1,2,3,4
TC_FORCE_INLINE Vector4 color_from_hex(uint32 c) {
  return Vector4(c / 65536, c / 256 % 256, c % 256, 255) * (1 / 255.0_f);
}

#if (false)
constexpr uint32 text_color = 0x02547D;
constexpr uint32 widget_bg = 0x02BEC4;
constexpr uint32 widget_hover = 0xA9E8DC;
constexpr uint32 slider_bar_color = text_color;
constexpr uint32 slider_circle_color = 0x0284A8;
#else
constexpr uint32 text_color = 0x111111;
constexpr uint32 widget_bg = 0xAAAAAA;
constexpr uint32 widget_hover = 0xCCCCCC;
constexpr uint32 slider_bar_color = 0x333333;
constexpr uint32 slider_circle_color = 0x555555;
#endif

class Canvas {
  struct Context {
    Vector4 _color;
    real _radius;
  };

 public:
  Context context;

  Canvas &color(Vector4 val) {
    context._color = val;
    return *this;
  }

  TC_FORCE_INLINE Canvas &color(real r, real g, real b, real a = 1) {
    context._color = Vector4(r, g, b, a);
    return *this;
  }

  TC_FORCE_INLINE Canvas &color(int r, int g, int b, int a = 255) {
    context._color = (1.0_f / 255) * Vector4(r, g, b, a);
    return *this;
  }

  TC_FORCE_INLINE Canvas &radius(real radius) {
    context._radius = radius;
    return *this;
  }

  struct Line {
    Canvas &canvas;
    Vector4 _color;
    real _radius;
    int n_vertices;
    bool finished;
    static Vector2 vertices[128];  // TODO: ...

    TC_FORCE_INLINE Line(Canvas &canvas)
        : canvas(canvas),
          _color(canvas.context._color),
          _radius(canvas.context._radius) {
      n_vertices = 0;
      finished = false;
    }

    TC_FORCE_INLINE Line(Canvas &canvas, Vector2 a, Vector2 b) : Line(canvas) {
      push(a);
      push(b);
    }

    TC_FORCE_INLINE Line(Canvas &canvas, Vector2 a, Vector2 b, Vector2 c)
        : Line(canvas) {
      push(a);
      push(b);
      push(c);
    }

    TC_FORCE_INLINE Line(Canvas &canvas,
                         Vector2 a,
                         Vector2 b,
                         Vector2 c,
                         Vector2 d)
        : Line(canvas) {
      push(a);
      push(b);
      push(c);
      push(d);
    }

    TC_FORCE_INLINE void push(Vector2 vec) {
      vertices[n_vertices++] = vec;
    }

    TC_FORCE_INLINE Line &path(Vector2 a) {
      push(a);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b) {
      push(a);
      push(b);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b, Vector2 c) {
      push(a);
      push(b);
      push(c);
      return *this;
    }

    TC_FORCE_INLINE Line &path(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
      push(a);
      push(b);
      push(c);
      push(d);
      return *this;
    }

    TC_FORCE_INLINE Line &close() {
      TC_ASSERT(n_vertices > 0);
      push(vertices[0]);
      return *this;
    }

    TC_FORCE_INLINE Line &color(Vector4 color) {
      _color = color;
      return *this;
    }

    TC_FORCE_INLINE Line &color(int c) {
      return color(c / 65536, c / 256 % 256, c % 256, 255);
    }

    TC_FORCE_INLINE Line &color(real r, real g, real b, real a = 1) {
      _color = Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Line &color(int r, int g, int b, int a = 255) {
      _color = (1.0_f / 255) * Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Line &width(real width) {
      _radius = width * 0.5;
      return *this;
    }

    TC_FORCE_INLINE Line &radius(real radius) {
      _radius = radius;
      return *this;
    }

    // TODO: end style e.g. arrow

    void stroke(Vector2 a, Vector2 b) {
      // TODO: accelerate
      auto a_i = (a + Vector2(0.5_f)).template cast<int>();
      auto b_i = (b + Vector2(0.5_f)).template cast<int>();
      auto radius_i = (int)std::ceil(_radius + 0.5_f);
      auto range_lower = Vector2i(std::min(a_i.x, b_i.x) - radius_i,
                                  std::min(a_i.y, b_i.y) - radius_i);
      range_lower(0) = std::max(0, range_lower(0));
      range_lower(1) = std::max(0, range_lower(1));
      auto range_higher = Vector2i(std::max(a_i.x, b_i.x) + radius_i,
                                   std::max(a_i.y, b_i.y) + radius_i);
      range_higher(0) = std::min(canvas.img.get_width(), range_higher(0));
      range_higher(1) = std::min(canvas.img.get_height(), range_higher(1));
      auto direction = normalized(b - a);
      auto l = length(b - a);
      auto tangent = Vector2(-direction.y, direction.x);
      for (int i = range_lower.x; i <= range_higher.x; i++) {
        for (int j = range_lower.y; j <= range_higher.y; j++) {
          auto pixel_coord = Vector2(i + 0.5_f, j + 0.5_f) - a;
          auto u = dot(tangent, pixel_coord);
          auto v = dot(direction, pixel_coord);
          if (v > 0) {
            v = std::max(0.0_f, v - l);
          }
          real dist = length(Vector2(u, v));
          auto alpha = _color.w * clamp(_radius - dist);
          auto &dest = canvas.img[Vector2i(i, j)];
          dest = lerp(alpha, dest, _color);
        }
      }
    }

    void finish() {
      TC_ASSERT(!finished);
      finished = true;
      for (int i = 0; i + 1 < n_vertices; i++) {
        stroke(canvas.transform(vertices[i]),
               canvas.transform(vertices[i + 1]));
      }
    }
  };

  struct Circle {
    Canvas &canvas;
    Vector2 _center;
    Vector4 _color;
    real _radius;
    bool finished;

    TC_FORCE_INLINE Circle(Canvas &canvas, Vector2 center)
        : canvas(canvas),
          _center(center),
          _color(canvas.context._color),
          _radius(canvas.context._radius) {
      finished = false;
    }

    TC_FORCE_INLINE Circle &color(Vector4 color) {
      _color = color;
      return *this;
    }

    TC_FORCE_INLINE Circle &color(real r, real g, real b, real a = 1) {
      _color = Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Circle &color(int r, int g, int b, int a = 255) {
      _color = (1.0_f / 255) * Vector4(r, g, b, a);
      return *this;
    }

    TC_FORCE_INLINE Circle &color(int c) {
      return color(c / 65536, c / 256 % 256, c % 256, 255);
    }

    TC_FORCE_INLINE Circle &radius(real radius) {
      _radius = radius;
      return *this;
    }

    void finish() {
      TC_ASSERT(finished == false);
      finished = true;
      auto center = canvas.transform(_center);
      auto const canvas_width = canvas.img.get_width();
      auto const canvas_height = canvas.img.get_height();
      const auto r = _radius;
      int i_lower = std::max(0, (int)std::ceil(center(0) - r));
      int j_lower = std::max(0, (int)std::ceil(center(1) - r));
      int i_higher = std::min((int)std::floor(center(0) + r), canvas_width - 1);
      int j_higher =
          std::min((int)std::floor(center(1) + r), canvas_height - 1);
      const auto w = _color.w;
      for (int i = i_lower; i <= i_higher; i++) {
        for (int j = j_lower; j <= j_higher; j++) {
          real dist = length(center - Vector2(i, j));
          auto alpha = w * clamp(r - dist);
          auto &dest = canvas.img[Vector2i(i, j)];
          dest = lerp(alpha, dest, _color);
        }
      }
    }

    TC_FORCE_INLINE ~Circle() {
      if (!finished)
        finish();
    }
  };

 public:
  Array2D<Vector4> &img;
  Matrix3 transform_matrix;

  Canvas(Array2D<Vector4> &img) : img(img) {
    transform_matrix = Matrix3(Vector3(img.get_res().cast<real>(), 1.0_f));
  }

  TC_FORCE_INLINE Vector2 transform(Vector2 x) const {
    return Vector2(transform_matrix * Vector3(x, 1.0_f));
  }

  std::vector<Circle> circles;
  std::vector<Line> lines;

  Circle &circle(Vector2 center) {
    circles.emplace_back(*this, center);
    return circles.back();
  }

  Circle &circle(real x, real y) {
    circles.emplace_back(*this, Vector2(x, y));
    return circles.back();
  }

  void circles_batched(int n,
                       std::size_t x,
                       uint32 color_single,
                       std::size_t color_array,
                       real radius_single,
                       std::size_t radius_array);

  Line &path(real xa, real ya, real xb, real yb) {
    return path(Vector2(xa, ya), Vector2(xb, yb));
  }

  Line &path() {
    lines.emplace_back(*this);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b) {
    lines.emplace_back(*this);
    lines.back().path(a, b);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b, Vector2 c) {
    lines.emplace_back(*this, a, b, c);
    return lines.back();
  }

  Line &path(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
    lines.emplace_back(*this, a, b, c, d);
    return lines.back();
  }

  Line &rect(Vector2 a, Vector2 b) {
    lines.emplace_back(*this, a, Vector2(a.x, b.y), b, Vector2(b.x, a.y));
    return lines.back();
  }

  void line(Vector2 start, Vector2 end, Vector4 color) {
    // convert to screen space
    start = transform(start);
    end = transform(end);
    real len = length(end - start);
    int samples = (int)len * 2 + 4;
    for (int i = 0; i < samples; i++) {
      real alpha = (1.0_f / (samples - 1)) * i;
      Vector2i coord = (lerp(alpha, start, end)).floor().cast<int>();
      if (img.inside(coord)) {
        img[coord] = color;
      }
    }
  }

  void triangle(Vector2 a, Vector2 b, Vector2 c, Vector4 color) {
    a = transform(a);
    b = transform(b);
    c = transform(c);

    // real points[3] = {a, b, c};
    // std::sort(points, points + 3, [](const Vector2 &a, const Vector2 &b) {
    //  return a.y < b.y;
    //});
    Vector2 limits[2];
    limits[0].x = min(a.x, min(b.x, c.x));
    limits[0].y = min(a.y, min(b.y, c.y));
    limits[1].x = max(a.x, max(b.x, c.x));
    limits[1].y = max(a.y, max(b.y, c.y));
    for (int i = (int)std::floor(limits[0].x); i < (int)std::ceil(limits[1].x);
         i++) {
      for (int j = (int)std::floor(limits[0].y);
           j < (int)std::ceil(limits[1].y); j++) {
        Vector2 pixel(i + 0.5_f, j + 0.5_f);
        bool inside_a = cross(pixel - a, b - a) <= 0;
        bool inside_b = cross(pixel - b, c - b) <= 0;
        bool inside_c = cross(pixel - c, a - c) <= 0;
        if (inside_a && inside_b && inside_c && img.inside(i, j)) {
          img[i][j] = color;
        }
      }
    }
  }

  void text(const std::string &str,
            Vector2 position,
            real size,
            Vector4 color) {
    position = transform(position);
#if defined(TC_AMALGAMATED)
    auto ttf_path = std::string("");  // use amalgamated font
#else
    std::string root_dir = get_repo_dir();
    auto ttf_path = root_dir + std::string("/assets/fonts/go/Go-Regular.ttf");
#endif
    img.write_text(ttf_path, str, size, position.x, position.y, color);
  }

  void clear(Vector4 color) {
    circles.clear();
    lines.clear();
    img.reset(color);
  }

  void clear(int c) {
    img.reset((1.0_f / 255) * Vector4(c / 65536, c / 256 % 256, c % 256, 255));
  }

  ~Canvas() {
  }

  void set_idendity_transform_matrix() {
    transform_matrix = Matrix3(1);
  }
};

#if defined(TC_GUI_X11)

class CXImage;

class GUIBaseX11 {
 public:
  void *display;
  void *visual;
  unsigned long window;
  CXImage *img;
};

using GUIBase = GUIBaseX11;

#endif

#if defined(TC_GUI_WIN32)
class GUIBaseWin32 {
 public:
  HWND hwnd;
  HDC hdc;
  COLORREF *data;
  HDC src;
  HBITMAP bitmap;
};

using GUIBase = GUIBaseWin32;
#endif

#if defined(TC_GUI_COCOA)
class GUIBaseCocoa {
 public:
  id window, view;
  std::size_t img_data_length;
  std::vector<uint8_t> img_data;
};

using GUIBase = GUIBaseCocoa;
#endif

class GUI : public GUIBase {
 public:
  std::string window_name;
  int width, height;
  int frame_id = 0;
  const int fps = 60;
  float64 start_time;
  Array2D<Vector4> buffer;
  std::vector<real> last_frame_interval;
  std::unique_ptr<Canvas> canvas;
  float64 last_frame_time;
  bool key_pressed;
  td::vector<std::string> log_entries;
  Vector2i cursor_pos;
  bool button_status[3];
  int widget_height;

  void set_mouse_pos(int x, int y) {
    cursor_pos = Vector2i(x, y);
  }
{'fraction_non_alphanumeric': 0.1416019857373626, 'fraction_numerical': 0.04377290098892873, 'mean_word_length': 2.487017447451573, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 43, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4511704', 'n_tokens_mistral': 10258, 'n_tokens_neox': 9362, 'n_words': 2184}