test_scene.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. from typing import List
  2. import numpy as np
  3. from selenium.common.exceptions import JavascriptException
  4. from nicegui import ui
  5. from nicegui.elements.scene_object3d import Object3D
  6. from nicegui.testing import Screen
  7. def test_moving_sphere_with_timer(screen: Screen):
  8. with ui.scene() as scene:
  9. sphere = scene.sphere().with_name('sphere')
  10. ui.timer(0.1, lambda: sphere.move(0, 0, sphere.z + 0.01))
  11. screen.open('/')
  12. def position() -> float:
  13. for _ in range(3):
  14. try:
  15. pos = screen.selenium.execute_script(
  16. f'return scene_{scene.html_id}.getObjectByName("sphere").position.z')
  17. if pos is not None:
  18. return pos
  19. except JavascriptException as e:
  20. print(e.msg, flush=True)
  21. screen.wait(1.0)
  22. raise RuntimeError('Could not get position')
  23. screen.wait(0.2)
  24. assert position() > 0
  25. def test_no_object_duplication_on_index_client(screen: Screen):
  26. with ui.scene() as scene:
  27. sphere = scene.sphere().move(0, -4, 0)
  28. ui.timer(0.1, lambda: sphere.move(0, sphere.y + 0.5, 0))
  29. screen.open('/')
  30. screen.wait(0.4)
  31. screen.switch_to(1)
  32. screen.open('/')
  33. screen.switch_to(0)
  34. screen.wait(0.2)
  35. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children.length') == 5
  36. def test_no_object_duplication_with_page_builder(screen: Screen):
  37. scene_html_ids: List[int] = []
  38. @ui.page('/')
  39. def page():
  40. with ui.scene() as scene:
  41. sphere = scene.sphere().move(0, -4, 0)
  42. ui.timer(0.1, lambda: sphere.move(0, sphere.y + 0.5, 0))
  43. scene_html_ids.append(scene.html_id)
  44. screen.open('/')
  45. screen.wait(0.4)
  46. screen.switch_to(1)
  47. screen.open('/')
  48. screen.switch_to(0)
  49. screen.wait(0.2)
  50. assert screen.selenium.execute_script(f'return scene_{scene_html_ids[0]}.children.length') == 5
  51. screen.switch_to(1)
  52. screen.wait(0.2)
  53. assert screen.selenium.execute_script(f'return scene_{scene_html_ids[1]}.children.length') == 5
  54. def test_deleting_group(screen: Screen):
  55. with ui.scene() as scene:
  56. with scene.group() as group:
  57. scene.sphere()
  58. ui.button('Delete group', on_click=group.delete)
  59. screen.open('/')
  60. screen.wait(0.5)
  61. assert len(scene.objects) == 2
  62. screen.click('Delete group')
  63. screen.wait(0.5)
  64. assert len(scene.objects) == 0
  65. def test_replace_scene(screen: Screen):
  66. with ui.row() as container:
  67. with ui.scene() as scene:
  68. scene.sphere().with_name('sphere')
  69. def replace():
  70. container.clear()
  71. with container:
  72. nonlocal scene
  73. with ui.scene() as scene:
  74. scene.box().with_name('box')
  75. ui.button('Replace scene', on_click=replace)
  76. screen.open('/')
  77. screen.wait(0.5)
  78. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children[4].name') == 'sphere'
  79. screen.click('Replace scene')
  80. screen.wait(0.5)
  81. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children[4].name') == 'box'
  82. def test_create_dynamically(screen: Screen):
  83. ui.button('Create', on_click=ui.scene)
  84. screen.open('/')
  85. screen.click('Create')
  86. assert screen.find_by_tag('canvas')
  87. def test_rotation_matrix_from_euler():
  88. omega, phi, kappa = 0.1, 0.2, 0.3
  89. Rx = np.array([[1, 0, 0], [0, np.cos(omega), -np.sin(omega)], [0, np.sin(omega), np.cos(omega)]])
  90. Ry = np.array([[np.cos(phi), 0, np.sin(phi)], [0, 1, 0], [-np.sin(phi), 0, np.cos(phi)]])
  91. Rz = np.array([[np.cos(kappa), -np.sin(kappa), 0], [np.sin(kappa), np.cos(kappa), 0], [0, 0, 1]])
  92. R = Rz @ Ry @ Rx
  93. assert np.allclose(Object3D.rotation_matrix_from_euler(omega, phi, kappa), R)
  94. def test_object_creation_via_context(screen: Screen):
  95. with ui.scene() as scene:
  96. scene.box().with_name('box')
  97. screen.open('/')
  98. screen.wait(0.5)
  99. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children[4].name') == 'box'
  100. def test_object_creation_via_attribute(screen: Screen):
  101. scene = ui.scene()
  102. scene.box().with_name('box')
  103. screen.open('/')
  104. screen.wait(0.5)
  105. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children[4].name') == 'box'
  106. def test_clearing_scene(screen: Screen):
  107. with ui.scene() as scene:
  108. scene.box().with_name('box')
  109. with scene.group(): # see https://github.com/zauberzeug/nicegui/issues/4560
  110. scene.box().with_name('box2')
  111. ui.button('Clear', on_click=scene.clear)
  112. screen.open('/')
  113. screen.wait(0.5)
  114. assert len(scene.objects) == 3
  115. screen.click('Clear')
  116. screen.wait(0.5)
  117. assert len(scene.objects) == 0
  118. def test_gltf(screen: Screen):
  119. with ui.scene() as scene:
  120. scene.gltf('https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/Box/glTF-Binary/Box.glb')
  121. screen.open('/')
  122. screen.wait(1.0)
  123. assert screen.selenium.execute_script(f'return scene_{scene.html_id}.children.length') == 5