Created
March 21, 2025 20:38
-
-
Save daisyUniverse/f60f921e28af58b95071054b1a64091d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Screenbuffer Experiments | |
# More experiments in learning how to implement a fast powershell screenbuffer | |
# Robin Universe [D] | |
# 03 . 05 . 25 | |
param( | |
[switch]$stepthrough, | |
[switch]$cls, | |
[switch]$slow | |
) | |
# X IS SIDE TO SIDE ($pos[0]) | |
# Y IS UP AND DOWN ($pos[1]) | |
[Console]::SetCursorPosition(0,0) | |
[Console]::CursorVisible = $false | |
$Esc=[char]0x1b | |
if ($cls){Clear-Host} | |
function rgb( [int[]]$rgb=($null), [int[]]$bgrgb=($null), [string]$string, [switch]$NoReset){ | |
if (!$NoReset) { $rst = "$Esc[0m" } else { $rst="" } # Reset control code | |
$bgc = "$Esc[48;2;" # Background control code | |
$fgc = "$Esc[38;2;" # Foreground control code | |
if ($null -ne $rgb ) { $out = ( $fgc + $rgb[0] + ";" + $rgb[1] + ";" + $rgb[2] + "m" ) } # if there is a fg rgb value, apply | |
if ($null -ne $bgrgb) { $out += ( $bgc + $bgrgb[0] + ";" + $bgrgb[1] + ";" + $bgrgb[2] + "m" ) } # if there is a bg rgb value, apply | |
Return ($out + $string + $rst) # Return string with ansi control characters | |
} | |
class hue { | |
[int]$speed | |
[int[]]$rgb | |
[bool[]]$up = ( $true, $true, $true ) | |
$scd = $this.speed | |
hue() { | |
$this.speed = 5 # Speed controller, higher = slower | |
$this.scd = $this.speed | |
$this.rgb = ( 0, 0, 0 ) # Initial RGB values | |
$this.up = ( $true, $true, $true ) # Indicator for if the values are rising or falling | |
} | |
hue([int]$speed, [int[]]$rgb){ | |
$this.speed = $speed | |
$this.rgb = $rgb | |
$this.scd = $speed | |
} | |
[string] increment(){ | |
$i=0; $this.scd-- # Init counters | |
if ($this.scd -lt 0){ # Repeat same values for as long as SCD (Speed Countdown) is above zero | |
foreach ($color in $this.rgb){ | |
$sup = $this.up[$i] # Grab direction | |
$scale = [Math]::Abs( $color - 128 ) # Find current distance to 128 | |
$normalize = ( $scale - -255 ) / ( 255 - -255 ) # Normalize scale for easing | |
if ( $sup ) { $color += [Math]::Floor( $normalize + 1) } # Add normalized scale going upwards | |
elseif ( $false -eq $sup ) { $color -= [Math]::Floor( $normalize + 1) } # Sub normalized scale going downwards | |
if ( $color -gt 253 ) { $sup = $false; $color = 253 } # Clamp value max | |
elseif ( $color -lt 55 ) { $sup = $true; $color = 55 } # Clamp value min | |
$this.scd = $this.speed # Apply loop changes | |
$this.rgb[$i] = $color | |
$this.up[$i] = $sup | |
$i++ | |
} | |
} | |
Return $this.rgb | |
} | |
} | |
function Get-Canvas-Size { | |
$window = [PSCustomObject]@{ | |
height = (Get-Host).UI.RawUI.MaxWindowSize.Height | |
width = (Get-Host).UI.RawUI.MaxWindowSize.Width | |
} | |
return $window | |
} | |
function BuildBuffer($window, $char="") { | |
$l = 0 | |
$buffer = [string[]]::new($window.height) | |
while ($l -lt ($window.height -1)){ | |
$buffer[$l] = ($char * $window.width); $l++ | |
} | |
return $buffer | |
} | |
function BuildColorBuffer($window, $buffer){ | |
} | |
function Render($buffer){ | |
[Console]::SetCursorPosition(0,0) | |
foreach ($line in $buffer){ if ( $line -gt $window.width ) { $line = $line.Substring(0, $window.width) } } | |
[Console]::Write($buffer -join "$Esc[0m`n") | |
} | |
class canvas{ | |
$window | |
canvas() { | |
$this.window = Get-Canvas-Size | |
} | |
# Issue: The positioning of the string is skewed by the length of the string being different with ansi control characters | |
# IE: "X" becomes "[38;2;255;255;255m[48;2;21;23;255mX[0m" when given an RGB ansi control code | |
# This causes the screenbuffer to have unwritable space, or go out of bounds when attempting to modify the buffer line | |
[string] SetCharacter([int[]]$pos, [string]$char, [string]$buffer, [int[]]$rgb=$null, [int[]]$bgrgb=$null) { | |
$lines = $buffer.split("`n") # Splits original buffer into an array of lines | |
$l = $char.Length | |
$char = (rgb -rgb $rgb -bgrgb $bgrgb -string $char) # Prepends RGB ansi code to string | |
$charleng = $char.split([char]0x1b).Length | |
Write-Host $charleng | |
$lines[$pos[1]] = $lines[$pos[1]].Remove($pos[0], $l).Insert($pos[0],$char) # Replaces characters starting at specified coordinate | |
$buffer = $lines -join "`n" # Stitches the buffer back together | |
Return $buffer | |
} | |
[string] Get([int]$x, [int]$y) { | |
Return " " | |
} | |
} | |
function Get-NormalizeCoordinates([int[]]$pos){ | |
$window = Get-Canvas-Size | |
$normalizedX = ($pos[0] / $window.width * 2) -1 | |
$normalizedY = ($pos[1] / $window.height * 2) -1 | |
return ($normalizedX, $normalizedY) | |
} | |
function Set-NormalizedCoordinates([double[]]$pos){ | |
$window = Get-Canvas-Size | |
$screenX = [Math]::Round( ( ( $pos[0] + 1 ) / 2) * $window.width ) | |
$screenY = [Math]::Round( ( ( $pos[1] + 1 ) / 2) * $window.height ) | |
return ($screenX, $screenY) | |
} | |
$window = Get-Canvas-Size | |
$buffer = BuildBuffer $window "X" | |
$canvas = [canvas]::new() | |
function rect($char = " ", [int[]]$size = (1,1) , [int[]]$pos = (0,0), $buffer, [int[]]$rgb, [int[]]$bgrgb){ | |
$x=0; $y=0 | |
$char = $char * $size[0] | |
while ($x -lt $size[1]){ | |
$buffer = $canvas.SetCharacter( ( $pos[1], ($pos[0] + $x) ), $char, $buffer, $rgb, $bgrgb ) | |
$x++ | |
} | |
return $buffer | |
} | |
if($stepthrough){ # Advance frame by frame | |
$pos = 0, 0 | |
while($true){ | |
if ([console]::KeyAvailable){ | |
$x = [System.Console]::ReadKey($true) | |
if ($x.KeyChar -eq 'w'){ | |
$pos[1] = $pos[1] - 1 | |
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer) | |
Render $buffer | |
} | |
if ($x.KeyChar -eq 's'){ | |
$pos[1] = $pos[1] + 1 | |
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer) | |
Render $buffer | |
} | |
if ($x.KeyChar -eq 'a'){ | |
$pos[0] = $pos[0] - 1 | |
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer) | |
Render $buffer | |
} | |
if ($x.KeyChar -eq 'd'){ | |
$pos[0] = $pos[0] + 1 | |
$buffer = $canvas.Set($pos[0],$pos[1],"O", $buffer) | |
Render $buffer | |
} | |
} | |
} | |
}else{ # Render as fast as possible. | |
Measure-Command { | |
while($true){ | |
$CS = Get-Canvas-Size | |
if (($window.height -ne $CS.height) -or ($window.width -ne $CS.width)){$window = $CS; $buffer = BuildBuffer $window "X"} # Change buffer size as window size changes | |
$normalizedCoords = @(0.5, -0.5) | |
$fg = (255, 255, 255) | |
$bg = (21, 23, 255) | |
$screenCoords = Set-NormalizedCoordinates $normalizedCoords | |
#$buffer = (Insert-BufferString $buffer $screenCoords "FUCK").ToString() | |
#$buffer = $canvas.SetCharacter( $screenCoords, "X", $buffer, $fg, $bg) | |
if ($slow){sleep 1} | |
Render $buffer | |
#Write-Host ("The window is " + $window.height + " tall, and is " + $window.width + " wide ") | |
#for ($y = 0; $y -lt $window.height; $y++) { | |
# for ($x = 0; $x -lt $window.width; $x++) { | |
# } | |
#} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment