198 lines
6.3 KiB
JavaScript
198 lines
6.3 KiB
JavaScript
import * as THREE from "https://cdn.jsdelivr.net/npm/three@v0.120.0/build/three.module.js";
|
|
import { STLLoader } from "https://cdn.jsdelivr.net/npm/three@v0.120.0/examples/jsm/loaders/STLLoader.js";
|
|
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@v0.120.0/examples/jsm/controls/OrbitControls.js";
|
|
|
|
|
|
class STLViewer {
|
|
|
|
constructor(container) {
|
|
this.mesh = null;
|
|
this.loader = new STLLoader();
|
|
this.container = container;
|
|
this.camera = new THREE.PerspectiveCamera(
|
|
60,
|
|
this.container.clientWidth / this.container.clientHeight,
|
|
1,
|
|
1000
|
|
);
|
|
this.camera.position.set(100, 100, 100);
|
|
|
|
// Renderer
|
|
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
this.setRenderer();
|
|
|
|
// Controls
|
|
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
this.setControls();
|
|
|
|
// World
|
|
this.scene = new THREE.Scene();
|
|
this.scene.background = new THREE.Color(0xefefef);
|
|
|
|
const axes = new THREE.AxesHelper(100);
|
|
this.scene.add(axes);
|
|
|
|
const gridXZ = new THREE.GridHelper(100, 10, new THREE.Color(0x006600));
|
|
gridXZ.position.set(0, 0, 0);
|
|
this.scene.add(gridXZ);
|
|
|
|
const gridXY = new THREE.GridHelper(100, 10, new THREE.Color(0x000066));
|
|
gridXY.position.set(0, 0, 0);
|
|
gridXY.rotation.x = Math.PI/2;
|
|
this.scene.add(gridXY);
|
|
|
|
const gridYZ = new THREE.GridHelper(100, 10, new THREE.Color(0x660000));
|
|
gridYZ.position.set(0, 0, 0);
|
|
gridYZ.rotation.z = Math.PI/2;
|
|
this.scene.add(gridYZ);
|
|
|
|
// Lights
|
|
this.scene.add(new THREE.HemisphereLight(0x443333, 0x111122));
|
|
|
|
this.addShadowedLight(1, 1, 1, 0xffffff, 1.35);
|
|
this.addShadowedLight(0.5, 1, - 1, 0xffffff, 1);
|
|
|
|
this.container.appendChild(this.renderer.domElement);
|
|
|
|
// Events listeners
|
|
window.addEventListener('resize', this.onWindowResize);
|
|
|
|
this.animate();
|
|
}
|
|
|
|
loadSTLBlob(blob) {
|
|
if (this.mesh != null) {
|
|
this.mesh.geometry.dispose();
|
|
this.mesh.material.dispose();
|
|
this.scene.remove(this.mesh);
|
|
this.mesh = null;
|
|
}
|
|
|
|
blob.arrayBuffer().then(buffer => {
|
|
const geometry = this.loader.parse(buffer);
|
|
|
|
const material = new THREE.MeshPhongMaterial( { color: 0xff5533, specular: 0x111111, shininess: 200 } );
|
|
this.mesh = new THREE.Mesh( geometry, material );
|
|
|
|
this.mesh.position.set(0, 0, 0);
|
|
this.mesh.rotation.set(0, 0, 0);
|
|
this.mesh.scale.set(1, 1, 1 );
|
|
|
|
this.mesh.castShadow = true;
|
|
this.mesh.receiveShadow = true;
|
|
|
|
this.scene.add(this.mesh);
|
|
});
|
|
}
|
|
|
|
setControls() {
|
|
this.controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
|
|
this.controls.dampingFactor = 0.05;
|
|
|
|
this.controls.screenSpacePanning = false;
|
|
this.controls.enablePan = false;
|
|
|
|
this.controls.minDistance = 1;
|
|
this.controls.maxDistance = 500;
|
|
|
|
this.controls.maxPolarAngle = Math.PI;
|
|
}
|
|
|
|
setRenderer() {
|
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
this.renderer.outputEncoding = THREE.sRGBEncoding;
|
|
|
|
this.renderer.shadowMap.enabled = true;
|
|
}
|
|
|
|
addShadowedLight(x, y, z, color, intensity) {
|
|
const directionalLight = new THREE.DirectionalLight(color, intensity);
|
|
directionalLight.position.set(x, y, z);
|
|
this.scene.add(directionalLight);
|
|
|
|
directionalLight.castShadow = false;
|
|
|
|
const d = 1;
|
|
directionalLight.shadow.camera.left = - d;
|
|
directionalLight.shadow.camera.right = d;
|
|
directionalLight.shadow.camera.top = d;
|
|
directionalLight.shadow.camera.bottom = - d;
|
|
|
|
directionalLight.shadow.camera.near = 1;
|
|
directionalLight.shadow.camera.far = 4;
|
|
|
|
directionalLight.shadow.bias = - 0.002;
|
|
}
|
|
|
|
onWindowResize() {
|
|
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
|
|
this.camera.updateProjectionMatrix();
|
|
|
|
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
}
|
|
|
|
animate() {
|
|
requestAnimationFrame(this.animate.bind(this));
|
|
this.render(this.renderer, this.scene, this.camera);
|
|
}
|
|
|
|
render() {
|
|
this.renderer.render(this.scene, this.camera);
|
|
}
|
|
}
|
|
|
|
function addFileInput(parent, id) {
|
|
const newLine = document.createElement("div");
|
|
const newInput = document.createElement("input");
|
|
const newLabel = document.createElement("label");
|
|
const newButton = document.createElement("button");
|
|
|
|
const uploadIcon = document.createElement("i");
|
|
const removeIcon = document.createElement("i");
|
|
|
|
const uploadText = document.createElement("span");
|
|
|
|
newLine.classList.add(..."w-100 mt2 mb2 flex".split(" "));
|
|
newInput.classList = ["clip"];
|
|
newLabel.classList.add(..."f6 pointer dim blue bl bt bb b--dark-blue w-90 ph3 pv2 link mr0 ml0 dib".split(" "));
|
|
newButton.classList.add(..."f6 pointer dim white bg-red ba b--dark-red ph3 pv2 w-10".split(" "));
|
|
|
|
uploadIcon.classList.add("fa", "fa-upload");
|
|
removeIcon.classList.add("fa", "fa-trash");
|
|
|
|
uploadIcon.setAttribute("aria-hidden", "true");
|
|
removeIcon.setAttribute("aria-hidden", "true");
|
|
|
|
uploadText.textContent = " Choose a file to upload";
|
|
|
|
newInput.type = "file";
|
|
newInput.name = id;
|
|
newInput.id = id;
|
|
|
|
newLabel.htmlFor = id;
|
|
newLabel.appendChild(uploadIcon);
|
|
newLabel.appendChild(uploadText);
|
|
|
|
newButton.appendChild(removeIcon);
|
|
|
|
newLine.appendChild(newInput);
|
|
newLine.appendChild(newLabel);
|
|
newLine.appendChild(newButton);
|
|
|
|
newInput.addEventListener('change', (event) => {
|
|
const fileName = event.target.value.split('\\').pop();
|
|
uploadText.textContent = fileName ? ` ${fileName}` : " Choose a file to upload";
|
|
});
|
|
|
|
newButton.addEventListener('click', (event) => {
|
|
newLine.remove();
|
|
});
|
|
|
|
parent.appendChild(newLine);
|
|
}
|
|
|
|
export {
|
|
STLViewer,
|
|
addFileInput
|
|
}; |