From e165bd649441f448217f1c07d5cafbfa7e72ba1b Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Thu, 3 Jul 2025 21:23:06 +0200 Subject: FUCK YES --- build.bat | 2 +- res/RedHatMono.ttf | Bin 0 -> 34520 bytes src/main.odin | 492 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 441 insertions(+), 53 deletions(-) create mode 100644 res/RedHatMono.ttf diff --git a/build.bat b/build.bat index 9fb4372..29391c1 100644 --- a/build.bat +++ b/build.bat @@ -1 +1 @@ -odin run src/ \ No newline at end of file +odin run src/ -- 0 \ No newline at end of file diff --git a/res/RedHatMono.ttf b/res/RedHatMono.ttf new file mode 100644 index 0000000..3862c31 Binary files /dev/null and b/res/RedHatMono.ttf differ diff --git a/src/main.odin b/src/main.odin index dacbc87..11cea8e 100644 --- a/src/main.odin +++ b/src/main.odin @@ -6,15 +6,18 @@ import "core:math" import "core:os" import "core:strconv" import "core:reflect" +import "core:strings" -waveform_a : []f32 : {0.0, 0.1, 0.5, 0.3, 0.1, -0.1, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.0} -waveform_b : []f32 : {0.0, 0.0, 0.0, 0.2, 0.4, 0.2, -0.1, 0.0, 0.0, 0.1, 0.3, 0.1, 0.0} +MONO_FONT :: #load("../res/RedHatMono.ttf") +font : rl.Font +BLACK : rl.Color = {16, 16, 16, 255} YELLOW : rl.Color = {254, 175, 1, 255} ORANGE : rl.Color = {238, 123, 26, 255} RED : rl.Color = {222, 73, 50, 255} -LERP_SPEED :: 0.20 +LERP_SPEED :: 0.10 +HIGHLIGHT_THICKNESS :: 4 layout :: struct { wave_a_start : rl.Vector2, @@ -26,31 +29,272 @@ layout :: struct { wave_b_amp : f32, grid_opacity : f32, - grid_highlight_opacity : f32, - grid_highlight_position : rl.Vector2, + grid_data_opacity : f32, + highlight_opacity : f32, + highlight_index : int, + highlight_position : rl.Vector2, + highlight_dimensions : rl.Vector2, + arrow_thickness : f32, + arrow_chopping : f32, + heatmap_opacity : f32, + + billing : f32, + + camera_position : rl.Vector2, + camera_zoom : f32, + + grid_start : rl.Vector2 + grid_end : rl.Vector2 } +waveform_a := [?]f32{0.0, 0.1, 0.5, 0.3, 0.1, -0.1, 0.0, 0.0, 0.0, 0.1, 0.3, 0.0} +waveform_b := [?]f32{0.0, 0.0, 0.0, 0.2, 0.4, 0.2, -0.1, 0.0, 0.1, 0.3, 0.1, 0.0} + +data_frames : [dynamic][len(waveform_a)*len(waveform_b)]cell + + + slides : [dynamic]layout state : layout target : layout +data_state : [len(waveform_a)*len(waveform_b)]cell +data_target : [len(waveform_a)*len(waveform_b)]cell delta : f32 + + + init_slides :: proc() { - // 0 - current_slide : layout = { - wave_a_start = {-600, -250}, - wave_a_end = {600, -250}, - wave_a_amp = 250, + // ---------------------------- Waveforms + c : layout = { + wave_a_start = {-600, -200}, + wave_a_end = {600, -200}, + wave_a_amp = 260, - wave_b_start = {-600, 250}, - wave_b_end = {600, 250}, - wave_b_amp = 250, + wave_b_start = {-600, 200}, + wave_b_end = {600, 200}, + wave_b_amp = 260, grid_opacity = 0, - grid_highlight_opacity = 0, - grid_highlight_position = 0, + grid_data_opacity = 0, + highlight_opacity = 0, + highlight_position = 0, + arrow_thickness = 3, + arrow_chopping = 0.3, + + heatmap_opacity = 0.5, + + billing = f32(len(waveform_a)), + + grid_start = {-400, 350}, + grid_end = { 400, -450}, + } + d : [12*12]cell + highlight_rect := rect_from_index(0, HIGHLIGHT_THICKNESS) + c.highlight_dimensions = {highlight_rect.width, highlight_rect.height} + c.highlight_position = {highlight_rect.x, highlight_rect.y} + + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Move waveforms into place + c.wave_a_start = {grid_start.x, grid_start.y+120} + c.wave_a_end = {grid_end.x , grid_start.y+120} + c.wave_b_start = {grid_start.x-120, grid_start.y} + c.wave_b_end = {grid_start.x-120, grid_end.y} + c.wave_a_amp = 120 + c.wave_b_amp = 130 + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Show grid + c.grid_opacity = 1 + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Show data numbers + c.grid_data_opacity = 1 + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Infinity coating + for _, i in waveform_a { + if i==0 do continue + d[i_from_xy(i, 0)].cost = math.INF_F32 + d[i_from_xy(0, i)].cost = math.INF_F32 } - append(&slides, current_slide) + append(&slides, c) + append(&data_frames, d) + + + + // ---------------------------- FIRST CELL + d[i_from_xy(1,1)].cost = distance(waveform_a[1], waveform_b[1]) + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Detecting minimum direction for first cost calculation + d[i_from_xy(1,1)].direction = .DIAGONAL + append(&slides, c) + append(&data_frames, d) + + + + // ---------------------------- SECOND CELL + d[i_from_xy(2,1)].cost = distance(waveform_a[2], waveform_b[1]) + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Detecting minimum direction for second cost calculation + + d[i_from_xy(2,1)].direction = .LEFT + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Adding previous minimum to second cost calculation + d[i_from_xy(2,1)].cost += d[i_from_xy(1,1)].cost + append(&slides, c) + append(&data_frames, d) + + + + // ---------------------------- THIRD CELL + d[i_from_xy(1,2)].cost += distance(waveform_a[1], waveform_b[2]) + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Detect minimum + d[i_from_xy(1,2)].direction = .DOWN + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Add minimum + d[i_from_xy(1,2)].cost += d[i_from_xy(1,1)].cost + append(&slides, c) + append(&data_frames, d) + + + + // ---------------------------- FOURTH CELL + d[i_from_xy(2,2)].cost += distance(waveform_a[2], waveform_b[2]) + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- Detect minimum + a_cost, a_direction := three_min(d[i_from_xy(1,2)], + d[i_from_xy(1,1)], + d[i_from_xy(2,1)]) + d[i_from_xy(2,2)].cost += a_cost + d[i_from_xy(2,2)].direction = a_direction + append(&slides, c) + append(&data_frames, d) + + // ---------------------------- FIFTH CELL + full_calc(d[:], 1, 3) + append(&slides, c) + append(&data_frames, d) + + + // ---------------------------- SIXTH CELL + full_calc(d[:], 3, 1) + append(&slides, c) + append(&data_frames, d) + + + // ---------------------------- WAVEFRONT first half + for i in 4.. rl.Rectangle { + // rect is top left corener and dimensions + width := abs(grid_end.x - grid_start.x) + height := abs(grid_end.y - grid_start.y) + intergrid_dimensions : rl.Vector2 = {width, height} + cell_dimensions := intergrid_dimensions/f32(len(waveform_a)-1) + position_zero := grid_start-(cell_dimensions/2) + output := position_zero + cell_dimensions * {f32(i%len(waveform_a)), -f32(i/len(waveform_b))} + thickness_offset := thickness/2 + return {output.x-thickness_offset, output.y-thickness_offset, cell_dimensions.x+thickness_offset, cell_dimensions.y+thickness_offset} +} + +i_from_xy :: proc(x : int, y : int) -> int { + return y * len(waveform_a) + x } lerp :: proc() { @@ -75,24 +319,41 @@ lerp :: proc() { // Type switch to lerp and write directly back to state switch field_type.id { - case typeid_of(f32): - from_val := (cast(^f32)state_ptr)^ - to_val := (cast(^f32)target_ptr)^ - (cast(^f32)state_ptr)^ = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)) - - case typeid_of(rl.Vector2): - from_val := (cast(^rl.Vector2)state_ptr)^ - to_val := (cast(^rl.Vector2)target_ptr)^ - (cast(^rl.Vector2)state_ptr)^ = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)) + case typeid_of(f32): + from_val := (cast(^f32)state_ptr)^ + to_val := (cast(^f32)target_ptr)^ + (cast(^f32)state_ptr)^ = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)) + + case typeid_of(rl.Vector2): + from_val := (cast(^rl.Vector2)state_ptr)^ + to_val := (cast(^rl.Vector2)target_ptr)^ + (cast(^rl.Vector2)state_ptr)^ = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)) } } + + for cell, i in data_state { + + from_val := data_state[i].cost + to_val := data_target[i].cost + if data_target[i].cost != math.INF_F32 && data_state[i].cost != math.nan_f32() { + data_state[i].cost = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)*0.9) + } else { + data_state[i].cost = data_target[i].cost + } + + from_val = data_state[i].stepped_in + to_val = data_target[i].stepped_in + data_state[i].stepped_in = from_val + (to_val - from_val) * (1.0 - math.pow(LERP_SPEED, delta)) + + data_state[i].direction = data_target[i].direction + } } normalize :: proc(v : rl.Vector2) -> rl.Vector2 { return v / math.sqrt((v.x*v.x) + (v.y*v.y)) } -draw_waveform :: proc(wave : []f32, start : rl.Vector2, end : rl.Vector2, amp : f32) { +draw_waveform :: proc(wave : []f32, start : rl.Vector2, end : rl.Vector2, amp : f32, name : cstring) { samples := len(wave) lines := samples-1 @@ -107,27 +368,121 @@ draw_waveform :: proc(wave : []f32, start : rl.Vector2, end : rl.Vector2, amp : end_base := start + (step*f32(i+1)) start_pos : rl.Vector2 = start_base + (perpendicular*wave[i]*amp) end_pos = end_base + (perpendicular*wave[i+1]*amp) - rl.DrawLineEx(start_pos, end_pos, 4, rl.PURPLE) - rl.DrawCircleV(start_pos, 5, rl.RED) + rl.DrawLineEx(start_pos, end_pos, 4, ORANGE) + rl.DrawCircleV(start_pos, 5, YELLOW) + + // Text on each point + point_font_size :: 24 + end_value := wave[i+1] + end_text := fmt.caprintf("%.1f", end_value, allocator = context.temp_allocator) + value_pos := (end_base + (perpendicular*(wave[i+1]*amp-25))) - (rl.MeasureTextEx(font, end_text, point_font_size, 0)*0.5) + rl.DrawTextEx(font, end_text, value_pos, point_font_size, 0, rl.GRAY) + } + rl.DrawCircleV(end_pos, 5, YELLOW) + + rl.DrawTextEx(font, name, start, 32, 0, rl.LIGHTGRAY) +} + +draw_grid :: proc(first : rl.Vector2, last : rl.Vector2, data : []cell) { + highest_cost : f32 = 0 + for c in data { + if c.cost > highest_cost && c.cost != math.INF_F32 { + highest_cost = c.cost + } + } + color := rl.DARKGRAY + color.a = u8(255.0*state.grid_opacity) + text_color := rl.WHITE + text_color.a = u8(255.0*state.grid_data_opacity) + cost_font_size :: 28 + for cell, i in data { + + // Cell bg color + cell_bg := RED + cell_bg.a = u8(255*cell.cost/highest_cost*state.heatmap_opacity) + if cell.cost == math.INF_F32 do cell_bg = YELLOW + rect := rect_from_index(i, 2) + rl.DrawRectangleRec(rect, cell_bg) + + // Stepped in + stepped_bg := rl.WHITE + stepped_bg.a = u8(255*cell.stepped_in*0.8) + rl.DrawRectangleRec(rect, stepped_bg) + + // Outline + rl.DrawRectangleLinesEx(rect, 2*state.grid_opacity, color) + + // Cost value text + cost_string := fmt.caprintf("%.2f", cell.cost, allocator = context.temp_allocator) + text_dim := rl.MeasureTextEx(font, cost_string, cost_font_size, 0) + text_draw_point := rl.Vector2{rect.x, rect.y}+(({rect.width, rect.height}-text_dim)*0.5) + rl.DrawTextEx(font, fmt.ctprintf("%.2f", cell.cost), text_draw_point, cost_font_size, 0, text_color) + + if cell.direction != .NONE { + target_point : rl.Vector2 + #partial switch cell.direction { + case .LEFT: + target_point = middle_of_rect(rect_from_index(i-1)) + case .DIAGONAL: + target_point = middle_of_rect(rect_from_index(i-len(waveform_a)-1)) + case .DOWN: + target_point = middle_of_rect(rect_from_index(i-len(waveform_a))) + } + arrow_start := middle_of_rect(rect) + arrow_end := target_point + diff := arrow_end - arrow_start + arrow_start += diff*state.arrow_chopping + arrow_end -= diff*state.arrow_chopping + draw_arrow(arrow_start, arrow_end) + } } - rl.DrawCircleV(end_pos, 5, rl.RED) } -draw_grid :: proc(first : rl.Vector2, last : rl.Vector2) { +middle_of_rect :: proc(rect : rl.Rectangle) -> rl.Vector2 { + return {rect.x+(rect.width*0.5), rect.y+(rect.height*0.5)} +} + +draw_arrow :: proc(start, end : rl.Vector2) { + thickness := state.arrow_thickness + outline := thickness*0.8 + + total_direction := end - start + perpendicular : rl.Vector2 = normalize({total_direction.y, -total_direction.x}) + reverse : rl.Vector2 = normalize({-total_direction.x, -total_direction.y}) + + right_hand : rl.Vector2 = end + (reverse*10) + (perpendicular*-10) + left_hand : rl.Vector2 = end + (reverse*10) + (perpendicular*10) + + rl.DrawLineEx(start, end, thickness+outline, BLACK) + rl.DrawLineEx(end, right_hand, thickness+outline, BLACK) + rl.DrawLineEx(end, left_hand, thickness+outline, BLACK) + + rl.DrawCircleV(start, (thickness+outline)*0.5, BLACK) + rl.DrawCircleV(end, (thickness+outline)*0.5, BLACK) + rl.DrawCircleV(right_hand, (thickness+outline)*0.5, BLACK) + rl.DrawCircleV(left_hand, (thickness+outline)*0.5, BLACK) + + + rl.DrawLineEx(start, end, thickness, ORANGE) + rl.DrawLineEx(end, right_hand, thickness, ORANGE) + rl.DrawLineEx(end, left_hand, thickness, ORANGE) + + rl.DrawCircleV(start, thickness*0.5, ORANGE) + rl.DrawCircleV(end, thickness*0.5, ORANGE) + rl.DrawCircleV(right_hand, thickness*0.5, ORANGE) + rl.DrawCircleV(left_hand, thickness*0.5, ORANGE) } main :: proc() { - fast_forward := -1 - if len(os.args) > 1 { - fast_forward, _ = strconv.parse_int(os.args[1]) - } - fmt.println("Hello") // Initialization //-------------------------------------------------------------------------------------- slide : int = 0 + if len(os.args) > 1 { + slide, _ = strconv.parse_int(os.args[1]) + } rotation : f32 = 0.0 @@ -147,6 +502,9 @@ main :: proc() { camera.offset = {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} camera.zoom = f32(rl.GetScreenHeight())/1080 + font = rl.LoadFontFromMemory(".ttf", raw_data(MONO_FONT), i32(len(MONO_FONT)), 128, nil, 0) + //rl.ToggleBorderlessWindowed() + for !rl.WindowShouldClose() { delta = rl.GetFrameTime() if rl.IsWindowResized() { @@ -162,45 +520,45 @@ main :: proc() { mousePosition := rl.GetMousePosition() left_clicked := rl.IsMouseButtonReleased(rl.MouseButton(0)) right_clicked := rl.IsMouseButtonReleased(rl.MouseButton(1)) - right_arrow := rl.IsKeyReleased(.RIGHT) - left_arrow := rl.IsKeyReleased(.LEFT) - go_forward = left_clicked || right_arrow - go_back = right_clicked || left_arrow - - if slide < fast_forward { - go_forward = true - } else { - fast_forward = -1 - } + go_forward = left_clicked || rl.IsKeyReleased(.RIGHT) || rl.IsKeyReleased(.PAGE_DOWN) + go_back = right_clicked || rl.IsKeyReleased(.LEFT) || rl.IsKeyReleased(.PAGE_UP) // Process //---------------------------------------------------------------------------------- - if go_forward { + if go_forward && slide < len(slides)-1 { slide += 1 fmt.printfln("Forward! To slide #{}", slide) - } else if go_back { + rect_from_index(1) + } else if go_back && slide > 0 { slide -= 1 - fmt.printfln("Back up! To slide #{}", slide) + fmt.printfln("Back! To slide #{}", slide) } target = slides[slide] + data_target = data_frames[slide] lerp() // Draw //---------------------------------------------------------------------------------- rl.BeginDrawing() - rl.ClearBackground(rl.Color{16, 16, 16, 255}) + rl.ClearBackground(BLACK) rl.BeginMode2D(camera) // World-space drawing - draw_waveform(waveform_a, state.wave_a_start, state.wave_a_end, state.wave_a_amp) - draw_waveform(waveform_b, state.wave_b_start, state.wave_b_end, state.wave_b_amp) - + draw_waveform(waveform_a[:], state.wave_a_start, state.wave_a_end, state.wave_a_amp, "Boom") + draw_waveform(waveform_b[:], state.wave_b_start, state.wave_b_end, state.wave_b_amp, "Lav") + draw_grid(grid_start, grid_end, data_state[:]) + highlight_color := YELLOW + highlight_color.a = u8(255*state.highlight_opacity) + rl.DrawRectangleLinesEx({state.highlight_position.x, + state.highlight_position.y, + state.highlight_dimensions.x, + state.highlight_dimensions.y}, HIGHLIGHT_THICKNESS, highlight_color) rl.EndMode2D() @@ -215,3 +573,33 @@ main :: proc() { rl.CloseWindow() } + +distance :: proc(a, b : f32) -> f32 { + //return (a - b)*(a - b); + return math.abs(a - b); +} +three_min :: proc(left, diagonal, down : cell) -> (f32, DIRECTION) { + if (left.cost <= diagonal.cost && left.cost <= down.cost) do return left.cost, .LEFT + if (down.cost <= diagonal.cost && down.cost <= left.cost) do return down.cost, .DOWN + return diagonal.cost, .DIAGONAL; +} +full_calc :: proc(d: []cell, x, y : int) { + d[i_from_xy(x,y)].cost = distance(waveform_a[x], waveform_b[y]) + a_cost, a_direction := three_min(d[i_from_xy(x-1, y )], + d[i_from_xy(x-1, y-1)], + d[i_from_xy(x , y-1)]) + d[i_from_xy(x,y)].cost += a_cost + d[i_from_xy(x,y)].direction = a_direction +} + +cell :: struct { + cost : f32, + direction : DIRECTION, + stepped_in : f32, +} +DIRECTION :: enum { + NONE, + LEFT, + DIAGONAL, + DOWN, +} \ No newline at end of file -- cgit v1.2.1