浏览代码

三维裁剪展示

ronaldo9Zhang 1 年之前
父节点
当前提交
62136b4c41

+ 50 - 7
package-lock.json

@@ -25,8 +25,10 @@
         "quill-image-resize-module": "^3.0.0",
         "spark-md5": "^3.0.1",
         "style-loader": "^0.23.1",
-        "three": "^0.142.0",
+        "three": "^0.124.0",
+        "three-bvh-csg": "^0.0.5",
         "three-css2drender": "^1.0.0",
+        "three-js-csg": "^72.0.0",
         "three-obj-mtl-loader": "^1.0.3",
         "tinymce": "^5.1.5",
         "vue": "^2.6.10",
@@ -14556,9 +14558,18 @@
       }
     },
     "node_modules/three": {
-      "version": "0.142.0",
-      "resolved": "https://registry.npmjs.org/three/-/three-0.142.0.tgz",
-      "integrity": "sha512-ESjPO+3geFr+ZUfVMpMnF/eVU2uJPOh0e2ZpMFqjNca1wApS9lJb7E4MjwGIczgt9iuKd8PEm6Pfgp2bJ92Xtg=="
+      "version": "0.124.0",
+      "resolved": "https://registry.npmjs.org/three/-/three-0.124.0.tgz",
+      "integrity": "sha512-ROXp1Ly7YyF+jC910DQyAWj++Qlw2lQv0qwYLNQwdDbjk4bsOXAfGO92wYTMPNei1GMJUmCxSxc3MjGBTS09Rg=="
+    },
+    "node_modules/three-bvh-csg": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/three-bvh-csg/-/three-bvh-csg-0.0.5.tgz",
+      "integrity": "sha512-gxqmaV3HlGHUGB857AV8oLoNxx5Q0T7io1DUs5nrCwmmFqjWZT3a8ue7WapE394wY8awFVAMr+YvdSrzqswpFA==",
+      "peerDependencies": {
+        "three": "^0.141.0",
+        "three-mesh-bvh": "^0.5.13"
+      }
     },
     "node_modules/three-css2drender": {
       "version": "1.0.0",
@@ -14568,6 +14579,20 @@
         "three": ">= 0.91"
       }
     },
+    "node_modules/three-js-csg": {
+      "version": "72.0.0",
+      "resolved": "https://registry.npmjs.org/three-js-csg/-/three-js-csg-72.0.0.tgz",
+      "integrity": "sha512-Fr2pWWaHN1PPgjVsA7P2VZOHrMMXebQmJlasxGYebbSwURRwwi/vlayI69D+3qW39eNXQ6j+iO1VEOVC41mVFQ=="
+    },
+    "node_modules/three-mesh-bvh": {
+      "version": "0.5.23",
+      "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz",
+      "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==",
+      "peer": true,
+      "peerDependencies": {
+        "three": ">= 0.123.0"
+      }
+    },
     "node_modules/three-obj-mtl-loader": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/three-obj-mtl-loader/-/three-obj-mtl-loader-1.0.3.tgz",
@@ -26306,9 +26331,15 @@
       }
     },
     "three": {
-      "version": "0.142.0",
-      "resolved": "https://registry.npmjs.org/three/-/three-0.142.0.tgz",
-      "integrity": "sha512-ESjPO+3geFr+ZUfVMpMnF/eVU2uJPOh0e2ZpMFqjNca1wApS9lJb7E4MjwGIczgt9iuKd8PEm6Pfgp2bJ92Xtg=="
+      "version": "0.124.0",
+      "resolved": "https://registry.npmjs.org/three/-/three-0.124.0.tgz",
+      "integrity": "sha512-ROXp1Ly7YyF+jC910DQyAWj++Qlw2lQv0qwYLNQwdDbjk4bsOXAfGO92wYTMPNei1GMJUmCxSxc3MjGBTS09Rg=="
+    },
+    "three-bvh-csg": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/three-bvh-csg/-/three-bvh-csg-0.0.5.tgz",
+      "integrity": "sha512-gxqmaV3HlGHUGB857AV8oLoNxx5Q0T7io1DUs5nrCwmmFqjWZT3a8ue7WapE394wY8awFVAMr+YvdSrzqswpFA==",
+      "requires": {}
     },
     "three-css2drender": {
       "version": "1.0.0",
@@ -26316,6 +26347,18 @@
       "integrity": "sha512-redDCSBDvgxiJIm6EDT8WO5WJTVg5gUEy/vjtAgJa/zX4peEUnZ8K+mXo6v94EudbnuI5h48/hQo9NExf04ENg==",
       "requires": {}
     },
+    "three-js-csg": {
+      "version": "72.0.0",
+      "resolved": "https://registry.npmjs.org/three-js-csg/-/three-js-csg-72.0.0.tgz",
+      "integrity": "sha512-Fr2pWWaHN1PPgjVsA7P2VZOHrMMXebQmJlasxGYebbSwURRwwi/vlayI69D+3qW39eNXQ6j+iO1VEOVC41mVFQ=="
+    },
+    "three-mesh-bvh": {
+      "version": "0.5.23",
+      "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz",
+      "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==",
+      "peer": true,
+      "requires": {}
+    },
     "three-obj-mtl-loader": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/three-obj-mtl-loader/-/three-obj-mtl-loader-1.0.3.tgz",

+ 3 - 1
package.json

@@ -27,8 +27,10 @@
     "quill-image-resize-module": "^3.0.0",
     "spark-md5": "^3.0.1",
     "style-loader": "^0.23.1",
-    "three": "^0.142.0",
+    "three": "^0.124.0",
+    "three-bvh-csg": "^0.0.5",
     "three-css2drender": "^1.0.0",
+    "three-js-csg": "^72.0.0",
     "three-obj-mtl-loader": "^1.0.3",
     "tinymce": "^5.1.5",
     "vue": "^2.6.10",

二进制
public/sensor/ogt/ogt.glb


+ 679 - 0
src/views/modelLibrary/common/ThreeBSP.js

@@ -0,0 +1,679 @@
+/** 说明
+ 基于https://github.com/sshirokov/ThreeBSP修改的 这个版本太老了 很多api都不支持
+ 修改:目前适用于threeR140+  by李东琳
+ 必须在前面先引入 THREE 包
+ import * as THREE from 'three'
+ 用法:
+ 创建球形几何体
+ const sphereGeometry = new THREE.SphereGeometry(50, 20, 20)
+ const sphere = this.createMesh(sphereGeometry)
+ 创建立方体几何体
+ const cubeGeometry = new THREE.BoxGeometry(30, 30, 30)
+ const cube = this.createMesh(cubeGeometry)
+ cube.position.x = -50
+ 生成 ThreeBSP 对象
+ const sphereBSP = new ThreeBSP(sphere)
+ const cubeBSP = new ThreeBSP(cube)
+ 以下计算方式取其一即可
+ 进行差集计算(使用该函数可以在第一个几何体中移除两个几何体重叠的部分来创建新的几何体。)
+ const resultBSP = sphereBSP.subtract(cubeBSP)
+ 进行并集计算(使用该函数可以将两个几何体联合起来创建出一个新的几何体。)
+ const resultBSP = sphereBSP.union(cubeBSP)
+ 进行交集计算(使用该函数可以基于两个现有几何体的重合的部分定义此几何体的形状。)
+ const resultBSP = sphereBSP.intersect(cubeBSP)
+ 从 BSP 对象内获取到处理完后的 mesh 模型数据
+ const result = resultBSP.toMesh()
+ 更新模型的面和顶点的数据
+ result.geometry.computeVertexNormals()
+ 重新赋值一个纹理
+ const material = new THREE.MeshPhongMaterial({ color: 0x00ffff })
+ result.material = material
+ 将计算出来模型添加到场景当中
+ this.scene.add(result)
+ **/
+
+import * as THREE from 'three'
+// 表示最大的偏差量,如果偏差小于此值,则忽略偏差
+const EPSILON = 1e-5;
+
+// 共面
+const COPLANAR = 0;
+
+// 在前方
+const FRONT = 1;
+
+// 在后方
+const BACK = 2;
+
+// 存在交集
+const SPANNING = 3;
+
+var __slice = [].slice,
+    __hasProp = {}.hasOwnProperty,
+    __extends = function (child, parent) {
+        for (var key in parent) {
+            if (__hasProp.call(parent, key)) child[key] = parent[key];
+        }
+
+        function ctor() {
+            this.constructor = child;
+        }
+
+        ctor.prototype = parent.prototype;
+        child.prototype = new ctor();
+        child.__super__ = parent.prototype;
+        return child;
+    };
+const ThreeBSP = (function () {
+
+    function ThreeBSP(treeIsh, matrix) {
+        this.matrix = matrix;
+        if (this.matrix == null) {
+            this.matrix = new THREE.Matrix4();
+        }
+        this.tree = this.toTree(treeIsh);
+    }
+
+    ThreeBSP.prototype.toTree = function (treeIsh) {
+        if (treeIsh instanceof ThreeBSP.Node) {
+            return treeIsh;
+        }
+        // 看Three.js 0.142源码,各类Geometry基本都是继承或由BufferGeometry实现,基本上THREE.Geometry就废弃了,所以需要将获取点、法向量、uv信息要从以前的THREE.Geometry的faces中获取改为从THREE.BufferGeometry的attributes中获取
+        var polygons = [],
+            geometry = (treeIsh === THREE.BufferGeometry || (treeIsh.type || "").endsWith("Geometry")) ? treeIsh : treeIsh.constructor === THREE.Mesh ? (treeIsh.updateMatrix(), this.matrix = treeIsh.matrix.clone(), treeIsh.geometry) : void 0;
+
+            if (geometry && geometry.attributes) {
+            // todo 暂时就不对geometry.attributes中的position、 normal和uv进行非空验证了,日后有时间在说吧,正常创建的BufferGeometry这些值通常都是有的
+            var attributes = geometry.attributes, normal = attributes.normal, position = attributes.position,
+                uv = attributes.uv;
+            // 点的数量
+            var pointsLength = attributes.position.array.length / attributes.position.itemSize;
+
+            // 如果索引三角形index不为空,则根据index获取面的顶点、法向量、uv信息
+            if (geometry.index) {
+                var pointsArr = [], normalsArr = [], uvsArr = [];
+                // 从geometry的attributes读取点、法向量、uv数据
+                for (var i = 0, len = pointsLength; i < len; i++) {
+                    // 通常一个点和一个法向量的数据量(itemSize)是3,一个uv的数据量(itemSize)是2
+                    var startIndex = 3 * i
+                    pointsArr.push(new THREE.Vector3(position.array[startIndex], position.array[startIndex + 1], position.array[startIndex + 2]));
+                    normalsArr.push(new THREE.Vector3(normal.array[startIndex], normal.array[startIndex + 1], normal.array[startIndex + 2]));
+                    uvsArr.push(new THREE.Vector2(uv.array[2 * i], uv.array[2 * i + 1]));
+                }
+                var index = geometry.index.array;
+                for (let i = 0, len = index.length; i < len;) {
+                    var polygon = new ThreeBSP.Polygon();
+                    // 将所有面都按照三角面进行处理,即三个顶点组成一个面
+                    for (var j = 0; j < 3; j++) {
+                        var pointIndex = index[i], point = pointsArr[pointIndex];
+                        var vertex = new ThreeBSP.Vertex(point.x, point.y, point.z, normalsArr[pointIndex], uvsArr[pointIndex]);
+                        vertex.applyMatrix4(this.matrix);
+                        polygon.vertices.push(vertex);
+                        i++;
+                    }
+                    polygons.push(polygon.calculateProperties());
+                }
+            } else {
+                // 如果索引三角形index为空,则假定每三个相邻位置(即相邻的三个点)表示一个三角形
+                for (let i = 0, len = pointsLength; i < len;) {
+                    let polygon = new ThreeBSP.Polygon();
+                    // 将所有面都按照三角面进行处理,即三个顶点组成一个面
+                    for (let j = 0; j < 3; j++) {
+                        let startIndex = 3 * i
+                        let vertex = new ThreeBSP.Vertex(position.array[startIndex], position.array[startIndex + 1], position.array[startIndex + 2], new THREE.Vector3(normal.array[startIndex], normal.array[startIndex + 1], normal.array[startIndex + 2]), new THREE.Vector2(uv.array[2 * i], uv.array[2 * i + 1]));
+                        vertex.applyMatrix4(this.matrix);
+                        polygon.vertices.push(vertex);
+                        i++;
+                    }
+                    polygons.push(polygon.calculateProperties());
+                }
+            }
+        } else {
+            console.error("初始化ThreeBSP时未获取到几何数据信息,请检查初始化参数");
+        }
+        return new ThreeBSP.Node(polygons);
+    };
+
+    /**
+     * 转换成mesh
+     * @param {*} material 材质
+     * @param {Boolean} groupByCoplanar 是否根据共面进行分组
+     * @param {Boolean} uniqueMaterial 每个分组使用唯一的材质(此处将材质索引和组索引设置为一样的)
+     * @returns
+     */
+    ThreeBSP.prototype.toMesh = function (material, groupByCoplanar, uniqueMaterial) {
+        var geometry = this.toGeometry(groupByCoplanar, uniqueMaterial);
+        if (material == null) {
+            material = new THREE.MeshNormalMaterial();
+        }
+        var mesh = new THREE.Mesh(geometry, material);
+        // mesh.position.getPositionFromMatrix(this.matrix);//弃用api
+        mesh.position.setFromMatrixPosition(this.matrix);
+        mesh.rotation.setFromRotationMatrix(this.matrix);
+        return mesh;
+    };
+
+    /**
+     * 转换成图形
+     * @param {Boolean} groupByCoplanar 是否根据共面进行分组
+     * @param {Boolean} uniqueMaterial 每个分组使用唯一的材质(此处将材质索引和组索引设置为一样的)
+     * @returns
+     */
+    ThreeBSP.prototype.toGeometry = function (groupByCoplanar, uniqueMaterial) {
+        var geometry = new THREE.BufferGeometry();
+        //Matrix4 没有getInverse方法 改成
+        //var matrix = new THREE.Matrix4().getInverse(this.matrix);
+        var matrix = this.matrix.invert();
+        // verticesArr用于记录点(去重),index依次记录面中各个点对应的索引
+        var position = [], normal = [], uv = [], verticesArr = [], index = [];
+        var resolvePolygon = (polygon) => {
+            polygon.vertices.forEach(item => {
+                var vertice = item.clone().applyMatrix4(matrix), verticeIndex = null;
+                for (var i = 0, len = verticesArr.length; i < len; i++) {
+                    if (vertice.equals(verticesArr[i])) {
+                        verticeIndex = i;
+                        break;
+                    }
+                }
+                // verticeIndex为空,表示数组中未记录当前点,进行点数据处理
+                if (verticeIndex == null) {
+                    verticeIndex = verticesArr.length;
+                    verticesArr.push(vertice);
+                    position.push(vertice.x);
+                    position.push(vertice.y);
+                    position.push(vertice.z);
+                    normal.push(vertice.normal.x);
+                    normal.push(vertice.normal.y);
+                    normal.push(vertice.normal.z);
+                    uv.push(vertice.uv.x);
+                    uv.push(vertice.uv.y);
+                }
+                // 存储点的索引
+                index.push(verticeIndex);
+            })
+        };
+        if (groupByCoplanar) {
+            // 将共面的面分到相同的组中
+            var polygonGroups = [], groups = [];
+            this.tree.allPolygons().forEach(polygon => {
+                // 归入分组标志
+                var flag = false;
+                for (var i = 0, len = polygonGroups.length; i < len; i++) {
+                    if (COPLANAR === polygon.classifySide(polygonGroups[i][0])) {
+                        polygonGroups[i].push(polygon);
+                        flag = true;
+                        break;
+                    }
+                }
+                if (!flag) {
+                    // 为归入到已有分组中,建立新的分组
+                    polygonGroups.push([polygon]);
+                }
+            });
+            // 按照共面组进行数据的处理
+            var start = 0;
+            for (var i = 0, len = polygonGroups.length; i < len; i++) {
+                var polygonGroup = polygonGroups[i], count = polygonGroup.length * 3, groupItem = {
+                    start: start,
+                    count: count
+                };
+                if (uniqueMaterial) {
+                    groupItem.materialIndex = i;
+                }
+                polygonGroup.forEach(resolvePolygon);
+                groups.push(groupItem);
+                start = start + count;
+            }
+            geometry.groups = groups;
+        } else {
+            this.tree.allPolygons().forEach(resolvePolygon);
+        }
+
+        geometry.setAttribute('position', new THREE.BufferAttribute(Float32Array.from(position), 3, false));
+        geometry.setAttribute('normal', new THREE.BufferAttribute(Float32Array.from(normal), 3, false));
+        geometry.setAttribute('uv', new THREE.BufferAttribute(Float32Array.from(uv), 2, false));
+        geometry.index = new THREE.Uint16BufferAttribute(new Uint16Array(index), 1, false);
+        return geometry;
+    };
+
+    /**
+     * 差集
+     * @param {*} other
+     * @returns
+     */
+    ThreeBSP.prototype.subtract = function (other) {
+        var us = this.tree.clone(), them = other.tree.clone();
+        us.invert().clipTo(them);
+        them.clipTo(us).invert().clipTo(us).invert();
+        return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
+    };
+
+    /**
+     * 并集
+     * @param {*} other
+     * @returns
+     */
+    ThreeBSP.prototype.union = function (other) {
+        var us = this.tree.clone(), them = other.tree.clone();
+        us.clipTo(them);
+        them.clipTo(us).invert().clipTo(us).invert();
+        return new ThreeBSP(us.build(them.allPolygons()), this.matrix);
+    };
+
+    /**
+     * 交集
+     * @param {*} other
+     * @returns
+     */
+    ThreeBSP.prototype.intersect = function (other) {
+        var us = this.tree.clone(), them = other.tree.clone();
+        them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
+        return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
+    };
+    return ThreeBSP;
+})();
+
+ThreeBSP.Vertex = (function (_super) {
+    __extends(Vertex, _super);
+
+    function Vertex(x, y, z, normal, uv) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.normal = normal != null ? normal : new THREE.Vector3();
+        this.uv = uv != null ? uv : new THREE.Vector2();
+        // 此方法调用父级THREE.Vector3构造函数会报错TypeError: Class constructor Vector3 cannot be invoked without 'new',因此弃用此方式,改为显示初始化x,y,z
+        // Vertex.__super__.constructor.call(this, x, y, z);
+    }
+
+    /**
+     * 克隆点
+     * @returns
+     */
+    Vertex.prototype.clone = function () {
+        return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
+    };
+
+    /**
+     * 插点函数
+     * @param {*} v
+     * @param {*} alpha
+     * @returns
+     */
+    Vertex.prototype.lerp = function (v, alpha) {
+        // 对uv进行插值
+        this.uv.add(v.uv.clone().sub(this.uv).multiplyScalar(alpha));
+        // 对法向量进行插值
+        this.normal.lerp(v, alpha);
+        // 调用THREE.Vector3的lerp方法对点进行插值
+        return Vertex.__super__.lerp.apply(this, arguments);
+    };
+
+    /**
+     * 在两个点之间插入新的点
+     * @returns
+     */
+    Vertex.prototype.interpolate = function () {
+        var args = 1 <= arguments.length ? __slice.call(arguments, 0) : [], cloneVertex = this.clone();
+        return cloneVertex.lerp.apply(cloneVertex, args);
+    };
+
+    /**
+     * 判断两个点是否相同
+     * @returns
+     */
+    Vertex.prototype.equals = function (vertex) {
+        if (vertex) {
+            if (this.x === vertex.x && this.y === vertex.y && this.z === vertex.z) {
+                var checkUv = function (uv1, uv2) {
+                    if (uv1 && uv2 && uv1.x === uv2.x && uv1.y === uv2.y) {
+                        return true;
+                    } else if (!uv1 && !uv2) {
+                        return true;
+                    }
+                    return false;
+                }
+                if (this.normal && vertex.normal && this.normal.x === vertex.normal.x && this.normal.y === vertex.normal.y && this.normal.z === vertex.normal.z) {
+                    return checkUv(this.uv, vertex.uv);
+                }
+                if (!this.normal && !vertex.normal) {
+                    return checkUv(this.uv, vertex.uv);
+                }
+            }
+        }
+        return false;
+    };
+
+    return Vertex;
+
+})(THREE.Vector3);
+
+/**
+ * 多边形(或者成为网格),目前默认只有三角形
+ */
+ThreeBSP.Polygon = (function () {
+    function Polygon(vertices, normal, w) {
+        this.vertices = vertices != null ? vertices : [];
+        // 网格面的法向量
+        this.normal = normal;
+        this.w = w;
+        if (this.vertices.length) {
+            this.calculateProperties();
+        }
+    }
+
+    /**
+     * 计算面的一些属性
+     * @returns
+     */
+    Polygon.prototype.calculateProperties = function () {
+        var a = this.vertices[0], b = this.vertices[1], c = this.vertices[2];
+        // 计算面的法向量
+        this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
+        this.w = this.normal.clone().dot(a);
+        return this;
+    };
+
+    /**
+     * 克隆网格面
+     * @returns
+     */
+    Polygon.prototype.clone = function () {
+        return new ThreeBSP.Polygon(this.vertices.map(v => v.clone()), this.normal.clone(), this.w);
+    };
+
+    /**
+     * 对网格面进行翻转倒置
+     * @returns
+     */
+    Polygon.prototype.invert = function () {
+        this.normal.multiplyScalar(-1);
+        this.w *= -1;
+        this.vertices.reverse();
+        return this;
+    };
+
+    /**
+     * 判断点与当前网格面的关系
+     * @param {*} vertex
+     * @returns
+     */
+    Polygon.prototype.classifyVertex = function (vertex) {
+        var side = this.normal.dot(vertex) - this.w;
+        switch (false) {
+            case !(side < -EPSILON):
+                return BACK;
+            case !(side > EPSILON):
+                return FRONT;
+            default:
+                // 共面
+                return COPLANAR;
+        }
+    };
+
+    /**
+     * 判断指定网格面polygon与当前网格面的关系(FRONT:在当前面的前方, BACK:在当前面的后方, COPLANAR:与当前面共面, SPANNING:两个面交叉)
+     * @param {*} polygon
+     * @returns
+     */
+    Polygon.prototype.classifySide = function (polygon) {
+        var back = 0, front = 0;
+        polygon.vertices.forEach(vertice => {
+            switch (this.classifyVertex(vertice)) {
+                case FRONT:
+                    front += 1;
+                    break;
+                case BACK:
+                    back += 1;
+                    break;
+            }
+        })
+
+        if (front > 0 && back === 0) {
+            return FRONT;
+        }
+        if (front === 0 && back > 0) {
+            return BACK;
+        }
+        if ((front === back && back === 0)) {
+            // 共面
+            return COPLANAR;
+        }
+        return SPANNING;
+    };
+
+    /**
+     * 对两个网格面进行嵌合
+     * @param {*} poly
+     * @returns
+     */
+    Polygon.prototype.tessellate = function (poly) {
+        // 如果两个面是平行的或者时共面,则直接返回无需处理
+        if (this.classifySide(poly) !== SPANNING) {
+            return [poly];
+        }
+        // 如果两个面是相交面,则进行插值处理
+        let frontVertices = [], backVertices = [], count = poly.vertices.length, t, ti, tj, v, vi, vj;
+        for (let i = 0, _len = poly.vertices.length; i < _len; i++) {
+            vi = poly.vertices[i];
+            vj = poly.vertices[((i + 1) % count)];
+            ti = this.classifyVertex(vi),
+                tj = this.classifyVertex(vj);
+            if (ti !== BACK) {
+                frontVertices.push(vi);
+            }
+            if (ti !== FRONT) {
+                backVertices.push(vi);
+            }
+            if ((ti | tj) === SPANNING) {
+                t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().sub(vi));
+                v = vi.interpolate(vj, t);
+                frontVertices.push(v);
+                backVertices.push(v);
+            }
+        }
+        var polys = [], frontLength = frontVertices.length, backLength = backVertices.length;
+        if (frontLength >= 3) {
+            polys.push(new ThreeBSP.Polygon(frontVertices.slice(0, 3)));
+            // 将多边形切割成多个三角面
+            if (frontLength > 3) {
+                var newVertices;
+                for (var start = 2; start < frontLength; start++) {
+                    newVertices = [frontVertices[start], frontVertices[(start + 1) % frontLength], frontVertices[(start + 2) % frontLength]]
+                    polys.push(new ThreeBSP.Polygon(newVertices));
+                }
+            }
+        }
+        if (backLength >= 3) {
+            polys.push(new ThreeBSP.Polygon(backVertices.slice(0, 3)));
+            // 将多边形切割成多个三角面
+            if (backLength > 3) {
+                let newVertices;
+                for (let start = 2; start < backLength - 1; start++) {
+                    newVertices = [backVertices[start], backVertices[(start + 1) % backLength], backVertices[(start + 2) % backLength]]
+                    polys.push(new ThreeBSP.Polygon(newVertices));
+                }
+            }
+        }
+        return polys;
+    };
+
+    /**
+     * 将指定面的点对于当前面的关系进行细分
+     * @param {*} polygon
+     * @param {*} coplanar_front
+     * @param {*} coplanar_back
+     * @param {*} front
+     * @param {*} back
+     * @returns
+     */
+    Polygon.prototype.subdivide = function (polygon, coplanar_front, coplanar_back, front, back) {
+        var poly, side, _ref = this.tessellate(polygon), _results = [];
+        for (var _i = 0, _len = _ref.length; _i < _len; _i++) {
+            poly = _ref[_i];
+            side = this.classifySide(poly);
+            switch (side) {
+                case FRONT:
+                    _results.push(front.push(poly));
+                    break;
+                case BACK:
+                    _results.push(back.push(poly));
+                    break;
+                case COPLANAR:
+                    if (this.normal.dot(poly.normal) > 0) {
+                        _results.push(coplanar_front.push(poly));
+                    } else {
+                        _results.push(coplanar_back.push(poly));
+                    }
+                    break;
+                default:
+                    throw new Error("BUG: Polygon of classification " + side + " in subdivision");
+            }
+        }
+        return _results;
+    };
+
+    return Polygon;
+})();
+
+ThreeBSP.Node = (function () {
+
+    function Node(polygons) {
+        this.polygons = [];
+        if (polygons != null && polygons.length) {
+            this.build(polygons);
+        }
+    }
+
+    /**
+     *
+     * @returns 克隆Node
+     */
+    Node.prototype.clone = function () {
+        var node = new ThreeBSP.Node();
+        node.divider = this.divider != null ? this.divider.clone() : void 0;
+        node.polygons = this.polygons.map(item => item.clone());
+        node.front = this.front != null ? this.front.clone() : void 0;
+        node.back = this.back != null ? this.back.clone() : void 0;
+        return node;
+    };
+
+    /**
+     * 根据面构建Node
+     * @param {*} polygons
+     * @returns
+     */
+    Node.prototype.build = function (polygons) {
+        var polys, sides = {
+            front: [],
+            back: []
+        };
+        if (this.divider == null) {
+            this.divider = polygons[0].clone();
+        }
+        for (var _i = 0, _len = polygons.length; _i < _len; _i++) {
+            this.divider.subdivide(polygons[_i], this.polygons, this.polygons, sides.front, sides.back);
+        }
+        for (var side in sides) {
+            if (!__hasProp.call(sides, side)) continue;
+            polys = sides[side];
+            if (polys.length) {
+                if (this[side] == null) {
+                    this[side] = new ThreeBSP.Node();
+                }
+                this[side].build(polys)
+            }
+        }
+        return this;
+    };
+
+    /**
+     * 判断是否是凸面的
+     * @param {*} polys
+     * @returns
+     */
+    Node.prototype.isConvex = function (polys) {
+        var inner, outer;
+        for (var _i = 0, _len = polys.length; _i < _len; _i++) {
+            inner = polys[_i];
+            for (var _j = 0, _len1 = polys.length; _j < _len1; _j++) {
+                outer = polys[_j];
+                if (inner !== outer && outer.classifySide(inner) !== BACK) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    };
+
+    /**
+     * 获取Node的所有网格面
+     * @returns
+     */
+    Node.prototype.allPolygons = function () {
+        return this.polygons.slice().concat(this.front != null ? this.front.allPolygons() : []).concat(this.back != null ? this.back.allPolygons() : []);
+    };
+
+    /**
+     * 将Node进行倒置反转
+     * @returns
+     */
+    Node.prototype.invert = function () {
+        // 反转网格
+        this.polygons.forEach(item => item.invert());
+
+        [this.divider, this.front, this.back].forEach(item => {
+            if (item != null) {
+                item.invert();
+            }
+        });
+
+        var _ref2 = [this.back, this.front];
+        this.front = _ref2[0];
+        this.back = _ref2[1]
+        return this;
+    };
+
+    /**
+     * 剪裁多边形
+     * @param {*} polygons
+     * @returns
+     */
+    Node.prototype.clipPolygons = function (polygons) {
+        var back = [], front = [];
+        if (!this.divider) {
+            return polygons.slice();
+        }
+        polygons.forEach(poly => this.divider.subdivide(poly, front, back, front, back))
+        if (this.front) {
+            front = this.front.clipPolygons(front);
+        }
+        if (this.back) {
+            back = this.back.clipPolygons(back);
+        }
+        return front.concat(this.back ? back : []);
+    };
+
+    /**
+     * 用指定node对当前node进行剪裁
+     * @param {*} node
+     * @returns
+     */
+    Node.prototype.clipTo = function (node) {
+        this.polygons = node.clipPolygons(this.polygons);
+        if (this.front != null) {
+            this.front.clipTo(node);
+        }
+        if (this.back != null) {
+            this.back.clipTo(node);
+        }
+
+        return this;
+    };
+
+    return Node;
+})();
+
+export {
+    ThreeBSP
+}

+ 586 - 0
src/views/modelLibrary/common/ThreeCSG.js

@@ -0,0 +1,586 @@
+/*jshint esversion: 6 */
+
+import * as THREE from "three";
+
+const EPSILON = 1e-5,
+    COPLANAR = 0,
+    FRONT = 1,
+    BACK = 2,
+    SPANNING = 3;
+
+export default class ThreeBSP {
+    constructor(geometry) {
+        // Convert THREE.Geometry to ThreeBSP
+        var i, _length_i,
+            face, vertex, faceVertexUvs, uvs,
+            polygon,
+            polygons = [],
+            tree;
+
+        this.Polygon = Polygon;
+        this.Vertex = Vertex;
+        this.Node = Node;
+        if (geometry instanceof THREE.Geometry) {
+            this.matrix = new THREE.Matrix4();
+        } else if (geometry instanceof THREE.Mesh) {
+            // #todo: add hierarchy support
+            geometry.updateMatrix();
+            this.matrix = geometry.matrix.clone();
+            geometry = geometry.geometry;
+        } else if (geometry instanceof Node) {
+            this.tree = geometry;
+            this.matrix = new THREE.Matrix4();
+            return this;
+        } else {
+            throw 'ThreeBSP: Given geometry is unsupported';
+        }
+
+        for (i = 0, _length_i = geometry.faces.length; i < _length_i; i++) {
+            face = geometry.faces[i];
+            faceVertexUvs = geometry.faceVertexUvs[0][i];
+            polygon = new Polygon();
+
+            if (face instanceof THREE.Face3) {
+                vertex = geometry.vertices[face.a];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[0].x, faceVertexUvs[0].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+
+                vertex = geometry.vertices[face.b];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[1].x, faceVertexUvs[1].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+
+                vertex = geometry.vertices[face.c];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[2].x, faceVertexUvs[2].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+            } else if (typeof THREE.Face4) {
+                vertex = geometry.vertices[face.a];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[0].x, faceVertexUvs[0].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+
+                vertex = geometry.vertices[face.b];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[1].x, faceVertexUvs[1].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+
+                vertex = geometry.vertices[face.c];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[2].x, faceVertexUvs[2].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+
+                vertex = geometry.vertices[face.d];
+                uvs = faceVertexUvs ? new THREE.Vector2(faceVertexUvs[3].x, faceVertexUvs[3].y) : null;
+                vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[3], uvs);
+                vertex.applyMatrix4(this.matrix);
+                polygon.vertices.push(vertex);
+            } else {
+                throw 'Invalid face type at index ' + i;
+            }
+
+            polygon.calculateProperties();
+            polygons.push(polygon);
+        }
+
+        this.tree = new Node(polygons);
+    }
+
+    subtract(other_tree) {
+        var a = this.tree.clone(),
+            b = other_tree.tree.clone();
+
+        a.invert();
+        a.clipTo(b);
+        b.clipTo(a);
+        b.invert();
+        b.clipTo(a);
+        b.invert();
+        a.build(b.allPolygons());
+        a.invert();
+        a = new ThreeBSP(a);
+        a.matrix = this.matrix;
+        return a;
+    }
+
+    union(other_tree) {
+        var a = this.tree.clone(),
+            b = other_tree.tree.clone();
+
+        a.clipTo(b);
+        b.clipTo(a);
+        b.invert();
+        b.clipTo(a);
+        b.invert();
+        a.build(b.allPolygons());
+        a = new ThreeBSP(a);
+        a.matrix = this.matrix;
+        return a;
+    }
+
+    intersect(other_tree) {
+        var a = this.tree.clone(),
+            b = other_tree.tree.clone();
+
+        a.invert();
+        b.clipTo(a);
+        b.invert();
+        a.clipTo(b);
+        b.clipTo(a);
+        a.build(b.allPolygons());
+        a.invert();
+        a = new ThreeBSP(a);
+        a.matrix = this.matrix;
+        return a;
+    }
+
+    toGeometry() {
+        var i, j,
+            // matrix = new THREE.Matrix4().getInverse(this.matrix),
+            matrix = new THREE.Matrix4().copy(this.matrix).invert(),
+            geometry = new THREE.Geometry(),
+            polygons = this.tree.allPolygons(),
+            polygon_count = polygons.length,
+            polygon, polygon_vertice_count,
+            vertice_dict = {},
+            vertex_idx_a, vertex_idx_b, vertex_idx_c,
+            vertex, face,
+            verticeUvs;
+
+        for (i = 0; i < polygon_count; i++) {
+            polygon = polygons[i];
+            polygon_vertice_count = polygon.vertices.length;
+
+            for (j = 2; j < polygon_vertice_count; j++) {
+                verticeUvs = [];
+
+                vertex = polygon.vertices[0];
+                verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));
+                vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
+                vertex.applyMatrix4(matrix);
+
+                if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
+                    vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
+                } else {
+                    geometry.vertices.push(vertex);
+                    vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
+                }
+
+                vertex = polygon.vertices[j - 1];
+                verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));
+                vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
+                vertex.applyMatrix4(matrix);
+                if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
+                    vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
+                } else {
+                    geometry.vertices.push(vertex);
+                    vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
+                }
+
+                vertex = polygon.vertices[j];
+                verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));
+                vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
+                vertex.applyMatrix4(matrix);
+                if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
+                    vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
+                } else {
+                    geometry.vertices.push(vertex);
+                    vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
+                }
+
+                face = new THREE.Face3(
+                    vertex_idx_a,
+                    vertex_idx_b,
+                    vertex_idx_c,
+                    new THREE.Vector3(polygon.normal.x, polygon.normal.y, polygon.normal.z)
+                );
+
+                geometry.faces.push(face);
+                geometry.faceVertexUvs[0].push(verticeUvs);
+            }
+
+        }
+        return geometry;
+    }
+
+    toMesh(material) {
+        var geometry = this.toGeometry(),
+            mesh = new THREE.Mesh(geometry, material);
+
+        mesh.position.setFromMatrixPosition(this.matrix);
+        mesh.rotation.setFromRotationMatrix(this.matrix);
+
+        return mesh;
+    }
+}
+class Polygon {
+    constructor(vertices, normal, w) {
+        if (!(vertices instanceof Array)) {
+            vertices = [];
+        }
+
+        this.vertices = vertices;
+        if (vertices.length > 0) {
+            this.calculateProperties();
+        } else {
+            this.normal = this.w = undefined;
+        }
+    }
+
+    calculateProperties() {
+        var a = this.vertices[0],
+            b = this.vertices[1],
+            c = this.vertices[2];
+
+        this.normal = b.clone().subtract(a).cross(
+            c.clone().subtract(a)
+        ).normalize();
+
+        this.w = this.normal.clone().dot(a);
+
+        return this;
+    }
+
+    clone() {
+        var i, vertice_count,
+            polygon = new Polygon();
+
+        for (i = 0, vertice_count = this.vertices.length; i < vertice_count; i++) {
+            polygon.vertices.push(this.vertices[i].clone());
+        }
+        polygon.calculateProperties();
+
+        return polygon;
+    }
+
+    flip() {
+        var i, vertices = [];
+
+        this.normal.multiplyScalar(-1);
+        this.w *= -1;
+
+        for (i = this.vertices.length - 1; i >= 0; i--) {
+            vertices.push(this.vertices[i]);
+        }
+        this.vertices = vertices;
+
+        return this;
+    }
+
+    classifyVertex(vertex) {
+        var side_value = this.normal.dot(vertex) - this.w;
+
+        if (side_value < -EPSILON) {
+            return BACK;
+        } else if (side_value > EPSILON) {
+            return FRONT;
+        } else {
+            return COPLANAR;
+        }
+    }
+
+    classifySide(polygon) {
+        var i, vertex, classification,
+            num_positive = 0,
+            num_negative = 0,
+            vertice_count = polygon.vertices.length;
+
+        for (i = 0; i < vertice_count; i++) {
+            vertex = polygon.vertices[i];
+            classification = this.classifyVertex(vertex);
+            if (classification === FRONT) {
+                num_positive++;
+            } else if (classification === BACK) {
+                num_negative++;
+            }
+        }
+
+        if (num_positive === vertice_count && num_negative === 0) {
+            return FRONT;
+        } else if (num_positive === 0 && num_negative === vertice_count) {
+            return BACK;
+        } else if (num_positive > 0 && num_negative > 0) {
+            return SPANNING;
+        } else {
+            return COPLANAR;
+        }
+    }
+
+    splitPolygon(polygon, coplanar_front, coplanar_back, front, back) {
+        var classification = this.classifySide(polygon);
+
+        if (classification === COPLANAR) {
+
+            (this.normal.dot(polygon.normal) > 0 ? coplanar_front : coplanar_back).push(polygon);
+
+        } else if (classification === FRONT) {
+
+            front.push(polygon);
+
+        } else if (classification === BACK) {
+
+            back.push(polygon);
+
+        } else {
+
+            var vertice_count,
+                i, j, ti, tj, vi, vj,
+                t, v,
+                f = [],
+                b = [];
+
+            for (i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++) {
+
+                j = (i + 1) % vertice_count;
+                vi = polygon.vertices[i];
+                vj = polygon.vertices[j];
+                ti = this.classifyVertex(vi);
+                tj = this.classifyVertex(vj);
+
+                if (ti != BACK) f.push(vi);
+                if (ti != FRONT) b.push(vi);
+                if ((ti | tj) === SPANNING) {
+                    t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().subtract(vi));
+                    v = vi.interpolate(vj, t);
+                    f.push(v);
+                    b.push(v);
+                }
+            }
+
+
+            if (f.length >= 3) front.push(new Polygon(f).calculateProperties());
+            if (b.length >= 3) back.push(new Polygon(b).calculateProperties());
+        }
+    }
+}
+class Vertex {
+    constructor(x, y, z, normal, uv) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.normal = normal || new THREE.Vector3();
+        this.uv = uv || new THREE.Vector2();
+    }
+
+    clone() {
+        return new Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
+    }
+
+    add(vertex) {
+        this.x += vertex.x;
+        this.y += vertex.y;
+        this.z += vertex.z;
+        return this;
+    }
+
+    subtract(vertex) {
+        this.x -= vertex.x;
+        this.y -= vertex.y;
+        this.z -= vertex.z;
+        return this;
+    }
+
+    multiplyScalar(scalar) {
+        this.x *= scalar;
+        this.y *= scalar;
+        this.z *= scalar;
+        return this;
+    }
+
+    cross(vertex) {
+        var x = this.x,
+            y = this.y,
+            z = this.z;
+
+        this.x = y * vertex.z - z * vertex.y;
+        this.y = z * vertex.x - x * vertex.z;
+        this.z = x * vertex.y - y * vertex.x;
+
+        return this;
+    }
+
+    normalize() {
+        var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
+
+        this.x /= length;
+        this.y /= length;
+        this.z /= length;
+
+        return this;
+    }
+
+    dot(vertex) {
+        return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
+    }
+
+    lerp(a, t) {
+        this.add(
+            a.clone().subtract(this).multiplyScalar(t)
+        );
+
+        this.normal.add(
+            a.normal.clone().sub(this.normal).multiplyScalar(t)
+        );
+
+        this.uv.add(
+            a.uv.clone().sub(this.uv).multiplyScalar(t)
+        );
+
+        return this;
+    }
+
+    interpolate(other, t) {
+        return this.clone().lerp(other, t);
+    }
+
+    applyMatrix4(m) {
+
+        // input: THREE.Matrix4 affine matrix
+
+        var x = this.x, y = this.y, z = this.z;
+
+        var e = m.elements;
+
+        this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
+        this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
+        this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
+
+        return this;
+
+    }
+}
+class Node {
+    constructor(polygons) {
+        var i, polygon_count,
+            front = [],
+            back = [];
+
+        this.polygons = [];
+        this.front = this.back = undefined;
+
+        if (!(polygons instanceof Array) || polygons.length === 0) return;
+
+        this.divider = polygons[0].clone();
+
+        for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {
+            this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
+        }
+
+        if (front.length > 0) {
+            this.front = new Node(front);
+        }
+
+        if (back.length > 0) {
+            this.back = new Node(back);
+        }
+    }
+
+    isConvex(polygons) {
+        var i, j;
+        for (i = 0; i < polygons.length; i++) {
+            for (j = 0; j < polygons.length; j++) {
+                if (i !== j && polygons[i].classifySide(polygons[j]) !== BACK) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    build(polygons) {
+        var i, polygon_count,
+            front = [],
+            back = [];
+
+        if (!this.divider) {
+            this.divider = polygons[0].clone();
+        }
+
+        for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {
+            this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
+        }
+
+        if (front.length > 0) {
+            if (!this.front) this.front = new Node();
+            this.front.build(front);
+        }
+
+        if (back.length > 0) {
+            if (!this.back) this.back = new Node();
+            this.back.build(back);
+        }
+    }
+
+    allPolygons() {
+        var polygons = this.polygons.slice();
+        if (this.front) polygons = polygons.concat(this.front.allPolygons());
+        if (this.back) polygons = polygons.concat(this.back.allPolygons());
+        return polygons;
+    }
+
+    clone() {
+        var node = new Node();
+
+        node.divider = this.divider.clone();
+        node.polygons = this.polygons.map(function (polygon) {
+            return polygon.clone();
+        });
+        node.front = this.front && this.front.clone();
+        node.back = this.back && this.back.clone();
+
+        return node;
+    }
+
+    invert() {
+        var i, polygon_count, temp;
+
+        for (i = 0, polygon_count = this.polygons.length; i < polygon_count; i++) {
+            this.polygons[i].flip();
+        }
+
+        this.divider.flip();
+        if (this.front) this.front.invert();
+        if (this.back) this.back.invert();
+
+        temp = this.front;
+        this.front = this.back;
+        this.back = temp;
+
+        return this;
+    }
+
+    clipPolygons(polygons) {
+        var i, polygon_count,
+            front, back;
+
+        if (!this.divider) return polygons.slice();
+
+        front = [];
+        back = [];
+
+        for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {
+            this.divider.splitPolygon(polygons[i], front, back, front, back);
+        }
+
+        if (this.front) front = this.front.clipPolygons(front);
+        if (this.back) back = this.back.clipPolygons(back);
+        else back = [];
+
+        return front.concat(back);
+    }
+
+    clipTo(node) {
+        this.polygons = node.clipPolygons(this.polygons);
+        if (this.front) this.front.clipTo(node);
+        if (this.back) this.back.clipTo(node);
+    }
+}
+
+window.ThreeBSP = ThreeBSP;

+ 4 - 2
src/views/modelLibrary/common/coordinateAxes.js

@@ -1,10 +1,12 @@
 import * as THREE from 'three'
 import {
     FontLoader
-} from "three/examples/jsm/loaders/FontLoader.js"
+} from "three/src/loaders/FontLoader.js"
+// } from "three/examples/jsm/loaders/FontLoader.js"
 import {
     TextGeometry
-} from "three/examples/jsm/geometries/TextGeometry.js"
+} from "three/src/geometries/TextGeometry.js"
+// } from "three/examples/jsm/geometries/TextGeometry.js"
 
 let publicPath = process.env.BASE_URL
 

+ 94 - 71
src/views/modelLibrary/components/formCamera.vue

@@ -6,69 +6,39 @@
                 <div>{{ form.sensorCode }}</div>
             </div>
             <el-form-item label="传感器名称:" prop="sensorName">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="60"
-                    v-autoTrim="{ obj: form, key: 'sensorName' }"
-                    v-model="form.sensorName"
-                >
+                <el-input placeholder="请输入" maxlength="60" v-autoTrim="{ obj: form, key: 'sensorName' }"
+                    v-model="form.sensorName">
                 </el-input>
             </el-form-item>
             <el-form-item label="传感器描述:" prop="description">
-                <el-input
-                    v-autoTrim="{
-                        obj: form,
-                        key: 'description',
-                    }"
-                    v-model="form.description"
-                    type="textarea"
-                    placeholder="请输入传感器描述"
-                    :autosize="{ minRows: 4, maxRows: 4 }"
-                    maxlength="200"
-                ></el-input>
+                <el-input v-autoTrim="{
+                    obj: form,
+                    key: 'description',
+                }" v-model="form.description" type="textarea" placeholder="请输入传感器描述"
+                    :autosize="{ minRows: 4, maxRows: 4 }" maxlength="200"></el-input>
             </el-form-item>
 
             <div class="titlePanel">
                 <div class="titlePanelBor">内参</div>
             </div>
             <el-form-item label="盲区距离(m):" prop="nearDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'nearDistance' }"
