@@ -1944,6 +1944,192 @@ $null = @(
19441944 $this.Rotate($OppositeAngle)
19451945)
19461946
1947+ return $this
1948+ </Script >
1949+ </ScriptMethod >
1950+ <ScriptMethod >
1951+ <Name >PieGraph</Name >
1952+ <Script >
1953+ < #
1954+ .SYNOPSIS
1955+ Draws a pie graph using turtle graphics.
1956+ .DESCRIPTION
1957+ This script uses turtle graphics to draw a pie graph based on the provided data.
1958+ .EXAMPLE
1959+ turtle PieGraph 400 400 80 20 save ./80-20.svg
1960+ .EXAMPLE
1961+ turtle PieGraph 400 400 5 10 15 20 15 10 5 | Save-Turtle ./PieGraph.svg
1962+ .EXAMPLE
1963+ turtle width 400 height 400 PieGraph 400 400 @{value=20;fill='red'} @{value=40;fill='blue'} save ./PieGraphColor.svg
1964+ .EXAMPLE
1965+ turtle PieGraph 400 400 @(
1966+ 5,10,15,20,15,10,5 | Sort-Object -Descending
1967+ ) | Save-Turtle ./PieGraphDescending.svg
1968+ .EXAMPLE
1969+ turtle rotate (Get-Random -Max 360) PieGraph 400 400 @(
1970+ 5,10,15,20,15,10,5 | Sort-Object -Descending
1971+ ) | Save-Turtle ./PieGraphDescendingRotated.svg
1972+ .EXAMPLE
1973+ turtle PieGraph 200 200 (
1974+ @(1..50) |
1975+ Get-Random -Count (Get-Random -Minimum 5 -Maximum 20)
1976+ ) save ./RandomPieGraph.svg
1977+ .EXAMPLE
1978+ $n = Get-Random -Min 5 -Max 10
1979+ turtle width 200 height 200 morph @(
1980+ turtle PieGraph 200 200 @(1..50 | Get-Random -Count $n)
1981+ turtle PieGraph 200 200 @(1..50 | Get-Random -Count $n)
1982+ turtle PieGraph 200 200 @(1..50 | Get-Random -Count $n)
1983+ ) save ./RandomPieGraphMorph.svg
1984+ .EXAMPLE
1985+ turtle PieGraph 200 200 (
1986+ @(1..50;-1..-50) |
1987+ Get-Random -Count (Get-Random -Minimum 5 -Maximum 20)
1988+ ) save ./RandomPieGraphWithNegative.svg
1989+ .EXAMPLE
1990+ $randomNegativePie = turtle PieGraph 200 200 (
1991+ @(1..50;-1..-50) |
1992+ Get-Random -Count 10
1993+ )
1994+ turtle width 200 height 200 morph @(
1995+ $randomNegativePie
1996+ turtle PieGraph 200 200 (
1997+ @(1..50;-1..-50) |
1998+ Get-Random -Count 10
1999+ )
2000+ $randomNegativePie
2001+ ) save ./RandomPieGraphWithNegativeMorph.svg
2002+ #>
2003+ param(
2004+ # The radius of the bar graph
2005+ [double]$Radius,
2006+
2007+ # The points in the bar graph.
2008+ # Each point will be turned into a relative number and turned into an equal-width bar.
2009+ [Parameter(ValueFromRemainingArguments)]
2010+ [PSObject[]]
2011+ $GraphData
2012+ )
2013+
2014+
2015+ # If there were no points, we are drawing nothing, so return ourself.
2016+ if (-not $GraphData) { return $this}
2017+
2018+ filter IsPrimitive {$_.GetType -and $_.GetType().IsPrimitive}
2019+
2020+ # To make a pie graph we need to know the total, and thus we need to make a couple of passes
2021+ [double]$Total = 0.0
2022+
2023+ $sliceObjects = [Ordered]@{}
2024+ $richSlices = $false
2025+ $Slices = @(
2026+ $dataPointIndex = 0
2027+ foreach ($dataPoint in $GraphData)
2028+ {
2029+ $sliceObjects["slice$($sliceObjects.Count)"] = $dataPoint
2030+ # If the data point is a number (or other primitive data)
2031+ if ($dataPoint | IsPrimitive)
2032+ {
2033+ $Total += $dataPoint # add it to the total
2034+ $dataPoint -as [double] # and output it
2035+ }
2036+ # Otherwise, if the data point has a value that is a number
2037+ elseif ($dataPoint.value | IsPrimitive)
2038+ {
2039+ $Total += $dataPoint.value # add it to the total
2040+ $dataPoint.value -as [double] # and output that
2041+ $richSlices = $true
2042+ }
2043+ elseif ($dataPoint -is [Collections.IDictionary]) {
2044+ foreach ($key in $dataPoint.Keys) {
2045+ if ($dataPoint[$key] | IsPrimitive) {
2046+ $Total += $dataPoint[$key] # add it to the total
2047+ $dataPoint[$key] -as [double] # and output that
2048+ }
2049+ }
2050+ $richSlices = $true
2051+ }
2052+ }
2053+ )
2054+
2055+ # Turn each numeric slice into a ratio
2056+ $relativeSlices =
2057+ foreach ($slice in $Slices) { $slice/ $total }
2058+
2059+ # If we have no ratios, we have nothing to graph, and we are done here.
2060+ if (-not $relativeSlices) { return $this }
2061+
2062+ # Next let's figure out the maximum delta x and delta y
2063+ $dx = $this.X + ($Radius * 2)
2064+ $dy = $this.Y + ($Radius * 2)
2065+ # and resize our viewbox with respect to our radius
2066+ $null = $this.ResizeViewBox($Radius)
2067+
2068+ # Calulate the midpoint of the circle
2069+ $midX = $this.X + $dx/2
2070+ $midY = $this.Y + $dy/2
2071+
2072+ # and teleport to it
2073+ $null = $this.Teleport($midX, $midY)
2074+
2075+ # If we are not rendering "rich" slices, we can draw the arcs as one path.
2076+ if (-not $richSlices) {
2077+ for ($sliceNumber =0 ; $sliceNumber -lt $Slices.Length; $sliceNumber++) {
2078+ # Turn each ratio into an angle
2079+ $Angle = $relativeSlices[$sliceNumber] * 360
2080+ $this = $this.
2081+ # Draw an arc of that angle,
2082+ CircleArc($Radius, $Angle).
2083+ # then rotate by the angle.
2084+ Rotate($angle)
2085+ }
2086+ }
2087+ else {
2088+ # Otherwise, we are making multiple turtles
2089+ $nestedTurtles = [Ordered]@{}
2090+ # The idea is the same, but the implementation is more complicated
2091+ $heading = $this.Heading
2092+ if (-not $heading) { $heading = 0.0 }
2093+ for ($sliceNumber =0 ; $sliceNumber -lt $Slices.Length; $sliceNumber++) {
2094+ $Angle = $relativeSlices[$sliceNumber] * 360
2095+ $sliceName = "slice$sliceNumber"
2096+ # created a nested turtle at the midpoint
2097+ $nestedTurtles["slice$sliceNumber"] = turtle teleport $midX $midY
2098+ # with the current heading
2099+ $nestedTurtles["slice$sliceNumber"].Heading = $this.Heading
2100+ # and arc by the angle
2101+ $null = $nestedTurtles["slice$sliceNumber"].CircleArc($Radius, $Angle)
2102+
2103+ # If the slice was of a dictionary
2104+ if ($sliceObjects[$sliceName] -is [Collections.IDictionary])
2105+ {
2106+ # set any settable properties on the turtle
2107+ foreach ($key in $sliceObjects[$sliceName].Keys) {
2108+ # that exist in both the turtle and the dictionary
2109+ if ($nestedTurtles[$sliceName].psobject.properties[$key].SetterScript) {
2110+ $nestedTurtles[$sliceName].$key = $sliceObjects[$sliceName][$key]
2111+ }
2112+ }
2113+ }
2114+ # If the slice was not a string
2115+ elseif ($sliceObjects[$sliceName] -isnot [string])
2116+ {
2117+ # Set any settable properties on the turlte
2118+ foreach ($property in $sliceObjects[$sliceName].Keys) {
2119+ # that exist in both the turtle and the slice object.
2120+ if ($nestedTurtles[$sliceName].psobject.properties[$key].SetterScript) {
2121+ $nestedTurtles[$sliceName].$key = $sliceObjects[$sliceName][$key]
2122+ }
2123+ }
2124+ }
2125+
2126+ # Now rotate our own heading, even though we are not drawing anything.
2127+ $null = $this.Rotate($angle)
2128+ }
2129+ # and set our nested turtles.
2130+ $this.Turtles = $nestedTurtles
2131+ }
2132+
19472133return $this
19482134 </Script >
19492135 </ScriptMethod >
0 commit comments