diff --git a/CHANGELOG.md b/CHANGELOG.md index b753de12..5e3842fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Minor version by [@jscastro76](https://github.com/jscastro76), some enhancements #### :sparkles: Enhancements - #182 Resource interpreted as Stylesheet but transferred with MIME type text/plain +- #187 Create an option to translate an object based on world coordinates (obj.position) #### :pencil: Documentation - Updated [documentation](/docs/Threebox.md) started to link points and improving documentation descriptions in general. diff --git a/dist/threebox.js b/dist/threebox.js index 68174916..6fd3f45e 100755 --- a/dist/threebox.js +++ b/dist/threebox.js @@ -1184,8 +1184,6 @@ AnimationManager.prototype = { this.stop(); options.rotation = utils.radify(options.rotation); this._setObject(options); - - } return this @@ -1243,7 +1241,8 @@ AnimationManager.prototype = { let s = options.scale; // custom scale let w = options.worldCoordinates; //Vector3 let q = options.quaternion; // [axis, angle in rads] - let t = options.translate; //[jscastro] lnglat + height for 3D objects + let t = options.translate; // [jscastro] lnglat + height for 3D objects + let wt = options.worldTranslate; // [jscastro] Vector3 translation if (p) { this.coordinates = p; @@ -1254,12 +1253,21 @@ AnimationManager.prototype = { if (t) { this.coordinates = [this.coordinates[0] + t[0], this.coordinates[1] + t[1], this.coordinates[2] + t[2]]; let c = utils.projectToWorld(t); - this.translateX(c.x); - this.translateY(c.y); - this.translateZ(c.z); + this.position.copy(c) + //this.translateX(c.x); + //this.translateY(c.y); + //this.translateZ(c.z); options.position = this.coordinates; } + if (wt) { + this.translateX(wt.x); + this.translateY(wt.y); + this.translateZ(wt.z); + let p = utils.unprojectFromWorld(this.position); + this.coordinates = options.position = p; + } + if (r) { this.rotation.set(r[0], r[1], r[2]); options.rotation = new THREE.Vector3(r[0], r[1], r[2]); diff --git a/dist/threebox.min.js b/dist/threebox.min.js index 9ae50ecc..ff67e61e 100644 --- a/dist/threebox.min.js +++ b/dist/threebox.min.js @@ -5,7 +5,7 @@ window.Threebox=require("./src/Threebox.js"),window.THREE=require("./src/three.j 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,preserveDrawingBuffer:i.preserveDrawingBuffer,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(){this.selectedObject.selected=!1,this.selectedObject=null},this.outObject=function(){this.overedObject.over=!1,this.overedObject=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.outFeature=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()}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.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.outFeature(this.overedFeature),this.getCanvasContainer().style.cursor="pointer",this.selectedObject&&e.uuid==this.selectedObject.uuid||(this.overedObject&&this.overedObject.uuid!=e.uuid&&this.outObject(),e.over=!0,this.overedObject=e),this.repaint=!0,s.preventDefault())}else{this.overedObject&&this.outObject();let t=[];e.tb.enableSelectingFeatures&&(t=this.queryRenderedFeatures(s.point)),t.length>0&&(this.outFeature(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.outFeature(t[0]))}},this.onZoom=function(e){this.tb.zoomLayers.forEach(e=>{this.tb.toggleLayer(e)}),this.tb.world.children.filter(e=>null!=e.fixedZoom).forEach(e=>{e.setObjectScale(this.transform.scale)})};let d=!1,u=!1,c=17,g=91,p=16,b=83;this.on("click",this.onClick),this.on("mousemove",this.onMouseMove),this.on("mouseout",this.onMouseOut),this.on("mousedown",this.onMouseDown),this.on("zoom",this.onZoom),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.options.realSunlight&&this.renderer.state.setBlending(THREE.NormalBlending),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.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,e.mapScale=this.map.transform.scale},memory:function(){return this.renderer.info.memory},programs:function(){return this.renderer.info.programs.length},version:"2.1.8"};var defaultOptions={defaultLights:!1,realSunlight:!1,realSunlightHelper:!1,passiveRendering:!0,preserveDrawingBuffer:!1,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(e){t.setScale();let i=e.position,a=e.rotation,n=e.scale,o=e.worldCoordinates,s=e.quaternion,r=e.translate;if(i){this.coordinates=i;let t=utils.projectToWorld(i);this.position.copy(t)}if(r){this.coordinates=[this.coordinates[0]+r[0],this.coordinates[1]+r[1],this.coordinates[2]+r[2]];let t=utils.projectToWorld(r);this.translateX(t.x),this.translateY(t.y),this.translateZ(t.z),e.position=this.coordinates}if(a&&(this.rotation.set(a[0],a[1],a[2]),e.rotation=new THREE.Vector3(a[0],a[1],a[2])),n&&(this.scale.set(n[0],n[1],n[2]),e.scale=this.scale),s&&(this.quaternion.setFromAxisAngle(s[0],s[1]),e.rotation=s[0].multiplyScalar(s[1])),o){this.position.copy(o);let t=utils.unprojectFromWorld(o);this.coordinates=e.position=t}this.setBoundingBoxShadowFloor(),this.setReceiveShadowFloor(),this.updateMatrixWorld(),tb.map.repaint=!0,this.dispatchEvent(new CustomEvent("ObjectChanged",{detail:{object:this,action:{position:e.position,rotation:e.rotation,scale:e.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={worldCoordinates:n.pathCurve.getPointAt(e)};if(n.trackHeading){let i=n.pathCurve.getTangentAt(e).normalize(),a=new THREE.Vector3(0,0,0),o=new THREE.Vector3(0,1,0);a.crossVectors(o,i).normalize();let s=Math.acos(o.dot(i));t.quaternion=[a,s]}i._setObject(t)}"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(e){t.setScale();let i=e.position,a=e.rotation,n=e.scale,o=e.worldCoordinates,s=e.quaternion,r=e.translate,l=e.worldTranslate;if(i){this.coordinates=i;let t=utils.projectToWorld(i);this.position.copy(t)}if(r){this.coordinates=[this.coordinates[0]+r[0],this.coordinates[1]+r[1],this.coordinates[2]+r[2]];let t=utils.projectToWorld(r);this.position.copy(t),e.position=this.coordinates}if(l){this.translateX(l.x),this.translateY(l.y),this.translateZ(l.z);let t=utils.unprojectFromWorld(this.position);this.coordinates=e.position=t}if(a&&(this.rotation.set(a[0],a[1],a[2]),e.rotation=new THREE.Vector3(a[0],a[1],a[2])),n&&(this.scale.set(n[0],n[1],n[2]),e.scale=this.scale),s&&(this.quaternion.setFromAxisAngle(s[0],s[1]),e.rotation=s[0].multiplyScalar(s[1])),o){this.position.copy(o);let t=utils.unprojectFromWorld(o);this.coordinates=e.position=t}this.setBoundingBoxShadowFloor(),this.setReceiveShadowFloor(),this.updateMatrixWorld(),tb.map.repaint=!0,this.dispatchEvent(new CustomEvent("ObjectChanged",{detail:{object:this,action:{position:e.position,rotation:e.rotation,scale:e.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={worldCoordinates:n.pathCurve.getPointAt(e)};if(n.trackHeading){let i=n.pathCurve.getTangentAt(e).normalize(),a=new THREE.Vector3(0,0,0),o=new THREE.Vector3(0,1,0);a.crossVectors(o,i).normalize();let s=Math.acos(o.dot(i));t.quaternion=[a,s]}i._setObject(t)}"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; diff --git a/docs/Threebox.md b/docs/Threebox.md index 073d7af3..d94a54ee 100755 --- a/docs/Threebox.md +++ b/docs/Threebox.md @@ -1225,7 +1225,7 @@ Broad method to update object's position, rotation, and scale in only one call. This method can also be used to animate an object if `options.duration` has a value. Check out the Threebox Types section below for details. -**options object** +**options object on animations (`duration > 0`)** | option | required | default | type | description | |-----------|----------|---------|--------|------------| @@ -1234,6 +1234,18 @@ Check out the Threebox Types section below for details. | `scale` | no | NA | `scaleTransform` | Scale(s) to set the object, where 1 is the default scale | | `duration` | no | 1000 | number | Duration of the animation, in milliseconds to complete the values specified in the other properties `scale`, `rotation` and `coords`. If 0 or undefined it will apply the values to the object directly with no animation. | +**options object without animations (`duration == 0`)** + +| option | required | default | type | description | +|-----------|----------|---------|--------|------------| +| `position` | no | NA | `lnglat` | Position to which to move the object | +| `rotation` | no | NA | `rotationTransform` | Rotation(s) to set the object, in units of degrees | +| `scale` | no | NA | `scaleTransform` | Scale(s) to set the object, where 1 is the default scale | +| `worldCoordinates` | no | NA | `Vector3` | Duration of the animation, in milliseconds to complete the values specified in the other properties `scale`, `rotation` and `coords`. If 0 or undefined it will apply the values to the object directly with no animation. | +| `quaternion` | no | NA | [`Vector3`, `radians`] | Rotation(s) to set to the axes received by the `Vector3` and in by the angle in radians | +| `translate` | no | NA | `lnglat` | Increment to the coords position to translate the object. | +| `worldTranslate` | no | NA | `Vector3` | Increment to the object world position to translate the object. | +
#### setAnchor diff --git a/src/animation/AnimationManager.js b/src/animation/AnimationManager.js index 59ea7d6a..d9074e09 100644 --- a/src/animation/AnimationManager.js +++ b/src/animation/AnimationManager.js @@ -141,8 +141,6 @@ AnimationManager.prototype = { this.stop(); options.rotation = utils.radify(options.rotation); this._setObject(options); - - } return this @@ -200,7 +198,8 @@ AnimationManager.prototype = { let s = options.scale; // custom scale let w = options.worldCoordinates; //Vector3 let q = options.quaternion; // [axis, angle in rads] - let t = options.translate; //[jscastro] lnglat + height for 3D objects + let t = options.translate; // [jscastro] lnglat + height for 3D objects + let wt = options.worldTranslate; // [jscastro] Vector3 translation if (p) { this.coordinates = p; @@ -211,12 +210,21 @@ AnimationManager.prototype = { if (t) { this.coordinates = [this.coordinates[0] + t[0], this.coordinates[1] + t[1], this.coordinates[2] + t[2]]; let c = utils.projectToWorld(t); - this.translateX(c.x); - this.translateY(c.y); - this.translateZ(c.z); + this.position.copy(c) + //this.translateX(c.x); + //this.translateY(c.y); + //this.translateZ(c.z); options.position = this.coordinates; } + if (wt) { + this.translateX(wt.x); + this.translateY(wt.y); + this.translateZ(wt.z); + let p = utils.unprojectFromWorld(this.position); + this.coordinates = options.position = p; + } + if (r) { this.rotation.set(r[0], r[1], r[2]); options.rotation = new THREE.Vector3(r[0], r[1], r[2]); diff --git a/tests/threebox-tests-bundle.js b/tests/threebox-tests-bundle.js index df92b5c4..0b80f86f 100644 --- a/tests/threebox-tests-bundle.js +++ b/tests/threebox-tests-bundle.js @@ -12605,8 +12605,6 @@ AnimationManager.prototype = { this.stop(); options.rotation = utils.radify(options.rotation); this._setObject(options); - - } return this @@ -12664,7 +12662,8 @@ AnimationManager.prototype = { let s = options.scale; // custom scale let w = options.worldCoordinates; //Vector3 let q = options.quaternion; // [axis, angle in rads] - let t = options.translate; //[jscastro] lnglat + height for 3D objects + let t = options.translate; // [jscastro] lnglat + height for 3D objects + let wt = options.worldTranslate; // [jscastro] Vector3 translation if (p) { this.coordinates = p; @@ -12675,12 +12674,21 @@ AnimationManager.prototype = { if (t) { this.coordinates = [this.coordinates[0] + t[0], this.coordinates[1] + t[1], this.coordinates[2] + t[2]]; let c = utils.projectToWorld(t); - this.translateX(c.x); - this.translateY(c.y); - this.translateZ(c.z); + this.position.copy(c) + //this.translateX(c.x); + //this.translateY(c.y); + //this.translateZ(c.z); options.position = this.coordinates; } + if (wt) { + this.translateX(wt.x); + this.translateY(wt.y); + this.translateZ(wt.z); + let p = utils.unprojectFromWorld(this.position); + this.coordinates = options.position = p; + } + if (r) { this.rotation.set(r[0], r[1], r[2]); options.rotation = new THREE.Vector3(r[0], r[1], r[2]);