def get_pixel_location(self, index): """ Returns a given pixel's location in scene coordinates. """ loc = self._pixel_locations_cache.get(index, None) if loc is None: strand, address, pixel = BufferUtils.index_to_logical(index) f = self.fixture(strand, address) if pixel == 0: loc = f.pos1 elif pixel == (f.pixels - 1): loc = f.pos2 else: x1, y1 = f.pos1 x2, y2 = f.pos2 scale = float(pixel) / f.pixels relx, rely = ((x2 - x1) * scale, (y2 - y1) * scale) loc = (x1 + relx, y1 + rely) self._pixel_locations_cache[index] = loc return loc
def _spawn(self, current_strand, current_fixture, population): children = set() neighbors = self.pattern.scene().get_pixel_neighbors(self.loc) neighbors = [BufferUtils.index_to_logical(n) for n in neighbors] random.shuffle(neighbors) # Iterate over candidate pixels that aren't on the current fixture candidates = [ n for n in neighbors if n[:2] != (current_strand, current_fixture) ] for candidate in candidates: child_index = BufferUtils.logical_to_index(candidate) if len(children) == 0: # Spawn at least one new dragon to replace the old one. This first one skips the growth. dir = 1 if candidate[2] == 0 else -1 child = _Dragon(self.pattern, child_index, dir, self.pattern._current_time) child.growing = False child.alive = True children.add(child) population += 1 elif (population < self.pattern.parameter('pop-limit').get() and random.random() < self.pattern.parameter('birth-rate').get()): # Randomly spawn new dragons dir = 1 if candidate[2] == 0 else -1 child = _Dragon(self.pattern, child_index, dir, self.pattern._current_time) children.add(child) population += 1 return children
def get_pixel_neighbors(self, index): """ Returns a list of pixel addresses that are adjacent to the given address. """ neighbors = self._pixel_neighbors_cache.get(index, None) if neighbors is None: neighbors = [] strand, address, pixel = BufferUtils.index_to_logical(index) f = self.fixture(strand, address) neighbors = [BufferUtils.logical_to_index((strand, address, p)) for p in f.pixel_neighbors(pixel)] if (pixel == 0) or (pixel == f.pixels - 1): # If this pixel is on the end of a fixture, consider the neighboring fixtures loc = 'end' if pixel == 0: loc = 'start' logical_neighbors = self.get_colliding_fixtures(strand, address, loc) neighbors += [BufferUtils.logical_to_index(n) for n in logical_neighbors] self._pixel_neighbors_cache[index] = neighbors return neighbors
def get_pixel_location(self, index): """ Returns a given pixel's location in scene coordinates. """ loc = self._pixel_locations_cache.get(index, None) if loc is None: strand, address, pixel = BufferUtils.index_to_logical(index) f = self.fixture(strand, address) if pixel == 0: loc = f.pos1 elif pixel == (f.pixels - 1): loc = f.pos2 else: x1, y1 = f.pos1 x2, y2 = f.pos2 scale = old_div(float(pixel), f.pixels) relx, rely = ((x2 - x1) * scale, (y2 - y1) * scale) loc = (x1 + relx, y1 + rely) self._pixel_locations_cache[index] = loc return loc
def get_pixel_neighbors(self, index): """ Returns a list of pixel addresses that are adjacent to the given address. """ neighbors = self._pixel_neighbors_cache.get(index, None) if neighbors is None: neighbors = [] strand, address, pixel = BufferUtils.index_to_logical(index) f = self.fixture(strand, address) neighbors = [ BufferUtils.logical_to_index((strand, address, p)) for p in f.pixel_neighbors(pixel) ] if (pixel == 0) or (pixel == f.pixels - 1): # If this pixel is on the end of a fixture, consider the neighboring fixtures loc = 'end' if pixel == 0: loc = 'start' logical_neighbors = self.get_colliding_fixtures( strand, address, loc) neighbors += [ BufferUtils.logical_to_index(n) for n in logical_neighbors ] self._pixel_neighbors_cache[index] = neighbors return neighbors
def render(self, to_add, to_remove, population): pop_delta = 0 # Fade in if self.growing: p = (self.pattern._current_time - self.lifetime) / self.pattern.parameter('growth-time').get() if (p > 1): p = 1.0 color = self.pattern._growth_fader.get_color( p * self.pattern._fader_steps) if p >= 1.0: self.growing = False self.alive = True self.lifetime = self.pattern._current_time self.pattern.setPixelHLS(self.pattern._buffer, self.loc, color) # Alive - can move or die if not self.alive: return pop_delta for times in range(int(self.growth)): s, f, p = BufferUtils.index_to_logical(self.loc) self.pattern.setPixelHLS(self.pattern._buffer, self.loc, (0, 0, 0)) if random.random() < self.growth: self.growth -= 1 if ((self.dir == -1 and p == 0) or (self.dir == 1 and p == (self.pattern.scene().fixture(s, f).pixels - 1))): # At a vertex: kill dragons that reach the end of a fixture # and optionally spawn new dragons self.pattern._tails.append( (self.loc, self.pattern._current_time, self.pattern._tail_fader)) to_remove.add(self) pop_delta -= 1 spawned = self._spawn(s, f, population + pop_delta) to_add |= spawned pop_delta += len(spawned) break else: # Move dragons along the fixture self.pattern._tails.append( (self.loc, self.pattern._current_time, self.pattern._tail_fader)) new_address = BufferUtils.logical_to_index( (s, f, p + self.dir)) self.loc = new_address self.pattern.setPixelHLS(self.pattern._buffer, new_address, self.pattern._alive_color) # Kill dragons that run into each other if self not in to_remove: others = (self.pattern._dragons | to_add) - to_remove colliding = [ d for d in others if d != self and d.loc == self.loc ] if len(colliding) > 0: #print "collision between", self, "and", colliding[0] to_remove.add(self) pop_delta -= 1 for other in colliding: to_remove.add(other) pop_delta -= 1 self.pattern._tails.append( (self.loc, self.pattern._current_time, self.pattern._explode_fader)) neighbors = self.pattern.scene().get_pixel_neighbors( self.loc) for neighbor in neighbors: self.pattern._tails.append( (neighbor, self.pattern._current_time, self.pattern._explode_fader)) break return pop_delta
def draw(self, dt): self._current_time += dt # Spontaneous birth: Rare after startup if (len(self._dragons) < self.parameter('pop-limit').get() ) and random.random() < self.parameter('birth-rate').get(): address = BufferUtils.logical_to_index( (random.randint(0, self._max_strand - 1), random.randint(0, self._max_fixture - 1), 0)) if address not in [d.loc for d in self._dragons]: self._dragons.append( self.Dragon(address, 1, self._current_time)) growth_rate = self.parameter('growth-rate').get() # Dragon life cycle for dragon in self._dragons: # Fade in if dragon.growing: p = (self._current_time - dragon.lifetime) / self.parameter('growth-time').get() if (p > 1): p = 1.0 color = self._growth_fader.get_color(p * self._fader_steps) if p >= 1.0: dragon.growing = False dragon.alive = True dragon.lifetime = self._current_time self.setPixelHLS(dragon.loc, color) # Alive - can move or die if dragon.alive: dragon.growth += dt * growth_rate for times in range(int(dragon.growth)): s, f, p = BufferUtils.index_to_logical(dragon.loc) self.setPixelHLS(dragon.loc, (0, 0, 0)) if random.random() < dragon.growth: dragon.growth -= 1 # At a vertex: optionally spawn new dragons if dragon.moving and (p == 0 or p == ( self.scene().fixture(s, f).pixels - 1)): neighbors = self.scene().get_pixel_neighbors( dragon.loc) neighbors = [ BufferUtils.index_to_logical(n) for n in neighbors ] random.shuffle(neighbors) # Kill dragons that reach the end of a fixture dragon.moving = False if dragon in self._dragons: self._dragons.remove(dragon) # Iterate over candidate pixels that aren't on the current fixture num_children = 0 for candidate in [ n for n in neighbors if n[1] != f ]: child_index = BufferUtils.logical_to_index( candidate) if num_children == 0: # Spawn at least one new dragon to replace the old one. This first one skips the growth. dir = 1 if candidate[2] == 0 else -1 child = self.Dragon( child_index, dir, self._current_time) child.growing = False child.alive = True child.moving = False self._dragons.append(child) num_children += 1 elif (len(self._dragons) < self.parameter('pop-limit').get()): # Randomly spawn new dragons if random.random() < self.parameter( 'birth-rate').get(): dir = 1 if candidate[2] == 0 else -1 child = self.Dragon( child_index, dir, self._current_time) child.moving = False self._dragons.append(child) num_children += 1 break else: # Move dragons along the fixture self._tails.append((dragon.loc, self._current_time, self._tail_fader)) new_address = BufferUtils.logical_to_index( (s, f, p + dragon.dir)) dragon.loc = new_address dragon.moving = True self.setPixelHLS(new_address, self._alive_color) # Kill dragons that run into each other if dragon in self._dragons: colliding = [ d for d in self._dragons if d != dragon and d.loc == dragon.loc ] if len(colliding) > 0: #print "collision between", dragon, "and", colliding[0] self._dragons.remove(dragon) self._dragons.remove(colliding[0]) self._tails.append((dragon.loc, self._current_time, self._explode_fader)) neighbors = self.scene().get_pixel_neighbors( dragon.loc) for neighbor in neighbors: self._tails.append( (neighbor, self._current_time, self._explode_fader)) break # Draw tails for loc, time, fader in self._tails: if (self._current_time - time) > self.parameter('tail-persist').get(): if (loc, time, fader) in self._tails: self._tails.remove((loc, time, fader)) self.setPixelHLS(loc, (0, 0, 0)) else: progress = (self._current_time - time) / self.parameter('tail-persist').get() self.setPixelHLS(loc, fader.get_color(progress * self._fader_steps))
def draw(self, dt): self._current_time += dt # Spontaneous birth: Rare after startup if (len(self._dragons) < self.parameter('pop-limit').get()) and random.random() < self.parameter('birth-rate').get(): strand = random.randint(0, BufferUtils.num_strands - 1) fixture = random.randint(0, BufferUtils.strand_num_fixtures(strand) - 1) address = BufferUtils.logical_to_index((strand, fixture, 0)) if address not in [d.loc for d in self._dragons]: self._dragons.append(self.Dragon(address, 1, self._current_time)) growth_rate = self.parameter('growth-rate').get() # Dragon life cycle for dragon in self._dragons: # Fade in if dragon.growing: p = (self._current_time - dragon.lifetime) / self.parameter('growth-time').get() if (p > 1): p = 1.0 color = self._growth_fader.get_color(p * self._fader_steps) if p >= 1.0: dragon.growing = False dragon.alive = True dragon.lifetime = self._current_time self.setPixelHLS(dragon.loc, color) # Alive - can move or die if dragon.alive: dragon.growth += dt * growth_rate for times in range(int(dragon.growth)): s, f, p = BufferUtils.index_to_logical(dragon.loc) self.setPixelHLS(dragon.loc, (0, 0, 0)) if random.random() < dragon.growth: dragon.growth -= 1 # At a vertex: optionally spawn new dragons if dragon.moving and (p == 0 or p == (self.scene().fixture(s, f).pixels - 1)): neighbors = self.scene().get_pixel_neighbors(dragon.loc) neighbors = [BufferUtils.index_to_logical(n) for n in neighbors] random.shuffle(neighbors) # Kill dragons that reach the end of a fixture dragon.moving = False if dragon in self._dragons: self._dragons.remove(dragon) # Iterate over candidate pixels that aren't on the current fixture num_children = 0 for candidate in [n for n in neighbors if n[1] != f]: child_index = BufferUtils.logical_to_index(candidate) if num_children == 0: # Spawn at least one new dragon to replace the old one. This first one skips the growth. dir = 1 if candidate[2] == 0 else -1 child = self.Dragon(child_index, dir, self._current_time) child.growing = False child.alive = True child.moving = False self._dragons.append(child) num_children += 1 elif (len(self._dragons) < self.parameter('pop-limit').get()): # Randomly spawn new dragons if random.random() < self.parameter('birth-rate').get(): dir = 1 if candidate[2] == 0 else -1 child = self.Dragon(child_index, dir, self._current_time) child.moving = False self._dragons.append(child) num_children += 1 break; else: # Move dragons along the fixture self._tails.append((dragon.loc, self._current_time, self._tail_fader)) new_address = BufferUtils.logical_to_index((s, f, p + dragon.dir)) dragon.loc = new_address dragon.moving = True self.setPixelHLS(new_address, self._alive_color) # Kill dragons that run into each other if dragon in self._dragons: colliding = [d for d in self._dragons if d != dragon and d.loc == dragon.loc] if len(colliding) > 0: #print "collision between", dragon, "and", colliding[0] self._dragons.remove(dragon) self._dragons.remove(colliding[0]) self._tails.append((dragon.loc, self._current_time, self._explode_fader)) neighbors = self.scene().get_pixel_neighbors(dragon.loc) for neighbor in neighbors: self._tails.append((neighbor, self._current_time, self._explode_fader)) break # Draw tails for loc, time, fader in self._tails: if (self._current_time - time) > self.parameter('tail-persist').get(): if (loc, time, fader) in self._tails: self._tails.remove((loc, time, fader)) self.setPixelHLS(loc, (0, 0, 0)) else: progress = (self._current_time - time) / self.parameter('tail-persist').get() self.setPixelHLS(loc, fader.get_color(progress * self._fader_steps))