Skip to content

Instantly share code, notes, and snippets.

@allen-munsch
Created February 26, 2024 14:57
Show Gist options
  • Save allen-munsch/456b7fef390d1e5cd7f50f3c78f9f9cc to your computer and use it in GitHub Desktop.
Save allen-munsch/456b7fef390d1e5cd7f50f3c78f9f9cc to your computer and use it in GitHub Desktop.
quick 3js example of directory tree file browsing
// Import Three.js
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls";
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Handle user interaction (e.g., mouse click)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const createText = (label) => {
const cv = document.createElement( 'canvas' );
cv.width = 1536 // 3 * 512
cv.height = 512;
const ctx = cv.getContext( '2d' );
ctx.fillStyle = '#fefefe';
ctx.fillRect( 0, 0, cv.width, cv.height );
ctx.fillStyle = '#129912';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.font = 'bold 6vh Arial';
ctx.fillText( `Tree | ${label}` , 0, 0.1 * cv.height );
const txtGeometry = new THREE.BoxGeometry( 2.4, 0.8, 0.1 ); // w 3 : h 1
const cvTexture = new THREE.Texture( cv );
cvTexture.needsUpdate = true; // otherwise all black only
return cvTexture
}
// Create a cube representing a file icon
var createCube = (x, y, z, color, depth, label) => {
var cubeGeometry = new THREE.BoxGeometry(depth, depth, );
var cubeMaterial = new THREE.MeshBasicMaterial({ map: createText(label) });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(x, y, z);
scene.add(cube);
// Create a label above the cube
function onMouseClick(event) {
// Calculate mouse position in normalized device coordinates
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Update the picking ray
raycaster.setFromCamera(mouse, camera);
// Check for intersections with the file cube
const intersects = raycaster.intersectObject(cube);
if (intersects.length > 0) {
// User clicked on the file cube
alert(`What would you like to do with ${label}`);
// TODO hookup to LLM agent executors etc
}
}
// Listen for mouse clicks
window.addEventListener('click', onMouseClick);
return cube;
};
// Define your tree structure
var treeData = ['a -> b', 'b -> c', 'd -> null', 'e -> f'];
// Create cubes and labels
var cubes = {};
let yOffset = 2; // Vertical spacing between levels
let parentOffset = 0;
treeData.forEach((item) => {
var [parent, child] = item.split(' -> ');
if (!cubes[parent]) {
cubes[parent] = createCube(parentOffset,parentOffset,parentOffset, 0x00ff00, 1, parent); // Green for directories
parentOffset += 1
}
if (child) {
cubes[child] = createCube(1, yOffset, 0, 0xff0000, .5); // Red for files
yOffset += 2; // Increase vertical spacing for the next level
// Create a line connecting parent and child
var lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push(cubes[parent].position, cubes[child].position);
var lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
var line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
}
});
// Set camera position
camera.position.z = 5;
// Add mouse controls
console.log(OrbitControls)
var controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.update(); // Update mouse controls
// Create an animation loop
function animate() {
requestAnimationFrame(animate);
// Rotate the cubes
for (var cube of Object.values(cubes)) {
cube.rotation.x += 0.0001;
cube.rotation.y += 0.0001;
}
renderer.render(scene, camera);
}
animate();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment