565baee6 |
1 | // Copyright (c) 2019 OPEN CASCADE SAS |
2 | // |
3 | // This file is part of the examples of the Open CASCADE Technology software library. |
4 | // |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | // of this software and associated documentation files (the "Software"), to deal |
7 | // in the Software without restriction, including without limitation the rights |
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | // copies of the Software, and to permit persons to whom the Software is |
10 | // furnished to do so, subject to the following conditions: |
11 | // |
12 | // The above copyright notice and this permission notice shall be included in all |
13 | // copies or substantial portions of the Software. |
14 | // |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE |
21 | |
22 | #include "WasmOcctView.h" |
23 | |
24 | #include "WasmVKeys.h" |
25 | |
26 | #include <AIS_Shape.hxx> |
27 | #include <AIS_ViewCube.hxx> |
28 | #include <Aspect_Handle.hxx> |
29 | #include <Aspect_DisplayConnection.hxx> |
30 | #include <Aspect_NeutralWindow.hxx> |
31 | #include <Message.hxx> |
32 | #include <Message_Messenger.hxx> |
33 | #include <OpenGl_GraphicDriver.hxx> |
34 | #include <Prs3d_DatumAspect.hxx> |
35 | |
36 | #include <iostream> |
37 | |
38 | #define THE_CANVAS_ID "canvas" |
39 | |
40 | namespace |
41 | { |
42 | EM_JS(int, jsCanvasGetWidth, (), { |
43 | return canvas.width; |
44 | }); |
45 | |
46 | EM_JS(int, jsCanvasGetHeight, (), { |
47 | return canvas.height; |
48 | }); |
49 | |
50 | EM_JS(float, jsDevicePixelRatio, (), { |
51 | var aDevicePixelRatio = window.devicePixelRatio || 1; |
52 | return aDevicePixelRatio; |
53 | }); |
54 | |
55 | //! Return cavas size in pixels. |
56 | static Graphic3d_Vec2i jsCanvasSize() |
57 | { |
58 | return Graphic3d_Vec2i (jsCanvasGetWidth(), jsCanvasGetHeight()); |
59 | } |
60 | } |
61 | |
62 | // ================================================================ |
63 | // Function : WasmOcctView |
64 | // Purpose : |
65 | // ================================================================ |
66 | WasmOcctView::WasmOcctView() |
67 | : myDevicePixelRatio (1.0f), |
68 | myUpdateRequests (0) |
69 | { |
70 | } |
71 | |
72 | // ================================================================ |
73 | // Function : ~WasmOcctView |
74 | // Purpose : |
75 | // ================================================================ |
76 | WasmOcctView::~WasmOcctView() |
77 | { |
78 | } |
79 | |
80 | // ================================================================ |
81 | // Function : run |
82 | // Purpose : |
83 | // ================================================================ |
84 | void WasmOcctView::run() |
85 | { |
86 | initWindow(); |
87 | initViewer(); |
88 | initDemoScene(); |
89 | if (myView.IsNull()) |
90 | { |
91 | return; |
92 | } |
93 | |
94 | myView->MustBeResized(); |
95 | myView->Redraw(); |
96 | |
a110c4a3 |
97 | // There is no infinite message loop, main() will return from here immediately. |
565baee6 |
98 | // Tell that our Module should be left loaded and handle events through callbacks. |
99 | //emscripten_set_main_loop (redrawView, 60, 1); |
100 | //emscripten_set_main_loop (redrawView, -1, 1); |
101 | EM_ASM(Module['noExitRuntime'] = true); |
102 | } |
103 | |
104 | // ================================================================ |
105 | // Function : initWindow |
106 | // Purpose : |
107 | // ================================================================ |
108 | void WasmOcctView::initWindow() |
109 | { |
110 | myDevicePixelRatio = jsDevicePixelRatio(); |
111 | myCanvasId = THE_CANVAS_ID; |
7465bfa6 |
112 | const char* aTargetId = !myCanvasId.IsEmpty() ? myCanvasId.ToCString() : EMSCRIPTEN_EVENT_TARGET_WINDOW; |
565baee6 |
113 | const EM_BOOL toUseCapture = EM_TRUE; |
7465bfa6 |
114 | emscripten_set_resize_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onResizeCallback); |
565baee6 |
115 | |
7465bfa6 |
116 | emscripten_set_mousedown_callback (aTargetId, this, toUseCapture, onMouseCallback); |
117 | emscripten_set_mouseup_callback (aTargetId, this, toUseCapture, onMouseCallback); |
118 | emscripten_set_mousemove_callback (aTargetId, this, toUseCapture, onMouseCallback); |
565baee6 |
119 | emscripten_set_dblclick_callback (aTargetId, this, toUseCapture, onMouseCallback); |
120 | emscripten_set_click_callback (aTargetId, this, toUseCapture, onMouseCallback); |
121 | emscripten_set_mouseenter_callback (aTargetId, this, toUseCapture, onMouseCallback); |
122 | emscripten_set_mouseleave_callback (aTargetId, this, toUseCapture, onMouseCallback); |
123 | emscripten_set_wheel_callback (aTargetId, this, toUseCapture, onWheelCallback); |
124 | |
125 | emscripten_set_touchstart_callback (aTargetId, this, toUseCapture, onTouchCallback); |
126 | emscripten_set_touchend_callback (aTargetId, this, toUseCapture, onTouchCallback); |
127 | emscripten_set_touchmove_callback (aTargetId, this, toUseCapture, onTouchCallback); |
128 | emscripten_set_touchcancel_callback(aTargetId, this, toUseCapture, onTouchCallback); |
129 | |
7465bfa6 |
130 | //emscripten_set_keypress_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onKeyCallback); |
131 | emscripten_set_keydown_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onKeyDownCallback); |
132 | emscripten_set_keyup_callback (EMSCRIPTEN_EVENT_TARGET_WINDOW, this, toUseCapture, onKeyUpCallback); |
565baee6 |
133 | } |
134 | |
135 | // ================================================================ |
136 | // Function : dumpGlInfo |
137 | // Purpose : |
138 | // ================================================================ |
139 | void WasmOcctView::dumpGlInfo (bool theIsBasic) |
140 | { |
141 | TColStd_IndexedDataMapOfStringString aGlCapsDict; |
142 | myView->DiagnosticInformation (aGlCapsDict, theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete); |
143 | if (theIsBasic) |
144 | { |
145 | TCollection_AsciiString aViewport; |
146 | aGlCapsDict.FindFromKey ("Viewport", aViewport); |
147 | aGlCapsDict.Clear(); |
148 | aGlCapsDict.Add ("Viewport", aViewport); |
149 | } |
150 | aGlCapsDict.Add ("Display scale", TCollection_AsciiString(myDevicePixelRatio)); |
151 | |
152 | // beautify output |
153 | { |
154 | TCollection_AsciiString* aGlVer = aGlCapsDict.ChangeSeek ("GLversion"); |
155 | TCollection_AsciiString* aGlslVer = aGlCapsDict.ChangeSeek ("GLSLversion"); |
156 | if (aGlVer != NULL |
157 | && aGlslVer != NULL) |
158 | { |
159 | *aGlVer = *aGlVer + " [GLSL: " + *aGlslVer + "]"; |
160 | aGlslVer->Clear(); |
161 | } |
162 | } |
163 | |
164 | TCollection_AsciiString anInfo; |
165 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter (aGlCapsDict); aValueIter.More(); aValueIter.Next()) |
166 | { |
167 | if (!aValueIter.Value().IsEmpty()) |
168 | { |
169 | if (!anInfo.IsEmpty()) |
170 | { |
171 | anInfo += "\n"; |
172 | } |
173 | anInfo += aValueIter.Key() + ": " + aValueIter.Value(); |
174 | } |
175 | } |
176 | |
177 | ::Message::DefaultMessenger()->Send (anInfo, Message_Warning); |
178 | } |
179 | |
180 | // ================================================================ |
181 | // Function : initPixelScaleRatio |
182 | // Purpose : |
183 | // ================================================================ |
184 | void WasmOcctView::initPixelScaleRatio() |
185 | { |
186 | SetTouchToleranceScale (myDevicePixelRatio); |
187 | if (!myView.IsNull()) |
188 | { |
189 | myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5); |
190 | } |
191 | if (!myContext.IsNull()) |
192 | { |
193 | myContext->SetPixelTolerance (int(myDevicePixelRatio * 6.0)); |
194 | if (!myViewCube.IsNull()) |
195 | { |
196 | static const double THE_CUBE_SIZE = 60.0; |
197 | myViewCube->SetSize (myDevicePixelRatio * THE_CUBE_SIZE, false); |
198 | myViewCube->SetBoxFacetExtension (myViewCube->Size() * 0.15); |
199 | myViewCube->SetAxesPadding (myViewCube->Size() * 0.10); |
200 | myViewCube->SetFontHeight (THE_CUBE_SIZE * 0.16); |
201 | if (myViewCube->HasInteractiveContext()) |
202 | { |
203 | myContext->Redisplay (myViewCube, false); |
204 | } |
205 | } |
206 | } |
207 | } |
208 | |
209 | // ================================================================ |
210 | // Function : initViewer |
211 | // Purpose : |
212 | // ================================================================ |
213 | bool WasmOcctView::initViewer() |
214 | { |
215 | // Build with "--preload-file MyFontFile.ttf" option |
216 | // and register font in Font Manager to use custom font(s). |
217 | /*const char* aFontPath = "MyFontFile.ttf"; |
218 | if (Handle(Font_SystemFont) aFont = Font_FontMgr::GetInstance()->CheckFont (aFontPath)) |
219 | { |
220 | Font_FontMgr::GetInstance()->RegisterFont (aFont, true); |
221 | } |
222 | else |
223 | { |
224 | Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: font '") + aFontPath + "' is not found", Message_Fail); |
225 | }*/ |
226 | |
227 | Handle(Aspect_DisplayConnection) aDisp; |
228 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (aDisp, false); |
229 | aDriver->ChangeOptions().buffersNoSwap = true; // swap has no effect in WebGL |
230 | if (!aDriver->InitContext()) |
231 | { |
232 | Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: EGL initialization failed"), Message_Fail); |
233 | return false; |
234 | } |
235 | |
236 | Handle(V3d_Viewer) aViewer = new V3d_Viewer (aDriver); |
237 | aViewer->SetComputedMode (false); |
238 | aViewer->SetDefaultShadingModel (Graphic3d_TOSM_FRAGMENT); |
239 | aViewer->SetDefaultLights(); |
240 | aViewer->SetLightOn(); |
241 | |
242 | Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow(); |
243 | Graphic3d_Vec2i aWinSize = jsCanvasSize(); |
244 | if (aWinSize.x() < 10 || aWinSize.y() < 10) |
245 | { |
246 | Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning); |
247 | } |
248 | aWindow->SetSize (aWinSize.x(), aWinSize.y()); |
249 | |
250 | myTextStyle = new Prs3d_TextAspect(); |
251 | myTextStyle->SetFont (Font_NOF_ASCII_MONO); |
252 | myTextStyle->SetHeight (12); |
253 | myTextStyle->Aspect()->SetColor (Quantity_NOC_GRAY95); |
254 | myTextStyle->Aspect()->SetColorSubTitle (Quantity_NOC_BLACK); |
255 | myTextStyle->Aspect()->SetDisplayType (Aspect_TODT_SHADOW); |
256 | myTextStyle->Aspect()->SetTextFontAspect (Font_FA_Bold); |
257 | myTextStyle->Aspect()->SetTextZoomable (false); |
258 | myTextStyle->SetHorizontalJustification (Graphic3d_HTA_LEFT); |
259 | myTextStyle->SetVerticalJustification (Graphic3d_VTA_BOTTOM); |
260 | |
261 | myView = new V3d_View (aViewer); |
262 | myView->SetImmediateUpdate (false); |
263 | myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5); |
264 | myView->ChangeRenderingParams().ToShowStats = true; |
265 | myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect(); |
266 | myView->ChangeRenderingParams().StatsTextHeight = (int )myTextStyle->Height(); |
267 | myView->SetWindow (aWindow); |
268 | dumpGlInfo (false); |
269 | |
270 | myContext = new AIS_InteractiveContext (aViewer); |
271 | initPixelScaleRatio(); |
272 | return true; |
273 | } |
274 | |
275 | // ================================================================ |
276 | // Function : initDemoScene |
277 | // Purpose : |
278 | // ================================================================ |
279 | void WasmOcctView::initDemoScene() |
280 | { |
281 | if (myContext.IsNull()) |
282 | { |
283 | return; |
284 | } |
285 | |
286 | //myView->TriedronDisplay (Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_WIREFRAME); |
287 | |
288 | myViewCube = new AIS_ViewCube(); |
289 | // presentation parameters |
290 | initPixelScaleRatio(); |
291 | myViewCube->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_RIGHT_LOWER, Graphic3d_Vec2i (100, 100))); |
292 | myViewCube->Attributes()->SetDatumAspect (new Prs3d_DatumAspect()); |
293 | myViewCube->Attributes()->DatumAspect()->SetTextAspect (myTextStyle); |
294 | // animation parameters |
295 | myViewCube->SetViewAnimation (myViewAnimation); |
296 | myViewCube->SetFixedAnimationLoop (false); |
297 | myViewCube->SetAutoStartAnimation (true); |
298 | myContext->Display (myViewCube, false); |
299 | |
300 | // Build with "--preload-file MySampleFile.brep" option to load some shapes here. |
301 | } |
302 | |
303 | |
304 | // ================================================================ |
305 | // Function : updateView |
306 | // Purpose : |
307 | // ================================================================ |
308 | void WasmOcctView::updateView() |
309 | { |
310 | if (!myView.IsNull()) |
311 | { |
312 | if (++myUpdateRequests == 1) |
313 | { |
314 | emscripten_async_call (onRedrawView, this, 0); |
315 | } |
316 | } |
317 | } |
318 | |
319 | // ================================================================ |
320 | // Function : redrawView |
321 | // Purpose : |
322 | // ================================================================ |
323 | void WasmOcctView::redrawView() |
324 | { |
325 | if (!myView.IsNull()) |
326 | { |
327 | FlushViewEvents (myContext, myView, true); |
328 | } |
329 | } |
330 | |
331 | // ================================================================ |
332 | // Function : handleViewRedraw |
333 | // Purpose : |
334 | // ================================================================ |
335 | void WasmOcctView::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx, |
336 | const Handle(V3d_View)& theView) |
337 | { |
338 | myUpdateRequests = 0; |
339 | AIS_ViewController::handleViewRedraw (theCtx, theView); |
340 | if (myToAskNextFrame) |
341 | { |
342 | // ask more frames |
343 | ++myUpdateRequests; |
344 | emscripten_async_call (onRedrawView, this, 0); |
345 | } |
346 | } |
347 | |
348 | // ================================================================ |
349 | // Function : onResizeEvent |
350 | // Purpose : |
351 | // ================================================================ |
352 | EM_BOOL WasmOcctView::onResizeEvent (int theEventType, const EmscriptenUiEvent* theEvent) |
353 | { |
354 | (void )theEventType; // EMSCRIPTEN_EVENT_RESIZE or EMSCRIPTEN_EVENT_CANVASRESIZED |
355 | (void )theEvent; |
356 | if (myView.IsNull()) |
357 | { |
358 | return EM_FALSE; |
359 | } |
360 | |
361 | Handle(Aspect_NeutralWindow) aWindow = Handle(Aspect_NeutralWindow)::DownCast (myView->Window()); |
362 | Graphic3d_Vec2i aWinSizeOld, aWinSizeNew (jsCanvasSize()); |
363 | if (aWinSizeNew.x() < 10 || aWinSizeNew.y() < 10) |
364 | { |
365 | Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning); |
366 | } |
367 | aWindow->Size (aWinSizeOld.x(), aWinSizeOld.y()); |
368 | const float aPixelRatio = jsDevicePixelRatio(); |
369 | if (aWinSizeNew != aWinSizeOld |
370 | || aPixelRatio != myDevicePixelRatio) |
371 | { |
372 | if (myDevicePixelRatio != aPixelRatio) |
373 | { |
374 | myDevicePixelRatio = aPixelRatio; |
375 | initPixelScaleRatio(); |
376 | } |
377 | aWindow->SetSize (aWinSizeNew.x(), aWinSizeNew.y()); |
378 | myView->MustBeResized(); |
379 | myView->Invalidate(); |
380 | myView->Redraw(); |
381 | dumpGlInfo (true); |
382 | } |
383 | return EM_TRUE; |
384 | } |
385 | |
386 | // ================================================================ |
387 | // Function : onMouseEvent |
388 | // Purpose : |
389 | // ================================================================ |
390 | EM_BOOL WasmOcctView::onMouseEvent (int theEventType, const EmscriptenMouseEvent* theEvent) |
391 | { |
392 | if (myView.IsNull()) |
393 | { |
394 | return EM_FALSE; |
395 | } |
396 | |
397 | Graphic3d_Vec2i aWinSize; |
398 | myView->Window()->Size (aWinSize.x(), aWinSize.y()); |
7465bfa6 |
399 | const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->targetX, theEvent->targetY)); |
565baee6 |
400 | Aspect_VKeyFlags aFlags = 0; |
401 | if (theEvent->ctrlKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_CTRL; } |
402 | if (theEvent->shiftKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_SHIFT; } |
403 | if (theEvent->altKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_ALT; } |
404 | if (theEvent->metaKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_META; } |
405 | |
406 | const bool isEmulated = false; |
407 | const Aspect_VKeyMouse aButtons = WasmVKeys_MouseButtonsFromNative (theEvent->buttons); |
408 | switch (theEventType) |
409 | { |
410 | case EMSCRIPTEN_EVENT_MOUSEMOVE: |
411 | { |
412 | if ((aNewPos.x() < 0 || aNewPos.x() > aWinSize.x() |
413 | || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y()) |
414 | && PressedMouseButtons() == Aspect_VKeyMouse_NONE) |
415 | { |
416 | return EM_FALSE; |
417 | } |
418 | if (UpdateMousePosition (aNewPos, aButtons, aFlags, isEmulated)) |
419 | { |
420 | updateView(); |
421 | } |
422 | break; |
423 | } |
424 | case EMSCRIPTEN_EVENT_MOUSEDOWN: |
425 | case EMSCRIPTEN_EVENT_MOUSEUP: |
426 | { |
427 | if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x() |
428 | || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y()) |
429 | { |
430 | return EM_FALSE; |
431 | } |
432 | if (UpdateMouseButtons (aNewPos, aButtons, aFlags, isEmulated)) |
433 | { |
434 | updateView(); |
435 | } |
436 | break; |
437 | } |
438 | case EMSCRIPTEN_EVENT_CLICK: |
439 | case EMSCRIPTEN_EVENT_DBLCLICK: |
440 | { |
441 | if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x() |
442 | || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y()) |
443 | { |
444 | return EM_FALSE; |
445 | } |
446 | break; |
447 | } |
448 | case EMSCRIPTEN_EVENT_MOUSEENTER: |
449 | { |
450 | break; |
451 | } |
452 | case EMSCRIPTEN_EVENT_MOUSELEAVE: |
453 | { |
454 | // there is no SetCapture() support, so that mouse unclick events outside canvas will not arrive, |
455 | // so we have to forget current state... |
456 | if (UpdateMouseButtons (aNewPos, Aspect_VKeyMouse_NONE, aFlags, isEmulated)) |
457 | { |
458 | updateView(); |
459 | } |
460 | break; |
461 | } |
462 | } |
463 | return EM_TRUE; |
464 | } |
465 | |
466 | // ================================================================ |
467 | // Function : onWheelEvent |
468 | // Purpose : |
469 | // ================================================================ |
470 | EM_BOOL WasmOcctView::onWheelEvent (int theEventType, const EmscriptenWheelEvent* theEvent) |
471 | { |
472 | if (myView.IsNull() |
473 | || theEventType != EMSCRIPTEN_EVENT_WHEEL) |
474 | { |
475 | return EM_FALSE; |
476 | } |
477 | |
478 | Graphic3d_Vec2i aWinSize; |
479 | myView->Window()->Size (aWinSize.x(), aWinSize.y()); |
7465bfa6 |
480 | const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->mouse.targetX, theEvent->mouse.targetY)); |
565baee6 |
481 | if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x() |
482 | || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y()) |
483 | { |
484 | return EM_FALSE; |
485 | } |
486 | |
487 | double aDelta = 0.0; |
488 | switch (theEvent->deltaMode) |
489 | { |
490 | case DOM_DELTA_PIXEL: |
491 | { |
492 | aDelta = theEvent->deltaY / (5.0 * myDevicePixelRatio); |
493 | break; |
494 | } |
495 | case DOM_DELTA_LINE: |
496 | { |
497 | aDelta = theEvent->deltaY * 8.0; |
498 | break; |
499 | } |
500 | case DOM_DELTA_PAGE: |
501 | { |
502 | aDelta = theEvent->deltaY >= 0.0 ? 24.0 : -24.0; |
503 | break; |
504 | } |
505 | } |
506 | |
507 | if (UpdateZoom (Aspect_ScrollDelta (aNewPos, -aDelta))) |
508 | { |
509 | updateView(); |
510 | } |
511 | return EM_TRUE; |
512 | } |
513 | |
514 | // ================================================================ |
515 | // Function : onTouchEvent |
516 | // Purpose : |
517 | // ================================================================ |
518 | EM_BOOL WasmOcctView::onTouchEvent (int theEventType, const EmscriptenTouchEvent* theEvent) |
519 | { |
520 | const double aClickTolerance = 5.0; |
521 | if (myView.IsNull()) |
522 | { |
523 | return EM_FALSE; |
524 | } |
525 | |
526 | Graphic3d_Vec2i aWinSize; |
527 | myView->Window()->Size (aWinSize.x(), aWinSize.y()); |
528 | bool hasUpdates = false; |
529 | for (int aTouchIter = 0; aTouchIter < theEvent->numTouches; ++aTouchIter) |
530 | { |
531 | const EmscriptenTouchPoint& aTouch = theEvent->touches[aTouchIter]; |
532 | if (!aTouch.isChanged) |
533 | { |
534 | continue; |
535 | } |
536 | |
537 | const Standard_Size aTouchId = (Standard_Size )aTouch.identifier; |
538 | const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (aTouch.canvasX, aTouch.canvasY)); |
539 | switch (theEventType) |
540 | { |
541 | case EMSCRIPTEN_EVENT_TOUCHSTART: |
542 | { |
543 | if (aNewPos.x() >= 0 && aNewPos.x() < aWinSize.x() |
544 | && aNewPos.y() >= 0 && aNewPos.y() < aWinSize.y()) |
545 | { |
546 | hasUpdates = true; |
547 | AddTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos)); |
548 | myClickTouch.From.SetValues (-1.0, -1.0); |
549 | if (myTouchPoints.Extent() == 1) |
550 | { |
551 | myClickTouch.From = Graphic3d_Vec2d (aNewPos); |
552 | } |
553 | } |
554 | break; |
555 | } |
556 | case EMSCRIPTEN_EVENT_TOUCHMOVE: |
557 | { |
558 | const int anOldIndex = myTouchPoints.FindIndex (aTouchId); |
559 | if (anOldIndex != 0) |
560 | { |
561 | hasUpdates = true; |
562 | UpdateTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos)); |
563 | if (myTouchPoints.Extent() == 1 |
564 | && (myClickTouch.From - Graphic3d_Vec2d (aNewPos)).cwiseAbs().maxComp() > aClickTolerance) |
565 | { |
566 | myClickTouch.From.SetValues (-1.0, -1.0); |
567 | } |
568 | } |
569 | break; |
570 | } |
571 | case EMSCRIPTEN_EVENT_TOUCHEND: |
572 | case EMSCRIPTEN_EVENT_TOUCHCANCEL: |
573 | { |
574 | if (RemoveTouchPoint (aTouchId)) |
575 | { |
576 | if (myTouchPoints.IsEmpty() |
577 | && myClickTouch.From.minComp() >= 0.0) |
578 | { |
579 | if (myDoubleTapTimer.IsStarted() |
580 | && myDoubleTapTimer.ElapsedTime() <= myMouseDoubleClickInt) |
581 | { |
582 | myView->FitAll (0.01, false); |
583 | myView->Invalidate(); |
584 | } |
585 | else |
586 | { |
587 | myDoubleTapTimer.Stop(); |
588 | myDoubleTapTimer.Reset(); |
589 | myDoubleTapTimer.Start(); |
590 | SelectInViewer (Graphic3d_Vec2i (myClickTouch.From), false); |
591 | } |
592 | } |
593 | hasUpdates = true; |
594 | } |
595 | break; |
596 | } |
597 | } |
598 | } |
599 | if (hasUpdates) |
600 | { |
601 | updateView(); |
602 | } |
603 | return hasUpdates || !myTouchPoints.IsEmpty() ? EM_TRUE : EM_FALSE; |
604 | } |
605 | |
606 | // ================================================================ |
607 | // Function : onKeyDownEvent |
608 | // Purpose : |
609 | // ================================================================ |
610 | EM_BOOL WasmOcctView::onKeyDownEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent) |
611 | { |
612 | if (myView.IsNull() |
613 | || theEventType != EMSCRIPTEN_EVENT_KEYDOWN) // EMSCRIPTEN_EVENT_KEYPRESS |
614 | { |
615 | return EM_FALSE; |
616 | } |
617 | |
618 | const double aTimeStamp = EventTime(); |
619 | const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode); |
620 | if (aVKey == Aspect_VKey_UNKNOWN) |
621 | { |
622 | return EM_FALSE; |
623 | } |
624 | |
625 | if (theEvent->repeat == EM_FALSE) |
626 | { |
627 | myKeys.KeyDown (aVKey, aTimeStamp); |
628 | } |
629 | |
630 | if (Aspect_VKey2Modifier (aVKey) == 0) |
631 | { |
632 | // normal key |
633 | } |
634 | return EM_FALSE; |
635 | } |
636 | |
637 | // ================================================================ |
638 | // Function : onKeyUpEvent |
639 | // Purpose : |
640 | // ================================================================ |
641 | EM_BOOL WasmOcctView::onKeyUpEvent (int theEventType, const EmscriptenKeyboardEvent* theEvent) |
642 | { |
643 | if (myView.IsNull() |
644 | || theEventType != EMSCRIPTEN_EVENT_KEYUP) |
645 | { |
646 | return EM_FALSE; |
647 | } |
648 | |
649 | const double aTimeStamp = EventTime(); |
650 | const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode); |
651 | if (aVKey == Aspect_VKey_UNKNOWN) |
652 | { |
653 | return EM_FALSE; |
654 | } |
655 | |
656 | if (theEvent->repeat == EM_TRUE) |
657 | { |
658 | return EM_FALSE; |
659 | } |
660 | |
661 | const unsigned int aModif = myKeys.Modifiers(); |
662 | myKeys.KeyUp (aVKey, aTimeStamp); |
663 | if (Aspect_VKey2Modifier (aVKey) == 0) |
664 | { |
665 | // normal key released |
666 | switch (aVKey | aModif) |
667 | { |
668 | case Aspect_VKey_F: |
669 | { |
670 | myView->FitAll (0.01, false); |
671 | myView->Invalidate(); |
672 | updateView(); |
673 | return EM_TRUE; |
674 | } |
675 | } |
676 | } |
677 | return EM_FALSE; |
678 | } |