waxy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +16 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/img/random.png +0 -0
- data/examples/img/rectangle.png +0 -0
- data/lib/waxy.rb +71 -0
- data/lib/waxy/geometry.rb +60 -0
- data/lib/waxy/geometry/fractional_hex.rb +62 -0
- data/lib/waxy/geometry/hex.rb +93 -0
- data/lib/waxy/geometry/layout.rb +130 -0
- data/lib/waxy/geometry/orientation.rb +50 -0
- data/lib/waxy/geometry/point.rb +30 -0
- data/lib/waxy/meta.rb +71 -0
- data/lib/waxy/render.rb +20 -0
- data/lib/waxy/render/canvas.rb +28 -0
- data/lib/waxy/render/svg.rb +134 -0
- data/lib/waxy/render/templates/close_svg.erb +1 -0
- data/lib/waxy/render/templates/hex.erb +6 -0
- data/lib/waxy/render/templates/hex_pie.erb +32 -0
- data/lib/waxy/render/templates/open_svg.erb +1 -0
- data/lib/waxy/render/templates/svg.erb +59 -0
- data/lib/waxy/version.rb +3 -0
- data/waxy.gemspec +46 -0
- metadata +163 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Waxy
|
2
|
+
module Geometry
|
3
|
+
|
4
|
+
class FractionalHex
|
5
|
+
|
6
|
+
attr_accessor :q, :r, :mq, :mr
|
7
|
+
|
8
|
+
# Ints
|
9
|
+
def initialize(_q, _r, _mq, _mr)
|
10
|
+
@q = _q
|
11
|
+
@r = _r
|
12
|
+
@mq = _mq
|
13
|
+
@mr = _mr
|
14
|
+
end
|
15
|
+
|
16
|
+
# def == (b)
|
17
|
+
# q == b.q && r == b.r && s == b.s
|
18
|
+
# end
|
19
|
+
|
20
|
+
# def != (b)
|
21
|
+
# !(self == b)
|
22
|
+
# end
|
23
|
+
|
24
|
+
# def + (b)
|
25
|
+
# Hex.new(q + b.q, r + b.r, s + b.s)
|
26
|
+
# end
|
27
|
+
|
28
|
+
# def - (b)
|
29
|
+
# Hex.new(q - b.q, r - b.r, s - b.s)
|
30
|
+
# end
|
31
|
+
|
32
|
+
# # @param a [Hex]
|
33
|
+
# # @param k [Int]
|
34
|
+
# def * (k)
|
35
|
+
# Hex.new(q * k, r * k, s * k)
|
36
|
+
# end
|
37
|
+
|
38
|
+
# def hex_length(hex)
|
39
|
+
# ((hex.q.abs + hex.r.abs + hex.s.abs) / 2).to_i
|
40
|
+
# end
|
41
|
+
|
42
|
+
# def distance(b)
|
43
|
+
# hex_length(self - b)
|
44
|
+
# end
|
45
|
+
|
46
|
+
# def hex_direction(i)
|
47
|
+
# raise if !(0..5).include?(i)
|
48
|
+
# HEX_DIRECTIONS[i]
|
49
|
+
# end
|
50
|
+
|
51
|
+
# # (0..5, requires modulo prior)
|
52
|
+
# def hex_neighbor(i)
|
53
|
+
# self + hex_direction(i)
|
54
|
+
# end
|
55
|
+
|
56
|
+
# def polygon_corners(layout)
|
57
|
+
|
58
|
+
# end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Waxy
|
2
|
+
module Geometry
|
3
|
+
class Hex
|
4
|
+
|
5
|
+
attr_accessor :q, :r, :s
|
6
|
+
|
7
|
+
# Vector of 6 x (0.0 - 1.0)
|
8
|
+
# Used to scale the pie slices
|
9
|
+
# !! @size is typically set during render through a Waxy::Meta
|
10
|
+
|
11
|
+
attr_accessor :size
|
12
|
+
|
13
|
+
# All params are Int
|
14
|
+
def initialize(_q, _r, _s = nil)
|
15
|
+
@q = _q
|
16
|
+
@r = _r
|
17
|
+
@s = _s
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Array]
|
21
|
+
# initialized here for foreseable geometry calculations,
|
22
|
+
# but in general this should be handled through a Meta
|
23
|
+
def size
|
24
|
+
@size ||= [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
|
25
|
+
@size
|
26
|
+
end
|
27
|
+
|
28
|
+
def == (b)
|
29
|
+
q == b.q && r == b.r && s == b.s
|
30
|
+
end
|
31
|
+
|
32
|
+
def != (b)
|
33
|
+
!(self == b)
|
34
|
+
end
|
35
|
+
|
36
|
+
def + (b)
|
37
|
+
Hex.new(q + b.q, r + b.r, ( s ? s + b.s : nil))
|
38
|
+
end
|
39
|
+
|
40
|
+
def - (b)
|
41
|
+
Hex.new(q - b.q, r - b.r, (s ? s - b.s : nil))
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param a [Hex]
|
45
|
+
# @param k [Int]
|
46
|
+
def * (k)
|
47
|
+
Hex.new(q * k, r * k, (s ? s * k : nil))
|
48
|
+
end
|
49
|
+
|
50
|
+
def hex_length(hex)
|
51
|
+
((hex.q.abs + hex.r.abs + hex.s.abs) / 2).to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
def distance(b)
|
55
|
+
hex_length(self - b)
|
56
|
+
end
|
57
|
+
|
58
|
+
def hex_direction(i)
|
59
|
+
raise if !(0..5).include?(i)
|
60
|
+
HEX_DIRECTIONS_FLAT[i]
|
61
|
+
end
|
62
|
+
|
63
|
+
# (0..5, requires modulo prior)
|
64
|
+
def hex_neighbor(i)
|
65
|
+
self + hex_direction(i)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#TODO(mjy) fix
|
70
|
+
def self.point(center, size, i)
|
71
|
+
angle_deg = 60.0 * i
|
72
|
+
angle_rad = Math::PI / 180 * angle_deg
|
73
|
+
Waxy::Geometry::Point.new(
|
74
|
+
((center.x + size) * Math.cos(angle_rad)),
|
75
|
+
((center.y + size ) * Math.sin(angle_rad))
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.hexagon(center, size)
|
80
|
+
Hexagon.new(
|
81
|
+
outer_coords: (1..6).collect{|i| point(center, size, i)}
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.triangle(center, size, i)
|
86
|
+
points = [[0,0]]
|
87
|
+
points.push point(center, size, i)
|
88
|
+
points.push point(center, size, (i + 1 == 7 ? 1 : i + 1))
|
89
|
+
points
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Waxy
|
2
|
+
module Geometry
|
3
|
+
|
4
|
+
class Layout
|
5
|
+
|
6
|
+
# Waxy::Geometry::Orientation
|
7
|
+
attr_accessor :orientation
|
8
|
+
|
9
|
+
# Waxy::Geometry::Point
|
10
|
+
attr_accessor :size
|
11
|
+
|
12
|
+
# Waxy::Geometry::Point
|
13
|
+
attr_accessor :origin
|
14
|
+
|
15
|
+
# pixels between hexes
|
16
|
+
# !! Padding is applied at render time, not geometry compute time, i.e. using this will currently
|
17
|
+
# !! break things like pixel_to_hex
|
18
|
+
attr_accessor :padding
|
19
|
+
|
20
|
+
attr_reader :pad_h_offset, :pad_w_offset
|
21
|
+
|
22
|
+
def initialize(_orientation, _size, _origin, _padding = 0.0)
|
23
|
+
@orientation = _orientation
|
24
|
+
@size = _size
|
25
|
+
@origin = _origin
|
26
|
+
@padding = _padding
|
27
|
+
reset_padding
|
28
|
+
end
|
29
|
+
|
30
|
+
def hex_to_pixel(hex)
|
31
|
+
m = orientation
|
32
|
+
x = (m.f0 * hex.q + m.f1 * hex.r) * size.x
|
33
|
+
y = (m.f2 * hex.q + m.f3 * hex.r) * size.y
|
34
|
+
|
35
|
+
Waxy::Geometry::Point.new(x + origin.x, y + origin.y)
|
36
|
+
end
|
37
|
+
|
38
|
+
def pixel_to_hex(p)
|
39
|
+
m = orientation
|
40
|
+
pt = Waxy::Geometry::Point.new((p.x - origin.x) / size.x,
|
41
|
+
(p.y - origin.y) / size.y)
|
42
|
+
q = m.b0 * pt.x + m.b1 * pt.y
|
43
|
+
r = m.b2 * pt.x + m.b3 * pt.y
|
44
|
+
|
45
|
+
Waxy::Geometry::FractionalHex.new(q, r, q * -1.0, r * -1.0)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param corner [Integer]
|
49
|
+
def hex_corner_offset(corner, s = nil)
|
50
|
+
s ||= size
|
51
|
+
angle = hex_corner_angle(corner)
|
52
|
+
Waxy::Geometry::Point.new(s.x * Math.cos(angle), s.y * Math.sin(angle))
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param corner [Integer]
|
56
|
+
def hex_corner_angle(corner)
|
57
|
+
2.0 * Math::PI * (orientation.start_angle + corner) / 6
|
58
|
+
end
|
59
|
+
|
60
|
+
# @params h [Waxy::Geometry::Hex]
|
61
|
+
def polygon_corners(h)
|
62
|
+
corners = []
|
63
|
+
center = hex_to_pixel(h)
|
64
|
+
(0..5).each do |i|
|
65
|
+
offset = hex_corner_offset(i)
|
66
|
+
|
67
|
+
corners.push Waxy::Geometry::Point.new(
|
68
|
+
center.x + offset.x,
|
69
|
+
center.y + offset.y)
|
70
|
+
end
|
71
|
+
corners
|
72
|
+
end
|
73
|
+
|
74
|
+
# Departing from Redblob here
|
75
|
+
|
76
|
+
def point(hex, i, scale = 1.0 )
|
77
|
+
center = hex_to_pixel(hex)
|
78
|
+
|
79
|
+
resized = size.dup
|
80
|
+
resized.x = resized.x * scale
|
81
|
+
resized.y = resized.y * scale
|
82
|
+
|
83
|
+
offset = hex_corner_offset(i, resized)
|
84
|
+
|
85
|
+
Waxy::Geometry::Point.new( center.x + offset.x, center.y + offset.y )
|
86
|
+
end
|
87
|
+
|
88
|
+
def triangle(hex, i)
|
89
|
+
scale = hex.size[i]
|
90
|
+
|
91
|
+
points = [ hex_to_pixel(hex) ]
|
92
|
+
points.push point(hex, i, scale)
|
93
|
+
points.push point(hex, (i + 1 == 6 ? 0 : i + 1), scale)
|
94
|
+
points
|
95
|
+
end
|
96
|
+
|
97
|
+
def triangles(hex)
|
98
|
+
(0..5).each_with_index.collect{|t,i| triangle(hex, i) }
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Padding methods are not part of Amit's original code. They are not integrated into geometry compute.
|
103
|
+
#
|
104
|
+
|
105
|
+
def reset_padding
|
106
|
+
reset_h_padding
|
107
|
+
reset_w_padding
|
108
|
+
end
|
109
|
+
|
110
|
+
def reset_h_padding
|
111
|
+
@pad_h_offset = 0.0
|
112
|
+
end
|
113
|
+
|
114
|
+
def reset_w_padding
|
115
|
+
@pad_w_offset = 0.0
|
116
|
+
end
|
117
|
+
|
118
|
+
def increase_h_padding
|
119
|
+
@pad_h_offset += padding
|
120
|
+
end
|
121
|
+
|
122
|
+
def increase_w_padding
|
123
|
+
@pad_w_offset += padding
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Waxy
|
2
|
+
module Geometry
|
3
|
+
|
4
|
+
class Orientation
|
5
|
+
attr_accessor :f0, :f1, :f2, :f3,
|
6
|
+
:b0, :b1, :b2, :b3,
|
7
|
+
:start_angle
|
8
|
+
|
9
|
+
def initialize(_f0, _f1, _f2, _f3, _b0, _b1, _b2, _b3, _start_angle)
|
10
|
+
@f0 = _f0
|
11
|
+
@f1 = _f1
|
12
|
+
@f2 = _f2
|
13
|
+
@f3 = _f3
|
14
|
+
@b0 = _b0
|
15
|
+
@b1 = _b1
|
16
|
+
@b2 = _b2
|
17
|
+
@b3 = _b3
|
18
|
+
@start_angle = _start_angle
|
19
|
+
end
|
20
|
+
|
21
|
+
LAYOUT_POINTY = Orientation.new(
|
22
|
+
Math.sqrt(3.0),
|
23
|
+
Math.sqrt(3.0) / 2.0,
|
24
|
+
0.0,
|
25
|
+
3.0 / 2.0,
|
26
|
+
Math.sqrt(3.0) / 3.0,
|
27
|
+
-1.0 / 3.0,
|
28
|
+
0.0,
|
29
|
+
2.0 / 3.0,
|
30
|
+
0.5
|
31
|
+
)
|
32
|
+
|
33
|
+
LAYOUT_FLAT = Orientation.new(
|
34
|
+
3.0 / 2.0,
|
35
|
+
0.0,
|
36
|
+
Math.sqrt(3.0) / 2.0,
|
37
|
+
Math.sqrt(3.0),
|
38
|
+
2.0 / 3.0,
|
39
|
+
0.0,
|
40
|
+
-1.0 / 3.0,
|
41
|
+
Math.sqrt(3.0) / 3.0,
|
42
|
+
0.0
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Waxy
|
2
|
+
module Geometry
|
3
|
+
|
4
|
+
class Point
|
5
|
+
attr_accessor :x, :y
|
6
|
+
|
7
|
+
def initialize(_x, _y)
|
8
|
+
@x = _x
|
9
|
+
@y = _y
|
10
|
+
end
|
11
|
+
|
12
|
+
def hex_to_pixel(layout, h)
|
13
|
+
m = layout.orientation
|
14
|
+
x = (m.f0 * h.q + m.f1 * h.r) * layout.size.x
|
15
|
+
y = (m.f2 * h.q + m.f3 * h.r) * layout.size.y
|
16
|
+
return Point.new(x + layout.origin.x, y + layout.origin.y)
|
17
|
+
end
|
18
|
+
|
19
|
+
def hex_corner_offset(layout, corner)
|
20
|
+
size = Point.new(layout.size)
|
21
|
+
angle = 2.0 * M_PI * (layout.orientation.start_angle + corner ) / 6
|
22
|
+
Point.new(size.x * cos(angle), size.y * sin(angle))
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/waxy/meta.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Waxy
|
2
|
+
|
3
|
+
# Instances of Meta contain attributes
|
4
|
+
# that describe the visual appearance of a single hexagon.
|
5
|
+
class Meta
|
6
|
+
|
7
|
+
## -- Attributes for solid hex layouts
|
8
|
+
|
9
|
+
# Hex border SVG stroke
|
10
|
+
attr_accessor :stroke
|
11
|
+
|
12
|
+
# Hex fill
|
13
|
+
# not applicable to all templates
|
14
|
+
attr_accessor :fill
|
15
|
+
|
16
|
+
# a URL for the whole hexagon
|
17
|
+
# not applicable to all templates
|
18
|
+
attr_accessor :link
|
19
|
+
|
20
|
+
# title value for hex <a>
|
21
|
+
attr_accessor :link_title
|
22
|
+
|
23
|
+
## -- Attributes for "pie" layouts
|
24
|
+
|
25
|
+
# Array of 6 * (0.0..1.0)
|
26
|
+
# scales "pie" slices
|
27
|
+
attr_accessor :size
|
28
|
+
|
29
|
+
# An Array (0..5) of links, one for each slice of 'Pie'
|
30
|
+
attr_accessor :pie_links
|
31
|
+
|
32
|
+
# An Array (0..5) of SVG color values
|
33
|
+
attr_accessor :colors
|
34
|
+
|
35
|
+
# @return [Array]
|
36
|
+
# 6 strings with SVG color= values
|
37
|
+
def colors
|
38
|
+
@colors.nil? ? Waxy::COLORS.values : @colors
|
39
|
+
end
|
40
|
+
|
41
|
+
def pie_links
|
42
|
+
@pie_links.nil? ? [] : @pie_links
|
43
|
+
end
|
44
|
+
|
45
|
+
# Describes how "full" a pie hexagon is
|
46
|
+
def sum_size
|
47
|
+
size.sum
|
48
|
+
end
|
49
|
+
|
50
|
+
# Helper methods
|
51
|
+
|
52
|
+
def hex_link_start
|
53
|
+
return nil unless link
|
54
|
+
[
|
55
|
+
'<a href="' + link + '"',
|
56
|
+
'class="waxy__link"',
|
57
|
+
'>'
|
58
|
+
].compact.join(' ')
|
59
|
+
end
|
60
|
+
|
61
|
+
def hex_link_title
|
62
|
+
return nil unless link_title
|
63
|
+
"<title>#{link_title}</title>"
|
64
|
+
end
|
65
|
+
|
66
|
+
def hex_link_end
|
67
|
+
'</a>' if link
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/waxy/render.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'tilt'
|
3
|
+
|
4
|
+
require_relative 'render/svg'
|
5
|
+
require_relative 'render/canvas'
|
6
|
+
|
7
|
+
module Waxy
|
8
|
+
|
9
|
+
module Render
|
10
|
+
COLORS = {
|
11
|
+
1 => 'yellow',
|
12
|
+
2 => 'orange',
|
13
|
+
3 => 'red',
|
14
|
+
4 => 'purple',
|
15
|
+
5 => 'blue',
|
16
|
+
6 => 'green'
|
17
|
+
}
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|