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.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +30 -0
- data/.gitignore +1 -0
- data/.simplecov +26 -0
- data/Gemfile +17 -17
- data/README.md +154 -29
- data/Rakefile +72 -13
- data/fixtures/generated_patterns/chevrons.svg +1 -0
- data/fixtures/generated_patterns/concentric_circles.svg +1 -0
- data/fixtures/generated_patterns/diamonds.svg +1 -0
- data/fixtures/generated_patterns/diamonds_with_base_color.svg +1 -0
- data/fixtures/generated_patterns/diamonds_with_color.svg +1 -0
- data/fixtures/generated_patterns/hexagons.svg +1 -0
- data/fixtures/generated_patterns/mosaic_squares.svg +1 -0
- data/fixtures/generated_patterns/nested_squares.svg +1 -0
- data/fixtures/generated_patterns/octagons.svg +1 -0
- data/fixtures/generated_patterns/overlapping_circles.svg +1 -0
- data/fixtures/generated_patterns/overlapping_rings.svg +1 -0
- data/fixtures/generated_patterns/plaid.svg +1 -0
- data/fixtures/generated_patterns/plus_signs.svg +1 -0
- data/fixtures/generated_patterns/sine_waves.svg +1 -0
- data/fixtures/generated_patterns/squares.svg +1 -0
- data/fixtures/generated_patterns/tessellation.svg +1 -0
- data/fixtures/generated_patterns/triangles.svg +1 -0
- data/fixtures/generated_patterns/xes.svg +1 -0
- data/geo_pattern.gemspec +19 -17
- data/lib/geo_pattern/background.rb +27 -0
- data/lib/geo_pattern/background_generators/solid_generator.rb +54 -0
- data/lib/geo_pattern/color.rb +27 -0
- data/lib/geo_pattern/color_generators/base_color_generator.rb +57 -0
- data/lib/geo_pattern/color_generators/simple_generator.rb +29 -0
- data/lib/geo_pattern/color_preset.rb +32 -0
- data/lib/geo_pattern/errors.rb +9 -0
- data/lib/geo_pattern/geo_pattern_task.rb +61 -0
- data/lib/geo_pattern/helpers.rb +50 -1
- data/lib/geo_pattern/pattern.rb +86 -0
- data/lib/geo_pattern/pattern_generator.rb +34 -81
- data/lib/geo_pattern/pattern_helpers.rb +34 -2
- data/lib/geo_pattern/pattern_preset.rb +25 -0
- data/lib/geo_pattern/pattern_sieve.rb +38 -0
- data/lib/geo_pattern/pattern_store.rb +65 -0
- data/lib/geo_pattern/pattern_validator.rb +29 -0
- data/lib/geo_pattern/rake_task.rb +112 -0
- data/lib/geo_pattern/roles/comparable_metadata.rb +37 -0
- data/lib/geo_pattern/roles/named_generator.rb +15 -0
- data/lib/geo_pattern/seed.rb +23 -0
- data/lib/geo_pattern/structure.rb +27 -0
- data/lib/geo_pattern/structure_generators/base_generator.rb +88 -0
- data/lib/geo_pattern/structure_generators/chevrons_generator.rb +57 -0
- data/lib/geo_pattern/structure_generators/concentric_circles_generator.rb +58 -0
- data/lib/geo_pattern/structure_generators/diamonds_generator.rb +72 -0
- data/lib/geo_pattern/structure_generators/hexagons_generator.rb +69 -0
- data/lib/geo_pattern/structure_generators/mosaic_squares_generator.rb +85 -0
- data/lib/geo_pattern/structure_generators/nested_squares_generator.rb +62 -0
- data/lib/geo_pattern/structure_generators/octagons_generator.rb +45 -0
- data/lib/geo_pattern/structure_generators/overlapping_circles_generator.rb +57 -0
- data/lib/geo_pattern/structure_generators/overlapping_rings_generator.rb +57 -0
- data/lib/geo_pattern/structure_generators/plaid_generator.rb +55 -0
- data/lib/geo_pattern/structure_generators/plus_signs_generator.rb +68 -0
- data/lib/geo_pattern/structure_generators/sine_waves_generator.rb +45 -0
- data/lib/geo_pattern/structure_generators/squares_generator.rb +37 -0
- data/lib/geo_pattern/structure_generators/tessellation_generator.rb +105 -0
- data/lib/geo_pattern/structure_generators/triangles_generator.rb +64 -0
- data/lib/geo_pattern/structure_generators/xes_generator.rb +74 -0
- data/lib/geo_pattern/svg_image.rb +103 -0
- data/lib/geo_pattern/version.rb +3 -1
- data/lib/geo_pattern.rb +54 -30
- data/script/bootstrap +30 -0
- data/script/console +9 -0
- data/script/test +21 -0
- data/spec/background_generators/solid_generator_spec.rb +52 -0
- data/spec/background_spec.rb +27 -0
- data/spec/color_generators/base_color_generator_spec.rb +33 -0
- data/spec/color_generators/simple_generator_spec.rb +14 -0
- data/spec/color_preset_spec.rb +55 -0
- data/spec/color_spec.rb +17 -0
- data/spec/geo_pattern_spec.rb +98 -26
- data/spec/helpers_spec.rb +67 -0
- data/spec/pattern_preset_spec.rb +43 -0
- data/spec/pattern_sieve_spec.rb +68 -0
- data/spec/pattern_spec.rb +74 -0
- data/spec/pattern_store_spec.rb +49 -0
- data/spec/pattern_validator_spec.rb +30 -0
- data/spec/seed_spec.rb +16 -0
- data/spec/spec_helper.rb +9 -13
- data/spec/structure_generators/chevrons_generator_spec.rb +7 -0
- data/spec/structure_generators/concentric_circles_generator_spec.rb +7 -0
- data/spec/structure_generators/diamonds_generator_spec.rb +7 -0
- data/spec/structure_generators/hexagons_generator_spec.rb +7 -0
- data/spec/structure_generators/mosaic_squares_generator_spec.rb +7 -0
- data/spec/structure_generators/nested_squares_generator_spec.rb +7 -0
- data/spec/structure_generators/octagons_generator_spec.rb +7 -0
- data/spec/structure_generators/overlapping_circles_generator_spec.rb +7 -0
- data/spec/structure_generators/overlapping_rings_generator_spec.rb +7 -0
- data/spec/structure_generators/plaid_generator_spec.rb +7 -0
- data/spec/structure_generators/plus_signs_generator_spec.rb +7 -0
- data/spec/structure_generators/sine_waves_generator_spec.rb +7 -0
- data/spec/structure_generators/squares_generator_spec.rb +7 -0
- data/spec/structure_generators/tessellation_generator_spec.rb +7 -0
- data/spec/structure_generators/triangles_generator_spec.rb +7 -0
- data/spec/structure_generators/xes_generator_spec.rb +7 -0
- data/spec/structure_spec.rb +40 -0
- data/spec/support/aruba.rb +6 -6
- data/spec/support/helpers/fixtures.rb +14 -0
- data/spec/support/kernel.rb +46 -0
- data/spec/support/matchers/image.rb +19 -0
- data/spec/support/matchers/name.rb +17 -0
- data/spec/support/rspec.rb +4 -2
- data/spec/support/shared_examples/generator.rb +48 -0
- data/spec/support/shared_examples/pattern.rb +33 -0
- data/spec/support/shared_examples/pattern_name.rb +9 -0
- data/spec/support/shared_examples/structure.rb +50 -0
- data/spec/support/string.rb +3 -2
- data/spec/svg_spec.rb +8 -6
- metadata +105 -37
- data/.rubocop.yml +0 -13
- data/lib/geo_pattern/pattern/base_pattern.rb +0 -47
- data/lib/geo_pattern/pattern/chevron_pattern.rb +0 -45
- data/lib/geo_pattern/pattern/concentric_circles_pattern.rb +0 -47
- data/lib/geo_pattern/pattern/diamond_pattern.rb +0 -56
- data/lib/geo_pattern/pattern/hexagon_pattern.rb +0 -57
- data/lib/geo_pattern/pattern/mosaic_squares_pattern.rb +0 -76
- data/lib/geo_pattern/pattern/nested_squares_pattern.rb +0 -51
- data/lib/geo_pattern/pattern/octagon_pattern.rb +0 -35
- data/lib/geo_pattern/pattern/overlapping_circles_pattern.rb +0 -46
- data/lib/geo_pattern/pattern/overlapping_rings_pattern.rb +0 -46
- data/lib/geo_pattern/pattern/plaid_pattern.rb +0 -49
- data/lib/geo_pattern/pattern/plus_sign_pattern.rb +0 -53
- data/lib/geo_pattern/pattern/sine_wave_pattern.rb +0 -36
- data/lib/geo_pattern/pattern/square_pattern.rb +0 -27
- data/lib/geo_pattern/pattern/tessellation_pattern.rb +0 -93
- data/lib/geo_pattern/pattern/triangle_pattern.rb +0 -51
- data/lib/geo_pattern/pattern/xes_pattern.rb +0 -58
- data/lib/geo_pattern/svg.rb +0 -77
- 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
|