Enjoy the 'Truchet Tiling' with your own patterns.
'Truchet Tiling' is the way that makes you create some interesting patterns with the simple design and the simple algorithm.
In this article, I'll start with 'What is the Truchet Tiling' and show you how to make the tile design. And also, I'll show you the example code to draw the pattern.
Let's enjoy 'Truchet Tiling' with your own design using the p5.js and the Processing.
What is the Truchet tiling or Truchet pattern?
The 'Truchet Tiling' is the pattern made with the 'Truchet Tile'. And the 'Truchet Tile' is the square tile that is not rotationally symmetric.
Wikipedia : Truchet tiles
Let me explain with concrete examples. You might have seen a picture like this.
This is the 10 x 10 matrix tiling.
The 'Truchet Tile' used in this picture is 'A'. And the 'B' is 90 degrees rotated 'A'.
This picture is made with just one kind of tile rotating 90 degrees randomly.
Here is the example code of the 'Truchet Tiling' with p5.js.
/*
* Truchet Tiling example code.
* p5.js
* @author @deconbatch
* @version 0.1
* created 2022.02.28
* license CC0
*/
const w = 640;
const h = w;
const num = 10;
function setup() {
createCanvas(w, h);
imageMode(CENTER);
const cell = getPattern(floor(w / num));
background(224);
for (let x = 0; x < num; x++) {
for (let y = 0; y < num; y++) {
push();
translate((x + 0.5) * w / num, (y + 0.5) * height / num);
if (random(1.0) < 0.5) {
rotate(HALF_PI);
}
image(cell, 0, 0);
pop();
}
}
}
function getPattern(_size) {
g = createGraphics(_size, _size);
g.background(224);
g.noFill();
g.stroke(96);
g.strokeWeight(_size * 0.1);
g.circle(0, 0, _size);
g.circle(_size, _size, _size);
return g;
}
How to design the tile of Truchet tiling?
It's fun and a little thrilling to make some interesting patterns with the simply designed tile. It's must be more fun to use your own designed tile.
By definition, the tile design that is not rotationally symmetric is correct.
But it's not enough for making an interesting pattern. It often disconnect the lines in pattern.
You should make the design that the connection points on four sides connect when it rotates to make the lines in the pattern will not disconnect.
1.Plot the connection points on one side.
2.Copy it to the rest three sides rotating 90 degrees.
3.Copy it upside-down or left-to-right.
Then draw the lines between the connection points. Be aware it must not be rotationally symmetric. I drew lines by the hand like this by way of trial.
And I made a pattern with it.
Let's play with the tile of your own design.
You can make a pattern with the tile of your own design using the example code I showed before. All you need to do is change the 'getPattern()' function.
If you want to use hand drawn designed tile, you can use the 'loadImage()' not 'getPattern()' function. Don't forget to resize the tile image to fit the size of the cell.
For example.
let img;
function preload() {
img = loadImage('ptn.jpg');
}
function setup() {
createCanvas(w, h);
imageMode(CENTER);
const cell = img.resize(floor(w / num), floor(w / num));
Example source code of the 'Processing'.
I designed the tile with the 'bezier()' function in this example work. And I used upside-down and left-to-right turns, not rotation. That's because the rotation sometimes creates the gap when the center axis shifts.
Please feel free to use this example code under the terms of the GPL. To see other works based on my code is my pleasure. And my honor.
/**
* Magic Carpet Ride.
* auto pattern generated Truchet Tiling.
*
* @author @deconbatch
* @version 0.1
* @license GPL Version 3 http://www.gnu.org/licenses/
* Processing 3.5.3
* 2022.02.20
*/
void setup() {
size(900, 900);
colorMode(HSB, 360, 100, 100, 100);
imageMode(CENTER);
int frmMax = 3;
int divBase = 15;
int start = floor(random(frmMax));
float hueBase = random(360.0);
for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
int div = divBase + ((start + frmCnt) % frmMax) * 5;
float hueVal = hueBase + frmCnt * 120.0;
PImage cell = getPattern(floor(width / div), hueVal);
background(hueVal % 360.0, 5.0, 80.0, 100.0);
for (int x = 0; x < div; x++) {
for (int y = 0; y < div; y++) {
pushMatrix();
translate((x + 0.5) * width * 1.0 / div, (y + 0.5) * height * 1.0 / div);
// rotate(floor(random(4.0)) * HALF_PI);
if (random(1.0) < 0.5) {
scale(-1.0, 1.0);
}
if (random(1.0) < 0.5) {
scale(1.0, -1.0);
}
image(cell, 0, 0);
popMatrix();
}
}
casing(100);
saveFrame("frames/" + String.format("%04d", frmCnt + 1) + ".png");
}
exit();
}
/**
* getPattern : returns random generated pattern
*/
PImage getPattern(int _size, float _hue) {
// connect points
PVector[][] points = {
{new PVector(0.0, 0.5), new PVector(0.5, 1.0)},
{new PVector(0.5, 0.0), new PVector(1.0, 0.5)},
{new PVector(0.0, 0.25), new PVector(0.25, 0.0)},
{new PVector(0.75, 1.0), new PVector(1.0, 0.75)},
{new PVector(0.75, 0.0), new PVector(1.0, 0.25)},
{new PVector(0.0, 0.75), new PVector(0.25, 1.0)},
};
int pNum = points.length;
PGraphics g = createGraphics(_size, _size);
g.beginDraw();
g.colorMode(HSB, 360, 100, 100, 100);
g.blendMode(BLEND);
g.background(_hue % 360.0, 5.0, 80.0, 100.0);
g.noStroke();
for (int i = 0; i < pNum; i++) {
PVector[] p = points[i];
float hueVal = (_hue + i * 30.0) % 360;
float pRnd = random(0.1, 0.9);
g.fill(hueVal, 40, 60, 100);
g.bezier(
p[0].x * _size, p[0].y * _size,
pRnd * _size, pRnd * _size,
(1.0 - pRnd) * _size, (1.0 - pRnd) * _size,
p[1].x * _size, p[1].y * _size
);
}
g.endDraw();
return g;
}
/**
* casing : draw fancy casing
*/
public void casing(int _m) {
fill(0.0, 0.0);
strokeWeight(_m * 2 - 10.0);
stroke(0.0, 0.0, 95.0, 100.0);
rect(0.0, 0.0, width, height);
strokeWeight(_m * 2 - 32.0);
stroke(0.0, 0.0, 80.0, 100.0);
rect(0.0, 0.0, width, height);
strokeWeight(20.0);
stroke(0.0, 0.0, 50.0, 100.0);
rect(0.0, 0.0, width, height);
strokeWeight(16.0);
stroke(0.0, 0.0, 100.0, 100.0);
rect(0.0, 0.0, width, height);
}