Show context menu in Autodesk.Viewing.Viewer3D - autodesk-forge

I am trying to show a context menu using Autodesk.Viewing.Viewer3D (Headless Viewer).
I can get the context menu to show up easily when using Autodesk.Viewing.Private.GuiViewer3D as my viewer type but i don't want to use this viewer type as it has a toolbar and i don't want it to appear for this viewer. I can't use the css approach suggested here as I also want the toolbar in a different viewer in the same application.
My attempt to initialise a context menu using Autodesk.Viewing.Viewer3D (Headless Viewer) look like the following:
var contextMenu = new Autodesk.Viewing.UI.ObjectContextMenu(viewer);
viewer.setContextMenu(contextMenu);
viewer.registerContextMenuCallback('CustomContextMenuItems', function (menu, status) {
if (status.hasSelected) {
if(menu === null){menu=[];}
while (menu.length > 0) {
menu.pop();
}
menu.push({
title: 'Do Something',
target: function () {
console.log("Doing something")
}
});
}
});

The context menu is intended to be used with GuiViewer3D. It's most likely relying on some of its HTML or CSS setup, which is why it's causing issues with Viewer3D. If you need this level of customization of the GUI, I'd suggest to implement a separate, simple context menu instead of bending the built-in one.

Related

Why updated forge viewer's model browser isolate the items instead of select?

In the older version(7.1.*) of forge viewer the Model browser select the item and focus the selected item.
But in the latest version(7.*.*) it isolate the item(s).
Is there any way or settings to get back to the older functionality in the latest version?
You can change the behavior by specifying options.docStructureConfig on ModelStructurePanel.
Pls. check below blog post and query on stack overflow which answering for this topics.
https://forge.autodesk.com/blog/customizing-modelstructurepanel-behavior-forge-viewer
Prevent zoom in Forge viewer when clicking in Model Browser
As described in the blog post, you can change the behavior by creating custom ModelStructure Panel on Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT handler or you can specify the option in its constructor.
You also can specify the option in GuiViewer3D constrictor like below.
var options = {
docStructureConfig: {
click: {
onObject: ["selectOnly"] //instead of toggleOverlayedSelection
},
clickShift: {
onObject: ["isolate"] //instead of toggleMultipleOverlayedSelection
},
clickCtrl: {
onObject: ["selectToggle"] //instead of toggleMultipleOverlayedSelection
}
}
}
viewer3d = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer3d'), options);

Can i use docking panel in Autodesk forge for both viewers?

