/******************************************************************** * class for 3D plot * * Example: import plot3d_class; G = new Plot3D; G.line([0,.5],[0,.5],[0,.5]); G.show(); * ********************************************************************/ load("zegraph.dll", "matrix.dll"); class Plot3D { c_width = 600; c_height = 600; c_xmin = -1; c_xmax = 1; c_ymin = -1; c_ymax = 1; c_zmin = -1; c_zmax = 1; c_rotatex = -60; c_rotatez = 20; c_render = zegraph("render"); c_render.size(c_width, c_height); c_scene = zegraph("scene"); c_scene.viewport(0, 0, c_width, c_height); c_render.add(c_scene); c_root = zegraph("node"); c_scene.root(c_root); c_light = zegraph("light"); c_plot = zegraph("plot"); c_root.add(c_light, c_plot); disable(c_light); c_font = zegraph("font"); c_plot.font(c_font, 10); c_plot.scale(.5, .5, .5); c_plot.rotate(c_rotatez, c_rotatex); xaxis(); yaxis(); zaxis(); /**************************************************************** * set image size ****************************************************************/ function size(width, height) { assert(width > 32, height >= 32); global:c_width = width; global:c_height = height; c_render.size(width, height); c_scene.viewport(0, 0, width, height); } /*************************************************************** * add object to plot ***************************************************************/ function add(shape, transform=true, anchor=null) { if (isarray(anchor)) { c_plot.anchor(shape, anchor[0], anchor[1], anchor[2]); } else if (transform) { c_plot.add(shape); } else { c_root.add(shape); } } /**************************************************************** * scale image in the view ****************************************************************/ function scale(xscal, yscale, zscale) { c_plot.scale(xscale, yscale, zscale); } /**************************************************************** * move plot up-down or left-right ****************************************************************/ function move(dx, dy) { c_plot.translate(dx, dy, 0); } /**************************************************************** * rotate plot ****************************************************************/ function rotate(rotatex, rotatez) { global:c_rotatex = rotatex; global:c_rotatez = rotatez; c_plot.rotate(rotatez, rotatex); } /**************************************************************** * set x-axis ****************************************************************/ function xaxis(title="X", range=[-1,1], anchor=[0,-1,-1], tickmarks=[-1,.5,0], tickdigits=1) { assert(range[1] > range[0], tickmarks[0] >= range[0], tickmarks[1] > (range[1]-range[0])/50.0); global:c_xmin = range[0]; global:c_xmax = range[1]; axis = c_plot.xaxis(); axis.title(title); axis.range(range[0], range[1]); axis.tickmarks(tickmarks[0], tickmarks[1], tickmarks[2]); axis.tickdigits(tickdigits); c_plot.xaxis(anchor[0], anchor[1], anchor[2]); return axis; } /**************************************************************** * set y-axis ****************************************************************/ function yaxis(title="Y", range=[-1,1], anchor=[-1,0,-1], tickmarks=[-1,.5,0], tickdigits=1) { assert(range[1] > range[0], tickmarks[0] >= range[0], tickmarks[1] > (range[1]-range[0])/50.0); global:c_ymin = range[0]; global:c_ymax = range[1]; axis = c_plot.yaxis(); axis.title(title); axis.range(range[0], range[1]); axis.tickmarks(tickmarks[0], tickmarks[1], tickmarks[2]); axis.tickdigits(tickdigits); c_plot.yaxis(anchor[0], anchor[1], anchor[2]); return axis; } /**************************************************************** * set y-axis ****************************************************************/ function zaxis(title="Z", range=[-1,1], anchor=[-1,1,0], tickmarks=[-1,.5,0], tickdigits=1) { assert(range[1] > range[0], tickmarks[0] >= range[0], tickmarks[1] > (range[1]-range[0])/50.0); global:c_zmin = range[0]; global:c_zmax = range[1]; axis = c_plot.zaxis(); axis.title(title); axis.range(range[0], range[1]); axis.tickmarks(tickmarks[0], tickmarks[1], tickmarks[2]); axis.tickdigits(tickdigits); c_plot.zaxis(anchor[0], anchor[1], anchor[2]); return axis; } /**************************************************************** * set font ****************************************************************/ function font(size=10, truetype="") { assert(size > 4); if (truetype != "") c_font.truetype(truetype); c_plot.font(size); } /**************************************************************** * draw a line ****************************************************************/ function line(x, y, z) { line = zegraph("line"); c_plot.add(line); vert = zegraph("vertex"); line.vertex(vert); line.type("strip"); n = x.size(); if (isarray(x) && isarray(y) && isarray(z)) { for (i = 0; i < n; i++) vert.add(x[i], y[i], z[i]); } else if (isuser(x) && isuser(y) && isuser(z)) { D = matrix("double", n[2], 3); D[null,0] = x; D[null,1] = y; D[null,2] = z; p = D.ptr(); vert.add(p[0], p[1]); } else { error("expecting array or double matrix"); } return line; } /**************************************************************** * draw a polygon ****************************************************************/ function polygon(x, y, z, fill=false) { poly = zegraph("polygon"); c_plot.add(poly); vert = zegraph("vertex"); poly.vertex(vert); poly.type("polygon"); if (!fill) poly.fill(-1); n = x.size(); if (isarray(x) && isarray(y)&& isarray(z)) { for (i = 0; i < n; i++) vert.add(x[i], y[i], z[i]); } else if (isuser(x) && isuser(y) && isuser(z)) { D = matrix("double", n[2], 3); D[null,0] = x; D[null,1] = y; D[null,2] = z; p = D.ptr(); vert.add(p[0], p[1]); } else { error("expecting array or double matrix"); } return poly; } /**************************************************************** * draw points ****************************************************************/ function point(x, y, z, size=1) { point = zegraph("point"); c_plot.add(point); vert = zegraph("vertex"); point.vertex(vert); if (size > 1) point.size(size); n = x.size(); if (isarray(x) && isarray(y) && isarray(z)) { for (i = 0; i < n; i++) vert.add(x[i], y[i], z[i]); } else if (isuser(x) && isuser(y) && isuser(z)) { D = matrix("double", n[2], 3); D[null,0] = x; D[null,1] = y; D[null,2] = z; p = D.ptr(); vert.add(p[0], p[1]); } else { vert.add(x, y, z); } return point; } /**************************************************************** * put symbol in plot ****************************************************************/ function symbol(x, y, z, symbol) { n = x.size(); if (isarray(x) && isarray(y) && isarray(z)) { for (i = 0; i < n; i++) c_plot.anchor(symbol, x[i], y[i], z[i]); } else if (isuser(x) && isuser(y) && isuser(z)) { for (i = 0; i < n; i++) c_plot.anchor(symbol, x[i], y[i], z[i]); } else { error("expecting array or double matrix"); } } /**************************************************************** * text layout ****************************************************************/ function text(x, y, z, string, size=12, halign=0, valign=-1) { text = zegraph("text"); text.font(c_font, size); text.string(string); size = text.size(); h = -size[0]/2; v = -size[1]/2; if (halign < 0) h = 0; if (halign > 0) h = -size[0]; if (valign < 0) v = 0; if (valign > 0) v = -size[1]; text.layout(h, v, 0, h + 1, v, 0, h, v + 1, 0); c_plot.anchor(text, x, y, z); return text; } /**************************************************************** * put image in plot ****************************************************************/ function image(p1, p2, p3, p4, fname) { node = zegraph("node"); texture = zegraph("texture"); polygon = zegraph("polygon"); vertex = zegraph("vertex"); texcoord = zegraph("texcoord"); node.closed(true); node.add(texture, polygon); texture.image(fname, -1, -1, -1); polygon.type("quads"); polygon.color(1, 1, 1); polygon.vertex(vertex); polygon.texcoord(texcoord); vertex.add(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], p3[0], p3[1], p3[2], p4[0], p4[1], p4[2]); texcoord.add(0, 0, 1, 0, 1, 1, 0, 1); c_plot.add(node); } /**************************************************************** * plot coatlines ****************************************************************/ function gshhs(fname, z=c_zmin) { x1 = c_xmin; x2 = c_xmax; if (c_xmin < 0) { x1 += 180; x2 += 180; } D = matrix("double"); D.readgshhs(fname, "land", x1, x2, c_ymin, c_ymax); D.insert(2, z); if (c_xmin < 0) { v1 = D[null,0]; idx = v1 >= 180; v2 = v1*0; v2[idx] = v1; v2 -= 360; v1[idx] = v2; D[null,0] = v1; } coast = zegraph("line"); c_plot.add(coast); coast.type("lines"); xyz = zegraph("vertex"); coast.vertex(xyz); p = D.ptr(); xyz.add(p[0], p[1]); return coast; } /**************************************************************** * show plot ****************************************************************/ function show() { window(c_width, c_height, "show_callback"); function show_callback(hwnd, msg, wp, hwp, lwp, lp, hlp, llp) { if (msg == "PAINT") { c_render.towindow(hwnd); } else if (msg == "SIZE") { hide(); } } } /**************************************************************** * save imaget ****************************************************************/ function save(fname) { c_render.tofile(fname); } /**************************************************************** * animate the globe and control rotation by arrow kyes and mouse ********************************************************************/ function animate() { rotx = c_rotatex; rotz = c_rotatez; scx = c_width / 2; scy = c_height / 2; x0 = 0; y0 = 0; cx = 0; cy = 0; drag = 0; deg = 15; clock = null; key = 39; window(c_width, c_height, "animate_callback"); function animate_callback(hwnd, msg, wp, hwp, lwp, lp, hlp, llp) { if (msg == "PAINT") { c_render.towindow(hwnd); } else if (msg =="KEYUP") { if (wp >= 37 && wp <= 40) { global.key = wp; if (wp == 37) // left arrow global:rotz -= deg; else if (wp == 38) // up arrow global:rotx -= deg; else if (wp == 39) // right arrow global:rotz += deg; else // down arrow global:rotx += deg; c_plot.rotate(rotz, rotx); c_render.towindow(hwnd); } } else if (msg == "LBUTTONDOWN") { global:x0 = hlp; global:y0 = llp; global:drag = 1; } else if (msg == "LBUTTONUP") { global:drag = 0; } else if (msg == "RBUTTONUP") { global:drag = 0; c_plot.rotate(c_rotatez, c_rotatex); c_render.towindow(hwnd); } else if (msg == "MOUSEMOVE") { if (drag > 0) { if (cx != x0 && cx != hlp) { if ((x0 - hlp) / (cx - hlp) < 0) { global:x0 = hlp; global:cx = x0; global:y0 = llp; global:cy = y0; return; } } if (cy != y0 && cy != llp) { if ((y0 - llp) / (cy - llp) < 0) { global:x0 = hlp; global:cx = x0; global:y0 = llp; global:cy = y0; return; } } a = arcball(x0, y0, hlp, llp, scx, scy); global:rotx = rotx + a[0]; global:rotz = rotz + a[1]; c_plot.rotate(rotz, rotx); c_render.towindow(hwnd); global:cx = hlp; global:cy = llp; } } else if (msg =="LBUTTONDBLCLK") { if (clock == null) { timer(30); global:clock = true; } else { timer(0); global:clock = null; } } else if (msg == "TIMER") { if (key == 37) global:rotz -= 2; else if (key == 38) global:rotx -= 2; else if (key == 39) global:rotz += 2; else global:rotx += 2; c_plot.rotate(rotz, rotx); c_render.towindow(hwnd); } else if (msg == "SIZE") { hide(); } } } /******************************************************************** * Arcball emulation ********************************************************************/ function arcball(x0, y0, x1, y1, cx, cy) { radius = c_width / 3.0; if (c_height < c_width) radius = c_height / 3.0; x0 -= cx; x0 /= radius; y0 -= cy; y0 /= -radius; a = x0*x0 + y0*y0; if (a > 1.0) { a = sqrt(a); x0 /= a; y0 /= a; a = 1.0; } z0 = sqrt(1 - a); x1 -= cx; x1 /= radius; y1 -= cy; y1 /= -radius; a = x1*x1 + y1*y1; if (a > 1.0) { a = sqrt(a); x1 /= a; y1 /= a; a = 1.0; } z1 = sqrt(1 - a); a = direction(0, 0, 0, z0, x0, y0); b = direction(0, 0, 0, z1, x1, y1); return [a[0] - b[0], b[1] - a[1]]; } }