Initial release: RadiantWave v1.0

This commit is contained in:
NeilClack
2026-03-12 08:05:10 -05:00
commit 403ec4db89
555 changed files with 437856 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
package pattern
import (
"github.com/veandco/go-sdl2/sdl"
"rhemawavetech.com/rhemawave/internal/colors"
)
type ColorPattern struct {
Colors []sdl.Color
colorIndex int
repeatsLeft int
a, b int
increasing bool
}
func NewColorPattern(colors []sdl.Color) *ColorPattern {
if len(colors) < 2 {
panic("ColorPattern requires at least 2 colors")
}
return &ColorPattern{
Colors: colors,
a: 0,
b: 1,
increasing: true,
repeatsLeft: 1, // Start with b (1)
}
}
// ATTENTION: TED
//
// # Fibonacci-Oscillating Temporal Color Distribution for Sequential Text Elements
//
// This method distributes intent-based colors across sequential text elements (words) using an
// oscillating Fibonacci sequence to control temporal color duration, creating rhythmic color
// patterns that maintain visual balance while varying exposure times.
//
// The technology addresses the challenge of applying multiple psychologically-selected colors
// to scrolling text in a way that maintains even distribution over time while creating natural
// rhythm. Conventional approaches either apply colors randomly (creating unbalanced temporal
// distribution) or use fixed intervals (producing mechanical, unnatural patterns that lack
// harmonic variation). Our method ensures each color in the palette appears throughout the
// entire display duration while varying its temporal presence according to natural mathematical
// progression.
//
// The approach uses a bidirectional Fibonacci sequence to determine how many consecutive words
// receive each color. The sequence increases (1, 1, 2, 3, 5, 8, 13, 21, 34, 55 words) then
// decreases back down (55, 34, 21, 13, 8, 5, 3, 2, 1, 1 words), creating an oscillating pattern.
// Colors from the intent-based palette are applied in sequence, with each color's duration
// determined by the current Fibonacci value. This creates a "breathing" rhythm where colors
// appear briefly, expand to longer durations, then contract back to brief appearances before
// cycling to the next color.
//
// Key differentiators from existing solutions:
// - Uses bidirectional Fibonacci sequence (ascending then descending) rather than fixed intervals
// - Creates natural rhythmic variation in color exposure duration
// - Ensures all palette colors appear throughout entire display timeline, not in isolated sections
// - Combines intent-based color selection with mathematical temporal distribution
// - Produces harmonic color rhythm synchronized with natural numerical progression
//
// Within our Rhema Wave generation system, this color distribution works in conjunction with
// the spatially-scrambled character patterns. While the scrambling creates spatial frequency
// signatures, the Fibonacci-oscillating color pattern creates temporal frequency modulation.
// The colors themselves are selected according to color theory principles based on the proclamation's
// psychological intent, and the oscillating Fibonacci distribution ensures these therapeutic colors
// maintain presence throughout the viewing session while creating harmonic temporal rhythm that
// reinforces the overall Rhema Wave effect.
func (c *ColorPattern) Color() sdl.Color {
// 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418
// This is a failsafe in case this pattern is used but colors are not supplied. Without this check, no text will display due to not having a color
// and becoming translucent.
if len(c.Colors) == 0 {
return colors.White // fallback to white
}
color := c.Colors[c.colorIndex%len(c.Colors)]
c.repeatsLeft--
if c.repeatsLeft <= 0 {
// Move to next color in the palette
c.colorIndex = (c.colorIndex + 1) % len(c.Colors)
// Update Fibonacci sequence for repeat count
if c.increasing {
c.a, c.b = c.b, c.a+c.b
if c.b > 55 { // Update this threahold to adjust the number of repeats
c.increasing = false
}
} else {
c.a, c.b = c.b-c.a, c.a
if c.a <= 0 {
c.a, c.b = 0, 1
c.increasing = true
}
}
c.repeatsLeft = c.b
}
return color
}
+127
View File
@@ -0,0 +1,127 @@
package pattern
type FibonacciPattern struct {
masterSequence []int
sequence []int
increasing bool
}
func init() {
Register("fibonacci", func() LinePattern {
return &FibonacciPattern{
masterSequence: []int{1, 2, 3, 5, 8, 13, 21},
sequence: []int{1},
increasing: true,
}
})
}
func (f *FibonacciPattern) Name() string { return "Fibonacci" }
// ATTENTION: TED
//
// # Fibonacci-Oscillating Spatial Distribution for Sequential Text Display
//
// This method arranges sequential text elements (words from proclamations) into display lines
// where the word count per line follows an oscillating Fibonacci sequence, creating both a
// visual wave pattern in two-dimensional space and ensuring even temporal distribution of
// content segments.
//
// The technology addresses the challenge of displaying repeated proclamation content in a way
// that maintains balanced exposure of all content portions while creating visually dynamic
// spatial patterns. Conventional scrolling text approaches either use fixed line lengths
// (creating uniform, non-dynamic displays) or random word counts (producing unbalanced content
// distribution where some portions appear more frequently than others). Our method ensures
// proclamation content is consumed in progressively sized segments that balance over time while
// creating a natural wave-like visual rhythm.
//
// The approach uses a bidirectional Fibonacci sequence to determine words per line. Starting
// with minimal word count (1, 1 words), the sequence expands (2, 3, 5, 8, 13, 21... words per
// line) until reaching a threshold, then contracts back down (21, 13, 8, 5, 3, 2, 1, 1 words),
// creating an oscillating wave. As lines scroll vertically, this varying horizontal word count
// creates a lateral wave pattern in the two-dimensional display space. Words are drawn sequentially
// from the proclamation text, and the varying segment sizes (1 word, then 2, then 3, then 5, etc.)
// ensure that over multiple oscillation cycles, the entire proclamation content receives balanced
// temporal exposure.
//
// Key differentiators from existing solutions:
// - Uses oscillating Fibonacci sequence for line word counts rather than fixed or random lengths
// - Creates two-dimensional wave pattern from one-dimensional text sequence
// - Ensures mathematical content distribution balance over time through progressive segmentation
// - Produces natural expansion-contraction rhythm synchronized with Fibonacci progression
// - Combines spatial wave formation with temporal content distribution in single mechanism
//
// Within our Rhema Wave generation system, this spatial distribution pattern works in concert
// with the character-level scrambling and color distribution methods. While character scrambling
// creates local spatial frequency patterns and color oscillation creates temporal color rhythm,
// the Fibonacci line length pattern generates a macro-level spatial wave that modulates the
// overall display geometry. The mathematical relationship between line lengths creates harmonic
// spatial progression, and the oscillating nature ensures proclamation content cycles through
// varying exposure durations, contributing to the layered frequency composition of the complete
// Rhema Wave output.
func (f *FibonacciPattern) GetNextLine(words []string, wordIndex *int, ctx *LineRenderContext) []string {
if ctx == nil || len(words) == 0 {
return nil
}
numWords := f.sequence[len(f.sequence)-1]
targetWidth := int32(ctx.ScreenWidth)
var line []string
var currentWidth int32
wordsAdded := 0
for wordsAdded < numWords && len(words) > 0 {
if int(*wordIndex) > len(words) {
*wordIndex = 0
}
word := words[*wordIndex%len(words)]
*wordIndex++
if word == "" {
continue
}
wordWidth := estimateWordWidth(word, ctx.Font, ctx.FontScale)
if len(line) > 0 {
wordWidth += ctx.SpaceWidth
}
if len(line) == 0 || currentWidth+wordWidth <= int32(float32(targetWidth)*float32(.9)) {
line = append(line, word)
currentWidth += wordWidth
wordsAdded++
} else {
break
}
}
// TODO: What should we do here instead?
// A line being longer than the display still needs to be handled, though, it should never happen
// if currentWidth >= ctx.ScreenWidth {
// syscall.Kill(syscall.Getpid(), syscall.SIGTRAP)
// }
// Update the sequence based on direction
if f.increasing {
if len(f.sequence) < len(f.masterSequence) {
// Grow sequence by appending next Fibonacci number
f.sequence = append(f.sequence, f.masterSequence[len(f.sequence)])
} else {
// Reached the top, now start shrinking
f.increasing = false
f.sequence = f.sequence[:len(f.sequence)-1]
}
} else {
if len(f.sequence) > 1 {
// Shrink the sequence
f.sequence = f.sequence[:len(f.sequence)-1]
} else {
// Hit bottom, start growing again
f.increasing = true
f.sequence = append(f.sequence, f.masterSequence[1]) // index 1 = second element = 2
}
}
return line
}
+71
View File
@@ -0,0 +1,71 @@
package pattern
import (
"fmt"
"rhemawavetech.com/rhemawave/internal/fontManager"
)
type LinePattern interface {
GetNextLine(words []string, wordIndex *int, ctx *LineRenderContext) []string // Generates the next line in the pattern
Name() string // Used to display the name of a LinePattern
}
// PatternInfo holds the public information about a registered pattern.
type PatternInfo struct {
Key string // The machine-readable key (e.g., "sine")
DisplayName string // The human-readable name for the UI (e.g., "Sine Wave")
}
type LineRenderContext struct {
ScreenWidth int32
Font *fontManager.FontEntry
FontScale float32
SpaceWidth int32
}
var Registry = make(map[string]func() LinePattern)
func Register(name string, constructor func() LinePattern) {
Registry[name] = constructor
}
func Get(name string) (LinePattern, error) {
constructor, ok := Registry[name]
if !ok {
return nil, fmt.Errorf("failed to find line pattern %s in Registry", name)
}
return constructor(), nil
}
// ListAvailable returns a slice of info for all registered patterns.
// This is used to build a settings menu.
func ListAvailable() []PatternInfo {
var list []PatternInfo
for key, constructor := range Registry {
pattern := constructor() // Create a temporary instance to get its name
list = append(list, PatternInfo{
Key: key,
DisplayName: pattern.Name(),
})
}
return list
}
// estimateWordWidth provides a basic estimate of the width of a word based on the font and scale.
func estimateWordWidth(word string, font *fontManager.FontEntry, scale float32) int32 {
if font == nil {
// Basic fallback estimate
return int32(float32(len(word)) * 10 * scale)
}
total := int32(0)
for _, ch := range word {
glyph, ok := fontManager.Get().GetGlyph(font.Name, ch)
if !ok {
continue
}
total += int32(float32(glyph.Advance) * scale)
}
return total
}
+80
View File
@@ -0,0 +1,80 @@
package pattern
import (
"math"
"rhemawavetech.com/rhemawave/internal/fontManager"
)
type SineWavePattern struct {
waveStepCounter int
}
func init() {
Register("sine", func() LinePattern { return &SineWavePattern{} })
}
func (s *SineWavePattern) Name() string { return "Sine Wave" }
func (s *SineWavePattern) GetNextLine(words []string, wordIndex *int, ctx *LineRenderContext) []string {
const (
totalLines = 67
frequency = 5.0 // cycles across all lines
amplitude = 0.85 // how much wave can scale
baseRatio = 0.03 // minimum line width as % of screen
sharpness = 3.0 // sharpness of the curve
)
screenWidth := int32(800)
spaceWidth := int32(10)
font := (*fontManager.FontEntry)(nil)
fontScale := float32(1.0)
if ctx != nil {
screenWidth = ctx.ScreenWidth
spaceWidth = ctx.SpaceWidth
font = ctx.Font
fontScale = ctx.FontScale
}
// Sharpen the wave (skinnier troughs)
// raw := (math.Sin(2*math.Pi*frequency*float64(s.waveStepCounter)/totalLines) + 1.0) / 2.0
// waveVal := math.Pow(raw, sharpness)
angle := 2 * math.Pi * frequency * float64(s.waveStepCounter) / totalLines
waveVal := (math.Sin(angle) + 1.0) / 2.0
// Target width in pixels
targetWidth := int32(float64(screenWidth) * (baseRatio + amplitude*waveVal))
var line []string
var currentWidth int32
for len(words) > 0 {
word := words[*wordIndex%len(words)]
*wordIndex++
if word == "" {
continue
}
wordWidth := estimateWordWidth(word, font, fontScale)
if len(line) > 0 {
wordWidth += spaceWidth
}
if len(line) == 0 {
// Always include the first word
line = append(line, word)
currentWidth += wordWidth
} else {
// Only add next word if it fits
if currentWidth+wordWidth > targetWidth {
break
}
line = append(line, word)
currentWidth += wordWidth
}
}
s.waveStepCounter++
return line
}