Search code examples
autodesk-forgeautodesk-vieweriterableautodesk

How can I show 2 different label sets as markup in the forge viewer


I'm trying to show some labels in the forge viewer, based on the example markup code. My code works fine for one dataset, but when I add another one, I get: "BuildingData.js:113 Uncaught TypeError: this.frags[("dbId" + dbId)] is not iterable". The 2 datasets are extensions that extend the BuildingData class & they just create a button & call super.init(). I don't get why it fails when I activate the second label set.

I'm using v7 of the viewer.

class BuildingData extends Autodesk.Viewing.Extension {
    constructor(viewer, options) {
        super(viewer, options);
    }

    init(){
        //Callback to update labels
        const updateLabelsCallback = () => {
            if(this.button.getState() === 0) {
                this.updateLabels();
            }
        };

        //Events when to update the labels
        OnEvent(this.viewer, Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateLabelsCallback);
        OnEvent(this.viewer, Autodesk.Viewing.ISOLATE_EVENT, updateLabelsCallback);
        OnEvent(this.viewer, Autodesk.Viewing.HIDE_EVENT, updateLabelsCallback);
        OnEvent(this.viewer, Autodesk.Viewing.SHOW_EVENT, updateLabelsCallback);       
        
        //Add the button to the data bar
        this.button.onClick = (ev) => {           
            this.enabled = !this.enabled;
            this.button.setState(this.enabled ? 0 : 1);
            this.showLabels();
        };
        
        this.viewer.dataBar.getControl("dataGrp").addControl(this.button);
    }

    cleanup(){
        //remove button from data bar
        this.viewer.dataBar.getControl("dataGroup").removeControl(this.button);

        //remove labels
        let labels = FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer label.data.${this.dataClass}`);
        for(let label of labels) label.remove();
    }

    showLabels() {
        let viewerContainer = Find(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer`);

        //remove old labels
        let labels = FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer label.data.${this.dataClass}`);
        for(let label of labels) label.remove();
        
        //show new labels?
        if(!this.enabled) return;

        //check if the model tree is available
        let tree = this.viewer.model.getInstanceTree();
        if(tree === undefined){
            console.log("Model tree is not loaded yet"); 
            return;
        }

        //select sessor & fit to view
        const onClick = (e) => {
            this.viewer.select(e.currentTarget.dataset.dbId);
            this.viewer.utilities.fitToView();
        };

        this.frags = [];
        for(let i = 0; i < this.labels.length; i++){
            const label = this.labels[i];
            this.frags["dbId" + label.dbId] = [];

            // create the label for the dbId
            let lbl = document.createElement("label");
            lbl.classList.add("data");
            lbl.classList.add("update");
            lbl.classList.add(this.dataClass);
            lbl.dataset.dbId = label.dbId;
            lbl.style.cssText = `display: ${this.viewer.isNodeVisible(label.dbId) ? "block" : "none"}`;
            
            //add click event
            OnEvent(lbl, "click", onClick);
            
            let span = document.createElement("span");
            lbl.appendChild(span);
            span.innerText = label.name;       
            
            viewerContainer.appendChild(lbl);
            
            //Collect fragment ids of dbId
            tree.enumNodeFragments(label.dbId, (fragId) => {
                this.frags["dbId" + label.dbId].push(fragId);
                this.updateLabels();
            });
        }
    }

    updateLabels() {
        for(const label of FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer .update`)){
            const dbId = label.dataset.dbId;
            
            //get center of the dbId based on the bounding box of the fragments
            const pos = this.viewer.worldToClient(this.getBoundingBox(dbId).center());

            //position label in the center of the box
            label.style.cssText = `left: ${Math.floor(pos.x - label.offsetWidth / 2)}px`;
            label.style.cssText +=`top: ${Math.floor(pos.y - label.offsetHeight / 2)}px`;
            label.style.cssText +=`display: ${this.viewer.isNodeVisible(dbId) ? "block" : "none"})`;
        }
    }

    getBoundingBox(dbId) {
        var fragList = this.viewer.model.getFragmentList();
        const nodebBox = new THREE.Box3()
        
        //get bounding box for each fragment
        for(const fragId of this.frags["dbId" + dbId]){ //<----- ERROR is here
            const fragBBox = new THREE.Box3();
            fragList.getWorldBounds(fragId, fragBBox);
            nodebBox.union(fragBBox);
        }

        return nodebBox
    }
}

Solution

  • I found the solution, I was trying to update all labels (of both datasets) in the viewer & not only the ones corresponding to the active dataset. Changing 1 line in updateLabels() fixed it:

    const label of FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer .data.${this.dataClass}`