geo_pattern 1.3.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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