|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
import { CSSProperties, ReactElement, useCallback, useEffect, useRef } from "react"
|
|
|
|
|
import { add, Pos, relativeTo, size } from "./Pos"
|
|
|
|
|
import { add, angle, Pos, relativeTo, size } from "./Pos"
|
|
|
|
|
|
|
|
|
|
export interface BendableArrowProps {
|
|
|
|
|
basePos: Pos
|
|
|
|
@ -24,6 +24,15 @@ const ArrowStyleDefaults = {
|
|
|
|
|
width: 4
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function constraintInCircle(pos: Pos, from: Pos, radius: number): Pos {
|
|
|
|
|
const theta = angle(pos, from)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
x: pos.x - Math.sin(theta) * radius,
|
|
|
|
|
y: pos.y - Math.cos(theta) * radius
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function BendableArrow({ basePos, startPos, endPos, style, startRadius = 0, endRadius = 0 }: BendableArrowProps) {
|
|
|
|
|
const svgRef = useRef<SVGSVGElement>(null)
|
|
|
|
|
|
|
|
|
@ -36,11 +45,15 @@ export default function BendableArrow({ basePos, startPos, endPos, style, startR
|
|
|
|
|
const startRelative = relativeTo(startPos, basePos)
|
|
|
|
|
const endRelative = relativeTo(endPos, basePos)
|
|
|
|
|
|
|
|
|
|
const tailPos = constraintInCircle(startRelative, endRelative, startRadius)
|
|
|
|
|
const headPos = constraintInCircle(endRelative, startRelative, endRadius)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// the width and height of the arrow svg
|
|
|
|
|
const svgBoxBounds = size(startPos, endPos)
|
|
|
|
|
|
|
|
|
|
const left = Math.min(startRelative.x, endRelative.x)
|
|
|
|
|
const top = Math.min(startRelative.y, endRelative.y)
|
|
|
|
|
const left = Math.min(tailPos.x, headPos.x)
|
|
|
|
|
const top = Math.min(tailPos.y, headPos.y)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const svgStyle: CSSProperties = {
|
|
|
|
@ -52,7 +65,7 @@ export default function BendableArrow({ basePos, startPos, endPos, style, startR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const d = `M${startRelative.x - left} ${startRelative.y - top} L${endRelative.x - left} ${endRelative.y - top}`
|
|
|
|
|
const d = `M${tailPos.x - left} ${tailPos.y - top} L${headPos.x - left} ${headPos.y - top}`
|
|
|
|
|
pathRef.current!.setAttribute("d", d)
|
|
|
|
|
|
|
|
|
|
Object.assign(svgRef.current!.style, svgStyle)
|
|
|
|
|