So while looking for visual examples to showcase Weave, I went a outside of my comfort zone and tried my hand at graphics programming, in particular raytracing.
# Faster I was pleasantly surprised to see Nim consistently faster than C++ on single-threaded code: [SmallPT](https://www.kevinbeason.com/smallpt/) (Raytracer in 99 lines of C++) Bench| Nim| Clang C++ OpenMP| GCC 10 C++ OpenMP| GCC 8 C++ OpenMP ---|---|---|---|--- Single-threaded| 4min43.369s| 4m51.052s| 4min50.934s| 4m50.648s Multithreaded| 12.977s| 14.428s| 2min14.616s| 12.244s Nested-parallel| 12.981s| | | Parallel speedup| 21.83x| 20.17x| 2.16x| 23.74x I've also implemented the first [Ray Tracing in One Weekend book](https://github.com/RayTracing/raytracing.github.io) And written an extensive writeup in the README of the repo. I find Nim especially suited for graphics computation. For this raytracing project, it was once again faster than C++ by about 10% (though I had to use an iterative `radiance` function instead of the traditional recursive because I had too many nested generic instantiation when i wanted to use `Option[T]` which were removed anyway because it made the code 5x slower). Nim| C++ ---|--- 38.577s| 42.303s # Safer Regarding safety, I've used distinct types to make sure I don't mix `Point3`, `Vec3`, `UnitVector`, `Degrees`, `Radians`, `Color`, `Attenuations`. This reminds me of my physics teachers always asking to check for your units/dimensions. Except that now it's the compiler that does that for you. For example substracting 2 points gives a vector, adding a vector to a point gives a point and adding 2 points is a compile-time error: func `-`*(a, b: Point3): Vec3 {.inline.}= ## Substracting points from one point to the other ## gives a vector result.x = a.x - b.x result.y = a.y - b.y result.z = a.z - b.z func `+`*(p: Point3, v: Vec3): Point3 {.inline.}= ## Adding a vector to a point results in a point Point3(Vec3(p) + v) func `-`*(p: Point3, v: Vec3): Point3 {.inline.}= ## Substracting a vector to a point results in a point Point3(Vec3(p) - v) func `+`*(a, b: Point3): Point3 {.error: "Adding 2 Point3 doesn't make physical sense".} Run As is multiplying color, you should multiply with attenuation instead: type Color* {.borrow: `.`.} = distinct Vec3 func `*`*(a, b: Color): Color {.error: "Multiplying 2 Colors doesn't make physical sense".} type Attenuation* {.borrow: `.`.} = distinct Color func `*=`*(a: var Attenuation, b: Attenuation) {.inline.} = # Multiply a color by a per-channel attenuation factor a.x *= b.x a.y *= b.y a.z *= b.z func `*`*(a, b: Attenuation): Attenuation {.inline.} = # Multiply a color by a per-channel attenuation factor result.x = a.x * b.x result.y = a.y * b.y result.z = a.z * b.z func `*=`*(a: var Color, b: Attenuation) {.inline.} = # Multiply a color by a per-channel attenuation factor a.x *= b.x a.y *= b.y a.z *= b.z Run And this might have caught a "semantic bug" upstream (where they took the length of a point instead of a vector): [https://github.com/RayTracing/raytracing.github.io/issues/609](https://github.com/RayTracing/raytracing.github.io/issues/609) The repo is here: [https://github.com/mratsim/trace-of-radiance](https://github.com/mratsim/trace-of-radiance) The README is a writeup that can be turned into a blog post
