简体   繁体   中英

IFC.js show/hide layers and components

I'm wrapping "web-ifc-viewer" in an angular application. I've some troubles to hide and show components inside the IFC.

I've started from this example but I need to build a generic BIM viewer, so I can't define any category to prior.

import {AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {IfcViewerAPI} from "web-ifc-viewer";
import {Subject, takeUntil} from "rxjs";
import {AppThemes, BimViewerService} from "./bim-viewer.service";

@Component({
  selector: 'almaviva-bim-viewer',
  templateUrl: './bim-viewer.template.html',
  styleUrls: ['./bim-viewer.component.scss']
})
export class BimViewerComponent implements AfterViewInit {
  @ViewChild('viewerContainer')
  container!: ElementRef;
  viewer?: IfcViewerAPI;
  model: any;
  ifcElement: any;
  loadingValue: number = 0;


  constructor() {
  }

  ngAfterViewInit(): void {
    if (this.container) {
      this.viewer = new IfcViewerAPI({container: this.container.nativeElement});
      this.loadIfc('/assets/sample.ifc');
    }

  }

  private async loadIfc(url: string) {
    try {
      if (this.viewer) {
        await this.viewer.IFC.loader.ifcManager.useWebWorkers(true, '/assets/IFCWorker.js');
        await this.viewer.IFC.setWasmPath("wasm/");
        this.viewer.axes.setAxes(1000);
        this.viewer.grid.setGrid(1000);
        await this.viewer.IFC.loader.ifcManager.applyWebIfcConfig({
          USE_FAST_BOOLS: true,
          COORDINATE_TO_ORIGIN: true
        });
        this.viewer.IFC.loader.ifcManager.setOnProgress(
          (event) => {
            this.loadingValue = Math.floor((event.loaded * 100) / event.total);
          }
        )
        this.model = await this.viewer.IFC.loadIfcUrl(url);
        const project = await this.viewer.IFC.getSpatialStructure(this.model.modelID, true);
        this.ifcElement = project.children[0];
        await this.viewer.shadowDropper.renderShadow(this.model.modelID);
      }
    } catch (e) {
      console.log(e);
    }
  }

  async toggleLayer(event: Event, layer: any) {
      const subset = this.viewer?.IFC.loader.ifcManager.createSubset({
      modelID: this.model.modelID,
      ids: [layer.expressID],
      removePrevious: true,
      customID: `${layer.expressID}-custom-id`
    });
    if (subset) {
      this.viewer?.context.getScene().remove(subset);
    }
  }
}

When I toggle the layer ( toggleLayer() ) I receive an object from the subset like this

子集对象

This is my html

<div>
  <mat-sidenav-container>
    <mat-sidenav mode="side" opened>
      <mat-toolbar>
      <span>
        BIM
      </span>
      </mat-toolbar>
      <mat-progress-bar mode="determinate" [value]="loadingValue"></mat-progress-bar>
      <mat-list role="list" *ngIf="!ifcElement">
          <mat-list-item role="listitem">
            Caricamento IFC in corso...
          </mat-list-item>
      </mat-list>
      <mat-accordion *ngIf="ifcElement">
        <mat-expansion-panel *ngFor="let arch of ifcElement?.children || []">
          <mat-expansion-panel-header>
            <mat-panel-title>
              {{arch.Name?.value || arch.LongName?.value || 'Architettura'}}
            </mat-panel-title>
          </mat-expansion-panel-header>
          <mat-list role="list">
            <mat-list-item role="listitem" *ngFor="let layer of arch.children">
              <mat-checkbox (click)="toggleLayer($event, layer)">
                {{layer.Name?.value || layer.LongName?.value || 'N/A'}}
              </mat-checkbox>
            </mat-list-item>
          </mat-list>
        </mat-expansion-panel>
      </mat-accordion>
    </mat-sidenav>
    <mat-sidenav-content>
      <div id="viewer-container" #viewerContainer></div>
      <div class="loading-spinner-wrapper" *ngIf="loadingValue!==100">
        <mat-spinner mode="indeterminate" diameter="35"></mat-spinner>
      </div>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

Here the final result in the browser

国际金融公司查看器

The issue it's that nothing happens when I toggle the layer. And the subset log looks always the same.

Subsets or createSubset() requires customID along side ids to identify the created subset from the original model without the customID you are overwriting your subset, it can be any string, in the example its was defined by category.toString()

When you load an IFC model, it also gets added to the scene. You need to remove the original model from the scene.

Instead of creating a subset every time the user toggles a layer, you need to create subsets for each layer in the beginning, and just add or remove the subset from the scene when the user toggles a layer. You can find a tutorial about that here , the full example here and the working app here .

The structure of your logic should be similar to the following snippet. This has been extracted from the minimal example linked above, so probably you need to adapt it a bit to you project:

import {
    IFCWALLSTANDARDCASE,
    IFCSLAB,
    IFCDOOR,
    IFCWINDOW,
    IFCFURNISHINGELEMENT,
    IFCMEMBER,
    IFCPLATE,
} from 'web-ifc';

//Sets up the IFC loading
const ifcModels = [];
const ifcLoader = new IFCLoader();
ifcLoader.ifcManager.setWasmPath('../../../');
ifcLoader.load('../../../IFC/01.ifc', async (ifcModel) => {
    ifcModels.push(ifcModel);
    await setupAllCategories();
});


// Sets up optimized picking
ifcLoader.ifcManager.setupThreeMeshBVH(
    computeBoundsTree,
    disposeBoundsTree,
    acceleratedRaycast);

// List of categories names
const categories = {
    IFCWALLSTANDARDCASE,
    IFCSLAB,
    IFCFURNISHINGELEMENT,
    IFCDOOR,
    IFCWINDOW,
    IFCPLATE,
    IFCMEMBER,
};

// Gets the name of a category
function getName(category) {
    const names = Object.keys(categories);
    return names.find(name => categories[name] === category);
}

// Gets all the items of a category
async function getAll(category) {
    return ifcLoader.ifcManager.getAllItemsOfType(0, category, false);
}

// Creates a new subset containing all elements of a category
async function newSubsetOfType(category) {
    const ids = await getAll(category);
    return ifcLoader.ifcManager.createSubset({
        modelID: 0,
        scene,
        ids,
        removePrevious: true,
        customID: category.toString(),
    });
}

// Stores the created subsets
const subsets = {};

async function setupAllCategories() {
    const allCategories = Object.values(categories);
    for (let i = 0; i < allCategories.length; i++) {
        const category = allCategories[i];
        await setupCategory(category);
    }
}

// Creates a new subset and configures the checkbox
async function setupCategory(category) {
    subsets[category] = await newSubsetOfType(category);
    setupCheckBox(category);
}

// Sets up the checkbox event to hide / show elements
function setupCheckBox(category) {
    const name = getName(category);
    const checkBox = document.getElementById(name);
    checkBox.addEventListener('change', (event) => {
        const checked = event.target.checked;
        const subset = subsets[category];
        if (checked) scene.add(subset);
        else subset.removeFromParent();
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM