Forge Viewer Select in a multi-model context - autodesk-forge

We have extensions that currently leverage viewer.select() with a list of dbIds from the model.
Our customers would like to see secondary models in the same viewer, and we’re giving them the ability to load reference models after the first model has been loaded.
We’re running into a problem with multiple models, however, where the viewer is selecting from one of the models other than the first model loaded when we call viewer.select().
It seems like we may want to stop using viewer.select() but instead start using model.selector.select() after keeping a reference to the first model loaded. This would mean changing quite a bit of code.
Is there a way to set the context of viewer.select() so that it always uses the first model we load?

Before Forge Viewer v3.3, Viewer3D#select( dbIds, selectionType) didn't be exposed for the multi-model use case unfortunately. The 2nd argument of Viewer3D#select has been changed to Viewer3D#select( dbIds, model ). So, the below code snippets will changed to:
var scene = viewer.impl.modelQueue();
var models = scene.getModels();
var targetIndex = ...;
var targetModel = models[targetIndex];
var selectionType = ...;
// Method 1:
viewer.impl.selector.setSelection( dbIds, targetModel, selectionType );
// Method 2:
model.selector.select( dbIds, selectionType );
// Method 3: (After Forge Viewer v4)
viewer.select( dbIds, targetModel );
// Method 4: (After Forge Viewer v4)
var selections = [
{
model: targetModel,
ids: dbIds
}
];
viewer.impl.selector.setAggregateSelection( selections );
==== Update End ====
Unfortunately, Viewer3D#select didn't be exposed for the multi-model use case. However, there are few ways to select items via the API in multi-model environment:
var scene = viewer.impl.modelQueue();
var models = scene.getModels();
var targetIndex = ...;
var targetModel = models[targetIndex];
var selectionType = ...;
// Method 1:
viewer.impl.selector.setSelection( dbIds, targetModel, selectionType );
// Method 2:
model.selector.select( dbIds, selectionType );
// Method 3: (After Forge Viewer v4)
var selections = [
{
model: targetModel,
ids: dbIds
}
];
viewer.impl.selector.setAggregateSelection( selections );
Or, you can write your own Viewer class which extends Autodesk.Viewing.Viewer3D or Autodesk.Viewing.Private.GuiViewer3D to private a select function that supports passing model argument.

Related

Transform point from DWG model coordinates to autodesk forge viewer coordinates (new WGS.LmvMatrix4 undefined)

I am trying to display a point on model displayed in the Autodesk forge viewer. However I am unable to figure out how to transform the point. I found the next question that seems to resolve this question:
Transform point from DWG model coordinates to autodesk forge viewer coordinates
When I try to use the function of this question:
var vpXform = viewer.model.getPageToModelTransform(viewportId).clone();
var invVpXform = new WGS.LmvMatrix4(true);
invVpXform.getInverse(vpXform, true);
var ptInCadX = ...;
var ptInCadY = ...;
var verticesInViewer = new THREE.Vector3().set(ptInCadX, ptInCadY, 0).applyMatrix4(invVpXform);
It shows the next error: Uncaught ReferenceError: WGS is not defined
And if I try it without the WGS.LmvMatrix4, it shows the following error: Uncaught ReferenceError: new LmvMatrix4 is not defined
I'm using the latest version of the Forge Viewer, v7.Can someone help me finding what am I doing wrong?
Thank you very much.
Here's an updated version of the snippet:
// Start by getting a list of all viewports in your model
let viewports = viewer.model.getData().viewports;
// Choose a viewport you want to use for the design-to-viewer coordinate mapping (it must include a `transform` property);
// in my case I'll just pick the first one
let viewport = viewports[0];
// Load the viewport transform into a three.js matrix
let xform = new THREE.Matrix4().fromArray(viewport.transform);
// Compute the inverse transxform
let inverse = new THREE.Matrix4().getInverse(xform, true);
// Use the inverse transform to convert design coordinates to paper coordinates
let someX = 0, someY = 0;
let viewerCoords = new THREE.Vector3().set(someX, someY, 0).applyMatrix4(inverse);

Creating custom geometry on Forge Viewer

I am trying to include the snapshot for each issue which includes the highlighted issue with the complete plan, which I intend to use in a custom report. The files are in PDF format for the plan.
Now, the optimal workflow which I feel would be the following:
1) Create a custom geometry (circle) at the issue location.
2) Take a snapshot of the viewer and save it at a location
I have been trying the below code for step 1:
var pushpinAttributes = issue.attributes.pushpin_attributes;
var viewer = viewerApp.getCurrentViewer();
var geom = new THREE.SphereGeometry(0.05, 32, 32);
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var sphereMesh = new THREE.Mesh(geom, material);
sphereMesh.position.set(pushpinAttributes.location.x, pushpinAttributes.location.y, pushpinAttributes.location.z);
viewer.impl.createOverlayScene ('overlay-scene', material);
viewer.impl.addOverlay('overlay-scene', sphereMesh);
viewer.impl.invalidate (true);
For step 2, I am using viewer.getScreenShot();
However, the geometry is created at a different location than that of the pushpin.
Where could I be going wrong?
Is there be a better way of doing the same?

Viewing Revit Drafting Views with the Forge API's

