Mercurial > hg > orthanc-stone
comparison Deprecated/Platforms/Wasm/wasm-viewport.ts @ 1400:419d0320c344
moved Platforms into Deprecated
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Wed, 29 Apr 2020 20:45:14 +0200 |
parents | Platforms/Wasm/wasm-viewport.ts@67d0a8da4afe |
children |
comparison
equal
deleted
inserted
replaced
1399:ff8d2e46ac63 | 1400:419d0320c344 |
---|---|
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 } |