# Copyright (c) 1996, 1997, The Regents of the University of California. # All rights reserved. See Legal.htm for full text and disclaimer. # # PLWF.PY # Simple "painter's algorithm"-class routine for making 3-D wire frames # and related models. # # $Id: plwf.py,v 1.9 1997/11/12 22:05:09 motteler Exp $ # ## execfile ("pl3d.py") from types import * from arrayfns import * from pl3d import * def plwf (z, y = None, x = None, fill = None, shade = 0, edges = 1, ecolor = None, ewidth = None, cull = None, scale = None, cmax = None, clear = 1) : # plwf (z) # or plwf (z, y, x) # plots a 3-D wire frame of the given Z array, which must have the # same dimensions as the mesh (X, Y). If X and Y are not given, they # default to the first and second indices of Z, respectively. # The drawing order of the zones is determined by a simple "painter's # algorithm", which works fairly well if the mesh is reasonably near # rectilinear, but can fail even then if the viewpoint is chosen to # produce extreme fisheye perspective effects. Look at the resulting # plot carefully to be sure the algorithm has correctly rendered the # model in each case. # KEYWORDS: fill -- optional colors to use (default is to make zones # have background color), same dimension options as # for z argument to plf function # shade -- set non-zero to compute shading from current # 3D lighting sources # edges -- default is 1 (draw edges), but if you provide fill # colors, you may set to 0 to supress the edges # ecolor, ewidth -- color and width of edges # cull -- default is 1 (cull back surfaces), but if you want # to see the "underside" of the model, set to 0 # scale -- by default, Z is scaled to "reasonable" maximum # and minimum values related to the scale of (X,Y). # This keyword alters the default scaling factor, in # the sense that scale=2.0 will produce twice the # Z-relief of the default scale=1.0. # cmax -- the ambient= keyword in light3 can be used to # control how dark the darkest surface is; use this # to control how light the lightest surface is # the lightwf routine can change this parameter # interactively # SEE ALSO: lightwf, plm, plf, orient3, light3, fma, window3 _draw3 = get_draw3_ ( ) _square = get_square_ ( ) [_xfactor, _yfactor] = get_factors_ ( ) if (type (z) == ListType) : xyz = z [0] fill = z [1] shade = z [2] edges = z [3] ecolor = z [4] ewidth = z [5] cull = z [6] cmax = z [7] xyz1 = get3_xy(xyz, 1) x = xyz [0] # the original x y = xyz [1] # the original y # rotate (x,y,0) into on-screen orientation to determine order # just use four corners for this nx = shape (x) ny = nx [1] nx = nx [0] xx = array([[x [0, 0], x[nx - 1, 0]], [x [0, ny - 1] , x[nx - 1, ny - 1]]]) yy = array([[y [0, 0], y[nx - 1, 0]], [y [0, ny - 1] , y[nx - 1, ny - 1]]]) xyzc = array ( [ xx , yy, array ( [ [0., 0.], [0., 0.]])]) xyzc = get3_xy(xyzc, 1) # compute mean i-edge and j-edge vector z-components iedge = avg_ (xyzc [2, :, -1] - xyzc [2, :, 0]) jedge = avg_ (xyzc [2, -1] - xyzc [2, 0]) # compute shading if necessary if (shade) : xyz = xyz1 fill = get3_light (xyz) # The order either requires a transpose or not, reversal of the # order of the first dimension or not, and reversal of the order # of the second dimension or not. # The direction with the minimum magnitude average z-component must # vary fastest in the painting order. If this is the j-direction, # a transpose will be required to make this the i-direction. if abs (iedge) < abs (jedge) : tmp = iedge iedge = jedge jedge = tmp x = transpose (array (xyz1 [0])) y = transpose (array (xyz1 [1])) if fill != None : fill = transpose (fill) else : x = xyz1 [0] y = xyz1 [1] # Zones must be drawn from back to front, which means that the # average z-component of the edge vectors must be positive. This # can be arranged by reversing the order of the elements if # necessary. if iedge < 0.0 : x = reverse (x, 0) y = reverse (y, 0) if fill != None : fill = reverse (fill, 0) if jedge < 0.0 : x = reverse (x, 1) y = reverse (y, 1) if fill != None : fill = reverse (fill, 1) xmax = maxelt_ (x) xmin = minelt_ (x) ymax = maxelt_ (y) ymin = minelt_ (y) if _xfactor != 1. : xmax = xmax + (_xfactor - 1) * (xmax - xmin) / 2.0 xmin = xmin - (_xfactor - 1) * (xmax - xmin) / 2.0 if _yfactor != 1. : ymax = ymax + (_yfactor - 1) * (ymax - ymin) / 2.0 ymin = ymin - (_yfactor - 1) * (ymax - ymin) / 2.0 if _square : xdif = xmax - xmin ydif = ymax - ymin if xdif > ydif : dif = (xdif - ydif) / 2. ymin = ymin - dif ymax = ymax + dif elif ydif > xdif : dif = (ydif - xdif) / 2. xmin = xmin - dif xmax = xmax + dif if fill != None : if len (fill.shape) == 1: fill = bytscl (fill) else: k = fill.shape [0] l = fill.shape [1] fill = reshape ( bytscl (ravel (fill)), (k, l)) if cull == 0 : #transparent mesh if ecolor != None : plm (y, x, color = ecolor) else : plm (y, x) elif ecolor != None and ewidth != None and cmax != None : plf (fill, y, x, edges = edges, ecolor = ecolor, ewidth = ewidth, cmin = 0.0, cmax = cmax, legend = "") elif ecolor != None and ewidth != None : plf (fill, y, x, edges = edges, ewidth = ewidth, cmin = 0.0, ecolor = ecolor, legend = "") elif ecolor != None and cmax != None : plf (fill, y, x, edges = edges, ecolor = ecolor, cmin = 0.0, cmax = cmax, legend = "") elif ewidth != None and cmax != None : plf (fill, y, x, edges = edges, ewidth = ewidth, cmin = 0.0, cmax = cmax, legend = "") elif ecolor != None : plf (fill, y, x, edges = edges, ecolor = ecolor, cmin = 0.0, legend = "") elif ewidth != None : plf (fill, y, x, edges = edges, ewidth = ewidth, cmin = 0.0, legend = "") elif cmax != None : plf (fill, y, x, edges = edges, cmin = 0.0, cmax = cmax, legend = "") else : plf (fill, y, x, edges = edges, cmin = 0.0, legend = "") return [xmin, xmax, ymin, ymax] xyz = xyz_wf (z, y, x, scale = scale) if clear : clear3 ( ) set3_object (plwf, [xyz, fill, shade, edges, ecolor, ewidth, cull, cmax]) if ( _draw3 ) : call_idler ( ) # This will traverse and execute the drawing list # if the default idler has been set. _LightwfError = "LightwfError" def lightwf (cmax) : # lightwf (cmax) # Sets the cmax= parameter interactively, assuming the current # 3D display list contains the result of a previous plwf call. # This changes the color of the brightest surface in the picture. # The darkest surface color can be controlled using the ambient= # keyword to light3. # SEE ALSO: plwf, light3 _draw3_list = get_draw3_list_ () _draw3_n = get_draw3_n_ () list = _draw3_list [_draw3_n:] if list [0] != plwf : raise _LightwfError, "current 3D display list is not a plwf" list [1] [7] = cmax undo3_set_ (lightwf, list) _Xyz_wfError = "Xyz_wfError" def xyz_wf (z, y, x, scale = 1.0) : # xyz_wf (z, [y, x] [,scale = 1.0]) # returns a 3-by-ni-by-nj array whose 0th entry is x, 1th entry # is y, and 2th entry is z. z is ni-by-nj. x and y, if present, # must be the same shape. If not present, integer ranges will # be used to create an equally spaced coordinate grid in x and y. # The function which scales the "topography" of z(x,y) is # potentially useful apart from plwf. # For example, the xyz array used by plwf can be converted from # a quadrilateral mesh plotted using plf to a polygon list plotted # using plfp like this: # xyz= xyz_wf(z,y,x,scale=scale); # ni= shape(z)[1]; # nj= shape(z)[2]; # list = ravel (outer (add, # ravel(outer(add,adders,zeros(nj-1, Int))) + # arange((ni-1)*(nj-1), typecode = Float), # array ( [[0, 1], [nj + 1, nj]]))) # xyz=array([take(ravel(xyz[0]),list), # take(ravel(xyz[1]),list), # take(ravel(xyz[2]),list)]) # nxyz= ones((ni-1)*(nj-1)) * 4; # The resulting array xyz is 3-by-(4*(nj-1)*(ni-1)). # xyz[0:3,4*i:4*(i+1)] are the clockwise coordinates of the # vertices of cell number i. if len (shape (z)) < 2 : raise _Xyz_wfError, "impossible dimensions for z array" nx = shape (z) [0] ny = shape (z) [1] if y == None or x == None : if x != None or y != None : raise _Xyz_wfError, "either give y,x both or neither" x = span (0, ny - 1, ny, nx) y = transpose (span (0, nx - 1, nx, ny)) elif shape (x) != shape (z) or shape (y) != shape (z) : raise _Xyz_wfError, "x, y, and z must all have same dimensions" xyscl = max (maxelt_ (x) - minelt_ (x), maxelt_ (y) - minelt_ (y)) if scale != None: xyscl = xyscl * scale dz = maxelt_ (z) - minelt_ (z) zscl= dz + (dz == 0.0) if zscl : z = z * 0.5 * xyscl /zscl xbar = avg_ (x) ybar = avg_ (y) zbar = avg_ (z) xyz = array ( [x - xbar, y - ybar, z - zbar], Float) return (xyz)