radames commited on
Commit
587ecf3
·
1 Parent(s): 838b286
frontend/src/lib/DrawingCanvas.svelte CHANGED
@@ -1,15 +1,17 @@
1
  <script lang="ts">
 
2
  import PxBrush from 'px-brush';
3
  import { onMount } from 'svelte';
4
  import type { Brush } from '../types';
5
- import { selectedBrush, selectedImageBlob, currentCanvas } from '$lib/store';
 
 
6
 
7
  let canvas: HTMLCanvasElement;
8
  let brush: HTMLCanvasElement;
9
 
10
  let brushCtx: CanvasRenderingContext2D;
11
  let ctx: CanvasRenderingContext2D;
12
- let pxBrush: PxBrush;
13
  let startPosition: { x: number; y: number } = { x: 0, y: 0 };
14
 
15
  $: {
@@ -20,28 +22,22 @@
20
  }
21
  }
22
  $: {
23
- if ($selectedImageBlob) {
24
- drawImage(ctx, $selectedImageBlob);
 
25
  }
26
  }
 
27
  onMount(() => {
28
  ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
29
  brushCtx = brush.getContext('2d') as CanvasRenderingContext2D;
30
  window.devicePixelRatio = 1;
31
- pxBrush = new PxBrush(canvas);
32
  $currentCanvas = canvas;
33
  clearCanvas(ctx);
34
  });
35
 
36
- const drawImage = (ctx: CanvasRenderingContext2D, blob: Blob) => {
37
- const img = new Image();
38
- img.onload = function () {
39
- ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
40
- };
41
- img.src = URL.createObjectURL(blob);
42
- };
43
-
44
  let mouseDown: boolean = false;
 
45
  function pointerEnter() {
46
  // brush.hidden = false;
47
  }
@@ -53,12 +49,22 @@
53
  function pointerDown(e: MouseEvent) {
54
  mouseDown = true;
55
  startPosition = getPosition(canvas, e);
56
- pxBrush.draw({
57
- from: startPosition,
58
- to: startPosition,
59
- size: $selectedBrush.size,
60
- color: $selectedBrush.color
 
 
 
 
 
 
 
 
 
61
  });
 
62
  }
63
  function pointerMove(e: MouseEvent) {
64
  const position = getPosition(canvas, e);
@@ -67,13 +73,23 @@
67
  if (!mouseDown) {
68
  return;
69
  }
70
- pxBrush.draw({
71
- from: startPosition,
72
- to: position,
73
- size: $selectedBrush.size,
74
- color: $selectedBrush.color
 
 
 
 
 
 
 
 
 
75
  });
76
  startPosition = position;
 
77
  }
78
  function getPosition(canvas: HTMLCanvasElement, event: MouseEvent) {
79
  const rect = canvas.getBoundingClientRect();
@@ -98,25 +114,72 @@
98
  ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
99
  ctx.fill();
100
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  </script>
102
 
103
- <div class="inline-block relative overflow-clip">
104
- <canvas
105
- bind:this={canvas}
106
- class="canvas"
107
- width="256"
108
- height="512"
109
- on:touchmove={(e) => e.preventDefault()}
110
- on:pointerenter={pointerEnter}
111
- on:pointerup={pointerOut}
112
- on:pointerleave={pointerOut}
113
- on:pointercancel={pointerOut}
114
- on:pointerout={pointerOut}
115
- on:pointermove={pointerMove}
116
- on:pointerdown={pointerDown}
117
- />
118
- <canvas bind:this={brush} class="brush" width="10" height="10" />
119
- <span class="label">{$selectedBrush?.label} </span>
 
 
 
 
 
 
 
120
  </div>
121
 
122
  <style lang="postcss" scoped>
 
1
  <script lang="ts">
2
+ import { nanoid } from 'nanoid';
3
  import PxBrush from 'px-brush';
4
  import { onMount } from 'svelte';
5
  import type { Brush } from '../types';
6
+ import UndoIcon from '$lib/Icons/Undo.svelte';
7
+
8
+ import { selectedBrush, selectedImage, currentCanvas, drawingLayers } from '$lib/store';
9
 
10
  let canvas: HTMLCanvasElement;
11
  let brush: HTMLCanvasElement;
12
 
13
  let brushCtx: CanvasRenderingContext2D;
14
  let ctx: CanvasRenderingContext2D;
 
15
  let startPosition: { x: number; y: number } = { x: 0, y: 0 };
16
 
17
  $: {
 
22
  }
23
  }
24
  $: {
25
+ if ($selectedImage) {
26
+ drawImage(ctx, $selectedImage);
27
+ $drawingLayers = new Map();
28
  }
29
  }
