Scripts/lunar-orbit.ps1
$esc = [char]27 $reset = "$esc[0m" $rows = 26 $cols = 80 $moons = @( @{ X = 18.0; Y = 12.0; Radius = 5.5; Hue = 0.15; Phase = 0.2 }, @{ X = 40.0; Y = 10.0; Radius = 6.5; Hue = 0.55; Phase = 0.5 }, @{ X = 62.0; Y = 14.0; Radius = 4.8; Hue = 0.75; Phase = 0.85 } ) $orbits = @( @{ CX = 36.0; CY = 11.5; A = 26.0; B = 9.5 }, @{ CX = 36.0; CY = 11.5; A = 18.0; B = 6.5 } ) function HslToRgb { param( [double]$Hue, [double]$Saturation, [double]$Lightness ) if ($Saturation -eq 0) { $value = [int]([math]::Round($Lightness * 255)) return @($value, $value, $value) } $q = if ($Lightness -lt 0.5) { $Lightness * (1 + $Saturation) } else { $Lightness + $Saturation - $Lightness * $Saturation } $p = 2 * $Lightness - $q $hk = $Hue % 1 $toRgb = { param($t) if ($t -lt 0) { $t += 1 } if ($t -gt 1) { $t -= 1 } if (6 * $t -lt 1) { return $p + ($q - $p) * 6 * $t } if (2 * $t -lt 1) { return $q } if (3 * $t -lt 2) { return $p + ($q - $p) * ((2 / 3) - $t) * 6 } return $p } $r = & $toRgb ($hk + 1.0 / 3.0) $g = & $toRgb $hk $b = & $toRgb ($hk - 1.0 / 3.0) return @([int]([math]::Round($r * 255)), [int]([math]::Round($g * 255)), [int]([math]::Round($b * 255))) } Write-Host for ($y = 0; $y -lt $rows; $y++) { $sb = [System.Text.StringBuilder]::new() $ny = $y / [double]($rows - 1) $baseHue = 0.6 + 0.05 * [math]::Sin($ny * [math]::PI) $baseLight = 0.12 + 0.25 * (1 - $ny) $baseRgb = HslToRgb -Hue $baseHue -Saturation 0.55 -Lightness $baseLight for ($x = 0; $x -lt $cols; $x++) { $char = '·' $rgb = $baseRgb.Clone() foreach ($orbit in $orbits) { $dx = ($x - $orbit.CX) / $orbit.A $dy = ($y - $orbit.CY) / $orbit.B $value = $dx * $dx + $dy * $dy if ([math]::Abs($value - 1.0) -lt 0.04) { $char = '∙' $rgb = HslToRgb -Hue 0.58 -Saturation 0.6 -Lightness 0.5 } } foreach ($moon in $moons) { $dx = $x - $moon.X $dy = ($y - $moon.Y) * 1.2 $dist = [math]::Sqrt($dx * $dx + $dy * $dy) if ($dist -le $moon.Radius) { $ratio = 1 - ($dist / $moon.Radius) $rgb = HslToRgb -Hue $moon.Hue -Saturation (0.35 + 0.4 * $ratio) -Lightness (0.6 + 0.2 * $ratio) $angle = ([math]::Atan2($dy, $dx) + [math]::PI * 2) % (2 * [math]::PI) $phaseAngle = ($moon.Phase * 2 * [math]::PI) $diff = [math]::Abs($angle - $phaseAngle) if ($diff -gt [math]::PI) { $diff = 2 * [math]::PI - $diff } $char = if ($diff -lt 0.5) { '◕' } elseif ($diff -lt 1.0) { '◑' } elseif ($diff -lt 1.5) { '◐' } else { '○' } break } } $null = $sb.Append("$esc[38;2;$($rgb[0]);$($rgb[1]);$($rgb[2])" + "m$char") } $null = $sb.Append($reset) Write-Host $sb.ToString() } $label = "$esc[38;2;200;220;255mLUNAR$esc[0m $esc[38;2;160;200;255mORBIT$esc[0m" $padLeft = [math]::Max(0, [int](($cols - ([regex]::Replace($label, "$([char]27)\[[0-9;]*m", '')).Length) / 2)) Write-Host ((' ' * $padLeft) + $label) Write-Host $reset |