From c0d84bae4310ca82b3c59bf85e636033f85766d4 Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Sun, 30 Aug 2026 14:01:17 +0200 Subject: [PATCH] Grid matrix math fix --- src/animation.odin | 8 +++--- src/grid.odin | 17 ++++++------- src/program.odin | 63 +++++++++++++++++++++++++++++++++------------- src/utils.odin | 26 ++++++++++++++++--- 4 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/animation.odin b/src/animation.odin index b6b7a82..2562803 100644 --- a/src/animation.odin +++ b/src/animation.odin @@ -24,10 +24,10 @@ rectangle_corners_from_rect :: proc "contextless" (rect: rect2) -> RectangleCorn rectangle_corners_transform_mat3 :: proc "contextless" (transform: mat3, corners: RectangleCorners) -> RectangleCorners { return { - matrix3_transform_v2(transform, corners.tl), - matrix3_transform_v2(transform, corners.tr), - matrix3_transform_v2(transform, corners.br), - matrix3_transform_v2(transform, corners.bl), + matrix3_transform_xy(transform, corners.tl), + matrix3_transform_xy(transform, corners.tr), + matrix3_transform_xy(transform, corners.br), + matrix3_transform_xy(transform, corners.bl), } } diff --git a/src/grid.odin b/src/grid.odin index 9af01a7..c8610c8 100644 --- a/src/grid.odin +++ b/src/grid.odin @@ -35,37 +35,36 @@ grid_get_line_separation :: proc(zoom: v2, min_line_distance: v2) -> v2 { grid_world_to_grid_matrix :: proc(zoom, offset: v2) -> mat3 { zoom_amount := grid_get_zoom(zoom) - return matrix3_translate2(offset) * linalg.matrix3_scale(v3{**zoom_amount, 1.0}) + return matrix3_translate2(offset) * matrix3_scale2(zoom_amount) } -draw_grid :: proc(grid: Grid, area, text_draw, text_clip: rect2) { +draw_grid :: proc(grid: Grid, area, text_draw, text_clip: rect2, color: rl.Color) { world_to_grid_matrix := grid_world_to_grid_matrix(grid.zoom, grid.offset) grid_to_world_matrix := linalg.inverse(world_to_grid_matrix) position := rect_get_tl(area) size := rect_get_size(area) grid_world_br := position + size - grid_color := rl.Color{64, 64, 64, 255} sep := grid_get_line_separation(grid.zoom, grid.cell_size_px) - tl := matrix3_transform_v2(world_to_grid_matrix, position) + tl := matrix3_transform_xy(world_to_grid_matrix, position) tl = linalg.floor(tl / sep) * sep - br := matrix3_transform_v2(world_to_grid_matrix, grid_world_br) + br := matrix3_transform_xy(world_to_grid_matrix, grid_world_br) for x: f32 = tl.x; x < br.x; x += sep.x { - world_position_x := (v3{x, 0.0, 1.0} * grid_to_world_matrix).x + world_position_x := (grid_to_world_matrix * v3{x, 0.0, 1.0}).x p1 := v2{world_position_x, position.y} p2 := v2{world_position_x, grid_world_br.y} - rl.DrawLineV(p1, p2, grid_color) + rl.DrawLineV(p1, p2, color) if p1.x >= text_clip.x && p1.x < text_clip.x + text_clip.width { draw_float_aligned(x, {world_position_x, text_draw.y}, program.font_style, .Middle, .Top, rl.GRAY) } } for y: f32 = tl.y; y < br.y; y += sep.y { - world_position_y := (v3{0.0, y, 1.0} * grid_to_world_matrix).y + world_position_y := (grid_to_world_matrix * v3{0.0, y, 1.0}).y p1 := v2{position.x, world_position_y} p2 := v2{grid_world_br.x, world_position_y} - rl.DrawLineV(p1, p2, grid_color) + rl.DrawLineV(p1, p2, color) if p1.y >= text_clip.y && p1.y < text_clip.y + text_clip.height { draw_float_aligned(y, {text_draw.x, world_position_y}, program.font_style, .Left, .Center, rl.GRAY) } diff --git a/src/program.odin b/src/program.odin index 1fbc1f8..9243e90 100644 --- a/src/program.odin +++ b/src/program.odin @@ -1,5 +1,32 @@ package main +/* + Rules of matrices: + 1) Order matters when multiplying with vectors + - m * v = Column vector x matrix + - v * m = Row vector x matrix + + 2) Odin matrices are column-major for math efficiency + fmt.println(matrix[3, 3]f32 { + a, -b, 0, + b, a, 0, + 0, 0, 1, + }) + >> [0.8660254, 0.5, 0, -0.5, 0.8660254, 0, 0, 0, 1] + + Additionally this means that the first index of a matrix is the column. + mat[, ]. + If you had an array of 9 floats ([9]f32) and you accessed the first one + It'd be the same as accessing the first colmun of a matrix. + array[1] = mat[1, 0] + + 3) Matrix multiplication order matters + // scale first then translate + transformation_a := translation * scale + // translate first then scale + transformation_b := scale * translation +*/ + import rl "libraries/raylib" import ui "libraries/snths_ui" import "core:mem" @@ -275,8 +302,10 @@ matrix_pool_get_mat3 :: proc() -> (value_strings: []InputFloatField, values: []f assert(program.matrix_count + 1 < matrix_pool_max(), "Max matrix count exceeded") value_strings = program.matrix_value_string_pool[program.matrix_count * 9 : program.matrix_count * 9 + 9] values = program.matrix_float_pool[program.matrix_count * 9 : program.matrix_count * 9 + 9] - for &str in value_strings { - str[0] = '0' + for i in 0.. mat3 { return { - 1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - translation.x, translation.y, 1.0, + 1.0, 0.0, translation.x, + 0.0, 1.0, translation.y, + 0.0, 0.0, 1.0, } } -matrix3_transform_v2 :: proc "contextless" (m: mat3, v: v2) -> v2 { +matrix3_scale2 :: proc "contextless" (scale: v2) -> mat3 { + return { + scale.x, 0.0, 0.0, + 0.0, scale.y, 0.0, + 0.0, 0.0, 1.0, + } +} + +matrix3_rotate2 :: proc "contextless" (ang: f32) -> mat3 { + a := linalg.cos(ang) + b := linalg.sin(ang) + return { + a, b, 0, + -b, a, 0, + 0, 0, 1, + } +} + +matrix3_transform_xy :: proc "contextless" (m: mat3, v: v2) -> v2 { return (m * v3{**v, 1.0}).xy }