Graphite icon indicating copy to clipboard operation
Graphite copied to clipboard

Fix the Bevel node so it applies a consistent bevel size regardless of angle

Open MohdMohsin97 opened this issue 10 months ago • 16 comments

Previously algorithm only spilt the line from both side with the give length.

411837396-7bfb998d-604c-45d5-90e4-c607c3144855

Update the bevel algorithm for straight line only to give same bevel length.

Screenshot 2025-02-15 222335

Issue #2281

MohdMohsin97 avatar Feb 15 '25 17:02 MohdMohsin97

Thanks!

Update the bevel algorithm for straight line only to give same bevel length.

Based on this sentence, do I presume correctly that this means curved segments are left for future work?

Keavon avatar Feb 16 '25 09:02 Keavon

Based on this sentence, do I presume correctly that this means curved segments are left for future work?

Yes, currently it only work for straight line only, I'll try to make it work for curve line also.

MohdMohsin97 avatar Feb 17 '25 09:02 MohdMohsin97

!build

Keavon avatar Feb 20 '25 11:02 Keavon

📦 Build Complete for 2900271f1055f7f313d9bdab584f6524c0c369de
https://93383e91.graphite.pages.dev

github-actions[bot] avatar Feb 20 '25 11:02 github-actions[bot]

This doesn't seem to have the expected behavior after passing a certain size, see video:

https://github.com/user-attachments/assets/09c2e234-0601-4c36-a896-f02394bd7f66

Could that be fixed, please?

Keavon avatar Feb 20 '25 11:02 Keavon

I attempted to resolve the algorithm. Is this the expected behavior?

https://github.com/user-attachments/assets/ef92e6d4-6b5a-4122-b864-b7800810ec7e

MohdMohsin97 avatar Feb 20 '25 16:02 MohdMohsin97

!build

Keavon avatar Feb 20 '25 22:02 Keavon

📦 Build Complete for 8c2de3bd75e9aa2280861e0460efe0daee167115
https://77b9d277.graphite.pages.dev

github-actions[bot] avatar Feb 20 '25 22:02 github-actions[bot]

https://github.com/user-attachments/assets/83855615-c988-4326-aa63-de9d0b2bec44

Keavon avatar Feb 20 '25 22:02 Keavon

!build

Keavon avatar Mar 11 '25 06:03 Keavon

📦 Build Complete for 8001050783f57e32ececa6af2264e52cdfd1514b
https://764db1b7.graphite.pages.dev

github-actions[bot] avatar Mar 11 '25 07:03 github-actions[bot]

https://github.com/user-attachments/assets/b2437b7b-8393-4edb-a8f2-b1971f0d52dc

Fixes the curve part

MohdMohsin97 avatar Mar 15 '25 09:03 MohdMohsin97

!build

Keavon avatar Mar 15 '25 09:03 Keavon

📦 Build Complete for f716b8ba2293343b03901d41f1886fc303eec8cc
https://9f507811.graphite.pages.dev

github-actions[bot] avatar Mar 15 '25 09:03 github-actions[bot]

Fixes the layer transformed issue.

https://github.com/user-attachments/assets/4c77cdbc-381a-4dd3-a76b-2c2394d3fe96

MohdMohsin97 avatar Apr 27 '25 09:04 MohdMohsin97

This has some pretty significant performance limitations due to the calls of .evaluate(TValue::Euclidean(...)). But the solution is probably just to adopt Kurbo. (CC: @indierusty, who has been working on such refactors.)

Keavon avatar May 01 '25 11:05 Keavon

!build

Keavon avatar Jul 09 '25 02:07 Keavon

📦 Build Complete for 83a1e1fd1aba4c6e84e2a898ad7df5451d1c98ec
https://e3360650.graphite.pages.dev

github-actions[bot] avatar Jul 09 '25 02:07 github-actions[bot]

Adding bevel to the following layer crashes the node (paste this into a document then add a Bevel node):

