Skip to content

Add teardrop and heard shape to the predefined shapes #3998

Draft
LennartKDeveloper wants to merge 5 commits intoGraphiteEditor:masterfrom
LennartKDeveloper:adding-teardrop-and-heart-shape-tools
Draft

Add teardrop and heard shape to the predefined shapes #3998
LennartKDeveloper wants to merge 5 commits intoGraphiteEditor:masterfrom
LennartKDeveloper:adding-teardrop-and-heart-shape-tools

Conversation

@LennartKDeveloper
Copy link
Copy Markdown

Closes #3419

Screen.Recording.2026-04-03.at.13.20.45.mov
  • This PR adds teardrop and heart shapes to the shape tool options. It fully integrates both shapes and implements them in core.rs.

  • Additionally, the HANDLE_OFFSET_FACTOR constant is extracted and properly documented.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 10 files

Confidence score: 2/5

  • There is clear functional risk in shape handling: editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs checks get_spiral_id for Teardrop/Heart, so the correct gizmo handlers may not be selected during editing.
  • editor/src/messages/tool/common_functionality/graph_modification_utils.rs has a concrete node-type mismatch (get_teardrop_id resolving spiral), which can propagate wrong behavior anywhere teardrop detection is expected.
  • editor/src/messages/tool/common_functionality/shapes/teardrop_shape.rs can divide by zero on zero-sized drags, producing inf/NaN transforms and likely unstable user-facing geometry behavior.
  • Pay close attention to editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs, editor/src/messages/tool/common_functionality/graph_modification_utils.rs, editor/src/messages/tool/common_functionality/shapes/teardrop_shape.rs - identifier mix-ups and zero-drag math need correction before merge.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs">

<violation number="1" location="editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs:241">
P1: Teardrop and Heart gizmo detection incorrectly checks `get_spiral_id`, so new shape handlers are not selected correctly.</violation>
</file>

<file name="editor/src/messages/tool/common_functionality/shapes/teardrop_shape.rs">

<violation number="1" location="editor/src/messages/tool/common_functionality/shapes/teardrop_shape.rs:139">
P1: Division by zero in teardrop scale computation can generate `inf`/`NaN` transforms for zero-sized drags.</violation>
</file>

<file name="editor/src/messages/tool/common_functionality/graph_modification_utils.rs">

<violation number="1" location="editor/src/messages/tool/common_functionality/graph_modification_utils.rs:381">
P1: `get_teardrop_id` looks up the spiral identifier, so teardrop node detection returns the wrong node type.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Keavon
Copy link
Copy Markdown
Member

Keavon commented Apr 3, 2026

Thanks for the contribution. As it stands, this will not work because it requires properly parametric controls over all the unique variations on each of these shapes. I see only a radius is implemented, which is insufficient to give full artistic control over the forms of the shapes. Additionally, the tool needs to draw, not just place, the shapes. I am marking this as a draft to give you time to make these updates. Note that this is a research-focused topic as you'll have to find high-quality shape parameters and algorithms to create them. If they do not meet our requirements for intuitive and flexible artistic control, we will unfortunately be unable to merge the feature. Good luck!

@Keavon Keavon marked this pull request as draft April 3, 2026 12:08
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Heart and Teardrop shapes to the editor. Several critical issues were identified, including copy-paste errors in the gizmo manager and graph modification utilities where spiral identifiers were incorrectly used for the new shapes. Additionally, the gizmo handlers for both shapes appear to be copied from other tools, leading to inappropriate UI controls, and the generator nodes currently hardcode parameters that should be exposed. I have provided suggestions to fix the identifier lookups and the tool creation calls, and I recommend refactoring the gizmo handlers and node parameters to better suit the new shapes.

Comment on lines +241 to +246
if graph_modification_utils::get_spiral_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Teardrop(TeardropGizmoHandler::default()));
}
// Heart
if graph_modification_utils::get_spiral_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Heart(HeartGizmoHandler::default()));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There seems to be a copy-paste error here. You are using get_spiral_id to detect Teardrop and Heart shapes. You should use get_teardrop_id and get_heart_id respectively.

Suggested change
if graph_modification_utils::get_spiral_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Teardrop(TeardropGizmoHandler::default()));
}
// Heart
if graph_modification_utils::get_spiral_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Heart(HeartGizmoHandler::default()));
if graph_modification_utils::get_teardrop_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Teardrop(TeardropGizmoHandler::default()));
}
// Heart
if graph_modification_utils::get_heart_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Heart(HeartGizmoHandler::default()));
}

Comment on lines +380 to +386
pub fn get_teardrop_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::spiral::IDENTIFIER))
}

