Mercurial > hg > orthanc-stone
comparison Platforms/Wasm/wasm-viewport.ts @ 236:f73d722d98c8 am
renamed folder
author | am@osimis.io |
---|---|
date | Tue, 19 Jun 2018 16:06:32 +0200 |
parents | Platforms/WebAssembly/wasm-viewport.ts@9afb50d1ac14 |
children | 313903066093 |
comparison
equal
deleted
inserted
replaced
235:ce4405d98b92 | 236:f73d722d98c8 |
---|---|
1 var isPendingRedraw = false; | |
2 | |
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 }); | |
12 } | |
13 } | |
14 | |
15 declare function UTF8ToString(any): string; | |
16 | |
17 function CreateWasmViewport(htmlCanvasId: string) : any { | |
18 var cppViewportHandle = CreateCppViewport(); | |
19 var canvasId = UTF8ToString(htmlCanvasId); | |
20 var webViewport = new Stone.WasmViewport(StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted | |
21 webViewport.Initialize(); | |
22 | |
23 return cppViewportHandle; | |
24 } | |
25 | |
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"; | |
34 | |
35 export class WasmViewport { | |
36 | |
37 private static cppWebViewportsMaps_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); | |
38 | |
39 private module_ : any; | |
40 private canvasId_ : string; | |
41 private htmlCanvas_ : HTMLCanvasElement; | |
42 private context_ : CanvasRenderingContext2D; | |
43 private imageData_ : any = null; | |
44 private renderingBuffer_ : any = null; | |
45 private touchZoom_ : any = false; | |
46 private touchTranslation_ : any = false; | |
47 | |
48 private ViewportSetSize : Function; | |
49 private ViewportRender : Function; | |
50 private ViewportMouseDown : Function; | |
51 private ViewportMouseMove : Function; | |
52 private ViewportMouseUp : Function; | |
53 private ViewportMouseEnter : Function; | |
54 private ViewportMouseLeave : Function; | |
55 private ViewportMouseWheel : Function; | |
56 private ViewportKeyPressed : Function; | |
57 | |
58 private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object | |
59 | |
60 public constructor(module: any, canvasId: string, cppViewport: any) { | |
61 | |
62 this.pimpl_ = cppViewport; | |
63 WasmViewport.cppWebViewportsMaps_[this.pimpl_] = this; | |
64 | |
65 this.module_ = module; | |
66 this.canvasId_ = canvasId; | |
67 this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; | |
68 this.context_ = this.htmlCanvas_.getContext('2d'); | |
69 | |
70 this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); | |
71 this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); | |
72 this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); | |
73 this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); | |
74 this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); | |
75 this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); | |
76 this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); | |
77 this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); | |
78 this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'string', 'number', 'number' ]); | |
79 } | |
80 | |
81 public GetCppViewport() : number { | |
82 return this.pimpl_; | |
83 } | |
84 | |
85 public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport { | |
86 if (WasmViewport.cppWebViewportsMaps_[cppViewportHandle] !== undefined) { | |
87 return WasmViewport.cppWebViewportsMaps_[cppViewportHandle]; | |
88 } | |
89 console.log("WasmViewport not found !"); | |
90 return undefined; | |
91 } | |
92 | |
93 public Redraw() { | |
94 if (this.imageData_ === null || | |
95 this.renderingBuffer_ === null || | |
96 this.ViewportRender(this.pimpl_, | |
97 this.imageData_.width, | |
98 this.imageData_.height, | |
99 this.renderingBuffer_) == 0) { | |
100 console.log('The rendering has failed'); | |
101 } else { | |
102 // Create an accessor to the rendering buffer (i.e. create a | |
103 // "window" above the heap of the WASM module), then copy it to | |
104 // the ImageData object | |
105 this.imageData_.data.set(new Uint8ClampedArray( | |
106 this.module_.buffer, | |
107 this.renderingBuffer_, | |
108 this.imageData_.width * this.imageData_.height * 4)); | |
109 | |
110 this.context_.putImageData(this.imageData_, 0, 0); | |
111 } | |
112 } | |
113 | |
114 public Resize() { | |
115 if (this.imageData_ != null && | |
116 (this.imageData_.width != window.innerWidth || | |
117 this.imageData_.height != window.innerHeight)) { | |
118 this.imageData_ = null; | |
119 } | |
120 | |
121 // width/height can be defined in percent of window width/height through html attributes like data-width-ratio="50" and data-height-ratio="20" | |
122 var widthRatio = Number(this.htmlCanvas_.dataset["widthRatio"]) || 100; | |
123 var heightRatio = Number(this.htmlCanvas_.dataset["heightRatio"]) || 100; | |
124 | |
125 this.htmlCanvas_.width = window.innerWidth * (widthRatio / 100); | |
126 this.htmlCanvas_.height = window.innerHeight * (heightRatio / 100); | |
127 | |
128 console.log("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); | |
129 | |
130 if (this.imageData_ === null) { | |
131 this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); | |
132 this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); | |
133 | |
134 if (this.renderingBuffer_ != null) { | |
135 this.module_._free(this.renderingBuffer_); | |
136 } | |
137 | |
138 this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); | |
139 } | |
140 | |
141 this.Redraw(); | |
142 } | |
143 | |
144 public Initialize() { | |
145 | |
146 // Force the rendering of the viewport for the first time | |
147 this.Resize(); | |
148 | |
149 var that : WasmViewport = this; | |
150 // Register an event listener to call the Resize() function | |
151 // each time the window is resized. | |
152 window.addEventListener('resize', function(event) { | |
153 that.Resize(); | |
154 }, false); | |
155 | |
156 this.htmlCanvas_.addEventListener('contextmenu', function(event) { | |
157 // Prevent right click on the canvas | |
158 event.preventDefault(); | |
159 }, false); | |
160 | |
161 this.htmlCanvas_.addEventListener('mouseleave', function(event) { | |
162 that.ViewportMouseLeave(that.pimpl_); | |
163 }); | |
164 | |
165 this.htmlCanvas_.addEventListener('mouseenter', function(event) { | |
166 that.ViewportMouseEnter(that.pimpl_); | |
167 }); | |
168 | |
169 this.htmlCanvas_.addEventListener('mousedown', function(event) { | |
170 var x = event.pageX - this.offsetLeft; | |
171 var y = event.pageY - this.offsetTop; | |
172 that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO */); | |
173 }); | |
174 | |
175 this.htmlCanvas_.addEventListener('mousemove', function(event) { | |
176 var x = event.pageX - this.offsetLeft; | |
177 var y = event.pageY - this.offsetTop; | |
178 that.ViewportMouseMove(that.pimpl_, x, y); | |
179 }); | |
180 | |
181 this.htmlCanvas_.addEventListener('mouseup', function(event) { | |
182 that.ViewportMouseUp(that.pimpl_); | |
183 }); | |
184 | |
185 window.addEventListener('keydown', function(event) { | |
186 that.ViewportKeyPressed(that.pimpl_, event.key, event.shiftKey, event.ctrlKey, event.altKey); | |
187 }); | |
188 | |
189 this.htmlCanvas_.addEventListener('wheel', function(event) { | |
190 var x = event.pageX - this.offsetLeft; | |
191 var y = event.pageY - this.offsetTop; | |
192 that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); | |
193 event.preventDefault(); | |
194 }); | |
195 | |
196 this.htmlCanvas_.addEventListener('touchstart', function(event) { | |
197 that.ResetTouch(); | |
198 }); | |
199 | |
200 this.htmlCanvas_.addEventListener('touchend', function(event) { | |
201 that.ResetTouch(); | |
202 }); | |
203 | |
204 this.htmlCanvas_.addEventListener('touchmove', function(event) { | |
205 if (that.touchTranslation_.length == 2) { | |
206 var t = that.GetTouchTranslation(event); | |
207 that.ViewportMouseMove(that.pimpl_, t[0], t[1]); | |
208 } | |
209 else if (that.touchZoom_.length == 3) { | |
210 var z0 = that.touchZoom_; | |
211 var z1 = that.GetTouchZoom(event); | |
212 that.ViewportMouseMove(that.pimpl_, z0[0], z0[1] - z0[2] + z1[2]); | |
213 } | |
214 else { | |
215 // Realize the gesture event | |
216 if (event.targetTouches.length == 1) { | |
217 // Exactly one finger inside the canvas => Setup a translation | |
218 that.touchTranslation_ = that.GetTouchTranslation(event); | |
219 that.ViewportMouseDown(that.pimpl_, | |
220 1 /* middle button */, | |
221 that.touchTranslation_[0], | |
222 that.touchTranslation_[1], 0); | |
223 } else if (event.targetTouches.length == 2) { | |
224 // Exactly 2 fingers inside the canvas => Setup a pinch/zoom | |
225 that.touchZoom_ = that.GetTouchZoom(event); | |
226 var z0 = that.touchZoom_; | |
227 that.ViewportMouseDown(that.pimpl_, | |
228 2 /* right button */, | |
229 z0[0], | |
230 z0[1], 0); | |
231 } | |
232 } | |
233 }); | |
234 } | |
235 | |
236 public ResetTouch() { | |
237 if (this.touchTranslation_ || | |
238 this.touchZoom_) { | |
239 this.ViewportMouseUp(this.pimpl_); | |
240 } | |
241 | |
242 this.touchTranslation_ = false; | |
243 this.touchZoom_ = false; | |
244 } | |
245 | |
246 public GetTouchTranslation(event) { | |
247 var touch = event.targetTouches[0]; | |
248 return [ | |
249 touch.pageX, | |
250 touch.pageY | |
251 ]; | |
252 } | |
253 | |
254 public GetTouchZoom(event) { | |
255 var touch1 = event.targetTouches[0]; | |
256 var touch2 = event.targetTouches[1]; | |
257 var dx = (touch1.pageX - touch2.pageX); | |
258 var dy = (touch1.pageY - touch2.pageY); | |
259 var d = Math.sqrt(dx * dx + dy * dy); | |
260 return [ | |
261 (touch1.pageX + touch2.pageX) / 2.0, | |
262 (touch1.pageY + touch2.pageY) / 2.0, | |
263 d | |
264 ]; | |
265 } | |
266 | |
267 } | |
268 } | |
269 |