Forge Viewer : Turning "Display Edges" per model instead of Global settings - autodesk-forge

We are displaying an architectural Revit model and adding a fairly detailled site context.
In the Forge Viewer Settings I can turn On or Off "Display Edges" globally.
However we'd like to have the Edges "On" for the Revit Model but "Off" for the context. Is there a way to achieve this?
I found this post and I tried to load the site model with:
viewer.loadDocumentNode(doc, viewables,{
placementTransform: m.matrix,
keepCurrentModels: true,
globalOffset: {x:0,y:0,z:0},
isAEC: false //!<<< Tried this to prevent Edges
})
But it did not change anything and it still depends on the Global settings.
I found another setting and also tried the following but no luck either:
const models = viewer.impl.modelQueue().getModels()
const contextModel = models.filter(m => m.loader.svfUrn === contextURN);
if (Array.isArray(models) && models.length) {
models[0].hasEdges = false;
}
Any help much appreciated!

Not sure which model format of your road model is, but passing the createWireframe: false option should help turn off the model edges with my research.
viewer.loadDocumentNode(doc, bubble, {createWireframe: false, keepCurrentModels: true})

Related

Set transparent for specific element

I want set transparent for specific element, i follow this code:
var instanceTree = this.viewer.model.getInstanceTree();
var fragList = this.viewer.model.getFragmentList();
this.listElement.forEach(element => {
instanceTree.enumNodeFragments(element, (fragId) => {
console.log(element.material)
var material = fragList.getMaterial(fragId)
if (material) {
material.opacity = value;
material.transparent = true;
material.needsUpdate = true;
}
});
});
this.viewer.impl.invalidate(true, true, true);
but it overide for all elements have that material. How can i set for selected element?
Appreciate any comments.
UPDATE 1:
i found go around way is clone main material than register it with different name:
var newMaterial = material.clone();
const materials = this.viewer.impl.matman();
materials.addMaterial('mymaterial',newMaterial,true);
fragList.setMaterial(fragId,newMaterial);
newMaterial.opacity = value;
newMaterial.transparent = true;
newMaterial.needsUpdate = true;
but effect is not what i want, it has different color and when set transparent i can only see a couple object behind it
You can create your own, custom THREE.js material and assign it to the fragment using fragList.setMaterial(fragId, material).
For more information on using custom materials or shaders, see https://forge.autodesk.com/blog/custom-shader-materials-forge-viewer.
EDIT:
Regarding the visual anomalies (for example, when you only see some of the objects behind something semi-transparent), this is a known problem, unfortunately with no clear solution. When the Forge Model Derivative service creates an SVF file to be viewed in Forge Viewer, the individual scene elements are stored in a data structure optimized for fast traversal, depending on whether they are semi-transparent or fully opaque. This data structure is fixed, and so unfortunately, when you take an object that was originally fully opaque, and you make it semi-transparent, it will most likely be rendered in a wrong order...

What does the isVisible property mean in intersectionObeserver API?

Before lynching me in the comments hear me out.
The intersectionobserver is supposed to listen for when an element is scrolled into view right?
But when I do so the isVisible property is false, cant figure out why. I think its because Im not understanding isVisible property because the other properties are working.
Here is my code:
import React, { useRef, useEffect, useState, useContext } from 'react'
import ReactGA from 'react-ga';
import Context from '../utils/context'
const Home = props => {
const context = useContext(Context)
const [scrollState, setScroll] = useState(false)
const intersectTarget = useRef(null)
useEffect(() => {
// ReactGA.pageview(props.location.pathname);
if(!context.initialLoadProp) {
context.setInitialLoadProp(true)
}
}, [])
useEffect(() => {
const opts = {
root: null,
rootMargin: '0px',
threshold: 0
}
const callback = entry => {
console.log(entry)
}
const observerScroll = new IntersectionObserver(callback, opts)
observerScroll.observe(intersectTarget.current)
}, [])
return(
<div>
<img height="500px"
width="500px"
src="https://timedotcom.files.wordpress.com/2019/03/kitten-report.jpg" alt=""/>
<div id="heading1" style={{height: '500px'}}>
<h1>First Heading</h1>
</div>
<div style={{height: "500px"}}>
<h1 ref={intersectTarget}
id="heading2">
Second Heading
</h1>
</div>
</div>
)
};
export default Home;
My output even thought isIntersecting is true isVisible is false.
To activate IntersectionObserver v2 isVisible feature, you must add two options as follows:
const options = {
root: null,
rootMargin: '0px',
threshold: 0,
/* required options*/
trackVisibility: true,
delay: 100 // minimum 100
}
Visibility is calculated as follows:
If the observer's trackVisibility attribute is false, then the target is considered visible. This corresponds to the current v1 behavior.
If the target has an effective transformation matrix other than a 2D translation or proportional 2D upscaling, then the target is considered invisible.
If the target, or any element in its containing block chain, has an effective opacity other than 1.0, then the target is considered invisible.
If the target, or any element in its containing block chain, has any filters applied, then the target is considered invisible.
If the implementation cannot guarantee that the target is completely unoccluded by other page content, then the target is considered invisible.
Check IntersectionObserver v2 browser support before using it https://caniuse.com/#feat=intersectionobserver-v2
The "isVisible" property is part of proposed Intersection Observer v2 updates concerning actual visibility of the target element to the user. When passing the options object to the observer you can include a "trackVisibility" boolean to implement it, but a corresponding "delay" property is also required. The value of this delay is in milliseconds and needs to be a minimum of 100. If the appropriate delay is not provided then you'll receive an error in the console and the observer will not have been initiated.
At this point I'm only aware of Chrome supporting this feature. So far I've been unable to actually get the visibility feature to work, the delay seems to work though. It's an experimental feature of an experimental feature.
Intersection Observer v2 article
[note: I wrote the specification for IntersectionObserver and implemented it in Chrome].
As stated in previous answers, isVisible only has meaning if the browser supports IntersectionObserver V2 (currently, only chromium-based browsers, including Chrome and Edge); and if you include the 'trackVisibility' and 'delay' parameters when constructing the observer.
This is not considered to be an experimental feature in Chrome; it is a complete, stable feature used by large production websites. However, it is unclear when it will be supported by other browsers. The most up-to-date information about the browsers supporting this feature is here:
https://chromestatus.com/features/5878481493688320
If you think the feature could be improved, consider filing an issue in the spec repository:
https://github.com/w3c/IntersectionObserver/issues
According to https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry there is no isVisible property as far as I can see.
If your goal is to check when the element is completely in view, you can change the threshold to threshold: 1 and then check if the intersectionRatio is equal to one entry.intersectionRatio == 1.
I might have missed the isVisible somewhere but I cant find it.
Hope this helps!

Load multiple models(aggregation) in forge viewer and load other models as hidden by default until user selects from model browser

I am trying to load multiple models(aggregation) on to the forge viewer(v.6.x) initially while loading, but I want to show only one model and other models as hidden by default(can be with ghost view). Later when user clicks on eye icon from model browser, then that model should be visible/hidden.
I tried calling viewer.hideModel(modelId) after loading the model. But even though the model is showing in model browser, when I click on it, it says error model is not loaded.
var showModel = false;
this.viewer.loadModel(url, {globalOffset: { x:0, y:0, z:0 }, modelNameOverride: modelName}, () =>
{
this.isModelLoaded = true;
this.viewer.caller = this;
this.addEventListenersToViewer();
if(!showModel){
this.viewer.hideModel(modelId); // This is to hide the model by default after loading.
}
},
errorMsg => {
this.isModelLoaded = false;
this.viewer.container.style.opacity = 0;
this.modelLoadError(this.fetchTranslationByKey('getModelError'));
}
);
Expected behavior is to allow user to select from model browser, which models to show/hide on the viewer among all the models loaded initially(linked models should be hidden by default).
Current result I am getting is the linked models are showing in the model browser but when I click on that it says - Error Model is not loaded
A few issues here:
When using arrow function the context that this points to is different so be careful with that
how did you obtain the modelId? Instead of using arbitrary counter better to iterate the model array via Viewer.impl.modelQueue()
I am unable to replicate the model not loaded error with model browser. Can you provide more details or a live sample (jsfiddle/jsbin)?

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.

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.