1
# colormap.rb: a way to map values to colors
2
# copyright (c) 2009 by Vincent Fourmond
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details (in the COPYING file).
14
require 'ctioga2/utils'
17
# This module contains all the classes used by ctioga
20
Version::register_svn_info('$Revision: 199 $', '$Date: 2010-11-30 00:48:26 +0100 (Tue, 30 Nov 2010) $')
27
# A mapping Z values -> color.
29
# It can be a simple two-point gradient, but it can also be much
32
# Basically, a ColorMap is a series of colors with an optional Z
33
# value (taken as the average of the ones around if missing) + a
34
# color for above and a color for below.
36
# @todo For now, ColorMap relies on the intrisic tioga color
37
# map, but it would be interesting to implement that "by hand"
38
# for the case when a byte of resolution isn't enough (which are
39
# going to be rare, I think)
45
# Corresponding colors
48
# Colors for points of Z value below and above the limit;
49
# _nil_ for no specific value, :mask for masking them out
51
# @todo These are currently not implemented.
52
attr_accessor :below, :above
54
# Whether the map follows RGB (true) or HLS (false). On by
57
# It does not change anything with respect to how the colors
58
# are interpreted: whatever happens, the values are RGB.
61
def initialize(values = [], colors = [])
68
# Creates a ColorMap from a text specification of the kind:
70
# Red--Blue(1.0)--Green
72
# The specification can optionally be surrounded by colors with ::
74
# Green::Red--Blue::Orange
76
# Means that Green are for colors below, Orange for
77
# above. These colors can also be "cut" or "mask", meaning
78
# that the corresponding side isn't displayed.
79
def self.from_text(str)
82
re = /natural:?/i # Not too bad ?
91
if l.size == 2 # This is the complex case
102
## @todo More and more I find that this metabuilder thing is
103
## a little cumbersome, especially since I have an
104
## additional type system on top of this one.
105
colortype = Commands::CommandType.get_type('color')
108
# Now, we have three elements
110
if l[0] =~ /mask|cut/i
113
below = colortype.string_to_type(l[0])
120
if l[2] =~ /mask|cut/i
123
above = colortype.string_to_type(l[2])
129
specs = l[1].split(/--/)
134
if s =~ /([^(]+)\((.*)\)/
136
colors << colortype.string_to_type($1)
139
colors << colortype.string_to_type(s)
142
cm = ColorMap.new(values, colors)
150
# Prepares the 'data', 'colormap' and 'value_mask' arguments
151
# to t.create_image based on the given data, and the min and
154
# @todo handle masking + in and out of range.
156
# @todo I don't think this function is named properly.
157
def prepare_data_display(t, data, zmin, zmax)
158
# We correct zmin and zmax
159
cmap, zmin, zmax = *self.to_colormap(t, zmin, zmax)
161
data = t.create_image_data(data.reverse_rows,
165
return { 'data' => data,
170
# Returns a color triplet corresponding to the given z value
172
# @todo For now, the HSV parameter isn't honored.
173
def z_color(z, zmin, zmax)
174
zvs = z_values(zmin, zmax)
176
idx = zvs.where_first_ge(z)
178
x = (zvs[idx] - z)/(zvs[idx] - zvs[idx-1])
179
c = Utils::mix_objects(@colors[idx-1],@colors[idx], x)
180
# p [c, idx, z, zmin, zmax]
189
# Converts to a Tioga color_map
191
# @todo That won't work when there are things inside/outside
193
def to_colormap(t, zmin, zmax)
195
# OK. Now, we have correct z values. We just need to scale
196
# them between z_values[0] and z_values.last, to get a [0:1]
198
zvs = z_values(zmin, zmax)
200
p_values.sub!(p_values.first)
201
p_values.div!(p_values.last)
220
col = t.rgb_to_hls(col)
226
return [t.create_colormap(dict), zvs.first, zvs.last]
231
# Returns a Dvector holding z values corresponding to each of
234
# @todo This function will be called very often and is not
235
# very efficient; there should be a way to cache the results,
236
# either implicitly using a realy cache or explicitly by
237
# "instantiating" the colormap for given values of zmin and
240
# @todo This function doesn't ensure that the resulting z
241
# values are monotonic, which isn't quite that good.
242
def z_values(zmin, zmax)
244
z_values = @values.dup
246
z_values[-1] ||= zmax
248
# Now, we replace all the nil values by the correct position
249
# (the middle or both around when only one _nil_ is found,
250
# 1/3 2/3 for 2 consecutive _nil_ values, and so on).
252
1.upto(z_values.size-1) do |i|
254
if last_value + 1 < i
255
(last_value+1).upto(i - 1) do |j|
256
frac = (j - last_value)/(i - last_value + 1.0)
257
p [last_value, j, i, frac]
258
z_values[j] = z_values[last_value] * frac +
259
z_values[i] * (1 - frac)
265
return Dobjects::Dvector[*z_values]