diff --git a/packages/core/src/utils/svg.ts b/packages/core/src/utils/svg.ts index 4e8514cbb..b8e6aefb2 100644 --- a/packages/core/src/utils/svg.ts +++ b/packages/core/src/utils/svg.ts @@ -40,8 +40,8 @@ export class Svg { r, r, 0, - Utils.getSweep(C, A, B) > 0 ? '1' : '0', 0, + Utils.getArcLength(C, r, A, B) > 0 ? '1' : '0', B[0], B[1], ].join(' ') diff --git a/packages/tldraw/src/shape/shapes/arrow/arrow.tsx b/packages/tldraw/src/shape/shapes/arrow/arrow.tsx index 93f777f1b..138e16d5b 100644 --- a/packages/tldraw/src/shape/shapes/arrow/arrow.tsx +++ b/packages/tldraw/src/shape/shapes/arrow/arrow.tsx @@ -8,6 +8,7 @@ import { Intersect, TLHandle, TLPointerInfo, + Svg, } from '@tldraw/core' import getStroke from 'perfect-freehand' import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape/shape-styles' @@ -235,57 +236,88 @@ export class Arrow extends TLDrawShapeUtil { renderIndicator(shape: ArrowShape) { const { - handles: { start, end }, - bend, + decorations, + handles: { start, end, bend: _bend }, style, } = shape - const path = Utils.getFromCache(this.simplePathCache, shape, () => - getArrowArcPath(start, end, getCtp(shape), bend) - ) - const styles = getShapeStyle(style, false) - - const { strokeWidth } = styles + const { strokeWidth } = getShapeStyle(style, false) const arrowDist = Vec.dist(start.point, end.point) const arrowHeadlength = Math.min(arrowDist / 3, strokeWidth * 8) - let insetStart: number[] - let insetEnd: number[] + const aw = arrowHeadlength / 2 - if (bend === 0) { - insetStart = Vec.nudge(start.point, end.point, arrowHeadlength) - insetEnd = Vec.nudge(end.point, start.point, arrowHeadlength) + const path: (string | number)[] = [] + + const isStraightLine = Vec.dist(_bend.point, Vec.round(Vec.med(start.point, end.point))) < 1 + + if (isStraightLine) { + path.push(Svg.moveTo(start.point), Svg.lineTo(end.point)) + + if (decorations?.start) { + const point = start.point + const ints = Intersect.circle.lineSegment(start.point, aw, start.point, end.point).points + const int = ints[0] + + path.push( + Svg.moveTo(Vec.nudge(Vec.rotWith(int, point, Math.PI / 6), point, -aw)), + Svg.lineTo(start.point), + Svg.lineTo(Vec.nudge(Vec.rotWith(int, point, -Math.PI / 6), point, -aw)) + ) + } + + if (decorations?.end) { + const point = end.point + const ints = Intersect.circle.lineSegment(end.point, aw, start.point, end.point).points + const int = ints[0] + + path.push( + Svg.moveTo(Vec.nudge(Vec.rotWith(int, point, Math.PI / 6), point, -aw)), + Svg.lineTo(end.point), + Svg.lineTo(Vec.nudge(Vec.rotWith(int, point, -Math.PI / 6), point, -aw)) + ) + } } else { const circle = getCtp(shape) - - const arcLength = Utils.getArcLength( - [circle[0], circle[1]], - circle[2], - start.point, - end.point - ) - const center = [circle[0], circle[1]] const radius = circle[2] - const sa = Vec.angle(center, start.point) - const ea = Vec.angle(center, end.point) - const t = arrowHeadlength / Math.abs(arcLength) + const sweep = Utils.getArcLength(center, radius, start.point, end.point) > 0 - insetStart = Vec.nudgeAtAngle(center, Utils.lerpAngles(sa, ea, t), radius) - insetEnd = Vec.nudgeAtAngle(center, Utils.lerpAngles(ea, sa, t), radius) + path.push( + Svg.moveTo(start.point), + `A ${radius} ${radius} 0 0 ${sweep ? '1' : '0'} ${end.point}` + ) + + if (decorations?.start) { + const point = start.point + const ints = Intersect.circle.circle(center, radius, point, aw).points + const int = sweep ? ints[0] : ints[1] + + path.push( + Svg.moveTo(Vec.nudge(Vec.rotWith(int, point, Math.PI / 6), point, -aw)), + Svg.lineTo(start.point), + Svg.lineTo(Vec.nudge(Vec.rotWith(int, point, -Math.PI / 6), point, -aw)) + ) + } + + if (decorations?.end) { + const point = end.point + const ints = Intersect.circle.circle(center, radius, point, aw).points + const int = sweep ? ints[1] : ints[0] + + path.push( + Svg.moveTo(Vec.nudge(Vec.rotWith(int, point, Math.PI / 6), point, -aw)), + Svg.lineTo(end.point), + Svg.lineTo(Vec.nudge(Vec.rotWith(int, point, -Math.PI / 6), point, -aw)) + ) + } } return ( - - {shape.decorations?.start === Decoration.Arrow && ( - - )} - {shape.decorations?.end === Decoration.Arrow && ( - - )} + ) }