# # $Id: contour.py,v 1.21 2004/03/08 04:30:06 mrnolta Exp $ # # Copyright (C) 2001 Mike Nolta # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # from biggles import \ _series, _message, \ _LineComponent, _PathObject, _PlotComponent, BigglesError from geometry import * import _biggles import Numeric def _span( a, b, n ): return a + float(b - a)*Numeric.arange( 0, n, 1, Numeric.Float )/(n-1) def _pop2( x, i, j ): if i < j: b = x.pop( j ) a = x.pop( i ) elif i > j: a = x.pop( i ) b = x.pop( j ) return a, b def _unzip( line ): x = [] y = [] for x0,y0 in line: x.append( x0 ) y.append( y0 ) return x, y class Contour( _LineComponent ): def __init__( self, x, y, z, z0, **kw ): _LineComponent.__init__( self ) self.kw_init( kw ) self.x = x self.y = y self.z = z self.z0 = z0 def limits( self ): p = min(self.x), min(self.y) q = max(self.x), max(self.y) return BoundingBox( p, q ) def _get_contours( self ): segs = _biggles.contour_segments( \ self.x, self.y, self.z, self.z0 ) open = [[ segs[0][0], segs[0][1] ]] closed = [] for a,b in segs[1:]: xxx = [] for i in range(len(open)): begin = open[i][0] end = open[i][-1] if a == begin: xxx.append( (i,0,b) ) elif a == end: xxx.append( (i,1,b) ) if b == begin: xxx.append( (i,0,a) ) elif b == end: xxx.append( (i,1,a) ) if len(xxx) == 0: open.append( [a,b] ) elif len(xxx) == 1: i,end,pt = xxx[0] if end == 0: open[i].insert( 0, pt ) else: open[i].append( pt ) elif len(xxx) == 2: i0,end0,pt0 = xxx[0] i1,end1,pt1 = xxx[1] if i0 == i1: # closed l0 = open.pop( i0 ) l0.append( l0[0] ) closed.append( l0 ) else: l0, l1 = _pop2( open, i0, i1 ) m = None if end0==1 and end1==0: m = l0 + l1 elif end0==0 and end1==1: m = l1 + l0 elif end0==0 and end1==0: l0.reverse() m = l0 + l1 elif end0==1 and end1==1: l1.reverse() m = l0 + l1 if m is not None: open.append( m ) else: _message( "contour: m is None" ) elif len(xxx) > 2: _message( "contour: len(xxx) > 2" ) return open + closed def make( self, context ): lines = self._get_contours() for line in lines: x, y = _unzip( line ) u, v = context.geom.call_vec( x, y ) self.add( _PathObject(u, v) ) def _func_color_black( i, n, z0, z_min, z_max ): return 0x000000 def _func_linetype_dotneg( i, n, z0, z_min, z_max ): if z0 < 0: return "dotted" return "solid" def _func_linewidth_placeholder( i, n, z0, z_min, z_max ): return 1 class Contours( _PlotComponent ): _named_func_color = { "black" : _func_color_black, } _named_func_linetype = { "dotted-negative" : _func_linetype_dotneg, } _named_func_linewidth = { "placeholder" : _func_linewidth_placeholder, } def __init__( self, z, x=None, y=None, zrange=None, **kw ): _PlotComponent.__init__( self ) #apply( self.conf_setattr, ("Contours",), kw ) self.conf_setattr( "Contours" ) self.kw_init( kw ) self.z = z self.x = x self.y = y self.zrange = zrange def _get_coords( self ): dim = self.z.shape x = self.x if x is None: x = range(dim[0]) y = self.y if y is None: y = range(dim[1]) return x, y, self.z def limits( self ): x, y, z = self._get_coords() return BoundingBox( (min(x),min(y)), (max(x),max(y)) ) def make( self, context ): self.clear() x, y, z = self._get_coords() limits = self.limits() xr = limits.xrange() yr = limits.yrange() zr = self.zrange if zr is None: zr = _biggles.range( z ) levels = self.levels if type(levels) == type(0): levels = _series( 1, self.levels, \ float(zr[1]-zr[0])/(self.levels+1), zr[0] ) colorfunc = self.func_color if type(colorfunc) == type(""): colorfunc = self._named_func_color[colorfunc] linefunc = self.func_linetype if type(linefunc) == type(""): linefunc = self._named_func_linetype[linefunc] widthfunc = self.func_linewidth if type(widthfunc) == type(""): widthfunc = self._named_func_linewidth[widthfunc] nlevels = len(levels) for i in range(nlevels): kw = {} z0 = levels[i] args = i, nlevels, z0, zr[0], zr[1] if colorfunc is not None: color = apply( colorfunc, args ) if color is not None: kw["color"] = color if linefunc is not None: linetype = apply( linefunc, args ) if linetype is not None: kw["linetype"] = linetype if widthfunc is not None: linewidth = apply( widthfunc, args ) if linewidth is not None: kw["linewidth"] = linewidth c = apply( Contour, (x, y, z, z0), kw ) self.add( c )