針對特定模型上色
This commit is contained in:
parent
5e0d6a6236
commit
41fac6cfbb
31
server.js
31
server.js
@ -1,39 +1,38 @@
|
|||||||
const express = require('express');
|
const express = require("express");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const { PORT } = require('./config.js');
|
const { PORT } = require("./config.js");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === "production") {
|
||||||
// 🔧 URL rewrite 必須在所有路由之前執行(對應 Vite dev proxy 的行為)
|
// 🔧 URL rewrite 必須在所有路由之前執行(對應 Vite dev proxy 的行為)
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (req.path.startsWith('/forge/api')) {
|
if (req.path.startsWith("/forge/api")) {
|
||||||
req.url = req.url.replace(/^\/forge\/api/, '/api');
|
req.url = req.url.replace(/^\/forge\/api/, "/api");
|
||||||
req.path = req.path.replace(/^\/forge\/api/, '/api');
|
req.path = req.path.replace(/^\/forge\/api/, "/api");
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
const distPath = path.join(__dirname, 'wwwroot/ibms_ems/dist');
|
const distPath = path.join(__dirname, "wwwroot/ibms_ems/dist");
|
||||||
app.use(express.static(distPath));
|
app.use(express.static(distPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 👉 API routes(在 rewrite 之後)
|
// 👉 API routes(在 rewrite 之後)
|
||||||
app.use(require('./routes/auth.js'));
|
app.use(require("./routes/auth.js"));
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === "production") {
|
||||||
// Vue Router history mode (Express 5 catch-all syntax)
|
// Vue Router history mode (Express 5 catch-all syntax)
|
||||||
app.get('/{*path}', (req, res) => {
|
app.get("/{*path}", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname, 'wwwroot/ibms_ems/dist', 'index.html'));
|
res.sendFile(path.join(__dirname, "wwwroot/ibms_ems/dist", "index.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 👉 Dev:只跑 API,不管前端
|
// 👉 Dev:只跑 API,不管前端
|
||||||
app.get('/', (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.send('API server running...');
|
res.send("API server running...");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`Server listening on port ${PORT}...`);
|
console.log(`Server listening on port ${PORT}...`);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import background from "@/assets/bg_tech.jpg";
|
|||||||
import titleLogo from "@/assets/title.png";
|
import titleLogo from "@/assets/title.png";
|
||||||
import ForgeViewer from "@/components/ForgeViewer.vue";
|
import ForgeViewer from "@/components/ForgeViewer.vue";
|
||||||
import Info from "@/components/Info.vue";
|
import Info from "@/components/Info.vue";
|
||||||
import { ref, useTemplateRef, watch } from "vue";
|
import { ref, useTemplateRef, watch, toRaw } from "vue";
|
||||||
import btn01B from "@/assets/btn_01_b.png";
|
import btn01B from "@/assets/btn_01_b.png";
|
||||||
import btn01R from "@/assets/btn_01_r.png";
|
import btn01R from "@/assets/btn_01_r.png";
|
||||||
import btn02B from "@/assets/btn_02_b.png";
|
import btn02B from "@/assets/btn_02_b.png";
|
||||||
@ -83,7 +83,15 @@ const left_data = ref([
|
|||||||
tag: "ROOM_Information_Data_Center",
|
tag: "ROOM_Information_Data_Center",
|
||||||
arcSide: 1,
|
arcSide: 1,
|
||||||
cameraDistance: 5,
|
cameraDistance: 5,
|
||||||
value: 1.22
|
value: 1.22,
|
||||||
|
content: [
|
||||||
|
"Total Facility Energy: 122,000 kWh",
|
||||||
|
"IT Equipment Energy: 100,000 kWh",
|
||||||
|
"Cooling Energy: 30,000 kWh",
|
||||||
|
"Power System Loss: 6,500 kWh",
|
||||||
|
"Lighting & Others: 3,500 kWh"
|
||||||
|
],
|
||||||
|
spaceColor: new THREE.Vector4(1, 0, 0, 1)
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -97,7 +105,16 @@ const right_data = ref([
|
|||||||
tag: "ROOM_OFFICE",
|
tag: "ROOM_OFFICE",
|
||||||
arcSide: 1,
|
arcSide: 1,
|
||||||
cameraDistance: 2,
|
cameraDistance: 2,
|
||||||
value: 92
|
value: 92,
|
||||||
|
content: [
|
||||||
|
"IAQ Score: 92 / 100",
|
||||||
|
"CO₂ Level: 680 ppm",
|
||||||
|
"PM2.5: 8 μg/m³",
|
||||||
|
"Temperature: 24.5 °C",
|
||||||
|
"Humidity: 52%",
|
||||||
|
"TVOC: 0.28 mg/m³"
|
||||||
|
],
|
||||||
|
spaceColor: new THREE.Vector4(1, 0, 0, 1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "REAL-TIME THROUGHPUT",
|
title: "REAL-TIME THROUGHPUT",
|
||||||
@ -108,7 +125,16 @@ const right_data = ref([
|
|||||||
tag: "TAG_Chiller-Water_Cooled",
|
tag: "TAG_Chiller-Water_Cooled",
|
||||||
arcSide: -1,
|
arcSide: -1,
|
||||||
cameraDistance: 10,
|
cameraDistance: 10,
|
||||||
value: 96
|
value: 96,
|
||||||
|
content: [
|
||||||
|
"Real-Time Throughput: 56%",
|
||||||
|
"Operating Chillers: 4 / 8 units",
|
||||||
|
"Active Cooling Capacity: 5,600 RT",
|
||||||
|
"Available Cooling Capacity: 10,000 RT",
|
||||||
|
"Standby Chillers: 3 units",
|
||||||
|
"Maintenance Chillers: 1 unit"
|
||||||
|
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "REAL-TIME",
|
title: "REAL-TIME",
|
||||||
@ -119,7 +145,15 @@ const right_data = ref([
|
|||||||
tag: "TAG_solder_paste_screen_printer",
|
tag: "TAG_solder_paste_screen_printer",
|
||||||
arcSide: -1,
|
arcSide: -1,
|
||||||
cameraDistance: 5,
|
cameraDistance: 5,
|
||||||
value: 98
|
value: 98,
|
||||||
|
content: [
|
||||||
|
"Predicted Yield (AI): 98.0%",
|
||||||
|
"Target Yield: 98.5%",
|
||||||
|
"Defect Risk: 1.2%",
|
||||||
|
"Quality Deviation: 0.5%",
|
||||||
|
"Key Impact Factor: Temperature Stability",
|
||||||
|
"AI Confidence: 94%"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "SAFETY & ALARMS",
|
title: "SAFETY & ALARMS",
|
||||||
@ -140,6 +174,7 @@ const forgeLabelRef = useTemplateRef("forgeLabelRef");
|
|||||||
|
|
||||||
const imgSrcActive = ref(null);
|
const imgSrcActive = ref(null);
|
||||||
const onClick = (item) => {
|
const onClick = (item) => {
|
||||||
|
const viewer = toRaw(forgeViewerRef.value.forgeViewer);
|
||||||
|
|
||||||
if (item.tag === "") return
|
if (item.tag === "") return
|
||||||
imgSrcActive.value = item;
|
imgSrcActive.value = item;
|
||||||
@ -147,11 +182,23 @@ const onClick = (item) => {
|
|||||||
//TODO: 切換視角
|
//TODO: 切換視角
|
||||||
console.log(forgeLabelRef.value);
|
console.log(forgeLabelRef.value);
|
||||||
changeCameraPosition(
|
changeCameraPosition(
|
||||||
forgeViewerRef.value.forgeViewer,
|
viewer,
|
||||||
forgeLabelRef.value.tagDom,
|
forgeLabelRef.value.tagDom,
|
||||||
item,
|
item,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
item.spaceColor &&
|
||||||
|
viewer.model.getInstanceTree().enumNodeChildren(
|
||||||
|
item.forgeID,
|
||||||
|
(dbId) => {
|
||||||
|
console.log("Found dbId:", dbId);
|
||||||
|
viewer.setThemingColor(dbId, new THREE.Vector4(1, 0, 0, 1)) // RGBA (紅色));
|
||||||
|
viewer.impl.invalidate(true);
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// console.log(forgeViewerRef.value.Viewpoints[7]);
|
// console.log(forgeViewerRef.value.Viewpoints[7]);
|
||||||
// changeCameraView(forgeViewerRef.value.forgeViewer, forgeViewerRef.value.Viewpoints[7].data);
|
// changeCameraView(forgeViewerRef.value.forgeViewer, forgeViewerRef.value.Viewpoints[7].data);
|
||||||
};
|
};
|
||||||
@ -184,6 +231,6 @@ watch(
|
|||||||
<ForgeViewer ref="forgeViewerRef" :forge-ids="forgeIDs" />
|
<ForgeViewer ref="forgeViewerRef" :forge-ids="forgeIDs" />
|
||||||
<Info position="right" :data="right_data" :imgSrcActive="imgSrcActive?.forgeID" :onClick="onClick" />
|
<Info position="right" :data="right_data" :imgSrcActive="imgSrcActive?.forgeID" :onClick="onClick" />
|
||||||
|
|
||||||
<ForgeLabel ref="forgeLabelRef" v-if="imgSrcActive" :data="imgSrcActive" />
|
<ForgeLabel ref="forgeLabelRef" v-show="imgSrcActive" :data="imgSrcActive" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="tag" ref="tag" class="hud-container z-50 hidden">
|
<div id="tag" ref="tag" class="hud-container hidden z-50 transform -translate-y-1/2">
|
||||||
<div class="hud-panel p-5 text-white text-3xl flex justify-center items-center">
|
<div class="hud-panel p-5 text-white text-3xl flex flex-col justify-center items-center">
|
||||||
<span class="tag_name mr-5">
|
<span class="tag_name mr-5">
|
||||||
{{ data.title }}
|
{{ data?.title || "" }}
|
||||||
</span>
|
</span>
|
||||||
<div class="flex flex-col items-start">
|
<div class="flex flex-col items-start">
|
||||||
<span v-for="(value, index) in data.content" :key="index" class="text-xl">
|
<span v-for="(value, index) in data?.content" :key="index" class="text-xl">
|
||||||
{{ value }}
|
{{ value || "" }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -143,7 +143,7 @@ watch(forgeViewerDOM, async (newVal) => {
|
|||||||
|
|
||||||
|
|
||||||
// 根據你的 UI(橢圓框大小)調整
|
// 根據你的 UI(橢圓框大小)調整
|
||||||
const scaleFactor = 1.3; // ⭐ 微調用
|
const scaleFactor = 1; // ⭐ 微調用
|
||||||
const direction = cameraPos.clone().sub(cameraTarget).normalize();
|
const direction = cameraPos.clone().sub(cameraTarget).normalize();
|
||||||
const distance = cameraPos.distanceTo(cameraTarget) * scaleFactor;
|
const distance = cameraPos.distanceTo(cameraTarget) * scaleFactor;
|
||||||
const newPos = cameraTarget.clone().add(direction.multiplyScalar(distance));
|
const newPos = cameraTarget.clone().add(direction.multiplyScalar(distance));
|
||||||
|
|||||||
@ -200,7 +200,11 @@ export default function changeCameraPosition(forgeViewer, label, item) {
|
|||||||
console.log("物件中心:", center);
|
console.log("物件中心:", center);
|
||||||
|
|
||||||
// ⭐ 計算最佳觀察點
|
// ⭐ 計算最佳觀察點
|
||||||
const { position, target } = computeCameraOffset(viewer, bbox, item.cameraDistance);
|
const { position, target } = computeCameraOffset(
|
||||||
|
viewer,
|
||||||
|
bbox,
|
||||||
|
item.cameraDistance,
|
||||||
|
);
|
||||||
|
|
||||||
// ⭐ 第 1 段:飛到物件
|
// ⭐ 第 1 段:飛到物件
|
||||||
const toTarget = {
|
const toTarget = {
|
||||||
@ -229,9 +233,17 @@ export default function changeCameraPosition(forgeViewer, label, item) {
|
|||||||
returnToOriginal: true,
|
returnToOriginal: true,
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
item,
|
item,
|
||||||
arcSide: -(item.arcSide),
|
arcSide: -item.arcSide,
|
||||||
};
|
};
|
||||||
flyArc(toOriginal);
|
flyArc(toOriginal);
|
||||||
|
viewer.model.getInstanceTree().enumNodeChildren(
|
||||||
|
item.forgeID,
|
||||||
|
(dbId) => {
|
||||||
|
viewer.setThemingColor(dbId, null); // RGBA (紅色));
|
||||||
|
viewer.impl.invalidate(true);
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
viewer.showAll(); // 恢復顯示所有物件
|
viewer.showAll(); // 恢復顯示所有物件
|
||||||
}, 8000);
|
}, 8000);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user