mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
Extract gelement-nodes
for nodes accepting both vector, raster and graphic element
This commit is contained in:
parent
4c75ddf936
commit
56ac8f3a68
16 changed files with 174 additions and 58 deletions
|
@ -1,52 +0,0 @@
|
|||
use crate::{Ctx, ExtractAnimationTime, ExtractTime};
|
||||
|
||||
const DAY: f64 = 1000. * 3600. * 24.;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, dyn_any::DynAny, Default, Hash, node_macro::ChoiceType, serde::Serialize, serde::Deserialize)]
|
||||
pub enum RealTimeMode {
|
||||
#[label("UTC")]
|
||||
Utc,
|
||||
Year,
|
||||
Hour,
|
||||
Minute,
|
||||
#[default]
|
||||
Second,
|
||||
Millisecond,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AnimationTimeMode {
|
||||
AnimationTime,
|
||||
FrameNumber,
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Animation"))]
|
||||
fn real_time(ctx: impl Ctx + ExtractTime, _primary: (), mode: RealTimeMode) -> f64 {
|
||||
let time = ctx.try_time().unwrap_or_default();
|
||||
// TODO: Implement proper conversion using and existing time implementation
|
||||
match mode {
|
||||
RealTimeMode::Utc => time,
|
||||
RealTimeMode::Year => (time / DAY / 365.25).floor() + 1970.,
|
||||
RealTimeMode::Hour => (time / 1000. / 3600.).floor() % 24.,
|
||||
RealTimeMode::Minute => (time / 1000. / 60.).floor() % 60.,
|
||||
|
||||
RealTimeMode::Second => (time / 1000.).floor() % 60.,
|
||||
RealTimeMode::Millisecond => time % 1000.,
|
||||
}
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Animation"))]
|
||||
fn animation_time(ctx: impl Ctx + ExtractAnimationTime) -> f64 {
|
||||
ctx.try_animation_time().unwrap_or_default()
|
||||
}
|
||||
|
||||
// These nodes require more sophistcated algorithms for giving the correct result
|
||||
|
||||
// #[node_macro::node(category("Animation"))]
|
||||
// fn month(ctx: impl Ctx + ExtractTime) -> f64 {
|
||||
// ((ctx.try_time().unwrap_or_default() / DAY / 365.25 % 1.) * 12.).floor()
|
||||
// }
|
||||
// #[node_macro::node(category("Animation"))]
|
||||
// fn day(ctx: impl Ctx + ExtractTime) -> f64 {
|
||||
// (ctx.try_time().unwrap_or_default() / DAY
|
||||
// }
|
|
@ -1,175 +0,0 @@
|
|||
use crate::raster::Image;
|
||||
use crate::raster_types::{CPU, RasterDataTable};
|
||||
use crate::registry::types::Percentage;
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{BlendMode, Color, Ctx, GraphicElement, GraphicGroupTable};
|
||||
|
||||
pub(super) trait MultiplyAlpha {
|
||||
fn multiply_alpha(&mut self, factor: f64);
|
||||
}
|
||||
|
||||
impl MultiplyAlpha for Color {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
*self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.))
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for VectorDataTable {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for GraphicGroupTable {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for RasterDataTable<CPU>
|
||||
where
|
||||
GraphicElement: From<Image<Color>>,
|
||||
{
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.opacity *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) trait MultiplyFill {
|
||||
fn multiply_fill(&mut self, factor: f64);
|
||||
}
|
||||
impl MultiplyFill for Color {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
*self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.))
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for VectorDataTable {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.fill *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for GraphicGroupTable {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.fill *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for RasterDataTable<CPU> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.fill *= factor as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SetBlendMode {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode);
|
||||
}
|
||||
|
||||
impl SetBlendMode for VectorDataTable {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for GraphicGroupTable {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for RasterDataTable<CPU> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.blend_mode = blend_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SetClip {
|
||||
fn set_clip(&mut self, clip: bool);
|
||||
}
|
||||
|
||||
impl SetClip for VectorDataTable {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.clip = clip;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetClip for GraphicGroupTable {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.clip = clip;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SetClip for RasterDataTable<CPU> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
instance.alpha_blending.clip = clip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Style"))]
|
||||
fn blend_mode<T: SetBlendMode>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
GraphicGroupTable,
|
||||
VectorDataTable,
|
||||
RasterDataTable<CPU>,
|
||||
)]
|
||||
mut value: T,
|
||||
blend_mode: BlendMode,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its row in its parent table or Instance<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
value.set_blend_mode(blend_mode);
|
||||
value
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Style"))]
|
||||
fn opacity<T: MultiplyAlpha>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
GraphicGroupTable,
|
||||
VectorDataTable,
|
||||
RasterDataTable<CPU>,
|
||||
)]
|
||||
mut value: T,
|
||||
#[default(100.)] opacity: Percentage,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its row in its parent table or Instance<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
value.multiply_alpha(opacity / 100.);
|
||||
value
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Style"))]
|
||||
fn blending<T: SetBlendMode + MultiplyAlpha + MultiplyFill + SetClip>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
GraphicGroupTable,
|
||||
VectorDataTable,
|
||||
RasterDataTable<CPU>,
|
||||
)]
|
||||
mut value: T,
|
||||
blend_mode: BlendMode,
|
||||
#[default(100.)] opacity: Percentage,
|
||||
#[default(100.)] fill: Percentage,
|
||||
#[default(false)] clip: bool,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its row in its parent table or Instance<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
value.set_blend_mode(blend_mode);
|
||||
value.multiply_alpha(opacity / 100.);
|
||||
value.multiply_fill(fill / 100.);
|
||||
value.set_clip(clip);
|
||||
value
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod animation;
|
||||
pub mod blending;
|
||||
pub mod blending_nodes;
|
||||
pub mod bounds;
|
||||
pub mod color;
|
||||
pub mod consts;
|
||||
|
@ -14,7 +12,6 @@ pub mod generic;
|
|||
pub mod gradient;
|
||||
pub mod graphic_element;
|
||||
pub mod instances;
|
||||
pub mod logic;
|
||||
pub mod math;
|
||||
pub mod memo;
|
||||
pub mod misc;
|
||||
|
@ -26,7 +23,6 @@ pub mod render_complexity;
|
|||
pub mod structural;
|
||||
pub mod text;
|
||||
pub mod transform;
|
||||
pub mod transform_nodes;
|
||||
pub mod uuid;
|
||||
pub mod value;
|
||||
pub mod vector;
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
use crate::ArtboardGroupTable;
|
||||
use crate::Color;
|
||||
use crate::GraphicElement;
|
||||
use crate::GraphicGroupTable;
|
||||
use crate::gradient::GradientStops;
|
||||
use crate::graphene_core::registry::types::TextArea;
|
||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{Context, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2)] value: T) -> String {
|
||||
format!("{:?}", value)
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn string_concatenate(_: impl Ctx, #[implementations(String)] first: String, second: TextArea) -> String {
|
||||
first.clone() + &second
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn string_replace(_: impl Ctx, #[implementations(String)] string: String, from: TextArea, to: TextArea) -> String {
|
||||
string.replace(&from, &to)
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn string_slice(_: impl Ctx, #[implementations(String)] string: String, start: f64, end: f64) -> String {
|
||||
let start = if start < 0. { string.len() - start.abs() as usize } else { start as usize };
|
||||
let end = if end <= 0. { string.len() - end.abs() as usize } else { end as usize };
|
||||
let n = end.saturating_sub(start);
|
||||
string.char_indices().skip(start).take(n).map(|(_, c)| c).collect()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn string_length(_: impl Ctx, #[implementations(String)] string: String) -> usize {
|
||||
string.len()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
async fn switch<T, C: Send + 'n + Clone>(
|
||||
#[implementations(Context)] ctx: C,
|
||||
condition: bool,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
Context -> String,
|
||||
Context -> bool,
|
||||
Context -> f32,
|
||||
Context -> f64,
|
||||
Context -> u32,
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> ArtboardGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> GraphicElement,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
)]
|
||||
if_true: impl Node<C, Output = T>,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
Context -> String,
|
||||
Context -> bool,
|
||||
Context -> f32,
|
||||
Context -> f64,
|
||||
Context -> u32,
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> ArtboardGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> GraphicElement,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
)]
|
||||
if_false: impl Node<C, Output = T>,
|
||||
) -> T {
|
||||
if condition {
|
||||
// We can't remove these calls because we only want to evaluate the branch that we actually need
|
||||
if_true.eval(ctx).await
|
||||
} else {
|
||||
if_false.eval(ctx).await
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
use crate::instances::Instances;
|
||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use crate::transform::{ApplyTransform, Footprint, Transform};
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl};
|
||||
use core::f64;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn transform<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
)]
|
||||
transform_target: impl Node<Context<'static>, Output = Instances<T>>,
|
||||
translate: DVec2,
|
||||
rotate: f64,
|
||||
scale: DVec2,
|
||||
skew: DVec2,
|
||||
) -> Instances<T> {
|
||||
let matrix = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., skew.y, skew.x, 1., 0., 0.]);
|
||||
|
||||
let footprint = ctx.try_footprint().copied();
|
||||
|
||||
let mut ctx = OwnedContextImpl::from(ctx);
|
||||
if let Some(mut footprint) = footprint {
|
||||
footprint.apply_transform(&matrix);
|
||||
ctx = ctx.with_footprint(footprint);
|
||||
}
|
||||
|
||||
let mut transform_target = transform_target.eval(ctx.into_context()).await;
|
||||
|
||||
for data_transform in transform_target.instance_mut_iter() {
|
||||
*data_transform.transform = matrix * *data_transform.transform;
|
||||
}
|
||||
|
||||
transform_target
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
fn replace_transform<Data, TransformInput: Transform>(
|
||||
_: impl Ctx,
|
||||
#[implementations(VectorDataTable, RasterDataTable<CPU>, GraphicGroupTable)] mut data: Instances<Data>,
|
||||
#[implementations(DAffine2)] transform: TransformInput,
|
||||
) -> Instances<Data> {
|
||||
for data_transform in data.instance_mut_iter() {
|
||||
*data_transform.transform = transform.transform();
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn boundless_footprint<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> String,
|
||||
Context -> f64,
|
||||
)]
|
||||
transform_target: impl Node<Context<'static>, Output = T>,
|
||||
) -> T {
|
||||
let ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::BOUNDLESS);
|
||||
|
||||
transform_target.eval(ctx.into_context()).await
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn freeze_real_time<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> String,
|
||||
Context -> f64,
|
||||
)]
|
||||
transform_target: impl Node<Context<'static>, Output = T>,
|
||||
) -> T {
|
||||
let ctx = OwnedContextImpl::from(ctx).with_real_time(0.);
|
||||
|
||||
transform_target.eval(ctx.into_context()).await
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
use crate::instances::{InstanceRef, Instances};
|
||||
use crate::raster_types::{CPU, RasterDataTable};
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
||||
use glam::DVec2;
|
||||
|
||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
|
||||
points: VectorDataTable,
|
||||
#[implementations(
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> RasterDataTable<CPU>
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Instances<T>>,
|
||||
reverse: bool,
|
||||
) -> Instances<T> {
|
||||
let mut result_table = Instances::<T>::default();
|
||||
|
||||
for InstanceRef { instance: points, transform, .. } in points.instance_ref_iter() {
|
||||
let mut iteration = async |index, point| {
|
||||
let transformed_point = transform.transform_point2(point);
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(Box::new(transformed_point));
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
|
||||
for mut instanced in generated_instance.instance_iter() {
|
||||
instanced.transform.translation = transformed_point;
|
||||
result_table.push(instanced);
|
||||
}
|
||||
};
|
||||
|
||||
let range = points.point_domain.positions().iter().enumerate();
|
||||
if reverse {
|
||||
for (index, &point) in range.rev() {
|
||||
iteration(index, point).await;
|
||||
}
|
||||
} else {
|
||||
for (index, &point) in range {
|
||||
iteration(index, point).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> RasterDataTable<CPU>
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Instances<T>>,
|
||||
#[default(1)] count: u64,
|
||||
reverse: bool,
|
||||
) -> Instances<T> {
|
||||
let count = count.max(1) as usize;
|
||||
|
||||
let mut result_table = Instances::<T>::default();
|
||||
|
||||
for index in 0..count {
|
||||
let index = if reverse { count - index - 1 } else { index };
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
|
||||
for instanced in generated_instance.instance_iter() {
|
||||
result_table.push(instanced);
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 {
|
||||
match ctx.vararg(0).map(|dynamic| dynamic.downcast_ref::<DVec2>()) {
|
||||
Ok(Some(position)) => return *position,
|
||||
Ok(_) => warn!("Extracted value of incorrect type"),
|
||||
Err(e) => warn!("Cannot extract position vararg: {e:?}"),
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
|
||||
// TODO: Make this return a u32 instead of an f64, but we ned to improve math-related compatibility with integer types first.
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_index(ctx: impl Ctx + ExtractIndex) -> f64 {
|
||||
match ctx.try_index() {
|
||||
Some(index) => return index as f64,
|
||||
None => warn!("Extracted value of incorrect type"),
|
||||
}
|
||||
0.
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Node;
|
||||
use crate::extract_xy::{ExtractXyNode, XY};
|
||||
use crate::vector::VectorData;
|
||||
use bezier_rs::Subpath;
|
||||
use glam::DVec2;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FutureWrapperNode<T: Clone>(T);
|
||||
|
||||
impl<'i, I: Ctx, T: 'i + Clone + Send> Node<'i, I> for FutureWrapperNode<T> {
|
||||
type Output = Pin<Box<dyn Future<Output = T> + 'i + Send>>;
|
||||
fn eval(&'i self, _input: I) -> Self::Output {
|
||||
let value = self.0.clone();
|
||||
Box::pin(async move { value })
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn instance_on_points_test() {
|
||||
let owned = OwnedContextImpl::default().into_context();
|
||||
let rect = crate::vector::generator_nodes::RectangleNode::new(
|
||||
FutureWrapperNode(()),
|
||||
ExtractXyNode::new(InstancePositionNode {}, FutureWrapperNode(XY::Y)),
|
||||
FutureWrapperNode(2_f64),
|
||||
FutureWrapperNode(false),
|
||||
FutureWrapperNode(0_f64),
|
||||
FutureWrapperNode(false),
|
||||
);
|
||||
|
||||
let positions = [DVec2::new(40., 20.), DVec2::ONE, DVec2::new(-42., 9.), DVec2::new(10., 345.)];
|
||||
let points = VectorDataTable::new(VectorData::from_subpath(Subpath::from_anchors_linear(positions, false)));
|
||||
let repeated = super::instance_on_points(owned, points, &rect, false).await;
|
||||
assert_eq!(repeated.len(), positions.len());
|
||||
for (position, instanced) in positions.into_iter().zip(repeated.instance_ref_iter()) {
|
||||
let bounds = instanced.instance.bounding_box_with_transform(*instanced.transform).unwrap();
|
||||
assert!(position.abs_diff_eq((bounds[0] + bounds[1]) / 2., 1e-10));
|
||||
assert_eq!((bounds[1] - bounds[0]).x, position.y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
pub mod bezpath_algorithms;
|
||||
pub mod instance;
|
||||
pub mod merge_by_distance;
|
||||
pub mod offset_subpath;
|
||||
pub mod poisson_disk;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue