Types/Turtle/Morph.ps1
<# .SYNOPSIS Morphs a Turtle .DESCRIPTION Morphs a Turtle by animating its path. Any two paths with the same number of points can be morphed into each other smoothly. Any two paths with a different number of points will become a step-by-step animation. Since animations can include multiple complex paths, they can get quite large, and be quite beautiful. .EXAMPLE $sierpinskiTriangle = turtle SierpinskiTriangle 42 4 $SierpinskiTriangleFlipped = turtle rotate 180 SierpinskiTriangle 42 4 turtle SierpinskiTriangle 42 4 morph ( $SierpinskiTriangle, $SierpinskiTriangleFlipped, $sierpinskiTriangle ) save ./SierpinskiTriangleFlip.svg .EXAMPLE $sideCount = (3..24 | Get-Random ) $stepCount = 36 $flower = turtle rotate ((Get-Random -Max 180) * -1) flower 42 10 $sideCount $stepCount $flower2 = turtle rotate ((Get-Random -Max 180)) flower 42 50 $sideCount $stepCount $flower3 = turtle rotate ((Get-Random -Max 90)) flower 42 20 $sideCount $stepCount turtle flower 42 10 $sideCount $stepCount duration ($sideCount * 3) morph ($flower, $flower2,$flower) | save-turtle ./flowerMorph.svg Pattern .EXAMPLE $flowerAngle = (40..60 | Get-Random ) $stepCount = 36 $radius = 23..42 | Get-Random $flowerPetals = turtle rotate ((Get-Random -Max 180) * -1) flowerPetal $radius 10 $flowerAngle $stepCount $flowerPetals3 = turtle rotate ((Get-Random -Max 180)) flowerPetal $radius 40 $flowerAngle $stepCount turtle flowerPetal $radius 10 $flowerAngle $stepCount duration $radius morph ( $flowerPetals, $flowerPetals3, $flowerPetals ) | Save-Turtle ./flowerPetalMorph.svg Pattern .EXAMPLE turtle SierpinskiTriangle 42 4 morph | Save-Turtle ./SierpinskiTriangleConstruction.svg .EXAMPLE turtle stroke '#224488' fill '#4488ff' backgroundColor '#112244' rotate 60 SierpinskiTriangle 42 4 SierpinskiTriangle -42 4 morph | Save-Turtle ./SierpinskiTriangleReflectionConstructionAndFill.svg #> param( [Parameter(ValueFromRemainingArguments)] $Arguments ) $durationArgument = $null $hasPoints = $false $segmentCount = 0 $newPaths = @(foreach ($arg in $Arguments) { if ($arg -is [string]) { if ($arg -match '^\s{0,}m') { $arg $hasPoints = $true } } elseif ($arg.PathData) { $arg.PathData $hasPoints = $true } elseif ($arg.D) { $arg.D $hasPoints = $true } elseif ($arg -is [TimeSpan]) { $durationArgument = $arg } elseif ($arg -is [double] -or $arg -is [int]) { if (-not $hasPoints -and $arg -is [int]) { $segmentCount = [Math]::Abs($arg) } else { $durationArgument = [TimeSpan]::FromSeconds($arg) } } }) if (-not $newPaths) { if ($this.Steps.Count) { $stepList = @($this.PathData -join ' ' -split '(?=\p{L})' -ne '') if ($segmentCount) { $newPaths = @( for ($n = 1; $n -lt $stepList.Length; $n += ($stepList.Length/$segmentCount)) { $stepList[0..$n] -join ' ' } ) } else { $newPaths = @(foreach ($n in 1..($stepList.Length)) { $stepList[0..$n] -join ' ' }) } } else { return $this } } if ($this.PathAnimation) { $updatedAnimations = @(foreach ($animationXML in $this.PathAnimation -split '(?<=/>)') { $animationXML = $animationXML -as [xml] if (-not $animationXML) { continue } if ($animationXML.animate.attributeName -eq 'd') { $animationXML.animate.values = "$($newPaths -join ';')" } $animationXML.OuterXml }) $this.PathAnimation = $updatedAnimations } else { $this.PathAnimation += [Ordered]@{ attributeName = 'd' ; values = "$($newPaths -join ';')" ; repeatCount = 'indefinite'; dur = $( if ($durationArgument) { "$($durationArgument.TotalSeconds)s" } elseif ($this.Duration) { "$($this.Duration.TotalSeconds)s" } else { "4.2s" } ) } } return $this |