class File: def __init__(self, length, visualizer): self.visualizer = visualizer self.arriving_chunks = OrderedDict() self.gatherer = Gatherer() def add_chunk(self, chunk): chunk.boid = Boid(self.get_departure_position(chunk), 10.0, 3.0) chunk.target = None chunk.target_type = None chunk.target_position = None chunk.target_position = self.get_target_position(chunk) chunk.boid.arrive(chunk.target_position) chunk.arrived = False self.arriving_chunks[chunk.id] = chunk def stopped_playing(self, chunk_id): chunk = self.arriving_chunks[chunk_id] self.gather_chunk(chunk) def gather_chunk(self, chunk): del self.arriving_chunks[chunk.id] # TODO: verify that these positions are really OK chunk.begin_position = Vector(chunk.boid.loc.x, chunk.boid.loc.y) chunk.end_position = Vector(chunk.boid.loc.x, chunk.boid.loc.y) self.gatherer.add(chunk) self.reorient_chunks_following(chunk) def get_departure_position(self, chunk): if chunk.pan < 0.5: x = 0 else: x = self.visualizer.width y = chunk.height * self.visualizer.height return Vector(x, y) def get_target_position(self, chunk): if not chunk.target_position: self.find_target(chunk) return chunk.target_position def find_target(self, chunk): position = self.find_joinable_piece(chunk) if position: chunk.target_type = TargetType.PIECE chunk.target_position = position return other_chunk = self.find_other_huntable_chunk(chunk) if other_chunk: chunk.target_type = TargetType.CHUNK chunk.target = other_chunk chunk.target_position = other_chunk.boid.loc return if len(self.gatherer.pieces()) > 0: chunk.target_position = self.close_to_existing_piece() return chunk.target_type = TargetType.NEW_PIECE chunk.target_position = self.anywhere() def anywhere(self): return Vector(random.uniform(self.visualizer.width * INNER_MARGIN, self.visualizer.width * (1 - INNER_MARGIN*2)), random.uniform(self.visualizer.height * INNER_MARGIN, self.visualizer.height * (1 - INNER_MARGIN*2))) def close_to_existing_piece(self): piece = random.choice(self.gatherer.pieces()) position = random.choice([piece.begin_position, piece.end_position]) angle = random.uniform(0, 2 * math.pi) distance = 1.0 movement = Vector(distance * math.cos(angle), distance * math.sin(angle)) return position + movement def find_joinable_piece(self, chunk): appendable_piece_key = self.gatherer.find_appendable_piece(chunk) prependable_piece_key = self.gatherer.find_prependable_piece(chunk) if appendable_piece_key and prependable_piece_key: appendable_position = self.gatherer.piece(appendable_piece_key).begin_position prepandable_position = self.gatherer.piece(prependable_piece_key).end_position chunk.target_position = (appendable_position + prepandable_position) / 2 elif appendable_piece_key: return self.gatherer.piece(appendable_piece_key).begin_position elif prependable_piece_key: return self.gatherer.piece(prependable_piece_key).end_position def find_other_huntable_chunk(self, chunk): # optimization: iterate appendables in reverse order, or use some kind of cache for other in self.arriving_chunks.values(): if other.end == chunk.begin: return other if other.begin == chunk.end: return other def reorient_chunks_following(self, targeted_chunk): for chunk in self.arriving_chunks.values(): if chunk.target_type == TargetType.CHUNK and \ chunk.target == targeted_chunk: self.find_target(chunk) def update(self): self.update_pieces() self.update_arriving_chunks() def update_pieces(self): for piece in self.gatherer.pieces(): self.get_piece_force(piece) for piece in self.gatherer.pieces(): self.apply_piece_force(piece) def get_piece_force(self, piece): piece.desired_length = self.desired_piece_length(piece) piece.begin_force = Vector(0,0) piece.end_force = Vector(0,0) self.consider_desired_length(piece, piece.begin_force, piece.begin_position, piece.end_position) self.consider_desired_length(piece, piece.end_force, piece.end_position, piece.begin_position) piece.begin_force.limit(3.0) piece.end_force.limit(3.0) def desired_piece_length(self, piece): return piece.byte_size / 10 def consider_desired_length(self, piece, force, position, opposite_position): force += spring_force(position, opposite_position, piece.desired_length) def apply_piece_force(self, piece): piece.begin_position += piece.begin_force piece.end_position += piece.end_force def update_arriving_chunks(self): for chunk in self.arriving_chunks.values(): if not chunk.arrived: chunk.boid.update() if self.arrived(chunk): #self.visualizer.play_chunk(chunk) # TEMP chunk.arrived = True self.gather_chunk(chunk) # TEMP def arrived(self, chunk): if chunk.target_type in [TargetType.PIECE, TargetType.NEW_PIECE]: distance = (chunk.target_position - chunk.boid.loc).mag() return distance < 1.0 else: return False