Skip to content

Commit f4cc8e3

Browse files
Merge pull request #96 from PowerShellWeb/Turtle-Growth
Turtle 0.1.1
2 parents a845bda + cdc46c7 commit f4cc8e3

30 files changed

+308
-44
lines changed

Build/Turtle.GitHubAction.PSDevOps.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ Import-BuildStep -SourcePath (
55

66
$PSScriptRoot | Split-Path | Push-Location
77

8-
New-GitHubAction -Name "TurtlePower" -Description 'Turtles in a PowerShell' -Action TurtleAction -Icon chevron-right -OutputPath .\action.yml
8+
New-GitHubAction -Name "TurtlePowerShell" -Description 'Turtles in a PowerShell' -Action TurtleAction -Icon chevron-right -OutputPath .\action.yml
99

1010
Pop-Location

CHANGELOG.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
## Turtle 0.1.1:
2+
3+
* Updates:
4+
* `Turtle.get/set_ID` allows for turtle identifiers
5+
* `Turtle.ToString()` stringifies the SVG
6+
* Fixes:
7+
* Fixing GoTo/Teleport (#90)
8+
* Fixing Position default (#85) (thanks @ninmonkey !)
9+
* Fixing Turtle Action ID (#89)
10+
* New:
11+
* `Turtle.Push()` pushes position/heading to a stack (#91)
12+
* `Turtle.Pop()` pops position/heading from a stack (#92)
13+
* `Turtle.get_Stack` gets the position stack (#93)
14+
* New Fractals:
15+
* `BinaryTree()` (#94)
16+
* `FractalPlant()` (#95)
17+
18+
---
19+
20+
## Turtle 0.1:
21+
22+
* Initial Release
23+
* Builds a Turtle Graphics engine in PowerShell
24+
* Core commands
25+
* `Get-Turtle` (alias `turtle`) runs multiple moves
26+
* `New-Turtle` create a turtle
27+
* `Move-Turtle` performas a single move
28+
* `Set-Turtle` changes a turtle
29+
* `Save-Turtle` saves a turtle
30+
31+
~~~PowerShell
32+
turtle Forward 10 Rotate 120 Forward 10 Roate 120 Forward 10 Rotate 120 |
33+
Set-Turtle Stroke '#4488ff' |
34+
Save-Turtle ./Triangle.svg
35+
~~~
36+
37+
* Core Object
38+
* `.Heading` controls the turtle heading
39+
* `.Steps` stores a list of moves as an SVG path
40+
* `.IsPenDown` controls the pen
41+
* `.Forward()` moves forward at heading
42+
* `.Rotate()` rotates the heading
43+
* `.Square()` draws a square
44+
* `.Polygon()` draws a polygon
45+
* `.Circle()` draws a circle (or partial circle)
46+
* LSystems
47+
* Turtle can draw a L system. Several are included:
48+
* `BoxFractal`
49+
* `GosperCurve`
50+
* `HilbertCurve`
51+
* `KochCurve`
52+
* `KochIsland`
53+
* `KochSnowflake`
54+
* `MooreCurve`
55+
* `PeanoCurve`
56+
* `SierpinskiTriangle`
57+
* `SierpinskiCurve`
58+
* `SierpinskiSquareCurve`
59+
* `SierpinskiArrowheadCurve`
60+
* `TerdragonCurve`
61+
* `TwinDragonCurve`
62+
63+
~~~PowerShell
64+
turtle SierpinskiTriangle 10 4 |
65+
Set-Turtle Stroke '#4488ff' |
66+
Save-Turtle ./SierpinskiTriangle.svg
67+
~~~
68+

Examples/BoxFractal.svg

Lines changed: 1 addition & 1 deletion
Loading

Examples/EndlessBoxFractal.svg

Lines changed: 1 addition & 1 deletion
Loading

Examples/EndlessHilbert.svg

Lines changed: 1 addition & 1 deletion
Loading

Examples/EndlessSierpinskiTrianglePattern.svg

Lines changed: 1 addition & 1 deletion
Loading

Examples/EndlessSnowflake.svg

Lines changed: 1 addition & 1 deletion
Loading

Examples/SierpinskiTriangle.svg

Lines changed: 1 addition & 1 deletion
Loading

Turtle.psd1

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@{
22
# Version number of this module.
3-
ModuleVersion = '0.1'
3+
ModuleVersion = '0.1.1'
44
# Description of the module
55
Description = "Turtles in a PowerShell"
66
# Script module or binary module file associated with this manifest.
@@ -30,6 +30,25 @@
3030
# A URL to the license for this module.
3131
LicenseURI = 'https://github.com/PowerShellWeb/Turtle/blob/main/LICENSE'
3232
ReleaseNotes = @'
33+
## Turtle 0.1.1:
34+
35+
* Updates:
36+
* `Turtle.get/set_ID` allows for turtle identifiers
37+
* `Turtle.ToString()` stringifies the SVG
38+
* Fixes:
39+
* Fixing GoTo/Teleport (#90)
40+
* Fixing Position default (#85) (thanks @ninmonkey !)
41+
* Fixing Turtle Action ID (#89)
42+
* New:
43+
* `Turtle.Push()` pushes position/heading to a stack (#91)
44+
* `Turtle.Pop()` pops position/heading from a stack (#92)
45+
* `Turtle.get_Stack` gets the position stack (#93)
46+
* New Fractals:
47+
* `BinaryTree()` (#94)
48+
* `FractalPlant()` (#95)
49+
50+
---
51+
3352
## Turtle 0.1:
3453
3554
* Initial Release

Turtle.types.ps1xml

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,26 @@ $Distance = 10
9898

9999
$this.Forward($Distance * -1)
100100

101+
</Script>
102+
</ScriptMethod>
103+
<ScriptMethod>
104+
<Name>BinaryTree</Name>
105+
<Script>
106+
param(
107+
[double]$Size = 20,
108+
[int]$Order = 4,
109+
[double]$Angle = 45
110+
)
111+
return $this.Rotate(-90).LSystem('0', [Ordered]@{
112+
'1' = '11'
113+
'0' = '1[0]0'
114+
}, $Order, [Ordered]@{
115+
'[01]' = { $this.Forward($Size) }
116+
'\[' = { $this.Rotate($Angle * -1).Push() }
117+
'\]' = { $this.Pop().Rotate($Angle) }
118+
})
119+
120+
101121
</Script>
102122
</ScriptMethod>
103123
<ScriptMethod>
@@ -285,6 +305,26 @@ if ($This.IsPenDown) {
285305
return $this
286306
</Script>
287307
</ScriptMethod>
308+
<ScriptMethod>
309+
<Name>FractalPlant</Name>
310+
<Script>
311+
param(
312+
[double]$Size = 20,
313+
[int]$Order = 4,
314+
[double]$Angle = 25
315+
)
316+
return $this.Rotate(-90).LSystem('-X', [Ordered]@{
317+
'X' = 'F+[[X]-X]-F[-FX]+X'
318+
'F' = 'FF'
319+
}, $Order, [Ordered]@{
320+
'F' = { $this.Forward($Size) }
321+
'\[' = { $this.Rotate($Angle * -1).Push() }
322+
'\]' = { $this.Pop().Rotate($Angle) }
323+
})
324+
325+
326+
</Script>
327+
</ScriptMethod>
288328
<ScriptMethod>
289329
<Name>GosperCurve</Name>
290330
<Script>
@@ -352,7 +392,7 @@ if ($this.IsPenDown) {
352392
} else {
353393
$this.Steps += " m $deltaX $deltaY"
354394
}
355-
$this.Position = $x, $y
395+
$this.Position = $deltaX, $deltaY
356396
return $this
357397
</Script>
358398
</ScriptMethod>
@@ -365,10 +405,10 @@ return $this
365405
[double]$Angle = 90
366406
)
367407

368-
return $this.LSystem('A', @{
408+
return $this.LSystem('A', [Ordered]@{
369409
A = '+BF-AFA-FB+'
370410
B = '-AF+BFB+FA-'
371-
}, $Order, @{
411+
}, $Order, [Ordered]@{
372412
'F' = { $this.Forward($Size) }
373413
'\+' = { $this.Rotate($Angle) }
374414
'\-' = { $this.Rotate($Angle * -1) }
@@ -682,6 +722,12 @@ $null = foreach ($character in $finalState.ToCharArray()) {
682722
}
683723
}
684724
}
725+
$this.PathAttribute = [Ordered]@{
726+
"data-l-order" = $N
727+
"data-l-axiom" = $Axiom
728+
"data-l-rules" = ConvertTo-Json $Rule
729+
"data-l-expanded" = $finalState
730+
}
685731
return $this
686732

687733
</Script>
@@ -798,6 +844,37 @@ $null = foreach ($n in 1..$SideCount) {
798844
$this.Forward($Size)
799845
$this.Rotate(360 / $SideCount)
800846
}
847+
return $this
848+
</Script>
849+
</ScriptMethod>
850+
<ScriptMethod>
851+
<Name>Pop</Name>
852+
<Script>
853+
if ($this.'.Stack' -isnot [Collections.Stack]) {
854+
return
855+
}
856+
857+
if ($this.'.Stack'.Count -eq 0) {
858+
return
859+
}
860+
861+
$popped = $this.'.Stack'.Pop()
862+
$this.PenUp().Goto($popped.Position.X, $popped.Position.Y).PenDown()
863+
$this.Heading = $popped.Heading
864+
return $this
865+
</Script>
866+
</ScriptMethod>
867+
<ScriptMethod>
868+
<Name>Push</Name>
869+
<Script>
870+
if (-not $this.'.Stack') {
871+
$this | Add-Member NoteProperty '.Stack' ([Collections.Stack]::new()) -Force
872+
}
873+
874+
$this.'.Stack'.Push(@{
875+
Position = [Ordered]@{X=$this.Position.X;Y=$this.Position.Y}
876+
Heading = $this.Heading
877+
})
801878
return $this
802879
</Script>
803880
</ScriptMethod>
@@ -1057,7 +1134,7 @@ $Y
10571134
$deltaX = $x - $this.X
10581135
$deltaY = $y - $this.Y
10591136
$this.Steps += "m $deltaX $deltaY"
1060-
$this.Position = $x, $y
1137+
$this.Position = $deltaX, $deltaY
10611138
return $this
10621139
</Script>
10631140
</ScriptMethod>
@@ -1104,6 +1181,14 @@ return $this.LSystem('F', [Ordered]@{
11041181

11051182
</Script>
11061183
</ScriptMethod>
1184+
<ScriptMethod>
1185+
<Name>ToString</Name>
1186+
<Script>
1187+
param()
1188+
1189+
return "$($this.SVG.OuterXml)"
1190+
</Script>
1191+
</ScriptMethod>
11071192
<ScriptMethod>
11081193
<Name>TwinDragonCurve</Name>
11091194
<Script>
@@ -1236,12 +1321,12 @@ $this | Add-Member NoteProperty -Name '.BackgroundColor' -Value $value -Force
12361321
$viewBox = $this.ViewBox
12371322
$null, $null, $viewX, $viewY = $viewBox
12381323
"&lt;style&gt;canvas {max-width: 100%; height: 100%}&lt;/style&gt;"
1239-
"&lt;canvas id='turtle-canvas' width='$($viewX + 1)' height='$($viewY + 1)'&gt;&lt;/canvas&gt;"
1324+
"&lt;canvas id='$($this.ID)-canvas' width='$($viewX + 1)' height='$($viewY + 1)'&gt;&lt;/canvas&gt;"
12401325

12411326
"&lt;script&gt;"
12421327
@"
12431328
window.onload = async function() {
1244-
var canvas = document.getElementById('turtle-canvas');
1329+
var canvas = document.getElementById('$($this.ID)-canvas');
12451330
var ctx = canvas.getContext('2d');
12461331
ctx.strokeStyle = '$($this.Stroke)'
12471332
ctx.lineWidth = '$(
@@ -1351,6 +1436,20 @@ if ($VerbosePreference -ne 'SilentlyContinue') {
13511436
}
13521437
</SetScriptBlock>
13531438
</ScriptProperty>
1439+
<ScriptProperty>
1440+
<Name>ID</Name>
1441+
<GetScriptBlock>
1442+
if ($this.'.ID') { return $this.'.ID'}
1443+
return 'turtle'
1444+
1445+
</GetScriptBlock>
1446+
<SetScriptBlock>
1447+
param([string]$Value)
1448+
1449+
$this | Add-Member NoteProperty '.ID' $Value -Force
1450+
1451+
</SetScriptBlock>
1452+
</ScriptProperty>
13541453
<ScriptProperty>
13551454
<Name>IsPenDown</Name>
13561455
<GetScriptBlock>
@@ -1429,7 +1528,7 @@ if ($chromeOutput -match '&lt;img\ssrc="data:image/\w+;base64,(?&lt;b64&gt;[^"]+
14291528
$segments = @(
14301529
"&lt;svg xmlns='http://www.w3.org/2000/svg' width='0%' height='0%'&gt;"
14311530
"&lt;defs&gt;"
1432-
"&lt;mask id='turtle-mask'&gt;"
1531+
"&lt;mask id='$($this.Id)-mask'&gt;"
14331532
$this.Symbol.OuterXml -replace '\&lt;\?[^\&gt;]+\&gt;'
14341533
"&lt;/mask&gt;"
14351534
"&lt;/defs&gt;"
@@ -1546,7 +1645,7 @@ $this | Add-Member -MemberType NoteProperty -Force -Name '.PathClass' -Value @(
15461645
<Name>PathElement</Name>
15471646
<GetScriptBlock>
15481647
@(
1549-
"&lt;path id='turtle-path' d='$($this.PathData)' stroke='$(
1648+
"&lt;path id='$($this.id)-path' d='$($this.PathData)' stroke='$(
15501649
if ($this.Stroke) { $this.Stroke } else { 'currentColor' }
15511650
)' stroke-width='$(
15521651
if ($this.StrokeWidth) { $this.StrokeWidth } else { '0.1%' }
@@ -1569,7 +1668,7 @@ $viewBox = $this.ViewBox
15691668
$null, $null, $viewX, $viewY = $viewBox
15701669
"&lt;svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'&gt;"
15711670
"&lt;defs&gt;"
1572-
"&lt;pattern id='turtle-pattern' patternUnits='userSpaceOnUse' width='$viewX' height='$viewY' transform-origin='50% 50%'$(
1671+
"&lt;pattern id='$($this.ID)-pattern' patternUnits='userSpaceOnUse' width='$viewX' height='$viewY' transform-origin='50% 50%'$(
15731672
if ($this.PatternTransform) {
15741673
" patternTransform='" + (
15751674
@(foreach ($key in $this.PatternTransform.Keys) {
@@ -1587,7 +1686,7 @@ $(
15871686
"&lt;rect width='10000%' height='10000%' x='-5000%' y='-5000%' fill='$($this.BackgroundColor)' transform-origin='50% 50%' /&gt;"
15881687
}
15891688
)
1590-
"&lt;rect width='10000%' height='10000%' x='-5000%' y='-5000%' fill='url(#turtle-pattern)' transform-origin='50% 50%' /&gt;"
1689+
"&lt;rect width='10000%' height='10000%' x='-5000%' y='-5000%' fill='url(#$($this.ID)-pattern)' transform-origin='50% 50%' /&gt;"
15911690
"&lt;/svg&gt;")
15921691

15931692
$segments -join '' -as [xml]
@@ -1657,7 +1756,7 @@ $b64 = [Convert]::ToBase64String($OutputEncoding.GetBytes($thisPattern.outerXml)
16571756
$segments = @(
16581757
"&lt;svg xmlns='http://www.w3.org/2000/svg' width='0%' height='0%'&gt;"
16591758
"&lt;defs&gt;"
1660-
"&lt;mask id='turtle-mask'&gt;"
1759+
"&lt;mask id='$($this.ID)-mask'&gt;"
16611760
$this.Pattern.OuterXml -replace '\&lt;\?[^\&gt;]+\&gt;'
16621761
"&lt;/mask&gt;"
16631762
"&lt;/defs&gt;"
@@ -1744,9 +1843,10 @@ if ($chromeOutput -match '&lt;img\ssrc="data:image/png;base64,(?&lt;b64&gt;[^"]+
17441843
<Name>Position</Name>
17451844
<GetScriptBlock>
17461845
if (-not $this.'.Position') {
1747-
$this | Add-Member -MemberType NoteProperty -Force -Name '.Position' -Value [pscustomobject]@{ X = 0; Y = 0 }
1846+
$this | Add-Member -MemberType NoteProperty -Force -Name '.Position' -Value ([pscustomobject]@{ X = 0; Y = 0 })
17481847
}
17491848
return $this.'.Position'
1849+
17501850
</GetScriptBlock>
17511851
<SetScriptBlock>
17521852
param([double[]]$xy)
@@ -1777,6 +1877,16 @@ if ($posY -gt $this.'.Maximum'.Y) {
17771877
}
17781878
</SetScriptBlock>
17791879
</ScriptProperty>
1880+
<ScriptProperty>
1881+
<Name>Stack</Name>
1882+
<GetScriptBlock>
1883+
if ($null -ne $this.'.Stack'.Count) {
1884+
$this | Add-Member NoteProperty '.Stack' ([Collections.Stack]::new()) -Force
1885+
}
1886+
$this.'.Stack'
1887+
1888+
</GetScriptBlock>
1889+
</ScriptProperty>
17801890
<ScriptProperty>
17811891
<Name>Steps</Name>
17821892
<GetScriptBlock>
@@ -1865,15 +1975,15 @@ param()
18651975

18661976
@(
18671977
"&lt;svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' transform-origin='50% 50%'&gt;"
1868-
"&lt;symbol id='turtle-symbol' viewBox='$($this.ViewBox)' transform-origin='50% 50%'&gt;"
1978+
"&lt;symbol id='$($this.ID)-symbol' viewBox='$($this.ViewBox)' transform-origin='50% 50%'&gt;"
18691979
$this.PathElement.OuterXml
18701980
"&lt;/symbol&gt;"
18711981
$(
18721982
if ($this.BackgroundColor) {
18731983
"&lt;rect width='10000%' height='10000%' x='-5000%' y='-5000%' fill='$($this.BackgroundColor)' transform-origin='50% 50%' /&gt;"
18741984
}
18751985
)
1876-
"&lt;use href='#turtle-symbol' width='100%' height='100%' transform-origin='50% 50%' /&gt;"
1986+
"&lt;use href='#$($this.ID)-symbol' width='100%' height='100%' transform-origin='50% 50%' /&gt;"
18771987
"&lt;/svg&gt;"
18781988
) -join '' -as [xml]
18791989
</GetScriptBlock>

0 commit comments

Comments
 (0)