/** * 判断数组是否是两条线段组成 * @param {线段数组} arr */ function isTwoLine(arr) { var max = 0, min = 1; for (var i = 0; i < arr.length; i++) { if (arr[i][0] > max) { max = arr[i][0]; } if (arr[i][0] < min) { min = arr[i][0]; } } return [min === 0, max]; } /* @function: 加载3D线段,需要根据平台自行实现 @info: 这里生成一个3D单位CUBE的所有线段 */ function _3dLine(r2x, r3x, ret) { // 坐标点长度 var r1x = 0.5; // 矩形的各个定点 var r2x_t = [ [-r1x, -r1x, +r1x], [-r1x, +r1x, +r1x], [+r1x, +r1x, +r1x], [+r1x, -r1x, +r1x], [-r1x, -r1x, -r1x], [-r1x, +r1x, -r1x], [+r1x, +r1x, -r1x], [+r1x, -r1x, -r1x] ]; // 线段的坐标 // z轴 var r3x_t = [ [0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7] ]; // 各个线段的坐标 var ret_t = [ ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""], ["", ""] ]; // ret[0][0] = r2x[r3x[0][0]]; for (var i = 0; i < r3x.length; i++) { ret[i] = { "3d_id": i, "3d_point1": r2x[r3x[i][0]], "3d_point2": r2x[r3x[i][1]] } // ret[i][0] = r2x[r3x[i][0]]; // ret[i][1] = r2x[r3x[i][1]]; } ret_t[0][0] = r2x_t[r3x_t[0][0]]; for (var i = 0; i < r3x_t.length; i++) { ret_t[i][0] = r2x_t[r3x_t[i][0]]; ret_t[i][1] = r2x_t[r3x_t[i][1]]; } return ret; } /** * 生成3D格式数据 * @param {三维线段数组} arr * @param {组成面得线段数组} faceArr */ function fix3dLineArr(arr, faceArr) { var ret = [] for (let i = 0; i < faceArr.length; i++) { var item = { faceLine: [], index: [], verticalLine: [] } for (let j = 0; j < faceArr[i].length; j++) { for (let k = 0; k < arr.length; k++) { if (faceArr[i][j] == arr[k]['3d_id']) { item.faceLine.push({ id: faceArr[i][j], points: [arr[k]["3d_point1"], arr[k]["3d_point2"]] }) item.index.push(faceArr[i][j]) } } } ret.push(item) } return ret } // 归一化 function normalize(xyz, isFixed) { var _mo_ = Math.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]); var x = xyz[0] / _mo_; var y = xyz[1] / _mo_; var z = xyz[2] / _mo_; var r1x; if (isFixed) { r1x = [-y, -x, z]; } else { r1x = [x, y, z]; } return r1x; } /* @function: 相机在坐标原点上,返回空间点在球目图片上的投影点,和'_calculate3dPoint'互逆 @param {xyz}: 空间中的3D点,不需要先进行归一化 @return {ret}: 纹理空间的2D点,范围为[0 ,1] */ function _calculate2dPoint(xyz) { var ret = ["", ""]; //@info: 这一步是为了兼容我们所用的球目相机的正方向,并且进行归一化 // const auto r1x = Vector {-xyz[1] ,-xyz[0] ,xyz[2] ,0}.normalize () ; var r1x = normalize(xyz, true); // var r1x = xyz ret[0] = Math.atan2(r1x[1], r1x[0]) / (Math.PI * 2) + 0.5; ret[1] = (Math.acos(r1x[2]) / (Math.PI * 2)) * 2; return ret; // ARRAY2 ret ; // //@info: 这一步是为了兼容我们所用的球目相机的正方向,并且进行归一化 // const auto r1x = Vector {-xyz[1] ,-xyz[0] ,xyz[2] ,0}.normalize () ; // //@info: 分别计算投影点坐标 // //@info: 反三角函数,缩放,平移 // ret[0] = _ATAN_ (r1x[1] , r1x[0]) / VAL64 (VALX_PI * 2) + VAL64 (0.5) ; // ret[1] = _ACOS_ (r1x[2]) / VAL64 (VALX_PI * 2) * 2 ; // return std::move (ret) ; } /* @function: 相机在坐标原点上,返回球目图片上的投影点所在单位球上的原点,和'_calculate2dPoint'互逆 @param {txy}: 纹理空间的2D点,范围为[0 ,1] @return {ret}: 空间中的3D点,在单位球上 */ function _calculate3dPoint(txy) { var ret = ["", "", ""]; var r1x = (txy[1] / 2) * (Math.PI * 2); var r2x = (txy[0] - 0.5) * (Math.PI * 2); ret[0] = -Math.sin(r1x) * Math.sin(r2x); ret[1] = -Math.sin(r1x) * Math.cos(r2x); ret[2] = Math.cos(r1x); return ret; } /* @function: 计算栅格化所有线段后最大生成点的数量 @param {line}: 线段的数组,line[i][0]为第一个点,line[i][1]为第二个点 @param {gap}: 栅格化所需的步长,既每多长生成一个点 @return {ret}: 最大生成点的数量 */ function _calculateMaxPoint(line, gap) { var ret = 0; for (var i = 0; i < line.length; i++) { var r1x = line[i][0]; var r2x = line[i][1]; // Math.hypot(x1 - x0, y1 - y0); ret += Math.ceil( Math.hypot(r1x[0] - r2x[0], r1x[1] - r2x[1], r1x[2] - r2x[2]) / gap + 0.5 ); } return ret; } /* @function: 计算3D线段在球目图片上的栅格化投影 @param {camera_pose_matrix}: 相机位置,外参矩阵 @param {line}: 线段的数组,line[i][0]为第一个点,line[i][1]为第二个点 @param {gap}: 栅格化所需的步长,既每多长生成一个点 @return {ret}: 栅格化投影投影后的2D线段,点坐标属于纹理空间,范围为[0 ,1] */ function _3dTo2d(camera_pose_matrix, line, gap) { var ret = []; var ret_t = []; var item; var r10x = numbers.matrix.inverse(camera_pose_matrix); for (var i = 0; i < line.length; i++) { ret_t.push({ line: [] }); item = [] var r1x_temp = [[line[i]["3d_point1"][0]], [line[i]['3d_point1'][1]], [line[i]['3d_point1'][2]], [1]]; var r2x_temp = [[line[i]['3d_point2'][0]], [line[i]['3d_point2'][1]], [line[i]['3d_point2'][2]], [1]]; var r1x = numbers.matrix.multiply(r10x, r1x_temp); var r2x = numbers.matrix.multiply(r10x, r2x_temp); var r3x = Math.hypot(r2x[0] - r1x[0], r2x[1] - r1x[1], r2x[2] - r1x[2]) / gap var r2r1 = [r2x[0] - r1x[0], r2x[1] - r1x[1], r2x[2] - r1x[2]]; var hasNormalize = normalize(r2r1, false); var r5x = [ hasNormalize[0] * gap, hasNormalize[1] * gap, hasNormalize[2] * gap ]; for (var j = 0; j < r3x; j++) { var r6x = [ parseFloat(r1x[0]) + parseFloat(r5x[0]) * j, parseFloat(r1x[1]) + parseFloat(r5x[1]) * j, parseFloat(r1x[2]) + parseFloat(r5x[2]) * j ]; var r7x = _calculate2dPoint(r6x); if (r7x[0] > maxX) { maxX = r7x[0]; } ret_t[i].line.push(toDecimal(r7x)); item.push(toDecimal(r7x)) } var r6x_t = [ parseFloat(r1x[0]) + parseFloat(r5x[0]) * r3x, parseFloat(r1x[1]) + parseFloat(r5x[1]) * r3x, parseFloat(r1x[2]) + parseFloat(r5x[2]) * r3x ]; var r7x_t = _calculate2dPoint(r6x_t); ret_t[i].line.push(toDecimal(r7x_t)); item.push(toDecimal(r7x_t)) ret.push({ "3d_id": [line[i]["3d_id"]][0], '2d_arr': fixLine(item) }) } // ret2dObj(ret) return { drawArr: ret_t, to3d: ret2dObj(ret) }; // return ret_t; } /** * 生成对应的2d线段 * @param {三维线段数组} arr */ function ret2dObj(arr) { var ret = []; var count = 0 for (var i = 0; i < arr.length; i++) { for (var j = 0; j < arr[i]["2d_arr"].length; j++) { ret.push({ "2d_id": count, "3d_id": i, "2d_point1": arr[i]["2d_arr"][j]["2d_point1"], "2d_point2": arr[i]["2d_arr"][j]["2d_point2"] }) count++ } } return { panorama_line_2d: ret } } function fixLine(arr) { var obj, t_arr = []; for (var i = 0; i < arr.length; i++) { obj = { "2d_point1": arr[i], "2d_point2": arr[i + 1] }; if (obj["2d_point2"] && obj["2d_point2"] != obj["2d_point1"]) { t_arr.push(obj); } } return t_arr; } /** * 四舍五入保留6位小数 * @param {数组} arr */ function toDecimal(arr) { var xf = parseFloat(arr[0]); var yf = parseFloat(arr[1]); if (isNaN(xf) || isNaN(yf)) { return; } tx = Math.round(xf * 10e5) / 10e5; ty = Math.round(yf * 10e5) / 10e5; return [tx, ty]; } function make_matrix_from_quat(quat) { var ret = numbers.matrix.identity(4); var r1x = quat[0] * quat[0] + quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]; if (r1x != 0) { var x2 = (2 / r1x) * quat[0]; var y2 = (2 / r1x) * quat[1]; var z2 = (2 / r1x) * quat[2]; var xx = quat[0] * x2; var xy = quat[0] * y2; var xz = quat[0] * z2; var yy = quat[1] * y2; var yz = quat[1] * z2; var zz = quat[2] * z2; var wx = quat[3] * x2; var wy = quat[3] * y2; var wz = quat[3] * z2; ret[0][0] = 1 - (yy + zz); ret[1][0] = xy - wz; ret[2][0] = xz + wy; ret[0][1] = xy + wz; ret[1][1] = 1 - (xx + zz); ret[2][1] = yz - wx; ret[0][2] = xz - wy; ret[1][2] = yz + wx; ret[2][2] = 1 - (xx + yy); } // ret = numbers.matrix.multiply([[1, 0, 0], [0, 1, 0], [0, 0, 1]], transpose(ret)) var tran = [ [1, 0, 0, quat[4]], [0, 1, 0, quat[5]], [0, 0, 1, quat[6]], [0, 0, 0, 1] ]; ret = numbers.matrix.multiply(tran, transpose(ret)); return ret; // [ // [1, 0, 0, quat[4]], // [0, 1, 0, quat[5]], // [0, 0, 1, quat[6]], // [0 ,0 ,0 ,1] // ] } /** * 矩阵转置 * @param {矩阵} arr */ function transpose(arr) { var arr2 = []; for (var i = 0; i < arr[0].length; i++) { arr2[i] = []; } for (var i = 0; i < arr.length; i++) { for (var j = 0; j < arr[i].length; j++) { arr2[j][i] = arr[i][j]; } } return arr2; } /** * 画线 * @param {*画布} context * @param {*画布宽度 } w * @param {*画布高度} h * @param {*水平缩放倍数} scaleTime * @param {*垂直缩放倍数} scaleTimeH * @param {*线条数组} lineArr * @param {*是否重画} isclear */ function drawSingleLine( context, w, h, scaleTime, scaleTimeH, lineArr, isclear ) { context.beginPath(); if (isclear) { context.clearRect(0, 0, w, h); context.drawImage(img, 0, 0, w, h); } context.moveTo( lineArr[0].line[0][0] * scaleTime, lineArr[0].line[0][1] * scaleTimeH ); for (var i = 0; i < lineArr.length; i++) { if (isTwoLine(lineArr[i].line)[0]) { for (var k = 0; k < lineArr[i].line.length; k++) { if (lineArr[i].line[k][0] < 0.5) { var temp = lineArr[i].line[k][0] + isTwoLine(lineArr[i].line)[1]; if (temp <= 1) { lineArr[i].line.push([temp, lineArr[i].line[k][1]]); } } } } context.moveTo( lineArr[i].line[0][0] * scaleTime, lineArr[i].line[0][1] * scaleTimeH ); for (var j = 0; j < lineArr[i].line.length; j++) { if (j + 1 < lineArr[i].line.length) { // Math.hypot( // lineArr[i].line[j+1][0] - lineArr[i].line[j][0], // lineArr[i].line[j+1][1] - lineArr[i].line[j][1] // ) < 0.05 if (Math.abs(lineArr[i].line[j + 1][0] - lineArr[i].line[j][0]) > 0.5) { context.moveTo( lineArr[i].line[j + 1][0] * scaleTime, lineArr[i].line[j + 1][1] * scaleTimeH ); } else { context.lineTo( lineArr[i].line[j][0] * scaleTime, lineArr[i].line[j][1] * scaleTimeH ); } } if (j + 1 === lineArr[i].line.length) { context.lineTo( lineArr[i].line[j][0] * scaleTime, lineArr[i].line[j][1] * scaleTimeH ); } } } context.stroke(); } /** * 选中伸缩的墙进行画线 * @param {画布} context * @param {画布宽度} w * @param {画布高度} h * @param {水平缩放倍数} scaleTime * @param {垂直缩放倍数} scaleTimeH * @param {线条数组} lineArr */ function selectDragLine(context, w, h, scaleTime, scaleTimeH, lineArr, test) { context.strokeStyle = "#ff0000"; drawSingleLine(context, w, h, scaleTime, scaleTimeH, lineArr, true); context.strokeStyle = "#00ff00"; drawSingleLine(context, w, h, scaleTime, scaleTimeH, test, false); } /** * 找到面得中心坐标 * @param {线段数组} lineArr * @param {水平缩放倍数} scaleTime * @param {垂直缩放倍数} scaleTimeH */ function _calculateMiddlePoint(test, scaleTime, scaleTimeH) { var minX = 1, minY = 1, maxX = 0, maxY = 0; for (var i = 0; i < test.length; i++) { for (var j = 0; j < test[i].line.length; j++) { minX = Math.min(minX, test[i].line[j][0]); minY = Math.min(minY, test[i].line[j][1]); maxX = Math.max(maxX, test[i].line[j][0]); maxY = Math.max(maxY, test[i].line[j][1]); } } return [ ((maxX - minX) / 2 + minX) * scaleTime, ((maxY - minY) / 2 + minY) * scaleTimeH ]; } /** * 计算墙体中心和鼠标坐标点的距离 * @param {当前鼠标点击的坐标点} current * @param {墙体的中心点坐标} middle */ function _calculateMiddlePointDistance(current, middle) { return _calculateDistance(current, middle); } /** * 计算两点之间的距离 * @param {坐标点} p1 * @param {坐标点} p2 */ function _calculateDistance(p1, p2) { return Math.sqrt(Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[0] - p1[0], 2)); } /** * 判断点是否在墙体上 * @param {鼠标当前坐标点} current * @param {墙体线段} lineArr * @param {水平缩放倍数} scaleTime * @param {垂直缩放倍数} scaleTimeH */ function isInFace(current, test, scaleTime, scaleTimeH) { var fixArr = []; var selectYArr = []; var minX = 1, minY = 1, maxX = 0, maxY = 0; for (var i = 0; i < test.length; i++) { for (var j = 0; j < test[i].line.length; j++) { minX = Math.min(minX, test[i].line[j][0]); // minY = Math.min(minY, test[i].line[j][1]); maxX = Math.max(maxX, test[i].line[j][0]); // maxY = Math.max(maxY, test[i].line[j][1]); fixArr.push({ id: parseFloat(test[i].line[j][0]).toFixed(1), y: test[i].line[j][1] }); } } for (var i = 0; i < fixArr.length; i++) { if (fixArr[i].id == parseFloat(current[0] / scaleTime).toFixed(1)) { selectYArr.push(fixArr[i].y); } } for (var i = 0; i < selectYArr.length; i++) { minY = Math.min(minY, selectYArr[i]); maxY = Math.max(maxY, selectYArr[i]); } return ( current[0] > minX * scaleTime && current[0] < maxX * scaleTime && current[1] > minY * scaleTimeH && current[1] < maxY * scaleTimeH ); } /** * 更新墙体线段的位置 * @param {缩放的大小} distance * @param {墙体线段} lineArr */ function updateLineArr(distance, lineArr, _3dPoint, tag) { // deltaX:值为负的(-1),则表示滚轮向左滚动。值为正的(1),则表示滚轮向右滚动。 // deltaY:值为负的(-1),则表示滚轮向下滚动。值为正的(1),则表示滚轮向上滚动。 // deltaFactor:增量因子。通过 deltaFactor * deltaX 或者 deltaFactor * deltaY 可以得到浏览器实际的滚动距离。 // distance = -10 if (tag) { _3dPoint[2][0] += distance _3dPoint[3][0] += distance _3dPoint[((_3dPoint.length) / 2 + 2)][0] += distance _3dPoint[((_3dPoint.length) / 2 + 3)][0] += distance } else { _3dPoint[1][1] += distance _3dPoint[2][1] += distance _3dPoint[((_3dPoint.length) / 2 + 1)][1] += distance _3dPoint[((_3dPoint.length) / 2 + 2)][1] += distance } rx1 = _3dLine(r2x, r3x, ret); lineArr = _3dTo2d( make_matrix_from_quat([ 0.008515, -0.014279, 0.016179, 0.999731, -5.438891, 2.167653, 0.165233 ]), rx1, 0.05 )["drawArr"]; drawGeometry(rx1) return lineArr; } function updateSelectArr(lineArr, idxArr) { var ret = []; for (var i = 0; i < idxArr.length; i++) { ret.push(lineArr[idxArr[i]]) } return ret } function _culaculateFaceArr(lineArr, faceArr) { var ret = []; for (var i = 0; i < faceArr.length; i++) { var item = []; for (var j = 0; j < faceArr[i].length; j++) { item.push(lineArr[faceArr[i][j]]); } ret.push({ id: i, arr: item }); } return ret; } function selectWhichFace(lineArr, faceArr, current, scaleTime, scaleTimeH) { var allFaceArr = _culaculateFaceArr(lineArr, faceArr); var ret = 0; var tempRet = 0; var minDist = faceArr && _calculateMiddlePointDistance( current, _calculateMiddlePoint(allFaceArr[0].arr, scaleTime, scaleTimeH) ); for (var i = 0; i < allFaceArr.length; i++) { if (isInFace(current, allFaceArr[i].arr, scaleTime, scaleTimeH)) { // tempRet = i ret = i if ( _calculateMiddlePointDistance( current, _calculateMiddlePoint(allFaceArr[i].arr, scaleTime, scaleTimeH) ) < minDist ) { minDist = _calculateMiddlePointDistance( current, _calculateMiddlePoint(allFaceArr[i].arr, scaleTime, scaleTimeH) ); } else { ret = i } } } return allFaceArr[ret]; } /** * 求与该平面相交的线段的相关信息 * @param {array} faceLine 面的索引数组 * @return {object} verticalLineData */ function findverticalLine(faceLine) { let lineAry = []; let verticalLineData = { lineAry: [], vecAry: [], verticalVec: [] } for (let k = 0; k < faceLine.length; k++) { n_panorama_line_3d.find(item => { if (item['3d_id'] === faceLine[k]) { for (let l = 0; l < geoKeys.length; l++) { let sta_point = item[geoKeys[l]]; for (let i = 0; i < n_panorama_line_3d.length; i++) { for (let j = 0; j < geoKeys.length; j++) { let line = n_panorama_line_3d[i]; let curpoint = line[geoKeys[j]]; let id = n_panorama_line_3d[i]['3d_id']; // 如果相交线段在当前面中, 则求它对应的向量 if (arrayEquals(sta_point, curpoint) && faceLine.indexOf(id) >= 0 && verticalLineData.vecAry.length < 2) { let croodData = getThreeDVec(line['3d_point1'], line['3d_point2']); let vec3 = new THREE.Vector3(croodData[0], croodData[1], croodData[2]) verticalLineData.vecAry.push(vec3); } if (arrayEquals(sta_point, curpoint) && faceLine.indexOf(id) < 0 && verticalLineData.lineAry.indexOf(id) < 0) { verticalLineData.lineAry.push(id); } } } } } }) } let crossVec = new THREE.Vector3(); crossVec.crossVectors(verticalLineData.vecAry[0], verticalLineData.vecAry[1]); verticalLineData.verticalVec.push(crossVec.x, crossVec.y, crossVec.z) // console.log(tmp); // console.log(lineAry); return lineAry; } /** * 计算三维空间中的向量 * @param {array} point1 * @param {array} point2 */ function getThreeDVec(point1, point2) { if (!point1 || !point2 || (point1.length !== point2.length)) { console.log('点数据有误, 请检查'); return; } let vec = []; for (let i = 0; i < point1.length; i++) { let val = point1[i] - point2[i]; vec.push(val); } if (vec.length < 3) { console.log('所求三维向量有误, 请检查'); return; } return vec; } /** * 根据sta查找点 * @param {array} line1 第一条线段的点坐标 * @param {array} line2 第二条线段的点坐标 * @param {Boolean} sta 决定查找相同的点或不相同的点 */ function findPoint(line1, line2, sta) { if (!line1 || !line2) { console.log('线段未定义' + line1 + ' ' + line2); return null; } if (line1.length < 2 || line2.length < 2) { console.log('线段点坐标个数小于3, 请检查') return } if (sta === null || typeof sta === 'undefined') { sta = true; } // 根据sta值寻找当前线段与下一线段中符合要求的点 for (let i = 0; i < line1.length; i++) { // sta为false, 寻找与下一线段都不相同的点 if (!sta && sta === arrayEquals(line1[i], line2[0]) && sta === arrayEquals(line1[i], line2[1])) { return line1[i]; } // sta为true, 寻找与下一线段相交的点 if (sta && (sta === arrayEquals(line1[i], line2[0]) || sta === arrayEquals(line1[i], line2[1]))) { return line1[i]; } } return null; } /** * 判断两个数组是否相等 * @param {array} array1 * @param {array} array2 */ function arrayEquals(array1, array2) { if (!array1 || !array2) { return false; } if (array1.length !== array2.length) { return false; } for (let i = 0, len = array1.length; i < len; i++) { if (array1[i] instanceof Array && array2[i] instanceof Array) { if (!equals(array1[i], array2[2])) { return false; } } else if (array1[i] !== array2[i]) { return false; } } return true; } /** * 判断一个数组中的元素是否都包含在另一个数组中 * @param {array} arrays * @param {array} array2 * @returns {boolean} true-表示数组元素唯一, false表示数组元素不唯一 */ function arrayOnly(array1, array2) { if (!array1 || !array2) { console.log('数组未定义, 请检查'); return; } if (!array1.length || !array2.length || array1.length === 0) { return false; } for (let i = 0; i < array1.length; i++) { let num = 0; for (let j = 0; j < array1[i].length; j++) { if (array2.indexOf(array1[i][j]) >= 0) { // return false; num++; } } if (num === array1[i].length) { return true; } } return false; }