coloryze 2.0.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.
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'color/hsb'
5
+ require 'color/rgb'
6
+ require 'color/util'
7
+
8
+ # shows a set of colors, in text and html
9
+
10
+ module Color
11
+
12
+ class Palette
13
+
14
+ attr_reader :background
15
+ attr_reader :saturation_start
16
+ attr_reader :saturation_end
17
+ attr_reader :saturation_step
18
+ attr_reader :brightness_start
19
+ attr_reader :brightness_end
20
+ attr_reader :brightness_step
21
+
22
+ def initialize(args = Hash.new)
23
+ @background = args[:background]
24
+
25
+ @saturation_start = args[:saturation_start] || 0
26
+ @saturation_end = args[:saturation_end] || 100
27
+ @saturation_step = args[:saturation_step] || 25
28
+
29
+ @brightness_start = args[:brightness_start] || 0
30
+ @brightness_end = args[:brightness_end] || 100
31
+ @brightness_step = args[:brightness_step] || 25
32
+ end
33
+
34
+ # Returns nested hashes: hue => saturation => brightness => rgb
35
+ def colors_by_hue
36
+ by_hue = Hash.new
37
+
38
+ colors.each do |color|
39
+ by_hue[color.hue] ||= Hash.new
40
+ by_hue[color.hue][color.saturation] ||= Hash.new
41
+ by_hue[color.hue][color.saturation][color.brightness] = color
42
+ end
43
+
44
+ by_hue
45
+ end
46
+
47
+ def print_as_text(io = $stdout)
48
+ colors.sort.each do |hsb|
49
+ io.printf "%4.1f %3d %3d %06x\n", hsb.hue, hsb.saturation, hsb.brightness, hsb.to_rgb
50
+ end
51
+ end
52
+
53
+ def print_as_html(io = $stdout)
54
+ puts "@background: #{@background}"
55
+
56
+ bg = @background || "FFFFFF"
57
+ io.puts "<div style=\"background-color: " + bg + ";\">"
58
+
59
+ print_html_tables(io)
60
+
61
+ io.puts "</div>"
62
+ end
63
+
64
+ def print_html_hue_line(io, hue, colspan)
65
+ # if no background or is A+ (dark), then
66
+ textcolor = @background.nil? || @background.index(%r{[A-Z]\w}) ? "000000" : "FFFFFF"
67
+ io.puts " <td colspan=\"" + colspan.to_s + "\" style=\"color: #" + textcolor + ";\">hue " + hue.to_s + "&deg;</td>"
68
+ end
69
+
70
+ def print_html_color(io, saturation, brightness, rgb, cellwidth)
71
+ rgbstr = sprintf("%06x", rgb)
72
+ io.puts " <td style=\"width: " + cellwidth.to_s + "%; vertical-align: bottom; border:2px outset; border-collapse:collapse;\">"
73
+ io.puts " <table width=\"100%\" border=\"0\" border-width=\"4px\">"
74
+ io.print " <tr><td"
75
+ if @background
76
+ print "style=\"color: " + rgbstr + ";\""
77
+ end
78
+ io.printf ">#%s; s: %3.2f; b: %3.2f\n</td></tr>", rgbstr, saturation, brightness
79
+ io.puts " <tr><td style=\"background-color: " + rgbstr + "; vertical-align: bottom;\"><br>&nbsp;</td></tr>"
80
+ io.puts " </table>"
81
+ io.puts " </td>"
82
+ end
83
+
84
+ #$$$ todo: change to return hsbs
85
+
86
+ def colors
87
+ raise "abstract method: colors"
88
+ end
89
+
90
+ end
91
+
92
+ end
data/lib/color/rgb.rb ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'color/util'
5
+ require 'color/hsb'
6
+ require 'rubygems'
7
+ require 'riel'
8
+
9
+ module Color
10
+
11
+ class RGB
12
+ include Loggable
13
+
14
+ attr_accessor :red, :green, :blue
15
+
16
+ RGB_VALUES = '([0-9a-f]{2})'
17
+ # not supported, although in CSS: RGB_REGEXP = Regexp.new '(' + RGB_VALUES + '){3}', Regexp::IGNORECASE
18
+ RRGGBB_REGEXP = Regexp.new '^\#?' + (RGB_VALUES * 3) + '$', Regexp::IGNORECASE # '$
19
+
20
+ class << self
21
+ alias_method :new_orig, :new
22
+ def new(*args)
23
+ if args
24
+ if args.size == 3
25
+ new_orig(*args)
26
+ elsif args.size == 1 and args[0].kind_of? String
27
+ str = args[0].sub(%r{^\#}, '') # optional
28
+ # str.hex does not validate, so we do here:
29
+ if md = matches_rgb_string?(str)
30
+ r, g, b = (1 .. 3).collect { |idx| md[idx].hex }
31
+ new_orig r, g, b
32
+ else
33
+ raise RuntimeError.new("invalid RGB string '#{str}: does not match #{RRGGBB_REGEXP.source}")
34
+ end
35
+ else
36
+ raise RuntimeError.new("invalid arguments '#{args.inspect}': expecting 0, 1, or 3")
37
+ end
38
+ else
39
+ new_orig 0, 0, 0
40
+ end
41
+ end
42
+
43
+ def matches_rgb_string?(val)
44
+ val.kind_of?(String) && RRGGBB_REGEXP.match(val)
45
+ end
46
+ end
47
+
48
+ def initialize(red, green, blue)
49
+ [ [ red, "red" ], [ green, "green" ], [ blue, "blue" ] ].each do |val, str|
50
+ Color::check_constraint(val, str, 0, 255)
51
+ end
52
+
53
+ @red = red.to_i
54
+ @green = green.to_i
55
+ @blue = blue.to_i
56
+ end
57
+
58
+ def to_s
59
+ "#{red},#{green},#{blue}"
60
+ end
61
+
62
+ def to_i
63
+ (red << 16) | (green << 8) | blue
64
+ end
65
+
66
+ def <=>(other)
67
+ to_i <=> other.to_i
68
+ end
69
+
70
+ def ==(other)
71
+ to_i == other.to_i
72
+ end
73
+
74
+ # Converts RGB to an HSB object.
75
+ def to_hsb
76
+ # this is not the only implementation of RGB to HSB, but it works.
77
+
78
+ # scale from 0..255 to 0..1
79
+ r, g, b = [ red, green, blue ].collect { |v| v / 255.0 }
80
+
81
+ log "r", r
82
+ log "g", g
83
+ log "b", b
84
+
85
+ minrgb = [ r, g, b ].min
86
+ log "minrgb", minrgb
87
+ maxrgb = [ r, g, b ].max
88
+ log "maxrgb", maxrgb
89
+ delta = maxrgb - minrgb
90
+ log "delta", delta
91
+
92
+ hue = nil
93
+ saturation = nil
94
+ brightness = maxrgb
95
+
96
+ if delta == 0
97
+ # this is a gray, no chroma...
98
+ hue = 0
99
+ saturation = 0
100
+ else # chromatic data...
101
+ saturation = delta / maxrgb
102
+
103
+ del_red, del_green, del_blue = [ r, g, b ].collect do |v|
104
+ (((maxrgb - v) / 6.0) + (delta / 2.0)) / delta
105
+ end
106
+
107
+ if r == maxrgb
108
+ hue = del_blue - del_green
109
+ elsif g == maxrgb
110
+ hue = (1.0 / 3) + del_red - del_blue
111
+ elsif b == maxrgb
112
+ hue = (2.0 / 3) + del_green - del_red
113
+ end
114
+
115
+ if hue < 0
116
+ hue += 1
117
+ end
118
+
119
+ if hue > 1
120
+ hue -= 1
121
+ end
122
+ end
123
+
124
+ log "hue", hue
125
+ log "saturation", saturation
126
+ log "brightness", brightness
127
+
128
+ # hue is to one decimal place; saturation and brightness to three
129
+ Color::HSB.new Color::round(hue * 360, 1), Color::round(saturation, 3), Color::round(brightness, 3)
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'color/hsb'
5
+ require 'color/rgb'
6
+ require 'color/util'
7
+ require 'color/palette'
8
+ require 'rubygems'
9
+ require 'riel'
10
+
11
+ # shows a set of colors, in text and html
12
+
13
+ module Color
14
+
15
+ class SamplePalette < Palette
16
+ include Loggable
17
+
18
+ def initialize(hues, args = Hash.new)
19
+ super(args)
20
+
21
+ n_samples = args[:samples]
22
+ n_total_samples = args[:total_samples]
23
+ n_per_hue = if n_samples
24
+ hues.collect { n_samples }
25
+ else
26
+ Color::distribution(n_total_samples, hues.length)
27
+ end
28
+
29
+ info "n_per_hue: #{n_per_hue}"
30
+
31
+ @colors = Array.new
32
+
33
+ hues.each_with_index do |hue, idx|
34
+ n_per_hue[idx].times do
35
+ # don't keep trying ...
36
+ 10.times do
37
+ sat = Color::random_in_range(saturation_start, saturation_end, saturation_step)
38
+ brt = Color::random_in_range(brightness_start, brightness_end, brightness_step)
39
+
40
+ hsb = HSB.new hue, sat / 100.0, brt / 100.0
41
+
42
+ if @colors.include?(hsb)
43
+ # try, try again
44
+ else
45
+ @colors << hsb
46
+ break
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def colors
54
+ @colors
55
+ end
56
+
57
+ def print_html_tables(io = $stdout)
58
+ n_columns = 1 + (@brightness_end - @brightness_start) / @brightness_step
59
+
60
+ colors_by_hue.sort.each do |hue, by_sat|
61
+ io.puts "<table width=\"100%\">"
62
+ io.puts " <tr>"
63
+ print_html_hue_line(io, hue, 1)
64
+ io.puts " </tr>"
65
+
66
+ by_sat.sort.each do |sat, by_brt|
67
+ by_brt.sort.reverse.each do |brt, hsb|
68
+ io.puts " <tr>"
69
+ print_html_color(io, sat, brt, hsb.to_rgb, 100)
70
+ io.puts " </tr>"
71
+ end
72
+ end
73
+ io.puts "</table>"
74
+ io.puts "<br/>"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ # creates schemes
5
+
6
+ require 'color/rgb'
7
+
8
+ module Color
9
+
10
+ # classes or methods, or factory?
11
+
12
+ class Scheme
13
+ include Loggable
14
+
15
+ # creates a hue from the value, processing it as a hue (0-360) or as
16
+ # RRGGBB, or generating a random one, if nil or an empty string.
17
+
18
+ def self.create_hue(value)
19
+ if value.nil?
20
+ (rand * 360).round
21
+ elsif value.kind_of? Numeric
22
+ value.round % 360
23
+ elsif value.kind_of? String
24
+ if %r{^\d{3}$}.match(value)
25
+ value.to_i.round % 360
26
+ elsif md = Color::RGB::RRGGBB_REGEXP.match(value)
27
+ Color::RGB.new(value).to_hsb.hue
28
+ else
29
+ raise RuntimeError.new("invalid hue value '#{value}")
30
+ end
31
+ else
32
+ raise RuntimeError.new("invalid hue value '#{value}")
33
+ end
34
+ end
35
+
36
+ attr_reader :hues
37
+
38
+ def initialize
39
+ @hues = Array.new
40
+ end
41
+
42
+ def add_hue(val)
43
+ hue = self.class.create_hue(val)
44
+ @hues << hue
45
+ hue
46
+ end
47
+ end
48
+
49
+ class Monochromatic < Scheme
50
+ def initialize(val)
51
+ super()
52
+
53
+ add_hue(val)
54
+ end
55
+ end
56
+
57
+ class Complementary < Scheme
58
+ def initialize(val)
59
+ super()
60
+
61
+ hue = add_hue(val)
62
+ add_hue(hue + 180)
63
+ end
64
+ end
65
+
66
+ class Triadic < Scheme
67
+ def initialize(val)
68
+ super()
69
+
70
+ hue = add_hue(val)
71
+ add_hue(hue + 120)
72
+ add_hue(hue + 240)
73
+ end
74
+ end
75
+
76
+ class SplitComplementary < Scheme
77
+ def initialize(val, deg)
78
+ super()
79
+
80
+ hue = add_hue(val)
81
+ add_hue(hue + 180 + deg)
82
+ add_hue(hue + 180 - deg)
83
+ end
84
+ end
85
+
86
+ class DoubleComplementary < Scheme
87
+ def initialize(val0, val1)
88
+ super()
89
+
90
+ hue0 = add_hue(val0)
91
+ hue1 = add_hue(val1)
92
+
93
+ add_hue(hue0 + 180)
94
+ add_hue(hue1 + 180)
95
+ end
96
+ end
97
+
98
+ class Analogous < Scheme
99
+ def initialize(val, deg)
100
+ super()
101
+
102
+ hue = add_hue(val)
103
+
104
+ add_hue(hue - deg)
105
+ add_hue(hue + deg)
106
+ end
107
+ end
108
+
109
+ class Hues < Scheme
110
+ def initialize(startval, endval, interval)
111
+ super()
112
+
113
+ starthue = self.class.create_hue(startval)
114
+ info "starthue: #{starthue}"
115
+ endhue = self.class.create_hue(endval)
116
+ info "endhue: #{endhue}"
117
+
118
+ (starthue .. endhue).step(interval) do |hue|
119
+ add_hue(hue)
120
+ end
121
+ end
122
+ end
123
+
124
+ end
data/lib/color/util.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ module Color # should be riel
5
+
6
+ def self.check_constraint(val, str, minimum, maximum)
7
+ if val < minimum || val > maximum
8
+ raise RuntimeError.new("#{str} must be (#{minimum} .. #{maximum}), not: #{val}")
9
+ end
10
+ end
11
+
12
+ def self.round(num, places)
13
+ (num * 10 ** places).round / (10 ** places).to_f
14
+ end
15
+
16
+ # returns an array, of length len, of numbers, equally (or near-equally)
17
+ # distributed for the total
18
+
19
+ def self.distribution(total, len)
20
+ base_per = total.to_f / len
21
+ pivot = total - (base_per.floor * len)
22
+
23
+ (0 ... len).collect { |idx| base_per.floor + (idx < pivot ? 1 : 0) }
24
+ end
25
+
26
+ def self.random_in_range(from, to, interval)
27
+ from + rand((to.to_f - from) / interval).round * interval
28
+ end
29
+
30
+ end