made changes
This commit is contained in:
parent
36e2f03894
commit
4fcd317390
18 changed files with 528 additions and 230 deletions
|
@ -1,8 +1,8 @@
|
|||
[workspace]
|
||||
|
||||
members = [
|
||||
"libcxgraph",
|
||||
"cxgraph-desktop",
|
||||
"cxgraph-web",
|
||||
"libcxgraph",
|
||||
"cxgraph-desktop",
|
||||
"cxgraph-web",
|
||||
]
|
||||
|
||||
|
|
8
README.md
Normal file
8
README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# cxgraph
|
||||
|
||||
cxgraph is a complex function graphing tool built around WebGPU available
|
||||
[on the web](https://cx.trimill.xyz/) or (slightly) for desktop.
|
||||
|
||||
## documentation
|
||||
- [language](docs/language.md)
|
||||
- [web interface](docs/web.md)
|
|
@ -21,10 +21,10 @@ fn main() {
|
|||
}
|
||||
|
||||
async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
|
||||
let size = window.inner_size();
|
||||
let size = window.inner_size();
|
||||
let mut state = WgpuState::new(&window, size.into()).await;
|
||||
|
||||
state.load_shaders(code);
|
||||
state.load_shaders(code);
|
||||
|
||||
state.uniforms.bounds_min = (-5.0, -5.0).into();
|
||||
state.uniforms.bounds_max = ( 5.0, 5.0).into();
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
<div class="canvas-container">
|
||||
<canvas id="canvas"></canvas>
|
||||
<svg id="overlay">
|
||||
<line id="overlay_axis_x" x1="0" y1="0" x2="0" y2="0" stroke="#0006" stroke-width="1.5" visibility="hidden" />
|
||||
<line id="overlay_axis_y" x1="0" y1="0" x2="0" y2="0" stroke="#0006" stroke-width="1.5" visibility="hidden" />
|
||||
<line id="svg_axis_x" x1="0" y1="0" x2="0" y2="0" stroke="#0006" stroke-width="1.5" visibility="hidden" />
|
||||
<line id="svg_axis_y" x1="0" y1="0" x2="0" y2="0" stroke="#0006" stroke-width="1.5" visibility="hidden" />
|
||||
<circle id="svg_unitcircle" cx="0" cy="0" r="0" stroke="#0006" fill="none" stroke-width="1.5" visibility="hidden" />
|
||||
<g id="svg_point_template" visibility="hidden">
|
||||
<circle cx="0" cy="0" r="15" stroke="none" fill="#6664" />
|
||||
<circle cx="0" cy="0" r="5" stroke="none" fill="#6666" />
|
||||
|
@ -39,7 +40,7 @@
|
|||
<summary>Options</summary>
|
||||
<div>
|
||||
<input type="button" id="button_reset_view" value="Reset view">
|
||||
<input type="button" id="button_help" value="Help" onclick="window.open('docs.html')">
|
||||
<input type="button" id="button_help" value="Help" onclick="window.open('https://g.trimill.xyz/trimill/cxgraph')">
|
||||
</div>
|
||||
<div>
|
||||
<div><label for="range_resolution">Resolution</label></div>
|
||||
|
@ -91,9 +92,13 @@
|
|||
<div>
|
||||
Overlay
|
||||
<div>
|
||||
<input type="checkbox" id="overlay_axes" checked>
|
||||
<input type="checkbox" id="overlay_axes">
|
||||
<label for="overlay_axes">Draw axes</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="overlay_unitcircle">
|
||||
<label for="overlay_unitcircle">Draw unit circle</label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ let mouseY = 0.0;
|
|||
let mousePressed = false;
|
||||
|
||||
function remap(x, lo1, hi1, lo2, hi2) {
|
||||
return lo2 + (hi2 - lo2) * (x - lo1) / (hi1 - lo1);
|
||||
return lo2 + (hi2 - lo2) * (x - lo1) / (hi1 - lo1);
|
||||
}
|
||||
|
||||
function cxToScreen(cx) {
|
||||
|
@ -70,15 +70,25 @@ function onViewChange() {
|
|||
let bounds = calcBounds();
|
||||
cxgraph.set_bounds(bounds.x_min, bounds.y_min, bounds.x_max, bounds.y_max);
|
||||
let origin = cxToScreen({ re: 0, im: 0 });
|
||||
let one = cxToScreen({ re: 1, im: 0 });
|
||||
|
||||
overlay_axis_x.setAttribute("x1", 0)
|
||||
overlay_axis_x.setAttribute("x2", dim.w);
|
||||
overlay_axis_x.setAttribute("y1", origin.y);
|
||||
overlay_axis_x.setAttribute("y2", origin.y);
|
||||
overlay_axis_y.setAttribute("x1", origin.x);
|
||||
overlay_axis_y.setAttribute("x2", origin.x);
|
||||
overlay_axis_y.setAttribute("y1", 0);
|
||||
overlay_axis_y.setAttribute("y2", dim.h);
|
||||
if(svg_axis_x.visibility != "hidden") {
|
||||
svg_axis_x.setAttribute("x1", 0)
|
||||
svg_axis_x.setAttribute("x2", dim.w);
|
||||
svg_axis_x.setAttribute("y1", origin.y);
|
||||
svg_axis_x.setAttribute("y2", origin.y);
|
||||
}
|
||||
if(svg_axis_y.visibility != "hidden") {
|
||||
svg_axis_y.setAttribute("x1", origin.x);
|
||||
svg_axis_y.setAttribute("x2", origin.x);
|
||||
svg_axis_y.setAttribute("y1", 0);
|
||||
svg_axis_y.setAttribute("y2", dim.h);
|
||||
}
|
||||
if(svg_unitcircle.visibility != "hidden") {
|
||||
svg_unitcircle.setAttribute("cx", origin.x);
|
||||
svg_unitcircle.setAttribute("cy", origin.y);
|
||||
svg_unitcircle.setAttribute("r", one.x - origin.x);
|
||||
}
|
||||
|
||||
for(let point of graphPoints) {
|
||||
point.onViewChange();
|
||||
|
@ -148,7 +158,7 @@ function onGraph() {
|
|||
redraw();
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
div_error_msg.textContent = e.toString();
|
||||
div_error_msg.textContent = e.toString().replace("\n", "\r\n");
|
||||
div_error_msg.hidden = false;
|
||||
}
|
||||
}
|
||||
|
@ -214,10 +224,16 @@ let specialChars = new RegExp(
|
|||
console.log(specialChars);
|
||||
|
||||
source_text.addEventListener("input", () => {
|
||||
let e = source_text.selectionEnd;
|
||||
let amnt = 0;
|
||||
source_text.value = source_text.value.replace(
|
||||
specialChars,
|
||||
(_m, p) => charMap[p]
|
||||
)
|
||||
(m, p) => {
|
||||
amnt += m.length - charMap[p].length;
|
||||
return charMap[p];
|
||||
}
|
||||
);
|
||||
source_text.selectionEnd = e - amnt;
|
||||
});
|
||||
|
||||
//
|
||||
|
@ -276,8 +292,13 @@ nameColorMode[0].checked = true;
|
|||
|
||||
overlay_axes.addEventListener("change", () => {
|
||||
let vis = overlay_axes.checked ? "visible" : "hidden";
|
||||
overlay_axis_x.setAttribute("visibility", vis);
|
||||
overlay_axis_y.setAttribute("visibility", vis);
|
||||
svg_axis_x.setAttribute("visibility", vis);
|
||||
svg_axis_y.setAttribute("visibility", vis);
|
||||
});
|
||||
|
||||
overlay_unitcircle.addEventListener("change", () => {
|
||||
let vis = overlay_unitcircle.checked ? "visible" : "hidden";
|
||||
svg_unitcircle.setAttribute("visibility", vis);
|
||||
});
|
||||
|
||||
//
|
||||
|
|
|
@ -42,7 +42,7 @@ pub async fn start() {
|
|||
.build(&event_loop)
|
||||
.expect("Failed to build window");
|
||||
|
||||
let size = window.inner_size();
|
||||
let size = window.inner_size();
|
||||
let mut state = WgpuState::new(&window, size.into()).await;
|
||||
state.uniforms.bounds_min = (-5.0, -5.0).into();
|
||||
state.uniforms.bounds_max = ( 5.0, 5.0).into();
|
||||
|
@ -113,5 +113,5 @@ pub fn set_variable(idx: usize, re: f32, im: f32) {
|
|||
with_state(|state| {
|
||||
state.uniforms.variables[idx*2 + 0] = re;
|
||||
state.uniforms.variables[idx*2 + 1] = im;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -71,13 +71,14 @@ summary {
|
|||
}
|
||||
|
||||
#source_text {
|
||||
width: 300px;
|
||||
height: 500px;
|
||||
width: 400px;
|
||||
height: 150px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#div_error_msg {
|
||||
color: #f9a;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
input {
|
||||
|
|
165
docs/language.md
Normal file
165
docs/language.md
Normal file
|
@ -0,0 +1,165 @@
|
|||
# cxgraph language
|
||||
|
||||
cxgraph uses a custom expression language that is compiled to WGSL.
|
||||
|
||||
## names
|
||||
|
||||
names must begin with any alphabetic character (lowercase or capital letters, Greek letters,
|
||||
etc.) and may contain alphanumeric chararcters as well as underscores (`_`) and apostrophes (`'`).
|
||||
the words `sum`, `prod`, and `iter` may not be used for names. names may refer to either
|
||||
functions or variables.
|
||||
|
||||
examples of names include:
|
||||
```
|
||||
a A aaa ω z_3 __5__ f' К'םαl'けx焼__검
|
||||
```
|
||||
|
||||
names may either be **built-in**, **global**, or **local**. global or local names may shadow
|
||||
built-in names, and local names may shadow global ones.
|
||||
|
||||
## declarations
|
||||
|
||||
a **function declaration** declares a new function. functions may have zero or more arguments.
|
||||
|
||||
```
|
||||
f(x) = 3
|
||||
```
|
||||
|
||||
a **constant declaration** declares a new constant.
|
||||
|
||||
```
|
||||
n = 5
|
||||
```
|
||||
|
||||
declarations are separated by newlines. declarations may only reference functions and constants in
|
||||
declarations that precede them. the name used in a declaration (`f` and `n` in the above examples)
|
||||
is in the global scope
|
||||
|
||||
## built-ins
|
||||
|
||||
|
||||
arithmetic functions:
|
||||
| name | description |
|
||||
|---------------|-------------------------------------------------------|
|
||||
| `pos(z)` | equivalent to unary `+` |
|
||||
| `neg(z)` | equivalent to unary `-` |
|
||||
| `conj(z)` | complex conjugate, equivalent to unary `*` |
|
||||
| `re(z)` | real part |
|
||||
| `im(z)` | imaginary part |
|
||||
| `abs(z)` | absolute value (distance from `0`) |
|
||||
| `abs_sq(z)` | square of absolute value |
|
||||
| `arg(z)` | argument (angle about `0`) in the range `(-τ/2, τ/2]` |
|
||||
| `argbr(z,br)` | argument in the range `(-τ/2, τ/2] + br` |
|
||||
| `add(z,w)` | equivalent to `z + w` |
|
||||
| `sub(z,w)` | equivalent to `z - w` |
|
||||
| `mul(z,w)` | equivalent to `z * w` |
|
||||
| `div(z,w)` | equivalent to `z / w` |
|
||||
| `recip(z)` | reciprocal, equivalent to `1/z` |
|
||||
|
||||
power/exponential functions:
|
||||
| name | description |
|
||||
|---------------|-------------------------------------------|
|
||||
| `exp(z)` | exponential function, equivalent to `e^z` |
|
||||
| `log(z)` | logarithm base `e` |
|
||||
| `logbr(z,br)` | logarithm base `e` with specified branch |
|
||||
| `pow(z)` | power, equivalent to `^` |
|
||||
| `powbr(z,br)` | `pow` with specified branch |
|
||||
| `sqrt(z)` | square root, equivalent to `z^0.5` |
|
||||
| `sqrtbr(z,br)`| square root with specified branch |
|
||||
| `cbrt(z)` | cube root, equivalent to `z^0.5` |
|
||||
| `cbrtbr(z,br)`| cube root with specified branch |
|
||||
|
||||
trigonometric functions:
|
||||
| name | description |
|
||||
|------------|-------------------------------------|
|
||||
| `sin(z)` | sine function |
|
||||
| `cos(z)` | cosine function |
|
||||
| `tan(z)` | tangent function |
|
||||
| `sinh(z)` | hyperbolic sine function |
|
||||
| `cosh(z)` | hyperbolic cosine function |
|
||||
| `tanh(z)` | hyperbolic tangent function |
|
||||
| `asin(z)` | inverse sine function |
|
||||
| `acos(z)` | inverse cosine function |
|
||||
| `atan(z)` | inverse tangent function |
|
||||
| `asinh(z)` | inverse hyperbolic sine function |
|
||||
| `acosh(z)` | inverse hyperbolic cosine function |
|
||||
| `atanh(z)` | inverse hyperbolic tangent function |
|
||||
|
||||
special functions:
|
||||
| function | description |
|
||||
|--------------------------|--------------------------------------------------------------------|
|
||||
| `gamma(z)`, `Γ(z)` | [gamma function](https://en.wikipedia.org/wiki/Gamma_function) |
|
||||
| `invgamma(z)`, `invΓ(z)` | reciprocal of the gamma function |
|
||||
| `loggamma(z)`, `logΓ(z)` | logarithm of the gamma function |
|
||||
| `digamma(z)`, `ψ(z)` | [digamma function](https://en.wikipedia.org/wiki/Digamma_function) |
|
||||
|
||||
logic functions:
|
||||
| function | description |
|
||||
|-----------------|----------------------------------------------------------------------------|
|
||||
| `signre(z)` | sign of real part (1 if `re(z) > 0`, -1 if `re(z) < 0`, 0 if `re(z) == 0`) |
|
||||
| `signim(z)` | sign of imaginary part |
|
||||
| `ifgt(p,q,z,w)` | evaluates to `z` if `re(p) > re(q)`, otherwise `w` |
|
||||
| `iflt(p,q,z,w)` | evaluates to `z` if `re(p) < re(q)`, otherwise `w` |
|
||||
| `ifge(p,q,z,w)` | evaluates to `z` if `re(p) ≥ re(q)`, otherwise `w` |
|
||||
| `ifle(p,q,z,w)` | evaluates to `z` if `re(p) ≤ re(q)`, otherwise `w` |
|
||||
| `ifeq(p,q,z,w)` | evaluates to `z` if `re(p) = re(q)`, otherwise `w` |
|
||||
| `ifne(p,q,z,w)` | evaluates to `z` if `re(p) ≠ re(q)`, otherwise `w` |
|
||||
| `ifnan(p,z,w)` | evaluates to `z` if `p` is `NaN`, otherwise `w` |
|
||||
|
||||
constants:
|
||||
| name | description |
|
||||
|----------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `i` | the imaginary constant, equal to `sqrt(-1)` |
|
||||
| `e` | the [exponential constant](https://en.wikipedia.org/wiki/E_(mathematical_constant)), equal to `exp(1)` |
|
||||
| `tau`, `τ` | the [circle constant](https://tauday.com/tau-manifesto) |
|
||||
| `emgamma`, `γ` | the [Euler-Mascheroni](https://en.wikipedia.org/wiki/Euler%27s_constant) constant, equal to `-ψ(1)` |
|
||||
| `phi`, `φ` | the [golden ratio](https://en.wikipedia.org/wiki/Golden_ratio), equal to `1/2 + sqrt(5)/2` |
|
||||
|
||||
## ebnf grammar
|
||||
|
||||
```
|
||||
Program := Definitions
|
||||
|
||||
Definitions := NEWLINE* (Definition NEWLINE+)* Definition?
|
||||
|
||||
Definition := NAME "(" (NAME ",") NAME? ")" "=" Exprs
|
||||
| NAME "=" Exprs
|
||||
|
||||
Exprs := (Expr ",")* Expr ","?
|
||||
|
||||
Expr := Store
|
||||
|
||||
Store := Store "->" NAME | Sum
|
||||
|
||||
Sum := Sum "+" Product
|
||||
| Sum "-" Product
|
||||
| Product
|
||||
|
||||
Product := Product "*" Unary
|
||||
| Product "/" Unary
|
||||
| Unary
|
||||
|
||||
Unary := "+" Unary
|
||||
| "-" Unary
|
||||
| "*" Unary
|
||||
| Juxtapose Power
|
||||
| Power
|
||||
|
||||
Juxtapose := Juxtapose PreJuxtapose | PreJuxtapose
|
||||
|
||||
Power := FnCall "^" Unary | FnCall
|
||||
|
||||
FnCall := NAME "(" Exprs ")" | Item
|
||||
|
||||
PreJuxtapose := Number | "(" <Expr> ")"
|
||||
|
||||
Item := Number
|
||||
| NAME
|
||||
| "(" Expr ")"
|
||||
| "{" Exprs "}"
|
||||
| "sum" "(" NAME ":" INT "," INT ")" "{" Exprs "}"
|
||||
| "prod" "(" NAME ":" INT "," INT ")" "{" Exprs "}"
|
||||
| "iter" "(" INT "," NAME ":" Expr ")" "{" Exprs "}"
|
||||
|
||||
Number = FLOAT | INT
|
||||
```
|
0
docs/web.md
Normal file
0
docs/web.md
Normal file
|
@ -1,3 +1,3 @@
|
|||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
lalrpop::process_root().unwrap();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ pub enum UnaryOp {
|
|||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ExpressionType<'a> {
|
||||
Block,
|
||||
Number(Complex),
|
||||
Name(&'a str),
|
||||
Binary(BinaryOp),
|
||||
|
@ -32,6 +33,10 @@ pub struct Expression<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Expression<'a> {
|
||||
pub fn new_block(exs: Vec<Expression<'a>>) -> Self {
|
||||
Self { ty: ExpressionType::Block, children: exs }
|
||||
}
|
||||
|
||||
pub fn new_number(x: f64) -> Self {
|
||||
Self { ty: ExpressionType::Number(Complex::new(x, 0.0)), children: Vec::with_capacity(0) }
|
||||
}
|
||||
|
@ -87,6 +92,7 @@ pub enum Definition<'a> {
|
|||
fn display_expr(w: &mut impl fmt::Write, expr: &Expression, depth: usize) -> fmt::Result {
|
||||
let indent = depth*2;
|
||||
match expr.ty {
|
||||
ExpressionType::Block => write!(w, "{:indent$}BLOCK", "", indent=indent)?,
|
||||
ExpressionType::Number(n) => write!(w, "{:indent$}NUMBER {n:?}", "", indent=indent)?,
|
||||
ExpressionType::Name(n) => write!(w, "{:indent$}NAME {n}", "", indent=indent)?,
|
||||
ExpressionType::Binary(op) => write!(w, "{:indent$}OP {op:?}", "", indent=indent)?,
|
||||
|
|
|
@ -10,6 +10,14 @@ thread_local! {
|
|||
m.insert("recip", ("c_recip", 1));
|
||||
m.insert("conj", ("c_conj", 1));
|
||||
|
||||
m.insert("ifgt", ("c_ifgt", 4));
|
||||
m.insert("iflt", ("c_iflt", 4));
|
||||
m.insert("ifge", ("c_ifge", 4));
|
||||
m.insert("ifle", ("c_ifle", 4));
|
||||
m.insert("ifeq", ("c_ifeq", 4));
|
||||
m.insert("ifne", ("c_ifne", 4));
|
||||
m.insert("ifnan", ("c_ifnan", 3));
|
||||
|
||||
m.insert("re", ("c_re", 1));
|
||||
m.insert("im", ("c_im", 1));
|
||||
m.insert("signre", ("c_signre", 1));
|
||||
|
@ -24,11 +32,15 @@ thread_local! {
|
|||
m.insert("mul", ("c_mul", 2));
|
||||
m.insert("div", ("c_div", 2));
|
||||
m.insert("pow", ("c_pow", 2));
|
||||
m.insert("powbr", ("c_powbr", 3));
|
||||
|
||||
m.insert("exp", ("c_exp", 1));
|
||||
m.insert("log", ("c_log", 1));
|
||||
m.insert("logbr", ("c_logbr", 2));
|
||||
m.insert("sqrt", ("c_sqrt", 1));
|
||||
m.insert("sqrtbr", ("c_sqrtbr", 2));
|
||||
m.insert("cbrt", ("c_cbrt", 1));
|
||||
m.insert("cbrtbr", ("c_cbrtbr", 2));
|
||||
|
||||
m.insert("sin", ("c_sin", 1));
|
||||
m.insert("cos", ("c_cos", 1));
|
||||
|
@ -46,6 +58,8 @@ thread_local! {
|
|||
|
||||
m.insert("gamma", ("c_gamma", 1));
|
||||
m.insert("\u{0393}", ("c_gamma", 1));
|
||||
m.insert("invgamma", ("c_invgamma", 1));
|
||||
m.insert("inv\u{0393}", ("c_invgamma", 1));
|
||||
m.insert("loggamma", ("c_loggamma", 1));
|
||||
m.insert("log\u{0393}", ("c_loggamma", 1));
|
||||
m.insert("digamma", ("c_digamma", 1));
|
||||
|
|
|
@ -6,23 +6,23 @@ use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, bu
|
|||
pub struct CompileError(String);
|
||||
|
||||
impl fmt::Display for CompileError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CompileError {}
|
||||
|
||||
impl From<String> for CompileError {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
fn from(value: String) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for CompileError {
|
||||
fn from(value: fmt::Error) -> Self {
|
||||
Self(value.to_string())
|
||||
}
|
||||
fn from(value: fmt::Error) -> Self {
|
||||
Self(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn format_char(buf: &mut String, c: char) {
|
||||
|
@ -136,9 +136,9 @@ impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
|
|||
pub fn ensure_plot_defined(&self) -> Result<(), CompileError> {
|
||||
if let Some(n) = self.global_funcs.get("plot") {
|
||||
if *n == 1 {
|
||||
Ok(())
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Plot function has wrong number of arguments".to_owned().into())
|
||||
Err("Plot function has wrong number of arguments".to_owned().into())
|
||||
}
|
||||
} else {
|
||||
Err("No plot function defined".to_owned().into())
|
||||
|
@ -148,6 +148,19 @@ impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
|
|||
fn compile_expr(&mut self, local: &mut LocalState<'i>, expr: &Expression<'i>)
|
||||
-> Result<String, CompileError> {
|
||||
match expr.ty {
|
||||
ExpressionType::Block => {
|
||||
let tmp = local.next_tmp();
|
||||
writeln!(self.buf, "var {tmp}: vec2f;")?;
|
||||
writeln!(self.buf, "{{")?;
|
||||
let mut block_local = local.clone();
|
||||
let mut last = String::new();
|
||||
for child in &expr.children {
|
||||
last = self.compile_expr(&mut block_local, child)?;
|
||||
}
|
||||
writeln!(self.buf, "{tmp} = {last};")?;
|
||||
writeln!(self.buf, "}}")?;
|
||||
Ok(tmp)
|
||||
}
|
||||
ExpressionType::Name(v) => self.resolve_var(local, v),
|
||||
ExpressionType::Store(var) => {
|
||||
let a = self.compile_expr(local, &expr.children[0])?;
|
||||
|
|
|
@ -4,31 +4,31 @@ use crate::language::token::*;
|
|||
grammar<'input>(input: &'input str);
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexerError;
|
||||
type Location = usize;
|
||||
type Error = LexerError;
|
||||
|
||||
enum Token<'input> {
|
||||
"(" => Token::LParen,
|
||||
")" => Token::RParen,
|
||||
"{" => Token::LBrace,
|
||||
"}" => Token::RBrace,
|
||||
"+" => Token::Plus,
|
||||
"-" => Token::Minus,
|
||||
"*" => Token::Star,
|
||||
"/" => Token::Slash,
|
||||
"^" => Token::Caret,
|
||||
"," => Token::Comma,
|
||||
"->" => Token::Arrow,
|
||||
"=" => Token::Equal,
|
||||
":" => Token::Colon,
|
||||
"\n" => Token::Newline,
|
||||
"sum" => Token::Sum,
|
||||
"prod" => Token::Prod,
|
||||
"iter" => Token::Iter,
|
||||
Float => Token::Float(<f64>),
|
||||
Int => Token::Int(<i32>),
|
||||
Name => Token::Name(<&'input str>),
|
||||
}
|
||||
enum Token<'input> {
|
||||
"(" => Token::LParen,
|
||||
")" => Token::RParen,
|
||||
"{" => Token::LBrace,
|
||||
"}" => Token::RBrace,
|
||||
"+" => Token::Plus,
|
||||
"-" => Token::Minus,
|
||||
"*" => Token::Star,
|
||||
"/" => Token::Slash,
|
||||
"^" => Token::Caret,
|
||||
"," => Token::Comma,
|
||||
"->" => Token::Arrow,
|
||||
"=" => Token::Equal,
|
||||
":" => Token::Colon,
|
||||
"\n" => Token::Newline,
|
||||
"sum" => Token::Sum,
|
||||
"prod" => Token::Prod,
|
||||
"iter" => Token::Iter,
|
||||
Float => Token::Float(<f64>),
|
||||
Int => Token::Int(<i32>),
|
||||
Name => Token::Name(<&'input str>),
|
||||
}
|
||||
}
|
||||
|
||||
// Definitions
|
||||
|
@ -36,88 +36,89 @@ extern {
|
|||
pub Program: Vec<Definition<'input>> = Definitions;
|
||||
|
||||
Definitions: Vec<Definition<'input>> = {
|
||||
"\n"* <defs:(<Definition> "\n"+)*> <last:Definition?> => defs.into_iter().chain(last).collect(),
|
||||
"\n"* <defs:(<Definition> "\n"+)*> <last:Definition?> => defs.into_iter().chain(last).collect(),
|
||||
}
|
||||
|
||||
Definition: Definition<'input> = {
|
||||
<n:Name> "(" <args:(<Name> ",")*> <last:Name?> ")" "=" <exs:Exprs> => Definition::Function {
|
||||
name: n,
|
||||
args: args.into_iter().chain(last).collect(),
|
||||
value: exs,
|
||||
},
|
||||
<n:Name> "=" <exs:Exprs> => Definition::Constant {
|
||||
name: n,
|
||||
value: exs,
|
||||
},
|
||||
<n:Name> "(" <args:(<Name> ",")*> <last:Name?> ")" "=" <exs:Exprs> => Definition::Function {
|
||||
name: n,
|
||||
args: args.into_iter().chain(last).collect(),
|
||||
value: exs,
|
||||
},
|
||||
<n:Name> "=" <exs:Exprs> => Definition::Constant {
|
||||
name: n,
|
||||
value: exs,
|
||||
},
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
Exprs: Vec<Expression<'input>> = {
|
||||
<args:(<Expr> ",")*> <last:Expr> ","? => args.into_iter().chain(std::iter::once(last)).collect(),
|
||||
<args:(<Expr> ",")*> <last:Expr> ","? => args.into_iter().chain(std::iter::once(last)).collect(),
|
||||
}
|
||||
|
||||
Expr: Expression<'input> = Store;
|
||||
|
||||
Store: Expression<'input> = {
|
||||
<a:Store> "->" <n:Name> => Expression::new_store(a, n),
|
||||
Sum,
|
||||
<a:Store> "->" <n:Name> => Expression::new_store(a, n),
|
||||
Sum,
|
||||
}
|
||||
|
||||
Sum: Expression<'input> = {
|
||||
<a:Sum> "+" <b:Product> => Expression::new_binary(BinaryOp::Add, a, b),
|
||||
<a:Sum> "-" <b:Product> => Expression::new_binary(BinaryOp::Sub, a, b),
|
||||
Product,
|
||||
<a:Sum> "+" <b:Product> => Expression::new_binary(BinaryOp::Add, a, b),
|
||||
<a:Sum> "-" <b:Product> => Expression::new_binary(BinaryOp::Sub, a, b),
|
||||
Product,
|
||||
}
|
||||
|
||||
Product: Expression<'input> = {
|
||||
<a:Product> "*" <b:Unary> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
<a:Product> "/" <b:Unary> => Expression::new_binary(BinaryOp::Div, a, b),
|
||||
Unary,
|
||||
<a:Product> "*" <b:Unary> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
<a:Product> "/" <b:Unary> => Expression::new_binary(BinaryOp::Div, a, b),
|
||||
Unary,
|
||||
}
|
||||
|
||||
Unary: Expression<'input> = {
|
||||
"+" <a:Unary> => Expression::new_unary(UnaryOp::Pos, a),
|
||||
"-" <a:Unary> => Expression::new_unary(UnaryOp::Neg, a),
|
||||
"*" <a:Unary> => Expression::new_unary(UnaryOp::Conj, a),
|
||||
<a:Juxtapose> <b:Power> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
Power,
|
||||
"+" <a:Unary> => Expression::new_unary(UnaryOp::Pos, a),
|
||||
"-" <a:Unary> => Expression::new_unary(UnaryOp::Neg, a),
|
||||
"*" <a:Unary> => Expression::new_unary(UnaryOp::Conj, a),
|
||||
<a:Juxtapose> <b:Power> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
Power,
|
||||
}
|
||||
|
||||
Juxtapose: Expression<'input> = {
|
||||
<a:Juxtapose> <b:PreJuxtapose> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
PreJuxtapose,
|
||||
<a:Juxtapose> <b:PreJuxtapose> => Expression::new_binary(BinaryOp::Mul, a, b),
|
||||
PreJuxtapose,
|
||||
}
|
||||
|
||||
Power: Expression<'input> = {
|
||||
<a:FnCall> "^" <b:Unary> => Expression::new_binary(BinaryOp::Pow, a, b),
|
||||
FnCall,
|
||||
<a:FnCall> "^" <b:Unary> => Expression::new_binary(BinaryOp::Pow, a, b),
|
||||
FnCall,
|
||||
}
|
||||
|
||||
FnCall: Expression<'input> = {
|
||||
<n:Name> "(" <args:Exprs> ")"
|
||||
=> Expression::new_fncall(n, args),
|
||||
<Item>
|
||||
<n:Name> "(" <args:Exprs> ")"
|
||||
=> Expression::new_fncall(n, args),
|
||||
<Item>
|
||||
}
|
||||
|
||||
PreJuxtapose: Expression<'input> = {
|
||||
Number,
|
||||
"(" <Expr> ")",
|
||||
Number,
|
||||
"(" <Expr> ")",
|
||||
}
|
||||
|
||||
Item: Expression<'input> = {
|
||||
Number,
|
||||
<n:Name> => Expression::new_name(n),
|
||||
"(" <Expr> ")",
|
||||
"sum" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_sum(name, min, max, exs),
|
||||
"prod" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_prod(name, min, max, exs),
|
||||
"iter" "(" <count:Int> "," <name:Name> ":" <init:Expr> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_iter(name, count, init, exs),
|
||||
Number,
|
||||
<n:Name> => Expression::new_name(n),
|
||||
"(" <Expr> ")",
|
||||
"{" <exs:Exprs> "}" => Expression::new_block(exs),
|
||||
"sum" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_sum(name, min, max, exs),
|
||||
"prod" "(" <name:Name> ":" <min:Int> "," <max:Int> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_prod(name, min, max, exs),
|
||||
"iter" "(" <count:Int> "," <name:Name> ":" <init:Expr> ")" "{" <exs:Exprs> "}"
|
||||
=> Expression::new_iter(name, count, init, exs),
|
||||
}
|
||||
|
||||
Number: Expression<'input> = {
|
||||
<n:Float> => Expression::new_number(n),
|
||||
<n:Int> => Expression::new_number(n as f64),
|
||||
<n:Float> => Expression::new_number(n),
|
||||
<n:Int> => Expression::new_number(n as f64),
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ pub enum Token<'i> {
|
|||
Int(i32),
|
||||
Name(&'i str),
|
||||
Sum, Prod, Iter,
|
||||
LParen, RParen,
|
||||
LParen, RParen,
|
||||
LBrace, RBrace,
|
||||
Plus, Minus, Star, Slash, Caret,
|
||||
Comma, Arrow, Equal, Colon,
|
||||
|
@ -15,30 +15,30 @@ pub enum Token<'i> {
|
|||
}
|
||||
|
||||
impl<'i> fmt::Display for Token<'i> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Token::Float(n) => write!(f, "{n}"),
|
||||
Token::Int(n) => write!(f, "{n}"),
|
||||
Token::Name(n) => write!(f, "{n}"),
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Token::Float(n) => write!(f, "{n}"),
|
||||
Token::Int(n) => write!(f, "{n}"),
|
||||
Token::Name(n) => write!(f, "{n}"),
|
||||
Token::Sum => f.write_str("sum"),
|
||||
Token::Prod => f.write_str("prod"),
|
||||
Token::Iter => f.write_str("iter"),
|
||||
Token::LParen => f.write_str("("),
|
||||
Token::RParen => f.write_str(")"),
|
||||
Token::LBrace => f.write_str("{{"),
|
||||
Token::RBrace => f.write_str("}}"),
|
||||
Token::Plus => f.write_str("+"),
|
||||
Token::Minus => f.write_str("-"),
|
||||
Token::Star => f.write_str("*"),
|
||||
Token::Slash => f.write_str("/"),
|
||||
Token::Caret => f.write_str("^"),
|
||||
Token::Comma => f.write_str(","),
|
||||
Token::Arrow => f.write_str("->"),
|
||||
Token::Equal => f.write_str("="),
|
||||
Token::Colon => f.write_str(":"),
|
||||
Token::Newline => f.write_str("newline")
|
||||
}
|
||||
}
|
||||
Token::LParen => f.write_str("("),
|
||||
Token::RParen => f.write_str(")"),
|
||||
Token::LBrace => f.write_str("{"),
|
||||
Token::RBrace => f.write_str("}"),
|
||||
Token::Plus => f.write_str("+"),
|
||||
Token::Minus => f.write_str("-"),
|
||||
Token::Star => f.write_str("*"),
|
||||
Token::Slash => f.write_str("/"),
|
||||
Token::Caret => f.write_str("^"),
|
||||
Token::Comma => f.write_str(","),
|
||||
Token::Arrow => f.write_str("->"),
|
||||
Token::Equal => f.write_str("="),
|
||||
Token::Colon => f.write_str(":"),
|
||||
Token::Newline => f.write_str("newline")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -48,12 +48,12 @@ pub enum LexerError {
|
|||
}
|
||||
|
||||
impl fmt::Display for LexerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LexerError::Unexpected(i, c) => write!(f, "Unexpected character {c:?} at {i}"),
|
||||
LexerError::InvalidNumber(i, j) => write!(f, "Invalid number at {i}:{j}"),
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LexerError::Unexpected(i, c) => write!(f, "Unexpected character {c:?} at {i}"),
|
||||
LexerError::InvalidNumber(i, j) => write!(f, "Invalid number at {i}:{j}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Spanned<T, L, E> = Result<(L, T, L), E>;
|
||||
|
@ -61,6 +61,7 @@ pub type Spanned<T, L, E> = Result<(L, T, L), E>;
|
|||
pub struct Lexer<'i> {
|
||||
src: &'i str,
|
||||
chars: Peekable<CharIndices<'i>>,
|
||||
bracket_depth: usize,
|
||||
}
|
||||
|
||||
fn is_ident_begin(c: char) -> bool {
|
||||
|
@ -73,7 +74,11 @@ fn is_ident_middle(c: char) -> bool {
|
|||
|
||||
impl<'i> Lexer<'i> {
|
||||
pub fn new(src: &'i str) -> Self {
|
||||
Self { src, chars: src.char_indices().peekable() }
|
||||
Self {
|
||||
src,
|
||||
chars: src.char_indices().peekable(),
|
||||
bracket_depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_number(&mut self, i: usize, mut has_dot: bool) -> Spanned<Token<'i>, usize, LexerError> {
|
||||
|
@ -117,16 +122,29 @@ impl<'i> Lexer<'i> {
|
|||
}
|
||||
}
|
||||
|
||||
fn next_token(&mut self) -> Option<Spanned<Token<'i>, usize, LexerError>> {
|
||||
while matches!(self.chars.peek(), Some((_, ' ' | '\t' | '\r'))) {
|
||||
fn skip_whitespace(&mut self) {
|
||||
while matches!(self.chars.peek(), Some((_, ' ' | '\t' | '\n' | '\r'))) {
|
||||
if self.bracket_depth == 0 && matches!(self.chars.peek(), Some((_, '\n'))) {
|
||||
break
|
||||
}
|
||||
self.chars.next();
|
||||
}
|
||||
}
|
||||
|
||||
fn next_token(&mut self) -> Option<Spanned<Token<'i>, usize, LexerError>> {
|
||||
self.skip_whitespace();
|
||||
|
||||
Some(match self.chars.next()? {
|
||||
(i, '(') => Ok((i, Token::LParen, i + 1)),
|
||||
(i, ')') => Ok((i, Token::RParen, i + 1)),
|
||||
(i, '{') => Ok((i, Token::LBrace, i + 1)),
|
||||
(i, '}') => Ok((i, Token::RBrace, i + 1)),
|
||||
(_, '#') => {
|
||||
while !matches!(self.chars.peek(), Some((_, '\n')) | None) {
|
||||
self.chars.next();
|
||||
}
|
||||
self.next_token()?
|
||||
}
|
||||
(i, '(') => { self.bracket_depth += 1; Ok((i, Token::LParen, i + 1)) },
|
||||
(i, ')') => { self.bracket_depth -= 1; Ok((i, Token::RParen, i + 1)) },
|
||||
(i, '{') => { self.bracket_depth += 1; Ok((i, Token::LBrace, i + 1)) },
|
||||
(i, '}') => { self.bracket_depth -= 1; Ok((i, Token::RBrace, i + 1)) },
|
||||
(i, '+') => Ok((i, Token::Plus, i + 1)),
|
||||
(i, '-') => match self.chars.peek() {
|
||||
Some((_, '>')) => {
|
||||
|
@ -151,9 +169,9 @@ impl<'i> Lexer<'i> {
|
|||
}
|
||||
|
||||
impl<'i> Iterator for Lexer<'i> {
|
||||
type Item = Spanned<Token<'i>, usize, LexerError>;
|
||||
type Item = Spanned<Token<'i>, usize, LexerError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.next_token()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,18 @@ fn correct_mod2(x: vec2f, y: vec2f) -> vec2f {
|
|||
return ((x % y) + y) % y;
|
||||
}
|
||||
|
||||
fn vlength(v: vec2f) -> f32 {
|
||||
let l = length(v);
|
||||
if l != l || l <= 3.4e+38 {
|
||||
return l;
|
||||
}
|
||||
let a = max(abs(v.x), abs(v.y));
|
||||
let b = RECIP_SQRT2 * abs(v.x) + RECIP_SQRT2 * abs(v.y);
|
||||
let c = 2.0 * RECIP_SQRT29 * abs(v.x) + 5.0 * RECIP_SQRT29 * abs(v.y);
|
||||
let d = 5.0 * RECIP_SQRT29 * abs(v.x) + 2.0 * RECIP_SQRT29 * abs(v.y);
|
||||
return max(max(a, b), max(c, d));
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// constants //
|
||||
/////////////////
|
||||
|
@ -40,6 +52,7 @@ const E = 2.718281828459045;
|
|||
const RECIP_SQRT2 = 0.7071067811865475;
|
||||
const LOG_TAU = 1.8378770664093453;
|
||||
const LOG_2 = 0.6931471805599453;
|
||||
const RECIP_SQRT29 = 0.18569533817705186;
|
||||
|
||||
const C_TAU = vec2f(TAU, 0.0);
|
||||
const C_E = vec2f(E, 0.0);
|
||||
|
@ -67,6 +80,34 @@ fn c_signim(z: vec2f) -> vec2f {
|
|||
return vec2(sign(z.y), 0.0);
|
||||
}
|
||||
|
||||
fn c_ifgt(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x > q.x);
|
||||
}
|
||||
|
||||
fn c_iflt(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x < q.x);
|
||||
}
|
||||
|
||||
fn c_ifge(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x >= q.x);
|
||||
}
|
||||
|
||||
fn c_ifle(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x <= q.x);
|
||||
}
|
||||
|
||||
fn c_ifeq(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x == q.x);
|
||||
}
|
||||
|
||||
fn c_ifne(p: vec2f, q: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x != q.x);
|
||||
}
|
||||
|
||||
fn c_ifnan(p: vec2f, z: vec2f, w: vec2f) -> vec2f {
|
||||
return select(w, z, p.x != p.x && p.y != p.y);
|
||||
}
|
||||
|
||||
fn c_conj(z: vec2f) -> vec2f {
|
||||
return z * vec2(1.0, -1.0);
|
||||
}
|
||||
|
@ -76,17 +117,20 @@ fn c_abs_sq(z: vec2f) -> vec2f {
|
|||
}
|
||||
|
||||
fn c_abs(z: vec2f) -> vec2f {
|
||||
return vec2(length(z), 0.0);
|
||||
return vec2(vlength(z), 0.0);
|
||||
}
|
||||
|
||||
fn c_arg(z: vec2f) -> vec2f {
|
||||
if z.x < 0.0 && z.y == 0.0 {
|
||||
return vec2(TAU/2.0, 0.0);
|
||||
}
|
||||
return vec2(atan2(z.y, z.x), 0.0);
|
||||
}
|
||||
|
||||
fn c_argbr(z: vec2f, br: vec2f) -> vec2f {
|
||||
let r = vec2(cos(-br.x), sin(-br.x));
|
||||
let zr = c_mul(z, r);
|
||||
return vec2(atan2(zr.y, zr.x) + br.x, 0.0);
|
||||
return c_arg(zr) + vec2(br.x, 0.0);
|
||||
}
|
||||
|
||||
fn c_add(u: vec2f, v: vec2f) -> vec2f {
|
||||
|
@ -122,7 +166,7 @@ fn c_exp(z: vec2f) -> vec2f {
|
|||
}
|
||||
|
||||
fn c_log(z: vec2f) -> vec2f {
|
||||
return vec2(0.5 * log(dot(z, z)), atan2(z.y, z.x));
|
||||
return vec2(0.5 * log(dot(z, z)), c_arg(z).x);
|
||||
}
|
||||
|
||||
fn c_logbr(z: vec2f, br: vec2f) -> vec2f {
|
||||
|
@ -133,10 +177,26 @@ fn c_pow(u: vec2f, v: vec2f) -> vec2f {
|
|||
return c_exp(c_mul(c_log(u), v));
|
||||
}
|
||||
|
||||
fn c_powbr(u: vec2f, v: vec2f, br: vec2f) -> vec2f {
|
||||
return c_exp(c_mul(c_logbr(u, br), v));
|
||||
}
|
||||
|
||||
fn c_sqrt(z: vec2f) -> vec2f {
|
||||
return c_pow(z, vec2(0.5, 0.0));
|
||||
}
|
||||
|
||||
fn c_sqrtbr(z: vec2f, br: vec2f) -> vec2f {
|
||||
return c_powbr(z, vec2(0.5, 0.0), br);
|
||||
}
|
||||
|
||||
fn c_cbrt(z: vec2f, br: vec2f) -> vec2f {
|
||||
return c_pow(z, vec2(1.0/3.0, 0.0));
|
||||
}
|
||||
|
||||
fn c_cbrtbr(z: vec2f, br: vec2f) -> vec2f {
|
||||
return c_powbr(z, vec2(1.0/3.0, 0.0), br);
|
||||
}
|
||||
|
||||
fn c_sin(z: vec2f) -> vec2f {
|
||||
return vec2(sin(z.x)*cosh(z.y), cos(z.x)*sinh(z.y));
|
||||
}
|
||||
|
@ -162,84 +222,50 @@ fn c_tanh(z: vec2f) -> vec2f {
|
|||
}
|
||||
|
||||
fn c_asin(z: vec2f) -> vec2f {
|
||||
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
||||
let u = c_sqrt(vec2(1.0, 0.0) - c_mul(z, z));
|
||||
let v = c_log(u + vec2(-z.y, z.x));
|
||||
return vec2(v.y, -v.x);
|
||||
}
|
||||
|
||||
fn c_acos(z: vec2f) -> vec2f {
|
||||
let u = c_sqrt(vec2f(1.0, 0.0) - c_mul(z, z));
|
||||
let v = c_log(u + vec2f(-z.y, z.x));
|
||||
return vec2f(TAU*0.25 - v.y, v.x);
|
||||
let u = c_sqrt(vec2(1.0, 0.0) - c_mul(z, z));
|
||||
let v = c_log(u + vec2(-z.y, z.x));
|
||||
return vec2(TAU*0.25 - v.y, v.x);
|
||||
}
|
||||
|
||||
fn c_atan(z: vec2f) -> vec2f {
|
||||
let u = vec2f(1.0, 0.0) - vec2f(-z.y, z.x);
|
||||
let v = vec2f(1.0, 0.0) + vec2f(-z.y, z.x);
|
||||
let u = vec2(1.0, 0.0) - vec2(-z.y, z.x);
|
||||
let v = vec2(1.0, 0.0) + vec2(-z.y, z.x);
|
||||
let w = c_log(c_div(u, v));
|
||||
return 0.5 * vec2f(-w.y, w.x);
|
||||
return 0.5 * vec2(-w.y, w.x);
|
||||
}
|
||||
|
||||
fn c_asinh(z: vec2f) -> vec2f {
|
||||
let u = c_sqrt(vec2f(1.0, 0.0) + c_mul(z, z));
|
||||
let u = c_sqrt(vec2(1.0, 0.0) + c_mul(z, z));
|
||||
return c_log(u + z);
|
||||
}
|
||||
|
||||
fn c_acosh(z: vec2f) -> vec2f {
|
||||
let u = c_sqrt(vec2f(-1.0, 0.0) + c_mul(z, z));
|
||||
let u = c_sqrt(vec2(-1.0, 0.0) + c_mul(z, z));
|
||||
return c_log(u + z);
|
||||
}
|
||||
|
||||
fn c_atanh(z: vec2f) -> vec2f {
|
||||
let u = vec2f(1.0, 0.0) + z;
|
||||
let v = vec2f(1.0, 0.0) - z;
|
||||
let u = vec2(1.0, 0.0) + z;
|
||||
let v = vec2(1.0, 0.0) - z;
|
||||
return 0.5 * c_log(c_div(u, v));
|
||||
}
|
||||
|
||||
// gamma //
|
||||
|
||||
fn c_gamma(z: vec2f) -> vec2f {
|
||||
let reflect = z.x < 0.5;
|
||||
var zp = z;
|
||||
if reflect {
|
||||
zp = vec2(1.0, 0.0) - z;
|
||||
}
|
||||
var w = c_gamma_inner2(zp);
|
||||
if reflect {
|
||||
w = TAU * 0.5 * c_recip(c_mul(c_sin(TAU * 0.5 * z), w));
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
// Yang, ZH., Tian, JF. An accurate approximation formula for gamma function. J Inequal Appl 2018, 56 (2018).
|
||||
// https://doi.org/10.1186/s13660-018-1646-6
|
||||
fn c_gamma_inner(z: vec2f) -> vec2f {
|
||||
let z2 = c_mul(z, z);
|
||||
let z3 = c_mul(z2, z);
|
||||
|
||||
let a = c_sqrt(TAU * z);
|
||||
let b = c_pow(1.0 / (E * E) * c_mul(z3, c_sinh(c_recip(z))), 0.5 * z);
|
||||
let c = c_exp(7.0/324.0 * c_recip(c_mul(z3, 35.0 * z2 + 33.0)));
|
||||
|
||||
return c_mul(c_mul(a, b), c);
|
||||
}
|
||||
|
||||
fn c_gamma_inner2(z: vec2f) -> vec2f {
|
||||
let w = c_gamma_inner(z + vec2(3.0, 0.0));
|
||||
return c_div(w, c_mul(c_mul(z, z + vec2(1.0, 0.0)), c_mul(z + vec2(2.0, 0.0), z + vec2(3.0, 0.0))));
|
||||
}
|
||||
|
||||
// log gamma //
|
||||
|
||||
fn c_loggamma(z: vec2f) -> vec2f {
|
||||
let reflect = z.x < 0.5 && abs(z.y) < 10.0;
|
||||
let reflect = z.x < 0.5 && abs(z.y) < 13.0;
|
||||
var zp = z;
|
||||
if reflect {
|
||||
zp = vec2(1.0, 0.0) - z;
|
||||
}
|
||||
var w = c_loggamma_inner2(zp);
|
||||
if reflect {
|
||||
//let offset = select(0.0, TAU / 2.0, z % 2.0 < 1.0);
|
||||
let br = 0.5 * TAU * (0.5 - z.x) * sign(z.y);
|
||||
w = vec2(LOG_TAU - LOG_2, 0.0) - c_logbr(c_sin(TAU/2.0 * z), vec2(br, 0.0)) - w;
|
||||
}
|
||||
|
@ -256,10 +282,20 @@ fn c_loggamma_inner2(z: vec2f) -> vec2f {
|
|||
return w - l;
|
||||
}
|
||||
|
||||
// gamma //
|
||||
|
||||
fn c_gamma(z: vec2f) -> vec2f {
|
||||
return c_exp(c_loggamma(z));
|
||||
}
|
||||
|
||||
fn c_invgamma(z: vec2f) -> vec2f {
|
||||
return c_exp(-c_loggamma(z));
|
||||
}
|
||||
|
||||
// digamma //
|
||||
|
||||
fn c_digamma(z: vec2f) -> vec2f {
|
||||
let reflect = z.x < 0.5;
|
||||
let reflect = z.x < 0.5 && abs(z.y) < 13.0;
|
||||
var zp = z;
|
||||
if reflect {
|
||||
zp = vec2(1.0, 0.0) - z;
|
||||
|
@ -290,8 +326,8 @@ fn c_digamma_inner2(z: vec2f) -> vec2f {
|
|||
/////////////////
|
||||
|
||||
fn hsv2rgb(c: vec3f) -> vec3f {
|
||||
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
||||
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
||||
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
||||
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
||||
}
|
||||
|
||||
fn shademap(r: f32) -> f32 {
|
||||
|
@ -308,13 +344,18 @@ fn coloring_standard(z: vec2f) -> vec3f {
|
|||
return vec3f(0.5, 0.5, 0.5);
|
||||
}
|
||||
|
||||
let mag_sq = dot(z, z);
|
||||
if mag_sq > 3.40282347E+38 {
|
||||
let mag = vlength(z);
|
||||
if mag > 3.40282347E+38 {
|
||||
return vec3f(1.0, 1.0, 1.0);
|
||||
}
|
||||
let mag = sqrt(mag_sq);
|
||||
if uniforms.shading_intensity > 0.0 && mag > 1.8446E+19 {
|
||||
return vec3f(1.0, 1.0, 1.0);
|
||||
}
|
||||
if uniforms.shading_intensity == 0.0 && mag < 1.0E-38 {
|
||||
return vec3f(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
let arg = atan2(z.y, z.x);
|
||||
let arg = c_arg(z).x;
|
||||
|
||||
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/mag), shademap(mag));
|
||||
return hsv2rgb(hsv);
|
||||
|
@ -328,13 +369,18 @@ fn coloring_uniform(z: vec2f) -> vec3f {
|
|||
return vec3f(0.5, 0.5, 0.5);
|
||||
}
|
||||
|
||||
let mag_sq = dot(z, z);
|
||||
if mag_sq > 3.40282347E+38 {
|
||||
let mag = vlength(z);
|
||||
if mag > 3.40282347E+38 {
|
||||
return vec3f(1.0, 1.0, 1.0);
|
||||
}
|
||||
let mag = sqrt(mag_sq);
|
||||
if uniforms.shading_intensity > 0.0 && mag > 1.8446E+19 {
|
||||
return vec3f(1.0, 1.0, 1.0);
|
||||
}
|
||||
if uniforms.shading_intensity == 0.0 && mag < 1.0E-38 {
|
||||
return vec3f(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
let arg = atan2(z.y, z.x);
|
||||
let arg = c_arg(z).x;
|
||||
|
||||
let r = cos(arg - 0.0*TAU/3.0)*0.5 + 0.5;
|
||||
let g = cos(arg - 1.0*TAU/3.0)*0.5 + 0.5;
|
||||
|
@ -358,7 +404,7 @@ fn decoration_contour_im(z: vec2f) -> f32 {
|
|||
}
|
||||
|
||||
fn decoration_contour_arg(z: vec2f) -> f32 {
|
||||
let arg = atan2(z.y, z.x);
|
||||
let arg = c_arg(z).x;
|
||||
return round(correct_mod(arg + TAU, TAU/8.0) * 8.0/TAU) * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
|
@ -370,7 +416,7 @@ fn decoration_contour_mag(z: vec2f) -> f32 {
|
|||
@fragment
|
||||
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
||||
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
||||
let w = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
||||
let w = remap(pos, vec2(0.0, 0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
||||
|
||||
let z = func_plot(w);
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ impl WgpuState {
|
|||
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(),
|
||||
|
@ -125,17 +125,17 @@ impl WgpuState {
|
|||
|
||||
// Done //
|
||||
|
||||
let uniforms = Uniforms {
|
||||
let uniforms = Uniforms {
|
||||
variables: [0.0; 16],
|
||||
resolution: size.into(),
|
||||
bounds_min: (-0.0, -0.0),
|
||||
bounds_max: ( 0.0, 0.0),
|
||||
shading_intensity: 0.0,
|
||||
contour_intensity: 0.0,
|
||||
resolution: size.into(),
|
||||
bounds_min: (-0.0, -0.0),
|
||||
bounds_max: ( 0.0, 0.0),
|
||||
shading_intensity: 0.0,
|
||||
contour_intensity: 0.0,
|
||||
decorations: 0,
|
||||
coloring: 0,
|
||||
_padding: [0; 8],
|
||||
};
|
||||
};
|
||||
|
||||
Self {
|
||||
uniforms,
|
||||
|
@ -187,10 +187,10 @@ impl WgpuState {
|
|||
// Pipeline //
|
||||
|
||||
let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&self.uniform_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
label: None,
|
||||
bind_group_layouts: &[&self.uniform_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
|
@ -226,10 +226,10 @@ impl WgpuState {
|
|||
depth_stencil_attachment: None,
|
||||
});
|
||||
if let Some(pipeline) = &self.render_pipeline {
|
||||
rpass.set_pipeline(pipeline);
|
||||
rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
rpass.draw(0..3, 0..1);
|
||||
rpass.draw(1..4, 0..1);
|
||||
rpass.set_pipeline(pipeline);
|
||||
rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
rpass.draw(0..3, 0..1);
|
||||
rpass.draw(1..4, 0..1);
|
||||
}
|
||||
}
|
||||
let mut cursor = Cursor::new([0; UNIFORM_SIZE]);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@vertex
|
||||
fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f {
|
||||
var pos = array<vec2f, 4>(
|
||||
vec2(-1.0,-1.0),
|
||||
vec2( 1.0,-1.0),
|
||||
vec2(-1.0, 1.0),
|
||||
vec2( 1.0, 1.0),
|
||||
);
|
||||
var pos = array<vec2f, 4>(
|
||||
vec2(-1.0,-1.0),
|
||||
vec2( 1.0,-1.0),
|
||||
vec2(-1.0, 1.0),
|
||||
vec2( 1.0, 1.0),
|
||||
);
|
||||
|
||||
return vec4f(pos[in_vertex_index], 0.0, 1.0);
|
||||
return vec4f(pos[in_vertex_index], 0.0, 1.0);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue