grid snap

This commit is contained in:
Synthasmagoria 2026-09-15 15:00:11 +02:00
commit 5b01ad22f4
6 changed files with 66 additions and 13 deletions

View file

@ -2,12 +2,12 @@
- Web read/write - Web read/write
- Ship with default setup - Ship with default setup
- Fix -vet errors - Fix -vet errors
- Snap to grid option
- Set grid - Set grid
- Set animation value
DONE: DONE:
- When there is no matrix it should still draw the rectangle - When there is no matrix it should still draw the rectangle
- Always show coordinates - Always show coordinates
- Web build - Web build
- Escape closes dialogue if on dialogue. Otherwise stops program if on desktop, but not without an unsaved changes warning. - Escape closes dialogue if on dialogue. Otherwise stops program if on desktop, but not without an unsaved changes warning.
- Set animation value
- Snap to grid option

BIN
default

Binary file not shown.

View file

@ -72,10 +72,6 @@ rectangle_corners_move_corner :: proc "contextless" (corners: RectangleCorners,
return corners return corners
} }
rectangle_corners_transform_mat2 :: proc "contextless" (corners: RectangleCorners, transform: mat2) -> RectangleCorners {
return {corners.tl * transform, corners.tr * transform, corners.br * transform, corners.bl * transform}
}
draw_animation_rectangle_corners :: proc(corners: RectangleCorners, color: rl.Color) { draw_animation_rectangle_corners :: proc(corners: RectangleCorners, color: rl.Color) {
triangles := rectangle_corners_get_triangles(corners) triangles := rectangle_corners_get_triangles(corners)
draw_triangle(triangles[0], {**color.rgb, color.a >> 1}) draw_triangle(triangles[0], {**color.rgb, color.a >> 1})

View file

@ -66,6 +66,7 @@ ElementType :: enum {
ToggleGroup, ToggleGroup,
Label, Label,
Divider, Divider,
Toggle,
Custom0, Custom1, Custom2, Custom3, Custom4, Custom5, Custom6, Custom7, Custom8, Custom9, Custom0, Custom1, Custom2, Custom3, Custom4, Custom5, Custom6, Custom7, Custom8, Custom9,
} }

View file

@ -10,7 +10,7 @@ WINDOW_CAPTION :: "Transformation Visualizer"
init :: proc() { init :: proc() {
rl.SetTraceLogLevel(.WARNING) rl.SetTraceLogLevel(.WARNING)
rl.SetConfigFlags({.VSYNC_HINT}) rl.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE})
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION) rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION)
rl.SetTargetFPS(WINDOW_FRAMERATE) rl.SetTargetFPS(WINDOW_FRAMERATE)
rl.SetExitKey(.KEY_NULL) rl.SetExitKey(.KEY_NULL)

View file

