import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; class Pyramid extends THREE.Object3D { constructor() { super(); let width = 1; let height = 1; let length = 2; let vertices = [ new THREE.Vector3( 0, 0, 0), new THREE.Vector3( width, height, length), new THREE.Vector3(-width, height, length), new THREE.Vector3(-width, -height, length), new THREE.Vector3( width, -height, length), ] // Faces { let material = new THREE.MeshPhongMaterial({color: 0xffffff}); material.transparent = true; material.opacity = 0.8; let geometry = new THREE.BufferGeometry(); let faces = [ // Sides of the pyramid [0, 2, 1], [0, 3, 2], [0, 4, 3], [0, 1, 4], // Base of the pyramid [1, 2, 3], [1, 3, 4], ] let buffer = new Float32Array(3 * 3 * faces.length); for (let faceIndex in faces) { let face = faces[faceIndex]; buffer[faceIndex * 3 * 3 + 0] = vertices[face[0]].x; buffer[faceIndex * 3 * 3 + 1] = vertices[face[0]].y; buffer[faceIndex * 3 * 3 + 2] = vertices[face[0]].z; buffer[faceIndex * 3 * 3 + 3] = vertices[face[1]].x; buffer[faceIndex * 3 * 3 + 4] = vertices[face[1]].y; buffer[faceIndex * 3 * 3 + 5] = vertices[face[1]].z; buffer[faceIndex * 3 * 3 + 6] = vertices[face[2]].x; buffer[faceIndex * 3 * 3 + 7] = vertices[face[2]].y; buffer[faceIndex * 3 * 3 + 8] = vertices[face[2]].z; } geometry.setAttribute('position', new THREE.BufferAttribute(buffer, 3)); const mesh = new THREE.Mesh(geometry, material); this.add(mesh); } // Lines { let material = new THREE.LineBasicMaterial({color: 0xff0000}); let geometry = new THREE.BufferGeometry(); let width = 1; let height = 1; let length = 2; let lines = [ [0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [2, 3], [3, 4], [4, 1], ] let buffer = new Float32Array(2 * 3 * lines.length); for (let lineIndex in lines) { let line = lines[lineIndex]; buffer[lineIndex * 2 * 3 + 0] = vertices[line[0]].x; buffer[lineIndex * 2 * 3 + 1] = vertices[line[0]].y; buffer[lineIndex * 2 * 3 + 2] = vertices[line[0]].z; buffer[lineIndex * 2 * 3 + 3] = vertices[line[1]].x; buffer[lineIndex * 2 * 3 + 4] = vertices[line[1]].y; buffer[lineIndex * 2 * 3 + 5] = vertices[line[1]].z; } geometry.setAttribute('position', new THREE.BufferAttribute(buffer, 3)); const mesh = new THREE.Line(geometry, material); this.add(mesh); } } } let renderer, scene, camera, controls, leds, cameraObject, domElement, raycaster, pointer, selectedObject; async function init(dataPath, domElementArg = document.body) { let request = await fetch('/api/calibration-data'); let data = await request.json(); domElement = domElementArg; let w = domElement === document.body ? window.innerWidth : domElement.offsetWidth; let h = domElement === document.body ? window.innerHeight : domElement.offsetHeight; raycaster = new THREE.Raycaster(); camera = new THREE.PerspectiveCamera(45, w / h, 0.001, 1000); camera.position.set(0, 0, -50); scene = new THREE.Scene(); leds = new THREE.Object3D(); for (let key in data) { let row = data[key]; const geometry = new THREE.SphereGeometry(1, 32, 16); const material = new THREE.MeshPhongMaterial({ color: 0xffff00 }); let sphere = new THREE.Mesh(geometry, material); sphere.position.x = row[0]; sphere.position.y = row[1]; sphere.position.z = row[2]; sphere.name = key leds.add(sphere); } scene.add(leds); cameraObject = new Pyramid(); scene.add(cameraObject); const axesHelper = new THREE.AxesHelper(10); scene.add(axesHelper); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); scene.add(directionalLight); const ambientLight = new THREE.AmbientLight(0xffffff, 0.25); scene.add(ambientLight); renderer = new THREE.WebGLRenderer({antialias: true, alpha: true}); renderer.setAnimationLoop(animate); selectedObject = document.getElementById('selected-object'); // Add listeners controls = new OrbitControls(camera, renderer.domElement); controls.update(); window.addEventListener('pointermove', onPointerMove); window.addEventListener('resize', onWindowResize, false); onWindowResize(); domElement.appendChild(renderer.domElement); } function animate(time) { controls.update(); if (pointer !== undefined) { raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(leds.children); if (intersects.length > 0) { for (let led of leds.children) { if (led === intersects[0].object) { led.material.color.setHex(0xff0000); selectedObject.innerText = led.name; } else { led.material.color.setHex(0xffff00); } } } } renderer.render(scene, camera); } function onWindowResize() { let w = domElement === document.body ? window.innerWidth : domElement.offsetWidth; let h = domElement === document.body ? window.innerHeight : domElement.offsetHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); } function onPointerMove(event) { // calculate pointer position in normalized device coordinates // (-1 to +1) for both components if (pointer === undefined) { pointer = new THREE.Vector2(); } let w = domElement === document.body ? window.innerWidth : domElement.offsetWidth; let h = domElement === document.body ? window.innerHeight : domElement.offsetHeight; pointer.x = (event.offsetX / w) * 2 - 1; pointer.y = - (event.offsetY / h) * 2 + 1; } export { init };