geo_pattern 1.3.2 → 1.5.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 (135) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +30 -0
  3. data/.gitignore +1 -0
  4. data/.simplecov +26 -0
  5. data/Gemfile +17 -17
  6. data/README.md +154 -29
  7. data/Rakefile +72 -13
  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 +19 -17
  27. data/lib/geo_pattern/background.rb +27 -0
  28. data/lib/geo_pattern/background_generators/solid_generator.rb +54 -0
  29. data/lib/geo_pattern/color.rb +27 -0
  30. data/lib/geo_pattern/color_generators/base_color_generator.rb +57 -0
  31. data/lib/geo_pattern/color_generators/simple_generator.rb +29 -0
  32. data/lib/geo_pattern/color_preset.rb +32 -0
  33. data/lib/geo_pattern/errors.rb +9 -0
  34. data/lib/geo_pattern/geo_pattern_task.rb +61 -0
  35. data/lib/geo_pattern/helpers.rb +50 -1
  36. data/lib/geo_pattern/pattern.rb +86 -0
  37. data/lib/geo_pattern/pattern_generator.rb +34 -81
  38. data/lib/geo_pattern/pattern_helpers.rb +34 -2
  39. data/lib/geo_pattern/pattern_preset.rb +25 -0
  40. data/lib/geo_pattern/pattern_sieve.rb +38 -0
  41. data/lib/geo_pattern/pattern_store.rb +65 -0
  42. data/lib/geo_pattern/pattern_validator.rb +29 -0
  43. data/lib/geo_pattern/rake_task.rb +112 -0
  44. data/lib/geo_pattern/roles/comparable_metadata.rb +37 -0
  45. data/lib/geo_pattern/roles/named_generator.rb +15 -0
  46. data/lib/geo_pattern/seed.rb +23 -0
  47. data/lib/geo_pattern/structure.rb +27 -0
  48. data/lib/geo_pattern/structure_generators/base_generator.rb +88 -0
  49. data/lib/geo_pattern/structure_generators/chevrons_generator.rb +57 -0
  50. data/lib/geo_pattern/structure_generators/concentric_circles_generator.rb +58 -0
  51. data/lib/geo_pattern/structure_generators/diamonds_generator.rb +72 -0
  52. data/lib/geo_pattern/structure_generators/hexagons_generator.rb +69 -0
  53. data/lib/geo_pattern/structure_generators/mosaic_squares_generator.rb +85 -0
  54. data/lib/geo_pattern/structure_generators/nested_squares_generator.rb +62 -0
  55. data/lib/geo_pattern/structure_generators/octagons_generator.rb +45 -0
  56. data/lib/geo_pattern/structure_generators/overlapping_circles_generator.rb +57 -0
  57. data/lib/geo_pattern/structure_generators/overlapping_rings_generator.rb +57 -0
  58. data/lib/geo_pattern/structure_generators/plaid_generator.rb +55 -0
  59. data/lib/geo_pattern/structure_generators/plus_signs_generator.rb +68 -0
  60. data/lib/geo_pattern/structure_generators/sine_waves_generator.rb +45 -0
  61. data/lib/geo_pattern/structure_generators/squares_generator.rb +37 -0
  62. data/lib/geo_pattern/structure_generators/tessellation_generator.rb +105 -0
  63. data/lib/geo_pattern/structure_generators/triangles_generator.rb +64 -0
  64. data/lib/geo_pattern/structure_generators/xes_generator.rb +74 -0
  65. data/lib/geo_pattern/svg_image.rb +103 -0
  66. data/lib/geo_pattern/version.rb +3 -1
  67. data/lib/geo_pattern.rb +54 -30
  68. data/script/bootstrap +30 -0
  69. data/script/console +9 -0
  70. data/script/test +21 -0
  71. data/spec/background_generators/solid_generator_spec.rb +52 -0
  72. data/spec/background_spec.rb +27 -0
  73. data/spec/color_generators/base_color_generator_spec.rb +33 -0
  74. data/spec/color_generators/simple_generator_spec.rb +14 -0
  75. data/spec/color_preset_spec.rb +55 -0
  76. data/spec/color_spec.rb +17 -0
  77. data/spec/geo_pattern_spec.rb +98 -26
  78. data/spec/helpers_spec.rb +67 -0
  79. data/spec/pattern_preset_spec.rb +43 -0
  80. data/spec/pattern_sieve_spec.rb +68 -0
  81. data/spec/pattern_spec.rb +74 -0
  82. data/spec/pattern_store_spec.rb +49 -0
  83. data/spec/pattern_validator_spec.rb +30 -0
  84. data/spec/seed_spec.rb +16 -0
  85. data/spec/spec_helper.rb +9 -13
  86. data/spec/structure_generators/chevrons_generator_spec.rb +7 -0
  87. data/spec/structure_generators/concentric_circles_generator_spec.rb +7 -0
  88. data/spec/structure_generators/diamonds_generator_spec.rb +7 -0
  89. data/spec/structure_generators/hexagons_generator_spec.rb +7 -0
  90. data/spec/structure_generators/mosaic_squares_generator_spec.rb +7 -0
  91. data/spec/structure_generators/nested_squares_generator_spec.rb +7 -0
  92. data/spec/structure_generators/octagons_generator_spec.rb +7 -0
  93. data/spec/structure_generators/overlapping_circles_generator_spec.rb +7 -0
  94. data/spec/structure_generators/overlapping_rings_generator_spec.rb +7 -0
  95. data/spec/structure_generators/plaid_generator_spec.rb +7 -0
  96. data/spec/structure_generators/plus_signs_generator_spec.rb +7 -0
  97. data/spec/structure_generators/sine_waves_generator_spec.rb +7 -0
  98. data/spec/structure_generators/squares_generator_spec.rb +7 -0
  99. data/spec/structure_generators/tessellation_generator_spec.rb +7 -0
  100. data/spec/structure_generators/triangles_generator_spec.rb +7 -0
  101. data/spec/structure_generators/xes_generator_spec.rb +7 -0
  102. data/spec/structure_spec.rb +40 -0
  103. data/spec/support/aruba.rb +6 -6
  104. data/spec/support/helpers/fixtures.rb +14 -0
  105. data/spec/support/kernel.rb +46 -0
  106. data/spec/support/matchers/image.rb +19 -0
  107. data/spec/support/matchers/name.rb +17 -0
  108. data/spec/support/rspec.rb +4 -2
  109. data/spec/support/shared_examples/generator.rb +48 -0
  110. data/spec/support/shared_examples/pattern.rb +33 -0
  111. data/spec/support/shared_examples/pattern_name.rb +9 -0
  112. data/spec/support/shared_examples/structure.rb +50 -0
  113. data/spec/support/string.rb +3 -2
  114. data/spec/svg_spec.rb +8 -6
  115. metadata +105 -37
  116. data/.rubocop.yml +0 -13
  117. data/lib/geo_pattern/pattern/base_pattern.rb +0 -47
  118. data/lib/geo_pattern/pattern/chevron_pattern.rb +0 -45
  119. data/lib/geo_pattern/pattern/concentric_circles_pattern.rb +0 -47
  120. data/lib/geo_pattern/pattern/diamond_pattern.rb +0 -56
  121. data/lib/geo_pattern/pattern/hexagon_pattern.rb +0 -57
  122. data/lib/geo_pattern/pattern/mosaic_squares_pattern.rb +0 -76
  123. data/lib/geo_pattern/pattern/nested_squares_pattern.rb +0 -51
  124. data/lib/geo_pattern/pattern/octagon_pattern.rb +0 -35
  125. data/lib/geo_pattern/pattern/overlapping_circles_pattern.rb +0 -46
  126. data/lib/geo_pattern/pattern/overlapping_rings_pattern.rb +0 -46
  127. data/lib/geo_pattern/pattern/plaid_pattern.rb +0 -49
  128. data/lib/geo_pattern/pattern/plus_sign_pattern.rb +0 -53
  129. data/lib/geo_pattern/pattern/sine_wave_pattern.rb +0 -36
  130. data/lib/geo_pattern/pattern/square_pattern.rb +0 -27
  131. data/lib/geo_pattern/pattern/tessellation_pattern.rb +0 -93
  132. data/lib/geo_pattern/pattern/triangle_pattern.rb +0 -51
  133. data/lib/geo_pattern/pattern/xes_pattern.rb +0 -58
  134. data/lib/geo_pattern/svg.rb +0 -77
  135. data/spec/support/helpers.rb +0 -34
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class TessellationGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :side_length, :hex_height, :hex_width, :triangle_height, :triangle, :tile_width, :tile_height
9
+
10
+ def after_initialize
11
+ # 3.4.6.4 semi-regular tessellation
12
+ @side_length = map(hex_val(0, 1), 0, 15, 5, 40)
13
+ @hex_height = side_length * Math.sqrt(3)
14
+ @hex_width = side_length * 2
15
+ @triangle_height = side_length / 2 * Math.sqrt(3)
16
+ @triangle = build_rotated_triangle_shape(side_length, triangle_height)
17
+ @tile_width = side_length * 3 + triangle_height * 2
18
+ @tile_height = (hex_height * 2) + (side_length * 2)
19
+
20
+ self.height = tile_height
21
+ self.width = tile_width
22
+ end
23
+
24
+ def generate_structure
25
+ 20.times do |i|
26
+ val = hex_val(i, 1)
27
+ opacity = opacity(val)
28
+ fill = fill_color(val)
29
+
30
+ styles = {
31
+ "stroke" => stroke_color,
32
+ "stroke-opacity" => stroke_opacity,
33
+ "fill" => fill,
34
+ "fill-opacity" => opacity,
35
+ "stroke-width" => 1
36
+ }
37
+
38
+ case i
39
+ when 0 # all 4 corners
40
+ svg.rect(-side_length / 2, -side_length / 2, side_length, side_length, styles)
41
+ svg.rect(tile_width - side_length / 2, -side_length / 2, side_length, side_length, styles)
42
+ svg.rect(-side_length / 2, tile_height - side_length / 2, side_length, side_length, styles)
43
+ svg.rect(tile_width - side_length / 2, tile_height - side_length / 2, side_length, side_length, styles)
44
+ when 1 # center / top square
45
+ svg.rect(hex_width / 2 + triangle_height, hex_height / 2, side_length, side_length, styles)
46
+ when 2 # side squares
47
+ svg.rect(-side_length / 2, tile_height / 2 - side_length / 2, side_length, side_length, styles)
48
+ svg.rect(tile_width - side_length / 2, tile_height / 2 - side_length / 2, side_length, side_length, styles)
49
+ when 3 # center / bottom square
50
+ svg.rect(hex_width / 2 + triangle_height, hex_height * 1.5 + side_length, side_length, side_length, styles)
51
+ when 4 # left top / bottom triangle
52
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{side_length / 2}, #{-side_length / 2}) rotate(0, #{side_length / 2}, #{triangle_height / 2})"))
53
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{side_length / 2}, #{tile_height - -side_length / 2}) rotate(0, #{side_length / 2}, #{triangle_height / 2}) scale(1, -1)"))
54
+ when 5 # right top / bottom triangle
55
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width - side_length / 2}, #{-side_length / 2}) rotate(0, #{side_length / 2}, #{triangle_height / 2}) scale(-1, 1)"))
56
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width - side_length / 2}, #{tile_height + side_length / 2}) rotate(0, #{side_length / 2}, #{triangle_height / 2}) scale(-1, -1)"))
57
+ when 6 # center / top / right triangle
58
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width / 2 + side_length / 2}, #{hex_height / 2})"))
59
+ when 7 # center / top / left triangle
60
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width - tile_width / 2 - side_length / 2}, #{hex_height / 2}) scale(-1, 1)"))
61
+ when 8 # center / bottom / right triangle
62
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width / 2 + side_length / 2}, #{tile_height - hex_height / 2}) scale(1, -1)"))
63
+ when 9 # center / bottom / left triangle
64
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width - tile_width / 2 - side_length / 2}, #{tile_height - hex_height / 2}) scale(-1, -1)"))
65
+ when 10 # left / middle triangle
66
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{side_length / 2}, #{tile_height / 2 - side_length / 2})"))
67
+ when 11 # right / middle triangle
68
+ svg.polyline(triangle, styles.merge("transform" => "translate(#{tile_width - side_length / 2}, #{tile_height / 2 - side_length / 2}) scale(-1, 1)"))
69
+ when 12 # left / top square
70
+ svg.rect(0, 0, side_length, side_length,
71
+ styles.merge("transform" => "translate(#{side_length / 2}, #{side_length / 2}) rotate(-30, 0, 0)"))
72
+ when 13 # right / top square
73
+ svg.rect(0, 0, side_length, side_length,
74
+ styles.merge("transform" => "scale(-1, 1) translate(#{-tile_width + side_length / 2}, #{side_length / 2}) rotate(-30, 0, 0)"))
75
+ when 14 # left / center-top square
76
+ svg.rect(0, 0, side_length, side_length,
77
+ styles.merge("transform" => "translate(#{side_length / 2}, #{tile_height / 2 - side_length / 2 - side_length}) rotate(30, 0, #{side_length})"))
78
+ when 15 # right / center-top square
79
+ svg.rect(0, 0, side_length, side_length,
80
+ styles.merge("transform" => "scale(-1, 1) translate(#{-tile_width + side_length / 2}, #{tile_height / 2 - side_length / 2 - side_length}) rotate(30, 0, #{side_length})"))
81
+ when 16 # left / center-top square
82
+ svg.rect(0, 0, side_length, side_length,
83
+ styles.merge("transform" => "scale(1, -1) translate(#{side_length / 2}, #{-tile_height + tile_height / 2 - side_length / 2 - side_length}) rotate(30, 0, #{side_length})"))
84
+ when 17 # right / center-bottom square
85
+ svg.rect(0, 0, side_length, side_length,
86
+ styles.merge("transform" => "scale(-1, -1) translate(#{-tile_width + side_length / 2}, #{-tile_height + tile_height / 2 - side_length / 2 - side_length}) rotate(30, 0, #{side_length})"))
87
+ when 18 # left / bottom square
88
+ svg.rect(0, 0, side_length, side_length,
89
+ styles.merge("transform" => "scale(1, -1) translate(#{side_length / 2}, #{-tile_height + side_length / 2}) rotate(-30, 0, 0)"))
90
+ when 19 # right / bottom square
91
+ svg.rect(0, 0, side_length, side_length,
92
+ styles.merge("transform" => "scale(-1, -1) translate(#{-tile_width + side_length / 2}, #{-tile_height + side_length / 2}) rotate(-30, 0, 0)"))
93
+ end
94
+ end
95
+
96
+ svg
97
+ end
98
+
99
+ def build_rotated_triangle_shape(side_length, width)
100
+ half_height = side_length / 2
101
+ "0, 0, #{width}, #{half_height}, 0, #{side_length}, 0, 0"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class TrianglesGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :scale, :side_length, :triangle_height, :triangle
9
+
10
+ def after_initialize
11
+ @scale = hex_val(0, 1)
12
+ @side_length = map(scale, 0, 15, 15, 80)
13
+ @triangle_height = side_length / 2 * Math.sqrt(3)
14
+ @triangle = build_triangle_shape(side_length, triangle_height)
15
+
16
+ self.width = side_length * 3
17
+ self.height = triangle_height * 6
18
+ end
19
+
20
+ def generate_structure
21
+ i = 0
22
+ 6.times do |y|
23
+ 6.times do |x|
24
+ val = hex_val(i, 1)
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
+ rotation = if y % 2 == 0
36
+ (x % 2 == 0) ? 180 : 0
37
+ else
38
+ (x % 2 != 0) ? 180 : 0
39
+ end
40
+
41
+ svg.polyline(triangle, styles.merge(
42
+ "transform" => "translate(#{x * side_length * 0.5 - side_length / 2}, #{triangle_height * y}) rotate(#{rotation}, #{side_length / 2}, #{triangle_height / 2})"
43
+ ))
44
+
45
+ # Add an extra one at top-right, for tiling.
46
+ if x == 0
47
+ svg.polyline(triangle, styles.merge(
48
+ "transform" => "translate(#{6 * side_length * 0.5 - side_length / 2}, #{triangle_height * y}) rotate(#{rotation}, #{side_length / 2}, #{triangle_height / 2})"
49
+ ))
50
+ end
51
+ i += 1
52
+ end
53
+ end
54
+
55
+ svg
56
+ end
57
+
58
+ def build_triangle_shape(side_length, height)
59
+ half_width = side_length / 2
60
+ "#{half_width}, 0, #{side_length}, #{height}, 0, #{height}, #{half_width}, 0"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class XesGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :square_size, :x_shape, :x_size
9
+
10
+ def after_initialize
11
+ @square_size = map(hex_val(0, 1), 0, 15, 10, 25)
12
+ @x_shape = build_plus_shape(square_size) # rotated later
13
+ @x_size = square_size * 3 * 0.943
14
+
15
+ self.height = self.width = x_size * 3
16
+ end
17
+
18
+ def generate_structure
19
+ i = 0
20
+ 6.times do |y|
21
+ 6.times do |x|
22
+ val = hex_val(i, 1)
23
+ opacity = opacity(val)
24
+ dy = (x % 2 == 0) ? y * x_size - x_size * 0.5 : y * x_size - x_size * 0.5 + x_size / 4
25
+ fill = fill_color(val)
26
+
27
+ styles = {
28
+ "fill" => fill,
29
+ "style" => {
30
+ "opacity" => opacity
31
+ }
32
+ }
33
+
34
+ svg.group(x_shape, styles.merge(
35
+ "transform" => "translate(#{x * x_size / 2 - x_size / 2},#{dy - y * x_size / 2}) rotate(45, #{x_size / 2}, #{x_size / 2})"
36
+ ))
37
+
38
+ # Add an extra column on the right for tiling.
39
+ if x == 0
40
+ svg.group(x_shape, styles.merge(
41
+ "transform" => "translate(#{6 * x_size / 2 - x_size / 2},#{dy - y * x_size / 2}) rotate(45, #{x_size / 2}, #{x_size / 2})"
42
+ ))
43
+ end
44
+
45
+ # Add an extra row on the bottom that matches the first row, for tiling.
46
+ if y == 0
47
+ dy = (x % 2 == 0) ? 6 * x_size - x_size / 2 : 6 * x_size - x_size / 2 + x_size / 4
48
+ svg.group(x_shape, styles.merge(
49
+ "transform" => "translate(#{x * x_size / 2 - x_size / 2},#{dy - 6 * x_size / 2}) rotate(45, #{x_size / 2}, #{x_size / 2})"
50
+ ))
51
+ end
52
+
53
+ # These can hang off the bottom, so put a row at the top for tiling.
54
+ if y == 5
55
+ svg.group(x_shape, styles.merge(
56
+ "transform" => "translate(#{x * x_size / 2 - x_size / 2},#{dy - 11 * x_size / 2}) rotate(45, #{x_size / 2}, #{x_size / 2})"
57
+ ))
58
+ end
59
+
60
+ # Add an extra one at top-right and bottom-right, for tiling.
61
+ if x == 0 && y == 0
62
+ svg.group(x_shape, styles.merge(
63
+ "transform" => "translate(#{6 * x_size / 2 - x_size / 2},#{dy - 6 * x_size / 2}) rotate(45, #{x_size / 2}, #{x_size / 2})"
64
+ ))
65
+ end
66
+ i += 1
67
+ end
68
+ end
69
+
70
+ svg
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class SvgImage
5
+ include Comparable
6
+
7
+ private
8
+
9
+ attr_reader :svg_string
10
+
11
+ public
12
+
13
+ attr_reader :height, :width
14
+
15
+ def initialize
16
+ @width = 100
17
+ @height = 100
18
+ @svg_string = +""
19
+ end
20
+
21
+ def width=(width)
22
+ @width = width.floor
23
+ end
24
+
25
+ def height=(height)
26
+ @height = height.floor
27
+ end
28
+
29
+ # Pattern includes string
30
+ #
31
+ # @param [String] string
32
+ # The string which should be included in the body of the SvgImage
33
+ def include?(string)
34
+ body.include? string
35
+ end
36
+
37
+ def svg_header
38
+ %(<svg xmlns="http://www.w3.org/2000/svg" width="#{@width}" height="#{@height}">)
39
+ end
40
+
41
+ def svg_closer
42
+ "</svg>"
43
+ end
44
+
45
+ def to_s
46
+ svg_header + svg_string + svg_closer
47
+ end
48
+
49
+ def body
50
+ svg_string
51
+ end
52
+
53
+ def <<(svg)
54
+ svg_string << svg.body
55
+ end
56
+
57
+ def rect(x, y, width, height, args = {})
58
+ svg_string << %(<rect x="#{x}" y="#{y}" width="#{width}" height="#{height}" #{write_args(args)} />)
59
+ end
60
+
61
+ def circle(cx, cy, r, args = {})
62
+ svg_string << %(<circle cx="#{cx}" cy="#{cy}" r="#{r}" #{write_args(args)} />)
63
+ end
64
+
65
+ def path(str, args = {})
66
+ svg_string << %(<path d="#{str}" #{write_args(args)} />)
67
+ end
68
+
69
+ def polyline(str, args = {})
70
+ svg_string << %(<polyline points="#{str}" #{write_args(args)} />)
71
+ end
72
+
73
+ def group(elements, args = {})
74
+ svg_string << %(<g #{write_args(args)}>)
75
+ elements.each { |e| eval e } # rubocop:disable Security/Eval
76
+ svg_string << %(</g>)
77
+ end
78
+
79
+ def write_args(args)
80
+ str = +""
81
+ args.each do |key, value|
82
+ if value.is_a?(Hash)
83
+ str << %(#{key}=")
84
+ value.each do |k, v|
85
+ str << %(#{k}:#{v};)
86
+ end
87
+ str << %(" )
88
+ else
89
+ str << %(#{key}="#{value}" )
90
+ end
91
+ end
92
+ str
93
+ end
94
+
95
+ def self.as_comment(str)
96
+ "<!-- #{str} -->"
97
+ end
98
+
99
+ def <=>(other)
100
+ to_s <=> other.to_s
101
+ end
102
+ end
103
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoPattern
2
- VERSION = "1.3.2"
4
+ VERSION = "1.5.0"
3
5
  end
data/lib/geo_pattern.rb CHANGED
@@ -1,34 +1,58 @@
1
- require 'base64'
2
- require 'digest/sha1'
3
- require 'color'
4
-
5
- require 'geo_pattern/version'
6
- require 'geo_pattern/svg'
7
- require 'geo_pattern/pattern_helpers'
8
- require 'geo_pattern/helpers'
9
-
10
- require 'geo_pattern/pattern/base_pattern'
11
- require 'geo_pattern/pattern/chevron_pattern'
12
- require 'geo_pattern/pattern/concentric_circles_pattern'
13
- require 'geo_pattern/pattern/diamond_pattern'
14
- require 'geo_pattern/pattern/hexagon_pattern'
15
- require 'geo_pattern/pattern/mosaic_squares_pattern'
16
- require 'geo_pattern/pattern/nested_squares_pattern'
17
- require 'geo_pattern/pattern/octagon_pattern'
18
- require 'geo_pattern/pattern/overlapping_circles_pattern'
19
- require 'geo_pattern/pattern/overlapping_rings_pattern'
20
- require 'geo_pattern/pattern/plaid_pattern'
21
- require 'geo_pattern/pattern/plus_sign_pattern'
22
- require 'geo_pattern/pattern/sine_wave_pattern'
23
- require 'geo_pattern/pattern/square_pattern'
24
- require 'geo_pattern/pattern/tessellation_pattern'
25
- require 'geo_pattern/pattern/triangle_pattern'
26
- require 'geo_pattern/pattern/xes_pattern'
27
-
28
- require 'geo_pattern/pattern_generator'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "digest/sha1"
5
+ require "color"
6
+ require "forwardable"
7
+
8
+ require "geo_pattern/version"
9
+
10
+ require "geo_pattern/roles/named_generator"
11
+ require "geo_pattern/roles/comparable_metadata"
12
+
13
+ require "geo_pattern/errors"
14
+ require "geo_pattern/color"
15
+ require "geo_pattern/svg_image"
16
+ require "geo_pattern/pattern_helpers"
17
+ require "geo_pattern/helpers"
18
+ require "geo_pattern/pattern_store"
19
+ require "geo_pattern/pattern_validator"
20
+ require "geo_pattern/pattern_sieve"
21
+ require "geo_pattern/pattern"
22
+ require "geo_pattern/seed"
23
+ require "geo_pattern/pattern_preset"
24
+ require "geo_pattern/color_preset"
25
+
26
+ require "geo_pattern/structure"
27
+ require "geo_pattern/background"
28
+
29
+ require "geo_pattern/color_generators/simple_generator"
30
+ require "geo_pattern/color_generators/base_color_generator"
31
+
32
+ require "geo_pattern/background_generators/solid_generator"
33
+
34
+ require "geo_pattern/structure_generators/base_generator"
35
+ require "geo_pattern/structure_generators/chevrons_generator"
36
+ require "geo_pattern/structure_generators/concentric_circles_generator"
37
+ require "geo_pattern/structure_generators/diamonds_generator"
38
+ require "geo_pattern/structure_generators/hexagons_generator"
39
+ require "geo_pattern/structure_generators/mosaic_squares_generator"
40
+ require "geo_pattern/structure_generators/nested_squares_generator"
41
+ require "geo_pattern/structure_generators/octagons_generator"
42
+ require "geo_pattern/structure_generators/overlapping_circles_generator"
43
+ require "geo_pattern/structure_generators/overlapping_rings_generator"
44
+ require "geo_pattern/structure_generators/plaid_generator"
45
+ require "geo_pattern/structure_generators/plus_signs_generator"
46
+ require "geo_pattern/structure_generators/sine_waves_generator"
47
+ require "geo_pattern/structure_generators/squares_generator"
48
+ require "geo_pattern/structure_generators/tessellation_generator"
49
+ require "geo_pattern/structure_generators/triangles_generator"
50
+ require "geo_pattern/structure_generators/xes_generator"
51
+
52
+ require "geo_pattern/pattern_generator"
29
53
 
30
54
  module GeoPattern
31
- def self.generate(string=Time.now, opts={})
32
- GeoPattern::PatternGenerator.new(string.to_s, opts)
55
+ def self.generate(string = Time.now, opts = {})
56
+ GeoPattern::PatternGenerator.new(string.to_s, **opts).generate
33
57
  end
34
58
  end
data/script/bootstrap ADDED
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ info_msg="\e[0;32m[INFO]\e[0;30m"
6
+ error_msg="\e[0;31mFAILED\e[0;30m"
7
+
8
+ function output_error_log {
9
+ [[ -f error.log ]] && ( cat error.log >&2; rm error.log)
10
+ }
11
+
12
+ echo -ne "$info_msg Checking if ruby installed? "
13
+ which 'ruby' >/dev/null 2>error.log || ( echo -e "$error_msg\n\nCould not find \`ruby\`. Please install ruby or add it to PATH"; output_error_log; exit 1 )
14
+ echo OK
15
+
16
+ echo -en "$info_msg rubygem \"rake\" "
17
+ gem install rake >/dev/null 2>error.log || ( echo -e "$error_msg\n\nAn error occurred during installation of rake. Run \`gem install rake\` yourself."; output_error_log; exit 1 )
18
+ echo OK
19
+
20
+ if [ -n "$CI" ]; then
21
+ echo -e "$info_msg Bootstrapping in CI mode"
22
+ rake_task='bootstrap:ci'
23
+ else
24
+ echo -e "$info_msg Bootstrapping in normal mode"
25
+ rake_task='bootstrap'
26
+ fi
27
+
28
+ echo -en "$info_msg Running rake task \"$rake_task\" "
29
+ rake --trace $rake_task >error.log 2>&1 || ( echo -e "$error_msg\n\nAn error occurred during run of rake-task \"$rake_task\". Run \`rake $rake_task\` yourself."; output_error_log; exit 1 )
30
+ echo OK
data/script/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH << File.expand_path("../lib", __dir__)
5
+
6
+ require "pry"
7
+ require "geo_pattern"
8
+
9
+ Pry.start
data/script/test ADDED
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ function output_error_log {
6
+ [[ -f error.log ]] && ( cat error.log >&2; rm error.log)
7
+ }
8
+
9
+ info_msg="\e[0;32m[INFO]\e[0;30m"
10
+ error_msg="\e[0;31mFAILED\e[0;30m"
11
+
12
+ if [ -n "$CI" ]; then
13
+ echo -e "$info_msg Testing in CI mode"
14
+ rake_task='test:ci'
15
+ else
16
+ echo -e "$info_msg Testing in normal mode"
17
+ rake_task='test'
18
+ fi
19
+
20
+ echo -en "$info_msg Running rake task \"$rake_task\" "
21
+ bundle exec rake --trace $rake_task
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe BackgroundGenerators::SolidGenerator do
6
+ subject(:generator) { described_class.new(seed, preset) }
7
+
8
+ let(:seed) { instance_double("GeoPattern::Seed") }
9
+ let(:preset) { instance_double("GeoPattern::ColorPreset") }
10
+ let(:pattern) { instance_double("GeoPattern::Pattern") }
11
+ let(:background_metadata) { instance_double("GeoPattern::BackgroundMetadata") }
12
+
13
+ let(:color) { "#aaaaaa" }
14
+ let(:base_color) { "#bbbbbb" }
15
+ let(:base_color_should_be_used) { true }
16
+
17
+ before :each do
18
+ allow(seed).to receive(:to_i).with(14, 3).and_return(2616)
19
+ allow(seed).to receive(:to_i).with(17, 1).and_return(3)
20
+
21
+ allow(preset).to receive(:mode?).with(:base_color).and_return(base_color_should_be_used)
22
+ allow(preset).to receive(:color).and_return(color)
23
+ allow(preset).to receive(:base_color).and_return(base_color)
24
+ end
25
+
26
+ it { is_expected.not_to be_nil }
27
+
28
+ describe "#generate" do
29
+ context "when base color is given" do
30
+ let(:generated_color) { %w[187 187 187] }
31
+
32
+ before :each do
33
+ expect(pattern).to receive(:background=).with(have_image_with_rgb_color(generated_color))
34
+ end
35
+
36
+ it { generator.generate(pattern) }
37
+ end
38
+
39
+ context "when color is given" do
40
+ let(:base_color_should_be_used) { false }
41
+ let(:generated_color) { %w[170 170 170] }
42
+
43
+ before :each do
44
+ expect(pattern).to receive(:background=).with(have_image_with_rgb_color(generated_color))
45
+ end
46
+
47
+ it { generator.generate(pattern) }
48
+ end
49
+
50
+ it_behaves_like "a named generator", :solid
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Background do
6
+ subject(:metadata) { described_class.new(image: svg_image, preset: preset, generator: generator, color: color) }
7
+
8
+ let(:svg_image) { instance_double("GeoPattern::SvgImage") }
9
+ let(:svg_image_content) { fixtures_path("generated_patterns/sine_waves.svg").read.chomp }
10
+ let(:preset) { instance_double("GeoPattern::ColorPreset") }
11
+ let(:generator) { stub_const("GeoPattern::BackgroundGenerators::SolidGenerator", Class.new) }
12
+
13
+ let(:color) { "#aaaaaa" }
14
+ let(:base_color) { "#bbbbbb" }
15
+
16
+ it { is_expected.not_to be_nil }
17
+
18
+ before :each do
19
+ allow(preset).to receive(:color).and_return(color)
20
+ allow(preset).to receive(:base_color).and_return(base_color)
21
+ end
22
+
23
+ it_behaves_like "a metadata argument", :color
24
+ it_behaves_like "a metadata argument", :generator
25
+ it_behaves_like "a forwarded metadata argument", :base_color
26
+ it_behaves_like "a forwarded metadata argument", :color
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe ColorGenerators::BaseColorGenerator do
6
+ subject(:generator) { described_class.new(color_string, seed) }
7
+
8
+ let(:seed) { instance_double("GeoPattern::Seed") }
9
+ let(:seed_value1) { 2616 }
10
+ let(:seed_value2) { 2 }
11
+
12
+ let(:color_string) { "#ff00ff" }
13
+
14
+ it { is_expected.not_to be_nil }
15
+
16
+ before :each do
17
+ allow(seed).to receive(:to_i).with(14, 3).and_return(seed_value1)
18
+ allow(seed).to receive(:to_i).with(17, 1).and_return(seed_value2)
19
+ end
20
+
21
+ describe "#generate" do
22
+ let(:color) { generator.generate }
23
+
24
+ context "when sat offset is % 2 == 0" do
25
+ it { expect(color.to_svg).to eq "rgb(210, 255, 0)" }
26
+ end
27
+
28
+ context "when sat offset is not % 2 == 0" do
29
+ let(:seed_value2) { 3 }
30
+ it { expect(color.to_svg).to eq "rgb(207, 251, 4)" }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ColorGenerators::SimpleGenerator do
4
+ subject(:generator) { described_class.new(color_string) }
5
+
6
+ let(:color_string) { "#ff00ff" }
7
+
8
+ it { is_expected.not_to be_nil }
9
+
10
+ describe "#generate" do
11
+ let(:color) { generator.generate }
12
+ it { expect(color.to_svg).to eq "rgb(255, 0, 255)" }
13
+ end
14
+ end