Basic grid zooming

This commit is contained in:
Synthasmagoria 2026-08-23 04:16:17 +02:00
commit bd47c52111

View file

@ -93,18 +93,22 @@ UiElementData_Button :: struct {
UI_ALLOWED_FOCUS :: bit_set[ui.ElementType]{.InputFloat}
MatrixTypes :: enum {
Identity,
Translation,
Scale,
Rotation,
HotkeyTypes :: enum {
AddIdentity,
AddTranslation,
AddScale,
AddRotation,
ZoomXOnly,
ZoomYOnly,
}
@rodata hotkey_without_modifier := [MatrixTypes]rl.KeyboardKey {
.Identity = .I,
.Translation = .T,
.Scale = .S,
.Rotation = .R,
@rodata hotkey_without_modifier := [HotkeyTypes]rl.KeyboardKey {
.AddIdentity = .I,
.AddTranslation = .T,
.AddScale = .S,
.AddRotation = .R,
.ZoomXOnly = .X,
.ZoomYOnly = .Y,
}
HotkeyWithModifier :: enum {
@ -180,12 +184,14 @@ Program :: struct {
ui_overlay_queue_focus_first: bool,
matrix_float_pool: []f32,
matrix_value_string_pool: []InputFloatField,
matrix_create_dropdown_value: MatrixTypes,
matrix_create_dropdown_value: HotkeyTypes,
matrix_count: int,
matrix_scroll: f32,
matrix_scroll_speed: f32,
mouse_state: MouseState,
grid: Grid,
grid_zoom_x_only: bool,
grid_zoom_y_only: bool,
font_style: FontStyle,
state: ProgramState,
message_type: ProgramMessageType,
@ -213,9 +219,9 @@ AnimationState :: enum {
}
Grid :: struct {
area: rect2,
inner_area: rect2,
zoom: f32,
cell_size: v2,
zoom_previous, zoom: v2,
world_position, world_size, offset: v2,
}
float_slice_get_mat3 :: proc(floats: []f32, index: int) -> ^mat3 {
@ -275,9 +281,10 @@ program_init :: proc(allocator := context.allocator) {
spacing = 1.0
}
program.grid = {
area = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT},
inner_area = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT},
cell_size = 32.0,
zoom = 1.0,
world_position = 0.0,
world_size = {WINDOW_WIDTH, WINDOW_HEIGHT}
}
ui.init(&program.ui_context, 4000, ui_measure_text, &program.font_style, allocator)
@ -326,12 +333,12 @@ program_set_transformation_state :: proc(state: AnimationState) {
}
program_update :: proc() {
ui_elements, matrix_container := program_build_workspace_ui(&program.ui_context)
ui_elements, top_bar_container, matrix_container := program_build_workspace_ui(&program.ui_context)
ui_overlay_elements: []ui.Element
switch program.state {
case .Normal:
program.mouse_state = program_handle_mouse_workspace(program.mouse_state, matrix_container)
program.mouse_state = program_handle_mouse_workspace(program.mouse_state, top_bar_container, matrix_container)
if rl.IsKeyPressed(.TAB) {
program.ui_element_focus = ui.focus_next_element(program.ui_element_focus, ui_elements, UI_ALLOWED_FOCUS)
}
@ -348,19 +355,35 @@ program_update :: proc() {
} else {
for hotkey, index in hotkey_without_modifier {
if rl.IsKeyPressed(hotkey) {
switch index {
case .Identity:
#partial switch index {
case .AddIdentity:
program_add_identity_matrix()
program.ui_overlay_queue_focus_first = true
case .Translation:
case .AddTranslation:
program_show_dialog(&program, .CreateTranslationMatrix)
program.ui_overlay_queue_focus_first = true
case .Scale:
case .AddScale:
program_show_dialog(&program, .CreateScaleMatrix)
program.ui_overlay_queue_focus_first = true
case .Rotation:
case .AddRotation:
program_show_dialog(&program, .CreateRotationMatrix)
program.ui_overlay_queue_focus_first = true
case .ZoomXOnly:
if program.grid_zoom_x_only {
program.grid_zoom_x_only = false
program.grid_zoom_y_only = false
} else {
program.grid_zoom_x_only = true
program.grid_zoom_y_only = false
}
case .ZoomYOnly:
if program.grid_zoom_y_only {
program.grid_zoom_x_only = false
program.grid_zoom_y_only = false
} else {
program.grid_zoom_x_only = false
program.grid_zoom_y_only = true
}
}
}
}
@ -393,8 +416,32 @@ program_update :: proc() {
}
grid := program.grid
grid_inner_position := rect_get_tl(grid.inner_area)
draw_grid(grid.area, {32.0, 32.0}, rl.GRAY)
zoom_amount := grid_get_zoom_amount(grid.zoom)
world_to_grid_matrix := (
mat3 {
1, 0, 0,
0, 1, 0,
grid.offset.x, grid.offset.y, 1,
} *
linalg.matrix3_scale(v3{**zoom_amount, 1.0}))
grid_to_world_matrix := linalg.inverse(world_to_grid_matrix)
grid_world_br := grid.world_position + grid.world_size
grid_tl := (v3{**grid.world_position, 1.0} * world_to_grid_matrix).xy
grid_tl = linalg.floor(grid_tl / grid.cell_size) * grid.cell_size
grid_br := (v3{**grid_world_br, 1.0} * world_to_grid_matrix).xy
for x: f32 = grid_tl.x; x < grid_br.x; x += grid.cell_size.x {
world_position := (v3{x, 0.0, 1.0} * grid_to_world_matrix).x
p1 := v2{world_position, grid.world_position.y}
p2 := v2{world_position, grid_world_br.y}
rl.DrawLineV(p1, p2, {255, 255, 255, 128})
}
for y: f32 = grid_tl.y; y < grid_br.y; y += grid.cell_size.y {
world_position := (v3{0.0, y, 1.0} * grid_to_world_matrix).y
p1 := v2{grid.world_position.x, world_position}
p2 := v2{grid_world_br.x, world_position}
rl.DrawLineV(p1, p2, {255, 255, 255, 128})
}
if program.animation_state == .Playing {
program.animation_matrix_progress += program.animation_speed * delta_time_fraction()
@ -517,11 +564,12 @@ program_build_dialog_ui :: proc(ctx: ^ui.Context, dialog_type: ProgramDialogType
}
program_build_workspace_ui :: proc(ctx: ^ui.Context) -> (elements: []ui.Element, 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}, {WINDOW_WIDTH, WINDOW_HEIGHT})
top_bar_container_config := ui.ElementConfiguration {margin = 4.0}
top_bar_element_margin: f32 = 4.0
top_bar_container := ui.container_begin({ui.Size_Percentage(1.0), ui.Size_Pixels(48.0)}, top_bar_container_config)
top_bar_container = ui.container_begin({ui.Size_Percentage(1.0), ui.Size_Pixels(48.0)}, top_bar_container_config)
top_bar_inner_height := ui.get_element_inner_area(top_bar_container^).height
top_bar_button_dimensions := ui.Dimensions {ui.Size_Pixels(top_bar_inner_height), ui.Size_Pixels(top_bar_inner_height)}
top_bar_button_config := ui.ElementConfiguration {margin = top_bar_element_margin}
@ -595,22 +643,34 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) -> (elements: []ui.Element,
return
}
program_handle_mouse_workspace :: proc(mouse_state: MouseState, matrix_container: ^ui.Element) -> MouseState {
grid_get_zoom_amount :: proc(zoom: v2) -> v2 {
return linalg.pow(v2{2.0, 2.0}, zoom) - 1.0
}
program_handle_mouse_workspace :: proc(mouse_state: MouseState, top_bar_container, matrix_container: ^ui.Element) -> MouseState {
mouse := rl.GetMousePosition()
mouse_wheel := rl.GetMouseWheelMoveV()
switch mouse_state {
case .Hover:
if rl.IsMouseButtonPressed(.LEFT) {
return .DraggingGrid
} else if rect_has_point(rect2(matrix_container.area), mouse) {
if rect_has_point(rect2(matrix_container.area), mouse) {
return .HoverMatrices
}
if rect_has_point(rect2(top_bar_container.area), mouse) {
return mouse_state
}
if rl.IsMouseButtonPressed(.LEFT) {
return .DraggingGrid
}
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.01
case .DraggingGrid:
program.grid.inner_area = rect_translate(program.grid.inner_area, rl.GetMouseDelta())
program.grid.offset -= rl.GetMouseDelta()
if !rl.IsMouseButtonDown(.LEFT) {
return .Hover
}
case .HoverMatrices:
program.matrix_scroll = max(0, program.matrix_scroll - rl.GetMouseWheelMove() * program.matrix_scroll_speed)
program.matrix_scroll = max(0, program.matrix_scroll - mouse_wheel.y * program.matrix_scroll_speed)
if !rect_has_point(rect2(matrix_container.area), mouse) {
return .Hover
}