30
+
31
  onMount(() => {
32
  ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
33
  brushCtx = brush.getContext('2d') as CanvasRenderingContext2D;
34
  window.devicePixelRatio = 1;
 
35
  $currentCanvas = canvas;
36
  clearCanvas(ctx);
37
  });
38
 
 
 
 
 
 
 
 
 
39
  let mouseDown: boolean = false;
40
+ let currLayerId: string;
41
  function pointerEnter() {
42
  // brush.hidden = false;
43
  }
 
49
  function pointerDown(e: MouseEvent) {
50
  mouseDown = true;
51
  startPosition = getPosition(canvas, e);
52
+ // pxBrush.draw({
53
+ // from: startPosition,
54
+ // to: startPosition,
55
+ // size: $selectedBrush.size,
56
+ // color: $selectedBrush.color
57
+ // });
58
+
59
+ currLayerId = nanoid();
60
+ drawingLayers.update((map) => {
61
+ map.set(currLayerId, {
62
+ brush: $selectedBrush,
63
+ points: [{ from: startPosition, to: startPosition }]
64
+ });
65
+ return map;
66
  });
67
+ drawLayers();
68
  }
69
  function pointerMove(e: MouseEvent) {
70
  const position = getPosition(canvas, e);
 
73
  if (!mouseDown) {
74
  return;
75
  }
76
+ // pxBrush.draw({
77
+ // from: startPosition,
78
+ // to: position,
79
+ // size: $selectedBrush.size,
80
+ // color: $selectedBrush.color
81
+ // });
82
+
83
+ drawingLayers.update((map) => {
84
+ const currentLayer = map.get(currLayerId);
85
+ currentLayer?.points.push({
86
+ from: startPosition,
87
+ to: position
88
+ });
89
+ return map;
90
  });
91
  startPosition = position;
92
+ drawLayers();
93
  }
94
  function getPosition(canvas: HTMLCanvasElement, event: MouseEvent) {
95
  const rect = canvas.getBoundingClientRect();
 
114
  ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
115
  ctx.fill();
116
  }
117
+ function drawImage(ctx: CanvasRenderingContext2D, img: HTMLImageElement | HTMLCanvasElement) {
118
+ ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
119
+ }
120
+ function rollBack() {
121
+ if ($drawingLayers.size <= 0) {
122
+ return;
123
+ }
124
+ // clear current layer
125
+ const ids = Array.from($drawingLayers.keys());
126
+ drawingLayers.update((map) => {
127
+ map.delete(ids[ids.length - 1]);
128
+ return map;
129
+ });
130
+ drawLayers();
131
+ }
132
+ function drawLayers() {
133
+ const tempcanvas = document.createElement('canvas');
134
+ tempcanvas.width = 256;
135
+ tempcanvas.height = 512;
136
+ window.devicePixelRatio = 1;
137
+ const pxBrush = new PxBrush(tempcanvas);
138
+ clearCanvas(ctx);
139
+ if ($selectedImage) {
140
+ drawImage(ctx, $selectedImage);
141
+ }
142
+ Array.from($drawingLayers.values()).forEach((layer) => {
143
+ // clear temp canvas
144
+ layer.points.forEach((point, i) => {
145
+ pxBrush.draw({
146
+ from: point.from,
147
+ to: point.to,
148
+ size: layer.brush.size,
149
+ color: layer.brush.color
150
+ });
151
+ });
152
+ });
153
+ requestAnimationFrame(() => {
154
+ drawImage(ctx, tempcanvas);
155
+ });
156
+ }
157
  </script>
158
 
159
+ <div>
160
+ <div class="inline-block relative overflow-clip">
161
+ <canvas
162
+ bind:this={canvas}
163
+ class="canvas"
164
+ width="256"
165
+ height="512"
166
+ on:touchmove={(e) => e.preventDefault()}
167
+ on:pointerenter={pointerEnter}
168
+ on:pointerup={pointerOut}
169
+ on:pointerleave={pointerOut}
170
+ on:pointercancel={pointerOut}
171
+ on:pointerout={pointerOut}
172
+ on:pointermove={pointerMove}
173
+ on:pointerdown={pointerDown}
174
+ />
175
+ <canvas bind:this={brush} class="brush" width="10" height="10" />
176
+ <span class="label">{$selectedBrush?.label} </span>
177
+ <button
178
+ class="absolute bottom-1 left-1"
179
+ on:click|preventDefault={() => rollBack()}
180
+ disabled={$drawingLayers.size <= 0}><UndoIcon /></button
181
+ >
182
+ </div>
183
  </div>
184
 
