diff --git a/node-graph/libraries/vector-types/src/subpath/core.rs b/node-graph/libraries/vector-types/src/subpath/core.rs index 846d053f6e..ba63782f57 100644 --- a/node-graph/libraries/vector-types/src/subpath/core.rs +++ b/node-graph/libraries/vector-types/src/subpath/core.rs @@ -379,6 +379,43 @@ impl Subpath { Self::new(manipulator_groups, false) } + + pub fn new_heart(center: DVec2, radius: f64, cleft_angle: f64, tip_angle: f64, bulb_height: f64, bulb_expand: f64, cleft_depth: f64, tip_depth: f64) -> Self { + // Anchors + let top_anchor = center - DVec2::new(0., radius * cleft_depth); + let bottom_anchor = center + DVec2::new(0., radius * tip_depth); + + let side_x = radius; + // Offset is for less squat shape (-0.25). + let right_anchor = DVec2::new( + center.x + side_x * (1. + bulb_expand), + (center.y + radius * (bulb_height - 0.25)) * (1. + bulb_expand) - center.y * bulb_expand, + ); + let left_anchor = DVec2::new( + center.x - side_x * (1. + bulb_expand), + (center.y + radius * (bulb_height - 0.25)) * (1. + bulb_expand) - center.y * bulb_expand, + ); + + // Handle Directions + let (top_sin, top_cos) = cleft_angle.sin_cos(); + let top_dir = DVec2::new(top_sin, -top_cos) * radius * 0.5; + + let (bot_sin, bot_cos) = tip_angle.sin_cos(); + let bot_dir = DVec2::new(bot_sin, -bot_cos) * radius * 0.45; + + // Side Tangents + let side_up = DVec2::new(0., -radius * 0.4); + let side_down = DVec2::new(0., radius * 0.5); + + let manipulator_groups = vec![ + ManipulatorGroup::new(top_anchor, Some(top_anchor + DVec2::new(-top_dir.x, top_dir.y)), Some(top_anchor + top_dir)), + ManipulatorGroup::new(right_anchor, Some(right_anchor + side_up), Some(right_anchor + side_down)), + ManipulatorGroup::new(bottom_anchor, Some(bottom_anchor + bot_dir), Some(bottom_anchor + DVec2::new(-bot_dir.x, bot_dir.y))), + ManipulatorGroup::new(left_anchor, Some(left_anchor + side_down), Some(left_anchor + side_up)), + ]; + + Self::new(manipulator_groups, true) + } } pub fn calculate_growth_factor(a: f64, turns: f64, outer_radius: f64, spiral_type: SpiralType) -> f64 { diff --git a/node-graph/nodes/vector/src/generator_nodes.rs b/node-graph/nodes/vector/src/generator_nodes.rs index 098b1782ac..0fca3589b1 100644 --- a/node-graph/nodes/vector/src/generator_nodes.rs +++ b/node-graph/nodes/vector/src/generator_nodes.rs @@ -145,6 +145,33 @@ fn rectangle( corner_radius.generate(DVec2::new(width, height), clamped) } +/// Generates a heart shape with adjustable proportions. +#[node_macro::node(category("Vector: Shape"))] +fn heart( + _: impl Ctx, + _primary: (), + #[unit(" px")] + #[default(50.)] + radius: f64, + #[default(30.)] cleft_angle: f64, + #[default(45.)] tip_angle: f64, + #[default(0.4)] cleft_depth: f64, + #[default(0.9)] tip_depth: f64, + #[default(0.)] bulb_height: f64, + #[default(0.)] bulb_expand: f64, +) -> Table { + Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_heart( + DVec2::splat(0.), + radius, + cleft_angle.to_radians(), + tip_angle.to_radians(), + bulb_height, + bulb_expand, + cleft_depth, + tip_depth, + ))) +} + /// Generates an regular polygon shape like a triangle, square, pentagon, hexagon, heptagon, octagon, or any higher n-gon. #[node_macro::node(category("Vector: Shape"))] fn regular_polygon(