Py.Cafe

kolibril13/

threejs-gauss

Customizable "Hello World" Widget using AnyWidget Library

DocsPricing
  • app.py
  • requirements.txt
  • widget.css
  • widget.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
import anywidget
import traitlets
from traitlets import List, Any
from pathlib import Path
import numpy as np
import scipy.ndimage
import ipywidgets as widgets
from IPython.display import display

grids = 2
boxs = 5

voxelarray = np.zeros((boxs * grids, boxs * grids, boxs * grids))

i = 1
for xi in range(0, 2):
    for yi in range(0, 2):
        for zi in range(0, 2):
            voxelarray[
                xi * boxs : xi * boxs + boxs,
                yi * boxs : yi * boxs + boxs,
                zi * boxs : zi * boxs + boxs,
            ] = i
            i += 1

voxelarray = np.uint8(voxelarray * 255 / i)

class HelloWidget(anywidget.AnyWidget):
    _esm = Path("widget.js")

    count = traitlets.Int(0).tag(sync=True)
    voxel_data = List(List(List(Any()))).tag(sync=True)

# Initialize the widget first
three_viewer = HelloWidget()

# Then set the voxel data
voxel_data_list = voxelarray.tolist()
three_viewer.voxel_data = voxel_data_list

# Create and display the slider
sigma_slider = widgets.FloatSlider(value=0.1, min=0, max=4, step=0.01, description='Sigma:')

def apply_gaussian_filter(sigma):
    voxelarrayX = scipy.ndimage.gaussian_filter(voxelarray, sigma=sigma)
    voxel_data_list = voxelarrayX.tolist()
    three_viewer.voxel_data = voxel_data_list
    print(f"Applied Gaussian filter with sigma: {sigma}")

def on_slider_change(change):
    apply_gaussian_filter(change['new'])

sigma_slider.observe(on_slider_change, names='value')

# Display the widgets together
w = widgets.VBox([sigma_slider, three_viewer])
display(w)
page = w
widget.js
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
import * as THREE from "https://esm.sh/three";
import { OrbitControls } from "https://esm.sh/three/examples/jsm/controls/OrbitControls.js";

function render({ model, el }) {
  let container = document.createElement("div");
  container.style.height = "500px";
  container.style.width = "500px";
  el.appendChild(container);

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    75,
    container.clientWidth / container.clientHeight,
    0.1,
    1000
  );
  camera.position.z = 15;

  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(container.clientWidth, container.clientHeight);
  container.appendChild(renderer.domElement);

  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;
  controls.screenSpacePanning = false;
  controls.minDistance = 5;
  controls.maxDistance = 50;

  function getColor(value) {
    const minColor = new THREE.Color("yellow");
    const maxColor = new THREE.Color("red");
    return minColor.lerp(maxColor, value / 255);
  }

  function drawVoxels(data) {
    const offsetX = data.length / 2;
    const offsetY = data[0].length / 2;
    const offsetZ = data[0][0].length / 2;
    const geometry = new THREE.BoxGeometry();

    for (let x = 0; x < data.length; x++) {
      for (let y = 0; y < data[x].length; y++) {
        for (let z = 0; z < data[x][y].length; z++) {
          if (data[x][y][z] > 0) {
            const color = getColor(data[x][y][z]);
            const material = new THREE.MeshBasicMaterial({ color });
            const cube = new THREE.Mesh(geometry, material);
            cube.position.set(x - offsetX, y - offsetY, z - offsetZ);
            scene.add(cube);
          }
        }
      }
    }
  }

  function clearScene() {
    scene.children.forEach((child) => {
      if (child instanceof THREE.Mesh) {
        child.geometry.dispose();
        child.material.dispose();
        scene.remove(child);
      }
    });
  }

  function animate() {
    requestAnimationFrame(animate);

    // Add constant rotation
    scene.rotation.y += 0.01; // Adjust the speed as needed

    controls.update();
    renderer.render(scene, camera);
  }

  // Initial draw
  setTimeout(() => {
    const data = model.get("voxel_data");
    drawVoxels(data);
  }, 1000); // Adjust the timeout duration as needed

  model.on("change:voxel_data", () => {
    clearScene();
    const newData = model.get("voxel_data");
    drawVoxels(newData);
  });

  animate();
}

export default { render };