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
data/geo_pattern.gemspec CHANGED
@@ -1,23 +1,25 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'geo_pattern/version'
5
+ require "geo_pattern/version"
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = 'geo_pattern'
8
- spec.version = GeoPattern::VERSION
9
- spec.authors = ['Jason Long']
10
- spec.email = ['jlong@github.com']
11
- spec.summary = %q{Generate SVG beautiful patterns}
12
- spec.description = %q{Generate SVG beautiful patterns}
13
- spec.homepage = 'https://github.com/jasonlong/geo_pattern'
14
- spec.license = 'MIT'
8
+ spec.name = "geo_pattern"
9
+ spec.version = GeoPattern::VERSION
10
+ spec.authors = ["Jason Long"]
11
+ spec.email = ["jason@jasonlong.me"]
12
+ spec.summary = "Generate SVG beautiful patterns"
13
+ spec.description = "Generate SVG beautiful patterns"
14
+ spec.homepage = "https://github.com/jasonlong/geo_pattern"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
15
20
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ['lib']
21
+ spec.required_ruby_version = ">= 2.6"
20
22
 
21
- spec.add_dependency 'color', '~> 1.5'
22
- spec.add_development_dependency 'bundler', '~> 1.5'
23
+ spec.add_dependency "color", "~> 1.5"
24
+ spec.add_development_dependency "bundler", "~> 2.2"
23
25
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class Background
5
+ include Roles::ComparableMetadata
6
+
7
+ extend Forwardable
8
+
9
+ attr_reader :image, :preset, :color, :generator
10
+
11
+ def_delegators :@preset, :base_color
12
+
13
+ def initialize(options)
14
+ @image = options[:image]
15
+ @preset = options[:preset]
16
+ @color = options[:color]
17
+ @generator = options[:generator]
18
+
19
+ raise ArgumentError, "Argument color is missing" if @color.nil?
20
+ raise ArgumentError, "Argument image is missing" if @image.nil?
21
+ raise ArgumentError, "Argument preset is missing" if @preset.nil?
22
+ raise ArgumentError, "Argument generator is missing" if @generator.nil?
23
+ end
24
+
25
+ def_comparators :base_color, :color
26
+ end
27
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # Generating backgrounds
5
+ module BackgroundGenerators
6
+ # Generating a solid background
7
+ class SolidGenerator
8
+ include Roles::NamedGenerator
9
+
10
+ private
11
+
12
+ attr_reader :color, :seed, :preset
13
+
14
+ public
15
+
16
+ # New generator
17
+ #
18
+ # @param [Seed] seed
19
+ # The seed used during generation the background
20
+ #
21
+ # @param [ColorPreset] preset
22
+ # A preset of values which are used during generating the background
23
+ def initialize(seed, preset)
24
+ @color = color_for(seed, preset)
25
+ @preset = preset
26
+ end
27
+
28
+ # Generate the background for pattern
29
+ #
30
+ # @param [#background=] pattern
31
+ # The pattern for which the background should be generated
32
+ def generate(pattern)
33
+ pattern.background = Background.new(image: generate_background, preset: preset, color: color, generator: self.class)
34
+
35
+ self
36
+ end
37
+
38
+ private
39
+
40
+ def generate_background
41
+ svg = SvgImage.new
42
+ svg.rect(0, 0, "100%", "100%", "fill" => color.to_svg)
43
+
44
+ svg
45
+ end
46
+
47
+ def color_for(seed, preset)
48
+ return ColorGenerators::BaseColorGenerator.new(preset.base_color, seed).generate if preset.mode? :base_color
49
+
50
+ ColorGenerators::SimpleGenerator.new(preset.color).generate
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class Color
5
+ private
6
+
7
+ attr_accessor :color
8
+
9
+ public
10
+
11
+ def initialize(html_color)
12
+ @color = ::Color::RGB.from_html html_color
13
+ end
14
+
15
+ def to_svg
16
+ r = (color.r * 255).round
17
+ g = (color.g * 255).round
18
+ b = (color.b * 255).round
19
+
20
+ format("rgb(%<r>d, %<g>d, %<b>d)", r: r, g: g, b: b)
21
+ end
22
+
23
+ def to_html
24
+ color.html
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # Color generators
5
+ module ColorGenerators
6
+ # Generate color based on Base Color and seed
7
+ class BaseColorGenerator
8
+ private
9
+
10
+ attr_reader :creator, :seed, :color
11
+
12
+ public
13
+
14
+ # New
15
+ #
16
+ # @param [String] color
17
+ # HTML color string, #0a0a0a
18
+ def initialize(color, seed, creator = Color)
19
+ @color = color
20
+ @seed = seed
21
+ @creator = creator
22
+ end
23
+
24
+ # Generator color
25
+ def generate
26
+ creator.new(transform(color, seed))
27
+ end
28
+
29
+ private
30
+
31
+ def transform(color, seed)
32
+ hue_offset = map(seed.to_i(14, 3), 0, 4095, 0, 359)
33
+ sat_offset = seed.to_i(17, 1)
34
+ new_color = ::Color::RGB.from_html(color).to_hsl
35
+ new_color.hue = new_color.hue - hue_offset
36
+
37
+ new_color.saturation = if sat_offset % 2 == 0
38
+ new_color.saturation + sat_offset
39
+ else
40
+ new_color.saturation - sat_offset
41
+ end
42
+ new_color.html
43
+ end
44
+
45
+ # Ruby implementation of Processing's map function
46
+ # http://processing.org/reference/map_.html
47
+ # v for value, d for desired
48
+ def map(value, v_min, v_max, d_min, d_max)
49
+ v_value = value.to_f # so it returns float
50
+
51
+ v_range = v_max - v_min
52
+ d_range = d_max - d_min
53
+ (v_value - v_min) * d_range / v_range + d_min
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # Color generators
5
+ module ColorGenerators
6
+ # Simple one
7
+ class SimpleGenerator
8
+ private
9
+
10
+ attr_reader :color, :creator
11
+
12
+ public
13
+
14
+ # New
15
+ #
16
+ # @param [String] color
17
+ # HTML color string, #0a0a0a
18
+ def initialize(color, creator = Color)
19
+ @color = color
20
+ @creator = creator
21
+ end
22
+
23
+ # Generator color
24
+ def generate
25
+ creator.new(color)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Lint/DuplicateMethods
4
+
5
+ module GeoPattern
6
+ class ColorPreset
7
+ attr_accessor :color, :base_color, :mode
8
+
9
+ def initialize(color: nil, base_color: nil)
10
+ @color = color
11
+ @base_color = base_color
12
+ end
13
+
14
+ # Return mode
15
+ #
16
+ # @return [Symbol]
17
+ # The color mode
18
+ def mode
19
+ if color.nil? || color.empty?
20
+ :base_color
21
+ else
22
+ :color
23
+ end
24
+ end
25
+
26
+ def mode?(m)
27
+ mode == m
28
+ end
29
+ end
30
+ end
31
+
32
+ # rubocop:enable Lint/DuplicateMethods
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # user errors
5
+ class UserError < StandardError; end
6
+
7
+ # raised if an invalid pattern is requested
8
+ class InvalidPatternError < UserError; end
9
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "geo_pattern"
4
+ require "geo_pattern/rake_task"
5
+
6
+ module GeoPattern
7
+ # GeoPatternTask
8
+ #
9
+ # @see Rakefile
10
+ class GeoPatternTask < RakeTask
11
+ # @!attribute [r] data
12
+ # The data which should be used to generate patterns
13
+ attr_reader :data
14
+
15
+ # @!attribute [r] allowed_patterns
16
+ # The the patterns which are allowed to be used
17
+ attr_reader :allowed_patterns
18
+
19
+ # Create a new geo pattern task
20
+ #
21
+ # @param [Hash] data
22
+ # The data which should be used to generate patterns
23
+ #
24
+ # @param [Array] allowed_patterns
25
+ # The allowed_patterns
26
+ #
27
+ # @see RakeTask
28
+ # For other arguments accepted
29
+ def initialize(opts = {})
30
+ super
31
+
32
+ raise ArgumentError, :data if @options[:data].nil?
33
+
34
+ @data = @options[:data]
35
+ @allowed_patterns = @options[:allowed_patterns]
36
+ end
37
+
38
+ # @private
39
+ def run_task(_verbose)
40
+ data.each do |path, string|
41
+ opts = {}
42
+ path = File.expand_path(path)
43
+
44
+ if string.is_a?(Hash)
45
+ input = string[:input]
46
+ opts[:patterns] = string[:patterns] if string.key? :patterns
47
+ opts[:color] = string[:color] if string.key? :color
48
+ opts[:base_color] = string[:base_color] if string.key? :base_color
49
+ else
50
+ raise "Invalid data structure for Rake Task"
51
+ end
52
+
53
+ pattern = GeoPattern.generate(input, opts)
54
+
55
+ logger.info "Creating pattern at \"#{path}\"."
56
+ FileUtils.mkdir_p File.dirname(path)
57
+ File.write(path, pattern.to_svg)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,7 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoPattern
2
4
  module Helpers
3
5
  def require_files_matching_pattern(pattern)
4
- Dir.glob(pattern).each { |f| require_relative f }
6
+ Dir.glob(pattern).sort.each { |f| require_relative f }
5
7
  end
8
+
9
+ # Makes an underscored, lowercase form from the expression in the string.
10
+ #
11
+ # @see ActiveSupport
12
+ # It's MIT-Licensed
13
+ #
14
+ def underscore(camel_cased_word)
15
+ return camel_cased_word unless /[A-Z-]/.match?(camel_cased_word)
16
+
17
+ word = camel_cased_word.to_s.dup
18
+
19
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
20
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
21
+
22
+ word.downcase!
23
+
24
+ word
25
+ end
26
+
27
+ # Removes the module part from the expression in the string.
28
+ #
29
+ # @see ActiveSupport
30
+ # It's MIT-Licensed
31
+ #
32
+ # @exmple Use demodulize
33
+ #
34
+ # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
35
+ # 'Inflections'.demodulize # => "Inflections"
36
+ # '::Inflections'.demodulize # => "Inflections"
37
+ # ''.demodulize # => ""
38
+ #
39
+ # See also +deconstantize+.
40
+ def demodulize(path)
41
+ path = path.to_s
42
+
43
+ if (i = path.rindex("::"))
44
+ path[(i + 2)..]
45
+ else
46
+ path
47
+ end
48
+ end
49
+
50
+ def build_arguments(*methods)
51
+ methods.flatten.map { |m| [m, "#{m}?"] }.flatten
52
+ end
53
+
54
+ module_function :underscore, :demodulize, :build_arguments, :require_files_matching_pattern
6
55
  end
