Zoom camera inside room and show interior of the room - autodesk-forge

We are using forge viewer in our web application. We need to zoom camera inside room and show interior of the room.
Please help me to find out that how we can view room interior in forge viewer.

This is a duplicate question on SO, here is an already answered link: https://stackoverflow.com/a/65827867/5747150

You may check out this sample. It’s a revision of my sample forge-viewer-traveling-path. It will move the viewer camera smoothly to the center point of the room’s bounding box.
Demo: https://youtu.be/3MzihDJpi70
Here is the main extension: https://github.com/yiskang/forge-viewer-traveling-path/blob/room-navigation/public/js/MoveToRoomExt.js
/////////////////////////////////////////////////////////////////////
// Copyright (c) Autodesk, Inc. All rights reserved
// Written by Forge Partner Development
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
/////////////////////////////////////////////////////////////////////
(function () {
class RoomListPanel extends Autodesk.Viewing.UI.DockingPanel {
constructor(parent) {
const options = {};
// Height adjustment for scroll container, offset to height of the title bar and footer by default.
if (!options.heightAdjustment)
options.heightAdjustment = 70;
if (!options.marginTop)
options.marginTop = 0;
//options.addFooter = false;
const viewer = parent.viewer;
super(viewer.container, viewer.container.id + 'RoomListPanel', 'Rooms', options);
this.container.classList.add('adn-docking-panel');
this.container.classList.add('adn-room-list-panel');
this.createScrollContainer(options);
this.viewer = viewer;
this.parent = parent;
this.options = options;
this.uiCreated = false;
this.addVisibilityListener(async (show) => {
if (!show) return;
if (!this.uiCreated)
await this.createUI();
});
}
async createUI() {
this.uiCreated = true;
const div = document.createElement('div');
const treeDiv = document.createElement('div');
div.appendChild(treeDiv);
this.treeContainer = treeDiv;
this.scrollContainer.appendChild(div);
const data = await this.getRoomData();
this.buildTree(data);
}
async getRoomData() {
const getRoomDbIds = () => {
return new Promise((resolve, reject) => {
this.viewer.search(
'Revit Rooms',
(dbIds) => resolve(dbIds),
(error) => reject(error),
['Category'],
{ searchHidden: true }
);
});
};
const getPropertiesAsync = (dbId) => {
return new Promise((resolve, reject) => {
this.viewer.getProperties(
dbId,
(result) => resolve(result),
(error) => reject(error),
);
});
}
const data = [];
try {
const roomDbIds = await getRoomDbIds();
if (!roomDbIds || roomDbIds.length <= 0) {
throw new Error('No Rooms found in current model');
}
for (let i = 0; i < roomDbIds.length; i++) {
const dbId = roomDbIds[i];
const propData = await getPropertiesAsync(dbId);
data.push({
id: propData.externalId,
dbId,
name: propData.name
});
}
} catch (ex) {
console.warn(`[RoomListPanel]: ${ex}`);
throw new Error('Failed to extract room data');
}
return data;
}
getBoundingBox(dbId) {
const model = this.viewer.model;
const it = model.getInstanceTree();
const fragList = model.getFragmentList();
let bounds = new THREE.Box3();
it.enumNodeFragments(dbId, (fragId) => {
let box = new THREE.Box3();
fragList.getWorldBounds(fragId, box);
bounds.union(box);
}, true);
return bounds;
}
buildTree(data) {
const nodes = [];
for (let i = 0; i < data.length; i++) {
const node = {
id: data[i].id,
dbId: data[i].dbId,
type: 'spaces',
text: data[i].name
};
nodes.push(node);
}
console.log(nodes);
$(this.treeContainer)
.jstree({
core: {
data: nodes,
multiple: false,
themes: {
icons: false,
name: 'default-dark'
}
},
sort: function (a, b) {
const a1 = this.get_node(a);
const b1 = this.get_node(b);
return (a1.text > b1.text) ? 1 : -1;
},
checkbox: {
keep_selected_style: false,
three_state: false,
deselect_all: true,
cascade: 'none'
},
types: {
spaces: {}
},
plugins: ['types', 'sort', 'wholerow'],
})
.on('changed.jstree', async (e, data) => {
console.log(e, data);
console.log(data.node.original);
const { dbId } = data.node.original;
if (!dbId) return;
const bbox = this.getBoundingBox(dbId);
const center = bbox.center();
const point = new THREE.Vector3(center.x, center.y, bbox.min.z);
this.parent.tweenToPoint(point);
});
}
}
class MoveToRoomExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.cameraTweenTool = null;
this.uiCreated = false;
}
onToolbarCreated(toolbar) {
const panel = new RoomListPanel(this);
viewer.addPanel(panel);
this.panel = panel;
const roomsPanelButton = new Autodesk.Viewing.UI.Button('room-panel-button');
roomsPanelButton.onClick = () => {
panel.setVisible(!panel.isVisible());
};
roomsPanelButton.setToolTip('Open room list panel');
this.group = new Autodesk.Viewing.UI.ControlGroup('room-nav-tool-group');
this.group.addControl(roomsPanelButton);
toolbar.addControl(this.group);
}
tweenToPoint(point) {
this.viewer.setActiveNavigationTool('bimwalk');
const views = [];
const up = new THREE.Vector3(0, 0, 1);
const currentEye = this.viewer.navigation.getPosition().clone();
const targetPos = point.clone().add(up.clone().multiplyScalar(1.7 * 3.2808399));
const sightDir = point.clone().sub(currentEye).normalize();
const eyeLen = this.viewer.navigation.getEyeVector().length();
const target = targetPos.clone().add(sightDir.clone().multiplyScalar(eyeLen));
views.push({
up: up.toArray(),
eye: targetPos.toArray(),
target: target.toArray()
});
this.processTweens(views);
}
executeTweenPromised(view) {
return new Promise((resolve, reject) => {
const onTweenExecuted = (event) => {
console.log(event);
this.viewer.removeEventListener(
Autodesk.ADN.CameraTweenTool.CAMERA_TWEEN_ANIMATION_COMPLETED_EVENT,
onTweenExecuted
);
resolve();
};
this.viewer.addEventListener(
Autodesk.ADN.CameraTweenTool.CAMERA_TWEEN_ANIMATION_COMPLETED_EVENT,
onTweenExecuted
);
this.cameraTweenTool.tweenCameraTo({ viewport: view });
});
}
processTweens(data) {
//process each promise
//refer to http://jsfiddle.net/jfriend00/h3zaw8u8/
const promisesInSequence = (tasks, callback) => {
const results = [];
return tasks.reduce((p, item) => {
return p.then(() => {
return callback(item).then((data) => {
results.push(data);
return results;
});
});
}, Promise.resolve());
};
//start to process
return promisesInSequence(data, (d) => this.executeTweenPromised(d));
}
async load() {
const loadCSS = (href) => new Promise(function (resolve, reject) {
const el = document.createElement('link');
el.rel = 'stylesheet';
el.href = href;
el.onload = resolve;
el.onerror = reject;
document.head.appendChild(el);
});
await Promise.all([
Autodesk.Viewing.Private.theResourceLoader.loadScript('https://unpkg.com/#tweenjs/tween.js#18.6.4/dist/tween.umd.js', 'TWEEN'),
Autodesk.Viewing.Private.theResourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js', '$'),
Autodesk.Viewing.Private.theResourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js', '$'),
loadCSS('https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css'),
loadCSS('https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default-dark/style.min.css'),
this.viewer.loadExtension('Autodesk.BimWalk'),
this.viewer.loadExtension('Autodesk.ADN.CameraTweenTool')
]);
this.viewer.setBimWalkToolPopup(false);
this.cameraTweenTool = this.viewer.getExtension('Autodesk.ADN.CameraTweenTool');
console.log('MoveToRoomExtension has been loaded.');
return true;
}
async unload() {
this.viewer.unloadExtension('Autodesk.ADN.CameraTweenTool');
this.viewer.setBimWalkToolPopup(true);
delete this.cameraTweenTool;
this.cameraTweenTool = null;
console.log('MoveToRoomExtension has been unloaded.');
return true;
}
}
Autodesk.Viewing.theExtensionManager.registerExtension('Autodesk.ADN.MoveToRoomExtension', MoveToRoomExtension);
})();

Related

Component responsible to display all my transactions are not updating after submit

I'm using redux toolkit with react and have a basic setup because I'm building a simple expense tracker, so I have two operations: get all transactions and add a new transaction. That's it.
My problem: When I create a new transaction the component responsible for displaying my data does not update and I can only see the changes after refreshing the page.
Below you can see my transactionSlice file:
const initialState = {
transactions: [],
loading: false,
error: null,
}
export const getTransactions = createAsyncThunk(
"transactions/getTransactions",
async () => {
const res = await axios.get('http://localhost:8000/transactions')
return res.data
}
)
export const addTransaction = createAsyncThunk(
"transaction/addTransaction",
async(data) => {
const res = await axios.post('http://localhost:8000/transactions', data);
return res.data
}
)
const transactionsSlice = createSlice({
name: 'transactions',
initialState,
reducers: {},
extraReducers: {
[getTransactions.pending]: (state) => {
state.loading = true;
},
[getTransactions.fulfilled]: (state, {payload}) => {
console.log(payload);
state.loading = false;
state.transactions = payload;
state.error = ''
},
[getTransactions.rejected]: (state) => {
state.loading = false;
state.error = state.error.message;
},
[addTransaction.pending]: (state) => {
state.loading = true;
},
[addTransaction.fulfilled]: (state) => {
state.loading = false;
},
[addTransaction.rejected]: (state) => {
state.loading = false;
state.error = state.error.message;
}
}
});
and here is the code from the component where I'm displaying all transactions
const { transactions, loading } = useSelector(selectAllTransactions);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTransactions());
}, [dispatch]);
but when I make a post request my state with all transactions doesn't update immediately. I can only see the changes if I update the page and I'm doing it manually. I'm wondering why is this happening if I have useEffect watching for changes?
AddTransaction.js file :
const [transactionName, setTransactionName] = useState('');
const [amount, setAmount] = useState('');
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
const data = {
transactionName,
amount
}
if(transactionName && amount){
dispatch(addTransaction(data));
dispatch(getTransactions());
setTransactionName('')
setAmount('');
}
}
I've tried to google it but it seems my doubt is so silly that I can't even find an answer for that.
Here is my server file:
app.post('/transactions',(req, res) => {
const {transactionName, amount} = req.body;
const query = `INSERT INTO transactions (title, amount)
VALUES ("${transactionName}", "${amount}")`
db.query(query, (err, result) => {
if(err){
console.log(err)
}
res.send(result)
})
});
Am I missing something? Could someone explain to me why the component responsible to display all transactions are not updating after submit, please?
Try executing getTransactions once addTransaction(data) is finished, not at the same time:
const handleSubmit = (e) => {
e.preventDefault();
const data = {
transactionName,
amount
}
if(transactionName && amount){
dispatch(addTransaction(data))
.then(() => {
dispatch(getTransactions())
setTransactionName('')
setAmount('')
}
}
}

forge viewer isolatemulti models in the veiwer

i am trying to isolate an array of dbid in the viewer that contains multi models. but the problem sometimes isolate model A and sometimes model B is there any way to isolate both models at the same time?
function getExternalIds(model: any): any {
return new Promise(function(resolve, reject) {
model.getExternalIdMapping(resolve, reject);
});
}
//---------------------------------------------
async function isolateAndColorObject(
viewer: any,
externalId ? : string[],
color ? : any
) {
const externalIds = await getExternalIds(viewer.model);
let arr: any = [];
externalId ? .forEach((id) => {
const dbid = externalIds[id];
arr.push(dbid);
});
for (const model of viewer.getAllModels()) { //viewer.getVisibleModels()
viewer.isolate(arr, model);
}
}
//calling the function:
isolateAndColorObject(viewer, [
"5aa7c220-434e-47ec-966b-7aa35a5327a9-001c988b",
"5cd83cb7-08c9-4bb2-bf3a-523af6622a4f-000e9567",
"4845d7d6-c3ba-433c-9418-acbdb1ff7e5f-0011d9e2",
"7e6d9dcb-b26f-4e71-9eb2-3169d28411da-001e3370",
]);
update: I have fixed
function getExternalIds(
model: Autodesk.Viewing.Model
): Promise<{ [key: string]: number }> {
return new Promise(function (resolve, reject) {
model.getExternalIdMapping(resolve, reject);
});
}
async function isolateAndColorObject(
viewer: Autodesk.Viewing.GuiViewer3D,
externalIds: string[]
) {
let neededDbId: number[] = [];
const allModels = viewer.getAllModels();
if (allModels) {
for await (const model of allModels) {
const iExIds = await getExternalIds(model);
if (iExIds) {
externalIds.forEach((id: string) => {
const dbid = iExIds[id];
neededDbId.push(dbid);
});
}
viewer.isolate(neededDbId, model);
}
}
}

Why do sometimes informations not are gathering using puppeter?

I'm using puppeter for gathering information and something one of these informations cames []. This is an example of how I'm calling the functions of puppeter
try {
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
],
});
const page = await browser.newPage();
await page.goto(url);
await page.waitForSelector('div#results');
await page.waitForSelector('h6.bestPrice');
const result = await page.evaluate(() => {
let items = document.querySelectorAll('maintable > tbody > tr.productsName > td > div.products');
if (items.length !== 0) {
let data = [];
items.forEach(function (el) {
let object = null;
el.querySelectorAll('h5 a').forEach(function (el) {
object = { product: el.innerHTML, img: [] };
});
el.querySelectorAll('p a').forEach(function (el) {
object.description = el.innerHTML;
});
el.querySelectorAll('h6.bestPriceText').forEach(function (el) {
object.price = el.innerHTML;
});
el.querySelectorAll('div.slide a.fancybox-thumbs img').forEach(function (el) {
object.img.push(el.getAttribute('src'));
});
data.push(object);
});
return data;
}
let error = null;
document.querySelectorAll('div.noResults div.info-message h2').forEach(function (el) {
error = ({ message: el.innerHTML });
});
return error;
});
await browser.close();
return result;
} catch (error) {
return { message: `Problems loading the page! Please, try again.${error}` };
}
};
The information which something comes empty is price, sorry but I can't reveal the url, but I would like to know if exists any problem with this design code.
A Friend suggest me to add a timeout, because it could be a problem with rendering
const resultado = await page.evaluate(async () => {
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
await timeout(5000);

What's the best way to mock a nested function?

consider a function
exports.projectNotifyLaunch = (admin, functions) => {
return functions.database.ref("/projects/{pid}").onCreate(snap => {
const { title } = snap.val();
const notification = {
title: `${title} just launched!`,
body: `We just heard about a new cryptocurrency project called ${title}`
};
return admin.messaging().sendToTopic("premium", { notification });
});
};
How should I mock deeply nested functions such as
functions.database.ref("/projects/{pid}").onCreate(snap => {});
or
admin.messaging().sendToTopic("premium", { notification });
in Jest? I want to fire off the snap=>{} callback and assert against the value of notification.
I was able to make this work
This works but it's quite verbose. I'm wondering if there is a better way, or a type of testing I'm not aware of with Jest.
describe("send notification to premium users on new project", () => {
// INPUTS
const snap = {
val: () => ({
title: "Test Title"
})
};
const functions = {
database: {
ref: () => ({
onCreate: callback => callback(snap)
})
}
};
// outputs
let topicStub = null;
let notificationStub = null;
const admin = {
messaging: () => ({
sendToTopic: (topic, notification) => {
topicStub = topic;
notificationStub = notification;
}
})
};
projectNotifyLaunch(admin, functions);
test("title is correct", () => {
expect(notificationStub.notification.title).toBe(
"Test Title just launched!"
);
});
test("topic is premium", () => {
expect(topicStub).toBe("premium");
});
});

Get the Camera position and restore it in Forge Viewer for a virtual visit

I'm trying to put the camera correctly on dbId defines.
For this, I run in the javascript console this command after put the view like I want :
JSON.stringify(v.navigation.getCamera());
But if I make a test and try to load the result directly, I have an error :
v.navigation.setCamera(JSON.parse("{\"metadata\":{\"version\":4.3,\"type\":\"Object\",\"generator\":\"Ob...."))
Error :
camera.up is undefined
In other words, how can I save the camera position manually and restore it?
EDIT
I try to do this with setViewFromArray :
viewerApp.myCurrentViewer.setViewFromArray([
454.76857106060265,
96.01886808305997,
212.6431659314611,
287.11932000223214,
167.19053946002487,
97.17925996096139,
-0.49285695792051964,
0.20923119682030047,
0.8445793777416518,
2.7467811158798283,
45.00000125223908,
1,
1
]);
But it zoom to much IN the object and beyond...
EDIT 2
I've found a solution with restoreState() and getState() but it moving to fast and these methods do not seem to be queued.
I would like to make a virtual visit of my building...
Edit 3
I've try to use your function. So I've migrate it to use it in a Javascript file :
var animate = false;
function tweenCameraTo(viewer, state) {
var targetTweenEasing = {
id: TWEEN.Easing.Linear.None,
name: 'Linear'
};
var posTweenEasing = {
id: TWEEN.Easing.Linear.None,
name: 'Linear'
};
var upTweenEasing = {
id: TWEEN.Easing.Linear.None,
name: 'Linear'
};
const targetEnd = new THREE.Vector3(
state.viewport.target[0],
state.viewport.target[1],
state.viewport.target[2])
const posEnd = new THREE.Vector3(
state.viewport.eye[0],
state.viewport.eye[1],
state.viewport.eye[2])
const upEnd = new THREE.Vector3(
state.viewport.up[0],
state.viewport.up[1],
state.viewport.up[2])
const nav = viewer.navigation
const target = new THREE.Vector3().copy(
nav.getTarget())
const pos = new THREE.Vector3().copy(
nav.getPosition())
const up = new THREE.Vector3().copy(
nav.getCameraUpVector())
//nav.setView (posEnd, targetEnd);
//nav.setCameraUpVector(upEnd);
var targetTween = createTween({
easing: targetTweenEasing.id,
onUpdate: (v) => {
nav.setTarget(v)
},
duration: 25000, //targetTweenDuration,
object: target,
to: targetEnd
}).then((r) => {console.log("targetTween");});
var posTween = createTween({
easing: posTweenEasing.id,
onUpdate: (v) => {
nav.setPosition(v)
},
duration: 25000,//posTweenDuration,
object: pos,
to: posEnd
}).then((r) => {console.log("posTween");});
var upTween = createTween({
easing: upTweenEasing.id,
onUpdate: (v) => {
nav.setCameraUpVector(v)
},
duration: 25000, //upTweenDuration,
object: up,
to: upEnd
}).then((r) => {console.log("upTween");});
Promise.all([
targetTween,
posTween,
upTween]).then(() => {
console.log("Fin animation");
animate = false;
})
runAnimation(true);
}
// var animId = null;
var runAnimation = function runAnimation (start) {
if(start || animate){
requestAnimationFrame(runAnimation);
TWEEN.update()
}
}
function createTween (params) {
return new Promise((resolve) => {
console.log("params.to", params.to);
new TWEEN.Tween(params.object)
.to(params.to, params.duration)
.onComplete(() => {resolve();})
.onUpdate(params.onUpdate)
.easing(params.easing)
.start()
})
}
But When I try to use it, it drive me in a big zoom, not in front of my building for example. I've miss something but I do not see what.
tweenCameraTo(viewer, {"viewport":{"name":"","eye":[888.5217895507812,-257.4985656738281,576.9136962890625],"target":[262.7552795410156,81.58747863769531,73.64283752441406],"up":[0,0,1],"worldUpVector":[0,0,1],"pivotPoint":[262.7552795410156,81.58747863769531,73.64283752441406],"distanceToOrbit":871.6906720725796,"aspectRatio":2.7507163323782233,"projection":"perspective","isOrthographic":false,"fieldOfView":45.00000125223908}});
tweenCameraTo(viewer, {"viewport":{"name":"","eye":[243.36675374870242,423.8180714045694,167.78380714288494],"target":[303.9841786300087,-347.23884414908446,-234.26269334678466],"up":[0.03614822612815841,-0.4598073869962326,0.8872826340076113],"worldUpVector":[0,0,1],"pivotPoint":[262.7552795410156,81.58747863769531,73.64283752441406],"distanceToOrbit":347.4897746012467,"aspectRatio":2.7507163323782233,"projection":"perspective","isOrthographic":false,"fieldOfView":45.00000125223908}});
This blog can probably help you: Smooth Camera Transitions in the Forge Viewer
/////////////////////////////////////////////////////////
// Smooth camera transition from current state to
// target state using Tween.js
//
/////////////////////////////////////////////////////////
tweenCameraTo (state) {
// tween parameters, specific to my app but easy
// to adapt ...
const {
targetTweenDuration,
posTweenDuration,
upTweenDuration,
targetTweenEasing,
posTweenEasing,
upTweenEasing
} = this.react.getState()
const targetEnd = new THREE.Vector3(
state.viewport.target[0],
state.viewport.target[1],
state.viewport.target[2])
const posEnd = new THREE.Vector3(
state.viewport.eye[0],
state.viewport.eye[1],
state.viewport.eye[2])
const upEnd = new THREE.Vector3(
state.viewport.up[0],
state.viewport.up[1],
state.viewport.up[2])
const nav = this.navigation
const target = new THREE.Vector3().copy(
nav.getTarget())
const pos = new THREE.Vector3().copy(
nav.getPosition())
const up = new THREE.Vector3().copy(
nav.getCameraUpVector())
const targetTween = this.createTween({
easing: targetTweenEasing.id,
onUpdate: (v) => {
nav.setTarget(v)
},
duration: targetTweenDuration,
object: target,
to: targetEnd
})
const posTween = this.createTween({
easing: posTweenEasing.id,
onUpdate: (v) => {
nav.setPosition(v)
},
duration: posTweenDuration,
object: pos,
to: posEnd
})
const upTween = this.createTween({
easing: upTweenEasing.id,
onUpdate: (v) => {
nav.setCameraUpVector(v)
},
duration: upTweenDuration,
object: up,
to: upEnd
})
Promise.all([
targetTween,
posTween,
upTween]).then(() => {
this.animate = false
})
this.runAnimation(true)
}