Compare commits
10 Commits
4b227fdd28
...
d835dc48ce
| Author | SHA1 | Date | |
|---|---|---|---|
| d835dc48ce | |||
| e770e6f8a7 | |||
| b38e6c00a5 | |||
| 1871f6cae4 | |||
| 88bbcf036f | |||
| ad0792dcd3 | |||
| 1856de7685 | |||
| 3e11142361 | |||
| 9179f06132 | |||
| 6a3348d727 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.direnv/
|
||||
target/
|
||||
result/
|
||||
*.png
|
||||
|
||||
119
Cargo.lock
generated
119
Cargo.lock
generated
@ -16,9 +16,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.93"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@ -125,9 +125,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.20.0"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@ -143,9 +143,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.1"
|
||||
version = "1.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@ -191,9 +191,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
@ -210,9 +210,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
@ -249,9 +249,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
@ -388,9 +388,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -439,9 +439,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -490,9 +490,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.164"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@ -511,11 +511,29 @@ dependencies = [
|
||||
"as-any",
|
||||
"futures",
|
||||
"image",
|
||||
"lispers-core",
|
||||
"lispers-macro",
|
||||
"nalgebra",
|
||||
"nix",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lispers-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"as-any",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lispers-macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
@ -565,9 +583,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
@ -725,9 +743,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.14"
|
||||
version = "0.17.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
|
||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
@ -747,9 +765,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -790,9 +808,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -911,27 +929,27 @@ checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "0.7.2"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a"
|
||||
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -998,9 +1016,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
version = "2.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1128,9 +1146,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@ -1139,13 +1157,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -1154,9 +1171,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -1164,9 +1181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1177,9 +1194,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
@ -1189,9 +1206,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "wide"
|
||||
version = "0.7.30"
|
||||
version = "0.7.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019"
|
||||
checksum = "cc0ca27312d1e9218687a4e804cd4b7410bf54125d54d13e5170efbf09066d24"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"safe_arch",
|
||||
@ -1199,9 +1216,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -1244,9 +1261,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
|
||||
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
26
Cargo.toml
26
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lispers"
|
||||
description = "lisp interpreter in rust"
|
||||
description = "lisp interpreter in rust for raytracing"
|
||||
publish = false
|
||||
version = "0.1.0"
|
||||
|
||||
@ -11,17 +11,35 @@ name = "lispers"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "demo"
|
||||
path = "src/bin/demo.rs"
|
||||
name = "lisp_demo"
|
||||
path = "src/bin/lisp_demo.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "repl"
|
||||
path = "src/bin/repl.rs"
|
||||
|
||||
[dependencies]
|
||||
[[bin]]
|
||||
name = "rt_lisp_demo"
|
||||
path = "src/bin/rt_lisp_demo.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rt_interp"
|
||||
path = "src/bin/rt_interp.rs"
|
||||
|
||||
[workspace]
|
||||
members = [ "lispers-core", "lispers-macro"]
|
||||
|
||||
[workspace.dependencies]
|
||||
lispers-core = {path = "lispers-core"}
|
||||
lispers-macro = {path = "lispers-macro"}
|
||||
as-any = "0.3.1"
|
||||
|
||||
[dependencies]
|
||||
as-any = {workspace = true}
|
||||
futures = "0.3.30"
|
||||
image = "0.25.5"
|
||||
nalgebra = "0.33.2"
|
||||
nix = "0.29.0"
|
||||
rayon = "1.10.0"
|
||||
lispers-core = {workspace = true}
|
||||
lispers-macro = {workspace = true}
|
||||
|
||||
16
flake.nix
16
flake.nix
@ -63,9 +63,9 @@
|
||||
};
|
||||
in rec {
|
||||
apps = rec {
|
||||
demo = {
|
||||
lisp_demo = {
|
||||
type = "app";
|
||||
program = "${packages.default}/bin/demo";
|
||||
program = "${packages.default}/bin/lisp_demo";
|
||||
};
|
||||
repl = {
|
||||
type = "app";
|
||||
@ -75,10 +75,18 @@
|
||||
type = "app";
|
||||
program = "${packages.default}/bin/rt_demo";
|
||||
};
|
||||
default = demo;
|
||||
rt_demo_lisp = {
|
||||
type = "app";
|
||||
program = "${packages.default}/bin/rt_lisp_demo";
|
||||
};
|
||||
rt_interp = {
|
||||
type = "app";
|
||||
program = "${packages.default}/bin/rt_interp";
|
||||
};
|
||||
default = rt_demo_lisp;
|
||||
};
|
||||
packages = rec {
|
||||
lispers = cargoNix.rootCrate.build;
|
||||
lispers = cargoNix.workspaceMembers.lispers.build;
|
||||
default = lispers;
|
||||
};
|
||||
devShell = pkgs.mkShell {
|
||||
|
||||
7
lispers-core/Cargo.toml
Normal file
7
lispers-core/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "lispers-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
as-any = {workspace = true}
|
||||
2
lispers-core/src/lib.rs
Normal file
2
lispers-core/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod lisp;
|
||||
pub mod parser;
|
||||
@ -198,6 +198,34 @@ impl From<(Expression, Expression)> for Expression {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Expression {
|
||||
fn from(value: i64) -> Self {
|
||||
Expression::Integer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Expression {
|
||||
fn from(value: f64) -> Self {
|
||||
Expression::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Expression {
|
||||
fn from(value: String) -> Self {
|
||||
Expression::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Expression {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Expression::True
|
||||
} else {
|
||||
Expression::Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Expression> for i64 {
|
||||
type Error = EvalError;
|
||||
fn try_from(value: Expression) -> Result<i64, Self::Error> {
|
||||
@ -321,7 +349,7 @@ impl Display for Expression {
|
||||
Expression::Symbol(s) => write!(f, "{}", s),
|
||||
Expression::Integer(i) => write!(f, "{}", i),
|
||||
Expression::Float(fl) => write!(f, "{}", fl),
|
||||
Expression::String(s) => write!(f, "\"{}\"", s),
|
||||
Expression::String(s) => write!(f, "{}", s),
|
||||
Expression::True => write!(f, "true"),
|
||||
Expression::Nil => write!(f, "nil"),
|
||||
}
|
||||
@ -213,10 +213,17 @@ pub fn prelude_set(env: &Environment, expr: Expression) -> Result<Expression, Ev
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prelude_println(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let [e] = expr.try_into()?;
|
||||
let e = eval(env, e)?;
|
||||
println!("{}", e);
|
||||
Ok(e)
|
||||
}
|
||||
|
||||
pub fn prelude_print(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let [e] = expr.try_into()?;
|
||||
let e = eval(env, e)?;
|
||||
println!("Prelude: {}", e);
|
||||
print!("{}", e);
|
||||
Ok(e)
|
||||
}
|
||||
|
||||
@ -255,6 +262,69 @@ pub fn prelude_progn(env: &Environment, expr: Expression) -> Result<Expression,
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn prelude_list(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let exprs: Vec<Expression> = expr.try_into()?;
|
||||
|
||||
let evaled_exprs: Vec<_> = exprs
|
||||
.iter()
|
||||
.map(|e| eval(env, e.to_owned()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(evaled_exprs.into())
|
||||
}
|
||||
|
||||
pub fn prelude_append(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let exprs: Vec<Expression> = expr.try_into()?;
|
||||
|
||||
let evaled_exprs: Vec<_> = exprs
|
||||
.iter()
|
||||
.map(|e| eval(env, e.to_owned())?.try_into())
|
||||
.collect::<Result<Vec<Vec<Expression>>, _>>()?;
|
||||
|
||||
Ok(evaled_exprs.concat().into())
|
||||
}
|
||||
|
||||
pub fn prelude_concat(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let exprs: Vec<Expression> = expr.try_into()?;
|
||||
|
||||
let evaled_exprs: Vec<String> = exprs
|
||||
.iter()
|
||||
.map(|e| eval(env, e.to_owned())?.try_into())
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(evaled_exprs.concat().into())
|
||||
}
|
||||
|
||||
pub fn prelude_map(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let [f, list]: [Expression; 2] = expr.try_into()?;
|
||||
|
||||
let f = eval(env, f)?;
|
||||
let list: Vec<Expression> = eval(env, list)?.try_into()?;
|
||||
|
||||
let list: Vec<Expression> = list
|
||||
.iter()
|
||||
.map(|e| {
|
||||
eval(
|
||||
env,
|
||||
Expression::Cell(
|
||||
Box::new(f.clone()),
|
||||
Box::new(Expression::Cell(
|
||||
Box::new(e.to_owned()),
|
||||
Box::new(Expression::Nil),
|
||||
)),
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
pub fn prelude_to_string(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let [e] = expr.try_into()?;
|
||||
Ok(Expression::String(format!("{}", eval(env, e)?)))
|
||||
}
|
||||
|
||||
pub fn mk_prelude(layer: &mut EnvironmentLayer) {
|
||||
layer.set("+".to_string(), Expression::Function(prelude_add));
|
||||
layer.set("-".to_string(), Expression::Function(prelude_sub));
|
||||
@ -270,10 +340,19 @@ pub fn mk_prelude(layer: &mut EnvironmentLayer) {
|
||||
layer.set("not".to_string(), Expression::Function(prelude_not));
|
||||
layer.set("let".to_string(), Expression::Function(prelude_let));
|
||||
layer.set("set".to_string(), Expression::Function(prelude_set));
|
||||
layer.set("println".to_string(), Expression::Function(prelude_println));
|
||||
layer.set("print".to_string(), Expression::Function(prelude_print));
|
||||
layer.set("cons".to_string(), Expression::Function(prelude_cons));
|
||||
layer.set("car".to_string(), Expression::Function(prelude_car));
|
||||
layer.set("cdr".to_string(), Expression::Function(prelude_cdr));
|
||||
layer.set("eval".to_string(), Expression::Function(prelude_eval));
|
||||
layer.set("progn".to_string(), Expression::Function(prelude_progn));
|
||||
layer.set("list".to_string(), Expression::Function(prelude_list));
|
||||
layer.set("append".to_string(), Expression::Function(prelude_append));
|
||||
layer.set("concat".to_string(), Expression::Function(prelude_concat));
|
||||
layer.set("map".to_string(), Expression::Function(prelude_map));
|
||||
layer.set(
|
||||
"to-string".to_string(),
|
||||
Expression::Function(prelude_to_string),
|
||||
);
|
||||
}
|
||||
12
lispers-macro/Cargo.toml
Normal file
12
lispers-macro/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "lispers-macro"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.92"
|
||||
quote = "1.0.38"
|
||||
syn = "2.0.94"
|
||||
192
lispers-macro/src/lib.rs
Normal file
192
lispers-macro/src/lib.rs
Normal file
@ -0,0 +1,192 @@
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, punctuated::Punctuated, FnArg, Ident, ItemFn, Pat, PatType, Token};
|
||||
|
||||
enum FlagOrKV {
|
||||
Flag(Ident),
|
||||
KV(Ident, Ident),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FlagOrKV {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident: Ident = input.parse()?;
|
||||
if input.peek(Token![=]) {
|
||||
input.parse::<Token![=]>()?;
|
||||
let value: Ident = input.parse()?;
|
||||
Ok(FlagOrKV::KV(ident, value))
|
||||
} else {
|
||||
Ok(FlagOrKV::Flag(ident))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NativeLispAttrs {
|
||||
pub eval: bool,
|
||||
pub fname: Option<Ident>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for NativeLispAttrs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let exprs = Punctuated::<FlagOrKV, Token![,]>::parse_terminated(input)?;
|
||||
|
||||
let mut ret = NativeLispAttrs {
|
||||
eval: false,
|
||||
fname: None,
|
||||
};
|
||||
|
||||
for e in exprs {
|
||||
match e {
|
||||
FlagOrKV::Flag(flag) => {
|
||||
if flag.to_string() == "eval" {
|
||||
ret.eval = true;
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(flag, "Unknown flag"));
|
||||
}
|
||||
}
|
||||
FlagOrKV::KV(k, v) => {
|
||||
if k.to_string() == "fname" {
|
||||
ret.fname = Some(v);
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(k, "Unknown key"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
struct NativeLispProxyAttrs {
|
||||
pub eval: bool,
|
||||
pub fname: Ident,
|
||||
pub dispatcher: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for NativeLispProxyAttrs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let exprs = Punctuated::<FlagOrKV, Token![,]>::parse_terminated(input)?;
|
||||
|
||||
let mut ret = NativeLispProxyAttrs {
|
||||
eval: false,
|
||||
fname: Ident::new("proxy", proc_macro2::Span::call_site()),
|
||||
dispatcher: Vec::new(),
|
||||
};
|
||||
|
||||
for e in exprs {
|
||||
match e {
|
||||
FlagOrKV::Flag(flag) => {
|
||||
if flag.to_string() == "eval" {
|
||||
ret.eval = true;
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(flag, "Unknown flag"));
|
||||
}
|
||||
}
|
||||
FlagOrKV::KV(k, v) => {
|
||||
if k.to_string() == "dispatch" {
|
||||
ret.dispatcher.push(v);
|
||||
} else if k.to_string() == "fname" {
|
||||
ret.fname = v;
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(k, "Unknown key"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// Parse function
|
||||
let input = parse_macro_input!(item as ItemFn);
|
||||
let vis = &input.vis;
|
||||
let sig = &input.sig;
|
||||
let func_name = &sig.ident;
|
||||
let block = &input.block;
|
||||
let ret = &sig.output;
|
||||
|
||||
// Parse attrs
|
||||
let attr = parse_macro_input!(attr as NativeLispAttrs);
|
||||
|
||||
// Extract argument conversion statements
|
||||
let mut conversion_statements = Vec::new();
|
||||
|
||||
for arg in &sig.inputs {
|
||||
if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
|
||||
if let Pat::Ident(ident) = pat.as_ref() {
|
||||
let arg_name_str = ident.ident.to_string();
|
||||
if attr.eval {
|
||||
conversion_statements.push(quote! {
|
||||
let #ident: #ty = eval(env, args_iter.next().ok_or(EvalError::ArgumentError(format!("Missing Argument {}", #arg_name_str)))?)?.try_into()?;
|
||||
});
|
||||
} else {
|
||||
conversion_statements.push(quote! {
|
||||
let #ident: #ty = args_iter.next().ok_or(EvalError::ArgumentError(format!("Missing Argument {}", #arg_name_str)))?.try_into()?;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let func_name = match attr.fname {
|
||||
Some(fname) => fname,
|
||||
None => func_name.clone(),
|
||||
};
|
||||
|
||||
quote! {
|
||||
#vis fn #func_name(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let args: Vec<Expression> = expr.try_into()?;
|
||||
let mut args_iter = args.into_iter();
|
||||
|
||||
#(#conversion_statements)*
|
||||
|
||||
Ok((|| #ret #block)()?.into())
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn native_lisp_function_proxy(item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(item as NativeLispProxyAttrs);
|
||||
let fname = &args.fname;
|
||||
|
||||
let eval_statement = if args.eval {
|
||||
quote! {
|
||||
let exprs: Vec<Expression> = expr.try_into()?;
|
||||
let exprs = exprs.into_iter().map(|expr| eval(env, expr)).collect::<Result<Vec<Expression>, EvalError>>()?;
|
||||
let expr: Expression = exprs.into();
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let try_apply_statements = args
|
||||
.dispatcher
|
||||
.iter()
|
||||
.map(|impl_name| {
|
||||
quote! {
|
||||
match #impl_name(env, expr.clone()) {
|
||||
Err(EvalError::ArgumentError(e)) => {/*Pass*/},
|
||||
Err(EvalError::TypeError(e)) => {/*Pass*/},
|
||||
x => return x,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
fn #fname(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
#eval_statement
|
||||
|
||||
#(#try_apply_statements)*
|
||||
|
||||
Err(EvalError::TypeError("No applicable method found".to_string()))
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
79
scenes/demo-1.lisp
Normal file
79
scenes/demo-1.lisp
Normal file
@ -0,0 +1,79 @@
|
||||
(set 'red
|
||||
(material
|
||||
(color 1 0 0)
|
||||
(color 1 0 0)
|
||||
(color 0.5 0 0)
|
||||
50 0.25))
|
||||
(set 'blue
|
||||
(material
|
||||
(color 0 0 1)
|
||||
(color 0 0 1)
|
||||
(color 0 0 0.6)
|
||||
50 0.25))
|
||||
(set 'green
|
||||
(material
|
||||
(color 0 1 0)
|
||||
(color 0 1 0)
|
||||
(color 0 0.6 0)
|
||||
50 0.25))
|
||||
(set 'white
|
||||
(material
|
||||
(color 1 1 1)
|
||||
(color 1 1 1)
|
||||
(color 0.6 0.6 0.6)
|
||||
100 0.5))
|
||||
(set 'black
|
||||
(material
|
||||
(color 0 0 0)
|
||||
(color 0 0 0)
|
||||
(color 0.6 0.6 0.6)
|
||||
100 0.5))
|
||||
|
||||
(set 's1
|
||||
(sphere
|
||||
(point 0 1 0) 1 blue))
|
||||
(set 's2
|
||||
(sphere
|
||||
(point 2 0.5 2) 0.5 green))
|
||||
|
||||
(defun spiral-sphere (i n)
|
||||
(sphere
|
||||
(progn
|
||||
(print "Spiral Sphere at: ")
|
||||
(println (point
|
||||
(* 2 (cos (/ (* i 6.2) n)))
|
||||
0.5
|
||||
(* 2 (sin (/ (* i 6.2) n)))))
|
||||
)
|
||||
0.5 red))
|
||||
|
||||
(defun spiral (scn i n)
|
||||
(if (< i n)
|
||||
(scene-add
|
||||
(spiral scn (+ i 1) n)
|
||||
(spiral-sphere i n))
|
||||
scn))
|
||||
|
||||
(set 'p1
|
||||
(checkerboard
|
||||
(point 0 0 0)
|
||||
(vector 0 1 0)
|
||||
black white 0.5
|
||||
(vector 0.5 0 1)))
|
||||
|
||||
(set 'l1 (light (point 3 10 5) (color 1 1 1)))
|
||||
(set 'l2 (light (point 2 10 5) (color 1 1 1)))
|
||||
|
||||
|
||||
(set 'scn (scene
|
||||
(color 0.1 0.1 0.1)
|
||||
'(s1 s2 p1)
|
||||
'(l1 l2)))
|
||||
|
||||
(set 'scn (spiral scn 0.0 10.0))
|
||||
|
||||
(println (cons "Final Scene:" scn))
|
||||
|
||||
(set 'cam (camera (point 0 3 6) (point 0 0 0) (vector 0 1 0) 40 1920 1080))
|
||||
|
||||
(render cam scn 5 4 "demo-1.png")
|
||||
@ -1,5 +1,5 @@
|
||||
use lispers::lisp::{eval, Environment};
|
||||
use lispers::parser::ExpressionStream;
|
||||
use lispers_core::lisp::{eval, Environment};
|
||||
use lispers_core::parser::ExpressionStream;
|
||||
|
||||
fn main() {
|
||||
let programs = [
|
||||
@ -1,7 +1,7 @@
|
||||
use lispers::lisp::Expression;
|
||||
use lispers::parser::ParserError;
|
||||
use lispers_core::lisp::Expression;
|
||||
use lispers_core::parser::ParserError;
|
||||
|
||||
use lispers::{lisp, parser};
|
||||
use lispers_core::{lisp, parser};
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
|
||||
@ -3,10 +3,9 @@ use lispers::raytracer::{
|
||||
plane::Checkerboard,
|
||||
scene::Scene,
|
||||
sphere::Sphere,
|
||||
types::{Color, Light, Material, Point3, Vector3},
|
||||
types::{Color, Light, Material, Point3, RTObjectWrapper, Vector3},
|
||||
};
|
||||
extern crate nalgebra as na;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() {
|
||||
@ -23,7 +22,7 @@ fn main() {
|
||||
color: Color::new(1.0, 1.0, 1.0),
|
||||
});
|
||||
|
||||
scene.add_object(Arc::new(Checkerboard::new(
|
||||
scene.add_object(RTObjectWrapper::new(Box::new(Checkerboard::new(
|
||||
Point3::new(0.0, -1.0, 0.0),
|
||||
Vector3::new(0.0, 1.0, 0.0),
|
||||
Material::new(
|
||||
@ -42,9 +41,9 @@ fn main() {
|
||||
),
|
||||
0.3,
|
||||
Vector3::new(0.0, 0.0, 1.0),
|
||||
)));
|
||||
))));
|
||||
|
||||
scene.add_object(Arc::new(Sphere::new(
|
||||
scene.add_object(RTObjectWrapper::new(Box::new(Sphere::new(
|
||||
Point3::new(-2.0, 0.0, 1.0),
|
||||
1.0,
|
||||
Material::new(
|
||||
@ -54,9 +53,9 @@ fn main() {
|
||||
20.0,
|
||||
0.3,
|
||||
),
|
||||
)));
|
||||
))));
|
||||
|
||||
scene.add_object(Arc::new(Sphere::new(
|
||||
scene.add_object(RTObjectWrapper::new(Box::new(Sphere::new(
|
||||
Point3::new(0.2, -0.5, -0.2),
|
||||
0.5,
|
||||
Material::new(
|
||||
@ -66,9 +65,9 @@ fn main() {
|
||||
20.0,
|
||||
0.3,
|
||||
),
|
||||
)));
|
||||
))));
|
||||
|
||||
scene.add_object(Arc::new(Sphere::new(
|
||||
scene.add_object(RTObjectWrapper::new(Box::new(Sphere::new(
|
||||
Point3::new(-0.5, 0.5, -2.0),
|
||||
1.5,
|
||||
Material::new(
|
||||
@ -78,7 +77,7 @@ fn main() {
|
||||
20.0,
|
||||
0.3,
|
||||
),
|
||||
)));
|
||||
))));
|
||||
|
||||
let camera = Camera::new(
|
||||
Point3::new(0.0, 0.7, 5.0),
|
||||
|
||||
38
src/bin/rt_interp.rs
Normal file
38
src/bin/rt_interp.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use std::env;
|
||||
|
||||
use lispers::raytracer::lisp::mk_raytrace;
|
||||
use lispers_core::lisp::environment::EnvironmentLayer;
|
||||
use lispers_core::lisp::prelude::mk_prelude;
|
||||
use lispers_core::lisp::{eval, Environment};
|
||||
use lispers_core::parser::ExpressionStream;
|
||||
|
||||
fn main() {
|
||||
let program_paths: Vec<_> = env::args().skip(1).collect();
|
||||
let programs: Vec<_> = program_paths
|
||||
.iter()
|
||||
.map(|path| std::fs::read_to_string(path).unwrap())
|
||||
.collect();
|
||||
|
||||
let mut layer = EnvironmentLayer::new();
|
||||
mk_prelude(&mut layer);
|
||||
mk_raytrace(&mut layer);
|
||||
|
||||
let environment = Environment::from_layer(layer);
|
||||
|
||||
for (i, r) in
|
||||
ExpressionStream::from_char_stream(programs.iter().map(|p| p.chars()).flatten()).enumerate()
|
||||
{
|
||||
match r {
|
||||
Err(err) => {
|
||||
println!("ParserError in Expression {}: {:?}", i + 1, err);
|
||||
break;
|
||||
}
|
||||
Ok(expr) => match eval(&environment, expr) {
|
||||
Ok(_) => {}
|
||||
Err(e) => println!("Error evaluating Expression {}: {}", i + 1, e),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
println!("Interpreter Done!");
|
||||
}
|
||||
51
src/bin/rt_lisp_demo.rs
Normal file
51
src/bin/rt_lisp_demo.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use lispers::raytracer::lisp::mk_raytrace;
|
||||
use lispers_core::lisp::environment::EnvironmentLayer;
|
||||
use lispers_core::lisp::prelude::mk_prelude;
|
||||
use lispers_core::lisp::{eval, Environment};
|
||||
use lispers_core::parser::ExpressionStream;
|
||||
|
||||
fn main() {
|
||||
let programs = [
|
||||
"(set 'red (material (color 1 0 0) (color 1 0 0) (color 0.5 0 0) 50 0.25))",
|
||||
"(set 'blue (material (color 0 0 1) (color 0 0 1) (color 0 0 0.6) 50 0.25))",
|
||||
"(set 'green (material (color 0 1 0) (color 0 1 0) (color 0 0.6 0) 50 0.25))",
|
||||
"(set 'white (material (color 1 1 1) (color 1 1 1) (color 0.6 0.6 0.6) 100 0.5))",
|
||||
"(set 'black (material (color 0 0 0) (color 0 0 0) (color 0.6 0.6 0.6) 100 0.5))",
|
||||
"(set 's1 (sphere (point 0 1 0) 1 blue))",
|
||||
"(set 's2 (sphere (point 2 0.5 2) 0.5 green))",
|
||||
"(defun spiral-sphere (i n) (sphere (print (point (* 2 (cos (/ (* i 6.2) n))) 0.5 (* 2 (sin (/ (* i 6.2) n))))) 0.5 red))",
|
||||
"(defun spiral (scn i n) (if (< i n) (scene-add (spiral scn (+ i 1) n) (spiral-sphere i n)) scn))",
|
||||
"(set 'p1 (checkerboard (point 0 0 0) (vector 0 1 0) black white 0.5 (vector 0.5 0 1)))",
|
||||
"(set 'l1 (light (point 3 10 5) (color 1 1 1)))",
|
||||
"(set 'l2 (light (point 2 10 5) (color 1 1 1)))",
|
||||
"(set 'scn (scene (color 0.1 0.1 0.1) '(s1 s2 p1) '(l1 l2)))",
|
||||
"(set 'scn (spiral scn 0.0 10.0))",
|
||||
"(print scn)",
|
||||
"(set 'cam (camera (point 0 3 6) (point 0 0 0) (vector 0 1 0) 40 1920 1080))",
|
||||
"(render cam scn 5 4 \"rt-lisp-demo.png\")",
|
||||
];
|
||||
|
||||
let mut layer = EnvironmentLayer::new();
|
||||
mk_prelude(&mut layer);
|
||||
mk_raytrace(&mut layer);
|
||||
|
||||
let environment = Environment::from_layer(layer);
|
||||
|
||||
for r in ExpressionStream::from_char_stream(programs.iter().map(|p| p.chars()).flatten()) {
|
||||
match r {
|
||||
Err(err) => {
|
||||
println!("ParserError: {:?}", err);
|
||||
break;
|
||||
}
|
||||
Ok(expr) => {
|
||||
println!("Evaluating: {}", expr.clone());
|
||||
match eval(&environment, expr) {
|
||||
Ok(e) => println!("=> {}", e),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Interpreter Done!");
|
||||
}
|
||||
@ -1,3 +1 @@
|
||||
pub mod lisp;
|
||||
pub mod parser;
|
||||
pub mod raytracer;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
scene::Scene,
|
||||
types::{Color, Point3, Ray, Scalar, Vector3},
|
||||
@ -6,6 +8,7 @@ use image::RgbImage;
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// A camera that can render a scene.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Camera {
|
||||
/// Position of the camera's eye.
|
||||
position: Point3,
|
||||
@ -106,3 +109,16 @@ impl Camera {
|
||||
img
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Camera {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Camera {{ position: {}, lower_left: {}, x_dir: {}, y_dir: {}, width: {}, height: {} }}",
|
||||
self.position, self.lower_left, self.x_dir, self.y_dir, self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Camera {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
282
src/raytracer/lisp.rs
Normal file
282
src/raytracer/lisp.rs
Normal file
@ -0,0 +1,282 @@
|
||||
use crate::raytracer::{scene::Scene, types::Light};
|
||||
|
||||
use lispers_macro::{native_lisp_function, native_lisp_function_proxy};
|
||||
|
||||
use lispers_core::lisp::{
|
||||
environment::EnvironmentLayer,
|
||||
eval::{eval, EvalError},
|
||||
expression::ForeignDataWrapper,
|
||||
Environment, Expression,
|
||||
};
|
||||
|
||||
use super::{
|
||||
camera::Camera,
|
||||
plane::{Checkerboard, Plane},
|
||||
sphere::Sphere,
|
||||
types::{Color, Material, Point3, RTObjectWrapper, Vector3},
|
||||
};
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn point(x: f64, y: f64, z: f64) -> Result<ForeignDataWrapper<Point3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Point3::new(x, y, z)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn vector(x: f64, y: f64, z: f64) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Vector3::new(x, y, z)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn color(r: f64, g: f64, b: f64) -> Result<ForeignDataWrapper<Color>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Color::new(r, g, b)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn light(
|
||||
pos: ForeignDataWrapper<Point3>,
|
||||
col: ForeignDataWrapper<Color>,
|
||||
) -> Result<ForeignDataWrapper<Light>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Light::new(*pos, *col)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn material(
|
||||
amb: ForeignDataWrapper<Color>,
|
||||
dif: ForeignDataWrapper<Color>,
|
||||
spe: ForeignDataWrapper<Color>,
|
||||
shi: f64,
|
||||
mir: f64,
|
||||
) -> Result<ForeignDataWrapper<Material>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Material::new(
|
||||
*amb, *dif, *spe, shi, mir,
|
||||
)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn sphere(
|
||||
pos: ForeignDataWrapper<Point3>,
|
||||
rad: f64,
|
||||
mat: ForeignDataWrapper<Material>,
|
||||
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Sphere::new(*pos, rad, *mat))).into())
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn plane(
|
||||
pos: ForeignDataWrapper<Point3>,
|
||||
dir: ForeignDataWrapper<Vector3>,
|
||||
mat: ForeignDataWrapper<Material>,
|
||||
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Plane::new(*pos, *dir, *mat))).into())
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn checkerboard(
|
||||
pos: ForeignDataWrapper<Point3>,
|
||||
norm: ForeignDataWrapper<Vector3>,
|
||||
mat1: ForeignDataWrapper<Material>,
|
||||
mat2: ForeignDataWrapper<Material>,
|
||||
sca: f64,
|
||||
up: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
|
||||
Ok(
|
||||
ForeignDataWrapper::new(RTObjectWrapper::from(Checkerboard::new(
|
||||
*pos, *norm, *mat1, *mat2, sca, *up,
|
||||
)))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn scene(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||
let [amb, objs, lgts]: [Expression; 3] = expr.try_into()?;
|
||||
|
||||
let amb: ForeignDataWrapper<Color> = eval(env, amb)?.try_into()?;
|
||||
let objs: Vec<Expression> = eval(env, objs)?.try_into()?;
|
||||
let lgts: Vec<Expression> = eval(env, lgts)?.try_into()?;
|
||||
|
||||
let mut scene = Scene::new();
|
||||
|
||||
scene.set_ambient(*amb);
|
||||
for o in objs {
|
||||
let o: ForeignDataWrapper<RTObjectWrapper> = eval(env, o)?.try_into()?;
|
||||
scene.add_object(o.clone());
|
||||
}
|
||||
for l in lgts {
|
||||
let l: ForeignDataWrapper<Light> = eval(env, l)?.try_into()?;
|
||||
scene.add_light(*l);
|
||||
}
|
||||
|
||||
Ok(ForeignDataWrapper::new(scene).into())
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn scene_add_object(
|
||||
mut sce: ForeignDataWrapper<Scene>,
|
||||
obj: ForeignDataWrapper<RTObjectWrapper>,
|
||||
) -> Result<ForeignDataWrapper<Scene>, EvalError> {
|
||||
sce.add_object(obj.clone());
|
||||
Ok(sce)
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn scene_add_light(
|
||||
mut sce: ForeignDataWrapper<Scene>,
|
||||
lgt: ForeignDataWrapper<Light>,
|
||||
) -> Result<ForeignDataWrapper<Scene>, EvalError> {
|
||||
sce.add_light(*lgt);
|
||||
Ok(sce)
|
||||
}
|
||||
|
||||
native_lisp_function_proxy!(
|
||||
fname = scene_add,
|
||||
eval,
|
||||
dispatch = scene_add_object,
|
||||
dispatch = scene_add_light
|
||||
);
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn camera(
|
||||
pos: ForeignDataWrapper<Point3>,
|
||||
cnt: ForeignDataWrapper<Point3>,
|
||||
up: ForeignDataWrapper<Vector3>,
|
||||
fovy: f64,
|
||||
w: i64,
|
||||
h: i64,
|
||||
) -> Result<ForeignDataWrapper<Camera>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(Camera::new(
|
||||
*pos, *cnt, *up, fovy, w as usize, h as usize,
|
||||
)))
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn render(
|
||||
cam: ForeignDataWrapper<Camera>,
|
||||
sce: ForeignDataWrapper<Scene>,
|
||||
dpt: i64,
|
||||
sbp: i64,
|
||||
out: String,
|
||||
) -> Result<Expression, EvalError> {
|
||||
println!("Rendering to {}...", out);
|
||||
let img = cam.render(&sce, dpt as u32, sbp as u32);
|
||||
|
||||
match img.save(out) {
|
||||
Ok(_) => Ok(Expression::Nil),
|
||||
Err(e) => Err(EvalError::RuntimeError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn sin(x: f64) -> Result<f64, EvalError> {
|
||||
Ok(x.sin())
|
||||
}
|
||||
|
||||
#[native_lisp_function(eval)]
|
||||
pub fn cos(x: f64) -> Result<f64, EvalError> {
|
||||
Ok(x.cos())
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vadd_vv(
|
||||
a: ForeignDataWrapper<Vector3>,
|
||||
b: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*a + *b))
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vadd_vp(
|
||||
a: ForeignDataWrapper<Vector3>,
|
||||
b: ForeignDataWrapper<Point3>,
|
||||
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*b + *a))
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vadd_pv(
|
||||
a: ForeignDataWrapper<Point3>,
|
||||
b: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*a + *b))
|
||||
}
|
||||
|
||||
native_lisp_function_proxy!(
|
||||
fname = vadd,
|
||||
eval,
|
||||
dispatch = vadd_vv,
|
||||
dispatch = vadd_vp,
|
||||
dispatch = vadd_pv
|
||||
);
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vsub_vv(
|
||||
a: ForeignDataWrapper<Vector3>,
|
||||
b: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*a - *b))
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vsub_vp(
|
||||
a: ForeignDataWrapper<Vector3>,
|
||||
b: ForeignDataWrapper<Point3>,
|
||||
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*b - *a))
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vsub_pv(
|
||||
a: ForeignDataWrapper<Point3>,
|
||||
b: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*a - *b))
|
||||
}
|
||||
|
||||
native_lisp_function_proxy!(
|
||||
fname = vsub,
|
||||
eval,
|
||||
dispatch = vsub_vv,
|
||||
dispatch = vsub_vp,
|
||||
dispatch = vsub_pv
|
||||
);
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vmul_vs(
|
||||
a: ForeignDataWrapper<Vector3>,
|
||||
b: f64,
|
||||
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*a * b))
|
||||
}
|
||||
|
||||
#[native_lisp_function]
|
||||
pub fn vmul_sv(
|
||||
a: f64,
|
||||
b: ForeignDataWrapper<Vector3>,
|
||||
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
|
||||
Ok(ForeignDataWrapper::new(*b * a))
|
||||
}
|
||||
|
||||
native_lisp_function_proxy!(fname = vmul, eval, dispatch = vmul_vs, dispatch = vmul_sv);
|
||||
|
||||
/// Adds the raytracing functions to the given environment layer.
|
||||
pub fn mk_raytrace(layer: &mut EnvironmentLayer) {
|
||||
layer.set("point".to_string(), Expression::Function(point));
|
||||
layer.set("vector".to_string(), Expression::Function(vector));
|
||||
layer.set("color".to_string(), Expression::Function(color));
|
||||
layer.set("light".to_string(), Expression::Function(light));
|
||||
layer.set("material".to_string(), Expression::Function(material));
|
||||
layer.set("plane".to_string(), Expression::Function(plane));
|
||||
layer.set(
|
||||
"checkerboard".to_string(),
|
||||
Expression::Function(checkerboard),
|
||||
);
|
||||
layer.set("sphere".to_string(), Expression::Function(sphere));
|
||||
layer.set("scene".to_string(), Expression::Function(scene));
|
||||
layer.set("scene-add".to_string(), Expression::Function(scene_add));
|
||||
layer.set("camera".to_string(), Expression::Function(camera));
|
||||
layer.set("render".to_string(), Expression::Function(render));
|
||||
layer.set("sin".to_string(), Expression::Function(sin));
|
||||
layer.set("cos".to_string(), Expression::Function(cos));
|
||||
layer.set("vadd".to_string(), Expression::Function(vadd));
|
||||
layer.set("vsub".to_string(), Expression::Function(vsub));
|
||||
layer.set("vmul".to_string(), Expression::Function(vmul));
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod camera;
|
||||
pub mod lisp;
|
||||
pub mod plane;
|
||||
pub mod scene;
|
||||
pub mod sphere;
|
||||
|
||||
@ -3,6 +3,7 @@ use super::types::{Intersect, Material, Point3, Scalar, Vector3};
|
||||
extern crate nalgebra as na;
|
||||
|
||||
/// An infinite plane in 3D space.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Plane {
|
||||
/// The position of the plane.
|
||||
position: Point3,
|
||||
@ -13,6 +14,7 @@ pub struct Plane {
|
||||
}
|
||||
|
||||
/// A infinite checkerboard plane in 3D space.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Checkerboard {
|
||||
/// The base plane containing the "white" material
|
||||
base: Plane,
|
||||
@ -65,14 +67,14 @@ impl Checkerboard {
|
||||
}
|
||||
|
||||
impl Intersect for Plane {
|
||||
fn intersect<'a>(
|
||||
&'a self,
|
||||
fn intersect(
|
||||
&self,
|
||||
ray: &super::types::Ray,
|
||||
) -> Option<(
|
||||
Point3,
|
||||
Vector3,
|
||||
super::types::Scalar,
|
||||
&'a super::types::Material,
|
||||
super::types::Material,
|
||||
)> {
|
||||
let denom = self.normal.dot(&ray.direction);
|
||||
if denom != 0.0 {
|
||||
@ -81,7 +83,7 @@ impl Intersect for Plane {
|
||||
|
||||
if t > 1e-5 {
|
||||
let point = ray.origin + ray.direction * t;
|
||||
return Some((point, self.normal, t, &self.material));
|
||||
return Some((point, self.normal, t, self.material.clone()));
|
||||
}
|
||||
}
|
||||
None
|
||||
@ -89,14 +91,14 @@ impl Intersect for Plane {
|
||||
}
|
||||
|
||||
impl Intersect for Checkerboard {
|
||||
fn intersect<'a>(
|
||||
&'a self,
|
||||
fn intersect(
|
||||
&self,
|
||||
ray: &super::types::Ray,
|
||||
) -> Option<(
|
||||
Point3,
|
||||
Vector3,
|
||||
super::types::Scalar,
|
||||
&'a super::types::Material,
|
||||
super::types::Material,
|
||||
)> {
|
||||
if let Some((point, normal, t, material)) = self.base.intersect(ray) {
|
||||
let v3 = point - self.base.position;
|
||||
@ -105,12 +107,44 @@ impl Intersect for Checkerboard {
|
||||
if ((v2.x / self.scale).round() % 2.0 == 0.0)
|
||||
== ((v2.y / self.scale).round() % 2.0 == 0.0)
|
||||
{
|
||||
Some((point, normal, t, material))
|
||||
Some((point, normal, t, material.clone()))
|
||||
} else {
|
||||
Some((point, normal, t, &self.material_alt))
|
||||
Some((point, normal, t, self.material_alt.clone()))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Plane {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(plane position: {}, normal: {}, material: {})",
|
||||
self.position, self.normal, self.material
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Checkerboard {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(checkerboard position: {}, normal: {}, material1: {}, material2: {}, scale: {})",
|
||||
self.base.position, self.base.normal, self.base.material, self.material_alt, self.scale
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Plane {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Checkerboard {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::types::Color;
|
||||
use super::types::Intersect;
|
||||
use super::types::Light;
|
||||
use super::types::Material;
|
||||
use super::types::Point3;
|
||||
use super::types::RTObjectWrapper;
|
||||
use super::types::Ray;
|
||||
use super::types::Vector3;
|
||||
use super::vec::mirror;
|
||||
use super::vec::reflect;
|
||||
use std::sync::Arc;
|
||||
extern crate nalgebra as na;
|
||||
|
||||
/// A scene is a collection of objects and lights, and provides a method to trace a ray through the scene.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Scene {
|
||||
/// The ambient light of the scene
|
||||
ambient: Color,
|
||||
/// The objects in the scene
|
||||
objects: Vec<Arc<dyn Intersect + Send + Sync>>,
|
||||
objects: Vec<RTObjectWrapper>,
|
||||
/// The lights in the scene
|
||||
lights: Vec<Light>,
|
||||
}
|
||||
@ -36,7 +39,7 @@ impl Scene {
|
||||
}
|
||||
|
||||
/// Add an object to the scene
|
||||
pub fn add_object(&mut self, obj: Arc<dyn Intersect + Send + Sync>) {
|
||||
pub fn add_object(&mut self, obj: RTObjectWrapper) {
|
||||
self.objects.push(obj);
|
||||
}
|
||||
|
||||
@ -61,7 +64,7 @@ impl Scene {
|
||||
{
|
||||
Some((isect_pt, isect_norm, _, material)) => {
|
||||
// Lighting of material at the intersection point
|
||||
let color = self.lighting(-&ray.direction, material, isect_pt, isect_norm);
|
||||
let color = self.lighting(-&ray.direction, &material, isect_pt, isect_norm);
|
||||
|
||||
// Calculate reflections, if the material has mirror properties
|
||||
if material.mirror > 0.0 {
|
||||
@ -128,3 +131,21 @@ impl Scene {
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Scene {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Scene {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(scene ambient: {}, #objects: {}, #lights: {})",
|
||||
self.ambient,
|
||||
self.objects.len(),
|
||||
self.lights.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use super::types::{Intersect, Material, Point3, Ray, Scalar, Vector3};
|
||||
extern crate nalgebra as na;
|
||||
|
||||
/// A sphere in 3D space
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Sphere {
|
||||
/// Center of the sphere
|
||||
center: Point3,
|
||||
@ -27,7 +28,7 @@ impl Sphere {
|
||||
const EPSILON: Scalar = 1e-5;
|
||||
|
||||
impl Intersect for Sphere {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, &'a Material)> {
|
||||
fn intersect(&self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, Material)> {
|
||||
let co = ray.origin - self.center;
|
||||
|
||||
let a = ray.direction.dot(&ray.direction);
|
||||
@ -55,7 +56,7 @@ impl Intersect for Sphere {
|
||||
isect_pt,
|
||||
(isect_pt - self.center) / self.radius,
|
||||
t,
|
||||
&self.material,
|
||||
self.material.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -63,3 +64,19 @@ impl Intersect for Sphere {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Sphere {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(sphere center: {}, radius: {}, material: {})",
|
||||
self.center, self.radius, self.material
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Sphere {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use as_any::AsAny;
|
||||
|
||||
extern crate nalgebra as na;
|
||||
|
||||
/// The Scalar type to use for raytracing (f32 may result in acne effects)
|
||||
@ -16,10 +20,11 @@ pub trait Intersect {
|
||||
/// Otherwise the intersection point, a normal vector at the intersection point,
|
||||
/// the distance from the ray origin to the intersection point and
|
||||
/// the material of the object are returned.
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, &'a Material)>;
|
||||
fn intersect(&self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, Material)>;
|
||||
}
|
||||
|
||||
/// A point light source
|
||||
#[derive(Clone, Debug, PartialEq, Copy)]
|
||||
pub struct Light {
|
||||
/// Position of the light source
|
||||
pub position: Point3,
|
||||
@ -34,6 +39,12 @@ impl Light {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Light {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A ray with origin and direction
|
||||
pub struct Ray {
|
||||
/// Ray origin
|
||||
@ -50,6 +61,7 @@ impl Ray {
|
||||
}
|
||||
|
||||
/// A Material used for PHONG shading
|
||||
#[derive(Clone, Debug, PartialEq, Copy)]
|
||||
pub struct Material {
|
||||
/// Ambient color, aka color without direct or indirect light
|
||||
pub ambient_color: Color,
|
||||
@ -86,3 +98,145 @@ impl Material {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Material {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
//////// Display traits ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl Display for Light {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(light position: {}, color: {})",
|
||||
self.position, self.color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Material {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(material ambient_color: {}, diffuse_color: {}, specular_color: {}, shininess: {}, mirror: {})",
|
||||
self.ambient_color, self.diffuse_color, self.specular_color, self.shininess, self.mirror
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// RTWrapper ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A trait used for Objects, which can be stored inside of the Scene, are Intersectable and are ForeignData compatible.
|
||||
pub trait RTObject: Intersect + Display + Debug + AsAny + Sync + Send + 'static {
|
||||
/// Convert the object to a Box<dyn Any> allowing downcasts to Self
|
||||
fn as_any_box(self: Box<Self>) -> Box<dyn std::any::Any>;
|
||||
/// Explicitly compare the object with another RTObject for object safety
|
||||
fn eq_impl(&self, other: &dyn RTObject) -> bool;
|
||||
/// Explicitly clone the object for object safety
|
||||
fn clone_impl(&self) -> Box<dyn RTObject>;
|
||||
}
|
||||
|
||||
impl<T: Intersect + Display + Debug + PartialEq + Clone + Sync + Send + 'static> RTObject for T {
|
||||
fn as_any_box(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
fn eq_impl(&self, other: &dyn RTObject) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref::<T>() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn clone_impl(&self) -> Box<dyn RTObject> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for dyn RTObject {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.eq_impl(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// The RTObjectWrapper is a wrapper around a Box<dyn RTObject> to make it ForeignData compatible
|
||||
/// (not depending on the concrete type of the object).
|
||||
pub struct RTObjectWrapper(Box<dyn RTObject>);
|
||||
|
||||
impl RTObjectWrapper {
|
||||
/// Create a new RTObjectWrapper from a Box<dyn RTObject>
|
||||
pub fn new<T: RTObject>(value: Box<T>) -> RTObjectWrapper {
|
||||
RTObjectWrapper(value)
|
||||
}
|
||||
/// Create a new RTObjectWrapper from a RTObject
|
||||
pub fn from<T: RTObject>(value: T) -> RTObjectWrapper {
|
||||
RTObjectWrapper::new(Box::new(value))
|
||||
}
|
||||
/// Get the inner box as Box<dyn Any> allowing downcasts to the concrete type
|
||||
pub fn as_any_box(self) -> Box<dyn std::any::Any> {
|
||||
self.0.as_any_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RTObjectWrapper {
|
||||
fn clone(&self) -> Self {
|
||||
RTObjectWrapper(self.0.clone_impl())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RTObjectWrapper {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.0 == *other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RTObjectWrapper {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "RTObjectWrapper({})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RTObjectWrapper {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "RTObjectWrapper({:?})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Intersect for RTObjectWrapper {
|
||||
fn intersect(&self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, Material)> {
|
||||
self.0.intersect(ray)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RTObjectWrapper {
|
||||
fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rt_wrapper_expr_conversion() {
|
||||
use super::sphere::Sphere;
|
||||
use lispers_core::lisp::expression::{Expression, ForeignDataWrapper};
|
||||
let sphere = Sphere::new(
|
||||
Point3::new(0.0, 0.0, 0.0),
|
||||
1.0,
|
||||
Material::new(
|
||||
Color::new(0.0, 0.0, 0.0),
|
||||
Color::new(0.0, 0.0, 0.0),
|
||||
Color::new(0.0, 0.0, 0.0),
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
);
|
||||
|
||||
let sphere = RTObjectWrapper::new(Box::new(sphere));
|
||||
|
||||
let expr: Expression = ForeignDataWrapper::new(sphere.clone()).into();
|
||||
|
||||
let sphere2: ForeignDataWrapper<RTObjectWrapper> = expr.try_into().unwrap();
|
||||
|
||||
assert_eq!(sphere, *sphere2.0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user