diff --git a/.gitignore b/.gitignore index ea8c4bf..b8b7bd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +/pkg +/.vscode diff --git a/Cargo.lock b/Cargo.lock index 5f4550e..429f045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index c73874b..00f0bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"]} diff --git a/index.html b/index.html new file mode 100644 index 0000000..521da46 --- /dev/null +++ b/index.html @@ -0,0 +1,40 @@ + + + + + + cxgraph + + + +
+ +
+ +
+
+
+
+ + + diff --git a/src/language/compiler.rs b/src/language/compiler.rs index 90054bc..c854fff 100644 --- a/src/language/compiler.rs +++ b/src/language/compiler.rs @@ -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 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>; @@ -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, - 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>, nderiv: u32) -> CompileResult<'s> { + fn expr_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec>) -> 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 { 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 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 Complex { } } -pub fn optimize<'s>(stmts: Vec>) -> Vec> { +pub fn optimize(stmts: Vec) -> Vec { 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> = args.into_iter().map(|a| optimize_expr(a)).collect(); + Expr::FnCall(name, args) => { + let args: Vec> = 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 = 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 = 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, } } diff --git a/src/language/parser.rs b/src/language/parser.rs index 1cfba2c..f507ea5 100644 --- a/src/language/parser.rs +++ b/src/language/parser.rs @@ -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>), Binary(BinaryOp, Box>, Box>), - FnCall { name: &'s str, args: Vec>, nderiv: u32 }, + FnCall(&'s str, Vec>), } #[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, 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, 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, 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>, 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); diff --git a/src/language/scanner.rs b/src/language/scanner.rs index bfe0753..907e3d7 100644 --- a/src/language/scanner.rs +++ b/src/language/scanner.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e633d0b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod language; +pub mod renderer; +pub mod complex; +pub mod wasm; diff --git a/src/main.rs b/src/main.rs index 6e822e7..f555acd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); + } + _ => (), + } + }); } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 050cdb9..ce9970a 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -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; type Vec2f = cgmath::Vector2; #[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, 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(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) { - 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(); - } - _ => (), - } - }); -} diff --git a/src/wasm.rs b/src/wasm.rs new file mode 100644 index 0000000..da498f5 --- /dev/null +++ b/src/wasm.rs @@ -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 = None; + +fn with_state(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)); +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..ae0e2cb --- /dev/null +++ b/style.css @@ -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; +} diff --git a/syntax.md b/syntax.md deleted file mode 100644 index c370dbc..0000000 --- a/syntax.md +++ /dev/null @@ -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