7
56
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class Pattern
5
+ private
6
+
7
+ attr_reader :svg_image
8
+
9
+ public
10
+
11
+ attr_accessor :background, :structure, :height, :width
12
+
13
+ def initialize(svg_image = SvgImage.new)
14
+ @svg_image = svg_image
15
+ end
16
+
17
+ # Check if string is included in pattern
18
+ #
19
+ # @param [String] string
20
+ # The checked string
21
+ def include?(string)
22
+ image.include?(string)
23
+ end
24
+
25
+ # Generate things for the pattern
26
+ #
27
+ # @param [#generate) generator
28
+ # The generator which should do things with this pattern - e.g. adding
29
+ # background or a structure
30
+ def generate_me(generator)
31
+ generator.generate self
32
+ end
33
+
34
+ # Convert pattern to svg
35
+ def to_svg
36
+ image.to_s
37
+ end
38
+ alias_method :to_s, :to_svg
39
+
40
+ # Convert to base64
41
+ def to_base64
42
+ Base64.strict_encode64(to_svg)
43
+ end
44
+
45
+ # Convert to data uri
46
+ def to_data_uri
47
+ "url(data:image/svg+xml;base64,#{to_base64});"
48
+ end
49
+
50
+ # @see #to_data_uri
51
+ # @deprecated
52
+ def uri_image
53
+ warn 'Using "#uri_image" is deprecated as of 1.3.1. Please use "#to_data_uri" instead.'
54
+
55
+ to_data_uri
56
+ end
57
+
58
+ # @see #to_svg
59
+ # @deprecated
60
+ def svg_string
61
+ warn 'Using "#svg_string" is deprecated as of 1.3.1. Please use "#to_svg" instead.'
62
+
63
+ to_svg
64
+ end
65
+
66
+ # @see #to_base64
67
+ # @deprecated
68
+ def base64_string
69
+ warn 'Using "#base64_string" is deprecated as of 1.3.1. Please use "#to_base64" instead.'
70
+
71
+ to_base64
72
+ end
73
+
74
+ private
75
+
76
+ def image
77
+ svg_image.height = height
78
+ svg_image.width = width
79
+
80
+ svg_image << background.image if background
81
+ svg_image << structure.image if structure
82
+
83
+ svg_image
84
+ end
85
+ end
86
+ end
@@ -1,101 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoPattern
2
4
  class PatternGenerator
