123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- 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
|