Penrose tiling: Difference between revisions

From Rosetta Code
Content added Content deleted
(added Penrose tiling draft task)
 
(→‎{{header|Java}}: added Java)
Line 33: Line 33:
* [http://preshing.com/20110831/penrose-tiling-explained/| Deflation explained for Kite and Dart, includes Python code (preshing.com)]
* [http://preshing.com/20110831/penrose-tiling-explained/| Deflation explained for Kite and Dart, includes Python code (preshing.com)]
<br><br>
<br><br>
=={{header|Java}}==
[[File:Penrose_java.png|300px|thumb|right]]
{{works with|Java|8}}
<lang 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 {
// ignores missing hash code
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; // golden ratio
static final double T = toRadians(36); // theta

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<>();

// sun
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));
}
}
}
// remove duplicates
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);
});
}
}</lang>

Revision as of 18:46, 22 April 2016

Penrose tiling is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

A Penrose tiling can cover an entire plane without creating a pattern that periodically repeats.

There are many tile sets that can create non-periodic tilings, but those can typically also be used to create a periodic tiling. What makes Penrose tiles special is that they can only be used to produce non-periodic tilings.

The two best-known Penrose tile sets are Kite and Dart (P2) and Thin Rhombus and Fat Rhombus (P3)

These so-called prototiles are usually depicted with smooth edges, but in reality Penrose tiles have interlocking tabs and cut-outs like the pieces of a Jigsaw puzzle. For convenience these deformations are often replaced with matching rules, which ensure that the tiles are only connected in ways that guarantee a non-periodic tiling. (Otherwise, for instance, you could combine the kite and dart to form a rhombus, and easily create a periodic tiling from there.)

You can construct a Penrose tiling by setting up some prototiles, and adding tiles through trial and error, backtracking whenever you get stuck.

More commonly a method is used that takes advantage of the fact that Penrose tilings, like fractals, have a self-similarity on different levels. When zooming out it can be observed that groups of tiles are enclosed in areas that form exactly the same pattern as the tiles on the lower level. Departing from an inflated level, the prototiles can be subdivided into smaller tiles, always observing the matching rules. If done correctly it will always fit. The subdivision may have to repeated several times, before you reach the desired level of detail. This process is called deflation.

More information can be found through the links below.

The task: fill a rectangular area with a Penrose tiling.

See also



Java

Works with: Java version 8

<lang 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 {

   // ignores missing hash code
   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; // golden ratio
   static final double T = toRadians(36); // theta
   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<>();
       // sun
       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));
               }
           }
       }
       // remove duplicates
       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);
       });
   }

}</lang>