From c643e65b8cd1044db7256bf937aaf0ae0f095367 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sat, 9 Aug 2025 14:42:32 -0700 Subject: [PATCH 01/49] feat: `Turtle.get/set_Opacity` ( Fixes #115 ) --- Types/Turtle/get_Opacity.ps1 | 14 ++++++++++++++ Types/Turtle/set_Opacity.ps1 | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Types/Turtle/get_Opacity.ps1 create mode 100644 Types/Turtle/set_Opacity.ps1 diff --git a/Types/Turtle/get_Opacity.ps1 b/Types/Turtle/get_Opacity.ps1 new file mode 100644 index 0000000..97b47e1 --- /dev/null +++ b/Types/Turtle/get_Opacity.ps1 @@ -0,0 +1,14 @@ +<# +.SYNOPSIS + Gets the turtle opacity +.DESCRIPTION + Gets the opacity of the turtle path. +#> +if (-not $this.'.PathAttribute') { + $this | Add-Member -MemberType NoteProperty -Name '.PathAttribute' -Value ([Ordered]@{}) -Force +} +if ($this.'.PathAttribute'.'opacity') { + return $this.'.PathAttribute'.'opacity' +} else { + return 1.0 +} \ No newline at end of file diff --git a/Types/Turtle/set_Opacity.ps1 b/Types/Turtle/set_Opacity.ps1 new file mode 100644 index 0000000..dc17539 --- /dev/null +++ b/Types/Turtle/set_Opacity.ps1 @@ -0,0 +1,14 @@ +<# +.SYNOPSIS + Sets the opacity +.DESCRIPTION + Sets the opacity of the path +.EXAMPLE + turtle forward 100 opacity 0.5 save ./dimLine.svg +#> +param( +[double] +$Opacity = 'nonzero' +) + +$this.PathAttribute = [Ordered]@{'opacity' = $Opacity} From fd319696bcea8b94467a8494cf5ad0de65e10d52 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sat, 9 Aug 2025 21:43:14 +0000 Subject: [PATCH 02/49] feat: `Turtle.get/set_Opacity` ( Fixes #115 ) --- Turtle.types.ps1xml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 90d669a..6e10a7e 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -1687,6 +1687,42 @@ return ([pscustomobject]@{ X = 0; Y = 0 }) "offset-path: $($this.PathData);" + + Opacity + + <# +.SYNOPSIS + Gets the turtle opacity +.DESCRIPTION + Gets the opacity of the turtle path. +#> +if (-not $this.'.PathAttribute') { + $this | Add-Member -MemberType NoteProperty -Name '.PathAttribute' -Value ([Ordered]@{}) -Force +} +if ($this.'.PathAttribute'.'opacity') { + return $this.'.PathAttribute'.'opacity' +} else { + return 1.0 +} + + + <# +.SYNOPSIS + Sets the opacity +.DESCRIPTION + Sets the opacity of the path +.EXAMPLE + turtle forward 100 opacity 0.5 save ./dimLine.svg +#> +param( +[double] +$Opacity = 'nonzero' +) + +$this.PathAttribute = [Ordered]@{'opacity' = $Opacity} + + + PathAttribute From 6a63eaeb43564e0299a6d865deaf6aa929d81d84 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 09:20:51 -0700 Subject: [PATCH 03/49] feat: `Turtle.LSystem` improvement ( Fixes #116 ) --- Types/Turtle/LSystem.ps1 | 66 +++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/Types/Turtle/LSystem.ps1 b/Types/Turtle/LSystem.ps1 index ea1488f..6481717 100644 --- a/Types/Turtle/LSystem.ps1 +++ b/Types/Turtle/LSystem.ps1 @@ -112,28 +112,28 @@ $Axiom, [Collections.IDictionary] $Rule = [Ordered]@{}, -[Alias('Iterations', 'Steps', 'IterationCount','StepCount')] +[Alias('Iterations', 'IterationCount', 'N', 'Steps','StepCount')] [int] -$N = 2, +$Order = 2, [Collections.IDictionary] $Variable = @{} ) -if ($n -lt 1) { return $Axiom} - $currentState = "$Axiom" -$combinedPattern = "(?>$($Rule.Keys -join '|'))" -foreach ($iteration in 1..$n) { - $currentState = $currentState -replace $combinedPattern, { - $match = $_ - $matchingRule = $rule["$match"] - if ($matchingRule -is [ScriptBlock]) { - return "$(& $matchingRule $match)" - } else { - return $matchingRule - } - } +if ($Order -ge 1) { + $combinedPattern = "(?>$($Rule.Keys -join '|'))" + foreach ($iteration in 1..$Order) { + $currentState = $currentState -replace $combinedPattern, { + $match = $_ + $matchingRule = $rule["$match"] + if ($matchingRule -is [ScriptBlock]) { + return "$(. $matchingRule $match)" + } else { + return $matchingRule + } + } + } } $localReplacement = [Ordered]@{} @@ -147,22 +147,32 @@ foreach ($key in $variable.Keys) { } $finalState = $currentState -$null = foreach ($character in $finalState.ToCharArray()) { - foreach ($key in $Variable.Keys) { - if ($character -match $key) { - $action = $localReplacement[$key] - if ($action -is [ScriptBlock]) { - . $action $character - } else { - $action - } - } - } -} + $this.PathAttribute = [Ordered]@{ - "data-l-order" = $N + "data-l-order" = $Order "data-l-axiom" = $Axiom "data-l-rules" = ConvertTo-Json $Rule "data-l-expanded" = $finalState } + +$MatchesAny = "(?>$($variable.Keys -join '|'))" +$allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePatternWhitespace')) +$matchCache = @{} +:nextMatch foreach ($match in $allMatches) { + $m = "$match" + if (-not $matchCache[$m]) { + foreach ($key in $Variable.Keys) { + if (-not ($match -match $key)) { continue } + # if ($variable[$key] -isnot [ScriptBlock]) { continue } + $matchCache[$m] = $localReplacement[$key] + break + } + } + + if ($matchCache[$m] -is [ScriptBlock]) { + $Orderull = . $matchCache[$m] $match + continue nextMatch + } +} + return $this From 1ee75ee7649545eea16051d3ab34ac6ed9d018f2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 16:21:14 +0000 Subject: [PATCH 04/49] feat: `Turtle.LSystem` improvement ( Fixes #116 ) --- Turtle.types.ps1xml | 66 ++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 6e10a7e..3c8295a 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -716,28 +716,28 @@ $Axiom, [Collections.IDictionary] $Rule = [Ordered]@{}, -[Alias('Iterations', 'Steps', 'IterationCount','StepCount')] +[Alias('Iterations', 'IterationCount', 'N', 'Steps','StepCount')] [int] -$N = 2, +$Order = 2, [Collections.IDictionary] $Variable = @{} ) -if ($n -lt 1) { return $Axiom} - $currentState = "$Axiom" -$combinedPattern = "(?>$($Rule.Keys -join '|'))" -foreach ($iteration in 1..$n) { - $currentState = $currentState -replace $combinedPattern, { - $match = $_ - $matchingRule = $rule["$match"] - if ($matchingRule -is [ScriptBlock]) { - return "$(& $matchingRule $match)" - } else { - return $matchingRule - } - } +if ($Order -ge 1) { + $combinedPattern = "(?>$($Rule.Keys -join '|'))" + foreach ($iteration in 1..$Order) { + $currentState = $currentState -replace $combinedPattern, { + $match = $_ + $matchingRule = $rule["$match"] + if ($matchingRule -is [ScriptBlock]) { + return "$(. $matchingRule $match)" + } else { + return $matchingRule + } + } + } } $localReplacement = [Ordered]@{} @@ -751,24 +751,34 @@ foreach ($key in $variable.Keys) { } $finalState = $currentState -$null = foreach ($character in $finalState.ToCharArray()) { - foreach ($key in $Variable.Keys) { - if ($character -match $key) { - $action = $localReplacement[$key] - if ($action -is [ScriptBlock]) { - . $action $character - } else { - $action - } - } - } -} + $this.PathAttribute = [Ordered]@{ - "data-l-order" = $N + "data-l-order" = $Order "data-l-axiom" = $Axiom "data-l-rules" = ConvertTo-Json $Rule "data-l-expanded" = $finalState } + +$MatchesAny = "(?>$($variable.Keys -join '|'))" +$allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePatternWhitespace')) +$matchCache = @{} +:nextMatch foreach ($match in $allMatches) { + $m = "$match" + if (-not $matchCache[$m]) { + foreach ($key in $Variable.Keys) { + if (-not ($match -match $key)) { continue } + # if ($variable[$key] -isnot [ScriptBlock]) { continue } + $matchCache[$m] = $localReplacement[$key] + break + } + } + + if ($matchCache[$m] -is [ScriptBlock]) { + $Orderull = . $matchCache[$m] $match + continue nextMatch + } +} + return $this From 73e12b945391fa87cde987bfe51507df6928d4db Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 09:23:04 -0700 Subject: [PATCH 05/49] feat: `Turtle.LSystem` improvement ( Fixes #116 ) Correcting nullification --- Types/Turtle/LSystem.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Types/Turtle/LSystem.ps1 b/Types/Turtle/LSystem.ps1 index 6481717..4ea4941 100644 --- a/Types/Turtle/LSystem.ps1 +++ b/Types/Turtle/LSystem.ps1 @@ -112,7 +112,7 @@ $Axiom, [Collections.IDictionary] $Rule = [Ordered]@{}, -[Alias('Iterations', 'IterationCount', 'N', 'Steps','StepCount')] +[Alias('Iterations', 'IterationCount', 'N', 'Steps', 'N','StepCount')] [int] $Order = 2, @@ -170,7 +170,7 @@ $matchCache = @{} } if ($matchCache[$m] -is [ScriptBlock]) { - $Orderull = . $matchCache[$m] $match + $null = . $matchCache[$m] $match continue nextMatch } } From 5fc62678489f8005b8915e000cf5ced6b352d230 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 16:23:25 +0000 Subject: [PATCH 06/49] feat: `Turtle.LSystem` improvement ( Fixes #116 ) Correcting nullification --- Turtle.types.ps1xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 3c8295a..cc29e87 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -716,7 +716,7 @@ $Axiom, [Collections.IDictionary] $Rule = [Ordered]@{}, -[Alias('Iterations', 'IterationCount', 'N', 'Steps','StepCount')] +[Alias('Iterations', 'IterationCount', 'N', 'Steps', 'N','StepCount')] [int] $Order = 2, @@ -774,7 +774,7 @@ $matchCache = @{} } if ($matchCache[$m] -is [ScriptBlock]) { - $Orderull = . $matchCache[$m] $match + $null = . $matchCache[$m] $match continue nextMatch } } From 71ad086f72074c66c8e7bb5a26f1e3b48a170aa5 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 09:30:02 -0700 Subject: [PATCH 07/49] docs: `Turtle.LSystem` improvement ( Fixes #116 ) Improving parameter docs and inner docs --- Types/Turtle/LSystem.ps1 | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Types/Turtle/LSystem.ps1 b/Types/Turtle/LSystem.ps1 index 4ea4941..583f07a 100644 --- a/Types/Turtle/LSystem.ps1 +++ b/Types/Turtle/LSystem.ps1 @@ -120,34 +120,31 @@ $Order = 2, $Variable = @{} ) +# First, let us expand our axiom $currentState = "$Axiom" +# (at least, as long as we're supposed to) if ($Order -ge 1) { $combinedPattern = "(?>$($Rule.Keys -join '|'))" foreach ($iteration in 1..$Order) { + # To expand each iteration, we replace any matching characters $currentState = $currentState -replace $combinedPattern, { $match = $_ $matchingRule = $rule["$match"] + # a matching rule could be dynamically specified with a script block if ($matchingRule -is [ScriptBlock]) { return "$(. $matchingRule $match)" } else { + # but is often statically expanded with a string. return $matchingRule } } } } -$localReplacement = [Ordered]@{} -foreach ($key in $variable.Keys) { - $localReplacement[$key] = - if ($variable[$key] -is [ScriptBlock]) { - [ScriptBlock]::Create($variable[$key]) - } else { - $variable[$key] - } -} - +# Now we know our final state $finalState = $currentState +# and can add the appropriate data attributes. $this.PathAttribute = [Ordered]@{ "data-l-order" = $Order "data-l-axiom" = $Axiom @@ -155,15 +152,29 @@ $this.PathAttribute = [Ordered]@{ "data-l-expanded" = $finalState } +# Next, prepare our replacements. +# The provided script block will almost always be scoped differently +# so we need to recreate it. +$localReplacement = [Ordered]@{} +foreach ($key in $variable.Keys) { + $localReplacement[$key] = + if ($variable[$key] -is [ScriptBlock]) { + [ScriptBlock]::Create($variable[$key]) + } else { + $variable[$key] + } +} + +# Now we need to find all potential matches $MatchesAny = "(?>$($variable.Keys -join '|'))" $allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePatternWhitespace')) +# we want to minimize rematching, so create a temporary cache. $matchCache = @{} :nextMatch foreach ($match in $allMatches) { $m = "$match" if (-not $matchCache[$m]) { foreach ($key in $Variable.Keys) { - if (-not ($match -match $key)) { continue } - # if ($variable[$key] -isnot [ScriptBlock]) { continue } + if (-not ($match -match $key)) { continue } $matchCache[$m] = $localReplacement[$key] break } From 1501421c5cc05ee59856f1fed33635cdf9c7b6dd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 09:30:25 -0700 Subject: [PATCH 08/49] docs: `Turtle.LSystem` improvement ( Fixes #116 ) Improving parameter docs and inner docs --- Types/Turtle/LSystem.ps1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Types/Turtle/LSystem.ps1 b/Types/Turtle/LSystem.ps1 index 583f07a..eb44a87 100644 --- a/Types/Turtle/LSystem.ps1 +++ b/Types/Turtle/LSystem.ps1 @@ -104,20 +104,25 @@ F+F+F+F +JJJJ+ F+F+F+F ++ JJJJ' }, #> param( +# The axiom, or starting string. [Alias('Start', 'StartString', 'Initiator')] [string] $Axiom, +# The rules for expanding each iteration of the axiom. [Alias('Rules', 'ProductionRules')] [Collections.IDictionary] $Rule = [Ordered]@{}, +# The order of magnitude (or number of iterations) [Alias('Iterations', 'IterationCount', 'N', 'Steps', 'N','StepCount')] [int] $Order = 2, +# The ways each variable will be expanded. [Collections.IDictionary] $Variable = @{} + ) # First, let us expand our axiom @@ -172,7 +177,9 @@ $allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePat $matchCache = @{} :nextMatch foreach ($match in $allMatches) { $m = "$match" + # If we have not mapped the match to a script, if (-not $matchCache[$m]) { + # find the matching replacement. foreach ($key in $Variable.Keys) { if (-not ($match -match $key)) { continue } $matchCache[$m] = $localReplacement[$key] @@ -180,10 +187,14 @@ $matchCache = @{} } } + # If we have a script to run if ($matchCache[$m] -is [ScriptBlock]) { + # run it $null = . $matchCache[$m] $match + # and continue to the next match. continue nextMatch } } +# return this so we can pipe and chain this method. return $this From a9d7fd0df1e326cd6f126758eabf47e4aeff91d0 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 16:30:26 +0000 Subject: [PATCH 09/49] docs: `Turtle.LSystem` improvement ( Fixes #116 ) Improving parameter docs and inner docs --- Turtle.types.ps1xml | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index cc29e87..b1220ab 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -724,34 +724,31 @@ $Order = 2, $Variable = @{} ) +# First, let us expand our axiom $currentState = "$Axiom" +# (at least, as long as we're supposed to) if ($Order -ge 1) { $combinedPattern = "(?>$($Rule.Keys -join '|'))" foreach ($iteration in 1..$Order) { + # To expand each iteration, we replace any matching characters $currentState = $currentState -replace $combinedPattern, { $match = $_ $matchingRule = $rule["$match"] + # a matching rule could be dynamically specified with a script block if ($matchingRule -is [ScriptBlock]) { return "$(. $matchingRule $match)" } else { + # but is often statically expanded with a string. return $matchingRule } } } } -$localReplacement = [Ordered]@{} -foreach ($key in $variable.Keys) { - $localReplacement[$key] = - if ($variable[$key] -is [ScriptBlock]) { - [ScriptBlock]::Create($variable[$key]) - } else { - $variable[$key] - } -} - +# Now we know our final state $finalState = $currentState +# and can add the appropriate data attributes. $this.PathAttribute = [Ordered]@{ "data-l-order" = $Order "data-l-axiom" = $Axiom @@ -759,15 +756,29 @@ $this.PathAttribute = [Ordered]@{ "data-l-expanded" = $finalState } +# Next, prepare our replacements. +# The provided script block will almost always be scoped differently +# so we need to recreate it. +$localReplacement = [Ordered]@{} +foreach ($key in $variable.Keys) { + $localReplacement[$key] = + if ($variable[$key] -is [ScriptBlock]) { + [ScriptBlock]::Create($variable[$key]) + } else { + $variable[$key] + } +} + +# Now we need to find all potential matches $MatchesAny = "(?>$($variable.Keys -join '|'))" $allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePatternWhitespace')) +# we want to minimize rematching, so create a temporary cache. $matchCache = @{} :nextMatch foreach ($match in $allMatches) { $m = "$match" if (-not $matchCache[$m]) { foreach ($key in $Variable.Keys) { - if (-not ($match -match $key)) { continue } - # if ($variable[$key] -isnot [ScriptBlock]) { continue } + if (-not ($match -match $key)) { continue } $matchCache[$m] = $localReplacement[$key] break } From 641f6f9973355021cca5bac8c595995760d254dd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 16:31:04 +0000 Subject: [PATCH 10/49] docs: `Turtle.LSystem` improvement ( Fixes #116 ) Improving parameter docs and inner docs --- Turtle.types.ps1xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index b1220ab..2972280 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -708,20 +708,25 @@ F+F+F+F +JJJJ+ F+F+F+F ++ JJJJ' }, #> param( +# The axiom, or starting string. [Alias('Start', 'StartString', 'Initiator')] [string] $Axiom, +# The rules for expanding each iteration of the axiom. [Alias('Rules', 'ProductionRules')] [Collections.IDictionary] $Rule = [Ordered]@{}, +# The order of magnitude (or number of iterations) [Alias('Iterations', 'IterationCount', 'N', 'Steps', 'N','StepCount')] [int] $Order = 2, +# The ways each variable will be expanded. [Collections.IDictionary] $Variable = @{} + ) # First, let us expand our axiom @@ -776,7 +781,9 @@ $allMatches = @([Regex]::Matches($finalState, $MatchesAny, 'IgnoreCase,IgnorePat $matchCache = @{} :nextMatch foreach ($match in $allMatches) { $m = "$match" + # If we have not mapped the match to a script, if (-not $matchCache[$m]) { + # find the matching replacement. foreach ($key in $Variable.Keys) { if (-not ($match -match $key)) { continue } $matchCache[$m] = $localReplacement[$key] @@ -784,12 +791,16 @@ $matchCache = @{} } } + # If we have a script to run if ($matchCache[$m] -is [ScriptBlock]) { + # run it $null = . $matchCache[$m] $match + # and continue to the next match. continue nextMatch } } +# return this so we can pipe and chain this method. return $this From a79c8612512dde1ad7e629551e42a746a7b4de65 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 10:42:11 -0700 Subject: [PATCH 11/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Types/Turtle/get_PathAnimation.ps1 | 3 +++ Types/Turtle/get_PathElement.ps1 | 6 +++-- Types/Turtle/set_PathAnimation.ps1 | 35 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Types/Turtle/get_PathAnimation.ps1 create mode 100644 Types/Turtle/set_PathAnimation.ps1 diff --git a/Types/Turtle/get_PathAnimation.ps1 b/Types/Turtle/get_PathAnimation.ps1 new file mode 100644 index 0000000..b1aef2e --- /dev/null +++ b/Types/Turtle/get_PathAnimation.ps1 @@ -0,0 +1,3 @@ +if ($this.'.PathAnimation') { + return $this.'.PathAnimation' +} diff --git a/Types/Turtle/get_PathElement.ps1 b/Types/Turtle/get_PathElement.ps1 index 504103e..6e36b0e 100644 --- a/Types/Turtle/get_PathElement.ps1 +++ b/Types/Turtle/get_PathElement.ps1 @@ -5,9 +5,11 @@ if ($this.StrokeWidth) { $this.StrokeWidth } else { '0.1%' } )' fill='$($this.Fill)' class='$( $this.PathClass -join ' ' -)' $( +)' transform-origin='50% 50%' $( foreach ($pathAttributeName in $this.PathAttribute.Keys) { " $pathAttributeName='$($this.PathAttribute[$pathAttributeName])'" } -) />" +)>" +if ($this.PathAnimation) {$this.PathAnimation} +"" ) -as [xml] \ No newline at end of file diff --git a/Types/Turtle/set_PathAnimation.ps1 b/Types/Turtle/set_PathAnimation.ps1 new file mode 100644 index 0000000..95000f2 --- /dev/null +++ b/Types/Turtle/set_PathAnimation.ps1 @@ -0,0 +1,35 @@ +param( +[PSObject] +$PathAnimation +) + +$newAnimation = @(foreach ($animation in $PathAnimation) { + if ($animation -is [Collections.IDictionary]) { + $animationCopy = [Ordered]@{} + $animation + if (-not $animationCopy['attributeType']) { + $animationCopy['attributeType'] = 'XML' + } + if (-not $animationCopy['attributeName']) { + $animationCopy['attributeName'] = 'transform' + } + if ($animationCopy.values -is [object[]]) { + $animationCopy['values'] = $animationCopy['values'] -join ';' + } + + $elementName = 'animate' + if ($animationCopy['attributeName'] -eq 'transform') { + $elementName = 'animateTransform' + } + + "<$elementName $( + @(foreach ($key in $animationCopy.Keys) { + " $key='$([Web.HttpUtility]::HtmlAttributeEncode($animationCopy[$key]))'" + }) -join '' + )/>" + } + if ($animation -is [string]) { + $animation + } +}) + +$this | Add-Member -MemberType NoteProperty -Force -Name '.PathAnimation' -Value $newAnimation From dda63d1d57df957b2af46c777f05ffc2a396f2c2 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 10 Aug 2025 17:42:30 +0000 Subject: [PATCH 12/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Turtle.types.ps1xml | 53 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 2972280..997c4f2 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -1755,6 +1755,53 @@ $this.PathAttribute = [Ordered]@{'opacity' = $Opacity} + + PathAnimation + + if ($this.'.PathAnimation') { + return $this.'.PathAnimation' +} + + + + param( +[PSObject] +$PathAnimation +) + +$newAnimation = @(foreach ($animation in $PathAnimation) { + if ($animation -is [Collections.IDictionary]) { + $animationCopy = [Ordered]@{} + $animation + if (-not $animationCopy['attributeType']) { + $animationCopy['attributeType'] = 'XML' + } + if (-not $animationCopy['attributeName']) { + $animationCopy['attributeName'] = 'transform' + } + if ($animationCopy.values -is [object[]]) { + $animationCopy['values'] = $animationCopy['values'] -join ';' + } + + $elementName = 'animate' + if ($animationCopy['attributeName'] -eq 'transform') { + $elementName = 'animateTransform' + } + + "<$elementName $( + @(foreach ($key in $animationCopy.Keys) { + " $key='$([Web.HttpUtility]::HtmlAttributeEncode($animationCopy[$key]))'" + }) -join '' + )/>" + } + if ($animation -is [string]) { + $animation + } +}) + +$this | Add-Member -MemberType NoteProperty -Force -Name '.PathAnimation' -Value $newAnimation + + + PathAttribute @@ -1837,11 +1884,13 @@ $this | Add-Member -MemberType NoteProperty -Force -Name '.PathClass' -Value @( if ($this.StrokeWidth) { $this.StrokeWidth } else { '0.1%' } )' fill='$($this.Fill)' class='$( $this.PathClass -join ' ' -)' $( +)' transform-origin='50% 50%' $( foreach ($pathAttributeName in $this.PathAttribute.Keys) { " $pathAttributeName='$($this.PathAttribute[$pathAttributeName])'" } -) />" +)>" +if ($this.PathAnimation) {$this.PathAnimation} +"</path>" ) -as [xml] From d5f837554b8128bee552b14785e36e76fcf86e19 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:02 +0000 Subject: [PATCH 13/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/BoxFractal.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/BoxFractal.svg b/Examples/BoxFractal.svg index 0c6a946..e745d7c 100644 --- a/Examples/BoxFractal.svg +++ b/Examples/BoxFractal.svg @@ -1,6 +1,7 @@ - + + \ No newline at end of file From dba7eab4c0b0aee2ba57f8a8370bf5868e06cc42 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:03 +0000 Subject: [PATCH 14/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/EndlessBoxFractal.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/EndlessBoxFractal.svg b/Examples/EndlessBoxFractal.svg index a0f3f41..b31525d 100644 --- a/Examples/EndlessBoxFractal.svg +++ b/Examples/EndlessBoxFractal.svg @@ -6,7 +6,8 @@ - + + From af06aecc7fa7f8c188d048c5d2f4f5033766d973 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:03 +0000 Subject: [PATCH 15/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/EndlessHilbert.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/EndlessHilbert.svg b/Examples/EndlessHilbert.svg index c130204..996cd0e 100644 --- a/Examples/EndlessHilbert.svg +++ b/Examples/EndlessHilbert.svg @@ -6,7 +6,8 @@ - + + From a8d772ed56fd12a9dce07997f7ece94502bbd229 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:03 +0000 Subject: [PATCH 16/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/EndlessSierpinskiTrianglePattern.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/EndlessSierpinskiTrianglePattern.svg b/Examples/EndlessSierpinskiTrianglePattern.svg index 0f93759..01fdacc 100644 --- a/Examples/EndlessSierpinskiTrianglePattern.svg +++ b/Examples/EndlessSierpinskiTrianglePattern.svg @@ -6,7 +6,8 @@ - + + From 9103165c1d620577eccc670fb6b3b953df4caca1 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:03 +0000 Subject: [PATCH 17/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/EndlessSnowflake.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/EndlessSnowflake.svg b/Examples/EndlessSnowflake.svg index 17575f2..5e944d6 100644 --- a/Examples/EndlessSnowflake.svg +++ b/Examples/EndlessSnowflake.svg @@ -6,7 +6,8 @@ - + + From 0f6656c5236ae668579c0ec125f1a7f5296edf4a Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sun, 10 Aug 2025 17:43:04 +0000 Subject: [PATCH 18/49] feat: `Turtle.get/set_PathAnimation` ( Fixes #117 ) --- Examples/SierpinskiTriangle.svg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Examples/SierpinskiTriangle.svg b/Examples/SierpinskiTriangle.svg index 30b944f..8d733a4 100644 --- a/Examples/SierpinskiTriangle.svg +++ b/Examples/SierpinskiTriangle.svg @@ -1,6 +1,7 @@ - + + \ No newline at end of file From 03ebafec537fb90542cd82f758e7382cf85a7b24 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Tue, 12 Aug 2025 21:08:57 -0700 Subject: [PATCH 19/49] feat: `Turtle.ArcLeft/ArcRight` ( Fixes #118 ) --- Types/Turtle/Alias.psd1 | 2 ++ Types/Turtle/ArcLeft.ps1 | 24 ++++++++++++++++++++++++ Types/Turtle/ArcRight.ps1 | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 Types/Turtle/ArcLeft.ps1 create mode 100644 Types/Turtle/ArcRight.ps1 diff --git a/Types/Turtle/Alias.psd1 b/Types/Turtle/Alias.psd1 index 9630759..b3bc369 100644 --- a/Types/Turtle/Alias.psd1 +++ b/Types/Turtle/Alias.psd1 @@ -16,4 +16,6 @@ MoveTo = 'Teleport' Back = 'Backward' bk = 'Backward' + ArcR = 'ArcRight' + ArcL = 'ArcLeft' } \ No newline at end of file diff --git a/Types/Turtle/ArcLeft.ps1 b/Types/Turtle/ArcLeft.ps1 new file mode 100644 index 0000000..c6095ee --- /dev/null +++ b/Types/Turtle/ArcLeft.ps1 @@ -0,0 +1,24 @@ +<# +.SYNOPSIS + Arcs the turtle to the left +.DESCRIPTION + Arcs the turtle to the left (counter-clockwise) a number of degrees. + + For each degree, the turtle will move forward and rotate. +.NOTES + The amount moved forward will be the portion of the circumference. +#> +param( +# The radius of a the circle, were it to complete the arc. +[double] +$Radius = 10, + +# The angle of the arc +[double] +$Angle = 60 +) + +# Rather than duplicate logic, we will simply reverse the angle +$angle *= -1 +# and arc to the "right". +return $this.ArcRight($Radius, $Angle) \ No newline at end of file diff --git a/Types/Turtle/ArcRight.ps1 b/Types/Turtle/ArcRight.ps1 new file mode 100644 index 0000000..d6a9b85 --- /dev/null +++ b/Types/Turtle/ArcRight.ps1 @@ -0,0 +1,34 @@ +<# +.SYNOPSIS + Arcs the turtle to the right +.DESCRIPTION + Arcs the turtle to the right (clockwise) a number of degrees. + + For each degree, the turtle will move forward and rotate. +.NOTES + The amount moved forward will be the portion of the circumference. +#> +param( +# The radius of a the circle, were it to complete the arc. +[double] +$Radius = 10, + +# The angle of the arc +[double] +$Angle = 60 +) + + +# Determine the absolute angle, for this +$absAngle = [Math]::Abs($angle) +$circumferenceStep = ([Math]::PI * 2 * $Radius) / $absAngle + +$iteration = $angle / [Math]::Floor($absAngle) +$angleDelta = 0 +$null = while ([Math]::Abs($angleDelta) -lt $absAngle) { + $this.Forward($circumferenceStep) + $this.Rotate($iteration) + $angleDelta+=$iteration +} + +return $this \ No newline at end of file From 3ac06854d8200f050be23fadd08450010cef441c Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Wed, 13 Aug 2025 04:09:17 +0000 Subject: [PATCH 20/49] feat: `Turtle.ArcLeft/ArcRight` ( Fixes #118 ) --- Turtle.types.ps1xml | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 997c4f2..cf02c50 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -15,6 +15,14 @@ + + ArcL + ArcLeft + + + ArcR + ArcRight + Back Backward @@ -83,6 +91,74 @@ yPos ycor + + ArcLeft + + + + ArcRight + + Backward + + Petal + + Polygon + + + StepSpiral + From a054812dbd5d364159de1689b9a202914f11b1fd Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Wed, 13 Aug 2025 12:40:45 -0700 Subject: [PATCH 28/49] feat: Turtle.Spirolateral ( Fixes #120 ) --- Types/Turtle/Spirolateral.ps1 | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Types/Turtle/Spirolateral.ps1 diff --git a/Types/Turtle/Spirolateral.ps1 b/Types/Turtle/Spirolateral.ps1 new file mode 100644 index 0000000..948920e --- /dev/null +++ b/Types/Turtle/Spirolateral.ps1 @@ -0,0 +1,62 @@ +<# +.SYNOPSIS + Draws a spirolateral +.DESCRIPTION + Draws a spirolateral +.LINK + https://en.wikipedia.org/wiki/Spirolateral +.EXAMPLE + turtle spirolateral save ./Spirolateral.svg +.EXAMPLE + turtle spirolateral 50 144 8 save ./Spirolateral-144-8.svg +.EXAMPLE + turtle spirolateral 50 60 10 save ./Spirolateral-60-10.svg +.EXAMPLE + turtle spirolateral 50 120 6 @(1,3) save ./Spirolateral-120-6-1_3.svg +.EXAMPLE + turtle spirolateral 50 90 11 @(3,4,5) save ./Spirolateral-90-11-3_4_5.svg +#> +param( +# The base length of each side (this will be multiplied by the step number) +[double] +$Side = 10, + +# The angle of the turn +[double] +$Angle = 90, + +# The step count. +# This is the number of times the steps will be repeated. +# This is also the maximum number of iterations the shape will complete. +[int] +$StepCount = 10, + +# The step numbers that are left turns (counter-clockwise). +# This allows the creation of general spirolaterals +[Parameter(ValueFromRemainingArguments)] +[int[]] +$LeftTurnSteps +) + +$stepNumber = 1 +$majorStepCount = 0 +do { + $null = for ($stepNumber = 1; $stepNumber -le [Math]::Abs($StepCount); $stepNumber++) { + $null = $this.Forward($side * $stepNumber) + if ($LeftTurnSteps) { + if ($LeftTurnSteps -contains $stepNumber) { + $this.Left($angle) + } else { + $this.Right($angle) + } + } else { + $this.Right($angle) + } + } + $majorStepCount++ +} until ( + (-not ([Math]::Round($this.Heading, 5) % 360 )) -or + $majorStepCount -gt $StepCount +) + +return $this From b517669d027ad6685a786117a50fe42ec62aea6d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Wed, 13 Aug 2025 19:41:01 +0000 Subject: [PATCH 29/49] feat: Turtle.Spirolateral ( Fixes #120 ) --- Turtle.types.ps1xml | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 18a10f8..de164d4 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -1285,6 +1285,74 @@ return $this.LSystem('F-G-G', [Ordered]@{ + + Spirolateral + + Square @@ -1526,7 +1526,7 @@ return "$($this.SVG.OuterXml)" .SYNOPSIS Determines the angle towards a point .DESCRIPTION - Determines the angle from the turtle's current position towards a point. + Determines the angle from the turtle's current heading towards a point. #> param( # The X-coordinate @@ -1536,12 +1536,12 @@ param( ) # Determine the delta from the turtle's current position to the specified point -$deltaX = $this.Position.X - $X -$deltaY = $this.Position.Y - $Y +$deltaX = $X - $this.Position.X +$deltaY = $Y - $this.Position.Y # Calculate the angle in radians and convert to degrees $angle = [Math]::Atan2($deltaY, $deltaX) * 180 / [Math]::PI -# Return the angle rotate by 90 to account for the turtle's coordinate system -return $angle + 90 +# Return the angle minus the current heading (modulo 360) +return $angle - ($this.Heading % 360) From c087d9ef0142d5669ea74acc41b75879a901ad70 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 11:53:54 -0700 Subject: [PATCH 32/49] docs: `Turtle.set_PathAnimation docs ( re #117 ) --- Types/Turtle/set_PathAnimation.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Types/Turtle/set_PathAnimation.ps1 b/Types/Turtle/set_PathAnimation.ps1 index 95000f2..49d2b9b 100644 --- a/Types/Turtle/set_PathAnimation.ps1 +++ b/Types/Turtle/set_PathAnimation.ps1 @@ -1,4 +1,20 @@ +<# +.SYNOPSIS + Sets the Turtle Path Animation +.DESCRIPTION + Sets an animation for the Turtle path. +.EXAMPLE + turtle flower PathAnimation ([Ordered]@{ + attributeName = 'fill' ; values = "#4488ff;#224488;#4488ff" ; repeatCount = 'indefinite'; dur = "4.2s" # ; additive = 'sum' + }, [Ordered]@{ + attributeName = 'stroke' ; values = "#224488;#4488ff;#224488" ; repeatCount = 'indefinite'; dur = "2.1s" # ; additive = 'sum' + }, [Ordered]@{ + type = 'rotate' ; values = 0, 360 ;repeatCount = 'indefinite'; dur = "41s" + }) save ./AnimatedFlower.svg +#> param( +# The path animation object. +# This may be a string containing animation XML, XML, or a dictionary containing animation settings. [PSObject] $PathAnimation ) @@ -30,6 +46,9 @@ $newAnimation = @(foreach ($animation in $PathAnimation) { if ($animation -is [string]) { $animation } + if ($animation.OuterXml) { + $animation.OuterXml + } }) $this | Add-Member -MemberType NoteProperty -Force -Name '.PathAnimation' -Value $newAnimation From f68863feea29e32440f9218a42686fd6a016d6bf Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 18:54:11 +0000 Subject: [PATCH 33/49] docs: `Turtle.set_PathAnimation docs ( re #117 ) --- Turtle.types.ps1xml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 6eb33cc..3498c81 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -1992,7 +1992,23 @@ $this.PathAttribute = [Ordered]@{'opacity' = $Opacity} - param( + <# +.SYNOPSIS + Sets the Turtle Path Animation +.DESCRIPTION + Sets an animation for the Turtle path. +.EXAMPLE + turtle flower PathAnimation ([Ordered]@{ + attributeName = 'fill' ; values = "#4488ff;#224488;#4488ff" ; repeatCount = 'indefinite'; dur = "4.2s" # ; additive = 'sum' + }, [Ordered]@{ + attributeName = 'stroke' ; values = "#224488;#4488ff;#224488" ; repeatCount = 'indefinite'; dur = "2.1s" # ; additive = 'sum' + }, [Ordered]@{ + type = 'rotate' ; values = 0, 360 ;repeatCount = 'indefinite'; dur = "41s" + }) save ./AnimatedFlower.svg +#> +param( +# The path animation object. +# This may be a string containing animation XML, XML, or a dictionary containing animation settings. [PSObject] $PathAnimation ) @@ -2024,6 +2040,9 @@ $newAnimation = @(foreach ($animation in $PathAnimation) { if ($animation -is [string]) { $animation } + if ($animation.OuterXml) { + $animation.OuterXml + } }) $this | Add-Member -MemberType NoteProperty -Force -Name '.PathAnimation' -Value $newAnimation From b5344ea6eba817e8f67464e634b7218b3ceb5f45 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 12:03:47 -0700 Subject: [PATCH 34/49] test: Rounding test for positions Floats and irrational roots can create interesting test failures --- Turtle.tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Turtle.tests.ps1 b/Turtle.tests.ps1 index bce5b94..62306f8 100644 --- a/Turtle.tests.ps1 +++ b/Turtle.tests.ps1 @@ -36,13 +36,13 @@ describe Turtle { $turtle = $turtle.Rotate($turtle.Towards(1,1)) $turtle = $turtle.Forward($turtle.Distance(1,1)) $turtle.Heading | Should -be 45 - $turtle.Position.X | Should -be 1 - $turtle.Position.Y | Should -be 1 + [Math]::Round($turtle.Position.X,10) | Should -be 1 + [Math]::Round($turtle.Position.Y,10) | Should -be 1 $turtle = $turtle.Rotate($turtle.Towards(2,2)) $turtle = $turtle.Forward($turtle.Distance(2,2)) $turtle.Heading | Should -be 45 - $turtle.Position.X | Should -be 2 - $turtle.Position.Y | Should -be 2 + [Math]::Round($turtle.Position.Y,10) | Should -be 2 + [Math]::Round($turtle.Position.Y,10) | Should -be 2 } } From a8f76471de2eb0901dfbbb2bcc0565d09155e065 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 15:13:12 -0700 Subject: [PATCH 35/49] feat: `Turtle.FlowerPetal` ( Fixes #124 ) --- Types/Turtle/FlowerPetal.ps1 | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Types/Turtle/FlowerPetal.ps1 diff --git a/Types/Turtle/FlowerPetal.ps1 b/Types/Turtle/FlowerPetal.ps1 new file mode 100644 index 0000000..4c39a5f --- /dev/null +++ b/Types/Turtle/FlowerPetal.ps1 @@ -0,0 +1,34 @@ +<# +.SYNOPSIS + Draws a flower made of petals +.DESCRIPTION + Draws a flower made of a series of petals. + + Each petal is a combination of two arcs and rotations. +.EXAMPLE + turtle FlowerPetal 60 +#> +param( +# The radius of the flower +[double] +$Radius = 10, + +# The rotation per step +[double] +$Rotation = 30, + +# The angle of the petal. +[double] +$PetalAngle = 60, + +# The number of steps. +[ValidateRange(1,1mb)] +[int] +$StepCount = 12 +) + +foreach ($n in 1..$stepCount) { + $this = $this.Petal($radius, $PetalAngle).Rotate($Rotation) +} + +return $this \ No newline at end of file From 7f4980fd92b781d531b3d4a4b1455b4c8feeacfa Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 22:13:31 +0000 Subject: [PATCH 36/49] feat: `Turtle.FlowerPetal` ( Fixes #124 ) --- Turtle.types.ps1xml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 3498c81..b8f8d17 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -391,6 +391,45 @@ $null = foreach ($n in 1..$StepCount) { $this.Rotate($Rotation) } +return $this + + + + FlowerPetal + From 12a1c259e755761b545bb4723195cff9527d7e30 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 15:29:03 -0700 Subject: [PATCH 37/49] feat: `Turtle.get/set_Width/Height` ( Fixes #125 ) --- Types/Turtle/get_Height.ps1 | 20 ++++++++++++++++++++ Types/Turtle/get_Width.ps1 | 18 ++++++++++++++++++ Types/Turtle/set_Height.ps1 | 15 +++++++++++++++ Types/Turtle/set_Width.ps1 | 16 ++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 Types/Turtle/get_Height.ps1 create mode 100644 Types/Turtle/get_Width.ps1 create mode 100644 Types/Turtle/set_Height.ps1 create mode 100644 Types/Turtle/set_Width.ps1 diff --git a/Types/Turtle/get_Height.ps1 b/Types/Turtle/get_Height.ps1 new file mode 100644 index 0000000..fe28814 --- /dev/null +++ b/Types/Turtle/get_Height.ps1 @@ -0,0 +1,20 @@ +<# +.SYNOPSIS + Gets the Turtle height +.DESCRIPTION + Gets the Turtle's ViewBox height. +.NOTES + If this has not been set, it will be automatically computed from the distance between the minimum and maximum. +.EXAMPLE + turtle rotate 90 forward 100 width +#> +param() +if ($this.'.ViewBox') { + return @($this.'.ViewBox')[-1] +} + +$viewY = $this.Maximum.Y + ($this.Minimum.Y * -1) +return $viewY + + + diff --git a/Types/Turtle/get_Width.ps1 b/Types/Turtle/get_Width.ps1 new file mode 100644 index 0000000..27bb3d3 --- /dev/null +++ b/Types/Turtle/get_Width.ps1 @@ -0,0 +1,18 @@ +<# +.SYNOPSIS + Gets the Turtle width +.DESCRIPTION + Gets the Turtle's ViewBox width. +.NOTES + If this has not been set, it will be automatically computed from the distance between the minimum and maximum. +.EXAMPLE + turtle forward 100 width +#> +if ($this.'.ViewBox') { + return @($this.'.ViewBox')[-2] +} + +$viewX = $this.Maximum.X + ($this.Minimum.X * -1) +return $viewX + + diff --git a/Types/Turtle/set_Height.ps1 b/Types/Turtle/set_Height.ps1 new file mode 100644 index 0000000..5d39dd5 --- /dev/null +++ b/Types/Turtle/set_Height.ps1 @@ -0,0 +1,15 @@ +<# +.SYNOPSIS + Sets the turtle height +.DESCRIPTION + Sets the Turtle viewbox height. +.NOTES + Once set, it will no longer be automatically computed. +#> +param( +[double] +$height +) + +$viewBox = $this.ViewBox +$this.ViewBox = $viewBox[0],$viewBox[1],$viewbox[-2], $height diff --git a/Types/Turtle/set_Width.ps1 b/Types/Turtle/set_Width.ps1 new file mode 100644 index 0000000..6dc9f70 --- /dev/null +++ b/Types/Turtle/set_Width.ps1 @@ -0,0 +1,16 @@ +<# +.SYNOPSIS + Sets the turtle width +.DESCRIPTION + Sets the Turtle viewbox width. +.NOTES + Once set, it will no longer be automatically computed. +#> +param( +# The new viewbox width. +[double] +$Width +) + +$viewBox = $this.ViewBox +$this.ViewBox = $viewBox[0],$viewBox[1],$width, $viewBox[-1] From b373046538a650ff8b504808f1b43960824f3bea Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 22:29:24 +0000 Subject: [PATCH 38/49] feat: `Turtle.get/set_Width/Height` ( Fixes #125 ) --- Turtle.types.ps1xml | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index b8f8d17..36a14e2 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -1853,6 +1853,50 @@ if ($VerbosePreference -ne 'SilentlyContinue') { } + + Height + + <# +.SYNOPSIS + Gets the Turtle height +.DESCRIPTION + Gets the Turtle's ViewBox height. +.NOTES + If this has not been set, it will be automatically computed from the distance between the minimum and maximum. +.EXAMPLE + turtle rotate 90 forward 100 width +#> +param() +if ($this.'.ViewBox') { + return @($this.'.ViewBox')[-1] +} + +$viewY = $this.Maximum.Y + ($this.Minimum.Y * -1) +return $viewY + + + + + + + <# +.SYNOPSIS + Sets the turtle height +.DESCRIPTION + Sets the Turtle viewbox height. +.NOTES + Once set, it will no longer be automatically computed. +#> +param( +[double] +$height +) + +$viewBox = $this.ViewBox +$this.ViewBox = $viewBox[0],$viewBox[1],$viewbox[-2], $height + + + ID @@ -2609,6 +2653,49 @@ if ($chromeOutput -match '<img\ssrc="data:image/\w+;base64,(?<b64>[^"]+ + + Width + + <# +.SYNOPSIS + Gets the Turtle width +.DESCRIPTION + Gets the Turtle's ViewBox width. +.NOTES + If this has not been set, it will be automatically computed from the distance between the minimum and maximum. +.EXAMPLE + turtle forward 100 width +#> +if ($this.'.ViewBox') { + return @($this.'.ViewBox')[-2] +} + +$viewX = $this.Maximum.X + ($this.Minimum.X * -1) +return $viewX + + + + + + <# +.SYNOPSIS + Sets the turtle width +.DESCRIPTION + Sets the Turtle viewbox width. +.NOTES + Once set, it will no longer be automatically computed. +#> +param( +# The new viewbox width. +[double] +$Width +) + +$viewBox = $this.ViewBox +$this.ViewBox = $viewBox[0],$viewBox[1],$width, $viewBox[-1] + + + X From 6f710c52f474ee50ddc98b2ef06adb3adfa9f0e4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 15:51:10 -0700 Subject: [PATCH 39/49] feat: `Turtle.Horizontal/VerticalLine` ( Fixes #126 ) --- Types/Turtle/Alias.psd1 | 2 ++ Types/Turtle/HorizontalLine.ps1 | 15 +++++++++++++++ Types/Turtle/VerticalLine.ps1 | 14 ++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 Types/Turtle/HorizontalLine.ps1 create mode 100644 Types/Turtle/VerticalLine.ps1 diff --git a/Types/Turtle/Alias.psd1 b/Types/Turtle/Alias.psd1 index b3bc369..e4b0432 100644 --- a/Types/Turtle/Alias.psd1 +++ b/Types/Turtle/Alias.psd1 @@ -18,4 +18,6 @@ bk = 'Backward' ArcR = 'ArcRight' ArcL = 'ArcLeft' + HLineBy = 'HorizontalLine' + VLineBy = 'VerticalLine' } \ No newline at end of file diff --git a/Types/Turtle/HorizontalLine.ps1 b/Types/Turtle/HorizontalLine.ps1 new file mode 100644 index 0000000..111600c --- /dev/null +++ b/Types/Turtle/HorizontalLine.ps1 @@ -0,0 +1,15 @@ +<# +.SYNOPSIS + Draws a horizontal line +.DESCRIPTION + Draws a horizontal line. + + The heading will not be changed. +#> +param( +[double] +$Distance +) + + +$this.GoTo($this.Position.X + $Distance, $this.Position.Y) diff --git a/Types/Turtle/VerticalLine.ps1 b/Types/Turtle/VerticalLine.ps1 new file mode 100644 index 0000000..de0dfd3 --- /dev/null +++ b/Types/Turtle/VerticalLine.ps1 @@ -0,0 +1,14 @@ +<# +.SYNOPSIS + Draws a vertical line +.DESCRIPTION + Draws a vertical line. + + The heading will not be changed. +#> +param( +[double] +$Distance +) + +$this.GoTo($this.Position.X, $this.Position.Y + $Distance) From 77e04a4f2832934f96016763885c186064d11115 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Thu, 14 Aug 2025 22:51:31 +0000 Subject: [PATCH 40/49] feat: `Turtle.Horizontal/VerticalLine` ( Fixes #126 ) --- Turtle.types.ps1xml | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml index 36a14e2..57d0b90 100644 --- a/Turtle.types.ps1xml +++ b/Turtle.types.ps1xml @@ -39,6 +39,10 @@ fd Forward + + HLineBy + HorizontalLine + l Left @@ -83,6 +87,10 @@ up PenUp + + VLineBy + VerticalLine + xPos xcor @@ -572,6 +580,27 @@ return $this.Teleport(0,0) + + HorizontalLine + + Jump + + VerticalLine + + xcor