Skip to content

Commit

Permalink
Add shapeCloud and some documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
derpylz committed Nov 16, 2020
1 parent db8761f commit 767b3da
Show file tree
Hide file tree
Showing 38 changed files with 1,261 additions and 528 deletions.
3 changes: 0 additions & 3 deletions PointCloud.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Scene } from "@babylonjs/core/scene";
import { Plot, LegendData } from "./babyplots";
export declare class PointCloud extends Plot {
private _SPS;
private _pointPicking;
private _selectionCallback;
private _folded;
Expand All @@ -15,6 +14,4 @@ export declare class PointCloud extends Plot {
private _createPointCloud;
resetAnimation(): void;
update(): boolean;
private _pointPicker;
updateSize(): void;
}
190 changes: 43 additions & 147 deletions PointCloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@

import { Scene } from "@babylonjs/core/scene";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { SphereBuilder } from "@babylonjs/core/Meshes/Builders/sphereBuilder";
import { Vector3, Color4, Color3 } from "@babylonjs/core/Maths/math";
import { SolidParticleSystem } from "@babylonjs/core/Particles/solidParticleSystem";
import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { FloatArray } from "@babylonjs/core/types";
import { PickingInfo } from "@babylonjs/core/Collisions/pickingInfo";
import { Plot, LegendData } from "./babyplots";

export class PointCloud extends Plot {
private _SPS: SolidParticleSystem;
private _pointPicking: boolean = false;
private _selectionCallback = function (selection: number[]) { return false; };
private _folded: boolean;
Expand Down Expand Up @@ -106,87 +103,44 @@ export class PointCloud extends Plot {
*/
private _createPointCloud(): void {
// prototype cell
if (this._coords.length > 10000) {
let customMesh = new Mesh("custom", this._scene);
// Set arrays for positions and indices
let positions = [];
let colors = [];
if (this._folded) {
for (let p = 0; p < this._coords.length; p++) {
positions.push(
this._foldedEmbedding[p][0] * this.xScale,
this._foldedEmbedding[p][2] * this.zScale,
this._foldedEmbedding[p][1] * this.yScale
);
let col = Color4.FromHexString(this._coordColors[p]);
colors.push(col.r, col.g, col.b, col.a);
}
} else {
for (let p = 0; p < this._coords.length; p++) {
positions.push(
this._coords[p][0] * this.xScale,
this._coords[p][2] * this.zScale,
this._coords[p][1] * this.yScale
);
let col = Color4.FromHexString(this._coordColors[p]);
colors.push(col.r, col.g, col.b, col.a);
}
let customMesh = new Mesh("custom", this._scene);
// Set arrays for positions and indices
let positions = [];
let colors = [];
if (this._folded) {
for (let p = 0; p < this._coords.length; p++) {
positions.push(
this._foldedEmbedding[p][0] * this.xScale,
this._foldedEmbedding[p][2] * this.zScale,
this._foldedEmbedding[p][1] * this.yScale
);
let col = Color4.FromHexString(this._coordColors[p]);
colors.push(col.r, col.g, col.b, col.a);
}
var vertexData = new VertexData();
// Assign positions
vertexData.positions = positions;
vertexData.colors = colors;
// Apply vertexData to custom mesh
vertexData.applyToMesh(customMesh, true);
var mat = new StandardMaterial("mat", this._scene);
mat.emissiveColor = new Color3(1, 1, 1);
mat.disableLighting = true;
mat.pointsCloud = true;
mat.pointSize = this._size;
customMesh.material = mat;
this.mesh = customMesh;
}
else {
let cell = SphereBuilder.CreateSphere("sphere", { segments: 2, diameter: this._size * 0.1 }, this._scene);
// let cell = MeshBuilder.CreateDisc("disc", {tessellation: 6, radius: this._size}, this._scene);
// particle system
let SPS = new SolidParticleSystem('SPS', this._scene, {
updatable: true,
isPickable: true
});
// add all cells to SPS
SPS.addShape(cell, this._coords.length);
// position and color cells
if (this._folded) {
for (let i = 0; i < SPS.nbParticles; i++) {
SPS.particles[i].position.x = this._foldedEmbedding[i][0] * this.xScale;
SPS.particles[i].position.z = this._foldedEmbedding[i][1] * this.zScale;
SPS.particles[i].position.y = this._foldedEmbedding[i][2] * this.yScale;
SPS.particles[i].color = Color4.FromHexString(this._coordColors[i]);
}
} else {
for (let i = 0; i < SPS.nbParticles; i++) {
SPS.particles[i].position.x = this._coords[i][0] * this.xScale;
SPS.particles[i].position.z = this._coords[i][1] * this.zScale;
SPS.particles[i].position.y = this._coords[i][2] * this.yScale;
SPS.particles[i].color = Color4.FromHexString(this._coordColors[i]);
}
} else {
for (let p = 0; p < this._coords.length; p++) {
positions.push(
this._coords[p][0] * this.xScale,
this._coords[p][2] * this.zScale,
this._coords[p][1] * this.yScale
);
let col = Color4.FromHexString(this._coordColors[p]);
colors.push(col.r, col.g, col.b, col.a);
}
SPS.buildMesh();
// scale bounding box to actual size of the SPS particles
SPS.computeBoundingBox = true;
// remove prototype cell
cell.dispose();
// calculate SPS particles
SPS.setParticles();
// SPS.billboard = true;
SPS.computeBoundingBox = true;
this._SPS = SPS;
this.mesh = SPS.mesh;
var mat = new StandardMaterial("pointMat", this._scene);
mat.alpha = 1;
this.mesh.material = mat;
}
var vertexData = new VertexData();
// Assign positions
vertexData.positions = positions;
vertexData.colors = colors;
// Apply vertexData to custom mesh
vertexData.applyToMesh(customMesh, true);
var mat = new StandardMaterial("mat", this._scene);
mat.emissiveColor = new Color3(1, 1, 1);
mat.disableLighting = true;
mat.pointsCloud = true;
mat.pointSize = this._size;
customMesh.material = mat;
this.mesh = customMesh;
Object.defineProperty(this, "alpha", {
set(newAlpha) {
this.mesh.material.alpha = newAlpha;
Expand All @@ -196,53 +150,21 @@ export class PointCloud extends Plot {

resetAnimation(): void {
this._folded = true;
if (this._SPS) {
for (let i = 0; i < this._SPS.particles.length; i++) {
this._SPS.particles[i].position = new Vector3(
this._foldedEmbedding[i][0] * this.xScale,
this._foldedEmbedding[i][2] * this.zScale,
this._foldedEmbedding[i][1] * this.yScale
);
let positionFunction = function (positions: FloatArray) {
let numberOfVertices = positions.length / 3;
for (let i = 0; i < numberOfVertices; i++) {
positions[i * 3] = this._foldedEmbedding[i][0] * this.xScale;
positions[i * 3 + 1] = this._foldedEmbedding[i][2] * this.zScale;
positions[i * 3 + 2] = this._foldedEmbedding[i][1] * this.yScale;
}
this._SPS.setParticles();
} else {
let positionFunction = function (positions: FloatArray) {
let numberOfVertices = positions.length / 3;
for (let i = 0; i < numberOfVertices; i++) {
positions[i * 3] = this._foldedEmbedding[i][0] * this.xScale;
positions[i * 3 + 1] = this._foldedEmbedding[i][2] * this.zScale;
positions[i * 3 + 2] = this._foldedEmbedding[i][1] * this.yScale;
}
}
this.mesh.updateMeshPositions(positionFunction.bind(this), true);
}
this.mesh.updateMeshPositions(positionFunction.bind(this), true);
this.mesh.refreshBoundingInfo();
this._foldCounter = 0;
}

update(): boolean {
if (this._SPS && this._folded) {
if (this._foldCounter < this._foldDelay) {
this._foldCounter += 1;
} else if (this._foldCounter < this._foldAnimFrames + this._foldDelay) {
for (let i = 0; i < this._SPS.particles.length; i++) {
this._SPS.particles[i].position.addInPlace(this._foldVectorFract[i]);
}
this._foldCounter += 1;
this._SPS.setParticles();
} else {
this._folded = false;
for (let i = 0; i < this._SPS.particles.length; i++) {
this._SPS.particles[i].position = new Vector3(
this._coords[i][0] * this.xScale,
this._coords[i][2] * this.zScale,
this._coords[i][1] * this.yScale
);
}
this._SPS.setParticles();
this.mesh.refreshBoundingInfo();
}
} else if (this.mesh && this._folded) {
if (this.mesh && this._folded) {
if (this._foldCounter < this._foldDelay) {
this._foldCounter += 1;
} else if (this._foldCounter < this._foldAnimFrames + this._foldDelay) {
Expand Down Expand Up @@ -277,30 +199,4 @@ export class PointCloud extends Plot {
}
return this._folded;
}
private _pointPicker(_evt: PointerEvent, pickResult: PickingInfo) {
if (this._pointPicking) {
const faceId = pickResult.faceId;
if (faceId == -1) {
return;
}
const idx = this._SPS.pickedParticles[faceId].idx;
for (let i = 0; i < this._SPS.nbParticles; i++) {
this._SPS.particles[i].color = new Color4(0.3, 0.3, 0.8, 1);
}
let p = this._SPS.particles[idx];
p.color = new Color4(1, 0, 0, 1);
this._SPS.setParticles();
this.selection = [idx];
this._selectionCallback(this.selection);
}
}
updateSize(): void {
for (let i = 0; i < this._SPS.nbParticles; i++) {
this._SPS.particles[i].scale.x = this._size;
this._SPS.particles[i].scale.y = this._size;
this._SPS.particles[i].scale.z = this._size;
}
this._SPS.setParticles();
super.updateSize();
}
}
9 changes: 9 additions & 0 deletions ShapeCloud.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Scene } from "@babylonjs/core/scene";
import "@babylonjs/core/Meshes/thinInstanceMesh";
import { Plot, LegendData } from "./babyplots";
export declare class ShapeCloud extends Plot {
private _shape;
private _shading;
constructor(scene: Scene, coordinates: number[][], colorVar: string[], shape: string, shading: boolean, size: number, legendData: LegendData, xScale?: number, yScale?: number, zScale?: number);
private _createShapeCloud;
}
115 changes: 115 additions & 0 deletions ShapeCloud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Babyplots - Easy, fast, interactive 3D visualizations
*
* Copyright (c) 2020, Nils Jonathan Trost. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { Scene } from "@babylonjs/core/scene";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import "@babylonjs/core/Meshes/thinInstanceMesh";
import { SphereBuilder } from "@babylonjs/core/Meshes/Builders/sphereBuilder";
import { BoxBuilder } from "@babylonjs/core/Meshes/Builders/boxBuilder";
import { TorusBuilder } from "@babylonjs/core/Meshes/Builders/torusBuilder";
import { CylinderBuilder } from "@babylonjs/core/Meshes/Builders/cylinderBuilder";
import { Color3, Color4, Matrix } from "@babylonjs/core/Maths/math";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { Plot, LegendData } from "./babyplots";

export class ShapeCloud extends Plot {
private _shape: string;
private _shading: boolean;
constructor(
scene: Scene,
coordinates: number[][],
colorVar: string[],
shape: string,
shading: boolean,
size: number,
legendData: LegendData,
xScale: number = 1,
yScale: number = 1,
zScale: number = 1
) {
super(scene, coordinates, colorVar, size * 0.1, legendData, xScale, yScale, zScale);
this._shape = shape;
this._shading = shading;
this._createShapeCloud();
}
/**
* Creates shapes at coordinates
*/
private _createShapeCloud(): void {
let instanceCount = this._coords.length;

let matricesData = new Float32Array(16 * instanceCount);
let colorData = new Float32Array(4 * instanceCount);

// set position and color data for shapes
for (let i = 0; i < instanceCount; i++) {
let matrix = Matrix.Translation(
this._coords[i][0] * this.xScale,
this._coords[i][1] * this.zScale,
this._coords[i][2] * this.yScale
);

matrix.copyToArray(matricesData, i * 16);

let col = Color4.FromHexString(this._coordColors[i]);

colorData.set(col.asArray(), i * 4);
}

let origMesh: Mesh;

switch (this._shape) {
case "box":
origMesh = BoxBuilder.CreateBox("root", { size: this._size });
break;
case "sphere":
origMesh = SphereBuilder.CreateSphere("root", { diameter: this._size });
break;
case "cone":
origMesh = CylinderBuilder.CreateCylinder("root", { height: this._size, diameterBottom: this._size, diameterTop: 0 }, this._scene);
break;
case "torus":
origMesh = TorusBuilder.CreateTorus("root", { diameter: this._size, thickness: this._size * 0.5 }, this._scene);
break;
case "cylinder":
origMesh = CylinderBuilder.CreateCylinder("root", { height: this._size, diameter: this._size }, this._scene);
break;
default:
origMesh = BoxBuilder.CreateBox("root", { size: 1 });
break;
}

origMesh.thinInstanceSetBuffer("matrix", matricesData, 16, true);
origMesh.thinInstanceSetBuffer("color", colorData, 4, true);

let mat = new StandardMaterial("shapeMat", this._scene);
if (!this._shading) {
mat.disableLighting = true;
mat.emissiveColor = Color3.White();
}
origMesh.material = mat;

this.mesh = origMesh;
Object.defineProperty(this, "alpha", {
set(newAlpha) {
this.mesh.material.alpha = newAlpha;
}
});
}
}
Loading

0 comments on commit 767b3da

Please sign in to comment.