import React, {Component} from 'react'; import PropTypes from 'prop-types' import ReactDOM from 'react-dom'; import * as THREE from 'three'; import STLLoader from './Loader' import OrbitControlsModule from 'three-orbit-controls' // import {ScaleLoader} from 'react-spinners'; // const STLLoader = STLLoaderModule(THREE); const OrbitControls = OrbitControlsModule(THREE); class STLViewer extends Component { static propTypes = { className: PropTypes.string, url: PropTypes.string, file: PropTypes.object, width: PropTypes.number, height: PropTypes.number, backgroundColor: PropTypes.string, modelMaterial: PropTypes.object, sceneClassName: PropTypes.string, onSceneRendered: PropTypes.func, }; static defaultProps = { backgroundColor: '#EAEAEA', modelMaterial: { color: '#888888' }, height: 400, width: 400, rotate: true, orbitControls: true, sceneClassName: '', }; componentDidMount() { this.renderModel(this.props); } shouldComponentUpdate(nextProps, nextState) { if (JSON.stringify(nextProps) === JSON.stringify(this.props)) { return false } return true } componentDidUpdate(prevProps, nextState) { this.renderModel(prevProps); } componentWillUnmount() { cancelAnimationFrame(this.animationFrame) } componentDidCatch(error, info) { console.log(error, info) } renderModel(props) { let camera, scene, renderer, mesh, distance, controls; const {url, file, width, height, modelMaterial, backgroundColor, orbitControls, sceneClassName, onSceneRendered} = props; let xDims, yDims, zDims; let component = this; if (!url) return scene = new THREE.Scene(); distance = 10000; const directionalLight = new THREE.DirectionalLight(0xddeeff, 0.6); directionalLight.position.x = 100; directionalLight.position.y = 100; directionalLight.position.z = 200; directionalLight.position.normalize(); scene.add(directionalLight); const directionalLight2 = new THREE.DirectionalLight(0xffffdd, 0.6); directionalLight2.position.x = -75; directionalLight2.position.y = 75; directionalLight2.position.z = -200; directionalLight2.position.normalize(); scene.add(directionalLight2); const ambientLight = new THREE.AmbientLight(0x404040); // soft white light scene.add(ambientLight); const onLoad = geometry => { geometry.computeFaceNormals(); geometry.computeVertexNormals(); geometry.center(); mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial(modelMaterial) ); geometry.computeBoundingBox(); xDims = geometry.boundingBox.max.x - geometry.boundingBox.min.x; yDims = geometry.boundingBox.max.y - geometry.boundingBox.min.y; zDims = geometry.boundingBox.max.z - geometry.boundingBox.min.z; scene.add(mesh); mesh.rotateX(this.props.transform.rotate.x) mesh.rotateY(this.props.transform.rotate.y) mesh.rotateZ(this.props.transform.rotate.z) camera = new THREE.PerspectiveCamera(30, width / height, 1, distance); camera.position.set(0, 0, Math.max(xDims * 3, yDims * 3, zDims * 3)); scene.add(camera); renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true, antialias: true, alpha: true, }); renderer.setSize(width, height); renderer.setClearColor(backgroundColor, 0); renderer.domElement.className = sceneClassName; if (orbitControls) { controls = new OrbitControls(camera, ReactDOM.findDOMNode(component)); controls.enableKeys = false; controls.addEventListener('change', orbitRender); } ReactDOM.findDOMNode(this).replaceChild(renderer.domElement, ReactDOM.findDOMNode(this).firstChild); render(); animate(); if (typeof onSceneRendered === "function") { onSceneRendered(ReactDOM.findDOMNode(renderer.domElement)) } }; const onProgress = (xhr) => { if (xhr.lengthComputable) { let percentComplete = xhr.loaded / xhr.total * 100; } }; const loader = new STLLoader(); if (file) { loader.loadFile(file, onLoad, onProgress); } else { loader.load(url, onLoad, onProgress); } const render = () => { renderer.render(scene, camera); }; const orbitRender = () => { render(); }; const animate = () => { if (!mesh) return this.animationFrame = requestAnimationFrame(animate) mesh.rotateZ(Math.PI * 0.001) render() } } render() { return (
); }; }; export default STLViewer;