paraview-lite
Version:
Lite ParaView client for Scientific Visualization on the Web
388 lines (359 loc) • 10.2 kB
JavaScript
/* eslint-disable no-param-reassign */
function extractProperties(names, properties, result) {
for (let i = 0; i < properties.length; i++) {
if (properties[i].children) {
extractProperties(names, properties[i].children, result);
} else if (names.indexOf(properties[i].name) !== -1) {
if (result[properties[i].name]) {
result[properties[i].name].push({ ...properties[i] });
} else {
result[properties[i].name] = [{ ...properties[i] }];
}
}
}
}
function objClone(item) {
return { ...item };
}
function copyMap(src, dest) {
Object.keys(src).forEach((key) => {
dest[key] = src[key].map(objClone);
});
}
/* eslint-enable no-param-reassign */
function defaultConvert(value) {
if (Array.isArray(value)) {
return value.slice();
}
return value;
}
function isDifferent(a, b) {
if (a !== b) {
if (Array.isArray(a)) {
for (let i = 0; i < a.length; i++) {
if (isDifferent(a[i], b[i])) {
return true;
}
}
return false;
}
return true;
}
// They are the same
return false;
}
export function toBoolean(value) {
return !!value;
}
export function bool2int(value) {
return value ? 1 : 0;
}
export function getStep(min, max) {
if (Number.isInteger(min) && Number.isInteger(max) && max - min > 10) {
return 1;
}
let delta = max - min;
let targetStep = 1;
if (delta > 0) {
while (delta > 0) {
targetStep *= 10;
delta /= 10;
}
return targetStep;
}
while (delta < 0) {
targetStep /= 10;
delta *= 10;
}
return targetStep;
}
// Expected structure
//
// {
// [propNameForVue]: {
// name: 'Background', // Server side property
// autoApply: true/false
// clientToServer(value) {},
// serverToClient(value) {},
// }
// }
//
// >>>>>
//
// {
// computed: { [...] },
// methods: {
// apply(),
// hasChange(),
// },
// created,
// }
export function generateComponentWithServerBinding(
proxyNameToCreate, // Provide null if not expected to create new proxy
proxyType,
propMaps,
componentDefinition
) {
let store = null;
let mtime = 0;
const serverPropNames = Object.keys(propMaps).map((i) => propMaps[i].name);
const serverState = {};
const localState = {};
const changeSet = [];
const computed = {
isNetworkBusy() {
return this.$store.getters.PVL_BUSY_COUNT;
},
activeSourceId() {
return this.$store.getters.PVL_PROXY_SELECTED_IDS[0];
},
activeViewId() {
return this.$store.getters.PVL_VIEW_ID;
},
activeRepresentationId() {
return this.$store.getters.PVL_PROXY_SOURCE_TO_REPRESENTATION_MAP[
this.$store.getters.PVL_PROXY_SELECTED_IDS[0]
];
},
activeProxyData() {
const id = this[`active${proxyType}Id`];
const proxyData = this.$store.getters.PVL_PROXY_DATA_MAP;
if (!id) {
// console.log(proxyType, 'No id available to update state');
return null;
}
if (!proxyData || !proxyData[id]) {
// console.log(proxyType, 'no proxy data for id', id, this.proxyData);
return null;
}
return proxyData[id];
},
inputBounds() {
if (this.create) {
return this.activeProxyData.data.bounds;
}
const myId = this.activeProxyData.id;
const nodeInfo = this.$store.getters.PVL_PROXY_PIPELINE.find(
(n) => n.id === myId
);
const parentId = nodeInfo.parent;
const proxyData = this.$store.getters.PVL_PROXY_DATA_MAP;
if (!proxyData || !proxyData[parentId]) {
// Fallback
return [-1, 1, -1, 1, -1, 1];
}
return proxyData[parentId].data.bounds;
},
inputArrays() {
const sourceId = this.activeSourceId;
const proxyData = this.$store.getters.PVL_PROXY_DATA_MAP;
const nodeInfo = this.$store.getters.PVL_PROXY_PIPELINE.find(
(n) => n.id === sourceId
);
if (!proxyData || !proxyData[sourceId]) {
return [];
}
if (this.create) {
return proxyData[sourceId].data.arrays;
}
if (proxyType === 'Representation') {
return proxyData[sourceId].data.arrays;
}
const idToUse = nodeInfo.parent === '0' ? sourceId : nodeInfo.parent;
if (!nodeInfo || !proxyData[idToUse]) {
return [];
}
return proxyData[idToUse].data.arrays;
},
...componentDefinition.computed,
};
function refreshState() {
// Create simple dependancy between method call and getters
this.mtime = mtime++;
const proxyData = this.activeProxyData;
if (!proxyData) {
return;
}
// Reset state
const keys = Object.keys(serverState);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
delete serverState[key];
delete localState[key];
}
if (this.create) {
// Reset props to default
Object.keys(propMaps).forEach((key) => {
serverState[propMaps[key].name] = [
{
id: '0',
value: propMaps[key].default,
subProxy: propMaps[key].subProxy,
label: propMaps[key].label || propMaps[key].name,
},
];
});
copyMap(serverState, localState);
return;
}
extractProperties(serverPropNames, proxyData.properties, serverState);
copyMap(serverState, localState);
}
function hasChange() {
changeSet.length = 0;
for (let i = 0; i < serverPropNames.length; i++) {
const key = serverPropNames[i];
if (
localState[key] &&
serverState[key] &&
isDifferent(localState[key][0].value, serverState[key][0].value)
) {
// console.log('hasChange', key, localState[key], serverState[key]);
for (let j = 0; j < localState[key].length; j++) {
changeSet.push(localState[key][j]);
}
}
}
// console.log('=>', changeSet.length);
return changeSet.length;
}
let scheduledApply = null;
function apply() {
if (this.create) {
return;
}
if (this.isNetworkBusy && !scheduledApply) {
scheduledApply = setTimeout(() => {
scheduledApply = null;
apply.apply(this);
}, 100);
}
if (this.isNetworkBusy < 3 && hasChange()) {
// console.log('apply', JSON.stringify(changeSet, null, 2));
store.dispatch('PVL_PROXY_UPDATE', changeSet);
store.dispatch('PVL_PROXY_DATA_FETCH', {
proxyId: this[`active${proxyType}Id`],
needUI: false,
});
}
}
function reset() {
copyMap(serverState, localState);
this.mtime++;
}
// Fill computed fields
Object.keys(propMaps).forEach((key) => {
const propName = propMaps[key].name;
const autoApply = propMaps[key].autoApply || false;
const noAutoApply = propMaps[key].noAutoApply || false;
const getFn = propMaps[key].serverToClient || defaultConvert;
const setFn = propMaps[key].clientToServer || defaultConvert;
computed[key] = {
get() {
// register dependency
this.mtime; // eslint-disable-line
return localState[propName]
? getFn(localState[propName][0].value)
: getFn(propMaps[key].default);
},
set(value) {
this.mtime++;
const newValue = setFn(value);
for (let i = 0; i < localState[propName].length; i++) {
localState[propName][i].value = newValue;
}
if (autoApply || (this.autoApply && !noAutoApply)) {
apply.apply(this);
} else {
this.$forceUpdate();
}
},
};
});
const props = { ...componentDefinition.props };
if (!props.create && !computed.create) {
props.create = {
type: Boolean,
default: false,
};
}
if (!props.autoApply && !computed.autoApply) {
props.autoApply = {
type: Boolean,
default: false,
};
}
return {
...componentDefinition,
computed,
props,
data() {
return { mtime: -1, ...componentDefinition.data.apply(this) };
},
methods: {
getStep,
apply,
reset,
hasChange,
refreshState,
...(proxyNameToCreate
? {
deleteProxy() {
if (!this.create) {
this.$store.dispatch('PVL_PROXY_DELETE', this.activeSourceId);
this.$store.commit('PVL_PROXY_SELECTED_IDS_SET', []);
} else {
this.$store.dispatch('PVL_MODULES_ACTIVE_CLEAR');
}
},
createProxy() {
const initialValues = {};
const subProxyValues = {};
if (hasChange()) {
Object.keys(localState).forEach((key) => {
for (let i = 0; i < localState[key].length; i++) {
if (!localState[key][i].subProxy) {
initialValues[localState[key][i].label] =
localState[key][i].value;
} else {
if (!subProxyValues[localState[key][i].subProxy]) {
subProxyValues[localState[key][i].subProxy] = {};
}
subProxyValues[localState[key][i].subProxy][
localState[key][i].label
] = localState[key][i].value;
}
}
});
}
this.$store.dispatch('PVL_PROXY_CREATE', {
name: proxyNameToCreate,
parentId: this.activeSourceId,
initialValues,
subProxyValues,
skipDomain: !!hasChange(),
});
},
}
: null),
...componentDefinition.methods,
},
watch: {
activeProxyData: refreshState,
...componentDefinition.watch,
},
created() {
// Capture store inside closure
store = this.$store;
if (componentDefinition.created) {
componentDefinition.created.apply(this);
}
},
mounted() {
this.refreshState();
if (componentDefinition.mounted) {
componentDefinition.mounted.apply(this);
}
},
};
}