Mexican Blanket (#127)

Several solutions to this quiz called out to RMagick to draw pictures of
their
work. That made for some pretty output. One such solution was from
Justin
Ethier and I want to take a look at that code.

Justin observed that each row of the pattern is just the previous row
shifted
over one pixel. In order to build those rows, Justing decided to just
build one
long pattern and slice it as needed. Here’s that chunk of code:

def create_gradient(colors, width=5)
pattern = []

for i in 0…(width)
(width-i).times { pattern.push(colors[0]) }
(i+1).times { pattern.push(colors[1]) }
end

for i in 0…(width)
(i+1).times { pattern.push(colors[2]) }
(width-i-1).times { pattern.push(colors[1]) }
end

pattern
end

You pass this method an Array of three colors and a width for a full
band of
color. It begins by fading the first color into the second by
iteratively
adding thinner and thinner lines to the gradient pattern. It then
repeats the
process in reverse to fade the second color into the third.

Justin’s blankets also include solid color bands created by the
following
method:

def create_solid(colors, width)
pattern = []
for color in colors
width.times { pattern.push(color) }
end
pattern
end

This methods works exactly like create_gradient() except there’s no
blending of
colors.

With the pieces to create the structure in place, we are now ready for
some
rendering code:

def draw_ascii(pattern, width)
for i in 0…(pattern.size-width+1)
puts pattern.slice(i, width).join
end
end

As you can see, generating the ASCII output is trivial. The long
pattern is
simply divided into a moving window of width slices. Each slice is then
printed
as one row of output.

RMagick rendering takes a little more work, but still isn’t hard:

require ‘RMagick’
include Magick

def draw(filename, pattern, width, height)
canvas = Magick::ImageList.new
canvas.new_image(width, height, Magick::HatchFill.new(‘white’,
‘white’))
pts = Magick::Draw.new

for y in 0… height
line = pattern.slice(y, width)

 x = 0
 for color in line
   pts.fill(color)
   pts.point(x, y)
   x = x + 1
 end

end

pts.draw(canvas)
canvas.write(filename)
end

This method begins by preparing a canvas on which it can draw points.
From
there it does the same looping over the pattern we saw earlier, but this
time
points are plotted on the canvas. When all the marks have been made,
the image
is flushed to a file on the disk.

The last bit of code puts the generators and renderers to work:

draw_ascii(create_gradient([‘R’, ‘B’, ‘Y’]), 28)

mex_flag = create_solid([‘rgb(0, 64, 0)’, ‘white’, ‘red’], 5)
border = create_solid([‘rgb(0, 64, 0)’], 25)

pattern = create_gradient([‘red’, ‘blue’, ‘yellow’])
pattern = pattern + mex_flag
pattern = pattern + border
pattern = pattern + create_gradient([‘black’, ‘red’, ‘orange’])
pattern = pattern + border
pattern = pattern + mex_flag.reverse
pattern = pattern + create_gradient([‘red’, ‘purple’, ‘black’], 8)
draw(“mexican_blanket.jpg”, pattern, 100, 200)

You can see here that the ASCII renderer is fed a trivial pattern
created from a
single gradient. The pattern built for the image file is more complex,
combining several different patterns. In both cases though, it’s a
single call
to the drawing routines we examined above to show results.

My thanks to all the weavers. I’ll bet you never knew you had such a
talent for
fabric.

Tomorrow we will try some non-traditional arithmetic…