geo_pattern 1.3.2 → 1.4.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +58 -0
  3. data/.simplecov +25 -0
  4. data/.travis.yml +12 -0
  5. data/Gemfile +3 -1
  6. data/README.md +125 -23
  7. data/Rakefile +72 -9
  8. data/fixtures/generated_patterns/chevrons.svg +1 -0
  9. data/fixtures/generated_patterns/concentric_circles.svg +1 -0
  10. data/fixtures/generated_patterns/diamonds.svg +1 -0
  11. data/fixtures/generated_patterns/diamonds_with_base_color.svg +1 -0
  12. data/fixtures/generated_patterns/diamonds_with_color.svg +1 -0
  13. data/fixtures/generated_patterns/hexagons.svg +1 -0
  14. data/fixtures/generated_patterns/mosaic_squares.svg +1 -0
  15. data/fixtures/generated_patterns/nested_squares.svg +1 -0
  16. data/fixtures/generated_patterns/octagons.svg +1 -0
  17. data/fixtures/generated_patterns/overlapping_circles.svg +1 -0
  18. data/fixtures/generated_patterns/overlapping_rings.svg +1 -0
  19. data/fixtures/generated_patterns/plaid.svg +1 -0
  20. data/fixtures/generated_patterns/plus_signs.svg +1 -0
  21. data/fixtures/generated_patterns/sine_waves.svg +1 -0
  22. data/fixtures/generated_patterns/squares.svg +1 -0
  23. data/fixtures/generated_patterns/tessellation.svg +1 -0
  24. data/fixtures/generated_patterns/triangles.svg +1 -0
  25. data/fixtures/generated_patterns/xes.svg +1 -0
  26. data/geo_pattern.gemspec +8 -2
  27. data/lib/geo_pattern.rb +42 -20
  28. data/lib/geo_pattern/background.rb +25 -0
  29. data/lib/geo_pattern/background_generators/solid_generator.rb +52 -0
  30. data/lib/geo_pattern/color.rb +25 -0
  31. data/lib/geo_pattern/color_generators/base_color_generator.rb +55 -0
  32. data/lib/geo_pattern/color_generators/simple_generator.rb +27 -0
  33. data/lib/geo_pattern/color_preset.rb +26 -0
  34. data/lib/geo_pattern/errors.rb +7 -0
  35. data/lib/geo_pattern/geo_pattern_task.rb +59 -0
  36. data/lib/geo_pattern/helpers.rb +47 -0
  37. data/lib/geo_pattern/pattern.rb +84 -0
  38. data/lib/geo_pattern/pattern_generator.rb +33 -82
  39. data/lib/geo_pattern/pattern_helpers.rb +31 -2
  40. data/lib/geo_pattern/pattern_preset.rb +23 -0
  41. data/lib/geo_pattern/pattern_sieve.rb +36 -0
  42. data/lib/geo_pattern/pattern_store.rb +63 -0
  43. data/lib/geo_pattern/pattern_validator.rb +27 -0
  44. data/lib/geo_pattern/rake_task.rb +109 -0
  45. data/lib/geo_pattern/roles/comparable_metadata.rb +35 -0
  46. data/lib/geo_pattern/roles/named_generator.rb +13 -0
  47. data/lib/geo_pattern/seed.rb +21 -0
  48. data/lib/geo_pattern/structure.rb +25 -0
  49. data/lib/geo_pattern/structure_generators/base_generator.rb +85 -0
  50. data/lib/geo_pattern/structure_generators/chevrons_generator.rb +55 -0
  51. data/lib/geo_pattern/structure_generators/concentric_circles_generator.rb +56 -0
  52. data/lib/geo_pattern/structure_generators/diamonds_generator.rb +66 -0
  53. data/lib/geo_pattern/structure_generators/hexagons_generator.rb +67 -0
  54. data/lib/geo_pattern/structure_generators/mosaic_squares_generator.rb +85 -0
  55. data/lib/geo_pattern/structure_generators/nested_squares_generator.rb +60 -0
  56. data/lib/geo_pattern/structure_generators/octagons_generator.rb +44 -0
  57. data/lib/geo_pattern/structure_generators/overlapping_circles_generator.rb +55 -0
  58. data/lib/geo_pattern/structure_generators/overlapping_rings_generator.rb +55 -0
  59. data/lib/geo_pattern/structure_generators/plaid_generator.rb +55 -0
  60. data/lib/geo_pattern/structure_generators/plus_signs_generator.rb +62 -0
  61. data/lib/geo_pattern/structure_generators/sine_waves_generator.rb +43 -0
  62. data/lib/geo_pattern/structure_generators/squares_generator.rb +36 -0
  63. data/lib/geo_pattern/structure_generators/tessellation_generator.rb +103 -0
  64. data/lib/geo_pattern/structure_generators/triangles_generator.rb +61 -0
  65. data/lib/geo_pattern/structure_generators/xes_generator.rb +67 -0
  66. data/lib/geo_pattern/svg_image.rb +101 -0
  67. data/lib/geo_pattern/version.rb +1 -1
  68. data/script/bootstrap +30 -0
  69. data/script/console +8 -0
  70. data/script/test +21 -0
  71. data/spec/background_generators/solid_generator_spec.rb +50 -0
  72. data/spec/background_spec.rb +25 -0
  73. data/spec/color_generators/base_color_generator_spec.rb +31 -0
  74. data/spec/color_generators/simple_generator_spec.rb +12 -0
  75. data/spec/color_preset_spec.rb +53 -0
  76. data/spec/color_spec.rb +15 -0
  77. data/spec/geo_pattern_spec.rb +95 -24
  78. data/spec/helpers_spec.rb +65 -0
  79. data/spec/pattern_preset_spec.rb +41 -0
  80. data/spec/pattern_sieve_spec.rb +66 -0
  81. data/spec/pattern_spec.rb +72 -0
  82. data/spec/pattern_store_spec.rb +47 -0
  83. data/spec/pattern_validator_spec.rb +28 -0
  84. data/spec/seed_spec.rb +14 -0
  85. data/spec/spec_helper.rb +1 -6
  86. data/spec/structure_generators/chevrons_generator_spec.rb +5 -0
  87. data/spec/structure_generators/concentric_circles_generator_spec.rb +5 -0
  88. data/spec/structure_generators/diamonds_generator_spec.rb +5 -0
  89. data/spec/structure_generators/hexagons_generator_spec.rb +5 -0
  90. data/spec/structure_generators/mosaic_squares_generator_spec.rb +5 -0
  91. data/spec/structure_generators/nested_squares_generator_spec.rb +5 -0
  92. data/spec/structure_generators/octagons_generator_spec.rb +5 -0
  93. data/spec/structure_generators/overlapping_circles_generator_spec.rb +5 -0
  94. data/spec/structure_generators/overlapping_rings_generator_spec.rb +5 -0
  95. data/spec/structure_generators/plaid_generator_spec.rb +5 -0
  96. data/spec/structure_generators/plus_signs_generator_spec.rb +5 -0
  97. data/spec/structure_generators/sine_waves_generator_spec.rb +5 -0
  98. data/spec/structure_generators/squares_generator_spec.rb +5 -0
  99. data/spec/structure_generators/tessellation_generator_spec.rb +5 -0
  100. data/spec/structure_generators/triangles_generator_spec.rb +5 -0
  101. data/spec/structure_generators/xes_generator_spec.rb +5 -0
  102. data/spec/structure_spec.rb +38 -0
  103. data/spec/support/helpers/fixtures.rb +12 -0
  104. data/spec/support/kernel.rb +40 -0
  105. data/spec/support/matchers/image.rb +17 -0
  106. data/spec/support/matchers/name.rb +15 -0
  107. data/spec/support/rspec.rb +1 -1
  108. data/spec/support/shared_examples/generator.rb +46 -0
  109. data/spec/support/shared_examples/pattern.rb +31 -0
  110. data/spec/support/shared_examples/pattern_name.rb +7 -0
  111. data/spec/support/shared_examples/structure.rb +48 -0
  112. data/spec/svg_spec.rb +3 -3
  113. metadata +141 -25
  114. data/lib/geo_pattern/pattern/base_pattern.rb +0 -47
  115. data/lib/geo_pattern/pattern/chevron_pattern.rb +0 -45
  116. data/lib/geo_pattern/pattern/concentric_circles_pattern.rb +0 -47
  117. data/lib/geo_pattern/pattern/diamond_pattern.rb +0 -56
  118. data/lib/geo_pattern/pattern/hexagon_pattern.rb +0 -57
  119. data/lib/geo_pattern/pattern/mosaic_squares_pattern.rb +0 -76
  120. data/lib/geo_pattern/pattern/nested_squares_pattern.rb +0 -51
  121. data/lib/geo_pattern/pattern/octagon_pattern.rb +0 -35
  122. data/lib/geo_pattern/pattern/overlapping_circles_pattern.rb +0 -46
  123. data/lib/geo_pattern/pattern/overlapping_rings_pattern.rb +0 -46
  124. data/lib/geo_pattern/pattern/plaid_pattern.rb +0 -49
  125. data/lib/geo_pattern/pattern/plus_sign_pattern.rb +0 -53
  126. data/lib/geo_pattern/pattern/sine_wave_pattern.rb +0 -36
  127. data/lib/geo_pattern/pattern/square_pattern.rb +0 -27
  128. data/lib/geo_pattern/pattern/tessellation_pattern.rb +0 -93
  129. data/lib/geo_pattern/pattern/triangle_pattern.rb +0 -51
  130. data/lib/geo_pattern/pattern/xes_pattern.rb +0 -58
  131. data/lib/geo_pattern/svg.rb +0 -77
  132. data/spec/support/helpers.rb +0 -34
