geo_pattern 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +53 -28
- data/lib/geo_pattern.rb +4 -3
- data/lib/geo_pattern/pattern/base_pattern.rb +47 -0
- data/lib/geo_pattern/pattern/chevron_pattern.rb +45 -0
- data/lib/geo_pattern/pattern/concentric_circles_pattern.rb +47 -0
- data/lib/geo_pattern/pattern/diamond_pattern.rb +56 -0
- data/lib/geo_pattern/pattern/hexagon_pattern.rb +57 -0
- data/lib/geo_pattern/pattern/mosaic_squares_pattern.rb +76 -0
- data/lib/geo_pattern/pattern/nested_squares_pattern.rb +51 -0
- data/lib/geo_pattern/pattern/octagon_pattern.rb +35 -0
- data/lib/geo_pattern/pattern/overlapping_circles_pattern.rb +46 -0
- data/lib/geo_pattern/pattern/overlapping_rings_pattern.rb +46 -0
- data/lib/geo_pattern/pattern/pattern_helpers.rb +19 -0
- data/lib/geo_pattern/pattern/plaid_pattern.rb +49 -0
- data/lib/geo_pattern/pattern/plus_sign_pattern.rb +53 -0
- data/lib/geo_pattern/pattern/sine_wave_pattern.rb +36 -0
- data/lib/geo_pattern/pattern/square_pattern.rb +27 -0
- data/lib/geo_pattern/pattern/tessellation_pattern.rb +93 -0
- data/lib/geo_pattern/pattern/triangle_pattern.rb +51 -0
- data/lib/geo_pattern/pattern/xes_pattern.rb +58 -0
- data/lib/geo_pattern/pattern_generator.rb +105 -0
- data/lib/geo_pattern/svg.rb +4 -0
- data/lib/geo_pattern/version.rb +1 -1
- metadata +33 -23
- data/lib/geo_pattern/pattern.rb +0 -906
@@ -0,0 +1,76 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class MosaicSquaresPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
triangle_size = map(hex_val(0, 1), 0, 15, 15, 50)
|
5
|
+
|
6
|
+
svg.set_width(triangle_size * 8)
|
7
|
+
svg.set_height(triangle_size * 8)
|
8
|
+
|
9
|
+
i = 0
|
10
|
+
for y in 0..3
|
11
|
+
for x in 0..3
|
12
|
+
if x.even?
|
13
|
+
if y.even?
|
14
|
+
draw_outer_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, hex_val(i, 1))
|
15
|
+
else
|
16
|
+
draw_inner_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, [hex_val(i, 1), hex_val(i+1, 1)])
|
17
|
+
end
|
18
|
+
else
|
19
|
+
if y.even?
|
20
|
+
draw_inner_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, [hex_val(i, 1), hex_val(i+1, 1)])
|
21
|
+
else
|
22
|
+
draw_outer_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, hex_val(i, 1))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
i += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw_inner_mosaic_tile(x, y, triangle_size, vals)
|
31
|
+
triangle = build_right_triangle_shape(triangle_size)
|
32
|
+
opacity = opacity(vals[0])
|
33
|
+
fill = fill_color(vals[0])
|
34
|
+
styles = {
|
35
|
+
"stroke" => STROKE_COLOR,
|
36
|
+
"stroke-opacity" => STROKE_OPACITY,
|
37
|
+
"fill-opacity" => opacity,
|
38
|
+
"fill" => fill
|
39
|
+
}
|
40
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y}) scale(-1, 1)"}))
|
41
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y+triangle_size*2}) scale(1, -1)"}))
|
42
|
+
|
43
|
+
opacity = opacity(vals[1])
|
44
|
+
fill = fill_color(vals[1])
|
45
|
+
styles = {
|
46
|
+
"stroke" => STROKE_COLOR,
|
47
|
+
"stroke-opacity" => STROKE_OPACITY,
|
48
|
+
"fill-opacity" => opacity,
|
49
|
+
"fill" => fill
|
50
|
+
}
|
51
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y+triangle_size*2}) scale(-1, -1)"}))
|
52
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y}) scale(1, 1)"}))
|
53
|
+
end
|
54
|
+
|
55
|
+
def draw_outer_mosaic_tile(x, y, triangle_size, val)
|
56
|
+
opacity = opacity(val)
|
57
|
+
fill = fill_color(val)
|
58
|
+
triangle = build_right_triangle_shape(triangle_size)
|
59
|
+
styles = {
|
60
|
+
"stroke" => STROKE_COLOR,
|
61
|
+
"stroke-opacity" => STROKE_OPACITY,
|
62
|
+
"fill-opacity" => opacity,
|
63
|
+
"fill" => fill
|
64
|
+
}
|
65
|
+
|
66
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x}, #{y+triangle_size}) scale(1, -1)"}))
|
67
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size*2}, #{y+triangle_size}) scale(-1, -1)"}))
|
68
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x}, #{y+triangle_size}) scale(1, 1)"}))
|
69
|
+
svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size*2}, #{y+triangle_size}) scale(-1, 1)"}))
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_right_triangle_shape(side_length)
|
73
|
+
"0, 0, #{side_length}, #{side_length}, 0, #{side_length}, 0, 0"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class NestedSquaresPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
block_size = map(hex_val(0, 1), 0, 15, 4, 12)
|
5
|
+
square_size = block_size * 7
|
6
|
+
|
7
|
+
svg.set_width((square_size + block_size)*6 + block_size*6)
|
8
|
+
svg.set_height((square_size + block_size)*6 + block_size*6)
|
9
|
+
|
10
|
+
i = 0
|
11
|
+
for y in 0..5
|
12
|
+
for x in 0..5
|
13
|
+
val = hex_val(i, 1)
|
14
|
+
opacity = opacity(val)
|
15
|
+
fill = fill_color(val)
|
16
|
+
|
17
|
+
styles = {
|
18
|
+
"fill" => "none",
|
19
|
+
"stroke" => fill,
|
20
|
+
"style" => {
|
21
|
+
"opacity" => opacity,
|
22
|
+
"stroke-width" => "#{block_size}px"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
svg.rect(x*square_size + x*block_size*2 + block_size/2,
|
27
|
+
y*square_size + y*block_size*2 + block_size/2,
|
28
|
+
square_size, square_size, styles)
|
29
|
+
|
30
|
+
val = hex_val(39-i, 1)
|
31
|
+
opacity = opacity(val)
|
32
|
+
fill = fill_color(val)
|
33
|
+
|
34
|
+
styles = {
|
35
|
+
"fill" => "none",
|
36
|
+
"stroke" => fill,
|
37
|
+
"style" => {
|
38
|
+
"opacity" => opacity,
|
39
|
+
"stroke-width" => "#{block_size}px"
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
svg.rect(x*square_size + x*block_size*2 + block_size/2 + block_size*2,
|
44
|
+
y*square_size + y*block_size*2 + block_size/2 + block_size*2,
|
45
|
+
block_size * 3, block_size * 3, styles)
|
46
|
+
i += 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class OctagonPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
square_size = map(hex_val(0, 1), 0, 15, 10, 60)
|
5
|
+
tile = build_octogon_shape(square_size)
|
6
|
+
|
7
|
+
svg.set_width(square_size * 6)
|
8
|
+
svg.set_height(square_size * 6)
|
9
|
+
|
10
|
+
i = 0
|
11
|
+
for y in 0..5
|
12
|
+
for x in 0..5
|
13
|
+
val = hex_val(i, 1)
|
14
|
+
opacity = opacity(val)
|
15
|
+
fill = fill_color(val)
|
16
|
+
|
17
|
+
svg.polyline(tile, {
|
18
|
+
"fill" => fill,
|
19
|
+
"fill-opacity" => opacity,
|
20
|
+
"stroke" => STROKE_COLOR,
|
21
|
+
"stroke-opacity" => STROKE_OPACITY,
|
22
|
+
"transform" => "translate(#{x*square_size}, #{y*square_size})"
|
23
|
+
})
|
24
|
+
i += 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_octogon_shape(square_size)
|
30
|
+
s = square_size
|
31
|
+
c = s * 0.33
|
32
|
+
"#{c},0,#{s-c},0,#{s},#{c},#{s},#{s-c},#{s-c},#{s},#{c},#{s},0,#{s-c},0,#{c},#{c},0"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class OverlappingCirclesPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
scale = hex_val(0, 1)
|
5
|
+
diameter = map(scale, 0, 15, 25, 200)
|
6
|
+
radius = diameter/2;
|
7
|
+
|
8
|
+
svg.set_width(radius * 6)
|
9
|
+
svg.set_height(radius * 6)
|
10
|
+
|
11
|
+
i = 0
|
12
|
+
for y in 0..5
|
13
|
+
for x in 0..5
|
14
|
+
val = hex_val(i, 1)
|
15
|
+
opacity = opacity(val)
|
16
|
+
fill = fill_color(val)
|
17
|
+
|
18
|
+
styles = {
|
19
|
+
"fill" => fill,
|
20
|
+
"style" => {
|
21
|
+
"opacity" => opacity
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
svg.circle(x*radius, y*radius, radius, styles)
|
26
|
+
|
27
|
+
# Add an extra one at top-right, for tiling.
|
28
|
+
if (x == 0)
|
29
|
+
svg.circle(6*radius, y*radius, radius, styles)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add an extra row at the end that matches the first row, for tiling.
|
33
|
+
if (y == 0)
|
34
|
+
svg.circle(x*radius, 6*radius, radius, styles)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add an extra one at bottom-right, for tiling.
|
38
|
+
if (x == 0 and y == 0)
|
39
|
+
svg.circle(6*radius, 6*radius, radius, styles)
|
40
|
+
end
|
41
|
+
i += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class OverlappingRingsPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
scale = hex_val(0, 1)
|
5
|
+
ring_size = map(scale, 0, 15, 10, 60)
|
6
|
+
stroke_width = ring_size / 4
|
7
|
+
|
8
|
+
svg.set_width(ring_size * 6)
|
9
|
+
svg.set_height(ring_size * 6)
|
10
|
+
|
11
|
+
i = 0
|
12
|
+
for y in 0..5
|
13
|
+
for x in 0..5
|
14
|
+
val = hex_val(i, 1)
|
15
|
+
opacity = opacity(val)
|
16
|
+
fill = fill_color(val)
|
17
|
+
|
18
|
+
styles = {
|
19
|
+
"fill" => "none",
|
20
|
+
"stroke" => fill,
|
21
|
+
"style" => {
|
22
|
+
"opacity" => opacity,
|
23
|
+
"stroke-width" => "#{stroke_width}px"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
svg.circle(x*ring_size, y*ring_size, ring_size - stroke_width/2, styles)
|
28
|
+
|
29
|
+
# Add an extra one at top-right, for tiling.
|
30
|
+
if (x == 0)
|
31
|
+
svg.circle(6*ring_size, y*ring_size, ring_size - stroke_width/2, styles)
|
32
|
+
end
|
33
|
+
|
34
|
+
if (y == 0)
|
35
|
+
svg.circle(x*ring_size, 6*ring_size, ring_size - stroke_width/2, styles)
|
36
|
+
end
|
37
|
+
|
38
|
+
if (x == 0 and y == 0)
|
39
|
+
svg.circle(6*ring_size, 6*ring_size, ring_size - stroke_width/2, styles)
|
40
|
+
end
|
41
|
+
i += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '**', '*.rb')].each { |file| require file }
|
2
|
+
|
3
|
+
module GeoPattern
|
4
|
+
module PatternHelpers
|
5
|
+
def self.hex_val(hash, index, length)
|
6
|
+
hash[index, length || 1].to_i(16)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Ruby implementation of Processing's map function
|
10
|
+
# http://processing.org/reference/map_.html
|
11
|
+
def self.map(value, v_min, v_max, d_min, d_max) # v for value, d for desired
|
12
|
+
v_value = value.to_f # so it returns float
|
13
|
+
|
14
|
+
v_range = v_max - v_min
|
15
|
+
d_range = d_max - d_min
|
16
|
+
(v_value - v_min) * d_range / v_range + d_min
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class PlaidPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
height = 0
|
5
|
+
width = 0
|
6
|
+
|
7
|
+
# horizontal stripes
|
8
|
+
i = 0
|
9
|
+
18.times do
|
10
|
+
space = hex_val(i, 1)
|
11
|
+
height += space + 5
|
12
|
+
|
13
|
+
val = hex_val(i+1, 1)
|
14
|
+
opacity = opacity(val)
|
15
|
+
fill = fill_color(val)
|
16
|
+
stripe_height = val + 5
|
17
|
+
|
18
|
+
svg.rect(0, height, "100%", stripe_height, {
|
19
|
+
"opacity" => opacity,
|
20
|
+
"fill" => fill
|
21
|
+
})
|
22
|
+
height += stripe_height
|
23
|
+
i += 2
|
24
|
+
end
|
25
|
+
|
26
|
+
# vertical stripes
|
27
|
+
i = 0
|
28
|
+
18.times do
|
29
|
+
space = hex_val(i, 1)
|
30
|
+
width += space + 5
|
31
|
+
|
32
|
+
val = hex_val(i+1, 1)
|
33
|
+
opacity = opacity(val)
|
34
|
+
fill = fill_color(val)
|
35
|
+
stripe_width = val + 5
|
36
|
+
|
37
|
+
svg.rect(width, 0, stripe_width, "100%", {
|
38
|
+
"opacity" => opacity,
|
39
|
+
"fill" => fill
|
40
|
+
})
|
41
|
+
width += stripe_width
|
42
|
+
i += 2
|
43
|
+
end
|
44
|
+
|
45
|
+
svg.set_width(width)
|
46
|
+
svg.set_height(height)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class PlusSignPattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
square_size = map(hex_val(0, 1), 0, 15, 10, 25)
|
5
|
+
plus_size = square_size * 3
|
6
|
+
plus_shape = build_plus_shape(square_size)
|
7
|
+
|
8
|
+
svg.set_width(square_size * 12)
|
9
|
+
svg.set_height(square_size * 12)
|
10
|
+
|
11
|
+
i = 0
|
12
|
+
for y in 0..5
|
13
|
+
for x in 0..5
|
14
|
+
val = hex_val(i, 1)
|
15
|
+
opacity = opacity(val)
|
16
|
+
fill = fill_color(val)
|
17
|
+
dx = (y % 2 == 0) ? 0 : 1
|
18
|
+
|
19
|
+
styles = {
|
20
|
+
"fill" => fill,
|
21
|
+
"stroke" => STROKE_COLOR,
|
22
|
+
"stroke-opacity" => STROKE_OPACITY,
|
23
|
+
"style" => {
|
24
|
+
"fill-opacity" => opacity
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
svg.group(plus_shape, styles.merge({
|
29
|
+
"transform" => "translate(#{x*plus_size - x*square_size + dx*square_size - square_size},#{y*plus_size - y*square_size - plus_size/2})"}))
|
30
|
+
|
31
|
+
# Add an extra column on the right for tiling.
|
32
|
+
if (x == 0)
|
33
|
+
svg.group(plus_shape, styles.merge({
|
34
|
+
"transform" => "translate(#{4*plus_size - x*square_size + dx*square_size - square_size},#{y*plus_size - y*square_size - plus_size/2})"}))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add an extra row on the bottom that matches the first row, for tiling.
|
38
|
+
if (y == 0)
|
39
|
+
svg.group(plus_shape, styles.merge({
|
40
|
+
"transform" => "translate(#{x*plus_size - x*square_size + dx*square_size - square_size},#{4*plus_size - y*square_size - plus_size/2})"}))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add an extra one at top-right and bottom-right, for tiling.
|
44
|
+
if (x == 0 && y == 0)
|
45
|
+
svg.group(plus_shape, styles.merge({
|
46
|
+
"transform" => "translate(#{4*plus_size - x*square_size + dx*square_size - square_size},#{4*plus_size - y*square_size - plus_size/2})"}))
|
47
|
+
end
|
48
|
+
i += 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class SineWavePattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
period = map(hex_val(0, 1), 0, 15, 100, 400).floor
|
5
|
+
amplitude = map(hex_val(1, 1), 0, 15, 30, 100).floor
|
6
|
+
wave_width = map(hex_val(2, 1), 0, 15, 3, 30).floor
|
7
|
+
|
8
|
+
svg.set_width(period)
|
9
|
+
svg.set_height(wave_width * 36)
|
10
|
+
|
11
|
+
for i in 0..35
|
12
|
+
val = hex_val(i, 1)
|
13
|
+
opacity = opacity(val)
|
14
|
+
fill = fill_color(val)
|
15
|
+
x_offset = period / 4 * 0.7
|
16
|
+
|
17
|
+
styles = {
|
18
|
+
"fill" => "none",
|
19
|
+
"stroke" => fill,
|
20
|
+
"style" => {
|
21
|
+
"opacity" => opacity,
|
22
|
+
"stroke-width" => "#{wave_width}px"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
str = "M0 "+amplitude.to_s+
|
27
|
+
" C "+x_offset.to_s+" 0, "+(period/2 - x_offset).to_s+" 0, "+(period/2).to_s+" "+amplitude.to_s+
|
28
|
+
" S "+(period-x_offset).to_s+" "+(amplitude*2).to_s+", "+period.to_s+" "+amplitude.to_s+
|
29
|
+
" S "+(period*1.5-x_offset).to_s+" 0, "+(period*1.5).to_s+", "+amplitude.to_s;
|
30
|
+
|
31
|
+
svg.path(str, styles.merge({"transform" => "translate(-#{period/4}, #{wave_width*i-amplitude*1.5})"}))
|
32
|
+
svg.path(str, styles.merge({"transform" => "translate(-#{period/4}, #{wave_width*i-amplitude*1.5 + wave_width*36})"}))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module GeoPattern
|
2
|
+
class SquarePattern < BasePattern
|
3
|
+
def render_to_svg
|
4
|
+
square_size = map(hex_val(0, 1), 0, 15, 10, 60)
|
5
|
+
|
6
|
+
svg.set_width(square_size * 6)
|
7
|
+
svg.set_height(square_size * 6)
|
8
|
+
|
9
|
+
i = 0
|
10
|
+
for y in 0..5
|
11
|
+
for x in 0..5
|
12
|
+
val = hex_val(i, 1)
|
13
|
+
opacity = opacity(val)
|
14
|
+
fill = fill_color(val)
|
15
|
+
|
16
|
+
svg.rect(x*square_size, y*square_size, square_size, square_size, {
|
17
|
+
"fill" => fill,
|
18
|
+
"fill-opacity" => opacity,
|
19
|
+
"stroke" => STROKE_COLOR,
|
20
|
+
"stroke-opacity" => STROKE_OPACITY
|
21
|
+
})
|
22
|
+
i += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|