Expand description
Rhai bindings to Fidget
The engine function lets you construct a rhai::Engine with
Fidget-specific bindings. The rest of this documentation explains the
behavior of those bindings; for low-level details, see the engine
docstring.
§Introduction
Rhai is a general-purpose scripting language embedded in Rust. When used for Fidget scripting, we use the Rhai script to capture a math expression. This math expression can then be processed by Fidget’s optimized evaluators.
It’s important to distinguish between the Rhai script and the target math expression:
- The Rhai script is general-purpose, evaluated a single time to compute math expressions, and supports language features like conditionals and loops
- The math expression is closed-form arithmetic, evaluated many times
over the course of rendering, and only supports operations from
TreeOp
The purpose of evaluating the Rhai script is to capture a trace of a math
expression. Evaluating x + y in a Rhai script does not actually do any
arithmetic; instead, it creates a math expression Add(Var::X, Var::Y).
Operator overloading makes this ergonomic, but can mask the fact that the Rhai script is not the math expression!
§Trees
The basic type for math expressions is a Tree, which is equivalent to
fidget_core::context::Tree. Trees are typically built from (x, y, z)
primitives, which can be constructed with the axes() function:
let xyz = axes();
xyz.x + xyz.yx, y, z variables are also automatically injected into the Engine’s
context before evaluation.
§Mathematical constants
The Rhai context includes common mathematical constants that can be used in expressions:
// Use PI for angular calculations
let angle = PI / 4.0;
let radius = 2.0;
circle(#{ center: [radius * cos(angle), radius * sin(angle)], radius: 1.0 })Available constants include:
PI- π (3.14159…)E- Euler’s number (2.71828…)TAU- 2π (6.28318…)PHI/GOLDEN_RATIO- Golden ratio (1.61803…)SQRT_2,SQRT_3- Square rootsFRAC_PI_2,FRAC_PI_4, etc. - Fractions of πLN_2,LN_10- Natural logarithms- And many more mathematical constants
§Vector types
The Rhai context includes vec2, vec3, and vec4 types, which are
roughly analogous to their GLSL equivalents (for floating-point only).
Many (but not all) functions are implemented and overloaded for these types;
if you encounter something missing, feel free to open an issue.
§Shapes
In Rhai scripts, shapes can be constructed using object map notation:
circle(#{ center: vec2(1.0, 2.0), radius: 3.0 })This works for any object type; in addition, there are a bunch of ergonomic improvements on top of this low-level syntax.
§Type coercions
Shapes are built from a set of Rust primitives, with generous conversions from Rhai’s native types:
- Scalar values (
f64)- Both floating-point and integer Rhai values will be accepted
- Vectors (
vec2andvec3)- These may be explicitly constructed with
vec2(x, y)andvec3(x, y, z) - Appropriately-sized arrays of numbers will be automatically converted
- A
vec2(or something convertible into avec2) will be converted into avec3with a defaultzvalue. This default value is shape-specific, e.g. it will be 0 for a position and 1 for a scale.
- These may be explicitly constructed with
// array -> vec2
let c = circle(#{ center: [1, 2], radius: 3 });
// array -> vec3
let s = sphere(#{ center: [1, 2, 4], radius: 3 });
// array -> vec2 -> vec3
move(#{ shape: c, offset: [1, 1] });§Default values
Many shape fields have sensibly-defined default values; these are usually
either 0 or 1 (or the equivalent VecX values). Fields with default values
may be omitted from the map:
let c = circle(#{ center: [1, 2] }); // radius = 1
let s = sphere(#{ radius: 3 }); // center = [0, 0, 0]§Uniquely typed functions
Any shape with unique arguments may skip the object map and pass arguments directly; order doesn’t matter, because the type is unambiguous.
// array -> vec2
let c1 = circle([1, 2], 3);
let c2 = circle(3, [1, 2]); // order doesn't matter!In addition, fields with default values may be skipped:
// array -> vec2
let c1 = circle([1, 2]); // radius = 1
let c2 = circle(); // center = [0, 0], radius = 1vec2 -> vec3 coercion also works in this regime, if the vec3 has a
default value:
// array -> vec2 -> vec3
let c1 = sphere([1, 1], 4); // z = 0§Function chaining
Shapes with a single initial Tree member are typically transforms (e.g.
move from above). These functions may be called with the tree as their
first (unnamed) argument, followed by an object map of remaining parameters.
let c = circle(#{ center: [1, 2], radius: 3 });
move(c, #{ offset: [1, 1] });Given Rhai’s dispatch strategy, this can also be written as a function chain, which is more ergonomic for a string of transforms:
circle(#{ center: [1, 2], radius: 3 })
.move(#{ offset: [1, 1] });A transform which only take a single argument may skip the object map:
circle(#{ center: [1, 2], radius: 3 })
.move([1, 1]);§Functions of two trees
Shapes which take two trees can be called with two (unnamed) arguments:
let a = circle(#{ center: [0, 0], radius: 1 });
let b = circle(#{ center: [1, 0], radius: 0.5 });
difference(a, b);§Tree reduction functions
Any function which takes a single Vec<Tree> will accept both an array of
trees or individual tree arguments (up to an 8-tuple).
let a = circle(#{ center: [1, 1], radius: 3 });
let b = circle(#{ center: [2, 2], radius: 3 });
let c = circle(#{ center: [3, 3], radius: 3 });
union([a, b, c]);
union(a, b, c);
union(a, b, c, a, b, c, a, b);§Automatic tree reduction
Any shape which takes a Tree will also accept an array of trees, which are
automatically reduced with a union operation.
[
circle(#{ center: [0, 0], radius: 3 }),
circle(#{ center: [2, 2], radius: 3 }),
]
.move(#{ offset: [1, 1] });Modules§
- constants
- Mathematical constants for Rhai scripts
- shapes
- Tools for using
fidget::shapesin Rhai - tree
- Rhai bindings for the Fidget
Treetype - types
- Rhai bindings for Fidget’s 2D and 3D vector types
Traits§
- From
Dynamic - Helper trait to go from a Rhai dynamic object to a particular type