Autodesk Forge - Browser labels problems - autodesk-forge

I have a problem with Forge browser using AggregatedView.
If I load 3 models in the viewer, in the browser appears label "Model" 3 times, instead of revit file name ("1.rvt", "2.rvt" e "3.rvt"), as reported in the attached imageforge browser.
Is it possible to change the behavior (using some options)?
I've tried to use "accessControlProperties" option (https://forge.autodesk.com/en/docs/viewer/v2/reference/javascript/document/#load-documentid-onsuccesscallback-onerrorcallback-accesscontrolproperties) but nothing changes.
TIA
Alder
function fetchForgeToken( callback ) {
callback( "eyJhbGciOiJSUzI1NiIsImtpZCI6IlU3c0dGRldUTzlBekNhSzBqZURRM2dQZXBURVdWN2VhIn0.eyJzY29wZSI6WyJkYXRhOnJlYWQiLCJkYXRhOmNyZWF0ZSIsImRhdGE6d3JpdGUiLCJ2aWV3YWJsZXM6cmVhZCJdLCJjbGllbnRfaWQiOiJpSGVHMlRrbW9ucmF6TjUyS1JoRUdHWnZFTVU3RWg3dCIsImF1ZCI6Imh0dHBzOi8vYXV0b2Rlc2suY29tL2F1ZC9hand0ZXhwNjAiLCJqdGkiOiJpVEVVZTdYdVRqam9MVzFnNzQyYWNrREtkd244Q3ZLRHZLcXI1NzBrVkk2ZUR6dzRlNDc3N1BkeFNhelRCWWo3IiwiZXhwIjoxNjI3MzAzNjQ0fQ.VNZZHhuo4fGLsy-StB_YLkEtFNphPuuTDCJt-NtAvzGOD8_dsRjj6szBm9-rXNqKIBUVeg3DkYHG3jfSFQeAN_ie76H2_fVs-zmHMelNi6jZBvyM6DGoE62068TvKpEK9_8po__YstQ1JygEuUyiqivXE0-RqYEjd15wNrb1PUqVJTA-Ug4lEEIGmFNyhQsW861MwcRklkcHzhC9gDijQKqcDAqC_udueWZ5Bh48SPtNNJe4bXFwWcWqEipom-apyArd1nLw9-fpmNd-qPgSQJwNo_0sk-wEIn1Zl6Uq8k67f1bBXBrg7XfUAS2-_M_YOTrHZFl6QVEQ-fvKr4PR9g", "2000" );
}
function launchViewer( models ) {
if( !models || models.length <= 0 )
return console.error( 'Empty model input' );
const options = {
env: 'AutodeskProduction',
getAccessToken: fetchForgeToken
};
const options3d = {
viewerConfig: {
disableBimWalkInfoIcon: true
}
};
function loadManifest( documentId, documentName ) {
return new Promise(( resolve, reject ) => {
const onDocumentLoadSuccess = ( doc ) => {
//doc.downloadAecModelData();
doc.downloadAecModelData(() => resolve(doc));
};
console.log("doc.id = " + documentId);
//var accessControlProperties = {name: documentName};
var accessControlProperties = {modelNameOverride: documentName};
//https://forge.autodesk.com/en/docs/viewer/v2/reference/javascript/document/#load-documentid-onsuccesscallback-onerrorcallback-accesscontrolproperties
//oppure
//https://forge.autodesk.com/blog/customizing-model-browser-custom-label-behavior-styling-and-data-sources
Autodesk.Viewing.Document.load( documentId, onDocumentLoadSuccess, reject, accessControlProperties );
});
}
Autodesk.Viewing.Initializer( options, function() {
//get the viewer div
const viewerDiv = document.getElementById( 'viewer' );
//initialize the viewer object
const view = new Autodesk.Viewing.AggregatedView();
view.init( viewerDiv, options3d );
const viewer = view.viewer;
const tasks = [];
models.forEach( md => tasks.push( loadManifest( md.urn, md.name ) ) );
Promise.all(tasks)
.then( docs => Promise.resolve( docs.map( doc => {
const bubbles = doc.getRoot().search({type:'geometry', role: '3d'});
const bubble = bubbles[0];
if( !bubble ) return null;
return bubble;
})))
.then( bubbles => view.setNodes( bubbles ) );
});
}
const models = [
{ name: '1.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLm5kLXcyZUNSU3ktdHhHTmZ6OWo4bFE/dmVyc2lvbj0x' },
{ name: '2.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLkxHVDFOSW1nUlgtZnN0cnhMZUU5aUE/dmVyc2lvbj0x' },
{ name: '3.rvt', urn: 'urn:dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLjhTbE1zOGNWUllTenV1TGR6SXJiWHc/dmVyc2lvbj0x' }
];
launchViewer( models.concat() );

To assign modelNameOverride, we need to take advantage of getCustomLoadOptions. See below code snippet for example:
const options3d = {
viewerConfig: {
disableBimWalkInfoIcon: true,
},
getCustomLoadOptions: (bubble) => {
const modelName = bubble.getDocument().getRoot().search({ role: "viewable" })[0].data.name;
console.log(modelName);
let loadOptions = { modelNameOverride: modelName };
return loadOptions;
}
};
view.init( viewerDiv, options3d );

Related

Cesium ImageryLayer Not Working on Map load

When the map loads and this component gets hit my layer doesn't show up and it doesn't make a network request to OpenSeaMap. However, if I set a timer the layer will show up after 10 seconds (arbitrary number). I tried checking if Cesium's viewer or camera were defined, before loading, but that didn't work.
I am at a loss of what a good solution is here, this clearly isn't it. Both provider objects are exactly the same when it works vs. when it doesn't.
export const TiledImageryOverlay = memo(({ urlTemplate, bounds, blackAsAlpha }: ITiledImageryOverlayProps) => {
const boundingRectangle = bounds
? Rectangle.fromCartographicArray(
bounds.map(point => Cartographic.fromDegrees(point.lon ?? 0 - 1, point.lat ?? 0))
)
: undefined;
const provider = useMemo(() => {
return new UrlTemplateImageryProvider({
url: urlTemplate,
rectangle: boundingRectangle
});
}, [urlTemplate, boundingRectangle]);
const layerAlpha = blackAsAlpha ? new Color(0, 0, 0, 0) : undefined;
return <ImageryLayer imageryProvider={provider} rectangle={boundingRectangle} colorToAlpha={layerAlpha} />;
});
export const TiledImageryOverlay = memo(({ urlTemplate, bounds, blackAsAlpha }: ITiledImageryOverlayProps) => {
const [show, setShow] = useState<boolean>(false);
const boundingRectangle = bounds
? Rectangle.fromCartographicArray(
bounds.map(point => Cartographic.fromDegrees(point.lon ?? 0 - 1, point.lat ?? 0))
)
: undefined;
useEffect(() => {
setTimeout(() => {
setShow(true);
}, 10000);
}, [setShow]);
const provider = useMemo(() => {
return new UrlTemplateImageryProvider({
url: urlTemplate,
rectangle: boundingRectangle
});
}, [urlTemplate, boundingRectangle]);
const layerAlpha = blackAsAlpha ? new Color(0, 0, 0, 0) : undefined;
if (!show) return null;
return <ImageryLayer imageryProvider={provider} rectangle={boundingRectangle} colorToAlpha={layerAlpha} />;
});

Zoom camera inside room and show interior of the room

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);
})();

Problems working with new AggregatedView, I need an usage example of this class

