Task: Penrose tiling


C++: #include <cmath>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>

int main() {
    std::ofstream out("penrose_tiling.svg");
    if (!out) {
        std::cerr << "Cannot open output file.\n";
        return EXIT_FAILURE;
    }
    std::string penrose("[N]++[N]++[N]++[N]++[N]");
    for (int i = 1; i <= 4; ++i) {
        std::string next;
        for (char ch : penrose) {
            switch (ch) {
            case 'A':
                break;
            case 'M':
                next += "OA++PA----NA[-OA----MA]++";
                break;
            case 'N':
                next += "+OA--PA[---MA--NA]+";
                break;
            case 'O':
                next += "-MA++NA[+++OA++PA]-";
                break;
            case 'P':
                next += "--OA++++MA[+PA++++NA]--NA";
                break;
            default:
                next += ch;
                break;
            }
        }
        penrose = std::move(next);
    }
    const double r = 30;
    const double pi5 = 0.628318530717959;
    double x = r * 8, y = r * 8, theta = pi5;
    std::set<std::string> svg;
    std::stack<std::tuple<double, double, double>> stack;
    for (char ch : penrose) {
        switch (ch) {
        case 'A': {
            double nx = x + r * std::cos(theta);
            double ny = y + r * std::sin(theta);
            std::ostringstream line;
            line << std::fixed << std::setprecision(3) << "<line x1='" << x
                 << "' y1='" << y << "' x2='" << nx << "' y2='" << ny << "'/>";
            svg.insert(line.str());
            x = nx;
            y = ny;
        } break;
        case '+':
            theta += pi5;
            break;
        case '-':
            theta -= pi5;
            break;
        case '[':
            stack.push({x, y, theta});
            break;
        case ']':
            std::tie(x, y, theta) = stack.top();
            stack.pop();
            break;
        }
    }
    out << "<svg xmlns='http:
        << "' width='" << r * 16 << "'>\n"
        << "<rect height='100%' width='100%' fill='black'/>\n"
        << "<g stroke='rgb(255,165,0)'>\n";
    for (const auto& line : svg)
        out << line << '\n';
    out << "</g>\n</svg>\n";
    return EXIT_SUCCESS;
}



Go: package main

import (
    "github.com/fogleman/gg"
    "math"
)

type tiletype int

const (
    kite tiletype = iota
    dart
)

type tile struct {
    tt          tiletype
    x, y        float64
    angle, size float64
}

var gr = (1 + math.Sqrt(5)) / 2 

const theta = math.Pi / 5 

func setupPrototiles(w, h int) []tile {
    var proto []tile
    
    for a := math.Pi/2 + theta; a < 3*math.Pi; a += 2 * theta {
        ww := float64(w / 2)
        hh := float64(h / 2)
        proto = append(proto, tile{kite, ww, hh, a, float64(w) / 2.5})
    }
    return proto
}

func distinctTiles(tls []tile) []tile {
    tileset := make(map[tile]bool)
    for _, tl := range tls {
        tileset[tl] = true
    }
    distinct := make([]tile, len(tileset))
    for tl, _ := range tileset {
        distinct = append(distinct, tl)
    }
    return distinct
}

func deflateTiles(tls []tile, gen int) []tile {
    if gen <= 0 {
        return tls
    }
    var next []tile
    for _, tl := range tls {
        x, y, a, size := tl.x, tl.y, tl.angle, tl.size/gr
        var nx, ny float64
        if tl.tt == dart {
            next = append(next, tile{kite, x, y, a + 5*theta, size})
            for i, sign := 0, 1.0; i < 2; i, sign = i+1, -sign {
                nx = x + math.Cos(a-4*theta*sign)*gr*tl.size
                ny = y - math.Sin(a-4*theta*sign)*gr*tl.size
                next = append(next, tile{dart, nx, ny, a - 4*theta*sign, size})
            }
        } else {
            for i, sign := 0, 1.0; i < 2; i, sign = i+1, -sign {
                next = append(next, tile{dart, x, y, a - 4*theta*sign, size})
                nx = x + math.Cos(a-theta*sign)*gr*tl.size
                ny = y - math.Sin(a-theta*sign)*gr*tl.size
                next = append(next, tile{kite, nx, ny, a + 3*theta*sign, size})
            }
        }
    }
    
    tls = distinctTiles(next)
    return deflateTiles(tls, gen-1)
}

func drawTiles(dc *gg.Context, tls []tile) {
    dist := [2][3]float64{{gr, gr, gr}, {-gr, -1, -gr}}
    for _, tl := range tls {
        angle := tl.angle - theta
        dc.MoveTo(tl.x, tl.y)
        ord := tl.tt
        for i := 0; i < 3; i++ {
            x := tl.x + dist[ord][i]*tl.size*math.Cos(angle)
            y := tl.y - dist[ord][i]*tl.size*math.Sin(angle)
            dc.LineTo(x, y)
            angle += theta
        }
        dc.ClosePath()
        if ord == kite {
            dc.SetHexColor("FFA500") 
        } else {
            dc.SetHexColor("FFFF00") 
        }
        dc.FillPreserve()
        dc.SetHexColor("A9A9A9") 
        dc.SetLineWidth(1)
        dc.Stroke()
    }
}

func main() {
    w, h := 700, 450
    dc := gg.NewContext(w, h)
    dc.SetRGB(1, 1, 1)
    dc.Clear()
    tiles := deflateTiles(setupPrototiles(w, h), 5)
    drawTiles(dc, tiles)
    dc.SavePNG("penrose_tiling.png")
}