├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
└── org.eclipse.jdt.core.prefs
├── README.markdown
├── bimsurfer
├── lib
│ ├── DRACOLoader.js
│ ├── GLTFLoader.js
│ ├── OrbitControls.js
│ ├── StringView.js
│ ├── arraybuffer-slice.js
│ ├── domReady.js
│ ├── draco_decoder.js
│ ├── draco_decoder.wasm
│ ├── draco_wasm_wrapper.js
│ ├── es6-promise-3.2.2.min.js
│ ├── require.js
│ ├── svg-pan-zoom.js
│ ├── text.js
│ ├── three.js
│ ├── xeogl.js
│ └── xeogl.min.js
└── src
│ ├── AnnotationRenderer.js
│ ├── Assets.js
│ ├── BimServerGeometryLoader.js
│ ├── BimServerModel.js
│ ├── BimServerModelLoader.js
│ ├── BimSurfer.js
│ ├── DataInputStreamReader.js
│ ├── DefaultMaterials.js
│ ├── EventHandler.js
│ ├── MeasurementCanvas.js
│ ├── MetaDataRenderer.js
│ ├── MultiModal.js
│ ├── Notifier.js
│ ├── PreloadQuery.js
│ ├── Request.js
│ ├── StaticTreeRenderer.js
│ ├── Utils.js
│ ├── svgViewer
│ └── svgViewer.js
│ ├── threeViewer
│ └── threeViewer.js
│ └── xeoViewer
│ ├── controls
│ ├── bimCameraControl.js
│ └── cursors
│ │ └── rotate.png
│ ├── effects
│ └── highlightEffect.js
│ ├── entities
│ ├── bimModel.js
│ └── bimObject.js
│ ├── geometry
│ └── vectorTextGeometry.js
│ ├── helpers
│ └── bimBoundaryHelper.js
│ ├── utils
│ └── collection.js
│ └── xeoViewer.js
├── css
├── metadata.css
└── tree.css
├── examples
├── README.md
├── bimserver.html
├── build-gltf_app.js
├── css
│ ├── apiref.css
│ └── demo.css
├── gltf.html
├── gltf_app.js
├── gltf_app.min.js
├── js
│ ├── app.js
│ └── utils.js
└── models
│ ├── Duplex_A_20110907_optimized.bin
│ ├── Duplex_A_20110907_optimized.glb
│ ├── Duplex_A_20110907_optimized.gltf
│ ├── Duplex_A_20110907_optimized.xml
│ ├── Duplex_A_20110907_optimized0FS.glsl
│ ├── Duplex_A_20110907_optimized0VS.glsl
│ ├── Duplex_A_20110907_optimized1FS.glsl
│ └── Duplex_A_20110907_optimized1VS.glsl
├── files
└── gltf
│ └── gearbox
│ ├── gearbox_assy.bin
│ └── gearbox_assy.gltf
├── index.html
├── license.txt
├── plugin
├── icon.png
├── plugin.xml
└── version.properties
└── pom.xml
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | BIMsurferV2
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.ui.externaltools.ExternalToolBuilder
10 | full,incremental,
11 |
12 |
13 | LaunchConfigHandle
14 | <project>/.externalToolBuilders/org.eclipse.jdt.core.javabuilder (2).launch
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.common.project.facet.core.builder
20 |
21 |
22 |
23 |
24 | org.eclipse.m2e.core.maven2Builder
25 |
26 |
27 |
28 |
29 |
30 | org.eclipse.jdt.core.javanature
31 | org.eclipse.m2e.core.maven2Nature
32 | org.eclipse.wst.common.project.facet.core.nature
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 | encoding/bimsurfer=UTF-8
4 | encoding/css=UTF-8
5 | encoding/plugin=UTF-8
6 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
3 | org.eclipse.jdt.core.compiler.compliance=1.5
4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
5 | org.eclipse.jdt.core.compiler.release=disabled
6 | org.eclipse.jdt.core.compiler.source=1.5
7 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # Table of Contents
2 |
3 | - [Introduction](#introduction)
4 | - [Usage](#usage)
5 | - [BIMSurfer](#bimsurfer)
6 | - [Objects](#objects)
7 | - [Selecting and deselecting objects](#selecting-and-deselecting-objects)
8 | - [Showing and hiding objects](#showing-and-hiding-objects)
9 | - [Changing color and transparency of objects](#changing-color-and-transparency-of-objects)
10 | - [Camera](#camera)
11 | - [Controlling the camera](#controlling-the-camera)
12 | - [Fitting objects in view](#fitting-objects-in-view)
13 | - [Resetting](#resetting)
14 | - [Camera](#camera-1)
15 | - [Objects](#objects-1)
16 |
17 | # Introduction
18 |
19 | BIMSurfer2 is a WebGL-based IFC model viewer for BIMServer and IfcOpenShell built on xeogl, ThreeJS and SVG.
20 |
21 | ## Versions
22 |
23 | Over time there have been various versions of BIMsurfer. You are now looking at the v2 version of BIMSurfer.
24 |
25 | |BIMSufer|Model loader|Technologies used|
26 | |---|---|---|
27 | |v1|BIMServer|XeoEngine|
28 | |v2|IfcOpenShell/glTF, BIMServer|ThreeJS, xeogl, SVG|
29 | |v3|BIMServer|Custom webgl2|
30 |
31 | Visit BIMSurfer3 here https://github.com/opensourceBIM/BIMsurfer/
32 |
33 | ### Selecting which version of BIMSurfer to use
34 |
35 | Usage of v1 is not recommended in new projects, because of the dependency on outdated libraries and lack of a stable API. Choose v3 for highest performance, but note that is webgl2 only, which is not universally supported (54% at the time of writing [source](https://webglstats.com/webgl2)). v2 can be used solely on static files generated by IfcOpenShell and is entirely built around open standards such as glTF. v3 has an interesting set of additional features such as partial support for 3D Tiles, measurements and up to 6 section planes.
36 |
37 | # Usage
38 |
39 | ## BIMSurfer
40 |
41 | Creating a [BIMSurfer](bimsurfer/src/BimSurfer.js):
42 |
43 | ````javascript
44 | var bimSurfer = new BimSurfer({
45 | domNode: "viewerContainer"
46 | });
47 | ````
48 |
49 | Loading a model from BIMServer:
50 |
51 | ````javascript
52 | bimSurfer.load({
53 | bimserver: ADDRESS,
54 | username: USERNAME,
55 | password: PASSWORD,
56 | poid: 131073,
57 | roid: 65539,
58 | schema: "ifc2x3tc1" // < TODO: Deduce automatically
59 | })
60 | .then(function (model) {
61 |
62 | // Model is now loaded and rendering.
63 | // The following sections show what you can do with BIMSurfer at this point.
64 | //...
65 | });
66 | ````
67 |
68 | Generate a random test model if you want to test BIMSurfer without loading anything from BIMServer:
69 |
70 | ````javascript
71 | bimSurfer.loadRandom();
72 | ````
73 |
74 | The following usage examples in this guide will refer to objects from the generated test model.
75 |
76 | ## Objects
77 |
78 | ### Selecting and deselecting objects
79 |
80 | Selecting four objects:
81 |
82 | ````javascript
83 | bimSurfer.setSelection({ids: ["object3", "object2", "object4", "object6"], selected: true });
84 | ````
85 |
86 | then querying which objects are selected:
87 |
88 | ````javascript
89 | bimSurfer.getSelection()
90 | ````
91 |
92 | The result shows that those four objects are currently selected:
93 |
94 | ````json
95 | ["object3", "object2", "object4", "object6"]
96 | ````
97 |
98 | If we then deselect two objects, then query the selection again:
99 |
100 | ````javascript
101 | bimSurfer.setSelection({ids: ["object3", "object6"], selected: false });
102 | bimSurfer.getSelection()
103 | ````
104 |
105 | The result shows that only two objects are now selected:
106 |
107 | ````json
108 | ["object2", "object4"]
109 | ````
110 |
111 | Subscribing to selection updates:
112 |
113 | ````javascript
114 | bimSurfer.on("selection-changed",
115 | function() {
116 | var selected = bimSurfer.getSelection();
117 | console.log("selection = " + JSON.stringify(selected));
118 | });
119 | ````
120 |
121 | ### Showing and hiding objects
122 |
123 | Hiding three objects by ID:
124 |
125 | ````javascript
126 | bimSurfer.setVisibility({ids: ["object3", "object1", "object6"], visible: false });
127 | ````
128 |
129 | Setting two objects visible by ID:
130 |
131 | ````javascript
132 | bimSurfer.setVisibility({ids: ["object1", "object6"], visible: true });
133 | ````
134 |
135 | Hiding all objects of IFC types "IfcSlab" and "IfcWall":
136 |
137 | ````javascript
138 | bimSurfer.setVisibility({types: ["IfcSlab", "IfcWall"], visible: false });
139 | ````
140 |
141 | ### Changing color and transparency of objects
142 |
143 | Making two objects pink:
144 |
145 | ````javascript
146 | bimSurfer.setColor({ids: ["object3", "object6"], color: [1, 0, 1] })
147 | ````
148 |
149 | An optional fourth element may be specified in the color to set opacity:
150 |
151 | ````javascript
152 | bimSurfer.setColor({ids: ["object3", "object6"], color: [1, 0, 1, 0.5] })
153 | ````
154 |
155 | ## Camera
156 |
157 | ### Controlling the camera
158 |
159 | Setting the camera position:
160 |
161 | ````javascript
162 | bimSurfer.setCamera({
163 | eye: [-20,0,20],
164 | target: [0,10,0],
165 | up: [0,1,0]
166 | });
167 | ````
168 |
169 | Then "target" will then be the position we'll orbit about with the mouse or arrow keys (until we double-click an object to
170 | select a different orbit position).
171 |
172 | Setting the camera projection to orthographic:
173 |
174 | ````javascript
175 | bimSurfer.setCamera({
176 | type:"ortho"
177 | });
178 | ````
179 |
180 | Setting the view volume size for orthographic, switching to orthographic projection first if necessary:
181 |
182 | ````javascript
183 | bimSurfer.setCamera({
184 | type:"ortho",
185 | scale: 100
186 | });
187 | ````
188 | This uses the same technique as Blender, where the scale argument relates to the "real world" size of the model, meaning
189 | that if you set scale to 100, then your view would at most encompass an element of 100 units size.
190 |
191 | Setting the camera projection to perspective:
192 |
193 | ````javascript
194 | bimSurfer.setCamera({
195 | type:"persp"
196 | });
197 | ````
198 |
199 | Setting the FOV on Y-axis for perspective, switching to perspective projection first if necessary:
200 |
201 | ````javascript
202 | bimSurfer.setCamera({
203 | type:"persp",
204 | fovy: 65
205 | });
206 | ````
207 |
208 | Querying camera state:
209 |
210 | ````javascript
211 | var camera = bimSurfer.getCamera();
212 | ````
213 |
214 | The returned value would be:
215 |
216 | ````json
217 | {
218 | "type": "persp",
219 | "eye": [-20,0,20],
220 | "target": [0,10,0],
221 | "up": [0,1,0],
222 | "fovy": 65
223 | }
224 | ````
225 |
226 | Subscribing to camera updates:
227 |
228 | ````javascript
229 | bimSurfer.on("camera-changed",
230 | function() {
231 | var camera = bimSurfer.getCamera();
232 | console.log(JSON.stringify(camera));
233 | });
234 | ````
235 |
236 | ### Fitting objects in view
237 |
238 | Flying the camera to fit the specified objects in view:
239 |
240 | ````javascript
241 | bimSurfer.viewFit({ ids: ["object3", "object1", "object6"], animate: true });
242 | ````
243 |
244 | Jumping the camera to fit the specified objects in view:
245 |
246 | ````javascript
247 | bimSurfer.viewFit({ids: ["object1", "object6"], animate: false });
248 | ````
249 |
250 | Flying to fit all objects in view:
251 |
252 | ````javascript
253 | bimSurfer.viewFit({ animate: true });
254 | ````
255 |
256 | Jumping to fit all objects in view:
257 |
258 | ````javascript
259 | bimSurfer.viewFit({ animate: false });
260 | ````
261 |
262 | ## Resetting
263 |
264 | ### Camera
265 |
266 | Resetting the camera to initial position:
267 |
268 | ````javascript
269 | bimSurfer.reset({ cameraPosition: true });
270 | ````
271 |
272 | ### Objects
273 |
274 | Resetting all objects to initial visibilities:
275 |
276 | ````javascript
277 | bimSurfer.reset({ visibility: true });
278 | ````
279 |
280 | Resetting two objects to their initial visibilities:
281 |
282 | ````javascript
283 | bimSurfer.reset({ ids: ["object3", "object6"], visibility: true });
284 | ````
285 |
286 | Resetting all objects to their initial colors:
287 |
288 | ````javascript
289 | bimSurfer.reset({ elementColors: true });
290 | ````
291 |
292 | Resetting two objects to their initial colors:
293 |
294 | ````javascript
295 | bimSurfer.reset({ ids: ["object3", "object6"], elementColors: true });
296 | ````
297 |
298 | Deselecting all objects:
299 |
300 | ````javascript
301 | bimSurfer.reset({ selectionState: true });
302 | ````
303 |
304 | # Acknowledgements
305 |
306 | BIM Surfer is licensed under the MIT License.
307 |
308 | Copyright 2020 BIM Surfer contributors
309 |
310 | For this version thanks to TNO and Bimforce and developed by @aothms @rubendel @xeolabs and @johltn
311 |
--------------------------------------------------------------------------------
/bimsurfer/lib/DRACOLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Don McCurdy / https://www.donmccurdy.com
3 | */
4 |
5 | THREE.DRACOLoader = function ( manager ) {
6 |
7 | THREE.Loader.call( this, manager );
8 |
9 | this.decoderPath = '';
10 | this.decoderConfig = {};
11 | this.decoderBinary = null;
12 | this.decoderPending = null;
13 |
14 | this.workerLimit = 4;
15 | this.workerPool = [];
16 | this.workerNextTaskID = 1;
17 | this.workerSourceURL = '';
18 |
19 | this.defaultAttributeIDs = {
20 | position: 'POSITION',
21 | normal: 'NORMAL',
22 | color: 'COLOR',
23 | uv: 'TEX_COORD'
24 | };
25 | this.defaultAttributeTypes = {
26 | position: 'Float32Array',
27 | normal: 'Float32Array',
28 | color: 'Float32Array',
29 | uv: 'Float32Array'
30 | };
31 |
32 | };
33 |
34 | THREE.DRACOLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
35 |
36 | constructor: THREE.DRACOLoader,
37 |
38 | setDecoderPath: function ( path ) {
39 |
40 | this.decoderPath = path;
41 |
42 | return this;
43 |
44 | },
45 |
46 | setDecoderConfig: function ( config ) {
47 |
48 | this.decoderConfig = config;
49 |
50 | return this;
51 |
52 | },
53 |
54 | setWorkerLimit: function ( workerLimit ) {
55 |
56 | this.workerLimit = workerLimit;
57 |
58 | return this;
59 |
60 | },
61 |
62 | /** @deprecated */
63 | setVerbosity: function () {
64 |
65 | console.warn( 'THREE.DRACOLoader: The .setVerbosity() method has been removed.' );
66 |
67 | },
68 |
69 | /** @deprecated */
70 | setDrawMode: function () {
71 |
72 | console.warn( 'THREE.DRACOLoader: The .setDrawMode() method has been removed.' );
73 |
74 | },
75 |
76 | /** @deprecated */
77 | setSkipDequantization: function () {
78 |
79 | console.warn( 'THREE.DRACOLoader: The .setSkipDequantization() method has been removed.' );
80 |
81 | },
82 |
83 | load: function ( url, onLoad, onProgress, onError ) {
84 |
85 | var loader = new THREE.FileLoader( this.manager );
86 |
87 | loader.setPath( this.path );
88 | loader.setResponseType( 'arraybuffer' );
89 |
90 | if ( this.crossOrigin === 'use-credentials' ) {
91 |
92 | loader.setWithCredentials( true );
93 |
94 | }
95 |
96 | loader.load( url, ( buffer ) => {
97 |
98 | var taskConfig = {
99 | attributeIDs: this.defaultAttributeIDs,
100 | attributeTypes: this.defaultAttributeTypes,
101 | useUniqueIDs: false
102 | };
103 |
104 | this.decodeGeometry( buffer, taskConfig )
105 | .then( onLoad )
106 | .catch( onError );
107 |
108 | }, onProgress, onError );
109 |
110 | },
111 |
112 | /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
113 | decodeDracoFile: function ( buffer, callback, attributeIDs, attributeTypes ) {
114 |
115 | var taskConfig = {
116 | attributeIDs: attributeIDs || this.defaultAttributeIDs,
117 | attributeTypes: attributeTypes || this.defaultAttributeTypes,
118 | useUniqueIDs: !! attributeIDs
119 | };
120 |
121 | this.decodeGeometry( buffer, taskConfig ).then( callback );
122 |
123 | },
124 |
125 | decodeGeometry: function ( buffer, taskConfig ) {
126 |
127 | var worker;
128 | var taskID = this.workerNextTaskID ++;
129 | var taskCost = buffer.byteLength;
130 |
131 | // TODO: For backward-compatibility, support 'attributeTypes' objects containing
132 | // references (rather than names) to typed array constructors. These must be
133 | // serialized before sending them to the worker.
134 | for ( var attribute in taskConfig.attributeTypes ) {
135 |
136 | var type = taskConfig.attributeTypes[ attribute ];
137 |
138 | if ( type.BYTES_PER_ELEMENT !== undefined ) {
139 |
140 | taskConfig.attributeTypes[ attribute ] = type.name;
141 |
142 | }
143 |
144 | }
145 |
146 | // Obtain a worker and assign a task, and construct a geometry instance
147 | // when the task completes.
148 | var geometryPending = this._getWorker( taskID, taskCost )
149 | .then( ( _worker ) => {
150 |
151 | worker = _worker;
152 |
153 | return new Promise( ( resolve, reject ) => {
154 |
155 | worker._callbacks[ taskID ] = { resolve, reject };
156 |
157 | worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
158 |
159 | // this.debug();
160 |
161 | } );
162 |
163 | } )
164 | .then( ( message ) => this._createGeometry( message.geometry ) );
165 |
166 | // Remove task from the task list.
167 | geometryPending
168 | .finally( () => {
169 |
170 | if ( worker && taskID ) {
171 |
172 | this._releaseTask( worker, taskID );
173 |
174 | // this.debug();
175 |
176 | }
177 |
178 | } );
179 |
180 | return geometryPending;
181 |
182 | },
183 |
184 | _createGeometry: function ( geometryData ) {
185 |
186 | var geometry = new THREE.BufferGeometry();
187 |
188 | if ( geometryData.index ) {
189 |
190 | geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );
191 |
192 | }
193 |
194 | for ( var i = 0; i < geometryData.attributes.length; i ++ ) {
195 |
196 | var attribute = geometryData.attributes[ i ];
197 | var name = attribute.name;
198 | var array = attribute.array;
199 | var itemSize = attribute.itemSize;
200 |
201 | geometry.addAttribute( name, new THREE.BufferAttribute( array, itemSize ) );
202 |
203 | }
204 |
205 | return geometry;
206 |
207 | },
208 |
209 | _loadLibrary: function ( url, responseType ) {
210 |
211 | var loader = new THREE.FileLoader( this.manager );
212 | loader.setPath( this.decoderPath );
213 | loader.setResponseType( responseType );
214 |
215 | return new Promise( ( resolve, reject ) => {
216 |
217 | loader.load( url, resolve, undefined, reject );
218 |
219 | } );
220 |
221 | },
222 |
223 | _initDecoder: function () {
224 |
225 | if ( this.decoderPending ) return this.decoderPending;
226 |
227 | var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
228 | var librariesPending = [];
229 |
230 | if ( useJS ) {
231 |
232 | librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
233 |
234 | } else {
235 |
236 | librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
237 | librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
238 |
239 | }
240 |
241 | this.decoderPending = Promise.all( librariesPending )
242 | .then( ( libraries ) => {
243 |
244 | var jsContent = libraries[ 0 ];
245 |
246 | if ( ! useJS ) {
247 |
248 | this.decoderConfig.wasmBinary = libraries[ 1 ];
249 |
250 | }
251 |
252 | var fn = THREE.DRACOLoader.DRACOWorker.toString();
253 |
254 | var body = [
255 | '/* draco decoder */',
256 | jsContent,
257 | '',
258 | '/* worker */',
259 | fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
260 | ].join( '\n' );
261 |
262 | this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
263 |
264 | } );
265 |
266 | return this.decoderPending;
267 |
268 | },
269 |
270 | _getWorker: function ( taskID, taskCost ) {
271 |
272 | return this._initDecoder().then( () => {
273 |
274 | if ( this.workerPool.length < this.workerLimit ) {
275 |
276 | var worker = new Worker( this.workerSourceURL );
277 |
278 | worker._callbacks = {};
279 | worker._taskCosts = {};
280 | worker._taskLoad = 0;
281 |
282 | worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
283 |
284 | worker.onmessage = function ( e ) {
285 |
286 | var message = e.data;
287 |
288 | switch ( message.type ) {
289 |
290 | case 'decode':
291 | worker._callbacks[ message.id ].resolve( message );
292 | break;
293 |
294 | case 'error':
295 | worker._callbacks[ message.id ].reject( message );
296 | break;
297 |
298 | default:
299 | console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
300 |
301 | }
302 |
303 | };
304 |
305 | this.workerPool.push( worker );
306 |
307 | } else {
308 |
309 | this.workerPool.sort( function ( a, b ) {
310 |
311 | return a._taskLoad > b._taskLoad ? - 1 : 1;
312 |
313 | } );
314 |
315 | }
316 |
317 | var worker = this.workerPool[ this.workerPool.length - 1 ];
318 | worker._taskCosts[ taskID ] = taskCost;
319 | worker._taskLoad += taskCost;
320 | return worker;
321 |
322 | } );
323 |
324 | },
325 |
326 | _releaseTask: function ( worker, taskID ) {
327 |
328 | worker._taskLoad -= worker._taskCosts[ taskID ];
329 | delete worker._callbacks[ taskID ];
330 | delete worker._taskCosts[ taskID ];
331 |
332 | },
333 |
334 | debug: function () {
335 |
336 | console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
337 |
338 | },
339 |
340 | dispose: function () {
341 |
342 | for ( var i = 0; i < this.workerPool.length; ++ i ) {
343 |
344 | this.workerPool[ i ].terminate();
345 |
346 | }
347 |
348 | this.workerPool.length = 0;
349 |
350 | return this;
351 |
352 | }
353 |
354 | } );
355 |
356 | /* WEB WORKER */
357 |
358 | THREE.DRACOLoader.DRACOWorker = function () {
359 |
360 | var decoderConfig;
361 | var decoderPending;
362 |
363 | onmessage = function ( e ) {
364 |
365 | var message = e.data;
366 |
367 | switch ( message.type ) {
368 |
369 | case 'init':
370 | decoderConfig = message.decoderConfig;
371 | decoderPending = new Promise( function ( resolve/*, reject*/ ) {
372 |
373 | decoderConfig.onModuleLoaded = function ( draco ) {
374 |
375 | // Module is Promise-like. Wrap before resolving to avoid loop.
376 | resolve( { draco: draco } );
377 |
378 | };
379 |
380 | DracoDecoderModule( decoderConfig );
381 |
382 | } );
383 | break;
384 |
385 | case 'decode':
386 | var buffer = message.buffer;
387 | var taskConfig = message.taskConfig;
388 | decoderPending.then( ( module ) => {
389 |
390 | var draco = module.draco;
391 | var decoder = new draco.Decoder();
392 | var decoderBuffer = new draco.DecoderBuffer();
393 | decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
394 |
395 | try {
396 |
397 | var geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
398 |
399 | var buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
400 |
401 | if ( geometry.index ) buffers.push( geometry.index.array.buffer );
402 |
403 | self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
404 |
405 | } catch ( error ) {
406 |
407 | console.error( error );
408 |
409 | self.postMessage( { type: 'error', id: message.id, error: error.message } );
410 |
411 | } finally {
412 |
413 | draco.destroy( decoderBuffer );
414 | draco.destroy( decoder );
415 |
416 | }
417 |
418 | } );
419 | break;
420 |
421 | }
422 |
423 | };
424 |
425 | function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
426 |
427 | var attributeIDs = taskConfig.attributeIDs;
428 | var attributeTypes = taskConfig.attributeTypes;
429 |
430 | var dracoGeometry;
431 | var decodingStatus;
432 |
433 | var geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
434 |
435 | if ( geometryType === draco.TRIANGULAR_MESH ) {
436 |
437 | dracoGeometry = new draco.Mesh();
438 | decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
439 |
440 | } else if ( geometryType === draco.POINT_CLOUD ) {
441 |
442 | dracoGeometry = new draco.PointCloud();
443 | decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
444 |
445 | } else {
446 |
447 | throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
448 |
449 | }
450 |
451 | if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
452 |
453 | throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
454 |
455 | }
456 |
457 | var geometry = { index: null, attributes: [] };
458 |
459 | // Gather all vertex attributes.
460 | for ( var attributeName in attributeIDs ) {
461 |
462 | var attributeType = self[ attributeTypes[ attributeName ] ];
463 |
464 | var attribute;
465 | var attributeID;
466 |
467 | // A Draco file may be created with default vertex attributes, whose attribute IDs
468 | // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
469 | // a Draco file may contain a custom set of attributes, identified by known unique
470 | // IDs. glTF files always do the latter, and `.drc` files typically do the former.
471 | if ( taskConfig.useUniqueIDs ) {
472 |
473 | attributeID = attributeIDs[ attributeName ];
474 | attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
475 |
476 | } else {
477 |
478 | attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
479 |
480 | if ( attributeID === - 1 ) continue;
481 |
482 | attribute = decoder.GetAttribute( dracoGeometry, attributeID );
483 |
484 | }
485 |
486 | geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
487 |
488 | }
489 |
490 | // Add index.
491 | if ( geometryType === draco.TRIANGULAR_MESH ) {
492 |
493 | // Generate mesh faces.
494 | var numFaces = dracoGeometry.num_faces();
495 | var numIndices = numFaces * 3;
496 | var index = new Uint32Array( numIndices );
497 | var indexArray = new draco.DracoInt32Array();
498 |
499 | for ( var i = 0; i < numFaces; ++ i ) {
500 |
501 | decoder.GetFaceFromMesh( dracoGeometry, i, indexArray );
502 |
503 | for ( var j = 0; j < 3; ++ j ) {
504 |
505 | index[ i * 3 + j ] = indexArray.GetValue( j );
506 |
507 | }
508 |
509 | }
510 |
511 | geometry.index = { array: index, itemSize: 1 };
512 |
513 | draco.destroy( indexArray );
514 |
515 | }
516 |
517 | draco.destroy( dracoGeometry );
518 |
519 | return geometry;
520 |
521 | }
522 |
523 | function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
524 |
525 | var numComponents = attribute.num_components();
526 | var numPoints = dracoGeometry.num_points();
527 | var numValues = numPoints * numComponents;
528 | var dracoArray;
529 |
530 | var array;
531 |
532 | switch ( attributeType ) {
533 |
534 | case Float32Array:
535 | dracoArray = new draco.DracoFloat32Array();
536 | decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, dracoArray );
537 | array = new Float32Array( numValues );
538 | break;
539 |
540 | case Int8Array:
541 | dracoArray = new draco.DracoInt8Array();
542 | decoder.GetAttributeInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
543 | array = new Int8Array( numValues );
544 | break;
545 |
546 | case Int16Array:
547 | dracoArray = new draco.DracoInt16Array();
548 | decoder.GetAttributeInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
549 | array = new Int16Array( numValues );
550 | break;
551 |
552 | case Int32Array:
553 | dracoArray = new draco.DracoInt32Array();
554 | decoder.GetAttributeInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
555 | array = new Int32Array( numValues );
556 | break;
557 |
558 | case Uint8Array:
559 | dracoArray = new draco.DracoUInt8Array();
560 | decoder.GetAttributeUInt8ForAllPoints( dracoGeometry, attribute, dracoArray );
561 | array = new Uint8Array( numValues );
562 | break;
563 |
564 | case Uint16Array:
565 | dracoArray = new draco.DracoUInt16Array();
566 | decoder.GetAttributeUInt16ForAllPoints( dracoGeometry, attribute, dracoArray );
567 | array = new Uint16Array( numValues );
568 | break;
569 |
570 | case Uint32Array:
571 | dracoArray = new draco.DracoUInt32Array();
572 | decoder.GetAttributeUInt32ForAllPoints( dracoGeometry, attribute, dracoArray );
573 | array = new Uint32Array( numValues );
574 | break;
575 |
576 | default:
577 | throw new Error( 'THREE.DRACOLoader: Unexpected attribute type.' );
578 |
579 | }
580 |
581 | for ( var i = 0; i < numValues; i ++ ) {
582 |
583 | array[ i ] = dracoArray.GetValue( i );
584 |
585 | }
586 |
587 | draco.destroy( dracoArray );
588 |
589 | return {
590 | name: attributeName,
591 | array: array,
592 | itemSize: numComponents
593 | };
594 |
595 | }
596 |
597 | };
598 |
599 | /** Deprecated static methods */
600 |
601 | /** @deprecated */
602 | THREE.DRACOLoader.setDecoderPath = function () {
603 |
604 | console.warn( 'THREE.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.' );
605 |
606 | };
607 |
608 | /** @deprecated */
609 | THREE.DRACOLoader.setDecoderConfig = function () {
610 |
611 | console.warn( 'THREE.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.' );
612 |
613 | };
614 |
615 | /** @deprecated */
616 | THREE.DRACOLoader.releaseDecoderModule = function () {
617 |
618 | console.warn( 'THREE.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.' );
619 |
620 | };
621 |
622 | /** @deprecated */
623 | THREE.DRACOLoader.getDecoderModule = function () {
624 |
625 | console.warn( 'THREE.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.' );
626 |
627 | };
628 |
--------------------------------------------------------------------------------
/bimsurfer/lib/arraybuffer-slice.js:
--------------------------------------------------------------------------------
1 | // https://github.com/ttaubert/node-arraybuffer-slice
2 | // (c) 2014 Tim Taubert
3 | // arraybuffer-slice may be freely distributed under the MIT license.
4 |
5 | (function (undefined) {
6 | "use strict";
7 |
8 | function clamp(val, length) {
9 | val = (val|0) || 0;
10 |
11 | if (val < 0) {
12 | return Math.max(val + length, 0);
13 | }
14 |
15 | return Math.min(val, length);
16 | }
17 |
18 | if (!ArrayBuffer.prototype.slice) {
19 | ArrayBuffer.prototype.slice = function (from, to) {
20 | var length = this.byteLength;
21 | var begin = clamp(from, length);
22 | var end = length;
23 |
24 | if (to !== undefined) {
25 | end = clamp(to, length);
26 | }
27 |
28 | if (begin > end) {
29 | return new ArrayBuffer(0);
30 | }
31 |
32 | var num = end - begin;
33 | var target = new ArrayBuffer(num);
34 | var targetArray = new Uint8Array(target);
35 |
36 | var sourceArray = new Uint8Array(this, begin, num);
37 | targetArray.set(sourceArray);
38 |
39 | return target;
40 | };
41 | }
42 | })();
43 |
44 |
45 | if (typeof Int8Array !== 'undefined') {
46 | if (!Int8Array.prototype.fill) Int8Array.prototype.fill = Array.prototype.fill;
47 | if (!Int8Array.prototype.slice) Int8Array.prototype.slice = Array.prototype.slice;
48 | }
49 | if (typeof Uint8Array !== 'undefined') {
50 | if (!Uint8Array.prototype.fill) Uint8Array.prototype.fill = Array.prototype.fill;
51 | if (!Uint8Array.prototype.slice) Uint8Array.prototype.slice = Array.prototype.slice;
52 | }
53 | if (typeof Uint8ClampedArray !== 'undefined') {
54 | if (!Uint8ClampedArray.prototype.fill) Uint8ClampedArray.prototype.fill = Array.prototype.fill;
55 | if (!Uint8ClampedArray.prototype.slice) Uint8ClampedArray.prototype.slice = Array.prototype.slice;
56 | }
57 | if (typeof Int16Array !== 'undefined') {
58 | if (!Int16Array.prototype.fill) Int16Array.prototype.fill = Array.prototype.fill;
59 | if (!Int16Array.prototype.slice) Int16Array.prototype.slice = Array.prototype.slice;
60 | }
61 | if (typeof Uint16Array !== 'undefined') {
62 | if (!Uint16Array.prototype.fill) Uint16Array.prototype.fill = Array.prototype.fill;
63 | if (!Uint16Array.prototype.slice) Uint16Array.prototype.slice = Array.prototype.slice;
64 | }
65 | if (typeof Int32Array !== 'undefined') {
66 | if (!Int32Array.prototype.fill) Int32Array.prototype.fill = Array.prototype.fill;
67 | if (!Int32Array.prototype.slice) Int32Array.prototype.slice = Array.prototype.slice;
68 | }
69 | if (typeof Uint32Array !== 'undefined') {
70 | if (!Uint32Array.prototype.fill) Uint32Array.prototype.fill = Array.prototype.fill;
71 | if (!Uint32Array.prototype.slice) Uint32Array.prototype.slice = Array.prototype.slice;
72 | }
73 | if (typeof Float32Array !== 'undefined') {
74 | if (!Float32Array.prototype.fill) Float32Array.prototype.fill = Array.prototype.fill;
75 | if (!Float32Array.prototype.slice) Float32Array.prototype.slice = Array.prototype.slice;
76 | }
77 | if (typeof Float64Array !== 'undefined') {
78 | if (!Float64Array.prototype.fill) Float64Array.prototype.fill = Array.prototype.fill;
79 | if (!Float64Array.prototype.slice) Float64Array.prototype.slice = Array.prototype.slice;
80 | }
81 | if (typeof TypedArray !== 'undefined') {
82 | if (!TypedArray.prototype.fill) TypedArray.prototype.fill = Array.prototype.fill;
83 | if (!TypedArray.prototype.slice) TypedArray.prototype.slice = Array.prototype.slice;
84 | }
--------------------------------------------------------------------------------
/bimsurfer/lib/domReady.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
3 | * Available via the MIT or new BSD license.
4 | * see: http://github.com/requirejs/domReady for details
5 | */
6 | /*jslint */
7 | /*global require: false, define: false, requirejs: false,
8 | window: false, clearInterval: false, document: false,
9 | self: false, setInterval: false */
10 |
11 |
12 | define(function () {
13 | 'use strict';
14 |
15 | var isTop, testDiv, scrollIntervalId,
16 | isBrowser = typeof window !== "undefined" && window.document,
17 | isPageLoaded = !isBrowser,
18 | doc = isBrowser ? document : null,
19 | readyCalls = [];
20 |
21 | function runCallbacks(callbacks) {
22 | var i;
23 | for (i = 0; i < callbacks.length; i += 1) {
24 | callbacks[i](doc);
25 | }
26 | }
27 |
28 | function callReady() {
29 | var callbacks = readyCalls;
30 |
31 | if (isPageLoaded) {
32 | //Call the DOM ready callbacks
33 | if (callbacks.length) {
34 | readyCalls = [];
35 | runCallbacks(callbacks);
36 | }
37 | }
38 | }
39 |
40 | /**
41 | * Sets the page as loaded.
42 | */
43 | function pageLoaded() {
44 | if (!isPageLoaded) {
45 | isPageLoaded = true;
46 | if (scrollIntervalId) {
47 | clearInterval(scrollIntervalId);
48 | }
49 |
50 | callReady();
51 | }
52 | }
53 |
54 | if (isBrowser) {
55 | if (document.addEventListener) {
56 | //Standards. Hooray! Assumption here that if standards based,
57 | //it knows about DOMContentLoaded.
58 | document.addEventListener("DOMContentLoaded", pageLoaded, false);
59 | window.addEventListener("load", pageLoaded, false);
60 | } else if (window.attachEvent) {
61 | window.attachEvent("onload", pageLoaded);
62 |
63 | testDiv = document.createElement('div');
64 | try {
65 | isTop = window.frameElement === null;
66 | } catch (e) {}
67 |
68 | //DOMContentLoaded approximation that uses a doScroll, as found by
69 | //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/,
70 | //but modified by other contributors, including jdalton
71 | if (testDiv.doScroll && isTop && window.external) {
72 | scrollIntervalId = setInterval(function () {
73 | try {
74 | testDiv.doScroll();
75 | pageLoaded();
76 | } catch (e) {}
77 | }, 30);
78 | }
79 | }
80 |
81 | //Check if document already complete, and if so, just trigger page load
82 | //listeners. Latest webkit browsers also use "interactive", and
83 | //will fire the onDOMContentLoaded before "interactive" but not after
84 | //entering "interactive" or "complete". More details:
85 | //http://dev.w3.org/html5/spec/the-end.html#the-end
86 | //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded
87 | //Hmm, this is more complicated on further use, see "firing too early"
88 | //bug: https://github.com/requirejs/domReady/issues/1
89 | //so removing the || document.readyState === "interactive" test.
90 | //There is still a window.onload binding that should get fired if
91 | //DOMContentLoaded is missed.
92 | if (document.readyState === "complete") {
93 | pageLoaded();
94 | }
95 | }
96 |
97 | /** START OF PUBLIC API **/
98 |
99 | /**
100 | * Registers a callback for DOM ready. If DOM is already ready, the
101 | * callback is called immediately.
102 | * @param {Function} callback
103 | */
104 | function domReady(callback) {
105 | if (isPageLoaded) {
106 | callback(doc);
107 | } else {
108 | readyCalls.push(callback);
109 | }
110 | return domReady;
111 | }
112 |
113 | domReady.version = '2.0.1';
114 |
115 | /**
116 | * Loader Plugin API method
117 | */
118 | domReady.load = function (name, req, onLoad, config) {
119 | if (config.isBuild) {
120 | onLoad(null);
121 | } else {
122 | domReady(onLoad);
123 | }
124 | };
125 |
126 | /** END OF PUBLIC API **/
127 |
128 | return domReady;
129 | });
130 |
--------------------------------------------------------------------------------
/bimsurfer/lib/draco_decoder.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AECgeeks/BIMsurfer2/926d1fcc08a373c879a2416110bc3107be1d654c/bimsurfer/lib/draco_decoder.wasm
--------------------------------------------------------------------------------
/bimsurfer/lib/es6-promise-3.2.2.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * @overview es6-promise - a tiny implementation of Promises/A+.
3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
4 | * @license Licensed under MIT license
5 | * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
6 | * @version 3.2.2+35df15ea
7 | */
8 |
9 | (function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function e(t){return"function"==typeof t}function n(t){G=t}function r(t){Q=t}function o(){return function(){process.nextTick(a)}}function i(){return function(){B(a)}}function s(){var t=0,e=new X(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){t.port2.postMessage(0)}}function c(){return function(){setTimeout(a,1)}}function a(){for(var t=0;J>t;t+=2){var e=tt[t],n=tt[t+1];e(n),tt[t]=void 0,tt[t+1]=void 0}J=0}function f(){try{var t=require,e=t("vertx");return B=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=new this.constructor(p);void 0===r[rt]&&k(r);var o=n._state;if(o){var i=arguments[o-1];Q(function(){x(o,r,i,n._result)})}else E(n,r,t,e);return r}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function d(){return new TypeError("A promises callback cannot return that same promise.")}function v(t){try{return t.then}catch(e){return ut.error=e,ut}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){Q(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):S(t,n))},function(e){r||(r=!0,j(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,j(t,o))},t)}function b(t,e){e._state===it?S(t,e._result):e._state===st?j(t,e._result):E(e,void 0,function(e){g(t,e)},function(e){j(t,e)})}function w(t,n,r){n.constructor===t.constructor&&r===et&&constructor.resolve===nt?b(t,n):r===ut?j(t,ut.error):void 0===r?S(t,n):e(r)?m(t,n,r):S(t,n)}function g(e,n){e===n?j(e,_()):t(n)?w(e,n,v(n)):S(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function S(t,e){t._state===ot&&(t._result=e,t._state=it,0!==t._subscribers.length&&Q(T,t))}function j(t,e){t._state===ot&&(t._state=st,t._result=e,Q(A,t))}function E(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+it]=n,o[i+st]=r,0===i&&t._state&&Q(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;si;i++)e.resolve(t[i]).then(n,r)}:function(t,e){e(new TypeError("You must pass an array to race."))})}function F(t){var e=this,n=new e(p);return j(n,t),n}function D(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function K(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function L(t){this[rt]=O(),this._result=this._state=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&D(),this instanceof L?C(this,t):K())}function N(t,e){this._instanceConstructor=t,this.promise=new t(p),this.promise[rt]||k(this.promise),I(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?S(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&S(this.promise,this._result))):j(this.promise,U())}function U(){return new Error("Array Methods must be provided an Array")}function W(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=pt)}var z;z=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var B,G,H,I=z,J=0,Q=function(t,e){tt[J]=t,tt[J+1]=e,J+=2,2===J&&(G?G(a):H())},R="undefined"!=typeof window?window:void 0,V=R||{},X=V.MutationObserver||V.WebKitMutationObserver,Z="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),$="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,tt=new Array(1e3);H=Z?o():X?s():$?u():void 0===R&&"function"==typeof require?f():c();var et=l,nt=h,rt=Math.random().toString(36).substring(16),ot=void 0,it=1,st=2,ut=new M,ct=new M,at=0,ft=Y,lt=q,ht=F,pt=L;L.all=ft,L.race=lt,L.resolve=nt,L.reject=ht,L._setScheduler=n,L._setAsap=r,L._asap=Q,L.prototype={constructor:L,then:et,"catch":function(t){return this.then(null,t)}};var _t=N;N.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===ot&&t>n;n++)this._eachEntry(e[n],n)},N.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===nt){var o=v(t);if(o===et&&t._state!==ot)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===pt){var i=new n(p);w(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},N.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===ot&&(this._remaining--,t===st?j(r,n):this._result[e]=n),0===this._remaining&&S(r,this._result)},N.prototype._willSettleAt=function(t,e){var n=this;E(t,void 0,function(t){n._settledAt(it,e,t)},function(t){n._settledAt(st,e,t)})};var dt=W,vt={Promise:pt,polyfill:dt};"function"==typeof define&&define.amd?define(function(){return vt}):"undefined"!=typeof module&&module.exports?module.exports=vt:"undefined"!=typeof this&&(this.ES6Promise=vt),dt()}).call(this);
--------------------------------------------------------------------------------
/bimsurfer/lib/require.js:
--------------------------------------------------------------------------------
1 | /*
2 | RequireJS 2.1.20 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
3 | Available via the MIT or new BSD license.
4 | see: http://github.com/jrburke/requirejs for details
5 | */
6 | var requirejs,require,define;
7 | (function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||e.onError!==ca)try{f=h.execCb(c,l,b,f)}catch(d){a=d}else f=h.execCb(c,l,b,f);this.map.isDefine&&
19 | void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(q[c]=f,e.onResourceLoad))e.onResourceLoad(h,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=
20 | !0)}}else t(h.defQueueMap,c)||this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=i(a.prefix);this.depMaps.push(d);s(d,"defined",u(this,function(f){var l,d;d=n(aa,this.map.id);var g=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,p=h.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(g=f.normalize(g,function(a){return c(a,P,!0)})||""),f=i(a.prefix+"!"+g,this.map.parentMap),s(f,"defined",u(this,function(a){this.init([],function(){return a},
21 | null,{enabled:!0,ignore:!0})})),d=n(m,f.id)){this.depMaps.push(f);if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=h.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];A(m,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,g=i(d),P=M;c&&(f=c);P&&
22 | (M=!1);r(g);t(k.config,b)&&(k.config[d]=k.config[b]);try{e.exec(f)}catch(m){return w(B("fromtexteval","fromText eval for "+b+" failed: "+m,m,[b]))}P&&(M=!0);this.depMaps.push(g);h.completeLoad(d);p([d],l)}),f.load(a.name,p,l,k))}));h.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=i(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=
23 | n(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;s(a,"defined",u(this,function(a){this.undefed||(this.defineDep(b,a),this.check())}));this.errback?s(a,"error",u(this,this.errback)):this.events.error&&s(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=m[c];!t(L,c)&&(f&&!f.enabled)&&h.enable(a,this)}));A(this.pluginMaps,u(this,function(a){var b=n(m,a.id);b&&!b.enabled&&h.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=
24 | []);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};h={config:k,contextName:b,registry:m,defined:q,urlFetched:S,defQueue:C,defQueueMap:{},Module:Z,makeModuleMap:i,nextTick:e.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=k.shim,c={paths:!0,bundles:!0,config:!0,map:!0};A(a,function(a,b){c[b]?(k[b]||(k[b]={}),U(k[b],a,!0,!0)):k[b]=a});a.bundles&&A(a.bundles,function(a,b){v(a,
25 | function(a){a!==b&&(aa[a]=b)})});a.shim&&(A(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=h.makeShimExports(a);b[c]=a}),k.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(k.paths[b]=a.location);k.pkgs[b]=a.name+"/"+(a.main||"main").replace(ha,"").replace(Q,"")});A(m,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=i(b,null,!0))});if(a.deps||a.callback)h.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;
26 | a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,j){function g(c,d,p){var k,n;j.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(B("requireargs","Invalid require call"),p);if(a&&t(L,c))return L[c](m[a.id]);if(e.get)return e.get(h,c,a,g);k=i(c,a,!1,!0);k=k.id;return!t(q,k)?w(B("notloaded",'Module name "'+k+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):q[k]}J();h.nextTick(function(){J();
27 | n=r(i(null,a));n.skipMap=j.skipMap;n.init(c,d,p,{enabled:!0});D()});return g}j=j||{};U(g,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),j=b.split("/")[0];if(-1!==e&&(!("."===j||".."===j)||1g.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1));g.src=d;J=g;D?y.insertBefore(g,D):y.appendChild(g);J=null;return g}if(ea)try{importScripts(d),b.completeLoad(c)}catch(i){b.onError(B("importscripts",
35 | "importScripts failed for "+c+" at "+d,i,[c]))}};z&&!s.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return r=I,s.baseUrl||(E=r.split("/"),r=E.pop(),O=E.length?E.join("/")+"/":"./",s.baseUrl=O),r=r.replace(Q,""),e.jsExtRegExp.test(r)&&(r=I),s.deps=s.deps?s.deps.concat(r):[r],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ja,"").replace(ka,
36 | function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}g?(g.defQueue.push([b,c,d]),g.defQueueMap[b]=!0):R.push([b,c,d])};define.amd={jQuery:!0};e.exec=function(b){return eval(b)};e(s)}})(this);
37 |
--------------------------------------------------------------------------------
/bimsurfer/lib/text.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
3 | * Available via the MIT or new BSD license.
4 | * see: http://github.com/requirejs/text for details
5 | */
6 | /*jslint regexp: true */
7 | /*global require, XMLHttpRequest, ActiveXObject,
8 | define, window, process, Packages,
9 | java, location, Components, FileUtils */
10 |
11 | define(['module'], function (module) {
12 | 'use strict';
13 |
14 | var text, fs, Cc, Ci, xpcIsWindows,
15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im,
18 | hasLocation = typeof location !== 'undefined' && location.href,
19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
20 | defaultHostName = hasLocation && location.hostname,
21 | defaultPort = hasLocation && (location.port || undefined),
22 | buildMap = {},
23 | masterConfig = (module.config && module.config()) || {};
24 |
25 | text = {
26 | version: '2.0.14',
27 |
28 | strip: function (content) {
29 | //Strips declarations so that external SVG and XML
30 | //documents can be added to a document without worry. Also, if the string
31 | //is an HTML document, only the part inside the body tag is returned.
32 | if (content) {
33 | content = content.replace(xmlRegExp, "");
34 | var matches = content.match(bodyRegExp);
35 | if (matches) {
36 | content = matches[1];
37 | }
38 | } else {
39 | content = "";
40 | }
41 | return content;
42 | },
43 |
44 | jsEscape: function (content) {
45 | return content.replace(/(['\\])/g, '\\$1')
46 | .replace(/[\f]/g, "\\f")
47 | .replace(/[\b]/g, "\\b")
48 | .replace(/[\n]/g, "\\n")
49 | .replace(/[\t]/g, "\\t")
50 | .replace(/[\r]/g, "\\r")
51 | .replace(/[\u2028]/g, "\\u2028")
52 | .replace(/[\u2029]/g, "\\u2029");
53 | },
54 |
55 | createXhr: masterConfig.createXhr || function () {
56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
57 | var xhr, i, progId;
58 | if (typeof XMLHttpRequest !== "undefined") {
59 | return new XMLHttpRequest();
60 | } else if (typeof ActiveXObject !== "undefined") {
61 | for (i = 0; i < 3; i += 1) {
62 | progId = progIds[i];
63 | try {
64 | xhr = new ActiveXObject(progId);
65 | } catch (e) {}
66 |
67 | if (xhr) {
68 | progIds = [progId]; // so faster next time
69 | break;
70 | }
71 | }
72 | }
73 |
74 | return xhr;
75 | },
76 |
77 | /**
78 | * Parses a resource name into its component parts. Resource names
79 | * look like: module/name.ext!strip, where the !strip part is
80 | * optional.
81 | * @param {String} name the resource name
82 | * @returns {Object} with properties "moduleName", "ext" and "strip"
83 | * where strip is a boolean.
84 | */
85 | parseName: function (name) {
86 | var modName, ext, temp,
87 | strip = false,
88 | index = name.lastIndexOf("."),
89 | isRelative = name.indexOf('./') === 0 ||
90 | name.indexOf('../') === 0;
91 |
92 | if (index !== -1 && (!isRelative || index > 1)) {
93 | modName = name.substring(0, index);
94 | ext = name.substring(index + 1);
95 | } else {
96 | modName = name;
97 | }
98 |
99 | temp = ext || modName;
100 | index = temp.indexOf("!");
101 | if (index !== -1) {
102 | //Pull off the strip arg.
103 | strip = temp.substring(index + 1) === "strip";
104 | temp = temp.substring(0, index);
105 | if (ext) {
106 | ext = temp;
107 | } else {
108 | modName = temp;
109 | }
110 | }
111 |
112 | return {
113 | moduleName: modName,
114 | ext: ext,
115 | strip: strip
116 | };
117 | },
118 |
119 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
120 |
121 | /**
122 | * Is an URL on another domain. Only works for browser use, returns
123 | * false in non-browser environments. Only used to know if an
124 | * optimized .js version of a text resource should be loaded
125 | * instead.
126 | * @param {String} url
127 | * @returns Boolean
128 | */
129 | useXhr: function (url, protocol, hostname, port) {
130 | var uProtocol, uHostName, uPort,
131 | match = text.xdRegExp.exec(url);
132 | if (!match) {
133 | return true;
134 | }
135 | uProtocol = match[2];
136 | uHostName = match[3];
137 |
138 | uHostName = uHostName.split(':');
139 | uPort = uHostName[1];
140 | uHostName = uHostName[0];
141 |
142 | return (!uProtocol || uProtocol === protocol) &&
143 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
144 | ((!uPort && !uHostName) || uPort === port);
145 | },
146 |
147 | finishLoad: function (name, strip, content, onLoad) {
148 | content = strip ? text.strip(content) : content;
149 | if (masterConfig.isBuild) {
150 | buildMap[name] = content;
151 | }
152 | onLoad(content);
153 | },
154 |
155 | load: function (name, req, onLoad, config) {
156 | //Name has format: some.module.filext!strip
157 | //The strip part is optional.
158 | //if strip is present, then that means only get the string contents
159 | //inside a body tag in an HTML string. For XML/SVG content it means
160 | //removing the declarations so the content can be inserted
161 | //into the current doc without problems.
162 |
163 | // Do not bother with the work if a build and text will
164 | // not be inlined.
165 | if (config && config.isBuild && !config.inlineText) {
166 | onLoad();
167 | return;
168 | }
169 |
170 | masterConfig.isBuild = config && config.isBuild;
171 |
172 | var parsed = text.parseName(name),
173 | nonStripName = parsed.moduleName +
174 | (parsed.ext ? '.' + parsed.ext : ''),
175 | url = req.toUrl(nonStripName),
176 | useXhr = (masterConfig.useXhr) ||
177 | text.useXhr;
178 |
179 | // Do not load if it is an empty: url
180 | if (url.indexOf('empty:') === 0) {
181 | onLoad();
182 | return;
183 | }
184 |
185 | //Load the text. Use XHR if possible and in a browser.
186 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
187 | text.get(url, function (content) {
188 | text.finishLoad(name, parsed.strip, content, onLoad);
189 | }, function (err) {
190 | if (onLoad.error) {
191 | onLoad.error(err);
192 | }
193 | });
194 | } else {
195 | //Need to fetch the resource across domains. Assume
196 | //the resource has been optimized into a JS module. Fetch
197 | //by the module name + extension, but do not include the
198 | //!strip part to avoid file system issues.
199 | req([nonStripName], function (content) {
200 | text.finishLoad(parsed.moduleName + '.' + parsed.ext,
201 | parsed.strip, content, onLoad);
202 | });
203 | }
204 | },
205 |
206 | write: function (pluginName, moduleName, write, config) {
207 | if (buildMap.hasOwnProperty(moduleName)) {
208 | var content = text.jsEscape(buildMap[moduleName]);
209 | write.asModule(pluginName + "!" + moduleName,
210 | "define(function () { return '" +
211 | content +
212 | "';});\n");
213 | }
214 | },
215 |
216 | writeFile: function (pluginName, moduleName, req, write, config) {
217 | var parsed = text.parseName(moduleName),
218 | extPart = parsed.ext ? '.' + parsed.ext : '',
219 | nonStripName = parsed.moduleName + extPart,
220 | //Use a '.js' file name so that it indicates it is a
221 | //script that can be loaded across domains.
222 | fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
223 |
224 | //Leverage own load() method to load plugin value, but only
225 | //write out values that do not have the strip argument,
226 | //to avoid any potential issues with ! in file names.
227 | text.load(nonStripName, req, function (value) {
228 | //Use own write() method to construct full module value.
229 | //But need to create shell that translates writeFile's
230 | //write() to the right interface.
231 | var textWrite = function (contents) {
232 | return write(fileName, contents);
233 | };
234 | textWrite.asModule = function (moduleName, contents) {
235 | return write.asModule(moduleName, fileName, contents);
236 | };
237 |
238 | text.write(pluginName, nonStripName, textWrite, config);
239 | }, config);
240 | }
241 | };
242 |
243 | if (masterConfig.env === 'node' || (!masterConfig.env &&
244 | typeof process !== "undefined" &&
245 | process.versions &&
246 | !!process.versions.node &&
247 | !process.versions['node-webkit'] &&
248 | !process.versions['atom-shell'])) {
249 | //Using special require.nodeRequire, something added by r.js.
250 | fs = require.nodeRequire('fs');
251 |
252 | text.get = function (url, callback, errback) {
253 | try {
254 | var file = fs.readFileSync(url, 'utf8');
255 | //Remove BOM (Byte Mark Order) from utf8 files if it is there.
256 | if (file[0] === '\uFEFF') {
257 | file = file.substring(1);
258 | }
259 | callback(file);
260 | } catch (e) {
261 | if (errback) {
262 | errback(e);
263 | }
264 | }
265 | };
266 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
267 | text.createXhr())) {
268 | text.get = function (url, callback, errback, headers) {
269 | var xhr = text.createXhr(), header;
270 | xhr.open('GET', url, true);
271 |
272 | //Allow plugins direct access to xhr headers
273 | if (headers) {
274 | for (header in headers) {
275 | if (headers.hasOwnProperty(header)) {
276 | xhr.setRequestHeader(header.toLowerCase(), headers[header]);
277 | }
278 | }
279 | }
280 |
281 | //Allow overrides specified in config
282 | if (masterConfig.onXhr) {
283 | masterConfig.onXhr(xhr, url);
284 | }
285 |
286 | xhr.onreadystatechange = function (evt) {
287 | var status, err;
288 | //Do not explicitly handle errors, those should be
289 | //visible via console output in the browser.
290 | if (xhr.readyState === 4) {
291 | status = xhr.status || 0;
292 | if (status > 399 && status < 600) {
293 | //An http 4xx or 5xx error. Signal an error.
294 | err = new Error(url + ' HTTP status: ' + status);
295 | err.xhr = xhr;
296 | if (errback) {
297 | errback(err);
298 | }
299 | } else {
300 | callback(xhr.responseText);
301 | }
302 |
303 | if (masterConfig.onXhrComplete) {
304 | masterConfig.onXhrComplete(xhr, url);
305 | }
306 | }
307 | };
308 | xhr.send(null);
309 | };
310 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
311 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
312 | //Why Java, why is this so awkward?
313 | text.get = function (url, callback) {
314 | var stringBuffer, line,
315 | encoding = "utf-8",
316 | file = new java.io.File(url),
317 | lineSeparator = java.lang.System.getProperty("line.separator"),
318 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
319 | content = '';
320 | try {
321 | stringBuffer = new java.lang.StringBuffer();
322 | line = input.readLine();
323 |
324 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
325 | // http://www.unicode.org/faq/utf_bom.html
326 |
327 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
328 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
329 | if (line && line.length() && line.charAt(0) === 0xfeff) {
330 | // Eat the BOM, since we've already found the encoding on this file,
331 | // and we plan to concatenating this buffer with others; the BOM should
332 | // only appear at the top of a file.
333 | line = line.substring(1);
334 | }
335 |
336 | if (line !== null) {
337 | stringBuffer.append(line);
338 | }
339 |
340 | while ((line = input.readLine()) !== null) {
341 | stringBuffer.append(lineSeparator);
342 | stringBuffer.append(line);
343 | }
344 | //Make sure we return a JavaScript string and not a Java string.
345 | content = String(stringBuffer.toString()); //String
346 | } finally {
347 | input.close();
348 | }
349 | callback(content);
350 | };
351 | } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
352 | typeof Components !== 'undefined' && Components.classes &&
353 | Components.interfaces)) {
354 | //Avert your gaze!
355 | Cc = Components.classes;
356 | Ci = Components.interfaces;
357 | Components.utils['import']('resource://gre/modules/FileUtils.jsm');
358 | xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
359 |
360 | text.get = function (url, callback) {
361 | var inStream, convertStream, fileObj,
362 | readData = {};
363 |
364 | if (xpcIsWindows) {
365 | url = url.replace(/\//g, '\\');
366 | }
367 |
368 | fileObj = new FileUtils.File(url);
369 |
370 | //XPCOM, you so crazy
371 | try {
372 | inStream = Cc['@mozilla.org/network/file-input-stream;1']
373 | .createInstance(Ci.nsIFileInputStream);
374 | inStream.init(fileObj, 1, 0, false);
375 |
376 | convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
377 | .createInstance(Ci.nsIConverterInputStream);
378 | convertStream.init(inStream, "utf-8", inStream.available(),
379 | Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
380 |
381 | convertStream.readString(inStream.available(), readData);
382 | convertStream.close();
383 | inStream.close();
384 | callback(readData.value);
385 | } catch (e) {
386 | throw new Error((fileObj && fileObj.path || '') + ': ' + e);
387 | }
388 | };
389 | }
390 | return text;
391 | });
392 |
--------------------------------------------------------------------------------
/bimsurfer/src/AnnotationRenderer.js:
--------------------------------------------------------------------------------
1 | define(["./Utils"], function (Utils) {
2 | "use strict";
3 |
4 | var AnnotationRenderer = function(args) {
5 |
6 | var v = args.viewer;
7 | var m = args.model;
8 | var a = args.assets;
9 |
10 | var aabb, modelExtent, use_xeogl;
11 |
12 | if (use_xeogl = !!v.scene.worldBoundary) {
13 | aabb = v.scene.worldBoundary.aabb;
14 | var max = aabb.subarray(3);
15 | var min = aabb.subarray(0, 3);
16 | var diag = xeogl.math.subVec3(max, min, xeogl.math.vec3());
17 | modelExtent = xeogl.math.lenVec3(diag);
18 | } else {
19 | aabb = new THREE.Box3();
20 | aabb.setFromObject(v.scene);
21 | modelExtent = aabb.getSize(new THREE.Vector3()).length();
22 | }
23 |
24 | var visit = function(n, fn) {
25 | fn(n);
26 | (n.children || []).forEach(function(c) {visit(c, fn);});
27 | };
28 |
29 | var traverse = function(types, p, n) {
30 | var li = [];
31 | var _ = function(p, n) {
32 | var t = n["xlink:href"];
33 | if (t) t = types[t.substr(1)];
34 | if (t) li.push([p, t]);
35 | (n.children || []).forEach(function(c) {_(n, c);});
36 | }
37 | _(p, n);
38 | return li;
39 | };
40 |
41 | var makeMatrix = use_xeogl
42 | ? function(s, lengthUnit) {
43 | if (arguments.length == 0) {
44 | return xeogl.math.mat4();
45 | }
46 |
47 | var m = xeogl.math.mat4(s.split(" "));
48 | m[12] *= lengthUnit;
49 | m[13] *= lengthUnit;
50 | m[14] *= lengthUnit;
51 | return m;
52 | }
53 | : function(s, lengthUnit) {
54 | var m = new THREE.Matrix4;
55 | if (arguments.length == 0) return m;
56 |
57 | var f = new Float32Array(s.split(" "));
58 | f[12] *= lengthUnit;
59 | f[13] *= lengthUnit;
60 | f[14] *= lengthUnit;
61 | m.set.apply(m, f);
62 | m.transpose();
63 | return m;
64 | };
65 |
66 | var invert = use_xeogl ? xeogl.math.inverseMat4 : function(m, n) { return m.getInverse(n); }
67 |
68 | var scaleMatrix = use_xeogl ? xeogl.math.scalingMat4s : function(v) {
69 | return makeMatrix().scale(new THREE.Vector3(v,v,v));
70 | };
71 |
72 | var makeVec4 = use_xeogl ? function() { return xeogl.math.vec4(arguments); } : function(x,y,z,w) { return new THREE.Vector4(x,y,z,w); };
73 |
74 | var m4v4 = use_xeogl ? xeogl.math.mulMat4v4 : function(m, v) { return v.clone().applyMatrix4(m); };
75 |
76 | var m4m4_inplace = use_xeogl ? function(m, n) { xeogl.math.mulMat4(m, n, m); } : function(m, n) { m.multiply(n); };;
77 |
78 | var logMatrix = function(n, m) {
79 | if (m.elements) {
80 | m = m.elements;;
81 | }
82 | console.log(n, ...m);
83 | };
84 |
85 | this.render = function() {
86 | var typelist = Utils.FindNodeOfType(m, "types")[0].children;
87 | var decomposition = Utils.FindNodeOfType(m, "decomposition")[0];
88 | var units = Utils.FindNodeOfType(m, "units")[0].children;
89 |
90 | var types = {};
91 | typelist.forEach(function(t) {
92 | types[t.guid] = t;
93 | });
94 |
95 | var lengthUnit = 1.;
96 | units.forEach(function(u) {
97 | if (u.UnitType === "LENGTHUNIT") {
98 | lengthUnit = parseFloat(u.SI_equivalent);
99 | }
100 | });
101 |
102 | var elementsWithType = traverse(types, null, decomposition);
103 |
104 | elementsWithType.forEach(function(l) {
105 | var elem = l[0];
106 | var type = l[1];
107 |
108 | var m1 = makeMatrix(elem.ObjectPlacement, lengthUnit);
109 | var m1i = makeMatrix();
110 | invert(m1, m1i);
111 |
112 | v.createModel("Annotations");
113 |
114 | var s = scaleMatrix(Math.sqrt(modelExtent) / 100.);
115 | var z0 = makeVec4(0,0,0,1);
116 | var z1 = makeVec4(0,0,1,1);
117 |
118 | visit(type, function(c) {
119 | if (!c.ObjectPlacement) {
120 | return;
121 | }
122 |
123 | var m2 = makeMatrix(c.ObjectPlacement, lengthUnit);
124 | m4m4_inplace(m2, s);
125 | m4m4_inplace(m2, m1);
126 |
127 | var symbol = null;
128 |
129 | if (c.type === "IfcDistributionPort") {
130 | if (c.FlowDirection == "SINK") {
131 | symbol = a.ArrowOut();
132 | } else if (c.FlowDirection == "SOURCE") {
133 | symbol = a.ArrowIn();
134 | } else if (c.FlowDirection == "SOURCEANDSINK") {
135 | symbol = a.ArrowInOut();
136 | }
137 | }
138 |
139 | if (symbol === null) {
140 | symbol = a.Sphere();
141 | }
142 |
143 | symbol.register(v);
144 | symbol.render(v, c.guid, c.type, m2);
145 | });
146 | });
147 | }
148 |
149 | };
150 |
151 | return AnnotationRenderer;
152 |
153 | });
--------------------------------------------------------------------------------
/bimsurfer/src/BimServerModel.js:
--------------------------------------------------------------------------------
1 | define(["../lib/text"], function(text) {
2 | "use strict";
3 |
4 | /*
5 | var getElementName = function(elem) {
6 | var name = null;
7 | // TODO: This is synchronous right?
8 | ["LongName", "Name"].forEach(function(attr) {
9 | if (name === null && elem["get" + attr] && elem["get" + attr]()) {
10 | name = elem["get" + attr]();
11 | }
12 | });
13 | return name;
14 | };
15 | */
16 |
17 | function BimServerModel(apiModel) {
18 |
19 | this.api = apiModel.bimServerApi;
20 | this.apiModel = apiModel;
21 | this.tree = null;
22 | this.treePromise = null;
23 |
24 | this.getTree = function(args) {
25 |
26 | /*
27 | // TODO: This is rather tricky. Never know when the list of Projects is exhausted.
28 | // Luckily a valid IFC contains one and only one. Let's assume there is just one.
29 | var projectEncountered = false;
30 |
31 | this.model.getAllOfType("IfcProject", false, function(project) {
32 | if (projectEncountered) {
33 | throw new Error("More than a single project encountered, bleh!");
34 | }
35 | console.log('project', project);
36 | });
37 |
38 | */
39 |
40 | var self = this;
41 |
42 | return self.treePromise || (self.treePromise = new Promise(function(resolve, reject) {
43 |
44 | if (self.tree) {
45 | resolve(self.tree);
46 | }
47 |
48 | var query =
49 | {
50 | defines:{
51 | Representation:{
52 | type: "IfcProduct",
53 | field: "Representation"
54 | },
55 | ContainsElementsDefine: {
56 | type: "IfcSpatialStructureElement",
57 | field:"ContainsElements",
58 | include:{
59 | type:"IfcRelContainedInSpatialStructure",
60 | field:"RelatedElements",
61 | includes:[
62 | "IsDecomposedByDefine",
63 | "ContainsElementsDefine",
64 | "Representation"
65 | ]
66 | }
67 | },
68 | IsDecomposedByDefine: {
69 | type: "IfcObjectDefinition",
70 | field:"IsDecomposedBy",
71 | include: {
72 | type:"IfcRelDecomposes",
73 | field:"RelatedObjects",
74 | includes:[
75 | "IsDecomposedByDefine",
76 | "ContainsElementsDefine",
77 | "Representation"
78 | ]
79 | }
80 | },
81 | },
82 | queries:[{
83 | type:"IfcProject",
84 | includes:[
85 | "IsDecomposedByDefine",
86 | "ContainsElementsDefine"
87 | ]
88 | }, {
89 | type:"IfcRepresentation",
90 | includeAllSubtypes: true
91 | },{
92 | type:"IfcProductRepresentation"
93 | },{
94 | type:"IfcPresentationLayerWithStyle"
95 | },{
96 | type:"IfcProduct",
97 | includeAllSubtypes: true
98 | }, {
99 | type:"IfcProductDefinitionShape"
100 | }, {
101 | type:"IfcPresentationLayerAssignment"
102 | }, {
103 | type:"IfcRelAssociatesClassification",
104 | includes:[{
105 | type:"IfcRelAssociatesClassification",
106 | field:"RelatedObjects"
107 | }, {
108 | type:"IfcRelAssociatesClassification",
109 | field:"RelatingClassification"
110 | }]
111 | }, {
112 | type:"IfcSIUnit"
113 | }, {
114 | type:"IfcPresentationLayerAssignment"
115 | }]
116 | };
117 |
118 | // Perform the download
119 | // apiModel.query(query, function(o) {}).done(function(){
120 |
121 | // A list of entities that define parent-child relationships
122 | var entities = {
123 | 'IfcRelDecomposes': 1,
124 | 'IfcRelAggregates': 1,
125 | 'IfcRelContainedInSpatialStructure': 1,
126 | 'IfcRelFillsElement': 1,
127 | 'IfcRelVoidsElement': 1
128 | }
129 |
130 | // Create a mapping from id->instance
131 | var instance_by_id = {};
132 | var objects = [];
133 |
134 | for (var e in apiModel.objects) {
135 | // The root node in a dojo store should have its parent
136 | // set to null, not just something that evaluates to false
137 | var o = apiModel.objects[e].object;
138 | o.parent = null;
139 | instance_by_id[o._i] = o;
140 | objects.push(o);
141 | };
142 |
143 | // Filter all instances based on relationship entities
144 | var relationships = objects.filter(function (o) {
145 | return entities[o._t];
146 | });
147 |
148 | // Construct a tuple of {parent, child} ids
149 | var parents = relationships.map(function (o) {
150 | var ks = Object.keys(o);
151 | var related = ks.filter(function (k) {
152 | return k.indexOf('Related') !== -1;
153 | });
154 | var relating = ks.filter(function (k) {
155 | return k.indexOf('Relating') !== -1;
156 | });
157 | return [o[relating[0]], o[related[0]]];
158 | });
159 |
160 | var is_array = function (o) {
161 | return Object.prototype.toString.call(o) === '[object Array]';
162 | }
163 |
164 | var data = [];
165 | var visited = {};
166 | parents.forEach(function (a) {
167 | // Relationships in IFC can be one to one/many
168 | var ps = is_array(a[0]) ? a[0] : [a[0]];
169 | var cs = is_array(a[1]) ? a[1] : [a[1]];
170 | for (var i = 0; i < ps.length; ++i) {
171 | for (var j = 0; j < cs.length; ++j) {
172 | // Lookup the instance ids in the mapping
173 | var p = instance_by_id[ps[i]._i];
174 | var c = instance_by_id[cs[j]._i];
175 |
176 | // parent, id, hasChildren are significant attributes in a dojo store
177 | c.parent = p.id = p._i;
178 | c.id = c._i;
179 | p.hasChildren = true;
180 |
181 | // Make sure to only add instances once
182 | if (!visited[c.id]) {
183 | data.push(c);
184 | }
185 | if (!visited[p.id]) {
186 | data.push(p);
187 | }
188 | visited[p.id] = visited[c.id] = true;
189 | }
190 | }
191 | });
192 |
193 | var make_element = function (o) {
194 | return {name: o.Name, id: o.id, guid: o.GlobalId, parent: o.parent, gid: (o._rgeometry == null ? null : o._rgeometry._i)};
195 | };
196 |
197 | var fold = (function() {
198 | var root = null;
199 | return function(li) {
200 | var by_oid = {};
201 | li.forEach(function(elem) {
202 | by_oid[elem.id] = elem;
203 | });
204 | li.forEach(function(elem) {
205 | if (elem.parent === null) {
206 | root = elem;
207 | } else {
208 | var p = by_oid[elem.parent];
209 | (p.children || (p.children = [])).push(elem);
210 | }
211 | });
212 | return root;
213 | }})();
214 |
215 | resolve(self.tree = fold(data.map(make_element)));
216 | // });
217 | }));
218 | };
219 |
220 | }
221 |
222 | return BimServerModel;
223 |
224 | });
--------------------------------------------------------------------------------
/bimsurfer/src/BimServerModelLoader.js:
--------------------------------------------------------------------------------
1 | define(["./BimServerModel", "./PreloadQuery", "./BimServerGeometryLoader", "./BimSurfer"], function(BimServerModel, PreloadQuery, BimServerGeometryLoader, BimSufer) {
2 | "use strict";
3 |
4 | function BimServerModelLoader(bimServerClient, bimSurfer) {
5 |
6 | var o = this;
7 |
8 | this.loadFullModel = function(apiModel){
9 | return new Promise(function(resolve, reject) {
10 | var model = new BimServerModel(apiModel);
11 |
12 | apiModel.query(PreloadQuery, function () {}).done(function(){
13 | var oids = [];
14 | apiModel.getAllOfType("IfcProduct", true, function(object){
15 | oids.push(object.oid);
16 | });
17 | o.loadOids(model, oids);
18 | resolve(model);
19 | });
20 | });
21 | };
22 |
23 | this.loadObjects = function(apiModel, objects){
24 | return new Promise(function(resolve, reject) {
25 | var model = new BimServerModel(apiModel);
26 |
27 | var oids = [];
28 | objects.forEach(function(object){
29 | oids.push(object.oid);
30 | });
31 | o.loadOids(model, oids);
32 | resolve(model);
33 | });
34 | };
35 |
36 | this.loadOids = function(model, oids){
37 | var oidToGuid = {};
38 | var guidToOid = {};
39 |
40 | var oidGid = {};
41 |
42 | oids.forEach(function(oid){
43 | model.apiModel.get(oid, function(object){
44 | if (object.object._rgeometry != null) {
45 | var gid = object.object._rgeometry._i;
46 | var guid = object.object.GlobalId;
47 | oidToGuid[oid] = guid;
48 | guidToOid[guid] = oid;
49 | oidGid[gid] = oid;
50 | }
51 | });
52 | });
53 |
54 | bimSurfer._idMapping.toGuid.push(oidToGuid);
55 | bimSurfer._idMapping.toId.push(guidToOid);
56 |
57 | var viewer = bimSurfer.viewer;
58 | viewer.taskStarted();
59 |
60 | viewer.createModel(model.apiModel.roid);
61 |
62 | var loader = new BimServerGeometryLoader(model.api, viewer, model, model.apiModel.roid, o.globalTransformationMatrix);
63 |
64 | loader.addProgressListener(function (progress, nrObjectsRead, totalNrObjects) {
65 | if (progress == "start") {
66 | console.log("Started loading geometries");
67 | // self.fire("loading-started");
68 | } else if (progress == "done") {
69 | console.log("Finished loading geometries (" + totalNrObjects + " objects received)");
70 | // self.fire("loading-finished");
71 | viewer.taskFinished();
72 | }
73 | });
74 |
75 | loader.setLoadOids(oidGid);
76 |
77 | // viewer.clear(); // For now, until we support multiple models through the API
78 |
79 | viewer.on("tick", function () { // TODO: Fire "tick" event from xeoViewer
80 | loader.process();
81 | });
82 |
83 | loader.start();
84 | }
85 |
86 | this.setGlobalTransformationMatrix = function(globalTransformationMatrix) {
87 | o.globalTransformationMatrix = globalTransformationMatrix;
88 | }
89 | }
90 |
91 | BimServerModelLoader.prototype = Object.create(BimServerModelLoader.prototype);
92 |
93 | return BimServerModelLoader;
94 | });
--------------------------------------------------------------------------------
/bimsurfer/src/BimSurfer.js:
--------------------------------------------------------------------------------
1 | window.BIMSERVER_VERSION = "1.5";
2 |
3 | define([/*"./Notifier", "./BimServerModel", "./PreloadQuery", "./BimServerGeometryLoader", "./xeoViewer/xeoViewer",*/ "./EventHandler", "./svgViewer/svgViewer", "./threeViewer/threeViewer"], function (/*Notifier, Model, PreloadQuery, GeometryLoader, XeoViewer,*/ EventHandler, SvgViewer, ThreeViewer, _BimServerApi) {
4 | "use strict";
5 |
6 | // Backwards compatibility
7 | /*
8 | var BimServerApi;
9 | if (_BimServerApi) {
10 | BimServerApi = _BimServerApi;
11 | } else {
12 | BimServerApi = window.BimServerClient;
13 | }
14 | */
15 |
16 | function BimSurfer(cfg) {
17 |
18 | var self = this;
19 |
20 | EventHandler.call(this);
21 |
22 | cfg = cfg || {};
23 |
24 | self.engine = (cfg.engine || "threejs").toLowerCase();
25 | var engine = {
26 | svg: SvgViewer,
27 | xeogl: window.XeoViewer,
28 | threejs: ThreeViewer
29 | }[self.engine];
30 |
31 | var viewer = this.viewer = new engine(Object.assign(cfg, {app: this}));
32 |
33 | /**
34 | * Fired whenever this BIMSurfer's camera changes.
35 | * @event camera-changed
36 | */
37 | viewer.on("camera-changed", function() {
38 | self.fire("camera-changed", arguments);
39 | });
40 |
41 | /**
42 | * Fired whenever this BIMSurfer's selection changes.
43 | * @event selection-changed
44 | */
45 | viewer.on("selection-changed", function() {
46 | self.fire("selection-changed", arguments);
47 | });
48 |
49 | // This are arrays as multiple models might be loaded or unloaded.
50 | this._idMapping = {
51 | 'toGuid': [],
52 | 'toId' : []
53 | };
54 |
55 | /**
56 | * Loads a model into this BIMSurfer.
57 | * @param params
58 | */
59 | this.load = function (params) {
60 |
61 | if (params.test) {
62 | viewer.loadRandom(params);
63 | return null;
64 |
65 | } else if (params.bimserver) {
66 | return this._loadFromServer(params);
67 |
68 | } else if (params.api) {
69 | return this._loadFromAPI(params);
70 |
71 | } else if (params.src && ((window.XeoViewer && self.viewer instanceof XeoViewer) || self.viewer instanceof ThreeViewer)) {
72 | return this._loadFrom_glTF(params);
73 | } else if (params.src && self.viewer instanceof SvgViewer) {
74 | return this._loadFrom_SVG(params);
75 | }
76 | };
77 |
78 | this._loadFromServer = function (params) {
79 |
80 | var notifier = new Notifier();
81 | var bimServerApi = new BimServerApi(params.bimserver, notifier);
82 |
83 | params.api = bimServerApi; // TODO: Make copy of params
84 |
85 | return self._initApi(params)
86 | .then(self._loginToServer)
87 | .then(self._getRevisionFromServer)
88 | .then(self._loadFromAPI);
89 | };
90 |
91 | this._initApi = function(params) {
92 | return new Promise(function(resolve, reject) {
93 | params.api.init(function () {
94 | resolve(params);
95 | });
96 | });
97 | };
98 |
99 | this._loginToServer = function (params) {
100 | return new Promise(function(resolve, reject) {
101 | if (params.token) {
102 | params.api.setToken(params.token, function() {
103 | resolve(params)
104 | }, reject);
105 | } else {
106 | params.api.login(params.username, params.password, function() {
107 | resolve(params)
108 | }, reject);
109 | }
110 | });
111 | };
112 |
113 | this.shouldClearSelection = function(evt) {
114 | return !evt.shiftKey;
115 | };
116 |
117 | this._getRevisionFromServer = function (params) {
118 | return new Promise(function(resolve, reject) {
119 | if (params.roid) {
120 | resolve(params);
121 | } else {
122 | params.api.call("ServiceInterface", "getAllRelatedProjects", {poid: params.poid}, function(data) {
123 | var resolved = false;
124 |
125 | data.forEach(function(projectData) {
126 | if (projectData.oid == params.poid) {
127 | params.roid = projectData.lastRevisionId;
128 | params.schema = projectData.schema;
129 | if (!self.models) {
130 | self.models = [];
131 | }
132 | self.models.push(projectData);
133 | resolved = true;
134 | resolve(params);
135 | }
136 | });
137 |
138 | if (!resolved) {
139 | reject();
140 | }
141 | }, reject);
142 | }
143 | });
144 | };
145 |
146 | this._loadFrom_SVG = function (params) {
147 | if (params.src) {
148 | return viewer.load(params.src + ".svg");
149 | }
150 | };
151 |
152 | this._loadFrom_glTF = function (params) {
153 | if (params.src) {
154 | var maxActiveProcessesEncountered = 0;
155 | var oldProgress = 0;
156 | return new Promise(function (resolve, reject) {
157 | var m = viewer.loadglTF(params.src);
158 |
159 | if (window.XeoViewer && self.viewer instanceof XeoViewer) {
160 | m.on("loaded", function() {
161 | viewer.scene.canvas.spinner.on('processes', function(n) {
162 | if (n === 0) {
163 | viewer.viewFit({});
164 | resolve(m);
165 | }
166 | if (n > maxActiveProcessesEncountered) {
167 | maxActiveProcessesEncountered = n;
168 | }
169 | var progress = parseInt((maxActiveProcessesEncountered - n) * 100 / maxActiveProcessesEncountered);
170 | if (oldProgress != progress) {
171 | self.fire("progress", [progress]);
172 | }
173 | oldProgress = progress;
174 | });
175 | });
176 | } else {
177 | viewer.on("loaded", function() {
178 | resolve(viewer);
179 |
180 | if (cfg.initiallyInvisible) {
181 | viewer.setVisibility({ids: viewer.getObjectIds(), visible:false});
182 | }
183 | });
184 | }
185 | });
186 | }
187 | };
188 |
189 | this._loadFromAPI = function (params) {
190 |
191 | return new Promise(function (resolve, reject) {
192 |
193 | params.api.getModel(params.poid, params.roid, params.schema, false,
194 | function (model) {
195 |
196 | // TODO: Preload not necessary combined with the bruteforce tree
197 | var fired = false;
198 |
199 | model.query(PreloadQuery,
200 | function () {
201 | if (!fired) {
202 | fired = true;
203 | var vmodel = new Model(params.api, model);
204 |
205 | self._loadModel(vmodel);
206 |
207 | resolve(vmodel);
208 | }
209 | });
210 | });
211 | });
212 | };
213 |
214 | this._loadModel = function (model) {
215 |
216 | model.getTree().then(function (tree) {
217 |
218 | var oids = [];
219 | var oidToGuid = {};
220 | var guidToOid = {};
221 |
222 | var visit = function (n) {
223 | if (BIMSERVER_VERSION == "1.4") {
224 | oids.push(n.id);
225 | } else {
226 | oids[n.gid] = n.id;
227 | }
228 | oidToGuid[n.id] = n.guid;
229 | guidToOid[n.guid] = n.id;
230 |
231 | for (var i = 0; i < (n.children || []).length; ++i) {
232 | visit(n.children[i]);
233 | }
234 | };
235 |
236 | visit(tree);
237 |
238 | self._idMapping.toGuid.push(oidToGuid);
239 | self._idMapping.toId.push(guidToOid);
240 |
241 | var models = {};
242 |
243 | // TODO: Ugh. Undecorate some of the newly created classes
244 | models[model.model.roid] = model.model;
245 |
246 | // Notify viewer that things are loading, so viewer can
247 | // reduce rendering speed and show a spinner.
248 | viewer.taskStarted();
249 |
250 | viewer.createModel(model.model.roid);
251 |
252 | var loader = new GeometryLoader(model.api, models, viewer);
253 |
254 | loader.addProgressListener(function (progress, nrObjectsRead, totalNrObjects) {
255 | if (progress == "start") {
256 | console.log("Started loading geometries");
257 | self.fire("loading-started");
258 | } else if (progress == "done") {
259 | console.log("Finished loading geometries (" + totalNrObjects + " objects received)");
260 | self.fire("loading-finished");
261 | viewer.taskFinished();
262 | }
263 | });
264 |
265 | loader.setLoadOids([model.model.roid], oids);
266 |
267 | // viewer.clear(); // For now, until we support multiple models through the API
268 |
269 | viewer.on("tick", function () { // TODO: Fire "tick" event from XeoViewer
270 | loader.process();
271 | });
272 |
273 | loader.start();
274 | });
275 | };
276 |
277 | // Helper function to traverse over the mappings for individually loaded models
278 | var _traverseMappings = function(mappings) {
279 | return function(k) {
280 | for (var i = 0; i < mappings.length; ++i) {
281 | var v = mappings[i][k];
282 | if (v) return v;
283 | }
284 | return null;
285 | }
286 | };
287 |
288 | /**
289 | * Returns a list of object ids (oid) for the list of guids (GlobalId)
290 | *
291 | * @param guids List of globally unique identifiers from the IFC model
292 | */
293 | this.toId = function(guids) {
294 | return guids.map(_traverseMappings(self._idMapping.toId));
295 | };
296 |
297 | /**
298 | * Returns a list of guids (GlobalId) for the list of object ids (oid)
299 | *
300 | * @param ids List of internal object ids from the BIMserver / glTF file
301 | */
302 | this.toGuid = function(ids) {
303 | return ids.map(_traverseMappings(self._idMapping.toGuid));
304 | };
305 |
306 | /**
307 | * Shows/hides objects specified by id or entity type, e.g IfcWall.
308 | *
309 | * When recursive is set to true, hides children (aggregates, spatial structures etc) or
310 | * subtypes (IfcWallStandardCase ⊆ IfcWall).
311 | *
312 | * @param params
313 | */
314 | this.setVisibility = function (params) {
315 | viewer.setVisibility(params);
316 | };
317 |
318 | /**
319 | * Selects/deselects objects specified by id.
320 | **
321 | * @param params
322 | */
323 | this.setSelection = function (params) {
324 | if (cfg.initiallyInvisible) {
325 | return viewer.setVisibility(Object.assign({}, params, {visible: params.selected}));
326 | } else {
327 | return viewer.setSelection(params);
328 | }
329 | };
330 |
331 | /**
332 | * Gets a list of selected elements.
333 | */
334 | this.getSelection = function () {
335 | return viewer.getSelection();
336 | };
337 |
338 | /**
339 | * Sets color of objects specified by ids or entity type, e.g IfcWall.
340 | **
341 | * @param params
342 | */
343 | this.setColor = function (params) {
344 | viewer.setColor(params);
345 | };
346 |
347 | /**
348 | * Sets opacity of objects specified by ids or entity type, e.g IfcWall.
349 | **
350 | * @param params
351 | */
352 | this.setOpacity = function (params) {
353 | viewer.setOpacity(params);
354 | };
355 |
356 | /**
357 | * Fits the elements into view.
358 | *
359 | * Fits the entire model into view if ids is an empty array, null or undefined.
360 | * Animate allows to specify a transition period in milliseconds in which the view is altered.
361 | *
362 | * @param params
363 | */
364 | this.viewFit = function (params) {
365 | viewer.viewFit(params);
366 | };
367 |
368 | /**
369 | *
370 | */
371 | this.getCamera = function () {
372 | return viewer.getCamera();
373 | };
374 |
375 | /**
376 | *
377 | * @param params
378 | */
379 | this.setCamera = function (params) {
380 | viewer.setCamera(params);
381 | };
382 |
383 | /**
384 | * Redefines light sources.
385 | *
386 | * @param params Array of lights {type: "ambient"|"dir"|"point", params: {[...]}}
387 | * See http://xeoengine.org/docs/classes/Lights.html for possible params for each light type
388 | */
389 | this.setLights = function (params) {
390 | viewer.setLights(params);
391 | };
392 |
393 |
394 | /**
395 | * Returns light sources.
396 | *
397 | * @returns Array of lights {type: "ambient"|"dir"|"point", params: {[...]}}
398 | */
399 | this.getLights = function () {
400 | return viewer.getLights;
401 | };
402 |
403 | /**
404 | *
405 | * @param params
406 | */
407 | this.reset = function (params) {
408 | viewer.reset(params);
409 | }
410 |
411 | /**
412 | * Returns a list of loaded IFC entity types in the model.
413 | *
414 | * @method getTypes
415 | * @returns {Array} List of loaded IFC entity types, with visibility flag
416 | */
417 | this.getTypes = function() {
418 | return viewer.getTypes();
419 | };
420 |
421 | /**
422 | * Sets the default behaviour of mouse and touch drag input
423 | *
424 | * @method setDefaultDragAction
425 | * @param {String} action ("pan" | "orbit")
426 | */
427 | this.setDefaultDragAction = function (action) {
428 | viewer.setDefaultDragAction(action);
429 | };
430 |
431 | /**
432 | * Returns the world boundary of an object
433 | *
434 | * @method getWorldBoundary
435 | * @param {String} objectId id of object
436 | * @param {Object} result Existing boundary object
437 | * @returns {Object} World boundary of object, containing {obb, aabb, center, sphere} properties. See xeogl.Boundary3D
438 | */
439 | this.getWorldBoundary = function(objectId, result) {
440 | return viewer.getWorldBoundary(objectId, result);
441 | };
442 |
443 | /**
444 | * Destroys the BIMSurfer
445 | */
446 | this.destroy = function() {
447 | viewer.destroy();
448 | };
449 |
450 | this.resize = function() {
451 | viewer.resize();
452 | };
453 | }
454 |
455 | BimSurfer.prototype = Object.create(EventHandler.prototype);
456 |
457 | return BimSurfer;
458 |
459 | });
460 |
--------------------------------------------------------------------------------
/bimsurfer/src/DataInputStreamReader.js:
--------------------------------------------------------------------------------
1 | define(["../lib/StringView"], function(StringView) {
2 | "use strict";
3 |
4 | function DataInputStreamReader(arrayBuffer) {
5 |
6 | this.arrayBuffer = arrayBuffer;
7 | this.dataView = new DataView(this.arrayBuffer);
8 | this.pos = 0;
9 |
10 | this.readUTF8 = function() {
11 | var length = this.dataView.getInt16(this.pos);
12 | this.pos += 2;
13 | var view = this.arrayBuffer.slice(this.pos, this.pos + length);
14 | var result = new StringView(view).toString();
15 | this.pos += length;
16 | return result;
17 | };
18 |
19 | this.remaining = function() {
20 | return this.arrayBuffer.byteLength - this.pos;
21 | };
22 |
23 | this.align4 = function() {
24 | // Skips to the next alignment of 4 (source should have done the same!)
25 | var skip = 4 - (this.pos % 4);
26 | if(skip > 0 && skip != 4) {
27 | // console.log("Skip", skip);
28 | this.pos += skip;
29 | }
30 | };
31 |
32 | this.align8 = function() {
33 | // Skips to the next alignment of 4 (source should have done the same!)
34 | var skip = 8 - (this.pos % 8);
35 | if(skip > 0 && skip != 8) {
36 | // console.log("Skip", skip);
37 | this.pos += skip;
38 | }
39 | };
40 |
41 | this.readDoubleArray = function(length) {
42 | var result = new Float64Array(this.arrayBuffer, this.pos, length);
43 | this.pos += length * 8;
44 | return result;
45 | },
46 |
47 | this.readFloat = function() {
48 | var value = this.dataView.getFloat32(this.pos, true);
49 | this.pos += 4;
50 | return value;
51 | };
52 |
53 | this.readInt = function() {
54 | var value = this.dataView.getInt32(this.pos, true);
55 | this.pos += 4;
56 | return value;
57 | };
58 |
59 | this.readByte = function() {
60 | var value = this.dataView.getInt8(this.pos);
61 | this.pos += 1;
62 | return value;
63 | };
64 |
65 | this.readLong = function() {
66 | var value = this.dataView.getUint32(this.pos, true) + 0x100000000 * this.dataView.getUint32(this.pos + 4, true);
67 | this.pos += 8;
68 | return value;
69 | };
70 |
71 | this.readFloatArray2 = function(length) {
72 | var results = [];
73 | for (var i=0; i= -1) {
18 | h.splice(i, 1);
19 | found = true;
20 | }
21 | }
22 | if (!found) {
23 | throw new Error("Handler not found");
24 | }
25 | };
26 |
27 | EventHandler.prototype.fire = function(evt, args) {
28 | var h = this.handlers[evt];
29 | if (!h) {
30 | return;
31 | }
32 | for (var i = 0; i < h.length; ++i) {
33 | h[i].apply(this, args);
34 | }
35 | };
36 |
37 | return EventHandler;
38 |
39 | });
--------------------------------------------------------------------------------
/bimsurfer/src/MetaDataRenderer.js:
--------------------------------------------------------------------------------
1 | define(["./EventHandler", "./Request", "./Utils"], function(EventHandler, Request, Utils) {
2 | "use strict";
3 |
4 | function Row(args) {
5 | var self = this;
6 | var num_names = 0;
7 | var num_values = 0;
8 |
9 | this.setName = function(name) {
10 | if (num_names++ > 0) {
11 | args.name.appendChild(document.createTextNode(" "));
12 | }
13 | args.name.appendChild(document.createTextNode(name));
14 | }
15 |
16 | this.setValue = function(value) {
17 | if (num_values++ > 0) {
18 | args.value.appendChild(document.createTextNode(", "));
19 | }
20 | args.value.appendChild(document.createTextNode(value));
21 | }
22 | }
23 |
24 | function identity(x) { return x; }
25 |
26 | function Section(args) {
27 | var self = this;
28 |
29 | var div = self.div = document.createElement("div");
30 | var nameh = document.createElement("h3");
31 | var table = document.createElement("table");
32 |
33 | var tr = document.createElement("tr");
34 | table.appendChild(tr);
35 | var nameth = document.createElement("th");
36 | var valueth = document.createElement("th");
37 | nameth.appendChild(document.createTextNode("Name"));
38 | valueth.appendChild(document.createTextNode("Value"));
39 | tr.appendChild(nameth);
40 | tr.appendChild(valueth);
41 |
42 | div.appendChild(nameh);
43 | div.appendChild(table);
44 |
45 | args.domNode.appendChild(div);
46 |
47 | this.setName = function(name) {
48 | nameh.appendChild(document.createTextNode(name));
49 | }
50 |
51 | this.addRow = function() {
52 | var tr = document.createElement("tr");
53 | table.appendChild(tr);
54 | var nametd = document.createElement("td");
55 | var valuetd = document.createElement("td");
56 | tr.appendChild(nametd);
57 | tr.appendChild(valuetd);
58 | return new Row({name:nametd, value:valuetd});
59 | }
60 | };
61 |
62 | function loadModelFromSource(src) {
63 | return Request.Make({url: src}).then(function(xml) {
64 | var json = Utils.XmlToJson(xml, {'Name': 'name', 'id': 'guid'});
65 | return loadModelFromJson(json);
66 | });
67 | }
68 |
69 |
70 | function loadModelFromJson(json) {
71 | return new Promise(function (resolve, reject) {
72 | var psets = Utils.FindNodeOfType(json, "properties")[0];
73 | var project = Utils.FindNodeOfType(json, "decomposition")[0].children[0];
74 | var types = Utils.FindNodeOfType(json, "types")[0];
75 |
76 | var objects = {};
77 | var typeObjects = {};
78 | var properties = {};
79 | (psets.children || []).forEach(function(pset) {
80 | properties[pset.guid] = pset;
81 | });
82 |
83 | var visitObject = function(parent, node) {
84 | var props = [];
85 | var o = (parent && parent.ObjectPlacement) ? objects : typeObjects;
86 |
87 | if (node["xlink:href"]) {
88 | if (!o[parent.guid]) {
89 | var p = Utils.Clone(parent);
90 | p.GlobalId = p.guid;
91 | o[p.guid] = p;
92 | o[p.guid].properties = []
93 | }
94 | var g = node["xlink:href"].substr(1);
95 | var p = properties[g];
96 | if (p) {
97 | o[parent.guid].properties.push(p);
98 | } else if (typeObjects[g]) {
99 | // If not a pset, it is a type, so concatenate type props
100 | o[parent.guid].properties = o[parent.guid].properties.concat(typeObjects[g].properties);
101 | }
102 | }
103 | (node.children || []).forEach(function(n) {
104 | visitObject(node, n);
105 | });
106 | };
107 |
108 | visitObject(null, types);
109 | var numTypes = Object.keys(objects).length;
110 | visitObject(null, project);
111 | var numTotal = Object.keys(objects).length;
112 | var productCount = numTotal - numTypes;
113 |
114 | resolve({model: {objects: objects, productCount: productCount, source: 'XML'}});
115 | });
116 | }
117 |
118 | function MetaDataRenderer(args) {
119 |
120 | var self = this;
121 | EventHandler.call(this);
122 |
123 | var models = {};
124 | var domNode = document.getElementById(args['domNode']);
125 |
126 | this.addModel = function(args) {
127 |
128 | return new Promise(function (resolve, reject) {
129 | if (args.model) {
130 | models[args.id] = args.model;
131 | resolve(args.model);
132 | } else {
133 | var fn = args.src ? loadModelFromSource : loadModelFromJson;
134 | fn(args.src ? args.src : args.json).then(function(m) {
135 | models[args.id] = m;
136 | resolve(m);
137 | });
138 | }
139 | });
140 |
141 | };
142 |
143 | var renderAttributes = function(elem) {
144 | var s = new Section({domNode:domNode});
145 | s.setName(elem.type || elem.getType());
146 | ["GlobalId", "Name", "OverallWidth", "OverallHeight", "Tag", "PredefinedType", "FlowDirection"].forEach(function(k) {
147 | var v = elem[k];
148 | if (typeof(v) === 'undefined') {
149 | var fn = elem["get"+k];
150 | if (fn) {
151 | v = fn.apply(elem);
152 | }
153 | }
154 | if (typeof(v) !== 'undefined') {
155 | var r = s.addRow();
156 | r.setName(k);
157 | r.setValue(v);
158 | }
159 | });
160 | return s;
161 | };
162 |
163 | var renderPSet = function(pset) {
164 | var s = new Section({domNode:domNode});
165 | if (pset.name && pset.children) {
166 | s.setName(pset.name);
167 | pset.children.forEach(function(v) {
168 | var r = s.addRow();
169 | r.setName(v.name);
170 | r.setValue(v.NominalValue);
171 | });
172 | } else {
173 | pset.getName(function(name) {
174 | s.setName(name);
175 | });
176 | pset.getHasProperties(function(prop) {
177 | var r = s.addRow();
178 | prop.getName(function(name) {
179 | r.setName(name);
180 | });
181 | prop.getNominalValue(function(value) {
182 | r.setValue(value._v);
183 | });
184 | });
185 | }
186 | return s;
187 | };
188 |
189 | var queryPSet = function(resolve, pset, psetName, propName) {
190 | if (pset.name && pset.children) {
191 | // based on XML
192 | if (pset.name !== psetName) {
193 | return false;
194 | }
195 | return pset.children.map(function(v) {
196 | if (v.name !== propName) {
197 | return false;
198 | }
199 | resolve(v.NominalValue);
200 | return true;
201 | }).some(identity);
202 | } else {
203 | // based on BIMserver
204 | pset.getName(function(name) {
205 | if (name !== psetName) {
206 | return;
207 | }
208 | pset.getHasProperties(function(prop) {
209 | prop.getName(function(name) {
210 | if (name !== propName) {
211 | return;
212 | }
213 | prop.getNominalValue(function(value) {
214 | resolve(value._v);
215 | });
216 | });
217 |
218 | });
219 | });
220 |
221 | }
222 | return s;
223 | };
224 |
225 | this.query = function(oid, psetName, propName) {
226 | return new Promise(function(resolve, reject) {
227 | oid = oid.split(':');
228 | if (oid.length == 1) {
229 | oid = [Object.keys(models)[0], oid];
230 | }
231 | var model = models[oid[0]].model || models[oid[0]].apiModel;
232 | var ob = model.objects[oid[1]];
233 |
234 | if (model.source === 'XML') {
235 | let containedInPset = ob.properties.map(function(pset) {
236 | return queryPSet(resolve, pset, psetName, propName);
237 | });
238 | console.log(containedInPset);
239 | if (!containedInPset.some(identity)) {
240 | reject();
241 | }
242 | } else {
243 | ob.getIsDefinedBy(function(isDefinedBy){
244 | if (isDefinedBy.getType() == "IfcRelDefinesByProperties") {
245 | isDefinedBy.getRelatingPropertyDefinition(function(pset){
246 | if (pset.getType() == "IfcPropertySet") {
247 | queryPSet(resolve, pset, propsetName, propName);
248 | }
249 | });
250 | }
251 | });
252 | }
253 | });
254 | };
255 |
256 | let selectedParent = null;
257 | let selectedElement = null;
258 | let selectedElements = [];
259 |
260 | this.setSelectedParent = function(oid) {
261 | selectedParent = oid;
262 | this.processSelection();
263 | }
264 |
265 | this.setSelected = function(oid) {
266 | // @todo this should take into account the clear flag, to detect
267 | // multiple selection events from the 3d viewer, but it's not
268 | // available in this handler.
269 |
270 | if (oid.length === 1) {
271 | selectedElement = oid[0];
272 | } else {
273 | selectedElement = null;
274 | }
275 | this.processSelection();
276 | }
277 |
278 | this.processSelection = function () {
279 |
280 | selectedElements = [selectedParent, selectedElement].filter(x => x);
281 |
282 | if (self.highlightMode) {
283 |
284 | (self.selectedSections || []).forEach(function(s) {
285 | s.div.className = "";
286 | });
287 |
288 | if (selectedElement) {
289 | self.sections[selectedElement].forEach(function(s) {
290 | s.div.className = "selected";
291 | });
292 |
293 | self.selectedSections = self.sections[selectedElement];
294 | } else {
295 | self.selectedSections = [];
296 | }
297 |
298 | } else {
299 |
300 | domNode.innerHTML = "";
301 |
302 | selectedElements.forEach(oid => {
303 |
304 | oid = oid.split(':');
305 | if (oid.length == 1) {
306 | oid = [Object.keys(models)[0], oid];
307 | }
308 |
309 | var idModel;
310 |
311 | for(var i =0; i {
6 | let parts = id.split('-');
7 | parts = parts.slice(parts.lastIndexOf('product'));
8 |
9 | const id2 = parts.filter(s => s !== 'product' && s !== 'body' && s !== 'storey').join('-');
10 | return Utils.CompressGuid(id2);
11 | }
12 |
13 | function createElem(tag, attrs, NS) {
14 | const ob = NS ? document.createElementNS(NS, tag) : document.createElement(tag);
15 | for(let [k,v] of Object.entries(attrs || {})) {
16 | if (NS) {
17 | ob.setAttribute(k, v);
18 | } else {
19 | ob.setAttributeNS(null, k, v);
20 | }
21 | }
22 | return ob;
23 | }
24 |
25 | function children(node) {
26 | return Array.from(node.childNodes).filter(n => n.nodeType === 1);
27 | }
28 |
29 | function testOverlap(text, path) {
30 | var Mt = text.getScreenCTM().inverse();
31 |
32 | var P1 = text.ownerSVGElement.createSVGPoint();
33 | var P2 = text.ownerSVGElement.createSVGPoint();
34 | var bb = text.getBoundingClientRect();
35 |
36 | if (typeof(bb) === 'undefined') {
37 | return false;
38 | }
39 |
40 | P1.x = bb.x;
41 | P1.y = bb.y;
42 | P2.x = bb.x + bb.width;
43 | P2.y = bb.y + bb.height;
44 |
45 | P1 = P1.matrixTransform(Mt);
46 | P2 = P2.matrixTransform(Mt);
47 |
48 | var bb_width = P2.x - P1.x;
49 | var bb_height = P2.y - P1.y;
50 |
51 | var len = path.getTotalLength();
52 | var u = 0.;
53 | var step = (bb_width < bb_height ? bb_width : bb_height) / 2.;
54 |
55 | while (u < len) {
56 | // Sample some points over the bath when contained in the AABB rectangle
57 | // for the text we know text and path intersect and text should be hidden
58 | var p = path.getPointAtLength(u);
59 | if (p.x >= P1.x && p.y >= P1.y && p.x <= P2.x && p.y <= P2.y) {
60 | return true;
61 | }
62 | u += step;
63 | }
64 |
65 | return false;
66 | }
67 |
68 | function SvgViewer(cfg) {
69 | let self = this;
70 |
71 | EventHandler.call(this);
72 |
73 | self.selected = new Set();
74 | self.lineMapping = new Map();
75 |
76 | self.svg = null;
77 |
78 | var xmlns = "http://www.w3.org/2000/svg";
79 |
80 | var elem = document.getElementById(cfg.domNode);
81 |
82 | self.load = function(src) {
83 |
84 | self.select = createElem("select");
85 | /*self.obj = createElem("object", {
86 | type : "image/svg+xml",
87 | data : src
88 | });*/
89 | self.obj = createElem("div");
90 | elem.appendChild(self.obj);
91 | var d = createElem("div", {
92 | class: "selectcontainer"
93 | });
94 |
95 | elem.appendChild(d);
96 | d.appendChild(self.select);
97 |
98 | self.obj.style.width = elem.offsetWidth + 'px';
99 | self.obj.style.height = (elem.offsetHeight - d.offsetHeight) + 'px';
100 |
101 | return fetch(src)
102 | .then(response => {
103 | if (!response.ok) {
104 | throw new Error("HTTP status " + response.status);
105 | } else {
106 | return response.text();
107 | }
108 | }).then(text => {
109 | self.obj.innerHTML = text;
110 | var svg = self.obj.getElementsByTagName('svg')[0];
111 | svg.style.width = svg.style.height = '100%';
112 | }).catch(exc => {
113 | self.error = true;
114 | }).then(() => {
115 | self._onload();
116 | });
117 | }
118 |
119 | self.resize = function() {}
120 |
121 | self._updateState = function(n, parentState) {
122 | if (parentState || self.selected.has(n)) {
123 | if (!self.lineMapping.has(n)) {
124 | for (let c of children(n)) {
125 | self._updateState(c, true);
126 | }
127 | if (n.tagName == 'path') {
128 | const line = n.cloneNode(false);
129 | line.style.cssText = "fill: none; stroke: lime; stroke-width: 3px";
130 | self.lineMapping.set(n, line);
131 | // children[0] is the pan-zoom viewport
132 | self.rootGroup.appendChild(line);
133 | }
134 | }
135 | } else {
136 | if (self.lineMapping.has(n)) {
137 | // the groups do not get a line
138 | self.rootGroup.removeChild(self.lineMapping.get(n));
139 | self.lineMapping.delete(n);
140 | }
141 | for (let c of children(n)) {
142 | self._updateState(c, false);
143 | }
144 | }
145 | }
146 |
147 | self.setSelection = function(params) {
148 | const updateState = (n) => {self._updateState(n)};
149 | if (params.clear) {
150 | const previous = Array.from(self.selected);
151 | self.selected.clear();
152 | previous.forEach(updateState);
153 | }
154 | const fn = params.selected ? self.selected.add : self.selected.delete;
155 |
156 | const convertGuidOrIdentity = (s) => (self.guidToIdMap.get(s) || [s]);
157 |
158 | var nodes = null;
159 | if (params.nodes) {
160 | nodes = params.nodes;
161 | } else if (params.ids) {
162 | nodes = params.ids.flatMap(convertGuidOrIdentity).map(self.svg.getElementById.bind(self.svg)).filter((s) => (s !== null));
163 | }
164 |
165 | nodes.forEach(fn.bind(self.selected));
166 | nodes.forEach(updateState);
167 |
168 | const getStoreyIdx = (n) => {
169 | while (n) {
170 | let idx = self.storeys.indexOf(n);
171 | if (idx !== -1) {
172 | return idx;
173 | }
174 | n = n.parentElement;
175 | }
176 | }
177 |
178 | const sids = new Set(nodes.map(getStoreyIdx));
179 | if (params.clear && sids.size && !sids.has(self.select.selectedIndex)) {
180 | self.select.selectedIndex = Array.from(sids).sort()[0];
181 | self.toggleStorey(self.select.selectedIndex, false);
182 | }
183 |
184 | if (params.nodes) {
185 | const ids = params.nodes.map((n) => (n.getAttribute("id"))).map(convertId);
186 | self.fire("selection-changed", [{objects: ids}]);
187 | }
188 | }
189 |
190 | self.toggleStorey = function(i, clearSelection) {
191 | if (clearSelection !== false) {
192 | self.setSelection({clear: true, nodes:[], selected: true});
193 | }
194 | self.storeys.forEach((s, j)=>{
195 | s.style.visibility = (i == j) ? 'visible' : 'hidden';
196 | });
197 | self.updateTextVisibility();
198 | };
199 |
200 | self.reset = function(args) {
201 | if (args.colors) {
202 | for (let p of Array.from(self.svg.getElementsByTagName("path"))) {
203 | /*
204 | if (p.parentNode.className.baseVal == 'IfcDoor') {
205 | // @todo this is mainly for the door arcs, but we need to annotate closed areas and line annotations better in the IfcConvert binary
206 | p.style.fill = 'none';
207 | } else {
208 | p.style.fill = '#444';
209 | if (p.parentNode.className.baseVal == 'IfcSpace') {
210 | p.style.fillOpacity = '.2';
211 | }
212 | }
213 | p.style.stroke = '#222';
214 | */
215 | }
216 | }
217 | }
218 |
219 | self.setColor = function(args) {
220 | const convertGuidOrIdentity = (s) => (self.guidToIdMap.get(s) || s);
221 | var nodes = args.ids.map(convertGuidOrIdentity).map((s)=>`product-product-${s}-body`).map(self.svg.getElementById.bind(self.svg)).filter((s) => (s !== null));
222 | var color = "#" + args.color.map((f) => (("0" + parseInt(f * 255.).toString(16)).substr(-2))).join("");
223 |
224 | nodes.forEach((n) => {
225 | n.style.fill = n.style.stroke = color;
226 | Array.from(n.getElementsByTagName("path")).forEach((n) => {
227 | n.style.fill = n.style.stroke = color;
228 | });
229 | });
230 | }
231 |
232 | self.destroy = function() {
233 | self.spz.destroy();
234 | while(elem.lastChild) {
235 | elem.removeChild(elem.lastChild);
236 | }
237 | }
238 |
239 | self.updateTextVisibility = function() {
240 | if (!self.textNodes) return;
241 | self.textNodes.forEach(t => {
242 | let n = t;
243 | var storeyVisible;
244 | while (n) {
245 | if (self.storeys.indexOf(n) !== -1) {
246 | storeyVisible = n.style.visibility !== 'hidden';
247 | break;
248 | }
249 | n = n.parentElement;
250 | }
251 | // Dimensions and storey elevations are always visible
252 | let cls = t.parentElement.className.baseVal
253 | var visible = storeyVisible && (cls.includes("Dimension") || cls.includes("IfcBuildingStorey") || !Array.from(t.parentElement.querySelectorAll('path')).some((path) => {
254 | return testOverlap(t, path)
255 | }));
256 | t.style.visibility = visible ? 'visible' : 'hidden';
257 | });
258 | }
259 |
260 | self._onload = function() {
261 | if (self.error) {
262 | return;
263 | }
264 |
265 | var svgDoc = self.obj.contentDocument || self.obj.getElementsByTagName('svg')[0];
266 | self.svg = self.obj.contentDocument ? children(svgDoc)[0] : svgDoc;
267 | self.reset({colors:true});
268 | self.storeys = children(self.svg).filter(n => n.tagName == 'g');
269 |
270 | if (self.storeys.length === 0) {
271 | return;
272 | }
273 |
274 | self.guidToIdMap = new Map();
275 | const traverse = (e) => {
276 | const id = e.getAttribute('id');
277 | if (id !== null) {
278 | const guid = convertId(id);
279 | if (!self.guidToIdMap.has(guid)) {
280 | self.guidToIdMap.set(guid, []);
281 | }
282 | self.guidToIdMap.get(guid).push(id);
283 | }
284 | for (const c of children(e)) {
285 | traverse(c);
286 | }
287 | };
288 | traverse(self.svg);
289 | self.toggleStorey(0);
290 | self.select.onchange = function(evt) {
291 | self.toggleStorey(evt.target.selectedIndex);
292 | }
293 | self.storeys.forEach((s, i) => {
294 | const opt = document.createElement('option');
295 |
296 | var N;
297 | if (s.hasAttribute('data-name')) {
298 | N = s.getAttribute('data-name')
299 | } else if (s.hasAttribute("ifc:name")) {
300 | N = s.getAttribute("ifc:name")
301 | } else {
302 | N = `storey ${i}`;
303 | }
304 | opt.setAttribute("value", N);
305 | opt.appendChild(document.createTextNode(N));
306 | self.select.appendChild(opt);
307 | });
308 | self.textNodes = Array.from(self.svg.querySelectorAll('text'));
309 | const updateZoom = (scale) => {
310 | self.svg.style.fontSize = 10 / self.rootGroup.transform.baseVal.getItem(0).matrix.a + "pt";
311 | self.updateTextVisibility();
312 | };
313 | self.spz = svgPanZoom(self.obj.contentDocument ? self.obj : self.obj.getElementsByTagName('svg')[0], {
314 | zoomEnabled: true,
315 | preventMouseEventsDefault: true,
316 | controlIconsEnabled: false,
317 | onZoom: updateZoom
318 | });
319 | self.rootGroup = children(self.svg).filter(n => n.tagName == 'g')[0];
320 | updateZoom();
321 | svgDoc.onclick = function(evt) {
322 | let n = evt.target;
323 | const nodes = []
324 | if (n.tagName !== 'svg') {
325 | while (n.tagName !== 'g' && n.parentNode) {
326 | n = n.parentNode;
327 | }
328 | nodes.push(n);
329 | }
330 | self.setSelection({
331 | selected: true,
332 | clear: cfg.app.shouldClearSelection(evt),
333 | nodes: nodes
334 | });
335 | }
336 | };
337 | }
338 |
339 | SvgViewer.prototype = Object.create(EventHandler.prototype);
340 |
341 | return SvgViewer;
342 | });
343 |
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/controls/cursors/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AECgeeks/BIMsurfer2/926d1fcc08a373c879a2416110bc3107be1d654c/bimsurfer/src/xeoViewer/controls/cursors/rotate.png
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/effects/highlightEffect.js:
--------------------------------------------------------------------------------
1 | define(["../../../lib/xeogl"], function () {
2 |
3 | "use strict";
4 |
5 | xeogl.HighlightEffect = xeogl.Component.extend({
6 |
7 | type: "xeogl.HighlightEffect",
8 |
9 | _init: function (cfg) {
10 |
11 | this._modes = this.create({
12 | type: "xeogl.Modes",
13 | transparent: true,
14 | collidable: false // Has no collision boundary of its own
15 | });
16 |
17 | this._stage = this.create({
18 | type: "xeogl.Stage",
19 | priority: 2
20 | });
21 |
22 | this._depthBuf = this.create({
23 | type: "xeogl.DepthBuf",
24 | active: false
25 | });
26 |
27 | this._emissiveColor = (cfg.color || [0.2, 0.9, 0.2]).slice(0,3);
28 | this._opacity = cfg.color && cfg.color.length > 3 ? cfg.color[3] : 0.25;
29 |
30 | this._helpers = {};
31 | this._freeHelpers = [];
32 | },
33 |
34 | add: function (bimObject) {
35 | var entities = bimObject.entities;
36 | if (entities) {
37 | var entity;
38 | for (var i = 0, len = entities.length; i < len; i++) {
39 | entity = entities[i];
40 | this._createHelper(entity);
41 | }
42 | } else {
43 | this._createHelper(bimObject);
44 | }
45 | },
46 |
47 | _createHelper: function (entity) {
48 | var helper = this._freeHelpers.pop();
49 | if (!helper) {
50 | helper = this.create({
51 | type: "xeogl.Entity",
52 | geometry: entity.geometry,
53 | transform: entity.transform,
54 | material: this.create({
55 | type: "xeogl.PhongMaterial",
56 | emissive: this._emissiveColor,
57 | specular: [0, 0, 0],
58 | diffuse: [0, 0, 0],
59 | ambient: [0, 0, 0],
60 | opacity: this._opacity
61 | }),
62 | modes: this._modes,
63 | stage: this._stage,
64 | depthBuf: this._depthBuf,
65 | visibility: this.create({
66 | type: "xeogl.Visibility",
67 | visible: true
68 | }),
69 | meta: {
70 | entityId: entity.id
71 | }
72 | });
73 | } else {
74 | helper.geometry = entity.geometry;
75 | helper.material.diffuse = entity.material.diffuse;
76 | helper.material.ambient = entity.material.ambient;
77 | helper.transform = entity.transform;
78 | helper.visibility.visible = true;
79 | helper.meta.entityId = entity.id;
80 | }
81 | this._helpers[entity.id] = helper;
82 | },
83 |
84 | clear: function () {
85 | var helper;
86 | for (var id in this._helpers) {
87 | if (this._helpers.hasOwnProperty(id)) {
88 | helper = this._helpers[id];
89 | this._destroyHelper(helper);
90 | }
91 | }
92 | },
93 |
94 | remove: function (bimObject) {
95 | var entities = bimObject.entities;
96 | var entity;
97 | for (var i = 0, len = entities.length; i < len; i++) {
98 | entity = entities[i];
99 | var helper = this._helpers[entity.id];
100 | if (helper) {
101 | this._destroyHelper(helper);
102 | }
103 | }
104 | },
105 |
106 | _destroyHelper: function (helper) {
107 | helper.visibility.visible = false;
108 | this._freeHelpers.push(helper);
109 | delete this._helpers[helper.meta.entityId];
110 | }
111 |
112 | });
113 | });
114 |
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/entities/bimModel.js:
--------------------------------------------------------------------------------
1 | define(["../../../lib/xeogl"], function () {
2 |
3 | "use strict";
4 |
5 | /**
6 | Custom xeoEngine component that represents a BIMSurfer model within a xeoEngine scene.
7 |
8 | @class BIMModel
9 | @module XEO
10 | @constructor
11 | @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
12 | @param [cfg] {*} Configs
13 | @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
14 | @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this xeogl.BIMModel.
15 | @extends Component
16 | */
17 | xeogl.BIMModel = xeogl.Component.extend({
18 |
19 | // JavaScript class name for this xeogl.BIMModel.
20 | type: "xeogl.BIMModel",
21 |
22 | // Constructor
23 | _init: function (cfg) {
24 | this.collection = this.create({
25 | type: "xeogl.Collection"// http://xeoengine.org/docs/classes/Collection.html
26 | });
27 | },
28 |
29 | // Adds a BIMObject to this BIMModel
30 | addObject: function (object) {
31 | this.collection.add(object);
32 | }
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/entities/bimObject.js:
--------------------------------------------------------------------------------
1 | define(["../../../lib/xeogl"], function () {
2 |
3 | "use strict";
4 |
5 | /**
6 | Custom xeoEngine component that represents a BIMSurfer object within a xeoEngine scene.
7 |
8 | An object consists of a set of xeogl.Entity's that share components between them.
9 |
10 | The components control functionality for the Entity's as a group, while the Entity's
11 | themselves each have their own xeogl.Geometry.
12 |
13 | This way, we are able to have BIM objects containing multiple geometries.
14 |
15 | @class BIMObject
16 | @module XEO
17 | @constructor
18 | @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
19 | @param [cfg] {*} Configs
20 | @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
21 | @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this xeogl.BIMObject.
22 | @extends Component
23 | */
24 | xeogl.BIMObject = xeogl.Component.extend({
25 |
26 | /**
27 | JavaScript class name for this xeogl.BIMObject.
28 |
29 | @property type
30 | @type String
31 | @final
32 | */
33 | type: "xeogl.BIMObject",
34 |
35 | // Constructor
36 |
37 | _init: function (cfg) {
38 |
39 | // Model this object belongs to, will be null when no model
40 | this.model = cfg.model; // xeogl.BIMModel
41 |
42 | // Modelling transform component
43 | this.transform = this.create({
44 | type: "xeogl.Transform",// http://xeoengine.org/docs/classes/Transform.html
45 | matrix: cfg.matrix
46 | });
47 |
48 | // Visibility control component.
49 | this.visibility = this.create({
50 | type: "xeogl.Visibility", // http://xeoengine.org/docs/classes/Visibility.html
51 | visible: true
52 | });
53 |
54 | // Material component
55 | this.material = this.create({
56 | type: "xeogl.PhongMaterial", // http://xeoengine.org/docs/classes/Material.html
57 | emissive: [0, 0, 0],
58 | diffuse: [Math.random(), Math.random(), Math.random()], // Random color until we set for type
59 | opacity: 1.0
60 | });
61 |
62 | // Rendering modes component
63 | this.modes = this.create({
64 | type: "xeogl.Modes", // http://xeoengine.org/docs/classes/Modes.html
65 | transparent: false,
66 | backfaces: true
67 | });
68 |
69 | // When highlighting, causes this object to render after non-highlighted objects
70 | this.stage = this.create({
71 | type: "xeogl.Stage",
72 | priority: 0
73 | });
74 |
75 | // When highlighting, we use this component to disable depth-testing so that this object
76 | // appears to "float" over non-highlighted objects
77 | this.depthBuf = this.create({
78 | type: "xeogl.DepthBuf",
79 | active: true
80 | });
81 |
82 | // Create a xeogl.Entity for each xeogl.Geometry
83 | // Each xeogl.Entity shares the components defined above
84 |
85 | // TODO: If all geometries are of same primitive, then we can combine them
86 |
87 | this.entities = [];
88 | var entity;
89 |
90 | for (var i = 0, len = cfg.geometryIds.length; i < len; i++) {
91 |
92 | entity = this.create({ // http://xeoengine.org/docs/classes/Entity.html
93 | type: "xeogl.Entity",
94 | meta: {
95 | objectId: this.id
96 | },
97 | geometry: "geometry." + cfg.geometryIds[i],
98 | transform: this.transform,
99 | visibility: this.visibility,
100 | material: this.material,
101 | modes: this.modes,
102 | stage: this.stage,
103 | depthBuf: this.depthBuf
104 | });
105 |
106 | this.entities.push(entity);
107 | }
108 | },
109 |
110 | add: function(geometryId){
111 | var entity = this.create({ // http://xeoengine.org/docs/classes/Entity.html
112 | type: "xeogl.Entity",
113 | meta: {
114 | objectId: this.id
115 | },
116 | geometry: "geometry." + geometryId,
117 | transform: this.transform,
118 | visibility: this.visibility,
119 | material: this.material,
120 | modes: this.modes,
121 | stage: this.stage,
122 | depthBuf: this.depthBuf
123 | });
124 |
125 | this.entities.push(entity);
126 | },
127 |
128 | // Define read-only properties of xeogl.BIMObject
129 |
130 | _props: {
131 |
132 | // World-space bounding volume
133 | worldBoundary: {
134 | get: function () {
135 | return this.entities[0].worldBoundary
136 | }
137 | },
138 |
139 | // View-space bounding volume
140 | viewBoundary: {
141 | get: function () {
142 | return this.entities[0].viewBoundary
143 | }
144 | },
145 |
146 | // Canvas-space bounding volume
147 | canvasBoundary: {
148 | get: function () {
149 | return this.entities[0].viewBoundary
150 | }
151 | },
152 |
153 | // Whether or not this object is highlighted
154 | highlighted: {
155 | set: function (highlight) {
156 | this.depthBuf.active = !highlight;
157 | this.stage.priority = highlight ? 2 : 0;
158 | this.material.emissive = highlight ? [0.5, 0.5, 0.5] : [0, 0, 0];
159 | }
160 | }
161 | }
162 | });
163 | });
164 |
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/helpers/bimBoundaryHelper.js:
--------------------------------------------------------------------------------
1 | define(["../../../lib/xeogl"], function () {
2 |
3 | "use strict";
4 |
5 | /**
6 | Custom xeoEngine component that shows a wireframe box representing an non axis-aligned 3D boundary.
7 | */
8 | var BIMBoundaryHelperEntity = xeogl.Entity.extend({
9 |
10 | type: "xeogl.BIMBoundaryHelperEntity",
11 |
12 | _init: function (cfg) {
13 |
14 | var obbGeometry = this.create({
15 | type: "xeogl.OBBGeometry"
16 | });
17 |
18 | var phongMaterial = this.create({
19 | type: "xeogl.PhongMaterial",
20 | diffuse: cfg.color || [0.5, 0.5, 0.5],
21 | ambient: [0, 0, 0],
22 | specular: [0, 0, 0],
23 | lineWidth: 2,
24 | });
25 |
26 | var nonCollidableMode = this.create({
27 | type: "xeogl.Modes",
28 | collidable: false // This helper has no collision boundary of its own
29 | });
30 |
31 | // Causes this entity to render after all other entities
32 | var stagePriorityThree = this.create({
33 | type: "xeogl.Stage",
34 | priority: 3
35 | });
36 |
37 | var visible = this.create({
38 | type: "xeogl.Visibility",
39 | visible: true
40 | });
41 |
42 | // Disables depth-testing so that this entity
43 | // appears to "float" over other entities
44 | var depthBufInactive = this.create({
45 | type: "xeogl.DepthBuf",
46 | active: false
47 | });
48 |
49 | this._super(xeogl._apply({
50 | geometry: obbGeometry,
51 | material: phongMaterial,
52 | modes: nonCollidableMode,
53 | stage: stagePriorityThree,
54 | depthBuf: depthBufInactive,
55 | visibility: visible
56 | }, cfg));
57 |
58 | }
59 | });
60 |
61 | xeogl.BIMBoundaryHelper = function(scene, viewer, cfg) {
62 |
63 | var self = this;
64 |
65 | self._entities = {};
66 | self._pool = [];
67 |
68 | self.setSelected = function(ids) {
69 |
70 | var oldIds = Object.keys(self._entities);
71 |
72 | ids.forEach(function(id) {
73 | if (!self._entities[id]) {
74 | var h;
75 | if (self._pool.length > 0) {
76 | h = self._entities[id] = self._pool.shift();
77 | h.visibility.visible = true;
78 | } else {
79 | h = self._entities[id] = new BIMBoundaryHelperEntity(scene, cfg);
80 | }
81 | h.geometry.boundary = viewer.getObject(id).worldBoundary;
82 | }
83 |
84 | var oldIdx = oldIds.indexOf(id);
85 | if (oldIdx !== -1) {
86 | oldIds.splice(oldIdx, 1);
87 | }
88 | });
89 |
90 | oldIds.forEach(function(id) {
91 | var h = self._entities[id];
92 | h.visibility.visible = false;
93 | self._pool.push(h);
94 | delete self._entities[id];
95 | });
96 | };
97 |
98 | };
99 |
100 | });
101 |
--------------------------------------------------------------------------------
/bimsurfer/src/xeoViewer/utils/collection.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Components for managing collections of components.
3 | *
4 | * @module xeogl
5 | * @submodule collections
6 | */;/**
7 | A **Collection** is a set of {{#crossLink "Component"}}Components{{/crossLink}}.
8 |
9 |
10 |
A {{#crossLink "Component"}}Component{{/crossLink}} can be included in more than one Collection.
11 |
{{#crossLink "Component"}}Components{{/crossLink}} can be added to a Collection by instance, ID or type.
12 |
A Collection supports iteration over its {{#crossLink "Component"}}Components{{/crossLink}}.
13 |
A {{#crossLink "Model"}}Model{{/crossLink}} stores the {{#crossLink "Component"}}Components{{/crossLink}} it has loaded in a Collection.
14 |
A {{#crossLink "CollectionBoundary"}}CollectionBoundary{{/crossLink}} provides a World-space {{#crossLink "Boundary3D"}}{{/crossLink}} that encloses a Collection.
15 |
16 |
17 |
18 |
19 | ## Examples
20 |
21 |
22 |
[Adding Entities to a Collection](../../examples/#collections_Collection_creating_add)
23 |
[Adding components types to a Collection](../../examples/#collections_Collection_creating_type)
24 |
[Iterating a Collection](../../examples/#boundaries_Collection_iterating)
25 |
[Visualizing World-space boundary of a Collection](../../examples/#boundaries_CollectionBoundary)
26 |
[Visualizing World-space boundaries of a hierarchy of Collections](../../examples/#boundaries_CollectionBoundary_hierarchy)
27 |
28 |
29 | ## Creating Collections
30 |
31 | Our first Collection contains a {{#crossLink "PhongMaterial"}}{{/crossLink}}, added by ID, plus a {{#crossLink "BoxGeometry"}}{{/crossLink}} and
32 | an {{#crossLink "Entity"}}{{/crossLink}}, both added by instance.
33 |
34 | ````javascript
35 | var material = new xeogl.PhongMaterial({
36 | id: "myMaterial",
37 | diffuse: [0.5, 0.5, 0.0]
38 | });
39 |
40 | var geometry = new xeogl.BoxGeometry();
41 |
42 | var entity = new xeogl.Entity({
43 | id: "myEntity",
44 | material: material,
45 | geometry: geometry
46 | });
47 |
48 | var collection1 = new xeogl.Collection({ // Initialize with the three components
49 | components: [
50 | "myMaterial",
51 | geometry,
52 | myEntity
53 | ]
54 | });
55 | ````
56 | Our second Collection includes the {{#crossLink "BoxGeometry"}}{{/crossLink}}, added by instance,
57 | and the {{#crossLink "Entity"}}{{/crossLink}}, added by type. If there were more than
58 | one {{#crossLink "Entity"}}{{/crossLink}} in the scene, then that type would ensure
59 | that all the {{#crossLink "Entity"}}Entities{{/crossLink}} were in the Collection.
60 |
61 | ````javascript
62 | var collection2 = new xeogl.Collection();
63 |
64 | collection2.add([ // Add two components
65 | geometry,
66 | "xeogl.Entity",
67 | ]);
68 | ````
69 |
70 | ## Accessing Components
71 |
72 | Iterate over the components in a Collection using the convenience iterator:
73 |
74 | ````javascript
75 | collection1.iterate(function(component) {
76 | if (component.isType("xeogl.Entity")) {
77 | this.log("Found the Entity: " + component.id);
78 | }
79 | //..
80 | });
81 | ````
82 |
83 | A Collection also registers its components by type:
84 |
85 | ````javascript
86 | var entities = collection1.types["xeogl.Entity"];
87 | var theEntity = entities["myEntity"];
88 | ````
89 |
90 | ## Removing Components
91 |
92 | We can remove components from a Collection by instance, ID or type:
93 |
94 | ````javascript
95 | collection1.remove("myMaterial"); // Remove one component by ID
96 | collection1.remove([geometry, myEntity]); // Remove two components by instance
97 | collection2.remove("xeogl.Geometry"); // Remove all Geometries
98 | ````
99 |
100 | ## Getting the boundary of a Collection
101 |
102 | A {{#crossLink "CollectionBoundary"}}{{/crossLink}} provides a {{#crossLink "Boundary3D"}}{{/crossLink}} that
103 | dynamically fits to the collective World-space boundary of all the Components in a Collection.
104 |
105 | ````javascript
106 | var collectionBoundary = new xeogl.CollectionBoundary({
107 | collection: collection1
108 | });
109 |
110 | var worldBoundary = collectionBoundary.worldBoundary;
111 | ````
112 | The {{#crossLink "Boundary3D"}}{{/crossLink}}
113 | will automatically update whenever we add, remove or update any Components that have World-space boundaries. We can subscribe
114 | to updates on it like so:
115 |
116 | ````javascript
117 | worldBoundary.on("updated", function() {
118 | obb = worldBoundary.obb;
119 | aabb = worldBoundary.aabb;
120 | center = worldBoundary.center;
121 | //...
122 | });
123 | ````
124 |
125 | Now, if we now re-insert our {{#crossLink "Entity"}}{{/crossLink}} into to our Collection,
126 | the {{#crossLink "Boundary3D"}}{{/crossLink}} will fire our update handler.
127 |
128 | ````javascript
129 | collection1.add(myEntity);
130 | ````
131 |
132 |
133 | @class Collection
134 | @module xeogl
135 | @submodule collections
136 | @constructor
137 | @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
138 | @param [cfg] {*} Configs
139 | @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
140 | @param [cfg.meta] {String:Component} Optional map of user-defined metadata to attach to this Collection.
141 | @param [cfg.components] {{Array of String|Component}} Array of {{#crossLink "Component"}}{{/crossLink}} IDs or instances.
142 | @extends Component
143 | */
144 | define(["../../../lib/xeogl"], function () {
145 |
146 | "use strict";
147 |
148 | xeogl.Collection = xeogl.Component.extend({
149 |
150 | /**
151 | JavaScript class name for this Component.
152 |
153 | @property type
154 | @type String
155 | @final
156 | */
157 | type: "xeogl.Collection",
158 |
159 | _init: function (cfg) {
160 |
161 | /**
162 | * The {{#crossLink "Components"}}{{/crossLink}} within this Collection, mapped to their IDs.
163 | *
164 | * Fires an {{#crossLink "Collection/updated:event"}}{{/crossLink}} event on change.
165 | *
166 | * @property components
167 | * @type {{String:Component}}
168 | */
169 | this.components = {};
170 |
171 | /**
172 | * The number of {{#crossLink "Components"}}{{/crossLink}} within this Collection.
173 | *
174 | * @property numComponents
175 | * @type Number
176 | */
177 | this.numComponents = 0;
178 |
179 | /**
180 | * A map of maps; for each {{#crossLink "Component"}}{{/crossLink}} type in this Collection,
181 | * a map to IDs to {{#crossLink "Component"}}{{/crossLink}} instances, eg.
182 | *
183 | * ````
184 | * "xeogl.Geometry": {
185 | * "alpha": ,
186 | * "beta":
187 | * },
188 | * "xeogl.Rotate": {
189 | * "charlie": ,
190 | * "delta": ,
191 | * "echo": ,
192 | * },
193 | * //...
194 | * ````
195 | *
196 | * @property types
197 | * @type {String:{String:xeogl.Component}}
198 | */
199 | this.types = {};
200 |
201 | // Subscriptions to "destroyed" events from components
202 | this._destroyedSubs = {};
203 |
204 | if (cfg.components) {
205 | this.add(cfg.components);
206 | }
207 | },
208 |
209 | /**
210 | * Adds one or more {{#crossLink "Component"}}Components{{/crossLink}}s to this Collection.
211 | *
212 | * The {{#crossLink "Component"}}Component(s){{/crossLink}} may be specified by instance, ID or type.
213 | *
214 | * See class comment for usage examples.
215 | *
216 | * The {{#crossLink "Component"}}Components{{/crossLink}} must be in the same {{#crossLink "Scene"}}{{/crossLink}} as this Collection.
217 | *
218 | * Fires an {{#crossLink "Collection/added:event"}}{{/crossLink}} event.
219 | *
220 | * @method add
221 | * @param {Array of Component} components Array of {{#crossLink "Component"}}Components{{/crossLink}} instances.
222 | */
223 | add: function (components) {
224 |
225 | components = xeogl._isArray(components) ? components : [components];
226 |
227 | for (var i = 0, len = components.length; i < len; i++) {
228 | this._add(components[i]);
229 | }
230 | },
231 |
232 | _add: function (c) {
233 |
234 | var componentId;
235 | var component;
236 | var type;
237 | var types;
238 |
239 | if (c.type) {
240 |
241 | // Component instance
242 |
243 | component = c;
244 |
245 | } else if (xeogl._isNumeric(c) || xeogl._isString(c)) {
246 |
247 | if (this.scene.types[c]) {
248 |
249 | // Component type
250 |
251 | type = c;
252 |
253 | types = this.scene.types[type];
254 |
255 | if (!types) {
256 | this.warn("Component type not found: '" + type + "'");
257 | return;
258 | }
259 |
260 | for (componentId in types) {
261 | if (types.hasOwnProperty(componentId)) {
262 | this._add(types[componentId]);
263 | }
264 | }
265 |
266 | return;
267 |
268 | } else {
269 |
270 | // Component ID
271 |
272 | component = this.scene.components[c];
273 |
274 | if (!component) {
275 | this.warn("Component not found: " + xeogl._inQuotes(c));
276 | return;
277 | }
278 | }
279 |
280 | } else {
281 |
282 | return;
283 | }
284 |
285 | if (component.scene !== this.scene) {
286 |
287 | // Component in wrong Scene
288 |
289 | this.warn("Attempted to add component from different xeogl.Scene: " + xeogl._inQuotes(component.id));
290 | return;
291 | }
292 |
293 | // Add component to this map
294 |
295 | if (this.components[component.id]) {
296 |
297 | // Component already in this Collection
298 | return;
299 | }
300 |
301 | this.components[component.id] = component;
302 |
303 | // Register component for its type
304 |
305 | types = this.types[component.type];
306 |
307 | if (!types) {
308 | types = this.types[component.type] = {};
309 | }
310 |
311 | types[component.id] = component;
312 |
313 | this.numComponents++;
314 |
315 | // Remove component when it's destroyed
316 |
317 | var self = this;
318 |
319 | this._destroyedSubs[component.id] = component.on("destroyed",
320 | function () {
321 | self._remove(component);
322 | });
323 |
324 | /**
325 | * Fired whenever an individual {{#crossLink "Component"}}{{/crossLink}} is added to this {{#crossLink "Collection"}}{{/crossLink}}.
326 | * @event added
327 | * @param value {Component} The {{#crossLink "Component"}}{{/crossLink}} that was added.
328 | */
329 | this.fire("added", component);
330 |
331 | if (!this._dirty) {
332 | this._scheduleUpdate();
333 | }
334 | },
335 |
336 | _scheduleUpdate: function () {
337 | if (!this._dirty) {
338 | this._dirty = true;
339 | xeogl.scheduleTask(this._notifyUpdated, this);
340 | }
341 | },
342 |
343 | _notifyUpdated: function () {
344 |
345 | /* Fired on the next {{#crossLink "Scene/tick.animate:event"}}{{/crossLink}} whenever
346 | * {{#crossLink "Component"}}Components{{/crossLink}} were added or removed since the
347 | * last {{#crossLink "Scene/tick.animate:event"}}{{/crossLink}} event, to provide a batched change event
348 | * for subscribers who don't want to react to every individual addition or removal on this Collection.
349 | *
350 | * @event updated
351 | */
352 | this.fire("updated");
353 | this._dirty = false;
354 | },
355 |
356 | /**
357 | * Removes all {{#crossLink "Component"}}Components{{/crossLink}} from this Collection.
358 | *
359 | * Fires an {{#crossLink "Collection/updated:event"}}{{/crossLink}} event.
360 | *
361 | * @method clear
362 | */
363 | clear: function () {
364 |
365 | this.iterate(function (component) {
366 | this._remove(component);
367 | });
368 | },
369 |
370 | /**
371 | * Destroys all {{#crossLink "Component"}}Components{{/crossLink}} in this Collection.
372 | *
373 | * @method destroyAll
374 | */
375 | destroyAll: function () {
376 |
377 | this.iterate(function (component) {
378 | component.destroy();
379 | });
380 | },
381 |
382 | /**
383 | * Removes one or more {{#crossLink "Component"}}Components{{/crossLink}} from this Collection.
384 | *
385 | * The {{#crossLink "Component"}}Component(s){{/crossLink}} may be specified by instance, ID or type.
386 | *
387 | * See class comment for usage examples.
388 | *
389 | * Fires a {{#crossLink "Collection/removed:event"}}{{/crossLink}} event.
390 | *
391 | * @method remove
392 | * @param {Array of Components} components Array of {{#crossLink "Component"}}Components{{/crossLink}} instances.
393 | */
394 | remove: function (components) {
395 |
396 | components = xeogl._isArray(components) ? components : [components];
397 |
398 | for (var i = 0, len = components.length; i < len; i++) {
399 | this._remove(components[i]);
400 | }
401 | },
402 |
403 | _remove: function (component) {
404 |
405 | var componentId = component.id;
406 |
407 | if (component.scene !== this.scene) {
408 | this.warn("Attempted to remove component that's not in same xeogl.Scene: '" + componentId + "'");
409 | return;
410 | }
411 |
412 | delete this.components[componentId];
413 |
414 | // Unsubscribe from component destruction
415 |
416 | component.off(this._destroyedSubs[componentId]);
417 |
418 | delete this._destroyedSubs[componentId];
419 |
420 | // Unregister component for its type
421 |
422 | var types = this.types[component.type];
423 |
424 | if (types) {
425 | delete types[component.id];
426 | }
427 |
428 | this.numComponents--;
429 |
430 | /**
431 | * Fired whenever an individual {{#crossLink "Component"}}{{/crossLink}} is removed from this {{#crossLink "Collection"}}{{/crossLink}}.
432 | * @event removed
433 | * @param value {Component} The {{#crossLink "Component"}}{{/crossLink}} that was removed.
434 | */
435 | this.fire("removed", component);
436 |
437 | if (!this._dirty) {
438 | this._scheduleUpdate();
439 | }
440 | },
441 |
442 | /**
443 | * Iterates with a callback over the {{#crossLink "Component"}}Components{{/crossLink}} in this Collection.
444 | *
445 | * @method iterate
446 | * @param {Function} callback Callback called for each {{#crossLink "Component"}}{{/crossLink}}.
447 | * @param {Object} [scope=this] Optional scope for the callback, defaults to this Collection.
448 | */
449 | iterate: function (callback, scope) {
450 | scope = scope || this;
451 | var components = this.components;
452 | for (var componentId in components) {
453 | if (components.hasOwnProperty(componentId)) {
454 | callback.call(scope, components[componentId]);
455 | }
456 | }
457 | },
458 |
459 | _getJSON: function () {
460 |
461 | var componentIds = [];
462 |
463 | for (var componentId in this.components) {
464 | if (this.components.hasOwnProperty(componentId)) {
465 | componentIds.push(this.components[componentId].id); // Don't convert numbers into strings
466 | }
467 | }
468 |
469 | return {
470 | components: componentIds
471 | };
472 | },
473 |
474 | _destroy: function () {
475 |
476 | this.clear();
477 | }
478 | });
479 |
480 | });
481 |
--------------------------------------------------------------------------------
/css/metadata.css:
--------------------------------------------------------------------------------
1 | .bimsurfer-metadata h3 {
2 | font-weight: bold;
3 | margin: 0;
4 | padding: 10px 0 0 0;
5 | width: 1000px;
6 | }
7 |
8 | .bimsurfer-metadata h3:before {
9 | content: "\25b8 ";
10 | }
11 |
12 | .bimsurfer-metadata th,
13 | .bimsurfer-metadata td {
14 | width: 50%;
15 | border-bottom: solid 1px #eee;
16 | text-align: left;
17 | }
--------------------------------------------------------------------------------
/css/tree.css:
--------------------------------------------------------------------------------
1 | .bimsurfer-static-tree, .bimsurfer-static-tree * {
2 | user-select: none;
3 | -ms-user-select: none;
4 | -moz-user-select: none;
5 | -webkit-user-select: none;
6 | z-index: 1;
7 | }
8 |
9 | .bimsurfer-static-tree div.item {
10 | border-top: solid 1px #eee;
11 | line-height: 12pt;
12 | }
13 |
14 | .bimsurfer-static-tree > div > div.item {
15 | border: none;
16 | }
17 |
18 | .bimsurfer-static-tree div.bimsurfer-tree-label {
19 | margin-left: -100px;
20 | padding-left: 100px;
21 | width: 1000px;
22 | cursor: pointer;
23 | }
24 |
25 | .bimsurfer-static-tree .bimsurfer-tree-children-with-indent {
26 | padding-left: 10px;
27 | }
28 |
29 | .bimsurfer-static-tree div.selected {
30 | background: #6ae;
31 | color: white;
32 | }
33 |
34 | .bimsurfer-static-tree div.bimsurfer-tree-label:before {
35 | content: "\21b3 ";
36 | }
37 |
38 | .bimsurfer-static-tree div.bimsurfer-tree-eye:before {
39 | content: "\1f441";
40 | }
41 |
42 | .bimsurfer-tree-column {
43 | display: inline-block;
44 | overflow: hidden;
45 | }
46 |
47 | div.bimsurfer-tree-eye-off {
48 | opacity: 0.5;
49 | }
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # BIMsurfer example scripts
2 |
3 | ## Compilation and minification
4 |
5 | BimSurfer relies on RequireJS for module loading. You can use r.js to build compiled and minified source to reduce the number of requests. The glTF example uses such a minified source. Note that the resulting file includes xeogl.
6 |
7 | ~~~
8 | npm install -g requirejs uglify-es
9 | r.js -o build-gltf_app.js
10 | uglifyjs --compress --mangle -o gltf_app.min.js -- gltf_app.build.js
11 | ~~~
12 |
--------------------------------------------------------------------------------
/examples/build-gltf_app.js:
--------------------------------------------------------------------------------
1 | ({
2 | baseUrl: ".",
3 | name: "gltf_app",
4 | out: "gltf_app.build.js",
5 | paths: {
6 | bimsurfer: "../bimsurfer/"
7 | },
8 | optimize: "none"
9 | })
10 |
--------------------------------------------------------------------------------
/examples/css/apiref.css:
--------------------------------------------------------------------------------
1 | textarea, pre {
2 | padding: 20px;
3 | border: solid 1px #ddd;
4 | background: #f4f4f4;
5 | height: 12px;
6 | line-height: 12px;
7 | font-size: 12px;
8 | width: 1200px;
9 | display: block;
10 | }
11 |
12 | pre {
13 | background: white;
14 | overflow: hidden;
15 | white-space: pre-wrap;
16 | }
17 |
18 | button {
19 | margin: 10px 10px;
20 | width: 100px;
21 | }
22 |
23 | #apirefContainer {
24 | padding: 30px 10px;
25 | clear: both;
26 | }
--------------------------------------------------------------------------------
/examples/css/demo.css:
--------------------------------------------------------------------------------
1 | body{
2 | margin:0;
3 | padding:0;
4 | font-family: "Open Sans";
5 | font-size: 10pt;
6 | }
7 |
8 | #topsection{
9 | border-bottom: solid 1px #ddd;
10 | }
11 |
12 | h1, h2 {
13 | padding: 10px;
14 | margin: 0;
15 | }
16 |
17 | /* From: http://matthewjamestaylor.com/blog/holy-grail-no-quirks-mode.css */
18 | #contentwrapper{
19 | position:relative;
20 | clear:both;
21 | float:left;
22 | width:100%;
23 | overflow:hidden;
24 | border-bottom: solid 1px #ddd;
25 | }
26 |
27 | #colmid {
28 | float:left;
29 | width:200%;
30 | position:relative;
31 | left:200px;
32 | }
33 |
34 | #colright {
35 | float:left;
36 | width:100%;
37 | position:relative;
38 | left:50%;
39 | margin-left:-400px;
40 | }
41 |
42 | #col1wrap {
43 | float:right;
44 | width:50%;
45 | position:relative;
46 | right:100%;
47 | }
48 |
49 | #col1pad {
50 | margin:0 15px 0 415px;
51 | overflow:hidden;
52 | border-right: solid 1px #ddd;
53 | }
54 |
55 | #viewerContainer {
56 | width:100%;
57 | overflow:hidden;
58 | line-height: 150%;
59 | }
60 |
61 | #treeContainer {
62 | float:left;
63 | width:200px;
64 | position:relative;
65 | margin-left:-50%;
66 | left:215px;
67 | overflow-x:hidden;
68 | overflow-y:scroll;
69 | height: 600px;
70 | }
71 |
72 | #dataContainer {
73 | float:left;
74 | width:200px;
75 | position:relative;
76 | left:0px;
77 | overflow-x:hidden;
78 | overflow-y:scroll;
79 | height: 600px;
80 | }
81 |
82 | canvas {
83 | display: block;
84 | width: 100%;
85 | height: 600px;
86 | cursor: default;
87 | }
88 |
89 | #status {
90 | border-bottom: solid 1px #ddd;
91 | padding: 5px 15px;
92 | clear: both;
93 | }
94 |
95 | #typeSelector {
96 | border-top: solid 1px #ddd;
97 | padding: 2px 12px;
98 | }
99 |
100 | #typeSelector div {
101 | margin: 0 5px 0 0;
102 | padding: 3px;
103 | border: solid 1px #fff;
104 | border-radius: 3px;
105 | cursor: pointer;
106 | display: inline-block;
107 | height: 20px;
108 | line-height: 20px;
109 | }
110 |
111 | #typeSelector div.inactive:before {
112 | color: #ddd;
113 | }
114 |
115 | #typeSelector div:hover {
116 | border-color: #ddd;
117 | }
118 |
119 | #typeSelector div:before {
120 | margin-right: 5px;
121 | }
122 |
--------------------------------------------------------------------------------
/examples/gltf.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
31 |
32 |
33 |
34 |
35 |
36 |