How to add custom menu to Autodesk Forge Viewer? - autodesk-forge

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

Related

Show context menu in Autodesk.Viewing.Viewer3D

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.

How do you collapse a category in PropertyPanel in Forge Viewer v7?

I'm using the Forge Viewer to display properties on selected items in my model.
When an object is selected, I want to automatically show the properties panel but keep a category collapsed. Here's the code I'm using in an attempt to achieve this:
viewer.addEventListener(
Autodesk.Viewing.SELECTION_CHANGED_EVENT,
() => {
const panel = viewer.getPropertyPanel();
panel.setVisible(true);
panel.setCategoryCollapsed("Category Name", true);
},
);
The event is raised and the panel is shown, however, the function setCategoryCollapsed does not work (understandable as it was last documented in v5). Is there a similar function in v7 to collapse a category?
That function expects an object with name and type properties - e.g. if I wanted to collapse the "Graphics" category, I could do it like this:
const panel = viewer.getPropertyPanel();
panel.setVisible(true);
panel.setCategoryCollapsed({name: "Graphics", type: "category"}, true);
Have also just written a blog post about it now: Collapse category in the Property Panel

Get Property Name of Part that is Selected

I am currently making a web page that uses the forge viewer. How can I make a function that will return the value from the "Properties" tab inside the viewer. I need to be able to get the value of the 'name' property inside the viewer, when a part is selected by the user. Any help is much appreciated!
Cheers!
viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, onSelectionChanged)
function onSelectionChanged(dbids){
console.log(dbids)
viewer.getProperties(dbIds, props => {
props.properties.forEach(prop => {
// any custom action with data?
//prop.displayName
//prop.displayValue
//prop.displayCategory
// etc
});
}
Blog: https://forge.autodesk.com/blog/property-inspector-viewer

Change default ViewCube Orientation

I'm using the Forge Viewer to display simple geometry extractions from buildings.
However, when loading them the orientation of the model/view cube is not matching the expected use case (see image).
Basically I would need to swap the "Front View" with the "Top View".
Is this possible to achieve such a thing through e.g. default settings on the viewer object?
My set up is basically identical to the one in this 3rd-party react wrapper of the Forge Viewer: https://github.com/outer-labs/react-forge-viewer
Thank you already very much.
Daniel
EDIT: The model is in STP format
Basically, you can archive this with following steps via Viewer APIs after the model is loaded completely and can be separated into two parts.
(Preproccess) Get the Front view state of the Viewer that your want to make it as the Top:
a. Orient the current view to Front view: viewer.setViewCube( 'front' ).
b. Obtain current view statue of the viewport: var viewState = .getState( { viewport: true } ).
c. Save this viewState to somewhere, your js file or database.
Restore view state and set it as the Top view:
a. Obtain the viewState from somewhere (e.g. js file or database) that you got from the step1.
b. Restore view state via viewer.restoreState( viewState ).
c. Set the current view as Top view: viewer.autocam.setCurrentViewAsTop().
d. Set the current view as Home to avoid the state of the viewcube to be reset: viewer.autocam.setCurrentViewAsHome().
The code snippet for step2:
viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
function( event ) {
console.log( '%cGEOMETRY_LOADED_EVENT: !!!Geometries loaded!!!', 'color: green;' );
setTimeout(() => {
const onOrientTopViewCompleted = function() {
viewer.removeEventListener(
Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED,
onOrientTopViewCompleted
);
viewer.autocam.setCurrentViewAsTop();
viewer.autocam.setCurrentViewAsHome();
console.log( 'CAMERA_TRANSITION_COMPLETED' );
};
viewer.addEventListener(
Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED,
onOrientTopViewCompleted
);
var viewState = '....'; //!<< the view state of the original `Front` view.
viewer.restoreState( viewState )
}, 1000);
});
Hope it helps!

How can I embed a Google Map in Streetview mode with an iframe?

When I pull up a Google Map, there is a little gear icon near the bottom-right that allows me to share. It includes finding an embeddable <iframe>. However, once I go into Streetview this gear icon disappears.
How can I embed the streetview version of the map?
It appears the problem is that the new google maps does not have a way to embed.
If you click on the in the bottom right corner while NOT in pano / street view mode you can revert to classic maps
Then you'll see
from there you can select embed frame.
When you're in the Streeview mode, click on the "link button" next to the print button. You'll have an iframe and a link to customize and preview it.
Since Google changed in the past two years I'll post an answer that shows how to embed Street View with the new Maps.
Enter in Street View Mode with the best view you want
Click those "three dots" on the top left corner of the screen
Click "Share or Embed Image"
Click on the tab "Embed Image" and copy/paste the code
of the iframe
its too simple .just go to link below https://developers.google.com/maps/documentation/javascript/examples/streetview-embed
copy the html+javascript code into your page and modify the div style (by default it goes full screen) having id= map-canvas
now go to the desired street view and copy the latitude,longitude in the url then replace in your code in initialize function latling(latitude,longitude) done!!!! happy to post my first answer stack overflow has answered me so many times
If you are looking for a more generic way, for example to display embedded streetview based on the regular google map link or coordinates. Here is my solution:
Extract coordinates from a regular link
export const getCoordinatesFromGoogleMapURL = (url: string) => {
if (!url) {
return undefined
}
const urlParts = url.split('/#')[1]?.split(',')
if (!(urlParts && urlParts?.length > 1)) {
return undefined
}
const gpsX = parseFloat(urlParts[0])
const gpsY = parseFloat(urlParts[1])
if (isNaN(gpsX) || isNaN(gpsY)) {
return undefined
}
return [gpsX, gpsY] as [number, number]
}
Generate embed url from coordinates:
export const generateGoogleMapEmbedUrl = (coordinates: [number, number]) => {
if (!coordinates) {
return undefined
}
const baseUrl = "https://www.google.com/maps/embed/v1/streetview"
const coordinatesString = `${String(coordinates[0])},${String(coordinates[1])}`
const url = `${baseUrl}?key=${process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}&location=${coordinatesString}`
return url
}
Finally we can put it together:
export function convertToEmbedURL(url: string): string {
const coordinates = getCoordinatesFromGoogleMapURL(url)
const embedUrl = generateGoogleMapEmbedUrl(coordinates)
return embedUrl;
}
You can read the official docs to find out more about params etc: https://developers.google.com/maps/documentation/embed/embedding-map#streetview_mode