3
- DEFAULTS = {
4
- :base_color => '#933c3c'
5
- }
5
+ private
6
6
 
7
- PATTERNS = {
8
- 'chevrons' => ChevronPattern,
9
- 'concentric_circles' => ConcentricCirclesPattern,
10
- 'diamonds' => DiamondPattern,
11
- 'hexagons' => HexagonPattern,
12
- 'mosaic_squares' => MosaicSquaresPattern,
13
- 'nested_squares' => NestedSquaresPattern,
14
- 'octagons' => OctagonPattern,
15
- 'overlapping_circles' => OverlappingCirclesPattern,
16
- 'overlapping_rings' => OverlappingRingsPattern,
17
- 'plaid' => PlaidPattern,
18
- 'plus_signs' => PlusSignPattern,
19
- 'sine_waves' => SineWavePattern,
20
- 'squares' => SquarePattern,
21
- 'tessellation' => TessellationPattern,
22
- 'triangles' => TrianglePattern,
23
- 'xes' => XesPattern,
24
- }.freeze
7
+ attr_reader :background_generator, :structure_generator
25
8
 
26
- FILL_COLOR_DARK = "#222"
27
- FILL_COLOR_LIGHT = "#ddd"
28
- STROKE_COLOR = "#000"
29
- STROKE_OPACITY = 0.02
30
- OPACITY_MIN = 0.02
31
- OPACITY_MAX = 0.15
9
+ public
32
10
 
33
- attr_reader :opts, :hash, :svg
11
+ def initialize(string, generator: nil, patterns: nil, base_color: nil, color: nil)
12
+ warn "Using generator key is deprecated as of 1.3.1" if generator
34
13
 
35
- def initialize(string, opts={})
36
- @opts = DEFAULTS.merge(opts)
37
- @hash = Digest::SHA1.hexdigest string
38
- @svg = SVG.new
14
+ requested_patterns = (Array(generator) | Array(patterns)).flatten.compact
39
15
 
40
- generate_background
41
- generate_pattern
42
- end
16
+ pattern_preset = PatternPreset.new(
17
+ fill_color_dark: "#222",
18
+ fill_color_light: "#ddd",
19
+ stroke_color: "#000",
20
+ stroke_opacity: 0.02,
21
+ opacity_min: 0.02,
22
+ opacity_max: 0.15
23
+ )
43
24
 
44
- def svg_string
45
- svg.to_s
46
- end
47
- alias_method :to_s, :svg_string
25
+ color_preset = ColorPreset.new(
26
+ base_color: "#933c3c"
27
+ )
28
+ color_preset.color = color if color
29
+ color_preset.base_color = base_color if base_color
48
30
 
49
- def base64_string
50
- Base64.strict_encode64(svg_string)
51
- end
31
+ seed = Seed.new(string)
52
32
 
53
- def uri_image
54
- "url(data:image/svg+xml;base64,#{base64_string});"
55
- end
33
+ pattern_validator = PatternValidator.new
34
+ pattern_validator.validate(requested_patterns)
56
35
 
57
- def generate_background
58
- if opts[:color]
59
- rgb = Color::RGB.from_html(opts[:color])
60
- else
61
- hue_offset = PatternHelpers.map(PatternHelpers.hex_val(hash, 14, 3), 0, 4095, 0, 359)
62
- sat_offset = PatternHelpers.hex_val(hash, 17, 1)
63
- base_color = Color::RGB.from_html(opts[:base_color]).to_hsl
64
- base_color.hue = base_color.hue - hue_offset;
36
+ pattern_sieve = PatternSieve.new(requested_patterns, seed)
65
37
 
