mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 12:48:11 +08:00
Adapt the cutover to the reviewed core-types API
This commit is contained in:
@@ -246,10 +246,10 @@ fn test_nested_network_flattening() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_metadata_preservation() {
|
fn test_metadata_preservation() {
|
||||||
// Create a network with nodes that have non-default metadata
|
// Create a network with nodes that have non-default metadata
|
||||||
let context_features = ContextDependencies {
|
let context_features = ContextDependencies::new(
|
||||||
extract: core_types::context::ContextFeatures::FOOTPRINT | core_types::context::ContextFeatures::REAL_TIME,
|
core_types::context::ContextFeatures::FOOTPRINT | core_types::context::ContextFeatures::REAL_TIME,
|
||||||
..Default::default()
|
core_types::context::ContextFeatures::empty(),
|
||||||
};
|
);
|
||||||
|
|
||||||
let network = NodeNetwork {
|
let network = NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
|
|||||||
@@ -1008,7 +1008,7 @@ impl NodeNetwork {
|
|||||||
DocumentNodeMetadata::SourceId => {
|
DocumentNodeMetadata::SourceId => {
|
||||||
let source_id = Self::source_id_for_path(path);
|
let source_id = Self::source_id_for_path(path);
|
||||||
if let Some(context_features) = context_features.as_deref_mut() {
|
if let Some(context_features) = context_features.as_deref_mut() {
|
||||||
core_types::context::merge_sorted_sources(&mut context_features.sources, &[source_id]);
|
context_features.add_sources(&[source_id]);
|
||||||
}
|
}
|
||||||
(TaggedValue::U64(source_id).into(), false)
|
(TaggedValue::U64(source_id).into(), false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use criterion::BenchmarkGroup;
|
use criterion::BenchmarkGroup;
|
||||||
use criterion::measurement::Measurement;
|
use criterion::measurement::Measurement;
|
||||||
use futures::executor::block_on;
|
|
||||||
use graph_craft::proto::ProtoNetwork;
|
use graph_craft::proto::ProtoNetwork;
|
||||||
use graph_craft::util::{DEMO_ART, compile, load_from_name};
|
use graph_craft::util::{DEMO_ART, compile, load_from_name};
|
||||||
use graphene_std::application_io::EditorApi;
|
use graphene_std::application_io::EditorApi;
|
||||||
@@ -14,7 +13,7 @@ pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
|||||||
let preprocessor = preprocessor::Preprocessor::new();
|
let preprocessor = preprocessor::Preprocessor::new();
|
||||||
preprocessor.preprocess(&mut network, &|_| None).unwrap();
|
preprocessor.preprocess(&mut network, &|_| None).unwrap();
|
||||||
let proto_network = compile(network);
|
let proto_network = compile(network);
|
||||||
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
|
let executor = DynamicExecutor::new(proto_network.clone()).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ mod benchmark_util;
|
|||||||
|
|
||||||
use benchmark_util::{bench_for_each_demo, setup_network};
|
use benchmark_util::{bench_for_each_demo, setup_network};
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{Criterion, criterion_group, criterion_main};
|
||||||
|
use graph_craft::graphene_compiler::Executor;
|
||||||
use graphene_std::application_io::RenderConfig;
|
use graphene_std::application_io::RenderConfig;
|
||||||
|
|
||||||
fn subsequent_evaluations(c: &mut Criterion) {
|
fn subsequent_evaluations(c: &mut Criterion) {
|
||||||
@@ -9,9 +10,7 @@ fn subsequent_evaluations(c: &mut Criterion) {
|
|||||||
let context = RenderConfig::default();
|
let context = RenderConfig::default();
|
||||||
bench_for_each_demo(&mut group, |name, g| {
|
bench_for_each_demo(&mut group, |name, g| {
|
||||||
let (executor, _) = setup_network(name);
|
let (executor, _) = setup_network(name);
|
||||||
g.bench_function(name, |b| {
|
g.bench_function(name, |b| b.iter(|| Executor::execute(&&executor, std::hint::black_box(context)).unwrap()));
|
||||||
b.iter(|| futures::executor::block_on(executor.tree().eval_tagged_value(executor.output(), std::hint::black_box(context))).unwrap())
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod benchmark_util;
|
mod benchmark_util;
|
||||||
|
|
||||||
use benchmark_util::setup_network;
|
use benchmark_util::setup_network;
|
||||||
|
use graph_craft::graphene_compiler::Executor;
|
||||||
use graphene_std::application_io::RenderConfig;
|
use graphene_std::application_io::RenderConfig;
|
||||||
use gungraun::prelude::*;
|
use gungraun::prelude::*;
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
@@ -11,7 +12,7 @@ fn setup_run_cached(name: &str) -> DynamicExecutor {
|
|||||||
|
|
||||||
// Warm up the cache by running once
|
// Warm up the cache by running once
|
||||||
let context = RenderConfig::default();
|
let context = RenderConfig::default();
|
||||||
let _ = futures::executor::block_on(executor.tree().eval_tagged_value(executor.output(), context));
|
let _ = Executor::execute(&&executor, context);
|
||||||
|
|
||||||
executor
|
executor
|
||||||
}
|
}
|
||||||
@@ -20,7 +21,7 @@ fn setup_run_cached(name: &str) -> DynamicExecutor {
|
|||||||
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_run_cached)]
|
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_run_cached)]
|
||||||
pub fn run_cached(executor: DynamicExecutor) -> DynamicExecutor {
|
pub fn run_cached(executor: DynamicExecutor) -> DynamicExecutor {
|
||||||
let context = RenderConfig::default();
|
let context = RenderConfig::default();
|
||||||
black_box(futures::executor::block_on(executor.tree().eval_tagged_value(executor.output(), black_box(context))).unwrap());
|
black_box(Executor::execute(&&executor, black_box(context)).unwrap());
|
||||||
|
|
||||||
// Return the executor so its teardown happens outside the measured section
|
// Return the executor so its teardown happens outside the measured section
|
||||||
executor
|
executor
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ use interpreted_executor::dynamic_executor::DynamicExecutor;
|
|||||||
fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
||||||
let network = load_from_name(name);
|
let network = load_from_name(name);
|
||||||
let proto_network = compile(network);
|
let proto_network = compile(network);
|
||||||
let empty = ProtoNetwork::default();
|
|
||||||
|
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
|
||||||
|
|
||||||
c.bench_function(name, |b| {
|
c.bench_function(name, |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| (executor.clone(), proto_network.clone()),
|
|| (DynamicExecutor::new(ProtoNetwork::default()).unwrap(), proto_network.clone()),
|
||||||
|(mut executor, network)| futures::executor::block_on(executor.update(std::hint::black_box(network))),
|
|(mut executor, network)| executor.update(std::hint::black_box(network)),
|
||||||
criterion::BatchSize::SmallInput,
|
criterion::BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -33,10 +30,10 @@ fn run_once<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
|||||||
let network = load_from_name(name);
|
let network = load_from_name(name);
|
||||||
let proto_network = compile(network);
|
let proto_network = compile(network);
|
||||||
|
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(proto_network)).unwrap();
|
let executor = DynamicExecutor::new(proto_network).unwrap();
|
||||||
let footprint = Footprint::default();
|
let footprint = Footprint::default();
|
||||||
|
|
||||||
c.bench_function(name, |b| b.iter(|| futures::executor::block_on((&executor).execute(footprint))));
|
c.bench_function(name, |b| b.iter(|| (&executor).execute(footprint)));
|
||||||
}
|
}
|
||||||
fn run_once_demo(c: &mut Criterion) {
|
fn run_once_demo(c: &mut Criterion) {
|
||||||
let mut g = c.benchmark_group("Run Once no render");
|
let mut g = c.benchmark_group("Run Once no render");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ mod benchmark_util;
|
|||||||
|
|
||||||
use benchmark_util::{bench_for_each_demo, setup_network};
|
use benchmark_util::{bench_for_each_demo, setup_network};
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{Criterion, criterion_group, criterion_main};
|
||||||
|
use graph_craft::graphene_compiler::Executor;
|
||||||
use graphene_std::application_io::RenderConfig;
|
use graphene_std::application_io::RenderConfig;
|
||||||
|
|
||||||
fn run_once(c: &mut Criterion) {
|
fn run_once(c: &mut Criterion) {
|
||||||
@@ -11,7 +12,7 @@ fn run_once(c: &mut Criterion) {
|
|||||||
g.bench_function(name, |b| {
|
g.bench_function(name, |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| setup_network(name),
|
|| setup_network(name),
|
||||||
|(executor, _)| futures::executor::block_on(executor.tree().eval_tagged_value(executor.output(), std::hint::black_box(context))).unwrap(),
|
|(executor, _)| Executor::execute(&&executor, std::hint::black_box(context)).unwrap(),
|
||||||
criterion::BatchSize::SmallInput,
|
criterion::BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod benchmark_util;
|
mod benchmark_util;
|
||||||
|
|
||||||
use benchmark_util::setup_network;
|
use benchmark_util::setup_network;
|
||||||
|
use graph_craft::graphene_compiler::Executor;
|
||||||
use graphene_std::application_io;
|
use graphene_std::application_io;
|
||||||
use gungraun::prelude::*;
|
use gungraun::prelude::*;
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
@@ -15,7 +16,7 @@ fn setup_run_once(name: &str) -> DynamicExecutor {
|
|||||||
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_run_once)]
|
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_run_once)]
|
||||||
pub fn run_once(executor: DynamicExecutor) -> DynamicExecutor {
|
pub fn run_once(executor: DynamicExecutor) -> DynamicExecutor {
|
||||||
let context = application_io::RenderConfig::default();
|
let context = application_io::RenderConfig::default();
|
||||||
black_box(futures::executor::block_on(executor.tree().eval_tagged_value(executor.output(), black_box(context))).unwrap());
|
black_box(Executor::execute(&&executor, black_box(context)).unwrap());
|
||||||
|
|
||||||
// Return the executor so its teardown happens outside the measured section
|
// Return the executor so its teardown happens outside the measured section
|
||||||
executor
|
executor
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ fn update_executor(c: &mut Criterion) {
|
|||||||
|| {
|
|| {
|
||||||
let (_, proto_network) = setup_network(name);
|
let (_, proto_network) = setup_network(name);
|
||||||
let empty = ProtoNetwork::default();
|
let empty = ProtoNetwork::default();
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
let executor = DynamicExecutor::new(empty).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
},
|
},
|
||||||
|(mut executor, network)| futures::executor::block_on(executor.update(std::hint::black_box(network))),
|
|(mut executor, network)| executor.update(std::hint::black_box(network)),
|
||||||
criterion::BatchSize::SmallInput,
|
criterion::BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::hint::black_box;
|
|||||||
fn setup_update_executor(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
fn setup_update_executor(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
||||||
let (_, proto_network) = setup_network(name);
|
let (_, proto_network) = setup_network(name);
|
||||||
let empty = ProtoNetwork::default();
|
let empty = ProtoNetwork::default();
|
||||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
let executor = DynamicExecutor::new(empty).unwrap();
|
||||||
(executor, proto_network)
|
(executor, proto_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ fn setup_update_executor(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
|||||||
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_update_executor)]
|
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = setup_update_executor)]
|
||||||
pub fn update_executor(setup: (DynamicExecutor, ProtoNetwork)) -> DynamicExecutor {
|
pub fn update_executor(setup: (DynamicExecutor, ProtoNetwork)) -> DynamicExecutor {
|
||||||
let (mut executor, network) = setup;
|
let (mut executor, network) = setup;
|
||||||
let _ = black_box(futures::executor::block_on(executor.update(black_box(network))));
|
let _ = black_box(executor.update(black_box(network)));
|
||||||
|
|
||||||
// Return the executor so its teardown happens outside the measured section
|
// Return the executor so its teardown happens outside the measured section
|
||||||
executor
|
executor
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ use std::sync::{Arc, Mutex, PoisonError};
|
|||||||
|
|
||||||
const ARENA_CAPACITY: usize = 1 << 20;
|
const ARENA_CAPACITY: usize = 1 << 20;
|
||||||
|
|
||||||
|
fn new_arena() -> Arena {
|
||||||
|
Arena::new(ARENA_CAPACITY).unwrap_or_else(|| {
|
||||||
|
log::error!("arena generations exhausted; continuing without frame caching");
|
||||||
|
Arena::parked()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
||||||
pub struct DynamicExecutor {
|
pub struct DynamicExecutor {
|
||||||
output: NodeId,
|
output: NodeId,
|
||||||
@@ -42,7 +49,7 @@ impl Default for DynamicExecutor {
|
|||||||
tree: Default::default(),
|
tree: Default::default(),
|
||||||
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
||||||
orphaned_nodes: HashSet::new(),
|
orphaned_nodes: HashSet::new(),
|
||||||
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
arena: Mutex::new(new_arena()),
|
||||||
runtime: noop_runtime(),
|
runtime: noop_runtime(),
|
||||||
live_sources: Vec::new(),
|
live_sources: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -78,7 +85,7 @@ impl DynamicExecutor {
|
|||||||
output,
|
output,
|
||||||
typing_context,
|
typing_context,
|
||||||
orphaned_nodes: HashSet::new(),
|
orphaned_nodes: HashSet::new(),
|
||||||
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
arena: Mutex::new(new_arena()),
|
||||||
runtime,
|
runtime,
|
||||||
live_sources: sources,
|
live_sources: sources,
|
||||||
})
|
})
|
||||||
@@ -480,7 +487,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_root_builds_the_bare_root_with_the_call_argument_as_vararg_0() {
|
fn eval_root_builds_the_bare_root_with_the_call_argument_as_vararg_0() {
|
||||||
let mut arena = Arena::new(64);
|
let mut arena = Arena::new(64).unwrap();
|
||||||
let runtime = GraphRuntime::new(InertSpawner);
|
let runtime = GraphRuntime::new(InertSpawner);
|
||||||
let argument = 21.5f64;
|
let argument = 21.5f64;
|
||||||
let result = eval_root(&mut arena, &runtime, &argument, |ctx| {
|
let result = eval_root(&mut arena, &runtime, &argument, |ctx| {
|
||||||
@@ -492,7 +499,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_root_resets_the_arena_at_eval_start() {
|
fn eval_root_resets_the_arena_at_eval_start() {
|
||||||
let mut arena = Arena::new(64);
|
let mut arena = Arena::new(64).unwrap();
|
||||||
let runtime = GraphRuntime::new(InertSpawner);
|
let runtime = GraphRuntime::new(InertSpawner);
|
||||||
let cell = ArenaCell::new();
|
let cell = ArenaCell::new();
|
||||||
eval_root(&mut arena, &runtime, &(), |ctx| {
|
eval_root(&mut arena, &runtime, &(), |ctx| {
|
||||||
@@ -509,7 +516,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_panicking_eval_reports_the_error_and_resets_the_arena() {
|
fn a_panicking_eval_reports_the_error_and_resets_the_arena() {
|
||||||
let mut arena = Arena::new(64);
|
let mut arena = Arena::new(64).unwrap();
|
||||||
let runtime = GraphRuntime::new(InertSpawner);
|
let runtime = GraphRuntime::new(InertSpawner);
|
||||||
let cell = ArenaCell::new();
|
let cell = ArenaCell::new();
|
||||||
let result: GPoll<()> = eval_root(&mut arena, &runtime, &(), |ctx| {
|
let result: GPoll<()> = eval_root(&mut arena, &runtime, &(), |ctx| {
|
||||||
@@ -530,7 +537,7 @@ mod test {
|
|||||||
tree.push_node(NodeId(0), val_1_protonode, &context).unwrap();
|
tree.push_node(NodeId(0), val_1_protonode, &context).unwrap();
|
||||||
let _node = tree.get(NodeId(0)).unwrap();
|
let _node = tree.get(NodeId(0)).unwrap();
|
||||||
|
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ impl Arena {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An arena that refuses every allocation and resolves no handle, so a caller that
|
||||||
|
/// cannot fail can degrade instead of propagating exhaustion.
|
||||||
|
pub fn parked() -> Self {
|
||||||
|
LIVE_ARENAS.fetch_add(1, Ordering::Release);
|
||||||
|
Self {
|
||||||
|
generation: AtomicU64::new(PARKED_GENERATION),
|
||||||
|
offset: AtomicUsize::new(0),
|
||||||
|
buf: Box::new([]),
|
||||||
|
drops: Mutex::new(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generation(&self) -> u64 {
|
pub fn generation(&self) -> u64 {
|
||||||
self.generation.load(Ordering::Acquire)
|
self.generation.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ struct FrameSlot<T> {
|
|||||||
value: UnsafeCell<MaybeUninit<T>>,
|
value: UnsafeCell<MaybeUninit<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: the key CAS reserves a slot for one writer, and the Release store of its
|
||||||
|
// state publishes the value to every Acquire load in `lookup`, so concurrent access
|
||||||
|
// is ordered. Sharing the table hands out `&T` and drops `T` on whichever thread
|
||||||
|
// drops the table, which is what the `Send + Sync` bounds cover.
|
||||||
|
unsafe impl<T: Send + Sync, const CAP: usize> Sync for FrameTable<T, CAP> {}
|
||||||
|
unsafe impl<T: Send, const CAP: usize> Send for FrameTable<T, CAP> {}
|
||||||
|
|
||||||
pub enum Lookup<'t, T> {
|
pub enum Lookup<'t, T> {
|
||||||
Hit(Finality, &'t T),
|
Hit(Finality, &'t T),
|
||||||
Vacant(VacantSlot<'t, T>),
|
Vacant(VacantSlot<'t, T>),
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ mod tests {
|
|||||||
|
|
||||||
type ErasedSplitEdge = dyn for<'c> Node<ContextImpl<'c>, Output = SplitBorrow<'c>> + Send + Sync;
|
type ErasedSplitEdge = dyn for<'c> Node<ContextImpl<'c>, Output = SplitBorrow<'c>> + Send + Sync;
|
||||||
|
|
||||||
let arena = Arena::new(4096);
|
let arena = Arena::new(4096).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -391,7 +391,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -443,7 +443,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -493,7 +493,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duplicated_edges_share_one_instance_and_outlive_each_other() {
|
fn duplicated_edges_share_one_instance_and_outlive_each_other() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn async_source_spawns_once_and_lands_via_the_slot() {
|
fn async_source_spawns_once_and_lands_via_the_slot() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -301,7 +301,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn async_source_reports_the_placeholder_while_in_flight() {
|
fn async_source_reports_the_placeholder_while_in_flight() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -316,7 +316,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_partial_maps_the_placeholder_frame_to_pending() {
|
fn no_partial_maps_the_placeholder_frame_to_pending() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -331,7 +331,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prologue_runs_sync_and_spawns_once() {
|
fn prologue_runs_sync_and_spawns_once() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -350,7 +350,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prologue_interrupt_defers_the_spawn() {
|
fn prologue_interrupt_defers_the_spawn() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -369,7 +369,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn async_kernels_read_captured_varargs() {
|
fn async_kernels_read_captured_varargs() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let root = ContextImpl::root(&scope);
|
let root = ContextImpl::root(&scope);
|
||||||
@@ -390,7 +390,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn async_kernels_read_the_captured_context_snapshot() {
|
fn async_kernels_read_the_captured_context_snapshot() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let root = ContextImpl::root(&scope);
|
let root = ContextImpl::root(&scope);
|
||||||
@@ -484,7 +484,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_source_slot_lands_through_the_runtime_while_downstream_keys_invalidate() {
|
fn a_source_slot_lands_through_the_runtime_while_downstream_keys_invalidate() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let runtime = Arc::new(GraphRuntime::new(CollectSpawner::default()));
|
let runtime = Arc::new(GraphRuntime::new(CollectSpawner::default()));
|
||||||
runtime.retain_sources(&[11]);
|
runtime.retain_sources(&[11]);
|
||||||
let graph = EpilogueDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(11u64));
|
let graph = EpilogueDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(11u64));
|
||||||
|
|||||||
@@ -46,6 +46,6 @@ fn context_modification<T>(
|
|||||||
/// The parts of the context to keep when evaluating the input value. All other parts are nullified.
|
/// The parts of the context to keep when evaluating the input value. All other parts are nullified.
|
||||||
modification: ContextModification,
|
modification: ContextModification,
|
||||||
) -> GPoll<T> {
|
) -> GPoll<T> {
|
||||||
let scope = ctx.scope().nullified(modification.features, Some(&modification.sources));
|
let scope = ctx.scope().nullified(modification.features, Some(modification.sources()));
|
||||||
value.eval(&ctx.nullified(modification.features, &scope))
|
value.eval(&ctx.nullified(modification.features, &scope))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""), path(graphene_core::memo), skip_impl, extent(frame_memo_extent))]
|
#[node_macro::node(category(""), path(graphene_core::memo), skip_impl, extent(frame_memo_extent))]
|
||||||
fn frame_memo<'e, T: Clone + 'static>(ctx: impl Ctx + CacheHash + ExtractArena<'e>, #[data] cell: ArenaCell<FrameTable<T, 32>>, content: impl Node<Context<'_>, Output = T>) -> GPoll<&'e T> {
|
fn frame_memo<'e, T: Clone + 'static + Send + Sync>(
|
||||||
|
ctx: impl Ctx + CacheHash + ExtractArena<'e>,
|
||||||
|
#[data] cell: ArenaCell<FrameTable<T, 32>>,
|
||||||
|
content: impl Node<Context<'_>, Output = T>,
|
||||||
|
) -> GPoll<&'e T> {
|
||||||
let arena = ctx.arena();
|
let arena = ctx.arena();
|
||||||
let table = match cell.load(arena) {
|
let table = match cell.load(arena) {
|
||||||
Some(table) => table,
|
Some(table) => table,
|
||||||
@@ -70,13 +74,13 @@ fn frame_memo<'e, T: Clone + 'static>(ctx: impl Ctx + CacheHash + ExtractArena<'
|
|||||||
|
|
||||||
fn frame_memo_extent<C, T, NodeContent>(node: &FrameMemoNode<T, NodeContent>, ctx: &C) -> GPoll<Extent>
|
fn frame_memo_extent<C, T, NodeContent>(node: &FrameMemoNode<T, NodeContent>, ctx: &C) -> GPoll<Extent>
|
||||||
where
|
where
|
||||||
T: Clone + 'static,
|
T: Clone + 'static + Send + Sync,
|
||||||
NodeContent: Node<C, Output = T>,
|
NodeContent: Node<C, Output = T>,
|
||||||
{
|
{
|
||||||
node.content.extent(ctx)
|
node.content.extent(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn park<T>(arena: &Arena, result: GPoll<T>) -> GPoll<&T> {
|
pub fn park<T: Send + Sync>(arena: &Arena, result: GPoll<T>) -> GPoll<&T> {
|
||||||
match result {
|
match result {
|
||||||
GPoll::Final(value) => match arena.alloc(value) {
|
GPoll::Final(value) => match arena.alloc(value) {
|
||||||
Some((parked, _)) => GPoll::Final(parked),
|
Some((parked, _)) => GPoll::Final(parked),
|
||||||
@@ -126,8 +130,6 @@ fn serialize_monitor<T: Clone + 'static + Send + Sync>(io: &MonitorValue<T>) ->
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core_types::SourceId;
|
use core_types::SourceId;
|
||||||
use core_types::Type;
|
|
||||||
use core_types::concrete;
|
|
||||||
use core_types::context::{ContextImpl, EvalScope};
|
use core_types::context::{ContextImpl, EvalScope};
|
||||||
use core_types::registry::{EdgeHandle, ErasedLendNode, ErasedNode};
|
use core_types::registry::{EdgeHandle, ErasedLendNode, ErasedNode};
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
@@ -168,7 +170,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn monitor_serialize_exposes_the_io_record_through_the_edge() {
|
fn monitor_serialize_exposes_the_io_record_through_the_edge() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -186,7 +188,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn memoize_caches_across_evals() {
|
fn memoize_caches_across_evals() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -199,7 +201,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn memo_invalidates_on_generation_bump() {
|
fn memo_invalidates_on_generation_bump() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let source: SourceId = 7;
|
let source: SourceId = 7;
|
||||||
let before = [(source, 1)];
|
let before = [(source, 1)];
|
||||||
let after = [(source, 2)];
|
let after = [(source, 2)];
|
||||||
@@ -215,7 +217,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn memo_replays_partiality_on_hit() {
|
fn memo_replays_partiality_on_hit() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -228,7 +230,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn memoized_edges_stack_and_rewire() {
|
fn memoized_edges_stack_and_rewire() {
|
||||||
let arena = Arena::new(1024);
|
let arena = Arena::new(1024).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
@@ -243,7 +245,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_memo_turns_an_owned_edge_into_a_lending_edge() {
|
fn frame_memo_turns_an_owned_edge_into_a_lending_edge() {
|
||||||
let arena = Arena::new(4096);
|
let arena = Arena::new(4096).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_context_builds_the_render_context_from_the_root_vararg() {
|
fn create_context_builds_the_render_context_from_the_root_vararg() {
|
||||||
let arena = Arena::new(256);
|
let arena = Arena::new(256).unwrap();
|
||||||
let generations = [];
|
let generations = [];
|
||||||
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||||
let root = ContextImpl::root(&scope);
|
let root = ContextImpl::root(&scope);
|
||||||
|
|||||||
@@ -989,8 +989,6 @@ fn normalize(_: impl Ctx, vector: DVec2) -> DVec2 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core_types::Node;
|
|
||||||
use core_types::generic::FnNode;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn dot_product_function() {
|
pub fn dot_product_function() {
|
||||||
@@ -1029,12 +1027,6 @@ mod test {
|
|||||||
assert_eq!(result, 0.);
|
assert_eq!(result, 0.);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn foo() {
|
|
||||||
let fnn = FnNode::new(|(a, b)| (b, a));
|
|
||||||
assert_eq!(fnn.eval((1u32, 2u32)), (2, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn add_vectors() {
|
pub fn add_vectors() {
|
||||||
assert_eq!(super::add(&(), DVec2::ONE, DVec2::ONE), DVec2::ONE * 2.);
|
assert_eq!(super::add(&(), DVec2::ONE, DVec2::ONE), DVec2::ONE * 2.);
|
||||||
@@ -1098,7 +1090,7 @@ mod graphene_test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generated_add_evaluates_through_the_node_path() {
|
fn generated_add_evaluates_through_the_node_path() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1108,7 +1100,7 @@ mod graphene_test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generated_add_batches_through_the_erased_edge() {
|
fn generated_add_batches_through_the_erased_edge() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1118,13 +1110,13 @@ mod graphene_test {
|
|||||||
let BatchStatus::Filled(lanes, finality) = status else {
|
let BatchStatus::Filled(lanes, finality) = status else {
|
||||||
panic!("expected filled, got {status:?}");
|
panic!("expected filled, got {status:?}");
|
||||||
};
|
};
|
||||||
assert_eq!(lanes, &[12.0, 13.0, 14.0, 15.0]);
|
assert_eq!(lanes.values(), &[12.0, 13.0, 14.0, 15.0]);
|
||||||
assert_eq!(finality, Finality::AllFinal);
|
assert_eq!(finality, Finality::AllFinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generated_wire_constructor_resolves_and_wires() {
|
fn generated_wire_constructor_resolves_and_wires() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1148,16 +1140,16 @@ mod graphene_test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generic_add_registers_one_entry_per_implementation() {
|
fn generic_add_registers_one_entry_per_implementation() {
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
let entries = add_entries();
|
let entries = add_entries();
|
||||||
assert_eq!(entries.len(), 6);
|
assert_eq!(entries.len(), 6);
|
||||||
assert_eq!(entries[0].io.inputs, vec![core_types::concrete!(f64), core_types::concrete!(f64)]);
|
assert_eq!(entries[0].io.inputs, vec![core_types::concrete!(f64), core_types::concrete!(f64)]);
|
||||||
assert_eq!(entries[0].io.output, core_types::concrete!(f64));
|
assert_eq!(entries[0].io.return_value, core_types::concrete!(f64));
|
||||||
assert_eq!(entries[3].io.inputs, vec![core_types::concrete!(DVec2), core_types::concrete!(DVec2)]);
|
assert_eq!(entries[3].io.inputs, vec![core_types::concrete!(DVec2), core_types::concrete!(DVec2)]);
|
||||||
assert_eq!(entries[3].io.output, core_types::concrete!(DVec2));
|
assert_eq!(entries[3].io.return_value, core_types::concrete!(DVec2));
|
||||||
|
|
||||||
let augend = EdgeHandle::new(Arc::new(SourceNode(1.5f64)) as Arc<ErasedNode<f64>>);
|
let augend = EdgeHandle::new(Arc::new(SourceNode(1.5f64)) as Arc<ErasedNode<f64>>);
|
||||||
let addend = EdgeHandle::new(Arc::new(SourceNode(2.5f64)) as Arc<ErasedNode<f64>>);
|
let addend = EdgeHandle::new(Arc::new(SourceNode(2.5f64)) as Arc<ErasedNode<f64>>);
|
||||||
@@ -1182,7 +1174,7 @@ mod graphene_test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1217,7 +1209,7 @@ mod graphene_test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1240,7 +1232,7 @@ mod graphene_test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
@@ -1260,7 +1252,7 @@ mod graphene_test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arena = Arena::new(64);
|
let arena = Arena::new(64).unwrap();
|
||||||
let scope = scope_fixture(&arena);
|
let scope = scope_fixture(&arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
|
|||||||
@@ -201,8 +201,8 @@ mod test {
|
|||||||
use raster_types::Image;
|
use raster_types::Image;
|
||||||
use raster_types::Raster;
|
use raster_types::Raster;
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn color_overlay_multiply() {
|
fn color_overlay_multiply() {
|
||||||
let image_color = Color::from_rgbaf32_unchecked(0.7, 0.6, 0.5, 0.4);
|
let image_color = Color::from_rgbaf32_unchecked(0.7, 0.6, 0.5, 0.4);
|
||||||
let image = Image::new(1, 1, image_color);
|
let image = Image::new(1, 1, image_color);
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ mod test {
|
|||||||
// 100% of the output should come from the multiplied value
|
// 100% of the output should come from the multiplied value
|
||||||
let opacity = 100.;
|
let opacity = 100.;
|
||||||
|
|
||||||
let result = super::color_overlay((), List::new_from_element(Raster::new_cpu(image.clone())), overlay_color, BlendMode::Multiply, opacity);
|
let result = super::color_overlay(&(), List::new_from_element(Raster::new_cpu(image.clone())), overlay_color, BlendMode::Multiply, opacity);
|
||||||
let result = result.element(0).unwrap().clone();
|
let result = result.element(0).unwrap().clone();
|
||||||
|
|
||||||
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_image_color_palette() {
|
fn test_image_color_palette() {
|
||||||
let result = image_color_palette(
|
let result = image_color_palette(
|
||||||
(),
|
&(),
|
||||||
List::new_from_element(Raster::new_cpu(Image {
|
List::new_from_element(Raster::new_cpu(Image {
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
@@ -77,6 +77,6 @@ mod test {
|
|||||||
})),
|
})),
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
assert_eq!(futures::executor::block_on(result), List::new_from_element(Color::from_rgbaf32(0., 0., 0., 1.).unwrap()));
|
assert_eq!(result, List::new_from_element(Color::from_rgbaf32(0., 0., 0., 1.).unwrap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -411,11 +411,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn isometric_grid_test() {
|
fn isometric_grid_test() {
|
||||||
// Doesn't crash with weird angles
|
// Doesn't crash with weird angles
|
||||||
grid((), (), GridType::Isometric, 0., 5, 5, (0., 0.).into());
|
grid(&(), (), GridType::Isometric, 0., 5, 5, (0., 0.).into());
|
||||||
grid((), (), GridType::Isometric, 90., 5, 5, (90., 90.).into());
|
grid(&(), (), GridType::Isometric, 90., 5, 5, (90., 90.).into());
|
||||||
|
|
||||||
// Works properly
|
// Works properly
|
||||||
let grid = grid((), (), GridType::Isometric, 10., 5, 5, (30., 30.).into());
|
let grid = grid(&(), (), GridType::Isometric, 10., 5, 5, (30., 30.).into());
|
||||||
assert_eq!(grid.element(0).unwrap().point_domain.ids().len(), 5 * 5);
|
assert_eq!(grid.element(0).unwrap().point_domain.ids().len(), 5 * 5);
|
||||||
assert_eq!(grid.element(0).unwrap().segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
assert_eq!(grid.element(0).unwrap().segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||||
for (_, bezier, _, _) in grid.element(0).unwrap().segment_bezier_iter() {
|
for (_, bezier, _, _) in grid.element(0).unwrap().segment_bezier_iter() {
|
||||||
@@ -430,7 +430,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn skew_isometric_grid_test() {
|
fn skew_isometric_grid_test() {
|
||||||
let grid = grid((), (), GridType::Isometric, 10., 5, 5, (40., 30.).into());
|
let grid = grid(&(), (), GridType::Isometric, 10., 5, 5, (40., 30.).into());
|
||||||
assert_eq!(grid.element(0).unwrap().point_domain.ids().len(), 5 * 5);
|
assert_eq!(grid.element(0).unwrap().point_domain.ids().len(), 5 * 5);
|
||||||
assert_eq!(grid.element(0).unwrap().segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
assert_eq!(grid.element(0).unwrap().segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||||
for (_, bezier, _, _) in grid.element(0).unwrap().segment_bezier_iter() {
|
for (_, bezier, _, _) in grid.element(0).unwrap().segment_bezier_iter() {
|
||||||
@@ -443,7 +443,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn qr_code_test() {
|
fn qr_code_test() {
|
||||||
let qr = qr_code((), (), "https://graphite.art".to_string(), false, 1., QRCodeErrorCorrectionLevel::Low, true);
|
let qr = qr_code(&(), (), "https://graphite.art".to_string(), false, 1., QRCodeErrorCorrectionLevel::Low, true);
|
||||||
assert!(qr.element(0).unwrap().point_domain.ids().len() > 0);
|
assert!(qr.element(0).unwrap().point_domain.ids().len() > 0);
|
||||||
assert!(qr.element(0).unwrap().segment_domain.ids().len() > 0);
|
assert!(qr.element(0).unwrap().segment_domain.ids().len() > 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3261,24 +3261,10 @@ fn centroid(ctx: impl Ctx + DeriveCtx, content: impl Node<Context<'_>, Output =
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use core_types::Node;
|
|
||||||
use kurbo::{CubicBez, Ellipse, Point, Rect};
|
use kurbo::{CubicBez, Ellipse, Point, Rect};
|
||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use vector_types::vector::algorithms::bezpath_algorithms::{TValue, trim_pathseg};
|
use vector_types::vector::algorithms::bezpath_algorithms::{TValue, trim_pathseg};
|
||||||
use vector_types::vector::misc::pathseg_abs_diff_eq;
|
use vector_types::vector::misc::pathseg_abs_diff_eq;
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct FutureWrapperNode<T: Clone>(T);
|
|
||||||
|
|
||||||
impl<'i, T: 'i + Clone + Send> Node<'i, Footprint> for FutureWrapperNode<T> {
|
|
||||||
type Output = Pin<Box<dyn Future<Output = T> + 'i + Send>>;
|
|
||||||
fn eval(&'i self, _input: Footprint) -> Self::Output {
|
|
||||||
let value = self.0.clone();
|
|
||||||
Box::pin(async move { value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vector_node_from_bezpath(bezpath: BezPath) -> List<Vector> {
|
fn vector_node_from_bezpath(bezpath: BezPath) -> List<Vector> {
|
||||||
List::new_from_element(Vector::from_bezpath(bezpath))
|
List::new_from_element(Vector::from_bezpath(bezpath))
|
||||||
}
|
}
|
||||||
@@ -3289,9 +3275,9 @@ mod test {
|
|||||||
Item::new_from_element(row).with_attribute(ATTR_TRANSFORM, transform)
|
Item::new_from_element(row).with_attribute(ATTR_TRANSFORM, transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bounding_box() {
|
fn bounding_box() {
|
||||||
let bounding_box = super::bounding_box((), vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY))).await;
|
let bounding_box = super::bounding_box(&(), vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY)));
|
||||||
let bounding_box = bounding_box.element(0).unwrap();
|
let bounding_box = bounding_box.element(0).unwrap();
|
||||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||||
let manipulator_groups_anchors = bounding_box
|
let manipulator_groups_anchors = bounding_box
|
||||||
@@ -3309,7 +3295,7 @@ mod test {
|
|||||||
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
||||||
let mut square = List::new_from_element(square);
|
let mut square = List::new_from_element(square);
|
||||||
square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
|
square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
|
||||||
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
let bounding_box = super::bounding_box(&(), square);
|
||||||
let bounding_box = bounding_box.element(0).unwrap();
|
let bounding_box = bounding_box.element(0).unwrap();
|
||||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||||
let manipulator_groups_anchors = bounding_box
|
let manipulator_groups_anchors = bounding_box
|
||||||
@@ -3326,15 +3312,15 @@ mod test {
|
|||||||
assert_eq!(manipulator_groups_anchors[i], expected_bounding_box[i]);
|
assert_eq!(manipulator_groups_anchors[i], expected_bounding_box[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn copy_to_points() {
|
fn copy_to_points() {
|
||||||
let points = Rect::new(-10., -10., 10., 10.).to_path(DEFAULT_ACCURACY);
|
let points = Rect::new(-10., -10., 10., 10.).to_path(DEFAULT_ACCURACY);
|
||||||
let element = Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY);
|
let element = Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY);
|
||||||
|
|
||||||
let expected_points = Vector::from_bezpath(points.clone()).point_domain.positions().to_vec();
|
let expected_points = Vector::from_bezpath(points.clone()).point_domain.positions().to_vec();
|
||||||
|
|
||||||
let copy_to_points = super::copy_to_points(Footprint::default(), vector_node_from_bezpath(points), vector_node_from_bezpath(element), 1., 1., 0., 0, 0., 0).await;
|
let copy_to_points = super::copy_to_points(&Footprint::default(), vector_node_from_bezpath(points), vector_node_from_bezpath(element), 1., 1., 0., 0, 0., 0);
|
||||||
let flatten_path = super::flatten_path(Footprint::default(), copy_to_points).await;
|
let flatten_path = super::flatten_path(&Footprint::default(), copy_to_points);
|
||||||
let flattened_copy_to_points = flatten_path.element(0).unwrap();
|
let flattened_copy_to_points = flatten_path.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(flattened_copy_to_points.region_manipulator_groups().count(), expected_points.len());
|
assert_eq!(flattened_copy_to_points.region_manipulator_groups().count(), expected_points.len());
|
||||||
@@ -3349,35 +3335,34 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn sample_polyline() {
|
fn sample_polyline() {
|
||||||
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
||||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node_from_bezpath(path), PointSpacingType::Separation, 30., 0, 0., 0., false).await;
|
let sample_polyline = super::sample_polyline(&Footprint::default(), vector_node_from_bezpath(path), PointSpacingType::Separation, 30., 0, 0., 0., false);
|
||||||
let sample_polyline = sample_polyline.element(0).unwrap();
|
let sample_polyline = sample_polyline.element(0).unwrap();
|
||||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
||||||
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn sample_polyline_adaptive_spacing() {
|
fn sample_polyline_adaptive_spacing() {
|
||||||
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
let path = BezPath::from_vec(vec![PathEl::MoveTo(Point::ZERO), PathEl::CurveTo(Point::ZERO, Point::new(100., 0.), Point::new(100., 0.))]);
|
||||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node_from_bezpath(path), PointSpacingType::Separation, 18., 0, 45., 10., true).await;
|
let sample_polyline = super::sample_polyline(&Footprint::default(), vector_node_from_bezpath(path), PointSpacingType::Separation, 18., 0, 45., 10., true);
|
||||||
let sample_polyline = sample_polyline.element(0).unwrap();
|
let sample_polyline = sample_polyline.element(0).unwrap();
|
||||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
||||||
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
assert!(pos.distance(expected) < 1e-3, "Expected {expected} found {pos}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn poisson() {
|
fn poisson() {
|
||||||
let poisson_points = super::scatter_points(
|
let poisson_points = super::scatter_points(
|
||||||
Footprint::default(),
|
&Footprint::default(),
|
||||||
vector_node_from_bezpath(Ellipse::from_rect(Rect::new(-50., -50., 50., 50.)).to_path(DEFAULT_ACCURACY)),
|
vector_node_from_bezpath(Ellipse::from_rect(Rect::new(-50., -50., 50., 50.)).to_path(DEFAULT_ACCURACY)),
|
||||||
10. * std::f64::consts::SQRT_2,
|
10. * std::f64::consts::SQRT_2,
|
||||||
0,
|
0,
|
||||||
)
|
);
|
||||||
.await;
|
|
||||||
let poisson_points = poisson_points.element(0).unwrap();
|
let poisson_points = poisson_points.element(0).unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
(20..=40).contains(&poisson_points.point_domain.positions().len()),
|
(20..=40).contains(&poisson_points.point_domain.positions().len()),
|
||||||
@@ -3388,33 +3373,33 @@ mod test {
|
|||||||
assert!(point.length() < 50. + 1., "Expected point in circle {point}")
|
assert!(point.length() < 50. + 1., "Expected point in circle {point}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn path_length() {
|
fn path_length() {
|
||||||
let bezpath = Rect::new(100., 100., 201., 201.).to_path(DEFAULT_ACCURACY);
|
let bezpath = Rect::new(100., 100., 201., 201.).to_path(DEFAULT_ACCURACY);
|
||||||
let transform = DAffine2::from_scale(DVec2::new(2., 2.));
|
let transform = DAffine2::from_scale(DVec2::new(2., 2.));
|
||||||
let row = create_vector_item(bezpath, transform);
|
let row = create_vector_item(bezpath, transform);
|
||||||
let list = (0..5).map(|_| row.clone()).collect::<List<Vector>>();
|
let list = (0..5).map(|_| row.clone()).collect::<List<Vector>>();
|
||||||
|
|
||||||
let length = super::path_length(Footprint::default(), list).await;
|
let length = super::path_length(&Footprint::default(), list);
|
||||||
|
|
||||||
// 101 (each rectangle edge length) * 4 (rectangle perimeter) * 2 (scale) * 5 (number of rows)
|
// 101 (each rectangle edge length) * 4 (rectangle perimeter) * 2 (scale) * 5 (number of rows)
|
||||||
assert_eq!(length, 101. * 4. * 2. * 5.);
|
assert_eq!(length, 101. * 4. * 2. * 5.);
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn spline() {
|
fn spline() {
|
||||||
let spline = super::spline(Footprint::default(), vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY))).await;
|
let spline = super::spline(&Footprint::default(), vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY)));
|
||||||
let spline = spline.element(0).unwrap();
|
let spline = spline.element(0).unwrap();
|
||||||
assert_eq!(spline.stroke_bezpath_iter().count(), 1);
|
assert_eq!(spline.stroke_bezpath_iter().count(), 1);
|
||||||
assert_eq!(spline.point_domain.positions(), &[DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]);
|
assert_eq!(spline.point_domain.positions(), &[DVec2::ZERO, DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]);
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn morph() {
|
fn morph() {
|
||||||
let mut rectangles = vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
let mut rectangles = vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
||||||
let mut second_rectangle = rectangles.clone_item(0).unwrap();
|
let mut second_rectangle = rectangles.clone_item(0).unwrap();
|
||||||
*second_rectangle.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM) *= DAffine2::from_translation((-100., -100.).into());
|
*second_rectangle.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM) *= DAffine2::from_translation((-100., -100.).into());
|
||||||
rectangles.push(second_rectangle);
|
rectangles.push(second_rectangle);
|
||||||
|
|
||||||
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), List::default()).await;
|
let morphed = super::morph(&Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), List::default());
|
||||||
let morphed_element = morphed.element(0).unwrap();
|
let morphed_element = morphed.element(0).unwrap();
|
||||||
// Geometry stays in local space (original rectangle coordinates)
|
// Geometry stays in local space (original rectangle coordinates)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -3425,8 +3410,8 @@ mod test {
|
|||||||
assert!((morphed.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0).translation - DVec2::new(-50., -50.)).length() < 1e-3);
|
assert!((morphed.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0).translation - DVec2::new(-50., -50.)).length() < 1e-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn morph_interpolates_fill() {
|
fn morph_interpolates_fill() {
|
||||||
let rect = || {
|
let rect = || {
|
||||||
let mut v = Vector::default();
|
let mut v = Vector::default();
|
||||||
v.append_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
v.append_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
||||||
@@ -3443,7 +3428,7 @@ mod test {
|
|||||||
let mut content = List::new_from_item(item_a);
|
let mut content = List::new_from_item(item_a);
|
||||||
content.push(item_b);
|
content.push(item_b);
|
||||||
|
|
||||||
let morphed = super::morph(Footprint::default(), content, 0.5, false, InterpolationDistribution::default(), List::default()).await;
|
let morphed = super::morph(&Footprint::default(), content, 0.5, false, InterpolationDistribution::default(), List::default());
|
||||||
|
|
||||||
let fill = graphic_list_at(&morphed, 0, ATTR_FILL).expect("Morph should keep the fill paint at the midpoint");
|
let fill = graphic_list_at(&morphed, 0, ATTR_FILL).expect("Morph should keep the fill paint at the midpoint");
|
||||||
|
|
||||||
@@ -3470,10 +3455,10 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bevel_rect() {
|
fn bevel_rect() {
|
||||||
let source = Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY);
|
let source = Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY);
|
||||||
let beveled = super::bevel(Footprint::default(), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
let beveled = super::bevel(&Footprint::default(), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = beveled.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 8);
|
assert_eq!(beveled.point_domain.positions().len(), 8);
|
||||||
@@ -3492,8 +3477,8 @@ mod test {
|
|||||||
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(10., 100.), Point::new(0., 90.))));
|
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(10., 100.), Point::new(0., 90.))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bevel_open_curve() {
|
fn bevel_open_curve() {
|
||||||
let curve = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(10., 0.), Point::new(10., 100.), Point::new(100., 0.)));
|
let curve = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(10., 0.), Point::new(10., 100.), Point::new(100., 0.)));
|
||||||
|
|
||||||
let mut source = BezPath::new();
|
let mut source = BezPath::new();
|
||||||
@@ -3501,7 +3486,7 @@ mod test {
|
|||||||
source.line_to(Point::ZERO);
|
source.line_to(Point::ZERO);
|
||||||
source.push(curve.as_path_el());
|
source.push(curve.as_path_el());
|
||||||
|
|
||||||
let beveled = super::bevel((), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
let beveled = super::bevel(&(), vector_node_from_bezpath(source), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = beveled.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||||
@@ -3516,8 +3501,8 @@ mod test {
|
|||||||
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(-8.2, 0.), trimmed.start())));
|
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(-8.2, 0.), trimmed.start())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bevel_with_transform() {
|
fn bevel_with_transform() {
|
||||||
let curve = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(10., 0.), Point::new(10., 100.), Point::new(100., 0.)));
|
let curve = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(10., 0.), Point::new(10., 100.), Point::new(100., 0.)));
|
||||||
|
|
||||||
let mut source = BezPath::new();
|
let mut source = BezPath::new();
|
||||||
@@ -3530,7 +3515,7 @@ mod test {
|
|||||||
|
|
||||||
vector_list.set_attribute(ATTR_TRANSFORM, 0, DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)));
|
vector_list.set_attribute(ATTR_TRANSFORM, 0, DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)));
|
||||||
|
|
||||||
let beveled = super::bevel((), List::new_from_element(vector), 2_f64.sqrt() * 10.);
|
let beveled = super::bevel(&(), List::new_from_element(vector), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = beveled.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||||
@@ -3545,15 +3530,15 @@ mod test {
|
|||||||
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(-8.2, 0.), trimmed.start())));
|
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(-8.2, 0.), trimmed.start())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bevel_too_high() {
|
fn bevel_too_high() {
|
||||||
let mut source = BezPath::new();
|
let mut source = BezPath::new();
|
||||||
source.move_to(Point::ZERO);
|
source.move_to(Point::ZERO);
|
||||||
source.line_to(Point::new(100., 0.));
|
source.line_to(Point::new(100., 0.));
|
||||||
source.line_to(Point::new(100., 100.));
|
source.line_to(Point::new(100., 100.));
|
||||||
source.line_to(Point::new(0., 100.));
|
source.line_to(Point::new(0., 100.));
|
||||||
|
|
||||||
let beveled = super::bevel(Footprint::default(), vector_node_from_bezpath(source), 999.);
|
let beveled = super::bevel(&Footprint::default(), vector_node_from_bezpath(source), 999.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = beveled.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||||
@@ -3569,15 +3554,15 @@ mod test {
|
|||||||
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(100., 50.), Point::new(50., 100.))));
|
contains_segment(beveled.clone(), PathSeg::Line(Line::new(Point::new(100., 50.), Point::new(50., 100.))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn bevel_repeated_point() {
|
fn bevel_repeated_point() {
|
||||||
let line = PathSeg::Line(Line::new(Point::ZERO, Point::new(100., 0.)));
|
let line = PathSeg::Line(Line::new(Point::ZERO, Point::new(100., 0.)));
|
||||||
let point = PathSeg::Cubic(CubicBez::new(Point::new(100., 0.), Point::ZERO, Point::ZERO, Point::new(100., 0.)));
|
let point = PathSeg::Cubic(CubicBez::new(Point::new(100., 0.), Point::ZERO, Point::ZERO, Point::new(100., 0.)));
|
||||||
let curve = PathSeg::Cubic(CubicBez::new(Point::new(100., 0.), Point::new(110., 0.), Point::new(110., 200.), Point::new(200., 0.)));
|
let curve = PathSeg::Cubic(CubicBez::new(Point::new(100., 0.), Point::new(110., 0.), Point::new(110., 200.), Point::new(200., 0.)));
|
||||||
|
|
||||||
let subpath = BezPath::from_path_segments([line, point, curve].into_iter());
|
let subpath = BezPath::from_path_segments([line, point, curve].into_iter());
|
||||||
|
|
||||||
let beveled_list = super::bevel(Footprint::default(), vector_node_from_bezpath(subpath), 5.);
|
let beveled_list = super::bevel(&Footprint::default(), vector_node_from_bezpath(subpath), 5.);
|
||||||
let beveled = beveled_list.element(0).unwrap();
|
let beveled = beveled_list.element(0).unwrap();
|
||||||
|
|
||||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
trait Attr {
|
||||||
|
fn name() -> &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attr for bool {
|
||||||
|
fn name() {"condition"}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Opacity(f64);
|
||||||
|
|
||||||
|
impl Attr for Opacity {
|
||||||
|
fn name() {"opacity"}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
fn opacity<T>(_: impl Ctx, input: T, x: ReadAttr<bool>, opacity: WriteAttr<Opacity>) -> T {
|
||||||
|
if x {
|
||||||
|
*opacity = 1.;
|
||||||
|
}
|
||||||
|
input
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user