This commit is contained in:
Synthasmagoria 2026-08-28 08:23:29 +02:00
commit 5bc671936c
4 changed files with 58 additions and 45 deletions

2
.gitignore vendored
View file

@ -3,3 +3,5 @@ cscope.out
cscope.in.out
cscope.po.out
transformation_visualizer
libraries/raylib/linux/*
libraries/raylib/wasm/*

View file

@ -4,7 +4,7 @@ import "core:math/linalg"
import rl "libraries/raylib"
Grid :: struct {
cell_size: v2,
cell_size_px: v2,
zoom_previous, zoom: v2,
world_position, world_size, offset: v2,
}
@ -13,18 +13,18 @@ grid_get_zoom :: proc(zoom: v2) -> v2 {
return linalg.pow(v2(2.0), zoom)
}
grid_get_line_separation :: proc(zoom: v2, min_line_distance: f32) -> v2 {
grid_get_line_separation :: proc(zoom: v2, min_line_distance: v2) -> v2 {
zoom_factor := grid_get_zoom(zoom)
sep := linalg.pow(v2(10.0), linalg.floor(linalg.log10(min_line_distance * zoom_factor)))
for {
if sep.x / zoom_factor.x >= min_line_distance {
if sep.x / zoom_factor.x >= min_line_distance.x {
break
}
if sep.x * 2.5 / zoom_factor.x >= min_line_distance {
if sep.x * 2.5 / zoom_factor.x >= min_line_distance.x {
sep *= 2.5
break
}
if sep.x * 5.0 / zoom_factor.x >= min_line_distance {
if sep.x * 5.0 / zoom_factor.x >= min_line_distance.x {
sep *= 5.0
break
}
@ -44,15 +44,13 @@ grid_world_to_grid_matrix :: proc(zoom, offset: v2) -> mat3 {
linalg.matrix3_scale(v3{**zoom_amount, 1.0}))
}
import "core:fmt"
draw_grid :: proc(grid: Grid, text_draw, text_clip: rect2) {
world_to_grid_matrix := grid_world_to_grid_matrix(grid.zoom, grid.offset)
grid_to_world_matrix := linalg.inverse(world_to_grid_matrix)
grid_world_br := grid.world_position + grid.world_size
grid_color := rl.Color{64, 64, 64, 255}
sep := grid_get_line_separation(grid.zoom, 32.0)
sep := grid_get_line_separation(grid.zoom, grid.cell_size_px)
tl := (v3{**grid.world_position, 1.0} * world_to_grid_matrix).xy
tl = linalg.floor(tl / sep) * sep
br := (v3{**grid_world_br, 1.0} * world_to_grid_matrix).xy

View file

@ -9,7 +9,7 @@ WINDOW_FRAMERATE :: 60
WINDOW_CAPTION :: "Transformation Visualizer"
init :: proc() {
rl.SetTraceLogLevel(.INFO)
rl.SetTraceLogLevel(.WARNING)
rl.SetConfigFlags({.VSYNC_HINT})
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION)
rl.SetTargetFPS(WINDOW_FRAMERATE)

View file

@ -7,6 +7,14 @@ import "core:fmt"
import "core:math/linalg"
import "core:strings"
RAYGUI_ICON_NEW :: "#009#"
RAYGUI_ICON_SAVE :: "#002#"
RAYGUI_ICON_SAVE_AS :: "#001#"
RAYGUI_ICON_LOAD :: "#006#"
RAYGUI_ICON_IDENTITY_MATRIX :: "#000#"
RAYGUI_ICON_TRANSLATION_MATRIX :: "#068#"
RAYGUI_ICON_SCALE_MATRIX :: "#070#"
RAYGUI_ICON_ROTATION_MATRIX :: "#078#"
RAYGUI_ICON_PLAY :: "#131#"
RAYGUI_ICON_PAUSE :: "#132#"
RAYGUI_ICON_RESET :: "#211#"
@ -117,13 +125,13 @@ HotkeyTypes :: enum {
}
HotkeyWithModifier :: enum {
Save,
Open,
ProjectSave,
ProjectLoad,
}
@rodata hotkey_with_modifier := [HotkeyWithModifier]rl.KeyboardKey {
.Save = .S,
.Open = .O,
.ProjectSave = .S,
.ProjectLoad = .O,
}
FontStyle :: struct {
@ -133,7 +141,6 @@ FontStyle :: struct {
ProgramState :: enum {
Normal,
Message,
Dialog,
}
@ -336,7 +343,8 @@ program_init :: proc(allocator := context.allocator) {
program.matrix_value_string_pool = make(type_of(program.matrix_value_string_pool), MATRIX_POOL_SIZE)
program.matrix_scroll_speed = 32.0
font := rl.LoadFontEx("assets/fonts/cmuntb.ttf", 24.0, nil, 0)
@static @rodata font_data := #load("assets/fonts/cmuntb.ttf")
font := rl.LoadFontFromMemory(".ttf", raw_data(font_data), i32(len(font_data)), 24.0, nil, 0)
program.font_style = {
font = font,
size = 24.0,
@ -347,7 +355,7 @@ program_init :: proc(allocator := context.allocator) {
rl.GuiSetStyle(.DEFAULT, i32(rl.GuiDefaultProperty.TEXT_SPACING), i32(program.font_style.spacing))
program.grid = {
cell_size = 32.0,
cell_size_px = 40.0,
zoom = 1.0,
world_position = 0.0,
world_size = program_screen_size()
@ -410,9 +418,9 @@ program_update :: proc() {
for hotkey, index in hotkey_with_modifier {
if rl.IsKeyPressed(hotkey) {
switch index {
case .Save:
case .ProjectSave:
program_save_project_from_project_string_or_open_dialog(&program)
case .Open:
case .ProjectLoad:
program_show_dialog(&program, .LoadProject)
}
}
@ -449,7 +457,6 @@ program_update :: proc() {
}
}
}
case .Message:
case .Dialog:
ui_overlay_elements = program_build_dialog_ui(&program.ui_overlay_context, program.dialog_type)
if rl.IsKeyPressed(.TAB) {
@ -525,6 +532,19 @@ program_update :: proc() {
}
}
grid_to_world_matrix := linalg.inverse(grid_world_to_grid_matrix(program.grid.zoom, program.grid.offset))
for draggable in program.mouse_draggables {
switch draggable in draggable {
case MouseDraggable_Point:
position_world := (v3{**draggable.ref^, 1.0} * grid_to_world_matrix).xy
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleCornerData:
position_world := (v3{**draggable.corner_ref^, 1.0} * grid_to_world_matrix).xy
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleData:
}
}
ui_element_focus := program.state == .Normal ? program.ui_element_focus : ui.element_id_invalid()
program.ui_element_focus = program_render_ui_and_handle_focus(&program.ui_context, ui_element_focus, program.font_style, ui_elements)
@ -645,8 +665,7 @@ program_screen_size :: proc() -> v2 {
return {f32(rl.GetScreenWidth()), f32(rl.GetScreenHeight())}
}
program_build_workspace_ui :: proc(ctx: ^ui.Context) ->
(elements: []ui.Element, top_bar_container, matrix_container: ^ui.Element) {
program_build_workspace_ui :: proc(ctx: ^ui.Context) -> (elements: []ui.Element, top_bar_container, matrix_container: ^ui.Element) {
ui.start(ctx, {0, 0}, program_screen_size())
top_bar_container_config := ui.ElementConfiguration {margin = 4.0}
top_bar_element_margin: f32 = 4.0
@ -659,30 +678,33 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) ->
{
button_margin := top_bar_element_margin
button_config: ui.ElementConfiguration
button_tooltip: cstring
button_dimensions := top_bar_button_dimensions
button_data: ^UiElementData_Button
divider_dimensions := ui.Dimensions{ui.Size_Pixels(1.0), ui.Size_Percentage(1.0)}
divider_config := ui.ElementConfiguration{padding = 4.0, color = ui.col(rl.WHITE)}
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "S", tooltip = "Save project\n(ctrl+s)"}
button_tooltip = fmt.ctprint("Save project\n(CTRL+", hotkey_with_modifier[.ProjectSave], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_SAVE, tooltip = button_tooltip}
button_data^ = {type = .ProjectSave}
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "SA", tooltip = "Save project as..."}
button_config = {margin = button_margin, label = RAYGUI_ICON_SAVE_AS, tooltip = "Save project as..."}
button_data^ = {type = .ProjectSaveAs}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "L", tooltip = "Load project\n(ctrl+o)"}
button_tooltip = fmt.ctprint("Load project\n(CTRL+", hotkey_with_modifier[.ProjectLoad], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_LOAD, tooltip = button_tooltip}
button_data^ = {type = .ProjectLoad}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "N", tooltip = "New project"}
button_config = {margin = button_margin, label = RAYGUI_ICON_NEW, tooltip = "New project"}
button_data^ = {type = .ProjectNew}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
@ -691,26 +713,29 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) ->
ui.element(.Divider, divider_dimensions, divider_config)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "I", tooltip = "Add identity\nmatrix"}
button_tooltip = fmt.ctprint("Add identity\nmatrix (", hotkey_without_modifier[.AddIdentity], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_IDENTITY_MATRIX, tooltip = button_tooltip}
button_data^ = {type = .AddIdentity}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config.label = "T"
button_config = {margin = button_margin, label = "T", tooltip = "Add translation\nmatrix"}
button_tooltip = fmt.ctprint("Add translation\nmatrix (", hotkey_without_modifier[.AddTranslation], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_TRANSLATION_MATRIX, tooltip = button_tooltip}
button_data^ = {type = .AddTranslation}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "S", tooltip = "Add scale\nmatrix"}
button_tooltip = fmt.ctprint("Add scale\nmatrix (", hotkey_without_modifier[.AddScale], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_SCALE_MATRIX, tooltip = button_tooltip}
button_data^ = {type = .AddScale}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator)
button_config = {margin = button_margin, label = "R", tooltip = "Add rotation\nmatrix"}
button_tooltip = fmt.ctprint("Add rotation\nmatrix (", hotkey_without_modifier[.AddIdentity], ")", sep = "")
button_config = {margin = button_margin, label = RAYGUI_ICON_ROTATION_MATRIX, tooltip = button_tooltip}
button_data^ = {type = .AddRotation}
ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data)
@ -815,7 +840,7 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
}
program.grid.zoom_previous = program.grid.zoom
grid_zoom_dimension_lock := v2{1.0 - f32(int(program.grid_zoom_y_only)), 1.0 - f32(int(program.grid_zoom_x_only))}
program.grid.zoom += grid_zoom_dimension_lock * mouse_wheel.y * 0.2 * program.grid_zoom_speed
program.grid.zoom -= grid_zoom_dimension_lock * mouse_wheel.y * 0.2 * program.grid_zoom_speed
case .DraggingDraggable:
if !rl.IsMouseButtonDown(.LEFT) {
mouse_state = .Hover
@ -847,18 +872,6 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
}
}
for draggable in program.mouse_draggables {
switch draggable in draggable {
case MouseDraggable_Point:
position_world := (v3{**draggable.ref^, 1.0} * grid_to_world_matrix).xy
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleCornerData:
position_world := (v3{**draggable.corner_ref^, 1.0} * grid_to_world_matrix).xy
rl.DrawCircleV(position_world, program.mouse_draggable_radius, ANIMATION_COLOR_ORIGIN)
case MouseDraggable_RectangleData:
}
}
return mouse_state
}
@ -1136,8 +1149,8 @@ ui_mat3 :: proc(idx: int, mat: []f32, value_strings: []InputFloatField, dimensio
matrix_input_dimensions := ui.Dimensions {ui.Size_Percentage(1.0 / 3.0), ui.Size_Percentage(1.0 / 3.0)}
matrix_input_config := ui.ElementConfiguration {margin = 4.0}
// column first matrices
#unroll for j in 0..<3 {
#unroll for i in 0..<3 {
#unroll for j in 0..<3 {
data := new(UiElementData_FloatInput, allocator)
index := i * 3 + j
data^ = {value = &mat[index], value_string = cstring(&value_strings[index][0]), tag = {.UpdateMatrix}}