From 8c70795045663f337821e75aaee9eeba4fe3098e Mon Sep 17 00:00:00 2001 From: Jesus Serrano Date: Mon, 11 Jan 2021 15:39:13 +0100 Subject: [PATCH] - #167 Create a new event `ObjectChanged` every time the object has modified its position, rotation or scale. - Related to #163 request to get coordinates when the model follow path with line? - Modified example [05-logistics.html](https://github.com/jscastro76/threebox/blob/master/examples/05-logistics.html) to attach to the event `ObjectChanged` - Modified example [11-logistics.html](https://github.com/jscastro76/threebox/blob/master/examples/11-animation.html) to attach to the event `ObjectChanged` - Modified example [08-3dbuildings.html](https://github.com/jscastro76/threebox/blob/master/examples/08-3dbuildings.html) to adjust perspective and position to a more relevant zone (empire state building) --- CHANGELOG.md | 20 +++++++++++ LICENSE.txt | 2 +- dist/threebox.js | 56 +++++++++++++++++++++++++++---- dist/threebox.min.js | 6 ++-- docs/Threebox.md | 53 ++++++++++++++++++++++++----- examples/05-logistics.html | 6 ++++ examples/08-3dbuildings.html | 2 +- examples/11-animation.html | 20 +++++++---- examples/README.md | 1 + package.json | 3 +- src/Threebox.js | 2 +- src/animation/AnimationManager.js | 23 ++++++++++--- src/utils/utils.js | 31 ++++++++++++++++- threebox-new.njsproj | 4 ++- 14 files changed, 193 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85301147..a3392639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 2.1.7 + +Minor version by [@jscastro76](https://github.com/jscastro76), some enhancements and bugs. + +#### :sparkles: Enhancements +- #167 Create a new event `ObjectChanged` every time the object has modified its position, rotation or scale. + - Related to #163 request to get coordinates when the model follow path with line? + - Modified example [05-logistics.html](https://github.com/jscastro76/threebox/blob/master/examples/05-logistics.html) to attach to the event `ObjectChanged` + - Modified example [11-logistics.html](https://github.com/jscastro76/threebox/blob/master/examples/11-animation.html) to attach to the event `ObjectChanged` + - Modified example [08-3dbuildings.html](https://github.com/jscastro76/threebox/blob/master/examples/08-3dbuildings.html) to adjust perspective and position to a more relevant zone (empire state building) + +#### :pencil: Documentation +- Updated [documentation](/docs/Threebox.md) (`ObjectChanged` and other events) +- Updated [README.md](/). +- Updated [Examples](/examples) documentation. + +
+ +- - - + ## 2.1.6 Minor version by [@jscastro76](https://github.com/jscastro76), some enhancements and bugs. diff --git a/LICENSE.txt b/LICENSE.txt index 325b219b..23e9d454 100755 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -v.2.0.1 - v.2.1.6 +v.2.0.1 - v.2.1.7 MIT License Copyright (c) 2020 Jesus Serrano diff --git a/dist/threebox.js b/dist/threebox.js index 66038d38..e749504d 100755 --- a/dist/threebox.js +++ b/dist/threebox.js @@ -1024,7 +1024,7 @@ Threebox.prototype = { programs: function () { return this.renderer.info.programs.length }, - version: '2.1.6', + version: '2.1.7', } @@ -1257,22 +1257,35 @@ AnimationManager.prototype = { this.translateX(c.x); this.translateY(c.y); this.translateZ(c.z); + options.position = this.coordinates; } - if (r) this.rotation.set(r[0], r[1], r[2]); + if (r) { + this.rotation.set(r[0], r[1], r[2]); + options.rotation = new THREE.Vector3(r[0], r[1], r[2]); + } - if (s) this.scale.set(s[0], s[1], s[2]); + if (s) { + this.scale.set(s[0], s[1], s[2]); + options.scale = this.scale; + } - if (q) this.quaternion.setFromAxisAngle(q[0], q[1]); + if (q) { + this.quaternion.setFromAxisAngle(q[0], q[1]); + options.rotation = q[0].multiplyScalar(q[1]); + } if (w) { this.position.copy(w); let p = utils.unprojectFromWorld(w); - this.coordinates = p; + this.coordinates = options.position = p; } this.updateMatrixWorld(); - tb.map.repaint = true + tb.map.repaint = true; + // fire the ObjectChanged event to notify UI object change + this.dispatchEvent(new CustomEvent('ObjectChanged', { detail: { object: this, action: { position: options.position, rotation: options.rotation, scale: options.scale } }, bubbles: true, cancelable: true })); + }; //[jscastro] play default animation @@ -19332,7 +19345,6 @@ var utils = { }, // retrieve object parameters from an options object - types: { rotation: function (r, currentRotation) { @@ -19402,6 +19414,36 @@ var utils = { return object != null && typeof object === 'object'; }, + curveToLine: (curve, params) => { + let { width, color } = params; + let geometry = new THREE.BufferGeometry().setFromPoints( + curve.getPoints(100) + ); + + let material = new THREE.LineBasicMaterial({ + color: color, + linewidth: width, + }); + + let line = new THREE.Line(geometry, material); + + return line; + }, + + curvesToLines: (curves) => { + var colors = [0xff0000, 0x1eff00, 0x2600ff]; + var lines = curves.map((curve, i) => { + let params = { + width: 3, + color: colors[i] || 'purple', + }; + let curveline = curveToLine(curve, params); + + return curveline; + }); + return lines; + }, + _validate: function (userInputs, defaults) { userInputs = userInputs || {}; diff --git a/dist/threebox.min.js b/dist/threebox.min.js index 11ee0c9b..fc072a3f 100644 --- a/dist/threebox.min.js +++ b/dist/threebox.min.js @@ -2,10 +2,10 @@ window.Threebox=require("./src/Threebox.js"),window.THREE=require("./src/three.js"); },{"./src/Threebox.js":2,"./src/three.js":23}],2:[function(require,module,exports){ -const THREE=require("./three.js"),CameraSync=require("./camera/CameraSync.js"),utils=require("./utils/utils.js"),SunCalc=require("./utils/suncalc.js"),ThreeboxConstants=require("./utils/constants.js"),Objects=require("./objects/objects.js"),material=require("./utils/material.js"),sphere=require("./objects/sphere.js"),extrusion=require("./objects/extrusion.js"),label=require("./objects/label.js"),tooltip=require("./objects/tooltip.js"),loader=require("./objects/loadObj.js"),Object3D=require("./objects/Object3D.js"),line=require("./objects/line.js"),tube=require("./objects/tube.js"),LabelRenderer=require("./objects/LabelRenderer.js"),BuildingShadows=require("./objects/effects/BuildingShadows.js");function Threebox(e,t,i){this.init(e,t,i)}Threebox.prototype={repaint:function(){this.map.repaint=!0},init:function(e,t,i){this.options=utils._validate(i||{},defaultOptions),this.map=e,this.map.tb=this,this.objects=new Objects,this.renderer=new THREE.WebGLRenderer({alpha:!0,antialias:!0,canvas:e.getCanvas(),context:t}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(this.map.getCanvas().clientWidth,this.map.getCanvas().clientHeight),this.renderer.outputEncoding=THREE.sRGBEncoding,this.renderer.autoClear=!1,this.labelRenderer=new LabelRenderer(this.map),this.scene=new THREE.Scene,this.world=new THREE.Group,this.world.name="world",this.scene.add(this.world),this.objectsCache=new Map,this.zoomLayers=[],this.fov=this.options.fov,this.orthographic=this.options.orthographic||!1,this.raycaster=new THREE.Raycaster,this.raycaster.layers.set(0),this.mapCenter=this.map.getCenter(),this.mapCenterUnits=utils.projectToWorld([this.mapCenter.lng,this.mapCenter.lat]),this.lightDateTime=new Date,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition,this.rotationStep=5,this.gridStep=6,this.altitudeStep=.1,this.lights=this.initLights,this.options.defaultLights&&this.defaultLights(),this.options.realSunlight&&this.realSunlight(this.options.realSunlightHelper),this.enableSelectingFeatures=this.options.enableSelectingFeatures||!1,this.enableSelectingObjects=this.options.enableSelectingObjects||!1,this.enableDraggingObjects=this.options.enableDraggingObjects||!1,this.enableRotatingObjects=this.options.enableRotatingObjects||!1,this.enableTooltips=this.options.enableTooltips||!1,this.multiLayer=this.options.multiLayer||!1,this.map.on("style.load",function(){this.tb.zoomLayers=[],this.tb.options.multiLayer&&this.addLayer({id:"threebox_layer",type:"custom",renderingMode:"3d",map:this,onAdd:function(e,t){},render:function(e,t){this.map.tb.update()}})}),this.map.on("load",function(){let t;this.selectedObject,this.selectedFeature,this.draggedObject,this.overedObject,this.overedFeature;let i,s=this.getCanvasContainer();this.getCanvasContainer().style.cursor="default";let r,o,n,a,h=[];function l(e){var t=s.getBoundingClientRect();return{x:e.originalEvent.clientX-t.left-s.clientLeft,y:e.originalEvent.clientY-t.top-s.clientTop}}this.unselectObject=function(e){e.selected=!1,this.selectedObject=null},this.unselectFeature=function(e){void 0!==e.id&&(this.setFeatureState({source:e.source,sourceLayer:e.sourceLayer,id:e.id},{select:!1}),this.removeTooltip(e),(e=this.queryRenderedFeatures({layers:[e.layer.id],filter:["==",["id"],e.id]})[0])&&this.fire("SelectedFeatureChange",{detail:e}),this.selectedFeature=null)},this.selectFeature=function(e){this.selectedFeature=e,this.setFeatureState({source:this.selectedFeature.source,sourceLayer:this.selectedFeature.sourceLayer,id:this.selectedFeature.id},{select:!0}),this.selectedFeature=this.queryRenderedFeatures({layers:[this.selectedFeature.layer.id],filter:["==",["id"],this.selectedFeature.id]})[0],this.addTooltip(this.selectedFeature),this.fire("SelectedFeatureChange",{detail:this.selectedFeature})},this.unoverFeature=function(t){this.overedFeature&&void 0!==this.overedFeature&&this.overedFeature.id!=t&&(e.setFeatureState({source:this.overedFeature.source,sourceLayer:this.overedFeature.sourceLayer,id:this.overedFeature.id},{hover:!1}),this.removeTooltip(this.overedFeature),this.overedFeature=null)},this.addTooltip=function(e){if(!this.tb.enableTooltips)return;let t=this.tb.getFeatureCenter(e),i=this.tb.tooltip({text:e.properties.name||e.id||e.type,mapboxStyle:!0,feature:e});i.setCoords(t),this.tb.add(i,e.layer.id),e.tooltip=i,e.tooltip.tooltip.visible=!0},this.removeTooltip=function(e){e.tooltip&&(e.tooltip.visibility=!1,this.tb.remove(e.tooltip),e.tooltip=null)},e.onContextMenu=function(e){alert("contextMenu")},this.onClick=function(t){let i,s=[];if(e.tb.enableSelectingObjects&&(s=this.tb.queryRenderedFeatures(t.point)),i="object"==typeof s[0]){let e=Threebox.prototype.findParent3DObject(s[0]);if(e){if(this.selectedFeature&&this.unselectFeature(this.selectedFeature),this.selectedObject){if(this.selectedObject.uuid!=e.uuid)this.selectedObject.selected=!1,e.selected=!0,this.selectedObject=e;else if(this.selectedObject.uuid==e.uuid)return void this.unselectObject(this.selectedObject)}else this.selectedObject=e,this.selectedObject.selected=!0;this.selectedObject.dispatchEvent(new CustomEvent("Wireframed",{detail:this.selectedObject,bubbles:!0,cancelable:!0})),this.selectedObject.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:this.selectedObject,bubbles:!0,cancelable:!0})),this.repaint=!0,t.preventDefault()}}else{let i=[];if(e.tb.enableSelectingFeatures&&(i=this.queryRenderedFeatures(t.point)),i.length>0&&"fill-extrusion"==i[0].layer.type&&void 0!==i[0].id)if(this.selectedObject&&this.unselectObject(this.selectedObject),this.selectedFeature){if(this.selectedFeature.id!=i[0].id)this.unselectFeature(this.selectedFeature),this.selectFeature(i[0]);else if(this.selectedFeature.id==i[0].id)return void this.unselectFeature(this.selectedFeature)}else this.selectFeature(i[0])}},this.onMouseMove=function(s){let h,d=l(s);if(this.getCanvasContainer().style.cursor="default",s.originalEvent.altKey&&this.draggedObject){if(!e.tb.enableRotatingObjects)return;t="rotate",this.getCanvasContainer().style.cursor="move";Math.min(i.x,d.x),Math.max(i.x,d.x),Math.min(i.y,d.y),Math.max(i.y,d.y);let s={x:0,y:0,z:Math.round(a[2]+~~((d.x-i.x)/this.tb.rotationStep)%360*this.tb.rotationStep%360)};return this.draggedObject.setRotation(s),void this.draggedObject.addHelp("rot: "+s.z+"°")}if(s.originalEvent.shiftKey&&this.draggedObject){if(!e.tb.enableDraggingObjects)return;t="translate",this.getCanvasContainer().style.cursor="move";let i=s.lngLat,n=[Number((i.lng+r).toFixed(this.tb.gridStep)),Number((i.lat+o).toFixed(this.tb.gridStep)),this.draggedObject.modelHeight];return this.draggedObject.setCoords(n),void this.draggedObject.addHelp("lng: "+n[0]+"°, lat: "+n[1]+"°")}if(s.originalEvent.ctrlKey&&this.draggedObject){if(!e.tb.enableDraggingObjects)return;t="altitude",this.getCanvasContainer().style.cursor="move";let i=s.point.y*this.tb.altitudeStep,r=[this.draggedObject.coordinates[0],this.draggedObject.coordinates[1],Number((-i-n).toFixed(this.tb.gridStep))];return this.draggedObject.setCoords(r),void this.draggedObject.addHelp("alt: "+r[2]+"m")}let u=[];if(e.tb.enableSelectingObjects&&(u=this.tb.queryRenderedFeatures(s.point)),h="object"==typeof u[0]){let e=Threebox.prototype.findParent3DObject(u[0]);e&&(this.unoverFeature(this.overedFeature),this.getCanvasContainer().style.cursor="pointer",this.selectedObject&&e.uuid==this.selectedObject.uuid||(this.overedObject&&(this.overedObject.over=!1,this.overedObject=null),e.over=!0,this.overedObject=e),this.repaint=!0,s.preventDefault())}else{this.overedObject&&(this.overedObject.over=!1,this.overedObject=null);let t=[];e.tb.enableSelectingFeatures&&(t=this.queryRenderedFeatures(s.point)),t.length>0&&(this.unoverFeature(t[0]),"fill-extrusion"==t[0].layer.type&&void 0!==t[0].id&&(this.selectedFeature&&this.selectedFeature.id==t[0].id||(this.getCanvasContainer().style.cursor="pointer",this.overedFeature=t[0],this.setFeatureState({source:this.overedFeature.source,sourceLayer:this.overedFeature.sourceLayer,id:this.overedFeature.id},{hover:!0}),this.overedFeature=e.queryRenderedFeatures({layers:[this.overedFeature.layer.id],filter:["==",["id"],this.overedFeature.id]})[0],this.addTooltip(this.overedFeature))))}},this.onMouseDown=function(t){(t.originalEvent.shiftKey||t.originalEvent.altKey||t.originalEvent.ctrlKey)&&0===t.originalEvent.button&&this.selectedObject&&(e.tb.enableDraggingObjects||e.tb.enableRotatingObjects)&&(t.preventDefault(),e.getCanvasContainer().style.cursor="move",e.once("mouseup",this.onMouseUp),this.draggedObject=this.selectedObject,i=l(t),h=this.draggedObject.coordinates,a=utils.degreeify(this.draggedObject.rotation),r=h[0]-t.lngLat.lng,o=h[1]-t.lngLat.lat,n=-this.draggedObject.modelHeight-t.point.y*this.tb.altitudeStep)},this.onMouseUp=function(e){this.getCanvasContainer().style.cursor="default",this.off("mouseup",this.onMouseUp),this.off("mouseout",this.onMouseUp),this.dragPan.enable(),this.draggedObject&&(this.draggedObject.dispatchEvent(new CustomEvent("ObjectDragged",{detail:{draggedObject:this.draggedObject,draggedAction:t},bubbles:!0,cancelable:!0})),this.draggedObject.removeHelp(),this.draggedObject=null,t=null)},this.onMouseOut=function(e){if(this.overedFeature){let t=this.queryRenderedFeatures(e.point);t.length>0&&this.overedFeature.id!=t[0].id&&(this.getCanvasContainer().style.cursor="default",this.unoverFeature(t[0]))}},this.onZoomEnd=function(e){this.tb.zoomLayers.forEach(e=>{this.tb.toggleLayer(e)})};let d=!1,u=!1,c=17,g=91,p=16,b=83;dK=68,this.on("click",this.onClick),this.on("mousemove",this.onMouseMove),this.on("mouseout",this.onMouseOut),this.on("mousedown",this.onMouseDown),this.on("zoom",this.onZoomEnd),document.addEventListener("keydown",function(e){e.which!==c&&e.which!==g||(d=!0),e.which===p&&(u=!0);let t=this.selectedObject;if(u&&e.which===b&&t){let e=utils.toDecimal;if(t.help)t.removeHelp();else{let i=t.modelSize,s=1;"meters"!==t.userData.units&&((s=utils.projectedUnitsPerMeter(t.coordinates[1]))||(s=1),s=e(s,7)),t.addHelp("size(m): "+e(i.x/s,3)+" W, "+e(i.y/s,3)+" L, "+e(i.z/s,3)+" H"),this.repaint=!0}return!1}}.bind(this),!0),document.addEventListener("keyup",function(e){e.which!=c&&e.which!=g||(d=!1),e.which===p&&(u=!1)}.bind(this))})},get fov(){return this.options.fov},set fov(e){this.camera instanceof THREE.PerspectiveCamera&&this.options.fov!==e&&(this.map.transform.fov=e,this.camera.fov=this.map.transform.fov,this.cameraSync.setupCamera(),this.map.repaint=!0,this.options.fov=e)},get orthographic(){return this.options.orthographic},set orthographic(e){const t=this.map.getCanvas().clientHeight,i=this.map.getCanvas().clientWidth;e?(this.map.transform.fov=0,this.camera=new THREE.OrthographicCamera(i/-2,i/2,t/2,t/-2,1,1e21)):(this.map.transform.fov=this.fov,this.camera=new THREE.PerspectiveCamera(this.map.transform.fov,i/t,1,1e21)),this.camera.layers.enable(0),this.camera.layers.enable(1),this.cameraSync=new CameraSync(this.map,this.camera,this.world),this.map.repaint=!0,this.options.orthographic=e},sphere:function(e){return this.setDefaultView(e,this.options),sphere(e,this.world)},line:line,label:label,tooltip:tooltip,tube:function(e){return this.setDefaultView(e,this.options),tube(e,this.world)},extrusion:function(e){return this.setDefaultView(e,this.options),extrusion(e)},Object3D:function(e){return this.setDefaultView(e,this.options),Object3D(e)},loadObj:async function(e,t){this.setDefaultView(e,this.options);let i=this.objectsCache.get(e.obj);i?i.promise.then(i=>{t(i.duplicate(e))}).catch(t=>{this.objectsCache.delete(e.obj),console.error("Could not load model file: "+e.obj)}):this.objectsCache.set(e.obj,{promise:new Promise(async(i,s)=>{loader(e,t,async e=>{e.duplicate?i(e.duplicate()):s(e)})})})},material:function(e){return material(e)},initLights:{ambientLight:null,dirLight:null,dirLightBack:null,dirLightHelper:null,hemiLight:null,pointLight:null},utils:utils,SunCalc:SunCalc,Constants:ThreeboxConstants,projectToWorld:function(e){return this.utils.projectToWorld(e)},unprojectFromWorld:function(e){return this.utils.unprojectFromWorld(e)},projectedUnitsPerMeter:function(e){return this.utils.projectedUnitsPerMeter(e)},getFeatureCenter:function(e,t,i){return utils.getFeatureCenter(e,t,i)},getObjectHeightOnFloor:function(e,t,i){return utils.getObjectHeightOnFloor(e,t,i)},queryRenderedFeatures:function(e){let t=new THREE.Vector2;return t.x=e.x/this.map.transform.width*2-1,t.y=1-e.y/this.map.transform.height*2,this.raycaster.setFromCamera(t,this.camera),this.raycaster.intersectObjects(this.world.children,!0)},findParent3DObject:function(e){var t;return e.object.traverseAncestors(function(e){e.parent&&"Group"==e.parent.type&&e.userData.obj&&(t=e)}),t},setLayoutProperty:function(e,t,i){this.map.setLayoutProperty(e,t,i),null==i||"visibility"!==t||this.world.children.forEach(function(t){t.layer===e&&(t.visibility=i)})},setLayerZoomRange:function(e,t,i){this.map.getLayer(e)&&(this.map.setLayerZoomRange(e,t,i),this.zoomLayers.includes(e)||this.zoomLayers.push(e),this.toggleLayer(e))},setLayerHeigthProperty:function(e,t){let i=this.map.getLayer(e);if(i)if("fill-extrusion"==i.type){let e=this.map.getStyle().sources[i.source].data;e.features.forEach(function(e){e.properties.level=t}),this.map.getSource(i.source).setData(e)}else"custom"==i.type&&this.world.children.forEach(function(i){let s=i.userData.feature;if(s&&s.layer===e){let e=this.tb.getFeatureCenter(s,i,t);i.setCoords(e)}})},setStyle:function(e,t){this.clear().then(()=>{this.map.setStyle(e,t)})},toggleLayer:function(e,t=!0){let i=this.map.getLayer(e);if(i){if(!t)return void this.toggle(i.id,!1);let e=this.map.getZoom();if(i.minzoom&&e=i.maxzoom)return void this.toggle(i.id,!1);this.toggle(i.id,!0)}},toggle:function(e,t){this.setLayoutProperty(e,"visibility",t?"visible":"none"),this.labelRenderer.toggleLabels(e,t)},update:function(){this.map.repaint&&(this.map.repaint=!1);var e=Date.now();this.objects.animationManager.update(e),this.updateLightHelper(),this.renderer.state.reset(),this.renderer.render(this.scene,this.camera),this.labelRenderer.render(this.scene,this.camera),!1===this.options.passiveRendering&&this.map.triggerRepaint()},add:function(e,t,i){if(!this.enableTooltips&&e.tooltip&&(e.tooltip.visibility=!1),this.world.add(e),t){e.layer=t,e.source=i;let s=this.map.getLayer(t);if(s){let t=s.visibility,i=void 0===t;e.visibility=!(!i&&"visible"!==t)}}},remove:function(e){this.map.selectedObject&&e.uuid==this.map.selectedObject.uuid&&this.map.unselectObject(this.map.selectedObject),this.map.draggedObject&&e.uuid==this.map.draggedObject.uuid&&(this.map.draggedObject=null),e.dispose&&e.dispose(),this.world.remove(e),e=null},clear:async function(e=null,t=!1){return new Promise((i,s)=>{let r=[];this.world.children.forEach(function(e){r.push(e)});for(let t=0;t{e.promise.then(e=>{e.dispose(),e=null})}),i("clear")})},removeLayer:function(e){this.clear(e,!0).then(()=>{this.map.removeLayer(e)})},getSunPosition:function(e,t){return SunCalc.getPosition(e,t[1],t[0])},getSunTimes:function(e,t){return SunCalc.getTimes(e,t[1],t[0],t[2]?t[2]:0)},setBuildingShadows:function(e){if(this.map.getLayer(e.buildingsLayerId)){let t=new BuildingShadows(e,this);this.map.addLayer(t,e.buildingsLayerId)}else console.warn("The layer '"+e.buildingsLayerId+"' does not exist in the map.")},setSunlight:function(e=new Date,t){if(!this.lights.dirLight||!this.options.realSunlight)return void console.warn("To use setSunlight it's required to set realSunlight : true in Threebox initial options.");var i=new Date(e.getTime());if(t?t.lng&&t.lat?this.mapCenter=t:this.mapCenter={lng:t[0],lat:t[1]}:this.mapCenter=this.map.getCenter(),this.lightDateTime&&this.lightDateTime.getTime()===i.getTime()&&this.lightLng===this.mapCenter.lng&&this.lightLat===this.mapCenter.lat)return;this.lightDateTime=i,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition=this.getSunPosition(i,[this.mapCenter.lng,this.mapCenter.lat]);let s=this.sunPosition.altitude,r=Math.PI+this.sunPosition.azimuth,o=ThreeboxConstants.WORLD_SIZE/2,n=Math.sin(s),a=Math.cos(s),h=Math.cos(r)*a,l=Math.sin(r)*a;this.lights.dirLight.position.set(l,h,n),this.lights.dirLight.position.multiplyScalar(o),this.lights.dirLight.intensity=Math.max(n,-.15),this.lights.dirLight.updateMatrixWorld(),this.updateLightHelper(),this.map.loaded()&&this.map.setLight({anchor:"map",position:[1.5,180+180*this.sunPosition.azimuth/Math.PI,90-180*this.sunPosition.altitude/Math.PI],"position-transition":{duration:0},color:`hsl(40, ${50*Math.cos(this.sunPosition.altitude)}%, ${96*Math.sin(this.sunPosition.altitude)}%)`},{duration:0})},updateLightHelper:function(){this.lights.dirLightHelper&&(this.lights.dirLightHelper.position.setFromMatrixPosition(this.lights.dirLight.matrixWorld),this.lights.dirLightHelper.updateMatrix(),this.lights.dirLightHelper.update())},dispose:async function(){return console.log(this.memory()),new Promise(e=>{e(this.clear(null,!0).then(e=>(this.map.remove(),this.map={},this.scene.remove(this.world),this.scene.dispose(),this.world.children=[],this.world=null,this.objectsCache.clear(),this.labelRenderer.dispose(),console.log(this.memory()),this.renderer.dispose(),e)))})},defaultLights:function(){this.lights.ambientLight=new THREE.AmbientLight(new THREE.Color("hsl(0, 0%, 100%)"),.75),this.scene.add(this.lights.ambientLight),this.lights.dirLightBack=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLightBack.position.set(30,100,100),this.scene.add(this.lights.dirLightBack),this.lights.dirLight=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLight.position.set(-30,100,-100),this.scene.add(this.lights.dirLight)},realSunlight:function(e=!1){this.renderer.shadowMap.enabled=!0,this.lights.dirLight=new THREE.DirectionalLight(16777215,1),this.scene.add(this.lights.dirLight),e&&(this.lights.dirLightHelper=new THREE.DirectionalLightHelper(this.lights.dirLight,5),this.scene.add(this.lights.dirLightHelper));this.lights.dirLight.castShadow=!0,this.lights.dirLight.shadow.radius=2,this.lights.dirLight.shadow.mapSize.width=8192,this.lights.dirLight.shadow.mapSize.height=8192,this.lights.dirLight.shadow.camera.top=this.lights.dirLight.shadow.camera.right=1e3,this.lights.dirLight.shadow.camera.bottom=this.lights.dirLight.shadow.camera.left=-1e3,this.lights.dirLight.shadow.camera.near=1,this.lights.dirLight.shadow.camera.visible=!0,this.lights.dirLight.shadow.camera.far=4e8,this.lights.hemiLight=new THREE.HemisphereLight(new THREE.Color(16777215),new THREE.Color(16777215),.6),this.lights.hemiLight.color.setHSL(.661,.96,.12),this.lights.hemiLight.groundColor.setHSL(.11,.96,.14),this.lights.hemiLight.position.set(0,0,50),this.scene.add(this.lights.hemiLight),this.setSunlight()},setDefaultView:function(e,t){e.bbox=e.bbox||t.enableSelectingObjects,e.tooltip=e.tooltip||t.enableTooltips},memory:function(){return this.renderer.info.memory},programs:function(){return this.renderer.info.programs.length},version:"2.1.6"};var defaultOptions={defaultLights:!1,realSunlight:!1,realSunlightHelper:!1,passiveRendering:!0,enableSelectingFeatures:!1,enableSelectingObjects:!1,enableDraggingObjects:!1,enableRotatingObjects:!1,enableTooltips:!1,multiLayer:!1,orthographic:!1,fov:ThreeboxConstants.FOV_DEGREES};module.exports=exports=Threebox; +const THREE=require("./three.js"),CameraSync=require("./camera/CameraSync.js"),utils=require("./utils/utils.js"),SunCalc=require("./utils/suncalc.js"),ThreeboxConstants=require("./utils/constants.js"),Objects=require("./objects/objects.js"),material=require("./utils/material.js"),sphere=require("./objects/sphere.js"),extrusion=require("./objects/extrusion.js"),label=require("./objects/label.js"),tooltip=require("./objects/tooltip.js"),loader=require("./objects/loadObj.js"),Object3D=require("./objects/Object3D.js"),line=require("./objects/line.js"),tube=require("./objects/tube.js"),LabelRenderer=require("./objects/LabelRenderer.js"),BuildingShadows=require("./objects/effects/BuildingShadows.js");function Threebox(e,t,i){this.init(e,t,i)}Threebox.prototype={repaint:function(){this.map.repaint=!0},init:function(e,t,i){this.options=utils._validate(i||{},defaultOptions),this.map=e,this.map.tb=this,this.objects=new Objects,this.renderer=new THREE.WebGLRenderer({alpha:!0,antialias:!0,canvas:e.getCanvas(),context:t}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(this.map.getCanvas().clientWidth,this.map.getCanvas().clientHeight),this.renderer.outputEncoding=THREE.sRGBEncoding,this.renderer.autoClear=!1,this.labelRenderer=new LabelRenderer(this.map),this.scene=new THREE.Scene,this.world=new THREE.Group,this.world.name="world",this.scene.add(this.world),this.objectsCache=new Map,this.zoomLayers=[],this.fov=this.options.fov,this.orthographic=this.options.orthographic||!1,this.raycaster=new THREE.Raycaster,this.raycaster.layers.set(0),this.mapCenter=this.map.getCenter(),this.mapCenterUnits=utils.projectToWorld([this.mapCenter.lng,this.mapCenter.lat]),this.lightDateTime=new Date,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition,this.rotationStep=5,this.gridStep=6,this.altitudeStep=.1,this.lights=this.initLights,this.options.defaultLights&&this.defaultLights(),this.options.realSunlight&&this.realSunlight(this.options.realSunlightHelper),this.enableSelectingFeatures=this.options.enableSelectingFeatures||!1,this.enableSelectingObjects=this.options.enableSelectingObjects||!1,this.enableDraggingObjects=this.options.enableDraggingObjects||!1,this.enableRotatingObjects=this.options.enableRotatingObjects||!1,this.enableTooltips=this.options.enableTooltips||!1,this.multiLayer=this.options.multiLayer||!1,this.map.on("style.load",function(){this.tb.zoomLayers=[],this.tb.options.multiLayer&&this.addLayer({id:"threebox_layer",type:"custom",renderingMode:"3d",map:this,onAdd:function(e,t){},render:function(e,t){this.map.tb.update()}})}),this.map.on("load",function(){let t;this.selectedObject,this.selectedFeature,this.draggedObject,this.overedObject,this.overedFeature;let i,s=this.getCanvasContainer();this.getCanvasContainer().style.cursor="default";let r,o,n,a,h=[];function l(e){var t=s.getBoundingClientRect();return{x:e.originalEvent.clientX-t.left-s.clientLeft,y:e.originalEvent.clientY-t.top-s.clientTop}}this.unselectObject=function(e){e.selected=!1,this.selectedObject=null},this.unselectFeature=function(e){void 0!==e.id&&(this.setFeatureState({source:e.source,sourceLayer:e.sourceLayer,id:e.id},{select:!1}),this.removeTooltip(e),(e=this.queryRenderedFeatures({layers:[e.layer.id],filter:["==",["id"],e.id]})[0])&&this.fire("SelectedFeatureChange",{detail:e}),this.selectedFeature=null)},this.selectFeature=function(e){this.selectedFeature=e,this.setFeatureState({source:this.selectedFeature.source,sourceLayer:this.selectedFeature.sourceLayer,id:this.selectedFeature.id},{select:!0}),this.selectedFeature=this.queryRenderedFeatures({layers:[this.selectedFeature.layer.id],filter:["==",["id"],this.selectedFeature.id]})[0],this.addTooltip(this.selectedFeature),this.fire("SelectedFeatureChange",{detail:this.selectedFeature})},this.unoverFeature=function(t){this.overedFeature&&void 0!==this.overedFeature&&this.overedFeature.id!=t&&(e.setFeatureState({source:this.overedFeature.source,sourceLayer:this.overedFeature.sourceLayer,id:this.overedFeature.id},{hover:!1}),this.removeTooltip(this.overedFeature),this.overedFeature=null)},this.addTooltip=function(e){if(!this.tb.enableTooltips)return;let t=this.tb.getFeatureCenter(e),i=this.tb.tooltip({text:e.properties.name||e.id||e.type,mapboxStyle:!0,feature:e});i.setCoords(t),this.tb.add(i,e.layer.id),e.tooltip=i,e.tooltip.tooltip.visible=!0},this.removeTooltip=function(e){e.tooltip&&(e.tooltip.visibility=!1,this.tb.remove(e.tooltip),e.tooltip=null)},e.onContextMenu=function(e){alert("contextMenu")},this.onClick=function(t){let i,s=[];if(e.tb.enableSelectingObjects&&(s=this.tb.queryRenderedFeatures(t.point)),i="object"==typeof s[0]){let e=Threebox.prototype.findParent3DObject(s[0]);if(e){if(this.selectedFeature&&this.unselectFeature(this.selectedFeature),this.selectedObject){if(this.selectedObject.uuid!=e.uuid)this.selectedObject.selected=!1,e.selected=!0,this.selectedObject=e;else if(this.selectedObject.uuid==e.uuid)return void this.unselectObject(this.selectedObject)}else this.selectedObject=e,this.selectedObject.selected=!0;this.selectedObject.dispatchEvent(new CustomEvent("Wireframed",{detail:this.selectedObject,bubbles:!0,cancelable:!0})),this.selectedObject.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:this.selectedObject,bubbles:!0,cancelable:!0})),this.repaint=!0,t.preventDefault()}}else{let i=[];if(e.tb.enableSelectingFeatures&&(i=this.queryRenderedFeatures(t.point)),i.length>0&&"fill-extrusion"==i[0].layer.type&&void 0!==i[0].id)if(this.selectedObject&&this.unselectObject(this.selectedObject),this.selectedFeature){if(this.selectedFeature.id!=i[0].id)this.unselectFeature(this.selectedFeature),this.selectFeature(i[0]);else if(this.selectedFeature.id==i[0].id)return void this.unselectFeature(this.selectedFeature)}else this.selectFeature(i[0])}},this.onMouseMove=function(s){let h,d=l(s);if(this.getCanvasContainer().style.cursor="default",s.originalEvent.altKey&&this.draggedObject){if(!e.tb.enableRotatingObjects)return;t="rotate",this.getCanvasContainer().style.cursor="move";Math.min(i.x,d.x),Math.max(i.x,d.x),Math.min(i.y,d.y),Math.max(i.y,d.y);let s={x:0,y:0,z:Math.round(a[2]+~~((d.x-i.x)/this.tb.rotationStep)%360*this.tb.rotationStep%360)};return this.draggedObject.setRotation(s),void this.draggedObject.addHelp("rot: "+s.z+"°")}if(s.originalEvent.shiftKey&&this.draggedObject){if(!e.tb.enableDraggingObjects)return;t="translate",this.getCanvasContainer().style.cursor="move";let i=s.lngLat,n=[Number((i.lng+r).toFixed(this.tb.gridStep)),Number((i.lat+o).toFixed(this.tb.gridStep)),this.draggedObject.modelHeight];return this.draggedObject.setCoords(n),void this.draggedObject.addHelp("lng: "+n[0]+"°, lat: "+n[1]+"°")}if(s.originalEvent.ctrlKey&&this.draggedObject){if(!e.tb.enableDraggingObjects)return;t="altitude",this.getCanvasContainer().style.cursor="move";let i=s.point.y*this.tb.altitudeStep,r=[this.draggedObject.coordinates[0],this.draggedObject.coordinates[1],Number((-i-n).toFixed(this.tb.gridStep))];return this.draggedObject.setCoords(r),void this.draggedObject.addHelp("alt: "+r[2]+"m")}let u=[];if(e.tb.enableSelectingObjects&&(u=this.tb.queryRenderedFeatures(s.point)),h="object"==typeof u[0]){let e=Threebox.prototype.findParent3DObject(u[0]);e&&(this.unoverFeature(this.overedFeature),this.getCanvasContainer().style.cursor="pointer",this.selectedObject&&e.uuid==this.selectedObject.uuid||(this.overedObject&&(this.overedObject.over=!1,this.overedObject=null),e.over=!0,this.overedObject=e),this.repaint=!0,s.preventDefault())}else{this.overedObject&&(this.overedObject.over=!1,this.overedObject=null);let t=[];e.tb.enableSelectingFeatures&&(t=this.queryRenderedFeatures(s.point)),t.length>0&&(this.unoverFeature(t[0]),"fill-extrusion"==t[0].layer.type&&void 0!==t[0].id&&(this.selectedFeature&&this.selectedFeature.id==t[0].id||(this.getCanvasContainer().style.cursor="pointer",this.overedFeature=t[0],this.setFeatureState({source:this.overedFeature.source,sourceLayer:this.overedFeature.sourceLayer,id:this.overedFeature.id},{hover:!0}),this.overedFeature=e.queryRenderedFeatures({layers:[this.overedFeature.layer.id],filter:["==",["id"],this.overedFeature.id]})[0],this.addTooltip(this.overedFeature))))}},this.onMouseDown=function(t){(t.originalEvent.shiftKey||t.originalEvent.altKey||t.originalEvent.ctrlKey)&&0===t.originalEvent.button&&this.selectedObject&&(e.tb.enableDraggingObjects||e.tb.enableRotatingObjects)&&(t.preventDefault(),e.getCanvasContainer().style.cursor="move",e.once("mouseup",this.onMouseUp),this.draggedObject=this.selectedObject,i=l(t),h=this.draggedObject.coordinates,a=utils.degreeify(this.draggedObject.rotation),r=h[0]-t.lngLat.lng,o=h[1]-t.lngLat.lat,n=-this.draggedObject.modelHeight-t.point.y*this.tb.altitudeStep)},this.onMouseUp=function(e){this.getCanvasContainer().style.cursor="default",this.off("mouseup",this.onMouseUp),this.off("mouseout",this.onMouseUp),this.dragPan.enable(),this.draggedObject&&(this.draggedObject.dispatchEvent(new CustomEvent("ObjectDragged",{detail:{draggedObject:this.draggedObject,draggedAction:t},bubbles:!0,cancelable:!0})),this.draggedObject.removeHelp(),this.draggedObject=null,t=null)},this.onMouseOut=function(e){if(this.overedFeature){let t=this.queryRenderedFeatures(e.point);t.length>0&&this.overedFeature.id!=t[0].id&&(this.getCanvasContainer().style.cursor="default",this.unoverFeature(t[0]))}},this.onZoomEnd=function(e){this.tb.zoomLayers.forEach(e=>{this.tb.toggleLayer(e)})};let d=!1,u=!1,c=17,g=91,p=16,b=83;dK=68,this.on("click",this.onClick),this.on("mousemove",this.onMouseMove),this.on("mouseout",this.onMouseOut),this.on("mousedown",this.onMouseDown),this.on("zoom",this.onZoomEnd),document.addEventListener("keydown",function(e){e.which!==c&&e.which!==g||(d=!0),e.which===p&&(u=!0);let t=this.selectedObject;if(u&&e.which===b&&t){let e=utils.toDecimal;if(t.help)t.removeHelp();else{let i=t.modelSize,s=1;"meters"!==t.userData.units&&((s=utils.projectedUnitsPerMeter(t.coordinates[1]))||(s=1),s=e(s,7)),t.addHelp("size(m): "+e(i.x/s,3)+" W, "+e(i.y/s,3)+" L, "+e(i.z/s,3)+" H"),this.repaint=!0}return!1}}.bind(this),!0),document.addEventListener("keyup",function(e){e.which!=c&&e.which!=g||(d=!1),e.which===p&&(u=!1)}.bind(this))})},get fov(){return this.options.fov},set fov(e){this.camera instanceof THREE.PerspectiveCamera&&this.options.fov!==e&&(this.map.transform.fov=e,this.camera.fov=this.map.transform.fov,this.cameraSync.setupCamera(),this.map.repaint=!0,this.options.fov=e)},get orthographic(){return this.options.orthographic},set orthographic(e){const t=this.map.getCanvas().clientHeight,i=this.map.getCanvas().clientWidth;e?(this.map.transform.fov=0,this.camera=new THREE.OrthographicCamera(i/-2,i/2,t/2,t/-2,1,1e21)):(this.map.transform.fov=this.fov,this.camera=new THREE.PerspectiveCamera(this.map.transform.fov,i/t,1,1e21)),this.camera.layers.enable(0),this.camera.layers.enable(1),this.cameraSync=new CameraSync(this.map,this.camera,this.world),this.map.repaint=!0,this.options.orthographic=e},sphere:function(e){return this.setDefaultView(e,this.options),sphere(e,this.world)},line:line,label:label,tooltip:tooltip,tube:function(e){return this.setDefaultView(e,this.options),tube(e,this.world)},extrusion:function(e){return this.setDefaultView(e,this.options),extrusion(e)},Object3D:function(e){return this.setDefaultView(e,this.options),Object3D(e)},loadObj:async function(e,t){this.setDefaultView(e,this.options);let i=this.objectsCache.get(e.obj);i?i.promise.then(i=>{t(i.duplicate(e))}).catch(t=>{this.objectsCache.delete(e.obj),console.error("Could not load model file: "+e.obj)}):this.objectsCache.set(e.obj,{promise:new Promise(async(i,s)=>{loader(e,t,async e=>{e.duplicate?i(e.duplicate()):s(e)})})})},material:function(e){return material(e)},initLights:{ambientLight:null,dirLight:null,dirLightBack:null,dirLightHelper:null,hemiLight:null,pointLight:null},utils:utils,SunCalc:SunCalc,Constants:ThreeboxConstants,projectToWorld:function(e){return this.utils.projectToWorld(e)},unprojectFromWorld:function(e){return this.utils.unprojectFromWorld(e)},projectedUnitsPerMeter:function(e){return this.utils.projectedUnitsPerMeter(e)},getFeatureCenter:function(e,t,i){return utils.getFeatureCenter(e,t,i)},getObjectHeightOnFloor:function(e,t,i){return utils.getObjectHeightOnFloor(e,t,i)},queryRenderedFeatures:function(e){let t=new THREE.Vector2;return t.x=e.x/this.map.transform.width*2-1,t.y=1-e.y/this.map.transform.height*2,this.raycaster.setFromCamera(t,this.camera),this.raycaster.intersectObjects(this.world.children,!0)},findParent3DObject:function(e){var t;return e.object.traverseAncestors(function(e){e.parent&&"Group"==e.parent.type&&e.userData.obj&&(t=e)}),t},setLayoutProperty:function(e,t,i){this.map.setLayoutProperty(e,t,i),null==i||"visibility"!==t||this.world.children.forEach(function(t){t.layer===e&&(t.visibility=i)})},setLayerZoomRange:function(e,t,i){this.map.getLayer(e)&&(this.map.setLayerZoomRange(e,t,i),this.zoomLayers.includes(e)||this.zoomLayers.push(e),this.toggleLayer(e))},setLayerHeigthProperty:function(e,t){let i=this.map.getLayer(e);if(i)if("fill-extrusion"==i.type){let e=this.map.getStyle().sources[i.source].data;e.features.forEach(function(e){e.properties.level=t}),this.map.getSource(i.source).setData(e)}else"custom"==i.type&&this.world.children.forEach(function(i){let s=i.userData.feature;if(s&&s.layer===e){let e=this.tb.getFeatureCenter(s,i,t);i.setCoords(e)}})},setStyle:function(e,t){this.clear().then(()=>{this.map.setStyle(e,t)})},toggleLayer:function(e,t=!0){let i=this.map.getLayer(e);if(i){if(!t)return void this.toggle(i.id,!1);let e=this.map.getZoom();if(i.minzoom&&e=i.maxzoom)return void this.toggle(i.id,!1);this.toggle(i.id,!0)}},toggle:function(e,t){this.setLayoutProperty(e,"visibility",t?"visible":"none"),this.labelRenderer.toggleLabels(e,t)},update:function(){this.map.repaint&&(this.map.repaint=!1);var e=Date.now();this.objects.animationManager.update(e),this.updateLightHelper(),this.renderer.state.reset(),this.renderer.render(this.scene,this.camera),this.labelRenderer.render(this.scene,this.camera),!1===this.options.passiveRendering&&this.map.triggerRepaint()},add:function(e,t,i){if(!this.enableTooltips&&e.tooltip&&(e.tooltip.visibility=!1),this.world.add(e),t){e.layer=t,e.source=i;let s=this.map.getLayer(t);if(s){let t=s.visibility,i=void 0===t;e.visibility=!(!i&&"visible"!==t)}}},remove:function(e){this.map.selectedObject&&e.uuid==this.map.selectedObject.uuid&&this.map.unselectObject(this.map.selectedObject),this.map.draggedObject&&e.uuid==this.map.draggedObject.uuid&&(this.map.draggedObject=null),e.dispose&&e.dispose(),this.world.remove(e),e=null},clear:async function(e=null,t=!1){return new Promise((i,s)=>{let r=[];this.world.children.forEach(function(e){r.push(e)});for(let t=0;t{e.promise.then(e=>{e.dispose(),e=null})}),i("clear")})},removeLayer:function(e){this.clear(e,!0).then(()=>{this.map.removeLayer(e)})},getSunPosition:function(e,t){return SunCalc.getPosition(e,t[1],t[0])},getSunTimes:function(e,t){return SunCalc.getTimes(e,t[1],t[0],t[2]?t[2]:0)},setBuildingShadows:function(e){if(this.map.getLayer(e.buildingsLayerId)){let t=new BuildingShadows(e,this);this.map.addLayer(t,e.buildingsLayerId)}else console.warn("The layer '"+e.buildingsLayerId+"' does not exist in the map.")},setSunlight:function(e=new Date,t){if(!this.lights.dirLight||!this.options.realSunlight)return void console.warn("To use setSunlight it's required to set realSunlight : true in Threebox initial options.");var i=new Date(e.getTime());if(t?t.lng&&t.lat?this.mapCenter=t:this.mapCenter={lng:t[0],lat:t[1]}:this.mapCenter=this.map.getCenter(),this.lightDateTime&&this.lightDateTime.getTime()===i.getTime()&&this.lightLng===this.mapCenter.lng&&this.lightLat===this.mapCenter.lat)return;this.lightDateTime=i,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition=this.getSunPosition(i,[this.mapCenter.lng,this.mapCenter.lat]);let s=this.sunPosition.altitude,r=Math.PI+this.sunPosition.azimuth,o=ThreeboxConstants.WORLD_SIZE/2,n=Math.sin(s),a=Math.cos(s),h=Math.cos(r)*a,l=Math.sin(r)*a;this.lights.dirLight.position.set(l,h,n),this.lights.dirLight.position.multiplyScalar(o),this.lights.dirLight.intensity=Math.max(n,-.15),this.lights.dirLight.updateMatrixWorld(),this.updateLightHelper(),this.map.loaded()&&this.map.setLight({anchor:"map",position:[1.5,180+180*this.sunPosition.azimuth/Math.PI,90-180*this.sunPosition.altitude/Math.PI],"position-transition":{duration:0},color:`hsl(40, ${50*Math.cos(this.sunPosition.altitude)}%, ${96*Math.sin(this.sunPosition.altitude)}%)`},{duration:0})},updateLightHelper:function(){this.lights.dirLightHelper&&(this.lights.dirLightHelper.position.setFromMatrixPosition(this.lights.dirLight.matrixWorld),this.lights.dirLightHelper.updateMatrix(),this.lights.dirLightHelper.update())},dispose:async function(){return console.log(this.memory()),new Promise(e=>{e(this.clear(null,!0).then(e=>(this.map.remove(),this.map={},this.scene.remove(this.world),this.scene.dispose(),this.world.children=[],this.world=null,this.objectsCache.clear(),this.labelRenderer.dispose(),console.log(this.memory()),this.renderer.dispose(),e)))})},defaultLights:function(){this.lights.ambientLight=new THREE.AmbientLight(new THREE.Color("hsl(0, 0%, 100%)"),.75),this.scene.add(this.lights.ambientLight),this.lights.dirLightBack=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLightBack.position.set(30,100,100),this.scene.add(this.lights.dirLightBack),this.lights.dirLight=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLight.position.set(-30,100,-100),this.scene.add(this.lights.dirLight)},realSunlight:function(e=!1){this.renderer.shadowMap.enabled=!0,this.lights.dirLight=new THREE.DirectionalLight(16777215,1),this.scene.add(this.lights.dirLight),e&&(this.lights.dirLightHelper=new THREE.DirectionalLightHelper(this.lights.dirLight,5),this.scene.add(this.lights.dirLightHelper));this.lights.dirLight.castShadow=!0,this.lights.dirLight.shadow.radius=2,this.lights.dirLight.shadow.mapSize.width=8192,this.lights.dirLight.shadow.mapSize.height=8192,this.lights.dirLight.shadow.camera.top=this.lights.dirLight.shadow.camera.right=1e3,this.lights.dirLight.shadow.camera.bottom=this.lights.dirLight.shadow.camera.left=-1e3,this.lights.dirLight.shadow.camera.near=1,this.lights.dirLight.shadow.camera.visible=!0,this.lights.dirLight.shadow.camera.far=4e8,this.lights.hemiLight=new THREE.HemisphereLight(new THREE.Color(16777215),new THREE.Color(16777215),.6),this.lights.hemiLight.color.setHSL(.661,.96,.12),this.lights.hemiLight.groundColor.setHSL(.11,.96,.14),this.lights.hemiLight.position.set(0,0,50),this.scene.add(this.lights.hemiLight),this.setSunlight()},setDefaultView:function(e,t){e.bbox=e.bbox||t.enableSelectingObjects,e.tooltip=e.tooltip||t.enableTooltips},memory:function(){return this.renderer.info.memory},programs:function(){return this.renderer.info.programs.length},version:"2.1.7"};var defaultOptions={defaultLights:!1,realSunlight:!1,realSunlightHelper:!1,passiveRendering:!0,enableSelectingFeatures:!1,enableSelectingObjects:!1,enableDraggingObjects:!1,enableRotatingObjects:!1,enableTooltips:!1,multiLayer:!1,orthographic:!1,fov:ThreeboxConstants.FOV_DEGREES};module.exports=exports=Threebox; },{"./camera/CameraSync.js":4,"./objects/LabelRenderer.js":6,"./objects/Object3D.js":7,"./objects/effects/BuildingShadows.js":9,"./objects/extrusion.js":10,"./objects/label.js":11,"./objects/line.js":12,"./objects/loadObj.js":13,"./objects/objects.js":19,"./objects/sphere.js":20,"./objects/tooltip.js":21,"./objects/tube.js":22,"./three.js":23,"./utils/constants.js":24,"./utils/material.js":25,"./utils/suncalc.js":26,"./utils/utils.js":27}],3:[function(require,module,exports){ -const THREE=require("../three.js"),utils=require("../utils/utils.js");function AnimationManager(t){this.map=t,this.enrolledObjects=[],this.previousFrameTime}AnimationManager.prototype={unenroll:function(t){this.enrolledObjects.splice(this.enrolledObjects.indexOf(t),1)},enroll:function(t){if(t.clock=new THREE.Clock,t.hasDefaultAnimation=!1,t.defaultAction,t.actions=[],t.mixer,t.animations&&t.animations.length>0){t.hasDefaultAnimation=!0;let i=t.userData.defaultAnimation?t.userData.defaultAnimation:0;t.mixer=new THREE.AnimationMixer(t),e(i)}function e(e){for(let i=0;it.animations.length&&console.log("The animation index "+e+" doesn't exist for this object");let a=t.animations[i],n=t.mixer.clipAction(a);t.actions.push(n),e===i?(t.defaultAction=n,n.setEffectiveWeight(1)):n.setEffectiveWeight(0),n.play()}}let i=!1;Object.defineProperty(t,"isPlaying",{get:()=>i,set(e){i!=e&&(i=e,t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})))}}),this.enrolledObjects.push(t),t.animationQueue=[],t.set=function(e){if(e.duration>0){let i={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,i);let a=e.coords,n=e.rotation,o=e.scale||e.scaleX||e.scaleY||e.scaleZ;if(n){let i=t.rotation;e.startRotation=[i.x,i.y,i.z],e.endState.rotation=utils.types.rotation(e.rotation,e.startRotation),e.rotationPerMs=e.endState.rotation.map(function(t,i){return(t-e.startRotation[i])/e.duration})}if(o){let i=t.scale;e.startScale=[i.x,i.y,i.z],e.endState.scale=utils.types.scale(e.scale,e.startScale),e.scalePerMs=e.endState.scale.map(function(t,i){return(t-e.startScale[i])/e.duration})}a&&(e.pathCurve=new THREE.CatmullRomCurve3(utils.lnglatsToWorld([t.coordinates,e.coords])));let s={type:"set",parameters:e};this.animationQueue.push(s),tb.map.repaint=!0}else this.stop(),e.rotation=utils.radify(e.rotation),this._setObject(e);return this},t.animationMethod=null,t.stop=function(e){return t.mixer&&(t.isPlaying=!1,cancelAnimationFrame(t.animationMethod)),this.animationQueue=[],this},t.followPath=function(t,e){let i={type:"followPath",parameters:utils._validate(t,defaults.followPath)};return utils.extend(i.parameters,{pathCurve:new THREE.CatmullRomCurve3(utils.lnglatsToWorld(t.path)),start:Date.now(),expiration:Date.now()+i.parameters.duration,cb:e}),this.animationQueue.push(i),tb.map.repaint=!0,this},t._setObject=function(t){let e=t.position,i=t.rotation,a=t.scale,n=t.worldCoordinates,o=t.quaternion,s=t.translate;if(e){this.coordinates=e;let t=utils.projectToWorld(e);this.position.copy(t)}if(s){this.coordinates=[this.coordinates[0]+s[0],this.coordinates[1]+s[1],this.coordinates[2]+s[2]];let t=utils.projectToWorld(s);this.translateX(t.x),this.translateY(t.y),this.translateZ(t.z)}if(i&&this.rotation.set(i[0],i[1],i[2]),a&&this.scale.set(a[0],a[1],a[2]),o&&this.quaternion.setFromAxisAngle(o[0],o[1]),n){this.position.copy(n);let t=utils.unprojectFromWorld(n);this.coordinates=t}this.updateMatrixWorld(),tb.map.repaint=!0},t.playDefault=function(e){if(t.mixer&&t.hasDefaultAnimation){let i={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,i),t.mixer.timeScale=e.speed||1;let a={type:"playDefault",parameters:e};return this.animationQueue.push(a),tb.map.repaint=!0,this}},t.playAnimation=function(i){t.mixer&&(i.animation&&e(i.animation),t.playDefault(i))},t.pauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!0})},t.unPauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!1})},t.deactivateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.stop()})},t.activateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.play()})},t.idle=function(){return t.mixer&&t.mixer.update(.01),tb.map.repaint=!0,this}},update:function(t){void 0===this.previousFrameTime&&(this.previousFrameTime=t);if(!this.enrolledObjects)return!1;for(let e=this.enrolledObjects.length-1;e>=0;e--){let i=this.enrolledObjects[e];if(i.animationQueue&&0!==i.animationQueue.length)for(let e=i.animationQueue.length-1;e>=0;e--){let a=i.animationQueue[e];if(!a)continue;let n=a.parameters;if(!n.expiration)return i.animationQueue.splice(e,1),void(i.animationQueue[e]&&(i.animationQueue[e].parameters.start=t));if(t>=n.expiration)n.expiration=!1,"playDefault"===a.type?i.stop():(n.endState&&i._setObject(n.endState),void 0!==n.cb&&n.cb());else{let e=(t-n.start)/n.duration;if("set"===a.type){let t={};n.pathCurve&&(t.worldCoordinates=n.pathCurve.getPoint(e)),n.rotationPerMs&&(t.rotation=n.startRotation.map(function(t,i){return t+n.rotationPerMs[i]*e*n.duration})),n.scalePerMs&&(t.scale=n.startScale.map(function(t,i){return t+n.scalePerMs[i]*e*n.duration})),i._setObject(t)}if("followPath"===a.type){let t=n.pathCurve.getPointAt(e);if(objectState={worldCoordinates:t},n.trackHeading){let t=n.pathCurve.getTangentAt(e).normalize(),i=new THREE.Vector3(0,0,0),a=new THREE.Vector3(0,1,0);i.crossVectors(a,t).normalize();let o=Math.acos(a.dot(t));objectState.quaternion=[i,o]}i._setObject(objectState)}"playDefault"===a.type&&(i.activateAllActions(),i.isPlaying=!0,i.animationMethod=requestAnimationFrame(this.update),i.mixer.update(i.clock.getDelta()),tb.map.repaint=!0)}}}this.previousFrameTime=t}};const defaults={followPath:{path:null,duration:1e3,trackHeading:!0}};module.exports=exports=AnimationManager; +const THREE=require("../three.js"),utils=require("../utils/utils.js");function AnimationManager(t){this.map=t,this.enrolledObjects=[],this.previousFrameTime}AnimationManager.prototype={unenroll:function(t){this.enrolledObjects.splice(this.enrolledObjects.indexOf(t),1)},enroll:function(t){if(t.clock=new THREE.Clock,t.hasDefaultAnimation=!1,t.defaultAction,t.actions=[],t.mixer,t.animations&&t.animations.length>0){t.hasDefaultAnimation=!0;let i=t.userData.defaultAnimation?t.userData.defaultAnimation:0;t.mixer=new THREE.AnimationMixer(t),e(i)}function e(e){for(let i=0;it.animations.length&&console.log("The animation index "+e+" doesn't exist for this object");let a=t.animations[i],n=t.mixer.clipAction(a);t.actions.push(n),e===i?(t.defaultAction=n,n.setEffectiveWeight(1)):n.setEffectiveWeight(0),n.play()}}let i=!1;Object.defineProperty(t,"isPlaying",{get:()=>i,set(e){i!=e&&(i=e,t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})))}}),this.enrolledObjects.push(t),t.animationQueue=[],t.set=function(e){if(e.duration>0){let i={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,i);let a=e.coords,n=e.rotation,o=e.scale||e.scaleX||e.scaleY||e.scaleZ;if(n){let i=t.rotation;e.startRotation=[i.x,i.y,i.z],e.endState.rotation=utils.types.rotation(e.rotation,e.startRotation),e.rotationPerMs=e.endState.rotation.map(function(t,i){return(t-e.startRotation[i])/e.duration})}if(o){let i=t.scale;e.startScale=[i.x,i.y,i.z],e.endState.scale=utils.types.scale(e.scale,e.startScale),e.scalePerMs=e.endState.scale.map(function(t,i){return(t-e.startScale[i])/e.duration})}a&&(e.pathCurve=new THREE.CatmullRomCurve3(utils.lnglatsToWorld([t.coordinates,e.coords])));let s={type:"set",parameters:e};this.animationQueue.push(s),tb.map.repaint=!0}else this.stop(),e.rotation=utils.radify(e.rotation),this._setObject(e);return this},t.animationMethod=null,t.stop=function(e){return t.mixer&&(t.isPlaying=!1,cancelAnimationFrame(t.animationMethod)),this.animationQueue=[],this},t.followPath=function(t,e){let i={type:"followPath",parameters:utils._validate(t,defaults.followPath)};return utils.extend(i.parameters,{pathCurve:new THREE.CatmullRomCurve3(utils.lnglatsToWorld(t.path)),start:Date.now(),expiration:Date.now()+i.parameters.duration,cb:e}),this.animationQueue.push(i),tb.map.repaint=!0,this},t._setObject=function(t){let e=t.position,i=t.rotation,a=t.scale,n=t.worldCoordinates,o=t.quaternion,s=t.translate;if(e){this.coordinates=e;let t=utils.projectToWorld(e);this.position.copy(t)}if(s){this.coordinates=[this.coordinates[0]+s[0],this.coordinates[1]+s[1],this.coordinates[2]+s[2]];let e=utils.projectToWorld(s);this.translateX(e.x),this.translateY(e.y),this.translateZ(e.z),t.position=this.coordinates}if(i&&(this.rotation.set(i[0],i[1],i[2]),t.rotation=new THREE.Vector3(i[0],i[1],i[2])),a&&(this.scale.set(a[0],a[1],a[2]),t.scale=this.scale),o&&(this.quaternion.setFromAxisAngle(o[0],o[1]),t.rotation=o[0].multiplyScalar(o[1])),n){this.position.copy(n);let e=utils.unprojectFromWorld(n);this.coordinates=t.position=e}this.updateMatrixWorld(),tb.map.repaint=!0,this.dispatchEvent(new CustomEvent("ObjectChanged",{detail:{object:this,action:{position:t.position,rotation:t.rotation,scale:t.scale}},bubbles:!0,cancelable:!0}))},t.playDefault=function(e){if(t.mixer&&t.hasDefaultAnimation){let i={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,i),t.mixer.timeScale=e.speed||1;let a={type:"playDefault",parameters:e};return this.animationQueue.push(a),tb.map.repaint=!0,this}},t.playAnimation=function(i){t.mixer&&(i.animation&&e(i.animation),t.playDefault(i))},t.pauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!0})},t.unPauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!1})},t.deactivateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.stop()})},t.activateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.play()})},t.idle=function(){return t.mixer&&t.mixer.update(.01),tb.map.repaint=!0,this}},update:function(t){void 0===this.previousFrameTime&&(this.previousFrameTime=t);if(!this.enrolledObjects)return!1;for(let e=this.enrolledObjects.length-1;e>=0;e--){let i=this.enrolledObjects[e];if(i.animationQueue&&0!==i.animationQueue.length)for(let e=i.animationQueue.length-1;e>=0;e--){let a=i.animationQueue[e];if(!a)continue;let n=a.parameters;if(!n.expiration)return i.animationQueue.splice(e,1),void(i.animationQueue[e]&&(i.animationQueue[e].parameters.start=t));if(t>=n.expiration)n.expiration=!1,"playDefault"===a.type?i.stop():(n.endState&&i._setObject(n.endState),void 0!==n.cb&&n.cb());else{let e=(t-n.start)/n.duration;if("set"===a.type){let t={};n.pathCurve&&(t.worldCoordinates=n.pathCurve.getPoint(e)),n.rotationPerMs&&(t.rotation=n.startRotation.map(function(t,i){return t+n.rotationPerMs[i]*e*n.duration})),n.scalePerMs&&(t.scale=n.startScale.map(function(t,i){return t+n.scalePerMs[i]*e*n.duration})),i._setObject(t)}if("followPath"===a.type){let t=n.pathCurve.getPointAt(e);if(objectState={worldCoordinates:t},n.trackHeading){let t=n.pathCurve.getTangentAt(e).normalize(),i=new THREE.Vector3(0,0,0),a=new THREE.Vector3(0,1,0);i.crossVectors(a,t).normalize();let o=Math.acos(a.dot(t));objectState.quaternion=[i,o]}i._setObject(objectState)}"playDefault"===a.type&&(i.activateAllActions(),i.isPlaying=!0,i.animationMethod=requestAnimationFrame(this.update),i.mixer.update(i.clock.getDelta()),tb.map.repaint=!0)}}}this.previousFrameTime=t}};const defaults={followPath:{path:null,duration:1e3,trackHeading:!0}};module.exports=exports=AnimationManager; },{"../three.js":23,"../utils/utils.js":27}],4:[function(require,module,exports){ const THREE=require("../three.js"),utils=require("../utils/utils.js"),ThreeboxConstants=require("../utils/constants.js");function CameraSync(t,a,e){this.map=t,this.camera=a,this.active=!0,this.camera.matrixAutoUpdate=!1,this.world=e||new THREE.Group,this.world.position.x=this.world.position.y=ThreeboxConstants.WORLD_SIZE/2,this.world.matrixAutoUpdate=!1,this.state={translateCenter:(new THREE.Matrix4).makeTranslation(ThreeboxConstants.WORLD_SIZE/2,-ThreeboxConstants.WORLD_SIZE/2,0),worldSizeRatio:ThreeboxConstants.TILE_SIZE/ThreeboxConstants.WORLD_SIZE,worldSize:ThreeboxConstants.TILE_SIZE*this.map.transform.scale};let i=this;this.map.on("move",function(){i.updateCamera()}).on("resize",function(){i.setupCamera()}),this.setupCamera()}CameraSync.prototype={setupCamera:function(){this.state.fov=this.map.transform._fov;const t=this.map.transform;this.camera.aspect=t.width/t.height,this.camera.updateProjectionMatrix(),this.halfFov=this.state.fov/2;const a={x:t.width/2,y:t.height/2},e=.5/Math.tan(this.halfFov)*t.height,i=t._maxPitch*Math.PI/180;this.acuteAngle=Math.PI/2-i,this.state.cameraToCenterDistance=e,this.state.offset=a,this.state.cameraTranslateZ=(new THREE.Matrix4).makeTranslation(0,0,this.state.cameraToCenterDistance),this.state.maxFurthestDistance=.95*this.state.cameraToCenterDistance*(Math.cos(this.acuteAngle)*Math.sin(this.halfFov)/Math.sin(Math.max(.01,Math.min(Math.PI-.01,this.acuteAngle-this.halfFov)))+1),this.updateCamera()},updateCamera:function(t){if(!this.camera)return void console.log("nocamera");const a=this.map.transform,e=Math.PI/2+a._pitch;this.cameraToCenterDistance=.5/Math.tan(this.halfFov)*a.height,this.state.cameraTranslateZ=(new THREE.Matrix4).makeTranslation(0,0,this.cameraToCenterDistance);const i=Math.sin(this.halfFov)*this.state.cameraToCenterDistance/Math.sin(Math.PI-e-this.halfFov),s=Math.cos(Math.PI/2-a._pitch),r=1.01*(s*i+this.state.cameraToCenterDistance),h=a.height/50,n=Math.max(h*s,h),o=a.height,m=a.width;this.camera instanceof THREE.OrthographicCamera?this.camera.projectionMatrix=utils.makeOrthographicMatrix(m/-2,m/2,o/2,o/-2,n,r):this.camera.projectionMatrix=utils.makePerspectiveMatrix(this.state.fov,m/o,n,r);let c=this.calcCameraMatrix(a._pitch,a.angle);this.camera.matrixWorld.copy(c);let l=a.scale*this.state.worldSizeRatio,p=new THREE.Matrix4,u=new THREE.Matrix4,x=new THREE.Matrix4;p.makeScale(l,l,l);let T=a.x||a.point.x,M=a.y||a.point.y;u.makeTranslation(-T,M,0),x.makeRotationZ(Math.PI),this.world.matrix=(new THREE.Matrix4).premultiply(x).premultiply(this.state.translateCenter).premultiply(p).premultiply(u)},calcCameraMatrix(t,a,e){const i=this.map.transform,s=void 0===t?i._pitch:t,r=void 0===a?i.angle:a,h=void 0===e?this.state.cameraTranslateZ:e;return(new THREE.Matrix4).premultiply(h).premultiply((new THREE.Matrix4).makeRotationX(s)).premultiply((new THREE.Matrix4).makeRotationZ(r))}},module.exports=exports=CameraSync; @@ -77,7 +77,7 @@ var utils=require("../utils/utils.js"),THREE=require("../three.js"),defaults={ma !function(){"use strict";var n=Math.PI,t=Math.sin,e=Math.cos,r=Math.tan,a=Math.asin,u=Math.atan2,o=Math.acos,i=n/180,c=864e5,s=2440588,d=2451545;function f(n){return n.valueOf()/c-.5+s}function l(n){return new Date((n+.5-s)*c)}function h(n){return f(n)-d}var M=23.4397*i;function g(n,a){return u(t(n)*e(M)-r(a)*t(M),e(n))}function v(n,r){return a(t(r)*e(M)+e(r)*t(M)*t(n))}function w(n,a,o){return u(t(n),e(n)*t(a)-r(o)*e(a))}function m(n,r,u){return a(t(r)*t(u)+e(r)*e(u)*e(n))}function D(n,t){return i*(280.16+360.9856235*n)-t}function P(n){return i*(357.5291+.98560028*n)}function p(e){return e+i*(1.9148*t(e)+.02*t(2*e)+3e-4*t(3*e))+102.9372*i+n}function H(n){var t=p(P(n));return{dec:v(t,0),ra:g(t,0)}}var T={getPosition:function(n,t,e){var r=i*-e,a=i*t,u=h(n),o=H(u),c=D(u,r)-o.ra;return{azimuth:w(c,a,o.dec),altitude:m(c,a,o.dec)}},toJulian:function(n){return f(n)}},b=T.times=[[-.833,"sunrise","sunset"],[-.3,"sunriseEnd","sunsetStart"],[-6,"dawn","dusk"],[-12,"nauticalDawn","nauticalDusk"],[-18,"nightEnd","night"],[6,"goldenHourEnd","goldenHour"]];T.addTime=function(n,t,e){b.push([n,t,e])};var E=9e-4;function I(t,e,r){return E+(t+e)/(2*n)+r}function k(n,e,r){return d+n+.0053*t(e)-.0069*t(2*r)}function q(n,r,a,u,i,c,s){return k(I(function(n,r,a){return o((t(n)-t(r)*t(a))/(e(r)*e(a)))}(n,a,u),r,i),c,s)}function x(n){var r=i*(134.963+13.064993*n),a=i*(93.272+13.22935*n),u=i*(218.316+13.176396*n)+6.289*i*t(r),o=5.128*i*t(a),c=385001-20905*e(r);return{ra:g(u,o),dec:v(u,o),dist:c}}function y(n,t){return new Date(n.valueOf()+t*c/24)}T.getTimes=function(t,e,r,a){var u,o,c,s,d,f=i*-r,M=i*e,g=function(n){return-2.076*Math.sqrt(n)/60}(a=a||0),w=function(t,e){return Math.round(t-E-e/(2*n))}(h(t),f),m=I(0,f,w),D=P(m),H=p(D),T=v(H,0),x=k(m,D,H),y={solarNoon:l(x),nadir:l(x-.5)};for(u=0,o=b.length;u=0&&(v=l-(m=Math.sqrt(M)/(2*Math.abs(d))),w=l+m,Math.abs(v)<=1&&g++,Math.abs(w)<=1&&g++,v<-1&&(v=w)),1===g?P<0?c=p+v:s=p+v:2===g&&(c=p+(h<0?w:v),s=p+(h<0?v:w)),!c||!s);p+=2)P=o;var H={};return c&&(H.rise=y(a,c)),s&&(H.set=y(a,s)),c||s||(H[h>0?"alwaysUp":"alwaysDown"]=!0),H},module.exports=exports=T}(); },{}],27:[function(require,module,exports){ -var THREE=require("../three.js"),Constants=require("./constants.js"),validate=require("./validate.js"),utils={prettyPrintMatrix:function(t){for(var e=0;e<4;e++){var n=[t[e],t[e+4],t[e+8],t[e+12]];console.log(n.map(function(t){return t.toFixed(4)}))}},makePerspectiveMatrix:function(t,e,n,r){var o=new THREE.Matrix4,i=1/Math.tan(t/2),s=1/(n-r),a=[i/e,0,0,0,0,i,0,0,0,0,(r+n)*s,-1,0,0,2*r*n*s,0];return o.elements=a,o},makeOrthographicMatrix:function(t,e,n,r,o,i){var s=new THREE.Matrix4;const a=1/(e-t),u=1/(n-r),c=1/(i-o);var l=[2*a,0,0,0,0,2*u,0,0,0,0,-1*c,0,-((e+t)*a),-((n+r)*u),-(o*c),1];return s.elements=l,s},radify:function(t){function e(t){return t=t||0,2*Math.PI*t/360}return"object"==typeof t?t.length>0?t.map(function(t){return e(t)}):[e(t.x),e(t.y),e(t.z)]:e(t)},degreeify:function(t){function e(t){return 360*(t=t||0)/(2*Math.PI)}return"object"==typeof t?[e(t.x),e(t.y),e(t.z)]:e(t)},projectToWorld:function(t){var e=[-Constants.MERCATOR_A*Constants.DEG2RAD*t[0]*Constants.PROJECTION_WORLD_SIZE,-Constants.MERCATOR_A*Math.log(Math.tan(.25*Math.PI+.5*Constants.DEG2RAD*t[1]))*Constants.PROJECTION_WORLD_SIZE];if(t[2]){var n=this.projectedUnitsPerMeter(t[1]);e.push(t[2]*n)}else e.push(0);return new THREE.Vector3(e[0],e[1],e[2])},projectedUnitsPerMeter:function(t){return Math.abs(Constants.WORLD_SIZE/Math.cos(Constants.DEG2RAD*t)/Constants.EARTH_CIRCUMFERENCE)},_circumferenceAtLatitude:function(t){return Constants.EARTH_CIRCUMFERENCE*Math.cos(t*Math.PI/180)},mercatorZfromAltitude:function(t,e){return t/this._circumferenceAtLatitude(e)},_scaleVerticesToMeters:function(t,e){for(var n=this.projectedUnitsPerMeter(t[1]),r=(this.projectToWorld(t),0);r0?t.map(function(t){return e(t)}):[e(t.x),e(t.y),e(t.z)]:e(t)},degreeify:function(t){function e(t){return 360*(t=t||0)/(2*Math.PI)}return"object"==typeof t?[e(t.x),e(t.y),e(t.z)]:e(t)},projectToWorld:function(t){var e=[-Constants.MERCATOR_A*Constants.DEG2RAD*t[0]*Constants.PROJECTION_WORLD_SIZE,-Constants.MERCATOR_A*Math.log(Math.tan(.25*Math.PI+.5*Constants.DEG2RAD*t[1]))*Constants.PROJECTION_WORLD_SIZE];if(t[2]){var r=this.projectedUnitsPerMeter(t[1]);e.push(t[2]*r)}else e.push(0);return new THREE.Vector3(e[0],e[1],e[2])},projectedUnitsPerMeter:function(t){return Math.abs(Constants.WORLD_SIZE/Math.cos(Constants.DEG2RAD*t)/Constants.EARTH_CIRCUMFERENCE)},_circumferenceAtLatitude:function(t){return Constants.EARTH_CIRCUMFERENCE*Math.cos(t*Math.PI/180)},mercatorZfromAltitude:function(t,e){return t/this._circumferenceAtLatitude(e)},_scaleVerticesToMeters:function(t,e){for(var r=this.projectedUnitsPerMeter(t[1]),n=(this.projectToWorld(t),0);n{let{width:r,color:n}=e,o=(new THREE.BufferGeometry).setFromPoints(t.getPoints(100)),i=new THREE.LineBasicMaterial({color:n,linewidth:r});return new THREE.Line(o,i)},curvesToLines:t=>{var e=[16711680,2031360,2490623];return t.map((t,r)=>{return curveToLine(t,{width:3,color:e[r]||"purple"})})},_validate:function(t,e){t=t||{};var r={};utils.extend(r,t);for(let n of Object.keys(e))if(void 0===t[n]){if(null===e[n])return void console.error(n+" is required");r[n]=e[n]}else r[n]=t[n];return r},Validator:new validate,exposedMethods:["projectToWorld","projectedUnitsPerMeter","extend","unprojectFromWorld"]};module.exports=exports=utils; },{"../three.js":23,"./constants.js":24,"./validate.js":28}],28:[function(require,module,exports){ function Validate(){}Validate.prototype={Coords:function(r){if(r.constructor===Array)if(r.length<2)console.error("Coords length must be at least 2");else{for(const o of r)if(o.constructor!==Number)return void console.error("Coords values must be numbers");if(!(Math.abs(r[1])>90))return r;console.error("Latitude must be between -90 and 90")}else console.error("Coords must be an array")},Line:function(r){if(r.constructor===Array){for(const o of r)if(!this.Coords(o))return void console.error("Each coordinate in a line must be a valid Coords type");return r}console.error("Line must be an array")},Rotation:function(r){if(r.constructor===Number)r={z:r};else{if(r.constructor!==Object)return void console.error("Rotation must be an object or a number");for(const o of Object.keys(r)){if(!["x","y","z"].includes(o))return void console.error("Rotation parameters must be x, y, or z");if(r[o].constructor!==Number)return void console.error("Individual rotation values must be numbers")}}return r},Scale:function(r){if(r.constructor===Number)r={x:r,y:r,z:r};else{if(r.constructor!==Object)return void console.error("Scale must be an object or a number");for(const o of Object.keys(r)){if(!["x","y","z"].includes(o))return void console.error("Scale parameters must be x, y, or z");if(r[o].constructor!==Number)return void console.error("Individual scale values must be numbers")}}return r}},module.exports=exports=Validate; diff --git a/docs/Threebox.md b/docs/Threebox.md index 43e5563a..f2f5c7a6 100755 --- a/docs/Threebox.md +++ b/docs/Threebox.md @@ -12,7 +12,7 @@ Threebox works by adding a *Three.js* scene to *Mapbox GL*, creating a new *Mapb ## Examples -Threebox contains [17 examples](https://github.com/jscastro76/threebox/blob/master/examples/readme.md) to showcase most of its features. Check them out to have a glance of what is possible. +Threebox contains [18 examples](https://github.com/jscastro76/threebox/blob/master/examples/readme.md) to showcase most of its features. Check them out to have a glance of what is possible. - [01-basic.html](https://github.com/jscastro76/threebox/blob/master/examples/01-basic.html) - [02-line.html](https://github.com/jscastro76/threebox/blob/master/examples/02-line.html) - [03-tube.html](https://github.com/jscastro76/threebox/blob/master/examples/03-tube.html) @@ -1372,6 +1372,41 @@ function onIsPlayingChanged(eventArgs) {
+#### ObjectChanged + +```js +obj.addEventListener('ObjectChanged', onObjectChanged, false) +``` + +This event is fired when an object is changed by any method, including animations. +The event can be listened at any time once the `tb.loadObj` callback method is being executed. +An instance of the object that changes is returned in `eventArgs.detail`, and the action made to the object that cound be one or more of the following: +- `position`: defined as lnglat + alt coords (i.e. `[-122.43471544901193, 37.73719686062993, 0]`), `undefined` if the change doesn't affect position. +- `rotation`: defined as radians in Vector3 format (i.e. `{x: 0, y: 0, z: -3.026900303641103}`), `undefined` if the change doesn't affect rotation. +- `scale`: defined as a Vector3 (i.e. `{x: 1, y: 1, z: 1}`), , `undefined` if the change doesn't affect scale. + +```js +map.addLayer({ + ... + tb.loadObj(options, function (model) { + model.setCoords(origin); + model.addEventListener('ObjectChanged', onObjectChanged, false); + tb.add(model); + }) + + ... +}); +... +function onObjectChanged(e) { + let object = e.detail.object; // the object that has changed + let action = e.detail.action; // the action that defines the change + //do something in the UI such as changing a button state or updating the new position and rotation +} +``` + +
+ + #### ObjectDragged ```js @@ -1397,7 +1432,7 @@ map.addLayer({ ... }); ... -function onDraggedObject(eventArgs) { +function onDraggedObject(e) { let draggedObject = e.detail.draggedObject; // the object dragged let draggedAction = e.detail.draggedAction; // the action during dragging @@ -1433,7 +1468,7 @@ map.addLayer({ ... }); ... -function onObjectMouseOver(eventArgs) { +function onObjectMouseOver(e) { //do something in the UI such as adding help or showing this object attributes } ``` @@ -1466,7 +1501,7 @@ map.addLayer({ ... }); ... -function onObjectMouseOut(eventArgs) { +function onObjectMouseOut(e) { //do something in the UI such as removing help } ``` @@ -1497,8 +1532,8 @@ map.addLayer({ ... }); ... -function onSelectedChange(eventArgs) { - let selectedObject = eventArgs.detail; //we get the object selected/unselected +function onSelectedChange(e) { + let selectedObject = e.detail; //we get the object selected/unselected let selectedValue = selectedObject.selected; //we get if the object is selected after the event } ``` @@ -1549,8 +1584,8 @@ map.addLayer({ //selected extrusion feature event map.on('SelectedFeature', onSelectedFeature); ... -function onSelectedFeature(eventArgs) { - let selectedObject = eventArgs.detail; //we get the object selected/unselected +function onSelectedFeature(e) { + let selectedObject = e.detail; //we get the object selected/unselected let selectedValue = selectedObject.selected; //we get if the object is selected after the event } ``` @@ -1582,7 +1617,7 @@ map.addLayer({ ... }); ... -function onWireframed(eventArgs) { +function onWireframed(e) { if (e.detail.wireframe) { //do something in the UI such as changing a button state } diff --git a/examples/05-logistics.html b/examples/05-logistics.html index 42076b04..7a74098b 100644 --- a/examples/05-logistics.html +++ b/examples/05-logistics.html @@ -93,6 +93,7 @@ tb.loadObj(options, function (model) { truck = model.setCoords(origin); + truck.addEventListener('ObjectChanged', onObjectChanged, false); tb.add(truck); }) @@ -108,6 +109,11 @@ travelPath(pt); }) + function onObjectChanged(e) { + let model = e.detail.object; //here's the object already modified + let action = e.detail.action; //here's the action that changed the object + console.log(action); + } function travelPath(destination) { diff --git a/examples/08-3dbuildings.html b/examples/08-3dbuildings.html index b20d239f..69103175 100644 --- a/examples/08-3dbuildings.html +++ b/examples/08-3dbuildings.html @@ -21,7 +21,7 @@ let popup; let minZoom = 12; let mapConfig = { - ALL: { center: [-73.983964, 40.759475, 0], zoom: 16.84, pitch: 46, bearing: 0 }, + ALL: { center: [-73.985699, 40.750042, 0], zoom: 16.25, pitch: 45, bearing: 0 }, names: { compositeSource: "composite", compositeSourceLayer: "building", diff --git a/examples/11-animation.html b/examples/11-animation.html index 72dacbff..6704a8e4 100644 --- a/examples/11-animation.html +++ b/examples/11-animation.html @@ -175,12 +175,14 @@ soldier = model.setCoords(origin); // Listening to the events - model.addEventListener('SelectedChange', onSelectedChange, false); - model.addEventListener('Wireframed', onWireframed, false); - model.addEventListener('IsPlayingChanged', onIsPlayingChanged, false); - model.addEventListener('ObjectDragged', onDraggedObject, false); - model.addEventListener('ObjectMouseOver', onObjectMouseOver, false); - model.addEventListener('ObjectMouseOut', onObjectMouseOut, false); + soldier.addEventListener('SelectedChange', onSelectedChange, false); + soldier.addEventListener('Wireframed', onWireframed, false); + soldier.addEventListener('IsPlayingChanged', onIsPlayingChanged, false); + soldier.addEventListener('ObjectDragged', onDraggedObject, false); + soldier.addEventListener('ObjectMouseOver', onObjectMouseOver, false); + soldier.addEventListener('ObjectMouseOut', onObjectMouseOut, false); + soldier.addEventListener('ObjectChanged', onObjectChanged, false); + tb.add(soldier); }) @@ -321,6 +323,12 @@ console.log("onObjectMouseOut"); } + function onObjectChanged(e) { + let model = e.detail.object; //here's the object already modified + let action = e.detail.action; //here's the action that changed the object + console.log(action); + } + //wire / unwire object $('#wireButton').on('click', function () { if (selectedObject) { diff --git a/examples/README.md b/examples/README.md index db67fffc..ad848276 100644 --- a/examples/README.md +++ b/examples/README.md @@ -29,6 +29,7 @@ These are the examples included in Threebox. #### [05-logistics.html](https://github.com/jscastro76/threebox/blob/master/examples/05-logistics.html) threebox
- This sample loads a 3D `.obj` model of a truck that is animated following a path once a point in the map is clicked. +- The model is attached to the event `ObjectChanged`. - - - - #### [06-object3d.html](https://github.com/jscastro76/threebox/blob/master/examples/06-object3d.html) diff --git a/package.json b/package.json index f327755f..1f3a1180 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "threebox-plugin", - "version": "2.1.6", + "version": "2.1.7", "description": "A Three.js plugin for Mapbox GL JS, using the CustomLayerInterface feature. Provides convenient methods to manage objects in lnglat coordinates, and to synchronize the map and scene cameras.", "main": "main.js", "repository": { @@ -39,6 +39,7 @@ "three.js", "mapbox", "mapbox-gl-js", + "azure-maps", "3D" ] } diff --git a/src/Threebox.js b/src/Threebox.js index ed36df08..6bbe2a49 100644 --- a/src/Threebox.js +++ b/src/Threebox.js @@ -1019,7 +1019,7 @@ Threebox.prototype = { programs: function () { return this.renderer.info.programs.length }, - version: '2.1.6', + version: '2.1.7', } diff --git a/src/animation/AnimationManager.js b/src/animation/AnimationManager.js index be1a4da4..14a943df 100644 --- a/src/animation/AnimationManager.js +++ b/src/animation/AnimationManager.js @@ -209,22 +209,35 @@ AnimationManager.prototype = { this.translateX(c.x); this.translateY(c.y); this.translateZ(c.z); + options.position = this.coordinates; } - if (r) this.rotation.set(r[0], r[1], r[2]); + if (r) { + this.rotation.set(r[0], r[1], r[2]); + options.rotation = new THREE.Vector3(r[0], r[1], r[2]); + } - if (s) this.scale.set(s[0], s[1], s[2]); + if (s) { + this.scale.set(s[0], s[1], s[2]); + options.scale = this.scale; + } - if (q) this.quaternion.setFromAxisAngle(q[0], q[1]); + if (q) { + this.quaternion.setFromAxisAngle(q[0], q[1]); + options.rotation = q[0].multiplyScalar(q[1]); + } if (w) { this.position.copy(w); let p = utils.unprojectFromWorld(w); - this.coordinates = p; + this.coordinates = options.position = p; } this.updateMatrixWorld(); - tb.map.repaint = true + tb.map.repaint = true; + // fire the ObjectChanged event to notify UI object change + this.dispatchEvent(new CustomEvent('ObjectChanged', { detail: { object: this, action: { position: options.position, rotation: options.rotation, scale: options.scale } }, bubbles: true, cancelable: true })); + }; //[jscastro] play default animation diff --git a/src/utils/utils.js b/src/utils/utils.js index 92198424..79407df6 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -289,7 +289,6 @@ var utils = { }, // retrieve object parameters from an options object - types: { rotation: function (r, currentRotation) { @@ -359,6 +358,36 @@ var utils = { return object != null && typeof object === 'object'; }, + curveToLine: (curve, params) => { + let { width, color } = params; + let geometry = new THREE.BufferGeometry().setFromPoints( + curve.getPoints(100) + ); + + let material = new THREE.LineBasicMaterial({ + color: color, + linewidth: width, + }); + + let line = new THREE.Line(geometry, material); + + return line; + }, + + curvesToLines: (curves) => { + var colors = [0xff0000, 0x1eff00, 0x2600ff]; + var lines = curves.map((curve, i) => { + let params = { + width: 3, + color: colors[i] || 'purple', + }; + let curveline = curveToLine(curve, params); + + return curveline; + }); + return lines; + }, + _validate: function (userInputs, defaults) { userInputs = userInputs || {}; diff --git a/threebox-new.njsproj b/threebox-new.njsproj index 4d697412..f0ca0e2c 100644 --- a/threebox-new.njsproj +++ b/threebox-new.njsproj @@ -6,7 +6,7 @@ threebox-new Debug|Any CPU True - /examples/01-basic.html + /examples/05-logistics.html Tape tests\unit\ @@ -80,7 +80,9 @@ + +