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

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

Related

Save and retrive in forge viewer

I am using forge viewer for displaying AutoCAD files.
Also using the drawing tool over viewer based on the sample source.
I will draw the area by using box or sphere draw tools.
I need to save the current viewer including box or sphere area which I was marked over viewer and when again loading same file the area which has been marked that should be bind default.
How it is possible please help me
Suggest any way to implement this scenario.
Thanks in advance.
You can do that with 2 steps.
First, taking advantage of Object3D.toJSON() method.
Let's summarize in a sample where we generate a JSON object from our mesh:
//here we create a BoxGeometry
let geom = new THREE.BufferGeometry().fromGeometry(new THREE.BoxGeometry(100,100,100));
let phongMaterial = new THREE.MeshPhongMaterial({
color: new THREE.Color(1, 0, 0)
});
let mesh = new THREE.Mesh(geom, phongMaterial);
if (!viewer.overlays.hasScene("CustomScene")) {
viewer.overlays.addScene("CustomScene");
}
viewer.overlays.addMesh(mesh, "CustomScene");
viewer.impl.sceneUpdated(true);
//here we generate the JSON from the mesh and download it
let jsonObject = JSON.stringify(mesh.toJSON())
download(jsonObject, 'Box.json', 'text/plain');
download function can be found here.
The next step is about generating the box from the saved JSON.
For that, we'll use ObjectLoader.parse method.
And again, we can summarize in the code below:
//here we read the JSON object from our generated file
var request = new XMLHttpRequest();
request.open("GET", "js/Box.json", false);
request.send(null)
var my_JSON_object = JSON.parse(request.responseText);
//here we generate the mesh
let mesh = new THREE.ObjectLoader().parse(my_JSON_object);
if (!viewer.overlays.hasScene("CustomScene")) {
viewer.overlays.addScene("CustomScene");
}
viewer.overlays.addMesh(mesh, "CustomScene");
viewer.impl.sceneUpdated(true);
Refer here for the function to read objects from JSON file.

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?

Forge Viewer Select in a multi-model context

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.

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