var Tool = { none: 0, // measurePoint: 1, measureLength: 2, measureArea: 3, measureZ: 4, measureFacade: 5, measureAngle: 6, measureSlope: 7, createPoint: 11, createPolyline: 12, createPolygon: 13, caliplane: 14, // pick: 21, remove: 22, fullscreen: 23, lrs: 24, // play: 31, stop: 32, next: 33, prev: 34, branch: 35, back: 36, history: 37, // }; var FullMode = { fill: 1, //填充,黑边,保持全部数据可见 trans: 2, //镂空,露出背景,保持全部数据可见 clip: 3, //裁剪,充满div,数据不全 stretch: 4, //拉伸,充满div,会变形 } var SampleMode = { none: 0, cloud: 1, photo: 2, ground: 3, plane: 4, object: 5, depth: 6, }; var LocateState = { success: 0, typeError: 1, dataError: 2, imageError: 3, busy: 4, }; var ControlType = { arrow: 0, history: 1, }; function PPV(divID) { var self = this; this.getVersion = function () { return '青岛定制-20200309(base 20181123)'; } console.log('ppv:' + self.getVersion()); var $div = $("#" + divID); var div = document.getElementById(divID); //var div = $div[0]; //var div = $div.get(0); //var $div = $(div); var userKey; var server = ""; var device_type = 3;//3:imaj, 4:streetview, 0:ppv, -1:php var photo_type = 1;//1:360, 2:flat, 3:fisheye var cur_tool = Tool.none; var sampleMode = SampleMode.photo; var playing = false;//是否正在播放 var busy = false;//是否正在加载 var cur_frame_id = 0; var cur_frame;//当前帧的前后左右历史关系总和 var lastCE;// 记录camera与轨迹heading之间的夹角 var ground = -1.45;//相机与地面的高度差 var sampleDistance = 50;//采样距离限制,距离太远,误差太大 // imajbox光学参数 // 发现一些关键的事 // sensor的长宽比并不等于2448/2048 // 而是等于2448/2050 // 2050是imaj内部的属性,可以查到 // 2048并不是全部的sensor的像素,而是少了两行 // 2448/2048 = 1.1953125 // 8.4456/7.0656 = 1.1953125 // 2448/2050 = 1.1941463414634146341463414634146 // 8.4456/7.0725 = 1.1941463414634146341463414634146 var camScale = 100;//camScale = pref.scope/focal; var sensorWidth = 8.4456; var sensorHeight = 7.0656;//7.0725;//后者是全尺寸sensor var focal = 5; var imajAspect = 1.1953125;//2448/2048 var principalX = 0; var principalY = 0; var deviceList; var twins;//标记是否twins,数值代表yaw-delta // function baseUrl() { var js = document.scripts; var jsPath = ''; for (var i = js.length - 1; i >= 0; i--) { var src = js[i].src; var pos = src.toLowerCase().indexOf("ppv/js/ppv."); if (pos >= 0) { jsPath = src.substring(0, pos); break; } } return jsPath; } var ppvPath = baseUrl(); var loader = new THREE.TextureLoader(); var camera; var magnifier; var scene; var renderer; // 场景中,工作用到的对象 var groupOne = new THREE.Group();//挂载点 var groupAid = new THREE.Group();//辅助对象 var group360 = new THREE.Group(); var groupArrow = new THREE.Group(); var groupHistory = new THREE.Group(); var groupTrack = new THREE.Group();//正在生成的对象容器 var groupRay = new THREE.Group();//摄影测量射线 var groupBead = new THREE.Group();//当前采样点标记 var groupSample = new THREE.Group();// var groupLabel = new THREE.Group();//标注文字 var groupFace = new THREE.Group();//探面环 var groupLayers = new THREE.Group(); //缺省定义,以后每一帧都可能改变 var tm = "+proj=merc +lat_0=0 +lon_0=0 +k=1.000000 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs "; var trb = proj4(tm);//工程投影变换器 var coord_org = new THREE.Vector3();//工程坐标系原点 var eye = new THREE.Vector3();//工程坐标系原点 var camgroup = new THREE.Group();//group for pose var camplaneMesh;//object3d var camballMesh;//object3d panorama var planeGeometry = new THREE.PlaneBufferGeometry(1, 1); var sphereGeometry = new THREE.SphereBufferGeometry(1, 32, 32); var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xff00ff, side: THREE.FrontSide, shading: THREE.SmoothShading }); var camballGeometry = new THREE.SphereBufferGeometry(1, 100, 50); var camballMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide, shading: THREE.SmoothShading }); var ringGeometry = new THREE.RingGeometry(1.0, 1.2, 64); var ringMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide, transparent: true, opacity: 0.6, }); var face = new THREE.Mesh(ringGeometry, ringMaterial); var faceLabel = new TextSprite({ style: { textColor: '#ffff88', //文字颜色 borderThickness: 0, //不使用边框和背景 backgroundColor: '#000000', } }); groupFace.add(face); groupFace.add(faceLabel); function normalizeTexture(texture) { texture.minFilter = THREE.LinearFilter; texture.magFilter = THREE.LinearFilter; texture.generateMipmaps = false; texture.mapping = THREE.UVMapping; } var arrowTextureE = loader.load(ppvPath + 'ppv/icon/arrow-e.png'); normalizeTexture(arrowTextureE); var arrowTextureW = loader.load(ppvPath + 'ppv/icon/arrow-w.png'); normalizeTexture(arrowTextureW); var arrowTextureS = loader.load(ppvPath + 'ppv/icon/arrow-s.png'); normalizeTexture(arrowTextureS); var arrowTextureN = loader.load(ppvPath + 'ppv/icon/arrow-n.png'); normalizeTexture(arrowTextureN); var arrowTextureNE = loader.load(ppvPath + 'ppv/icon/arrow-ne.png'); normalizeTexture(arrowTextureNE); var arrowTextureNW = loader.load(ppvPath + 'ppv/icon/arrow-nw.png'); normalizeTexture(arrowTextureNW); var arrowTextureSE = loader.load(ppvPath + 'ppv/icon/arrow-se.png'); normalizeTexture(arrowTextureSE); var arrowTextureSW = loader.load(ppvPath + 'ppv/icon/arrow-sw.png'); normalizeTexture(arrowTextureSW); var historyTexture = loader.load(ppvPath + 'ppv/icon/history.png'); normalizeTexture(arrowTextureSW); // function arrowTexture(heading) { if (heading >= -22.5 && heading < 22.5) return arrowTextureN; else if (heading >= 22.5 && heading < 67.5) return arrowTextureNE; else if (heading >= 67.5 && heading < 112.5) return arrowTextureE; else if (heading >= 112.5 && heading < 157.5) return arrowTextureSE; else if (heading >= 157.5 || heading < -157.5) return arrowTextureS; else if (heading >= -157.5 && heading < -112.5) return arrowTextureSW; else if (heading >= -112.5 && heading < -67.5) return arrowTextureW; else if (heading >= -67.5 && heading < -22.5) return arrowTextureNW; } var error_imaj_url = ppvPath + 'ppv/icon/error-imaj.jpg'; var error_imaj = loader.load(error_imaj_url); normalizeTexture(error_imaj); error_360_url = ppvPath + 'ppv/icon/error-360.jpg'; var error_360 = loader.load(error_360_url); normalizeTexture(error_360); // var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2();//鼠标位置,归一化 var mousePos = new THREE.Vector2(-1, -1);//鼠标位置,像素单位 var magPos = new THREE.Vector2(-1, -1);//放大镜位置,像素单位,按下ctrl时,放大镜起始位置 var mouseMag = new THREE.Vector2(0, 0)////鼠标位置,归一化,用于放大镜,由于鼠标总在放大镜中心,所以它总是(0,0) var selectArrow; var selectFeature; var pref = { bgcolor: 0x000000, fullView: FullMode.fill, enableArrow: true, enableHistory: true, enableSurfaceDetection: true, enableDeviceOrientation: false, scope: 100,//可视范围 thumb: 'Middle', magnifier: { size: 256, zoom: 5.0, fix: false,//是否启动放大镜后,固定位置不变。另一个方式是,放大镜随鼠标移动 }, arrows: { //方向箭头位置 forward: 5,//向前,米 below: 1.8,//降低,米 lean: 15,//前倾,度 }, key: { del: 46, //删除 delete play: 32, //播放 space fforward: 33, //快进 page up fbackward: 34, //快退 page down forward: 38, //前进 up backward: 40, //后退 down fullscreen: 120, //全屏 f9 }, label: { fontface: '微软雅黑', //字体 fontsize: 15, //字高 textColor: '#000000', //文字颜色 borderThickness: 1, //边框线宽,如果取0值,将禁用边框 borderFillet: 6, //边框圆角 borderColor: 'rgba(0,0,0,0.8)', //边框颜色 backgroundColor: 'rgba(255,255,255,0.8)', //背景颜色 }, }; this.setPref = function (_pref) { if (_pref.bgcolor != null) { pref.bgcolor = _pref.bgcolor; renderer.setClearColor(pref.bgcolor); } if (_pref.fullView != null) { pref.fullView = _pref.fullView; //onResize(); } if (_pref.enableArrow != null) { pref.enableArrow = _pref.enableArrow; groupArrow.visible = pref.enableArrow; } if (_pref.enableHistory != null) { pref.enableHistory = _pref.enableHistory; groupHistory.visible = pref.enableArrow; } if (_pref.enableSurfaceDetection != null) { pref.enableSurfaceDetection = _pref.enableSurfaceDetection; groupFace.visible = pref.enableSurfaceDetection; } if (_pref.enableDeviceOrientation != null) { pref.enableDeviceOrientation = _pref.enableDeviceOrientation; } if (_pref.scope != null) { pref.scope = _pref.scope; // 更新camplane的位置 camScale = pref.scope / focal; var pos = new THREE.Vector3(0, focal * camScale, 0); var rot = new THREE.Euler(Math.PI / 2, 0, 0); var scale = new THREE.Vector3(sensorWidth * camScale, sensorHeight * camScale, 1); camplaneMesh.rotation.copy(rot); camplaneMesh.scale.copy(scale); camplaneMesh.position.copy(pos); // camballMesh.scale.set(-pref.scope, pref.scope, pref.scope); } if (_pref.arrows) { if (_pref.arrows.forward != null) { pref.arrows.forward = _pref.arrows.forward; } if (_pref.arrows.below != null) { pref.arrows.below = _pref.arrows.below; } if (_pref.arrows.lean != null) { pref.arrows.lean = _pref.arrows.lean; } } if (_pref.magnifier) { if (_pref.magnifier.size != null) { pref.magnifier.size = _pref.magnifier.size; } if (_pref.magnifier.zoom != null) { pref.magnifier.zoom = _pref.magnifier.zoom; } if (_pref.magnifier.fix != null) { pref.magnifier.fix = _pref.magnifier.fix; } } if (_pref.key) { if (_pref.key.del != null) { pref.key.del = _pref.key.del; } if (_pref.key.play != null) { pref.key.play = _pref.key.play; } if (_pref.key.fforward != null) { pref.key.fforward = _pref.key.fforward; } if (_pref.key.fbackward != null) { pref.key.fbackward = _pref.key.fbackward; } if (_pref.key.forward != null) { pref.key.forward = _pref.key.forward; } if (_pref.key.backward != null) { pref.key.backward = _pref.key.backward; } if (_pref.key.fullscreen != null) { pref.key.fullscreen = _pref.key.fullscreen; } } if (_pref.label) { if (_pref.label.fontface != null) pref.label.fontface = _pref.label.fontface; if (_pref.label.fontsize != null) pref.label.fontsize = _pref.label.fontsize; if (_pref.label.textColor != null) pref.label.textColor = _pref.label.textColor; if (_pref.label.borderThickness != null) pref.label.borderThickness = _pref.label.borderThickness; if (_pref.label.borderFillet != null) pref.label.borderFillet = _pref.label.borderFillet; if (_pref.label.borderColor != null) pref.label.borderColor = _pref.label.borderColor; if (_pref.label.backgroundColor != null) pref.label.backgroundColor = _pref.label.backgroundColor; } } this.getPref = function () { return pref; } // this.isPlaying = function () { return playing; } // this.setServer = function (url) { server = url; deviceList = null; // 下载设备标定参数列表 //一般的用法是,调用setServer之后,马上会调用locate,还没来得及下载设备表 // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetEquipParam", { name: "" }, function (ret) { deviceList = $.parseJSON(ret.d); }, function error(x, e) { console.warn('GetEquipParam error'); } ); } this.locate = function (type, lon, lat, key, backward) { userKey = key; // busy设置状态,和清除状态,要保持配对 // 由于没有析构概念,所以,在整个load_frame过程中,所有的return,都要对应清除busy状态 // 此外还要考虑缩略图到大图的refine过程 // 至于groupArrow.visible,只要busy,就应该false if (busy) { var event = { state: LocateState.busy }; this.onLocate(event); return; } busy = true; // 清除历史列表 clearHistory(); // device_type = type; switch (device_type) { case -1://php { var tol = 50;//米 var angle = 15;//度 //这个方法,即使失败,也会回调主函数 $.get( server + "/php/locate.php", { lon: lon, lat: lat, tol: tol, angle: angle }, function (ret) { var p = ret.substr(0, 6); if (p == 'error:') { console.error(ret); busy = false; return; } load_frame_php(ret, true); } ); } break; case 0://ppv { console.log(lon, lat) localStorage.setItem('loc', lon + "," + lat) // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByCoord", { type: device_type, x: lon, y: lat, key: userKey }, function (ret) { load_frame_ppv(ret.d, true); }, function error(x, e) { busy = false; } ); } break; case 3://imaj { console.log(lon, lat) localStorage.setItem('loc', lon + "," + lat) // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByCoord", { type: device_type, x: lon, y: lat, key: userKey }, function (ret) { if (backward != null) { busy = false; var res_v = $.parseJSON(ret.d); //jQuery,支持很好 self.locateByID(device_type, res_v.Currentframe.PmId - backward, userKey); } else { load_frame_imaj(ret.d, true); } }, function error(x, e) { busy = false; } ); } break; case 4://streetview { console.log(lon, lat) // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByCoord", { type: device_type, x: lon, y: lat, key: userKey }, function (ret) { load_frame_streetview(ret.d, true); }, function error(x, e) { busy = false; } ); } break; } } this.locateByID = function (type, id, key) { userKey = key; if (busy) { var event = { state: LocateState.busy }; this.onLocate(event); return; } busy = true; // 清除历史列表 clearHistory(); // device_type = type; switch (device_type) { case -1://php { var tol = 50;//米 var angle = 15;//度 //这个方法,即使失败,也会回调主函数 $.get( server + "/php/locate_by_id.php", { id: id, tol: tol, angle: angle }, function (ret) { var p = ret.substr(0, 6); if (p == 'error:') { console.error(ret); busy = false; return; } load_frame_php(ret, true); } ); } break; case 0://ppv { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_ppv(ret.d, true); }, function error(x, e) { busy = false; } ); } break; case 100://single pano { load_frame_ppv(id, true); } break; case 3://imaj { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_imaj(ret.d, true); }, function error(x, e) { busy = false; } ); } break; case 4://streetview { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_streetview(ret.d, true); }, function error(x, e) { busy = false; } ); } break; } } function load_frame(id, new_workspace) { if (busy) { return; } busy = true; // 清除历史列表 clearHistory(); setTimeout(function () { switch (device_type) { case -1://php { var tol = 50;//米 var angle = 15;//度 //这个方法,即使失败,也会回调主函数 $.get( server + "/php/locate_by_id.php", { id: id, tol: tol, angle: angle }, function (ret) { var p = ret.substr(0, 6); if (p == 'error:') { console.error(ret); busy = false; return; } load_frame_php(ret); } ); } break; case 0://ppv { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_ppv(ret.d); }, function error(x, e) { busy = false; } ); } break; case 3://imaj { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_imaj(ret.d); }, function error(x, e) { busy = false; } ); } break; case 4://streetview { // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/GetBranchByID", { type: device_type, id: id, step: 1, key: userKey }, function (ret) { load_frame_streetview(ret.d); }, function error(x, e) { busy = false; } ); } break; } }, 100); } // 根据step与cur_frame_id的关系,分析当前帧结构,从中得到合理的id function getFrameID(step) { if (cur_frame != null) { if (step == 1)//next { switch (device_type) { case -1://php if (cur_frame.next) return cur_frame.next.id; break; case 0://ppv if (cur_frame.Nextframe) return cur_frame.Nextframe.ImgId; break; case 3://imaj if (cur_frame.Nextframe) return cur_frame.Nextframe.PmId; break; case 4://streetview if (cur_frame.Nextframe) return cur_frame.Nextframe.Id; break; } } else if (step == -1)//prev { switch (device_type) { case -1://php if (cur_frame.prev) return cur_frame.prev.id; break; case 0://ppv if (cur_frame.Prevframe) return cur_frame.Prevframe.ImgId; break; case 3://imaj if (cur_frame.Prevframe) return cur_frame.Prevframe.PmId; break; case 4://streetview if (cur_frame.Prevframe) return cur_frame.Prevframe.Id; break; } } else//还无法处理 { } } return cur_frame_id + step; } this.step = function (step) { if (playing) return; // 要修改所有的cur_frame_id+n // n并不应该是一个具体是数字,而是前后的逻辑 // 当然,目前还不能解决n>1的情况 load_frame(getFrameID(step)); } this.play = function () { if (playing) return; // var event = { tool: Tool.play, }; this.onTool(event); // playing = true; load_frame(getFrameID(1)); } this.stop = function () { playing = false; // var event = { tool: Tool.stop, }; this.onTool(event); } this.setTool = function (tool) { setTool(tool); } this.getTool = function () { return cur_tool; } this.setSampleMode = function (mode) { sampleMode = mode; } this.getSampleMode = function () { return sampleMode; } this.getFrame = function () { return cur_frame_id; } this.lookAt = function (lon, lat, alt) { if (!is360()) return; //旋转视点 // 1 转换到工程坐标 var lonlat = [lon, lat]; var coord = trb.forward(lonlat); var pos = new THREE.Vector3(coord[0] - coord_org.x, coord[1] - coord_org.y, alt - coord_org.z); // 2 求当前dir,目标dir var src = camera.getWorldDirection(); var dst = new THREE.Vector3(); dst.subVectors(pos, camera.position); dst.normalize(); // 如果两个向量太接近,qend.setFromUnitVectors会无效,所以分成过渡算法 var dist = src.distanceTo(dst); if (dist < 0.1) { // 3 从当前dir过渡到目标dir var step = 0; var count = 30; var grad = new THREE.Vector3(); var timer = setInterval(function () { if (++step == count)//动画结束 { clearInterval(timer); // 4 画一条直线,指向目标 } // var s = 1.0 * step / count; grad.x = src.x + (dst.x - src.x) * s; grad.y = src.y + (dst.y - src.y) * s; grad.z = src.z + (dst.z - src.z) * s; grad.normalize(); // var up = new THREE.Vector3(0, 0, 1); camera.position.set(0, 0, 0); camera.up.copy(up); camera.lookAt(grad); camera.position.copy(eye); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); }, 20); } else { // 3 从当前dir过渡到目标dir var step = 0; var count = 30; var grad = new THREE.Vector3(); var quat = new THREE.Quaternion(); var qend = new THREE.Quaternion(); qend.setFromUnitVectors(src, dst); var timer = setInterval(function () { if (++step == count)//动画结束 { clearInterval(timer); // 4 画一条直线,指向目标 } // var s = 1.0 * step / count; quat.slerp(qend, s); grad.copy(src); grad.applyQuaternion(quat); grad.normalize(); // var up = new THREE.Vector3(0, 0, 1); camera.position.set(0, 0, 0); camera.up.copy(up); camera.lookAt(grad); camera.position.copy(eye); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); }, 20); } } // this.undo = function () { var event = { button: 1 };//中键 switch (cur_tool) { case Tool.measurePoint: doMeasurePoint(mouse, event); break; case Tool.measureLength: doMeasureLength(mouse, event); break; case Tool.measureArea: doMeasureArea(mouse, event); break; case Tool.measureZ: doMeasureZ(mouse, event); break; case Tool.measureFacade: doMeasureFacade(mouse, event); break; case Tool.measureAngle: doMeasureAngle(mouse, event); break; case Tool.measureSlope: doMeasureSlope(mouse, event); break; case Tool.createPoint: doCreatePoint(mouse, event); break; case Tool.createPolyline: doCreatePolyline(mouse, event); break; case Tool.createPolygon: doCreatePolygon(mouse, event); break; case Tool.pick: doPick(mouse, event); break; case Tool.remove: doRemove(mouse, event); break; } } this.finish = function () { var event = { button: 2 };//右键 switch (cur_tool) { case Tool.measurePoint: doMeasurePoint(mouse, event); break; case Tool.measureLength: doMeasureLength(mouse, event); break; case Tool.measureArea: doMeasureArea(mouse, event); break; case Tool.measureZ: doMeasureZ(mouse, event); break; case Tool.measureFacade: doMeasureFacade(mouse, event); break; case Tool.measureAngle: doMeasureAngle(mouse, event); break; case Tool.measureSlope: doMeasureSlope(mouse, event); break; case Tool.createPoint: doCreatePoint(mouse, event); break; case Tool.createPolyline: doCreatePolyline(mouse, event); break; case Tool.createPolygon: doCreatePolygon(mouse, event); break; case Tool.pick: doPick(mouse, event); break; case Tool.remove: doRemove(mouse, event); break; } } // // // this.addLayer = function (def) { var layer = groupLayers.getObjectByName(def.name); if (layer != null) return layer; layer = new THREE.Group(); layer.name = def.name; layer.userData = def; if (def.type == 'Point') { var icon = def.icon; if (icon == null) { icon = ppvPath + 'ppv/icon/disc.png'; } layer.texture = loader.load(icon); } groupLayers.add(layer); return layer; } this.findLayer = function (name) { var layer = groupLayers.getObjectByName(name); if (layer == null) return; return layer; } this.getLayer = function (handle) { var layer = handle; return layer.userData; } this.setLayer = function (handle, def) { var layer = handle; layer.name = def.name; layer.userData = def; if (def.type == 'Point') { var icon = def.icon; if (icon == null) { icon = ppvPath + 'ppv/icon/disc.png'; } layer.texture = loader.load(icon); } //没这么简单,修改图层定义,应该更新已有实体的样式 for (var i = 0; i < layer.children.length; i++) { var fe = layer.children[i]; this.setFeature(fe, fe.userData); } } this.removeLayer = function (handle) { var layer = handle; groupLayers.remove(layer); } this.removeAllLayers = function () { groupLayers.children = []; } // // // this.needsUpdate = false; function parsePoint(layer, def, fe) { var layDef = layer.userData; var lonlat = def.geometry.coordinates; var coord = trb.forward(lonlat); var v = new THREE.Vector3(0, 0, 0); if (def.toGround != null) { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, camera.position.z + def.toGround + ground); } else { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, lonlat[2] - coord_org.z); } var material = new THREE.SpriteMaterial({ map: layer.texture, color: layDef.color, opacity: layDef.opacity, }); // 不要尝试共享Object3D,不行,我试过了 // geometry 和 material可以共享 layer.material = material; var sprite = new THREE.Sprite(layer.material); sprite.position.copy(v); sprite.updateMatrixWorld(true);//注意 fe.add(sprite); // 标注文字 if (def.name != null && def.name.length > 0) { var label = new TextSprite({ text: def.name, offset: [0, -(layDef.fontSize * 1.2 + layDef.size) / 2], style: pref.label } ); label.setStyle({ fontsize: layDef.fontSize }); label.position.copy(v); label.updateMatrixWorld(true);//注意 fe.add(label); } return fe; } function parseMultiPoint(layDef, def, fe) { var mc = def.geometry.coordinates; var cen = new THREE.Vector3(0, 0, 0); var count = 0; for (var m = 0; m < mc.length; m++) { var lonlat = mc[m]; var coord = trb.forward(lonlat); var v = new THREE.Vector3(0, 0, 0); if (def.toGround != null) { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, camera.position.z + def.toGround + ground); } else { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, lonlat[2] - coord_org.z); } // cen.add(v); count++; // 偏移 var offset = new THREE.Vector3(0, 0, 0); if (layDef.offset != null) offset.set(layDef.offset[0] / layDef.size, 0, layDef.offset[1] / layDef.size); if (layDef.icon != null) { var spriteMap = loader.load(layDef.icon); var spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap, color: layDef.color, opacity: layDef.opacity, }); var sprite = new THREE.Sprite(spriteMaterial); sprite.position.copy(offset); var group = new THREE.Group(); group.add(sprite); group.position.copy(v); fe.add(group); } else { var material = new THREE.MeshStandardMaterial({ color: layDef.color, opacity: layDef.opacity, //emissive: 0xff00ff, side: THREE.FrontSide, shading: THREE.SmoothShading }) var bead = new THREE.Mesh(sphereGeometry, material); bead.position.copy(v); fe.add(bead); } } // 标注文字 if (def.name != null && def.name.length > 0) { var label = new TextSprite({ text: def.name, offset: [0, -(layDef.fontSize * 1.2 + layDef.size) / 2], style: pref.label } ); label.setStyle({ fontsize: layDef.fontSize }); cen.x /= count; cen.y /= count; cen.z /= count; label.position.copy(cen); fe.add(label); } return fe; } function parseLineString(layDef, def, fe) { var coordinates = def.geometry.coordinates; var geometry = new THREE.Geometry(); for (var i = 0; i < coordinates.length; i++) { var lonlat = coordinates[i]; var coord = trb.forward(lonlat); if (def.toGround != null) { var v = new THREE.Vector3(coord[0] - coord_org.x, coord[1] - coord_org.y, camera.position.z + def.toGround + ground); geometry.vertices.push(v); } else { var v = new THREE.Vector3(coord[0] - coord_org.x, coord[1] - coord_org.y, lonlat[2] - coord_org.z); geometry.vertices.push(v); } } var material = new THREE.LineBasicMaterial({ color: layDef.color, opacity: layDef.opacity, linewidth: layDef.lineWidth }); var line = new THREE.Line(geometry, material); fe.add(line); // 标注文字 if (def.name != null && def.name.length > 0) { var label = new TextSprite({ text: def.name, style: pref.label } ); label.setStyle({ fontsize: layDef.fontSize }); geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); fe.add(label); } return fe; } function parseMultiLineString(layDef, def, fe) { var mc = def.geometry.coordinates; var cen = new THREE.Vector3(0, 0, 0); var count = 0; for (var m = 0; m < mc.length; m++) { var coordinates = mc[m]; var geometry = new THREE.Geometry(); for (var i = 0; i < coordinates.length; i++) { var lonlat = coordinates[i]; var coord = trb.forward(lonlat); var v = new THREE.Vector3(0, 0, 0); if (def.toGround != null) { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, camera.position.z + def.toGround + ground); } else { v.set(coord[0] - coord_org.x, coord[1] - coord_org.y, lonlat[2] - coord_org.z); } // cen.add(v); count++; // geometry.vertices.push(v); } var material = new THREE.LineBasicMaterial({ color: layDef.color, opacity: layDef.opacity, linewidth: layDef.lineWidth }); var line = new THREE.Line(geometry, material); fe.add(line); } // 标注文字 if (def.name != null && def.name.length > 0) { var label = new TextSprite({ text: def.name, style: pref.label } ); label.setStyle({ fontsize: layDef.fontSize }); cen.x /= count; cen.y /= count; cen.z /= count; label.position.copy(cen); fe.add(label); } return fe; } function parsePolygon(layDef, def, fe) { var coordinates = def.geometry.coordinates; var material = new THREE.LineBasicMaterial({ color: layDef.color, opacity: layDef.opacity, linewidth: layDef.lineWidth }); var shape; for (var r = 0; r < coordinates.length; r++) { var ring = coordinates[r]; var geometry = new THREE.Geometry(); for (var i = 0; i < ring.length; i++) { var lonlat = ring[i]; var coord = trb.forward(lonlat); if (def.toGround != null) { var v = new THREE.Vector3(coord[0] - coord_org.x, coord[1] - coord_org.y, camera.position.z + def.toGround + ground); geometry.vertices.push(v); } else { var v = new THREE.Vector3(coord[0] - coord_org.x, coord[1] - coord_org.y, lonlat[2] - coord_org.z); geometry.vertices.push(v); } } var line = new THREE.Line(geometry, material); fe.add(line); //填充 if (def.toGround != null) { var p1 = geometry.vertices[0]; var vert = []; for (var i = 0; i < geometry.vertices.length; i++) { var p = new THREE.Vector3(); p.copy(geometry.vertices[i]); p.sub(p1); vert.push(p); } if (!shape) { shape = new THREE.Shape(vert); } else { var path = new THREE.Path(vert); shape.holes.push(path); } } } //填充 if (shape) { var fill = new THREE.ShapeBufferGeometry(shape); // 4 构造模型 var material = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); var mesh = new THREE.Mesh(fill, material); mesh.position.copy(p1); fe.add(mesh); } // 标注文字 if (def.name != null && def.name.length > 0) { var label = new TextSprite({ text: def.name, style: pref.label } ); label.setStyle({ fontsize: layDef.fontSize }); var geometry = fe.children[0].geometry; geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); fe.add(label); } return fe; } function parseMultiLineString(layDef, def, fe) { console.warn('N/A: MultiLineString'); } function parseGeometryCollection(layDef, def, fe) { console.warn('N/A: GeometryCollection'); } this.addFeature = function (hlayer, def) { var layer = hlayer; var layDef = layer.userData; var fe = new THREE.Group(); fe.userData = def; //fe.name = def.properties.fid; //fe.name = def.properties.name; var type = def.type; if (type == 'Feature') { var geotype = def.geometry.type; if (geotype == 'Point') { parsePoint(layer, def, fe); } else if (geotype == 'LineString') { parseLineString(layDef, def, fe); } else if (geotype == 'Polygon') { parsePolygon(layDef, def, fe); } else if (geotype == 'MultiPoint') { parseMultiPoint(layDef, def, fe); } else if (geotype == 'MultiLineString') { parseMultiLineString(layDef, def, fe); } else if (geotype == 'MultiPolygon') { parseMultiPolygon(layDef, def, fe); } else if (geotype == 'GeometryCollection') { parseGeometryCollection(layDef, def, fe); } } else if (type == 'FeatureCollection') { } layer.add(fe); // 添加到图层中 return fe; } this.findFeature = function (fid) { for (var i = 0; i < groupLayers.children.length; i++) { var layer = groupLayers.children[i]; for (var j = 0; j < layer.children.length; j++) { var fe = layer.children[j]; if (fe.userData.fid == fid) return fe; } } } this.getFeature = function (handle) { var fe = handle; return fe.userData; } this.setFeature = function (handle, def) { var fe = handle; fe.children = []; //和addFeature几乎一样 var layer = fe.parent; var layDef = layer.userData; fe.userData = def; //fe.name = def.properties.fid; //fe.name = def.properties.name; var type = def.type; if (type == 'Feature') { var geotype = def.geometry.type; if (geotype == 'Point') { parsePoint(layer, def, fe); } else if (geotype == 'LineString') { parseLineString(layDef, def, fe); } else if (geotype == 'Polygon') { parsePolygon(layDef, def, fe); } else if (geotype == 'MultiPoint') { parseMultiPoint(layDef, def, fe); } else if (geotype == 'MultiLineString') { parseMultiLineString(layDef, def, fe); } else if (geotype == 'MultiPolygon') { parseMultiPolygon(layDef, def, fe); } else if (geotype == 'GeometryCollection') { parseGeometryCollection(layDef, def, fe); } } else if (type == 'FeatureCollection') { } if (selectFeature == fe) { highlight(selectFeature); } return fe; } this.removeFeature = function (handle) { var fe = handle; fe.parent.remove(fe); } this.removeAllFeatures = function (hlayer) { var layer = hlayer; layer.children = []; } this.selectFeature = function (handle) { var fe = handle; if (selectFeature != fe) { if (selectFeature) selectFeature.children[0].material.color.setHex(selectFeature.children[0].currentHex); selectFeature = fe; selectFeature.children[0].currentHex = selectFeature.children[0].material.color.getHex(); selectFeature.children[0].material.color.setHex(0xff0000); } } this.visible = function (handle, visible) { handle.visible = visible; } // // // this.onLocate = function (event) { } this.onPosition = function (event) { } this.onEye = function (event) { } this.onFeatureCreate = function (event) { } this.onFeatureSelect = function (event) { } this.onFeatureRemove = function (event) { } this.onMeasure = function (event) { } this.onTool = function (event) { } this.onRender = function (event) { } //归一化屏幕坐标,raycaster要用 // pos已经是左下角坐标系了 // 他要做两件事 function normalizePos(pos) { if (pref.magnifier.fix) { //renderer.setViewport(magPos.x-half,magPos.y-half, pref.magnifier.size, pref.magnifier.size); var vp = { x: magPos.x - pref.magnifier.size * 0.5, y: magPos.y - pref.magnifier.size * 0.5, w: pref.magnifier.size, h: pref.magnifier.size }; var posNorm = new THREE.Vector2(); posNorm.x = ((pos.x - vp.x) / vp.w) * 2 - 1; posNorm.y = ((pos.y - vp.y) / vp.h) * 2 - 1; return posNorm; } else { // var posZoom = new THREE.Vector2(magPos.x + (pos.x - magPos.x) / pref.magnifier.zoom, magPos.y + (pos.y - magPos.y) / pref.magnifier.zoom); var ret = new THREE.Vector2(); posZoom.x = (posZoom.x / div.clientWidth) * 2 - 1; posZoom.y = (posZoom.y / div.clientHeight) * 2 - 1; return posZoom; } } function updateMousePosition(event) { mouse.x = (event.offsetX / div.clientWidth) * 2 - 1; mouse.y = - (event.offsetY / div.clientHeight) * 2 + 1; } // touch消息里没有offseXY数值,我得想办法从其他数值计算出来 // 以前我记得做过,好像是要遍历父子链 function getDivPos(div) { var pos = {}; pos.x = div.offsetLeft; pos.y = div.offsetTop; while (div.offsetParent != null) { div = div.offsetParent; pos.x += div.offsetLeft; pos.y += div.offsetTop; } return pos; } function calcOffset(event) { if (event.offsetX != null) return; var pos = getDivPos(event.target); event.offsetX = event.clientX - pos.x + document.body.scrollLeft; event.offsetY = event.clientY - pos.y + document.body.scrollTop; } function projectedRadius(radius, fov, distance, screenHeight) { var projFactor = (1 / Math.tan(fov / 2)) / distance; projFactor = projFactor * screenHeight / 2; return radius * projFactor; }; function pickPoint(mouse, callback) { switch (sampleMode) { case SampleMode.photo: samplePhoto(mouse, callback); break; case SampleMode.ground: sampleGround(mouse, callback); break; case SampleMode.depth: sampleDepth(mouse, callback); break; } } function sampleDepth(mouse, callback) { // 1 得到鼠标点击射线 if (magnifier.visible) { raycaster.setFromCamera(mouseMag, magnifier); } else { raycaster.setFromCamera(mouse, camera); } var ray = raycaster.ray; // 2 目标是将dir(模拟一个点云上的点)从模型坐标系转换成点云坐标系 var quat = new THREE.Quaternion; quat.setFromEuler(camgroup.rotation); quat.inverse(); ray.direction.applyQuaternion(quat); // 3 调用webservice,在服务端计算,从深度图采样 //cur_frame_id //device_type // 这个方法,如果失败,会回调error函数 $.postJSON( server + "/SarchDepth", { type: device_type, id: cur_frame_id, v0: ray.direction.x, v1: ray.direction.y, v2: ray.direction.z }, function (ret) { console.log(ret.d); // 4 正转,按深度lerp //double len = (65535-p)*dm->range/65536; //z::mat::lerp(es_360->pose->pos, dir2, len, v); var res_v = $.parseJSON(ret.d); //jQuery,支持很好 var depthRange = 100;//这里有问题 var len = (65535 - res_v.depth) * depthRange / 65536; var tag = new THREE.Vector3(res_v.v0 * len, res_v.v1 * len, res_v.v2 * len); tag.applyEuler(camgroup.rotation); tag.add(camgroup.position); callback(tag); }, function error(x, e) { console.warn("SarchDepth error"); } ); } function samplePhoto(mouse, callback) { if (magnifier.visible) { raycaster.setFromCamera(mouseMag, magnifier); } else { raycaster.setFromCamera(mouse, camera); } var ray = raycaster.ray; if (groupRay.children.length == 0) { // 准备顶点坐标 var vertices = [ new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0) ]; var tar = new THREE.Vector3(); tar.copy(ray.direction); tar.setLength(100); vertices[0].copy(camera.position); vertices[1].copy(camera.position).add(tar); // var material = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 1, linewidth: 3, vertexColors: THREE.VertexColors }); var geometry = new THREE.Geometry(); geometry.vertices = vertices; geometry.colors.push( new THREE.Color(0x00ff00), new THREE.Color(0xff0000) ); var line = new THREE.Line(geometry, material); groupRay.add(line); // 自动后退一帧 var id = getFrameID(-1); load_frame(id); } else { var geo = groupRay.children[0].geometry; var per = [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0)]; var dist = ray.distanceSqToSegment(geo.vertices[0], geo.vertices[1], per[0], per[1]); var mid = per[0].lerp(per[1], 0.5); //清空辅助射线 groupRay.children = []; // bead /* var bead = new THREE.Mesh( sphereGeometry, sphereMaterial ); groupBead.children = []; groupBead.add( group ); */ // 自动回到上一帧 var id = getFrameID(1); load_frame(id); /* if (mid.distanceTo(camera.position) > sampleDistance) { alert('距离超限'); return; } */ callback(mid); } } function sampleGround(mouse, callback) { if (magnifier.visible) { raycaster.setFromCamera(mouseMag, magnifier); } else { raycaster.setFromCamera(mouse, camera); } var ray = raycaster.ray; // 注意,第二个参数指的是,第一个参数所定义的方向的反方向 var plane = new THREE.Plane(new THREE.Vector3(0, 0, -1), camera.position.z + ground); var pos = ray.intersectPlane(plane); /* if (pos.distanceTo(camera.position) > sampleDistance) { alert('距离超限'); return; } */ if (pos != null) callback(pos); } // 递归高亮颜色 function highlight(group) { for (var i = 0; i < group.children.length; i++) { var obj = group.children[i]; if (obj.material) { obj.currentHex = obj.material.color.getHex(); obj.material.color.setHex(0xff0000); } highlight(obj); } } function clearHighlight(group) { for (var i = 0; i < group.children.length; i++) { var obj = group.children[i]; if (obj.material) { obj.material.color.setHex(obj.currentHex); } clearHighlight(obj); } } function doPick(mouse, event) { if (magnifier.visible) { raycaster.setFromCamera(mouseMag, magnifier); } else { raycaster.setFromCamera(mouse, camera); } var intersects = raycaster.intersectObject(groupLayers, true); if (intersects.length > 0) { var fe = intersects[0].object; while (!fe.userData.geometry) { fe = fe.parent; } if (selectFeature != fe) { // 恢复正常颜色 if (selectFeature) { clearHighlight(selectFeature); } selectFeature = fe; // 高亮颜色 highlight(selectFeature); // var event = { layer: fe.parent, feature: fe, layername: fe.parent.name, fid: fe.userData.fid, }; self.onFeatureSelect(event); } } else { // 恢复正常颜色 if (selectFeature) { clearHighlight(selectFeature); } selectFeature = null; } } function doRemove(mouse, event) { switch (event.button) { case 0://左键 { doPick(mouse, event); } break; case 1://中键 { } break; case 2://右键 { if (selectFeature) { var fe = selectFeature; var event = { layer: fe.parent, feature: fe, layername: fe.parent.name, fid: fe.userData.fid, }; self.onFeatureRemove(event); if (event.cancel) return; self.removeFeature(fe); selectFeature = null; } } break; } } function doCreatePoint(mouse, event) { pickPoint(mouse, function (mid) { var coord = [mid.x + coord_org.x, mid.y + coord_org.y]; var lonlat = trb.inverse(coord); lonlat[2] = mid.z + coord_org.z; var def = { 'type': 'Feature', 'geometry': { 'type': 'Point', 'coordinates': lonlat, } }; resetOSG(); self.onFeatureCreate(def); }); } function doCreatePolyline(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); //geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); //geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.pop(); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); } } break; case 2://右键 { if (groupTrack.children.length == 0) return; var geometry = groupTrack.children[0].geometry; if (geometry.vertices.length < 2) return; var coordinates = []; for (var i = 0; i < geometry.vertices.length; i++) { var v = geometry.vertices[i]; var coord = [v.x + coord_org.x, v.y + coord_org.y]; var lonlat = trb.inverse(coord); lonlat[2] = v.z + coord_org.z; coordinates[i] = lonlat; } var def = { 'type': 'Feature', 'geometry': { 'type': 'LineString', 'coordinates': coordinates, } }; resetOSG(); self.onFeatureCreate(def); } break; } } function doCreatePolygon(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.pop(); geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.pop(); geometry.vertices.pop(); geometry.vertices.push(geometry.vertices[0]);//close var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); } } break; case 2://右键 { if (groupTrack.children.length == 0) return; var geometry = groupTrack.children[0].geometry; if (geometry.vertices.length < 2) return; var ring = []; for (var i = 0; i < geometry.vertices.length; i++) { var v = geometry.vertices[i]; var coord = [v.x + coord_org.x, v.y + coord_org.y]; var lonlat = trb.inverse(coord); lonlat[2] = v.z + coord_org.z; ring[i] = lonlat; } var def = { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [ring], } }; resetOSG(); self.onFeatureCreate(def); } break; } } function doMeasurePoint(mouse, event) { pickPoint(mouse, function (mid) { //标注文字 var coord = [mid.x + coord_org.x, mid.y + coord_org.y, mid.z + coord_org.z]; var lonlat = trb.inverse(coord); var lonlatStr = 'lon ' + lonlat[0].toFixed(8) + "° lat " + lonlat[1].toFixed(8) + '° alt ' + coord[2].toFixed(2) + ' m'; var label = new TextSprite({ text: lonlatStr, offset: [0, pref.label.fontsize * 1.2], style: pref.label } ); label.position.copy(mid); /* var label = new ztext({ text:lonlatStr, //offset:[0, pref.label.fontsize*1.2], style:pref.label} , div); //label.position.copy(mid); */ resetGroupLabel(); groupLabel.add(label); // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); // 这个功能是可以工作的,但是需要有geometry,对空的object3D不会触发这个回调函数 /* bead.onBeforeRender = function(renderer, scene, camera, geometry, material, group){ var text = ""; this.children[0].updatePosition(renderer, camera); this.children[0].show(); } bead.onAfterRender = function(renderer, scene, camera, geometry, material, group){ //var text = ""; //this.children[0].hide(); } bead.add(label); */ bead.position.copy(mid); groupBead.children = []; groupBead.add(bead); // var event = { lonlat: [lonlat[0], lonlat[1], mid.z + coord_org.z] }; self.onMeasure(event); }); } function doMeasureLength(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3, }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3, }); var geometry = new THREE.Geometry(); //geometry.copy(groupTrack.children[0].geometry); //不能使用上面那句话,虽然含义好像是正确的,但会导致某些角度,对象消失不见 geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); //标注文字 if (geometry.vertices.length > 1) { var length = 0; for (var i = 1; i < geometry.vertices.length; i++) { var v1 = geometry.vertices[i - 1]; var v2 = geometry.vertices[i]; length += v1.distanceTo(v2); } var lengthStr = length.toFixed(2) + ' m'; var label = new TextSprite({ text: lengthStr, style: pref.label } ); geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { length: length }; self.onMeasure(event); } else { resetGroupLabel(); } } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.pop(); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); //标注文字 if (geometry.vertices.length > 1) { var length = 0; for (var i = 1; i < geometry.vertices.length; i++) { var v1 = geometry.vertices[i - 1]; var v2 = geometry.vertices[i]; length += v1.distanceTo(v2); } var lengthStr = length.toFixed(2) + ' m'; var label = new TextSprite({ text: lengthStr, style: pref.label } ); geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { length: length }; self.onMeasure(event); } else { resetGroupLabel(); } } } break; case 2://右键 { } break; } } // 给定一个方向向量,获得一对旋转变换,正向是从基准面(基准轴)方向转换到dir方向 // 返回值是正向变换 // quatRev 可选,是反向变换 // lockZ 表示是否锁定z轴为旋转轴 // from 表示基准面(基准轴),缺省是y轴。如果想选择xy平面,可以输入'z'或'xy' // 这个函数是为了替代quaternion.setFromUnitVectors // 因为他在v1==v2时,会产生奇异值(无解) function getRotFromDir(dir, quatRev, lockZ, from) { var up = new THREE.Vector3(0, 0, 0); var side = new THREE.Vector3(0, 0, 0); if (lockZ) { up.z = 1; } else { // 选择一个合适的up var a = [Math.abs(dir.x), Math.abs(dir.y), Math.abs(dir.z)]; if (a[0] < a[1]) { if (a[0] < a[2]) up.x = 1; else up.z = 1; } else { if (a[1] < a[2]) up.y = 1; else up.z = 1; } } side.crossVectors(dir, up).normalize(); up.crossVectors(side, dir).normalize(); var m = new THREE.Matrix4(); if (from != null) { if (from == 'z' || from == 'xy') m.makeBasis(up, side, dir); else if (from == 'y' || from == 'xz') m.makeBasis(side, dir, up); else if (from == 'x' || from == 'yz') m.makeBasis(dir, up, side); } else { m.makeBasis(side, dir, up); } var quat = new THREE.Quaternion(); quat.setFromRotationMatrix(m); if (quatRev != null) { var mi = new THREE.Matrix4(); mi.getInverse(m); quatRev.setFromRotationMatrix(mi); } return quat; } function doMeasureArea(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); if (geometry.vertices.length < 2) { geometry.vertices.push(mid); } else if (geometry.vertices.length == 2) { geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close } else//>2 { geometry.vertices.pop(); geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close } var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 只有当顶点数达到三个以后,才能创建面 // 1 根据前三个点,计算出一个平面来 // 2 旋转该平面xy平面,构造一个旋转变换 // 3 变换全部顶点 // 4 用二维坐标创建shape var size = geometry.vertices.length; if (size > 3) { // 1 计算平面 var p1 = geometry.vertices[0]; var p2 = geometry.vertices[1]; var p3 = geometry.vertices[2]; var v1 = new THREE.Vector3(); var v2 = new THREE.Vector3(); var dir = new THREE.Vector3(); v1.subVectors(p2, p1).normalize(); v2.subVectors(p3, p1).normalize(); dir.crossVectors(v1, v2).normalize(); var plane = new THREE.Plane(); plane.setFromNormalAndCoplanarPoint(dir, p1); // 2 仅仅根据一个方向向量,获得一个旋转变换 var quatRev = new THREE.Quaternion(); var quat = getRotFromDir(dir, quatRev, null, 'z'); // 3 旋转到xy平面 var vert = []; for (var i = 0; i < geometry.vertices.length; i++) { var p = new THREE.Vector3(); p.copy(geometry.vertices[i]); p.sub(p1); p.applyQuaternion(quatRev); vert.push(new THREE.Vector2(p.x, p.y)); } /* var shape = new THREE.Shape(vert); // var fill = new THREE.ShapeBufferGeometry( shape ); // 4 构造模型 var material = new THREE.MeshBasicMaterial( { color: 0x00ffff, transparent: true, opacity:0.5, side:THREE.DoubleSide } ); var mesh = new THREE.Mesh( fill, material); mesh.position.copy(p1); mesh.quaternion.copy(quat); line.add( mesh ); //groupTrack.add( mesh ); */ //标注文字 var area = Math.abs(THREE.ShapeUtils.area(vert)); var areaStr = area.toFixed(2) + ' m²'; geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); var label = new TextSprite({ text: areaStr, style: pref.label } ); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { area: area }; self.onMeasure(event); } } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); if (geometry.vertices.length > 4) { geometry.vertices.pop(); geometry.vertices.pop(); geometry.vertices.push(geometry.vertices[0]);//close } else if (geometry.vertices.length > 3) { geometry.vertices.pop(); geometry.vertices.pop(); } else { geometry.vertices.pop(); } var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); //重新构造填充面 if (geometry.vertices.length > 3) { // 1 计算平面 var p1 = geometry.vertices[0]; var p2 = geometry.vertices[1]; var p3 = geometry.vertices[2]; var v1 = new THREE.Vector3(); var v2 = new THREE.Vector3(); var dir = new THREE.Vector3(); v1.subVectors(p2, p1); v1.normalize(); v2.subVectors(p3, p1); v2.normalize(); dir.crossVectors(v1, v2); dir.normalize(); var plane = new THREE.Plane(); plane.setFromNormalAndCoplanarPoint(dir, p1); // 2 仅仅根据一个方向向量,获得一个旋转变换 var quatRev = new THREE.Quaternion(); var quat = getRotFromDir(dir, quatRev, null, 'z'); // 3 旋转到xy平面 var vert = []; for (var i = 0; i < geometry.vertices.length; i++) { var p = new THREE.Vector3(); p.copy(geometry.vertices[i]); p.sub(p1); p.applyQuaternion(quatRev); vert.push(new THREE.Vector2(p.x, p.y)); } var shape = new THREE.Shape(vert); // var fill = new THREE.ShapeBufferGeometry(shape); // 4 构造模型 var material = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); var mesh = new THREE.Mesh(fill, material); mesh.position.copy(p1); mesh.quaternion.copy(quat); line.add(mesh); //标注文字 var area = Math.abs(THREE.ShapeUtils.area(vert)); var areaStr = area.toFixed(2) + ' m²'; geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); var label = new TextSprite({ text: areaStr, style: pref.label } ); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { area: area }; self.onMeasure(event); } else { resetGroupLabel(); } } } break; case 2://右键 { } break; } } function doMeasureZ(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); groupBead.add(bead); } else { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); var v0 = groupTrack.children[0].geometry.vertices[0]; // var v1;//较高 var v2; if (v0.z > mid.z) { v1 = v0; v2 = mid; } else { v1 = mid; v2 = v0; } var v3 = new THREE.Vector3(v1.x, v1.y, v2.z); geometry.vertices.push(v1); geometry.vertices.push(v2); geometry.vertices.push(v3); geometry.vertices.push(v1); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // // bead groupBead.children = []; { var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(v1); groupBead.add(bead); } { var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(v2); groupBead.add(bead); } //标注文字 resetGroupLabel(); var event = {}; { var length = v1.distanceTo(v2); var lengthStr = length.toFixed(2) + ' m'; event.length = length; var label = new TextSprite({ text: lengthStr, style: pref.label } ); //var cen = new THREE.Vector3(); //cen.lerpVectors(v1, v2, 0.5); label.position.copy(v1); groupLabel.add(label); } { var length = v2.distanceTo(v3); var lengthStr = length.toFixed(2) + ' m'; event.lengthXY = length; var label = new TextSprite({ text: lengthStr, style: pref.label } ); var cen = new THREE.Vector3(); cen.lerpVectors(v2, v3, 0.5); label.position.copy(cen); groupLabel.add(label); } { var length = v1.distanceTo(v3); var lengthStr = length.toFixed(2) + ' m'; event.lengthZ = length; var label = new TextSprite({ text: lengthStr, style: pref.label } ); var cen = new THREE.Vector3(); cen.lerpVectors(v1, v3, 0.5); label.position.copy(cen); groupLabel.add(label); } // self.onMeasure(event); } }); } break; case 1://中键 { if (groupTrack.children.length > 0) { // 删除geometry groupTrack.children = []; // 删除bead groupBead.children = []; // 删除标注文字 resetGroupLabel(); } } break; case 2://右键 { } break; } } function doMeasureFacade(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); groupBead.add(bead); } else { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); var v1 = groupTrack.children[0].geometry.vertices[0]; var v2 = mid; var v3 = new THREE.Vector3(v1.x, v1.y, v2.z); var v4 = new THREE.Vector3(v2.x, v2.y, v1.z); geometry.vertices.push(v1); geometry.vertices.push(v3); geometry.vertices.push(v2); geometry.vertices.push(v4); geometry.vertices.push(v1); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // // bead groupBead.children = []; { var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(v1); groupBead.add(bead); } { var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(v2); groupBead.add(bead); } //标注文字 resetGroupLabel(); var event = {}; { var length = v1.distanceTo(v3); var lengthStr = length.toFixed(2) + ' m'; event.height = length; var label = new TextSprite({ text: lengthStr, style: pref.label } ); var cen = new THREE.Vector3(); cen.lerpVectors(v1, v3, 0.5); label.position.copy(cen); groupLabel.add(label); } { var length = v1.distanceTo(v4); var lengthStr = length.toFixed(2) + ' m'; event.width = length; var label = new TextSprite({ text: lengthStr, style: pref.label } ); var cen = new THREE.Vector3(); cen.lerpVectors(v1, v4, 0.5); label.position.copy(cen); groupLabel.add(label); } { // 1 计算平面 var p1 = v1; var side = new THREE.Vector3(); var up = new THREE.Vector3(); var dir = new THREE.Vector3(); side.subVectors(v4, v1); side.normalize(); up.subVectors(v3, v1); up.normalize(); dir.crossVectors(side, up); dir.normalize(); var plane = new THREE.Plane(); plane.setFromNormalAndCoplanarPoint(dir, v1); // 2 仅仅根据一个方向向量,获得一个旋转变换 var quatRev = new THREE.Quaternion(); var quat = getRotFromDir(dir, quatRev, null, 'z'); // 3 旋转到xy平面 var vert = []; for (var i = 0; i < geometry.vertices.length; i++) { var p = new THREE.Vector3(); p.copy(geometry.vertices[i]); p.sub(p1); p.applyQuaternion(quatRev); vert.push(new THREE.Vector2(p.x, p.y)); } var shape = new THREE.Shape(vert); // var fill = new THREE.ShapeBufferGeometry(shape); // 4 构造模型 var material = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); var mesh = new THREE.Mesh(fill, material); mesh.position.copy(p1); mesh.quaternion.copy(quat); line.add(mesh); // label var area = Math.abs(THREE.ShapeUtils.area(vert)); var areaStr = area.toFixed(2) + ' m²'; event.area = area; var label = new TextSprite({ text: areaStr, style: pref.label } ); var cen = new THREE.Vector3(); cen.lerpVectors(v1, v2, 0.5); label.position.copy(cen); groupLabel.add(label); } // self.onMeasure(event); } }); } break; case 1://中键 { if (groupTrack.children.length > 0) { groupTrack.children = []; // 删除bead groupBead.children = []; //标注文字 resetGroupLabel(); } } break; case 2://右键 { } break; } } function doMeasureAngle(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); if (groupTrack.children[0].geometry.vertices.length == 3)//重置,重新开始 groupBead.children = []; else geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); //标注文字 if (geometry.vertices.length == 3) { var p1 = geometry.vertices[0]; var p2 = geometry.vertices[1]; var p3 = geometry.vertices[2]; var v1 = new THREE.Vector3(); var v2 = new THREE.Vector3(); v1.subVectors(p1, p2); v2.subVectors(p3, p2); var angle = v1.angleTo(v2) * 180 / Math.PI; var angleStr = angle.toFixed(2) + '°'; var label = new TextSprite({ text: angleStr, style: pref.label } ); geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { angle: angle }; self.onMeasure(event); } else { resetGroupLabel(); } } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); geometry.vertices.pop(); var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); //标注文字 resetGroupLabel(); } } break; case 2://右键 { } break; } } function doMeasureSlope(mouse, event) { switch (event.button) { case 0://左键 { pickPoint(mouse, function (mid) { if (groupTrack.children.length == 0) { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices.push(mid); var line = new THREE.Line(geometry, material); groupTrack.add(line); } else { var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); if (groupTrack.children[0].geometry.vertices.length == 4)//重置,重新开始 groupBead.children = []; else geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); if (geometry.vertices.length < 2) { geometry.vertices.push(mid); } else if (geometry.vertices.length == 2) { geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close } else//>2 { geometry.vertices.pop(); geometry.vertices.push(mid); geometry.vertices.push(geometry.vertices[0]);//close } var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); //标注文字 if (geometry.vertices.length == 4) { // 1 计算平面 var p1 = geometry.vertices[0]; var p2 = geometry.vertices[1]; var p3 = geometry.vertices[2]; var v1 = new THREE.Vector3(); var v2 = new THREE.Vector3(); var dir = new THREE.Vector3(); v1.subVectors(p2, p1); v1.normalize(); v2.subVectors(p3, p1); v2.normalize(); dir.crossVectors(v1, v2); dir.normalize(); var plane = new THREE.Plane(); plane.setFromNormalAndCoplanarPoint(dir, p1); // 2 仅仅根据一个方向向量,获得一个旋转变换 var quatRev = new THREE.Quaternion(); var quat = getRotFromDir(dir, quatRev, null, 'z'); // 3 旋转到xy平面 var vert = []; for (var i = 0; i < geometry.vertices.length; i++) { var p = new THREE.Vector3(); p.copy(geometry.vertices[i]); p.sub(p1); p.applyQuaternion(quatRev); vert.push(new THREE.Vector2(p.x, p.y)); } var shape = new THREE.Shape(vert); // var fill = new THREE.ShapeBufferGeometry(shape); // 4 构造模型 var material = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); var mesh = new THREE.Mesh(fill, material); mesh.position.copy(p1); mesh.quaternion.copy(quat); line.add(mesh); // label var z = new THREE.Vector3(0, 0, 1); var angle = dir.angleTo(z) * 180 / Math.PI; var angleStr = angle.toFixed(2) + '°'; var label = new TextSprite({ text: angleStr, style: pref.label } ); geometry.computeBoundingBox(); var cen = geometry.boundingBox.getCenter(); label.position.copy(cen); resetGroupLabel(); groupLabel.add(label); // var event = { slope: angle }; self.onMeasure(event); } else { resetGroupLabel(); } } // bead var bead = new THREE.Mesh(sphereGeometry, sphereMaterial); bead.position.copy(mid); //groupBead.children = []; groupBead.add(bead); }); } break; case 1://中键 { if (groupTrack.children.length > 0) { //如果想动态改变buf长度,很麻烦。所以我想,还不如新建一个对象 var material = new THREE.LineBasicMaterial({ color: 0xff00ff, opacity: 1, linewidth: 3 }); var geometry = new THREE.Geometry(); geometry.vertices = groupTrack.children[0].geometry.vertices.slice(0); if (geometry.vertices.length > 4) { geometry.vertices.pop(); geometry.vertices.pop(); geometry.vertices.push(geometry.vertices[0]);//close } else if (geometry.vertices.length > 3) { geometry.vertices.pop(); geometry.vertices.pop(); } else { geometry.vertices.pop(); } var line = new THREE.Line(geometry, material); groupTrack.children = []; groupTrack.add(line); // 删除bead最后一个节点 groupBead.children.pop(); //标注文字 resetGroupLabel(); } } break; case 2://右键 { } break; } } function resetGroupLabel() { /* for (var i=0;i pref.scope){ fe.visible = false; break; } */ var pr = projectedRadius(1, camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); //var scale = (layDef.fontSize / pr); // 青岛定制 var scale = (120 / pr); obj.scale.set(scale, scale, scale); } else if (obj instanceof THREE.Sprite)//点图标 { var distance = camera.position.distanceTo(obj.getWorldPosition()); if (distance > pref.scope) { fe.visible = false; break; } var pr = projectedRadius(1, camera.fov * Math.PI / 180, distance, renderer.domElement.clientHeight); var scale = (layDef.size / pr); obj.scale.set(scale, scale, scale); } } } } if (busy) { groupArrow.visible = false; groupHistory.visible = false; } else { groupArrow.visible = pref.enableArrow; groupHistory.visible = pref.enableHistory; } var size = renderer.getSize(); var scissor = renderer.userData; renderer.setViewport(0, 0, size.width, size.height); renderer.setScissor(scissor.left, scissor.top, scissor.width, scissor.height); renderer.setScissorTest(true); renderer.render(scene, camera); var event = { canvas: renderer.domElement, }; self.onRender(event); // if (magnifier.visible) { //隐藏一部分辅助物体 groupBead.visibleTemp = groupBead.visible; groupTrack.visibleTemp = groupTrack.visible; groupArrow.visibleTemp = groupArrow.visible; groupHistory.visibleTemp = groupHistory.visible; groupFace.visibleTemp = groupFace.visible; groupBead.visible = false; groupTrack.visible = false; groupArrow.visible = false; groupHistory.visible = false; groupFace.visible = false; // var half = pref.magnifier.size / 2; if (pref.magnifier.fix) { renderer.setViewport(magPos.x - half, magPos.y - half, pref.magnifier.size, pref.magnifier.size); renderer.setScissor(magPos.x - half, magPos.y - half, pref.magnifier.size, pref.magnifier.size); } else { renderer.setViewport(mousePos.x - half, mousePos.y - half, pref.magnifier.size, pref.magnifier.size); renderer.setScissor(mousePos.x - half, mousePos.y - half, pref.magnifier.size, pref.magnifier.size); } renderer.setScissorTest(true); renderer.render(scene, magnifier); // 恢复辅助物体 groupBead.visible = groupBead.visibleTemp; groupTrack.visible = groupTrack.visibleTemp; groupArrow.visible = groupArrow.visibleTemp; groupHistory.visible = groupHistory.visibleTemp; groupFace.visible = groupFace.visibleTemp; } } function is360() { if (camgroup.children.length > 0 && camgroup.children[0] == camballMesh) return true; else return false; } function onResize() { var fullscreen = isFullscreen(); var width = div.clientWidth; var height = div.clientHeight; //为什么不直接用scene.view->aspect,因为首次加载工程时,view参数都还没设置 var viewAspect = width / height; var scissor = { top: 0, left: 0, width: width, height: height, }; renderer.userData = scissor; camera.aspect = viewAspect; if (is360()) { } else { var sh = sensorHeight;//=sensorWidth / imajAspect; switch (pref.fullView) { case FullMode.fill: { if (viewAspect > imajAspect) {//如果窗口比较宽 } else { sh = sensorWidth / viewAspect; } camera.aspect = viewAspect; } break; case FullMode.trans: { if (viewAspect > imajAspect) {//如果窗口比较宽 scissor.left = (width - height * imajAspect) / 2; scissor.width = height * imajAspect; } else { scissor.top = (height - width / imajAspect) / 2; scissor.height = width / imajAspect; sh = sensorWidth / viewAspect; } } break; case FullMode.clip: { if (viewAspect > imajAspect) {//如果窗口比较宽 sh = sensorWidth / viewAspect; } } break; case FullMode.stretch: { camera.aspect = imajAspect; } break; } var fovy = 2 * (Math.atan(sh / 2 / focal)) * 180 / Math.PI; camera.fov = fovy; } camera.updateProjectionMatrix(); // if (magnifier.visible) { magnifier.fov = camera.fov * pref.magnifier.size / height / pref.magnifier.zoom; magnifier.updateProjectionMatrix(); } // renderer.setSize(width, height); } function launchIntoFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } } function exitFullscreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } function isFullscreen() { var a = document.mozFullScreen; var b = document.webkitIsFullScreen; var c = document.fullscreen; var d = document.msFullscreen; return a || b || c || d; } // function onKeyDown(event) { switch (event.keyCode) { case 27: //esc { setTool(Tool.none); } break; case pref.key.del: if (selectFeature) { var fe = selectFeature; var event = { layer: fe.parent, feature: fe, layername: fe.parent.name, fid: fe.userData.fid, }; self.onFeatureRemove(event); if (event.cancel) return; self.removeFeature(fe); selectFeature = null; } break; case pref.key.play: { if (playing) self.stop(); else self.play(); } break; case pref.key.fforward: { self.step(10); } break; case pref.key.fbackward: { self.step(-10); } break; case pref.key.forward: { self.step(1); } break; case pref.key.backward: { self.step(-1); } break; case pref.key.fullscreen: { if (isFullscreen()) exitFullscreen(); else launchIntoFullscreen(div); } break; } } function onContextMenu(event) { event.preventDefault(); } function onTouch(event) { event.preventDefault(); if (event.originalEvent.touches.length == 1) { event = event.originalEvent.touches[0]; // 重点touch消息里没有offseXY数值,我得想办法从其他数值计算出来 calcOffset(event); //down { updateMousePosition(event); control.enabled = is360(); tap = true; drag = false; ptLast.x = event.offsetX; ptLast.y = event.offsetY; } //up { drag = false; if (!tap) return; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObject(groupArrow, true); if (intersects.length > 0) { if (intersects[0].object.name == 'history') { showHistory(intersects[0].object.userData); } else { var id = intersects[0].object.name; load_frame(id); } } else { console.debug("onPPVisionMouseUp cur_tool: %d", cur_tool); switch (cur_tool) { case Tool.none: doMoveTo(mouse, event); break; case Tool.measurePoint: doMeasurePoint(mouse, event); break; case Tool.measureLength: doMeasureLength(mouse, event); break; case Tool.measureArea: doMeasureArea(mouse, event); break; case Tool.measureZ: doMeasureZ(mouse, event); break; case Tool.measureFacade: doMeasureFacade(mouse, event); break; case Tool.measureAngle: doMeasureAngle(mouse, event); break; case Tool.measureSlope: doMeasureSlope(mouse, event); break; case Tool.createPoint: doCreatePoint(mouse, event); break; case Tool.createPolyline: doCreatePolyline(mouse, event); break; case Tool.createPolygon: doCreatePolygon(mouse, event); break; case Tool.pick: doPick(mouse, event); break; case Tool.remove: doRemove(mouse, event); break; } } } } } var tap = false; var drag = false; var ptLast = { x: 0, y: 0 }; function onMouseDown(event) { event.preventDefault(); updateMousePosition(event); control.enabled = is360(); tap = true; drag = false; ptLast.x = event.offsetX; ptLast.y = event.offsetY; } function onMouseUp(event) { event.preventDefault(); updateMousePosition(event); drag = false; if (!tap) return; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObject(groupArrow, true); if (intersects.length > 0) { if (intersects[0].object.name == 'history') { showHistory(intersects[0].object.userData); } else { var id = intersects[0].object.name; load_frame(id); } } else { console.debug("onPPVisionMouseUp cur_tool: %d", cur_tool); switch (cur_tool) { case Tool.none: doMoveTo(mouse, event); break; case Tool.measurePoint: doMeasurePoint(mouse, event); break; case Tool.measureLength: doMeasureLength(mouse, event); break; case Tool.measureArea: doMeasureArea(mouse, event); break; case Tool.measureZ: doMeasureZ(mouse, event); break; case Tool.measureFacade: doMeasureFacade(mouse, event); break; case Tool.measureAngle: doMeasureAngle(mouse, event); break; case Tool.measureSlope: doMeasureSlope(mouse, event); break; case Tool.createPoint: doCreatePoint(mouse, event); break; case Tool.createPolyline: doCreatePolyline(mouse, event); break; case Tool.createPolygon: doCreatePolygon(mouse, event); break; case Tool.pick: doPick(mouse, event); break; case Tool.remove: doRemove(mouse, event); break; } } } function onMouseMove(event) { event.preventDefault(); updateMousePosition(event); // event.button\buttons\which和按键有关 // 但没有按键时,button也等于0,和左键冲突 // buttons=1、4、2,好像是ie的定义 // which=1、2、3,比较正常 if (event.buttons > 0)//左中右健按下,意味着开始拖拽 { if (!drag) { var dx = event.offsetX - ptLast.x; var dy = event.offsetY - ptLast.y; if (Math.abs(dx) > 3 || Math.abs(dy) > 3) { drag = true; tap = false; } } } // 拾取箭头、探面、放大镜,三者之间可能有互斥,以及优先级关系 // 放大镜优先级最高,启用放大镜时,禁止拾取箭头,禁止探面 // 拾取箭头其次,拾取到箭头时,禁止探面 // 3 groupFace.visible = false; // 1 放大镜 mousePos.x = event.offsetX; mousePos.y = div.clientHeight - event.offsetY; if (event.ctrlKey) { if (!magnifier.visible) { magnifier.visible = true; magPos.copy(mousePos); // if (pref.magnifier.fix) { raycaster.setFromCamera(mouse, camera); var ray = raycaster.ray; magnifier.position.set(0, 0, 0); //magnifier.up.set(0,0,1); magnifier.up.copy(camera.up); magnifier.lookAt(ray.direction); magnifier.position.copy(camera.position); } } if (pref.magnifier.fix) { mouseMag = normalizePos(mousePos); } else { mouseMag.set(0, 0); var mouseZoom = normalizePos(mousePos);//这是一个虚拟的位置,并不对应真实的鼠标位置 raycaster.setFromCamera(mouseZoom, camera); var ray = raycaster.ray; magnifier.position.set(0, 0, 0); //magnifier.up.set(0,0,1); magnifier.up.copy(camera.up); magnifier.lookAt(ray.direction); magnifier.position.copy(camera.position); } // 后续的功能就屏蔽了,因为他优先级较高 return; } else { magnifier.visible = false; } // 2 拾取箭头 raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObject(groupArrow, true); if (intersects.length > 0) { if (selectArrow != intersects[0].object) { if (selectArrow) selectArrow.material.color.setHex(selectArrow.currentHex); selectArrow = intersects[0].object; selectArrow.currentHex = selectArrow.material.color.getHex(); selectArrow.material.color.setHex(0xff0000); } // 后续的功能就屏蔽了,因为他优先级较高 return; } else { if (selectArrow) selectArrow.material.color.setHex(selectArrow.currentHex); selectArrow = null; } // 3 探面 if (pref.enableSurfaceDetection && cur_tool == Tool.none) { groupFace.visible = false; sampleGround(mouse, function (pos) { // 判断前进还是后退,同时求距离 var dist = pos.distanceTo(camera.position); var da = dist; /* var dir = camera.getWorldDirection(); var plane = new THREE.Plane(); plane.setFromNormalAndCoplanarPoint(dir, camera.position); var dist = plane.distanceToPoint(pos); var da = Math.abs(dist); */ if (da > 5 && da < 100) { groupFace.visible = true; groupFace.position.copy(pos); // 按远近调整大小 //var s = da/100 * (10-1) + 1; //face.scale.set(s,s,s); // 标注文字 if (dist > 0) faceLabel.setText('前进 ' + da.toFixed(0) + 'm'); else faceLabel.setText('后退 ' + da.toFixed(0) + 'm'); } else { groupFace.visible = false; } }); } } function onMouseWheel(event) { event.preventDefault(); if (is360()) { if (event.deltaY > 0 && camera.fov > 5) { camera.fov *= 0.8; camera.updateProjectionMatrix(); } else if (event.deltaY < 0 && camera.fov < 90) { camera.fov *= 1.2;; camera.updateProjectionMatrix(); } } else { if (event.deltaY > 0) { self.step(1); } else { self.step(-1); } } } function doMoveTo(mouse, event) { if (!groupFace.visible) return; var coord = [groupFace.position.x + coord_org.x, groupFace.position.y + coord_org.y, groupFace.position.z + coord_org.z]; var lonlat = trb.inverse(coord); self.locate(device_type, lonlat[0], lonlat[1], userKey); } function showHistory(history) { //先死后生 var historyContainer = clearHistory(); if (historyContainer == null) return; var listHistory = document.createElement('div'); historyContainer.appendChild(listHistory); listHistory.id = 'history'; listHistory.className = 'history'; for (var i = 0; i < history.length; i++) { var item = history[i]; var img = document.createElement('img'); img.id = 'history_item_' + i; img.className = 'history_item'; img.setAttribute('src', item.thumb); img.setAttribute('title', item.date); img.userData = item; img.thumbError = false; img.addEventListener( 'error', function () { if (!this.thumbError) { this.thumbError = true; this.src = this.userData.img; } else { this.src = error_imaj_url; } }, false ); img.addEventListener( 'click', function () { load_frame(this.userData.id); }, false ); listHistory.appendChild(img); } } function clearHistory() { var historyContainer = document.getElementById('history_container'); if (historyContainer != null) { var listHistory = document.getElementById('history'); if (listHistory != null) { historyContainer.removeChild(listHistory); } } return historyContainer; } function updateArrows() { var pos = camera.position; var dir = camera.getWorldDirection(); var up = new THREE.Vector3(0, 0, 1); var side = new THREE.Vector3(); side.crossVectors(dir, up); side.normalize(); dir.setLength(pref.arrows.forward); groupArrow.position.set(0, 0, - pref.arrows.below); groupArrow.position.add(pos); groupArrow.position.add(dir); groupArrow.quaternion.setFromAxisAngle(side, Math.PI / pref.arrows.lean); } function post_load_frame_php(res_v, new_workspace) { var cur = res_v.cur; // ok 1 获取当前帧的姿态 // ok 2 以当前帧的位置,作为视点 // ok 3 修改坐标系,以视点作为原点,2、3这两步其实什么也不需要做,可以记录下视点的绝对值,以及lonlat // ok 4 根据姿态数据,得到变换参数,构造一个Euler对象 // ok 5 修改camplane(mesh)的姿态,matrix.makeRotationFromEuler(euler) // ok 6 修改camplane的位置,matrix.makeTranslation(x,y,z) // 7 新建arrow,关联next、prev、branch,arrow的姿态是本地坐标系 // ok 8 修改视点,面向camplane // 构造变换器,从84到TM为正向 // tm的中央经线和基准纬线,取当前视点 var org; if (new_workspace) { resetOSG(); // 当投影参数改变后,整个场景里的要素,需要重新投影,重新计算坐标 // 也就是说,需要重新addFeature // 或者,你记录下json,每次你都重新解析? var tm = "+proj=tmerc +lat_0=" + cur.lat + " +lon_0=" + cur.lon + " +k=1.000000 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs "; trb = proj4(tm); org = trb.forward([cur.lon, cur.lat]); coord_org.x = org[0]; coord_org.y = org[1]; coord_org.z = cur.alt; // self.needsUpdate = true; } else { org = trb.forward([cur.lon, cur.lat]); } eye.set(org[0], org[1], cur.alt); eye.sub(coord_org); // z::mat::rot_pyr的定义: // z x y // yaw pitch roll // 3.14/4, 3.14/8, 3.14/2 // y x z // 旋转的顺序是y,x,z(roll、pitch、yaw) // 我应该保证这里和z::mat的坐标定义一致,就是xyz的含义一致 // 顺序是X、Y、Z,对应pitch、roll、yaw,但旋转顺序是YXZ,也就是roll、pitch、yaw,(extrinsic ordering) var rot = new THREE.Euler(cur.pitch, cur.roll, cur.yaw, 'ZXY'); // 更新照片姿态 camgroup.position.copy(eye); camgroup.rotation.copy(rot); // 调整相机视角 if (is360())//全景,跟随轨迹走向 { // 计算camera与当前帧heading的夹角 var dir = camera.getWorldDirection(); var headingCamera = Math.atan2(dir.x, dir.y);//[-pi, pi] var heading = -cur.heading; // 计算camera与轨迹heading之间的夹角 var curCE = heading - headingCamera; // 差值 if (lastCE != null) { var dif = lastCE - curCE; // 构造一个微调旋转,绕z轴 var euler = new THREE.Euler(0, 0, dif); var quat = new THREE.Quaternion(); quat.setFromEuler(euler); // 对当前camera应用视角调整 camera.quaternion.premultiply(quat); } } else //实景,对准它 { var pos = new THREE.Vector3(0, 1, 0); var up = new THREE.Vector3(0, 0, 1); pos.applyEuler(rot); up.applyEuler(rot); // 对准照片,使充满视野 // 为什么camera不能像camplane一样简单的应用ypr // 因为我现在用的坐标系是我熟悉的y代表航向,除非,改造坐标系统,用z代表航向 camera.position.set(0, 0, 0); camera.up.copy(up); camera.lookAt(pos); //camera.lookAt(pos.add(eye));//特殊,他和camera.position相关,所以position的先后会有不同 } camera.position.copy(eye); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); groupArrow.children = []; // 准备onPosition事件中,返回帧信息 var eventPos = { lon: cur.lon, lat: cur.lat, alt: cur.alt }; // 判断是否到了尽头,如果是,可以随便挑一个历史帧,作为前后帧 if (res_v.history != null && res_v.history.length > 0) { if (res_v.next == null) { res_v.next = res_v.history.shift();//删除并返回第一个元素 } else if (res_v.prev == null) { res_v.prev = res_v.history.shift();//删除并返回第一个元素 } } // 判断是否立交 // 添加方向箭头 var zoffset = 0.01; var arrow_offset = new THREE.Vector3(0, 1, 0); var item = res_v.next; if (item != null) { var pos = trb.forward([item.lon, item.lat]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize();//注意,我是在水平面旋转 var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.id, 0xffeeee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.next = { id: item.id, heading: heading }; } var item = res_v.prev; if (item != null) { var pos = trb.forward([item.lon, item.lat]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize(); var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.id, 0xeeffee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.prev = { id: item.id, heading: heading }; } //branch if (res_v.branch != null) { eventPos.branch = []; for (var i = 0; i < res_v.branch.length; i++) { var item = res_v.branch[i]; if (item != null) { var headingRad = -item.heading; var heading = headingRad * 180 / Math.PI; //禁止反向箭头,与视线对比 var angle = Math.abs(cur.heading - item.heading) * 180 / Math.PI; while (angle > 360) { angle -= 360; } if (angle > 150) { // eventPos.back = { id: item.id, heading: heading }; continue; } // var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.id, 0xeeeeff, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.branch.push({ id: item.id, heading: heading }); } } } var history = false;//历史 if (res_v.history != null) { if (res_v.history.length > 0) { var mesh = addGeometry(groupArrow, planeGeometry, 'history', 0xffffff, historyTexture); mesh.scale.set(0.7, 0.7, 0.7); mesh.userData = []; eventPos.history = []; for (var i = 0; i < res_v.history.length; i++) { var item = res_v.history[i]; if (item != null) { var thumb = server + "/train/" + item.train + "/images/" + pref.thumb + "/" + item.image; var img = server + "/train/" + item.train + "/images/" + item.image; var his = { id: item.id, date: item.train, thumb: thumb, img: img, }; mesh.userData.push(his); // var his2 = { id: item.id, date: item.train, }; eventPos.history.push(his2); } } } } // self.onPosition(eventPos); // 允许客户在onPosition中设置needsUpdate=true/false,选择是否更新图层要素 if (self.needsUpdate) { self.needsUpdate = false; for (var i = 0; i < groupLayers.children.length; i++) { var layer = groupLayers.children[i]; self.setLayer(layer, layer.userData); } } // 视角发生改变,才应该调用它,所以他的依据应该是camera,而不是frame { var dir = camera.getWorldDirection(); var heading = Math.atan2(dir.x, dir.y) * 180 / Math.PI;//[-pi, pi] var fovy = camera.fov; var fovx = (Math.atan(Math.tan(fovy / 2) * camera.aspect) * 2) * 180 / Math.PI; var eventEye = { heading: heading, fovx: fovx }; self.onEye(eventEye); } // cur_frame_id = cur.id; var event = { state: LocateState.success }; self.onLocate(event); busy = false; // if (playing) { load_frame(getFrameID(1)); } } function load_frame_ppv(result, new_workspace) { cur_frame = null; if (result == "") { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var res_v = $.parseJSON(result); //jQuery,支持很好 //var res_v = JSON.parse(result); //浏览器内置支持,除了低版本ie if (res_v == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var cur = res_v.Currentframe; //这种形式也可以,这是php的方式,var item = res_v['cur']; if (cur == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } // 记录当前帧的结构 cur_frame = res_v; // 考虑缩略图 var img = cur.Imgurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); loadImage(thumb, img, post_load_frame_ppv, res_v, new_workspace); } function post_load_frame_ppv(res_v, new_workspace) { var cur = res_v.Currentframe; // ok 1 获取当前帧的姿态 // ok 2 以当前帧的位置,作为视点 // ok 3 修改坐标系,以视点作为原点,2、3这两步其实什么也不需要做,可以记录下视点的绝对值,以及lonlat // ok 4 根据姿态数据,得到变换参数,构造一个Euler对象 // ok 5 修改camplane(mesh)的姿态,matrix.makeRotationFromEuler(euler) // ok 6 修改camplane的位置,matrix.makeTranslation(x,y,z) // 7 新建arrow,关联next、prev、branch,arrow的姿态是本地坐标系 // ok 8 修改视点,面向camplane // 构造变换器,从84到TM为正向 // tm的中央经线和基准纬线,取当前视点 var org; if (new_workspace) { resetOSG(); // 当投影参数改变后,整个场景里的要素,需要重新投影,重新计算坐标 // 也就是说,需要重新addFeature // 或者,你记录下json,每次你都重新解析? var tm = "+proj=tmerc +lat_0=" + cur.Lat + " +lon_0=" + cur.Lon + " +k=1.000000 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs "; trb = proj4(tm); org = trb.forward([cur.Lon, cur.Lat]); coord_org.x = org[0]; coord_org.y = org[1]; coord_org.z = cur.Alt; // self.needsUpdate = true; } else { org = trb.forward([cur.Lon, cur.Lat]); } eye.set(org[0], org[1], cur.Alt); eye.sub(coord_org); // z::mat::rot_pyr的定义: // z x y // yaw pitch roll // 3.14/4, 3.14/8, 3.14/2 // y x z // 旋转的顺序是y,x,z(roll、pitch、yaw) // 我应该保证这里和z::mat的坐标定义一致,就是xyz的含义一致 // 顺序是X、Y、Z,对应pitch、roll、yaw,但旋转顺序是YXZ,也就是roll、pitch、yaw,(extrinsic ordering) var rot = new THREE.Euler(cur.Pitch, cur.Roll, cur.Yaw, 'ZXY'); // 更新照片姿态 camgroup.position.copy(eye); camgroup.rotation.copy(rot); // 调整相机视角 if (is360())//全景,跟随轨迹走向 { // 计算camera与当前帧heading的夹角 var dir = camera.getWorldDirection(); var headingCamera = Math.atan2(dir.x, dir.y);//[-pi, pi] var heading = -cur.Heading; // 计算camera与轨迹heading之间的夹角 var curCE = heading - headingCamera; // 差值 if (lastCE != null) { var dif = lastCE - curCE; // 构造一个微调旋转,绕z轴 var euler = new THREE.Euler(0, 0, dif); var quat = new THREE.Quaternion(); quat.setFromEuler(euler); // 对当前camera应用视角调整 camera.quaternion.premultiply(quat); } } else //实景,对准它 { var pos = new THREE.Vector3(0, 1, 0); var up = new THREE.Vector3(0, 0, 1); pos.applyEuler(rot); up.applyEuler(rot); // 对准照片,使充满视野 // 为什么camera不能像camplane一样简单的应用ypr // 因为我现在用的坐标系是我熟悉的y代表航向,除非,改造坐标系统,用z代表航向 camera.position.set(0, 0, 0); camera.up.copy(up); camera.lookAt(pos); //camera.lookAt(pos.add(eye));//特殊,他和camera.position相关,所以position的先后会有不同 } camera.position.copy(eye); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); groupArrow.children = []; // 准备onPosition事件中,返回帧信息 var eventPos = { lon: cur.Lon, lat: cur.Lat, alt: cur.Alt }; // 判断是否到了尽头,如果是,可以随便挑一个历史帧,作为前后帧 if (res_v.Historyframe != null && res_v.Historyframe.length > 0) { if (res_v.next == null) { res_v.next = res_v.Historyframe.shift();//删除并返回第一个元素 } else if (res_v.prev == null) { res_v.prev = res_v.Historyframe.shift();//删除并返回第一个元素 } } // 判断是否立交 // 添加方向箭头 var zoffset = 0.01; var arrow_offset = new THREE.Vector3(0, 1, 0); var item = res_v.Nextframe; if (item != null) { var pos = trb.forward([item.Lon, item.Lat]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize();//注意,我是在水平面旋转 var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.ImgId, 0xffeeee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.next = { id: item.ImgId, heading: heading }; } var item = res_v.Prevframe; if (item != null) { var pos = trb.forward([item.Lon, item.Lat]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize(); var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.ImgId, 0xeeffee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.prev = { id: item.ImgId, heading: heading }; } //branch if (res_v.Branchframe != null) { eventPos.branch = []; for (var i = 0; i < res_v.Branchframe.length; i++) { var item = res_v.Branchframe[i]; if (item != null) { var headingRad = -item.Heading; var heading = headingRad * 180 / Math.PI; //禁止反向箭头,与视线对比 var angle = Math.abs(cur.Heading - item.Heading) * 180 / Math.PI; while (angle > 360) { angle -= 360; } if (angle > 150) { // eventPos.back = { id: item.ImgId, heading: heading }; continue; } // var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.ImgId, 0xeeeeff, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.branch.push({ id: item.ImgId, heading: heading }); } } } var history = false;//历史 if (res_v.Historyframe != null) { if (res_v.Historyframe.length > 0) { var mesh = addGeometry(groupArrow, planeGeometry, 'history', 0xffffff, historyTexture); mesh.scale.set(0.7, 0.7, 0.7); mesh.userData = []; eventPos.history = []; for (var i = 0; i < res_v.Historyframe.length; i++) { var item = res_v.Historyframe[i]; if (item != null) { var img = cur.Imgurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); var his = { id: item.ImgId, date: item.Train, thumb: thumb, img: img, }; mesh.userData.push(his); // var his2 = { id: item.ImgId, date: item.Train, }; eventPos.history.push(his2); } } } } // self.onPosition(eventPos); // 允许客户在onPosition中设置needsUpdate=true/false,选择是否更新图层要素 if (self.needsUpdate) { self.needsUpdate = false; for (var i = 0; i < groupLayers.children.length; i++) { var layer = groupLayers.children[i]; self.setLayer(layer, layer.userData); } } // 视角发生改变,才应该调用它,所以他的依据应该是camera,而不是frame { var dir = camera.getWorldDirection(); var heading = Math.atan2(dir.x, dir.y) * 180 / Math.PI;//[-pi, pi] var fovy = camera.fov; var fovx = (Math.atan(Math.tan(fovy / 2) * camera.aspect) * 2) * 180 / Math.PI; var eventEye = { heading: heading, fovx: fovx }; self.onEye(eventEye); } // cur_frame_id = cur.ImgId; var event = { state: LocateState.success }; self.onLocate(event); busy = false; // if (playing) { load_frame(getFrameID(1)); } } // 支持标定参数,从设备表查找,设备表已经在初始化的时候下载下来了,只不过时机并不完美,是在setServer的时候 // 如果用户调用了调用了setServer,立即调用locate,可能反应不及 // 输入参数是不同类型的轨迹,待检测字段,这个字段中应包含设备表的设备id // 只有实景才需要这个方法 function caliCamplaneMesh(test) { twins = null;//twins yaw-delta // // 设置设备标定参数 // if (deviceList != null && deviceList.length > 0) { if (test != null) { for (var i = 0; i < deviceList.length; i++) { var device = deviceList[i]; var id = device.EquipId; var pos = test.indexOf(id); if (pos >= 0) { if (device.Width > 0) sensorWidth = device.Width; if (device.Height > 0) sensorHeight = device.Height; if (device.Focalx > 0) focal = device.Focalx; imajAspect = sensorWidth / sensorHeight;//1.1953125;//2448/2048 //if (device.Principalx > 0) principalX = device.Principalx; //if (device.Principaly > 0) principalY = device.Principaly; // if (device.Ext != null) { var ext = $.parseJSON(device.Ext); //jQuery,支持很好 if (ext.twins != null && ext.twins != 0) { twins = ext.twins * Math.PI / 180; } } // 更新camplane camScale = pref.scope / focal; var pos = new THREE.Vector3(principalX * camScale, focal * camScale, principalY * camScale); var rot = new THREE.Euler(Math.PI / 2, 0, 0); var scale = new THREE.Vector3(sensorWidth * camScale, sensorHeight * camScale, 1); camplaneMesh.rotation.copy(rot); camplaneMesh.scale.copy(scale); camplaneMesh.position.copy(pos); break; } } } } // } function loadImage(thumb, img, post_load_frame, res_v, new_workspace) { // console.log(img); if (cur_frame != null) { switch (device_type) { case -1://php photo_type = cur_frame.cur.type; break; case 0://ppv photo_type = cur_frame.Currentframe.Type; caliCamplaneMesh(cur_frame.Currentframe.Train); break; case 3://imaj photo_type = 2; caliCamplaneMesh(cur_frame.Currentframe.Pictureurl); break; case 4://streetview photo_type = 1; break; } } // 这里是各种类型设备的必经之处 // 在这里切换照片几何体 // 设置设备标定参数 //这里曾经发生过恐怖的事情 //以前的写法是 camplaneMesh.material.map = texture; //导致内存直线上升 //目前的写法是 map.image = texture.image; var map = camplaneMesh.material.map; if (photo_type == 1)//360 { map = camballMesh.material.map; // camgroup.children = []; camgroup.add(camballMesh); } else { camgroup.children = []; camgroup.add(camplaneMesh); // // 设置设备标定参数 // /* if (deviceList != null && deviceList.length>0) { } sensorWidth = 8.4456; sensorHeight = 7.0656;//7.0725;//后者是全尺寸sensor focal = 5; imajAspect = 1.1953125;//2448/2048 principalX = 0; principalY = 0; */ // } //第一步先下载照片,异步,下载完成后,才能改变现状 /* var texture = new THREE.TextureLoader().load(img); var material = new THREE.MeshBasicMaterial( { map: texture } ); camplaneMesh = new THREE.Mesh(planeGeometry, material); post_load_frame(res_v, new_workspace); */ //var loader = new THREE.TextureLoader(); loader.load( img, function (texture) { //var material = new THREE.MeshBasicMaterial( { map: texture } ); //camplaneMesh = new THREE.Mesh(planeGeometry, material); map.image = texture.image; //map=null; //map = texture; map.needsUpdate = true; //camplaneMesh.material.needsUpdate = true; post_load_frame(res_v, new_workspace); }, null, function () { var event = { state: LocateState.imageError }; self.onLocate(event); if (photo_type == 1)//360 { map.image = error_360.image; map.needsUpdate = true; } else { map.image = error_imaj.image; map.needsUpdate = true; } post_load_frame(res_v, new_workspace); } ); } function loadImage_org(thumb, img, post_load_frame, res_v, new_workspace) { // if (cur_frame != null) { switch (device_type) { case -1://php photo_type = cur_frame.cur.type; break; case 0://ppv photo_type = cur_frame.Currentframe.Type; caliCamplaneMesh(cur_frame.Currentframe.Train); break; case 3://imaj photo_type = 2; caliCamplaneMesh(cur_frame.Currentframe.Pictureurl); break; case 4://streetview photo_type = 1; break; } } // 这里是各种类型设备的必经之处 // 在这里切换照片几何体 // 设置设备标定参数 //这里曾经发生过恐怖的事情 //以前的写法是 camplaneMesh.material.map = texture; //导致内存直线上升 //目前的写法是 map.image = texture.image; var map = camplaneMesh.material.map; if (photo_type == 1)//360 { map = camballMesh.material.map; // camgroup.children = []; camgroup.add(camballMesh); } else { camgroup.children = []; camgroup.add(camplaneMesh); // // 设置设备标定参数 // /* if (deviceList != null && deviceList.length>0) { } sensorWidth = 8.4456; sensorHeight = 7.0656;//7.0725;//后者是全尺寸sensor focal = 5; imajAspect = 1.1953125;//2448/2048 principalX = 0; principalY = 0; */ // } loader.load( thumb, function (texture) { /* //这里曾经发生过恐怖的事情 //以前的写法是 camplaneMesh.material.map = texture; //导致内存直线上升 var map = camplaneMesh.material.map; if (photo_type==1)//360 { map = camballMesh.material.map; camgroup.children = []; camgroup.add( camballMesh); } else { camgroup.children = []; camgroup.add( camplaneMesh); } */ map.image = texture.image; map.needsUpdate = true; post_load_frame(res_v, new_workspace);//注意 if (playing) return; //继续加载大图 //第一步先下载照片,异步,下载完成后,才能改变现状 busy = true; loader.load( img, function (texture) { //这里曾经发生过恐怖的事情 //以前的写法是 camplaneMesh.material.map = texture; //导致内存直线上升 /* var map = camplaneMesh.material.map; if (photo_type==1)//360 { map = camballMesh.material.map; camgroup.children = []; camgroup.add( camballMesh); } else { camgroup.children = []; camgroup.add( camplaneMesh); } */ map.image = texture.image; map.needsUpdate = true; //post_load_frame(res_v, false);//注意 // busy = false; }, null, function () { busy = false; } ); }, null, function () { //第一步先下载照片,异步,下载完成后,才能改变现状 loader.load( img, function (texture) { //这里曾经发生过恐怖的事情 //以前的写法是 camplaneMesh.material.map = texture; //导致内存直线上升 /* var map = camplaneMesh.material.map; if (photo_type==1)//360 { map = camballMesh.material.map; camgroup.children = []; camgroup.add( camballMesh); } else { camgroup.children = []; camgroup.add( camplaneMesh); } */ map.image = texture.image; map.needsUpdate = true; post_load_frame(res_v, new_workspace); }, null, function () { var event = { state: LocateState.imageError }; self.onLocate(event); if (photo_type == 1)//360 { /* var map = camballMesh.material.map; camgroup.children = []; camgroup.add( camballMesh); */ map.image = error_360.image; map.needsUpdate = true; } else { /* var map = camplaneMesh.material.map; camgroup.children = []; camgroup.add( camplaneMesh); */ map.image = error_imaj.image; map.needsUpdate = true; } post_load_frame(res_v, new_workspace); } ); } ); } function load_frame_php(result, new_workspace) { cur_frame = null; // if (result == "") { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var res_v = $.parseJSON(result); //jQuery,支持很好 //var res_v = JSON.parse(result); //浏览器内置支持,除了低版本ie if (res_v == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var cur = res_v.cur; //这种形式也可以,这是php的方式,var item = res_v['cur']; if (cur == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } // 记录当前帧的结构 cur_frame = res_v; // 考虑缩略图 var thumb = server + "/train/" + cur.train + "/images/" + pref.thumb + "/" + cur.image; var img = server + "/train/" + cur.train + "/images/" + cur.image; loadImage(thumb, img, post_load_frame_php, res_v, new_workspace); } function load_frame_imaj(result, new_workspace) { cur_frame = null; if (result == "") { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var res_v = $.parseJSON(result); //jQuery,支持很好 //var res_v = JSON.parse(result); //浏览器内置支持,除了低版本ie if (res_v == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var cur = res_v.Currentframe; //这种形式也可以,这是php的方式,var item = res_v['cur']; if (cur == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } // 记录当前帧的结构 cur_frame = res_v; // 考虑缩略图 var img = cur.Pictureurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); loadImage(thumb, img, post_load_frame_imaj, res_v, new_workspace); } function load_frame_streetview(result, new_workspace) { cur_frame = null; if (result == "") { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var res_v = $.parseJSON(result); //jQuery,支持很好 //var res_v = JSON.parse(result); //浏览器内置支持,除了低版本ie if (res_v == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } var cur = res_v.Currentframe; //这种形式也可以,这是php的方式,var item = res_v['cur']; if (cur == null) { var event = { state: LocateState.dataError }; self.onLocate(event); console.warn(result); busy = false; return; } // 记录当前帧的结构 cur_frame = res_v; // 考虑缩略图 var img = cur.Pictureurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); loadImage(thumb, img, post_load_frame_streetview, res_v, new_workspace); } function post_load_frame_imaj(res_v, new_workspace) { var cur = res_v.Currentframe; // ok 1 获取当前帧的姿态 // ok 2 以当前帧的位置,作为视点 // ok 3 修改坐标系,以视点作为原点,2、3这两步其实什么也不需要做,可以记录下视点的绝对值,以及lonlat // ok 4 根据姿态数据,得到变换参数,构造一个Euler对象 // ok 5 修改camplane(mesh)的姿态,matrix.makeRotationFromEuler(euler) // ok 6 修改camplane的位置,matrix.makeTranslation(x,y,z) // 7 新建arrow,关联next、prev、branch,arrow的姿态是本地坐标系 // ok 8 修改视点,面向camplane // 构造变换器,从84到TM为正向 // tm的中央经线和基准纬线,取当前视点 var org; if (new_workspace) { resetOSG(); // 当投影参数改变后,整个场景里的要素,需要重新投影,重新计算坐标 // 也就是说,需要重新addFeature // 或者,你记录下json,每次你都重新解析? var tm = "+proj=tmerc +lat_0=" + cur.Latitude + " +lon_0=" + cur.Longitude + " +k=1.000000 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs "; trb = proj4(tm); org = trb.forward([cur.Longitude, cur.Latitude]); coord_org.x = org[0]; coord_org.y = org[1]; coord_org.z = cur.Altitude; // self.needsUpdate = true; } else { org = trb.forward([cur.Longitude, cur.Latitude]); } eye.set(org[0], org[1], cur.Altitude); eye.sub(coord_org); // z::mat::rot_pyr的定义: // z x y // yaw pitch roll // 3.14/4, 3.14/8, 3.14/2 // y x z // 旋转的顺序是y,x,z(roll、pitch、yaw) // 我应该保证这里和z::mat的坐标定义一致,就是xyz的含义一致 // 顺序是X、Y、Z,对应pitch、roll、yaw,但旋转顺序是YXZ,也就是roll、pitch、yaw,(extrinsic ordering) var rot = new THREE.Euler(-cur.Pitch * Math.PI / 180, cur.Roll * Math.PI / 180, cur.Yaw * Math.PI / 180, 'ZXY'); if (twins != null) { rot.z += twins; } var pos = new THREE.Vector3(0, focal, 0); var up = new THREE.Vector3(0, 0, 1); pos.applyEuler(rot); up.applyEuler(rot); if (!is360()) { // 对准照片,使充满视野 // 为什么camera不能像camplane一样简单的应用ypr // 因为我现在用的坐标系是我熟悉的y代表航向,除非,改造坐标系统,用z代表航向 camera.position.set(0, 0, 0); camera.up.set(up.x, up.y, up.z); camera.lookAt(pos); //camera.lookAt(pos.add(eye));//特殊,他和camera.position相关,所以position的先后会有不同 } camera.position.copy(eye); // 更新照片姿态 camgroup.position.copy(eye); camgroup.rotation.copy(rot); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); groupArrow.children = []; // 准备onPosition事件中,返回帧信息 var eventPos = { lon: cur.Longitude, lat: cur.Latitude, alt: cur.Altitude }; // 判断是否到了尽头,如果是,可以随便挑一个历史帧,作为前后帧 if (res_v.Historyframe != null && res_v.Historyframe.length > 0) { if (res_v.Nextframe == null) { res_v.Nextframe = res_v.Historyframe.shift();//删除并返回第一个元素 } else if (res_v.Prevframe == null) { res_v.Prevframe = res_v.Historyframe.shift();//删除并返回第一个元素 } } // 判断是否立交 // 添加方向箭头 var arrow_offset = new THREE.Vector3(0, 1, 0); var item = res_v.Nextframe; if (item != null) { var pos = trb.forward([item.Longitude, item.Latitude]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize();//注意,我是在水平面旋转 var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.PmId, 0xffeeee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.next = { id: item.PmId, heading: heading }; } var item = res_v.Prevframe; if (item != null) { var pos = trb.forward([item.Longitude, item.Latitude]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize(); var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.PmId, 0xeeffee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.prev = { id: item.PmId, heading: heading }; } //branch var back;//反向箭头 if (res_v.Branchframe != null) { eventPos.branch = []; for (var i = 0; i < res_v.Branchframe.length; i++) { var item = res_v.Branchframe[i]; if (item != null) { var heading = -item.Yaw; var headingRad = heading * Math.PI / 180; //禁止反向箭头,与视线对比 var angle = Math.abs(cur.Yaw - item.Yaw); while (angle > 360) { angle -= 360; } if (angle > 150) { // eventPos.back = { id: item.PmId, heading: heading }; continue; } // var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.PmId, 0xeeeeff, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.branch.push({ id: item.PmId, heading: heading }); } } } var history = false;//历史 if (res_v.Historyframe != null) { if (res_v.Historyframe.length > 0) { var mesh = addGeometry(groupArrow, planeGeometry, 'history', 0xffffff, historyTexture); mesh.scale.set(0.7, 0.7, 0.7); mesh.userData = []; eventPos.history = []; for (var i = 0; i < res_v.Historyframe.length; i++) { var item = res_v.Historyframe[i]; if (item != null) { var img = cur.Pictureurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); var his = { id: item.PmId, date: item.Importtime, thumb: thumb, img: img, }; mesh.userData.push(his); // var his2 = { id: item.PmId, date: item.Importtime, }; eventPos.history.push(his2); } } } } // self.onPosition(eventPos); // 允许客户在onPosition中设置needsUpdate=true/false,选择是否更新图层要素 if (self.needsUpdate) { self.needsUpdate = false; for (var i = 0; i < groupLayers.children.length; i++) { var layer = groupLayers.children[i]; self.setLayer(layer, layer.userData); } } // 视角发生改变,才应该调用它,所以他的依据应该是camera,而不是frame { var dir = camera.getWorldDirection(); var heading = Math.atan2(dir.x, dir.y) * 180 / Math.PI;//[-pi, pi] var fovy = camera.fov; var fovx = (Math.atan(Math.tan(fovy / 2) * camera.aspect) * 2) * 180 / Math.PI; var eventEye = { heading: heading, fovx: fovx }; self.onEye(eventEye); } // cur_frame_id = cur.PmId; var event = { state: LocateState.success }; self.onLocate(event); // busy = false; if (playing) { load_frame(getFrameID(1), new_workspace); } } function post_load_frame_streetview(res_v, new_workspace) { var cur = res_v.Currentframe; // ok 1 获取当前帧的姿态 // ok 2 以当前帧的位置,作为视点 // ok 3 修改坐标系,以视点作为原点,2、3这两步其实什么也不需要做,可以记录下视点的绝对值,以及lonlat // ok 4 根据姿态数据,得到变换参数,构造一个Euler对象 // ok 5 修改camplane(mesh)的姿态,matrix.makeRotationFromEuler(euler) // ok 6 修改camplane的位置,matrix.makeTranslation(x,y,z) // 7 新建arrow,关联next、prev、branch,arrow的姿态是本地坐标系 // ok 8 修改视点,面向camplane // 构造变换器,从84到TM为正向 // tm的中央经线和基准纬线,取当前视点 var org; if (new_workspace) { resetOSG(); // 当投影参数改变后,整个场景里的要素,需要重新投影,重新计算坐标 // 也就是说,需要重新addFeature // 或者,你记录下json,每次你都重新解析? var tm = "+proj=tmerc +lat_0=" + cur.Latitude + " +lon_0=" + cur.Longitude + " +k=1.000000 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs "; trb = proj4(tm); org = trb.forward([cur.Longitude, cur.Latitude]); coord_org.x = org[0]; coord_org.y = org[1]; coord_org.z = cur.Height; // self.needsUpdate = true; } else { org = trb.forward([cur.Longitude, cur.Latitude]); } eye.set(org[0], org[1], cur.Height); eye.sub(coord_org); // z::mat::rot_pyr的定义: // z x y // yaw pitch roll // 3.14/4, 3.14/8, 3.14/2 // y x z // 旋转的顺序是y,x,z(roll、pitch、yaw) // 我应该保证这里和z::mat的坐标定义一致,就是xyz的含义一致 // 顺序是X、Y、Z,对应pitch、roll、yaw,但旋转顺序是YXZ,也就是roll、pitch、yaw,(extrinsic ordering) var rot = new THREE.Euler(0, 0, -(cur.Heading + 90) * Math.PI / 180, 'ZXY'); // 更新照片姿态 camgroup.position.copy(eye); camgroup.rotation.copy(rot); // 计算camera与当前帧heading的夹角 var dir = camera.getWorldDirection(); var headingCamera = Math.atan2(dir.x, dir.y);//[-pi, pi] var heading = cur.Heading * Math.PI / 180; // 计算camera与轨迹heading之间的夹角 var curCE = heading - headingCamera; // 差值 if (lastCE != null) { var dif = lastCE - curCE; // 构造一个微调旋转,绕z轴 var euler = new THREE.Euler(0, 0, dif); var quat = new THREE.Quaternion(); quat.setFromEuler(euler); // 对当前camera应用视角调整 camera.quaternion.premultiply(quat); } camera.position.copy(eye); // 将箭头组,向视线方向移动,以便总能看到 updateArrows(); groupArrow.children = []; // 准备onPosition事件中,返回帧信息 var eventPos = { lon: cur.Longitude, lat: cur.Latitude, alt: cur.Height }; // 判断是否到了尽头,如果是,可以随便挑一个历史帧,作为前后帧 if (res_v.Historyframe != null && res_v.Historyframe.length > 0) { if (res_v.Nextframe == null) { res_v.Nextframe = res_v.Historyframe.shift();//删除并返回第一个元素 } else if (res_v.Prevframe == null) { res_v.Prevframe = res_v.Historyframe.shift();//删除并返回第一个元素 } } // 判断是否立交 // 添加方向箭头 var arrow_offset = new THREE.Vector3(0, 1, 0); var item = res_v.Nextframe; if (item != null) { var pos = trb.forward([item.Longitude, item.Latitude]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize();//注意,我是在水平面旋转 var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.Id, 0xffeeee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.next = { id: item.Id, heading: heading }; } var item = res_v.Prevframe; if (item != null) { var pos = trb.forward([item.Longitude, item.Latitude]); var dir = new THREE.Vector3(pos[0] - org[0], pos[1] - org[1], 0).normalize(); var headingRad = Math.atan2(dir.x, dir.y); var heading = headingRad * 180 / Math.PI;//[-pi, pi] var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.Id, 0xeeffee, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.prev = { id: item.Id, heading: heading }; } //branch var back;//反向箭头 if (res_v.Branchframe != null) { eventPos.branch = []; for (var i = 0; i < res_v.Branchframe.length; i++) { var item = res_v.Branchframe[i]; if (item != null) { var heading = -item.Heading * Math.PI / 180; var headingRad = heading * Math.PI / 180; //禁止反向箭头,与视线对比 var angle = Math.abs(cur.Heading - item.Heading); while (angle > 360) { angle -= 360; } if (angle > 150) { // eventPos.back = { id: item.Id, heading: heading }; continue; } // var arrow = new THREE.Group(); groupArrow.add(arrow); arrow.rotation.set(0, 0, -headingRad); var mesh = addGeometry(arrow, planeGeometry, item.Id, 0xeeeeff, arrowTexture(heading)); mesh.position.copy(arrow_offset); arrow_offset.z += 0.01; // eventPos.branch.push({ id: item.Id, heading: heading }); } } } var history = false;//历史 if (res_v.Historyframe != null) { if (res_v.Historyframe.length > 0) { var mesh = addGeometry(groupArrow, planeGeometry, 'history', 0xffffff, historyTexture); mesh.scale.set(0.7, 0.7, 0.7); mesh.userData = []; eventPos.history = []; for (var i = 0; i < res_v.Historyframe.length; i++) { var item = res_v.Historyframe[i]; if (item != null) { var img = cur.Pictureurl; var pos = img.lastIndexOf('/'); var thumb = img.substr(0, pos) + "/" + pref.thumb + img.substr(pos); var his = { id: item.Id, date: item.Datetime, thumb: thumb, img: img, }; mesh.userData.push(his); // var his2 = { id: item.Id, date: item.Datetime, }; eventPos.history.push(his2); } } } } // self.onPosition(eventPos); // 允许客户在onPosition中设置needsUpdate=true/false,选择是否更新图层要素 if (self.needsUpdate) { self.needsUpdate = false; for (var i = 0; i < groupLayers.children.length; i++) { var layer = groupLayers.children[i]; self.setLayer(layer, layer.userData); } } // 视角发生改变,才应该调用它,所以他的依据应该是camera,而不是frame { var dir = camera.getWorldDirection(); var heading = Math.atan2(dir.x, dir.y) * 180 / Math.PI;//[-pi, pi] var fovy = camera.fov; var fovx = (Math.atan(Math.tan(fovy / 2) * camera.aspect) * 2) * 180 / Math.PI; var eventEye = { heading: heading, fovx: fovx }; self.onEye(eventEye); } // cur_frame_id = cur.Id; var event = { state: LocateState.success }; self.onLocate(event); // busy = false; if (playing) { load_frame(getFrameID(1)); } } init(); };