|  | @@ -18,6 +18,21 @@ const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  var elements = Array.of<ExcalidrawElement>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316
 | 
	
		
			
				|  |  | +const LCG = (seed: number) => () =>
 | 
	
		
			
				|  |  | +  ((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Unfortunately, roughjs doesn't support a seed attribute (https://github.com/pshihn/rough/issues/27).
 | 
	
		
			
				|  |  | +// We can achieve the same result by overriding the Math.random function with a
 | 
	
		
			
				|  |  | +// pseudo random generator that supports a random seed and swapping it back after.
 | 
	
		
			
				|  |  | +function withCustomMathRandom<T>(seed: number, cb: () => T): T {
 | 
	
		
			
				|  |  | +  const random = Math.random;
 | 
	
		
			
				|  |  | +  Math.random = LCG(seed);
 | 
	
		
			
				|  |  | +  const result = cb();
 | 
	
		
			
				|  |  | +  Math.random = random;
 | 
	
		
			
				|  |  | +  return result;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // https://stackoverflow.com/a/6853926/232122
 | 
	
		
			
				|  |  |  function distanceBetweenPointAndSegment(
 | 
	
		
			
				|  |  |    x: number,
 | 
	
	
		
			
				|  | @@ -126,6 +141,7 @@ function newElement(
 | 
	
		
			
				|  |  |      isSelected: false,
 | 
	
		
			
				|  |  |      strokeColor: strokeColor,
 | 
	
		
			
				|  |  |      backgroundColor: backgroundColor,
 | 
	
		
			
				|  |  | +    seed: Math.floor(Math.random() * 2 ** 31),
 | 
	
		
			
				|  |  |      draw(rc: RoughCanvas, context: CanvasRenderingContext2D) {}
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |    return element;
 | 
	
	
		
			
				|  | @@ -307,22 +323,27 @@ function generateDraw(element: ExcalidrawElement) {
 | 
	
		
			
				|  |  |        context.fillStyle = fillStyle;
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |    } else if (element.type === "rectangle") {
 | 
	
		
			
				|  |  | -    const shape = generator.rectangle(0, 0, element.width, element.height, {
 | 
	
		
			
				|  |  | -      stroke: element.strokeColor,
 | 
	
		
			
				|  |  | -      fill: element.backgroundColor
 | 
	
		
			
				|  |  | +    const shape = withCustomMathRandom(element.seed, () => {
 | 
	
		
			
				|  |  | +      return generator.rectangle(0, 0, element.width, element.height, {
 | 
	
		
			
				|  |  | +        stroke: element.strokeColor,
 | 
	
		
			
				|  |  | +        fill: element.backgroundColor
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |      });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      element.draw = (rc, context) => {
 | 
	
		
			
				|  |  |        context.translate(element.x, element.y);
 | 
	
		
			
				|  |  |        rc.draw(shape);
 | 
	
		
			
				|  |  |        context.translate(-element.x, -element.y);
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |    } else if (element.type === "ellipse") {
 | 
	
		
			
				|  |  | -    const shape = generator.ellipse(
 | 
	
		
			
				|  |  | -      element.width / 2,
 | 
	
		
			
				|  |  | -      element.height / 2,
 | 
	
		
			
				|  |  | -      element.width,
 | 
	
		
			
				|  |  | -      element.height,
 | 
	
		
			
				|  |  | -      { stroke: element.strokeColor, fill: element.backgroundColor }
 | 
	
		
			
				|  |  | +    const shape = withCustomMathRandom(element.seed, () =>
 | 
	
		
			
				|  |  | +      generator.ellipse(
 | 
	
		
			
				|  |  | +        element.width / 2,
 | 
	
		
			
				|  |  | +        element.height / 2,
 | 
	
		
			
				|  |  | +        element.width,
 | 
	
		
			
				|  |  | +        element.height,
 | 
	
		
			
				|  |  | +        { stroke: element.strokeColor, fill: element.backgroundColor }
 | 
	
		
			
				|  |  | +      )
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      element.draw = (rc, context) => {
 | 
	
		
			
				|  |  |        context.translate(element.x, element.y);
 | 
	
	
		
			
				|  | @@ -331,14 +352,14 @@ function generateDraw(element: ExcalidrawElement) {
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |    } else if (element.type === "arrow") {
 | 
	
		
			
				|  |  |      const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
 | 
	
		
			
				|  |  | -    const shapes = [
 | 
	
		
			
				|  |  | +    const shapes = withCustomMathRandom(element.seed, () => [
 | 
	
		
			
				|  |  |        //    \
 | 
	
		
			
				|  |  |        generator.line(x3, y3, x2, y2, { stroke: element.strokeColor }),
 | 
	
		
			
				|  |  |        // -----
 | 
	
		
			
				|  |  |        generator.line(x1, y1, x2, y2, { stroke: element.strokeColor }),
 | 
	
		
			
				|  |  |        //    /
 | 
	
		
			
				|  |  |        generator.line(x4, y4, x2, y2, { stroke: element.strokeColor })
 | 
	
		
			
				|  |  | -    ];
 | 
	
		
			
				|  |  | +    ]);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      element.draw = (rc, context) => {
 | 
	
		
			
				|  |  |        context.translate(element.x, element.y);
 |