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,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class PatternPreset
5
+ private
6
+
7
+ attr_accessor :options
8
+
9
+ public
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ end
14
+
15
+ %i[fill_color_dark fill_color_light stroke_color stroke_opacity opacity_min opacity_max].each do |m|
16
+ define_method m do
17
+ options[m]
18
+ end
19
+ end
20
+
21
+ def update(opts)
22
+ options.merge! opts
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class PatternSieve
5
+ private
6
+
7
+ attr_reader :pattern_store, :requested_patterns, :seed, :available_patterns, :index
8
+
9
+ public
10
+
11
+ def initialize(requested_patterns, seed, pattern_store = PatternStore.new)
12
+ @requested_patterns = requested_patterns
13
+ @seed = seed
14
+ @pattern_store = pattern_store
15
+
16
+ @available_patterns = determine_available_patterns
17
+ @index = determine_index
18
+ end
19
+
20
+ def fetch
21
+ available_patterns[index]
22
+ end
23
+
24
+ private
25
+
26
+ def determine_index
27
+ [seed.to_i(20, 1), available_patterns.length - 1].min
28
+ end
29
+
30
+ def determine_available_patterns
31
+ patterns = Array(requested_patterns).compact
32
+
33
+ return pattern_store.all if patterns.empty?
34
+
35
+ patterns.map { |p| pattern_store[p] }.compact
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # rubocop:disable Naming/ConstantName
5
+ ChevronPattern = :chevrons
6
+ ConcentricCirclesPattern = :concentric_circles
7
+ DiamondPattern = :diamonds
8
+ HexagonPattern = :hexagons
9
+ MosaicSquaresPattern = :mosaic_squares
10
+ NestedSquaresPattern = :nested_squares
11
+ OctagonPattern = :octagons
12
+ OverlappingCirclesPattern = :overlapping_circles
13
+ OverlappingRingsPattern = :overlapping_rings
14
+ PlaidPattern = :plaid
15
+ PlusSignPattern = :plus_signs
16
+ SineWavePattern = :sine_waves
17
+ SquarePattern = :squares
18
+ TessellationPattern = :tessellation
19
+ TrianglePattern = :triangles
20
+ XesPattern = :xes
21
+ # rubocop:enable Naming/ConstantName
22
+
23
+ class PatternStore
24
+ private
25
+
26
+ attr_reader :store
27
+
28
+ public
29
+
30
+ def initialize
31
+ @store = {
32
+ chevrons: StructureGenerators::ChevronsGenerator,
33
+ concentric_circles: StructureGenerators::ConcentricCirclesGenerator,
34
+ diamonds: StructureGenerators::DiamondsGenerator,
35
+ hexagons: StructureGenerators::HexagonsGenerator,
36
+ mosaic_squares: StructureGenerators::MosaicSquaresGenerator,
37
+ nested_squares: StructureGenerators::NestedSquaresGenerator,
38
+ octagons: StructureGenerators::OctagonsGenerator,
39
+ overlapping_circles: StructureGenerators::OverlappingCirclesGenerator,
40
+ overlapping_rings: StructureGenerators::OverlappingRingsGenerator,
41
+ plaid: StructureGenerators::PlaidGenerator,
42
+ plus_signs: StructureGenerators::PlusSignsGenerator,
43
+ sine_waves: StructureGenerators::SineWavesGenerator,
44
+ squares: StructureGenerators::SquaresGenerator,
45
+ tessellation: StructureGenerators::TessellationGenerator,
46
+ triangles: StructureGenerators::TrianglesGenerator,
47
+ xes: StructureGenerators::XesGenerator
48
+ }
49
+ end
50
+
51
+ def [](pattern)
52
+ warn "String pattern references are deprecated as of 1.3.0" if pattern.is_a?(String)
53
+
54
+ store[pattern.to_s.to_sym]
55
+ end
56
+
57
+ def all
58
+ store.values
59
+ end
60
+
61
+ def known?(pattern)
62
+ store.key?(pattern.to_s.to_sym)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class PatternValidator
5
+ private
6
+
7
+ attr_reader :pattern_store
8
+
9
+ public
10
+
11
+ def initialize(pattern_store = PatternStore.new)
12
+ @pattern_store = pattern_store
13
+ end
14
+
15
+ def validate(requested_patterns)
16
+ message = "Error: At least one of the requested patterns \"#{requested_patterns.join(", ")}\" is invalid"
17
+
18
+ raise InvalidPatternError, message unless valid?(requested_patterns)
19
+
20
+ self
21
+ end
22
+
23
+ private
24
+
25
+ def valid?(requested_patterns)
26
+ requested_patterns.all? { |p| pattern_store.known? p }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/tasklib"
5
+ require "logger"
6
+
7
+ module GeoPattern
8
+ # Rake Task
9
+ #
10
+ # This task can be used to generate pattern files
11
+ class RakeTask < ::Rake::TaskLib
12
+ include ::Rake::DSL if defined?(::Rake::DSL)
13
+
14
+ # @!attribute [r] name
15
+ # Name of task.
16
+ attr_reader :name
17
+
18
+ # @!attribute [r] description
19
+ # A description for the task
20
+ attr_reader :description
21
+
22
+ # @!attribute [r] verbose (true)
23
+ # Use verbose output. If this is set to true, the task will print the
24
+ # executed spec command to stdout.
25
+ attr_reader :verbose
26
+
27
+ private
28
+
29
+ attr_reader :task_arguments, :task_block, :logger, :working_directory
30
+
31
+ # Create task instance
32
+ #
33
+ # @param [String] description
34
+ # A description for the task
35
+ #
36
+ # @param [String] name
37
+ # The name for the task (including namespace), e.g. namespace1:task1
38
+ #
39
+ # @param [Array] arguments
40
+ # Arguments for the task. Look
41
+ # [here](http://viget.com/extend/protip-passing-parameters-to-your-rake-tasks)
42
+ # for a better description on how to use arguments in rake tasks
43
+ #
44
+ # @yield
45
+ # A block which is called before the "run_task"-method is called. The
46
+ # parameters it taskes depends on the number of parameters the block
47
+ # can take. If the block is defined which two parameters, it takes two
48
+ # parameters from the paramter 'arguments'.
49
+ def initialize(opts = {}, &task_block)
50
+ @options = {
51
+ description: nil,
52
+ name: GeoPattern::Helpers.underscore(self.class.to_s.split("::").slice(-2..-1).join(":").gsub(/Task$/, "")),
53
+ arguments: [],
54
+ logger: ::Logger.new($stderr),
55
+ working_directory: Dir.getwd
56
+ }.merge opts
57
+
58
+ before_initialize
59
+
60
+ raise ArgumentError, :description if @options[:description].nil?
61
+
62
+ @description = @options[:description]
63
+ @task_arguments = Array(@options[:arguments])
64
+ @task_block = task_block
65
+ @logger = @options[:logger]
66
+ @working_directory = @options[:working_directory]
67
+ @name = @options[:name]
68
+
69
+ after_initialize
70
+
71
+ define_task
72
+ end
73
+
74
+ # Run code after initialize
75
+ def after_initialize
76
+ end
77
+
78
+ # Run code before initialize
79
+ def before_initialize
80
+ end
81
+
82
+ # Define task
83
+ def define_task
84
+ desc description unless ::Rake.application.last_description
85
+
86
+ task name, *task_arguments do |_, task_args|
87
+ RakeFileUtils.__send__(:verbose, verbose) do
88
+ instance_exec(*[self, task_args].slice(0, task_block.arity), &task_block) if task_block.respond_to? :call
89
+ run_task verbose
90
+ end
91
+ end
92
+ end
93
+
94
+ # Run code if task is executed
95
+ def run_task(_verbose)
96
+ end
97
+
98
+ public
99
+
100
+ # Binding to instance
101
+ def instance_binding
102
+ binding
103
+ end
104
+
105
+ # Include module in instance
106
+ def include(modules)
107
+ modules = Array(modules)
108
+
109
+ modules.each { |m| self.class.include m }
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ # Roles
5
+ module Roles
6
+ # A comparable metadata
7
+ module ComparableMetadata
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ def generator?(value)
13
+ return false unless value.is_a?(Class) || value.nil?
14
+ return true if value.nil? && @generator
15
+
16
+ return true if value == generator
17
+
18
+ false
19
+ end
20
+
21
+ # Class Methods
22
+ module ClassMethods
23
+ # Define comparators
24
+ def def_comparators(*methods)
25
+ methods.flatten.each do |m|
26
+ define_method "#{m}?".to_sym do |value|
27
+ return true if value.nil? && public_send(m)
28
+ return true if value == public_send(m)
29
+
30
+ false
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module Roles
5
+ module NamedGenerator
6
+ def name?(other_name)
7
+ name == other_name.to_sym
8
+ end
9
+
10
+ def name
11
+ Helpers.underscore(Helpers.demodulize(self.class).gsub(/Generator/, "")).to_sym
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class Seed
5
+ private
6
+
7
+ attr_reader :seed
8
+
9
+ public
10
+
11
+ def initialize(string)
12
+ @seed = Digest::SHA1.hexdigest string.to_s
13
+ end
14
+
15
+ def [](*args)
16
+ seed[*args]
17
+ end
18
+
19
+ def to_i(index, length)
20
+ seed[index, length || 1].to_i(16)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ class Structure
5
+ include Roles::ComparableMetadata
6
+
7
+ extend Forwardable
8
+
9
+ attr_reader :image, :preset, :name, :generator
10
+
11
+ def_delegators :@preset, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max
12
+
13
+ def initialize(options)
14
+ @image = options[:image]
15
+ @preset = options[:preset]
16
+ @name = options[:name]
17
+ @generator = options[:generator]
18
+
19
+ raise ArgumentError, "Argument name is missing" if @name.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 :name, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class BaseGenerator
6
+ include Roles::NamedGenerator
7
+
8
+ private
9
+
10
+ attr_reader :svg, :seed, :fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max, :preset
11
+ attr_accessor :height, :width
12
+
13
+ public
14
+
15
+ def initialize(seed, preset, svg = SvgImage.new)
16
+ @svg = svg
17
+ @seed = seed
18
+ @preset = preset
19
+
20
+ @fill_color_dark = @preset.fill_color_dark
21
+ @fill_color_light = @preset.fill_color_light
22
+ @stroke_color = @preset.stroke_color
23
+ @stroke_opacity = @preset.stroke_opacity
24
+ @opacity_min = @preset.opacity_min
25
+ @opacity_max = @preset.opacity_max
26
+
27
+ @height = 100
28
+ @width = 100
29
+
30
+ after_initialize
31
+ end
32
+
33
+ def generate(pattern)
34
+ pattern.structure = Structure.new(image: svg_image, preset: preset, generator: self.class, name: name)
35
+
36
+ pattern.height = height
37
+ pattern.width = width
38
+
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ # Set Svg Image
45
+ def svg_image
46
+ image = generate_structure
47
+ image.height = height
48
+ image.width = width
49
+
50
+ image
51
+ end
52
+
53
+ # Hook for generators
54
+ def after_initialize
55
+ end
56
+
57
+ # Generate the structure
58
+ def generate_structure
59
+ raise NotImplementedError
60
+ end
61
+
62
+ def hex_val(index, len)
63
+ seed.to_i(index, len)
64
+ end
65
+
66
+ def fill_color(val)
67
+ val.even? ? fill_color_light : fill_color_dark
68
+ end
69
+
70
+ def opacity(val)
71
+ map(val, 0, 15, opacity_min, opacity_max)
72
+ end
73
+
74
+ def map(value, v_min, v_max, d_min, d_max)
75
+ PatternHelpers.map(value, v_min, v_max, d_min, d_max)
76
+ end
77
+
78
+ protected
79
+
80
+ def build_plus_shape(square_size)
81
+ [
82
+ "rect(#{square_size},0,#{square_size},#{square_size * 3})",
83
+ "rect(0, #{square_size},#{square_size * 3},#{square_size})"
84
+ ]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class ChevronsGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :chevron_height, :chevron_width, :chevron
9
+
10
+ def after_initialize
11
+ @chevron_width = map(hex_val(0, 1), 0, 15, 30, 80)
12
+ @chevron_height = map(hex_val(0, 1), 0, 15, 30, 80)
13
+ @chevron = build_chevron_shape(chevron_width, chevron_height)
14
+
15
+ self.height = chevron_height * 6 * 0.66
16
+ self.width = chevron_width * 6
17
+ end
18
+
19
+ def generate_structure
20
+ i = 0
21
+ 6.times do |y|
22
+ 6.times do |x|
23
+ val = hex_val(i, 1)
24
+ opacity = opacity(val)
25
+ fill = fill_color(val)
26
+
27
+ styles = {
28
+ "stroke" => stroke_color,
29
+ "stroke-opacity" => stroke_opacity,
30
+ "fill" => fill,
31
+ "fill-opacity" => opacity,
32
+ "stroke-width" => 1
33
+ }
34
+
35
+ svg.group(chevron, styles.merge("transform" => "translate(#{x * chevron_width},#{y * chevron_height * 0.66 - chevron_height / 2})"))
36
+
37
+ # Add an extra row at the end that matches the first row, for tiling.
38
+ if y == 0
39
+ svg.group(chevron, styles.merge("transform" => "translate(#{x * chevron_width},#{6 * chevron_height * 0.66 - chevron_height / 2})"))
40
+ end
41
+ i += 1
42
+ end
43
+ end
44
+
45
+ svg
46
+ end
47
+
48
+ def build_chevron_shape(width, height)
49
+ e = height * 0.66
50
+ [
51
+ %{polyline("0,0,#{width / 2},#{height - e},#{width / 2},#{height},0,#{e},0,0")},
52
+ %{polyline("#{width / 2},#{height - e},#{width},0,#{width},#{e},#{width / 2},#{height},#{width / 2},#{height - e}")}
53
+ ]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class ConcentricCirclesGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :scale, :ring_size, :stroke_width
9
+
10
+ def after_initialize
11
+ @scale = hex_val(0, 1)
12
+ @ring_size = map(scale, 0, 15, 10, 60)
13
+ @stroke_width = ring_size / 5
14
+
15
+ self.width = self.height = (ring_size + stroke_width) * 6
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
+ fill = fill_color(val)
25
+
26
+ svg.circle(
27
+ x * ring_size + x * stroke_width + (ring_size + stroke_width) / 2,
28
+ y * ring_size + y * stroke_width + (ring_size + stroke_width) / 2,
29
+ ring_size / 2,
30
+ "fill" => "none",
31
+ "stroke" => fill,
32
+ "style" => {
33
+ "opacity" => opacity,
34
+ "stroke-width" => "#{stroke_width}px"
35
+ }
36
+ )
37
+
38
+ val = hex_val(39 - i, 1)
39
+ opacity = opacity(val)
40
+ fill = fill_color(val)
41
+
42
+ svg.circle(
43
+ x * ring_size + x * stroke_width + (ring_size + stroke_width) / 2,
44
+ y * ring_size + y * stroke_width + (ring_size + stroke_width) / 2,
45
+ ring_size / 4,
46
+ "fill" => fill,
47
+ "fill-opacity" => opacity
48
+ )
49
+
50
+ i += 1
51
+ end
52
+ end
53
+
54
+ svg
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GeoPattern
4
+ module StructureGenerators
5
+ class DiamondsGenerator < BaseGenerator
6
+ private
7
+
8
+ attr_reader :diamond_height, :diamond_width, :diamond
9
+
10
+ def after_initialize
11
+ @diamond_width = map(hex_val(0, 1), 0, 15, 10, 50)
12
+ @diamond_height = map(hex_val(1, 1), 0, 15, 10, 50)
13
+ @diamond = build_diamond_shape(diamond_width, diamond_height)
14
+
15
+ self.height = diamond_height * 3
16
+ self.width = diamond_width * 6
17
+ end
18
+
19
+ def generate_structure
20
+ i = 0
21
+ 6.times do |y|
22
+ 6.times do |x|
23
+ val = hex_val(i, 1)
24
+ opacity = opacity(val)
25
+ fill = fill_color(val)
26
+
27
+ styles = {
28
+ "fill" => fill,
29
+ "fill-opacity" => opacity,
30
+ "stroke" => stroke_color,
31
+ "stroke-opacity" => stroke_opacity
32
+ }
33
+
34
+ dx = (y % 2 == 0) ? 0 : diamond_width / 2
35
+
36
+ svg.polyline(diamond, styles.merge(
37
+ "transform" => "translate(#{x * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * y - diamond_height / 2})"
38
+ ))
39
+
40
+ # Add an extra one at top-right, for tiling.
41
+ if x == 0
42
+ svg.polyline(diamond, styles.merge(
43
+ "transform" => "translate(#{6 * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * y - diamond_height / 2})"
44
+ ))
45
+ end
46
+
47
+ # Add an extra row at the end that matches the first row, for tiling.
48
+ if y == 0
49
+ svg.polyline(diamond, styles.merge(
50
+ "transform" => "translate(#{x * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * 6 - diamond_height / 2})"
51
+ ))
52
+ end
53
+
54
+ # Add an extra one at bottom-right, for tiling.
55
+ if x == 0 && y == 0
56
+ svg.polyline(diamond, styles.merge(
57
+ "transform" => "translate(#{6 * diamond_width - diamond_width / 2 + dx}, #{diamond_height / 2 * 6 - diamond_height / 2})"
58
+ ))
59
+ end
60
+ i += 1
61
+ end
62
+ end
63
+
64
+ svg
65
+ end
66
+
67
+ def build_diamond_shape(width, height)
68
+ "#{width / 2}, 0, #{width}, #{height / 2}, #{width / 2}, #{height}, 0, #{height / 2}"
69
+ end
70
+ end
71
+ end
72
+ end