diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c42b4f..e30fbf5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## Turtle 0.1.9:
+
+* Turtle Text Path Support
+ * `Turtle.get/set_Text` controls the text (#167)
+ * `Turtle.get/set_TextAttribute` sets text attributes (#168)
+ * `Turtle.get/set_TextAnimation` animates text attributes (#171)
+* `Get-Turtle` parameter improvements (#169, #170)
+* `Get-Turtle` tracks invocation info (#157)
+
+---
+
## Turtle 0.1.8:
* Turtle Performance
diff --git a/Commands/Get-Turtle.ps1 b/Commands/Get-Turtle.ps1
index 16e97cd..9cc58ed 100644
--- a/Commands/Get-Turtle.ps1
+++ b/Commands/Get-Turtle.ps1
@@ -30,6 +30,8 @@ function Get-Turtle {
Each argument can be the name of a move of the turtle object.
After a member name is encountered, subsequent arguments will be passed to the member as parameters.
+
+ Any parameter that begins with whitespace will be split into multiple words.
.EXAMPLE
# We can write shapes as a series of steps
turtle "
@@ -370,6 +372,10 @@ function Get-Turtle {
$memberNames = $memberNames | Sort-Object @{Expression={ $_.Length };Descending=$true}, name
# Create a new turtle object in case we have no turtle input.
$currentTurtle = [PSCustomObject]@{PSTypeName='Turtle'}
+
+ $invocationInfo = $MyInvocation
+ $invocationInfo |
+ Add-Member ScriptProperty History {Get-History -Id $this.HistoryId} -Force
}
process {
@@ -381,6 +387,12 @@ function Get-Turtle {
return $PSBoundParameters.InputObject
}
+ if (-not $currentTurtle.Invocations) {
+ $currentTurtle | Add-Member NoteProperty Invocations -Force @(,$invocationInfo)
+ } elseif ($currentTurtle.Invocations -is [object[]]) {
+ $currentTurtle.Invocations += $invocationInfo
+ }
+
# First we want to split each argument into words.
# This way, it is roughly the same if you say:
@@ -388,14 +400,18 @@ function Get-Turtle {
# * `turtle forward 10`
# * `turtle 'forward', 10`
$wordsAndArguments = @(foreach ($arg in $ArgumentList) {
- # If the argument is a string, split it by whitespace.
+ # If the argument is a string, and it starts with whitespace
if ($arg -is [string]) {
- $arg -split '\s{1,}'
+ if ($arg -match '^[\r\n\s]+') {
+ $arg -split '\s{1,}'
+ } else {
+ $arg
+ }
} else {
# otherwise, leave the argument alone.
$arg
}
- })
+ })
# Now that we have a series of words, we can process them.
# We want to keep track of the current member,
@@ -406,7 +422,7 @@ function Get-Turtle {
# To do this in one pass, we will iterate through the words and arguments.
# We use an indexed loop so we can skip past claimed arguments.
- for ($argIndex =0; $argIndex -lt $wordsAndArguments.Length; $argIndex++) {
+ for ($argIndex =0; $argIndex -lt $wordsAndArguments.Length; $argIndex++) {
$arg = $wordsAndArguments[$argIndex]
# If the argument is not in the member names list, we can complain about it.
if ($arg -notin $memberNames) {
@@ -415,7 +431,6 @@ function Get-Turtle {
}
continue
}
-
# If we have a current member, we can invoke it or get it.
$currentMember = $arg
@@ -478,7 +493,16 @@ function Get-Turtle {
# If we have any arguments,
if ($argList) {
- # lets try to set it.
+ # Check to see if they are strongly typed
+ if ($memberInfo -is [Management.Automation.Runspaces.ScriptPropertyData]) {
+ $desiredType = $memberInfo.SetScriptBlock.Ast.ParamBlock.Parameters.StaticType
+ if ($desiredType -is [Type] -and
+ $argList.Length -eq 1 -and
+ $argList[0] -as $desiredType) {
+ $argList = $argList[0] -as $desiredType
+ }
+ }
+ # lets try to set it.
$currentTurtle.$currentMember = $argList
} else {
# otherwise, lets get the property
@@ -492,9 +516,9 @@ function Get-Turtle {
# Properties being returned will largely be strings or numbers, and these will always output directly.
if ($null -ne $stepOutput -and -not ($stepOutput.pstypenames -eq 'Turtle')) {
# Output the step
- $stepOutput
+ $stepOutput
# and set the output turtle to false.
- $outputTurtle = $false
+ $outputTurtle = $false
} elseif ($null -ne $stepOutput) {
# Set the current turtle to the step output.
$currentTurtle = $stepOutput
diff --git a/Examples/TurtlesOnATextPath-ATurtleCircle.svg b/Examples/TurtlesOnATextPath-ATurtleCircle.svg
new file mode 100644
index 0000000..13991b8
--- /dev/null
+++ b/Examples/TurtlesOnATextPath-ATurtleCircle.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/Examples/TurtlesOnATextPath-Morph.svg b/Examples/TurtlesOnATextPath-Morph.svg
new file mode 100644
index 0000000..b3887d5
--- /dev/null
+++ b/Examples/TurtlesOnATextPath-Morph.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/Examples/TurtlesOnATextPath.svg b/Examples/TurtlesOnATextPath.svg
new file mode 100644
index 0000000..819e67f
--- /dev/null
+++ b/Examples/TurtlesOnATextPath.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/Examples/TurtlesOnATextPath.turtle.ps1 b/Examples/TurtlesOnATextPath.turtle.ps1
new file mode 100644
index 0000000..49f3008
--- /dev/null
+++ b/Examples/TurtlesOnATextPath.turtle.ps1
@@ -0,0 +1,32 @@
+if ($PSScriptRoot) { Push-Location $PSScriptRoot}
+
+$turtlesOnATextPath = turtle rotate 90 jump 50 rotate -90 ArcRight 50 60 text 'turtles on a text path' textattribute @{'font-size'=36}
+$turtlesOnATextPath | Save-Turtle ./TurtlesOnATextPath.svg
+
+
+$textPath2 = turtle rotate 90 jump 50 rotate -90 ArcRight 50 -60
+
+$turtlesOnATextPath =
+ turtle rotate 90 jump 50 rotate -90 rotate -30 forward 200 text 'turtles on a text path' morph @(
+ turtle rotate 90 jump 50 rotate -90 rotate -10 forward 200
+ turtle rotate 90 jump 50 rotate -90 rotate -5 forward 200
+ turtle rotate 90 jump 50 rotate -90 rotate -10 forward 200
+ ) textAnimation ([Ordered]@{
+ attributeName = 'fill' ; values = "#4488ff;#224488;#4488ff" ; repeatCount = 'indefinite'; dur = "4.2s"
+ },[Ordered]@{
+ attributeName = 'font-size' ; values = "1em;1.3em;1em" ; repeatCount = 'indefinite'; dur = "4.2s"
+ },[Ordered]@{
+ attributeName = 'textLength' ; values = "100%;1%;100%" ; repeatCount = 'indefinite'; dur = "4.2s"
+ },[Ordered]@{
+ attributeName = 'x' ; values = "-100%; 100%; -50%" ; repeatCount = 'indefinite'; dur = "4.2s"
+ })
+$turtlesOnATextPath | Save-Turtle ./TurtlesOnATextPath-Morph.svg
+
+
+turtle rotate -90 circle 42 text "a turtle circle" textattribute ([Ordered]@{
+ 'x'='5%'
+ 'dominant-baseline'='text-before-edge'
+ 'letter-spacing'='.16em'
+}) save ./TurtlesOnATextPath-ATurtleCircle.svg
+
+if ($PSScriptRoot) { Pop-Location }
\ No newline at end of file
diff --git a/Turtle.psd1 b/Turtle.psd1
index 21acfbf..aa98f0e 100644
--- a/Turtle.psd1
+++ b/Turtle.psd1
@@ -1,6 +1,6 @@
@{
# Version number of this module.
- ModuleVersion = '0.1.8'
+ ModuleVersion = '0.1.9'
# Description of the module
Description = "Turtles in a PowerShell"
# Script module or binary module file associated with this manifest.
@@ -37,15 +37,14 @@
# A URL to the license for this module.
LicenseURI = 'https://github.com/PowerShellWeb/Turtle/blob/main/LICENSE'
ReleaseNotes = @'
-## Turtle 0.1.8:
-
-* Turtle Performance
- * Improving `.Steps` performance (#159)
- * Reducing Turtle Verbosity (#160)
-* New Moves:
- * Step (#161)
- * Forward,Teleport, and GoTo now use Step (#161)
-* New Reflection Examples (#162)
+## Turtle 0.1.9:
+
+* Turtle Text Path Support
+ * `Turtle.get/set_Text` controls the text (#167)
+ * `Turtle.get/set_TextAttribute` sets text attributes (#168)
+ * `Turtle.get/set_TextAnimation` animates text attributes (#171)
+* `Get-Turtle` parameter improvements (#169, #170)
+* `Get-Turtle` tracks invocation info (#157)
---
diff --git a/Turtle.types.ps1xml b/Turtle.types.ps1xml
index 5126e63..d6d1d95 100644
--- a/Turtle.types.ps1xml
+++ b/Turtle.types.ps1xml
@@ -2859,7 +2859,8 @@ $null, $null, $viewX, $viewY = $viewBox
}
)>"
$(if ($this.PatternAnimation) { $this.PatternAnimation })
- $this.PathElement.OuterXml
+ $this.PathElement.OuterXml
+ $this.TextElement.OuterXml
"</pattern>"
"</defs>"
$(
@@ -3143,6 +3144,7 @@ $this | Add-Member -MemberType NoteProperty -Force -Name '.StrokeWidth' -Value $
@(
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='$($this.ViewBox)' transform-origin='50% 50%' width='100%' height='100%'>"
$this.PathElement.OuterXml
+ $this.TextElement.OuterXml
"</svg>"
) -join '' -as [xml]
@@ -3169,6 +3171,7 @@ param()
"<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' transform-origin='50% 50%'>"
"<symbol id='$($this.ID)-symbol' viewBox='$($this.ViewBox)' transform-origin='50% 50%'>"
$this.PathElement.OuterXml
+ $this.TextElement.OuterXml
"</symbol>"
$(
if ($this.BackgroundColor) {
@@ -3180,6 +3183,165 @@ param()
) -join '' -as [xml]
+
+ Text
+
+ <#
+.SYNOPSIS
+ Gets the Turtle text
+.DESCRIPTION
+ Gets the text associated with the Turtle, if any exists.
+
+#>
+return $this.'.Text'
+
+
+ <#
+.SYNOPSIS
+ Sets the Turtle text
+.DESCRIPTION
+ Sets the text displayed along the turtle path.
+
+ Once this property is set, a text element will be displayed along with the turtle path.
+
+ To display only text, please also set the path's stroke to `transparent`
+#>
+param(
+[string[]]
+$Text
+)
+
+$this | Add-Member NoteProperty '.Text' -Force ($text -join ' ')
+
+
+
+
+ TextAnimation
+
+ if ($this.'.TextAnimation') {
+ return $this.'.TextAnimation'
+}
+
+
+
+ <#
+.SYNOPSIS
+ Sets the Turtle Text Animation
+.DESCRIPTION
+ Sets an animation for the Turtle path.
+.EXAMPLE
+ turtle rotate 90 jump 50 rotate -90 forward 100 text 'Hello World' textAnimation ([Ordered]@{
+ attributeName = 'fill' ; values = "#4488ff;#224488;#4488ff" ; repeatCount = 'indefinite'; dur = "4.2s"
+ },[Ordered]@{
+ attributeName = 'font-size' ; values = "1em;1.3em;1em" ; repeatCount = 'indefinite'; dur = "4.2s"
+ }) save ./textAnimation.svg
+#>
+param(
+# The text animation object.
+# This may be a string containing animation XML, XML, or a dictionary containing animation settings.
+[PSObject]
+$TextAnimation
+)
+
+$newAnimation = @(foreach ($animation in $TextAnimation) {
+ 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'
+ }
+
+
+ if (-not $animationCopy['dur'] -and $this.Duration) {
+ $animationCopy['dur'] = "$($this.Duration.TotalSeconds)s"
+ }
+
+ "<$elementName $(
+ @(foreach ($key in $animationCopy.Keys) {
+ " $key='$([Web.HttpUtility]::HtmlAttributeEncode($animationCopy[$key]))'"
+ }) -join ''
+ )/>"
+ }
+ if ($animation -is [string]) {
+ $animation
+ }
+ if ($animation.OuterXml) {
+ $animation.OuterXml
+ }
+})
+
+$this | Add-Member -MemberType NoteProperty -Force -Name '.TextAnimation' -Value $newAnimation
+
+
+
+
+ TextAttribute
+
+ <#
+.SYNOPSIS
+ Gets any Text Attributes
+.DESCRIPTION
+ Gets any attributes associated with the Turtle text
+
+#>
+if (-not $this.'.TextAttribute') {
+ $this | Add-Member NoteProperty '.TextAttribute' ([Ordered]@{}) -Force
+}
+return $this.'.TextAttribute'
+
+
+ <#
+.SYNOPSIS
+ Sets text attributes
+.DESCRIPTION
+ Sets any attributes associated with the turtle text.
+
+ These will become the attributes on the `<text>` element.
+#>
+param(
+# The text attributes.
+[Collections.IDictionary]
+$TextAttribute = [Ordered]@{}
+)
+
+if (-not $this.'.TextAttribute') {
+ $this | Add-Member -MemberType NoteProperty -Name '.TextAttribute' -Value ([Ordered]@{}) -Force
+}
+foreach ($key in $TextAttribute.Keys) {
+ $this.'.TextAttribute'[$key] = $TextAttribute[$key]
+}
+
+
+
+ TextElement
+
+
+if ($this.Text) {
+ return @(
+ "<text id='$($this.ID)-text' $(
+ foreach ($TextAttributeName in $this.TextAttribute.Keys) {
+ " $TextAttributeName='$($this.TextAttribute[$TextAttributeName])'"
+ }
+)>"
+ "<textPath href='#$($this.id)-path'>$([Security.SecurityElement]::Escape($this.Text))</textPath>"
+ if ($this.TextAnimation) {$this.TextAnimation}
+ "</text>"
+ ) -as [xml]
+}
+
+
+
+
ViewBox
diff --git a/Types/Turtle/get_Pattern.ps1 b/Types/Turtle/get_Pattern.ps1
index 74abe88..5a05e6e 100644
--- a/Types/Turtle/get_Pattern.ps1
+++ b/Types/Turtle/get_Pattern.ps1
@@ -14,7 +14,8 @@ $null, $null, $viewX, $viewY = $viewBox
}
)>"
$(if ($this.PatternAnimation) { $this.PatternAnimation })
- $this.PathElement.OuterXml
+ $this.PathElement.OuterXml
+ $this.TextElement.OuterXml
""
""
$(
diff --git a/Types/Turtle/get_SVG.ps1 b/Types/Turtle/get_SVG.ps1
index 4f2c4a0..5dddfa2 100644
--- a/Types/Turtle/get_SVG.ps1
+++ b/Types/Turtle/get_SVG.ps1
@@ -2,5 +2,6 @@ param()
@(
""
) -join '' -as [xml]
\ No newline at end of file
diff --git a/Types/Turtle/get_Symbol.ps1 b/Types/Turtle/get_Symbol.ps1
index e697e9a..f29d10f 100644
--- a/Types/Turtle/get_Symbol.ps1
+++ b/Types/Turtle/get_Symbol.ps1
@@ -17,6 +17,7 @@ param()
"