mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Fix and improve robustness of node/parameter tooltip plumbing; simplify properties row widget boilerplate (#2600)
* WIP * WIP2 * Further cleanup
This commit is contained in:
parent
6196dbff95
commit
a376832480
11 changed files with 1084 additions and 1106 deletions
2
demo-artwork/changing-seasons.graphite
generated
2
demo-artwork/changing-seasons.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/procedural-string-lights.graphite
generated
2
demo-artwork/procedural-string-lights.graphite
generated
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
use super::node_properties;
|
||||
use super::node_properties::{self, ParameterWidgetsInfo};
|
||||
use super::utility_types::FrontendNodeType;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
||||
|
|
@ -39,7 +39,7 @@ impl NodePropertiesContext<'_> {
|
|||
let input_properties_row = self.network_interface.input_properties_row(node_id, index, self.selection_network_path)?;
|
||||
if let Some(widget_override) = &input_properties_row.widget_override {
|
||||
let Some(widget_override_lambda) = INPUT_OVERRIDES.get(widget_override) else {
|
||||
log::error!("Could not get widget override lambda in call_widget_override");
|
||||
log::error!("Could not get widget override '{widget_override}' lambda in call_widget_override");
|
||||
return None;
|
||||
};
|
||||
widget_override_lambda(*node_id, index, self)
|
||||
|
|
@ -125,7 +125,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("The identity node passes its data through. You can use this to organize your node graph."),
|
||||
description: Cow::Borrowed("Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes."),
|
||||
properties: Some("identity_properties"),
|
||||
},
|
||||
// TODO: Auto-generate this from its proto node macro
|
||||
|
|
@ -856,7 +856,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
.enumerate()
|
||||
.map(|(id, node)| (NodeId(id as u64), node))
|
||||
.collect(),
|
||||
|
||||
..Default::default()
|
||||
}),
|
||||
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
|
||||
|
|
@ -1488,7 +1487,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
|
|
@ -2267,7 +2265,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
|
|
@ -2366,109 +2363,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Copy to Points",
|
||||
category: "Vector",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
// TODO: Wrap this implementation with a document node that has a cache node so the output is cached?
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::vector::CopyToPointsNode"),
|
||||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::VectorData(graphene_core::vector::VectorDataTable::default()), true),
|
||||
NodeInput::value(TaggedValue::VectorData(graphene_core::vector::VectorDataTable::default()), true),
|
||||
NodeInput::value(TaggedValue::F64(1.), false),
|
||||
NodeInput::value(TaggedValue::F64(1.), false),
|
||||
NodeInput::value(TaggedValue::F64(0.), false),
|
||||
NodeInput::value(TaggedValue::U32(0), false),
|
||||
NodeInput::value(TaggedValue::F64(0.), false),
|
||||
NodeInput::value(TaggedValue::U32(0), false),
|
||||
],
|
||||
manual_composition: Some(concrete!(Context)),
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_properties: vec![
|
||||
("Points", "TODO").into(),
|
||||
Into::<PropertiesRow>::into(("Instance", "TODO")).with_tooltip("Artwork to be copied and placed at each point"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Scale Min",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(0.),
|
||||
range_max: Some(2.),
|
||||
unit: Some("x".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Minimum range of randomized sizes given to each instance"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Scale Max",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(0.),
|
||||
range_max: Some(2.),
|
||||
unit: Some("x".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Minimum range of randomized sizes given to each instance"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Scale Bias",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(-50.),
|
||||
range_max: Some(50.),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes)"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Scale Seed",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
is_integer: true,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Seed to determine unique variations on all the randomized instance sizes"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Rotation",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
max: Some(360.),
|
||||
mode: NumberInputMode::Range,
|
||||
unit: Some("°".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise"),
|
||||
PropertiesRow::with_override(
|
||||
"Random Rotation Seed",
|
||||
"TODO",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
is_integer: true,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Seed to determine unique variations on all the randomized instance angles"),
|
||||
],
|
||||
output_names: vec!["Vector".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
|
|
@ -2587,45 +2481,41 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
}),
|
||||
input_properties: vec![
|
||||
("Vector Data", "TODO").into(),
|
||||
("Vector Data", "The shape to be resampled and converted into a polyline.").into(),
|
||||
PropertiesRow::with_override(
|
||||
"Spacing",
|
||||
"TODO",
|
||||
"Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled).",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(1.),
|
||||
unit: Some(" px".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled)"),
|
||||
),
|
||||
PropertiesRow::with_override(
|
||||
"Start Offset",
|
||||
"TODO",
|
||||
"Exclude some distance from the start of the path before the first instance.",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
unit: Some(" px".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Exclude some distance from the start of the path before the first instance"),
|
||||
),
|
||||
PropertiesRow::with_override(
|
||||
"Stop Offset",
|
||||
"TODO",
|
||||
"Exclude some distance from the end of the path after the last instance.",
|
||||
WidgetOverride::Number(NumberInputSettings {
|
||||
min: Some(0.),
|
||||
unit: Some(" px".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.with_tooltip("Exclude some distance from the end of the path after the last instance"),
|
||||
Into::<PropertiesRow>::into(("Adaptive Spacing", "TODO")).with_tooltip("Round 'Spacing' to a nearby value that divides into the path length evenly"),
|
||||
),
|
||||
Into::<PropertiesRow>::into(("Adaptive Spacing", "Round 'Spacing' to a nearby value that divides into the path length evenly.")),
|
||||
],
|
||||
output_names: vec!["Vector".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
description: Cow::Borrowed("Convert vector geometry into a polyline composed of evenly spaced points."),
|
||||
properties: None,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
|
@ -2750,7 +2640,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: None,
|
||||
},
|
||||
|
|
@ -2849,7 +2738,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
output_names: vec![output_type.to_string()],
|
||||
has_primary_output: true,
|
||||
locked: false,
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
|
|
@ -3100,7 +2988,7 @@ fn static_input_properties() -> InputProperties {
|
|||
true
|
||||
});
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::number_widget(document_node, node_id, index, input_name, input_description, number_input, blank_assist),
|
||||
widgets: node_properties::number_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, blank_assist), number_input),
|
||||
}])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3137,17 +3025,12 @@ fn static_input_properties() -> InputProperties {
|
|||
.input_metadata(&node_id, index, "min", context.selection_network_path)
|
||||
.and_then(|value| value.as_f64());
|
||||
|
||||
Ok(vec![node_properties::vec2_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
Ok(vec![node_properties::vector2_widget(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
x,
|
||||
y,
|
||||
unit,
|
||||
min,
|
||||
node_properties::add_blank_assist,
|
||||
)])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3157,13 +3040,8 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let scale = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![scale.into()])
|
||||
}),
|
||||
|
|
@ -3172,7 +3050,7 @@ fn static_input_properties() -> InputProperties {
|
|||
"noise_properties_noise_type".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let noise_type_row = node_properties::noise_type(document_node, node_id, index, input_name, input_description, true);
|
||||
let noise_type_row = node_properties::noise_type_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true));
|
||||
Ok(vec![noise_type_row, LayoutGroup::Row { widgets: Vec::new() }])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3181,7 +3059,7 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let domain_warp_type = node_properties::domain_warp_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active);
|
||||
let domain_warp_type = node_properties::domain_warp_type_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true), !coherent_noise_active);
|
||||
Ok(vec![domain_warp_type])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3191,13 +3069,8 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, _, _, domain_warp_active, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let domain_warp_amplitude = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
}),
|
||||
|
|
@ -3207,7 +3080,7 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_type_row = node_properties::fractal_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active);
|
||||
let fractal_type_row = node_properties::fractal_type_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true), !coherent_noise_active);
|
||||
Ok(vec![fractal_type_row])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3217,11 +3090,7 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_octaves = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(1.)
|
||||
|
|
@ -3229,7 +3098,6 @@ fn static_input_properties() -> InputProperties {
|
|||
.range_max(Some(4.))
|
||||
.is_integer(true)
|
||||
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![fractal_octaves.into()])
|
||||
}),
|
||||
|
|
@ -3240,17 +3108,12 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_lacunarity = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
.range_max(Some(10.))
|
||||
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![fractal_lacunarity.into()])
|
||||
}),
|
||||
|
|
@ -3261,17 +3124,12 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_gain = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
.range_max(Some(10.))
|
||||
.disabled(!coherent_noise_active || !fractal_active || domain_warp_only_fractal_type_wrongly_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![fractal_gain.into()])
|
||||
}),
|
||||
|
|
@ -3282,17 +3140,12 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_weighted_strength = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
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,
|
||||
);
|
||||
Ok(vec![fractal_weighted_strength.into()])
|
||||
}),
|
||||
|
|
@ -3303,17 +3156,12 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (fractal_active, coherent_noise_active, _, ping_pong_active, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_ping_pong_strength = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
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,
|
||||
);
|
||||
Ok(vec![fractal_ping_pong_strength.into(), LayoutGroup::Row { widgets: Vec::new() }])
|
||||
}),
|
||||
|
|
@ -3323,8 +3171,10 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_distance_function_row =
|
||||
node_properties::cellular_distance_function(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active || !cellular_noise_active);
|
||||
let cellular_distance_function_row = node_properties::cellular_distance_function_widget(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
!coherent_noise_active || !cellular_noise_active,
|
||||
);
|
||||
Ok(vec![cellular_distance_function_row])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3333,7 +3183,10 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_return_type = node_properties::cellular_return_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active || !cellular_noise_active);
|
||||
let cellular_return_type = node_properties::cellular_return_type_widget(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
!coherent_noise_active || !cellular_noise_active,
|
||||
);
|
||||
Ok(vec![cellular_return_type])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3343,17 +3196,12 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_jitter = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.range_min(Some(0.))
|
||||
.range_max(Some(1.))
|
||||
.disabled(!coherent_noise_active || !cellular_noise_active),
|
||||
true,
|
||||
);
|
||||
Ok(vec![cellular_jitter.into()])
|
||||
}),
|
||||
|
|
@ -3372,13 +3220,8 @@ fn static_input_properties() -> InputProperties {
|
|||
.unwrap_or(false);
|
||||
let (b_min, b_max) = if is_use_classic { (-100., 100.) } else { (-100., 150.) };
|
||||
let brightness = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().mode_range().range_min(Some(b_min)).range_max(Some(b_max)).unit("%").display_decimal_places(2),
|
||||
true,
|
||||
);
|
||||
Ok(vec![brightness.into()])
|
||||
}),
|
||||
|
|
@ -3397,13 +3240,8 @@ fn static_input_properties() -> InputProperties {
|
|||
.unwrap_or(false);
|
||||
let (c_min, c_max) = if is_use_classic { (-100., 100.) } else { (-50., 100.) };
|
||||
let contrast = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().mode_range().range_min(Some(c_min)).range_max(Some(c_max)).unit("%").display_decimal_places(2),
|
||||
true,
|
||||
);
|
||||
Ok(vec![contrast.into()])
|
||||
}),
|
||||
|
|
@ -3412,7 +3250,10 @@ fn static_input_properties() -> InputProperties {
|
|||
"assign_colors_gradient".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let gradient_row = node_properties::color_widget(document_node, node_id, index, input_name, input_description, ColorInput::default().allow_none(false), true);
|
||||
let gradient_row = node_properties::color_widget(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
ColorInput::default().allow_none(false),
|
||||
);
|
||||
Ok(vec![gradient_row])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3422,13 +3263,8 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
|
||||
let seed_row = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().min(0.).int().disabled(!randomize_enabled),
|
||||
true,
|
||||
);
|
||||
Ok(vec![seed_row.into()])
|
||||
}),
|
||||
|
|
@ -3439,13 +3275,8 @@ fn static_input_properties() -> InputProperties {
|
|||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
|
||||
let repeat_every_row = node_properties::number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
NumberInput::default().min(0.).int().disabled(randomize_enabled),
|
||||
true,
|
||||
);
|
||||
Ok(vec![repeat_every_row.into()])
|
||||
}),
|
||||
|
|
@ -3454,7 +3285,7 @@ fn static_input_properties() -> InputProperties {
|
|||
"mask_stencil".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let mask = node_properties::color_widget(document_node, node_id, index, input_name, input_description, ColorInput::default(), true);
|
||||
let mask = node_properties::color_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true), ColorInput::default());
|
||||
Ok(vec![mask])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3463,7 +3294,10 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::vec_dvec2_input(document_node, node_id, index, input_name, input_description, TextInput::default().centered(true), true),
|
||||
widgets: node_properties::array_of_vector2_widget(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
TextInput::default().centered(true),
|
||||
),
|
||||
}])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3472,7 +3306,10 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
|
||||
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, input_description, super::utility_types::FrontendGraphDataType::Number, true);
|
||||
let mut widgets = node_properties::start_widgets(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
super::utility_types::FrontendGraphDataType::Number,
|
||||
);
|
||||
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
return Err("Input not found in transform rotation input override".to_string());
|
||||
|
|
@ -3504,7 +3341,10 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
|
||||
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, input_description, super::utility_types::FrontendGraphDataType::Number, true);
|
||||
let mut widgets = node_properties::start_widgets(
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
super::utility_types::FrontendGraphDataType::Number,
|
||||
);
|
||||
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
return Err("Input not found in transform skew input override".to_string());
|
||||
|
|
@ -3549,7 +3389,7 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
Ok(vec![LayoutGroup::Row {
|
||||
widgets: node_properties::text_area_widget(document_node, node_id, index, input_name, input_description, true),
|
||||
widgets: node_properties::text_area_widget(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true)),
|
||||
}])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3557,7 +3397,7 @@ fn static_input_properties() -> InputProperties {
|
|||
"text_font".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
let (font, style) = node_properties::font_inputs(document_node, node_id, index, input_name, input_description, true);
|
||||
let (font, style) = node_properties::font_inputs(ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true));
|
||||
let mut result = vec![LayoutGroup::Row { widgets: font }];
|
||||
if let Some(style) = style {
|
||||
result.push(LayoutGroup::Row { widgets: style });
|
||||
|
|
@ -3570,13 +3410,8 @@ fn static_input_properties() -> InputProperties {
|
|||
Box::new(|node_id, index, context| {
|
||||
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
|
||||
Ok(vec![node_properties::color_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
index,
|
||||
input_name,
|
||||
input_description,
|
||||
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
|
||||
ColorInput::default().allow_none(false),
|
||||
true,
|
||||
)])
|
||||
}),
|
||||
);
|
||||
|
|
@ -3673,7 +3508,12 @@ pub fn collect_node_types() -> Vec<FrontendNodeType> {
|
|||
pub fn collect_node_descriptions() -> Vec<(String, String)> {
|
||||
DOCUMENT_NODE_TYPES
|
||||
.iter()
|
||||
.map(|definition| (definition.identifier.to_string(), definition.description.to_string()))
|
||||
.map(|definition| {
|
||||
(
|
||||
definition.identifier.to_string(),
|
||||
if definition.description != "TODO" { definition.description.to_string() } else { String::new() },
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_layer_id] });
|
||||
}
|
||||
NodeGraphMessage::AddImport => {
|
||||
network_interface.add_import(graph_craft::document::value::TaggedValue::None, true, -1, "", breadcrumb_network_path);
|
||||
network_interface.add_import(graph_craft::document::value::TaggedValue::None, true, -1, "", "", breadcrumb_network_path);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
NodeGraphMessage::AddExport => {
|
||||
|
|
@ -2236,8 +2236,8 @@ impl NodeGraphMessageHandler {
|
|||
data_type: FrontendGraphDataType::displayed_type(&input.ty, &input.type_source),
|
||||
resolved_type: Some(format!("{:?} from {:?}", &input.ty, input.type_source)),
|
||||
valid_types: input.valid_types.iter().map(|ty| ty.to_string()).collect(),
|
||||
name: input.name.unwrap_or_else(|| input.ty.nested_type().to_string()),
|
||||
description: input.description.unwrap_or_default(),
|
||||
name: input.input_name.unwrap_or_else(|| input.ty.nested_type().to_string()),
|
||||
description: input.input_description.unwrap_or_default(),
|
||||
connected_to: input.output_connector,
|
||||
})
|
||||
});
|
||||
|
|
@ -2546,8 +2546,8 @@ impl NodeGraphMessageHandler {
|
|||
|
||||
#[derive(Default)]
|
||||
struct InputLookup {
|
||||
name: Option<String>,
|
||||
description: Option<String>,
|
||||
input_name: Option<String>,
|
||||
input_description: Option<String>,
|
||||
ty: Type,
|
||||
type_source: TypeSource,
|
||||
valid_types: Vec<Type>,
|
||||
|
|
@ -2574,20 +2574,17 @@ fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface:
|
|||
}
|
||||
|
||||
// Get the name from the metadata here (since it also requires a reference to the `network_interface`)
|
||||
let name = network_interface
|
||||
.input_name(&node_id, index, breadcrumb_network_path)
|
||||
let input_name = network_interface
|
||||
.input_name(node_id, index, breadcrumb_network_path)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|name| name.to_string());
|
||||
let description = network_interface
|
||||
.input_description(&node_id, index, breadcrumb_network_path)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|description| description.to_string());
|
||||
let input_description = network_interface.input_description(node_id, index, breadcrumb_network_path).map(|description| description.to_string());
|
||||
// Get the output connector that feeds into this input (done here as well for simplicity)
|
||||
let connector = OutputConnector::from_input(input);
|
||||
|
||||
inputs.push(Some(InputLookup {
|
||||
name,
|
||||
description,
|
||||
input_name,
|
||||
input_description,
|
||||
output_connector: connector,
|
||||
..Default::default()
|
||||
}));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,546 @@
|
|||
//! This has all been copied out of node_properties.rs to avoid leaving hundreds of lines of commented out code in that file. It's left here instead for future reference.
|
||||
|
||||
// pub fn imaginate_sampling_method(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup {
|
||||
// let ParameterWidgetsInfo { node_id, index, .. } = parameter_widgets_info;
|
||||
|
||||
// vec![
|
||||
// DropdownInput::new(
|
||||
// ImaginateSamplingMethod::list()
|
||||
// .into_iter()
|
||||
// .map(|method| {
|
||||
// vec![
|
||||
// MenuListEntry::new(format!("{:?}", method))
|
||||
// .label(method.to_string())
|
||||
// .on_update(update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, index)),
|
||||
// ]
|
||||
// })
|
||||
// .collect(),
|
||||
// )
|
||||
// .widget_holder(),
|
||||
// ]
|
||||
// .into()
|
||||
// }
|
||||
|
||||
// pub fn imaginate_mask_starting_fill(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup {
|
||||
// let ParameterWidgetsInfo { node_id, index, .. } = parameter_widgets_info;
|
||||
|
||||
// vec![
|
||||
// DropdownInput::new(
|
||||
// ImaginateMaskStartingFill::list()
|
||||
// .into_iter()
|
||||
// .map(|fill| {
|
||||
// vec![
|
||||
// MenuListEntry::new(format!("{:?}", fill))
|
||||
// .label(fill.to_string())
|
||||
// .on_update(update_value(move |_| TaggedValue::ImaginateMaskStartingFill(fill), node_id, index)),
|
||||
// ]
|
||||
// })
|
||||
// .collect(),
|
||||
// )
|
||||
// .widget_holder(),
|
||||
// ]
|
||||
// .into()
|
||||
// }
|
||||
|
||||
// pub(crate) fn imaginate_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
// let imaginate_node = [context.selection_network_path, &[node_id]].concat();
|
||||
|
||||
// let resolve_input = |name: &str| {
|
||||
// IMAGINATE_NODE
|
||||
// .default_node_template()
|
||||
// .persistent_node_metadata
|
||||
// .input_properties
|
||||
// .iter()
|
||||
// .position(|row| row.input_name.as_str() == name)
|
||||
// .unwrap_or_else(|| panic!("Input {name} not found"))
|
||||
// };
|
||||
// let seed_index = resolve_input("Seed");
|
||||
// let resolution_index = resolve_input("Resolution");
|
||||
// let samples_index = resolve_input("Samples");
|
||||
// let sampling_method_index = resolve_input("Sampling Method");
|
||||
// let text_guidance_index = resolve_input("Prompt Guidance");
|
||||
// let text_index = resolve_input("Prompt");
|
||||
// let neg_index = resolve_input("Negative Prompt");
|
||||
// let base_img_index = resolve_input("Adapt Input Image");
|
||||
// let img_creativity_index = resolve_input("Image Creativity");
|
||||
// // let mask_index = resolve_input("Masking Layer");
|
||||
// // let inpaint_index = resolve_input("Inpaint");
|
||||
// // let mask_blur_index = resolve_input("Mask Blur");
|
||||
// // let mask_fill_index = resolve_input("Mask Starting Fill");
|
||||
// let faces_index = resolve_input("Improve Faces");
|
||||
// let tiling_index = resolve_input("Tiling");
|
||||
|
||||
// let document_node = match get_document_node(node_id, context) {
|
||||
// Ok(document_node) => document_node,
|
||||
// Err(err) => {
|
||||
// log::error!("Could not get document node in imaginate_properties: {err}");
|
||||
// return Vec::new();
|
||||
// }
|
||||
// };
|
||||
// let controller = &document_node.inputs[resolve_input("Controller")];
|
||||
|
||||
// let server_status = {
|
||||
// let server_status = context.persistent_data.imaginate.server_status();
|
||||
// let status_text = server_status.to_text();
|
||||
// let mut widgets = vec![
|
||||
// TextLabel::new("Server").widget_holder(),
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// IconButton::new("Settings", 24)
|
||||
// .tooltip("Preferences: Imaginate")
|
||||
// .on_update(|_| DialogMessage::RequestPreferencesDialog.into())
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// TextLabel::new(status_text).bold(true).widget_holder(),
|
||||
// Separator::new(SeparatorType::Related).widget_holder(),
|
||||
// IconButton::new("Reload", 24)
|
||||
// .tooltip("Refresh connection status")
|
||||
// .on_update(|_| PortfolioMessage::ImaginateCheckServerStatus.into())
|
||||
// .widget_holder(),
|
||||
// ];
|
||||
// if let ImaginateServerStatus::Unavailable | ImaginateServerStatus::Failed(_) = server_status {
|
||||
// widgets.extend([
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// TextButton::new("Server Help")
|
||||
// .tooltip("Learn how to connect Imaginate to an image generation server")
|
||||
// .on_update(|_| {
|
||||
// FrontendMessage::TriggerVisitLink {
|
||||
// url: "https://github.com/GraphiteEditor/Graphite/discussions/1089".to_string(),
|
||||
// }
|
||||
// .into()
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// ]);
|
||||
// }
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
||||
// };
|
||||
|
||||
// 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 Some(&TaggedValue::Bool(use_base_image)) = &document_node.inputs[base_img_index].as_value() {
|
||||
// use_base_image
|
||||
// } else {
|
||||
// true
|
||||
// };
|
||||
|
||||
// let transform_not_connected = false;
|
||||
|
||||
// let progress = {
|
||||
// let mut widgets = vec![TextLabel::new("Progress").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
// add_blank_assist(&mut widgets);
|
||||
// let status = imaginate_status.to_text();
|
||||
// widgets.push(TextLabel::new(status.as_ref()).bold(true).widget_holder());
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(match imaginate_status {
|
||||
// ImaginateStatus::Failed(_) => status.as_ref(),
|
||||
// _ => "When generating, the percentage represents how many sampling steps have so far been processed out of the target number",
|
||||
// })
|
||||
// };
|
||||
|
||||
// let image_controls = {
|
||||
// let mut widgets = vec![TextLabel::new("Image").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
|
||||
// match &imaginate_status {
|
||||
// ImaginateStatus::Beginning | ImaginateStatus::Uploading => {
|
||||
// add_blank_assist(&mut widgets);
|
||||
// widgets.push(TextButton::new("Beginning...").tooltip("Sending image generation request to the server").disabled(true).widget_holder());
|
||||
// }
|
||||
// ImaginateStatus::Generating(_) => {
|
||||
// add_blank_assist(&mut widgets);
|
||||
// widgets.push(
|
||||
// TextButton::new("Terminate")
|
||||
// .tooltip("Cancel the in-progress image generation and keep the latest progress")
|
||||
// .on_update({
|
||||
// let controller = controller.clone();
|
||||
// move |_| {
|
||||
// controller.request_termination();
|
||||
// Message::NoOp
|
||||
// }
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// );
|
||||
// }
|
||||
// ImaginateStatus::Terminating => {
|
||||
// add_blank_assist(&mut widgets);
|
||||
// widgets.push(
|
||||
// TextButton::new("Terminating...")
|
||||
// .tooltip("Waiting on the final image generated after termination")
|
||||
// .disabled(true)
|
||||
// .widget_holder(),
|
||||
// );
|
||||
// }
|
||||
// ImaginateStatus::Ready | ImaginateStatus::ReadyDone | ImaginateStatus::Terminated | ImaginateStatus::Failed(_) => widgets.extend_from_slice(&[
|
||||
// IconButton::new("Random", 24)
|
||||
// .tooltip("Generate with a new random seed")
|
||||
// .on_update({
|
||||
// let imaginate_node = imaginate_node.clone();
|
||||
// let controller = controller.clone();
|
||||
// move |_| {
|
||||
// controller.trigger_regenerate();
|
||||
// DocumentMessage::ImaginateRandom {
|
||||
// imaginate_node: imaginate_node.clone(),
|
||||
// then_generate: true,
|
||||
// }
|
||||
// .into()
|
||||
// }
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// TextButton::new("Generate")
|
||||
// .tooltip("Fill layer frame by generating a new image")
|
||||
// .on_update({
|
||||
// let controller = controller.clone();
|
||||
// let imaginate_node = imaginate_node.clone();
|
||||
// move |_| {
|
||||
// controller.trigger_regenerate();
|
||||
// DocumentMessage::ImaginateGenerate {
|
||||
// imaginate_node: imaginate_node.clone(),
|
||||
// }
|
||||
// .into()
|
||||
// }
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Related).widget_holder(),
|
||||
// TextButton::new("Clear")
|
||||
// .tooltip("Remove generated image from the layer frame")
|
||||
// .disabled(!matches!(imaginate_status, ImaginateStatus::ReadyDone))
|
||||
// .on_update({
|
||||
// let controller = controller.clone();
|
||||
// let imaginate_node = imaginate_node.clone();
|
||||
// move |_| {
|
||||
// controller.set_status(ImaginateStatus::Ready);
|
||||
// DocumentMessage::ImaginateGenerate {
|
||||
// imaginate_node: imaginate_node.clone(),
|
||||
// }
|
||||
// .into()
|
||||
// }
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// ]),
|
||||
// }
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Buttons that control the image generation process")
|
||||
// };
|
||||
|
||||
// // Requires custom layout for the regenerate button
|
||||
// let seed = {
|
||||
// let mut widgets = start_widgets(document_node, node_id, seed_index, "Seed", FrontendGraphDataType::Number, false);
|
||||
|
||||
// let Some(input) = document_node.inputs.get(seed_index) else {
|
||||
// log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
// return vec![];
|
||||
// };
|
||||
// if let Some(&TaggedValue::F64(seed)) = &input.as_non_exposed_value() {
|
||||
// widgets.extend_from_slice(&[
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// IconButton::new("Resync", 24)
|
||||
// .tooltip("Set a new random seed")
|
||||
// .on_update({
|
||||
// let imaginate_node = imaginate_node.clone();
|
||||
// move |_| {
|
||||
// DocumentMessage::ImaginateRandom {
|
||||
// imaginate_node: imaginate_node.clone(),
|
||||
// then_generate: false,
|
||||
// }
|
||||
// .into()
|
||||
// }
|
||||
// })
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// NumberInput::new(Some(seed))
|
||||
// .int()
|
||||
// .min(-((1_u64 << f64::MANTISSA_DIGITS) as f64))
|
||||
// .max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||
// .on_update(update_value(move |input: &NumberInput| TaggedValue::F64(input.value.unwrap()), node_id, seed_index))
|
||||
// .on_commit(commit_value)
|
||||
// .mode(NumberInputMode::Increment)
|
||||
// .widget_holder(),
|
||||
// ])
|
||||
// }
|
||||
// // Note: Limited by f64. You cannot even have all the possible u64 values :)
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Seed determines the random outcome, enabling limitless unique variations")
|
||||
// };
|
||||
|
||||
// // let transform = context
|
||||
// // .executor
|
||||
// // .introspect_node_in_network(context.network, &imaginate_node, |network| network.inputs.first().copied(), |frame: &ImageFrame<Color>| frame.transform)
|
||||
// // .unwrap_or_default();
|
||||
// let image_size = context
|
||||
// .executor
|
||||
// .introspect_node_in_network(
|
||||
// context.network_interface.document_network().unwrap(),
|
||||
// &imaginate_node,
|
||||
// |network| {
|
||||
// network
|
||||
// .nodes
|
||||
// .iter()
|
||||
// .find(|node| {
|
||||
// node.1
|
||||
// .inputs
|
||||
// .iter()
|
||||
// .any(|node_input| if let NodeInput::Network { import_index, .. } = node_input { *import_index == 0 } else { false })
|
||||
// })
|
||||
// .map(|(node_id, _)| node_id)
|
||||
// .copied()
|
||||
// },
|
||||
// |frame: &IORecord<(), ImageFrame<Color>>| (frame.output.image.width, frame.output.image.height),
|
||||
// )
|
||||
// .unwrap_or_default();
|
||||
|
||||
// let document_node = match get_document_node(node_id, context) {
|
||||
// Ok(document_node) => document_node,
|
||||
// Err(err) => {
|
||||
// log::error!("Could not get document node in imaginate_properties: {err}");
|
||||
// return Vec::new();
|
||||
// }
|
||||
// };
|
||||
|
||||
// let resolution = {
|
||||
// let mut widgets = start_widgets(document_node, node_id, resolution_index, "Resolution", FrontendGraphDataType::Number, false);
|
||||
|
||||
// let round = |size: DVec2| {
|
||||
// let (x, y) = graphene_std::imaginate::pick_safe_imaginate_resolution(size.into());
|
||||
// DVec2::new(x as f64, y as f64)
|
||||
// };
|
||||
|
||||
// let Some(input) = document_node.inputs.get(resolution_index) else {
|
||||
// log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
// return vec![];
|
||||
// };
|
||||
// if let Some(&TaggedValue::OptionalDVec2(vec2)) = &input.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()));
|
||||
|
||||
// widgets.extend_from_slice(&[
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// IconButton::new("FrameAll", 24)
|
||||
// .tooltip("Set the layer dimensions to this resolution")
|
||||
// .on_update(move |_| DialogMessage::RequestComingSoonDialog { issue: None }.into())
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// CheckboxInput::new(!dimensions_is_auto || transform_not_connected)
|
||||
// .icon("Edit12px")
|
||||
// .tooltip({
|
||||
// let message = "Set a custom resolution instead of using the input's dimensions (rounded to the nearest 64)";
|
||||
// let manual_message = "Set a custom resolution instead of using the input's dimensions (rounded to the nearest 64).\n\
|
||||
// \n\
|
||||
// (Resolution must be set manually while the 'Transform' input is disconnected.)";
|
||||
|
||||
// if transform_not_connected {
|
||||
// manual_message
|
||||
// } else {
|
||||
// message
|
||||
// }
|
||||
// })
|
||||
// .disabled(transform_not_connected)
|
||||
// .on_update(update_value(
|
||||
// move |checkbox_input: &CheckboxInput| TaggedValue::OptionalDVec2(if checkbox_input.checked { Some(vec2) } else { None }),
|
||||
// node_id,
|
||||
// resolution_index,
|
||||
// ))
|
||||
// .on_commit(commit_value)
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Related).widget_holder(),
|
||||
// NumberInput::new(Some(vec2.x))
|
||||
// .label("W")
|
||||
// .min(64.)
|
||||
// .step(64.)
|
||||
// .unit(" px")
|
||||
// .disabled(dimensions_is_auto && !transform_not_connected)
|
||||
// .on_update(update_value(
|
||||
// move |number_input: &NumberInput| TaggedValue::OptionalDVec2(Some(round(DVec2::new(number_input.value.unwrap(), vec2.y)))),
|
||||
// node_id,
|
||||
// resolution_index,
|
||||
// ))
|
||||
// .on_commit(commit_value)
|
||||
// .widget_holder(),
|
||||
// Separator::new(SeparatorType::Related).widget_holder(),
|
||||
// NumberInput::new(Some(vec2.y))
|
||||
// .label("H")
|
||||
// .min(64.)
|
||||
// .step(64.)
|
||||
// .unit(" px")
|
||||
// .disabled(dimensions_is_auto && !transform_not_connected)
|
||||
// .on_update(update_value(
|
||||
// move |number_input: &NumberInput| TaggedValue::OptionalDVec2(Some(round(DVec2::new(vec2.x, number_input.value.unwrap())))),
|
||||
// node_id,
|
||||
// resolution_index,
|
||||
// ))
|
||||
// .on_commit(commit_value)
|
||||
// .widget_holder(),
|
||||
// ])
|
||||
// }
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// "Width and height of the image that will be generated. Larger resolutions take longer to compute.\n\
|
||||
// \n\
|
||||
// 512x512 yields optimal results because the AI is trained to understand that scale best. Larger sizes may tend to integrate the prompt's subject more than once. Small sizes are often incoherent.\n\
|
||||
// \n\
|
||||
// Dimensions must be a multiple of 64, so these are set by rounding the layer dimensions. A resolution exceeding 1 megapixel is reduced below that limit because larger sizes may exceed available GPU memory on the server.")
|
||||
// };
|
||||
|
||||
// let sampling_steps = {
|
||||
// let widgets = number_widget(document_node, node_id, samples_index, "Sampling Steps", NumberInput::default().min(0.).max(150.).int(), true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Number of iterations to improve the image generation quality, with diminishing returns around 40 when using the Euler A sampling method")
|
||||
// };
|
||||
|
||||
// let sampling_method = {
|
||||
// let mut widgets = start_widgets(document_node, node_id, sampling_method_index, "Sampling Method", FrontendGraphDataType::General, true);
|
||||
|
||||
// let Some(input) = document_node.inputs.get(sampling_method_index) else {
|
||||
// log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
// return vec![];
|
||||
// };
|
||||
// if let Some(&TaggedValue::ImaginateSamplingMethod(sampling_method)) = &input.as_non_exposed_value() {
|
||||
// let sampling_methods = ImaginateSamplingMethod::list();
|
||||
// let mut entries = Vec::with_capacity(sampling_methods.len());
|
||||
// for method in sampling_methods {
|
||||
// entries.push(
|
||||
// MenuListEntry::new(format!("{method:?}"))
|
||||
// .label(method.to_string())
|
||||
// .on_update(update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, sampling_method_index))
|
||||
// .on_commit(commit_value),
|
||||
// );
|
||||
// }
|
||||
// let entries = vec![entries];
|
||||
|
||||
// widgets.extend_from_slice(&[
|
||||
// Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).widget_holder(),
|
||||
// ]);
|
||||
// }
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step")
|
||||
// };
|
||||
|
||||
// let text_guidance = {
|
||||
// let widgets = number_widget(document_node, node_id, text_guidance_index, "Prompt Guidance", NumberInput::default().min(0.).max(30.), true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// "Amplification of the text prompt's influence over the outcome. At 0, the prompt is entirely ignored.\n\
|
||||
// \n\
|
||||
// Lower values are more creative and exploratory. Higher values are more literal and uninspired.\n\
|
||||
// \n\
|
||||
// This parameter is otherwise known as CFG (classifier-free guidance).",
|
||||
// )
|
||||
// };
|
||||
|
||||
// let text_prompt = {
|
||||
// let widgets = text_area_widget(document_node, node_id, text_index, "Prompt", true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// "Description of the desired image subject and style.\n\
|
||||
// \n\
|
||||
// Include an artist name like \"Rembrandt\" or art medium like \"watercolor\" or \"photography\" to influence the look. List multiple to meld styles.\n\
|
||||
// \n\
|
||||
// To boost (or lessen) the importance of a word or phrase, wrap it in parentheses ending with a colon and a multiplier, for example:\n\
|
||||
// \"Colorless green ideas (sleep:1.3) furiously\"",
|
||||
// )
|
||||
// };
|
||||
// let negative_prompt = {
|
||||
// let widgets = text_area_widget(document_node, node_id, neg_index, "Negative Prompt", true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("A negative text prompt can be used to list things like objects or colors to avoid")
|
||||
// };
|
||||
// let base_image = {
|
||||
// let widgets = bool_widget(document_node, node_id, base_img_index, "Adapt Input Image", CheckboxInput::default(), true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Generate an image based upon the bitmap data plugged into this node")
|
||||
// };
|
||||
// let image_creativity = {
|
||||
// let props = NumberInput::default().percentage().disabled(!use_base_image);
|
||||
// let widgets = number_widget(document_node, node_id, img_creativity_index, "Image Creativity", props, true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// "Strength of the artistic liberties allowing changes from the input image. The image is unchanged at 0% and completely different at 100%.\n\
|
||||
// \n\
|
||||
// This parameter is otherwise known as denoising strength.",
|
||||
// )
|
||||
// };
|
||||
|
||||
// let mut layout = vec![
|
||||
// server_status,
|
||||
// progress,
|
||||
// image_controls,
|
||||
// seed,
|
||||
// resolution,
|
||||
// sampling_steps,
|
||||
// sampling_method,
|
||||
// text_guidance,
|
||||
// text_prompt,
|
||||
// negative_prompt,
|
||||
// base_image,
|
||||
// image_creativity,
|
||||
// // layer_mask,
|
||||
// ];
|
||||
|
||||
// // if use_base_image && layer_reference_input_layer_is_some {
|
||||
// // let in_paint = {
|
||||
// // let mut widgets = start_widgets(document_node, node_id, inpaint_index, "Inpaint", FrontendGraphDataType::Boolean, true);
|
||||
|
||||
// // 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(),
|
||||
// // RadioInput::new(
|
||||
// // [(true, "Inpaint"), (false, "Outpaint")]
|
||||
// // .into_iter()
|
||||
// // .map(|(paint, name)| RadioEntryData::new(name).label(name).on_update(update_value(move |_| TaggedValue::Bool(paint), node_id, inpaint_index)))
|
||||
// // .collect(),
|
||||
// // )
|
||||
// // .selected_index(Some(1 - in_paint as u32))
|
||||
// // .widget_holder(),
|
||||
// // ]);
|
||||
// // }
|
||||
// // LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// // "Constrain image generation to the interior (inpaint) or exterior (outpaint) of the mask, while referencing the other unchanged parts as context imagery.\n\
|
||||
// // \n\
|
||||
// // An unwanted part of an image can be replaced by drawing around it with a black shape and inpainting with that mask layer.\n\
|
||||
// // \n\
|
||||
// // An image can be uncropped by resizing the Imaginate layer to the target bounds and outpainting with a black rectangle mask matching the original image bounds.",
|
||||
// // )
|
||||
// // };
|
||||
|
||||
// // let blur_radius = {
|
||||
// // let number_props = NumberInput::default().unit(" px").min(0.).max(25.).int();
|
||||
// // let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", number_props, true);
|
||||
// // LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
|
||||
// // };
|
||||
|
||||
// // let mask_starting_fill = {
|
||||
// // let mut widgets = start_widgets(document_node, node_id, mask_fill_index, "Mask Starting Fill", FrontendGraphDataType::General, true);
|
||||
|
||||
// // 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());
|
||||
// // for mode in mask_fill_content_modes {
|
||||
// // entries.push(MenuListEntry::new(format!("{mode:?}")).label(mode.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateMaskStartingFill(mode), node_id, mask_fill_index)));
|
||||
// // }
|
||||
// // let entries = vec![entries];
|
||||
|
||||
// // widgets.extend_from_slice(&[
|
||||
// // Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
// // DropdownInput::new(entries).selected_index(Some(starting_fill as u32)).widget_holder(),
|
||||
// // ]);
|
||||
// // }
|
||||
// // LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// // "Begin in/outpainting the masked areas using this fill content as the starting input image.\n\
|
||||
// // \n\
|
||||
// // Each option can be visualized by generating with 'Sampling Steps' set to 0.",
|
||||
// // )
|
||||
// // };
|
||||
// // layout.extend_from_slice(&[in_paint, blur_radius, mask_starting_fill]);
|
||||
// // }
|
||||
|
||||
// let improve_faces = {
|
||||
// let widgets = bool_widget(document_node, node_id, faces_index, "Improve Faces", CheckboxInput::default(), true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip(
|
||||
// "Postprocess human (or human-like) faces to look subtly less distorted.\n\
|
||||
// \n\
|
||||
// This filter can be used on its own by enabling 'Adapt Input Image' and setting 'Sampling Steps' to 0.",
|
||||
// )
|
||||
// };
|
||||
// let tiling = {
|
||||
// let widgets = bool_widget(document_node, node_id, tiling_index, "Tiling", CheckboxInput::default(), true);
|
||||
// LayoutGroup::Row { widgets }.with_tooltip("Generate the image so its edges loop seamlessly to make repeatable patterns or textures")
|
||||
// };
|
||||
// layout.extend_from_slice(&[improve_faces, tiling]);
|
||||
|
||||
// layout
|
||||
// }
|
||||
|
|
@ -795,10 +795,7 @@ impl NodeNetworkInterface {
|
|||
let (input_type, type_source) = self.input_type(&InputConnector::node(encapsulating_node_id, *import_index), &encapsulating_path);
|
||||
let data_type = FrontendGraphDataType::displayed_type(&input_type, &type_source);
|
||||
|
||||
let Some(input_name) = properties_row.input_data.get("input_name").and_then(|input_name| input_name.as_str()) else {
|
||||
log::error!("Could not get input_name in frontend_imports");
|
||||
return None;
|
||||
};
|
||||
let input_name = properties_row.input_name.as_str();
|
||||
let import_name = if input_name.is_empty() {
|
||||
input_type.clone().nested_type().to_string()
|
||||
} else {
|
||||
|
|
@ -1126,20 +1123,36 @@ impl NodeNetworkInterface {
|
|||
Some(&node.implementation)
|
||||
}
|
||||
|
||||
pub fn input_name(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&str> {
|
||||
let Some(value) = self.input_metadata(node_id, index, "input_name", network_path) else {
|
||||
pub fn input_name<'a>(&'a self, node_id: NodeId, index: usize, network_path: &[NodeId]) -> Option<&'a str> {
|
||||
let Some(input_row) = self.input_properties_row(&node_id, index, network_path) else {
|
||||
log::error!("Could not get input_name for node {node_id} index {index}");
|
||||
return None;
|
||||
};
|
||||
value.as_str()
|
||||
let name = input_row.input_name.as_str();
|
||||
if !name.is_empty() {
|
||||
Some(name)
|
||||
} else {
|
||||
let node_definition = resolve_document_node_type(self.reference(&node_id, network_path)?.as_ref()?)?;
|
||||
let rows = &node_definition.node_template.persistent_node_metadata.input_properties;
|
||||
|
||||
rows.get(index).map(|row| row.input_name.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_description(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&str> {
|
||||
let Some(value) = self.input_metadata(node_id, index, "input_description", network_path) else {
|
||||
log::error!("Could not get input_description for node {node_id} index {index}");
|
||||
pub fn input_description<'a>(&'a self, node_id: NodeId, index: usize, network_path: &[NodeId]) -> Option<&'a str> {
|
||||
let Some(input_row) = self.input_properties_row(&node_id, index, network_path) else {
|
||||
log::error!("Could not get node_metadata in input_description");
|
||||
return None;
|
||||
};
|
||||
value.as_str()
|
||||
let description = input_row.input_description.as_str();
|
||||
if !description.is_empty() && description != "TODO" {
|
||||
Some(description)
|
||||
} else {
|
||||
let node_definition = resolve_document_node_type(self.reference(&node_id, network_path)?.as_ref()?)?;
|
||||
let rows = &node_definition.node_template.persistent_node_metadata.input_properties;
|
||||
|
||||
rows.get(index).map(|row| row.input_description.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_properties_row(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&PropertiesRow> {
|
||||
|
|
@ -1155,6 +1168,7 @@ impl NodeNetworkInterface {
|
|||
input_row.input_data.get(field)
|
||||
}
|
||||
|
||||
/// Returns the display name of the node. If the display name is empty, it will return "Untitled Node" or "Untitled Layer" depending on the node type.
|
||||
pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
|
||||
let is_layer = self
|
||||
.node_metadata(node_id, network_path)
|
||||
|
|
@ -1184,9 +1198,11 @@ impl NodeNetworkInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the description of the node, or an empty string if it is not set.
|
||||
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
|
||||
self.get_node_definition(network_path, *node_id)
|
||||
.and_then(|node_definition| Some(node_definition.description.to_string()))
|
||||
.map(|node_definition| node_definition.description.to_string())
|
||||
.filter(|description| description != "TODO")
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
|
@ -3394,7 +3410,7 @@ impl NodeNetworkInterface {
|
|||
}
|
||||
|
||||
/// Inserts a new input at insert index. If the insert index is -1 it is inserted at the end. The output_name is used by the encapsulating node.
|
||||
pub fn add_import(&mut self, default_value: TaggedValue, exposed: bool, insert_index: isize, input_name: &str, network_path: &[NodeId]) {
|
||||
pub fn add_import(&mut self, default_value: TaggedValue, exposed: bool, insert_index: isize, input_name: &str, input_description: &str, network_path: &[NodeId]) {
|
||||
let mut encapsulating_network_path = network_path.to_vec();
|
||||
let Some(node_id) = encapsulating_network_path.pop() else {
|
||||
log::error!("Cannot add import for document network");
|
||||
|
|
@ -3428,10 +3444,11 @@ impl NodeNetworkInterface {
|
|||
log::error!("Could not get node_metadata in insert_input");
|
||||
return;
|
||||
};
|
||||
let new_input = (input_name, input_description).into();
|
||||
if insert_index == -1 {
|
||||
node_metadata.persistent_metadata.input_properties.push((input_name, "TODO").into());
|
||||
node_metadata.persistent_metadata.input_properties.push(new_input);
|
||||
} else {
|
||||
node_metadata.persistent_metadata.input_properties.insert(insert_index as usize, (input_name, "TODO").into());
|
||||
node_metadata.persistent_metadata.input_properties.insert(insert_index as usize, new_input);
|
||||
}
|
||||
|
||||
// Clear the reference to the nodes definition
|
||||
|
|
@ -4410,7 +4427,7 @@ impl NodeNetworkInterface {
|
|||
self.unload_node_click_targets(node_id, network_path);
|
||||
}
|
||||
|
||||
pub fn set_import_export_name(&mut self, name: String, index: ImportOrExport, network_path: &[NodeId]) {
|
||||
pub fn set_import_export_name(&mut self, mut name: String, index: ImportOrExport, network_path: &[NodeId]) {
|
||||
let Some(encapsulating_node) = self.encapsulating_node_metadata_mut(network_path) else {
|
||||
log::error!("Could not get encapsulating network in set_import_export_name");
|
||||
return;
|
||||
|
|
@ -4422,12 +4439,9 @@ impl NodeNetworkInterface {
|
|||
log::error!("Could not get input properties in set_import_export_name");
|
||||
return;
|
||||
};
|
||||
// Only return true if the previous value is the same as the current value
|
||||
input_properties
|
||||
.input_data
|
||||
.insert("input_name".to_string(), json!(name))
|
||||
.filter(|val| val.as_str().is_some_and(|old_name| *old_name == name))
|
||||
.is_none()
|
||||
// Only return false if the previous value is the same as the current value
|
||||
std::mem::swap(&mut input_properties.input_name, &mut name);
|
||||
input_properties.input_name != name
|
||||
}
|
||||
ImportOrExport::Export(export_index) => {
|
||||
let Some(export_name) = encapsulating_node.persistent_metadata.output_names.get_mut(export_index) else {
|
||||
|
|
@ -6178,6 +6192,10 @@ pub struct PropertiesRow {
|
|||
// An input can override a widget, which would otherwise be automatically generated from the type
|
||||
// The string is the identifier to the widget override function stored in INPUT_OVERRIDES
|
||||
pub widget_override: Option<String>,
|
||||
#[serde(skip)]
|
||||
pub input_name: String,
|
||||
#[serde(skip)]
|
||||
pub input_description: String,
|
||||
}
|
||||
|
||||
impl Default for PropertiesRow {
|
||||
|
|
@ -6195,19 +6213,29 @@ impl From<(&str, &str)> for PropertiesRow {
|
|||
impl PropertiesRow {
|
||||
pub fn with_override(input_name: &str, input_description: &str, widget_override: WidgetOverride) -> Self {
|
||||
let mut input_data = HashMap::new();
|
||||
input_data.insert("input_name".to_string(), Value::String(input_name.to_string()));
|
||||
input_data.insert("input_description".to_string(), Value::String(input_description.to_string()));
|
||||
let input_name = input_name.to_string();
|
||||
let input_description = input_description.to_string();
|
||||
|
||||
match widget_override {
|
||||
WidgetOverride::None => PropertiesRow { input_data, widget_override: None },
|
||||
WidgetOverride::None => PropertiesRow {
|
||||
input_data,
|
||||
widget_override: None,
|
||||
input_name,
|
||||
input_description,
|
||||
},
|
||||
WidgetOverride::Hidden => PropertiesRow {
|
||||
input_data,
|
||||
widget_override: Some("hidden".to_string()),
|
||||
input_name,
|
||||
input_description,
|
||||
},
|
||||
WidgetOverride::String(string_properties) => {
|
||||
input_data.insert("string_properties".to_string(), Value::String(string_properties));
|
||||
PropertiesRow {
|
||||
input_data,
|
||||
widget_override: Some("string".to_string()),
|
||||
input_name,
|
||||
input_description,
|
||||
}
|
||||
}
|
||||
WidgetOverride::Number(mut number_properties) => {
|
||||
|
|
@ -6235,6 +6263,8 @@ impl PropertiesRow {
|
|||
PropertiesRow {
|
||||
input_data,
|
||||
widget_override: Some("number".to_string()),
|
||||
input_name,
|
||||
input_description,
|
||||
}
|
||||
}
|
||||
WidgetOverride::Vec2(vec2_properties) => {
|
||||
|
|
@ -6247,11 +6277,15 @@ impl PropertiesRow {
|
|||
PropertiesRow {
|
||||
input_data,
|
||||
widget_override: Some("vec2".to_string()),
|
||||
input_name,
|
||||
input_description,
|
||||
}
|
||||
}
|
||||
WidgetOverride::Custom(lambda_name) => PropertiesRow {
|
||||
input_data,
|
||||
widget_override: Some(lambda_name),
|
||||
input_name,
|
||||
input_description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -6366,7 +6400,7 @@ impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMe
|
|||
.as_ref()
|
||||
.and_then(|reference| resolve_document_node_type(reference))
|
||||
.map(|definition| definition.node_template.persistent_node_metadata.input_properties.clone())
|
||||
.unwrap_or(old.input_names.into_iter().map(|name| (name.as_str(), "TODO").into()).collect());
|
||||
.unwrap_or(old.input_names.into_iter().map(|name| (name.as_str(), "").into()).collect());
|
||||
|
||||
DocumentNodePersistentMetadata {
|
||||
reference: old.reference,
|
||||
|
|
|
|||
|
|
@ -842,7 +842,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
document.network_interface.replace_implementation(node_id, network_path, new_image_node.document_node.implementation);
|
||||
|
||||
// Insert a new empty input for the image
|
||||
document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", &[*node_id]);
|
||||
document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", "", &[*node_id]);
|
||||
document.network_interface.set_reference(node_id, network_path, Some("Image".to_string()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -800,7 +800,7 @@ impl EditorHandle {
|
|||
document
|
||||
.network_interface
|
||||
.replace_implementation(&node_id, &[], DocumentNodeImplementation::proto("graphene_core::ToArtboardNode"));
|
||||
document.network_interface.add_import(TaggedValue::IVec2(glam::IVec2::default()), false, 2, "", &[node_id]);
|
||||
document.network_interface.add_import(TaggedValue::IVec2(glam::IVec2::default()), false, 2, "", "", &[node_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ pub mod types {
|
|||
pub type SignedPercentage = f64;
|
||||
/// -180° - 180°
|
||||
pub type Angle = f64;
|
||||
/// Ends in the unit of x
|
||||
pub type Multiplier = f64;
|
||||
/// Non-negative integer with px unit
|
||||
pub type PixelLength = f64;
|
||||
/// Non-negative
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::misc::CentroidType;
|
|||
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
||||
use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable};
|
||||
use crate::instances::{InstanceMut, Instances};
|
||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Percentage, PixelLength, SeedValue};
|
||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, SeedValue};
|
||||
use crate::renderer::GraphicElementRendered;
|
||||
use crate::transform::{Footprint, Transform, TransformMut};
|
||||
use crate::vector::PointDomain;
|
||||
|
|
@ -51,7 +51,6 @@ async fn assign_colors<T>(
|
|||
gradient: GradientStops,
|
||||
/// Whether to reverse the gradient.
|
||||
reverse: bool,
|
||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_randomize")]
|
||||
/// Whether to randomize the color selection for each element from throughout the gradient.
|
||||
randomize: bool,
|
||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
|
||||
|
|
@ -268,18 +267,33 @@ where
|
|||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
#[node_macro::node(name("Copy to Points"), category("Vector"), path(graphene_core::vector))]
|
||||
async fn copy_to_points<I: 'n + Send>(
|
||||
_: impl Ctx,
|
||||
points: VectorDataTable,
|
||||
#[expose]
|
||||
/// Artwork to be copied and placed at each point.
|
||||
#[implementations(VectorDataTable, GraphicGroupTable)]
|
||||
instance: Instances<I>,
|
||||
#[default(1)] random_scale_min: f64,
|
||||
#[default(1)] random_scale_max: f64,
|
||||
/// Minimum range of randomized sizes given to each instance.
|
||||
#[default(1)]
|
||||
#[range((0., 2.))]
|
||||
#[unit("x")]
|
||||
random_scale_min: Multiplier,
|
||||
/// Maximum range of randomized sizes given to each instance.
|
||||
#[default(1)]
|
||||
#[range((0., 2.))]
|
||||
#[unit("x")]
|
||||
random_scale_max: Multiplier,
|
||||
/// Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes).
|
||||
#[range((-50., 50.))]
|
||||
random_scale_bias: f64,
|
||||
/// Seed to determine unique variations on all the randomized instance sizes.
|
||||
random_scale_seed: SeedValue,
|
||||
/// Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise.
|
||||
#[range((0., 360.))]
|
||||
random_rotation: Angle,
|
||||
/// Seed to determine unique variations on all the randomized instance angles.
|
||||
random_rotation_seed: SeedValue,
|
||||
) -> GraphicGroupTable
|
||||
where
|
||||
|
|
@ -1245,7 +1259,7 @@ async fn position_on_path(
|
|||
/// The path to traverse.
|
||||
vector_data: VectorDataTable,
|
||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||
progress: f64,
|
||||
progress: Fraction,
|
||||
/// Swap the direction of the path.
|
||||
reverse: bool,
|
||||
/// Traverse the path using each segment's Bézier curve parameterization instead of the Euclidean distance. Faster to compute but doesn't respect actual distances.
|
||||
|
|
@ -1277,7 +1291,7 @@ async fn tangent_on_path(
|
|||
/// The path to traverse.
|
||||
vector_data: VectorDataTable,
|
||||
/// The factor from the start to the end of the path, 0–1 for one subpath, 1–2 for a second subpath, and so on.
|
||||
progress: f64,
|
||||
progress: Fraction,
|
||||
/// Swap the direction of the path.
|
||||
reverse: bool,
|
||||
/// Traverse the path using each segment's Bézier curve parameterization instead of the Euclidean distance. Faster to compute but doesn't respect actual distances.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue