Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <#
- requires ps v5.0 or higher (uses ps classes)
- http://www.leeholmes.com/blog/2014/10/09/playing-with-classes-in-powershell-v5-preview/
- Playing with Classes in PowerShell v5 Preview
- Thursday, 9 October 2014
- One of the features we’re working on in the latest version of PowerShell is the ability to define custom classes.
- If you’ve ever used a PSCustomObject or a Hashtable in a script for storing related data, you’ll probably find classes to be a useful addition to your scripting toolkit.
- And Show-BoxDemo in all of its glory. TAB switches between objects, and there are a few other hotkeys in the source.
- http://www.leeholmes.com/projects/boxdemo/Show-BoxDemo.ps1
- #>
- [CmdletBinding()]
- param()
- ## Class to hold a 3d point
- class Point
- {
- Point($xCoord, $yCoord, $zCoord)
- {
- $this.X = $xCoord
- $this.Y = $yCoord
- $this.Z = $zCoord
- }
- [double] $X
- [double] $Y
- [double] $Z
- }
- ## Class that represents a 3d object that can be populated, rotated, and drawn.
- class GraphicObject
- {
- ## Creates an instance of the GraphicObject class with no default
- ## points.
- GraphicObject()
- {
- $this.Points = [System.Collections.Generic.List[Point]]::New()
- $this._backBuffer = " " * ([Console]::WindowWidth * ([Console]::WindowHeight - 1))
- }
- ## The list of points in this object
- [System.Collections.Generic.List[Point]] $Points
- ## A virtual console buffer to make updates appear smoother
- [char[]] $_backBuffer
- ## Some scale factors to account for console dimensions and character aspect
- ## ratios
- $Transpose = ([Console]::WindowWidth / 2), ([Console]::WindowHeight / 2)
- $Scale = 2, 2
- $CharacterType = [CharacterType]::Dot
- ## Draws the object
- [Void] Draw()
- {
- $pointList = [System.Collections.Generic.List[Point]]::New()
- foreach($point in $this.Points)
- {
- $pointlist.Add( [Point]::New( $point.X, $point.Y, $point.Z ))
- }
- $this.Draw($pointlist, $this.CharacterType)
- }
- ## Draws the object with the given character type
- [Void] Draw($PointsToDraw, [CharacterType] $character)
- {
- $this._backBuffer = " " * ([Console]::WindowWidth * ([Console]::WindowHeight - 1))
- ## Map the enum to an actual character
- $charToDraw = switch($character)
- {
- ([CharacterType]::Dot) { '.' }
- ([CharacterType]::Star) { '*' }
- }
- ## Iterate through the points in the object. We do this from far to near, although
- ## it doesn't actually matter in this example.
- foreach($point in $PointsToDraw | Sort-Object -Descending -Property Z)
- {
- $this._PutPixel($point.X, $point.Y, $point.Z, $charToDraw)
- }
- ## Replace the console text with what we drew to the background buffer
- [Console]::SetCursorPosition(0,0)
- [Console]::Write($this._backBuffer)
- }
- ## Rotates the object along x, y, and z.
- [void] Rotate($xDegrees, $yDegrees, $zDegrees)
- {
- $xRadians = $this._DegreesToRadians($xDegrees)
- $yRadians = $this._DegreesToRadians($yDegrees)
- $zRadians = $this._DegreesToRadians($zDegrees)
- foreach($point in $this.Points)
- {
- ## Rotate on X
- $oldX = $point.X
- $oldY = $point.Y
- $oldZ = $point.Z
- $point.Y = [Math]::Cos($xRadians) * $oldY -
- [Math]::Sin($xRadians) * $oldZ
- $point.Z = [Math]::Sin($xRadians) * $oldY +
- [Math]::Cos($xRadians) * $oldZ
- ## Rotate on Y
- $oldX = $point.X
- $oldY = $point.Y
- $oldZ = $point.Z
- $point.X = [Math]::Cos($yRadians) * $oldX -
- [Math]::Sin($yRadians) * $oldZ
- $point.Z = [Math]::Sin($yRadians) * $oldX +
- [Math]::Cos($yRadians) * $oldZ
- ## Rotate on Z
- $oldX = $point.X
- $oldY = $point.Y
- $oldZ = $point.Z
- $point.X = [Math]::Cos($zRadians) * $oldX -
- [Math]::Sin($zRadians) * $oldY
- $point.Y = [Math]::Sin($zRadians) * $oldX +
- [Math]::Cos($zRadians) * $oldY
- }
- }
- ## Helper function to write a character to the screen in the given location
- [void] _PutPixel($x, $y, $z, $character)
- {
- $scaledX = [int] ($x * $this.scale[0] + $this.transpose[0])
- $scaledY = [int] ($y * (3/4) * $this.scale[1] + $this.transpose[1])
- ## If we wanted to account for perspective or camera distance,
- ## we would apply that to X and Y based on the Z coordinate. Skip
- ## that for simplicity.
- $this._backBuffer[([Console]::WindowWidth * $scaledY) + $scaledX] = $character
- }
- ## Helper function to convert degrees to radians
- [double] _DegreesToRadians($degrees)
- {
- return $degrees / 180 * [Math]::Pi
- }
- ## Static methods to return interesting object types
- static [GraphicObject] Box()
- {
- $object = [GraphicObject]::New()
- $min = -1 * [Console]::WindowHeight / 3
- $max = [Console]::WindowHeight / 3
- for($x = $min; $x -lt $max; $x++)
- {
- $object.Points.Add([Point]::New($x, $min, $min))
- $object.Points.Add([Point]::New($x, $max, $min))
- $object.Points.Add([Point]::New($x, $min, $max))
- $object.Points.Add([Point]::New($x, $max, $max))
- }
- for($y = $min; $y -lt $max; $y++)
- {
- $object.Points.Add([Point]::New($min, $y, $min))
- $object.Points.Add([Point]::New($max,$y, $min))
- $object.Points.Add([Point]::New($min, $y, $max))
- $object.Points.Add([Point]::New($max, $y, $max))
- }
- for($z = $min; $z -lt $max; $z++)
- {
- $object.Points.Add([Point]::New($min, $min, $z))
- $object.Points.Add([Point]::New($max, $min, $z))
- $object.Points.Add([Point]::New($min, $max, $z))
- $object.Points.Add([Point]::New($max, $max, $z))
- }
- $object.Transpose = ([Console]::WindowWidth / 2), ([Console]::WindowHeight / 2)
- $scaleFactor = [Console]::WindowHeight / ($max - $min) * 2/3
- $object.Scale = $scaleFactor, $scaleFactor
- return $object
- }
- static [GraphicObject] Saddle()
- {
- $object = [GraphicObject]::New()
- $min = -1
- $max = 1
- $increment = 0.15
- for($x = $min; $x -lt $max; $x += $increment)
- {
- for($y = $min; $y -lt $max; $y += $increment)
- {
- $object.Points.Add([Point]::New($x, $y, ($x * $x) - ($y * $y)))
- }
- }
- $object.Transpose = ([Console]::WindowWidth / 2), ([Console]::WindowHeight / 2)
- $scaleFactor = [Console]::WindowHeight / ($max - $min) * 2/3
- $object.Scale = $scaleFactor, $scaleFactor
- return $object
- }
- static [GraphicObject] Helix()
- {
- $object = [GraphicObject]::New()
- $rotationDegrees = 0
- for($z = -1; $z -lt 1; $z += 0.01)
- {
- $object.Points.Add([Point]::New([Math]::Sin($rotationDegrees), [Math]::Cos($rotationDegrees), $z))
- $rotationDegrees += 0.1 % (2 * [Math]::Pi)
- }
- $object.Transpose = ([Console]::WindowWidth / 2), ([Console]::WindowHeight / 2)
- $scaleFactor = [Console]::WindowHeight / 3
- $object.Scale = $scaleFactor, $scaleFactor
- return $object
- }
- static [GraphicObject] PowerShell()
- {
- $centerX = 115
- $centerY = 168
- $max = 180
- $min = 1
- $object = [GraphicObject]::New()
- foreach($zIndex in -10,0,10)
- {
- [System.Collections.Generic.List[Point]] $newPoints =
- [GraphicObject]::DrawLine(74 - $centerX, 196 - $centerY, 194 - $centerX, 113 - $centerY, $zIndex)
- $object.Points.AddRange($newPoints)
- [System.Collections.Generic.List[Point]] $newPoints =
- [GraphicObject]::DrawLine(106 - $centerX, 34 - $centerY, 194 - $centerX, 113 - $centerY, $zIndex)
- $object.Points.AddRange($newPoints)
- [System.Collections.Generic.List[Point]] $newPoints =
- [GraphicObject]::DrawLine(134 - $centerX, 186 - $centerY, 204 - $centerX, 186 - $centerY, $zIndex)
- $object.Points.AddRange($newPoints)
- }
- $scaleFactor = [Console]::WindowHeight / ($max - $min) * 2/3
- $object.Scale = $scaleFactor, $scaleFactor
- return $object
- }
- ## Basic implementation of the Bresenham line drawing algorithm
- static [System.Collections.Generic.List[Point]] DrawLine($startX, $startY, $endX, $endY, $zIndex)
- {
- [System.Collections.Generic.List[Point]] $newPoints =
- [System.Collections.Generic.List[Point]]::New()
- $deltaX = $endX - $startX
- $deltaY = $endY - $startY
- $error = 0
- $deltaError = [Math]::Abs($deltaY / $deltaX)
- $y = $startY
- $yIncr = 1;
- $xIncr = 1;
- $xCount = [Math]::Abs($endX - $startX)
- if($endY -lt $startY) { $yIncr = -1 }
- if($endX -lt $startX) { $xIncr = -1 }
- $x = $startX
- while($xCount -gt 0)
- {
- $newPoints.Add( ([Point]::New($x, $y, $zIndex)) )
- $error = $error + $deltaError
- if($error -gt 0.5)
- {
- $y += $yIncr
- $error--
- }
- $xCount--
- $x +=$xIncr
- }
- return $newPoints
- }
- }
- ## call.
- enum CharacterType
- {
- Dot = 0
- Star = 1
- }
- ## Now play with the objects
- cls
- ## The GraphicObject class has three static factory methods to generate
- ## interesting versions of itself. Store these so that we can cycle through
- ## them with the TAB key.
- $objects = "Saddle", "PowerShell", "Box", "Helix"
- $currentObject = 0
- $object = [GraphicObject]::($objects[$currentObject]).Invoke()
- $startTime = Get-Date
- $frames = 0
- $xRot = 1
- $yRot = 1
- $zRot = 2
- $shouldExit = $false
- while(-not $shouldExit)
- {
- if([Console]::KeyAvailable)
- {
- $key = [Console]::ReadKey()
- switch($key.Key)
- {
- ## Change rotation speed
- RightArrow { $yRot++ }
- LeftArrow { $yRot-- }
- DownArrow { $xRot-- }
- UpArrow { $xRot++ }
- SpaceBar {
- $xRot, $yRot, $zRot = 1..3 | % { Get-Random -Min -5 -Max 5 }
- }
- P { $xRot, $yRot, $zRot = 0, 0, 0 }
- ## Change drawing characters
- D8 { $object.CharacterType = "Star" }
- OemPeriod { $object.CharacterType = "Dot" }
- ## Change objects
- Tab {
- $currentObject = ($currentObject + 1) % $objects.Length
- $object = [GraphicObject]::($objects[$currentObject]).Invoke()
- }
- ## Quit
- Q { $shouldExit = $true }
- default { if($PSBoundParameters["Debug"]) { $host.EnterNestedPrompt() } }
- }
- }
- $object.Draw()
- $object.Rotate($xRot, $yRot, $zRot)
- $frames++
- }
- $endTime = Get-Date
- if($PSBoundParameters['Debug']) { "`r`nFrame rate: {0} FPS" -f ($frames / ($endTime - $startTime).TotalSeconds) }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement