|
|
@@ -91,7 +91,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|
91
|
91
|
|
|
92
|
92
|
const arrowHeadLength = Math.min(arrowDist / 3, strokeWidth * 8)
|
|
93
|
93
|
|
|
94
|
|
- let shaftPath: JSX.Element
|
|
|
94
|
+ let shaftPath: JSX.Element | null
|
|
95
|
95
|
let startArrowHead: { left: number[]; right: number[] } | undefined
|
|
96
|
96
|
let endArrowHead: { left: number[]; right: number[] } | undefined
|
|
97
|
97
|
|
|
|
@@ -120,31 +120,32 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|
120
|
120
|
}
|
|
121
|
121
|
|
|
122
|
122
|
// Straight arrow path
|
|
123
|
|
- shaftPath = (
|
|
124
|
|
- <>
|
|
125
|
|
- <path
|
|
126
|
|
- d={path}
|
|
127
|
|
- fill="none"
|
|
128
|
|
- strokeWidth={Math.max(8, strokeWidth * 2)}
|
|
129
|
|
- strokeDasharray="none"
|
|
130
|
|
- strokeDashoffset="none"
|
|
131
|
|
- strokeLinecap="round"
|
|
132
|
|
- strokeLinejoin="round"
|
|
133
|
|
- pointerEvents="stroke"
|
|
134
|
|
- />
|
|
135
|
|
- <path
|
|
136
|
|
- d={path}
|
|
137
|
|
- fill={styles.stroke}
|
|
138
|
|
- stroke={styles.stroke}
|
|
139
|
|
- strokeWidth={sw}
|
|
140
|
|
- strokeDasharray={strokeDasharray}
|
|
141
|
|
- strokeDashoffset={strokeDashoffset}
|
|
142
|
|
- strokeLinecap="round"
|
|
143
|
|
- strokeLinejoin="round"
|
|
144
|
|
- pointerEvents="stroke"
|
|
145
|
|
- />
|
|
146
|
|
- </>
|
|
147
|
|
- )
|
|
|
123
|
+ shaftPath =
|
|
|
124
|
+ arrowDist > 2 ? (
|
|
|
125
|
+ <>
|
|
|
126
|
+ <path
|
|
|
127
|
+ d={path}
|
|
|
128
|
+ fill="none"
|
|
|
129
|
+ strokeWidth={Math.max(8, strokeWidth * 2)}
|
|
|
130
|
+ strokeDasharray="none"
|
|
|
131
|
+ strokeDashoffset="none"
|
|
|
132
|
+ strokeLinecap="round"
|
|
|
133
|
+ strokeLinejoin="round"
|
|
|
134
|
+ pointerEvents="stroke"
|
|
|
135
|
+ />
|
|
|
136
|
+ <path
|
|
|
137
|
+ d={path}
|
|
|
138
|
+ fill={styles.stroke}
|
|
|
139
|
+ stroke={styles.stroke}
|
|
|
140
|
+ strokeWidth={sw}
|
|
|
141
|
+ strokeDasharray={strokeDasharray}
|
|
|
142
|
+ strokeDashoffset={strokeDashoffset}
|
|
|
143
|
+ strokeLinecap="round"
|
|
|
144
|
+ strokeLinejoin="round"
|
|
|
145
|
+ pointerEvents="stroke"
|
|
|
146
|
+ />
|
|
|
147
|
+ </>
|
|
|
148
|
+ ) : null
|
|
148
|
149
|
} else {
|
|
149
|
150
|
const circle = getCtp(shape)
|
|
150
|
151
|
|
|
|
@@ -496,6 +497,18 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|
496
|
497
|
}
|
|
497
|
498
|
})
|
|
498
|
499
|
|
|
|
500
|
+ nextHandles = {
|
|
|
501
|
+ ...nextHandles,
|
|
|
502
|
+ start: {
|
|
|
503
|
+ ...nextHandles.start,
|
|
|
504
|
+ point: Vec.round(nextHandles.start.point),
|
|
|
505
|
+ },
|
|
|
506
|
+ end: {
|
|
|
507
|
+ ...nextHandles.end,
|
|
|
508
|
+ point: Vec.round(nextHandles.end.point),
|
|
|
509
|
+ },
|
|
|
510
|
+ }
|
|
|
511
|
+
|
|
499
|
512
|
// If the user is moving the bend handle, we want to move the bend point
|
|
500
|
513
|
if ('bend' in handles) {
|
|
501
|
514
|
const { start, end, bend } = nextHandles
|
|
|
@@ -524,23 +537,16 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|
524
|
537
|
// negative, otherwise it's positive.
|
|
525
|
538
|
const angleToBend = Vec.angle(start.point, bendPoint)
|
|
526
|
539
|
|
|
527
|
|
- if (Utils.isAngleBetween(angle, angle + Math.PI, angleToBend)) {
|
|
|
540
|
+ // If resulting bend is low enough that the handle will snap to center,
|
|
|
541
|
+ // then also snap the bend to center
|
|
|
542
|
+ if (Vec.isEqual(midPoint, getBendPoint(nextHandles, nextBend))) {
|
|
|
543
|
+ nextBend = 0
|
|
|
544
|
+ } else if (Utils.isAngleBetween(angle, angle + Math.PI, angleToBend)) {
|
|
|
545
|
+ // Otherwise, fix the bend direction
|
|
528
|
546
|
nextBend *= -1
|
|
529
|
547
|
}
|
|
530
|
548
|
}
|
|
531
|
549
|
|
|
532
|
|
- nextHandles = {
|
|
533
|
|
- ...nextHandles,
|
|
534
|
|
- start: {
|
|
535
|
|
- ...nextHandles.start,
|
|
536
|
|
- point: Vec.round(nextHandles.start.point),
|
|
537
|
|
- },
|
|
538
|
|
- end: {
|
|
539
|
|
- ...nextHandles.end,
|
|
540
|
|
- point: Vec.round(nextHandles.end.point),
|
|
541
|
|
- },
|
|
542
|
|
- }
|
|
543
|
|
-
|
|
544
|
550
|
const nextShape = {
|
|
545
|
551
|
point: shape.point,
|
|
546
|
552
|
bend: nextBend,
|
|
|
@@ -613,16 +619,14 @@ function renderFreehandArrowShaft(shape: ArrowShape) {
|
|
613
|
619
|
|
|
614
|
620
|
const strokeWidth = +getShapeStyle(style).strokeWidth * 2
|
|
615
|
621
|
|
|
616
|
|
- const st = Math.abs(getRandom())
|
|
617
|
|
-
|
|
618
|
622
|
const stroke = getStroke(
|
|
619
|
|
- [...Vec.pointsBetween(start.point, end.point), end.point, end.point, end.point, end.point],
|
|
|
623
|
+ [...Vec.pointsBetween(start.point, end.point), end.point, end.point, end.point],
|
|
620
|
624
|
{
|
|
621
|
625
|
size: strokeWidth / 2,
|
|
622
|
626
|
thinning: 0.5 + getRandom() * 0.3,
|
|
623
|
627
|
easing: (t) => t * t,
|
|
624
|
|
- end: { taper: 1 },
|
|
625
|
|
- start: { taper: 1 + 32 * (st * st * st) },
|
|
|
628
|
+ end: { cap: true },
|
|
|
629
|
+ start: { cap: true },
|
|
626
|
630
|
simulatePressure: true,
|
|
627
|
631
|
last: true,
|
|
628
|
632
|
}
|
|
|
@@ -641,8 +645,6 @@ function renderCurvedFreehandArrowShaft(shape: ArrowShape, circle: number[]) {
|
|
641
|
645
|
|
|
642
|
646
|
const strokeWidth = +getShapeStyle(style).strokeWidth * 2
|
|
643
|
647
|
|
|
644
|
|
- const st = Math.abs(getRandom())
|
|
645
|
|
-
|
|
646
|
648
|
const center = [circle[0], circle[1]]
|
|
647
|
649
|
const radius = circle[2]
|
|
648
|
650
|
|
|
|
@@ -658,16 +660,12 @@ function renderCurvedFreehandArrowShaft(shape: ArrowShape, circle: number[]) {
|
|
658
|
660
|
points.push(Vec.round(Vec.nudgeAtAngle(center, angle, radius)))
|
|
659
|
661
|
}
|
|
660
|
662
|
|
|
661
|
|
- const stroke = getStroke([...points, end.point, end.point], {
|
|
|
663
|
+ const stroke = getStroke([...points, end.point, end.point, end.point], {
|
|
662
|
664
|
size: strokeWidth / 2,
|
|
663
|
665
|
thinning: 0.5 + getRandom() * 0.3,
|
|
664
|
666
|
easing: (t) => t * t,
|
|
665
|
|
- end: {
|
|
666
|
|
- taper: shape.decorations?.end ? 1 : 1 + strokeWidth * 5 * (st * st * st),
|
|
667
|
|
- },
|
|
668
|
|
- start: {
|
|
669
|
|
- taper: shape.decorations?.start ? 1 : 1 + strokeWidth * 5 * (st * st * st),
|
|
670
|
|
- },
|
|
|
667
|
+ end: { cap: true },
|
|
|
668
|
+ start: { cap: true },
|
|
671
|
669
|
simulatePressure: true,
|
|
672
|
670
|
streamline: 0.01,
|
|
673
|
671
|
last: true,
|
|
|
@@ -782,68 +780,3 @@ function getArrowPath(shape: ArrowShape) {
|
|
782
|
780
|
|
|
783
|
781
|
return path.join(' ')
|
|
784
|
782
|
}
|
|
785
|
|
-
|
|
786
|
|
-// function getArrowHeadPath(shape: ArrowShape, point: number[], inset: number[]) {
|
|
787
|
|
-// const { left, right } = getArrowHeadPoints(shape, point, inset)
|
|
788
|
|
-// return ['M', left, 'L', point, right].join(' ')
|
|
789
|
|
-// }
|
|
790
|
|
-
|
|
791
|
|
-// function getArrowHeadPoints(shape: ArrowShape, point: number[], inset: number[]) {
|
|
792
|
|
-// // Use the shape's random seed to create minor offsets for the angles
|
|
793
|
|
-// const getRandom = Utils.rng(shape.id)
|
|
794
|
|
-
|
|
795
|
|
-// return {
|
|
796
|
|
-// left: Vec.rotWith(inset, point, Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
|
797
|
|
-// right: Vec.rotWith(inset, point, -Math.PI / 6 + (Math.PI / 12) * getRandom()),
|
|
798
|
|
-// }
|
|
799
|
|
-// }
|
|
800
|
|
-
|
|
801
|
|
-// function getDrawArrowPath(shape: ArrowShape) {
|
|
802
|
|
-// const {
|
|
803
|
|
-// decorations,
|
|
804
|
|
-// handles: { start, end, bend: _bend },
|
|
805
|
|
-// style,
|
|
806
|
|
-// } = shape
|
|
807
|
|
-
|
|
808
|
|
-// const { strokeWidth } = getShapeStyle(style, false)
|
|
809
|
|
-
|
|
810
|
|
-// const arrowDist = Vec.dist(start.point, end.point)
|
|
811
|
|
-
|
|
812
|
|
-// const arrowHeadLength = Math.min(arrowDist / 3, strokeWidth * 8)
|
|
813
|
|
-
|
|
814
|
|
-// const path: (string | number)[] = []
|
|
815
|
|
-
|
|
816
|
|
-// const isStraightLine = Vec.dist(_bend.point, Vec.round(Vec.med(start.point, end.point))) < 1
|
|
817
|
|
-
|
|
818
|
|
-// if (isStraightLine) {
|
|
819
|
|
-// // Path (line segment)
|
|
820
|
|
-// path.push(`M ${start.point} L ${end.point}`)
|
|
821
|
|
-
|
|
822
|
|
-// // Start arrow head
|
|
823
|
|
-// if (decorations?.start) {
|
|
824
|
|
-// path.push(getStraightArrowHeadPath(start.point, end.point, arrowHeadLength))
|
|
825
|
|
-// }
|
|
826
|
|
-
|
|
827
|
|
-// // End arrow head
|
|
828
|
|
-// if (decorations?.end) {
|
|
829
|
|
-// path.push(getStraightArrowHeadPath(end.point, start.point, arrowHeadLength))
|
|
830
|
|
-// }
|
|
831
|
|
-// } else {
|
|
832
|
|
-// const { center, radius, length } = getArrowArc(shape)
|
|
833
|
|
-
|
|
834
|
|
-// // Path (arc)
|
|
835
|
|
-// path.push(`M ${start.point} A ${radius} ${radius} 0 0 ${length > 0 ? '1' : '0'} ${end.point}`)
|
|
836
|
|
-
|
|
837
|
|
-// // Start Arrow head
|
|
838
|
|
-// if (decorations?.start) {
|
|
839
|
|
-// path.push(getCurvedArrowHeadPath(start.point, arrowHeadLength, center, radius, length < 0))
|
|
840
|
|
-// }
|
|
841
|
|
-
|
|
842
|
|
-// // End arrow head
|
|
843
|
|
-// if (decorations?.end) {
|
|
844
|
|
-// path.push(getCurvedArrowHeadPath(end.point, arrowHeadLength, center, radius, length >= 0))
|
|
845
|
|
-// }
|
|
846
|
|
-// }
|
|
847
|
|
-
|
|
848
|
|
-// return path.join(' ')
|
|
849
|
|
-// }
|