├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── README.markdown ├── bimsurfer ├── lib │ ├── StringView.js │ ├── domReady.js │ ├── require.js │ ├── text.js │ ├── xeogl.js │ └── xeogl.min.js └── src │ ├── BimServerGeometryLoader.js │ ├── BimServerModel.js │ ├── BimServerModelLoader.js │ ├── BimSurfer.js │ ├── DataInputStreamReader.js │ ├── DefaultMaterials.js │ ├── EventHandler.js │ ├── MetaDataRenderer.js │ ├── Notifier.js │ ├── PreloadQuery.js │ ├── Request.js │ ├── StaticTreeRenderer.js │ ├── Utils.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.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 | 2 | Copyright 2018, bimsurfer.org 3 | BIM Surfer is licensed under the MIT License. 4 | 5 | # Outdated 6 | 7 | This repo holds the BIM Surfer v1 and v2. This codebase is not maintained anymore. Find the new BIM Surfer on https://github.com/opensourceBIM/BIMsurfer 8 | 9 | 10 | # Table of Contents 11 | 12 | - [Introduction](#introduction) 13 | - [Usage](#usage) 14 | - [BIMSurfer](#bimsurfer) 15 | - [Objects](#objects) 16 | - [Selecting and deselecting objects](#selecting-and-deselecting-objects) 17 | - [Showing and hiding objects](#showing-and-hiding-objects) 18 | - [Changing color and transparency of objects](#changing-color-and-transparency-of-objects) 19 | - [Camera](#camera) 20 | - [Controlling the camera](#controlling-the-camera) 21 | - [Fitting objects in view](#fitting-objects-in-view) 22 | - [Resetting](#resetting) 23 | - [Camera](#camera-1) 24 | - [Objects](#objects-1) 25 | 26 | # Introduction 27 | 28 | BIMSurfer is a WebGL-based 3D viewer for [BIMServer]() that's built on [xeogl](https://github.com/xeolabs/xeogl). 29 | 30 | TODO: More info 31 | 32 | # Usage 33 | 34 | ## BIMSurfer 35 | 36 | Creating a [BIMSurfer](bimsurfer/src/BimSurfer.js): 37 | 38 | ````javascript 39 | var bimSurfer = new BimSurfer({ 40 | domNode: "viewerContainer" 41 | }); 42 | ```` 43 | 44 | Loading a model from BIMServer: 45 | 46 | ````javascript 47 | bimSurfer.load({ 48 | bimserver: ADDRESS, 49 | username: USERNAME, 50 | password: PASSWORD, 51 | poid: 131073, 52 | roid: 65539, 53 | schema: "ifc2x3tc1" // < TODO: Deduce automatically 54 | }) 55 | .then(function (model) { 56 | 57 | // Model is now loaded and rendering. 58 | // The following sections show what you can do with BIMSurfer at this point. 59 | //... 60 | }); 61 | ```` 62 | 63 | Generate a random test model if you want to test BIMSurfer without loading anything from BIMServer: 64 | 65 | ````javascript 66 | bimSurfer.loadRandom(); 67 | ```` 68 | 69 | The following usage examples in this guide will refer to objects from the generated test model. 70 | 71 | ## Objects 72 | 73 | ### Selecting and deselecting objects 74 | 75 | Selecting four objects: 76 | 77 | ````javascript 78 | bimSurfer.setSelection({ids: ["object3", "object2", "object4", "object6"], selected: true }); 79 | ```` 80 | 81 | then querying which objects are selected: 82 | 83 | ````javascript 84 | bimSurfer.getSelection() 85 | ```` 86 | 87 | The result shows that those four objects are currently selected: 88 | 89 | ````json 90 | ["object3", "object2", "object4", "object6"] 91 | ```` 92 | 93 | If we then deselect two objects, then query the selection again: 94 | 95 | ````javascript 96 | bimSurfer.setSelection({ids: ["object3", "object6"], selected: false }); 97 | bimSurfer.getSelection() 98 | ```` 99 | 100 | The result shows that only two objects are now selected: 101 | 102 | ````json 103 | ["object2", "object4"] 104 | ```` 105 | 106 | Subscribing to selection updates: 107 | 108 | ````javascript 109 | bimSurfer.on("selection-changed", 110 | function() { 111 | var selected = bimSurfer.getSelection(); 112 | console.log("selection = " + JSON.stringify(selected)); 113 | }); 114 | ```` 115 | 116 | ### Showing and hiding objects 117 | 118 | Hiding three objects by ID: 119 | 120 | ````javascript 121 | bimSurfer.setVisibility({ids: ["object3", "object1", "object6"], visible: false }); 122 | ```` 123 | 124 | Setting two objects visible by ID: 125 | 126 | ````javascript 127 | bimSurfer.setVisibility({ids: ["object1", "object6"], visible: true }); 128 | ```` 129 | 130 | Hiding all objects of IFC types "IfcSlab" and "IfcWall": 131 | 132 | ````javascript 133 | bimSurfer.setVisibility({types: ["IfcSlab", "IfcWall"], visible: false }); 134 | ```` 135 | 136 | ### Changing color and transparency of objects 137 | 138 | Making two objects pink: 139 | 140 | ````javascript 141 | bimSurfer.setColor({ids: ["object3", "object6"], color: [1, 0, 1] }) 142 | ```` 143 | 144 | An optional fourth element may be specified in the color to set opacity: 145 | 146 | ````javascript 147 | bimSurfer.setColor({ids: ["object3", "object6"], color: [1, 0, 1, 0.5] }) 148 | ```` 149 | 150 | ## Camera 151 | 152 | ### Controlling the camera 153 | 154 | Setting the camera position: 155 | 156 | ````javascript 157 | bimSurfer.setCamera({ 158 | eye: [-20,0,20], 159 | target: [0,10,0], 160 | up: [0,1,0] 161 | }); 162 | ```` 163 | 164 | Then "target" will then be the position we'll orbit about with the mouse or arrow keys (until we double-click an object to 165 | select a different orbit position). 166 | 167 | Setting the camera projection to orthographic: 168 | 169 | ````javascript 170 | bimSurfer.setCamera({ 171 | type:"ortho" 172 | }); 173 | ```` 174 | 175 | Setting the view volume size for orthographic, switching to orthographic projection first if necessary: 176 | 177 | ````javascript 178 | bimSurfer.setCamera({ 179 | type:"ortho", 180 | scale: 100 181 | }); 182 | ```` 183 | This uses the same technique as Blender, where the scale argument relates to the "real world" size of the model, meaning 184 | that if you set scale to 100, then your view would at most encompass an element of 100 units size. 185 | 186 | Setting the camera projection to perspective: 187 | 188 | ````javascript 189 | bimSurfer.setCamera({ 190 | type:"persp" 191 | }); 192 | ```` 193 | 194 | Setting the FOV on Y-axis for perspective, switching to perspective projection first if necessary: 195 | 196 | ````javascript 197 | bimSurfer.setCamera({ 198 | type:"persp", 199 | fovy: 65 200 | }); 201 | ```` 202 | 203 | Querying camera state: 204 | 205 | ````javascript 206 | var camera = bimSurfer.getCamera(); 207 | ```` 208 | 209 | The returned value would be: 210 | 211 | ````json 212 | { 213 | "type": "persp", 214 | "eye": [-20,0,20], 215 | "target": [0,10,0], 216 | "up": [0,1,0], 217 | "fovy": 65 218 | } 219 | ```` 220 | 221 | Subscribing to camera updates: 222 | 223 | ````javascript 224 | bimSurfer.on("camera-changed", 225 | function() { 226 | var camera = bimSurfer.getCamera(); 227 | console.log(JSON.stringify(camera)); 228 | }); 229 | ```` 230 | 231 | ### Fitting objects in view 232 | 233 | Flying the camera to fit the specified objects in view: 234 | 235 | ````javascript 236 | bimSurfer.viewFit({ ids: ["object3", "object1", "object6"], animate: true }); 237 | ```` 238 | 239 | Jumping the camera to fit the specified objects in view: 240 | 241 | ````javascript 242 | bimSurfer.viewFit({ids: ["object1", "object6"], animate: false }); 243 | ```` 244 | 245 | Flying to fit all objects in view: 246 | 247 | ````javascript 248 | bimSurfer.viewFit({ animate: true }); 249 | ```` 250 | 251 | Jumping to fit all objects in view: 252 | 253 | ````javascript 254 | bimSurfer.viewFit({ animate: false }); 255 | ```` 256 | 257 | ## Resetting 258 | 259 | ### Camera 260 | 261 | Resetting the camera to initial position: 262 | 263 | ````javascript 264 | bimSurfer.reset({ cameraPosition: true }); 265 | ```` 266 | 267 | ### Objects 268 | 269 | Resetting all objects to initial visibilities: 270 | 271 | ````javascript 272 | bimSurfer.reset({ visibility: true }); 273 | ```` 274 | 275 | Resetting two objects to their initial visibilities: 276 | 277 | ````javascript 278 | bimSurfer.reset({ ids: ["object3", "object6"], visibility: true }); 279 | ```` 280 | 281 | Resetting all objects to their initial colors: 282 | 283 | ````javascript 284 | bimSurfer.reset({ elementColors: true }); 285 | ```` 286 | 287 | Resetting two objects to their initial colors: 288 | 289 | ````javascript 290 | bimSurfer.reset({ ids: ["object3", "object6"], elementColors: true }); 291 | ```` 292 | 293 | Deselecting all objects: 294 | 295 | ````javascript 296 | bimSurfer.reset({ selectionState: true }); 297 | ```` 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /bimsurfer/lib/StringView.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | function() { 4 | 5 | /*\ 6 | |*| 7 | |*| :: Number.isInteger() polyfill :: 8 | |*| 9 | |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger 10 | |*| 11 | \*/ 12 | 13 | if (!Number.isInteger) { 14 | Number.isInteger = function isInteger (nVal) { 15 | return typeof nVal === "number" && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal; 16 | }; 17 | } 18 | 19 | /*\ 20 | |*| 21 | |*| StringView - Mozilla Developer Network - revision #6 22 | |*| 23 | |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView 24 | |*| https://developer.mozilla.org/User:fusionchess 25 | |*| 26 | |*| This framework is released under the GNU Public License, version 3 or later. 27 | |*| http://www.gnu.org/licenses/gpl-3.0-standalone.html 28 | |*| 29 | \*/ 30 | 31 | function StringView (vInput, sEncoding /* optional (default: UTF-8) */, nOffset /* optional */, nLength /* optional */) { 32 | 33 | var fTAView, aWhole, aRaw, fPutOutptCode, fGetOutptChrSize, nInptLen, nStartIdx = isFinite(nOffset) ? nOffset : 0, nTranscrType = 15; 34 | 35 | if (sEncoding) { this.encoding = sEncoding.toString(); } 36 | 37 | encSwitch: switch (this.encoding) { 38 | case "UTF-8": 39 | fPutOutptCode = StringView.putUTF8CharCode; 40 | fGetOutptChrSize = StringView.getUTF8CharLength; 41 | fTAView = Uint8Array; 42 | break encSwitch; 43 | case "UTF-16": 44 | fPutOutptCode = StringView.putUTF16CharCode; 45 | fGetOutptChrSize = StringView.getUTF16CharLength; 46 | fTAView = Uint16Array; 47 | break encSwitch; 48 | case "UTF-32": 49 | fTAView = Uint32Array; 50 | nTranscrType &= 14; 51 | break encSwitch; 52 | default: 53 | /* case "ASCII", or case "BinaryString" or unknown cases */ 54 | fTAView = Uint8Array; 55 | nTranscrType &= 14; 56 | } 57 | 58 | typeSwitch: switch (typeof vInput) { 59 | case "string": 60 | /* the input argument is a primitive string: a new buffer will be created. */ 61 | nTranscrType &= 7; 62 | break typeSwitch; 63 | case "object": 64 | classSwitch: switch (vInput.constructor) { 65 | case StringView: 66 | /* the input argument is a stringView: a new buffer will be created. */ 67 | nTranscrType &= 3; 68 | break typeSwitch; 69 | case String: 70 | /* the input argument is an objectified string: a new buffer will be created. */ 71 | nTranscrType &= 7; 72 | break typeSwitch; 73 | case ArrayBuffer: 74 | /* the input argument is an arrayBuffer: the buffer will be shared. */ 75 | aWhole = new fTAView(vInput); 76 | nInptLen = this.encoding === "UTF-32" ? 77 | vInput.byteLength >>> 2 78 | : this.encoding === "UTF-16" ? 79 | vInput.byteLength >>> 1 80 | : 81 | vInput.byteLength; 82 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 83 | aWhole 84 | : new fTAView(vInput, nStartIdx, !isFinite(nLength) ? nInptLen - nStartIdx : nLength); 85 | 86 | break typeSwitch; 87 | case Uint32Array: 88 | case Uint16Array: 89 | case Uint8Array: 90 | /* the input argument is a typedArray: the buffer, and possibly the array itself, will be shared. */ 91 | fTAView = vInput.constructor; 92 | nInptLen = vInput.length; 93 | aWhole = vInput.byteOffset === 0 && vInput.length === ( 94 | fTAView === Uint32Array ? 95 | vInput.buffer.byteLength >>> 2 96 | : fTAView === Uint16Array ? 97 | vInput.buffer.byteLength >>> 1 98 | : 99 | vInput.buffer.byteLength 100 | ) ? vInput : new fTAView(vInput.buffer); 101 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 102 | vInput 103 | : vInput.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); 104 | 105 | break typeSwitch; 106 | default: 107 | /* the input argument is an array or another serializable object: a new typedArray will be created. */ 108 | aWhole = new fTAView(vInput); 109 | nInptLen = aWhole.length; 110 | aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? 111 | aWhole 112 | : aWhole.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); 113 | } 114 | break typeSwitch; 115 | default: 116 | /* the input argument is a number, a boolean or a function: a new typedArray will be created. */ 117 | aWhole = aRaw = new fTAView(Number(vInput) || 0); 118 | 119 | } 120 | 121 | if (nTranscrType < 8) { 122 | 123 | var vSource, nOutptLen, nCharStart, nCharEnd, nEndIdx, fGetInptChrSize, fGetInptChrCode; 124 | 125 | if (nTranscrType & 4) { /* input is string */ 126 | 127 | vSource = vInput; 128 | nOutptLen = nInptLen = vSource.length; 129 | nTranscrType ^= this.encoding === "UTF-32" ? 0 : 2; 130 | /* ...or...: nTranscrType ^= Number(this.encoding !== "UTF-32") << 1; */ 131 | nStartIdx = nCharStart = nOffset ? Math.max((nOutptLen + nOffset) % nOutptLen, 0) : 0; 132 | nEndIdx = nCharEnd = (Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0) + nStartIdx, nOutptLen) : nOutptLen) - 1; 133 | 134 | } else { /* input is stringView */ 135 | 136 | vSource = vInput.rawData; 137 | nInptLen = vInput.makeIndex(); 138 | nStartIdx = nCharStart = nOffset ? Math.max((nInptLen + nOffset) % nInptLen, 0) : 0; 139 | nOutptLen = Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0), nInptLen - nCharStart) : nInptLen; 140 | nEndIdx = nCharEnd = nOutptLen + nCharStart; 141 | 142 | if (vInput.encoding === "UTF-8") { 143 | fGetInptChrSize = StringView.getUTF8CharLength; 144 | fGetInptChrCode = StringView.loadUTF8CharCode; 145 | } else if (vInput.encoding === "UTF-16") { 146 | fGetInptChrSize = StringView.getUTF16CharLength; 147 | fGetInptChrCode = StringView.loadUTF16CharCode; 148 | } else { 149 | nTranscrType &= 1; 150 | } 151 | 152 | } 153 | 154 | if (nOutptLen === 0 || nTranscrType < 4 && vSource.encoding === this.encoding && nCharStart === 0 && nOutptLen === nInptLen) { 155 | 156 | /* the encoding is the same, the length too and the offset is 0... or the input is empty! */ 157 | 158 | nTranscrType = 7; 159 | 160 | } 161 | 162 | conversionSwitch: switch (nTranscrType) { 163 | 164 | case 0: 165 | 166 | /* both the source and the new StringView have a fixed-length encoding... */ 167 | 168 | aWhole = new fTAView(nOutptLen); 169 | for (var nOutptIdx = 0; nOutptIdx < nOutptLen; aWhole[nOutptIdx] = vSource[nStartIdx + nOutptIdx++]); 170 | break conversionSwitch; 171 | 172 | case 1: 173 | 174 | /* the source has a fixed-length encoding but the new StringView has a variable-length encoding... */ 175 | 176 | /* mapping... */ 177 | 178 | nOutptLen = 0; 179 | 180 | for (var nInptIdx = nStartIdx; nInptIdx < nEndIdx; nInptIdx++) { 181 | nOutptLen += fGetOutptChrSize(vSource[nInptIdx]); 182 | } 183 | 184 | aWhole = new fTAView(nOutptLen); 185 | 186 | /* transcription of the source... */ 187 | 188 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx++) { 189 | nOutptIdx = fPutOutptCode(aWhole, vSource[nInptIdx], nOutptIdx); 190 | } 191 | 192 | break conversionSwitch; 193 | 194 | case 2: 195 | 196 | /* the source has a variable-length encoding but the new StringView has a fixed-length encoding... */ 197 | 198 | /* mapping... */ 199 | 200 | nStartIdx = 0; 201 | 202 | var nChrCode; 203 | 204 | for (nChrIdx = 0; nChrIdx < nCharStart; nChrIdx++) { 205 | nChrCode = fGetInptChrCode(vSource, nStartIdx); 206 | nStartIdx += fGetInptChrSize(nChrCode); 207 | } 208 | 209 | aWhole = new fTAView(nOutptLen); 210 | 211 | /* transcription of the source... */ 212 | 213 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode), nOutptIdx++) { 214 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 215 | aWhole[nOutptIdx] = nChrCode; 216 | } 217 | 218 | break conversionSwitch; 219 | 220 | case 3: 221 | 222 | /* both the source and the new StringView have a variable-length encoding... */ 223 | 224 | /* mapping... */ 225 | 226 | nOutptLen = 0; 227 | 228 | var nChrCode; 229 | 230 | for (var nChrIdx = 0, nInptIdx = 0; nChrIdx < nCharEnd; nInptIdx += fGetInptChrSize(nChrCode)) { 231 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 232 | if (nChrIdx === nCharStart) { nStartIdx = nInptIdx; } 233 | if (++nChrIdx > nCharStart) { nOutptLen += fGetOutptChrSize(nChrCode); } 234 | } 235 | 236 | aWhole = new fTAView(nOutptLen); 237 | 238 | /* transcription... */ 239 | 240 | for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode)) { 241 | nChrCode = fGetInptChrCode(vSource, nInptIdx); 242 | nOutptIdx = fPutOutptCode(aWhole, nChrCode, nOutptIdx); 243 | } 244 | 245 | break conversionSwitch; 246 | 247 | case 4: 248 | 249 | /* DOMString to ASCII or BinaryString or other unknown encodings */ 250 | 251 | aWhole = new fTAView(nOutptLen); 252 | 253 | /* transcription... */ 254 | 255 | for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { 256 | aWhole[nIdx] = vSource.charCodeAt(nIdx) & 0xff; 257 | } 258 | 259 | break conversionSwitch; 260 | 261 | case 5: 262 | 263 | /* DOMString to UTF-8 or to UTF-16 */ 264 | 265 | /* mapping... */ 266 | 267 | nOutptLen = 0; 268 | 269 | for (var nMapIdx = 0; nMapIdx < nInptLen; nMapIdx++) { 270 | if (nMapIdx === nCharStart) { nStartIdx = nOutptLen; } 271 | nOutptLen += fGetOutptChrSize(vSource.charCodeAt(nMapIdx)); 272 | if (nMapIdx === nCharEnd) { nEndIdx = nOutptLen; } 273 | } 274 | 275 | aWhole = new fTAView(nOutptLen); 276 | 277 | /* transcription... */ 278 | 279 | for (var nOutptIdx = 0, nChrIdx = 0; nOutptIdx < nOutptLen; nChrIdx++) { 280 | nOutptIdx = fPutOutptCode(aWhole, vSource.charCodeAt(nChrIdx), nOutptIdx); 281 | } 282 | 283 | break conversionSwitch; 284 | 285 | case 6: 286 | 287 | /* DOMString to UTF-32 */ 288 | 289 | aWhole = new fTAView(nOutptLen); 290 | 291 | /* transcription... */ 292 | 293 | for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { 294 | aWhole[nIdx] = vSource.charCodeAt(nIdx); 295 | } 296 | 297 | break conversionSwitch; 298 | 299 | case 7: 300 | 301 | aWhole = new fTAView(nOutptLen ? vSource : 0); 302 | break conversionSwitch; 303 | 304 | } 305 | 306 | aRaw = nTranscrType > 3 && (nStartIdx > 0 || nEndIdx < aWhole.length - 1) ? aWhole.subarray(nStartIdx, nEndIdx) : aWhole; 307 | 308 | } 309 | 310 | this.buffer = aWhole.buffer; 311 | this.bufferView = aWhole; 312 | this.rawData = aRaw; 313 | 314 | Object.freeze(this); 315 | 316 | } 317 | 318 | /* CONSTRUCTOR'S METHODS */ 319 | 320 | StringView.loadUTF8CharCode = function (aChars, nIdx) { 321 | 322 | var nLen = aChars.length, nPart = aChars[nIdx]; 323 | 324 | return nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? 325 | /* (nPart - 252 << 32) is not possible in ECMAScript! So...: */ 326 | /* six bytes */ (nPart - 252) * 1073741824 + (aChars[nIdx + 1] - 128 << 24) + (aChars[nIdx + 2] - 128 << 18) + (aChars[nIdx + 3] - 128 << 12) + (aChars[nIdx + 4] - 128 << 6) + aChars[nIdx + 5] - 128 327 | : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? 328 | /* five bytes */ (nPart - 248 << 24) + (aChars[nIdx + 1] - 128 << 18) + (aChars[nIdx + 2] - 128 << 12) + (aChars[nIdx + 3] - 128 << 6) + aChars[nIdx + 4] - 128 329 | : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? 330 | /* four bytes */(nPart - 240 << 18) + (aChars[nIdx + 1] - 128 << 12) + (aChars[nIdx + 2] - 128 << 6) + aChars[nIdx + 3] - 128 331 | : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? 332 | /* three bytes */ (nPart - 224 << 12) + (aChars[nIdx + 1] - 128 << 6) + aChars[nIdx + 2] - 128 333 | : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? 334 | /* two bytes */ (nPart - 192 << 6) + aChars[nIdx + 1] - 128 335 | : 336 | /* one byte */ nPart; 337 | 338 | }; 339 | 340 | StringView.putUTF8CharCode = function (aTarget, nChar, nPutAt) { 341 | 342 | var nIdx = nPutAt; 343 | 344 | if (nChar < 0x80 /* 128 */) { 345 | /* one byte */ 346 | aTarget[nIdx++] = nChar; 347 | } else if (nChar < 0x800 /* 2048 */) { 348 | /* two bytes */ 349 | aTarget[nIdx++] = 0xc0 /* 192 */ + (nChar >>> 6); 350 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 351 | } else if (nChar < 0x10000 /* 65536 */) { 352 | /* three bytes */ 353 | aTarget[nIdx++] = 0xe0 /* 224 */ + (nChar >>> 12); 354 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 355 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 356 | } else if (nChar < 0x200000 /* 2097152 */) { 357 | /* four bytes */ 358 | aTarget[nIdx++] = 0xf0 /* 240 */ + (nChar >>> 18); 359 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 360 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 361 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 362 | } else if (nChar < 0x4000000 /* 67108864 */) { 363 | /* five bytes */ 364 | aTarget[nIdx++] = 0xf8 /* 248 */ + (nChar >>> 24); 365 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); 366 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 367 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 368 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 369 | } else /* if (nChar <= 0x7fffffff) */ { /* 2147483647 */ 370 | /* six bytes */ 371 | aTarget[nIdx++] = 0xfc /* 252 */ + /* (nChar >>> 32) is not possible in ECMAScript! So...: */ (nChar / 1073741824); 372 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 24) & 0x3f /* 63 */); 373 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); 374 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); 375 | aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); 376 | aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); 377 | } 378 | 379 | return nIdx; 380 | 381 | }; 382 | 383 | StringView.getUTF8CharLength = function (nChar) { 384 | return nChar < 0x80 ? 1 : nChar < 0x800 ? 2 : nChar < 0x10000 ? 3 : nChar < 0x200000 ? 4 : nChar < 0x4000000 ? 5 : 6; 385 | }; 386 | 387 | StringView.loadUTF16CharCode = function (aChars, nIdx) { 388 | 389 | /* UTF-16 to DOMString decoding algorithm */ 390 | var nFrstChr = aChars[nIdx]; 391 | 392 | return nFrstChr > 0xD7BF /* 55231 */ && nIdx + 1 < aChars.length ? 393 | (nFrstChr - 0xD800 /* 55296 */ << 10) + aChars[nIdx + 1] + 0x2400 /* 9216 */ 394 | : nFrstChr; 395 | 396 | }; 397 | 398 | StringView.putUTF16CharCode = function (aTarget, nChar, nPutAt) { 399 | 400 | var nIdx = nPutAt; 401 | 402 | if (nChar < 0x10000 /* 65536 */) { 403 | /* one element */ 404 | aTarget[nIdx++] = nChar; 405 | } else { 406 | /* two elements */ 407 | aTarget[nIdx++] = 0xD7C0 /* 55232 */ + (nChar >>> 10); 408 | aTarget[nIdx++] = 0xDC00 /* 56320 */ + (nChar & 0x3FF /* 1023 */); 409 | } 410 | 411 | return nIdx; 412 | 413 | }; 414 | 415 | StringView.getUTF16CharLength = function (nChar) { 416 | return nChar < 0x10000 ? 1 : 2; 417 | }; 418 | 419 | /* Array of bytes to base64 string decoding */ 420 | 421 | StringView.b64ToUint6 = function (nChr) { 422 | 423 | return nChr > 64 && nChr < 91 ? 424 | nChr - 65 425 | : nChr > 96 && nChr < 123 ? 426 | nChr - 71 427 | : nChr > 47 && nChr < 58 ? 428 | nChr + 4 429 | : nChr === 43 ? 430 | 62 431 | : nChr === 47 ? 432 | 63 433 | : 434 | 0; 435 | 436 | }; 437 | 438 | StringView.uint6ToB64 = function (nUint6) { 439 | 440 | return nUint6 < 26 ? 441 | nUint6 + 65 442 | : nUint6 < 52 ? 443 | nUint6 + 71 444 | : nUint6 < 62 ? 445 | nUint6 - 4 446 | : nUint6 === 62 ? 447 | 43 448 | : nUint6 === 63 ? 449 | 47 450 | : 451 | 65; 452 | 453 | }; 454 | 455 | /* Base64 string to array encoding */ 456 | 457 | StringView.bytesToBase64 = function (aBytes) { 458 | 459 | var sB64Enc = ""; 460 | 461 | for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { 462 | nMod3 = nIdx % 3; 463 | if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; } 464 | nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); 465 | if (nMod3 === 2 || aBytes.length - nIdx === 1) { 466 | sB64Enc += String.fromCharCode(StringView.uint6ToB64(nUint24 >>> 18 & 63), StringView.uint6ToB64(nUint24 >>> 12 & 63), StringView.uint6ToB64(nUint24 >>> 6 & 63), StringView.uint6ToB64(nUint24 & 63)); 467 | nUint24 = 0; 468 | } 469 | } 470 | 471 | return sB64Enc.replace(/A(?=A$|$)/g, "="); 472 | 473 | }; 474 | 475 | 476 | StringView.base64ToBytes = function (sBase64, nBlockBytes) { 477 | 478 | var 479 | sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, 480 | nOutLen = nBlockBytes ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockBytes) * nBlockBytes : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen); 481 | 482 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { 483 | nMod4 = nInIdx & 3; 484 | nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; 485 | if (nMod4 === 3 || nInLen - nInIdx === 1) { 486 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { 487 | aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; 488 | } 489 | nUint24 = 0; 490 | } 491 | } 492 | 493 | return aBytes; 494 | 495 | }; 496 | 497 | StringView.makeFromBase64 = function (sB64Inpt, sEncoding, nByteOffset, nLength) { 498 | 499 | return new StringView(sEncoding === "UTF-16" || sEncoding === "UTF-32" ? StringView.base64ToBytes(sB64Inpt, sEncoding === "UTF-16" ? 2 : 4).buffer : StringView.base64ToBytes(sB64Inpt), sEncoding, nByteOffset, nLength); 500 | 501 | }; 502 | 503 | /* DEFAULT VALUES */ 504 | 505 | StringView.prototype.encoding = "UTF-8"; /* Default encoding... */ 506 | 507 | /* INSTANCES' METHODS */ 508 | 509 | StringView.prototype.makeIndex = function (nChrLength, nStartFrom) { 510 | 511 | var 512 | 513 | aTarget = this.rawData, nChrEnd, nRawLength = aTarget.length, 514 | nStartIdx = nStartFrom || 0, nIdxEnd = nStartIdx, nStopAtChr = isNaN(nChrLength) ? Infinity : nChrLength; 515 | 516 | if (nChrLength + 1 > aTarget.length) { throw new RangeError("StringView.prototype.makeIndex - The offset can\'t be major than the length of the array - 1."); } 517 | 518 | switch (this.encoding) { 519 | 520 | case "UTF-8": 521 | 522 | var nPart; 523 | 524 | for (nChrEnd = 0; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { 525 | nPart = aTarget[nIdxEnd]; 526 | nIdxEnd += nPart > 251 && nPart < 254 && nIdxEnd + 5 < nRawLength ? 6 527 | : nPart > 247 && nPart < 252 && nIdxEnd + 4 < nRawLength ? 5 528 | : nPart > 239 && nPart < 248 && nIdxEnd + 3 < nRawLength ? 4 529 | : nPart > 223 && nPart < 240 && nIdxEnd + 2 < nRawLength ? 3 530 | : nPart > 191 && nPart < 224 && nIdxEnd + 1 < nRawLength ? 2 531 | : 1; 532 | } 533 | 534 | break; 535 | 536 | case "UTF-16": 537 | 538 | for (nChrEnd = nStartIdx; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { 539 | nIdxEnd += aTarget[nIdxEnd] > 0xD7BF /* 55231 */ && nIdxEnd + 1 < aTarget.length ? 2 : 1; 540 | } 541 | 542 | break; 543 | 544 | default: 545 | 546 | nIdxEnd = nChrEnd = isFinite(nChrLength) ? nChrLength : nRawLength - 1; 547 | 548 | } 549 | 550 | if (nChrLength) { return nIdxEnd; } 551 | 552 | return nChrEnd; 553 | 554 | }; 555 | 556 | StringView.prototype.toBase64 = function (bWholeBuffer) { 557 | 558 | return StringView.bytesToBase64( 559 | bWholeBuffer ? 560 | ( 561 | this.bufferView.constructor === Uint8Array ? 562 | this.bufferView 563 | : 564 | new Uint8Array(this.buffer) 565 | ) 566 | : this.rawData.constructor === Uint8Array ? 567 | this.rawData 568 | : 569 | new Uint8Array(this.buffer, this.rawData.byteOffset, this.rawData.length << (this.rawData.constructor === Uint16Array ? 1 : 2)) 570 | ); 571 | 572 | }; 573 | 574 | StringView.prototype.subview = function (nCharOffset /* optional */, nCharLength /* optional */) { 575 | 576 | var 577 | 578 | nChrLen, nCharStart, nStrLen, bVariableLen = this.encoding === "UTF-8" || this.encoding === "UTF-16", 579 | nStartOffset = nCharOffset, nStringLength, nRawLen = this.rawData.length; 580 | 581 | if (nRawLen === 0) { 582 | return new StringView(this.buffer, this.encoding); 583 | } 584 | 585 | nStringLength = bVariableLen ? this.makeIndex() : nRawLen; 586 | nCharStart = nCharOffset ? Math.max((nStringLength + nCharOffset) % nStringLength, 0) : 0; 587 | nStrLen = Number.isInteger(nCharLength) ? Math.max(nCharLength, 0) + nCharStart > nStringLength ? nStringLength - nCharStart : nCharLength : nStringLength; 588 | 589 | if (nCharStart === 0 && nStrLen === nStringLength) { return this; } 590 | 591 | if (bVariableLen) { 592 | nStartOffset = this.makeIndex(nCharStart); 593 | nChrLen = this.makeIndex(nStrLen, nStartOffset) - nStartOffset; 594 | } else { 595 | nStartOffset = nCharStart; 596 | nChrLen = nStrLen - nCharStart; 597 | } 598 | 599 | if (this.encoding === "UTF-16") { 600 | nStartOffset <<= 1; 601 | } else if (this.encoding === "UTF-32") { 602 | nStartOffset <<= 2; 603 | } 604 | 605 | return new StringView(this.buffer, this.encoding, nStartOffset, nChrLen); 606 | 607 | }; 608 | 609 | StringView.prototype.forEachChar = function (fCallback, oThat, nChrOffset, nChrLen) { 610 | 611 | var aSource = this.rawData, nRawEnd, nRawIdx; 612 | 613 | if (this.encoding === "UTF-8" || this.encoding === "UTF-16") { 614 | 615 | var fGetInptChrSize, fGetInptChrCode; 616 | 617 | if (this.encoding === "UTF-8") { 618 | fGetInptChrSize = StringView.getUTF8CharLength; 619 | fGetInptChrCode = StringView.loadUTF8CharCode; 620 | } else if (this.encoding === "UTF-16") { 621 | fGetInptChrSize = StringView.getUTF16CharLength; 622 | fGetInptChrCode = StringView.loadUTF16CharCode; 623 | } 624 | 625 | nRawIdx = isFinite(nChrOffset) ? this.makeIndex(nChrOffset) : 0; 626 | nRawEnd = isFinite(nChrLen) ? this.makeIndex(nChrLen, nRawIdx) : aSource.length; 627 | 628 | for (var nChrCode, nChrIdx = 0; nRawIdx < nRawEnd; nChrIdx++) { 629 | nChrCode = fGetInptChrCode(aSource, nRawIdx); 630 | fCallback.call(oThat || null, nChrCode, nChrIdx, nRawIdx, aSource); 631 | nRawIdx += fGetInptChrSize(nChrCode); 632 | } 633 | 634 | } else { 635 | 636 | nRawIdx = isFinite(nChrOffset) ? nChrOffset : 0; 637 | nRawEnd = isFinite(nChrLen) ? nChrLen + nRawIdx : aSource.length; 638 | 639 | for (nRawIdx; nRawIdx < nRawEnd; nRawIdx++) { 640 | fCallback.call(oThat || null, aSource[nRawIdx], nRawIdx, nRawIdx, aSource); 641 | } 642 | 643 | } 644 | 645 | }; 646 | 647 | StringView.prototype.valueOf = StringView.prototype.toString = function () { 648 | 649 | if (this.encoding !== "UTF-8" && this.encoding !== "UTF-16") { 650 | /* ASCII, UTF-32 or BinaryString to DOMString */ 651 | return String.fromCharCode.apply(null, this.rawData); 652 | } 653 | 654 | var fGetCode, fGetIncr, sView = ""; 655 | 656 | if (this.encoding === "UTF-8") { 657 | fGetIncr = StringView.getUTF8CharLength; 658 | fGetCode = StringView.loadUTF8CharCode; 659 | } else if (this.encoding === "UTF-16") { 660 | fGetIncr = StringView.getUTF16CharLength; 661 | fGetCode = StringView.loadUTF16CharCode; 662 | } 663 | 664 | for (var nChr, nLen = this.rawData.length, nIdx = 0; nIdx < nLen; nIdx += fGetIncr(nChr)) { 665 | nChr = fGetCode(this.rawData, nIdx); 666 | sView += String.fromCharCode(nChr); 667 | } 668 | 669 | return sView; 670 | 671 | }; 672 | 673 | return StringView; 674 | 675 | }); 676 | -------------------------------------------------------------------------------- /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/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/BimServerGeometryLoader.js: -------------------------------------------------------------------------------- 1 | define(["./DataInputStreamReader"], function (DataInputStreamReader) { 2 | 3 | function BimServerGeometryLoader(bimServerApi, viewer, model, roid, globalTransformationMatrix) { 4 | 5 | var o = this; 6 | 7 | o.bimServerApi = bimServerApi; 8 | o.viewer = viewer; 9 | o.state = {}; 10 | o.progressListeners = []; 11 | o.objectAddedListeners = []; 12 | o.prepareReceived = false; 13 | o.todo = []; 14 | o.geometryIds = {}; 15 | o.dataToInfo = {}; 16 | 17 | o.model = model; 18 | o.roid = roid; 19 | 20 | this.addProgressListener = function (progressListener) { 21 | o.progressListeners.push(progressListener); 22 | }; 23 | 24 | this.processMessage = function(stream) { 25 | var messageType = stream.readByte(); 26 | 27 | if (messageType == 0) { 28 | o._readStart(stream); 29 | } else if (messageType == 6) { 30 | o._readEnd(stream); 31 | } else { 32 | o._readObject(stream, messageType); 33 | } 34 | stream.align8(); 35 | return stream.remaining() > 0; 36 | } 37 | 38 | this.process = function () { 39 | var data = o.todo.shift(); 40 | var stream; 41 | 42 | while (data != null) { 43 | stream = new DataInputStreamReader(data); 44 | var topicId = stream.readLong(); 45 | while (o.processMessage(stream)) { 46 | 47 | } 48 | data = o.todo.shift(); 49 | } 50 | }; 51 | 52 | this.setLoadOids = function (oids) { 53 | o.options = {type: "oids", oids: oids}; 54 | }; 55 | 56 | /** 57 | * Starts this loader. 58 | */ 59 | this.start = function () { 60 | if (!o.options || o.options.type !== "oids") { 61 | throw new Error("Invalid loader configuration"); 62 | } 63 | 64 | var obj = []; 65 | 66 | o.groupId = o.roid; 67 | o.infoToOid = o.options.oids; 68 | 69 | for (var k in o.infoToOid) { 70 | var oid = parseInt(o.infoToOid[k]); 71 | o.model.apiModel.get(oid, function(object){ 72 | if (object.object._rgeometry != null) { 73 | if (object.model.objects[object.object._rgeometry] != null) { 74 | // Only if this data is preloaded, otherwise just don't include any gi 75 | object.getGeometry(function(geometryInfo){ 76 | obj.push({gid: object.object._rgeometry, oid: object.oid, object: object, info: geometryInfo.object}); 77 | }); 78 | } else { 79 | obj.push({gid: object.object._rgeometry, oid: object.oid, object: object}); 80 | } 81 | } 82 | }); 83 | } 84 | obj.sort(function(a, b){ 85 | if (a.info != null && b.info != null) { 86 | var topa = (a.info._emaxBounds.z + a.info._eminBounds.z) / 2; 87 | var topb = (b.info._emaxBounds.z + b.info._eminBounds.z) / 2; 88 | return topa - topb; 89 | } else { 90 | // Resort back to type 91 | // TODO this is dodgy when some objects do have info, and others don't 92 | return a.object.getType().localeCompare(b.object.getType()); 93 | } 94 | }); 95 | 96 | var oids = []; 97 | obj.forEach(function(wrapper){ 98 | oids.push(wrapper.object.object._rgeometry._i); 99 | }); 100 | 101 | var serializerName = "org.bimserver.serializers.binarygeometry.BinaryGeometryMessagingStreamingSerializerPlugin"; 102 | 103 | var fieldsToInclude = ["indices"]; 104 | fieldsToInclude.push("normals"); 105 | fieldsToInclude.push("vertices"); 106 | fieldsToInclude.push("colorsQuantized"); 107 | 108 | var newQuery = { 109 | type: "GeometryInfo", 110 | oids: oids, 111 | include: { 112 | type: "GeometryInfo", 113 | field: "data", 114 | include: { 115 | type: "GeometryData", 116 | fieldsDirect: fieldsToInclude 117 | } 118 | }, 119 | loaderSettings: { 120 | splitGeometry: false 121 | } 122 | }; 123 | 124 | var oldQuery = { 125 | type: "GeometryInfo", 126 | oids: oids, 127 | include: { 128 | type: "GeometryInfo", 129 | field: "data" 130 | } 131 | }; 132 | 133 | var useNewQuery = false; 134 | 135 | var pluginCallback = function (serializer) { 136 | o.bimServerApi.call("ServiceInterface", "download", { 137 | roids: [o.roid], 138 | query: JSON.stringify(useNewQuery ? newQuery : oldQuery), 139 | serializerOid : serializer.oid, 140 | sync : false 141 | }, function(topicId){ 142 | o.topicId = topicId; 143 | o.bimServerApi.registerProgressHandler(o.topicId, o._progressHandler); 144 | }); 145 | }; 146 | 147 | var promise = o.bimServerApi.getSerializerByPluginClassName(serializerName + "3", pluginCallback); 148 | if (promise) { 149 | // If this returns a promise (it'll never be cancelled btw. even in case of error) we're 150 | // talking to a newer version of the plugin ecosystem and we can try the new query. 151 | useNewQuery = true; 152 | o.bimServerApi.getSerializerByPluginClassName(serializerName).then(pluginCallback); 153 | } 154 | }; 155 | 156 | this._progressHandler = function (topicId, state) { 157 | if (topicId == o.topicId) { 158 | if (state.title == "Done preparing") { 159 | if (!o.prepareReceived) { 160 | o.prepareReceived = true; 161 | o._downloadInitiated(); 162 | } 163 | } 164 | if (state.state == "FINISHED") { 165 | o.bimServerApi.unregisterProgressHandler(o.topicId, o._progressHandler); 166 | } 167 | } 168 | }; 169 | 170 | this._downloadInitiated = function () { 171 | o.state = { 172 | mode: 0, 173 | nrObjectsRead: 0, 174 | nrObjects: 0 175 | }; 176 | // o.viewer.SYSTEM.events.trigger('progressStarted', ['Loading Geometry']); 177 | // o.viewer.SYSTEM.events.trigger('progressBarStyleChanged', BIMSURFER.Constants.ProgressBarStyle.Continuous); 178 | var msg = { 179 | longActionId: o.topicId, 180 | topicId: o.topicId 181 | }; 182 | o.bimServerApi.setBinaryDataListener(o.topicId, o._binaryDataListener); 183 | o.bimServerApi.downloadViaWebsocket(msg); 184 | }; 185 | 186 | this._binaryDataListener = function (data) { 187 | o.todo.push(data); 188 | }; 189 | 190 | this._afterRegistration = function (topicId) { 191 | o.bimServerApi.call("Bimsie1NotificationRegistryInterface", "getProgress", { 192 | topicId: o.topicId 193 | }, function (state) { 194 | o._progressHandler(o.topicId, state); 195 | }); 196 | }; 197 | 198 | this._readEnd = function (data) { 199 | o.progressListeners.forEach(function(progressListener){ 200 | progressListener("done", o.state.nrObjectsRead, o.state.nrObjectsRead); 201 | }); 202 | o.bimServerApi.call("ServiceInterface", "cleanupLongAction", {topicId: o.topicId}, function(){}); 203 | }; 204 | 205 | this._readStart = function (data) { 206 | var start = data.readUTF8(); 207 | 208 | if (start != "BGS") { 209 | console.error("data does not start with BGS (" + start + ")"); 210 | return false; 211 | } 212 | 213 | o.protocolVersion = data.readByte(); 214 | console.log("Protocol version", o.protocolVersion); 215 | 216 | if (o.protocolVersion != 10 && o.protocolVersion != 11 && o.protocolVersion != 16) { 217 | console.error("Unimplemented version"); 218 | return false; 219 | } 220 | if (o.protocolVersion > 15) { 221 | this.multiplierToMm = data.readFloat(); 222 | } 223 | data.align8(); 224 | 225 | var boundary = data.readDoubleArray(6); 226 | 227 | this._initCamera(boundary); 228 | 229 | o.state.mode = 1; 230 | 231 | o.progressListeners.forEach(function(progressListener){ 232 | progressListener("start", o.state.nrObjectsRead, o.state.nrObjectsRead); 233 | }); 234 | //o._updateProgress(); 235 | }; 236 | 237 | this._initCamera = function (boundary) { 238 | 239 | if (!this._gotCamera) { 240 | 241 | this._gotCamera = true; 242 | 243 | // Bump scene origin to center the model 244 | 245 | var xmin = boundary[0]; 246 | var ymin = boundary[1]; 247 | var zmin = boundary[2]; 248 | var xmax = boundary[3]; 249 | var ymax = boundary[4]; 250 | var zmax = boundary[5]; 251 | 252 | var diagonal = Math.sqrt( 253 | Math.pow(xmax - xmin, 2) + 254 | Math.pow(ymax - ymin, 2) + 255 | Math.pow(zmax - zmin, 2)); 256 | 257 | var scale = 100 / diagonal; 258 | 259 | this.viewer.setScale(scale); // Temporary until we find a better scaling system. 260 | 261 | var far = diagonal * 5; // 5 being a guessed constant that should somehow coincide with the max zoom-out-factor 262 | 263 | var center = [ 264 | scale * ((xmax + xmin) / 2), 265 | scale * ((ymax + ymin) / 2), 266 | scale * ((zmax + zmin) / 2) 267 | ]; 268 | 269 | this.viewer.setCamera({ 270 | type: "persp", 271 | target: center, 272 | up: [0, 0, 1], 273 | eye: [center[0] - scale * diagonal, center[1] - scale * diagonal, center[2] + scale * diagonal], 274 | far: 5000, 275 | near: 0.1, 276 | fovy: 35.8493 277 | }); 278 | } 279 | }; 280 | 281 | this._updateProgress = function () {}; 282 | 283 | this._readObject = function (stream, geometryType) { 284 | var geometryId; 285 | var numGeometries; 286 | var numParts; 287 | var objectBounds; 288 | var numIndices; 289 | var indices; 290 | var numPositions; 291 | var positions; 292 | var numNormals; 293 | var normals; 294 | var numColors; 295 | var colors = null; 296 | var reused; 297 | var type; 298 | var roid; 299 | var croid; 300 | var hasTransparency; 301 | 302 | 303 | var i; 304 | 305 | if (o.protocolVersion < 16) { 306 | stream.align8(); 307 | } 308 | 309 | if (geometryType == 1) { 310 | if (o.protocolVersion > 15) { 311 | reused = stream.readInt(); 312 | type = stream.readUTF8(); 313 | stream.align8(); 314 | 315 | roid = stream.readLong(); 316 | croid = stream.readLong(); 317 | hasTransparency = stream.readLong() == 1; 318 | } 319 | 320 | geometryId = stream.readLong(); 321 | numIndices = stream.readInt(); 322 | indices = stream.readShortArray(numIndices); 323 | 324 | if (o.protocolVersion >= 11) { 325 | stream.align4(); 326 | var b = stream.readInt(); 327 | if (b == 1) { 328 | var color = {r: stream.readFloat(), g: stream.readFloat(), b: stream.readFloat(), a: stream.readFloat()}; 329 | } 330 | } 331 | 332 | numPositions = stream.readInt(); 333 | positions = stream.readFloatArray(numPositions); 334 | numNormals = stream.readInt(); 335 | normals = stream.readFloatArray(numNormals); 336 | numColors = stream.readInt(); 337 | if (numColors > 0) { 338 | colors = stream.readFloatArray(numColors); 339 | } else if (color != null) { 340 | // Creating vertex colors here anyways (not transmitted over the line is a plus), should find a way to do this with scenejs without vertex-colors 341 | colors = new Array(numPositions * 4); 342 | for (var i=0; i 15) { 374 | indices = stream.readIntArray(numIndices); 375 | } else { 376 | indices = stream.readShortArray(numIndices); 377 | } 378 | 379 | if (o.protocolVersion >= 11) { 380 | var b = stream.readInt(); 381 | if (b == 1) { 382 | var color = {r: stream.readFloat(), g: stream.readFloat(), b: stream.readFloat(), a: stream.readFloat()}; 383 | } 384 | } 385 | stream.align4(); 386 | 387 | numPositions = stream.readInt(); 388 | positions = stream.readFloatArray(numPositions); 389 | numNormals = stream.readInt(); 390 | normals = stream.readFloatArray(numNormals); 391 | numColors = stream.readInt(); 392 | if (numColors > 0) { 393 | colors = stream.readFloatArray(numColors); 394 | } else if (color != null) { 395 | // Creating vertex colors here anyways (not transmitted over the line is a plus), should find a way to do this with scenejs without vertex-colors 396 | colors = new Array(numPositions * 4); 397 | for (var i=0; i 15) { 422 | oid = stream.readLong(); 423 | type = stream.readUTF8(); 424 | stream.align8(); 425 | } 426 | 427 | var roid = stream.readLong(); 428 | var geometryInfoOid = stream.readLong(); 429 | 430 | if (o.protocolVersion > 15) { 431 | var hasTransparency = stream.readLong() == 1; 432 | } 433 | 434 | var objectBounds = stream.readDoubleArray(6); 435 | var matrix = stream.readDoubleArray(16); 436 | if (globalTransformationMatrix != null) { 437 | xeogl.math.mulMat4(matrix, matrix, globalTransformationMatrix); 438 | } 439 | var geometryDataOid = stream.readLong(); 440 | var geometryDataOids = o.geometryIds[geometryDataOid]; 441 | var oid = o.infoToOid[geometryInfoOid]; 442 | if (geometryDataOids == null) { 443 | geometryDataOids = []; 444 | var list = o.dataToInfo[geometryDataOid]; 445 | if (list == null) { 446 | list = []; 447 | o.dataToInfo[geometryDataOid] = list; 448 | } 449 | list.push(oid); 450 | } 451 | if (oid == null) { 452 | console.error("Not found", o.infoToOid, geometryInfoOid); 453 | } else { 454 | o.model.apiModel.get(oid, function(object){ 455 | object.gid = geometryInfoOid; 456 | var modelId = o.roid; // TODO: set to the model ID 457 | o._createObject(modelId, roid, oid, oid, geometryDataOids, object.getType(), matrix); 458 | }); 459 | } 460 | } else { 461 | this.warn("Unsupported geometry type: " + geometryType); 462 | return; 463 | } 464 | 465 | o.state.nrObjectsRead++; 466 | 467 | o._updateProgress(); 468 | }; 469 | 470 | this._createObject = function (modelId, roid, oid, objectId, geometryIds, type, matrix) { 471 | 472 | if (o.state.mode == 0) { 473 | console.log("Mode is still 0, should be 1"); 474 | return; 475 | } 476 | 477 | 478 | // o.models[roid].get(oid, 479 | // function () { 480 | if (o.viewer.createObject(modelId, roid, oid, objectId, geometryIds, type, matrix)) { 481 | 482 | // o.objectAddedListeners.forEach(function (listener) { 483 | // listener(objectId); 484 | // }); 485 | } 486 | 487 | // }); 488 | }; 489 | } 490 | 491 | return BimServerGeometryLoader; 492 | 493 | }); -------------------------------------------------------------------------------- /bimsurfer/src/BimServerModel.js: -------------------------------------------------------------------------------- 1 | define(["../lib/text"], function(text) { 2 | 3 | /* 4 | var getElementName = function(elem) { 5 | var name = null; 6 | // TODO: This is synchronous right? 7 | ["LongName", "Name"].forEach(function(attr) { 8 | if (name === null && elem["get" + attr] && elem["get" + attr]()) { 9 | name = elem["get" + attr](); 10 | } 11 | }); 12 | return name; 13 | }; 14 | */ 15 | 16 | function BimServerModel(apiModel) { 17 | 18 | this.api = apiModel.bimServerApi; 19 | this.apiModel = apiModel; 20 | this.tree = null; 21 | this.treePromise = null; 22 | 23 | this.getTree = function(args) { 24 | 25 | /* 26 | // TODO: This is rather tricky. Never know when the list of Projects is exhausted. 27 | // Luckily a valid IFC contains one and only one. Let's assume there is just one. 28 | var projectEncountered = false; 29 | 30 | this.model.getAllOfType("IfcProject", false, function(project) { 31 | if (projectEncountered) { 32 | throw new Error("More than a single project encountered, bleh!"); 33 | } 34 | console.log('project', project); 35 | }); 36 | 37 | */ 38 | 39 | var self = this; 40 | 41 | return self.treePromise || (self.treePromise = new Promise(function(resolve, reject) { 42 | 43 | if (self.tree) { 44 | resolve(self.tree); 45 | } 46 | 47 | var query = 48 | { 49 | defines:{ 50 | Representation:{ 51 | type: "IfcProduct", 52 | field: "Representation" 53 | }, 54 | ContainsElementsDefine: { 55 | type: "IfcSpatialStructureElement", 56 | field:"ContainsElements", 57 | include:{ 58 | type:"IfcRelContainedInSpatialStructure", 59 | field:"RelatedElements", 60 | includes:[ 61 | "IsDecomposedByDefine", 62 | "ContainsElementsDefine", 63 | "Representation" 64 | ] 65 | } 66 | }, 67 | IsDecomposedByDefine: { 68 | type: "IfcObjectDefinition", 69 | field:"IsDecomposedBy", 70 | include: { 71 | type:"IfcRelDecomposes", 72 | field:"RelatedObjects", 73 | includes:[ 74 | "IsDecomposedByDefine", 75 | "ContainsElementsDefine", 76 | "Representation" 77 | ] 78 | } 79 | }, 80 | }, 81 | queries:[{ 82 | type:"IfcProject", 83 | includes:[ 84 | "IsDecomposedByDefine", 85 | "ContainsElementsDefine" 86 | ] 87 | }, { 88 | type:"IfcRepresentation", 89 | includeAllSubtypes: true 90 | },{ 91 | type:"IfcProductRepresentation" 92 | },{ 93 | type:"IfcPresentationLayerWithStyle" 94 | },{ 95 | type:"IfcProduct", 96 | includeAllSubtypes: true 97 | }, { 98 | type:"IfcProductDefinitionShape" 99 | }, { 100 | type:"IfcPresentationLayerAssignment" 101 | }, { 102 | type:"IfcRelAssociatesClassification", 103 | includes:[{ 104 | type:"IfcRelAssociatesClassification", 105 | field:"RelatedObjects" 106 | }, { 107 | type:"IfcRelAssociatesClassification", 108 | field:"RelatingClassification" 109 | }] 110 | }, { 111 | type:"IfcSIUnit" 112 | }, { 113 | type:"IfcPresentationLayerAssignment" 114 | }] 115 | }; 116 | 117 | // Perform the download 118 | // apiModel.query(query, function(o) {}).done(function(){ 119 | 120 | // A list of entities that define parent-child relationships 121 | var entities = { 122 | 'IfcRelDecomposes': 1, 123 | 'IfcRelAggregates': 1, 124 | 'IfcRelContainedInSpatialStructure': 1, 125 | 'IfcRelFillsElement': 1, 126 | 'IfcRelVoidsElement': 1 127 | } 128 | 129 | // Create a mapping from id->instance 130 | var instance_by_id = {}; 131 | var objects = []; 132 | 133 | for (var e in apiModel.objects) { 134 | // The root node in a dojo store should have its parent 135 | // set to null, not just something that evaluates to false 136 | var o = apiModel.objects[e].object; 137 | o.parent = null; 138 | instance_by_id[o._i] = o; 139 | objects.push(o); 140 | }; 141 | 142 | // Filter all instances based on relationship entities 143 | var relationships = objects.filter(function (o) { 144 | return entities[o._t]; 145 | }); 146 | 147 | // Construct a tuple of {parent, child} ids 148 | var parents = relationships.map(function (o) { 149 | var ks = Object.keys(o); 150 | var related = ks.filter(function (k) { 151 | return k.indexOf('Related') !== -1; 152 | }); 153 | var relating = ks.filter(function (k) { 154 | return k.indexOf('Relating') !== -1; 155 | }); 156 | return [o[relating[0]], o[related[0]]]; 157 | }); 158 | 159 | var is_array = function (o) { 160 | return Object.prototype.toString.call(o) === '[object Array]'; 161 | } 162 | 163 | var data = []; 164 | var visited = {}; 165 | parents.forEach(function (a) { 166 | // Relationships in IFC can be one to one/many 167 | var ps = is_array(a[0]) ? a[0] : [a[0]]; 168 | var cs = is_array(a[1]) ? a[1] : [a[1]]; 169 | for (var i = 0; i < ps.length; ++i) { 170 | for (var j = 0; j < cs.length; ++j) { 171 | // Lookup the instance ids in the mapping 172 | var p = instance_by_id[ps[i]._i]; 173 | var c = instance_by_id[cs[j]._i]; 174 | 175 | // parent, id, hasChildren are significant attributes in a dojo store 176 | c.parent = p.id = p._i; 177 | c.id = c._i; 178 | p.hasChildren = true; 179 | 180 | // Make sure to only add instances once 181 | if (!visited[c.id]) { 182 | data.push(c); 183 | } 184 | if (!visited[p.id]) { 185 | data.push(p); 186 | } 187 | visited[p.id] = visited[c.id] = true; 188 | } 189 | } 190 | }); 191 | 192 | var make_element = function (o) { 193 | return {name: o.Name, id: o.id, guid: o.GlobalId, parent: o.parent, gid: (o._rgeometry == null ? null : o._rgeometry._i)}; 194 | }; 195 | 196 | var fold = (function() { 197 | var root = null; 198 | return function(li) { 199 | var by_oid = {}; 200 | li.forEach(function(elem) { 201 | by_oid[elem.id] = elem; 202 | }); 203 | li.forEach(function(elem) { 204 | if (elem.parent === null) { 205 | root = elem; 206 | } else { 207 | var p = by_oid[elem.parent]; 208 | (p.children || (p.children = [])).push(elem); 209 | } 210 | }); 211 | return root; 212 | }})(); 213 | 214 | resolve(self.tree = fold(data.map(make_element))); 215 | // }); 216 | })); 217 | }; 218 | 219 | } 220 | 221 | return BimServerModel; 222 | 223 | }); -------------------------------------------------------------------------------- /bimsurfer/src/BimServerModelLoader.js: -------------------------------------------------------------------------------- 1 | define(["./BimServerModel", "./PreloadQuery", "./BimServerGeometryLoader", "./BimSurfer"], function(BimServerModel, PreloadQuery, BimServerGeometryLoader, BimSufer) { 2 | 3 | function BimServerModelLoader(bimServerClient, bimSurfer) { 4 | 5 | var o = this; 6 | 7 | this.loadFullModel = function(apiModel){ 8 | return new Promise(function(resolve, reject) { 9 | var model = new BimServerModel(apiModel); 10 | 11 | apiModel.query(PreloadQuery, function () {}).done(function(){ 12 | var oids = []; 13 | apiModel.getAllOfType("IfcProduct", true, function(object){ 14 | oids.push(object.oid); 15 | }); 16 | o.loadOids(model, oids); 17 | resolve(model); 18 | }); 19 | }); 20 | }; 21 | 22 | this.loadObjects = function(apiModel, objects){ 23 | return new Promise(function(resolve, reject) { 24 | var model = new BimServerModel(apiModel); 25 | 26 | var oids = []; 27 | objects.forEach(function(object){ 28 | oids.push(object.oid); 29 | }); 30 | o.loadOids(model, oids); 31 | resolve(model); 32 | }); 33 | }; 34 | 35 | this.loadOids = function(model, oids){ 36 | var oidToGuid = {}; 37 | var guidToOid = {}; 38 | 39 | var oidGid = {}; 40 | 41 | oids.forEach(function(oid){ 42 | model.apiModel.get(oid, function(object){ 43 | if (object.object._rgeometry != null) { 44 | var gid = object.object._rgeometry._i; 45 | var guid = object.object.GlobalId; 46 | oidToGuid[oid] = guid; 47 | guidToOid[guid] = oid; 48 | oidGid[gid] = oid; 49 | } 50 | }); 51 | }); 52 | 53 | bimSurfer._idMapping.toGuid.push(oidToGuid); 54 | bimSurfer._idMapping.toId.push(guidToOid); 55 | 56 | var viewer = bimSurfer.viewer; 57 | viewer.taskStarted(); 58 | 59 | viewer.createModel(model.apiModel.roid); 60 | 61 | var loader = new BimServerGeometryLoader(model.api, viewer, model, model.apiModel.roid, o.globalTransformationMatrix); 62 | 63 | loader.addProgressListener(function (progress, nrObjectsRead, totalNrObjects) { 64 | if (progress == "start") { 65 | console.log("Started loading geometries"); 66 | // self.fire("loading-started"); 67 | } else if (progress == "done") { 68 | console.log("Finished loading geometries (" + totalNrObjects + " objects received)"); 69 | // self.fire("loading-finished"); 70 | viewer.taskFinished(); 71 | } 72 | }); 73 | 74 | loader.setLoadOids(oidGid); 75 | 76 | // viewer.clear(); // For now, until we support multiple models through the API 77 | 78 | viewer.on("tick", function () { // TODO: Fire "tick" event from xeoViewer 79 | loader.process(); 80 | }); 81 | 82 | loader.start(); 83 | } 84 | 85 | this.setGlobalTransformationMatrix = function(globalTransformationMatrix) { 86 | o.globalTransformationMatrix = globalTransformationMatrix; 87 | } 88 | } 89 | 90 | BimServerModelLoader.prototype = Object.create(BimServerModelLoader.prototype); 91 | 92 | return BimServerModelLoader; 93 | }); -------------------------------------------------------------------------------- /bimsurfer/src/BimSurfer.js: -------------------------------------------------------------------------------- 1 | window.BIMSERVER_VERSION = "1.5"; 2 | 3 | define(["./Notifier", "./BimServerModel", "./PreloadQuery", "./BimServerGeometryLoader", "./xeoViewer/xeoViewer", "./EventHandler"], function (Notifier, Model, PreloadQuery, GeometryLoader, xeoViewer, EventHandler, _BimServerApi) { 4 | 5 | // Backwards compatibility 6 | var BimServerApi; 7 | if (_BimServerApi) { 8 | BimServerApi = _BimServerApi; 9 | } else { 10 | BimServerApi = window.BimServerClient; 11 | } 12 | 13 | function BimSurfer(cfg) { 14 | 15 | var self = this; 16 | 17 | EventHandler.call(this); 18 | 19 | cfg = cfg || {}; 20 | 21 | var viewer = this.viewer = new xeoViewer(cfg); 22 | 23 | /** 24 | * Fired whenever this BIMSurfer's camera changes. 25 | * @event camera-changed 26 | */ 27 | viewer.on("camera-changed", function() { 28 | self.fire("camera-changed", arguments); 29 | }); 30 | 31 | /** 32 | * Fired whenever this BIMSurfer's selection changes. 33 | * @event selection-changed 34 | */ 35 | viewer.on("selection-changed", function() { 36 | self.fire("selection-changed", arguments); 37 | }); 38 | 39 | // This are arrays as multiple models might be loaded or unloaded. 40 | this._idMapping = { 41 | 'toGuid': [], 42 | 'toId' : [] 43 | }; 44 | 45 | /** 46 | * Loads a model into this BIMSurfer. 47 | * @param params 48 | */ 49 | this.load = function (params) { 50 | 51 | if (params.test) { 52 | viewer.loadRandom(params); 53 | return null; 54 | 55 | } else if (params.bimserver) { 56 | return this._loadFromServer(params); 57 | 58 | } else if (params.api) { 59 | return this._loadFromAPI(params); 60 | 61 | } else if (params.src) { 62 | return this._loadFrom_glTF(params); 63 | } 64 | }; 65 | 66 | this._loadFromServer = function (params) { 67 | 68 | var notifier = new Notifier(); 69 | var bimServerApi = new BimServerApi(params.bimserver, notifier); 70 | 71 | params.api = bimServerApi; // TODO: Make copy of params 72 | 73 | return self._initApi(params) 74 | .then(self._loginToServer) 75 | .then(self._getRevisionFromServer) 76 | .then(self._loadFromAPI); 77 | }; 78 | 79 | this._initApi = function(params) { 80 | return new Promise(function(resolve, reject) { 81 | params.api.init(function () { 82 | resolve(params); 83 | }); 84 | }); 85 | }; 86 | 87 | this._loginToServer = function (params) { 88 | return new Promise(function(resolve, reject) { 89 | if (params.token) { 90 | params.api.setToken(params.token, function() { 91 | resolve(params) 92 | }, reject); 93 | } else { 94 | params.api.login(params.username, params.password, function() { 95 | resolve(params) 96 | }, reject); 97 | } 98 | }); 99 | }; 100 | 101 | this._getRevisionFromServer = function (params) { 102 | return new Promise(function(resolve, reject) { 103 | if (params.roid) { 104 | resolve(params); 105 | } else { 106 | params.api.call("ServiceInterface", "getAllRelatedProjects", {poid: params.poid}, function(data) { 107 | var resolved = false; 108 | 109 | data.forEach(function(projectData) { 110 | if (projectData.oid == params.poid) { 111 | params.roid = projectData.lastRevisionId; 112 | params.schema = projectData.schema; 113 | if (!self.models) { 114 | self.models = []; 115 | } 116 | self.models.push(projectData); 117 | resolved = true; 118 | resolve(params); 119 | } 120 | }); 121 | 122 | if (!resolved) { 123 | reject(); 124 | } 125 | }, reject); 126 | } 127 | }); 128 | }; 129 | 130 | this._loadFrom_glTF = function (params) { 131 | if (params.src) { 132 | var maxActiveProcessesEncountered = 0; 133 | var oldProgress = 0; 134 | return new Promise(function (resolve, reject) { 135 | var m = viewer.loadglTF(params.src); 136 | m.on("loaded", function() { 137 | viewer.scene.canvas.spinner.on('processes', function(n) { 138 | if (n === 0) { 139 | viewer.viewFit({}); 140 | resolve(m); 141 | } 142 | if (n > maxActiveProcessesEncountered) { 143 | maxActiveProcessesEncountered = n; 144 | } 145 | var progress = parseInt((maxActiveProcessesEncountered - n) * 100 / maxActiveProcessesEncountered); 146 | if (oldProgress != progress) { 147 | self.fire("progress", [progress]); 148 | } 149 | oldProgress = progress; 150 | }); 151 | }); 152 | }); 153 | } 154 | }; 155 | 156 | this._loadFromAPI = function (params) { 157 | 158 | return new Promise(function (resolve, reject) { 159 | 160 | params.api.getModel(params.poid, params.roid, params.schema, false, 161 | function (model) { 162 | 163 | // TODO: Preload not necessary combined with the bruteforce tree 164 | var fired = false; 165 | 166 | model.query(PreloadQuery, 167 | function () { 168 | if (!fired) { 169 | fired = true; 170 | var vmodel = new Model(params.api, model); 171 | 172 | self._loadModel(vmodel); 173 | 174 | resolve(vmodel); 175 | } 176 | }); 177 | }); 178 | }); 179 | }; 180 | 181 | this._loadModel = function (model) { 182 | 183 | model.getTree().then(function (tree) { 184 | 185 | var oids = []; 186 | var oidToGuid = {}; 187 | var guidToOid = {}; 188 | 189 | var visit = function (n) { 190 | if (BIMSERVER_VERSION == "1.4") { 191 | oids.push(n.id); 192 | } else { 193 | oids[n.gid] = n.id; 194 | } 195 | oidToGuid[n.id] = n.guid; 196 | guidToOid[n.guid] = n.id; 197 | 198 | for (var i = 0; i < (n.children || []).length; ++i) { 199 | visit(n.children[i]); 200 | } 201 | }; 202 | 203 | visit(tree); 204 | 205 | self._idMapping.toGuid.push(oidToGuid); 206 | self._idMapping.toId.push(guidToOid); 207 | 208 | var models = {}; 209 | 210 | // TODO: Ugh. Undecorate some of the newly created classes 211 | models[model.model.roid] = model.model; 212 | 213 | // Notify viewer that things are loading, so viewer can 214 | // reduce rendering speed and show a spinner. 215 | viewer.taskStarted(); 216 | 217 | viewer.createModel(model.model.roid); 218 | 219 | var loader = new GeometryLoader(model.api, models, viewer); 220 | 221 | loader.addProgressListener(function (progress, nrObjectsRead, totalNrObjects) { 222 | if (progress == "start") { 223 | console.log("Started loading geometries"); 224 | self.fire("loading-started"); 225 | } else if (progress == "done") { 226 | console.log("Finished loading geometries (" + totalNrObjects + " objects received)"); 227 | self.fire("loading-finished"); 228 | viewer.taskFinished(); 229 | } 230 | }); 231 | 232 | loader.setLoadOids([model.model.roid], oids); 233 | 234 | // viewer.clear(); // For now, until we support multiple models through the API 235 | 236 | viewer.on("tick", function () { // TODO: Fire "tick" event from xeoViewer 237 | loader.process(); 238 | }); 239 | 240 | loader.start(); 241 | }); 242 | }; 243 | 244 | // Helper function to traverse over the mappings for individually loaded models 245 | var _traverseMappings = function(mappings) { 246 | return function(k) { 247 | for (var i = 0; i < mappings.length; ++i) { 248 | var v = mappings[i][k]; 249 | if (v) return v; 250 | } 251 | return null; 252 | } 253 | }; 254 | 255 | /** 256 | * Returns a list of object ids (oid) for the list of guids (GlobalId) 257 | * 258 | * @param guids List of globally unique identifiers from the IFC model 259 | */ 260 | this.toId = function(guids) { 261 | return guids.map(_traverseMappings(self._idMapping.toId)); 262 | }; 263 | 264 | /** 265 | * Returns a list of guids (GlobalId) for the list of object ids (oid) 266 | * 267 | * @param ids List of internal object ids from the BIMserver / glTF file 268 | */ 269 | this.toGuid = function(ids) { 270 | return ids.map(_traverseMappings(self._idMapping.toGuid)); 271 | }; 272 | 273 | /** 274 | * Shows/hides objects specified by id or entity type, e.g IfcWall. 275 | * 276 | * When recursive is set to true, hides children (aggregates, spatial structures etc) or 277 | * subtypes (IfcWallStandardCase ⊆ IfcWall). 278 | * 279 | * @param params 280 | */ 281 | this.setVisibility = function (params) { 282 | viewer.setVisibility(params); 283 | }; 284 | 285 | /** 286 | * Selects/deselects objects specified by id. 287 | ** 288 | * @param params 289 | */ 290 | this.setSelection = function (params) { 291 | return viewer.setSelection(params); 292 | }; 293 | 294 | /** 295 | * Gets a list of selected elements. 296 | */ 297 | this.getSelection = function () { 298 | return viewer.getSelection(); 299 | }; 300 | 301 | /** 302 | * Sets color of objects specified by ids or entity type, e.g IfcWall. 303 | ** 304 | * @param params 305 | */ 306 | this.setColor = function (params) { 307 | viewer.setColor(params); 308 | }; 309 | 310 | /** 311 | * Sets opacity of objects specified by ids or entity type, e.g IfcWall. 312 | ** 313 | * @param params 314 | */ 315 | this.setOpacity = function (params) { 316 | viewer.setOpacity(params); 317 | }; 318 | 319 | /** 320 | * Fits the elements into view. 321 | * 322 | * Fits the entire model into view if ids is an empty array, null or undefined. 323 | * Animate allows to specify a transition period in milliseconds in which the view is altered. 324 | * 325 | * @param params 326 | */ 327 | this.viewFit = function (params) { 328 | viewer.viewFit(params); 329 | }; 330 | 331 | /** 332 | * 333 | */ 334 | this.getCamera = function () { 335 | return viewer.getCamera(); 336 | }; 337 | 338 | /** 339 | * 340 | * @param params 341 | */ 342 | this.setCamera = function (params) { 343 | viewer.setCamera(params); 344 | }; 345 | 346 | /** 347 | * Redefines light sources. 348 | * 349 | * @param params Array of lights {type: "ambient"|"dir"|"point", params: {[...]}} 350 | * See http://xeoengine.org/docs/classes/Lights.html for possible params for each light type 351 | */ 352 | this.setLights = function (params) { 353 | viewer.setLights(params); 354 | }; 355 | 356 | 357 | /** 358 | * Returns light sources. 359 | * 360 | * @returns Array of lights {type: "ambient"|"dir"|"point", params: {[...]}} 361 | */ 362 | this.getLights = function () { 363 | return viewer.getLights; 364 | }; 365 | 366 | /** 367 | * 368 | * @param params 369 | */ 370 | this.reset = function (params) { 371 | viewer.reset(params); 372 | } 373 | 374 | /** 375 | * Returns a list of loaded IFC entity types in the model. 376 | * 377 | * @method getTypes 378 | * @returns {Array} List of loaded IFC entity types, with visibility flag 379 | */ 380 | this.getTypes = function() { 381 | return viewer.getTypes(); 382 | }; 383 | 384 | /** 385 | * Sets the default behaviour of mouse and touch drag input 386 | * 387 | * @method setDefaultDragAction 388 | * @param {String} action ("pan" | "orbit") 389 | */ 390 | this.setDefaultDragAction = function (action) { 391 | viewer.setDefaultDragAction(action); 392 | }; 393 | 394 | /** 395 | * Returns the world boundary of an object 396 | * 397 | * @method getWorldBoundary 398 | * @param {String} objectId id of object 399 | * @param {Object} result Existing boundary object 400 | * @returns {Object} World boundary of object, containing {obb, aabb, center, sphere} properties. See xeogl.Boundary3D 401 | */ 402 | this.getWorldBoundary = function(objectId, result) { 403 | return viewer.getWorldBoundary(objectId, result); 404 | }; 405 | 406 | /** 407 | * Destroys the BIMSurfer 408 | */ 409 | this.destroy = function() { 410 | viewer.destroy(); 411 | } 412 | } 413 | 414 | BimSurfer.prototype = Object.create(EventHandler.prototype); 415 | 416 | return BimSurfer; 417 | 418 | }); 419 | -------------------------------------------------------------------------------- /bimsurfer/src/DataInputStreamReader.js: -------------------------------------------------------------------------------- 1 | define(["../lib/StringView"], function(StringView) { 2 | 3 | function DataInputStreamReader(arrayBuffer) { 4 | 5 | this.arrayBuffer = arrayBuffer; 6 | this.dataView = new DataView(this.arrayBuffer); 7 | this.pos = 0; 8 | 9 | this.readUTF8 = function() { 10 | var length = this.dataView.getInt16(this.pos); 11 | this.pos += 2; 12 | var view = this.arrayBuffer.slice(this.pos, this.pos + length); 13 | var result = new StringView(view).toString(); 14 | this.pos += length; 15 | return result; 16 | }; 17 | 18 | this.remaining = function() { 19 | return this.arrayBuffer.byteLength - this.pos; 20 | }; 21 | 22 | this.align4 = function() { 23 | // Skips to the next alignment of 4 (source should have done the same!) 24 | var skip = 4 - (this.pos % 4); 25 | if(skip > 0 && skip != 4) { 26 | // console.log("Skip", skip); 27 | this.pos += skip; 28 | } 29 | }; 30 | 31 | this.align8 = function() { 32 | // Skips to the next alignment of 4 (source should have done the same!) 33 | var skip = 8 - (this.pos % 8); 34 | if(skip > 0 && skip != 8) { 35 | // console.log("Skip", skip); 36 | this.pos += skip; 37 | } 38 | }; 39 | 40 | this.readDoubleArray = function(length) { 41 | var result = new Float64Array(this.arrayBuffer, this.pos, length); 42 | this.pos += length * 8; 43 | return result; 44 | }, 45 | 46 | this.readFloat = function() { 47 | var value = this.dataView.getFloat32(this.pos, true); 48 | this.pos += 4; 49 | return value; 50 | }; 51 | 52 | this.readInt = function() { 53 | var value = this.dataView.getInt32(this.pos, true); 54 | this.pos += 4; 55 | return value; 56 | }; 57 | 58 | this.readByte = function() { 59 | var value = this.dataView.getInt8(this.pos); 60 | this.pos += 1; 61 | return value; 62 | }; 63 | 64 | this.readLong = function() { 65 | var value = this.dataView.getUint32(this.pos, true) + 0x100000000 * this.dataView.getUint32(this.pos + 4, true); 66 | this.pos += 8; 67 | return value; 68 | }; 69 | 70 | this.readFloatArray2 = function(length) { 71 | var results = []; 72 | for (var i=0; i= -1) { 17 | h.splice(i, 1); 18 | found = true; 19 | } 20 | } 21 | if (!found) { 22 | throw new Error("Handler not found"); 23 | } 24 | }; 25 | 26 | EventHandler.prototype.fire = function(evt, args) { 27 | var h = this.handlers[evt]; 28 | if (!h) { 29 | return; 30 | } 31 | for (var i = 0; i < h.length; ++i) { 32 | h[i].apply(this, args); 33 | } 34 | }; 35 | 36 | return EventHandler; 37 | 38 | }); -------------------------------------------------------------------------------- /bimsurfer/src/MetaDataRenderer.js: -------------------------------------------------------------------------------- 1 | define(["./EventHandler", "./Request", "./Utils"], function(EventHandler, Request, Utils) { 2 | 3 | function Row(args) { 4 | var self = this; 5 | var num_names = 0; 6 | var num_values = 0; 7 | 8 | this.setName = function(name) { 9 | if (num_names++ > 0) { 10 | args.name.appendChild(document.createTextNode(" ")); 11 | } 12 | args.name.appendChild(document.createTextNode(name)); 13 | } 14 | 15 | this.setValue = function(value) { 16 | if (num_values++ > 0) { 17 | args.value.appendChild(document.createTextNode(", ")); 18 | } 19 | args.value.appendChild(document.createTextNode(value)); 20 | } 21 | } 22 | 23 | function Section(args) { 24 | var self = this; 25 | 26 | var div = self.div = document.createElement("div"); 27 | var nameh = document.createElement("h3"); 28 | var table = document.createElement("table"); 29 | 30 | var tr = document.createElement("tr"); 31 | table.appendChild(tr); 32 | var nameth = document.createElement("th"); 33 | var valueth = document.createElement("th"); 34 | nameth.appendChild(document.createTextNode("Name")); 35 | valueth.appendChild(document.createTextNode("Value")); 36 | tr.appendChild(nameth); 37 | tr.appendChild(valueth); 38 | 39 | div.appendChild(nameh); 40 | div.appendChild(table); 41 | 42 | args.domNode.appendChild(div); 43 | 44 | this.setName = function(name) { 45 | nameh.appendChild(document.createTextNode(name)); 46 | } 47 | 48 | this.addRow = function() { 49 | var tr = document.createElement("tr"); 50 | table.appendChild(tr); 51 | var nametd = document.createElement("td"); 52 | var valuetd = document.createElement("td"); 53 | tr.appendChild(nametd); 54 | tr.appendChild(valuetd); 55 | return new Row({name:nametd, value:valuetd}); 56 | } 57 | }; 58 | 59 | function loadModelFromSource(src) { 60 | return new Promise(function (resolve, reject) { 61 | Request.Make({url: src}).then(function(xml) { 62 | var json = Utils.XmlToJson(xml, {'Name': 'name', 'id': 'guid'}); 63 | 64 | var psets = Utils.FindNodeOfType(json, "properties")[0]; 65 | var project = Utils.FindNodeOfType(json, "decomposition")[0].children[0]; 66 | var types = Utils.FindNodeOfType(json, "types")[0]; 67 | 68 | var objects = {}; 69 | var typeObjects = {}; 70 | var properties = {}; 71 | psets.children.forEach(function(pset) { 72 | properties[pset.guid] = pset; 73 | }); 74 | 75 | var visitObject = function(parent, node) { 76 | var props = []; 77 | var o = (parent && parent.ObjectPlacement) ? objects : typeObjects; 78 | 79 | if (node["xlink:href"]) { 80 | if (!o[parent.guid]) { 81 | var p = Utils.Clone(parent); 82 | p.GlobalId = p.guid; 83 | o[p.guid] = p; 84 | o[p.guid].properties = [] 85 | } 86 | var g = node["xlink:href"].substr(1); 87 | var p = properties[g]; 88 | if (p) { 89 | o[parent.guid].properties.push(p); 90 | } else if (typeObjects[g]) { 91 | // If not a pset, it is a type, so concatenate type props 92 | o[parent.guid].properties = o[parent.guid].properties.concat(typeObjects[g].properties); 93 | } 94 | } 95 | node.children.forEach(function(n) { 96 | visitObject(node, n); 97 | }); 98 | }; 99 | 100 | visitObject(null, types); 101 | visitObject(null, project); 102 | 103 | resolve({model: {objects: objects, source: 'XML'}}); 104 | }); 105 | }); 106 | } 107 | 108 | function MetaDataRenderer(args) { 109 | 110 | var self = this; 111 | EventHandler.call(this); 112 | 113 | var models = {}; 114 | var domNode = document.getElementById(args['domNode']); 115 | 116 | this.addModel = function(args) { 117 | return new Promise(function (resolve, reject) { 118 | if (args.model) { 119 | models[args.id] = args.model; 120 | resolve(args.model); 121 | } else { 122 | loadModelFromSource(args.src).then(function(m) { 123 | models[args.id] = m; 124 | resolve(m); 125 | }); 126 | } 127 | }); 128 | }; 129 | 130 | var renderAttributes = function(elem) { 131 | var s = new Section({domNode:domNode}); 132 | s.setName(elem.type || elem.getType()); 133 | ["GlobalId", "Name", "OverallWidth", "OverallHeight", "Tag"].forEach(function(k) { 134 | var v = elem[k]; 135 | if (typeof(v) === 'undefined') { 136 | var fn = elem["get"+k]; 137 | if (fn) { 138 | v = fn.apply(elem); 139 | } 140 | } 141 | if (typeof(v) !== 'undefined') { 142 | r = s.addRow(); 143 | r.setName(k); 144 | r.setValue(v); 145 | } 146 | }); 147 | return s; 148 | }; 149 | 150 | var renderPSet = function(pset) { 151 | var s = new Section({domNode:domNode}); 152 | if (pset.name && pset.children) { 153 | s.setName(pset.name); 154 | pset.children.forEach(function(v) { 155 | var r = s.addRow(); 156 | r.setName(v.name); 157 | r.setValue(v.NominalValue); 158 | }); 159 | } else { 160 | pset.getName(function(name) { 161 | s.setName(name); 162 | }); 163 | var render = function(prop, index, row) { 164 | var r = row || s.addRow(); 165 | prop.getName(function(name) { 166 | r.setName(name); 167 | }); 168 | if (prop.getNominalValue) { 169 | prop.getNominalValue(function(value) { 170 | r.setValue(value._v); 171 | }); 172 | } 173 | if (prop.getHasProperties) { 174 | prop.getHasProperties(function(prop, index) { 175 | render(prop, index, r); 176 | }); 177 | } 178 | }; 179 | pset.getHasProperties(render); 180 | } 181 | return s; 182 | }; 183 | 184 | this.setSelected = function(oid) { 185 | if (oid.length !== 1) { 186 | domNode.innerHTML = " 
Select a single element in order to see object properties." 187 | return; 188 | }; 189 | 190 | domNode.innerHTML = ""; 191 | 192 | oid = oid[0]; 193 | 194 | if (oid.indexOf(':') !== -1) { 195 | oid = oid.split(':'); 196 | var model = models[oid[0]].model || models[oid[0]].apiModel; 197 | var o = model.objects[oid[1]]; 198 | 199 | renderAttributes(o); 200 | 201 | o.getIsDefinedBy(function(isDefinedBy){ 202 | if (isDefinedBy.getType() == "IfcRelDefinesByProperties") { 203 | isDefinedBy.getRelatingPropertyDefinition(function(pset){ 204 | if (pset.getType() == "IfcPropertySet") { 205 | renderPSet(pset); 206 | } 207 | }); 208 | } 209 | }); 210 | } else { 211 | var o = models["1"].model.objects[oid]; 212 | renderAttributes(o); 213 | o.properties.forEach(function(pset) { 214 | renderPSet(pset); 215 | }); 216 | } 217 | }; 218 | 219 | self.setSelected([]); 220 | }; 221 | 222 | MetaDataRenderer.prototype = Object.create(EventHandler.prototype); 223 | 224 | return MetaDataRenderer; 225 | 226 | }); -------------------------------------------------------------------------------- /bimsurfer/src/Notifier.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | 3 | function Notifier() { 4 | this.setSelector = function(selector) { 5 | console.log('setSelector', arguments); 6 | }; 7 | 8 | this.clear = function() { 9 | console.log('clear', arguments); 10 | }; 11 | 12 | this.resetStatus = function(){ 13 | console.log('status', arguments); 14 | }; 15 | 16 | this.resetStatusQuick = function(){ 17 | console.log('status', arguments); 18 | }; 19 | 20 | this.setSuccess = function(status, timeToShow) { 21 | console.log('success', arguments); 22 | }; 23 | 24 | this.setInfo = function(status, timeToShow) { 25 | console.log('info', arguments); 26 | }; 27 | 28 | this.setError = function(error) { 29 | console.log('error', arguments); 30 | }; 31 | }; 32 | 33 | return Notifier; 34 | 35 | }); -------------------------------------------------------------------------------- /bimsurfer/src/PreloadQuery.js: -------------------------------------------------------------------------------- 1 | define({ 2 | defines: { 3 | Representation: { 4 | type: "IfcProduct", 5 | fields: ["Representation", "geometry"] 6 | }, 7 | ContainsElementsDefine: { 8 | type: "IfcSpatialStructureElement", 9 | field: "ContainsElements", 10 | include: { 11 | type: "IfcRelContainedInSpatialStructure", 12 | field: "RelatedElements", 13 | includes: [ 14 | "IsDecomposedByDefine", 15 | "ContainsElementsDefine", 16 | "Representation" 17 | ] 18 | } 19 | }, 20 | IsDecomposedByDefine: { 21 | type: "IfcObjectDefinition", 22 | field: "IsDecomposedBy", 23 | include: { 24 | type: "IfcRelDecomposes", 25 | field: "RelatedObjects", 26 | includes: [ 27 | "IsDecomposedByDefine", 28 | "ContainsElementsDefine", 29 | "Representation" 30 | ] 31 | } 32 | } 33 | }, 34 | queries: [ 35 | { 36 | type: "IfcProject", 37 | includes: [ 38 | "IsDecomposedByDefine", 39 | "ContainsElementsDefine" 40 | ] 41 | }, 42 | { 43 | type: "IfcRepresentation", 44 | includeAllSubtypes: true 45 | }, 46 | { 47 | type: "IfcProductRepresentation" 48 | }, 49 | { 50 | type: "IfcPresentationLayerWithStyle" 51 | }, 52 | { 53 | type: "IfcProduct", 54 | includeAllSubtypes: true 55 | }, 56 | { 57 | type: "IfcProductDefinitionShape" 58 | }, 59 | { 60 | type: "IfcPresentationLayerAssignment" 61 | }, 62 | { 63 | type: "IfcRelAssociatesClassification", 64 | includes: [ 65 | { 66 | type: "IfcRelAssociatesClassification", 67 | field: "RelatedObjects" 68 | }, 69 | { 70 | type: "IfcRelAssociatesClassification", 71 | field: "RelatingClassification" 72 | } 73 | ] 74 | }, 75 | { 76 | type: "IfcSIUnit" 77 | }, 78 | { 79 | type: "IfcPresentationLayerAssignment" 80 | } 81 | ] 82 | }); 83 | -------------------------------------------------------------------------------- /bimsurfer/src/Request.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | 3 | function make(args) { 4 | return new Promise(function (resolve, reject) { 5 | var xhr = new XMLHttpRequest(); 6 | xhr.open(args.method || "GET", args.url, true); 7 | xhr.onload = function (e) { 8 | console.log(args.url, xhr.readyState, xhr.status) 9 | if (xhr.readyState === 4) { 10 | if (xhr.status === 200) { 11 | resolve(xhr.responseXML); 12 | } else { 13 | reject(xhr.statusText); 14 | } 15 | } 16 | }; 17 | xhr.send(null); 18 | }); 19 | } 20 | 21 | return { 22 | 'Make': make 23 | }; 24 | 25 | }); -------------------------------------------------------------------------------- /bimsurfer/src/StaticTreeRenderer.js: -------------------------------------------------------------------------------- 1 | define(["./EventHandler", "./Request", "./Utils"], function(EventHandler, Request, Utils) { 2 | 3 | function StaticTreeRenderer(args) { 4 | 5 | var self = this; 6 | EventHandler.call(this); 7 | 8 | var TOGGLE = self.TOGGLE = 0; 9 | var SELECT = self.SELECT = 1; 10 | var SELECT_EXCLUSIVE = self.SELECT_EXCLUSIVE = 2; 11 | var DESELECT = self.DESELECT = 3; 12 | 13 | var fromXml = false; 14 | 15 | var domNodes = {}; 16 | var selectionState = {}; 17 | 18 | this.getOffset = function(elem) { 19 | var reference = document.getElementById(args['domNode']); 20 | var y = 0; 21 | while (true) { 22 | y += elem.offsetTop; 23 | if (elem == reference) { 24 | break; 25 | } 26 | elem = elem.offsetParent; 27 | } 28 | return y; 29 | }; 30 | 31 | this.setSelected = function(ids, mode) { 32 | if (mode == SELECT_EXCLUSIVE) { 33 | self.setSelected(self.getSelected(true), DESELECT); 34 | } 35 | 36 | ids.forEach(function(id) { 37 | var s = null; 38 | if (mode == TOGGLE) { 39 | s = selectionState[id] = !selectionState[id]; 40 | } else if (mode == SELECT || mode == SELECT_EXCLUSIVE) { 41 | s = selectionState[id] = true; 42 | } else if (mode == DESELECT) { 43 | s = selectionState[id] = false; 44 | } 45 | 46 | domNodes[id].className = s ? "label selected" : "label"; 47 | }); 48 | 49 | var desiredViewRange = self.getSelected().map(function(id) { 50 | return self.getOffset(domNodes[id]); 51 | }); 52 | 53 | if (desiredViewRange.length) { 54 | desiredViewRange.sort() 55 | desiredViewRange = [desiredViewRange[0], desiredViewRange[desiredViewRange.length-1]]; 56 | 57 | var domNode = document.getElementById(args['domNode']); 58 | var currentViewRange = [domNode.scrollTop, domNode.scrollTop + domNode.offsetHeight]; 59 | 60 | if (!(desiredViewRange[0] >= currentViewRange[0] && desiredViewRange[1] <= currentViewRange[1])) { 61 | if ( (desiredViewRange[1] - desiredViewRange[0]) > (currentViewRange[1] - currentViewRange[0]) ) { 62 | domNode.scrollTop = desiredViewRange[0]; 63 | } else { 64 | var l = parseInt((desiredViewRange[1] + desiredViewRange[0]) / 2. - (currentViewRange[1] - currentViewRange[0]) / 2., 10); 65 | l = Math.max(l, 0); 66 | l = Math.min(l, domNode.scrollHeight - domNode.offsetHeight); 67 | domNode.scrollTop = l; 68 | } 69 | } 70 | } 71 | 72 | this.fire("selection-changed", [self.getSelected(true)]) 73 | }; 74 | 75 | this.getSelected = function(b) { 76 | b = typeof(b) === 'undefined' ? true: !!b; 77 | var l = []; 78 | Object.keys(selectionState).forEach(function (k) { 79 | if (!!selectionState[k] === b) { 80 | l.push(k); 81 | } 82 | }); 83 | return l; 84 | }; 85 | 86 | var models = []; 87 | 88 | this.addModel = function(args) { 89 | models.push(args); 90 | if (args.src) { 91 | fromXml = true; 92 | } 93 | }; 94 | 95 | this.qualifyInstance = function(modelId, id) { 96 | if (fromXml) { 97 | return id; 98 | } else { 99 | return modelId + ":" + id; 100 | } 101 | }; 102 | 103 | this.build = function() { 104 | var build = function(modelId, d, n) { 105 | var qid = self.qualifyInstance(modelId, fromXml ? n.guid : n.id); 106 | var label = document.createElement("div"); 107 | var children = document.createElement("div"); 108 | 109 | label.className = "label"; 110 | label.appendChild(document.createTextNode(n.name || n.guid)); 111 | d.appendChild(label); 112 | children.className = "children"; 113 | d.appendChild(children); 114 | domNodes[qid] = label; 115 | 116 | label.onclick = function(evt) { 117 | evt.stopPropagation(); 118 | evt.preventDefault(); 119 | self.setSelected([qid], evt.shiftKey ? TOGGLE : SELECT_EXCLUSIVE); 120 | self.fire("click", [qid, self.getSelected(true)]); 121 | return false; 122 | }; 123 | 124 | for (var i = 0; i < (n.children || []).length; ++i) { 125 | var child = n.children[i]; 126 | if (fromXml) { 127 | if (child["xlink:href"]) continue; 128 | if (child.type === "IfcOpeningElement") continue; 129 | } 130 | var d2 = document.createElement("div"); 131 | d2.className = "item"; 132 | children.appendChild(d2); 133 | build(modelId, d2, child); 134 | } 135 | } 136 | models.forEach(function(m) { 137 | var d = document.createElement("div"); 138 | d.className = "item"; 139 | if (m.tree) { 140 | build(m.id, d, m.tree); 141 | } else if (m.src) { 142 | Request.Make({url: m.src}).then(function(xml) { 143 | var json = Utils.XmlToJson(xml, {'Name': 'name', 'id': 'guid'}); 144 | var project = Utils.FindNodeOfType(json.children[0], "decomposition")[0].children[0]; 145 | build(m.id || i, d, project); 146 | }); 147 | } 148 | document.getElementById(args['domNode']).appendChild(d); 149 | }); 150 | } 151 | 152 | }; 153 | 154 | StaticTreeRenderer.prototype = Object.create(EventHandler.prototype); 155 | 156 | return StaticTreeRenderer; 157 | 158 | }); -------------------------------------------------------------------------------- /bimsurfer/src/Utils.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | 3 | var xmlToJson = function(node, attributeRenamer) { 4 | if (node.nodeType === node.TEXT_NODE) { 5 | var v = node.nodeValue; 6 | if (v.match(/^\s+$/) === null) { 7 | return v; 8 | } 9 | } else if (node.nodeType === node.ELEMENT_NODE || 10 | node.nodeType === node.DOCUMENT_NODE) 11 | { 12 | var json = {type: node.nodeName, children: []}; 13 | 14 | if (node.nodeType === node.ELEMENT_NODE) { 15 | for (var j = 0; j < node.attributes.length; j++) { 16 | var attribute = node.attributes[j]; 17 | var nm = attributeRenamer[attribute.nodeName] || attribute.nodeName; 18 | json[nm] = attribute.nodeValue; 19 | } 20 | } 21 | 22 | for (var i = 0; i < node.childNodes.length; i++) { 23 | var item = node.childNodes[i]; 24 | var j = xmlToJson(item, attributeRenamer); 25 | if (j) json.children.push(j); 26 | } 27 | 28 | return json; 29 | } 30 | }; 31 | 32 | var clone = function(ob) { 33 | return JSON.parse(JSON.stringify(ob)); 34 | }; 35 | 36 | var guidChars = [["0",10],["A",26],["a",26],["_",1],["$",1]].map(function(a) { 37 | var li = []; 38 | var st = a[0].charCodeAt(0); 39 | var en = st + a[1]; 40 | for (var i = st; i < en; ++i) { 41 | li.push(i); 42 | } 43 | return String.fromCharCode.apply(null, li); 44 | }).join(""); 45 | 46 | var b64 = function(v, len) { 47 | var r = (!len || len == 4) ? [0,6,12,18] : [0,6]; 48 | return r.map(function(i) { 49 | return guidChars.substr(parseInt(v / (1 << i)) % 64, 1) 50 | }).reverse().join(""); 51 | }; 52 | 53 | var compressGuid = function(g) { 54 | var bs = [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30].map(function(i) { 55 | return parseInt(g.substr(i, 2), 16); 56 | }); 57 | return b64(bs[0], 2) + [1, 4, 7, 10, 13].map(function(i) { 58 | return b64((bs[i] << 16) + (bs[i+1] << 8) + bs[i+2]); 59 | }).join(""); 60 | }; 61 | 62 | var findNodeOfType = function(m, t) { 63 | var li = []; 64 | var _ = function(n) { 65 | if (n.type === t) li.push(n); 66 | (n.children || []).forEach(function(c) {_(c);}); 67 | } 68 | _(m); 69 | return li; 70 | }; 71 | 72 | var timeout = function(dt) { 73 | return new Promise(function (resolve, reject) { 74 | setTimeout(resolve, dt); 75 | }); 76 | }; 77 | 78 | return { 79 | 'XmlToJson': xmlToJson, 80 | 'Clone': clone, 81 | 'CompressGuid': compressGuid, 82 | 'FindNodeOfType': findNodeOfType, 83 | 'Delay': timeout 84 | }; 85 | 86 | }); -------------------------------------------------------------------------------- /bimsurfer/src/xeoViewer/controls/cursors/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensourceBIM/BIMsurfer-before2019/12246d89de08ed789301303d059a90287e9e5cb4/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 | } 12 | 13 | .bimsurfer-static-tree > div.item { 14 | border: none; 15 | } 16 | 17 | .bimsurfer-static-tree div.label { 18 | margin-left: -100px; 19 | padding-left: 100px; 20 | width: 1000px; 21 | cursor: pointer; 22 | } 23 | 24 | .bimsurfer-static-tree div.children { 25 | padding-left: 10px; 26 | } 27 | 28 | .bimsurfer-static-tree div.selected { 29 | background: #6ae; 30 | color: white; 31 | } 32 | 33 | .bimsurfer-static-tree div.label:before { 34 | content: "\21b3 "; 35 | } 36 | -------------------------------------------------------------------------------- /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/bimserver.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BIMsurfer demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 39 | 40 | 41 | 318 | 319 | 320 |
321 |
322 |

