math.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import * as THREE from "../../../libs/three.js/build/three.module.js";
  2. import searchRings from "./searchRings.js";
  3. import {ExtendView} from '../../viewer/ExtendView.js'
  4. let view = new ExtendView()
  5. var math = {
  6. getBaseLog(x, y) {//返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数
  7. return Math.log(y) / Math.log(x);
  8. }
  9. ,
  10. convertVector : {
  11. ZupToYup: function(e){//navvis -> 4dkk
  12. return new THREE.Vector3(e.x,e.z,-e.y)
  13. },
  14. YupToZup: function(e){//4dkk -> navvis
  15. return new THREE.Vector3(e.x,-e.z,e.y)
  16. },
  17. },
  18. convertQuaternion: {
  19. ZupToYup: function(e){//navvis -> 4dkk //不同于convertVisionQuaternion
  20. let rotation = new THREE.Euler(-Math.PI/2,0,0)
  21. let quaternion = new THREE.Quaternion().setFromEuler(rotation)
  22. return e.clone().premultiply(quaternion)
  23. //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)))
  24. },
  25. YupToZup: function(e){//4dkk -> navvis
  26. let rotation = new THREE.Euler(Math.PI/2,0,0)
  27. let quaternion = new THREE.Quaternion().setFromEuler(rotation)
  28. return e.clone().premultiply(quaternion)
  29. },
  30. },
  31. convertVisionQuaternion: function(e) {
  32. 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)))
  33. },
  34. invertVisionQuaternion : function(e) {//反转给算法部
  35. var a = e.clone().multiply((new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(0,1,0), THREE.Math.degToRad(-90)))
  36. return new THREE.Quaternion(a.x,-a.z,a.y,a.w)
  37. },
  38. //------------
  39. getVec2Angle : function(dir1,dir2){
  40. return Math.acos( THREE.Math.clamp(this.getVec2Cos(dir1,dir2), -1,1) )
  41. },
  42. getVec2Cos : function(dir1,dir2){
  43. return dir1.dot(dir2) / dir1.length() / dir2.length()
  44. },
  45. getAngle:function(vec1, vec2, axis='z'){//带方向的角度 vector3
  46. if(!vec1.isVector3){
  47. vec1 = new THREE.Vector3(vec1.x, vec1.y, 0)
  48. vec2 = new THREE.Vector3(vec2.x, vec2.y, 0)
  49. }
  50. var angle = vec1.angleTo(vec2)
  51. var axis_ = vec1.clone().cross(vec2);
  52. if(typeof axis == 'string'){
  53. if(axis_[axis] < 0){
  54. angle *= -1
  55. }
  56. }else{//vector3
  57. if(axis_.dot(axis)< 0){
  58. angle *= -1
  59. }
  60. }
  61. return angle
  62. },
  63. closeTo : function(a,b, precision=1e-6){
  64. let f = (a,b)=>{
  65. return Math.abs(a-b) < precision;
  66. }
  67. if(typeof (a) == 'number'){
  68. return f(a, b);
  69. }else{
  70. let judge = (name)=>{
  71. if(a[name] == void 0)return true //有值就判断,没值就不判断
  72. else return f(a[name],b[name])
  73. }
  74. return judge('x') && judge('y') && judge('z') && judge('w')
  75. }
  76. },
  77. toPrecision: function (e, t) {//xzw change 保留小数
  78. var f = function (e, t) {
  79. var i = Math.pow(10, t);
  80. return Math.round(e * i) / i
  81. }
  82. if (e instanceof Array) {
  83. for (var s = 0; s < e.length; s++) {
  84. e[s] = f(e[s], t);
  85. }
  86. return e;
  87. } else if (e instanceof Object) {
  88. for (var s in e) {
  89. e[s] = f(e[s], t);
  90. }
  91. return e;
  92. } else if(typeof e == 'number'){
  93. return f(e, t)
  94. }else{
  95. return e
  96. }
  97. },
  98. isEmptyQuaternion: function(e) {
  99. return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w)
  100. },
  101. projectPositionToCanvas: function(e, t, i) {
  102. i = i || new THREE.Vector3,
  103. i.copy(e);
  104. var r = .5 * $('#player').width()
  105. , o = .5 * $('#player').height();
  106. return i.project(t),
  107. i.x = i.x * r + r,
  108. i.y = -(i.y * o) + o,
  109. i
  110. },
  111. handelPadResize:false,
  112. /* handelPadding : function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
  113. var pads = [];//记录下来避免反复计算
  114. var index = [];
  115. var resetPad = function(){
  116. pads = [];
  117. index = [];
  118. math.handelPadResize = false; //switchview时resized为true
  119. }
  120. if(config.isEdit && !config.isMobile){
  121. window.addEventListener('resize',resetPad);
  122. }
  123. return function(x, y, domE){
  124. if(!config.isEdit || config.isMobile) {
  125. return {
  126. x: x,
  127. y: y
  128. }
  129. }
  130. if(this.handelPadResize)resetPad();
  131. domE = domE || $('#player')[0];
  132. var pad;
  133. var i = index.indexOf(domE);
  134. if (i == -1){
  135. index.push(domE);
  136. pad = {
  137. x: this.getOffset("left", domE),
  138. y: this.getOffset("top", domE)
  139. }
  140. pads.push(pad)
  141. }
  142. else pad = pads[i];
  143. return {
  144. x: x - pad.x,
  145. y: y - pad.y
  146. }
  147. }
  148. }(), */
  149. getOffset: function (type, element, parent) {//获取元素的边距 许钟文
  150. var offset = (type == "left") ? element.offsetLeft : element.offsetTop;
  151. if (!parent) parent = $("body")[0];
  152. while (element = element.offsetParent) {
  153. if (element == parent) break;
  154. offset += (type == "left") ? element.offsetLeft : element.offsetTop;
  155. }
  156. return offset;
  157. }
  158. ,
  159. constrainedTurn: function(e) {
  160. var t = e % (2 * Math.PI);
  161. return t = t > Math.PI ? t -= 2 * Math.PI : t < -Math.PI ? t += 2 * Math.PI : t
  162. },
  163. getFOVDotThreshold: function(e) {
  164. return Math.cos(THREE.Math.degToRad(e / 2))
  165. },
  166. transform2DForwardVectorByCubeFace: function(e, t, i, n) {
  167. switch (e) {
  168. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
  169. i.set(1, t.y, t.x);
  170. break;
  171. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
  172. i.set(-1, t.y, -t.x);
  173. break;
  174. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
  175. i.set(-t.x, 1, -t.y);
  176. break;
  177. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
  178. i.set(-t.x, -1, t.y);
  179. break;
  180. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
  181. i.set(-t.x, t.y, 1);
  182. break;
  183. case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
  184. i.set(t.x, t.y, -1)
  185. }
  186. n && i.normalize()
  187. },
  188. getFootPoint : function(oldPos, p1, p2, restricInline){ //找oldPos在线段p1, p2上的垂足
  189. /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position
  190. p1 = p1.clone();
  191. p2 = p2.clone();
  192. p1.y += mainDesign.meshGroup.position.y;
  193. p2.y += mainDesign.meshGroup.position.y;
  194. } */
  195. if(p1.equals(p2))return p1.clone()
  196. var op1 = oldPos.clone().sub(p1);
  197. var p1p2 = p1.clone().sub(p2)
  198. var p1p2Len = p1p2.length()
  199. var leftLen = op1.dot(p1p2) / p1p2Len;
  200. var pos = p1.clone().add(p1p2.multiplyScalar( leftLen/p1p2Len ));
  201. if(restricInline && pos.clone().sub(p1).dot( pos.clone().sub(p2) ) > 0){//foot不在线段上
  202. if(pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone();
  203. else pos = p2.clone();
  204. }
  205. return pos;
  206. },
  207. /**
  208. * 计算多边形的重心
  209. * @param {*} points
  210. */
  211. getCenterOfGravityPoint : function(mPoints){
  212. var area = 0.0;//多边形面积
  213. var Gx = 0.0, Gy = 0.0;// 重心的x、y
  214. for (var i = 1; i <= mPoints.length; i++) {
  215. var ix = mPoints[i % mPoints.length].x;
  216. var iy = mPoints[i % mPoints.length].y;
  217. var nx = mPoints[i - 1].x;
  218. var ny = mPoints[i - 1].y;
  219. var temp = (ix * ny - iy * nx) / 2.0;
  220. area += temp;
  221. Gx += temp * (ix + nx) / 3.0;
  222. Gy += temp * (iy + ny) / 3.0;
  223. }
  224. Gx = Gx / area;
  225. Gy = Gy / area;
  226. return { x: Gx, y: Gy };
  227. },
  228. getBound : function(ring){
  229. var bound = new THREE.Box2();
  230. for(var j=0,len = ring.length; j<len; j++){
  231. bound.expandByPoint(ring[j])
  232. }
  233. return bound;
  234. },
  235. isPointInArea : function(ring, holes, point, ifAtLine){//判断点是否在某个环内, 若传递了holes代表还要不能在内环内
  236. var bound = this.getBound(ring);
  237. if(point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y)return false;
  238. var inside = false;
  239. var x = point.x,
  240. y = point.y;
  241. for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
  242. var xi = ring[i].x,
  243. yi = ring[i].y;
  244. var xj = ring[j].x,
  245. yj = ring[j].y;
  246. if((xi - x)*(yj - y) == (xi - x)*(yi - y) && x>=Math.min(xi,xj) && x<=Math.max(xi,xj)//xzw add
  247. && y>=Math.min(yi,yj) && y<=Math.max(yi,yj)
  248. ){
  249. //return !!ifAtLine;//在线段上,则判断为…… (默认在外)
  250. return {atLine:true}
  251. }
  252. if (((yi > y) != (yj > y)) &&
  253. (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
  254. ) {
  255. inside = !inside;
  256. }
  257. }
  258. if(inside && holes){
  259. return !holes.some(ring=>this.isPointInArea(ring, null, point, ifAtLine) ) //不能存在于任何一个二级内环内
  260. }else{
  261. return inside;
  262. }
  263. },
  264. getArea : function (ring) { //求面积 顺时针为正 来自three shape
  265. for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++)
  266. i += ring[n].x * ring[r].y - ring[r].x * ring[n].y;
  267. return -.5 * i
  268. },
  269. getVolume:function(faceArr){//求三角多面体的体积。和求面积同理都用鞋带计算法 https://blog.csdn.net/weixin_43414513/article/details/123758897
  270. //问题:怎么确定方向
  271. //每个三角形的顺序必须是右手螺旋法则指向物体外(外向里看为逆时针),分别为ABC,则每个三角形和原点O构成的三棱锥体积为 (OA cross OB) dot(OC) / 6 . 结果一般朝向原点的为负,反之为正
  272. },
  273. isInBetween : function(a, b, c, precision) {
  274. // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  275. /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
  276. return false;
  277. }
  278. return (a <= b && b <= c) || (c <= b && b <= a);*/
  279. //更改:如果b和a或c中一个接近 就算在a和c之间
  280. return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a,b,precision) || this.closeTo(b,c,precision);
  281. },
  282. ifPointAtLineBound:function(point, linePoints, precision){
  283. //待验证 横线和竖线比较特殊
  284. return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision)
  285. }
  286. ,
  287. isLineIntersect: function (line1, line2, notSegment, precision) {//线段和线段是否有交点. notSegment代表是直线而不是线段
  288. var a1 = line1[1].y - line1[0].y;
  289. var b1 = line1[0].x - line1[1].x;
  290. var c1 = a1 * line1[0].x + b1 * line1[0].y;
  291. //转换成一般式: Ax+By = C
  292. var a2 = line2[1].y - line2[0].y;
  293. var b2 = line2[0].x - line2[1].x;
  294. var c2 = a2 * line2[0].x + b2 * line2[0].y;
  295. // 计算交点
  296. var d = a1 * b2 - a2 * b1;
  297. // 当d==0时,两线平行
  298. if (d == 0) {
  299. return false;
  300. } else {
  301. var x = (b2 * c1 - b1 * c2) / d;
  302. var y = (a1 * c2 - a2 * c1) / d;
  303. // 检测交点是否在两条线段上
  304. /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
  305. (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
  306. return {x,y};
  307. } */
  308. if (notSegment || math.ifPointAtLineBound({x,y}, line1, precision) && math.ifPointAtLineBound({x,y}, line2, precision)){
  309. return {x,y};
  310. }
  311. }
  312. },
  313. getNormal2d : function(o={} ){//获取二维法向量 方向向内
  314. var x,y, x1,y1;
  315. //line2d的向量
  316. if(o.vec){
  317. x1 = o.vec.x; y1 = o.vec.y
  318. }else{
  319. x1 = o.p1.x - o.p2.x;
  320. y1 = o.p1.y - o.p2.y;
  321. }
  322. //假设法向量的x或y固定为1或-1
  323. if(y1 != 0){
  324. x = 1;
  325. y = - (x1 * x) / y1;
  326. }else if(x1 != 0){//y如果为0,正常情况x不会是0
  327. y = 1;
  328. x = - (y1 * y) / x1;
  329. }else{
  330. console.log("两个点一样");
  331. return null;
  332. }
  333. //判断方向里或者外:
  334. var vNormal = new THREE.Vector3(x, 0, y);
  335. var vLine = new THREE.Vector3(x1, 0, y1);
  336. var vDir = vNormal.cross(vLine);
  337. if(vDir.y>0){
  338. x *= -1;
  339. y *= -1;
  340. }
  341. return new THREE.Vector2(x, y).normalize();
  342. },
  343. getQuaBetween2Vector:function(oriVec, newVec, upVec){ //获取从oriVec旋转到newVec可以应用的quaternion
  344. var angle = oriVec.angleTo(newVec);
  345. var axis = oriVec.clone().cross( newVec).normalize();//两个up之间
  346. if(axis.length() == 0){//当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec
  347. return new THREE.Quaternion().setFromAxisAngle( upVec, angle );
  348. }
  349. return new THREE.Quaternion().setFromAxisAngle( axis, angle );
  350. } ,
  351. /*
  352. getQuaBetween2Vector2 : function(oriVec, newVec ){//not camera
  353. var _ = (new THREE.Matrix4).lookAt( oriVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
  354. var aimQua = (new THREE.Quaternion).setFromRotationMatrix(_)
  355. var _2 = (new THREE.Matrix4).lookAt( newVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
  356. var aimQua2 = (new THREE.Quaternion).setFromRotationMatrix(_2)
  357. return aimQua2.multiply(aimQua.clone().inverse())
  358. } */
  359. getQuaByAim: function (aim, center=new THREE.Vector3) {
  360. let forward = new THREE.Vector3(0, 1, 0)
  361. let qua1 = new THREE.Quaternion().setFromUnitVectors(forward, aim.clone().sub(center).normalize())
  362. /* var _ = (new THREE.Matrix4).lookAt(center,aim, new THREE.Vector3(0,1,0));
  363. let qua2 = (new THREE.Quaternion).setFromRotationMatrix(_);
  364. let rot1 = new THREE.Euler().setFromQuaternion(qua1)
  365. let rot2 = new THREE.Euler().setFromQuaternion(qua2) //奇怪,qua2怎么都不对
  366. console.log(rot1,rot2) */
  367. return qua1
  368. },
  369. getAimByQua: function (quaternion, center=new THREE.Vector3) {
  370. return new THREE.Vector3(0, 0, -1).applyQuaternion(quaternion).add(center)
  371. },
  372. getScaleForConstantSize : function(){ //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
  373. var w;
  374. var i = new THREE.Vector3, o = new THREE.Vector3, l = new THREE.Vector3, c = new THREE.Vector3, h = new THREE.Vector3
  375. return function(op={}){
  376. if(op.width2d) w = op.width2d //如果恒定二维宽度
  377. else{//否则考虑上距离,加一丢丢近大远小的效果
  378. var currentDis, nearBound, farBound
  379. if(op.camera.type == "OrthographicCamera"){
  380. currentDis = 200 / op.camera.zoom //(op.camera.right - op.camera.left) / op.camera.zoom
  381. }else{
  382. currentDis = op.position.distanceTo(op.camera.position);
  383. }
  384. w = op.maxSize - ( op.maxSize - op.minSize) * THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound);
  385. //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
  386. }
  387. i.copy(op.position).project(op.camera); //tag中心在屏幕上的二维坐标
  388. o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i); //转化成px -w/2 到 w/2的范围
  389. l.set(w / 2, 0, 0).add(o); //加上tag宽度的一半
  390. c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l); //再转回 -1 到 1的范围
  391. h.copy(c).unproject(op.camera);//再转成三维坐标,求得tag边缘的位置
  392. var g = h.distanceTo(op.position)//就能得到tag的三维半径
  393. //这里使用的都是resolution2, 好处是手机端不会太小, 坏处是pc更改网页显示百分比时显示的大小会变(或许可以自己算出设备真实的deviceRatio, 因window.screen是不会改变的),但考虑到用户可以自行调节字大小也许是好的
  394. return g //可能NAN 当相机和position重叠时
  395. }
  396. }()
  397. ,
  398. //W , H, left, top分别是rect的宽、高、左、上
  399. getCrossPointAtRect : function(p1, aim, W , H, left, top){//求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上)
  400. var x,y, borderX;
  401. var r = (aim.x - p1.x) / (aim.y - p1.y);//根据相似三角形原理先求出这个比值
  402. var getX = function(y){
  403. return r * (y-p1.y) + p1.x;
  404. }
  405. var getY = function(x){
  406. return 1/r * (x-p1.x) + p1.y;
  407. }
  408. if(aim.x >= p1.x){
  409. borderX = W+left;
  410. }else{
  411. borderX = left;
  412. }
  413. x = borderX;
  414. y = getY(x);
  415. if(y < top || y > top+H){
  416. if(y < top){
  417. y = top;
  418. }else{
  419. y = top+H;
  420. }
  421. x = getX(y)
  422. }
  423. return new THREE.Vector2(x, y);
  424. },
  425. getDirFromUV : function(uv){ //获取dir 反向计算 - - 二维转三维比较麻烦
  426. var dirB; //所求 单位向量
  427. var y = Math.cos(uv.y * Math.PI); //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
  428. // 故 uv.y * Math.PI 就是到垂直线(向上)的夹角
  429. var angle = 2 * Math.PI * uv.x - Math.PI //x/z代表的是角度
  430. var axisX, axisZ; //axis为1代表是正,-1是负数
  431. if (-Math.PI <= angle && angle < 0) {
  432. axisX = -1 //下半圆
  433. } else {
  434. axisX = 1 //上半圆
  435. }
  436. if (-Math.PI / 2 <= angle && angle < Math.PI / 2) {
  437. axisZ = 1 //右半圆
  438. } else {
  439. axisZ = -1 //左半圆
  440. }
  441. var XDivideZ = Math.tan(angle);
  442. var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ));
  443. var x = XDivideZ * z
  444. if (z * axisZ < 0) { //异号
  445. z *= -1;
  446. x *= -1;
  447. if (x * axisX < 0) {
  448. // console.log("wrong!!!!!??????????")
  449. }
  450. }
  451. x *= -1 //计算完成后这里不能漏掉 *= -1
  452. dirB = this.convertVector.YupToZup(new THREE.Vector3(x, y, z))
  453. //理想状态下x和z和anotherDir相同
  454. return dirB
  455. },
  456. getUVfromDir : function(dir) { //获取UV 同shader里的计算
  457. var dir = this.convertVector.ZupToYup(dir)
  458. dir.x *= -1; //计算前这里不能漏掉 *= -1 见shader
  459. var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5; //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值
  460. var ty = Math.acos(dir.y) / Math.PI;
  461. return new THREE.Vector2(tx, ty)
  462. //理想状态下tx相同
  463. },
  464. getDirByLonLat : function(lon,lat){
  465. var dir = new THREE.Vector3
  466. var phi = THREE.Math.degToRad(90 - lat);
  467. var theta = THREE.Math.degToRad(lon);
  468. dir.x = Math.sin(phi) * Math.cos(theta);
  469. dir.y = Math.cos(phi);
  470. dir.z = Math.sin(phi) * Math.sin(theta);
  471. return dir
  472. } //0,0 => (1,0,0) 270=>(0,0,-1)
  473. ,
  474. projectPointAtPlane:function(o={}){//获取一个点在一个面上的投影 {facePoints:[a,b,c], point:}
  475. var plane = new THREE.Plane().setFromCoplanarPoints(...o.facePoints)
  476. return plane.projectPoint(o.point, new THREE.Vector3() )
  477. }
  478. ,
  479. getPolygonsMixedRings:function( polygons, onlyGetOutRing){//{points:[vector2,...],holes:[[],[]]}
  480. let points = []
  481. let lines = []
  482. let i = 0
  483. polygons.forEach(e=> points.push(...e.map(a=>new THREE.Vector2().copy(a) )) )
  484. polygons.forEach((ps,j)=>{
  485. let length = ps.length;
  486. let index = 0;
  487. while(index<length){
  488. lines.push({p1:index+i,p2:(index+1)%length+i});
  489. index ++;
  490. }
  491. i+=length
  492. })
  493. points.forEach((p,j)=>{p.id = j})
  494. let rings = searchRings({
  495. points,
  496. lines,
  497. onlyGetOutRing
  498. })
  499. //console.log(rings)
  500. rings = rings.filter(e=>e.closetParent == void 0)// 子环不加,被外环包含了
  501. return rings
  502. },
  503. getQuaFromPosAim( position, target ){
  504. /* let matrix = (new THREE.Matrix4).lookAt(position, target, new THREE.Vector3(0,0,1)) //这里垂直的话会默认给一个右向所以不这么写
  505. return (new THREE.Quaternion).setFromRotationMatrix(matrix) */
  506. view.direction = new THREE.Vector3().subVectors(target,position)
  507. return view.quaternion
  508. },
  509. getYawPitchByQua(qua){
  510. view.quaternion = qua
  511. return {yaw:view.yaw, pitch:view.pitch}
  512. },
  513. getBoundByPoints(points, minSize){
  514. var bound = new THREE.Box3
  515. points.forEach(point=>{
  516. bound.expandByPoint(point)
  517. })
  518. let center = bound.getCenter(new THREE.Vector3)
  519. if(minSize){
  520. let minBound = (new THREE.Box3()).setFromCenterAndSize(center, minSize)
  521. bound.union(minBound)
  522. }
  523. return {
  524. bounding:bound,
  525. size: bound.getSize(new THREE.Vector3),
  526. center,
  527. }
  528. },
  529. /* linearClamp(value, x1,x2, y1, y2){//x为bound.min, bound.max
  530. value = THREE.Math.clamp(value, x1,x2)
  531. return y1 + ( y2 - y1) * (value - x1) / (x2 - x1)
  532. } */
  533. linearClamp(value, xArr , yArr){ //xArr需要按顺序从小到大,yArr对应xArr中的值
  534. let len = xArr.length
  535. if(value <= xArr[0]) return yArr[0]
  536. if(value >= xArr[len - 1]) return yArr[len - 1]
  537. let i = 0
  538. while(++i < len ){
  539. if(value < xArr[i]){
  540. let x1 = xArr[i-1], x2 = xArr[i], y1 = yArr[i-1], y2 = yArr[i]
  541. value = y1 + ( y2 - y1) * (value - x1) / (x2 - x1)
  542. break
  543. }
  544. }
  545. return value
  546. },
  547. getKelvinFromCelsius(c){ //摄氏度->开尔文
  548. return c + 273.1 //273.15 算法的值是整数然后除以10所以只保留一位小数
  549. }
  550. };
  551. /*
  552. 如何将若干个点拟合出线段
  553. // 假设你有一个点数组,每个点表示为一个包含x和y坐标的对象
  554. const points = [
  555. { x: 1, y: 2 },
  556. { x: 2, y: 3 },
  557. { x: 3, y: 4 },
  558. { x: 4, y: 5 },
  559. // ... 更多点
  560. ];
  561. // 定义用于计算最小二乘法参数的函数
  562. function leastSquares( ) {
  563. let sumX = 0;
  564. let sumY = 0;
  565. let sumXY = 0;
  566. let sumX2 = 0;
  567. let n = points.length;
  568. for (const point of points) {
  569. sumX += point.x;
  570. sumY += point.y;
  571. sumXY += point.x * point.y;
  572. sumX2 += point.x * point.x;
  573. }
  574. const k = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); //不知道为何这样算,自己解不出来(使点到直线距离之和平方最小),但测了下似乎是准的
  575. const b = (sumY - k * sumX) / n;
  576. return { k, b };
  577. }
  578. // 使用上述函数计算拟合直线的参数
  579. const { k, b } = leastSquares(points);
  580. // 现在你可以使用这些参数来表示拟合出的线段
  581. console.log(`拟合线段的方程为: y = ${k}x + ${b}`);
  582. */
  583. Potree.math = math
  584. export default math