123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- import * as THREE from '../lib/three.module.js'
- var math = {
- getBaseLog(x, y) {
- //返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数
- return Math.log(y) / Math.log(x)
- },
- convertVector: {
- ZupToYup: function(e) {
- //navvis -> 4dkk
- return new THREE.Vector3(e.x, e.z, -e.y)
- },
- YupToZup: function(e) {
- //4dkk -> navvis
- return new THREE.Vector3(e.x, -e.z, e.y)
- }
- },
- convertQuaternion: {
- ZupToYup: function(e) {
- //navvis -> 4dkk //不同于convertVisionQuaternion
- let rotation = new THREE.Euler(-Math.PI / 2, 0, 0)
- let quaternion = new THREE.Quaternion().setFromEuler(rotation)
- return e.clone().premultiply(quaternion)
- //return new THREE.Quaternion(e.x,e.z,-e.y,e.w).multiply((new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(1,0,0), THREE.Math.degToRad(90)))
- },
- YupToZup: function(e) {
- //4dkk -> navvis
- let rotation = new THREE.Euler(Math.PI / 2, 0, 0)
- let quaternion = new THREE.Quaternion().setFromEuler(rotation)
- return e.clone().premultiply(quaternion)
- }
- },
- 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.Math.degToRad(90)))
- },
- invertVisionQuaternion: function(e) {
- //反转给算法部
- var a = e.clone().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.Math.degToRad(-90)))
- return new THREE.Quaternion(a.x, -a.z, a.y, a.w)
- },
- //------------
- getVec2Angle: function(dir1, dir2) {
- return Math.acos(THREE.Math.clamp(this.getVec2Cos(dir1, dir2), -1, 1))
- },
- getVec2Cos: function(dir1, dir2) {
- return dir1.dot(dir2) / dir1.length() / dir2.length()
- },
- getAngle: function(vec1, vec2, axis) {
- //带方向的角度 vector3
- var angle = vec1.angleTo(vec2)
- var axis_ = vec1.clone().cross(vec2)
- if (axis_[axis] < 0) {
- angle *= -1
- }
- return angle
- },
- closeTo: function(a, b, precision = 1e-6) {
- let f = (a, b) => {
- return Math.abs(a - b) < precision
- }
- if (typeof a == 'number') {
- return f(a, b)
- } else {
- let judge = name => {
- if (a[name] == void 0) return true
- //有值就判断,没值就不判断
- else return f(a[name], b[name])
- }
- return judge('x') && judge('y') && judge('z') && judge('w')
- }
- },
- 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) {
- ;(i = i || new THREE.Vector3()), i.copy(e)
- var r = 0.5 * $('#player').width(),
- o = 0.5 * $('#player').height()
- return i.project(t), (i.x = i.x * r + r), (i.y = -(i.y * o) + o), i
- },
- handelPadResize: false,
- /* handelPadding : function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
-
- var pads = [];//记录下来避免反复计算
- var index = [];
- var resetPad = function(){
- pads = [];
- index = [];
- math.handelPadResize = false; //switchview时resized为true
- }
-
- if(config.isEdit && !config.isMobile){
- window.addEventListener('resize',resetPad);
- }
- return function(x, y, domE){
- if(!config.isEdit || config.isMobile) {
- return {
- x: x,
- y: y
- }
- }
-
- if(this.handelPadResize)resetPad();
- domE = domE || $('#player')[0];
- var pad;
- var i = index.indexOf(domE);
- if (i == -1){
- index.push(domE);
- pad = {
- x: this.getOffset("left", domE),
- y: this.getOffset("top", domE)
- }
- pads.push(pad)
- }
- else pad = pads[i];
- 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 = $('body')[0]
- 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.Math.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;
- } */
- if (p1.equals(p2)) return p1.clone()
- 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, holes, point, ifAtLine) {
- //判断点是否在某个环内, 若传递了holes代表还要不能在内环内
- 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;//在线段上,则判断为…… (默认在外)
- return { atLine: true }
- }
- if (yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
- inside = !inside
- }
- }
- if (inside && holes) {
- return !holes.some(ring => this.isPointInArea(ring, null, point, ifAtLine)) //不能存在于任何一个二级内环内
- } else {
- 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, precision) {
- //线段和线段是否有交点. 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, precision) && math.ifPointAtLineBound({ x, y }, line2, precision))) {
- return { x, y }
- }
- }
- },
- getNormal2d: function(o = {}) {
- //获取二维法向量 方向向内
- var x, y, x1, y1
- //line2d的向量
- if (o.vec) {
- x1 = o.vec.x
- y1 = o.vec.y
- } else {
- x1 = o.p1.x - o.p2.x
- y1 = o.p1.y - o.p2.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)
- },
- /* ,
- getQuaBetween2Vector2 : function(oriVec, newVec ){//not camera
- var _ = (new THREE.Matrix4).lookAt( oriVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
- var aimQua = (new THREE.Quaternion).setFromRotationMatrix(_)
- var _2 = (new THREE.Matrix4).lookAt( newVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
- var aimQua2 = (new THREE.Quaternion).setFromRotationMatrix(_2)
-
- return aimQua2.multiply(aimQua.clone().inverse())
-
- } */
- 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, nearBound, farBound
- if (op.camera.type == 'OrthographicCamera') {
- currentDis = 200 / op.camera.zoom //(op.camera.right - op.camera.left) / op.camera.zoom
- } else {
- currentDis = op.position.distanceTo(op.camera.position)
- }
- w = op.maxSize - (op.maxSize - op.minSize) * THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound)
- //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
- }
- i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
- o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
- l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
- c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l), //再转回 -1 到 1的范围
- h.copy(c).unproject(op.camera) //再转成三维坐标,求得tag边缘的位置
- var g = h.distanceTo(op.position) //就能得到tag的三维半径
- //这里使用的都是resolution2, 好处是手机端不会太小, 坏处是pc更改网页显示百分比时显示的大小会变(或许可以自己算出设备真实的deviceRatio, 因window.screen是不会改变的),但考虑到用户可以自行调节字大小也许是好的
- return g //可能NAN 当相机和position重叠时
- }
- })(),
- //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 //所求 单位向量
- var y = Math.cos(uv.y * Math.PI) //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
- // 故 uv.y * Math.PI 就是到垂直线(向上)的夹角
- 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 = this.convertVector.YupToZup(new THREE.Vector3(x, y, z))
- //理想状态下x和z和anotherDir相同
- return dirB
- },
- getUVfromDir: function(dir) {
- //获取UV 同shader里的计算
- var dir = this.convertVector.ZupToYup(dir)
- 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 new THREE.Vector2(tx, ty)
- //理想状态下tx相同
- },
- getDirByLonLat: function(lon, lat) {
- var dir = new THREE.Vector3()
- var phi = THREE.Math.degToRad(90 - lat)
- var theta = THREE.Math.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)
- projectPointAtPlane: function(o = {}) {
- //获取一个点在一个面上的投影 {facePoints:[a,b,c], point:}
- var plane = new THREE.Plane().setFromCoplanarPoints(...o.facePoints)
- return plane.projectPoint(o.point, new THREE.Vector3())
- },
- getPolygonsMixedRings: function(polygons, onlyGetOutRing) {
- //{points:[vector2,...],holes:[[],[]]}
- let points = []
- let lines = []
- let i = 0
- polygons.forEach(e => points.push(...e.map(a => new THREE.Vector2().copy(a))))
- polygons.forEach((ps, j) => {
- let length = ps.length
- let index = 0
- while (index < length) {
- lines.push({ p1: index + i, p2: ((index + 1) % length) + i })
- index++
- }
- i += length
- })
- points.forEach((p, j) => {
- p.id = j
- })
- let rings = searchRings({
- points,
- lines,
- onlyGetOutRing
- })
- //console.log(rings)
- rings = rings.filter(e => e.closetParent == void 0) // 子环不加,被外环包含了
- return rings
- },
- getQuaFromPosAim(position, target) {
- let matrix = new THREE.Matrix4().lookAt(position, target, new THREE.Vector3(0, 0, 1))
- return new THREE.Quaternion().setFromRotationMatrix(matrix)
- },
- getBoundByPoints(points, minSize) {
- var bound = new THREE.Box3()
- points.forEach(point => {
- bound.expandByPoint(point)
- })
- let center = bound.getCenter(new THREE.Vector3())
- if (minSize) {
- let minBound = new THREE.Box3().setFromCenterAndSize(center, minSize)
- bound.union(minBound)
- }
- return {
- bounding: bound,
- size: bound.getSize(new THREE.Vector3()),
- center
- }
- },
- convertScreenPositionToNDC(pointer, mouse, width, height) {
- return (pointer = pointer || new THREE.Vector2()), (pointer.x = (mouse.x / width) * 2 - 1), (pointer.y = 2 * -(mouse.y / height) + 1), pointer
- },
- convertNDCToScreenPosition(pointer, mouse, width, height) {
- return (mouse = mouse || new THREE.Vector2()), (mouse.x = Math.round(((pointer.x + 1) / 2) * width)), (mouse.y = Math.round((-(pointer.y - 1) / 2) * height)), mouse
- },
- averageVectors(e, t) {
- var i = new THREE.Vector3()
- if (0 === e.length) return i
- for (var r = 0, o = 0; o < e.length; o++) {
- var a = t ? e[o][t] : e[o]
- i.add(a), r++
- }
- return i.divideScalar(r)
- }
- }
- export default math
|