Skip to content

Commit 0b532c0

Browse files
committed
Fix hud flowing animation
1 parent 753ed7f commit 0b532c0

3 files changed

Lines changed: 314 additions & 117 deletions

File tree

lib/minigun/hud/flow_diagram.rb

Lines changed: 131 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class FlowDiagram
99

1010
def initialize(_frame_width, _frame_height)
1111
@animation_frame = 0
12+
@render_tick = 0 # Counter for slowing down animation
1213
@width = 0 # Actual width of diagram content
1314
@height = 0 # Actual height of diagram content
1415
end
@@ -82,8 +83,13 @@ def render(terminal, stats_data, x_offset: 0, y_offset: 0)
8283
diagram_stage.render(terminal, stage_data, x_offset, y_offset)
8384
end
8485

86+
# Update animation (every 2 renders for half speed)
87+
@render_tick += 1
88+
if @render_tick % 2 == 0
89+
@animation_frame = (@animation_frame + 1) % 24
90+
end
8591
# Update animation
86-
@animation_frame = (@animation_frame + 1) % 60
92+
# @animation_frame = (@animation_frame + 1) % 24
8793

8894
# Clear cached data for next frame
8995
@cached_layout = nil
@@ -366,14 +372,17 @@ def render_fanout_connection(terminal, from_pos, target_positions, stage_data, x
366372

367373
# Check if connection is active
368374
active = stage_data[:throughput] && stage_data[:throughput] > 0
369-
color = active ? Theme.primary : Theme.muted
370375

371376
# Calculate split point (horizontal spine where fan-out occurs)
372-
first_target_y = target_positions.first[:y]
373377
split_y = from_y + 1
374378

379+
# Distance counter starts at 0 from source box exit
380+
distance = 0
381+
375382
# Draw vertical line from source to split point
376-
terminal.write_at(x_offset + from_x, y_offset + from_y, "│", color: color)
383+
char, color = Theme.animated_flow_char(:vertical, distance, @animation_frame, active)
384+
terminal.write_at(x_offset + from_x, y_offset + from_y, char, color: color)
385+
distance += 1
377386

378387
# Get X positions of all targets (sorted)
379388
target_xs = target_positions.map { |pos| pos[:x] + pos[:width] / 2 }.sort
@@ -383,47 +392,43 @@ def render_fanout_connection(terminal, from_pos, target_positions, stage_data, x
383392
# Check if there's a target directly below the source
384393
has_center_target = target_xs.include?(from_x)
385394

386-
# Draw horizontal spine with junctions
387-
# Pattern with center target: ┌───────────────┼───────────────┐
388-
# Pattern without center: ┌───────────────┴───────────────┐
395+
# Draw horizontal spine with distance radiating from center
396+
# Distance flows outward from center (from_x)
389397
(leftmost_x..rightmost_x).each do |x|
390-
# Determine the proper box-drawing character
391-
char = if x == leftmost_x
392-
# Left corner
393-
"┌"
394-
elsif x == rightmost_x
395-
# Right corner
396-
"┐"
397-
elsif x == from_x
398-
# Source position: ┼ if target below, ┴ if not
399-
has_center_target ? "┼" : "┴"
400-
else
401-
# Regular horizontal line (spine)
402-
if active
403-
offset = (@animation_frame / 4) % 4
404-
["─", "╌", "┄", "┈"][offset]
405-
else
406-
"─"
407-
end
408-
end
409-
398+
# Calculate distance from center (where flow splits)
399+
x_distance_from_center = (x - from_x).abs
400+
spine_distance = distance + x_distance_from_center
401+
402+
# Determine the proper box-drawing character type
403+
char_type = if x == leftmost_x
404+
:corner_tl # Left corner ┌
405+
elsif x == rightmost_x
406+
:corner_tr # Right corner ┐
407+
elsif x == from_x
408+
# Source position: cross if target below, t_up if not
409+
has_center_target ? :cross : :t_up
410+
else
411+
:horizontal # Regular horizontal line
412+
end
413+
414+
char, color = Theme.animated_flow_char(char_type, spine_distance, @animation_frame, active)
410415
terminal.write_at(x_offset + x, y_offset + split_y, char, color: color)
411416
end
412417

413418
# Draw vertical lines down to each target
419+
# Distance continues from spine position
414420
target_positions.each do |to_pos|
415421
to_x = to_pos[:x] + to_pos[:width] / 2
416422
to_y = to_pos[:y]
417423

418-
((split_y + 1)...to_y).each do |y|
419-
char = if active
420-
offset = (@animation_frame / 4) % Theme::FLOW_CHARS.length
421-
phase = (y - split_y + offset) % Theme::FLOW_CHARS.length
422-
Theme::FLOW_CHARS[phase]
423-
else
424-
"│"
425-
end
424+
# Calculate distance at spine for this target
425+
x_distance_from_center = (to_x - from_x).abs
426+
spine_distance_at_target = distance + x_distance_from_center
426427

428+
((split_y + 1)...to_y).each do |y|
429+
# Distance increases as we go down
430+
drop_distance = spine_distance_at_target + (y - split_y)
431+
char, color = Theme.animated_flow_char(:vertical, drop_distance, @animation_frame, active)
427432
terminal.write_at(x_offset + to_x, y_offset + y, char, color: color)
428433
end
429434
end
@@ -436,7 +441,6 @@ def render_fanin_connection(terminal, source_positions, to_pos, stage_data, x_of
436441

437442
# Check if connection is active
438443
active = stage_data[:throughput] && stage_data[:throughput] > 0
439-
color = active ? Theme.primary : Theme.muted
440444

441445
# Calculate merge point (where horizontal lines converge)
442446
# Place it 1 line above the target
@@ -450,64 +454,66 @@ def render_fanin_connection(terminal, source_positions, to_pos, stage_data, x_of
450454
}
451455
end.sort_by { |s| s[:x] }
452456

453-
# Draw vertical lines from each source down to merge level
454-
# Then turn inward with corners
457+
# Draw lines from each source down to merge level, then turn inward
455458
source_data.each do |source|
459+
# Distance starts at 0 for each source
460+
distance = 0
461+
456462
# Vertical line from source to turn point
457463
(source[:y]...merge_y).each do |y|
458-
char = if active
459-
offset = (@animation_frame / 4) % Theme::FLOW_CHARS.length
460-
phase = (y - source[:y] + offset) % Theme::FLOW_CHARS.length
461-
Theme::FLOW_CHARS[phase]
462-
else
463-
"│"
464-
end
465-
464+
char, color = Theme.animated_flow_char(:vertical, distance, @animation_frame, active)
466465
terminal.write_at(x_offset + source[:x], y_offset + y, char, color: color)
466+
distance += 1
467467
end
468468

469-
# Corner at turn point
469+
# Corner at turn point and horizontal line to center
470470
if source[:x] < to_x
471471
# Left source: turn right with └
472-
terminal.write_at(x_offset + source[:x], y_offset + merge_y, "└", color: color)
472+
char, color = Theme.animated_flow_char(:corner_bl, distance, @animation_frame, active)
473+
terminal.write_at(x_offset + source[:x], y_offset + merge_y, char, color: color)
474+
distance += 1
473475

474-
# Horizontal line from corner to center (or near target)
476+
# Horizontal line from corner to center
475477
((source[:x] + 1)...to_x).each do |x|
476-
char = if active
477-
offset = (@animation_frame / 4) % 4
478-
["─", "╌", "┄", "┈"][offset]
479-
else
480-
"─"
481-
end
482-
478+
char, color = Theme.animated_flow_char(:horizontal, distance, @animation_frame, active)
483479
terminal.write_at(x_offset + x, y_offset + merge_y, char, color: color)
480+
distance += 1
484481
end
485482
elsif source[:x] > to_x
486483
# Right source: turn left with ┘
487-
terminal.write_at(x_offset + source[:x], y_offset + merge_y, "┘", color: color)
488-
489-
# Horizontal line from corner to center (or near target)
490-
((to_x + 1)...source[:x]).each do |x|
491-
char = if active
492-
offset = (@animation_frame / 4) % 4
493-
["─", "╌", "┄", "┈"][offset]
494-
else
495-
"─"
496-
end
484+
char, color = Theme.animated_flow_char(:corner_br, distance, @animation_frame, active)
485+
terminal.write_at(x_offset + source[:x], y_offset + merge_y, char, color: color)
486+
distance += 1
497487

488+
# Horizontal line from corner to center
489+
((to_x + 1)...source[:x]).reverse_each do |x|
490+
char, color = Theme.animated_flow_char(:horizontal, distance, @animation_frame, active)
498491
terminal.write_at(x_offset + x, y_offset + merge_y, char, color: color)
492+
distance += 1
499493
end
500494
else
501-
# Source directly above target - just draw vertical line
495+
# Source directly above target - vertical line continues
502496
# (already drawn above)
503497
end
504498
end
505499

506500
# Draw junction at the converge point (center X position)
507-
# Use if there's a source directly above, if not
501+
# Use cross if there's a source directly above, t_down if not
508502
has_center_source = source_data.any? { |s| s[:x] == to_x }
509-
junction_char = has_center_source ? "┼" : "┬"
510-
terminal.write_at(x_offset + to_x, y_offset + merge_y, junction_char, color: color)
503+
504+
# Calculate distance for junction (use center source distance if exists)
505+
center_source = source_data.find { |s| s[:x] == to_x }
506+
junction_distance = if center_source
507+
merge_y - center_source[:y]
508+
else
509+
# Use distance from closest source
510+
closest = source_data.min_by { |s| (s[:x] - to_x).abs }
511+
(merge_y - closest[:y]) + (closest[:x] - to_x).abs
512+
end
513+
514+
junction_type = has_center_source ? :cross : :t_down
515+
char, color = Theme.animated_flow_char(junction_type, junction_distance, @animation_frame, active)
516+
terminal.write_at(x_offset + to_x, y_offset + merge_y, char, color: color)
511517
end
512518

513519
# Draw animated connection line between two boxes
@@ -521,62 +527,76 @@ def render_connection_line(terminal, from_pos, to_pos, stage_data, x_offset, y_o
521527

522528
# Check if connection is active (has throughput)
523529
active = stage_data[:throughput] && stage_data[:throughput] > 0
524-
color = active ? Theme.primary : Theme.muted
530+
531+
# Distance counter starts at 0 from source
532+
distance = 0
525533

526534
if from_x == to_x
527535
# Straight vertical line
528536
(from_y...to_y).each do |y|
529-
char = if active
530-
offset = (@animation_frame / 4) % Theme::FLOW_CHARS.length
531-
phase = (y - from_y + offset) % Theme::FLOW_CHARS.length
532-
Theme::FLOW_CHARS[phase]
533-
else
534-
"│"
535-
end
536-
537+
char, color = Theme.animated_flow_char(:vertical, distance, @animation_frame, active)
537538
terminal.write_at(x_offset + from_x, y_offset + y, char, color: color)
539+
distance += 1
538540
end
539541
else
540-
# L-shaped connection: vertical down, horizontal across, vertical down
542+
# L-shaped connection: vertical down, corner, horizontal across, corner, vertical down
541543
mid_y = from_y + 1
542544

543545
# First vertical segment (short drop from source)
544-
terminal.write_at(x_offset + from_x, y_offset + from_y, "│", color: color)
545-
546-
# Horizontal segment
547-
x_start = [from_x, to_x].min
548-
x_end = [from_x, to_x].max
549-
(x_start..x_end).each do |x|
550-
char = if active
551-
offset = (@animation_frame / 4) % 4
552-
["─", "╌", "┄", "┈"][offset]
553-
else
554-
"─"
555-
end
556-
557-
terminal.write_at(x_offset + x, y_offset + mid_y, char, color: color)
546+
char, color = Theme.animated_flow_char(:vertical, distance, @animation_frame, active)
547+
terminal.write_at(x_offset + from_x, y_offset + from_y, char, color: color)
548+
distance += 1
549+
550+
# Determine corner and horizontal direction
551+
if from_x < to_x
552+
# Going right: use └ and ┐
553+
corner1_type = :corner_bl
554+
corner2_type = :corner_tr
555+
556+
# First corner
557+
char, color = Theme.animated_flow_char(corner1_type, distance, @animation_frame, active)
558+
terminal.write_at(x_offset + from_x, y_offset + mid_y, char, color: color)
559+
distance += 1
560+
561+
# Horizontal segment (left to right)
562+
((from_x + 1)...to_x).each do |x|
563+
char, color = Theme.animated_flow_char(:horizontal, distance, @animation_frame, active)
564+
terminal.write_at(x_offset + x, y_offset + mid_y, char, color: color)
565+
distance += 1
566+
end
567+
568+
# Second corner
569+
char, color = Theme.animated_flow_char(corner2_type, distance, @animation_frame, active)
570+
terminal.write_at(x_offset + to_x, y_offset + mid_y, char, color: color)
571+
distance += 1
572+
else
573+
# Going left: use ┘ and ┌
574+
corner1_type = :corner_br
575+
corner2_type = :corner_tl
576+
577+
# First corner
578+
char, color = Theme.animated_flow_char(corner1_type, distance, @animation_frame, active)
579+
terminal.write_at(x_offset + from_x, y_offset + mid_y, char, color: color)
580+
distance += 1
581+
582+
# Horizontal segment (right to left)
583+
((to_x + 1)...from_x).reverse_each do |x|
584+
char, color = Theme.animated_flow_char(:horizontal, distance, @animation_frame, active)
585+
terminal.write_at(x_offset + x, y_offset + mid_y, char, color: color)
586+
distance += 1
587+
end
588+
589+
# Second corner
590+
char, color = Theme.animated_flow_char(corner2_type, distance, @animation_frame, active)
591+
terminal.write_at(x_offset + to_x, y_offset + mid_y, char, color: color)
592+
distance += 1
558593
end
559594

560595
# Second vertical segment (drop to target)
561596
((mid_y + 1)...to_y).each do |y|
562-
char = if active
563-
offset = (@animation_frame / 4) % Theme::FLOW_CHARS.length
564-
phase = (y - mid_y + offset) % Theme::FLOW_CHARS.length
565-
Theme::FLOW_CHARS[phase]
566-
else
567-
"│"
568-
end
569-
597+
char, color = Theme.animated_flow_char(:vertical, distance, @animation_frame, active)
570598
terminal.write_at(x_offset + to_x, y_offset + y, char, color: color)
571-
end
572-
573-
# Corner characters
574-
if from_x < to_x
575-
terminal.write_at(x_offset + from_x, y_offset + mid_y, "└", color: color)
576-
terminal.write_at(x_offset + to_x, y_offset + mid_y, "┐", color: color)
577-
else
578-
terminal.write_at(x_offset + from_x, y_offset + mid_y, "┘", color: color)
579-
terminal.write_at(x_offset + to_x, y_offset + mid_y, "┌", color: color)
599+
distance += 1
580600
end
581601
end
582602
end

0 commit comments

Comments
 (0)