amaze 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +110 -0
  8. data/Rakefile +6 -0
  9. data/amaze.gemspec +30 -0
  10. data/bin/console +15 -0
  11. data/bin/setup +8 -0
  12. data/exe/amaze +5 -0
  13. data/lib/amaze.rb +17 -0
  14. data/lib/amaze/algorithm.rb +44 -0
  15. data/lib/amaze/algorithm/aldous_border.rb +36 -0
  16. data/lib/amaze/algorithm/binary_tree.rb +20 -0
  17. data/lib/amaze/algorithm/growing_tree.rb +52 -0
  18. data/lib/amaze/algorithm/hunt_and_kill.rb +53 -0
  19. data/lib/amaze/algorithm/recursive_backtracker.rb +77 -0
  20. data/lib/amaze/algorithm/sidewinder.rb +42 -0
  21. data/lib/amaze/algorithm/wilson.rb +54 -0
  22. data/lib/amaze/cell.rb +63 -0
  23. data/lib/amaze/cell/hex.rb +10 -0
  24. data/lib/amaze/cell/octo.rb +10 -0
  25. data/lib/amaze/cell/polar.rb +16 -0
  26. data/lib/amaze/cell/square.rb +10 -0
  27. data/lib/amaze/distances.rb +53 -0
  28. data/lib/amaze/factory.rb +153 -0
  29. data/lib/amaze/formatter.rb +5 -0
  30. data/lib/amaze/formatter/ascii.rb +91 -0
  31. data/lib/amaze/formatter/ascii/delta.rb +180 -0
  32. data/lib/amaze/formatter/ascii/ortho.rb +105 -0
  33. data/lib/amaze/formatter/ascii/polar.rb +199 -0
  34. data/lib/amaze/formatter/ascii/sigma.rb +213 -0
  35. data/lib/amaze/formatter/ascii/upsilon.rb +281 -0
  36. data/lib/amaze/formatter/image.rb +127 -0
  37. data/lib/amaze/formatter/image/delta.rb +123 -0
  38. data/lib/amaze/formatter/image/ortho.rb +103 -0
  39. data/lib/amaze/formatter/image/polar.rb +173 -0
  40. data/lib/amaze/formatter/image/sigma.rb +122 -0
  41. data/lib/amaze/formatter/image/upsilon.rb +135 -0
  42. data/lib/amaze/grid.rb +66 -0
  43. data/lib/amaze/grid/delta.rb +87 -0
  44. data/lib/amaze/grid/ortho.rb +38 -0
  45. data/lib/amaze/grid/polar.rb +61 -0
  46. data/lib/amaze/grid/sigma.rb +61 -0
  47. data/lib/amaze/grid/upsilon.rb +74 -0
  48. data/lib/amaze/mask.rb +75 -0
  49. data/lib/amaze/masked_grid.rb +29 -0
  50. data/lib/amaze/script.rb +361 -0
  51. data/lib/amaze/shape.rb +23 -0
  52. data/lib/amaze/shape/diamond.rb +26 -0
  53. data/lib/amaze/shape/hexagon.rb +79 -0
  54. data/lib/amaze/shape/star.rb +114 -0
  55. data/lib/amaze/shape/triangle.rb +25 -0
  56. data/lib/amaze/version.rb +3 -0
  57. data/support/characters.txt +17 -0
  58. data/support/mask/mask1.txt +10 -0
  59. data/support/mask/mask2.txt +12 -0
  60. data/support/mask/mask3.txt +15 -0
  61. metadata +203 -0
