param( [string]$OutputPath = "" ) # Writes src\CultMath\Swizzles.g.cs: HLSL swizzles and mixed constructors for # CultMath's vector types. The output is checked in; rerun after changing the # family table below and commit both files together. $ErrorActionPreference = "Stop" if (-not $OutputPath) { $OutputPath = Join-Path $PSScriptRoot "..\src\CultMath\Swizzles.g.cs" } # Functions: emit math.(...) constructor functions. double has none because # a math.double3 method group would hide the double3 type name under # `using static CultMath.math;` and HLSL shader code does not construct doubles. $families = @( @{ Scalar = "float"; Sizes = @(2, 3, 4); Functions = $true }, @{ Scalar = "int"; Sizes = @(2, 3, 4); Functions = $true }, @{ Scalar = "bool"; Sizes = @(2, 3, 4); Functions = $true }, @{ Scalar = "double"; Sizes = @(2, 3); Functions = $false } ) $componentFields = @("x", "y", "z", "w") $namings = @(@("x", "y", "z", "w"), @("r", "g", "b", "a")) function Get-Compositions([int]$total) { # Ordered part sizes summing to $total, excluding the all-scalar form that the # hand-written base constructor already owns. $results = [System.Collections.Generic.List[object]]::new() function Expand([int]$remaining, [int[]]$prefix) { if ($remaining -eq 0) { if (@($prefix | Where-Object { $_ -ne 1 }).Count -gt 0) { $results.Add($prefix) } return } for ($part = 1; $part -le $remaining; $part++) { Expand ($remaining - $part) ($prefix + $part) } } Expand $total @() return , $results } $builder = [System.Text.StringBuilder]::new() function Line([string]$text = "") { [void]$builder.Append($text).Append("`n") } Line "// " Line "// Generated by tools/generate-swizzles.ps1. Do not edit by hand." Line "// " Line "" Line "namespace CultMath;" $functionLines = [System.Collections.Generic.List[string]]::new() foreach ($family in $families) { $scalar = $family.Scalar $maxSize = ($family.Sizes | Measure-Object -Maximum).Maximum foreach ($size in $family.Sizes) { $type = "$scalar$size" Line "" Line "public partial struct $type" Line "{" foreach ($composition in (Get-Compositions $size)) { $parameters = @() $arguments = @() $offset = 0 foreach ($part in $composition) { $name = -join $componentFields[$offset..($offset + $part - 1)] if ($part -eq 1) { $parameters += "$scalar $name" $arguments += $name } else { $parameters += "$scalar$part $name" for ($i = 0; $i -lt $part; $i++) { $arguments += "$name.$($componentFields[$i])" } } $offset += $part } $parameterList = $parameters -join ", " Line " public $type($parameterList) : this($($arguments -join ', ')) { }" if ($family.Functions) { $names = @($parameters | ForEach-Object { ($_ -split " ")[1] }) $functionLines.Add(" public static $type $type($parameterList) => new($($names -join ', '));") } } # Single-component color aliases; x/y/z/w are the fields themselves. $letters = $namings[1] for ($i = 0; $i -lt $size; $i++) { Line " public $scalar $($letters[$i]) { readonly get => $($componentFields[$i]); set => $($componentFields[$i]) = value; }" } foreach ($letters in $namings) { for ($length = 2; $length -le $maxSize; $length++) { $count = [math]::Pow($size, $length) for ($index = 0; $index -lt $count; $index++) { [int[]]$digits = @() [int]$remainder = $index for ($d = 0; $d -lt $length; $d++) { $digits = @([int]($remainder % $size)) + $digits $remainder = [int][math]::Floor($remainder / $size) } $name = -join ($digits | ForEach-Object { $letters[$_] }) $getter = ($digits | ForEach-Object { $componentFields[$_] }) -join ", " $resultType = "$scalar$length" $unique = @($digits | Select-Object -Unique).Count -eq $length if ($unique) { $assignments = for ($d = 0; $d -lt $length; $d++) { "$($componentFields[$digits[$d]]) = value.$($componentFields[$d]);" } Line " public $resultType $name { readonly get => new($getter); set { $($assignments -join ' ') } }" } else { Line " public readonly $resultType $name => new($getter);" } } } } Line "}" } } Line "" Line "public static partial class math" Line "{" foreach ($functionLine in $functionLines) { Line $functionLine } Line "}" $fullPath = [System.IO.Path]::GetFullPath($OutputPath) $content = $builder.ToString() -replace "`n", "`r`n" [System.IO.File]::WriteAllText($fullPath, $content, [System.Text.UTF8Encoding]::new($false)) Write-Host "Wrote $fullPath ($((Get-Item -LiteralPath $fullPath).Length) bytes)"