Strange, downloading lmr.tgz gives a demo.lmr which seems to be a corrupted copy or something of lmr.arc.
What is the file format, anyway? It looks like Arc readable format but I can't figure out the structure.
Regarding the intersect code: generally most systems work with shapes that are just the basic triangle plane. This also generally means that the intersect code would really just return one point, not many - currently, your code seems to return a list of points for each shape. Even if your "shape" becomes an entire tree of shapes (say a BSP tree), I would assume that only one intersection - the one nearest to the ray source - would "win", so intersections on shape trees should (I think) return just one intersection.
Returning just one intersection saves memory - you don't need to store several lists. In particular, all you have to do is to get the intersection with the least distance:
(def gen-intersection (ray)
(= intersection nil)
(each sh shape
(aif (intersect ray sh)
(when (or (no intersection) (< (car it) (car intersection)))
(= intersection it))))
intersection)
Can you give your reasons why you chose to retain multiple intersections?
If you really think retaining multiple intersections is better, I would suggest you also study lib/tconc.arc, which includes a somewhat-fast list concatenate function (but you need a transparent wrapper around the list you are building).
Depending on how motivated you are at studying the list structure and advanced stuff, you might prefer to use explicit head+tail catenations, although head+tail tends to be better used on continuation passing style, especially in languages (e.g. Arc) which don't naturally support multiple-value-returns (lists don't count, you have to destructure them, which takes time).
Also: avoid using nested maps - each map reallocates memory:
(of course, cross3 doesn't seem to be used anyway)
Finally: avoid using 't as a variable name.
I think the greatest consumer of time would probably be 'map. 'map is nice but it creates a structure, and the implementation is not written in tail-recursive-modulo-cons ^^. If you're just going to work with individual lists, you can use map1, which is like map but only works on one list, but is slightly faster.
The other thing I think that the time is being consumed in might be with the use of 'alref. It might be better to use map 'listtab on your shapes, so that you don't have to use alref, which always does linear search.
First off, thanks for the pointers! I'll certainly look into those when I have a chance.
I have no idea why the file was corrupted. I'll re-archive and update the file on the server.
You're right about the multiple intersections I indeed sort them by distance. But I forgot to return only the first two elements (needed for entering and exiting a shape for refraction). I'll fix that.
Ugh - I used t as a var? I should have known better than that. Must be remnants of my terse C coding style :)
> But I forgot to return only the first two elements (needed for entering and exiting a shape for refraction).
Strange; I would have thought you needed only one intersection, because my mental model for refraction would be:
|
-----+-------
\
\ <-----new ray
--------+----
|
|
viewer
So you really need only one intersection - the nearest, because the second intersection wouldn't really be aligned to the refraction ray. But then I haven't read the book you are reading.
Basically at each intersection point I'd expect to split into three rays: a reflection ray (cross product to the normal), a refraction ray (if at least partially transparent) and a shadow ray (towards any source(s) of light).
I'd be interested in knowing what you found simple/difficult in working with Arc. There's precious few applications written so far, so it would be nice to know where you found the language lacking.
Good question. Here are a few things that tripped me up:
1) An easy way to call differing functions based on the data type of an argument (polymorphism). Ruby has a nice approach as shown here (see the section marked 'Duck Typing'): http://www.ibm.com/developerworks/java/library/j-ruby/
2) I spent a while chasing a bug in an 'if' statement because I neglected to surround multiple statements with a 'do' statement.
3) It wasn't obvious to me how to build a bitmap array. I naively created an array filled with zeros for each scanline. I would then push this scanline onto another var. But it turned out all of the scanlines pushed this way were pointing to the same scanline. This was a problem until I found out how to copy a var in Arc (use copy!).
4) It's not clear to me how you modularize code, so everything is in one file.
5) Coding style. I have none when it comes to Arc, and there are few examples. For example, I didn't realize that an Arc convention is to append an asterisk to global variables (This follows the Lisp convention I think).
6) Spent time figuring out how to make my vector operations take list arguments without quotes (This was my initiation into the world of macros). Then realized that this was not necessary since these functions would have variables passed in arguments, not literal lists.
7) Errors were not specific (understatement).
8) No raw file output, or low level bit operators. So I added these to the base language.
1) Hmm. Currently I've been experimenting around with Arc's type system. The type system is "not bad" but there's a definite problem: making a user-defined type quack like a built-in type is very difficult. My "Create your own collection" series is probably of interest in this research.
If however you are using only user types (not masquerading existing types), then nex3's defm macro is your friend: http://arclanguage.com/item?id=4644
2) LOL. However, if you need just the "then" branch, or just the "else" branch, you can use the 'when and 'unless macros:
(from "arc.arc")
[mac] (when test . body)
When `test' is true, do `body'.
See also [[unless]] [[if]] [[awhen]]
nil
arc> (help unless)
(from "arc.arc")
[mac] (unless test . body)
When `test' is not true, do `body'.
See also [[when]] [[if]] [[no]]
nil
4) Definite problem. In fact, pg's own code doesn't seem very modularized: bits and pieces of the various parts of the webserver are in srv.arc, html.arc, and app.arc. Fortunately Arkani's 'help command also includes which file the function is defined in, so at least you can track files a little (although in practice I often find myself looking for definitions using grep).
5) Arkani has a "CONVENTIONS" file for some conventions. However it's incomplete IMO, so probably we need to fill this in somewhat.
7) True, true. Sure, Arc code is fun to write, until it encounters a problem. Then it's hard to debug/optimize/whatever