Add path-bool library (#1952)

* Add path-bool library

* Cleanup code

* Cargo format

* Integrate boolean ops into graphite

* Add test for editor crash

* Fix edge sort floating point instability

* Add unit test for red-dress failure

* Backport tests and aux functions

* Use curvature based sorting

* Convert linear cubic splines to line segments

* Deduplicate reversed path segments

* Fix epsilon for empty segments

* Remove parameter based intersection pruning

* Add support for reversed paths

* Add benchmark infrastructure

* Add intersection benchmark

* Add recursion bound

* Implement support for overlapping path segments

* Remove rouge prinln

* Fix sorting for bezier segments with one control point at the start of the segment

* Cleanup log statements

* Directly translate graphite paths to Path segments

* Round data before passing it to path_bool

* Fix flag_faces traversal order

* Add test for white dots in bottom right of painted dreams

* Make rounding configurable

* Update demo artwork to remove manual path modifications

* Convert from path segments to manipulator groups directly

* Remove dead code

* Fix clippy lints

* Replace functions in path segment with methods and add documentation

* Add more documentation

* Close subpaths

* Reorganize files and add README.md

* Add license information

* Code review

* Fix license info

* Adopt new node macro and fix demo artwork

* Close subpaths with Z

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert
2024-09-21 02:06:43 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 2febbfd698
commit 3eb98c6d6d
165 changed files with 5990 additions and 78 deletions
@@ -145,15 +145,15 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) {
let shape = resolve_document_node_type("Shape")
.expect("Shape node does not exist")
let path = resolve_document_node_type("Path")
.expect("Path node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))]);
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
let shape_id = NodeId(generate_uuid());
self.network_interface.insert_node(shape_id, shape, &[]);
self.network_interface.insert_node(shape_id, path, &[]);
self.network_interface.move_node_to_chain_start(&shape_id, layer, &[]);
let transform_id = NodeId(generate_uuid());
@@ -2159,12 +2159,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(VectorData), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::vector::BooleanOperationNode")),
manual_composition: Some(generic!(T)),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode")),
manual_composition: Some(concrete!(Footprint)),
manual_composition: Some(generic!(T)),
..Default::default()
},
]
@@ -2268,17 +2268,10 @@ pub(crate) fn generate_node_properties(document_node: &DocumentNode, node_id: No
}
pub(crate) fn boolean_operation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let group_of_paths_index = 0;
let operation_index = 1;
let mut widgets = start_widgets(document_node, node_id, group_of_paths_index, "Group of Paths", FrontendGraphDataType::Graphic, true);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("The output of a layer stack, which contains all elements to operate on").widget_holder());
let operation = boolean_operation_radio_buttons(document_node, node_id, operation_index, "Operation", true);
vec![LayoutGroup::Row { widgets }, operation]
vec![operation]
}
pub(crate) fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {