@mcolabs/threebox-plugin 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +97 -0
- package/README.md +199 -0
- package/dist/threebox.cjs +2 -0
- package/dist/threebox.cjs.map +1 -0
- package/dist/threebox.iife.js +2 -0
- package/dist/threebox.iife.js.map +1 -0
- package/dist/threebox.js +3521 -0
- package/dist/threebox.js.map +1 -0
- package/package.json +57 -0
- package/src/Threebox.js +1201 -0
- package/src/animation/AnimationManager.js +482 -0
- package/src/camera/CameraSync.js +298 -0
- package/src/index.js +8 -0
- package/src/objects/CSS2DRenderer.js +236 -0
- package/src/objects/LabelRenderer.js +70 -0
- package/src/objects/Object3D.js +32 -0
- package/src/objects/effects/BuildingShadows.js +163 -0
- package/src/objects/extrusion.js +59 -0
- package/src/objects/fflate.min.js +6 -0
- package/src/objects/label.js +25 -0
- package/src/objects/line.js +45 -0
- package/src/objects/loadObj.js +139 -0
- package/src/objects/objects.js +1111 -0
- package/src/objects/sphere.js +22 -0
- package/src/objects/tooltip.js +26 -0
- package/src/objects/tube.js +28 -0
- package/src/utils/ValueGenerator.js +11 -0
- package/src/utils/constants.js +23 -0
- package/src/utils/material.js +52 -0
- package/src/utils/suncalc.js +311 -0
- package/src/utils/utils.js +420 -0
- package/src/utils/validate.js +114 -0
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author peterqliu / https://github.com/peterqliu
|
|
3
|
+
* @author jscastro / https://github.com/jscastro76
|
|
4
|
+
*/
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
import utils from '../utils/utils.js';
|
|
7
|
+
import material from '../utils/material.js';
|
|
8
|
+
import AnimationManager from '../animation/AnimationManager.js';
|
|
9
|
+
import { CSS2DObject } from './CSS2DRenderer.js';
|
|
10
|
+
|
|
11
|
+
function Objects(){
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Objects.prototype = {
|
|
16
|
+
|
|
17
|
+
// standard 1px line with gl
|
|
18
|
+
line: function (obj) {
|
|
19
|
+
|
|
20
|
+
obj = utils._validate(obj, this._defaults.line);
|
|
21
|
+
|
|
22
|
+
//project to world and normalize
|
|
23
|
+
var straightProject = utils.lnglatsToWorld(obj.geometry);
|
|
24
|
+
var normalized = utils.normalizeVertices(straightProject);
|
|
25
|
+
|
|
26
|
+
//flatten array for buffergeometry
|
|
27
|
+
var flattenedArray = utils.flattenVectors(normalized.vertices);
|
|
28
|
+
|
|
29
|
+
var positions = new Float32Array(flattenedArray); // 3 vertices per point
|
|
30
|
+
var geometry = new THREE.BufferGeometry();
|
|
31
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
32
|
+
|
|
33
|
+
// material
|
|
34
|
+
var material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 21 });
|
|
35
|
+
var line = new THREE.Line(geometry, material);
|
|
36
|
+
|
|
37
|
+
line.options = options || {};
|
|
38
|
+
line.position.copy(normalized.position)
|
|
39
|
+
|
|
40
|
+
return line
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
extrusion: function (options) {
|
|
44
|
+
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
unenroll: function (obj, isStatic) {
|
|
48
|
+
var root = this;
|
|
49
|
+
|
|
50
|
+
if (isStatic) {
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
else {
|
|
55
|
+
// Bestow this mesh with animation superpowers and keeps track of its movements in the global animation queue
|
|
56
|
+
root.animationManager.unenroll(obj);
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
_addMethods: function (obj, isStatic) {
|
|
63
|
+
|
|
64
|
+
var root = this;
|
|
65
|
+
const labelName = "label";
|
|
66
|
+
const tooltipName = "tooltip";
|
|
67
|
+
const helpName = "help";
|
|
68
|
+
const shadowPlane = "shadowPlane";
|
|
69
|
+
|
|
70
|
+
if (isStatic) {
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
else {
|
|
75
|
+
|
|
76
|
+
if (!obj.coordinates) obj.coordinates = [0, 0, 0];
|
|
77
|
+
|
|
78
|
+
//[jscastro] added property for the internal 3D model
|
|
79
|
+
Object.defineProperty(obj, 'model', {
|
|
80
|
+
get() {
|
|
81
|
+
return obj.getObjectByName("model");
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
let _animations;
|
|
86
|
+
//[jscastro] added property for the internal 3D model
|
|
87
|
+
Object.defineProperty(obj, 'animations', {
|
|
88
|
+
get() {
|
|
89
|
+
const model = obj.model;
|
|
90
|
+
if (model) {
|
|
91
|
+
return model.animations
|
|
92
|
+
} else return null;
|
|
93
|
+
},
|
|
94
|
+
//set(value) { _animations = value}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Bestow this mesh with animation superpowers and keeps track of its movements in the global animation queue
|
|
98
|
+
root.animationManager.enroll(obj);
|
|
99
|
+
|
|
100
|
+
// Place an object on the map at the given lnglat
|
|
101
|
+
obj.setCoords = function (lnglat) {
|
|
102
|
+
|
|
103
|
+
// CSS2DObjects could bring an specific vertical positioning to correct in units
|
|
104
|
+
if (obj.userData.topMargin && obj.userData.feature) {
|
|
105
|
+
lnglat[2] += ((obj.userData.feature.properties.height || 0) - (obj.userData.feature.properties.base_height || obj.userData.feature.properties.min_height || 0)) * (obj.userData.topMargin || 0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
obj.coordinates = lnglat;
|
|
109
|
+
obj.set({ position: lnglat });
|
|
110
|
+
return obj;
|
|
111
|
+
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
obj.setTranslate = function (lnglat) {
|
|
115
|
+
|
|
116
|
+
obj.set({ translate: lnglat });
|
|
117
|
+
return obj;
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
obj.setRotation = function (xyz) {
|
|
122
|
+
|
|
123
|
+
if (typeof xyz === 'number') xyz = { z: xyz }
|
|
124
|
+
|
|
125
|
+
var r = {
|
|
126
|
+
x: utils.radify(xyz.x) || obj.rotation.x,
|
|
127
|
+
y: utils.radify(xyz.y) || obj.rotation.y,
|
|
128
|
+
z: utils.radify(xyz.z) || obj.rotation.z
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
obj._setObject({ rotation: [r.x, r.y, r.z] })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//[jscastro] added method to adjust 3D models to their issues with center position for rotation
|
|
135
|
+
obj.calculateAdjustedPosition = function (lnglat, xyz, inverse) {
|
|
136
|
+
|
|
137
|
+
let location = lnglat.slice();
|
|
138
|
+
|
|
139
|
+
//we convert the units to Long/Lat/Height
|
|
140
|
+
let newCoords = utils.unprojectFromWorld(obj.modelSize);
|
|
141
|
+
|
|
142
|
+
if (inverse) {
|
|
143
|
+
//each model will have different adjustment attributes, we add them for x, y, z
|
|
144
|
+
location[0] -= (xyz.x != 0 ? (newCoords[0] / xyz.x) : 0);
|
|
145
|
+
location[1] -= (xyz.y != 0 ? (newCoords[1] / xyz.y) : 0);
|
|
146
|
+
location[2] -= (xyz.z != 0 ? (newCoords[2] / xyz.z) : 0);
|
|
147
|
+
} else {
|
|
148
|
+
//each model will have different adjustment attributes, we add them for x, y, z
|
|
149
|
+
location[0] += (xyz.x != 0 ? (newCoords[0] / xyz.x) : 0);
|
|
150
|
+
location[1] += (xyz.y != 0 ? (newCoords[1] / xyz.y) : 0);
|
|
151
|
+
location[2] += (xyz.z != 0 ? (newCoords[2] / xyz.z) : 0);
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
return location;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//[jscastro] added method to rotate on objects on an axis instead of centers
|
|
158
|
+
obj.setRotationAxis = function (xyz) {
|
|
159
|
+
if (typeof xyz === 'number') xyz = { z: xyz }
|
|
160
|
+
|
|
161
|
+
let bb = obj.modelBox();
|
|
162
|
+
|
|
163
|
+
let point = new THREE.Vector3(bb.max.x, bb.max.y, bb.min.z);
|
|
164
|
+
//apply Axis rotation on angle
|
|
165
|
+
if (xyz.x != 0) _applyAxisAngle(obj, point, new THREE.Vector3(0, 0, 1), xyz.x);
|
|
166
|
+
if (xyz.y != 0) _applyAxisAngle(obj, point, new THREE.Vector3(0, 0, 1), xyz.y);
|
|
167
|
+
if (xyz.z != 0) _applyAxisAngle(obj, point, new THREE.Vector3(0, 0, 1), xyz.z);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//[jscastro] Auxiliar method to rotate an object on an axis
|
|
171
|
+
function _applyAxisAngle(model, point, axis, degrees) {
|
|
172
|
+
let theta = utils.radify(degrees);
|
|
173
|
+
model.position.sub(point); // remove the offset
|
|
174
|
+
model.position.applyAxisAngle(axis, theta); // rotate the POSITION
|
|
175
|
+
model.position.add(point); // re-add the offset
|
|
176
|
+
model.rotateOnAxis(axis, theta)
|
|
177
|
+
|
|
178
|
+
tb.map.repaint = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
//[jscastro] added property for scaled group inside threeboxObject
|
|
183
|
+
Object.defineProperty(obj, 'scaleGroup', {
|
|
184
|
+
get() {
|
|
185
|
+
return obj.getObjectByName("scaleGroup");
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
//[jscastro] added property for boundingBox group helper
|
|
190
|
+
Object.defineProperty(obj, 'boxGroup', {
|
|
191
|
+
get() {
|
|
192
|
+
return obj.getObjectByName("boxGroup");
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
//[jscastro] added property for boundingBox helper
|
|
197
|
+
Object.defineProperty(obj, 'boundingBox', {
|
|
198
|
+
get() {
|
|
199
|
+
return obj.getObjectByName("boxModel");
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
let _boundingBoxShadow;
|
|
204
|
+
//[jscastro] added property for boundingBox shadow helper
|
|
205
|
+
Object.defineProperty(obj, 'boundingBoxShadow', {
|
|
206
|
+
get() {
|
|
207
|
+
return obj.getObjectByName("boxShadow");
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
//[jscastro] added method to create a bounding box and a shadow box
|
|
212
|
+
obj.drawBoundingBox = function () {
|
|
213
|
+
//let's create 2 wireframes, one for the object and one to project on the floor position
|
|
214
|
+
let bb = obj.box3();
|
|
215
|
+
//create the group to return
|
|
216
|
+
let boxGroup = new THREE.Group();
|
|
217
|
+
boxGroup.name = "boxGroup";
|
|
218
|
+
boxGroup.updateMatrixWorld(true);
|
|
219
|
+
let boxModel = new THREE.Box3Helper(bb, Objects.prototype._defaults.colors.yellow);
|
|
220
|
+
boxModel.name = "boxModel";
|
|
221
|
+
boxGroup.add(boxModel);
|
|
222
|
+
boxModel.layers.disable(0); // it makes the object invisible for the raycaster
|
|
223
|
+
//obj.boundingBox = boxModel;
|
|
224
|
+
|
|
225
|
+
//it needs to clone, to avoid changing the object by reference
|
|
226
|
+
let bb2 = bb.clone();
|
|
227
|
+
//we make the second box flat and at the floor height level
|
|
228
|
+
bb2.max.z = bb2.min.z;
|
|
229
|
+
let boxShadow = new THREE.Box3Helper(bb2, Objects.prototype._defaults.colors.black);
|
|
230
|
+
boxShadow.name = "boxShadow";
|
|
231
|
+
|
|
232
|
+
boxGroup.add(boxShadow);
|
|
233
|
+
boxShadow.layers.disable(0); // it makes the object invisible for the raycaster
|
|
234
|
+
//obj.boundingBoxShadow = boxShadow;
|
|
235
|
+
|
|
236
|
+
boxGroup.visible = false; // visibility is managed from the parent
|
|
237
|
+
obj.scaleGroup.add(boxGroup);
|
|
238
|
+
obj.setBoundingBoxShadowFloor();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//[jscastro] added method to position the shadow box on the floor depending the object height
|
|
242
|
+
obj.setBoundingBoxShadowFloor = function () {
|
|
243
|
+
if (obj.boundingBoxShadow) {
|
|
244
|
+
let h = -obj.modelHeight, r = obj.rotation, o = obj.boundingBoxShadow;
|
|
245
|
+
o.box.max.z = o.box.min.z = h;
|
|
246
|
+
o.rotation.y = r.y;
|
|
247
|
+
o.rotation.x = -r.x;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
//[jscastro] Set the positional and pivotal anchor automatically from string param
|
|
252
|
+
obj.setAnchor = function (anchor) {
|
|
253
|
+
const b = obj.box3();
|
|
254
|
+
//const size = b.getSize(new THREE.Vector3());
|
|
255
|
+
const c = b.getCenter(new THREE.Vector3());
|
|
256
|
+
obj.none = { x: 0, y: 0, z: 0 };
|
|
257
|
+
obj.center = { x: c.x, y: c.y, z: b.min.z };
|
|
258
|
+
obj.bottom = { x: c.x, y: b.max.y, z: b.min.z };
|
|
259
|
+
obj.bottomLeft = { x: b.max.x, y: b.max.y, z: b.min.z };
|
|
260
|
+
obj.bottomRight = { x: b.min.x, y: b.max.y, z: b.min.z };
|
|
261
|
+
obj.top = { x: c.x, y: b.min.y, z: b.min.z };
|
|
262
|
+
obj.topLeft = { x: b.max.x, y: b.min.y, z: b.min.z };
|
|
263
|
+
obj.topRight = { x: b.min.x, y: b.min.y, z: b.min.z };
|
|
264
|
+
obj.left = { x: b.max.x, y: c.y, z: b.min.z };
|
|
265
|
+
obj.right = { x: b.min.x, y: c.y, z: b.min.z };
|
|
266
|
+
|
|
267
|
+
switch (anchor) {
|
|
268
|
+
case 'center':
|
|
269
|
+
obj.anchor = obj.center;
|
|
270
|
+
break;
|
|
271
|
+
case 'top':
|
|
272
|
+
obj.anchor = obj.top;
|
|
273
|
+
break;
|
|
274
|
+
case 'top-left':
|
|
275
|
+
obj.anchor = obj.topLeft;
|
|
276
|
+
break;
|
|
277
|
+
case 'top-right':
|
|
278
|
+
obj.anchor = obj.topRight;
|
|
279
|
+
break;
|
|
280
|
+
case 'left':
|
|
281
|
+
obj.anchor = obj.left;
|
|
282
|
+
break;
|
|
283
|
+
case 'right':
|
|
284
|
+
obj.anchor = obj.right;
|
|
285
|
+
break;
|
|
286
|
+
case 'bottom':
|
|
287
|
+
obj.anchor = obj.bottom;
|
|
288
|
+
break;
|
|
289
|
+
case 'bottom-left':
|
|
290
|
+
default:
|
|
291
|
+
obj.anchor = obj.bottomLeft;
|
|
292
|
+
break;
|
|
293
|
+
case 'bottom-right':
|
|
294
|
+
obj.anchor = obj.bottomRight;
|
|
295
|
+
break;
|
|
296
|
+
case 'auto':
|
|
297
|
+
case 'none':
|
|
298
|
+
obj.anchor = obj.none;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
obj.model.position.set(-obj.anchor.x, -obj.anchor.y, -obj.anchor.z);
|
|
302
|
+
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
//[jscastro] Set the positional and pivotal anchor based on (x, y, z) size units
|
|
306
|
+
obj.setCenter = function (center) {
|
|
307
|
+
//[jscastro] if the object options have an adjustment to center the 3D Object different to 0
|
|
308
|
+
if (center && (center.x != 0 || center.y != 0 || center.z != 0)) {
|
|
309
|
+
let size = obj.getSize();
|
|
310
|
+
obj.anchor = { x: obj.anchor.x - (size.x * center.x), y: obj.anchor.y - (size.y * center.y), z: obj.anchor.z - (size.z * center.z) };
|
|
311
|
+
obj.model.position.set(-obj.anchor.x, -obj.anchor.y, -obj.anchor.z)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
//[jscastro] added property for simulated label
|
|
316
|
+
Object.defineProperty(obj, 'label', {
|
|
317
|
+
get() { return obj.getObjectByName(labelName); }
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
//[jscastro] added property for simulated tooltip
|
|
321
|
+
Object.defineProperty(obj, 'tooltip', {
|
|
322
|
+
get() { return obj.getObjectByName(tooltipName); }
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
//[jscastro] added property for help
|
|
326
|
+
Object.defineProperty(obj, 'help', {
|
|
327
|
+
get() { return obj.getObjectByName(helpName); }
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
let _hidden = false;
|
|
331
|
+
//[jscastro] added property for explicitely hidden object to avoid zoom layer behavior
|
|
332
|
+
Object.defineProperty(obj, 'hidden', {
|
|
333
|
+
get() { return _hidden; },
|
|
334
|
+
set(value) {
|
|
335
|
+
if (_hidden != value) {
|
|
336
|
+
_hidden = value;
|
|
337
|
+
obj.visibility = !_hidden;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
//[jscastro] added property to redefine visible, including the label and tooltip
|
|
343
|
+
Object.defineProperty(obj, 'visibility', {
|
|
344
|
+
get() { return obj.visible; },
|
|
345
|
+
set(value) {
|
|
346
|
+
let _value = value;
|
|
347
|
+
if (value == 'visible' || value == true) {
|
|
348
|
+
_value = true;
|
|
349
|
+
if (obj.label) obj.label.visible = _value;
|
|
350
|
+
}
|
|
351
|
+
else if (value == 'none' || value == false) {
|
|
352
|
+
_value = false;
|
|
353
|
+
if (obj.label && obj.label.alwaysVisible) obj.label.visible = _value;
|
|
354
|
+
if (obj.tooltip) obj.tooltip.visible = _value;
|
|
355
|
+
}
|
|
356
|
+
else return;
|
|
357
|
+
if (obj.visible != _value) {
|
|
358
|
+
if (obj.hidden && _value) return;
|
|
359
|
+
|
|
360
|
+
obj.visible = _value;
|
|
361
|
+
|
|
362
|
+
if (obj.model) {
|
|
363
|
+
obj.model.traverse(function (c) {
|
|
364
|
+
if (c.type == "Mesh" || c.type == "SkinnedMesh") {
|
|
365
|
+
if (_value && obj.raycasted) {
|
|
366
|
+
c.layers.enable(0); //this makes the meshes visible for raycast
|
|
367
|
+
} else {
|
|
368
|
+
c.layers.disable(0); //this makes the meshes invisible for raycast
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (c.type == "LineSegments") {
|
|
372
|
+
c.layers.disableAll();
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
//[jscastro] add CSS2 label method
|
|
381
|
+
obj.addLabel = function (HTMLElement, visible, center, height) {
|
|
382
|
+
if (HTMLElement) {
|
|
383
|
+
//we add it to the first children to get same boxing and position
|
|
384
|
+
//obj.children[0].add(obj.drawLabel(text, height));
|
|
385
|
+
obj.drawLabelHTML(HTMLElement, visible, center, height);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//[jscastro] remove CSS2 label method
|
|
390
|
+
obj.removeLabel = function () {
|
|
391
|
+
obj.removeCSS2D(labelName);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
//[jscastro] draw label method can be invoked separately
|
|
395
|
+
obj.drawLabelHTML = function (HTMLElement, visible = false, center = obj.anchor, height = 0.5) {
|
|
396
|
+
let divLabel = root.drawLabelHTML(HTMLElement, Objects.prototype._defaults.label.cssClass);
|
|
397
|
+
let label = obj.addCSS2D(divLabel, labelName, center, height) //label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered
|
|
398
|
+
label.alwaysVisible = visible;
|
|
399
|
+
label.visible = visible;
|
|
400
|
+
return label;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
//[jscastro] add tooltip method
|
|
404
|
+
obj.addTooltip = function (tooltipText, mapboxStyle, center, custom = true, height = 1) {
|
|
405
|
+
let t = obj.addHelp(tooltipText, tooltipName, mapboxStyle, center, height);
|
|
406
|
+
t.visible = false;
|
|
407
|
+
t.custom = custom;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
//[jscastro] remove CSS2 tooltip method
|
|
411
|
+
obj.removeTooltip = function () {
|
|
412
|
+
obj.removeCSS2D(tooltipName);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
//[jscastro] add tooltip method
|
|
416
|
+
obj.addHelp = function (helpText, objName = helpName, mapboxStyle = false, center = obj.anchor, height = 0) {
|
|
417
|
+
let divHelp = root.drawTooltip(helpText, mapboxStyle);
|
|
418
|
+
let h = obj.addCSS2D(divHelp, objName, center, height);
|
|
419
|
+
h.visible = true;
|
|
420
|
+
return h;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
//[jscastro] remove CSS2 tooltip method
|
|
424
|
+
obj.removeHelp = function () {
|
|
425
|
+
obj.removeCSS2D(helpName);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
//[jscastro] add CSS2D help method
|
|
429
|
+
obj.addCSS2D = function (element, objName, center = obj.anchor, height = 1) {
|
|
430
|
+
if (element) {
|
|
431
|
+
const box = obj.box3();
|
|
432
|
+
const size = box.getSize(new THREE.Vector3());
|
|
433
|
+
let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z };
|
|
434
|
+
obj.removeCSS2D(objName);
|
|
435
|
+
let c = new CSS2DObject(element);
|
|
436
|
+
c.name = objName;
|
|
437
|
+
c.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * height);
|
|
438
|
+
c.visible = false; //only visible on mouseover or selected
|
|
439
|
+
obj.scaleGroup.add(c);
|
|
440
|
+
return c;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
//[jscastro] remove CSS2 help method
|
|
445
|
+
obj.removeCSS2D = function (objName) {
|
|
446
|
+
let css2D = obj.getObjectByName(objName);
|
|
447
|
+
if (css2D) {
|
|
448
|
+
css2D.dispose();
|
|
449
|
+
let g = obj.scaleGroup.children;
|
|
450
|
+
g.splice(g.indexOf(css2D), 1);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
//[jscastro] added property for help
|
|
455
|
+
Object.defineProperty(obj, 'shadowPlane', {
|
|
456
|
+
get() { return obj.getObjectByName(shadowPlane); }
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
let _castShadow = false;
|
|
460
|
+
//[jscastro] added property for traverse an object to cast a shadow
|
|
461
|
+
Object.defineProperty(obj, 'castShadow', {
|
|
462
|
+
get() { return _castShadow; },
|
|
463
|
+
set(value) {
|
|
464
|
+
if (!obj.model || _castShadow === value) return;
|
|
465
|
+
|
|
466
|
+
obj.model.traverse(function (c) {
|
|
467
|
+
if (c.isMesh) c.castShadow = true;
|
|
468
|
+
});
|
|
469
|
+
if (value) {
|
|
470
|
+
// we add the shadow plane automatically
|
|
471
|
+
const s = obj.modelSize;
|
|
472
|
+
const sz = [s.x, s.y, s.z, obj.modelHeight];
|
|
473
|
+
const pSize = Math.max(...sz) * 10;
|
|
474
|
+
const pGeo = new THREE.PlaneGeometry(pSize, pSize);
|
|
475
|
+
const pMat = new THREE.ShadowMaterial();
|
|
476
|
+
//const pMat = new THREE.MeshStandardMaterial({ color: 0x660000 });
|
|
477
|
+
pMat.opacity = 0.5;
|
|
478
|
+
let p = new THREE.Mesh(pGeo, pMat);
|
|
479
|
+
p.name = shadowPlane;
|
|
480
|
+
p.layers.enable(1); p.layers.disable(0); // it makes the object invisible for the raycaster
|
|
481
|
+
p.receiveShadow = value;
|
|
482
|
+
obj.add(p);
|
|
483
|
+
} else {
|
|
484
|
+
// or we remove it
|
|
485
|
+
obj.traverse(function (c) {
|
|
486
|
+
if (c.isMesh && c.material instanceof THREE.ShadowMaterial)
|
|
487
|
+
obj.remove(c);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
}
|
|
491
|
+
_castShadow = value;
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
//[jscastro] added method to position the shadow box on the floor depending the object height
|
|
497
|
+
obj.setReceiveShadowFloor = function () {
|
|
498
|
+
if (obj.castShadow) {
|
|
499
|
+
let sp = obj.shadowPlane, p = sp.position, r = sp.rotation;
|
|
500
|
+
p.z = -obj.modelHeight;
|
|
501
|
+
r.y = obj.rotation.y;
|
|
502
|
+
r.x = -obj.rotation.x;
|
|
503
|
+
if (obj.userData.units === 'meters') {
|
|
504
|
+
const s = obj.modelSize;
|
|
505
|
+
const sz = [s.x, s.y, s.z, -p.z];
|
|
506
|
+
const ps = Math.max(...sz) * 10;
|
|
507
|
+
const sc = ps / sp.geometry.parameters.width;
|
|
508
|
+
sp.scale.set(sc, sc, sc);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
let _receiveShadow = false;
|
|
514
|
+
//[jscastro] added property for traverse an object to receive a shadow
|
|
515
|
+
Object.defineProperty(obj, 'receiveShadow', {
|
|
516
|
+
get() { return _receiveShadow; },
|
|
517
|
+
set(value) {
|
|
518
|
+
if (!obj.model || _receiveShadow === value) return;
|
|
519
|
+
obj.model.traverse(function (c) {
|
|
520
|
+
if (c.isMesh) c.receiveShadow = true;
|
|
521
|
+
});
|
|
522
|
+
_receiveShadow = value;
|
|
523
|
+
}
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
let _wireframe = false;
|
|
527
|
+
//[jscastro] added property for wireframes state
|
|
528
|
+
Object.defineProperty(obj, 'wireframe', {
|
|
529
|
+
get() { return _wireframe; },
|
|
530
|
+
set(value) {
|
|
531
|
+
if (!obj.model || _wireframe === value) return;
|
|
532
|
+
obj.model.traverse(function (c) {
|
|
533
|
+
if (c.type == "Mesh" || c.type == "SkinnedMesh") {
|
|
534
|
+
let materials = [];
|
|
535
|
+
if (!Array.isArray(c.material)) {
|
|
536
|
+
materials.push(c.material);
|
|
537
|
+
} else {
|
|
538
|
+
materials = c.material;
|
|
539
|
+
}
|
|
540
|
+
let m = materials[0];
|
|
541
|
+
if (value) {
|
|
542
|
+
c.userData.materials = m;
|
|
543
|
+
c.material = m.clone();
|
|
544
|
+
c.material.wireframe = c.material.transparent = value;
|
|
545
|
+
c.material.opacity = 0.3;
|
|
546
|
+
} else {
|
|
547
|
+
c.material.dispose();
|
|
548
|
+
c.material = c.userData.materials;
|
|
549
|
+
c.userData.materials.dispose();
|
|
550
|
+
c.userData.materials = null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (value) { c.layers.disable(0); c.layers.enable(1); } else { c.layers.disable(1); c.layers.enable(0); }
|
|
554
|
+
}
|
|
555
|
+
if (c.type == "LineSegments") {
|
|
556
|
+
c.layers.disableAll();
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
_wireframe = value;
|
|
560
|
+
// Dispatch new event WireFramed
|
|
561
|
+
obj.dispatchEvent({ type: 'Wireframed', detail: obj });
|
|
562
|
+
}
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
let _color = null;
|
|
566
|
+
//[jscastro] added property for wireframes state
|
|
567
|
+
Object.defineProperty(obj, 'color', {
|
|
568
|
+
get() { return _color; },
|
|
569
|
+
set(value) {
|
|
570
|
+
if (!obj.model || _color === value) return;
|
|
571
|
+
obj.model.traverse(function (c) {
|
|
572
|
+
if (c.type == "Mesh" || c.type == "SkinnedMesh") {
|
|
573
|
+
let materials = [];
|
|
574
|
+
if (!Array.isArray(c.material)) {
|
|
575
|
+
materials.push(c.material);
|
|
576
|
+
} else {
|
|
577
|
+
materials = c.material;
|
|
578
|
+
}
|
|
579
|
+
let m = materials[0];
|
|
580
|
+
if (value) {
|
|
581
|
+
c.userData.materials = m;
|
|
582
|
+
c.material = new THREE.MeshStandardMaterial();
|
|
583
|
+
c.material.color.setHex(value);
|
|
584
|
+
} else {
|
|
585
|
+
c.material.dispose();
|
|
586
|
+
c.material = c.userData.materials;
|
|
587
|
+
c.userData.materials.dispose();
|
|
588
|
+
c.userData.materials = null;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
_color = value;
|
|
594
|
+
}
|
|
595
|
+
})
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
let _selected = false;
|
|
599
|
+
//[jscastro] added property for selected state
|
|
600
|
+
Object.defineProperty(obj, 'selected', {
|
|
601
|
+
get() { return _selected; },
|
|
602
|
+
set(value) {
|
|
603
|
+
if (value) {
|
|
604
|
+
if (obj.userData.bbox && !obj.boundingBox) obj.drawBoundingBox();
|
|
605
|
+
if (obj.boxGroup) {
|
|
606
|
+
obj.boundingBox.material = Objects.prototype._defaults.materials.boxSelectedMaterial;
|
|
607
|
+
obj.boundingBox.parent.visible = true;
|
|
608
|
+
obj.boundingBox.layers.enable(1);
|
|
609
|
+
obj.boundingBoxShadow.layers.enable(1);
|
|
610
|
+
}
|
|
611
|
+
if (obj.label && !obj.label.alwaysVisible) obj.label.visible = true;
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
if (obj.boxGroup) {
|
|
615
|
+
obj.remove(obj.boxGroup); //remove the box group
|
|
616
|
+
}
|
|
617
|
+
if (obj.label && !obj.label.alwaysVisible) obj.label.visible = false;
|
|
618
|
+
obj.removeHelp();
|
|
619
|
+
}
|
|
620
|
+
if (obj.tooltip) obj.tooltip.visible = value;
|
|
621
|
+
//only fire the event if value is different
|
|
622
|
+
if (_selected != value) {
|
|
623
|
+
_selected = value;
|
|
624
|
+
// Dispatch new event SelectedChange
|
|
625
|
+
obj.dispatchEvent({ type: 'SelectedChange', detail: obj });
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
let _raycasted = true;
|
|
631
|
+
//[jscastro] added property for including/excluding an object from raycast
|
|
632
|
+
Object.defineProperty(obj, 'raycasted', {
|
|
633
|
+
get() { return _raycasted; },
|
|
634
|
+
set(value) {
|
|
635
|
+
if (!obj.model || _raycasted === value) return;
|
|
636
|
+
obj.model.traverse(function (c) {
|
|
637
|
+
if (c.type == "Mesh" || c.type == "SkinnedMesh") {
|
|
638
|
+
if (!value) { c.layers.disable(0); c.layers.enable(1); } else { c.layers.disable(1); c.layers.enable(0); }
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
_raycasted = value;
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
let _over = false;
|
|
646
|
+
//[jscastro] added property for over state
|
|
647
|
+
Object.defineProperty(obj, 'over', {
|
|
648
|
+
get() { return _over; },
|
|
649
|
+
set(value) {
|
|
650
|
+
if (value) {
|
|
651
|
+
if (!obj.selected) {
|
|
652
|
+
if (obj.userData.bbox && !obj.boundingBox) obj.drawBoundingBox();
|
|
653
|
+
if (obj.userData.tooltip && !obj.tooltip) obj.addTooltip(obj.uuid, true, obj.anchor, false);
|
|
654
|
+
if (obj.boxGroup) {
|
|
655
|
+
obj.boundingBox.material = Objects.prototype._defaults.materials.boxOverMaterial;
|
|
656
|
+
obj.boundingBox.parent.visible = true;
|
|
657
|
+
obj.boundingBox.layers.enable(1);
|
|
658
|
+
obj.boundingBoxShadow.layers.enable(1);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (obj.label && !obj.label.alwaysVisible) { obj.label.visible = true; }
|
|
662
|
+
// Dispatch new event ObjectOver
|
|
663
|
+
obj.dispatchEvent({ type: 'ObjectMouseOver', detail: obj });
|
|
664
|
+
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
if (!obj.selected) {
|
|
668
|
+
if (obj.boxGroup) {
|
|
669
|
+
obj.remove(obj.boxGroup); //remove the box group
|
|
670
|
+
if (obj.tooltip && !obj.tooltip.custom) obj.removeTooltip();
|
|
671
|
+
}
|
|
672
|
+
if (obj.label && !obj.label.alwaysVisible) { obj.label.visible = false; }
|
|
673
|
+
}
|
|
674
|
+
// Dispatch new event ObjectOver
|
|
675
|
+
obj.dispatchEvent({ type: 'ObjectMouseOut', detail: obj });
|
|
676
|
+
}
|
|
677
|
+
if (obj.tooltip) obj.tooltip.visible = value || obj.selected;
|
|
678
|
+
_over = value;
|
|
679
|
+
}
|
|
680
|
+
})
|
|
681
|
+
|
|
682
|
+
//[jscastro] get the object model Box3 in runtime
|
|
683
|
+
obj.box3 = function () {
|
|
684
|
+
//update Matrix and MatrixWorld to avoid issues with transformations not full applied
|
|
685
|
+
obj.updateMatrix();
|
|
686
|
+
obj.updateMatrixWorld(true, true);
|
|
687
|
+
let bounds;
|
|
688
|
+
//clone also the model inside it's the one who keeps the real size
|
|
689
|
+
if (obj.model) {
|
|
690
|
+
//let's clone the object before manipulate it
|
|
691
|
+
let dup = obj.clone(true);
|
|
692
|
+
let model = obj.model.clone();
|
|
693
|
+
//get the size of the model because the object is translated and has boundingBoxShadow
|
|
694
|
+
bounds = new THREE.Box3().setFromObject(model);
|
|
695
|
+
//if the object has parent it's already in the added to world so it's scaled and it could be rotated
|
|
696
|
+
if (obj.parent) {
|
|
697
|
+
//first, we return the object to it's original position of rotation, extract rotation and apply inversed
|
|
698
|
+
let rm = new THREE.Matrix4();
|
|
699
|
+
let rmi = new THREE.Matrix4();
|
|
700
|
+
obj.matrix.extractRotation(rm);
|
|
701
|
+
rmi.copy(rm).invert();
|
|
702
|
+
dup.setRotationFromMatrix(rmi);
|
|
703
|
+
//now the object inside will give us a NAABB Non-Axes Aligned Bounding Box
|
|
704
|
+
bounds = new THREE.Box3().setFromObject(model);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return bounds;
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
//[jscastro] modelBox
|
|
711
|
+
obj.modelBox = function () {
|
|
712
|
+
return obj.box3();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
obj.getSize = function () {
|
|
716
|
+
return obj.box3().getSize(new THREE.Vector3(0, 0, 0));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
//[jscastro]
|
|
720
|
+
let _modelSize = false;
|
|
721
|
+
//[jscastro] added property for wireframes state
|
|
722
|
+
Object.defineProperty(obj, 'modelSize', {
|
|
723
|
+
get() {
|
|
724
|
+
_modelSize = obj.getSize();
|
|
725
|
+
return _modelSize;
|
|
726
|
+
},
|
|
727
|
+
set(value) {
|
|
728
|
+
if (_modelSize != value) {
|
|
729
|
+
_modelSize = value;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
//[jscastro] added property to get modelHeight
|
|
736
|
+
Object.defineProperty(obj, 'modelHeight', {
|
|
737
|
+
get() {
|
|
738
|
+
let h = obj.coordinates[2] || 0;
|
|
739
|
+
if (obj.userData.units === 'scene') h *= (obj.unitsPerMeter / obj.scale.x);
|
|
740
|
+
return h;
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
//[jscastro] added property to calculate the units per meter in a given latitude
|
|
745
|
+
//reduced to 7 decimals to avoid deviations on the size of the same object
|
|
746
|
+
Object.defineProperty(obj, 'unitsPerMeter', {
|
|
747
|
+
get() { return Number(utils.projectedUnitsPerMeter(obj.coordinates[1]).toFixed(7)); }
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
let _fixedZoom = null;
|
|
751
|
+
//[jscastro] added property to have a fixed scale for some objects
|
|
752
|
+
Object.defineProperty(obj, 'fixedZoom', {
|
|
753
|
+
get() { return obj.userData.fixedZoom; },
|
|
754
|
+
set(value) {
|
|
755
|
+
if (obj.userData.fixedZoom === value) return;
|
|
756
|
+
obj.userData.fixedZoom = value;
|
|
757
|
+
obj.userData.units = (value ? 'scene' : 'meters');
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
//[jscastro] sets the scale of an object based fixedZoom
|
|
762
|
+
obj.setFixedZoom = function (scale) {
|
|
763
|
+
if (obj.fixedZoom != null && obj.fixedZoom != 0) {
|
|
764
|
+
if (!scale) scale = obj.userData.mapScale;
|
|
765
|
+
let s = zoomScale(obj.fixedZoom);
|
|
766
|
+
if (s > scale) {
|
|
767
|
+
let calc = s / scale;
|
|
768
|
+
obj.scale.set(calc, calc, calc);
|
|
769
|
+
} else {
|
|
770
|
+
obj.scale.set(1, 1, 1);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
//[jscastro] sets the scale of an object based in the scale and fixedZoom
|
|
776
|
+
obj.setScale = function (scale) {
|
|
777
|
+
// scale the model so that its units are interpreted as meters at the given latitude
|
|
778
|
+
if (obj.userData.units != 'scene') {
|
|
779
|
+
let s = obj.unitsPerMeter;
|
|
780
|
+
obj.scale.set(s, s, s);
|
|
781
|
+
} else if (obj.fixedZoom) {
|
|
782
|
+
if (scale) obj.userData.mapScale = scale;
|
|
783
|
+
obj.setFixedZoom(obj.userData.mapScale); //apply fixed zoom
|
|
784
|
+
} else obj.scale.set(1, 1, 1);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function zoomScale(zoom) { return Math.pow(2, zoom); }
|
|
788
|
+
|
|
789
|
+
//[jscastro] sets the scale and shadows position of an object based in the scale
|
|
790
|
+
obj.setObjectScale = function (scale) {
|
|
791
|
+
obj.setScale(scale);
|
|
792
|
+
obj.setBoundingBoxShadowFloor();
|
|
793
|
+
obj.setReceiveShadowFloor();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
obj.add = function (o) {
|
|
799
|
+
obj.scaleGroup.add(o);
|
|
800
|
+
o.position.z = (obj.coordinates[2] ? -obj.coordinates[2] : 0);
|
|
801
|
+
return o;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
obj.remove = function (o) {
|
|
805
|
+
if (!o) return;
|
|
806
|
+
o.traverse(m => {
|
|
807
|
+
//console.log('dispose geometry!')
|
|
808
|
+
if (m.geometry) m.geometry.dispose();
|
|
809
|
+
if (m.material) {
|
|
810
|
+
if (m.material.isMaterial) {
|
|
811
|
+
cleanMaterial(m.material)
|
|
812
|
+
} else {
|
|
813
|
+
// an array of materials
|
|
814
|
+
for (const material of m.material) cleanMaterial(material)
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (m.dispose) m.dispose();
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
obj.scaleGroup.remove(o);
|
|
821
|
+
tb.map.repaint = true;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
//[jscastro] clone + assigning all the attributes
|
|
825
|
+
obj.duplicate = function (options) {
|
|
826
|
+
|
|
827
|
+
let dupe = obj.clone(true); //clone the whole threebox object
|
|
828
|
+
dupe.getObjectByName("model").animations = obj.animations; //we must set this explicitly before addMethods
|
|
829
|
+
if (dupe.userData.feature) {
|
|
830
|
+
if (options && options.feature) dupe.userData.feature = options.feature;
|
|
831
|
+
dupe.userData.feature.properties.uuid = dupe.uuid;
|
|
832
|
+
}
|
|
833
|
+
root._addMethods(dupe); // add methods
|
|
834
|
+
|
|
835
|
+
if (!options || utils.equal(options.scale, obj.userData.scale)) {
|
|
836
|
+
//no options, no changes, just return the same object
|
|
837
|
+
dupe.copyAnchor(obj); // copy anchors
|
|
838
|
+
//[jscastro] we add by default a tooltip that can be overriden later or hide it with threebox `enableTooltips`
|
|
839
|
+
return dupe;
|
|
840
|
+
} else {
|
|
841
|
+
dupe.userData = options;
|
|
842
|
+
dupe.userData.isGeoGroup = true;
|
|
843
|
+
dupe.remove(dupe.boxGroup);
|
|
844
|
+
// [jscastro] rotate and scale the model
|
|
845
|
+
const r = utils.types.rotation(options.rotation, [0, 0, 0]);
|
|
846
|
+
const s = utils.types.scale(options.scale, [1, 1, 1]);
|
|
847
|
+
// [jscastro] reposition to 0,0,0
|
|
848
|
+
dupe.model.position.set(0, 0, 0);
|
|
849
|
+
// rotate and scale
|
|
850
|
+
dupe.model.rotation.set(r[0], r[1], r[2]);
|
|
851
|
+
dupe.model.scale.set(s[0], s[1], s[2]);
|
|
852
|
+
//[jscastro] calculate automatically the pivotal center of the object
|
|
853
|
+
dupe.setAnchor(options.anchor);
|
|
854
|
+
//[jscastro] override the center calculated if the object has adjustments
|
|
855
|
+
dupe.setCenter(options.adjustment);
|
|
856
|
+
return dupe;
|
|
857
|
+
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
//[jscastro] copy anchor values
|
|
863
|
+
obj.copyAnchor = function (o) {
|
|
864
|
+
|
|
865
|
+
obj.anchor = o.anchor;
|
|
866
|
+
obj.none = { x: 0, y: 0, z: 0 };
|
|
867
|
+
obj.center = o.center;
|
|
868
|
+
obj.bottom = o.bottom;
|
|
869
|
+
obj.bottomLeft = o.bottomLeft;
|
|
870
|
+
obj.bottomRight = o.bottomRight;
|
|
871
|
+
obj.top = o.top;
|
|
872
|
+
obj.topLeft = o.topLeft;
|
|
873
|
+
obj.topRight = o.topRight;
|
|
874
|
+
obj.left = o.left;
|
|
875
|
+
obj.right = o.right;
|
|
876
|
+
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
obj.dispose = function () {
|
|
880
|
+
|
|
881
|
+
Objects.prototype.unenroll(obj);
|
|
882
|
+
|
|
883
|
+
obj.traverse(o => {
|
|
884
|
+
//don't dispose th object itself as it will be recursive
|
|
885
|
+
if (o.parent && o.parent.name == "world") return;
|
|
886
|
+
if (o.name === "threeboxObject") return;
|
|
887
|
+
|
|
888
|
+
//console.log('dispose geometry!')
|
|
889
|
+
if (o.geometry) o.geometry.dispose();
|
|
890
|
+
|
|
891
|
+
if (o.material) {
|
|
892
|
+
if (o.material.isMaterial) {
|
|
893
|
+
cleanMaterial(o.material)
|
|
894
|
+
} else {
|
|
895
|
+
// an array of materials
|
|
896
|
+
for (const material of o.material) cleanMaterial(material)
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (o.dispose) o.dispose();
|
|
900
|
+
|
|
901
|
+
})
|
|
902
|
+
|
|
903
|
+
obj.children = [];
|
|
904
|
+
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const cleanMaterial = material => {
|
|
908
|
+
//console.log('dispose material!')
|
|
909
|
+
material.dispose()
|
|
910
|
+
|
|
911
|
+
// dispose textures
|
|
912
|
+
for (const key of Object.keys(material)) {
|
|
913
|
+
const value = material[key]
|
|
914
|
+
if (value && typeof value === 'object' && 'minFilter' in value) {
|
|
915
|
+
//console.log('dispose texture!')
|
|
916
|
+
value.dispose()
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
let m = material;
|
|
920
|
+
let md = (m.map || m.alphaMap || m.aoMap || m.bumpMap || m.displacementMap || m.emissiveMap || m.envMap || m.lightMap || m.metalnessMap || m.normalMap || m.roughnessMap)
|
|
921
|
+
if (md) {
|
|
922
|
+
if (m.map) m.map.dispose();
|
|
923
|
+
if (m.alphaMap) m.alphaMap.dispose();
|
|
924
|
+
if (m.aoMap) m.aoMap.dispose();
|
|
925
|
+
if (m.bumpMap) m.bumpMap.dispose();
|
|
926
|
+
if (m.displacementMap) m.displacementMap.dispose();
|
|
927
|
+
if (m.emissiveMap) m.emissiveMap.dispose();
|
|
928
|
+
if (m.envMap) m.envMap.dispose();
|
|
929
|
+
if (m.lightMap) m.lightMap.dispose();
|
|
930
|
+
if (m.metalnessMap) m.metalnessMap.dispose();
|
|
931
|
+
if (m.normalMap) m.normalMap.dispose();
|
|
932
|
+
if (m.roughnessMap) m.roughnessMap.dispose();
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return obj
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
_makeGroup: function (obj, options) {
|
|
940
|
+
let projScaleGroup = new THREE.Group();
|
|
941
|
+
projScaleGroup.name = "scaleGroup";
|
|
942
|
+
projScaleGroup.add(obj)
|
|
943
|
+
|
|
944
|
+
var geoGroup = new THREE.Group();
|
|
945
|
+
geoGroup.userData = options || {};
|
|
946
|
+
geoGroup.userData.isGeoGroup = true;
|
|
947
|
+
if (geoGroup.userData.feature) {
|
|
948
|
+
geoGroup.userData.feature.properties.uuid = geoGroup.uuid;
|
|
949
|
+
}
|
|
950
|
+
var isArrayOfObjects = projScaleGroup.length;
|
|
951
|
+
if (isArrayOfObjects) for (o of projScaleGroup) geoGroup.add(o)
|
|
952
|
+
else geoGroup.add(projScaleGroup);
|
|
953
|
+
|
|
954
|
+
//utils._flipMaterialSides(projScaleGroup);
|
|
955
|
+
geoGroup.name = "threeboxObject";
|
|
956
|
+
|
|
957
|
+
return geoGroup
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
animationManager: new AnimationManager,
|
|
961
|
+
|
|
962
|
+
//[jscastro] add tooltip method
|
|
963
|
+
drawTooltip : function (tooltipText, mapboxStyle = false) {
|
|
964
|
+
if (tooltipText) {
|
|
965
|
+
let divToolTip;
|
|
966
|
+
if (mapboxStyle) {
|
|
967
|
+
let divContent = document.createElement('div');
|
|
968
|
+
divContent.className = 'mapboxgl-popup-content';
|
|
969
|
+
let strong = document.createElement('strong');
|
|
970
|
+
strong.innerHTML = tooltipText;
|
|
971
|
+
divContent.appendChild(strong);
|
|
972
|
+
let tip = document.createElement('div');
|
|
973
|
+
tip.className = 'mapboxgl-popup-tip';
|
|
974
|
+
let div = document.createElement('div');
|
|
975
|
+
div.className = 'marker mapboxgl-popup-anchor-bottom';
|
|
976
|
+
div.appendChild(tip);
|
|
977
|
+
div.appendChild(divContent);
|
|
978
|
+
divToolTip = document.createElement('div');
|
|
979
|
+
divToolTip.className += 'label3D';
|
|
980
|
+
divToolTip.appendChild(div);
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
divToolTip = document.createElement('span');
|
|
984
|
+
divToolTip.className = this._defaults.tooltip.cssClass;
|
|
985
|
+
divToolTip.innerHTML = tooltipText;
|
|
986
|
+
}
|
|
987
|
+
return divToolTip;
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
|
|
991
|
+
//[jscastro] draw label method can be invoked separately
|
|
992
|
+
drawLabelHTML: function (HTMLElement, cssClass) {
|
|
993
|
+
let div = document.createElement('div');
|
|
994
|
+
div.className += cssClass;
|
|
995
|
+
// [jscastro] create a div [TODO] analize if must be moved
|
|
996
|
+
if (typeof (HTMLElement) == 'string') {
|
|
997
|
+
div.innerHTML = HTMLElement;
|
|
998
|
+
} else {
|
|
999
|
+
div.innerHTML = HTMLElement.outerHTML;
|
|
1000
|
+
}
|
|
1001
|
+
return div;
|
|
1002
|
+
},
|
|
1003
|
+
|
|
1004
|
+
_defaults: {
|
|
1005
|
+
colors: {
|
|
1006
|
+
red: new THREE.Color(0xff0000),
|
|
1007
|
+
yellow: new THREE.Color(0xffff00),
|
|
1008
|
+
green: new THREE.Color(0x00ff00),
|
|
1009
|
+
black: new THREE.Color(0x000000)
|
|
1010
|
+
},
|
|
1011
|
+
|
|
1012
|
+
materials: {
|
|
1013
|
+
boxNormalMaterial: new THREE.LineBasicMaterial({ color: new THREE.Color(0xff0000) }),
|
|
1014
|
+
boxOverMaterial: new THREE.LineBasicMaterial({ color: new THREE.Color(0xffff00) }),
|
|
1015
|
+
boxSelectedMaterial: new THREE.LineBasicMaterial({ color: new THREE.Color(0x00ff00) })
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
line: {
|
|
1019
|
+
geometry: null,
|
|
1020
|
+
color: 'black',
|
|
1021
|
+
width: 1,
|
|
1022
|
+
opacity: 1
|
|
1023
|
+
},
|
|
1024
|
+
|
|
1025
|
+
label: {
|
|
1026
|
+
htmlElement: null,
|
|
1027
|
+
cssClass: " label3D",
|
|
1028
|
+
alwaysVisible: false,
|
|
1029
|
+
topMargin: -0.5
|
|
1030
|
+
},
|
|
1031
|
+
|
|
1032
|
+
tooltip: {
|
|
1033
|
+
text: '',
|
|
1034
|
+
cssClass: 'toolTip text-xs',
|
|
1035
|
+
mapboxStyle: false,
|
|
1036
|
+
topMargin: 0
|
|
1037
|
+
},
|
|
1038
|
+
|
|
1039
|
+
sphere: {
|
|
1040
|
+
position: [0, 0, 0],
|
|
1041
|
+
radius: 1,
|
|
1042
|
+
sides: 20,
|
|
1043
|
+
units: 'scene',
|
|
1044
|
+
material: 'MeshBasicMaterial',
|
|
1045
|
+
anchor: 'bottom-left',
|
|
1046
|
+
bbox: true,
|
|
1047
|
+
tooltip: true,
|
|
1048
|
+
raycasted: true
|
|
1049
|
+
|
|
1050
|
+
},
|
|
1051
|
+
|
|
1052
|
+
tube: {
|
|
1053
|
+
geometry: null,
|
|
1054
|
+
radius: 1,
|
|
1055
|
+
sides: 6,
|
|
1056
|
+
units: 'scene',
|
|
1057
|
+
material: 'MeshBasicMaterial',
|
|
1058
|
+
anchor: 'center',
|
|
1059
|
+
bbox: true,
|
|
1060
|
+
tooltip: true,
|
|
1061
|
+
raycasted: true
|
|
1062
|
+
},
|
|
1063
|
+
|
|
1064
|
+
loadObj: {
|
|
1065
|
+
type: null,
|
|
1066
|
+
obj: null,
|
|
1067
|
+
units: 'scene',
|
|
1068
|
+
scale: 1,
|
|
1069
|
+
rotation: 0,
|
|
1070
|
+
defaultAnimation: 0,
|
|
1071
|
+
anchor: 'bottom-left',
|
|
1072
|
+
bbox: true,
|
|
1073
|
+
tooltip: true,
|
|
1074
|
+
raycasted: true,
|
|
1075
|
+
clone: true,
|
|
1076
|
+
withCredentials: false
|
|
1077
|
+
},
|
|
1078
|
+
|
|
1079
|
+
Object3D: {
|
|
1080
|
+
obj: null,
|
|
1081
|
+
units: 'scene',
|
|
1082
|
+
anchor: 'bottom-left',
|
|
1083
|
+
bbox: true,
|
|
1084
|
+
tooltip: true,
|
|
1085
|
+
raycasted: true
|
|
1086
|
+
},
|
|
1087
|
+
|
|
1088
|
+
extrusion: {
|
|
1089
|
+
coordinates: [[[]]],
|
|
1090
|
+
geometryOptions: {},
|
|
1091
|
+
height: 100,
|
|
1092
|
+
materials: new THREE.MeshPhongMaterial({ color: 0x660000, side: THREE.DoubleSide }),
|
|
1093
|
+
scale: 1,
|
|
1094
|
+
rotation: 0,
|
|
1095
|
+
units: 'scene',
|
|
1096
|
+
anchor: 'center',
|
|
1097
|
+
bbox: true,
|
|
1098
|
+
tooltip: true,
|
|
1099
|
+
raycasted: true
|
|
1100
|
+
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
1103
|
+
|
|
1104
|
+
geometries: {
|
|
1105
|
+
line: ['LineString'],
|
|
1106
|
+
tube: ['LineString'],
|
|
1107
|
+
sphere: ['Point'],
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export default Objects;
|