Drawing Celtic Knotwork 3I want to hurry on to making pictures, so let's rush through the Grid part. That's easy enough, actually. We only need to be able to do a few simple things with a Grid:
And here's the code.
# I am a single small section of a knotwork image. I know about my
# dimensions, and can describe myself on a pixel-by-pixel basis.
class Tile
def initialize(str = nil)
@pixels = []
(0..8).each {
row = []
(0..8).each { row << nil }
@pixels << row
}
if str then
set_from_string(str)
end
end
def at(x, y)
return @pixels[x][y]
end
alias is_set? at
def set(x, y, value=true)
@pixels[x][y] = value
end
def unset(x, y)
@pixels[x][y] = nil
return true
end
def set_from_string(str)
str.split("\n").each_with_index do |line, row|
line.split(' ').each_with_index do |pixel, col|
set(row, col, pixel)
end
end
end
def to_s
str = ""
@pixels.each { |row|
str += "|"
row.each { |pixel|
pixel ||= " "
str += "#{pixel}|"
}
str += "\n"
}
return str
end
end
# I am an arranged collection of Tiles. I know how to add and remove
# Tiles along a 2-d grid, and can also present myself as if I were a single
# large Tile.
class Grid
def initialize(rows, columns)
@tile_size = 9
@rows = rows
@columns = columns
@pixels = Array.new(rows*@tile_size) { |i|
Array.new(columns*@tile_size)
}
end
def set_tile(row, column, tile)
pixel_origin_x = row * @tile_size
pixel_origin_y = column * @tile_size
(0...@tile_size).each { |tile_x|
x = pixel_origin_x + tile_x
(0...@tile_size).each { |tile_y|
y = pixel_origin_y + tile_y
@pixels[x][y] = tile.at(tile_x, tile_y)
}
}
end
def at(row, column)
return @pixels[row][column]
end
def to_s
str = ""
@pixels.each { |row|
str += row.join(' ')
str += "\n"
}
return str
end
end
#####
# Test code
#####
$source_string =<<HERE
x . . . . . . . x
. x . . . . . x .
. . x . . . x . .
. . . x . x . . .
. . . . x . . . .
. . . x . x . . .
. . x . . . x . .
. x . . . . . x .
x . . . . . . . x
HERE
$source_string.gsub!(/^\s+/m, '')
require 'test/unit'
class TC_Tile < Test::Unit::TestCase
def setup
@@tile = Tile.new()
end
def test_pixels
assert_equal(nil, @@tile.is_set?(0, 0),
"By default, any pixel in a Tile is blank")
assert(@@tile.set(0, 0),
"Use Tile#set(row, col) to set a pixel at coordinates (row, col)")
assert(@@tile.is_set?(0, 0),
"A pixel (row, col) is set after Tile#set(row, col) has been called")
assert(@@tile.unset(0, 0),
"Use Tile#unset(row, col) to clear a pixel at coordinates (row, col)")
assert_equal(nil, @@tile.is_set?(0, 0),
"An unset pixel has no set value")
@@tile.set(1, 1)
assert_equal(nil, @@tile.is_set?(0, 0),
"Setting one pixel has no effect on other pixels in a Tile")
assert(@@tile.is_set?(1, 1),
"Tile remembers the set status of each pixel in its confines.")
assert(@@tile.set_from_string($source_string),
"You can use ASCII art strings to set the pixels in a Tile")
assert(@@tile.is_set?(0, 0))
assert(@@tile.is_set?(1, 0))
assert_equal('x', @@tile.at(0, 0),
"A Tile remembers the value assigned, if given, " \
"during Tile#set(row, col, val)")
end
end
class TC_Grid < Test::Unit::TestCase
def test_simple_grid
grid = Grid.new(1, 1)
tile = Tile.new($source_string)
grid.set_tile(0, 0, tile)
assert_equal("x", grid.at(0, 0),
"Use Grid#pixel_at(row, col) to access pixel at " \
"(row, col) distance from upper left corner")
assert_equal($source_string, grid.to_s)
end
def test_large_grid
grid = Grid.new(1, 2)
tile1 = Tile.new($source_string)
tile2 = Tile.new($source_string)
grid.set_tile(0, 0, tile1)
grid.set_tile(0, 1, tile2)
assert_equal("x", grid.at(0, 0))
assert_equal("x", grid.at(0, 9),
"Grid#pixel_at uses whole grid as coordinate system")
expected_output =<<HERE
x . . . . . . . x x . . . . . . . x
. x . . . . . x . . x . . . . . x .
. . x . . . x . . . . x . . . x . .
. . . x . x . . . . . . x . x . . .
. . . . x . . . . . . . . x . . . .
. . . x . x . . . . . . x . x . . .
. . x . . . x . . . . x . . . x . .
. x . . . . . x . . x . . . . . x .
x . . . . . . . x x . . . . . . . x
HERE
expected_output.gsub!(/^\s+/, '')
assert_equal(expected_output, grid.to_s)
end
end
class TestKnotworkPanel < Test::Unit::TestCase
end
There, that's another fifteen minutes of coding done. Yes, the combination of TDD and Ruby makes it about this easy to write a program. |
![]() |
|
|
Copyright 1999 - 2009 Brian Wisti
|
||