├── .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 |