Check if Model has any default hidden elements in Forge's 3D Viewer - autodesk-forge

Some background:
I'm using Forge to visualize IFC models. Some of my uploaded models have IfcOpeningElements which seems to be hidden in Forge by default, at least when setting ghosting to false via viewer.setGhosting(false). I'm also having functionality to hide normal elements in the viewer (viewer.hide(dbIds, model)), and to show/hide all elements (model.setAllVisibility(show)).
The problem I'm having is that I want to be able to show/hide the IfcOpeningElements regardless of showing and hiding "normal" elements.
The approach I tried, which is not working very well is to call model.visibilityManager.getHiddenNodes(). The problem with this approach is that, even for models with IfcOpeningElements, getHiddenNodes only returns a non-empty array after ~15 seconds (probably varies with the size of the model). In the meantime, if the user does anything that makes the app call model.setAllVisibility(true), I'm no longer able to detect the original hidden IfcOpeningElements.
Furthermore, when calling getHiddenNodes after an arbitrary waiting period after the model has loaded, I'm not sure if it returns an empty array because the model is not "ready" to detect hidden elements from IfcOpeningElements or if the model simply does not have any IfcOpeningElements.
So, Is there any good way to detect if a model has any "default" hidden elements without having to wait long after the model is loaded? Or perhaps there is a way to call change the visibility of the entire model without changing the visibility of the IfcOpeningElements?
I'm aware of the possibility to listen to the HIDE_EVENT event, but since I'm not sure if it will fire at all (since I'm not sure if the model has any IfcOpeningElements), I cannot block the application from calling model.setAllVisibility(true) which in turn would make getHiddenNodes() return an empty array even if there was hidden elements to begin with.

How about just skipping loading IfcOpeningElements' geometries? To do so, pass skipHiddenFragments: true to the 3rd argument of Viewer3D#loadDocumentNode
viewer.loadDocumentNode(
doc,
viewable,
{
skipHiddenFragments: true
}
);
This approach will skip loading the IfcOpeningElements' meshes, but you can still see their properties while selecting IfcOpeningElements on the model structure panel. On the other hand, you cannot access their bounding boxes and geometries with this approach.
Regarding how to check default hidden elements, try to call this code after all geometries are loaded. The hiddenDbIds are elements hidden by default.
let model = viewer.getAllModels()[0]; //!<< Check the first model just for demo
let fragList = model.getFragmentList();
let hiddenDbIds = Object.keys( fragList.vizflags ).filter(fragId => !fragList.isFragVisible( fragId )).map(fragId => fragList.getDbIds( fragId ) );
// hiddenDbIds.forEach(dbId => viewer.getProperties(dbId, console.log))
Note. The visible flag will also be changed after changing the objects' visibility. So, ensure that run the above before changing the visibilities.

Related

SetThemingColor of hidden elements

How do i set the theming of components of a model which is hidden:
If i not using the hideModel function everthing is working perfect, but if the model is hided i get an error 2 => BAD_DATA = 2,
this.viewerComponent.viewer.hideModel(this.viewerComponent.viewer.model);
The following things i already tried out:
this.viewerComponent.viewer.getHiddenModels()[0].setThemingColor(idArray[0], color, true);
this.viewerComponent.viewer.setThemingColor(idArray[0], color, this.viewerComponent.viewer.getHiddenModels()[0]);
This is not working for sure, since the model of the viewer will be null after hiding
this.viewerComponent.viewer.setThemingColor(idArray[0], color, this.viewerComponent.viewer.model());
Viewer Version 7
Thanks for help
Unfortunately, viewer.hideModel is a bit of a misnomer because it doesn't just hide the model, it actually unloads it. If you want to hide the model while still keeping all its data in memory, you might be able to achieve a similar result by calling viewer.hide(viewer.model.getRootId()). This will hide (or "ghost", if you have "ghosting" enabled; see below) the individual elements of the model while still keeping them in memory, so you can still configure properties like the theming color.

In Blazor client app (razor component), does every event triggered refresh the UI?

Does in Blazor client app every event triggered (mouse, keyboard, touch,...) cause the whole UI refreshed?
In the below example, on every key input, i is incremented while it not bound to oninput event.
<input type="text" #bind-value="#name" #bind-value:event="oninput"/>
#name
#ComputeResult()
#code {
string name;
int i=0;
public double ComputeResult()
{
i = i + 1;
return i;
}
}
At the beginning, whenever you wanted to re-render your component, you had to call the StateHasChanged method which serves as the starting point of the rendering process. Nowadays, this is not necessary any longer. The StateHasChanged method is automatically called whenever a UI event, such as change or click is triggered. If you attach an empty event handler to a button control, and hit it, the StateHasChanged method is still called, which results in the re-rendering of your component, and consequently evaluating the expression #ComputeResult(). Note that this behavior can be altered by overriding the ComponentBase.ShouldRender method whose default returned value is true. Note that even if you overrides this method to return false, the first render always takes place.
Components are created only once, and they may be re-rendered multiple times. The re-rendering process and what is re-rendered is described in the answer by Kyle...
Only DOM elements that have changes get updated, not the entire UI. Blazor uses what they call a Render Tree to keep track of the elements that have changed and need to be updated. When an event fires, it regenerates the Render Tree and compares it to the old one to find changes, and then only updates the changed items in the render tree in the DOM.
Components render into an in-memory representation of the browser's Document Object Model (DOM) called a render tree, which is used to update the UI in a flexible and efficient way.
After the component is initially rendered, the component regenerates
its render tree in response to events. Blazor then compares the new
render tree against the previous one and applies any modifications to
the browser's Document Object Model (DOM).
From: https://learn.microsoft.com/en-us/aspnet/core/blazor/components?view=aspnetcore-3.1

