mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Rename the "Table" type to "List" everywhere (#4133)
* Rename the "Table" type to "List" everywhere * Fix a few missed ones * Re-save demo artwork
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::Percentage;
|
||||
use core_types::table::Table;
|
||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_OPACITY, ATTR_OPACITY_FILL, BlendMode, Color, Ctx};
|
||||
use graphic_types::Graphic;
|
||||
use graphic_types::Vector;
|
||||
@@ -16,41 +16,41 @@ impl MultiplyAlpha for Color {
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply_table_attribute<T>(table: &mut Table<T>, key: &str, factor: f64) {
|
||||
if let Some(values) = table.iter_attribute_values_mut::<f64>(key) {
|
||||
fn multiply_list_attribute<T>(list: &mut List<T>, key: &str, factor: f64) {
|
||||
if let Some(values) = list.iter_attribute_values_mut::<f64>(key) {
|
||||
for v in values {
|
||||
*v *= factor;
|
||||
}
|
||||
} else {
|
||||
for v in table.iter_attribute_values_mut_or_default::<f64>(key) {
|
||||
for v in list.iter_attribute_values_mut_or_default::<f64>(key) {
|
||||
*v = factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiplyAlpha for Table<Vector> {
|
||||
impl MultiplyAlpha for List<Vector> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for Table<Graphic> {
|
||||
impl MultiplyAlpha for List<Graphic> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for Table<Raster<CPU>> {
|
||||
impl MultiplyAlpha for List<Raster<CPU>> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for Table<Color> {
|
||||
impl MultiplyAlpha for List<Color> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for Table<GradientStops> {
|
||||
impl MultiplyAlpha for List<GradientStops> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY, factor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,29 +62,29 @@ impl MultiplyFill for Color {
|
||||
*self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.))
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<Vector> {
|
||||
impl MultiplyFill for List<Vector> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<Graphic> {
|
||||
impl MultiplyFill for List<Graphic> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<Raster<CPU>> {
|
||||
impl MultiplyFill for List<Raster<CPU>> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<Color> {
|
||||
impl MultiplyFill for List<Color> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<GradientStops> {
|
||||
impl MultiplyFill for List<GradientStops> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
multiply_table_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
multiply_list_attribute(self, ATTR_OPACITY_FILL, factor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,35 +92,35 @@ trait SetBlendMode {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode);
|
||||
}
|
||||
|
||||
fn set_table_blend_mode<T>(table: &mut Table<T>, blend_mode: BlendMode) {
|
||||
for v in table.iter_attribute_values_mut_or_default::<BlendMode>(ATTR_BLEND_MODE) {
|
||||
fn set_list_blend_mode<T>(list: &mut List<T>, blend_mode: BlendMode) {
|
||||
for v in list.iter_attribute_values_mut_or_default::<BlendMode>(ATTR_BLEND_MODE) {
|
||||
*v = blend_mode;
|
||||
}
|
||||
}
|
||||
|
||||
impl SetBlendMode for Table<Vector> {
|
||||
impl SetBlendMode for List<Vector> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
set_table_blend_mode(self, blend_mode);
|
||||
set_list_blend_mode(self, blend_mode);
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for Table<Graphic> {
|
||||
impl SetBlendMode for List<Graphic> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
set_table_blend_mode(self, blend_mode);
|
||||
set_list_blend_mode(self, blend_mode);
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for Table<Raster<CPU>> {
|
||||
impl SetBlendMode for List<Raster<CPU>> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
set_table_blend_mode(self, blend_mode);
|
||||
set_list_blend_mode(self, blend_mode);
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for Table<Color> {
|
||||
impl SetBlendMode for List<Color> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
set_table_blend_mode(self, blend_mode);
|
||||
set_list_blend_mode(self, blend_mode);
|
||||
}
|
||||
}
|
||||
impl SetBlendMode for Table<GradientStops> {
|
||||
impl SetBlendMode for List<GradientStops> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
set_table_blend_mode(self, blend_mode);
|
||||
set_list_blend_mode(self, blend_mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,35 +128,35 @@ trait SetClip {
|
||||
fn set_clip(&mut self, clip: bool);
|
||||
}
|
||||
|
||||
fn set_table_clip<T>(table: &mut Table<T>, clip: bool) {
|
||||
for v in table.iter_attribute_values_mut_or_default::<bool>(ATTR_CLIPPING_MASK) {
|
||||
fn set_list_clip<T>(list: &mut List<T>, clip: bool) {
|
||||
for v in list.iter_attribute_values_mut_or_default::<bool>(ATTR_CLIPPING_MASK) {
|
||||
*v = clip;
|
||||
}
|
||||
}
|
||||
|
||||
impl SetClip for Table<Vector> {
|
||||
impl SetClip for List<Vector> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
set_table_clip(self, clip);
|
||||
set_list_clip(self, clip);
|
||||
}
|
||||
}
|
||||
impl SetClip for Table<Graphic> {
|
||||
impl SetClip for List<Graphic> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
set_table_clip(self, clip);
|
||||
set_list_clip(self, clip);
|
||||
}
|
||||
}
|
||||
impl SetClip for Table<Raster<CPU>> {
|
||||
impl SetClip for List<Raster<CPU>> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
set_table_clip(self, clip);
|
||||
set_list_clip(self, clip);
|
||||
}
|
||||
}
|
||||
impl SetClip for Table<Color> {
|
||||
impl SetClip for List<Color> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
set_table_clip(self, clip);
|
||||
set_list_clip(self, clip);
|
||||
}
|
||||
}
|
||||
impl SetClip for Table<GradientStops> {
|
||||
impl SetClip for List<GradientStops> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
set_table_clip(self, clip);
|
||||
set_list_clip(self, clip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,17 +166,17 @@ fn blend_mode<T: SetBlendMode>(
|
||||
_: impl Ctx,
|
||||
/// The layer stack that will be composited when rendering.
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut content: T,
|
||||
/// The choice of equation that controls how brightness and color blends between overlapping pixels.
|
||||
blend_mode: BlendMode,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or Item<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List<T> or Item<T>) rather than applying to each item in its own list, which produces the undesired result
|
||||
content.set_blend_mode(blend_mode);
|
||||
content
|
||||
}
|
||||
@@ -189,11 +189,11 @@ fn opacity<T: MultiplyAlpha + MultiplyFill>(
|
||||
_: impl Ctx,
|
||||
/// The layer stack that will be composited when rendering.
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut content: T,
|
||||
/// Whether the *Opacity* property is enabled, multiplying the existing opacity by the chosen percentage.
|
||||
@@ -214,7 +214,7 @@ fn opacity<T: MultiplyAlpha + MultiplyFill>(
|
||||
#[default(100.)]
|
||||
fill: Percentage,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or Item<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List<T> or Item<T>) rather than applying to each item in its own list, which produces the undesired result
|
||||
if has_opacity {
|
||||
content.multiply_alpha(opacity / 100.);
|
||||
}
|
||||
@@ -230,17 +230,17 @@ fn clipping_mask<T: SetClip>(
|
||||
_: impl Ctx,
|
||||
/// The layer stack that will be composited when rendering.
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut content: T,
|
||||
/// Whether the content inherits the alpha of the content beneath it.
|
||||
clip: bool,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or Item<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the list's parent (i.e. its item in its parent List<T> or Item<T>) rather than applying to each item in its own list, which produces the undesired result
|
||||
content.set_clip(clip);
|
||||
content
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use core_types::blending::BlendMode;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::color::{Alpha, Color, Pixel, Sample};
|
||||
use core_types::generic::FnNode;
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::math::bbox::{AxisAlignedBbox, Bbox};
|
||||
use core_types::registry::FutureWrapperNode;
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::transform::Transform;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::value::ClonedNode;
|
||||
@@ -83,7 +83,7 @@ fn brush_stamp_generator(#[unit(" px")] diameter: f64, color: Color, hardness: f
|
||||
|
||||
/// Used to efficiently paint brush strokes. Applies the same texture repeatedly at different positions with proper blending and boundary handling.
|
||||
#[node_macro::node(category(""), skip_impl)]
|
||||
fn blit<BlendFn>(mut target: Table<Raster<CPU>>, texture: Raster<CPU>, positions: Vec<DVec2>, blend_mode: BlendFn) -> Table<Raster<CPU>>
|
||||
fn blit<BlendFn>(mut target: List<Raster<CPU>>, texture: Raster<CPU>, positions: Vec<DVec2>, blend_mode: BlendFn) -> List<Raster<CPU>>
|
||||
where
|
||||
BlendFn: for<'any_input> Node<'any_input, (Color, Color), Output = Color>,
|
||||
{
|
||||
@@ -137,7 +137,7 @@ where
|
||||
pub async fn create_brush_texture(brush_style: &BrushStyle) -> Raster<CPU> {
|
||||
let stamp = brush_stamp_generator(brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow);
|
||||
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(brush_style.diameter), 0., -DVec2::splat(brush_style.diameter / 2.));
|
||||
let blank_texture = empty_image((), transform, Table::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap_or_default();
|
||||
let blank_texture = empty_image((), transform, List::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap_or_default();
|
||||
let image = blend_stamp_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 1.));
|
||||
|
||||
image.into_element()
|
||||
@@ -191,20 +191,20 @@ pub fn blend_with_mode(background: Item<Raster<CPU>>, foreground: Item<Raster<CP
|
||||
async fn brush(
|
||||
_: impl Ctx,
|
||||
/// Optional raster content that may be drawn onto.
|
||||
mut background: Table<Raster<CPU>>,
|
||||
mut background: List<Raster<CPU>>,
|
||||
/// The list of brush stroke paths drawn by the Brush tool, with each including both its coordinates and styles.
|
||||
trace: Table<BrushStroke>,
|
||||
trace: List<BrushStroke>,
|
||||
/// Internal cache data used to accelerate rendering of the brush content.
|
||||
#[data]
|
||||
cache: BrushCache,
|
||||
) -> Table<Raster<CPU>> {
|
||||
) -> List<Raster<CPU>> {
|
||||
if background.is_empty() {
|
||||
background.push(Item::default());
|
||||
}
|
||||
// TODO: Find a way to handle more than one item
|
||||
let table_row = background.clone_item(0).expect("Expected the one item we just pushed");
|
||||
let list_item = background.clone_item(0).expect("Expected the one item we just pushed");
|
||||
|
||||
let bounds = Table::new_from_item(table_row.clone()).bounding_box(DAffine2::IDENTITY, false);
|
||||
let bounds = List::new_from_item(list_item.clone()).bounding_box(DAffine2::IDENTITY, false);
|
||||
let [start, end] = if let RenderBoundingBox::Rectangle(rect) = bounds { rect } else { [DVec2::ZERO, DVec2::ZERO] };
|
||||
let background_bbox = AxisAlignedBbox { start, end };
|
||||
let stroke_bbox = trace.iter_element_values().map(|s| s.bounding_box()).reduce(|a, b| a.union(&b)).unwrap_or(AxisAlignedBbox::ZERO);
|
||||
@@ -221,11 +221,11 @@ async fn brush(
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let mut brush_plan = cache.compute_brush_plan(table_row, &draw_strokes);
|
||||
let mut brush_plan = cache.compute_brush_plan(list_item, &draw_strokes);
|
||||
|
||||
// TODO: Find a way to handle more than one item
|
||||
let Some(mut actual_image) = extend_image_to_bounds((), Table::new_from_item(brush_plan.background), background_bounds).into_iter().next() else {
|
||||
return Table::new();
|
||||
let Some(mut actual_image) = extend_image_to_bounds((), List::new_from_item(brush_plan.background), background_bounds).into_iter().next() else {
|
||||
return List::new();
|
||||
};
|
||||
|
||||
let final_stroke_idx = brush_plan.strokes.len().saturating_sub(1);
|
||||
@@ -263,15 +263,15 @@ async fn brush(
|
||||
);
|
||||
let blit_target = if idx == 0 {
|
||||
let target = core::mem::take(&mut brush_plan.first_stroke_texture);
|
||||
extend_image_to_bounds((), Table::new_from_item(target), stroke_to_layer)
|
||||
extend_image_to_bounds((), List::new_from_item(target), stroke_to_layer)
|
||||
} else {
|
||||
empty_image((), stroke_to_layer, Table::new_from_element(Color::TRANSPARENT))
|
||||
empty_image((), stroke_to_layer, List::new_from_element(Color::TRANSPARENT))
|
||||
// EmptyImageNode::new(CopiedNode::new(stroke_to_layer), CopiedNode::new(Color::TRANSPARENT)).eval(())
|
||||
};
|
||||
|
||||
let table = blit_node.eval(blit_target).await;
|
||||
assert_eq!(table.len(), 1);
|
||||
table.into_iter().next().unwrap_or_default()
|
||||
let list = blit_node.eval(blit_target).await;
|
||||
assert_eq!(list.len(), 1);
|
||||
list.into_iter().next().unwrap_or_default()
|
||||
};
|
||||
|
||||
// Cache image before doing final blend, and store final stroke texture.
|
||||
@@ -311,7 +311,7 @@ async fn brush(
|
||||
FutureWrapperNode::new(ClonedNode::new(positions)),
|
||||
FutureWrapperNode::new(ClonedNode::new(blend_params)),
|
||||
);
|
||||
erase_restore_mask = blit_node.eval(Table::new_from_item(erase_restore_mask)).await.into_iter().next().unwrap_or_default();
|
||||
erase_restore_mask = blit_node.eval(List::new_from_item(erase_restore_mask)).await.into_iter().next().unwrap_or_default();
|
||||
}
|
||||
|
||||
let blend_params = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::MultiplyAlpha, 1.));
|
||||
@@ -323,7 +323,7 @@ async fn brush(
|
||||
let opacity: f64 = actual_image.attribute_cloned_or(ATTR_OPACITY, 1.);
|
||||
let fill: f64 = actual_image.attribute_cloned_or(ATTR_OPACITY_FILL, 1.);
|
||||
let clip: bool = actual_image.attribute_cloned_or_default(ATTR_CLIPPING_MASK);
|
||||
let layer: Table<NodeId> = actual_image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
|
||||
let layer: List<NodeId> = actual_image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
|
||||
|
||||
*background.element_mut(0).unwrap() = actual_image.into_element();
|
||||
background.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||
@@ -421,8 +421,8 @@ mod test {
|
||||
let image = brush(
|
||||
(),
|
||||
&BrushCache::default(),
|
||||
Table::new_from_element(Raster::new_cpu(Image::<Color>::default())),
|
||||
Table::new_from_element(BrushStroke {
|
||||
List::new_from_element(Raster::new_cpu(Image::<Color>::default())),
|
||||
List::new_from_element(BrushStroke {
|
||||
trace: vec![crate::brush_stroke::BrushInputSample { position: DVec2::ZERO }],
|
||||
style: BrushStyle {
|
||||
color: Color::BLACK,
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::brush_stroke::BrushStroke;
|
||||
use crate::brush_stroke::BrushStyle;
|
||||
use core_types::ATTR_TRANSFORM;
|
||||
use core_types::graphene_hash::CacheHashWrapper;
|
||||
use core_types::table::Item;
|
||||
use core_types::list::Item;
|
||||
use raster_types::CPU;
|
||||
use raster_types::Raster;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -19,12 +19,12 @@ pub mod migrations {
|
||||
#[serde(untagged)]
|
||||
enum BrushStrokesFormat {
|
||||
Strokes(Vec<BrushStroke>),
|
||||
Table(LegacyTable),
|
||||
List(LegacyTable),
|
||||
}
|
||||
|
||||
Ok(match BrushStrokesFormat::deserialize(deserializer)? {
|
||||
BrushStrokesFormat::Strokes(strokes) => strokes,
|
||||
BrushStrokesFormat::Table(table) => table.element,
|
||||
BrushStrokesFormat::List(list) => list.element,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use core_types::transform::Footprint;
|
||||
use core_types::{CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime, OwnedContextImpl};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -73,15 +73,15 @@ async fn quantize_real_time<T>(
|
||||
Context -> DAffine2,
|
||||
Context -> Footprint,
|
||||
Context -> DVec2,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> Table<String>,
|
||||
Context -> Table<f64>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<GradientStops>,
|
||||
Context -> List<String>,
|
||||
Context -> List<f64>,
|
||||
Context -> (),
|
||||
)]
|
||||
value: impl Node<'n, Context<'static>, Output = T>,
|
||||
@@ -113,15 +113,15 @@ async fn quantize_animation_time<T>(
|
||||
Context -> DAffine2,
|
||||
Context -> Footprint,
|
||||
Context -> DVec2,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> Table<String>,
|
||||
Context -> Table<f64>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<GradientStops>,
|
||||
Context -> List<String>,
|
||||
Context -> List<f64>,
|
||||
Context -> (),
|
||||
)]
|
||||
value: impl Node<'n, Context<'static>, Output = T>,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use core_types::{Color, ExtractVarArgs};
|
||||
use core_types::{Ctx, ExtractIndex, ExtractPosition};
|
||||
use glam::DVec2;
|
||||
@@ -7,7 +7,7 @@ use graphic_types::{Graphic, Vector};
|
||||
use raster_types::{CPU, Raster};
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_graphic(ctx: impl Ctx + ExtractVarArgs) -> Table<Graphic> {
|
||||
fn read_graphic(ctx: impl Ctx + ExtractVarArgs) -> List<Graphic> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
@@ -15,7 +15,7 @@ fn read_graphic(ctx: impl Ctx + ExtractVarArgs) -> Table<Graphic> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_vector(ctx: impl Ctx + ExtractVarArgs) -> Table<Vector> {
|
||||
fn read_vector(ctx: impl Ctx + ExtractVarArgs) -> List<Vector> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
@@ -23,7 +23,7 @@ fn read_vector(ctx: impl Ctx + ExtractVarArgs) -> Table<Vector> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_raster(ctx: impl Ctx + ExtractVarArgs) -> Table<Raster<CPU>> {
|
||||
fn read_raster(ctx: impl Ctx + ExtractVarArgs) -> List<Raster<CPU>> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
@@ -31,7 +31,7 @@ fn read_raster(ctx: impl Ctx + ExtractVarArgs) -> Table<Raster<CPU>> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_color(ctx: impl Ctx + ExtractVarArgs) -> Table<Color> {
|
||||
fn read_color(ctx: impl Ctx + ExtractVarArgs) -> List<Color> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
@@ -39,7 +39,7 @@ fn read_color(ctx: impl Ctx + ExtractVarArgs) -> Table<Color> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_gradient(ctx: impl Ctx + ExtractVarArgs) -> Table<GradientStops> {
|
||||
fn read_gradient(ctx: impl Ctx + ExtractVarArgs) -> List<GradientStops> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::f64;
|
||||
use core_types::context::{CloneVarArgs, Context, ContextFeatures, Ctx, ExtractAll};
|
||||
use core_types::table::{AttributeDyn, AttributeValueDyn, Table, TableDyn};
|
||||
use core_types::list::{AttributeDyn, AttributeValueDyn, List, ListDyn};
|
||||
use core_types::transform::Footprint;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{Color, OwnedContextImpl};
|
||||
@@ -26,20 +26,20 @@ async fn context_modification<T>(
|
||||
Context -> DAffine2,
|
||||
Context -> Footprint,
|
||||
Context -> DVec2,
|
||||
Context -> Table<String>,
|
||||
Context -> Table<NodeId>,
|
||||
Context -> Table<f64>,
|
||||
Context -> Table<u8>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<String>,
|
||||
Context -> List<NodeId>,
|
||||
Context -> List<f64>,
|
||||
Context -> List<u8>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<GradientStops>,
|
||||
Context -> AttributeDyn,
|
||||
Context -> AttributeValueDyn,
|
||||
Context -> TableDyn,
|
||||
Context -> ListDyn,
|
||||
)]
|
||||
value: impl Node<Context<'static>, Output = T>,
|
||||
/// The parts of the context to keep when evaluating the input value. All other parts are nullified.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use core_types::Ctx;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use raster_types::{CPU, Raster};
|
||||
|
||||
@@ -31,6 +31,6 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
|
||||
|
||||
/// Meant for debugging purposes, not general use. Clones the input value.
|
||||
#[node_macro::node(category("Debug"))]
|
||||
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&Table<Raster<CPU>>)] value: &'i T) -> T {
|
||||
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&List<Raster<CPU>>)] value: &'i T) -> T {
|
||||
value.clone()
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::transform::TransformMut;
|
||||
use core_types::{ATTR_BACKGROUND, ATTR_CLIP, ATTR_DIMENSIONS, ATTR_LOCATION, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::graphic::{Graphic, IntoGraphicTable};
|
||||
use graphic_types::graphic::{Graphic, IntoGraphicList};
|
||||
use graphic_types::{Artboard, Vector};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::GradientStops;
|
||||
|
||||
/// Constructs a single-row `Table<Artboard>` with the given content and metadata stored as row attributes.
|
||||
/// Constructs a single-row `List<Artboard>` with the given content and metadata stored as row attributes.
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn create_artboard<T: IntoGraphicTable + 'n>(
|
||||
pub async fn create_artboard<T: IntoGraphicList + 'n>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
/// Graphics to include within the artboard.
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
Context -> DAffine2,
|
||||
)]
|
||||
content: impl Node<Context<'static>, Output = T>,
|
||||
@@ -27,18 +27,18 @@ pub async fn create_artboard<T: IntoGraphicTable + 'n>(
|
||||
/// Width and height of the artboard within the document.
|
||||
dimensions: DVec2,
|
||||
/// Color of the artboard background.
|
||||
background: Table<Color>,
|
||||
background: List<Color>,
|
||||
/// Whether to cut off the contained content that extends outside the artboard, or keep it visible.
|
||||
#[default(true)]
|
||||
clip: bool,
|
||||
) -> Table<Artboard> {
|
||||
) -> List<Artboard> {
|
||||
let footprint = ctx.try_footprint().copied();
|
||||
let mut new_ctx = OwnedContextImpl::from(ctx);
|
||||
if let Some(mut footprint) = footprint {
|
||||
footprint.translate(location);
|
||||
new_ctx = new_ctx.with_footprint(footprint);
|
||||
}
|
||||
let content = content.eval(new_ctx.into_context()).await.into_graphic_table();
|
||||
let content = content.eval(new_ctx.into_context()).await.into_graphic_list();
|
||||
|
||||
// Normalize so `location` is the top-left corner and `dimensions` are positive (allowing negative input
|
||||
// dimensions to represent dragging from the opposite corner). Compute the corner using the raw signed
|
||||
@@ -49,7 +49,7 @@ pub async fn create_artboard<T: IntoGraphicTable + 'n>(
|
||||
let background = background.element(0).copied().unwrap_or(Color::WHITE);
|
||||
|
||||
// Name is not stored here, it's resolved live from the parent layer's display name
|
||||
Table::new_from_item(
|
||||
List::new_from_item(
|
||||
Item::new_from_element(Artboard::new(content))
|
||||
.with_attribute(ATTR_LOCATION, normalized_location)
|
||||
.with_attribute(ATTR_DIMENSIONS, normalized_dimensions)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::list::{AttributeDyn, AttributeValueDyn, Item, List, ListDyn};
|
||||
use core_types::registry::types::{Angle, SignedInteger};
|
||||
use core_types::table::{AttributeDyn, AttributeValueDyn, Item, Table, TableDyn};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, AnyHash, BlendMode, CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::graphic::{Graphic, IntoGraphicTable};
|
||||
use graphic_types::graphic::{Graphic, IntoGraphicList};
|
||||
use graphic_types::{Artboard, Vector};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::gradient::{GradientSpreadMethod, GradientType};
|
||||
@@ -17,17 +17,17 @@ pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
|
||||
_: impl Ctx,
|
||||
/// The list of data.
|
||||
#[implementations(
|
||||
Table<Artboard>,
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
Table<String>,
|
||||
Table<f64>,
|
||||
Table<u8>,
|
||||
Table<NodeId>,
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<String>,
|
||||
List<f64>,
|
||||
List<u8>,
|
||||
List<NodeId>,
|
||||
)]
|
||||
list: T,
|
||||
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the list, starting from -1 for the last item.
|
||||
@@ -48,14 +48,14 @@ pub fn omit_element<T: graphic_types::graphic::OmitIndex + Clone + Default>(
|
||||
_: impl Ctx,
|
||||
/// The list of data.
|
||||
#[implementations(
|
||||
Table<String>,
|
||||
Table<Artboard>,
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<String>,
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
list: T,
|
||||
/// The index of the item to remove, starting from 0 for the first item. Negative indices count backwards from the end of the list, starting from -1 for the last item.
|
||||
@@ -70,30 +70,30 @@ pub fn omit_element<T: graphic_types::graphic::OmitIndex + Clone + Default>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bare element (without the item's attributes) at the specified index in a `Table`.
|
||||
/// Use this when downstream nodes want just the inner value rather than a `Table` containing a single item.
|
||||
/// Returns the bare element (without the item's attributes) at the specified index in a `List`.
|
||||
/// Use this when downstream nodes want just the inner value rather than a `List` containing a single item.
|
||||
/// If no value exists at that index, the element type's default is returned.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub fn extract_element<T: Clone + Default + Send + Sync + 'static>(
|
||||
_: impl Ctx,
|
||||
/// The `Table` of data to extract from.
|
||||
/// The `List` of data to extract from.
|
||||
#[implementations(
|
||||
Table<String>,
|
||||
Table<f64>,
|
||||
Table<u8>,
|
||||
Table<NodeId>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Graphic>,
|
||||
Table<Artboard>,
|
||||
List<String>,
|
||||
List<f64>,
|
||||
List<u8>,
|
||||
List<NodeId>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Graphic>,
|
||||
List<Artboard>,
|
||||
)]
|
||||
table: Table<T>,
|
||||
list: List<T>,
|
||||
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the list, starting from -1 for the last item.
|
||||
index: SignedInteger,
|
||||
) -> T {
|
||||
let len = table.len();
|
||||
let len = list.len();
|
||||
let index = index as i32;
|
||||
let resolved = if index < 0 {
|
||||
let from_end = index.unsigned_abs() as usize;
|
||||
@@ -104,37 +104,37 @@ pub fn extract_element<T: Clone + Default + Send + Sync + 'static>(
|
||||
} else {
|
||||
index as usize
|
||||
};
|
||||
table.element(resolved).cloned().unwrap_or_default()
|
||||
list.element(resolved).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn map<Item: AnyHash + Send + Sync + CacheHash>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
content: Table<Item>,
|
||||
content: List<Item>,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
mapped: impl Node<Context<'static>, Output = Table<Item>>,
|
||||
) -> Table<Item> {
|
||||
let mut rows = Table::new();
|
||||
mapped: impl Node<Context<'static>, Output = List<Item>>,
|
||||
) -> List<Item> {
|
||||
let mut rows = List::new();
|
||||
|
||||
for (i, row) in content.into_iter().enumerate() {
|
||||
let owned_ctx = OwnedContextImpl::from(ctx.clone());
|
||||
let owned_ctx = owned_ctx.with_vararg(Box::new(Table::new_from_item(row))).with_index(i);
|
||||
let table = mapped.eval(owned_ctx.into_context()).await;
|
||||
let owned_ctx = owned_ctx.with_vararg(Box::new(List::new_from_item(row))).with_index(i);
|
||||
let list = mapped.eval(owned_ctx.into_context()).await;
|
||||
|
||||
rows.extend(table);
|
||||
rows.extend(list);
|
||||
}
|
||||
|
||||
rows
|
||||
@@ -144,20 +144,20 @@ async fn map<Item: AnyHash + Send + Sync + CacheHash>(
|
||||
async fn mirror<T: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
content: Table<T>,
|
||||
content: List<T>,
|
||||
#[default(ReferencePoint::Center)] relative_to_bounds: ReferencePoint,
|
||||
#[unit(" px")] offset: f64,
|
||||
#[range((-90., 90.))] angle: Angle,
|
||||
#[default(true)] keep_original: bool,
|
||||
) -> Table<T>
|
||||
) -> List<T>
|
||||
where
|
||||
Table<T>: BoundingBox,
|
||||
List<T>: BoundingBox,
|
||||
{
|
||||
// Normalize the direction vector
|
||||
let normal = DVec2::from_angle(angle.to_radians());
|
||||
@@ -186,12 +186,12 @@ where
|
||||
reflection * DAffine2::from_translation(DVec2::from_angle(angle.to_radians()) * DVec2::splat(-offset))
|
||||
};
|
||||
|
||||
let mut result_table = Table::new();
|
||||
let mut result_list = List::new();
|
||||
|
||||
// Add original items depending on the keep_original flag
|
||||
if keep_original {
|
||||
for item in content.clone().into_iter() {
|
||||
result_table.push(item);
|
||||
result_list.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,10 +199,10 @@ where
|
||||
for mut row in content.into_iter() {
|
||||
let current_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||
row.set_attribute(ATTR_TRANSFORM, reflected_transform * current_transform);
|
||||
result_table.push(row);
|
||||
result_list.push(row);
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
/// Returns the path identifying the subgraph (network) that contains this proto node — i.e. the input `node_path`
|
||||
@@ -212,13 +212,13 @@ where
|
||||
/// editor tools (e.g. selection, click target routing) trace data back to its owning layer regardless of whether
|
||||
/// the layer is at the root document network or nested inside a custom subgraph.
|
||||
#[node_macro::node(name("Path of Subgraph"), category(""))]
|
||||
pub fn path_of_subgraph(_: impl Ctx, node_path: Table<NodeId>) -> Table<NodeId> {
|
||||
pub fn path_of_subgraph(_: impl Ctx, node_path: List<NodeId>) -> List<NodeId> {
|
||||
let len = node_path.len();
|
||||
node_path.into_iter().take(len.saturating_sub(1)).collect()
|
||||
}
|
||||
|
||||
/// Sets a named attribute on the input `Table`, computing one value per item via the value-producing input. That input
|
||||
/// is evaluated once per item, with the item's index and the item itself (as a `Table` containing only that item,
|
||||
/// Sets a named attribute on the input `List`, computing one value per item via the value-producing input. That input
|
||||
/// is evaluated once per item, with the item's index and the item itself (as a `List` containing only that item,
|
||||
/// passed as a vararg) provided via context, so the upstream pipeline can return a different value per item that may
|
||||
/// be derived from the item's own data. If the attribute already exists, its values are replaced; if not, it's added.
|
||||
/// The value is type-erased into an `AttributeValueDyn` by an auto-inserted convert node, so this node only
|
||||
@@ -226,66 +226,66 @@ pub fn path_of_subgraph(_: impl Ctx, node_path: Table<NodeId>) -> Table<NodeId>
|
||||
#[node_macro::node(category("Attributes: Write"))]
|
||||
async fn write_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
/// The `Table` to set the named attribute on (one value per item).
|
||||
/// The `List` to set the named attribute on (one value per item).
|
||||
#[implementations(
|
||||
Table<Artboard>,
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
Table<f64>,
|
||||
Table<bool>,
|
||||
Table<String>,
|
||||
Table<DAffine2>,
|
||||
Table<BlendMode>,
|
||||
Table<GradientType>,
|
||||
Table<GradientSpreadMethod>,
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<f64>,
|
||||
List<bool>,
|
||||
List<String>,
|
||||
List<DAffine2>,
|
||||
List<BlendMode>,
|
||||
List<GradientType>,
|
||||
List<GradientSpreadMethod>,
|
||||
)]
|
||||
mut content: Table<T>,
|
||||
mut content: List<T>,
|
||||
/// The attribute name (key) to write or replace.
|
||||
name: String,
|
||||
/// The node that produces the attribute value for each item. Called once per item with the item's index in context.
|
||||
#[implementations(Context -> AttributeValueDyn)]
|
||||
value: impl Node<'n, Context<'static>, Output = AttributeValueDyn>,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
for index in 0..content.len() {
|
||||
let row = content.clone_item(index).expect("index is within bounds");
|
||||
let owned_ctx = OwnedContextImpl::from(ctx.clone()).with_vararg(Box::new(Table::new_from_item(row))).with_index(index);
|
||||
let owned_ctx = OwnedContextImpl::from(ctx.clone()).with_vararg(Box::new(List::new_from_item(row))).with_index(index);
|
||||
let v = value.eval(owned_ctx.into_context()).await;
|
||||
content.set_attribute_value_dyn(&name, index, v);
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
/// Sets a named attribute on the primary table, with each value taken from the corresponding item's element in the source table (paired by index, wrapping if the source has fewer items).
|
||||
/// Sets a named attribute on the primary list, with each value taken from the corresponding item's element in the source list (paired by index, wrapping if the source has fewer items).
|
||||
/// The source is type-erased into an `AttributeDyn` by an auto-inserted convert node, so this node only monomorphizes over `T` instead of the cartesian product `(T, U)`.
|
||||
#[node_macro::node(category("Attributes: Write"))]
|
||||
fn attach_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
|
||||
_: impl Ctx,
|
||||
/// The `Table` to attach the new attribute to.
|
||||
/// The `List` to attach the new attribute to.
|
||||
#[implementations(
|
||||
Table<Artboard>,
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
Table<f64>,
|
||||
Table<bool>,
|
||||
Table<String>,
|
||||
Table<DAffine2>,
|
||||
Table<BlendMode>,
|
||||
Table<GradientType>,
|
||||
Table<GradientSpreadMethod>,
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<f64>,
|
||||
List<bool>,
|
||||
List<String>,
|
||||
List<DAffine2>,
|
||||
List<BlendMode>,
|
||||
List<GradientType>,
|
||||
List<GradientSpreadMethod>,
|
||||
)]
|
||||
mut content: Table<T>,
|
||||
/// The source values to attach. Any `Table<U>` wired here is type-erased via an auto-inserted convert.
|
||||
mut content: List<T>,
|
||||
/// The source values to attach. Any `List<U>` wired here is type-erased via an auto-inserted convert.
|
||||
#[expose]
|
||||
source: AttributeDyn,
|
||||
/// The name to assign to the new destination attribute.
|
||||
name: String,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
if source.is_empty() {
|
||||
return content;
|
||||
}
|
||||
@@ -293,15 +293,15 @@ fn attach_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
|
||||
content
|
||||
}
|
||||
|
||||
/// Reads a named `Vector` attribute from the input table, outputting each value as an element of a new `Table<Vector>`.
|
||||
/// Reads a named `Vector` attribute from the input list, outputting each value as an element of a new `List<Vector>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_vector(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<Vector> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<Vector> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Vector>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
@@ -309,15 +309,15 @@ fn read_attribute_vector(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named numeric attribute (`f64`, `u64`, or `u32`) from the input table, outputting each value as an element of a new `Table<f64>`. Integer values are converted to `f64`.
|
||||
/// Reads a named numeric attribute (`f64`, `u64`, or `u32`) from the input list, outputting each value as an element of a new `List<f64>`. Integer values are converted to `f64`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_number(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<f64> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<f64> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let value = content
|
||||
.attribute::<f64>(&name, index)
|
||||
@@ -330,15 +330,15 @@ fn read_attribute_number(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `bool` attribute from the input table, outputting each value as an element of a new `Table<bool>`.
|
||||
/// Reads a named `bool` attribute from the input list, outputting each value as an element of a new `List<bool>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_bool(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<bool> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<bool> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<bool>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -346,15 +346,15 @@ fn read_attribute_bool(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `String` attribute from the input table, outputting each value as an element of a new `Table<String>`.
|
||||
/// Reads a named `String` attribute from the input list, outputting each value as an element of a new `List<String>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_string(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<String> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<String> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<String>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
@@ -362,15 +362,15 @@ fn read_attribute_string(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `DAffine2` transform attribute from the input table, outputting each value as an element of a new `Table<DAffine2>`.
|
||||
/// Reads a named `DAffine2` transform attribute from the input list, outputting each value as an element of a new `List<DAffine2>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_transform(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<DAffine2> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<DAffine2> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<DAffine2>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -378,15 +378,15 @@ fn read_attribute_transform(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Color` attribute from the input table, outputting each value as an element of a new `Table<Color>`.
|
||||
/// Reads a named `Color` attribute from the input list, outputting each value as an element of a new `List<Color>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_color(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<Color> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<Color> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Color>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -394,15 +394,15 @@ fn read_attribute_color(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `BlendMode` attribute from the input table, outputting each value as an element of a new `Table<BlendMode>`.
|
||||
/// Reads a named `BlendMode` attribute from the input list, outputting each value as an element of a new `List<BlendMode>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_blend_mode(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<BlendMode> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<BlendMode> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<BlendMode>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -410,15 +410,15 @@ fn read_attribute_blend_mode(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientType` attribute from the input table, outputting each value as an element of a new `Table<GradientType>`.
|
||||
/// Reads a named `GradientType` attribute from the input list, outputting each value as an element of a new `List<GradientType>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_gradient_type(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<GradientType> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<GradientType> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientType>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -426,15 +426,15 @@ fn read_attribute_gradient_type(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientSpreadMethod` attribute from the input table, outputting each value as an element of a new `Table<GradientSpreadMethod>`.
|
||||
/// Reads a named `GradientSpreadMethod` attribute from the input list, outputting each value as an element of a new `List<GradientSpreadMethod>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_spread_method(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<GradientSpreadMethod> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<GradientSpreadMethod> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientSpreadMethod>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
@@ -442,15 +442,15 @@ fn read_attribute_spread_method(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientStops` attribute from the input table, outputting each value as an element of a new `Table<GradientStops>`.
|
||||
/// Reads a named `GradientStops` attribute from the input list, outputting each value as an element of a new `List<GradientStops>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_gradient_stops(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<GradientStops> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<GradientStops> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientStops>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
@@ -458,15 +458,15 @@ fn read_attribute_gradient_stops(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Artboard` attribute from the input table, outputting each value as an element of a new `Table<Artboard>`.
|
||||
/// Reads a named `Artboard` attribute from the input list, outputting each value as an element of a new `List<Artboard>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_artboard(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<Artboard> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<Artboard> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Artboard>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
@@ -474,15 +474,15 @@ fn read_attribute_artboard(
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Raster<CPU>` attribute from the input table, outputting each value as an element of a new `Table<Raster<CPU>>`.
|
||||
/// Reads a named `Raster<CPU>` attribute from the input list, outputting each value as an element of a new `List<Raster<CPU>>`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_raster(
|
||||
_: impl Ctx,
|
||||
content: TableDyn,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> Table<Raster<CPU>> {
|
||||
let mut result = Table::with_capacity(content.len());
|
||||
) -> List<Raster<CPU>> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Raster<CPU>>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
@@ -490,18 +490,18 @@ fn read_attribute_raster(
|
||||
result
|
||||
}
|
||||
|
||||
/// Joins two `Table`s of the same type, extending the base `Table` with the items from the new `Table`.
|
||||
/// Joins two `List`s of the same type, extending the base `List` with the items from the new `List`.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn extend<T: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The `Table` whose items will appear at the start of the extended `Table`.
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
|
||||
base: Table<T>,
|
||||
/// The `Table` whose items will appear at the end of the extended `Table`.
|
||||
/// The `List` whose items will appear at the start of the extended `List`.
|
||||
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
|
||||
base: List<T>,
|
||||
/// The `List` whose items will appear at the end of the extended `List`.
|
||||
#[expose]
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
|
||||
new: Table<T>,
|
||||
) -> Table<T> {
|
||||
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
|
||||
new: List<T>,
|
||||
) -> List<T> {
|
||||
let mut base = base;
|
||||
base.extend(new);
|
||||
|
||||
@@ -514,12 +514,12 @@ pub async fn extend<T: 'n + Send + Clone>(
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn legacy_layer_extend<T: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)] base: Table<T>,
|
||||
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)] base: List<T>,
|
||||
#[expose]
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
|
||||
new: Table<T>,
|
||||
nested_node_path: Table<NodeId>,
|
||||
) -> Table<T> {
|
||||
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
|
||||
new: List<T>,
|
||||
nested_node_path: List<NodeId>,
|
||||
) -> List<T> {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
// This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node).
|
||||
let layer = {
|
||||
@@ -542,46 +542,46 @@ pub async fn legacy_layer_extend<T: 'n + Send + Clone>(
|
||||
pub async fn wrap_graphic<T: Into<Graphic> + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
DAffine2,
|
||||
)]
|
||||
content: T,
|
||||
) -> Table<Graphic> {
|
||||
Table::new_from_element(content.into())
|
||||
) -> List<Graphic> {
|
||||
List::new_from_element(content.into())
|
||||
}
|
||||
|
||||
/// Converts a `Table` of graphical content into a `Table<Graphic>` by placing it into an element of a new wrapper `Table<Graphic>`.
|
||||
/// If it is already a `Table<Graphic>`, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
|
||||
/// Converts a `List` of graphical content into a `List<Graphic>` by placing it into an element of a new wrapper `List<Graphic>`.
|
||||
/// If it is already a `List<Graphic>`, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn to_graphic<T: IntoGraphicTable + 'n>(
|
||||
pub async fn to_graphic<T: IntoGraphicList + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
content: T,
|
||||
) -> Table<Graphic> {
|
||||
content.into_graphic_table()
|
||||
) -> List<Graphic> {
|
||||
content.into_graphic_list()
|
||||
}
|
||||
|
||||
/// Removes a level of nesting from a `Table<Graphic>`, or all nesting if "Fully Flatten" is enabled.
|
||||
/// Removes a level of nesting from a `List<Graphic>`, or all nesting if "Fully Flatten" is enabled.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
|
||||
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
|
||||
fn flatten_table(output_graphic_table: &mut Table<Graphic>, current_graphic_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
|
||||
for index in 0..current_graphic_table.len() {
|
||||
let Some(current_element) = current_graphic_table.element(index) else { continue };
|
||||
pub async fn flatten_graphic(_: impl Ctx, content: List<Graphic>, fully_flatten: bool) -> List<Graphic> {
|
||||
// TODO: Avoid mutable reference, instead return a new List<Graphic>?
|
||||
fn flatten_list(output_graphic_list: &mut List<Graphic>, current_graphic_list: List<Graphic>, fully_flatten: bool, recursion_depth: usize) {
|
||||
for index in 0..current_graphic_list.len() {
|
||||
let Some(current_element) = current_graphic_list.element(index) else { continue };
|
||||
let current_element = current_element.clone();
|
||||
let current_transform: DAffine2 = current_graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let current_transform: DAffine2 = current_graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
|
||||
let recurse = fully_flatten || recursion_depth == 0;
|
||||
|
||||
@@ -593,82 +593,82 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
|
||||
*graphic_transform = current_transform * *graphic_transform;
|
||||
}
|
||||
|
||||
flatten_table(output_graphic_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
flatten_list(output_graphic_list, current_element, fully_flatten, recursion_depth + 1);
|
||||
}
|
||||
// Push any leaf elements we encounter: either `Graphic::Graphic(...)` values beyond the recursion depth, or non-`Graphic::Graphic` variants (e.g. `Graphic::Vector`, `Graphic::Raster*`, `Graphic::Color`, `Graphic::Gradient`)
|
||||
_ => {
|
||||
let attributes = current_graphic_table.clone_item_attributes(index);
|
||||
output_graphic_table.push(Item::from_parts(current_element, attributes));
|
||||
let attributes = current_graphic_list.clone_item_attributes(index);
|
||||
output_graphic_list.push(Item::from_parts(current_element, attributes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = Table::new();
|
||||
flatten_table(&mut output, content, fully_flatten, 0);
|
||||
let mut output = List::new();
|
||||
flatten_list(&mut output, content, fully_flatten, 0);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// Converts a `Table<Graphic>` into a `Table<Vector>` by deeply flattening any vector content it contains, and discarding any non-vector content.
|
||||
/// Converts a `List<Graphic>` into a `List<Vector>` by deeply flattening any vector content it contains, and discarding any non-vector content.
|
||||
#[node_macro::node(category("Vector"))]
|
||||
pub async fn flatten_vector<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: T) -> Table<Vector> {
|
||||
let graphic_table = content.into_graphic_table();
|
||||
let mut output: Table<Vector> = graphic_table.clone().into_flattened_table();
|
||||
pub async fn flatten_vector<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Vector>)] content: T) -> List<Vector> {
|
||||
let graphic_list = content.into_graphic_list();
|
||||
let mut output: List<Vector> = graphic_list.clone().into_flattened_list();
|
||||
|
||||
// TODO: Replace this snapshot hack with per-layer metadata driven by each layer's Monitor node.
|
||||
// TODO: Flattening here erases the upstream `Table<Graphic>` hierarchy that editor metadata collection walks
|
||||
// TODO: Flattening here erases the upstream `List<Graphic>` hierarchy that editor metadata collection walks
|
||||
// TODO: to populate `upstream_footprints` / `local_transforms` / `click_targets` per child layer. As a workaround
|
||||
// TODO: we stash the pre-flattened table on the output so `Table<Vector>::collect_metadata` can recurse into it,
|
||||
// TODO: we stash the pre-flattened list on the output so `List<Vector>::collect_metadata` can recurse into it,
|
||||
// TODO: which conflates render output with editor metadata and forces the pre-compensation dance below.
|
||||
// TODO: The cleaner fix is to drive each layer's metadata from its own Monitor's captured `(Context, Table<Graphic>)`,
|
||||
// TODO: The cleaner fix is to drive each layer's metadata from its own Monitor's captured `(Context, List<Graphic>)`,
|
||||
// TODO: at which point this attribute (and the equivalents in Boolean Operation, Solidify Stroke, Flatten Path,
|
||||
// TODO: Morph, Rasterize) become unnecessary.
|
||||
if !output.is_empty() {
|
||||
// Item 0 carries a composed transform inherited from the flattened input, but the merged_layers
|
||||
// already holds the original transforms; pre-compensate by item 0's inverse so the renderer's
|
||||
// `upstream_footprint *= item_0_transform` recursion cancels out and leaves the originals intact.
|
||||
let mut graphic_table = graphic_table;
|
||||
let mut graphic_list = graphic_list;
|
||||
let item_0_transform: DAffine2 = output.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
if item_0_transform.matrix2.determinant().abs() > f64::EPSILON {
|
||||
let inverse = item_0_transform.inverse();
|
||||
for transform in graphic_table.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
for transform in graphic_list.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform = inverse * *transform;
|
||||
}
|
||||
}
|
||||
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_table);
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_list);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// Converts a `Table<Graphic>` into a `Table<Raster>` by deeply flattening any raster content it contains, and discarding any non-raster content.
|
||||
/// Converts a `List<Graphic>` into a `List<Raster>` by deeply flattening any raster content it contains, and discarding any non-raster content.
|
||||
#[node_macro::node(category("Raster"))]
|
||||
pub async fn flatten_raster<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Raster<CPU>>)] content: T) -> Table<Raster<CPU>> {
|
||||
content.into_flattened_table()
|
||||
pub async fn flatten_raster<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Raster<CPU>>)] content: T) -> List<Raster<CPU>> {
|
||||
content.into_flattened_list()
|
||||
}
|
||||
|
||||
/// Converts a `Table<Graphic>` into a `Table<Color>` by deeply flattening any color content it contains, and discarding any non-color content.
|
||||
/// Converts a `List<Graphic>` into a `List<Color>` by deeply flattening any color content it contains, and discarding any non-color content.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn flatten_color<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] content: T) -> Table<Color> {
|
||||
content.into_flattened_table()
|
||||
pub async fn flatten_color<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Color>)] content: T) -> List<Color> {
|
||||
content.into_flattened_list()
|
||||
}
|
||||
|
||||
/// Converts a `Table<Graphic>` into a `Table<GradientStops>` by deeply flattening any gradient content it contains, and discarding any non-gradient content.
|
||||
/// Converts a `List<Graphic>` into a `List<GradientStops>` by deeply flattening any gradient content it contains, and discarding any non-gradient content.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn flatten_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<GradientStops>)] content: T) -> Table<GradientStops> {
|
||||
content.into_flattened_table()
|
||||
pub async fn flatten_gradient<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<GradientStops>)] content: T) -> List<GradientStops> {
|
||||
content.into_flattened_list()
|
||||
}
|
||||
|
||||
/// Constructs a gradient from a `Table<Color>`, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
|
||||
/// Constructs a gradient from a `List<Color>`, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
|
||||
#[node_macro::node(category("Color"))]
|
||||
fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] colors: T) -> Table<GradientStops> {
|
||||
let colors = colors.into_flattened_table::<Color>();
|
||||
fn colors_to_gradient<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Color>)] colors: T) -> List<GradientStops> {
|
||||
let colors = colors.into_flattened_list::<Color>();
|
||||
let total_colors = colors.len();
|
||||
|
||||
if total_colors == 0 {
|
||||
return Table::new_from_element(GradientStops::new(vec![
|
||||
return List::new_from_element(GradientStops::new(vec![
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
@@ -683,7 +683,7 @@ fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[im
|
||||
}
|
||||
|
||||
if let (1, Some(&single_color)) = (total_colors, colors.element(0)) {
|
||||
return Table::new_from_element(GradientStops::new(vec![
|
||||
return List::new_from_element(GradientStops::new(vec![
|
||||
GradientStop {
|
||||
position: 0.,
|
||||
midpoint: 0.5,
|
||||
@@ -702,5 +702,5 @@ fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[im
|
||||
midpoint: 0.5,
|
||||
color: row.into_element(),
|
||||
});
|
||||
Table::new_from_element(GradientStops::new(colors))
|
||||
List::new_from_element(GradientStops::new(colors))
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
use base64::Engine;
|
||||
#[cfg(target_family = "wasm")]
|
||||
use canvas_utils::{Canvas, CanvasHandle};
|
||||
use core_types::list::{Item, List};
|
||||
#[cfg(target_family = "wasm")]
|
||||
use core_types::math::bbox::Bbox;
|
||||
use core_types::table::{Item, Table};
|
||||
#[cfg(target_family = "wasm")]
|
||||
use core_types::transform::Footprint;
|
||||
#[cfg(target_family = "wasm")]
|
||||
@@ -18,7 +18,7 @@ pub use graphene_canvas_utils as canvas_utils;
|
||||
#[cfg(target_family = "wasm")]
|
||||
use graphic_types::Graphic;
|
||||
#[cfg(target_family = "wasm")]
|
||||
use graphic_types::IntoGraphicTable;
|
||||
use graphic_types::IntoGraphicList;
|
||||
#[cfg(target_family = "wasm")]
|
||||
use graphic_types::Vector;
|
||||
use graphic_types::raster_types::Image;
|
||||
@@ -85,7 +85,7 @@ async fn post_request(
|
||||
#[name("URL")]
|
||||
url: String,
|
||||
/// The binary data to include in the body of the POST request.
|
||||
body: Table<u8>,
|
||||
body: List<u8>,
|
||||
/// Makes the request run in the background without waiting on a response. This is useful for triggering webhooks without blocking the continued execution of the graph.
|
||||
discard_result: bool,
|
||||
#[widget(ParsedWidgetOverride::Custom = "text_area")] headers: String,
|
||||
@@ -115,14 +115,14 @@ async fn post_request(
|
||||
|
||||
/// Converts a text string to raw binary data. Useful for transmission over HTTP or writing to files.
|
||||
#[node_macro::node(category("Web Request"), name("String to Bytes"))]
|
||||
fn string_to_bytes(_: impl Ctx, string: String) -> Table<u8> {
|
||||
fn string_to_bytes(_: impl Ctx, string: String) -> List<u8> {
|
||||
string.into_bytes().into_iter().map(Item::new_from_element).collect()
|
||||
}
|
||||
|
||||
/// Converts extracted raw RGBA pixel data from an input image. Each pixel becomes 4 sequential bytes. Useful for transmission over HTTP or writing to files.
|
||||
#[node_macro::node(category("Web Request"), name("Image to Bytes"))]
|
||||
fn image_to_bytes(_: impl Ctx, image: Table<Raster<CPU>>) -> Table<u8> {
|
||||
let Some(image) = image.element(0) else { return Table::new() };
|
||||
fn image_to_bytes(_: impl Ctx, image: List<Raster<CPU>>) -> List<u8> {
|
||||
let Some(image) = image.element(0) else { return List::new() };
|
||||
image.data.iter().flat_map(|color| color.to_rgba8_srgb()).map(Item::new_from_element).collect()
|
||||
}
|
||||
|
||||
@@ -146,9 +146,9 @@ async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")]
|
||||
///
|
||||
/// Works with standard image format (PNG, JPEG, WebP, etc.). Automatically converts the color space to linear sRGB for accurate compositing.
|
||||
#[node_macro::node(category("Web Request"))]
|
||||
fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> Table<Raster<CPU>> {
|
||||
fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> List<Raster<CPU>> {
|
||||
let Some(image) = image::load_from_memory(data.as_ref()).ok() else {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
};
|
||||
let image = image.to_rgba32f();
|
||||
let image = Image {
|
||||
@@ -161,7 +161,7 @@ fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> Table<Raster<CPU>> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Table::new_from_element(Raster::new_cpu(image))
|
||||
List::new_from_element(Raster::new_cpu(image))
|
||||
}
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
@@ -176,29 +176,29 @@ async fn create_canvas(_: impl Ctx) -> CanvasHandle {
|
||||
async fn rasterize<T: WasmNotSend + Clone + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Graphic>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Graphic>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut data: Table<T>,
|
||||
mut data: List<T>,
|
||||
footprint: Footprint,
|
||||
mut canvas: CanvasHandle,
|
||||
) -> Table<Raster<CPU>>
|
||||
) -> List<Raster<CPU>>
|
||||
where
|
||||
Table<T>: Render + Clone + graphic_types::IntoGraphicTable,
|
||||
List<T>: Render + Clone + graphic_types::IntoGraphicList,
|
||||
{
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
if footprint.transform.matrix2.determinant() == 0. {
|
||||
log::trace!("Invalid footprint received for rasterization");
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
|
||||
// Snapshot the input as a Table<Graphic> so the renderer can recurse into the original child layers
|
||||
// Snapshot the input as a List<Graphic> so the renderer can recurse into the original child layers
|
||||
// when collecting metadata, exposing their click targets to editor tools (same mechanism as Boolean Operation).
|
||||
let upstream_graphic_table = data.clone().into_graphic_table();
|
||||
let upstream_graphic_list = data.clone().into_graphic_list();
|
||||
|
||||
let mut render = SvgRender::new();
|
||||
let aabb = Bbox::from_transform(footprint.transform).to_axis_aligned_bbox();
|
||||
@@ -235,9 +235,9 @@ where
|
||||
let rasterized = context.get_image_data(0., 0., resolution.x as f64, resolution.y as f64).unwrap();
|
||||
|
||||
let image = Image::from_image_data(&rasterized.data().0, resolution.x as u32, resolution.y as u32);
|
||||
Table::new_from_item(
|
||||
List::new_from_item(
|
||||
Item::new_from_element(Raster::new_cpu(image))
|
||||
.with_attribute(ATTR_TRANSFORM, footprint.transform)
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_table),
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_list),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use core_types::transform::{Footprint, Transform};
|
||||
use core_types::uuid::generate_uuid;
|
||||
use core_types::{CloneVarArgs, ExtractAll, ExtractVarArgs};
|
||||
@@ -33,12 +33,12 @@ pub struct RenderIntermediate {
|
||||
async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send + Sync>(
|
||||
ctx: impl Ctx + ExtractVarArgs + ExtractAll + CloneVarArgs,
|
||||
#[implementations(
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
data: impl Node<Context<'static>, Output = T>,
|
||||
) -> RenderIntermediate {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core_types::{Ctx, table::Table};
|
||||
use core_types::Ctx;
|
||||
use core_types::list::List;
|
||||
use graph_craft::application_io::PlatformEditorApi;
|
||||
use graphic_types::Vector;
|
||||
pub use text_nodes::*;
|
||||
@@ -61,7 +62,7 @@ fn text<'i: 'n>(
|
||||
align: TextAlign,
|
||||
/// Whether to split every letterform into its own vector item. Otherwise, a single vector compound path is produced.
|
||||
separate_glyphs: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let typesetting = TypesettingConfig {
|
||||
font_size: size,
|
||||
line_height_ratio: line_height,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core_types::Context;
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::{Fraction, Percentage, PixelSize};
|
||||
use core_types::table::Table;
|
||||
use core_types::transform::Footprint;
|
||||
use core_types::{Color, Ctx, num_traits};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -753,13 +753,13 @@ async fn switch<T, C: Send + 'n + Clone>(
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
if_true: impl Node<C, Output = T>,
|
||||
#[expose]
|
||||
@@ -772,13 +772,13 @@ async fn switch<T, C: Send + 'n + Clone>(
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Artboard>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
if_false: impl Node<C, Output = T>,
|
||||
) -> T {
|
||||
@@ -811,70 +811,70 @@ fn vec2_value(_: impl Ctx, _primary: (), x: f64, y: f64) -> DVec2 {
|
||||
|
||||
/// Constructs a color value which may be set to any color, or no color.
|
||||
#[node_macro::node(category("Value"))]
|
||||
fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Table<Color>) -> Table<Color> {
|
||||
fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: List<Color>) -> List<Color> {
|
||||
color
|
||||
}
|
||||
|
||||
/// Constructs a color value from red, green, blue, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("RGBA to Color"))]
|
||||
fn rgba_to_color(_: impl Ctx, _primary: (), red: Fraction, green: Fraction, blue: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
fn rgba_to_color(_: impl Ctx, _primary: (), red: Fraction, green: Fraction, blue: Fraction, #[default(1.)] alpha: Fraction) -> List<Color> {
|
||||
let red = (red as f32).clamp(0., 1.);
|
||||
let green = (green as f32).clamp(0., 1.);
|
||||
let blue = (blue as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_rgbaf32_unchecked(red, green, blue, alpha))
|
||||
List::new_from_element(Color::from_rgbaf32_unchecked(red, green, blue, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from hue, saturation, value, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("HSVA to Color"))]
|
||||
fn hsva_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(1.)] value: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
fn hsva_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(1.)] value: Fraction, #[default(1.)] alpha: Fraction) -> List<Color> {
|
||||
let hue = (hue as f32) - (hue as f32).floor();
|
||||
let saturation = (saturation as f32).clamp(0., 1.);
|
||||
let value = (value as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_hsva(hue, saturation, value, alpha))
|
||||
List::new_from_element(Color::from_hsva(hue, saturation, value, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from hue, saturation, lightness, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("HSLA to Color"))]
|
||||
fn hsla_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(0.5)] lightness: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
fn hsla_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(0.5)] lightness: Fraction, #[default(1.)] alpha: Fraction) -> List<Color> {
|
||||
let hue = (hue as f32) - (hue as f32).floor();
|
||||
let saturation = (saturation as f32).clamp(0., 1.);
|
||||
let lightness = (lightness as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_hsla(hue, saturation, lightness, alpha))
|
||||
List::new_from_element(Color::from_hsla(hue, saturation, lightness, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from an sRGB color code string, such as `#RRGGBB` or `#RRGGBBAA`. Invalid hex code strings produce no color.
|
||||
#[node_macro::node(category("Color"), name("Hex to Color"))]
|
||||
fn hex_to_color(_: impl Ctx, hex_code: String) -> Table<Color> {
|
||||
fn hex_to_color(_: impl Ctx, hex_code: String) -> List<Color> {
|
||||
match Color::from_hex_str(&hex_code) {
|
||||
Some(c) => Table::new_from_element(c),
|
||||
None => Table::new(),
|
||||
Some(c) => List::new_from_element(c),
|
||||
None => List::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors.
|
||||
#[node_macro::node(category("Value"))]
|
||||
fn gradient_value(_: impl Ctx, _primary: (), gradient: Table<GradientStops>) -> Table<GradientStops> {
|
||||
fn gradient_value(_: impl Ctx, _primary: (), gradient: List<GradientStops>) -> List<GradientStops> {
|
||||
gradient
|
||||
}
|
||||
|
||||
/// Sets the type (linear or radial) of each gradient in the input table.
|
||||
/// Sets the type (linear or radial) of each gradient in the input list.
|
||||
#[node_macro::node(category("Color"))]
|
||||
fn gradient_type(_: impl Ctx, mut gradient: Table<GradientStops>, gradient_type: vector_types::GradientType) -> Table<GradientStops> {
|
||||
fn gradient_type(_: impl Ctx, mut gradient: List<GradientStops>, gradient_type: vector_types::GradientType) -> List<GradientStops> {
|
||||
for value in gradient.iter_attribute_values_mut_or_default::<vector_types::GradientType>(core_types::ATTR_GRADIENT_TYPE) {
|
||||
*value = gradient_type;
|
||||
}
|
||||
gradient
|
||||
}
|
||||
|
||||
/// Sets how each gradient in the input table extends past its endpoints: Pad, Reflect, or Repeat.
|
||||
/// Sets how each gradient in the input list extends past its endpoints: Pad, Reflect, or Repeat.
|
||||
#[node_macro::node(category("Color"))]
|
||||
fn spread_method(_: impl Ctx, mut gradient: Table<GradientStops>, spread_method: vector_types::GradientSpreadMethod) -> Table<GradientStops> {
|
||||
fn spread_method(_: impl Ctx, mut gradient: List<GradientStops>, spread_method: vector_types::GradientSpreadMethod) -> List<GradientStops> {
|
||||
for value in gradient.iter_attribute_values_mut_or_default::<vector_types::GradientSpreadMethod>(core_types::ATTR_SPREAD_METHOD) {
|
||||
*value = spread_method;
|
||||
}
|
||||
@@ -883,12 +883,12 @@ fn spread_method(_: impl Ctx, mut gradient: Table<GradientStops>, spread_method:
|
||||
|
||||
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
|
||||
#[node_macro::node(category("Color"))]
|
||||
fn sample_gradient(_: impl Ctx, _primary: (), gradient: Table<GradientStops>, position: Fraction) -> Table<Color> {
|
||||
let Some(gradient) = gradient.element(0) else { return Table::new() };
|
||||
fn sample_gradient(_: impl Ctx, _primary: (), gradient: List<GradientStops>, position: Fraction) -> List<Color> {
|
||||
let Some(gradient) = gradient.element(0) else { return List::new() };
|
||||
|
||||
let position = position.clamp(0., 1.);
|
||||
let color = gradient.evaluate(position);
|
||||
Table::new_from_element(color)
|
||||
List::new_from_element(color)
|
||||
}
|
||||
|
||||
/// Constructs a footprint value which may be set to any transformation of a unit square describing a render area, and a render resolution at least 1x1 integer pixels.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, BlendMode, Color, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -14,15 +14,15 @@ use vector_types::kurbo::{Affine, BezPath, CubicBez, Line, ParamCurve, PathSeg,
|
||||
pub use vector_types::vector::misc::BooleanOperation;
|
||||
|
||||
// TODO: Fix boolean ops to work by removing .transform() and .one_instance_*() calls,
|
||||
// TODO: since before we used a Vec of single-item `Table`s and now we use a single `Table`
|
||||
// TODO: since before we used a Vec of single-item `List`s and now we use a single `List`
|
||||
// TODO: with multiple items while still assuming a single item for the boolean operations.
|
||||
|
||||
/// Combines the geometric forms of one or more closed paths into a new vector path that results from cutting or joining the paths by the chosen method.
|
||||
#[node_macro::node(category("Vector: Modifier"), memoize)]
|
||||
async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clone>(
|
||||
async fn boolean_operation<I: graphic_types::IntoGraphicList + 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The `Table` of vector paths to perform the boolean operation on. Nested `Table`s are automatically flattened.
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
/// The `List` of vector paths to perform the boolean operation on. Nested `List`s are automatically flattened.
|
||||
#[implementations(List<Graphic>, List<Vector>)]
|
||||
content: I,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
///
|
||||
@@ -31,32 +31,32 @@ async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clon
|
||||
/// Intersection cuts away all but the overlapping areas shared by every path.
|
||||
/// Difference cuts away the overlapping areas shared by every path, leaving only the non-overlapping areas.
|
||||
operation: BooleanOperation,
|
||||
) -> Table<Vector> {
|
||||
let content = content.into_graphic_table();
|
||||
) -> List<Vector> {
|
||||
let content = content.into_graphic_list();
|
||||
|
||||
// The first index is the bottom of the stack
|
||||
let flattened = flatten_vector(&content);
|
||||
let mut result_vector_table = boolean_operation_on_vector_table(&flattened, operation);
|
||||
let mut result_vector_list = boolean_operation_on_vector_list(&flattened, operation);
|
||||
|
||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||
if result_vector_table.element_mut(0).is_some() {
|
||||
let transform: DAffine2 = result_vector_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
result_vector_table.set_attribute(ATTR_TRANSFORM, 0, DAffine2::IDENTITY);
|
||||
if result_vector_list.element_mut(0).is_some() {
|
||||
let transform: DAffine2 = result_vector_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
result_vector_list.set_attribute(ATTR_TRANSFORM, 0, DAffine2::IDENTITY);
|
||||
|
||||
let result_vector = result_vector_table.element_mut(0).unwrap();
|
||||
let result_vector = result_vector_list.element_mut(0).unwrap();
|
||||
Vector::transform(result_vector, transform);
|
||||
result_vector.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
|
||||
// Snapshot the input layers as the `editor:merged_layers` attribute so the renderer can recurse into them
|
||||
// for editor click-target preservation.
|
||||
result_vector_table.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, content.clone());
|
||||
result_vector_list.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, content.clone());
|
||||
|
||||
// Clean up the boolean operation result by merging duplicated points
|
||||
let merge_transform: DAffine2 = result_vector_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
result_vector_table.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
||||
let merge_transform: DAffine2 = result_vector_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
result_vector_list.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
||||
}
|
||||
|
||||
result_vector_table
|
||||
result_vector_list
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
@@ -113,9 +113,9 @@ impl WindingNumber {
|
||||
}
|
||||
}
|
||||
|
||||
fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation: BooleanOperation) -> Table<Vector> {
|
||||
fn boolean_operation_on_vector_list(vector: &List<Vector>, boolean_operation: BooleanOperation) -> List<Vector> {
|
||||
const EPSILON: f64 = 1e-5;
|
||||
let mut table = Table::new();
|
||||
let mut list = List::new();
|
||||
let mut paths = Vec::new();
|
||||
|
||||
let copy_from_index = if matches!(boolean_operation, BooleanOperation::SubtractFront) {
|
||||
@@ -146,8 +146,8 @@ fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation:
|
||||
Ok(top) => top,
|
||||
Err(e) => {
|
||||
log::error!("Boolean operation failed while building topology: {e}");
|
||||
table.push(row);
|
||||
return table;
|
||||
list.push(row);
|
||||
return list;
|
||||
}
|
||||
};
|
||||
let contours = top.contours(|winding| winding.is_inside(boolean_operation));
|
||||
@@ -158,18 +158,18 @@ fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation:
|
||||
row.element_mut().append_subpath(subpath.reverse(), false);
|
||||
}
|
||||
|
||||
table.push(row);
|
||||
table
|
||||
list.push(row);
|
||||
list
|
||||
}
|
||||
|
||||
fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
(0..graphic_table.len())
|
||||
fn flatten_vector(graphic_list: &List<Graphic>) -> List<Vector> {
|
||||
(0..graphic_list.len())
|
||||
.flat_map(|index| {
|
||||
let graphic = graphic_table.element(index).unwrap();
|
||||
let graphic = graphic_list.element(index).unwrap();
|
||||
match graphic.clone() {
|
||||
Graphic::Vector(vector) => {
|
||||
// Apply the parent graphic's transform to each element of the `Table<Vector>`
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
// Apply the parent graphic's transform to each element of the `List<Vector>`
|
||||
let parent_transform: DAffine2 = graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
vector
|
||||
.into_iter()
|
||||
.map(|mut sub_vector| {
|
||||
@@ -180,7 +180,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::RasterCPU(image) => {
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let parent_transform: DAffine2 = graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let make_item = |transform, layer, blend_mode: BlendMode, opacity: f64, fill: f64, clip: bool| {
|
||||
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
@@ -202,7 +202,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
(0..image.len())
|
||||
.map(|i| {
|
||||
let row_transform: DAffine2 = image.attribute_cloned_or_default(ATTR_TRANSFORM, i);
|
||||
let layer: Table<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||
let layer: List<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||
let blend_mode: BlendMode = image.attribute_cloned_or_default(ATTR_BLEND_MODE, i);
|
||||
let opacity: f64 = image.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
||||
let fill: f64 = image.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
||||
@@ -212,7 +212,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::RasterGPU(image) => {
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let parent_transform: DAffine2 = graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
let make_item = |transform, layer, blend_mode: BlendMode, opacity: f64, fill: f64, clip: bool| {
|
||||
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
@@ -234,7 +234,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
(0..image.len())
|
||||
.map(|i| {
|
||||
let row_transform: DAffine2 = image.attribute_cloned_or_default(ATTR_TRANSFORM, i);
|
||||
let layer: Table<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||
let layer: List<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||
let blend_mode: BlendMode = image.attribute_cloned_or_default(ATTR_BLEND_MODE, i);
|
||||
let opacity: f64 = image.attribute_cloned_or(ATTR_OPACITY, i, 1.);
|
||||
let fill: f64 = image.attribute_cloned_or(ATTR_OPACITY_FILL, i, 1.);
|
||||
@@ -244,15 +244,15 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::Graphic(mut graphic) => {
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
// Apply the parent graphic's transform to each element of the inner `Table`
|
||||
let parent_transform: DAffine2 = graphic_list.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
// Apply the parent graphic's transform to each element of the inner `List`
|
||||
for transform in graphic.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform = parent_transform * *transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner `Table` into the output `Table<Vector>`
|
||||
// Recursively flatten the inner `List` into the output `List<Vector>`
|
||||
let flattened = flatten_vector(&graphic);
|
||||
let unioned = boolean_operation_on_vector_table(&flattened, BooleanOperation::Union);
|
||||
let unioned = boolean_operation_on_vector_list(&flattened, BooleanOperation::Union);
|
||||
|
||||
unioned.into_iter().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ impl Adjust<Color> for Color {
|
||||
#[cfg(feature = "std")]
|
||||
mod adjust_std {
|
||||
use super::*;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use raster_types::{CPU, Raster};
|
||||
use vector_types::GradientStops;
|
||||
|
||||
impl Adjust<Color> for Table<Raster<CPU>> {
|
||||
impl Adjust<Color> for List<Raster<CPU>> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for element in self.iter_element_values_mut() {
|
||||
for color in element.data_mut().data.iter_mut() {
|
||||
@@ -25,14 +25,14 @@ mod adjust_std {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for Table<Color> {
|
||||
impl Adjust<Color> for List<Color> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for element in self.iter_element_values_mut() {
|
||||
*element = map_fn(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for Table<GradientStops> {
|
||||
impl Adjust<Color> for List<GradientStops> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for element in self.iter_element_values_mut() {
|
||||
element.adjust(&map_fn);
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::adjust::Adjust;
|
||||
use crate::cubic_spline::CubicSplines;
|
||||
use core::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use glam::{Vec3, Vec4};
|
||||
use no_std_types::color::Color;
|
||||
use no_std_types::context::Ctx;
|
||||
@@ -53,9 +53,9 @@ pub enum LuminanceCalculation {
|
||||
fn luminance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -78,9 +78,9 @@ fn luminance<T: Adjust<Color>>(
|
||||
fn gamma_correction<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -99,9 +99,9 @@ fn gamma_correction<T: Adjust<Color>>(
|
||||
fn extract_channel<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -123,9 +123,9 @@ fn extract_channel<T: Adjust<Color>>(
|
||||
fn make_opaque<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -145,9 +145,9 @@ fn make_opaque<T: Adjust<Color>>(
|
||||
fn brightness_contrast_classic<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -176,9 +176,9 @@ fn brightness_contrast_classic<T: Adjust<Color>>(
|
||||
fn brightness_contrast<T: Adjust<Color>>(
|
||||
_ctx: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -257,9 +257,9 @@ fn brightness_contrast<T: Adjust<Color>>(
|
||||
fn levels<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -321,14 +321,14 @@ fn levels<T: Adjust<Color>>(
|
||||
// Algorithm from:
|
||||
// https://stackoverflow.com/a/55233732/775283
|
||||
// Works the same for gamma and linear color
|
||||
// TODO: Currently the un-Table-wrapped `tint` Color is causing a type error. Put this back in the "Raster: Adjustment" category once that's fixed.
|
||||
// TODO: Currently the un-List-wrapped `tint` Color is causing a type error. Put this back in the "Raster: Adjustment" category once that's fixed.
|
||||
#[node_macro::node(name("Black & White"), category(""), properties("black_and_white_properties"), shader_node(PerPixelAdjust))]
|
||||
fn black_and_white<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -399,9 +399,9 @@ fn black_and_white<T: Adjust<Color>>(
|
||||
fn hue_saturation<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -434,9 +434,9 @@ fn hue_saturation<T: Adjust<Color>>(
|
||||
fn invert<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -457,9 +457,9 @@ fn invert<T: Adjust<Color>>(
|
||||
fn threshold<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -503,9 +503,9 @@ fn threshold<T: Adjust<Color>>(
|
||||
fn vibrance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -682,9 +682,9 @@ pub enum DomainWarpType {
|
||||
fn channel_mixer<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -816,9 +816,9 @@ pub enum SelectiveColorChoice {
|
||||
fn selective_color<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -962,9 +962,9 @@ fn selective_color<T: Adjust<Color>>(
|
||||
fn posterize<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
@@ -996,9 +996,9 @@ fn posterize<T: Adjust<Color>>(
|
||||
fn exposure<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::adjust::Adjust;
|
||||
#[cfg(feature = "std")]
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use no_std_types::Ctx;
|
||||
use no_std_types::blending::BlendMode;
|
||||
use no_std_types::color::{Color, Pixel};
|
||||
@@ -23,54 +23,54 @@ impl Blend<Color> for Color {
|
||||
mod blend_std {
|
||||
use super::*;
|
||||
use core::cmp::Ordering;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use raster_types::Image;
|
||||
use raster_types::Raster;
|
||||
|
||||
impl Blend<Color> for Table<Raster<CPU>> {
|
||||
impl Blend<Color> for List<Raster<CPU>> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
let pair_count = result_table.len().min(under.len());
|
||||
let mut result_list = self.clone();
|
||||
let pair_count = result_list.len().min(under.len());
|
||||
for index in 0..pair_count {
|
||||
let Some(over) = result_table.element(index) else { break };
|
||||
let Some(over) = result_list.element(index) else { break };
|
||||
let Some(under_element) = under.element(index) else { break };
|
||||
let data = over.data.iter().zip(under_element.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
let (width, height) = (over.width, over.height);
|
||||
|
||||
*result_table.element_mut(index).unwrap() = Raster::new_cpu(Image {
|
||||
*result_list.element_mut(index).unwrap() = Raster::new_cpu(Image {
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
base64_string: None,
|
||||
});
|
||||
}
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for Table<Color> {
|
||||
impl Blend<Color> for List<Color> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
let pair_count = result_table.len().min(under.len());
|
||||
let mut result_list = self.clone();
|
||||
let pair_count = result_list.len().min(under.len());
|
||||
for index in 0..pair_count {
|
||||
let Some(over) = result_table.element(index) else { break };
|
||||
let Some(over) = result_list.element(index) else { break };
|
||||
let Some(under_element) = under.element(index) else { break };
|
||||
let new_val = blend_fn(*over, *under_element);
|
||||
*result_table.element_mut(index).unwrap() = new_val;
|
||||
*result_list.element_mut(index).unwrap() = new_val;
|
||||
}
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for Table<GradientStops> {
|
||||
impl Blend<Color> for List<GradientStops> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
let pair_count = result_table.len().min(under.len());
|
||||
let mut result_list = self.clone();
|
||||
let pair_count = result_list.len().min(under.len());
|
||||
for index in 0..pair_count {
|
||||
let Some(over) = result_table.element(index) else { break };
|
||||
let Some(over) = result_list.element(index) else { break };
|
||||
let Some(under_element) = under.element(index) else { break };
|
||||
let new_val = over.blend(under_element, &blend_fn);
|
||||
*result_table.element_mut(index).unwrap() = new_val;
|
||||
*result_list.element_mut(index).unwrap() = new_val;
|
||||
}
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for GradientStops {
|
||||
@@ -145,17 +145,17 @@ pub fn apply_blend_mode(foreground: Color, background: Color, blend_mode: BlendM
|
||||
fn mix<T: Blend<Color> + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
over: T,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
under: T,
|
||||
@@ -169,9 +169,9 @@ fn mix<T: Blend<Color> + Send>(
|
||||
fn color_overlay<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut image: T,
|
||||
@@ -197,7 +197,7 @@ fn color_overlay<T: Adjust<Color>>(
|
||||
mod test {
|
||||
use core_types::blending::BlendMode;
|
||||
use core_types::color::Color;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use raster_types::Image;
|
||||
use raster_types::Raster;
|
||||
|
||||
@@ -212,7 +212,7 @@ mod test {
|
||||
// 100% of the output should come from the multiplied value
|
||||
let opacity = 100.;
|
||||
|
||||
let result = super::color_overlay((), Table::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();
|
||||
|
||||
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core_types::context::Ctx;
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::Percentage;
|
||||
use core_types::table::Table;
|
||||
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
|
||||
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
|
||||
use raster_types::Image;
|
||||
@@ -8,7 +8,7 @@ use raster_types::{CPU, Raster};
|
||||
use std::cmp::{max, min};
|
||||
|
||||
#[node_macro::node(category("Raster: Filter"))]
|
||||
async fn dehaze(_: impl Ctx, image_frame: Table<Raster<CPU>>, strength: Percentage) -> Table<Raster<CPU>> {
|
||||
async fn dehaze(_: impl Ctx, image_frame: List<Raster<CPU>>, strength: Percentage) -> List<Raster<CPU>> {
|
||||
image_frame
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core_types::color::Color;
|
||||
use core_types::context::Ctx;
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::PixelLength;
|
||||
use core_types::table::Table;
|
||||
use raster_types::Image;
|
||||
use raster_types::{Bitmap, BitmapMut};
|
||||
use raster_types::{CPU, Raster};
|
||||
@@ -11,7 +11,7 @@ use raster_types::{CPU, Raster};
|
||||
async fn blur(
|
||||
_: impl Ctx,
|
||||
/// The image to be blurred.
|
||||
image_frame: Table<Raster<CPU>>,
|
||||
image_frame: List<Raster<CPU>>,
|
||||
/// The radius of the blur kernel.
|
||||
#[range((0., 100.))]
|
||||
#[hard_min(0.)]
|
||||
@@ -20,7 +20,7 @@ async fn blur(
|
||||
box_blur: bool,
|
||||
/// Opt to incorrectly apply the filter with color calculations in gamma space for compatibility with the results from other software.
|
||||
gamma: bool,
|
||||
) -> Table<Raster<CPU>> {
|
||||
) -> List<Raster<CPU>> {
|
||||
image_frame
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -47,12 +47,12 @@ async fn blur(
|
||||
async fn median_filter(
|
||||
_: impl Ctx,
|
||||
/// The image to be filtered.
|
||||
image_frame: Table<Raster<CPU>>,
|
||||
image_frame: List<Raster<CPU>>,
|
||||
/// The radius of the filter kernel. Larger values remove more noise but may blur fine details.
|
||||
#[range((0., 50.))]
|
||||
#[hard_min(0.)]
|
||||
radius: PixelLength,
|
||||
) -> Table<Raster<CPU>> {
|
||||
) -> List<Raster<CPU>> {
|
||||
image_frame
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Not immediately shader compatible due to needing [`GradientStops`] as a param, which needs [`Vec`]
|
||||
|
||||
use crate::adjust::Adjust;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use core_types::{Color, Ctx};
|
||||
use raster_types::{CPU, Raster};
|
||||
use vector_types::GradientStops;
|
||||
@@ -13,12 +13,12 @@ use vector_types::GradientStops;
|
||||
async fn gradient_map<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut image: T,
|
||||
gradient: Table<GradientStops>,
|
||||
gradient: List<GradientStops>,
|
||||
reverse: bool,
|
||||
) -> T {
|
||||
let Some(gradient) = gradient.element(0) else { return image };
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use core_types::color::Color;
|
||||
use core_types::context::Ctx;
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::list::{Item, List};
|
||||
use raster_types::{CPU, Raster};
|
||||
|
||||
#[node_macro::node(category("Color"))]
|
||||
async fn image_color_palette(
|
||||
_: impl Ctx,
|
||||
image: Table<Raster<CPU>>,
|
||||
image: List<Raster<CPU>>,
|
||||
#[default(4)]
|
||||
#[hard_min(1)]
|
||||
count: u32,
|
||||
) -> Table<Color> {
|
||||
) -> List<Color> {
|
||||
const GRID: f32 = 3.;
|
||||
|
||||
let bins = GRID * GRID * GRID;
|
||||
@@ -71,7 +71,7 @@ mod test {
|
||||
fn test_image_color_palette() {
|
||||
let result = image_color_palette(
|
||||
(),
|
||||
Table::new_from_element(Raster::new_cpu(Image {
|
||||
List::new_from_element(Raster::new_cpu(Image {
|
||||
width: 100,
|
||||
height: 100,
|
||||
data: vec![Color::from_rgbaf32(0., 0., 0., 1.).unwrap(); 10000],
|
||||
@@ -79,6 +79,6 @@ mod test {
|
||||
})),
|
||||
1,
|
||||
);
|
||||
assert_eq!(futures::executor::block_on(result), Table::new_from_element(Color::from_rgbaf32(0., 0., 0., 1.).unwrap()));
|
||||
assert_eq!(futures::executor::block_on(result), List::new_from_element(Color::from_rgbaf32(0., 0., 0., 1.).unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use core_types::ATTR_TRANSFORM;
|
||||
use core_types::color::Color;
|
||||
use core_types::color::{Alpha, AlphaMut, Channel, LinearChannel, Luminance, RGBMut};
|
||||
use core_types::context::{Ctx, ExtractFootprint};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::math::bbox::Bbox;
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::transform::Transform;
|
||||
use dyn_any::DynAny;
|
||||
use fastnoise_lite;
|
||||
@@ -30,7 +30,7 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Table<Raster<CPU>>) -> Table<Raster<CPU>> {
|
||||
pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: List<Raster<CPU>>) -> List<Raster<CPU>> {
|
||||
image_frame
|
||||
.into_iter()
|
||||
.filter_map(|row| {
|
||||
@@ -97,11 +97,11 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Tabl
|
||||
pub fn combine_channels(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[expose] red: Table<Raster<CPU>>,
|
||||
#[expose] green: Table<Raster<CPU>>,
|
||||
#[expose] blue: Table<Raster<CPU>>,
|
||||
#[expose] alpha: Table<Raster<CPU>>,
|
||||
) -> Table<Raster<CPU>> {
|
||||
#[expose] red: List<Raster<CPU>>,
|
||||
#[expose] green: List<Raster<CPU>>,
|
||||
#[expose] blue: List<Raster<CPU>>,
|
||||
#[expose] alpha: List<Raster<CPU>>,
|
||||
) -> List<Raster<CPU>> {
|
||||
let max_len = red.len().max(green.len()).max(blue.len()).max(alpha.len());
|
||||
let red = red.into_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let green = green.into_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
@@ -178,11 +178,11 @@ pub fn combine_channels(
|
||||
pub fn mask(
|
||||
_: impl Ctx,
|
||||
/// The image to be masked.
|
||||
image: Table<Raster<CPU>>,
|
||||
image: List<Raster<CPU>>,
|
||||
/// The stencil to be used for masking.
|
||||
#[expose]
|
||||
stencil: Table<Raster<CPU>>,
|
||||
) -> Table<Raster<CPU>> {
|
||||
stencil: List<Raster<CPU>>,
|
||||
) -> List<Raster<CPU>> {
|
||||
// TODO: Figure out what it means to support multiple stencil items?
|
||||
let Some(stencil) = stencil.into_iter().next() else {
|
||||
// No stencil provided so we return the original image
|
||||
@@ -226,7 +226,7 @@ pub fn mask(
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DAffine2) -> Table<Raster<CPU>> {
|
||||
pub fn extend_image_to_bounds(_: impl Ctx, image: List<Raster<CPU>>, bounds: DAffine2) -> List<Raster<CPU>> {
|
||||
image
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -240,7 +240,7 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
|
||||
let image_data = &row.element().data;
|
||||
let (image_width, image_height) = (row.element().width, row.element().height);
|
||||
if image_width == 0 || image_height == 0 {
|
||||
return empty_image((), bounds, Table::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap();
|
||||
return empty_image((), bounds, List::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap();
|
||||
}
|
||||
|
||||
let orig_image_scale = DVec2::new(image_width as f64, image_height as f64);
|
||||
@@ -274,23 +274,23 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Table<Color>) -> Table<Raster<CPU>> {
|
||||
pub fn empty_image(_: impl Ctx, transform: DAffine2, color: List<Color>) -> List<Raster<CPU>> {
|
||||
let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32;
|
||||
let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32;
|
||||
|
||||
let color = color.element(0).copied().unwrap_or(Color::WHITE);
|
||||
let image = Image::new(width, height, color);
|
||||
|
||||
let mut result_table = Table::new_from_element(Raster::new_cpu(image));
|
||||
result_table.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||
let mut result_list = List::new_from_element(Raster::new_cpu(image));
|
||||
result_list.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||
|
||||
// Callers of empty_image can safely unwrap on returned `Table`
|
||||
result_table
|
||||
// Callers of empty_image can safely unwrap on returned `List`
|
||||
result_list
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn image(_: impl Ctx, _primary: (), image: Image<Color>) -> Table<Raster<CPU>> {
|
||||
Table::new_from_element(Raster::new_cpu(image))
|
||||
pub fn image(_: impl Ctx, _primary: (), image: Image<Color>) -> List<Raster<CPU>> {
|
||||
List::new_from_element(Raster::new_cpu(image))
|
||||
}
|
||||
|
||||
/// Generates customizable procedural noise patterns.
|
||||
@@ -328,7 +328,7 @@ pub fn noise_pattern(
|
||||
#[widget(ParsedWidgetOverride::Custom = "noise_properties_cellular_jitter")]
|
||||
#[default(1.)]
|
||||
cellular_jitter: f64,
|
||||
) -> Table<Raster<CPU>> {
|
||||
) -> List<Raster<CPU>> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
@@ -346,7 +346,7 @@ pub fn noise_pattern(
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
|
||||
let transform = DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
@@ -392,7 +392,7 @@ pub fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
return Table::new_from_item(Item::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform));
|
||||
return List::new_from_item(Item::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform));
|
||||
}
|
||||
};
|
||||
noise.set_noise_type(Some(noise_type));
|
||||
@@ -450,11 +450,11 @@ pub fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
Table::new_from_item(Item::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform))
|
||||
List::new_from_item(Item::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Pattern"))]
|
||||
pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> Table<Raster<CPU>> {
|
||||
pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> List<Raster<CPU>> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
@@ -466,7 +466,7 @@ pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> Table<Raster<CPU>> {
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
|
||||
let scale = footprint.scale();
|
||||
@@ -488,7 +488,7 @@ pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> Table<Raster<CPU>> {
|
||||
}
|
||||
}
|
||||
|
||||
Table::new_from_item(
|
||||
List::new_from_item(
|
||||
Item::new_from_element(Raster::new_cpu(Image {
|
||||
width,
|
||||
height,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::gcore::Context;
|
||||
use core::f64::consts::TAU;
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::{Angle, PixelSize};
|
||||
use core_types::table::Table;
|
||||
use core_types::{ATTR_TRANSFORM, CloneVarArgs, Color, Ctx, ExtractAll, InjectVarArgs, OwnedContextImpl};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::{Graphic, Vector};
|
||||
@@ -12,23 +12,23 @@ use vector_types::GradientStops;
|
||||
async fn repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = List<T>>,
|
||||
#[default(1)]
|
||||
#[hard_min(1)]
|
||||
count: u32,
|
||||
reverse: bool,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
// Someday this node can have the option to generate infinitely instead of a fixed count (basically `std::iter::repeat`).
|
||||
|
||||
let count = count.max(1) as usize;
|
||||
|
||||
let mut result_table = Table::new();
|
||||
let mut result_list = List::new();
|
||||
|
||||
for index in 0..count {
|
||||
let index = if reverse { count - index - 1 } else { index };
|
||||
@@ -37,24 +37,24 @@ async fn repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||
|
||||
for generated_row in generated_content.into_iter() {
|
||||
result_table.push(generated_row);
|
||||
result_list.push(generated_row);
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Repeat"))]
|
||||
pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = List<T>>,
|
||||
#[default(100., 100.)]
|
||||
// TODO: When using a custom Properties panel layout in document_node_definitions.rs and this default is set, the widget weirdly doesn't show up in the Properties panel. Investigation is needed.
|
||||
direction: PixelSize,
|
||||
@@ -62,12 +62,12 @@ pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
#[default(5)]
|
||||
#[hard_min(1)]
|
||||
count: u32,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
let angle = angle.to_radians();
|
||||
let count = count.max(1);
|
||||
let total = (count - 1) as f64;
|
||||
|
||||
let mut result_table = Table::new();
|
||||
let mut result_list = List::new();
|
||||
|
||||
for index in 0..count {
|
||||
let angle = index as f64 * angle / total;
|
||||
@@ -85,24 +85,24 @@ pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
||||
*row.attribute_mut_or_insert_default(ATTR_TRANSFORM) = local_translation * transform * local_matrix;
|
||||
|
||||
result_table.push(row);
|
||||
result_list.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Repeat"))]
|
||||
async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = List<T>>,
|
||||
start_angle: Angle,
|
||||
#[unit(" px")]
|
||||
#[default(5)]
|
||||
@@ -110,10 +110,10 @@ async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
#[default(5)]
|
||||
#[hard_min(1)]
|
||||
count: u32,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
let count = count.max(1);
|
||||
|
||||
let mut result_table = Table::new();
|
||||
let mut result_list = List::new();
|
||||
|
||||
for index in 0..count {
|
||||
let angle = DAffine2::from_angle((TAU / count as f64) * index as f64 + start_angle.to_radians());
|
||||
@@ -131,28 +131,28 @@ async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
||||
*row.attribute_mut_or_insert_default(ATTR_TRANSFORM) = local_translation * transform * local_matrix;
|
||||
|
||||
result_table.push(row);
|
||||
result_list.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Repeat"), name("Repeat on Points"))]
|
||||
async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx + InjectVarArgs,
|
||||
points: Table<Vector>,
|
||||
points: List<Vector>,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = List<T>>,
|
||||
reverse: bool,
|
||||
) -> Table<T> {
|
||||
let mut result_table = Table::new();
|
||||
) -> List<T> {
|
||||
let mut result_list = List::new();
|
||||
|
||||
for points_index in 0..points.len() {
|
||||
let Some(points_element) = points.element(points_index) else { continue };
|
||||
@@ -166,7 +166,7 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
|
||||
for mut generated_row in generated_content.into_iter() {
|
||||
generated_row.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM).translation = transformed_point;
|
||||
result_table.push(generated_row);
|
||||
result_list.push(generated_row);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -182,7 +182,7 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -202,8 +202,8 @@ mod test {
|
||||
use vector_nodes::generator_nodes::RectangleNode;
|
||||
use vector_types::subpath::Subpath;
|
||||
|
||||
fn vector_node_from_bezpath(bezpath: BezPath) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_bezpath(bezpath))
|
||||
fn vector_node_from_bezpath(bezpath: BezPath) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_bezpath(bezpath))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -230,7 +230,7 @@ mod test {
|
||||
);
|
||||
|
||||
let positions = [DVec2::new(40., 20.), DVec2::ONE, DVec2::new(-42., 9.), DVec2::new(10., 345.)];
|
||||
let points = Table::new_from_element(Vector::from_subpath(Subpath::from_anchors(positions, false)));
|
||||
let points = List::new_from_element(Vector::from_subpath(Subpath::from_anchors(positions, false)));
|
||||
let generated = super::repeat_on_points(context, points, &rect, false).await;
|
||||
assert_eq!(generated.len(), positions.len());
|
||||
for (position, index) in positions.into_iter().zip(0..generated.len()) {
|
||||
@@ -257,8 +257,8 @@ mod test {
|
||||
count,
|
||||
)
|
||||
.await;
|
||||
let vector_table = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_table.element(0).unwrap();
|
||||
let vector_list = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_list.element(0).unwrap();
|
||||
assert_eq!(vector.region_manipulator_groups().count(), 3);
|
||||
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() {
|
||||
assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5);
|
||||
@@ -278,8 +278,8 @@ mod test {
|
||||
count,
|
||||
)
|
||||
.await;
|
||||
let vector_table = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_table.element(0).unwrap();
|
||||
let vector_list = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_list.element(0).unwrap();
|
||||
assert_eq!(vector.region_manipulator_groups().count(), 8);
|
||||
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() {
|
||||
assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5);
|
||||
@@ -290,8 +290,8 @@ mod test {
|
||||
async fn repeat_radial() {
|
||||
let context = OwnedContextImpl::default().into_context();
|
||||
let repeated = super::repeat_radial(context, &FutureWrapperNode(vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY))), 45., 4., 8).await;
|
||||
let vector_table = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_table.element(0).unwrap();
|
||||
let vector_list = vector_nodes::flatten_path(Footprint::default(), repeated).await;
|
||||
let vector = vector_list.element(0).unwrap();
|
||||
assert_eq!(vector.region_manipulator_groups().count(), 8);
|
||||
|
||||
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::{ATTR_TYPE, Ctx};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -240,10 +240,10 @@ fn query_json_all(
|
||||
/// Strips the surrounding double quotes from string values, returning the raw text. Other types are never wrapped in quotes.
|
||||
#[default(true)]
|
||||
unquote_strings: bool,
|
||||
) -> Table<String> {
|
||||
) -> List<String> {
|
||||
let cleaned = strip_trailing_commas(&json);
|
||||
let Ok(value): Result<Value, _> = serde_json::from_str(&cleaned) else { return Table::new() };
|
||||
let Some(segments) = parse_json_path(path.trim()) else { return Table::new() };
|
||||
let Ok(value): Result<Value, _> = serde_json::from_str(&cleaned) else { return List::new() };
|
||||
let Some(segments) = parse_json_path(path.trim()) else { return List::new() };
|
||||
|
||||
let mut results = Vec::new();
|
||||
resolve_all(&value, &segments, !unquote_strings, &mut results);
|
||||
|
||||
@@ -7,8 +7,8 @@ mod to_path;
|
||||
|
||||
use convert_case::{Boundary, Converter, pattern};
|
||||
use core_types::graphene_hash::CacheHash;
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::registry::types::{SignedInteger, TextArea};
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractVarArgs, OwnedContextImpl};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -737,7 +737,7 @@ fn string_split(
|
||||
/// "\n" (newline), "\r" (carriage return), "\t" (tab), "\0" (null), and "\\" (backslash).
|
||||
#[default(true)]
|
||||
delimiter_escaping: bool,
|
||||
) -> Table<String> {
|
||||
) -> List<String> {
|
||||
let delimiter = if delimiter_escaping { unescape_string(delimiter) } else { delimiter };
|
||||
|
||||
string.split(&delimiter).map(str::to_string).map(Item::new_from_element).collect()
|
||||
@@ -750,7 +750,7 @@ fn string_split(
|
||||
fn string_join(
|
||||
_: impl Ctx,
|
||||
/// The list of strings to join together.
|
||||
strings: Table<String>,
|
||||
strings: List<String>,
|
||||
/// The text placed between each pair of strings.
|
||||
#[default(", ")]
|
||||
separator: String,
|
||||
@@ -768,12 +768,12 @@ fn string_join(
|
||||
#[node_macro::node(category("Text"))]
|
||||
async fn map_string(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
strings: Table<String>,
|
||||
strings: List<String>,
|
||||
#[expose]
|
||||
#[implementations(Context -> String)]
|
||||
mapped: impl Node<Context<'static>, Output = String>,
|
||||
) -> Table<String> {
|
||||
let mut result = Table::new();
|
||||
) -> List<String> {
|
||||
let mut result = List::new();
|
||||
|
||||
for (i, row) in strings.into_iter().enumerate() {
|
||||
let string = row.into_element();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::{ATTR_EDITOR_CLICK_TARGET, ATTR_EDITOR_TEXT_FRAME, ATTR_TRANSFORM};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use parley::GlyphRun;
|
||||
@@ -14,7 +14,7 @@ pub struct PathBuilder {
|
||||
current_subpath: Subpath<PointId>,
|
||||
origin: DVec2,
|
||||
glyph_subpaths: Vec<Subpath<PointId>>,
|
||||
pub vector_table: Table<Vector>,
|
||||
pub vector_list: List<Vector>,
|
||||
/// Per-glyph AABBs collected in single-item mode, published as `ATTR_EDITOR_CLICK_TARGET` in `finalize()`.
|
||||
merged_click_target_bboxes: Vec<[DVec2; 2]>,
|
||||
/// Per-glyph baselines, parallel to `merged_click_target_bboxes`. Groups glyphs by line for the widening pass.
|
||||
@@ -35,7 +35,7 @@ impl PathBuilder {
|
||||
Self {
|
||||
current_subpath: Subpath::new(Vec::new(), false),
|
||||
glyph_subpaths: Vec::new(),
|
||||
vector_table: if per_glyph_items { Table::new() } else { Table::new_from_element(Vector::default()) },
|
||||
vector_list: if per_glyph_items { List::new() } else { List::new_from_element(Vector::default()) },
|
||||
merged_click_target_bboxes: Vec::new(),
|
||||
merged_click_target_baselines: Vec::new(),
|
||||
per_glyph_bboxes: Vec::new(),
|
||||
@@ -87,14 +87,14 @@ impl PathBuilder {
|
||||
let item = Item::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false))
|
||||
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(glyph_offset))
|
||||
.with_attribute(ATTR_EDITOR_TEXT_FRAME, frame_in_item_local);
|
||||
self.vector_table.push(item);
|
||||
self.vector_list.push(item);
|
||||
|
||||
// Defer click target creation to `finalize()` where adjacent AABBs get widened
|
||||
self.per_glyph_bboxes.push(glyph_bbox);
|
||||
} else {
|
||||
for subpath in self.glyph_subpaths.drain(..) {
|
||||
// Unwrapping here is ok because `self.vector_table` is initialized with a single `Table<Vector>` item
|
||||
self.vector_table.element_mut(0).unwrap().append_subpath(subpath, false);
|
||||
// Unwrapping here is ok because `self.vector_list` is initialized with a single `List<Vector>` item
|
||||
self.vector_list.element_mut(0).unwrap().append_subpath(subpath, false);
|
||||
}
|
||||
if let Some(bbox) = glyph_bbox {
|
||||
self.merged_click_target_bboxes.push(bbox);
|
||||
@@ -163,16 +163,16 @@ impl PathBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalize(mut self) -> Table<Vector> {
|
||||
// Empty table = all glyphs clipped by height. Create a placeholder with the same item-0
|
||||
// transform a populated table would have so `local_transforms` stays stable mid-drag.
|
||||
pub fn finalize(mut self) -> List<Vector> {
|
||||
// Empty list = all glyphs clipped by height. Create a placeholder with the same item-0
|
||||
// transform a populated list would have so `local_transforms` stays stable mid-drag.
|
||||
// TODO: Remove this hack and move the attribute up to the parent return value when <https://github.com/GraphiteEditor/Graphite/issues/3779> is done.
|
||||
if self.vector_table.is_empty() {
|
||||
if self.vector_list.is_empty() {
|
||||
let frame_in_item_local = DAffine2::from_scale_angle_translation(self.text_frame_size, 0., -self.first_glyph_offset);
|
||||
let item = Item::new_from_element(Vector::default())
|
||||
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(self.first_glyph_offset))
|
||||
.with_attribute(ATTR_EDITOR_TEXT_FRAME, frame_in_item_local);
|
||||
self.vector_table.push(item);
|
||||
self.vector_list.push(item);
|
||||
}
|
||||
|
||||
// Widen per-glyph AABBs to close horizontal gaps, then publish as click targets
|
||||
@@ -184,7 +184,7 @@ impl PathBuilder {
|
||||
.enumerate()
|
||||
.filter_map(|(index, bbox)| {
|
||||
let bbox = (*bbox)?;
|
||||
let offset = self.vector_table.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, index).translation;
|
||||
let offset = self.vector_list.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, index).translation;
|
||||
Some((index, offset, [bbox[0] + offset, bbox[1] + offset]))
|
||||
})
|
||||
.collect();
|
||||
@@ -197,7 +197,7 @@ impl PathBuilder {
|
||||
for (entry, widened) in entries.iter().zip(layer_bboxes.iter()) {
|
||||
let glyph_local = [widened[0] - entry.1, widened[1] - entry.1];
|
||||
let rect = Subpath::new_rectangle(glyph_local[0], glyph_local[1]);
|
||||
self.vector_table.set_attribute(ATTR_EDITOR_CLICK_TARGET, entry.0, Vector::from_subpaths([rect], false));
|
||||
self.vector_list.set_attribute(ATTR_EDITOR_CLICK_TARGET, entry.0, Vector::from_subpaths([rect], false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,18 +207,18 @@ impl PathBuilder {
|
||||
widen_horizontal_gaps(&mut bboxes, &self.merged_click_target_baselines);
|
||||
|
||||
let widened_subpaths: Vec<_> = bboxes.iter().map(|[min, max]| Subpath::new_rectangle(*min, *max)).collect();
|
||||
self.vector_table.set_attribute(ATTR_EDITOR_CLICK_TARGET, 0, Vector::from_subpaths(widened_subpaths, false));
|
||||
self.vector_list.set_attribute(ATTR_EDITOR_CLICK_TARGET, 0, Vector::from_subpaths(widened_subpaths, false));
|
||||
}
|
||||
|
||||
// Fill in text frame for items that don't have one yet (single-item mode, where item 0 = identity)
|
||||
let frame = DAffine2::from_scale(self.text_frame_size);
|
||||
for index in 0..self.vector_table.len() {
|
||||
if self.vector_table.attribute::<DAffine2>(ATTR_EDITOR_TEXT_FRAME, index).is_none() {
|
||||
self.vector_table.set_attribute(ATTR_EDITOR_TEXT_FRAME, index, frame);
|
||||
for index in 0..self.vector_list.len() {
|
||||
if self.vector_list.attribute::<DAffine2>(ATTR_EDITOR_TEXT_FRAME, index).is_none() {
|
||||
self.vector_list.set_attribute(ATTR_EDITOR_TEXT_FRAME, index, frame);
|
||||
}
|
||||
}
|
||||
|
||||
self.vector_table
|
||||
self.vector_list
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::registry::types::SignedInteger;
|
||||
use core_types::table::{Item, Table};
|
||||
use core_types::{ATTR_END, ATTR_NAME, ATTR_START, Ctx};
|
||||
|
||||
/// Checks whether the string contains a match for the given regular expression pattern. Optionally restricts the match to only the start and/or end of the string.
|
||||
@@ -96,9 +96,9 @@ fn regex_find(
|
||||
case_insensitive: bool,
|
||||
/// Make `^` and `$` match the start and end of each line, not just the whole string.
|
||||
multiline: bool,
|
||||
) -> Table<String> {
|
||||
) -> List<String> {
|
||||
if pattern.is_empty() {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
|
||||
let flags = match (case_insensitive, multiline) {
|
||||
@@ -111,7 +111,7 @@ fn regex_find(
|
||||
|
||||
let Ok(regex) = fancy_regex::Regex::new(&full_pattern) else {
|
||||
log::error!("Invalid regex pattern: {pattern}");
|
||||
return Table::new();
|
||||
return List::new();
|
||||
};
|
||||
|
||||
// Capture group names indexed positionally; index 0 (the whole match) is always None.
|
||||
@@ -124,7 +124,7 @@ fn regex_find(
|
||||
let resolved_index = if match_index < 0 {
|
||||
let from_end = (-match_index) as usize;
|
||||
if from_end > matches.len() {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
matches.len() - from_end
|
||||
} else {
|
||||
@@ -132,7 +132,7 @@ fn regex_find(
|
||||
};
|
||||
|
||||
let Some(captures) = matches.get(resolved_index) else {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
};
|
||||
|
||||
// Index 0 is the whole match, 1+ are capture groups
|
||||
@@ -165,9 +165,9 @@ fn regex_find_all(
|
||||
case_insensitive: bool,
|
||||
/// Make `^` and `$` match the start and end of each line, not just the whole string.
|
||||
multiline: bool,
|
||||
) -> Table<String> {
|
||||
) -> List<String> {
|
||||
if pattern.is_empty() {
|
||||
return Table::new();
|
||||
return List::new();
|
||||
}
|
||||
|
||||
let flags = match (case_insensitive, multiline) {
|
||||
@@ -180,7 +180,7 @@ fn regex_find_all(
|
||||
|
||||
let Ok(regex) = fancy_regex::Regex::new(&full_pattern) else {
|
||||
log::error!("Invalid regex pattern: {pattern}");
|
||||
return Table::new();
|
||||
return List::new();
|
||||
};
|
||||
|
||||
regex
|
||||
@@ -208,9 +208,9 @@ fn regex_split(
|
||||
case_insensitive: bool,
|
||||
/// Make `^` and `$` match the start and end of each line, not just the whole string.
|
||||
multiline: bool,
|
||||
) -> Table<String> {
|
||||
) -> List<String> {
|
||||
if pattern.is_empty() {
|
||||
return Table::new_from_element(string);
|
||||
return List::new_from_element(string);
|
||||
}
|
||||
|
||||
let flags = match (case_insensitive, multiline) {
|
||||
@@ -223,7 +223,7 @@ fn regex_split(
|
||||
|
||||
let Ok(regex) = fancy_regex::Regex::new(&full_pattern) else {
|
||||
log::error!("Invalid regex pattern: {pattern}");
|
||||
return Table::new_from_element(string);
|
||||
return List::new_from_element(string);
|
||||
};
|
||||
|
||||
regex.split(&string).filter_map(|s| s.ok()).map(|s| s.to_string()).map(Item::new_from_element).collect()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{Font, FontCache, TypesettingConfig};
|
||||
use core::cell::RefCell;
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use glam::DVec2;
|
||||
use parley::fontique::{Blob, FamilyId, FontInfo};
|
||||
use parley::{AlignmentOptions, FontContext, Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty};
|
||||
@@ -87,9 +87,9 @@ impl TextContext {
|
||||
}
|
||||
|
||||
/// Convert text to vector paths using the specified font and typesetting configuration
|
||||
pub fn to_path(&mut self, text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> Table<Vector> {
|
||||
pub fn to_path(&mut self, text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> List<Vector> {
|
||||
let Some(layout) = self.layout_text(text, font, font_cache, typesetting) else {
|
||||
return Table::new_from_element(Vector::default());
|
||||
return List::new_from_element(Vector::default());
|
||||
};
|
||||
|
||||
let text_frame_size = DVec2::new(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::text_context::TextContext;
|
||||
use super::{Font, FontCache, TypesettingConfig};
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use glam::DVec2;
|
||||
use parley::fontique::Blob;
|
||||
use std::sync::Arc;
|
||||
use vector_types::Vector;
|
||||
|
||||
pub fn to_path(text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> Table<Vector> {
|
||||
pub fn to_path(text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> List<Vector> {
|
||||
TextContext::with_thread_local(|ctx| ctx.to_path(text, font, font_cache, typesetting, per_glyph_items))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::f64;
|
||||
use core_types::color::Color;
|
||||
use core_types::table::{Table, TableDyn};
|
||||
use core_types::list::{List, ListDyn};
|
||||
use core_types::transform::{ApplyTransform, ScaleType, Transform};
|
||||
use core_types::{ATTR_TRANSFORM, CloneVarArgs, Context, Ctx, ExtractAll, InjectFootprint, ModifyFootprint, OwnedContextImpl};
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
@@ -16,12 +16,12 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
|
||||
#[implementations(
|
||||
Context -> DAffine2,
|
||||
Context -> DVec2,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
Context -> List<Graphic>,
|
||||
Context -> List<Vector>,
|
||||
Context -> List<Raster<CPU>>,
|
||||
Context -> List<Raster<GPU>>,
|
||||
Context -> List<Color>,
|
||||
Context -> List<GradientStops>,
|
||||
)]
|
||||
content: impl Node<Context<'static>, Output = T>,
|
||||
#[widget(ParsedWidgetOverride::Custom = "transform_translation")] translation: DVec2,
|
||||
@@ -56,18 +56,18 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
|
||||
fn reset_transform<T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut content: Table<T>,
|
||||
mut content: List<T>,
|
||||
#[default(true)] reset_translation: bool,
|
||||
reset_rotation: bool,
|
||||
reset_scale: bool,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
if reset_translation {
|
||||
row_transform.translation = DVec2::ZERO;
|
||||
@@ -89,21 +89,21 @@ fn reset_transform<T>(
|
||||
content
|
||||
}
|
||||
|
||||
/// Overwrites the transform of each item in the input `Table` with the specified transform.
|
||||
/// Overwrites the transform of each item in the input `List` with the specified transform.
|
||||
#[node_macro::node(category("Math: Transform"))]
|
||||
fn replace_transform<T>(
|
||||
_: impl Ctx + InjectFootprint,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
)]
|
||||
mut content: Table<T>,
|
||||
mut content: List<T>,
|
||||
transform: DAffine2,
|
||||
) -> Table<T> {
|
||||
) -> List<T> {
|
||||
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*row_transform = transform.transform();
|
||||
}
|
||||
@@ -111,9 +111,9 @@ fn replace_transform<T>(
|
||||
}
|
||||
|
||||
// TODO: Figure out how this node should behave once #2982 is implemented.
|
||||
/// Obtains the transform of the first item in the input `Table`, if present.
|
||||
/// Obtains the transform of the first item in the input `List`, if present.
|
||||
#[node_macro::node(category("Math: Transform"), path(core_types::vector))]
|
||||
async fn extract_transform(_: impl Ctx, content: TableDyn) -> DAffine2 {
|
||||
async fn extract_transform(_: impl Ctx, content: ListDyn) -> DAffine2 {
|
||||
content.attribute::<DAffine2>(ATTR_TRANSFORM, 0).copied().unwrap_or_default()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::{Angle, PixelLength, PixelSize};
|
||||
use core_types::table::Table;
|
||||
use core_types::{CacheHash, Ctx};
|
||||
use dyn_any::DynAny;
|
||||
use glam::DVec2;
|
||||
@@ -10,16 +10,16 @@ use vector_types::vector::misc::{HandleId, SpiralType};
|
||||
use vector_types::vector::{PointId, SegmentId, StrokeId};
|
||||
|
||||
trait CornerRadius {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector>;
|
||||
fn generate(self, size: DVec2, clamped: bool) -> List<Vector>;
|
||||
}
|
||||
impl CornerRadius for f64 {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector> {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> List<Vector> {
|
||||
let clamped_radius = if clamped { self.clamp(0., size.x.min(size.y).max(0.) / 2.) } else { self };
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_rounded_rectangle(size / -2., size / 2., [clamped_radius; 4])))
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_rounded_rectangle(size / -2., size / 2., [clamped_radius; 4])))
|
||||
}
|
||||
}
|
||||
impl CornerRadius for Table<f64> {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector> {
|
||||
impl CornerRadius for List<f64> {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> List<Vector> {
|
||||
// Expand to four corners using the CSS `border-radius` shorthand rules.
|
||||
// - `[a]` → `[a, a, a, a]`
|
||||
// - `[a, b]` → `[a, b, a, b]`
|
||||
@@ -50,7 +50,7 @@ impl CornerRadius for Table<f64> {
|
||||
} else {
|
||||
radii
|
||||
};
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_rounded_rectangle(size / -2., size / 2., clamped_radius)))
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_rounded_rectangle(size / -2., size / 2., clamped_radius)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ fn circle(
|
||||
#[unit(" px")]
|
||||
#[default(50.)]
|
||||
radius: f64,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let radius = radius.abs();
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
|
||||
}
|
||||
|
||||
/// Generates an arc shape forming a portion of a circle which may be open, closed, or a pie slice.
|
||||
@@ -80,8 +80,8 @@ fn arc(
|
||||
#[range((0., 360.))]
|
||||
sweep_angle: Angle,
|
||||
arc_type: ArcType,
|
||||
) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_arc(
|
||||
) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_arc(
|
||||
radius,
|
||||
start_angle / 360. * std::f64::consts::TAU,
|
||||
sweep_angle / 360. * std::f64::consts::TAU,
|
||||
@@ -104,8 +104,8 @@ fn spiral(
|
||||
#[default(0.)] inner_radius: f64,
|
||||
#[default(25)] outer_radius: f64,
|
||||
#[default(90.)] angular_resolution: f64,
|
||||
) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_spiral(
|
||||
) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_spiral(
|
||||
inner_radius,
|
||||
outer_radius,
|
||||
turns,
|
||||
@@ -126,7 +126,7 @@ fn ellipse(
|
||||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_y: f64,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let radius = DVec2::new(radius_x, radius_y);
|
||||
let corner1 = -radius;
|
||||
let corner2 = radius;
|
||||
@@ -140,7 +140,7 @@ fn ellipse(
|
||||
.push([HandleId::end(ellipse.segment_domain.ids()[i]), HandleId::primary(ellipse.segment_domain.ids()[(i + 1) % len])]);
|
||||
}
|
||||
|
||||
Table::new_from_element(ellipse)
|
||||
List::new_from_element(ellipse)
|
||||
}
|
||||
|
||||
/// Generates a rectangle shape with the chosen width and height. It may also have rounded corners if desired.
|
||||
@@ -155,9 +155,9 @@ fn rectangle<T: CornerRadius>(
|
||||
#[default(100)]
|
||||
height: f64,
|
||||
_individual_corner_radii: bool, // TODO: Move this to the bottom once we have a migration capability
|
||||
#[implementations(f64, Table<f64>)] corner_radius: T,
|
||||
#[implementations(f64, List<f64>)] corner_radius: T,
|
||||
#[default(true)] clamped: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
corner_radius.generate(DVec2::new(width, height), clamped)
|
||||
}
|
||||
|
||||
@@ -173,10 +173,10 @@ fn regular_polygon<T: AsU64>(
|
||||
#[unit(" px")]
|
||||
#[default(50)]
|
||||
radius: f64,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let points = sides.as_u64();
|
||||
let radius: f64 = radius * 2.;
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_regular_polygon(DVec2::splat(-radius), points, radius)))
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_regular_polygon(DVec2::splat(-radius), points, radius)))
|
||||
}
|
||||
|
||||
/// Generates an n-pointed star shape with inner and outer points at chosen radii from the center.
|
||||
@@ -194,12 +194,12 @@ fn star<T: AsU64>(
|
||||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_2: f64,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let points = sides.as_u64();
|
||||
let diameter: f64 = radius_1 * 2.;
|
||||
let inner_diameter = radius_2 * 2.;
|
||||
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_star_polygon(DVec2::splat(-diameter), points, diameter, inner_diameter)))
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_star_polygon(DVec2::splat(-diameter), points, diameter, inner_diameter)))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
@@ -233,7 +233,7 @@ fn qr_code(
|
||||
size: f64,
|
||||
error_correction: QRCodeErrorCorrectionLevel,
|
||||
#[default(false)] individual_squares: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let ecc = match error_correction {
|
||||
QRCodeErrorCorrectionLevel::Low => qrcodegen::QrCodeEcc::Low,
|
||||
QRCodeErrorCorrectionLevel::Medium => qrcodegen::QrCodeEcc::Medium,
|
||||
@@ -241,7 +241,7 @@ fn qr_code(
|
||||
QRCodeErrorCorrectionLevel::High => qrcodegen::QrCodeEcc::High,
|
||||
};
|
||||
|
||||
let Ok(qr_code) = qrcodegen::QrCode::encode_text(&text, ecc) else { return Table::default() };
|
||||
let Ok(qr_code) = qrcodegen::QrCode::encode_text(&text, ecc) else { return List::default() };
|
||||
|
||||
let mut vector = match individual_squares {
|
||||
true => {
|
||||
@@ -270,7 +270,7 @@ fn qr_code(
|
||||
vector.transform(glam::DAffine2::from_scale(DVec2::splat(size.max(1.) / qr_code.size() as f64)));
|
||||
}
|
||||
|
||||
Table::new_from_element(vector)
|
||||
List::new_from_element(vector)
|
||||
}
|
||||
|
||||
/// Generates an arrow from the origin to the chosen coordinate.
|
||||
@@ -282,13 +282,13 @@ fn arrow(
|
||||
#[default(10)] shaft_width: PixelLength,
|
||||
#[default(30)] head_width: PixelLength,
|
||||
#[default(20)] head_length: PixelLength,
|
||||
) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_arrow(DVec2::ZERO, arrow_to, shaft_width, head_width, head_length)))
|
||||
) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_arrow(DVec2::ZERO, arrow_to, shaft_width, head_width, head_length)))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn line(_: impl Ctx, _primary: (), #[default(100., 100.)] line_to: PixelSize) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_line(DVec2::ZERO, line_to)))
|
||||
fn line(_: impl Ctx, _primary: (), #[default(100., 100.)] line_to: PixelSize) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_subpath(subpath::Subpath::new_line(DVec2::ZERO, line_to)))
|
||||
}
|
||||
|
||||
trait GridSpacing {
|
||||
@@ -319,7 +319,7 @@ fn grid<T: GridSpacing>(
|
||||
#[default(10)] columns: u32,
|
||||
#[default(10)] rows: u32,
|
||||
#[default(30., 30.)] angles: DVec2,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let (x_spacing, y_spacing) = spacing.as_dvec2().into();
|
||||
let (angle_a, angle_b) = angles.into();
|
||||
|
||||
@@ -401,7 +401,7 @@ fn grid<T: GridSpacing>(
|
||||
}
|
||||
}
|
||||
|
||||
Table::new_from_element(vector)
|
||||
List::new_from_element(vector)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core_types::table::Table;
|
||||
use core_types::list::List;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_EDITOR_CLICK_TARGET, ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Ctx};
|
||||
use glam::DAffine2;
|
||||
@@ -7,8 +7,8 @@ use vector_types::vector::VectorModification;
|
||||
|
||||
/// Applies a differential modification to a vector path, associating changes made by the Pen and Path tools to indices of edited points and segments.
|
||||
#[node_macro::node(category(""))]
|
||||
async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Box<VectorModification>, node_path: Table<NodeId>) -> Table<Vector> {
|
||||
use core_types::table::Item;
|
||||
async fn path_modify(_ctx: impl Ctx, mut vector: List<Vector>, modification: Box<VectorModification>, node_path: List<NodeId>) -> List<Vector> {
|
||||
use core_types::list::Item;
|
||||
|
||||
if vector.is_empty() {
|
||||
vector.push(Item::default());
|
||||
@@ -20,11 +20,11 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||
|
||||
// Set the path to the encapsulating subgraph (drop our own trailing entry from `node_path`),
|
||||
// matching the `path_of_subgraph` proto so editor tools can route data back to the parent layer.
|
||||
let subgraph_path: Table<NodeId> = {
|
||||
let subgraph_path: List<NodeId> = {
|
||||
let len = node_path.len();
|
||||
node_path.into_iter().take(len.saturating_sub(1)).collect()
|
||||
};
|
||||
let existing: Table<NodeId> = vector.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0);
|
||||
let existing: List<NodeId> = vector.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0);
|
||||
vector.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, if existing.is_empty() { subgraph_path } else { existing });
|
||||
|
||||
if vector.len() > 1 {
|
||||
@@ -35,7 +35,7 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||
|
||||
/// Applies the vector path's local transformation to its geometry and resets the transform to the identity.
|
||||
#[node_macro::node(category("Vector"))]
|
||||
async fn apply_transform(_ctx: impl Ctx, mut vector: Table<Vector>) -> Table<Vector> {
|
||||
async fn apply_transform(_ctx: impl Ctx, mut vector: List<Vector>) -> List<Vector> {
|
||||
let (elements, transforms) = vector.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||
for (element, transform) in elements.iter_mut().zip(transforms.iter_mut()) {
|
||||
for (_, point) in element.point_domain.positions_mut() {
|
||||
|
||||
@@ -3,8 +3,8 @@ use core::f64::consts::{PI, TAU};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core_types::blending::BlendMode;
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::list::{Item, List, ListDyn};
|
||||
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
|
||||
use core_types::table::{Item, Table, TableDyn};
|
||||
use core_types::transform::{Footprint, Transform};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{
|
||||
@@ -14,7 +14,7 @@ use core_types::{
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
use graphic_types::Vector;
|
||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||
use graphic_types::{Graphic, IntoGraphicTable};
|
||||
use graphic_types::{Graphic, IntoGraphicList};
|
||||
use kurbo::simplify::{SimplifyOptions, simplify_bezpath};
|
||||
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveArclen, PathEl, PathSeg, Shape};
|
||||
use rand::{Rng, SeedableRng};
|
||||
@@ -33,18 +33,18 @@ use vector_types::vector::style::{Fill, Gradient, GradientStops, PaintOrder, Str
|
||||
use vector_types::vector::{FillId, PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorExt};
|
||||
|
||||
/// Implemented for types that contain vector items reachable via mutable access.
|
||||
/// Used for the fill and stroke nodes so they can apply to either `Table<Graphic>` or `Table<Vector>`.
|
||||
trait VectorTableIterMut {
|
||||
/// Used for the fill and stroke nodes so they can apply to either `List<Graphic>` or `List<Vector>`.
|
||||
trait VectorListIterMut {
|
||||
fn for_each_vector_mut(&mut self, f: impl FnMut(&mut Vector, DAffine2));
|
||||
|
||||
fn vector_count(&self) -> usize;
|
||||
}
|
||||
|
||||
impl VectorTableIterMut for Table<Graphic> {
|
||||
impl VectorListIterMut for List<Graphic> {
|
||||
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
||||
for graphic in self.iter_element_values_mut() {
|
||||
let Some(vector_table) = graphic.as_vector_mut() else { continue };
|
||||
let (elements, transforms) = vector_table.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||
let Some(vector_list) = graphic.as_vector_mut() else { continue };
|
||||
let (elements, transforms) = vector_list.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
||||
f(vector, *transform);
|
||||
}
|
||||
@@ -52,11 +52,11 @@ impl VectorTableIterMut for Table<Graphic> {
|
||||
}
|
||||
|
||||
fn vector_count(&self) -> usize {
|
||||
self.iter_element_values().filter_map(|element| element.as_vector()).map(|table| table.len()).sum()
|
||||
self.iter_element_values().filter_map(|element| element.as_vector()).map(|list| list.len()).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl VectorTableIterMut for Table<Vector> {
|
||||
impl VectorListIterMut for List<Vector> {
|
||||
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
||||
let (elements, transforms) = self.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
||||
@@ -74,7 +74,7 @@ impl VectorTableIterMut for Table<Vector> {
|
||||
async fn assign_colors<T>(
|
||||
_: impl Ctx,
|
||||
/// The content with vector paths to apply the fill and/or stroke style to.
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
#[implementations(List<Graphic>, List<Vector>)]
|
||||
#[widget(ParsedWidgetOverride::Hidden)]
|
||||
mut content: T,
|
||||
/// Whether to style the fill.
|
||||
@@ -84,7 +84,7 @@ async fn assign_colors<T>(
|
||||
stroke: bool,
|
||||
/// The range of colors to select from.
|
||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
|
||||
gradient: Table<GradientStops>,
|
||||
gradient: List<GradientStops>,
|
||||
/// Whether to reverse the gradient.
|
||||
reverse: bool,
|
||||
/// Whether to randomize the color selection for each element from throughout the gradient.
|
||||
@@ -98,7 +98,7 @@ async fn assign_colors<T>(
|
||||
repeat_every: u32,
|
||||
) -> T
|
||||
where
|
||||
T: VectorTableIterMut + 'n + Send,
|
||||
T: VectorListIterMut + 'n + Send,
|
||||
{
|
||||
let Some(row) = gradient.into_iter().next() else { return content };
|
||||
|
||||
@@ -136,34 +136,34 @@ where
|
||||
|
||||
/// Applies a fill style to the vector content, giving an appearance to the area within the interior of the geometry.
|
||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
||||
async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>(
|
||||
async fn fill<F: Into<Fill> + 'n + Send, V: VectorListIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
/// The content with vector paths to apply the fill style to.
|
||||
#[implementations(
|
||||
Table<Vector>,
|
||||
Table<Vector>,
|
||||
Table<Vector>,
|
||||
Table<Vector>,
|
||||
Table<Graphic>,
|
||||
Table<Graphic>,
|
||||
Table<Graphic>,
|
||||
Table<Graphic>,
|
||||
List<Vector>,
|
||||
List<Vector>,
|
||||
List<Vector>,
|
||||
List<Vector>,
|
||||
List<Graphic>,
|
||||
List<Graphic>,
|
||||
List<Graphic>,
|
||||
List<Graphic>,
|
||||
)]
|
||||
mut content: V,
|
||||
/// The fill to paint the path with.
|
||||
#[default(Color::BLACK)]
|
||||
#[implementations(
|
||||
Fill,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
Gradient,
|
||||
Fill,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
Gradient,
|
||||
)]
|
||||
fill: F,
|
||||
_backup_color: Table<Color>,
|
||||
_backup_color: List<Color>,
|
||||
_backup_gradient: Gradient,
|
||||
) -> V {
|
||||
let fill: Fill = fill.into();
|
||||
@@ -182,7 +182,7 @@ impl IntoF64Vec for f64 {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
impl IntoF64Vec for Table<f64> {
|
||||
impl IntoF64Vec for List<f64> {
|
||||
fn into_vec(self) -> Vec<f64> {
|
||||
self.into_iter().map(|row| row.into_element()).collect()
|
||||
}
|
||||
@@ -198,11 +198,11 @@ impl IntoF64Vec for String {
|
||||
async fn stroke<V, L: IntoF64Vec>(
|
||||
_: impl Ctx,
|
||||
/// The content with vector paths to apply the stroke style to.
|
||||
#[implementations(Table<Vector>, Table<Vector>, Table<Vector>, Table<Graphic>, Table<Graphic>, Table<Graphic>)]
|
||||
mut content: Table<V>,
|
||||
#[implementations(List<Vector>, List<Vector>, List<Vector>, List<Graphic>, List<Graphic>, List<Graphic>)]
|
||||
mut content: List<V>,
|
||||
/// The stroke color.
|
||||
#[default(Color::BLACK)]
|
||||
color: Table<Color>,
|
||||
color: List<Color>,
|
||||
/// The stroke thickness.
|
||||
#[unit(" px")]
|
||||
#[default(2.)]
|
||||
@@ -220,14 +220,14 @@ async fn stroke<V, L: IntoF64Vec>(
|
||||
/// The order to paint the stroke on top of the fill, or the fill on top of the stroke.
|
||||
paint_order: PaintOrder,
|
||||
/// The stroke dash lengths. Each length forms a distance in a pattern where the first length is a dash, the second is a gap, and so on. If the list is an odd length, the pattern repeats with solid-gap roles reversed.
|
||||
#[implementations(Table<f64>, f64, String, Table<f64>, f64, String)]
|
||||
#[implementations(List<f64>, f64, String, List<f64>, f64, String)]
|
||||
dash_lengths: L,
|
||||
/// The phase offset distance from the starting point of the dash pattern.
|
||||
#[unit(" px")]
|
||||
dash_offset: f64,
|
||||
) -> Table<V>
|
||||
) -> List<V>
|
||||
where
|
||||
Table<V>: VectorTableIterMut + 'n + Send,
|
||||
List<V>: VectorListIterMut + 'n + Send,
|
||||
{
|
||||
let dash_lengths = dash_lengths.into_vec().into_iter().map(|length| length.max(0.)).collect();
|
||||
|
||||
@@ -256,11 +256,11 @@ where
|
||||
#[node_macro::node(name("Copy to Points"), category("Repeat"), path(core_types::vector))]
|
||||
async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
points: Table<Vector>,
|
||||
points: List<Vector>,
|
||||
/// Artwork to be copied and placed at each point.
|
||||
#[expose]
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>, Table<GradientStops>)]
|
||||
content: Table<I>,
|
||||
#[implementations(List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Color>, List<GradientStops>)]
|
||||
content: List<I>,
|
||||
/// Minimum range of randomized sizes given to each placed copy.
|
||||
#[default(1)]
|
||||
#[range((0., 2.))]
|
||||
@@ -281,8 +281,8 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
random_rotation: Angle,
|
||||
/// Seed to determine unique variations on all the randomized copy angles.
|
||||
random_rotation_seed: SeedValue,
|
||||
) -> Table<I> {
|
||||
let mut result_table = Table::new();
|
||||
) -> List<I> {
|
||||
let mut result_list = List::new();
|
||||
|
||||
let random_scale_difference = random_scale_max - random_scale_min;
|
||||
|
||||
@@ -325,18 +325,18 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||
row.set_attribute(ATTR_TRANSFORM, transform * row_transform);
|
||||
|
||||
result_table.push(row);
|
||||
result_list.push(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
result_list
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn round_corners(
|
||||
_: impl Ctx,
|
||||
source: Table<Vector>,
|
||||
source: List<Vector>,
|
||||
#[hard_min(0.)]
|
||||
#[default(10.)]
|
||||
radius: PixelLength,
|
||||
@@ -351,7 +351,7 @@ async fn round_corners(
|
||||
#[hard_max(180.)]
|
||||
#[default(5.)]
|
||||
min_angle_threshold: Angle,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
(0..source.len())
|
||||
.map(|index| {
|
||||
let source_transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
@@ -450,12 +450,12 @@ async fn round_corners(
|
||||
#[node_macro::node(name("Merge by Distance"), category("Vector: Modifier"), path(core_types::vector))]
|
||||
pub fn merge_by_distance(
|
||||
_: impl Ctx,
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
#[default(0.1)]
|
||||
#[hard_min(0.0001)]
|
||||
distance: PixelLength,
|
||||
algorithm: MergeByDistanceAlgorithm,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
match algorithm {
|
||||
MergeByDistanceAlgorithm::Spatial => content
|
||||
.into_iter()
|
||||
@@ -673,7 +673,7 @@ pub mod extrude_algorithms {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn extrude(_: impl Ctx, mut source: Table<Vector>, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> Table<Vector> {
|
||||
async fn extrude(_: impl Ctx, mut source: List<Vector>, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> List<Vector> {
|
||||
for vector in source.iter_element_values_mut() {
|
||||
extrude_algorithms::extrude(vector, direction, joining_algorithm);
|
||||
}
|
||||
@@ -681,7 +681,7 @@ async fn extrude(_: impl Ctx, mut source: Table<Vector>, direction: DVec2, joini
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn box_warp(_: impl Ctx, content: Table<Vector>, #[expose] rectangle: Table<Vector>) -> Table<Vector> {
|
||||
async fn box_warp(_: impl Ctx, content: List<Vector>, #[expose] rectangle: List<Vector>) -> List<Vector> {
|
||||
let Some(target) = rectangle.element(0).cloned() else { return content };
|
||||
let target_transform: DAffine2 = rectangle.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
|
||||
@@ -746,7 +746,7 @@ async fn box_warp(_: impl Ctx, content: Table<Vector>, #[expose] rectangle: Tabl
|
||||
|
||||
result.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
|
||||
// Add this to the `Table` and reset the transform since we've applied it directly to the points
|
||||
// Add this to the `List` and reset the transform since we've applied it directly to the points
|
||||
*row.element_mut() = result;
|
||||
row.set_attribute(ATTR_TRANSFORM, DAffine2::IDENTITY);
|
||||
row
|
||||
@@ -769,12 +769,12 @@ fn bilinear_interpolate(t: DVec2, quad: &[DVec2; 4]) -> DVec2 {
|
||||
async fn pack_strips<T: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Raster<GPU>>,
|
||||
)]
|
||||
elements: Table<T>,
|
||||
elements: List<T>,
|
||||
#[default(0.)]
|
||||
#[unit(" px")]
|
||||
separation: f64,
|
||||
@@ -782,10 +782,10 @@ async fn pack_strips<T: 'n + Send + Clone>(
|
||||
#[unit(" px")]
|
||||
strip_max_length: f64,
|
||||
strip_direction: RowsOrColumns,
|
||||
) -> Table<T>
|
||||
) -> List<T>
|
||||
where
|
||||
Graphic: From<Table<T>>,
|
||||
Table<T>: BoundingBox,
|
||||
Graphic: From<List<T>>,
|
||||
List<T>: BoundingBox,
|
||||
{
|
||||
// Packs shapes using bounds with Best-Fit Decreasing Height (BFDH) algorithm:
|
||||
// - Sort shapes by cross-axis size (tallest first for rows, widest first for columns)
|
||||
@@ -802,8 +802,8 @@ where
|
||||
let mut items: Vec<(f64, f64, DVec2, Item<T>)> = elements
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
// Single-item `Table` to query its bounding box
|
||||
let single = Table::new_from_item(row.clone());
|
||||
// Single-item `List` to query its bounding box
|
||||
let single = List::new_from_item(row.clone());
|
||||
let (w, h, top_left) = match single.bounding_box(DAffine2::IDENTITY, false) {
|
||||
RenderBoundingBox::Rectangle([min, max]) => {
|
||||
let size = max - min;
|
||||
@@ -822,7 +822,7 @@ where
|
||||
// Sort by cross-axis size, largest first
|
||||
items.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
|
||||
|
||||
let mut result = Table::new();
|
||||
let mut result = List::new();
|
||||
let mut strips: Vec<Strip> = Vec::new();
|
||||
|
||||
// This looks n^2 but it is just n*k where k is the number of strips, which is generally much smaller than n
|
||||
@@ -889,7 +889,7 @@ where
|
||||
#[node_macro::node(category("Vector: Modifier"), name("Auto-Tangents"), path(core_types::vector))]
|
||||
async fn auto_tangents(
|
||||
_: impl Ctx,
|
||||
source: Table<Vector>,
|
||||
source: List<Vector>,
|
||||
/// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread).
|
||||
#[default(0.5)]
|
||||
// TODO: Make this a soft range to allow any value to be typed in outside the slider range of 0 to 1
|
||||
@@ -898,7 +898,7 @@ async fn auto_tangents(
|
||||
/// If active, existing non-zero handles won't be affected.
|
||||
#[default(true)]
|
||||
preserve_existing: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
(0..source.len())
|
||||
.map(|index| {
|
||||
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
@@ -1041,7 +1041,7 @@ async fn auto_tangents(
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn bounding_box(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
async fn bounding_box(_: impl Ctx, content: List<Vector>) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -1066,7 +1066,7 @@ async fn bounding_box(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||
async fn dimensions(_: impl Ctx, content: Table<Vector>) -> DVec2 {
|
||||
async fn dimensions(_: impl Ctx, content: List<Vector>) -> DVec2 {
|
||||
(0..content.len())
|
||||
.filter_map(|index| content.element(index).unwrap().bounding_box_with_transform(content.attribute_cloned_or_default(ATTR_TRANSFORM, index)))
|
||||
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
|
||||
@@ -1079,16 +1079,16 @@ async fn dimensions(_: impl Ctx, content: Table<Vector>) -> DVec2 {
|
||||
///
|
||||
/// This is useful in conjunction with nodes that repeat it, followed by the "Points to Polyline" node to string together a path of the points.
|
||||
#[node_macro::node(category("Vector"), name("Vec2 to Point"), path(core_types::vector))]
|
||||
async fn vec2_to_point(_: impl Ctx, vec2: DVec2) -> Table<Vector> {
|
||||
async fn vec2_to_point(_: impl Ctx, vec2: DVec2) -> List<Vector> {
|
||||
let mut point_domain = PointDomain::new();
|
||||
point_domain.push(PointId::generate(), vec2);
|
||||
|
||||
Table::new_from_item(Item::new_from_element(Vector { point_domain, ..Default::default() }))
|
||||
List::new_from_item(Item::new_from_element(Vector { point_domain, ..Default::default() }))
|
||||
}
|
||||
|
||||
/// Creates a polyline from a series of vector points, replacing any existing segments and regions that may already exist.
|
||||
#[node_macro::node(category("Vector"), name("Points to Polyline"), path(core_types::vector))]
|
||||
async fn points_to_polyline(_: impl Ctx, mut points: Table<Vector>, #[default(true)] closed: bool) -> Table<Vector> {
|
||||
async fn points_to_polyline(_: impl Ctx, mut points: List<Vector>, #[default(true)] closed: bool) -> List<Vector> {
|
||||
for vector in points.iter_element_values_mut() {
|
||||
let mut segment_domain = SegmentDomain::new();
|
||||
let mut next_id = SegmentId::ZERO;
|
||||
@@ -1116,7 +1116,7 @@ async fn points_to_polyline(_: impl Ctx, mut points: Table<Vector>, #[default(tr
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("offset_path_properties"))]
|
||||
async fn offset_path(_: impl Ctx, content: Table<Vector>, distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> Table<Vector> {
|
||||
async fn offset_path(_: impl Ctx, content: List<Vector>, distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -1160,13 +1160,13 @@ async fn offset_path(_: impl Ctx, content: Table<Vector>, distance: f64, join: S
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn solidify_stroke<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: T) -> Table<Vector> {
|
||||
async fn solidify_stroke<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Vector>)] content: T) -> List<Vector> {
|
||||
// TODO: Make this node support stroke align, which it currently ignores
|
||||
|
||||
let graphic_table = content.into_graphic_table();
|
||||
let flattened: Table<Vector> = graphic_table.clone().into_flattened_table();
|
||||
let graphic_list = content.into_graphic_list();
|
||||
let flattened: List<Vector> = graphic_list.clone().into_flattened_list();
|
||||
|
||||
let mut output: Table<Vector> = flattened
|
||||
let mut output: List<Vector> = flattened
|
||||
.into_iter()
|
||||
.flat_map(|row| {
|
||||
let (mut vector, attributes) = row.into_parts();
|
||||
@@ -1227,7 +1227,7 @@ async fn solidify_stroke<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #
|
||||
|
||||
let stroke_row = Item::from_parts(solidified_stroke, attributes);
|
||||
|
||||
// Ordering based on the paint order. The first item in the `Table` is rendered below the second.
|
||||
// Ordering based on the paint order. The first item in the `List` is rendered below the second.
|
||||
match paint_order {
|
||||
PaintOrder::StrokeAbove => fill_row.into_iter().chain(std::iter::once(stroke_row)).collect::<Vec<_>>(),
|
||||
PaintOrder::StrokeBelow => std::iter::once(stroke_row).chain(fill_row).collect::<Vec<_>>(),
|
||||
@@ -1241,23 +1241,23 @@ async fn solidify_stroke<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #
|
||||
// Row 0 carries a composed transform inherited from the flattened input, but the merged_layers
|
||||
// already holds the original transforms; pre-compensate by row 0's inverse so the renderer's
|
||||
// `upstream_footprint *= row_0_transform` recursion cancels out and leaves the originals intact.
|
||||
let mut graphic_table = graphic_table;
|
||||
let mut graphic_list = graphic_list;
|
||||
let row_0_transform: DAffine2 = output.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||
if row_0_transform.matrix2.determinant().abs() > f64::EPSILON {
|
||||
let inverse = row_0_transform.inverse();
|
||||
for transform in graphic_table.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
for transform in graphic_list.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform = inverse * *transform;
|
||||
}
|
||||
}
|
||||
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_table);
|
||||
output.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_list);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn separate_subpaths(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
async fn separate_subpaths(_: impl Ctx, content: List<Vector>) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.flat_map(|row| {
|
||||
@@ -1283,7 +1283,7 @@ async fn separate_subpaths(_: impl Ctx, content: Table<Vector>) -> Table<Vector>
|
||||
async fn path_is_closed(
|
||||
_: impl Ctx,
|
||||
/// The vector content whose subpaths are inspected.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The index of the subpath to check, counting across subpaths in all vector elements.
|
||||
index: f64,
|
||||
) -> bool {
|
||||
@@ -1295,7 +1295,7 @@ async fn path_is_closed(
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
async fn map_points(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: Table<Vector>, mapped: impl Node<Context<'static>, Output = DVec2>) -> Table<Vector> {
|
||||
async fn map_points(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: List<Vector>, mapped: impl Node<Context<'static>, Output = DVec2>) -> List<Vector> {
|
||||
let mut content = content;
|
||||
let mut index = 0;
|
||||
|
||||
@@ -1313,18 +1313,18 @@ async fn map_points(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: Table<Ve
|
||||
|
||||
// TODO: Rename to "Combine Paths" and make this happen per-element instead of flattening every element into a single path. The migration for this should then become a Flatten Vector -> Combine Paths pair of nodes.
|
||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: T) -> Table<Vector> {
|
||||
let graphic_table = content.into_graphic_table();
|
||||
let flattened = graphic_table.clone().into_flattened_table::<Vector>();
|
||||
pub async fn flatten_path<T: IntoGraphicList + 'n + Send>(_: impl Ctx, #[implementations(List<Graphic>, List<Vector>)] content: T) -> List<Vector> {
|
||||
let graphic_list = content.into_graphic_list();
|
||||
let flattened = graphic_list.clone().into_flattened_list::<Vector>();
|
||||
|
||||
// Create a `Table` with one empty `Vector` element, then get a mutable reference to it which we append flattened subpaths to
|
||||
let mut output_table = Table::new_from_element(Vector::default());
|
||||
let output = output_table.element_mut(0).unwrap();
|
||||
// Create a `List` with one empty `Vector` element, then get a mutable reference to it which we append flattened subpaths to
|
||||
let mut output_list = List::new_from_element(Vector::default());
|
||||
let output = output_list.element_mut(0).unwrap();
|
||||
|
||||
// Concatenate every vector element's subpaths into the single output compound path
|
||||
for index in 0..flattened.len() {
|
||||
let Some(element) = flattened.element(index) else { continue };
|
||||
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let layer_path: List<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||
let node_id = layer_path.iter_element_values().next_back().map(|node_id| node_id.0).unwrap_or_default();
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
@@ -1338,26 +1338,26 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||
output.style = element.style.clone();
|
||||
}
|
||||
|
||||
// Preserve a reference to the original upstream `Table<Graphic>` so the renderer can recurse into it
|
||||
// Preserve a reference to the original upstream `List<Graphic>` so the renderer can recurse into it
|
||||
// when collecting metadata, exposing the original child layers' click targets to editor tools.
|
||||
// This is the same mechanism Boolean Operation uses to keep its inputs editable after the merge.
|
||||
output_table.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_table);
|
||||
output_list.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_list);
|
||||
|
||||
// Adopt the last input item's layer so the editor can also bucket clicks under a contributing child layer
|
||||
if !flattened.is_empty() {
|
||||
let primary = flattened.len() - 1;
|
||||
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary);
|
||||
output_table.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, layer_path);
|
||||
let layer_path: List<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary);
|
||||
output_list.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, layer_path);
|
||||
}
|
||||
|
||||
output_table
|
||||
output_list
|
||||
}
|
||||
|
||||
/// Convert vector geometry into a polyline composed of evenly spaced points.
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("sample_polyline_properties"), memoize)]
|
||||
async fn sample_polyline(
|
||||
_: impl Ctx,
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
spacing: PointSpacingType,
|
||||
#[default(100.)]
|
||||
#[hard_min(0.)]
|
||||
@@ -1373,7 +1373,7 @@ async fn sample_polyline(
|
||||
#[unit(" px")]
|
||||
stop_offset: f64,
|
||||
adaptive_spacing: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let pathseg_perimeter = |segment: PathSeg| {
|
||||
if is_linear(segment) {
|
||||
Line::new(segment.start(), segment.end()).perimeter(DEFAULT_ACCURACY)
|
||||
@@ -1444,12 +1444,12 @@ async fn sample_polyline(
|
||||
async fn simplify(
|
||||
_: impl Ctx,
|
||||
/// The vector paths to simplify.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The maximum distance the simplified path may deviate from the original.
|
||||
#[default(5.)]
|
||||
#[unit(" px")]
|
||||
tolerance: Length,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
if tolerance <= 0. {
|
||||
return content;
|
||||
}
|
||||
@@ -1488,12 +1488,12 @@ async fn simplify(
|
||||
async fn decimate(
|
||||
_: impl Ctx,
|
||||
/// The vector paths to decimate.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The maximum distance a point can deviate from the simplified path before it is kept.
|
||||
#[default(5.)]
|
||||
#[unit(" px")]
|
||||
tolerance: Length,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
// Tolerance of 0 means no simplification is possible, so return immediately
|
||||
if tolerance <= 0. {
|
||||
return content;
|
||||
@@ -1616,14 +1616,14 @@ async fn decimate(
|
||||
async fn cut_path(
|
||||
_: impl Ctx,
|
||||
/// The path to insert a cut into.
|
||||
mut content: Table<Vector>,
|
||||
mut content: List<Vector>,
|
||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||
progression: Progression,
|
||||
/// Swap the direction of the path.
|
||||
reverse: bool,
|
||||
/// Traverse the path using each segment's Bézier curve parameterization instead of the Euclidean distance. Faster to compute but doesn't respect actual distances.
|
||||
parameterized_distance: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let euclidian = !parameterized_distance;
|
||||
|
||||
let bezpaths = content
|
||||
@@ -1664,7 +1664,7 @@ async fn cut_path(
|
||||
|
||||
/// Cuts path segments into separate disconnected pieces where each is a distinct subpath.
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn cut_segments(_: impl Ctx, mut content: Table<Vector>) -> Table<Vector> {
|
||||
async fn cut_segments(_: impl Ctx, mut content: List<Vector>) -> List<Vector> {
|
||||
// Iterate through every segment and make a copy of each of its endpoints, then reassign each segment's endpoints to its own unique point copy
|
||||
for vector in content.iter_element_values_mut() {
|
||||
let points_count = vector.point_domain.ids().len();
|
||||
@@ -1726,7 +1726,7 @@ async fn cut_segments(_: impl Ctx, mut content: Table<Vector>) -> Table<Vector>
|
||||
async fn position_on_path(
|
||||
_: impl Ctx,
|
||||
/// The path to traverse.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||
progression: Progression,
|
||||
/// Swap the direction of the path.
|
||||
@@ -1764,7 +1764,7 @@ async fn position_on_path(
|
||||
async fn tangent_on_path(
|
||||
_: impl Ctx,
|
||||
/// The path to traverse.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||
progression: Progression,
|
||||
/// Swap the direction of the path.
|
||||
@@ -1811,14 +1811,14 @@ async fn tangent_on_path(
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), memoize)]
|
||||
async fn scatter_points(
|
||||
_: impl Ctx,
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
#[unit(" px")]
|
||||
#[default(10.)]
|
||||
#[hard_min(0.01)]
|
||||
#[range((1., 100.))]
|
||||
separation: f64,
|
||||
seed: SeedValue,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||
|
||||
content
|
||||
@@ -1858,7 +1858,7 @@ async fn scatter_points(
|
||||
}
|
||||
|
||||
#[node_macro::node(name("Spline"), category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn spline(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
async fn spline(_: impl Ctx, content: List<Vector>) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.filter_map(|mut row| {
|
||||
@@ -1961,7 +1961,7 @@ fn apply_point_deltas(element: &mut Vector, deltas: &[DVec2], transform: DAffine
|
||||
async fn jitter_points(
|
||||
_: impl Ctx,
|
||||
/// The vector geometry with points to be jittered.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The maximum extent of the random distance each point can be offset.
|
||||
#[default(5.)]
|
||||
#[unit(" px")]
|
||||
@@ -1971,7 +1971,7 @@ async fn jitter_points(
|
||||
/// Whether to offset anchor points along their normal direction (perpendicular to the path) or in a random direction. Free-floating and branching points have no normal direction, so they receive a random-angled offset regardless of this setting.
|
||||
#[default(true)]
|
||||
along_normals: bool,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -2011,12 +2011,12 @@ async fn jitter_points(
|
||||
async fn offset_points(
|
||||
_: impl Ctx,
|
||||
/// The vector geometry with points to be offset.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The distance to offset each anchor point along its normal. Positive values move outward, negative values move inward.
|
||||
#[default(10.)]
|
||||
#[unit(" px")]
|
||||
distance: f64,
|
||||
) -> Table<Vector> {
|
||||
) -> List<Vector> {
|
||||
content
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -2045,10 +2045,10 @@ async fn offset_points(
|
||||
///
|
||||
/// *Progression* morphs through all objects. Interpolation is linear unless *Path* geometry is provided to control the trajectory between key objects. The **Origins to Polyline** node may be used to create a path with anchor points corresponding to each object. Other nodes can modify its path segments.
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
async fn morph<I: IntoGraphicList + 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The vector objects to interpolate between. Mixed graphic content is deeply flattened to keep only vector elements.
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
#[implementations(List<Graphic>, List<Vector>)]
|
||||
content: I,
|
||||
/// The fractional part `[0, 1)` traverses the morph uniformly along the path. If the control path has multiple subpaths, each added integer selects the next subpath.
|
||||
progression: Progression,
|
||||
@@ -2059,8 +2059,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
/// "Objects" morphs through each group element at an equal rate. "Distances" keeps constant speed with time between objects proportional to their distances. "Angles" keeps constant rotational speed. "Sizes" keeps constant shrink/growth speed. "Slants" keeps constant shearing angle speed.
|
||||
distribution: InterpolationDistribution,
|
||||
/// An optional control path whose anchor points correspond to each object. Curved segments between points will shape the morph trajectory instead of traveling straight. If there is a break between path segments, the separate subpaths are selected by index from the integer part of the progression value. For example, `[1, 2)` morphs along the segments of the second subpath, and so on.
|
||||
path: Table<Vector>,
|
||||
) -> Table<Vector> {
|
||||
path: List<Vector>,
|
||||
) -> List<Vector> {
|
||||
/// Promotes a segment's handle pair to cubic-equivalent Bézier control points.
|
||||
/// For linear segments (both None), handles are placed at their respective anchors (zero-length)
|
||||
/// so that interpolation against another zero-length cubic doesn't introduce unwanted curvature.
|
||||
@@ -2158,11 +2158,11 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve original `Table<Graphic>` as upstream data so this group layer's nested layers can be edited by the tools.
|
||||
let mut graphic_table_content = content.clone().into_graphic_table();
|
||||
// Preserve original `List<Graphic>` as upstream data so this group layer's nested layers can be edited by the tools.
|
||||
let mut graphic_list_content = content.clone().into_graphic_list();
|
||||
|
||||
// If the input isn't a Table<Vector>, we convert it into one by flattening any Table<Graphic> content.
|
||||
let content = content.into_flattened_table::<Vector>();
|
||||
// If the input isn't a List<Vector>, we convert it into one by flattening any List<Graphic> content.
|
||||
let content = content.into_flattened_list::<Vector>();
|
||||
|
||||
// Not enough elements to interpolate between, so we return the input as-is
|
||||
if content.len() <= 1 {
|
||||
@@ -2398,7 +2398,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
// in which case we skip pre-compensation to avoid propagating NaN through merged_layers transforms.
|
||||
if lerped_transform.matrix2.determinant().abs() > f64::EPSILON {
|
||||
let lerped_inverse = lerped_transform.inverse();
|
||||
for transform in graphic_table_content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
for transform in graphic_list_content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||
*transform = lerped_inverse * *transform;
|
||||
}
|
||||
}
|
||||
@@ -2411,9 +2411,9 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
|
||||
let mut attributes = content.clone_item_attributes(endpoint_index);
|
||||
attributes.insert(ATTR_TRANSFORM, lerped_transform);
|
||||
attributes.insert(ATTR_EDITOR_MERGED_LAYERS, graphic_table_content);
|
||||
attributes.insert(ATTR_EDITOR_MERGED_LAYERS, graphic_list_content);
|
||||
|
||||
return Table::new_from_item(Item::from_parts(endpoint_element.clone(), attributes));
|
||||
return List::new_from_item(Item::from_parts(endpoint_element.clone(), attributes));
|
||||
}
|
||||
|
||||
let mut vector = Vector {
|
||||
@@ -2567,9 +2567,9 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
// The result is a synthesis of source and target, so adopt whichever endpoint the result is closer to as
|
||||
// the click-target identity (so the editor can route clicks back to one of the contributing layers)
|
||||
let primary_index = if time < 0.5 { source_index } else { target_index };
|
||||
let layer_path: Table<NodeId> = content.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary_index);
|
||||
let layer_path: List<NodeId> = content.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary_index);
|
||||
|
||||
Table::new_from_item(
|
||||
List::new_from_item(
|
||||
Item::new_from_element(vector)
|
||||
.with_attribute(ATTR_TRANSFORM, lerped_transform)
|
||||
.with_attribute(ATTR_BLEND_MODE, lerped_blend_mode)
|
||||
@@ -2577,7 +2577,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
.with_attribute(ATTR_OPACITY_FILL, lerped_fill)
|
||||
.with_attribute(ATTR_CLIPPING_MASK, lerped_clip)
|
||||
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer_path)
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, graphic_table_content),
|
||||
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, graphic_list_content),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2852,7 +2852,7 @@ fn bevel_algorithm(mut vector: Vector, transform: DAffine2, distance: f64) -> Ve
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
fn bevel(_: impl Ctx, source: Table<Vector>, #[default(10.)] distance: Length) -> Table<Vector> {
|
||||
fn bevel(_: impl Ctx, source: List<Vector>, #[default(10.)] distance: Length) -> List<Vector> {
|
||||
source
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
@@ -2865,7 +2865,7 @@ fn bevel(_: impl Ctx, source: Table<Vector>, #[default(10.)] distance: Length) -
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||
fn close_path(_: impl Ctx, source: Table<Vector>) -> Table<Vector> {
|
||||
fn close_path(_: impl Ctx, source: List<Vector>) -> List<Vector> {
|
||||
source
|
||||
.into_iter()
|
||||
.map(|mut row| {
|
||||
@@ -2876,7 +2876,7 @@ fn close_path(_: impl Ctx, source: Table<Vector>) -> Table<Vector> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||
fn point_inside(_: impl Ctx, source: Table<Vector>, point: DVec2) -> bool {
|
||||
fn point_inside(_: impl Ctx, source: List<Vector>, point: DVec2) -> bool {
|
||||
source.into_iter().any(|row| {
|
||||
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||
row.element().check_point_inside_shape(transform, point)
|
||||
@@ -2886,22 +2886,22 @@ fn point_inside(_: impl Ctx, source: Table<Vector>, point: DVec2) -> bool {
|
||||
// TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs.
|
||||
// TODO: (Currently automatic type conversion only works for concrete types, via the Graphene preprocessor and not the full Graphene type system.)
|
||||
#[node_macro::node(category("General"), path(graphene_core::vector))]
|
||||
async fn count_elements(_: impl Ctx, content: TableDyn) -> f64 {
|
||||
async fn count_elements(_: impl Ctx, content: ListDyn) -> f64 {
|
||||
content.len() as f64
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||
async fn count_points(_: impl Ctx, content: Table<Vector>) -> f64 {
|
||||
async fn count_points(_: impl Ctx, content: List<Vector>) -> f64 {
|
||||
content.iter_element_values().map(|vector| vector.point_domain.positions().len() as f64).sum()
|
||||
}
|
||||
|
||||
/// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `Table` of vector elements.
|
||||
/// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `List` of vector elements.
|
||||
/// If no value exists at that index, the position (0, 0) is returned.
|
||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||
async fn index_points(
|
||||
_: impl Ctx,
|
||||
/// The vector element or elements containing the anchor points to be retrieved.
|
||||
content: Table<Vector>,
|
||||
content: List<Vector>,
|
||||
/// The index of the points to retrieve, starting from 0 for the first point. Negative indices count backwards from the end, starting from -1 for the last item.
|
||||
index: f64,
|
||||
) -> DVec2 {
|
||||
@@ -2932,7 +2932,7 @@ async fn index_points(
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||
async fn path_length(_: impl Ctx, source: Table<Vector>) -> f64 {
|
||||
async fn path_length(_: impl Ctx, source: List<Vector>) -> f64 {
|
||||
(0..source.len())
|
||||
.map(|index| {
|
||||
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
@@ -2951,7 +2951,7 @@ async fn path_length(_: impl Ctx, source: Table<Vector>) -> f64 {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||
async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Context<'static>, Output = Table<Vector>>) -> f64 {
|
||||
async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Context<'static>, Output = List<Vector>>) -> f64 {
|
||||
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
|
||||
let vector = content.eval(new_ctx).await;
|
||||
|
||||
@@ -2965,7 +2965,7 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Cont
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||
async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Context<'static>, Output = Table<Vector>>, centroid_type: CentroidType) -> DVec2 {
|
||||
async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Context<'static>, Output = List<Vector>>, centroid_type: CentroidType) -> DVec2 {
|
||||
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
|
||||
let vector = content.eval(new_ctx).await;
|
||||
|
||||
@@ -3042,8 +3042,8 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
fn vector_node_from_bezpath(bezpath: BezPath) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_bezpath(bezpath))
|
||||
fn vector_node_from_bezpath(bezpath: BezPath) -> List<Vector> {
|
||||
List::new_from_element(Vector::from_bezpath(bezpath))
|
||||
}
|
||||
|
||||
fn create_vector_item(bezpath: BezPath, transform: DAffine2) -> Item<Vector> {
|
||||
@@ -3070,7 +3070,7 @@ mod test {
|
||||
|
||||
// Test a rectangular path with non-zero rotation
|
||||
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
||||
let mut square = Table::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));
|
||||
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
||||
let bounding_box = bounding_box.element(0).unwrap();
|
||||
@@ -3156,9 +3156,9 @@ mod test {
|
||||
let bezpath = Rect::new(100., 100., 201., 201.).to_path(DEFAULT_ACCURACY);
|
||||
let transform = DAffine2::from_scale(DVec2::new(2., 2.));
|
||||
let row = create_vector_item(bezpath, transform);
|
||||
let table = (0..5).map(|_| row.clone()).collect::<Table<Vector>>();
|
||||
let list = (0..5).map(|_| row.clone()).collect::<List<Vector>>();
|
||||
|
||||
let length = super::path_length(Footprint::default(), table).await;
|
||||
let length = super::path_length(Footprint::default(), list).await;
|
||||
|
||||
// 101 (each rectangle edge length) * 4 (rectangle perimeter) * 2 (scale) * 5 (number of rows)
|
||||
assert_eq!(length, 101. * 4. * 2. * 5.);
|
||||
@@ -3177,7 +3177,7 @@ mod test {
|
||||
*second_rectangle.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM) *= DAffine2::from_translation((-100., -100.).into());
|
||||
rectangles.push(second_rectangle);
|
||||
|
||||
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), Table::default()).await;
|
||||
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), List::default()).await;
|
||||
let morphed_element = morphed.element(0).unwrap();
|
||||
// Geometry stays in local space (original rectangle coordinates)
|
||||
assert_eq!(
|
||||
@@ -3259,11 +3259,11 @@ mod test {
|
||||
source.push(curve.as_path_el());
|
||||
|
||||
let vector = Vector::from_bezpath(source);
|
||||
let mut vector_table = Table::new_from_element(vector.clone());
|
||||
let mut vector_list = List::new_from_element(vector.clone());
|
||||
|
||||
vector_table.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((), Table::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();
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 4);
|
||||
@@ -3310,8 +3310,8 @@ mod test {
|
||||
|
||||
let subpath = BezPath::from_path_segments([line, point, curve].into_iter());
|
||||
|
||||
let beveled_table = super::bevel(Footprint::default(), vector_node_from_bezpath(subpath), 5.);
|
||||
let beveled = beveled_table.element(0).unwrap();
|
||||
let beveled_list = super::bevel(Footprint::default(), vector_node_from_bezpath(subpath), 5.);
|
||||
let beveled = beveled_list.element(0).unwrap();
|
||||
|
||||
assert_eq!(beveled.point_domain.positions().len(), 6);
|
||||
assert_eq!(beveled.segment_domain.ids().len(), 5);
|
||||
|
||||
Reference in New Issue
Block a user