Пример #1
0
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