mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-11 03:35:11 +00:00
339 lines
9.6 KiB
Vue
339 lines
9.6 KiB
Vue
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for additional information regarding copyright ownership.
|
|
The ASF licenses this file to You 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. -->
|
|
<template>
|
|
<div class="infrastructure-box">
|
|
<div ref="mapRef" class="map"></div>
|
|
<div class="info-box" v-show="showInfo">
|
|
{{ NodeTypes[type] }} Information
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, onMounted } from "vue";
|
|
import * as THREE from "three";
|
|
import fac from "three-orbit-controls";
|
|
import { Line2 } from "three/examples/jsm/lines/Line2";
|
|
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
|
|
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial";
|
|
import { HexagonCreateParams } from "@/types/infrastructure";
|
|
import HexagonPillar from "./geometry/hexagon-pillar";
|
|
import { Layout } from "./geometry/hexagon-layout";
|
|
import { NodeTypes } from "./data";
|
|
|
|
const animateCallbacks: Array<() => void> = [];
|
|
const showInfo = ref<boolean>(false);
|
|
const objSelected = ref<any>(null);
|
|
const meshColors = ref([0xa1cffb, 0x333333, 0x333840, 0x999999]); //[0xa489b2, 0xf2bfd0, 0xf0eaea, 0xef6775, 0xfbc580];
|
|
const type = ref<number>(0);
|
|
const width = ref<number>(1920);
|
|
const height = ref<number>(900);
|
|
const mapRef = ref<HTMLDivElement | null>(null);
|
|
let lineObj: any;
|
|
let scene: any;
|
|
let camera: any;
|
|
let raycaster: any;
|
|
let pointer: any;
|
|
|
|
onMounted(() => {
|
|
if (mapRef.value) {
|
|
init(mapRef.value);
|
|
}
|
|
window.addEventListener("click", onMouseClick, false);
|
|
animate();
|
|
});
|
|
|
|
function init(dom: HTMLDivElement): void {
|
|
width.value = dom.offsetWidth;
|
|
height.value = dom.offsetHeight + 74;
|
|
camera = new THREE.PerspectiveCamera(
|
|
45,
|
|
width.value / height.value,
|
|
1,
|
|
10000
|
|
);
|
|
camera.position.set(0, 30, 140);
|
|
scene = new THREE.Scene();
|
|
let light = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
light.position.set(4, 10, 4);
|
|
|
|
scene.add(light);
|
|
scene.add(new THREE.AmbientLight(0x404040));
|
|
scene.background = new THREE.Color(0x333840);
|
|
const renderer = new THREE.WebGLRenderer({
|
|
antialias: true,
|
|
alpha: true,
|
|
});
|
|
renderer.setSize(width.value, height.value);
|
|
dom.appendChild(renderer.domElement);
|
|
|
|
const window = Window as any;
|
|
const OrbitControls = fac(THREE);
|
|
window.controls = new OrbitControls(camera, renderer.domElement);
|
|
window.controls.enableZoom = true;
|
|
window.controls.enablePan = true;
|
|
|
|
const helper = new THREE.GridHelper(10000, 40, 0x04002c, 0x04002c);
|
|
helper.position.y = -1000;
|
|
// this.scene.add(helper);
|
|
// const axis = new THREE.AxesHelper(15000);
|
|
// this.scene.add(axis);
|
|
// add mesh
|
|
createInfrastructure();
|
|
|
|
raycaster = new THREE.Raycaster();
|
|
pointer = new THREE.Vector2();
|
|
animateCallbacks.push(() => {
|
|
renderer.render(scene, camera);
|
|
});
|
|
}
|
|
|
|
function createInfrastructure() {
|
|
//layer mesh
|
|
const l = {
|
|
hexagonParam: [27, 0.04, 5, 0.04, 0],
|
|
count: 1,
|
|
radius: 28, // layout hexagons radius
|
|
};
|
|
const [originVectors] = createHexagonLine(l, [0, 0, 0], 0);
|
|
// group mesh
|
|
const g: HexagonCreateParams = {
|
|
hexagonParam: [8, 0.04, 5, 0.04, 0.01],
|
|
count: 1,
|
|
radius: 8.5,
|
|
};
|
|
const [gVecs] = createPillarMesh(g, originVectors, 1);
|
|
// service mesh
|
|
const s: HexagonCreateParams = {
|
|
hexagonParam: [2, 0.04, 5, 0.04, 0.2],
|
|
count: 1,
|
|
radius: 2.2,
|
|
};
|
|
const [sVecs] = createPillarMesh(s, gVecs, 2);
|
|
|
|
// instance mesh
|
|
const i: HexagonCreateParams = {
|
|
hexagonParam: [0.2, 0.04, 5, 0.04, 0.3],
|
|
count: 2,
|
|
radius: 0.3,
|
|
};
|
|
createPillarMesh(i, sVecs, 3);
|
|
}
|
|
|
|
function createHexagonLine(
|
|
p: HexagonCreateParams,
|
|
originVectors: number[],
|
|
type: number
|
|
) {
|
|
lineObj = new THREE.Object3D();
|
|
const centers: number[] = [];
|
|
const geo = HexagonPillar.getVertices(false, ...p.hexagonParam);
|
|
|
|
for (let i = 0; i < originVectors.length / 3; i++) {
|
|
const c = [originVectors[3 * i], originVectors[3 * i + 2]];
|
|
const [origins] = hexGrid(p.count, p.radius, c);
|
|
centers.push(...origins);
|
|
}
|
|
for (let c = 0; c < centers.length / 3; c++) {
|
|
const vertices = [];
|
|
for (let v = 0; v < geo.vertices.length; v++) {
|
|
vertices.push(
|
|
centers[3 * c] + geo.vertices[v].x,
|
|
centers[3 * c + 1] + geo.vertices[v].y,
|
|
centers[3 * c + 2] + geo.vertices[v].z
|
|
);
|
|
}
|
|
const geometry = new LineGeometry().setPositions(vertices);
|
|
geometry.setAttribute(
|
|
"id",
|
|
new THREE.BufferAttribute(new Int8Array([type]), 1)
|
|
);
|
|
const material = new LineMaterial({
|
|
color: meshColors.value[type],
|
|
linewidth: 1,
|
|
// opacity: 0.2,
|
|
dashed: false,
|
|
});
|
|
material.resolution.set(width.value, height.value);
|
|
const line = new Line2(geometry, material);
|
|
line.computeLineDistances();
|
|
lineObj.add(line);
|
|
}
|
|
scene.add(lineObj);
|
|
return [centers];
|
|
}
|
|
|
|
function createPillarMesh(
|
|
p: HexagonCreateParams,
|
|
originVectors: number[],
|
|
type: number
|
|
) {
|
|
const centers: number[] = [];
|
|
const geo = HexagonPillar.get(false, ...p.hexagonParam);
|
|
|
|
for (let i = 0; i < originVectors.length / 3; i++) {
|
|
const c = [originVectors[3 * i], originVectors[3 * i + 2]];
|
|
const [origins] = hexGrid(p.count, p.radius, c);
|
|
centers.push(...origins);
|
|
}
|
|
const hMat = new THREE.MeshStandardMaterial({
|
|
side: THREE.DoubleSide,
|
|
transparent: true,
|
|
});
|
|
const geometry = new THREE.BufferGeometry();
|
|
geometry.setIndex(geo.indices);
|
|
geometry.setAttribute(
|
|
"position",
|
|
new THREE.BufferAttribute(new Float32Array(geo.vertices), 3)
|
|
);
|
|
geometry.setAttribute(
|
|
"normal",
|
|
new THREE.BufferAttribute(new Float32Array(geo.normals), 3)
|
|
);
|
|
geometry.setAttribute(
|
|
"type",
|
|
new THREE.BufferAttribute(new Int8Array([type]), 1)
|
|
);
|
|
const mesh = new THREE.InstancedMesh(geometry, hMat, centers.length / 3);
|
|
for (let c = 0; c < centers.length / 3; c++) {
|
|
const matrix = new THREE.Matrix4();
|
|
const color = new THREE.Color();
|
|
color.setHex(meshColors.value[type]);
|
|
for (let j = 0; j < geo.vertices.length / 3; j++) {
|
|
matrix.setPosition(
|
|
geo.vertices[3 * j] + centers[3 * c],
|
|
geo.vertices[3 * j + 1] + centers[3 * c + 1],
|
|
geo.vertices[3 * j + 2] + centers[3 * c + 2]
|
|
);
|
|
}
|
|
mesh.setMatrixAt(c, matrix);
|
|
mesh.setColorAt(c, color);
|
|
}
|
|
mesh.instanceMatrix.needsUpdate = true;
|
|
scene.add(mesh);
|
|
|
|
return [centers];
|
|
}
|
|
|
|
function hexGrid(n = 1, radius = 1, origin = [0, 0]) {
|
|
let x, y, yn, p;
|
|
const gLayout = new Layout(radius, origin);
|
|
// const coord = [];
|
|
const pos = [];
|
|
// x = -1; n = 1.5
|
|
for (x = -n; x <= n; x++) {
|
|
y = Math.max(-n, -x - n); // 0
|
|
yn = Math.min(n, -x + n); // 1
|
|
// y = 0 yn = 1
|
|
for (y; y <= yn; y++) {
|
|
p = gLayout.axialToPixel(x, y);
|
|
pos.push(p[0], 0, p[1]);
|
|
// coord.push(x, y);
|
|
}
|
|
}
|
|
return [pos];
|
|
}
|
|
|
|
function onMouseClick(event: MouseEvent) {
|
|
pointer.x = ((event.clientX - 210) / width.value) * 2 - 1;
|
|
pointer.y = -((event.clientY - 0) / height.value) * 2 + 1;
|
|
raycaster.setFromCamera(pointer, camera);
|
|
let meshes = scene.children.filter((d: any) => d instanceof THREE.Mesh);
|
|
meshes = [...meshes, ...lineObj.children];
|
|
const intersects = raycaster.intersectObjects(meshes, true);
|
|
const intersect = intersects[0];
|
|
|
|
if (objSelected.value) {
|
|
for (const m of meshes) {
|
|
if (m instanceof Line2) {
|
|
m.material.color.setHex(meshColors.value[0]);
|
|
m.material.needsUpdate = true;
|
|
} else {
|
|
const mType = m.geometry.getAttribute("type").array[0];
|
|
m.setColorAt(
|
|
objSelected.value.instanceId,
|
|
new THREE.Color(meshColors.value[mType])
|
|
);
|
|
m.instanceColor.needsUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
if (intersect) {
|
|
showInfo.value = true;
|
|
if (intersect.object instanceof Line2) {
|
|
intersect.object.material.color.setHex(0x73b3b9);
|
|
objSelected.value = intersect;
|
|
type.value = 0;
|
|
intersect.object.material.needsUpdate = true;
|
|
} else {
|
|
intersect.object.setColorAt(
|
|
intersect.instanceId,
|
|
new THREE.Color(0x73b3b9)
|
|
);
|
|
intersect.object.instanceColor.needsUpdate = true;
|
|
objSelected.value = intersect;
|
|
type.value =
|
|
objSelected.value.object.geometry.getAttribute("type").array[0];
|
|
}
|
|
} else {
|
|
if (objSelected.value) {
|
|
for (const m of meshes) {
|
|
if (m instanceof Line2) {
|
|
m.material.color.setHex(meshColors.value[0]);
|
|
m.material.needsUpdate = true;
|
|
} else {
|
|
const mType = m.geometry.getAttribute("type").array[0];
|
|
m.setColorAt(
|
|
objSelected.value.instanceId,
|
|
new THREE.Color(meshColors.value[mType])
|
|
);
|
|
m.instanceColor.needsUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
objSelected.value = null;
|
|
type.value = NaN;
|
|
showInfo.value = false;
|
|
}
|
|
}
|
|
|
|
function animate(): void {
|
|
animateCallbacks.forEach((fn) => fn());
|
|
requestAnimationFrame(animate);
|
|
}
|
|
</script>
|
|
<style scoped>
|
|
.info-box {
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 10px;
|
|
background: #fff;
|
|
color: #000;
|
|
height: 700px;
|
|
width: 300px;
|
|
border-radius: 3px;
|
|
padding: 10px;
|
|
}
|
|
.infrastructure-box {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.map {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
</style>
|