Node graph improvements (#855)

* Selecting multiple nodes

* Improve logs

* Log bad types in dyn any

* Add (broken) node links

* New topological sort

* Fix reorder ids function

* Input and output node

* Add nodes that operate on images

* Fixups

* Show node parameters together with layer properties

* New nodes don't crash editor

* Fix tests

* Node positions backend

* Generate node graph on value change

* Add expose input message

* Fix tests

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2022-11-17 23:36:23 +00:00
committed by Keavon Chambers
parent bbe98d3fe3
commit 2994afa6b8
23 changed files with 734 additions and 331 deletions

View File

@@ -26,3 +26,8 @@ proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macr
quote = {version = "1.0", default-features = false }
image = "*"
dyn-clone = "1.0"
[dependencies.serde]
version = "1.0"
optional = true
features = ["derive"]

View File

@@ -98,7 +98,8 @@ impl<Reader: std::io::Read> Node<Reader> for BufferNode {
}
}
#[derive(Clone, DynAny)]
#[derive(Clone, Debug, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Image {
pub width: u32,
pub height: u32,
@@ -165,16 +166,104 @@ pub fn export_image_node<'n>() -> impl Node<(Image, &'n str), Output = Result<()
})
}
#[derive(Debug, Clone, Copy)]
pub struct GrayscaleImageNode;
impl Node<Image> for GrayscaleImageNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
for pixel in &mut image.data {
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
}
image
}
}
impl Node<Image> for &GrayscaleImageNode {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
for pixel in &mut image.data {
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
}
image
}
}
#[derive(Debug, Clone, Copy)]
pub struct BrightenImageNode<N: Node<(), Output = f32>>(N);
impl<N: Node<(), Output = f32>> Node<Image> for BrightenImageNode<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let brightness = self.0.eval(());
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(per_channel(pixel.r()), per_channel(pixel.g()), per_channel(pixel.b()), pixel.a());
}
image
}
}
impl<N: Node<(), Output = f32> + Copy> Node<Image> for &BrightenImageNode<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let brightness = self.0.eval(());
let per_channel = |col: f32| (col + brightness / 255.).clamp(0., 1.);
for pixel in &mut image.data {
*pixel = Color::from_rgbaf32_unchecked(per_channel(pixel.r()), per_channel(pixel.g()), per_channel(pixel.b()), pixel.a());
}
image
}
}
impl<N: Node<(), Output = f32> + Copy> BrightenImageNode<N> {
pub fn new(node: N) -> Self {
Self(node)
}
}
#[derive(Debug, Clone, Copy)]
pub struct HueShiftImage<N: Node<(), Output = f32>>(N);
impl<N: Node<(), Output = f32>> Node<Image> for HueShiftImage<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let hue_shift = self.0.eval(());
for pixel in &mut image.data {
let [hue, saturation, luminance, alpha] = pixel.to_hsla();
*pixel = Color::from_hsla(hue + hue_shift / 360., saturation, luminance, alpha);
}
image
}
}
impl<N: Node<(), Output = f32> + Copy> Node<Image> for &HueShiftImage<N> {
type Output = Image;
fn eval(self, mut image: Image) -> Image {
let hue_shift = self.0.eval(());
for pixel in &mut image.data {
let [hue, saturation, luminance, alpha] = pixel.to_hsla();
*pixel = Color::from_hsla(hue + hue_shift / 360., saturation, luminance, alpha);
}
image
}
}
impl<N: Node<(), Output = f32> + Copy> HueShiftImage<N> {
pub fn new(node: N) -> Self {
Self(node)
}
}
#[cfg(test)]
mod test {
use super::*;
use graphene_core::raster::color::Color;
use graphene_core::raster::GrayscaleNode;
use graphene_core::raster::GrayscaleColorNode;
#[test]
fn map_node() {
let array = [Color::from_rgbaf32(1.0, 0.0, 0.0, 1.0).unwrap()];
let map = MapNode(GrayscaleNode, PhantomData);
let map = MapNode(GrayscaleColorNode, PhantomData);
let values = map.eval(array.into_iter());
assert_eq!(values[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap());
}
@@ -182,7 +271,7 @@ mod test {
#[test]
fn load_image() {
let image = image_node::<&str>();
let gray = MapImageNode::new(GrayscaleNode);
let gray = MapImageNode::new(GrayscaleColorNode);
let grayscale_picture = image.then(MapResultNode::new(&gray));
let export = export_image_node();