math.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. import * as THREE from 'three'
  2. var math = {
  3. getBaseLog(x, y) {
  4. //返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数
  5. return Math.log(y) / Math.log(x)
  6. },
  7. convertVisionVector: function (e) {
  8. return new THREE.Vector3(e.x, e.z, -e.y)
  9. },
  10. invertVisionVector: function (e) {
  11. //反转给算法部
  12. return new THREE.Vector3(e.x, -e.z, e.y)
  13. },
  14. convertVisionQuaternion: function (e) {
  15. 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)))
  16. },
  17. invertVisionQuaternion: function (e) {
  18. //反转给算法部
  19. var a = e.clone().multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), THREE.MathUtils.degToRad(-90)))
  20. return new THREE.Quaternion(a.x, -a.z, a.y, a.w)
  21. },
  22. convertWorkshopVector: function (e) {
  23. return new THREE.Vector3(-e.x, e.y, e.z)
  24. },
  25. convertWorkshopQuaternion: function (e) {
  26. 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))
  27. },
  28. convertWorkshopPanoramaQuaternion: function (e) {
  29. 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)))
  30. },
  31. convertWorkshopOrthoZoom: function (e, dom) {
  32. //xzw
  33. return e === -1 ? -1 : e * (dom.clientHeight / dom.clientHeight)
  34. },
  35. getVec2Angle: function (dir1, dir2) {
  36. return Math.acos(THREE.MathUtils.clamp(this.getVec2Cos(dir1, dir2), -1, 1))
  37. },
  38. getVec2Cos: function (dir1, dir2) {
  39. return dir1.dot(dir2) / dir1.length() / dir2.length()
  40. },
  41. closeTo: function (a, b, num) {
  42. if (num != void 0) return Math.abs(a - b) < num
  43. return Math.abs(a - b) < 1e-6
  44. },
  45. toPrecision: function (e, t) {
  46. //xzw change 保留小数
  47. var f = function (e, t) {
  48. var i = Math.pow(10, t)
  49. return Math.round(e * i) / i
  50. }
  51. if (e instanceof Array) {
  52. for (var s = 0; s < e.length; s++) {
  53. e[s] = f(e[s], t)
  54. }
  55. return e
  56. } else if (e instanceof Object) {
  57. for (var s in e) {
  58. e[s] = f(e[s], t)
  59. }
  60. return e
  61. } else return f(e, t)
  62. },
  63. isEmptyQuaternion: function (e) {
  64. return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w)
  65. },
  66. projectPositionToCanvas: function (e, t, i, domE) {
  67. ;(i = i || new THREE.Vector3()), i.copy(e)
  68. var r = 0.5 * domE.clientWidth,
  69. o = 0.5 * domE.clientHeight
  70. return i.project(t), (i.x = i.x * r + r), (i.y = -(i.y * o) + o), i
  71. },
  72. convertScreenPositionToNDC: function (e, t, i, domE) {
  73. /* return i = i || new THREE.Vector2,
  74. i.x = e / window.innerWidth * 2 - 1,
  75. i.y = 2 * -(t / window.innerHeight) + 1,
  76. i
  77. */
  78. return (i = i || new n.Vector2()), (i.x = (e / domE.clientWidth) * 2 - 1), (i.y = 2 * -(t / domE.clientHeight) + 1), i
  79. },
  80. handelPadding: (function () {
  81. //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
  82. var pads = new Map() //记录下来避免反复计算
  83. return function (x, y, domE) {
  84. let pad
  85. let padInfo = pads.get(domE)
  86. if (padInfo) {
  87. if (domE.clientWidth == padInfo.width && domE.clientHeight == padInfo.height) {
  88. pad = padInfo.pad
  89. }
  90. }
  91. if (!pad) {
  92. pad = {
  93. x: this.getOffset('left', domE),
  94. y: this.getOffset('top', domE),
  95. }
  96. pads.set(domE, {
  97. width: domE.clientWidth,
  98. height: domE.clientHeight,
  99. pad,
  100. })
  101. }
  102. return {
  103. x: x - pad.x,
  104. y: y - pad.y,
  105. }
  106. }
  107. })(),
  108. getOffset: function (type, element, parent) {
  109. //获取元素的边距 许钟文
  110. var offset = type == 'left' ? element.offsetLeft : element.offsetTop
  111. if (!parent) parent = document.body
  112. while ((element = element.offsetParent)) {
  113. if (element == parent) break
  114. offset += type == 'left' ? element.offsetLeft : element.offsetTop
  115. }
  116. return offset
  117. },
  118. constrainedTurn: function (e) {
  119. var t = e % (2 * Math.PI)
  120. return (t = t > Math.PI ? (t -= 2 * Math.PI) : t < -Math.PI ? (t += 2 * Math.PI) : t)
  121. },
  122. getFOVDotThreshold: function (e) {
  123. return Math.cos(THREE.MathUtils.degToRad(e / 2))
  124. },
  125. transform2DForwardVectorByCubeFace: function (e, t, i, n) {
  126. switch (e) {
  127. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
  128. i.set(1, t.y, t.x)
  129. break
  130. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
  131. i.set(-1, t.y, -t.x)
  132. break
  133. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
  134. i.set(-t.x, 1, -t.y)
  135. break
  136. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
  137. i.set(-t.x, -1, t.y)
  138. break
  139. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
  140. i.set(-t.x, t.y, 1)
  141. break
  142. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
  143. i.set(t.x, t.y, -1)
  144. }
  145. n && i.normalize()
  146. },
  147. getFootPoint: function (oldPos, p1, p2, restricInline) {
  148. //找oldPos在线段p1, p2上的垂足
  149. /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position
  150. p1 = p1.clone();
  151. p2 = p2.clone();
  152. p1.y += mainDesign.meshGroup.position.y;
  153. p2.y += mainDesign.meshGroup.position.y;
  154. } */
  155. var op1 = oldPos.clone().sub(p1)
  156. var p1p2 = p1.clone().sub(p2)
  157. var p1p2Len = p1p2.length()
  158. var leftLen = op1.dot(p1p2) / p1p2Len
  159. var pos = p1.clone().add(p1p2.multiplyScalar(leftLen / p1p2Len))
  160. if (restricInline && pos.clone().sub(p1).dot(pos.clone().sub(p2)) > 0) {
  161. //foot不在线段上
  162. if (pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone()
  163. else pos = p2.clone()
  164. }
  165. return pos
  166. },
  167. /**
  168. * 计算多边形的重心
  169. * @param {*} points
  170. */
  171. getCenterOfGravityPoint: function (mPoints) {
  172. var area = 0.0 //多边形面积
  173. var Gx = 0.0,
  174. Gy = 0.0 // 重心的x、y
  175. for (var i = 1; i <= mPoints.length; i++) {
  176. var ix = mPoints[i % mPoints.length].x
  177. var iy = mPoints[i % mPoints.length].y
  178. var nx = mPoints[i - 1].x
  179. var ny = mPoints[i - 1].y
  180. var temp = (ix * ny - iy * nx) / 2.0
  181. area += temp
  182. Gx += (temp * (ix + nx)) / 3.0
  183. Gy += (temp * (iy + ny)) / 3.0
  184. }
  185. Gx = Gx / area
  186. Gy = Gy / area
  187. return { x: Gx, y: Gy }
  188. },
  189. getBound: function (ring) {
  190. var bound = new THREE.Box2()
  191. for (var j = 0, len = ring.length; j < len; j++) {
  192. bound.expandByPoint(ring[j])
  193. }
  194. return bound
  195. },
  196. isPointInArea: function (ring, point, ifAtLine) {
  197. //判断点是否在某个环内
  198. var bound = this.getBound(ring)
  199. if (point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y) return false
  200. var inside = false
  201. var x = point.x,
  202. y = point.y
  203. for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
  204. var xi = ring[i].x,
  205. yi = ring[i].y
  206. var xj = ring[j].x,
  207. yj = ring[j].y
  208. if (
  209. (xi - x) * (yj - y) == (xi - x) * (yi - y) &&
  210. x >= Math.min(xi, xj) &&
  211. x <= Math.max(xi, xj) && //xzw add
  212. y >= Math.min(yi, yj) &&
  213. y <= Math.max(yi, yj)
  214. ) {
  215. return !!ifAtLine //在线段上,则判断为…… (默认在外)
  216. }
  217. if (yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
  218. inside = !inside
  219. }
  220. }
  221. return inside
  222. },
  223. getArea: function (ring) {
  224. //求面积 顺时针为正 来自three shape
  225. 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
  226. return -0.5 * i
  227. },
  228. isInBetween: function (a, b, c, precision) {
  229. // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  230. /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
  231. return false;
  232. }
  233. return (a <= b && b <= c) || (c <= b && b <= a);*/
  234. //更改:如果b和a或c中一个接近 就算在a和c之间
  235. return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a, b, precision) || this.closeTo(b, c, precision)
  236. },
  237. ifPointAtLineBound: function (point, linePoints, precision) {
  238. //待验证 横线和竖线比较特殊
  239. return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision)
  240. },
  241. isLineIntersect: function (line1, line2, notSegment) {
  242. //线段和线段是否有交点. notSegment代表是直线而不是线段
  243. var a1 = line1[1].y - line1[0].y
  244. var b1 = line1[0].x - line1[1].x
  245. var c1 = a1 * line1[0].x + b1 * line1[0].y
  246. //转换成一般式: Ax+By = C
  247. var a2 = line2[1].y - line2[0].y
  248. var b2 = line2[0].x - line2[1].x
  249. var c2 = a2 * line2[0].x + b2 * line2[0].y
  250. // 计算交点
  251. var d = a1 * b2 - a2 * b1
  252. // 当d==0时,两线平行
  253. if (d == 0) {
  254. return false
  255. } else {
  256. var x = (b2 * c1 - b1 * c2) / d
  257. var y = (a1 * c2 - a2 * c1) / d
  258. // 检测交点是否在两条线段上
  259. /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
  260. (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
  261. return {x,y};
  262. } */
  263. if (notSegment || (math.ifPointAtLineBound({ x, y }, line1) && math.ifPointAtLineBound({ x, y }, line2))) {
  264. return { x, y }
  265. }
  266. }
  267. },
  268. getNormal: function (line2d) {
  269. //获取二维法向量 方向向内
  270. var x, y //要求的向量
  271. //line2d的向量
  272. var x1 = line2d.points[1].x - line2d.points[0].x
  273. var y1 = line2d.points[1].y - line2d.points[0].y
  274. //假设法向量的x或y固定为1或-1
  275. if (y1 != 0) {
  276. x = 1
  277. y = -(x1 * x) / y1
  278. } else if (x1 != 0) {
  279. //y如果为0,正常情况x不会是0
  280. y = 1
  281. x = -(y1 * y) / x1
  282. } else {
  283. console.log('两个点一样')
  284. return null
  285. }
  286. //判断方向里或者外:
  287. var vNormal = new THREE.Vector3(x, 0, y)
  288. var vLine = new THREE.Vector3(x1, 0, y1)
  289. var vDir = vNormal.cross(vLine)
  290. if (vDir.y > 0) {
  291. x *= -1
  292. y *= -1
  293. }
  294. return new THREE.Vector2(x, y).normalize()
  295. },
  296. getQuaBetween2Vector: function (oriVec, newVec, upVec) {
  297. //获取从oriVec旋转到newVec可以应用的quaternion
  298. var angle = oriVec.angleTo(newVec)
  299. var axis = oriVec.clone().cross(newVec).normalize() //两个up之间
  300. if (axis.length() == 0) {
  301. //当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec
  302. return new THREE.Quaternion().setFromAxisAngle(upVec, angle)
  303. }
  304. return new THREE.Quaternion().setFromAxisAngle(axis, angle)
  305. },
  306. getScaleForConstantSize: (function () {
  307. //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
  308. var w
  309. var i = new THREE.Vector3(),
  310. o = new THREE.Vector3(),
  311. l = new THREE.Vector3(),
  312. c = new THREE.Vector3(),
  313. h = new THREE.Vector3()
  314. return function (op = {}) {
  315. if (op.width2d) w = op.width2d
  316. //如果恒定二维宽度
  317. else {
  318. var currentDis
  319. if (op.camera.type == 'OrthographicCamera') {
  320. //floorplan要直接使用activeControl.camera,主要用到projectionMatrix
  321. currentDis = (op.camera.right - op.camera.left) / op.camera.zoom / 3
  322. } else {
  323. currentDis = op.position.distanceTo(op.camera.position) //dollhouse要直接使用player.camera, 因为activeControl.camera没有更新matrixWorld
  324. }
  325. if ((op.nearBound == void 0 && op.farBound != void 0) || (op.nearBound != void 0 && op.farBound == void 0)) {
  326. //仅限制最大或最小的话,不判断像素大小,直接限制mesh的scale
  327. //这个判断也可以写到getScaleForConstantSize里,可以更严谨控制像素宽度,这里只简单计算大小
  328. let scale
  329. if (op.farBound == void 0 && currentDis < op.nearBound) {
  330. scale = (op.scale * currentDis) / op.nearBound
  331. } else if (op.nearBound == void 0 && currentDis > op.farBound) {
  332. scale = (op.scale * currentDis) / op.farBound
  333. } else {
  334. scale = op.scale
  335. }
  336. return scale
  337. }
  338. w = op.maxSize - (op.maxSize - op.minSize) * THREE.MathUtils.smoothstep(currentDis, op.nearBound, op.farBound)
  339. //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
  340. }
  341. i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
  342. o.set(op.dom.clientWidth / 2, op.dom.clientHeight / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
  343. l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
  344. c.set(2 / op.dom.clientWidth, 2 / op.dom.clientHeight, 1).multiply(l), //再转回 -1 到 1的范围
  345. h.copy(c).unproject(op.camera) //再转成三维坐标,求得tag边缘的位置
  346. var g = h.distanceTo(op.position) //就能得到tag的三维半径
  347. return g
  348. }
  349. })(),
  350. //W , H, left, top分别是rect的宽、高、左、上
  351. getCrossPointAtRect: function (p1, aim, W, H, left, top) {
  352. //求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上)
  353. var x, y, borderX
  354. var r = (aim.x - p1.x) / (aim.y - p1.y) //根据相似三角形原理先求出这个比值
  355. var getX = function (y) {
  356. return r * (y - p1.y) + p1.x
  357. }
  358. var getY = function (x) {
  359. return (1 / r) * (x - p1.x) + p1.y
  360. }
  361. if (aim.x >= p1.x) {
  362. borderX = W + left
  363. } else {
  364. borderX = left
  365. }
  366. x = borderX
  367. y = getY(x)
  368. if (y < top || y > top + H) {
  369. if (y < top) {
  370. y = top
  371. } else {
  372. y = top + H
  373. }
  374. x = getX(y)
  375. }
  376. return new THREE.Vector2(x, y)
  377. },
  378. getDirFromUV: function (uv) {
  379. //获取dir 反向计算 - - 二维转三维比较麻烦
  380. var dirB //所求 单位向量
  381. uv.x %= 1
  382. if (uv.x < 0) uv.x += 1 //调整为0-1
  383. var y = Math.cos(uv.y * Math.PI) //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
  384. var angle = 2 * Math.PI * uv.x - Math.PI //x/z代表的是角度
  385. var axisX, axisZ //axis为1代表是正,-1是负数
  386. if (-Math.PI <= angle && angle < 0) {
  387. axisX = -1 //下半圆
  388. } else {
  389. axisX = 1 //上半圆
  390. }
  391. if (-Math.PI / 2 <= angle && angle < Math.PI / 2) {
  392. axisZ = 1 //右半圆
  393. } else {
  394. axisZ = -1 //左半圆
  395. }
  396. var XDivideZ = Math.tan(angle)
  397. var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ))
  398. var x = XDivideZ * z
  399. if (z * axisZ < 0) {
  400. //异号
  401. z *= -1
  402. x *= -1
  403. if (x * axisX < 0) {
  404. // console.log("wrong!!!!!??????????")
  405. }
  406. }
  407. x *= -1 //计算完成后这里不能漏掉 *= -1
  408. dirB = new THREE.Vector3(x, y, z)
  409. //理想状态下x和z和anotherDir相同
  410. return dirB
  411. },
  412. getUVfromDir: function (dir) {
  413. //获取UV 同shader里的计算
  414. var dir = dir.clone()
  415. dir.x *= -1 //计算前这里不能漏掉 *= -1 见shader
  416. var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5 //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值
  417. var ty = Math.acos(dir.y) / Math.PI
  418. return { x: tx, y: ty }
  419. //理想状态下tx相同
  420. },
  421. crossRight: function (vec3, matrix) {
  422. //向量右乘矩阵,不能用向量的applyMatrix4(左乘)
  423. var e = matrix.elements
  424. var v = new THREE.Vector3()
  425. v.x = e[0] * vec3.x + e[1] * vec3.y + e[2] * vec3.z + e[3]
  426. v.y = e[4] * vec3.x + e[5] * vec3.y + e[6] * vec3.z + e[7]
  427. v.z = e[8] * vec3.x + e[9] * vec3.y + e[10] * vec3.z + e[11]
  428. //v.w不要
  429. return v
  430. },
  431. getNormalDir: function (point, supportsTiles, currentPano) {
  432. //获取A单位法线
  433. /* console.log("lookVector:")
  434. console.log(objects.player.cameraControls.activeControl.lookVector) */
  435. var dir = point.clone().sub(currentPano.position) //OA
  436. /* console.log("A的dir(无matrix转化):")
  437. console.log(dir.clone().normalize()); */
  438. if (supportsTiles) {
  439. var matrixWorld = currentPano.rot90Matrix.clone() //因为热点求点时所右乘的matrix必须是单张全景照片时用的转90度的matrix才行
  440. } else {
  441. var matrixWorld = currentPano.matrixWorld.clone()
  442. }
  443. dir = this.crossRight(dir, matrixWorld) //右乘matrixWorld 得matrix转化的向量
  444. dir.normalize()
  445. /* var b = player.currentPano.skyboxMesh.matrixWorld.clone().getInverse(player.currentPano.skyboxMesh.matrixWorld)
  446. console.log(crossRight(dir,b).normalize()) */
  447. return dir
  448. },
  449. getDirByLonLat: function (lon, lat) {
  450. var dir = new THREE.Vector3()
  451. var phi = THREE.MathUtils.degToRad(90 - lat)
  452. var theta = THREE.MathUtils.degToRad(lon)
  453. dir.x = Math.sin(phi) * Math.cos(theta)
  454. dir.y = Math.cos(phi)
  455. dir.z = Math.sin(phi) * Math.sin(theta)
  456. return dir
  457. }, //0,0 => (1,0,0) 270=>(0,0,-1)
  458. getLineIntersect2(o = {}) {
  459. //得两条直线在其向量构成的面的法线方向的交点,投影在线上点的中点。
  460. if (o.A != void 0) {
  461. // Ap1为一条线,Bp2为一条线
  462. var A = o.A
  463. var B = o.B
  464. var p1 = o.p1
  465. var p2 = o.p2
  466. var dir0 = o.dir0 || new THREE.Vector3().subVectors(p1, A)
  467. var dir1 = o.dir1 || new THREE.Vector3().subVectors(p2, B)
  468. }
  469. if (A.equals(B)) return { pos3d: p1.clone() }
  470. //寻找两个向量所在的面
  471. let normal = dir0.clone().cross(dir1) //面的法线
  472. //先把整体旋转到使在xz平面上,求完交点再转回来
  473. var qua = math.getQuaBetween2Vector(normal, new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 1, 0))
  474. let newPoints = [A, B, p1, p2].map(e => {
  475. return e.clone().applyQuaternion(qua)
  476. })
  477. var pos2d = math.isLineIntersect(
  478. [
  479. { x: newPoints[0].x, y: newPoints[0].z },
  480. { x: newPoints[2].x, y: newPoints[2].z },
  481. ],
  482. [
  483. { x: newPoints[1].x, y: newPoints[1].z },
  484. { x: newPoints[3].x, y: newPoints[3].z },
  485. ],
  486. true
  487. )
  488. var quaInverse = qua.clone().invert()
  489. let pos3d = new THREE.Vector3(pos2d.x, 0, pos2d.y)
  490. let pos3d1 = pos3d.clone().setY(newPoints[0].y)
  491. let pos3d2 = pos3d.clone().setY(newPoints[1].y)
  492. pos3d1.applyQuaternion(quaInverse)
  493. pos3d2.applyQuaternion(quaInverse)
  494. pos3d = new THREE.Vector3().addVectors(pos3d1, pos3d2).multiplyScalar(0.5)
  495. return { pos3d, mid1: pos3d1, mid2: pos3d2 }
  496. },
  497. getLineIntersect(o) {
  498. //两条三维直线相交 //取两线最短线段中心点 并且不能超出起点
  499. o = o || {}
  500. if (o.A != void 0) {
  501. // Ap1为一条线,Bp2为一条线
  502. var A = o.A
  503. var B = o.B
  504. var p1 = o.p1
  505. var p2 = o.p2
  506. }
  507. if (A.equals(B)) return { pos3d: p1.clone() }
  508. /* console.log("v1:")
  509. console.log(A.clone().sub(p1).normalize())
  510. console.log("v2:")
  511. console.log(B.clone().sub(p2).normalize())
  512. */
  513. //调试热点夹角
  514. var line1 = p1.clone().sub(A).normalize()
  515. var line2 = p2.clone().sub(B).normalize()
  516. var angle = line1.angleTo(line2)
  517. //var pano = player.model.panos.index[player.posGets.list[1]]
  518. //console.log('真实两线夹角: ', THREE.MathUtils.radToDeg(angle)) /*+ "旧夹角min: "+getAngle(pano.recentAngleScore)+"("+pano.recentAngleScore+")" */
  519. //----------
  520. var compute = function () {
  521. var pos3d
  522. var ux = p1.x - A.x
  523. var uy = p1.y - A.y
  524. var uz = p1.z - A.z
  525. var vx = p2.x - B.x
  526. var vy = p2.y - B.y
  527. var vz = p2.z - B.z
  528. var wx = A.x - B.x
  529. var wy = A.y - B.y
  530. var wz = A.z - B.z
  531. var a = ux * ux + uy * uy + uz * uz
  532. //u*u
  533. var b = ux * vx + uy * vy + uz * vz
  534. //u*v
  535. var c = vx * vx + vy * vy + vz * vz
  536. //v*v
  537. var d = ux * wx + uy * wy + uz * wz
  538. //u*w
  539. var e = vx * wx + vy * wy + vz * wz
  540. //v*w
  541. var dt = a * c - b * b
  542. var sd = dt
  543. var td = dt
  544. var sn = 0.0
  545. //sn = be-cd
  546. var tn = 0.0
  547. //tn = ae-bd
  548. var behind = function (index) {
  549. //在后方交点的话,直接其中一个点 不用两posget点中心点是因为可能从不同方位 距离很大
  550. pos3d = (index == 1 ? p1 : p2).clone()
  551. //console.log(pos3d , ' 在后方交点,使用点' + index)
  552. }.bind(this)
  553. if (math.closeTo(dt, 0.0)) {
  554. //两直线平行
  555. sn = 0.0
  556. //在s上指定取s0
  557. sd = 1.0
  558. //防止计算时除0错误
  559. tn = e
  560. //按(公式3)求tc
  561. td = c
  562. } else {
  563. sn = b * e - c * d
  564. tn = a * e - b * d
  565. if (sn < 0.0) {
  566. //最近点在s起点以外,同平行条件
  567. behind(1)
  568. return { pos3d, behind: true }
  569. sn = 0.0
  570. tn = e
  571. td = c
  572. } else if (sn > sd) {
  573. //超出终点不限制
  574. /* //最近点在s终点以外(即sc>1,则取sc=1)
  575. sn = sd;
  576. tn = e + b; //按(公式3)计算
  577. td = c; */
  578. }
  579. }
  580. if (tn < 0.0) {
  581. //最近点在t起点以外
  582. behind(2)
  583. return { pos3d, behind: true }
  584. tn = 0.0
  585. if (-d < 0.0) sn = 0.0
  586. //按(公式2)计算,如果等号右边小于0,则sc也小于零,取sc=0
  587. else if (-d > a) sn = sd
  588. //按(公式2)计算,如果sc大于1,取sc=1
  589. else {
  590. sn = -d
  591. sd = a
  592. }
  593. }
  594. /* else if (tn > td){ //超出终点不限制
  595. tn = td;
  596. if ((-d + b) < 0.0)
  597. sn = 0.0;
  598. else if ((-d + b) > a)
  599. sn = sd;
  600. else
  601. {
  602. sn = (-d + b);
  603. sd = a;
  604. }
  605. } */
  606. var sc = 0.0
  607. var tc = 0.0
  608. if (math.closeTo(sn, 0.0)) sc = 0.0
  609. else sc = sn / sd
  610. if (math.closeTo(tn, 0.0)) tc = 0.0
  611. else tc = tn / td
  612. //两个最近点
  613. var mid1 = new THREE.Vector3(A.x + sc * ux, A.y + sc * uy, A.z + sc * uz)
  614. var mid2 = new THREE.Vector3(B.x + tc * vx, B.y + tc * vy, B.z + tc * vz)
  615. /* console.log("v11:")
  616. console.log(A.clone().sub(mid1).normalize())
  617. console.log("v22:")
  618. console.log(B.clone().sub(mid2).normalize()) */
  619. //console.log('另一个结果', math.getLineIntersect2(o)) //结果一样的,只是没有限制后方交点
  620. let r = { pos3d: mid1.clone().add(mid2).multiplyScalar(0.5), mid1, mid2 }
  621. return r
  622. }
  623. return compute()
  624. //https://blog.csdn.net/u011511587/article/details/52063663 三维空间两直线/线段最短距离、线段计算算法
  625. },
  626. getShapeGeo: function (points, holes) {
  627. //获取任意形状(多边形或弧形)的形状面 //quadraticCurveTo() 这是弧形的含函数
  628. var shape = new THREE.Shape()
  629. shape.moveTo(points[0].x, points[0].y)
  630. for (var i = 1, len = points.length; i < len; i++) {
  631. shape.lineTo(points[i].x, points[i].y)
  632. }
  633. /* var holePath = new THREE.Path()
  634. .moveTo( 20, 10 )
  635. .absarc( 10, 10, 10, 0, Math.PI * 2, true )
  636. arcShape.holes.push( holePath );
  637. */
  638. if (holes) {
  639. //挖空
  640. holes.forEach(points => {
  641. var holePath = new THREE.Path()
  642. holePath.moveTo(points[0].x, points[0].y)
  643. for (var i = 1, len = points.length; i < len; i++) {
  644. holePath.lineTo(points[i].x, points[i].y)
  645. }
  646. shape.holes.push(holePath)
  647. })
  648. }
  649. var geometry = new THREE.ShapeBufferGeometry(shape) //ShapeGeometry
  650. return geometry
  651. },
  652. getUnPosPlaneGeo: (function () {
  653. //获取还没有赋值位置的plane geometry
  654. var e = new Uint16Array([0, 1, 2, 0, 2, 3]),
  655. // , t = new Float32Array([-.5, -.5, 0, .5, -.5, 0, .5, .5, 0, -.5, .5, 0])
  656. i = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
  657. g = new THREE.BufferGeometry()
  658. g.setIndex(new THREE.BufferAttribute(e, 1)),
  659. //g.setAttribute("position", new n.BufferAttribute(t, 3)),
  660. g.setAttribute('uv', new THREE.BufferAttribute(i, 2))
  661. return function () {
  662. return g
  663. }
  664. })(),
  665. getPlaneGeo: function (A, B, C, D) {
  666. var geo = this.getUnPosPlaneGeo().clone()
  667. 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])
  668. geo.setAttribute('position', new THREE.BufferAttribute(pos, 3))
  669. geo.computeVertexNormals()
  670. geo.computeBoundingSphere() //for raycaster
  671. return geo
  672. },
  673. drawPlane: function (A, B, C, D, material) {
  674. var wall = new THREE.Mesh(this.getPlaneGeo(A, B, C, D), material)
  675. return wall
  676. },
  677. movePlane: function (mesh, A, B, C, D) {
  678. 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])
  679. mesh.geometry.setAttribute('position', new THREE.BufferAttribute(pos, 3))
  680. mesh.geometry.computeBoundingSphere() //for checkIntersect
  681. },
  682. getAngle(vec1, vec2, axis) {
  683. var angle = vec1.angleTo(vec2)
  684. var axis_ = vec1.clone().cross(vec2)
  685. if (axis_[axis] < 0) {
  686. angle *= -1
  687. }
  688. return angle
  689. },
  690. linearClamp(value, x1, x2, y1, y2) {
  691. //x为bound.min, bound.max
  692. value = THREE.MathUtils.clamp(value, x1, x2)
  693. return y1 + ((y2 - y1) * (value - x1)) / (x2 - x1)
  694. },
  695. isInsideFrustum(bounding, camera) {
  696. // bounding是否在视野范围内有可见部分(视野就是一个锥状box)
  697. let frustumMatrix = new THREE.Matrix4()
  698. frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
  699. let frustum = new THREE.Frustum()
  700. frustum.setFromProjectionMatrix(frustumMatrix)
  701. if (bounding instanceof THREE.Sphere) {
  702. return frustum.intersectsSphere(bounding)
  703. } else {
  704. return frustum.intersectsBox(bounding)
  705. }
  706. },
  707. getStandardYaw(yaw1, yaw2) {
  708. //使yaw1过渡到yaw2时朝角度差小的那边走。如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度)
  709. if (Math.abs(yaw1 - yaw2) > Math.PI) {
  710. yaw1 > yaw2 ? (yaw1 -= Math.PI * 2) : (yaw2 -= Math.PI * 2)
  711. }
  712. return [yaw1, yaw2]
  713. },
  714. }
  715. export default math