Py.Cafe

kolibril13/

solara-ipyreact-threejs-fiber-boxes

Dynamic rotating cubes using threejs-fiber in solara

DocsPricing
  • app.py
  • requirements.txt
  • threejs-fiber.bundle.js
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import ipyreact
from pathlib import Path
ipyreact.define_module("threejs-fiber", Path("./threejs-fiber.bundle.js"))

class BoxWidget(ipyreact.Widget):
    _esm = """
        import React, { useRef, useState } from "react"
        import { Canvas, useFrame, useThree } from 'threejs-fiber'
        import { OrbitControls } from "threejs-fiber";

        export default function Box({position, color}) {
          const ref = useRef()
          useFrame(() => (ref.current.rotation.x = ref.current.rotation.y += 0.01))

          return (
            <mesh position={position} ref={ref}>
              <boxGeometry args={[1, 1, 1]} attach="geometry" />
              <meshPhongMaterial color={color} attach="material" />
            </mesh>
          )
        }

    """

import random


def random_color():
    # Generates a random hex color code
    return "#" + ''.join([random.choice('0123456789ABCDEF') for _ in range(6)])


import solara

@solara.component
def Box(position, color, props={}, events={}, children=[]):
    return BoxWidget.element(props={**props, **dict(color=color, position=position)}, events=events, children=children)


@solara.component
def Canvas(props={}, events={}, children=[]):
    return ipyreact.Widget.element(props=props, events=events, children=children, _type="Canvas", _module="threejs-fiber")


@solara.component
def OrbitControls(props={}, events={}, children=[]):
    return ipyreact.Widget.element(_type="OrbitControls", _module="threejs-fiber", props=props, events=events, children=children)


@solara.component
def DirectionalLight(props={}, events={}, children=[]):
    # starts with a lower case, should be available globally, so we don't need to pass
    # _module="threejs-fiber"
    return ipyreact.Widget.element(_type="directionalLight", props=props, events=events, children=children)


@solara.component
def Div(style={}, props={}, events={}, children=[]):
    # we use a ipyreact based div to avoid an extra wrapper div which will affect layout
    return ipyreact.Widget.element(_type="div", props={**props, **dict(style=style)}, children=children, events=events)

y0 = 1
boxes = solara.reactive([
    ([-1, y0, 3], "#18a36e"),
    ([1, y0, 3], "#f56f42"),
])
    
def add(event_data=None):
    x = random.random() * 4 - 2
    z = random.random() * 4 - 1
    color = random_color()  # Call the random_color function to get a random color
    boxes.value = [*boxes.value, ([x, y0, z], color)]


def clear():
    boxes.value = boxes.value[:2]


def add_10():
    for i in range(10):
        add()
        
@solara.component
def Page():
    with solara.Row():
        solara.Button("Clear", on_click=clear)
        solara.Button("Add 10", on_click=add_10)
    solara.Markdown("Click to add a new box")
    with Div(style={"height": "600px"}):
        # a canvas fill the available space, so we add a parent div with height
        with Canvas(events={"onClick": add}):
            for position, color in boxes.value:
                Box(position=position, color=color)
            OrbitControls()
            DirectionalLight(props=dict(color="#ffffff", intensity=1, position=[-1, 2, 4]))