
| 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 16 Make Package and ShapesThe make pakage of the utility library has several shape creation functions. An object in the real world can often be dissembled to basic building blocks, some of which can be easily generated by these functions, which often use zeArray of double floating type for input and output. FanIf used alone without texture or light, a fan does not look interesting. We use an earth image here again to make it attractive. Note that if you set the sweeping angle to 360 degrees, you will get a disk that may be used as the top of a column or a large filled dot. A fan needs only one normal, which usually should point to the z-direction. Since we do not use light here, it is not necessary to have the normal.
require("register")
render, scene, node, texture, shape, xyz, st
= zeGrf.new("render" ,"scene", "node",
"texture", "polygon", "vertex", "texcoord")
render:add(scene)
scene:set{node = node}
node:add(texture, shape)
texture:set{image = "earth.png"}
shape:set{vertex = xyz, texture_coord = st,
color = {1, 1, 1, 1}, type = "trianglefan"}
arr = zeUtl.new("double")
zeMake.fan(arr, 200, 36, 30, 120)
-- Vertex cooidinates are in the first three vectors and
-- texture coordinates are in the fourth and fifth vectors.
xyz:add(arr)
arr:shift(3)
st:add(arr)
render:tofile("fan.png")
fan.png DiskThe disk function is very similar to the fan function. Actually, if the inner radius is zero, the resulted shape looks the same as a fan. However, there are subtle differences between the two: a fan has fewer vertices and hence should be more efficient for rendering; whereas a disk can be textured more accurately. Like a fan, a disk needs only one normal, which usually should point to the z-direction.
require("register")
render, scene, node, texture, shape, xyz, st
= zeGrf.new("render" ,"scene", "node",
"texture", "polygon", "vertex", "texcoord")
render:add(scene)
scene:set{node = node}
node:add(texture, shape)
texture:set{image = "earth.png"}
shape:set{vertex = xyz, texture_coord = st,
color = {1, 1, 1, 1}, type = "quadstrip"}
arr = zeUtl.new("double")
zeMake.disk(arr, 80, 200, 36, 0, 360)
xyz:add(arr)
arr:shift(3)
st:add(arr)
render:tofile("disk.png")
disk.png CylinderSweeping a straight line around the z-axis forms a cylinder. A cone differs from a bar ony in the base radius or the top radius. In this example, the shape looks like a cup.
require("register")
render, scene, node, light, texture, shape, xyz, nor, st
= zeGrf.new("render" ,"scene", "node", "light",
"texture", "polygon", "vertex", "vertex", "texcoord")
render:add(scene)
scene:set{node = node}
node:add(light, texture, shape)
light:set{position = {500, -500, 500}}
texture:set{image = "earth.png"}
shape:set{vertex = xyz, vertex_normal = nor,
texture_coord = st, color = {1, 1, 1, 1},
type = "quadstrip"}
arr = zeUtl.new("double")
zeMake.cylinder(arr, 300, 80, 120, 36, 0, 360)
xyz:add(arr)
arr:shift(3)
nor:add(arr)
arr:shift(3)
st:add(arr)
node:rotatex(-80)
node:translate(0, -100, 0)
render:tofile("cylinder.png")
cylinder.png ExtrudeThe function extrudes any curve for a given depth along the z-axis. To see how it works, first let's create a smooth curve using the spline interpolation function in the math package.
require("register")
render, scene, node, blend, line, lxyz, point, pxyz
= zeGrf.new("render" ,"scene", "node", "blend",
"line", "vertex", "point", "vertex")
render:add(scene)
scene:set{node = node}
node:add(blend, line, point)
line:set{vertex = lxyz, color = {0, 0.5, 0.5, 1},
type = "strip", solid = 2, smooth = true}
point:set{vertex = pxyz, color = {1, 0, 0, 1}, size = 5}
point:translate(0, 0, 10) -- To avoid mixing with the line.
arr, vec, tmp = zeUtl.new("double", "double", "double")
arr:resize(10, 3)
-- Make the first vector increase monotonically
--and the third vector all zeros.
vec:range(-100, 20, 10)
arr:setarr(0, vec)
arr:setarr(2, 0)
-- Use the math object to populate the second
-- vector of arr with random numbers.
zeMath.rand(vec)
-- Scale and translate vec so that it varies in proper ranges.
vec:mul(200)
vec:sub(100)
arr:setarr(1, vec)
-- Transfer array data to the vertex object of points.
pxyz:add(arr)
-- Now the spline.
vec:copy(tmp)
zeMath.spline(tmp, 5)
arr:getarr(0, vec)
n = tmp:size()
arr:resize(n, 3)
arr:setarr(1, tmp)
zeMath.linear(vec, 5)
arr:setarr(0, vec)
arr:setarr(2, 0)
-- Transfer array data to the vertex object of line.
lxyz:add(arr)
render:tofile("spline.png")
spline.png Now the extruding. The input array should have three vectors containing x-, y-, and z-coordinates. The first three vectors of the output array contains vertices, the second three vectors contains normals, and the last two vectors contains texture coordinates.
require("register")
render, scene, node, light, poly, xyz, nor
= zeGrf.new("render" ,"scene", "node",
"light", "polygon", "vertex", "vertex")
render:add(scene)
scene:set{node = node}
node:add(light, poly)
light:set{position = {100, 500, 500}}
poly:rotatex(60)
poly:set{vertex = xyz, vertex_normal = nor,
color = {0, 1, 1, 1}, type = "quadstrip"}
arr, vec, tmp = zeUtl.new("double", "double", "double")
-- generate x coordinate
tmp:range(-100, 20, 10)
zeMath.linear(tmp, 5)
n = tmp:size()
arr:resize(n, 3)
arr:fill(0)
arr:setarr(0, tmp)
-- generate y coordinate
vec:resize(10, 1)
zeMath.rand(vec)
vec:mul(200)
vec:sub(100)
zeMath.spline(vec, 5)
arr:setarr(1, vec)
-- generate the surface
zeMake.extrude(vec, 150, arr)
xyz:add(vec)
vec:shift(3)
nor:add(vec)
render:tofile("extrude.png")
extrude.png SweepThis function sweeps a curve around the z-axis to from a surface. Most of us are used to think of a curve on the x-y plane, as in extruding, but in sweeping we must get used to imagine the curve on the x-z or y-z plane. Sweeping a curve a few degrees a time for 360 degrees results in a symmetrical surface. Similar to the extruding functions, the input array should have three vectors containing x-, y-, and z-coordinates. The first three vectors of the output array contains vertices, the second three vectors contains normals, and the last two vectors contains texture coordinates. The following code demonstrates creating a Piriform teardrop by sweeping. To have a smoother surface, the normals should be calculated from the derivatives of the equation.
require("register")
render, scene, root, node, light
= zeGrf.new("render" ,"scene", "node", "node", "light")
render:add(scene)
scene:set{node = root}
root:add(light, node)
light:set{position = {500, 500, 500}}
-- x = sqrt(z^3 - z^4) for 0 < z < 1
arr, z, x, p = zeUtl.new("double", "double", "double", "double")
x:range(0, 0.02, 51)
z:range(0, 0.02, 51)
x:mul(z)
x:mul(z) -- z^3
z:mul(x) -- z^4
x:sub(z) -- z^3 - z^4
zeMath.sqrt(x) -- x = sqrt(z^3 - z^4)
arr:resize(51, 3)
arr:fill(0)
x:mul(400) -- scale x
arr:setarr(0, x)
z:range(0, 0.02, 51)
z:mul(400) -- scale z
arr:setarr(2, z)
zeMake.sweep(z, 5, arr)
z:copy(x)
x:shift(3)
for i = 0, 355, 5 do
poly, xyz, nor = zeGrf.new("polygon", "vertex", "vertex")
node:add(poly)
poly:set{vertex = xyz, vertex_normal = nor,
color = {0, .8, .8, 1}, type = "quadstrip"}
poly:rotatez(i)
xyz:add(z)
nor:add(x)
end
node:rotatex(90)
node:translate(0, 200, 0)
render:tofile("sweep.png")
sweep.png Iso-surfaceThe function is implemented for volume rendering of 4D data in the 3D space, equivalent to contour of 3D data in a 2D space. We again use the Piriform teardrop equation to validate the isosurf() function. One form of the equation can be written as f(x, y, z) = x^2 + y^2 + z^4 - z^3 What the following code does is using the isosurf() function to construct the surface on which f(x, y, z) = 0.
require("register")
render, scene, root, light, material, shape, xyz, nor
= zeGrf.new("render", "scene", "node", "light",
"material", "polygon", "vertex", "vertex")
render:add(scene)
scene:set{node = root}
root:add(light, material, shape)
light:set{position = {-500, 500, 500}}
material:set{ambient = {0.0215, 0.1745, 0.0215, 1, 0},
diffuse = {0.07568, 0.61424, 0.07568, 1, 0},
specular = {0.633, 0.727811, 0.633, 1, 0},
shininess = {78.8, 0}}
shape:set{vertex = xyz, vertex_normal = nor,
type = "triangles", color = {1, 1, 1, 1}}
shape:translate(0, 0, -200)
shape:rotatex(90)
arr, tri = zeUtl.new("double", "double")
arr:resize(8, 4)
step = 0.01
scale = 300
for z = 0, 1, step do
z1 = z*z*z*z - z*z*z
z2 = z + step
z2 = z2*z2*z2*z2 - z2*z2*z2
arr:setele(0, 2, scale*z)
arr:setele(1, 2, scale*z)
arr:setele(2, 2, scale*z)
arr:setele(3, 2, scale*z)
arr:setele(4, 2, scale*(z+step))
arr:setele(5, 2, scale*(z+step))
arr:setele(6, 2, scale*(z+step))
arr:setele(7, 2, scale*(z+step))
for x = -0.35, 0.33, step do
x1 = x*x
x2 = (x+step)*(x+step)
arr:setele(0, 0, scale*x)
arr:setele(1, 0, scale*(x+step))
arr:setele(2, 0, scale*(x+step))
arr:setele(3, 0, scale*x)
arr:setele(4, 0, scale*x)
arr:setele(5, 0, scale*(x+step))
arr:setele(6, 0, scale*(x+step))
arr:setele(7, 0, scale*x)
for y = -0.35, 0.33, step do
y1 = y*y
y2 = (y+step)*(y+step)
arr:setele(0, 1, scale*y)
arr:setele(1, 1, scale*y)
arr:setele(2, 1, scale*(y+step))
arr:setele(3, 1, scale*(y+step))
arr:setele(4, 1, scale*y)
arr:setele(5, 1, scale*y)
arr:setele(6, 1, scale*(y+step))
arr:setele(7, 1, scale*(y+step))
arr:setele(0, 3, -(z1+x1+y1))
arr:setele(1, 3, -(z1+x2+y1))
arr:setele(2, 3, -(z1+x2+y2))
arr:setele(3, 3, -(z1+x1+y2))
arr:setele(4, 3, -(z2+x1+y1))
arr:setele(5, 3, -(z2+x2+y1))
arr:setele(6, 3, -(z2+x2+y2))
arr:setele(7, 3, -(z2+x1+y2))
zeMake.isosurf(tri, 0, arr)
n = tri:size()
if (n > 2) then
xyz:add(tri)
tri:shift(3)
nor:add(tri)
end
end
end
end
render:tofile("isosurf.png")
isosurf.png ShellYou have seen the applications of sphere in examples for zeLight and zeMaterial. The difference between sphere() and sphere2() is that the former generates less triangles for a similar quality sphere while the later generate texture coordinates as well. The sphere2() function actually call shell() function 8 times for produce a sphere shape. Here let's a product of the shell function.
require("register")
render, scene, node, light, texture, shape, xyz, nor, st
= zeGrf.new("render" ,"scene", "node", "light",
"texture", "polygon", "vertex", "vertex", "texcoord")
render:add(scene)
scene:set{node = node}
node:add(light, texture, shape)
light:set{position = {500, 500, 500}}
texture:set{image = "earth.png"}
shape:set{vertex = xyz, vertex_normal = nor,
texture_coord = st, color = {1, 1, 1, 1},
type = "triangles"}
arr = zeUtl.new("double")
zeMake.shell(arr, 200, 4, 1)
xyz:add(arr)
arr:shift(3)
nor:add(arr)
arr:shift(3)
st:add(arr)
node:rotatey(-30)
node:rotatex(30)
render:tofile("shell.png")
shell.png Delaunay TriangulationThe Delaunay triangulation is a method to construct triangles from a series of randomly sampled data. These triangles may then be used for surface and contour plots. The function takes a double array of m by 3 as input and returns triangles on the xy-plane and the z-value at triangle vertices. Here is a test for the function:
require("register")
render, scene, node, blend, poly, point, xyz
= zeGrf.new("render" ,"scene", "node",
"blend", "polygon", "point", "vertex")
render:add(scene)
scene:set{node = node}
node:add(blend, poly, point)
poly:set{vertex = xyz, color = {0, 0, 1, 1},
type = "triangles", fill = 0, linewidth = 1.5,
smooth = true}
point:translate(0, 0, 10) -- avoid overlap of line on point
point:set{vertex = xyz, color = {1, 0, 0, 1}, size = 5}
arr, tmp = zeUtl.new("double", "double")
tmp:resize(100, 3)
zeMath.rand(tmp)
-- Sort the whole array in assending order according
-- of the first vector. and then triangulate
tmp:sort(0, 1)
zeMake.delaunay(arr, tmp)
-- Scale and array
arr:sub(0.5)
arr:mul(200)
xyz:add(arr)
render:tofile("delaunay.png")
delaunay.png We took a short cut in this sample: let the line and point objects to share the same vertex object. To make a similar plot with a large number of data, it may be much more efficient to use separate vertex objects for line and point and transfer the data to the vertices of points before triangulation, because the array on output is several times larger than its initial size. Finding a contour line is very easy. Here is an example.
require("register")
render, scene, node, blend, poly, pxyz, pclr, line, lxyz
= zeGrf.new("render" ,"scene", "node", "blend",
"polygon", "vertex", "color", "line", "vertex")
render:add(scene)
scene:set{node = node}
node:add(blend, poly, line)
poly:set{vertex = pxyz, vertex_color = pclr, type = "triangles"}
line:translate(0, 0, 10)
line:set{vertex = lxyz, color = {0, 0, 0, 1},
solid = 1.5, smooth = true, type = "lines"}
arr, vec, tmp = zeUtl.new("double", "double", "double")
tmp:resize(100, 3)
zeMath.rand(tmp)
tmp:sort(0, 1)
zeMake.delaunay(arr, tmp)
pxyz:add(arr)
-- Color triangles acoording to z-values
arr:getarr(2, tmp)
n = tmp:size()
vec:resize(n, 4)
vec:fill(1)
vec:setarr(0, tmp)
vec:setarr(1, tmp)
vec:setarr(2, tmp)
pclr:add(vec)
-- Contour
tmp:resize(1, 1)
tmp:fill(0.5)
zeMake.contour3(vec, tmp, 0, arr)
lxyz:add(vec)
node:scale(200, 200, 10)
render:tofile("delaunay2.png")
delaunay2.png |