Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird Rhai error #2

Open
virtualritz opened this issue Feb 18, 2023 · 12 comments
Open

Weird Rhai error #2

virtualritz opened this issue Feb 18, 2023 · 12 comments

Comments

@virtualritz
Copy link
Contributor

virtualritz commented Feb 18, 2023

So I just started playing around with the viewer.

This Rhai script

fn length(x, y) {
    sqrt(x*x + y*y)
}

draw(|x, y| length(x, y) - 1))

works as expected.

However

fn length(x, y) {
    sqrt(x*x + y*y)
}

fn circle(x, y, r) {
    length(x, y) - r
}

draw(|x, y| circle(x, y, 1))

Yields:

render thread got error "Rhai error: Function not found: __draw (Fn) (line 2, position 5)\nin call to function 'draw' (line 9, position 1)"; forwarding
@mkeeter
Copy link
Owner

mkeeter commented Feb 19, 2023

This is somewhat sneaky: there is already a function named circle, and it's getting called instead of your function. In addition, it's already set up to return a lamda function, so you'd invoke it like

draw(circle(0, 0, 1)) // center x, center y, r -> (Fn(x, y) -> f32)

It works if you rename your function to not collide with the existing function:

fn length(x, y) {
    sqrt(x*x + y*y)
}
fn my_circle(x, y, r) {
    length(x, y) - r
}
draw(|x, y| my_circle(x, y, 1));

However, this violates the search order in the Rhai docs (and is confusing!), so let's leave this issue open.

@virtualritz
Copy link
Contributor Author

However, this violates the search order in the Rhai docs (and is confusing!), so let's leave this issue open.

Shall I report this upstream to the rhai peeps?

@virtualritz
Copy link
Contributor Author

It works if you rename your function to not collide with the existing function:

fn length(x, y) {
    sqrt(x*x + y*y)
}
fn my_circle(x, y, r) {
    length(x, y) - r
}
draw(|x, y| my_circle(x, y, 1));

When I try this with viewer in the current main branch, I get:

❯ cargo run -- test.rhai
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running `/home/moritz/code/crates/fidget/target/debug/viewer test.rhai`
[2024-04-09T11:58:18Z ERROR viewer] render thread got error "Rhai error: Function not found: draw (Fn) (line 9, position 1)"; forwarding
[...]

@virtualritz
Copy link
Contributor Author

Ah, I see, it doesn't take a closure any more. Sorry about the noise, kindly ignore!

@virtualritz
Copy link
Contributor Author

Docs say there is a draw_rgb function in Rhai but I get an error when trying to call this. Also: what's the signature of this function?

@mkeeter
Copy link
Owner

mkeeter commented Apr 9, 2024

Yes, #67 switched to using the new Tree type in scripts (instead of closures).

The new signature is draw_rgb(Tree, f32, f32, f32), e.g. this works for me:

draw_rgb(circle(0, 1, 2), 1.0, 0.0, 0.0)

@virtualritz
Copy link
Contributor Author

Cheers, it was a typo.

Is there a list of built-ins somewhere? It seems e.g. Rhai is missing e.g. abs? Docs say one needs to include ArithmeticPackage but the docs also say Engine::new() would include that. What am I missing?

@virtualritz
Copy link
Contributor Author

I can not get a simple box in the viewer. Whatever function I try, except for the circle, I only get the positive part of the axes, i.e.

fn abs(x) {
    if x < 0 {
        -x
    } else {
        x
    }
}

fn box(x, y, radius_x, radius_y)
{
    let p_x = abs(x) - radius_x;
    let p_y = abs(y) - radius_y;

    max(p_x, p_y)
}

draw(box(x, y, 1, 1))

I get:
image

Zooming out:
image

I also tried move(box(...),...) but no luck. What am I missing?

@mkeeter
Copy link
Owner

mkeeter commented Apr 10, 2024

Here's the issue:

fn abs(x) {
    if x < 0 {
        -x
    } else {
        x
    }
}

During script evaluation, x is a Tree handle. Normally, trees capture operations through operator overloading, e.g. x + 1 will return a new Tree. For some reason, Rhai comparisons of different types always return false, so x < 0 is evaluated during script evaluation and returns a new Tree that is always -x.

#70 (merging soon) adds overloaded operators to ban these comparisons, so that it will fail at script evaluation instead of returning a misleading result. This PR also adds missing opcodes, so you can write your script as

fn box(radius_x, radius_y)
{
    let ax = axes();
    let p_x = abs(ax.x) - radius_x;
    let p_y = abs(ax.y) - radius_y;

    max(p_x, p_y)
}

draw(box(1, 1))

(I'm still figuring out best practices, but using axes() within a function seems cleaner than passing x, y as arguments)

@virtualritz
Copy link
Contributor Author

virtualritz commented Apr 10, 2024

(I'm still figuring out best practices, but using axes() within a function seems cleaner than passing x, y as arguments)

Speaking of which: writing the functions in Rhai, doing each operation for each coordinate, is very verbose because of the lack of tuple types. Most functions from e.g. here almost double in code length.
For 3D cases it will be even more verbose.

What is the performance hit if one used arrays of two (or three) elements to represent vectors?

Of course then one would need to write the entire shebang of basics like mul, add, div etc. that work on those arrays.
And the code of the kind of expressions used in SDFs would still look more like Lisp than math. 😁.

I.e. best would probably be some language extension that supports vectors (and possible matrices) inside Rhai? Not sure this possible.

EDIT: I just saw this, so it seems this is possible and likely axes() does something like this already?

@virtualritz
Copy link
Contributor Author

virtualritz commented Apr 11, 2024

#70 (merging soon) adds overloaded operators to ban these comparisons, so that it will fail at script evaluation instead of returning a misleading result. This PR also adds missing opcodes, [...]

Cheers! 😃

So I have something like:

fn rounded_box(
    height_x, height_y,
    radius_top_right,
    radius_bottom_right,
    radius_top_left,
    radius_bottom_left)
{
    let ax = axes();
    let x = ax.x;
    let y = ax.y;

    let r_x = if 0.0 < x { radius_top_right } else { radius_top_left };
    let r_y = if 0.0 < x { radius_bottom_right } else { radius_bottom_left };
    let r_x = if 0.0 < y { r_x } else { r_y };
    let q_x = abs(x)- height_x + r_x;
    let q_y = abs(y)- height_y + r_x;

    min(max(q_x, q_y), 0) + length(max(q_x, 0.0), max(q_y, 0)) - r_x
}

And that gives me an error for the comparisons in the if a < bs:

cannot compare Tree types during function tracing

I tried rewriting the resp. expressions with compare(a, b) but this:

    let r_x = if compare(0, x) == -1 { radius_top_right } else { radius_top_left };
    let r_y = if compare(0, x) == -1 { radius_bottom_right } else { radius_bottom_left };
    let r_x = if compare(0, y) == -1 { r_x } else { r_y };

results in the same error.

What is the workaround here?

EDIT: thinking back of my days as a VFX shader writer (where conditionals were a no-no): step()/filteredstep() would be the alternative(s). And re. the latter: will there be access to filter size(s)/derivatives?

@virtualritz
Copy link
Contributor Author

virtualritz commented Apr 11, 2024

And another one. A simple rounding op, i.e.

fn round(shape, radius) {
    shape - radius
}

Only grows my shape by radius but there is no rounding. This is obviously somehow possible as visible in the viewer for the gradient 2D SDF view mode.

What am I missing/what is the workaround?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants