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

1233 lines
42 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var viewer;
let fragProxy;
var targetFloorZ;
var elevatorSpeed;
var selector = "#forgeViewer";
var myDataList;
var lightDataList;
var lightList = [];//燈光清單
var levels;//剖面用
var lowerIdx;//剖面的下方樓層
var upperIdx;//剖面的上方樓層
function launchViewer(urn, callback,failCallback, _selector = "#forgeViewer") {
selector = _selector;
var options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken,
settings: {
ambientShadows: false,
groundShadows: false
}
};
Autodesk.Viewing.Initializer(options, () => {
viewer = new Autodesk.Viewing.GuiViewer3D($(selector)[0]);
//viewer = new Autodesk.Viewing.Viewer3D(document.getElementById('forgeViewer'));
viewer.start();
var documentId = 'urn:' + urn;
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
setShadowShow();
$(selector).on("autodesk:loaded", function (e, nodeIds) {
callback ? callback(viewer, nodeIds) : "";
})
$(selector).on("autodesk:loaded:fail", function (e) {
failCallback ? failCallback(viewer) : "";
})
//test
// for (let i = 0; i < urn.length; i++) {
// Autodesk.Viewing.Document.load(urn[i]["urn"], async (doc) => {
// let viewables = doc.getRoot().getDefaultGeometry();
// let model = await viewer.loadDocumentNode(doc, viewables, {
// preserveView: false,
// keepCurrentModels: true,
// placementTransform: (new THREE.Matrix4()).setPosition(urn[i]["xform"]),
// keepCurrentModels: true,
// globalOffset: {
// x: 0,
// y: 0,
// z: 0
// }
// });
// await viewer.waitForLoadDone(); //!<<< Wait for loading materials, properties and geometries for this model (URN)
// });
// }
//loadHeatmaps(viewer.getAllModels()[0]); //!<<< equals to viewer.model
});
}
function launchViewerForHotspot(urn, callback, failCallback, _selector = "#forgeViewer") {
selector = _selector;
var av = Autodesk.Viewing;
var options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken,
settings: {
ambientShadows: false,
groundShadows: false
}
};
Autodesk.Viewing.Initializer(options, () => {
viewer = new Autodesk.Viewing.GuiViewer3D($(selector)[0]);
viewer.start();
var documentId = 'urn:' + urn;
viewer.addEventListener(av.GEOMETRY_LOADED_EVENT, addHotPoint, {
once: true,
});
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
setShadowShow();
$(selector).on("autodesk:loaded", function (e, nodeIds) {
callback ? callback(viewer, nodeIds) : "";
console.log(viewer.get)
})
$(selector).on("autodesk:loaded:fail", function (e) {
failCallback ? failCallback(viewer) : "";
})
});
}
function launchViewerNoTools(urn, callback, failCallback, _selector = "#forgeViewer") {
selector = _selector;
var options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken,
settings: {
ambientShadows: false,
groundShadows: false
}
};
Autodesk.Viewing.Initializer(options, () => {
//viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer'));
viewer = new Autodesk.Viewing.Viewer3D($(selector)[0]);
viewer.start();
var documentId = 'urn:' + urn;
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
setShadowShow();
$(selector).on("autodesk:loaded", function (e, nodeIds) {
callback ? callback(viewer, nodeIds) : "";
})
$(selector).on("autodesk:loaded:fail", function (e) {
failCallback ? failCallback(viewer) : "";
})
});
}
function getAllLeafComponents(viewer, callback) {
var cbCount = 0;
var tree;
var jsData = []
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, '#');
});
}
class elevator3D {
constructor(option = {}) {
this.ele = option.element;
this.viewer = option.viewer;
this.nodeId = option.nodeId;
this.speed = option.speed ?? 0.03;
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.init();
}
// 定義遍歷模型中所有片段的異步函數
enumFragments = function (nodeId) {
return new Promise((resolve, reject) => {
// 定義結果陣列
let result = [];
// 使用 InstanceTree.enumNodeFragments 遍歷模型中的所有片段
this.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 = this.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);
fragments.forEach((frag) => {
// 取得當前片段的 FragmentProxy 物件
let fragProxy = this.viewer.impl.getFragmentProxy(this.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;
// 更新當前片段的動畫變換矩陣
fragProxy.updateAnimTransform();
})
}
// 通知檢視器更新場景
this.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 = this.viewer.impl.getFragmentProxy(this.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;
fragProxy.updateAnimTransform()
})
this.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;
}
}
}
function onDocumentLoadSuccess(doc, eleOption) {
var viewables = doc.getRoot().getDefaultGeometry();
viewer.loadDocumentNode(doc, viewables).then(i => {
// documented loaded, any action?
});
// viewer.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, (args) => {
// var currSelection = viewer.getSelection();
// var domElem = document.getElementById('id_printer');
// domElem.innerText = currSelection[0];
// });
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
let instanceTree = viewer.model?.getData().instanceTree;
console.log(instanceTree.nodeAccess)
allDbIdsStr = Object.keys(instanceTree.nodeAccess.dbIdToIndex);
/*getNodeIdByDbIds("【電梯】", (nodeIds) => {*/
$(selector).trigger("autodesk:loaded");
/*});*/
});
}
// 輔助函數,使用 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;
}
function setElevatorFloor(floor) {
if (floor == 0)
targetFloorZ = 0;
else if (floor == 1)
targetFloorZ = 15;
else if (floor == 2)
targetFloorZ = 26;
}
function setElevatorSpeed(speed) { //0.01 ~ 1
elevatorSpeed = speed;
}
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;
}
fragProxyZ = fragProxy.position.z;
fragProxy.updateAnimTransform()
});
viewer.impl.sceneUpdated(true);
if (movStatus == 2) {
if (fragProxyZ >= targetFloorZ) {
requestAnimationFrame(movElevator);
}
}
else if (movStatus == 1) {
if (fragProxyZ <= targetFloorZ) {
requestAnimationFrame(movElevator);
}
}
//let fragPosition = new THREE.Vector3(position);// 一樓0 二樓15 三樓 26
//fragProxy.position = fragPosition;
//fragProxy.updateAnimTransform();
//viewer.impl.sceneUpdated(true);
}
function getAllDbIds(viewer) {
var instanceTree = viewer.model?.getData().instanceTree;
var allDbIdsStr = Object.keys(instanceTree.nodeAccess.dbIdToIndex);
return allDbIdsStr.map(function (id) { return parseInt(id) });
}
/**
* Autodesk.Viewing.Document.load() failuire callback.
*/
function onDocumentLoadFailure(viewerErrorCode) {
$(selector).trigger("autodesk:loaded:fail");
console.error("onDocumentLoadFailure() - errorCode:" + viewerErrorCode);
}
function getForgeToken(callback) {
fetch(forgeTokenBase.url).then(res => {
res.json().then(data => {
let limitResType = ["object", "undefined"];
let access_token = data;
$.each(forgeTokenBase.res_access_token, (idx, dict) => {
access_token = access_token[dict];
})
let expires_in = data;
$.each(forgeTokenBase.res_expires_in, (idx, dict) => {
expires_in = expires_in[dict];
})
if (limitResType.includes(typeof access_token) || limitResType.includes(typeof expires_in)) {
access_token = "", expires_in = "";
throw "access_token or expires_in on the ForgeTokenBase object for getting forge token by URL is not the correct path.";
}
callback(access_token, expires_in);
});
});
//callback("eyJhbGciOiJSUzI1NiIsImtpZCI6IlU3c0dGRldUTzlBekNhSzBqZURRM2dQZXBURVdWN2VhIn0.eyJzY29wZSI6WyJkYXRhOndyaXRlIiwiZGF0YTpyZWFkIiwiYnVja2V0OnJlYWQiLCJidWNrZXQ6dXBkYXRlIiwiYnVja2V0OmNyZWF0ZSJdLCJjbGllbnRfaWQiOiJUQTNocXNGZnpRYk5PVVhLcGxkS1VLU2V3NFNKMjF3NSIsImF1ZCI6Imh0dHBzOi8vYXV0b2Rlc2suY29tL2F1ZC9hand0ZXhwNjAiLCJqdGkiOiJiemxzWE5qWElvZ2R1UjUzTUJkdlhrTTNTT01qeVB1bHJrMmdTVWJudGNTeDg1b01kRG1xejg3Z05jenJkRzhpIiwiZXhwIjoxNjY4MTgzMDM2fQ.VU3qLwTJ9nlXnomKCdk4y5UcgszGEO_zlvE7w5mWWajeBMwKLo-zw7LJEqUEajRksvssppR9SbVsjLSx-vDVc3DRhCo3jYTWKPT1T3wQrlkOSqLeIrAdnKdBDNBWKgrGJt_xcmXc3dZ3XNKf9L_F6Ex808rUlo6cem1mcPpKl1jCBDqKu1mAX7aDtZ65TTQZbGGhbG4HdnET-d1i5w4LunGN11UAHhDUW3n0SWWIBL27PiiUQONZttajhD5st6IngYLcjr93BYVyJmDF7-wm4WZlHSw2OnXIfbJcFXEd83uVv_Rej4UXjzZ0e6kHwzc2nvGvKSIFu3Nt7CabdR8CkA", 3599);
}
//------------------ 熱圖 -------------------------------
class ADHeatMaps {
constructor(option = {}) {
this.devices = option.devices ?? [];
this.checkNodeString = "【ROOM】";
this.tempVals = [];
this.roomDbIds = []; //房間 dbId
this.model = null;
this.dataVizExtn = null;
this.onComplete = option.onComplete ?? null;
this.init();
}
init = async function () {
// 載入 Autodesk Viewer 的資料視覺化擴充功能
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
// 儲存 Viewer 的模型
this.model = viewer.model;
const {
SurfaceShadingData,
SurfaceShadingPoint,
SurfaceShadingNode,
} = Autodesk.DataVisualization.Core;
//let nodeIds = await getNodeIdByDbIds(this.checkNodeString);
//nodeIds = Array.from(nodeIds);
//nodeIds = nodeIds.map(x => { return { room: x[0] , nodeId: x[1]} });
this.roomDbIds = this.devices.filter(x => x.roomDbId != -1).map(x => x.roomDbId).Distinct();
// 建立一個 SurfaceShadingData 物件,並將 SurfaceShadingNode 加入到該物件中
const heatmapData = new SurfaceShadingData();
$.each(this.roomDbIds, (idx, rDbid) => {
// 建立一個名為 "Room Panel" 的 SurfaceShadingNode 物件,並將房間的模型給傳入,只在該房間呈現溫度
const shadingNode = new SurfaceShadingNode("RoomPanel" + rDbid, rDbid);
// 遍歷每個設備,建立一個 SurfaceShadingPoint 物件並加入到 SurfaceShadingNode 中,透過這些設備渲染溫度
this.devices.filter(x => x.roomDbId == rDbid).forEach((device) => {
const shadingPoint = new SurfaceShadingPoint(
device.id,
device.position,
device.sensorTypes
);
shadingNode.addPoint(shadingPoint);
device.temp = 0;
});
heatmapData.addChild(shadingNode);
})
// 將資料初始化並顯示在模型上
heatmapData.initialize(this.model);
// 對模型做表面顏色的渲染
await dataVizExtn.setupSurfaceShading(this.model, heatmapData);
// 對 "temperature" 的溫度設定兩種顏色:紅色和藍色
dataVizExtn.registerSurfaceShadingColors("temperature", [0x0000ff, 0x00ff00, 0xffff00, 0xff0000]);
this.dataVizExtn = dataVizExtn;
$.each(this.roomDbIds, (idx, rDbid) => {
this.dataVizExtn.renderSurfaceShading("RoomPanel" + rDbid, "temperature", this.getSensorValue.bind(this));
})
/*this.changeTemp(this.tempVal);*/
this.onComplete ? this.onComplete() : "";
}
getSensorValue = function(device, sensorType) {
let dev = this.devices.filter(x => x.id == device.id)[0];
return dev.temp / 40;
}
// 改變溫度
changeTemp = function (devId, temp) {
this.tempVal = temp;
// 透過 device id 取得 roomDbId
this.devices.forEach((dev) => {
if (devId == dev.id) {
dev.temp = temp;
}
})
$.each(this.roomDbIds, (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);
//}
}
}
//全部物件 透明度: 輸入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) {
dataList.forEach((myData, index) => {
const position = JSON.parse(myData.device_coordinate_3d);
if (lightList.findIndex(x => x.device_guid == myData.device_guid) == -1) {
lightList.push({ dbid: myData.forge_dbid, device_guid: myData.device_guid, lightObject: new THREE.SpotLight(0xff0000, 200, 20, 0.6, 0.5, 10) });
}
lightList[index].lightObject.position.set(position.x, position.y, position.z);
lightList[index].lightObject.castShadow = true;
lightList[index].lightObject.visible = true;
lightList[index].lightObject.target.position.set(position.x, position.y, position.z - 100);
viewer.scene.add(lightList[index].lightObject.target);
viewer.scene.add(lightList[index].lightObject);
//let spotLightHelper = new THREE.SpotLightHelper(lightList[index].lightObject);
//viewer.scene.add(spotLightHelper);
});
viewer.impl.sceneUpdated(true);
}
//------------------- 加入熱點 -----------------
async function addHotPoint(data) {
var viewer = data.target;
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
const DataVizCore = Autodesk.DataVisualization.Core;
const viewableType = Autodesk.DataVisualization.Core.ViewableType.SPRITE;//DataVizCore.ViewableType.SPRITE;
const spriteColor = new THREE.Color(0xffffff);
const dbIdStart = 10;
const dbIdEnd = 19;
let spriteIcon = "/file/img/forge/hotspot.svg";
if (location.href.indexOf("localhost:5966") != -1) {
spriteIcon = "/img/forge/hotspot.svg";
}
const style = new DataVizCore.ViewableStyle(viewableType, spriteColor, spriteIcon);
if (lightDataList != undefined && lightDataList != null && lightDataList.length > 0) {
testNewLight(lightDataList);
}
//熱點 點擊事件註冊
viewer.addEventListener(DataVizCore.MOUSE_CLICK, onSpriteClicked);// SPRITE_SELECTED
//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);
$(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++) {
changeColorForHotspot(i);
changeScaleForHotspot(i, false);
}
let myData = myDataList.filter(x => x._dbId == event.dbId)[0];
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 {
$(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 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");
// }
// }
//}
}
// 熱點 更換顏色
async function changeColorForHotspot(dbId, type = null) {
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
let spriteColorFocus = new THREE.Color(0xffffff);
if (type == "focus") {
spriteColorFocus = new THREE.Color(0x00ffe1);
} else if (type == "error") {
spriteColorFocus = new THREE.Color(0xff0000);
}
const viewablesToUpdate = dbId;
dataVizExtn.invalidateViewables(viewablesToUpdate, (viewable) => {
return {
color: spriteColorFocus,
};
});
}
// 熱點 更換大小
async function changeScaleForHotspot(dbId, type = true) {
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
let scale = 1;
if (type) {
scale = 1.2;
}
const viewablesToUpdate = dbId;
dataVizExtn.invalidateViewables(viewablesToUpdate, (viewable) => {
return {
scale: scale, // Restore the viewable size
//url: "https://.../circle.svg",
};
});
}
//------------------- end --------------
//------------------ 熱圖 -------------------------------
async function loadHeatmaps(model) {
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
//取三個空調設備的位置打點
const devices = [
{
id: "Oficina 5",
//name: "Oficina-",
position: { x: 6.98, y: -19.00, 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.00, 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);
}
//------------ 剖面 ----------------------
async function getRemoteLevels() {
const aecData = await Autodesk.Viewing.Document.getAecModelData(this.viewer.model.getDocumentNode());
if (!aecData.levels) return null;
const levels2 = aecData.levels;
levels2.sort((a, b) => b.elevation - a.elevation);
return levels2;
}
async function getLevelsData(lowerFloor, upperFloor, callback = null) {
// 樓層正規化 取得樓層
const floorRegex = /[\d|\w]+F/gmi;
const data = await this.getRemoteLevels();
for (var i = 0; i < data.length; i++) {
let name = data[i].name?.match(floorRegex);
if (name && name[0] == lowerFloor) {
lowerIdx = i;
}
if (name && name[0] == upperFloor) {
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 = this.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);
this.viewer.setCutPlanes([upperCutPlaneParam, lowerCutPlaneParam]);
callback ? callback() : "";
}
//----------------- end -----------------------------------------------
//新增燈光
async function newLight(lightPosition) {
//聚光燈
var spotLight = new THREE.SpotLight(0xffffff, 200, 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) {
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 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) {
const model = viewer.model;
loadHeatmaps(model, roomArr);
}
function setShadowShow(type = false) {
viewer.setGroundShadow(type);
viewer.impl.sceneUpdated(true);
}
//============================= end ===================================
function moveViewToDevice(letter) {
if (letter != "") {
viewer.clearSelection();
viewer.select(letter);
viewer.fitToView([letter]);
}
else {
viewer.clearSelection();
}
}