針對特定模型上色

This commit is contained in:
JouChun 2026-06-11 15:36:50 -04:00
parent 5e0d6a6236
commit 41fac6cfbb
5 changed files with 89 additions and 31 deletions

View File

@ -1,39 +1,38 @@
const express = require('express');
const path = require('path');
const { PORT } = require('./config.js');
const express = require("express");
const path = require("path");
const { PORT } = require("./config.js");
const app = express();
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === "production") {
// 🔧 URL rewrite 必須在所有路由之前執行(對應 Vite dev proxy 的行為)
app.use((req, res, next) => {
if (req.path.startsWith('/forge/api')) {
req.url = req.url.replace(/^\/forge\/api/, '/api');
req.path = req.path.replace(/^\/forge\/api/, '/api');
if (req.path.startsWith("/forge/api")) {
req.url = req.url.replace(/^\/forge\/api/, "/api");
req.path = req.path.replace(/^\/forge\/api/, "/api");
}
next();
});
const distPath = path.join(__dirname, 'wwwroot/ibms_ems/dist');
const distPath = path.join(__dirname, "wwwroot/ibms_ems/dist");
app.use(express.static(distPath));
}
// 👉 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)
app.get('/{*path}', (req, res) => {
res.sendFile(path.join(__dirname, 'wwwroot/ibms_ems/dist', 'index.html'));
app.get("/{*path}", (req, res) => {
res.sendFile(path.join(__dirname, "wwwroot/ibms_ems/dist", "index.html"));
});
} else {
// 👉 Dev只跑 API不管前端
app.get('/', (req, res) => {
res.send('API server running...');
app.get("/", (req, res) => {
res.send("API server running...");
});
}
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
});

View File

@ -3,7 +3,7 @@ import background from "@/assets/bg_tech.jpg";
import titleLogo from "@/assets/title.png";
import ForgeViewer from "@/components/ForgeViewer.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 btn01R from "@/assets/btn_01_r.png";
import btn02B from "@/assets/btn_02_b.png";
@ -83,7 +83,15 @@ const left_data = ref([
tag: "ROOM_Information_Data_Center",
arcSide: 1,
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",
arcSide: 1,
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",
@ -108,7 +125,16 @@ const right_data = ref([
tag: "TAG_Chiller-Water_Cooled",
arcSide: -1,
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",
@ -119,7 +145,15 @@ const right_data = ref([
tag: "TAG_solder_paste_screen_printer",
arcSide: -1,
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",
@ -140,6 +174,7 @@ const forgeLabelRef = useTemplateRef("forgeLabelRef");
const imgSrcActive = ref(null);
const onClick = (item) => {
const viewer = toRaw(forgeViewerRef.value.forgeViewer);
if (item.tag === "") return
imgSrcActive.value = item;
@ -147,11 +182,23 @@ const onClick = (item) => {
//TODO:
console.log(forgeLabelRef.value);
changeCameraPosition(
forgeViewerRef.value.forgeViewer,
viewer,
forgeLabelRef.value.tagDom,
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]);
// changeCameraView(forgeViewerRef.value.forgeViewer, forgeViewerRef.value.Viewpoints[7].data);
};
@ -184,6 +231,6 @@ watch(
<ForgeViewer ref="forgeViewerRef" :forge-ids="forgeIDs" />
<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>
</template>

View File

@ -1,12 +1,12 @@
<template>
<div id="tag" ref="tag" class="hud-container z-50 hidden">
<div class="hud-panel p-5 text-white text-3xl flex justify-center items-center">
<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 flex-col justify-center items-center">
<span class="tag_name mr-5">
{{ data.title }}
{{ data?.title || "" }}
</span>
<div class="flex flex-col items-start">
<span v-for="(value, index) in data.content" :key="index" class="text-xl">
{{ value }}
<span v-for="(value, index) in data?.content" :key="index" class="text-xl">
{{ value || "" }}
</span>
</div>
</div>

View File

@ -143,7 +143,7 @@ watch(forgeViewerDOM, async (newVal) => {
// UI調
const scaleFactor = 1.3; // 調
const scaleFactor = 1; // 調
const direction = cameraPos.clone().sub(cameraTarget).normalize();
const distance = cameraPos.distanceTo(cameraTarget) * scaleFactor;
const newPos = cameraTarget.clone().add(direction.multiplyScalar(distance));

View File

@ -200,7 +200,11 @@ export default function changeCameraPosition(forgeViewer, label, item) {
console.log("物件中心:", center);
// ⭐ 計算最佳觀察點
const { position, target } = computeCameraOffset(viewer, bbox, item.cameraDistance);
const { position, target } = computeCameraOffset(
viewer,
bbox,
item.cameraDistance,
);
// ⭐ 第 1 段:飛到物件
const toTarget = {
@ -229,9 +233,17 @@ export default function changeCameraPosition(forgeViewer, label, item) {
returnToOriginal: true,
duration: 5000,
item,
arcSide: -(item.arcSide),
arcSide: -item.arcSide,
};
flyArc(toOriginal);
viewer.model.getInstanceTree().enumNodeChildren(
item.forgeID,
(dbId) => {
viewer.setThemingColor(dbId, null); // RGBA (紅色));
viewer.impl.invalidate(true);
},
true,
);
viewer.showAll(); // 恢復顯示所有物件
}, 8000);
}