/******************************************************************** * globe plot class * * Example: import globe_class; G = new Globe; G->surface(); G->gshhs("data/gshhs_c.b"); G->grid(20); G->show(); * ********************************************************************/ load("zegraph.dll", "matrix.dll"); D2R = 0.017453292519943; // degree to radius SCALE = 0.35; // globe to image ratio class Globe { c_width = 600; // image size c_height = 600; c_rotatex = -60; // Atlentic centered c_rotatez = -90; c_radius = SCALE * c_width; // globe size c_render = zegraph("render"); c_scene = zegraph("scene"); c_root = zegraph("node"); c_render:size(c_width, c_height); c_scene:viewport(0, 0, c_width, c_height); c_render:add(c_scene); c_scene:root(c_root); c_light = zegraph("light"); c_light:position(0, 0.25, 1); c_light:ambient(0.5, 0.5, 0.5); c_node = zegraph("node"); c_node:rotatez(c_rotatez); c_node:rotatex(c_rotatex); c_root:add(c_light, c_node); c_font = zegraph("font"); /*************************************************************** * set size ***************************************************************/ function size(width, height) { global:c_width = width; global:c_height = height; c_render:size(width, height); c_scene:viewport(0, 0, width, height); global:c_radius = SCALE * width; } /*************************************************************** * add object to the node ***************************************************************/ function add(shape, transform=true) { if (transform) c_node:add(shape); else c_root:add(shape); } /*************************************************************** * rotation ***************************************************************/ function rotate(rotatex, rotatez) { global:c_rotatex = rotatex; global:c_rotatez = rotatez; c_node:rotatez(rotatez); c_node:rotatex(rotatex); } /**************************************************************** * move globbe up-down or left-right ****************************************************************/ function move(dx, dy) { c_root:translate(dx, dy, 0); } /*************************************************************** * rotation ***************************************************************/ function view(lat, lon) { global:c_rotatex = -(90 - lat); global:c_rotatez = -(90 + lon); c_node:rotatez(c_rotatez); c_node:rotatex(c_rotatex); } /*************************************************************** * show the globe ***************************************************************/ 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 image to file ***************************************************************/ function save(fname) { c_render:tofile(fname); } /**************************************************************** * globe surface ****************************************************************/ function surface(iteration=5) { polygon = zegraph("polygon"); polygon:color(0, 0, .5); c_node:add(polygon); polygon:sphere(c_radius, iteration); return polygon; } /**************************************************************** * create a globe using image for texutre ****************************************************************/ function texture(fname) { node = zegraph("node"); texture = zegraph("texture"); polygon = zegraph("polygon"); polygon:color(1, 1, 1); node:add(texture, polygon); node:closed(true); c_node:add(node); texture:image(fname, -1, -1, -1); polygon:sphere(c_radius, 5, true); } /*************************************************************** * draw grid lines ****************************************************************/ function grid(deg, width=1) { deg = integer(deg); assert(deg > 1); r = 1.001*c_radius; node = zegraph("node"); node:color(.5, .5, .5); c_node:add(node); n = (180 - 2*deg)/2 + 1; D = matrix("double", n, 1); D:fill(-90+deg, 2); XYZ = matrix("double", n, 3); XYZ[null,1] = D; XYZ[null,2] = 0; for (lon = 0; lon < 360; lon += deg) { XYZ[null,0] = lon; line = zegraph("line"); node:add(line); line:type("strip"); line:solid(width); xyz = zegraph("vertex"); line:vertex(xyz); ptr = XYZ:ptr(); xyz:add(ptr[0], ptr[1]); xyz:xy2sphere(r); nor = xyz:normal(1); line:normal(nor); } n = 181; D:resize(n, 1); D:fill(0, 2); XYZ:resize(n, 3); XYZ[null,0] = D; XYZ[null,2] = 0; for (lat = -90+deg; lat <= 90-deg; lat += deg) { XYZ[null,1] = lat; line = zegraph("line"); node:add(line); line:type("strip"); line:solid(width); xyz = zegraph("vertex"); line:vertex(xyz); ptr = XYZ:ptr(); xyz:add(ptr[0], ptr[1]); xyz:xy2sphere(r); nor = xyz:normal(1); line:normal(nor); } return node; } /**************************************************************** * make coastlines using GSHHS data ****************************************************************/ function gshhs(fname) { r = 1.001*c_radius; line = zegraph("line"); c_node:add(line); xyz = zegraph("vertex"); nor = zegraph("vertex"); line:type("lines"); line:color(.8, .8, .8); line:vertex(xyz); D = matrix("double"); D:readgshhs(fname, "land"); D:insert(2, 0); ptr = D:ptr(); xyz:add(ptr[0], ptr[1]); xyz:xy2sphere(r); nor = xyz:normal(1); line:normal(nor); return line; } /**************************************************************** * draw a line on surface. ****************************************************************/ function line(lon1, lat1, lon2, lat2) { lon1 *= D2R; lon2 *= D2R; lat1 *= D2R; lat2 *= D2R; z1 = sin(lat1); r = cos(lat1); x1 = r * cos(lon1); y1 = r * sin(lon1); z2 = sin(lat2); r = cos(lat2); x2 = r * cos(lon2); y2 = r * sin(lon2); r = 1.001*c_radius; line = zegraph("line"); c_node:add(line); vertex = zegraph("vertex"); normal = zegraph("vertex"); line:type("strip"); line:vertex(vertex); line:normal(normal); dx = real(x2 - x1); dy = real(y2 - y1); dz = real(z2 - z1); d = sqrt(dx*dx + dy*dy + dz*dz); assert(d > 0); n = integer(d*180.0); vertex:add(x1*r, y1*r, z1*r); normal:add(x1, y1, z1); if (n < 1) { vertex:add(x2*r, y2*r, z2*r); normal:add(x2, y2, z2); return line; } dx /= n; dy /= n; dz /= n; for (i = 1; i < n; i++) { x = x1 + i*dx; y = y1 + i*dy; z = z1 + i*dz; d = sqrt(x*x + y*y + z*z); x /= d; y /= d; z /= d; vertex:add(x*r, y*r, z*r); normal:add(x, y, z); } vertex:add(x2*r, y2*r, z2*r); normal:add(x2, y2, z2); return line; } /**************************************************************** * text layout ****************************************************************/ function text(x, y, 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(x+h, y+v, 0, x+h+1, y+v, 0, x+h, y+v+1, 0); c_root:add(text); return text; } /**************************************************************** * lon-lat to x-y-z projection ****************************************************************/ function projection(lon, lat) { lon *= D2R; lat *= D2R; z = c_radius * sin(lat); r = c_radius * cos(lat); x = r * cos(lon); y = r * sin(lon); return [x, y, z]; } /**************************************************************** * greate circle distance on unit globe ****************************************************************/ function distance(lon1, lat1, lon2, lat2) { lon1 *= D2R; lat1 *= D2R; lon2 *= D2G; lat2 *= D2R; return acos(cos(lat1)*cos(lat2)*cos(lon1-lon2) + sin(lat1)*sin(lat2)); } /**************************************************************** * animate the globe and control rotation by arrow kyes and mouse ********************************************************************/ function animate() { rotx0 = c_rotatex; rotz0 = c_rotatez; rotx = rotx0; rotz = rotz0; 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) { 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_node:rotatez(rotz); c_node:rotatex(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_node:rotatez(rotz0); c_node:rotatex(rotx0); 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_node:rotatez(rotz); c_node:rotatex(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_node:rotatez(rotz); c_node:rotatex(rotx); c_render:towindow(hwnd); } else if (msg == "SIZE") { hide(); } } } /******************************************************************** * Arcball emulation ********************************************************************/ function arcball(x0, y0, x1, y1, cx, cy) { x0 -= cx; x0 /= c_radius; y0 -= cy; y0 /= -c_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 /= c_radius; y1 -= cy; y1 /= -c_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]]; } }