mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Replace deprecated row/cell/instance terminology with "item" and "value" terms (#4075)
This commit is contained in:
@@ -116,7 +116,7 @@ macro_rules! tagged_value {
|
||||
// Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned.
|
||||
Some(match concrete_type.id? {
|
||||
x if x == TypeId::of::<()>() => TaggedValue::None,
|
||||
// Table-wrapped types need a single-row default with the element's default, not an empty table
|
||||
// Table-wrapped types need a single-item default with the element's default, not an empty table
|
||||
x if x == TypeId::of::<Table<Color>>() => TaggedValue::Color(Table::new_from_element(Color::default())),
|
||||
x if x == TypeId::of::<Table<GradientStops>>() => TaggedValue::GradientTable(Table::new_from_element(GradientStops::default())),
|
||||
$( x if x == TypeId::of::<$ty>() => TaggedValue::$identifier(Default::default()), )*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! A collection of utilities for working with HTML canvases.
|
||||
//! Utilities for working with HTML canvases.
|
||||
//! This library is designed to be used in a WebAssembly context.
|
||||
//! It doesn't expose any functionality when compiled for non-WebAssembly targets
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ trait AttributeColumn: std::any::Any + Send + Sync {
|
||||
fn display_at(&self, index: usize) -> Option<String>;
|
||||
|
||||
/// Clones a single value from this column into a boxed scalar attribute value.
|
||||
fn clone_cell(&self, index: usize) -> Option<Box<dyn AttributeValue>>;
|
||||
fn clone_value(&self, index: usize) -> Option<Box<dyn AttributeValue>>;
|
||||
|
||||
/// Drains all values out of this column into a Vec of scalar attribute values.
|
||||
fn drain(self: Box<Self>) -> Vec<Box<dyn AttributeValue>>;
|
||||
@@ -191,7 +191,7 @@ impl<T: Clone + Send + Sync + Default + Debug + 'static> AttributeColumn for Col
|
||||
}
|
||||
|
||||
/// Clones the value at the given index into a boxed scalar attribute value.
|
||||
fn clone_cell(&self, index: usize) -> Option<Box<dyn AttributeValue>> {
|
||||
fn clone_value(&self, index: usize) -> Option<Box<dyn AttributeValue>> {
|
||||
self.0.get(index).map(|v| Box::new(v.clone()) as Box<dyn AttributeValue>)
|
||||
}
|
||||
|
||||
@@ -346,8 +346,8 @@ impl AttributeColumns {
|
||||
// Push values into existing columns, or a default if the row lacks that attribute
|
||||
for (column_key, column) in &mut self.columns {
|
||||
if let Some(position) = row_entries.iter().position(|(k, _)| k == column_key) {
|
||||
let (_, cell_value) = row_entries.swap_remove(position);
|
||||
column.push(cell_value);
|
||||
let (_, value) = row_entries.swap_remove(position);
|
||||
column.push(value);
|
||||
} else {
|
||||
column.push_default();
|
||||
}
|
||||
@@ -389,8 +389,8 @@ impl AttributeColumns {
|
||||
self.len += other_len;
|
||||
}
|
||||
|
||||
/// Gets a reference to a cell value at the given index from the column for the given key.
|
||||
fn get_cell<T: 'static>(&self, key: &str, index: usize) -> Option<&T> {
|
||||
/// Gets a reference to the value at the given index from the column for the given key.
|
||||
fn get_value<T: 'static>(&self, key: &str, index: usize) -> Option<&T> {
|
||||
self.columns.iter().find_map(|(k, column)| if k == key { column.get_any(index)?.downcast_ref::<T>() } else { None })
|
||||
}
|
||||
|
||||
@@ -415,28 +415,28 @@ impl AttributeColumns {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to a cell value at the given index, creating the column if it doesn't exist or has the wrong type.
|
||||
fn get_or_insert_default_cell<T: Clone + Send + Sync + Default + Debug + 'static>(&mut self, key: &str, index: usize) -> &mut T {
|
||||
/// Gets a mutable reference to the value at the given index, creating the column if it doesn't exist or has the wrong type.
|
||||
fn get_or_insert_default_value<T: Clone + Send + Sync + Default + Debug + 'static>(&mut self, key: &str, index: usize) -> &mut T {
|
||||
let column_position = self.find_or_create_column::<T>(key);
|
||||
let column = (*self.columns[column_position].1).as_any_mut().downcast_mut::<Column<T>>().unwrap();
|
||||
&mut column.0[index]
|
||||
}
|
||||
|
||||
/// Sets a cell value at the given index in the column for the given key.
|
||||
/// Sets the value at the given index in the column for the given key.
|
||||
/// Creates the column with defaults if it doesn't exist.
|
||||
fn set_cell<T: Clone + Send + Sync + Default + Debug + 'static>(&mut self, key: impl Into<String>, index: usize, value: T) {
|
||||
fn set_value<T: Clone + Send + Sync + Default + Debug + 'static>(&mut self, key: impl Into<String>, index: usize, value: T) {
|
||||
let key = key.into();
|
||||
let column_position = self.find_or_create_column::<T>(&key);
|
||||
let column = (*self.columns[column_position].1).as_any_mut().downcast_mut::<Column<T>>().unwrap();
|
||||
column.0[index] = value;
|
||||
}
|
||||
|
||||
/// Returns a debug-formatted string for a cell at the given index in the column for the given key.
|
||||
fn display_cell_value(&self, key: &str, index: usize, overrides: fn(&dyn std::any::Any) -> Option<String>) -> Option<String> {
|
||||
/// Returns a debug-formatted string for the value at the given index in the column for the given key.
|
||||
fn display_value(&self, key: &str, index: usize, overrides: fn(&dyn std::any::Any) -> Option<String>) -> Option<String> {
|
||||
self.columns.iter().find_map(|(k, column)| {
|
||||
if k == key {
|
||||
if let Some(cell) = column.get_any(index)
|
||||
&& let Some(text) = overrides(cell)
|
||||
if let Some(value) = column.get_any(index)
|
||||
&& let Some(text) = overrides(value)
|
||||
{
|
||||
return Some(text);
|
||||
}
|
||||
@@ -447,8 +447,8 @@ impl AttributeColumns {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a type-erased reference to the cell value at the given index in the column for the given key.
|
||||
fn get_any_cell(&self, key: &str, index: usize) -> Option<&dyn std::any::Any> {
|
||||
/// Returns a type-erased reference to the value at the given index in the column for the given key.
|
||||
fn get_any_value(&self, key: &str, index: usize) -> Option<&dyn std::any::Any> {
|
||||
self.columns.iter().find_map(|(k, column)| if k == key { column.get_any(index) } else { None })
|
||||
}
|
||||
|
||||
@@ -487,8 +487,8 @@ impl AttributeColumns {
|
||||
let mut attributes = AttributeValues::new();
|
||||
|
||||
for (key, column) in &self.columns {
|
||||
if let Some(cell) = column.clone_cell(index) {
|
||||
attributes.0.push((key.clone(), cell));
|
||||
if let Some(value) = column.clone_value(index) {
|
||||
attributes.0.push((key.clone(), value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,8 +500,8 @@ impl AttributeColumns {
|
||||
let mut rows: Vec<AttributeValues> = (0..self.len).map(|_| AttributeValues::new()).collect();
|
||||
|
||||
for (key, column) in self.columns {
|
||||
for (i, cell) in column.drain().into_iter().enumerate() {
|
||||
rows[i].0.push((key.clone(), cell));
|
||||
for (i, value) in column.drain().into_iter().enumerate() {
|
||||
rows[i].0.push((key.clone(), value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,38 +640,38 @@ impl<T> Table<T> {
|
||||
|
||||
/// Returns a shared reference to the attribute value at the given row index and key, if it exists and can be downcast to the requested type.
|
||||
pub fn attribute<U: 'static>(&self, key: &str, index: usize) -> Option<&U> {
|
||||
self.attributes.get_cell(key, index)
|
||||
self.attributes.get_value(key, index)
|
||||
}
|
||||
|
||||
/// Returns a clone of the attribute value at the given row index and key, or `U::default()` if absent or of a different type.
|
||||
pub fn attribute_cloned_or_default<U: Clone + Default + 'static>(&self, key: &str, index: usize) -> U {
|
||||
self.attributes.get_cell::<U>(key, index).cloned().unwrap_or_default()
|
||||
self.attributes.get_value::<U>(key, index).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns a clone of the attribute value at the given row index and key, or the provided default if absent or of a different type.
|
||||
pub fn attribute_cloned_or<U: Clone + 'static>(&self, key: &str, index: usize, default: U) -> U {
|
||||
self.attributes.get_cell::<U>(key, index).cloned().unwrap_or(default)
|
||||
self.attributes.get_value::<U>(key, index).cloned().unwrap_or(default)
|
||||
}
|
||||
|
||||
/// Sets the attribute value at the given row index and key, creating the column with defaults if it doesn't exist.
|
||||
pub fn set_attribute<U: Clone + Send + Sync + Default + Debug + 'static>(&mut self, key: impl Into<String>, index: usize, value: U) {
|
||||
self.attributes.set_cell(key, index, value);
|
||||
self.attributes.set_value(key, index, value);
|
||||
}
|
||||
|
||||
/// Runs the given closure on a mutable reference to the attribute value at the given row index,
|
||||
/// creating the column with defaults if it doesn't exist, and returns the closure's result.
|
||||
pub fn with_attribute_mut_or_default<U: Clone + Send + Sync + Default + Debug + 'static, R, F: FnOnce(&mut U) -> R>(&mut self, key: &str, index: usize, f: F) -> R {
|
||||
f(self.attributes.get_or_insert_default_cell::<U>(key, index))
|
||||
f(self.attributes.get_or_insert_default_value::<U>(key, index))
|
||||
}
|
||||
|
||||
/// Returns a debug-formatted display string for the attribute at the given row index and key.
|
||||
pub fn attribute_display_value(&self, key: &str, index: usize, overrides: fn(&dyn std::any::Any) -> Option<String>) -> Option<String> {
|
||||
self.attributes.display_cell_value(key, index, overrides)
|
||||
self.attributes.display_value(key, index, overrides)
|
||||
}
|
||||
|
||||
/// Returns a type-erased reference to the attribute value at the given row index and key, or `None` if absent.
|
||||
pub fn attribute_any(&self, key: &str, index: usize) -> Option<&dyn std::any::Any> {
|
||||
self.attributes.get_any_cell(key, index)
|
||||
self.attributes.get_any_value(key, index)
|
||||
}
|
||||
|
||||
// =====================
|
||||
|
||||
@@ -114,7 +114,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-row attribute values are populated at runtime by the node graph.
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match ArtboardFormat::deserialize(deserializer)? {
|
||||
ArtboardFormat::ArtboardGroup(artboard_group) => artboard_group.artboards.into_iter().map(|(artboard, _)| TableRow::new_from_element(artboard)).collect(),
|
||||
ArtboardFormat::OldArtboardTable(old_table) => old_table.element.into_iter().map(TableRow::new_from_element).collect(),
|
||||
|
||||
@@ -127,8 +127,8 @@ impl From<Table<GradientStops>> for Graphic {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deeply flattens a graphic table, collecting only elements matching a specific variant (extracted by `extract_variant`)
|
||||
/// and discarding all other non-matching content. Recursion through `Graphic::Graphic` sub-tables composes transforms and opacity.
|
||||
/// Deeply flattens a `Table<Graphic>`, collecting only elements matching a specific variant (extracted by `extract_variant`)
|
||||
/// and discarding all other non-matching content. Recursion through `Graphic::Graphic` sub-`Table`s composes transforms and opacity.
|
||||
fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) -> Table<T> {
|
||||
fn compose_alpha_blending(parent: AlphaBlending, child: AlphaBlending) -> AlphaBlending {
|
||||
AlphaBlending {
|
||||
@@ -146,7 +146,7 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default("alpha_blending");
|
||||
|
||||
match current_graphic_row.into_element() {
|
||||
// Recurse into nested graphic tables, composing the parent's transform onto each child
|
||||
// Recurse into nested `Table<Graphic>` items, composing the parent's transform onto each child
|
||||
Graphic::Graphic(mut sub_table) => {
|
||||
for index in 0..sub_table.len() {
|
||||
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default("transform", index);
|
||||
@@ -158,7 +158,7 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||
|
||||
flatten_recursive(output, sub_table, extract_variant);
|
||||
}
|
||||
// Try to extract the target variant; if it matches, push its rows with composed transform and opacity
|
||||
// Try to extract the target variant; if it matches, push its items with composed transform and opacity
|
||||
other => {
|
||||
if let Some(typed_table) = extract_variant(other) {
|
||||
for row in typed_table.into_iter() {
|
||||
@@ -184,7 +184,7 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||
}
|
||||
|
||||
/// Maps from a concrete element type to its corresponding `Graphic` enum variant,
|
||||
/// enabling type-directed casting of typed tables from a `Graphic` value.
|
||||
/// enabling type-directed casting of typed `Table`s from a `Graphic` value.
|
||||
pub trait TryFromGraphic: Clone + Sized {
|
||||
fn try_from_graphic(graphic: Graphic) -> Option<Table<Self>>;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ impl TryFromGraphic for GradientStops {
|
||||
pub trait IntoGraphicTable {
|
||||
fn into_graphic_table(self) -> Table<Graphic>;
|
||||
|
||||
/// Deeply flattens any content of type `T` within a graphic table, discarding all other content, and returning a flat table of only `T` elements.
|
||||
/// Deeply flattens any content of type `T` within a `Table<Graphic>`, discarding all other content, and returning a flat `Table<T>`.
|
||||
fn into_flattened_table<T: TryFromGraphic>(self) -> Table<T>
|
||||
where
|
||||
Self: std::marker::Sized,
|
||||
@@ -505,7 +505,7 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-row attribute values are populated at runtime by the node graph.
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match GraphicFormat::deserialize(deserializer)? {
|
||||
GraphicFormat::OldGraphicGroup(old) => old.elements.into_iter().map(|(graphic, _)| TableRow::new_from_element(graphic)).collect(),
|
||||
GraphicFormat::OlderTableOldGraphicGroup(old) => old
|
||||
@@ -524,7 +524,7 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
||||
.flat_map(|element| element.elements.into_iter().map(|(graphic, _)| TableRow::new_from_element(graphic)))
|
||||
.collect(),
|
||||
GraphicFormat::Table(value) => {
|
||||
// Try to deserialize as either table format
|
||||
// Try to deserialize as either `Table` format
|
||||
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
|
||||
let mut graphic_table = Table::new();
|
||||
for index in 0..old_table.len() {
|
||||
|
||||
@@ -73,7 +73,7 @@ pub mod migrations {
|
||||
Ok(match VectorFormat::deserialize(deserializer)? {
|
||||
VectorFormat::Vector(vector) => Table::new_from_element(vector),
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-row attribute values are populated at runtime by the node graph.
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
VectorFormat::OldVectorData(old) => Table::new_from_element(Vector {
|
||||
style: old.style,
|
||||
colinear_manipulators: old.colinear_manipulators,
|
||||
|
||||
@@ -320,7 +320,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// to recover the elements. Per-row attribute values are populated at runtime by the node graph.
|
||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||
fn old_table_to_new_table<T>(old_table: OldTable<T>) -> Table<T> {
|
||||
old_table.element.into_iter().map(TableRow::new_from_element).collect()
|
||||
}
|
||||
@@ -431,7 +431,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
||||
}
|
||||
|
||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
||||
// to recover the element. Per-row attribute values are populated at runtime by the node graph.
|
||||
// to recover the element. Per-item attribute values are populated at runtime by the node graph.
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => TableRow::new_from_element(Raster::new_cpu(image)),
|
||||
FormatVersions::OldImageFrame(old) => TableRow::new_from_element(Raster::new_cpu(old.image)),
|
||||
|
||||
@@ -410,7 +410,7 @@ impl Render for Graphic {
|
||||
}
|
||||
Graphic::Vector(table) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than the first row
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
let layer_path: Table<NodeId> = table.attribute_cloned_or_default("editor:layer", 0);
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
@@ -423,7 +423,7 @@ impl Render for Graphic {
|
||||
Graphic::RasterCPU(table) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than the first row
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
}
|
||||
@@ -431,7 +431,7 @@ impl Render for Graphic {
|
||||
Graphic::RasterGPU(table) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than the first row
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
}
|
||||
@@ -439,7 +439,7 @@ impl Render for Graphic {
|
||||
Graphic::Color(table) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than the first row
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
}
|
||||
@@ -447,7 +447,7 @@ impl Render for Graphic {
|
||||
Graphic::Gradient(table) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than the first row
|
||||
// TODO: Find a way to handle more than the first item
|
||||
if !table.is_empty() {
|
||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
||||
}
|
||||
@@ -817,7 +817,7 @@ impl Render for Table<Graphic> {
|
||||
if let Some(element_id) = layer {
|
||||
element.collect_metadata(metadata, footprint, Some(element_id));
|
||||
} else {
|
||||
// Recurse through anonymous wrapper rows to reach nested content with editor:layer tags
|
||||
// Recurse through anonymous wrapper items to reach nested content with editor:layer tags
|
||||
element.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
}
|
||||
@@ -1334,7 +1334,7 @@ impl Render for Table<Vector> {
|
||||
let layer = layer_path.iter_element_values().next_back().copied();
|
||||
|
||||
if let Some(element_id) = caller_element_id.or(layer) {
|
||||
// When recovering element_id from the row's editor:layer tag (because the caller
|
||||
// When recovering element_id from the item's editor:layer tag (because the caller
|
||||
// passed None), also store the transform metadata that Graphic::collect_metadata
|
||||
// normally provides but skipped due to the None element_id.
|
||||
if caller_element_id.is_none() {
|
||||
@@ -1375,7 +1375,7 @@ impl Render for Table<Vector> {
|
||||
metadata.vector_data.entry(element_id).or_insert_with(|| Arc::new(vector.clone()));
|
||||
}
|
||||
|
||||
// If this row carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
||||
// If this item carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
||||
// Flatten Path, Morph, or any other destructive merge), recurse into that snapshot so the editor can
|
||||
// surface the original child layers' click targets.
|
||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", index);
|
||||
@@ -1575,7 +1575,7 @@ impl Render for Table<Raster<CPU>> {
|
||||
|
||||
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.).into()]);
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than one row of the raster table
|
||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||
if !self.is_empty() {
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
||||
metadata.local_transforms.insert(element_id, transform);
|
||||
@@ -1665,7 +1665,7 @@ impl Render for Table<Raster<GPU>> {
|
||||
|
||||
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.).into()]);
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than one row of the raster table
|
||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||
if !self.is_empty() {
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
||||
metadata.local_transforms.insert(element_id, transform);
|
||||
|
||||
@@ -18,7 +18,7 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||
let mut intersections_vec = Vec::new();
|
||||
let err = accuracy.unwrap_or(MAX_ABSOLUTE_DIFFERENCE);
|
||||
let num_curves = self.len();
|
||||
// TODO: optimization opportunity - this for-loop currently compares all intersections with all curve-segments in the subpath collection
|
||||
// TODO: optimization opportunity - this for-loop currently compares all intersections with all curve-segments in the subpath list
|
||||
self.iter_closed().enumerate().for_each(|(i, other)| {
|
||||
intersections_vec.extend(pathseg_self_intersections(other, accuracy, minimum_separation).iter().flat_map(|value| [(i, value.0), (i, value.1)]));
|
||||
self.iter_closed().enumerate().skip(i + 1).for_each(|(j, curve)| {
|
||||
|
||||
@@ -137,14 +137,14 @@ impl RasterGpuToRasterCpuConverter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Passthrough conversion for GPU tables - no conversion needed
|
||||
/// Passthrough conversion for GPU `Table`s - no conversion needed
|
||||
impl<'i> Convert<Table<Raster<GPU>>, &'i WgpuExecutor> for Table<Raster<GPU>> {
|
||||
async fn convert(self, _: Footprint, _converter: &'i WgpuExecutor) -> Table<Raster<GPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts CPU raster table to GPU by uploading each image to a texture
|
||||
/// Converts a `Table<Raster<CPU>>` to `Table<Raster<GPU>>` by uploading each image to a texture
|
||||
impl<'i> Convert<Table<Raster<GPU>>, &'i WgpuExecutor> for Table<Raster<CPU>> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> Table<Raster<GPU>> {
|
||||
let device = &executor.context.device;
|
||||
@@ -176,16 +176,14 @@ impl<'i> Convert<Raster<GPU>, &'i WgpuExecutor> for Raster<CPU> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Passthrough conversion for CPU tables - no conversion needed
|
||||
/// Passthrough conversion for CPU `Table`s - no conversion needed
|
||||
impl<'i> Convert<Table<Raster<CPU>>, &'i WgpuExecutor> for Table<Raster<CPU>> {
|
||||
async fn convert(self, _: Footprint, _converter: &'i WgpuExecutor) -> Table<Raster<CPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts GPU raster table to CPU by downloading texture data in one go
|
||||
///
|
||||
/// then asynchronously maps all buffers and processes the results.
|
||||
/// Converts a `Table<Raster<GPU>>` to `Table<Raster<CPU>>` by downloading texture data in one go then asynchronously maps all buffers and processes the results.
|
||||
impl<'i> Convert<Table<Raster<CPU>>, &'i WgpuExecutor> for Table<Raster<GPU>> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> Table<Raster<CPU>> {
|
||||
let device = &executor.context.device;
|
||||
@@ -218,7 +216,7 @@ impl<'i> Convert<Table<Raster<CPU>>, &'i WgpuExecutor> for Table<Raster<GPU>> {
|
||||
|
||||
map_results
|
||||
.into_iter()
|
||||
.zip(rows_meta.into_iter())
|
||||
.zip(rows_meta)
|
||||
.map(|(element, row)| {
|
||||
let (_, attributes) = row.into_parts();
|
||||
TableRow::from_parts(element, attributes)
|
||||
@@ -247,7 +245,7 @@ impl<'i> Convert<Raster<CPU>, &'i WgpuExecutor> for Raster<GPU> {
|
||||
|
||||
/// Uploads an raster texture from the CPU to the GPU. This is now deprecated and the Convert node should be used in the future.
|
||||
///
|
||||
/// Accepts either individual raster data or a table of raster elements and converts it to the GPU format using the WgpuExecutor's device and queue.
|
||||
/// Accepts either individual raster data or a `Table` of raster elements and converts it to the GPU format using the WgpuExecutor's device and queue.
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn upload_texture<'a: 'n, T: Convert<Table<Raster<GPU>>, &'a WgpuExecutor>>(
|
||||
_: impl Ctx,
|
||||
|
||||
@@ -192,7 +192,7 @@ fn blend_mode<T: SetBlendMode>(
|
||||
/// 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 row in its parent table or TableRow<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or TableRow<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
content.set_blend_mode(blend_mode);
|
||||
content
|
||||
}
|
||||
@@ -216,7 +216,7 @@ fn opacity<T: MultiplyAlpha>(
|
||||
#[default(100.)]
|
||||
opacity: Percentage,
|
||||
) -> T {
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its row in its parent table or TableRow<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or TableRow<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
content.multiply_alpha(opacity / 100.);
|
||||
content
|
||||
}
|
||||
@@ -247,7 +247,7 @@ fn blending<T: SetBlendMode + MultiplyAlpha + MultiplyFill + SetClip>(
|
||||
/// 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 row in its parent table or TableRow<T>) rather than applying to each row in its own table, which produces the undesired result
|
||||
// TODO: Find a way to make this apply once to the table's parent (i.e. its item in its parent table or TableRow<T>) rather than applying to each item in its own table, which produces the undesired result
|
||||
content.set_blend_mode(blend_mode);
|
||||
content.multiply_alpha(opacity / 100.);
|
||||
content.multiply_fill(fill / 100.);
|
||||
|
||||
@@ -199,8 +199,8 @@ async fn brush(
|
||||
if image.is_empty() {
|
||||
image.push(TableRow::default());
|
||||
}
|
||||
// TODO: Find a way to handle more than one row
|
||||
let table_row = image.clone_row(0).expect("Expected the one row we just pushed");
|
||||
// TODO: Find a way to handle more than one item
|
||||
let table_row = image.clone_row(0).expect("Expected the one item we just pushed");
|
||||
|
||||
let bounds = Table::new_from_row(table_row.clone()).bounding_box(DAffine2::IDENTITY, false);
|
||||
let [start, end] = if let RenderBoundingBox::Rectangle(rect) = bounds { rect } else { [DVec2::ZERO, DVec2::ZERO] };
|
||||
@@ -217,7 +217,7 @@ async fn brush(
|
||||
|
||||
let mut brush_plan = cache.compute_brush_plan(table_row, &draw_strokes);
|
||||
|
||||
// TODO: Find a way to handle more than one row
|
||||
// TODO: Find a way to handle more than one item
|
||||
let Some(mut actual_image) = extend_image_to_bounds((), Table::new_from_row(brush_plan.background), background_bounds).into_iter().next() else {
|
||||
return Table::new();
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@ impl BrushCacheImpl {
|
||||
background = std::mem::take(&mut self.blended_image);
|
||||
|
||||
// Check if the first non-blended stroke is an extension of the last one.
|
||||
// Transform is set to ZERO (not the default IDENTITY) as a sentinel to mark this row as uninitialized.
|
||||
// Transform is set to ZERO (not the default IDENTITY) as a sentinel to mark this item as uninitialized.
|
||||
let mut first_stroke_texture = TableRow::new_from_element(Raster::<CPU>::default()).with_attribute("transform", glam::DAffine2::ZERO);
|
||||
let mut first_stroke_point_skip = 0;
|
||||
let strokes = input[num_blended_strokes..].to_vec();
|
||||
|
||||
@@ -60,7 +60,7 @@ async fn read_position(
|
||||
|
||||
// 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.)
|
||||
/// Produces the index of the current iteration of a loop by reading from the evaluation context, which is supplied by downstream nodes such as *Instance Repeat*.
|
||||
/// Produces the index of the current iteration of a loop by reading from the evaluation context, which is supplied by downstream nodes such as *Repeat*.
|
||||
///
|
||||
/// Nested loops can enable 2D or higher-dimensional iteration by using the *Loop Level* parameter to read the index from outer levels of loops.
|
||||
#[node_macro::node(category("Context"), path(core_types::vector))]
|
||||
|
||||
@@ -7,7 +7,7 @@ use graphic_types::{
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::GradientStops;
|
||||
|
||||
/// Constructs a new single artboard table with the chosen properties.
|
||||
/// Constructs a new single-item `Table<Artboard>` with the chosen properties.
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn create_artboard<T: IntoGraphicTable + 'n>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
|
||||
@@ -9,12 +9,12 @@ use graphic_types::{Artboard, Vector};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::{GradientStop, GradientStops, ReferencePoint};
|
||||
|
||||
/// Returns the value at the specified index in the collection.
|
||||
/// Returns the value at the specified index in the list.
|
||||
/// If no value exists at that index, the type's default value is returned.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
|
||||
_: impl Ctx,
|
||||
/// The collection of data, such as a list or table.
|
||||
/// The list of data.
|
||||
#[implementations(
|
||||
Table<Artboard>,
|
||||
Table<Graphic>,
|
||||
@@ -28,8 +28,8 @@ pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
|
||||
Table<u8>,
|
||||
Table<NodeId>,
|
||||
)]
|
||||
collection: T,
|
||||
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the collection, starting from -1 for the last item.
|
||||
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::Output
|
||||
where
|
||||
@@ -37,20 +37,15 @@ where
|
||||
{
|
||||
let index = index as i32;
|
||||
|
||||
if index < 0 {
|
||||
collection.at_index_from_end(-index as usize)
|
||||
} else {
|
||||
collection.at_index(index as usize)
|
||||
}
|
||||
.unwrap_or_default()
|
||||
if index < 0 { list.at_index_from_end(-index as usize) } else { list.at_index(index as usize) }.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns the collection with the element at the specified index removed.
|
||||
/// If no value exists at that index, the collection is returned unchanged.
|
||||
/// Returns the list with the element at the specified index removed.
|
||||
/// If no value exists at that index, the list is returned unchanged.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub fn omit_element<T: graphic_types::graphic::OmitIndex + Clone + Default>(
|
||||
_: impl Ctx,
|
||||
/// The collection of data, such as a list or table.
|
||||
/// The list of data.
|
||||
#[implementations(
|
||||
Table<String>,
|
||||
Table<Artboard>,
|
||||
@@ -61,26 +56,26 @@ pub fn omit_element<T: graphic_types::graphic::OmitIndex + Clone + Default>(
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
)]
|
||||
collection: T,
|
||||
/// The index of the item to remove, starting from 0 for the first item. Negative indices count backwards from the end of the collection, starting from -1 for the last item.
|
||||
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.
|
||||
index: SignedInteger,
|
||||
) -> T {
|
||||
let index = index as i32;
|
||||
|
||||
if index < 0 {
|
||||
collection.omit_index_from_end(index.unsigned_abs() as usize)
|
||||
list.omit_index_from_end(index.unsigned_abs() as usize)
|
||||
} else {
|
||||
collection.omit_index(index as usize)
|
||||
list.omit_index(index as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bare element (without its row attributes) at the specified index in a table.
|
||||
/// Use this when downstream nodes want just the inner value rather than a single-row table.
|
||||
/// 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.
|
||||
/// 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 `Table` of data to extract from.
|
||||
#[implementations(
|
||||
Table<String>,
|
||||
Table<f64>,
|
||||
@@ -94,7 +89,7 @@ pub fn extract_element<T: Clone + Default + Send + Sync + 'static>(
|
||||
Table<Artboard>,
|
||||
)]
|
||||
table: Table<T>,
|
||||
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the collection, starting from -1 for the last item.
|
||||
/// 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();
|
||||
@@ -192,14 +187,14 @@ where
|
||||
|
||||
let mut result_table = Table::new();
|
||||
|
||||
// Add original instance depending on the keep_original flag
|
||||
// Add original items depending on the keep_original flag
|
||||
if keep_original {
|
||||
for instance in content.clone().into_iter() {
|
||||
result_table.push(instance);
|
||||
for item in content.clone().into_iter() {
|
||||
result_table.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Create and add mirrored instance
|
||||
// Create and add mirrored items
|
||||
for mut row in content.into_iter() {
|
||||
let current_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
||||
row.set_attribute("transform", reflected_transform * current_transform);
|
||||
@@ -212,7 +207,7 @@ where
|
||||
/// Returns the path identifying the subgraph (network) that contains this proto node — i.e. the input `node_path`
|
||||
/// with its own trailing entry dropped. The terminating element of the returned path is the document node whose
|
||||
/// encapsulated network we live in, so the path doubles as a unique reference to that node at any nesting depth.
|
||||
/// Used as the value source for stamping the `editor:layer` attribute on each row of a layer's output, which lets
|
||||
/// Used as the value source for stamping the `editor:layer` attribute on each item of a layer's output, which lets
|
||||
/// 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(""))]
|
||||
@@ -221,14 +216,14 @@ pub fn path_of_subgraph(_: impl Ctx, node_path: Table<NodeId>) -> Table<NodeId>
|
||||
node_path.into_iter().take(len.saturating_sub(1)).collect()
|
||||
}
|
||||
|
||||
/// Writes a per-row attribute column on the input table. The value-producing input is evaluated once per row,
|
||||
/// with the row's element index and the row itself (as a single-row table vararg) passed via context, so the
|
||||
/// upstream pipeline can return a different value per row that may be derived from the row's own data.
|
||||
/// If the column already exists, its values are replaced; if not, the column is created.
|
||||
/// Writes a named attribute on each item of the input `Table`. The value-producing input is evaluated once per item,
|
||||
/// with the item's index and the item itself (as a `Table` 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, the attribute is added.
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn write_attribute<T: AnyHash + Clone + Send + Sync + core_types::CacheHash, U: Clone + Send + Sync + Default + std::fmt::Debug + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
/// The table whose rows will gain or replace the named attribute column.
|
||||
/// The `Table` whose items will gain or have replaced the named attribute.
|
||||
#[implementations(
|
||||
Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>, Table<Artboard>,
|
||||
Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>, Table<Graphic>,
|
||||
@@ -239,9 +234,9 @@ async fn write_attribute<T: AnyHash + Clone + Send + Sync + core_types::CacheHas
|
||||
Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>, Table<GradientStops>,
|
||||
)]
|
||||
mut content: Table<T>,
|
||||
/// The attribute name (column key) to write or replace.
|
||||
/// The attribute name (key) to write or replace.
|
||||
name: String,
|
||||
/// The node that produces the per-row value. Called once per row with the row index in context.
|
||||
/// The node that produces the attribute value for each item. Called once per item with the item's index in context.
|
||||
#[implementations(
|
||||
Context -> f64, Context -> u32, Context -> bool, Context -> String, Context -> Table<String>, Context -> DVec2, Context -> DAffine2, Context -> Table<NodeId>, Context -> Table<Color>, Context -> Table<GradientStops>,
|
||||
Context -> f64, Context -> u32, Context -> bool, Context -> String, Context -> Table<String>, Context -> DVec2, Context -> DAffine2, Context -> Table<NodeId>, Context -> Table<Color>, Context -> Table<GradientStops>,
|
||||
@@ -262,14 +257,14 @@ async fn write_attribute<T: AnyHash + Clone + Send + Sync + core_types::CacheHas
|
||||
content
|
||||
}
|
||||
|
||||
/// Joins two tables of the same type, extending the base table with the rows of the new table.
|
||||
/// Joins two `Table`s of the same type, extending the base `Table` with the items from the new `Table`.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn extend<T: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The table whose rows will appear at the start of the extended table.
|
||||
/// 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 rows will appear at the end of the extended table.
|
||||
/// The `Table` whose items will appear at the end of the extended `Table`.
|
||||
#[expose]
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
|
||||
new: Table<T>,
|
||||
@@ -327,8 +322,8 @@ pub async fn wrap_graphic<T: Into<Graphic> + 'n>(
|
||||
Table::new_from_element(content.into())
|
||||
}
|
||||
|
||||
/// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table.
|
||||
/// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
|
||||
/// 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.
|
||||
#[node_macro::node(category("General"))]
|
||||
pub async fn to_graphic<T: IntoGraphicTable + 'n>(
|
||||
_: impl Ctx,
|
||||
@@ -345,7 +340,7 @@ pub async fn to_graphic<T: IntoGraphicTable + 'n>(
|
||||
content.into_graphic_table()
|
||||
}
|
||||
|
||||
/// Removes a level of nesting from a graphic table, or all nesting if "Fully Flatten" is enabled.
|
||||
/// Removes a level of nesting from a `Table<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>?
|
||||
@@ -367,7 +362,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
|
||||
|
||||
flatten_table(output_graphic_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
}
|
||||
// Push any leaf Graphic elements we encounter, which can be either Graphic table elements beyond the recursion depth, or table elements other than Graphic tables
|
||||
// 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_row_attributes(index);
|
||||
output_graphic_table.push(TableRow::from_parts(current_element, attributes));
|
||||
@@ -382,31 +377,31 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
|
||||
output
|
||||
}
|
||||
|
||||
/// Converts a graphic table into a vector table by deeply flattening any vector content it contains, and discarding any non-vector content.
|
||||
/// Converts a `Table<Graphic>` into a `Table<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> {
|
||||
content.into_flattened_table()
|
||||
}
|
||||
|
||||
/// Converts a graphic table into a raster table by deeply flattening any raster content it contains, and discarding any non-raster content.
|
||||
/// Converts a `Table<Graphic>` into a `Table<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()
|
||||
}
|
||||
|
||||
/// Converts a graphic table into a color table by deeply flattening any color content it contains, and discarding any non-color content.
|
||||
/// Converts a `Table<Graphic>` into a `Table<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()
|
||||
}
|
||||
|
||||
/// Converts a graphic table into a gradient table by deeply flattening any gradient content it contains, and discarding any non-gradient content.
|
||||
/// Converts a `Table<Graphic>` into a `Table<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()
|
||||
}
|
||||
|
||||
/// Constructs a gradient from a table of colors, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
|
||||
/// Constructs a gradient from a `Table<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>();
|
||||
|
||||
@@ -14,14 +14,14 @@ 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-row tables and now we use a single table
|
||||
// TODO: with multiple rows while still assuming a single row for the boolean operations.
|
||||
// TODO: since before we used a Vec of single-item `Table`s and now we use a single `Table`
|
||||
// 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>(
|
||||
_: impl Ctx,
|
||||
/// The table of vector paths to perform the boolean operation on. Nested tables are automatically flattened.
|
||||
/// The `Table` of vector paths to perform the boolean operation on. Nested `Table`s are automatically flattened.
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
content: I,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
@@ -47,7 +47,7 @@ async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clon
|
||||
Vector::transform(result_vector, transform);
|
||||
result_vector.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
|
||||
// Snapshot the input layers as the `editor:merged_layers` row attribute so the renderer can recurse into them
|
||||
// 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("editor:merged_layers", 0, content.clone());
|
||||
|
||||
@@ -125,7 +125,7 @@ fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation:
|
||||
};
|
||||
let mut row = if let Some(index) = copy_from_index {
|
||||
let mut attributes = vector.clone_row_attributes(index);
|
||||
// The boolean op bakes input transforms into the output geometry, so the result row carries no transform of its own
|
||||
// The boolean op bakes input transforms into the output geometry, so the result item carries no transform of its own
|
||||
attributes.insert("transform", DAffine2::IDENTITY);
|
||||
let copy_from = vector.element(index).unwrap();
|
||||
let element = Vector {
|
||||
@@ -166,7 +166,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
let graphic = graphic_table.element(index).unwrap();
|
||||
match graphic.clone() {
|
||||
Graphic::Vector(vector) => {
|
||||
// Apply the parent graphic's transform to each element of the vector table
|
||||
// Apply the parent graphic's transform to each element of the `Table<Vector>`
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
||||
vector
|
||||
.into_iter()
|
||||
@@ -191,7 +191,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
.with_attribute("editor:layer", layer)
|
||||
};
|
||||
|
||||
// Apply the parent graphic's transform to each raster element, preserving each row's layer
|
||||
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
||||
// and alpha_blending so the boolean op downstream can route clicks (and inherit blending state)
|
||||
// back to the originating raster layer
|
||||
(0..image.len())
|
||||
@@ -217,7 +217,7 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
.with_attribute("editor:layer", layer)
|
||||
};
|
||||
|
||||
// Apply the parent graphic's transform to each raster element, preserving each row's layer
|
||||
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
||||
// and alpha_blending so the boolean op downstream can route clicks (and inherit blending state)
|
||||
// back to the originating raster layer
|
||||
(0..image.len())
|
||||
@@ -231,12 +231,12 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
}
|
||||
Graphic::Graphic(mut graphic) => {
|
||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
||||
// Apply the parent graphic's transform to each element of inner table
|
||||
// Apply the parent graphic's transform to each element of the inner `Table`
|
||||
for transform in graphic.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
||||
*transform = parent_transform * *transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner table into the output vector table
|
||||
// Recursively flatten the inner `Table` into the output `Table<Vector>`
|
||||
let flattened = flatten_vector(&graphic);
|
||||
let unioned = boolean_operation_on_vector_table(&flattened, BooleanOperation::Union);
|
||||
|
||||
|
||||
@@ -112,13 +112,13 @@ pub fn combine_channels(
|
||||
.zip(blue)
|
||||
.zip(alpha)
|
||||
.filter_map(|(((red, green), blue), alpha)| {
|
||||
// Turn any default zero-sized image rows into None
|
||||
// Turn any default zero-sized image items into None
|
||||
let red = red.filter(|i| i.element().width > 0 && i.element().height > 0);
|
||||
let green = green.filter(|i| i.element().width > 0 && i.element().height > 0);
|
||||
let blue = blue.filter(|i| i.element().width > 0 && i.element().height > 0);
|
||||
let alpha = alpha.filter(|i| i.element().width > 0 && i.element().height > 0);
|
||||
|
||||
// Get this row's transform and alpha blending mode from the first non-empty channel
|
||||
// Get this item's transform and alpha blending mode from the first non-empty channel
|
||||
let attributes = [&red, &green, &blue, &alpha].iter().find_map(|i| i.as_ref()).map(|i| i.attributes().clone())?;
|
||||
|
||||
// Get the common width and height of the channels, which must have equal dimensions
|
||||
@@ -183,7 +183,7 @@ pub fn mask(
|
||||
#[expose]
|
||||
stencil: Table<Raster<CPU>>,
|
||||
) -> Table<Raster<CPU>> {
|
||||
// TODO: Figure out what it means to support multiple stencil rows?
|
||||
// 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
|
||||
return image;
|
||||
@@ -285,7 +285,7 @@ pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Table<Color>) -> Tab
|
||||
result_table.set_attribute("transform", 0, transform);
|
||||
result_table.set_attribute("alpha_blending", 0, AlphaBlending::default());
|
||||
|
||||
// Callers of empty_image can safely unwrap on returned table
|
||||
// Callers of empty_image can safely unwrap on returned `Table`
|
||||
result_table
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ async fn repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
#[default(1)]
|
||||
#[hard_min(1)]
|
||||
count: u32,
|
||||
@@ -34,9 +34,9 @@ async fn repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let index = if reverse { count - index - 1 } else { index };
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||
|
||||
for generated_row in generated_instance.into_iter() {
|
||||
for generated_row in generated_content.into_iter() {
|
||||
result_table.push(generated_row);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = Table<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,
|
||||
@@ -75,10 +75,10 @@ pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let transform = DAffine2::from_angle(angle) * DAffine2::from_translation(translation);
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index as usize);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||
|
||||
for row_index in 0..generated_instance.len() {
|
||||
let Some(mut row) = generated_instance.clone_row(row_index) else { continue };
|
||||
for row_index in 0..generated_content.len() {
|
||||
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
||||
|
||||
let local_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
||||
let local_translation = DAffine2::from_translation(local_transform.translation);
|
||||
@@ -102,7 +102,7 @@ async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
start_angle: Angle,
|
||||
#[unit(" px")]
|
||||
#[default(5)]
|
||||
@@ -121,10 +121,10 @@ async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let transform = angle * translation;
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index as usize);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||
|
||||
for row_index in 0..generated_instance.len() {
|
||||
let Some(mut row) = generated_instance.clone_row(row_index) else { continue };
|
||||
for row_index in 0..generated_content.len() {
|
||||
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
||||
|
||||
let local_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
||||
let local_translation = DAffine2::from_translation(local_transform.translation);
|
||||
@@ -149,7 +149,7 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
Context -> Table<Color>,
|
||||
Context -> Table<GradientStops>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
content: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
reverse: bool,
|
||||
) -> Table<T> {
|
||||
let mut result_table = Table::new();
|
||||
@@ -162,9 +162,9 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
let transformed_point = transform.transform_point2(point);
|
||||
|
||||
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_position(transformed_point);
|
||||
let generated_instance = instance.eval(new_ctx.into_context()).await;
|
||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||
|
||||
for mut generated_row in generated_instance.into_iter() {
|
||||
for mut generated_row in generated_content.into_iter() {
|
||||
generated_row.attribute_mut_or_insert_default::<DAffine2>("transform").translation = transformed_point;
|
||||
result_table.push(generated_row);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ fn query_json(
|
||||
|
||||
/// Extracts every matched value from a JSON string using a path expression (see that parameter's description for its syntax). A list of zero or more resultant strings is produced. The `[]` path accessor is used to read more than one value.
|
||||
///
|
||||
/// Each row carries a `type` attribute holding the matched value's JSON type (`"string"`, `"number"`, `"bool"`, `"null"`, `"object"`, or `"array"`).
|
||||
/// Each item carries a `type` attribute holding the matched value's JSON type (`"string"`, `"number"`, `"bool"`, `"null"`, `"object"`, or `"array"`).
|
||||
///
|
||||
/// This is useful in conjunction with the nodes:
|
||||
/// • **Index Elements**: access the `N`th query result.
|
||||
|
||||
@@ -19,11 +19,11 @@ pub struct PathBuilder {
|
||||
}
|
||||
|
||||
impl PathBuilder {
|
||||
pub fn new(per_glyph_instances: bool, scale: f64) -> Self {
|
||||
pub fn new(per_glyph_items: bool, scale: f64) -> Self {
|
||||
Self {
|
||||
current_subpath: Subpath::new(Vec::new(), false),
|
||||
glyph_subpaths: Vec::new(),
|
||||
vector_table: if per_glyph_instances { Table::new() } else { Table::new_from_element(Vector::default()) },
|
||||
vector_table: if per_glyph_items { Table::new() } else { Table::new_from_element(Vector::default()) },
|
||||
scale,
|
||||
id: PointId::ZERO,
|
||||
origin: DVec2::default(),
|
||||
@@ -35,7 +35,7 @@ impl PathBuilder {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn draw_glyph(&mut self, glyph: &OutlineGlyph<'_>, size: f32, normalized_coords: &[NormalizedCoord], glyph_offset: DVec2, style_skew: Option<DAffine2>, skew: DAffine2, per_glyph_instances: bool) {
|
||||
fn draw_glyph(&mut self, glyph: &OutlineGlyph<'_>, size: f32, normalized_coords: &[NormalizedCoord], glyph_offset: DVec2, style_skew: Option<DAffine2>, skew: DAffine2, per_glyph_items: bool) {
|
||||
let location_ref = LocationRef::new(normalized_coords);
|
||||
let settings = DrawSettings::unhinted(Size::new(size), location_ref);
|
||||
glyph.draw(settings, self).unwrap();
|
||||
@@ -50,18 +50,18 @@ impl PathBuilder {
|
||||
glyph_subpath.apply_transform(skew);
|
||||
}
|
||||
|
||||
if per_glyph_instances {
|
||||
if per_glyph_items {
|
||||
self.vector_table
|
||||
.push(TableRow::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false)).with_attribute("transform", DAffine2::from_translation(glyph_offset)));
|
||||
} else {
|
||||
for subpath in self.glyph_subpaths.drain(..) {
|
||||
// Unwrapping here is ok because `self.vector_table` is initialized with a single `Vector` table element
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_glyph_run(&mut self, glyph_run: &GlyphRun<'_, ()>, tilt: f64, per_glyph_instances: bool) {
|
||||
pub fn render_glyph_run(&mut self, glyph_run: &GlyphRun<'_, ()>, tilt: f64, per_glyph_items: bool) {
|
||||
let mut run_x = glyph_run.offset();
|
||||
let run_y = glyph_run.baseline();
|
||||
|
||||
@@ -69,7 +69,7 @@ impl PathBuilder {
|
||||
|
||||
// User-requested tilt applied around baseline to avoid vertical displacement
|
||||
// Translation ensures rotation point is at the baseline, not origin
|
||||
let skew = if per_glyph_instances {
|
||||
let skew = if per_glyph_items {
|
||||
DAffine2::from_cols_array(&[1., 0., -tilt.to_radians().tan(), 1., 0., 0.])
|
||||
} else {
|
||||
DAffine2::from_translation(DVec2::new(0., run_y as f64))
|
||||
@@ -82,7 +82,7 @@ impl PathBuilder {
|
||||
// Font synthesis (e.g., synthetic italic) applied separately from user transforms
|
||||
// This preserves the distinction between font styling and user transformations
|
||||
let style_skew = synthesis.skew().map(|angle| {
|
||||
if per_glyph_instances {
|
||||
if per_glyph_items {
|
||||
DAffine2::from_cols_array(&[1., 0., -angle.to_radians().tan() as f64, 1., 0., 0.])
|
||||
} else {
|
||||
DAffine2::from_translation(DVec2::new(0., run_y as f64))
|
||||
@@ -107,10 +107,10 @@ impl PathBuilder {
|
||||
|
||||
let glyph_id = GlyphId::from(glyph.id);
|
||||
if let Some(glyph_outline) = outlines.get(glyph_id) {
|
||||
if !per_glyph_instances {
|
||||
if !per_glyph_items {
|
||||
self.origin = glyph_offset;
|
||||
}
|
||||
self.draw_glyph(&glyph_outline, font_size, &normalized_coords, glyph_offset, style_skew, skew, per_glyph_instances);
|
||||
self.draw_glyph(&glyph_outline, font_size, &normalized_coords, glyph_offset, style_skew, skew, per_glyph_items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,11 +77,11 @@ fn regex_replace(
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a regex match in the string and returns its components. The result is a list where the first element is the whole match (`$0`) and subsequent elements are the capture groups (`$1`, `$2`, etc., if any).
|
||||
/// Finds a regex match in the string and returns its components. The result is a list where the first item is the whole match (`$0`) and subsequent items are the capture groups (`$1`, `$2`, etc., if any).
|
||||
///
|
||||
/// The match index selects which non-overlapping occurrence to return (0 for the first match). Returns an empty list if no match is found at the given index.
|
||||
///
|
||||
/// Each row carries `start` and `end` byte-offset attributes pointing into the original string, plus a `name` attribute holding
|
||||
/// Each item carries `start` and `end` byte-offset attributes pointing into the original string, plus a `name` attribute holding
|
||||
/// the capture group's name (empty for unnamed groups, and for index 0 which is the whole match).
|
||||
#[node_macro::node(category(""))]
|
||||
fn regex_find(
|
||||
@@ -150,7 +150,7 @@ fn regex_find(
|
||||
|
||||
/// Finds all non-overlapping matches of a regular expression pattern in the string, returning a list of the matched substrings.
|
||||
///
|
||||
/// Each row carries `start` and `end` byte-offset attributes pointing into the original string.
|
||||
/// Each item carries `start` and `end` byte-offset attributes pointing into the original string.
|
||||
#[node_macro::node(category("Text: Regex"))]
|
||||
fn regex_find_all(
|
||||
_: impl Ctx,
|
||||
|
||||
@@ -87,19 +87,19 @@ 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_instances: bool) -> Table<Vector> {
|
||||
pub fn to_path(&mut self, text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> Table<Vector> {
|
||||
let Some(layout) = self.layout_text(text, font, font_cache, typesetting) else {
|
||||
return Table::new_from_element(Vector::default());
|
||||
};
|
||||
|
||||
let mut path_builder = PathBuilder::new(per_glyph_instances, layout.scale() as f64);
|
||||
let mut path_builder = PathBuilder::new(per_glyph_items, layout.scale() as f64);
|
||||
|
||||
for line in layout.lines() {
|
||||
for item in line.items() {
|
||||
if let PositionedLayoutItem::GlyphRun(glyph_run) = item
|
||||
&& typesetting.max_height.filter(|&max_height| glyph_run.baseline() > max_height as f32).is_none()
|
||||
{
|
||||
path_builder.render_glyph_run(&glyph_run, typesetting.tilt, per_glyph_instances);
|
||||
path_builder.render_glyph_run(&glyph_run, typesetting.tilt, per_glyph_items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ 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_instances: bool) -> Table<Vector> {
|
||||
TextContext::with_thread_local(|ctx| ctx.to_path(text, font, font_cache, typesetting, per_glyph_instances))
|
||||
pub fn to_path(text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, per_glyph_items: bool) -> Table<Vector> {
|
||||
TextContext::with_thread_local(|ctx| ctx.to_path(text, font, font_cache, typesetting, per_glyph_items))
|
||||
}
|
||||
|
||||
pub fn bounding_box(text: &str, font: &Font, font_cache: &FontCache, typesetting: TypesettingConfig, for_clipping_test: bool) -> DVec2 {
|
||||
|
||||
@@ -87,7 +87,7 @@ fn reset_transform<T>(
|
||||
content
|
||||
}
|
||||
|
||||
/// Overwrites the transform of each element in the input table with the specified transform.
|
||||
/// Overwrites the transform of each item in the input `Table` with the specified transform.
|
||||
#[node_macro::node(category("Math: Transform"))]
|
||||
fn replace_transform<T>(
|
||||
_: impl Ctx + InjectFootprint,
|
||||
@@ -109,7 +109,7 @@ fn replace_transform<T>(
|
||||
}
|
||||
|
||||
// TODO: Figure out how this node should behave once #2982 is implemented.
|
||||
/// Obtains the transform of the first element in the input table, if present.
|
||||
/// Obtains the transform of the first item in the input `Table`, if present.
|
||||
#[node_macro::node(category("Math: Transform"), path(core_types::vector))]
|
||||
async fn extract_transform<T>(
|
||||
_: impl Ctx,
|
||||
|
||||
@@ -25,7 +25,7 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||
vector.set_attribute("editor:layer", 0, if existing.is_empty() { subgraph_path } else { existing });
|
||||
|
||||
if vector.len() > 1 {
|
||||
warn!("The path modify ran on {} vector rows. Only the first can be modified.", vector.len());
|
||||
warn!("The path modify ran on {} vector items. Only the first can be modified.", vector.len());
|
||||
}
|
||||
vector
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ use vector_types::vector::misc::{
|
||||
use vector_types::vector::style::{Fill, Gradient, GradientStops, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
use vector_types::vector::{FillId, PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorExt};
|
||||
|
||||
/// Implemented for types that contain vector rows reachable via mutable access.
|
||||
/// 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 {
|
||||
fn for_each_vector_mut(&mut self, f: impl FnMut(&mut Vector, DAffine2));
|
||||
@@ -255,13 +255,13 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
/// Artwork to be copied and placed at each point.
|
||||
#[expose]
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>, Table<GradientStops>)]
|
||||
instance: Table<I>,
|
||||
/// Minimum range of randomized sizes given to each instance.
|
||||
content: Table<I>,
|
||||
/// Minimum range of randomized sizes given to each placed copy.
|
||||
#[default(1)]
|
||||
#[range((0., 2.))]
|
||||
#[unit("x")]
|
||||
random_scale_min: Multiplier,
|
||||
/// Maximum range of randomized sizes given to each instance.
|
||||
/// Maximum range of randomized sizes given to each placed copy.
|
||||
#[default(1)]
|
||||
#[range((0., 2.))]
|
||||
#[unit("x")]
|
||||
@@ -269,12 +269,12 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
/// Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes).
|
||||
#[range((-50., 50.))]
|
||||
random_scale_bias: f64,
|
||||
/// Seed to determine unique variations on all the randomized instance sizes.
|
||||
/// Seed to determine unique variations on all the randomized copy sizes.
|
||||
random_scale_seed: SeedValue,
|
||||
/// Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise.
|
||||
/// Range of randomized angles given to each placed copy, in degrees ranging from furthest clockwise to counterclockwise.
|
||||
#[range((0., 360.))]
|
||||
random_rotation: Angle,
|
||||
/// Seed to determine unique variations on all the randomized instance angles.
|
||||
/// Seed to determine unique variations on all the randomized copy angles.
|
||||
random_rotation_seed: SeedValue,
|
||||
) -> Table<I> {
|
||||
let mut result_table = Table::new();
|
||||
@@ -315,8 +315,8 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
|
||||
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation);
|
||||
|
||||
for row_index in 0..instance.len() {
|
||||
let Some(mut row) = instance.clone_row(row_index) else { continue };
|
||||
for row_index in 0..content.len() {
|
||||
let Some(mut row) = content.clone_row(row_index) else { continue };
|
||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
||||
row.set_attribute("transform", transform * row_transform);
|
||||
|
||||
@@ -741,7 +741,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 `Table` and reset the transform since we've applied it directly to the points
|
||||
*row.element_mut() = result;
|
||||
row.set_attribute("transform", DAffine2::IDENTITY);
|
||||
row
|
||||
@@ -797,7 +797,7 @@ where
|
||||
let mut items: Vec<(f64, f64, DVec2, TableRow<T>)> = elements
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
// Single-element table to query its bounding box
|
||||
// Single-item `Table` to query its bounding box
|
||||
let single = Table::new_from_row(row.clone());
|
||||
let (w, h, top_left) = match single.bounding_box(DAffine2::IDENTITY, false) {
|
||||
RenderBoundingBox::Rectangle([min, max]) => {
|
||||
@@ -1210,7 +1210,7 @@ async fn solidify_stroke(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
solidified_stroke.style.set_fill(Fill::solid_or_none(stroke.color));
|
||||
}
|
||||
|
||||
// If the original vector has a fill, preserve it as a separate row with the stroke cleared.
|
||||
// If the original vector has a fill, preserve it as a separate item with the stroke cleared.
|
||||
let has_fill = !vector.style.fill().is_none();
|
||||
let fill_row = has_fill.then(|| {
|
||||
vector.style.clear_stroke();
|
||||
@@ -1219,7 +1219,7 @@ async fn solidify_stroke(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
|
||||
let stroke_row = TableRow::from_parts(solidified_stroke, attributes);
|
||||
|
||||
// Ordering based on the paint order. The first row in the table is rendered below the second.
|
||||
// Ordering based on the paint order. The first item in the `Table` 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<_>>(),
|
||||
@@ -1289,7 +1289,7 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||
let graphic_table = content.into_graphic_table();
|
||||
let flattened = graphic_table.clone().into_flattened_table::<Vector>();
|
||||
|
||||
// Create a table with one empty `Vector` element, then get a mutable reference to it which we append flattened subpaths to
|
||||
// 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();
|
||||
|
||||
@@ -1310,12 +1310,12 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||
output.style = element.style.clone();
|
||||
}
|
||||
|
||||
// Preserve a reference to the original upstream graphic table so the renderer can recurse into it
|
||||
// Preserve a reference to the original upstream `Table<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("editor:merged_layers", 0, graphic_table);
|
||||
|
||||
// Adopt the last input row's layer so the editor can also bucket clicks under a contributing child layer
|
||||
// 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("editor:layer", primary);
|
||||
@@ -2130,7 +2130,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve original graphic table as upstream data so this group layer's nested layers can be edited by the tools.
|
||||
// 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();
|
||||
|
||||
// If the input isn't a Table<Vector>, we convert it into one by flattening any Table<Graphic> content.
|
||||
@@ -2191,7 +2191,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
let control_bezpath = &control_bezpaths[subpath_index];
|
||||
let segment_count = control_bezpath.segments().count();
|
||||
|
||||
// If the control path has no segments, return the first element
|
||||
// If the control path has no segments, return the first item
|
||||
if segment_count == 0 {
|
||||
return content.into_iter().next().into_iter().collect();
|
||||
}
|
||||
@@ -2352,7 +2352,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||
};
|
||||
|
||||
// Pre-compensate merged_layers transforms so that when collect_metadata applies
|
||||
// the row transform (which will be group_transform * lerped_transform after the
|
||||
// the item transform (which will be group_transform * lerped_transform after the
|
||||
// pipeline's Transform node runs), the lerped_transform cancels out and children
|
||||
// get the correct footprint: parent * group_transform * child_transform.
|
||||
// Only pre-compensate if the lerped transform is invertible (non-zero determinant).
|
||||
@@ -2878,7 +2878,7 @@ async fn count_points(_: impl Ctx, content: Table<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 table of vector elements.
|
||||
/// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `Table` 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(
|
||||
|
||||
Reference in New Issue
Block a user