Grid matrix math fix

This commit is contained in:
Synthasmagoria 2026-08-30 14:01:17 +02:00
commit c0d84bae43
4 changed files with 80 additions and 34 deletions

View file

@ -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),
}
}

View file

@ -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)
}

View file

@ -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[<column>, <row>].
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..<len(value_strings) {
mem.set(raw_data(&value_strings[i]), 0, len(InputFloatField))
value_strings[i][0] = '0'
values[i] = 0.0
}
program.matrix_count += 1
return value_strings, values
@ -508,7 +537,7 @@ program_update :: proc() {
matrix_container_br := rect_get_br(rect2(matrix_container.area))
grid_text_clip := rect_from_area(v2{matrix_container_br.x, top_bar_container_br.y}, program_screen_size())
grid_area := rect2{0.0, 0.0, **program_screen_size()}
draw_grid(program.grid, grid_area, rect_grow(grid_text_clip, -4.0), rect_grow(grid_text_clip, -32.0))
draw_grid(program.grid, grid_area, rect_grow(grid_text_clip, -4.0), rect_grow(grid_text_clip, -32.0), {255, 255, 255, 64})
{
animation := &program.animation
@ -545,10 +574,10 @@ program_update :: proc() {
for draggable in program.mouse_draggables {
switch draggable in draggable {
case MouseDraggable_Point:
position_world := matrix3_transform_v2(grid_to_world_matrix, draggable.ref^)
position_world := matrix3_transform_xy(grid_to_world_matrix, draggable.ref^)
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleCornerData:
position_world := matrix3_transform_v2(grid_to_world_matrix, draggable.corner_ref^)
position_world := matrix3_transform_xy(grid_to_world_matrix, draggable.corner_ref^)
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleData:
}
@ -611,15 +640,15 @@ program_add_rotation_matrix :: proc(angle: f32) {
a := linalg.cos(radians)
b := linalg.sin(radians)
fmt.bprint(value_strings[0][:], a)
fmt.bprintf(value_strings[0][:], "%.3f", a)
values[0] = a
fmt.bprint(value_strings[1][:], -b)
values[1] = b
fmt.bprint(value_strings[3][:], b)
values[3] = a
fmt.bprint(value_strings[4][:], a)
fmt.bprintf(value_strings[1][:], "%.3f", -b)
values[1] = -b
fmt.bprintf(value_strings[3][:], "%.3f", b)
values[3] = b
fmt.bprintf(value_strings[4][:], "%.3f", a)
values[4] = a
fmt.bprint(value_strings[8][:], 1)
fmt.bprint(value_strings[8][:], f32(1.0))
values[8] = 1.0
// rotmat := linalg.matrix2_rotate_f32(angle)
@ -861,14 +890,14 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
for draggable in program.mouse_draggables {
switch draggable in draggable {
case MouseDraggable_Point:
circle_position := matrix3_transform_v2(grid_to_world_matrix, draggable.ref^)
circle_position := matrix3_transform_xy(grid_to_world_matrix, draggable.ref^)
if linalg.distance(mouse, circle_position) <= program.mouse_draggable_radius {
program.mouse_current_draggable = draggable
mouse_state = .DraggingDraggable
break state_label
}
case MouseDraggable_RectangleCornerData:
circle_position := matrix3_transform_v2(grid_to_world_matrix, draggable.corner_ref^)
circle_position := matrix3_transform_xy(grid_to_world_matrix, draggable.corner_ref^)
if linalg.distance(mouse, circle_position) <= program.mouse_draggable_radius {
program.mouse_current_draggable = draggable
mouse_state = .DraggingDraggable
@ -898,9 +927,9 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
draggable := program.mouse_current_draggable
switch draggable in draggable {
case MouseDraggable_Point:
draggable.ref^ = matrix3_transform_v2(world_to_grid_matrix, mouse)
draggable.ref^ = matrix3_transform_xy(world_to_grid_matrix, mouse)
case MouseDraggable_RectangleCornerData:
position := matrix3_transform_v2(world_to_grid_matrix, mouse)
position := matrix3_transform_xy(world_to_grid_matrix, mouse)
corners := rectangle_corners_move_corner(draggable.corners_ref^, position, draggable.corner)
draggable.corners_ref^ = corners
case MouseDraggable_RectangleData:
@ -908,7 +937,7 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
draggable.corners^ = rectangle_corners_add(draggable.corners^, mouse_delta_grid)
}
case .DraggingGrid:
program.grid.offset -= rl.GetMouseDelta()
program.grid.offset -= rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom)
if !rl.IsMouseButtonDown(.LEFT) {
mouse_state = .Hover
break state_label

View file

@ -289,13 +289,31 @@ rect_get_size :: proc {rect2_get_size, irect2_get_size}
/// ## LINEAR ALGEBRA ## ///
matrix3_translate2 :: proc "contextless" (translation: v2) -> 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
}