Home
Admin | Edit

Tiny code Truchet 3x3 pixels patterns

This article is a follow-up of my Tiny bit-field based text renderer article with slightly different usage.

Contents

Introduction


There is a well-known one line extremely short BASIC program which show a skewed maze when run on a 1982 C64 8-bit home computer, the clever trick is that it only use two characters "/" and "\" print randomly across the characters grid.

The C64 maze use the bundled character set known as PETSCII, each characters comprise of an 8x8 pixel block, it is not a correct maze as it has no beginning nor end but it still looks like it.

The inspiration for this i guess was Truchet tiles which was first described in a 1704 memoir by Sébastien Truchet.

These kind of patterns also has a much deeper history and can be seen in many historical works such as stitching. (immediate example is Sashiko)

Many cool patterns can be generated with small amount of code with the same method by computing a selection of characters across a grid, the selection can be made from all combinations of pixels of says a 3x3 grid for simplicity, the immediate advantage is that the font is entirely computed so there is no needs to store glyph data, the character data can be encoded in as low as a byte with a "font" offset  :

TIC-80 (fantasy computer) maze using two characters computed from the 3x3 pixels binary matrix shown below the maze

By selecting different characters many patterns can be made which may be useful to produce details (through layering / modulation ?) in generated pixels art for games or intros with small code / performance cost :


Many tricks could be applied to ramp up the diversity of the patterns such as colors, spatial distribution (gradient noise ?), adding small offset, skewing, layering (with blending ?), shadowing (drop shadow), modulating with logical operators etc. and combining these methods; random is good enough but specific patterns distribution (scheme) must be done for more intricate patterns.

with colors variation (constrained to 4 random nearest colors), selection of patterns with logical operators / function (4), can looks like cheap noise (5)

In codegolf context the characters can also be computed specifically by adapting the code to the selected characters and applying a transformation (such as a Reflection), the character may also be simply taken from RAM, this might be cheaper.

Note that these characters can also be seen in many parts of the world historically when they are scaled up such as elements in Huari crafts / Andean arts.

Tool


Here is a TIC-80 interactive experiment which was used to produce all the images in this article, you can try it online (tool code is bundled into the cart) :

TIC-80 Code


Here is some LUA code (TIC-80 platform) producing the maze in the first image of this article  :

w=3 -- grid width
h=3 -- grid height
n=w*h -- pixels count
c=2^n -- number of combinations (2 colors)
function cb(i,x,y,d,s,f)
 -- s: scale
 -- f: skew
 for k=0,w*s-1 do
  for p=0,h*s-1 do
   j=k//s+(p//s+p//s+p//s)
   b=i&(1<<j)
   if f then -- skew
    b=b>0 and pix(x+k+p,y+p-k,d) -- skewing may need offsetting ? : pix(x+(k-p)//2+s+s//2,y+(p+k)//2,d)
   else
    b=b>0 and pix(x+k,y+p,d)
   end
  end
 end
end

function TIC()
 cls()

 -- show full set (all combinations)
 x=0
 y=0
 for i=0,c-1 do
  yoff=73
  cb(i,x,yoff+y,12,1)
  x=x+w+1
  if x>=126 then
   x=0
   y=y+h+1
  end
 end
 
 -- maze
 g={84,273} -- / and \
 math.randomseed(0)
 for y=0,23 do
  for x=0,240/3 do
   gi=math.random(2)
   cb(g[gi],x*3,y*3,2,1)
  end
 end
end

The cb function is the character generator, it takes an index (0 to 511) and plot the content of a character taken from the set of all combinations of a 3x3 binary matrix, the data is contained in the index and the character is computed and drawn from there.

The character generator function is also able to scale (integer factor) and skew the character (45° rotation), skewing works best when scaled.

A 2x2 binary matrix also works although the maze example looks more limited.

Dither and smoothing


A way to get more interesting look is to average per tile or for all pixels, i especially like averaging per tile with a custom pattern and adding a slight offset to the resulting color, it looks like cheap dithering :

different averaging methods applied: raw (1), per tile (2), per pixels (3), with color offset

3x3 Truchet tiles: raw (1), per tile (2), per pixels (3), no color offset

same as above with color offset (3)

some more patterns

per tile and by adjusting color offset (animation)

-- averaging made to be applied for each tile, it affect tile pixels : 1,0 0,1 2,1 1,2
-- change pattern for different "dither" style
function smooth1(x,y,o)
 r=(pix(x,y)+pix(x+1,y-1)+pix(x+2,y)+pix(x+1,y+1))/4
 pix(x+1,y,r+o)
 r=(pix(x-1,y+1)+pix(x,y)+pix(x+1,y+1)+pix(x,y+2))/4
 pix(x,y+1,r+o)
 r=(pix(x+1,y+1)+pix(x+2,y)+pix(x+3,y+1)+pix(x+2,y+2))/4
 pix(x+2,y+1,r+o)
 r=(pix(x,y+2)+pix(x+1,y+1)+pix(x+2,y+2)+pix(x+1,y+3))/4
 pix(x+1,y+2,r+o)
end

-- per pixel (applied on all pixels) using a kernel : https://en.wikipedia.org/wiki/Kernel_(image_processing)
function smooth2(x,y,o)
 l1=pix(x-1,y)
 u1=pix(x,y-1)
 d1=pix(x,y+1)
 r1=pix(x+1,y)
 cr=pix(x,y)
 
 r=(l1+u1+d1+r1+cr)/5
 pix(x,y,r+o)
end

fun result per pixel with the above kernel, might also be due to boundary handling, looks like stone cracks (1) or a circuitry (2)

Isometric projection


rendered with isometric projection and using integer cubes, still fairly small code, p5js sketch

References


back to topLicence Creative Commons