awv3
Version:
⚡ AWV3 embedded CAD
229 lines (198 loc) • 7.3 kB
JavaScript
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, '');
};