coloryze 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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