New node: Noise Pattern (#1518)
Add the Noise Pattern node Closes #1517
This commit is contained in:
parent
9d3344808f
commit
9f0ea35d9b
|
|
@ -1461,6 +1461,15 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastnoise-lite"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea2b3d9ffd3c9e98a5fc12f58ac3f7c48943dac14c6638e96db09ac77880fa9a"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
|
|
@ -2218,6 +2227,7 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"compilation-client",
|
"compilation-client",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
|
"fastnoise-lite",
|
||||||
"futures",
|
"futures",
|
||||||
"glam",
|
"glam",
|
||||||
"gpu-compiler-bin-wrapper",
|
"gpu-compiler-bin-wrapper",
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ quote = "1.0"
|
||||||
axum = "0.6"
|
axum = "0.6"
|
||||||
chrono = "^0.4.23"
|
chrono = "^0.4.23"
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
|
fastnoise-lite = "1.1.0"
|
||||||
wgpu-types = "0.17"
|
wgpu-types = "0.17"
|
||||||
wgpu = "0.17"
|
wgpu = "0.17"
|
||||||
wasm-bindgen-futures = { version = "0.4.36" }
|
wasm-bindgen-futures = { version = "0.4.36" }
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@ use graph_craft::ProtoNodeIdentifier;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use graphene_core::application_io::SurfaceHandle;
|
use graphene_core::application_io::SurfaceHandle;
|
||||||
use graphene_core::raster::brush_cache::BrushCache;
|
use graphene_core::raster::brush_cache::BrushCache;
|
||||||
use graphene_core::raster::{BlendMode, Color, Image, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice};
|
use graphene_core::raster::{
|
||||||
|
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, Image, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute,
|
||||||
|
SelectiveColorChoice,
|
||||||
|
};
|
||||||
use graphene_core::text::Font;
|
use graphene_core::text::Font;
|
||||||
use graphene_core::transform::Footprint;
|
use graphene_core::transform::Footprint;
|
||||||
use graphene_core::vector::VectorData;
|
use graphene_core::vector::VectorData;
|
||||||
|
|
@ -567,17 +570,33 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
name: "Pixel Noise",
|
name: "Noise Pattern",
|
||||||
category: "General",
|
category: "General",
|
||||||
implementation: NodeImplementation::proto("graphene_std::raster::PixelNoiseNode<_, _, _>"),
|
implementation: NodeImplementation::proto("graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
DocumentInputType::value("Width", TaggedValue::U32(100), false),
|
DocumentInputType::value("None", TaggedValue::None, false),
|
||||||
DocumentInputType::value("Height", TaggedValue::U32(100), false),
|
// All
|
||||||
|
DocumentInputType::value("Dimensions", TaggedValue::UVec2((512, 512).into()), false),
|
||||||
DocumentInputType::value("Seed", TaggedValue::U32(0), false),
|
DocumentInputType::value("Seed", TaggedValue::U32(0), false),
|
||||||
DocumentInputType::value("Noise Type", TaggedValue::NoiseType(NoiseType::WhiteNoise), false),
|
DocumentInputType::value("Scale", TaggedValue::F32(10.), false),
|
||||||
|
DocumentInputType::value("Noise Type", TaggedValue::NoiseType(NoiseType::Perlin), false),
|
||||||
|
// Domain Warp
|
||||||
|
DocumentInputType::value("Domain Warp Type", TaggedValue::DomainWarpType(DomainWarpType::None), false),
|
||||||
|
DocumentInputType::value("Domain Warp Amplitude", TaggedValue::F32(100.), false),
|
||||||
|
// Fractal
|
||||||
|
DocumentInputType::value("Fractal Type", TaggedValue::FractalType(FractalType::None), false),
|
||||||
|
DocumentInputType::value("Fractal Octaves", TaggedValue::U32(3), false),
|
||||||
|
DocumentInputType::value("Fractal Lacunarity", TaggedValue::F32(2.), false),
|
||||||
|
DocumentInputType::value("Fractal Gain", TaggedValue::F32(0.5), false),
|
||||||
|
DocumentInputType::value("Fractal Weighted Strength", TaggedValue::F32(0.), false), // 0-1 range
|
||||||
|
DocumentInputType::value("Fractal Ping Pong Strength", TaggedValue::F32(2.), false),
|
||||||
|
// Cellular
|
||||||
|
DocumentInputType::value("Cellular Distance Function", TaggedValue::CellularDistanceFunction(CellularDistanceFunction::Euclidean), false),
|
||||||
|
DocumentInputType::value("Cellular Return Type", TaggedValue::CellularReturnType(CellularReturnType::Nearest), false),
|
||||||
|
DocumentInputType::value("Cellular Jitter", TaggedValue::F32(1.), false),
|
||||||
],
|
],
|
||||||
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
|
||||||
properties: node_properties::pixel_noise_properties,
|
properties: node_properties::noise_pattern_properties,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,13 @@ use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||||
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateServerStatus, ImaginateStatus};
|
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateServerStatus, ImaginateStatus};
|
||||||
use graphene_core::memo::IORecord;
|
use graphene_core::memo::IORecord;
|
||||||
use graphene_core::raster::{BlendMode, Color, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice};
|
use graphene_core::raster::{
|
||||||
|
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice,
|
||||||
|
};
|
||||||
use graphene_core::text::Font;
|
use graphene_core::text::Font;
|
||||||
use graphene_core::vector::style::{FillType, GradientType, LineCap, LineJoin};
|
use graphene_core::vector::style::{FillType, GradientType, LineCap, LineJoin};
|
||||||
|
|
||||||
use glam::{DVec2, IVec2};
|
use glam::{DVec2, IVec2, UVec2};
|
||||||
|
|
||||||
pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
||||||
let widget = TextLabel::new(text).widget_holder();
|
let widget = TextLabel::new(text).widget_holder();
|
||||||
|
|
@ -129,57 +131,84 @@ fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
widgets
|
widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, x: &str, y: &str, unit: &str, mut assist: impl FnMut(&mut Vec<WidgetHolder>)) -> LayoutGroup {
|
fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, x: &str, y: &str, unit: &str, min: Option<f64>, mut assist: impl FnMut(&mut Vec<WidgetHolder>)) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Vector, false);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Vector, false);
|
||||||
|
|
||||||
assist(&mut widgets);
|
assist(&mut widgets);
|
||||||
|
|
||||||
if let NodeInput::Value {
|
if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::DVec2(vec2),
|
tagged_value: TaggedValue::DVec2(dvec2),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
} = document_node.inputs[index]
|
} = document_node.inputs[index]
|
||||||
{
|
{
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(vec2.x))
|
NumberInput::new(Some(dvec2.x))
|
||||||
.label(x)
|
.label(x)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(vec2.y))
|
NumberInput::new(Some(dvec2.y))
|
||||||
.label(y)
|
.label(y)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
]);
|
]);
|
||||||
} else if let NodeInput::Value {
|
} else if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::IVec2(vec2),
|
tagged_value: TaggedValue::IVec2(ivec2),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
} = document_node.inputs[index]
|
} = document_node.inputs[index]
|
||||||
{
|
{
|
||||||
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, vec2.y));
|
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, ivec2.y));
|
||||||
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(vec2.x, input.value.unwrap() as i32));
|
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(ivec2.x, input.value.unwrap() as i32));
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(vec2.x as f64))
|
NumberInput::new(Some(ivec2.x as f64))
|
||||||
.int()
|
.int()
|
||||||
.label(x)
|
.label(x)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_x, node_id, index))
|
.on_update(update_value(update_x, node_id, index))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(vec2.y as f64))
|
NumberInput::new(Some(ivec2.y as f64))
|
||||||
.int()
|
.int()
|
||||||
.label(y)
|
.label(y)
|
||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
|
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||||
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.on_update(update_value(update_y, node_id, index))
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
} else if let NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::UVec2(uvec2),
|
||||||
|
exposed: false,
|
||||||
|
} = document_node.inputs[index]
|
||||||
|
{
|
||||||
|
let update_x = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(input.value.unwrap() as u32, uvec2.y));
|
||||||
|
let update_y = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(uvec2.x, input.value.unwrap() as u32));
|
||||||
|
widgets.extend_from_slice(&[
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
NumberInput::new(Some(uvec2.x as f64))
|
||||||
|
.int()
|
||||||
|
.label(x)
|
||||||
|
.unit(unit)
|
||||||
|
.min(min.unwrap_or(0.))
|
||||||
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.on_update(update_value(update_x, node_id, index))
|
||||||
|
.widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
|
NumberInput::new(Some(uvec2.y as f64))
|
||||||
|
.int()
|
||||||
|
.label(y)
|
||||||
|
.unit(unit)
|
||||||
|
.min(min.unwrap_or(0.))
|
||||||
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
.on_update(update_value(update_y, node_id, index))
|
.on_update(update_value(update_y, node_id, index))
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
|
|
@ -323,7 +352,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
widgets
|
widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Use generalized Version of this as soon as it's available
|
//TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn color_channel(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn color_channel(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let &NodeInput::Value {
|
if let &NodeInput::Value {
|
||||||
|
|
@ -346,30 +375,117 @@ fn color_channel(document_node: &DocumentNode, node_id: u64, index: usize, name:
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
|
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Use generalized Version of this as soon as it's available
|
// TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn noise_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn noise_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let &NodeInput::Value {
|
if let &NodeInput::Value {
|
||||||
tagged_value: TaggedValue::NoiseType(calculation),
|
tagged_value: TaggedValue::NoiseType(noise_type),
|
||||||
exposed: false,
|
exposed: false,
|
||||||
} = &document_node.inputs[index]
|
} = &document_node.inputs[index]
|
||||||
{
|
{
|
||||||
let calculation_modes = NoiseType::list();
|
let entries = NoiseType::list()
|
||||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
.iter()
|
||||||
for method in calculation_modes {
|
.map(|noise_type| MenuListEntry::new(noise_type.to_string()).on_update(update_value(move |_| TaggedValue::NoiseType(*noise_type), node_id, index)))
|
||||||
entries.push(MenuListEntry::new(method.to_string()).on_update(update_value(move |_| TaggedValue::NoiseType(method), node_id, index)));
|
.collect();
|
||||||
}
|
|
||||||
let entries = vec![entries];
|
|
||||||
|
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
DropdownInput::new(entries).selected_index(Some(calculation as u32)).widget_holder(),
|
DropdownInput::new(vec![entries]).selected_index(Some(noise_type as u32)).widget_holder(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Type of Noise")
|
LayoutGroup::Row { widgets }.with_tooltip("Style of noise pattern")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use generalized version of this as soon as it's available
|
// TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
|
fn fractal_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::FractalType(fractal_type),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[index]
|
||||||
|
{
|
||||||
|
let entries = FractalType::list()
|
||||||
|
.iter()
|
||||||
|
.map(|fractal_type| MenuListEntry::new(fractal_type.to_string()).on_update(update_value(move |_| TaggedValue::FractalType(*fractal_type), node_id, index)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
widgets.extend_from_slice(&[
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
DropdownInput::new(vec![entries]).selected_index(Some(fractal_type as u32)).disabled(disabled).widget_holder(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
LayoutGroup::Row { widgets }.with_tooltip("Style of layered levels of the noise pattern")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
|
fn cellular_distance_function(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::CellularDistanceFunction(cellular_distance_function),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[index]
|
||||||
|
{
|
||||||
|
let entries = CellularDistanceFunction::list()
|
||||||
|
.iter()
|
||||||
|
.map(|cellular_distance_function| {
|
||||||
|
MenuListEntry::new(cellular_distance_function.to_string()).on_update(update_value(move |_| TaggedValue::CellularDistanceFunction(*cellular_distance_function), node_id, index))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
widgets.extend_from_slice(&[
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
DropdownInput::new(vec![entries])
|
||||||
|
.selected_index(Some(cellular_distance_function as u32))
|
||||||
|
.disabled(disabled)
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
LayoutGroup::Row { widgets }.with_tooltip("Distance function used by the cellular noise")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
|
fn cellular_return_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::CellularReturnType(cellular_return_type),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[index]
|
||||||
|
{
|
||||||
|
let entries = CellularReturnType::list()
|
||||||
|
.iter()
|
||||||
|
.map(|cellular_return_type| MenuListEntry::new(cellular_return_type.to_string()).on_update(update_value(move |_| TaggedValue::CellularReturnType(*cellular_return_type), node_id, index)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
widgets.extend_from_slice(&[
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
DropdownInput::new(vec![entries]).selected_index(Some(cellular_return_type as u32)).disabled(disabled).widget_holder(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
LayoutGroup::Row { widgets }.with_tooltip("Return type of the cellular noise")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Generalize this instead of using a separate function per dropdown menu enum
|
||||||
|
fn domain_warp_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
if let &NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::DomainWarpType(domain_warp_type),
|
||||||
|
exposed: false,
|
||||||
|
} = &document_node.inputs[index]
|
||||||
|
{
|
||||||
|
let entries = DomainWarpType::list()
|
||||||
|
.iter()
|
||||||
|
.map(|domain_warp_type| MenuListEntry::new(domain_warp_type.to_string()).on_update(update_value(move |_| TaggedValue::DomainWarpType(*domain_warp_type), node_id, index)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
widgets.extend_from_slice(&[
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
DropdownInput::new(vec![entries]).selected_index(Some(domain_warp_type as u32)).disabled(disabled).widget_holder(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
LayoutGroup::Row { widgets }.with_tooltip("Type of domain warp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn blend_mode(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn blend_mode(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let &NodeInput::Value {
|
if let &NodeInput::Value {
|
||||||
|
|
@ -803,17 +919,158 @@ pub fn extract_channel_properties(document_node: &DocumentNode, node_id: NodeId,
|
||||||
|
|
||||||
// Noise Type is commented out for now as there is only one type of noise (White Noise).
|
// Noise Type is commented out for now as there is only one type of noise (White Noise).
|
||||||
// As soon as there are more types of noise, this should be uncommented.
|
// As soon as there are more types of noise, this should be uncommented.
|
||||||
pub fn pixel_noise_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let width = number_widget(document_node, node_id, 0, "Width", NumberInput::default().unit("px").min(1.), true);
|
// Get the current values of the inputs of interest so they can set whether certain inputs are disabled based on various conditions.
|
||||||
let height = number_widget(document_node, node_id, 1, "Height", NumberInput::default().unit("px").min(1.), true);
|
let current_noise_type = match &document_node.inputs[4] {
|
||||||
let seed = number_widget(document_node, node_id, 2, "Seed", NumberInput::default().min(0.), true);
|
NodeInput::Value {
|
||||||
let _noise_type = noise_type(document_node, node_id, 3, "Noise Type", true);
|
tagged_value: TaggedValue::NoiseType(noise_type),
|
||||||
|
..
|
||||||
|
} => Some(*noise_type),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let current_domain_warp_type = match &document_node.inputs[5] {
|
||||||
|
NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::DomainWarpType(domain_warp_type),
|
||||||
|
..
|
||||||
|
} => Some(*domain_warp_type),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let current_fractal_type = match &document_node.inputs[7] {
|
||||||
|
NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::FractalType(fractal_type),
|
||||||
|
..
|
||||||
|
} => Some(*fractal_type),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let fractal_active = current_fractal_type != Some(FractalType::None);
|
||||||
|
let coherent_noise_active = current_noise_type != Some(NoiseType::WhiteNoise);
|
||||||
|
let cellular_noise_active = current_noise_type == Some(NoiseType::Cellular);
|
||||||
|
let ping_pong_active = current_fractal_type == Some(FractalType::PingPong);
|
||||||
|
let domain_warp_active = current_domain_warp_type != Some(DomainWarpType::None);
|
||||||
|
let domain_warp_only_fractal_type_wrongly_active =
|
||||||
|
!domain_warp_active && (current_fractal_type == Some(FractalType::DomainWarpIndependent) || current_fractal_type == Some(FractalType::DomainWarpProgressive));
|
||||||
|
|
||||||
|
// All
|
||||||
|
let dimensions = vec2_widget(document_node, node_id, 1, "Dimensions", "W", "H", "px", Some(1.), add_blank_assist);
|
||||||
|
let seed = number_widget(document_node, node_id, 2, "Seed", NumberInput::default().min(0.).is_integer(true), true);
|
||||||
|
let scale = number_widget(document_node, node_id, 3, "Scale", NumberInput::default().min(0.).disabled(!coherent_noise_active), true);
|
||||||
|
let noise_type_row = noise_type(document_node, node_id, 4, "Noise Type", true);
|
||||||
|
|
||||||
|
// Domain Warp
|
||||||
|
let domain_warp_type_row = domain_warp_type(document_node, node_id, 5, "Domain Warp Type", true, !coherent_noise_active);
|
||||||
|
let domain_warp_amplitude = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
6,
|
||||||
|
"Domain Warp Amplitude",
|
||||||
|
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fractal
|
||||||
|
let fractal_type_row = fractal_type(document_node, node_id, 7, "Fractal Type", true, !coherent_noise_active);
|
||||||
|
let fractal_octaves = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
8,
|
||||||
|
"Fractal Octaves",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.min(1.)
|
||||||
|
.max(10.)
|
||||||
|
.range_max(Some(4.))
|
||||||
|
.is_integer(true)
|
||||||
|
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let fractal_lacunarity = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
9,
|
||||||
|
"Fractal Lacunarity",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.min(0.)
|
||||||
|
.range_max(Some(10.))
|
||||||
|
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let fractal_gain = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
10,
|
||||||
|
"Fractal Gain",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.min(0.)
|
||||||
|
.range_max(Some(10.))
|
||||||
|
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let fractal_weighted_strength = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
11,
|
||||||
|
"Fractal Weighted Strength",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.min(0.)
|
||||||
|
.max(1.) // Defined for the 0-1 range
|
||||||
|
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let fractal_ping_pong_strength = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
12,
|
||||||
|
"Fractal Ping Pong Strength",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.min(0.)
|
||||||
|
.range_max(Some(10.))
|
||||||
|
.disabled(!ping_pong_active || !coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cellular
|
||||||
|
let cellular_distance_function_row = cellular_distance_function(document_node, node_id, 13, "Cellular Distance Function", true, !coherent_noise_active || !cellular_noise_active);
|
||||||
|
let cellular_return_type = cellular_return_type(document_node, node_id, 14, "Cellular Return Type", true, !coherent_noise_active || !cellular_noise_active);
|
||||||
|
let cellular_jitter = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
15,
|
||||||
|
"Cellular Jitter",
|
||||||
|
NumberInput::default()
|
||||||
|
.mode_range()
|
||||||
|
.range_min(Some(0.))
|
||||||
|
.range_max(Some(1.))
|
||||||
|
.disabled(!coherent_noise_active || !cellular_noise_active),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
LayoutGroup::Row { widgets: width },
|
// All
|
||||||
LayoutGroup::Row { widgets: height },
|
dimensions,
|
||||||
LayoutGroup::Row { widgets: seed },
|
LayoutGroup::Row { widgets: seed },
|
||||||
//_noise_type
|
LayoutGroup::Row { widgets: scale },
|
||||||
|
noise_type_row,
|
||||||
|
LayoutGroup::Row { widgets: Vec::new() },
|
||||||
|
// Domain Warp
|
||||||
|
domain_warp_type_row,
|
||||||
|
LayoutGroup::Row { widgets: domain_warp_amplitude },
|
||||||
|
LayoutGroup::Row { widgets: Vec::new() },
|
||||||
|
// Fractal
|
||||||
|
fractal_type_row,
|
||||||
|
LayoutGroup::Row { widgets: fractal_octaves },
|
||||||
|
LayoutGroup::Row { widgets: fractal_lacunarity },
|
||||||
|
LayoutGroup::Row { widgets: fractal_gain },
|
||||||
|
LayoutGroup::Row { widgets: fractal_weighted_strength },
|
||||||
|
LayoutGroup::Row { widgets: fractal_ping_pong_strength },
|
||||||
|
LayoutGroup::Row { widgets: Vec::new() },
|
||||||
|
// Cellular
|
||||||
|
cellular_distance_function_row,
|
||||||
|
cellular_return_type,
|
||||||
|
LayoutGroup::Row { widgets: cellular_jitter },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1163,7 +1420,7 @@ pub fn star_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn line_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let operand = |name: &str, index| vec2_widget(document_node, node_id, index, name, "X", "Y", "px", add_blank_assist);
|
let operand = |name: &str, index| vec2_widget(document_node, node_id, index, name, "X", "Y", "px", None, add_blank_assist);
|
||||||
vec![operand("Start", 1), operand("End", 2)]
|
vec![operand("Start", 1), operand("End", 2)]
|
||||||
}
|
}
|
||||||
pub fn spline_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn spline_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
|
@ -1195,7 +1452,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
||||||
add_blank_assist(widgets);
|
add_blank_assist(widgets);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let translation = vec2_widget(document_node, node_id, 1, "Translation", "X", "Y", " px", translation_assist);
|
let translation = vec2_widget(document_node, node_id, 1, "Translation", "X", "Y", " px", None, translation_assist);
|
||||||
|
|
||||||
let rotation = {
|
let rotation = {
|
||||||
let index = 2;
|
let index = 2;
|
||||||
|
|
@ -1226,7 +1483,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
||||||
LayoutGroup::Row { widgets }
|
LayoutGroup::Row { widgets }
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale = vec2_widget(document_node, node_id, 3, "Scale", "W", "H", "x", add_blank_assist);
|
let scale = vec2_widget(document_node, node_id, 3, "Scale", "W", "H", "x", None, add_blank_assist);
|
||||||
|
|
||||||
let vector_data = start_widgets(document_node, node_id, 0, "Data", FrontendGraphDataType::Vector, false);
|
let vector_data = start_widgets(document_node, node_id, 0, "Data", FrontendGraphDataType::Vector, false);
|
||||||
let vector_data = LayoutGroup::Row { widgets: vector_data };
|
let vector_data = LayoutGroup::Row { widgets: vector_data };
|
||||||
|
|
@ -1836,7 +2093,7 @@ pub fn stroke_properties(document_node: &DocumentNode, node_id: NodeId, _context
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repeat_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn repeat_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let direction = vec2_widget(document_node, node_id, 1, "Direction", "X", "Y", " px", add_blank_assist);
|
let direction = vec2_widget(document_node, node_id, 1, "Direction", "X", "Y", " px", None, add_blank_assist);
|
||||||
let count = number_widget(document_node, node_id, 2, "Count", NumberInput::default().min(1.), true);
|
let count = number_widget(document_node, node_id, 2, "Count", NumberInput::default().min(1.), true);
|
||||||
|
|
||||||
vec![direction, LayoutGroup::Row { widgets: count }]
|
vec![direction, LayoutGroup::Row { widgets: count }]
|
||||||
|
|
@ -1895,8 +2152,8 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let location = vec2_widget(document_node, node_id, 1, "Location", "X", "Y", " px", add_blank_assist);
|
let location = vec2_widget(document_node, node_id, 1, "Location", "X", "Y", " px", None, add_blank_assist);
|
||||||
let dimensions = vec2_widget(document_node, node_id, 2, "Dimensions", "W", "H", " px", add_blank_assist);
|
let dimensions = vec2_widget(document_node, node_id, 2, "Dimensions", "W", "H", " px", None, add_blank_assist);
|
||||||
let background = color_widget(document_node, node_id, 3, "Background", ColorButton::default().allow_none(false), true);
|
let background = color_widget(document_node, node_id, 3, "Background", ColorButton::default().allow_none(false), true);
|
||||||
let clip = LayoutGroup::Row {
|
let clip = LayoutGroup::Row {
|
||||||
widgets: bool_widget(document_node, node_id, 4, "Clip", true),
|
widgets: bool_widget(document_node, node_id, 4, "Clip", true),
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
|
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
|
||||||
$: styles = {
|
$: styles = {
|
||||||
...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}),
|
...(minWidth > 0 ? { "min-width": `${minWidth}px` } : {}),
|
||||||
...(mode === "Range" ? { "--progress-factor": (rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin) } : {}),
|
...(mode === "Range" ? { "--progress-factor": Math.min(Math.max((rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin), 0), 1) } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep track of the Ctrl key being held down.
|
// Keep track of the Ctrl key being held down.
|
||||||
|
|
@ -129,7 +129,7 @@
|
||||||
|
|
||||||
// Called internally to update the value indirectly by informing the parent component of the new value,
|
// Called internally to update the value indirectly by informing the parent component of the new value,
|
||||||
// so it can update the prop for this component, finally yielding the value change.
|
// so it can update the prop for this component, finally yielding the value change.
|
||||||
function updateValue(newValue: number | undefined) {
|
function updateValue(newValue: number | undefined): number | undefined {
|
||||||
// Check if the new value is valid, otherwise we use the old value (rounded if it's an integer)
|
// Check if the new value is valid, otherwise we use the old value (rounded if it's an integer)
|
||||||
const oldValue = value !== undefined && isInteger ? Math.round(value) : value;
|
const oldValue = value !== undefined && isInteger ? Math.round(value) : value;
|
||||||
let newValueValidated = newValue !== undefined ? newValue : oldValue;
|
let newValueValidated = newValue !== undefined ? newValue : oldValue;
|
||||||
|
|
@ -138,6 +138,8 @@
|
||||||
if (typeof min === "number" && !Number.isNaN(min)) newValueValidated = Math.max(newValueValidated, min);
|
if (typeof min === "number" && !Number.isNaN(min)) newValueValidated = Math.max(newValueValidated, min);
|
||||||
if (typeof max === "number" && !Number.isNaN(max)) newValueValidated = Math.min(newValueValidated, max);
|
if (typeof max === "number" && !Number.isNaN(max)) newValueValidated = Math.min(newValueValidated, max);
|
||||||
|
|
||||||
|
if (isInteger) newValueValidated = Math.round(newValueValidated);
|
||||||
|
|
||||||
rangeSliderValue = newValueValidated;
|
rangeSliderValue = newValueValidated;
|
||||||
rangeSliderValueAsRendered = newValueValidated;
|
rangeSliderValueAsRendered = newValueValidated;
|
||||||
}
|
}
|
||||||
|
|
@ -145,6 +147,9 @@
|
||||||
text = displayText(newValueValidated);
|
text = displayText(newValueValidated);
|
||||||
|
|
||||||
if (newValue !== undefined) dispatch("value", newValueValidated);
|
if (newValue !== undefined) dispatch("value", newValueValidated);
|
||||||
|
|
||||||
|
// For any caller that needs to know what the value was changed to, we return it here
|
||||||
|
return newValueValidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================
|
// ================
|
||||||
|
|
@ -185,7 +190,10 @@
|
||||||
// The `unFocus()` call at the bottom of this function and in `onTextChangeCanceled()` causes this function to be run again, so this check skips a second run.
|
// The `unFocus()` call at the bottom of this function and in `onTextChangeCanceled()` causes this function to be run again, so this check skips a second run.
|
||||||
if (!editing) return;
|
if (!editing) return;
|
||||||
|
|
||||||
let newValue = evaluateMathExpression(text);
|
// Insert a leading zero before all decimal points lacking a preceding digit, since the library doesn't realize that "point" means "zero point".
|
||||||
|
const textWithLeadingZeroes = text.replaceAll(/(?<=^|[^0-9])\./g, "0."); // Match any "." that is preceded by the start of the string (^) or a non-digit character ([^0-9])
|
||||||
|
|
||||||
|
let newValue = evaluateMathExpression(textWithLeadingZeroes);
|
||||||
if (newValue !== undefined && isNaN(newValue)) newValue = undefined; // Rejects `sqrt(-1)`
|
if (newValue !== undefined && isNaN(newValue)) newValue = undefined; // Rejects `sqrt(-1)`
|
||||||
updateValue(newValue);
|
updateValue(newValue);
|
||||||
|
|
||||||
|
|
@ -321,9 +329,12 @@
|
||||||
cumulativeDragDelta += dragDelta;
|
cumulativeDragDelta += dragDelta;
|
||||||
|
|
||||||
const combined = initialValueBeforeDragging + cumulativeDragDelta;
|
const combined = initialValueBeforeDragging + cumulativeDragDelta;
|
||||||
const combineSnapped = e.ctrlKey || isInteger ? Math.round(combined) : combined;
|
const combineSnapped = e.ctrlKey ? Math.round(combined) : combined;
|
||||||
|
|
||||||
updateValue(combineSnapped);
|
const newValue = updateValue(combineSnapped);
|
||||||
|
|
||||||
|
// If the value was altered within the `updateValue()` call, we need to rectify the cumulative drag delta to account for the change.
|
||||||
|
if (newValue !== undefined) cumulativeDragDelta -= combineSnapped - newValue;
|
||||||
}
|
}
|
||||||
ignoredFirstMovement = true;
|
ignoredFirstMovement = true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -611,20 +611,178 @@ impl core::fmt::Display for RedGreenBlue {
|
||||||
#[cfg_attr(feature = "std", derive(specta::Type))]
|
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
||||||
pub enum NoiseType {
|
pub enum NoiseType {
|
||||||
|
Perlin,
|
||||||
|
OpenSimplex2,
|
||||||
|
OpenSimplex2S,
|
||||||
|
Cellular,
|
||||||
|
ValueCubic,
|
||||||
|
Value,
|
||||||
WhiteNoise,
|
WhiteNoise,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for NoiseType {
|
impl core::fmt::Display for NoiseType {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
NoiseType::Perlin => write!(f, "Perlin"),
|
||||||
|
NoiseType::OpenSimplex2 => write!(f, "OpenSimplex2"),
|
||||||
|
NoiseType::OpenSimplex2S => write!(f, "OpenSimplex2S"),
|
||||||
|
NoiseType::Cellular => write!(f, "Cellular"),
|
||||||
|
NoiseType::ValueCubic => write!(f, "Value Cubic"),
|
||||||
|
NoiseType::Value => write!(f, "Value"),
|
||||||
NoiseType::WhiteNoise => write!(f, "White Noise"),
|
NoiseType::WhiteNoise => write!(f, "White Noise"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseType {
|
impl NoiseType {
|
||||||
pub fn list() -> [NoiseType; 1] {
|
pub fn list() -> &'static [NoiseType; 7] {
|
||||||
[NoiseType::WhiteNoise]
|
&[
|
||||||
|
NoiseType::Perlin,
|
||||||
|
NoiseType::OpenSimplex2,
|
||||||
|
NoiseType::OpenSimplex2S,
|
||||||
|
NoiseType::Cellular,
|
||||||
|
NoiseType::ValueCubic,
|
||||||
|
NoiseType::Value,
|
||||||
|
NoiseType::WhiteNoise,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
||||||
|
pub enum FractalType {
|
||||||
|
None,
|
||||||
|
FBm,
|
||||||
|
Ridged,
|
||||||
|
PingPong,
|
||||||
|
DomainWarpProgressive,
|
||||||
|
DomainWarpIndependent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for FractalType {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FractalType::None => write!(f, "None"),
|
||||||
|
FractalType::FBm => write!(f, "Fractional Brownian Motion"),
|
||||||
|
FractalType::Ridged => write!(f, "Ridged"),
|
||||||
|
FractalType::PingPong => write!(f, "Ping Pong"),
|
||||||
|
FractalType::DomainWarpProgressive => write!(f, "Progressive (Domain Warp Only)"),
|
||||||
|
FractalType::DomainWarpIndependent => write!(f, "Independent (Domain Warp Only)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FractalType {
|
||||||
|
pub fn list() -> &'static [FractalType; 6] {
|
||||||
|
&[
|
||||||
|
FractalType::None,
|
||||||
|
FractalType::FBm,
|
||||||
|
FractalType::Ridged,
|
||||||
|
FractalType::PingPong,
|
||||||
|
FractalType::DomainWarpProgressive,
|
||||||
|
FractalType::DomainWarpIndependent,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
||||||
|
pub enum CellularDistanceFunction {
|
||||||
|
Euclidean,
|
||||||
|
EuclideanSq,
|
||||||
|
Manhattan,
|
||||||
|
Hybrid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for CellularDistanceFunction {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CellularDistanceFunction::Euclidean => write!(f, "Euclidean"),
|
||||||
|
CellularDistanceFunction::EuclideanSq => write!(f, "Euclidean Squared (Faster)"),
|
||||||
|
CellularDistanceFunction::Manhattan => write!(f, "Manhattan"),
|
||||||
|
CellularDistanceFunction::Hybrid => write!(f, "Hybrid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellularDistanceFunction {
|
||||||
|
pub fn list() -> &'static [CellularDistanceFunction; 4] {
|
||||||
|
&[
|
||||||
|
CellularDistanceFunction::Euclidean,
|
||||||
|
CellularDistanceFunction::EuclideanSq,
|
||||||
|
CellularDistanceFunction::Manhattan,
|
||||||
|
CellularDistanceFunction::Hybrid,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
||||||
|
pub enum CellularReturnType {
|
||||||
|
CellValue,
|
||||||
|
Nearest,
|
||||||
|
NextNearest,
|
||||||
|
Average,
|
||||||
|
Difference,
|
||||||
|
Product,
|
||||||
|
Division,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for CellularReturnType {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CellularReturnType::CellValue => write!(f, "Cell Value"),
|
||||||
|
CellularReturnType::Nearest => write!(f, "Nearest (F1)"),
|
||||||
|
CellularReturnType::NextNearest => write!(f, "Next Nearest (F2)"),
|
||||||
|
CellularReturnType::Average => write!(f, "Average (F1 / 2 + F2 / 2)"),
|
||||||
|
CellularReturnType::Difference => write!(f, "Difference (F2 - F1)"),
|
||||||
|
CellularReturnType::Product => write!(f, "Product (F2 * F1 / 2)"),
|
||||||
|
CellularReturnType::Division => write!(f, "Division (F1 / F2)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellularReturnType {
|
||||||
|
pub fn list() -> &'static [CellularReturnType; 7] {
|
||||||
|
&[
|
||||||
|
CellularReturnType::CellValue,
|
||||||
|
CellularReturnType::Nearest,
|
||||||
|
CellularReturnType::NextNearest,
|
||||||
|
CellularReturnType::Average,
|
||||||
|
CellularReturnType::Difference,
|
||||||
|
CellularReturnType::Product,
|
||||||
|
CellularReturnType::Division,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny)]
|
||||||
|
pub enum DomainWarpType {
|
||||||
|
None,
|
||||||
|
OpenSimplex2,
|
||||||
|
OpenSimplex2Reduced,
|
||||||
|
BasicGrid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for DomainWarpType {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
DomainWarpType::None => write!(f, "None"),
|
||||||
|
DomainWarpType::OpenSimplex2 => write!(f, "OpenSimplex2"),
|
||||||
|
DomainWarpType::OpenSimplex2Reduced => write!(f, "OpenSimplex2 Reduced"),
|
||||||
|
DomainWarpType::BasicGrid => write!(f, "Basic Grid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DomainWarpType {
|
||||||
|
pub fn list() -> &'static [DomainWarpType; 4] {
|
||||||
|
&[DomainWarpType::None, DomainWarpType::OpenSimplex2, DomainWarpType::OpenSimplex2Reduced, DomainWarpType::BasicGrid]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use graphene_core::{Color, Node, Type};
|
||||||
|
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
pub use dyn_any::StaticType;
|
pub use dyn_any::StaticType;
|
||||||
pub use glam::{DAffine2, DVec2};
|
pub use glam::{DAffine2, DVec2, UVec2};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
pub use std::sync::Arc;
|
pub use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@ pub enum TaggedValue {
|
||||||
F32(f32),
|
F32(f32),
|
||||||
F64(f64),
|
F64(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
UVec2(UVec2),
|
||||||
DVec2(DVec2),
|
DVec2(DVec2),
|
||||||
OptionalDVec2(Option<DVec2>),
|
OptionalDVec2(Option<DVec2>),
|
||||||
DAffine2(DAffine2),
|
DAffine2(DAffine2),
|
||||||
|
|
@ -45,6 +46,10 @@ pub enum TaggedValue {
|
||||||
VecDVec2(Vec<DVec2>),
|
VecDVec2(Vec<DVec2>),
|
||||||
RedGreenBlue(graphene_core::raster::RedGreenBlue),
|
RedGreenBlue(graphene_core::raster::RedGreenBlue),
|
||||||
NoiseType(graphene_core::raster::NoiseType),
|
NoiseType(graphene_core::raster::NoiseType),
|
||||||
|
FractalType(graphene_core::raster::FractalType),
|
||||||
|
CellularDistanceFunction(graphene_core::raster::CellularDistanceFunction),
|
||||||
|
CellularReturnType(graphene_core::raster::CellularReturnType),
|
||||||
|
DomainWarpType(graphene_core::raster::DomainWarpType),
|
||||||
RelativeAbsolute(graphene_core::raster::RelativeAbsolute),
|
RelativeAbsolute(graphene_core::raster::RelativeAbsolute),
|
||||||
SelectiveColorChoice(graphene_core::raster::SelectiveColorChoice),
|
SelectiveColorChoice(graphene_core::raster::SelectiveColorChoice),
|
||||||
LineCap(graphene_core::vector::style::LineCap),
|
LineCap(graphene_core::vector::style::LineCap),
|
||||||
|
|
@ -76,70 +81,75 @@ impl Hash for TaggedValue {
|
||||||
core::mem::discriminant(self).hash(state);
|
core::mem::discriminant(self).hash(state);
|
||||||
match self {
|
match self {
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
Self::String(s) => s.hash(state),
|
Self::String(x) => x.hash(state),
|
||||||
Self::U32(u) => u.hash(state),
|
Self::U32(x) => x.hash(state),
|
||||||
Self::F32(f) => f.to_bits().hash(state),
|
Self::F32(x) => x.to_bits().hash(state),
|
||||||
Self::F64(f) => f.to_bits().hash(state),
|
Self::F64(x) => x.to_bits().hash(state),
|
||||||
Self::Bool(b) => b.hash(state),
|
Self::Bool(x) => x.hash(state),
|
||||||
Self::DVec2(v) => v.to_array().iter().for_each(|x| x.to_bits().hash(state)),
|
Self::UVec2(x) => x.to_array().iter().for_each(|x| x.hash(state)),
|
||||||
|
Self::DVec2(x) => x.to_array().iter().for_each(|x| x.to_bits().hash(state)),
|
||||||
Self::OptionalDVec2(None) => 0.hash(state),
|
Self::OptionalDVec2(None) => 0.hash(state),
|
||||||
Self::OptionalDVec2(Some(v)) => {
|
Self::OptionalDVec2(Some(x)) => {
|
||||||
1.hash(state);
|
1.hash(state);
|
||||||
Self::DVec2(*v).hash(state)
|
Self::DVec2(*x).hash(state)
|
||||||
}
|
}
|
||||||
Self::DAffine2(m) => m.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)),
|
Self::DAffine2(x) => x.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)),
|
||||||
Self::Image(i) => i.hash(state),
|
Self::Image(x) => x.hash(state),
|
||||||
Self::ImaginateCache(i) => i.hash(state),
|
Self::ImaginateCache(x) => x.hash(state),
|
||||||
Self::Color(c) => c.hash(state),
|
Self::Color(x) => x.hash(state),
|
||||||
Self::Subpaths(s) => s.iter().for_each(|subpath| subpath.hash(state)),
|
Self::Subpaths(x) => x.iter().for_each(|subpath| subpath.hash(state)),
|
||||||
Self::RcSubpath(s) => s.hash(state),
|
Self::RcSubpath(x) => x.hash(state),
|
||||||
Self::BlendMode(b) => b.hash(state),
|
Self::BlendMode(x) => x.hash(state),
|
||||||
Self::LuminanceCalculation(l) => l.hash(state),
|
Self::LuminanceCalculation(x) => x.hash(state),
|
||||||
Self::ImaginateSamplingMethod(m) => m.hash(state),
|
Self::ImaginateSamplingMethod(x) => x.hash(state),
|
||||||
Self::ImaginateMaskStartingFill(f) => f.hash(state),
|
Self::ImaginateMaskStartingFill(x) => x.hash(state),
|
||||||
Self::ImaginateController(s) => s.hash(state),
|
Self::ImaginateController(x) => x.hash(state),
|
||||||
Self::LayerPath(p) => p.hash(state),
|
Self::LayerPath(x) => x.hash(state),
|
||||||
Self::ImageFrame(i) => i.hash(state),
|
Self::ImageFrame(x) => x.hash(state),
|
||||||
Self::VectorData(vector_data) => vector_data.hash(state),
|
Self::VectorData(x) => x.hash(state),
|
||||||
Self::Fill(fill) => fill.hash(state),
|
Self::Fill(x) => x.hash(state),
|
||||||
Self::Stroke(stroke) => stroke.hash(state),
|
Self::Stroke(x) => x.hash(state),
|
||||||
Self::VecF32(vec_f32) => vec_f32.iter().for_each(|val| val.to_bits().hash(state)),
|
Self::VecF32(x) => x.iter().for_each(|val| val.to_bits().hash(state)),
|
||||||
Self::VecDVec2(vec_dvec2) => vec_dvec2.iter().for_each(|val| val.to_array().iter().for_each(|x| x.to_bits().hash(state))),
|
Self::VecDVec2(x) => x.iter().for_each(|val| val.to_array().iter().for_each(|x| x.to_bits().hash(state))),
|
||||||
Self::RedGreenBlue(red_green_blue) => red_green_blue.hash(state),
|
Self::RedGreenBlue(x) => x.hash(state),
|
||||||
Self::NoiseType(noise_type) => noise_type.hash(state),
|
Self::NoiseType(x) => x.hash(state),
|
||||||
Self::RelativeAbsolute(relative_absolute) => relative_absolute.hash(state),
|
Self::FractalType(x) => x.hash(state),
|
||||||
Self::SelectiveColorChoice(selective_color_choice) => selective_color_choice.hash(state),
|
Self::CellularDistanceFunction(x) => x.hash(state),
|
||||||
Self::LineCap(line_cap) => line_cap.hash(state),
|
Self::CellularReturnType(x) => x.hash(state),
|
||||||
Self::LineJoin(line_join) => line_join.hash(state),
|
Self::DomainWarpType(x) => x.hash(state),
|
||||||
Self::FillType(fill_type) => fill_type.hash(state),
|
Self::RelativeAbsolute(x) => x.hash(state),
|
||||||
Self::GradientType(gradient_type) => gradient_type.hash(state),
|
Self::SelectiveColorChoice(x) => x.hash(state),
|
||||||
Self::GradientPositions(gradient_positions) => {
|
Self::LineCap(x) => x.hash(state),
|
||||||
gradient_positions.len().hash(state);
|
Self::LineJoin(x) => x.hash(state),
|
||||||
for (position, color) in gradient_positions {
|
Self::FillType(x) => x.hash(state),
|
||||||
|
Self::GradientType(x) => x.hash(state),
|
||||||
|
Self::GradientPositions(x) => {
|
||||||
|
x.len().hash(state);
|
||||||
|
for (position, color) in x {
|
||||||
position.to_bits().hash(state);
|
position.to_bits().hash(state);
|
||||||
color.hash(state);
|
color.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Quantization(quantized_image) => quantized_image.hash(state),
|
Self::Quantization(x) => x.hash(state),
|
||||||
Self::OptionalColor(color) => color.hash(state),
|
Self::OptionalColor(x) => x.hash(state),
|
||||||
Self::ManipulatorGroupIds(mirror) => mirror.hash(state),
|
Self::ManipulatorGroupIds(x) => x.hash(state),
|
||||||
Self::Font(font) => font.hash(state),
|
Self::Font(x) => x.hash(state),
|
||||||
Self::BrushStrokes(brush_strokes) => brush_strokes.hash(state),
|
Self::BrushStrokes(x) => x.hash(state),
|
||||||
Self::BrushCache(brush_cache) => brush_cache.hash(state),
|
Self::BrushCache(x) => x.hash(state),
|
||||||
Self::Segments(segments) => {
|
Self::Segments(x) => {
|
||||||
for segment in segments {
|
for segment in x {
|
||||||
segment.hash(state)
|
segment.hash(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::DocumentNode(document_node) => document_node.hash(state),
|
Self::DocumentNode(x) => x.hash(state),
|
||||||
Self::GraphicGroup(graphic_group) => graphic_group.hash(state),
|
Self::GraphicGroup(x) => x.hash(state),
|
||||||
Self::Artboard(artboard) => artboard.hash(state),
|
Self::Artboard(x) => x.hash(state),
|
||||||
Self::Curve(curve) => curve.hash(state),
|
Self::Curve(x) => x.hash(state),
|
||||||
Self::IVec2(v) => v.hash(state),
|
Self::IVec2(x) => x.hash(state),
|
||||||
Self::SurfaceFrame(surface_id) => surface_id.hash(state),
|
Self::SurfaceFrame(x) => x.hash(state),
|
||||||
Self::Footprint(footprint) => footprint.hash(state),
|
Self::Footprint(x) => x.hash(state),
|
||||||
Self::RenderOutput(render_output) => render_output.hash(state),
|
Self::RenderOutput(x) => x.hash(state),
|
||||||
Self::Palette(palette) => palette.hash(state),
|
Self::Palette(x) => x.hash(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +164,7 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::F32(x) => Box::new(x),
|
TaggedValue::F32(x) => Box::new(x),
|
||||||
TaggedValue::F64(x) => Box::new(x),
|
TaggedValue::F64(x) => Box::new(x),
|
||||||
TaggedValue::Bool(x) => Box::new(x),
|
TaggedValue::Bool(x) => Box::new(x),
|
||||||
|
TaggedValue::UVec2(x) => Box::new(x),
|
||||||
TaggedValue::DVec2(x) => Box::new(x),
|
TaggedValue::DVec2(x) => Box::new(x),
|
||||||
TaggedValue::OptionalDVec2(x) => Box::new(x),
|
TaggedValue::OptionalDVec2(x) => Box::new(x),
|
||||||
TaggedValue::DAffine2(x) => Box::new(x),
|
TaggedValue::DAffine2(x) => Box::new(x),
|
||||||
|
|
@ -176,6 +187,10 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::VecDVec2(x) => Box::new(x),
|
TaggedValue::VecDVec2(x) => Box::new(x),
|
||||||
TaggedValue::RedGreenBlue(x) => Box::new(x),
|
TaggedValue::RedGreenBlue(x) => Box::new(x),
|
||||||
TaggedValue::NoiseType(x) => Box::new(x),
|
TaggedValue::NoiseType(x) => Box::new(x),
|
||||||
|
TaggedValue::FractalType(x) => Box::new(x),
|
||||||
|
TaggedValue::CellularDistanceFunction(x) => Box::new(x),
|
||||||
|
TaggedValue::CellularReturnType(x) => Box::new(x),
|
||||||
|
TaggedValue::DomainWarpType(x) => Box::new(x),
|
||||||
TaggedValue::RelativeAbsolute(x) => Box::new(x),
|
TaggedValue::RelativeAbsolute(x) => Box::new(x),
|
||||||
TaggedValue::SelectiveColorChoice(x) => Box::new(x),
|
TaggedValue::SelectiveColorChoice(x) => Box::new(x),
|
||||||
TaggedValue::LineCap(x) => Box::new(x),
|
TaggedValue::LineCap(x) => Box::new(x),
|
||||||
|
|
@ -210,8 +225,8 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::F32(x) => x.to_string() + "_f32",
|
TaggedValue::F32(x) => x.to_string() + "_f32",
|
||||||
TaggedValue::F64(x) => x.to_string() + "_f64",
|
TaggedValue::F64(x) => x.to_string() + "_f64",
|
||||||
TaggedValue::Bool(x) => x.to_string(),
|
TaggedValue::Bool(x) => x.to_string(),
|
||||||
TaggedValue::BlendMode(blend_mode) => "BlendMode::".to_string() + &blend_mode.to_string(),
|
TaggedValue::BlendMode(x) => "BlendMode::".to_string() + &x.to_string(),
|
||||||
TaggedValue::Color(color) => format!("Color {color:?}"),
|
TaggedValue::Color(x) => format!("Color {x:?}"),
|
||||||
_ => panic!("Cannot convert to primitive string"),
|
_ => panic!("Cannot convert to primitive string"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -224,6 +239,7 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::F32(_) => concrete!(f32),
|
TaggedValue::F32(_) => concrete!(f32),
|
||||||
TaggedValue::F64(_) => concrete!(f64),
|
TaggedValue::F64(_) => concrete!(f64),
|
||||||
TaggedValue::Bool(_) => concrete!(bool),
|
TaggedValue::Bool(_) => concrete!(bool),
|
||||||
|
TaggedValue::UVec2(_) => concrete!(UVec2),
|
||||||
TaggedValue::DVec2(_) => concrete!(DVec2),
|
TaggedValue::DVec2(_) => concrete!(DVec2),
|
||||||
TaggedValue::OptionalDVec2(_) => concrete!(Option<DVec2>),
|
TaggedValue::OptionalDVec2(_) => concrete!(Option<DVec2>),
|
||||||
TaggedValue::Image(_) => concrete!(graphene_core::raster::Image<Color>),
|
TaggedValue::Image(_) => concrete!(graphene_core::raster::Image<Color>),
|
||||||
|
|
@ -246,6 +262,10 @@ impl<'a> TaggedValue {
|
||||||
TaggedValue::VecDVec2(_) => concrete!(Vec<DVec2>),
|
TaggedValue::VecDVec2(_) => concrete!(Vec<DVec2>),
|
||||||
TaggedValue::RedGreenBlue(_) => concrete!(graphene_core::raster::RedGreenBlue),
|
TaggedValue::RedGreenBlue(_) => concrete!(graphene_core::raster::RedGreenBlue),
|
||||||
TaggedValue::NoiseType(_) => concrete!(graphene_core::raster::NoiseType),
|
TaggedValue::NoiseType(_) => concrete!(graphene_core::raster::NoiseType),
|
||||||
|
TaggedValue::FractalType(_) => concrete!(graphene_core::raster::FractalType),
|
||||||
|
TaggedValue::CellularDistanceFunction(_) => concrete!(graphene_core::raster::CellularDistanceFunction),
|
||||||
|
TaggedValue::CellularReturnType(_) => concrete!(graphene_core::raster::CellularReturnType),
|
||||||
|
TaggedValue::DomainWarpType(_) => concrete!(graphene_core::raster::DomainWarpType),
|
||||||
TaggedValue::RelativeAbsolute(_) => concrete!(graphene_core::raster::RelativeAbsolute),
|
TaggedValue::RelativeAbsolute(_) => concrete!(graphene_core::raster::RelativeAbsolute),
|
||||||
TaggedValue::SelectiveColorChoice(_) => concrete!(graphene_core::raster::SelectiveColorChoice),
|
TaggedValue::SelectiveColorChoice(_) => concrete!(graphene_core::raster::SelectiveColorChoice),
|
||||||
TaggedValue::LineCap(_) => concrete!(graphene_core::vector::style::LineCap),
|
TaggedValue::LineCap(_) => concrete!(graphene_core::vector::style::LineCap),
|
||||||
|
|
@ -283,6 +303,7 @@ impl<'a> TaggedValue {
|
||||||
x if x == TypeId::of::<f32>() => Ok(TaggedValue::F32(*downcast(input).unwrap())),
|
x if x == TypeId::of::<f32>() => Ok(TaggedValue::F32(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<f64>() => Ok(TaggedValue::F64(*downcast(input).unwrap())),
|
x if x == TypeId::of::<f64>() => Ok(TaggedValue::F64(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<bool>() => Ok(TaggedValue::Bool(*downcast(input).unwrap())),
|
x if x == TypeId::of::<bool>() => Ok(TaggedValue::Bool(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<UVec2>() => Ok(TaggedValue::UVec2(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<DVec2>() => Ok(TaggedValue::DVec2(*downcast(input).unwrap())),
|
x if x == TypeId::of::<DVec2>() => Ok(TaggedValue::DVec2(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<Option<DVec2>>() => Ok(TaggedValue::OptionalDVec2(*downcast(input).unwrap())),
|
x if x == TypeId::of::<Option<DVec2>>() => Ok(TaggedValue::OptionalDVec2(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::raster::Image<Color>>() => Ok(TaggedValue::Image(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::raster::Image<Color>>() => Ok(TaggedValue::Image(*downcast(input).unwrap())),
|
||||||
|
|
@ -305,6 +326,10 @@ impl<'a> TaggedValue {
|
||||||
x if x == TypeId::of::<Vec<DVec2>>() => Ok(TaggedValue::VecDVec2(*downcast(input).unwrap())),
|
x if x == TypeId::of::<Vec<DVec2>>() => Ok(TaggedValue::VecDVec2(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::raster::RedGreenBlue>() => Ok(TaggedValue::RedGreenBlue(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::raster::RedGreenBlue>() => Ok(TaggedValue::RedGreenBlue(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::raster::NoiseType>() => Ok(TaggedValue::NoiseType(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::raster::NoiseType>() => Ok(TaggedValue::NoiseType(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<graphene_core::raster::FractalType>() => Ok(TaggedValue::FractalType(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<graphene_core::raster::CellularDistanceFunction>() => Ok(TaggedValue::CellularDistanceFunction(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<graphene_core::raster::CellularReturnType>() => Ok(TaggedValue::CellularReturnType(*downcast(input).unwrap())),
|
||||||
|
x if x == TypeId::of::<graphene_core::raster::DomainWarpType>() => Ok(TaggedValue::DomainWarpType(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::raster::RelativeAbsolute>() => Ok(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::raster::RelativeAbsolute>() => Ok(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::raster::SelectiveColorChoice>() => Ok(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::raster::SelectiveColorChoice>() => Ok(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())),
|
||||||
x if x == TypeId::of::<graphene_core::vector::style::LineCap>() => Ok(TaggedValue::LineCap(*downcast(input).unwrap())),
|
x if x == TypeId::of::<graphene_core::vector::style::LineCap>() => Ok(TaggedValue::LineCap(*downcast(input).unwrap())),
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ resvg = ["dep:resvg"]
|
||||||
wayland = []
|
wayland = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
fastnoise-lite = { workspace = true }
|
||||||
rand = { workspace = true, features = [
|
rand = { workspace = true, features = [
|
||||||
"alloc",
|
"alloc",
|
||||||
"small_rng",
|
"small_rng",
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
|
use crate::wasm_application_io::WasmEditorApi;
|
||||||
|
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
use glam::{DAffine2, DVec2, Vec2};
|
|
||||||
use graph_craft::imaginate_input::{ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
use graph_craft::imaginate_input::{ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
|
||||||
use graph_craft::proto::DynFuture;
|
use graph_craft::proto::DynFuture;
|
||||||
use graphene_core::raster::{Alpha, Bitmap, BitmapMut, BlendMode, BlendNode, Image, ImageFrame, Linear, LinearChannel, Luminance, NoiseType, Pixel, RGBMut, RedGreenBlue, Sample};
|
|
||||||
use graphene_core::transform::{Footprint, Transform};
|
|
||||||
|
|
||||||
use crate::wasm_application_io::WasmEditorApi;
|
|
||||||
use graphene_core::raster::bbox::{AxisAlignedBbox, Bbox};
|
use graphene_core::raster::bbox::{AxisAlignedBbox, Bbox};
|
||||||
|
use graphene_core::raster::{
|
||||||
|
Alpha, Bitmap, BitmapMut, BlendMode, BlendNode, CellularDistanceFunction, CellularReturnType, DomainWarpType, FractalType, Image, ImageFrame, Linear, LinearChannel, Luminance, NoiseType, Pixel,
|
||||||
|
RGBMut, RedGreenBlue, Sample,
|
||||||
|
};
|
||||||
|
use graphene_core::transform::{Footprint, Transform};
|
||||||
use graphene_core::value::CopiedNode;
|
use graphene_core::value::CopiedNode;
|
||||||
use graphene_core::{AlphaBlending, Color, Node};
|
use graphene_core::{AlphaBlending, Color, Node};
|
||||||
|
|
||||||
|
use fastnoise_lite;
|
||||||
|
use glam::{DAffine2, DVec2, UVec2, Vec2};
|
||||||
|
use rand::prelude::*;
|
||||||
|
use rand_chacha::ChaCha8Rng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rand::prelude::*;
|
|
||||||
use rand_chacha::ChaCha8Rng;
|
|
||||||
|
|
||||||
#[derive(Debug, DynAny)]
|
#[derive(Debug, DynAny)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
|
|
@ -192,7 +195,7 @@ pub struct MaskImageNode<P, S, Stencil> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(MaskImageNode<_P, _S>)]
|
#[node_macro::node_fn(MaskImageNode<_P, _S>)]
|
||||||
fn mask_imge<
|
fn mask_image<
|
||||||
// _P is the color of the input image. It must have an alpha channel because that is going to
|
// _P is the color of the input image. It must have an alpha channel because that is going to
|
||||||
// be modified by the mask
|
// be modified by the mask
|
||||||
_P: Copy + Alpha,
|
_P: Copy + Alpha,
|
||||||
|
|
@ -405,7 +408,7 @@ fn extend_image_to_bounds_node(image: ImageFrame<Color>, bounds: DAffine2) -> Im
|
||||||
let new_end = bounds_in_image_space.end.ceil().max(orig_image_scale);
|
let new_end = bounds_in_image_space.end.ceil().max(orig_image_scale);
|
||||||
let new_scale = new_end - new_start;
|
let new_scale = new_end - new_start;
|
||||||
|
|
||||||
// Copy over original image into embiggened 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_img = 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.image.height {
|
for y in 0..image.image.height {
|
||||||
|
|
@ -553,25 +556,155 @@ fn image_frame<_P: Pixel>(image: Image<_P>, transform: DAffine2) -> graphene_cor
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PixelNoiseNode<Height, Seed, NoiseType> {
|
pub struct NoisePatternNode<
|
||||||
height: Height,
|
Dimensions,
|
||||||
|
Seed,
|
||||||
|
Scale,
|
||||||
|
NoiseType,
|
||||||
|
DomainWarpType,
|
||||||
|
DomainWarpAmplitude,
|
||||||
|
FractalType,
|
||||||
|
FractalOctaves,
|
||||||
|
FractalLacunarity,
|
||||||
|
FractalGain,
|
||||||
|
FractalWeightedStrength,
|
||||||
|
FractalPingPongStrength,
|
||||||
|
CellularDistanceFunction,
|
||||||
|
CellularReturnType,
|
||||||
|
CellularJitter,
|
||||||
|
> {
|
||||||
|
dimensions: Dimensions,
|
||||||
seed: Seed,
|
seed: Seed,
|
||||||
|
scale: Scale,
|
||||||
noise_type: NoiseType,
|
noise_type: NoiseType,
|
||||||
|
domain_warp_type: DomainWarpType,
|
||||||
|
domain_warp_amplitude: DomainWarpAmplitude,
|
||||||
|
fractal_type: FractalType,
|
||||||
|
fractal_octaves: FractalOctaves,
|
||||||
|
fractal_lacunarity: FractalLacunarity,
|
||||||
|
fractal_gain: FractalGain,
|
||||||
|
fractal_weighted_strength: FractalWeightedStrength,
|
||||||
|
fractal_ping_pong_strength: FractalPingPongStrength,
|
||||||
|
cellular_distance_function: CellularDistanceFunction,
|
||||||
|
cellular_return_type: CellularReturnType,
|
||||||
|
cellular_jitter: CellularJitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(PixelNoiseNode)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn pixel_noise(width: u32, height: u32, seed: u32, noise_type: NoiseType) -> graphene_core::raster::ImageFrame<Color> {
|
#[node_macro::node_fn(NoisePatternNode)]
|
||||||
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
fn noise_pattern(
|
||||||
|
_no_primary_input: (),
|
||||||
|
dimensions: UVec2,
|
||||||
|
seed: u32,
|
||||||
|
scale: f32,
|
||||||
|
noise_type: NoiseType,
|
||||||
|
domain_warp_type: DomainWarpType,
|
||||||
|
domain_warp_amplitude: f32,
|
||||||
|
fractal_type: FractalType,
|
||||||
|
fractal_octaves: u32,
|
||||||
|
fractal_lacunarity: f32,
|
||||||
|
fractal_gain: f32,
|
||||||
|
fractal_weighted_strength: f32,
|
||||||
|
fractal_ping_pong_strength: f32,
|
||||||
|
cellular_distance_function: CellularDistanceFunction,
|
||||||
|
cellular_return_type: CellularReturnType,
|
||||||
|
cellular_jitter: f32,
|
||||||
|
) -> graphene_core::raster::ImageFrame<Color> {
|
||||||
|
// All
|
||||||
|
let [width, height] = dimensions.to_array();
|
||||||
let mut image = Image::new(width, height, Color::from_luminance(0.5));
|
let mut image = Image::new(width, height, Color::from_luminance(0.5));
|
||||||
|
let mut noise = fastnoise_lite::FastNoiseLite::with_seed(seed as i32);
|
||||||
|
noise.set_frequency(Some(scale / 1000.));
|
||||||
|
|
||||||
|
// Domain Warp
|
||||||
|
let domain_warp_type = match domain_warp_type {
|
||||||
|
DomainWarpType::None => None,
|
||||||
|
DomainWarpType::OpenSimplex2 => Some(fastnoise_lite::DomainWarpType::OpenSimplex2),
|
||||||
|
DomainWarpType::OpenSimplex2Reduced => Some(fastnoise_lite::DomainWarpType::OpenSimplex2Reduced),
|
||||||
|
DomainWarpType::BasicGrid => Some(fastnoise_lite::DomainWarpType::BasicGrid),
|
||||||
|
};
|
||||||
|
let domain_warp_active = domain_warp_type.is_some();
|
||||||
|
noise.set_domain_warp_type(domain_warp_type);
|
||||||
|
noise.set_domain_warp_amp(Some(domain_warp_amplitude));
|
||||||
|
|
||||||
|
// Fractal
|
||||||
|
let noise_type = match noise_type {
|
||||||
|
NoiseType::Perlin => fastnoise_lite::NoiseType::Perlin,
|
||||||
|
NoiseType::OpenSimplex2 => fastnoise_lite::NoiseType::OpenSimplex2,
|
||||||
|
NoiseType::OpenSimplex2S => fastnoise_lite::NoiseType::OpenSimplex2S,
|
||||||
|
NoiseType::Cellular => fastnoise_lite::NoiseType::Cellular,
|
||||||
|
NoiseType::ValueCubic => fastnoise_lite::NoiseType::ValueCubic,
|
||||||
|
NoiseType::Value => fastnoise_lite::NoiseType::Value,
|
||||||
|
NoiseType::WhiteNoise => {
|
||||||
|
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
||||||
|
|
||||||
|
for y in 0..height {
|
||||||
|
for x in 0..width {
|
||||||
|
let pixel = image.get_pixel_mut(x, y).unwrap();
|
||||||
|
let luminance = rng.gen_range(0.0..1.) as f32;
|
||||||
|
*pixel = Color::from_luminance(luminance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImageFrame::<Color> {
|
||||||
|
image,
|
||||||
|
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
|
||||||
|
alpha_blending: AlphaBlending::default(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
noise.set_noise_type(Some(noise_type));
|
||||||
|
let fractal_type = match fractal_type {
|
||||||
|
FractalType::None => fastnoise_lite::FractalType::None,
|
||||||
|
FractalType::FBm => fastnoise_lite::FractalType::FBm,
|
||||||
|
FractalType::Ridged => fastnoise_lite::FractalType::Ridged,
|
||||||
|
FractalType::PingPong => fastnoise_lite::FractalType::PingPong,
|
||||||
|
FractalType::DomainWarpProgressive => fastnoise_lite::FractalType::DomainWarpProgressive,
|
||||||
|
FractalType::DomainWarpIndependent => fastnoise_lite::FractalType::DomainWarpIndependent,
|
||||||
|
};
|
||||||
|
noise.set_fractal_type(Some(fractal_type));
|
||||||
|
noise.set_fractal_octaves(Some(fractal_octaves as i32));
|
||||||
|
noise.set_fractal_lacunarity(Some(fractal_lacunarity));
|
||||||
|
noise.set_fractal_gain(Some(fractal_gain));
|
||||||
|
noise.set_fractal_weighted_strength(Some(fractal_weighted_strength));
|
||||||
|
noise.set_fractal_ping_pong_strength(Some(fractal_ping_pong_strength));
|
||||||
|
|
||||||
|
// Cellular
|
||||||
|
let cellular_distance_function = match cellular_distance_function {
|
||||||
|
CellularDistanceFunction::Euclidean => fastnoise_lite::CellularDistanceFunction::Euclidean,
|
||||||
|
CellularDistanceFunction::EuclideanSq => fastnoise_lite::CellularDistanceFunction::EuclideanSq,
|
||||||
|
CellularDistanceFunction::Manhattan => fastnoise_lite::CellularDistanceFunction::Manhattan,
|
||||||
|
CellularDistanceFunction::Hybrid => fastnoise_lite::CellularDistanceFunction::Hybrid,
|
||||||
|
};
|
||||||
|
let cellular_return_type = match cellular_return_type {
|
||||||
|
CellularReturnType::CellValue => fastnoise_lite::CellularReturnType::CellValue,
|
||||||
|
CellularReturnType::Nearest => fastnoise_lite::CellularReturnType::Distance,
|
||||||
|
CellularReturnType::NextNearest => fastnoise_lite::CellularReturnType::Distance2,
|
||||||
|
CellularReturnType::Average => fastnoise_lite::CellularReturnType::Distance2Add,
|
||||||
|
CellularReturnType::Difference => fastnoise_lite::CellularReturnType::Distance2Sub,
|
||||||
|
CellularReturnType::Product => fastnoise_lite::CellularReturnType::Distance2Mul,
|
||||||
|
CellularReturnType::Division => fastnoise_lite::CellularReturnType::Distance2Div,
|
||||||
|
};
|
||||||
|
noise.set_cellular_distance_function(Some(cellular_distance_function));
|
||||||
|
noise.set_cellular_return_type(Some(cellular_return_type));
|
||||||
|
noise.set_cellular_jitter(Some(cellular_jitter));
|
||||||
|
|
||||||
|
// Calculate the noise for every pixel
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
let pixel = image.get_pixel_mut(x, y).unwrap();
|
let pixel = image.get_pixel_mut(x, y).unwrap();
|
||||||
let luminance = match noise_type {
|
|
||||||
NoiseType::WhiteNoise => rng.gen_range(0.0..1.0) as f32,
|
let (mut x, mut y) = (x as f32, y as f32);
|
||||||
};
|
if domain_warp_active && domain_warp_amplitude > 0. {
|
||||||
|
(x, y) = noise.domain_warp_2d(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
let luminance = (noise.get_noise_2d(x, y) + 1.) * 0.5;
|
||||||
*pixel = Color::from_luminance(luminance);
|
*pixel = Color::from_luminance(luminance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the coherent noise image
|
||||||
ImageFrame::<Color> {
|
ImageFrame::<Color> {
|
||||||
image,
|
image,
|
||||||
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
|
transform: DAffine2::from_scale(DVec2::new(width as f64, height as f64)),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use graphene_std::wasm_application_io::WasmEditorApi;
|
||||||
use wgpu_executor::WgpuExecutor;
|
use wgpu_executor::WgpuExecutor;
|
||||||
|
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2, UVec2};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -655,7 +655,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: RenderOutput, params: [RenderOutput]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: RenderOutput, params: [RenderOutput]),
|
||||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image<Color>, params: [&str]),
|
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image<Color>, params: [&str]),
|
||||||
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
|
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
|
||||||
register_node!(graphene_std::raster::PixelNoiseNode<_, _, _>, input: u32, params: [u32, u32, NoiseType]),
|
register_node!(graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>, input: (), params: [UVec2, u32, f32, NoiseType, DomainWarpType, f32, FractalType, u32, f32, f32, f32, f32, CellularDistanceFunction, CellularReturnType, f32]),
|
||||||
#[cfg(feature = "quantization")]
|
#[cfg(feature = "quantization")]
|
||||||
register_node!(graphene_std::quantization::GenerateQuantizationNode<_, _>, input: ImageFrame<Color>, params: [u32, u32]),
|
register_node!(graphene_std::quantization::GenerateQuantizationNode<_, _>, input: ImageFrame<Color>, params: [u32, u32]),
|
||||||
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
register_node!(graphene_core::quantization::QuantizeNode<_>, input: Color, params: [QuantizationChannels]),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue