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:
Dennis Kobert 2024-08-01 13:42:15 +02:00 committed by GitHub
parent 44ffb635e9
commit a6af5d4831
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 224 additions and 466 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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);
});
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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());

View File

@ -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))
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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)]),

View File

@ -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 }
}
}

View File

@ -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()
},
),

View File

@ -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> {

View File

@ -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();

View File

@ -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()
},