make changes

This commit is contained in:
TriMill 2023-05-19 20:34:08 -04:00
parent 5a8ef55bb3
commit ee26b6133a
14 changed files with 585 additions and 449 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
/target
/pkg
/.vscode

86
Cargo.lock generated
View file

@ -191,9 +191,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.12.1"
version = "3.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
[[package]]
name = "bytemuck"
@ -261,6 +261,26 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "console_log"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
dependencies = [
"log",
"web-sys",
]
[[package]]
name = "const_panic"
version = "0.2.8"
@ -317,6 +337,26 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "cxgraph"
version = "0.1.0"
dependencies = [
"cfg-if",
"cgmath",
"console_error_panic_hook",
"console_log",
"encase",
"env_logger",
"log",
"pollster",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
"winit",
]
[[package]]
name = "d3d12"
version = "0.6.0"
@ -351,9 +391,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "encase"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ca07b3a7863d1bc1ddea8647d67394a0236fc5582035a343c4ef3a962ea62e"
checksum = "8fce2eeef77fd4a293a54b62aa00ac9daebfbcda4bf8998c5a815635b004aa1c"
dependencies = [
"cgmath",
"const_panic",
@ -363,18 +403,18 @@ dependencies = [
[[package]]
name = "encase_derive"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54141cb8a5d4ed9308fec79fb1cb9cd272fcd38aa8d1b1165bef654b18671161"
checksum = "0e520cde08cbf4f7cc097f61573ec06ce467019803de8ae82fb2823fa1554a0e"
dependencies = [
"encase_derive_impl",
]
[[package]]
name = "encase_derive_impl"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dbc65b49ac8078eee25dd0a341e2763974031008518d17b9c448bffc1025541"
checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c"
dependencies = [
"proc-macro2",
"quote",
@ -660,9 +700,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.143"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "libloading"
@ -1066,9 +1106,9 @@ checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2"
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
dependencies = [
"proc-macro2",
]
@ -1623,20 +1663,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wgpu_calc"
version = "0.1.0"
dependencies = [
"cgmath",
"encase",
"env_logger",
"log",
"pollster",
"regex",
"wgpu",
"winit",
]
[[package]]
name = "widestring"
version = "1.0.2"
@ -1817,9 +1843,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winit"
version = "0.28.5"
version = "0.28.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94c9651471cd576737671fbf7081edfea43de3e06846dd9bd4e49ea803c9f55f"
checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196"
dependencies = [
"android-activity",
"bitflags 1.3.2",
@ -1881,6 +1907,6 @@ dependencies = [
[[package]]
name = "xml-rs"
version = "0.8.7"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "699d0104bcdd7e7af6d093d6c6e2d0c479b8a129ee0d1023b31d2e0c71bfdda2"
checksum = "dc95a04ea24f543cd9be5aab44f963fa35589c99e18415c38fb2b17e133bf8d2"

View file

@ -1,16 +1,30 @@
[package]
name = "wgpu_calc"
name = "cxgraph"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cfg-if = "1"
wgpu = "0.16"
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
winit = "0.28"
cgmath = "0.18"
encase = { version = "0.6", features = ["cgmath"] }
regex = "1.8"
raw-window-handle = "0.5"
winit = "0.28"
log = "0.4"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
pollster = "0.3"
env_logger = "0.10"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.16", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = ["Document", "Window", "Element"]}

40
index.html Normal file
View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>cxgraph</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="main">
<div id="header">
<h1>CXGraph</h1>
</div>
<div id="content">
<div id="sidebar" class="split left">
<textarea id="srcinput"></textarea>
</div>
<div id="canvas_container" class="split right">
</div>
</div>
</div>
<script type="module">
import init, * as cxgraph from "./pkg/cxgraph.js";
await init();
await cxgraph.load_shader("plot(z) = z");
await cxgraph.redraw();
let canvas = document.getElementsByTagName("canvas")[0];
canvas.style.width = "100%";
canvas.style.height = "100%";
new ResizeObserver(async () => {
//console.log("resize");
//let width = canvas.clientWidth;
//let height = canvas.clientHeight;
//await cxgraph.resize(width, height);
//await cxgraph.redraw();
}).observe(canvas);
</script>
</body>
</html>

View file

@ -1,6 +1,4 @@
use std::{collections::HashMap, fmt::Write};
use regex::Regex;
use std::{collections::{HashMap, HashSet}, fmt::Write};
use crate::complex::{Complex, cxfn};
@ -10,11 +8,8 @@ use super::parser::{Expr, Stmt, UnaryOp, BinaryOp};
pub enum CompileError<'s> {
FmtError,
TypeError(&'s str),
ArgCount(&'s str),
UndefinedVar(&'s str),
GlobalReassignment(&'s str),
BuiltinReassignment(&'s str),
StandaloneDerivative(&'s str),
Reassignment(&'s str),
}
impl <'s> From<std::fmt::Error> for CompileError<'s> {
@ -55,9 +50,6 @@ thread_local! {
m.insert("gamma", ("c_gamma", Type::Function(1), None));
m
};
static RE_INVOKE: Regex = Regex::new(r"^invoke(\d+)$").unwrap();
static RE_ITER: Regex = Regex::new(r"^iter(\d+)$").unwrap();
}
#[derive(Clone, Copy)]
@ -66,33 +58,30 @@ pub enum Type {
Function(u32),
}
#[derive(Clone, PartialEq, Eq, Hash)]
enum Generate {
FunctionId { argc: u32, id: u32 },
Invoke { argc: u32 },
Iter { argc: u32 },
Derivative { func: String, nderiv: u32, argc: u32 },
#[derive(Clone, Copy)]
enum NameScope {
Local, Global, Builtin
}
enum NameInfo {
Local { id: u32 },
Global { ty: Type, cname: String },
Builtin { ty: Type, bname: &'static str },
Generated { ty: Type, gname: String, gen: Generate }
struct NameInfo<'s> {
scope: NameScope,
name: &'s str,
ty: Type
}
impl NameInfo {
pub fn get_compiled_name(&self) -> String {
match self {
NameInfo::Local { id } => format!("arg_{id}"),
NameInfo::Global { cname, .. } => cname.to_owned(),
NameInfo::Builtin { bname, .. } => (*bname).to_owned(),
NameInfo::Generated { gname, .. } => gname.to_owned(),
impl <'s> NameInfo<'s> {
pub fn get_cname(&self) -> String {
let name = self.name;
match (self.scope, self.ty) {
(NameScope::Local, _) => format!("arg_{name}"),
(NameScope::Global, Type::Number) => format!("VAR_{name}"),
(NameScope::Global, Type::Function(_)) => format!("func_{name}"),
(NameScope::Builtin, _) => name.to_owned(),
}
}
}
type LocalTable<'s> = HashMap<&'s str, u32>;
type LocalTable<'s> = HashSet<&'s str>;
type CompileResult<'s,T=()> = Result<T, CompileError<'s>>;
@ -101,16 +90,12 @@ pub fn compile<'s>(buf: &mut impl Write, stmts: &[Stmt<'s>]) -> CompileResult<'s
for stmt in stmts {
compiler.compile_stmt(stmt)?;
}
compiler.generate()?;
Ok(())
}
struct Compiler<'s, 'w, W> where W: Write {
buf: &'w mut W,
globals: HashMap<&'s str, Type>,
generate: HashMap<String, Generate>,
next_fn_id: u32,
}
impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
@ -118,8 +103,6 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
Self {
buf,
globals: HashMap::new(),
generate: HashMap::new(),
next_fn_id: 0,
}
}
@ -128,67 +111,136 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
//////////////////
fn compile_stmt(&mut self, stmt: &Stmt<'s>) -> CompileResult<'s> {
match stmt {
Stmt::AssignConst(name, expr) => {
if BUILTINS.with(|m| m.contains_key(name)) {
return Err(CompileError::BuiltinReassignment(name))
}
if self.globals.contains_key(name) {
return Err(CompileError::GlobalReassignment(name))
}
self.globals.insert(name, Type::Number);
write!(self.buf, "const VAR_{name} = ")?;
let locals = LocalTable::with_capacity(0);
self.compile_expr(&locals, expr)?;
write!(self.buf, ";")?;
}
Stmt::AssignFunc(name, args, expr) => {
if BUILTINS.with(|m| m.contains_key(name)) {
return Err(CompileError::BuiltinReassignment(name))
}
if self.globals.contains_key(name) {
return Err(CompileError::GlobalReassignment(name))
}
self.globals.insert(name, Type::Function(args.len() as u32));
write!(self.buf, "fn func_{name}(")?;
let mut locals = LocalTable::with_capacity(args.len());
for (i, arg) in args.iter().enumerate() {
write!(self.buf, "arg_{}:vec2f,", i)?;
locals.insert(arg, i as u32);
}
write!(self.buf, ")->vec2f{{return ")?;
self.compile_expr(&locals, expr)?;
write!(self.buf, ";}}")?;
}
}
let res = match stmt {
Stmt::Const { name, body } => self.stmt_const(name, body),
Stmt::Func { name, args, body } => self.stmt_func(name, args, body),
Stmt::Deriv { name, func } => self.stmt_deriv(name, func),
Stmt::Iter { name, func, count } => self.stmt_iter(name, func, *count),
};
writeln!(self.buf)?;
Ok(())
res
}
fn stmt_const(&mut self, name: &'s str, body: &Expr<'s>) -> CompileResult<'s> {
if self.name_info(name, None).is_some() {
return Err(CompileError::Reassignment(name))
}
self.globals.insert(name, Type::Number);
write!(self.buf, "const VAR_{name} = ")?;
let locals = LocalTable::with_capacity(0);
self.compile_expr(&locals, body)?;
write!(self.buf, ";")?;
Ok(())
}
fn stmt_func(&mut self, name: &'s str, args: &[&'s str], body: &Expr<'s>) -> CompileResult<'s> {
if self.name_info(name, None).is_some() {
return Err(CompileError::Reassignment(name))
}
self.globals.insert(name, Type::Function(args.len() as u32));
write!(self.buf, "fn func_{name}(")?;
let mut locals = LocalTable::with_capacity(args.len());
for arg in args {
write!(self.buf, "arg_{arg}:vec2f,")?;
locals.insert(arg);
}
write!(self.buf, ")->vec2f{{return ")?;
self.compile_expr(&locals, body)?;
write!(self.buf, ";}}")?;
Ok(())
}
fn stmt_deriv(&mut self, name: &'s str, func: &'s str) -> CompileResult<'s> {
if self.name_info(name, None).is_some() {
return Err(CompileError::Reassignment(name))
}
let Some(name_info) = self.name_info(func, None) else {
return Err(CompileError::UndefinedVar(name))
};
let Type::Function(argc) = name_info.ty else {
return Err(CompileError::TypeError(name))
};
let func_name = name_info.get_cname();
self.globals.insert(name, Type::Function(argc));
write!(self.buf, "fn func_{name}(")?;
for i in 0..argc {
write!(self.buf, "arg_{i}:vec2f,")?;
}
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
write!(self.buf, ")->vec2f{{\
let a = c_mul({func_name}(arg_0 + vec2( D_EPS, 0.0){args}), vec2( 0.25/D_EPS, 0.0));\
let b = c_mul({func_name}(arg_0 + vec2(-D_EPS, 0.0){args}), vec2(-0.25/D_EPS, 0.0));\
let c = c_mul({func_name}(arg_0 + vec2(0.0, D_EPS){args}), vec2(0.0, -0.25/D_EPS));\
let d = c_mul({func_name}(arg_0 + vec2(0.0, -D_EPS){args}), vec2(0.0, 0.25/D_EPS));\
return a + b + c + d;}}\
")?;
Ok(())
}
fn stmt_iter(&mut self, name: &'s str, func: &'s str, count: u32) -> CompileResult<'s> {
if self.name_info(name, None).is_some() {
return Err(CompileError::Reassignment(name))
}
let Some(name_info) = self.name_info(func, None) else {
return Err(CompileError::UndefinedVar(name))
};
let Type::Function(argc) = name_info.ty else {
return Err(CompileError::TypeError(name))
};
let func_name = name_info.get_cname();
self.globals.insert(name, Type::Function(argc));
write!(self.buf, "fn func_{name}(")?;
for i in 0..argc {
write!(self.buf, "arg_{i}:vec2f,")?;
}
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
write!(self.buf, ")->vec2f{{\
var r=arg_0;\
for(var i=0;i<{count};i++){{\
r={func_name}(r{args});\
}}\
return r;}}\
")?;
Ok(())
}
///////////////////
// Expressions //
///////////////////
fn compile_expr(&mut self, locals: &LocalTable<'s>, expr: &Expr<'s>) -> CompileResult<'s> {
match expr {
Expr::Number(z) => self.compile_number(*z),
Expr::Name(name) => self.compile_var(locals, name),
Expr::NameDeriv(name, _) => return Err(CompileError::StandaloneDerivative(name)),
Expr::Unary(op, arg) => self.compile_unary(locals, *op, arg),
Expr::Binary(op, lhs, rhs) => self.compile_binary(locals, *op, lhs, rhs),
Expr::FnCall { name, args, nderiv } => self.compile_fncall(locals, name, args, *nderiv),
Expr::Number(z) => self.expr_number(*z),
Expr::Name(name) => self.expr_var(locals, name),
Expr::Unary(op, arg) => self.expr_unary(locals, *op, arg),
Expr::Binary(op, lhs, rhs) => self.expr_binary(locals, *op, lhs, rhs),
Expr::FnCall(name, args) => self.expr_fncall(locals, name, args),
}
}
fn compile_number(&mut self, z: Complex) -> CompileResult<'s> {
fn expr_number(&mut self, z: Complex) -> CompileResult<'s> {
write!(self.buf, "vec2f({:?},{:?})", z.re, z.im)?;
Ok(())
}
fn compile_unary(&mut self, locals: &LocalTable<'s>, op: UnaryOp, arg: &Expr<'s>) -> CompileResult<'s> {
fn expr_unary(&mut self, locals: &LocalTable<'s>, op: UnaryOp, arg: &Expr<'s>) -> CompileResult<'s> {
let strings = unop_strings(op);
write!(self.buf, "{}", strings[0])?;
self.compile_expr(locals, arg)?;
@ -196,7 +248,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
Ok(())
}
fn compile_binary(&mut self, locals: &LocalTable<'s>, op: BinaryOp, lhs: &Expr<'s>, rhs: &Expr<'s>) -> CompileResult<'s> {
fn expr_binary(&mut self, locals: &LocalTable<'s>, op: BinaryOp, lhs: &Expr<'s>, rhs: &Expr<'s>) -> CompileResult<'s> {
let strings = binop_strings(op);
write!(self.buf, "{}", strings[0])?;
self.compile_expr(locals, lhs)?;
@ -206,24 +258,25 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
Ok(())
}
fn compile_var(&mut self, locals: &LocalTable<'s>, name: &'s str) -> CompileResult<'s> {
fn expr_var(&mut self, locals: &LocalTable<'s>, name: &'s str) -> CompileResult<'s> {
let Some(name_info) = self.name_info(name, Some(locals)) else {
return Err(CompileError::UndefinedVar(name))
};
self.compile_name(name, name_info)?;
if !matches!(name_info.ty, Type::Number) {
return Err(CompileError::TypeError(name))
}
write!(self.buf, "{}", name_info.get_cname())?;
Ok(())
}
fn compile_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>, nderiv: u32) -> CompileResult<'s> {
fn expr_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>) -> CompileResult<'s> {
let Some(name_info) = self.name_info(name, Some(locals)) else {
return Err(CompileError::UndefinedVar(name))
};
let compname = &name_info.get_compiled_name();
for i in 0..nderiv {
self.add_gen_deriv(&compname, i+1, args.len() as u32);
write!(self.buf, "deriv_")?;
if !matches!(name_info.ty, Type::Function(n) if n as usize == args.len()) {
return Err(CompileError::TypeError(name))
}
self.compile_name_fncall(name, name_info, args.len() as u32)?;
write!(self.buf, "{}", name_info.get_cname())?;
write!(self.buf, "(")?;
for arg in args {
self.compile_expr(locals, arg)?;
@ -237,196 +290,40 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
// Names //
/////////////
fn compile_name_fncall(&mut self, name: &'s str, name_info: NameInfo, argc: u32) -> CompileResult<'s> {
match name_info {
NameInfo::Local { .. } => return Err(CompileError::TypeError(name)),
NameInfo::Global { ty: Type::Function(c), cname } if c == argc => {
write!(self.buf, "{cname}")?;
}
NameInfo::Global { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
NameInfo::Global { .. } => return Err(CompileError::TypeError(name)),
NameInfo::Builtin { ty: Type::Function(c), bname } if c == argc => {
write!(self.buf, "{bname}")?;
}
NameInfo::Builtin { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
NameInfo::Builtin { .. } => return Err(CompileError::TypeError(name)),
NameInfo::Generated { ty: Type::Function(c), gname, gen } if c == argc => {
write!(self.buf, "{gname}")?;
self.generate.insert(gname, gen);
}
NameInfo::Generated { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
NameInfo::Generated { .. } => return Err(CompileError::TypeError(name)),
}
Ok(())
}
fn compile_name(&mut self, name: &str, name_info: NameInfo) -> CompileResult<'s> {
match name_info {
NameInfo::Local { id } => {
write!(self.buf, "arg_{id}")?;
},
NameInfo::Global { ty: Type::Function(c), cname } => {
self.add_gen_fid(format!("{cname}"), c);
write!(self.buf, "FID_{cname}")?;
},
NameInfo::Global { ty: Type::Number, .. } => {
write!(self.buf, "VAR_{name}")?;
},
NameInfo::Builtin { ty: Type::Function(c), bname } => {
self.add_gen_fid(bname.to_owned(), c);
write!(self.buf, "FID_{bname}")?;
},
NameInfo::Builtin { ty: Type::Number, bname } => {
write!(self.buf, "{bname}")?;
},
NameInfo::Generated { ty: Type::Function(c), gname, gen } => {
write!(self.buf, "FID_{gname}")?;
self.add_gen_fid(gname.to_owned(), c);
self.generate.insert(gname, gen);
},
NameInfo::Generated { ty: Type::Number, gname, gen } => {
write!(self.buf, "{gname}")?;
self.generate.insert(gname, gen);
},
}
Ok(())
}
fn name_info(&self, name: &'s str, locals: Option<&LocalTable<'s>>) -> Option<NameInfo> {
if let Some(locals) = locals {
if let Some(id) = locals.get(name) {
return Some(NameInfo::Local { id: *id })
if locals.contains(name) {
return Some(NameInfo { scope: NameScope::Local, name, ty: Type::Number });
}
}
if let Some(ty) = self.globals.get(name) {
return Some(NameInfo::Global { ty: *ty, cname: format!("func_{name}") })
if let Some(ty) = self.globals.get(name).copied() {
return Some(NameInfo { scope: NameScope::Global, name, ty })
}
if let Some((bname, ty, _)) = BUILTINS.with(|m| m.get(name).copied()) {
return Some(NameInfo::Builtin { ty, bname })
}
if let Some(caps) = RE_INVOKE.with(|re| re.captures(name)) {
if let Ok(n) = caps[1].parse() {
return Some(NameInfo::Generated {
ty: Type::Function(n + 1),
gname: format!("invoke{n}"),
gen: Generate::Invoke { argc: n },
})
}
}
if let Some(caps) = RE_ITER.with(|re| re.captures(name)) {
if let Ok(n) = caps[1].parse() {
return Some(NameInfo::Generated {
ty: Type::Function(n + 2),
gname: format!("iter{n}"),
gen: Generate::Iter { argc: n }
})
}
return Some(NameInfo { scope: NameScope::Builtin, name: bname, ty })
}
None
}
//////////////////
// Generation //
//////////////////
fn add_gen_fid(&mut self, name: String, argc: u32) {
self.generate.insert(name, Generate::FunctionId { argc, id: self.next_fn_id });
self.next_fn_id += 1;
}
fn add_gen_deriv(&mut self, name: &str, nderiv: u32, argc: u32) {
let func_name = "deriv_".repeat(nderiv as usize - 1) + name;
let deriv_name = "deriv_".repeat(nderiv as usize) + name;
self.generate.insert(deriv_name, Generate::Derivative { func: func_name, nderiv, argc });
}
fn generate(&mut self) -> CompileResult<'s> {
for (name, g) in self.generate.clone() {
match g {
Generate::FunctionId { id, .. } => {
write!(self.buf, "const FID_{name}=vec2f({id}.0,0.0);")?;
}
Generate::Invoke { argc } => self.generate_invoke(argc)?,
Generate::Iter { argc } => self.generate_iter(argc)?,
Generate::Derivative { func, nderiv, argc } => self.generate_derivative(func, nderiv, argc)?,
}
writeln!(self.buf)?;
}
Ok(())
}
fn generate_invoke(&mut self, argc: u32) -> CompileResult<'s> {
write!(self.buf, "fn invoke{argc}(func:vec2f,")?;
for i in 0..argc {
write!(self.buf, "arg_{i}:vec2f")?;
write!(self.buf, ",")?;
}
write!(self.buf, ")->vec2f{{switch i32(func.x){{")?;
for (name, g) in &self.generate {
if let Generate::FunctionId { argc: fargc, id } = g {
write!(self.buf, "case {id}")?;
if *id == 0 {
write!(self.buf, ",default")?;
}
write!(self.buf, "{{return {name}(")?;
let a = argc.min(*fargc);
for i in 0..a {
write!(self.buf, "arg_{i},")?;
}
for _ in a..*fargc {
write!(self.buf, "vec2f(0.0),")?;
}
write!(self.buf, ");}}")?;
}
}
write!(self.buf, "}};}}")?;
Ok(())
}
fn generate_iter(&mut self, argc: u32) -> CompileResult<'s> {
if !self.generate.contains_key(&format!("invoke{argc}")) {
self.generate.insert(format!("invoke{argc}"), Generate::Iter { argc });
self.generate_invoke(argc)?;
writeln!(self.buf)?;
}
write!(self.buf, "fn iter{argc}(func:vec2f,n:vec2f,")?;
for i in 0..argc {
write!(self.buf, "arg_{i}:vec2f")?;
write!(self.buf, ",")?;
}
write!(self.buf, ")->vec2f{{var result=arg_0;")?;
write!(self.buf, "for(var i=0;i<i32(n.x);i++){{result=invoke{argc}(func,result,")?;
for i in 1..argc {
write!(self.buf, "arg_{i},")?;
}
write!(self.buf, ");}}return result;}}")?;
Ok(())
}
fn generate_derivative(&mut self, func: String, nderiv: u32, argc: u32) -> CompileResult<'s> {
if let Some(f) = func.strip_suffix("deriv_") {
if !self.generate.contains_key(f) {
self.generate.insert(func.clone(), Generate::Derivative { func: f.to_owned(), nderiv: nderiv - 1, argc });
self.generate_derivative(f.to_owned(), nderiv - 1, argc)?;
}
}
write!(self.buf, "fn deriv_{func}(z:vec2f")?;
let mut args = String::new();
for i in 1..argc {
write!(self.buf, ",arg{i}:vec2f")?;
args += &format!(",arg{i}");
}
write!(self.buf, ")->vec2f{{")?;
write!(self.buf, "\
let a = c_mul({func}(z + vec2( D_EPS, 0.0){args}), vec2( 0.25/D_EPS, 0.0));\
let b = c_mul({func}(z + vec2(-D_EPS, 0.0){args}), vec2(-0.25/D_EPS, 0.0));\
let c = c_mul({func}(z + vec2(0.0, D_EPS){args}), vec2(0.0, -0.25/D_EPS));\
let d = c_mul({func}(z + vec2(0.0, -D_EPS){args}), vec2(0.0, 0.25/D_EPS));\
return a + b + c + d;}}\
")?;
Ok(())
}
// fn generate_iter(&mut self, argc: u32) -> CompileResult<'s> {
// if !self.generate.contains_key(&format!("invoke{argc}")) {
// self.generate.insert(format!("invoke{argc}"), Generate::Iter { argc });
// self.generate_invoke(argc)?;
// writeln!(self.buf)?;
// }
// write!(self.buf, "fn iter{argc}(func:vec2f,n:vec2f,")?;
// for i in 0..argc {
// write!(self.buf, "arg_{i}:vec2f")?;
// write!(self.buf, ",")?;
// }
// write!(self.buf, ")->vec2f{{var result=arg_0;")?;
// write!(self.buf, "for(var i=0;i<i32(n.x);i++){{result=invoke{argc}(func,result,")?;
// for i in 1..argc {
// write!(self.buf, "arg_{i},")?;
// }
// write!(self.buf, ");}}return result;}}")?;
// Ok(())
// }
}

View file

@ -20,10 +20,11 @@ fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex {
}
}
pub fn optimize<'s>(stmts: Vec<Stmt<'s>>) -> Vec<Stmt<'s>> {
pub fn optimize(stmts: Vec<Stmt>) -> Vec<Stmt> {
stmts.into_iter().map(|s| match s {
Stmt::AssignConst(a, expr) => Stmt::AssignConst(a, optimize_expr(expr)),
Stmt::AssignFunc(a, b, expr) => Stmt::AssignFunc(a, b, optimize_expr(expr)),
Stmt::Const { name, body } => Stmt::Const { name, body: optimize_expr(body) },
Stmt::Func { name, args, body } => Stmt::Func { name, args, body: optimize_expr(body) },
_ => s,
}).collect()
}
@ -41,17 +42,16 @@ fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
(u, v) => Expr::Binary(op, Box::new(u), Box::new(v))
}
},
Expr::FnCall { name, args, nderiv } => {
let args: Vec<Expr<'s>> = args.into_iter().map(|a| optimize_expr(a)).collect();
Expr::FnCall(name, args) => {
let args: Vec<Expr<'s>> = args.into_iter().map(optimize_expr).collect();
if let Some((_, Type::Function(argc), Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
if argc as usize == args.len() && nderiv == 0 {
if args.iter().all(|e| matches!(e, Expr::Number(_))) {
let ns: Vec<Complex> = args.into_iter().map(|a| match a { Expr::Number(n) => n, _ => unreachable!() }).collect();
return Expr::Number(func(&ns))
}
if argc as usize == args.len()
&& args.iter().all(|e| matches!(e, Expr::Number(_))) {
let ns: Vec<Complex> = args.into_iter().map(|a| match a { Expr::Number(n) => n, _ => unreachable!() }).collect();
return Expr::Number(func(&ns))
}
}
Expr::FnCall { name, args, nderiv }
Expr::FnCall(name, args)
}
Expr::Name(name) => {
if let Some((_, Type::Number, Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
@ -60,6 +60,6 @@ fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
e
}
}
_ => e,
Expr::Number(_) => e,
}
}

View file

@ -6,7 +6,8 @@ use super::{scanner::{Scanner, Token}, Position};
#[derive(Clone, Debug)]
pub enum ParseError<'s> {
UnexpectedTokenPrefix(Position, Token<'s>),
UnexpectedExpr(Position, Expr<'s>),
Unexpected(Position, Token<'s>),
Expected(Position, Token<'s>),
InvalidLValue(Position),
InvalidFunction(Expr<'s>),
@ -31,10 +32,10 @@ impl BinaryOp {
pub const fn precedence(self) -> (u32, u32) {
match self {
BinaryOp::Add => (10, 11),
BinaryOp::Sub => (10, 11),
BinaryOp::Mul => (20, 21),
BinaryOp::Div => (20, 21),
BinaryOp::Add
| BinaryOp::Sub => (10, 11),
BinaryOp::Mul
| BinaryOp::Div => (20, 21),
BinaryOp::Pow => (31, 30),
}
}
@ -65,16 +66,17 @@ impl UnaryOp {
pub enum Expr<'s> {
Number(Complex),
Name(&'s str),
NameDeriv(&'s str, u32),
Unary(UnaryOp, Box<Expr<'s>>),
Binary(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
FnCall { name: &'s str, args: Vec<Expr<'s>>, nderiv: u32 },
FnCall(&'s str, Vec<Expr<'s>>),
}
#[derive(Debug)]
pub enum Stmt<'s> {
AssignConst(&'s str, Expr<'s>),
AssignFunc(&'s str, Vec<&'s str>, Expr<'s>),
Const { name: &'s str, body: Expr<'s> },
Func { name: &'s str, args: Vec<&'s str>, body: Expr<'s> },
Deriv { name: &'s str, func: &'s str },
Iter { name: &'s str, func: &'s str, count: u32 }
}
pub struct Parser<'s> {
@ -113,21 +115,13 @@ impl <'s> Parser<'s> {
tok => if let Some(op) = UnaryOp::from_token(tok) {
Expr::Unary(op, Box::new(self.expr(op.precedence())?))
} else {
return Err(ParseError::UnexpectedTokenPrefix(pos, tok))
return Err(ParseError::Unexpected(pos, tok))
}
};
while let Some((_, tok)) = self.scanner.peek() {
expr = match tok {
Token::Equal | Token::RParen | Token::Newline | Token::Comma => break,
Token::Quote => {
self.scanner.next();
match expr {
Expr::Name(name) => Expr::NameDeriv(name, 1),
Expr::NameDeriv(name, nderiv) => Expr::NameDeriv(name, nderiv+1),
_ => return Err(ParseError::InvalidFunction(expr))
}
},
Token::Equal | Token::Colon | Token::RParen | Token::Newline | Token::Comma => break,
Token::LParen => {
self.scanner.next();
let mut args = Vec::new();
@ -142,8 +136,7 @@ impl <'s> Parser<'s> {
}
self.expect(Token::RParen)?;
match expr {
Expr::Name(name) => Expr::FnCall { name, args, nderiv: 0 },
Expr::NameDeriv(name, nderiv) => Expr::FnCall { name, args, nderiv },
Expr::Name(name) => Expr::FnCall(name, args),
_ => return Err(ParseError::InvalidFunction(expr)),
}
},
@ -169,6 +162,70 @@ impl <'s> Parser<'s> {
Ok(expr)
}
pub fn parse_stmt_equals(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
if self.scanner.peek().is_none() {
return Err(ParseError::UnexpectedEof)
}
let rhs = self.expr(0)?;
if self.scanner.peek().is_some() {
self.expect(Token::Newline)?;
}
match lhs {
Expr::Name(name) => Ok(Stmt::Const { name, body: rhs }),
Expr::FnCall(name, args) => {
let mut arg_names = Vec::with_capacity(args.len());
for arg in args {
if let Expr::Name(name) = arg {
arg_names.push(name);
}
}
Ok(Stmt::Func { name, args: arg_names, body: rhs })
}
_ => return Err(ParseError::InvalidLValue(lhs_pos))
}
}
pub fn parse_stmt_deriv(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
if self.scanner.peek().is_none() {
return Err(ParseError::UnexpectedEof)
}
let pos = self.scanner.peek().unwrap().0;
let f = self.expr(0)?;
match (lhs, f) {
(Expr::Name(name), Expr::Name(func)) => Ok(Stmt::Deriv { name, func }),
(Expr::Name(_), f) => Err(ParseError::UnexpectedExpr(pos, f)),
(lhs, _) => Err(ParseError::UnexpectedExpr(lhs_pos, lhs)),
}
}
pub fn parse_stmt_iter(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
let Expr::Name(name) = lhs else {
return Err(ParseError::UnexpectedExpr(lhs_pos, lhs))
};
if self.scanner.peek().is_none() {
return Err(ParseError::UnexpectedEof)
}
let pos = self.scanner.peek().unwrap().0;
let func = self.expr(0)?;
let Expr::Name(func) = func else {
return Err(ParseError::UnexpectedExpr(pos, func))
};
self.expect(Token::Comma)?;
if self.scanner.peek().is_none() {
return Err(ParseError::UnexpectedEof)
}
let pos = self.scanner.peek().unwrap().0;
let count = self.expr(0)?;
let Expr::Number(count) = count else {
return Err(ParseError::UnexpectedExpr(pos, count))
};
Ok(Stmt::Iter { name, func, count: count.re as u32 })
}
pub fn parse(&mut self) -> Result<Vec<Stmt<'s>>, ParseError<'s>> {
let mut stmts = Vec::new();
while self.scanner.peek().is_some() {
@ -183,30 +240,16 @@ impl <'s> Parser<'s> {
let lhs_pos = self.scanner.peek().unwrap().0;
let lhs = self.expr(0)?;
self.expect(Token::Equal)?;
if self.scanner.peek().is_none() {
return Err(ParseError::UnexpectedEof)
}
let rhs = self.expr(0)?;
if self.scanner.peek().is_some() {
self.expect(Token::Newline)?;
}
let stmt = match lhs {
Expr::Name(name) => Stmt::AssignConst(name, rhs),
Expr::FnCall { name, args, nderiv: 0 } => {
let mut arg_names = Vec::with_capacity(args.len());
for arg in args {
if let Expr::Name(name) = arg {
arg_names.push(name)
}
}
Stmt::AssignFunc(name, arg_names, rhs)
let stmt = match self.scanner.next() {
Some((_, Token::Equal)) => self.parse_stmt_equals(lhs_pos, lhs)?,
Some((_, Token::Colon)) => match self.scanner.next() {
Some((_, Token::Name("deriv"))) => self.parse_stmt_deriv(lhs_pos, lhs)?,
Some((_, Token::Name("iter"))) => self.parse_stmt_iter(lhs_pos, lhs)?,
Some((pos, tok)) => return Err(ParseError::Unexpected(pos, tok)),
None => return Err(ParseError::UnexpectedEof),
}
_ => return Err(ParseError::InvalidLValue(lhs_pos))
Some((pos, tok)) => return Err(ParseError::Unexpected(pos, tok)),
None => return Err(ParseError::UnexpectedEof),
};
stmts.push(stmt);

View file

@ -14,7 +14,7 @@ pub enum Token<'s> {
Caret,
Equal,
Comma,
Quote,
Colon,
LParen,
RParen,
Newline,
@ -94,7 +94,7 @@ impl <'s> Scanner<'s> {
'*' => Token::Star,
'/' => Token::Slash,
'^' => Token::Caret,
'\'' => Token::Quote,
':' => Token::Colon,
'=' => Token::Equal,
',' => Token::Comma,
'(' => Token::LParen,

4
src/lib.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod language;
pub mod renderer;
pub mod complex;
pub mod wasm;

View file

@ -1,26 +1,48 @@
use language::compile;
use renderer::run;
use winit::{event_loop::EventLoop, window::Window};
mod language;
mod renderer;
mod complex;
use cxgraph::{renderer::WgpuState, language::compile};
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
fn main() {
env_logger::init();
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.init();
let src = r#"
f(z,a,b,c) = (z-a)*(z-b)*(z-c)
g(z,a,b,c) = z - f(z,a,b,c)/f'(z,a,b,c)
plot(z) = iter4(g, 50, z/3, z, 1, -1)
f(z, c) = z^2 + c
g: iter f, 100
plot(z) = g(z, z)
"#;
let wgsl = compile(src).unwrap();
println!("{}", wgsl);
println!("{wgsl}");
let event_loop = EventLoop::new();
let window = Window::new(&event_loop).unwrap();
window.set_title("window");
pollster::block_on(run(event_loop, window, &wgsl));
}
async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
let size = window.inner_size();
let mut state = WgpuState::new(&window, size.into()).await;
state.load_shaders(code);
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
state.set_shading_intensity(0.01);
event_loop.run(move |event, _, control_flow| {
control_flow.set_wait();
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. }
=> control_flow.set_exit(),
Event::RedrawRequested(_)
=> state.redraw(),
Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
state.resize(size.into());
window.request_redraw();
}
_ => (),
}
});
}

View file

@ -2,36 +2,36 @@ use std::num::NonZeroU64;
use encase::{ShaderType, ShaderSize, UniformBuffer};
use wgpu::util::DeviceExt;
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}, dpi::PhysicalSize};
type Vec2u = cgmath::Vector2<u32>;
type Vec2f = cgmath::Vector2<f32>;
#[derive(ShaderType)]
struct Uniforms {
resolution: Vec2u,
bounds_min: Vec2f,
bounds_max: Vec2f,
shading_intensity: f32,
pub resolution: Vec2u,
pub bounds_min: Vec2f,
pub bounds_max: Vec2f,
pub shading_intensity: f32,
}
struct State {
pub struct WgpuState {
uniforms: Uniforms,
surface: wgpu::Surface,
device: wgpu::Device,
config: wgpu::SurfaceConfiguration,
render_pipeline: Option<wgpu::RenderPipeline>,
uniform_bind_group: wgpu::BindGroup,
uniform_bind_group_layout: wgpu::BindGroupLayout,
uniform_layout: wgpu::BindGroupLayout,
uniform_buffer: wgpu::Buffer,
queue: wgpu::Queue,
queue: wgpu::Queue
}
impl State {
async fn new(window: &Window) -> Self {
let size = window.inner_size();
impl WgpuState {
pub async fn new<W>(window: &W, size: (u32, u32)) -> Self
where W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
@ -43,19 +43,19 @@ impl State {
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
limits: wgpu::Limits::downlevel_webgl2_defaults(),
},
None
).await.unwrap();
).await.map_err(|e| e.to_string()).unwrap();
let format = surface.get_capabilities(&adapter).formats[0];
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
width: size.0,
height: size.1,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![format],
};
@ -69,7 +69,7 @@ impl State {
contents: &[0; Uniforms::SHADER_SIZE.get() as usize],
});
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
wgpu::BindGroupLayoutEntry {
@ -87,7 +87,7 @@ impl State {
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &uniform_bind_group_layout,
layout: &uniform_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 1,
@ -102,19 +102,27 @@ impl State {
// Done //
let uniforms = Uniforms {
resolution: size.into(),
bounds_min: [-0.0, -0.0].into(),
bounds_max: [ 0.0, 0.0].into(),
shading_intensity: 0.0,
};
Self {
uniforms,
surface,
device,
config,
device,
render_pipeline: None,
queue,
uniform_bind_group,
uniform_bind_group_layout,
uniform_layout,
uniform_buffer,
queue,
}
}
fn load_shaders(&mut self, userdefs: &str) {
pub fn load_shaders(&mut self, userdefs: &str) {
// Shaders //
let src = include_str!("shader.wgsl");
let src = src.replace("//INCLUDE//\n", userdefs);
@ -147,7 +155,7 @@ impl State {
let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&self.uniform_bind_group_layout],
bind_group_layouts: &[&self.uniform_layout],
push_constant_ranges: &[],
});
@ -165,7 +173,7 @@ impl State {
self.render_pipeline = Some(render_pipeline);
}
fn redraw(&self, uniforms: &Uniforms) {
pub fn redraw(&self) {
let frame = self.surface.get_current_texture().unwrap();
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
@ -192,45 +200,26 @@ impl State {
}
}
let mut uniform_buffer = UniformBuffer::new([0; Uniforms::SHADER_SIZE.get() as usize]);
uniform_buffer.write(uniforms).unwrap();
uniform_buffer.write(&self.uniforms).unwrap();
self.queue.write_buffer(&self.uniform_buffer, 0, &uniform_buffer.into_inner());
self.queue.submit(Some(encoder.finish()));
frame.present();
}
fn resize(&mut self, size: PhysicalSize<u32>) {
self.config.width = size.width;
self.config.height = size.height;
pub fn resize(&mut self, size: (u32, u32)) {
let size = (size.0.max(1).min(2048), size.1.max(1).min(2048));
self.config.width = size.0;
self.config.height = size.1;
self.surface.configure(&self.device, &self.config);
self.uniforms.resolution = size.into();
}
pub fn set_bounds(&mut self, min: (f32, f32), max: (f32, f32)) {
self.uniforms.bounds_min = min.into();
self.uniforms.bounds_max = max.into();
}
pub fn set_shading_intensity(&mut self, value: f32) {
self.uniforms.shading_intensity = value;
}
}
pub async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
let mut state = State::new(&window).await;
state.load_shaders(code);
let mut uniforms = Uniforms {
resolution: [window.inner_size().width, window.inner_size().height].into(),
bounds_min: [0.73, 0.65].into(),
bounds_max: [0.98, 0.9].into(),
shading_intensity: 0.001,
};
event_loop.run(move |event, _, control_flow| {
control_flow.set_wait();
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. }
=> control_flow.set_exit(),
Event::RedrawRequested(_)
=> state.redraw(&uniforms),
Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
uniforms.resolution = [size.width, size.height].into();
state.resize(size);
window.request_redraw()
}
Event::MainEventsCleared => {
//window.request_redraw();
}
_ => (),
}
});
}

77
src/wasm.rs Normal file
View file

@ -0,0 +1,77 @@
#![cfg(target_arch="wasm32")]
use crate::{renderer::WgpuState, language::compile};
use winit::{event_loop::EventLoop, window::Window};
use wasm_bindgen::prelude::*;
// wasm is single-threaded so there's no possibility of Bad happening
// due to mutable global state. this will be Some(..) once start runs
static mut WGPU_STATE: Option<WgpuState> = None;
fn with_state<F>(f: F)
where F: Fn(&mut WgpuState) {
let mut state = unsafe { WGPU_STATE.take().unwrap() };
f(&mut state);
unsafe { WGPU_STATE = Some(state) };
}
#[wasm_bindgen(start)]
pub async fn start() {
use winit::dpi::PhysicalSize;
use winit::platform::web::WindowExtWebSys;
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
let event_loop = EventLoop::new();
let window = Window::new(&event_loop).unwrap();
window.set_inner_size(PhysicalSize::new(1200, 1200));
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("canvas_container")?;
let canvas = web_sys::Element::from(window.canvas());
dst.append_child(&canvas).ok()?;
Some(())
}).expect("Couldn't append canvas to document body.");
window.set_title("window");
let size = window.inner_size();
let mut state = WgpuState::new(&window, size.into()).await;
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
state.set_shading_intensity(0.01);
unsafe { WGPU_STATE = Some(state) };
}
#[wasm_bindgen]
pub fn load_shader(src: &str) {
let wgsl = compile(src).unwrap();
with_state(|state| {
state.load_shaders(&wgsl);
state.redraw();
});
}
#[wasm_bindgen]
pub fn redraw() {
with_state(|state| {
state.redraw();
});
}
#[wasm_bindgen]
pub fn resize(width: u32, height: u32) {
with_state(|state| state.resize((width, height)));
}
#[wasm_bindgen]
pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
with_state(|state| state.set_bounds((min_x, min_y), (max_x, max_y)));
}
#[wasm_bindgen]
pub fn set_shading_intensity(value: f32) {
with_state(|state| state.set_shading_intensity(value));
}

37
style.css Normal file
View file

@ -0,0 +1,37 @@
* {
margin: 0;
padding: 0;
}
body {
height: 100%;
overflow: hidden;
}
#main {
min-height: 100vh;
display: flex;
flex-direction: column;
}
#header {
height: 50px;
}
#content {
display: flex;
flex: 1;
width: 100vw;
}
#sidebar {
flex: 1;
}
#canvas_container {
flex: 3;
}
canvas {
background-color: black;
}

View file

@ -1,15 +0,0 @@
```
f(z) = z^3 - 1
fp(z): deriv f(z)
n(z) = z - f(z)/fp(z)
plot(z): iter w = z, w = f(w), w, 100
```
unary ops: - / (neg recip)
binary ops: + - * / ^ (add sub mul div pow)
holomorphic fns: sqrt exp ln sin cos tan sinh cosh tanh asin acos atan asinh acosh atanh gamma
bad fns: re im abs abssq arg conj
higher order: iter deriv
constants: i tau e
type sigils: complex, $function, @integer