feat: implemented renderer

This commit is contained in:
Yandrik 2022-12-30 17:27:47 +01:00
commit 6011ee68b4
8 changed files with 643 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/term-render.iml" filepath="$PROJECT_DIR$/.idea/term-render.iml" />
</modules>
</component>
</project>

11
.idea/term-render.iml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

313
Cargo.lock generated Normal file
View File

@ -0,0 +1,313 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "matrixmultiply"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84"
dependencies = [
"rawpointer",
]
[[package]]
name = "mio"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "term-render"
version = "0.1.0"
dependencies = [
"anyhow",
"crossterm",
"ndarray",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "term-render"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.68"
crossterm = "0.25.0"
ndarray = "0.15.6"

285
src/main.rs Normal file
View File

@ -0,0 +1,285 @@
use crossterm::cursor::MoveTo;
use crossterm::style::Attribute::Dim;
use crossterm::style::Print;
use crossterm::terminal::{Clear, ClearType};
use crossterm::{execute, QueueableCommand};
use ndarray::{arr1, arr2, Array1, Array2};
use std::io::{stdout, Write};
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
const X_DRAW_SCALING_FACTOR: f32 = 2.8;
enum Dimension {
X,
Y,
Z,
}
struct Mesh {
points: Vec<Array1<f32>>,
edges: Vec<[usize; 2]>,
}
#[inline]
#[rustfmt::skip]
fn rotation_matrix(angle: f32, dimension: Dimension) -> ndarray::Array2<f32> {
let sin = angle.to_radians().sin();
let cos = angle.to_radians().cos();
match dimension {
Dimension::X => arr2(&[
[1., 0., 0.],
[0., cos, -sin],
[0., sin, cos]
]),
Dimension::Y => arr2(&[
[cos, 0., -sin],
[0., 1., 0.],
[sin, 0., cos]
]),
Dimension::Z => arr2(&[
[cos, -sin, 0.],
[sin, cos, 0.],
[0., 0., 1.]
])
}
}
#[inline]
#[rustfmt::skip]
fn scale(x: f32, y: f32, z: f32) -> ndarray::Array2<f32> {
arr2(&[
[x, 0., 0.],
[0., y, 0.],
[0., 0., z],
])
}
#[inline]
#[rustfmt::skip]
fn ortho_matrix() -> ndarray::Array2<f32> {
arr2(&[
[1., 0., 0.],
[0., 1., 0.],
])
}
fn plot_line(
origin: &[i32; 2],
destination: &[i32; 2],
out: &mut impl Write,
) -> anyhow::Result<()> {
assert!(origin[0] > 0 && destination[0] > 0);
let (origin, destination) = if origin[0] < destination[0] {
(origin, destination)
} else {
(destination, origin)
};
let (xdiff, ydiff) = (
destination[0].abs_diff(origin[0]),
destination[1].abs_diff(origin[1]),
);
if ydiff > xdiff {
plot_line_vertical(origin, destination, out)
} else {
plot_line_horizontal(origin, destination, out)
}
}
/// # CONTRACT
/// * origin is *left of* destination (`origin[0] <= destination[0]`)
/// * inclination from origin to destination fulfills `-1 <= incl <= 1`
/// * line is inside (u16::MAX x u16::MAX) space
fn plot_line_horizontal(
origin: &[i32; 2],
destination: &[i32; 2],
out: &mut impl Write,
) -> anyhow::Result<()> {
// assert!(destination[0] > origin[0]);
let incl = (destination[1] - origin[1]) as f32 / (destination[0] - origin[0]) as f32;
// assert!(((-1.)..=1.).contains(&incl));
for i in 0..(destination[0] - origin[0]) {
// CONTRACT: i is in u16 space
let x = (i + origin[0]) as u16;
// CONTRACT: line points are in u16 space
let y = ((incl * i as f32).round() + origin[1] as f32) as u16;
out.queue(MoveTo(x, y))?
.queue(match (incl < -0.4, incl < 0.4) {
(false, true) => Print("-"),
(false, false) => Print("\\"),
(true, true) => Print("/"),
_ => unreachable!(),
})?;
}
Ok(())
}
/// # CONTRACT
/// * origin is *left of* destination (`origin[0] <= destination[0]`)
/// * inclination from origin to destination fulfills `incl <= -1` or `incl >= 1`
/// * line is inside (u16::MAX x u16::MAX) space
fn plot_line_vertical(
origin: &[i32; 2],
destination: &[i32; 2],
out: &mut impl Write,
) -> anyhow::Result<()> {
let (origin, destination) = if origin[1] < destination[1] {
(origin, destination)
} else {
(destination, origin)
};
let incl = (destination[0] - origin[0]) as f32 / (destination[1] - origin[1]) as f32;
// assert!((..=(-1.)).contains(&incl) || (1.0..).contains(&incl));
for i in 0..destination[1] - origin[1] {
// CONTRACT: i is in u16 space
let y = (i + origin[1]) as u16;
// CONTRACT: line points are in u16 space
let x = ((incl * i as f32).round() + origin[0] as f32) as u16;
out.queue(MoveTo(x, y))?
.queue(match (incl > -2. && incl < 2., incl > 0.) {
(true, false) => Print("/"),
(true, true) => Print("\\"),
_ => Print("|"),
})?;
}
Ok(())
}
static ANGLE: Mutex<f32> = Mutex::new(0.);
fn draw_rotating_mesh(meshes: Vec<Mesh>) -> anyhow::Result<()> {
let mut angle = 0.;
loop {
// rotation and matmul
let rot_matrix = rotation_matrix(angle, Dimension::Z);
let cam_matrix = ortho_matrix();
let mut stdout = stdout();
stdout.queue(Clear(ClearType::All))?;
for mesh in &meshes {
let points = &mesh.points;
let edges = &mesh.edges;
let projected_points = points
.iter()
.map(|pt| scale(1.6, 1.6, 1.6).dot(pt))
.map(|pt| rotation_matrix((angle * 10.) % 360., Dimension::X).dot(&pt))
.map(|pt| rotation_matrix((angle * 2.) % 360., Dimension::Y).dot(&pt))
.map(|pt| rot_matrix.dot(&pt))
.map(|pt| cam_matrix.dot(&pt))
.map(|pt| pt + arr1(&[30., 30.])) // draw shift
.collect::<Vec<Array1<_>>>();
for edge in edges.iter() {
let origin = &projected_points[edge[0]];
let dest = &projected_points[edge[1]];
plot_line(
&[
(origin[0] * X_DRAW_SCALING_FACTOR).round() as i32,
origin[1] as i32,
],
&[(dest[0] * X_DRAW_SCALING_FACTOR) as i32, dest[1] as i32],
&mut stdout,
)?;
}
for pt in &projected_points {
let pt = pt;
stdout
.queue(MoveTo((pt[0] * X_DRAW_SCALING_FACTOR) as u16, pt[1] as u16))?
.queue(Print(""))?;
}
}
stdout.flush()?;
angle += 0.1;
angle %= 360.;
thread::sleep(Duration::from_millis(10));
}
Ok(())
}
fn main() -> anyhow::Result<()> {
let cube = vec![
arr1(&[-10., -10., 10.]),
arr1(&[-10., 10., 10.]),
arr1(&[-10., -10., -10.]),
arr1(&[-10., 10., -10.]),
arr1(&[10., -10., 10.]),
arr1(&[10., 10., 10.]),
arr1(&[10., -10., -10.]),
arr1(&[10., 10., -10.]),
arr1(&[-6., 0., 0.]),
arr1(&[6., 0., 0.]),
];
let edges = vec![
// left face
[0usize, 1],
[0, 2],
[1, 3],
[2, 3],
// right face
[4, 5],
[4, 6],
[5, 7],
[6, 7],
// center edges
[0, 8],
[3, 8],
[4, 9],
[7, 9],
];
let thing = Mesh {
points: vec![
arr1(&[0., 0., 6.]),
arr1(&[0., 0., -6.]),
arr1(&[0., 6., 0.]),
arr1(&[0., -6., 0.]),
arr1(&[6., 0., 0.]),
arr1(&[-6., 0., 0.]),
],
edges: vec![
[0, 2],
[0, 3],
[0, 4],
[0, 5],
[1, 2],
[1, 3],
[1, 4],
[1, 5],
[3, 4],
[3, 5],
[2, 4],
[2, 5],
],
};
draw_rotating_mesh(vec![
Mesh {
points: cube,
edges,
},
thing,
])?;
loop {}
unreachable!()
}