|
@@ -0,0 +1,808 @@
|
|
|
+import * as THREE from 'three'
|
|
|
+
|
|
|
+var math = {
|
|
|
+ getBaseLog(x, y) {
|
|
|
+ //返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数
|
|
|
+ return Math.log(y) / Math.log(x)
|
|
|
+ },
|
|
|
+ convertVisionVector: function (e) {
|
|
|
+ return new THREE.Vector3(e.x, e.z, -e.y)
|
|
|
+ },
|
|
|
+ invertVisionVector: function (e) {
|
|
|
+ //反转给算法部
|
|
|
+ return new THREE.Vector3(e.x, -e.z, e.y)
|
|
|
+ },
|
|
|
+ convertVisionQuaternion: function (e) {
|
|
|
+ return new THREE.Quaternion(e.x, e.z, -e.y, e.w).multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(90)))
|
|
|
+ },
|
|
|
+ invertVisionQuaternion: function (e) {
|
|
|
+ //反转给算法部
|
|
|
+ var a = e.clone().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(-90)))
|
|
|
+ return new THREE.Quaternion(a.x, -a.z, a.y, a.w)
|
|
|
+ },
|
|
|
+ convertWorkshopVector: function (e) {
|
|
|
+ return new THREE.Vector3(-e.x, e.y, e.z)
|
|
|
+ },
|
|
|
+ convertWorkshopQuaternion: function (e) {
|
|
|
+ return new THREE.Quaternion(-e.x, e.y, e.z, -e.w).multiply(new THREE.Quaternion(Math.sqrt(2) / 2, Math.sqrt(2) / 2, 0, 0))
|
|
|
+ },
|
|
|
+ convertWorkshopPanoramaQuaternion: function (e) {
|
|
|
+ return new THREE.Quaternion(e.x, -e.y, -e.z, e.w).normalize().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(270)))
|
|
|
+ },
|
|
|
+ convertWorkshopOrthoZoom: function (e, dom) {
|
|
|
+ //xzw
|
|
|
+ return e === -1 ? -1 : e * (dom.clientHeight / dom.clientHeight)
|
|
|
+ },
|
|
|
+
|
|
|
+ getVec2Angle: function (dir1, dir2) {
|
|
|
+ return Math.acos(THREE.MathUtils.clamp(this.getVec2Cos(dir1, dir2), -1, 1))
|
|
|
+ },
|
|
|
+ getVec2Cos: function (dir1, dir2) {
|
|
|
+ return dir1.dot(dir2) / dir1.length() / dir2.length()
|
|
|
+ },
|
|
|
+
|
|
|
+ closeTo: function (a, b, num) {
|
|
|
+ if (num != void 0) return Math.abs(a - b) < num
|
|
|
+ return Math.abs(a - b) < 1e-6
|
|
|
+ },
|
|
|
+ toPrecision: function (e, t) {
|
|
|
+ //xzw change 保留小数
|
|
|
+ var f = function (e, t) {
|
|
|
+ var i = Math.pow(10, t)
|
|
|
+ return Math.round(e * i) / i
|
|
|
+ }
|
|
|
+ if (e instanceof Array) {
|
|
|
+ for (var s = 0; s < e.length; s++) {
|
|
|
+ e[s] = f(e[s], t)
|
|
|
+ }
|
|
|
+ return e
|
|
|
+ } else if (e instanceof Object) {
|
|
|
+ for (var s in e) {
|
|
|
+ e[s] = f(e[s], t)
|
|
|
+ }
|
|
|
+ return e
|
|
|
+ } else return f(e, t)
|
|
|
+ },
|
|
|
+ isEmptyQuaternion: function (e) {
|
|
|
+ return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w)
|
|
|
+ },
|
|
|
+ projectPositionToCanvas: function (e, t, i, domE) {
|
|
|
+ ;(i = i || new THREE.Vector3()), i.copy(e)
|
|
|
+ var r = 0.5 * domE.clientWidth,
|
|
|
+ o = 0.5 * domE.clientHeight
|
|
|
+ return i.project(t), (i.x = i.x * r + r), (i.y = -(i.y * o) + o), i
|
|
|
+ },
|
|
|
+ convertScreenPositionToNDC: function (e, t, i, domE) {
|
|
|
+ /* return i = i || new THREE.Vector2,
|
|
|
+ i.x = e / window.innerWidth * 2 - 1,
|
|
|
+ i.y = 2 * -(t / window.innerHeight) + 1,
|
|
|
+ i
|
|
|
+ */
|
|
|
+
|
|
|
+ return (i = i || new n.Vector2()), (i.x = (e / domE.clientWidth) * 2 - 1), (i.y = 2 * -(t / domE.clientHeight) + 1), i
|
|
|
+ },
|
|
|
+
|
|
|
+ handelPadding: (function () {
|
|
|
+ //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
|
|
|
+
|
|
|
+ var pads = new Map() //记录下来避免反复计算
|
|
|
+
|
|
|
+ return function (x, y, domE) {
|
|
|
+ let pad
|
|
|
+
|
|
|
+ let padInfo = pads.get(domE)
|
|
|
+ if (padInfo) {
|
|
|
+ if (domE.clientWidth == padInfo.width && domE.clientHeight == padInfo.height) {
|
|
|
+ pad = padInfo.pad
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!pad) {
|
|
|
+ pad = {
|
|
|
+ x: this.getOffset('left', domE),
|
|
|
+ y: this.getOffset('top', domE),
|
|
|
+ }
|
|
|
+
|
|
|
+ pads.set(domE, {
|
|
|
+ width: domE.clientWidth,
|
|
|
+ height: domE.clientHeight,
|
|
|
+ pad,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ x: x - pad.x,
|
|
|
+ y: y - pad.y,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+
|
|
|
+ getOffset: function (type, element, parent) {
|
|
|
+ //获取元素的边距 许钟文
|
|
|
+ var offset = type == 'left' ? element.offsetLeft : element.offsetTop
|
|
|
+ if (!parent) parent = document.body
|
|
|
+ while ((element = element.offsetParent)) {
|
|
|
+ if (element == parent) break
|
|
|
+ offset += type == 'left' ? element.offsetLeft : element.offsetTop
|
|
|
+ }
|
|
|
+ return offset
|
|
|
+ },
|
|
|
+
|
|
|
+ constrainedTurn: function (e) {
|
|
|
+ var t = e % (2 * Math.PI)
|
|
|
+ return (t = t > Math.PI ? (t -= 2 * Math.PI) : t < -Math.PI ? (t += 2 * Math.PI) : t)
|
|
|
+ },
|
|
|
+ getFOVDotThreshold: function (e) {
|
|
|
+ return Math.cos(THREE.MathUtils.degToRad(e / 2))
|
|
|
+ },
|
|
|
+ transform2DForwardVectorByCubeFace: function (e, t, i, n) {
|
|
|
+ switch (e) {
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
|
|
+ i.set(1, t.y, t.x)
|
|
|
+ break
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
|
|
+ i.set(-1, t.y, -t.x)
|
|
|
+ break
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
|
|
+ i.set(-t.x, 1, -t.y)
|
|
|
+ break
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
|
|
+ i.set(-t.x, -1, t.y)
|
|
|
+ break
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
|
|
+ i.set(-t.x, t.y, 1)
|
|
|
+ break
|
|
|
+ case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
|
|
+ i.set(t.x, t.y, -1)
|
|
|
+ }
|
|
|
+ n && i.normalize()
|
|
|
+ },
|
|
|
+
|
|
|
+ getFootPoint: function (oldPos, p1, p2, restricInline) {
|
|
|
+ //找oldPos在线段p1, p2上的垂足
|
|
|
+ /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position
|
|
|
+ p1 = p1.clone();
|
|
|
+ p2 = p2.clone();
|
|
|
+ p1.y += mainDesign.meshGroup.position.y;
|
|
|
+ p2.y += mainDesign.meshGroup.position.y;
|
|
|
+ } */
|
|
|
+ var op1 = oldPos.clone().sub(p1)
|
|
|
+ var p1p2 = p1.clone().sub(p2)
|
|
|
+ var p1p2Len = p1p2.length()
|
|
|
+ var leftLen = op1.dot(p1p2) / p1p2Len
|
|
|
+ var pos = p1.clone().add(p1p2.multiplyScalar(leftLen / p1p2Len))
|
|
|
+
|
|
|
+ if (restricInline && pos.clone().sub(p1).dot(pos.clone().sub(p2)) > 0) {
|
|
|
+ //foot不在线段上
|
|
|
+ if (pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone()
|
|
|
+ else pos = p2.clone()
|
|
|
+ }
|
|
|
+
|
|
|
+ return pos
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算多边形的重心
|
|
|
+ * @param {*} points
|
|
|
+ */
|
|
|
+ getCenterOfGravityPoint: function (mPoints) {
|
|
|
+ var area = 0.0 //多边形面积
|
|
|
+ var Gx = 0.0,
|
|
|
+ Gy = 0.0 // 重心的x、y
|
|
|
+
|
|
|
+ for (var i = 1; i <= mPoints.length; i++) {
|
|
|
+ var ix = mPoints[i % mPoints.length].x
|
|
|
+ var iy = mPoints[i % mPoints.length].y
|
|
|
+ var nx = mPoints[i - 1].x
|
|
|
+ var ny = mPoints[i - 1].y
|
|
|
+ var temp = (ix * ny - iy * nx) / 2.0
|
|
|
+ area += temp
|
|
|
+ Gx += (temp * (ix + nx)) / 3.0
|
|
|
+ Gy += (temp * (iy + ny)) / 3.0
|
|
|
+ }
|
|
|
+ Gx = Gx / area
|
|
|
+ Gy = Gy / area
|
|
|
+ return { x: Gx, y: Gy }
|
|
|
+ },
|
|
|
+
|
|
|
+ getBound: function (ring) {
|
|
|
+ var bound = new THREE.Box2()
|
|
|
+ for (var j = 0, len = ring.length; j < len; j++) {
|
|
|
+ bound.expandByPoint(ring[j])
|
|
|
+ }
|
|
|
+ return bound
|
|
|
+ },
|
|
|
+
|
|
|
+ isPointInArea: function (ring, point, ifAtLine) {
|
|
|
+ //判断点是否在某个环内
|
|
|
+ var bound = this.getBound(ring)
|
|
|
+ if (point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y) return false
|
|
|
+
|
|
|
+ var inside = false
|
|
|
+ var x = point.x,
|
|
|
+ y = point.y
|
|
|
+
|
|
|
+ for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
|
+ var xi = ring[i].x,
|
|
|
+ yi = ring[i].y
|
|
|
+ var xj = ring[j].x,
|
|
|
+ yj = ring[j].y
|
|
|
+
|
|
|
+ if (
|
|
|
+ (xi - x) * (yj - y) == (xi - x) * (yi - y) &&
|
|
|
+ x >= Math.min(xi, xj) &&
|
|
|
+ x <= Math.max(xi, xj) && //xzw add
|
|
|
+ y >= Math.min(yi, yj) &&
|
|
|
+ y <= Math.max(yi, yj)
|
|
|
+ ) {
|
|
|
+ return !!ifAtLine //在线段上,则判断为…… (默认在外)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
|
|
|
+ inside = !inside
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return inside
|
|
|
+ },
|
|
|
+
|
|
|
+ getArea: function (ring) {
|
|
|
+ //求面积 顺时针为正 来自three shape
|
|
|
+ for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++) i += ring[n].x * ring[r].y - ring[r].x * ring[n].y
|
|
|
+ return -0.5 * i
|
|
|
+ },
|
|
|
+ isInBetween: function (a, b, c, precision) {
|
|
|
+ // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
|
|
|
+
|
|
|
+ /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (a <= b && b <= c) || (c <= b && b <= a);*/
|
|
|
+
|
|
|
+ //更改:如果b和a或c中一个接近 就算在a和c之间
|
|
|
+ return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a, b, precision) || this.closeTo(b, c, precision)
|
|
|
+ },
|
|
|
+
|
|
|
+ ifPointAtLineBound: function (point, linePoints, precision) {
|
|
|
+ //待验证 横线和竖线比较特殊
|
|
|
+ return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision)
|
|
|
+ },
|
|
|
+
|
|
|
+ isLineIntersect: function (line1, line2, notSegment) {
|
|
|
+ //线段和线段是否有交点. notSegment代表是直线而不是线段
|
|
|
+ var a1 = line1[1].y - line1[0].y
|
|
|
+ var b1 = line1[0].x - line1[1].x
|
|
|
+ var c1 = a1 * line1[0].x + b1 * line1[0].y
|
|
|
+ //转换成一般式: Ax+By = C
|
|
|
+ var a2 = line2[1].y - line2[0].y
|
|
|
+ var b2 = line2[0].x - line2[1].x
|
|
|
+ var c2 = a2 * line2[0].x + b2 * line2[0].y
|
|
|
+ // 计算交点
|
|
|
+ var d = a1 * b2 - a2 * b1
|
|
|
+
|
|
|
+ // 当d==0时,两线平行
|
|
|
+ if (d == 0) {
|
|
|
+ return false
|
|
|
+ } else {
|
|
|
+ var x = (b2 * c1 - b1 * c2) / d
|
|
|
+ var y = (a1 * c2 - a2 * c1) / d
|
|
|
+
|
|
|
+ // 检测交点是否在两条线段上
|
|
|
+ /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
|
|
|
+ (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
|
|
|
+ return {x,y};
|
|
|
+ } */
|
|
|
+ if (notSegment || (math.ifPointAtLineBound({ x, y }, line1) && math.ifPointAtLineBound({ x, y }, line2))) {
|
|
|
+ return { x, y }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ getNormal: function (line2d) {
|
|
|
+ //获取二维法向量 方向向内
|
|
|
+ var x, y //要求的向量
|
|
|
+ //line2d的向量
|
|
|
+ var x1 = line2d.points[1].x - line2d.points[0].x
|
|
|
+ var y1 = line2d.points[1].y - line2d.points[0].y
|
|
|
+ //假设法向量的x或y固定为1或-1
|
|
|
+ if (y1 != 0) {
|
|
|
+ x = 1
|
|
|
+ y = -(x1 * x) / y1
|
|
|
+ } else if (x1 != 0) {
|
|
|
+ //y如果为0,正常情况x不会是0
|
|
|
+ y = 1
|
|
|
+ x = -(y1 * y) / x1
|
|
|
+ } else {
|
|
|
+ console.log('两个点一样')
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断方向里或者外:
|
|
|
+ var vNormal = new THREE.Vector3(x, 0, y)
|
|
|
+ var vLine = new THREE.Vector3(x1, 0, y1)
|
|
|
+ var vDir = vNormal.cross(vLine)
|
|
|
+ if (vDir.y > 0) {
|
|
|
+ x *= -1
|
|
|
+ y *= -1
|
|
|
+ }
|
|
|
+ return new THREE.Vector2(x, y).normalize()
|
|
|
+ },
|
|
|
+
|
|
|
+ getQuaBetween2Vector: function (oriVec, newVec, upVec) {
|
|
|
+ //获取从oriVec旋转到newVec可以应用的quaternion
|
|
|
+ var angle = oriVec.angleTo(newVec)
|
|
|
+ var axis = oriVec.clone().cross(newVec).normalize() //两个up之间
|
|
|
+ if (axis.length() == 0) {
|
|
|
+ //当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec
|
|
|
+ return new THREE.Quaternion().setFromAxisAngle(upVec, angle)
|
|
|
+ }
|
|
|
+ return new THREE.Quaternion().setFromAxisAngle(axis, angle)
|
|
|
+ },
|
|
|
+
|
|
|
+ getScaleForConstantSize: (function () {
|
|
|
+ //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
|
|
|
+ var w
|
|
|
+ var i = new THREE.Vector3(),
|
|
|
+ o = new THREE.Vector3(),
|
|
|
+ l = new THREE.Vector3(),
|
|
|
+ c = new THREE.Vector3(),
|
|
|
+ h = new THREE.Vector3()
|
|
|
+ return function (op = {}) {
|
|
|
+ if (op.width2d) w = op.width2d
|
|
|
+ //如果恒定二维宽度
|
|
|
+ else {
|
|
|
+ var currentDis
|
|
|
+ if (op.camera.type == 'OrthographicCamera') {
|
|
|
+ //floorplan要直接使用activeControl.camera,主要用到projectionMatrix
|
|
|
+ currentDis = (op.camera.right - op.camera.left) / op.camera.zoom / 3
|
|
|
+ } else {
|
|
|
+ currentDis = op.position.distanceTo(op.camera.position) //dollhouse要直接使用player.camera, 因为activeControl.camera没有更新matrixWorld
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((op.nearBound == void 0 && op.farBound != void 0) || (op.nearBound != void 0 && op.farBound == void 0)) {
|
|
|
+ //仅限制最大或最小的话,不判断像素大小,直接限制mesh的scale
|
|
|
+ //这个判断也可以写到getScaleForConstantSize里,可以更严谨控制像素宽度,这里只简单计算大小
|
|
|
+ let scale
|
|
|
+ if (op.farBound == void 0 && currentDis < op.nearBound) {
|
|
|
+ scale = (op.scale * currentDis) / op.nearBound
|
|
|
+ } else if (op.nearBound == void 0 && currentDis > op.farBound) {
|
|
|
+ scale = (op.scale * currentDis) / op.farBound
|
|
|
+ } else {
|
|
|
+ scale = op.scale
|
|
|
+ }
|
|
|
+ return scale
|
|
|
+ }
|
|
|
+
|
|
|
+ w = op.maxSize - (op.maxSize - op.minSize) * THREE.MathUtils.smoothstep(currentDis, op.nearBound, op.farBound)
|
|
|
+ //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
|
|
|
+ }
|
|
|
+ i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
|
|
|
+ o.set(op.dom.clientWidth / 2, op.dom.clientHeight / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
|
|
|
+ l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
|
|
|
+ c.set(2 / op.dom.clientWidth, 2 / op.dom.clientHeight, 1).multiply(l), //再转回 -1 到 1的范围
|
|
|
+ h.copy(c).unproject(op.camera) //再转成三维坐标,求得tag边缘的位置
|
|
|
+ var g = h.distanceTo(op.position) //就能得到tag的三维半径
|
|
|
+
|
|
|
+ return g
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+
|
|
|
+ //W , H, left, top分别是rect的宽、高、左、上
|
|
|
+ getCrossPointAtRect: function (p1, aim, W, H, left, top) {
|
|
|
+ //求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上)
|
|
|
+
|
|
|
+ var x, y, borderX
|
|
|
+ var r = (aim.x - p1.x) / (aim.y - p1.y) //根据相似三角形原理先求出这个比值
|
|
|
+ var getX = function (y) {
|
|
|
+ return r * (y - p1.y) + p1.x
|
|
|
+ }
|
|
|
+ var getY = function (x) {
|
|
|
+ return (1 / r) * (x - p1.x) + p1.y
|
|
|
+ }
|
|
|
+ if (aim.x >= p1.x) {
|
|
|
+ borderX = W + left
|
|
|
+ } else {
|
|
|
+ borderX = left
|
|
|
+ }
|
|
|
+ x = borderX
|
|
|
+ y = getY(x)
|
|
|
+ if (y < top || y > top + H) {
|
|
|
+ if (y < top) {
|
|
|
+ y = top
|
|
|
+ } else {
|
|
|
+ y = top + H
|
|
|
+ }
|
|
|
+ x = getX(y)
|
|
|
+ }
|
|
|
+ return new THREE.Vector2(x, y)
|
|
|
+ },
|
|
|
+ getDirFromUV: function (uv) {
|
|
|
+ //获取dir 反向计算 - - 二维转三维比较麻烦
|
|
|
+ var dirB //所求 单位向量
|
|
|
+
|
|
|
+ uv.x %= 1
|
|
|
+ if (uv.x < 0) uv.x += 1 //调整为0-1
|
|
|
+
|
|
|
+ var y = Math.cos(uv.y * Math.PI) //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
|
|
|
+
|
|
|
+ var angle = 2 * Math.PI * uv.x - Math.PI //x/z代表的是角度
|
|
|
+
|
|
|
+ var axisX, axisZ //axis为1代表是正,-1是负数
|
|
|
+ if (-Math.PI <= angle && angle < 0) {
|
|
|
+ axisX = -1 //下半圆
|
|
|
+ } else {
|
|
|
+ axisX = 1 //上半圆
|
|
|
+ }
|
|
|
+ if (-Math.PI / 2 <= angle && angle < Math.PI / 2) {
|
|
|
+ axisZ = 1 //右半圆
|
|
|
+ } else {
|
|
|
+ axisZ = -1 //左半圆
|
|
|
+ }
|
|
|
+
|
|
|
+ var XDivideZ = Math.tan(angle)
|
|
|
+ var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ))
|
|
|
+ var x = XDivideZ * z
|
|
|
+
|
|
|
+ if (z * axisZ < 0) {
|
|
|
+ //异号
|
|
|
+ z *= -1
|
|
|
+ x *= -1
|
|
|
+ if (x * axisX < 0) {
|
|
|
+ // console.log("wrong!!!!!??????????")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ x *= -1 //计算完成后这里不能漏掉 *= -1
|
|
|
+ dirB = new THREE.Vector3(x, y, z)
|
|
|
+
|
|
|
+ //理想状态下x和z和anotherDir相同
|
|
|
+ return dirB
|
|
|
+ },
|
|
|
+
|
|
|
+ getUVfromDir: function (dir) {
|
|
|
+ //获取UV 同shader里的计算
|
|
|
+ var dir = dir.clone()
|
|
|
+ dir.x *= -1 //计算前这里不能漏掉 *= -1 见shader
|
|
|
+ var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5 //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值
|
|
|
+ var ty = Math.acos(dir.y) / Math.PI
|
|
|
+ return { x: tx, y: ty }
|
|
|
+
|
|
|
+ //理想状态下tx相同
|
|
|
+ },
|
|
|
+ crossRight: function (vec3, matrix) {
|
|
|
+ //向量右乘矩阵,不能用向量的applyMatrix4(左乘)
|
|
|
+ var e = matrix.elements
|
|
|
+ var v = new THREE.Vector3()
|
|
|
+ v.x = e[0] * vec3.x + e[1] * vec3.y + e[2] * vec3.z + e[3]
|
|
|
+ v.y = e[4] * vec3.x + e[5] * vec3.y + e[6] * vec3.z + e[7]
|
|
|
+ v.z = e[8] * vec3.x + e[9] * vec3.y + e[10] * vec3.z + e[11]
|
|
|
+ //v.w不要
|
|
|
+ return v
|
|
|
+ },
|
|
|
+ getNormalDir: function (point, supportsTiles, currentPano) {
|
|
|
+ //获取A单位法线
|
|
|
+ /* console.log("lookVector:")
|
|
|
+ console.log(objects.player.cameraControls.activeControl.lookVector) */
|
|
|
+
|
|
|
+ var dir = point.clone().sub(currentPano.position) //OA
|
|
|
+ /* console.log("A的dir(无matrix转化):")
|
|
|
+ console.log(dir.clone().normalize()); */
|
|
|
+
|
|
|
+ if (supportsTiles) {
|
|
|
+ var matrixWorld = currentPano.rot90Matrix.clone() //因为热点求点时所右乘的matrix必须是单张全景照片时用的转90度的matrix才行
|
|
|
+ } else {
|
|
|
+ var matrixWorld = currentPano.matrixWorld.clone()
|
|
|
+ }
|
|
|
+ dir = this.crossRight(dir, matrixWorld) //右乘matrixWorld 得matrix转化的向量
|
|
|
+ dir.normalize()
|
|
|
+ /* var b = player.currentPano.skyboxMesh.matrixWorld.clone().getInverse(player.currentPano.skyboxMesh.matrixWorld)
|
|
|
+ console.log(crossRight(dir,b).normalize()) */
|
|
|
+
|
|
|
+ return dir
|
|
|
+ },
|
|
|
+ getDirByLonLat: function (lon, lat) {
|
|
|
+ var dir = new THREE.Vector3()
|
|
|
+ var phi = THREE.MathUtils.degToRad(90 - lat)
|
|
|
+ var theta = THREE.MathUtils.degToRad(lon)
|
|
|
+ dir.x = Math.sin(phi) * Math.cos(theta)
|
|
|
+ dir.y = Math.cos(phi)
|
|
|
+ dir.z = Math.sin(phi) * Math.sin(theta)
|
|
|
+ return dir
|
|
|
+ }, //0,0 => (1,0,0) 270=>(0,0,-1)
|
|
|
+
|
|
|
+ getLineIntersect2(o = {}) {
|
|
|
+ //得两条直线在其向量构成的面的法线方向的交点,投影在线上点的中点。
|
|
|
+ if (o.A != void 0) {
|
|
|
+ // Ap1为一条线,Bp2为一条线
|
|
|
+ var A = o.A
|
|
|
+ var B = o.B
|
|
|
+ var p1 = o.p1
|
|
|
+ var p2 = o.p2
|
|
|
+
|
|
|
+ var dir0 = o.dir0 || new THREE.Vector3().subVectors(p1, A)
|
|
|
+ var dir1 = o.dir1 || new THREE.Vector3().subVectors(p2, B)
|
|
|
+ }
|
|
|
+ if (A.equals(B)) return { pos3d: p1.clone() }
|
|
|
+
|
|
|
+ //寻找两个向量所在的面
|
|
|
+ let normal = dir0.clone().cross(dir1) //面的法线
|
|
|
+
|
|
|
+ //先把整体旋转到使在xz平面上,求完交点再转回来
|
|
|
+ var qua = math.getQuaBetween2Vector(normal, new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 1, 0))
|
|
|
+
|
|
|
+ let newPoints = [A, B, p1, p2].map(e => {
|
|
|
+ return e.clone().applyQuaternion(qua)
|
|
|
+ })
|
|
|
+
|
|
|
+ var pos2d = math.isLineIntersect(
|
|
|
+ [
|
|
|
+ { x: newPoints[0].x, y: newPoints[0].z },
|
|
|
+ { x: newPoints[2].x, y: newPoints[2].z },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ { x: newPoints[1].x, y: newPoints[1].z },
|
|
|
+ { x: newPoints[3].x, y: newPoints[3].z },
|
|
|
+ ],
|
|
|
+ true
|
|
|
+ )
|
|
|
+ var quaInverse = qua.clone().invert()
|
|
|
+
|
|
|
+ let pos3d = new THREE.Vector3(pos2d.x, 0, pos2d.y)
|
|
|
+ let pos3d1 = pos3d.clone().setY(newPoints[0].y)
|
|
|
+ let pos3d2 = pos3d.clone().setY(newPoints[1].y)
|
|
|
+
|
|
|
+ pos3d1.applyQuaternion(quaInverse)
|
|
|
+ pos3d2.applyQuaternion(quaInverse)
|
|
|
+
|
|
|
+ pos3d = new THREE.Vector3().addVectors(pos3d1, pos3d2).multiplyScalar(0.5)
|
|
|
+ return { pos3d, mid1: pos3d1, mid2: pos3d2 }
|
|
|
+ },
|
|
|
+
|
|
|
+ getLineIntersect(o) {
|
|
|
+ //两条三维直线相交 //取两线最短线段中心点 并且不能超出起点
|
|
|
+ o = o || {}
|
|
|
+ if (o.A != void 0) {
|
|
|
+ // Ap1为一条线,Bp2为一条线
|
|
|
+ var A = o.A
|
|
|
+ var B = o.B
|
|
|
+ var p1 = o.p1
|
|
|
+ var p2 = o.p2
|
|
|
+ }
|
|
|
+
|
|
|
+ if (A.equals(B)) return { pos3d: p1.clone() }
|
|
|
+
|
|
|
+ /* console.log("v1:")
|
|
|
+ console.log(A.clone().sub(p1).normalize())
|
|
|
+ console.log("v2:")
|
|
|
+ console.log(B.clone().sub(p2).normalize())
|
|
|
+ */
|
|
|
+
|
|
|
+ //调试热点夹角
|
|
|
+ var line1 = p1.clone().sub(A).normalize()
|
|
|
+ var line2 = p2.clone().sub(B).normalize()
|
|
|
+ var angle = line1.angleTo(line2)
|
|
|
+ //var pano = player.model.panos.index[player.posGets.list[1]]
|
|
|
+ //console.log('真实两线夹角: ', THREE.MathUtils.radToDeg(angle)) /*+ "旧夹角min: "+getAngle(pano.recentAngleScore)+"("+pano.recentAngleScore+")" */
|
|
|
+
|
|
|
+ //----------
|
|
|
+
|
|
|
+ var compute = function () {
|
|
|
+ var pos3d
|
|
|
+ var ux = p1.x - A.x
|
|
|
+ var uy = p1.y - A.y
|
|
|
+ var uz = p1.z - A.z
|
|
|
+
|
|
|
+ var vx = p2.x - B.x
|
|
|
+ var vy = p2.y - B.y
|
|
|
+ var vz = p2.z - B.z
|
|
|
+
|
|
|
+ var wx = A.x - B.x
|
|
|
+ var wy = A.y - B.y
|
|
|
+ var wz = A.z - B.z
|
|
|
+
|
|
|
+ var a = ux * ux + uy * uy + uz * uz
|
|
|
+ //u*u
|
|
|
+ var b = ux * vx + uy * vy + uz * vz
|
|
|
+ //u*v
|
|
|
+ var c = vx * vx + vy * vy + vz * vz
|
|
|
+ //v*v
|
|
|
+ var d = ux * wx + uy * wy + uz * wz
|
|
|
+ //u*w
|
|
|
+ var e = vx * wx + vy * wy + vz * wz
|
|
|
+ //v*w
|
|
|
+ var dt = a * c - b * b
|
|
|
+
|
|
|
+ var sd = dt
|
|
|
+ var td = dt
|
|
|
+
|
|
|
+ var sn = 0.0
|
|
|
+ //sn = be-cd
|
|
|
+ var tn = 0.0
|
|
|
+ //tn = ae-bd
|
|
|
+
|
|
|
+ var behind = function (index) {
|
|
|
+ //在后方交点的话,直接其中一个点 不用两posget点中心点是因为可能从不同方位 距离很大
|
|
|
+ pos3d = (index == 1 ? p1 : p2).clone()
|
|
|
+ //console.log(pos3d , ' 在后方交点,使用点' + index)
|
|
|
+ }.bind(this)
|
|
|
+
|
|
|
+ if (math.closeTo(dt, 0.0)) {
|
|
|
+ //两直线平行
|
|
|
+ sn = 0.0
|
|
|
+ //在s上指定取s0
|
|
|
+ sd = 1.0
|
|
|
+ //防止计算时除0错误
|
|
|
+ tn = e
|
|
|
+ //按(公式3)求tc
|
|
|
+ td = c
|
|
|
+ } else {
|
|
|
+ sn = b * e - c * d
|
|
|
+ tn = a * e - b * d
|
|
|
+ if (sn < 0.0) {
|
|
|
+ //最近点在s起点以外,同平行条件
|
|
|
+ behind(1)
|
|
|
+ return { pos3d, behind: true }
|
|
|
+ sn = 0.0
|
|
|
+ tn = e
|
|
|
+ td = c
|
|
|
+ } else if (sn > sd) {
|
|
|
+ //超出终点不限制
|
|
|
+ /* //最近点在s终点以外(即sc>1,则取sc=1)
|
|
|
+ sn = sd;
|
|
|
+ tn = e + b; //按(公式3)计算
|
|
|
+ td = c; */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tn < 0.0) {
|
|
|
+ //最近点在t起点以外
|
|
|
+ behind(2)
|
|
|
+ return { pos3d, behind: true }
|
|
|
+ tn = 0.0
|
|
|
+ if (-d < 0.0) sn = 0.0
|
|
|
+ //按(公式2)计算,如果等号右边小于0,则sc也小于零,取sc=0
|
|
|
+ else if (-d > a) sn = sd
|
|
|
+ //按(公式2)计算,如果sc大于1,取sc=1
|
|
|
+ else {
|
|
|
+ sn = -d
|
|
|
+ sd = a
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* else if (tn > td){ //超出终点不限制
|
|
|
+ tn = td;
|
|
|
+ if ((-d + b) < 0.0)
|
|
|
+ sn = 0.0;
|
|
|
+ else if ((-d + b) > a)
|
|
|
+ sn = sd;
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sn = (-d + b);
|
|
|
+ sd = a;
|
|
|
+ }
|
|
|
+ } */
|
|
|
+
|
|
|
+ var sc = 0.0
|
|
|
+ var tc = 0.0
|
|
|
+
|
|
|
+ if (math.closeTo(sn, 0.0)) sc = 0.0
|
|
|
+ else sc = sn / sd
|
|
|
+ if (math.closeTo(tn, 0.0)) tc = 0.0
|
|
|
+ else tc = tn / td
|
|
|
+ //两个最近点
|
|
|
+ var mid1 = new THREE.Vector3(A.x + sc * ux, A.y + sc * uy, A.z + sc * uz)
|
|
|
+ var mid2 = new THREE.Vector3(B.x + tc * vx, B.y + tc * vy, B.z + tc * vz)
|
|
|
+
|
|
|
+ /* console.log("v11:")
|
|
|
+ console.log(A.clone().sub(mid1).normalize())
|
|
|
+ console.log("v22:")
|
|
|
+ console.log(B.clone().sub(mid2).normalize()) */
|
|
|
+
|
|
|
+ //console.log('另一个结果', math.getLineIntersect2(o)) //结果一样的,只是没有限制后方交点
|
|
|
+
|
|
|
+ let r = { pos3d: mid1.clone().add(mid2).multiplyScalar(0.5), mid1, mid2 }
|
|
|
+ return r
|
|
|
+ }
|
|
|
+
|
|
|
+ return compute()
|
|
|
+
|
|
|
+ //https://blog.csdn.net/u011511587/article/details/52063663 三维空间两直线/线段最短距离、线段计算算法
|
|
|
+ },
|
|
|
+
|
|
|
+ getShapeGeo: function (points, holes) {
|
|
|
+ //获取任意形状(多边形或弧形)的形状面 //quadraticCurveTo() 这是弧形的含函数
|
|
|
+ var shape = new THREE.Shape()
|
|
|
+ shape.moveTo(points[0].x, points[0].y)
|
|
|
+ for (var i = 1, len = points.length; i < len; i++) {
|
|
|
+ shape.lineTo(points[i].x, points[i].y)
|
|
|
+ }
|
|
|
+
|
|
|
+ /* var holePath = new THREE.Path()
|
|
|
+ .moveTo( 20, 10 )
|
|
|
+ .absarc( 10, 10, 10, 0, Math.PI * 2, true )
|
|
|
+ arcShape.holes.push( holePath );
|
|
|
+ */
|
|
|
+ if (holes) {
|
|
|
+ //挖空
|
|
|
+ holes.forEach(points => {
|
|
|
+ var holePath = new THREE.Path()
|
|
|
+ holePath.moveTo(points[0].x, points[0].y)
|
|
|
+ for (var i = 1, len = points.length; i < len; i++) {
|
|
|
+ holePath.lineTo(points[i].x, points[i].y)
|
|
|
+ }
|
|
|
+ shape.holes.push(holePath)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ var geometry = new THREE.ShapeBufferGeometry(shape) //ShapeGeometry
|
|
|
+ return geometry
|
|
|
+ },
|
|
|
+ getUnPosPlaneGeo: (function () {
|
|
|
+ //获取还没有赋值位置的plane geometry
|
|
|
+ var e = new Uint16Array([0, 1, 2, 0, 2, 3]),
|
|
|
+ // , t = new Float32Array([-.5, -.5, 0, .5, -.5, 0, .5, .5, 0, -.5, .5, 0])
|
|
|
+ i = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
|
|
|
+ g = new THREE.BufferGeometry()
|
|
|
+ g.setIndex(new THREE.BufferAttribute(e, 1)),
|
|
|
+ //g.setAttribute("position", new n.BufferAttribute(t, 3)),
|
|
|
+ g.setAttribute('uv', new THREE.BufferAttribute(i, 2))
|
|
|
+ return function () {
|
|
|
+ return g
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ getPlaneGeo: function (A, B, C, D) {
|
|
|
+ var geo = this.getUnPosPlaneGeo().clone()
|
|
|
+ var pos = new Float32Array([A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z])
|
|
|
+ geo.setAttribute('position', new THREE.BufferAttribute(pos, 3))
|
|
|
+ geo.computeVertexNormals()
|
|
|
+ geo.computeBoundingSphere() //for raycaster
|
|
|
+ return geo
|
|
|
+ },
|
|
|
+ drawPlane: function (A, B, C, D, material) {
|
|
|
+ var wall = new THREE.Mesh(this.getPlaneGeo(A, B, C, D), material)
|
|
|
+ return wall
|
|
|
+ },
|
|
|
+ movePlane: function (mesh, A, B, C, D) {
|
|
|
+ var pos = new Float32Array([A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z])
|
|
|
+ mesh.geometry.setAttribute('position', new THREE.BufferAttribute(pos, 3))
|
|
|
+ mesh.geometry.computeBoundingSphere() //for checkIntersect
|
|
|
+ },
|
|
|
+
|
|
|
+ getAngle(vec1, vec2, axis) {
|
|
|
+ var angle = vec1.angleTo(vec2)
|
|
|
+ var axis_ = vec1.clone().cross(vec2)
|
|
|
+ if (axis_[axis] < 0) {
|
|
|
+ angle *= -1
|
|
|
+ }
|
|
|
+ return angle
|
|
|
+ },
|
|
|
+
|
|
|
+ linearClamp(value, x1, x2, y1, y2) {
|
|
|
+ //x为bound.min, bound.max
|
|
|
+ value = THREE.MathUtils.clamp(value, x1, x2)
|
|
|
+ return y1 + ((y2 - y1) * (value - x1)) / (x2 - x1)
|
|
|
+ },
|
|
|
+
|
|
|
+ isInsideFrustum(bounding, camera) {
|
|
|
+ // bounding是否在视野范围内有可见部分(视野就是一个锥状box)
|
|
|
+ let frustumMatrix = new THREE.Matrix4()
|
|
|
+ frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
|
|
|
+
|
|
|
+ let frustum = new THREE.Frustum()
|
|
|
+ frustum.setFromProjectionMatrix(frustumMatrix)
|
|
|
+
|
|
|
+ if (bounding instanceof THREE.Sphere) {
|
|
|
+ return frustum.intersectsSphere(bounding)
|
|
|
+ } else {
|
|
|
+ return frustum.intersectsBox(bounding)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ getStandardYaw(yaw1, yaw2) {
|
|
|
+ //使yaw1过渡到yaw2时朝角度差小的那边走。如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度)
|
|
|
+ if (Math.abs(yaw1 - yaw2) > Math.PI) {
|
|
|
+ yaw1 > yaw2 ? (yaw1 -= Math.PI * 2) : (yaw2 -= Math.PI * 2)
|
|
|
+ }
|
|
|
+ return [yaw1, yaw2]
|
|
|
+ },
|
|
|
+}
|
|
|
+
|
|
|
+export default math
|