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]))