Biswajit Banerjee

### Setting up the Three.js ellipsoids

Javascript scientific visualization - Part 6

##### Introduction

In the previous articles in this series we talked about:

- Part 1: Reading VTK format particles with Javascript in a browser
- Part 2: Saving the read-in particle data in a Vuex store
- Part 3: Initialization of a store and the user interface.
- Part 4: Setting up the Three.js renderer
- Part 5: Setting up the Three.js scene and camera

We are now ready to discuss the `three-ellipsoid-particles`

component that we introduced in `ThreeGraphicsPanel.vue`

. Recall that the template has the following form:

```
<template>
<div id='three-graphics-container'>
<div class="uk-card uk-card-default uk-card-large">
<div class="uk-card-body">
<three-renderer v-bind:size="{w:500, h:500}">
<three-scene v-bind:size="size">
<three-camera v-bind:size="size" v-bind:position="{x: 100, z: 15 }">
</three-camera>
<three-ellipsoid-particles> </three-ellipsoid-particles>
</three-scene>
</three-renderer>
</div>
</div>
</div>
</template>
```

##### The ellipsoid particles component

The file `ThreeEllipsoidParticles.vue`

also does not contain any information inside the `<template>`

tags and contains the following:

```
<template> </template>
<script src="./ThreeEllipsoidParticles.ts"> </script>
```

However, the source code contains more - as you can see below.

```
import * as Vue from "vue";
import { Component, Lifecycle } from 'av-ts';
import THREE = require("three");
import Store from "./Store";
@Component({
name: 'ThreeEllipsoidParticles'
})
export default class ThreeEllipsoidParticles extends Vue {
@Lifecycle
public created() {
// Create a watch in the store to make sure the particle
// file has been read before particles are created
var self = this;
Store.watch(
function () {
return Store.getters.particleReadComplete;
},
function () {
if (Store.getters.particleReadComplete)
self.createThreeParticles();
});
}
// Actually create the particles
private createThreeParticles() {
..... see below ....
}
}
```

There are several aspects that need consideration in this code.

We use the `watch`

“instance method” of Vuex to make sure that the particles are added to the scene only after the particle file has been read.

In this example we do not stop watching the variable `particleReadComplete`

(see Part 2 for details) after the particle data has been converted and saved. However, that step is highly recommended.

##### Creating the ellipsoid particles

The particle axis data are in the form of angles between the ellipsoid axes and the world coordinate axes. These are converted directly into the appropriate rotation matrix. Sphere objects are then created at the origin, rotated, scaled, and translated to their actual positions. The sphere objects are then transformed into `SphereBufferGeometry`

objects to make their manipulation slightly more efficient. Finally, a “material” shading model is added to make sure that the image displayed isn’t flat and a triangulated mesh is generated for each object.

```
private createThreeParticles() {
// Get the particle data
let particles = Store.getters.particleData;
// Extract the radius and center
let radii = particles["Radius"];
let centers = particles["Position"];
let axes_a = particles["Axis a"];
let axes_b = particles["Axis b"];
let axes_c = particles["Axis c"];
// Loop through particles
var self = this;
radii.map(function (radius: any, index: number) {
// Get the radius ratios
let ratio = [1.0, radius[1]/radius[0], radius[2]/radius[0]];
// Get the axes into vectors and compute rotation matrix
let axis_a =
new THREE.Vector3(Math.cos(axes_a[index][0]),
Math.cos(axes_a[index][1]),
Math.cos(axes_a[index][2]));
let axis_b =
new THREE.Vector3(Math.cos(axes_b[index][0]),
Math.cos(axes_b[index][1]),
Math.cos(axes_b[index][2]));
let axis_c =
new THREE.Vector3(Math.cos(axes_c[index][0]),
Math.cos(axes_c[index][1]),
Math.cos(axes_c[index][2]));
let rotMatrix = new THREE.Matrix4();
rotMatrix.set(
axis_a.x, axis_b.x, axis_c.y, 0,
axis_a.y, axis_b.y, axis_c.y, 0,
axis_a.z, axis_b.z, axis_c.z, 0,
0, 0, 0, 1
);
// Create the sphere geometry
const sph_geometry = new THREE.SphereGeometry(radius[0], 32, 16);
// Rotate sphere
sph_geometry.applyMatrix(rotMatrix);
// Convert into ellipsoid
sph_geometry.applyMatrix(new THREE.Matrix4().makeScale(ratio[0], ratio[1], ratio[2]));
// Translate the geometry
let center = new THREE.Vector3(centers[index][0], centers[index][1], centers[index][2]);
sph_geometry.translate(center.x, center.y, center.z);
// Convert into buffer geometry
const geometry = new THREE.BufferGeometry().fromGeometry(sph_geometry);
// Create the material for display purposes
const material = new THREE.MeshPhongMaterial({
color: 0xffaa00,
emissive: 0x072534,
side: THREE.DoubleSide,
shading: THREE.SmoothShading,
wireframe: false
});
// Create the mesh
const sphere = new THREE.Mesh(geometry, material);
// Save the data
Store.commit('ADD_THREE_OBJECT', sphere);
});
Store.commit('THREE_OBJECTS_CREATED', true);
}
```

The rotation matrix performs a transformation of the form We want to rotate the world coordinate vectors , , , into vectors that are aligned with the ellipsoid axes, , , . Therefore, we need to find a rotation matrix such that where . Taking dot products of both sides with , we get Now, . So we have . Therefore, .

Let us look at the ellipsoid axis vector . This vector has components where , , and . That is why we can create the rotation matrix directly from the axis data in the code sample.

#### Remarks

A plot of the ellipsoids produced by our code can be seen below.

In the next part of this series we will explore how `vtk.js`

can be used to do the same plot.

If you have questions/comments/corrections, please contact banerjee at parresianz dot com dot zen (without the dot zen).

📅 01.03.2017 📁 JAVASCRIPT · TYPESCRIPT · THREEJS · VUE