<s> import math <EOL> EPSILON = <NUM_LIT> <EOL> INF = <NUM_LIT> <EOL> class Vector ( object ) : <EOL> def __init__ ( self , initx , inity , initz ) : <EOL> self . x = initx <EOL> self . y = inity <EOL> self . z = initz <EOL> def __str__ ( self ) : <EOL> return '<STR_LIT>' % ( self . x , self . y , self . z ) <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' % ( self . x , self . y , self . z ) <EOL> def magnitude ( self ) : <EOL> return math . sqrt ( self . dot ( self ) ) <EOL> def __add__ ( self , other ) : <EOL> return Vector ( self . x + other . x , self . y + other . y , self . z + other . z ) <EOL> def __sub__ ( self , other ) : <EOL> return Vector ( self . x - other . x , self . y - other . y , self . z - other . z ) <EOL> def scale ( self , factor ) : <EOL> return Vector ( factor * self . x , factor * self . y , factor * self . z ) <EOL> def dot ( self , other ) : <EOL> return ( self . x * other . x ) + ( self . y * other . y ) + ( self . z * other . z ) <EOL> def cross ( self , other ) : <EOL> return Vector ( self . y * other . z - self . z * other . y , <EOL> self . z * other . x - self . x * other . z , <EOL> self . x * other . y - self . y * other . x ) <EOL> def normalized ( self ) : <EOL> return self . scale ( <NUM_LIT:1.0> / self . magnitude ( ) ) <EOL> def negated ( self ) : <EOL> return self . scale ( - <NUM_LIT:1> ) <EOL> def __eq__ ( self , other ) : <EOL> return ( self . x == other . x ) and ( self . y == other . y ) and ( self . z == other . z ) <EOL> def isVector ( self ) : <EOL> return True <EOL> def isPoint ( self ) : <EOL> return False <EOL> def reflectThrough ( self , normal ) : <EOL> d = normal . scale ( self . dot ( normal ) ) <EOL> return self - d . scale ( <NUM_LIT:2> ) <EOL> VZERO = Vector ( <NUM_LIT:0> , <NUM_LIT:0> , <NUM_LIT:0> ) <EOL> VRIGHT = Vector ( <NUM_LIT:1> , <NUM_LIT:0> , <NUM_LIT:0> ) <EOL> VUP = Vector ( <NUM_LIT:0> , <NUM_LIT:1> , <NUM_LIT:0> ) <EOL> VOUT = Vector ( <NUM_LIT:0> , <NUM_LIT:0> , <NUM_LIT:1> ) <EOL> if not ( VRIGHT . reflectThrough ( VUP ) == VRIGHT ) : <EOL> print <NUM_LIT:1> / <NUM_LIT:0> <EOL> if not ( Vector ( - <NUM_LIT:1> , - <NUM_LIT:1> , <NUM_LIT:0> ) . reflectThrough ( VUP ) == Vector ( - <NUM_LIT:1> , <NUM_LIT:1> , <NUM_LIT:0> ) ) : <EOL> print <NUM_LIT:1> / <NUM_LIT:0> <EOL> class Point ( object ) : <EOL> def __init__ ( self , initx , inity , initz ) : <EOL> self . x = initx <EOL> self . y = inity <EOL> self . z = initz <EOL> def __str__ ( self ) : <EOL> return '<STR_LIT>' % ( self . x , self . y , self . z ) <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' % ( self . x , self . y , self . z ) <EOL> def __add__ ( self , other ) : <EOL> return Point ( self . x + other . x , self . y + other . y , self . z + other . z ) <EOL> def __sub__ ( self , other ) : <EOL> return Vector ( self . x - other . x , self . y - other . y , self . z - other . z ) <EOL> def isVector ( self ) : <EOL> return False <EOL> def isPoint ( self ) : <EOL> return True <EOL> class Sphere ( object ) : <EOL> def __init__ ( self , centre , radius ) : <EOL> self . centre = centre <EOL> self . radius = radius <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' % ( repr ( self . centre ) , self . radius ) <EOL> def intersectionTime ( self , ray ) : <EOL> cp = self . centre - ray . point <EOL> v = cp . dot ( ray . vector ) <EOL> discriminant = ( self . radius * self . radius ) - ( cp . dot ( cp ) - v * v ) <EOL> if discriminant < <NUM_LIT:0> : <EOL> return INF + <NUM_LIT:1> <EOL> else : <EOL> return v - math . sqrt ( discriminant ) <EOL> def normalAt ( self , p ) : <EOL> return ( p - self . centre ) . normalized ( ) <EOL> class Halfspace ( object ) : <EOL> def __init__ ( self , point , normal ) : <EOL> self . point = point <EOL> self . normal = normal . normalized ( ) <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' % ( repr ( self . point ) , repr ( self . normal ) ) <EOL> def intersectionTime ( self , ray ) : <EOL> v = ray . vector . dot ( self . normal ) <EOL> if v : <EOL> return <NUM_LIT:1> / - v <EOL> else : <EOL> return INF + <NUM_LIT:1> <EOL> def normalAt ( self , p ) : <EOL> return self . normal <EOL> class Ray ( object ) : <EOL> def __init__ ( self , point , vector ) : <EOL> self . point = point <EOL> self . vector = vector . normalized ( ) <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' % ( repr ( self . point ) , repr ( self . vector ) ) <EOL> def pointAtTime ( self , t ) : <EOL> return self . point + self . vector . scale ( t ) <EOL> PZERO = Point ( <NUM_LIT:0> , <NUM_LIT:0> , <NUM_LIT:0> ) <EOL> a = Vector ( <NUM_LIT:3> , <NUM_LIT:4> , <NUM_LIT:12> ) <EOL> b = Vector ( <NUM_LIT:1> , <NUM_LIT:1> , <NUM_LIT:1> ) <EOL> class PpmCanvas ( object ) : <EOL> def __init__ ( self , width , height , filenameBase ) : <EOL> self . bytes = [ <NUM_LIT:0> ] * ( width * height * <NUM_LIT:3> ) <EOL> for i in range ( width * height ) : <EOL> self . bytes [ i * <NUM_LIT:3> + <NUM_LIT:2> ] = <NUM_LIT:255> <EOL> self . width = width <EOL> self . height = height <EOL> self . filenameBase = filenameBase <EOL> def plot ( self , x , y , r , g , b ) : <EOL> i = ( ( self . height - y - <NUM_LIT:1> ) * self . width + x ) * <NUM_LIT:3> <EOL> self . bytes [ i ] = max ( <NUM_LIT:0> , min ( <NUM_LIT:255> , int ( r * <NUM_LIT:255> ) ) ) <EOL> self . bytes [ i + <NUM_LIT:1> ] = max ( <NUM_LIT:0> , min ( <NUM_LIT:255> , int ( g * <NUM_LIT:255> ) ) ) <EOL> self . bytes [ i + <NUM_LIT:2> ] = max ( <NUM_LIT:0> , min ( <NUM_LIT:255> , int ( b * <NUM_LIT:255> ) ) ) <EOL> def save ( self ) : <EOL> with open ( self . filenameBase + '<STR_LIT>' , '<STR_LIT:wb>' ) as f : <EOL> f . write ( '<STR_LIT>' % ( self . width , self . height ) ) <EOL> l = [ ] <EOL> for c in self . bytes : <EOL> l . append ( chr ( c ) ) <EOL> f . write ( '<STR_LIT>' . join ( l ) ) <EOL> def firstIntersection ( intersections ) : <EOL> result = intersections [ <NUM_LIT:0> ] [ <NUM_LIT:0> ] , INF + <NUM_LIT:1> , intersections [ <NUM_LIT:0> ] [ <NUM_LIT:2> ] <EOL> for i in intersections : <EOL> candidateT = i [ <NUM_LIT:1> ] <EOL> if candidateT < INF and candidateT > - EPSILON : <EOL> if result [ <NUM_LIT:1> ] > INF or candidateT < result [ <NUM_LIT:1> ] : <EOL> result = i <EOL> return result <EOL> class Scene ( object ) : <EOL> def __init__ ( self ) : <EOL> self . objects = [ ] <EOL> self . lightPoints = [ ] <EOL> self . position = Point ( <NUM_LIT:0> , <NUM_LIT> , <NUM_LIT:10> ) <EOL> self . lookingAt = PZERO <EOL> self . fieldOfView = <NUM_LIT> <EOL> self . recursionDepth = <NUM_LIT:0> <EOL> def lookAt ( self , p ) : <EOL> self . lookingAt = p <EOL> def addObject ( self , on , oi , sc ) : <EOL> self . objects . append ( ( on , oi , sc ) ) <EOL> def addLight ( self , p ) : <EOL> self . lightPoints . append ( p ) <EOL> def render ( self , canvas ) : <EOL> fovRadians = math . pi * ( self . fieldOfView / <NUM_LIT> ) / <NUM_LIT> <EOL> halfWidth = math . tan ( fovRadians ) <EOL> halfHeight = <NUM_LIT> * halfWidth <EOL> width = halfWidth * <NUM_LIT:2> <EOL> height = halfHeight * <NUM_LIT:2> <EOL> pixelWidth = width / ( canvas . width - <NUM_LIT:1> ) <EOL> pixelHeight = height / ( canvas . height - <NUM_LIT:1> ) <EOL> eye = Ray ( self . position , self . lookingAt - self . position ) <EOL> vpRight = eye . vector . cross ( VUP ) . normalized ( ) <EOL> vpUp = vpRight . cross ( eye . vector ) . normalized ( ) <EOL> previousfraction = <NUM_LIT:0.0> <EOL> for y in range ( canvas . height ) : <EOL> currentfraction = <NUM_LIT:1.0> * y / canvas . height <EOL> if currentfraction - previousfraction > <NUM_LIT> : <EOL> print '<STR_LIT>' % int ( currentfraction * <NUM_LIT:100> ) <EOL> previousfraction = currentfraction <EOL> for x in range ( canvas . width ) : <EOL> xcomp = vpRight . scale ( x * pixelWidth - halfWidth ) <EOL> ycomp = vpUp . scale ( y * pixelHeight - halfHeight ) <EOL> ray = Ray ( eye . point , eye . vector + xcomp + ycomp ) <EOL> colour = self . rayColour ( ray ) <EOL> canvas . plot ( x , y , colour [ <NUM_LIT:0> ] , colour [ <NUM_LIT:1> ] , colour [ <NUM_LIT:2> ] ) <EOL> print '<STR_LIT>' <EOL> def rayColour ( self , ray ) : <EOL> if self . recursionDepth > <NUM_LIT:3> : <EOL> return ( <NUM_LIT:0.0> , <NUM_LIT:0.0> , <NUM_LIT:0.0> ) <EOL> self . recursionDepth = self . recursionDepth + <NUM_LIT:1> <EOL> intersections = [ ] <EOL> for on , oi , sc in self . objects : <EOL> intersections . append ( ( on , oi ( ray ) , sc ) ) <EOL> i = firstIntersection ( intersections ) <EOL> if i [ <NUM_LIT:1> ] > INF : <EOL> self . recursionDepth = self . recursionDepth - <NUM_LIT:1> <EOL> return ( <NUM_LIT:0.0> , <NUM_LIT:0.0> , <NUM_LIT:0.0> ) <EOL> else : <EOL> ( o , t , s ) = i <EOL> p = ray . pointAtTime ( t ) <EOL> r = s ( self , ray , p , o ( p ) ) <EOL> self . recursionDepth = self . recursionDepth - <NUM_LIT:1> <EOL> return r <EOL> def _lightIsVisible ( self , l , p ) : <EOL> for ( on , oi , sc ) in self . objects : <EOL> t = oi ( Ray ( p , l - p ) ) <EOL> if t < INF and t > EPSILON : <EOL> return False <EOL> return True <EOL> def visibleLights ( self , p ) : <EOL> result = [ ] <EOL> for l in self . lightPoints : <EOL> if self . _lightIsVisible ( l , p ) : <EOL> result . append ( l ) <EOL> return result <EOL> def addColours ( a , scale , b ) : <EOL> return ( a [ <NUM_LIT:0> ] + scale * b [ <NUM_LIT:0> ] , <EOL> a [ <NUM_LIT:1> ] + scale * b [ <NUM_LIT:1> ] , <EOL> a [ <NUM_LIT:2> ] + scale * b [ <NUM_LIT:2> ] ) <EOL> class SimpleSurface ( object ) : <EOL> def __init__ ( self , baseColour ) : <EOL> self . baseColour = baseColour <EOL> self . specularCoefficient = <NUM_LIT> <EOL> self . lambertCoefficient = <NUM_LIT> <EOL> self . ambientCoefficient = <NUM_LIT:1.0> - self . specularCoefficient - self . lambertCoefficient <EOL> def baseColourAt ( self , p ) : <EOL> return self . baseColour <EOL> def colourAt ( self , scene , ray , p , normal ) : <EOL> b = self . baseColourAt ( p ) <EOL> c = ( <NUM_LIT:0.0> , <NUM_LIT:0.0> , <NUM_LIT:0.0> ) <EOL> if self . specularCoefficient > <NUM_LIT:0> : <EOL> reflectedRay = Ray ( p , ray . vector . reflectThrough ( normal ) ) <EOL> reflectedColour = scene . rayColour ( reflectedRay ) <EOL> c = addColours ( c , self . specularCoefficient , reflectedColour ) <EOL> if self . lambertCoefficient > <NUM_LIT:0> : <EOL> lambertAmount = <NUM_LIT:0.0> <EOL> for lightPoint in scene . visibleLights ( p ) : <EOL> contribution = ( lightPoint - p ) . normalized ( ) . dot ( normal ) <EOL> if contribution > <NUM_LIT:0> : <EOL> lambertAmount = lambertAmount + contribution <EOL> lambertAmount = min ( <NUM_LIT:1> , lambertAmount ) <EOL> c = addColours ( c , self . lambertCoefficient * lambertAmount , b ) <EOL> if self . ambientCoefficient > <NUM_LIT:0> : <EOL> c = addColours ( c , self . ambientCoefficient , b ) <EOL> return c <EOL> class CheckerboardSurface ( object ) : <EOL> def __init__ ( self ) : <EOL> self . baseColour = ( <NUM_LIT:1.0> , <NUM_LIT:1.0> , <NUM_LIT:1.0> ) <EOL> self . specularCoefficient = <NUM_LIT> <EOL> self . lambertCoefficient = <NUM_LIT> <EOL> self . ambientCoefficient = <NUM_LIT:1.0> - self . specularCoefficient - self . lambertCoefficient <EOL> self . otherColour = ( <NUM_LIT:0.0> , <NUM_LIT:0.0> , <NUM_LIT:0.0> ) <EOL> self . checkSize = <NUM_LIT:1> <EOL> def baseColourAt ( self , p ) : <EOL> v = p - PZERO <EOL> v . scale ( <NUM_LIT:1.0> / self . checkSize ) <EOL> if ( int ( abs ( v . x ) + <NUM_LIT:0.5> ) + int ( abs ( v . y ) + <NUM_LIT:0.5> ) + int ( abs ( v . z ) + <NUM_LIT:0.5> ) ) % <NUM_LIT:2> : <EOL> return self . otherColour <EOL> else : <EOL> return self . baseColour <EOL> def colourAt ( self , scene , ray , p , normal ) : <EOL> b = self . baseColourAt ( p ) <EOL> c = ( <NUM_LIT:0.0> , <NUM_LIT:0.0> , <NUM_LIT:0.0> ) <EOL> if self . specularCoefficient > <NUM_LIT:0> : <EOL> reflectedRay = Ray ( p , ray . vector . reflectThrough ( normal ) ) <EOL> reflectedColour = scene . rayColour ( reflectedRay ) <EOL> c = addColours ( c , self . specularCoefficient , reflectedColour ) <EOL> if self . lambertCoefficient > <NUM_LIT:0> : <EOL> lambertAmount = <NUM_LIT:0.0> <EOL> for lightPoint in scene . visibleLights ( p ) : <EOL> contribution = ( lightPoint - p ) . normalized ( ) . dot ( normal ) <EOL> if contribution > <NUM_LIT:0> : <EOL> lambertAmount = lambertAmount + contribution <EOL> lambertAmount = min ( <NUM_LIT:1> , lambertAmount ) <EOL> c = addColours ( c , self . lambertCoefficient * lambertAmount , b ) <EOL> if self . ambientCoefficient > <NUM_LIT:0> : <EOL> c = addColours ( c , self . ambientCoefficient , b ) <EOL> return c <EOL> def _main ( ) : <EOL> Canvas = PpmCanvas <EOL> c = Canvas ( <NUM_LIT> , <NUM_LIT> , '<STR_LIT>' ) <EOL> s = Scene ( ) <EOL> s . addLight ( Point ( <NUM_LIT:30> , <NUM_LIT:30> , <NUM_LIT:10> ) ) <EOL> s . addLight ( Point ( - <NUM_LIT:10> , <NUM_LIT:100> , <NUM_LIT:30> ) ) <EOL> s . lookAt ( Point ( <NUM_LIT:0> , <NUM_LIT:2> , <NUM_LIT:0> ) ) <EOL> obj = Sphere ( Point ( <NUM_LIT:1> , <NUM_LIT:3> , - <NUM_LIT:10> ) , <NUM_LIT:2> ) <EOL> surf = SimpleSurface ( ( <NUM_LIT:1.0> , <NUM_LIT:1.0> , <NUM_LIT:0.0> ) ) <EOL> s . addObject ( obj . normalAt , obj . intersectionTime , surf . colourAt ) <EOL> for y in range ( <NUM_LIT:6> ) : <EOL> obj = Sphere ( Point ( - <NUM_LIT:3> - y * <NUM_LIT> , <NUM_LIT> , - <NUM_LIT:5> ) , <NUM_LIT> ) <EOL> surf = SimpleSurface ( ( y / <NUM_LIT> , <NUM_LIT:1> - y / <NUM_LIT> , <NUM_LIT:0.5> ) ) <EOL> s . addObject ( obj . normalAt , obj . intersectionTime , surf . colourAt ) <EOL> obj = Halfspace ( Point ( <NUM_LIT:0> , <NUM_LIT:0> , <NUM_LIT:0> ) , VUP ) <EOL> surf = CheckerboardSurface ( ) <EOL> s . addObject ( obj . normalAt , obj . intersectionTime , surf . colourAt ) <EOL> s . render ( c ) <EOL> def main ( n ) : <EOL> import time <EOL> times = [ ] <EOL> for i in range ( n ) : <EOL> t1 = time . time ( ) <EOL> _main ( ) <EOL> t2 = time . time ( ) <EOL> times . append ( t2 - t1 ) <EOL> return times <EOL> main ( <NUM_LIT:1> ) </s>
