math.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. var math = {
  3. convertVector : {
  4. ZupToYup: function(e){//navvis -> 4dkk
  5. return new THREE.Vector3(e.x,e.z,-e.y)
  6. },
  7. YupToZup: function(e){//4dkk -> navvis
  8. return new THREE.Vector3(e.x,-e.z,e.y)
  9. },
  10. },
  11. convertVisionQuaternion: function(e) {
  12. 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)))
  13. },
  14. invertVisionQuaternion : function(e) {//反转给算法部
  15. var a = e.clone().multiply((new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(0,1,0), THREE.Math.degToRad(-90)))
  16. return new THREE.Quaternion(a.x,-a.z,a.y,a.w)
  17. },
  18. //------------
  19. getVec2Angle : function(dir1,dir2){
  20. return Math.acos( THREE.Math.clamp(this.getVec2Cos(dir1,dir2), -1,1) )
  21. },
  22. getVec2Cos : function(dir1,dir2){
  23. return dir1.dot(dir2) / dir1.length() / dir2.length()
  24. },
  25. closeTo : function(a,b, num){
  26. if(num != void 0)return Math.abs(a-b) < num;
  27. return Math.abs(a-b) < 1e-6;
  28. },
  29. toPrecision: function (e, t) {//xzw change 保留小数
  30. var f = function (e, t) {
  31. var i = Math.pow(10, t);
  32. return Math.round(e * i) / i
  33. }
  34. if (e instanceof Array) {
  35. for (var s = 0; s < e.length; s++) {
  36. e[s] = f(e[s], t);
  37. }
  38. return e;
  39. } else if (e instanceof Object) {
  40. for (var s in e) {
  41. e[s] = f(e[s], t);
  42. }
  43. return e;
  44. } else return f(e, t)
  45. },
  46. isEmptyQuaternion: function(e) {
  47. return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w)
  48. },
  49. projectPositionToCanvas: function(e, t, i) {
  50. i = i || new THREE.Vector3,
  51. i.copy(e);
  52. var r = .5 * $('#player').width()
  53. , o = .5 * $('#player').height();
  54. return i.project(t),
  55. i.x = i.x * r + r,
  56. i.y = -(i.y * o) + o,
  57. i
  58. },
  59. convertScreenPositionToNDC: function(pointer, mouse, width, height) {
  60. return pointer = pointer || new THREE.Vector2,
  61. pointer.x = mouse.x / width * 2 - 1,
  62. pointer.y = 2 * -(mouse.y / height) + 1,
  63. pointer
  64. },
  65. handelPadResize:false,
  66. /* handelPadding : function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
  67. var pads = [];//记录下来避免反复计算
  68. var index = [];
  69. var resetPad = function(){
  70. pads = [];
  71. index = [];
  72. math.handelPadResize = false; //switchview时resized为true
  73. }
  74. if(config.isEdit && !config.isMobile){
  75. window.addEventListener('resize',resetPad);
  76. }
  77. return function(x, y, domE){
  78. if(!config.isEdit || config.isMobile) {
  79. return {
  80. x: x,
  81. y: y
  82. }
  83. }
  84. if(this.handelPadResize)resetPad();
  85. domE = domE || $('#player')[0];
  86. var pad;
  87. var i = index.indexOf(domE);
  88. if (i == -1){
  89. index.push(domE);
  90. pad = {
  91. x: this.getOffset("left", domE),
  92. y: this.getOffset("top", domE)
  93. }
  94. pads.push(pad)
  95. }
  96. else pad = pads[i];
  97. return {
  98. x: x - pad.x,
  99. y: y - pad.y
  100. }
  101. }
  102. }(), */
  103. getOffset: function (type, element, parent) {//获取元素的边距 许钟文
  104. var offset = (type == "left") ? element.offsetLeft : element.offsetTop;
  105. if (!parent) parent = $("body")[0];
  106. while (element = element.offsetParent) {
  107. if (element == parent) break;
  108. offset += (type == "left") ? element.offsetLeft : element.offsetTop;
  109. }
  110. return offset;
  111. }
  112. ,
  113. constrainedTurn: function(e) {
  114. var t = e % (2 * Math.PI);
  115. return t = t > Math.PI ? t -= 2 * Math.PI : t < -Math.PI ? t += 2 * Math.PI : t
  116. },
  117. getFOVDotThreshold: function(e) {
  118. return Math.cos(THREE.Math.degToRad(e / 2))
  119. },
  120. transform2DForwardVectorByCubeFace: function(e, t, i, n) {
  121. switch (e) {
  122. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
  123. i.set(1, t.y, t.x);
  124. break;
  125. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
  126. i.set(-1, t.y, -t.x);
  127. break;
  128. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
  129. i.set(-t.x, 1, -t.y);
  130. break;
  131. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
  132. i.set(-t.x, -1, t.y);
  133. break;
  134. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
  135. i.set(-t.x, t.y, 1);
  136. break;
  137. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
  138. i.set(t.x, t.y, -1)
  139. }
  140. n && i.normalize()
  141. },
  142. getFootPoint : function(oldPos, p1, p2, restricInline){ //找oldPos在线段p1, p2上的垂足
  143. /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position
  144. p1 = p1.clone();
  145. p2 = p2.clone();
  146. p1.y += mainDesign.meshGroup.position.y;
  147. p2.y += mainDesign.meshGroup.position.y;
  148. } */
  149. if(p1.equals(p2))return p1.clone()
  150. var op1 = oldPos.clone().sub(p1);
  151. var p1p2 = p1.clone().sub(p2)
  152. var p1p2Len = p1p2.length()
  153. var leftLen = op1.dot(p1p2) / p1p2Len;
  154. var pos = p1.clone().add(p1p2.multiplyScalar( leftLen/p1p2Len ));
  155. if(restricInline && pos.clone().sub(p1).dot( pos.clone().sub(p2) ) > 0){//foot不在线段上
  156. if(pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone();
  157. else pos = p2.clone();
  158. }
  159. return pos;
  160. },
  161. /**
  162. * 计算多边形的重心
  163. * @param {*} points
  164. */
  165. getCenterOfGravityPoint : function(mPoints){
  166. var area = 0.0;//多边形面积
  167. var Gx = 0.0, Gy = 0.0;// 重心的x、y
  168. for (var i = 1; i <= mPoints.length; i++) {
  169. var ix = mPoints[i % mPoints.length].x;
  170. var iy = mPoints[i % mPoints.length].y;
  171. var nx = mPoints[i - 1].x;
  172. var ny = mPoints[i - 1].y;
  173. var temp = (ix * ny - iy * nx) / 2.0;
  174. area += temp;
  175. Gx += temp * (ix + nx) / 3.0;
  176. Gy += temp * (iy + ny) / 3.0;
  177. }
  178. Gx = Gx / area;
  179. Gy = Gy / area;
  180. return { x: Gx, y: Gy };
  181. },
  182. getBound : function(ring){
  183. var bound = new THREE.Box2();
  184. for(var j=0,len = ring.length; j<len; j++){
  185. bound.expandByPoint(ring[j])
  186. }
  187. return bound;
  188. },
  189. isPointInArea : function(ring, point, ifAtLine){//判断点是否在某个环内
  190. var bound = this.getBound(ring);
  191. if(point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y)return false;
  192. var inside = false;
  193. var x = point.x,
  194. y = point.y;
  195. for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
  196. var xi = ring[i].x,
  197. yi = ring[i].y;
  198. var xj = ring[j].x,
  199. yj = ring[j].y;
  200. if((xi - x)*(yj - y) == (xi - x)*(yi - y) && x>=Math.min(xi,xj) && x<=Math.max(xi,xj)//xzw add
  201. && y>=Math.min(yi,yj) && y<=Math.max(yi,yj)
  202. ){
  203. return !!ifAtLine;//在线段上,则判断为…… (默认在外)
  204. }
  205. if (((yi > y) != (yj > y)) &&
  206. (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
  207. ) {
  208. inside = !inside;
  209. }
  210. }
  211. return inside;
  212. },
  213. getArea : function (ring) { //求面积 顺时针为正 来自three shape
  214. for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++)
  215. i += ring[n].x * ring[r].y - ring[r].x * ring[n].y;
  216. return -.5 * i
  217. },
  218. isInBetween : function(a, b, c, precision) {
  219. // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  220. /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
  221. return false;
  222. }
  223. return (a <= b && b <= c) || (c <= b && b <= a);*/
  224. //更改:如果b和a或c中一个接近 就算在a和c之间
  225. return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a,b,precision) || this.closeTo(b,c,precision);
  226. },
  227. ifPointAtLineBound:function(point, linePoints, precision){
  228. //待验证 横线和竖线比较特殊
  229. return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision)
  230. }
  231. ,
  232. isLineIntersect: function (line1, line2, notSegment, precision) {//线段和线段是否有交点. notSegment代表是直线而不是线段
  233. var a1 = line1[1].y - line1[0].y;
  234. var b1 = line1[0].x - line1[1].x;
  235. var c1 = a1 * line1[0].x + b1 * line1[0].y;
  236. //转换成一般式: Ax+By = C
  237. var a2 = line2[1].y - line2[0].y;
  238. var b2 = line2[0].x - line2[1].x;
  239. var c2 = a2 * line2[0].x + b2 * line2[0].y;
  240. // 计算交点
  241. var d = a1 * b2 - a2 * b1;
  242. // 当d==0时,两线平行
  243. if (d == 0) {
  244. return false;
  245. } else {
  246. var x = (b2 * c1 - b1 * c2) / d;
  247. var y = (a1 * c2 - a2 * c1) / d;
  248. // 检测交点是否在两条线段上
  249. /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
  250. (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
  251. return {x,y};
  252. } */
  253. if (notSegment || math.ifPointAtLineBound({x,y}, line1, precision) && math.ifPointAtLineBound({x,y}, line2, precision)){
  254. return {x,y};
  255. }
  256. }
  257. },
  258. getNormal2d : function(o={} ){//获取二维法向量 方向向内
  259. var x,y, x1,y1;
  260. //line2d的向量
  261. if(o.vec){
  262. x1 = o.vec.x; y1 = o.vec.y
  263. }else{
  264. x1 = o.p1.x - o.p2.x;
  265. y1 = o.p1.y - o.p2.y;
  266. }
  267. //假设法向量的x或y固定为1或-1
  268. if(y1 != 0){
  269. x = 1;
  270. y = - (x1 * x) / y1;
  271. }else if(x1 != 0){//y如果为0,正常情况x不会是0
  272. y = 1;
  273. x = - (y1 * y) / x1;
  274. }else{
  275. console.log("两个点一样");
  276. return null;
  277. }
  278. //判断方向里或者外:
  279. var vNormal = new THREE.Vector3(x, 0, y);
  280. var vLine = new THREE.Vector3(x1, 0, y1);
  281. var vDir = vNormal.cross(vLine);
  282. if(vDir.y>0){
  283. x *= -1;
  284. y *= -1;
  285. }
  286. return new THREE.Vector2(x, y).normalize();
  287. },
  288. getQuaBetween2Vector:function(oriVec, newVec, upVec){ //获取从oriVec旋转到newVec可以应用的quaternion
  289. var angle = oriVec.angleTo(newVec);
  290. var axis = oriVec.clone().cross( newVec).normalize();//两个up之间
  291. if(axis.length() == 0){//当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec
  292. return new THREE.Quaternion().setFromAxisAngle( upVec, angle );
  293. }
  294. return new THREE.Quaternion().setFromAxisAngle( axis, angle );
  295. }
  296. /* ,
  297. getQuaBetween2Vector2 : function(oriVec, newVec ){//not camera
  298. var _ = (new THREE.Matrix4).lookAt( oriVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
  299. var aimQua = (new THREE.Quaternion).setFromRotationMatrix(_)
  300. var _2 = (new THREE.Matrix4).lookAt( newVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
  301. var aimQua2 = (new THREE.Quaternion).setFromRotationMatrix(_2)
  302. return aimQua2.multiply(aimQua.clone().inverse())
  303. } */
  304. ,
  305. getScaleForConstantSize : function(){ //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
  306. var w;
  307. var i = new THREE.Vector3, o = new THREE.Vector3, l = new THREE.Vector3, c = new THREE.Vector3, h = new THREE.Vector3
  308. return function(op={}){
  309. if(op.width2d) w = op.width2d //如果恒定二维宽度
  310. else{//否则考虑上距离,加一丢丢近大远小的效果
  311. var currentDis, nearBound, farBound
  312. if(op.camera.type == "OrthographicCamera"){
  313. currentDis = op.camera.right - op.camera.left
  314. }else{
  315. currentDis = op.position.distanceTo(op.camera.position);
  316. }
  317. w = op.maxSize - ( op.maxSize - op.minSize) * THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound);
  318. //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
  319. }
  320. i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
  321. o.set(op.resulution.x / 2, op.resulution.y / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
  322. l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
  323. c.set(2 / op.resulution.x, 2 / op.resulution.y, 1).multiply(l), //再转回 -1 到 1的范围
  324. h.copy(c).unproject(op.camera);//再转成三维坐标,求得tag边缘的位置
  325. var g = h.distanceTo(op.position)//就能得到tag的三维半径
  326. return g
  327. }
  328. }()
  329. ,
  330. //W , H, left, top分别是rect的宽、高、左、上
  331. getCrossPointAtRect : function(p1, aim, W , H, left, top){//求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上)
  332. var x,y, borderX;
  333. var r = (aim.x - p1.x) / (aim.y - p1.y);//根据相似三角形原理先求出这个比值
  334. var getX = function(y){
  335. return r * (y-p1.y) + p1.x;
  336. }
  337. var getY = function(x){
  338. return 1/r * (x-p1.x) + p1.y;
  339. }
  340. if(aim.x >= p1.x){
  341. borderX = W+left;
  342. }else{
  343. borderX = left;
  344. }
  345. x = borderX;
  346. y = getY(x);
  347. if(y < top || y > top+H){
  348. if(y < top){
  349. y = top;
  350. }else{
  351. y = top+H;
  352. }
  353. x = getX(y)
  354. }
  355. return new THREE.Vector2(x, y);
  356. },
  357. getDirFromUV : function(uv){ //获取dir 反向计算 - - 二维转三维比较麻烦
  358. var dirB; //所求 单位向量
  359. var y = Math.cos(uv.y * Math.PI); //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
  360. var angle = 2 * Math.PI * uv.x - Math.PI //x/z代表的是角度
  361. var axisX, axisZ; //axis为1代表是正,-1是负数
  362. if (-Math.PI <= angle && angle < 0) {
  363. axisX = -1 //下半圆
  364. } else {
  365. axisX = 1 //上半圆
  366. }
  367. if (-Math.PI / 2 <= angle && angle < Math.PI / 2) {
  368. axisZ = 1 //右半圆
  369. } else {
  370. axisZ = -1 //左半圆
  371. }
  372. var XDivideZ = Math.tan(angle);
  373. var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ));
  374. var x = XDivideZ * z
  375. if (z * axisZ < 0) { //异号
  376. z *= -1;
  377. x *= -1;
  378. if (x * axisX < 0) {
  379. // console.log("wrong!!!!!??????????")
  380. }
  381. }
  382. x *= -1 //计算完成后这里不能漏掉 *= -1
  383. dirB = new THREE.Vector3(x, y, z)
  384. //理想状态下x和z和anotherDir相同
  385. return dirB
  386. },
  387. getUVfromDir : function(dir) { //获取UV 同shader里的计算
  388. var dir = dir.clone();
  389. dir.x *= -1; //计算前这里不能漏掉 *= -1 见shader
  390. var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5; //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值
  391. var ty = Math.acos(dir.y) / Math.PI;
  392. return { x: tx, y: ty }
  393. //理想状态下tx相同
  394. },
  395. crossRight : function(vec3, matrix) { //向量右乘矩阵,不能用向量的applyMatrix4(左乘)
  396. var e = matrix.elements;
  397. var v = new THREE.Vector3;
  398. v.x = e[0] * vec3.x + e[1] * vec3.y + e[2] * vec3.z + e[3];
  399. v.y = e[4] * vec3.x + e[5] * vec3.y + e[6] * vec3.z + e[7];
  400. v.z = e[8] * vec3.x + e[9] * vec3.y + e[10] * vec3.z + e[11];
  401. //v.w不要
  402. return v;
  403. },
  404. getNormalDir : function(point, supportsTiles, currentPano) { //获取A单位法线
  405. /* console.log("lookVector:")
  406. console.log(objects.player.cameraControls.activeControl.lookVector) */
  407. var dir = point.clone().sub(currentPano.position); //OA
  408. /* console.log("A的dir(无matrix转化):")
  409. console.log(dir.clone().normalize()); */
  410. if (supportsTiles) {
  411. var matrixWorld = currentPano.rot90Matrix.clone(); //因为热点求点时所右乘的matrix必须是单张全景照片时用的转90度的matrix才行
  412. } else {
  413. var matrixWorld = currentPano.skyboxMesh.matrixWorld.clone();
  414. }
  415. dir = this.crossRight(dir, matrixWorld) //右乘matrixWorld 得matrix转化的向量
  416. dir.normalize();
  417. /* var b = player.currentPano.skyboxMesh.matrixWorld.clone().getInverse(player.currentPano.skyboxMesh.matrixWorld)
  418. console.log(crossRight(dir,b).normalize()) */
  419. return dir;
  420. },
  421. getDirByLonLat : function(lon,lat){
  422. var dir = new THREE.Vector3
  423. var phi = THREE.Math.degToRad(90 - lat);
  424. var theta = THREE.Math.degToRad(lon);
  425. dir.x = Math.sin(phi) * Math.cos(theta);
  426. dir.y = Math.cos(phi);
  427. dir.z = Math.sin(phi) * Math.sin(theta);
  428. return dir
  429. } //0,0 => (1,0,0) 270=>(0,0,-1)
  430. ,
  431. projectPointAtPlane:function(o={}){//获取一个点在一个面上的投影 {facePoints:[a,b,c], point:}
  432. var plane = new THREE.Plane().setFromCoplanarPoints(...o.facePoints)
  433. return plane.projectPoint(o.point, new THREE.Vector3() )
  434. }
  435. };
  436. export default math