Scripts/circle-packing.ps1

# Unique Concept: Circle packing algorithm with growing circles filling space organically.
# Circles expand until touching neighbors creating an efficient space-filling pattern.


$ErrorActionPreference = 'Stop'
$esc = [char]27
$reset = "$esc[0m"

function Convert-HsvToRgb {
    param([double]$Hue, [double]$Saturation, [double]$Value)
    $h = ($Hue % 1) * 6
    $sector = [math]::Floor($h)
    $fraction = $h - $sector
    $p = $Value * (1 - $Saturation)
    $q = $Value * (1 - $fraction * $Saturation)
    $t = $Value * (1 - (1 - $fraction) * $Saturation)
    switch ($sector) {
        0 { $r = $Value; $g = $t; $b = $p }
        1 { $r = $q; $g = $Value; $b = $p }
        2 { $r = $p; $g = $Value; $b = $t }
        3 { $r = $p; $g = $q; $b = $Value }
        4 { $r = $t; $g = $p; $b = $Value }
        default { $r = $Value; $g = $p; $b = $q }
    }
    return @([int][math]::Round($r * 255), [int][math]::Round($g * 255), [int][math]::Round($b * 255))
}

$width = 110
$height = 30
$rand = [System.Random]::new(101)

# Initialize circles at random positions
$circles = for ($i = 0; $i -lt 150; $i++) {
    @{
        X   = $rand.Next($width)
        Y   = $rand.Next($height)
        R   = 0.5
        Hue = $rand.NextDouble()
    }
}

# Grow circles
$growthSteps = 25
for ($step = 0; $step -lt $growthSteps; $step++) {
    foreach ($circ in $circles) {
        # Check if can grow
        $canGrow = $true
        $maxR = 15.0

        # Check distance to all other circles
        foreach ($other in $circles) {
            if ($circ -eq $other) { continue }

            $dx = $circ.X - $other.X
            $dy = $circ.Y - $other.Y
            $dist = [math]::Sqrt($dx * $dx + $dy * $dy)

            # Must maintain gap
            $minDist = $circ.R + $other.R + 0.8
            if ($dist -lt $minDist) {
                $canGrow = $false
                break
            }
        }

        # Check bounds
        if ($circ.X - $circ.R -lt 0 -or $circ.X + $circ.R -gt $width - 1 -or
            $circ.Y - $circ.R -lt 0 -or $circ.Y + $circ.R -gt $height - 1) {
            $canGrow = $false
        }

        if ($canGrow -and $circ.R -lt $maxR) {
            $circ.R += 0.3
        }
    }
}

# Render
$grid = @{}

foreach ($circ in $circles) {
    # Draw filled circle
    for ($y = [int]($circ.Y - $circ.R); $y -le [int]($circ.Y + $circ.R); $y++) {
        for ($x = [int]($circ.X - $circ.R); $x -le [int]($circ.X + $circ.R); $x++) {
            if ($x -ge 0 -and $x -lt $width -and $y -ge 0 -and $y -lt $height) {
                $dx = $x - $circ.X
                $dy = $y - $circ.Y
                $dist = [math]::Sqrt($dx * $dx + $dy * $dy)

                if ($dist -le $circ.R) {
                    $key = "$x,$y"
                    $edgeDist = $circ.R - $dist

                    if (-not $grid.ContainsKey($key) -or $grid[$key].R -lt $circ.R) {
                        $grid[$key] = @{
                            Hue      = $circ.Hue
                            R        = $circ.R
                            EdgeDist = $edgeDist
                        }
                    }
                }
            }
        }
    }
}

for ($row = 0; $row -lt $height; $row++) {
    $sb = [System.Text.StringBuilder]::new()
    for ($col = 0; $col -lt $width; $col++) {
        $key = "$col,$row"

        if ($grid.ContainsKey($key)) {
            $cell = $grid[$key]

            $hue = $cell.Hue
            $saturation = 0.65 + 0.3 * ($cell.R / 15.0)
            $edgeRatio = $cell.EdgeDist / $cell.R
            $value = 0.4 + 0.5 * $edgeRatio

            $rgb = Convert-HsvToRgb -Hue $hue -Saturation $saturation -Value $value

            $symbol = if ($edgeRatio -lt 0.15) { '○' }
            elseif ($cell.R -gt 8) { '●' }
            elseif ($cell.R -gt 4) { '◉' }
            else { '∙' }

            $null = $sb.Append("$esc[38;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m$symbol")
        }
        else {
            $null = $sb.Append("$esc[38;2;8;8;12m ")
        }
    }
    Write-Host ($sb.ToString() + $reset)
}
Write-Host $reset