pub fn get_heart_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::spiral::IDENTIFIER))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

It looks like there's a copy-paste error in these new functions. Both get_teardrop_id and get_heart_id are checking for the spiral identifier. They should be checking for their respective shape identifiers.

Suggested change
pub fn get_teardrop_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::spiral::IDENTIFIER))
}
pub fn get_heart_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::spiral::IDENTIFIER))
}
pub fn get_teardrop_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::teardrop::IDENTIFIER))
}
pub fn get_heart_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::heart::IDENTIFIER))
}

Comment on lines +21 to +23
pub struct HeartGizmoHandler {
number_of_points_dial: NumberOfPointsDial,
point_radius_handle: PointRadiusHandle,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The HeartGizmoHandler seems to be using gizmos for number_of_points_dial and point_radius_handle, which appear to be copied from another shape handler. The number_of_points_dial is not applicable to a heart shape. While point_radius_handle could potentially be used to control a property like the spike length, the heart generator node currently doesn't expose such a parameter. This will lead to incorrect and confusing behavior for the user. Please implement gizmos appropriate for the heart shape.

Comment on lines +21 to +23
pub struct TeardropGizmoHandler {
number_of_points_dial: NumberOfPointsDial,
point_radius_handle: PointRadiusHandle,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The TeardropGizmoHandler seems to be using gizmos for number_of_points_dial and point_radius_handle, which appear to be copied from another shape handler. The number_of_points_dial is not applicable to a teardrop shape. While point_radius_handle could potentially be used to control a property like the spike length, the teardrop generator node currently doesn't expose such a parameter. This will lead to incorrect and confusing behavior for the user. Please implement gizmos appropriate for the teardrop shape.

Comment on lines +191 to +200
fn teardrop(
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
) -> Table<Vector> {
let radius = radius.abs();
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_teardrop(DVec2::ZERO, radius, radius * 2.0)))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The teardrop node hardcodes the spike_radius to radius * 2.0. This limits the shape's customizability. It would be better to expose spike_radius as a parameter to this node. This would also allow a gizmo to control it, which seems to be the intention with the point_radius_handle in teardrop_shape.rs.

Comment on lines +204 to +213
fn heart(
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
) -> Table<Vector> {
let radius = radius.abs();
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_heart(DVec2::ZERO, radius, radius * 2.0)))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The heart node hardcodes the spike_radius to radius * 2.0. This limits the shape's customizability. It would be better to expose spike_radius as a parameter to this node. This would also allow a gizmo to control it, which seems to be the intention with the point_radius_handle in heart_shape.rs.

Comment on lines +130 to +131
// TODO: We need to determine how to allow the polygon node to make irregular shapes
update_radius_sign(end, start, layer, document, responses);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This TODO comment seems to be a leftover from another file. Also, update_radius_sign is likely not applicable to the heart shape as it's intended for flipping regular polygons and stars. Please remove the comment and verify if the function call is necessary.

Comment on lines +130 to +131
// TODO: We need to determine how to allow the polygon node to make irregular shapes
update_radius_sign(end, start, layer, document, responses);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This TODO comment seems to be a leftover from another file. Also, update_radius_sign is likely not applicable to the teardrop shape as it's intended for flipping regular polygons and stars. Please remove the comment and verify if the function call is necessary.

Comment on lines +954 to +955
ShapeType::Teardrop => Teardrop::create_node(tool_options.vertices), // FIXME
ShapeType::Heart => Heart::create_node(tool_options.vertices), // FIXME
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

As noted by your FIXME comments, Teardrop::create_node and Heart::create_node are incorrectly passed tool_options.vertices. The _vertices parameter in those functions is unused and should be removed. The calls here should be updated accordingly.

Suggested change
ShapeType::Teardrop => Teardrop::create_node(tool_options.vertices), // FIXME
ShapeType::Heart => Heart::create_node(tool_options.vertices), // FIXME
ShapeType::Teardrop => Teardrop::create_node(),
ShapeType::Heart => Heart::create_node(),

@LennartKDeveloper
Copy link
Copy Markdown
Author

Screen.Recording.2026-04-03.at.18.00.48.mov

@Keavon Thank you very much for the feedback. I fixed the issues Gemini suggested and considered how the teardrop and heart shapes should be mutable.

  • The teardrop is now defined by the body radius and tail_length (distance from the spike to the center).
  • The heart is defined by height, width, and the roundness of the two top lobes.

To be honest, I didn’t fully understand what you meant by “drawable” in the context of these shapes. Could you clarify that part for me?

I hope this is moving in the direction you had in mind.
I’ll spend some more time researching how to further refine the shape parameters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teardrop and Heart Shape Tools

2 participants