def render_command_list(self, list, buffer): """ Renders the output of a command list to the output buffer. Commands are rendered in FIFO overlap style. Run the list through filter_and_sort_commands() beforehand. If the output buffer is not zero (black) at a command's target, the output will be additively blended according to the blend_state (0.0 = 100% original, 1.0 = 100% new) """ for command in list: color = command.get_color() if isinstance(command, SetAll): buffer[:, :] = color elif isinstance(command, SetStrand): strand = command.get_strand() start, end = BufferUtils.get_strand_extents(strand) buffer[start:end] = color elif isinstance(command, SetFixture): strand = command.get_strand() fixture = command.get_address() start = BufferUtils.logical_to_index((strand, fixture, 0)) end = start + self._scene.fixture(strand, fixture).pixels buffer[start:end] = color elif isinstance(command, SetPixel): strand = command.get_strand() fixture = command.get_address() offset = command.get_pixel() pixel = BufferUtils.logical_to_index((strand, fixture, offset)) buffer[pixel] = color
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_command_list(self, list, buffer): """ Renders the output of a command list to the output buffer. Commands are rendered in FIFO overlap style. Run the list through filter_and_sort_commands() beforehand. If the output buffer is not zero (black) at a command's target, the output will be additively blended according to the blend_state (0.0 = 100% original, 1.0 = 100% new) """ for command in list: color = command.get_color() if isinstance(command, SetAll): buffer[:,:] = color elif isinstance(command, SetStrand): strand = command.get_strand() start, end = BufferUtils.get_strand_extents(strand) buffer[start:end] = color elif isinstance(command, SetFixture): strand = command.get_strand() fixture = command.get_address() start = BufferUtils.logical_to_index((strand, fixture, 0)) end = start + self._scene.fixture(strand, fixture).pixels buffer[start:end] = color elif isinstance(command, SetPixel): strand = command.get_strand() fixture = command.get_address() offset = command.get_pixel() pixel = BufferUtils.logical_to_index((strand, fixture, offset)) buffer[pixel] = color
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 _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 warmup(self): """ Warms up caches """ log.info("Warming up scene caches...") fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: self.get_colliding_fixtures(strand, fixture) for pixel in range(self.fixture(strand, fixture).pixels): index = BufferUtils.logical_to_index((strand, fixture, pixel)) neighbors = self.get_pixel_neighbors(index) self.get_pixel_location(index) for neighbor in neighbors: self.get_pixel_distance(index, neighbor) self.get_fixture_bounding_box() self.get_intersection_points() self.get_all_pixels_logical() self._tree = spatial.KDTree(self.get_all_pixel_locations()) locations = self.get_all_pixel_locations() self.pixelDistances = np.empty([len(locations), len(locations)]) for pixel in range(len(locations)): cx, cy = locations[pixel] x,y = (locations - (cx, cy)).T pixel_distances = np.sqrt(np.square(x) + np.square(y)) self.pixelDistances[pixel] = pixel_distances log.info("Done")
def warmup(self): """ Warms up caches """ log.info("Warming up scene caches...") fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: self.get_colliding_fixtures(strand, fixture) for pixel in range(self.fixture(strand, fixture).pixels): index = BufferUtils.logical_to_index( (strand, fixture, pixel)) neighbors = self.get_pixel_neighbors(index) self.get_pixel_location(index) for neighbor in neighbors: self.get_pixel_distance(index, neighbor) self.get_fixture_bounding_box() self.get_intersection_points() self.get_all_pixels_logical() self._tree = spatial.KDTree(self.get_all_pixel_locations()) locations = self.get_all_pixel_locations() self.pixelDistances = np.empty([len(locations), len(locations)]) for pixel in range(len(locations)): cx, cy = locations[pixel] x, y = (locations - (cx, cy)).T pixel_distances = np.sqrt(np.square(x) + np.square(y)) self.pixelDistances[pixel] = pixel_distances log.info("Done")
def get_fixture_bounding_box(self): """ Returns the bounding box containing all fixtures in the scene Return value is a tuple of (xmin, ymin, xmax, ymax) """ xmin = 999999 xmax = -999999 ymin = 999999 ymax = -999999 fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: for pixel in range(self.fixture(strand, fixture).pixels): x, y = self.get_pixel_location( BufferUtils.logical_to_index((strand, fixture, pixel))) if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y return (xmin, ymin, xmax, ymax)
def get_fixture_bounding_box(self): """ Returns the bounding box containing all fixtures in the scene Return value is a tuple of (xmin, ymin, xmax, ymax) """ xmin = 999999 xmax = -999999 ymin = 999999 ymax = -999999 fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: for pixel in range(self.fixture(strand, fixture).pixels): x, y = self.get_pixel_location(BufferUtils.logical_to_index((strand, fixture, pixel))) if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y return (xmin, ymin, xmax, ymax)
def get_all_pixels(self): """ Returns a list of all pixels in buffer address format (strand, offset) """ if self._all_pixels_raw is None: all_pixels = [] for s, a, p in self.get_all_pixels_logical(): #pxs.append(BufferUtils.get_buffer_address((s, a, p), scene=self)) all_pixels.append(BufferUtils.logical_to_index((s, a, p), scene=self)) all_pixels = sorted(all_pixels) self._all_pixels_raw = all_pixels return self._all_pixels_raw
def get_all_pixels(self): """ Returns a list of all pixels in buffer address format (strand, offset) """ if self._all_pixels_raw is None: all_pixels = [] for s, a, p in self.get_all_pixels_logical(): #pxs.append(BufferUtils.get_buffer_address((s, a, p), scene=self)) all_pixels.append( BufferUtils.logical_to_index((s, a, p), scene=self)) all_pixels = sorted(all_pixels) self._all_pixels_raw = all_pixels return self._all_pixels_raw
def warmup(self): """ Warms up caches """ log.info("Warming up scene caches...") fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: self.get_colliding_fixtures(strand, fixture) for pixel in range(self.fixture(strand, fixture).pixels): index = BufferUtils.logical_to_index((strand, fixture, pixel)) neighbors = self.get_pixel_neighbors(index) self.get_pixel_location(index) for neighbor in neighbors: self.get_pixel_distance(index, neighbor) self.get_fixture_bounding_box() self.get_intersection_points() self.get_all_pixels_logical() #self.get_all_pixels() #self.get_all_pixel_locations() log.info("Done")
def render(self, out): # 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.add(_Dragon(self, address, 1, self._current_time)) # Dragon life cycle to_add = set() to_remove = set() population = len(self._dragons) for dragon in self._dragons: population += dragon.render(to_add, to_remove, population) self._dragons = (self._dragons | to_add) - to_remove # Draw tails tails_to_remove = [] for loc, time, fader in self._tails: if (self._current_time - time) > self.parameter('tail-persist').get(): if (loc, time, fader) in self._tails: tails_to_remove.append((loc, time, fader)) self.setPixelHLS(self._buffer, loc, (0, 0, 0)) else: progress = (self._current_time - time) / self.parameter('tail-persist').get() self.setPixelHLS(self._buffer, loc, fader.get_color(progress * self._fader_steps)) for tail in tails_to_remove: self._tails.remove(tail) np.copyto(out, self._buffer)
def warmup(self): """ Warms up caches """ log.info("Warming up scene caches...") fh = self.fixture_hierarchy() for strand in fh: for fixture in fh[strand]: self.get_colliding_fixtures(strand, fixture) for pixel in range(self.fixture(strand, fixture).pixels): index = BufferUtils.logical_to_index( (strand, fixture, pixel)) neighbors = self.get_pixel_neighbors(index) self.get_pixel_location(index) for neighbor in neighbors: self.get_pixel_distance(index, neighbor) self.get_fixture_bounding_box() self.get_intersection_points() self.get_all_pixels_logical() #self.get_all_pixels() #self.get_all_pixel_locations() log.info("Done")
def draw(self, dt): self._current_time += dt self._mass_destruction_countdown -= dt # Ensure that empty displays start up with some seeds p_birth = (1.0 - self._spontaneous_birth_probability) if self._population > 5 else 0.5 # Spontaneous birth: Rare after startup if (self._population < self._population_limit) and random.random() + self.parameter('audio-onset-birth-boost').get() > p_birth: strand = random.randint(0, BufferUtils.num_strands - 1) fixture = random.randint(0, BufferUtils.strand_num_fixtures(strand) - 1) pixel = random.randint(0, BufferUtils.fixture_length(strand, fixture) - 1) address = BufferUtils.logical_to_index((strand, fixture, pixel)) if address not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(address) self._time[address] = self._current_time self._population += 1 self._spread_boost *= self.parameter('audio-onset-spread-boost-echo').get() if self._mixer.is_onset(): self._spread_boost += self.parameter('audio-onset-spread-boost').get() # Color growth for address in self._growing: neighbors = self.scene().get_pixel_neighbors(address) p, color = self._get_next_color(address, self._growth_time, self._current_time) if p >= 1.0: self._growing.remove(address) self._alive.append(address) self._time[address] = self._current_time self.setPixelHLS(address, color) # Spread spread_rate = self._spread_rate + self._spread_boost if (self._population < self._population_limit) and (random.random() < spread_rate * dt): for spread in neighbors: if spread not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(spread) self._time[spread] = self._current_time self._population += 1 # Lifetime for address in self._alive: neighbors = self.scene().get_pixel_neighbors(address) live_neighbors = [i for i in neighbors if i in self._alive] lt = self._life_time if len(neighbors) < 2: lt = self._isolated_life_time if len(live_neighbors) < 3 and ((self._current_time - self._time[address]) / lt) >= 1.0: self._alive.remove(address) self._dying.append(address) self._time[address] = self._current_time self._population -= 1 self.setPixelHLS(address, self._alive_color) # Spread if (self._population < self._population_limit) and random.random() < self._birth_rate * dt: for spread in neighbors: if spread not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(spread) self._time[spread] = self._current_time self._population += 1 # Color decay for address in self._dying: p, color = self._get_next_color(address, self._death_time, self._current_time + self.parameter('audio-onset-death-boost').get()) if p >= 1.0: self._dying.remove(address) self._fading_out.append(address) self._time[address] = self._current_time self.setPixelHLS(address, color) # Fade out for address in self._fading_out: p, color = self._get_next_color(address, self._fade_out_time, self._current_time + self.parameter('audio-onset-death-boost').get()) if p >= 1.0: self._fading_out.remove(address) self.setPixelHLS(address, color) # Mass destruction if (self._population == self._population_limit) or \ (self._population > self._mass_destruction_threshold and self._mass_destruction_countdown <= 0): for i in self._alive: if random.random() > 0.95: self._alive.remove(i) self._dying.append(i) self._population -= 1 for i in self._growing: if random.random() > 0.85: self._growing.remove(i) self._dying.append(i) self._population -= 1 self._mass_destruction_countdown = self.parameter('mass-destruction-time').get()
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))
def draw(self, dt): self._current_time += dt self._mass_destruction_countdown -= dt # Ensure that empty displays start up with some seeds p_birth = (1.0 - self._spontaneous_birth_probability ) if self._population > 5 else 0.5 # Spontaneous birth: Rare after startup if (self._population < self._population_limit) and random.random( ) + self.parameter('audio-onset-birth-boost').get() > p_birth: strand = random.randint(0, BufferUtils.num_strands - 1) fixture = random.randint( 0, BufferUtils.strand_num_fixtures(strand) - 1) pixel = random.randint( 0, BufferUtils.fixture_length(strand, fixture) - 1) address = BufferUtils.logical_to_index((strand, fixture, pixel)) if address not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(address) self._time[address] = self._current_time self._population += 1 self._spread_boost *= self.parameter( 'audio-onset-spread-boost-echo').get() if self._mixer.is_onset(): self._spread_boost += self.parameter( 'audio-onset-spread-boost').get() # Color growth for address in self._growing: neighbors = self.scene().get_pixel_neighbors(address) p, color = self._get_next_color(address, self._growth_time, self._current_time) if p >= 1.0: self._growing.remove(address) self._alive.append(address) self._time[address] = self._current_time self.setPixelHLS(address, color) # Spread spread_rate = self._spread_rate + self._spread_boost if (self._population < self._population_limit) and ( random.random() < spread_rate * dt): for spread in neighbors: if spread not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(spread) self._time[spread] = self._current_time self._population += 1 # Lifetime for address in self._alive: neighbors = self.scene().get_pixel_neighbors(address) live_neighbors = [i for i in neighbors if i in self._alive] lt = self._life_time if len(neighbors) < 2: lt = self._isolated_life_time if len(live_neighbors) < 3 and ( (self._current_time - self._time[address]) / lt) >= 1.0: self._alive.remove(address) self._dying.append(address) self._time[address] = self._current_time self._population -= 1 self.setPixelHLS(address, self._alive_color) # Spread if (self._population < self._population_limit ) and random.random() < self._birth_rate * dt: for spread in neighbors: if spread not in (self._growing + self._alive + self._dying + self._fading_out): self._growing.append(spread) self._time[spread] = self._current_time self._population += 1 # Color decay for address in self._dying: p, color = self._get_next_color( address, self._death_time, self._current_time + self.parameter('audio-onset-death-boost').get()) if p >= 1.0: self._dying.remove(address) self._fading_out.append(address) self._time[address] = self._current_time self.setPixelHLS(address, color) # Fade out for address in self._fading_out: p, color = self._get_next_color( address, self._fade_out_time, self._current_time + self.parameter('audio-onset-death-boost').get()) if p >= 1.0: self._fading_out.remove(address) self.setPixelHLS(address, color) # Mass destruction if (self._population == self._population_limit) or \ (self._population > self._mass_destruction_threshold and self._mass_destruction_countdown <= 0): for i in self._alive: if random.random() > 0.95: self._alive.remove(i) self._dying.append(i) self._population -= 1 for i in self._growing: if random.random() > 0.85: self._growing.remove(i) self._dying.append(i) self._population -= 1 self._mass_destruction_countdown = self.parameter( 'mass-destruction-time').get()
def draw(self, dt): self._hue = (self._hue + (dt * 0.1)) % 1.0 self.setAllHLS(self._hue, 0.2, 1.0) for strand in self._hierarchy: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 0), scene=self.scene()), (0.33, 0.5, 1.0)) if (strand & 0x8): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 1), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 1), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x4): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 2), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 2), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x2): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 3), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 3), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x1): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 4), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 4), scene=self.scene()), (0.0, 0.2, 0.0)) for fixture in self._hierarchy[strand]: last_fixture_pixel = self._hierarchy[strand][fixture].pixels - 1 self.setPixelHLS(BufferUtils.logical_to_index((strand, fixture, last_fixture_pixel), scene=self.scene()), (0.66, 0.5, 1.0)) if fixture > 0: self.setPixelHLS(BufferUtils.logical_to_index((strand, fixture, 0), scene=self.scene()), (0.15, 0.5, 1.0)) last_fixture = len(self._hierarchy[strand].keys()) - 1 last_pixel = self._hierarchy[strand][last_fixture].pixels - 1 self.setPixelHLS(BufferUtils.logical_to_index((strand, last_fixture, last_pixel), scene=self.scene()), (0.0, 0.5, 1.0))
def draw(self, dt): self._hue = (self._hue + (dt * 0.1)) % 1.0 self.setAllHLS(self._hue, 0.2, 1.0) for strand in self._heirarchy: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 0), scene=self.scene()), (0.33, 0.5, 1.0)) if (strand & 0x8): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 1), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 1), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x4): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 2), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 2), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x2): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 3), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 3), scene=self.scene()), (0.0, 0.2, 0.0)) if (strand & 0x1): self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 4), scene=self.scene()), (0.66, 0.9, 1.0)) else: self.setPixelHLS(BufferUtils.logical_to_index((strand, 0, 4), scene=self.scene()), (0.0, 0.2, 0.0)) for fixture in self._heirarchy[strand]: last_fixture_pixel = self._heirarchy[strand][fixture].pixels - 1 self.setPixelHLS(BufferUtils.logical_to_index((strand, fixture, last_fixture_pixel), scene=self.scene()), (0.66, 0.5, 1.0)) if fixture > 0: self.setPixelHLS(BufferUtils.logical_to_index((strand, fixture, 0), scene=self.scene()), (0.15, 0.5, 1.0)) last_fixture = len(self._heirarchy[strand].keys()) - 1 last_pixel = self._heirarchy[strand][last_fixture].pixels - 1 self.setPixelHLS(BufferUtils.logical_to_index((strand, last_fixture, last_pixel), scene=self.scene()), (0.0, 0.5, 1.0))
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))