
| Lesson
1 Lesson 2 Lesson 3 Lesson 4 Lesson 5 Lesson 6 Lesson 7 Lesson 8 Lesson 9 Lesson 10 Lesson 11 Lesson 12 Lesson 13 Lesson 14 Lesson 15 Lesson 16 Lesson 17 Lesson 18 Lesson 19 |
Lesson 17 2D PlotUnderstanding the characteristics of zePlot is creitical for using it for 2D plot: (1) It redefines the global coordinate for objects added to it; (2) Positioning its axes refers to the global coordinates; and (3) axes ranges of objects in the plot are determined by zePlot, not by axes, which are just objects for describing the meaning of data. Since axes are place in the center of a plot by default (Fiugre 17.1), you usually have to use the set() function to relocate them. There are two way to position a axis. Using the anchor option of the set() function is introduced in Lessen 9. Sometimes, you may want to position a axis with the offset option. To place a x-axis at the bottom, the offset is calculate by x_offset = - x_scale * image_height / 2 And to place a y-axis on the left, the offset is calculate by y_offset = - y_scale * image_width / 2
Figure 17.1 2D plot. Blending is recommended for producing smooth lines in a 2D plot. Of course, you need to set the smooth attibute of a line object to have the effect. Blending a smooth polygon usually makes it looks worse than without blending, especially with light effect enabled, because part of the polygon may be too transparent so that objects behind it or the background may be seen. The Window's implementation of OpenGL seems triangulates every type of polygon for rendering. Blending may reveal shared edges. Therefore we do not recommand blending with polygons. If you want to have a 3D polygon in a plot, you should add it to the plot with the x, y, z coordinate, because the plot applies different x- y- and z-scales internally to all objects it contains and therefore may distort the shape to a unacceptable degree. Object added in such a way will not be scaled by the plot. Line PlotThis is one the most frequently used plot type. Even if there is nothing special in drawing a line or connecting symbols with line segments, adding special effect to a line plot is not so trivial for many software. But with zeGraph, you can easily add images, pattern or color filled frames to the background. This example demonstrate using image in the background.
require("register")
render, scene, root, texture, plot, poly, xyz, st,
line, lxy, point, pxy, font
= zeGrf.new("render", "scene", "node",
"texture", "plot", "polygon",
"vertex", "texcoord", "line",
"vertex", "point", "vertex", "font")
render:add(scene)
scene:set{node = root}
root:add(plot)
plot:add(line, point, texture, poly)
plot:font(font)
plot:fontsize(12)
plot:scale(0.6, 0.4, 1)
plot:set{axis = "x", linewidth = 1.5, range = {0, 20},
tickmarks = {0, 5, 1}, label = "X",
offset = {0, -102, 0}}
plot:set{axis = "y", linewidth = 1.5, range = {0, 25},
tickmarks = {0, 5, 1}, tickdigit = {1, false},
label = "Y", offset= {-154, 0, 0}}
texture:set{image = "earth.png"}
xyz:add({0, 0, -10, 20, 0, -10, 20, 25, -10, 0, 25, -10})
st:add({.2, .2, .8, .2, .8, .8, .2, .8})
poly:set{vertex = xyz, texture_coord = st,
type = "quads", color = {1, 1, 1, 1}}
line:set{vertex = lxy, color = {1, 0, 0, 1}, solid = 3}
point:set{vertex = pxy, color = {1, 1, 0, 1}, size = 5}
--Generate data for use by vertices of line and point objects.
x, y, xy = zeUtl.new("double", "double", "double")
x:range(1, 1, 19)
y:resize(19, 1)
xy:resize(19, 3)
xy:fill(0)
zeMath.rand(y)
y:add(x)
xy:setarr(0, x)
xy:setarr(1, y)
lxy:add(xy)
pxy:add(xy)
render:tofile("line_2d.png")
line_2d.png The filled square symbols are drawn by using zePoint object. If its smooth() function is called, the symbols will appear in round shape. It is unfortunately however that the OpenGL implementation of Windows limits the maximal point size to 10. Well, this limitation is not a serious problem for zeGraph - the library lib2d.lua has functions for generating various symbols of any size. If the variety is not enough, you can always write your own symbol functions. Here is an example of drawing triangle symbols.
require("register")
render, scene, root, blend, plot, font
= zeGrf.new("render", "scene", "node",
"blend", "plot", "font")
render:add(scene)
scene:set{node = root}
root:add(blend, plot)
plot:font(font)
plot:fontsize(12)
plot:scale(0.6, 0.4, 1)
plot:set{axis = "x", linewidth = 1.5, range = {0, 1},
tickmarks = {0, .2, 1}, label = "X",
offset = {0, -102, 0}}
plot:set{axis = "y", linewidth = 1.5, range = {0, 1},
tickmarks = {0, 0.2, 1}, label = "Y",
offset= {-154, 0, 0}}
arr = zeUtl.new("double")
arr:resize(10, 2)
zeMath.rand(arr)
require("triangle_sym")
shape = triangle_sym(plot, 512, 512, arr, 15)
shape:set{color = {0, 1, 0, 1}, smooth = true}
root:translate(10, 10, 0)
render:tofile("triangle_symbol.png")
triangle_symbol.png Wind FieldMaking a good wind field plot takes time to practice. A NCEP wind dataset is used here to demonstrate the trick.
require("register")
data, spec, idx, uv, w, vec, tmp, clrtb
= zeUtl.new("short", "uint", "uint", "double",
"double", "double", "double", "double")
-- specify the block of data to read
spec:resize(3, 2)
spec:fill(0)
spec:setele(0, 1, 1)
spec:setele(1, 1, 73)
spec:setele(2, 1, 144)
-- process u-wind
nc = zeUtl.new("cdf")
nc:open("x:\\shared\\uwnd.sig995.1978.nc") -- data frome NOAA FTP
nc:getvar("uwnd", spec, data)
-- resize and re-arrange array from row major to column major;
-- and then scale the array.
w:resize(144 * 73, 1)
ptr, type = data:getptr()
w:setptr(ptr, type, 144 * 73)
w:reshape(144, 73)
w:transpose()
w:flip("r")
idx:range(0, 2, 72)
w:sample(idx, tmp)
idx:range(0, 2, 36)
tmp:sample(idx, w, "r")
w:mul(0.01)
w:add(225.45)
-- convert grid data to points
zeMake.grid2points(uv, 6, w)
-- process v-wind
nc:open("x:\\shared\\vwnd.sig995.1978.nc") -- data frome NOAA FTP
nc:getvar("vwnd", spec, data)
w:resize(144 * 73, 1)
ptr, type = data:getptr()
w:setptr(ptr, type, 144 * 73)
w:reshape(144, 73)
w:transpose()
w:flip("r")
idx:range(0, 2, 72)
w:sample(idx, tmp)
idx:range(0, 2, 36)
tmp:sample(idx, w, "r")
w:mul(0.01)
w:add(225.45)
zeMake.grid2points(vec, 6, w)
vec:getarr(2, w)
uv:insert(3, w)
zeMake.field(w, 5, uv)
clrtb:resize(3, 3)
clrtb:fill(0)
clrtb:setele(0, 2, 1)
clrtb:setele(1, 1, 1)
clrtb:setele(2, 0, 1)
w:getarr(2, vec)
zeMake.data2color(uv, vec, clrtb)
w:setarr(2, 0)
-- draw the wind field
render, scene, root, blend, shape, xyz, clr
= zeGrf.new("render", "scene", "node",
"blend", "line", "vertex", "color")
render:add(scene)
scene:set{node = root}
root:add(blend, shape)
shape:set{vertex = xyz, vertex_color = clr,
type = "lines", solid = 1.5, smooth = true}
shape:translate(-216, -108, 0)
shape:scale(2, 2, 1)
xyz:add(w)
clr:add(uv)
render:tofile("field.png")
field.png Height MapIn Lesson 12, You are shown how to make a map image from the earth relief data in etopo120.cdf. Actually, zeGraph has all the tools for you to make height map from such data. The product of the following code looks more informative than the one in Lesson 12.
require("register")
nc, rose, dat, arr, vec
= zeUtl.new("cdf", "float", "double",
"double", "double", "double")
nc:open("x:\\geo-data\\etopo120.cdf")
nc:getvar("ROSE", rose)
-- Convert data from float to double
ptr, type = rose:getptr()
nele, nvec = rose:size()
dat:setptr(ptr, type, nele * nvec)
dat:reshape(180, 90)
dat:transpose()
-- This lines rearrange the data so that the they
-- correspond to longitude of 1 to 359.
dat:shift(-10)
-- Scale the data for coloring later
max = dat:max()
min = dat:min()
dat:sub(min)
dat:div(max-min)
render, scene, root, light, poly, xyz, nor, rgb
= zeGrf.new("render", "scene", "node", "light",
"polygon", "vertex", "vertex", "color")
render:add(scene)
scene:set{node = root}
root:add(light, poly)
light:set{position = {500, 500, 500}}
poly:set{vertex = xyz, vertex_color = rgb,
vertex_normal = nor, type = "quads"}
-- Convert grid data to x, y and z
zeMake.grid2quads(arr, 2, dat)
xyz:add(arr)
arr:getarr(2, vec)
-- Calculate normal
zeMake.normal4(dat, 20, arr)
nor:add(dat)
-- Gray scale coloring based on z-values
dat:setarr(0, vec)
dat:setarr(1, vec)
dat:setarr(2, vec)
dat:insert(3, 1)
rgb:add(dat)
poly:set{color = {0, 1, 1, 1}}
poly:translate(-180, -90, 0)
render:tofile("heightmap.png")
heightmap.png ContourContour plot is probably the most frequently used 2D plot for 3D data. For randomly sampled data, you can make contour plot with the help of Delaunay triangulation, as shown Lesson 16. Making contours for grid data is very simple as shown bellow. Here We omitted procedures to draw axes and colorbar so that the code is short and therefore easier to understand. In practice, the grid data even do not have to cover a rectangle region. The contour4() function finds contour lines for every four x-y-z data; and therefore you can make contour for any arbitrary area. This feature is really useful - just image to make contour for seawater temperature, for instance.
require("register")
-- Read the air temperature data
nc = zeUtl.new("cdf")
nc:open("x:\\shared\\air.sig995.1978.nc") -- data from NOAA FTP
air, spec = zeUtl.new("short", "uint")
spec:resize(3, 2)
spec:fill(0)
spec:setele(0, 1, 1)
spec:setele(1, 1, 73)
spec:setele(2, 1, 144)
nc:getvar("air", spec, air)
dat = zeUtl.new("double")
dat:resize(144, 73)
ptr, type, nb = air:getptr()
dat:setptr(ptr, type, 144 * 73)
dat:reshape(144, 73)
dat:transpose()
dat:flip("r")
dat:mul(0.01)
dat:add(512.81)
-- Grid to quad shape
air = zeUtl.new("double")
zeMake.grid2quads(air, 1, dat)
-- Make color according to z-values
clrtb = zeUtl.new("double")
clrtb:resize(3, 3)
clrtb:fill(0)
clrtb:setele(0, 2, 1)
clrtb:setele(1, 1, 1)
clrtb:setele(2, 0, 1)
clr, vec = zeUtl.new("double", "double")
air:getarr(2, vec)
zeMake.data2color(clr, vec, clrtb)
-- Find contour lines.
iso = zeUtl.new("double")
iso:range(245, 5, 11)
zeMake.contour4(dat, iso, 0, air)
-- Construct scene
render, scene, root, blend, poly, pxyz, prgb, line, lxyz
= zeGrf.new("render", "scene", "node", "blend", "polygon",
"vertex", "color", "line", "vertex")
render:add(scene)
scene:set{node = root}
root:add(blend, poly, line)
poly:set{vertex = pxyz, vertex_color = prgb, type = "quads"}
line:set{vertex = lxyz, color = {1, 1, 1, 1}, type = "lines",
smooth = true}
n = dat:size()
vec:resize(n, 1)
vec:fill(10)
dat:setarr(2, vec) -- make contour line z-values all 10
lxyz:add(dat)
n = air:size()
vec:resize(n, 1)
vec:fill(0)
air:setarr(2, vec) -- make polygon z-values all zero
pxyz:add(air)
prgb:add(clr)
root:translate(-72, -36, 0)
root:scale(3, 3, 1)
render:tofile("contour_line.png")
contour_line.png This is often desirable to make discretely filled contour. Slightly modifying the previous example, you can achieve that effect, like this:
require("register")
-- Read the air temperature data
nc = zeUtl.new("cdf")
nc:open("x:\\shared\\air.sig995.1978.nc")
air, spec = zeUtl.new("short", "uint")
spec:resize(3, 2)
spec:fill(0)
spec:setele(0, 1, 1)
spec:setele(1, 1, 73)
spec:setele(2, 1, 144)
nc:getvar("air", spec, air)
dat = zeUtl.new("double")
dat:resize(144, 73)
ptr, type, nb = air:getptr()
dat:setptr(ptr, type, 144 * 73)
dat:reshape(144, 73)
dat:transpose()
dat:flip("r")
dat:mul(0.01)
dat:add(512.81)
-- Grid to quad shape
air = zeUtl.new("double")
zeMake.grid2quads(air, 1, dat)
-- Find contour triangles.
iso = zeUtl.new("double")
iso:range(245, 5, 11)
zeMake.contour4(dat, iso, 1, air)
-- Make color according to z-values
clrtb = zeUtl.new("double")
clrtb:resize(3, 3)
clrtb:fill(0)
clrtb:setele(0, 2, 1)
clrtb:setele(1, 1, 1)
clrtb:setele(2, 0, 1)
clr, vec = zeUtl.new("double", "double")
dat:getarr(2, vec)
zeMake.data2color(clr, vec, clrtb)
vec:fill(0)
dat:setarr(2, vec) -- make all z-values zero
-- Construct scene
render, scene, root, poly, pxyz, prgb
= zeGrf.new("render", "scene", "node",
"polygon", "vertex", "color")
render:add(scene)
scene:set{node = root}
root:add(poly)
poly:set{vertex = pxyz, vertex_color = prgb, type = "triangles"}
pxyz:add(dat)
prgb:add(clr)
root:translate(-72, -36, 0)
root:scale(3, 3, 1)
render:tofile("contour_fill.png")
contour_fill.png |