File: ray.sml

package info (click to toggle)
mlton 20100608-2
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 34,980 kB
  • ctags: 69,089
  • sloc: ansic: 18,421; lisp: 2,879; makefile: 1,570; sh: 1,325; pascal: 256; asm: 97
file content (459 lines) | stat: -rw-r--r-- 14,585 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
(* From the SML/NJ benchmark suite. *)

(* objects.sml
 *
 * COPYRIGHT (c) 1992 AT&T Bell Laboratories
 *
 * Type declarations for the various objects in the ray tracer.
 *)

structure Objects =
  struct

    datatype point = PT of {x : real, y : real, z : real}

    datatype vector = VEC of {l : real, m : real, n : real}

    datatype ray = Ray of {s : point, d : vector}

    datatype camera = Camera of {
        vp : point,
        ul : point,
        ur : point,
        ll : point,
        lr : point
      }

    datatype color = Color of {red : real, grn : real, blu : real}

    datatype sphere = Sphere of {c : point, r : real, color : color}

    datatype hit = Miss | Hit of {t : real, s : sphere}

    datatype visible = Visible of {h : point, s : sphere}

    datatype object
      = TOP
      | NUMBER of real
      | NAME of string
      | LIST of object list
      | OPERATOR of object list -> object list
      | MARK
      | LITERAL of string
      | UNMARK
      | POINT of point
      | VECTOR of vector
      | RAY of ray
      | CAMERA of camera
      | COLOR of color
      | SPHERE of sphere
      | HIT
      | VISIBLE

  end (* Objects *)
(* interp.sml
 *
 * COPYRIGHT (c) 1992 AT&T Bell Laboratories
 *)