-                    v-model="form.nearDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'nearDistance' }"
+                    v-model="form.nearDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="探测距离(m):" prop="farDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'farDistance' }"
-                    v-model="form.farDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'farDistance' }"
+                    v-model="form.farDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="水平视场角(deg):" prop="fovH">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovH' }"
-                    v-model="form.fovH"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovH' }" v-model="form.fovH"
+                    @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="垂直视场角(deg):" prop="fovV">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovV' }"
-                    v-model="form.fovV"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovV' }" v-model="form.fovV"
+                    @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="分辨率:" prop="resolution">
@@ -79,22 +49,14 @@
                     >800x600</el-radio
                 > -->
                 <el-select v-model="form.resolution">
-                    <el-option
-                        v-for="item in resolutionList"
-                        :label="item.caption"
-                        :value="item.code"
-                        :key="item.code"
-                    ></el-option>
+                    <el-option v-for="item in resolutionList" :label="item.caption" :value="item.code"
+                        :key="item.code"></el-option>
                 </el-select>
             </el-form-item>
             <el-form-item label="帧率(HZ):" prop="frameRate">
                 <el-select v-model="form.frameRate">
-                    <el-option
-                        v-for="item in frameRateList"
-                        :label="item.caption"
-                        :value="item.code"
-                        :key="item.code"
-                    ></el-option>
+                    <el-option v-for="item in frameRateList" :label="item.caption" :value="item.code"
+                        :key="item.code"></el-option>
                 </el-select>
             </el-form-item>
 
@@ -122,6 +84,19 @@ export default {
                 callback(new Error(rule.message));
             callback();
         };
