summaryrefslogtreecommitdiff
path: root/frontend/site/projects/museum/stl/STLViewer.js
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/site/projects/museum/stl/STLViewer.js')
-rw-r--r--frontend/site/projects/museum/stl/STLViewer.js191
1 files changed, 191 insertions, 0 deletions
diff --git a/frontend/site/projects/museum/stl/STLViewer.js b/frontend/site/projects/museum/stl/STLViewer.js
new file mode 100644
index 0000000..365b32d
--- /dev/null
+++ b/frontend/site/projects/museum/stl/STLViewer.js
@@ -0,0 +1,191 @@
+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: '#B92C2C' },
+ 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 = 300;
+ directionalLight.position.normalize();
+ scene.add(directionalLight);
+
+ const directionalLight2 = new THREE.DirectionalLight(0xffffdd, 0.6);
+ directionalLight2.position.x = -100;
+ directionalLight2.position.y = 100;
+ directionalLight2.position.z = -300;
+ 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.MeshPhongMaterial(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 (
+ <div
+ className={this.props.className}
+ style={{
+ position: 'fixed',
+ width: Math.round(this.props.width) + "px",
+ height: Math.round(this.props.height) + "px",
+ overflow: 'hidden',
+ ...this.props.style,
+ }}
+ >
+ <div style={{
+ height: '100%',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ }}>
+ </div>
+ </div>
+ );
+ };
+};
+
+export default STLViewer; \ No newline at end of file