Saving particle data in a Vuex store

13 minute read

Introduction

In the first part of this series on scientific visualization in Javascript, we saw how data in VTK unstructured grid format produced by C++ simulation codes can be read in to a Javascript frontend.

Recall the parseAndSaveVTKXML function in Typescript syntax:

public parseAndSaveVTKXML(xmlDoc : any) {
  let $xml = $(xmlDoc);
  ..............
  var pointData:any = {};
  ..............
  pointData["Position"] = arrayOfVec;
  // Save the data to a Vuex store
  Store.commit('SET_PARTICLE_DATA', pointData);
}

The Store in this code snippet, is a Vuex store that we use to save and access data that will be needed by various components of the visualizer. In this article, I will discuss my implementation of the store.

Typescript typings for Vue and Vuex

I’ve found the typings provided by av-ts to be more convenient than the “official” version for standard Vue-2, but for Vuex I use the official version. These type header files need the “experimental decorators” option to be included in the tsconfig.json file used by Visual Studio Code.

The compiler options in my tsconfig.json file are

{
    "compilerOptions": {
        "outDir": "./dist",
        "sourceMap": false,
        "noImplicitAny": true,
        "target": "ES6",
        "lib": ["dom", "es6", "es2015.promise"],
        "module": "commonjs",
        "allowJs": true,
        "rootDirs": [
           "vaango_ui"
        ],
        "experimentalDecorators": true
    },
    ...........
}
The store

I create the Store in a file called Store.ts. Because I am comparing the performance of Three.js and vtk.js, I include two modules that correspond to states needed by these. I also include a module for storing and retrieving the particle data. My Store.ts file is listed below:

import * as Vue   from 'vue';
import * as Vuex  from 'vuex';
import {ThreeGraphicsModule} from './ThreeGraphicsModule';
import {VTKGraphicsModule}   from './VTKGraphicsModule';
import {ParticleModule}      from './ParticleModule';

Vue.use(Vuex);
export default new Vuex.Store({
  modules: {
             threeGraphics : new ThreeGraphicsModule(),
             vtkGraphics   : new VTKGraphicsModule(),
             particles     : new ParticleModule()  
           }
})

To make sure that the store is available to all components, we add the store: Store key-value pair to the constructor in the file that acts as the entry point for the program, main.ts:

import * as Vue from "vue";
import Store from "./Store";
class main {
  public vm : Vue;
  constructor() {
    this.vm = new Vue ({
        store: Store,
        el: '#main-panel',
        components: {
            'main-panel' : MainPanel
        },
        render: h => {
            return h('main-panel');
        }
    });
  }
}
The particle module

The particle module creates the particle state and defines actions, getters, and mutations on this state. My implementation is in the file ParticleModule.ts listed below:

import { Module } from 'vuex';
import { ParticleState }    from './ParticleState';
import ParticleGetters      from './ParticleGetters';
import ParticleActions      from './ParticleActions';
import ParticleMutations    from './ParticleMutations';

export class ParticleModule implements Module<ParticleState, any> {
  public state : ParticleState;
  public getters   = ParticleGetters;
  public mutations = ParticleMutations;
  public actions   = ParticleActions;
  constructor() {
    this.state = new ParticleState();
  }
}
The particle state

The particle state contains the particle data that we want to store. Our implementation of ParticleState.ts is:

export class ParticleState {
  public particleData: any;
  public particleReadComplete: boolean;
  constructor() {
    this.particleData = {};
    this.particleReadComplete = false;
  }
};
The particle getters

The getter functions are needed to access the particle store data from Vue components. My implementation of ParticleGetters.ts is shown below:

import {Getter, GetterTree} from 'vuex';
import {ParticleState} from "./ParticleState";
export function particleData(state: ParticleState) {
  return state.particleData;
}
export function isParticleReadComplete(state: ParticleState) {
  return state.isParticleReadComplete;
}
export default <GetterTree<ParticleState, any>> {
  particleData,
  particleReadComplete
}
The particle mutations

To mutate the state of the store, we have to explicitly use mutation functions. These are implemented in ParticleMutations.ts as follows:

import {Mutation, MutationTree} from 'vuex';
import {ParticleState} from './ParticleState';
export function SET_PARTICLE_DATA(state: ParticleState, particleData: any) {
  state.particleData = particleData;
  state.isParticleReadComplete = true;
  console.log("Set particleData");
}
export function DELETE_PARTICLE_DATA(state: ParticleState, message: string) {
  state.particleData = null;
  state.isParticleReadComplete = false;
  console.log("Deleted particle data" + message);
}
export default <MutationTree<ParticleState>> {
  SET_PARTICLE_DATA,
  DELETE_PARTICLE_DATA
}

The names of the functions are capitalized by convention.

The SET_PARTICLE_DATA mutation is how we save the particle data when we call Store.commit('SET_PARTICLE_DATA', pointData); in our function parseAndSaveVTKXML.

The particle actions

Actions are similar to mutations but can perform asynchronous operations. In our case, we don’t want to use actions at this stage but implement the functionality in ParticleActions.ts:

import {Store, ActionTree, ActionContext} from 'vuex';
import {ParticleState} from "./ParticleState";
export function setParticleData(store: ActionContext<ParticleState, any>,
                                particleData: any)  {
  store.commit('SET_PARTICLE_DATA', particleData);
};
export function deleteParticleData(store: ActionContext<ParticleState, any>)  {
  store.commit('DELETE_PARTICLE_DATA', 'Deleting all particles');
};
export default <ActionTree<ParticleState, any>> {
  setParticleData,
  deleteParticleData
}

Remarks

Now that the particle data have been read in and stored, we can proceed with creating a three-dimensional view that we can interact with. We will explore a Three.js implementation in part 3 of this series.

Notice that we have used Typescript in our code. This choice has the advantage that we can use strong typing to catch errors during compile time. However, we a limited to using libraries that have been carefully assigned types by third-parties. This is a serious limitation and significantly reduces the attractiveness of Typescript as a development platform.