made changes
This commit is contained in:
parent
36e2f03894
commit
4fcd317390
18 changed files with 528 additions and 230 deletions
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)
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
//
|
||||
|
|
|
@ -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
|
@ -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));
|
||||
|
|
|
@ -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])?;
|
||||
|
|
|
@ -109,6 +109,7 @@ Item: Expression<'input> = {
|
|||
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> "}"
|
||||
|
|
|
@ -25,8 +25,8 @@ impl<'i> fmt::Display for Token<'i> {
|
|||
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::LBrace => f.write_str("{"),
|
||||
Token::RBrace => f.write_str("}"),
|
||||
Token::Plus => f.write_str("+"),
|
||||
Token::Minus => f.write_str("-"),
|
||||
Token::Star => f.write_str("*"),
|
||||
|
@ -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((_, '>')) => {
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue