diff --git a/public/styles.css b/public/styles.css index c67a8ff..d7a8c8a 100644 --- a/public/styles.css +++ b/public/styles.css @@ -156,7 +156,7 @@ h1 { #tutorial { z-index: 15; left: 100px; - top: 560px; + top: 750px; width: 160px; height: 210px; } diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 943a006..3f9fd32 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -1,5 +1,5 @@ import {AbstractMesh, ActionManager, InstancedMesh, Mesh, Observable, Scene, TransformNode} from "@babylonjs/core"; -import {DiagramEvent, DiagramEventType} from "./types/diagramEntity"; +import {DiagramEntity, DiagramEvent, DiagramEventType} from "./types/diagramEntity"; import log from "loglevel"; import {Controllers} from "../controllers/controllers"; import {AppConfig} from "../util/appConfig"; @@ -43,6 +43,27 @@ export class DiagramManager { this.cleanupOrphanConnections(mesh) } }); + document.addEventListener('uploadImage', (event: CustomEvent) => { + const diagramEntity: DiagramEntity = { + template: '#image-template', + image: event.detail.data, + text: event.detail.name, + position: {x: 0, y: 1.6, z: 0}, + rotation: {x: 0, y: Math.PI, z: 0}, + scale: {x: 1, y: 1, z: 1}, + } + + console.log(diagramEntity); + //const newMesh = buildMeshFromDiagramEntity(diagramEntity, this._scene); + if (this.onDiagramEventObservable) { + this.onDiagramEventObservable.notifyObservers({ + type: DiagramEventType.ADD, + entity: diagramEntity + }, DiagramEventObserverMask.ALL); + + //this.onDiagramEventObservable.notifyObservers({type: DiagramEventType.ADD, entity: diagramEntity}, DiagramEventObserverMask.FROM_DB); + } + }) } public get diagramMenuManager(): DiagramMenuManager { diff --git a/src/diagram/functions/buildImage.ts b/src/diagram/functions/buildImage.ts new file mode 100644 index 0000000..fb7ec5c --- /dev/null +++ b/src/diagram/functions/buildImage.ts @@ -0,0 +1,15 @@ +import log from "loglevel"; +import {AbstractMesh, MeshBuilder, StandardMaterial, Texture} from "@babylonjs/core"; +import {DefaultScene} from "../../defaultScene"; +import {DiagramEntity} from "../types/diagramEntity"; + +export function buildImage(entity: DiagramEntity): AbstractMesh { + const logger = log.getLogger('buildImage'); + logger.debug("buildImage: entity is image"); + const scene = DefaultScene.Scene; + const plane = MeshBuilder.CreatePlane("plane", {size: 1}, scene); + const material = new StandardMaterial("planeMaterial", scene); + const texture = new Texture(entity.image, scene); + material.emissiveTexture = texture; + return plane; +} \ No newline at end of file diff --git a/src/diagram/functions/buildMeshFromDiagramEntity.ts b/src/diagram/functions/buildMeshFromDiagramEntity.ts index 8958835..7a5a983 100644 --- a/src/diagram/functions/buildMeshFromDiagramEntity.ts +++ b/src/diagram/functions/buildMeshFromDiagramEntity.ts @@ -1,12 +1,21 @@ -import {DiagramEntity, DiagramEntityType} from "../types/diagramEntity"; -import {AbstractMesh, InstancedMesh, Mesh, Quaternion, Scene, Vector3} from "@babylonjs/core"; +import {DiagramEntity, DiagramEntityType, DiagramTemplates} from "../types/diagramEntity"; +import { + AbstractMesh, + InstancedMesh, + Mesh, + MeshBuilder, + Quaternion, + Scene, + StandardMaterial, + Texture, + Vector3 +} from "@babylonjs/core"; import {DiagramConnection} from "../diagramConnection"; import {updateTextNode} from "../../util/functions/updateTextNode"; import log from "loglevel"; import {v4 as uuidv4} from 'uuid'; import {buildStandardMaterial} from "../../materials/functions/buildStandardMaterial"; - export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene): AbstractMesh { const logger = log.getLogger('buildMeshFromDiagramEntity'); if (!entity) { @@ -33,25 +42,58 @@ function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): Abst logger.debug(`mesh ${oldMesh.id} already exists`); newMesh = oldMesh; } else { - if (entity.template == "#connection-template") { - const connection: DiagramConnection = new DiagramConnection(entity.from, entity.to, entity.id, scene); - - logger.debug(`connection.mesh = ${connection.mesh.id}`); - newMesh = connection.mesh; - } else { - const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); - if (toolMesh && !oldMesh) { - newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh)); - newMesh.metadata = {template: entity.template, exportable: true, tool: false}; - } else { + switch (entity.template) { + case DiagramTemplates.CONNECTION: + const connection: DiagramConnection = new DiagramConnection(entity.from, entity.to, entity.id, scene); + logger.debug(`connection.mesh = ${connection.mesh.id}`); + newMesh = connection.mesh; + break; + case DiagramTemplates.USER: + break; + case DiagramTemplates.IMAGE: + newMesh = buildImage(entity, scene); + newMesh.metadata = {template: entity.template, exportable: true, tool: false} + break; + case DiagramTemplates.BOX: + case DiagramTemplates.SPHERE: + case DiagramTemplates.CYLINDER: + case DiagramTemplates.CONE: + case DiagramTemplates.PLANE: + const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color); + if (toolMesh && !oldMesh) { + newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh)); + newMesh.metadata = {template: entity.template, exportable: true, tool: false}; + } else { + logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color); + } + break; + default: logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color); + break; - } } } return newMesh; } +function buildImage(entity: DiagramEntity, scene: Scene): AbstractMesh { + const logger = log.getLogger('buildImage'); + logger.debug("buildImage: entity is image"); + const plane = MeshBuilder.CreatePlane(entity.id, {size: 1}, scene); + const material = new StandardMaterial("planeMaterial", scene); + const image = new Image(); + image.src = entity.image; + const texture = new Texture(entity.image, scene); + material.emissiveTexture = texture; + material.backFaceCulling = false; + material.disableLighting = true; + plane.material = material; + image.decode().then(() => { + plane.scaling.x = image.width / image.height; + }); + return plane; +} + function generateId(entity: DiagramEntity) { if (!entity.id) { entity.id = "id" + uuidv4(); @@ -104,6 +146,7 @@ function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene) } return newMesh; } + function xyztovec(xyz: { x, y, z }): Vector3 { return new Vector3(xyz.x, xyz.y, xyz.z); } \ No newline at end of file diff --git a/src/diagram/types/diagramEntity.ts b/src/diagram/types/diagramEntity.ts index 5fc4e10..e521949 100644 --- a/src/diagram/types/diagramEntity.ts +++ b/src/diagram/types/diagramEntity.ts @@ -21,6 +21,16 @@ export enum DiagramEventMask { REMOTE = 2, } +export enum DiagramTemplates { + CONNECTION = "#connection-template", + USER = "#user-template", + BOX = "#box-template", + SPHERE = "#sphere-template", + CYLINDER = "#cylinder-template", + CONE = "#cone-template", + IMAGE = "#image-template", + PLANE = "#plane-template", +} export type DiagramEvent = { type: DiagramEventType; @@ -34,6 +44,7 @@ export type DiagramEntity = { id?: string; from?: string; to?: string; + image?: string; last_seen?: Date; position?: { x: number, y: number, z: number }; rotation?: { x: number, y: number, z: number }; diff --git a/src/react/functions/uploadImage.ts b/src/react/functions/uploadImage.ts new file mode 100644 index 0000000..bc87f01 --- /dev/null +++ b/src/react/functions/uploadImage.ts @@ -0,0 +1,43 @@ +const uploadImage = async (evt) => { + + const file = (evt.target as HTMLInputElement).files[0]; + const formData = new FormData(); + formData.append('file', file); + //formData.append('requireSignedURLs', 'true'); + const formInitData = new FormData(); + formInitData.append('requireSignedURLs', 'true'); + + const initialUpload = await fetch('/api/images', { + method: 'POST', + body: formInitData + + + }); + try { + const initialData = await initialUpload.json(); + if (initialData.success == true) { + const upload = await fetch(initialData.result.uploadURL, { + method: 'POST', mode: 'cors', body: + formData + }); + const uploadData = await upload.json(); + console.log(uploadData) + for (let variant of uploadData.result.variants) { + if (variant.indexOf('fullhd') > -1) { + const uploadEvent = new CustomEvent('uploadImage', { + detail: { + name: file.name, + data: variant + } + }); + document.dispatchEvent(uploadEvent); + evt.target.remove(); + console.log(variant); + } + } + } + } catch (err) { + console.error(err); + } +} +export {uploadImage}; \ No newline at end of file diff --git a/src/react/webApp.tsx b/src/react/webApp.tsx index 57efbab..838dba5 100644 --- a/src/react/webApp.tsx +++ b/src/react/webApp.tsx @@ -1,4 +1,5 @@ import {useEffect, useState} from "react"; +import {uploadImage} from "./functions/uploadImage"; function MainMenu({onClick}) { return ( @@ -7,6 +8,7 @@ function MainMenu({onClick}) {