Skip to content

Commit ed71a08

Browse files
Merge pull request #156 from gridaco/support-flow-connections
Support flow connections (render only)
2 parents d9236cb + 5a090fe commit ed71a08

File tree

9 files changed

+320
-0
lines changed

9 files changed

+320
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react";
2+
import type { XY } from "../types";
3+
4+
export function Arrow({
5+
b,
6+
color,
7+
size,
8+
width,
9+
direction,
10+
}: {
11+
b: XY;
12+
color: React.CSSProperties["color"];
13+
size: number;
14+
width: number;
15+
direction: "n" | "s" | "e" | "w";
16+
}) {
17+
return (
18+
<path
19+
stroke={color}
20+
strokeWidth={width}
21+
d={make_arrow_svg_path_data(b, direction, {
22+
width: size,
23+
height: size / 2,
24+
})}
25+
/>
26+
);
27+
}
28+
29+
/**
30+
*
31+
* the result will have 3 modifiers,
32+
* if the arrow is facing right, the modifiers will be:
33+
* - M - starting point [edge_x - height, edge_y + width / 2]
34+
* - L - edge [edge_x, edge_y]
35+
* - L - ending point [edge_x - height, edge_y - width / 2]
36+
*
37+
* @param edge the edge of a arrow (triangle)
38+
* @param width
39+
*/
40+
function make_arrow_svg_path_data(
41+
edge: XY,
42+
direction: "n" | "s" | "e" | "w",
43+
{ width, height }: { width: number; height: number }
44+
) {
45+
const [x, y] = edge;
46+
const w = width / 2;
47+
switch (direction) {
48+
case "e": {
49+
return `M${x - height},${y + w} L${x},${y} L${x - height},${y - w}`;
50+
}
51+
case "w": {
52+
return `M${x + height},${y + w} L${x},${y} L${x + height},${y - w}`;
53+
}
54+
case "n": {
55+
return `M${x - w},${y + height} L${x},${y} L${x + w},${y + height}`;
56+
}
57+
case "s": {
58+
return `M${x - w},${y - height} L${x},${y} L${x + w},${y - height}`;
59+
}
60+
default: {
61+
throw new Error(`invalid direction: ${direction}`);
62+
}
63+
}
64+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { color_connection_line } from "../theme";
2+
import type { XY } from "../types";
3+
import { Arrow } from "./arrow";
4+
import { get_direction } from "./math";
5+
import type { ConnectionLineStyleProps } from "./props";
6+
7+
export function BezierCurvedLine({
8+
a,
9+
b,
10+
width = 2,
11+
color = color_connection_line,
12+
}: { a: XY; b: XY } & ConnectionLineStyleProps) {
13+
const direction = get_direction(a, b);
14+
15+
return (
16+
<svg
17+
fill="transparent"
18+
style={{
19+
overflow: "visible",
20+
position: "absolute",
21+
pointerEvents: "none",
22+
// transform: `translate(${a[0]}px, ${a[1]}px)`,
23+
zIndex: 10,
24+
}}
25+
>
26+
<path
27+
stroke={color}
28+
strokeWidth={width}
29+
d={make_bazier_curved_svg_path_data(
30+
a,
31+
b,
32+
direction_to_axis_map[direction]
33+
)}
34+
/>
35+
<Arrow
36+
color={color}
37+
size={12}
38+
b={b}
39+
width={width}
40+
direction={direction}
41+
/>
42+
</svg>
43+
);
44+
}
45+
46+
const direction_to_axis_map = {
47+
n: "v",
48+
s: "v",
49+
e: "h",
50+
w: "h",
51+
} as const;
52+
53+
/**
54+
* make a svg path data to connect point a to point b
55+
*
56+
* the output will contain 2 commands
57+
* - M - starting point
58+
* - C - curve
59+
*
60+
* e.g. for a a[0, 0], b[1000, 500], (1000x500 box)
61+
* - `"M 0 0 C 500 0 500 500 1000 500"`
62+
* - M a[0], a[1] (start point)
63+
* - C0 a[0] + w / 2, a[1]
64+
* - C1 a[0] + w / 2, b[1]
65+
* - C2 b[0], b[1] (end point)
66+
*
67+
* @param a - starting point
68+
* @param b - ending point
69+
*/
70+
function make_bazier_curved_svg_path_data(a: XY, b: XY, axis: "h" | "v" = "h") {
71+
const [x0, y0] = a;
72+
const [x1, y1] = b;
73+
const w = axis === "h" ? x1 - x0 : y1 - y0;
74+
75+
if (axis === "h") {
76+
return `M ${x0},${y0} C ${x0 + w / 2},${y0} ${
77+
x0 + w / 2
78+
},${y1} ${x1},${y1}`;
79+
} else if (axis === "v") {
80+
return `M ${x0},${y0} C ${x0},${y0 + w / 2} ${x1},${
81+
y0 + w / 2
82+
} ${x1},${y1}`;
83+
}
84+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from "react";
2+
import type { XY } from "../types";
3+
import { Arrow } from "./arrow";
4+
import { get_direction } from "./math";
5+
6+
/**
7+
* @deprecated - not implemented
8+
* @param param0
9+
* @returns
10+
*/
11+
export function EdgeCurvedConnectionLine({
12+
a,
13+
b,
14+
width = 2,
15+
color = "blue",
16+
}: { a: XY; b: XY } & {
17+
width?: number;
18+
color?: React.CSSProperties["color"];
19+
}) {
20+
const direction = get_direction(a, b);
21+
return (
22+
<svg
23+
fill="transparent"
24+
height={Math.abs(b[1] - a[1])}
25+
width={Math.abs(b[0] - a[0])}
26+
style={{
27+
overflow: "visible",
28+
position: "absolute",
29+
pointerEvents: "none",
30+
transform: `translate(${a[0]}px, ${a[1]}px)`,
31+
zIndex: 10,
32+
}}
33+
>
34+
<Line a={a} b={b} />
35+
<Arrow
36+
color={color}
37+
size={12}
38+
b={b}
39+
width={width}
40+
direction={direction}
41+
/>
42+
</svg>
43+
);
44+
}
45+
46+
function Line({ a, b }: { a: XY; b: XY }) {
47+
return <path d="" />;
48+
}
49+
50+
/**
51+
*
52+
* makes the svg path data that connects point a to point b, with extra parameters, curve delta and edge inset
53+
*
54+
* the shape looks line
55+
* ```
56+
* (a) ---
57+
* |
58+
* |
59+
* |
60+
* |
61+
* |
62+
* --- (b)
63+
* ```
64+
*
65+
* the line components are..
66+
* 0. M | starting point
67+
* 1. L L | the line from `a - edge` to `a` - [a - edge, a]
68+
* 2. C | the curve to before 3
69+
* 3. L | the line from `a` to `b` - [a, b]
70+
* 4. C | the curve to after 3
71+
* 5. L L | the line from `b` to `b + edge` - [b, b + edge]
72+
*
73+
* the output command is:
74+
* - M - the start point (a)
75+
* - L - line start
76+
* - L - draw line to the curving point
77+
* - C - curve
78+
* - L - line between two curves
79+
* - C - curve
80+
* - L - line start
81+
* - L - line end point
82+
*
83+
* e.g. the output of this function is:
84+
* - `"M 0 0 L 0 0 L 8 0 L 8 0 C 17.1638 0 25.4139 5.5525 28.8641 14.042 L 165.907 351.249 C 169.358 359.739 177.608 365.291 186.772 365.291 L 186.772 365.291 L 194.772 365.291"`
85+
*
86+
* @param a the starting point a
87+
* @param b the ending point b
88+
* @param curve the curve delta
89+
* @param edge the edge (margin) value
90+
*/
91+
function make_svg_path_data(a: XY, b: XY, edge) {}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { EdgeCurvedConnectionLine } from "./connection-line-edge-curved";
2+
export { BezierCurvedLine } from "./connection-line-bezier-curved";
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
color_connection_knob_fill,
3+
color_connection_knob_stroke,
4+
} from "../theme";
5+
import type { XY } from "../types";
6+
7+
export function Knob({ point, size = 6 }: { point: XY; size: number }) {
8+
return (
9+
<svg
10+
fill="transparent"
11+
style={{
12+
overflow: "visible",
13+
position: "absolute",
14+
pointerEvents: "none",
15+
transform: `translate(${point[0]}px, ${point[1]}px)`,
16+
zIndex: 10,
17+
}}
18+
>
19+
<circle
20+
r={size}
21+
stroke={color_connection_knob_stroke}
22+
fill={color_connection_knob_fill}
23+
strokeWidth={2}
24+
/>
25+
</svg>
26+
);
27+
}

editor-packages/editor-canvas/flow-connections/line.tsx

Whitespace-only changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { XY } from "../types";
2+
3+
/**
4+
* get the direction based on two points (vector), a and b.
5+
* returns the most powerful direction, e.g. [0, 0], [10, 50] -> "s"
6+
* if the power is the same, return "e" | "w" first, then "s" | "n".
7+
*
8+
* examples
9+
* - [0, 0], [10, 50] -> "s"
10+
* - [0, 0], [10, 0] -> "e"
11+
* - [0, 0], [0, 50] -> "s"
12+
* - [0, 0], [0, 0] -> "e"
13+
* - [0, 0], [-10, 50] -> "n"
14+
* - [0, 0], [-10, 0] -> "w"
15+
* - [0, 0], [-10, -50] -> "n"
16+
* - [0, 0], [-10, 0] -> "w"
17+
* - [0, 0], [-100, -100] -> "w"
18+
*
19+
* @param a
20+
* @param b
21+
* @returns
22+
*/
23+
export function get_direction(a: XY, b: XY): "n" | "s" | "e" | "w" {
24+
const [x, y] = a;
25+
const [x2, y2] = b;
26+
27+
const x_diff = x2 - x;
28+
const y_diff = y2 - y;
29+
30+
if (Math.abs(x_diff) >= Math.abs(y_diff)) {
31+
if (x_diff > 0) {
32+
return "e";
33+
} else {
34+
return "w";
35+
}
36+
}
37+
38+
if (y_diff > 0) {
39+
return "s";
40+
}
41+
42+
return "n";
43+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from "react";
2+
3+
export interface ConnectionLineStyleProps {
4+
width?: number;
5+
color?: React.CSSProperties["color"];
6+
}

editor-packages/editor-canvas/theme/default-theme.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export const color_layer_highlight = "#0099ff";
22
export const color_layer_readonly_highlight = "#0099ff";
3+
export const color_connection_line = "#34AEFF";
4+
export const color_connection_knob_stroke = "#34AEFF";
5+
export const color_connection_knob_fill = "white";
36
export const color_frame_title = {
47
default: "grey",
58
highlight: color_layer_highlight,

0 commit comments

Comments
 (0)