ibms-MCUT/Frontend/js/forge/forgemodel.js

1430 lines
42 KiB
JavaScript
Raw Normal View History

let fragProxy;
2022-11-24 00:20:37 +08:00
var targetFloorZ;
var elevatorSpeed;
var selector = "#forgeViewer";
var myDataList;
var lightDataList;
var lightList = []; //燈光清單
var levels; //剖面用
var lowerIdx = 0; //剖面的下方樓層
var upperIdx = 0; //剖面的上方樓層
2022-11-18 21:27:54 +08:00
function getAllLeafComponents(viewer, callback) {
var cbCount = 0;
var tree;
var jsData = [];
if(typeof viewer == 'undefined'){
return;
}
function getLeafComponentsRec(current, parent) {
cbCount++;
if (tree.getChildCount(current) != 0) {
tree.enumNodeChildren(
current,
function (children) {
getLeafComponentsRec(children, current);
},
false
);
}
var nodeName = viewer.model.getInstanceTree().getNodeName(current);
jsData.push({ id: current, parent: parent, text: nodeName });
if (--cbCount == 0) callback(jsData);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var rootId = tree.getRootId();
var nodeName = viewer.model.getInstanceTree().getNodeName(rootId);
jsData.push({ id: rootId, parent: "#", text: nodeName });
var allLeafComponents = getLeafComponentsRec(rootId, "#");
});
2022-11-18 21:27:54 +08:00
}
class elevator3D {
constructor(option = {}) {
this.ele = option.element;
viewer = option.viewer;
this.nodeId = option.nodeId;
this.speed = option.speed ?? 0.07;
this.tagValue = option.tagValue ?? "";
this.fragProxys = [];
this.fragProxy = null;
this.initCallback = option.inited ?? null;
this.movStatus = 0;
this.targetFloorZ = 0;
this.floorHeight = option.floorHeight ?? [{}];
this.sensorObjs = option.sensorObjs ?? null;
this.init();
}
// 定義遍歷模型中所有片段的異步函數
enumFragments = function (nodeId) {
return new Promise((resolve, reject) => {
// 定義結果陣列
let result = [];
// 使用 InstanceTree.enumNodeFragments 遍歷模型中的所有片段
viewer?.model?.getData().instanceTree.enumNodeFragments(
nodeId,
(fragId) => {
// 將遍歷到的片段 ID 添加到結果陣列中
result.push(fragId);
},
(nodeId) => {
// 如果遍歷到的節點是葉節點,则返回 true表示繼續遍歷該節點的片段
return model.getData().instanceTree.isLeaf(nodeId);
}
);
// 将结果陣列作為参数调用 resolve 函数
resolve(result);
});
};
// 設置樹狀結構中片段
setTreeFrag = async function (callback) {
let tree = viewer?.model?.getData().instanceTree;
if (!tree) {
return;
}
let nodeId = this.nodeId;
if (nodeId) {
// 刪除 fragmentProxys 中與當前 nodeId 相同的項目
this.fragProxys
.filter((x) => x.nodeId == nodeId)
.forEach((x) => {
let idx = this.fragProxys.indexOf(x);
this.fragProxys.splice(idx, 1);
});
// 遍歷當前 nodeId 中的所有片段
let fragments = await this.enumFragments(nodeId);
2022-11-24 00:20:37 +08:00
fragments.forEach((frag) => {
// 取得當前片段的 FragmentProxy 物件
let fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
// 將當前片段的信息添加到 fragProxys 陣列中
this.fragProxys.push({ nodeId: nodeId, fragId: frag, frag: fragProxy });
// 取得當前片段的動畫變換矩陣
fragProxy.getAnimTransform();
// 定義當前片段的位置向量
let fragPosition = new THREE.Vector3(0, 0, 0); // 一樓0 二樓15 三樓 26
// 設置當前片段的位置
fragProxy.position = fragPosition;
2022-11-24 00:20:37 +08:00
// 更新當前片段的動畫變換矩陣
fragProxy.updateAnimTransform();
});
}
// 通知檢視器更新場景
viewer.impl.sceneUpdated(true);
if (!$(this.ele)[0]) {
callback ? callback() : "";
return;
}
if (typeof $(this.ele)[0]._elevator3D == "undefined") {
$(this.ele)[0]._elevator3D = [];
}
$(this.ele)[0]._elevator3D.push({ nodeId: nodeId, obj: this });
this.initCallback ? this.initCallback() : "";
callback ? callback() : "";
};
init = function (callback = null) {
this.setTreeFrag(callback);
};
setElevatorFloor = function (floor) {
this.targetFloorZ =
this.floorHeight.filter((x) => x.floor == floor)[0]?.height ?? 0;
};
setElevatorSpeed = function (speed) {
//0.01 ~ 1
this.speed = speed;
};
// 電梯移動
movElevator = async function () {
let nodeId = this.nodeId;
let fragProxyZ = 0; // 定義 fragProxyZ 變量,用於存儲當前片段的 z 坐標
// 取得當前 nodeId 的片段代理對象
let fragProxy = this.fragProxys.filter((x) => x.nodeId == nodeId)[0]?.frag;
if (!fragProxy) {
return;
}
changeColor(nodeId);
if (
(this.movStatus == 2 &&
fragProxy.position.z.roundDecimal(2) <
this.targetFloorZ.roundDecimal(2)) ||
(this.movStatus == 1 &&
fragProxy.position.z.roundDecimal(2) >
this.targetFloorZ.roundDecimal(2)) ||
fragProxy.position.z.roundDecimal(2) == this.targetFloorZ.roundDecimal(2)
) {
stoped(this);
}
if (
fragProxy.position.z.roundDecimal(2) > this.targetFloorZ.roundDecimal(2)
) {
this.movStatus = 2;
} else if (
fragProxy.position.z.roundDecimal(2) < this.targetFloorZ.roundDecimal(2)
) {
this.movStatus = 1;
}
// 遍歷當前 nodeId 中的所有片段
let fragments = await this.enumFragments(nodeId);
fragments.forEach((frag) => {
let fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
fragProxy.getAnimTransform();
if (this.movStatus == 2) {
fragProxy.position.z -= this.speed;
} else if (this.movStatus == 1) {
fragProxy.position.z += this.speed;
}
let tarFrag = this.fragProxys.filter(
(x) => x.nodeId == nodeId && x.fragId == frag
)[0];
if (tarFrag) {
tarFrag.frag.position.z = fragProxy.position.z;
}
fragProxyZ = fragProxy.position.z;
// 改變感測器熱點位置
this.sensorObjs && this.sensorObjs[0]?.changePos(nodeId + 2, fragProxyZ);
// if(nodeId === 15200) {
// }
fragProxy.updateAnimTransform();
});
viewer.impl.sceneUpdated(true);
let movElevator = $(this.ele)[0]
._elevator3D.filter((x) => x.nodeId == this.nodeId)[0]
?.obj.movElevator.bind(this);
if (this.movStatus == 2) {
if (fragProxyZ >= this.targetFloorZ) {
requestAnimationFrame(() => {
movElevator();
});
} else {
stoped(this);
}
} else if (this.movStatus == 1) {
if (fragProxyZ <= this.targetFloorZ) {
requestAnimationFrame(() => {
movElevator();
});
} else {
stoped(this);
}
} else {
stoped(this);
}
function stoped(obj) {
obj.movStatus = 0;
hideColor(nodeId);
return;
}
};
2022-11-24 00:20:37 +08:00
}
// 輔助函數,使用 Promise 封裝 viewer.getProperties 函數
function viewerGetProperties(dbIds, attributeName = null) {
// 在這裡,我們使用 viewer.getProperties 函數的成功回調函數作為 resolve 函數,
// 並使用 viewer.getProperties 函數的失敗回調函數作為 resolve 函數的參數
// 這樣,當 viewer.getProperties 函數成功時Promise 會傳回 properties 物件;
// 當 viewer.getProperties 函數失敗時Promise 會傳回 null。
return new Promise((resolve, reject) => {
let option = {};
if (attributeName != null) {
option.propFilter = [attributeName]; // 限制只返回指定的屬性
}
viewer.model.getBulkProperties2(
dbIds,
option, // 取得指定元素的屬性信息
function (elements) {
resolve(elements); // 成功時傳回 elements 物件
}
);
});
}
// 輔助函數,使用 Promise 封裝 viewer.search 函數
async function getNodeIdBySearch(text) {
return new Promise((resolve, reject) => {
viewer.search(
text,
(e) => {
// 在 3D 模型中搜索具有指定文本的元素
resolve(e); // 成功時傳回搜索結果
},
(e) => {
resolve(null); // 失敗時傳回 null
}
);
});
}
// 主函數 - 透過 model 全部 node 取得特定 nodeId
async function getNodeIdByDbIds(checkValue = [], callback = null) {
let evelMap = new Map();
let hasElement = false; // 設置是否有 【tag_id】的node
let targetNodeIds = await getNodeIdBySearch(checkValue);
let elements = await viewerGetProperties(targetNodeIds, "【tag_id】");
// 從 elements 中篩選出包含 【tag_id】 屬性的元素
elements = elements.filter(
(x) =>
x.properties.findIndex(
(y) =>
y.displayName == "【tag_id】" &&
checkValue.indexOf(y.displayValue) != -1
) != -1
);
if (elements) {
hasElement = true;
}
for (var e of elements) {
let name = e.properties[0].displayValue; // 獲取元素的 【tag_id】 屬性
let chiElements = null;
// 獲取 node 元素的子元素信息
targetNodeIds = await viewerGetProperties([e.dbId], "child");
// 二次篩選
chiElements = targetNodeIds.filter(
(x) => x.properties.findIndex((y) => y.displayName == "child") != -1
);
// 獲取子元素的 nodeId
targetNodeIds = chiElements.map((x) => x.properties[0].displayValue);
// 獲取子元素的屬性信息
chiElements = await viewerGetProperties(targetNodeIds);
// 將 node 元素的 【tag_id】 屬性和子元素的 nodeId 加入 Map 中
evelMap.set(name, chiElements[0]?.dbId);
}
if (!hasElement) {
callback ? callback([]) : "";
return [];
}
callback ? callback(evelMap) : "";
return evelMap;
}
2022-11-24 00:20:37 +08:00
function setElevatorFloor(floor) {
if (floor == 0) targetFloorZ = 0;
else if (floor == 1) targetFloorZ = 15;
else if (floor == 2) targetFloorZ = 26;
2022-11-24 00:20:37 +08:00
}
function setElevatorSpeed(speed) {
//0.01 ~ 1
elevatorSpeed = speed;
2022-11-24 00:20:37 +08:00
}
function movElevator() {
let tree = viewer.model?.getData().instanceTree;
let nodeId = 10952;
let fragProxyZ = 0;
var movStatus = 0; // 0=no 1=up 2=down
if (fragProxy.position.z > targetFloorZ) {
movStatus = 2;
} else if (fragProxy.position.z < targetFloorZ) {
movStatus = 1;
}
if (movStatus == 0) {
return;
}
tree.enumNodeFragments(nodeId, function (frag) {
fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
fragProxy.getAnimTransform();
//let fragPosition = new THREE.Vector3(0, 0, 15);// 一樓0 二樓15 三樓 26
if (movStatus == 2) {
fragProxy.position.z -= elevatorSpeed;
} else if (movStatus == 1) {
fragProxy.position.z += elevatorSpeed;
2022-11-24 00:20:37 +08:00
}
fragProxyZ = fragProxy.position.z;
fragProxy.updateAnimTransform();
});
viewer.impl.sceneUpdated(true);
2022-11-24 00:20:37 +08:00
if (movStatus == 2) {
if (fragProxyZ >= targetFloorZ) {
requestAnimationFrame(movElevator);
}
} else if (movStatus == 1) {
if (fragProxyZ <= targetFloorZ) {
requestAnimationFrame(movElevator);
2022-11-24 00:20:37 +08:00
}
}
//let fragPosition = new THREE.Vector3(position);// 一樓0 二樓15 三樓 26
//fragProxy.position = fragPosition;
//fragProxy.updateAnimTransform();
//viewer.impl.sceneUpdated(true);
2022-11-24 00:20:37 +08:00
}
function getAllDbIds(viewer) {
var instanceTree = viewer.model?.getData().instanceTree;
2022-11-18 21:27:54 +08:00
var allDbIdsStr = Object.keys(instanceTree.nodeAccess.dbIdToIndex);
2022-11-18 21:27:54 +08:00
return allDbIdsStr.map(function (id) {
return parseInt(id);
});
2022-11-18 21:27:54 +08:00
}
//------------------ 熱圖 -------------------------------
class ADHeatMaps {
constructor(option = {}) {
this.devices = option.devices ?? [];
this.checkNodeString = "【ROOM】";
this.tempVals = [];
this.roomDbIds = []; //房間 dbId
this.model = null;
this.dataVizExtn = null;
this.shadingData = null;
this.onComplete = option.onComplete ?? null;
this.init();
}
init = async function () {
// 儲存 Viewer 的模型
this.model = viewer.model;
this.addHeatMaps();
};
async addHeatMaps() {
// 載入 Autodesk Viewer 的資料視覺化擴充功能
const dataVizExtn = await viewer.loadExtension(
"Autodesk.DataVisualization"
);
const {
SurfaceShadingData,
SurfaceShadingPoint,
SurfaceShadingNode,
SurfaceShadingGroup,
} = Autodesk.DataVisualization.Core;
// test
const shadingGroup = new SurfaceShadingGroup("iot-heatmap");
const rooms = new Map();
for (const { id, roomDbId, forge_dbid, position, sensorTypes } of this
.devices) {
if (!id || roomDbId == -1) {
continue;
}
if (!rooms.has(roomDbId)) {
const room = new SurfaceShadingNode(id, roomDbId);
shadingGroup.addChild(room);
rooms.set(roomDbId, room);
}
let room = rooms.get(roomDbId);
room.addPoint(new SurfaceShadingPoint(id, position, sensorTypes));
}
const shadingData = new SurfaceShadingData();
shadingData.addChild(shadingGroup);
shadingData.initialize(this.model);
this.shadingData = shadingData;
if (pageAct.sysSubTag == "L1") {
await dataVizExtn.setupSurfaceShading(this.model, shadingData, {
type: "PlanarHeatmap",
placementPosition: 0.7,
confidence: 240.0,
});
dataVizExtn.registerSurfaceShadingColors(
"temperature",
// [0x000000, 0xffd524]
[0xff0000, 0x0000ff]
);
} else {
await dataVizExtn.setupSurfaceShading(this.model, shadingData);
dataVizExtn.registerSurfaceShadingColors(
"temperature",
[0x0000ff, 0x00ff00, 0xffff00, 0xff0000]
);
}
dataVizExtn.renderSurfaceShading(
"iot-heatmap",
"temperature",
this.getSensorValue.bind(this)
);
this.onComplete ? this.onComplete() : "";
}
getSensorValue = function (device, sensorType) {
let dev = this.devices.filter((x) => x.id == device.id)[0];
return dev._temp / 40;
};
// 改變溫度
changeTemp = async function (devId, temp) {
const dataVizExtn = await viewer.loadExtension(
"Autodesk.DataVisualization"
);
// console.log("changeTemp",devId, temp)
this.tempVal = temp;
// 透過 device id 取得 roomDbId
this.devices.forEach((dev) => {
if (devId == dev.id) {
dev._temp = temp;
}
});
if (!this.shadingData) {
await this.addHeatMaps();
dataVizExtn.renderSurfaceShading(
"iot-heatmap",
"temperature",
this.getSensorValue.bind(this)
);
} else {
dataVizExtn.updateSurfaceShading(this.getSensorValue.bind(this));
}
// $.each(this.roomDbIds, async (idx, rDbid) => {
// this.dataVizExtn.renderSurfaceShading("RoomPanel" + rDbid, "temperature", this.getSensorValue.bind(this));
// })
//if (rDbid != null) {
// // 取得新的溫度值
// let getSensorValue = (device, sensorType) => {
// return this.tempVal / 40;
// }
// // 對 "Room Panel" 做表面顏色的渲染,並使用新的溫度值
// this.dataVizExtn.renderSurfaceShading("RoomPanel" + rDbid, "temperature", getSensorValue);
//}
};
// 改變設備顏色
changeErrorDevice() {
const ErrorDeviceList = new ErrorDevice({
errorDevices: alarmDbIdList.map((d) => ({
...d,
roomDbId: !isNaN(parseInt(x.room_dbid)) ? parseInt(x.room_dbid) : -1,
id: d.device_number,
position: isJSON(d.device_coordinate_3d)
? JSON.parse(d.device_coordinate_3d)
: {}, // x: 0, y: 25, z: -2.5 (3.35, -4.81, 12.88
sensorTypes: ["error", "temperature", "humidity", "CO2"],
forge_dbid: parseInt(d.forge_dbid),
})),
});
}
}
//全部物件 透明度: 輸入0:透明輸入1:不透明
function setTransparentBuilding(transparent, filDbids = []) {
for (var i = 0; i < allDbIdsStr.length; i++) {
setTransparency(parseInt(allDbIdsStr[i]), transparent);
}
for (var i = 0; i < filDbids.length; i++) {
setTransparency(parseInt(filDbids[i]), transparent == 0 ? 1 : 0);
}
}
//設定模型 透明度
function setTransparency(nodeId, opacity) {
var model = viewer.model;
var fragList = viewer.model.getFragmentList();
var fragIds = [];
model.getData().instanceTree.enumNodeFragments(
nodeId,
(fragId) => {
// 將遍歷到的片段 ID 添加到結果陣列中
fragIds.push(fragId);
},
(nodeId) => {
// 如果遍歷到的節點是葉節點,则返回 true表示繼續遍歷該節點的片段
return model.getData().instanceTree.isLeaf(nodeId);
}
);
fragIds.forEach((fragId) => {
//获取材质
var material = fragList.getMaterial(fragId);
if (material) {
//设置透明度
material.opacity = opacity; //0.5;
material.transparent = true;
//标记更新
material.needsUpdate = true;
}
});
//更新viewer
viewer.impl.invalidate(true, true, true);
}
function changeColor(nodeId, color = [0, 255, 0, 1]) {
//電梯變綠色
let model = viewer.model;
let fragList = viewer.model.getFragmentList();
let instanceTree = viewer.model?.getData().instanceTree;
color = color.map((x, i) => (i < 3 ? x / 255 : x));
color = new THREE.Vector4().fromArray(color);
model.getData().instanceTree.enumNodeFragments(
nodeId,
(fragId) => {
// 將遍歷到的片段 ID 添加到結果陣列中
let material = fragList.getMaterial(fragId);
if (material) {
//设置透明度
material.opacity = 1; //0.5;
material.transparent = true;
//标记更新
material.needsUpdate = true;
}
},
(nodeId) => {
// 如果遍歷到的節點是葉節點,则返回 true表示繼續遍歷該節點的片段
return model.getData().instanceTree.isLeaf(nodeId);
}
);
instanceTree.enumNodeChildren(
nodeId,
function (chiNodeId) {
viewer.setThemingColor(chiNodeId, color);
},
(chiNodeId) => {
// 如果遍歷到的節點是葉節點,则返回 true表示繼續遍歷該節點的片段
return model.getData().instanceTree.isLeaf(chiNodeId);
}
);
}
function hideColor(nodeId) {
//顏色改成透明
let instanceTree = viewer.model?.getData().instanceTree;
instanceTree.enumNodeChildren(
nodeId,
function (chiNodeId) {
var color = new THREE.Vector4(0, 1, 0, 0);
viewer.setThemingColor(chiNodeId, color);
},
true
);
}
//紀錄燈具座標
async function getLightData(data) {
lightList = [];
lightDataList = data;
}
async function testNewLight(dataList) {
// changeColorForHotspot;
}
//------------------- 加入熱點 -----------------
async function addHotPoint(data) {
// console.log("addHotPoint", data)
if (!pageAct.sysSubTag && !pageAct.sysMainTag) {
return;
}
var viewer = data.target ? data.target : data;
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
const DataVizCore = Autodesk.DataVisualization.Core;
const viewableType = Autodesk.DataVisualization.Core.ViewableType.SPRITE; //DataVizCore.ViewableType.SPRITE;
let spriteColor = null;
let url = "";
const dbIdStart = 10;
const dbIdEnd = 19;
spriteColor = new THREE.Color(0xffffff);
url = "/file/img/forge/sensor_circle.svg";
if (location.href.indexOf("localhost:5966") != -1) {
url = "/img/forge/sensor_circle.svg";
}
const style = new DataVizCore.ViewableStyle(viewableType, spriteColor, url);
if (
lightDataList != undefined &&
lightDataList != null &&
lightDataList.length > 0
) {
testNewLight(lightDataList);
}
//熱點 點擊事件註冊
viewer.addEventListener(DataVizCore.MOUSE_CLICK, onSpriteClicked); // SPRITE_SELECTED
// viewer.addEventListener(DataVizCore.MOUSE_CLICK, onSpriteClickedOut);
//viewer.addEventListener(
// Autodesk.Viewing.SELECTION_CHANGED_EVENT,
// onSelectionChange
//);
const viewableData = new DataVizCore.ViewableData();
viewableData.spriteSize = 24; // Sprites as points of size 24 x 24 pixels
myDataList.forEach((myData, index) => {
const dbId = 10 + index;
const myPosition = myData.position;
const viewable = new DataVizCore.SpriteViewable(myPosition, style, dbId);
myData._dbId = dbId;
viewableData.addViewable(viewable);
});
await viewableData.finish();
dataVizExtn.addViewables(viewableData);
// console.log(dataVizExtn)
$(selector).trigger("autodesk:complete:sprite", { myDataList });
//---------------- 熱點點擊事件 --------------------
function onSpriteClicked(event) {
event.hasStopped = true;
if (event != undefined && event != null) {
if (event.dbId >= dbIdStart) {
//event.dbId > 0 && event.dbId < 19
// console.log(`Sprite clicked: ${event.dbId}`);
for (let i = dbIdStart; i <= myDataList.length + 10; i++) {
!alarmDbIdList.some(({ spriteDbid }) => spriteDbid == i) &&
pageAct.sysSubTag !== "L1" &&
changeColorForHotspot(i);
changeScaleForHotspot(i, true);
}
let myData = myDataList.filter((x) => x._dbId == event.dbId)[0];
if (myData && pageAct.sysSubTag == "L1" && pageAct.floTag == null) {
getLTNode(myData);
}
if (
lightList != undefined &&
lightList != null &&
lightList.length > 0
) {
//setLightOpenOrClose(false, myData.device_guid);//關燈測試
//setLightValues(myData.device_guid, 20, 0x00ff00);//更改燈光顏色和強度的測試
moveViewToDevice(myData.forge_dbid); //移動視角至該設備
}
$(selector).trigger("autodesk:click:sprite", { event, myData });
} else {
!alarmDbIdList.some(({ spriteDbid }) => spriteDbid == event.dbId) &&
pageAct.sysSubTag !== "L1" &&
changeColorForHotspot(event.dbId, null);
changeScaleForHotspot(event.dbId, false);
$(selector).trigger("autodesk:clickOut:sprite", { event });
}
if (event.clickInfo != null) {
//document.getElementById('deviceName').innerHTML = viewer.model.getInstanceTree().getNodeName(event.clickInfo.dbId);
//document.getElementById('deviceDbid').innerHTML = event.clickInfo.dbId;
//document.getElementById('devicePosition').innerHTML = "(" + (event.clickInfo.point.x).toFixed(2) + ", " + (event.clickInfo.point.y).toFixed(2) + ", " + (event.clickInfo.point.z).toFixed(2) + ")";
// console.log(`event>> dbId: ${event.clickInfo.dbId}, id: ${event.clickInfo.object.id}, position.x: ${event.clickInfo.point.x}, y: ${event.clickInfo.point.y}, z: ${event.clickInfo.point.z}, name: ${viewer.model.getInstanceTree().getNodeName(event.clickInfo.dbId)}`);
}
}
}
// function onSpriteClickedOut(event){
// event.hasStopped = true;
// if (event != undefined && event != null) {
// if (event.dbId >= dbIdStart) {
// for (let i = dbIdStart; i <= myDataList.length + 10; i++) {
// changeColorForHotspot(event.dbId);
// changeScaleForHotspot(event.dbId, false);
// }
// let myData = myDataList.filter(x => x._dbId == event.dbId)[0];
// debugger
// $(selector).trigger("autodesk:clickOut:sprite", { event });
// }
// }
// }
//function onSelectionChange(event) {
// if (event != undefined && event != null) {
// const dbIds = event.dbIdArray;
// if (dbIds.length > 0) {
// // 處理已選取元件的邏輯
// $(selector).trigger("autodesk:click:sprite", event);
// console.log(`------ name: ${viewer.model.getInstanceTree().getNodeName(dbIds)} , dbId: ${dbIds}`);//, id: ${event.clickInfo.object.id}, position.x: ${event.clickInfo.point.x}, y: ${event.clickInfo.point.y}, z: ${event.clickInfo.point.z}
// } else {
// // 處理沒有選取元件的邏輯
// $(selector).trigger("autodesk:click:sprite", event);
// console.log("no item");
// }
// }
//}
}
//hex -> rgb
function hexToRgb(hex) {
return (
"rgb(" +
parseInt("0x" + hex.slice(1, 3)) +
"," +
parseInt("0x" + hex.slice(3, 5)) +
"," +
parseInt("0x" + hex.slice(5, 7)) +
")"
);
}
// 熱點 更換顏色
async function changeColorForHotspot(dbId, type = null, OnOff = false) {
if(typeof viewer === "undefined"){
return;
}
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
let spriteColorFocus = new THREE.Color(0xffffff);
spriteColorFocus = OnOff
? pageAct.sysSubObj.device_normal_color
? new THREE.Color(hexToRgb(pageAct.sysSubObj.device_normal_color))
: new THREE.Color(0xffffff)
: pageAct.sysSubObj.device_close_color
? new THREE.Color(hexToRgb(pageAct.sysSubObj.device_close_color))
: new THREE.Color(0xffffff);
if (type == "focus") {
spriteColorFocus = new THREE.Color(0x00ffe1);
} else if (type == "error") {
spriteColorFocus = new THREE.Color(
hexToRgb(pageAct.sysSubObj.device_error_color)
);
spriteErrIcon = baseForgeApiUrl + "/file/img/forge/sensor_circle.svg";
if (location.href.indexOf("localhost:5966") != -1) {
spriteErrIcon = baseForgeApiUrl + "/img/forge/sensor_circle.svg";
}
}
const viewablesToUpdate = dbId;
dataVizExtn.invalidateViewables(viewablesToUpdate, (viewable) => {
return {
url: "/file/img/forge/sensor_circle.svg",
color: spriteColorFocus,
};
});
}
// 更換 icon
async function changeScaleForHotspot(dbId, type = true) {
if(typeof viewer == 'undefined'){
return;
}
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
let spriteErrIcon = baseForgeApiUrl + "/file/img/forge/sensor_circle.svg";
if (location.href.indexOf("localhost:5966") != -1) {
spriteErrIcon = baseForgeApiUrl + "/img/forge/hotspot.svg";
}
const viewablesToUpdate = dbId;
dataVizExtn.invalidateViewables(viewablesToUpdate, (viewable) => {
return {
url: spriteErrIcon,
};
});
}
// 熱點 更換大小
async function changeScaleForHotspot(dbId, type = true) {
if(typeof viewer == 'undefined'){
return;
}
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
let scale = 1;
if (type) {
scale = 1.5;
}
const viewablesToUpdate = dbId;
dataVizExtn.invalidateViewables(viewablesToUpdate, (viewable) => {
return {
scale: scale, // Restore the viewable size
//url: "https://.../circle.svg",
};
});
}
//------------------- end --------------
//------------------ 熱圖 -------------------------------
async function loadHeatmaps(model) {
//console.log("熱圖 loadHeat")
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
//取三個空調設備的位置打點
const devices = [
{
id: "Oficina 5",
//name: "Oficina-",
position: { x: 6.98, y: -19.0, z: 16.86 }, // x: 0, y: 25, z: -2.5 (-4.93, -20.61, 16.86)
sensorTypes: ["temperature", "humidity"],
},
{
id: "Oficina 4",
//name: "Oficina-",
position: { x: 35.85, y: -2.24, z: 16.86 }, // x: 0, y: 25.03, z: -2.52 (23.94, -3.85, 16.86)
sensorTypes: ["temperature", "humidity"],
},
{
id: "Oficina 3",
//name: "Oficina-",
position: { x: 6.98, y: -2.24, z: 16.86 }, // x: 0, y: 25.03, z: -2.52 (-4.93, -3.85, 16.86)
sensorTypes: ["temperature", "humidity"],
},
];
//冷氣N5: (-4.93, -20.61, 16.86), N4: (23.94, -3.85, 16.86), N3: (-4.93, -3.85, 16.86)
// Initialize sensor values
let sensorVals = [];
for (let i = 0; i < devices.length; i++) {
sensorVals[i] = Math.random();
}
const roomDbIds = [];
roomDbIds.push(7567);
const { SurfaceShadingData, SurfaceShadingPoint, SurfaceShadingNode } =
Autodesk.DataVisualization.Core;
const shadingNode = new SurfaceShadingNode("Room Panel", roomDbIds);
devices.forEach((device) => {
const shadingPoint = new SurfaceShadingPoint(
device.id,
device.position,
device.sensorTypes
);
shadingNode.addPoint(shadingPoint);
});
const heatmapData = new SurfaceShadingData();
heatmapData.addChild(shadingNode);
heatmapData.initialize(model);
// Setup surface shading
await dataVizExtn.setupSurfaceShading(model, heatmapData);
dataVizExtn.registerSurfaceShadingColors("temperature", [0xff0000, 0x0000ff]);
function getSensorValue(device, sensorType) {
return sensorVals[parseInt(device.id.slice(-1)) - 1];
}
dataVizExtn.renderSurfaceShading("Room Panel", "temperature", getSensorValue);
setInterval(() => {
// Modify sensor values.
for (let i = 0; i < devices.length; i++) {
sensorVals[i] = Math.random();
}
dataVizExtn.updateSurfaceShading(getSensorValue);
}, 2000);
}
//------------------ end --------------------------------
async function loadHeatmapsForFloor(model) {
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
//x: -17.33, y: 51.03, z: -2.52
const devices = [
{
id: "Oficina 5",
//name: "Oficina-",
position: { x: 6.98, y: -19.0, z: 16.86 }, // x: 0, y: 25, z: -2.5 (-4.93, -20.61, 16.86)
sensorTypes: ["temperature", "humidity"],
},
{
id: "Oficina 4",
//name: "Oficina-",
position: { x: 35.85, y: -2.24, z: 16.86 }, // x: 0, y: 25.03, z: -2.52 (23.94, -3.85, 16.86)
sensorTypes: ["temperature", "humidity"],
},
{
id: "Oficina 3",
//name: "Oficina-",
position: { x: 6.98, y: -2.24, z: 16.86 }, // x: 0, y: 25.03, z: -2.52 (-4.93, -3.85, 16.86)
sensorTypes: ["temperature", "humidity"],
},
];
// Initialize sensor values
let sensorVals = [];
for (let i = 0; i < devices.length; i++) {
sensorVals[i] = Math.random();
}
const roomDbIds = [];
roomDbIds.push(7567);
const { SurfaceShadingData, SurfaceShadingPoint, SurfaceShadingNode } =
Autodesk.DataVisualization.Core;
const shadingNode = new SurfaceShadingNode("Room Panel", roomDbIds);
devices.forEach((device) => {
const shadingPoint = new SurfaceShadingPoint(
device.id,
device.position,
device.sensorTypes
);
shadingNode.addPoint(shadingPoint);
});
const heatmapData = new SurfaceShadingData();
heatmapData.addChild(shadingNode);
heatmapData.initialize(model);
// Setup surface shading
await dataVizExtn.setupSurfaceShading(model, heatmapData);
//dataVizExtn.registerSurfaceShadingColors("co2", [0x00ff00, 0xff0000]);
dataVizExtn.registerSurfaceShadingColors("temperature", [0xff0000, 0x0000ff]);
function getSensorValue(device, sensorType) {
return sensorVals[parseInt(device.id.slice(-1)) - 1];
}
dataVizExtn.renderSurfaceShading("Room Panel", "temperature", getSensorValue);
setInterval(() => {
// Modify sensor values.
for (let i = 0; i < devices.length; i++) {
sensorVals[i] = Math.random();
}
dataVizExtn.updateSurfaceShading(getSensorValue);
}, 2000);
}
//
async function loadHeatmap() {
const model = viewer.model;
loadHeatmaps(model);
}
async function loadHeatmapForFloor() {
const model = viewer.model;
loadHeatmapsForFloor(model);
}
//------------ 剖面 ----------------------
function findLevels(model) {
return new Promise((resolve, reject) => {
model.search(
"layer",
(nodeIds) => {
let levels = [];
const tree = viewer.model.getInstanceTree();
for (let i = 0; i < nodeIds.length; i++) {
const dbId = nodeIds[i];
const name = tree.getNodeName(dbId);
if (!name || name.includes("<沒有層級>")) continue;
levels.push({
guid: dbId,
name,
dbId,
extension: {
buildingStory: true,
structure: false,
computationHeight: 0,
groundPlane: false,
hasAssociatedViewPlans: false,
},
});
}
resolve(levels);
},
(e) => {
reject(e);
}
);
});
}
async function getRemoteLevels() {
let aecData = await Autodesk.Viewing.Document.getAecModelData(
viewer.model.getDocumentNode()
);
let levels;
if (!aecData || !aecData.levels) {
const levelExt = await viewer.loadExtension("Autodesk.AEC.LevelsExtension");
levelExt.setAecModelData(undefined, viewer.model); //!<<< Clear before reset
levels = await findLevels(viewer.model);
aecdata = Autodesk.AEC.AecModelData.computeLevels(levels, viewer.model); //!<<< Rebuild aec model data
} else {
levels = aecData.levels;
}
levels.sort((a, b) => b.elevation - a.elevation);
return levels;
}
async function getLevelsData(lowerFloor, upperFloor, callback = null) {
// 樓層正規化 取得樓層
const floorRegex = /[\d|\w]+F/gim;
const floorRegex2 = /^FL[\d|\w]+/gim;
const data = await getRemoteLevels();
for (var i = 0; i < data.length; i++) {
let name =
data[i].name?.match(floorRegex) || data[i].name?.match(floorRegex2);
if (
name &&
(name[0] == lowerFloor || name[0].split("L")[1] + "F" == lowerFloor)
) {
lowerIdx = i;
}
if (
name &&
(name[0] == upperFloor || name[0].split("L")[1] + "F" == upperFloor)
) {
if (i > upperIdx && lowerFloor == upperFloor) {
} else {
upperIdx = i;
}
}
}
this.levels = data;
profile(callback);
}
function getCutPlaneParam(idx, n) {
if (idx < 0 || !n) return;
const level = this.levels[idx];
if (!level) return;
const model = viewer.model;
const globalOffset = model.getData().globalOffset;
const units = model.getUnitString();
const elevRaw = Autodesk.Viewing.Private.convertUnits(
"ft",
units,
1,
level.elevation
);
let d = elevRaw - globalOffset.z - 0.5;
if (n == 1) d = -1 * d;
return new THREE.Vector4(0, 0, n, d);
}
function profile(callback = null) {
//const upperIdx = 6;
const upperCutPlaneParam = this.getCutPlaneParam(upperIdx, 1);
//const lowerIdx = 7;
const lowerCutPlaneParam = this.getCutPlaneParam(lowerIdx, -1);
viewer.setCutPlanes([upperCutPlaneParam, lowerCutPlaneParam]);
callback ? callback() : "";
}
//----------------- end -----------------------------------------------
//新增燈光
async function newLight(lightPosition) {
//聚光燈
var spotLight = new THREE.SpotLight(0xe1cf18, 0, 20, 0.6, 0.5, 10);
spotLight.position.set(lightPosition.x, lightPosition.y, lightPosition.z);
// console.log(lightPosition.x, lightPosition.y, lightPosition.z)
spotLight.castShadow = false;
spotLight.visible = true;
spotLight.target.position.set(
lightPosition.x,
lightPosition.y,
lightPosition.z - 20
);
viewer.scene.add(spotLight.target);
viewer.scene.add(spotLight);
viewer.impl.sceneUpdated(true);
return spotLight;
}
//調整燈光 強度、顏色
async function setLightValues(deviceGuid, intensity, color) {
const light = lightList.find(({ device_guid }) => device_guid === deviceGuid);
if (light) {
light.lightObject.intensity = intensity;
var tempcolor = new THREE.Color().setHex(color);
light.lightObject.color = tempcolor;
}
// for (var i = 0; i < lightList.length; i++) {
// if (lightList[i].device_guid == deviceGuid) {
// lightList[i].lightObject.intensity = intensity;
// var tempcolor = new THREE.Color().setHex(color);
// lightList[i].lightObject.color = tempcolor;
// }
// }
viewer.impl.sceneUpdated(true);
}
//燈光開關
async function setLightOpenOrClose(value, deviceGuid) {
for (var i = 0; i < lightList.length; i++) {
if (lightList[i].device_guid == deviceGuid) {
lightList[i].lightObject.visible = value;
}
}
viewer.impl.sceneUpdated(true);
}
//透過nodeId更改物件顏色或顯示與否color請填寫THREE.Vector4
function changeColorTransparency(nodeId, color) {
//變綠色
//var color = new THREE.Vector4(0, 1, 0, 1);//綠色前三個代表r、g、b; 亦可填入255/255
//var color = new THREE.Vector4(0, 1, 0, 0);//不顯示顏色;最後的參數為透明度
viewer.setThemingColor(nodeId, color);
}
//隱藏全物件
function hideAllObjects(filDbids = []) {
//viewer.hide(4);//只針對一個物件(dbid為4)做隱藏
for (var i = 0; i < allDbIdsStr.length; i++) {
viewer.hide(parseInt(allDbIdsStr[i]));
}
for (var i = 0; i < filDbids.length; i++) {
viewer.show(parseInt(filDbids[i]), viewer.model);
}
}
//顯示全物件
function showAllObjects() {
//viewer.show(4); //只針對一個物件(dbid為4)做顯示
for (var i = 0; i < allDbIdsStr.length; i++) {
viewer.show(parseInt(allDbIdsStr[i]));
}
}
function showHeat(selector) {
const labels = [
`${(10).toFixed(2)}${"°C"}`,
`${(40 / 2).toFixed(2)}${"°C"}`,
`${(30).toFixed(2)}${"°C"}`,
];
const colorStops = ["blue", "green", "yellow", "red"];
createHeatmapRect(labels, colorStops, selector);
}
function showHeatCO2(selector) {
const labels = [
`${(400).toFixed(2)}`,
`${(1600 / 2 + 400).toFixed(2)}`,
`${(2000).toFixed(2)}`,
];
const colorStops = ["blue", "green", "yellow", "red"];
createHeatmapRect(labels, colorStops, selector);
}
function createHeatmapRect(labels, colorStops, selector) {
if (!$(selector)[0]) {
return;
}
const context = $(selector)[0].getContext("2d");
let i, len;
context.clearRect(0, 0, 200, 50);
context.fillStyle = "back"; //white
for (i = 0, len = labels.length; i < len; i++) {
let x = 10 + (180 * i) / (len - 1);
if (i === len - 1) {
x -= context.measureText(labels[i]).width;
} else if (i > 0) {
x -= 0.5 * context.measureText(labels[i]).width;
}
context.fillText(labels[i], x, 10);
}
const gradient = context.createLinearGradient(0, 0, 200, 0);
for (i = 0, len = colorStops.length; i < len; i++) {
gradient.addColorStop(i / (len - 1), colorStops[i]);
}
context.fillStyle = gradient;
context.fillRect(10, 20, 200, 25);
}
//======================== 外部呼叫function ===========================
//紀錄熱點座標
function getHopspotPoint(data) {
myDataList = data;
}
//呼叫載入熱圖
async function toLoadHeatmap(roomArr) {
if(typeof viewer == 'undefined'){
return;
}
const model = viewer.model;
loadHeatmaps(model, roomArr);
}
function setShadowShow(type = false) {
if(typeof viewer == 'undefined'){
return;
}
viewer.setGroundShadow(type);
viewer.impl.sceneUpdated(true);
}
//============================= end ===================================
function moveViewToDevice(letter) {
if(typeof viewer == 'undefined'){
return;
}
if (letter != "") {
viewer.clearSelection();
viewer.select(letter);
viewer.fitToView([letter]);
} else {
viewer.clearSelection();
}
}
class ErrorDevice {
constructor(options) {
this.errorDevices = options.errorDevices ?? [];
this.model = null;
this.#init();
}
#init = async function () {
this.model = viewer.model;
// 載入 Autodesk Viewer 的資料視覺化擴充功能
const dataVizExtn = await viewer.loadExtension(
"Autodesk.DataVisualization"
);
// dataVizExtn.removeSurfaceShading(this.model);
// 儲存 Viewer 的模型
this.addHeatMaps();
};
async addHeatMaps() {
const dataVizExtn = await viewer.loadExtension(
"Autodesk.DataVisualization"
);
const {
SurfaceShadingData,
SurfaceShadingPoint,
SurfaceShadingNode,
SurfaceShadingGroup,
} = Autodesk.DataVisualization.Core;
// test
const shadingGroup = new SurfaceShadingGroup("device-error-heatmap");
const rooms = new Map();
for (const { id, forge_dbid, roomDbId, position, sensorTypes } of this
.errorDevices) {
if (!id || forge_dbid == -1 || roomDbId == -1) {
continue;
}
if (!rooms.has(forge_dbid) || !rooms.has(roomDbId)) {
const room = new SurfaceShadingNode(`error_${id}`, forge_dbid);
shadingGroup.addChild(room);
rooms.set(forge_dbid, room);
}
const room = rooms.get(forge_dbid);
room.addPoint(
new SurfaceShadingPoint(`error_${id}`, position, sensorTypes)
);
}
const shadingData = new SurfaceShadingData();
shadingData.addChild(shadingGroup);
shadingData.initialize(this.model);
await dataVizExtn.setupSurfaceShading(this.model, shadingData);
dataVizExtn.registerSurfaceShadingColors("error", [0xff0000, 0xff0000]);
dataVizExtn.renderSurfaceShading(
"device-error-heatmap",
"error",
this.getSensorValue.bind(this)
);
}
getSensorValue = function (device, sensorType) {
let dev = this.errorDevices.filter((x) => `error_${x.id}` == device.id)[0];
if (sensorType === "error") {
return 0;
}
};
}
// =================================燈光用===============================================
async function getLTNode(device) {
// 處理 node 的資料
const nodes = device.device_nodes?.map((n) => ({
...n,
position: isJSON(n.device_node_coordinate_3D)
? JSON.parse(n.device_node_coordinate_3D)
: {}, // x: 0, y: 25, z: -2.5 (3.35, -4.81, 12.88
sensorTypes: ["TrafficDensity"],
forgeDbId: parseInt(n.forge_dbid),
dbId: parseInt(n.forge_dbid),
}));
// 載入 Autodesk Viewer 的資料視覺化擴充功能
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
const {
SurfaceShadingGroup,
SurfaceShadingData,
SurfaceShadingPoint,
SurfaceShadingNode,
} = Autodesk.DataVisualization.Core;
dataVizExtn.removeSurfaceShading(viewer.model);
// Group to select both north and southbound
const shadingGroup = new SurfaceShadingGroup("LT");
nodes.forEach(({ position, forgeDbId, sensorTypes, device_number }) => {
const LTGroup = new SurfaceShadingNode("LTGroup" + forgeDbId, forgeDbId);
LTGroup.addPoint(
new SurfaceShadingPoint(device_number, position, sensorTypes)
);
shadingGroup.addChild(LTGroup);
});
const heatmapData = new SurfaceShadingData();
heatmapData.addChild(shadingGroup);
// Initialize with model loaded from APS
heatmapData.initialize(viewer.model);
await dataVizExtn.setupSurfaceShading(viewer.model, heatmapData);
const sensorColors = [0x0080FF, 0x0080FF];
// Set heatmap colors for temperature
const sensorType = "TrafficDensity";
dataVizExtn.registerSurfaceShadingColors(sensorType, sensorColors);
dataVizExtn.renderSurfaceShading("LT", "TrafficDensity", getLTSensorValue);
console.log("dataVizExtn", dataVizExtn);
}
// Function that provides a [0,1] value for the Heatmap
function getLTSensorValue(surfaceShadingPoint, sensorType) {
return 1;
}