Home Truchet Tiling
Post
Cancel

Truchet Tiling

Coding Truchet tiles has been my second attempt at coding a tile generating program with inline SVG in HTML and Javascript. This has been slinghtly more difficult than coding Rhombile tiles, and what was interesting this time was handling contiguous groups that can form after generating a truchet tile mosaic.

Checkout the repository here.

Creating a tile and a mosaic

After a bit of research, generating a truchet tile with svg circle slices was fairly easy. For this classic truchet design, there are only two possible positions which I have called type a and type b:

1) each tile as a rotation of a single tile design
2) the two possible positions for this tile design


Here is my first tile I created using circle arcs. I took the circle arc code from here: https://www.codedrome.com/drawing-arcs-pie-slices-with-svg/

See the Pen truchet tile by Gareth Perilli (@gperilli) on CodePen.

Without considering contiguous groups, as in the joined up bits of the mosaic that you might color in if it were on paper, it’s possible to create some nice patterns. However handling groups within the mosaic was a very attractive challenge so I set about solving it.

Handling Contiguous Groups

After generating a matrix of truchet tiles, I created a simple algorithm that would consider each tile, the tile to the left of it (if it existed), and the tile above it (if that existed). Then the code checked the two elements within the tile that make contact with the adjacent tiles to the left and above, and from this, each constituent element within each tile would be assigned a number to represent a contiguous group of elements across many tiles. Essentially my code checks each tile from left to right and from top to bottom, assigning color group numbers based on the previously handled tiles before and above in the matrix.

At this point I realized that what comes so easily to us as we color in shapes, can take hours of coding time. Anyway, I ended up with a matrix of truchet tile class instances, and a set of three elements belonging to each tile. Many of these tile elements would have two or more color group numbers assignned to them, which would represent cases when two or more color groups, previously considered independent would have to be joined into a single color group.

1) considering all adjacent tiles and their joining constituent components
2) An example of one tile and its 4 neighbours


So the coding challenge started here. How could I group together number pairs based on shared values. The best stackOverflow answer to this is possibly this one: https://stackoverflow.com/questions/39552694/js-merge-arrays-that-share-at-least-one-common-value

This, for example, is more elegant than my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
function merger(arr){
  return arr.map((e,i,a) => a.slice(i)
                             .reduce((p,c) => e.some(n => c.includes(n)) ? [...new Set([...p,...c])] : p,[]))
            .reduce((r,s) => { var merged = false;
                               r = r.map(a => a.some(n => s.includes(n)) ? (merged = true, [...new Set([...a,...s])]) : a);
                               !merged && r.push(s);
                               return r;
                             },[]);
}

var arr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]],

console.log(merger(arr));

However, I don’t like cutting and pasting code if I don’t understand what’s going on, so I ended up coding it my way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// generate number random pairs
let unsortedArray = [];
let firstNumber, secondNumber;
function getRandomNumber(n) {
    return Math.floor(Math.random() * n)
}

for (i = 0; i < 10; i++) {
    do {
        firstNumber = getRandomNumber(20)
        secondNumber = getRandomNumber(20)
    }
    while (firstNumber == secondNumber)
    unsortedArray[i] = [firstNumber, secondNumber]
}

let joinerArray = [];
let labeledArray = [];
for (let i = 0; i < unsortedArray.length; i++) {
    labeledArray[i] = {group: unsortedArray[i], sorted: false}
}


labeledArray.forEach((labeledChunk) => {
    // putting unsorted items into subarrays of joinerArray
    if (labeledChunk.sorted == false) {
        joinerArray.forEach((joinerSubArray) => {
            if (joinerSubArray.includes(labeledChunk.group[0]) || joinerSubArray.includes(labeledChunk.group[1]) ) {
                joinerSubArray.push(labeledChunk.group[0]);
                joinerSubArray.push(labeledChunk.group[1]);
                labeledChunk.sorted = true;
            }
        })

        if (labeledChunk.sorted == false) {
            joinerArray.push(labeledChunk.group);
            labeledChunk.sorted = true;
        }

        joinerArray.forEach((joinerSubArray) => {
            joinerArray.forEach((joinerSubArrayComparison) => {
                // only compare different subarrays within parent array
                if (joinerSubArray != joinerSubArrayComparison) {
                    joinerSubArray.forEach((joinerSubArrayItem) => {
                        // determine if there is a shared item
                        if (joinerSubArrayComparison.includes(joinerSubArrayItem)) {
                            // if there is a shared item,  take all items from the joinerSubArrayComparison sub array into the joinerSubArray (if they are not already there)
                            joinerSubArrayComparison.forEach((joinerSubArrayComparisonItem) => {
                                if (joinerSubArray.includes(joinerSubArrayComparisonItem) == false) {
                                    joinerSubArray.push(joinerSubArrayComparisonItem)
                                }
                            })
                        }
                    })
                }
            })
        })
    }

})


joinerArray.forEach((joinerArraySubArray) => {
    joinerArraySubArray.sort()
})

console.log("The numbers groups before duplicates are removed:");
console.log(joinerArray);

function removeDuplicates(arr = []) {
    const map = new Map();
    arr.forEach((x) => map.set(JSON.stringify(x), x));
    arr = [...map.values()];
    return arr;
};

// remove duplicate numbers within the sub arrays
let innerDuplicateRemovedJoinerArray = [];
for (i = 0; i < joinerArray.length; i++) {
    innerDuplicateRemovedJoinerArray[i] = removeDuplicates(joinerArray[i]);
}

// remove duplicate groups
let duplicateRemovedJoinerArray = removeDuplicates(innerDuplicateRemovedJoinerArray);

console.log("The numbers groups after duplicates are removed:");
console.log(duplicateRemovedJoinerArray);

Some Truchet Tile Mosaics

1) two colors with shape outline
2) contiguous group coloring
3) monochrome
4) single color with various border width settings
5) two colors with various border settings
6) contiguous group coloring with wide shape border
This post is licensed under CC BY 4.0 by the author.