aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/gg
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/gg
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/vlib/gg')
-rw-r--r--v_windows/v/vlib/gg/enums.v161
-rw-r--r--v_windows/v/vlib/gg/gg.c.v278
-rw-r--r--v_windows/v/vlib/gg/gg.v750
-rw-r--r--v_windows/v/vlib/gg/gg_android.c.v37
-rw-r--r--v_windows/v/vlib/gg/gg_darwin.c.v23
-rw-r--r--v_windows/v/vlib/gg/gg_darwin.m125
-rw-r--r--v_windows/v/vlib/gg/image.c.v169
-rw-r--r--v_windows/v/vlib/gg/image.v223
-rw-r--r--v_windows/v/vlib/gg/m4/graphic.v110
-rw-r--r--v_windows/v/vlib/gg/m4/m4_test.v235
-rw-r--r--v_windows/v/vlib/gg/m4/matrix.v595
-rw-r--r--v_windows/v/vlib/gg/m4/vector.v230
-rw-r--r--v_windows/v/vlib/gg/text_rendering.c.v226
-rw-r--r--v_windows/v/vlib/gg/text_rendering.v150
14 files changed, 3312 insertions, 0 deletions
diff --git a/v_windows/v/vlib/gg/enums.v b/v_windows/v/vlib/gg/enums.v
new file mode 100644
index 0000000..c87f986
--- /dev/null
+++ b/v_windows/v/vlib/gg/enums.v
@@ -0,0 +1,161 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module gg
+
+pub enum MouseButton {
+ left = 0
+ right = 1
+ middle = 2
+ invalid = 256
+}
+
+// NB: unlike the MouseButton enum from above,
+// the [flag]-ed enum here can have combined states,
+// representing several pressed buttons at once.
+
+[flag]
+pub enum MouseButtons {
+ left
+ right
+ middle
+}
+
+[flag]
+pub enum Modifier {
+ shift // (1<<0)
+ ctrl // (1<<1)
+ alt // (1<<2)
+ super // (1<<3)
+}
+
+pub enum PenLineType {
+ solid
+ dashed
+ dotted
+}
+
+pub enum KeyCode {
+ invalid = 0
+ space = 32
+ apostrophe = 39 //'
+ comma = 44 //,
+ minus = 45 //-
+ period = 46 //.
+ slash = 47 ///
+ _0 = 48
+ _1 = 49
+ _2 = 50
+ _3 = 51
+ _4 = 52
+ _5 = 53
+ _6 = 54
+ _7 = 55
+ _8 = 56
+ _9 = 57
+ semicolon = 59 //;
+ equal = 61 //=
+ a = 65
+ b = 66
+ c = 67
+ d = 68
+ e = 69
+ f = 70
+ g = 71
+ h = 72
+ i = 73
+ j = 74
+ k = 75
+ l = 76
+ m = 77
+ n = 78
+ o = 79
+ p = 80
+ q = 81
+ r = 82
+ s = 83
+ t = 84
+ u = 85
+ v = 86
+ w = 87
+ x = 88
+ y = 89
+ z = 90
+ left_bracket = 91 //[
+ backslash = 92 //\
+ right_bracket = 93 //]
+ grave_accent = 96 //`
+ world_1 = 161 // non-us #1
+ world_2 = 162 // non-us #2
+ escape = 256
+ enter = 257
+ tab = 258
+ backspace = 259
+ insert = 260
+ delete = 261
+ right = 262
+ left = 263
+ down = 264
+ up = 265
+ page_up = 266
+ page_down = 267
+ home = 268
+ end = 269
+ caps_lock = 280
+ scroll_lock = 281
+ num_lock = 282
+ print_screen = 283
+ pause = 284
+ f1 = 290
+ f2 = 291
+ f3 = 292
+ f4 = 293
+ f5 = 294
+ f6 = 295
+ f7 = 296
+ f8 = 297
+ f9 = 298
+ f10 = 299
+ f11 = 300
+ f12 = 301
+ f13 = 302
+ f14 = 303
+ f15 = 304
+ f16 = 305
+ f17 = 306
+ f18 = 307
+ f19 = 308
+ f20 = 309
+ f21 = 310
+ f22 = 311
+ f23 = 312
+ f24 = 313
+ f25 = 314
+ kp_0 = 320
+ kp_1 = 321
+ kp_2 = 322
+ kp_3 = 323
+ kp_4 = 324
+ kp_5 = 325
+ kp_6 = 326
+ kp_7 = 327
+ kp_8 = 328
+ kp_9 = 329
+ kp_decimal = 330
+ kp_divide = 331
+ kp_multiply = 332
+ kp_subtract = 333
+ kp_add = 334
+ kp_enter = 335
+ kp_equal = 336
+ left_shift = 340
+ left_control = 341
+ left_alt = 342
+ left_super = 343
+ right_shift = 344
+ right_control = 345
+ right_alt = 346
+ right_super = 347
+ menu = 348
+}
+
+const key_code_max = 512
diff --git a/v_windows/v/vlib/gg/gg.c.v b/v_windows/v/vlib/gg/gg.c.v
new file mode 100644
index 0000000..f6956e6
--- /dev/null
+++ b/v_windows/v/vlib/gg/gg.c.v
@@ -0,0 +1,278 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+
+module gg
+
+import os
+import gx
+import sokol
+import sokol.sapp
+import sokol.sgl
+import sokol.gfx
+import math
+
+pub struct Event {
+pub mut:
+ frame_count u64
+ typ sapp.EventType
+ key_code KeyCode
+ char_code u32
+ key_repeat bool
+ modifiers u32
+ mouse_button MouseButton
+ mouse_x f32
+ mouse_y f32
+ mouse_dx f32
+ mouse_dy f32
+ scroll_x f32
+ scroll_y f32
+ num_touches int
+ touches [8]C.sapp_touchpoint
+ window_width int
+ window_height int
+ framebuffer_width int
+ framebuffer_height int
+}
+
+[heap]
+pub struct Context {
+mut:
+ render_text bool = true
+ // a cache with all images created by the user. used for sokol image init and to save space
+ // (so that the user can store image ids, not entire Image objects)
+ image_cache []Image
+ needs_refresh bool = true
+ ticks int // for ui mode only
+pub:
+ native_rendering bool
+pub mut:
+ scale f32 = 1.0
+ // will get set to 2.0 for retina, will remain 1.0 for normal
+ width int
+ height int
+ clear_pass C.sg_pass_action
+ window C.sapp_desc
+ timage_pip C.sgl_pipeline
+ config Config
+ ft &FT
+ font_inited bool
+ ui_mode bool // do not redraw everything 60 times/second, but only when the user requests
+ frame u64 // the current frame counted from the start of the application; always increasing
+ //
+ mbtn_mask byte
+ mouse_buttons MouseButtons // typed version of mbtn_mask; easier to use for user programs
+ mouse_pos_x int
+ mouse_pos_y int
+ mouse_dx int
+ mouse_dy int
+ scroll_x int
+ scroll_y int
+ //
+ key_modifiers Modifier // the current key modifiers
+ key_repeat bool // whether the pressed key was an autorepeated one
+ pressed_keys [key_code_max]bool // an array representing all currently pressed keys
+ pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
+ // *before* the current event was different
+}
+
+fn gg_init_sokol_window(user_data voidptr) {
+ mut g := unsafe { &Context(user_data) }
+ desc := sapp.create_desc()
+ /*
+ desc := C.sg_desc{
+ mtl_device: sapp.metal_get_device()
+ mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor
+ mtl_drawable_cb: sapp.metal_get_drawable
+ d3d11_device: sapp.d3d11_get_device()
+ d3d11_device_context: sapp.d3d11_get_device_context()
+ d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view
+ d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view
+ }
+ */
+ gfx.setup(&desc)
+ sgl_desc := C.sgl_desc_t{}
+ sgl.setup(&sgl_desc)
+ g.scale = dpi_scale()
+ // is_high_dpi := sapp.high_dpi()
+ // fb_w := sapp.width()
+ // fb_h := sapp.height()
+ // println('g.scale=$g.scale is_high_dpi=$is_high_dpi fb_w=$fb_w fb_h=$fb_h')
+ // if g.config.init_text {
+ // `os.is_file()` won't work on Android if the font file is embedded into the APK
+ exists := $if !android { os.is_file(g.config.font_path) } $else { true }
+ if g.config.font_path != '' && !exists {
+ g.render_text = false
+ } else if g.config.font_path != '' && exists {
+ // t := time.ticks()
+ g.ft = new_ft(
+ font_path: g.config.font_path
+ custom_bold_font_path: g.config.custom_bold_font_path
+ scale: dpi_scale()
+ ) or { panic(err) }
+ // println('FT took ${time.ticks()-t} ms')
+ g.font_inited = true
+ } else {
+ if g.config.font_bytes_normal.len > 0 {
+ g.ft = new_ft(
+ bytes_normal: g.config.font_bytes_normal
+ bytes_bold: g.config.font_bytes_bold
+ bytes_mono: g.config.font_bytes_mono
+ bytes_italic: g.config.font_bytes_italic
+ scale: sapp.dpi_scale()
+ ) or { panic(err) }
+ g.font_inited = true
+ } else {
+ sfont := system_font_path()
+ if g.config.font_path != '' {
+ eprintln('font file "$g.config.font_path" does not exist, the system font ($sfont) was used instead.')
+ }
+
+ g.ft = new_ft(
+ font_path: sfont
+ custom_bold_font_path: g.config.custom_bold_font_path
+ scale: sapp.dpi_scale()
+ ) or { panic(err) }
+ g.font_inited = true
+ }
+ }
+ //
+ mut pipdesc := C.sg_pipeline_desc{
+ label: c'alpha_image'
+ }
+ unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
+
+ color_state := C.sg_color_state{
+ blend: C.sg_blend_state{
+ enabled: true
+ src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA)
+ dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA)
+ }
+ }
+ pipdesc.colors[0] = color_state
+
+ g.timage_pip = sgl.make_pipeline(&pipdesc)
+ //
+ if g.config.init_fn != voidptr(0) {
+ g.config.init_fn(g.config.user_data)
+ }
+ // Create images now that we can do that after sg is inited
+ if g.native_rendering {
+ return
+ }
+
+ for i in 0 .. g.image_cache.len {
+ if g.image_cache[i].simg.id == 0 {
+ g.image_cache[i].init_sokol_image()
+ }
+ }
+}
+
+//
+pub fn new_context(cfg Config) &Context {
+ mut g := &Context{
+ width: cfg.width
+ height: cfg.height
+ config: cfg
+ ft: 0
+ ui_mode: cfg.ui_mode
+ native_rendering: cfg.native_rendering
+ }
+ g.set_bg_color(cfg.bg_color)
+ // C.printf('new_context() %p\n', cfg.user_data)
+ window := C.sapp_desc{
+ user_data: g
+ init_userdata_cb: gg_init_sokol_window
+ frame_userdata_cb: gg_frame_fn
+ event_userdata_cb: gg_event_fn
+ fail_userdata_cb: gg_fail_fn
+ cleanup_userdata_cb: gg_cleanup_fn
+ window_title: &char(cfg.window_title.str)
+ html5_canvas_name: &char(cfg.window_title.str)
+ width: cfg.width
+ height: cfg.height
+ sample_count: cfg.sample_count
+ high_dpi: true
+ fullscreen: cfg.fullscreen
+ __v_native_render: cfg.native_rendering
+ }
+ g.window = window
+ return g
+}
+
+pub fn (ctx &Context) draw_circle_line(x f32, y f32, r int, segments int, c gx.Color) {
+ $if macos {
+ if ctx.native_rendering {
+ C.darwin_draw_circle(x - r + 1, ctx.height - (y + r + 3), r, c)
+ return
+ }
+ }
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ nr := r * ctx.scale
+ mut theta := f32(0)
+ mut xx := f32(0)
+ mut yy := f32(0)
+ sgl.begin_line_strip()
+ for i := 0; i < segments + 1; i++ {
+ theta = 2.0 * f32(math.pi) * f32(i) / f32(segments)
+ xx = nr * math.cosf(theta)
+ yy = nr * math.sinf(theta)
+ sgl.v2f(xx + nx, yy + ny)
+ }
+ sgl.end()
+}
+
+pub fn high_dpi() bool {
+ return C.sapp_high_dpi()
+}
+
+pub fn screen_size() Size {
+ $if macos {
+ return C.gg_get_screen_size()
+ }
+ // TODO windows, linux, etc
+ return Size{}
+}
+
+fn C.WaitMessage()
+
+/*
+pub fn wait_events() {
+ unsafe {
+ $if macos {
+ #NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
+ #untilDate:[NSDate distantFuture]
+ #inMode:NSDefaultRunLoopMode
+ #dequeue:YES];
+ #[NSApp sendEvent:event];
+ }
+ $if windows {
+ C.WaitMessage()
+ }
+ }
+}
+*/
+
+// TODO: Fix alpha
+pub fn (ctx &Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) {
+ $if macos {
+ if ctx.native_rendering {
+ C.darwin_draw_rect(x, ctx.height - (y + h), w, h, c)
+ return
+ }
+ }
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ sgl.begin_quads()
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + w) * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale)
+ sgl.v2f(x * ctx.scale, (y + h) * ctx.scale)
+ sgl.end()
+}
diff --git a/v_windows/v/vlib/gg/gg.v b/v_windows/v/vlib/gg/gg.v
new file mode 100644
index 0000000..d1dbaa4
--- /dev/null
+++ b/v_windows/v/vlib/gg/gg.v
@@ -0,0 +1,750 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+
+module gg
+
+import gx
+import sokol.sapp
+import sokol.sgl
+import sokol.gfx
+import math
+
+pub type FNCb = fn (data voidptr)
+
+pub type FNEvent = fn (e &Event, data voidptr)
+
+pub type FNFail = fn (msg string, data voidptr)
+
+pub type FNKeyDown = fn (c KeyCode, m Modifier, data voidptr)
+
+pub type FNKeyUp = fn (c KeyCode, m Modifier, data voidptr)
+
+pub type FNMove = fn (x f32, y f32, data voidptr)
+
+pub type FNClick = fn (x f32, y f32, button MouseButton, data voidptr)
+
+pub type FNUnClick = fn (x f32, y f32, button MouseButton, data voidptr)
+
+pub type FNChar = fn (c u32, data voidptr)
+
+pub struct Config {
+pub:
+ width int
+ height int
+ use_ortho bool // unused, still here just for backwards compatibility
+ retina bool
+ resizable bool
+ user_data voidptr
+ font_size int
+ create_window bool
+ // window_user_ptr voidptr
+ window_title string
+ borderless_window bool
+ always_on_top bool
+ bg_color gx.Color
+ init_fn FNCb = voidptr(0)
+ frame_fn FNCb = voidptr(0)
+ native_frame_fn FNCb = voidptr(0)
+ cleanup_fn FNCb = voidptr(0)
+ fail_fn FNFail = voidptr(0)
+ //
+ event_fn FNEvent = voidptr(0)
+ quit_fn FNEvent = voidptr(0)
+ //
+ keydown_fn FNKeyDown = voidptr(0)
+ keyup_fn FNKeyUp = voidptr(0)
+ char_fn FNChar = voidptr(0)
+ //
+ move_fn FNMove = voidptr(0)
+ click_fn FNClick = voidptr(0)
+ unclick_fn FNUnClick = voidptr(0)
+ leave_fn FNEvent = voidptr(0)
+ enter_fn FNEvent = voidptr(0)
+ resized_fn FNEvent = voidptr(0)
+ scroll_fn FNEvent = voidptr(0)
+ // wait_events bool // set this to true for UIs, to save power
+ fullscreen bool
+ scale f32 = 1.0
+ sample_count int
+ // ved needs this
+ // init_text bool
+ font_path string
+ custom_bold_font_path string
+ ui_mode bool // refreshes only on events to save CPU usage
+ // font bytes for embedding
+ font_bytes_normal []byte
+ font_bytes_bold []byte
+ font_bytes_mono []byte
+ font_bytes_italic []byte
+ native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows
+}
+
+pub struct PenConfig {
+ color gx.Color
+ line_type PenLineType = .solid
+ thickness int = 1
+}
+
+pub struct Size {
+pub:
+ width int
+ height int
+}
+
+fn gg_frame_fn(user_data voidptr) {
+ mut ctx := unsafe { &Context(user_data) }
+ ctx.frame++
+ if ctx.config.frame_fn == voidptr(0) {
+ return
+ }
+ if ctx.native_rendering {
+ // return
+ }
+
+ if ctx.ui_mode && !ctx.needs_refresh {
+ // Draw 3 more frames after the "stop refresh" command
+ ctx.ticks++
+ if ctx.ticks > 3 {
+ return
+ }
+ }
+ ctx.config.frame_fn(ctx.config.user_data)
+ ctx.needs_refresh = false
+}
+
+pub fn (mut ctx Context) refresh_ui() {
+ ctx.needs_refresh = true
+ ctx.ticks = 0
+}
+
+fn gg_event_fn(ce &C.sapp_event, user_data voidptr) {
+ // e := unsafe { &sapp.Event(ce) }
+ mut e := unsafe { &Event(ce) }
+ mut g := unsafe { &Context(user_data) }
+ if g.ui_mode {
+ g.refresh_ui()
+ }
+ if e.typ == .mouse_down {
+ bitplace := int(e.mouse_button)
+ g.mbtn_mask |= byte(1 << bitplace)
+ g.mouse_buttons = MouseButtons(g.mbtn_mask)
+ }
+ if e.typ == .mouse_up {
+ bitplace := int(e.mouse_button)
+ g.mbtn_mask &= ~(byte(1 << bitplace))
+ g.mouse_buttons = MouseButtons(g.mbtn_mask)
+ }
+ if e.typ == .mouse_move && e.mouse_button == .invalid {
+ if g.mbtn_mask & 0x01 > 0 {
+ e.mouse_button = .left
+ }
+ if g.mbtn_mask & 0x02 > 0 {
+ e.mouse_button = .right
+ }
+ if g.mbtn_mask & 0x04 > 0 {
+ e.mouse_button = .middle
+ }
+ }
+ g.mouse_pos_x = int(e.mouse_x / g.scale)
+ g.mouse_pos_y = int(e.mouse_y / g.scale)
+ g.mouse_dx = int(e.mouse_dx / g.scale)
+ g.mouse_dy = int(e.mouse_dy / g.scale)
+ g.scroll_x = int(e.scroll_x / g.scale)
+ g.scroll_y = int(e.scroll_y / g.scale)
+ g.key_modifiers = Modifier(e.modifiers)
+ g.key_repeat = e.key_repeat
+ if e.typ in [.key_down, .key_up] {
+ key_idx := int(e.key_code) % key_code_max
+ prev := g.pressed_keys[key_idx]
+ next := e.typ == .key_down
+ g.pressed_keys[key_idx] = next
+ g.pressed_keys_edge[key_idx] = prev != next
+ }
+ if g.config.event_fn != voidptr(0) {
+ g.config.event_fn(e, g.config.user_data)
+ }
+ match e.typ {
+ .mouse_move {
+ if g.config.move_fn != voidptr(0) {
+ g.config.move_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, g.config.user_data)
+ }
+ }
+ .mouse_down {
+ if g.config.click_fn != voidptr(0) {
+ g.config.click_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, e.mouse_button,
+ g.config.user_data)
+ }
+ }
+ .mouse_up {
+ if g.config.unclick_fn != voidptr(0) {
+ g.config.unclick_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, e.mouse_button,
+ g.config.user_data)
+ }
+ }
+ .mouse_leave {
+ if g.config.leave_fn != voidptr(0) {
+ g.config.leave_fn(e, g.config.user_data)
+ }
+ }
+ .mouse_enter {
+ if g.config.enter_fn != voidptr(0) {
+ g.config.enter_fn(e, g.config.user_data)
+ }
+ }
+ .mouse_scroll {
+ if g.config.scroll_fn != voidptr(0) {
+ g.config.scroll_fn(e, g.config.user_data)
+ }
+ }
+ .key_down {
+ if g.config.keydown_fn != voidptr(0) {
+ g.config.keydown_fn(e.key_code, Modifier(e.modifiers), g.config.user_data)
+ }
+ }
+ .key_up {
+ if g.config.keyup_fn != voidptr(0) {
+ g.config.keyup_fn(e.key_code, Modifier(e.modifiers), g.config.user_data)
+ }
+ }
+ .char {
+ if g.config.char_fn != voidptr(0) {
+ g.config.char_fn(e.char_code, g.config.user_data)
+ }
+ }
+ .resized {
+ if g.config.resized_fn != voidptr(0) {
+ g.config.resized_fn(e, g.config.user_data)
+ }
+ }
+ .quit_requested {
+ if g.config.quit_fn != voidptr(0) {
+ g.config.quit_fn(e, g.config.user_data)
+ }
+ }
+ else {
+ // dump(e)
+ }
+ }
+}
+
+fn gg_cleanup_fn(user_data voidptr) {
+ mut g := unsafe { &Context(user_data) }
+ if g.config.cleanup_fn != voidptr(0) {
+ g.config.cleanup_fn(g.config.user_data)
+ }
+}
+
+fn gg_fail_fn(msg &char, user_data voidptr) {
+ mut g := unsafe { &Context(user_data) }
+ vmsg := unsafe { tos3(msg) }
+ if g.config.fail_fn != voidptr(0) {
+ g.config.fail_fn(vmsg, g.config.user_data)
+ } else {
+ eprintln('gg error: $vmsg')
+ }
+}
+
+pub fn (gg &Context) run() {
+ sapp.run(&gg.window)
+}
+
+// quit closes the context window and exits the event loop for it
+pub fn (ctx &Context) quit() {
+ sapp.request_quit() // does not require ctx right now, but sokol multi-window might in the future
+}
+
+pub fn (mut ctx Context) set_bg_color(c gx.Color) {
+ ctx.clear_pass = gfx.create_clear_pass(f32(c.r) / 255.0, f32(c.g) / 255.0, f32(c.b) / 255.0,
+ f32(c.a) / 255.0)
+}
+
+[inline]
+pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) {
+ ctx.draw_rect(x, y, s, s, c)
+}
+
+[inline]
+pub fn (ctx &Context) set_pixel(x f32, y f32, c gx.Color) {
+ ctx.draw_square(x, y, 1, c)
+}
+
+pub fn (ctx &Context) set_pixels(points []f32, c gx.Color) {
+ assert points.len % 2 == 0
+ len := points.len / 2
+
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ sgl.begin_quads()
+ for i in 0 .. len {
+ x, y := points[i * 2], points[i * 2 + 1]
+
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + 1) * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + 1) * ctx.scale, (y + 1) * ctx.scale)
+ sgl.v2f(x * ctx.scale, (y + 1) * ctx.scale)
+ }
+ sgl.end()
+}
+
+pub fn (ctx &Context) draw_triangle(x f32, y f32, x2 f32, y2 f32, x3 f32, y3 f32, c gx.Color) {
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ sgl.begin_quads()
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ sgl.v2f(x2 * ctx.scale, y2 * ctx.scale)
+ sgl.v2f(x3 * ctx.scale, y3 * ctx.scale)
+ sgl.end()
+}
+
+pub fn (ctx &Context) draw_empty_rect(x f32, y f32, w f32, h f32, c gx.Color) {
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ sgl.begin_line_strip()
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + w) * ctx.scale, y * ctx.scale)
+ sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale)
+ sgl.v2f(x * ctx.scale, (y + h) * ctx.scale)
+ sgl.v2f(x * ctx.scale, (y - 1) * ctx.scale)
+ sgl.end()
+}
+
+[inline]
+pub fn (ctx &Context) draw_empty_square(x f32, y f32, s f32, c gx.Color) {
+ ctx.draw_empty_rect(x, y, s, s, c)
+}
+
+pub fn (ctx &Context) draw_circle(x f32, y f32, r f32, c gx.Color) {
+ ctx.draw_circle_with_segments(x, y, r, 10, c)
+}
+
+pub fn (ctx &Context) draw_circle_with_segments(x f32, y f32, r f32, segments int, c gx.Color) {
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ nr := r * ctx.scale
+ mut theta := f32(0)
+ mut xx := f32(0)
+ mut yy := f32(0)
+ sgl.begin_triangle_strip()
+ for i := 0; i < segments + 1; i++ {
+ theta = 2.0 * f32(math.pi) * f32(i) / f32(segments)
+ xx = nr * math.cosf(theta)
+ yy = nr * math.sinf(theta)
+ sgl.v2f(xx + nx, yy + ny)
+ sgl.v2f(nx, ny)
+ }
+ sgl.end()
+}
+
+pub fn (ctx &Context) draw_arc_line(x f32, y f32, r int, start_angle f32, arc_angle f32, segments int, c gx.Color) {
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ theta := f32(arc_angle / f32(segments))
+ tan_factor := math.tanf(theta)
+ rad_factor := math.cosf(theta)
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ mut xx := f32(r * math.cosf(start_angle))
+ mut yy := f32(r * math.sinf(start_angle))
+ sgl.begin_line_strip()
+ for i := 0; i < segments + 1; i++ {
+ sgl.v2f(xx + nx, yy + ny)
+ tx := -yy
+ ty := xx
+ xx += tx * tan_factor
+ yy += ty * tan_factor
+ xx *= rad_factor
+ yy *= rad_factor
+ }
+ sgl.end()
+}
+
+pub fn (ctx &Context) draw_arc(x f32, y f32, r int, start_angle f32, arc_angle f32, segments int, c gx.Color) {
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ theta := f32(arc_angle / f32(segments))
+ tan_factor := math.tanf(theta)
+ rad_factor := math.cosf(theta)
+ mut xx := f32(r * math.cosf(start_angle))
+ mut yy := f32(r * math.sinf(start_angle))
+ sgl.begin_triangle_strip()
+ for i := 0; i < segments + 1; i++ {
+ sgl.v2f(xx + nx, yy + ny)
+ sgl.v2f(nx, ny)
+ tx := -yy
+ ty := xx
+ xx += tx * tan_factor
+ yy += ty * tan_factor
+ xx *= rad_factor
+ yy *= rad_factor
+ }
+ sgl.end()
+}
+
+pub fn (gg &Context) begin() {
+ if gg.render_text && gg.font_inited {
+ gg.ft.flush()
+ }
+ sgl.defaults()
+ sgl.matrix_mode_projection()
+ sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0)
+}
+
+pub fn (gg &Context) end() {
+ gfx.begin_default_pass(gg.clear_pass, sapp.width(), sapp.height())
+ sgl.draw()
+ gfx.end_pass()
+ gfx.commit()
+ /*
+ if gg.config.wait_events {
+ // println('gg: waiting')
+ wait_events()
+ }
+ */
+}
+
+// resize the context's Window
+pub fn (mut ctx Context) resize(width int, height int) {
+ ctx.width = width
+ ctx.height = height
+}
+
+// draw_line draws a line between the points provided
+pub fn (ctx &Context) draw_line(x f32, y f32, x2 f32, y2 f32, c gx.Color) {
+ $if macos {
+ if ctx.native_rendering {
+ // Make the line more clear on hi dpi screens: draw a rectangle
+ mut width := math.abs(x2 - x)
+ mut height := math.abs(y2 - y)
+ if width == 0 {
+ width = 1
+ } else if height == 0 {
+ height = 1
+ }
+ ctx.draw_rect(x, y, f32(width), f32(height), c)
+ return
+ }
+ }
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+ sgl.begin_line_strip()
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ sgl.v2f(x2 * ctx.scale, y2 * ctx.scale)
+ sgl.end()
+}
+
+// draw_line_with_config draws a line between the points provided with the PenConfig
+pub fn (ctx &Context) draw_line_with_config(x f32, y f32, x2 f32, y2 f32, config PenConfig) {
+ if config.color.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+
+ if config.thickness <= 0 {
+ return
+ }
+
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ nx2 := x2 * ctx.scale
+ ny2 := y2 * ctx.scale
+
+ dx := nx2 - nx
+ dy := ny2 - ny
+ length := math.sqrtf(math.powf(x2 - x, 2) + math.powf(y2 - y, 2))
+ theta := f32(math.atan2(dy, dx))
+
+ sgl.push_matrix()
+
+ sgl.translate(nx, ny, 0)
+ sgl.rotate(theta, 0, 0, 1)
+ sgl.translate(-nx, -ny, 0)
+
+ if config.line_type == .solid {
+ ctx.draw_rect(x, y, length, config.thickness, config.color)
+ } else {
+ size := if config.line_type == .dotted { config.thickness } else { config.thickness * 3 }
+ space := if size == 1 { 2 } else { size }
+
+ mut available := length
+ mut start_x := x
+
+ for i := 0; available > 0; i++ {
+ if i % 2 == 0 {
+ ctx.draw_rect(start_x, y, size, config.thickness, config.color)
+ available -= size
+ start_x += size
+ continue
+ }
+
+ available -= space
+ start_x += space
+ }
+ }
+
+ sgl.pop_matrix()
+}
+
+pub fn (ctx &Context) draw_rounded_rect(x f32, y f32, w f32, h f32, radius f32, color gx.Color) {
+ sgl.c4b(color.r, color.g, color.b, color.a)
+ sgl.begin_triangle_strip()
+ mut theta := f32(0)
+ mut xx := f32(0)
+ mut yy := f32(0)
+ r := radius * ctx.scale
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ width := w * ctx.scale
+ height := h * ctx.scale
+ segments := 2 * math.pi * r
+ segdiv := segments / 4
+ rb := 0
+ lb := int(rb + segdiv)
+ lt := int(lb + segdiv)
+ rt := int(lt + segdiv)
+ // left top
+ lx := nx + r
+ ly := ny + r
+ for i in lt .. rt {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + lx, yy + ly)
+ sgl.v2f(lx, ly)
+ }
+ // right top
+ mut rx := nx + width - r
+ mut ry := ny + r
+ for i in rt .. int(segments) {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + rx, yy + ry)
+ sgl.v2f(rx, ry)
+ }
+ // right bottom
+ mut rbx := rx
+ mut rby := ny + height - r
+ for i in rb .. lb {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + rbx, yy + rby)
+ sgl.v2f(rbx, rby)
+ }
+ // left bottom
+ mut lbx := lx
+ mut lby := ny + height - r
+ for i in lb .. lt {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + lbx, yy + lby)
+ sgl.v2f(lbx, lby)
+ }
+ sgl.v2f(lx + xx, ly)
+ sgl.v2f(lx, ly)
+ sgl.end()
+ sgl.begin_quads()
+ sgl.v2f(lx, ly)
+ sgl.v2f(rx, ry)
+ sgl.v2f(rbx, rby)
+ sgl.v2f(lbx, lby)
+ sgl.end()
+}
+
+pub fn (ctx &Context) draw_empty_rounded_rect(x f32, y f32, w f32, h f32, radius f32, border_color gx.Color) {
+ mut theta := f32(0)
+ mut xx := f32(0)
+ mut yy := f32(0)
+ r := radius * ctx.scale
+ nx := x * ctx.scale
+ ny := y * ctx.scale
+ width := w * ctx.scale
+ height := h * ctx.scale
+ segments := 2 * math.pi * r
+ segdiv := segments / 4
+ rb := 0
+ lb := int(rb + segdiv)
+ lt := int(lb + segdiv)
+ rt := int(lt + segdiv)
+ sgl.c4b(border_color.r, border_color.g, border_color.b, border_color.a)
+ sgl.begin_line_strip()
+ // left top
+ lx := nx + r
+ ly := ny + r
+ for i in lt .. rt {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + lx, yy + ly)
+ }
+ // right top
+ mut rx := nx + width - r
+ mut ry := ny + r
+ for i in rt .. int(segments) {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + rx, yy + ry)
+ }
+ // right bottom
+ mut rbx := rx
+ mut rby := ny + height - r
+ for i in rb .. lb {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + rbx, yy + rby)
+ }
+ // left bottom
+ mut lbx := lx
+ mut lby := ny + height - r
+ for i in lb .. lt {
+ theta = 2 * f32(math.pi) * f32(i) / segments
+ xx = r * math.cosf(theta)
+ yy = r * math.sinf(theta)
+ sgl.v2f(xx + lbx, yy + lby)
+ }
+ sgl.v2f(lx + xx, ly)
+ sgl.end()
+}
+
+// draw_convex_poly draws a convex polygon, given an array of points, and a color.
+// Note that the points must be given in clockwise order.
+pub fn (ctx &Context) draw_convex_poly(points []f32, c gx.Color) {
+ assert points.len % 2 == 0
+ len := points.len / 2
+ assert len >= 3
+
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+
+ sgl.begin_triangle_strip()
+ x0 := points[0] * ctx.scale
+ y0 := points[1] * ctx.scale
+ for i in 1 .. (len / 2 + 1) {
+ sgl.v2f(x0, y0)
+ sgl.v2f(points[i * 4 - 2] * ctx.scale, points[i * 4 - 1] * ctx.scale)
+ sgl.v2f(points[i * 4] * ctx.scale, points[i * 4 + 1] * ctx.scale)
+ }
+
+ if len % 2 == 0 {
+ sgl.v2f(points[2 * len - 2] * ctx.scale, points[2 * len - 1] * ctx.scale)
+ }
+ sgl.end()
+}
+
+// draw_empty_poly - draws the borders of a polygon, given an array of points, and a color.
+// Note that the points must be given in clockwise order.
+pub fn (ctx &Context) draw_empty_poly(points []f32, c gx.Color) {
+ assert points.len % 2 == 0
+ len := points.len / 2
+ assert len >= 3
+
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+
+ sgl.begin_line_strip()
+ for i in 0 .. len {
+ sgl.v2f(points[2 * i] * ctx.scale, points[2 * i + 1] * ctx.scale)
+ }
+ sgl.v2f(points[0] * ctx.scale, points[1] * ctx.scale)
+ sgl.end()
+}
+
+// draw_cubic_bezier draws a cubic Bézier curve, also known as a spline, from four points.
+// The four points is provided as one `points` array which contains a stream of point pairs (x and y coordinates).
+// Thus a cubic Bézier could be declared as: `points := [x1, y1, control_x1, control_y1, control_x2, control_y2, x2, y2]`.
+// Please see `draw_cubic_bezier_in_steps` to control the amount of steps (segments) used to draw the curve.
+pub fn (ctx &Context) draw_cubic_bezier(points []f32, c gx.Color) {
+ ctx.draw_cubic_bezier_in_steps(points, u32(30 * ctx.scale), c)
+}
+
+// draw_cubic_bezier_in_steps draws a cubic Bézier curve, also known as a spline, from four points.
+// The smoothness of the curve can be controlled with the `steps` parameter. `steps` determines how many iterations is
+// taken to draw the curve.
+// The four points is provided as one `points` array which contains a stream of point pairs (x and y coordinates).
+// Thus a cubic Bézier could be declared as: `points := [x1, y1, control_x1, control_y1, control_x2, control_y2, x2, y2]`.
+pub fn (ctx &Context) draw_cubic_bezier_in_steps(points []f32, steps u32, c gx.Color) {
+ assert steps > 0
+ assert points.len == 8
+
+ if c.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ sgl.c4b(c.r, c.g, c.b, c.a)
+
+ sgl.begin_line_strip()
+
+ p1_x, p1_y := points[0], points[1]
+ p2_x, p2_y := points[6], points[7]
+
+ ctrl_p1_x, ctrl_p1_y := points[2], points[3]
+ ctrl_p2_x, ctrl_p2_y := points[4], points[5]
+
+ // The constant 3 is actually points.len() - 1;
+
+ step := f32(1.0) / steps
+ sgl.v2f(p1_x * ctx.scale, p1_y * ctx.scale)
+ for u := f32(0.0); u <= f32(1.0); u += step {
+ pow_2_u := u * u
+ pow_3_u := pow_2_u * u
+
+ x := pow_3_u * (p2_x + 3 * (ctrl_p1_x - ctrl_p2_x) - p1_x) +
+ 3 * pow_2_u * (p1_x - 2 * ctrl_p1_x + ctrl_p2_x) + 3 * u * (ctrl_p1_x - p1_x) + p1_x
+
+ y := pow_3_u * (p2_y + 3 * (ctrl_p1_y - ctrl_p2_y) - p1_y) +
+ 3 * pow_2_u * (p1_y - 2 * ctrl_p1_y + ctrl_p2_y) + 3 * u * (ctrl_p1_y - p1_y) + p1_y
+
+ sgl.v2f(x * ctx.scale, y * ctx.scale)
+ }
+ sgl.v2f(p2_x * ctx.scale, p2_y * ctx.scale)
+
+ sgl.end()
+}
+
+// window_size returns the `Size` of the active window
+pub fn window_size() Size {
+ s := dpi_scale()
+ return Size{int(sapp.width() / s), int(sapp.height() / s)}
+}
+
+// window_size_real_pixels returns the `Size` of the active window without scale
+pub fn window_size_real_pixels() Size {
+ return Size{sapp.width(), sapp.height()}
+}
+
+pub fn dpi_scale() f32 {
+ mut s := sapp.dpi_scale()
+ $if android {
+ s *= android_dpi_scale()
+ }
+ // NB: on older X11, `Xft.dpi` from ~/.Xresources, that sokol uses,
+ // may not be set which leads to sapp.dpi_scale reporting incorrectly 0.0
+ if s < 0.1 {
+ s = 1.0
+ }
+ return s
+}
diff --git a/v_windows/v/vlib/gg/gg_android.c.v b/v_windows/v/vlib/gg/gg_android.c.v
new file mode 100644
index 0000000..23450f2
--- /dev/null
+++ b/v_windows/v/vlib/gg/gg_android.c.v
@@ -0,0 +1,37 @@
+module gg
+
+import sokol.sapp
+
+#include <android/configuration.h>
+#include <android/native_activity.h>
+
+fn C.AConfiguration_new() voidptr
+fn C.AConfiguration_fromAssetManager(voidptr, voidptr)
+fn C.AConfiguration_getDensity(voidptr) u32
+fn C.AConfiguration_delete(voidptr)
+
+struct C.AAssetManager {}
+
+// See https://developer.android.com/ndk/reference/struct/a-native-activity for more info.
+struct C.ANativeActivity {
+pub:
+ assetManager voidptr // Pointer to the Asset Manager instance for the application. (AAssetManager *)
+ callbacks voidptr // Pointer to the callback function table of the native application. (struct ANativeActivityCallbacks *)
+ clazz voidptr // The NativeActivity object handle.
+ env voidptr // JNI context for the main thread of the app.
+ externalDataPath &char // Path to this application's external (removable/mountable) data directory.
+ instance voidptr // This is the native instance of the application.
+ internalDataPath &char // Path to this application's internal data directory.
+ obbPath &char // Available starting with Honeycomb: path to the directory containing the application's OBB files (if any).
+ sdkVersion int // The platform's SDK version code.
+ vm voidptr // The global handle on the process's Java VM
+}
+
+pub fn android_dpi_scale() f32 {
+ config := C.AConfiguration_new()
+ activity := &C.ANativeActivity(sapp.android_get_native_activity())
+ C.AConfiguration_fromAssetManager(config, activity.assetManager)
+ density := C.AConfiguration_getDensity(config)
+ C.AConfiguration_delete(config)
+ return f32(density) / 160
+}
diff --git a/v_windows/v/vlib/gg/gg_darwin.c.v b/v_windows/v/vlib/gg/gg_darwin.c.v
new file mode 100644
index 0000000..d95b8c4
--- /dev/null
+++ b/v_windows/v/vlib/gg/gg_darwin.c.v
@@ -0,0 +1,23 @@
+module gg
+
+#include "@VEXEROOT/vlib/gg/gg_darwin.m"
+
+fn C.gg_get_screen_size() Size
+
+fn C.darwin_draw_string(x int, y int, s string, cfg voidptr)
+
+fn C.darwin_text_width(s string) int
+
+fn C.darwin_text_width_runes(r []rune) int
+
+fn C.darwin_window_refresh()
+
+fn C.darwin_draw_rect(f32, f32, f32, f32, voidptr)
+
+fn C.darwin_create_image(path string) Image
+
+fn C.darwin_draw_image(f32, f32, f32, f32, &Image)
+
+fn C.darwin_draw_circle(f32, f32, f32, voidptr)
+
+//, gx.Color c)
diff --git a/v_windows/v/vlib/gg/gg_darwin.m b/v_windows/v/vlib/gg/gg_darwin.m
new file mode 100644
index 0000000..3ec883d
--- /dev/null
+++ b/v_windows/v/vlib/gg/gg_darwin.m
@@ -0,0 +1,125 @@
+#include <Cocoa/Cocoa.h>
+
+NSColor* nscolor(gx__Color c) {
+ float red= (float)c.r / 255.0f;
+ float green= (float)c.g / 255.0f;
+ float blue= (float)c.b / 255.0f;
+ return [NSColor colorWithDeviceRed:red green:green blue:blue alpha:1.0f];
+}
+
+NSString* nsstring(string s) {
+ return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len
+ encoding:NSUTF8StringEncoding freeWhenDone: false];
+}
+
+
+gg__Size gg_get_screen_size() {
+ NSScreen *screen = [NSScreen mainScreen];
+ NSDictionary *description = [screen deviceDescription];
+ NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+ CGSize displayPhysicalSize = CGDisplayScreenSize(
+ [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+ gg__Size res;
+ res.width = displayPixelSize.width;
+ res.height = displayPixelSize.height;
+ return res;
+}
+
+void darwin_draw_string(int x, int y, string s, gx__TextCfg cfg) {
+ NSFont* font = [NSFont userFontOfSize: 0]; //cfg.size];
+ // # NSFont* font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size];
+ if (cfg.mono) {
+ // # font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size];
+ font = [NSFont fontWithName:@"Menlo" size:cfg.size-5];
+ }
+if (cfg.bold) {
+ font = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask];
+}
+
+
+ NSDictionary* attr = @{
+NSForegroundColorAttributeName: nscolor(cfg.color),
+//NSParagraphStyleAttributeName: paragraphStyle,
+NSFontAttributeName: font,
+};
+ [nsstring(s) drawAtPoint:NSMakePoint(x,y-15) withAttributes:attr];
+}
+
+int darwin_text_width(string s) {
+ // println('text_width "$s" len=$s.len')
+ NSString* n = @"";
+ if (s.len == 1) {
+ // println('len=1')
+ n=[NSString stringWithFormat:@"%c" , s.str[0]];
+ }
+ else {
+ n = nsstring(s);
+ }
+ /*
+ # if (!defaultFont){
+ # defaultFont = [NSFont userFontOfSize: ui__DEFAULT_FONT_SIZE];
+ # }
+ # NSDictionary *attrs = @{
+ # NSFontAttributeName: defaultFont,
+ # };
+ */
+ NSSize size = [n sizeWithAttributes:nil];
+ // # printf("!!!%f\n", ceil(size.width));
+ return (int)(ceil(size.width));
+}
+
+void darwin_draw_rect(float x, float y, float width, float height, gx__Color c) {
+ NSColor* color = nscolor(c);
+ NSRect rect = NSMakeRect(x, y, width, height);
+ [color setFill];
+ NSRectFill(rect);
+}
+
+
+void darwin_window_refresh() {
+ //[g_view setNeedsDisplay:YES];
+ // update UI on the main thread TODO separate fn
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [g_view setNeedsDisplay:YES];
+ });
+
+ //puts("refresh");
+ //[g_view drawRect:NSMakeRect(0,0,2000,2000)];
+ //[[NSGraphicsContext currentContext] flushGraphics];
+}
+
+gg__Image darwin_create_image(string path_) {
+ // file = file.trim_space()
+ NSString* path = nsstring(path_);
+ NSImage* img = [[NSImage alloc] initWithContentsOfFile:path];
+ if (img == 0) {
+ }
+ NSSize size = [img size];
+ gg__Image res;
+ res.width = size.width;
+ res.height = size.height;
+ res.path = path_;
+ res.ok = true;
+ //printf("inited img width=%d\n", res.width) ;
+ // need __brige_retained so that the pointer is not freed by ARC
+ res.data = (__bridge_retained voidptr)(img);
+ return res;
+}
+
+void darwin_draw_image(float x, float y, float w, float h, gg__Image* img) {
+ NSImage* i= (__bridge NSImage*)(img->data);
+ [i drawInRect:NSMakeRect(x,y,w,h)];
+}
+
+void darwin_draw_circle(float x, float y, float d, gx__Color color) {
+ NSColor* c = nscolor(color);
+ NSRect rect = NSMakeRect(x, y, d * 2, d * 2);
+ NSBezierPath* circlePath = [NSBezierPath bezierPath];
+ [circlePath appendBezierPathWithOvalInRect: rect];
+ [c setFill];
+ // [circlePath stroke];
+ [circlePath fill];
+ // NSRectFill(rect);
+}
+
diff --git a/v_windows/v/vlib/gg/image.c.v b/v_windows/v/vlib/gg/image.c.v
new file mode 100644
index 0000000..352eac4
--- /dev/null
+++ b/v_windows/v/vlib/gg/image.c.v
@@ -0,0 +1,169 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module gg
+
+import os
+import stbi
+import sokol.gfx
+
+fn C.sg_isvalid() bool
+
+// TODO return ?Image
+pub fn (mut ctx Context) create_image(file string) Image {
+ // println('\ncreate_image("$file")')
+ if !os.exists(file) {
+ return Image{}
+ }
+ $if macos {
+ if ctx.native_rendering {
+ // return C.darwin_create_image(file)
+ mut img := C.darwin_create_image(file)
+ // println('created macos image: $img.path w=$img.width')
+ // C.printf('p = %p\n', img.data)
+ img.id = ctx.image_cache.len
+ ctx.image_cache << img
+ return img
+ }
+ }
+ if !C.sg_isvalid() {
+ // Sokol is not initialized yet, add stbi object to a queue/cache
+ // ctx.image_queue << file
+ stb_img := stbi.load(file) or { return Image{} }
+ img := Image{
+ width: stb_img.width
+ height: stb_img.height
+ nr_channels: stb_img.nr_channels
+ ok: false
+ data: stb_img.data
+ ext: stb_img.ext
+ path: file
+ id: ctx.image_cache.len
+ }
+ ctx.image_cache << img
+ return img
+ }
+ mut img := create_image(file)
+ img.id = ctx.image_cache.len
+ ctx.image_cache << img
+ return img
+}
+
+pub fn (mut img Image) init_sokol_image() &Image {
+ // println('\n init sokol image $img.path ok=$img.simg_ok')
+ mut img_desc := C.sg_image_desc{
+ width: img.width
+ height: img.height
+ num_mipmaps: 0
+ wrap_u: .clamp_to_edge
+ wrap_v: .clamp_to_edge
+ label: img.path.str
+ d3d11_texture: 0
+ }
+ img_desc.data.subimage[0][0] = C.sg_range{
+ ptr: img.data
+ size: size_t(img.nr_channels * img.width * img.height)
+ }
+ img.simg = C.sg_make_image(&img_desc)
+ img.simg_ok = true
+ img.ok = true
+ return img
+}
+
+// draw_image draws the provided image onto the screen
+pub fn (ctx &Context) draw_image(x f32, y f32, width f32, height f32, img_ &Image) {
+ $if macos {
+ if img_.id >= ctx.image_cache.len {
+ eprintln('gg: draw_image() bad img id $img_.id (img cache len = $ctx.image_cache.len)')
+ return
+ }
+ if ctx.native_rendering {
+ if img_.width == 0 {
+ return
+ }
+ if !os.exists(img_.path) {
+ return
+ }
+ C.darwin_draw_image(x, ctx.height - (y + height), width, height, img_)
+ return
+ }
+ }
+
+ ctx.draw_image_with_config(
+ img: img_
+ img_rect: Rect{x, y, width, height}
+ part_rect: Rect{0, 0, img_.width, img_.height}
+ )
+}
+
+// new_streaming_image returns a cached `image_idx` of a special image, that
+// can be updated *each frame* by calling: gg.update_pixel_data(image_idx, buf)
+// ... where buf is a pointer to the actual pixel data for the image.
+// NB: you still need to call app.gg.draw_image after that, to actually draw it.
+pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int, sicfg StreamingImageConfig) int {
+ mut img := Image{}
+ img.width = w
+ img.height = h
+ img.nr_channels = channels // 4 bytes per pixel for .rgba8, see pixel_format
+ mut img_desc := C.sg_image_desc{
+ width: img.width
+ height: img.height
+ pixel_format: sicfg.pixel_format
+ num_slices: 1
+ num_mipmaps: 1
+ usage: .stream
+ wrap_u: sicfg.wrap_u
+ wrap_v: sicfg.wrap_v
+ min_filter: sicfg.min_filter
+ mag_filter: sicfg.mag_filter
+ label: img.path.str
+ }
+ // Sokol requires that streamed images have NO .ptr/.size initially:
+ img_desc.data.subimage[0][0] = C.sg_range{
+ ptr: 0
+ size: size_t(0)
+ }
+ img.simg = C.sg_make_image(&img_desc)
+ img.simg_ok = true
+ img.ok = true
+ img_idx := ctx.cache_image(img)
+ return img_idx
+}
+
+// update_pixel_data is a helper for working with image streams (i.e. images,
+// that are updated dynamically by the CPU on each frame)
+pub fn (mut ctx Context) update_pixel_data(cached_image_idx int, buf &byte) {
+ mut image := ctx.get_cached_image_by_idx(cached_image_idx)
+ image.update_pixel_data(buf)
+}
+
+pub fn (mut img Image) update_pixel_data(buf &byte) {
+ mut data := C.sg_image_data{}
+ data.subimage[0][0].ptr = buf
+ data.subimage[0][0].size = size_t(img.width * img.height * img.nr_channels)
+ gfx.update_image(img.simg, &data)
+}
+
+// TODO copypasta
+pub fn (mut ctx Context) create_image_with_size(file string, width int, height int) Image {
+ if !C.sg_isvalid() {
+ // Sokol is not initialized yet, add stbi object to a queue/cache
+ // ctx.image_queue << file
+ stb_img := stbi.load(file) or { return Image{} }
+ img := Image{
+ width: width
+ height: height
+ nr_channels: stb_img.nr_channels
+ ok: false
+ data: stb_img.data
+ ext: stb_img.ext
+ path: file
+ id: ctx.image_cache.len
+ }
+ ctx.image_cache << img
+ return img
+ }
+ mut img := create_image(file)
+ img.id = ctx.image_cache.len
+ ctx.image_cache << img
+ return img
+}
diff --git a/v_windows/v/vlib/gg/image.v b/v_windows/v/vlib/gg/image.v
new file mode 100644
index 0000000..e4c2776
--- /dev/null
+++ b/v_windows/v/vlib/gg/image.v
@@ -0,0 +1,223 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module gg
+
+// import sokol.sapp
+import gx
+import sokol.gfx
+import os
+import sokol
+import sokol.sgl
+import stbi
+
+[heap]
+pub struct Image {
+pub mut:
+ id int
+ width int
+ height int
+ nr_channels int
+ ok bool
+ data voidptr
+ ext string
+ simg_ok bool
+ simg C.sg_image
+ path string
+}
+
+// DrawImageConfig struct defines the various options
+// that can be used to draw an image onto the screen
+pub struct DrawImageConfig {
+pub:
+ flip_x bool
+ flip_y bool
+ img &Image = voidptr(0)
+ img_id int
+ img_rect Rect // defines the size and position on image when rendering to the screen
+ part_rect Rect // defines the size and position of part of the image to use when rendering
+ rotate int // amount to rotate the image in degrees
+ z f32
+ color gx.Color = gx.white
+}
+
+pub struct Rect {
+pub:
+ x f32
+ y f32
+ width f32
+ height f32
+}
+
+// TODO remove this
+fn create_image(file string) Image {
+ if !os.exists(file) {
+ println('gg.create_image(): file not found: $file')
+ return Image{} // none
+ }
+ stb_img := stbi.load(file) or { return Image{} }
+ mut img := Image{
+ width: stb_img.width
+ height: stb_img.height
+ nr_channels: stb_img.nr_channels
+ ok: stb_img.ok
+ data: stb_img.data
+ ext: stb_img.ext
+ path: file
+ }
+ img.init_sokol_image()
+ return img
+}
+
+pub fn (mut ctx Context) create_image_from_memory(buf &byte, bufsize int) Image {
+ stb_img := stbi.load_from_memory(buf, bufsize) or { return Image{} }
+ mut img := Image{
+ width: stb_img.width
+ height: stb_img.height
+ nr_channels: stb_img.nr_channels
+ ok: stb_img.ok
+ data: stb_img.data
+ ext: stb_img.ext
+ id: ctx.image_cache.len
+ }
+ ctx.image_cache << img
+ return img
+}
+
+pub fn (mut ctx Context) create_image_from_byte_array(b []byte) Image {
+ return ctx.create_image_from_memory(b.data, b.len)
+}
+
+pub fn (mut ctx Context) cache_image(img Image) int {
+ ctx.image_cache << img
+ image_idx := ctx.image_cache.len - 1
+ ctx.image_cache[image_idx].id = image_idx
+ return image_idx
+}
+
+pub fn (mut ctx Context) get_cached_image_by_idx(image_idx int) &Image {
+ return &ctx.image_cache[image_idx]
+}
+
+pub struct StreamingImageConfig {
+ pixel_format gfx.PixelFormat = .rgba8
+ wrap_u gfx.Wrap = .clamp_to_edge
+ wrap_v gfx.Wrap = .clamp_to_edge
+ min_filter gfx.Filter = .linear
+ mag_filter gfx.Filter = .linear
+ num_mipmaps int = 1
+ num_slices int = 1
+}
+
+// draw_image_with_config takes in a config that details how the
+// provided image should be drawn onto the screen
+pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) {
+ id := if !isnil(config.img) { config.img.id } else { config.img_id }
+ if id >= ctx.image_cache.len {
+ eprintln('gg: draw_image() bad img id $id (img cache len = $ctx.image_cache.len)')
+ return
+ }
+
+ img := ctx.image_cache[id]
+ if !img.simg_ok {
+ return
+ }
+
+ mut img_rect := config.img_rect
+ if img_rect.width == 0 && img_rect.height == 0 {
+ img_rect = Rect{img_rect.x, img_rect.y, img.width, img.height}
+ }
+
+ mut part_rect := config.part_rect
+ if part_rect.width == 0 && part_rect.height == 0 {
+ part_rect = Rect{part_rect.x, part_rect.y, img.width, img.height}
+ }
+
+ u0 := part_rect.x / img.width
+ v0 := part_rect.y / img.height
+ u1 := (part_rect.x + part_rect.width) / img.width
+ v1 := (part_rect.y + part_rect.height) / img.height
+ x0 := img_rect.x * ctx.scale
+ y0 := img_rect.y * ctx.scale
+ x1 := (img_rect.x + img_rect.width) * ctx.scale
+ mut y1 := (img_rect.y + img_rect.height) * ctx.scale
+ if img_rect.height == 0 {
+ scale := f32(img.width) / f32(img_rect.width)
+ y1 = f32(img_rect.y + int(f32(img.height) / scale)) * ctx.scale
+ }
+
+ flip_x := config.flip_x
+ flip_y := config.flip_y
+
+ mut u0f := if !flip_x { u0 } else { u1 }
+ mut u1f := if !flip_x { u1 } else { u0 }
+ mut v0f := if !flip_y { v0 } else { v1 }
+ mut v1f := if !flip_y { v1 } else { v0 }
+
+ sgl.load_pipeline(ctx.timage_pip)
+ sgl.enable_texture()
+ sgl.texture(img.simg)
+
+ if config.rotate != 0 {
+ width := img_rect.width * ctx.scale
+ height := (if img_rect.height > 0 { img_rect.height } else { img.height }) * ctx.scale
+
+ sgl.push_matrix()
+ sgl.translate(x0 + (width / 2), y0 + (height / 2), 0)
+ sgl.rotate(sgl.rad(-config.rotate), 0, 0, 1)
+ sgl.translate(-x0 - (width / 2), -y0 - (height / 2), 0)
+ }
+
+ sgl.begin_quads()
+ sgl.c4b(config.color.r, config.color.g, config.color.b, config.color.a)
+ sgl.v3f_t2f(x0, y0, config.z, u0f, v0f)
+ sgl.v3f_t2f(x1, y0, config.z, u1f, v0f)
+ sgl.v3f_t2f(x1, y1, config.z, u1f, v1f)
+ sgl.v3f_t2f(x0, y1, config.z, u0f, v1f)
+ sgl.end()
+
+ if config.rotate != 0 {
+ sgl.pop_matrix()
+ }
+
+ sgl.disable_texture()
+}
+
+// Draw part of an image using uv coordinates
+// img_rect is the size and position (in pixels on screen) of the displayed rectangle (ie the draw_image args)
+// part_rect is the size and position (in absolute pixels in the image) of the wanted part
+// eg. On a 600*600 context, to display only the first 400*400 pixels of a 2000*2000 image
+// on the entire context surface, call :
+// draw_image_part(Rect{0, 0, 600, 600}, Rect{0, 0, 400, 400}, img)
+pub fn (ctx &Context) draw_image_part(img_rect Rect, part_rect Rect, img_ &Image) {
+ ctx.draw_image_with_config(
+ img: img_
+ img_rect: img_rect
+ part_rect: part_rect
+ )
+}
+
+// draw_image_flipped draws the provided image flipped horizontally (use `draw_image_with_config` to flip vertically)
+pub fn (ctx &Context) draw_image_flipped(x f32, y f32, width f32, height f32, img_ &Image) {
+ ctx.draw_image_with_config(
+ flip_x: true
+ img: img_
+ img_rect: Rect{x, y, width, height}
+ )
+}
+
+// draw_image_by_id draws an image by its id
+pub fn (ctx &Context) draw_image_by_id(x f32, y f32, width f32, height f32, id int) {
+ ctx.draw_image_with_config(
+ img_id: id
+ img_rect: Rect{x, y, width, height}
+ )
+}
+
+// draw_image_3d draws an image with a z depth
+pub fn (ctx &Context) draw_image_3d(x f32, y f32, z f32, width f32, height f32, img_ &Image) {
+ ctx.draw_image_with_config(
+ img: img_
+ img_rect: Rect{x, y, width, height}
+ z: z
+ )
+}
diff --git a/v_windows/v/vlib/gg/m4/graphic.v b/v_windows/v/vlib/gg/m4/graphic.v
new file mode 100644
index 0000000..e134e80
--- /dev/null
+++ b/v_windows/v/vlib/gg/m4/graphic.v
@@ -0,0 +1,110 @@
+/**********************************************************************
+*
+* Simply vector/matrix graphic utility
+*
+* Copyright (c) 2021 Dario Deledda. All rights reserved.
+* Use of this source code is governed by an MIT license
+* that can be found in the LICENSE file.
+*
+* TODO:
+**********************************************************************/
+module m4
+
+import math
+
+// Translate degrees to radians
+[inline]
+pub fn rad(deg f32) f32 {
+ return (math.pi / 180.0) * deg
+}
+
+// Translate radians to degrees
+[inline]
+pub fn deg(grad f32) f32 {
+ return (180.0 / math.pi) * grad
+}
+
+// calculate the Orthographic projection matrix
+pub fn ortho(left f32, right f32, bottom f32, top f32, z_near f32, z_far f32) Mat4 {
+ rml := right - left
+ rpl := right + left
+ tmb := top - bottom
+ tpb := top + bottom
+ fmn := z_far - z_near
+ fpn := z_far + z_near
+ if fmn != 0 {
+ return Mat4{ e: [
+ 2 / rml, 0 , 0, -(rpl / rml),
+ 0 , 2 / tmb, 0, -(tpb / tmb),
+ 0 , 0, 2 / fmn, -(fpn / fmn),
+ 0 , 0, 0, 1,
+ ]!
+ }
+ }
+ return Mat4{ e: [
+ 2 / rml, 0 , 0, -(rpl / rml),
+ 0 , 2 / tmb, 0, -(tpb / tmb),
+ 0 , 0, 0, 0,
+ 0 , 0, 0, 1,
+ ]!
+ }
+}
+
+// Calculate the perspective matrix using (fov:fov, ar:aspect_ratio ,n:near_pane, f:far_plane) as parameters
+pub fn perspective(fov f32, ar f32, n f32, f f32) Mat4 {
+ ctan := f32(1.0 / math.tan(fov * (f32(math.pi) / 360.0))) // for the FOV we use 360 instead 180
+ return Mat4{ e: [
+ ctan / ar, 0, 0, 0,
+ 0, ctan, 0, 0,
+ 0, 0, (n + f) / (n - f), -1.0,
+ 0, 0, (2.0 * n * f) / (n - f), 0,
+ ]!
+ }
+}
+
+// Calculate the look-at matrix
+pub fn look_at(eye Vec4, center Vec4, up Vec4) Mat4 {
+ f := (center - eye).normalize3()
+ s := (f % up).normalize3()
+ u := (s % f)
+
+ return Mat4{ e: [
+ /* [0][0] */ s.e[0],
+ /* [0][1] */ u.e[0],
+ /* [0][2] */ - f.e[0],
+ /* [0][3] */ 0,
+
+ /* [1][1] */ s.e[1],
+ /* [1][1] */ u.e[1],
+ /* [1][2] */ - f.e[1],
+ /* [1][3] */ 0,
+
+ /* [2][0] */ s.e[2],
+ /* [2][1] */ u.e[2],
+ /* [2][2] */ - f.e[2],
+ /* [2][3] */ 0,
+
+ /* [3][0] */ - (s * eye),
+ /* [3][1] */ - (u * eye),
+ /* [3][2] */ f * eye,
+ /* [3][3] */ 1,
+ ]!
+ }
+}
+
+
+// Get the complete transformation matrix for GLSL demos
+pub fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) Mat4 {
+ proj := perspective(60, w / h, 0.01, 10.0)
+ view := look_at(Vec4{ e: [f32(0.0), 1.5, 6, 0]! }, Vec4{ e: [f32(0), 0, 0, 0]! }, Vec4{ e: [f32(0), 1.0, 0, 0]! })
+ view_proj := view * proj
+
+ rxm := rotate(rad(rx), Vec4{ e: [f32(1), 0, 0, 0]! })
+ rym := rotate(rad(ry), Vec4{ e: [f32(0), 1, 0, 0]! })
+
+ model := rym * rxm
+ scale_m := scale(Vec4{ e: [in_scale, in_scale, in_scale, 1]! })
+
+ res := (scale_m * model) * view_proj
+ return res
+}
diff --git a/v_windows/v/vlib/gg/m4/m4_test.v b/v_windows/v/vlib/gg/m4/m4_test.v
new file mode 100644
index 0000000..90b4640
--- /dev/null
+++ b/v_windows/v/vlib/gg/m4/m4_test.v
@@ -0,0 +1,235 @@
+import gg.m4
+
+pub fn test_m4() {
+ unsafe {
+ // Test Mat4
+ mut a := m4.Mat4{ e: [
+ f32(0), 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 12, 13, 14, 15,
+ ]!
+ }
+ mut b := m4.Mat4{}
+ mut c := m4.Mat4{}
+
+ // equal test
+ assert a.e == [
+ f32(0), 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 12, 13, 14, 15,
+ ]!
+
+ // copy test
+ b.copy(a)
+ assert a.e == b.e
+
+ // test: transpose, scale
+ assert b.transpose().mul_scalar(2.0).mul_scalar(0.5).transpose().e == a.e
+ assert b.sum_all() == 120.0
+
+ // test rows/columns set/get
+ for i in 0 .. 4 {
+ b = m4.zero_m4()
+ b.set_row(i, m4.Vec4{ e: [f32(1.0), 2, 3, 4]! })
+ assert b.get_f(0, i) == 1.0
+ assert b.get_f(1, i) == 2.0
+ assert b.get_f(2, i) == 3.0
+ assert b.get_f(3, i) == 4.0
+ // println(b)
+ c = m4.zero_m4()
+ c.set_col(i, m4.Vec4{ e: [f32(1.0), 2, 3, 4]! })
+ assert c.get_f(i, 0) == 1.0
+ assert c.get_f(i, 1) == 2.0
+ assert c.get_f(i, 2) == 3.0
+ assert c.get_f(i, 3) == 4.0
+ // println(c)
+ }
+ }
+}
+
+fn test_swap_col_row() {
+ unsafe {
+ // swap_col / swap_row
+ b := m4.Mat4{ e: [
+ f32(1), 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16,
+ ]!
+ }
+ b.swap_col(0, 2)
+ assert b.e == [
+ f32(3), 2, 1, 4,
+ 7, 6, 5, 8,
+ 11, 10, 9, 12,
+ 15, 14, 13, 16,
+ ]!
+ b = m4.Mat4{ e: [
+ f32(1), 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16,
+ ]!
+ }
+ b.swap_row(0, 2)
+ assert b.e == [
+ f32(9), 10, 11, 12,
+ 5, 6, 7, 8,
+ 1, 2, 3, 4,
+ 13, 14, 15, 16,
+ ]!
+ }
+}
+
+fn test_sum_sub() {
+ unsafe {
+ // test sum/sub
+ b := m4.unit_m4()
+ c := m4.unit_m4()
+ assert m4.sub(m4.add(b, c), b).e == m4.unit_m4().e
+ assert (b + c - b).e == m4.unit_m4().e
+ }
+}
+
+fn test_transpose() {
+ unsafe {
+ b := m4.Mat4{ e: [
+ f32(0), 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 12, 13, 14, 15,
+ ]!
+ }
+ assert b.transpose().transpose().e == b.e
+ }
+}
+
+fn test_multiplication() {
+ unsafe {
+ b := m4.Mat4{ e: [
+ f32(1), 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 3, 0,
+ 0, 0, 0, 4,
+ ]!
+ }
+ c := m4.Mat4{ e: [
+ f32(1), 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 13, 14, 15, 16,
+ ]!
+ }
+
+ assert (c * c).e == [
+ f32(90),100,110,120,
+ 202,228,254,280,
+ 314,356,398,440,
+ 426,484,542,600,
+ ]!
+
+ assert m4.mul(c, c).e == [
+ f32(90),100,110,120,
+ 202,228,254,280,
+ 314,356,398,440,
+ 426,484,542,600,
+ ]!
+
+ assert m4.mul(b, c).e == [
+ f32(1), 2, 3, 4,
+ 10, 12, 14, 16,
+ 27, 30, 33, 36,
+ 52, 56, 60, 64,
+ ]!
+
+ assert (b * c).e == [
+ f32(1), 2, 3, 4,
+ 10, 12, 14, 16,
+ 27, 30, 33, 36,
+ 52, 56, 60, 64,
+ ]!
+
+ assert m4.det(b) == 24
+ }
+}
+
+fn test_det() {
+ unsafe {
+ b := m4.Mat4{ e: [
+ f32(5), 6, 6, 8,
+ 2, 2, 2, 8,
+ 6, 6, 2, 8,
+ 2, 3, 6, 7,
+ ]!
+ }
+ assert m4.det(b) == -8
+
+ c := m4.Mat4{ e: [
+ f32(1), 8, 2, 3,
+ 8, 2, 3, 1,
+ 2, 3, 3, 2,
+ 3, 1, 2, 4,
+ ]!
+ }
+ // println("*** INVERSE ****")
+ // println(m4.mul(b.inverse(),b))
+ // println(m4.clean_small(m4.mul(c.inverse(),c)))
+ // println("****************")
+ assert m4.mul(b.inverse(), b).e == m4.unit_m4().e
+ assert m4.mul(c.inverse(), c).is_equal(m4.unit_m4())
+ }
+}
+
+fn test_vec4() {
+ // Test Vec4
+ // println("*** Vector4 ****")
+ assert m4.vec3(1,2,3) == m4.Vec4{[f32(1), 2, 3, 1]!}
+ mut v := m4.Vec4{[f32(1), 2, 3, 4]!}
+ assert v * v.inv() == 4
+ assert v.mul_scalar(1.0 / v.mod()).mod() == 1
+ assert v + m4.Vec4{ e: [f32(5), 6, 7, 8]! } == m4.Vec4{ e: [f32(6), 8, 10, 12]! }
+ assert v - m4.Vec4{ e: [f32(1), 2, 3, 4]! } == m4.Vec4{ e: [f32(0), 0, 0, 0]! }
+ assert v.mul_vec4(m4.Vec4{ e: [f32(2), 2, 2, 2]! }) == m4.Vec4{ e: [f32(2), 4, 6, 8]! }
+ assert f32_abs(v.normalize().mod() - 1) < m4.precision
+ v = m4.Vec4{[f32(1), 2, 3, 0]!}
+ assert f32_abs(v.normalize3().mod3() - 1) < m4.precision
+ assert f32_abs(v.normalize3().mod() - 1) < m4.precision
+ // cross product
+ // x y z
+ // 1 2 3 ==> -3 6 -3 0
+ // 4 5 6
+ // println(m4.Vec4{[f32(1),2,3,2]!} % m4.Vec4{[f32(4),5,6,2]!})
+ assert m4.Vec4{[f32(1), 2, 3, 0]!} % m4.Vec4{[f32(4), 5, 6, 0]!} == m4.Vec4{[ f32(-3), 6, -3, 0, ]!}
+ assert m4.Vec4{[f32(1), 2, 3, 13]!} % m4.Vec4{[f32(4), 5, 6, 11]!} == m4.Vec4{[ f32(-3), 6, -3, 0, ]!}
+ // matrix * vector
+ a := m4.Mat4{ e: [
+ f32(1),2,3,4
+ 5,6,7,8
+ 9,10,11,12
+ 13,14,15,16
+ ]!
+ }
+ assert m4.mul_vec(a, m4.Vec4{[f32(1), 2, 3, 4]!}) == m4.Vec4{[ f32(30), 70, 110,150, ]!}
+ // Rotation
+ // println("*** Rotation ****")
+ rotx := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(1.0), 0, 0, 0]! }).clean()
+ roty := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(0), 1.0, 0, 0]! }).clean()
+ rotz := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(0), 0, 1, 0]! }).clean()
+ // println( rotx )
+ // println( roty )
+ // println( rotz )
+ // println( m4.mul_vec(rotx, m4.Vec4{e:[f32(0),0,1,0]!}).clean())
+ assert m4.mul_vec(roty, m4.Vec4{ e: [f32(1.0), 0.0, 0, 0]! }).clean() == m4.Vec4{ e: [f32(0), 0.0, -1, 0]! }
+ assert m4.mul_vec(rotz, m4.Vec4{ e: [f32(1.0), 0.0, 0, 0]! }).clean() == m4.Vec4{ e: [f32(0), 1, 0, 0]! }
+ assert m4.mul_vec(rotx, m4.Vec4{ e: [f32(0), 0, 1, 0]! }).clean() == m4.Vec4{ e: [f32(0), -1, 0, 0]! }
+ // println("****************")
+}
+
+fn test_proj() {
+ ort := m4.ortho(0,300,0,200,0,0)
+ assert m4.mul_vec(ort, m4.Vec4{[ f32(150), 100, 0, 1]!}) == m4.Vec4{[ f32(0), 0, 0, 1]!}
+ assert m4.mul_vec(ort, m4.Vec4{[ f32(0), 0, 0, 1]!}) == m4.Vec4{[ f32(-1), -1, 0, 1]!}
+ assert m4.mul_vec(ort, m4.Vec4{[ f32(300), 200, 0, 1]!}) == m4.Vec4{[ f32(1), 1, 0, 1]!}
+}
diff --git a/v_windows/v/vlib/gg/m4/matrix.v b/v_windows/v/vlib/gg/m4/matrix.v
new file mode 100644
index 0000000..c839b9d
--- /dev/null
+++ b/v_windows/v/vlib/gg/m4/matrix.v
@@ -0,0 +1,595 @@
+/**********************************************************************
+*
+* Simply vector/matrix utility
+*
+* Copyright (c) 2021 Dario Deledda. All rights reserved.
+* Use of this source code is governed by an MIT license
+* that can be found in the LICENSE file.
+*
+* TODO:
+**********************************************************************/
+module m4
+
+import math
+
+pub union Mat4 {
+pub mut:
+ e [16]f32
+ f [4][4]f32
+}
+
+pub const precision = f32(10e-7)
+
+// default precision for the module
+
+/*********************************************************************
+*
+* Utility
+*
+*********************************************************************/
+// String representation of the matrix
+pub fn (x Mat4) str() string {
+ unsafe {
+ return '|${x.e[0]:-6.3},${x.e[1]:-6.3},${x.e[2]:-6.3},${x.e[3]:-6.3}|\n' +
+ '|${x.e[4]:-6.3},${x.e[5]:-6.3},${x.e[6]:-6.3},${x.e[7]:-6.3}|\n' +
+ '|${x.e[8]:-6.3},${x.e[9]:-6.3},${x.e[10]:-6.3},${x.e[11]:-6.3}|\n' +
+ '|${x.e[12]:-6.3},${x.e[13]:-6.3},${x.e[14]:-6.3},${x.e[15]:-6.3}|'
+ }
+}
+
+// Remove all the raw zeros
+[direct_array_access]
+pub fn (a Mat4) clean() Mat4 {
+ unsafe {
+ x := Mat4{}
+ for c, value in a.e {
+ if f32_abs(value) < m4.precision {
+ x.e[c] = 0
+ } else {
+ x.e[c] = value
+ }
+ }
+ return x
+ }
+}
+
+// Sum all the elements of the matrix
+pub fn (x Mat4) sum_all() f32 {
+ mut res := f32(0)
+ for v in unsafe { x.e } {
+ res += v
+ }
+ return res
+}
+
+// Check if two matrix are equal using module precision
+[direct_array_access]
+pub fn (x Mat4) is_equal(y Mat4) bool {
+ unsafe {
+ for c, value in x.e {
+ if f32_abs(value - y.e[c]) > m4.precision {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+//-------------------------------------
+// Set/Get values
+//-------------------------------------
+// Get an element of the matrix using [0..15] indexes, one dimension
+pub fn (x Mat4) get_e(elem_index int) f32 {
+ unsafe {
+ return x.e[elem_index]
+ }
+}
+
+// Get an element of the matrix using [0..3][0..3] indexes, two dimension
+pub fn (x Mat4) get_f(index_col int, index_row int) f32 {
+ unsafe {
+ return x.e[(index_row << 2) + index_col]
+ }
+}
+
+// Set an element of the matrix using [0..15] indexes, one dimension
+pub fn (mut x Mat4) set_e(index int, value f32) {
+ unsafe {
+ x.e[index] = value
+ }
+}
+
+// Set an element of the matrix using [0..3][0..3] indexes, two dimension
+pub fn (mut x Mat4) set_f(index_col int, index_row int, value f32) {
+ unsafe {
+ x.e[(index_row << 2) + index_col] = value
+ }
+}
+
+// Copy a matrix elements from another matrix
+pub fn (mut x Mat4) copy(y Mat4) {
+ unsafe {
+ x.e = [
+ y.e[0 ], y.e[1 ], y.e[2 ], y.e[3 ],
+ y.e[4 ], y.e[5 ], y.e[6 ], y.e[7 ],
+ y.e[8 ], y.e[9 ], y.e[10], y.e[11],
+ y.e[12], y.e[13], y.e[14], y.e[15],
+ ]!
+ }
+}
+
+// Set the trace of the matrix using a vec4
+pub fn (mut x Mat4) set_trace(v3 Vec4) {
+ unsafe {
+ x.e[0 ] = v3.e[0]
+ x.e[5 ] = v3.e[1]
+ x.e[10] = v3.e[2]
+ x.e[15] = v3.e[3]
+ }
+}
+
+// Get the trace of the matrix
+pub fn (x Mat4) get_trace() Vec4 {
+ unsafe {
+ return Vec4{ e: [ x.e[0], x.e[5], x.e[10], x.e[15], ]! }
+ }
+}
+
+// Set all the matrix elements to value
+pub fn (mut x Mat4) set_f32(value f32) {
+ unsafe {
+ x.e = [
+ value, value, value, value,
+ value, value, value, value,
+ value, value, value, value,
+ value, value, value, value,
+ ]!
+ }
+}
+
+//-------------------------------------
+// Rows/Column access
+//-------------------------------------
+// Set the row as the input vec4
+[direct_array_access]
+[unsafe]
+pub fn (mut x Mat4) set_row(row int, v3 Vec4) {
+ unsafe {
+ x.e[row * 4 + 0] = v3.e[0]
+ x.e[row * 4 + 1] = v3.e[1]
+ x.e[row * 4 + 2] = v3.e[2]
+ x.e[row * 4 + 3] = v3.e[3]
+ }
+}
+
+// Get a row from a matrix
+[direct_array_access]
+[unsafe]
+pub fn (x Mat4) get_row(row int) Vec4 {
+ unsafe {
+ return Vec4{
+ e: [
+ x.e[row * 4 + 0],
+ x.e[row * 4 + 1],
+ x.e[row * 4 + 2],
+ x.e[row * 4 + 3],
+ ]!
+ }
+ }
+}
+
+// Set the column as the input vec4
+[direct_array_access]
+[unsafe]
+pub fn (mut x Mat4) set_col(col int, v3 Vec4) {
+ unsafe {
+ x.e[col] = v3.e[0]
+ x.e[col + 4 ] = v3.e[1]
+ x.e[col + 8 ] = v3.e[2]
+ x.e[col + 12] = v3.e[3]
+ }
+}
+
+// Get a column from a matrix
+[direct_array_access]
+[unsafe]
+pub fn (x Mat4) get_col(col int) Vec4 {
+ unsafe {
+ return Vec4{
+ e: [
+ x.e[col],
+ x.e[col + 4 ],
+ x.e[col + 8 ],
+ x.e[col + 12],
+ ]!
+ }
+ }
+}
+
+// Swap two columns in the matrix
+[direct_array_access]
+[unsafe]
+pub fn (mut x Mat4) swap_col(col1 int, col2 int) {
+ unsafe {
+ v0 := x.e[col1]
+ v1 := x.e[col1 + 4 ]
+ v2 := x.e[col1 + 8 ]
+ v3 := x.e[col1 + 12]
+
+ x.e[col1] = x.e[col2]
+ x.e[col1 + 4 ] = x.e[col2 + 4 ]
+ x.e[col1 + 8 ] = x.e[col2 + 8 ]
+ x.e[col1 + 12] = x.e[col2 + 12]
+
+ x.e[col2] = v0
+ x.e[col2 + 4 ] = v1
+ x.e[col2 + 8 ] = v2
+ x.e[col2 + 12] = v3
+ }
+}
+
+// Swap two rows in the matrix
+[direct_array_access]
+[unsafe]
+pub fn (mut x Mat4) swap_row(row1 int, row2 int) {
+ unsafe {
+ v0 := x.e[row1 * 4 + 0]
+ v1 := x.e[row1 * 4 + 1]
+ v2 := x.e[row1 * 4 + 2]
+ v3 := x.e[row1 * 4 + 3]
+
+ x.e[row1 * 4 + 0] = x.e[row2 * 4 + 0]
+ x.e[row1 * 4 + 1] = x.e[row2 * 4 + 1]
+ x.e[row1 * 4 + 2] = x.e[row2 * 4 + 2]
+ x.e[row1 * 4 + 3] = x.e[row2 * 4 + 3]
+
+ x.e[row2 * 4 + 0] = v0
+ x.e[row2 * 4 + 1] = v1
+ x.e[row2 * 4 + 2] = v2
+ x.e[row2 * 4 + 3] = v3
+ }
+}
+
+//-------------------------------------
+// Modify data
+//-------------------------------------
+// Transpose the matrix
+pub fn (x Mat4) transpose() Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ x.e[0 ], x.e[4 ], x.e[8 ], x.e[12],
+ x.e[1 ], x.e[5 ], x.e[9 ], x.e[13],
+ x.e[2 ], x.e[6 ], x.e[10], x.e[14],
+ x.e[3 ], x.e[7 ], x.e[11], x.e[15],
+ ]!
+ }
+ }
+}
+
+// Multiply the all the elements of the matrix by a scalar
+pub fn (x Mat4) mul_scalar(s f32) Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ x.e[0 ] * s, x.e[1 ] * s, x.e[2 ] * s, x.e[3 ] * s,
+ x.e[4 ] * s, x.e[5 ] * s, x.e[6 ] * s, x.e[7 ] * s,
+ x.e[8 ] * s, x.e[9 ] * s, x.e[10] * s, x.e[11] * s,
+ x.e[12] * s, x.e[13] * s, x.e[14] * s, x.e[15] * s,
+ ]!
+ }
+ }
+}
+
+/*********************************************************************
+*
+* Init/set
+*
+*********************************************************************/
+// Return a zero matrix
+pub fn zero_m4() Mat4 {
+ return Mat4{ e: [
+ f32(0), 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]!
+ }
+}
+
+// Return a unity matrix
+pub fn unit_m4() Mat4 {
+ return Mat4{ e: [
+ f32(1), 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ ]!
+ }
+}
+
+// Return a matrix initialized with value
+pub fn set_m4(value f32) Mat4 {
+ return Mat4{ e: [
+ value, value, value, value,
+ value, value, value, value,
+ value, value, value, value,
+ value, value, value, value,
+ ]!
+ }
+}
+
+/*********************************************************************
+*
+* Math
+*
+*********************************************************************/
+
+// Sum of matrix, operator +
+pub fn (a Mat4) + (b Mat4) Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ a.e[0 ] + b.e[0 ], a.e[1 ] + b.e[1 ], a.e[2 ] + b.e[2 ], a.e[3 ] + b.e[3 ],
+ a.e[4 ] + b.e[4 ], a.e[5 ] + b.e[5 ], a.e[6 ] + b.e[6 ], a.e[7 ] + b.e[7 ],
+ a.e[8 ] + b.e[8 ], a.e[9 ] + b.e[9 ], a.e[10] + b.e[10], a.e[11] + b.e[11],
+ a.e[12] + b.e[12], a.e[13] + b.e[13], a.e[14] + b.e[14], a.e[15] + b.e[15],
+ ]!
+ }
+ }
+}
+
+// Subtraction of matrix, operator -
+pub fn (a Mat4) - (b Mat4) Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ a.e[0 ] - b.e[0 ], a.e[1 ] - b.e[1 ], a.e[2 ] - b.e[2 ], a.e[3 ] - b.e[3 ],
+ a.e[4 ] - b.e[4 ], a.e[5 ] - b.e[5 ], a.e[6 ] - b.e[6 ], a.e[7 ] - b.e[7 ],
+ a.e[8 ] - b.e[8 ], a.e[9 ] - b.e[9 ], a.e[10] - b.e[10], a.e[11] - b.e[11],
+ a.e[12] - b.e[12], a.e[13] - b.e[13], a.e[14] - b.e[14], a.e[15] - b.e[15],
+ ]!
+ }
+ }
+}
+
+// Multiplication of matrix, operator *
+pub fn (a Mat4) * (b Mat4) Mat4 {
+ unsafe {
+ return Mat4{
+ e: [
+ /* [0][0] */ a.f[0][0] * b.f[0][0] + a.f[0][1] * b.f[1][0] + a.f[0][2] * b.f[2][0] + a.f[0][3] * b.f[3][0]
+ /* [0][1] */, a.f[0][0] * b.f[0][1] + a.f[0][1] * b.f[1][1] + a.f[0][2] * b.f[2][1] + a.f[0][3] * b.f[3][1]
+ /* [0][2] */, a.f[0][0] * b.f[0][2] + a.f[0][1] * b.f[1][2] + a.f[0][2] * b.f[2][2] + a.f[0][3] * b.f[3][2]
+ /* [0][3] */, a.f[0][0] * b.f[0][3] + a.f[0][1] * b.f[1][3] + a.f[0][2] * b.f[2][3] + a.f[0][3] * b.f[3][3]
+
+ /* [1][0] */, a.f[1][0] * b.f[0][0] + a.f[1][1] * b.f[1][0] + a.f[1][2] * b.f[2][0] + a.f[1][3] * b.f[3][0]
+ /* [1][1] */, a.f[1][0] * b.f[0][1] + a.f[1][1] * b.f[1][1] + a.f[1][2] * b.f[2][1] + a.f[1][3] * b.f[3][1]
+ /* [1][2] */, a.f[1][0] * b.f[0][2] + a.f[1][1] * b.f[1][2] + a.f[1][2] * b.f[2][2] + a.f[1][3] * b.f[3][2]
+ /* [1][3] */, a.f[1][0] * b.f[0][3] + a.f[1][1] * b.f[1][3] + a.f[1][2] * b.f[2][3] + a.f[1][3] * b.f[3][3]
+
+ /* [2][0] */, a.f[2][0] * b.f[0][0] + a.f[2][1] * b.f[1][0] + a.f[2][2] * b.f[2][0] + a.f[2][3] * b.f[3][0]
+ /* [2][1] */, a.f[2][0] * b.f[0][1] + a.f[2][1] * b.f[1][1] + a.f[2][2] * b.f[2][1] + a.f[2][3] * b.f[3][1]
+ /* [2][2] */, a.f[2][0] * b.f[0][2] + a.f[2][1] * b.f[1][2] + a.f[2][2] * b.f[2][2] + a.f[2][3] * b.f[3][2]
+ /* [2][3] */, a.f[2][0] * b.f[0][3] + a.f[2][1] * b.f[1][3] + a.f[2][2] * b.f[2][3] + a.f[2][3] * b.f[3][3]
+
+ /* [3][0] */, a.f[3][0] * b.f[0][0] + a.f[3][1] * b.f[1][0] + a.f[3][2] * b.f[2][0] + a.f[3][3] * b.f[3][0]
+ /* [3][1] */, a.f[3][0] * b.f[0][1] + a.f[3][1] * b.f[1][1] + a.f[3][2] * b.f[2][1] + a.f[3][3] * b.f[3][1]
+ /* [3][2] */, a.f[3][0] * b.f[0][2] + a.f[3][1] * b.f[1][2] + a.f[3][2] * b.f[2][2] + a.f[3][3] * b.f[3][2]
+ /* [3][3] */, a.f[3][0] * b.f[0][3] + a.f[3][1] * b.f[1][3] + a.f[3][2] * b.f[2][3] + a.f[3][3] * b.f[3][3],
+ ]!
+ }
+ }
+}
+
+// Sum of matrix function
+pub fn add(a Mat4, b Mat4) Mat4 {
+ unsafe {
+ return a + b
+ }
+}
+
+// Subtraction of matrix function
+pub fn sub(a Mat4, b Mat4) Mat4 {
+ unsafe {
+ return a - b
+ }
+}
+
+// Multiplication of matrix function
+pub fn mul(a Mat4, b Mat4) Mat4 {
+ unsafe {
+ return a * b
+ }
+}
+
+// Multiply a Matrix by a vector
+pub fn mul_vec(a Mat4, v Vec4) Vec4 {
+ unsafe {
+ return Vec4{ e: [
+ a.e[0 ] * v.e[0] + a.e[1 ] * v.e[1] + a.e[2 ] * v.e[2] + a.e[3 ] * v.e[3],
+ a.e[4 ] * v.e[0] + a.e[5 ] * v.e[1] + a.e[6 ] * v.e[2] + a.e[7 ] * v.e[3],
+ a.e[8 ] * v.e[0] + a.e[9 ] * v.e[1] + a.e[10] * v.e[2] + a.e[11] * v.e[3],
+ a.e[12] * v.e[0] + a.e[13] * v.e[1] + a.e[14] * v.e[2] + a.e[15] * v.e[3],
+ ]!
+ }
+ }
+}
+
+// Calculate the determinant of the Matrix
+pub fn det(x Mat4) f32 {
+ unsafe {
+ mut t := [6]f32{}
+ x00 := x.f[0][0]
+ x10 := x.f[1][0]
+ x20 := x.f[2][0]
+ x30 := x.f[3][0]
+ x01 := x.f[0][1]
+ x11 := x.f[1][1]
+ x21 := x.f[2][1]
+ x31 := x.f[3][1]
+ x02 := x.f[0][2]
+ x12 := x.f[1][2]
+ x22 := x.f[2][2]
+ x32 := x.f[3][2]
+ x03 := x.f[0][3]
+ x13 := x.f[1][3]
+ x23 := x.f[2][3]
+ x33 := x.f[3][3]
+
+ t[0] = x22 * x33 - x23 * x32
+ t[1] = x12 * x33 - x13 * x32
+ t[2] = x12 * x23 - x13 * x22
+ t[3] = x02 * x33 - x03 * x32
+ t[4] = x02 * x23 - x03 * x22
+ t[5] = x02 * x13 - x03 * x12
+
+ return 0.0 +
+ x00 * (x11 * t[0] - x21 * t[1] + x31 * t[2]) -
+ x10 * (x01 * t[0] - x21 * t[3] + x31 * t[4]) +
+ x20 * (x01 * t[1] - x11 * t[3] + x31 * t[5]) -
+ x30 * (x01 * t[2] - x11 * t[4] + x21 * t[5])
+ }
+}
+
+// Calculate the inverse of the Matrix
+pub fn (x Mat4) inverse() Mat4 {
+ unsafe {
+ mut t := [6]f32{}
+ mut det := f32(0)
+
+ a := x.f[0][0]
+ b := x.f[1][0]
+ c := x.f[2][0]
+ d := x.f[3][0]
+ e := x.f[0][1]
+ f := x.f[1][1]
+ g := x.f[2][1]
+ h := x.f[3][1]
+ i := x.f[0][2]
+ j := x.f[1][2]
+ k := x.f[2][2]
+ l := x.f[3][2]
+ m := x.f[0][3]
+ n := x.f[1][3]
+ o := x.f[2][3]
+ p := x.f[3][3]
+
+ t[0] = k * p - o * l
+ t[1] = j * p - n * l
+ t[2] = j * o - n * k
+ t[3] = i * p - m * l
+ t[4] = i * o - m * k
+ t[5] = i * n - m * j
+
+ mut dest := Mat4{}
+ dest.f[0][0] = f * t[0] - g * t[1] + h * t[2]
+ dest.f[0][1] = -(e * t[0] - g * t[3] + h * t[4])
+ dest.f[0][2] = e * t[1] - f * t[3] + h * t[5]
+ dest.f[0][3] = -(e * t[2] - f * t[4] + g * t[5])
+
+ dest.f[1][0] = -(b * t[0] - c * t[1] + d * t[2])
+ dest.f[1][1] = a * t[0] - c * t[3] + d * t[4]
+ dest.f[1][2] = -(a * t[1] - b * t[3] + d * t[5])
+ dest.f[1][3] = a * t[2] - b * t[4] + c * t[5]
+
+ t[0] = g * p - o * h
+ t[1] = f * p - n * h
+ t[2] = f * o - n * g
+ t[3] = e * p - m * h
+ t[4] = e * o - m * g
+ t[5] = e * n - m * f
+
+ dest.f[2][0] = b * t[0] - c * t[1] + d * t[2]
+ dest.f[2][1] = -(a * t[0] - c * t[3] + d * t[4])
+ dest.f[2][2] = a * t[1] - b * t[3] + d * t[5]
+ dest.f[2][3] = -(a * t[2] - b * t[4] + c * t[5])
+
+ t[0] = g * l - k * h
+ t[1] = f * l - j * h
+ t[2] = f * k - j * g
+ t[3] = e * l - i * h
+ t[4] = e * k - i * g
+ t[5] = e * j - i * f
+
+ dest.f[3][0] = -(b * t[0] - c * t[1] + d * t[2])
+ dest.f[3][1] = a * t[0] - c * t[3] + d * t[4]
+ dest.f[3][2] = -(a * t[1] - b * t[3] + d * t[5])
+ dest.f[3][3] = a * t[2] - b * t[4] + c * t[5]
+
+ tmp := (a * dest.f[0][0] + b * dest.f[0][1] + c * dest.f[0][2] + d * dest.f[0][3])
+ if tmp != 0 {
+ det = f32(1.0) / tmp
+ }
+ return dest.mul_scalar(det)
+ }
+}
+
+/*********************************************************************
+*
+* Transformations
+*
+*********************************************************************/
+
+// Get a rotation matrix using w as rotation axis vector, the angle is in radians
+pub fn rotate(angle f32, w Vec4) Mat4 {
+ cs := f32(math.cos(angle))
+ sn := f32(math.sin(angle))
+ cv := f32(1.0) - cs
+ axis := w.normalize3()
+ unsafe {
+ ax := axis.e[0]
+ ay := axis.e[1]
+ az := axis.e[2]
+
+ return Mat4{ e: [
+ /* [0][0] */ (ax * ax * cv) + cs
+ /* [0][1] */, (ax * ay * cv) + az * sn
+ /* [0][2] */, (ax * az * cv) - ay * sn
+ /* [0][3] */, 0
+
+ /* [1][0] */, (ay * ax * cv) - az * sn
+ /* [1][1] */, (ay * ay * cv) + cs
+ /* [1][2] */, (ay * az * cv) + ax * sn
+ /* [1][3] */, 0
+
+ /* [2][0] */, (az * ax * cv) + ay * sn
+ /* [2][1] */, (az * ay * cv) - ax * sn
+ /* [2][2] */, (az * az * cv) + cs
+ /* [2][3] */, 0
+
+ /* [3][0] */, 0
+ /* [3][1] */, 0
+ /* [3][2] */, 0
+ /* [3][3] */, 1,
+ ]!
+ }
+ }
+}
+
+/*********************************************************************
+*
+* Graphic
+*
+*********************************************************************/
+// Get a matrix translated by a vector w
+pub fn (x Mat4) translate(w Vec4) Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ x.e[0], x.e[1], x.e[2 ], x.e[3 ] ,
+ x.e[4], x.e[5], x.e[6 ], x.e[7 ] ,
+ x.e[8], x.e[9], x.e[10], x.e[11] ,
+ x.e[12] + w.e[0], x.e[13] + w.e[1], x.e[14] + w.e[2], x.e[15],
+ ]!
+ }
+ }
+}
+
+// Get a scale matrix, the scale vector is w, only xyz are evaluated.
+pub fn scale(w Vec4) Mat4 {
+ unsafe {
+ return Mat4{ e: [
+ w.e[0], 0, 0, 0,
+ 0, w.e[1], 0, 0,
+ 0, 0, w.e[2], 0,
+ 0, 0, 0, 1,
+ ]!
+ }
+ }
+}
diff --git a/v_windows/v/vlib/gg/m4/vector.v b/v_windows/v/vlib/gg/m4/vector.v
new file mode 100644
index 0000000..52e4c78
--- /dev/null
+++ b/v_windows/v/vlib/gg/m4/vector.v
@@ -0,0 +1,230 @@
+/**********************************************************************
+*
+* Simply vector/matrix utility
+*
+* Copyright (c) 2021 Dario Deledda. All rights reserved.
+* Use of this source code is governed by an MIT license
+* that can be found in the LICENSE file.
+*
+* TODO:
+**********************************************************************/
+module m4
+
+import math
+
+pub struct Vec4 {
+pub mut:
+ e [4]f32
+}
+
+/*********************************************************************
+*
+* Utility
+*
+*********************************************************************/
+pub fn (x Vec4) str() string {
+ return '|${x.e[0]:-6.3},${x.e[1]:-6.3},${x.e[2]:-6.3},${x.e[3]:-6.3}|'
+}
+
+// create a Vec4 function passing x,y,z as parameteres. w is set to 1
+pub fn vec3(x f32, y f32, z f32) Vec4 {
+ return Vec4{
+ e: [x, y, z, 1]!
+ }
+}
+
+// Remove all the raw zeros
+[direct_array_access]
+pub fn (a Vec4) clean() Vec4 {
+ mut x := Vec4{}
+ for c, value in a.e {
+ if f32_abs(value) < precision {
+ x.e[c] = 0
+ } else {
+ x.e[c] = value
+ }
+ }
+ return x
+}
+
+// Set all elements to value
+pub fn (mut x Vec4) copy(value f32) {
+ x.e = [value, value, value, value]!
+}
+
+// Scale the vector using a scalar
+pub fn (x Vec4) mul_scalar(value f32) Vec4 {
+ return Vec4{
+ e: [x.e[0] * value, x.e[1] * value, x.e[2] * value, x.e[3] * value]!
+ }
+}
+
+// Reciprocal of the vector
+pub fn (x Vec4) inv() Vec4 {
+ return Vec4{
+ e: [
+ if x.e[0] != 0 { 1.0 / x.e[0] } else { f32(0) },
+ if x.e[1] != 0 { 1.0 / x.e[1] } else { f32(0) },
+ if x.e[2] != 0 { 1.0 / x.e[2] } else { f32(0) },
+ if x.e[3] != 0 { 1.0 / x.e[3] } else { f32(0) },
+ ]!
+ }
+}
+
+// Normalize the vector
+pub fn (x Vec4) normalize() Vec4 {
+ m := x.mod()
+ if m == 0 {
+ return zero_v4()
+ }
+ return Vec4{
+ e: [
+ x.e[0] * (1 / m),
+ x.e[1] * (1 / m),
+ x.e[2] * (1 / m),
+ x.e[3] * (1 / m),
+ ]!
+ }
+}
+
+// Normalize only xyz, w set to 0
+pub fn (x Vec4) normalize3() Vec4 {
+ m := x.mod3()
+ if m == 0 {
+ return zero_v4()
+ }
+ return Vec4{
+ e: [
+ x.e[0] * (1 / m),
+ x.e[1] * (1 / m),
+ x.e[2] * (1 / m),
+ 0,
+ ]!
+ }
+}
+
+// Module of the vector xyzw
+pub fn (x Vec4) mod() f32 {
+ return f32(math.sqrt(x.e[0] * x.e[0] + x.e[1] * x.e[1] + x.e[2] * x.e[2] + x.e[3] * x.e[3]))
+}
+
+// Module for 3d vector xyz, w ignored
+pub fn (x Vec4) mod3() f32 {
+ return f32(math.sqrt(x.e[0] * x.e[0] + x.e[1] * x.e[1] + x.e[2] * x.e[2]))
+}
+
+/*********************************************************************
+*
+* Math
+*
+*********************************************************************/
+// Return a zero vector
+pub fn zero_v4() Vec4 {
+ return Vec4{
+ e: [
+ f32(0),
+ 0,
+ 0,
+ 0,
+ ]!
+ }
+}
+
+// Return all one vector
+pub fn one_v4() Vec4 {
+ return Vec4{
+ e: [
+ f32(1),
+ 1,
+ 1,
+ 1,
+ ]!
+ }
+}
+
+// Return a blank vector
+pub fn blank_v4() Vec4 {
+ return Vec4{
+ e: [
+ f32(0),
+ 0,
+ 0,
+ 1,
+ ]!
+ }
+}
+
+// Set all elements to value
+pub fn set_v4(value f32) Vec4 {
+ return Vec4{
+ e: [
+ value,
+ value,
+ value,
+ value,
+ ]!
+ }
+}
+
+// Sum of all the elements
+pub fn (x Vec4) sum() f32 {
+ return x.e[0] + x.e[1] + x.e[2] + x.e[3]
+}
+
+/*********************************************************************
+*
+* Operators
+*
+*********************************************************************/
+// Addition
+pub fn (a Vec4) + (b Vec4) Vec4 {
+ return Vec4{
+ e: [
+ a.e[0] + b.e[0],
+ a.e[1] + b.e[1],
+ a.e[2] + b.e[2],
+ a.e[3] + b.e[3],
+ ]!
+ }
+}
+
+// Subtraction
+pub fn (a Vec4) - (b Vec4) Vec4 {
+ return Vec4{
+ e: [
+ a.e[0] - b.e[0],
+ a.e[1] - b.e[1],
+ a.e[2] - b.e[2],
+ a.e[3] - b.e[3],
+ ]!
+ }
+}
+
+// Dot product
+pub fn (a Vec4) * (b Vec4) f32 {
+ return a.e[0] * b.e[0] + a.e[1] * b.e[1] + a.e[2] * b.e[2] + a.e[3] * b.e[3]
+}
+
+// Cross product
+pub fn (a Vec4) % (b Vec4) Vec4 {
+ return Vec4{
+ e: [
+ (a.e[1] * b.e[2]) - (a.e[2] * b.e[1]),
+ (a.e[2] * b.e[0]) - (a.e[0] * b.e[2]),
+ (a.e[0] * b.e[1]) - (a.e[1] * b.e[0]),
+ 0,
+ ]!
+ }
+}
+
+// Components multiplication
+pub fn (x Vec4) mul_vec4(y Vec4) Vec4 {
+ return Vec4{
+ e: [
+ x.e[0] * y.e[0],
+ x.e[1] * y.e[1],
+ x.e[2] * y.e[2],
+ x.e[3] * y.e[3],
+ ]!
+ }
+}
diff --git a/v_windows/v/vlib/gg/text_rendering.c.v b/v_windows/v/vlib/gg/text_rendering.c.v
new file mode 100644
index 0000000..9530dd2
--- /dev/null
+++ b/v_windows/v/vlib/gg/text_rendering.c.v
@@ -0,0 +1,226 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module gg
+
+import sokol.sfons
+import sokol.sgl
+import gx
+import os
+
+struct FT {
+pub:
+ fons &C.FONScontext
+ font_normal int
+ font_bold int
+ font_mono int
+ font_italic int
+ scale f32 = 1.0
+}
+
+fn new_ft(c FTConfig) ?&FT {
+ if c.font_path == '' {
+ if c.bytes_normal.len > 0 {
+ fons := sfons.create(512, 512, 1)
+ bytes_normal := c.bytes_normal
+ bytes_bold := if c.bytes_bold.len > 0 {
+ c.bytes_bold
+ } else {
+ debug_font_println('setting bold variant to normal')
+ bytes_normal
+ }
+ bytes_mono := if c.bytes_mono.len > 0 {
+ c.bytes_mono
+ } else {
+ debug_font_println('setting mono variant to normal')
+ bytes_normal
+ }
+ bytes_italic := if c.bytes_italic.len > 0 {
+ c.bytes_italic
+ } else {
+ debug_font_println('setting italic variant to normal')
+ bytes_normal
+ }
+
+ return &FT{
+ fons: fons
+ font_normal: C.fonsAddFontMem(fons, c'sans', bytes_normal.data, bytes_normal.len,
+ false)
+ font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len,
+ false)
+ font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len,
+ false)
+ font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len,
+ false)
+ scale: c.scale
+ }
+ } else {
+ // Load default font
+ }
+ }
+
+ if c.font_path == '' || !os.exists(c.font_path) {
+ $if !android {
+ println('failed to load font "$c.font_path"')
+ return none
+ }
+ }
+
+ mut bytes := []byte{}
+ $if android {
+ // First try any filesystem paths
+ bytes = os.read_bytes(c.font_path) or { []byte{} }
+ if bytes.len == 0 {
+ // ... then try the APK asset path
+ bytes = os.read_apk_asset(c.font_path) or {
+ println('failed to load font "$c.font_path"')
+ return none
+ }
+ }
+ } $else {
+ bytes = os.read_bytes(c.font_path) or {
+ println('failed to load font "$c.font_path"')
+ return none
+ }
+ }
+ bold_path := if c.custom_bold_font_path != '' {
+ c.custom_bold_font_path
+ } else {
+ get_font_path_variant(c.font_path, .bold)
+ }
+ bytes_bold := os.read_bytes(bold_path) or {
+ debug_font_println('failed to load font "$bold_path"')
+ bytes
+ }
+ mono_path := get_font_path_variant(c.font_path, .mono)
+ bytes_mono := os.read_bytes(mono_path) or {
+ debug_font_println('failed to load font "$mono_path"')
+ bytes
+ }
+ italic_path := get_font_path_variant(c.font_path, .italic)
+ bytes_italic := os.read_bytes(italic_path) or {
+ debug_font_println('failed to load font "$italic_path"')
+ bytes
+ }
+ fons := sfons.create(512, 512, 1)
+ return &FT{
+ fons: fons
+ font_normal: C.fonsAddFontMem(fons, c'sans', bytes.data, bytes.len, false)
+ font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, false)
+ font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, false)
+ font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len,
+ false)
+ scale: c.scale
+ }
+}
+
+pub fn (ctx &Context) set_cfg(cfg gx.TextCfg) {
+ if !ctx.font_inited {
+ return
+ }
+ if cfg.bold {
+ ctx.ft.fons.set_font(ctx.ft.font_bold)
+ } else if cfg.mono {
+ ctx.ft.fons.set_font(ctx.ft.font_mono)
+ } else if cfg.italic {
+ ctx.ft.fons.set_font(ctx.ft.font_italic)
+ } else {
+ ctx.ft.fons.set_font(ctx.ft.font_normal)
+ }
+ scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale }
+ size := if cfg.mono { cfg.size - 2 } else { cfg.size }
+ ctx.ft.fons.set_size(scale * f32(size))
+ C.fonsSetAlign(ctx.ft.fons, int(cfg.align) | int(cfg.vertical_align))
+ color := C.sfons_rgba(cfg.color.r, cfg.color.g, cfg.color.b, cfg.color.a)
+ if cfg.color.a != 255 {
+ sgl.load_pipeline(ctx.timage_pip)
+ }
+ C.fonsSetColor(ctx.ft.fons, color)
+ ascender := f32(0.0)
+ descender := f32(0.0)
+ lh := f32(0.0)
+ ctx.ft.fons.vert_metrics(&ascender, &descender, &lh)
+}
+
+pub fn (ctx &Context) draw_text(x int, y int, text_ string, cfg gx.TextCfg) {
+ $if macos {
+ if ctx.native_rendering {
+ if cfg.align == gx.align_right {
+ width := ctx.text_width(text_)
+ C.darwin_draw_string(x - width, ctx.height - y, text_, cfg)
+ } else {
+ C.darwin_draw_string(x, ctx.height - y, text_, cfg)
+ }
+ return
+ }
+ }
+ if !ctx.font_inited {
+ eprintln('gg: draw_text(): font not initialized')
+ return
+ }
+ // text := text_.trim_space() // TODO remove/optimize
+ // mut text := text_
+ // if text.contains('\t') {
+ // text = text.replace('\t', ' ')
+ // }
+ ctx.set_cfg(cfg)
+ scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale }
+ C.fonsDrawText(ctx.ft.fons, x * scale, y * scale, &char(text_.str), 0) // TODO: check offsets/alignment
+}
+
+pub fn (ctx &Context) draw_text_def(x int, y int, text string) {
+ ctx.draw_text(x, y, text)
+}
+
+/*
+pub fn (mut gg FT) init_font() {
+}
+*/
+pub fn (ft &FT) flush() {
+ sfons.flush(ft.fons)
+}
+
+pub fn (ctx &Context) text_width(s string) int {
+ $if macos {
+ if ctx.native_rendering {
+ return C.darwin_text_width(s)
+ }
+ }
+ // ctx.set_cfg(cfg) TODO
+ if !ctx.font_inited {
+ return 0
+ }
+ mut buf := [4]f32{}
+ C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0])
+ if s.ends_with(' ') {
+ return int((buf[2] - buf[0]) / ctx.scale) +
+ ctx.text_width('i') // TODO fix this in fontstash?
+ }
+ res := int((buf[2] - buf[0]) / ctx.scale)
+ // println('TW "$s" = $res')
+ $if macos {
+ if ctx.native_rendering {
+ return res * 2
+ }
+ }
+ return int((buf[2] - buf[0]) / ctx.scale)
+}
+
+pub fn (ctx &Context) text_height(s string) int {
+ // ctx.set_cfg(cfg) TODO
+ if !ctx.font_inited {
+ return 0
+ }
+ mut buf := [4]f32{}
+ C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0])
+ return int((buf[3] - buf[1]) / ctx.scale)
+}
+
+pub fn (ctx &Context) text_size(s string) (int, int) {
+ // ctx.set_cfg(cfg) TODO
+ if !ctx.font_inited {
+ return 0, 0
+ }
+ mut buf := [4]f32{}
+ C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0])
+ return int((buf[2] - buf[0]) / ctx.scale), int((buf[3] - buf[1]) / ctx.scale)
+}
diff --git a/v_windows/v/vlib/gg/text_rendering.v b/v_windows/v/vlib/gg/text_rendering.v
new file mode 100644
index 0000000..a34bbbb
--- /dev/null
+++ b/v_windows/v/vlib/gg/text_rendering.v
@@ -0,0 +1,150 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module gg
+
+import os
+import gx
+
+enum FontVariant {
+ normal = 0
+ bold
+ mono
+ italic
+}
+
+struct FTConfig {
+ font_path string
+ custom_bold_font_path string
+ scale f32 = 1.0
+ font_size int
+ bytes_normal []byte
+ bytes_bold []byte
+ bytes_mono []byte
+ bytes_italic []byte
+}
+
+struct StringToRender {
+ x int
+ y int
+ text string
+ cfg gx.TextCfg
+}
+
+pub fn system_font_path() string {
+ env_font := os.getenv('VUI_FONT')
+ if env_font != '' && os.exists(env_font) {
+ return env_font
+ }
+ $if windows {
+ return 'C:\\Windows\\Fonts\\arial.ttf'
+ }
+ mut fonts := ['Ubuntu-R.ttf', 'Arial.ttf', 'LiberationSans-Regular.ttf', 'NotoSans-Regular.ttf',
+ 'FreeSans.ttf', 'DejaVuSans.ttf']
+ $if macos {
+ fonts = ['/System/Library/Fonts/SFNS.ttf', '/System/Library/Fonts/SFNSText.ttf',
+ '/Library/Fonts/Arial.ttf',
+ ]
+ for font in fonts {
+ if os.is_file(font) {
+ return font
+ }
+ }
+ }
+ $if android {
+ xml_files := ['/system/etc/system_fonts.xml', '/system/etc/fonts.xml',
+ '/etc/system_fonts.xml', '/etc/fonts.xml', '/data/fonts/fonts.xml',
+ '/etc/fallback_fonts.xml',
+ ]
+ font_locations := ['/system/fonts', '/data/fonts']
+ for xml_file in xml_files {
+ if os.is_file(xml_file) && os.is_readable(xml_file) {
+ xml := os.read_file(xml_file) or { continue }
+ lines := xml.split('\n')
+ mut candidate_font := ''
+ for line in lines {
+ if line.contains('<font') {
+ candidate_font = line.all_after('>').all_before('<').trim(' \n\t\r')
+ if candidate_font.contains('.ttf') {
+ for location in font_locations {
+ candidate_path := os.join_path(location, candidate_font)
+ if os.is_file(candidate_path) && os.is_readable(candidate_path) {
+ return candidate_path
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ s := os.execute('fc-list')
+ if s.exit_code != 0 {
+ panic('failed to fetch system fonts')
+ }
+ system_fonts := s.output.split('\n')
+ for line in system_fonts {
+ for font in fonts {
+ if line.contains(font) && line.contains(':') {
+ res := line.all_before(':')
+ println('Using font $res')
+ return res
+ }
+ }
+ }
+ panic('failed to init the font')
+}
+
+fn get_font_path_variant(font_path string, variant FontVariant) string {
+ // TODO: find some way to make this shorter and more eye-pleasant
+ // NotoSans, LiberationSans, DejaVuSans, Arial and SFNS should work
+ mut file := os.file_name(font_path)
+ mut fpath := font_path.replace(file, '')
+ file = file.replace('.ttf', '')
+
+ match variant {
+ .normal {}
+ .bold {
+ if fpath.ends_with('-Regular') {
+ file = file.replace('-Regular', '-Bold')
+ } else if file.starts_with('DejaVuSans') {
+ file += '-Bold'
+ } else if file.to_lower().starts_with('arial') {
+ file += 'bd'
+ } else {
+ file += '-bold'
+ }
+ $if macos {
+ if os.exists('SFNS-bold') {
+ file = 'SFNS-bold'
+ }
+ }
+ }
+ .italic {
+ if file.ends_with('-Regular') {
+ file = file.replace('-Regular', '-Italic')
+ } else if file.starts_with('DejaVuSans') {
+ file += '-Oblique'
+ } else if file.to_lower().starts_with('arial') {
+ file += 'i'
+ } else {
+ file += 'Italic'
+ }
+ }
+ .mono {
+ if !file.ends_with('Mono-Regular') && file.ends_with('-Regular') {
+ file = file.replace('-Regular', 'Mono-Regular')
+ } else if file.to_lower().starts_with('arial') {
+ // Arial has no mono variant
+ } else {
+ file += 'Mono'
+ }
+ }
+ }
+ return fpath + file + '.ttf'
+}
+
+fn debug_font_println(s string) {
+ $if debug_font ? {
+ println(s)
+ }
+}