Skip to content

Commit

Permalink
demo
Browse files Browse the repository at this point in the history
  • Loading branch information
RyugaRyuzaki committed Sep 28, 2023
1 parent 4c1da5c commit ddf8475
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 187 deletions.
23 changes: 10 additions & 13 deletions .tmp/build/src/visual.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,21 @@ import 'regenerator-runtime/runtime';
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import * as OBC from 'openbim-components';
import * as THREE from 'three';
import "../style/visual.less";
/**
*
*/
export declare class Visual implements IVisual {
private target;
private updateCount;
private visualHost;
private events;
private selectionManager;
private viewer;
constructor(options: VisualConstructorOptions);
/**
* call this function every data change
* @param options
* @returns
*/
update(options: VisualUpdateOptions): void;
components: OBC.Components;
fragmentManager: OBC.FragmentManager;
highlighter: OBC.FragmentHighlighter;
private initScene;
private loadIfcModel;
private getSphereModel;
private fitToZoom;
_viewSphere: THREE.Sphere;
set viewSphere(sphere: THREE.Sphere);
get viewSphere(): THREE.Sphere;
}
2 changes: 1 addition & 1 deletion .tmp/drop/pbiviz.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .tmp/drop/status
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
1695468282075
1695866105911
openbimComponent
11 changes: 11 additions & 0 deletions .tmp/drop/visual.css
Original file line number Diff line number Diff line change
Expand Up @@ -1695,9 +1695,20 @@ dialog::backdrop{
p {
font-size: 20px;
font-weight: bold;
color: black;
z-index: 2000;
}
p em {
background: yellow;
padding: 5px;
}
table,
th,
td {
border: 1px solid;
}
th,
td {
padding: 15px;
}

2 changes: 1 addition & 1 deletion .tmp/drop/visual.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
],
"cSpell.words": [
"openbim",
"pako",
"pbiviz",
"postproduction",
"powerbi"
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# install
- Node JS >=20.0.5
- Power BI Desktop
- python >=3.10(recommended 3.11)
# install pbiviz local
- npm i -g powerbi-visuals-tools@latest
(https://learn.microsoft.com/vi-vn/power-bi/developer/visuals/environment-setup)
35 changes: 13 additions & 22 deletions capabilities.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
{
"dataRoles": [{
"displayName": "Category Data",
"name": "category",
"kind": "Grouping"
},
{
"displayName": "Measure Data",
"name": "measure",
"kind": "Measure"
}
],
"displayName": "Bim Model",
"name": "model",
"kind": "GroupingOrMeasure"
}],
"objects": {
"dataPoint": {
"properties": {
Expand Down Expand Up @@ -52,25 +46,22 @@
}
},
"dataViewMappings": [{
"categorical": {
"categories": {

"table": {
"rows": {
"for": {
"in": "category"
"in": "model"
},
"dataReductionAlgorithm": {
"top": {}
}
},
"values": {
"select": [{
"bind": {
"to": "measure"
"window": {
"count": 100000
}
}]
}
}
}
}],

"supportsMultiVisualSelection": true,
"supportsHighlight": true,
"privileges": [{
"name": "WebAccess",
"essential": true,
Expand Down
16 changes: 3 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
"pako": "^2.1.0",
"powerbi-visuals-api": "~5.3.0",
"powerbi-visuals-utils-formattingmodel": "5.0.0",
"regenerator-runtime": "^0.14.0",
"three": "^0.156.1",
"three-mesh-bvh": "^0.6.7",
"web-ifc": "^0.0.42"
"regenerator-runtime": "^0.14.0"
},
"devDependencies": {
"@types/node": "^20.6.3",
Expand All @@ -33,4 +30,4 @@
"eslint-plugin-powerbi-visuals": "^0.8.1",
"typescript": "4.9.3"
}
}
}
197 changes: 197 additions & 0 deletions src/Viewer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@

import powerbi from "powerbi-visuals-api";
import * as OBC from 'openbim-components'
import * as THREE from 'three'
import * as pako from "pako";
import IVisualEventService = powerbi.extensibility.IVisualEventService;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import ISelectionId = powerbi.visuals.ISelectionId
const baseUrl = "http://localhost:3000";
const fileId = "CenterConference";
export class Viewer {
/**
*
*/
components!: OBC.Components
container!: HTMLDivElement
fragmentManager!: OBC.FragmentManager
highlighter!: OBC.FragmentHighlighter
highlightMaterial!: THREE.MeshBasicMaterial
boundingBox!: THREE.Box3

private readonly target: any | HTMLDivElement
private selectionManager: ISelectionManager
private events: IVisualEventService;
private options: VisualUpdateOptions;
private selectionIds!: DataPoint[];
constructor( target: any | HTMLDivElement, selectionManager: ISelectionManager, selectionIds: DataPoint[], events: IVisualEventService, options: VisualUpdateOptions ) {
this.target = target
this.selectionManager = selectionManager
this.selectionIds = selectionIds
this.events = events
this.options = options
this.initScene()
this.initFragment()
this.loadFragment()
}
/**
* init scene
*/
private initScene() {
this.container = document.createElement( 'div' )
this.container.className = 'full-screen'
this.container.style.zIndex = '2000'
this.target.style.cursor = 'default'
this.target.appendChild( this.container )
this.components = new OBC.Components();
this.components.scene = new OBC.SimpleScene( this.components );
this.components.renderer = new OBC.PostproductionRenderer( this.components, this.container );
this.components.camera = new OBC.SimpleCamera( this.components );
this.components.raycaster = new OBC.SimpleRaycaster( this.components );

this.components.init();
( this.components.renderer as OBC.PostproductionRenderer ).postproduction.enabled = true;

const scene = this.components.scene.get();
// scene.background = null;
( this.components.camera as OBC.SimpleCamera ).controls.setLookAt( 10, 10, 10, 0, 0, 0 );

const directionalLight = new THREE.DirectionalLight();
directionalLight.position.set( 5, 10, 3 );
directionalLight.intensity = 0.5;
scene.add( directionalLight );

const ambientLight = new THREE.AmbientLight();
ambientLight.intensity = 0.5;
scene.add( ambientLight );

const grid = new OBC.SimpleGrid( this.components, new THREE.Color( 0x666666 ) );
this.components.tools.add( 'grid', grid );
const gridMesh = grid.get();
const effects = ( this.components.renderer as OBC.PostproductionRenderer ).postproduction.customEffects;
effects.excludedMeshes.push( gridMesh )
this.initToolBar()

}
/**
* init tool bar
*/
loadButton!: OBC.Button
private initToolBar() {
const toolbar = new OBC.Toolbar( this.components, { position: 'right' } );
this.components.ui.addToolbar( toolbar );
this.loadButton = new OBC.Button( this.components );
this.loadButton.materialIcon = "Home";
this.loadButton.tooltip = "Home";
toolbar.addChild( this.loadButton );
this.loadButton.onclick = () => {
if ( !this.boundingBox ) return
this.fitToZoom()
}
}
private fitToZoom() {
const { max, min } = this.boundingBox;
if ( !max || !min ) return;
// define vector from max to min
const dir = max.clone().sub( min.clone() ).normalize();
// distance max to min
const dis = max.distanceTo( min );
// center

const center = max.clone().add( dir.clone().multiplyScalar( -0.5 * dis ) );

// camera position
const pos = max.clone().add( dir.clone().multiplyScalar( 0.5 * dis ) );
// set true mean we can animate
( this.components.camera as OBC.SimpleCamera ).controls.setLookAt( pos.x, pos.y, pos.z, center.x, center.y, center.z, true );
}
/**
* dispose
*/
private disposeFragment() {
this.fragmentManager.dispose()
this.highlighter.dispose()
this.highlightMaterial.dispose()
}
/**
* init fragment
*/
private initFragment() {
this.fragmentManager = new OBC.FragmentManager( this.components );
this.highlighter = new OBC.FragmentHighlighter( this.components, this.fragmentManager );
( this.components.renderer as OBC.PostproductionRenderer ).postproduction.customEffects.outlineEnabled = true;
this.highlighter.outlineEnabled = true;
this.highlightMaterial = new THREE.MeshBasicMaterial( {
color: '#BCF124',
depthTest: false,
opacity: 0.8,
transparent: true
} );

this.highlighter.add( 'default', [this.highlightMaterial] );
this.highlighter.outlineMaterial.color.set( 0xf0ff7a );
this.container.addEventListener( 'click', ( event: MouseEvent ) => {
this.highlightOnClick( event )
} );

}
/**
*
* @param fileName load fragment
* @param type
*/
private loadFragment( fileName: string = fileId, type: string = 'frag' ) {
( async () => {
try {
const res = await fetch( `${baseUrl}/download/${fileName}${type}.gz`, {
method: "GET",
mode: "cors",
} )
const fileZip = await res.arrayBuffer()
const file = pako.inflate( fileZip )
const buffer = new Uint8Array( file );
const model = await this.fragmentManager.load( buffer );
this.highlighter.update();
if ( model.boundingBox ) {
this.boundingBox = model.boundingBox
this.fitToZoom()
}
// if everything is ok notify visual is success
this.events.renderingFinished( this.options );
} catch ( error ) {
// if not OK, notify visual is error that means can not show viewer in visual
this.events.renderingFailed( this.options );

}
} )()

}
lastSelection!: any
singleSelection: any = {
value: true,
};
private highlightOnClick = async ( event: MouseEvent ) => {
event.preventDefault();
const result = await this.highlighter.highlight( 'default', this.singleSelection.value, true );
if ( result ) {
this.lastSelection = {};

for ( const fragment of result.fragments ) {
const fragmentID = fragment.id;
this.lastSelection[fragmentID] = [result.id];
}
// find the selection by expressID
const selection = this.selectionIds.find( ( s: DataPoint ) => s.expressID.toString() === result.id )
// if found notify to another visual
await this.selectionManager.select( selection.selectionId )
}
}
}
/**
* define data point
*/
export interface DataPoint {
expressID: string | number;
selectionId: ISelectionId
}
Loading

0 comments on commit ddf8475

Please sign in to comment.