def __init__(self,lookfrom,lookat,vup,vfov,aspect,aperture,focus_dist):
   self.lens_radius = aperture /2
   theta = vfov * math.pi/180
   half_height = math.tan(theta/2)
   half_width = aspect * half_height
   self.origin = lookfrom
   self.w = Vec3.unit_vector(lookfrom -lookat)
   self.u = Vec3.unit_vector(Vec3.cross(vup,self.w))
   self.v = Vec3.cross(self.w,self.u)
   self.lower_left_corner = self.origin - half_width*focus_dist*self.u -half_height*focus_dist*self.v -focus_dist*self.w
   self.horizontal = 2*half_width*focus_dist*self.u
   self.vertical = 2*half_height*focus_dist*self.v
 def __init__(self, lookfrom, lookat, vup, vfov, aspect, aperture,
              focus_dist, t0, t1):
     self.time0 = t0
     self.time1 = t1
     self.lens_radius = aperture / 2
     theta = vfov * math.pi / 180
     half_height = math.tan(theta / 2)
     half_width = aspect * half_height
     self.origin = lookfrom
     self.w = Vec3.unit_vector(lookfrom - lookat)
     self.u = Vec3.unit_vector(Vec3.cross(vup, self.w))
     self.v = Vec3.cross(self.w, self.u)
     self.lower_left_corner = self.origin - half_width * focus_dist * self.u - half_height * focus_dist * self.v - focus_dist * self.w
     self.horizontal = 2 * half_width * focus_dist * self.u
     self.vertical = 2 * half_height * focus_dist * self.v
 def scatter(self, r_in,rec):
   reflected = Vec3.reflect(Vec3.unit_vector(r_in.direction),rec["normal"])
   scattered = Ray(rec["p"],reflected + self.fuzz * Material.random_in_unit_sphere())
   attenuation = self.albedo
   if Vec3.dot(scattered.direction,rec["normal"]) > 0:
     return attenuation,scattered
   else:
     return None,None
def color(r,world):
  rec = world.hit(r,0.0,float('inf'))
  if rec is not None:
    target = rec["p"]+rec["normal"] + random_in_unit_sphere()
    return 0.5 * color(Ray(rec["p"],target-rec["p"]),world)
  else:
    unit_direction = Vec3.unit_vector(r.direction)
    t = 0.5*(unit_direction.y +1.0)
    return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
 def scatter(self, r_in, rec):
     reflected = Vec3.reflect(Vec3.unit_vector(r_in.direction),
                              rec["normal"])
     scattered = Ray(
         rec["p"], reflected + self.fuzz * Material.random_in_unit_sphere())
     attenuation = self.albedo
     if Vec3.dot(scattered.direction, rec["normal"]) > 0:
         return attenuation, scattered
     else:
         return None, None
def color(r,world,depth):
  rec = world.hit(r,0.001,float('inf'))
  if rec is not None:
    if depth >= 50:
      return Vec3(0,0,0)
    attenuation,scattered =rec["material"].scatter(r,rec)
    if attenuation is not None:
      return attenuation * color(scattered,world,depth +1)
    else:
      return Vec3(0,0,0)
  else:
    unit_direction = Vec3.unit_vector(r.direction)
    t = 0.5*(unit_direction.y +1.0)
    return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def color(r, world, depth):
    rec = world.hit(r, 0.001, float('inf'))
    if rec is not None:
        if depth >= 50:
            return Vec3(0, 0, 0)
        attenuation, scattered = rec["material"].scatter(r, rec)
        if attenuation is not None:
            return attenuation * color(scattered, world, depth + 1)
        else:
            return Vec3(0, 0, 0)
    else:
        unit_direction = Vec3.unit_vector(r.direction)
        t = 0.5 * (unit_direction.y + 1.0)
        return (1.0 - t) * Vec3(1.0, 1.0, 1.0) + t * Vec3(0.5, 0.7, 1.0)
def color(r):
  unit_direction = Vec3.unit_vector(r.direction)
  t = 0.5*(unit_direction.y +1.0)
  return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def color(r):
  if hit_sphere(Vec3(0,0,-1),0.5,r):
    return Vec3(1,0,0)
  unit_direction = Vec3.unit_vector(r.direction)
  t = 0.5*(unit_direction.y +1.0)
  return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)