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;
}




Java: import java.awt.*;
import java.util.List;
import java.awt.geom.Path2D;
import java.util.*;
import javax.swing.*;
import static java.lang.Math.*;
import static java.util.stream.Collectors.toList;

public class PenroseTiling extends JPanel {
    
    class Tile {
        double x, y, angle, size;
        Type type;

        Tile(Type t, double x, double y, double a, double s) {
            type = t;
            this.x = x;
            this.y = y;
            angle = a;
            size = s;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof Tile) {
                Tile t = (Tile) o;
                return type == t.type && x == t.x && y == t.y && angle == t.angle;
            }
            return false;
        }
    }

    enum Type {
        Kite, Dart
    }

    static final double G = (1 + sqrt(5)) / 2; 
    static final double T = toRadians(36); 

    List<Tile> tiles = new ArrayList<>();

    public PenroseTiling() {
        int w = 700, h = 450;
        setPreferredSize(new Dimension(w, h));
        setBackground(Color.white);

        tiles = deflateTiles(setupPrototiles(w, h), 5);
    }

    List<Tile> setupPrototiles(int w, int h) {
        List<Tile> proto = new ArrayList<>();

        
        for (double a = PI / 2 + T; a < 3 * PI; a += 2 * T)
            proto.add(new Tile(Type.Kite, w / 2, h / 2, a, w / 2.5));

        return proto;
    }

    List<Tile> deflateTiles(List<Tile> tls, int generation) {
        if (generation <= 0)
            return tls;

        List<Tile> next = new ArrayList<>();

        for (Tile tile : tls) {
            double x = tile.x, y = tile.y, a = tile.angle, nx, ny;
            double size = tile.size / G;

            if (tile.type == Type.Dart) {
                next.add(new Tile(Type.Kite, x, y, a + 5 * T, size));

                for (int i = 0, sign = 1; i < 2; i++, sign *= -1) {
                    nx = x + cos(a - 4 * T * sign) * G * tile.size;
                    ny = y - sin(a - 4 * T * sign) * G * tile.size;
                    next.add(new Tile(Type.Dart, nx, ny, a - 4 * T * sign, size));
                }

            } else {

                for (int i = 0, sign = 1; i < 2; i++, sign *= -1) {
                    next.add(new Tile(Type.Dart, x, y, a - 4 * T * sign, size));

                    nx = x + cos(a - T * sign) * G * tile.size;
                    ny = y - sin(a - T * sign) * G * tile.size;
                    next.add(new Tile(Type.Kite, nx, ny, a + 3 * T * sign, size));
                }
            }
        }
        
        tls = next.stream().distinct().collect(toList());

        return deflateTiles(tls, generation - 1);
    }

    void drawTiles(Graphics2D g) {
        double[][] dist = {{G, G, G}, {-G, -1, -G}};
        for (Tile tile : tiles) {
            double angle = tile.angle - T;
            Path2D path = new Path2D.Double();
            path.moveTo(tile.x, tile.y);

            int ord = tile.type.ordinal();
            for (int i = 0; i < 3; i++) {
                double x = tile.x + dist[ord][i] * tile.size * cos(angle);
                double y = tile.y - dist[ord][i] * tile.size * sin(angle);
                path.lineTo(x, y);
                angle += T;
            }
            path.closePath();
            g.setColor(ord == 0 ? Color.orange : Color.yellow);
            g.fill(path);
            g.setColor(Color.darkGray);
            g.draw(path);
        }
    }

    @Override
    public void paintComponent(Graphics og) {
        super.paintComponent(og);
        Graphics2D g = (Graphics2D) og;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        drawTiles(g);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setTitle("Penrose Tiling");
            f.setResizable(false);
            f.add(new PenroseTiling(), BorderLayout.CENTER);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}