A slight tweak to the ConEmu install script, plus my own settings file.
iex (irm https://gist.githubusercontent.com/Jaykul/6deda247c677d425862d7098d3ee34ae/raw/Install.ps1)Sorry, I'll shorten that URL when I move this to a real repo 😉
| using namespace System.Numerics | |
| class Mandelbrot : System.Collections.IEnumerator { | |
| # From the constructor? | |
| # Sadly, PowerShell doesn't support optional parameters | |
| [double]$HorizontalViewOffset = -0.5 | |
| [double]$VerticalViewOffset = 0 | |
| $Columns = 120 | |
| $Rows = 28 | |
| $ZoomViewDistance = 6.75 | |
| # calculated during initialization | |
| hidden [double[]]$x_range | |
| hidden [double[]]$y_range | |
| hidden [double]$xscale | |
| hidden [double]$yscale | |
| hidden [int]$iterations | |
| hidden [Complex]$z0 | |
| # These are the things which implement IEnumerator | |
| hidden [int]$_ix = 0 | |
| hidden [int]$_iy = 0 | |
| hidden [int[]]$Actual = $null | |
| hidden [int]$SkipLines = -1 | |
| [void] Init() { | |
| $aspect_ratio = 1/3 | |
| # lowering the distance will zoom in | |
| $factor = $this.ZoomViewDistance / $this.Columns | |
| $this.xscale = $factor * $aspect_ratio | |
| $this.yscale = $factor | |
| $this.iterations = 170 | |
| $this.z0 = [Complex]::Zero | |
| $this.SkipLines = -1 | |
| $xmin = $this.HorizontalViewOffset - (($this.xscale * $this.Columns) / 2) | |
| $ymin = $this.VerticalViewOffset - (($this.yscale * $this.Rows) / 2) | |
| $this.x_range = foreach($ix in 0..($this.Columns - 1)){ $xmin + ($this.xscale * $ix) } | |
| $this.y_range = foreach($iy in 0..($this.Rows - 1)){ $ymin + ($this.yscale * $iy) } | |
| } | |
| [object] get_Current() { | |
| return $this.Actual | |
| } | |
| [void] Reset() { | |
| if($this.iterations -eq 0) { $this.Init() } | |
| $this._ix = 0 | |
| $this._iy = 0 | |
| } | |
| [bool] MoveNext() { | |
| if($this.iterations -eq 0) { $this.Init() } | |
| if($this._ix -ge $this.Columns) { | |
| $this._ix = 0 | |
| $this._iy++ | |
| } | |
| if($this._iy -ge $this.Rows) { | |
| return $false | |
| } | |
| $c = [Complex]::new($this.x_range[$this._ix], $this.y_range[$this._iy]) | |
| $z = $this.z0 | |
| $color = 0 | |
| $mind = 2 | |
| foreach ($i in (0..($this.iterations - 1))) { | |
| $z = $z * $z + $c | |
| $d = [Complex]::Abs($z) | |
| if ($d -ge 2) { | |
| $color = [Math]::Min([int]($mind / 0.007), 254) + 1 | |
| break | |
| } else { | |
| $mind = [Math]::Min($d, $mind) | |
| } | |
| } | |
| # yield return ... | |
| $this.Actual = @( $this._ix, $this._iy, $color ) | |
| # obscure feature which would allow skipping lines, if you were using this as a "working" animation ... | |
| # when you need to write output, you might skip as many lines of the mandelbrot as you had output lines | |
| if ($this.SkipLines -ge 0) { | |
| $this._iy += $this.SkipLines | |
| $do_break = $this.SkipLines -gt 0 | |
| $this.SkipLines = -1 | |
| $this._ix = 0 | |
| # NOTE: I still don't understand this | |
| if ($do_break) { return $false } | |
| } else { | |
| $this._ix++ | |
| } | |
| return $true | |
| } | |
| } | |
| $script:palette = @( | |
| $Null | |
| [ConsoleColor]::DarkBlue | |
| [ConsoleColor]::DarkMagenta | |
| [ConsoleColor]::DarkCyan | |
| [ConsoleColor]::DarkRed | |
| [ConsoleColor]::DarkYellow | |
| [ConsoleColor]::DarkGreen | |
| [ConsoleColor]::Gray | |
| ) | |
| # These are some "favorite" locations | |
| $script:MandelbrotFavoriteViews = @( | |
| # x, y, "distance", max color range | |
| [PSCustomObject]@{HorizontalViewOffset=-0.5; VerticalViewOffset= 0; ZoomViewDistance =6.75; ColorViewLimit=255} | |
| [PSCustomObject]@{HorizontalViewOffset=0.37865401; VerticalViewOffset= 0.669227668; ZoomViewDistance =0.04; ColorViewLimit=111} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.2693; VerticalViewOffset= -0.4145; ZoomViewDistance =0.2; ColorViewLimit=105} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.2693; VerticalViewOffset= -0.4145; ZoomViewDistance =0.05; ColorViewLimit=97} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.2642; VerticalViewOffset= -0.4185; ZoomViewDistance =0.01; ColorViewLimit=95} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.15; VerticalViewOffset= -0.28; ZoomViewDistance =0.9; ColorViewLimit=94} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.15; VerticalViewOffset= -0.28; ZoomViewDistance =0.3; ColorViewLimit=58} | |
| [PSCustomObject]@{HorizontalViewOffset=-1.15; VerticalViewOffset= -0.28; ZoomViewDistance =0.05; ColorViewLimit=26} | |
| ) | |
| function Get-ParameterValues { | |
| <# | |
| .Synopsis | |
| Get the actual values of parameters which have manually set (non-null) default values or values passed in the call | |
| .Description | |
| Unlike $PSBoundParameters, the hashtable returned from Get-ParameterValues includes non-empty default parameter values. | |
| NOTE: Default values that are the same as the implied values are ignored (e.g.: empty strings, zero numbers, nulls). | |
| .Example | |
| function Test-Parameters { | |
| [CmdletBinding()] | |
| param( | |
| $Name = $Env:UserName, | |
| $Age | |
| ) | |
| $Parameters = . Get-ParameterValues | |
| # This WILL ALWAYS have a value... | |
| Write-Host $Parameters["Name"] | |
| # But this will NOT always have a value... | |
| Write-Host $PSBoundParameters["Name"] | |
| } | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| # The $MyInvocation for the caller -- DO NOT pass this (dot-source Get-ParameterValues instead) | |
| $Invocation = $MyInvocation, | |
| # The $PSBoundParameters for the caller -- DO NOT pass this (dot-source Get-ParameterValues instead) | |
| $BoundParameters = $PSBoundParameters | |
| ) | |
| if($MyInvocation.Line[($MyInvocation.OffsetInLine - 1)] -ne '.') { | |
| throw "Get-ParameterValues must be dot-sourced, like this: . Get-ParameterValues" | |
| } | |
| if($PSBoundParameters.Count -gt 0) { | |
| throw "You should not pass parameters to Get-ParameterValues, just dot-source it like this: . Get-ParameterValues" | |
| } | |
| $ParameterValues = @{} | |
| foreach($parameter in $Invocation.MyCommand.Parameters.GetEnumerator()) { | |
| # gm -in $parameter.Value | Out-Default | |
| try { | |
| $key = $parameter.Key | |
| if($null -ne ($value = Get-Variable -Name $key -ValueOnly -ErrorAction Ignore)) { | |
| if($value -ne ($null -as $parameter.Value.ParameterType)) { | |
| $ParameterValues[$key] = $value | |
| } | |
| } | |
| if($BoundParameters.ContainsKey($key)) { | |
| $ParameterValues[$key] = $BoundParameters[$key] | |
| } | |
| } finally {} | |
| } | |
| $ParameterValues | |
| } | |
| function Write-Pixel { | |
| [CmdletBinding()] | |
| param($color, $char, $ColorViewLimit = 256, [Switch]$invert) | |
| $chars = @([char]183, "-", "+", "*", "%", "#") | |
| $idx = {param($chars) [Math]::Abs(($color+1) * ($chars.Count - 1) / $ColorViewLimit) } | |
| if(!$invert) { | |
| $idx2 = $idx | |
| $idx = {param($chars,$idx2=$idx2) $chars.Count - 1 - (&$idx2 $chars)} | |
| } | |
| if(!$char) { | |
| $char = $chars[(&$idx $chars)] | |
| } | |
| $ansi_color = $script:palette[(&$idx $script:palette)] | |
| if($null -eq $ansi_color) { | |
| Write-Host $char -NoNewLine | |
| } else { | |
| Write-Host $char -Foreground $ansi_color -NoNewLine | |
| } | |
| if($DebugPreference -gt "SilentlyContinue") { | |
| # When testing new locations, it's useful to be able to see the range of colors we *should* have used | |
| if($null -eq $script:color_range) { | |
| $script:color_range = $color, $color | |
| } | |
| else { | |
| $old_color_range = $script:color_range | |
| $script:color_range = @([Math]::Min($script:color_range[0], $color), [Math]::Max($script:color_range[1], $color)) | |
| if (($old_color_range[0] - $script:color) -gt 3 -or ($script:color - $old_color_range[1]) -gt 3) { | |
| return $true | |
| } | |
| } | |
| } | |
| return $false | |
| } | |
| $Script:LastFavorite = 0 | |
| function New-Mandelbrot { | |
| <# | |
| .Synopsis | |
| Generates an ASCII Mandelbrot in the console | |
| .Description | |
| Generates a Mandelbrot with the specified parameters using Write-Host to output it. | |
| When called without parameters, cycles through a series of favorite views of the mandelbrot | |
| .Example | |
| New-Mandelbrot -Favorite 0 -Zoom 12 | |
| Generates a default full-console mandelbrot, zoomed out so there's a border all around | |
| .Example | |
| New-Mandelbrot -Favorite 3 | |
| Generates a highly zoomed in view from the favorites list | |
| #> | |
| [CmdletBinding(DefaultParameterSetName="Favorites")] | |
| param( | |
| # Select from a pre-defined set of good View values | |
| # Combine with -Verbose if you want to see what the parameter values are | |
| [ValidateScript({if($_ -ge 0 -and $_ -lt $MandelbrotFavoriteViews.Count) {$true}else { throw "FavoriteView out of range, there are only $($MandelbrotFavoriteViews.Count) favorites"}})] | |
| [Parameter(ParameterSetName="Favorites", Position=0)] | |
| $FavoriteView = $Script:LastFavorite, | |
| # Set the width of the output Mandelbrot in console columns | |
| $Columns = $(if($w=$host.UI.RawUI.WindowSize.Width){$W}else{80}), | |
| # Set the height of the output Mandelbrot in console rows | |
| $Rows = $(if($h=$host.UI.RawUI.WindowSize.Height){$h}else{30}), | |
| # Controls the horizontal offset for the view (a good default would be -0.5) | |
| [Parameter(ParameterSetName="ManualLocation")] | |
| [double]$HorizontalViewOffset = $MandelbrotFavoriteViews[$FavoriteView].HorizontalViewOffset, | |
| # Controls the vertical offset for the view (a good default would be 0) | |
| [Parameter(ParameterSetName="ManualLocation")] | |
| [double]$VerticalViewOffset = $MandelbrotFavoriteViews[$FavoriteView].VerticalViewOffset, | |
| # Controls the zoom distance for the view (a good default would be 6.75) | |
| # Lower values zoom in closer | |
| [Parameter(ParameterSetName="ManualLocation")] | |
| $ZoomViewDistance = $MandelbrotFavoriteViews[$FavoriteView].ZoomViewDistance, | |
| # Limit the color display (a good default would be 255) | |
| [Parameter(ParameterSetName="ManualLocation")] | |
| $ColorViewLimit = $MandelbrotFavoriteViews[$FavoriteView].ColorViewLimit, | |
| # If set, inverts the colors of the mandelbrot | |
| [Switch]$invert | |
| ) | |
| begin { | |
| $ErrorActionPreference = "Stop" | |
| $InterestingCoordinates = @() | |
| $script:color_range = $Null | |
| $mandelbrot = [Mandelbrot]::new() | |
| $Parameters = . Get-ParameterValues | |
| $null = $Parameters.Remove("InputObject") | |
| $null = $Parameters.Remove("Invert") | |
| Write-Verbose (($Parameters | Out-String -stream) -join "`n") | |
| # Copy over whatever we can | |
| foreach($key in (Get-Member -InputObject $mandelbrot -Name @($Parameters.Keys)).ForEach("Name")) { | |
| $mandelbrot.$key = $Parameters[$key] | |
| } | |
| $null = $mandelbrot.Init() | |
| } | |
| process { | |
| $x = 0 | |
| $c = 0 | |
| while($mandelbrot.MoveNext()) { | |
| $x, $y, $c = $mandelbrot.Current | |
| if(Write-Pixel $c -ColorViewLimit:$ColorViewLimit -Invert:$invert) { | |
| $InterestingCoordinates += [PSCustomObject]@{x=@($x, $mandelbrot.x_range[$x]); y=@($y, $mandelbrot.y_range[$y])} | |
| } | |
| if($x -eq $Columns - 1) { | |
| $x = 0 | |
| Write-Host | |
| } | |
| } | |
| } | |
| end { | |
| if(($DebugPreference -gt "SilentlyContinue") -and $InterestingCoordinates) { | |
| Write-Debug "Interesting coordinates: $(($InterestingCoordinates | Out-String -Stream) -join "`n")" | |
| $InterestingCoordinates = @() | |
| } | |
| $Script:LastFavorite = ($FavoriteView + 1) % $MandelbrotFavoriteViews.Count | |
| $fav = $MandelbrotFavoriteViews[$Script:LastFavorite] | |
| $Mandelbrot.HorizontalViewOffset = $fav.HorizontalViewOffset | |
| $Mandelbrot.VerticalViewOffset = $fav.VerticalViewOffset | |
| $Mandelbrot.ZoomViewDistance = $fav.ZoomViewDistance | |
| $ColorViewLimit = $fav.ColorViewLimit | |
| if($DebugPreference -gt "SilentlyContinue") { | |
| # Only used for debugging new locations: | |
| Write-Debug "color range: $color_range" | |
| } | |
| $color_range = $null | |
| return | |
| } | |
| } | |
| Export-ModuleMember New-Mandelbrot -Variable MandelbrotFavoriteViews |