@ -50,6 +50,7 @@ RAYGUI_ICON_RESET :: "#211#"
RAYGUI_ICON_TRASH :: "#143#" RAYGUI_ICON_TRASH :: "#143#"
RAYGUI_ICON_DOWN :: "#120#" RAYGUI_ICON_DOWN :: "#120#"
RAYGUI_ICON_UP :: "#121#" RAYGUI_ICON_UP :: "#121#"
RAYGUI_ICON_SNAP :: "#136#"
TRANSFORMATION_RECTANGLE_COLOR_ORIGIN :: rl.RED TRANSFORMATION_RECTANGLE_COLOR_ORIGIN :: rl.RED
TRANSFORMATION_RECTANGLE_COLOR_MIDDLE :: rl.MAGENTA TRANSFORMATION_RECTANGLE_COLOR_MIDDLE :: rl.MAGENTA
@ -87,6 +88,10 @@ UiElementData_TextInput :: struct {
str: cstring, str: cstring,
} }
UiElementData_Toggle :: struct {
value: ^bool,
}
UiElementData_ToggleGroup :: struct { UiElementData_ToggleGroup :: struct {
value: ^i32, value: ^i32,
} }
@ -290,6 +295,7 @@ Program :: struct {
grid_zoom_x_only: bool, grid_zoom_x_only: bool,
grid_zoom_y_only: bool, grid_zoom_y_only: bool,
grid_zoom_speed: f32, grid_zoom_speed: f32,
grid_snap: bool,
font_style: FontStyle, font_style: FontStyle,
state: ProgramState, state: ProgramState,
message_type: ProgramMessageType, message_type: ProgramMessageType,
@ -300,6 +306,7 @@ Program :: struct {
animation_state: ProgramAnimationState, animation_state: ProgramAnimationState,
animation_speed: f32, animation_speed: f32,
animation_mode: AnimationMode, animation_mode: AnimationMode,
animation_progress_string: [DIALOG_STATE_TEXT_INPUT_FIELD_COUNT]byte,
window_should_close: bool, window_should_close: bool,
} }
@ -609,6 +616,7 @@ program_update :: proc() {
animation := &program.animation animation := &program.animation
if program.animation_state == .Playing { if program.animation_state == .Playing {
write_float_to_buffer(program.animation_progress_string[:], animation.matrix_index)
animation.matrix_index += 0.01 animation.matrix_index += 0.01
} }
@ -934,18 +942,43 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) -> (elements: []ui.Element,
ui.same_line() ui.same_line()
ui.element(.Slider, slider_dimensions, slider_config, slider_data) ui.element(.Slider, slider_dimensions, slider_config, slider_data)
float_config: ui.ElementConfiguration
float_data: ^UiElementData_FloatInput
float_dimensions := ui.Dimensions{top_bar_button_width * 2.0, ui.Size_Percentage(1.0)}
float_config = {margin = top_bar_element_margin * 2}
float_data = new(UiElementData_FloatInput, context.temp_allocator)
float_data^ = {
value = &program.animation.matrix_index,
value_string = transmute(cstring)&program.animation_progress_string,
}
ui.same_line()
ui.element(.InputFloat, float_dimensions, float_config, float_data)
toggle_group_config: ui.ElementConfiguration toggle_group_config: ui.ElementConfiguration
toggle_group_dimensions := ui.Dimensions{top_bar_button_width * 3.0, ui.Size_Percentage(1.0)} toggle_group_dimensions := ui.Dimensions{top_bar_button_width * 3.0, ui.Size_Percentage(1.0)}
toggle_group_data: ^UiElementData_ToggleGroup toggle_group_data: ^UiElementData_ToggleGroup
toggle_group_config = { toggle_group_config = {
margin = button_margin, margin = top_bar_element_margin * 2,
label = enum_to_raygui_options_string(AnimationMode, context.temp_allocator), label = enum_to_raygui_options_string(AnimationMode, context.temp_allocator),
tooltip = "Play animation\nstep by step;Play animation using the\nfull transformation matrix"} tooltip = "Play animation\nstep by step;Play animation using the\nfull transformation matrix"}
toggle_group_data = new(UiElementData_ToggleGroup, context.temp_allocator) toggle_group_data = new(UiElementData_ToggleGroup, context.temp_allocator)
toggle_group_data^ = {value = cast(^i32)&program.animation_mode} toggle_group_data^ = {value = cast(^i32)&program.animation_mode}
ui.same_line() ui.same_line()
ui.element(.ToggleGroup, toggle_group_dimensions, toggle_group_config, toggle_group_data) ui.element(.ToggleGroup, toggle_group_dimensions, toggle_group_config, toggle_group_data)
ui.same_line()
ui.element(.Divider, divider_dimensions, divider_config)
toggle_config: ui.ElementConfiguration
toggle_dimensions := ui.Dimensions{top_bar_button_width, ui.Size_Percentage(1.0)}
toggle_data: ^UiElementData_Toggle
toggle_config = {margin = top_bar_element_margin, label = RAYGUI_ICON_SNAP, tooltip = "Snap to grid"}
toggle_data = new(UiElementData_Toggle, context.temp_allocator)
toggle_data^ = {value = &program.grid_snap}
ui.same_line()
ui.element(.Toggle, toggle_dimensions, toggle_config, toggle_data)
} }
ui.container_end() ui.container_end()
@ -1021,16 +1054,31 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
break state_label break state_label
} }
draggable := program.mouse_current_draggable draggable := program.mouse_current_draggable
position := matrix3_transform_xy(world_to_grid_matrix, mouse)
drag_to_position := matrix3_transform_xy(world_to_grid_matrix, mouse)
if program.grid_snap {
grid_line_sep := grid_get_line_separation(program.grid.zoom, program.grid.cell_size_px)
drag_to_position = linalg.floor(drag_to_position / grid_line_sep) * grid_line_sep
}
switch draggable in draggable { switch draggable in draggable {
case MouseDraggable_Point: case MouseDraggable_Point:
draggable.ref^ = matrix3_transform_xy(world_to_grid_matrix, mouse) draggable.ref^ = drag_to_position
case MouseDraggable_RectangleCornerData: case MouseDraggable_RectangleCornerData:
position := matrix3_transform_xy(world_to_grid_matrix, mouse) corners := rectangle_corners_move_corner(draggable.corners_ref^, drag_to_position, draggable.corner)
corners := rectangle_corners_move_corner(draggable.corners_ref^, position, draggable.corner)
draggable.corners_ref^ = corners draggable.corners_ref^ = corners
case MouseDraggable_RectangleData: case MouseDraggable_RectangleData:
mouse_delta_grid := rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom) if program.grid_snap {
draggable.corners^ = rectangle_corners_add(draggable.corners^, mouse_delta_grid)
prev_tl := draggable.corners.tl
draggable.corners.tl = drag_to_position
draggable.corners.tr += draggable.corners.tl - prev_tl
draggable.corners.br += draggable.corners.tl - prev_tl
draggable.corners.bl += draggable.corners.tl - prev_tl
} else {
mouse_delta_grid := rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom)
draggable.corners^ = rectangle_corners_add(draggable.corners^, mouse_delta_grid)
}
} }
case .DraggingGrid: case .DraggingGrid:
program.grid.offset -= rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom) program.grid.offset -= rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom)
@ -1244,6 +1292,14 @@ program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: u
tooltip_text = strings.clone_to_cstring(tooltips_split[tooltip_index], context.temp_allocator) tooltip_text = strings.clone_to_cstring(tooltips_split[tooltip_index], context.temp_allocator)
tooltip_position = mouse tooltip_position = mouse
} }
case .Toggle:
data := cast(^UiElementData_Toggle)element.data
rl.GuiToggle(area, element.label, data.value)
mouse := rl.GetMousePosition()
if rect_has_point(area, mouse) {
tooltip_text = element.tooltip
tooltip_position = mouse
}
case: case:
rl.DrawRectangleRec(area, rl.MAGENTA) rl.DrawRectangleRec(area, rl.MAGENTA)
} }