graphite/layer: [{"nodes":[[3,{"document_node":{"inputs":[{"Value":{"tagged_value":{"VectorData":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Value":{"tagged_value":{"VectorModification":{"points":{"add":[7101966268902852897,12959623941848655864,9422361377909595935,3098174827925137377],"remove":[],"delta":[[12959623941848655864,[0.0,0.0]],[9422361377909595935,[-100.0,0.0]],[7101966268902852897,[0.0,0.0]],[3098174827925137377,[100.0,0.0]]]},"segments":{"add":[16003530597502309584,10585305760792964956,6527407443855195459],"remove":[],"start_point":[[16003530597502309584,7101966268902852897],[10585305760792964956,7101966268902852897],[6527407443855195459,12959623941848655864]],"end_point":[[6527407443855195459,3098174827925137377],[16003530597502309584,12959623941848655864],[10585305760792964956,9422361377909595935]],"handle_primary":[[6527407443855195459,[10.0,0.0]],[16003530597502309584,[0.0,0.0]],[10585305760792964956,[0.0,0.0]]],"handle_end":[[6527407443855195459,[-90.0,100.0]],[16003530597502309584,[0.0,0.0]],[10585305760792964956,[0.0,0.0]]],"stroke":[[10585305760792964956,0],[16003530597502309584,0],[6527407443855195459,0]]},"regions":{"add":[],"remove":[],"segment_range":[],"fill":[]},"add_g1_continuous":[],"remove_g1_continuous":[[{"ty":"End","segment":16003530597502309584},{"ty":"Primary","segment":6527407443855195459}]]}},"exposed":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Concrete":{"name":"graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>","alias":null}},"import_index":0}}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}],[1,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"import_type":{"Concrete":{"name":"graphene_core::vector::vector_data::modification::VectorModification","alias":null}},"import_index":1}},{"Reflection":"DocumentNodePath"}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::vector::vector_data::modification::PathModifyNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false},"persistent_node_metadata":{"reference":"Path","display_name":"Path","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Vector Data","input_description":"TODO"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Modification","input_description":"TODO"}}],"output_names":["Vector Data"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":null,"display_name":"Path Modify","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[7,0]}}},"network_metadata":null}}],[0,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}],[1,{"document_node":{"inputs":[{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"F64":2.0},"exposed":false}},{"Value":{"tagged_value":{"StrokeAlign":"Center"},"exposed":false}},{"Value":{"tagged_value":{"StrokeCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"StrokeJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F64":4.0},"exposed":false}},{"Value":{"tagged_value":{"PaintOrder":"StrokeAbove"},"exposed":false}},{"Value":{"tagged_value":{"VecF64":[]},"exposed":false}},{"Value":{"tagged_value":{"F64":0.0},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::StrokeNode"}},"visible":true,"skip_deduplication":false},"persistent_node_metadata":{"reference":"Stroke","display_name":"Stroke","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Vector Data","input_description":"The vector elements, or group of vector elements, to apply the stroke to.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Color","input_description":"The stroke color.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Weight","input_description":"The stroke weight.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Align","input_description":"The alignment of stroke to the path's centerline or (for closed shapes) the inside or outside of the shape.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Cap","input_description":"The shape of the stroke at open endpoints.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Join","input_description":"The curvature of the bent stroke at sharp corners.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Miter Limit","input_description":"The threshold for when a miter-joined stroke is converted to a bevel-joined stroke when a sharp angle becomes pointier than this ratio.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Paint Order","input_description":"The order to paint the stroke on top of the fill, or the fill on top of the stroke.\n<https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty>\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Dash Lengths","input_description":"The stroke dash lengths. Each length forms a distance in a pattern where the first length is a dash, the second is a gap, and so on. If the list is an odd length, the pattern repeats with solid-gap roles reversed.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Dash Offset","input_description":"The phase offset distance from the starting point of the dash pattern.\n"}}],"output_names":["Instances<VectorData>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":null}}],[2,{"document_node":{"inputs":[{"Node":{"node_id":3,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"Fill":{"Solid":{"red":0.99999994,"green":0.99999994,"blue":0.99999994,"alpha":1.0}}},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.99999994,"green":0.99999994,"blue":0.99999994,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"Gradient":{"stops":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]],"gradient_type":"Linear","start":[0.0,0.5],"end":[1.0,0.5],"transform":[1.0,0.0,0.0,1.0,0.0,0.0]}},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::FillNode"}},"visible":true,"skip_deduplication":false},"persistent_node_metadata":{"reference":"Fill","display_name":"Fill","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Vector Data","input_description":"The vector elements, or group of vector elements, to apply the fill to.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Fill","input_description":"The fill to paint the path with.\n"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Backup Color","input_description":""}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Backup Gradient","input_description":""}}],"output_names":["Instances<VectorData>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":"Chain"}},"network_metadata":null}}],[0,{"document_node":{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":null,"implementation":{"Network":{"exports":[{"Node":{"node_id":3,"output_index":0,"lambda":false}}],"nodes":[[0,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":1}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToElementNode"}},"visible":true,"skip_deduplication":false}],[3,{"inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Reflection":"DocumentNodePath"}],"manual_composition":{"Generic":"T"},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::LayerNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::memo::MonitorNode"}},"visible":true,"skip_deduplication":true}],[1,{"inputs":[{"Network":{"import_type":{"Generic":"T"},"import_index":0}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::graphic_element::ToGroupNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]}},"visible":true,"skip_deduplication":false},"persistent_node_metadata":{"reference":"Merge","display_name":"","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Graphical Data","input_description":"TODO"}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"Over","input_description":"TODO"}}],"output_names":["Out"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Layer":{"position":{"Absolute":[-6,5]}}},"network_metadata":{"persistent_metadata":{"node_metadata":[[3,{"persistent_metadata":{"reference":null,"display_name":"Layer","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}},{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,-3]}}},"network_metadata":null}}],[2,{"persistent_metadata":{"reference":null,"display_name":"Monitor","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-7,-1]}}},"network_metadata":null}}],[0,{"persistent_metadata":{"reference":null,"display_name":"To Element","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-1]}}},"network_metadata":null}}],[1,{"persistent_metadata":{"reference":null,"display_name":"To Group","input_metadata":[{"persistent_metadata":{"input_data":{},"widget_override":null,"input_name":"","input_description":""}}],"output_names":[],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-14,-3]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}}}]],"selected":true,"visible":true,"locked":false,"collapsed":false}]

This is a path manually constructed to represent these points from the bevel_repeated_point test, which is one of the four failing tests:

  • vector::vector_nodes::test::bevel_open_curve
  • vector::vector_nodes::test::bevel_rect
  • vector::vector_nodes::test::bevel_repeated_point
  • vector::vector_nodes::test::bevel_with_transform

Please fix the edge case covered by the crash (I presume it's the case where there's a zero-length segment connected between two other segments).

And then please also fix the four test cases which are failing, either due to flaws in the algorithm (which should be fixed) or outdated expected cases in the test output calculations (which should be updated within the tests). Please take care to analyze which case is which while solving these.

Thanks! The behavior is looking really nice to use now, so I look forward to getting this merged.

Keavon avatar Jul 09 '25 04:07 Keavon