It seems to be possible to view and navigate Revit Drafting View(s) graphics using the Forge API's based on how the BIM 360 Document Management web browser interface does it with our own Revit (.rvt) model having published Drafting Views where it list the 2D Drafting Views (thumbnails) in the left panel and the actual detail of the selected Drafting View in the right viewer. We do have one of the more recent Forge API Viewer examples setup and working, and tried modifying some of its code, but is seems to be designed to only work with Models (.rvt) components in the left panel, and its not obvious where and what code needs to be modify to change it to list 2D sheets/views like the Document Manager does. We are having difficulty locating a Forge API example that shows how to do this using the Forge API's and would like to obtain a working example that illustrates how to do this using the Forge API's?
Tried changing the ViewingApplication.bubble.search to include role 2d type view
function onDocumentLoadSuccess(doc) {
// We could still make use of Document.getSubItemsWithProperties()
// However, when using a ViewingApplication, we have access to the
*bubble** attribute,
// which references the root node of a graph that wraps each object from the Manifest JSON.
//var viewables = viewerApp.bubble.search({ 'type': 'geometry' });
var viewables = viewerApp.bubble.search({ 'role': '2d', 'type': 'view' });
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
The drafting view is kind of 2d role geometry, Therefore you can load it with the same way for the 2D view.
const rootItem = doc.getRoot();
const filter = { type: 'geometry', role: '2d' };
const viewables = rootItem.search( filter );
if( viewables.length === 0 ) {
return onLoadModelError( 'Document contains no viewables.' );
}
// Take the first viewable out as the loading target
const initialViewable = viewables[0];
const loadOptions = {
sharedPropertyDbPath: doc.getPropertyDbPath()
};
viewer.loadDocumentNode(doc, initialViewable.data, modelOptions).then(onItemLoadSuccess).catch(onItemLoadFail);
To show a list of views like the BIM360 Docs, you could load Autodesk.DocumentBrowser extension. It will show viewable items on it, and just click on it to switch. See below snapshot:

Unable to retrieve frag IDs in autodesk forge viewer

I am currently looking at trying to get a very simple solution working as a proof of concept before going any further with forge. I have gone through the basic application quick start guide and the basic extension guide and the viewer it self works perfectly with a file I created using Autodesk pipe 3D (tried both as a dwg file and a dwfx).
All I am trying to do is change the colour of the model, all the guides I could find all use similar methods but they all require you to get the list of FragIDs to apply a material
var it = viewer.model.getData().instanceTree;
var allDbIds = Object.keys(it.nodeAccess.dbIdToIndex);
for (var i=0; i<allDbIds.length; i++) {
var dbid = allDbIds[i];
var fragIds = []
try {
it.enumNodeFragments(dbid, function(fragId){
fragIds.push(fragId)
})
}catch(error) {
console.error(error);
}
fragIds.forEach(function(fragId) {
//code to actually change colour
})
It manages to get the dbid fine and brings back many however when it tries to get the frag ids it doesnt bring back anything. It does not break it just brings back nothing but am having trouble finding where to actually look in to why or where to even start
First thing would be to define the fragIds array outside of the for loop, so it's not getting overridden at each iteration.
You can use the following code to set a specific material to a fragId:
fragIds.forEach((fragId) => {
model.getFragmentList().setMaterial(
fragId, material)
})
Take a look at this thread for more info how the topic:
Forge Viewer THREE.MeshLambertMaterial

How to Retrieve Forge Viewer objectTree?

My goal is to highlight a room by adding new geometry to the viewer based on lines I have created in revit like they do here Link
but i can not figure out how to access those lines ids.
I know what they are in revit (element_id) but not how they are mapped as dbid.
Following this Blog Post
I want to access the objectTree in my extension to find out, but it always comes back as undefined.
var tree;
//old way - viewer is your viewer object - undefined
viewer.getObjectTree(function (objTree) {
tree = objTree;
});
//2.5 - undefined
var instanceTree = viewer.model.getData().instanceTree;
var rootId = this.rootId = instanceTree.getRootId();
//- undefined
var objectTree = viewer.getObjectTree();
Can anyone tell me if its still works for them I am using the v2 of the API for the rvt conversion to svf and 2.9 of the viewer3D.js
note I can see a list of dbid if I call this
var model = viewer.impl.model;
var data = model.getData();
var fragId2dbIdArray = data.fragments.fragId2dbId ;
but have no way of mapping back to the Revit element_id
As of version 2.9 this is still working. Here's my console:
Here's a couple of things you can try:
Is viewer undefined? Are you in the correct scope when grabbing the viewer?
The document have to be loaded before you can grab the instance tree. When the document is loaded, an event called Autodesk.Viewing.GEOMETRY_LOADED_EVENT will be fired, then you can start manipulating the instance tree.
Simply do this:
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
var instanceTree = viewer.model.getData().instanceTree;
});
For more structured code, follow this guide to add an extension.
There's a more detailed blog post on which event to listen for. It's still using the old way to get instance tree, though.
Shiya Luo was correct the viewer had not yet finished loading the geometry
in my extentions Load function I added two event listeners and made sure they both fired before trying to access the instanceTree
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
finishedGEOMETRY_LOADED_EVENT = true;
if(finishedGEOMETRY_LOADED_EVENT && finishedOBJECT_TREE_CREATED_EVENT ){
afterModelLoadEvents(viewer);
}
});
viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, function () {
finishedOBJECT_TREE_CREATED_EVENT = true;
if(finishedGEOMETRY_LOADED_EVENT && finishedOBJECT_TREE_CREATED_EVENT ){
afterModelLoadEvents(viewer);
}
});