I'm trying to load 3 different models in the viewer, and later access the properties in each model, for selecting parts...
The viewer is always looking to the first loaded model, I need to change this. Reading about this I discovered the new AggregatedView class.
Following the documentation I got this:
var view = new Autodesk.Viewing.AggregatedView();
const bubbleNodes = [];
function loadModels(docs) {
const options = {
env: 'AutodeskProduction',
getAccessToken: getForgeToken
};
// Initialize and load a document.
Autodesk.Viewing.Initializer(options, function onInitialized() {
// Get the Viewer DIV
var htmlDiv = document.getElementById('forgeViewer');
// Initialize the AggregatedView view
view.init(htmlDiv, options).then(function () {
console.log(docs)
for (let i = 0; i < docs.length; i++) {
Autodesk.Viewing.Document.load(docs[i], (doc) => {
// Set the nodes from the doc
var nodes = doc.getRoot().search({ type: 'geometry' });
bubbleNodes.push(nodes[0]);
}, (errorCode, errorMsg, messages) => {
// Do something with the failed document.
// ...
console.log(errorCode, errorMsg, messages)
});
}
view.setNodes(bubbleNodes);
});
});
}
I'm always getting this in the error callback --> 5 'Error: 404 (Not Found)' 404
I don`t know what I'm doing wrong.
The Autodesk.Viewing.Document.load is an async task, you cannot use it as you posted above. So, here you go:
<!DOCTYPE html>
<html>
<head>
<title>Multiple 3D Viewer - Autodesk Forge AggregatedView</title>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<!-- The Viewer CSS -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" type="text/css">
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="viewer"></div>
<!-- The Viewer JS -->
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.js"></script>
<!-- Developer JS -->
<script>
function fetchForgeToken( callback ) {
fetch( 'https://127.0.0.1:8080/api/forge/oauth/token', {
method: 'get',
headers: new Headers({ 'Content-Type': 'application/json' })
})
.then( ( response ) => {
if( response.status === 200 ) {
return response.json();
} else {
return Promise.reject(
new Error( `Failed to fetch token from server (status: ${response.status}, message: ${response.statusText})` )
);
}
})
.then( ( data ) => {
if( !data ) return Promise.reject( new Error( 'Empty token response' ) );
callback( data.access_token, data.expires_in );
})
.catch( ( error ) => console.error( error ) );
}
function launchViewer( models ) {
if( !models || models.length <= 0 )
return console.error( 'Empty model input' );
const options = {
env: 'AutodeskProduction',
getAccessToken: fetchForgeToken
};
const options3d = {
viewerConfig: {
disableBimWalkInfoIcon: true
}
};
function loadManifest( documentId ) {
return new Promise(( resolve, reject ) => {
const onDocumentLoadSuccess = ( doc ) => {
doc.downloadAecModelData(() => resolve(doc));
};
Autodesk.Viewing.Document.load( documentId, onDocumentLoadSuccess, reject );
});
}
Autodesk.Viewing.Initializer( options, function() {
//get the viewer div
const viewerDiv = document.getElementById( 'viewer' );
//initialize the viewer object
const view = new Autodesk.Viewing.AggregatedView();
view.init( viewerDiv, options3d );
const viewer = view.viewer;
const tasks = [];
models.forEach( md => tasks.push( loadManifest( md.urn ) ) );
Promise.all(tasks)
.then( docs => Promise.resolve( docs.map( doc => {
const bubbles = doc.getRoot().search({type:'geometry', role: '3d'});
const bubble = bubbles[0];
if( !bubble ) return null;
return bubble;
})))
.then( bubbles => view.setNodes( bubbles ) );
});
}
const models = [
{ name: 'A.rvt', urn: 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2Utb3RnLWxrd2VqN3hwYmdwNjN4NGhsMzM1eTZtMjZvYWtnZ29mL0EucnZ0' },
{ name: 'B.rvt', urn: 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2Utb3RnLWxrd2VqN3hwYmdwNjN4NGhsMzM1eTZtMjZvYWtnZ29mL0IucnZ0' },
{ name: 'C.rvt', urn: 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2Utb3RnLWxrd2VqN3hwYmdwNjN4NGhsMzM1eTZtMjZvYWtnZ29mL0MucnZ0' },
{ name: 'D.rvt', urn: 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2Utb3RnLWxrd2VqN3hwYmdwNjN4NGhsMzM1eTZtMjZvYWtnZ29mL0QucnZ0' }
];
launchViewer( models.concat() );
</script>
</body>
</html>
ref: https://gist.github.com/yiskang/c404af571ba4d631b5929c777503891e

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)
}