theseus 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|