diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a2ed7c..c138b1d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.2.8 +# 0.3.0 - Major refactoring of core evaluation traits - The lowest-level "thing that can be evaluated" trait has changed from `Shape` (taking `(x, y, z)` inputs) to `Function` (taking an arbitrary diff --git a/Cargo.lock b/Cargo.lock index c155e5be..abab924b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -877,7 +877,7 @@ dependencies = [ [[package]] name = "fidget" -version = "0.2.8" +version = "0.3.0" dependencies = [ "arrayvec", "bimap", diff --git a/demo/src/main.rs b/demo/src/main.rs index b6293c6b..e49dd403 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -258,7 +258,7 @@ fn main() -> Result<()> { let now = Instant::now(); let args = Args::parse(); let mut file = std::fs::File::open(&args.input)?; - let (mut ctx, root) = Context::from_text(&mut file)?; + let (ctx, root) = Context::from_text(&mut file)?; info!("Loaded file in {:?}", now.elapsed()); match args.cmd { diff --git a/fidget/Cargo.toml b/fidget/Cargo.toml index 204ef22a..302d8fa9 100644 --- a/fidget/Cargo.toml +++ b/fidget/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fidget" -version = "0.2.8" +version = "0.3.0" edition = "2021" license = "MPL-2.0" repository = "https://github.com/mkeeter/fidget" @@ -60,7 +60,7 @@ render = [] ## Enable 3D meshing, in the [`fidget::mesh`](crate::mesh) module mesh = ["dep:crossbeam-deque"] -## Enable `eval-tests` if you're writing your own Shape / evaluators and want to +## Enable `eval-tests` if you're writing your own evaluators and want to ## unit-test them. When enabled, the crate exports a set of macros to test each ## evaluator type, e.g. `float_slice_tests!(...)`. eval-tests = [] diff --git a/fidget/src/core/context/mod.rs b/fidget/src/core/context/mod.rs index f6a39be8..4643d0fb 100644 --- a/fidget/src/core/context/mod.rs +++ b/fidget/src/core/context/mod.rs @@ -5,7 +5,7 @@ //! - A [`Tree`] is a free-floating math expression, which can be cloned //! and has overloaded operators for ease of use. It is **not** deduplicated; //! two calls to [`Tree::constant(1.0)`](Tree::constant) will allocate two -//! different [`TreeOp`] objects. +//! different objects. //! `Tree` objects are typically used when building up expressions; they //! should be converted to `Node` objects (in a particular `Context`) after //! they have been constructed. @@ -38,7 +38,12 @@ define_index!(Node, "An index in the `Context::ops` map"); /// operations. /// /// It should be used like an arena allocator: it grows over time, then frees -/// all of its contents when dropped. +/// all of its contents when dropped. There is no reference counting within the +/// context. +/// +/// Items in the context are accessed with [`Node`] keys, which are simple +/// handles into an internal map. Inside the context, operations are +/// represented with the [`Op`] type. #[derive(Debug, Default)] pub struct Context { ops: IndexMap, @@ -121,10 +126,10 @@ impl Context { /// /// If the node is invalid for this tree, returns an error; if the node is /// not an `Op::Input`, returns `Ok(None)`. - pub fn get_var(&self, n: Node) -> Result, Error> { + pub fn get_var(&self, n: Node) -> Result { match self.get_op(n) { - Some(Op::Input(v)) => Ok(Some(*v)), - Some(_) => Ok(None), + Some(Op::Input(v)) => Ok(*v), + Some(..) => Err(Error::NotAVar), _ => Err(Error::BadNode), } } @@ -154,6 +159,22 @@ impl Context { } /// Constructs or finds a variable input node + /// + /// To make an anonymous variable, call this function with [`Var::new()`]: + /// + /// ``` + /// # use fidget::{context::Context, var::Var}; + /// # use std::collections::HashMap; + /// let mut ctx = Context::new(); + /// let v1 = ctx.var(Var::new()); + /// let v2 = ctx.var(Var::new()); + /// assert_ne!(v1, v2); + /// + /// let mut vars = HashMap::new(); + /// vars.insert(ctx.get_var(v1).unwrap(), 3.0); + /// assert_eq!(ctx.eval(v1, &vars).unwrap(), 3.0); + /// assert!(ctx.eval(v2, &vars).is_err()); // v2 isn't in the map + /// ``` pub fn var(&mut self, v: Var) -> Node { self.ops.insert(Op::Input(v)) } @@ -181,7 +202,7 @@ impl Context { let op_a = *self.get_op(a).ok_or(Error::BadNode)?; let n = self.ops.insert(Op::Unary(op, a)); let out = if matches!(op_a, Op::Const(_)) { - let v = self.eval(n, &BTreeMap::new())?; + let v = self.eval(n, &Default::default())?; self.pop().unwrap(); // removes `n` self.constant(v) } else { @@ -214,7 +235,7 @@ impl Context { // constant-folded (indeed, we pop the node right afterwards) let n = self.ops.insert(f(a, b)); let out = if matches!((op_a, op_b), (Op::Const(_), Op::Const(_))) { - let v = self.eval(n, &BTreeMap::new())?; + let v = self.eval(n, &Default::default())?; self.pop().unwrap(); // removes `n` self.constant(v) } else { @@ -762,7 +783,7 @@ impl Context { pub fn eval( &self, root: Node, - vars: &BTreeMap, + vars: &HashMap, ) -> Result { let mut cache = vec![None; self.ops.len()].into(); self.eval_inner(root, vars, &mut cache) @@ -771,7 +792,7 @@ impl Context { fn eval_inner( &self, node: Node, - vars: &BTreeMap, + vars: &HashMap, cache: &mut IndexVec, Node>, ) -> Result { if node.0 >= cache.len() { @@ -782,7 +803,7 @@ impl Context { } let mut get = |n: Node| self.eval_inner(n, vars, cache); let v = match self.get_op(node).ok_or(Error::BadNode)? { - Op::Input(v) => *vars.get(v).unwrap(), + Op::Input(v) => *vars.get(v).ok_or(Error::MissingVar(*v))?, Op::Const(c) => c.0, Op::Binary(op, a, b) => { diff --git a/fidget/src/core/eval/bulk.rs b/fidget/src/core/eval/bulk.rs index 4a31c44d..207ca1c7 100644 --- a/fidget/src/core/eval/bulk.rs +++ b/fidget/src/core/eval/bulk.rs @@ -9,10 +9,6 @@ use crate::{eval::Tape, Error}; /// Trait for bulk evaluation returning the given type `T` -/// -/// It's uncommon to use this trait outside the library itself; it's an -/// abstraction to reduce code duplication, and is public because it's used as a -/// constraint on other public APIs. pub trait BulkEvaluator: Default { /// Data type used during evaluation type Data: From + Copy + Clone; @@ -30,8 +26,12 @@ pub trait BulkEvaluator: Default { /// Evaluates many points using the given instruction tape /// - /// Returns an error if the `x`, `y`, `z`, and `out` slices are of different - /// lengths. + /// `vars` should be a slice-of-slices (or a slice-of-`Vec`s) representing + /// input arguments for each of the tape's variables; use [`Tape::vars`] to + /// map from [`Var`](crate::var::Var) to position in the list. + /// + /// Returns an error if any of the `var` slices are of different lengths, or + /// if all variables aren't present. fn eval>( &mut self, tape: &Self::Tape, @@ -42,26 +42,4 @@ pub trait BulkEvaluator: Default { fn new() -> Self { Self::default() } - - /// Helper function to return an error if the inputs are invalid - fn check_arguments>( - &self, - vars: &[V], - var_count: usize, - ) -> Result<(), Error> { - // It's fine if the caller has given us extra variables (e.g. due to - // tape simplification), but it must have given us enough. - if vars.len() < var_count { - Err(Error::BadVarSlice(vars.len(), var_count)) - } else { - let Some(n) = vars.first().map(|v| v.len()) else { - return Ok(()); - }; - if vars.iter().any(|v| v.len() == n) { - Ok(()) - } else { - Err(Error::MismatchedSlices) - } - } - } } diff --git a/fidget/src/core/eval/mod.rs b/fidget/src/core/eval/mod.rs index fa0b1973..6e22d898 100644 --- a/fidget/src/core/eval/mod.rs +++ b/fidget/src/core/eval/mod.rs @@ -18,10 +18,8 @@ pub use tracing::TracingEvaluator; /// A tape represents something that can be evaluated by an evaluator /// -/// It includes some kind of storage and the ability to look up variable -/// mapping. The variable mapping should be identical to calling -/// [`Function::vars`] on the `Function` which produced this tape, but it's -/// convenient to be able to look up vars locally. +/// It includes some kind of storage (which could be empty) and the ability to +/// look up variable mapping. pub trait Tape { /// Associated type for this tape's data storage type Storage: Default; diff --git a/fidget/src/core/eval/test/grad_slice.rs b/fidget/src/core/eval/test/grad_slice.rs index 1226f9c7..9b35a3d9 100644 --- a/fidget/src/core/eval/test/grad_slice.rs +++ b/fidget/src/core/eval/test/grad_slice.rs @@ -367,7 +367,6 @@ impl TestGradSlice { // that S is also a VmShape, but this comparison isn't particularly // expensive, so we'll do it regardless. let shape = VmFunction::new(&ctx, node).unwrap(); - #[allow(clippy::unit_arg)] let tape = shape.grad_slice_tape(Default::default()); let cmp = TestGradSlice::::eval_xyz(&tape, &x, &y, &z); diff --git a/fidget/src/core/eval/tracing.rs b/fidget/src/core/eval/tracing.rs index e64d18a8..bed4ff77 100644 --- a/fidget/src/core/eval/tracing.rs +++ b/fidget/src/core/eval/tracing.rs @@ -34,6 +34,12 @@ pub trait TracingEvaluator: Default { type Trace; /// Evaluates the given tape at a particular position + /// + /// `vars` should be a slice of values representing input arguments for each + /// of the tape's variables; use [`Tape::vars`] to map from + /// [`Var`](crate::var::Var) to position in the list. + /// + /// Returns an error if the `var` slice is not of sufficient length. fn eval( &mut self, tape: &Self::Tape, @@ -44,19 +50,4 @@ pub trait TracingEvaluator: Default { fn new() -> Self { Self::default() } - - /// Helper function to return an error if the inputs are invalid - fn check_arguments( - &self, - vars: &[Self::Data], - var_count: usize, - ) -> Result<(), Error> { - if vars.len() < var_count { - // It's okay to be passed extra vars, because expressions may have - // been simplified. - Err(Error::BadVarSlice(vars.len(), var_count)) - } else { - Ok(()) - } - } } diff --git a/fidget/src/core/shape/mod.rs b/fidget/src/core/shape/mod.rs index fbd73593..56d84b9a 100644 --- a/fidget/src/core/shape/mod.rs +++ b/fidget/src/core/shape/mod.rs @@ -1,7 +1,7 @@ //! Data structures for shape evaluation //! //! Types in this module are typically thin (generic) wrappers around objects -//! that implement traits in [`fidget::eval`](crate::eval). The wraper types +//! that implement traits in [`fidget::eval`](crate::eval). The wrapper types //! are specialized to operate on `x, y, z` arguments, rather than taking //! arbitrary numbers of variables. //! @@ -467,7 +467,11 @@ impl ShapeBulkEval where E::Data: From + Transformable, { - /// Bulk evaluation of many samples + /// Bulk evaluation of many samples, without any variables + /// + /// If the shape includes variables other than `X`, `Y`, `Z`, + /// [`eval_v`](Self::eval_v) should be used instead (and this function will + /// return an error). /// /// Before evaluation, the tape's transform matrix is applied (if present). pub fn eval( @@ -481,7 +485,7 @@ where self.eval_v(tape, x, y, z, &h) } - /// Bulk evaluation of many samples + /// Bulk evaluation of many samples, with variables /// /// Before evaluation, the tape's transform matrix is applied (if present). pub fn eval_v>( diff --git a/fidget/src/core/var/mod.rs b/fidget/src/core/var/mod.rs index 4dbcfd22..44411b3e 100644 --- a/fidget/src/core/var/mod.rs +++ b/fidget/src/core/var/mod.rs @@ -3,8 +3,9 @@ //! A [`Var`] maintains a persistent identity from //! [`Tree`](crate::context::Tree) to [`Context`](crate::context::Node) (where //! it is wrapped in a [`Op::Input`](crate::context::Op::Input)) to evaluation -//! (where [`Function::vars`](crate::eval::Function::vars) maps from `Var` to -//! index in the argument list). +//! (where [`Tape::vars`](crate::eval::Tape::vars) maps from `Var` to index in +//! the argument list). +use crate::Error; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -16,7 +17,6 @@ use std::collections::HashMap; /// Variables are "global", in that every instance of `Var::X` represents the /// same thing. To generate a "local" variable, [`Var::new`] picks a random /// 64-bit value, which is very unlikely to collide with anything else. -#[allow(missing_docs)] #[derive( Copy, Clone, @@ -30,13 +30,17 @@ use std::collections::HashMap; Deserialize, )] pub enum Var { + /// Variable representing the X axis for 2D / 3D shapes X, + /// Variable representing the Y axis for 2D / 3D shapes Y, + /// Variable representing the Z axis for 3D shapes Z, + /// Generic variable V(VarIndex), } -/// Type for a variable index (implemented as a `u64`) +/// Type for a variable index (implemented as a `u64`), used in [`Var::V`] #[derive( Copy, Clone, @@ -91,8 +95,10 @@ impl std::fmt::Display for Var { /// Variable indexes are automatically assigned the first time /// [`VarMap::insert`] is called on that variable. /// -/// Indexes are guaranteed to be tightly packed, i.e. contains values from -/// `0..vars.len()`. +/// Indexes are guaranteed to be tightly packed, i.e. a map `vars` will contains +/// values from `0..vars.len()`. +/// +/// For efficiency, this type does not allocate heap memory for `Var::X/Y/Z`. #[derive(Default, Serialize, Deserialize)] pub struct VarMap { x: Option, @@ -138,6 +144,39 @@ impl VarMap { Var::V(v) => self.v.entry(v).or_insert(next), }; } + + pub(crate) fn check_tracing_arguments( + &self, + vars: &[T], + ) -> Result<(), Error> { + if vars.len() < self.len() { + // It's okay to be passed extra vars, because expressions may have + // been simplified. + Err(Error::BadVarSlice(vars.len(), self.len())) + } else { + Ok(()) + } + } + + pub(crate) fn check_bulk_arguments>( + &self, + vars: &[V], + ) -> Result<(), Error> { + // It's fine if the caller has given us extra variables (e.g. due to + // tape simplification), but it must have given us enough. + if vars.len() < self.len() { + Err(Error::BadVarSlice(vars.len(), self.len())) + } else { + let Some(n) = vars.first().map(|v| v.len()) else { + return Ok(()); + }; + if vars.iter().any(|v| v.len() == n) { + Ok(()) + } else { + Err(Error::MismatchedSlices) + } + } + } } impl std::ops::Index<&Var> for VarMap { diff --git a/fidget/src/core/vm/data.rs b/fidget/src/core/vm/data.rs index 51ddcfc3..e7b299d3 100644 --- a/fidget/src/core/vm/data.rs +++ b/fidget/src/core/vm/data.rs @@ -109,14 +109,6 @@ impl VmData { self.asm.slot_count() } - /// Returns the number of variables that may be used - /// - /// Note that this can sometimes be an overestimate, if the inner tape has - /// been simplified. - pub fn var_count(&self) -> usize { - self.vars.len() - } - /// Simplifies both inner tapes, using the provided choice array /// /// To minimize allocations, this function takes a [`VmWorkspace`] and diff --git a/fidget/src/core/vm/mod.rs b/fidget/src/core/vm/mod.rs index 618948ce..18f6fe81 100644 --- a/fidget/src/core/vm/mod.rs +++ b/fidget/src/core/vm/mod.rs @@ -32,10 +32,14 @@ pub type VmFunction = GenericVmFunction<{ u8::MAX as usize }>; /// Shape that use a the [`VmFunction`] backend for evaluation pub type VmShape = Shape; +/// Tape storage type which indicates that there's no actual backing storage +#[derive(Default)] +pub struct EmptyTapeStorage; + impl Tape for GenericVmFunction { - type Storage = (); + type Storage = EmptyTapeStorage; fn recycle(self) -> Self::Storage { - // nothing to do here + EmptyTapeStorage } fn vars(&self) -> &VarMap { @@ -135,11 +139,6 @@ impl GenericVmFunction { pub fn choice_count(&self) -> usize { self.0.choice_count() } - - /// Returns the number of variables (inputs) in the tape - pub fn var_count(&self) -> usize { - self.0.var_count() - } } impl Function for GenericVmFunction { @@ -147,21 +146,21 @@ impl Function for GenericVmFunction { type Storage = VmData; type Workspace = VmWorkspace; - type TapeStorage = (); + type TapeStorage = EmptyTapeStorage; - fn float_slice_tape(&self, _storage: ()) -> Self { + fn float_slice_tape(&self, _storage: EmptyTapeStorage) -> Self { self.clone() } type GradSliceEval = VmGradSliceEval; - fn grad_slice_tape(&self, _storage: ()) -> Self { + fn grad_slice_tape(&self, _storage: EmptyTapeStorage) -> Self { self.clone() } type PointEval = VmPointEval; - fn point_tape(&self, _storage: ()) -> Self { + fn point_tape(&self, _storage: EmptyTapeStorage) -> Self { self.clone() } type IntervalEval = VmIntervalEval; - fn interval_tape(&self, _storage: ()) -> Self { + fn interval_tape(&self, _storage: EmptyTapeStorage) -> Self { self.clone() } type Trace = VmTrace; @@ -259,15 +258,15 @@ impl TracingEvaluator for VmIntervalEval { type Data = Interval; type Tape = GenericVmFunction; type Trace = VmTrace; - type TapeStorage = (); + type TapeStorage = EmptyTapeStorage; fn eval( &mut self, tape: &Self::Tape, vars: &[Interval], ) -> Result<(Interval, Option<&VmTrace>), Error> { + tape.vars().check_tracing_arguments(vars)?; let tape = tape.0.as_ref(); - self.check_arguments(vars, tape.var_count())?; self.0.resize_slots(tape); let mut simplify = false; @@ -488,15 +487,15 @@ impl TracingEvaluator for VmPointEval { type Data = f32; type Tape = GenericVmFunction; type Trace = VmTrace; - type TapeStorage = (); + type TapeStorage = EmptyTapeStorage; fn eval( &mut self, tape: &Self::Tape, vars: &[f32], ) -> Result<(f32, Option<&VmTrace>), Error> { + tape.vars().check_tracing_arguments(vars)?; let tape = tape.0.as_ref(); - self.check_arguments(vars, tape.var_count())?; self.0.resize_slots(tape); let mut choices = self.0.choices.as_mut_slice().iter_mut(); @@ -802,15 +801,15 @@ pub struct VmFloatSliceEval(BulkVmEval); impl BulkEvaluator for VmFloatSliceEval { type Data = f32; type Tape = GenericVmFunction; - type TapeStorage = (); + type TapeStorage = EmptyTapeStorage; fn eval>( &mut self, tape: &Self::Tape, vars: &[V], ) -> Result<&[f32], Error> { + tape.vars().check_bulk_arguments(vars)?; let tape = tape.0.as_ref(); - self.check_arguments(vars, tape.var_count())?; let size = vars.first().map(|v| v.len()).unwrap_or(0); self.0.resize_slots(tape, size); @@ -1111,15 +1110,15 @@ pub struct VmGradSliceEval(BulkVmEval); impl BulkEvaluator for VmGradSliceEval { type Data = Grad; type Tape = GenericVmFunction; - type TapeStorage = (); + type TapeStorage = EmptyTapeStorage; fn eval>( &mut self, tape: &Self::Tape, vars: &[V], ) -> Result<&[Grad], Error> { + tape.vars().check_bulk_arguments(vars)?; let tape = tape.0.as_ref(); - self.check_arguments(vars, tape.var_count())?; let size = vars.first().map(|v| v.len()).unwrap_or(0); self.0.resize_slots(tape, size); diff --git a/fidget/src/error.rs b/fidget/src/error.rs index 13d6bd58..92c4cc93 100644 --- a/fidget/src/error.rs +++ b/fidget/src/error.rs @@ -1,4 +1,5 @@ //! Module containing the Fidget universal error type +use crate::var::Var; use thiserror::Error; /// Universal error type for Fidget @@ -7,10 +8,15 @@ pub enum Error { /// Node is not present in this `Context` #[error("node is not present in this `Context`")] BadNode, + /// Variable is not present in this `Context` #[error("variable is not present in this `Context`")] BadVar, + /// Variable is missing in the evaluation map + #[error("variable {0} is missing in the evaluation map")] + MissingVar(Var), + /// The given node does not have an associated variable #[error("node does not have an associated variable")] NotAVar, @@ -18,6 +24,7 @@ pub enum Error { /// `Context` is empty #[error("`Context` is empty")] EmptyContext, + /// `IndexMap` is empty #[error("`IndexMap` is empty")] EmptyMap, @@ -25,6 +32,7 @@ pub enum Error { /// Unknown opcode {0} #[error("unknown opcode {0}")] UnknownOpcode(String), + /// Unknown variable {0} #[error("unknown variable {0}")] UnknownVariable(String), diff --git a/fidget/src/jit/mod.rs b/fidget/src/jit/mod.rs index ed9edbdf..6fe8daea 100644 --- a/fidget/src/jit/mod.rs +++ b/fidget/src/jit/mod.rs @@ -1027,7 +1027,7 @@ impl TracingEvaluator for JitIntervalEval { tape: &Self::Tape, vars: &[Self::Data], ) -> Result<(Self::Data, Option<&Self::Trace>), Error> { - self.check_arguments(vars, tape.vars().len())?; + tape.vars().check_tracing_arguments(vars)?; Ok(self.0.eval(tape, vars)) } } @@ -1046,7 +1046,7 @@ impl TracingEvaluator for JitPointEval { tape: &Self::Tape, vars: &[Self::Data], ) -> Result<(Self::Data, Option<&Self::Trace>), Error> { - self.check_arguments(vars, tape.vars().len())?; + tape.vars().check_tracing_arguments(vars)?; Ok(self.0.eval(tape, vars)) } } @@ -1201,7 +1201,7 @@ impl BulkEvaluator for JitFloatSliceEval { tape: &Self::Tape, vars: &[V], ) -> Result<&[Self::Data], Error> { - self.check_arguments(vars, tape.vars().len())?; + tape.vars().check_bulk_arguments(vars)?; Ok(self.0.eval(tape, vars)) } } @@ -1219,7 +1219,7 @@ impl BulkEvaluator for JitGradSliceEval { tape: &Self::Tape, vars: &[V], ) -> Result<&[Self::Data], Error> { - self.check_arguments(vars, tape.vars().len())?; + tape.vars().check_bulk_arguments(vars)?; Ok(self.0.eval(tape, vars)) } } diff --git a/fidget/src/lib.rs b/fidget/src/lib.rs index 4c19bd96..a0015315 100644 --- a/fidget/src/lib.rs +++ b/fidget/src/lib.rs @@ -1,5 +1,5 @@ -//! Fidget is a library of infrastructure and algorithms for complex closed-form -//! implicit surfaces. +//! Fidget is a library of infrastructure and algorithms for function +//! evaluation, with an emphasis on complex closed-form implicit surfaces. //! //! An **implicit surface** is a function `f(x, y, z)`, where `x`, `y`, and `z` //! represent a position in 3D space. By convention, if `f(x, y, z) < 0`, then @@ -21,6 +21,10 @@ //! the rest of this page is a quick tour through the library APIs. //! //! # Shape construction +//! A "shape" is a closed-form function of `(x, y, z)` with a single output. +//! For example, a circle of radius `1` could be expressed as +//! `sqrt(x*x + y*y) - 1`. +//! //! Shapes are constructed within a //! [`fidget::context::Context`](crate::context::Context). A context serves as //! an arena-style allocator, doing local deduplication and other simple @@ -109,8 +113,8 @@ //! [`Shape`](crate::shape::Shape), which binds `(x, y, z)` axes to specific //! variables. //! -//! Here's a simple example of interval evaluation, using a `Shape` to wrap a -//! function and evaluate it at a particular `(x, y, z)` position: +//! Here's a simple example of multi-point evaluation, using a `VmShape` to +//! evaluate the function `X + Y` at four sample locations: //! //! ``` //! use fidget::{ @@ -121,15 +125,15 @@ //! //! let tree = Tree::x() + Tree::y(); //! let shape = VmShape::from(tree); -//! let mut interval_eval = VmShape::new_interval_eval(); -//! let tape = shape.ez_interval_tape(); -//! let (out, _trace) = interval_eval.eval( +//! let mut eval = VmShape::new_float_slice_eval(); +//! let tape = shape.ez_float_slice_tape(); +//! let out = eval.eval( //! &tape, -//! [0.0, 1.0], // X -//! [2.0, 3.0], // Y -//! [0.0, 0.0], // Z +//! &[0.0, 1.0, 2.0, 3.0], // X +//! &[2.0, 3.0, 4.0, 5.0], // Y +//! &[0.0, 0.0, 0.0, 0.0], // Z //! )?; -//! assert_eq!(out, [2.0, 4.0].into()); +//! assert_eq!(out, &[2.0, 4.0, 6.0, 8.0]); //! # Ok::<(), fidget::Error>(()) //! ``` //! diff --git a/wasm-demo/Cargo.lock b/wasm-demo/Cargo.lock index 01cdaa4f..06e78184 100644 --- a/wasm-demo/Cargo.lock +++ b/wasm-demo/Cargo.lock @@ -255,7 +255,7 @@ dependencies = [ [[package]] name = "fidget" -version = "0.2.8" +version = "0.3.0" dependencies = [ "arrayvec", "bimap",