236 lines
9.1 KiB
TypeScript
236 lines
9.1 KiB
TypeScript
import log from "loglevel";
|
|
import {
|
|
AbstractMesh,
|
|
Axis,
|
|
Color3,
|
|
DynamicTexture,
|
|
MeshBuilder,
|
|
Scene,
|
|
Space,
|
|
StandardMaterial,
|
|
TransformNode,
|
|
Vector3
|
|
} from "@babylonjs/core";
|
|
import {DiagramManager} from "../diagram/diagramManager";
|
|
import {DiagramEventType} from "../diagram/diagramEntity";
|
|
|
|
export class DrawioManager {
|
|
private diagramManager: DiagramManager;
|
|
private readonly zdepth: Map<string, number> = new Map<string, number>();
|
|
private scene: Scene;
|
|
private readonly logger = log.getLogger('DrawioManager');
|
|
private minY = 0;
|
|
private minX = 0;
|
|
private maxX = 0;
|
|
private maxY = 0;
|
|
|
|
constructor(scene: Scene, diagramManager: DiagramManager) {
|
|
this.scene = scene;
|
|
this.diagramManager = diagramManager;
|
|
this.getGraph();
|
|
}
|
|
|
|
public static updateTextNode(mesh: AbstractMesh, text: string): AbstractMesh {
|
|
//Set font
|
|
const height = 0.1;
|
|
const font_size = 24;
|
|
const font = "bold " + font_size + "px Arial";
|
|
//Set height for dynamic texture
|
|
const DTHeight = 1.5 * font_size; //or set as wished
|
|
//Calc Ratio
|
|
const ratio = height / DTHeight;
|
|
|
|
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
|
|
const temp = new DynamicTexture("DynamicTexture", 32, mesh.getScene());
|
|
const tmpctx = temp.getContext();
|
|
tmpctx.font = font;
|
|
const DTWidth = tmpctx.measureText(text).width + 8;
|
|
|
|
//Calculate width the plane has to be
|
|
const planeWidth = DTWidth * ratio;
|
|
|
|
//Create dynamic texture and write the text
|
|
const dynamicTexture = new DynamicTexture("DynamicTexture", {
|
|
width: DTWidth,
|
|
height: DTHeight
|
|
}, mesh.getScene(), false);
|
|
const mat = new StandardMaterial("mat", mesh.getScene());
|
|
mat.diffuseTexture = dynamicTexture;
|
|
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
|
|
|
|
//Create plane and set dynamic texture as material
|
|
const plane = MeshBuilder.CreatePlane("text", {width: planeWidth, height: height}, mesh.getScene());
|
|
plane.material = mat;
|
|
//plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
|
|
plane.parent = mesh;
|
|
plane.position.z = .51;
|
|
plane.scaling.y = 1 / mesh.scaling.y;
|
|
plane.scaling.x = 1 / mesh.scaling.x;
|
|
plane.scaling.z = 1 / mesh.scaling.z;
|
|
plane.rotate(Axis.Z, Math.PI, Space.LOCAL);
|
|
plane.rotate(Axis.Y, Math.PI, Space.LOCAL);
|
|
//plane.rotate(Axis.X, Math.PI, Space.LOCAL);
|
|
|
|
return plane;
|
|
}
|
|
|
|
private async getGraph() {
|
|
this.logger.debug("starting to get graph");
|
|
const entities: Array<{ text: string, id: string, geometry: { x: number, y: number, width: number, height: number } }>
|
|
= new Array<{ text: string; id: string, geometry: { x: number; y: number; width: number; height: number } }>();
|
|
|
|
const graph = await fetch('/arch_demo.xml');
|
|
this.logger.debug('got graph');
|
|
const graphXml = await graph.text();
|
|
const doc = new DOMParser().parseFromString(graphXml, 'text/html');
|
|
//this.logger.debug(doc);
|
|
const firstDiagram = doc.querySelectorAll('diagram')[0];
|
|
const mxDiagram = firstDiagram.querySelector('mxGraphModel');
|
|
const width = mxDiagram.getAttribute('pageWidth');
|
|
const height = mxDiagram.getAttribute('pageHeight');
|
|
this.logger.debug('begin parse');
|
|
mxDiagram.querySelectorAll('mxCell').forEach((cell) => {
|
|
|
|
const value = cell.getAttribute('value');
|
|
if (!value) {
|
|
//this.logger.warn('no value for :' , cell);
|
|
} else {
|
|
const ent = new DOMParser().parseFromString(value, 'text/html');
|
|
const errorNode = ent.querySelector("parsererror");
|
|
this.logger.debug(value);
|
|
if (errorNode) {
|
|
//this.logger.debug(value);
|
|
} else {
|
|
const text = this.getText(ent, '');
|
|
const id = cell.getAttribute('id');
|
|
const parent = cell.getAttribute('parent');
|
|
if (this.zdepth.has(parent)) {
|
|
this.zdepth.set(id, this.zdepth.get(parent) + .2);
|
|
} else {
|
|
this.zdepth.set(cell.getAttribute('id'), 0);
|
|
}
|
|
const geo = cell.querySelector('mxGeometry');
|
|
const geometry = {
|
|
x: geo.getAttribute('x'),
|
|
y: geo.getAttribute('y'),
|
|
width: geo.getAttribute('width'),
|
|
height: geo.getAttribute('height'),
|
|
}
|
|
entities.push({text: text, id: id, geometry: this.fixMinMax(geometry)});
|
|
if (text) {
|
|
this.logger.debug('Text' + text);
|
|
this.logger.debug('Geometry' + JSON.stringify(geometry));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.logger.debug('done parsing');
|
|
|
|
this.logger.debug('MinX' + this.minX);
|
|
this.logger.debug('MinY' + this.minY);
|
|
this.logger.debug('MaxX' + this.maxX);
|
|
this.logger.debug('MaxY' + this.maxY);
|
|
const diagramWidth = this.maxX - this.minX;
|
|
const diagramHeight = this.maxY - this.minY;
|
|
let scale = 1;
|
|
if (diagramHeight > diagramWidth) {
|
|
scale = 20 / diagramHeight;
|
|
} else {
|
|
scale = 20 / diagramWidth;
|
|
}
|
|
const anchor = new TransformNode('anchor', this.scene);
|
|
|
|
if (entities.length > 0) {
|
|
//const basebox = MeshBuilder.CreateBox("box", {
|
|
// height: 1,
|
|
// width: 1, depth: 1
|
|
//}, this.scene);
|
|
|
|
//const material: StandardMaterial = new StandardMaterial("mat", this.scene);
|
|
//material.diffuseColor = new Color3(0.5, 0.5, 0.5);
|
|
//material.alpha = .4;
|
|
//basebox.material = material;
|
|
entities.forEach((entity) => {
|
|
/* const box = new InstancedMesh("box", basebox);
|
|
box.scaling.z = .1;
|
|
box.scaling.y = entity.geometry.height * scale;
|
|
box.scaling.x = entity.geometry.width * scale;
|
|
box.position.x = (entity.geometry.x - this.minX) * scale + (entity.geometry.width * scale / 2);
|
|
box.position.y = (entity.geometry.y - this.minY) * scale + (entity.geometry.height * scale / 2);
|
|
box.position.z = 2 + this.zdepth.get(entity.id); */
|
|
this.diagramManager.onDiagramEventObservable.notifyObservers(
|
|
{
|
|
type: DiagramEventType.ADD,
|
|
entity: {
|
|
text: entity.text,
|
|
id: entity.id,
|
|
position: new Vector3((entity.geometry.x - this.minX) * scale + (entity.geometry.width * scale / 2),
|
|
(entity.geometry.y - this.minY) * scale + (entity.geometry.height * scale / 2),
|
|
2 + this.zdepth.get(entity.id)),
|
|
scale: new Vector3(entity.geometry.width * scale, entity.geometry.height * scale, .1),
|
|
color: Color3.Blue().toHexString(),
|
|
template: '#box-template'
|
|
}
|
|
}
|
|
);
|
|
|
|
//box.metadata = {text: entity.text};
|
|
//box.setParent(anchor);
|
|
//DrawioManager.updateTextNode(box, entity.text);
|
|
});
|
|
anchor.position.y = 20;
|
|
anchor.rotation.x = Math.PI;
|
|
|
|
}
|
|
|
|
this.logger.debug('Scale' + scale);
|
|
|
|
|
|
}
|
|
|
|
private fixMinMax(geometry: { x: string; y: string; width: string; height: string; }):
|
|
{ x: number, y: number, width: number, height: number } {
|
|
let x = 0;
|
|
if (geometry.x) {
|
|
x = parseFloat(geometry.x);
|
|
if (x < this.minX) {
|
|
this.minX = x;
|
|
}
|
|
if (x > this.maxX) {
|
|
this.maxX = x;
|
|
}
|
|
}
|
|
let y = 0;
|
|
if (geometry.y) {
|
|
y = parseFloat(geometry.y);
|
|
if (y < this.minY) {
|
|
this.minY = y;
|
|
}
|
|
if (y > this.maxY) {
|
|
this.maxY = y;
|
|
}
|
|
}
|
|
return ({x: x, y: y, width: parseFloat(geometry.width), height: parseFloat(geometry.height)});
|
|
}
|
|
|
|
private getText(obj: Node, text: string): string {
|
|
if (obj.nodeType == Node.TEXT_NODE) {
|
|
if (obj.textContent) {
|
|
return obj.textContent.trim();
|
|
} else {
|
|
return '';
|
|
}
|
|
} else {
|
|
if (obj.childNodes) {
|
|
let t = '';
|
|
obj.childNodes.forEach((child) => {
|
|
t += ' ' + this.getText(child, '');
|
|
});
|
|
return text.trim() + ' ' + t.trim();
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
}
|
|
}
|