Example on Photo
I have two viewers to compare models. I created a docking panel for properties, and I want this panel to float in two viewers. Is it possible and who will tell you how to do it?
There are two options:
Option1:
Use the 'Autodesk.SplitScreen' extension, which will render up to 4 regions. You load it like this..
loadExtension('Autodesk.SplitScreen');
This simple extension can set up to four cameras, and render four regions. By default, it's just two (left and right). Here's the source code for how the core of it works, just in case you want to write your own...
https://autodeskviewer.com/viewers/latest/extensions/SplitScreen/SplitScreen.js
this.renderScenePart = function (scene) {
// Left
if (shouldRenderForViewport[0]) {
this.renderer.setViewport(0, vpVertStart, vpWidth, vpHeight);
this.context.renderScenePart.apply(this.context, arguments);
}
// Right
if (shouldRenderForViewport[1]) {
this.renderer.setViewport(vpWidth, vpVertStart, vpWidth, vpHeight);
this.context.renderScenePart.apply(this.context, arguments);
}
// Bottom left
if (shouldRenderForViewport[2]) {
this.renderer.setViewport(0, 0, vpWidth, vpHeight);
this.context.renderScenePart.apply(this.context, arguments);
}
// Bottom right
if (shouldRenderForViewport[3]) {
this.renderer.setViewport(vpWidth, 0, vpWidth, vpHeight);
this.context.renderScenePart.apply(this.context, arguments);
}
this.renderer.setViewport(0, 0, this.width, this.height);
this.renderer.enableViewportOnOffscreenTargets(false);
Option2:
For something more advanced, and specific to just 2D, you could also try the 'Autodesk.Viewing.PixelCompare' extension. Here's a blog post with much more detail and a demo...
BLOG: https://forge.autodesk.com/blog/compare-two-2d-documents-using-forge-viewer
ok, two more options:
option A:
if the second image is 'static'... why not just take a 'screenshot' and place it in the right side panel?
You can use the viewer.getScreenShot() command to retrieve a PNG blog, and paint it into the canvas.
// Get the full image
viewer.getScreenShot(viewer.container.clientWidth, viewer.container.clientHeight, function (blobURL) {
screenshot.src = blobURL;
});
For more details on drawing images to a canvas, see here: https://forge.autodesk.com/blog/screenshot-markups
option B:
if the two panels can be independently controlled, then perhaps try to sync the camera state, with some kind of button press (or action).
with the help of these blog articles:
https://forge.autodesk.com/blog/map-forge-viewer-camera-back-navisworks
Get the Camera position and restore it in Forge Viewer for a virtual visit
for example:
viewer.getState();
viewer.restoreState();
or restore camera position using navigation object:
const nav = this.navigation;
nav.getTarget();
nav.getPosition();
nav.getCameraUpVector();

How can I remove or hide an object on the model tree panel in Forge Viewer?

I need to hide (make it go away completely) from the model tree panel in Viewer.
I already tried overriding methods from the Viewer (some other stuff is done that way), but the Tree-related methods and objects are not accessible for extending. It also seems too dangerous to mess with instanceTree data, like removing the dbId from the nodes list.
I'm running on the latest Viewer code (6.5.3), and writing pure javascript extensions.
For example, I tried overriding this function, which is used internally to determine if a node should or not be displayed. It doesn't work, neither does overriding the same function on the ModelStructureTreeDelegate:
Autodesk.Viewing.UI.TreeDelegate.prototype.shouldCreateTreeNode = function (dbId)
{
// original code on the viewer.js is:
// return true;
let itGo = true;
// _objectsHiddenInTree is populated with dbIds of objects to be hidden right after initializing the viewer
_objectsHiddenInTree.forEach(x => {
if (x == dbId){
itGo = false;
}
});
// return false; doesn't work either
return itGo;
};
Is there a way to do this from the Viewer side? I mean, to remove an item from the model tree?
If it's more viable, removing the object from the scene altogether is also a valid option. But I can't remove it from the model before sending to model derivative, it has to be done when opening the Viewer, or before opening the Tree Model panel.
Personally the easiest way would be to access node element via viewer.modelstructure and use styling to hide the node:
<style>
.yourHiddenNodeClass{display:none!important}
</style>
...
<script>
let modelStructureControl = viewer.modelstructure;
modelStructureControl.createUI(); //initialize the panel if it hasn't
let treeViewControl = modelStructureControl.tree;
let modelDelegate = treeViewControl.getDelegate(model.id);
treeViewControl.addClass(modelDelegate, dbid, "yourHiddenNodeClass", false) //hide a node - last boolean to toggle recursiveness
...
treeViewControl.removeClass(modelDeleagate, dbid, "yourHiddenNodeClass", false) //remove your custom class
</script>
And to hide a node completely:
model.visibilityManager.setNodeOff(dbid, true) // true=hide, false=show
Bryan's answer gave me an idea that seems to work for now:
Every element on the tree panel has an atribute 'lmv-nodeid', with the dbId of the object. So I looked for it, and added the 'hidden' attribute to the div:
document.querySelectorAll('[lmv-nodeid="' + objectDbId + '"]')[0].hidden = true;
His answer is still better, though, because there is no guarantee that the attribute will remain on newer versions of the Viewer, whereas the Viewer classes and methods are more stable and future-proof.

How to add custom menu to Autodesk Forge Viewer?

Helo
I'm using Viewer Example form here: https://forge.autodesk.com/en/docs/viewer/v5/tutorials/basic-viewer/
(Step 1)
and now I need:
1. add custom menu on right click
2. get info's for clicked object, like Area, Volume, Length (if 3D) or length if 2D.
How to do that, please?
I try to copy whole "class MyContextMenu extends ... " code from
https://forge.autodesk.com/blog/customize-viewer-context-menu
but it does not worked.
Thank you.
Here's a simple example of adding custom menu items to the context menu: http://jsfiddle.net/s47vy5u3/2. You'll just need to include your Forge app's access token and some viewable URN. The menu customization code itself looks like this:
function customizeMenu() {
const viewer = NOP_VIEWER;
viewer.registerContextMenuCallback('MyCustomMenuItems', function(menu, status) {
menu.push({
title: 'My custom menu item',
target: () => {
// Add your menu item's code here
}
});
});
}

Prevent zoom in Forge viewer when clicking in Model Browser

There has been a change in the click behavior in the model browser from version 2 to version 3 of the Forge Viewer. In v2, a single click would select the elements and a double click would zoom to the selected elements. In v3, a single click will zoom to the elements. Sometimes this is great, but often it would be nice to disable this behavior. Is there an easy way to do this today? And if not, could it be possible to add a disableZoomOnSelection function to the viewer API?
I know that the eyes in the browser will take care of the show and hide elements, but it’s very easy to klick in the three by accident and suddenly the viewer zoom without the user intention.
Regards
Frode
I dig that code for you looking at the implementation of the ViewerModelStructurePanel that I was exposing in that article: Supporting multiple models in the new ModelStructurePanel
Events that occur in the tree are mapped to predefined actions through the options.docStructureConfig object, so the workaround is to instantiate a new ViewerModelStructurePanel with the desired options:
viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, () => {
var options = {
docStructureConfig: {
// go with default, which is viewer.select(node)
// click: {
// onObject: ["toggleOverlayedSelection"]
//},
clickShift: {
onObject: ["toggleMultipleOverlayedSelection"]
},
clickCtrl: {
onObject: ["toggleMultipleOverlayedSelection"]
}
}
}
var customModelStructurePanel =
new Autodesk.Viewing.Extensions.ViewerModelStructurePanel(
viewer, 'Browser', options)
viewer.setModelStructurePanel(customModelStructurePanel)
})
The double-click however is not linked to an event in the current implementation, so for a more powerful customization I would recommend you replace the whole implementation by a custom one as exposed in the article and implement desired action handlers. I implemented it as drop-in replacement, so in that case you just need to include it to your html after the viewer script and don't have to replace the model browser in OBJECT_TREE_CREATED_EVENT
The model browser receives an options object in its constructor. There, you can specify the actions for different events (click, clickCtrl, clickShift, etc).
To set the old behavior you can try the following:
var options = {};
options.docStructureConfig = {
"click": {
"onObject": ["isolate"]
},
"clickCtrl": {
"onObject": ["toggleVisibility"]
}
};
NOP_VIEWER.setModelStructurePanel(new ave.ViewerModelStructurePanel(NOP_VIEWER, "", options));
NOP_VIEWER can be replaced with your own viewer variable.