React - Rending JSON as UI objects and allowing any user changes to these UI objects to update the JSON

We have an internal tooling system that uses JSON to specify various actions, tasks etc. I'm making a single page web application to visualise this information. The web application contains a text area for editing/pasting JSON and another area which renders the JSON into UI elements.
So I want to take JSON from the text area and visualize it. But also, I want the user to be able to interact with this render visualization, making changes/configuring options and have it update the JSON immediately. I guess one could call that two-way binding.
While going from JSON -> HTML DOM elements is easy, I'm not sure how to do the reverse and go DOM -> JSON. Let's say we have some JSON
{"task1" : {
"canFail" : true,
"autoRestart" : false,
"connectionPropertys" : {
... }
}
}
I would visualize task1 as some UI element with check boxes for canFail and autoRestart. I want the user to be able to both
edit the JSON "canFail" value to 'true' or 'false' and have that
immediately render (this is easy - just re-render the entire JSON)
check or uncheck the canFail checkbox and have the JSON be
automatically updated (hard part - where I'm stuck)
In React we pass state/properties down the the children, so the component rending the checkbox(es) would only know about canFail or autoRestart... it might not know about "task1". If the checkbox is changed, sure I can have some handler function fire, but it doesn't actually know what key/value in the JSON this corresponds to (e.g. it knows nothing of task1 and there could be multiple task1s).
Interested to know what a good approach would be here to tackle this.
Thanks :)

How to make selection work for 2D multi-models in Autodesk-Forge Viewer

I have a couple of DWG drawings which I have converted into F2D files for offline viewing.
There is a base model which I load on the viewer initialization. Other drawings are loaded using viewer.loadModel as in when required.
These drawings get loaded fine. The problem is that it messes up the selection. Only one of the loaded models gets selected (and gets highlighted) on mouse down.
When I check the selection on the event received in AggregateSelectionChangedEvent, the selection has only the first model. So, I cannot even distinguish the model which has been selected.
Since some of the drawings can be loaded multiple times, (Consider a car drawing where one can load same wheel drawing and place at 4 positions) the dbId of the selected entity is same for multiple models. So, dbId can not be used as the only parameter. What I need is dbId + model.id combination to tell the selected object apart.
As per the suggestion received from elsewhere, I was setting
renderer.settings.numIdTargets = 2.
This made the selection work for multi-model pretty much as normal selection.
It worked a while, then suddenly stopped working. I am not able to figure out why.
Can anyone suggest a lasting solution to this problem?

Avoid ItemRenders Caching in a Spark List in Flex 4

I have two Spark Lists with custom Item Renderers. I'm working on an application that enables users to drag these Item Renderers from one List to the other. When one of these IRs is dropped in a new position or in another List, I'm updating the dataproviders: I remove the object from one list's dataprovider and add it to the other's dataprovider. This is working ok.
The problem is that sometimes the IR is cached and it doesn't show the correct information, based on its data.
How can I force the Lists to never cache IRs, so that every time I modify the dataprovider all Item Renders re-create all IRs. Performance won't be an issue since I have few items on each list.
A few things..
1) ItemRenderers should always be cached [and reused]. This is one of the benefits of using a Flex list in the first place. I suspect your itemRenderer is implemented inorrectly as to not change when it's data changes. If you share some code for this it would be helpful. But, basically, your itemRenderer should listen to the dataChange event and when the data changes you should update the component's visual display with new data.
2) In Flex 3, I'd have sworn that dragging an item from one list to another automatically updated the relevant dataProviders. Are you sure you need to write manual code to make those changes? You will, though, need code to update your backend as relevant.
Flextras has some good points, but to answer your specific question, you can set useVirtualLayout to false on Spark Lists. This will ensure there is a renderer for every item in your list and thus avoiding the recycling issues. You should really only do this when you have a relatively short list of items though, otherwise you will have performance issues, which as Flextras noted, is the reason Flex recycles renderers.
I put a reset method in my set data to assure renderer variables are reinitialized
override public function set data(object:Object):void
{
if (object == null)
{
return;
}
reset(); // reset locals to their base. ie. x=0, y=0, counter=0
// set up renderer using data
}