diff options
Diffstat (limited to 'v_windows/v/old/vlib/gg')
| -rw-r--r-- | v_windows/v/old/vlib/gg/enums.v | 161 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/gg.v | 946 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/gg_android.c.v | 37 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/gg_darwin.c.v | 21 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/gg_darwin.m | 126 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/image.v | 384 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/m4/graphic.v | 110 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/m4/m4_test.v | 235 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/m4/matrix.v | 595 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/m4/vector.v | 230 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/gg/text_rendering.v | 370 | 
11 files changed, 3215 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/gg/enums.v b/v_windows/v/old/vlib/gg/enums.v new file mode 100644 index 0000000..c87f986 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/gg.v b/v_windows/v/old/vlib/gg/gg.v new file mode 100644 index 0000000..3b882da --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg.v @@ -0,0 +1,946 @@ +// 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 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 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 +} + +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 +} + +[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 +} + +pub struct Size { +pub: +	width  int +	height int +} + +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 { C.memset(&pipdesc, 0, 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() +		} +	} +} + +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 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 (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) +} + +// 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() +} + +[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) 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 * 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_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 (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] +	y0 := points[1] +	for i in 1 .. (len / 2 + 1) { +		sgl.v2f(x0, y0) +		sgl.v2f(points[i * 4 - 2], points[i * 4 - 1]) +		sgl.v2f(points[i * 4], points[i * 4 + 1]) +	} + +	if len % 2 == 0 { +		sgl.v2f(points[2 * len - 2], points[2 * len - 1]) +	} +	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], points[2 * i + 1]) +	} +	sgl.v2f(points[0], points[1]) +	sgl.end() +} + +pub fn screen_size() Size { +	$if macos { +		return C.gg_get_screen_size() +	} +	// TODO windows, linux, etc +	return Size{} +} + +// 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. +	} +	return s +} + +pub fn high_dpi() bool { +	return C.sapp_high_dpi() +} + +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() +		} +	} +} +*/ diff --git a/v_windows/v/old/vlib/gg/gg_android.c.v b/v_windows/v/old/vlib/gg/gg_android.c.v new file mode 100644 index 0000000..23450f2 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/gg_darwin.c.v b/v_windows/v/old/vlib/gg/gg_darwin.c.v new file mode 100644 index 0000000..78e8a45 --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg_darwin.c.v @@ -0,0 +1,21 @@ +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_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/old/vlib/gg/gg_darwin.m b/v_windows/v/old/vlib/gg/gg_darwin.m new file mode 100644 index 0000000..d6bb5a1 --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg_darwin.m @@ -0,0 +1,126 @@ +#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/old/vlib/gg/image.v b/v_windows/v/old/vlib/gg/image.v new file mode 100644 index 0000000..89a16d7 --- /dev/null +++ b/v_windows/v/old/vlib/gg/image.v @@ -0,0 +1,384 @@ +// 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.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 +} + +pub struct Rect { +pub: +	x      f32 +	y      f32 +	width  f32 +	height f32 +} + +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 +} + +// 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 +} + +// 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 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 +} + +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 +} + +// 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) +} + +// 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(255, 255, 255, 255) +	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 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} +	) +} + +// 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/old/vlib/gg/m4/graphic.v b/v_windows/v/old/vlib/gg/m4/graphic.v new file mode 100644 index 0000000..e134e80 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/m4/m4_test.v b/v_windows/v/old/vlib/gg/m4/m4_test.v new file mode 100644 index 0000000..90b4640 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/m4/matrix.v b/v_windows/v/old/vlib/gg/m4/matrix.v new file mode 100644 index 0000000..c839b9d --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/m4/vector.v b/v_windows/v/old/vlib/gg/m4/vector.v new file mode 100644 index 0000000..52e4c78 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/gg/text_rendering.v b/v_windows/v/old/vlib/gg/text_rendering.v new file mode 100644 index 0000000..dd0499d --- /dev/null +++ b/v_windows/v/old/vlib/gg/text_rendering.v @@ -0,0 +1,370 @@ +// 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 + +enum FontVariant { +	normal = 0 +	bold +	mono +	italic +} + +struct FT { +pub: +	fons        &C.FONScontext +	font_normal int +	font_bold   int +	font_mono   int +	font_italic int +	scale       f32 = 1.0 +} + +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 +} + +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) +} + +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) +	} +}  | 
