diff --git a/animation.odin b/animation.odin index 0145bb9..3f91543 100644 --- a/animation.odin +++ b/animation.odin @@ -40,6 +40,15 @@ rectangle_corners_lerp :: proc "contextless" (a, b: RectangleCorners, t: f32) -> } } +rectangle_corners_add :: proc "contextless" (corners: RectangleCorners, translation: v2) -> RectangleCorners { + return { + corners.tl + translation, + corners.tr + translation, + corners.br + translation, + corners.bl + translation, + } +} + rectangle_corners_move_corner :: proc "contextless" (corners: RectangleCorners, position: v2, corner: RectangleCornersCorner) -> RectangleCorners { corners := corners switch corner { @@ -147,6 +156,10 @@ animation_is_finished :: proc(animation: Animation) -> bool { return animation.matrix_index == f32(animation.matrix_count) } +animation_get_eased_progress :: proc(animation: Animation) -> f32 { + return math.floor(animation.matrix_index) + ease.ease(animation.ease, fract(animation.matrix_index)) +} + animation_draw :: proc(animation: Animation, global_transform: mat3) { switch animation.type { case .Rectangle: diff --git a/grid.odin b/grid.odin index ddbb3f0..d01b840 100644 --- a/grid.odin +++ b/grid.odin @@ -9,8 +9,12 @@ Grid :: struct { world_position, world_size, offset: v2, } +grid_get_zoom :: proc(zoom: v2) -> v2 { + return linalg.pow(v2{2.0, 2.0}, zoom) - 1.0 +} + grid_world_to_grid_matrix :: proc(zoom, offset: v2) -> mat3 { - zoom_amount := linalg.pow(v2{2.0, 2.0}, zoom) - 1.0 + zoom_amount := grid_get_zoom(zoom) return ( mat3 { 1, 0, 0, diff --git a/libraries/snths_ui/ui.odin b/libraries/snths_ui/ui.odin index 2a9eabf..cbd539d 100644 --- a/libraries/snths_ui/ui.odin +++ b/libraries/snths_ui/ui.odin @@ -55,6 +55,7 @@ ElementConfiguration :: struct { margin, padding: v2, } +// TODO (Synthas): Element type can be removed since it is implemented by the user ElementType :: enum { Container, Button, @@ -64,16 +65,7 @@ ElementType :: enum { Dropdown, Label, Divider, - Custom0, - Custom1, - Custom2, - Custom3, - Custom4, - Custom5, - Custom6, - Custom7, - Custom8, - Custom9, + Custom0, Custom1, Custom2, Custom3, Custom4, Custom5, Custom6, Custom7, Custom8, Custom9, } @rodata element_type_input := bit_set[ElementType] {.InputFloat} diff --git a/program.odin b/program.odin index d0f764c..ed3712a 100644 --- a/program.odin +++ b/program.odin @@ -185,6 +185,7 @@ ProgramAnimationState :: enum { MouseDraggableType :: enum { Point, RectangleCorner, + Rectangle, } MouseDraggable_Point :: struct { @@ -197,9 +198,14 @@ MouseDraggable_RectangleCornerData :: struct { corner_ref: ^v2, } +MouseDraggable_RectangleData :: struct { + corners: ^RectangleCorners, +} + MouseDraggable :: union { MouseDraggable_Point, MouseDraggable_RectangleCornerData, + MouseDraggable_RectangleData, } Program :: struct { @@ -446,6 +452,7 @@ program_update :: proc() { corner = .Br, corners_ref = &animation.shapes.rectangle, corner_ref = &animation.shapes.rectangle.br}) + append(&program.mouse_draggables, MouseDraggable_RectangleData{&animation.shapes.rectangle}) } } @@ -587,8 +594,10 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) -> ui.same_line() ui.element(.Button, button_dimensions, button_config, button_data) - divider_dimensions := ui.Dimensions{ui.Size_Pixels(4.0), ui.Size_Percentage(1.0)} - divider_config := ui.ElementConfiguration{margin = 8.0, color = ui.col(rl.WHITE)} + divider_dimensions := ui.Dimensions{ui.Size_Pixels(1.0), ui.Size_Percentage(1.0)} + // TODO (synthas): Use raygui border theme color for consistency + // TODO (synthas): Padding and margin doesn't work here for some reason + divider_config := ui.ElementConfiguration{padding = 4.0, color = ui.col(rl.WHITE)} ui.same_line() ui.element(.Divider, divider_dimensions, divider_config) @@ -638,24 +647,16 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_ world_to_grid_matrix := grid_world_to_grid_matrix(program.grid.zoom, program.grid.offset) grid_to_world_matrix := linalg.inverse(world_to_grid_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) - } - } + mouse_state := mouse_state - switch mouse_state { + state_label: switch mouse_state { case .Hover: if rect_has_point(rect2(matrix_container.area), mouse) { - return .HoverMatrices + mouse_state = .HoverMatrices + break state_label } if rect_has_point(rect2(top_bar_container.area), mouse) { - return mouse_state + break state_label } if rl.IsMouseButtonPressed(.LEFT) { for draggable in program.mouse_draggables { @@ -664,24 +665,36 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_ circle_position := (v3{**draggable.ref^, 1.0} * grid_to_world_matrix).xy if linalg.distance(mouse, circle_position) <= program.mouse_draggable_radius { program.mouse_current_draggable = draggable - return .DraggingDraggable + mouse_state = .DraggingDraggable + break state_label } case MouseDraggable_RectangleCornerData: circle_position := (v3{**draggable.corner_ref^, 1.0} * grid_to_world_matrix).xy if linalg.distance(mouse, circle_position) <= program.mouse_draggable_radius { program.mouse_current_draggable = draggable - return .DraggingDraggable + mouse_state = .DraggingDraggable + break state_label + } + case MouseDraggable_RectangleData: + corners_world := rectangle_corners_transform_mat3(draggable.corners^, grid_to_world_matrix) + rect := rect_from(corners_world.tl, corners_world.tr, corners_world.br, corners_world.bl) + if rect_has_point(rect, mouse) { + program.mouse_current_draggable = draggable + mouse_state = .DraggingDraggable + break state_label } } } - return .DraggingGrid + mouse_state = .DraggingGrid + break state_label } 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 .DraggingDraggable: if !rl.IsMouseButtonDown(.LEFT) { - return .Hover + mouse_state = .Hover + break state_label } draggable := program.mouse_current_draggable switch draggable in draggable { @@ -691,18 +704,36 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_ position := (v3{**mouse, 1.0} * world_to_grid_matrix).xy corners := rectangle_corners_move_corner(draggable.corners_ref^, position, draggable.corner) draggable.corners_ref^ = corners + case MouseDraggable_RectangleData: + mouse_delta_grid := rl.GetMouseDelta() * grid_get_zoom(program.grid.zoom) + draggable.corners^ = rectangle_corners_add(draggable.corners^, mouse_delta_grid) } case .DraggingGrid: program.grid.offset -= rl.GetMouseDelta() if !rl.IsMouseButtonDown(.LEFT) { - return .Hover + mouse_state = .Hover + break state_label } case .HoverMatrices: 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 + mouse_state = .Hover + break state_label } } + + 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 } @@ -736,7 +767,7 @@ program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: u rl.GuiGroupBox(area, element.label) case .Matrix: element_matrix_index := f32(data.matrix_index + 1) - animation_matrix_index := program.animation.matrix_index + animation_matrix_index := animation_get_eased_progress(program.animation) matrix_animation_distance := element_matrix_index - animation_matrix_index alpha := max(0.0, 1.0 - abs(matrix_animation_distance)) color := rl.Color{128, 255, 128, u8(alpha * 64.0)} @@ -863,7 +894,7 @@ program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: u case .Label: rl.DrawTextEx(font_style.font, element.label, rect_get_tl(area), font_style.size, font_style.spacing, color) case .Divider: - rl.DrawRectangleRec(area, color) + rl.DrawRectangleRec(rect2(ui.get_element_outer_area(element)), color) case: rl.DrawRectangleRec(area, rl.MAGENTA) } diff --git a/utils.odin b/utils.odin index 83546dd..ddebc77 100644 --- a/utils.odin +++ b/utils.odin @@ -163,7 +163,7 @@ rect_translate :: proc {rect2_translate, irect2_translate} } -rect_from :: proc {rect2_from_v2, irect2_from_iv2} +rect_from :: proc {rect2_from_v2, irect2_from_iv2, rect2_from_corners, irect2_from_corners} @(private="file") rect2_from_v2 :: proc "contextless" (position, size: v2) -> rect2 { return {position.x, position.y, size.x, size.y}