UNPKG

angular-stl-model-viewer

Version:
228 lines (222 loc) 9.83 kB
import * as i0 from '@angular/core'; import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component, NgModule } from '@angular/core'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; /* eslint-disable @typescript-eslint/no-explicit-any */ const defaultMeshOptions = { castShadow: true, position: new THREE.Vector3(0, 0, 0), receiveShadow: true, scale: new THREE.Vector3(0.03, 0.03, 0.03) }; const isWebGLAvailable = () => { try { const canvas = document.createElement('canvas'); return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))); } catch { return false; } }; class StlModelViewerComponent { constructor(cdr, eleRef, ngZone) { this.cdr = cdr; this.eleRef = eleRef; this.ngZone = ngZone; this.stlModels = []; this.stlModelFiles = []; this.hasControls = true; this.camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 15); this.cameraTarget = new THREE.Vector3(0, 0, 0); this.light = new THREE.PointLight(0xffffff, 80); this.material = new THREE.MeshPhongMaterial({ color: 0x999999, shininess: 400, specular: 0x222222 }); this.scene = new THREE.Scene(); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.controls = null; this.meshOptions = []; this.centered = true; this.rendered = new EventEmitter(); this.hasWebGL = isWebGLAvailable(); this.meshGroup = new THREE.Object3D(); this.isRendered = false; this.showStlModel = true; this.stlLoader = new STLLoader(); this.middle = new THREE.Vector3(); this.render = () => { this.renderer.render(this.scene, this.camera); }; this.onWindowResize = () => { this.setSizes(); this.render(); }; this.cdr.detach(); // default light position this.light.position.set(1, 1, 2); // default camera position this.camera.position.set(3, 3, 3); // default scene background this.scene.background = new THREE.Color(0xffffff); // default renderer options this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.shadowMap.enabled = true; } ngOnInit() { if (!this.hasWebGL) { console.error('stl-model-viewer: Seems like your system does not support webgl.'); return; } this.ngZone.runOutsideAngular(() => { this.init(); }); } ngOnDestroy() { window.removeEventListener('resize', this.onWindowResize, false); this.meshGroup.children.forEach(child => this.meshGroup.remove(child)); this.scene.children.forEach(child => this.scene.remove(child)); this.camera.remove(this.light); if (this.material) { this.material.dispose(); } if (this.controls) { this.controls.removeEventListener('change', this.render); this.controls.dispose(); } if (this.eleRef && this.eleRef.nativeElement.children.length > 1) { this.eleRef.nativeElement.removeChild(this.renderer.domElement); } this.renderer.renderLists.dispose(); this.renderer.domElement.remove(); this.renderer.dispose(); } async createMesh(path, meshOptions = {}, parse = false) { let geometry = null; if (parse) { geometry = this.stlLoader.parse(path); } else { geometry = await this.stlLoader.loadAsync(path); } this.material.shininess = 100; const mesh = new THREE.Mesh(geometry, this.material); if (this.centered) { geometry.computeBoundingBox(); geometry.boundingBox.getCenter(this.middle); mesh.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(-this.middle.x, -this.middle.y, -this.middle.z)); } const vectorOptions = ['position', 'scale', 'up']; const options = Object.assign({}, defaultMeshOptions, meshOptions); Object.getOwnPropertyNames(options).forEach((option) => { if (vectorOptions.indexOf(option) > -1) { const vector = options[option]; const meshVectorOption = mesh[option]; meshVectorOption.set(vector.x, vector.y, vector.z); } else { mesh[option] = options[option]; } }); return mesh; } setSizes() { const width = this.eleRef.nativeElement.offsetWidth; const height = this.eleRef.nativeElement.offsetHeight; this.camera.aspect = width / height; this.camera.updateProjectionMatrix(); this.renderer.setSize(width, height); } async init() { this.camera.add(this.light); this.scene.add(this.camera); // use default controls if (this.hasControls) { if (!this.controls) { this.controls = new OrbitControls(this.camera, this.renderer.domElement); this.controls.enableZoom = true; this.controls.minDistance = 1; this.controls.maxDistance = 7; } this.controls.addEventListener('change', this.render); } window.addEventListener('resize', this.onWindowResize, false); let meshCreations = []; if (this.stlModels.length > 0) { meshCreations = this.stlModels.map((modelPath, index) => this.createMesh(modelPath, this.meshOptions[index])); } else { meshCreations = this.stlModelFiles.map((modelFile, index) => this.createMesh(modelFile, this.meshOptions[index], true)); } const meshes = await Promise.all(meshCreations); meshes.map(mesh => this.meshGroup.add(mesh)); this.scene.add(this.meshGroup); this.eleRef.nativeElement.appendChild(this.renderer.domElement); this.setSizes(); this.render(); this.ngZone.run(() => { this.isRendered = true; this.rendered.emit(); this.cdr.detectChanges(); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.1", type: StlModelViewerComponent, isStandalone: true, selector: "stl-model-viewer", inputs: { stlModels: "stlModels", stlModelFiles: "stlModelFiles", hasControls: "hasControls", camera: "camera", cameraTarget: "cameraTarget", light: "light", material: "material", scene: "scene", renderer: "renderer", controls: "controls", meshOptions: "meshOptions", centered: "centered" }, outputs: { rendered: "rendered" }, ngImport: i0, template: '', isInline: true, styles: [":host{width:100%;height:100%;display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerComponent, decorators: [{ type: Component, args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'stl-model-viewer', template: '', styles: [":host{width:100%;height:100%;display:block}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { stlModels: [{ type: Input }], stlModelFiles: [{ type: Input }], hasControls: [{ type: Input }], camera: [{ type: Input }], cameraTarget: [{ type: Input }], light: [{ type: Input }], material: [{ type: Input }], scene: [{ type: Input }], renderer: [{ type: Input }], controls: [{ type: Input }], meshOptions: [{ type: Input }], centered: [{ type: Input }], rendered: [{ type: Output }] } }); class StlModelViewerModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerModule, imports: [StlModelViewerComponent], exports: [StlModelViewerComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.1", ngImport: i0, type: StlModelViewerModule, decorators: [{ type: NgModule, args: [{ exports: [StlModelViewerComponent], imports: [ StlModelViewerComponent ] }] }] }); /* * Public API Surface of angular-stl-model-viewer */ /** * Generated bundle index. Do not edit. */ export { StlModelViewerComponent, StlModelViewerModule }; //# sourceMappingURL=angular-stl-model-viewer.mjs.map