//
// Wang Tiling Generator
//
// by David Walters
//
// Requires L3DT v2.9 (or later).
//
// Based on: http://research.microsoft.com/en-us/um/people/cohen/WangFinal.pdf
//
// This script generates a plane of Wang Tile indicies using the size of the design map.
// It is based around a set of 8 tiles, as per. figure 1a
//
// In the above paper the tile edges are coloured. This colouring of north/south
// and east/west edges uses four colours - but this can actually be reduced to two
// colours as only opposite edges are ever compared. This lets us create a set of
// binary codes for each tile:
//
// RED or BLUE = 0; GREEN or YELLOW = 1
//
// NS:EW
// 0: WANG_A => [01:10]
// 1: WANG_B => [11:00]
// 2: WANG_C => [00:11]
// 3: WANG_D => [10:01]
// 4: WANG_E => [01:01]
// 5: WANG_F => [11:11]
// 6: WANG_G => [00:00]
// 7: WANG_H => [10:10]
//
//
// Changes:
//
// 21st May 2013
//
// * Updated to support new ZeoScript syntax in L3DT v2.9 and later (by A.T.)
//
// 6th May 2009
//
// * Update to new variable declaration style
// * Update to use new 'SetValue2' when building tables
// * Add a call to the EditUI function to allow manual adjustment of size.
//
//==============================================================================
// Configure
//==============================================================================
// Create settings list 'ls'
varlist ls
uint ls.size_x
uint ls.size_y
// Use the design map size as default
hvar hDM
set hDM <GetMap "DM">
set ls.size_x <map.GetWidth hDM>
set ls.size_y <map.GetHeight hDM>
// edit settings list
assert <EditUI &ls "Enter Map Dimensions"> "Calculation aborted!"
//==============================================================================
// Initialisation
//==============================================================================
int x
int y
int value
int index
int current
int random
byte b
int row_base
int above_base
int is_west_one
int is_north_one
// Starting up
echo "Initialising ..."
//
// Populate 'is_east_one' array
//
// This array is a simple lookup table storing the value of the east edge.
//
hvar is_east_one
set is_east_one <new buffer>
assert <buffer.InitByType is_east_one 8 int > "Allocation failure."
// Does a tile have an 'east' value of one?
assert <buffer.SetValue2 is_east_one 0 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 1 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 2 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 3 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 4 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 5 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 6 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_east_one 7 1 > "Cannot set buffer value."
//
// Populate 'is_south_one' array
//
// This array is a simple lookup table storing the value of the south edge.
//
hvar is_south_one
set is_south_one <new buffer>
assert <buffer.InitByType is_south_one 8 int > "Allocation failure."
// Does a tile have a 'south' value of one?
assert <buffer.SetValue2 is_south_one 0 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 1 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 2 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 3 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 4 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 5 1 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 6 0 > "Cannot set buffer value."
assert <buffer.SetValue2 is_south_one 7 0 > "Cannot set buffer value."
//
// Populate 'west' array
//
// This is an array of tiles, two groups of four, that are the tiles
// that can be matched with east values of zero and one respectively.
//
hvar west
set west <new buffer>
assert <buffer.InitByType west 8 int > "Allocation failure."
// EAST: 0 => WEST: 0 (abgh)
assert <buffer.SetValue2 west 0 0 > "Cannot set buffer value." // WANG_A
assert <buffer.SetValue2 west 1 1 > "Cannot set buffer value." // WANG_B
assert <buffer.SetValue2 west 2 6 > "Cannot set buffer value." // WANG_G
assert <buffer.SetValue2 west 3 7 > "Cannot set buffer value." // WANG_H
// EAST: 1 => WEST: 1 (cdef)
assert <buffer.SetValue2 west 4 2 > "Cannot set buffer value." // WANG_C
assert <buffer.SetValue2 west 5 3 > "Cannot set buffer value." // WANG_D
assert <buffer.SetValue2 west 6 4 > "Cannot set buffer value." // WANG_E
assert <buffer.SetValue2 west 7 5 > "Cannot set buffer value." // WANG_F
//
// Populate 'north' array
//
// This is an array of tiles, two groups of four, that are the tiles
// that can be matched with south values of zero and one respectively.
//
hvar north
set north <new buffer>
assert <buffer.InitByType north 8 int > "Allocation failure."
// SOUTH: 0 => NORTH: 0 (aceg)
assert <buffer.SetValue2 north 0 0 > "Cannot set buffer value." // WANG_A
assert <buffer.SetValue2 north 1 2 > "Cannot set buffer value." // WANG_C
assert <buffer.SetValue2 north 2 4 > "Cannot set buffer value." // WANG_E
assert <buffer.SetValue2 north 3 6 > "Cannot set buffer value." // WANG_G
// SOUTH: 1 => NORTH: 1 (bdfh)
assert <buffer.SetValue2 north 4 1 > "Cannot set buffer value." // WANG_B
assert <buffer.SetValue2 north 5 3 > "Cannot set buffer value." // WANG_D
assert <buffer.SetValue2 north 6 5 > "Cannot set buffer value." // WANG_F
assert <buffer.SetValue2 north 7 7 > "Cannot set buffer value." // WANG_H
//
// Populate 'wang_table' array
//
// This is an array of tiles that resolves the two constraints of
// main body tiles. Each combination of constraints has two choices
// picked at random.
//
hvar wang_table
set wang_table <new buffer>
assert <buffer.InitByType wang_table 8 int > "Allocation failure."
// ====== WEST:0
// === NORTH:0
assert <buffer.SetValue2 wang_table 0 0 > "Cannot set buffer value." // WANG_A
assert <buffer.SetValue2 wang_table 1 6 > "Cannot set buffer value." // WANG_G
// == NORTH:1
assert <buffer.SetValue2 wang_table 2 1 > "Cannot set buffer value." // WANG_B
assert <buffer.SetValue2 wang_table 3 7 > "Cannot set buffer value." // WANG_H
// ====== WEST:1
// === NORTH:0
assert <buffer.SetValue2 wang_table 4 2 > "Cannot set buffer value." // WANG_C
assert <buffer.SetValue2 wang_table 5 4 > "Cannot set buffer value." // WANG_E
// === NORTH:1
assert <buffer.SetValue2 wang_table 6 3 > "Cannot set buffer value." // WANG_D
assert <buffer.SetValue2 wang_table 7 5 > "Cannot set buffer value." // WANG_F
// Prepare a buffer for all tiles
hvar tiles
set tiles <new buffer>
assert <buffer.InitByType tiles <mul ls.size_x ls.size_y > int > "Allocation failure."
//==============================================================================
// Generate Tiles
//==============================================================================
//
// Say Hello
//
string hello
set hello "Generating Wang Tiles ( "
set hello <strcat hello ls.size_x >
set hello <strcat hello " x " >
set hello <strcat hello ls.size_y >
set hello <strcat hello " )" >
echo hello
//
// Generate First Tile - pick any tile
//
// current = rand() % 8
set current <mod <rand > 8 > // [0-7]
// tiles[ 0 ] = current
assert <buffer.SetValue tiles 0 current > "Cannot set buffer value."
//
// Tile the First Row
//
// For each tile in this row the east/west "colour" should match. We use the
// 'is_east_one' lookup table to first see what our constraint is, then pick one
// of the four possible tiles at random from the 'west' array.
//
set x 1
do
// value = is_east_one[ current ]
assert <buffer.GetValue is_east_one current value > "Cannot get buffer value."
// current = west[ value ][ random ]
set random <mod <rand > 4 > // [0-3]
set index <add <mul value 4 > random >
assert <buffer.GetValue west index current > "Cannot get buffer value."
// Store 'current' as new tile
assert <buffer.SetValue tiles x current > "Cannot set buffer value."
set x <incr x >
while <islt x ls.size_x >
//
// For each remaining row...
//
set y 1
do
// First: Compute index of the first element of the row, and the one above.
set row_base <mul y ls.size_x >
set above_base <mul <sub y 1 > ls.size_x >
//
// Pick Row Start
//
// The first tile of a row must match with the south edge of the tile above.
// We use the 'is_south_one' this time to identify our constraint and then pick
// randomly from one of the four tiles in the 'north' array.
//
// value = is_south_one[ tiles[ above_base ] ]
assert <buffer.GetValue tiles above_base current > "Cannot get buffer value."
assert <buffer.GetValue is_south_one current value > "Cannot get buffer value."
// current = north[ value ][ random ]
set random <mod <rand > 4 > // [0-3]
set index <add <mul value 4 > random >
assert <buffer.GetValue north index current > "Cannot get buffer value."
// Store 'current' as new tile of this row
assert <buffer.SetValue tiles row_base current > "Cannot set buffer value."
//
// Complete the Row
//
// Additional tiles of these rows must now conform to two constraints.
// One from the north, and another from the west.
//
set x 1
do
// What is the 'colour' of the edge to the west of this tile?
// (the value of the tile to the west is stored in 'current')
assert <buffer.GetValue is_east_one current is_west_one > "Cannot get buffer value."
// What is the 'colour' of the edge to the north of this tile?
set index <add above_base x >
assert <buffer.GetValue tiles index value > "Cannot get buffer value."
assert <buffer.GetValue is_south_one value is_north_one > "Cannot set buffer value."
// We have two choices for each tile ...
set random <mod <rand > 2 >
// current = wang_table[ is_west_one ][ is_north_one ][ random ]
set index <mul is_west_one 4 >
set index <add index <mul is_north_one 2 > >
set index <add index random >
assert <buffer.GetValue wang_table index current > "Cannot get buffer value."
// Store 'current' in our row in the tiles array
set index <add row_base x >
assert <buffer.SetValue tiles index current > "Cannot set buffer value."
set x <incr x >
while <islt x ls.size_x >
set y <incr y >
while <islt y ls.size_y >
echo " ...generation complete."
//==============================================================================
// Write Data
//==============================================================================
string FileName
set FileName <file.SaveDlg "bin" NULL "Wang Tiling files (*.bin)|*.bin|All files (*.*)|*.*|">
if <iseq 0 <strlen FileName>>
return 0
endif
voidptr fp
set fp <fopen FileName "w">
assert fp "Cannot open file!"
set y 0
do
set x 0
do
// Get the tile value
set index <add <mul y ls.size_x > x >
assert <buffer.GetValue tiles index current > "Cannot get buffer value."
// Cast to a byte, and write 8-bits
set b current
fwrite fp b
set x <incr x >
while <islt x ls.size_x >
set y <incr y >
while <islt y ls.size_y >
fclose fp
echo "done."
// [EOF]