Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,17 @@ impl TableRowLayout for Raster<CPU> {
format!("Raster ({}x{})", self.width, self.height)
}
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
let base64_string = self.data().base64_string.clone().unwrap_or_else(|| {
let raster = self.data();

if raster.width == 0 || raster.height == 0 {
let widgets = vec![TextLabel::new("Image has no area").widget_holder()];
return vec![LayoutGroup::Row { widgets }];
}

let base64_string = raster.base64_string.clone().unwrap_or_else(|| {
use base64::Engine;

let output = self.data().to_png();
let output = raster.to_png();
let preamble = "data:image/png;base64,";
let mut base64_string = String::with_capacity(preamble.len() + output.len() * 4);
base64_string.push_str(preamble);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ pub(crate) fn property_from_type(
Some("PixelLength") => number_widget(default_info, number_input.min(min(0.)).unit(unit.unwrap_or(" px"))).into(),
Some("Length") => number_widget(default_info, number_input.min(min(0.))).into(),
Some("Fraction") => number_widget(default_info, number_input.mode_range().min(min(0.)).max(max(1.))).into(),
Some("SignedInteger") => number_widget(default_info, number_input.int()).into(),
Some("IntegerCount") => number_widget(default_info, number_input.int().min(min(1.))).into(),
Some("SeedValue") => number_widget(default_info, number_input.int().min(min(0.))).into(),
Some("PixelSize") => vec2_widget(default_info, "X", "Y", unit.unwrap_or(" px"), None, false),
Expand Down
2 changes: 1 addition & 1 deletion node-graph/graph-craft/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ impl NodeNetwork {
// If the input to self is a node, connect the corresponding output of the inner network to it
NodeInput::Node { node_id, output_index } => {
nested_node.populate_first_network_input(node_id, output_index, nested_input_index, node.original_location.inputs(*import_index), 1);
let input_node = self.nodes.get_mut(&node_id).unwrap_or_else(|| panic!("unable find input node {node_id:?}"));
let input_node = self.nodes.get_mut(&node_id).unwrap_or_else(|| panic!("Unable to find input node {node_id:?}"));
input_node.original_location.dependants[output_index].push(nested_node_id);
}
NodeInput::Import { import_index, .. } => {
Expand Down
2 changes: 2 additions & 0 deletions node-graph/libraries/core-types/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
66 changes: 66 additions & 0 deletions node-graph/libraries/graphic-types/src/graphic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -284,13 +333,18 @@ 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;

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>;
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions node-graph/libraries/no-std-types/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 18 additions & 55 deletions node-graph/nodes/graphic/src/graphic.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use core_types::Color;
use core_types::{
Ctx,
blending::AlphaBlending,
table::{Table, TableRow},
uuid::NodeId,
};
use core_types::Ctx;
use core_types::registry::types::SignedInteger;
use core_types::table::{Table, TableRow};
use core_types::uuid::NodeId;
use glam::{DAffine2, DVec2};
use graphic_types::{
Artboard, Vector,
graphic::{Graphic, IntoGraphicTable},
};
use graphic_types::graphic::{Graphic, IntoGraphicTable};
use graphic_types::{Artboard, Vector};
use raster_types::{CPU, GPU, Raster};
use vector_types::GradientStops;

Expand Down Expand Up @@ -164,48 +160,8 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten

/// Converts a graphic table into a vector table by deeply flattening any vector content it contains, and discarding any non-vector content.
#[node_macro::node(category("Vector"))]
pub async fn flatten_vector(_: impl Ctx, content: Table<Graphic>) -> Table<Vector> {
// 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
pub async fn flatten_vector<I: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: I) -> Table<Vector> {
content.into_flattened_vector_table()
}

/// Returns the value at the specified index in the collection.
Expand All @@ -229,11 +185,18 @@ pub fn index_elements<T: graphic_types::graphic::AtIndex + Clone + Default>(
Table<GradientStops>,
)]
collection: T,
/// The index of the item to retrieve, starting from 0 for the first item.
index: u32,
/// 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.
index: SignedInteger,
) -> T::Output
where
T::Output: Clone + Default,
{
collection.at_index(index as usize).unwrap_or_default()
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()
}
79 changes: 65 additions & 14 deletions node-graph/nodes/transform/src/transform_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core_types::color::Color;
use core_types::table::Table;
use core_types::transform::{ApplyTransform, Transform};
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, InjectFootprint, ModifyFootprint, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use glam::{DAffine2, DMat2, DVec2};
use graphic_types::Graphic;
use graphic_types::Vector;
use graphic_types::raster_types::{CPU, GPU, Raster};
Expand All @@ -16,14 +16,14 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
#[implementations(
Context -> DAffine2,
Context -> DVec2,
Context -> Table<Vector>,
Context -> Table<Graphic>,
Context -> Table<Vector>,
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<GradientStops>,
)]
value: impl Node<Context<'static>, Output = T>,
content: impl Node<Context<'static>, Output = T>,
translation: DVec2,
rotation: f64,
scale: DVec2,
Expand All @@ -41,24 +41,75 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
ctx = ctx.with_footprint(footprint);
}

let mut transform_target = value.eval(ctx.into_context()).await;
let mut transform_target = content.eval(ctx.into_context()).await;

transform_target.left_apply_transform(&matrix);

transform_target
}

/// Resets the desired components of the input transform to their default values. If all components are reset, the output will be set to the identity transform.
/// Shear is represented jointly by rotation and scale, so resetting both will also remove any shear.
#[node_macro::node(category("Math: Transform"))]
fn reset_transform<T>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
mut content: Table<T>,
#[default(true)] reset_translation: bool,
reset_rotation: bool,
reset_scale: bool,
) -> Table<T> {
for row in content.iter_mut() {
// Translation
if reset_translation {
row.transform.translation = DVec2::ZERO;
}
// (Rotation, Scale)
match (reset_rotation, reset_scale) {
(true, true) => {
row.transform.matrix2 = DMat2::IDENTITY;
}
(true, false) => {
let scale = row.transform.decompose_scale();
row.transform.matrix2 = DMat2::from_diagonal(scale);
}
(false, true) => {
let rotation = row.transform.decompose_rotation();
let rotation_matrix = DMat2::from_angle(rotation);
row.transform.matrix2 = rotation_matrix;
}
(false, false) => {}
}
}
content
}

/// Overwrites the transform of each element in the input table with the specified transform.
#[node_macro::node(category(""))]
fn replace_transform<Data, TransformInput: Transform>(
#[node_macro::node(category("Math: Transform"))]
fn replace_transform<T>(
_: impl Ctx + InjectFootprint,
#[implementations(Table<Vector>, Table<Raster<CPU>>, Table<Graphic>, Table<Color>, Table<GradientStops>)] mut data: Table<Data>,
#[implementations(DAffine2)] transform: TransformInput,
) -> Table<Data> {
for data_transform in data.iter_mut() {
*data_transform.transform = transform.transform();
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
mut content: Table<T>,
transform: DAffine2,
) -> Table<T> {
for row in content.iter_mut() {
*row.transform = transform.transform();
}
data
content
}

// TODO: Figure out how this node should behave once #2982 is implemented.
Expand All @@ -74,9 +125,9 @@ async fn extract_transform<T>(
Table<Color>,
Table<GradientStops>,
)]
vector: Table<T>,
content: Table<T>,
) -> DAffine2 {
vector.iter().next().map(|row| *row.transform).unwrap_or_default()
content.iter().next().map(|row| *row.transform).unwrap_or_default()
}

/// Produces the inverse of the input transform, which is the transform that undoes the effect of the original transform.
Expand Down
Loading
Loading