소스 검색

Fix arrow bend behavior

main
Steve Ruiz 4 년 전
부모
커밋
62a3da0498

+ 0
- 1
packages/core/src/components/page/page.tsx 파일 보기

@@ -58,7 +58,6 @@ export function Page<T extends TLShape>({
58 58
         <Bounds zoom={zoom} bounds={bounds} isLocked={isLocked} rotation={rotation} />
59 59
       )}
60 60
       {!hideIndicators &&
61
-        selectedIds.length > 1 &&
62 61
         selectedIds
63 62
           .filter(Boolean)
64 63
           .map((id) => (

+ 2
- 0
packages/core/src/hooks/useShapeTree.tsx 파일 보기

@@ -96,6 +96,8 @@ export function useShapeTree<T extends TLShape, M extends Record<string, unknown
96 96
   // Call onChange callback when number of rendering shapes changes
97 97
 
98 98
   if (shapesToRender.length !== rPreviousCount.current) {
99
+    // Use a timeout to clear call stack, in case the onChange handleer
100
+    // produces a new state change (React won't like that)
99 101
     setTimeout(() => onChange?.(shapesToRender.map((shape) => shape.id)), 0)
100 102
     rPreviousCount.current = shapesToRender.length
101 103
   }

+ 1
- 1
packages/core/src/utils/vec.tsx 파일 보기

@@ -510,7 +510,7 @@ export class Vec {
510 510
     return Array.from(Array(steps))
511 511
       .map((_, i) => {
512 512
         const t = i / steps
513
-        return t * t * t
513
+        return t * t * t * t
514 514
       })
515 515
       .map((t) => Vec.round([...Vec.lrp(a, b, t), (1 - t) / 2]))
516 516
   }

+ 51
- 118
packages/tldraw/src/shape/shapes/arrow/arrow.tsx 파일 보기

@@ -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
-// }

+ 9
- 5
packages/tldraw/src/shape/shapes/rectangle/rectangle.tsx 파일 보기

@@ -11,6 +11,9 @@ import {
11 11
   TLDrawRenderInfo,
12 12
 } from '~types'
13 13
 
14
+// TODO
15
+// [ ] - Make sure that fill does not extend drawn shape at corners
16
+
14 17
 export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
15 18
   type = TLDrawShapeType.Rectangle as const
16 19
   toolType = TLDrawToolType.Bounds
@@ -351,13 +354,14 @@ function renderPath(shape: RectangleShape) {
351 354
     Math.floor(5 + getRandom() * 4)
352 355
   )
353 356
 
354
-  const stroke = getStroke([...lines.flat().slice(2), ...lines[0], ...lines[0].slice(4)], {
355
-    size: 1 + +styles.strokeWidth,
356
-    thinning: 0.6,
357
+  const stroke = getStroke([...lines.flat().slice(4), ...lines[0], ...lines[0].slice(4)], {
358
+    size: 1 + styles.strokeWidth,
359
+    thinning: 0.618,
357 360
     easing: (t) => t * t * t * t,
358
-    end: { taper: +styles.strokeWidth * 20 },
359
-    start: { taper: +styles.strokeWidth * 20 },
361
+    end: { cap: true },
362
+    start: { cap: true },
360 363
     simulatePressure: false,
364
+    last: true,
361 365
   })
362 366
 
363 367
   return Utils.getSvgPathFromStroke(stroke)

Loading…
취소
저장