Package topo :: Package base :: Module patterngenerator
[hide private]
[frames] | no frames]

Source Code for Module topo.base.patterngenerator

  1  """ 
  2  PatternGenerator abstract class and basic example concrete class. 
  3   
  4  $Id: patterngenerator.py 10672 2009-10-28 01:00:50Z ceball $ 
  5  """ 
  6  __version__='$Revision: 10672 $' 
  7   
  8   
  9  from math import pi 
 10   
 11  from numpy import add,subtract,cos,sin 
 12   
 13  import param 
 14  from param.parameterized import ParamOverrides 
 15   
 16  from boundingregion import BoundingBox, BoundingRegionParameter 
 17  from sheetcoords import SheetCoordinateSystem 
 18  from functionfamily import TransferFn 
 19   
 20   
 21  # CEBALERT: PatternGenerator has become a bit of a monster abstract 
 22  # class.  Can it be split into the minimum required to specify the 
 23  # interface, with a subclass implementing the rest (this subclass 
 24  # still being above the rest of the PatternGenerators)?  We want to 
 25  # make it easy to add new types of PatternGenerator that don't match 
 26  # the assumptions of the current ones (OneDPowerSpectrum is an example 
 27  # of a PG that doesn't match the current assumptions), but still lets 
 28  # them be used like the current ones. 
 29  # (PatternGenerator-->TwoDPatternGenerator?) 
 30   
 31  # JLALERT: PatternGenerator should have 
 32  # override_plasticity_state/restore_plasticity_state functions which 
 33  # can override the plasticity of any output_fn that has state, in case 
 34  # anyone ever uses such an object in a PatternGenerator.  Will also 
 35  # need to support Composite patterns. 
 36   
 37   
