ngx-ifc-viewer
v0.0.23
Published
*Creator: Mads Holten Rasmussen*
Downloads
3
Readme
IFC-viewer for Angular
Creator: Mads Holten Rasmussen
The library includes my take on an IFC viewer for Angular based on IFC.js.
Install to Angular project
The library uses Angular Material Design, so please install this package beforehand.
npm i --save ngx-ifc-viewer
npm i --save three web-ifc-three camera-controls three-fatline
npm i --save-dev @types/three
Add to app.module.ts
import { IfcViewerModule } from 'ngx-ifc-viewer';
@NgModule({
imports: [
...,
IfcViewerModule
]
})
Use
See examples of use in the LD-BIM viewer.
Install to non-Angular project
npm i --save ngx-ifc-viewer
Add to HTML body
<ifc-viewer></ifc-viewer>
Copy the files web-ifc.wasm
and web-ifc-mt.wasm
from the folder node_modules/web-ifc/
to a new folder assets/ifcjs
.
Add the files IFCWorker.js
and IFCWorker.js.map
from node_modules/web-ifc-three/
.
Add some global styling to buttons that will also affect the buttons in the IFC-viewer (styles.scss
):
.btn {
background-color: DodgerBlue;
border: none;
color: white;
padding: 12px 16px;
font-size: 16px;
cursor: pointer;
}
/* Darker background on mouse-over */
.btn:hover {
background-color: RoyalBlue;
}
Use
See examples of use in the LD-BIM viewer.
Viewer
The viewer inherits the size of its parent div, so the example below will give us a full screen viewer.
<div style="width: 100%; height: 100%">
<ifc-viewer></ifc-viewer>
</div>
The viewer per default has a button to upload a model, but it is also possible to do that from outside using the IFCModelLoaderService
. This service takes a file and returns an observable that first shows the status of the loading process and once finished has the resulting model attached. This model can then be passed to the IFCViewerService
or other services such as the Linked Building Data (LBD) toolset. The viewer service can at any time be used to access all the models that have been loaded.
import { IFCModelLoaderService, IFCViewerService } from 'ngx-ifc-viewer';
import { IFCModel } from 'web-ifc-three/IFC/components/IFCModel';
@Component({
selector: 'app-component',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
public models: IFCModel[] = [];
constructor(
private: _modelLoader: IFCModelLoaderService,
private _viewer: IFCViewerService
){}
uploadFile(files: FileList){
this._modelLoader.loadModel(files[0]).subscribe(async (status: LoadingStatus) => {
// Load finished
if(status.result != undefined){
const model = status.result;
// Add to array of models
this.models.push(model);
// Append model to scene
this._viewer.appendModel(model);
}
},
err => console.log(err))
}
}
Input
|Name |Description | |:---------|:----------------------------------------------------------------------------------| |settings | An instance of the MenuPanelSettings class which holds all settings for the viewer|
Output
- elementClick
- canvasClick
Menu panel
The content displayed in the menu panel can be limited by specifying so in the viewer settings.
The top menu panel can be extended by placing HTML content between the <ifc-viewer>
-tags. To display this content in the top menu after a model is loaded in the scene, use the menuPostLoadContent
-selector as demonstrated below.
<div style="width: 100%; height: 100%">
<ifc-viewer>
<!-- MENU PANEL ADDITIONAL BUTTONS -->
<ng-container menuPostLoadContent>
<!-- My function -->
<button class="btn" *ngIf="rdfParsed" (click)="myFunction()" matTooltip="Do something cool">
<fa-icon [icon]="faUnicorn"></fa-icon>
</button>
</ng-container>
</ifc-viewer>
</div>
In the above example we append a font-awesome icon with a unicorn. This requires the icon to also be loaded from the @fortawesome/free-solid-svg-icons
package:
import { faUnicorn } from '@fortawesome/free-solid-svg-icons';
@Component({
...
})
export class AppComponent {
public faUnicorn = faUnicorn;
...
}
Context menu
The content displayed in the context menu on element click can be limited by specifying so in the viewer settings.
The context menu can also be extended with custom items by placing HTML content between the <ifc-viewer>
-tags. To display this content in the top menu after a model is loaded in the scene, use the contextMenu
-selector as demonstrated below.
This requires MatMenuModule
and MatButtonModule
to be imported in the app module.
<div style="width: 100%; height: 100%">
<ifc-viewer (elementClick)="lastClicekedElement = $event">
<!-- CONTEXT MENU ADDITIONAL BUTTONS -->
<ng-container contextMenu>
<button mat-menu-item (click)="colorElement()">Color element</button>
</ng-container>
</ifc-viewer>
</div>
Coloring
In the context menu example we define a "color element" function. Implementing this functionality is quite simple using the ColorService
. In the example below we make use of the lastClickedElement
which is defined by element clicks in the model using the elementClick
-output of the viewer (se context menu HTML snippet). From this we can access the expressID of the element and feed it to the colorSubset
-method of the color service. We also provide it with a stringified version of the expressID as the customID. The customID is used by the color service to know that it already exists in the scene, and therefore clicking and coloring the same element again will toggle the color off.
import { ColorService, ElementClickEvent } from 'ngx-ifc-viewer';
@Component({
...
})
export class AppComponent {
...
public lastClicekedElement?: ElementClickEvent;
constructor(
private _color: ColorService
){}
colorElement(){
if(this.lastClickedElement == undefined) return;
const expressID = this.lastClickedElement.expressID;
this._color.colorSubset([expressID], "red", expressID.toString());
}
}
The color service also has a function that applies color to groups of elements. Simply feed it with a nested array of expressIDs (an array per group) and the service will apply colors and return the colors used. The default color pallette has 10 colors.
import { ColorService, IFCModelLoaderService } from 'ngx-ifc-viewer';
@Component({
...
})
export class AppComponent {
...
constructor(
private _modelLoader: IFCModelLoaderService,
private _color: ColorService
){}
colorGroups(){
const wallIDs = await this._modelLoader.ifc.getAllItemsOfType(modelID, IFCWALLSTANDARDCASE, false);
const windowIDs = await this._modelLoader.ifc.getAllItemsOfType(modelID, IFCWINDOW, false);
const doorIDs = await this._modelLoader.ifc.getAllItemsOfType(modelID, IFCDOOR, false);
const colors = await this._color.colorByGroup([wallIDs, windowIDs, doorIDs]);
console.log(colors);
}
}