Instance table refactor part 6: remove usage of one_instance_* functions (#2672)

* Refactor the spline node

* Refactor the jitter_points node

* Refactor the morph node

* Refactor the merge_by_distance node

* Refactor the area node

* Refactor the centroid node

* Refactor the bevel node

* Refactor the tests

* Code review

* Refactor the morph node

* Refactor the extend_image_to_bounds and sample_image node

* Refactor the dehaze node

* Refactor the blur node

* Refactor the vector_points node

* Refactor the blit node

* Refactor the blend_gpu_image node

* Refactor the path_modify node

* Refactor the image_color_palette

* Fix copy_to_points

* Code review

* Partially make progress toward fixing the Draw Canvas node

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
mTvare
2025-05-29 03:08:16 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent fbefa5b827
commit 4d2e1d57fd
14 changed files with 640 additions and 588 deletions
@@ -609,7 +609,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [ nodes: [
DocumentNode { DocumentNode {
inputs: vec![NodeInput::network(concrete!(ImageFrameTable<Color>), 0)], inputs: vec![NodeInput::network(concrete!(ImageFrameTable<Color>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, ImageFrameTable>")), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, ImageFrameTable<SRGBA8>>")),
..Default::default() ..Default::default()
}, },
DocumentNode { DocumentNode {
@@ -647,7 +647,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Convert Image Frame".to_string(), display_name: "Into".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
+9
View File
@@ -36,6 +36,15 @@ impl AlphaBlending {
blend_mode: BlendMode::Normal, blend_mode: BlendMode::Normal,
} }
} }
pub fn lerp(&self, other: &Self, t: f32) -> Self {
let lerp = |a: f32, b: f32, t: f32| a + (b - a) * t;
AlphaBlending {
opacity: lerp(self.opacity, other.opacity, t),
blend_mode: if t < 0.5 { self.blend_mode } else { other.blend_mode },
}
}
} }
// TODO: Eventually remove this migration document upgrade code // TODO: Eventually remove this migration document upgrade code
+1 -1
View File
@@ -204,7 +204,7 @@ pub struct InstanceMut<'a, T> {
pub source_node_id: &'a mut Option<NodeId>, pub source_node_id: &'a mut Option<NodeId>,
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Default, Debug)]
pub struct Instance<T> { pub struct Instance<T> {
pub instance: T, pub instance: T,
pub transform: DAffine2, pub transform: DAffine2,
+18 -1
View File
@@ -2,7 +2,7 @@ use super::Color;
use super::discrete_srgb::float_to_srgb_u8; use super::discrete_srgb::float_to_srgb_u8;
use crate::AlphaBlending; use crate::AlphaBlending;
use crate::GraphicElement; use crate::GraphicElement;
use crate::instances::Instances; use crate::instances::{Instance, Instances};
use crate::transform::TransformMut; use crate::transform::TransformMut;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
@@ -393,6 +393,23 @@ impl From<Image<Color>> for Image<SRGBA8> {
} }
} }
impl From<ImageFrameTable<Color>> for ImageFrameTable<SRGBA8> {
fn from(image_frame_table: ImageFrameTable<Color>) -> Self {
let mut result_table = ImageFrameTable::<SRGBA8>::empty();
for image_frame_instance in image_frame_table.instance_iter() {
result_table.push(Instance {
instance: image_frame_instance.instance.into(),
transform: image_frame_instance.transform,
alpha_blending: image_frame_instance.alpha_blending,
source_node_id: image_frame_instance.source_node_id,
});
}
result_table
}
}
impl From<Image<SRGBA8>> for Image<Color> { impl From<Image<SRGBA8>> for Image<Color> {
fn from(image: Image<SRGBA8>) -> Self { fn from(image: Image<SRGBA8>) -> Self {
let data = image.data.into_iter().map(|x| x.into()).collect(); let data = image.data.into_iter().map(|x| x.into()).collect();
@@ -1,6 +1,5 @@
use super::*; use super::*;
use crate::Ctx; use crate::Ctx;
use crate::transform::TransformMut;
use crate::uuid::generate_uuid; use crate::uuid::generate_uuid;
use bezier_rs::BezierHandles; use bezier_rs::BezierHandles;
use core::hash::BuildHasher; use core::hash::BuildHasher;
@@ -425,14 +424,10 @@ impl core::hash::Hash for VectorModification {
/// A node that applies a procedural modification to some [`VectorData`]. /// A node that applies a procedural modification to some [`VectorData`].
#[node_macro::node(category(""))] #[node_macro::node(category(""))]
async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modification: Box<VectorModification>) -> VectorDataTable { async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modification: Box<VectorModification>) -> VectorDataTable {
let vector_data_transform = *vector_data.one_instance_ref().transform; for mut vector_data_instance in vector_data.instance_mut_iter() {
let vector_data = vector_data.one_instance_mut().instance; modification.apply(&mut vector_data_instance.instance);
}
modification.apply(vector_data); vector_data
let mut result = VectorDataTable::new(vector_data.clone());
*result.transform_mut() = vector_data_transform;
result
} }
#[test] #[test]
+128 -114
View File
@@ -7,7 +7,7 @@ use crate::instances::{Instance, InstanceMut, Instances};
use crate::raster::image::ImageFrameTable; use crate::raster::image::ImageFrameTable;
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue}; use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
use crate::renderer::GraphicElementRendered; use crate::renderer::GraphicElementRendered;
use crate::transform::{Footprint, ReferencePoint, Transform, TransformMut}; use crate::transform::{Footprint, ReferencePoint, Transform};
use crate::vector::PointDomain; use crate::vector::PointDomain;
use crate::vector::misc::dvec2_to_point; use crate::vector::misc::dvec2_to_point;
use crate::vector::style::{LineCap, LineJoin}; use crate::vector::style::{LineCap, LineJoin};
@@ -312,23 +312,22 @@ async fn copy_to_points<I: 'n + Send>(
where where
Instances<I>: GraphicElementRendered, Instances<I>: GraphicElementRendered,
{ {
let points_transform = points.transform(); let mut result_table = GraphicGroupTable::default();
let points_list = points.instance_ref_iter().flat_map(|element| element.instance.point_domain.positions());
let random_scale_difference = random_scale_max - random_scale_min; let random_scale_difference = random_scale_max - random_scale_min;
let instance_bounding_box = instance.bounding_box(DAffine2::IDENTITY, false).unwrap_or_default(); let instance_bounding_box = instance.bounding_box(DAffine2::IDENTITY, false).unwrap_or_default();
let instance_center = -0.5 * (instance_bounding_box[0] + instance_bounding_box[1]); let instance_center = -0.5 * (instance_bounding_box[0] + instance_bounding_box[1]);
for point_instance in points.instance_iter() {
let mut scale_rng = rand::rngs::StdRng::seed_from_u64(random_scale_seed.into()); let mut scale_rng = rand::rngs::StdRng::seed_from_u64(random_scale_seed.into());
let mut rotation_rng = rand::rngs::StdRng::seed_from_u64(random_rotation_seed.into()); let mut rotation_rng = rand::rngs::StdRng::seed_from_u64(random_rotation_seed.into());
let do_scale = random_scale_difference.abs() > 1e-6; let do_scale = random_scale_difference.abs() > 1e-6;
let do_rotation = random_rotation.abs() > 1e-6; let do_rotation = random_rotation.abs() > 1e-6;
let mut result_table = GraphicGroupTable::default(); let points_transform = point_instance.transform;
for &point in point_instance.instance.point_domain.positions() {
for &point in points_list.into_iter() {
let center_transform = DAffine2::from_translation(instance_center); let center_transform = DAffine2::from_translation(instance_center);
let translation = points_transform.transform_point2(point); let translation = points_transform.transform_point2(point);
@@ -363,6 +362,7 @@ where
source_node_id: None, source_node_id: None,
}); });
} }
}
result_table result_table
} }
@@ -445,12 +445,11 @@ async fn round_corners(
#[default(5.)] #[default(5.)]
min_angle_threshold: Angle, min_angle_threshold: Angle,
) -> VectorDataTable { ) -> VectorDataTable {
let source_transform = source.transform();
let source_transform_inverse = source_transform.inverse();
let mut result_table = VectorDataTable::empty(); let mut result_table = VectorDataTable::empty();
for source in source.instance_ref_iter() { for source in source.instance_ref_iter() {
let source_transform = *source.transform;
let source_transform_inverse = source_transform.inverse();
let source = source.instance; let source = source.instance;
let upstream_graphic_group = source.upstream_graphic_group.clone(); let upstream_graphic_group = source.upstream_graphic_group.clone();
@@ -1418,17 +1417,17 @@ async fn subpath_segment_lengths(_: impl Ctx, vector_data: VectorDataTable) -> V
} }
#[node_macro::node(name("Spline"), category("Vector"), path(graphene_core::vector))] #[node_macro::node(name("Spline"), category("Vector"), path(graphene_core::vector))]
async fn spline(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTable { async fn spline(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
let original_transform = vector_data.transform(); let mut result_table = VectorDataTable::empty();
let vector_data = vector_data.one_instance_mut().instance;
for mut vector_data_instance in vector_data.instance_iter() {
// Exit early if there are no points to generate splines from. // Exit early if there are no points to generate splines from.
if vector_data.point_domain.positions().is_empty() { if vector_data_instance.instance.point_domain.positions().is_empty() {
return VectorDataTable::new(VectorData::empty()); continue;
} }
let mut segment_domain = SegmentDomain::default(); let mut segment_domain = SegmentDomain::default();
for subpath in vector_data.stroke_bezier_paths() { for subpath in vector_data_instance.instance.stroke_bezier_paths() {
let positions = subpath.manipulator_groups().iter().map(|group| group.anchor).collect::<Vec<_>>(); let positions = subpath.manipulator_groups().iter().map(|group| group.anchor).collect::<Vec<_>>();
let closed = subpath.closed() && positions.len() > 2; let closed = subpath.closed() && positions.len() > 2;
@@ -1445,8 +1444,8 @@ async fn spline(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTabl
for i in 0..(positions.len() - if closed { 0 } else { 1 }) { for i in 0..(positions.len() - if closed { 0 } else { 1 }) {
let next_index = (i + 1) % positions.len(); let next_index = (i + 1) % positions.len();
let start_index = vector_data.point_domain.resolve_id(subpath.manipulator_groups()[i].id).unwrap(); let start_index = vector_data_instance.instance.point_domain.resolve_id(subpath.manipulator_groups()[i].id).unwrap();
let end_index = vector_data.point_domain.resolve_id(subpath.manipulator_groups()[next_index].id).unwrap(); let end_index = vector_data_instance.instance.point_domain.resolve_id(subpath.manipulator_groups()[next_index].id).unwrap();
let handle_start = first_handles[i]; let handle_start = first_handles[i];
let handle_end = positions[next_index] * 2. - first_handles[next_index]; let handle_end = positions[next_index] * 2. - first_handles[next_index];
@@ -1455,43 +1454,50 @@ async fn spline(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTabl
segment_domain.push(SegmentId::generate(), start_index, end_index, handles, stroke_id); segment_domain.push(SegmentId::generate(), start_index, end_index, handles, stroke_id);
} }
} }
vector_data.segment_domain = segment_domain;
let mut result = VectorDataTable::new(vector_data.clone()); vector_data_instance.instance.segment_domain = segment_domain;
*result.transform_mut() = original_transform; result_table.push(vector_data_instance);
result }
// TODO: remove after pt6 of instance table refactor
if result_table.is_empty() {
return VectorDataTable::new(VectorData::empty());
}
result_table
} }
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)] amount: f64, seed: SeedValue) -> VectorDataTable { async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)] amount: f64, seed: SeedValue) -> VectorDataTable {
let vector_data_transform = vector_data.transform(); let mut result_table = VectorDataTable::empty();
let mut vector_data = vector_data.one_instance_ref().instance.clone();
let inverse_transform = (vector_data_transform.matrix2.determinant() != 0.).then(|| vector_data_transform.inverse()).unwrap_or_default();
for mut vector_data_instance in vector_data.instance_iter() {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into()); let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
let deltas = (0..vector_data.point_domain.positions().len()) let vector_data_transform = vector_data_instance.transform;
let inverse_transform = (vector_data_transform.matrix2.determinant() != 0.).then(|| vector_data_transform.inverse()).unwrap_or_default();
let deltas = (0..vector_data_instance.instance.point_domain.positions().len())
.map(|_| { .map(|_| {
let angle = rng.random::<f64>() * std::f64::consts::TAU; let angle = rng.random::<f64>() * std::f64::consts::TAU;
inverse_transform.transform_vector2(DVec2::from_angle(angle) * rng.random::<f64>() * amount) inverse_transform.transform_vector2(DVec2::from_angle(angle) * rng.random::<f64>() * amount)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut already_applied = vec![false; vector_data.point_domain.positions().len()]; let mut already_applied = vec![false; vector_data_instance.instance.point_domain.positions().len()];
for (handles, start, end) in vector_data.segment_domain.handles_and_points_mut() { for (handles, start, end) in vector_data_instance.instance.segment_domain.handles_and_points_mut() {
let start_delta = deltas[*start]; let start_delta = deltas[*start];
let end_delta = deltas[*end]; let end_delta = deltas[*end];
if !already_applied[*start] { if !already_applied[*start] {
let start_position = vector_data.point_domain.positions()[*start]; let start_position = vector_data_instance.instance.point_domain.positions()[*start];
vector_data.point_domain.set_position(*start, start_position + start_delta); vector_data_instance.instance.point_domain.set_position(*start, start_position + start_delta);
already_applied[*start] = true; already_applied[*start] = true;
} }
if !already_applied[*end] { if !already_applied[*end] {
let end_position = vector_data.point_domain.positions()[*end]; let end_position = vector_data_instance.instance.point_domain.positions()[*end];
vector_data.point_domain.set_position(*end, end_position + end_delta); vector_data_instance.instance.point_domain.set_position(*end, end_position + end_delta);
already_applied[*end] = true; already_applied[*end] = true;
} }
@@ -1501,41 +1507,39 @@ async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)]
*handle_end += end_delta; *handle_end += end_delta;
} }
bezier_rs::BezierHandles::Quadratic { handle } => { bezier_rs::BezierHandles::Quadratic { handle } => {
*handle = vector_data_transform.transform_point2(*handle) + (start_delta + end_delta) / 2.; *handle = vector_data_instance.transform.transform_point2(*handle) + (start_delta + end_delta) / 2.;
} }
bezier_rs::BezierHandles::Linear => {} bezier_rs::BezierHandles::Linear => {}
} }
} }
vector_data.style.set_stroke_transform(DAffine2::IDENTITY); vector_data_instance.instance.style.set_stroke_transform(DAffine2::IDENTITY);
result_table.push(vector_data_instance);
}
let mut result = VectorDataTable::new(vector_data.clone()); result_table
*result.transform_mut() = vector_data_transform;
result
} }
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction) -> VectorDataTable { async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction) -> VectorDataTable {
let mut source = source;
let mut target = target;
let time = time.clamp(0., 1.); let time = time.clamp(0., 1.);
let mut result_table = VectorDataTable::default(); let mut result_table = VectorDataTable::empty();
for (source_instance, target_instance) in source.instance_iter().zip(target.instance_iter()) {
let mut vector_data_instance = VectorData::default();
// Lerp styles // Lerp styles
let source_alpha_blending = source.one_instance_ref().alpha_blending; let vector_data_alpha_blending = source_instance.alpha_blending.lerp(&target_instance.alpha_blending, time as f32);
let target_alpha_blending = target.one_instance_ref().alpha_blending; vector_data_instance.style = source_instance.instance.style.lerp(&target_instance.instance.style, time);
*result_table.one_instance_mut().alpha_blending = if time < 0.5 { *source_alpha_blending } else { *target_alpha_blending };
result_table.one_instance_mut().instance.style = source.one_instance_ref().instance.style.lerp(&target.one_instance_ref().instance.style, time);
// Before and after transforms // Before and after transforms
let source_transform = *source.one_instance_ref().transform; let source_transform = source_instance.transform;
let target_transform = *target.one_instance_ref().transform; let target_transform = target_instance.transform;
// Before and after paths // Before and after paths
let source_paths = source.one_instance_mut().instance.stroke_bezier_paths(); let source_paths = source_instance.instance.stroke_bezier_paths();
let target_paths = target.one_instance_mut().instance.stroke_bezier_paths(); let target_paths = target_instance.instance.stroke_bezier_paths();
for (mut source_path, mut target_path) in source_paths.zip(target_paths) { for (mut source_path, mut target_path) in source_paths.zip(target_paths) {
source_path.apply_transform(source_transform); source_path.apply_transform(source_transform);
target_path.apply_transform(target_transform); target_path.apply_transform(target_transform);
@@ -1565,14 +1569,14 @@ async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDat
source_manipulators.out_handle = Some(source_out_handle.lerp(target_out_handle, time)); source_manipulators.out_handle = Some(source_out_handle.lerp(target_out_handle, time));
} }
result_table.one_instance_mut().instance.append_subpath(source_path.clone(), true); vector_data_instance.append_subpath(source_path.clone(), true);
} }
// Deal with unmatched extra paths by collapsing them // Deal with unmatched extra paths by collapsing them
let source_paths_count = source.one_instance_ref().instance.stroke_bezier_paths().count(); let source_paths_count = source_instance.instance.stroke_bezier_paths().count();
let target_paths_count = target.one_instance_ref().instance.stroke_bezier_paths().count(); let target_paths_count = target_instance.instance.stroke_bezier_paths().count();
let source_paths = source.one_instance_mut().instance.stroke_bezier_paths().skip(target_paths_count); let source_paths = source_instance.instance.stroke_bezier_paths().skip(target_paths_count);
let target_paths = target.one_instance_mut().instance.stroke_bezier_paths().skip(source_paths_count); let target_paths = target_instance.instance.stroke_bezier_paths().skip(source_paths_count);
for mut source_path in source_paths { for mut source_path in source_paths {
source_path.apply_transform(source_transform); source_path.apply_transform(source_transform);
@@ -1582,7 +1586,7 @@ async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDat
group.in_handle = group.in_handle.map(|handle| handle.lerp(end, time)); group.in_handle = group.in_handle.map(|handle| handle.lerp(end, time));
group.out_handle = group.out_handle.map(|handle| handle.lerp(end, time)); group.out_handle = group.out_handle.map(|handle| handle.lerp(end, time));
} }
result_table.one_instance_mut().instance.append_subpath(source_path, true); vector_data_instance.append_subpath(source_path, true);
} }
for mut target_path in target_paths { for mut target_path in target_paths {
target_path.apply_transform(target_transform); target_path.apply_transform(target_transform);
@@ -1592,7 +1596,14 @@ async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDat
group.in_handle = group.in_handle.map(|handle| start.lerp(handle, time)); group.in_handle = group.in_handle.map(|handle| start.lerp(handle, time));
group.out_handle = group.out_handle.map(|handle| start.lerp(handle, time)); group.out_handle = group.out_handle.map(|handle| start.lerp(handle, time));
} }
result_table.one_instance_mut().instance.append_subpath(target_path, true); vector_data_instance.append_subpath(target_path, true);
}
result_table.push(Instance {
instance: vector_data_instance,
alpha_blending: vector_data_alpha_blending,
..Default::default()
});
} }
result_table result_table
@@ -1638,7 +1649,7 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2,
point_domain.push(next_id.next_id(), pos); point_domain.push(next_id.next_id(), pos);
// Add a new segment to be created later // Add a new segment to be created later
new_segments.push([new_index, original_index]) new_segments.push([new_index, original_index]);
} }
} }
@@ -1707,12 +1718,16 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2,
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable { fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable {
let source_transform = source.transform(); let mut result_table = VectorDataTable::empty();
let source = source.one_instance_ref().instance;
let mut result = VectorDataTable::new(bevel_algorithm(source.clone(), source_transform, distance)); for source_instance in source.instance_iter() {
*result.transform_mut() = source_transform; result_table.push(Instance {
result instance: bevel_algorithm(source_instance.instance, source_instance.transform, distance),
..Default::default()
});
}
result_table
} }
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
@@ -1734,15 +1749,14 @@ fn point_inside(_: impl Ctx, source: VectorDataTable, point: DVec2) -> bool {
#[node_macro::node(name("Merge by Distance"), category("Vector"), path(graphene_core::vector))] #[node_macro::node(name("Merge by Distance"), category("Vector"), path(graphene_core::vector))]
fn merge_by_distance(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable { fn merge_by_distance(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable {
let source_transform = source.transform(); let mut result_table = VectorDataTable::empty();
let mut source = source.one_instance_ref().instance.clone();
source.merge_by_distance(distance); for mut source_instance in source.instance_iter() {
source_instance.instance.merge_by_distance(distance);
result_table.push(source_instance);
}
let mut result = VectorDataTable::new(source); result_table
*result.transform_mut() = source_transform;
result
} }
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
@@ -1750,16 +1764,13 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context(); let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
let vector_data = vector_data.eval(new_ctx).await; let vector_data = vector_data.eval(new_ctx).await;
let vector_data_transform = vector_data.transform(); vector_data
let vector_data = vector_data.one_instance_ref().instance; .instance_ref_iter()
.map(|vector_data_instance| {
let mut area = 0.; let scale = vector_data_instance.transform.decompose_scale();
let scale = vector_data_transform.decompose_scale(); vector_data_instance.instance.stroke_bezier_paths().map(|subpath| subpath.area(Some(1e-3), Some(1e-3))).sum::<f64>() * scale.x * scale.y
for subpath in vector_data.stroke_bezier_paths() { })
area += subpath.area(Some(1e-3), Some(1e-3)); .sum()
}
area * scale[0] * scale[1]
} }
#[node_macro::node(category("Vector"), path(graphene_core::vector))] #[node_macro::node(category("Vector"), path(graphene_core::vector))]
@@ -1767,49 +1778,52 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl N
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context(); let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
let vector_data = vector_data.eval(new_ctx).await; let vector_data = vector_data.eval(new_ctx).await;
let vector_data_transform = vector_data.transform(); if vector_data.is_empty() {
let vector_data = vector_data.one_instance_ref().instance; return DVec2::ZERO;
}
if centroid_type == CentroidType::Area { // All subpath centroid positions added together as if they were vectors from the origin.
let mut area = 0.;
let mut centroid = DVec2::ZERO; let mut centroid = DVec2::ZERO;
for subpath in vector_data.stroke_bezier_paths() { // Cumulative area or length of all subpaths
if let Some((subpath_centroid, subpath_area)) = subpath.area_centroid_and_area(Some(1e-3), Some(1e-3)) { let mut sum = 0.;
if subpath_area == 0. {
continue; for vector_data_instance in vector_data.instance_ref_iter() {
for subpath in vector_data_instance.instance.stroke_bezier_paths() {
let partial = match centroid_type {
CentroidType::Area => subpath.area_centroid_and_area(Some(1e-3), Some(1e-3)).filter(|(_, area)| *area > 0.),
CentroidType::Length => subpath.length_centroid_and_length(None, true),
};
if let Some((subpath_centroid, area_or_length)) = partial {
let subpath_centroid = vector_data_instance.transform.transform_point2(subpath_centroid);
sum += area_or_length;
centroid += area_or_length * subpath_centroid;
} }
area += subpath_area;
centroid += subpath_area * subpath_centroid;
} }
} }
if area != 0. { if sum > 0. {
centroid /= area; centroid / sum
return vector_data_transform.transform_point2(centroid);
}
} }
// Without a summed denominator, return the average of all positions instead
else {
let mut count: usize = 0;
let mut length = 0.; let summed_positions = vector_data
let mut centroid = DVec2::ZERO; .instance_ref_iter()
for subpath in vector_data.stroke_bezier_paths() { .flat_map(|vector_data_instance| {
if let Some((subpath_centroid, subpath_length)) = subpath.length_centroid_and_length(None, true) { vector_data_instance
length += subpath_length; .instance
centroid += subpath_length * subpath_centroid; .point_domain
} .positions()
} .iter()
.map(|&p| vector_data_instance.transform.transform_point2(p))
})
.inspect(|_| count += 1)
.sum::<DVec2>();
if length != 0. { if count != 0 { summed_positions / (count as f64) } else { DVec2::ZERO }
centroid /= length;
return vector_data_transform.transform_point2(centroid);
} }
let positions = vector_data.point_domain.positions();
if !positions.is_empty() {
let centroid = positions.iter().sum::<DVec2>() / (positions.len() as f64);
return vector_data_transform.transform_point2(centroid);
}
DVec2::ZERO
} }
#[cfg(test)] #[cfg(test)]
@@ -1885,7 +1899,7 @@ mod test {
// Test a VectorData with non-zero rotation // Test a VectorData with non-zero rotation
let square = VectorData::from_subpath(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)); let square = VectorData::from_subpath(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE));
let mut square = VectorDataTable::new(square); let mut square = VectorDataTable::new(square);
*square.one_instance_mut().transform *= DAffine2::from_angle(core::f64::consts::FRAC_PI_4); *square.get_mut(0).unwrap().transform *= DAffine2::from_angle(core::f64::consts::FRAC_PI_4);
let bounding_box = BoundingBoxNode { let bounding_box = BoundingBoxNode {
vector_data: FutureWrapperNode(square), vector_data: FutureWrapperNode(square),
} }
@@ -2044,7 +2058,7 @@ mod test {
let vector_data = VectorData::from_subpath(source); let vector_data = VectorData::from_subpath(source);
let mut vector_data_table = VectorDataTable::new(vector_data.clone()); let mut vector_data_table = VectorDataTable::new(vector_data.clone());
*vector_data_table.one_instance_mut().transform = DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)); *vector_data_table.get_mut(0).unwrap().transform = DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.));
let beveled = super::bevel((), VectorDataTable::new(vector_data), 5.); let beveled = super::bevel((), VectorDataTable::new(vector_data), 5.);
let beveled = beveled.instance_ref_iter().next().unwrap().instance; let beveled = beveled.instance_ref_iter().next().unwrap().instance;
+10 -10
View File
@@ -15,9 +15,7 @@ use graphene_core::{Ctx, GraphicElement, Node};
#[node_macro::node(category("Debug"))] #[node_macro::node(category("Debug"))]
fn vector_points(_: impl Ctx, vector_data: VectorDataTable) -> Vec<DVec2> { fn vector_points(_: impl Ctx, vector_data: VectorDataTable) -> Vec<DVec2> {
let vector_data = vector_data.one_instance_ref().instance; vector_data.instance_iter().flat_map(|element| element.instance.point_domain.positions().to_vec()).collect()
vector_data.point_domain.positions().to_vec()
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@@ -96,16 +94,17 @@ where
return target; return target;
} }
let target_width = target.one_instance_ref().instance.width; for target_instance in target.instance_mut_iter() {
let target_height = target.one_instance_ref().instance.height; let target_width = target_instance.instance.width;
let target_height = target_instance.instance.height;
let target_size = DVec2::new(target_width as f64, target_height as f64); let target_size = DVec2::new(target_width as f64, target_height as f64);
let texture_size = DVec2::new(texture.width as f64, texture.height as f64); let texture_size = DVec2::new(texture.width as f64, texture.height as f64);
let document_to_target = DAffine2::from_translation(-texture_size / 2.) * DAffine2::from_scale(target_size) * target.transform().inverse(); let document_to_target = DAffine2::from_translation(-texture_size / 2.) * DAffine2::from_scale(target_size) * target_instance.transform.inverse();
for position in positions { for position in &positions {
let start = document_to_target.transform_point2(position).round(); let start = document_to_target.transform_point2(*position).round();
let stop = start + texture_size; let stop = start + texture_size;
// Half-open integer ranges [start, stop). // Half-open integer ranges [start, stop).
@@ -122,16 +121,17 @@ where
let max_y = (blit_area_offset.y + blit_area_dimensions.y).saturating_sub(1); let max_y = (blit_area_offset.y + blit_area_dimensions.y).saturating_sub(1);
let max_x = (blit_area_offset.x + blit_area_dimensions.x).saturating_sub(1); let max_x = (blit_area_offset.x + blit_area_dimensions.x).saturating_sub(1);
assert!(texture_index(max_x, max_y) < texture.data.len()); assert!(texture_index(max_x, max_y) < texture.data.len());
assert!(target_index(max_x, max_y) < target.one_instance_ref().instance.data.len()); assert!(target_index(max_x, max_y) < target_instance.instance.data.len());
for y in blit_area_offset.y..blit_area_offset.y + blit_area_dimensions.y { for y in blit_area_offset.y..blit_area_offset.y + blit_area_dimensions.y {
for x in blit_area_offset.x..blit_area_offset.x + blit_area_dimensions.x { for x in blit_area_offset.x..blit_area_offset.x + blit_area_dimensions.x {
let src_pixel = texture.data[texture_index(x, y)]; let src_pixel = texture.data[texture_index(x, y)];
let dst_pixel = &mut target.one_instance_mut().instance.data[target_index(x + clamp_start.x, y + clamp_start.y)]; let dst_pixel = &mut target_instance.instance.data[target_index(x + clamp_start.x, y + clamp_start.y)];
*dst_pixel = blend_mode.eval((src_pixel, *dst_pixel)); *dst_pixel = blend_mode.eval((src_pixel, *dst_pixel));
} }
} }
} }
}
target target
} }
+8 -9
View File
@@ -1,6 +1,5 @@
use graph_craft::proto::types::Percentage; use graph_craft::proto::types::Percentage;
use graphene_core::raster::image::{Image, ImageFrameTable}; use graphene_core::raster::image::{Image, ImageFrameTable};
use graphene_core::transform::{Transform, TransformMut};
use graphene_core::{Color, Ctx}; use graphene_core::{Color, Ctx};
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage}; use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr}; use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
@@ -8,11 +7,10 @@ use std::cmp::{max, min};
#[node_macro::node(category("Raster"))] #[node_macro::node(category("Raster"))]
async fn dehaze(_: impl Ctx, image_frame: ImageFrameTable<Color>, strength: Percentage) -> ImageFrameTable<Color> { async fn dehaze(_: impl Ctx, image_frame: ImageFrameTable<Color>, strength: Percentage) -> ImageFrameTable<Color> {
let image_frame_transform = image_frame.transform(); let mut result_table = ImageFrameTable::empty();
let image_frame_alpha_blending = image_frame.one_instance_ref().alpha_blending;
let image = image_frame.one_instance_ref().instance;
for mut image_frame_instance in image_frame.instance_iter() {
let image = image_frame_instance.instance;
// Prepare the image data for processing // Prepare the image data for processing
let image_data = bytemuck::cast_vec(image.data.clone()); let image_data = bytemuck::cast_vec(image.data.clone());
let image_buffer = image::Rgba32FImage::from_raw(image.width, image.height, image_data).expect("Failed to convert internal image format into image-rs data type."); let image_buffer = image::Rgba32FImage::from_raw(image.width, image.height, image_data).expect("Failed to convert internal image format into image-rs data type.");
@@ -31,11 +29,12 @@ async fn dehaze(_: impl Ctx, image_frame: ImageFrameTable<Color>, strength: Perc
base64_string: None, base64_string: None,
}; };
let mut result = ImageFrameTable::new(dehazed_image); image_frame_instance.instance = dehazed_image;
*result.transform_mut() = image_frame_transform; image_frame_instance.source_node_id = None;
*result.one_instance_mut().alpha_blending = *image_frame_alpha_blending; result_table.push(image_frame_instance);
}
result result_table
} }
// There is no real point in modifying these values because they do not change the final result all that much. // There is no real point in modifying these values because they do not change the final result all that much.
+8 -10
View File
@@ -1,7 +1,6 @@
use graph_craft::proto::types::PixelLength; use graph_craft::proto::types::PixelLength;
use graphene_core::raster::image::{Image, ImageFrameTable}; use graphene_core::raster::image::{Image, ImageFrameTable};
use graphene_core::raster::{Bitmap, BitmapMut}; use graphene_core::raster::{Bitmap, BitmapMut};
use graphene_core::transform::{Transform, TransformMut};
use graphene_core::{Color, Ctx}; use graphene_core::{Color, Ctx};
/// Blurs the image with a Gaussian or blur kernel filter. /// Blurs the image with a Gaussian or blur kernel filter.
@@ -19,10 +18,9 @@ async fn blur(
/// Opt to incorrectly apply the filter with color calculations in gamma space for compatibility with the results from other software. /// Opt to incorrectly apply the filter with color calculations in gamma space for compatibility with the results from other software.
gamma: bool, gamma: bool,
) -> ImageFrameTable<Color> { ) -> ImageFrameTable<Color> {
let image_frame_transform = image_frame.transform(); let mut result_table = ImageFrameTable::empty();
let image_frame_alpha_blending = image_frame.one_instance_ref().alpha_blending; for mut image_instance in image_frame.instance_iter() {
let image = image_instance.instance.clone();
let image = image_frame.one_instance_ref().instance.clone();
// Run blur algorithm // Run blur algorithm
let blurred_image = if radius < 0.1 { let blurred_image = if radius < 0.1 {
@@ -34,11 +32,11 @@ async fn blur(
gaussian_blur_algorithm(image, radius, gamma) gaussian_blur_algorithm(image, radius, gamma)
}; };
let mut result = ImageFrameTable::new(blurred_image); image_instance.instance = blurred_image;
*result.transform_mut() = image_frame_transform; image_instance.source_node_id = None;
*result.one_instance_mut().alpha_blending = *image_frame_alpha_blending; result_table.push(image_instance);
}
result result_table
} }
// 1D gaussian kernel // 1D gaussian kernel
+11 -13
View File
@@ -5,8 +5,6 @@ use graph_craft::document::*;
use graph_craft::proto::*; use graph_craft::proto::*;
use graphene_core::raster::BlendMode; use graphene_core::raster::BlendMode;
use graphene_core::raster::image::{Image, ImageFrameTable}; use graphene_core::raster::image::{Image, ImageFrameTable};
use graphene_core::transform::Transform;
use graphene_core::transform::TransformMut;
use graphene_core::*; use graphene_core::*;
use std::sync::Arc; use std::sync::Arc;
use wgpu_executor::{Bindgroup, PipelineLayout, Shader, ShaderIO, ShaderInput, WgpuExecutor}; use wgpu_executor::{Bindgroup, PipelineLayout, Shader, ShaderIO, ShaderInput, WgpuExecutor};
@@ -37,13 +35,13 @@ async fn compile_gpu<'a: 'n>(_: impl Ctx, node: &'a DocumentNode, typing_context
#[node_macro::node(category("Debug: GPU"))] #[node_macro::node(category("Debug: GPU"))]
async fn blend_gpu_image(_: impl Ctx, foreground: ImageFrameTable<Color>, background: ImageFrameTable<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrameTable<Color> { async fn blend_gpu_image(_: impl Ctx, foreground: ImageFrameTable<Color>, background: ImageFrameTable<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrameTable<Color> {
let foreground_transform = foreground.transform(); let mut result_table = ImageFrameTable::empty();
let background_transform = background.transform(); for (foreground_instance, mut background_instance) in foreground.instance_iter().zip(background.instance_iter()) {
let foreground_transform = foreground_instance.transform;
let background_transform = background_instance.transform;
let background_alpha_blending = background.one_instance_ref().alpha_blending; let foreground = foreground_instance.instance;
let background = background_instance.instance;
let foreground = foreground.one_instance_ref().instance;
let background = background.one_instance_ref().instance;
let foreground_size = DVec2::new(foreground.width as f64, foreground.height as f64); let foreground_size = DVec2::new(foreground.width as f64, foreground.height as f64);
let background_size = DVec2::new(background.width as f64, background.height as f64); let background_size = DVec2::new(background.width as f64, background.height as f64);
@@ -213,11 +211,11 @@ async fn blend_gpu_image(_: impl Ctx, foreground: ImageFrameTable<Color>, backgr
..Default::default() ..Default::default()
}; };
let mut result = ImageFrameTable::new(created_image); background_instance.instance = created_image;
*result.transform_mut() = background_transform; background_instance.source_node_id = None;
*result.one_instance_mut().alpha_blending = *background_alpha_blending; result_table.push(background_instance);
}
result result_table
} }
// struct ComputePass { // struct ComputePass {
+3 -3
View File
@@ -16,9 +16,8 @@ async fn image_color_palette(
let mut histogram: Vec<usize> = vec![0; (bins + 1.) as usize]; let mut histogram: Vec<usize> = vec![0; (bins + 1.) as usize];
let mut colors: Vec<Vec<Color>> = vec![vec![]; (bins + 1.) as usize]; let mut colors: Vec<Vec<Color>> = vec![vec![]; (bins + 1.) as usize];
let image = image.one_instance_ref().instance; for image_instance in image.instance_ref_iter() {
for pixel in image_instance.instance.data.iter() {
for pixel in image.data.iter() {
let r = pixel.r() * GRID; let r = pixel.r() * GRID;
let g = pixel.g() * GRID; let g = pixel.g() * GRID;
let b = pixel.b() * GRID; let b = pixel.b() * GRID;
@@ -28,6 +27,7 @@ async fn image_color_palette(
histogram[bin] += 1; histogram[bin] += 1;
colors[bin].push(pixel.to_gamma_srgb()); colors[bin].push(pixel.to_gamma_srgb());
} }
}
let shorted = histogram.iter().enumerate().filter(|&(_, &count)| count > 0).map(|(i, _)| i).collect::<Vec<usize>>(); let shorted = histogram.iter().enumerate().filter(|&(_, &count)| count > 0).map(|(i, _)| i).collect::<Vec<usize>>();
+51 -31
View File
@@ -27,10 +27,11 @@ impl From<std::io::Error> for Error {
#[node_macro::node(category("Debug: Raster"))] #[node_macro::node(category("Debug: Raster"))]
fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> { fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> {
let image_frame_transform = image_frame.transform(); let mut result_table = ImageFrameTable::empty();
let image_frame_alpha_blending = image_frame.one_instance_ref().alpha_blending;
let image = image_frame.one_instance_ref().instance; for mut image_frame_instance in image_frame.instance_iter() {
let image_frame_transform = image_frame_instance.transform;
let image = image_frame_instance.instance;
// Resize the image using the image crate // Resize the image using the image crate
let data = bytemuck::cast_vec(image.data.clone()); let data = bytemuck::cast_vec(image.data.clone());
@@ -43,9 +44,9 @@ fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFra
let size = intersection.size(); let size = intersection.size();
let size_px = image_size.transform_vector2(size).as_uvec2(); let size_px = image_size.transform_vector2(size).as_uvec2();
// If the image would not be visible, return an empty image // If the image would not be visible, add nothing.
if size.x <= 0. || size.y <= 0. { if size.x <= 0. || size.y <= 0. {
return ImageFrameTable::one_empty_image(); continue;
} }
let image_buffer = ::image::Rgba32FImage::from_raw(image.width, image.height, data).expect("Failed to convert internal image format into image-rs data type."); let image_buffer = ::image::Rgba32FImage::from_raw(image.width, image.height, data).expect("Failed to convert internal image format into image-rs data type.");
@@ -82,11 +83,18 @@ fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFra
let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size); let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
let mut result = ImageFrameTable::new(image); image_frame_instance.transform = new_transform;
*result.transform_mut() = new_transform; image_frame_instance.source_node_id = None;
*result.one_instance_mut().alpha_blending = *image_frame_alpha_blending; image_frame_instance.instance = image;
result_table.push(image_frame_instance)
}
result // TODO: Remove when we've completed part 6 of the instance tables refactor
if result_table.is_empty() {
return ImageFrameTable::one_empty_image();
}
result_table
} }
#[node_macro::node(category("Raster"))] #[node_macro::node(category("Raster"))]
@@ -251,19 +259,27 @@ where
#[node_macro::node(category(""))] #[node_macro::node(category(""))]
fn extend_image_to_bounds(_: impl Ctx, image: ImageFrameTable<Color>, bounds: DAffine2) -> ImageFrameTable<Color> { fn extend_image_to_bounds(_: impl Ctx, image: ImageFrameTable<Color>, bounds: DAffine2) -> ImageFrameTable<Color> {
let image_aabb = Bbox::unit().affine_transform(image.transform()).to_axis_aligned_bbox(); let mut result_table = ImageFrameTable::empty();
for mut image_instance in image.instance_iter() {
let image_aabb = Bbox::unit().affine_transform(image_instance.transform).to_axis_aligned_bbox();
let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox(); let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox();
if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) { if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) {
return image; result_table.push(image_instance);
continue;
} }
let image_instance = image.one_instance_ref().instance; let image_data = image_instance.instance.data;
if image_instance.width == 0 || image_instance.height == 0 { let (image_width, image_height) = (image_instance.instance.width, image_instance.instance.height);
return empty_image((), bounds, Color::TRANSPARENT); if image_width == 0 || image_height == 0 {
for image_instance in empty_image((), bounds, Color::TRANSPARENT).instance_iter() {
result_table.push(image_instance);
}
continue;
} }
let orig_image_scale = DVec2::new(image_instance.width as f64, image_instance.height as f64); let orig_image_scale = DVec2::new(image_width as f64, image_height as f64);
let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * image.transform().inverse(); let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * image_instance.transform.inverse();
let bounds_in_image_space = Bbox::unit().affine_transform(layer_to_image_space * bounds).to_axis_aligned_bbox(); let bounds_in_image_space = Bbox::unit().affine_transform(layer_to_image_space * bounds).to_axis_aligned_bbox();
let new_start = bounds_in_image_space.start.floor().min(DVec2::ZERO); let new_start = bounds_in_image_space.start.floor().min(DVec2::ZERO);
@@ -271,25 +287,27 @@ fn extend_image_to_bounds(_: impl Ctx, image: ImageFrameTable<Color>, bounds: DA
let new_scale = new_end - new_start; let new_scale = new_end - new_start;
// Copy over original image into enlarged image. // Copy over original image into enlarged image.
let mut new_img = Image::new(new_scale.x as u32, new_scale.y as u32, Color::TRANSPARENT); let mut new_image = Image::new(new_scale.x as u32, new_scale.y as u32, Color::TRANSPARENT);
let offset_in_new_image = (-new_start).as_uvec2(); let offset_in_new_image = (-new_start).as_uvec2();
for y in 0..image_instance.height { for y in 0..image_height {
let old_start = y * image_instance.width; let old_start = y * image_width;
let new_start = (y + offset_in_new_image.y) * new_img.width + offset_in_new_image.x; let new_start = (y + offset_in_new_image.y) * new_image.width + offset_in_new_image.x;
let old_row = &image_instance.data[old_start as usize..(old_start + image_instance.width) as usize]; let old_row = &image_data[old_start as usize..(old_start + image_width) as usize];
let new_row = &mut new_img.data[new_start as usize..(new_start + image_instance.width) as usize]; let new_row = &mut new_image.data[new_start as usize..(new_start + image_width) as usize];
new_row.copy_from_slice(old_row); new_row.copy_from_slice(old_row);
} }
// Compute new transform. // Compute new transform.
// let layer_to_new_texture_space = (DAffine2::from_scale(1. / new_scale) * DAffine2::from_translation(new_start) * layer_to_image_space).inverse(); // let layer_to_new_texture_space = (DAffine2::from_scale(1. / new_scale) * DAffine2::from_translation(new_start) * layer_to_image_space).inverse();
let new_texture_to_layer_space = image.transform() * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale); let new_texture_to_layer_space = image_instance.transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
let mut result = ImageFrameTable::new(new_img); image_instance.instance = new_image;
*result.transform_mut() = new_texture_to_layer_space; image_instance.transform = new_texture_to_layer_space;
*result.one_instance_mut().alpha_blending = *image.one_instance_ref().alpha_blending; image_instance.source_node_id = None;
result_table.push(image_instance);
}
result result_table
} }
#[node_macro::node(category("Debug: Raster"))] #[node_macro::node(category("Debug: Raster"))]
@@ -299,11 +317,13 @@ fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> ImageFrameTabl
let image = Image::new(width, height, color); let image = Image::new(width, height, color);
let mut result = ImageFrameTable::new(image); let mut result_table = ImageFrameTable::new(image);
*result.transform_mut() = transform; let image_instance = result_table.get_mut(0).unwrap();
*result.one_instance_mut().alpha_blending = AlphaBlending::default(); *image_instance.transform = transform;
*image_instance.alpha_blending = AlphaBlending::default();
result // Callers of empty_image can safely unwrap on returned table
result_table
} }
/// Constructs a raster image. /// Constructs a raster image.
+2 -1
View File
@@ -30,9 +30,10 @@ use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
#[node_macro::node(category("Debug: GPU"))] #[node_macro::node(category("Debug: GPU"))]
async fn create_surface<'a: 'n>(_: impl Ctx, editor: &'a WasmEditorApi) -> Arc<WasmSurfaceHandle> { async fn create_surface<'a: 'n>(_: impl Ctx, editor: &'a WasmEditorApi) -> Arc<WasmSurfaceHandle> {
return Arc::new(editor.application_io.as_ref().unwrap().create_window()); Arc::new(editor.application_io.as_ref().unwrap().create_window())
} }
// TODO: Fix and reenable in order to get the 'Draw Canvas' node working again.
// #[cfg(target_arch = "wasm32")] // #[cfg(target_arch = "wasm32")]
// use wasm_bindgen::Clamped; // use wasm_bindgen::Clamped;
// //
@@ -27,16 +27,17 @@ use wgpu_executor::{WgpuSurface, WindowHandle};
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> { fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
let node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ let node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
into_node!(from: f64, to: f64), into_node!(from: f64, to: f64),
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable),
into_node!(from: f64, to: f64), into_node!(from: f64, to: f64),
into_node!(from: u32, to: f64), into_node!(from: u32, to: f64),
into_node!(from: u8, to: u32), into_node!(from: u8, to: u32),
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable), into_node!(from: VectorDataTable, to: VectorDataTable),
into_node!(from: VectorDataTable, to: GraphicGroupTable),
into_node!(from: VectorDataTable, to: GraphicElement), into_node!(from: VectorDataTable, to: GraphicElement),
into_node!(from: ImageFrameTable<Color>, to: GraphicElement),
into_node!(from: GraphicGroupTable, to: GraphicElement),
into_node!(from: VectorDataTable, to: GraphicGroupTable), into_node!(from: VectorDataTable, to: GraphicGroupTable),
into_node!(from: GraphicGroupTable, to: GraphicGroupTable),
into_node!(from: GraphicGroupTable, to: GraphicElement),
into_node!(from: ImageFrameTable<Color>, to: ImageFrameTable<Color>),
into_node!(from: ImageFrameTable<Color>, to: ImageFrameTable<SRGBA8>),
into_node!(from: ImageFrameTable<Color>, to: GraphicElement),
into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable), into_node!(from: ImageFrameTable<Color>, to: GraphicGroupTable),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageFrameTable<Color>]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageFrameTable<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),