structure Interp =
  struct

    local
      val exit = OS.Process.exit
      fun ordof(s, i) = Char.ord(String.sub(s, i))
      exception NotAChar
      exception NotAReal
      fun fromStr x = 
        (case Char.fromString x
          of SOME c => c
           | NONE => raise NotAChar)

     fun strToReal s = 
      (case Real.fromString s
        of SOME r => r
        | _ => raise NotAReal)

    fun intToReal x = 
     (strToReal ((Int.toString x) ^ ".0"))


      val explode = (fn x => map Char.toString (explode x))
      val implode = (fn x => implode (map fromStr x))

      open Objects
      val dict = ref ([] : {key : string, value : object} list)
      fun dictInsert (NAME key, value) = let
            fun find [] = [{key=key, value=value}]
              | find (x::r) = if (key = #key x)
                  then {key=key, value=value}::r
                  else x :: (find r)
            in
              dict := find(!dict)
            end
        | dictInsert _ = raise Fail "dictInsert"
      fun prObj outStrm obj = let
            fun printf args = TextIO.output(outStrm, implode args)
            fun pr (NUMBER n) = printf["  ", Real.toString n, "\n"]
              | pr (NAME s) = printf["  ",  s, "\n"]
              | pr (LITERAL s) = printf["  ", s, "\n"]
              | pr (LIST l) = app pr l
              | pr MARK = printf["  MARK\n"]
              | pr (OPERATOR _) = printf["  <operator>\n"]
              | pr TOP = printf["  TOP OF STACK\n"]
              | pr _ = printf["  <object>\n"]
            in
              pr obj
            end
    in

    exception Stop

    fun error opName stk = let
          fun prStk ([], _) = ()
            | prStk (_, 0) = ()
            | prStk (obj::r, i) = (prObj TextIO.stdErr obj; prStk(r, i-1))
          in
            TextIO.output(TextIO.stdErr, "ERROR: "^opName^"\n");
            prStk (stk, 10);
            raise (Fail opName)
          end

    fun installOperator (name, rator) =
          dictInsert (NAME name, OPERATOR rator)

    fun ps_def (v::k::r) = (dictInsert(k, v); r)
      | ps_def stk = error "ps_def" stk

    local
      fun binOp (f, opName) = let
            fun g ((NUMBER arg1)::(NUMBER arg2)::r) =
                  NUMBER(f(arg2, arg1)) :: r
              | g stk = error opName stk
            in
              g
            end
    in
    val ps_add = binOp (op +, "add")
    val ps_sub = binOp (op -, "sub")
    val ps_mul = binOp (op *, "mul")
    val ps_div = binOp (op /, "div")
    end

    fun ps_rand stk = (NUMBER 0.5)::stk (** ??? **)

    fun ps_print (obj::r) = (prObj TextIO.stdOut obj; r)
      | ps_print stk = error "print" stk

    fun ps_dup (obj::r) = (obj::obj::r)
      | ps_dup stk = error "dup" stk

    fun ps_stop _ = raise Stop

  (* initialize dictionary and begin parsing input *)
    fun parse inStrm = let
          fun getc () = case TextIO.input1 inStrm of NONE => ""
                               | SOME c => Char.toString c
          fun peek () = case TextIO.lookahead inStrm
                         of SOME x => Char.toString x
                          | _ => ""
        (* parse one token from inStrm *)
          fun toke deferred = let
                fun doChar "" = exit OS.Process.success
                  | doChar "%" = let
                      fun lp "\n" = doChar(getc())
                        | lp "" = exit OS.Process.success
                        | lp _ = lp(getc())
                      in
                        lp(getc())
                      end
                  | doChar "{" = (MARK, deferred+1)
                  | doChar "}" = (UNMARK, deferred-1)
                  | doChar c = if Char.isSpace (fromStr c)
                      then doChar(getc())
                      else let
                        fun lp buf = (case peek()
                               of "{" => buf
                                | "}" => buf
                                | "%" => buf
                                | c => if Char.isSpace(fromStr c)
                                    then buf
                                    else (getc(); lp(c::buf))
                              (* end case *))
                        val tok = implode (rev (lp [c]))
                        val hd = ordof(tok, 0)
                        in
                          if (hd = ord (#"/"))
                            then (LITERAL(substring(tok, 1, size tok - 1)), deferred)
                          else 
                            if ((Char.isDigit (chr hd)) orelse (hd = ord (#"-")))
                            then (NUMBER(strToReal(tok)), deferred)
                            else (NAME tok, deferred)
                        end
                in
                  doChar(getc())
                end
        (* execute a token (if not deferred) *)
          fun exec (UNMARK, stk, _) = let
                fun lp ([], _) = raise Fail "MARK"
                  | lp (MARK::r, l) = (LIST l)::r
                  | lp (x::r, l) = lp (r, x::l)
                  in
                    lp (stk, [])
                  end
            | exec (OPERATOR f, stk, 0) = f stk
            | exec (LIST l, stk, 0) = let
                fun execBody ([], stk) = stk
                  | execBody (obj::r, stk) = (exec(obj, stk, 0); execBody(r, stk))
                in
                  execBody (l, stk)
                end
            | exec (NAME s, stk, 0) = let
                fun find [] = raise Fail "undefined name"
                  | find ({key, value}::r) = if (key = s) then value else find r
                in
                  exec (find (!dict), stk, 0)
                end
            | exec (obj, stk, _) = obj::stk
          fun lp (stk, level) = let
                val (obj, level) = toke level
                val stk = exec (obj, stk, level)
                in
                  lp (stk, level)
                end
          in
            installOperator ("add", ps_add);
            installOperator ("def", ps_def);
            installOperator ("div", ps_div);
            installOperator ("dup", ps_dup);
            installOperator ("mul", ps_mul);
            installOperator ("print", ps_print);
            installOperator ("rand", ps_rand);
            installOperator ("stop", ps_stop);
            installOperator ("sub", ps_sub);
            (lp ([], 0)) handle Stop => ()
          end (* parse *)

    end (* local *)

  end (* Interp *)
(* ray.sml
 *
 * COPYRIGHT (c) 1992 AT&T Bell Laboratories
 *)

structure Ray =
  struct
    local open Objects in

  (** basic operations on points and vectors **)

    fun scaleVector (s, VEC{l, m, n}) = VEC{l=s*l, m=s*m, n=s*n}

    fun vecPlusVec (VEC{l, m, n}, VEC{l=l', m=m', n=n'}) = VEC{l=l+l', m=m+m', n=n+n'}

    fun vecPlusPt (VEC{l, m, n}, PT{x, y, z}) = PT{x=x+l, y=y+m, z=z+n}

    fun ptMinusPt (PT{x, y, z}, PT{x=x', y=y', z=z'}) = VEC{l=x-x', m=y-y', n=z-z'}

    fun wave (PT{x, y, z}, PT{x=x', y=y', z=z'}, w) = PT{
            x = w * (x' - x) + x,
            y = w * (y' - y) + y,
            z = w * (z' - z) + z
          }

    fun dotProd (VEC{l, m, n}, VEC{l=l', m=m', n=n'}) = ((l*l') + (m*m') + (n*n'))

  (* normal vector to sphere *)
    fun normalSphere (Visible{h, s as Sphere{c, ...}}) = let
          val n = ptMinusPt(h, c)
          val norm = Math.sqrt(dotProd(n, n))
          in
            scaleVector(1.0 / norm, n)
          end

  (* intersect a ray with a sphere *)
    fun intersectSphere (Ray ray, s as Sphere sphere) = let
          val a = dotProd(#d ray, #d ray)
          val sdiffc = ptMinusPt(#s ray, #c sphere)
          val b = 2.0 * dotProd(sdiffc, #d ray)
          val c = dotProd(sdiffc, sdiffc) - (#r sphere * #r sphere)
          val d = b*b - 4.0*a*c
          in
            if (d <= 0.0)
              then Miss
              else let
                val d = Math.sqrt(d)
                val t1 = (~b - d) / (2.0 * a)
                val t2 = (~b + d) / (2.0 * a)
                val t = if ((t1 > 0.0) andalso (t1 < t2)) then t1 else t2
                in
                  Hit{t=t, s=s}
                end
          end

  (* simple shading function *)
    fun shade {light, phi} (visible as Visible{h, s}) = let
          val l = ptMinusPt(light, h)
          val n = normalSphere(visible)
          val irradiance = phi * dotProd(l,n) / dotProd(l,l);
          val irradiance = (if (irradiance < 0.0) then 0.0 else irradiance) + 0.05
          val Sphere{color=Color{red, grn, blu}, ...} = s
          in
            Color{red=red*irradiance, grn=grn*irradiance, blu=blu*irradiance}
          end

    fun trace (ray as (Ray ray'), objList) = let
          fun closest (Miss, x) = x
            | closest (x, Miss) = x
            | closest (h1 as Hit{t=t1, ...}, h2 as Hit{t=t2, ...}) =
                if (t2 < t1) then h2 else h1
          fun lp ([], Hit{t, s}) = Visible{
                  h = vecPlusPt(scaleVector(t, #d ray'), #s ray'),
                  s = s
                }
            | lp (s :: r, closestHit) =
                lp (r, closest (closestHit, intersectSphere (ray, s)))
            | lp _ = raise Fail "trace"
          in
            lp (objList, Miss)
          end

    fun camera (Camera cam) (x, y) = let
          val l = wave (#ul cam, #ll cam, y)
          val r = wave (#ur cam, #lr cam, y)
          val image_point = wave(l, r, x)
          in
            Ray{d = ptMinusPt(image_point, #vp cam), s = #vp cam}
          end

    val shade = shade {light = PT{x = 10.0, y = ~10.0, z = ~10.0}, phi = 16.0}
    val camera = camera (Camera{
            vp = PT{x = 0.0, y = 0.0, z = ~3.0},
            ul = PT{x = ~1.0, y = ~1.0, z = 0.0},
            ur = PT{x = 1.0, y = ~1.0, z = 0.0},
            ll = PT{x = ~1.0, y = 1.0, z = 0.0},
            lr = PT{x = 1.0, y = 1.0, z = 0.0}
          })

    fun image objList (x, y) = shade (trace(camera(x, y), objList))

    fun picture (picName, objList) = let
          val outStrm = TextIO.openOut picName
          val image = image objList
          val print = fn x => TextIO.output (outStrm, x)
          fun putc c = TextIO.output1(outStrm, chr c)
          fun doPixel (i, j) = let
                val x = (real i) / 512.0
                val y = (real j) / 512.0
                val (Color c) = image (x, y)
                fun cvt x = if (x >= 1.0) then 255 else floor(256.0*x)
                in
                  putc (cvt (#red c));
                  putc (cvt (#grn c));
                  putc (cvt (#blu c))
                end
          fun lp_j j = if (j < 512)
                then let
                  fun lp_i i = if (i < 512)
                        then (doPixel(i, j); lp_i(i+1))
                        else ()
                  in
                    lp_i 0; lp_j(j+1)
                  end
                else ()
          in
            print "TYPE=dump\n";
            print "WINDOW=0 0 512 512\n";
            print "NCHAN=3\n";
            print "CHAN=rgb\n";
            print "\n";
            lp_j 0;
            TextIO.closeOut outStrm
          end

    end (* local *)
  end; (* Ray *)
(* interface.sml
 *
 * COPYRIGHT (c) 1992 AT&T Bell Laboratories
 *
 * The interface between the interpreter and the ray tracer.
 *)

structure Interface =
  struct
    local
      open Objects
    in

  (* color pops three numbers and pushes a color object.
   * usage: red-value green-value blue-value color
   *)
    fun ps_color ((NUMBER blu)::(NUMBER grn)::(NUMBER red)::r) =
          (COLOR(Color{red=red, grn=grn, blu=blu})) :: r
      | ps_color stk = Interp.error "color" stk

  (* pop radius, coordinates of center, and a color and push a sphere
   * usage: radius x y z color-value sphere
   *)
    fun ps_sphere (
          (COLOR c)::(NUMBER z)::(NUMBER y)::(NUMBER x)::(NUMBER rad)::r
        ) = SPHERE(Sphere{c=PT{x=x, y=y, z=z}, r=rad, color=c}) :: r
      | ps_sphere stk = Interp.error "sphere" stk

  (* build an object list from solids on the stack, then invoke raytracer *)
    fun ps_raytrace ((LITERAL picName)::r) = let
          fun mkObjList ([], l) = l
            | mkObjList ((SPHERE s)::r, l) = mkObjList(r, s::l)
            | mkObjList (_::r, l) = mkObjList(r, l)
          in
            Ray.picture(picName, mkObjList(r, []));
            []
          end
      | ps_raytrace stk = Interp.error "raytrace" stk

  (* add ray tracing operations to interpreter dictionary *)
    fun rtInit () = (
          Interp.installOperator("color", ps_color);
          Interp.installOperator("sphere", ps_sphere);
          Interp.installOperator("raytrace", ps_raytrace))

    end (* local *)
  end;

signature BMARK =
  sig
    val doit : int -> unit
    val testit : TextIO.outstream -> unit
  end;
(* main.sml
 *
 * COPYRIGHT (c) 1992 AT&T Bell Laboratories
 *
 * Main structure for running raytracer as benchmark.
 *)

structure Main : BMARK =
  struct

    fun doit n =
       let
          fun loop n =
             if n = 0
                then ()
             else
                let
                   val strm = TextIO.openIn "DATA/ray"
                   val _ = Interface.rtInit()
                   val _ = Interp.parse strm
                   val _ = TextIO.closeIn strm
                in
                   loop (n - 1)
                end
       in
          loop n
       end

    fun testit _ = ()
  end