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