from __future__ import division import color as C import functools import constants import random import math class Fish(object): """Object displayed on the bridge, probably a fish. Must override color (or color_at), drag, width, height, tailThreshold, tailDuration, tailAccelMag """ is_diamond = True def __init__(self, x=None, y=None): """Create fish with default parameters.""" if x is not None: self.x = x elif not hasattr(self, 'x'): self.x = 0 if x is not None: self.y = y elif not hasattr(self, 'y'): self.y = 0.5 self.speed = C.Vector(0, 0) self.tailAcceleration = C.Vector(self.tailAccelMag, 0) self.tailThresholdCurrent = self.tailThreshold self.tailMoving = False self.tailMoveCount = 0 if not hasattr(self, 'theta_scale'): self.theta_scale = 12 @staticmethod def by_type(fish_type): if (fish_type == 'jellyfish'): return Jellyfish() if fish_type == 'dolphin': return Dolphin() if fish_type == 'boat': return Boat() if fish_type == 'dory': return Dory() if fish_type == 'nemo': return Nemo() if fish_type == 'whale': return Whale() if fish_type == 'stingray': return Stingray() if fish_type == 'eel': return Eel() if fish_type == 'shark': return Shark() def update(self): """Swim gently to the other side of the bridge.""" if self.x > constants.BRIDGE_WIDTH or self.x < -self.width: return False self.speed += self.drag * self.speed if abs(self.speed) < self.tailThresholdCurrent: self.tailMoving = True self.tailThresholdCurrent = max(rand_in_range( self.tailThreshold - 0.1, self.tailThreshold + 0.1 ), 0.0001) self.fix_direction() if self.tailMoveCount > self.tailDuration: self.tailMoveCount = 0 self.tailMoving = False if self.tailMoving: self.speed += self.tailAcceleration self.tailMoveCount += 1 self.x += self.speed.components[0] self.y += self.speed.components[1] return True def fix_direction(self): min_theta = -0.32 max_theta = 0.32 if self.y + self.height < 0: min_theta = 0 if self.y > 2: max_theta = 0 #theta = rand_in_range(min_theta, max_theta) theta = random.gauss( (max_theta+min_theta)/2, (max_theta - min_theta)/self.theta_scale ) self.tailAcceleration = C.Vector(self.tailAccelMag, 0).rot2d(theta) def color_at(self, x, y): """May be overridden to provide multicolored fish.""" return self.color def alpha_at(self, x, y): """Return an additional scale amount for clear parts. This is overridden to have oddly shaped fish, such as the eel.""" return 1 class Jellyfish(Fish): color = C.HSV(298/360, 89/100, 53/100).to_RGB() drag = -0.1 width = 15 height = 1.5 tailThreshold = 0.2 tailDuration = 6 tailAccelMag = 0.25 class Dolphin(Fish): color = C.HSV(18/360, 0/100, 10/100).to_RGB() drag = -0.09 width = 35 height = 1.5 tailThreshold = 0.7 tailDuration = 2 tailAccelMag = 0.4 gravity = C.Vector(0, -0.01) direction_fixed = False def update(self): """Swim gently to the other side of the bridge.""" if self.x > constants.BRIDGE_WIDTH or self.x < -self.width: return False if not self.direction_fixed: self.tailAcceleration = self.tailAcceleration.rot2d(math.pi / 32) self.direction_fixed = True self.speed += self.drag * self.speed + self.gravity if self.y <= 0.25: self.tailMoving = True if self.tailMoveCount > self.tailDuration: self.tailMoveCount = 0 self.tailMoving = False if self.tailMoving: self.speed += self.tailAcceleration self.tailMoveCount += 1 self.x += self.speed.components[0] self.y += self.speed.components[1] return True class Boat(Fish): color = C.HSV(24/360, 45/100, 44/100).to_RGB() drag = 0 width = 23 height = 1 tailDuration = 1 tailAccelMag = 0.25 tailThreshold = tailAccelMag y = 1 def fix_direction(self): pass class Dory(Fish): # Need to try this on the bridge color = C.HSV(235/360, 100/100, 100/100).to_RGB() drag = -0.1 width = 23 height = 1.5 tailThreshold = 0.7 tailDuration = 2 tailAccelMag = 0.4 class Nemo(Fish): color = C.HSV(40/360, 100/100, 10/100).to_RGB() drag = -0.09 width = 23 height = 1.5 tailThreshold = 0.7 tailDuration = 2 tailAccelMag = 0.4 class Whale(Fish): drag = -0.2 width = 80 height = 2 tailThreshold = 0.7 tailDuration = 3 tailAccelMag = 0.4 theta_scale = 20 y = 0 white_x_min = 58 white_x_width = 6 white_y_min = 1.45 white_y_height = 0.5 def color_at(self, x, y): """Mostly copied from compute_coverage. The point here is to render that white part of the Orca. """ white_x_min = self.white_x_min + self.x white_x_max = white_x_min + self.white_x_width white_y_min = self.white_y_min + self.y white_y_max = white_y_min + self.white_y_height min_x = min(max(x, white_x_min), x + 1) max_x = max(min(x + 1, white_x_max), x) min_y = min(max(y, white_y_min), y + 1) max_y = max(min(y + 1, white_y_max), y) coverage = (max_x - min_x) * (max_y - min_y) return C.white * coverage class Stingray(Fish): color = C.HSV(0/360, 0/100, 10/100).to_RGB() drag = -0.09 width = 15 height = 2 tailThreshold = 0.7 tailDuration = 2 tailAccelMag = 0.2 class Eel(Fish): color = C.HSV(109/360, 83/100, 0/100).to_RGB() drag = -0.09 width = 30 height = 2 tailThreshold = 0.7 tailDuration = 2 tailAccelMag = 0.2 front_offset = 0 y = 0 is_diamond = False def alpha_at(self, x, y): rel_x = x - self.x eel_height = math.sin( 2 * math.pi * (rel_x + self.front_offset) / self.width ) / 2 + 0.5 y_blend = abs(y - eel_height) if y_blend > 0.75: return 0 if y_blend < 0.25: return 1 return 1 - y_blend def update(self): self.front_offset += abs(self.speed) if self.front_offset > self.width: self.front_offset = 0 return super(Eel, self).update() def fix_direction(self): pass class Shark(Fish): color = C.HSV(0/360, 0/100, 10/100).to_RGB() drag = -0.2 width = 50 height = 2 tailThreshold = 0.7 tailDuration = 3 tailAccelMag = 0.4 theta_scale = 20 def rand_in_range(low, high): return random.random() * (high - low) + low