From f2cb1fdd3155fbf76cbe0f8871aba447acc7b5ba Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Tue, 7 Feb 2023 23:58:01 -0800 Subject: [PATCH] Transform shenanigans --- circledemo.jai | 6 - physics.jai | 493 ++++++++++++++++++++++++++++++++++++++++++++++++- testkit.jai | 240 ------------------------ 3 files changed, 488 insertions(+), 251 deletions(-) delete mode 100644 circledemo.jai diff --git a/circledemo.jai b/circledemo.jai deleted file mode 100644 index e067e43..0000000 --- a/circledemo.jai +++ /dev/null @@ -1,6 +0,0 @@ -#import "Basic"; -#import "Math"; -#import,file "testkit.jai"; -#import,file "physics.jai"; - -main :: () diff --git a/physics.jai b/physics.jai index e3c64cf..8e100ef 100644 --- a/physics.jai +++ b/physics.jai @@ -1,6 +1,9 @@ -#import "Basic"; +#import "Window_Creation"; +Debug :: #import "Debug"; +Simp :: #import "Simp"; +Input :: #import "Input"; #import "Math"; -#import,file "testkit.jai"; +#import "Basic"; TIMESTEP: float = 1.0 / 60.0; DEBUGGING :: true; @@ -50,11 +53,134 @@ hit_impulse :: (body_pos: Vector2, mouse_pos: Vector2) -> Vector2 { return .{0, -15.0}; //return normalize(body_pos - mouse_pos) * 15.0; } + ShapeType :: enum { Circle; Rectangle; }; +degrees :: (d: float) -> float { + return d * PI/180.0; +} + +/* +A transform is an offset, a rotation, and a scaling +transforms define new basis vectors which in turn define a new space, the space of that transform +when a transform transforms a point in one space, it moves it into its space. It takes whatever point you give it, and acts as if its offset was from the transform's point of view, not where the point came from. +An entity's transform, when applied to a point, converts from local to world space. The input is local, the output is world +The inverse does the opposite, world to local space. +*/ +Transform2D :: struct { + pos: Vector2; + angle: float; + scale: Vector2 = .{1.0, 1.0}; +}; +approx_eq :: (v1: Vector2, v2: Vector2) -> bool { + return abs(v1.x - v2.x) < 0.01 && abs(v1.y - v2.y) < 0.01; +} + +#run { + point := xy(5, 6); + t := Transform2D.{pos = .{1, 2}, angle = 0.0, scale = .{2.0, 1.0}}; + print("% | % | %\n", inv(t), xform(inv(t), xform(t, point)), point); + assert(approx_eq(point, xform(inv(t), xform(t, point)))); +} + +inv :: (t: Transform2D) -> Transform2D { + // v is a vector, v || s means xy(v.x*s.x, v.y*s.y), elementwise product. For + // scaling vector + + // inv(t) * t * pos = pos + // inv(t) * ( rotate(xy(pos.x*t.scale.x, pos.y*t.scale.y), t.angle) + t.pos ) = pos + // p' = rotate(xy(pos.x*t.scale.x, pos.y*t.scale.y), t.angle) + t.pos + // inv(t) * p' = pos + // in = inv(t) + // rotate(xy(p'.x*in.scale.x, p'.y*in.scale.y), in.angle) + in.pos = pos + // rotate(xy(p'.x*in.scale.x, p'.y*in.scale.y), in.angle) = pos - in.pos + // p' || in.scale = rotate(pos - in.pos, -in.angle) + // p' = rotate(pos - in.pos, -in.angle) || 1.0/in.scale + // rotate(xy(pos.x*t.scale.x, pos.y*t.scale.y), t.angle) + t.pos = rotate(pos - in.pos, -in.angle) || 1.0/in.scale + // rotate(pos || t.scale, t.angle) + t.pos = rotate(pos - in.pos, -in.angle) || 1.0/in.scale + // rotate(pos - in.pos, -in.angle) || 1.0/in.scale = rotate(pos || t.scale, t.angle) + t.pos + // ^^ trying to find all the in., so want to make the left side look like the right side + + return .{ + pos = -t.pos, + scale = .{1.0/t.scale.x, 1.0/t.scale.y}, + angle = -t.angle, + }; + + + // THIS IS WRONG: I think that: rotate(v, theta) || scale == rotate(v || scale, theta) + V :: Vector2.{0.5, 0.6}; + THETA :: #run degrees(39.0); + SCALE :: Vector2.{2.0, 3.0}; + apply_scale :: (to: Vector2, scale: Vector2) -> Vector2 { + return .{to.x*scale.x, to.y*scale.y}; + } + #run { + /* + rotation: + x' = x * cos(angle) + y * -sin(angle) + y' = x * sin(angle) + y * cos(angle) + + want to solve for inner + rotate(v, theta) || scale_in = rotate(v || inner, theta) + where x = v.x, y = v.y + + (x * cos(angle) + y * -sin(angle))*scale_in.x = x * inner.x * cos(angle) + y * inner.y -sin(angle) + (x * sin(angle) + y * cos(angle))*scale_in.y = x * inner.x * sin(angle) + y * inner.y * cos(angle) + + system of equations, all variables known except for inner. Solving for inner.x: + ((x * cos(angle) + y * -sin(angle))*scale_in.x - y * inner.y -sin(angle)) / (x * cos(angle)) = inner.x + + now reinserting into the other equation... + (x * sin(angle) + y * cos(angle))*scale_in.y = x * ((x * cos(angle) + y * -sin(angle))*scale_in.x - y * inner.y -sin(angle)) / (x * cos(angle)) * sin(angle) + y * inner.y * cos(angle) + (x * sin(angle) + y * cos(angle))*scale_in.y = (x * (x * cos(angle) + y * -sin(angle))*scale_in.x - x * y * inner.y -sin(angle)) / (x * cos(angle)) * sin(angle) + y * inner.y * cos(angle) + + + */ + /* + left := apply_scale(rotate(V, THETA), SCALE); + right := rotate(apply_scale(V, SCALE), THETA); + print("% %\n", left, right); + assert(approx_eq(left, right));*/ + } + //#assert approx_eq(apply_scale(rotate(V, THETA), SCALE), rotate(apply_scale(V, SCALE), THETA)); + + return .{}; + /*return .{ + pos = - + }*/ +} + +xform :: (using t: Transform2D, p: Vector2) -> Vector2 { + result: Vector2 = p; + result.x *= scale.x; + result.y *= scale.y; + result = rotate(result, angle); + result += pos; + return result; +} + +xform :: (from: Transform2D, other: Transform2D) -> Transform2D { + result: Transform2D; + result.pos = xform(from, other.pos); + result.angle = from.angle + other.angle; + result.scale.x = from.scale.x * other.scale.x; + result.scale.y = from.scale.y * other.scale.y; + return result; +} + +xform_inv :: (using t: Transform2D, p: Vector2) -> Vector2 { + result: Vector2 = p; + result -= pos; + result = rotate(result, -angle); + result.x /= scale.x; + result.y /= scale.y; + return result; +} + Shape :: struct { type: ShapeType; offset: Vector2; // in local space of body @@ -156,7 +282,243 @@ resolve :: (using c: Contact) { body_b.pos -= ds * tb; } -draw_body :: (d: DrawingSettings, using b: Body) { +#scope_file; +// ------------------------------------------------------------------------ +// demo stuff visualization/debugging stuff + + +window: Window_Type; +quit: bool; +dt: float; +panning: bool = false; +window_width: s32 = 1280; +window_height: s32 = 720; +// the camera is a transform which goes from its parent space (screen) to its space (world space) +camera: Transform2D = .{scale = .{0.01, 0.01}, pos = #run -xy(window_width, window_height)/2.0 * 0.01}; +mouse_delta_screen: Vector2; +mouse_frozen: bool; +mouse_frozen_at: Vector2; + +last_time: float64; +last_mouse_pos: Vector2; + + +dbgprint :: print; // so can be detected and removed + +#scope_file; +normalize :: (using v: *Vector2) -> float { + sq := sqrt(x*x + y*y); + + factor := 1.0 / sq; + x *= factor; + y *= factor; + + return sq; +} +normalize :: (v: Vector2) -> Vector2 #must { + normalize(*v); + return v; +} +xy :: (x: int, y: int) -> Vector2 { + return xy(cast(float)x, cast(float)y); +} +#scope_export; + +WHITE :: Vector4.{1.0, 1.0, 1.0, 1.0}; +RED :: Vector4.{1.0, 0.0, 0.0, 1.0}; +GREEN :: Vector4.{0.0, 1.0, 0.0, 1.0}; +BLUE :: Vector4.{0.0, 0.0, 1.0, 1.0}; +ORANGE :: Vector4.{1.0, cast(float)0xA5/ cast(float)255, 0.0, 1.0}; +DrawingSettings :: struct { + world_space: bool = true; + color: Vector4 = WHITE; +}; +line :: (using d: DrawingSettings, _from: Vector2, _to: Vector2, thickness: float = 1.0) { + width := thickness; + from := _from; + to := _to; + if world_space { + from = world_to_screen(from); + to = world_to_screen(to); + } + Simp.set_shader_for_color(true); + normal := rotate(unit_vector(to - from), PI/2.0); + Simp.immediate_quad(from + normal*width, from - normal*width, to + normal*width, to - normal*width, color = color); +} +vector :: (d: DrawingSettings, from: Vector2, vector: Vector2) { + line(d, from, from+vector); + + arrow_head_length := length(vector) * 0.5; + + // arrow head + line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, PI/2.0 + PI/4.0)); + line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, -PI/2.0 + -PI/4.0)); +} + +LastingPip :: struct { + alive_for: float; + pos: Vector2; + d: DrawingSettings; +}; + +pips : [100] LastingPip; +push_pip :: (d: DrawingSettings, at: Vector2) { + for * pips + { + if it.alive_for <= 0.0 + { + it.alive_for = 1.0; + it.pos = at; + it.d = d; + break; + } + } +} +draw_pips :: (dt: float) { + for * pips if it.alive_for > 0.0 + { + d := it.d; + d.color.w *= it.alive_for; + pip(d, it.pos); + it.alive_for -= dt; + } +} +pip :: (using d: DrawingSettings, _at: Vector2, size: float = 0.05) { + at := _at; + Simp.set_shader_for_color(true); + if world_space { + at = world_to_screen(at); + size /= camera.scale.x; + } + Simp.immediate_quad(at + xy(-size, size), at + xy(size, size), at + xy(size, -size), at + xy(-size, -size), color = color); +} + +font: *Simp.Dynamic_Font; +text :: (using d: DrawingSettings, t: string, p: Vector2) { + drawing_at := p; + if world_space { + drawing_at = world_to_screen(drawing_at); + } + Simp.draw_text(font, xx drawing_at.x, xx drawing_at.y, t, color = color); +} + +init :: () { + window = create_window(window_width, window_height, "Test Kit"); + // Actual render size in pixels can be different from the window dimensions we specified above (for example on high-resolution displays on macOS/iOS). + window_width, window_height = Simp.get_render_dimensions(window); + + Simp.set_render_target(window); + + font = Simp.get_font_at_size(".", "Roboto-Regular.ttf", 18); + assert(font != null); +} +// screen coordinates +mouse_screen :: () -> Vector2 { + if mouse_frozen return mouse_frozen_at; + x, y := get_mouse_pointer_position(); + pos: Vector2 = xy(cast(float)x, cast(float)y); + + // simp is lower left is (0, 0) and y+ is up + pos.y = window_height - pos.y; + + return pos; +} +mouse_world :: () -> Vector2 { + return screen_to_world(mouse_screen()); +} + +// query quit to see if should quit. Call this in a loop +frame_start :: () { + + dt = cast(float)(get_time() - last_time); + last_time = get_time(); + Input.update_window_events(); + mouse_delta_screen = mouse_screen() - last_mouse_pos; // using Input.mouse_delta_x seems to incorrectly accumulate + last_mouse_pos = mouse_screen(); + + for Input.get_window_resizes() { + Simp.update_window(it.window); // Simp will do nothing if it doesn't care about this window. + + if it.window == window { + should_reinit := (it.width != window_width) || (it.height != window_height); + + window_width = it.width; + window_height = it.height; + + //if should_reinit my_init_fonts(); // Resize the font for the new window size. + } + } + + camera.scale.x *= 1.0 - 0.1*Input.mouse_delta_z/120.0; + camera.scale.y = camera.scale.x; + if panning { + camera.pos -= mouse_delta_screen*camera.scale.x; + } + Simp.immediate_begin(); + Simp.clear_render_target(0.0, 0.0, 0.0, 1.0); + + // draw grid + grid_d: DrawingSettings = .{true,.{0.2, 0.2, 0.2, 0.5}}; + text_d := grid_d; + text_d.color = .{0.6, 0.6, 0.6, 1.0}; + help_text_max :: 8; + help_zoom_max :: 0.04; + for x: -30..30 { + line(grid_d, xy(x, 30), xy(x, -30)); + if camera.scale.x <= help_zoom_max && abs(x) < help_text_max then text(text_d, tprint("%m", x), .{xx x, 0.0}); + } + for y: -30..30 { + line(grid_d, xy(30, y), xy(-30, y)); + if camera.scale.x <= help_zoom_max && abs(y) < help_text_max then text(text_d, tprint("%m", y), .{0.0, xx y}); + } + + draw_pips(dt); + + if mouse_frozen pip(.{false, ORANGE}, mouse_frozen_at, size = 20.0); + + + // handle inputs + for Input.events_this_frame { + if it.type == .QUIT then quit = true; + + if it.type == { + case .KEYBOARD; + if it.key_pressed { + if it.key_code == { + case .ESCAPE; + quit = true; + + case #char "T"; + if !mouse_frozen mouse_frozen_at = mouse_screen(); + mouse_frozen = !mouse_frozen; + } + } + if it.key_code == .MOUSE_BUTTON_LEFT { + panning = cast(bool)it.key_pressed; + } + } + } +} + +frame_end :: () { + Simp.immediate_flush(); + Simp.swap_buffers(window); +} + +screen_to_world :: (screen: Vector2) -> Vector2 { + return xform(camera, screen); +} +world_to_screen :: (world: Vector2) -> Vector2 { + return xform_inv(camera, world); +} + +draw_body :: (d: DrawingSettings, b: Body) { + draw_shape(d, b.shape); +} + + +draw_shape :: (d: DrawingSettings, shape: Shape) { + #if false { if shape.type == { case .Circle; points: int = 32; @@ -187,9 +549,10 @@ draw_body :: (d: DrawingSettings, using b: Body) { line(d, lower_left, upper_left); } } +} -circledemo :: () { +demo_circle :: () { init(); bodies: [..]Body; @@ -278,4 +641,124 @@ circledemo :: () { } } -main :: () { circledemo(); } +demo_transforms_print :: () { + point := xy(1, 1); + t: Transform2D; + t.pos = xy(5, 0); + t.angle = degrees(45.0); + print("%\n", xform(t, point)); + print("%\n", xform_inv(t, xform(t, point))); +} + +demo_transforms :: () { + init(); + + + t: Transform2D; + point := xy(2, 1); + + while !quit { + frame_start(); + + pip(.{color = GREEN}, t.pos); + pip(.{}, xform(t, point)); + + POINTS :: 64; + cur_point := point; + cur_point_inv := point; + START_COLOR :: WHITE; + END_COLOR :: BLUE; + END_COLOR_INV :: ORANGE; + for 0..POINTS-1 { + progress: float = cast(float)it / cast(float)POINTS; + cur_point = xform(t, cur_point); + cur_point_inv = xform_inv(t, cur_point_inv); + pip(.{color = lerp(START_COLOR, END_COLOR, progress)}, cur_point); + pip(.{color = lerp(START_COLOR, END_COLOR_INV, progress)}, cur_point_inv); + } + print("\n"); + + + horizontal: float = 0.0; + vertical: float = 0.0; + for Input.input_button_states if it & .DOWN { + keycode: Input.Key_Code = xx it_index; + if keycode == #char "D" horizontal += 1.0; + if keycode == #char "A" horizontal -= 1.0; + if keycode == #char "W" vertical += 1.0; + if keycode == #char "S" vertical -= 1.0; + if keycode == .MOUSE_BUTTON_RIGHT t.pos += mouse_delta_screen*camera.scale.x; + } + print("should be equal: % %\n", point, xform_inv(t, xform(t, point))); + t.angle += horizontal*dt*degrees(45.0); + t.scale.x += vertical*dt; + print("%\n", t); + frame_end(); + } +} + +demo_transforms2 :: () { + init(); + + draw_transform :: (d: DrawingSettings, t: Transform2D) { + horizontal := xform(t, xy(1.0, 0.0)); + vertical := xform(t, xy(0.0, 1.0)); + line(d, t.pos, vertical); + line(d, t.pos, horizontal); + pip(d, t.pos); + } + + t1: Transform2D; + t2: Transform2D; + point := xy(2, 1); + while !quit { + print("%\n", camera); + frame_start(); + defer frame_end(); + + combined := xform(t1, t2); + draw_transform(.{color = GREEN}, t1); + draw_transform(.{color = BLUE}, t2); + draw_transform(.{color = ORANGE}, combined); + + pip(.{}, xform(t1, point)); + pip(.{color = ORANGE}, xform(combined, point)); + + horizontal: float; + vertical: float; + right_panning: Vector2; + for Input.input_button_states if it & .DOWN { + keycode: Input.Key_Code = xx it_index; + if keycode == #char "D" horizontal += 1.0; + if keycode == #char "A" horizontal -= 1.0; + if keycode == #char "W" vertical += 1.0; + if keycode == #char "S" vertical -= 1.0; + if keycode == .MOUSE_BUTTON_RIGHT right_panning = mouse_delta_screen*camera.scale.x; + } + t1.angle += horizontal*dt*degrees(45.0); + t2.angle += vertical*dt*degrees(45.0); + t1.pos += right_panning; + //t1.scale.x += horizontal*dt; + t2.pos += 2.0*right_panning; + } +} + +demo_contact :: () { + init(); + + size := Vector2.{1.0, 1.0}; + + from := Shape.{type = .Rectangle, halfsize = size/2.0}; + to := Shape.{type = .Rectangle, offset = xy(0, 1.0), halfsize = size/2.0}; + + while !quit { + frame_start(); + frame_end(); + } +} + +//main :: () { demo_circle(); } +//main :: () { demo_transforms_print(); } +//main :: () { demo_transforms(); } +main :: () { demo_transforms2(); } +//main :: () { demo_contact(); } diff --git a/testkit.jai b/testkit.jai index 4d854a7..e69de29 100644 --- a/testkit.jai +++ b/testkit.jai @@ -1,240 +0,0 @@ -#import "Window_Creation"; -Debug :: #import "Debug"; -Simp :: #import "Simp"; -Input :: #import "Input"; -#import "Math"; -#import "Basic"; - -Camera :: struct { - pos: Vector2; - zoom: float = 1.0; -}; - -window: Window_Type; -quit: bool; -dt: float; -panning: bool = false; -camera : Camera; -mouse_frozen: bool; -mouse_frozen_at: Vector2; - -last_time: float64; -last_mouse_pos: Vector2; - -window_width : s32 = 1280; -window_height : s32 = 720; - -dbgprint :: print; // so can be detected and removed - -#scope_file; -normalize :: (using v: *Vector2) -> float { - sq := sqrt(x*x + y*y); - - factor := 1.0 / sq; - x *= factor; - y *= factor; - - return sq; -} -normalize :: (v: Vector2) -> Vector2 #must { - normalize(*v); - return v; -} -xy :: (x: int, y: int) -> Vector2 { - return xy(cast(float)x, cast(float)y); -} -#scope_export; - -WHITE :: Vector4.{1.0, 1.0, 1.0, 1.0}; -RED :: Vector4.{1.0, 0.0, 0.0, 1.0}; -GREEN :: Vector4.{0.0, 1.0, 0.0, 1.0}; -ORANGE :: Vector4.{1.0, 0xA5/255, 0.0, 1.0}; -DrawingSettings :: struct { - world_space: bool = true; - color: Vector4 = WHITE; -}; -line :: (using d: DrawingSettings, _from: Vector2, _to: Vector2, thickness: float = 1.0) { - width := thickness; - from := _from; - to := _to; - if world_space { - from = world_to_screen(from); - to = world_to_screen(to); - } - Simp.set_shader_for_color(true); - normal := rotate(unit_vector(to - from), PI/2.0); - Simp.immediate_quad(from + normal*width, from - normal*width, to + normal*width, to - normal*width, color = color); -} -vector :: (d: DrawingSettings, from: Vector2, vector: Vector2) { - line(d, from, from+vector); - - arrow_head_length := length(vector) * 0.5; - - // arrow head - line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, PI/2.0 + PI/4.0)); - line(d, from + vector, from + vector + rotate(normalize(vector)*arrow_head_length, -PI/2.0 + -PI/4.0)); -} - -LastingPip :: struct { - alive_for: float; - pos: Vector2; - d: DrawingSettings; -}; - -pips : [10] LastingPip; -push_pip :: (d: DrawingSettings, at: Vector2) { - for * pips - { - if it.alive_for <= 0.0 - { - it.alive_for = 1.0; - it.pos = at; - it.d = d; - break; - } - } -} -draw_pips :: (dt: float) { - for * pips if it.alive_for > 0.0 - { - d := it.d; - d.color.w *= it.alive_for; - pip(d, it.pos); - it.alive_for -= dt; - } -} -pip :: (using d: DrawingSettings, _at: Vector2, size: float = 0.05) { - at := _at; - Simp.set_shader_for_color(true); - if world_space { - at = world_to_screen(at); - size /= camera.zoom; - } - Simp.immediate_quad(at + xy(-size, size), at + xy(size, size), at + xy(size, -size), at + xy(-size, -size), color = color); -} - -font: *Simp.Dynamic_Font; -text :: (using d: DrawingSettings, t: string, p: Vector2) { - drawing_at := p; - if world_space { - drawing_at = world_to_screen(drawing_at); - } - Simp.draw_text(font, xx drawing_at.x, xx drawing_at.y, t, color = color); -} - -init :: () { - window = create_window(window_width, window_height, "Test Kit"); - // Actual render size in pixels can be different from the window dimensions we specified above (for example on high-resolution displays on macOS/iOS). - window_width, window_height = Simp.get_render_dimensions(window); - - camera.pos = -xy(window_width, window_height)/2.0; - camera.zoom = 0.01; - - Simp.set_render_target(window); - - font = Simp.get_font_at_size(".", "Roboto-Regular.ttf", 18); - assert(font != null); -} -// screen coordinates -mouse_screen :: () -> Vector2 { - if mouse_frozen return mouse_frozen_at; - x, y := get_mouse_pointer_position(); - pos: Vector2 = xy(cast(float)x, cast(float)y); - - // simp is lower left is (0, 0) and y+ is up - pos.y = window_height - pos.y; - - return pos; -} -mouse_world :: () -> Vector2 { - return screen_to_world(mouse_screen()); -} - -// query quit to see if should quit. Call this in a loop -frame_start :: () { - - dt = cast(float)(get_time() - last_time); - last_time = get_time(); - Input.update_window_events(); - mouse_delta := mouse_screen() - last_mouse_pos; // using Input.mouse_delta_x seems to incorrectly accumulate - last_mouse_pos = mouse_screen(); - - for Input.get_window_resizes() { - Simp.update_window(it.window); // Simp will do nothing if it doesn't care about this window. - - if it.window == window { - should_reinit := (it.width != window_width) || (it.height != window_height); - - window_width = it.width; - window_height = it.height; - - //if should_reinit my_init_fonts(); // Resize the font for the new window size. - } - } - - camera.zoom *= 1.0 - 0.1*Input.mouse_delta_z/120.0; - if panning { - camera.pos -= mouse_delta; - } - Simp.immediate_begin(); - Simp.clear_render_target(0.0, 0.0, 0.0, 1.0); - - // draw grid - grid_d: DrawingSettings = .{true,.{0.2, 0.2, 0.2, 0.5}}; - text_d := grid_d; - text_d.color = .{0.6, 0.6, 0.6, 1.0}; - help_text_max :: 8; - help_zoom_max :: 0.04; - for x: -30..30 { - line(grid_d, xy(x, 30), xy(x, -30)); - if camera.zoom <= help_zoom_max && abs(x) < help_text_max then text(text_d, tprint("%m", x), .{xx x, 0.0}); - } - for y: -30..30 { - line(grid_d, xy(30, y), xy(-30, y)); - if camera.zoom <= help_zoom_max && abs(y) < help_text_max then text(text_d, tprint("%m", y), .{0.0, xx y}); - } - - draw_pips(dt); - - if mouse_frozen pip(.{false, ORANGE}, mouse_frozen_at, size = 20.0); - - - // handle inputs - for Input.events_this_frame { - if it.type == .QUIT then quit = true; - - if it.type == { - case .KEYBOARD; - if it.key_pressed { - if it.key_code == { - case .ESCAPE; - quit = true; - - case #char "T"; - if !mouse_frozen mouse_frozen_at = mouse_screen(); - mouse_frozen = !mouse_frozen; - } - } - if it.key_code == .MOUSE_BUTTON_LEFT { - panning = cast(bool)it.key_pressed; - } - } - } -} - -frame_end :: () { - Simp.immediate_flush(); - Simp.swap_buffers(window); -} - -screen_to_world :: (screen: Vector2) -> Vector2 { - using camera; - return (screen + pos)*zoom; -} -world_to_screen :: (world: Vector2) -> Vector2 { - using camera; - // world = (screen + pos)*zoom; - // world/zoom = screen + pos; - // world/zoom - pos = screen; - return (world/zoom) - pos; -}