113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
package ui
|
|
|
|
import "strings"
|
|
|
|
// renderRain draws bar-shaped columns filled with falling rain streaks.
|
|
// The bar height follows band level, but the interior is animated falling drops
|
|
// with head/body/tail coloring. Higher energy makes taller, denser rainfall.
|
|
func (v *Visualizer) renderRain(bands []float64) string {
|
|
height := v.Rows
|
|
lines := make([]string, height)
|
|
bandCount := len(bands)
|
|
|
|
for row := range height {
|
|
var sb, run strings.Builder
|
|
curTag := -1
|
|
col := 0
|
|
|
|
for b := range bandCount {
|
|
bw := visBandWidth(bandCount, b)
|
|
level := bands[b]
|
|
rowNorm := float64(height-1-row) / float64(height)
|
|
|
|
for range bw {
|
|
if rowNorm >= level {
|
|
// Above the bar — empty.
|
|
if curTag != -1 {
|
|
flushStyleRun(&sb, &run, curTag)
|
|
curTag = -1
|
|
}
|
|
run.WriteByte(' ')
|
|
col++
|
|
continue
|
|
}
|
|
|
|
// Inside the bar — check if this column is active.
|
|
seed := uint64(col)*7919 + 104729
|
|
|
|
// Column activation: gate changes slowly, energy controls density.
|
|
if scatterHash(b, 0, col, v.frame/12) > level*1.6+0.1 {
|
|
if curTag != -1 {
|
|
flushStyleRun(&sb, &run, curTag)
|
|
curTag = -1
|
|
}
|
|
run.WriteByte(' ')
|
|
col++
|
|
continue
|
|
}
|
|
|
|
// Per-column fall speed: 1-3 frames per row step.
|
|
speed := 1 + int(seed%3)
|
|
|
|
// Drop length: 2-4 characters.
|
|
dropLen := 2 + int((seed/7)%3)
|
|
|
|
// Cycle through visible height with gap before repeating.
|
|
cycleLen := height + dropLen + 3
|
|
offset := int((seed / 13) % uint64(cycleLen))
|
|
pos := (int(v.frame)/speed + offset) % cycleLen
|
|
|
|
dist := pos - row
|
|
if dist >= 0 && dist < dropLen {
|
|
// Pick character: head gets │, body gets thinner chars.
|
|
var ch rune
|
|
switch {
|
|
case dist == 0:
|
|
ch = '┃'
|
|
case dist == 1:
|
|
ch = '│'
|
|
default:
|
|
ch = ':'
|
|
}
|
|
|
|
// Color by drop position: bright head, mid body, dim tail.
|
|
var newTag int
|
|
switch {
|
|
case dist == 0:
|
|
newTag = 2
|
|
case dist == 1:
|
|
newTag = 1
|
|
default:
|
|
newTag = 0
|
|
}
|
|
if newTag != curTag {
|
|
flushStyleRun(&sb, &run, curTag)
|
|
curTag = newTag
|
|
}
|
|
run.WriteRune(ch)
|
|
} else {
|
|
// Empty space between drops.
|
|
if curTag != -1 {
|
|
flushStyleRun(&sb, &run, curTag)
|
|
curTag = -1
|
|
}
|
|
run.WriteByte(' ')
|
|
}
|
|
col++
|
|
}
|
|
if b < bandCount-1 {
|
|
if curTag != -1 {
|
|
flushStyleRun(&sb, &run, curTag)
|
|
curTag = -1
|
|
}
|
|
run.WriteByte(' ')
|
|
col++
|
|
}
|
|
}
|
|
flushStyleRun(&sb, &run, curTag)
|
|
lines[row] = sb.String()
|
|
}
|
|
|
|
return strings.Join(lines, "\n")
|
|
}
|