Truchet Tiling Variations

I recently came across a Truchet Tiling sketch by Jim Bumgardner where he created off screen images of the tiles and had a neat trick to avoid double nested loops when creating the grid. It seemed to me this was an ideal case to use the ruby-processing grid method to generate the grid in JRubyArt:-

DIM = 24
attr_reader :tiles, :images

def make_image(dim, alternate = false)
  create_graphics(dim, dim, P2D).tap do |g|
    g.smooth(4)
    g.begin_draw
    g.background(255)
    g.stroke(0)
    g.stroke_weight(4)
    g.no_fill
    g.ellipse_mode(RADIUS)
    if alternate
      g.ellipse(0, dim, dim / 2, dim / 2)
      g.ellipse(dim, 0, dim / 2, dim / 2)
    else
      g.ellipse(0, 0, dim / 2, dim / 2)
      g.ellipse(dim, dim, dim / 2, dim / 2)
    end
    g.end_draw
  end

  def setup
    sketch_title 'Truchet Tiling'
    @images = [make_image(DIM), make_image(DIM, true)]
    @tiles = []
    grid(width, height, DIM, DIM) do |posx, posy|
      tiles << Tile.new(Vec2D.new(posx, posy))
    end
    no_loop
  end

  def draw
    background(255)
    tiles.each(&:render)
  end

  def settings
    size(576, 576, P2D)
    smooth(4)
   end
end

class Tile
  include Processing::Proxy
  attr_reader :vec, :img

  def initialize(vec)
    @vec = vec
    @img = images.sample
  end

  def render
    image(img, vec.x, vec.y, img.width, img.height)
  end
end

Here is snapshot:-


So the there was diagonal variation:-

And a hybrid:-

3 Likes

Very nice. This reminds me of the fact that I still didn’t finish the Rosetta maze-solving task.

2 Likes

I extended sketch to be interactive, but I had to use default rendering (I think there are threading, and/or graphics context issues with P2D, where the suggested solution is to place all rendering code in the draw loop, which I think would be very untidy):-

DIM = 24
TYPE = %i[corner alt_corner alt_oval oval alt_line line].freeze
attr_reader :tiles, :images

def make_image(dim, alternate = :line)
  create_graphics(dim, dim).tap do |g|
    g.smooth(4)
    g.begin_draw
    g.background(255)
    g.stroke(0)
    g.stroke_weight(4)
    g.no_fill
    g.ellipse_mode(RADIUS)
    case alternate
    when :corner
      g.line(dim / 2, 0, dim / 2, dim / 2)
      g.line(0, dim / 2, dim / 2, dim / 2)
      g.line(dim / 2, dim, dim, dim / 2)
    when :alt_corner
      g.line(dim / 2, 0, dim / 2, dim / 2)
      g.line(dim / 2, dim / 2, dim, dim /  2)
      g.line(0, dim / 2, dim / 2, dim)
    when :line
      g.line(dim / 2, 0, dim, dim / 2)
      g.line(0, dim / 2, dim / 2, dim)
    when :alt_line
      g.line(0, dim / 2, dim / 2, 0)
      g.line(dim / 2, dim, dim, dim / 2)
    when :alt_oval
      g.ellipse(0, dim, dim / 2, dim / 2)
      g.ellipse(dim, 0, dim / 2, dim / 2)
    when :oval
      g.ellipse(0, 0, dim / 2, dim / 2)
      g.ellipse(dim, dim, dim / 2, dim / 2)
    end
    g.end_draw
  end
end

def create_image_array(except = [])
  @images = TYPE.reject do |type|
    except.include?(type)
  end.map { |type| make_image(DIM, type) }
  @tiles = []
end

def create_grid
  @tiles = []
  grid(width, height, DIM, DIM) do |posx, posy|
    tiles << Tile.new(Vec2D.new(posx, posy))
  end
end

def setup
  sketch_title 'Mixed Truchet Tiling'
  create_image_array
  create_grid
  no_loop
end

def key_pressed
  case key
  when 'o', 'O'
    create_image_array(%i[alt_line line corner alt_corner])
  when 'c', 'C'
    create_image_array(%i[alt_line line oval alt_oval])
  when 'l', 'L'
    create_image_array(%i[alt_oval oval])
  end
  create_grid
  redraw
end

def draw
  background(255)
  tiles.each(&:render)
end

def settings
  size(576, 576)
  smooth(4)
end

# encapsulate Tile as a class
class Tile
  include Processing::Proxy
  attr_reader :vec, :img

  def initialize(vec)
    @vec = vec
    @img = images.sample
  end

  def render
    image(img, vec.x, vec.y, img.width, img.height)
  end
end

Demonstrates facilities available through ruby ‘Enumerable’ module, and use of ruby symbols as one might use java enums.

1 Like

Thank you for sharing this! The Ruby approaches are fascinating.

For related examples of Truchet Tiling implemented in Java, p5.js, BASIC, and many other languages, I recommend checking out the book 10 PRINT CHR$(205.5+RND(1)); : GOTO 10 **

a neat trick to avoid double nested loops when creating the grid

The original Commodore64 example takes advantage of text output and the linefeed to avoid a nested loop – in fact, its loop is one line with a self-referencing GOTO statement to generate the tiles one character at a time. But you can create a linefeed-like 2d pointer in a single loop for drawing purposes in Processing using modulo (%) and the line width.

int w=4;
int h=2;
for(int i=0; i<w*h; i++) {
  int x = i%w;
  int y = i/w;
  println(x, y);
}

The most common place to see this kind of modulo-width code in Processing sketches is when converting a pixels[] index to and from 2D coordinates, especially as a way to avoid using set() and get().


** (I am one of the authors)

2 Likes

@jeremydouglass I remember when the Commodore64 was a thing, back in the day when I was studying xenobiotic metabolism. But my first experience with computer automation of results was a basic program running on an early Wang desktop (punch tape program entry), then there was the fortran programs on the PDP11 (two letter program commands) that was coupled to the Mass Spectrometer. Fortunately the abstraction of computer languages has moved on a lot from that.

1 Like

My first one was called MSX, with a 3,58 MHz 8-bit Z-80A processor. You really needed creativity to make programs with 64KB of ram. The drive was a cassette recorder!

2 Likes