New nodes: 'Reset Transform', 'Replace Transform', 'Count Points', 'Index Points' (#3420)

- Add the 'Reset Transform' and 'Replace Transform' nodes
- Add the 'Count Points' and 'Index Points' nodes
- Make the 'Index Elements' node support negative indexing from the end
- Make the 'Flatten Vector' node's implementation reusable
- Fix crash displaying 0x0 raster image in the Data panel
- Fix the 'Points to Polyline' node not working on two-point objects
This commit is contained in:
Keavon Chambers
2025-11-25 20:41:59 -08:00
committed by GitHub
parent 117ce301b0
commit eb0f019b15
9 changed files with 210 additions and 79 deletions

View File

@@ -79,6 +79,8 @@ impl Convert<DVec2, ()> for DVec2 {
}
}
// TODO: Add a DVec2 to Table<Vector> anchor point conversion implementation to replace the 'Vec2 to Point' node
/// Implements the [`Convert`] trait for conversion between the cartesian product of Rust's primitive numeric types.
macro_rules! impl_convert {
($from:ty, $to:ty) => {

View File

@@ -119,6 +119,55 @@ impl From<Table<GradientStops>> for Graphic {
// Local trait to convert types to Table<Graphic> (avoids orphan rule issues)
pub trait IntoGraphicTable {
fn into_graphic_table(self) -> Table<Graphic>;
/// Deeply flattens any vector content within a graphic table, discarding non-vector content, and returning a table of only vector elements.
fn into_flattened_vector_table(self) -> Table<Vector>
where
Self: std::marker::Sized,
{
let content = self.into_graphic_table();
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
fn flatten_table(output_vector_table: &mut Table<Vector>, current_graphic_table: Table<Graphic>) {
for current_graphic_row in current_graphic_table.iter() {
let current_graphic = current_graphic_row.element.clone();
let source_node_id = *current_graphic_row.source_node_id;
match current_graphic {
// If we're allowed to recurse, flatten any tables we encounter
Graphic::Graphic(mut current_graphic_table) => {
// Apply the parent graphic's transform to all child elements
for graphic in current_graphic_table.iter_mut() {
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
}
flatten_table(output_vector_table, current_graphic_table);
}
// Push any leaf Vector elements we encounter
Graphic::Vector(vector_table) => {
for current_vector_row in vector_table.iter() {
output_vector_table.push(TableRow {
element: current_vector_row.element.clone(),
transform: *current_graphic_row.transform * *current_vector_row.transform,
alpha_blending: AlphaBlending {
blend_mode: current_vector_row.alpha_blending.blend_mode,
opacity: current_graphic_row.alpha_blending.opacity * current_vector_row.alpha_blending.opacity,
fill: current_vector_row.alpha_blending.fill,
clip: current_vector_row.alpha_blending.clip,
},
source_node_id,
});
}
}
_ => {}
}
}
}
let mut output = Table::new();
flatten_table(&mut output, content);
output
}
}
impl IntoGraphicTable for Table<Graphic> {
@@ -284,6 +333,7 @@ impl RenderComplexity for Graphic {
pub trait AtIndex {
type Output;
fn at_index(&self, index: usize) -> Option<Self::Output>;
fn at_index_from_end(&self, index: usize) -> Option<Self::Output>;
}
impl<T: Clone> AtIndex for Vec<T> {
type Output = T;
@@ -291,6 +341,10 @@ impl<T: Clone> AtIndex for Vec<T> {
fn at_index(&self, index: usize) -> Option<Self::Output> {
self.get(index).cloned()
}
fn at_index_from_end(&self, index: usize) -> Option<Self::Output> {
if index == 0 || index > self.len() { None } else { self.get(self.len() - index).cloned() }
}
}
impl<T: Clone> AtIndex for Table<T> {
type Output = Table<T>;
@@ -304,6 +358,18 @@ impl<T: Clone> AtIndex for Table<T> {
None
}
}
fn at_index_from_end(&self, index: usize) -> Option<Self::Output> {
let mut result_table = Self::default();
if index == 0 || index > self.len() {
None
} else if let Some(row) = self.iter().nth(self.len() - index) {
result_table.push(row.into_cloned());
Some(result_table)
} else {
None
}
}
}
// TODO: Eventually remove this migration document upgrade code

View File

@@ -19,6 +19,8 @@ pub mod types {
pub type Length = f64;
/// 0 to 1
pub type Fraction = f64;
/// Signed integer that's actually a float because we don't handle type conversions very well yet
pub type SignedInteger = f64;
/// Unsigned integer
pub type IntegerCount = u32;
/// Unsigned integer to be used for random seeds