Mercurial > hg > orthanc-stone
annotate Platforms/Wasm/wasm-viewport.ts @ 403:99e31898910e
IObservable.cpp
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sun, 11 Nov 2018 12:13:31 +0100 |
parents | 8716176ff7f0 |
children | 8bf717c4e497 |
rev | line source |
---|---|
230 | 1 var isPendingRedraw = false; |
229 | 2 |
230 | 3 function ScheduleWebViewportRedraw(cppViewportHandle: any) : void |
4 { | |
5 if (!isPendingRedraw) { | |
6 isPendingRedraw = true; | |
7 console.log('Scheduling a refresh of the viewport, as its content changed'); | |
8 window.requestAnimationFrame(function() { | |
9 isPendingRedraw = false; | |
10 Stone.WasmViewport.GetFromCppViewport(cppViewportHandle).Redraw(); | |
11 }); | |
229 | 12 } |
230 | 13 } |
226 | 14 |
231
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
15 declare function UTF8ToString(any): string; |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
16 |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
17 function CreateWasmViewport(htmlCanvasId: string) : any { |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
18 var cppViewportHandle = CreateCppViewport(); |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
19 var canvasId = UTF8ToString(htmlCanvasId); |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
20 var webViewport = new Stone.WasmViewport(StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
21 webViewport.Initialize(); |
314 | 22 |
231
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
23 return cppViewportHandle; |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
24 } |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
25 |
226 | 26 module Stone { |
27 | |
28 // export declare type InitializationCallback = () => void; | |
29 | |
30 // export declare var StoneFrameworkModule : any; | |
31 | |
32 //const ASSETS_FOLDER : string = "assets/lib"; | |
33 //const WASM_FILENAME : string = "orthanc-framework"; | |
229 | 34 |
226 | 35 export class WasmViewport { |
36 | |
313 | 37 private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle |
38 private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId | |
229 | 39 |
226 | 40 private module_ : any; |
41 private canvasId_ : string; | |
42 private htmlCanvas_ : HTMLCanvasElement; | |
43 private context_ : CanvasRenderingContext2D; | |
44 private imageData_ : any = null; | |
45 private renderingBuffer_ : any = null; | |
46 private touchZoom_ : any = false; | |
47 private touchTranslation_ : any = false; | |
48 | |
49 private ViewportSetSize : Function; | |
50 private ViewportRender : Function; | |
51 private ViewportMouseDown : Function; | |
52 private ViewportMouseMove : Function; | |
53 private ViewportMouseUp : Function; | |
54 private ViewportMouseEnter : Function; | |
55 private ViewportMouseLeave : Function; | |
56 private ViewportMouseWheel : Function; | |
57 private ViewportKeyPressed : Function; | |
58 | |
227 | 59 private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object |
60 | |
231
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
61 public constructor(module: any, canvasId: string, cppViewport: any) { |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
62 |
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
63 this.pimpl_ = cppViewport; |
313 | 64 WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this; |
65 WasmViewport.viewportsMapByCanvasId_[canvasId] = this; | |
231
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
66 |
226 | 67 this.module_ = module; |
68 this.canvasId_ = canvasId; | |
69 this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; | |
244 | 70 if (this.htmlCanvas_ == null) { |
71 console.log("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'"); | |
72 } | |
226 | 73 this.context_ = this.htmlCanvas_.getContext('2d'); |
74 | |
234
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
75 this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
76 this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
77 this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
78 this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
79 this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
80 this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
81 this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
82 this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); |
327 | 83 this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); |
228 | 84 } |
85 | |
234
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
86 public GetCppViewport() : number { |
228 | 87 return this.pimpl_; |
226 | 88 } |
89 | |
234
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
90 public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport { |
313 | 91 if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { |
92 return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; | |
93 } | |
94 console.log("WasmViewport not found !"); | |
95 return undefined; | |
96 } | |
97 | |
98 public static GetFromCanvasId(canvasId: string) : WasmViewport { | |
99 if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { | |
100 return WasmViewport.viewportsMapByCanvasId_[canvasId]; | |
229 | 101 } |
102 console.log("WasmViewport not found !"); | |
103 return undefined; | |
104 } | |
105 | |
314 | 106 public static ResizeAll() { |
107 for (let canvasId in WasmViewport.viewportsMapByCanvasId_) { | |
108 WasmViewport.viewportsMapByCanvasId_[canvasId].Resize(); | |
109 } | |
110 } | |
111 | |
226 | 112 public Redraw() { |
113 if (this.imageData_ === null || | |
114 this.renderingBuffer_ === null || | |
227 | 115 this.ViewportRender(this.pimpl_, |
116 this.imageData_.width, | |
226 | 117 this.imageData_.height, |
118 this.renderingBuffer_) == 0) { | |
119 console.log('The rendering has failed'); | |
120 } else { | |
121 // Create an accessor to the rendering buffer (i.e. create a | |
122 // "window" above the heap of the WASM module), then copy it to | |
123 // the ImageData object | |
124 this.imageData_.data.set(new Uint8ClampedArray( | |
125 this.module_.buffer, | |
126 this.renderingBuffer_, | |
127 this.imageData_.width * this.imageData_.height * 4)); | |
128 | |
129 this.context_.putImageData(this.imageData_, 0, 0); | |
130 } | |
131 } | |
132 | |
133 public Resize() { | |
134 if (this.imageData_ != null && | |
135 (this.imageData_.width != window.innerWidth || | |
136 this.imageData_.height != window.innerHeight)) { | |
137 this.imageData_ = null; | |
138 } | |
139 | |
234
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
140 // width/height can be defined in percent of window width/height through html attributes like data-width-ratio="50" and data-height-ratio="20" |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
141 var widthRatio = Number(this.htmlCanvas_.dataset["widthRatio"]) || 100; |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
142 var heightRatio = Number(this.htmlCanvas_.dataset["heightRatio"]) || 100; |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
143 |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
144 this.htmlCanvas_.width = window.innerWidth * (widthRatio / 100); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
145 this.htmlCanvas_.height = window.innerHeight * (heightRatio / 100); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
146 |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
147 console.log("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); |
9afb50d1ac14
configure WasmViewport size relative to window size through html attributes
am@osimis.io
parents:
231
diff
changeset
|
148 |
226 | 149 if (this.imageData_ === null) { |
150 this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); | |
228 | 151 this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); |
226 | 152 |
153 if (this.renderingBuffer_ != null) { | |
154 this.module_._free(this.renderingBuffer_); | |
155 } | |
156 | |
157 this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); | |
313 | 158 } else { |
159 this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); | |
226 | 160 } |
161 | |
162 this.Redraw(); | |
163 } | |
164 | |
231
5027cb2feb51
viewport is now part of the Application itself and not global anymore
am@osimis.io
parents:
230
diff
changeset
|
165 public Initialize() { |
227 | 166 |
167 // Force the rendering of the viewport for the first time | |
168 this.Resize(); | |
226 | 169 |
228 | 170 var that : WasmViewport = this; |
227 | 171 // Register an event listener to call the Resize() function |
172 // each time the window is resized. | |
228 | 173 window.addEventListener('resize', function(event) { |
174 that.Resize(); | |
175 }, false); | |
226 | 176 |
227 | 177 this.htmlCanvas_.addEventListener('contextmenu', function(event) { |
178 // Prevent right click on the canvas | |
179 event.preventDefault(); | |
180 }, false); | |
181 | |
182 this.htmlCanvas_.addEventListener('mouseleave', function(event) { | |
228 | 183 that.ViewportMouseLeave(that.pimpl_); |
227 | 184 }); |
185 | |
186 this.htmlCanvas_.addEventListener('mouseenter', function(event) { | |
228 | 187 that.ViewportMouseEnter(that.pimpl_); |
227 | 188 }); |
189 | |
190 this.htmlCanvas_.addEventListener('mousedown', function(event) { | |
191 var x = event.pageX - this.offsetLeft; | |
192 var y = event.pageY - this.offsetTop; | |
228 | 193 that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO */); |
227 | 194 }); |
195 | |
196 this.htmlCanvas_.addEventListener('mousemove', function(event) { | |
197 var x = event.pageX - this.offsetLeft; | |
198 var y = event.pageY - this.offsetTop; | |
228 | 199 that.ViewportMouseMove(that.pimpl_, x, y); |
227 | 200 }); |
201 | |
202 this.htmlCanvas_.addEventListener('mouseup', function(event) { | |
228 | 203 that.ViewportMouseUp(that.pimpl_); |
227 | 204 }); |
205 | |
206 window.addEventListener('keydown', function(event) { | |
327 | 207 var keyChar = event.key; |
208 var keyCode = event.keyCode | |
209 if (keyChar.length == 1) { | |
210 keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic | |
211 } else { | |
212 keyChar = null; | |
213 } | |
214 // console.log("key: ", keyCode, keyChar); | |
215 that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey); | |
227 | 216 }); |
226 | 217 |
227 | 218 this.htmlCanvas_.addEventListener('wheel', function(event) { |
219 var x = event.pageX - this.offsetLeft; | |
220 var y = event.pageY - this.offsetTop; | |
228 | 221 that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); |
227 | 222 event.preventDefault(); |
223 }); | |
224 | |
225 this.htmlCanvas_.addEventListener('touchstart', function(event) { | |
226 that.ResetTouch(); | |
227 }); | |
228 | |
229 this.htmlCanvas_.addEventListener('touchend', function(event) { | |
230 that.ResetTouch(); | |
231 }); | |
232 | |
233 this.htmlCanvas_.addEventListener('touchmove', function(event) { | |
234 if (that.touchTranslation_.length == 2) { | |
235 var t = that.GetTouchTranslation(event); | |
228 | 236 that.ViewportMouseMove(that.pimpl_, t[0], t[1]); |
227 | 237 } |
238 else if (that.touchZoom_.length == 3) { | |
226 | 239 var z0 = that.touchZoom_; |
227 | 240 var z1 = that.GetTouchZoom(event); |
228 | 241 that.ViewportMouseMove(that.pimpl_, z0[0], z0[1] - z0[2] + z1[2]); |
227 | 242 } |
243 else { | |
244 // Realize the gesture event | |
245 if (event.targetTouches.length == 1) { | |
246 // Exactly one finger inside the canvas => Setup a translation | |
247 that.touchTranslation_ = that.GetTouchTranslation(event); | |
228 | 248 that.ViewportMouseDown(that.pimpl_, |
249 1 /* middle button */, | |
227 | 250 that.touchTranslation_[0], |
251 that.touchTranslation_[1], 0); | |
252 } else if (event.targetTouches.length == 2) { | |
253 // Exactly 2 fingers inside the canvas => Setup a pinch/zoom | |
254 that.touchZoom_ = that.GetTouchZoom(event); | |
255 var z0 = that.touchZoom_; | |
228 | 256 that.ViewportMouseDown(that.pimpl_, |
257 2 /* right button */, | |
227 | 258 z0[0], |
259 z0[1], 0); | |
260 } | |
261 } | |
262 }); | |
263 } | |
226 | 264 |
265 public ResetTouch() { | |
227 | 266 if (this.touchTranslation_ || |
267 this.touchZoom_) { | |
228 | 268 this.ViewportMouseUp(this.pimpl_); |
226 | 269 } |
227 | 270 |
271 this.touchTranslation_ = false; | |
272 this.touchZoom_ = false; | |
273 } | |
226 | 274 |
275 public GetTouchTranslation(event) { | |
227 | 276 var touch = event.targetTouches[0]; |
277 return [ | |
278 touch.pageX, | |
279 touch.pageY | |
280 ]; | |
281 } | |
226 | 282 |
283 public GetTouchZoom(event) { | |
227 | 284 var touch1 = event.targetTouches[0]; |
285 var touch2 = event.targetTouches[1]; | |
286 var dx = (touch1.pageX - touch2.pageX); | |
287 var dy = (touch1.pageY - touch2.pageY); | |
288 var d = Math.sqrt(dx * dx + dy * dy); | |
289 return [ | |
290 (touch1.pageX + touch2.pageX) / 2.0, | |
291 (touch1.pageY + touch2.pageY) / 2.0, | |
292 d | |
293 ]; | |
294 } | |
226 | 295 |
227 | 296 } |
226 | 297 } |
298 |