Add Area and Centroid nodes (#1749)

* initial attempt for area node

* allow node preview for more types

* make AreaNode sync and add CentroidNode

* cargo fmt

* preview of DVec2

* make the nodes async again

* use segment domain instead of region domain

* modify the check for linearity

* create a limit for area in centroid calculation

* cargo fmt

* reverse unnecessary changes

* add threshold to area calculation too.

* handle zero area edge case

* add todo comment

* implement 1D centroid and use it as fallback

* formatting floats to skip last zero

* add Centroid Type radio button to Centroid Node

* rename docs to use area and perimeter centroid

* add web demos for perimeter centroid

* add tests for perimeter centroid

* add fallback to use average of points

* Fix for broken area

* missing fixes

* Code review and rename Perimeter Centroid to Length Centroid

* Use dummy footprint in Area and Centroid nodes

* add doc and todo to clarify when `is_linear` fails

* use epsilon instead of zero

---------

Co-authored-by: 0hypercube <0hypercube@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
Elbert Ronnie
2024-05-22 13:11:11 -07:00
committed by GitHub
co-authored by 0hypercube Keavon Chambers Dennis Kobert
parent 4587457bfa
commit 5a1c171fc3
18 changed files with 443 additions and 43 deletions
@@ -2598,6 +2598,29 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
properties: node_properties::node_no_properties,
..Default::default()
},
DocumentNodeDefinition {
name: "Area",
category: "Vector",
implementation: DocumentNodeImplementation::proto("graphene_core::vector::AreaNode<_>"),
inputs: vec![DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true)],
outputs: vec![DocumentOutputType::new("Output", FrontendGraphDataType::Number)],
properties: node_properties::node_no_properties,
manual_composition: Some(concrete!(())),
..Default::default()
},
DocumentNodeDefinition {
name: "Centroid",
category: "Vector",
implementation: DocumentNodeImplementation::proto("graphene_core::vector::CentroidNode<_, _>"),
inputs: vec![
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Centroid Type", TaggedValue::CentroidType(graphene_core::vector::misc::CentroidType::Area), false),
],
outputs: vec![DocumentOutputType::new("Output", FrontendGraphDataType::Vector)],
properties: node_properties::centroid_properties,
manual_composition: Some(concrete!(())),
..Default::default()
},
DocumentNodeDefinition {
name: "Morph",
category: "Vector",
@@ -14,6 +14,7 @@ use graphene_core::raster::{
SelectiveColorChoice,
};
use graphene_core::text::Font;
use graphene_core::vector::misc::CentroidType;
use graphene_core::vector::style::{FillType, GradientType, LineCap, LineJoin};
use glam::{DVec2, IVec2, UVec2};
@@ -882,6 +883,39 @@ fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
LayoutGroup::Row { widgets }
}
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]
{
let entries = vec![
RadioEntryData::new("area")
.label("Area")
.tooltip("Center of mass for the interior area of the shape")
.on_update(update_value(move |_| TaggedValue::CentroidType(CentroidType::Area), node_id, index))
.on_commit(commit_value),
RadioEntryData::new("length")
.label("Length")
.tooltip("Center of mass for the perimeter arc length of the shape")
.on_update(update_value(move |_| TaggedValue::CentroidType(CentroidType::Length), node_id, index))
.on_commit(commit_value),
];
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
RadioInput::new(entries)
.selected_index(match centroid_type {
CentroidType::Area => Some(0),
CentroidType::Length => Some(1),
})
.widget_holder(),
]);
}
LayoutGroup::Row { widgets }
}
pub fn levels_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let input_shadows = number_widget(document_node, node_id, 1, "Shadows", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let input_midtones = number_widget(document_node, node_id, 2, "Midtones", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
@@ -2438,3 +2472,9 @@ pub fn image_color_palette(document_node: &DocumentNode, node_id: NodeId, _conte
vec![LayoutGroup::Row { widgets: size }]
}
pub fn centroid_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let centroid_type = centroid_widget(document_node, node_id, 1);
vec![centroid_type]
}
+1
View File
@@ -638,6 +638,7 @@ impl NodeGraphExecutor {
TaggedValue::Bool(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::String(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::F64(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::DVec2(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::VectorData(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::GraphicGroup(render_object) => Self::debug_render(render_object, transform, responses),