jellyflow_runtime/runtime/geometry/paths/
bezier.rs1use jellyflow_core::core::CanvasPoint;
2
3use super::super::endpoints::{EdgeEndpointPosition, HandlePosition};
4use super::label::bezier_label;
5use super::types::{EdgePath, PathCommand};
6
7#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct BezierEdgeOptions {
10 pub curvature: f32,
11}
12
13impl Default for BezierEdgeOptions {
14 fn default() -> Self {
15 Self { curvature: 0.25 }
16 }
17}
18
19pub fn bezier_edge_path(
20 source: EdgeEndpointPosition,
21 target: EdgeEndpointPosition,
22 options: BezierEdgeOptions,
23) -> Option<EdgePath> {
24 if !source.point.is_finite() || !target.point.is_finite() {
25 return None;
26 }
27
28 let curvature = if options.curvature.is_finite() && options.curvature >= 0.0 {
29 options.curvature
30 } else {
31 BezierEdgeOptions::default().curvature
32 };
33 let control1 = control_with_curvature(source.position, source.point, target.point, curvature);
34 let control2 = control_with_curvature(target.position, target.point, source.point, curvature);
35 let label = bezier_label(source.point, control1, control2, target.point)?;
36
37 Some(EdgePath {
38 commands: vec![
39 PathCommand::MoveTo(source.point),
40 PathCommand::CubicTo {
41 control1,
42 control2,
43 to: target.point,
44 },
45 ],
46 label,
47 })
48}
49
50fn control_with_curvature(
51 position: HandlePosition,
52 from: CanvasPoint,
53 to: CanvasPoint,
54 curvature: f32,
55) -> CanvasPoint {
56 match position {
57 HandlePosition::Left => CanvasPoint {
58 x: from.x - control_offset(from.x - to.x, curvature),
59 y: from.y,
60 },
61 HandlePosition::Right => CanvasPoint {
62 x: from.x + control_offset(to.x - from.x, curvature),
63 y: from.y,
64 },
65 HandlePosition::Top => CanvasPoint {
66 x: from.x,
67 y: from.y - control_offset(from.y - to.y, curvature),
68 },
69 HandlePosition::Bottom => CanvasPoint {
70 x: from.x,
71 y: from.y + control_offset(to.y - from.y, curvature),
72 },
73 }
74}
75
76fn control_offset(distance: f32, curvature: f32) -> f32 {
77 if distance >= 0.0 {
78 0.5 * distance
79 } else {
80 curvature * 25.0 * (-distance).sqrt()
81 }
82}