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
data/geo_pattern.gemspec
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
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
|
|
5
|
+
require "geo_pattern/version"
|
|
5
6
|
|
|
6
7
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name
|
|
8
|
-
spec.version
|
|
9
|
-
spec.authors
|
|
10
|
-
spec.email
|
|
11
|
-
spec.summary
|
|
12
|
-
spec.description
|
|
13
|
-
spec.homepage
|
|
14
|
-
spec.license
|
|
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.
|
|
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
|
|
22
|
-
spec.add_development_dependency
|
|
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,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
|
data/lib/geo_pattern/helpers.rb
CHANGED
|
@@ -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
|
-
|
|
4
|
-
:base_color => '#933c3c'
|
|
5
|
-
}
|
|
5
|
+
private
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
Base64.strict_encode64(svg_string)
|
|
51
|
-
end
|
|
31
|
+
seed = Seed.new(string)
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
33
|
+
pattern_validator = PatternValidator.new
|
|
34
|
+
pattern_validator.validate(requested_patterns)
|
|
56
35
|
|
|
57
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
80
|
-
|
|
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
|
-
|
|
48
|
+
pattern.generate_me background_generator
|
|
49
|
+
pattern.generate_me structure_generator
|
|
95
50
|
|
|
96
|
-
|
|
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
|
|
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
|
-
|
|
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
|