theseus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +137 -0
- data/Rakefile +42 -0
- data/bin/theseus +262 -0
- data/examples/a-star-search.rb +106 -0
- data/lib/theseus.rb +6 -0
- data/lib/theseus/delta_maze.rb +45 -0
- data/lib/theseus/formatters/ascii.rb +41 -0
- data/lib/theseus/formatters/ascii/delta.rb +79 -0
- data/lib/theseus/formatters/ascii/orthogonal.rb +156 -0
- data/lib/theseus/formatters/ascii/sigma.rb +57 -0
- data/lib/theseus/formatters/ascii/upsilon.rb +67 -0
- data/lib/theseus/formatters/png.rb +183 -0
- data/lib/theseus/formatters/png/delta.rb +85 -0
- data/lib/theseus/formatters/png/orthogonal.rb +87 -0
- data/lib/theseus/formatters/png/sigma.rb +105 -0
- data/lib/theseus/formatters/png/upsilon.rb +137 -0
- data/lib/theseus/mask.rb +113 -0
- data/lib/theseus/maze.rb +855 -0
- data/lib/theseus/orthogonal_maze.rb +195 -0
- data/lib/theseus/path.rb +91 -0
- data/lib/theseus/sigma_maze.rb +107 -0
- data/lib/theseus/solvers/astar.rb +144 -0
- data/lib/theseus/solvers/backtracker.rb +79 -0
- data/lib/theseus/solvers/base.rb +95 -0
- data/lib/theseus/upsilon_maze.rb +37 -0
- data/lib/theseus/version.rb +10 -0
- data/test/maze_test.rb +193 -0
- metadata +104 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Formatters
|
5
|
+
# This is an abstract superclass for PNG formatters. It simply provides some common
|
6
|
+
# utility and drawing methods that subclasses can take advantage of, to render
|
7
|
+
# mazes to a PNG canvas.
|
8
|
+
#
|
9
|
+
# Colors are given as 32-bit integers, with each RGBA component occupying 1 byte.
|
10
|
+
# R is the highest byte, A is the lowest byte. In other words, 0xFF0000FF is an
|
11
|
+
# opaque red, and 0x7f7f7f7f is a semi-transparent gray. 0x0 is fully transparent.
|
12
|
+
#
|
13
|
+
# You may also provide the colors as hexadecimal string values, and they will be
|
14
|
+
# converted to the corresponding integers.
|
15
|
+
class PNG
|
16
|
+
# The default options. Note that not all PNG formatters honor all of these options;
|
17
|
+
# specifically, +:wall_width+ is not consistently supported across all formatters.
|
18
|
+
DEFAULTS = {
|
19
|
+
:cell_size => 10,
|
20
|
+
:wall_width => 1,
|
21
|
+
:wall_color => 0x000000FF,
|
22
|
+
:cell_color => 0xFFFFFFFF,
|
23
|
+
:solution_color => 0xFFAFAFFF,
|
24
|
+
:background => 0x00000000,
|
25
|
+
:outer_padding => 2,
|
26
|
+
:cell_padding => 1,
|
27
|
+
:solution => false
|
28
|
+
}
|
29
|
+
|
30
|
+
# North, whether in the under or primary plane
|
31
|
+
ANY_N = Maze::N | (Maze::N << Maze::UNDER_SHIFT)
|
32
|
+
|
33
|
+
# South, whether in the under or primary plane
|
34
|
+
ANY_S = Maze::S | (Maze::S << Maze::UNDER_SHIFT)
|
35
|
+
|
36
|
+
# West, whether in the under or primary plane
|
37
|
+
ANY_W = Maze::W | (Maze::W << Maze::UNDER_SHIFT)
|
38
|
+
|
39
|
+
# East, whether in the under or primary plane
|
40
|
+
ANY_E = Maze::E | (Maze::E << Maze::UNDER_SHIFT)
|
41
|
+
|
42
|
+
# The options to use for the formatter. These are the ones passed
|
43
|
+
# to the constructor, plus the ones from the DEFAULTS hash.
|
44
|
+
attr_reader :options
|
45
|
+
|
46
|
+
# The +options+ must be a hash of any of the following options:
|
47
|
+
#
|
48
|
+
# [:cell_size] The number of pixels on a side that each cell
|
49
|
+
# should occupy. Different maze types will use that
|
50
|
+
# space differently. Also, the cell padding is applied
|
51
|
+
# inside the cell, and so consumes some of the area.
|
52
|
+
# The default is 10.
|
53
|
+
# [:wall_width] How thick the walls should be drawn. The default is 1.
|
54
|
+
# Note that not all PNG formatters will honor this value
|
55
|
+
# (yet).
|
56
|
+
# [:wall_color] The color to use when drawing the wall. Defaults to black.
|
57
|
+
# [:cell_color] The color to use when drawing the cell. Defaults to white.
|
58
|
+
# [:solution_color] The color to use when drawing the solution path. This is
|
59
|
+
# only used when the :solution option is given.
|
60
|
+
# [:background] The color to use for the background of the maze. Defaults
|
61
|
+
# to transparent.
|
62
|
+
# [:outer_padding] The extra padding (in pixels) to add around the outside
|
63
|
+
# edge of the maze. Defaults to 2.
|
64
|
+
# [:cell_padding] The padding (in pixels) to add around the inside of each
|
65
|
+
# cell. This has the effect of separating the cells. The
|
66
|
+
# default cell padding is 1.
|
67
|
+
# [:solution] A boolean value indicating whether or not to draw the
|
68
|
+
# solution path as well. The default is false.
|
69
|
+
def initialize(maze, options)
|
70
|
+
@options = DEFAULTS.merge(options)
|
71
|
+
|
72
|
+
[:background, :wall_color, :cell_color, :solution_color].each do |c|
|
73
|
+
@options[c] = ChunkyPNG::Color.from_hex(@options[c]) unless Fixnum === @options[c]
|
74
|
+
end
|
75
|
+
|
76
|
+
@paths = @options[:paths] || []
|
77
|
+
|
78
|
+
if @options[:solution]
|
79
|
+
path = maze.new_solver(type: @options[:solution]).solve.to_path(color: @options[:solution_color])
|
80
|
+
@paths = [path, *@paths]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the raw PNG data for the formatter.
|
85
|
+
def to_blob
|
86
|
+
@blob
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the color at the given point by considering all provided paths. The
|
90
|
+
# +:color: metadata from the first path that is set at the given point is
|
91
|
+
# returned. If no path describes the given point, then the value of the
|
92
|
+
# +:cell_color+ option is returned.
|
93
|
+
def color_at(pt, direction=nil)
|
94
|
+
@paths.each do |path|
|
95
|
+
return path[:color] if direction ? path.path?(pt, direction) : path.set?(pt)
|
96
|
+
end
|
97
|
+
|
98
|
+
return @options[:cell_color]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns a new 2-tuple (x2,y2), where x2 is point[0] + dx, and y2 is point[1] + dy.
|
102
|
+
def move(point, dx, dy)
|
103
|
+
[point[0] + dx, point[1] + dy]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Clamps the value +x+ so that it lies between +low+ and +hi+. In other words,
|
107
|
+
# returns +low+ if +x+ is less than +low+, and +high+ if +x+ is greater than
|
108
|
+
# +high+, and returns +x+ otherwise.
|
109
|
+
def clamp(x, low, hi)
|
110
|
+
x = low if x < low
|
111
|
+
x = hi if x > hi
|
112
|
+
return x
|
113
|
+
end
|
114
|
+
|
115
|
+
# Draws a line from +p1+ to +p2+ on the given canvas object, in the given
|
116
|
+
# color. The coordinates of the given points are clamped (naively) to lie
|
117
|
+
# within the canvas' bounds.
|
118
|
+
def line(canvas, p1, p2, color)
|
119
|
+
canvas.line(
|
120
|
+
clamp(p1[0].round, 0, canvas.width-1),
|
121
|
+
clamp(p1[1].round, 0, canvas.height-1),
|
122
|
+
clamp(p2[0].round, 0, canvas.width-1),
|
123
|
+
clamp(p2[1].round, 0, canvas.height-1),
|
124
|
+
color)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Fills the rectangle defined by the given coordinates with the given color.
|
128
|
+
# The coordinates are clamped to lie within the canvas' bounds.
|
129
|
+
def fill_rect(canvas, x0, y0, x1, y1, color)
|
130
|
+
x0 = clamp(x0, 0, canvas.width-1)
|
131
|
+
y0 = clamp(y0, 0, canvas.height-1)
|
132
|
+
x1 = clamp(x1, 0, canvas.width-1)
|
133
|
+
y1 = clamp(y1, 0, canvas.height-1)
|
134
|
+
[x0, x1].min.ceil.upto([x0, x1].max.floor) do |x|
|
135
|
+
[y0, y1].min.ceil.upto([y0, y1].max.floor) do |y|
|
136
|
+
canvas.point(x, y, color)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Fills the polygon defined by the +points+ array, with the given +color+.
|
142
|
+
# Each element of +points+ must be a 2-tuple describing a vertex of the
|
143
|
+
# polygon. It is assumed that the polygon is closed. All points are
|
144
|
+
# clamped (naively) to lie within the canvas' bounds.
|
145
|
+
def fill_poly(canvas, points, color)
|
146
|
+
min_y = 1_000_000
|
147
|
+
max_y = -1_000_000
|
148
|
+
points.each do |x,y|
|
149
|
+
min_y = y if y < min_y
|
150
|
+
max_y = y if y > max_y
|
151
|
+
end
|
152
|
+
|
153
|
+
min_y = clamp(min_y, 0, canvas.height-1)
|
154
|
+
max_y = clamp(max_y, 0, canvas.height-1)
|
155
|
+
|
156
|
+
min_y.floor.upto(max_y.ceil) do |y|
|
157
|
+
nodes = []
|
158
|
+
|
159
|
+
prev = points.last
|
160
|
+
points.each do |point|
|
161
|
+
if point[1] < y && prev[1] >= y || prev[1] < y && point[1] >= y
|
162
|
+
nodes << (point[0] + (y - point[1]).to_f / (prev[1] - point[1]) * (prev[0] - point[0]))
|
163
|
+
end
|
164
|
+
prev = point
|
165
|
+
end
|
166
|
+
|
167
|
+
next if nodes.empty?
|
168
|
+
nodes.sort!
|
169
|
+
|
170
|
+
prev = nil
|
171
|
+
0.step(nodes.length-1, 2) do |a|
|
172
|
+
x1, x2 = nodes[a], nodes[a+1]
|
173
|
+
x1, x2 = x2, x1 if x1 > x2
|
174
|
+
next if x1 < 0 || x2 >= canvas.width
|
175
|
+
x1.ceil.upto(x2.floor) do |x|
|
176
|
+
canvas.point(x, y, color)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'theseus/formatters/png'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Formatters
|
5
|
+
class PNG
|
6
|
+
# Renders a DeltaMaze to a PNG canvas. Does not currently support the
|
7
|
+
# +:wall_width+ option.
|
8
|
+
#
|
9
|
+
# You will almost never access this class directly. Instead, use
|
10
|
+
# DeltaMaze#to(:png, options) to return the raw PNG data directly.
|
11
|
+
class Delta < PNG
|
12
|
+
# Create and return a fully initialized PNG::Delta object, with the
|
13
|
+
# maze rendered. To get the maze data, call #to_blob.
|
14
|
+
#
|
15
|
+
# See Theseus::Formatters::PNG for a list of all supported options.
|
16
|
+
def initialize(maze, options={})
|
17
|
+
super
|
18
|
+
|
19
|
+
height = @options[:outer_padding] * 2 + maze.height * @options[:cell_size]
|
20
|
+
width = @options[:outer_padding] * 2 + (maze.width + 1) * @options[:cell_size] / 2
|
21
|
+
|
22
|
+
canvas = ChunkyPNG::Image.new(width, height, @options[:background])
|
23
|
+
|
24
|
+
maze.height.times do |y|
|
25
|
+
py = @options[:outer_padding] + y * @options[:cell_size]
|
26
|
+
maze.row_length(y).times do |x|
|
27
|
+
px = @options[:outer_padding] + x * @options[:cell_size] / 2.0
|
28
|
+
draw_cell(canvas, [x, y], maze.points_up?(x,y), px, py, maze[x, y])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@blob = canvas.to_blob
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def draw_cell(canvas, point, up, x, y, cell) #:nodoc:
|
38
|
+
return if cell == 0
|
39
|
+
|
40
|
+
p1 = [x + options[:cell_size] / 2.0, up ? (y + options[:cell_padding]) : (y + options[:cell_size] - options[:cell_padding])]
|
41
|
+
p2 = [x + options[:cell_padding], up ? (y + options[:cell_size] - options[:cell_padding]) : (y + options[:cell_padding])]
|
42
|
+
p3 = [x + options[:cell_size] - options[:cell_padding], p2[1]]
|
43
|
+
|
44
|
+
fill_poly(canvas, [p1, p2, p3], color_at(point))
|
45
|
+
|
46
|
+
if cell & (Maze::N | Maze::S) != 0
|
47
|
+
clr = color_at(point, (Maze::N | Maze::S))
|
48
|
+
dy = options[:cell_padding]
|
49
|
+
sign = (cell & Maze::N != 0) ? -1 : 1
|
50
|
+
r1, r2 = p2, move(p3, 0, sign*dy)
|
51
|
+
fill_rect(canvas, r1[0].round, r1[1].round, r2[0].round, r2[1].round, clr)
|
52
|
+
line(canvas, r1, [r1[0], r2[1]], options[:wall_color])
|
53
|
+
line(canvas, r2, [r2[0], r1[1]], options[:wall_color])
|
54
|
+
else
|
55
|
+
line(canvas, p2, p3, options[:wall_color])
|
56
|
+
end
|
57
|
+
|
58
|
+
dx = options[:cell_padding]
|
59
|
+
if cell & ANY_W != 0
|
60
|
+
r1, r2, r3, r4 = p1, move(p1,-dx,0), move(p2,-dx,0), p2
|
61
|
+
fill_poly(canvas, [r1, r2, r3, r4], color_at(point, ANY_W))
|
62
|
+
line(canvas, r1, r2, options[:wall_color])
|
63
|
+
line(canvas, r3, r4, options[:wall_color])
|
64
|
+
end
|
65
|
+
|
66
|
+
if cell & Maze::W == 0
|
67
|
+
line(canvas, p1, p2, options[:wall_color])
|
68
|
+
end
|
69
|
+
|
70
|
+
if cell & ANY_E != 0
|
71
|
+
r1, r2, r3, r4 = p1, move(p1,dx,0), move(p3,dx,0), p3
|
72
|
+
fill_poly(canvas, [r1, r2, r3, r4], color_at(point, ANY_E))
|
73
|
+
line(canvas, r1, r2, options[:wall_color])
|
74
|
+
line(canvas, r3, r4, options[:wall_color])
|
75
|
+
end
|
76
|
+
|
77
|
+
if cell & Maze::E == 0
|
78
|
+
line(canvas, p3, p1, options[:wall_color])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'theseus/formatters/png'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Formatters
|
5
|
+
class PNG
|
6
|
+
# Renders an OrthogonalMaze to a PNG canvas.
|
7
|
+
#
|
8
|
+
# You will almost never access this class directly. Instead, use
|
9
|
+
# OrthogonalMaze#to(:png, options) to return the raw PNG data directly.
|
10
|
+
class Orthogonal < PNG
|
11
|
+
# Create and return a fully initialized PNG::Orthogonal object, with the
|
12
|
+
# maze rendered. To get the maze data, call #to_blob.
|
13
|
+
#
|
14
|
+
# See Theseus::Formatters::PNG for a list of all supported options.
|
15
|
+
def initialize(maze, options={})
|
16
|
+
super
|
17
|
+
|
18
|
+
width = @options[:outer_padding] * 2 + maze.width * @options[:cell_size]
|
19
|
+
height = @options[:outer_padding] * 2 + maze.height * @options[:cell_size]
|
20
|
+
|
21
|
+
canvas = ChunkyPNG::Image.new(width, height, @options[:background])
|
22
|
+
|
23
|
+
@d1 = @options[:cell_padding]
|
24
|
+
@d2 = @options[:cell_size] - @options[:cell_padding]
|
25
|
+
@w1 = (@options[:wall_width] / 2.0).floor
|
26
|
+
@w2 = ((@options[:wall_width] - 1) / 2.0).floor
|
27
|
+
|
28
|
+
maze.height.times do |y|
|
29
|
+
py = @options[:outer_padding] + y * @options[:cell_size]
|
30
|
+
maze.width.times do |x|
|
31
|
+
px = @options[:outer_padding] + x * @options[:cell_size]
|
32
|
+
draw_cell(canvas, [x, y], px, py, maze[x, y])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@blob = canvas.to_blob
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def draw_cell(canvas, point, x, y, cell) #:nodoc:
|
42
|
+
return if cell == 0
|
43
|
+
|
44
|
+
fill_rect(canvas, x + @d1, y + @d1, x + @d2, y + @d2, color_at(point))
|
45
|
+
|
46
|
+
north = cell & Maze::N == Maze::N
|
47
|
+
north_under = (cell >> Maze::UNDER_SHIFT) & Maze::N == Maze::N
|
48
|
+
south = cell & Maze::S == Maze::S
|
49
|
+
south_under = (cell >> Maze::UNDER_SHIFT) & Maze::S == Maze::S
|
50
|
+
west = cell & Maze::W == Maze::W
|
51
|
+
west_under = (cell >> Maze::UNDER_SHIFT) & Maze::W == Maze::W
|
52
|
+
east = cell & Maze::E == Maze::E
|
53
|
+
east_under = (cell >> Maze::UNDER_SHIFT) & Maze::E == Maze::E
|
54
|
+
|
55
|
+
draw_vertical(canvas, x, y, 1, north || north_under, !north || north_under, color_at(point, ANY_N))
|
56
|
+
draw_vertical(canvas, x, y + options[:cell_size], -1, south || south_under, !south || south_under, color_at(point, ANY_S))
|
57
|
+
draw_horizontal(canvas, x, y, 1, west || west_under, !west || west_under, color_at(point, ANY_W))
|
58
|
+
draw_horizontal(canvas, x + options[:cell_size], y, -1, east || east_under, !east || east_under, color_at(point, ANY_E))
|
59
|
+
end
|
60
|
+
|
61
|
+
def draw_vertical(canvas, x, y, direction, corridor, wall, color) #:nodoc:
|
62
|
+
if corridor
|
63
|
+
fill_rect(canvas, x + @d1, y, x + @d2, y + @d1 * direction, color)
|
64
|
+
fill_rect(canvas, x + @d1 - @w1, y - (@w1 * direction), x + @d1 + @w2, y + (@d1 + @w2) * direction, options[:wall_color])
|
65
|
+
fill_rect(canvas, x + @d2 - @w2, y - (@w1 * direction), x + @d2 + @w1, y + (@d1 + @w2) * direction, options[:wall_color])
|
66
|
+
end
|
67
|
+
|
68
|
+
if wall
|
69
|
+
fill_rect(canvas, x + @d1 - @w1, y + (@d1 - @w1) * direction, x + @d2 + @w2, y + (@d1 + @w2) * direction, options[:wall_color])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def draw_horizontal(canvas, x, y, direction, corridor, wall, color) #:nodoc:
|
74
|
+
if corridor
|
75
|
+
fill_rect(canvas, x, y + @d1, x + @d1 * direction, y + @d2, color)
|
76
|
+
fill_rect(canvas, x - (@w1 * direction), y + @d1 - @w1, x + (@d1 + @w2) * direction, y + @d1 + @w2, options[:wall_color])
|
77
|
+
fill_rect(canvas, x - (@w1 * direction), y + @d2 - @w2, x + (@d1 + @w2) * direction, y + @d2 + @w1, options[:wall_color])
|
78
|
+
end
|
79
|
+
|
80
|
+
if wall
|
81
|
+
fill_rect(canvas, x + (@d1 - @w1) * direction, y + @d1 - @w1, x + (@d1 + @w2) * direction, y + @d2 + @w2, options[:wall_color])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'theseus/formatters/png'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Formatters
|
5
|
+
class PNG
|
6
|
+
# Renders a SigmaMaze to a PNG canvas. Does not currently support the
|
7
|
+
# +:wall_width+ option.
|
8
|
+
#
|
9
|
+
# You will almost never access this class directly. Instead, use
|
10
|
+
# SigmaMaze#to(:png, options) to return the raw PNG data directly.
|
11
|
+
class Sigma < PNG
|
12
|
+
# Create and return a fully initialized PNG::Sigma object, with the
|
13
|
+
# maze rendered. To get the maze data, call #to_blob.
|
14
|
+
#
|
15
|
+
# See Theseus::Formatters::PNG for a list of all supported options.
|
16
|
+
def initialize(maze, options={})
|
17
|
+
super
|
18
|
+
|
19
|
+
width = @options[:outer_padding] * 2 + (3 * maze.width + 1) * @options[:cell_size] / 4
|
20
|
+
height = @options[:outer_padding] * 2 + maze.height * @options[:cell_size] + @options[:cell_size] / 2
|
21
|
+
|
22
|
+
canvas = ChunkyPNG::Image.new(width, height, @options[:background])
|
23
|
+
|
24
|
+
maze.height.times do |y|
|
25
|
+
py = @options[:outer_padding] + y * @options[:cell_size]
|
26
|
+
maze.row_length(y).times do |x|
|
27
|
+
px = @options[:outer_padding] + x * 3 * @options[:cell_size] / 4.0
|
28
|
+
shifted = (x % 2 != 0)
|
29
|
+
dy = shifted ? (@options[:cell_size] / 2.0) : 0
|
30
|
+
draw_cell(canvas, [x, y], shifted, px, py+dy, maze[x, y])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@blob = canvas.to_blob
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def draw_cell(canvas, point, shifted, x, y, cell) #:nodoc:
|
40
|
+
return if cell == 0
|
41
|
+
|
42
|
+
size = options[:cell_size] - options[:cell_padding] * 2
|
43
|
+
s4 = size / 4.0
|
44
|
+
|
45
|
+
fs4 = options[:cell_size] / 4.0 # fs == full-size, without padding
|
46
|
+
|
47
|
+
p1 = [x + options[:cell_padding] + s4, y + options[:cell_padding]]
|
48
|
+
p2 = [x + options[:cell_size] - options[:cell_padding] - s4, p1[1]]
|
49
|
+
p3 = [x + options[:cell_padding] + size, y + options[:cell_size] / 2.0]
|
50
|
+
p4 = [p2[0], y + options[:cell_size] - options[:cell_padding]]
|
51
|
+
p5 = [p1[0], p4[1]]
|
52
|
+
p6 = [x + options[:cell_padding], p3[1]]
|
53
|
+
|
54
|
+
fill_poly(canvas, [p1, p2, p3, p4, p5, p6], color_at(point))
|
55
|
+
|
56
|
+
n = Maze::N
|
57
|
+
s = Maze::S
|
58
|
+
nw = shifted ? Maze::W : Maze::NW
|
59
|
+
ne = shifted ? Maze::E : Maze::NE
|
60
|
+
sw = shifted ? Maze::SW : Maze::W
|
61
|
+
se = shifted ? Maze::SE : Maze::E
|
62
|
+
|
63
|
+
any = proc { |x| x | (x << Maze::UNDER_SHIFT) }
|
64
|
+
|
65
|
+
if cell & any[s] != 0
|
66
|
+
r1, r2 = p5, move(p4, 0, options[:cell_padding]*2)
|
67
|
+
fill_rect(canvas, r1[0], r1[1], r2[0], r2[1], color_at(point, any[s]))
|
68
|
+
line(canvas, p5, move(p5, 0, options[:cell_padding]*2), options[:wall_color])
|
69
|
+
line(canvas, p4, move(p4, 0, options[:cell_padding]*2), options[:wall_color])
|
70
|
+
end
|
71
|
+
|
72
|
+
if cell & any[ne] != 0
|
73
|
+
ne_x = x + 3 * options[:cell_size] / 4.0
|
74
|
+
ne_y = y - options[:cell_size] * 0.5
|
75
|
+
ne_p5 = [ne_x + options[:cell_padding] + s4, ne_y + options[:cell_size] - options[:cell_padding]]
|
76
|
+
ne_p6 = [ne_x + options[:cell_padding], ne_y + options[:cell_size] * 0.5]
|
77
|
+
r1, r2, r3, r4 = p2, p3, ne_p5, ne_p6
|
78
|
+
fill_poly(canvas, [r1, r2, r3, r4], color_at(point, any[ne]))
|
79
|
+
line(canvas, r1, r4, options[:wall_color])
|
80
|
+
line(canvas, r2, r3, options[:wall_color])
|
81
|
+
end
|
82
|
+
|
83
|
+
if cell & any[se] != 0
|
84
|
+
se_x = x + 3 * options[:cell_size] / 4.0
|
85
|
+
se_y = y + options[:cell_size] * 0.5
|
86
|
+
se_p1 = [se_x + s4 + options[:cell_padding], se_y + options[:cell_padding]]
|
87
|
+
se_p6 = [se_x + options[:cell_padding], se_y + options[:cell_size] * 0.5]
|
88
|
+
r1, r2, r3, r4 = p3, p4, se_p6, se_p1
|
89
|
+
fill_poly(canvas, [r1, r2, r3, r4], color_at(point, any[se]))
|
90
|
+
line(canvas, r1, r4, options[:wall_color])
|
91
|
+
line(canvas, r2, r3, options[:wall_color])
|
92
|
+
end
|
93
|
+
|
94
|
+
line(canvas, p1, p2, options[:wall_color]) if cell & n == 0
|
95
|
+
line(canvas, p2, p3, options[:wall_color]) if cell & ne == 0
|
96
|
+
line(canvas, p3, p4, options[:wall_color]) if cell & se == 0
|
97
|
+
line(canvas, p4, p5, options[:wall_color]) if cell & s == 0
|
98
|
+
line(canvas, p5, p6, options[:wall_color]) if cell & sw == 0
|
99
|
+
line(canvas, p6, p1, options[:wall_color]) if cell & nw == 0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|