comparison Resources/Graveyard/Deprecated/Platforms/Wasm/wasm-viewport.ts @ 1503:553084468225

moving /Deprecated/ to /Resources/Graveyard/Deprecated/
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 30 Jun 2020 11:38:13 +0200
parents Deprecated/Platforms/Wasm/wasm-viewport.ts@419d0320c344
children
comparison
equal deleted inserted replaced
1502:e5729dab3f67 1503:553084468225
1 import * as wasmApplicationRunner from './wasm-application-runner'
2 import * as Logger from './logger'
3
4 var isPendingRedraw = false;
5
6 function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
7 {
8 if (!isPendingRedraw) {
9 isPendingRedraw = true;
10 Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed');
11 window.requestAnimationFrame(function() {
12 isPendingRedraw = false;
13 let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle);
14 if (viewport) {
15 viewport.Redraw();
16 }
17 });
18 }
19 }
20
21 (<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw;
22
23 declare function UTF8ToString(v: any): string;
24
25 function CreateWasmViewport(htmlCanvasId: string) : any {
26 var cppViewportHandle = wasmApplicationRunner.CreateCppViewport();
27 var canvasId = UTF8ToString(htmlCanvasId);
28 var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted
29 webViewport.Initialize();
30
31 return cppViewportHandle;
32 }
33
34 (<any>window).CreateWasmViewport = CreateWasmViewport;
35
36 export class WasmViewport {
37
38 private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle
39 private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId
40
41 private module_ : any;
42 private canvasId_ : string;
43 private htmlCanvas_ : HTMLCanvasElement;
44 private context_ : CanvasRenderingContext2D | null;
45 private imageData_ : any = null;
46 private renderingBuffer_ : any = null;
47
48 private touchGestureInProgress_: boolean = false;
49 private touchCount_: number = 0;
50 private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch
51
52 private touchZoom_ : any = false;
53 private touchTranslation_ : any = false;
54
55 private ViewportSetSize : Function;
56 private ViewportRender : Function;
57 private ViewportMouseDown : Function;
58 private ViewportMouseMove : Function;
59 private ViewportMouseUp : Function;
60 private ViewportMouseEnter : Function;
61 private ViewportMouseLeave : Function;
62 private ViewportMouseWheel : Function;
63 private ViewportKeyPressed : Function;
64 private ViewportTouchStart : Function;
65 private ViewportTouchMove : Function;
66 private ViewportTouchEnd : Function;
67
68 private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
69
70 public constructor(module: any, canvasId: string, cppViewport: any) {
71
72 this.pimpl_ = cppViewport;
73 WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this;
74 WasmViewport.viewportsMapByCanvasId_[canvasId] = this;
75
76 this.module_ = module;
77 this.canvasId_ = canvasId;
78 this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
79 if (this.htmlCanvas_ == null) {
80 Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'");
81 }
82 this.context_ = this.htmlCanvas_.getContext('2d');
83
84 this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]);
85 this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]);
86 this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]);
87 this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]);
88 this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]);
89 this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]);
90 this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]);
91 this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]);
92 this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]);
93 this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
94 this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
95 this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
96 }
97
98 public GetCppViewport() : number {
99 return this.pimpl_;
100 }
101
102 public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null {
103 if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) {
104 return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle];
105 }
106 Logger.defaultLogger.error("WasmViewport not found !");
107 return null;
108 }
109
110 public static GetFromCanvasId(canvasId: string) : WasmViewport | null {
111 if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) {
112 return WasmViewport.viewportsMapByCanvasId_[canvasId];
113 }
114 Logger.defaultLogger.error("WasmViewport not found !");
115 return null;
116 }
117
118 public static ResizeAll() {
119 for (let canvasId in WasmViewport.viewportsMapByCanvasId_) {
120 WasmViewport.viewportsMapByCanvasId_[canvasId].Resize();
121 }
122 }
123
124 public Redraw() {
125 if (this.imageData_ === null ||
126 this.renderingBuffer_ === null ||
127 this.ViewportRender(this.pimpl_,
128 this.imageData_.width,
129 this.imageData_.height,
130 this.renderingBuffer_) == 0) {
131 Logger.defaultLogger.error('The rendering has failed');
132 } else {
133 // Create an accessor to the rendering buffer (i.e. create a
134 // "window" above the heap of the WASM module), then copy it to
135 // the ImageData object
136 this.imageData_.data.set(new Uint8ClampedArray(
137 this.module_.HEAPU8.buffer,
138 this.renderingBuffer_,
139 this.imageData_.width * this.imageData_.height * 4));
140
141 if (this.context_) {
142 this.context_.putImageData(this.imageData_, 0, 0);
143 }
144 }
145 }
146
147 public Resize() {
148 if (this.imageData_ != null &&
149 (this.imageData_.width != window.innerWidth ||
150 this.imageData_.height != window.innerHeight)) {
151 this.imageData_ = null;
152 }
153
154 // width/height is defined by the parent width/height
155 if (this.htmlCanvas_.parentElement) {
156 this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth;
157 this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight;
158
159 Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
160
161 if (this.imageData_ === null && this.context_) {
162 this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height);
163 this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
164
165 if (this.renderingBuffer_ != null) {
166 this.module_._free(this.renderingBuffer_);
167 }
168
169 this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4);
170 } else {
171 this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
172 }
173
174 this.Redraw();
175 }
176 }
177
178 public Initialize() {
179
180 // Force the rendering of the viewport for the first time
181 this.Resize();
182
183 var that : WasmViewport = this;
184 // Register an event listener to call the Resize() function
185 // each time the window is resized.
186 window.addEventListener('resize', function(event) {
187 that.Resize();
188 }, false);
189
190 this.htmlCanvas_.addEventListener('contextmenu', function(event) {
191 // Prevent right click on the canvas
192 event.preventDefault();
193 }, false);
194
195 this.htmlCanvas_.addEventListener('mouseleave', function(event) {
196 that.ViewportMouseLeave(that.pimpl_);
197 });
198
199 this.htmlCanvas_.addEventListener('mouseenter', function(event) {
200 that.ViewportMouseEnter(that.pimpl_);
201 });
202
203 this.htmlCanvas_.addEventListener('mousedown', function(event) {
204 var x = event.pageX - this.offsetLeft;
205 var y = event.pageY - this.offsetTop;
206
207 that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/);
208 });
209
210 this.htmlCanvas_.addEventListener('mousemove', function(event) {
211 var x = event.pageX - this.offsetLeft;
212 var y = event.pageY - this.offsetTop;
213 that.ViewportMouseMove(that.pimpl_, x, y);
214 });
215
216 this.htmlCanvas_.addEventListener('mouseup', function(event) {
217 that.ViewportMouseUp(that.pimpl_);
218 });
219
220 window.addEventListener('keydown', function(event) {
221 var keyChar: string | null = event.key;
222 var keyCode = event.keyCode
223 if (keyChar.length == 1) {
224 keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic
225 } else {
226 keyChar = null;
227 }
228 // console.log("key: ", keyCode, keyChar);
229 that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey);
230 });
231
232 this.htmlCanvas_.addEventListener('wheel', function(event) {
233 var x = event.pageX - this.offsetLeft;
234 var y = event.pageY - this.offsetTop;
235 that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey);
236 event.preventDefault();
237 }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
238
239 this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) {
240 // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
241 event.preventDefault();
242 event.stopPropagation();
243
244 // TODO: find a way to pass the coordinates as an array between JS and C++
245 var x0 = 0;
246 var y0 = 0;
247 var x1 = 0;
248 var y1 = 0;
249 var x2 = 0;
250 var y2 = 0;
251 if (event.targetTouches.length > 0) {
252 x0 = event.targetTouches[0].pageX;
253 y0 = event.targetTouches[0].pageY;
254 }
255 if (event.targetTouches.length > 1) {
256 x1 = event.targetTouches[1].pageX;
257 y1 = event.targetTouches[1].pageY;
258 }
259 if (event.targetTouches.length > 2) {
260 x2 = event.targetTouches[2].pageX;
261 y2 = event.targetTouches[2].pageY;
262 }
263
264 that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
265 }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
266
267 this.htmlCanvas_.addEventListener('touchend', function(event) {
268 // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
269 event.preventDefault();
270 event.stopPropagation();
271
272 // TODO: find a way to pass the coordinates as an array between JS and C++
273 var x0 = 0;
274 var y0 = 0;
275 var x1 = 0;
276 var y1 = 0;
277 var x2 = 0;
278 var y2 = 0;
279 if (event.targetTouches.length > 0) {
280 x0 = event.targetTouches[0].pageX;
281 y0 = event.targetTouches[0].pageY;
282 }
283 if (event.targetTouches.length > 1) {
284 x1 = event.targetTouches[1].pageX;
285 y1 = event.targetTouches[1].pageY;
286 }
287 if (event.targetTouches.length > 2) {
288 x2 = event.targetTouches[2].pageX;
289 y2 = event.targetTouches[2].pageY;
290 }
291
292 that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
293 });
294
295 this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) {
296
297 // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
298 event.preventDefault();
299 event.stopPropagation();
300
301
302 // TODO: find a way to pass the coordinates as an array between JS and C++
303 var x0 = 0;
304 var y0 = 0;
305 var x1 = 0;
306 var y1 = 0;
307 var x2 = 0;
308 var y2 = 0;
309 if (event.targetTouches.length > 0) {
310 x0 = event.targetTouches[0].pageX;
311 y0 = event.targetTouches[0].pageY;
312 }
313 if (event.targetTouches.length > 1) {
314 x1 = event.targetTouches[1].pageX;
315 y1 = event.targetTouches[1].pageY;
316 }
317 if (event.targetTouches.length > 2) {
318 x2 = event.targetTouches[2].pageX;
319 y2 = event.targetTouches[2].pageY;
320 }
321
322 that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
323 return;
324
325 }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
326 }
327
328 public ResetTouch() {
329 if (this.touchTranslation_ ||
330 this.touchZoom_) {
331 this.ViewportMouseUp(this.pimpl_);
332 }
333
334 this.touchTranslation_ = false;
335 this.touchZoom_ = false;
336 }
337
338 public GetTouchTranslation(event: any) {
339 var touch = event.targetTouches[0];
340 return [
341 touch.pageX,
342 touch.pageY
343 ];
344 }
345
346 public GetTouchZoom(event: any) {
347 var touch1 = event.targetTouches[0];
348 var touch2 = event.targetTouches[1];
349 var dx = (touch1.pageX - touch2.pageX);
350 var dy = (touch1.pageY - touch2.pageY);
351 var d = Math.sqrt(dx * dx + dy * dy);
352 return [
353 (touch1.pageX + touch2.pageX) / 2.0,
354 (touch1.pageY + touch2.pageY) / 2.0,
355 d
356 ];
357 }
358
359 }