185
  <style lang="postcss" scoped>
frontend/src/lib/Icons/Undo.svelte ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ export let classNames = '';
3
+ </script>
4
+
5
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="0 0 512 512" class={classNames}
6
+ ><path
7
+ fill="white"
8
+ stroke="black"
9
+ stroke-width="30"
10
+ d="M480 256c0 123.4-100.5 223.9-223.9 223.9c-48.84 0-95.17-15.58-134.2-44.86c-14.12-10.59-16.97-30.66-6.375-44.81c10.59-14.12 30.62-16.94 44.81-6.375c27.84 20.91 61 31.94 95.88 31.94C344.3 415.8 416 344.1 416 256s-71.69-159.8-159.8-159.8c-37.46 0-73.09 13.49-101.3 36.64l45.12 45.14c17.01 17.02 4.955 46.1-19.1 46.1H35.17C24.58 224.1 16 215.5 16 204.9V59.04c0-24.04 29.07-36.08 46.07-19.07l47.6 47.63C149.9 52.71 201.5 32.11 256.1 32.11C379.5 32.11 480 132.6 480 256z"
11
+ /></svg
12
+ >
frontend/src/lib/TemplateGallery.svelte CHANGED
@@ -1,16 +1,31 @@
1
  <script lang="ts">
2
  import { IMAGES_LIST } from '../data';
3
- import { selectedImageBlob, generateHuman } from '$lib/store';
4
  import { base } from '$app/paths';
5
 
6
  const submit = async (e: Event) => {
7
  e.preventDefault();
8
  const src = IMAGES_LIST[parseInt((e.target as HTMLInputElement).value)];
9
  if (src) {
10
- const blob = await fetch(base + src).then((res) => res.blob());
11
- $selectedImageBlob = blob;
 
12
  }
13
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  </script>
15
 
16
  <div>
 
1
  <script lang="ts">
2
  import { IMAGES_LIST } from '../data';
3
+ import { selectedImage, generateHuman } from '$lib/store';
4
  import { base } from '$app/paths';
5
 
6
  const submit = async (e: Event) => {
7
  e.preventDefault();
8
  const src = IMAGES_LIST[parseInt((e.target as HTMLInputElement).value)];
9
  if (src) {
10
+ const blob = await fetch(base + src).then((res) => res.blob())
11
+ const img = await getImage(blob);
12
+ $selectedImage = img;
13
  }
14
  };
15
+
16
+ async function getImage(blob: Blob): Promise<HTMLImageElement> {
17
+ return new Promise((resolve, reject) => {
18
+ const img = new Image();
19
+ img.onload = () => {
20
+ URL.revokeObjectURL(img.src);
21
+ resolve(img);
22
+ };
23
+ img.onerror = (err) => {
24
+ reject(err);
25
+ };
26
+ img.src = URL.createObjectURL(blob);
27
+ });
28
+ }
29
  </script>
30
 
31
  <div>
frontend/src/lib/store.ts CHANGED
@@ -1,10 +1,11 @@
1
  import { writable } from 'svelte/store';
2
- import type { Brush, Params } from '../types';
3
  import { randomSeed } from '$lib/utils';
4
 
 
5
  export const resultImage = writable<string>();
6
  export const currentCanvas = writable<HTMLCanvasElement>();
7
- export const selectedImageBlob = writable<Blob>();
8
  export const selectedBrush = writable<Brush>();
9
  export const selectedParams = writable<Params>({
10
  texture: '',
 
1
  import { writable } from 'svelte/store';
2
+ import type { Brush, Params, DrawingLayer } from '../types';
3
  import { randomSeed } from '$lib/utils';
4
 
5
+ export const drawingLayers = writable<Map<string, DrawingLayer>>(new Map());
6
  export const resultImage = writable<string>();
7
  export const currentCanvas = writable<HTMLCanvasElement>();
8
+ export const selectedImage = writable<HTMLImageElement>();
9
  export const selectedBrush = writable<Brush>();
10
  export const selectedParams = writable<Params>({
11
  texture: '',
frontend/src/types.ts CHANGED
@@ -22,3 +22,15 @@ export interface FormElements extends HTMLCollection {
22
  texture1: HTMLInputElement;
23
  texture2: HTMLInputElement;
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  texture1: HTMLInputElement;
23
  texture2: HTMLInputElement;
24
  }
25
+ interface Point {
26
+ x: number;
27
+ y: number;
28
+ }
29
+ interface pxPoint {
30
+ from: Point;
31
+ to: Point;
32
+ }
33
+ export interface DrawingLayer {
34
+ brush: Brush;
35
+ points: pxPoint[];
36
+ }