From 6fa994fa153acaf65d536d87193d79831db33660 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 27 Nov 2023 14:15:16 -0800 Subject: [PATCH] add threejs_demo --- threejs-demo/.gitignore | 4 + threejs-demo/assets/favicon.ico | Bin 0 -> 4286 bytes threejs-demo/requirements.txt | 2 + threejs-demo/rxconfig.py | 5 + threejs-demo/threejs_demo/__init__.py | 0 threejs-demo/threejs_demo/threejs_demo.py | 162 ++++++++++++++++++++++ 6 files changed, 173 insertions(+) create mode 100644 threejs-demo/.gitignore create mode 100644 threejs-demo/assets/favicon.ico create mode 100644 threejs-demo/requirements.txt create mode 100644 threejs-demo/rxconfig.py create mode 100644 threejs-demo/threejs_demo/__init__.py create mode 100644 threejs-demo/threejs_demo/threejs_demo.py diff --git a/threejs-demo/.gitignore b/threejs-demo/.gitignore new file mode 100644 index 00000000..eab0d4b0 --- /dev/null +++ b/threejs-demo/.gitignore @@ -0,0 +1,4 @@ +*.db +*.py[cod] +.web +__pycache__/ \ No newline at end of file diff --git a/threejs-demo/assets/favicon.ico b/threejs-demo/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..166ae995eaa63fc96771410a758282dc30e925cf GIT binary patch literal 4286 zcmeHL>rYc>81ELdEe;}zmYd}cUgmJRfwjUwD1`#s5KZP>mMqza#Viv|_7|8f+0+bX zHuqusuw-7Ca`DTu#4U4^o2bjO#K>4%N?Wdi*wZ3Vx%~Ef4}D1`U_EMRg3u z#2#M|V>}}q-@IaO@{9R}d*u7f&~5HfxSkmHVcazU#i30H zAGxQ5Spe!j9`KuGqR@aExK`-}sH1jvqoIp3C7Vm)9Tu=UPE;j^esN~a6^a$ZILngo;^ zGLXl(ZFyY&U!li`6}y-hUQ99v?s`U4O!kgog74FPw-9g+V)qs!jFGEQyvBf><U|E2vRmx|+(VI~S=lT?@~C5pvZOd`x{Q_+3tG6H=gtdWcf z)+7-Zp=UqH^J4sk^>_G-Ufn-2Hz z2mN12|C{5}U`^eCQuFz=F%wp@}SzA1MHEaM^CtJs<{}Tzu$bx2orTKiedgmtVGM{ zdd#vX`&cuiec|My_KW;y{Ryz2kFu9}=~us6hvx1ZqQCk(d+>HP>ks>mmHCjjDh{pe zKQkKpk0SeDX#XMqf$}QV{z=xrN!mQczJAvud@;zFqaU1ocq==Py)qsa=8UKrt!J7r z{RsTo^rgtZo%$rak)DN*D)!(Y^$@yL6Nd=#eu&?unzhH8yq>v{gkt8xcG3S%H)-y_ zqQ1|v|JT$0R~Y}omg2Y+nDvR+K|kzR5i^fmKF>j~N;A35Vr`JWh4yRqKl#P|qlx?` z@|CmBiP}ysYO%m2{eBG6&ix5 zr#u((F2{vb=W4jNmTQh3M^F2o80T49?w>*rv0mt)-o1y!{hRk`E#UVPdna6jnz`rw dKpn)r^--YJZpr;bYU`N~>#v3X5BRU&{{=gv-{1fM literal 0 HcmV?d00001 diff --git a/threejs-demo/requirements.txt b/threejs-demo/requirements.txt new file mode 100644 index 00000000..37593cab --- /dev/null +++ b/threejs-demo/requirements.txt @@ -0,0 +1,2 @@ + +reflex>=0.3.4 diff --git a/threejs-demo/rxconfig.py b/threejs-demo/rxconfig.py new file mode 100644 index 00000000..e582c088 --- /dev/null +++ b/threejs-demo/rxconfig.py @@ -0,0 +1,5 @@ +import reflex as rx + +config = rx.Config( + app_name="threejs_demo", +) \ No newline at end of file diff --git a/threejs-demo/threejs_demo/__init__.py b/threejs-demo/threejs_demo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/threejs-demo/threejs_demo/threejs_demo.py b/threejs-demo/threejs_demo/threejs_demo.py new file mode 100644 index 00000000..8121f54b --- /dev/null +++ b/threejs-demo/threejs_demo/threejs_demo.py @@ -0,0 +1,162 @@ +import uuid +from typing import Any, Dict +import reflex as rx + + +class Canvas(rx.Component): + """https://docs.pmnd.rs/react-three-fiber/api/canvas""" + + tag = "Canvas" + library = "@react-three/fiber" + + def get_event_triggers(self) -> Dict[str, Any]: + return super().get_event_triggers() | { + "on_pointer_missed": lambda: [], + } + + +class ThreeCommon(rx.Component): + args: rx.Var[Any] + position: rx.Var[tuple[float, float, float]] + rotation: rx.Var[tuple[float, float, float]] + + def _render(self) -> Dict[str, Any]: + tag = super()._render() + if tag.name: + tag.name = tag.name[0].lower() + tag.name[1:] + return tag + + +class EventData(rx.Base): + intersections: list[Any] + object: Any + wheelDelta: int + + +def event_signature(event: EventData) -> list[Any]: + return [event.object, event.intersections] + + +def wheel_event_signature(event: EventData) -> list[Any]: + return [event.wheelDelta] + + +class mesh(ThreeCommon): + tag = "Mesh" + + scale: rx.Var[float] + + def get_event_triggers(self) -> Dict[str, Any]: + return super().get_event_triggers() | { + "on_click": event_signature, + "on_wheel": wheel_event_signature, + "on_context_menu": event_signature, + "on_pointer_over": event_signature, + "on_pointer_out": event_signature, + "on_pointer_missed": lambda: [], + } + + +class boxGeometry(ThreeCommon): + tag = "BoxGeometry" + + +class meshStandardMaterial(ThreeCommon): + tag = "MeshStandardMaterial" + + color: rx.Var[str] + + +class ambientLight(ThreeCommon): + tag = "AmbientLight" + + +class pointLight(ThreeCommon): + tag = "PointLight" + + +class Edges(rx.Component): + tag = "Edges" + library = "@react-three/drei" + + +class State(rx.State): + rotate_step: tuple[float, float, float] = (0.1, 0, 0) + hovered: dict[str, bool] = {} + active: str + rotation: dict[str, tuple[float, float, float]] = {} + + @rx.cached_var + def rotate_step_axis(self) -> str: + max_step = max(self.rotate_step) + if max_step == self.rotate_step[0]: + return "x" + if max_step == self.rotate_step[1]: + return "y" + return "z" + + def on_click(self, id): + self.active = id + + def on_wheel(self, id, wheel_delta): + self.active = id + rotation = self.rotation.get(id, (0, 0, 0)) + step = 1 if wheel_delta > 0 else -1 + self.rotation[id] = ( + rotation[0] + step * self.rotate_step[0], + rotation[1] + step * self.rotate_step[1], + rotation[2] + step * self.rotate_step[2], + ) + + def on_pointer_missed(self): + self.active = "" + + def on_pointer_over(self, id): + self.hovered[id] = True + + def on_pointer_out(self, id): + self.hovered[id] = False + + +def box(id, position): + box_geometry = boxGeometry.create(args=[1, 1, 1]) + return mesh.create( + box_geometry, + meshStandardMaterial.create( + color=rx.cond(State.hovered[id], "hotpink", "orange") + ), + Edges.create(), + position=position, + rotation=State.rotation[id], + scale=rx.cond(State.active == id, 1.5, 1.0), + on_click=lambda o, i: State.on_click(id), + on_wheel=lambda wheel_delta: State.on_wheel(id, wheel_delta), + on_pointer_over=lambda o, i: State.on_pointer_over(id), + on_pointer_out=lambda o, i: State.on_pointer_out(id), + ) + + +def index() -> rx.Component: + return rx.vstack( + Canvas.create( + ambientLight.create(), + pointLight.create(position=(10, 10, 10)), + box(str(uuid.uuid4()), (-1.2, 0, 0)), + box(str(uuid.uuid4()), (1.2, 0, 0)), + on_pointer_missed=State.on_pointer_missed, + ), + rx.hstack( + rx.text("Rotation Axis:"), + rx.radio_group( + rx.radio("x", on_click=State.set_rotate_step((0.1, 0, 0))), + rx.radio("y", on_click=State.set_rotate_step((0, 0.1, 0))), + rx.radio("z", on_click=State.set_rotate_step((0, 0, 0.1))), + value=State.rotate_step_axis, + ), + ), + ) + + +app = rx.App() +app.add_page(index) +app.compile()