amaze 0.2.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.
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