From 56f7e3b0c2251674a6340d17aa0757712209ca0e Mon Sep 17 00:00:00 2001
From: Braks <78412429+bcakmakoglu@users.noreply.github.com>
Date: Fri, 25 Mar 2022 16:24:09 +0100
Subject: [PATCH] feat: Add SimpleBezierEdge
* Implement base edge
---
examples/Interaction/InteractionExample.vue | 4 +-
src/components/Edges/SimpleBezierEdge.vue | 60 ++++++++
src/components/Edges/SmoothStepEdge.vue | 44 +++---
src/components/Edges/StepEdge.vue | 4 +-
src/components/Edges/StraightEdge.vue | 38 ++---
src/components/Edges/index.ts | 1 +
src/components/Edges/utils/bezier.ts | 115 ++++++++++++++
src/components/Edges/utils/general.ts | 46 ++++++
src/components/Edges/utils/index.ts | 4 +
src/components/Edges/utils/simple-bezier.ts | 95 ++++++++++++
.../Edges/{utils.ts => utils/smoothstep.ts} | 140 ++----------------
src/index.ts | 14 +-
src/store/state.ts | 12 +-
src/types/components.ts | 2 +-
src/types/edge.ts | 42 ------
15 files changed, 397 insertions(+), 224 deletions(-)
create mode 100644 src/components/Edges/SimpleBezierEdge.vue
create mode 100644 src/components/Edges/utils/bezier.ts
create mode 100644 src/components/Edges/utils/general.ts
create mode 100644 src/components/Edges/utils/index.ts
create mode 100644 src/components/Edges/utils/simple-bezier.ts
rename src/components/Edges/{utils.ts => utils/smoothstep.ts} (52%)
diff --git a/examples/Interaction/InteractionExample.vue b/examples/Interaction/InteractionExample.vue
index 69b1a6ad..4685428b 100644
--- a/examples/Interaction/InteractionExample.vue
+++ b/examples/Interaction/InteractionExample.vue
@@ -10,7 +10,7 @@ const {
zoomOnPinch,
panOnScroll,
panOnScrollMode,
- paneMovable,
+ panOnDrag,
onConnect,
onNodeDragStart,
onNodeDragStop,
@@ -102,7 +102,7 @@ onMoveEnd((flowTransform) => console.log('move end', flowTransform))
diff --git a/src/components/Edges/SimpleBezierEdge.vue b/src/components/Edges/SimpleBezierEdge.vue
new file mode 100644
index 00000000..130c95a8
--- /dev/null
+++ b/src/components/Edges/SimpleBezierEdge.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
diff --git a/src/components/Edges/SmoothStepEdge.vue b/src/components/Edges/SmoothStepEdge.vue
index 433cf58d..e735ce95 100644
--- a/src/components/Edges/SmoothStepEdge.vue
+++ b/src/components/Edges/SmoothStepEdge.vue
@@ -2,7 +2,7 @@
import { Position } from '../../types'
import type { SmoothStepEdgeProps } from '../../types/edge'
import { getCenter, getSmoothStepPath } from './utils'
-import EdgeText from './EdgeText.vue'
+import BaseEdge from './BaseEdge.vue'
const props = withDefaults(defineProps
(), {
selected: false,
@@ -16,14 +16,25 @@ const props = withDefaults(defineProps(), {
const centered = computed(() =>
getCenter({
- ...props,
+ sourceX: props.sourceX,
+ sourceY: props.sourceY,
+ targetX: props.targetX,
+ targetY: props.targetY,
+ sourcePosition: props.sourcePosition,
+ targetPosition: props.targetPosition,
}),
)
const path = computed(() => {
if (props.sourceX && props.sourceY)
return getSmoothStepPath({
- ...props,
+ sourceX: props.sourceX,
+ sourceY: props.sourceY,
+ targetX: props.targetX,
+ targetY: props.targetY,
+ sourcePosition: props.sourcePosition,
+ targetPosition: props.targetPosition,
+ borderRadius: props.borderRadius,
})
else return ''
})
@@ -31,28 +42,21 @@ const path = computed(() => {
-
-
-
-
diff --git a/src/components/Edges/StepEdge.vue b/src/components/Edges/StepEdge.vue
index 945dc21e..6d4f7a5b 100644
--- a/src/components/Edges/StepEdge.vue
+++ b/src/components/Edges/StepEdge.vue
@@ -19,7 +19,5 @@ export default {
}
-
-
-
+
diff --git a/src/components/Edges/StraightEdge.vue b/src/components/Edges/StraightEdge.vue
index 655a28db..345c3aec 100644
--- a/src/components/Edges/StraightEdge.vue
+++ b/src/components/Edges/StraightEdge.vue
@@ -1,8 +1,7 @@
-
-
-
-
diff --git a/src/components/Edges/index.ts b/src/components/Edges/index.ts
index 20a92f54..23c09f7f 100644
--- a/src/components/Edges/index.ts
+++ b/src/components/Edges/index.ts
@@ -1,4 +1,5 @@
export { default as BezierEdge } from './BezierEdge.vue'
+export { default as SimpleBezierEdge } from './SimpleBezierEdge.vue'
export { default as StepEdge } from './StepEdge.vue'
export { default as SmoothStepEdge } from './SmoothStepEdge.vue'
export { default as StraightEdge } from './StraightEdge.vue'
diff --git a/src/components/Edges/utils/bezier.ts b/src/components/Edges/utils/bezier.ts
new file mode 100644
index 00000000..848c9904
--- /dev/null
+++ b/src/components/Edges/utils/bezier.ts
@@ -0,0 +1,115 @@
+import { Position } from '~/types'
+
+interface GetControlWithCurvatureParams {
+ pos: Position
+ x1: number
+ y1: number
+ x2: number
+ y2: number
+ c: number
+}
+
+export interface GetBezierPathParams {
+ sourceX: number
+ sourceY: number
+ sourcePosition?: Position
+ targetX: number
+ targetY: number
+ targetPosition?: Position
+ curvature?: number
+ centerX?: number
+ centerY?: number
+}
+
+function calculateControlOffset(distance: number, curvature: number): number {
+ if (distance >= 0) {
+ return 0.5 * distance
+ } else {
+ return curvature * 25 * Math.sqrt(-distance)
+ }
+}
+
+function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurvatureParams): [number, number] {
+ let ctX: number, ctY: number
+ switch (pos) {
+ case Position.Left:
+ ctX = x1 - calculateControlOffset(x1 - x2, c)
+ ctY = y1
+ break
+ case Position.Right:
+ ctX = x1 + calculateControlOffset(x2 - x1, c)
+ ctY = y1
+ break
+ case Position.Top:
+ ctX = x1
+ ctY = y1 - calculateControlOffset(y1 - y2, c)
+ break
+ case Position.Bottom:
+ ctX = x1
+ ctY = y1 + calculateControlOffset(y2 - y1, c)
+ break
+ }
+ return [ctX, ctY]
+}
+
+export function getBezierPath({
+ sourceX,
+ sourceY,
+ sourcePosition = Position.Bottom,
+ targetX,
+ targetY,
+ targetPosition = Position.Top,
+ curvature = 0.25,
+}: GetBezierPathParams): string {
+ const [sourceControlX, sourceControlY] = getControlWithCurvature({
+ pos: sourcePosition,
+ x1: sourceX,
+ y1: sourceY,
+ x2: targetX,
+ y2: targetY,
+ c: curvature,
+ })
+ const [targetControlX, targetControlY] = getControlWithCurvature({
+ pos: targetPosition,
+ x1: targetX,
+ y1: targetY,
+ x2: sourceX,
+ y2: sourceY,
+ c: curvature,
+ })
+ return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`
+}
+
+export function getBezierCenter({
+ sourceX,
+ sourceY,
+ sourcePosition = Position.Bottom,
+ targetX,
+ targetY,
+ targetPosition = Position.Top,
+ curvature = 0.25,
+}: GetBezierPathParams): [number, number, number, number] {
+ const [sourceControlX, sourceControlY] = getControlWithCurvature({
+ pos: sourcePosition,
+ x1: sourceX,
+ y1: sourceY,
+ x2: targetX,
+ y2: targetY,
+ c: curvature,
+ })
+ const [targetControlX, targetControlY] = getControlWithCurvature({
+ pos: targetPosition,
+ x1: targetX,
+ y1: targetY,
+ x2: sourceX,
+ y2: sourceY,
+ c: curvature,
+ })
+ // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
+ // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
+ const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125
+ const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125
+ const xOffset = Math.abs(centerX - sourceX)
+ const yOffset = Math.abs(centerY - sourceY)
+ return [centerX, centerY, xOffset, yOffset]
+}
diff --git a/src/components/Edges/utils/general.ts b/src/components/Edges/utils/general.ts
new file mode 100644
index 00000000..60a59866
--- /dev/null
+++ b/src/components/Edges/utils/general.ts
@@ -0,0 +1,46 @@
+import { Position } from '~/types'
+
+export interface GetCenterParams {
+ sourceX: number
+ sourceY: number
+ targetX: number
+ targetY: number
+ sourcePosition?: Position
+ targetPosition?: Position
+}
+
+const LeftOrRight = [Position.Left, Position.Right]
+
+export const getCenter = ({
+ sourceX,
+ sourceY,
+ targetX,
+ targetY,
+ sourcePosition = Position.Bottom,
+ targetPosition = Position.Top,
+}: GetCenterParams): [number, number, number, number] => {
+ const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition)
+ const targetIsLeftOrRight = LeftOrRight.includes(targetPosition)
+
+ // we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
+ // a mixed edge is when one the source is on the left and the target is on the top for example.
+ const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight)
+
+ if (mixedEdge) {
+ const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0
+ const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset
+
+ const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY)
+ const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset
+
+ return [centerX, centerY, xOffset, yOffset]
+ }
+
+ const xOffset = Math.abs(targetX - sourceX) / 2
+ const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
+
+ const yOffset = Math.abs(targetY - sourceY) / 2
+ const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset
+
+ return [centerX, centerY, xOffset, yOffset]
+}
diff --git a/src/components/Edges/utils/index.ts b/src/components/Edges/utils/index.ts
new file mode 100644
index 00000000..ef856385
--- /dev/null
+++ b/src/components/Edges/utils/index.ts
@@ -0,0 +1,4 @@
+export * from './bezier'
+export * from './simple-bezier'
+export * from './smoothstep'
+export * from './general'
diff --git a/src/components/Edges/utils/simple-bezier.ts b/src/components/Edges/utils/simple-bezier.ts
new file mode 100644
index 00000000..6f8aca45
--- /dev/null
+++ b/src/components/Edges/utils/simple-bezier.ts
@@ -0,0 +1,95 @@
+import { Position } from '~/types'
+
+export interface GetSimpleBezierPathParams {
+ sourceX: number
+ sourceY: number
+ sourcePosition?: Position
+ targetX: number
+ targetY: number
+ targetPosition?: Position
+}
+
+interface GetControlParams {
+ pos: Position
+ x1: number
+ y1: number
+ x2: number
+ y2: number
+}
+
+function getControl({ pos, x1, y1, x2, y2 }: GetControlParams): [number, number] {
+ let ctX: number, ctY: number
+ switch (pos) {
+ case Position.Left:
+ case Position.Right:
+ ctX = 0.5 * (x1 + x2)
+ ctY = y1
+ break
+ case Position.Top:
+ case Position.Bottom:
+ ctX = x1
+ ctY = 0.5 * (y1 + y2)
+ break
+ }
+ return [ctX, ctY]
+}
+
+export function getSimpleBezierPath({
+ sourceX,
+ sourceY,
+ sourcePosition = Position.Bottom,
+ targetX,
+ targetY,
+ targetPosition = Position.Top,
+}: GetSimpleBezierPathParams): string {
+ const [sourceControlX, sourceControlY] = getControl({
+ pos: sourcePosition,
+ x1: sourceX,
+ y1: sourceY,
+ x2: targetX,
+ y2: targetY,
+ })
+ const [targetControlX, targetControlY] = getControl({
+ pos: targetPosition,
+ x1: targetX,
+ y1: targetY,
+ x2: sourceX,
+ y2: sourceY,
+ })
+ return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`
+}
+
+// @TODO: this function will recalculate the control points
+// one option is to let getXXXPath() return center points
+// but will introduce breaking changes
+// the getCenter() of other types of edges might need to change, too
+export function getSimpleBezierCenter({
+ sourceX,
+ sourceY,
+ sourcePosition = Position.Bottom,
+ targetX,
+ targetY,
+ targetPosition = Position.Top,
+}: GetSimpleBezierPathParams): [number, number, number, number] {
+ const [sourceControlX, sourceControlY] = getControl({
+ pos: sourcePosition,
+ x1: sourceX,
+ y1: sourceY,
+ x2: targetX,
+ y2: targetY,
+ })
+ const [targetControlX, targetControlY] = getControl({
+ pos: targetPosition,
+ x1: targetX,
+ y1: targetY,
+ x2: sourceX,
+ y2: sourceY,
+ })
+ // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
+ // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
+ const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125
+ const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125
+ const xOffset = Math.abs(centerX - sourceX)
+ const yOffset = Math.abs(centerY - sourceY)
+ return [centerX, centerY, xOffset, yOffset]
+}
diff --git a/src/components/Edges/utils.ts b/src/components/Edges/utils/smoothstep.ts
similarity index 52%
rename from src/components/Edges/utils.ts
rename to src/components/Edges/utils/smoothstep.ts
index 6cff639a..54225d31 100644
--- a/src/components/Edges/utils.ts
+++ b/src/components/Edges/utils/smoothstep.ts
@@ -1,132 +1,16 @@
-import { GetBezierPathParams, GetCenterParams, GetControlWithCurvatureParams, GetSmoothStepPathParams, Position } from '~/types'
+import { getCenter } from './general'
+import { Position } from '~/types'
-const LeftOrRight = [Position.Left, Position.Right]
-
-export const getCenter = ({
- sourceX,
- sourceY,
- targetX,
- targetY,
- sourcePosition = Position.Bottom,
- targetPosition = Position.Top,
-}: GetCenterParams): [number, number, number, number] => {
- const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition)
- const targetIsLeftOrRight = LeftOrRight.includes(targetPosition)
-
- // we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
- // a mixed edge is when one the source is on the left and the target is on the top for example.
- const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight)
-
- if (mixedEdge) {
- const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0
- const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset
-
- const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY)
- const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset
-
- return [centerX, centerY, xOffset, yOffset]
- }
-
- const xOffset = Math.abs(targetX - sourceX) / 2
- const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
-
- const yOffset = Math.abs(targetY - sourceY) / 2
- const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset
-
- return [centerX, centerY, xOffset, yOffset]
-}
-
-function calculateControlOffset(distance: number, curvature: number): number {
- if (distance >= 0) {
- return 0.5 * distance
- } else {
- return curvature * 25 * Math.sqrt(-distance)
- }
-}
-
-function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurvatureParams): [number, number] {
- let ctX: number, ctY: number
- switch (pos) {
- case Position.Left:
- ctX = x1 - calculateControlOffset(x1 - x2, c)
- ctY = y1
- break
- case Position.Right:
- ctX = x1 + calculateControlOffset(x2 - x1, c)
- ctY = y1
- break
- case Position.Top:
- ctX = x1
- ctY = y1 - calculateControlOffset(y1 - y2, c)
- break
- case Position.Bottom:
- ctX = x1
- ctY = y1 + calculateControlOffset(y2 - y1, c)
- break
- }
- return [ctX, ctY]
-}
-
-export function getBezierPath({
- sourceX,
- sourceY,
- sourcePosition = Position.Bottom,
- targetX,
- targetY,
- targetPosition = Position.Top,
- curvature = 0.25,
-}: GetBezierPathParams): string {
- const [sourceControlX, sourceControlY] = getControlWithCurvature({
- pos: sourcePosition,
- x1: sourceX,
- y1: sourceY,
- x2: targetX,
- y2: targetY,
- c: curvature,
- })
- const [targetControlX, targetControlY] = getControlWithCurvature({
- pos: targetPosition,
- x1: targetX,
- y1: targetY,
- x2: sourceX,
- y2: sourceY,
- c: curvature,
- })
- return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`
-}
-
-export function getBezierCenter({
- sourceX,
- sourceY,
- sourcePosition = Position.Bottom,
- targetX,
- targetY,
- targetPosition = Position.Top,
- curvature = 0.25,
-}: GetBezierPathParams): [number, number, number, number] {
- const [sourceControlX, sourceControlY] = getControlWithCurvature({
- pos: sourcePosition,
- x1: sourceX,
- y1: sourceY,
- x2: targetX,
- y2: targetY,
- c: curvature,
- })
- const [targetControlX, targetControlY] = getControlWithCurvature({
- pos: targetPosition,
- x1: targetX,
- y1: targetY,
- x2: sourceX,
- y2: sourceY,
- c: curvature,
- })
- // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
- // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
- const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125
- const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125
- const xOffset = Math.abs(centerX - sourceX)
- const yOffset = Math.abs(centerY - sourceY)
- return [centerX, centerY, xOffset, yOffset]
+export interface GetSmoothStepPathParams {
+ sourceX: number
+ sourceY: number
+ sourcePosition?: Position
+ targetX: number
+ targetY: number
+ targetPosition?: Position
+ borderRadius?: number
+ centerX?: number
+ centerY?: number
}
// These are some helper methods for drawing the round corners
diff --git a/src/index.ts b/src/index.ts
index b91f75c4..e0639952 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,7 +1,19 @@
export { default as VueFlow } from './container/VueFlow/VueFlow.vue'
export { default as Handle } from './components/Handle/Handle.vue'
export { default as EdgeText } from './components/Edges/EdgeText.vue'
-export { getBezierPath, getSmoothStepPath, getCenter as getEdgeCenter, getBezierCenter } from './components/Edges/utils'
+export { default as StraightEdge } from './components/Edges/StraightEdge.vue'
+export { default as StepEdge } from './components/Edges/StepEdge.vue'
+export { default as BezierEdge } from './components/Edges/BezierEdge.vue'
+export { default as SimpleBezierEdge } from './components/Edges/SimpleBezierEdge.vue'
+export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge.vue'
+export {
+ getBezierPath,
+ getBezierCenter,
+ getSimpleBezierPath,
+ getSimpleBezierCenter,
+ getSmoothStepPath,
+ getCenter as getEdgeCenter,
+} from './components/Edges/utils'
export {
isNode,
diff --git a/src/store/state.ts b/src/store/state.ts
index c190417a..956a6a06 100644
--- a/src/store/state.ts
+++ b/src/store/state.ts
@@ -8,7 +8,16 @@ import {
ConnectionLineType,
FlowOptions,
} from '~/types'
-import { DefaultNode, InputNode, OutputNode, BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components'
+import {
+ DefaultNode,
+ InputNode,
+ OutputNode,
+ BezierEdge,
+ SmoothStepEdge,
+ StepEdge,
+ StraightEdge,
+ SimpleBezierEdge,
+} from '~/components'
export const defaultNodeTypes: DefaultNodeTypes = {
input: InputNode,
@@ -21,6 +30,7 @@ export const defaultEdgeTypes: DefaultEdgeTypes = {
straight: StraightEdge,
step: StepEdge,
smoothstep: SmoothStepEdge,
+ simplebezier: SimpleBezierEdge,
}
const isDef = (val: T): val is NonNullable => typeof val !== 'undefined'
diff --git a/src/types/components.ts b/src/types/components.ts
index 5184d7db..5d185ba4 100644
--- a/src/types/components.ts
+++ b/src/types/components.ts
@@ -19,7 +19,7 @@ export type EdgeComponent =
| DefineComponent, any, any, any, any, any>
| GlobalComponentName
-export type DefaultEdgeTypes = { [key in 'default' | 'straight' | 'smoothstep' | 'step']: EdgeComponent }
+export type DefaultEdgeTypes = { [key in 'default' | 'straight' | 'smoothstep' | 'step' | 'simplebezier']: EdgeComponent }
export type DefaultNodeTypes = { [key in 'input' | 'output' | 'default']: NodeComponent }
export interface BackgroundProps {
diff --git a/src/types/edge.ts b/src/types/edge.ts
index ad1ec930..f5912449 100644
--- a/src/types/edge.ts
+++ b/src/types/edge.ts
@@ -168,45 +168,3 @@ export interface SmoothStepEdgeProps extends EdgeProps {
markerEnd?: string
borderRadius?: number
}
-
-export interface GetCenterParams {
- sourceX: number
- sourceY: number
- targetX: number
- targetY: number
- sourcePosition?: Position
- targetPosition?: Position
-}
-
-export interface GetBezierPathParams {
- sourceX: number
- sourceY: number
- sourcePosition?: Position
- targetX: number
- targetY: number
- targetPosition?: Position
- curvature?: number
- centerX?: number
- centerY?: number
-}
-
-export interface GetSmoothStepPathParams {
- sourceX: number
- sourceY: number
- sourcePosition?: Position
- targetX: number
- targetY: number
- targetPosition?: Position
- borderRadius?: number
- centerX?: number
- centerY?: number
-}
-
-export interface GetControlWithCurvatureParams {
- pos: Position
- x1: number
- y1: number
- x2: number
- y2: number
- c: number
-}