@@ -0,0 +1,35 @@
1
+ module GeoPattern
2
+ # Roles
3
+ module Roles
4
+ # A comparable metadata
5
+ module ComparableMetadata
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def generator?(value)
11
+ return false unless value.is_a?(Class) || value.nil?
12
+ return true if value.nil? && @generator
13
+
14
+ return true if value == generator
15
+
16
+ false
17
+ end
18
+
19
+ # Class Methods
20
+ module ClassMethods
21
+ # Define comparators
22
+ def def_comparators(*methods)
23
+ methods.flatten.each do |m|
24
+ define_method "#{m}?".to_sym do |value|
25
+ return true if value.nil? && public_send(m)
26
+ return true if value == public_send(m)
27
+
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module GeoPattern
2
+ module Roles
3
+ module NamedGenerator
4
+ def name?(other_name)
5
+ name == other_name.to_sym
6
+ end
7
+
8
+ def name
9
+ Helpers.underscore(Helpers.demodulize(self.class).gsub(/Generator/, '')).to_sym
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module GeoPattern
2
+ class Seed
3
+ private
4
+
5
+ attr_reader :seed
6
+
7
+ public
8
+
9
+ def initialize(string)
10
+ @seed = Digest::SHA1.hexdigest string.to_s
11
+ end
12
+
13
+ def [](*args)
14
+ seed[*args]
15
+ end
16
+
17
+ def to_i(index, length)
18
+ seed[index, length || 1].to_i(16)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module GeoPattern
2
+ class Structure
3
+ include Roles::ComparableMetadata
4
+
5
+ extend Forwardable
6
+
7
+ attr_reader :image, :preset, :name, :generator
8
+
9
+ def_delegators :@preset, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max
10
+
11
+ def initialize(options)
12
+ @image = options[:image]
13
+ @preset = options[:preset]
14
+ @name = options[:name]
15
+ @generator = options[:generator]
16
+
17
+ fail ArgumentError, 'Argument name is missing' if @name.nil?
18
+ fail ArgumentError, 'Argument image is missing' if @image.nil?
19
+ fail ArgumentError, 'Argument preset is missing' if @preset.nil?
20
+ fail ArgumentError, 'Argument generator is missing' if @generator.nil?
21
+ end
22
+
23
+ def_comparators :name, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max
24
+ end
25
+ end
@@ -0,0 +1,85 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class BaseGenerator
4
+ include Roles::NamedGenerator
5
+
6
+ private
7
+
8
+ attr_reader :svg, :seed, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max, :preset
9
+ attr_accessor :height, :width
10
+
11
+ public
12
+
13
+ def initialize(seed, preset, svg = SvgImage.new)
14
+ @svg = svg
15
+ @seed = seed
16
+ @preset = preset
17
+
18
+ @fill_color_dark = @preset.fill_color_dark
19
+ @fill_color_light = @preset.fill_color_light
20
+ @stroke_color = @preset.stroke_color
21
+ @stroke_opacity = @preset.stroke_opacity
22
+ @opacity_min = @preset.opacity_min
23
+ @opacity_max = @preset.opacity_max
24
+
25
+ @height = 100
26
+ @width = 100
27
+
28
+ after_initialize
29
+ end
30
+
31
+ def generate(pattern)
32
+ pattern.structure = Structure.new(image: svg_image, preset: preset, generator: self.class, name: name)
33
+
34
+ pattern.height = height
35
+ pattern.width = width
36
+
37
+ self
38
+ end
39
+
40
+ private
41
+
42
+ # Set Svg Image
43
+ def svg_image
44
+ image = generate_structure
45
+ image.height = height
46
+ image.width = width
47
+
48
+ image
49
+ end
50
+
51
+ # Hook for generators
52
+ def after_initialize; end
53
+
54
+ # Generate the structure
55
+ def generate_structure
56
+ fail NotImplementedError
57
+ end
58
+
59
+ def hex_val(index, len)
60
+ seed.to_i(index, len)
61
+ end
62
+
63
+ def fill_color(val)
64
+ (val.even?) ? fill_color_light : fill_color_dark
65
+ end
66
+
67
+ def opacity(val)
68
+ map(val, 0, 15, opacity_min, opacity_max)
69
+ end
70
+
71
+ def map(value, v_min, v_max, d_min, d_max)
72
+ PatternHelpers.map(value, v_min, v_max, d_min, d_max)
73
+ end
74
+
75
+ protected
76
+
77
+ def build_plus_shape(square_size)
78
+ [
79
+ "rect(#{square_size},0,#{square_size},#{square_size * 3})",
80
+ "rect(0, #{square_size},#{square_size * 3},#{square_size})"
81
+ ]
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,55 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class ChevronsGenerator < BaseGenerator
4
+ private
5
+
6
+ attr_reader :chevron_height, :chevron_width, :chevron
7
+
8
+ def after_initialize
9
+ @chevron_width = map(hex_val(0, 1), 0, 15, 30, 80)
10
+ @chevron_height = map(hex_val(0, 1), 0, 15, 30, 80)
11
+ @chevron = build_chevron_shape(chevron_width, chevron_height)
12
+
13
+ self.height = chevron_height * 6 * 0.66
14
+ self.width = chevron_width * 6
15
+ end
16
+
17
+ def generate_structure
18
+ i = 0
19
+ for y in 0..5
20
+ for x in 0..5
21
+ val = hex_val(i, 1)
22
+ opacity = opacity(val)
23
+ fill = fill_color(val)
24
+
25
+ styles = {
26
+ 'stroke' => stroke_color,
27
+ 'stroke-opacity' => stroke_opacity,
28
+ 'fill' => fill,
29
+ 'fill-opacity' => opacity,
30
+ 'stroke-width' => 1
31
+ }
32
+
33
+ svg.group(chevron, styles.merge('transform' => "translate(#{x * chevron_width},#{y * chevron_height * 0.66 - chevron_height / 2})"))
34
+
35
+ # Add an extra row at the end that matches the first row, for tiling.
36
+ if (y == 0)
37
+ svg.group(chevron, styles.merge('transform' => "translate(#{x * chevron_width},#{6 * chevron_height * 0.66 - chevron_height / 2})"))
38
+ end
39
+ i += 1
40
+ end
41
+ end
42
+
43
+ svg
44
+ end
45
+
46
+ def build_chevron_shape(width, height)
47
+ e = height * 0.66
48
+ [
49
+ %{polyline("0,0,#{width / 2},#{height - e},#{width / 2},#{height},0,#{e},0,0")},
50
+ %{polyline("#{width / 2},#{height - e},#{width},0,#{width},#{e},#{width / 2},#{height},#{width / 2},#{height - e}")}
51
+ ]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class ConcentricCirclesGenerator < BaseGenerator
4
+ private
5
+
6
+ attr_reader :scale, :ring_size, :stroke_width
7
+
8
+ def after_initialize
9
+ @scale = hex_val(0, 1)
10
+ @ring_size = map(scale, 0, 15, 10, 60)
11
+ @stroke_width = ring_size / 5
12
+
13
+ self.width = self.height = (ring_size + stroke_width) * 6
14
+ end
15
+
16
+ def generate_structure
17
+ i = 0
18
+ for y in 0..5
19
+ for x in 0..5
20
+ val = hex_val(i, 1)
21
+ opacity = opacity(val)
22
+ fill = fill_color(val)
23
+
24
+ svg.circle(
25
+ x * ring_size + x * stroke_width + (ring_size + stroke_width) / 2,
26
+ y * ring_size + y * stroke_width + (ring_size + stroke_width) / 2,
27
+ ring_size / 2,
28
+ 'fill' => 'none',
29
+ 'stroke' => fill,
30
+ 'style' => {
31
+ 'opacity' => opacity,
32
+ 'stroke-width' => "#{stroke_width}px"
33
+ }
34
+ )
35
+
36
+ val = hex_val(39 - i, 1)
37
+ opacity = opacity(val)
38
+ fill = fill_color(val)
39
+
40
+ svg.circle(
41
+ x * ring_size + x * stroke_width + (ring_size + stroke_width) / 2,
42
+ y * ring_size + y * stroke_width + (ring_size + stroke_width) / 2,
43
+ ring_size / 4,
44
+ 'fill' => fill,
45
+ 'fill-opacity' => opacity
46
+ )
47
+
48
+ i += 1
49
+ end
50
+ end
51
+
52
+ svg
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class DiamondsGenerator < BaseGenerator
4
+ private
5
+
6
+ attr_reader :diamond_height, :diamond_width, :diamond
7
+
8
+ def after_initialize
9
+ @diamond_width = map(hex_val(0, 1), 0, 15, 10, 50)
10
+ @diamond_height = map(hex_val(1, 1), 0, 15, 10, 50)
11
+ @diamond = build_diamond_shape(diamond_width, diamond_height)
12
+
13
+ self.height = diamond_height * 3
14
+ self.width = diamond_width * 6
15
+ end
16
+
17
+ def generate_structure
18
+ i = 0
19
+ for y in 0..5
20
+ for x in 0..5
21
+ val = hex_val(i, 1)
22
+ opacity = opacity(val)
23
+ fill = fill_color(val)
24
+
25
+ styles = {
26
+ 'fill' => fill,
27
+ 'fill-opacity' => opacity,
28
+ 'stroke' => stroke_color,
29
+ 'stroke-opacity' => stroke_opacity
30
+ }
31
+
32
+ dx = (y % 2 == 0) ? 0 : diamond_width / 2
33
+
34
+ svg.polyline(diamond, styles.merge(
35
+ 'transform' => "translate(#{x * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * y - diamond_height / 2})"))
36
+
37
+ # Add an extra one at top-right, for tiling.
38
+ if (x == 0)
39
+ svg.polyline(diamond, styles.merge(
40
+ 'transform' => "translate(#{6 * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * y - diamond_height / 2})"))
41
+ end
42
+
43
+ # Add an extra row at the end that matches the first row, for tiling.
44
+ if (y == 0)
45
+ svg.polyline(diamond, styles.merge(
46
+ 'transform' => "translate(#{x * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * 6 - diamond_height / 2})"))
47
+ end
48
+
49
+ # Add an extra one at bottom-right, for tiling.
50
+ if x == 0 && y == 0
51
+ svg.polyline(diamond, styles.merge(
52
+ 'transform' => "translate(#{6 * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * 6 - diamond_height / 2})"))
53
+ end
54
+ i += 1
55
+ end
56
+ end
57
+
58
+ svg
59
+ end
60
+
61
+ def build_diamond_shape(width, height)
62
+ "#{width / 2}, 0, #{width}, #{height / 2}, #{width / 2}, #{height}, 0, #{height / 2}"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class HexagonsGenerator < BaseGenerator
4
+ private
5
+
6
+ attr_reader :scale, :side_length, :hex_height, :hex_width, :hex
7
+
8
+ def after_initialize
9
+ @scale = hex_val(0, 1)
10
+ @side_length = map(scale, 0, 15, 8, 60)
11
+ @hex_height = side_length * Math.sqrt(3)
12
+ @hex_width = side_length * 2
13
+ @hex = build_hexagon_shape(side_length)
14
+
15
+ self.width = hex_width * 3 + side_length * 3
16
+ self.height = hex_height * 6
17
+ end
18
+
19
+ def generate_structure
20
+ i = 0
21
+ for y in 0..5
22
+ for x in 0..5
23
+ val = hex_val(i, 1)
24
+ dy = x % 2 == 0 ? y * hex_height : y * hex_height + hex_height / 2
25
+ opacity = opacity(val)
26
+ fill = fill_color(val)
27
+
28
+ styles = {
29
+ 'fill' => fill,
30
+ 'fill-opacity' => opacity,
31
+ 'stroke' => stroke_color,
32
+ 'stroke-opacity' => stroke_opacity
33
+ }
34
+
35
+ svg.polyline(hex, styles.merge('transform' => "translate(#{x * side_length * 1.5 - hex_width / 2}, #{dy - hex_height / 2})"))
36
+
37
+ # Add an extra one at top-right, for tiling.
38
+ if (x == 0)
39
+ svg.polyline(hex, styles.merge('transform' => "translate(#{6 * side_length * 1.5 - hex_width / 2}, #{dy - hex_height / 2})"))
40
+ end
41
+
42
+ # Add an extra row at the end that matches the first row, for tiling.
43
+ if (y == 0)
44
+ dy = x % 2 == 0 ? 6 * hex_height : 6 * hex_height + hex_height / 2
45
+ svg.polyline(hex, styles.merge('transform' => "translate(#{x * side_length * 1.5 - hex_width / 2}, #{dy - hex_height / 2})"))
46
+ end
47
+
48
+ # Add an extra one at bottom-right, for tiling.
49
+ if x == 0 && y == 0
50
+ svg.polyline(hex, styles.merge('transform' => "translate(#{6 * side_length * 1.5 - hex_width / 2}, #{5 * hex_height + hex_height / 2})"))
51
+ end
52
+ i += 1
53
+ end
54
+ end
55
+
56
+ svg
57
+ end
58
+
59
+ def build_hexagon_shape(sideLength)
60
+ c = sideLength
61
+ a = c / 2
62
+ b = Math.sin(60 * Math::PI / 180) * c
63
+ "0,#{b},#{a},0,#{a + c},0,#{2 * c},#{b},#{a + c},#{2 * b},#{a},#{2 * b},0,#{b}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,85 @@
1
+ module GeoPattern
2
+ module StructureGenerators
3
+ class MosaicSquaresGenerator < BaseGenerator
4
+ private
5
+
6
+ attr_reader :triangle_size
7
+
8
+ def after_initialize
9
+ @triangle_size = map(hex_val(0, 1), 0, 15, 15, 50)
10
+
11
+ self.height = self.width = triangle_size * 8
12
+ end
13
+
14
+ def generate_structure
15
+ i = 0
16
+ for y in 0..3
17
+ for x in 0..3
18
+ if x.even?
19
+ if y.even?
20
+ draw_outer_mosaic_tile(x * triangle_size * 2, y * triangle_size * 2, triangle_size, hex_val(i, 1))
21
+ else
22
+ draw_inner_mosaic_tile(x * triangle_size * 2, y * triangle_size * 2, triangle_size, [hex_val(i, 1), hex_val(i + 1, 1)])
23
+ end
24
+ else
25
+ if y.even?
26
+ draw_inner_mosaic_tile(x * triangle_size * 2, y * triangle_size * 2, triangle_size, [hex_val(i, 1), hex_val(i + 1, 1)])
27
+ else
28
+ draw_outer_mosaic_tile(x * triangle_size * 2, y * triangle_size * 2, triangle_size, hex_val(i, 1))
29
+ end
30
+ end
31
+ i += 1
32
+ end
33
+ end
34
+
35
+ svg
36
+ end
37
+
38
+ def draw_inner_mosaic_tile(x, y, triangle_size, vals)
39
+ triangle = build_right_triangle_shape(triangle_size)
40
+ opacity = opacity(vals[0])
41
+ fill = fill_color(vals[0])
42
+ styles = {
43
+ 'stroke' => stroke_color,
44
+ 'stroke-opacity' => stroke_opacity,
45
+ 'fill-opacity' => opacity,
46
+ 'fill' => fill
47
+ }
48
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size}, #{y}) scale(-1, 1)"))
49
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size}, #{y + triangle_size * 2}) scale(1, -1)"))
50
+
51
+ opacity = opacity(vals[1])
52
+ fill = fill_color(vals[1])
53
+ styles = {
54
+ 'stroke' => stroke_color,
55
+ 'stroke-opacity' => stroke_opacity,
56
+ 'fill-opacity' => opacity,
57
+ 'fill' => fill
58
+ }
59
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size}, #{y + triangle_size * 2}) scale(-1, -1)"))
60
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size}, #{y}) scale(1, 1)"))
61
+ end
62
+
63
+ def draw_outer_mosaic_tile(x, y, triangle_size, val)
64
+ opacity = opacity(val)
65
+ fill = fill_color(val)
66
+ triangle = build_right_triangle_shape(triangle_size)
67
+ styles = {
68
+ 'stroke' => stroke_color,
69
+ 'stroke-opacity' => stroke_opacity,
70
+ 'fill-opacity' => opacity,
71
+ 'fill' => fill
72
+ }
73
+
74
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x}, #{y + triangle_size}) scale(1, -1)"))
75
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size * 2}, #{y + triangle_size}) scale(-1, -1)"))
76
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x}, #{y + triangle_size}) scale(1, 1)"))
77
+ svg.polyline(triangle, styles.merge('transform' => "translate(#{x + triangle_size * 2}, #{y + triangle_size}) scale(-1, 1)"))
78
+ end
79
+
80
+ def build_right_triangle_shape(side_length)
81
+ "0, 0, #{side_length}, #{side_length}, 0, #{side_length}, 0, 0"
82
+ end
83
+ end
84
+ end
85
+ end