Our goal in this tutorial is to write a script that will allow us to visualize the relative values of the elements of a matrix. We will need to decide on a colormap, a way of mapping an integer value in the interval [0 Imax) to a color value. Actually, since color is represented with three components representing red, green, and blue, we will need to produce three color values for each numeric value. Furthermore, we will need to transform the input matrix so that its elements (approximately) retain their relative magnitudes but are in the range [0 Imax) . Finally, we will use a Tk canvas widget to draw the matrix. For each element in the matrix, we will create a rectangle in the canvas with the computed background color. The pseudo-code for what we want to do then looks like:
First let us create a MathViews interpreter called
M. We assume that package
Mxt has already been loaded (as described in the
installation and configuration instructions), so
that command minterp is available.
% minterp create
M
We've already done some of the hard work: We created a MathViews function
called clrmap which will create three
required colormap vectors. Here it is:
function [ r , g , b ] = clrmap( num_colors ) % black-red-yellow-white colormap third_num_colors = floor( num_colors / 3 ); % create red component vector r = ones(num_colors,1); r(1:third_num_colors+1) = [ 0 : 1/(third_num_colors) : 1 ]'; r = round( r * 255 ); % use vector shifting operator to create green and blue % component vectors g = r >> third_num_colors; b = g >> third_num_colors;
Cut the text for the clrmap function
and put it in a file called clrmap.m. Now
reset the _MVPATH variable so it contains
the directory where clrmap.m lives. This is
needed in order make the MathViews interpreter re-examine the contents of the
directories listed in the _MVPATH search
path:
% M eval { _MVPATH = [ _MVPATH ':/my/dir' ] }
You should now be able to invoke the function as follows:
% M eval { [ R, G, B ] = clrmap( 9 ); }
% M eval { [ R G B ] }
ans =
0 0 0
85 0 0
170 0 0
255 0 0
255 85 0
255 170 0
255 255 0
255 255 85
255 255 170
What we've just done is create a colormap containing 9 colors spanning black
to white, with red and yellow in between. R(i), G(i), and
B(i) contain the red, green, and blue components of ith color
in the color map. Later, we will use var
option to cast the values in R, G, and B into their 16-bit hexadecimal
representations, need by Tk to specify color. For example:
% M eval B9 = B(9) B9 =
170.0000
% M var B9 -format %x aa
The problem with a colormap of size nine is that it only lets us visually distinguish between nine different values. Let us instead use a colormap of size 128:
% M eval { [ R, G, B ] = clrmap( 128 ); }
Note how we used the semicolon after the MathViews command to suppress output.
Let us create a 40×40 random symmetric matrix using MathViews'
rand function. rand returns a matrix whose values are drawn from
a uniform probability distribution from zero to one.
% set n 40 % M eval "x = rand($n);" % M eval "x = x' * x;"
Now, let us transform the random matrix x into another matrix
y. We need to scale and translate all of x's values so they are in the range [1 128].
Recall that Imax=128 is the value we chose as the size of the
colormap. First, let us compute the range of values in x.
% M eval max_x = max(x(:)) max_x =
16.7125
% M eval min_x = min(x(:)) min_x =
6.1320
% M eval range_x = max_x - min_x range_x =
10.5805
Of course, your particular numbers may vary, since the random matrix you
generate is likely to be quite different than the one we are using for this
example. Now, let us create a new matrix y
which is the result of scaling and translating x:
% M eval {
y = x - min_x;
y = y / range_x;
y = y * ( 128 - 1 );
y = round( y ) + 1;
}
The elements of y should range between 1
and 128. To confirm this:
% M eval max(y(:)) ans =
128
% M eval min(y(:)) ans =
1
From y and our colormap, we will now
create three matrices (Ry, Gy, By) which
contain the red, green, and blue components of each element of y. For example, suppose that y(3,29)=103. Then,
we want Ry(3,29) to equal R(103). Clever use of MathViews' colon (:) operator allows us to do this mapping without
needing nested for loops as follows:
% M eval {
Ry = y;
Gy = y;
By = y;
Ry(:) = R(y(:));
Gy(:) = G(y(:));
By(:) = B(y(:));
}
From now-on, all our code will be in Tcl. First, let us convert
Ry, Gy,
and Byinto Tcl lists using the
var option:
% set ry [ M var Ry -format %0.2x ] % set gy [ M var Gy -format %0.2x ] % set by [ M var By -format %0.2x ]
Since Tk represents color in hexadecimal, we used the-format %0.2x option. Each command created a list
of sub-lists. Each sub-list corresponds to a matrix column. (We did not show
the output of the commands here because they are a few screens long.) Now, let
us create the Tk canvas .c. For each
element in the matrix, we will create a rectangle in .c with the computed background color. If we make
each rectangle a three pixel-wide square, then our canvas to have height and
width of 3×40=120 pixels:
% canvas .c -width 120 -height 120 % pack .c
We create the filled rectangles by iterating over the Tcl lists
ry, gy,
and by. For each element, we compose a
color by concatenating the elements extracted from these three lists. We use
the variables xp and
yp to denote the top-left corner position of each
rectangle within the canvas. We also use variables j and i as
column and row indices, respectively:
% for { set xp 0; set j 0 } { $j < 40 } { incr xp 3; incr j } {
set r_col [ lindex $ry $j ]
set g_col [ lindex $gy $j ]
set b_col [ lindex $by $j ]
for { set yp 0; set i 0 } { $i < 40 } { incr yp 3; incr i } {
set r_color [ lindex $r_col $i ]
set g_color [ lindex $g_col $i ]
set b_color [ lindex $b_col $i ]
set color \#$r_color$g_color$b_color
.c create rectangle $xp $yp [ expr $xp + 3 ] [ expr $yp + 3 ] \
-fill $color -outline ""
}
}
The resulting canvas should something like:
Last modified: Mon Nov 30 10:43:04 PST 1998