from image import Image
from ray import Ray
from point import Point
from colour import Colour

class RenderEngine:
    '''
    Renders 3D objects into 2D objects using ray tracing
    '''
    
    def render(self,scene):
        width = scene.width
        height = scene.height
        aspect_ratio = float(width)/height
        x0,x1 = -1.0,1.0
        xstep = (x1 - x0)/(width-1) #how much we move in xcoordinate space at each step when calculating pixels - also called delta in maths
        y0,y1 = -1.0/aspect_ratio,1.0/aspect_ratio
        ystep = (y1-y0)/(height-1)
        
        camera = scene.camera
        pixels = Image(width,height)
        
        for j in range(height):
            y = y0 + j * ystep
            for i in range(width):
                x = x0 + i * xstep
                
                ray = Ray(camera,Point(x,y)-camera)
                pixels.set_pixel(i,j,self.ray_trace(ray,scene))
            
        return pixels
    
    def ray_trace(self,ray,scene):
        colour = Colour(0,0,0)
        
        #find the nearest object hit by the ray in the scene
        dist_hit,obj_hit = self.find_nearest(ray,scene)
        
        if obj_hit is None:
            return colour
        hit_pos = ray.origin + ray.direction * dist_hit
        colour += self.colour_at(obj_hit,hit_pos,scene) # we accumulate colour
        return colour
    
    def find_nearest(self,ray,scene):
        '''
        Goes object by object through each object hit by the ray, and finds the closest one to the camera
        '''
        
        dist_min = None
        obj_hit = None
        for obj in scene.objects:
            dist = obj.intersects(ray)
            if dist is not None and (obj_hit is None or dis < dist_min):
                dist_min = dist
                obj_hit = obj
        return dist_min,obj_hit
    
    def colour_at(self,obj_hit,hit_pos,scene):
        return obj_hit.material