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
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