+
+        // 校验为正数且最多4位小数
+        let validateNumMore0 = (rule, value, callback) => {
+            if (value <= 0) {
+                callback(new Error(rule.message));
+                return;
+            }
+
+            !/^(0|[1-9][0-9]*)(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+
         // 校验不大于1500
         let validateNoMore1500 = (rule, value, callback) => {
             if (value > 1500) {
@@ -130,6 +105,7 @@ export default {
             }
             callback();
         };
+
         // 校验不大于90
         let validateNoMore90 = (rule, value, callback) => {
             if (value > 90) {
@@ -139,6 +115,27 @@ export default {
             callback();
         };
 
+        // 校验不大于180
+        let validateNoMore180 = (rule, value, callback) => {
+            if (value > 180) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
+        // 校验盲区距离应小于探测距离
+        let validateNoMoreFar = (rule, value, callback) => {
+            let far = this.form.farDistance;
+            if (!!far && far != 0) {
+                if (value >= far) {
+                    callback(new Error(rule.message));
+                    return;
+                }
+            }
+            callback();
+        };
+
         return {
             frameRateList: [],
             resolutionList: [
@@ -183,17 +180,22 @@ export default {
                         message: "请输入最多带有4位小数的非负数",
                         trigger: ["blur"],
                     },
+                    // {
+                    //     validator: validateNoMoreFar,
+                    //     message: "盲区距离应小于探测距离",
+                    //     trigger: ["blur"],
+                    // },
                 ],
                 farDistance: [
                     { required: true, message: "请输入", trigger: "blur" },
                     {
-                        validator: validateNum,
-                        message: "请输入最多带有4位小数的非负数",
+                        validator: validateNumMore0,
+                        message: "请输入最多带有4位小数的数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNoMore1500,
-                        message: "请输入不大于1500的非负数",
+                        message: "请输入不大于1500的数",
                         trigger: ["blur"],
                     },
                 ],
@@ -205,8 +207,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore180,
+                        message: "请输入不大于180的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -218,8 +220,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore180,
+                        message: "请输入不大于180的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -261,6 +263,12 @@ export default {
         save(isAdd = false) {
             this.$refs.form.validate((valid) => {
                 if (valid) {
+                    // 字符串有问题
+                    if (+this.form.farDistance <= +this.form.nearDistance) {
+                        this.$message.error("盲区距离应小于探测距离");
+                        return;
+                    }
+
                     // 判断是否新增
                     if (isAdd) {
                         this.form.id = "";
@@ -313,13 +321,28 @@ export default {
             }
         },
         inputChange() {
-            if (
-                this.form.farDistance === "" ||
-                this.form.nearDistance === "" ||
-                this.form.fovH === "" ||
-                this.form.fovV === ""
-            )
-                return;
+            let { farDistance, nearDistance, fovH, fovV } = this.form;
+
+            if (farDistance === "" || nearDistance === "" || fovH === "" || fovV === "") {
+                this.$emit("clearCube");
+                return
+            }
+
+            farDistance = +farDistance;
+            nearDistance = +nearDistance;
+            fovH = +fovH;
+            fovV = +fovV;
+
+            if (farDistance <= 0 || farDistance > 1500 || nearDistance < 0 || farDistance <= nearDistance) {
+                this.$emit("clearCube");
+                return
+            }
+
+            if (fovH < 0 || fovH > 180 || fovV < 0 || fovV > 180) {
+                this.$emit("clearCube");
+                return
+            }
+
             this.$emit("inputChange", {
                 n: +this.form.nearDistance,
                 f: +this.form.farDistance,

+ 88 - 83
src/views/modelLibrary/components/formLaserRadar.vue

@@ -6,97 +6,55 @@
                 <div>{{ form.sensorCode }}</div>
             </div>
             <el-form-item label="传感器名称:" prop="sensorName">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="60"
-                    v-autoTrim="{ obj: form, key: 'sensorName' }"
-                    v-model="form.sensorName"
-                >
+                <el-input placeholder="请输入" maxlength="60" v-autoTrim="{ obj: form, key: 'sensorName' }"
+                    v-model="form.sensorName">
                 </el-input>
             </el-form-item>
             <el-form-item label="传感器描述:" prop="description">
-                <el-input
-                    v-autoTrim="{
-                        obj: form,
-                        key: 'description',
-                    }"
-                    v-model="form.description"
-                    type="textarea"
-                    placeholder="请输入传感器描述"
-                    :autosize="{ minRows: 4, maxRows: 4 }"
-                    maxlength="200"
-                ></el-input>
+                <el-input v-autoTrim="{
+                    obj: form,
+                    key: 'description',
+                }" v-model="form.description" type="textarea" placeholder="请输入传感器描述"
+                    :autosize="{ minRows: 4, maxRows: 4 }" maxlength="200"></el-input>
             </el-form-item>
 
             <div class="titlePanel">
                 <div class="titlePanelBor">内参</div>
             </div>
             <el-form-item label="盲区距离(m):" prop="nearDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'nearDistance' }"
-                    v-model="form.nearDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'nearDistance' }"
+                    v-model="form.nearDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="探测距离(m):" prop="farDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'farDistance' }"
-                    v-model="form.farDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'farDistance' }"
+                    v-model="form.farDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="水平视场角(deg):" prop="fovH">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovH' }"
-                    v-model="form.fovH"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovH' }" v-model="form.fovH"
+                    @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="垂直视场角(deg):" prop="fovV">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovV' }"
-                    v-model="form.fovV"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovV' }" v-model="form.fovV"
+                    @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="线数(个):" prop="lineNumber">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="9"
-                    v-autoTrim="{ obj: form, key: 'lineNumber' }"
-                    v-model="form.lineNumber"
-                >
+                <el-input placeholder="请输入" maxlength="9" v-autoTrim="{ obj: form, key: 'lineNumber' }"
+                    v-model="form.lineNumber">
                 </el-input>
             </el-form-item>
             <el-form-item label="帧率(HZ):" prop="frameRate">
                 <el-select v-model="form.frameRate">
-                    <el-option
-                        v-for="item in frameRateList"
-                        :label="item.caption"
-                        :value="item.code"
-                        :key="item.code"
-                    ></el-option>
+                    <el-option v-for="item in frameRateList" :label="item.caption" :value="item.code"
+                        :key="item.code"></el-option>
                 </el-select>
             </el-form-item>
             <el-form-item label="出点数(个):" prop="outputPoints">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="9"
-                    v-autoTrim="{ obj: form, key: 'outputPoints' }"
-                    v-model="form.outputPoints"
-                >
+                <el-input placeholder="请输入" maxlength="9" v-autoTrim="{ obj: form, key: 'outputPoints' }"
+                    v-model="form.outputPoints">
                 </el-input>
             </el-form-item>
 
@@ -107,12 +65,8 @@
                 <!-- <el-radio v-model="form.outputMode" label="1">大地坐标系</el-radio>
                 <el-radio v-model="form.outputMode" label="2">相对本车坐标系</el-radio> -->
                 <el-select v-model="form.outputMode">
-                    <el-option
-                        v-for="item in outputModeList"
-                        :label="item.caption"
-                        :value="item.code"
-                        :key="item.code"
-                    ></el-option>
+                    <el-option v-for="item in outputModeList" :label="item.caption" :value="item.code"
+                        :key="item.code"></el-option>
                 </el-select>
             </el-form-item>
         </el-form>
@@ -132,6 +86,19 @@ export default {
                 callback(new Error(rule.message));
             callback();
         };
+
+        // 校验为正数且最多4位小数
+        let validateNumMore0 = (rule, value, callback) => {
+            if (value <= 0) {
+                callback(new Error(rule.message));
+                return;
+            }
+
+            !/^(0|[1-9][0-9]*)(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+
         // 校验不大于250
         let validateNoMore250 = (rule, value, callback) => {
             if (value > 250) {
@@ -140,6 +107,7 @@ export default {
             }
             callback();
         };
+
         // 校验不大于90
         let validateNoMore90 = (rule, value, callback) => {
             if (value > 90) {
@@ -148,6 +116,16 @@ export default {
             }
             callback();
         };
+
+        // 校验不大于180
+        let validateNoMore180 = (rule, value, callback) => {
+            if (value > 180) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
         // 校验不大于360
         let validateNoMore360 = (rule, value, callback) => {
             if (value > 360) {
@@ -156,6 +134,7 @@ export default {
             }
             callback();
         };
+
         // 校验非负整数
         let validateNumA = (rule, value, callback) => {
             !/^(0|[1-9]+[0]*)$/.test(value) &&
@@ -199,13 +178,13 @@ export default {
                 farDistance: [
                     { required: true, message: "请输入", trigger: "blur" },
                     {
-                        validator: validateNum,
-                        message: "请输入最多带有4位小数的非负数",
+                        validator: validateNumMore0,
+                        message: "请输入最多带有4位小数的数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNoMore250,
-                        message: "请输入不大于250的非负数",
+                        message: "请输入不大于250的数",
                         trigger: ["blur"],
                     },
                 ],
@@ -216,9 +195,14 @@ export default {
                         message: "请输入最多带有4位小数的非负数",
                         trigger: ["blur"],
                     },
+                    // {
+                    //     validator: validateNoMore360,
+                    //     message: "请输入不大于360的非负数",
+                    //     trigger: ["blur"],
+                    // },
                     {
-                        validator: validateNoMore360,
-                        message: "请输入不大于360的非负数",
+                        validator: validateNoMore180,
+                        message: "请输入不大于180的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -230,8 +214,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore180,
+                        message: "请输入不大于180的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -288,6 +272,12 @@ export default {
         save(isAdd = false) {
             this.$refs.form.validate((valid) => {
                 if (valid) {
+                    // 字符串有问题
+                    if (+this.form.farDistance <= +this.form.nearDistance) {
+                        this.$message.error("盲区距离应小于探测距离");
+                        return;
+                    }
+
                     // 判断是否新增
                     if (isAdd) {
                         this.form.id = "";
@@ -339,13 +329,28 @@ export default {
             }
         },
         inputChange() {
-            if (
-                this.form.nearDistance === "" ||
-                this.form.farDistance === "" ||
-                this.form.fovH === "" ||
-                this.form.fovV === ""
-            )
-                return;
+            let { farDistance, nearDistance, fovH, fovV } = this.form;
+
+            if (farDistance === "" || nearDistance === "" || fovH === "" || fovV === "") {
+                this.$emit("clearCube");
+                return
+            }
+
+            farDistance = +farDistance;
+            nearDistance = +nearDistance;
+            fovH = +fovH;
+            fovV = +fovV;
+
+            if (farDistance <= 0 || farDistance > 250 || nearDistance < 0 || farDistance <= nearDistance) {
+                this.$emit("clearCube");
+                return
+            }
+
+            if (fovH < 0 || fovH > 180 || fovV < 0 || fovV > 180) {
+                this.$emit("clearCube");
+                return
+            }
+
             this.$emit("inputChange", {
                 n: +this.form.nearDistance,
                 f: +this.form.farDistance,

+ 93 - 107
src/views/modelLibrary/components/formPerfectSensor.vue

@@ -6,89 +6,49 @@
                 <div>{{ form.sensorCode }}</div>
             </div>
             <el-form-item label="传感器名称:" prop="sensorName">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="60"
-                    v-autoTrim="{ obj: form, key: 'sensorName' }"
-                    v-model="form.sensorName"
-                >
+                <el-input placeholder="请输入" maxlength="60" v-autoTrim="{ obj: form, key: 'sensorName' }"
+                    v-model="form.sensorName">
                 </el-input>
             </el-form-item>
             <el-form-item label="传感器描述:" prop="description">
-                <el-input
-                    v-autoTrim="{
-                        obj: form,
-                        key: 'description',
-                    }"
-                    v-model="form.description"
-                    type="textarea"
-                    placeholder="请输入传感器描述"
-                    :autosize="{ minRows: 4, maxRows: 4 }"
-                    maxlength="200"
-                ></el-input>
+                <el-input v-autoTrim="{
+                    obj: form,
+                    key: 'description',
+                }" v-model="form.description" type="textarea" placeholder="请输入传感器描述"
+                    :autosize="{ minRows: 4, maxRows: 4 }" maxlength="200"></el-input>
             </el-form-item>
 
             <div class="titlePanel">
                 <div class="titlePanelBor">内参</div>
             </div>
             <el-form-item label="盲区距离(m):" prop="nearDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'nearDistance' }"
-                    v-model="form.nearDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'nearDistance' }"
+                    v-model="form.nearDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="探测距离(m):" prop="farDistance">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'farDistance' }"
-                    v-model="form.farDistance"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'farDistance' }"
+                    v-model="form.farDistance" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="水平视场角左(deg):" prop="fovHLeft">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovHLeft' }"
-                    v-model="form.fovHLeft"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovHLeft' }"
+                    v-model="form.fovHLeft" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="水平视场角右(deg):" prop="fovHRight">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovHRight' }"
-                    v-model="form.fovHRight"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovHRight' }"
+                    v-model="form.fovHRight" @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="垂直视场角顶(deg):" prop="fovVTop">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovVTop' }"
-                    v-model="form.fovVTop"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovVTop' }" v-model="form.fovVTop"
+                    @change="inputChange">
                 </el-input>
             </el-form-item>
             <el-form-item label="垂直视场角底(deg):" prop="fovVBottom">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="12"
-                    v-autoTrim="{ obj: form, key: 'fovVBottom' }"
-                    v-model="form.fovVBottom"
-                    @change="inputChange"
-                >
+                <el-input placeholder="请输入" maxlength="12" v-autoTrim="{ obj: form, key: 'fovVBottom' }"
+                    v-model="form.fovVBottom" @change="inputChange">
                 </el-input>
             </el-form-item>
 
@@ -100,41 +60,22 @@
                 <el-radio v-model="form.rangeDisplay" label="0">否</el-radio>
             </el-form-item>
             <el-form-item label="最大目标物数量(个):" prop="maxObjects">
-                <el-input
-                    placeholder="请输入"
-                    maxlength="9"
-                    v-autoTrim="{ obj: form, key: 'maxObjects' }"
-                    v-model="form.maxObjects"
-                >
+                <el-input placeholder="请输入" maxlength="9" v-autoTrim="{ obj: form, key: 'maxObjects' }"
+                    v-model="form.maxObjects">
                 </el-input>
             </el-form-item>
             <el-form-item label="坐标系:" prop="coordinateSystem">
                 <el-select v-model="form.coordinateSystem">
-                    <el-option
-                        v-for="item in coordinateSystemList"
-                        :label="item.caption"
-                        :value="item.code"
-                        :key="item.code"
-                    ></el-option>
+                    <el-option v-for="item in coordinateSystemList" :label="item.caption" :value="item.code"
+                        :key="item.code"></el-option>
                 </el-select>
             </el-form-item>
             <el-form-item label="目标物筛选:" prop="targetFilter">
-                <el-checkbox
-                    :indeterminate="isIndeterminate"
-                    v-model="checkAll"
-                    @change="handleCheckAllChange"
-                    >全选</el-checkbox
-                >
-                <el-checkbox-group
-                    v-model="form.targetFilter"
-                    @change="handleCheckedTargetChange"
-                >
-                    <el-checkbox
-                        v-for="target in targetOptions"
-                        :label="target.code"
-                        :key="target.code"
-                        >{{ target.caption }}</el-checkbox
-                    >
+                <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll"
+                    @change="handleCheckAllChange">全选</el-checkbox>
+                <el-checkbox-group v-model="form.targetFilter" @change="handleCheckedTargetChange">
+                    <el-checkbox v-for="target in targetOptions" :label="target.code" :key="target.code">{{ target.caption
+                    }}</el-checkbox>
                 </el-checkbox-group>
             </el-form-item>
         </el-form>
@@ -154,6 +95,19 @@ export default {
                 callback(new Error(rule.message));
             callback();
         };
+
+        // 校验为正数且最多4位小数
+        let validateNumMore0 = (rule, value, callback) => {
+            if (value <= 0) {
+                callback(new Error(rule.message));
+                return;
+            }
+
+            !/^(0|[1-9][0-9]*)(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+
         // 校验不大于1000
         let validateNoMore1000 = (rule, value, callback) => {
             if (value > 1000) {
@@ -162,6 +116,16 @@ export default {
             }
             callback();
         };
+
+        // 校验不大于360
+        let validateNoMore360 = (rule, value, callback) => {
+            if (value > 360) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
         // 校验不大于90
         let validateNoMore90 = (rule, value, callback) => {
             if (value > 90) {
@@ -170,6 +134,7 @@ export default {
             }
             callback();
         };
+
         // 校验不小于1且不大于20的正整数
         let validateNumA = (rule, value, callback) => {
             if (value < 1 || value > 20) {
@@ -221,13 +186,13 @@ export default {
                 farDistance: [
                     { required: true, message: "请输入", trigger: "blur" },
                     {
-                        validator: validateNum,
-                        message: "请输入最多带有4位小数的非负数",
+                        validator: validateNumMore0,
+                        message: "请输入最多带有4位小数的数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNoMore1000,
-                        message: "请输入不大于1000的非负数",
+                        message: "请输入不大于1000的数",
                         trigger: ["blur"],
                     },
                 ],
@@ -239,8 +204,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore360,
+                        message: "请输入不大于360的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -252,8 +217,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore360,
+                        message: "请输入不大于360的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -265,8 +230,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore360,
+                        message: "请输入不大于360的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -278,8 +243,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore90,
-                        message: "请输入不大于90的非负数",
+                        validator: validateNoMore360,
+                        message: "请输入不大于360的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -331,6 +296,12 @@ export default {
         save(isAdd = false) {
             this.$refs.form.validate((valid) => {
                 if (valid) {
+                    // 字符串有问题
+                    if (+this.form.farDistance <= +this.form.nearDistance) {
+                        this.$message.error("盲区距离应小于探测距离");
+                        return;
+                    }
+
                     // 判断是否新增
                     if (isAdd) {
                         this.form.id = "";
@@ -400,15 +371,30 @@ export default {
             this.isIndeterminate = checkedCount > 0 && checkedCount < this.targetOptions.length;
         },
         inputChange() {
-            if (
-                this.form.farDistance === "" ||
-                this.form.nearDistance === "" ||
-                this.form.fovHLeft === "" ||
-                this.form.fovHRight === "" ||
-                this.form.fovVTop === "" ||
-                this.form.fovVBottom === ""
-            )
-                return;
+            let { farDistance, nearDistance, fovHLeft, fovHRight, fovVTop, fovVBottom } = this.form;
+
+            if (farDistance === "" || nearDistance === "" || fovHLeft === "" || fovHRight === "" || fovVTop === "" || fovVBottom === "") {
+                this.$emit("clearCube");
+                return
+            }
+
+            farDistance = +farDistance;
+            nearDistance = +nearDistance;
+            fovHLeft = +fovHLeft;
+            fovHRight = +fovHRight;
+            fovVTop = +fovVTop;
+            fovVBottom = +fovVBottom;
+
+            if (farDistance <= 0 || farDistance > 1000 || nearDistance < 0 || farDistance <= nearDistance) {
+                this.$emit("clearCube");
+                return
+            }
+
+            if (fovHLeft < 0 || fovHLeft > 360 || fovHRight < 0 || fovHRight > 360 || fovVTop < 0 || fovVTop > 360 || fovVBottom < 0 || fovVBottom > 360) {
+                this.$emit("clearCube");
+                return
+            }
+
             this.$emit("inputChange", {
                 n: +this.form.nearDistance,
                 f: +this.form.farDistance,

+ 2280 - 230
src/views/modelLibrary/components/threeSensorModel.vue

@@ -10,12 +10,24 @@ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
 import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
 import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
 import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";
-// import { mergeVertices } from "three/examples/jsm/utils/BufferGeometryUtils.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+// import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
+import { BufferGeometryUtils } from "three/examples/jsm/utils/BufferGeometryUtils.js";
 import { mapState } from "vuex";
-// import {
-//     showFullScreenLoading,
-//     tryHideFullScreenLoading,
-// } from "../../../axios/filter";
+
+// "three": "^0.142.0"
+const ThreeBSP = require('three-js-csg')(THREE)
+// import ThreeBSP from "../common/ThreeCSG";
+
+// import { ThreeBSP } from "../common/ThreeBSP";
+
+
+// import { ADDITION, SUBTRACTION, INTERSECTION, DIFFERENCE, Brush, Evaluator } from 'three-bvh-csg';
+
+import {
+    showFullScreenLoading,
+    tryHideFullScreenLoading,
+} from "../../../axios/filter";
 
 export default {
     name: "threeSensorModel", // 传感器配置中的threeJS
@@ -29,7 +41,7 @@ export default {
             renderer: null,
             light: null,
             transformControls: null,
-            geometryName: null,
+            geometryName: "cube",
             controls: null,
             mesh: null,
             cube: null,
@@ -46,6 +58,32 @@ export default {
             lidar: null,
             gps: null,
             // scale: 2, // 物体加载换算倍数
+            group1: null,
+            group2: null,
+            r: 15,
+            r1: 6,
+            horizontal: 120,
+            vertical: 90,
+            horizontalL: 100,
+            horizontalR: 60,
+            verticalT: 30,
+            verticalB: 30,
+            a1: null,
+            b1: null,
+            c1: null,
+            d1: null,
+            a2: null,
+            b2: null,
+            c2: null,
+            d2: null,
+            x1: null,
+            y1: null,
+            z1: null,
+            w1: null,
+            x2: null,
+            y2: null,
+            z2: null,
+            w2: null,
         };
     },
 
@@ -60,7 +98,7 @@ export default {
         },
         coordinateA: {
             type: Object,
-            default: { f: 0, n: 0, hl: 0, hr: 0, vl: 0, vb: 0 },
+            default: { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 },
         },
         type: {
             type: Number,
@@ -72,9 +110,10 @@ export default {
         // 场景
         initScene() {
             this.scene = new THREE.Scene();
-            let axes = new THREE.AxesHelper(1500);
+            let axes = new THREE.AxesHelper(12000);
             this.scene.add(axes);
-            const gridHelper = new THREE.GridHelper(1000, 100);
+
+            const gridHelper = new THREE.GridHelper(21000, 2100);
             gridHelper.material.opacity = 0.25;
             gridHelper.material.transparent = true;
             this.scene.add(gridHelper);
@@ -85,7 +124,7 @@ export default {
                 75,
                 this.container.clientWidth / this.container.clientHeight,
                 0.1,
-                1000
+                30000
             );
 
             this.camera.position.set(200, 200, 200);
@@ -132,7 +171,7 @@ export default {
                 this.clearCache(sensor);
             }
 
-            if (this.type === 1) {
+            /* if (this.type === 1) {
                 if (this.cameraSensor) {
                     this.scene.add(this.cameraSensor);
                 } else {
@@ -156,7 +195,7 @@ export default {
                 } else {
                     this.initSensor({ x: 0, y: 0, z: 0 }, "gps");
                 }
-            }
+            } */
 
             if (this.type != 2) {
                 let r = this.coordinate.f;
@@ -190,8 +229,8 @@ export default {
             if (a >= 90) a = 89.99;
             if (b >= 90) b = 89.99;
 
-            x = Math.tan(THREE.MathUtils.degToRad(a)) * r;
-            y = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+            // x = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            // y = Math.tan(THREE.MathUtils.degToRad(b)) * r;
 
             let pointsArr = [];
 
@@ -208,8 +247,8 @@ export default {
                     y1 = 0,
                     z1 = r1;
 
-                x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
-                y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+                // x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                // y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
 
                 pointsArr = [
                     [x1, y1, z1],
@@ -243,10 +282,10 @@ export default {
             if (c >= 90) c = 89.99;
             if (d >= 90) d = 89.99;
 
-            x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r;
-            y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r;
-            x2 = Math.tan(THREE.MathUtils.degToRad(c)) * r;
-            y2 = Math.tan(THREE.MathUtils.degToRad(d)) * r;
+            // x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            // y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+            // x2 = Math.tan(THREE.MathUtils.degToRad(c)) * r;
+            // y2 = Math.tan(THREE.MathUtils.degToRad(d)) * r;
 
             let pointsArr = [];
 
@@ -265,10 +304,10 @@ export default {
                     n2 = 0,
                     z2 = r1;
 
-                m1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
-                n1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
-                m2 = Math.tan(THREE.MathUtils.degToRad(c)) * r1;
-                n2 = Math.tan(THREE.MathUtils.degToRad(d)) * r1;
+                // m1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                // n1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+                // m2 = Math.tan(THREE.MathUtils.degToRad(c)) * r1;
+                // n2 = Math.tan(THREE.MathUtils.degToRad(d)) * r1;
 
                 pointsArr = [
                     [m1, n1, z2],
@@ -284,246 +323,2257 @@ export default {
 
             this.initMesh(pointsArr);
         },
-        initMesh(pointsArr) {
-            let points = pointsArr.map(
-                (d) => new THREE.Vector3(d[0], d[1], d[2])
+        initDemo() {
+            let horizontal = this.horizontal;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
             );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
 
-            let color = null;
-            if (this.type === 1) {
-                color = 0x0000ff;
-            } else if (this.type === 2) {
-                color = 0x008000;
-            } else if (this.type === 3) {
-                color = 0xff4500;
-            } else {
-                color = 0x0000ff;
-            }
 
-            let material = new THREE.MeshPhongMaterial({
-                emissive: color,
-                color: 0x4c4c4c,
-                transparent: true,
-                opacity: 0.3,
-                lightMapIntensity: 0.1,
-                side: THREE.DoubleSide,
-            });
 
-            let tixing = new ConvexGeometry(points);
-            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
-            let tixingMesh = new THREE.Mesh(tixing, material);
-            tixingMesh.name = "cube";
-            this.scene.add(tixingMesh);
-        },
-        // 初始化传感器
-        initSensor(
-            pos = { x: 0, y: 0, z: 0 },
-            type = "camera",
-            canMove = false
-        ) {
-            let Loader = new MTLLoader(); //材质文件加载器
-            let loader = new OBJLoader(); //obj加载器
-            let that = this;
-            let mtlUrl = "";
-            let objUrl = "";
 
-            if (type === "camera") {
-                mtlUrl = `${that.publicPath}sensor/camera/camera.mtl`;
-                objUrl = `${that.publicPath}sensor/camera/camera.obj`;
-            } else if (type === "ogt") {
-                mtlUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.mtl`;
-                objUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.obj`;
-            } else if (type === "lidar") {
-                mtlUrl = `${that.publicPath}sensor/lidar/LIDAR.mtl`;
-                objUrl = `${that.publicPath}sensor/lidar/LIDAR.obj`;
-            } else if (type === "gps") {
-                mtlUrl = `${that.publicPath}sensor/gps/lidar.mtl`;
-                objUrl = `${that.publicPath}sensor/gps/lidar.obj`;
-            }
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(Math.PI * -0.25);
+            // this.scene.add(sphere);
 
-            this.$emit('loadingDone', 'carStart');
 
-            // if (type === "ogt") {
-            //     showFullScreenLoading();
-            // }
 
-            Loader.load(
-                mtlUrl,
-                function (materials) {
-                    loader.setMaterials(materials);
-                    loader.load(objUrl, function (obj) {
-                        // if (type === "ogt") {
-                        //     tryHideFullScreenLoading();
-                        // }
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
 
-                        that.$emit('loadingDone', 'carDone');
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // left
 
-                        if (type === "camera") {
-                            that.cameraSensor = obj;
-                        } else if (type === "ogt") {
-                            that.ogt = obj;
-                        } else if (type === "lidar") {
-                            that.lidar = obj;
-                        } else if (type === "gps") {
-                            that.gps = obj;
-                        }
 
-                        that.cacheList.push(obj);
-                        for (let i = 0; i < obj.children.length; i++) {
-                            if (type === "camera") {
-                                let scale = 0.8 * that.scale;
-                                obj.children[i].scale.set(scale, scale, scale);
-                            } else if (type === "ogt") {
-                                let scale = 0.2 * that.scale;
-                                obj.children[i].scale.set(scale, scale, scale);
-                            } else if (type === "lidar") {
-                                let scale = 0.1 * that.scale;
-                                obj.children[i].scale.set(scale, scale, scale);
-                            } else if (type === "gps") {
-                                let scale = 40 * that.scale;
-                                obj.children[i].scale.set(scale, scale, scale);
-                            }
-                        }
-                        obj.position.set(pos.x, pos.y, pos.z);
-                        obj.name = "sensor";
 
-                        that.sensor = obj;
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
 
-                        that.scene.add(that.sensor); //返回的组对象插入场景中
-                    });
-                },
-                (xhr) => {
-                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
-                },
-                (error) => {
-                    // if (type === "ogt") {
-                    //     tryHideFullScreenLoading();
-                    // }
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
 
-                    that.$emit('loadingDone', 'carDone');
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
 
-                    console.error(error);
-                }
-            );
-        },
-        // 初始化
-        init() {
-            this.initScene();
-            this.initCamera();
-            this.initRenderer();
-            this.initLight();
+            group.translateZ(40);
 
-            this.controls = new OrbitControls(
-                this.camera,
-                this.renderer.domElement
-            ); //创建控件对象
-            this.controls.minDistance = 30;
-            this.controls.maxDistance = 720;
-            this.controls.update();
+            this.scene.add(group);
         },
-        animate() {
-            this.raf = requestAnimationFrame(this.animate);
-            this.renderer.render(this.scene, this.camera);
-            if (this.transformControls) {
-                this.transformControls.update();
-            }
+        initDemo1() {
+            let vertical = this.vertical;
 
-            this.controls.update();
-        },
-        onWindowResize() {
-            this.camera.aspect =
-                this.container.clientWidth / this.container.clientHeight;
-            this.camera.updateProjectionMatrix();
-            this.renderer.setSize(
-                this.container.clientWidth,
-                this.container.clientHeight
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
             );
-        },
-        go() {
-            this.container = document.getElementById(
-                "containerThreeSensorModel"
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
             );
-            this.init();
-            this.animate();
 
-            window.addEventListener("resize", this.onWindowResize);
-        },
-        removeScene() {
-            this.clearScene();
-        },
-        clearCache(item) {
-            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
-            if (item.material && item.material.dispose) item.material.dispose();
-        },
-        clearScene() {
-            this.removeObj(this.scene);
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            // circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // circle2.rotateY(Math.PI * 0.5);
+            circle2.rotateX(-phiStart - Math.PI);
+            // this.scene.add(circle2); // bottom
+
+
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
         },
-        removeObj(obj) {
-            let arr = obj.children.filter((x) => x);
-            arr.forEach((item) => {
-                if (item.children.length) {
-                    this.removeObj(item);
-                } else {
-                    this.clearCache(item);
-                    item.clear();
-                }
-            }),
-                obj.clear();
-            arr = null;
+        initOgtDemo() {
+            let horizontal = this.horizontalR;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+            group.add(sphere0);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
         },
-    },
+        initOgtDemo1() {
+            let vertical = this.verticalB;
 
-    mounted() {
-        this.go();
-    },
-    destroyed() {
-        window.removeEventListener("resize", this.onWindowResize);
-        cancelAnimationFrame(this.raf);
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
 
-        if (this.renderer) {
-            this.renderer.renderLists.dispose();
-            this.renderer.dispose();
-            this.renderer.forceContextLoss();
-            this.renderer.domElement = null;
-            this.renderer.content = null;
-            this.renderer = null;
-        }
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
 
-        if (this.dragControls) {
-            this.dragControls.deactivate();
-            this.dragControls.dispose();
-            this.dragControls = null;
-        }
 
-        if (this.controls) {
-            this.controls.dispose();
-            this.controls = null;
-        }
 
-        if (this.transformControls) {
-            this.transformControls.detach();
-            this.transformControls.dispose();
-            this.transformControls = null;
-        }
 
-        if (this.cacheList.length > 0) {
-            this.scene.remove(...this.cacheList);
-            this.cacheList = [];
-        }
 
-        this.clearScene();
+            let vertical0 = this.verticalT;
 
-        this.scene = null;
-        this.camera = null;
-        this.light = null;
-        this.geometryName = null;
-        this.mesh = null;
-        this.cube = null;
-        this.container = null;
-        this.car = null;
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+            group.add(sphere0);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
+
+        },
+        initLeft() {
+            let horizontal = this.horizontal;
+
+            if (horizontal % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+
+
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(Math.PI * -0.25);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+            /* const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2); */
+
+            // this.scene.add(group);
+
+            /* const geometry6 = new THREE.BoxGeometry(1, 1, 1);
+            const material6 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle6 = new THREE.Mesh(geometry6, material6); */
+
+            // let geometryArray = [geometry, geometry1, geometry2]; // 将你的要合并的多个geometry放入到该数组
+            // let materialArray = [material]; // 将你的要赋值的多个material放入到该数组
+            // 合并模型
+            //  geometry.attributes = {
+            //     faceVertexUvs: geometry.faceVertexUvs,
+            //     faces: geometry.faces,
+            //  }
+            //  geometry.morphAttributes = {
+            //     faceVertexUvs: geometry.faceVertexUvs,
+            //     faces: geometry.faces,
+            //  }
+
+            //  console.log(geometry);
+            // console.log(geometry.attributes);
+            // console.log(geometry instanceof THREE.BufferGeometry);
+            // console.log(geometry.getAttribute());
+            // console.log(material);
+            // console.log(BufferGeometryUtils.mergeBufferGeometries);
+            // console.log(BufferGeometryUtils);
+            //  const mergedGeometries = BufferGeometryUtils.mergeBufferGeometries(geometryArray);
+            // console.log(mergedGeometries);
+            //  const singleMergeMesh = new THREE.Mesh(mergedGeometries, materialArray);
+
+            // this.scene.add(singleMergeMesh);
+
+
+
+            /* const geometry9 = BufferGeometryUtils.mergeBufferGeometries([
+                new THREE.BoxGeometry(2, 1, 1),
+                new THREE.CylinderGeometry(1, 1, 1, 10),
+            ]);
+
+            console.log(geometry);
+            const mergedMesh = new THREE.Mesh(geometry9, material);
+
+            mergedMesh.position.y = -1;
+            // scene.add(car, mergedMesh); */
+
+
+            // sphere.add(circle1);
+            // sphere.add(circle2);
+
+
+            // this.group1 = new ThreeBSP(sphere);
+            // this.group1 = new ThreeBSP(singleMergeMesh);
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+            this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2);
+
+            /*  var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+             // const resultBSP = a.subtract(b);
+             const resultBSP = a.subtract(b).subtract(c).toMesh(m);
+             // this.group1 = new ThreeBSP(sphere);
+             console.log(resultBSP);
+             this.scene.add(resultBSP); */
+
+
+
+            /* const evaluator = new Evaluator();
+            let a = new Brush(geometry, material);
+            a.updateMatrixWorld();
+            let b = new Brush(geometry1, material1);
+            b.rotateZ(Math.PI * -0.5);
+            b.rotateX(phiStart);
+            b.updateMatrixWorld();
+            let c = new Brush(geometry2, material2);
+            c.rotateZ(Math.PI * 0.5);
+            c.rotateX(phiStart);
+            c.updateMatrixWorld();
+
+            let result;
+            result = evaluator.evaluate(a, b, ADDITION);
+            result = evaluator.evaluate(result, c, ADDITION);
+
+            console.log(result); */
+
+            // this.scene.add(result);
+
+
+            // this.group1 = new Brush(geometry, material);
+            // this.group1 = result;
+            // this.group1 = new ThreeBSP(result.geometry);
+        },
+        initTop() {
+            let vertical = this.vertical;
+
+            if (vertical % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(phiStart);
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+            /* const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2); */
+
+            // this.scene.add(group);
+
+
+            /* let geometryArray = [geometry, geometry1, geometry2]; // 将你的要合并的多个geometry放入到该数组
+            let materialArray = [material]; // 将你的要赋值的多个material放入到该数组
+            const mergedGeometries = BufferGeometryUtils.mergeBufferGeometries(geometryArray);
+            const singleMergeMesh = new THREE.Mesh(mergedGeometries, materialArray); */
+
+            // sphere.add(circle1);
+            // sphere.add(circle2);
+
+
+            // this.group2 = new ThreeBSP(singleMergeMesh);
+            // this.group2 = new ThreeBSP(sphere);
+
+            /* this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2); */
+
+
+            this.a2 = new ThreeBSP(sphere);
+            this.b2 = new ThreeBSP(circle1);
+            this.c2 = new ThreeBSP(circle2);
+
+
+
+            /* const evaluator = new Evaluator();
+            let a = new Brush(geometry, material);
+            a.rotateZ(Math.PI * 0.5);
+            a.updateMatrixWorld();
+            let b = new Brush(geometry1, material1);
+            b.rotateX(phiStart);
+            b.updateMatrixWorld();
+            let c = new Brush(geometry2, material2);
+            c.rotateX(-phiStart - Math.PI);
+            c.updateMatrixWorld();
+
+            let result;
+            result = evaluator.evaluate(a, c, SUBTRACTION);
+            result = evaluator.evaluate(result, b, ADDITION);
+
+            this.scene.add(result);
+ */
+
+
+
+            // this.group2 = new Brush(geometry, material);
+            // this.group2 = result;
+            // this.group2 = new ThreeBSP(result.geometry);
+        },
+        initLeft1() {
+            let horizontal = this.horizontal;
+
+            if (horizontal % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r1, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+
+
+
+
+            this.x1 = new ThreeBSP(sphere);
+            this.y1 = new ThreeBSP(circle1);
+            this.z1 = new ThreeBSP(circle2);
+
+        },
+        initTop1() {
+            let vertical = this.vertical;
+
+            if (vertical % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r1, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(phiStart);
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            let p = -phiStart - Math.PI;
+            circle2.rotateX(p);
+
+
+
+
+            this.x2 = new ThreeBSP(sphere);
+            this.y2 = new ThreeBSP(circle1);
+            this.z2 = new ThreeBSP(circle2);
+        },
+        initOgt() {
+            let horizontal = this.horizontalR;
+
+            if ((horizontal + this.horizontalL) % 180 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+
+            this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2);
+            this.d1 = new ThreeBSP(sphere0);
+        },
+        initOgt1() {
+            let vertical = this.verticalB;
+
+            if ((vertical + this.verticalT) % 180 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
+
+
+
+
+
+            let vertical0 = this.verticalT;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+
+            this.a2 = new ThreeBSP(sphere);
+            this.b2 = new ThreeBSP(circle1);
+            this.c2 = new ThreeBSP(circle2);
+            this.d2 = new ThreeBSP(sphere0);
+        },
+        initOgt0() {
+            let horizontal = this.horizontalR;
+
+            if ((horizontal + this.horizontalL) % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+
+            this.x1 = new ThreeBSP(sphere);
+            this.y1 = new ThreeBSP(circle1);
+            this.z1 = new ThreeBSP(circle2);
+            this.w1 = new ThreeBSP(sphere0);
+        },
+        initOgt01() {
+            let vertical = this.verticalB;
+
+            if ((vertical + this.verticalT) % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
+
+
+
+
+
+            let vertical0 = this.verticalT;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+
+            this.x2 = new ThreeBSP(sphere);
+            this.y2 = new ThreeBSP(circle1);
+            this.z2 = new ThreeBSP(circle2);
+            this.w2 = new ThreeBSP(sphere0);
+        },
+        initMesh(pointsArr) {
+            /* let points = pointsArr.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let color = null;
+            if (this.type === 1) {
+                color = 0x0000ff;
+            } else if (this.type === 2) {
+                color = 0x008000;
+            } else if (this.type === 3) {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            let material = new THREE.MeshPhongMaterial({
+                emissive: color,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+                side: THREE.DoubleSide,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            let tixingMesh = new THREE.Mesh(tixing, material);
+            tixingMesh.name = "cube";
+            this.scene.add(tixingMesh); */
+
+            this.$emit('loadingDone', 'carDone');
+
+
+
+            // this.initLeft();
+            // this.initTop();
+
+
+            // this.initLeft1();
+            // this.initTop1();
+
+
+            // this.initDemo();
+            // this.initDemo1();
+
+
+
+            this.initOgtDemo();
+            this.initOgtDemo1();
+
+
+            this.initOgt();
+            this.initOgt1();
+
+            this.initOgt0();
+            this.initOgt01();
+
+            // console.log(this.group1);
+
+
+            // var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+
+            // var bsp = this.group1.subtract(this.group2);
+            // var mesh = bsp.toMesh(m);
+
+            // this.scene.add(mesh);
+
+            // console.log(bsp);
+
+            // this.scene.add(bsp);
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+            /* var m = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });
+            const resultBSP = this.group1.union(this.group2).toMesh(m);
+            console.log(resultBSP);
+            this.scene.add(resultBSP); */
+
+
+
+            var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            // var m = new THREE.MeshBasicMaterial({ color: 0xffaa00, transparent: true, blending: THREE.AdditiveBlending });
+            // var m = new THREE.MeshBasicMaterial( { color: 0xffaa00, wireframe: true } );
+
+
+            // let m = new THREE.MeshPhongMaterial({
+            //     emissive: color,
+            //     color: 0x4c4c4c,
+            //     transparent: true,
+            //     opacity: 0.3,
+            //     lightMapIntensity: 0.1,
+            //     side: THREE.DoubleSide,
+            // });
+
+
+
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).union(this.a2).union(this.b2).union(this.c2).toMesh(m);
+            // const resultBSP = (this.a1.subtract(this.b1).subtract(this.c1)).subtract(this.a2.subtract(this.c2).subtract(this.b2)).toMesh(m);
+
+            // const resultBSP = (this.a2.subtract(this.b2).subtract(this.c2)).subtract(this.a1.subtract(this.b1).subtract(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.subtract(this.b2.subtract(this.c2))
+
+            // let aa = new ThreeBSP(this.a1.subtract(this.b1).subtract(this.c1));
+            // let bb = new ThreeBSP(this.a2.subtract(this.b2).subtract(this.c2));
+
+
+            // m = new THREE.MeshBasicMaterial({
+            /* m = new THREE.MeshPhongMaterial({
+                emissive: 0x0000ff,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+                side: THREE.DoubleSide,
+            }); */
+
+
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+
+
+
+            // 都小于90
+            // const resultBSP = this.a2.intersect(this.c2).subtract(this.b2).intersect(this.a1.subtract(this.b1).subtract(this.c1)).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+            //     .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2)))
+            //     .toMesh(m);
+
+            // 大于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // 小于90
+            // const resultBSP = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+            // 1大于90 2小于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+            // .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+
+            // 小于90
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+            // 大于90
+            // const resultBSP = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // 1小于90 2大于90
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //         .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+
+            // 都大于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //         .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+
+
+
+
+
+
+
+            // 带盲区距离
+            // const resultBSP = 
+            // this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //                     .subtract
+            //                     (this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2))))
+            //                     .toMesh(m);
+
+
+
+
+
+
+
+            // ogt
+            // const resultBSP = this.a1.subtract(this.b1).intersect(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            // resultBSP = resultBSP.subtract(this.a2);
+
+
+            // ogt 带盲区距离
+            // const resultBSP = (this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)))
+            // .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            // .toMesh(m);
+
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+
+            // ogt 相加都小于180 
+            // const resultBSP =
+            //     this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            //         .toMesh(m);
+
+            // ogt 左右相加大于180  上下相加小于180
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+            // const resultBSP =
+            //     this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            //         .toMesh(m);
+
+            // ogt 左右相加小于180  上下相加大于180
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+            //     .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+            //     .toMesh(m);
+
+
+            // ogt 相加都大于180 
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+            //     .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+            //     .toMesh(m);
+
+
+
+
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+
+
+            // left大于90 
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+            // this.a1.subtract(this.c1)  this.d1.subtract(this.b1)
+            // const resultBSP = this.a1.subtract(this.c1).intersect(this.d1.subtract(this.b1))     
+            // const resultBSP = this.d1.subtract(this.b1).intersect(this.a1).intersect(this.c1)
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+            // .intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            // .toMesh(m);
+
+
+
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+
+            // const resultBSP = this.a1.toMesh(m);
+
+            // const resultBSP =
+            //     this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            //         .toMesh(m);
+
+            const resultBSP =
+                this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+                    .toMesh(m);
+
+
+            // console.log(resultBSP);
+            this.scene.add(resultBSP);
+
+
+            /* 
+            ADDITION
+            SUBTRACTION
+            DIFFERENCE
+            INTERSECTION
+            */
+            /* const evaluator = new Evaluator();
+            let result;
+            result = evaluator.evaluate(this.group1, this.group2, INTERSECTION);
+
+            console.log(this.group1); */
+
+            // result.castShadow = true
+            // result.receiveShadow = true;
+            // this.scene.add(result);
+
+
+
+            // console.log(ThreeBSP);
+        },
+        /*
+         * 初始化非ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContent0(coordinate, vh, nf) {
+            let r = 0, deg = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示 
+            // 值为测试出来的预估值 超过的话ThreeBSP会报内存溢出
+            let maxR = 10000;
+
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = ((coordinate.nearDistance * a) / rate).toFixed();
+                } else {
+                    r = maxR - 3.3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovV;
+                if (coordinate.fovH >= 47 && coordinate.fovH <= 53 && coordinate.fovV == 30) {
+                    deg = 31;
+                }
+
+                if (deg == 80) deg = 79;
+                if (deg == 20) deg = 19;
+                if (deg % 10 === 0 && deg % 90 != 0) deg = deg + 0.7;
+            } else {
+                deg = coordinate.fovH;
+                if (deg >= 47 && deg <= 53) deg = 53;
+                if (deg >= 17 && deg <= 23) deg = 17;
+                if (deg >= 117 && deg <= 123) deg = 121;
+                if (deg >= 147 && deg <= 153) deg = 148;
+            }
+
+            // 180°时融合有问题
+            if (deg % 90 === 0) deg = deg - 0.5;
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg * 2 / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                }
+            }
+        },
+        /**
+         * 初始化ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContentOgt(coordinate, vh, nf) {
+            let r = 0, deg = 0, deg1 = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            let maxR = 10000;
+
+            // 10000为预估 超过的话ThreeBSP会报内存溢出
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = (coordinate.nearDistance * a) / rate;
+                } else {
+                    r = maxR - 3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovVBottom;
+                deg1 = coordinate.fovVTop;
+            } else {
+                deg = coordinate.fovHRight;
+                deg1 = coordinate.fovHLeft;
+            }
+
+            // 180°时融合有问题
+            if ((deg + deg1) % 180 === 0) {
+                if (deg > 1) {
+                    deg = deg - 0.1;
+                } else {
+                    deg1 = deg1 - 0.1;
+                }
+            }
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material); // 右 下
+
+
+
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (deg1 / 180),
+                thetaStart0 = 0,
+                thetaLength0 = Math.PI;
+
+            const geometry0 = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0); // 左 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                sphere0.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                    this.w2 = new ThreeBSP(sphere0);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                    this.d2 = new ThreeBSP(sphere0);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                    this.w1 = new ThreeBSP(sphere0);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                    this.d1 = new ThreeBSP(sphere0);
+                }
+            }
+        },
+        initBSP(onlyClear = false) {
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+                this.clearCache(obj);
+            }
+
+            if (!!onlyClear) {
+                return
+            }
+
+            const sensor = this.scene.getObjectByName("sensor");
+            if (sensor) {
+                this.scene.remove(sensor);
+                this.clearCache(sensor);
+            }
+
+            showFullScreenLoading();
+
+            setTimeout(() => {
+                if (this.type === 1) {
+                    if (this.cameraSensor) {
+                        this.scene.add(this.cameraSensor);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "camera");
+                    }
+                } else if (this.type === 2) {
+                    if (this.ogt) {
+                        this.scene.add(this.ogt);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "ogt");
+                    }
+                } else if (this.type === 3) {
+                    if (this.lidar) {
+                        this.scene.add(this.lidar);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "lidar");
+                    }
+                } else if (this.type === 4) {
+                    if (this.gps) {
+                        this.scene.add(this.gps);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "gps");
+                    }
+                }
+            }, 0)
+
+
+            setTimeout(() => {
+                // 解决bsp内存溢出后没法去掉loading
+                let timer = setTimeout(() => {
+                    clearTimeout(timer);
+                    timer = null;
+
+                    tryHideFullScreenLoading();
+                }, 30000)
+
+                this.initSensorBSP();
+
+                clearTimeout(timer);
+                timer = null;
+
+                tryHideFullScreenLoading();
+            }, 300)
+        },
+        initSensorBSP() {
+            // let sensorId = this.configList[type][index].sensorId;
+
+            // if (!this.bsp[type][sensorId]) {
+            // let coordinate = this.allSensor[type].find(
+            //     (i) => i.id === sensorId
+            // );
+
+            // if (!coordinate) {
+            //     coordinate = {
+            //         farDistance: 60,
+            //         nearDistance: 0,
+            //         fovH: 60,
+            //         fovV: 60,
+            //         fovHLeft: 60,
+            //         fovVTop: 60,
+            //         fovHRight: 60,
+            //         fovVBottom: 60,
+            //     };
+            // } { f: 0, n: 0, h: 0, v: 0 }  { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 },
+
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+                this.clearCache(obj);
+            }
+
+
+            let coordinate = {};
+
+            // console.log(this.type);
+            // console.log(this.coordinate);
+            // console.log(this.coordinateA);
+
+            if (this.type != 2) {
+                if (this.coordinate.f <= 0 || this.coordinate.n < 0 || this.coordinate.f <= this.coordinate.n) {
+                    tryHideFullScreenLoading();
+                    return;
+                }
+
+                coordinate = {
+                    farDistance: this.coordinate.f / 100,
+                    nearDistance: this.coordinate.n / 100,
+                    fovH: this.coordinate.h,
+                    fovV: this.coordinate.v,
+                }
+
+
+            } else {
+                if (this.coordinateA.f <= 0 || this.coordinateA.n < 0 || this.coordinateA.f <= this.coordinateA.n) {
+                    tryHideFullScreenLoading();
+                    return;
+                }
+
+                coordinate = {
+                    farDistance: this.coordinateA.f / 100,
+                    nearDistance: this.coordinateA.n / 100,
+                    fovHLeft: this.coordinateA.hl,
+                    fovHRight: this.coordinateA.hr,
+                    fovVTop: this.coordinateA.vt,
+                    fovVBottom: this.coordinateA.vb,
+                }
+            }
+
+            // console.log(coordinate)
+
+            let color = null;
+
+            if (this.type === 1) {
+                color = 0x0000ff;
+            } else if (this.type === 2) {
+                color = 0x008000;
+            } else if (this.type === 3) {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            // let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, wireframe: true, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+            // let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, wireframe: true,  side: THREE.DoubleSide });
+            let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+            // let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c,side: THREE.DoubleSide });
+
+
+            if (this.type != 2) {
+                this.toCube(coordinate, m);
+            } else {
+                this.toCubeOgt(coordinate, m);
+            }
+
+            // this.bsp[type][sensorId] = this.cube;
+            // } else {
+            //     this.cube = this.bsp[type][sensorId];
+            // }
+
+            let a = (100 / (this.scale / 0.6)) * 16.5;
+
+            this.cube.scale.set(a, a, a);
+
+            this.cube.name = this.geometryName || "cube";
+            this.scene.add(this.cube);
+
+            this.$emit('loadingDone', 'carDone');
+        },
+        toCube(coordinate, m) {
+            let { fovH, fovV, nearDistance } = coordinate;
+
+            this.initContent0(coordinate, 'h', 'f');
+            this.initContent0(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContent0(coordinate, 'h', 'n');
+                this.initContent0(coordinate, 'v', 'n');
+
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).subtract(this.x2.subtract(this.y2.subtract(this.z2))).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).subtract(this.x1.subtract(this.y1.intersect(this.z1))).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                        // .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                        //     .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        toCubeOgt(coordinate, m) {
+            let { fovHLeft, fovHRight, fovVTop, fovVBottom, nearDistance } = coordinate;
+
+            this.initContentOgt(coordinate, 'h', 'f');
+            this.initContentOgt(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContentOgt(coordinate, 'h', 'n');
+                this.initContentOgt(coordinate, 'v', 'n');
+
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    // 不带盲区
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        // 初始化传感器
+        initSensor(
+            pos = { x: 0, y: 0, z: 0 },
+            type = "camera",
+            canMove = false
+        ) {
+            let Loader = new MTLLoader(); //材质文件加载器
+            let loader = new OBJLoader(); //obj加载器
+            let that = this;
+            let mtlUrl = "";
+            let objUrl = "";
+
+            if (type === "camera") {
+                mtlUrl = `${that.publicPath}sensor/camera/camera.mtl`;
+                objUrl = `${that.publicPath}sensor/camera/camera.obj`;
+            } else if (type === "ogt") {
+                mtlUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.mtl`;
+                objUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.obj`;
+            } else if (type === "lidar") {
+                mtlUrl = `${that.publicPath}sensor/lidar/LIDAR.mtl`;
+                objUrl = `${that.publicPath}sensor/lidar/LIDAR.obj`;
+            } else if (type === "gps") {
+                mtlUrl = `${that.publicPath}sensor/gps/lidar.mtl`;
+                objUrl = `${that.publicPath}sensor/gps/lidar.obj`;
+            }
+
+            this.$emit('loadingDone', 'carStart');
+
+            if (type === "ogt") {
+                // showFullScreenLoading();
+
+
+                var loader1 = new GLTFLoader(); //创建一个FBX加载器
+
+                loader1.load(
+                    `${that.publicPath}sensor/ogt/ogt.glb`,
+                    function (obj) {
+                        that.$emit('loadingDone', 'carDone');
+
+                        that.ogt = obj.scene;
+
+                        that.cacheList.push(obj.scene);
+
+                        let s = that.scale;
+
+                        let scale = 0.1;
+                        obj.scene.scale.set(scale, scale, scale);
+
+                        obj.scene.position.set(pos.x, pos.y, pos.z);
+                        obj.scene.name = "sensor";
+
+                        that.sensor = obj.scene;
+
+                        that.scene.add(that.sensor); //返回的组对象插入场景中
+                    },
+                    (xhr) => { },
+                    (error) => {
+                        that.$emit('loadingDone', 'carDone');
+
+                        console.error(error);
+                    }
+                );
+
+                return
+            }
+
+            Loader.load(
+                mtlUrl,
+                function (materials) {
+                    loader.setMaterials(materials);
+                    loader.load(objUrl, function (obj) {
+                        // if (type === "ogt") {
+                        //     tryHideFullScreenLoading();
+                        // }
+
+                        that.$emit('loadingDone', 'carDone');
+
+                        if (type === "camera") {
+                            that.cameraSensor = obj;
+                        } else if (type === "ogt") {
+                            that.ogt = obj;
+                        } else if (type === "lidar") {
+                            that.lidar = obj;
+                        } else if (type === "gps") {
+                            that.gps = obj;
+                        }
+
+                        that.cacheList.push(obj);
+                        for (let i = 0; i < obj.children.length; i++) {
+                            if (type === "camera") {
+                                let scale = 0.8 * that.scale;
+                                // obj.children[i].scale.set(scale, scale, scale);
+                                obj.children[i].scale.set(0.4, 0.4, 0.4);
+                            } else if (type === "ogt") {
+                                let scale = 0.2 * that.scale;
+                                obj.children[i].scale.set(scale, scale, scale);
+                            } else if (type === "lidar") {
+                                let scale = 0.03 * that.scale;
+                                // obj.children[i].scale.set(scale, scale, scale);
+                                obj.children[i].scale.set(0.06, 0.06, 0.06);
+                            } else if (type === "gps") {
+                                let scale = 40 * that.scale;
+                                obj.children[i].scale.set(scale, scale, scale);
+                            }
+                        }
+                        obj.position.set(pos.x, pos.y, pos.z);
+                        obj.name = "sensor";
+
+                        that.sensor = obj;
+
+                        that.scene.add(that.sensor); //返回的组对象插入场景中
+                    });
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    // if (type === "ogt") {
+                    //     tryHideFullScreenLoading();
+                    // }
+
+                    that.$emit('loadingDone', 'carDone');
+
+                    console.error(error);
+                }
+            );
+        },
+        // 初始化
+        init() {
+            this.initScene();
+            this.initCamera();
+            this.initRenderer();
+            this.initLight();
+
+            this.controls = new OrbitControls(
+                this.camera,
+                this.renderer.domElement
+            ); //创建控件对象
+            this.controls.minDistance = 30;
+            this.controls.maxDistance = 18000;
+            this.controls.update();
+        },
+        animate() {
+            this.raf = requestAnimationFrame(this.animate);
+            this.renderer.render(this.scene, this.camera);
+            if (this.transformControls) {
+                this.transformControls.update();
+            }
+
+            this.controls.update();
+        },
+        onWindowResize() {
+            this.camera.aspect =
+                this.container.clientWidth / this.container.clientHeight;
+            this.camera.updateProjectionMatrix();
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+        },
+        go() {
+            this.container = document.getElementById(
+                "containerThreeSensorModel"
+            );
+            this.init();
+            this.animate();
+
+            window.addEventListener("resize", this.onWindowResize);
+        },
+        removeScene() {
+            this.clearScene();
+        },
+        clearCache(item) {
+            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
+            if (item.material && item.material.dispose) item.material.dispose();
+        },
+        clearScene() {
+            this.removeObj(this.scene);
+        },
+        removeObj(obj) {
+            let arr = obj.children.filter((x) => x);
+            arr.forEach((item) => {
+                if (item.children.length) {
+                    this.removeObj(item);
+                } else {
+                    this.clearCache(item);
+                    item.clear();
+                }
+            }),
+                obj.clear();
+            arr = null;
+        },
+    },
+
+    mounted() {
+        this.go();
+    },
+    destroyed() {
+        window.removeEventListener("resize", this.onWindowResize);
+        cancelAnimationFrame(this.raf);
+
+        if (this.renderer) {
+            this.renderer.renderLists.dispose();
+            this.renderer.dispose();
+            this.renderer.forceContextLoss();
+            this.renderer.domElement = null;
+            this.renderer.content = null;
+            this.renderer = null;
+        }
+
+        if (this.dragControls) {
+            this.dragControls.deactivate();
+            this.dragControls.dispose();
+            this.dragControls = null;
+        }
+
+        if (this.controls) {
+            this.controls.dispose();
+            this.controls = null;
+        }
+
+        if (this.transformControls) {
+            this.transformControls.detach();
+            this.transformControls.dispose();
+            this.transformControls = null;
+        }
+
+        if (this.cacheList.length > 0) {
+            this.scene.remove(...this.cacheList);
+            this.cacheList = [];
+        }
+
+        this.clearScene();
+
+        this.scene = null;
+        this.camera = null;
+        this.light = null;
+        this.geometryName = null;
+        this.mesh = null;
+        this.cube = null;
+        this.container = null;
+        this.car = null;
         this.cubeTexture = null;
         this.raf = null;
 
+        this.bsp = null;
+        this.a1 = null;
+        this.b1 = null;
+        this.c1 = null;
+        this.d1 = null;
+        this.a2 = null;
+        this.b2 = null;
+        this.c2 = null;
+        this.d2 = null;
+        this.x1 = null;
+        this.y1 = null;
+        this.z1 = null;
+        this.w1 = null;
+        this.x2 = null;
+        this.y2 = null;
+        this.z2 = null;
+        this.w2 = null;
+
         THREE.Cache.clear();
     },
 };

+ 2531 - 0
src/views/modelLibrary/components/threeSensorModelByBSP.vue

@@ -0,0 +1,2531 @@
+<template>
+    <div>
+        <div id="containerThreeSensorModel"></div>
+    </div>
+</template>
+
+<script>
+import * as THREE from "three";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
+import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
+import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+// import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
+import { BufferGeometryUtils } from "three/examples/jsm/utils/BufferGeometryUtils.js";
+import { mapState } from "vuex";
+
+// "three": "^0.142.0"
+const ThreeBSP = require('three-js-csg')(THREE)
+// import ThreeBSP from "../common/ThreeCSG";
+
+// import { ThreeBSP } from "../common/ThreeBSP";
+
+
+// import { ADDITION, SUBTRACTION, INTERSECTION, DIFFERENCE, Brush, Evaluator } from 'three-bvh-csg';
+
+import {
+    showFullScreenLoading,
+    tryHideFullScreenLoading,
+} from "../../../axios/filter";
+
+export default {
+    name: "threeSensorModelByBSP", // 传感器配置中的threeJS
+    components: {},
+
+    data() {
+        return {
+            publicPath: process.env.BASE_URL,
+            scene: null,
+            camera: null,
+            renderer: null,
+            light: null,
+            transformControls: null,
+            geometryName: null,
+            controls: null,
+            mesh: null,
+            cube: null,
+            cacheList: [],
+            container: null,
+            car: null,
+            cubeTexture: null,
+            raf: null,
+            canDrag: true, // 是否可移动
+            dragControls: null,
+            sensor: null, // 当前操作的传感器配置
+            cameraSensor: null,
+            ogt: null,
+            lidar: null,
+            gps: null,
+            // scale: 2, // 物体加载换算倍数
+            group1: null,
+            group2: null,
+            r: 15,
+            r1: 6,
+            horizontal: 90,
+            vertical: 60,
+            horizontalL: 210,
+            horizontalR: 300,
+            verticalT: 60,
+            verticalB: 75,
+            a1: null,
+            b1: null,
+            c1: null,
+            d1: null,
+            a2: null,
+            b2: null,
+            c2: null,
+            d2: null,
+            x1: null,
+            y1: null,
+            z1: null,
+            w1: null,
+            x2: null,
+            y2: null,
+            z2: null,
+            w2: null,
+        };
+    },
+
+    computed: {
+        ...mapState(["scale"]),
+    },
+
+    props: {
+        coordinate: {
+            type: Object,
+            default: { f: 0, n: 0, h: 0, v: 0 },
+        },
+        coordinateA: {
+            type: Object,
+            default: { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 },
+        },
+        type: {
+            type: Number,
+            default: 1,
+        },
+    },
+
+    methods: {
+        // 场景
+        initScene() {
+            this.scene = new THREE.Scene();
+            let axes = new THREE.AxesHelper(12000);
+            this.scene.add(axes);
+
+            const gridHelper = new THREE.GridHelper(21000, 2100);
+            gridHelper.material.opacity = 0.25;
+            gridHelper.material.transparent = true;
+            this.scene.add(gridHelper);
+        },
+        // 相机
+        initCamera() {
+            this.camera = new THREE.PerspectiveCamera(
+                75,
+                this.container.clientWidth / this.container.clientHeight,
+                0.1,
+                30000
+            );
+
+            this.camera.position.set(200, 200, 200);
+            this.scene.add(this.camera);
+        },
+        // 渲染器
+        initRenderer() {
+            this.renderer = new THREE.WebGLRenderer({
+                antialias: true,
+                alpha: true,
+            });
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+            this.renderer.setClearColor("#272727");
+            this.container.appendChild(this.renderer.domElement);
+        },
+        // 初始化灯光
+        initLight() {
+            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
+            hemiLight.position.set(0, 20, 0);
+            this.scene.add(hemiLight);
+
+            // 环境光会均匀的照亮场景中的所有物体
+            const light = new THREE.AmbientLight(0x5c5c5c, 0.4); // soft white light
+            this.scene.add(light);
+
+            // 平行光是沿着特定方向发射的光
+            const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
+            this.scene.add(dirLight);
+        },
+        // 初始化模型
+        initContent() {
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+                this.clearCache(obj);
+            }
+
+            const sensor = this.scene.getObjectByName("sensor");
+            if (sensor) {
+                this.scene.remove(sensor);
+                this.clearCache(sensor);
+            }
+
+            /* if (this.type === 1) {
+                if (this.cameraSensor) {
+                    this.scene.add(this.cameraSensor);
+                } else {
+                    this.initSensor({ x: 0, y: 0, z: 0 }, "camera");
+                }
+            } else if (this.type === 2) {
+                if (this.ogt) {
+                    this.scene.add(this.ogt);
+                } else {
+                    this.initSensor({ x: 0, y: 0, z: 0 }, "ogt");
+                }
+            } else if (this.type === 3) {
+                if (this.lidar) {
+                    this.scene.add(this.lidar);
+                } else {
+                    this.initSensor({ x: 0, y: 0, z: 0 }, "lidar");
+                }
+            } else if (this.type === 4) {
+                if (this.gps) {
+                    this.scene.add(this.gps);
+                } else {
+                    this.initSensor({ x: 0, y: 0, z: 0 }, "gps");
+                }
+            } */
+
+            if (this.type != 2) {
+                let r = this.coordinate.f;
+                let r1 = this.coordinate.n;
+
+                if (r <= 0 || r1 < 0 || r <= r1) {
+                    return;
+                }
+
+                this.initSenA(r * this.scale, r1 * this.scale);
+            } else {
+                let r = this.coordinateA.f;
+                let r1 = this.coordinateA.n;
+
+                if (r <= 0 || r1 < 0 || r <= r1) {
+                    return;
+                }
+
+                this.initSenB(r * this.scale, r1 * this.scale);
+            }
+        },
+        // 处理轴对称物体
+        initSenA(r, r1) {
+            let x = 0,
+                y = 0,
+                z = r;
+
+            let a = this.coordinate.h,
+                b = this.coordinate.v;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+
+            // x = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            // y = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            } else {
+                let x1 = 0,
+                    y1 = 0,
+                    z1 = r1;
+
+                // x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                // y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+
+                pointsArr = [
+                    [x1, y1, z1],
+                    [x1, -y1, z1],
+                    [-x1, -y1, z1],
+                    [-x1, y1, z1],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            }
+
+            this.initMesh(pointsArr);
+        },
+        // 处理非轴对称物体
+        initSenB(r, r1) {
+            let x1 = 0,
+                x2 = 0,
+                y1 = 0,
+                y2 = 0,
+                z1 = r;
+
+            let a = this.coordinateA.hl,
+                b = this.coordinateA.vt,
+                c = this.coordinateA.hr,
+                d = this.coordinateA.vb;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+            if (c >= 90) c = 89.99;
+            if (d >= 90) d = 89.99;
+
+            // x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            // y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+            // x2 = Math.tan(THREE.MathUtils.degToRad(c)) * r;
+            // y2 = Math.tan(THREE.MathUtils.degToRad(d)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            } else {
+                let m1 = 0,
+                    m2 = 0,
+                    n1 = 0,
+                    n2 = 0,
+                    z2 = r1;
+
+                // m1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                // n1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+                // m2 = Math.tan(THREE.MathUtils.degToRad(c)) * r1;
+                // n2 = Math.tan(THREE.MathUtils.degToRad(d)) * r1;
+
+                pointsArr = [
+                    [m1, n1, z2],
+                    [m1, -n2, z2],
+                    [-m2, -n2, z2],
+                    [-m2, n1, z2],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            }
+
+            this.initMesh(pointsArr);
+        },
+        initDemo() {
+            let horizontal = this.horizontal;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+
+
+
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(Math.PI * -0.25);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
+        },
+        initDemo1() {
+            let vertical = this.vertical;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            // circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // circle2.rotateY(Math.PI * 0.5);
+            circle2.rotateX(-phiStart - Math.PI);
+            // this.scene.add(circle2); // bottom
+
+
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
+        },
+        initOgtDemo() {
+            let horizontal = this.horizontalR;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+            group.add(sphere0);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
+        },
+        initOgtDemo1() {
+            let vertical = this.verticalB;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
+
+
+
+
+
+            let vertical0 = this.verticalT;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+            const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2);
+            group.add(sphere0);
+
+            group.translateZ(40);
+
+            this.scene.add(group);
+
+        },
+        initLeft() {
+            let horizontal = this.horizontal;
+
+            if (horizontal % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+
+
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(Math.PI * -0.25);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+            /* const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2); */
+
+            // this.scene.add(group);
+
+            /* const geometry6 = new THREE.BoxGeometry(1, 1, 1);
+            const material6 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle6 = new THREE.Mesh(geometry6, material6); */
+
+            // let geometryArray = [geometry, geometry1, geometry2]; // 将你的要合并的多个geometry放入到该数组
+            // let materialArray = [material]; // 将你的要赋值的多个material放入到该数组
+            // 合并模型
+            //  geometry.attributes = {
+            //     faceVertexUvs: geometry.faceVertexUvs,
+            //     faces: geometry.faces,
+            //  }
+            //  geometry.morphAttributes = {
+            //     faceVertexUvs: geometry.faceVertexUvs,
+            //     faces: geometry.faces,
+            //  }
+
+            //  console.log(geometry);
+            // console.log(geometry.attributes);
+            // console.log(geometry instanceof THREE.BufferGeometry);
+            // console.log(geometry.getAttribute());
+            // console.log(material);
+            // console.log(BufferGeometryUtils.mergeBufferGeometries);
+            // console.log(BufferGeometryUtils);
+            //  const mergedGeometries = BufferGeometryUtils.mergeBufferGeometries(geometryArray);
+            // console.log(mergedGeometries);
+            //  const singleMergeMesh = new THREE.Mesh(mergedGeometries, materialArray);
+
+            // this.scene.add(singleMergeMesh);
+
+
+
+            /* const geometry9 = BufferGeometryUtils.mergeBufferGeometries([
+                new THREE.BoxGeometry(2, 1, 1),
+                new THREE.CylinderGeometry(1, 1, 1, 10),
+            ]);
+
+            console.log(geometry);
+            const mergedMesh = new THREE.Mesh(geometry9, material);
+
+            mergedMesh.position.y = -1;
+            // scene.add(car, mergedMesh); */
+
+
+            // sphere.add(circle1);
+            // sphere.add(circle2);
+
+
+            // this.group1 = new ThreeBSP(sphere);
+            // this.group1 = new ThreeBSP(singleMergeMesh);
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+            this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2);
+
+            /*  var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+             // const resultBSP = a.subtract(b);
+             const resultBSP = a.subtract(b).subtract(c).toMesh(m);
+             // this.group1 = new ThreeBSP(sphere);
+             console.log(resultBSP);
+             this.scene.add(resultBSP); */
+
+
+
+            /* const evaluator = new Evaluator();
+            let a = new Brush(geometry, material);
+            a.updateMatrixWorld();
+            let b = new Brush(geometry1, material1);
+            b.rotateZ(Math.PI * -0.5);
+            b.rotateX(phiStart);
+            b.updateMatrixWorld();
+            let c = new Brush(geometry2, material2);
+            c.rotateZ(Math.PI * 0.5);
+            c.rotateX(phiStart);
+            c.updateMatrixWorld();
+
+            let result;
+            result = evaluator.evaluate(a, b, ADDITION);
+            result = evaluator.evaluate(result, c, ADDITION);
+
+            console.log(result); */
+
+            // this.scene.add(result);
+
+
+            // this.group1 = new Brush(geometry, material);
+            // this.group1 = result;
+            // this.group1 = new ThreeBSP(result.geometry);
+        },
+        initTop() {
+            let vertical = this.vertical;
+
+            if (vertical % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(phiStart);
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+            /* const group = new THREE.Group();
+            group.add(sphere);
+            group.add(circle1);
+            group.add(circle2); */
+
+            // this.scene.add(group);
+
+
+            /* let geometryArray = [geometry, geometry1, geometry2]; // 将你的要合并的多个geometry放入到该数组
+            let materialArray = [material]; // 将你的要赋值的多个material放入到该数组
+            const mergedGeometries = BufferGeometryUtils.mergeBufferGeometries(geometryArray);
+            const singleMergeMesh = new THREE.Mesh(mergedGeometries, materialArray); */
+
+            // sphere.add(circle1);
+            // sphere.add(circle2);
+
+
+            // this.group2 = new ThreeBSP(singleMergeMesh);
+            // this.group2 = new ThreeBSP(sphere);
+
+            /* this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2); */
+
+
+            this.a2 = new ThreeBSP(sphere);
+            this.b2 = new ThreeBSP(circle1);
+            this.c2 = new ThreeBSP(circle2);
+
+
+
+            /* const evaluator = new Evaluator();
+            let a = new Brush(geometry, material);
+            a.rotateZ(Math.PI * 0.5);
+            a.updateMatrixWorld();
+            let b = new Brush(geometry1, material1);
+            b.rotateX(phiStart);
+            b.updateMatrixWorld();
+            let c = new Brush(geometry2, material2);
+            c.rotateX(-phiStart - Math.PI);
+            c.updateMatrixWorld();
+
+            let result;
+            result = evaluator.evaluate(a, c, SUBTRACTION);
+            result = evaluator.evaluate(result, b, ADDITION);
+
+            this.scene.add(result);
+ */
+
+
+
+            // this.group2 = new Brush(geometry, material);
+            // this.group2 = result;
+            // this.group2 = new ThreeBSP(result.geometry);
+        },
+        initLeft1() {
+            let horizontal = this.horizontal;
+
+            if (horizontal % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r1, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(phiStart);
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+
+
+
+
+            this.x1 = new ThreeBSP(sphere);
+            this.y1 = new ThreeBSP(circle1);
+            this.z1 = new ThreeBSP(circle2);
+
+        },
+        initTop1() {
+            let vertical = this.vertical;
+
+            if (vertical % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical * 2 / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                this.r1, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(phiStart);
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                this.r1, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            let p = -phiStart - Math.PI;
+            circle2.rotateX(p);
+
+
+
+
+            this.x2 = new ThreeBSP(sphere);
+            this.y2 = new ThreeBSP(circle1);
+            this.z2 = new ThreeBSP(circle2);
+        },
+        initOgt() {
+            let horizontal = this.horizontalR;
+
+            if ((horizontal + this.horizontalL) % 180 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+
+            this.a1 = new ThreeBSP(sphere);
+            this.b1 = new ThreeBSP(circle1);
+            this.c1 = new ThreeBSP(circle2);
+            this.d1 = new ThreeBSP(sphere0);
+        },
+        initOgt1() {
+            let vertical = this.verticalB;
+
+            if ((vertical + this.verticalT) % 180 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
+
+
+
+
+
+            let vertical0 = this.verticalT;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                15, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                15, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+
+            this.a2 = new ThreeBSP(sphere);
+            this.b2 = new ThreeBSP(circle1);
+            this.c2 = new ThreeBSP(circle2);
+            this.d2 = new ThreeBSP(sphere0);
+        },
+        initOgt0() {
+            let horizontal = this.horizontalR;
+
+            if ((horizontal + this.horizontalL) % 90 === 0) horizontal = horizontal - 0.1;
+
+            let phiStart = Math.PI * ((90 - horizontal) / 180),
+                phiLength = Math.PI * (horizontal / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere); // 右
+
+
+
+
+
+            let horizontal0 = this.horizontalL;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (horizontal0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            // phiStart = 0;
+
+            const geometry0 = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            // sphere.rotateY(phiStart);
+            // this.scene.add(sphere0); // 左
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateZ(Math.PI * -0.5);
+            circle1.rotateX(Math.PI * ((90 - horizontal0) / 180));
+            // this.scene.add(circle1); // left
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            circle2.rotateZ(Math.PI * 0.5);
+            circle2.rotateX(phiStart);
+            // this.scene.add(circle2); // right
+
+
+
+            this.x1 = new ThreeBSP(sphere);
+            this.y1 = new ThreeBSP(circle1);
+            this.z1 = new ThreeBSP(circle2);
+            this.w1 = new ThreeBSP(sphere0);
+        },
+        initOgt01() {
+            let vertical = this.verticalB;
+
+            if ((vertical + this.verticalT) % 90 === 0) vertical = vertical - 0.1;
+
+            let phiStart = Math.PI * ((90 - vertical) / 180),
+                phiLength = Math.PI * (vertical / 180),
+                thetaStart = Math.PI * 0,
+                thetaLength = Math.PI * 1;
+
+            const geometry = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+            sphere.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere); // 下
+
+
+
+
+
+            let vertical0 = this.verticalT;
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (vertical0 / 180),
+                thetaStart0 = Math.PI * 0,
+                thetaLength0 = Math.PI * 1;
+
+            const geometry0 = new THREE.SphereGeometry(
+                6, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const sphere0 = new THREE.Mesh(geometry0, material0);
+            sphere0.rotateZ(Math.PI * 0.5);
+            // this.scene.add(sphere0); // 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1);
+            circle1.rotateX(Math.PI * ((90 - vertical0) / 180));
+            // circle1.rotateZ(0);
+            // this.scene.add(circle1); // top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                6, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2);
+            // let p = -phiStart - Math.PI;
+            circle2.rotateX(-phiStart - Math.PI);
+            // circle2.rotateZ(0);
+            // this.scene.add(circle2); // bottom
+
+
+
+
+            this.x2 = new ThreeBSP(sphere);
+            this.y2 = new ThreeBSP(circle1);
+            this.z2 = new ThreeBSP(circle2);
+            this.w2 = new ThreeBSP(sphere0);
+        },
+        initMesh(pointsArr) {
+            /* let points = pointsArr.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let color = null;
+            if (this.type === 1) {
+                color = 0x0000ff;
+            } else if (this.type === 2) {
+                color = 0x008000;
+            } else if (this.type === 3) {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            let material = new THREE.MeshPhongMaterial({
+                emissive: color,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+                side: THREE.DoubleSide,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            let tixingMesh = new THREE.Mesh(tixing, material);
+            tixingMesh.name = "cube";
+            this.scene.add(tixingMesh); */
+
+            this.$emit('loadingDone', 'carDone');
+
+
+
+            this.initLeft();
+            this.initTop();
+
+
+            this.initLeft1();
+            this.initTop1();
+
+
+            this.initDemo();
+            this.initDemo1();
+
+
+
+            // this.initOgtDemo();
+            // this.initOgtDemo1();
+
+
+            // this.initOgt();
+            // this.initOgt1();
+
+            // this.initOgt0();
+            // this.initOgt01();
+
+            // console.log(this.group1);
+
+
+            // var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+
+            // var bsp = this.group1.subtract(this.group2);
+            // var mesh = bsp.toMesh(m);
+
+            // this.scene.add(mesh);
+
+            // console.log(bsp);
+
+            // this.scene.add(bsp);
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+            /* var m = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });
+            const resultBSP = this.group1.union(this.group2).toMesh(m);
+            console.log(resultBSP);
+            this.scene.add(resultBSP); */
+
+
+
+            var m = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            // var m = new THREE.MeshBasicMaterial({ color: 0xffaa00, transparent: true, blending: THREE.AdditiveBlending });
+            // var m = new THREE.MeshBasicMaterial( { color: 0xffaa00, wireframe: true } );
+
+
+            // let m = new THREE.MeshPhongMaterial({
+            //     emissive: color,
+            //     color: 0x4c4c4c,
+            //     transparent: true,
+            //     opacity: 0.3,
+            //     lightMapIntensity: 0.1,
+            //     side: THREE.DoubleSide,
+            // });
+
+
+
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).union(this.a2).union(this.b2).union(this.c2).toMesh(m);
+            // const resultBSP = (this.a1.subtract(this.b1).subtract(this.c1)).subtract(this.a2.subtract(this.c2).subtract(this.b2)).toMesh(m);
+
+            // const resultBSP = (this.a2.subtract(this.b2).subtract(this.c2)).subtract(this.a1.subtract(this.b1).subtract(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.subtract(this.b2.subtract(this.c2))
+
+            // let aa = new ThreeBSP(this.a1.subtract(this.b1).subtract(this.c1));
+            // let bb = new ThreeBSP(this.a2.subtract(this.b2).subtract(this.c2));
+
+
+            // m = new THREE.MeshBasicMaterial({
+            /* m = new THREE.MeshPhongMaterial({
+                emissive: 0x0000ff,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+                side: THREE.DoubleSide,
+            }); */
+
+
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+
+
+
+            // 都小于90
+            // const resultBSP = this.a2.intersect(this.c2).subtract(this.b2).intersect(this.a1.subtract(this.b1).subtract(this.c1)).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2)))
+                .toMesh(m);
+
+            // 大于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // 小于90
+            // const resultBSP = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+            // 1大于90 2小于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+
+            // 小于90
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+            // 大于90
+            // const resultBSP = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // 1小于90 2大于90
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //         .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+
+            // 都大于90
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+            // const resultBSP = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //         .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+
+
+
+
+
+
+
+            // 带盲区距离
+            // const resultBSP = 
+            // this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+            //                     .subtract
+            //                     (this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2))))
+            //                     .toMesh(m);
+
+
+
+
+
+
+
+            // ogt
+            // const resultBSP = this.a1.subtract(this.b1).intersect(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+            // resultBSP = resultBSP.subtract(this.a2);
+
+
+            // ogt 带盲区距离
+            // const resultBSP = (this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)))
+            // .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            // .toMesh(m);
+
+
+
+
+            // intersect (交集) union (并集) subtract (差集)
+
+
+            // ogt 相加都小于180 
+            // const resultBSP =
+            //     this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            //         .toMesh(m);
+
+            // ogt 左右相加大于180  上下相加小于180
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+            // const resultBSP =
+            //     this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            //         .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+            //         .toMesh(m);
+
+            // ogt 左右相加小于180  上下相加大于180
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+            //     .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+            //     .toMesh(m);
+
+
+            // ogt 相加都大于180 
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+            //     .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+            //     .toMesh(m);
+
+
+
+
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+
+
+            // left大于90 
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+            // this.a1.subtract(this.c1)  this.d1.subtract(this.b1)
+            // const resultBSP = this.a1.subtract(this.c1).intersect(this.d1.subtract(this.b1))     
+            // const resultBSP = this.d1.subtract(this.b1).intersect(this.a1).intersect(this.c1)
+            // const resultBSP = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+            // .intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+            // .toMesh(m);
+
+
+
+            // const resultBSP = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+
+            // const resultBSP = this.a1.toMesh(m);
+
+            // const resultBSP = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+            //     .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2)))
+            //     .toMesh(m);
+
+            // const resultBSP = this.a1.subtract(this.c1).toMesh(m);
+
+
+            // console.log(resultBSP);
+            this.scene.add(resultBSP);
+
+
+            /* 
+            ADDITION
+            SUBTRACTION
+            DIFFERENCE
+            INTERSECTION
+            */
+            /* const evaluator = new Evaluator();
+            let result;
+            result = evaluator.evaluate(this.group1, this.group2, INTERSECTION);
+
+            console.log(this.group1); */
+
+            // result.castShadow = true
+            // result.receiveShadow = true;
+            // this.scene.add(result);
+
+
+
+            // console.log(ThreeBSP);
+        },
+        /*
+         * 初始化非ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContent0(coordinate, vh, nf) {
+            let r = 0, deg = 0, rate = 1;
+            let a = 100 / (this.scale / 0.6);
+
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示 
+            // 值为测试出来的预估值 超过的话ThreeBSP会报内存溢出
+            let maxR = 10000;
+
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = ((coordinate.nearDistance * a) / rate).toFixed();
+                } else {
+                    r = maxR - 3.3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovV;
+            } else {
+                deg = coordinate.fovH;
+            }
+
+            if (deg ===50) deg = deg +1;
+
+            // 180°时融合有问题
+            if (deg % 90 === 0) deg = deg - 0.5;
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg * 2 / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                }
+            }
+        },
+        /**
+         * 初始化ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContentOgt(coordinate, vh, nf) {
+            let r = 0, deg = 0, deg1 = 0, rate = 1;
+            let a = 100 / (this.scale / 0.6);
+
+            let maxR = 10000;
+
+            // 10000为预估 超过的话ThreeBSP会报内存溢出
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = (coordinate.nearDistance * a) / rate;
+                } else {
+                    r = maxR - 3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovVBottom;
+                deg1 = coordinate.fovVTop;
+            } else {
+                deg = coordinate.fovHRight;
+                deg1 = coordinate.fovHLeft;
+            }
+
+            // 180°时融合有问题
+            if ((deg + deg1) % 180 === 0) {
+                if (deg > 1) {
+                    deg = deg - 0.1;
+                } else {
+                    deg1 = deg1 - 0.1;
+                }
+            }
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material); // 右 下
+
+
+
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (deg1 / 180),
+                thetaStart0 = 0,
+                thetaLength0 = Math.PI;
+
+            const geometry0 = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0); // 左 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                sphere0.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                    this.w2 = new ThreeBSP(sphere0);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                    this.d2 = new ThreeBSP(sphere0);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                    this.w1 = new ThreeBSP(sphere0);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                    this.d1 = new ThreeBSP(sphere0);
+                }
+            }
+        },
+        initBSP() {
+            showFullScreenLoading();
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+                this.clearCache(obj);
+            }
+
+            const sensor = this.scene.getObjectByName("sensor");
+            if (sensor) {
+                this.scene.remove(sensor);
+                this.clearCache(sensor);
+            }
+
+            setTimeout(() => {
+                if (this.type === 1) {
+                    if (this.cameraSensor) {
+                        this.scene.add(this.cameraSensor);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "camera");
+                    }
+                } else if (this.type === 2) {
+                    if (this.ogt) {
+                        this.scene.add(this.ogt);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "ogt");
+                    }
+                } else if (this.type === 3) {
+                    if (this.lidar) {
+                        this.scene.add(this.lidar);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "lidar");
+                    }
+                } else if (this.type === 4) {
+                    if (this.gps) {
+                        this.scene.add(this.gps);
+                    } else {
+                        this.initSensor({ x: 0, y: 0, z: 0 }, "gps");
+                    }
+                }
+            }, 0)
+
+
+            setTimeout(() => {
+                // 解决bsp内存溢出后没法去掉loading
+                let timer = setTimeout(() => {
+                    clearTimeout(timer);
+                    timer = null;
+
+                    tryHideFullScreenLoading();
+                }, 10000)
+
+                this.initSensorBSP();
+
+                clearTimeout(timer);
+                timer = null;
+
+                tryHideFullScreenLoading();
+            }, 300)
+        },
+        initSensorBSP() {
+            // let sensorId = this.configList[type][index].sensorId;
+
+            // if (!this.bsp[type][sensorId]) {
+            // let coordinate = this.allSensor[type].find(
+            //     (i) => i.id === sensorId
+            // );
+
+            // if (!coordinate) {
+            //     coordinate = {
+            //         farDistance: 60,
+            //         nearDistance: 0,
+            //         fovH: 60,
+            //         fovV: 60,
+            //         fovHLeft: 60,
+            //         fovVTop: 60,
+            //         fovHRight: 60,
+            //         fovVBottom: 60,
+            //     };
+            // } { f: 0, n: 0, h: 0, v: 0 }  { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 },
+
+
+
+
+            let coordinate = {};
+
+            // console.log(this.type);
+            // console.log(this.coordinate);
+            // console.log(this.coordinateA);
+
+            if (this.type != 2) {
+                if (this.coordinate.f <= 0 || this.coordinate.f <= this.coordinate.n) {
+                    tryHideFullScreenLoading();
+                    return;
+                }
+
+                coordinate = {
+                    farDistance: this.coordinate.f,
+                    nearDistance: this.coordinate.n,
+                    fovH: this.coordinate.h,
+                    fovV: this.coordinate.v,
+                }
+            } else {
+                if (this.coordinateA.f <= 0 || this.coordinateA.f <= this.coordinateA.n) {
+                    tryHideFullScreenLoading();
+                    return;
+                }
+
+                coordinate = {
+                    farDistance: this.coordinateA.f,
+                    nearDistance: this.coordinateA.n,
+                    fovHLeft: this.coordinateA.hl,
+                    fovHRight: this.coordinateA.hr,
+                    fovVTop: this.coordinateA.vt,
+                    fovVBottom: this.coordinateA.vb,
+                }
+            }
+
+            console.log(coordinate)
+
+            let color = null;
+
+            if (this.type === 1) {
+                color = 0x0000ff;
+            } else if (this.type === 2) {
+                color = 0x008000;
+            } else if (this.type === 3) {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, wireframe: true, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+
+
+            if (this.type != 2) {
+                this.toCube(coordinate, m);
+            } else {
+                this.toCubeOgt(coordinate, m);
+            }
+
+            // this.bsp[type][sensorId] = this.cube;
+            // } else {
+            //     this.cube = this.bsp[type][sensorId];
+            // }
+
+            this.cube.name = this.geometryName || "cube";
+            this.scene.add(this.cube);
+
+            this.$emit('loadingDone', 'carDone');
+        },
+        toCube(coordinate, m) {
+            let { fovH, fovV, nearDistance } = coordinate;
+
+            this.initContent0(coordinate, 'h', 'f');
+            this.initContent0(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContent0(coordinate, 'h', 'n');
+                this.initContent0(coordinate, 'v', 'n');
+
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).subtract(this.x2.subtract(this.y2.subtract(this.z2))).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).subtract(this.x1.subtract(this.y1.intersect(this.z1))).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        toCubeOgt(coordinate, m) {
+            let { fovHLeft, fovHRight, fovVTop, fovVBottom, nearDistance } = coordinate;
+
+            this.initContentOgt(coordinate, 'h', 'f');
+            this.initContentOgt(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContentOgt(coordinate, 'h', 'n');
+                this.initContentOgt(coordinate, 'v', 'n');
+
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    // 不带盲区
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        // 初始化传感器
+        initSensor(
+            pos = { x: 0, y: 0, z: 0 },
+            type = "camera",
+            canMove = false
+        ) {
+            let Loader = new MTLLoader(); //材质文件加载器
+            let loader = new OBJLoader(); //obj加载器
+            let that = this;
+            let mtlUrl = "";
+            let objUrl = "";
+
+            if (type === "camera") {
+                mtlUrl = `${that.publicPath}sensor/camera/camera.mtl`;
+                objUrl = `${that.publicPath}sensor/camera/camera.obj`;
+            } else if (type === "ogt") {
+                mtlUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.mtl`;
+                objUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.obj`;
+            } else if (type === "lidar") {
+                mtlUrl = `${that.publicPath}sensor/lidar/LIDAR.mtl`;
+                objUrl = `${that.publicPath}sensor/lidar/LIDAR.obj`;
+            } else if (type === "gps") {
+                mtlUrl = `${that.publicPath}sensor/gps/lidar.mtl`;
+                objUrl = `${that.publicPath}sensor/gps/lidar.obj`;
+            }
+
+            this.$emit('loadingDone', 'carStart');
+
+            if (type === "ogt") {
+                // showFullScreenLoading();
+
+
+                var loader1 = new GLTFLoader(); //创建一个FBX加载器
+
+                loader1.load(
+                    `${that.publicPath}sensor/ogt/ogt.glb`,
+                    function (obj) {
+                        that.$emit('loadingDone', 'carDone');
+
+                        that.ogt = obj.scene;
+
+                        that.cacheList.push(obj.scene);
+
+                        let s = that.scale;
+
+                        let scale = 0.2;
+                        obj.scene.scale.set(scale, scale, scale);
+
+                        obj.scene.position.set(pos.x, pos.y, pos.z);
+                        obj.scene.name = "sensor";
+
+                        that.sensor = obj.scene;
+
+                        that.scene.add(that.sensor); //返回的组对象插入场景中
+                    },
+                    (xhr) => { },
+                    (error) => {
+                        that.$emit('loadingDone', 'carDone');
+
+                        console.error(error);
+                    }
+                );
+
+                return
+            }
+
+            Loader.load(
+                mtlUrl,
+                function (materials) {
+                    loader.setMaterials(materials);
+                    loader.load(objUrl, function (obj) {
+                        // if (type === "ogt") {
+                        //     tryHideFullScreenLoading();
+                        // }
+
+                        that.$emit('loadingDone', 'carDone');
+
+                        if (type === "camera") {
+                            that.cameraSensor = obj;
+                        } else if (type === "ogt") {
+                            that.ogt = obj;
+                        } else if (type === "lidar") {
+                            that.lidar = obj;
+                        } else if (type === "gps") {
+                            that.gps = obj;
+                        }
+
+                        that.cacheList.push(obj);
+                        for (let i = 0; i < obj.children.length; i++) {
+                            if (type === "camera") {
+                                let scale = 0.8 * that.scale;
+                                // obj.children[i].scale.set(scale, scale, scale);
+                                obj.children[i].scale.set(0.4, 0.4, 0.4);
+                            } else if (type === "ogt") {
+                                let scale = 0.2 * that.scale;
+                                obj.children[i].scale.set(scale, scale, scale);
+                            } else if (type === "lidar") {
+                                let scale = 0.1 * that.scale;
+                                obj.children[i].scale.set(scale, scale, scale);
+                            } else if (type === "gps") {
+                                let scale = 40 * that.scale;
+                                obj.children[i].scale.set(scale, scale, scale);
+                            }
+                        }
+                        obj.position.set(pos.x, pos.y, pos.z);
+                        obj.name = "sensor";
+
+                        that.sensor = obj;
+
+                        that.scene.add(that.sensor); //返回的组对象插入场景中
+                    });
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    // if (type === "ogt") {
+                    //     tryHideFullScreenLoading();
+                    // }
+
+                    that.$emit('loadingDone', 'carDone');
+
+                    console.error(error);
+                }
+            );
+        },
+        // 初始化
+        init() {
+            this.initScene();
+            this.initCamera();
+            this.initRenderer();
+            this.initLight();
+
+            this.controls = new OrbitControls(
+                this.camera,
+                this.renderer.domElement
+            ); //创建控件对象
+            this.controls.minDistance = 30;
+            this.controls.maxDistance = 18000;
+            this.controls.update();
+        },
+        animate() {
+            this.raf = requestAnimationFrame(this.animate);
+            this.renderer.render(this.scene, this.camera);
+            if (this.transformControls) {
+                this.transformControls.update();
+            }
+
+            this.controls.update();
+        },
+        onWindowResize() {
+            this.camera.aspect =
+                this.container.clientWidth / this.container.clientHeight;
+            this.camera.updateProjectionMatrix();
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+        },
+        go() {
+            this.container = document.getElementById(
+                "containerThreeSensorModel"
+            );
+            this.init();
+            this.animate();
+
+            window.addEventListener("resize", this.onWindowResize);
+        },
+        removeScene() {
+            this.clearScene();
+        },
+        clearCache(item) {
+            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
+            if (item.material && item.material.dispose) item.material.dispose();
+        },
+        clearScene() {
+            this.removeObj(this.scene);
+        },
+        removeObj(obj) {
+            let arr = obj.children.filter((x) => x);
+            arr.forEach((item) => {
+                if (item.children.length) {
+                    this.removeObj(item);
+                } else {
+                    this.clearCache(item);
+                    item.clear();
+                }
+            }),
+                obj.clear();
+            arr = null;
+        },
+    },
+
+    mounted() {
+        this.go();
+    },
+    destroyed() {
+        window.removeEventListener("resize", this.onWindowResize);
+        cancelAnimationFrame(this.raf);
+
+        if (this.renderer) {
+            this.renderer.renderLists.dispose();
+            this.renderer.dispose();
+            this.renderer.forceContextLoss();
+            this.renderer.domElement = null;
+            this.renderer.content = null;
+            this.renderer = null;
+        }
+
+        if (this.dragControls) {
+            this.dragControls.deactivate();
+            this.dragControls.dispose();
+            this.dragControls = null;
+        }
+
+        if (this.controls) {
+            this.controls.dispose();
+            this.controls = null;
+        }
+
+        if (this.transformControls) {
+            this.transformControls.detach();
+            this.transformControls.dispose();
+            this.transformControls = null;
+        }
+
+        if (this.cacheList.length > 0) {
+            this.scene.remove(...this.cacheList);
+            this.cacheList = [];
+        }
+
+        this.clearScene();
+
+        this.scene = null;
+        this.camera = null;
+        this.light = null;
+        this.geometryName = null;
+        this.mesh = null;
+        this.cube = null;
+        this.container = null;
+        this.car = null;
+        this.cubeTexture = null;
+        this.raf = null;
+
+        THREE.Cache.clear();
+    },
+};
+</script>
+
+<style lang="less" scoped>
+#containerThreeSensorModel {
+    width: 100%;
+    height: calc(100vh - 180px);
+}
+</style>

+ 823 - 54
src/views/modelLibrary/components/threeVehicleConfiguration.vue

@@ -21,12 +21,13 @@ import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
 // import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
 // import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
 // import { Line2 } from "three/examples/jsm/lines/Line2.js";
-// import {
-//     showFullScreenLoading,
-//     tryHideFullScreenLoading,
-// } from "../../../axios/filter";
+import {
+    showFullScreenLoading,
+    tryHideFullScreenLoading,
+} from "../../../axios/filter";
 import { mapState } from "vuex";
 import CoordinateAxes from "../common/coordinateAxes";
+const ThreeBSP = require('three-js-csg')(THREE)
 
 export default {
     name: "threeVehicleConfiguration", // 车辆配置详情中的threeJS
@@ -60,8 +61,32 @@ export default {
             ogt: null,
             // scale: 2, // 物体加载换算倍数
             // rate: 20, // 坐标换算倍数
-            isLoading: false, // 判断是否正在载车辆
+            isLoading: false, // 判断是否正在载车辆
             lastCarModel: "", // 记录上一次加载的carModel
+            // 用于存储已经被bsp加载过的传感器
+            bsp: {
+                camera: {},
+                ogt: {},
+                lidar: {},
+            },
+            ogtObj: null,
+            loadingFull: false,
+            a1: null,
+            b1: null,
+            c1: null,
+            d1: null,
+            a2: null,
+            b2: null,
+            c2: null,
+            d2: null,
+            x1: null,
+            y1: null,
+            z1: null,
+            w1: null,
+            x2: null,
+            y2: null,
+            z2: null,
+            w2: null,
         };
     },
 
@@ -203,10 +228,10 @@ export default {
         // 场景
         initScene() {
             this.scene = new THREE.Scene();
-            let axes = new THREE.AxesHelper(1500);
+            let axes = new THREE.AxesHelper(12000);
             this.scene.add(axes);
 
-            const gridHelper = new THREE.GridHelper(1000, 100);
+            const gridHelper = new THREE.GridHelper(21000, 2100);
             gridHelper.material.opacity = 0.25;
             gridHelper.material.transparent = true;
             this.scene.add(gridHelper);
@@ -287,7 +312,7 @@ export default {
                 75,
                 this.container.clientWidth / this.container.clientHeight,
                 0.1,
-                1000
+                30000
             );
 
             // this.camera = new THREE.PerspectiveCamera(45, 1.5, 1, 1000);
@@ -539,6 +564,613 @@ export default {
                 );
             }
         },
+        /*
+         * 初始化非ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContent0(coordinate, vh, nf) {
+            let r = 0, deg = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示 
+            // 值为测试出来的预估值 超过的话ThreeBSP会报内存溢出
+            let maxR = 10000;
+
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = ((coordinate.nearDistance * a) / rate).toFixed();
+                } else {
+                    r = maxR - 3.3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovV;
+                if (coordinate.fovH >= 47 && coordinate.fovH <= 53 && coordinate.fovV == 30) {
+                    deg = 31;
+                }
+
+                if (deg == 80) deg = 79;
+                if (deg == 20) deg = 19;
+                if (deg % 10 === 0 && deg % 90 != 0) deg = deg + 0.7;
+            } else {
+                deg = coordinate.fovH;
+                if (deg >= 47 && deg <= 53) deg = 53;
+                if (deg >= 17 && deg <= 23) deg = 17;
+                if (deg >= 117 && deg <= 123) deg = 121;
+                if (deg >= 147 && deg <= 153) deg = 148;
+            }
+
+            // 180°时融合有问题
+            if (deg % 90 === 0) deg = deg - 0.5;
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg * 2 / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                }
+            }
+        },
+        /**
+         * 初始化ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContentOgt(coordinate, vh, nf) {
+            let r = 0, deg = 0, deg1 = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            let maxR = 10000;
+
+            // 10000为预估 超过的话ThreeBSP会报内存溢出
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = (coordinate.nearDistance * a) / rate;
+                } else {
+                    r = maxR - 3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovVBottom;
+                deg1 = coordinate.fovVTop;
+            } else {
+                deg = coordinate.fovHRight;
+                deg1 = coordinate.fovHLeft;
+            }
+
+            // 180°时融合有问题
+            if ((deg + deg1) % 180 === 0) {
+                if (deg > 1) {
+                    deg = deg - 0.1;
+                } else {
+                    deg1 = deg1 - 0.1;
+                }
+            }
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material); // 右 下
+
+
+
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (deg1 / 180),
+                thetaStart0 = 0,
+                thetaLength0 = Math.PI;
+
+            const geometry0 = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0); // 左 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                sphere0.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                    this.w2 = new ThreeBSP(sphere0);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                    this.d2 = new ThreeBSP(sphere0);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                    this.w1 = new ThreeBSP(sphere0);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                    this.d1 = new ThreeBSP(sphere0);
+                }
+            }
+        },
+        initSensorBSP(position, rotation, type, index, canMove) {
+            let sensorId = this.configList[type][index].sensorId;
+
+            if (!this.bsp[type][sensorId]) {
+                let coordinate = this.allSensor[type].find(
+                    (i) => i.id === sensorId
+                );
+
+                if (!coordinate) {
+                    coordinate = {
+                        farDistance: 60,
+                        nearDistance: 0,
+                        fovH: 60,
+                        fovV: 60,
+                        fovHLeft: 60,
+                        fovVTop: 60,
+                        fovHRight: 60,
+                        fovVBottom: 60,
+                    };
+                }
+
+                // console.log(coordinate)
+
+                if (coordinate.farDistance <= 0 || coordinate.farDistance <= coordinate.nearDistance) return;
+
+                let color = null;
+
+                if (type === "camera") {
+                    color = 0x0000ff;
+                } else if (type === "ogt") {
+                    color = 0x008000;
+                } else if (type === "lidar") {
+                    color = 0xff4500;
+                } else {
+                    color = 0x0000ff;
+                }
+
+                // let m = new THREE.MeshPhongMaterial({
+                //     emissive: color,
+                //     color: 0x4c4c4c,
+                //     transparent: true,
+                //     // opacity: 0.3,
+                //     // lightMapIntensity: 0.1,
+                //     side: THREE.DoubleSide,
+                // });
+
+                // let m = new THREE.MeshBasicMaterial({ color: 0xffaa00, transparent: true, side: THREE.DoubleSide, blending: THREE.AdditiveBlending });
+                // let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, wireframe: true, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+                let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+                
+                // showFullScreenLoading();
+                // console.log(showFullScreenLoading);
+
+                // console.log(6);
+
+                // const loading = this.$loading({
+                //     lock: true,
+                //     text: "模型加载中,请稍等...",
+                //     // spinner: "el-icon-loading",
+                //     background: "rgba(0, 0, 0, 0.2)",
+                // });
+
+                // this.loadingFull = true;
+
+                coordinate.farDistance = coordinate.farDistance / 100;
+                coordinate.nearDistance = coordinate.nearDistance / 100;
+
+                if (type != "ogt") {
+                    this.toCube(coordinate, m);
+                } else {
+                    this.toCubeOgt(coordinate, m);
+                }
+                // console.log(66);
+                // loading.close();
+                // tryHideFullScreenLoading();
+                // console.log(666);
+
+                this.bsp[type][sensorId] = this.cube;
+            } else {
+                this.cube = this.bsp[type][sensorId];
+            }
+
+            // 为了修复半径过大导致bsp死循环,所以先缩小半径,渲染出mesh后再放大
+            let a = (100 / (this.scale / 0.6)) * 16.5;
+
+            this.cube.scale.set(a, a, a);
+
+
+            if (position) {
+                this.cube.position.x = position.x;
+                this.cube.position.y = position.y;
+                this.cube.position.z = position.z;
+            }
+
+            if (canMove) {
+                this.cube.name = this.geometryName || "cube";
+
+                if (rotation) {
+                    this.cube.rotateX(rotation.x);
+                    this.cube.rotateY(rotation.y);
+                    this.cube.rotateZ(rotation.z);
+                }
+
+                if (this.transformControls) {
+                    this.transformControls.attach(this.cube);
+                }
+            } else {
+                if (rotation) {
+                    this.cube.rotation.set(
+                        (rotation.x * Math.PI) / 180,
+                        (rotation.y * Math.PI) / 180,
+                        (rotation.z * Math.PI) / 180
+                    );
+                }
+
+                this.cube.name = this.geometryName || "cubeA";
+                this.cacheList.push(this.cube);
+            }
+
+            this.scene.add(this.cube);
+        },
+        toCube(coordinate, m) {
+            let { fovH, fovV, nearDistance } = coordinate;
+
+            this.initContent0(coordinate, 'h', 'f');
+            this.initContent0(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContent0(coordinate, 'h', 'n');
+                this.initContent0(coordinate, 'v', 'n');
+
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).subtract(this.x2.subtract(this.y2.subtract(this.z2))).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).subtract(this.x1.subtract(this.y1.intersect(this.z1))).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                        // .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                        //     .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        toCubeOgt(coordinate, m) {
+            let { fovHLeft, fovHRight, fovVTop, fovVBottom, nearDistance } = coordinate;
+
+            this.initContentOgt(coordinate, 'h', 'f');
+            this.initContentOgt(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContentOgt(coordinate, 'h', 'n');
+                this.initContentOgt(coordinate, 'v', 'n');
+
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    // 不带盲区
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
         // 处理轴对称物体
         initSenA(position, rotation, coordinate, r, r1, type, canMove) {
             let x = 0,
@@ -1140,9 +1772,11 @@ export default {
                 // this.renderer.domElement
                 document.getElementById("container")
             ); //创建控件对象
+
             this.controls.minDistance = 30;
-            this.controls.maxDistance = 720;
+            this.controls.maxDistance = 18000;
             this.controls.update();
+
             // this.controls.target = this.cube.position
             // this.controls.addEventListener("change", (e) => {
             //     // console.log(document.getElementById("container"));
@@ -1284,52 +1918,97 @@ export default {
             z = Math.floor(z / this.rate);
 
             if (type != "gps") {
-                this.initContent(
-                    { x, y, z },
-                    {
-                        // x: (-90 * Math.PI) / 180,
-                        x: 0,
-                        y: 0,
-                        z: 0,
-                    },
-                    type,
-                    index,
-                    true
-                );
-            }
+                // this.initSensorBSP(type, index)
+                // this.initContent(
+                // this.loadingFull = true;
+                showFullScreenLoading();
 
-            setTimeout(() => {
-                this.initSensor({ x, y, z }, type, true);
+                setTimeout(() => {
+                    let timer = setTimeout(() => {
+                        clearTimeout(timer);
+                        timer = null;
 
-                const obj = this.scene.getObjectByName("cube");
+                        tryHideFullScreenLoading();
+                    }, 30000)
 
-                if (!obj) return;
+                    this.initSensorBSP(
+                        { x, y, z },
+                        {
+                            // x: (-90 * Math.PI) / 180,
+                            x: 0,
+                            y: 0,
+                            z: 0,
+                        },
+                        type,
+                        index,
+                        true
+                    );
+                    // this.loadingFull = false;
 
-                obj.position.x = x;
-                obj.position.y = y;
-                obj.position.z = z;
+                    clearTimeout(timer);
+                    timer = null;
 
-                // obj.rotateX(0 * -1);
-                // obj.rotateX((h * Math.PI) / 180);
-                // this.xAngle = ((h - 90) * Math.PI) / 180;
-                this.xAngle = (h * Math.PI) / 180;
+                    setTimeout(() => {
+                        this.initSensor({ x, y, z }, type, true);
 
-                // obj.rotateY(0 * -1);
-                // obj.rotateY((p * Math.PI) / 180);
-                this.yAngle = (p * Math.PI) / 180;
+                        const obj = this.scene.getObjectByName("cube");
+
+                        if (!obj) return;
+
+                        obj.position.x = x;
+                        obj.position.y = y;
+                        obj.position.z = z;
+
+                        // obj.rotateX(0 * -1);
+                        // obj.rotateX((h * Math.PI) / 180);
+                        // this.xAngle = ((h - 90) * Math.PI) / 180;
+                        this.xAngle = (h * Math.PI) / 180;
+
+                        // obj.rotateY(0 * -1);
+                        // obj.rotateY((p * Math.PI) / 180);
+                        this.yAngle = (p * Math.PI) / 180;
+
+                        // obj.rotateZ(0 * -1);
+                        // obj.rotateZ((r * Math.PI) / 180);
+                        this.zAngle = (r * Math.PI) / 180;
+                        obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+                    }, 0);
+
+
+                    tryHideFullScreenLoading();
+
+                }, 100)
+            } else {
+                setTimeout(() => {
+                    this.initSensor({ x, y, z }, type, true);
+
+                    const obj = this.scene.getObjectByName("cube");
+
+                    if (!obj) return;
+
+                    obj.position.x = x;
+                    obj.position.y = y;
+                    obj.position.z = z;
+
+                    // obj.rotateX(0 * -1);
+                    // obj.rotateX((h * Math.PI) / 180);
+                    // this.xAngle = ((h - 90) * Math.PI) / 180;
+                    this.xAngle = (h * Math.PI) / 180;
+
+                    // obj.rotateY(0 * -1);
+                    // obj.rotateY((p * Math.PI) / 180);
+                    this.yAngle = (p * Math.PI) / 180;
+
+                    // obj.rotateZ(0 * -1);
+                    // obj.rotateZ((r * Math.PI) / 180);
+                    this.zAngle = (r * Math.PI) / 180;
+                    obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+                }, 0);
+            }
 
-                // obj.rotateZ(0 * -1);
-                // obj.rotateZ((r * Math.PI) / 180);
-                this.zAngle = (r * Math.PI) / 180;
-                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
-            }, 0);
         },
         // 初始化传感器
-        initSensor(
-            pos = { x: 0, y: 0, z: 0 },
-            type = "camera",
-            canMove = false
-        ) {
+        initSensor(pos = { x: 0, y: 0, z: 0 }, type = "camera", canMove = false) {
             let Loader = new MTLLoader(); //材质文件加载器
             let loader = new OBJLoader(); //obj加载器
             let that = this;
@@ -1399,8 +2078,64 @@ export default {
                 //     background: "rgba(0, 0, 0, 0.2)",
                 // });
                 // showFullScreenLoading();
-                this.$emit('loadingDone', 'carStart');
+
+
+                // console.log(this.ogtObj);
+                if (this.ogtObj) {
+                    let a = this.ogtObj;
+
+                    a.position.set(pos.x, pos.y, pos.z);
+
+                    if (canMove) {
+                        this.sensor = a;
+                    }
+
+                    this.cacheList.push(a);
+
+                    this.scene.add(a); //返回的组对象插入场景中
+                    return;
+                }
+
+
+                // this.$emit('loadingDone', 'carStart');
                 // this.isLoading = true;
+
+
+
+                var loader1 = new GLTFLoader(); //创建一个FBX加载器
+
+                loader1.load(
+                    `${that.publicPath}sensor/ogt/ogt.glb`,
+                    function (obj) {
+                        // that.$emit('loadingDone', 'carDone');
+
+                        that.cacheList.push(obj.scene);
+
+                        that.ogtObj = obj.scene;
+
+                        let s = that.scale;
+
+                        let scale = 0.1;
+                        obj.scene.scale.set(scale, scale, scale);
+
+                        obj.scene.position.set(pos.x, pos.y, pos.z);
+                        // obj.scene.name = "sensor";
+
+                        // if (canMove) {
+                        //     that.sensor = obj.scene;
+                        // }
+
+                        that.scene.add(obj.scene); //返回的组对象插入场景中
+                    },
+                    (xhr) => { },
+                    (error) => {
+                        // that.$emit('loadingDone', 'carDone');
+
+                        console.error(error);
+                    }
+                );
+
+                return
             }
 
             Loader.load(
@@ -1427,11 +2162,13 @@ export default {
                         for (let i = 0; i < obj.children.length; i++) {
                             if (type === "camera") {
                                 // let scale = 0.8 * that.scale;
-                                obj.children[i].scale.set(0.8, 0.8, 0.8);
+                                // obj.children[i].scale.set(0.8, 0.8, 0.8);
+                                obj.children[i].scale.set(0.4, 0.4, 0.4);
                             } else if (type === "ogt") {
                                 obj.children[i].scale.set(0.2, 0.2, 0.2);
                             } else if (type === "lidar") {
-                                obj.children[i].scale.set(0.1, 0.1, 0.1);
+                                // obj.children[i].scale.set(0.1, 0.1, 0.1);
+                                obj.children[i].scale.set(0.06, 0.06, 0.06);
                             } else if (type === "gps") {
                                 obj.children[i].scale.set(40, 40, 40);
                             }
@@ -1441,6 +2178,11 @@ export default {
                             // obj.children[i].geometry.translate(-pos.x, -pos.y, -pos.z);
                             // obj.children[i].scale.set(30, 30, 30);
                         }
+
+                        if (type === "ogt") {
+                            that.ogtObj = obj;
+                        }
+
                         obj.position.set(pos.x, pos.y, pos.z);
                         // obj.geometry.translate.set(pos.x, pos.y, pos.z);
 
@@ -1547,7 +2289,8 @@ export default {
                     }
 
                     if (type != "gps") {
-                        this.initContent(
+                        // this.initContent(
+                        this.initSensorBSP(
                             {
                                 z: Math.floor(item.sensorX / this.rate || 0),
                                 x: Math.floor(item.sensorY / this.rate || 0),
@@ -1619,10 +2362,17 @@ export default {
 
             // console.log(this.configList);
 
-            this.showSensor(this.configList.camera, "camera");
-            this.showSensor(this.configList.ogt, "ogt");
-            this.showSensor(this.configList.lidar, "lidar");
-            this.showSensor(this.configList.gps, "gps");
+
+            showFullScreenLoading();
+
+            setTimeout(() => {
+                this.showSensor(this.configList.camera, "camera");
+                this.showSensor(this.configList.ogt, "ogt");
+                this.showSensor(this.configList.lidar, "lidar");
+                this.showSensor(this.configList.gps, "gps");
+
+                tryHideFullScreenLoading();
+            }, 100)
         },
         removeScene() {
             this.clearScene();
@@ -2173,6 +2923,25 @@ export default {
         this.cubeTexture = null;
         this.raf = null;
 
+        this.ogtObj = null;
+        this.bsp = null;
+        this.a1 = null;
+        this.b1 = null;
+        this.c1 = null;
+        this.d1 = null;
+        this.a2 = null;
+        this.b2 = null;
+        this.c2 = null;
+        this.d2 = null;
+        this.x1 = null;
+        this.y1 = null;
+        this.z1 = null;
+        this.w1 = null;
+        this.x2 = null;
+        this.y2 = null;
+        this.z2 = null;
+        this.w2 = null;
+
         THREE.Cache.clear();
     },
 };

+ 2186 - 0
src/views/modelLibrary/components/threeVehicleConfigurationByConvexGeometry.vue

@@ -0,0 +1,2186 @@
+<template>
+    <div>
+        <div id="container"></div>
+    </div>
+</template>
+
+<script>
+import * as THREE from "three";
+import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
+import { DragControls } from "three/examples/jsm/controls/DragControls.js";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
+import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";
+// import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
+// import { DDSLoader } from "three/examples/jsm/loaders/DDSLoader.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
+import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
+// import { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js";
+// import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js";
+// import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
+// import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
+// import { Line2 } from "three/examples/jsm/lines/Line2.js";
+// import {
+//     showFullScreenLoading,
+//     tryHideFullScreenLoading,
+// } from "../../../axios/filter";
+import { mapState } from "vuex";
+import CoordinateAxes from "../common/coordinateAxes";
+
+export default {
+    name: "threeVehicleConfigurationByConvexGeometry", // 车辆配置详情中的threeJS
+    components: {},
+
+    data() {
+        return {
+            publicPath: process.env.BASE_URL,
+            scene: null,
+            scene2: null,
+            camera: null,
+            camera2: null,
+            renderer: null,
+            light: null,
+            transformControls: null,
+            geometryName: null,
+            controls: null,
+            mesh: null,
+            cube: null,
+            cacheList: [],
+            xAngle: 0,
+            yAngle: 0,
+            zAngle: 0,
+            container: null,
+            car: null,
+            cubeTexture: null,
+            raf: null,
+            canDrag: true, // 是否可移动
+            dragControls: null,
+            sensor: null, // 当前操作的传感器配置
+            ogt: null,
+            // scale: 2, // 物体加载换算倍数
+            // rate: 20, // 坐标换算倍数
+            isLoading: false, // 判断是否正在记载车辆
+            lastCarModel: "", // 记录上一次加载的carModel
+        };
+    },
+
+    computed: {
+        ...mapState(["scale", "scaleRate"]),
+        // 坐标换算倍数
+        rate() {
+            return 40 / this.scale;
+        },
+    },
+
+    props: {
+        xValue: {
+            type: Number,
+            default: 0,
+        },
+        yValue: {
+            type: Number,
+            default: 0,
+        },
+        zValue: {
+            type: Number,
+            default: 0,
+        },
+        hValue: {
+            type: Number,
+            default: 0,
+        },
+        pValue: {
+            type: Number,
+            default: 0,
+        },
+        rValue: {
+            type: Number,
+            default: 0,
+        },
+        carModel: {
+            type: String,
+            default: "",
+        },
+        isAdd: {
+            type: Boolean,
+            default: false,
+        },
+        configList: {
+            // default: {},
+            // type: Object,
+            default: function () {
+                return {};
+            },
+            type: Object,
+        },
+        allSensor: {
+            default: function () {
+                return {};
+            },
+            type: Object,
+        },
+    },
+
+    watch: {
+        xValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) obj.position.x = newVal || 0;
+
+            if (this.sensor) this.sensor.position.x = newVal || 0;
+        },
+        yValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) obj.position.y = newVal || 0;
+
+            if (this.sensor) this.sensor.position.y = newVal || 0;
+        },
+        zValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) obj.position.z = newVal || 0;
+
+            if (this.sensor) this.sensor.position.z = newVal || 0;
+        },
+        hValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            // let value = (newVal || 0) - 90;
+            let value = newVal || 0;
+            value = (value * Math.PI) / 180;
+            this.xAngle = value;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+            }
+
+            // obj.rotateX(this.xAngle * -1);
+            // obj.rotateX(value);
+
+            // obj.rotation.x += value;
+            // var axis = new THREE.Vector3(0, 1, 0); //向量axis
+            // obj.rotateOnAxis(axis, value); //绕axis轴旋转π/8
+        },
+        pValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            let value = newVal || 0;
+            value = (value * Math.PI) / 180;
+            this.yAngle = value;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+            }
+        },
+        rValue(newVal, oldVal) {
+            if (newVal === oldVal) return;
+
+            let value = newVal || 0;
+            value = (value * Math.PI) / 180;
+            this.zAngle = value;
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+            }
+        },
+        carModel(newVal, oldVal) {
+            if (newVal != oldVal && !this.isLoading) {
+                this.lastCarModel = newVal;
+                this.initCar(newVal);
+            }
+        },
+    },
+
+    methods: {
+        // 场景
+        initScene() {
+            this.scene = new THREE.Scene();
+            let axes = new THREE.AxesHelper(1500);
+            this.scene.add(axes);
+
+            const gridHelper = new THREE.GridHelper(1000, 100);
+            gridHelper.material.opacity = 0.25;
+            gridHelper.material.transparent = true;
+            this.scene.add(gridHelper);
+
+            return;
+
+            /* var cubeGeometry = new THREE.ConeGeometry(75, 150, 6, 1, false);
+            cubeGeometry.translate(0, -75, 0);
+
+            let obj = {
+                color: 0x4c4c4c,
+                // wireframe: true,
+                // transparent: true,
+                // opacity: 0.3,
+                // ambient: 0x00ff00,
+                // emissive: 0x00ff00,
+                lightMapIntensity: 0.1,
+            };
+
+            var cubeMaterial = new THREE.MeshLambertMaterial(obj);
+            let cube = new THREE.Mesh(cubeGeometry, cubeMaterial); */
+
+            this.scene2 = new THREE.Scene();
+            let cube = new CoordinateAxes();
+            // this.scene2.background = null;
+            // this.scene2.background = new THREE.Color(0x00000, 1.0);
+            this.scene2.add(cube);
+
+            // const geometry = new THREE.BoxGeometry(100, 100, 100);
+            // const material = new THREE.MeshBasicMaterial({
+            //     color: 0xffffff,
+            //     opacity: 0.3,
+            //     transparent: true,
+            // });
+            // geometry.translate(100, 100, 100);
+            // const cube = new THREE.Mesh(geometry, material);
+            // this.scene.add(cube);
+
+            // let geometry = new THREE.BoxGeometry(10, 10, 10, 2, 2, 2);
+            // let material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
+            // let object = new THREE.Mesh(geometry, material);
+            // let edges = new THREE.VertexNormalsHelper(object, 2, 0x00ff00, 1);
+            // this.scene.add(object);
+            // this.scene.add(edges);
+
+            const genCubeUrls = function (prefix, postfix) {
+                return [
+                    prefix + "px" + postfix,
+                    prefix + "nx" + postfix,
+                    prefix + "py" + postfix,
+                    prefix + "ny" + postfix,
+                    prefix + "pz" + postfix,
+                    prefix + "nz" + postfix,
+                ];
+            };
+
+            const urls = genCubeUrls(`${this.publicPath}Park3Med/`, ".jpg");
+            // const urls = genCubeUrls(`${this.publicPath}6/`, ".png");
+
+            const cubeTextureLoader = new THREE.CubeTextureLoader();
+            const environmentMapTexture = cubeTextureLoader.load(urls);
+
+            // this.scene.background = environmentMapTexture;
+        },
+        // 相机
+        initCamera() {
+            /* this.camera = new THREE.OrthographicCamera(
+                this.container.clientWidth / -2,
+                this.container.clientWidth / 2,
+                this.container.clientHeight / 2,
+                this.container.clientHeight / -2,
+                1,
+                // 0.1,
+                1000
+            ); */
+
+            this.camera = new THREE.PerspectiveCamera(
+                75,
+                this.container.clientWidth / this.container.clientHeight,
+                0.1,
+                1000
+            );
+
+            // this.camera = new THREE.PerspectiveCamera(45, 1.5, 1, 1000);
+            // this.camera.position.set(600, 600, 600);
+            this.camera.position.set(200, 200, 200);
+            // this.camera.lookAt(this.scene.position);
+            // this.scene.add(this.camera);
+            // this.scene.add(this.camera2);
+
+            this.camera2 = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
+            // this.camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000);
+            // this.camera2.position.copy(this.camera.position);
+            // this.camera.layers.set(1);
+        },
+        // 渲染器
+        initRenderer() {
+            this.renderer = new THREE.WebGLRenderer({
+                antialias: true,
+                alpha: true,
+            });
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+            this.renderer.setClearColor("#272727");
+            this.container.appendChild(this.renderer.domElement);
+        },
+        // 初始化灯光
+        initLight() {
+            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
+            hemiLight.position.set(0, 20, 0);
+            this.scene.add(hemiLight);
+
+            // 环境光会均匀的照亮场景中的所有物体
+            const light = new THREE.AmbientLight(0x5c5c5c, 0.4); // soft white light
+            this.scene.add(light);
+
+            // 点光源
+            // const light1 = new THREE.PointLight(0xffffff, 2, 100);
+            // light1.position.set(0, 0, -100);
+            // this.scene.add(light1);
+
+            // 平行光是沿着特定方向发射的光
+            const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
+            // const dirLight = new THREE.DirectionalLight(0xffffff);
+            // dirLight.position.set(0, 200, 100);
+            // dirLight.castShadow = true;
+            // dirLight.shadow.camera.top = 180;
+            // dirLight.shadow.camera.bottom = -100;
+            // dirLight.shadow.camera.left = -120;
+            // dirLight.shadow.camera.right = 120;
+
+            // dirLight.shadow.camera.near = 0.01;
+            // dirLight.shadow.camera.far = 60;
+            // dirLight.shadow.camera.top = 22;
+            // dirLight.shadow.camera.bottom = -22;
+            // dirLight.shadow.camera.left = -35;
+            // dirLight.shadow.camera.right = 35;
+            // // //设置阴影分辨率
+            // dirLight.shadow.mapSize.width = 2048; // default
+            // dirLight.shadow.mapSize.height = 2048; // default
+            // //阴影限制
+            // dirLight.shadow.radius = 1;
+            // this.scene.add(dirLight);
+
+            // const spotLight = new THREE.SpotLight(0xffffff);
+            // spotLight.position.set(100, 1000, 100);
+
+            // spotLight.castShadow = true;
+
+            // spotLight.shadow.mapSize.width = 1024;
+            // spotLight.shadow.mapSize.height = 1024;
+
+            // spotLight.shadow.camera.near = 500;
+            // spotLight.shadow.camera.far = 4000;
+            // spotLight.shadow.camera.fov = 30;
+
+            // this.scene.add(spotLight);
+        },
+        initTransform() {
+            // 添加平移控件
+            this.transformControls = new TransformControls(
+                this.camera,
+                this.renderer.domElement
+            );
+            this.transformControls.setMode("translate");
+            // 可能的值有"world" 和 "local"
+            this.transformControls.setSpace("world");
+            this.transformControls.addEventListener("objectChange", (e) => {
+                // console.log(e.target.object);
+                // this.$emit(
+                //     "posChange",
+                //     ((e.target.object.rotation.x * 360) / Math.PI) >> 0,
+                //     "h"
+                // );
+                if (!this.canDrag) return;
+                this.$emit(
+                    "posChange",
+                    (e.target.object.position.x + 0.5) >> 0,
+                    "x"
+                );
+                this.$emit(
+                    "posChange",
+                    (e.target.object.position.y + 0.5) >> 0,
+                    "y"
+                );
+                this.$emit(
+                    "posChange",
+                    (e.target.object.position.z + 0.5) >> 0,
+                    "z"
+                );
+            });
+            this.transformControls.addEventListener("mouseDown", (e) => {
+                this.controls.enabled = false;
+                this.$emit("controlsEnabled", false);
+            });
+            this.transformControls.addEventListener("mouseUp", (e) => {
+                this.controls.enabled = true;
+                this.$emit("controlsEnabled", true);
+            });
+            this.scene.add(this.transformControls);
+        },
+        /**
+         * 初始化模型
+         * @param {*} r 半径
+         * @param {*} position 位置
+         * @param {*} rotation 旋转
+         * @param {*} type 传感器类型
+         * @param {*} index 该传感器所在数组下标
+         */
+        initContent1(r, position, rotation, type, index) {
+            var cubeGeometry = new THREE.ConeGeometry(
+                r || 45 * this.scale,
+                150 * this.scale,
+                4,
+                1,
+                false
+            );
+            cubeGeometry.translate(0, -75 * this.scale, 0);
+
+            let obj = {
+                color: 0x4c4c4c,
+                // wireframe: true,
+                transparent: true,
+                opacity: 0.3,
+                // ambient: 0x00ff00,
+                // emissive: 0xfaff72,
+                lightMapIntensity: 0.1,
+                // emissiveIntensity: 0.1,
+                // envMap: this.cubeTexture,
+                // side: THREE.DoubleSide,
+                // side: 2,
+            };
+
+            if (type === "camera") {
+                obj.emissive = 0x0000ff;
+                // obj.emissive = 0x000080;
+            } else if (type === "ogt") {
+                // obj.emissive = 0x228B22;
+                // obj.emissive = 0x006400;
+                obj.emissive = 0x008000;
+            } else if (type === "lidar") {
+                // obj.emissive = 0xff0000;
+                // obj.emissive = 0xE6A23C;
+                obj.emissive = 0xff4500;
+            } else if (type === "gps") {
+                // obj.emissive = 0xfaff72;
+                // obj.emissive = 0x9400D3;
+                obj.emissive = 0x8a2be2;
+            }
+
+            // var cubeMaterial = new THREE.MeshBasicMaterial(obj);
+            var cubeMaterial = new THREE.MeshLambertMaterial(obj);
+            this.cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
+            this.cube.name = this.geometryName || "cube";
+
+            if (position) {
+                this.cube.position.x = position.x;
+                this.cube.position.y = position.y;
+                this.cube.position.z = position.z;
+            }
+
+            if (rotation) {
+                this.cube.rotateX(rotation.x);
+                this.cube.rotateY(rotation.y);
+                this.cube.rotateZ(rotation.z);
+            }
+
+            this.scene.add(this.cube);
+
+            if (this.transformControls) {
+                this.transformControls.attach(this.cube);
+            }
+        },
+        /**
+         * 初始化模型
+         * @param {*} position 位置
+         * @param {*} rotation 旋转
+         * @param {*} type 传感器类型
+         * @param {*} index 该传感器所在数组下标
+         * @param {*} canMove 如果用于单独展示则可拖拽,用于全部预览则不可拖拽
+         */
+        initContent(position, rotation, type, index, canMove) {
+            let sensorId = this.configList[type][index].sensorId;
+            let coordinate = this.allSensor[type].find(
+                (i) => i.id === sensorId
+            );
+
+            if (!coordinate) {
+                coordinate = {
+                    farDistance: 60,
+                    nearDistance: 0,
+                    fovH: 60,
+                    fovV: 60,
+                    fovHLeft: 60,
+                    fovVTop: 60,
+                    fovHRight: 60,
+                    fovVBottom: 60,
+                };
+            }
+            // console.log(coordinate);
+
+            let r = coordinate.farDistance * 100;
+            let r1 = coordinate.nearDistance * 100;
+
+            if (r <= 0 || r1 < 0 || r <= r1) {
+                return;
+            }
+
+            if (type != "ogt") {
+                this.initSenA(
+                    position,
+                    rotation,
+                    coordinate,
+                    r / (this.scale / 0.6),
+                    r1 / (this.scale / 0.6),
+                    type,
+                    canMove
+                );
+            } else {
+                this.initSenB(
+                    position,
+                    rotation,
+                    coordinate,
+                    r / (this.scale / 0.6),
+                    r1 / (this.scale / 0.6),
+                    type,
+                    canMove
+                );
+            }
+        },
+        // 处理轴对称物体
+        initSenA(position, rotation, coordinate, r, r1, type, canMove) {
+            let x = 0,
+                y = 0,
+                z = r;
+
+            let a = coordinate.fovH,
+                b = coordinate.fovV;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+
+            x = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            y = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            } else {
+                let x1 = 0,
+                    y1 = 0,
+                    z1 = r1;
+
+                x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+
+                pointsArr = [
+                    [x1, y1, z1],
+                    [x1, -y1, z1],
+                    [-x1, -y1, z1],
+                    [-x1, y1, z1],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            }
+
+            this.initMesh(position, rotation, pointsArr, type, canMove);
+        },
+        // 处理非轴对称物体
+        initSenB(position, rotation, coordinateA, r, r1, type, canMove) {
+            let x1 = 0,
+                x2 = 0,
+                y1 = 0,
+                y2 = 0,
+                z1 = r;
+
+            let a = coordinateA.fovHLeft,
+                b = coordinateA.fovVTop,
+                c = coordinateA.fovHRight,
+                d = coordinateA.fovVBottom;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+            if (c >= 90) c = 89.99;
+            if (d >= 90) d = 89.99;
+
+            x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+            x2 = Math.tan(THREE.MathUtils.degToRad(c)) * r;
+            y2 = Math.tan(THREE.MathUtils.degToRad(d)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            } else {
+                let m1 = 0,
+                    m2 = 0,
+                    n1 = 0,
+                    n2 = 0,
+                    z2 = r1;
+
+                m1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                n1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+                m2 = Math.tan(THREE.MathUtils.degToRad(c)) * r1;
+                n2 = Math.tan(THREE.MathUtils.degToRad(d)) * r1;
+
+                pointsArr = [
+                    [m1, n1, z2],
+                    [m1, -n2, z2],
+                    [-m2, -n2, z2],
+                    [-m2, n1, z2],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            }
+
+            this.initMesh(position, rotation, pointsArr, type, canMove);
+        },
+        // canMove 如果用于单独展示则可拖拽,用于全部预览则不可拖拽
+        initMesh(position, rotation, pointsArr, type, canMove = false) {
+            let points = pointsArr.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let color = null;
+            if (type === "camera") {
+                color = 0x0000ff;
+            } else if (type === "ogt") {
+                color = 0x008000;
+            } else if (type === "lidar") {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            let material = new THREE.MeshPhongMaterial({
+                emissive: color,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+                side: THREE.DoubleSide,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            this.cube = new THREE.Mesh(tixing, material);
+            // this.cube.name = this.geometryName || "cube";
+
+            if (position) {
+                this.cube.position.x = position.x;
+                this.cube.position.y = position.y;
+                this.cube.position.z = position.z;
+            }
+
+            // if (rotation) {
+            //     this.cube.rotateX(rotation.x);
+            //     this.cube.rotateY(rotation.y);
+            //     this.cube.rotateZ(rotation.z);
+            // }
+
+            if (canMove) {
+                this.cube.name = this.geometryName || "cube";
+
+                if (rotation) {
+                    this.cube.rotateX(rotation.x);
+                    this.cube.rotateY(rotation.y);
+                    this.cube.rotateZ(rotation.z);
+                }
+
+                if (this.transformControls) {
+                    this.transformControls.attach(this.cube);
+                }
+            } else {
+                if (rotation) {
+                    this.cube.rotation.set(
+                        (rotation.x * Math.PI) / 180,
+                        (rotation.y * Math.PI) / 180,
+                        (rotation.z * Math.PI) / 180
+                    );
+                }
+
+                this.cube.name = this.geometryName || "cubeA";
+                this.cacheList.push(this.cube);
+            }
+
+            this.scene.add(this.cube);
+        },
+        // 添加拖拽控件
+        initDragControls() {
+            // 过滤不是 Mesh 的物体,例如辅助网格
+            var objects = [];
+            for (let i = 0; i < this.scene.children.length; i++) {
+                if (this.scene.children[i].isMesh) {
+                    objects.push(this.scene.children[i]);
+                }
+            }
+
+            if (this.dragControls) {
+                this.dragControls.deactivate();
+                this.dragControls.dispose();
+                this.dragControls = null;
+            }
+
+            // 初始化拖拽控件
+            this.dragControls = new DragControls(
+                objects,
+                this.camera,
+                this.renderer.domElement
+            );
+
+            // 鼠标略过事件
+            // this.dragControls.addEventListener("hoveron", (event) => {
+            //     // 让变换控件对象和选中的对象绑定
+            //     this.transformControls.attach(event.object);
+            // });
+
+            this.dragControls.addEventListener("drag", (e) => {
+                // console.log(e);
+                if (!this.canDrag) return;
+                // this.$emit("posChange", (e.object.position.x + 0.5) >> 0, "x");
+                // this.$emit("posChange", (e.object.position.y + 0.5) >> 0, "y");
+                // this.$emit("posChange", (e.object.position.z + 0.5) >> 0, "z");
+            });
+            this.dragControls.addEventListener("dragstart", (e) => {
+                this.controls.enabled = false;
+            });
+            this.dragControls.addEventListener("dragend", (e) => {
+                this.controls.enabled = true;
+            });
+        },
+        initCar0(model) {
+            if (this.car) {
+                // console.log(this.car);
+                this.scene.remove(this.car);
+                this.removeObj(this.car);
+                this.car = null;
+            }
+
+            // const loading = this.$loading({
+            //     lock: true,
+            //     text: "模型加载中,请稍等...",
+            //     // spinner: "el-icon-loading",
+            //     background: "rgba(0, 0, 0, 0.2)",
+            // });
+
+            // showFullScreenLoading();
+
+            this.$emit('loadingDone', 'carStart');
+            this.isLoading = true;
+
+            var that = this;
+            var loader = new GLTFLoader(); //创建一个FBX加载器
+
+            const dracoLoader = new DRACOLoader(); //
+            dracoLoader.setDecoderPath(`${this.publicPath}draco/`); // 设置public下的解码路径,注意最后面的/
+            dracoLoader.setDecoderConfig({ type: "js" }); // 使用兼容性强的draco_decoder.js解码器
+            dracoLoader.preload(); // 初始化_initDecoder 解码器
+            loader.setDRACOLoader(dracoLoader); // gltfloader使用dracoLoader
+
+            // loader.load(
+            //     `${this.publicPath}glb/AudiA6_10.glb`,
+            //     function (obj) {
+            loader.load(
+                model,
+                function (obj) {
+                    // loading.close();
+                    // tryHideFullScreenLoading();
+                    // console.log(obj);
+
+                    /*  for (let i = 0; i < obj.children.length; i++) {
+                    // obj.children[i].position.set(0, -135, 0);
+                    // obj.children[i].position.set(18, 36, 0);
+                    // obj.children[i].scale.set(30, 30, 30);
+                    // obj.children[i].scale.set(0.45, 0.45, 0.45);
+                    obj.children[i].rotation.set(
+                        (-90 * Math.PI) / 180,
+                        0,
+                        (-180 * Math.PI) / 180
+                    );
+                    // obj.children[i].transparent.set(0.5);
+                    // that.transformControls.attach(obj.children[i]);
+                } */
+
+                    obj.scene.rotation.set(
+                        (-90 * Math.PI) / 180,
+                        0,
+                        (-180 * Math.PI) / 180
+                    );
+
+                    let s = 0;
+                    if (model.includes("928.glb")) {
+                        s = that.scale / that.scaleRate;
+                    } else {
+                        s = that.scale;
+                    }
+
+                    let scale = 30 * s;
+                    obj.scene.scale.set(scale, scale, scale);
+                    // obj.scene.scale.set(30, 30, 30);
+
+                    // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
+                    // console.log(obj.animations);
+                    // that.scene.add(obj);
+                    that.scene.add(obj.scene);
+                    that.car = obj.scene;
+
+                    that.$emit('loadingDone', 'carDone');
+                    that.isLoading = false;
+                    that.needCar();
+                },
+                (xhr) => {
+                    // console.log(xhr);
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    // loading.close();
+                    // tryHideFullScreenLoading();
+                    that.$emit('loadingDone', 'carDone');
+                    that.isLoading = false;
+                    that.needCar();
+                    console.error(error);
+                }
+            );
+        },
+        initCar1() {
+            // const loader2 = new DDSLoader();
+            // const map1 = loader2.load(
+            //     `${this.publicPath}q5/AudiQ5_Texture.dds`
+            // );
+            // const map2 = loader2.load(`${this.publicPath}q5/env3.dds`);
+            // const map3 = loader2.load(
+            //     `${this.publicPath}q5/Tx_Blur_AudiQ5.dds`
+            // );
+            // const map4 = loader2.load(`${this.publicPath}q5/Tx_Driver.dds`);
+            // const map6 = loader2.load(
+            //     `${this.publicPath}q5/Tx_ln_AudiQ5_09.dds`
+            // );
+            // var that = this;
+            // var loader = new FBXLoader(); //创建一个FBX加载器
+            // // var loader = new GLTFLoader(); //创建一个FBX加载器
+            // // loader.load(`${this.publicPath}6/a8/audia8.fbx`, function (obj) {
+            // // loader.load(`${this.publicPath}6/q5/audiq5.fbx`, function (obj) {
+            // // loader.load(`${this.publicPath}6/a8/audia8.fbx`, function (obj) {
+            // // loader.load(`${this.publicPath}a8/audia8.fbx`, function (obj) {
+            // // loader.load(`${this.publicPath}q5/audiq5.fbx`, function (obj) {
+            // // loader.load(`${this.publicPath}lb/audiq5.fbx`, function (obj) {
+            // loader.load(
+            //     `${this.publicPath}lb/1/fbx/untitled.fbx`,
+            //     function (obj) {
+            //         // loader.load(`${this.publicPath}q6/audiq5.fbx`, function (obj) {
+            //         // loader.load(`${this.publicPath}untitled.fbx`, function (obj) {
+            //         // loader.load(`${this.publicPath}untitled.glb`, function (obj) {
+            //         // loader.load(`${this.publicPath}fbx/Samba Dancing.fbx`, function (obj) {
+            //         // 可以在控制台打印obj对象,找到animations属性
+            //         console.log(obj);
+            //         obj.name = "car";
+            //         // obj.map = map1;
+            //         function _ChangeMaterialEmissive(parent) {
+            //             parent.traverse(function (child) {
+            //                 // if (child instanceof THREE.Mesh) {
+            //                 if (child.isMesh) {
+            //                     // child.material.emissive = new THREE.Color(1, 1, 1);
+            //                     // child.material.emissive = child.material.color;
+            //                     // child.material.emissiveIntensity = 1;
+            //                     // child.material.emissiveMap = child.material.map;
+            //                     // child.material.envMap = child.material.map;
+            //                     // child.material.alphaMap = child.material.map;
+            //                     // child.material.aoMap = child.material.map;
+            //                     // child.material.bumpMap = child.material.map;
+            //                     // child.material.lightMap = child.material.map;
+            //                     // child.material.normalMap = child.material.map;
+            //                     // child.material.specularMap = child.material.map;
+            //                     // child.material.transparent = true;
+            //                     child.castShadow = true;
+            //                     child.receiveShadow = true;
+            //                 }
+            //             });
+            //         }
+            //         _ChangeMaterialEmissive(obj);
+            //         // obj.traverse(function (child) {
+            //         //     if (child.isMesh) {
+            //         //         // child.material.emissive = new THREE.Color(1, 1, 1);
+            //         //         child.material.emissive = child.material.color;
+            //         //         child.material.emissiveIntensity = 1;
+            //         //         child.material.emissiveMap = child.material.map;
+            //         //         child.castShadow = true;
+            //         //         child.receiveShadow = true;
+            //         //     }
+            //         // });
+            //         /*  for (let i = 0; i < obj.children.length; i++) {
+            //         // obj.children[i].position.set(0, -135, 0);
+            //         // obj.children[i].position.set(18, 36, 0);
+            //         // obj.children[i].scale.set(30, 30, 30);
+            //         // obj.children[i].scale.set(0.45, 0.45, 0.45);
+            //         obj.children[i].rotation.set(
+            //             (-90 * Math.PI) / 180,
+            //             0,
+            //             (-180 * Math.PI) / 180
+            //         );
+            //         // obj.children[i].transparent.set(0.5);
+            //         // that.transformControls.attach(obj.children[i]);
+            //     } */
+            //         obj.rotation.set(
+            //             (-90 * Math.PI) / 180,
+            //             0,
+            //             (-180 * Math.PI) / 180
+            //         );
+            //         // obj.scale.set(30, 30, 30);
+            //         // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
+            //         // console.log(obj.animations);
+            //         that.scene.add(obj);
+            //         // that.scene.add(obj.scene);
+            //     }
+            // );
+        },
+        initCar2() {
+            var that = this;
+            var loader2 = new THREE.ObjectLoader();
+            // var loader2 = new THREE.MeshLambertMaterial();
+            // loader2.load(`${that.publicPath}4.js`, function (obj) {
+            loader2.load(`${that.publicPath}car.json`, function (obj) {
+                // loader2.load(`${that.publicPath}Lidar.json`, function (obj) {
+                // console.log(obj);
+                obj.name = "car";
+                // console.log(obj.type);
+                // obj.position.set(0, 0, 0);
+                obj.scale.set(30, 30, 30);
+                obj.rotation.set(
+                    (-90 * Math.PI) / 180,
+                    0,
+                    (-90 * Math.PI) / 180
+                );
+                // obj.layers.set(1);
+                // that.transformControls.attach(obj);
+                that.scene.add(obj);
+            });
+        },
+        initCar3() {
+            var Loader = new MTLLoader(); //材质文件加载器
+            var loader = new OBJLoader(); //obj加载器
+            var that = this;
+
+            Loader.load(
+                // "../../../assets/common/image/female02/female02.mtl",
+                // "../../../../public/female02/female02.mtl",
+                // `${that.publicPath}male02/male02.mtl`,
+                // `${that.publicPath}GTR.mtl`,
+                // `${that.publicPath}lidar.mtl`,
+                // `${that.publicPath}female02/female02.mtl`,
+                // `${that.publicPath}new/audiq5.mtl`,
+                // `${that.publicPath}lb/obj/audiq5.mtl`,
+                // `${that.publicPath}lb/00obj/00.mtl`,
+                `${that.publicPath}lb/1/obj/untitled.mtl`,
+                function (materials) {
+                    // 返回一个包含材质的对象MaterialCreator
+                    // console.log(materials);
+                    // materials.transparent = true;
+                    // materials.opacity = 0.15;
+                    // materials.side = THREE.DoubleSide;
+                    // materials.depthWrite = false;
+                    //obj的模型会和MaterialCreator包含的材质对应起来
+                    loader.setMaterials(materials);
+                    loader.load(
+                        // "../../../../public/female02/female02.obj",
+                        // `${that.publicPath}female02/female02.obj`,
+                        // `${that.publicPath}new/audiq5.obj`,
+                        // `${that.publicPath}lb/00obj/00.obj`,
+                        `${that.publicPath}lb/1/obj/untitled.obj`,
+                        // `${that.publicPath}male02/male02.obj`,
+                        // `${that.publicPath}GTR.obj`,
+                        // `${that.publicPath}lidar.obj`,
+                        // `${that.publicPath}oddysey_2021.obj`,
+                        function (obj) {
+                            // console.log(obj);
+                            obj.name = "car";
+                            // console.log(obj.toJSON());
+                            // console.log(JSON.stringify(obj.toJSON()));
+                            // if (that.car) {
+                            //     that.scene.remove(that.car);
+                            // }
+
+                            for (let i = 0; i < obj.children.length; i++) {
+                                // obj.children[i].position.set(0, -135, 0);
+                                // obj.children[i].position.set(0, -30, 0);
+                                obj.children[i].position.set(0, 0, 0);
+                                // obj.children[i].scale.set(30, 30, 30);
+                                obj.children[i].rotation.set(
+                                    (-90 * Math.PI) / 180,
+                                    0,
+                                    (-180 * Math.PI) / 180
+                                );
+                                // obj.children[i].transparent.set(0.5);
+                                // that.transformControls.attach(obj.children[i]);
+                            }
+                            // that.transformControls.attach(that.cube);
+                            // console.log(obj.children[0].transparent);
+
+                            // that.transformControls.attach(obj);
+                            // that.car = obj;
+                            that.scene.add(obj); //返回的组对象插入场景中
+                            // this.camera.lookAt(obj);
+
+                            // loading.close();
+                            // that.camera.lookAt(obj.position);
+                            // 加载后操作
+                            // obj.children[0].scale.set(200, 200, 200); //缩放球体网格模型
+                            // 通过调节参数,地球表面的凹凸感更强
+                            // obj.children[0].material.normalScale.set(3, 3);
+                            // obj.children[0].position.set(100, 0, 0);
+                        }
+                    );
+                }
+            );
+        },
+        // 初始化车模型
+        initCar(model) {
+            // const loading = this.$loading({
+            //     lock: true,
+            //     text: "模型加载中,请稍等...",
+            //     // spinner: "el-icon-loading",
+            //     background: "rgba(0, 0, 0, 0.2)",
+            // });
+
+            // const car = this.scene.getObjectByName("car");
+            // console.log(car);
+            // console.log(this.scene);
+            // if (car) {
+            //     this.scene.remove(car);
+            // }
+
+            // console.log(type);
+
+            // if (type === "2") {
+            //     this.initCar1();
+            // } else if (type === "f") {
+            //     this.initCar2();
+            // } else if (type === "7") {
+            //     this.initCar3();
+            // } else {
+            //     this.initCar1();
+            // }
+
+            // console.log(model);
+            if (!model) return;
+            if (!model.includes(".glb")) return;
+
+            this.initCar0(model);
+
+            // console.log(this.scene);
+
+            // var Loader = new MTLLoader(); //材质文件加载器
+            // var loader = new OBJLoader(); //obj加载器
+            // var that = this;
+
+            /* var loader1 = new THREE.BufferGeometryLoader();
+            loader1.load(`${that.publicPath}car.json`, function (geometry) {
+            // loader1.load(`${that.publicPath}Lidar.json`, function (geometry) {
+                // 控制台查看加载放回的threejs对象结构
+                console.log(geometry);
+                var material = new THREE.MeshLambertMaterial({
+                    color: 0x0000ff,
+                }); //材质对象Material
+                var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
+                that.scene.add(mesh); //网格模型添加到场景中
+            }); */
+
+            // 没有材质文件,系统自动设置Phong网格材质
+            // loader.load("../../../assets/common/image/oddysey_2021.obj", function (obj) {
+            // loader.load("../../../assets/common/image/tree.obj", function (obj) {
+            // loader.load(`${that.publicPath}oddysey_2021.obj`, function (obj) {
+            // loader.load(`${that.publicPath}GTR.obj`, function (obj) {
+            /* loader.load(`${that.publicPath}Lidar.obj`, function (obj) {
+                if (that.car) {
+                    that.scene.remove(that.car);
+                }
+                console.log(666);
+                // console.log(JSON.stringify(obj.toJSON()));
+                that.car = obj;
+                // 控制台查看返回结构:包含一个网格模型Mesh的组Group
+                console.log(obj);
+                // loading.close();
+                // 查看加载器生成的材质对象:MeshPhongMaterial
+                // console.log(obj.children[0].material);
+                that.scene.add(obj);
+                for (let i = 0; i < obj.children.length; i++) {
+                    obj.children[i].position.set(0, 100, 0);
+                    obj.children[i].scale.set(20, 20, 20);
+                    // obj.children[i].layers.set(1);
+                }
+            }); */
+        },
+        // 判断是否继续加载car,为了应对上一个car正在加载中的时候,用户选择了新的car
+        needCar() {
+            if (this.carModel != this.lastCarModel) {
+                this.lastCarModel = this.carModel;
+                this.initCar(this.carModel);
+            }
+        },
+        // 初始化
+        init() {
+            this.initScene();
+            this.initCamera();
+            this.initRenderer();
+            this.initTransform();
+            this.initLight();
+            // this.initDragControls();
+
+            this.controls = new OrbitControls(
+                this.camera,
+                // this.renderer.domElement
+                document.getElementById("container")
+            ); //创建控件对象
+            this.controls.minDistance = 30;
+            this.controls.maxDistance = 720;
+            this.controls.update();
+            // this.controls.target = this.cube.position
+            // this.controls.addEventListener("change", (e) => {
+            //     // console.log(document.getElementById("container"));
+
+            //     console.log(this.renderer.domElement);
+            //     console.log(document.querySelector('#container canvas'));
+            //     // console.log(this.controls.getAzimuthalAngle(),this.controls.getPolarAngle());
+            // });
+        },
+        animate() {
+            this.raf = requestAnimationFrame(this.animate);
+            // this.renderer.render(this.scene, this.camera);
+            if (this.transformControls) {
+                this.transformControls.update();
+            }
+
+            this.controls.update();
+
+            // this.renderer.setClearColor("#272727");
+
+            // this.renderer.setViewport(
+            //     0,
+            //     0,
+            //     this.container.clientWidth,
+            //     this.container.clientHeight
+            // );
+
+            this.renderer.render(this.scene, this.camera);
+            return;
+
+            // inset scene
+
+            // this.renderer.setClearColor(0xdddddd, 1);
+            // this.renderer.setClearColor(0xffffff, 0);
+            // this.renderer.setClearAlpha(0);
+
+            this.renderer.clearDepth(); // important!
+            // this.scene2.background = new THREE.Color('hsla(100%, 100%, 100%, 0)');
+            // this.scene2.background = new THREE.Color('rgba(255, 255, 255, 0)');
+            // this.scene2.background = new THREE.Color('transparent');
+            // this.scene2.background = 0xffffff;
+
+            this.renderer.setScissorTest(true);
+            // this.renderer.setClearColor(0xffffff, 0.3);
+            // this.renderer.clearColor();
+            // this.renderer.setClearAlpha(0);
+
+            let insetWidth = this.container.clientWidth / 6; // square
+            let insetHeight = this.container.clientHeight / 6;
+
+            let x = insetHeight / 45;
+            let y = insetHeight * 4.2;
+
+            this.renderer.setScissor(x, y, insetWidth, insetHeight);
+
+            this.renderer.setViewport(x, y, insetWidth, insetHeight);
+
+            this.camera2.position.copy(this.camera.position);
+            this.camera2.quaternion.copy(this.camera.quaternion);
+
+            this.renderer.render(this.scene2, this.camera2);
+            // this.renderer.setClearAlpha(0);
+            // this.scene2.background = null;
+            this.renderer.setScissorTest(false);
+        },
+        onWindowResize() {
+            this.camera.aspect =
+                this.container.clientWidth / this.container.clientHeight;
+            this.camera.updateProjectionMatrix();
+
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+
+            let insetWidth = this.container.clientWidth / 6; // square
+            let insetHeight = this.container.clientHeight / 6;
+
+            this.camera2.aspect = insetWidth / insetHeight;
+            this.camera2.updateProjectionMatrix();
+        },
+        go() {
+            this.container = document.getElementById("container");
+            this.init();
+            this.animate();
+
+            window.addEventListener("resize", this.onWindowResize);
+        },
+        /**
+         * 每编辑一个传感器则重新生成对应的物体
+         * @param {*} type 传感器类型
+         * @param {*} index 该传感器所在数组下标
+         * @param {*} onlyClear 是否只用来清空效果
+         */
+        reset(type, index = -1, onlyClear = false) {
+            this.canDrag = true;
+
+            if (this.cacheList.length > 0) {
+                this.scene.remove(...this.cacheList);
+                this.cacheList.forEach((i) => {
+                    this.clearCache(i);
+                });
+                this.cacheList = [];
+            }
+
+            this.sensor = null;
+
+            const obj1 = this.scene.getObjectByName("cube");
+            if (obj1) {
+                this.scene.remove(obj1);
+            }
+
+            this.xAngle = 0;
+            this.yAngle = 0;
+            this.zAngle = 0;
+
+            if (this.dragControls) {
+                this.dragControls.deactivate();
+                this.dragControls.dispose();
+                this.dragControls = null;
+            }
+
+            if (this.transformControls) {
+                this.transformControls.detach();
+            }
+
+            // onlyClear若为true,表示只清除当前展示的东西,没有可增加展示的
+            if (onlyClear) return;
+
+            let z = +this.$parent.formA.sensorX || 0;
+            let x = +this.$parent.formA.sensorY || 0;
+            let y = +this.$parent.formA.sensorZ || 0;
+            let h = +this.$parent.formA.sensorP || 0;
+            let p = +this.$parent.formA.sensorR || 0;
+            let r = +this.$parent.formA.sensorH || 0;
+
+            x = Math.floor(x / this.rate);
+            y = Math.floor(y / this.rate);
+            z = Math.floor(z / this.rate);
+
+            if (type != "gps") {
+                this.initContent(
+                    { x, y, z },
+                    {
+                        // x: (-90 * Math.PI) / 180,
+                        x: 0,
+                        y: 0,
+                        z: 0,
+                    },
+                    type,
+                    index,
+                    true
+                );
+            }
+
+            setTimeout(() => {
+                this.initSensor({ x, y, z }, type, true);
+
+                const obj = this.scene.getObjectByName("cube");
+
+                if (!obj) return;
+
+                obj.position.x = x;
+                obj.position.y = y;
+                obj.position.z = z;
+
+                // obj.rotateX(0 * -1);
+                // obj.rotateX((h * Math.PI) / 180);
+                // this.xAngle = ((h - 90) * Math.PI) / 180;
+                this.xAngle = (h * Math.PI) / 180;
+
+                // obj.rotateY(0 * -1);
+                // obj.rotateY((p * Math.PI) / 180);
+                this.yAngle = (p * Math.PI) / 180;
+
+                // obj.rotateZ(0 * -1);
+                // obj.rotateZ((r * Math.PI) / 180);
+                this.zAngle = (r * Math.PI) / 180;
+                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+            }, 0);
+        },
+        // 初始化传感器
+        initSensor(
+            pos = { x: 0, y: 0, z: 0 },
+            type = "camera",
+            canMove = false
+        ) {
+            let Loader = new MTLLoader(); //材质文件加载器
+            let loader = new OBJLoader(); //obj加载器
+            let that = this;
+            let mtlUrl = "";
+            let objUrl = "";
+
+            if (type === "ogt1") {
+                var loader2 = new THREE.ObjectLoader();
+                loader2.load(
+                    `${that.publicPath}sensor/ogt/ogt.json`,
+                    function (obj) {
+                        // console.log(obj);
+                        that.cacheList.push(obj);
+                        for (let i = 0; i < obj.children.length; i++) {
+                            obj.children[i].scale.set(0.2, 0.2, 0.2);
+                        }
+                        obj.position.set(pos.x, pos.y, pos.z);
+                        obj.name = "ogt";
+                        if (canMove) {
+                            that.sensor = obj;
+                        }
+                        that.scene.add(obj);
+                    }
+                );
+                return;
+            }
+
+            if (type === "ogt1" && this.ogt) {
+                setTimeout(() => {
+                    let obj1 = this.ogt;
+                    // for (let i = 0; i < obj1.children.length; i++) {
+                    //     obj1.children[i].scale.set(0.2, 0.2, 0.2);
+                    // }
+                    obj1.position.set(pos.x, pos.y, pos.z);
+
+                    if (canMove) {
+                        that.sensor = obj1;
+                    }
+                    obj1.name = "ogt";
+                    // console.log(that.scene);
+
+                    that.scene.add(obj1); //返回的组对象插入场景中
+                }, 600);
+                return;
+            }
+
+            if (type === "camera") {
+                mtlUrl = `${that.publicPath}sensor/camera/camera.mtl`;
+                objUrl = `${that.publicPath}sensor/camera/camera.obj`;
+            } else if (type === "ogt") {
+                mtlUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.mtl`;
+                objUrl = `${that.publicPath}sensor/ogt/millimeter_wave_radar.obj`;
+            } else if (type === "lidar") {
+                mtlUrl = `${that.publicPath}sensor/lidar/LIDAR.mtl`;
+                objUrl = `${that.publicPath}sensor/lidar/LIDAR.obj`;
+            } else if (type === "gps") {
+                mtlUrl = `${that.publicPath}sensor/gps/lidar.mtl`;
+                objUrl = `${that.publicPath}sensor/gps/lidar.obj`;
+            }
+
+            let loading = null;
+            if (type === "ogt") {
+                // loading = this.$loading({
+                //     lock: true,
+                //     text: "模型加载中,请稍等...",
+                //     // spinner: "el-icon-loading",
+                //     background: "rgba(0, 0, 0, 0.2)",
+                // });
+                // showFullScreenLoading();
+                this.$emit('loadingDone', 'carStart');
+                // this.isLoading = true;
+            }
+
+            Loader.load(
+                mtlUrl,
+                function (materials) {
+                    // console.log(materials);
+                    loader.setMaterials(materials);
+                    loader.load(objUrl, function (obj) {
+                        // console.log(obj);
+                        // console.log(obj.clone());
+                        // console.log(JSON.stringify(obj.toJSON()));
+                        // if (type === "ogt" && !that.ogt) {
+                        //     that.ogt = obj.clone();
+                        // }
+
+                        if (type === "ogt") {
+                            // loading.close();
+                            // tryHideFullScreenLoading();
+                            that.$emit('loadingDone', 'carDone');
+                            // that.isLoading = false;
+                        }
+
+                        that.cacheList.push(obj);
+                        for (let i = 0; i < obj.children.length; i++) {
+                            if (type === "camera") {
+                                // let scale = 0.8 * that.scale;
+                                obj.children[i].scale.set(0.8, 0.8, 0.8);
+                            } else if (type === "ogt") {
+                                obj.children[i].scale.set(0.2, 0.2, 0.2);
+                            } else if (type === "lidar") {
+                                obj.children[i].scale.set(0.1, 0.1, 0.1);
+                            } else if (type === "gps") {
+                                obj.children[i].scale.set(40, 40, 40);
+                            }
+                            // obj.children[i].position.set(0, 0, 0);
+                            // obj.children[i].geometry.translate(pos.x, pos.y, pos.z);
+                            // obj.children[i].position.set(pos.x, pos.y, pos.z);
+                            // obj.children[i].geometry.translate(-pos.x, -pos.y, -pos.z);
+                            // obj.children[i].scale.set(30, 30, 30);
+                        }
+                        obj.position.set(pos.x, pos.y, pos.z);
+                        // obj.geometry.translate.set(pos.x, pos.y, pos.z);
+
+                        if (canMove) {
+                            that.sensor = obj;
+
+                            if (type === "gps" && that.transformControls) {
+                                that.transformControls.attach(that.sensor);
+                            }
+                        }
+
+                        that.scene.add(obj); //返回的组对象插入场景中
+                    });
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    if (type === "ogt") {
+                        // loading.close();
+                        // tryHideFullScreenLoading();
+                        that.$emit('loadingDone', 'carDone');
+                        // that.isLoading = false;
+                    }
+                    console.error(error);
+                }
+            );
+        },
+        // 初始化已保存过的传感器
+        initContentToShow(r, position = { x: 0, y: 0, z: 0 }, rotation, type) {
+            var cubeGeometry = new THREE.ConeGeometry(
+                r || 45 * this.scale,
+                150 * this.scale,
+                4,
+                1,
+                false
+            );
+            cubeGeometry.translate(0, -75 * this.scale, 0);
+
+            let obj = {
+                transparent: true,
+                opacity: 0.3,
+                // emissive: 0xfaff72,
+                lightMapIntensity: 0.1,
+                color: 0x4c4c4c,
+            };
+
+            if (type === "camera") {
+                obj.emissive = 0x000080;
+                // obj.color = 0xfaff72;
+            } else if (type === "ogt") {
+                obj.emissive = 0x008000;
+                // obj.color = 0x33cc66;
+            } else if (type === "lidar") {
+                // obj.emissive = 0xff0000;
+                // obj.color = 0xcc3366;
+                obj.emissive = 0xff4500;
+            } else if (type === "gps") {
+                // obj.emissive = 0xfaff72;
+                // obj.color = 0x3366cc;
+                obj.emissive = 0x8a2be2;
+            }
+
+            var cubeMaterial = new THREE.MeshLambertMaterial(obj);
+            // var cubeMaterial = new THREE.MeshBasicMaterial(obj);
+
+            var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
+            cube.name = this.geometryName || "cubeA";
+            cube.rotateX((-90 * Math.PI) / 180);
+
+            if (position) {
+                cube.position.x = position.x;
+                cube.position.y = position.y;
+                cube.position.z = position.z;
+            }
+            if (rotation) {
+                // cube.rotateX(0 * -1);
+                // cube.rotateX((rotation.x * Math.PI) / 180);
+                // cube.rotateY(0 * -1);
+                // cube.rotateY((rotation.y * Math.PI) / 180);
+                // cube.rotateZ(0 * -1);
+                // cube.rotateZ((rotation.z * Math.PI) / 180);
+
+                cube.rotation.set(
+                    ((rotation.x - 90) * Math.PI) / 180,
+                    (rotation.y * Math.PI) / 180,
+                    (rotation.z * Math.PI) / 180
+                );
+            }
+            this.cacheList.push(cube);
+            this.scene.add(cube);
+        },
+        // 生成一种类型的全部显示器
+        showSensor(sensor, type) {
+            if (sensor && Array.isArray(sensor) && sensor.length > 0) {
+                sensor.forEach((item, index) => {
+                    // 若是传感器没保存配置项,则不展示
+                    if (
+                        item.sensorX == null &&
+                        item.sensorY == null &&
+                        item.sensorZ == null
+                    ) {
+                        return;
+                    }
+
+                    if (type != "gps") {
+                        this.initContent(
+                            {
+                                z: Math.floor(item.sensorX / this.rate || 0),
+                                x: Math.floor(item.sensorY / this.rate || 0),
+                                y: Math.floor(item.sensorZ / this.rate || 0),
+                            },
+                            {
+                                x: +item.sensorP,
+                                y: +item.sensorR,
+                                z: +item.sensorH,
+                            },
+                            type,
+                            index,
+                            false
+                        );
+                    }
+
+                    /* this.initContentToShow(
+                        null,
+                        {
+                            z: Math.floor(item.sensorX / this.rate || 0),
+                            x: Math.floor(item.sensorY / this.rate || 0),
+                            y: Math.floor(item.sensorZ / this.rate || 0),
+                        },
+                        {
+                            x: +item.sensorP,
+                            y: +item.sensorR,
+                            z: +item.sensorH,
+                        },
+                        type
+                    ); */
+
+                    this.initSensor(
+                        {
+                            z: Math.floor(item.sensorX / this.rate || 0),
+                            x: Math.floor(item.sensorY / this.rate || 0),
+                            y: Math.floor(item.sensorZ / this.rate || 0),
+                        },
+                        type,
+                        false
+                    );
+                });
+            }
+        },
+        // 显示全部
+        showAll() {
+            // 避免重复加载所有传感器
+            if (!this.canDrag) return;
+
+            this.canDrag = false;
+            if (this.cacheList.length > 0) {
+                this.scene.remove(...this.cacheList);
+                this.cacheList = [];
+            }
+
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+            }
+
+            if (this.dragControls) {
+                this.dragControls.deactivate();
+                this.dragControls.dispose();
+                this.dragControls = null;
+            }
+
+            if (this.transformControls) {
+                this.transformControls.detach();
+            }
+
+            // console.log(this.configList);
+
+            this.showSensor(this.configList.camera, "camera");
+            this.showSensor(this.configList.ogt, "ogt");
+            this.showSensor(this.configList.lidar, "lidar");
+            this.showSensor(this.configList.gps, "gps");
+        },
+        removeScene() {
+            this.clearScene();
+        },
+        clearCache(item) {
+            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
+            if (item.material && item.material.dispose) item.material.dispose();
+        },
+        clearScene() {
+            this.removeObj(this.scene);
+        },
+        removeObj(obj) {
+            let arr = obj.children.filter((x) => x);
+            arr.forEach((item) => {
+                if (item.children.length) {
+                    this.removeObj(item);
+                } else {
+                    this.clearCache(item);
+                    item.clear();
+                }
+            }),
+                obj.clear();
+            arr = null;
+        },
+        showTX() {
+            var pointsArr1 = [
+                // [5, -1, 33],
+                // [5, 11, 33],
+                // [-5, -1, 33],
+                // [-5, 11, 33],
+                [0, 0, 0],
+                [10, -11, 66],
+                [30, 31, 66],
+                [-20, -21, 66],
+                [-30, 21, 66],
+            ];
+
+            let x = 0,
+                y = 0,
+                z = 0;
+
+            let r = 60;
+            z = r;
+
+            let a = 120,
+                b = 40;
+
+            x = Math.tan(THREE.MathUtils.degToRad(a / 2)) * r;
+            y = Math.tan(THREE.MathUtils.degToRad(b / 2)) * r;
+
+            let x1 = 0,
+                y1 = 0,
+                z1 = 0;
+
+            let r1 = 20;
+            z1 = r1;
+
+            x1 = Math.tan(THREE.MathUtils.degToRad(a / 2)) * r1;
+            y1 = Math.tan(THREE.MathUtils.degToRad(b / 2)) * r1;
+
+            let pointsArr2 = [
+                [0, 0, 0],
+                // [-x1, y1, z1],
+                // [-x1, -y1, z1],
+                // [x1, y1, z1],
+                // [x1, -y1, z1],
+                [-x, y, z],
+                [-x, -y, z],
+                [x, y, z],
+                [x, -y, z],
+            ];
+
+            let x2 = 0,
+                y2 = 0,
+                z2 = 0,
+                x3 = 0,
+                y3 = 0,
+                z3 = 0,
+                x4 = 0,
+                y4 = 0,
+                z4 = 0,
+                x6 = 0,
+                y6 = 0,
+                z6 = 0,
+                r2 = 90,
+                a2 = 30,
+                b2 = 60,
+                c2 = 90,
+                d2 = 60;
+
+            x2 = Math.tan(THREE.MathUtils.degToRad(a2 / 2)) * r2;
+            y2 = Math.tan(THREE.MathUtils.degToRad(b2 / 2)) * r2;
+            x3 = Math.tan(THREE.MathUtils.degToRad(c2 / 2)) * r2;
+            y3 = Math.tan(THREE.MathUtils.degToRad(d2 / 2)) * r2;
+
+            let pointsArr3 = [
+                [0, 0, 0],
+                [-x2, y3, r2],
+                [-x3, -y3, r2],
+                [x2, y2, r2],
+                [x3, -y2, r2],
+            ];
+            // console.log(pointsArr3);
+
+            let points = pointsArr3.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let material = new THREE.MeshPhongMaterial({
+                color: 0x2c85e1,
+                shininess: 60,
+                specular: 0x2c85e1,
+                // opacity: 0.5,
+                // transparent: true,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            let tixingMesh = new THREE.Mesh(tixing, material);
+            this.scene.add(tixingMesh);
+            // console.log(tixingMesh);
+
+            /* let vertices = [];
+
+            vertices.push(-155, 24, 90);
+            vertices.push(-63, -24, 90);
+            vertices.push(155, 91, 90);
+            vertices.push(63, -90, 90);
+
+            const geometry1 = new THREE.BufferGeometry();
+            geometry1.setAttribute(
+                "position",
+                new THREE.Float32BufferAttribute(vertices, 3)
+            );
+
+            const material1 = new THREE.PointsMaterial({
+                color: 0xff0000,
+                size: 10,
+                fog: false,
+            });
+
+            const points1 = new THREE.Points(geometry1, material1);
+
+            this.scene.add(points1); */
+
+            // if (this.transformControls) {
+            //     this.transformControls.attach(tixingMesh);
+            // }
+
+            // const a = new THREE.Vector3(1, 1, 0);
+            // const b = new THREE.Vector3(0, 0, 0);
+            // const d = a.distanceTo(b);
+            // const e = a.angleTo(b);
+
+            // console.log(d, e, (e * 180) / Math.PI, THREE.MathUtils.radToDeg(e));
+
+            // (value * Math.PI) / 180;
+        },
+        a() {
+            let line, thresholdLine, segments, thresholdSegments;
+            let renderer, scene, scene2, camera, camera2, controls;
+            let raycaster, sphereInter, sphereOnLine;
+            let matLine, matThresholdLine;
+
+            // viewport
+            let insetWidth;
+            let insetHeight;
+
+            const pointer = new THREE.Vector2(Infinity, Infinity);
+
+            init();
+            animate();
+
+            function init() {
+                let container = document.getElementById("container");
+                renderer = new THREE.WebGLRenderer({
+                    antialias: true,
+                    alpha: true,
+                });
+                renderer.setPixelRatio(window.devicePixelRatio);
+                renderer.setClearColor(0x00ff00);
+                renderer.setSize(container.clientWidth, container.clientHeight);
+                container.appendChild(renderer.domElement);
+
+                scene = new THREE.Scene();
+
+                camera = new THREE.PerspectiveCamera(
+                    40,
+                    container.clientWidth / container.clientHeight,
+                    1,
+                    1000
+                );
+                camera.position.set(-40, 0, 60);
+
+                camera2 = new THREE.PerspectiveCamera(40, 1, 1, 1000);
+                camera2.position.copy(camera.position);
+
+                controls = new OrbitControls(camera, renderer.domElement);
+                controls.minDistance = 10;
+                controls.maxDistance = 500;
+
+                // raycaster = new THREE.Raycaster();
+                // raycaster.params.Line2 = {};
+                // raycaster.params.Line2.threshold = 0;
+
+                // const sphereGeometry = new THREE.SphereGeometry(0.25);
+                // const sphereInterMaterial = new THREE.MeshBasicMaterial({
+                //     color: 0xff0000,
+                //     depthTest: false,
+                // });
+                // const sphereOnLineMaterial = new THREE.MeshBasicMaterial({
+                //     color: 0x00ff00,
+                //     depthTest: false,
+                // });
+
+                // sphereInter = new THREE.Mesh(
+                //     sphereGeometry,
+                //     sphereInterMaterial
+                // );
+                // sphereOnLine = new THREE.Mesh(
+                //     sphereGeometry,
+                //     sphereOnLineMaterial
+                // );
+                // sphereInter.visible = false;
+                // sphereOnLine.visible = false;
+                // sphereInter.renderOrder = 10;
+                // sphereOnLine.renderOrder = 10;
+                // scene.add(sphereInter);
+                // scene.add(sphereOnLine);
+
+                // Position and THREE.Color Data
+
+                const positions = [];
+                const colors = [];
+                const points = [];
+                for (let i = -50; i < 50; i++) {
+                    const t = i / 3;
+                    points.push(
+                        new THREE.Vector3(
+                            t * Math.sin(2 * t),
+                            t,
+                            t * Math.cos(2 * t)
+                        )
+                    );
+                }
+
+                const spline = new THREE.CatmullRomCurve3(points);
+                const divisions = Math.round(3 * points.length);
+                const point = new THREE.Vector3();
+                const color = new THREE.Color();
+
+                for (let i = 0, l = divisions; i < l; i++) {
+                    const t = i / l;
+
+                    spline.getPoint(t, point);
+                    positions.push(point.x, point.y, point.z);
+
+                    color.setHSL(t, 1.0, 0.5);
+                    colors.push(color.r, color.g, color.b);
+                }
+
+                const lineGeometry = new LineGeometry();
+                lineGeometry.setPositions(positions);
+                lineGeometry.setColors(colors);
+
+                const segmentsGeometry = new LineSegmentsGeometry();
+                segmentsGeometry.setPositions(positions);
+                segmentsGeometry.setColors(colors);
+
+                matLine = new LineMaterial({
+                    color: 0xffffff,
+                    linewidth: 1, // in world units with size attenuation, pixels otherwise
+                    worldUnits: true,
+                    vertexColors: true,
+
+                    //resolution:  // to be set by renderer, eventually
+                    alphaToCoverage: true,
+                });
+
+                matThresholdLine = new LineMaterial({
+                    color: 0xffffff,
+                    linewidth: matLine.linewidth, // in world units with size attenuation, pixels otherwise
+                    worldUnits: true,
+                    // vertexColors: true,
+                    transparent: true,
+                    opacity: 0.2,
+                    depthTest: false,
+                    visible: false,
+                    //resolution:  // to be set by renderer, eventually
+                });
+
+                // segments = new LineSegments2(segmentsGeometry, matLine);
+                // segments.computeLineDistances();
+                // segments.scale.set(1, 1, 1);
+                // scene.add(segments);
+                // segments.visible = false;
+
+                // thresholdSegments = new LineSegments2(
+                //     segmentsGeometry,
+                //     matThresholdLine
+                // );
+                // thresholdSegments.computeLineDistances();
+                // thresholdSegments.scale.set(1, 1, 1);
+                // scene.add(thresholdSegments);
+                // thresholdSegments.visible = false;
+
+                line = new Line2(lineGeometry, matLine);
+                line.computeLineDistances();
+                line.scale.set(1, 1, 1);
+                // scene.add(line);
+
+                var cubeGeometry = new THREE.ConeGeometry(75, 150, 4, 1, false);
+                cubeGeometry.translate(0, -75, 0);
+
+                let obj = {
+                    color: 0x4c4c4c,
+                    // wireframe: true,
+                    transparent: true,
+                    opacity: 0.3,
+                    // ambient: 0x00ff00,
+                    emissive: 0x0000ff,
+                    lightMapIntensity: 0.1,
+                };
+
+                var cubeMaterial = new THREE.MeshLambertMaterial(obj);
+                let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
+                scene.add(cube);
+
+                // thresholdLine = new Line2(lineGeometry, matThresholdLine);
+                // thresholdLine.computeLineDistances();
+                // thresholdLine.scale.set(1, 1, 1);
+                // scene.add(thresholdLine);
+
+                const geo = new THREE.BufferGeometry();
+                geo.setAttribute(
+                    "position",
+                    new THREE.Float32BufferAttribute(positions, 3)
+                );
+                geo.setAttribute(
+                    "color",
+                    new THREE.Float32BufferAttribute(colors, 3)
+                );
+
+                //
+                document.addEventListener("pointermove", onPointerMove);
+                window.addEventListener("resize", onWindowResize);
+                onWindowResize();
+            }
+
+            function onWindowResize() {
+                camera.aspect = container.clientWidth / container.clientHeight;
+                camera.updateProjectionMatrix();
+
+                renderer.setSize(container.clientWidth, container.clientHeight);
+
+                insetWidth = container.clientHeight / 6; // square
+                insetHeight = container.clientHeight / 6;
+
+                camera2.aspect = insetWidth / insetHeight;
+                camera2.updateProjectionMatrix();
+            }
+
+            function onPointerMove(event) {
+                pointer.x = (event.clientX / container.clientWidth) * 2 - 1;
+                pointer.y = -(event.clientY / container.clientHeight) * 2 + 1;
+            }
+
+            function animate() {
+                requestAnimationFrame(animate);
+
+                // main scene
+
+                renderer.setClearColor(0xdddddd);
+
+                renderer.setViewport(
+                    0,
+                    0,
+                    container.clientWidth,
+                    container.clientHeight
+                );
+
+                // raycaster.setFromCamera(pointer, camera);
+
+                // const obj = line.visible ? line : segments;
+                // const intersects = raycaster.intersectObject(obj, true);
+
+                // if (intersects.length > 0) {
+                //     sphereInter.visible = true;
+                //     sphereOnLine.visible = true;
+                //     sphereInter.position.copy(intersects[0].point);
+                //     sphereOnLine.position.copy(intersects[0].pointOnLine);
+                //     const i = intersects[0].faceIndex;
+                //     const colors =
+                //         obj.geometry.getAttribute("instanceColorStart");
+                //     const color = new THREE.Color().setRGB(
+                //         colors.getX(i),
+                //         colors.getY(i),
+                //         colors.getZ(i)
+                //     );
+                //     sphereInter.material.color.copy(
+                //         color.clone().offsetHSL(0.3, 0, 0)
+                //     );
+                //     sphereOnLine.material.color.copy(
+                //         color.clone().offsetHSL(0.7, 0, 0)
+                //     );
+                //     renderer.domElement.style.cursor = "crosshair";
+                // } else {
+                //     sphereInter.visible = false;
+                //     sphereOnLine.visible = false;
+                //     renderer.domElement.style.cursor = "";
+                // }
+
+                // renderer will set this eventually
+                matLine.resolution.set(
+                    container.clientWidth,
+                    container.clientHeight
+                ); // resolution of the viewport
+                matThresholdLine.resolution.set(
+                    container.clientWidth,
+                    container.clientHeight
+                ); // resolution of the viewport
+
+                // gpuPanel.startQuery();
+                renderer.render(scene, camera);
+                // gpuPanel.endQuery();
+
+                // inset scene
+
+                renderer.setClearColor(0xff00ff, 1);
+
+                renderer.clearDepth(); // important!
+
+                renderer.setScissorTest(true);
+
+                renderer.setScissor(20, 20, insetWidth, insetHeight);
+
+                renderer.setViewport(20, 20, insetWidth, insetHeight);
+
+                camera2.position.copy(camera.position);
+                camera2.quaternion.copy(camera.quaternion);
+
+                // renderer will set this eventually
+                matLine.resolution.set(insetWidth, insetHeight); // resolution of the inset viewport
+
+                scene2 = new THREE.Scene();
+
+                var cubeGeometry = new THREE.ConeGeometry(75, 150, 6, 1, false);
+                cubeGeometry.translate(0, -75, 0);
+
+                let obj = {
+                    color: 0x4c4c4c,
+                    // wireframe: true,
+                    // transparent: true,
+                    // opacity: 0.3,
+                    // ambient: 0x00ff00,
+                    // emissive: 0x00ff00,
+                    lightMapIntensity: 0.1,
+                };
+
+                var cubeMaterial = new THREE.MeshLambertMaterial(obj);
+                let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
+                scene2.add(cube);
+
+                renderer.render(scene2, camera2);
+
+                renderer.setScissorTest(false);
+            }
+        },
+    },
+
+    mounted() {
+        // this.a();
+        this.go();
+        // this.showTX();
+
+        // console.log(THREE.Loader.Handlers.add);
+        // THREE.Loader.Handlers.add( /\.dds$/i, new DDSLoader() );
+
+        // console.log(new THREE.LoadingManager()); RGBELoader
+        // let dds = new THREE.LoadingManager();
+        // dds.addHandler(/\.dds$/i, new DDSLoader());
+        // dds.addHandler(/\.rgb$/i, new RGBELoader());
+        // console.log(dds);
+
+        // THREE.DefaultLoadingManager.addHandler(/\.dds$/i, new DDSLoader());
+        // THREE.DefaultLoadingManager.addHandler(/\.rgb$/i, new RGBELoader());
+        // console.log(THREE.DefaultLoadingManager);
+
+        // console.log(THREE.LoadingManager.addHandler);
+        // THREE.Loader.Handlers.add( /\.dds$/i, new DDSLoader());
+        // THREE.Loader.addHandler(/\.dds$/i, new DDSLoader());
+        // THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
+        // THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
+
+        // if (!this.isAdd) {
+        //     setTimeout(() => {
+        //         this.initCar();
+        //     }, 270);
+        // }
+    },
+    destroyed() {
+        window.removeEventListener("resize", this.onWindowResize);
+        cancelAnimationFrame(this.raf);
+
+        if (this.renderer) {
+            this.renderer.renderLists.dispose();
+            this.renderer.dispose();
+            this.renderer.forceContextLoss();
+            this.renderer.domElement = null;
+            this.renderer.content = null;
+            this.renderer = null;
+        }
+
+        if (this.dragControls) {
+            this.dragControls.deactivate();
+            this.dragControls.dispose();
+            this.dragControls = null;
+        }
+
+        if (this.controls) {
+            this.controls.dispose();
+            this.controls = null;
+        }
+
+        if (this.transformControls) {
+            this.transformControls.detach();
+            this.transformControls.dispose();
+            this.transformControls = null;
+        }
+
+        if (this.cacheList.length > 0) {
+            // this.scene.remove(...this.cacheList);
+            this.cacheList = [];
+        }
+
+        this.clearScene();
+
+        this.scene = null;
+        this.scene2 = null;
+        this.camera = null;
+        this.camera2 = null;
+        this.light = null;
+        this.geometryName = null;
+        this.mesh = null;
+        this.cube = null;
+        this.container = null;
+        this.car = null;
+        this.cubeTexture = null;
+        this.raf = null;
+
+        THREE.Cache.clear();
+    },
+};
+</script>
+
+<style lang="less" scoped>
+#container {
+    width: 100%;
+    height: calc(100vh - 125px);
+}
+</style>

+ 13 - 5
src/views/modelLibrary/sensorModel.vue

@@ -30,6 +30,7 @@
                     @showDone="showDone"
                     @clearForm="addOne"
                     @inputChange="inputChange"
+                    @clearCube="clearCube"
                 ></form-camera>
                 <form-perfect-sensor
                     v-show="subPageActiveName === 2"
@@ -39,6 +40,7 @@
                     @showDone="showDone"
                     @clearForm="addOne"
                     @inputChange="inputChangeA"
+                    @clearCube="clearCube"
                 ></form-perfect-sensor>
                 <form-laser-radar
                     v-show="subPageActiveName === 3"
@@ -48,6 +50,7 @@
                     @showDone="showDone"
                     @clearForm="addOne"
                     @inputChange="inputChange"
+                    @clearCube="clearCube"
                 ></form-laser-radar>
                 <!-- <form-millimeter-wave-radar
                     v-show="subPageActiveName === 4"
@@ -218,7 +221,7 @@ export default {
             this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
             this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
             setTimeout(() => {
-                this.$refs.threeShow.initContent();
+                this.$refs.threeShow.initBSP();
             }, 0);
 
             this.curOne = {
@@ -246,7 +249,7 @@ export default {
             this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
             this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
             setTimeout(() => {
-                this.$refs.threeShow.initContent();
+                this.$refs.threeShow.initBSP();
             }, 0);
         },
         changeShare(form) {
@@ -278,7 +281,7 @@ export default {
             this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
             this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
             setTimeout(() => {
-                this.$refs.threeShow.initContent();
+                this.$refs.threeShow.initBSP();
             }, 0);
 
             this.curOne = {
@@ -325,13 +328,13 @@ export default {
         inputChange(data) {
             this.coordinate = data;
             setTimeout(() => {
-                this.$refs.threeShow.initContent();
+                this.$refs.threeShow.initBSP();
             }, 0);
         },
         inputChangeA(data) {
             this.coordinateA = data;
             setTimeout(() => {
-                this.$refs.threeShow.initContent();
+                this.$refs.threeShow.initBSP();
             }, 0);
         },
         loadingDone(done) {
@@ -341,6 +344,11 @@ export default {
                 this.loading = true;
             }
         },
+        clearCube() {
+            setTimeout(() => {
+                this.$refs.threeShow.initBSP(true);
+            }, 0);
+        }
     },
 
     // mounted() {},

+ 35 - 96
src/views/workManagement/autoRunProjectDetail.vue

@@ -1,31 +1,15 @@
 <template>
     <div>
-        <el-form
-            ref="form"
-            :model="form"
-            :rules="rules"
-            label-width="135px"
-            class="flexBox"
-        >
+        <el-form ref="form" :model="form" :rules="rules" label-width="135px" class="flexBox">
             <div class="formItemBox">
                 <el-form-item label="项目名称:" prop="projectName">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="30"
-                        v-autoTrim="{ obj: form, key: 'projectName' }"
-                        v-model="form.projectName"
-                    >
+                    <el-input placeholder="请输入" maxlength="120" v-autoTrim="{ obj: form, key: 'projectName' }"
+                        v-model="form.projectName">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="项目描述:" prop="projectDescribe">
-                    <el-input
-                        type="textarea"
-                        :autosize="{ minRows: 4, maxRows: 4 }"
-                        placeholder="请输入"
-                        maxlength="300"
-                        v-autoTrim="{ obj: form, key: 'projectDescribe' }"
-                        v-model="form.projectDescribe"
-                    >
+                    <el-input type="textarea" :autosize="{ minRows: 4, maxRows: 4 }" placeholder="请输入" maxlength="300"
+                        v-autoTrim="{ obj: form, key: 'projectDescribe' }" v-model="form.projectDescribe">
                     </el-input>
                 </el-form-item>
                 <!-- <el-form-item label="算法来源:" prop="algorithmType">
@@ -43,13 +27,8 @@
                     >
                 </el-form-item> -->
                 <el-form-item label="选择算法:" prop="algorithmArrayS">
-                    <el-cascader
-                        ref="cascaderA"
-                        v-model="form.algorithmArrayS"
-                        :options="algorithmList"
-                        :props="props"
-                        @change="algorithmSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderA" v-model="form.algorithmArrayS" :options="algorithmList" :props="props"
+                        @change="algorithmSelChange"></el-cascader>
                     <!-- <el-select v-model="form.algorithm">
                         <el-option
                             v-for="item in algorithmList"
@@ -61,13 +40,8 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="选择车辆:" prop="vehicleArrayS">
-                    <el-cascader
-                        ref="cascaderB"
-                        v-model="form.vehicleArrayS"
-                        :options="vehicleList"
-                        :props="props"
-                        @change="vehicleSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderB" v-model="form.vehicleArrayS" :options="vehicleList" :props="props"
+                        @change="vehicleSelChange"></el-cascader>
                     <!-- <el-select
                         v-model="form.vehicle"
                         @change="vehicleSelChange"
@@ -82,13 +56,8 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="选择场景:" prop="sceneArrayS">
-                    <el-cascader
-                        ref="cascaderC"
-                        v-model="form.sceneArrayS"
-                        :options="sceneList"
-                        :props="props"
-                        @change="sceneSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderC" v-model="form.sceneArrayS" :options="sceneList" :props="props"
+                        @change="sceneSelChange"></el-cascader>
                     <!-- <el-select v-model="form.scene" @change="sceneSelChange">
                         <el-option
                             v-for="item in sceneList"
@@ -99,31 +68,18 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="并行度:" prop="parallelism">
-                    <el-input
-                        :disabled="runDisabled"
-                        placeholder="请输入"
-                        maxlength="10"
-                        v-autoTrim="{ obj: form, key: 'parallelism' }"
-                        v-model="form.parallelism"
-                    >
+                    <el-input :disabled="runDisabled" placeholder="请输入" maxlength="10"
+                        v-autoTrim="{ obj: form, key: 'parallelism' }" v-model="form.parallelism">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="最大仿真时间(s):" prop="maxSimulationTime">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="10"
-                        v-autoTrim="{ obj: form, key: 'maxSimulationTime' }"
-                        v-model="form.maxSimulationTime"
-                    >
+                    <el-input placeholder="请输入" maxlength="10" v-autoTrim="{ obj: form, key: 'maxSimulationTime' }"
+                        v-model="form.maxSimulationTime">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="运行周期:" prop="operationCycle">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="60"
-                        v-autoTrim="{ obj: form, key: 'operationCycle' }"
-                        v-model="form.operationCycle"
-                    >
+                    <el-input placeholder="请输入" maxlength="60" v-autoTrim="{ obj: form, key: 'operationCycle' }"
+                        v-model="form.operationCycle">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="是否选择GPU:" prop="isChoiceGpu">
@@ -135,16 +91,12 @@
             <div class="tipBox">
                 <div class="tip tipA">
                     <!-- (传感器1:根据车辆自动带出;传感器2:根据车辆自动带出) -->
-                    <span
-                        v-for="item in sensors"
-                        :key="item"
-                        v-bind:class="{
-                            iconA: item === 'camera',
-                            iconB: item === 'ogt',
-                            iconC: item === 'lidar',
-                            iconE: item === 'gps',
-                        }"
-                    ></span>
+                    <span v-for="item in sensors" :key="item" v-bind:class="{
+                        iconA: item === 'camera',
+                        iconB: item === 'ogt',
+                        iconC: item === 'lidar',
+                        iconE: item === 'gps',
+                    }"></span>
                 </div>
                 <div class="tip">(场景数量:{{ sceneCount }})</div>
                 <!-- <div class="tip flexBox">
@@ -164,9 +116,7 @@
                     <!-- <div class="collapseInfo">运行周期规则查看</div> -->
                     <div class="collapseInfo">
                         <span>参考网址:</span>
-                        <a href="https://cron.qqe2.com" target="_blank"
-                            >https://cron.qqe2.com</a
-                        >
+                        <a href="https://cron.qqe2.com" target="_blank">https://cron.qqe2.com</a>
                     </div>
                 </el-collapse-item>
             </el-collapse>
@@ -174,18 +124,8 @@
 
         <div class="btns">
             <el-button type="primary" @click="save(false)">保存</el-button>
-            <el-button
-                type="primary"
-                @click="save(true)"
-                v-if="this.$route.query.id"
-                >另存为</el-button
-            >
-            <el-button
-                type="primary"
-                @click="save(false, true)"
-                :disabled="runDisabled"
-                >提交</el-button
-            >
+            <el-button type="primary" @click="save(true)" v-if="this.$route.query.id">另存为</el-button>
+            <el-button type="primary" @click="save(false, true)" :disabled="runDisabled">提交</el-button>
             <el-button type="primary" plain @click="cancel">取消</el-button>
         </div>
     </div>
@@ -517,28 +457,27 @@ export default {
             margin-top: 226px;
 
             .iconA {
-                background: url("../../assets/common/image/sensor/001.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/001.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconB {
-                background: url("../../assets/common/image/sensor/002.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/002.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconC {
-                background: url("../../assets/common/image/sensor/003.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/003.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconD {
-                background: url("../../assets/common/image/sensor/004.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/004.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconE {
-                background: url("../../assets/common/image/sensor/005.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/005.png") center no-repeat;
                 background-size: contain;
             }
 

+ 797 - 30
src/views/workManagement/components/threeProjectInfo.vue

@@ -12,6 +12,7 @@ import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";
 import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
 import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
 import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
+const ThreeBSP = require('three-js-csg')(THREE)
 // import {
 //     showFullScreenLoading,
 //     tryHideFullScreenLoading,
@@ -46,6 +47,29 @@ export default {
             // scale: 2, // 物体加载换算倍数
             // rate: 20, // 坐标换算倍数
             configList: {},
+            ogtObj: null,
+            loadingFull: false,
+            bsp: {
+                camera: {},
+                ogt: {},
+                lidar: {},
+            },
+            a1: null,
+            b1: null,
+            c1: null,
+            d1: null,
+            a2: null,
+            b2: null,
+            c2: null,
+            d2: null,
+            x1: null,
+            y1: null,
+            z1: null,
+            w1: null,
+            x2: null,
+            y2: null,
+            z2: null,
+            w2: null,
         };
     },
 
@@ -104,9 +128,9 @@ export default {
         // 场景
         initScene() {
             this.scene = new THREE.Scene();
-            let axes = new THREE.AxesHelper(1500);
+            let axes = new THREE.AxesHelper(12000);
             this.scene.add(axes);
-            const gridHelper = new THREE.GridHelper(1000, 100);
+            const gridHelper = new THREE.GridHelper(21000, 2100);
             gridHelper.material.opacity = 0.25;
             gridHelper.material.transparent = true;
             this.scene.add(gridHelper);
@@ -117,7 +141,7 @@ export default {
                 75,
                 this.container.clientWidth / this.container.clientHeight,
                 0.1,
-                1000
+                30000
             );
             this.camera.position.set(200, 200, 200);
             this.scene.add(this.camera);
@@ -257,6 +281,607 @@ export default {
                 );
             }
         },
+        initContent0(coordinate, vh, nf) {
+            let r = 0, deg = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示 
+            // 值为测试出来的预估值 超过的话ThreeBSP会报内存溢出
+            let maxR = 10000;
+
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = ((coordinate.nearDistance * a) / rate).toFixed();
+                } else {
+                    r = maxR - 3.3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovV;
+                if (coordinate.fovH >= 47 && coordinate.fovH <= 53 && coordinate.fovV == 30) {
+                    deg = 31;
+                }
+
+                if (deg == 80) deg = 79;
+                if (deg == 20) deg = 19;
+                if (deg % 10 === 0 && deg % 90 != 0) deg = deg + 0.7;
+            } else {
+                deg = coordinate.fovH;
+                if (deg >= 47 && deg <= 53) deg = 53;
+                if (deg >= 17 && deg <= 23) deg = 17;
+                if (deg >= 117 && deg <= 123) deg = 121;
+                if (deg >= 147 && deg <= 153) deg = 148;
+            }
+
+            // 180°时融合有问题
+            if (deg % 90 === 0) deg = deg - 0.5;
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg * 2 / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+            const sphere = new THREE.Mesh(geometry, material);
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI * 1 // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(phiStart);
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                }
+            }
+        },
+        /**
+         * 初始化ogt模型
+         * @param {*} coordinate 配置参数
+         * @param {*} vh v 垂直  h 水平
+         * @param {*} nf n 盲区距离  f 探测距离
+         */
+        initContentOgt(coordinate, vh, nf) {
+            let r = 0, deg = 0, deg1 = 0, rate = 1;
+            // let a = 100 / (this.scale / 0.6);
+            let a = 1;
+
+            let maxR = 10000;
+
+            // 10000为预估 超过的话ThreeBSP会报内存溢出
+            // 若探测距离超出最大值,则默认为10000,盲区距离按比例展示
+            if ((coordinate.farDistance * a) > maxR) {
+                if (nf === 'n') {
+                    rate = (coordinate.farDistance * a) / maxR;
+                    r = (coordinate.nearDistance * a) / rate;
+                } else {
+                    r = maxR - 3;
+                }
+            } else {
+                if (nf === 'n') {
+                    r = coordinate.nearDistance * a;
+                } else {
+                    r = coordinate.farDistance * a;
+                }
+            }
+
+            if (vh === 'v') {
+                deg = coordinate.fovVBottom;
+                deg1 = coordinate.fovVTop;
+            } else {
+                deg = coordinate.fovHRight;
+                deg1 = coordinate.fovHLeft;
+            }
+
+            // 180°时融合有问题
+            if ((deg + deg1) % 180 === 0) {
+                if (deg > 1) {
+                    deg = deg - 0.1;
+                } else {
+                    deg1 = deg1 - 0.1;
+                }
+            }
+
+
+            let phiStart = Math.PI * ((90 - deg) / 180),
+                phiLength = Math.PI * (deg / 180),
+                thetaStart = 0,
+                thetaLength = Math.PI;
+
+            const geometry = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere = new THREE.Mesh(geometry, material); // 右 下
+
+
+
+
+            let phiStart0 = Math.PI * (90 / 180),
+                phiLength0 = Math.PI * (deg1 / 180),
+                thetaStart0 = 0,
+                thetaLength0 = Math.PI;
+
+            const geometry0 = new THREE.SphereGeometry(
+                r, // radius — 球体半径,默认为1
+                32, // widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为32
+                16, // heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为16
+                phiStart0, // phiStart — 指定水平(经线)起始角度,默认值为0
+                phiLength0, // phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
+                thetaStart0, // thetaStart — 指定垂直(纬线)起始角度,默认值为0
+                thetaLength0 // thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI
+            );
+            const material0 = new THREE.MeshBasicMaterial({ color: 0xff00ff, side: THREE.DoubleSide });
+
+            const sphere0 = new THREE.Mesh(geometry0, material0); // 左 上
+
+
+
+            const geometry1 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material1 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle1 = new THREE.Mesh(geometry1, material1); // left top
+
+
+
+            const geometry2 = new THREE.CircleGeometry(
+                r, // radius — 圆形的半径,默认值为1
+                32, // segments — 分段(三角面)的数量,最小值为3,默认值为32
+                0, // thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
+                Math.PI // thetaLength — 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
+            );
+
+            const material2 = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
+            const circle2 = new THREE.Mesh(geometry2, material2); // right bottom
+
+
+
+            if (vh === 'v') {
+                sphere.rotateZ(Math.PI * 0.5);
+                sphere0.rotateZ(Math.PI * 0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateX(-phiStart - Math.PI);
+
+                if (nf === 'n') {
+                    this.x2 = new ThreeBSP(sphere);
+                    this.y2 = new ThreeBSP(circle1);
+                    this.z2 = new ThreeBSP(circle2);
+                    this.w2 = new ThreeBSP(sphere0);
+                } else {
+                    this.a2 = new ThreeBSP(sphere);
+                    this.b2 = new ThreeBSP(circle1);
+                    this.c2 = new ThreeBSP(circle2);
+                    this.d2 = new ThreeBSP(sphere0);
+                }
+            } else {
+                circle1.rotateZ(Math.PI * -0.5);
+                circle1.rotateX(Math.PI * ((90 - deg1) / 180));
+                circle2.rotateZ(Math.PI * 0.5);
+                circle2.rotateX(phiStart);
+
+                if (nf === 'n') {
+                    this.x1 = new ThreeBSP(sphere);
+                    this.y1 = new ThreeBSP(circle1);
+                    this.z1 = new ThreeBSP(circle2);
+                    this.w1 = new ThreeBSP(sphere0);
+                } else {
+                    this.a1 = new ThreeBSP(sphere);
+                    this.b1 = new ThreeBSP(circle1);
+                    this.c1 = new ThreeBSP(circle2);
+                    this.d1 = new ThreeBSP(sphere0);
+                }
+            }
+        },
+        initSensorBSP(position, rotation, type, index, canMove) {
+            let sensorId = this.configList[type][index].sensorId;
+
+            if (!this.bsp[type][sensorId]) {
+                let coordinate = this.allSensor[type].find(
+                    (i) => i.id === sensorId
+                );
+
+                if (!coordinate) {
+                    coordinate = {
+                        farDistance: 60,
+                        nearDistance: 0,
+                        fovH: 60,
+                        fovV: 60,
+                        fovHLeft: 60,
+                        fovVTop: 60,
+                        fovHRight: 60,
+                        fovVBottom: 60,
+                    };
+                }
+
+                // console.log(coordinate)
+
+                if (coordinate.farDistance <= 0 || coordinate.farDistance <= coordinate.nearDistance) return;
+
+                let color = null;
+
+                if (type === "camera") {
+                    color = 0x0000ff;
+                } else if (type === "ogt") {
+                    color = 0x008000;
+                } else if (type === "lidar") {
+                    color = 0xff4500;
+                } else {
+                    color = 0x0000ff;
+                }
+
+                // let m = new THREE.MeshPhongMaterial({
+                //     emissive: color,
+                //     color: 0x4c4c4c,
+                //     transparent: true,
+                //     // opacity: 0.3,
+                //     // lightMapIntensity: 0.1,
+                //     side: THREE.DoubleSide,
+                // });
+
+                // let m = new THREE.MeshBasicMaterial({ color: 0xffaa00, transparent: true, side: THREE.DoubleSide, blending: THREE.AdditiveBlending });
+                // let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, wireframe: true, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+                let m = new THREE.MeshPhongMaterial({ emissive: color, color: 0x4c4c4c, transparent: true, opacity: 0.3, side: THREE.DoubleSide });
+
+                // showFullScreenLoading();
+                // console.log(showFullScreenLoading);
+
+                // console.log(6);
+
+                // const loading = this.$loading({
+                //     lock: true,
+                //     text: "模型加载中,请稍等...",
+                //     // spinner: "el-icon-loading",
+                //     background: "rgba(0, 0, 0, 0.2)",
+                // });
+
+                // this.loadingFull = true;
+
+                coordinate.farDistance = coordinate.farDistance / 100;
+                coordinate.nearDistance = coordinate.nearDistance / 100;
+
+                if (type != "ogt") {
+                    this.toCube(coordinate, m);
+                } else {
+                    this.toCubeOgt(coordinate, m);
+                }
+                // console.log(66);
+                // loading.close();
+                // tryHideFullScreenLoading();
+                // console.log(666);
+
+                this.bsp[type][sensorId] = this.cube;
+            } else {
+                this.cube = this.bsp[type][sensorId];
+            }
+
+            // 为了修复半径过大导致bsp死循环,所以先缩小半径,渲染出mesh后再放大
+            let a = (100 / (this.scale / 0.6)) * 16.5;
+
+            this.cube.scale.set(a, a, a);
+
+
+            if (position) {
+                this.cube.position.x = position.x;
+                this.cube.position.y = position.y;
+                this.cube.position.z = position.z;
+            }
+
+            if (canMove) {
+                this.cube.name = this.geometryName || "cube";
+
+                if (rotation) {
+                    this.cube.rotateX(rotation.x);
+                    this.cube.rotateY(rotation.y);
+                    this.cube.rotateZ(rotation.z);
+                }
+
+                if (this.transformControls) {
+                    this.transformControls.attach(this.cube);
+                }
+            } else {
+                if (rotation) {
+                    this.cube.rotation.set(
+                        (rotation.x * Math.PI) / 180,
+                        (rotation.y * Math.PI) / 180,
+                        (rotation.z * Math.PI) / 180
+                    );
+                }
+
+                this.cube.name = this.geometryName || "cubeA";
+                this.cacheList.push(this.cube);
+            }
+
+            this.scene.add(this.cube);
+        },
+        toCube(coordinate, m) {
+            let { fovH, fovV, nearDistance } = coordinate;
+
+            this.initContent0(coordinate, 'h', 'f');
+            this.initContent0(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContent0(coordinate, 'h', 'n');
+                this.initContent0(coordinate, 'v', 'n');
+
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).subtract(this.x2.subtract(this.y2.subtract(this.z2))).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).subtract(this.x1.subtract(this.y1.intersect(this.z1))).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2))
+                        // .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2).intersect(this.z2))).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).subtract(this.x2.subtract(this.y2).intersect(this.z2)).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                        //     .subtract(this.x1.subtract(this.y1).subtract(this.z1).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).subtract(this.x1.subtract(this.y1).subtract(this.z1)).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.subtract(this.y1.intersect(this.z1)).intersect(this.x2.subtract(this.y2.subtract(this.z2)))).toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是等于360的
+                if (fovH >= 180 || fovV >= 180) {
+                    if (fovH >= 180 && fovV < 180) {
+                        if (fovV <= 90) {
+                            this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if (fovH < 180 && fovV >= 180) {
+                        if (fovH <= 90) {
+                            this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if (fovH >= 180 && fovV >= 180) {
+                        this.cube = this.a1.toMesh(m);
+                    }
+                } else {
+                    if (fovH <= 90 && fovV <= 90) {
+                        // 都小于90
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if (fovH > 90 && fovV <= 90) {
+                        // h大于90 v小于90
+                        // this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2).intersect(this.c2)).toMesh(m);
+                        this.cube = this.a2.subtract(this.b2).intersect(this.c2).toMesh(m);
+                    } else if (fovH <= 90 && fovV > 90) {
+                        // h小于90 v大于90
+                        // this.cube = this.a1.subtract(this.b1).subtract(this.c1).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                        this.cube = this.a1.subtract(this.b1).subtract(this.c1).toMesh(m);
+                    } else if (fovH > 90 && fovV > 90) {
+                        // 都大于90
+                        this.cube = this.a1.subtract(this.b1.intersect(this.c1)).intersect(this.a2.subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
+        toCubeOgt(coordinate, m) {
+            let { fovHLeft, fovHRight, fovVTop, fovVBottom, nearDistance } = coordinate;
+
+            this.initContentOgt(coordinate, 'h', 'f');
+            this.initContentOgt(coordinate, 'v', 'f');
+
+            if (nearDistance > 0) {
+                // 带盲区
+                this.initContentOgt(coordinate, 'h', 'n');
+                this.initContentOgt(coordinate, 'v', 'n');
+
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))
+                                .subtract(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1)
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1))
+                                .toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1))
+                                .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)))
+                                .toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    // 不带盲区
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2).intersect(this.z2)))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1).subtract(this.z1).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)))
+                            .subtract(this.x1.intersect(this.w1).subtract(this.y1.intersect(this.z1)).intersect(this.x2.intersect(this.w2).subtract(this.y2.subtract(this.z2))))
+                            .toMesh(m);
+                    }
+                }
+            } else {
+                // 有至少一个方向是大于360的
+                if ((fovHLeft + fovHRight >= 360) || (fovVTop + fovVBottom >= 360)) {
+                    if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom < 360)) {
+                        if (fovVTop + fovVBottom <= 180) {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2).toMesh(m);
+                        } else {
+                            this.cube = this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight < 360) && (fovVTop + fovVBottom >= 360)) {
+                        if (fovHLeft + fovHRight <= 180) {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).toMesh(m);
+                        } else {
+                            this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).toMesh(m);
+                        }
+                    } else if ((fovHLeft + fovHRight >= 360) && (fovVTop + fovVBottom >= 360)) {
+                        this.cube = this.a1.intersect(this.d1).toMesh(m);
+                    }
+                } else {
+                    if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 相加都小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom <= 180)) {
+                        // 左右相加大于180  上下相加小于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2).intersect(this.c2)).toMesh(m);
+                    } else if ((fovHLeft + fovHRight <= 180) && (fovVTop + fovVBottom > 180)) {
+                        // 左右相加小于180  上下相加大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1).subtract(this.c1).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    } else if ((fovHLeft + fovHRight > 180) && (fovVTop + fovVBottom > 180)) {
+                        // 相加都大于180
+                        this.cube = this.a1.intersect(this.d1).subtract(this.b1.intersect(this.c1)).intersect(this.a2.intersect(this.d2).subtract(this.b2.subtract(this.c2))).toMesh(m);
+                    }
+                }
+            }
+
+            this.a1 = null;
+            this.b1 = null;
+            this.c1 = null;
+            this.d1 = null;
+            this.a2 = null;
+            this.b2 = null;
+            this.c2 = null;
+            this.d2 = null;
+            this.x1 = null;
+            this.y1 = null;
+            this.z1 = null;
+            this.w1 = null;
+            this.x2 = null;
+            this.y2 = null;
+            this.z2 = null;
+            this.w2 = null;
+        },
         // 处理轴对称物体
         initSenA(position, rotation, coordinate, r, r1, type, canMove) {
             let x = 0,
@@ -505,7 +1130,7 @@ export default {
                 this.renderer.domElement
             ); //创建控件对象
             this.controls.minDistance = 30;
-            this.controls.maxDistance = 720;
+            this.controls.maxDistance = 18000;
             this.controls.update();
         },
         animate() {
@@ -535,8 +1160,13 @@ export default {
 
             window.addEventListener("resize", this.onWindowResize);
         },
-        // 每编辑一个传感器则重新生成对应的物体
-        reset(type) {
+        /**
+         * 每编辑一个传感器则重新生成对应的物体
+         * @param {*} type 传感器类型
+         * @param {*} index 该传感器所在数组下标
+         * @param {*} onlyClear 是否只用来清空效果
+         */
+        reset(type, index = -1, onlyClear = false) {
             this.canDrag = true;
 
             if (this.cacheList.length > 0) {
@@ -558,6 +1188,19 @@ export default {
             this.yAngle = 0;
             this.zAngle = 0;
 
+            if (this.dragControls) {
+                this.dragControls.deactivate();
+                this.dragControls.dispose();
+                this.dragControls = null;
+            }
+
+            if (this.transformControls) {
+                this.transformControls.detach();
+            }
+
+            // onlyClear若为true,表示只清除当前展示的东西,没有可增加展示的
+            if (onlyClear) return;
+
             let z = +this.$parent.formA.sensorX || 0;
             let x = +this.$parent.formA.sensorY || 0;
             let y = +this.$parent.formA.sensorZ || 0;
@@ -565,33 +1208,80 @@ export default {
             let p = +this.$parent.formA.sensorR || 0;
             let r = +this.$parent.formA.sensorH || 0;
 
-            this.initContent(
-                null,
-                { x, y, z },
-                {
-                    x: (-90 * Math.PI) / 180,
-                    y: 0,
-                    z: 0,
-                },
-                type
-            );
+            x = Math.floor(x / this.rate);
+            y = Math.floor(y / this.rate);
+            z = Math.floor(z / this.rate);
+
+            if (type != "gps") {
+                setTimeout(() => {
+                    this.initSensorBSP(
+                        { x, y, z },
+                        {
+                            // x: (-90 * Math.PI) / 180,
+                            x: 0,
+                            y: 0,
+                            z: 0,
+                        },
+                        type,
+                        index,
+                        true
+                    );
+
+                    setTimeout(() => {
+                        this.initSensor({ x, y, z }, type, true);
+
+                        const obj = this.scene.getObjectByName("cube");
+
+                        if (!obj) return;
+
+                        obj.position.x = x;
+                        obj.position.y = y;
+                        obj.position.z = z;
+
+                        // obj.rotateX(0 * -1);
+                        // obj.rotateX((h * Math.PI) / 180);
+                        // this.xAngle = ((h - 90) * Math.PI) / 180;
+                        this.xAngle = (h * Math.PI) / 180;
+
+                        // obj.rotateY(0 * -1);
+                        // obj.rotateY((p * Math.PI) / 180);
+                        this.yAngle = (p * Math.PI) / 180;
+
+                        // obj.rotateZ(0 * -1);
+                        // obj.rotateZ((r * Math.PI) / 180);
+                        this.zAngle = (r * Math.PI) / 180;
+                        obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+                    }, 0);
+
+                }, 100)
+            } else {
+                setTimeout(() => {
+                    this.initSensor({ x, y, z }, type, true);
+
+                    const obj = this.scene.getObjectByName("cube");
 
-            setTimeout(() => {
-                this.initSensor({ x, y, z }, type, true);
+                    if (!obj) return;
 
-                const obj = this.scene.getObjectByName("cube");
+                    obj.position.x = x;
+                    obj.position.y = y;
+                    obj.position.z = z;
 
-                obj.position.x = x;
-                obj.position.y = y;
-                obj.position.z = z;
+                    // obj.rotateX(0 * -1);
+                    // obj.rotateX((h * Math.PI) / 180);
+                    // this.xAngle = ((h - 90) * Math.PI) / 180;
+                    this.xAngle = (h * Math.PI) / 180;
 
-                this.xAngle = ((h - 90) * Math.PI) / 180;
+                    // obj.rotateY(0 * -1);
+                    // obj.rotateY((p * Math.PI) / 180);
+                    this.yAngle = (p * Math.PI) / 180;
 
-                this.yAngle = (p * Math.PI) / 180;
+                    // obj.rotateZ(0 * -1);
+                    // obj.rotateZ((r * Math.PI) / 180);
+                    this.zAngle = (r * Math.PI) / 180;
+                    obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
+                }, 0);
+            }
 
-                this.zAngle = (r * Math.PI) / 180;
-                obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
-            }, 0);
         },
         // 初始化传感器
         initSensor(
@@ -623,6 +1313,61 @@ export default {
             //     showFullScreenLoading();
             // }
 
+            if (type === "ogt") {
+                if (this.ogtObj) {
+                    let a = this.ogtObj;
+
+                    a.position.set(pos.x, pos.y, pos.z);
+
+                    if (canMove) {
+                        this.sensor = a;
+                    }
+
+                    this.cacheList.push(a);
+
+                    this.scene.add(a); //返回的组对象插入场景中
+                    return;
+                }
+
+
+                // this.$emit('loadingDone', 'carStart');
+
+                var loader1 = new GLTFLoader(); //创建一个FBX加载器
+
+                loader1.load(
+                    `${that.publicPath}sensor/ogt/ogt.glb`,
+                    function (obj) {
+                        // that.$emit('loadingDone', 'carDone');
+
+                        that.cacheList.push(obj.scene);
+
+                        that.ogtObj = obj.scene;
+
+                        let s = that.scale;
+
+                        let scale = 0.1;
+                        obj.scene.scale.set(scale, scale, scale);
+
+                        obj.scene.position.set(pos.x, pos.y, pos.z);
+                        // obj.scene.name = "sensor";
+
+                        if (canMove) {
+                            that.sensor = obj.scene;
+                        }
+
+                        that.scene.add(obj.scene); //返回的组对象插入场景中
+                    },
+                    (xhr) => { },
+                    (error) => {
+                        // that.$emit('loadingDone', 'carDone');
+
+                        console.error(error);
+                    }
+                );
+
+                return
+            }
+
             Loader.load(
                 mtlUrl,
                 function (materials) {
@@ -635,11 +1380,13 @@ export default {
                         that.cacheList.push(obj);
                         for (let i = 0; i < obj.children.length; i++) {
                             if (type === "camera") {
-                                obj.children[i].scale.set(0.8, 0.8, 0.8);
+                                // obj.children[i].scale.set(0.8, 0.8, 0.8);
+                                obj.children[i].scale.set(0.4, 0.4, 0.4);
                             } else if (type === "ogt") {
                                 obj.children[i].scale.set(0.2, 0.2, 0.2);
                             } else if (type === "lidar") {
-                                obj.children[i].scale.set(0.1, 0.1, 0.1);
+                                // obj.children[i].scale.set(0.1, 0.1, 0.1);
+                                obj.children[i].scale.set(0.06, 0.06, 0.06);
                             } else if (type === "gps") {
                                 obj.children[i].scale.set(40, 40, 40);
                             }
@@ -733,7 +1480,8 @@ export default {
                     ); */
 
                     if (type != "gps") {
-                        this.initContent(
+                        // this.initContent(
+                        this.initSensorBSP(
                             {
                                 z: Math.floor(item.sensorX / this.rate || 0),
                                 x: Math.floor(item.sensorY / this.rate || 0),
@@ -870,6 +1618,25 @@ export default {
         this.cubeTexture = null;
         this.raf = null;
 
+        this.ogtObj = null;
+        this.bsp = null;
+        this.a1 = null;
+        this.b1 = null;
+        this.c1 = null;
+        this.d1 = null;
+        this.a2 = null;
+        this.b2 = null;
+        this.c2 = null;
+        this.d2 = null;
+        this.x1 = null;
+        this.y1 = null;
+        this.z1 = null;
+        this.w1 = null;
+        this.x2 = null;
+        this.y2 = null;
+        this.z2 = null;
+        this.w2 = null;
+
         THREE.Cache.clear();
     },
 };

+ 34 - 99
src/views/workManagement/manualRunProjectDetail.vue

@@ -1,55 +1,24 @@
 <template>
     <div>
-        <el-form
-            ref="form"
-            :model="form"
-            :rules="rules"
-            label-width="135px"
-            class="flexBox"
-        >
+        <el-form ref="form" :model="form" :rules="rules" label-width="135px" class="flexBox">
             <div class="formItemBox">
                 <el-form-item label="项目名称:" prop="projectName">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="30"
-                        v-autoTrim="{ obj: form, key: 'projectName' }"
-                        v-model="form.projectName"
-                    >
+                    <el-input placeholder="请输入" maxlength="120" v-autoTrim="{ obj: form, key: 'projectName' }"
+                        v-model="form.projectName">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="项目描述:" prop="projectDescribe">
-                    <el-input
-                        type="textarea"
-                        :autosize="{ minRows: 4, maxRows: 4 }"
-                        placeholder="请输入"
-                        maxlength="300"
-                        v-autoTrim="{ obj: form, key: 'projectDescribe' }"
-                        v-model="form.projectDescribe"
-                    >
+                    <el-input type="textarea" :autosize="{ minRows: 4, maxRows: 4 }" placeholder="请输入" maxlength="300"
+                        v-autoTrim="{ obj: form, key: 'projectDescribe' }" v-model="form.projectDescribe">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="算法来源:" prop="algorithmType">
-                    <el-radio
-                        v-model="form.algorithmType"
-                        label="1"
-                        @change="typeChange"
-                        >私有导入</el-radio
-                    >
-                    <el-radio
-                        v-model="form.algorithmType"
-                        label="3"
-                        @change="typeChange"
-                        >算法平台</el-radio
-                    >
+                    <el-radio v-model="form.algorithmType" label="1" @change="typeChange">私有导入</el-radio>
+                    <el-radio v-model="form.algorithmType" label="3" @change="typeChange">算法平台</el-radio>
                 </el-form-item>
                 <el-form-item label="选择算法:" prop="algorithmArrayS">
-                    <el-cascader
-                        ref="cascaderA"
-                        v-model="form.algorithmArrayS"
-                        :options="algorithmList"
-                        :props="props"
-                        @change="algorithmSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderA" v-model="form.algorithmArrayS" :options="algorithmList" :props="props"
+                        @change="algorithmSelChange"></el-cascader>
                     <!-- <el-select v-model="form.algorithm">
                         <el-option
                             v-for="item in algorithmList"
@@ -61,13 +30,8 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="选择车辆:" prop="vehicleArrayS">
-                    <el-cascader
-                        ref="cascaderB"
-                        v-model="form.vehicleArrayS"
-                        :options="vehicleList"
-                        :props="props"
-                        @change="vehicleSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderB" v-model="form.vehicleArrayS" :options="vehicleList" :props="props"
+                        @change="vehicleSelChange"></el-cascader>
                     <!-- <el-select
                         v-model="form.vehicle"
                         @change="vehicleSelChange"
@@ -82,13 +46,8 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="选择场景:" prop="sceneArrayS">
-                    <el-cascader
-                        ref="cascaderC"
-                        v-model="form.sceneArrayS"
-                        :options="sceneList"
-                        :props="props"
-                        @change="sceneSelChange"
-                    ></el-cascader>
+                    <el-cascader ref="cascaderC" v-model="form.sceneArrayS" :options="sceneList" :props="props"
+                        @change="sceneSelChange"></el-cascader>
                     <!-- <el-select v-model="form.scene" @change="sceneSelChange">
                         <el-option
                             v-for="item in sceneList"
@@ -99,22 +58,13 @@
                     </el-select> -->
                 </el-form-item>
                 <el-form-item label="并行度:" prop="parallelism">
-                    <el-input
-                        :disabled="runDisabled"
-                        placeholder="请输入"
-                        maxlength="10"
-                        v-autoTrim="{ obj: form, key: 'parallelism' }"
-                        v-model="form.parallelism"
-                    >
+                    <el-input :disabled="runDisabled" placeholder="请输入" maxlength="10"
+                        v-autoTrim="{ obj: form, key: 'parallelism' }" v-model="form.parallelism">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="最大仿真时间(s):" prop="maxSimulationTime">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="10"
-                        v-autoTrim="{ obj: form, key: 'maxSimulationTime' }"
-                        v-model="form.maxSimulationTime"
-                    >
+                    <el-input placeholder="请输入" maxlength="10" v-autoTrim="{ obj: form, key: 'maxSimulationTime' }"
+                        v-model="form.maxSimulationTime">
                     </el-input>
                 </el-form-item>
                 <el-form-item label="是否选择GPU:" prop="isChoiceGpu">
@@ -126,16 +76,12 @@
             <div class="tipBox">
                 <div class="tip tipA">
                     <!-- (传感器1:根据车辆自动带出;传感器2:根据车辆自动带出) -->
-                    <span
-                        v-for="item in sensors"
-                        :key="item"
-                        v-bind:class="{
-                            iconA: item === 'camera',
-                            iconB: item === 'ogt',
-                            iconC: item === 'lidar',
-                            iconE: item === 'gps',
-                        }"
-                    ></span>
+                    <span v-for="item in sensors" :key="item" v-bind:class="{
+                        iconA: item === 'camera',
+                        iconB: item === 'ogt',
+                        iconC: item === 'lidar',
+                        iconE: item === 'gps',
+                    }"></span>
                 </div>
                 <div class="tip">(场景数量:{{ sceneCount }})</div>
                 <!-- <div class="tip flexBox">
@@ -150,18 +96,8 @@
         </el-form>
         <div class="btns">
             <el-button type="primary" @click="save(false)">保存</el-button>
-            <el-button
-                type="primary"
-                @click="save(true)"
-                v-if="this.$route.query.id"
-                >另存为</el-button
-            >
-            <el-button
-                type="primary"
-                @click="save(false, true)"
-                :disabled="runDisabled"
-                >提交</el-button
-            >
+            <el-button type="primary" @click="save(true)" v-if="this.$route.query.id">另存为</el-button>
+            <el-button type="primary" @click="save(false, true)" :disabled="runDisabled">提交</el-button>
             <el-button type="primary" plain @click="cancel">取消</el-button>
         </div>
     </div>
@@ -504,28 +440,27 @@ export default {
             margin-top: 280px;
 
             .iconA {
-                background: url("../../assets/common/image/sensor/001.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/001.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconB {
-                background: url("../../assets/common/image/sensor/002.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/002.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconC {
-                background: url("../../assets/common/image/sensor/003.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/003.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconD {
-                background: url("../../assets/common/image/sensor/004.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/004.png") center no-repeat;
                 background-size: contain;
             }
+
             .iconE {
-                background: url("../../assets/common/image/sensor/005.png")
-                    center no-repeat;
+                background: url("../../assets/common/image/sensor/005.png") center no-repeat;
                 background-size: contain;
             }
 

+ 82 - 11
src/views/workManagement/projectInfo.vue

@@ -1,9 +1,8 @@
 <template>
     <div class="projectInfoPanel">
-        <div v-show="
-            !$route.path.includes('taskInfo') &&
+        <div v-show="!$route.path.includes('taskInfo') &&
             !$route.path.includes('evaluationReport')
-        ">
+            ">
             <div class="headPanel panel">
                 <div class="titlePanel">
                     <div class="titlePanelBor">基本信息</div>
@@ -126,6 +125,11 @@
                         <div class="canvasBox">
                             <!-- <canvas-sensor v-if="modelImgSrc" :modelImgSrc="modelImgSrc" :configList="configList"></canvas-sensor> -->
 
+                            <div class="viewBox">
+                                <span v-show="!isOpen" class="view open" @click="showAll(true)" title="显示全部"></span>
+                                <span v-show="isOpen" class="view close" @click="showAll(false)" title="关闭全部"></span>
+                            </div>
+
                             <three-show v-loading="loading" @loadingDone="loadingDone" ref="threeShow" :carModel="carModel"
                                 :allSensor="allSensor"></three-show>
 
@@ -236,7 +240,7 @@ import handleConfigList from "../modelLibrary/components/handleConfigList.vue";
 import tableList from "@/components/grid/TableList";
 import pieChartProjectInfo from "@/components/echarts/pieChartProjectInfo";
 // import canvasSensor from "./components/canvasProjectInfo.vue";
-import threeShow from "./components/threeProjectInfo.vue";
+// import threeShow from "./components/threeProjectInfo.vue";
 import { mapState } from "vuex";
 
 export default {
@@ -246,7 +250,7 @@ export default {
         tableList,
         pieChartProjectInfo,
         // canvasSensor,
-        threeShow,
+        threeShow: () => import('./components/threeProjectInfo.vue'),
     },
     data() {
         return {
@@ -368,6 +372,7 @@ export default {
                 sensorR: "", // 横滚角
                 sensorPort: "", // 端口
             },
+            isOpen: false, // 是否已展示全部传感器
             rulesA: {},
             loading: true,
         };
@@ -443,6 +448,8 @@ export default {
 
             let formA = this.configList[item.name][item.index];
 
+            this.isOpen = false;
+
             this.formA.sensorX = formA.sensorX;
             this.formA.sensorY = formA.sensorY;
             this.formA.sensorZ = formA.sensorZ;
@@ -453,6 +460,13 @@ export default {
                 this.formA.sensorPort = formA.sensorPort;
             }
 
+            setTimeout(() => {
+                this.$refs.threeShow.reset(
+                    this.curOne.name,
+                    this.curOne.index
+                );
+            }, 0);
+
             this.configBox = true;
         },
         closeConfig() {
@@ -486,7 +500,7 @@ export default {
         },
         // 获取所有传感器
         getAllSensor() {
-            this.$axios({
+            return this.$axios({
                 method: "post",
                 url: this.$api.modelLibrary.getMySensorList,
                 data: {},
@@ -503,6 +517,25 @@ export default {
         loadingDone(done) {
             if (done === 'carDone') this.loading = false;
         },
+        // 显示全部传感器
+        showAll(needOpen) {
+            if (needOpen) {
+                this.$refs.threeShow.showAll(this.configList);
+
+                this.closeConfig();
+            } else {
+                if (this.curOne.index < 0) {
+                    this.$refs.threeShow.reset(null, -1, true);
+                } else {
+                    this.curItem(this.curOne, false, false);
+                }
+            }
+
+            this.isOpen = needOpen;
+        },
+        closeConfig() {
+            this.configBox = false;
+        },
     },
 
     // watch: {
@@ -518,8 +551,8 @@ export default {
     //     },
     // },
 
-    mounted() {
-        this.getAllSensor();
+    async mounted() {
+        await this.getAllSensor();
 
         if (this.$route.query.id) {
             this.id = this.$route.query.id;
@@ -547,10 +580,13 @@ export default {
                     // this.modelImgSrc = this.getImgUrl(res.info.vehicleTopView);
                     // this.carModel = this.getImgUrl(res.info.vehicleFrontView);
                     this.carModel = this.getImgUrl(res.info.vehicleTopView);
-                    // this.carModel = this.getImgUrl('/vehicleImg/20221019/1666158952428/AudiA6_10.glb');
 
-                    if (this.$refs.threeShow)
-                        this.$refs.threeShow.showAll(this.configList);
+                    if (this.$refs.threeShow) {
+                        setTimeout(() => {
+                            // this.$refs.threeShow.showAll(this.configList);
+                            this.showAll(true);
+                        }, 3000)
+                    }
 
                     if (this.info.nowRunState === "30") {
                         this.downImgSrc = require("@/assets/common/image/others/hasDoc.png");
@@ -718,6 +754,41 @@ export default {
                 flex: 1;
             }
 
+            .viewBox {
+                position: absolute;
+                left: 2px;
+                top: 2px;
+                width: 40px;
+                height: 40px;
+                line-height: 40px;
+                text-align: center;
+                background-color: #ffffff;
+                opacity: 0.75;
+
+                .view {
+                    display: block;
+                    width: 30px;
+                    height: 15px;
+                    margin: 12px auto 0;
+                    // font-size: 18px;
+                    cursor: pointer;
+                    // color: @themeColor;
+                }
+
+                .open {
+                    background: url("../../assets/common/image/others/open.png") center no-repeat;
+                    background-size: contain;
+                }
+
+                .close {
+                    width: 26px;
+                    height: 13px;
+                    margin-top: 15px;
+                    background: url("../../assets/common/image/others/close.png") center no-repeat;
+                    background-size: contain;
+                }
+            }
+
             .conditions {
                 position: absolute;
                 right: 2px;