rough-stuff/rough

Feature Request: Continous rough lines with svg path

dai-shi opened this issue · 5 comments

Ref: excalidraw/excalidraw#1931 (comment)

Currently, if we draw an svg path with lines and curves, it will be like this:

image

What we would like to have is like this:

image


With generator.path("M 0 0 L 100 0 C 120 0, 140 20, 140 40");

current:

image

with this new feature:

image

The straight line and the curved line are connected.

Even with generator.path("M 0 0 C 0 0, 100 0, 100 0 C 120 0, 140 20, 140 40"); we get a result similar to the first result. So, it's not about straight lines and curved lines, but about any svg path items.

@dai-shi what was your hack to get this working? I am also desiring this behavior for a personal project.

@jazzychad The idea is pretty simple. Just keep the last position and use it as a start position (instead of randomly creating a new point) for the next part of the path.

@jazzychad I found my local working directory. Here's the patch I tried.

diff --git a/src/renderer.ts b/src/renderer.ts
index f3512b4..3655450 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -138,6 +138,8 @@ export function arc(x: number, y: number, width: number, height: number, start:
   return { type: 'path', ops };
 }
 
+let lastCurrent: any = [];
+
 export function svgPath(path: string, o: ResolvedOptions): OpSet {
   const segments = normalize(absolutize(parsePath(path)));
   const ops: Op[] = [];
@@ -148,17 +150,28 @@ export function svgPath(path: string, o: ResolvedOptions): OpSet {
       case 'M': {
         const ro = 1 * (o.maxRandomnessOffset || 0);
         ops.push({ op: 'move', data: data.map((d) => d + _offsetOpt(ro, o)) });
+        console.log('a',data);
+        console.log('b',ops[ops.length-2]);
+        console.log('c',ops[ops.length-1]);
         current = [data[0], data[1]];
         first = [data[0], data[1]];
         break;
       }
       case 'L':
         ops.push(..._doubleLine(current[0], current[1], data[0], data[1], o));
+        lastCurrent = [
+          ops[ops.length-3].data.slice(-2),
+          ops[ops.length-1].data.slice(-2),
+        ];
         current = [data[0], data[1]];
         break;
       case 'C': {
         const [x1, y1, x2, y2, x, y] = data;
         ops.push(..._bezierTo(x1, y1, x2, y2, x, y, current, o));
+        lastCurrent = [
+          ops[ops.length-3].data.slice(-2),
+          ops[ops.length-1].data.slice(-2),
+        ];
         current = [x, y];
         break;
       }
@@ -259,11 +272,15 @@ function _offsetOpt(x: number, ops: ResolvedOptions, roughnessGain = 1): number
 
 function _doubleLine(x1: number, y1: number, x2: number, y2: number, o: ResolvedOptions, filling = false): Op[] {
   const singleStroke = filling ? o.disableMultiStrokeFill : o.disableMultiStroke;
-  const o1 = _line(x1, y1, x2, y2, o, true, false);
+  const o1 = lastCurrent[0]
+    ? _line(lastCurrent[0][0], lastCurrent[0][1], x2, y2, o, true, false)
+    : _line(x1, y1, x2, y2, o, true, false);
   if (singleStroke) {
     return o1;
   }
-  const o2 = _line(x1, y1, x2, y2, o, true, true);
+  const o2 = lastCurrent[1]
+    ? _line(lastCurrent[1][0], lastCurrent[1][1], x2, y2, o, true, true)
+    : _line(x1, y1, x2, y2, o, true, true);
   return o1.concat(o2);
 }
 
@@ -296,15 +313,15 @@ function _line(x1: number, y1: number, x2: number, y2: number, o: ResolvedOption
     if (overlay) {
       ops.push({
         op: 'move', data: [
-          x1 + randomHalf(),
-          y1 + randomHalf()
+          x1 + randomHalf() * 0,
+          y1 + randomHalf() * 0
         ]
       });
     } else {
       ops.push({
         op: 'move', data: [
-          x1 + _offsetOpt(offset, o, roughnessGain),
-          y1 + _offsetOpt(offset, o, roughnessGain)
+          x1 + _offsetOpt(offset, o, roughnessGain * 0),
+          y1 + _offsetOpt(offset, o, roughnessGain * 0)
         ]
       });
     }
@@ -456,6 +473,9 @@ function _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y:
   let f: Point = [0, 0];
   const iterations = o.disableMultiStroke ? 1 : 2;
   for (let i = 0; i < iterations; i++) {
+    if (lastCurrent[i]) {
+      ops.push({ op: 'move', data: [lastCurrent[i][0], lastCurrent[i][1]] });
+    } else
     if (i === 0) {
       ops.push({ op: 'move', data: [current[0], current[1]] });
     } else {
@@ -471,4 +491,4 @@ function _bezierTo(x1: number, y1: number, x2: number, y2: number, x: number, y:
     });
   }
   return ops;
-}
\ No newline at end of file
+}

@dai-shi Awesome, thank you!!

This is now implemented by #178
Add preserveVertices: true to the options and you get continuous rendering of paths.