diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index c79bd1b7..3269a452 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -1867,6 +1867,20 @@ fn static_nodes() -> Vec { properties: node_properties::repeat_properties, ..Default::default() }, + DocumentNodeType { + name: "Circular Repeat", + category: "Vector", + identifier: NodeImplementation::proto("graphene_core::vector::CircularRepeatNode<_, _, _>"), + inputs: vec![ + DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true), + DocumentInputType::value("Rotation Offset", TaggedValue::F32(0.), false), + DocumentInputType::value("Radius", TaggedValue::F32(5.), false), + DocumentInputType::value("Count", TaggedValue::U32(10), false), + ], + outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)], + properties: node_properties::circle_repeat_properties, + ..Default::default() + }, DocumentNodeType { name: "Image Segmentation", category: "Image Adjustments", diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index b1c385c1..31d0566b 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -1680,6 +1680,14 @@ pub fn repeat_properties(document_node: &DocumentNode, node_id: NodeId, _context vec![direction, LayoutGroup::Row { widgets: count }] } +pub fn circle_repeat_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let angle_radius = number_widget(document_node, node_id, 1, "Rotation Offset", NumberInput::default(), true); + let radius = number_widget(document_node, node_id, 2, "Radius", NumberInput::default().min(0.), true); + let count = number_widget(document_node, node_id, 3, "Count", NumberInput::default().min(1.), true); + + vec![LayoutGroup::Row { widgets: angle_radius }, LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: count }] +} + /// Fill Node Widgets LayoutGroup pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let fill_type_index = 1; diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 638de2a0..40f3216c 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -89,7 +89,41 @@ fn repeat_vector_data(mut vector_data: VectorData, direction: DVec2, count: u32) let direction = inverse.transform_vector2(direction); for i in 0..count { let transform = DAffine2::from_translation(direction * i as f64); - log::debug!("transform: {:?}", transform); + for mut subpath in subpaths.clone() { + subpath.apply_transform(transform); + new_subpaths.push(subpath); + } + } + + vector_data.subpaths = new_subpaths; + vector_data +} + +#[derive(Debug, Clone, Copy)] +pub struct CircularRepeatNode { + rotation_offset: RotationOffset, + radius: Radius, + count: Count, +} + +#[node_macro::node_fn(CircularRepeatNode)] +fn circular_repeat_vector_data(mut vector_data: VectorData, rotation_offset: f32, radius: f32, count: u32) -> VectorData { + // repeat the vector data + let VectorData { subpaths, transform, .. } = &vector_data; + + let mut new_subpaths: Vec> = Vec::with_capacity(subpaths.len() * count as usize); + + let bounding_box = vector_data.bounding_box().unwrap(); + let center = (bounding_box[0] + bounding_box[1]) / 2.; + + //let inverse = transform.inverse(); + //let radius_transform = DAffine2::from_translation(DVec2::new(0., radius as f64)); + let base_transform = DVec2::new(0., radius as f64) - center; + + for i in 0..count { + let angle = (2. * std::f64::consts::PI / count as f64) * i as f64 + rotation_offset.to_radians() as f64; + let rotation = DAffine2::from_angle(angle); + let transform = DAffine2::from_translation(center) * rotation * DAffine2::from_translation(base_transform); for mut subpath in subpaths.clone() { subpath.apply_transform(transform); new_subpaths.push(subpath); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index a9c83406..3938d410 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -567,6 +567,7 @@ fn node_registry() -> HashMap, input: VectorData, params: [graphene_core::vector::style::FillType, Option, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, Option)>]), register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option, f32, Vec, f32, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f32]), register_node!(graphene_core::vector::RepeatNode<_, _>, input: VectorData, params: [DVec2, u32]), + register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f32, f32, u32]), register_node!(graphene_core::vector::generator_nodes::UnitCircleGenerator, input: (), params: []), register_node!( graphene_core::vector::generator_nodes::PathGenerator<_>,