UNPKG

awv3

Version:
229 lines (198 loc) 7.3 kB
import * as THREE from 'three'; import Object3 from '../three/object3'; import { exponential } from '../animation/easing'; export class Pool extends THREE.Group { constructor({ session, name = 'pool' }) { super(); this.name = name; this.session = session; this.temporary = new THREE.Group(); this.temporary.name = `${name}.temporary`; this.temporary.updateParentMaterials = false; super.add(this.temporary); this.preset = { duration: 300, renderOrder: Object3.RenderOrder.Default }; this.defaults = this.preset; } update() { // ... } fadeIn(options) { if (!this.session) return; this.updateMaterials(); this.defaults = this.preset; let edgeOpacity = this.session.options.materials.edgeOpacity; let edgeColor = this.session.options.materials.edgeColor; options = { ...this.preset, ...options, }; this.materials.all.forEach(item => { let opacity = 1, color = item.color; if (item.meta) { color = item.meta.material.color.clone(); opacity = item.meta.material.opacity; } if (item.type.indexOf('Line') > -1) { opacity = edgeOpacity !== 'undefined' ? edgeOpacity : opacity; color = edgeColor !== 'undefined' ? edgeColor : color; } item .animate({ opacity, color, }) .start(options.duration); this.setRenderOrder(options.renderOrder); }); } fadeOut(options) { if (!this.session) return; this.updateMaterials(); let lineOpacity = this.session.options.materials.edgeOpacity || 0.3; this.defaults = { ...this.preset, meshOpacity: 0.3, lineOpacity, renderOrder: Object3.RenderOrder.MeshesFirst, ...options, }; this.animate({ materials: { meshes: [{ opacity: this.defaults.meshOpacity }], lines: [{ opacity: this.defaults.lineOpacity }], }, }).start(this.defaults.duration); this.setRenderOrder(this.defaults.renderOrder); } } export class ObjectPrototype extends THREE.Group { constructor({ session }) { super(); this.session = session; if (this.session) { this.pool = session.pool; } } reset(child) { if (this.pool) { child.updateMaterials(); const meshes = child.materials.meshes.map(material => ({ opacity: this.pool.defaults.meshOpacity || (session.options.materials.meshOpacity !== undefined ? session.options.materials.edgeOpacity : material.opacity), })); const lines = child.materials.lines.map(material => ({ opacity: this.pool.defaults.lineOpacity || (session.options.materials.edgeOpacity !== undefined ? session.options.materials.edgeOpacity : material.opacity), })); // This will fade the model from zero opacity to either its material-given default or to // various presets defined by this pool or its session child .animate({ materials: { meshes, lines, }, }) .from({ materials: { meshes: [{ opacity: 0 }], lines: [{ opacity: 0 }], }, }) .start(this.pool.defaults.duration) .easing(exponential.in); child.setRenderOrder(this.pool.defaults.renderOrder); } return this; } } // Helps detecting changes in two array-states export const arrayDiff = (next = [], current = [], callbackNew, callbackDeleted) => { let nextSet = new Set(next); let currentSet = new Set(current); let promises = []; if (callbackNew) { let newItems = next.filter(item => !currentSet.has(item)); if (newItems.length) { let results = callbackNew(newItems); if (results) { promises = results; } } } if (callbackDeleted) { let deletedItems = current.filter(item => !nextSet.has(item)); if (deletedItems.length) { let results = callbackDeleted(deletedItems); if (results) { promises = [...promises, ...results]; } } } return Promise.all(promises); }; // State observe with pre-select and user-select export const createObserver = (store, preSelect = store.getState) => (userSelect, onChange, options = {}) => { const { fireOnStart = false, // trigger onChange immediately unsubscribeOnUndefined = false, // unsubscribe if userSelect() returns undefined onRemove = undefined, // callback when state changes to undefined manager = undefined, // callback to pass unsubscribe function to } = options; let finish, unsubscribed = false; let preselect, state; if ( (preselect = preSelect()) === undefined || ((state = userSelect(preselect)) === undefined && unsubscribeOnUndefined) ) return () => {}; const handleChange = () => { if (unsubscribed) return; // Get pre-selected state const previousState = state; // Unsubscribe if preselect or state is undefined if ( (preselect = preSelect()) === undefined || ((state = userSelect(preselect)) === undefined && unsubscribeOnUndefined) ) { // revert state so that finish calls onRemove on a good state state = previousState; return finish(); } if (state === previousState) return; onChange(state, previousState); }; // Subscribe to store, fire on start, return hooked unsubscribe if (fireOnStart) { state = undefined; // so that onChange is called with prev === undefined handleChange(); } const unsubscribe = store.subscribe(handleChange); finish = () => { if (unsubscribed) return; unsubscribed = true; unsubscribe(); onRemove && onRemove(state, finish); }; // Call manager, if available. This allows classes to track subscribes and clean up // in bulk once they are destroyed. if (manager) manager(finish); return finish; }; // Build ClassAD feature path (_O. ...) export const buildFeaturePath = (tree, feature) => { if (typeof feature !== 'object') feature = tree[feature]; let path = [normalizeName(feature.name)], parent = feature; while (parent && parent.parent > 1) { parent = tree[parent.parent]; path.push(normalizeName(parent.name)); } return '_O.' + path.reverse().join('.'); }; export const normalizeName = name => { return name.replace(/(\s|-|\r?\n)/g, ''); };