Drawing Celtic Knotwork 2The simplest element of our description is the Tile. I decided that a Tile would be a two dimensional chunk of characters that would let you set or get any point in that space. Remember that this isn't the only way we could have done things. You could also describe the lines and curves in the tile, or the colors, transparency, and whatever else the crazy kids are coming up with these days. This is my first drawing program, though, and I want to keep it a simple as I can. So I'm going with the bitmap idea. The tile images in the Sloss book are provided in different sizes. Let's go with 9×9. It's small and manageable, without being too small to see.
# 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()
@pixels = []
(0..8).each {
row = []
(0..8).each { row << nil }
@pixels << row
}
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
#####
# Test code
#####
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.")
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, '')
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
I'm told this is Test Driven Development, where you write the tests for your code as you are writing the code itself. A little before the code itself, actually. TDD is useful for any non-trivial programming task. You have the tests there right from the beginning to describe what your classes are supposed to be able to do. Because TDD is based on lots of tiny changes being applied rapidly over time, I decided it would be tedious to describe that process to you at each little step. Instead, we stop and take a snapshot as we get each major stage accomplished. Like that code sample up there. It's really where I'm at right about now. See, there's the class definition, a couple of very basic accessors, and the ability to set all the pixels of a Tile at once from a String. Now that the Tile is pretty much doing everything I want it to, let's move on to the Grid. |
![]() |
|
|
Copyright 1999 - 2009 Brian Wisti
|
||