38 -class PatternGenerator(param.Parameterized):
39 """ 40 A class hierarchy for callable objects that can generate 2D patterns. 41 42 Once initialized, PatternGenerators can be called to generate a 43 value or a matrix of values from a 2D function, typically 44 accepting at least x and y. 45 46 A PatternGenerator's Parameters can make use of Parameter's 47 precedence attribute to specify the order in which they should 48 appear, e.g. in a GUI. The precedence attribute has a nominal 49 range of 0.0 to 1.0, with ordering going from 0.0 (first) to 1.0 50 (last), but any value is allowed. 51 52 The orientation and layout of the pattern matrices is defined by 53 the SheetCoordinateSystem class, which see. 54 55 Note that not every parameter defined for a PatternGenerator will 56 be used by every subclass. For instance, a Constant pattern will 57 ignore the x, y, orientation, and size parameters, because the 58 pattern does not vary with any of those parameters. However, 59 those parameters are still defined for all PatternGenerators, even 60 Constant patterns, to allow PatternGenerators to be scaled, rotated, 61 translated, etc. uniformly. 62 """ 63 __abstract = True 64 65 bounds = BoundingRegionParameter( 66 default=BoundingBox(points=((-0.5,-0.5), (0.5,0.5))),precedence=-1, 67 doc="BoundingBox of the area in which the pattern is generated.") 68 69 xdensity = param.Number(default=10,bounds=(0,None),precedence=-1,doc=""" 70 Density (number of samples per 1.0 length) in the x direction.""") 71 72 ydensity = param.Number(default=10,bounds=(0,None),precedence=-1,doc=""" 73 Density (number of samples per 1.0 length) in the y direction. 74 Typically the same as the xdensity.""") 75 76 x = param.Number(default=0.0,softbounds=(-1.0,1.0),precedence=0.20,doc=""" 77 X-coordinate location of pattern center.""") 78 79 y = param.Number(default=0.0,softbounds=(-1.0,1.0),precedence=0.21,doc=""" 80 Y-coordinate location of pattern center.""") 81 82 83 position = param.Composite(attribs=['x','y'],precedence=-1,doc=""" 84 Coordinates of location of pattern center. 85 Provides a convenient way to set the x and y parameters together 86 as a tuple (x,y), but shares the same actual storage as x and y 87 (and thus only position OR x and y need to be specified).""") 88 89 orientation = param.Number(default=0.0,softbounds=(0.0,2*pi),precedence=0.40,doc=""" 90 Polar angle of pattern, i.e., the orientation in the Cartesian coordinate 91 system, with zero at 3 o'clock and increasing counterclockwise.""") 92 93 size = param.Number(default=1.0,bounds=(0.0,None),softbounds=(0.0,6.0), 94 precedence=0.30,doc="""Determines the overall size of the pattern.""") 95 96 scale = param.Number(default=1.0,softbounds=(0.0,2.0),precedence=0.10,doc=""" 97 Multiplicative strength of input pattern, defaulting to 1.0""") 98 99 offset = param.Number(default=0.0,softbounds=(-1.0,1.0),precedence=0.11,doc=""" 100 Additive offset to input pattern, defaulting to 0.0""") 101 102 mask = param.Parameter(default=None,precedence=-1,doc=""" 103 Optional object (expected to be an array) with which to multiply the 104 pattern array after it has been created, before any output_fns are 105 applied. This can be used to shape the pattern.""") 106 107 # Note that the class type is overridden to PatternGenerator below 108 mask_shape = param.ClassSelector(param.Parameterized,default=None,precedence=0.06,doc=""" 109 Optional PatternGenerator used to construct a mask to be applied to 110 the pattern.""") 111 112 output_fns = param.HookList(default=[],class_=TransferFn,precedence=0.08,doc=""" 113 Optional function(s) to apply to the pattern array after it has been created. 114 Can be used for normalization, thresholding, etc.""") 115
116 - def __call__(self,**params_to_override):
117 """ 118 Call the subclass's 'function' method on a rotated and scaled coordinate system. 119 120 Creates and fills an array with the requested pattern. If 121 called without any params, uses the values for the Parameters 122 as currently set on the object. Otherwise, any params 123 specified override those currently set on the object. 124 """ 125 p=ParamOverrides(self,params_to_override) 126 127 # ALERT: position parameter is not currently supported: 128 # position=params_to_override.get('position',None) 129 # if position is not None: 130 # x,y = position 131 132 self._setup_xy(p.bounds,p.xdensity,p.ydensity,p.x,p.y,p.orientation) 133 fn_result = self.function(p) 134 self._apply_mask(p,fn_result) 135 result = p.scale*fn_result+p.offset 136 137 for of in p.output_fns: 138 of(result) 139 140 return result
141 142
143 - def _setup_xy(self,bounds,xdensity,ydensity,x,y,orientation):
144 """ 145 Produce pattern coordinate matrices from the bounds and 146 density (or rows and cols), and transforms them according to 147 x, y, and orientation. 148 """ 149 self.debug(lambda:"bounds=%s, xdensity=%s, ydensity=%s, x=%s, y=%s, orientation=%s"%(bounds,xdensity,ydensity,x,y,orientation)) 150 # Generate vectors representing coordinates at which the pattern 151 # will be sampled. 152 153 # CB: note to myself - use slice_._scs if supplied? 154 x_points,y_points = SheetCoordinateSystem(bounds,xdensity,ydensity).sheetcoordinates_of_matrixidx() 155 156 # Generate matrices of x and y sheet coordinates at which to 157 # sample pattern, at the correct orientation 158 self.pattern_x, self.pattern_y = self._create_and_rotate_coordinate_arrays(x_points-x,y_points-y,orientation)
159 160
161 - def function(self,p):
162 """ 163 Function to draw a pattern that will then be scaled and rotated. 164 165 Instead of implementing __call__ directly, PatternGenerator 166 subclasses will typically implement this helper function used 167 by __call__, because that way they can let __call__ handle the 168 scaling and rotation for them. Alternatively, __call__ itself 169 can be reimplemented entirely by a subclass (e.g. if it does 170 not need to do any scaling or rotation), in which case this 171 function will be ignored. 172 """ 173 raise NotImplementedError
174 175
176 - def _create_and_rotate_coordinate_arrays(self, x, y, orientation):
177 """ 178 Create pattern matrices from x and y vectors, and rotate 179 them to the specified orientation. 180 """ 181 # Using this two-liner requires that x increase from left to 182 # right and y decrease from left to right; I don't think it 183 # can be rewritten in so little code otherwise - but please 184 # prove me wrong. 185 pattern_y = subtract.outer(cos(orientation)*y, sin(orientation)*x) 186 pattern_x = add.outer(sin(orientation)*y, cos(orientation)*x) 187 return pattern_x, pattern_y
188 189
190 - def _apply_mask(self,p,mat):
191 """Create (if necessary) and apply the mask to the given matrix mat.""" 192 mask = p.mask 193 ms=p.mask_shape 194 if ms is not None: 195 mask = ms(x=p.x+p.size*(ms.x*cos(p.orientation)-ms.y*sin(p.orientation)), 196 y=p.y+p.size*(ms.x*sin(p.orientation)+ms.y*cos(p.orientation)), 197 orientation=ms.orientation+p.orientation,size=ms.size*p.size, 198 bounds=p.bounds,ydensity=p.ydensity,xdensity=p.xdensity) 199 if mask is not None: 200 mat*=mask
201 202 203 # Override class type; must be set here rather than when mask_shape is declared, 204 # to avoid referring to class not yet constructed 205 PatternGenerator.params('mask_shape').class_=PatternGenerator 206 207 208 # Trivial example of a PatternGenerator, provided for when a default is 209 # needed. The other concrete PatternGenerator classes are stored in 210 # patterns/, to be imported as needed. 211 from numpy.oldnumeric import ones, Float 212
213 -class Constant(PatternGenerator):
214 """Constant pattern generator, i.e., a solid, uniform field of the same value.""" 215 216 # The orientation is ignored, so we don't show it in 217 # auto-generated lists of parameters (e.g. in the GUI) 218 orientation = param.Number(precedence=-1) 219 220 # Optimization: We use a simpler __call__ method here to skip the 221 # coordinate transformations (which would have no effect anyway)
222 - def __call__(self,**params_to_override):
223 p = ParamOverrides(self,params_to_override) 224 225 shape = SheetCoordinateSystem(p.bounds,p.xdensity,p.ydensity).shape 226 227 result = p.scale*ones(shape, Float)+p.offset 228 self._apply_mask(p,result) 229 230 for of in p.output_fns: 231 of(result) 232 233 return result
234