diff --git a/.gitignore b/.gitignore index 07dfee8..ee01f9d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ cscope.out cscope.in.out cscope.po.out transformation_visualizer +libraries/raylib/linux/* +libraries/raylib/wasm/* diff --git a/grid.odin b/grid.odin index de23422..d0267bf 100644 --- a/grid.odin +++ b/grid.odin @@ -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 diff --git a/main.odin b/main.odin index d1554e7..7fb9ddf 100644 --- a/main.odin +++ b/main.odin @@ -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) diff --git a/program.odin b/program.odin index 2b34eef..8d6d092 100644 --- a/program.odin +++ b/program.odin @@ -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 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}}