Mexican Blanket (#127)

by Scott Prager

I'm looking at this blanket my dad got in Mexico, and I'm noticing that it has an interesting gradient. There are two colors (c1 and c2) and the outside c1 is 5 stitches wide, then a 1 stitch c2, 4 stitches c1, 2 stitches c2... It looks something like this:

RRRRRBRRRRBBRRRBBBRRBBBBRBBB
RRRRBRRRRBBRRRBBBRRBBBBRBBBB
RRRBRRRRBBRRRBBBRRBBBBRBBBBB
RRBRRRRBBRRRBBBRRBBBBRBBBBBY
RBRRRRBBRRRBBBRRBBBBRBBBBBYB
BRRRRBBRRRBBBRRBBBBRBBBBBYBB
RRRRBBRRRBBBRRBBBBRBBBBBYBBB
RRRBBRRRBBBRRBBBBRBBBBBYBBBB
RRBBRRRBBBRRBBBBRBBBBBYBBBBY
RBBRRRBBBRRBBBBRBBBBBYBBBBYY
BBRRRBBBRRBBBBRBBBBBYBBBBYYB
BRRRBBBRRBBBBRBBBBBYBBBBYYBB
RRRBBBRRBBBBRBBBBBYBBBBYYBBB
RRBBBRRBBBBRBBBBBYBBBBYYBBBY
RBBBRRBBBBRBBBBBYBBBBYYBBBYY
BBBRRBBBBRBBBBBYBBBBYYBBBYYY
BBRRBBBBRBBBBBYBBBBYYBBBYYYB
BRRBBBBRBBBBBYBBBBYYBBBYYYBB
RRBBBBRBBBBBYBBBBYYBBBYYYBBY
RBBBBRBBBBBYBBBBYYBBBYYYBBYY
BBBBRBBBBBYBBBBYYBBBYYYBBYYY
BBBRBBBBBYBBBBYYBBBYYYBBYYYY
BBRBBBBBYBBBBYYBBBYYYBBYYYYB
BRBBBBBYBBBBYYBBBYYYBBYYYYBY
RBBBBBYBBBBYYBBBYYYBBYYYYBYY
BBBBBYBBBBYYBBBYYYBBYYYYBYYY
BBBBYBBBBYYBBBYYYBBYYYYBYYYY
BBBYBBBBYYBBBYYYBBYYYYBYYYYY

It goes on and on like that and when one color ends, another begins. When two sets of gradients meet, there is a thick separator. Two gradients don't always meet at the same color, but their opposite borders are usually the same. (ex: black-to-yellow |separator| white-to-black) There is also the occasional gradient of thick bars of equal length; ex: |green|-|yellow|-|red|. It looks like this:

Photo

So the challenge is to make a pattern generator to generate the patterns as seen in the ASCI and picture. It should, or rather NEEDS to, contain the Mexican flag as it is respectful to the culture where this challenge comes from. For extra credit, try to put them on a blanket of 200x100 pexels. And if you really need extra credit to graduate, make the patterns symmetrical, however make the colors in the patterns NOT symmetrical (ex: WWRW middle OBOO).


Quiz Summary

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:

ruby
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:

ruby
# ...

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:

ruby
# ...

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:

ruby
# ...

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:

ruby
# ...
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...