def __init__(self, numSteps, diagonals=False, jitterSize=0, *args, **kwargs): """ numSteps -- diagonals -- Whether to step along the diagonal jitterSize -- How much to jitter around each step of the onion trajectories. """ BaseExplorer.__init__(self, *args, **kwargs) self.numSteps = numSteps self.diagonals = diagonals if self.diagonals: self.offsetDelta = [[-1,-1],[-1,1],[1,1],[1,-1]] else: self.offsetDelta = [[-numSteps,-numSteps],[-numSteps,numSteps], \ [numSteps,numSteps],[numSteps,-numSteps]] if jitterSize == 0: self.jitter = [[0,0]] self.jitterLength = 1 else: # eg. jitterSize = 2 -> # self.jitter = [[-2,0],[-1,0],[1,0],[2,0], # [0,2],[0,1],[0,-1],[0,-2], # [0,0]] self.jitter = [] listi = range(-jitterSize,0) + range(1,jitterSize+1) for i in listi: self.jitter.append([i,0]) for i in listi: self.jitter.append([0,i]) self.jitter.append([0,0]) self.jitterLength = len(self.jitter) assert(self.jitterLength == 4*jitterSize+1)
def __init__(self, sweepLength=4, numRepetitions=1, sweepOffMode=False, maxOffset=None, minVelocity=1, maxVelocity=3, seed=42, *args, **kwargs): """ @param sweepLen: number of presentations per sweep sequence. @param numRepetitions: number of times to present each inward sweep. """ BaseExplorer.__init__(self, *args, **kwargs) # Parameter checking if type(sweepLength) is not int or sweepLength < 1: raise RuntimeError("'sweepLength' should be a positive integer") if type(numRepetitions) is not int or numRepetitions < 1: raise RuntimeError("'numRepetitions' should be a positive integer") # Parameters self._sweepLength = sweepLength self._numRepetitions = numRepetitions self._minVelocity = minVelocity self._maxVelocity = maxVelocity self._sweepOffMode = sweepOffMode self._maxOffset = maxOffset # Internal state self._seqIndex = 0 self._repIndex = 0 self._state = None self._repMemory = None # Prepare PRNG self._rng = random.Random() self._rng.seed(seed)
def __init__(self, sweepDirections=['left', 'right', 'up', 'down'], shiftDuringSweep=1, sweepOffObject=False, *args, **kwargs): """ sweepDirections -- Directions for sweeping. Must be a list containing one or more of 'left', 'right', 'up', and 'down' for horizontal and vertical sweeps, or 'leftup', 'leftdown', 'rightup', and 'rightdown' for diagonal sweeps (or 'upleft, 'downleft', 'upright', and 'downright'). Can also be the string 'all', for all eight directions. shiftDuringSweep -- Number of pixels to jump with each step (during a sweep). sweepOffObject -- Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. """ BaseExplorer.__init__(self, *args, **kwargs) if sweepDirections == 'all': sweepDirections = ['left', 'right', 'up', 'down', 'leftdown', 'leftup', 'rightdown', 'rightup'] else: for direction in sweepDirections: if direction not in ('left', 'right', 'up', 'down', 'leftup', 'upleft', 'leftdown', 'downleft', 'rightup', 'upright', 'rightdown', 'downright'): raise RuntimeError('Unknown sweep direction: %s' % direction) if type(shiftDuringSweep) is not int: raise RuntimeError("'shiftDuringSweep' should be an integer") if type(sweepOffObject) not in (bool, int): raise RuntimeError("'sweepOffObject' should be a boolean") self.sweepDirections = sweepDirections self.shiftDuringSweep = shiftDuringSweep self.sweepOffObject = sweepOffObject
def __init__(self, replacement=True, start=0, equalizeCategories=False, *args, **kwargs): """ replacement -- Whether the same image can be picked multiple times. start -- Number of random choices to skip at the beginning, useful when seeding the random number generator. """ BaseExplorer.__init__(self, *args, **kwargs) if type(replacement) not in (bool, int): raise RuntimeError("'replacement' should be a boolean") if type(start) is not int: raise RuntimeError("'start' should be an integer") self.replacement = replacement self.start = start self.equalizeCategories = equalizeCategories self.imagesByCat = None if not self.replacement: self.history = []
def __init__(self, sweepDirections=["right", "down"], shiftDuringSweep=1, shiftBetweenSweeps=1, sweepOffObject=False, order=None, *args, **kwargs): """ sweepDirections -- Directions for sweeping (a list containing one or more of 'left', 'right', 'up', and 'down'). shiftDuringSweep -- Number of pixels to jump with each step (during a sweep). shiftBetweenSweeps -- Number of pixels to jump in between sweeps (for example, when moving down a line after sweeping across). sweepOffObject -- Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. If True, it will sweep until all of the object moves off the sensor. If set to a floating point number between 0 and 1, then it will sweep until that fraction of the object moves off the sensor. order -- Order in which to iterate (outer to inner). Default progresses through switching images, filters, and sweeping, where switching images is the outer loop and sweeping is the inner loop. Should be a list containing 'image', 'sweep', and 0, 1, ... numFilters-1. """ BaseExplorer.__init__(self, *args, **kwargs) for direction in sweepDirections: if direction not in ('left', 'right', 'up', 'down'): raise RuntimeError("Unknown sweep direction: '%s'" % direction) if type(shiftDuringSweep) is not int: raise RuntimeError("'shiftDuringSweep' must be an integer") if type(shiftBetweenSweeps) is not int: raise RuntimeError("'shiftBetweenSweeps' must be an integer") if float(sweepOffObject) < 0 or float(sweepOffObject) > 1.0: raise RuntimeError( "'sweepOffObject' should be a boolean, or floating point" " number between 0 and 1") if order is not None: if 'image' not in order or 'sweep' not in order: raise RuntimeError( "'order' must contain both 'image' and 'sweep'") if len([x for x in order if type(x) == str]) > 2: raise RuntimeError( "'order' must contain no other strings besides " "'image' and 'sweep'") self.customOrder = True else: self.customOrder = False self.sweepDirections = sweepDirections self.shiftDuringSweep = shiftDuringSweep self.shiftBetweenSweeps = shiftBetweenSweeps self.sweepOffObject = sweepOffObject self.order = order
def __init__( self, sweepDirections=["right", "down"], shiftDuringSweep=1, shiftBetweenSweeps=1, sweepOffObject=False, order=None, *args, **kwargs ): """ sweepDirections -- Directions for sweeping (a list containing one or more of 'left', 'right', 'up', and 'down'). shiftDuringSweep -- Number of pixels to jump with each step (during a sweep). shiftBetweenSweeps -- Number of pixels to jump in between sweeps (for example, when moving down a line after sweeping across). sweepOffObject -- Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. If True, it will sweep until all of the object moves off the sensor. If set to a floating point number between 0 and 1, then it will sweep until that fraction of the object moves off the sensor. order -- Order in which to iterate (outer to inner). Default progresses through switching images, filters, and sweeping, where switching images is the outer loop and sweeping is the inner loop. Should be a list containing 'image', 'sweep', and 0, 1, ... numFilters-1. """ BaseExplorer.__init__(self, *args, **kwargs) for direction in sweepDirections: if direction not in ("left", "right", "up", "down"): raise RuntimeError("Unknown sweep direction: '%s'" % direction) if type(shiftDuringSweep) is not int: raise RuntimeError("'shiftDuringSweep' must be an integer") if type(shiftBetweenSweeps) is not int: raise RuntimeError("'shiftBetweenSweeps' must be an integer") if float(sweepOffObject) < 0 or float(sweepOffObject) > 1.0: raise RuntimeError("'sweepOffObject' should be a boolean, or floating point" " number between 0 and 1") if order is not None: if "image" not in order or "sweep" not in order: raise RuntimeError("'order' must contain both 'image' and 'sweep'") if len([x for x in order if type(x) == str]) > 2: raise RuntimeError("'order' must contain no other strings besides " "'image' and 'sweep'") self.customOrder = True else: self.customOrder = False self.sweepDirections = sweepDirections self.shiftDuringSweep = shiftDuringSweep self.shiftBetweenSweeps = shiftBetweenSweeps self.sweepOffObject = sweepOffObject self.order = order
def __init__(self, radius=1, stepsize=1, minradius=None, includeCenter=False, sweepOffObject=True, randomSelections=0, *args, **kwargs): """ radius - the radius of the spiral sweep """ if minradius is None: minradius = stepsize assert (radius >= 1) if not ((radius >= stepsize) and (radius % stepsize == 0)): raise RuntimeError("radius must be a multiple of stepsize") if not ((minradius >= stepsize) and (minradius % stepsize == 0)): raise RuntimeError("minradius must be a multiple of stepsize") if type(sweepOffObject) not in (bool, int): raise RuntimeError("'sweepOffObject' should be a boolean") BaseExplorer.__init__(self, *args, **kwargs) self.sweepOffObject = sweepOffObject # Generate a list of possible offsets for this stepsize and radius self.offsets = [] if includeCenter: self.offsets += [(0, 0)] for i in range(minradius, radius + 1, stepsize): # Generate top row (not including sides) self.offsets += [(x, -i) for x in range(-i + stepsize, i, stepsize)] # Generate right edge (including top row, but not bottom row) self.offsets += [(i, y) for y in range(-i, i, stepsize)] # Generate bottom edge (not including left edge, including right edge) self.offsets += [(x, i) for x in range(i, -i, -stepsize)] # Generate left edge (including top and bottom row) self.offsets += [(-i, y) for y in range(i, -i - stepsize, -stepsize)] self.index = 0 # User-set parameters to control random selection. self.randomSelections = randomSelections # The cache of randomly selected offsets for the current image/filter. self._selectedOffsets = None
def __init__(self, radius=4, *args, **kwargs): """ @param radius: the distance from the center, in pixels, at which the sweeps start; @param numRepetitions: number of times to present each inward sweep. """ BaseExplorer.__init__(self, *args, **kwargs) # Parameter checking if type(radius) is not int or radius < 1: raise RuntimeError("'radius' should be a positive integer") # Parameters self._radius = radius # Internal state self._itersDone = 0
def __init__(self, shift=1, replacement=True, *args, **kwargs): """ shift -- Number of pixels to move from the center ("radius" of the eye movement square). replacement -- Whether the same image/position can be picked twice. """ BaseExplorer.__init__(self, *args, **kwargs) self.shift = shift self.replacement = replacement if not self.replacement: self.history = []
def __init__(self, shift=1, aggregate='sum', *args, **kwargs): """ @param shift -- Number of pixels to move from the center ("radius" of the eye movement square). @param aggregate -- A function that's used by inference analysis to aggregate the results of different eye movement presentations. Valid values are 'sum', 'average', 'product' and 'max'. The default is 'sum'. """ BaseExplorer.__init__(self, *args, **kwargs) assert aggregate in ('sum', 'average', 'max', 'product') self.aggregate_func = aggregate self.shift = shift
def __init__(self, jumpOffObject=False, numJumpsPerImage=None, numVisitsPerImage=None, spaceShape=None, *args, **kwargs): """ Parameters: ----------------------------------------------------------------- jumpOffObject: Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. numJumpsPerImage: The number of iterations for which RandomJump should dwell on one image before moving on to the next one. numVisitsPerImage: The number of times RandomJump should visit each image (and do numJumpsPerImage jumps on it). spaceShape: The (height, width) of the 2-D space to explore. This constrains how far away from the center point an image is allowed to be presented. """ BaseExplorer.__init__(self, *args, **kwargs) if type(jumpOffObject) not in (bool, int): raise RuntimeError("'jumpOffObject' should be a boolean") if numJumpsPerImage is not None and type(numJumpsPerImage) is not int: raise RuntimeError("'numJumpsPerImage' should be an integer") if numVisitsPerImage is not None and type( numVisitsPerImage) is not int: raise RuntimeError("'numVisitsPerImage' should be an integer") if numVisitsPerImage is not None and numJumpsPerImage is None: raise RuntimeError("Must specify 'numJumpsPerImage'" " when using 'numVisitsPerImage'") if spaceShape is not None and \ (len(spaceShape) != 2 or spaceShape[0] < 1 or spaceShape[1] < 1): raise RuntimeError( "'spaceShape' should be a 2-item tuple specifying the" "(height, width) of the overall space to explore.") self.jumpOffObject = jumpOffObject self.numJumpsPerImage = numJumpsPerImage self.numVisitsPerImage = numVisitsPerImage self.spaceShape = spaceShape # Keeps track of how many jumps on this image self.numJumpsThisImage = 0 self.lastImageIndex = None
def __init__(self, radius=1, stepsize=1, minradius=None, includeCenter=False, sweepOffObject=True, randomSelections=0, *args, **kwargs): """ radius - the radius of the spiral sweep """ if minradius is None: minradius = stepsize assert(radius >= 1) if not ((radius >= stepsize) and (radius % stepsize == 0)): raise RuntimeError("radius must be a multiple of stepsize") if not ((minradius >= stepsize) and (minradius % stepsize == 0)): raise RuntimeError("minradius must be a multiple of stepsize") if type(sweepOffObject) not in (bool, int): raise RuntimeError("'sweepOffObject' should be a boolean") BaseExplorer.__init__(self, *args, **kwargs) self.sweepOffObject = sweepOffObject # Generate a list of possible offsets for this stepsize and radius self.offsets = [] if includeCenter: self.offsets += [(0,0)] for i in range(minradius, radius+1, stepsize): # Generate top row (not including sides) self.offsets += [(x, -i) for x in range(-i+stepsize, i, stepsize)] # Generate right edge (including top row, but not bottom row) self.offsets += [(i, y) for y in range(-i, i, stepsize)] # Generate bottom edge (not including left edge, including right edge) self.offsets += [(x, i) for x in range(i, -i, -stepsize)] # Generate left edge (including top and bottom row) self.offsets += [(-i, y) for y in range(i, -i-stepsize, -stepsize)] self.index = 0 # User-set parameters to control random selection. self.randomSelections = randomSelections # The cache of randomly selected offsets for the current image/filter. self._selectedOffsets = None
def __init__(self, jumpOffObject=False, numJumpsPerImage=None, numVisitsPerImage=None, spaceShape=None, *args, **kwargs): """ Parameters: ----------------------------------------------------------------- jumpOffObject: Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. numJumpsPerImage: The number of iterations for which RandomJump should dwell on one image before moving on to the next one. numVisitsPerImage: The number of times RandomJump should visit each image (and do numJumpsPerImage jumps on it). spaceShape: The (height, width) of the 2-D space to explore. This constrains how far away from the center point an image is allowed to be presented. """ BaseExplorer.__init__(self, *args, **kwargs) if type(jumpOffObject) not in (bool, int): raise RuntimeError("'jumpOffObject' should be a boolean") if numJumpsPerImage is not None and type(numJumpsPerImage) is not int: raise RuntimeError("'numJumpsPerImage' should be an integer") if numVisitsPerImage is not None and type(numVisitsPerImage) is not int: raise RuntimeError("'numVisitsPerImage' should be an integer") if numVisitsPerImage is not None and numJumpsPerImage is None: raise RuntimeError("Must specify 'numJumpsPerImage'" " when using 'numVisitsPerImage'") if spaceShape is not None and \ (len(spaceShape) != 2 or spaceShape[0] < 1 or spaceShape[1] < 1): raise RuntimeError("'spaceShape' should be a 2-item tuple specifying the" "(height, width) of the overall space to explore.") self.jumpOffObject = jumpOffObject self.numJumpsPerImage = numJumpsPerImage self.numVisitsPerImage = numVisitsPerImage self.spaceShape = spaceShape # Keeps track of how many jumps on this image self.numJumpsThisImage = 0 self.lastImageIndex = None
def __init__(self, filename, *args, **kwargs): """ filename -- Path to the file with the pickled dictionary mapping image filenames to fixation points. """ BaseExplorer.__init__(self, *args, **kwargs) # Load the fixation points self.points = pickle.load(open(filename)) self.pointIndex = None self.currentPoints = None # Retain just the enclosing directory and filename, not the full path self.doSaliencySize = True keys = self.points.keys() for key in keys: path, filename = os.path.split(key) key2 = os.path.join(os.path.split(path)[1], filename) if key2 != key: self.points[key2] = self.points[key] self.points.pop(key) if 'saliencySize' not in self.points[key2]: self.doSaliencySize = False
def __init__(self, dimensions=None, sweepOffObject=False, crossDirectories=False, minSweepLength=0, pattern=None, *args, **kwargs): """ dimensions -- List of dimensions through which to sweep. Each element is a string with the name of a dimension, or a dictionary with more options (see below). 'translation', is normal translation sweeping, and 'image' holds the position constant as it moves across images. Include an integer from [0, numFilters-1] to specifying sweeping across the outputs of a certain filter while holding the rest of the position constant. If None, all dimensions are used, with default options. sweepOffObject -- Whether the sensor can only include a part of the object, as specified by the bounding box. If False, it will only move to positions that include as much of the object as possible. crossDirectories -- ** DEPRECATED ** If False and sweeping through images, the explorer looks at the filename of the images and stops sweeping when it would go to an image whose enclosing directory is different than the current image. minSweepLength -- Minimum length for each sweep. If a sweep is too short, image and filter sweeps continue smoothly if 'wraparound' is True, and otherwise they switch directions. Translation sweeps bounce into a new direction, excluding the opposite direction of the current sweep. pattern -- Pattern to use for extracting extra dimensions from image filenames. If you use the 'dimensions' argument, make sure to list these extra dimensions or they won't be used. Sweeps will hold all other dimensions constant while going through the selected dimension in sorted order. Can either be a tuple: (separator, dimensionName, dimensionName, ...), or a regular expression that extracts named dimensions. Example: If the filenames look like this: "Apple_1 50 90 foo.png", where "Apple_1" is the name of the object, "50" is the vertical angle, "90" is the horizontal angle, and "foo" is extra text to ignore, MultiSweep will sweep through the vertical and horizontal dimensions with either of these values for 'pattern' (and ['vertical', 'horizontal'] as the value for 'dimensions'): Tuple: (" ", "object", "vertical", "horizontal", None) The separator tells MultiSweep to split up the name on spaces, and the None argument specifies that the "foo" part of the name is not a dimension and should be ignored. Regular expression: "^(?P<object>\w+)\s(?P<vertical>\d+)\s(?P<horizontal>\d+)\s\w+$" "(?P<NAME>PATTERN)" is a Python-specific syntax for extracting named groups. Note that "object" is extracted as its own dimension even though it could have been ignored. This allows for multiple objects (such as "Apple_1" and "Apple_2") to appear in the same directory without MultiSweep generating sweeps across objects. After the dimensions are extracted, they are converted to ints or floats if possible. Then they are sorted using Python's list.sort() method. Dictionaries for dimensions take the following keywords: name : Name of the dimension, one of: - 'translation' (translation sweeping) - 'image' (cycle through images) - the name of a dimension extracted via 'pattern' - an integer specifying the index of a filter shift : Number of steps to jump on each iteration. For example, 2 means to jump by 2 pixels or 2 images. probability : Probability of randomly selecting this dimension. wraparound : Whether to 'wrap around' from one end of the dimension to the other, rather than stopping the sweep (or changing directions, if minSweepLength has not been met). ** ONLY IMPLEMENTED FOR SWEEPING THROUGH FILTER OUTPUTS WITH SHIFT == 1 ** sweepOffObject : Overrides the main 'sweepOffObject' parameter. """ BaseExplorer.__init__(self, *args, **kwargs) if type(sweepOffObject) not in (bool, int): raise RuntimeError("'sweepOffObject' should be a boolean") if type(crossDirectories) not in (bool, int): raise RuntimeError("'crossDirectories' should be a boolean") if type(minSweepLength) is not int: raise RuntimeError("'minSweepLength' should be an integer") self.sweepOffObject = sweepOffObject self.crossDirectories = crossDirectories self.minSweepLength = minSweepLength # Get dimensions to be parsed from filenames self.pattern = pattern self.parsedDimensions = [] if pattern: if type(pattern) in (list, tuple): if type(pattern) is tuple: pattern = list(pattern) self.parsedDimensions = pattern[1:] while None in self.parsedDimensions: self.parsedDimensions.remove(None) elif isinstance(pattern, basestring): self.parsedDimensions = re.findall("\(\?P<([^>]+)>", pattern) else: raise ValueError("'pattern' should be a list/tuple or string") # Extra instance variables for parsed dimensions self.parsedIndices = [] self.parsedIndex = 0 # Figure out all the dimensions if not dimensions: self.allDimensions = True self.dimensions = ['translation'] if not self.parsedDimensions: self.dimensions.append('image') else: self.allDimensions = False if type(dimensions) in (str, int): dimensions = [dimensions] self.dimensions = list(dimensions) # Add the dimensions to be parsed from filenames self.dimensions += self.parsedDimensions for i, d in enumerate(self.dimensions): if type(d) in (str, int): self.dimensions[i] = self._newSweepDictionary(name=d) else: self.dimensions[i] = self._newSweepDictionary(**d) self._calculateProbabilities()
def __init__(self, spaceShape=(5,5), spreadShape=None, spreadRadius=None, stepSize=1, resetEveryPos=False, verbosity=0, *args, **kwargs): """ spaceShape: The (height, width) of the 2-D space to explore. This sets the number of center-points. spreadShape: The shape (height, width) of the area around each center-point to explore if you want to spread in a square area. If this is specified, then radius must be None. spreadRadius: The radius of the spread if you want to spread in a circular area. If this is specified, then spreadShape must be None. When set to R, this explorer will visit all positions where: int(round(sqrt(dx*dx + dy*dy))) <= radius stepSize: The step size. How big each step is, in pixels. This controls *both* the spacing of the center-points within the block and the points we explore around each center-point. When spreadRadius is used to define the spread shape, then it will only visit points within stepSize*spreadRadius of the center point and insure that no two points it visits within this area are closer than stepSize from each other, where distance is defined as: int(round(sqrt(dx*dx + dy*dy))) This euclidean distance is NOT used to determine where the center points are - they are always laid out in a grid with x spacing and y spacing of stepSize. resetEveryPos: If False (the default), output a reset only when we first visit a new center point. This is what is normally used for training. If True, then output a reset on every iteration. This is often used for flash inference testing. """ BaseExplorer.__init__(self, *args, **kwargs) # Parameter checking if type(stepSize) is not int or stepSize < 1: raise RuntimeError("'stepSize' should be a positive integer") if len(spaceShape) != 2 or spaceShape[0] < 1 or spaceShape[1] < 1: raise RuntimeError("'spaceShape' should be a 2-item tuple specifying the" "(height, width) of the overall space to explore.") if spreadShape is not None: if spreadRadius is not None: raise RuntimeError ("When spreadShape is used, spreadRadius must be set to None") if len(spreadShape) != 2 or spreadShape[0] < 1 or spreadShape[1] < 1: raise RuntimeError("'spreadShape' should be a 2-item tuple specifying the" "(height, width) of the of the area round each center point to" "explore.") if spreadRadius is None and spreadShape is None: raise RuntimeError ("Either spreadRadius or spreadShape must be defined") # Parameters self._spaceShape = spaceShape self._spreadShape = spreadShape self._spreadRadius = spreadRadius self._stepSize = stepSize self._verbosity = verbosity self._resetEveryPos = resetEveryPos # ===================================================================== # Init data structures # What is the range on the X and Y offsets of the center points? shape = self._spaceShape # If the shape is (1,1), special case of just 1 center point if shape[0] == 1 and shape[1] == 1: self._centerOffsets = [(0,0)] else: xMin = -1 * (shape[1] // 2) xMax = xMin + shape[1] - 1 xPositions = range(stepSize * xMin, stepSize * xMax + 1, stepSize) yMin = -1 * (shape[0] // 2) yMax = yMin + shape[0] - 1 yPositions = range(stepSize * yMin, stepSize * yMax + 1, stepSize) self._centerOffsets = list(cross(yPositions, xPositions)) self._numCenterOffsets = len(self._centerOffsets) # ---------------------------------------------------------------- # Figure out the spread points based on spreadShape: if self._spreadShape is not None: # What is the range on the X and Y offsets of the spread points? shape = self._spreadShape # If the shape is (1,1), special case of no spreading around each center # point if shape[0] == 1 and shape[1] == 1: self._spreadOffsets = [(0,0)] else: xMin = -1 * (shape[1] // 2) xMax = xMin + shape[1] - 1 xPositions = range(stepSize * xMin, stepSize * xMax + 1, stepSize) yMin = -1 * (shape[0] // 2) yMax = yMin + shape[0] - 1 yPositions = range(stepSize * yMin, stepSize * yMax + 1, stepSize) self._spreadOffsets = list(cross(yPositions, xPositions)) # Put the (0,0) entry first self._spreadOffsets.remove((0,0)) self._spreadOffsets.insert(0, (0,0)) # --------------------------------------------------------------------- # Figure out the spread points based on spreadRadius else: # Special case of spreadRadius = 0:, no spreading around each center point if spreadRadius == 0: self._spreadOffsets = [(0,0)] # Build up a list of all offsets within spreadRadius * stepSize else: self._spreadOffsets = [] for y in range(-spreadRadius*stepSize, spreadRadius*stepSize+1): for x in range(-spreadRadius*stepSize, spreadRadius*stepSize+1): distance = int(round(math.sqrt(x*x + y*y))) if distance > spreadRadius*stepSize: continue # Make sure it's not closer than stepSize to another point within # the spread if not (x==0 and y==0) and stepSize > 1: tooClose = False for (otherY, otherX) in self._spreadOffsets: dx = x - otherX dy = y - otherY distance = int(round(math.sqrt(dx*dx + dy*dy))) if distance < stepSize: tooClose = True break if tooClose: continue self._spreadOffsets.append((y,x)) # Put the (0,0) entry first self._spreadOffsets.remove((0,0)) self._spreadOffsets.insert(0, (0,0)) if self._verbosity >= 1: print "Visiting spread positions:", self._spreadOffsets self._numSpreadOffsets = len(self._spreadOffsets) # Set start position self._centerPosIdx = 0 # Which center point self._spreadPosIdx = 0 # radial position around the center point