@@ -0,0 +1,127 @@
1
+
2
+ require 'rmagick'
3
+
4
+ class Amaze::Formatter::Image
5
+ autoload :Ortho, 'amaze/formatter/image/ortho'
6
+ autoload :Sigma, 'amaze/formatter/image/sigma'
7
+ autoload :Delta, 'amaze/formatter/image/delta'
8
+ autoload :Upsilon, 'amaze/formatter/image/upsilon'
9
+ autoload :Polar, 'amaze/formatter/image/polar'
10
+
11
+ # The grid
12
+ attr_reader :grid
13
+
14
+ # Options for the Image renderer
15
+ attr_reader :options
16
+
17
+ def initialize grid, options={}
18
+ @grid = grid
19
+ @options = options
20
+ end
21
+
22
+ def path? direction, cell
23
+ cell.linked?(cell.send(direction)) && path_cell?(cell.send(direction))
24
+ end
25
+
26
+ def path_cell? cell
27
+ path_cells.include? cell
28
+ end
29
+
30
+ def path_cells
31
+ Array(options[:path_cells])
32
+ end
33
+
34
+ def path_start
35
+ options[:path_start]
36
+ end
37
+
38
+ def path_finish
39
+ options[:path_finish]
40
+ end
41
+
42
+ def render
43
+ render_background if distances
44
+ render_grid if show_grid?
45
+ render_wall unless hide_walls?
46
+ render_path if path_cells.any?
47
+ end
48
+
49
+ def image
50
+ @image ||= Magick::Image.new image_width, image_height, Magick::SolidFill.new(background_color)
51
+ end
52
+
53
+ def canvas
54
+ @canvas ||= Magick::Draw.new
55
+ end
56
+
57
+ def write filename
58
+ canvas.draw image
59
+ image.write(filename)
60
+ end
61
+
62
+ def cell_width
63
+ @options[:cell_width] || 100
64
+ end
65
+
66
+ def border_width
67
+ @options[:border_width] || 0
68
+ end
69
+
70
+ def wall_width
71
+ @options[:wall_width] || 5
72
+ end
73
+
74
+ def wall_color
75
+ @options[:wall_color].to_s || 'black'
76
+ end
77
+
78
+ def path_width
79
+ @options[:path_width] || 3
80
+ end
81
+
82
+ def path_color
83
+ @options[:path_color].to_s || 'red'
84
+ end
85
+
86
+ def background_color
87
+ @options[:background_color].to_s || 'white'
88
+ end
89
+
90
+ def show_grid?
91
+ @options[:show_grid]
92
+ end
93
+
94
+ def hide_walls?
95
+ @options[:hide_walls]
96
+ end
97
+
98
+ def cell_offset
99
+ wall_width / 2.0 + border_width
100
+ end
101
+
102
+ def distances
103
+ options[:distances]
104
+ end
105
+
106
+ def distances_max
107
+ @distances_max ||= distances.max[1]
108
+ end
109
+
110
+ # Returns the background color of a cell, depending on its distance from the origin
111
+ def distance_color cell
112
+ if distances && distances[cell]
113
+ intensity = (distances_max - distances[cell].to_f) / distances_max
114
+ '#' + gradient.at(intensity).color.hex
115
+ else
116
+ nil
117
+ end
118
+ end
119
+
120
+ def gradient
121
+ options[:gradient_map]
122
+ end
123
+
124
+ def self.colors
125
+ Magick.colors.map(&:name)
126
+ end
127
+ end
@@ -0,0 +1,123 @@
1
+
2
+ class Amaze::Formatter::Image::Delta < Amaze::Formatter::Image
3
+
4
+ def render_background
5
+ canvas.stroke_antialias true
6
+ canvas.stroke_linecap 'round'
7
+ canvas.stroke_linejoin 'round'
8
+ canvas.stroke 'none'
9
+ grid.each_cell do |cell|
10
+ color = distance_color cell
11
+ next unless color
12
+ canvas.fill color
13
+ canvas.polygon *coord(cell)
14
+ end
15
+ end
16
+
17
+ def render_grid
18
+ canvas.stroke_antialias true
19
+ canvas.stroke_linecap 'round'
20
+ canvas.stroke_linejoin 'round'
21
+ canvas.stroke 'gray90'
22
+ canvas.stroke_width 1
23
+ canvas.fill 'none'
24
+
25
+ grid.each_cell do |cell|
26
+ canvas.polygon *coord(cell)
27
+ end
28
+ end
29
+
30
+ def render_wall
31
+ canvas.stroke_antialias true
32
+ canvas.stroke_linecap 'round'
33
+ canvas.stroke_linejoin 'round'
34
+ canvas.stroke wall_color
35
+ canvas.stroke_width wall_width
36
+ canvas.fill 'none'
37
+
38
+ grid.each_cell do |cell|
39
+ ax, ay, bx, by, cx, cy = coord cell
40
+
41
+ canvas.line ax, ay, cx, cy unless cell.linked_to?(:west)
42
+ canvas.line bx, by, cx, cy unless cell.linked_to?(:east)
43
+
44
+ direction = (cell.row+cell.column).even? ? :north : :south
45
+ canvas.line ax, ay, bx, by unless cell.linked_to?(direction)
46
+ end
47
+ end
48
+
49
+ def render_path
50
+ canvas.stroke_antialias true
51
+ canvas.stroke_linecap 'round'
52
+ canvas.stroke_linejoin 'round'
53
+ canvas.fill 'none'
54
+ canvas.stroke path_color
55
+ canvas.stroke_width path_width
56
+
57
+ grid.each_cell do |cell|
58
+ next unless path_cell? cell
59
+
60
+ x1, y1 = center_coord cell
61
+ %i( north east ).each do |direction|
62
+ next unless path?(direction, cell)
63
+ x2, y2 = center_coord cell.send(direction)
64
+ canvas.line x1, y1, x2, y2
65
+ end
66
+ end
67
+
68
+ # draw start and finish
69
+ canvas.stroke_antialias true
70
+ canvas.stroke_linecap 'round'
71
+ canvas.fill path_color
72
+ canvas.stroke 'none'
73
+ [path_start, path_finish].compact.each do |cell|
74
+ x, y = center_coord cell
75
+ canvas.ellipse x, y, path_width*2, path_width*2, 0, 360
76
+ end
77
+ end
78
+
79
+ def coord cell
80
+ row, column = cell.row, cell.column
81
+
82
+ x1 = column * pattern_width + cell_offset
83
+ y1 = row * pattern_height + cell_offset
84
+ x2 = x1 + pattern_width
85
+ x3 = x2 + pattern_width
86
+ y2 = y1 + pattern_height
87
+
88
+ if (row+column).even?
89
+ [x1, y1, x3, y1, x2, y2]
90
+ else
91
+ [x1, y2, x3, y2, x2, y1]
92
+ end
93
+ end
94
+
95
+ def center_coord cell
96
+ row, column = cell.row, cell.column
97
+
98
+ x = (column+1) * pattern_width + cell_offset
99
+ y0 = row * pattern_height + cell_offset
100
+
101
+ dy = cell_width * Math.sqrt(3)
102
+ fraction = (row+column).even? ? 6.0 : 3.0
103
+ y = y0 + dy / fraction
104
+
105
+ [x, y]
106
+ end
107
+
108
+ def pattern_width
109
+ @pattern_width ||= cell_width / 2.0
110
+ end
111
+
112
+ def pattern_height
113
+ @pattern_height ||= cell_width * Math.sqrt(3) / 2.0
114
+ end
115
+
116
+ def image_width
117
+ pattern_width * (grid.columns + 1) + wall_width + border_width * 2 + 1
118
+ end
119
+
120
+ def image_height
121
+ pattern_height * grid.rows + wall_width + border_width * 2 + 1
122
+ end
123
+ end
@@ -0,0 +1,103 @@
1
+
2
+ class Amaze::Formatter::Image::Ortho < Amaze::Formatter::Image
3
+
4
+ def render_background
5
+ canvas.stroke_antialias true
6
+ canvas.stroke_linecap 'square'
7
+ canvas.stroke 'none'
8
+ grid.each_cell do |cell|
9
+ color = distance_color cell
10
+ next unless color
11
+ canvas.fill color
12
+ x1, x2, y1, y2 = coord cell
13
+ canvas.polygon x1, y1, x2, y1, x2, y2, x1, y2
14
+ end
15
+ end
16
+
17
+ def render_grid
18
+ canvas.stroke_antialias true
19
+ canvas.stroke_linecap 'square'
20
+ canvas.stroke 'gray90'
21
+ canvas.stroke_width 1
22
+ canvas.fill 'none'
23
+
24
+ grid.each_cell do |cell|
25
+ x1, x2, y1, y2 = coord cell
26
+ canvas.polygon x1, y1, x2, y1, x2, y2, x1, y2
27
+ end
28
+ end
29
+
30
+ def render_wall
31
+ canvas.stroke_antialias true
32
+ canvas.stroke_linecap 'square'
33
+ canvas.stroke wall_color
34
+ canvas.stroke_width wall_width
35
+ canvas.fill 'none'
36
+
37
+ grid.each_cell do |cell|
38
+ x1, x2, y1, y2 = coord cell
39
+
40
+ canvas.line x1, y1, x2, y1 unless cell.linked_to?(:north)
41
+ canvas.line x2, y1, x2, y2 unless cell.linked_to?(:east)
42
+ canvas.line x1, y2, x2, y2 unless cell.linked_to?(:south)
43
+ canvas.line x1, y1, x1, y2 unless cell.linked_to?(:west)
44
+ end
45
+ end
46
+
47
+ def render_path
48
+ canvas.stroke_antialias true
49
+ canvas.stroke_linecap 'square'
50
+ canvas.fill 'none'
51
+ canvas.stroke path_color
52
+ canvas.stroke_width path_width
53
+
54
+ grid.each_cell do |cell|
55
+ next unless path_cell? cell
56
+
57
+ x1, y1 = center_coord cell
58
+ %i( north east ).each do |direction|
59
+ next unless path?(direction, cell)
60
+ x2, y2 = center_coord cell.send(direction)
61
+ canvas.line x1, y1, x2, y2
62
+ end
63
+ end
64
+
65
+ # draw start and finish
66
+ canvas.stroke_antialias true
67
+ canvas.stroke_linecap 'square'
68
+ canvas.fill path_color
69
+ canvas.stroke 'none'
70
+ [path_start, path_finish].compact.each do |cell|
71
+ x, y = center_coord cell
72
+ canvas.ellipse x, y, path_width*2, path_width*2, 0, 360
73
+ end
74
+ end
75
+
76
+ def coord cell
77
+ row, column = cell.row, cell.column
78
+
79
+ x1 = column * cell_width + cell_offset
80
+ x2 = (column+1) * cell_width + cell_offset
81
+ y1 = row * cell_width + cell_offset
82
+ y2 = (row+1) * cell_width + cell_offset
83
+
84
+ [x1, x2, y1, y2]
85
+ end
86
+
87
+ def center_coord cell
88
+ row, column = cell.row, cell.column
89
+
90
+ x = (column+0.5) * cell_width + cell_offset
91
+ y = (row+0.5) * cell_width + cell_offset
92
+
93
+ [x, y]
94
+ end
95
+
96
+ def image_width
97
+ cell_width * grid.columns + wall_width + border_width * 2 + 1
98
+ end
99
+
100
+ def image_height
101
+ cell_width * grid.rows + wall_width + border_width * 2 + 1
102
+ end
103
+ end
@@ -0,0 +1,173 @@
1
+
2
+ class Amaze::Formatter::Image::Polar < Amaze::Formatter::Image
3
+
4
+ def render_background
5
+ canvas.stroke_antialias true
6
+ canvas.stroke_linecap 'butt'
7
+ canvas.stroke_width cell_width
8
+ canvas.fill 'none'
9
+
10
+ grid.each_cell do |cell|
11
+ color = distance_color cell
12
+ next unless color
13
+ canvas.stroke color
14
+ _, _, _, _, _, ccw, cw = coord cell
15
+ radius, _ = center_coord cell
16
+ canvas.ellipse image_center, image_center, radius, radius, ccw, cw
17
+ end
18
+ end
19
+
20
+ def render_grid
21
+ canvas.stroke_antialias true
22
+ canvas.stroke_linecap 'square'
23
+ canvas.stroke 'gray90'
24
+ canvas.stroke_width 1
25
+ canvas.fill 'none'
26
+
27
+ grid.each_cell do |cell|
28
+ next if cell.row == 0
29
+ cx, cy, dx, dy, radius, ccw, cw = coord cell
30
+
31
+ canvas.ellipse image_center, image_center, radius, radius, ccw, cw
32
+ canvas.line cx, cy, dx, dy
33
+ end
34
+ canvas.ellipse(image_center, image_center, grid.rows * cell_width, grid.rows * cell_width, 0, 360)
35
+ end
36
+
37
+ def render_wall
38
+ canvas.stroke_antialias true
39
+ canvas.stroke_linecap 'square'
40
+ canvas.stroke wall_color
41
+ canvas.stroke_width wall_width
42
+ canvas.fill 'none'
43
+
44
+ grid.each_cell do |cell|
45
+ next if cell.row == 0
46
+ cx, cy, dx, dy, radius, ccw, cw = coord cell
47
+
48
+ canvas.ellipse image_center, image_center, radius, radius, ccw, cw unless cell.linked_to?(:inward)
49
+ canvas.line cx, cy, dx, dy unless cell.linked_to?(:cw)
50
+ end
51
+
52
+ canvas.ellipse(image_center, image_center, grid.rows * cell_width, grid.rows * cell_width, 0, 360)
53
+ end
54
+
55
+ def render_path
56
+ canvas.stroke_antialias true
57
+ canvas.stroke_linecap 'square'
58
+ canvas.fill 'none'
59
+ canvas.stroke path_color
60
+ canvas.stroke_width path_width
61
+
62
+ grid.each_cell do |cell|
63
+ next unless path_cell? cell
64
+
65
+ unless path?(:cw, cell) || path?(:ccw, cell)
66
+ # draw arc to close the gap if outward ring is subdivided
67
+ # and cell is linked outwards but not cw and ccw
68
+ # this can be the case even for cell(0,0)
69
+ outward_cells = path_outward(cell)
70
+ if outward_subdivided?(cell) && outward_cells.any?
71
+ radius, angle = center_coord cell
72
+ angles_outward_cells = outward_cells.map {|o| _, a = center_coord(o); a }
73
+ # don't use cell(0,0) own angel, override with one of the outward cells
74
+ angle = angles_outward_cells.first if cell.row == 0
75
+ angle1 = [angle, *angles_outward_cells].min
76
+ angle2 = [angle, *angles_outward_cells].max
77
+ canvas.ellipse image_center, image_center, radius, radius, angle1, angle2 unless angle1 == angle2
78
+ end
79
+ end
80
+
81
+ next if cell.row == 0
82
+
83
+ if path?(:inward, cell)
84
+ radius, theta = center_coord cell, :radian
85
+ # center of cell
86
+ x1, y1 = polar2cartesian(radius, theta)
87
+ # center of inward cell, but adjusted to the same angle of the current cell
88
+ x2, y2 = polar2cartesian(radius - cell_width, theta)
89
+ canvas.line x1, y1, x2, y2
90
+ end
91
+
92
+ if path?(:cw, cell)
93
+ radius1, angle1 = center_coord cell
94
+ radius2, angle2 = center_coord cell.cw
95
+ # adjust angle if outward ring is subdivided
96
+ if outward_subdivided?(cell)
97
+ outward_cells = path_outward(cell)
98
+ _, angle1 = center_coord(outward_cells.first) if outward_cells.any?
99
+ outward_cells_cw = path_outward(cell.cw)
100
+ _, angle2 = center_coord(outward_cells_cw.first) if outward_cells_cw.any?
101
+ end
102
+ canvas.ellipse image_center, image_center, radius1, radius1, angle1, angle2
103
+ end
104
+ end
105
+
106
+ # draw start and finish
107
+ canvas.stroke_antialias true
108
+ canvas.stroke_linecap 'square'
109
+ canvas.fill path_color
110
+ canvas.stroke 'none'
111
+ [path_start, path_finish].compact.each do |cell|
112
+ x, y = polar2cartesian(*center_coord(cell, :radian))
113
+ canvas.ellipse x, y, path_width*2, path_width*2, 0, 360
114
+ end
115
+ end
116
+
117
+ def outward_subdivided? cell
118
+ return false if grid.rows == cell.row + 1
119
+ grid.columns(cell.row).size != grid.columns(cell.row+1).size
120
+ end
121
+
122
+ def path_outward cell
123
+ cell.outward.select {|o| cell.linked?(o) && path_cell?(o) }
124
+ end
125
+
126
+ def coord cell, unit=:degree
127
+ inner_radius = cell.row * cell_width
128
+ outer_radius = (cell.row + 1) * cell_width
129
+ theta = 2 * Math::PI / grid.columns(cell.row).size
130
+ theta_ccw = cell.column * theta
131
+ theta_cw = (cell.column + 1) * theta
132
+
133
+ # we need only the cartesian coords of the cw wall
134
+ # ax, ay = polar2cartesian(inner_radius, theta_ccw)
135
+ # bx, by = polar2cartesian(outer_radius, theta_ccw)
136
+ cx, cy = polar2cartesian(inner_radius, theta_cw)
137
+ dx, dy = polar2cartesian(outer_radius, theta_cw)
138
+
139
+ if unit == :degree
140
+ theta_ccw = radian2degree theta_ccw
141
+ theta_cw = radian2degree theta_cw
142
+ end
143
+
144
+ [cx, cy, dx, dy, inner_radius, theta_ccw, theta_cw]
145
+ end
146
+
147
+ def center_coord cell, unit=:degree
148
+ radius = (cell.row + 0.5) * cell_width
149
+ theta = 2 * Math::PI / grid.columns(cell.row).size
150
+ angle = (cell.column + 0.5) * theta
151
+ angle = radian2degree(angle) if unit == :degree
152
+
153
+ [radius, angle]
154
+ end
155
+
156
+ def polar2cartesian radius, theta
157
+ [image_center + radius * Math.cos(theta), image_center + radius * Math.sin(theta)]
158
+ end
159
+
160
+ def radian2degree value
161
+ 360 / (2 * Math::PI) * value
162
+ end
163
+
164
+ def image_width
165
+ cell_width * grid.rows * 2 + wall_width + border_width * 2 + 3 # why? +3
166
+ end
167
+
168
+ alias_method :image_height, :image_width
169
+
170
+ def image_center
171
+ image_width / 2
172
+ end
173
+ end