66
- if (sat_offset % 2 == 0)
67
- base_color.saturation = base_color.saturation + sat_offset
68
- else
69
- base_color.saturation = base_color.saturation - sat_offset
70
- end
71
- rgb = base_color.to_rgb
38
+ @background_generator = BackgroundGenerators::SolidGenerator.new(seed, color_preset)
39
+ @structure_generator = begin
40
+ generator_klass = pattern_sieve.fetch
41
+ generator_klass.new(seed, pattern_preset)
72
42
  end
73
- r = (rgb.r * 255).round
74
- g = (rgb.g * 255).round
75
- b = (rgb.b * 255).round
76
- svg.rect(0, 0, "100%", "100%", {"fill" => "rgb(#{r}, #{g}, #{b})"})
77
43
  end
78
44
 
79
- def generate_pattern
80
- unless opts[:generator].nil?
81
- if opts[:generator].is_a? String
82
- generator = PATTERNS[opts[:generator]]
83
- puts SVG.as_comment("String pattern references are deprecated as of 1.3.0")
84
- elsif opts[:generator] < BasePattern
85
- if PATTERNS.values.include? opts[:generator]
86
- generator = opts[:generator]
87
- else
88
- abort("Error: the requested generator is invalid")
89
- generator = nil
90
- end
91
- end
92
- end
45
+ def generate
46
+ pattern = Pattern.new
93
47
 
94
- generator ||= PATTERNS.values[[PatternHelpers.hex_val(hash, 20, 1), PATTERNS.length - 1].min]
48
+ pattern.generate_me background_generator
49
+ pattern.generate_me structure_generator
95
50
 
96
- # Instantiate the generator with the needed references
97
- # and render the pattern to the svg object
98
- generator.new(svg, hash).render_to_svg
51
+ pattern
99
52
  end
100
53
  end
101
54
  end
@@ -1,17 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoPattern
2
4
  module PatternHelpers
3
- def self.hex_val(hash, index, length)
5
+ def hex_val(hash, index, length)
4
6
  hash[index, length || 1].to_i(16)
5
7
  end
6
8
 
7
9
  # Ruby implementation of Processing's map function
8
10
  # http://processing.org/reference/map_.html
9
- def self.map(value, v_min, v_max, d_min, d_max) # v for value, d for desired
11
+ # v for value, d for desired
12
+ def map(value, v_min, v_max, d_min, d_max)
10
13
  v_value = value.to_f # so it returns float
11
14
 
12
15
  v_range = v_max - v_min
13
16
  d_range = d_max - d_min
14
17
  (v_value - v_min) * d_range / v_range + d_min
15
18
  end
19
+
20
+ def html_to_rgb(color)
21
+ generate_rgb_string(::Color::RGB.from_html(color))
22
+ end
23
+
24
+ def html_to_rgb_for_string(seed, base_color)
25
+ hue_offset = map(seed.to_i(14, 3), 0, 4095, 0, 359)
26
+ sat_offset = seed.to_i(17, 1)
27
+ base_color = ::Color::RGB.from_html(base_color).to_hsl
28
+ base_color.hue = base_color.hue - hue_offset
29
+
30
+ base_color.saturation = if sat_offset % 2 == 0
31
+ base_color.saturation + sat_offset
32
+ else
33
+ base_color.saturation - sat_offset
34
+ end
35
+
36
+ generate_rgb_string(base_color.to_rgb)
37
+ end
38
+
39
+ def generate_rgb_string(rgb)
40
+ r = (rgb.r * 255).round
41
+ g = (rgb.g * 255).round
42
+ b = (rgb.b * 255).round
43
+
44
+ format("rgb(%<r>d, %<g>d, %<b>d)", r: r, g: g, b: b)
45
+ end
46
+
47
+ module_function :hex_val, :map, :html_to_rgb, :html_to_rgb_for_string, :generate_rgb_string
16
48
  end
17
49
  end