experiments with glb objects and data based diagrams.
This commit is contained in:
parent
dcd5a8835c
commit
a1adbf5bd9
@ -109,9 +109,8 @@
|
||||
<script src='/niceware.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
||||
On Quest</a>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
|
||||
-->
|
||||
<script>
|
||||
|
||||
BIN
public/assets/models/model.glb
Normal file
BIN
public/assets/models/model.glb
Normal file
Binary file not shown.
63
public/data/data.json
Normal file
63
public/data/data.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "Chart 1",
|
||||
"series": [
|
||||
{
|
||||
"name": "series 1",
|
||||
"values": [
|
||||
{
|
||||
"id": "1",
|
||||
"start": "2015-01-01",
|
||||
"end": "2015-12-31",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"start": "2016-01-01",
|
||||
"end": "2016-12-31",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"start": "2017-01-01",
|
||||
"end": "2017-12-31",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"start": "2018-01-01",
|
||||
"end": "2018-12-31",
|
||||
"value": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "series 2",
|
||||
"values": [
|
||||
{
|
||||
"id": "1",
|
||||
"start": "2015-01-01",
|
||||
"end": "2015-12-31",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"start": "2016-01-01",
|
||||
"end": "2016-12-31",
|
||||
"value": 6
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"start": "2017-01-01",
|
||||
"end": "2017-12-31",
|
||||
"value": 7
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"start": "2018-01-01",
|
||||
"end": "2018-12-31",
|
||||
"value": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -21,7 +21,7 @@ export class Rigplatform {
|
||||
private readonly xr: WebXRDefaultExperience;
|
||||
private yRotation: number = 0;
|
||||
public rigMesh: Mesh;
|
||||
|
||||
public flyMode: boolean = true;
|
||||
private turning: boolean = false;
|
||||
private velocity: Vector3 = Vector3.Zero();
|
||||
private turnVelocity: number = 0;
|
||||
@ -43,6 +43,7 @@ export class Rigplatform {
|
||||
this.fixRotation();
|
||||
this.initializeControllers();
|
||||
this.registerVelocityObserver();
|
||||
|
||||
}
|
||||
public forwardback(val: number) {
|
||||
this.velocity.z = (val * this.velocityArray[this.velocityIndex])*-1;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {DiagramEntity} from "../types/diagramEntity";
|
||||
import {DiagramEntity, DiagramEntityType} from "../types/diagramEntity";
|
||||
import {AbstractMesh, InstancedMesh, Mesh, Quaternion, Scene, Vector3} from "@babylonjs/core";
|
||||
import {DiagramConnection} from "../diagramConnection";
|
||||
import {TextLabel} from "../../objects/textLabel";
|
||||
@ -13,9 +13,19 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
||||
logger.error("buildMeshFromDiagramEntity: entity is null");
|
||||
return null;
|
||||
}
|
||||
if (!entity.id) {
|
||||
entity.id = "id" + uuidv4();
|
||||
switch (entity.type) {
|
||||
case DiagramEntityType.USER:
|
||||
logger.debug("buildMeshFromDiagramEntity: entity is user");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
generateId(entity);
|
||||
|
||||
const newMesh: AbstractMesh = createNewInstanceIfNecessary(entity, scene);
|
||||
return mapMetadata(entity, newMesh, scene);
|
||||
}
|
||||
|
||||
function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): AbstractMesh {
|
||||
const oldMesh: AbstractMesh = scene.getMeshById(entity.id);
|
||||
let newMesh: AbstractMesh;
|
||||
if (oldMesh) {
|
||||
@ -33,10 +43,20 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
||||
newMesh.metadata = {template: entity.template, exportable: true, tool: false};
|
||||
} else {
|
||||
logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return newMesh;
|
||||
}
|
||||
|
||||
function generateId(entity: DiagramEntity) {
|
||||
if (!entity.id) {
|
||||
entity.id = "id" + uuidv4();
|
||||
}
|
||||
}
|
||||
|
||||
function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene): AbstractMesh {
|
||||
if (newMesh) {
|
||||
if (entity.position) {
|
||||
newMesh.position = xyztovec(entity.position);
|
||||
@ -73,8 +93,6 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
||||
}
|
||||
return newMesh;
|
||||
}
|
||||
|
||||
|
||||
function xyztovec(xyz: { x, y, z }): Vector3 {
|
||||
return new Vector3(xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
@ -25,7 +25,15 @@ export function toDiagramEntity(mesh: AbstractMesh): DiagramEntity {
|
||||
entity.to = mesh?.metadata?.to;
|
||||
entity.scale = vectoxys(mesh.scaling);
|
||||
if (mesh.material) {
|
||||
switch (mesh.material.getClassName()) {
|
||||
case "StandardMaterial":
|
||||
entity.color = (mesh.material as any).diffuseColor.toHexString();
|
||||
break;
|
||||
case "PBRMaterial":
|
||||
entity.color = (mesh.material as any).albedoColor.toHexString();
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (entity.template != "#object-template") {
|
||||
logger.error("toDiagramEntity: mesh.material is null");
|
||||
|
||||
@ -13,6 +13,10 @@ export enum DiagramEventType {
|
||||
RESET
|
||||
}
|
||||
|
||||
export enum DiagramEntityType {
|
||||
USER = "user"
|
||||
}
|
||||
|
||||
export enum DiagramEventMask {
|
||||
LOCAL = 1,
|
||||
REMOTE = 2,
|
||||
@ -37,6 +41,7 @@ export type DiagramEntity = {
|
||||
position?: { x: number, y: number, z: number };
|
||||
rotation?: { x: number, y: number, z: number };
|
||||
template?: string;
|
||||
type?: DiagramEntityType;
|
||||
text?: string;
|
||||
scale?: { x: number, y: number, z: number };
|
||||
parent?: string;
|
||||
|
||||
87
src/objects/avatar.ts
Normal file
87
src/objects/avatar.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import {
|
||||
AssetContainer,
|
||||
Mesh,
|
||||
MeshBuilder,
|
||||
Observable,
|
||||
PhysicsAggregate,
|
||||
PhysicsMotionType,
|
||||
PhysicsShapeType,
|
||||
Scene,
|
||||
SceneLoader,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
|
||||
export function buildAvatar(scene: Scene) {
|
||||
const objectObservable = new Observable<AssetContainer>();
|
||||
objectObservable.add((container) => {
|
||||
try {
|
||||
|
||||
const data = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
|
||||
const mesh = (data.rootNodes[0] as Mesh);
|
||||
const body = scene.getMeshByName("Clone of avaturn_body");
|
||||
/*body.simplify(
|
||||
[
|
||||
{quality: .7, distance: 5},
|
||||
{quality: .1, distance: 10},
|
||||
],
|
||||
true,
|
||||
SimplificationType.QUADRATIC,
|
||||
function() {
|
||||
const skel = scene.getSkeletonById("Clone of Armature");
|
||||
body.parent.getChildMeshes().forEach((m) => {
|
||||
if (m.name.indexOf("ecimated") > -1) {
|
||||
m.skeleton = skel;
|
||||
}
|
||||
});
|
||||
console.log("simplification done");
|
||||
}
|
||||
)
|
||||
*/
|
||||
|
||||
|
||||
const bounds = mesh.getHierarchyBoundingVectors(true);
|
||||
console.log(bounds);
|
||||
const size = bounds.max.subtract(bounds.min);
|
||||
const top = MeshBuilder.CreateBox("container", {width: size.x, height: size.y, depth: size.z}, scene);
|
||||
top.position.y = 1.6;
|
||||
top.metadata = {grabbable: true};
|
||||
mesh.parent = top;
|
||||
mesh.position.y = -size.y / 2;
|
||||
top.position = new Vector3(-.6, size.y / 2 + 1, 0);
|
||||
|
||||
//top.scaling = new Vector3(.1, .1, .1);
|
||||
top.visibility = 0;
|
||||
|
||||
//top.physicsBody = new PhysicsBody(top, PhysicsMotionType.DYNAMIC, false, this.scene);
|
||||
|
||||
data.animationGroups[0].play(true);
|
||||
|
||||
|
||||
const physicsAggregate = new PhysicsAggregate(top,
|
||||
PhysicsShapeType.BOX, {mass: 100, restitution: .02, friction: .3}, scene);
|
||||
physicsAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC);
|
||||
physicsAggregate.body.setGravityFactor(1);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
//physicsAggregate.body.setAngularDamping(.5);
|
||||
|
||||
//top.physicsBody.setLinearVelocity(Vector3.Up().scale(100));
|
||||
//mesh.parent = top.physicsBody.transformNode;
|
||||
|
||||
|
||||
});
|
||||
loadObject(scene, objectObservable);
|
||||
}
|
||||
|
||||
function loadObject(scene: Scene, observable: Observable<AssetContainer>) {
|
||||
SceneLoader.LoadAssetContainer("/assets/models/",
|
||||
"model.glb",
|
||||
scene,
|
||||
(container: AssetContainer) => {
|
||||
observable.notifyObservers(container);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
@ -23,7 +23,7 @@ export class TextLabel {
|
||||
}
|
||||
|
||||
//Set font
|
||||
const height = 0.125;
|
||||
const height = 0.05;
|
||||
const font_size = 24;
|
||||
const font = "bold " + font_size + "px Arial";
|
||||
//Set height for dynamic texture
|
||||
@ -65,10 +65,11 @@ export class TextLabel {
|
||||
|
||||
const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
|
||||
plane.parent = mesh;
|
||||
plane.position.y = yOffset + height;
|
||||
//plane.scaling.y = mesh.scaling.y;
|
||||
//plane.scaling.x = mesh.scaling.x;
|
||||
//plane.scaling.z = mesh.scaling.z;
|
||||
|
||||
plane.scaling.y = (1 / mesh.scaling.y);
|
||||
plane.scaling.x = (1 / mesh.scaling.x);
|
||||
plane.scaling.z = (1 / mesh.scaling.z);
|
||||
plane.position.y = yOffset + (height * plane.scaling.y);
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,21 @@
|
||||
import {Color3, MeshBuilder, Observable, Scene, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
|
||||
import {Color3, MeshBuilder, Observable, PBRMaterial, Scene, TransformNode, Vector3} from "@babylonjs/core";
|
||||
import {enumKeys} from "../../util/functions/enumKeys";
|
||||
import {ToolType} from "../types/toolType";
|
||||
import {buildTool} from "./buildTool";
|
||||
import {AdvancedDynamicTexture, ColorPicker} from "@babylonjs/gui";
|
||||
import {MarbleProceduralTexture} from "@babylonjs/procedural-textures";
|
||||
|
||||
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number,
|
||||
colorChangeObservable: Observable<{ oldColor: string, newColor: string }>) {
|
||||
const width = 1;
|
||||
const depth = .2;
|
||||
const material = new StandardMaterial("material-" + color.toHexString(), scene);
|
||||
material.diffuseColor = color;
|
||||
const material = new PBRMaterial("material-" + color.toHexString(), scene);
|
||||
|
||||
//const material = new StandardMaterial("material-" + color.toHexString(), scene);
|
||||
material.albedoColor = color;
|
||||
material.metallic = 1;
|
||||
material.bumpTexture = new MarbleProceduralTexture("marble", 1024, scene);
|
||||
material.bumpTexture.level = 5;
|
||||
const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {
|
||||
width: width,
|
||||
height: .01,
|
||||
@ -42,9 +48,9 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i
|
||||
colorPicker.scaleX = 5;
|
||||
colorPicker.value = color;
|
||||
colorPicker.onValueChangedObservable.add((value) => {
|
||||
const oldColor = material.diffuseColor.clone();
|
||||
const oldColor = material.albedoColor.clone();
|
||||
const newColor = value.clone();
|
||||
material.diffuseColor = newColor;
|
||||
material.albedoColor = newColor;
|
||||
const newColorHex = newColor.toHexString();
|
||||
material.id = "material-" + newColorHex;
|
||||
material.name = "material-" + newColorHex;
|
||||
|
||||
@ -1,11 +1,23 @@
|
||||
import {AbstractMesh, Color3, InstancedMesh, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||
import {AbstractMesh, Color3, InstancedMesh, PBRMaterial, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||
import {ToolType} from "../types/toolType";
|
||||
import {buildMesh} from "./buildMesh";
|
||||
|
||||
const WIDGET_SIZE = .1;
|
||||
|
||||
export function buildTool(tool: ToolType, parent: AbstractMesh) {
|
||||
const id = toolId(tool, (parent.material as StandardMaterial).diffuseColor);
|
||||
let id = "ID";
|
||||
switch (parent.material.getClassName()) {
|
||||
|
||||
case "StandardMaterial":
|
||||
id = toolId(tool, (parent.material as StandardMaterial).diffuseColor);
|
||||
break;
|
||||
case "PBRMaterial":
|
||||
id = toolId(tool, (parent.material as PBRMaterial).albedoColor);
|
||||
break;
|
||||
default:
|
||||
this.logger.warn("buildTool: parent.material is null");
|
||||
}
|
||||
|
||||
|
||||
const newItem = buildMesh(tool, `tool-${id}`, parent.getScene());
|
||||
if (!newItem) {
|
||||
|
||||
@ -1,14 +1,4 @@
|
||||
import {
|
||||
AssetContainer,
|
||||
Color3,
|
||||
Mesh,
|
||||
MeshBuilder,
|
||||
Observable,
|
||||
Scene,
|
||||
SceneLoader,
|
||||
TransformNode,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {AssetContainer, Color3, Mesh, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
|
||||
|
||||
import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui";
|
||||
import {ControllerEventType, Controllers} from "../controllers/controllers";
|
||||
@ -81,25 +71,8 @@ export class Toolbox {
|
||||
this.addPanel.node.scaling = new Vector3(.1, .1, .1);
|
||||
this.addPanel.position = new Vector3(-.25, 0, 0);
|
||||
//@TODO: move this somewhere else, just to prototype loading objects.
|
||||
//loadObject(this.scene, this.objectObservable);
|
||||
this.objectObservable.add((container) => {
|
||||
this.logger.debug("loaded object");
|
||||
const data = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
|
||||
const mesh = (data.rootNodes[0] as Mesh);
|
||||
const bounds = data.rootNodes[0].getHierarchyBoundingVectors(true);
|
||||
console.log(bounds);
|
||||
const size = bounds.max.subtract(bounds.min);
|
||||
const top = MeshBuilder.CreateBox("container", {width: size.x, height: size.y, depth: size.z}, this.scene);
|
||||
top.position.y = 1.5;
|
||||
top.metadata = {template: "#object-template", grabbable: true, tool: true};
|
||||
mesh.parent = top;
|
||||
mesh.position.y = -size.y / 2;
|
||||
top.position = new Vector3(-.6, .2, 0);
|
||||
//top.scaling = new Vector3(.1, .1, .1);
|
||||
top.visibility = 0;
|
||||
console.log(data.rootNodes.length);
|
||||
|
||||
});
|
||||
|
||||
addButton.onPointerClickObservable.add(() => {
|
||||
buildColor(Color3.Random(), this.scene, this.node, this.index++, this.colorChangeObservable);
|
||||
});
|
||||
@ -111,16 +84,7 @@ export class Toolbox {
|
||||
}
|
||||
}
|
||||
|
||||
function loadObject(scene: Scene, observable: Observable<AssetContainer>) {
|
||||
SceneLoader.LoadAssetContainer("/assets/models/",
|
||||
"server_racking_system.glb",
|
||||
scene,
|
||||
(container: AssetContainer) => {
|
||||
observable.notifyObservers(container);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function createButton(): Button3D {
|
||||
const addButton = new Button3D("add-button");
|
||||
const text = new TextBlock("add-button-text", "Add Color");
|
||||
|
||||
@ -103,7 +103,7 @@ export class CustomEnvironment {
|
||||
|
||||
ground.material = groundMaterial;
|
||||
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, scene);
|
||||
|
||||
//buildAvatar(scene);
|
||||
return ground;
|
||||
}
|
||||
}
|
||||
16
src/util/functions/buildQuestLink.ts
Normal file
16
src/util/functions/buildQuestLink.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export function buildQuestLink() {
|
||||
/*
|
||||
<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
||||
On Quest</a>
|
||||
</div>
|
||||
*/
|
||||
const div = document.createElement("div");
|
||||
div.id = "questLaunch";
|
||||
const a = document.createElement("a");
|
||||
a.href = "https://www.oculus.com/open_url/?url=" + window.location.href;
|
||||
a.target = "_blank";
|
||||
a.innerText = "Launch On Quest";
|
||||
div.appendChild(a);
|
||||
document.body.appendChild(div);
|
||||
|
||||
}
|
||||
6
src/visualization/chartDataType.ts
Normal file
6
src/visualization/chartDataType.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {TimeseriesType} from "./timeseriesType";
|
||||
|
||||
export type ChartDataType = {
|
||||
name: string;
|
||||
series: TimeseriesType[];
|
||||
}
|
||||
22
src/visualization/functions/buildContainer.ts
Normal file
22
src/visualization/functions/buildContainer.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {Color3, Mesh, MeshBuilder, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
|
||||
|
||||
export function buildContainer(parent: TransformNode, size: Vector3) {
|
||||
const scene = parent.getScene();
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
const material = new StandardMaterial("container-material", scene);
|
||||
material.diffuseColor = new Color3(.5, .5, .5);
|
||||
|
||||
const floor = MeshBuilder.CreatePlane("floor", {width: size.x, height: size.z, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||
const left = MeshBuilder.CreatePlane("left", {width: size.z, height: size.y, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||
const back = MeshBuilder.CreatePlane("back", {width: size.x, height: size.y, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||
[floor, left, back].forEach((mesh) => {
|
||||
mesh.material = material;
|
||||
mesh.parent = parent;
|
||||
});
|
||||
left.position = new Vector3(size.x / 2, size.y / 2, 0);
|
||||
back.position = new Vector3(0, size.y / 2, -size.z / 2);
|
||||
left.rotation.y = Math.PI / 2;
|
||||
floor.rotation.x = Math.PI / 2;
|
||||
}
|
||||
17
src/visualization/functions/buildLabel.ts
Normal file
17
src/visualization/functions/buildLabel.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {AbstractMesh, Mesh, MeshBuilder, TransformNode} from "@babylonjs/core";
|
||||
|
||||
const LABEL_HEIGHT = .1;
|
||||
|
||||
export function buildLabel(name: string, parent: TransformNode, labelWidth: number): void {
|
||||
const scene = parent.getScene();
|
||||
const seriesLabel = MeshBuilder.CreatePlane(name + "-label", {
|
||||
width: labelWidth,
|
||||
height: LABEL_HEIGHT,
|
||||
sideOrientation: Mesh.DOUBLESIDE
|
||||
}, scene);
|
||||
seriesLabel.parent = parent;
|
||||
seriesLabel.position.z = LABEL_HEIGHT / 2;
|
||||
seriesLabel.position.y = .5;
|
||||
seriesLabel.rotation.x = Math.PI / 2;
|
||||
seriesLabel.material = (parent as AbstractMesh).material;
|
||||
}
|
||||
62
src/visualization/functions/buildSeries.ts
Normal file
62
src/visualization/functions/buildSeries.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import {
|
||||
AbstractMesh,
|
||||
Color3,
|
||||
InstancedMesh,
|
||||
Mesh,
|
||||
MeshBuilder,
|
||||
StandardMaterial,
|
||||
TransformNode,
|
||||
Vector3
|
||||
} from "@babylonjs/core";
|
||||
import {buildLabel} from "./buildLabel";
|
||||
import {TimeseriesType} from "../timeseriesType";
|
||||
|
||||
export function buildSeries(parent: TransformNode, series: TimeseriesType, index: number, count: number, scale: number): void {
|
||||
const scene = parent.getScene();
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
const labelWidth = 1 / count * .8;
|
||||
const material = new StandardMaterial(series.name + "-material", scene);
|
||||
const seriesMesh = MeshBuilder.CreatePlane(series.name, {width: labelWidth, height: 1, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||
seriesMesh.material = material;
|
||||
seriesMesh.parent = parent;
|
||||
seriesMesh.position.x = .5 - (index / count) - (1 / count / 2);
|
||||
seriesMesh.position.y = .001;
|
||||
seriesMesh.rotation.x = Math.PI / 2;
|
||||
material.diffuseColor = Color3.Random();
|
||||
buildLabel(series.name, seriesMesh, labelWidth);
|
||||
buildValues(seriesMesh, series, scale);
|
||||
}
|
||||
|
||||
function buildValues(parent: TransformNode, series: TimeseriesType, scale: number): void {
|
||||
const scene = parent.getScene();
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
const valueCount = series.values.length;
|
||||
const valueStep = 1 / valueCount;
|
||||
let lastValue = null;
|
||||
const linepoints: Vector3[] = [];
|
||||
const basepoint = MeshBuilder.CreatePlane(series.name + "-value-base", {height: .003, width: .01}, scene);
|
||||
|
||||
basepoint.parent = parent;
|
||||
basepoint.material = (parent as AbstractMesh).material;
|
||||
basepoint.visibility = .5;
|
||||
series.values.forEach((value, index) => {
|
||||
//const point = MeshBuilder.CreateSphere(series.name + "-value-" + index, {diameter: .01}, scene);
|
||||
const point = new InstancedMesh(series.name + "-value-" + index, basepoint);
|
||||
point.parent = parent;
|
||||
//point.rotation.x = Math.PI/2;
|
||||
point.position.z = .5 - index * valueStep;
|
||||
point.position.y = value.value * scale;
|
||||
|
||||
point.billboardMode = Mesh.BILLBOARDMODE_Y;
|
||||
linepoints.push(point.position.clone());
|
||||
});
|
||||
const line = MeshBuilder.CreateLines(series.name + "-line", {points: linepoints}, scene);
|
||||
line.color = ((parent as AbstractMesh).material as StandardMaterial).diffuseColor;
|
||||
line.parent = parent;
|
||||
line.isPickable = false;
|
||||
line.rotation.x = -Math.PI / 2;
|
||||
}
|
||||
58
src/visualization/functions/genData.ts
Normal file
58
src/visualization/functions/genData.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {ChartDataType} from "../chartDataType";
|
||||
import {TimepointType, TimeseriesType} from "../timeseriesType";
|
||||
|
||||
export function genData(): ChartDataType {
|
||||
return {
|
||||
name: "test",
|
||||
series: [
|
||||
testDataSeries("series1"),
|
||||
testDataSeries("series2"),
|
||||
testDataSeries("series3"),
|
||||
testDataSeries("series4"),
|
||||
testDataSeries("series5"),
|
||||
testDataSeries("series6"),
|
||||
testDataSeries("series7"),
|
||||
testDataSeries("series8"),
|
||||
testDataSeries("series9"),
|
||||
testDataSeries("series10"),
|
||||
testDataSeries("series11"),
|
||||
testDataSeries("series12"),
|
||||
testDataSeries("series13"),
|
||||
testDataSeries("series14"),
|
||||
testDataSeries("series15"),
|
||||
testDataSeries("series16"),
|
||||
testDataSeries("series17"),
|
||||
testDataSeries("series18"),
|
||||
testDataSeries("series19"),
|
||||
testDataSeries("series20"),
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function testDataSeries(name: string): TimeseriesType {
|
||||
const data = {
|
||||
name: name,
|
||||
values: getValues()
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
function getValues(): TimepointType[] {
|
||||
const data: TimepointType[] = [];
|
||||
const count = 150;
|
||||
let val = Math.random() * 50 + 25;
|
||||
for (let i = 0; i < count; i++) {
|
||||
let rand = (Math.random() * 4 - 2);
|
||||
if (Math.abs(rand) < .15) {
|
||||
rand = (Math.random() * 10 - 5);
|
||||
}
|
||||
data.push({
|
||||
start: i,
|
||||
end: i + 1,
|
||||
value: val += rand
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
46
src/visualization/timeseries.ts
Normal file
46
src/visualization/timeseries.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {TransformNode, Vector3} from "@babylonjs/core";
|
||||
import {TimeseriesType} from "./timeseriesType";
|
||||
import {buildContainer} from "./functions/buildContainer";
|
||||
import {ChartDataType} from "./chartDataType";
|
||||
import {buildSeries} from "./functions/buildSeries";
|
||||
|
||||
export class Timeseries {
|
||||
private parent: TransformNode;
|
||||
private data: ChartDataType;
|
||||
private size: Vector3 = new Vector3(1, 1, 1);
|
||||
|
||||
constructor(parent: TransformNode) {
|
||||
this.parent = parent;
|
||||
this.parent.onDisposeObservable.add(() => {
|
||||
this.parent = null;
|
||||
});
|
||||
}
|
||||
|
||||
public setData(data: ChartDataType) {
|
||||
this.data = data;
|
||||
if (this.parent) {
|
||||
this.build();
|
||||
}
|
||||
}
|
||||
|
||||
public fetchData(url: string) {
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
this.setData((data as ChartDataType));
|
||||
});
|
||||
}
|
||||
|
||||
build() {
|
||||
const scene = this.parent.getScene();
|
||||
if (!scene || !this.data) {
|
||||
return;
|
||||
}
|
||||
buildContainer(this.parent, this.size);
|
||||
const seriesCount = this.data.series.length;
|
||||
this.data.series.forEach((series: TimeseriesType, index: number) => {
|
||||
buildSeries(this.parent, series, index, seriesCount, .01);
|
||||
});
|
||||
this.parent.scaling = new Vector3(5, 5, 5);
|
||||
}
|
||||
}
|
||||
10
src/visualization/timeseriesType.ts
Normal file
10
src/visualization/timeseriesType.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export type TimeseriesType = {
|
||||
name: string;
|
||||
values: TimepointType[];
|
||||
}
|
||||
export type TimepointType = {
|
||||
id?: string,
|
||||
start?: string | number;
|
||||
end: string | number;
|
||||
value: number;
|
||||
}
|
||||
21
src/vrApp.ts
21
src/vrApp.ts
@ -13,6 +13,7 @@ import {addSceneInspector} from "./util/functions/sceneInspctor";
|
||||
import {groundMeshObserver} from "./util/functions/groundMeshObserver";
|
||||
import {MainMenu} from "./menus/mainMenu";
|
||||
import {Introduction} from "./tutorial/introduction";
|
||||
import {buildQuestLink} from "./util/functions/buildQuestLink";
|
||||
|
||||
export class VrApp {
|
||||
private scene: Scene;
|
||||
@ -63,11 +64,12 @@ export class VrApp {
|
||||
|
||||
await db.initialize();
|
||||
|
||||
const environment = new CustomEnvironment(scene, "default", config);
|
||||
|
||||
const camera: FreeCamera = new FreeCamera("Main Camera",
|
||||
new Vector3(0, 1.6, 0), scene);
|
||||
//camera.setTarget(new Vector3(0, 1.6, -3));
|
||||
scene.setActiveCameraByName("Main Camera");
|
||||
const environment = new CustomEnvironment(scene, "default", config);
|
||||
environment.groundMeshObservable.add((ground) => {
|
||||
groundMeshObserver(ground, scene, diagramManager, controllers, spinner);
|
||||
}, -1, false, this);
|
||||
@ -103,13 +105,24 @@ export class VrApp {
|
||||
*/
|
||||
addSceneInspector(scene);
|
||||
const mainMenu = new MainMenu(scene);
|
||||
//const zero = MeshBuilder.CreateSphere('target', {diameter: 1.6}, scene);
|
||||
// zero.position = new Vector3(0, .8, 0);
|
||||
|
||||
/*
|
||||
const base = new TransformNode("chart");
|
||||
base.position.y = .25;
|
||||
base.position.z = -5;
|
||||
const chart = new Timeseries(base);
|
||||
chart.setData(genData());
|
||||
*/
|
||||
//const newRelic = new NewRelicQuery(scene);
|
||||
//newRelic.getSales();
|
||||
this.logger.info('keydown event listener added, use Ctrl+Shift+Alt+I to toggle debug layer');
|
||||
let i = 0;
|
||||
this.engine.runRenderLoop(() => {
|
||||
this.scene.render();
|
||||
if (i++ % 60 == 0) {
|
||||
// console.log(this.engine.getFps());
|
||||
}
|
||||
|
||||
});
|
||||
this.logger.info('Render loop started');
|
||||
|
||||
@ -123,7 +136,7 @@ export class VrApp {
|
||||
const vrApp = new VrApp();
|
||||
const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement);
|
||||
vrApp.initialize(canvas).then(() => {
|
||||
|
||||
buildQuestLink();
|
||||
});
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user