import { AViewerPluginSync, BufferGeometry2, IGeometry, Mesh2, type Object3DGeneratorPlugin, PhysicalMaterial, ThreeViewer, } from 'threepipe' import {TorusGeometryGenerator} from './primitives/TorusGeometryGenerator' import {CircleGeometryGenerator} from './primitives/CircleGeometryGenerator' import {BoxGeometryGenerator} from './primitives/BoxGeometryGenerator' import {SphereGeometryGenerator} from './primitives/SphereGeometryGenerator' import {PlaneGeometryGenerator} from './primitives/PlaneGeometryGenerator' import {CylinderGeometryGenerator} from './primitives/CylinderGeometryGenerator' import {GeometryGenerator, updateUi} from './AGeometryGenerator' /** * GeometryGeneratorPlugin * * Geometry generator plugin to create updatable parametric objects/geometries. * Includes support for several primitive types from three.js */ export class GeometryGeneratorPlugin extends AViewerPluginSync<''> { public static readonly PluginType = 'GeometryGeneratorPlugin' enabled = true toJSON: any = undefined generators: Record = { plane: new PlaneGeometryGenerator('plane'), sphere: new SphereGeometryGenerator('sphere'), box: new BoxGeometryGenerator('box'), circle: new CircleGeometryGenerator('circle'), torus: new TorusGeometryGenerator('torus'), cylinder: new CylinderGeometryGenerator('cylinder'), } generateObject(type: string, params?: any) { const generator = this.generators[type] if (!generator) throw new Error('Unknown generator type: ' + type) const obj = new Mesh2(params?.geometry ?? new BufferGeometry2(), params?.material ?? new PhysicalMaterial()) generator.generate(obj.geometry, params) obj.name = type obj.geometry.name = 'Generated ' + type return obj } generateGeometry(type: string, params: any, geometry?: IGeometry) { const generator = this.generators[type] if (!generator) throw new Error('Unknown generator type: ' + type) const g = generator.generate(geometry, params) g.name = 'Generated ' + type return g } updateGeometry(geometry: IGeometry, params: any) { if (!geometry.userData.generationParams?.type) throw new Error('Geometry is not generated') const generator = this.generators[geometry.userData.generationParams.type] if (!generator) throw new Error('Unknown generator type: ' + geometry.userData.generationParams.type) generator.generate(geometry, params) } onAdded(v: ThreeViewer) { super.onAdded(v) v.scene.addEventListener('sceneUpdate', this._sceneUpdate) this.refreshObject3DGenerator() } onRemove(viewer: ThreeViewer) { this._removeObject3DGenerators() super.onRemove(viewer) } protected _removeObject3DGenerators(refresh = true) { const object3DGenerator = this._viewer?.getPlugin('Object3DGeneratorPlugin') if (!object3DGenerator) return object3DGenerator.generators = Object.fromEntries(Object.entries(object3DGenerator.generators) .filter(([k, _]) => !k.startsWith('geometry-'))) as any refresh && object3DGenerator.uiConfig?.uiRefresh?.(true) return object3DGenerator } refreshObject3DGenerator() { const object3DGenerator = this._removeObject3DGenerators(false) if (!object3DGenerator) return Object.keys(this.generators).forEach(key=>{ object3DGenerator.generators['geometry-' + key] = (params: any)=>{ const obj = this.generateObject(key, params) obj.name = key return obj } }) object3DGenerator.uiConfig?.uiRefresh?.(true) } protected _sceneUpdate = (e: any)=>{ if (e.hierarchyChanged) { const obj = e.object || this._viewer?.scene.modelRoot if (obj) { obj.traverse((o: any)=>{ const type = o.geometry?.userData?.generationParams?.type if (!type) return updateUi(o.geometry, ()=>{ const gen = this.generators[type] return gen?.createUiConfig ? gen.createUiConfig(o.geometry) ?? [] : [] }) }) } } } uiConfig = { type: 'folder', label: 'Generate Geometry', children: [()=>Object.keys(this.generators).map((v) => ({ type: 'button', uuid: 'generate_' + v, label: 'Generate ' + v, value: async() => { const obj = this.generateObject(v) obj.name = v this._viewer?.scene.addObject(obj) }, }))], } }