Files
wehub-resource-sync ead81af521
CI / go (push) Waiting to run
CI / build (darwin, macos-14) (push) Waiting to run
CI / build (windows, windows-2025) (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

97 lines
2.3 KiB
Go

package ui
import (
"math"
"strings"
)
// renderScope draws a Lissajous-style XY oscilloscope using Braille dots.
// Since the audio tap is mono, a phase-delayed copy of the signal is used as
// the Y axis. The delay slowly oscillates over time, producing continuously
// evolving Lissajous figures — circles for pure tones, complex knots for music.
func (v *Visualizer) renderScope() string {
height := v.Rows
dotRows := height * 4
dotCols := PanelWidth * 2
samples := v.waveBuf
n := len(samples)
grid := make([]bool, dotRows*dotCols)
if n > 1 {
// Phase delay slowly oscillates for evolving Lissajous patterns.
baseDelay := n / 4
wobble := int(math.Sin(float64(v.frame)*0.02) * float64(n/8))
delay := max(1, min(n-1, baseDelay+wobble))
// Plot ~512 XY pairs for a dense, smooth figure.
plotPoints := min(n-delay, 512)
step := max(1, (n-delay)/plotPoints)
var prevDotX, prevDotY int
first := true
for i := 0; i+delay < n; i += step {
x := samples[i]
y := samples[i+delay]
// Map [-1, 1] to dot coordinates.
dotX := int((x + 1.0) * 0.5 * float64(dotCols-1))
dotY := int((1.0 - y) * 0.5 * float64(dotRows-1))
dotX = max(0, min(dotCols-1, dotX))
dotY = max(0, min(dotRows-1, dotY))
grid[dotY*dotCols+dotX] = true
// Interpolate between consecutive points for smoother curves.
if !first {
dx := dotX - prevDotX
dy := dotY - prevDotY
adx := dx
if adx < 0 {
adx = -adx
}
ady := dy
if ady < 0 {
ady = -ady
}
steps := max(adx, ady)
if steps > 0 && steps < 30 {
for s := 1; s < steps; s++ {
mx := prevDotX + dx*s/steps
my := prevDotY + dy*s/steps
if mx >= 0 && mx < dotCols && my >= 0 && my < dotRows {
grid[my*dotCols+mx] = true
}
}
}
}
prevDotX = dotX
prevDotY = dotY
first = false
}
}
// Convert dot grid to Braille characters.
lines := make([]string, height)
for row := range height {
var content strings.Builder
for ch := range PanelWidth {
var braille rune = '\u2800'
for dr := range 4 {
for dc := range 2 {
if grid[(row*4+dr)*dotCols+ch*2+dc] {
braille |= brailleBit[dr][dc]
}
}
}
content.WriteRune(braille)
}
lines[row] = specWrap(float64(height-1-row)/float64(height), content.String())
}
return strings.Join(lines, "\n")
}