BIMsurfer demo

323 |
324 |
 
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 | 340 |
341 |
342 |
343 | 344 |
345 |
346 | 347 |
348 |

API Reference

349 |
350 | 351 | 352 | -------------------------------------------------------------------------------- /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 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |

BIMsurfer demo

34 |
35 |
 
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 51 |
52 |
53 |
54 | 55 |
56 |
57 | 58 | -------------------------------------------------------------------------------- /examples/gltf_app.js: -------------------------------------------------------------------------------- 1 | function highlight(oid, selected) { 2 | // Clicking an explorer node fits the view to its object and selects 3 | if (selected.length) { 4 | bimSurfer.viewFit({ 5 | ids: selected, 6 | animate: true 7 | }); 8 | } 9 | bimSurfer.setSelection({ 10 | ids:selected, 11 | clear:true, 12 | selected:true 13 | }); 14 | } 15 | 16 | require([ 17 | "bimsurfer/src/BimSurfer", 18 | "bimsurfer/src/StaticTreeRenderer", 19 | "bimsurfer/src/MetaDataRenderer", 20 | "bimsurfer/src/Request", 21 | "bimsurfer/src/Utils", 22 | "bimsurfer/lib/domReady!", 23 | ], 24 | function (BimSurfer, StaticTreeRenderer, MetaDataRenderer, Request, Utils) { 25 | var bimSurfer = new BimSurfer({ 26 | domNode: "viewerContainer" 27 | }); 28 | 29 | var modelName = window.location.hash; 30 | if (modelName.length < 1) { 31 | modelName = "Duplex_A_20110907_optimized"; 32 | } else { 33 | modelName = modelName.substr(1); 34 | } 35 | modelName = "models/" + modelName; 36 | 37 | var tree = new StaticTreeRenderer({ 38 | domNode: "treeContainer" 39 | }); 40 | tree.addModel({id: 1, src: modelName + ".xml"}); 41 | tree.build(); 42 | 43 | tree.on("click", highlight); 44 | 45 | var data = new MetaDataRenderer({ 46 | domNode: "dataContainer" 47 | }); 48 | data.addModel({id: 1, src: modelName + ".xml"}); 49 | 50 | bimSurfer.load({ 51 | src: modelName + ".gltf" 52 | }).then(function (model) { 53 | 54 | var scene = bimSurfer.viewer.scene; 55 | 56 | var aabb = scene.worldBoundary.aabb; 57 | var diag = xeogl.math.subVec3(aabb.slice(3), aabb, xeogl.math.vec3()); 58 | var modelExtent = xeogl.math.lenVec3(diag); 59 | 60 | scene.camera.project.near = modelExtent / 1000.; 61 | scene.camera.project.far = modelExtent * 100.; 62 | 63 | scene.camera.view.eye = [-1,-1,5]; 64 | scene.camera.view.up = [0,0,1]; 65 | bimSurfer.viewFit({centerModel:true}); 66 | 67 | bimSurfer.viewer.scene.canvas.canvas.style.display = 'block'; 68 | 69 | }); 70 | 71 | bimSurfer.on("selection-changed", function(selected) { 72 | data.setSelected(selected.objects.map(function(id) { 73 | return Utils.CompressGuid(id.split("#")[1].substr(8, 36).replace(/-/g, "")); 74 | })); 75 | }); 76 | 77 | // Lets us play with the Surfer in the console 78 | window.bimSurfer = bimSurfer; 79 | }); -------------------------------------------------------------------------------- /examples/js/app.js: -------------------------------------------------------------------------------- 1 | require(["bimsurfer/src/BimSurfer.js", "bimsurfer/src/StaticTreeRenderer.js", "bimsurfer/src/MetaDataRenderer.js", "bimsurfer/lib/domReady.js!"], function (BimSurfer, StaticTreeRenderer, MetaDataRenderer) { 2 | 3 | var bimSurfer = new BimSurfer({ 4 | domNode: "viewerContainer" 5 | }); 6 | 7 | // For console debugging 8 | window.bimSurfer = bimSurfer; 9 | 10 | bimSurfer.load({ 11 | bimserver: ADDRESS, 12 | username: USERNAME, 13 | password: PASSWORD, 14 | poid: 5439489, 15 | roid: 11468803, 16 | schema: "ifc2x3tc1" // < TODO: Deduce automatically 17 | }).then(function (model) { 18 | 19 | model.getTree().then(function (tree) { 20 | 21 | var domtree = new StaticTreeRenderer({ 22 | domNode: 'treeContainer' 23 | }); 24 | 25 | domtree.addModel({name: tree.name, id:model.model.roid, tree:tree}); 26 | domtree.build(); 27 | 28 | var metadata = new MetaDataRenderer({ 29 | domNode: 'dataContainer' 30 | }); 31 | 32 | metadata.addModel({name: tree.name, id:model.model.roid, model:model}); 33 | 34 | domtree.on("click", function (oid) { 35 | // Clicking an explorer node fits the view to its object 36 | bimSurfer.viewFit({ 37 | ids: [oid], 38 | animate: true 39 | }); 40 | }); 41 | 42 | bimSurfer.on("selection-changed", function(selected) { 43 | domtree.setSelected(selected, domtree.SELECT_EXCLUSIVE); 44 | metadata.setSelected(selected); 45 | }); 46 | }); 47 | 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /examples/js/utils.js: -------------------------------------------------------------------------------- 1 | // http://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-get-parameters 2 | var QueryString = function () { 3 | // This function is anonymous, is executed immediately and 4 | // the return value is assigned to QueryString! 5 | var query_string = {}; 6 | var query = window.location.search.substring(1); 7 | var vars = query.split("&"); 8 | for (var i=0;i 2 | 3 | 4 | BIMsurfer V2 - Demo index 5 | 6 | 7 |

BIMservers

8 |
9 |
10 |
11 |
12 |

Other BIMserver

13 | Load from other server 14 | 21 |

glTF

22 | glTF 23 | 144 | 145 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 bimsurfer.org 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /plugin/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensourceBIM/BIMsurfer-before2019/12246d89de08ed789301303d059a90287e9e5cb4/plugin/icon.png -------------------------------------------------------------------------------- /plugin/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bimsurfer2 5 | BIMsurferV2 6 | BIMsurfer is a JavaScript library to visualize BIM in 3D 7 | 8 | -------------------------------------------------------------------------------- /plugin/version.properties: -------------------------------------------------------------------------------- 1 | build.date=${timestamp} -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.opensourcebim 4 | bimsurfer2 5 | 0.0.1-SNAPSHOT 6 | jar 7 | 8 | UTF-8 9 | ${maven.build.timestamp} 10 | yyyy-MM-dd'T'HH:mm:ssZ 11 | 12 | BIMsurfer2 13 | 14 | 15 | 16 | ../../output 17 | 18 | 19 | plugin 20 | plugin 21 | 22 | 23 | css 24 | css 25 | 26 | 27 | bimsurfer 28 | bimsurfer 29 | 30 | 31 | 32 | 33 | org.codehaus.mojo 34 | build-helper-maven-plugin 35 | 36 | 37 | attach-plugin 38 | package 39 | 40 | attach-artifact 41 | 42 | 43 | 44 | 45 | plugin/plugin.xml 46 | xml 47 | plugin 48 | 49 | 50 | plugin/icon.png 51 | png 52 | icon 53 | 54 | 55 | ${project.build.outputDirectory}/plugin/version.properties 56 | properties 57 | version 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.sonatype.plugins 66 | nexus-staging-maven-plugin 67 | 1.6.3 68 | true 69 | 70 | ossrh 71 | https://oss.sonatype.org/ 72 | true 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-release-plugin 78 | 2.5.3 79 | 80 | github.com 81 | false 82 | release 83 | deploy 84 | 85 | 86 | 87 | 88 | 89 | 90 | release 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-gpg-plugin 96 | 1.5 97 | 98 | 99 | sign-artifacts 100 | verify 101 | 102 | sign 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ossrh 114 | https://oss.sonatype.org/content/repositories/snapshots 115 | 116 | 117 | ossrh 118 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 119 | 120 | 121 | 122 | 123 | org.opensourcebim 124 | bimserverapi 125 | 0.0.83 126 | 127 | 128 | 129 | https://github.com/opensourceBIM/BIMsurfer.git 130 | scm:git:https://github.com/opensourceBIM/BIMsurfer.git 131 | scm:git:https://github.com/opensourceBIM/BIMsurfer.git 132 | V2 133 | 134 | 135 | OpenSource BIM 136 | opensourcebim.org 137 | 138 | 139 | GitHub 140 | https://github.com/opensourceBIM/BIMsurfer/issues 141 | 142 | BIMsurfer 143 | https://github.com/opensourceBIM/BIMsurfer 144 | 145 | 146 | GNU Affero General Public License 147 | http://www.gnu.org/licenses/agpl-3.0.en.html 148 | repo 149 | 150 | 151 | 152 | 153 | ruben@logic-labs.nl 154 | Ruben de Laat 155 | 156 | 157 | --------------------------------------------------------------------------------