Memoize hashing (#1876)
* Implement memoization wrapper for hashing * Fix pattern matching errors * Revert proper point modification hash calculiton * Remove unused hashing code * Code review and bug fixes * Improve pattern matching * Fix tests --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
44ffb635e9
commit
a6af5d4831
|
|
@ -1631,11 +1631,7 @@ impl DocumentMessageHandler {
|
|||
let mut fonts = HashSet::new();
|
||||
for (_node_id, node) in self.network.recursive_nodes() {
|
||||
for input in &node.inputs {
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Font(font),
|
||||
..
|
||||
} = input
|
||||
{
|
||||
if let Some(TaggedValue::Font(font)) = input.as_value() {
|
||||
fonts.insert(font.clone());
|
||||
}
|
||||
}
|
||||
|
|
@ -2134,7 +2130,7 @@ fn root_network() -> NodeNetwork {
|
|||
{
|
||||
NodeNetwork {
|
||||
exports: vec![NodeInput::Value {
|
||||
tagged_value: TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY),
|
||||
tagged_value: TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY).into(),
|
||||
exposed: true,
|
||||
}],
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -70,56 +70,24 @@ impl LayerBounds {
|
|||
|
||||
/// Get the current affine transform from the transform node's inputs
|
||||
pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
||||
let translation = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(translation),
|
||||
..
|
||||
} = inputs[1]
|
||||
{
|
||||
let translation = if let Some(&TaggedValue::DVec2(translation)) = inputs[1].as_value() {
|
||||
translation
|
||||
} else {
|
||||
DVec2::ZERO
|
||||
};
|
||||
|
||||
let angle = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(angle),
|
||||
..
|
||||
} = inputs[2]
|
||||
{
|
||||
angle
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
let angle = if let Some(&TaggedValue::F64(angle)) = inputs[2].as_value() { angle } else { 0. };
|
||||
|
||||
let scale = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(scale),
|
||||
..
|
||||
} = inputs[3]
|
||||
{
|
||||
scale
|
||||
} else {
|
||||
DVec2::ONE
|
||||
};
|
||||
let scale = if let Some(&TaggedValue::DVec2(scale)) = inputs[3].as_value() { scale } else { DVec2::ONE };
|
||||
|
||||
let shear = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(shear),
|
||||
..
|
||||
} = inputs[4]
|
||||
{
|
||||
shear
|
||||
} else {
|
||||
DVec2::ZERO
|
||||
};
|
||||
let shear = if let Some(&TaggedValue::DVec2(shear)) = inputs[4].as_value() { shear } else { DVec2::ZERO };
|
||||
|
||||
DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
|
||||
}
|
||||
|
||||
/// Extract the current normalized pivot from the layer
|
||||
pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(pivot),
|
||||
..
|
||||
} = inputs[5]
|
||||
{
|
||||
if let Some(&TaggedValue::DVec2(pivot)) = inputs[5].as_value() {
|
||||
pivot
|
||||
} else {
|
||||
DVec2::splat(0.5)
|
||||
|
|
|
|||
|
|
@ -657,14 +657,12 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
|
||||
pub fn vector_modify(&mut self, modification_type: VectorModificationType) {
|
||||
self.modify_inputs("Path", false, |inputs, _node_id, _metadata| {
|
||||
let [_, NodeInput::Value {
|
||||
tagged_value: TaggedValue::VectorModification(modification),
|
||||
..
|
||||
}] = inputs.as_mut_slice()
|
||||
else {
|
||||
let Some(NodeInput::Value { tagged_value, .. }) = inputs.iter_mut().skip(1).next() else {
|
||||
panic!("Path node does not have modification input");
|
||||
};
|
||||
let TaggedValue::VectorModification(modification) = &mut *tagged_value.inner_mut() else {
|
||||
panic!("Path node does not have modification input");
|
||||
};
|
||||
|
||||
modification.modify(&modification_type);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ impl DocumentInputType {
|
|||
Self { name, data_type, default }
|
||||
}
|
||||
|
||||
pub const fn none() -> Self {
|
||||
pub fn none() -> Self {
|
||||
Self {
|
||||
name: "None",
|
||||
data_type: FrontendGraphDataType::General,
|
||||
|
|
|
|||
|
|
@ -1189,7 +1189,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
return;
|
||||
};
|
||||
if let Some(node) = network.nodes.get(&node_id) {
|
||||
let input = NodeInput::Value { tagged_value: value, exposed: false };
|
||||
let input = NodeInput::value(value, false);
|
||||
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
if (node.name != "Imaginate" || input_index == 0) && network.connected_to_output(node_id) {
|
||||
|
|
@ -1214,7 +1214,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
if input_index >= node.inputs.len() {
|
||||
node.inputs.extend(((node.inputs.len() - 1)..input_index).map(|_| NodeInput::network(generic!(T), 0)));
|
||||
}
|
||||
node.inputs[input_index] = NodeInput::Value { tagged_value: value, exposed: false };
|
||||
node.inputs[input_index] = NodeInput::value(value, false);
|
||||
if network.connected_to_output(node_id) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,7 @@ fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::String(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::String(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextInput::new(x.clone())
|
||||
|
|
@ -110,11 +106,7 @@ fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::String(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::String(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextAreaInput::new(x.clone())
|
||||
|
|
@ -129,14 +121,10 @@ fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Bool(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::Bool(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(*x)
|
||||
CheckboxInput::new(x)
|
||||
.on_update(update_value(|x: &CheckboxInput| TaggedValue::Bool(x.checked), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
|
|
@ -157,12 +145,7 @@ fn footprint_widget(document_node: &DocumentNode, node_id: NodeId, index: usize)
|
|||
add_blank_assist(&mut resolution_widgets);
|
||||
resolution_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Footprint(footprint),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
let footprint = *footprint;
|
||||
if let Some(&TaggedValue::Footprint(footprint)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let top_left = footprint.transform.transform_point2(DVec2::ZERO);
|
||||
let bounds = footprint.scale();
|
||||
let oversample = footprint.resolution.as_dvec2() / bounds;
|
||||
|
|
@ -292,11 +275,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
|
||||
assist(&mut widgets);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(dvec2),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::DVec2(dvec2)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
NumberInput::new(Some(dvec2.x))
|
||||
|
|
@ -317,11 +296,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
} else if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::IVec2(ivec2),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
} else if let Some(&TaggedValue::IVec2(ivec2)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
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(ivec2.x, input.value.unwrap() as i32));
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -346,11 +321,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
} else if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::UVec2(uvec2),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
} else if let Some(&TaggedValue::UVec2(uvec2)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
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(&[
|
||||
|
|
@ -393,11 +364,7 @@ fn vec_f64_input(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
.map(TaggedValue::VecF64)
|
||||
};
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::VecF64(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::VecF64(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
text_props
|
||||
|
|
@ -422,11 +389,7 @@ fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
.map(TaggedValue::VecDVec2)
|
||||
};
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::VecDVec2(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::VecDVec2(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
text_props
|
||||
|
|
@ -444,11 +407,7 @@ fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
|
||||
let from_font_input = |font: &FontInput| TaggedValue::Font(Font::new(font.font_family.clone(), font.font_style.clone()));
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Font(font),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::Font(font)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
first_widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
FontInput::new(font.font_family.clone(), font.font_style.clone())
|
||||
|
|
@ -484,11 +443,7 @@ fn vector_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, number_props: NumberInput, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::F64(x)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
number_props
|
||||
|
|
@ -497,11 +452,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::U32(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
} else if let Some(&TaggedValue::U32(x)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
number_props
|
||||
|
|
@ -517,11 +468,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::RedGreenBlue(mode),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::RedGreenBlue(mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let calculation_modes = [RedGreenBlue::Red, RedGreenBlue::Green, RedGreenBlue::Blue];
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
|
|
@ -544,11 +491,7 @@ fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
|
||||
fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::RedGreenBlueAlpha(mode),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::RedGreenBlueAlpha(mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let calculation_modes = [RedGreenBlueAlpha::Red, RedGreenBlueAlpha::Green, RedGreenBlueAlpha::Blue, RedGreenBlueAlpha::Alpha];
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
|
|
@ -572,11 +515,7 @@ fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::NoiseType(noise_type),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::NoiseType(noise_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = NoiseType::list()
|
||||
.iter()
|
||||
.map(|noise_type| {
|
||||
|
|
@ -598,11 +537,7 @@ fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn fractal_type(document_node: &DocumentNode, node_id: NodeId, 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]
|
||||
{
|
||||
if let Some(&TaggedValue::FractalType(fractal_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = FractalType::list()
|
||||
.iter()
|
||||
.map(|fractal_type| {
|
||||
|
|
@ -624,11 +559,7 @@ fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, 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]
|
||||
{
|
||||
if let Some(&TaggedValue::CellularDistanceFunction(cellular_distance_function)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = CellularDistanceFunction::list()
|
||||
.iter()
|
||||
.map(|cellular_distance_function| {
|
||||
|
|
@ -653,11 +584,7 @@ fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, ind
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, 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]
|
||||
{
|
||||
if let Some(&TaggedValue::CellularReturnType(cellular_return_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = CellularReturnType::list()
|
||||
.iter()
|
||||
.map(|cellular_return_type| {
|
||||
|
|
@ -679,11 +606,7 @@ fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: us
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, 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]
|
||||
{
|
||||
if let Some(&TaggedValue::DomainWarpType(domain_warp_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = DomainWarpType::list()
|
||||
.iter()
|
||||
.map(|domain_warp_type| {
|
||||
|
|
@ -705,11 +628,7 @@ fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||
fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::BlendMode(blend_mode),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::BlendMode(blend_mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = BlendMode::list_svg_subset()
|
||||
.iter()
|
||||
.map(|category| {
|
||||
|
|
@ -738,11 +657,7 @@ fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
|||
// TODO: Generalize this for all dropdowns (also see blend_mode and channel_extration)
|
||||
fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::LuminanceCalculation(calculation),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::LuminanceCalculation(calculation)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let calculation_modes = LuminanceCalculation::list();
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
|
|
@ -766,11 +681,7 @@ fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: u
|
|||
fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::BooleanOperation(calculation),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::BooleanOperation(calculation)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let operations = BooleanOperation::list();
|
||||
let icons = BooleanOperation::icons();
|
||||
let mut entries = Vec::with_capacity(operations.len());
|
||||
|
|
@ -795,11 +706,7 @@ fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId
|
|||
|
||||
fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::LineCap(line_cap),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::LineCap(line_cap)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = [("Butt", LineCap::Butt), ("Round", LineCap::Round), ("Square", LineCap::Square)]
|
||||
.into_iter()
|
||||
.map(|(name, val)| {
|
||||
|
|
@ -820,11 +727,7 @@ fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
|
||||
fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::LineJoin(line_join),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::LineJoin(line_join)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = [("Miter", LineJoin::Miter), ("Bevel", LineJoin::Bevel), ("Round", LineJoin::Round)]
|
||||
.into_iter()
|
||||
.map(|(name, val)| {
|
||||
|
|
@ -853,7 +756,7 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
|
||||
match tagged_value {
|
||||
match &**tagged_value {
|
||||
TaggedValue::Color(color) => widgets.push(
|
||||
color_props
|
||||
.value(FillChoice::Solid(*color))
|
||||
|
|
@ -891,11 +794,7 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Curve(curve),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(TaggedValue::Curve(curve)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CurveInput::new(curve.clone())
|
||||
|
|
@ -909,11 +808,7 @@ fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
|
||||
fn centroid_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, "Centroid Type", FrontendGraphDataType::General, true);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::CentroidType(centroid_type),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::CentroidType(centroid_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
||||
let entries = vec![
|
||||
RadioEntryData::new("area")
|
||||
.label("Area")
|
||||
|
|
@ -1054,25 +949,16 @@ pub fn extract_channel_properties(document_node: &DocumentNode, node_id: NodeId,
|
|||
// As soon as there are more types of noise, this should be uncommented.
|
||||
pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
// Get the current values of the inputs of interest so they can set whether certain inputs are disabled based on various conditions.
|
||||
let current_noise_type = match &document_node.inputs[4] {
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::NoiseType(noise_type),
|
||||
..
|
||||
} => Some(*noise_type),
|
||||
let current_noise_type = match &document_node.inputs[4].as_value() {
|
||||
Some(&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),
|
||||
let current_domain_warp_type = match &document_node.inputs[5].as_value() {
|
||||
Some(&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),
|
||||
let current_fractal_type = match &document_node.inputs[7].as_value() {
|
||||
Some(&TaggedValue::FractalType(fractal_type)) => Some(fractal_type),
|
||||
_ => None,
|
||||
};
|
||||
let fractal_active = current_fractal_type != Some(FractalType::None);
|
||||
|
|
@ -1262,11 +1148,7 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
|||
// Monochrome
|
||||
let monochrome_index = 1;
|
||||
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", true);
|
||||
let is_monochrome = if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::Bool(monochrome_choice),
|
||||
..
|
||||
} = &document_node.inputs[monochrome_index]
|
||||
{
|
||||
let is_monochrome = if let Some(&TaggedValue::Bool(monochrome_choice)) = &document_node.inputs[monochrome_index].as_value() {
|
||||
monochrome_choice
|
||||
} else {
|
||||
false
|
||||
|
|
@ -1276,11 +1158,7 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
|||
let output_channel_index = 18;
|
||||
let mut output_channel = vec![TextLabel::new("Output Channel").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
add_blank_assist(&mut output_channel);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::RedGreenBlue(choice),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[output_channel_index]
|
||||
{
|
||||
if let Some(&TaggedValue::RedGreenBlue(choice)) = &document_node.inputs[output_channel_index].as_non_exposed_value() {
|
||||
let entries = vec![
|
||||
RadioEntryData::new(format!("{:?}", RedGreenBlue::Red))
|
||||
.label(RedGreenBlue::Red.to_string())
|
||||
|
|
@ -1297,11 +1175,7 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
|||
];
|
||||
output_channel.extend([RadioInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||
};
|
||||
let is_output_channel = if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::RedGreenBlue(choice),
|
||||
..
|
||||
} = &document_node.inputs[output_channel_index]
|
||||
{
|
||||
let is_output_channel = if let Some(&TaggedValue::RedGreenBlue(choice)) = &document_node.inputs[output_channel_index].as_value() {
|
||||
choice
|
||||
} else {
|
||||
warn!("Channel Mixer node properties panel could not be displayed.");
|
||||
|
|
@ -1369,11 +1243,7 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
|
|||
let colors_index = 38;
|
||||
let mut colors = vec![TextLabel::new("Colors").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
add_blank_assist(&mut colors);
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::SelectiveColorChoice(choice),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[colors_index]
|
||||
{
|
||||
if let Some(&TaggedValue::SelectiveColorChoice(choice)) = &document_node.inputs[colors_index].as_non_exposed_value() {
|
||||
use SelectiveColorChoice::*;
|
||||
let entries = [[Reds, Yellows, Greens, Cyans, Blues, Magentas].as_slice(), [Whites, Neutrals, Blacks].as_slice()]
|
||||
.into_iter()
|
||||
|
|
@ -1391,11 +1261,7 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
|
|||
.collect();
|
||||
colors.extend([DropdownInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||
};
|
||||
let colors_choice_index = if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::SelectiveColorChoice(choice),
|
||||
..
|
||||
} = &document_node.inputs[colors_index]
|
||||
{
|
||||
let colors_choice_index = if let Some(&TaggedValue::SelectiveColorChoice(choice)) = &document_node.inputs[colors_index].as_value() {
|
||||
choice
|
||||
} else {
|
||||
warn!("Selective Color node properties panel could not be displayed.");
|
||||
|
|
@ -1423,11 +1289,7 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
|
|||
let mode_index = 1;
|
||||
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", FrontendGraphDataType::General, true);
|
||||
mode.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::RelativeAbsolute(relative_or_absolute),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[mode_index]
|
||||
{
|
||||
if let Some(&TaggedValue::RelativeAbsolute(relative_or_absolute)) = &document_node.inputs[mode_index].as_non_exposed_value() {
|
||||
let entries = vec![
|
||||
RadioEntryData::new("relative")
|
||||
.label("Relative")
|
||||
|
|
@ -1597,32 +1459,16 @@ pub fn rectangle_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
corner_radius_row_2.push(TextLabel::new("").widget_holder());
|
||||
add_blank_assist(&mut corner_radius_row_2);
|
||||
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::Bool(is_individual),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[corner_rounding_type_index]
|
||||
{
|
||||
if let Some(&TaggedValue::Bool(is_individual)) = &document_node.inputs[corner_rounding_type_index].as_non_exposed_value() {
|
||||
// Values
|
||||
let uniform_val = match document_node.inputs[corner_radius_index] {
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(x),
|
||||
exposed: false,
|
||||
} => x,
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64Array4(x),
|
||||
exposed: false,
|
||||
} => x[0],
|
||||
let uniform_val = match document_node.inputs[corner_radius_index].as_non_exposed_value() {
|
||||
Some(TaggedValue::F64(x)) => *x,
|
||||
Some(TaggedValue::F64Array4(x)) => x[0],
|
||||
_ => 0.,
|
||||
};
|
||||
let individual_val = match document_node.inputs[corner_radius_index] {
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64Array4(x),
|
||||
exposed: false,
|
||||
} => x,
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(x),
|
||||
exposed: false,
|
||||
} => [x; 4],
|
||||
let individual_val = match document_node.inputs[corner_radius_index].as_non_exposed_value() {
|
||||
Some(&TaggedValue::F64Array4(x)) => x,
|
||||
Some(&TaggedValue::F64(x)) => [x; 4],
|
||||
_ => [0.; 4],
|
||||
};
|
||||
|
||||
|
|
@ -1747,11 +1593,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
|
||||
let mut widgets = start_widgets(document_node, node_id, index, "Rotation", FrontendGraphDataType::Number, true);
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(val),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
if let Some(&TaggedValue::F64(val)) = document_node.inputs[index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
NumberInput::new(Some(val.to_degrees()))
|
||||
|
|
@ -1846,20 +1688,12 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
||||
};
|
||||
|
||||
let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::ImaginateController(ref controller),
|
||||
..
|
||||
} = controller
|
||||
else {
|
||||
let Some(TaggedValue::ImaginateController(controller)) = controller.as_value() else {
|
||||
panic!("Invalid output status input")
|
||||
};
|
||||
let imaginate_status = controller.get_status();
|
||||
|
||||
let use_base_image = if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::Bool(use_base_image),
|
||||
..
|
||||
} = &document_node.inputs[base_img_index]
|
||||
{
|
||||
let use_base_image = if let Some(&TaggedValue::Bool(use_base_image)) = &document_node.inputs[base_img_index].as_value() {
|
||||
use_base_image
|
||||
} else {
|
||||
true
|
||||
|
|
@ -1958,11 +1792,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
let seed = {
|
||||
let mut widgets = start_widgets(document_node, node_id, seed_index, "Seed", FrontendGraphDataType::Number, false);
|
||||
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(seed),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[seed_index]
|
||||
{
|
||||
if let Some(&TaggedValue::F64(seed)) = &document_node.inputs[seed_index].as_non_exposed_value() {
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
IconButton::new("Regenerate", 24)
|
||||
|
|
@ -2029,11 +1859,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
DVec2::new(x as f64, y as f64)
|
||||
};
|
||||
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::OptionalDVec2(vec2),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[resolution_index]
|
||||
{
|
||||
if let Some(&TaggedValue::OptionalDVec2(vec2)) = &document_node.inputs[resolution_index].as_non_exposed_value() {
|
||||
let dimensions_is_auto = vec2.is_none();
|
||||
let vec2 = vec2.unwrap_or_else(|| round((image_size.0 as f64, image_size.1 as f64).into()));
|
||||
|
||||
|
|
@ -2112,11 +1938,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
let sampling_method = {
|
||||
let mut widgets = start_widgets(document_node, node_id, sampling_method_index, "Sampling Method", FrontendGraphDataType::General, true);
|
||||
|
||||
if let &NodeInput::Value {
|
||||
tagged_value: TaggedValue::ImaginateSamplingMethod(sampling_method),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[sampling_method_index]
|
||||
{
|
||||
if let Some(&TaggedValue::ImaginateSamplingMethod(sampling_method)) = &document_node.inputs[sampling_method_index].as_non_exposed_value() {
|
||||
let sampling_methods = ImaginateSamplingMethod::list();
|
||||
let mut entries = Vec::with_capacity(sampling_methods.len());
|
||||
for method in sampling_methods {
|
||||
|
|
@ -2197,10 +2019,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
// let in_paint = {
|
||||
// let mut widgets = start_widgets(document_node, node_id, inpaint_index, "Inpaint", FrontendGraphDataType::Boolean, true);
|
||||
|
||||
// if let &NodeInput::Value {
|
||||
// tagged_value: TaggedValue::Bool(in_paint),
|
||||
// exposed: false,
|
||||
// } = &document_node.inputs[inpaint_index]
|
||||
// if let Some(& TaggedValue::Bool(in_paint)
|
||||
//)/ } = &document_node.inputs[inpaint_index].as_non_exposed_value()
|
||||
// {
|
||||
// widgets.extend_from_slice(&[
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
|
|
@ -2232,10 +2052,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
// let mask_starting_fill = {
|
||||
// let mut widgets = start_widgets(document_node, node_id, mask_fill_index, "Mask Starting Fill", FrontendGraphDataType::General, true);
|
||||
|
||||
// if let &NodeInput::Value {
|
||||
// tagged_value: TaggedValue::ImaginateMaskStartingFill(starting_fill),
|
||||
// exposed: false,
|
||||
// } = &document_node.inputs[mask_fill_index]
|
||||
// if let Some(& TaggedValue::ImaginateMaskStartingFill(starting_fill)
|
||||
//)/ } = &document_node.inputs[mask_fill_index].as_non_exposed_value()
|
||||
// {
|
||||
// let mask_fill_content_modes = ImaginateMaskStartingFill::list();
|
||||
// let mut entries = Vec::with_capacity(mask_fill_content_modes.len());
|
||||
|
|
@ -2450,30 +2268,17 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||
|
||||
let mut widgets_first_row = start_widgets(document_node, node_id, fill_index, "Fill", FrontendGraphDataType::General, true);
|
||||
|
||||
let (fill, backup_color, backup_gradient) = if let (
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::Fill(fill),
|
||||
..
|
||||
},
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::OptionalColor(backup_color),
|
||||
..
|
||||
},
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::Gradient(backup_gradient),
|
||||
..
|
||||
},
|
||||
) = (
|
||||
&document_node.inputs[fill_index],
|
||||
&document_node.inputs[backup_color_index],
|
||||
&document_node.inputs[backup_gradient_index],
|
||||
let (fill, backup_color, backup_gradient) = if let (Some(TaggedValue::Fill(fill)), Some(&TaggedValue::OptionalColor(backup_color)), Some(TaggedValue::Gradient(backup_gradient))) = (
|
||||
&document_node.inputs[fill_index].as_value(),
|
||||
&document_node.inputs[backup_color_index].as_value(),
|
||||
&document_node.inputs[backup_gradient_index].as_value(),
|
||||
) {
|
||||
(fill, backup_color, backup_gradient)
|
||||
} else {
|
||||
return vec![LayoutGroup::Row { widgets: widgets_first_row }];
|
||||
};
|
||||
let fill2 = fill.clone();
|
||||
let backup_color_fill: Fill = (*backup_color).into();
|
||||
let backup_color_fill: Fill = backup_color.into();
|
||||
let backup_gradient_fill: Fill = backup_gradient.clone().into();
|
||||
|
||||
widgets_first_row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
|
|
|
|||
|
|
@ -129,29 +129,10 @@ pub fn get_text_id(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -
|
|||
/// Gets properties from the Text node
|
||||
pub fn get_text(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<(&String, &Font, f64)> {
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Text")?;
|
||||
let NodeInput::Value {
|
||||
tagged_value: TaggedValue::String(text),
|
||||
..
|
||||
} = &inputs[1]
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Font(font),
|
||||
..
|
||||
} = &inputs[2]
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(font_size),
|
||||
..
|
||||
} = inputs[3]
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
|
||||
let Some(TaggedValue::Font(font)) = &inputs[2].as_value() else { return None };
|
||||
let Some(&TaggedValue::F64(font_size)) = inputs[3].as_value() else { return None };
|
||||
|
||||
Some((text, font, font_size))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
|||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNodeMetadata, NodeId, NodeInput};
|
||||
use graph_craft::document::{DocumentNodeMetadata, NodeId};
|
||||
use graphene_core::raster::BlendMode;
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::vector::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
||||
|
|
@ -268,11 +268,7 @@ impl BrushToolData {
|
|||
for (node, node_id) in document.network().upstream_flow_back_from_nodes(vec![layer.to_node()], graph_craft::document::FlowType::HorizontalFlow) {
|
||||
if node.name == "Brush" && node_id != layer.to_node() {
|
||||
let points_input = node.inputs.get(2)?;
|
||||
let NodeInput::Value {
|
||||
tagged_value: TaggedValue::BrushStrokes(strokes),
|
||||
..
|
||||
} = points_input
|
||||
else {
|
||||
let Some(TaggedValue::BrushStrokes(strokes)) = points_input.as_value() else {
|
||||
continue;
|
||||
};
|
||||
self.strokes.clone_from(strokes);
|
||||
|
|
|
|||
|
|
@ -751,26 +751,14 @@ impl EditorHandle {
|
|||
return;
|
||||
}
|
||||
|
||||
let empty_vec = Vec::new();
|
||||
let path_data = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::Subpaths(translation),
|
||||
..
|
||||
} = &inputs[0]
|
||||
{
|
||||
translation
|
||||
} else {
|
||||
&empty_vec
|
||||
let path_data = match &inputs[0].as_value() {
|
||||
Some(TaggedValue::Subpaths(translation)) => translation,
|
||||
_ => &Vec::new(),
|
||||
};
|
||||
|
||||
let empty_vec = Vec::new();
|
||||
let colinear_manipulators = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::PointIds(translation),
|
||||
..
|
||||
} = &inputs[1]
|
||||
{
|
||||
translation
|
||||
} else {
|
||||
&empty_vec
|
||||
let colinear_manipulators = match &inputs[1].as_value() {
|
||||
Some(TaggedValue::PointIds(translation)) => translation,
|
||||
_ => &Vec::new(),
|
||||
};
|
||||
|
||||
let mut vector_data = VectorData::from_subpaths(path_data, false);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ pub mod application_io;
|
|||
pub mod quantization;
|
||||
|
||||
use core::any::TypeId;
|
||||
pub use memo::MemoHash;
|
||||
pub use raster::Color;
|
||||
pub use types::Cow;
|
||||
|
||||
|
|
|
|||
|
|
@ -142,3 +142,101 @@ impl<I, T, N> MonitorNode<I, T, N> {
|
|||
MonitorNode { io: Arc::new(Mutex::new(None)), node }
|
||||
}
|
||||
}
|
||||
|
||||
use core::hash::{Hash, Hasher};
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct MemoHash<T: Hash> {
|
||||
hash: u64,
|
||||
value: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: serde::Deserialize<'de> + Hash> serde::Deserialize<'de> for MemoHash<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
T::deserialize(deserializer).map(|value| Self::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: Hash + serde::Serialize> serde::Serialize for MemoHash<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.value.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Hash> MemoHash<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
let hash = Self::calc_hash(&value);
|
||||
Self { hash, value }
|
||||
}
|
||||
pub fn new_with_hash(value: T, hash: u64) -> Self {
|
||||
Self { hash, value }
|
||||
}
|
||||
|
||||
fn calc_hash(data: &T) -> u64 {
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
data.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn inner_mut<'a>(&'a mut self) -> MemoHashGuard<'a, T> {
|
||||
MemoHashGuard { inner: self }
|
||||
}
|
||||
pub fn into_inner<'a>(self) -> T {
|
||||
self.value
|
||||
}
|
||||
pub fn hash_code(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
}
|
||||
impl<T: Hash> From<T> for MemoHash<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for MemoHash<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.hash.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> core::ops::Deref for MemoHash<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemoHashGuard<'a, T: Hash> {
|
||||
inner: &'a mut MemoHash<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Hash> core::ops::Drop for MemoHashGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
let hash = MemoHash::<T>::calc_hash(&self.inner.value);
|
||||
self.inner.hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Hash> core::ops::Deref for MemoHashGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Hash> core::ops::DerefMut for MemoHashGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner.value
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -830,9 +830,9 @@ impl Color {
|
|||
/// ```
|
||||
/// use graphene_core::raster::color::Color;
|
||||
/// let color1 = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61).to_gamma_srgb();
|
||||
/// assert_eq!("3240a261", color1.rgb_optional_a_hex())
|
||||
/// let color2 = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61).to_gamma_srgb();
|
||||
/// assert_eq!("3240a2", color2.rgb_optional_a_hex())
|
||||
/// assert_eq!("3240a261", color1.rgb_optional_a_hex());
|
||||
/// let color2 = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0xFF).to_gamma_srgb();
|
||||
/// assert_eq!("5267fa", color2.rgb_optional_a_hex());
|
||||
/// ```
|
||||
#[cfg(feature = "std")]
|
||||
pub fn rgb_optional_a_hex(&self) -> String {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use super::*;
|
||||
use crate::uuid::generate_uuid;
|
||||
use crate::Node;
|
||||
|
||||
use bezier_rs::BezierHandles;
|
||||
|
|
@ -18,15 +19,7 @@ pub struct PointModification {
|
|||
|
||||
impl Hash for PointModification {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.add.hash(state);
|
||||
|
||||
let mut remove = self.remove.iter().collect::<Vec<_>>();
|
||||
remove.sort_unstable();
|
||||
remove.hash(state);
|
||||
|
||||
let mut delta = self.delta.iter().map(|(&a, &b)| (a, [b.x.to_bits(), b.y.to_bits()])).collect::<Vec<_>>();
|
||||
delta.sort_unstable();
|
||||
delta.hash(state);
|
||||
generate_uuid().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,36 +97,6 @@ pub struct SegmentModification {
|
|||
stroke: HashMap<SegmentId, StrokeId>,
|
||||
}
|
||||
|
||||
impl Hash for SegmentModification {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.add.hash(state);
|
||||
|
||||
let mut remove = self.remove.iter().collect::<Vec<_>>();
|
||||
remove.sort_unstable();
|
||||
remove.hash(state);
|
||||
|
||||
let mut start_point = self.start_point.iter().map(|(&a, &b)| (a, b)).collect::<Vec<_>>();
|
||||
start_point.sort_unstable();
|
||||
start_point.hash(state);
|
||||
|
||||
let mut end_point = self.end_point.iter().map(|(&a, &b)| (a, b)).collect::<Vec<_>>();
|
||||
end_point.sort_unstable();
|
||||
end_point.hash(state);
|
||||
|
||||
let mut handle_primary = self.handle_primary.iter().map(|(&a, &b)| (a, b.map(|b| [b.x.to_bits(), b.y.to_bits()]))).collect::<Vec<_>>();
|
||||
handle_primary.sort_unstable();
|
||||
handle_primary.hash(state);
|
||||
|
||||
let mut handle_end = self.handle_end.iter().map(|(&a, &b)| (a, b.map(|b| [b.x.to_bits(), b.y.to_bits()]))).collect::<Vec<_>>();
|
||||
handle_end.sort_unstable();
|
||||
handle_end.hash(state);
|
||||
|
||||
let mut stroke = self.stroke.iter().map(|(&a, &b)| (a, b)).collect::<Vec<_>>();
|
||||
stroke.sort_unstable();
|
||||
stroke.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl SegmentModification {
|
||||
/// Apply this modification to the specified [`SegmentDomain`].
|
||||
pub fn apply(&self, segment_domain: &mut SegmentDomain, point_domain: &PointDomain) {
|
||||
|
|
@ -289,24 +252,6 @@ pub struct RegionModification {
|
|||
fill: HashMap<RegionId, FillId>,
|
||||
}
|
||||
|
||||
impl Hash for RegionModification {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.add.hash(state);
|
||||
|
||||
let mut remove = self.remove.iter().collect::<Vec<_>>();
|
||||
remove.sort_unstable();
|
||||
remove.hash(state);
|
||||
|
||||
let mut segment_range = self.segment_range.iter().map(|(&a, b)| (a, (*b.start(), *b.end()))).collect::<Vec<_>>();
|
||||
segment_range.sort_unstable();
|
||||
segment_range.hash(state);
|
||||
|
||||
let mut fill = self.fill.iter().map(|(&a, &b)| (a, b)).collect::<Vec<_>>();
|
||||
fill.sort_unstable();
|
||||
fill.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl RegionModification {
|
||||
/// Apply this modification to the specified [`RegionDomain`].
|
||||
pub fn apply(&self, region_domain: &mut RegionDomain) {
|
||||
|
|
@ -460,19 +405,7 @@ impl VectorModification {
|
|||
|
||||
impl core::hash::Hash for VectorModification {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.points.hash(state);
|
||||
|
||||
self.segments.hash(state);
|
||||
|
||||
self.regions.hash(state);
|
||||
|
||||
let mut add_g1_continuous = self.add_g1_continuous.iter().copied().collect::<Vec<_>>();
|
||||
add_g1_continuous.sort_unstable();
|
||||
add_g1_continuous.hash(state);
|
||||
|
||||
let mut remove_g1_continuous = self.remove_g1_continuous.iter().copied().collect::<Vec<_>>();
|
||||
remove_g1_continuous.sort_unstable();
|
||||
remove_g1_continuous.hash(state);
|
||||
generate_uuid().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
|||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
pub use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
||||
use graphene_core::{Cow, MemoHash, ProtoNodeIdentifier, Type};
|
||||
|
||||
use glam::IVec2;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
|
@ -442,7 +442,7 @@ pub enum NodeInput {
|
|||
Node { node_id: NodeId, output_index: usize, lambda: bool },
|
||||
|
||||
/// A hardcoded value that can't change after the graph is compiled. Gets converted into a value node during graph compilation.
|
||||
Value { tagged_value: TaggedValue, exposed: bool },
|
||||
Value { tagged_value: MemoHash<TaggedValue>, exposed: bool },
|
||||
|
||||
// TODO: Remove import_type and get type from parent node input
|
||||
/// Input that is provided by the parent network to this document node, instead of from a hardcoded value or another node within the same network.
|
||||
|
|
@ -478,7 +478,8 @@ impl NodeInput {
|
|||
Self::Node { node_id, output_index, lambda: true }
|
||||
}
|
||||
|
||||
pub const fn value(tagged_value: TaggedValue, exposed: bool) -> Self {
|
||||
pub fn value(tagged_value: TaggedValue, exposed: bool) -> Self {
|
||||
let tagged_value = tagged_value.into();
|
||||
Self::Value { tagged_value, exposed }
|
||||
}
|
||||
|
||||
|
|
@ -527,6 +528,13 @@ impl NodeInput {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn as_non_exposed_value(&self) -> Option<&TaggedValue> {
|
||||
if let NodeInput::Value { tagged_value, exposed: false } = self {
|
||||
Some(tagged_value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_node(&self) -> Option<NodeId> {
|
||||
if let NodeInput::Node { node_id, .. } = self {
|
||||
|
|
@ -1610,7 +1618,7 @@ mod test {
|
|||
assert_eq!(extraction_network.nodes.len(), 1);
|
||||
let inputs = extraction_network.nodes.get(&NodeId(1)).unwrap().inputs.clone();
|
||||
assert_eq!(inputs.len(), 1);
|
||||
assert!(matches!(&inputs[0], &NodeInput::Value{ tagged_value: TaggedValue::DocumentNode(ref network), ..} if network == &id_node));
|
||||
assert!(matches!(&inputs[0].as_value(), &Some(TaggedValue::DocumentNode(ref network), ..) if network == &id_node));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1621,13 +1629,7 @@ mod test {
|
|||
NodeId(1),
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![
|
||||
NodeInput::network(concrete!(u32), 0),
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
},
|
||||
],
|
||||
inputs: vec![NodeInput::network(concrete!(u32), 0), NodeInput::value(TaggedValue::U32(2), false)],
|
||||
implementation: DocumentNodeImplementation::Network(add_network()),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
@ -1712,7 +1714,7 @@ mod test {
|
|||
ProtoNode {
|
||||
identifier: "graphene_core::value::ClonedNode".into(),
|
||||
input: ProtoNodeInput::None,
|
||||
construction_args: ConstructionArgs::Value(TaggedValue::U32(2)),
|
||||
construction_args: ConstructionArgs::Value(TaggedValue::U32(2).into()),
|
||||
original_location: OriginalLocation {
|
||||
path: Some(vec![NodeId(1), NodeId(4)]),
|
||||
inputs_source: HashMap::new(),
|
||||
|
|
@ -1759,10 +1761,7 @@ mod test {
|
|||
NodeId(14),
|
||||
DocumentNode {
|
||||
name: "Value".into(),
|
||||
inputs: vec![NodeInput::Value {
|
||||
tagged_value: TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
}],
|
||||
inputs: vec![NodeInput::value(TaggedValue::U32(2), false)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()),
|
||||
original_location: OriginalLocation {
|
||||
path: Some(vec![NodeId(1), NodeId(4)]),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::wasm_application_io::WasmEditorApi;
|
|||
|
||||
use graphene_core::raster::brush_cache::BrushCache;
|
||||
use graphene_core::raster::{BlendMode, LuminanceCalculation};
|
||||
use graphene_core::{Color, Node, Type};
|
||||
use graphene_core::{Color, MemoHash, Node, Type};
|
||||
|
||||
use dyn_any::DynAny;
|
||||
pub use dyn_any::StaticType;
|
||||
|
|
@ -207,17 +207,17 @@ impl Display for TaggedValue {
|
|||
}
|
||||
|
||||
pub struct UpcastNode {
|
||||
value: TaggedValue,
|
||||
value: MemoHash<TaggedValue>,
|
||||
}
|
||||
impl<'input> Node<'input, DAny<'input>> for UpcastNode {
|
||||
type Output = FutureAny<'input>;
|
||||
|
||||
fn eval(&'input self, _: DAny<'input>) -> Self::Output {
|
||||
Box::pin(async move { self.value.clone().to_any() })
|
||||
Box::pin(async move { self.value.clone().into_inner().to_any() })
|
||||
}
|
||||
}
|
||||
impl UpcastNode {
|
||||
pub fn new(value: TaggedValue) -> Self {
|
||||
pub fn new(value: MemoHash<TaggedValue>) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl core::fmt::Display for ProtoNetwork {
|
|||
/// Defines the arguments used to construct the boxed node struct. This is used to call the constructor function in the `node_registry.rs` file - which is hidden behind a wall of macros.
|
||||
pub enum ConstructionArgs {
|
||||
/// A value of a type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||
Value(value::TaggedValue),
|
||||
Value(MemoHash<value::TaggedValue>),
|
||||
// TODO: use a struct for clearer naming.
|
||||
/// A list of nodes used as inputs to the constructor function in `node_registry.rs`.
|
||||
/// The bool indicates whether to treat the node as lambda node.
|
||||
|
|
@ -230,7 +230,7 @@ impl Default for ProtoNode {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
identifier: ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"),
|
||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(0)),
|
||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(0).into()),
|
||||
input: ProtoNodeInput::None,
|
||||
original_location: OriginalLocation::default(),
|
||||
skip_deduplication: false,
|
||||
|
|
@ -940,12 +940,12 @@ mod test {
|
|||
assert_eq!(
|
||||
ids,
|
||||
vec![
|
||||
NodeId(5686040524603683634),
|
||||
NodeId(13787140740513543798),
|
||||
NodeId(1280393769237740322),
|
||||
NodeId(3100442468152897091),
|
||||
NodeId(14834729712909816752),
|
||||
NodeId(8678825113056010444)
|
||||
NodeId(12083027370457564588),
|
||||
NodeId(10127202135369428481),
|
||||
NodeId(3781642984881236270),
|
||||
NodeId(9447822059040146367),
|
||||
NodeId(15916837829094140504),
|
||||
NodeId(1758919868423328454)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
@ -996,7 +996,7 @@ mod test {
|
|||
ProtoNode {
|
||||
identifier: "value".into(),
|
||||
input: ProtoNodeInput::None,
|
||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(2)),
|
||||
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(2).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use graphene_core::{transform::Footprint, GraphicGroup};
|
|||
use graphene_core::{vector::misc::BooleanOperation, GraphicElement};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub struct BinaryBooleanOperationNode<LowerVectorData, BooleanOp> {
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ impl BorrowTree {
|
|||
|
||||
match &proto_node.construction_args {
|
||||
ConstructionArgs::Value(value) => {
|
||||
let node = if let TaggedValue::EditorApi(api) = value {
|
||||
let node = if let TaggedValue::EditorApi(api) = &**value {
|
||||
let editor_api = UpcastAsRefNode::new(api.clone());
|
||||
let node = Box::new(editor_api) as TypeErasedBox<'_>;
|
||||
NodeContainer::new(node)
|
||||
|
|
@ -263,7 +263,7 @@ mod test {
|
|||
#[test]
|
||||
fn push_node_sync() {
|
||||
let mut tree = BorrowTree::default();
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)), vec![]);
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![]);
|
||||
let context = TypingContext::default();
|
||||
let future = tree.push_node(NodeId(0), val_1_protonode, &context);
|
||||
futures::executor::block_on(future).unwrap();
|
||||
|
|
|
|||
|
|
@ -48,13 +48,7 @@ mod tests {
|
|||
NodeId(0),
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![
|
||||
NodeInput::network(concrete!(u32), 0),
|
||||
NodeInput::Value {
|
||||
tagged_value: graph_craft::document::value::TaggedValue::U32(1u32),
|
||||
exposed: false,
|
||||
},
|
||||
],
|
||||
inputs: vec![NodeInput::network(concrete!(u32), 0), NodeInput::value(graph_craft::document::value::TaggedValue::U32(1u32), false)],
|
||||
implementation: DocumentNodeImplementation::Network(add_network()),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue