refactor(edges)!: simplify edge paths and center calculations
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { getBezierPath, getEdgeCenter, useVueFlow } from '@vue-flow/core'
|
||||
import { getBezierPath, useVueFlow } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -64,15 +64,6 @@ const edgePath = computed(() =>
|
||||
targetPosition: props.targetPosition,
|
||||
}),
|
||||
)
|
||||
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
sourceY: props.sourceY,
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY,
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<script>
|
||||
@@ -82,12 +73,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<path :id="id" :style="style" class="vue-flow__edge-path" :d="edgePath" :marker-end="markerEnd" />
|
||||
<path :id="id" :style="style" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="markerEnd" />
|
||||
<foreignObject
|
||||
:width="foreignObjectSize"
|
||||
:height="foreignObjectSize"
|
||||
:x="center[0] - foreignObjectSize / 2"
|
||||
:y="center[1] - foreignObjectSize / 2"
|
||||
:x="edgePath[1] - foreignObjectSize / 2"
|
||||
:y="edgePath[2] - foreignObjectSize / 2"
|
||||
class="edgebutton-foreignobject"
|
||||
requiredExtensions="http://www.w3.org/1999/xhtml"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { EdgeText, getBezierPath, getEdgeCenter } from '@vue-flow/core'
|
||||
import { EdgeText, getBezierPath } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -64,14 +64,6 @@ const edgePath = computed(() =>
|
||||
}),
|
||||
)
|
||||
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
sourceY: props.sourceY,
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY,
|
||||
}),
|
||||
)
|
||||
const onClick = () => console.log(props.data)
|
||||
</script>
|
||||
|
||||
@@ -82,10 +74,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<path :id="props.id" class="vue-flow__edge-path" :d="edgePath" :marker-end="props.markerEnd" />
|
||||
<path :id="props.id" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="props.markerEnd" />
|
||||
<EdgeText
|
||||
:x="center[0]"
|
||||
:y="center[1]"
|
||||
:x="edgePath[1]"
|
||||
:y="edgePath[2]"
|
||||
:label="props.data?.text"
|
||||
:label-style="{ fill: 'white' }"
|
||||
:label-show-bg="true"
|
||||
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
:id="props.id"
|
||||
class="vue-flow__edge-path"
|
||||
:style="{ stroke: props.data?.color }"
|
||||
:d="edgePath"
|
||||
:d="edgePath[0]"
|
||||
:marker-end="props.markerEnd"
|
||||
/>
|
||||
<text>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { EdgeProps, Position } from '@vue-flow/core'
|
||||
import { getBezierPath, getEdgeCenter, useVueFlow } from '@vue-flow/core'
|
||||
import { getBezierPath, useVueFlow } from '@vue-flow/core'
|
||||
|
||||
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||
id: string
|
||||
@@ -34,14 +34,6 @@ const edgePath = computed(() =>
|
||||
targetPosition: props.targetPosition,
|
||||
}),
|
||||
)
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
sourceY: props.sourceY,
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY,
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -51,12 +43,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<path :id="props.id" :style="props.style" class="vue-flow__edge-path" :d="edgePath" :marker-end="props.markerEnd" />
|
||||
<path :id="props.id" :style="props.style" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="props.markerEnd" />
|
||||
<foreignObject
|
||||
:width="foreignObjectSize"
|
||||
:height="foreignObjectSize"
|
||||
:x="center[0] - foreignObjectSize / 2"
|
||||
:y="center[1] - foreignObjectSize / 2"
|
||||
:x="edgePath[1] - foreignObjectSize / 2"
|
||||
:y="edgePath[2] - foreignObjectSize / 2"
|
||||
class="edgebutton-foreignobject"
|
||||
requiredExtensions="http://www.w3.org/1999/xhtml"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { EdgeProps, Position } from '@vue-flow/core'
|
||||
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerId } from '@vue-flow/core'
|
||||
import { EdgeText, getBezierPath, getMarkerId } from '@vue-flow/core'
|
||||
|
||||
interface CustomEdgeProps extends EdgeProps {
|
||||
source: string
|
||||
@@ -32,15 +32,9 @@ const edgePath = computed(() =>
|
||||
targetPosition: props.targetPosition,
|
||||
}),
|
||||
)
|
||||
|
||||
const markerEnd = computed(() => getMarkerId(props.markerEnd))
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
sourceY: props.sourceY,
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY,
|
||||
}),
|
||||
)
|
||||
|
||||
const onClick = () => console.log(props.data)
|
||||
</script>
|
||||
|
||||
@@ -51,10 +45,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<path :id="props.id" class="vue-flow__edge-path" :d="edgePath" :marker-end="markerEnd" />
|
||||
<path :id="props.id" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="markerEnd" />
|
||||
<EdgeText
|
||||
:x="center[0]"
|
||||
:y="center[1]"
|
||||
:x="edgePath[1]"
|
||||
:y="edgePath[2]"
|
||||
:label="props.data?.text"
|
||||
:label-style="{ fill: 'white' }"
|
||||
:label-show-bg="true"
|
||||
|
||||
@@ -34,7 +34,7 @@ const d = computed(() =>
|
||||
|
||||
<template>
|
||||
<g>
|
||||
<path fill="none" stroke="#222" :stroke-width="1.5" class="animated" :d="d" />
|
||||
<path fill="none" stroke="#222" :stroke-width="1.5" class="animated" :d="d[0]" />
|
||||
<circle :cx="props.targetX" :cy="props.targetY" fill="#fff" :r="3" stroke="#222" :stroke-width="1.5" />
|
||||
</g>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +40,7 @@ const d = computed(
|
||||
<path
|
||||
:id="props.id"
|
||||
class="vue-flow__edge-path"
|
||||
:d="d"
|
||||
:d="d[0]"
|
||||
:marker-start="props.markerStart"
|
||||
:marker-end="props.markerEnd"
|
||||
:style="props.style"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { getBezierPath, getSmoothStepPath } from '../Edges/utils'
|
||||
import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '../Edges/utils'
|
||||
import type { GraphNode, HandleElement } from '../../types'
|
||||
import { ConnectionLineType, Position } from '../../types'
|
||||
import { useVueFlow } from '../../composables'
|
||||
@@ -48,39 +48,34 @@ const targetY = $computed(() => (connectionPosition.y - viewport.y) / viewport.z
|
||||
|
||||
const dAttr = computed(() => {
|
||||
let path = `M${sourceX},${sourceY} ${targetX},${targetY}`
|
||||
|
||||
const pathParams = {
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
}
|
||||
|
||||
switch (connectionLineType || connectionLineOptions.type) {
|
||||
case ConnectionLineType.Bezier:
|
||||
path = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
})
|
||||
;[path] = getBezierPath(pathParams)
|
||||
break
|
||||
case ConnectionLineType.Step:
|
||||
path = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
;[path] = getSmoothStepPath({
|
||||
...pathParams,
|
||||
borderRadius: 0,
|
||||
})
|
||||
break
|
||||
case ConnectionLineType.SmoothStep:
|
||||
path = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
})
|
||||
;[path] = getSmoothStepPath(pathParams)
|
||||
break
|
||||
case ConnectionLineType.SimpleBezier:
|
||||
;[path] = getSimpleBezierPath(pathParams)
|
||||
break
|
||||
}
|
||||
|
||||
return path
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -8,9 +8,9 @@ import type { BaseEdgeProps } from '~/types'
|
||||
*/
|
||||
const BaseEdge: FunctionalComponent<BaseEdgeProps> = function ({
|
||||
path,
|
||||
centerX,
|
||||
centerY,
|
||||
label,
|
||||
labelX,
|
||||
labelY,
|
||||
labelBgBorderRadius,
|
||||
labelBgPadding,
|
||||
labelBgStyle,
|
||||
@@ -30,8 +30,8 @@ const BaseEdge: FunctionalComponent<BaseEdgeProps> = function ({
|
||||
}),
|
||||
label
|
||||
? h(EdgeText, {
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
x: labelX,
|
||||
y: labelY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
@@ -45,8 +45,8 @@ const BaseEdge: FunctionalComponent<BaseEdgeProps> = function ({
|
||||
|
||||
BaseEdge.props = [
|
||||
'path',
|
||||
'centerX',
|
||||
'centerY',
|
||||
'labelX',
|
||||
'labelY',
|
||||
'label',
|
||||
'labelBgBorderRadius',
|
||||
'labelBgPadding',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FunctionalComponent } from 'vue'
|
||||
import { getBezierCenter, getBezierPath } from './utils'
|
||||
import { getBezierPath } from './utils'
|
||||
import BaseEdge from './BaseEdge'
|
||||
import { Position } from '~/types'
|
||||
import type { EdgeProps } from '~/types'
|
||||
@@ -22,17 +22,7 @@ const BezierEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
markerStart,
|
||||
style,
|
||||
}) {
|
||||
const [centerX, centerY] = getBezierCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
curvature,
|
||||
})
|
||||
|
||||
const path = getBezierPath({
|
||||
const [path, labelX, labelY] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
@@ -44,8 +34,8 @@ const BezierEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
|
||||
return h(BaseEdge, {
|
||||
path,
|
||||
centerX,
|
||||
centerY,
|
||||
labelX,
|
||||
labelY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FunctionalComponent } from 'vue'
|
||||
import { getSimpleBezierCenter, getSimpleBezierPath } from './utils'
|
||||
import { getSimpleBezierPath } from './utils'
|
||||
import BaseEdge from './BaseEdge'
|
||||
import { Position } from '~/types'
|
||||
import type { EdgeProps } from '~/types'
|
||||
@@ -21,16 +21,7 @@ const SimpleBezierEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
markerStart,
|
||||
style,
|
||||
}) {
|
||||
const [centerX, centerY] = getSimpleBezierCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
})
|
||||
|
||||
const path = getSimpleBezierPath({
|
||||
const [path, labelX, labelY] = getSimpleBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
@@ -41,8 +32,8 @@ const SimpleBezierEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
|
||||
return h(BaseEdge, {
|
||||
path,
|
||||
centerX,
|
||||
centerY,
|
||||
labelX,
|
||||
labelY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FunctionalComponent } from 'vue'
|
||||
import { getCenter, getSmoothStepPath } from './utils'
|
||||
import { getSmoothStepPath } from './utils'
|
||||
import BaseEdge from './BaseEdge'
|
||||
import type { SmoothStepEdgeProps } from '~/types'
|
||||
import { Position } from '~/types'
|
||||
@@ -23,16 +23,7 @@ const SmoothStepEdge: FunctionalComponent<SmoothStepEdgeProps> = function ({
|
||||
offset,
|
||||
style,
|
||||
}) {
|
||||
const [centerX, centerY] = getCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
})
|
||||
|
||||
const path = getSmoothStepPath({
|
||||
const [path, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
@@ -45,8 +36,8 @@ const SmoothStepEdge: FunctionalComponent<SmoothStepEdgeProps> = function ({
|
||||
|
||||
return h(BaseEdge, {
|
||||
path,
|
||||
centerX,
|
||||
centerY,
|
||||
labelX,
|
||||
labelY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { FunctionalComponent } from 'vue'
|
||||
import BaseEdge from './BaseEdge'
|
||||
import { getStraightPath } from './utils'
|
||||
import type { EdgeProps } from '~/types'
|
||||
|
||||
const StraightEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
@@ -17,19 +18,12 @@ const StraightEdge: FunctionalComponent<EdgeProps> = function ({
|
||||
markerStart,
|
||||
style,
|
||||
}) {
|
||||
const centerY = computed(() => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2
|
||||
return targetY < sourceY ? targetY + yOffset : targetY - yOffset
|
||||
})
|
||||
const centerX = computed(() => {
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||
return targetX < sourceX ? targetX + xOffset : targetX - xOffset
|
||||
})
|
||||
const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY })
|
||||
|
||||
return h(BaseEdge, {
|
||||
path: `M ${sourceX},${sourceY}L ${targetX},${targetY}`,
|
||||
centerX: centerX.value,
|
||||
centerY: centerY.value,
|
||||
path,
|
||||
labelX,
|
||||
labelY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { getBezierEdgeCenter } from './general'
|
||||
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
|
||||
@@ -17,8 +9,15 @@ export interface GetBezierPathParams {
|
||||
targetY: number
|
||||
targetPosition?: Position
|
||||
curvature?: number
|
||||
centerX?: number
|
||||
centerY?: number
|
||||
}
|
||||
|
||||
interface GetControlWithCurvatureParams {
|
||||
pos: Position
|
||||
x1: number
|
||||
y1: number
|
||||
x2: number
|
||||
y2: number
|
||||
c: number
|
||||
}
|
||||
|
||||
function calculateControlOffset(distance: number, curvature: number): number {
|
||||
@@ -60,7 +59,7 @@ export function getBezierPath({
|
||||
targetY,
|
||||
targetPosition = Position.Top,
|
||||
curvature = 0.25,
|
||||
}: GetBezierPathParams): string {
|
||||
}: GetBezierPathParams): [string, number, number, number, number] {
|
||||
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
||||
pos: sourcePosition,
|
||||
x1: sourceX,
|
||||
@@ -77,39 +76,23 @@ export function getBezierPath({
|
||||
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 [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourceControlX,
|
||||
sourceControlY,
|
||||
targetControlX,
|
||||
targetControlY,
|
||||
})
|
||||
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]
|
||||
|
||||
return [
|
||||
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
||||
centerX,
|
||||
centerY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,41 +1,15 @@
|
||||
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 = ({
|
||||
// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB)
|
||||
export function getSimpleEdgeCenter({
|
||||
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]
|
||||
}
|
||||
|
||||
}: {
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
}): [number, number, number, number] {
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
|
||||
|
||||
@@ -44,3 +18,32 @@ export const getCenter = ({
|
||||
|
||||
return [centerX, centerY, xOffset, yOffset]
|
||||
}
|
||||
|
||||
export function getBezierEdgeCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourceControlX,
|
||||
sourceControlY,
|
||||
targetControlX,
|
||||
targetControlY,
|
||||
}: {
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
sourceControlX: number
|
||||
sourceControlY: number
|
||||
targetControlX: number
|
||||
targetControlY: number
|
||||
}): [number, number, number, number] {
|
||||
// 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 offsetX = Math.abs(centerX - sourceX)
|
||||
const offsetY = Math.abs(centerY - sourceY)
|
||||
|
||||
return [centerX, centerY, offsetX, offsetY]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './bezier'
|
||||
export * from './general'
|
||||
export * from './simple-bezier'
|
||||
export * from './smoothstep'
|
||||
export * from './general'
|
||||
export * from './straight'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getBezierEdgeCenter } from './general'
|
||||
import { Position } from '~/types'
|
||||
|
||||
export interface GetSimpleBezierPathParams {
|
||||
@@ -41,7 +42,7 @@ export function getSimpleBezierPath({
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition = Position.Top,
|
||||
}: GetSimpleBezierPathParams): string {
|
||||
}: GetSimpleBezierPathParams): [string, number, number, number, number] {
|
||||
const [sourceControlX, sourceControlY] = getControl({
|
||||
pos: sourcePosition,
|
||||
x1: sourceX,
|
||||
@@ -56,36 +57,23 @@ export function getSimpleBezierPath({
|
||||
x2: sourceX,
|
||||
y2: sourceY,
|
||||
})
|
||||
return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`
|
||||
}
|
||||
|
||||
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 [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourceControlX,
|
||||
sourceControlY,
|
||||
targetControlX,
|
||||
targetControlY,
|
||||
})
|
||||
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]
|
||||
|
||||
return [
|
||||
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
||||
centerX,
|
||||
centerY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCenter } from './general'
|
||||
import { getSimpleEdgeCenter } from './general'
|
||||
import type { XYPosition } from '~/types'
|
||||
import { Position } from '~/types'
|
||||
|
||||
@@ -53,9 +53,9 @@ function getPoints({
|
||||
sourcePosition: Position
|
||||
target: XYPosition
|
||||
targetPosition: Position
|
||||
center: XYPosition
|
||||
center: Partial<XYPosition>
|
||||
offset: number
|
||||
}): XYPosition[] {
|
||||
}): [XYPosition[], number, number, number, number] {
|
||||
const sourceDir = handleDirections[sourcePosition]
|
||||
const targetDir = handleDirections[targetPosition]
|
||||
const sourceGapped: XYPosition = { x: source.x + sourceDir.x * offset, y: source.y + sourceDir.y * offset }
|
||||
@@ -69,22 +69,32 @@ function getPoints({
|
||||
const currDir = dir[dirAccessor]
|
||||
|
||||
let points: XYPosition[] = []
|
||||
let centerX, centerY
|
||||
const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getSimpleEdgeCenter({
|
||||
sourceX: source.x,
|
||||
sourceY: source.y,
|
||||
targetX: target.x,
|
||||
targetY: target.y,
|
||||
})
|
||||
|
||||
// opposite handle positions, default case
|
||||
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
||||
centerX = center.x || defaultCenterX
|
||||
centerY = center.y || defaultCenterY
|
||||
|
||||
// --->
|
||||
// |
|
||||
// >---
|
||||
const verticalSplit: XYPosition[] = [
|
||||
{ x: center.x, y: sourceGapped.y },
|
||||
{ x: center.x, y: targetGapped.y },
|
||||
{ x: centerX, y: sourceGapped.y },
|
||||
{ x: centerX, y: targetGapped.y },
|
||||
]
|
||||
// |
|
||||
// ---
|
||||
// |
|
||||
const horizontalSplit: XYPosition[] = [
|
||||
{ x: sourceGapped.x, y: center.y },
|
||||
{ x: targetGapped.x, y: center.y },
|
||||
{ x: sourceGapped.x, y: centerY },
|
||||
{ x: targetGapped.x, y: centerY },
|
||||
]
|
||||
|
||||
if (sourceDir[dirAccessor] === currDir) {
|
||||
@@ -117,9 +127,14 @@ function getPoints({
|
||||
points = dirAccessor === 'x' ? sourceTarget : targetSource
|
||||
}
|
||||
}
|
||||
|
||||
centerX = points[0].x
|
||||
centerY = points[0].y
|
||||
}
|
||||
|
||||
return [source, sourceGapped, ...points, targetGapped, target]
|
||||
const pathPoints = [source, sourceGapped, ...points, targetGapped, target]
|
||||
|
||||
return [pathPoints, centerX, centerY, defaultOffsetX, defaultOffsetY]
|
||||
}
|
||||
|
||||
function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): string {
|
||||
@@ -154,21 +169,17 @@ export function getSmoothStepPath({
|
||||
centerX,
|
||||
centerY,
|
||||
offset = 20,
|
||||
}: GetSmoothStepPathParams) {
|
||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY })
|
||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
|
||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
|
||||
|
||||
const points = getPoints({
|
||||
}: GetSmoothStepPathParams): [string, number, number, number, number] {
|
||||
const [points, labelX, labelY, offsetX, offsetY] = getPoints({
|
||||
source: { x: sourceX, y: sourceY },
|
||||
sourcePosition,
|
||||
target: { x: targetX, y: targetY },
|
||||
targetPosition,
|
||||
center: { x: cX, y: cY },
|
||||
center: { x: centerX, y: centerY },
|
||||
offset,
|
||||
})
|
||||
|
||||
return points.reduce<string>((res, p, i) => {
|
||||
const path = points.reduce<string>((res, p, i) => {
|
||||
let segment = ''
|
||||
|
||||
if (i > 0 && i < points.length - 1) {
|
||||
@@ -181,4 +192,6 @@ export function getSmoothStepPath({
|
||||
|
||||
return res
|
||||
}, '')
|
||||
|
||||
return [path, labelX, labelY, offsetX, offsetY]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { getSimpleEdgeCenter } from './general'
|
||||
|
||||
export interface GetStraightPathParams {
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
}
|
||||
|
||||
export function getStraightPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
}: GetStraightPathParams): [string, number, number, number, number] {
|
||||
const [centerX, centerY, offsetX, offsetY] = getSimpleEdgeCenter({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
})
|
||||
|
||||
return [`M ${sourceX},${sourceY}L ${targetX},${targetY}`, centerX, centerY, offsetX, offsetY]
|
||||
}
|
||||
@@ -9,14 +9,7 @@ export { default as Handle } from './components/Handle/Handle.vue'
|
||||
|
||||
export { StraightEdge, StepEdge, BezierEdge, SimpleBezierEdge, SmoothStepEdge, BaseEdge, EdgeText } from './components/Edges'
|
||||
|
||||
export {
|
||||
getBezierPath,
|
||||
getBezierCenter,
|
||||
getSimpleBezierPath,
|
||||
getSimpleBezierCenter,
|
||||
getSmoothStepPath,
|
||||
getCenter as getEdgeCenter,
|
||||
} from './components/Edges/utils'
|
||||
export { getBezierPath, getSimpleBezierPath, getSmoothStepPath, getStraightPath } from './components/Edges/utils'
|
||||
|
||||
export {
|
||||
isNode,
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { Edge, EdgeMarkerType } from './edge'
|
||||
/** Connection line types (same as default edge types */
|
||||
export enum ConnectionLineType {
|
||||
Bezier = 'default',
|
||||
SimpleBezier = 'simple-bezier',
|
||||
Straight = 'straight',
|
||||
Step = 'step',
|
||||
SmoothStep = 'smoothstep',
|
||||
|
||||
@@ -184,8 +184,8 @@ export interface SmoothStepEdgeProps<Data = ElementData, CustomEvents = {}> exte
|
||||
}
|
||||
|
||||
export interface BaseEdgeProps {
|
||||
centerX: number
|
||||
centerY: number
|
||||
labelX: number
|
||||
labelY: number
|
||||
path: string
|
||||
label?: any
|
||||
style?: CSSProperties
|
||||
|
||||
Reference in New Issue
Block a user