{"version":3,"sources":["vendor/ember-cli/app-prefix.js","m08-2020/ThreeCSG.js","m08-2020/adapters/-json-api.js","m08-2020/adapters/application.js","m08-2020/adapters/projektdaten.js","m08-2020/adapters/x3ddefault.js","m08-2020/app.js","m08-2020/cldrs/en.js","m08-2020/component-managers/glimmer.js","m08-2020/components/ember-jstree.js","m08-2020/components/file-dropzone.js","m08-2020/components/file-picker.js","m08-2020/components/file-upload.js","m08-2020/components/welcome-page.js","m08-2020/components/x-tree-branch.js","m08-2020/components/x-tree-checkbox.js","m08-2020/components/x-tree-children.js","m08-2020/components/x-tree-collapsed-icon.js","m08-2020/components/x-tree-expanded-icon.js","m08-2020/components/x-tree-node.js","m08-2020/components/x-tree-toggle.js","m08-2020/components/x-tree.js","m08-2020/controllers/application.js","m08-2020/data-adapter.js","m08-2020/formats.js","m08-2020/helpers/app-version.js","m08-2020/helpers/file-queue.js","m08-2020/helpers/format-date.js","m08-2020/helpers/format-message.js","m08-2020/helpers/format-number.js","m08-2020/helpers/format-relative.js","m08-2020/helpers/format-time.js","m08-2020/helpers/pluralize.js","m08-2020/helpers/send.js","m08-2020/helpers/singularize.js","m08-2020/helpers/t.js","m08-2020/initializers/app-version.js","m08-2020/initializers/container-debug-adapter.js","m08-2020/initializers/ember-data-data-adapter.js","m08-2020/initializers/ember-data.js","m08-2020/initializers/export-application-global.js","m08-2020/initializers/model-locale.js","m08-2020/instance-initializers/ember-data.js","m08-2020/lib old/CSGMesh.js","m08-2020/lib old/ClippingUtils/DynamicSectionalPlane.js","m08-2020/lib old/ClippingUtils/OutlineGenerator.js","m08-2020/lib old/ClippingUtils/SectionalPlane.js","m08-2020/lib old/DXFGenerator.js","m08-2020/lib old/FigureLoaders/ArrowLoader.js","m08-2020/lib old/FigureLoaders/DimensionalChainLoader.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/ComplexVolume.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipse.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipsoid.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Point.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polygon.js","m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polyline.js","m08-2020/lib old/FigureLoaders/DrawingObjectLoader.js","m08-2020/lib old/FigureLoaders/DrawingPlaneLoader.js","m08-2020/lib old/FigureLoaders/FastenerLoader.js","m08-2020/lib old/ThreeCSG.js","m08-2020/lib old/ThreeExtensions/CustomEllipseCurve.js","m08-2020/lib old/ThreeExtensions/OutlinesGeometry.js","m08-2020/lib old/ThreeExtensions/PlanarExtrusionPointsGenerator.js","m08-2020/lib old/ThreeExtensions/PlanarRotationPointsGenerator.js","m08-2020/lib old/ThreeExtensions/RotatedGeometry.js","m08-2020/lib old/ThreeModule.js","m08-2020/lib old/Utils.js","m08-2020/lib old/Utils/BooleanOperations.js","m08-2020/lib old/Utils/DimensionalChainIntersectionDetection.js","m08-2020/lib old/Utils/LineIntersectionDetection.js","m08-2020/lib old/Utils/PointInsidePolygonCheck2D.js","m08-2020/lib old/WireframeGenerator.js","m08-2020/lib old/ZoomController.js","m08-2020/lib old/dxf_colors.js","m08-2020/lib/CSGMesh.js","m08-2020/lib/ClippingUtils/DynamicSectionalPlane.js","m08-2020/lib/ClippingUtils/OutlineGenerator.js","m08-2020/lib/ClippingUtils/SectionalPlane.js","m08-2020/lib/FigureLoaders/ArrowLoader.js","m08-2020/lib/FigureLoaders/DimensionalChainLoader.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/ComplexVolume.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipse.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipsoid.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/Point.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polygon.js","m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polyline.js","m08-2020/lib/FigureLoaders/DrawingObjectLoader.js","m08-2020/lib/FigureLoaders/FastenerLoader.js","m08-2020/lib/FigureLoaders/TextLoader.js","m08-2020/lib/FileGeneration/DXFGenerator.js","m08-2020/lib/FileGeneration/DrawingPlaneLoader.js","m08-2020/lib/FileGeneration/dxf_colors.js","m08-2020/lib/OutlinesGeometry.js","m08-2020/lib/SectionalPlane.js","m08-2020/lib/StateControl/DimChainViewController.js","m08-2020/lib/StateControl/DynamicTextsController.js","m08-2020/lib/StateControl/ZoomController.js","m08-2020/lib/ThreeExtensions/CustomEllipseCurve.js","m08-2020/lib/ThreeExtensions/OutlinesGeometry.js","m08-2020/lib/ThreeExtensions/PlanarExtrusionPointsGenerator.js","m08-2020/lib/ThreeExtensions/PlanarRotationPointsGenerator.js","m08-2020/lib/ThreeExtensions/RaycastModdedMesh.js","m08-2020/lib/ThreeExtensions/RotatedGeometry.js","m08-2020/lib/ThreeExtensions/VectorString.js","m08-2020/lib/ThreeModule.js","m08-2020/lib/Utils.js","m08-2020/lib/Utils/BooleanOperations.js","m08-2020/lib/Utils/DimensionalChainIntersectionDetection.js","m08-2020/lib/Utils/LineIntersectionDetection.js","m08-2020/lib/Utils/PointInsidePolygonCheck2D.js","m08-2020/lib/WireframeGenerator.js","m08-2020/lib/font_urls.js","m08-2020/mixins/model-validator.js","m08-2020/mixins/object-validator.js","m08-2020/models/application.js","m08-2020/models/position.js","m08-2020/models/rotation.js","m08-2020/router.js","m08-2020/routes/application.js","m08-2020/serializers/-default.js","m08-2020/serializers/-json-api.js","m08-2020/serializers/-rest.js","m08-2020/services/file-queue.js","m08-2020/services/intl.js","m08-2020/services/store.js","m08-2020/templates/application.js","m08-2020/templates/components/custom-input.js","m08-2020/templates/components/file-picker.js","m08-2020/transforms/boolean.js","m08-2020/transforms/date.js","m08-2020/transforms/number.js","m08-2020/transforms/string.js","m08-2020/translations/en-us.js","m08-2020/utils/intl/missing-message.js","vendor/ember-cli/app-suffix.js","vendor/ember-cli/app-config.js","vendor/ember-cli/app-boot.js"],"sourcesContent":["'use strict';\n\n\n","define(\"m08-2020/ThreeCSG\", [], function () {\n  'use strict';\n\n  window.ThreeBSP = function () {\n    var ThreeBSP,\n        EPSILON = 1e-5,\n        COPLANAR = 0,\n        FRONT = 1,\n        BACK = 2,\n        SPANNING = 3;\n\n    ThreeBSP = function (geometry) {\n      // Convert THREE.Geometry to ThreeBSP\n      var i,\n          _length_i,\n          face,\n          vertex,\n          faceVertexUvs,\n          polygon,\n          polygons = [],\n          tree;\n\n      if (geometry instanceof THREE.Geometry) {\n        this.matrix = new THREE.Matrix4();\n      } else if (geometry instanceof THREE.Mesh) {\n        // #todo: add hierarchy support \n        geometry.updateMatrix();\n        this.matrix = geometry.matrixWorld.clone(); // Lee was here -- changed matrix to matrixWorld\n\n        geometry = geometry.geometry;\n      } else if (geometry instanceof ThreeBSP.Node) {\n        this.tree = geometry;\n        this.matrix = new THREE.Matrix4();\n        return this;\n      } else {\n        throw 'ThreeBSP: Given geometry is unsupported';\n      }\n\n      for (i = 0, _length_i = geometry.faces.length; i < _length_i; i++) {\n        face = geometry.faces[i];\n        faceVertexUvs = geometry.faceVertexUvs[0][i];\n        polygon = new ThreeBSP.Polygon();\n\n        if (face instanceof THREE.Face3) {\n          vertex = geometry.vertices[face.a];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[0].u, faceVertexUvs[0].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.b];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], new THREE.Vector2(faceVertexUvs[1].u, faceVertexUvs[1].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.c];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], new THREE.Vector2(faceVertexUvs[2].u, faceVertexUvs[2].v));\n          polygon.vertices.push(vertex);\n        } else if (typeof THREE.Face4) {\n          vertex = geometry.vertices[face.a];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[0].u, faceVertexUvs[0].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.b];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], new THREE.Vector2(faceVertexUvs[1].u, faceVertexUvs[1].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.c];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], new THREE.Vector2(faceVertexUvs[2].u, faceVertexUvs[2].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.d];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[3], new THREE.Vector2(faceVertexUvs[3].u, faceVertexUvs[3].v));\n          polygon.vertices.push(vertex);\n        } else {\n          throw 'Invalid face type at index ' + i;\n        }\n\n        polygon.calculateProperties();\n        polygons.push(polygon);\n      }\n\n      ;\n      this.tree = new ThreeBSP.Node(polygons);\n    };\n\n    ThreeBSP.prototype.subtract = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.invert();\n      a.clipTo(b);\n      b.clipTo(a);\n      b.invert();\n      b.clipTo(a);\n      b.invert();\n      a.build(b.allPolygons());\n      a.invert();\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.union = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.clipTo(b);\n      b.clipTo(a);\n      b.invert();\n      b.clipTo(a);\n      b.invert();\n      a.build(b.allPolygons());\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.intersect = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.invert();\n      b.clipTo(a);\n      b.invert();\n      a.clipTo(b);\n      b.clipTo(a);\n      a.build(b.allPolygons());\n      a.invert();\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.toGeometry = function () {\n      var i,\n          j,\n          matrix = new THREE.Matrix4().getInverse(this.matrix),\n          geometry = new THREE.Geometry(),\n          polygons = this.tree.allPolygons(),\n          polygon_count = polygons.length,\n          polygon,\n          polygon_vertice_count,\n          vertice_dict = {},\n          vertex_idx_a,\n          vertex_idx_b,\n          vertex_idx_c,\n          vertex,\n          face,\n          verticeNormals,\n          verticeUvs;\n\n      for (i = 0; i < polygon_count; i++) {\n        polygon = polygons[i];\n        polygon_vertice_count = polygon.vertices.length;\n\n        for (j = 2; j < polygon_vertice_count; j++) {\n          verticeNormals = [];\n          verticeUvs = [];\n          vertex = polygon.vertices[0];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          vertex = polygon.vertices[j - 1];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          vertex = polygon.vertices[j];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          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));\n          face.vertexNormals.push(verticeNormals[0], verticeNormals[1], verticeNormals[2]);\n          geometry.faces.push(face);\n          geometry.faceVertexUvs[0].push(verticeUvs);\n        }\n      }\n\n      return geometry;\n    };\n\n    ThreeBSP.prototype.toMesh = function (material) {\n      var geometry = this.toGeometry(),\n          mesh = new THREE.Mesh(geometry, material);\n      mesh.position.getPositionFromMatrix(this.matrix); //mesh.rotation.setEulerFromRotationMatrix( this.matrix );//not working and commented\n\n      mesh.position.setFromMatrixPosition(this.matrix); //from stackoverflow (https://stackoverflow.com/questions/17711914/seteulerfromrotationmatrix-does-not-exist-in-three-js)\n\n      return mesh;\n    };\n\n    ThreeBSP.Polygon = function (vertices, normal, w) {\n      if (!(vertices instanceof Array)) {\n        vertices = [];\n      }\n\n      this.vertices = vertices;\n\n      if (vertices.length > 0) {\n        this.calculateProperties();\n      } else {\n        this.normal = this.w = undefined;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.calculateProperties = function () {\n      var a = this.vertices[0],\n          b = this.vertices[1],\n          c = this.vertices[2];\n      this.normal = b.clone().subtract(a).cross(c.clone().subtract(a)).normalize();\n      this.w = this.normal.clone().dot(a);\n      return this;\n    };\n\n    ThreeBSP.Polygon.prototype.clone = function () {\n      var i,\n          vertice_count,\n          polygon = new ThreeBSP.Polygon();\n\n      for (i = 0, vertice_count = this.vertices.length; i < vertice_count; i++) {\n        polygon.vertices.push(this.vertices[i].clone());\n      }\n\n      ;\n      polygon.calculateProperties();\n      return polygon;\n    };\n\n    ThreeBSP.Polygon.prototype.flip = function () {\n      var i,\n          vertices = [];\n      this.normal.multiplyScalar(-1);\n      this.w *= -1;\n\n      for (i = this.vertices.length - 1; i >= 0; i--) {\n        //this.vertices[i].normal.multiplyScalar( -1 );\n        vertices.push(this.vertices[i]);\n      }\n\n      ;\n      this.vertices = vertices;\n      return this;\n    };\n\n    ThreeBSP.Polygon.prototype.classifyVertex = function (vertex) {\n      var side_value = this.normal.dot(vertex) - this.w;\n\n      if (side_value < -EPSILON) {\n        return BACK;\n      } else if (side_value > EPSILON) {\n        return FRONT;\n      } else {\n        return COPLANAR;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.classifySide = function (polygon) {\n      var i,\n          vertex,\n          classification,\n          num_positive = 0,\n          num_negative = 0,\n          vertice_count = polygon.vertices.length;\n\n      for (i = 0; i < vertice_count; i++) {\n        vertex = polygon.vertices[i];\n        classification = this.classifyVertex(vertex);\n\n        if (classification === FRONT) {\n          num_positive++;\n        } else if (classification === BACK) {\n          num_negative++;\n        }\n      }\n\n      if (num_positive > 0 && num_negative === 0) {\n        return FRONT;\n      } else if (num_positive === 0 && num_negative > 0) {\n        return BACK;\n      } else if (num_positive === 0 && num_negative === 0) {\n        return COPLANAR;\n      } else {\n        return SPANNING;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.splitPolygon = function (polygon, coplanar_front, coplanar_back, front, back) {\n      var classification = this.classifySide(polygon);\n\n      if (classification === COPLANAR) {\n        (this.normal.dot(polygon.normal) > 0 ? coplanar_front : coplanar_back).push(polygon);\n      } else if (classification === FRONT) {\n        front.push(polygon);\n      } else if (classification === BACK) {\n        back.push(polygon);\n      } else {\n        var vertice_count,\n            i,\n            j,\n            ti,\n            tj,\n            vi,\n            vj,\n            t,\n            v,\n            f = [],\n            b = [];\n\n        for (i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++) {\n          j = (i + 1) % vertice_count;\n          vi = polygon.vertices[i];\n          vj = polygon.vertices[j];\n          ti = this.classifyVertex(vi);\n          tj = this.classifyVertex(vj);\n          if (ti != BACK) f.push(vi);\n          if (ti != FRONT) b.push(vi);\n\n          if ((ti | tj) === SPANNING) {\n            t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().subtract(vi));\n            v = vi.interpolate(vj, t);\n            f.push(v);\n            b.push(v);\n          }\n        }\n\n        if (f.length >= 3) front.push(new ThreeBSP.Polygon(f).calculateProperties());\n        if (b.length >= 3) back.push(new ThreeBSP.Polygon(b).calculateProperties());\n      }\n    };\n\n    ThreeBSP.Vertex = function (x, y, z, normal, uv) {\n      this.x = x;\n      this.y = y;\n      this.z = z;\n      this.normal = normal || new THREE.Vector3();\n      this.uv = uv || new THREE.Vector2();\n    };\n\n    ThreeBSP.Vertex.prototype.clone = function () {\n      return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());\n    };\n\n    ThreeBSP.Vertex.prototype.add = function (vertex) {\n      this.x += vertex.x;\n      this.y += vertex.y;\n      this.z += vertex.z;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.subtract = function (vertex) {\n      this.x -= vertex.x;\n      this.y -= vertex.y;\n      this.z -= vertex.z;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.multiplyScalar = function (scalar) {\n      this.x *= scalar;\n      this.y *= scalar;\n      this.z *= scalar;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.cross = function (vertex) {\n      var x = this.x,\n          y = this.y,\n          z = this.z;\n      this.x = y * vertex.z - z * vertex.y;\n      this.y = z * vertex.x - x * vertex.z;\n      this.z = x * vertex.y - y * vertex.x;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.normalize = function () {\n      var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);\n      this.x /= length;\n      this.y /= length;\n      this.z /= length;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.dot = function (vertex) {\n      return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;\n    };\n\n    ThreeBSP.Vertex.prototype.lerp = function (a, t) {\n      this.add(a.clone().subtract(this).multiplyScalar(t));\n      this.normal.add(a.normal.clone().sub(this.normal).multiplyScalar(t));\n      this.uv.add(a.uv.clone().sub(this.uv).multiplyScalar(t));\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.interpolate = function (other, t) {\n      return this.clone().lerp(other, t);\n    };\n\n    ThreeBSP.Node = function (polygons) {\n      var i,\n          polygon_count,\n          front = [],\n          back = [];\n      this.polygons = [];\n      this.front = this.back = undefined;\n      if (!(polygons instanceof Array) || polygons.length === 0) return;\n      this.divider = polygons[0].clone();\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n      }\n\n      if (front.length > 0) {\n        this.front = new ThreeBSP.Node(front);\n      }\n\n      if (back.length > 0) {\n        this.back = new ThreeBSP.Node(back);\n      }\n    };\n\n    ThreeBSP.Node.prototype.build = function (polygons) {\n      var i,\n          polygon_count,\n          front = [],\n          back = [];\n\n      if (!this.divider) {\n        this.divider = polygons[0].clone();\n      }\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n      }\n\n      if (front.length > 0) {\n        if (!this.front) this.front = new ThreeBSP.Node();\n        this.front.build(front);\n      }\n\n      if (back.length > 0) {\n        if (!this.back) this.back = new ThreeBSP.Node();\n        this.back.build(back);\n      }\n    };\n\n    ThreeBSP.Node.prototype.allPolygons = function () {\n      var polygons = this.polygons.slice();\n      if (this.front) polygons = polygons.concat(this.front.allPolygons());\n      if (this.back) polygons = polygons.concat(this.back.allPolygons());\n      return polygons;\n    };\n\n    ThreeBSP.Node.prototype.clone = function () {\n      var node = new ThreeBSP.Node();\n      node.divider = this.divider.clone();\n      node.polygons = this.polygons.map(function (polygon) {\n        return polygon.clone();\n      });\n      node.front = this.front && this.front.clone();\n      node.back = this.back && this.back.clone();\n      return node;\n    };\n\n    ThreeBSP.Node.prototype.invert = function () {\n      var i, polygon_count, temp;\n\n      for (i = 0, polygon_count = this.polygons.length; i < polygon_count; i++) {\n        this.polygons[i].flip();\n      }\n\n      this.divider.flip();\n      if (this.front) this.front.invert();\n      if (this.back) this.back.invert();\n      temp = this.front;\n      this.front = this.back;\n      this.back = temp;\n      return this;\n    };\n\n    ThreeBSP.Node.prototype.clipPolygons = function (polygons) {\n      var i, polygon_count, front, back;\n      if (!this.divider) return polygons.slice();\n      front = [], back = [];\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], front, back, front, back); //if (window.debug) console.debug( front.length, back.length );\n      }\n\n      if (this.front) front = this.front.clipPolygons(front);\n      if (this.back) back = this.back.clipPolygons(back);else back = [];\n      return front.concat(back);\n    };\n\n    ThreeBSP.Node.prototype.clipTo = function (node) {\n      this.polygons = node.clipPolygons(this.polygons);\n      if (this.front) this.front.clipTo(node);\n      if (this.back) this.back.clipTo(node);\n    };\n\n    return ThreeBSP;\n  }();\n});","define(\"m08-2020/adapters/-json-api\", [\"exports\", \"@ember-data/adapter/json-api\"], function (_exports, _jsonApi) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _jsonApi.default;\n    }\n  });\n});","define(\"m08-2020/adapters/application\", [\"exports\", \"@ember-data/adapter/rest\"], function (_exports, _rest) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  class ApplicationAdapter extends _rest.default {}\n\n  _exports.default = ApplicationAdapter;\n});","define(\"m08-2020/adapters/projektdaten\", [\"exports\", \"@ember-data/adapter/rest\"], function (_exports, _rest) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  class ApplicationAdapter extends _rest.default {}\n\n  _exports.default = ApplicationAdapter;\n});","define(\"m08-2020/adapters/x3ddefault\", [\"exports\", \"@ember-data/adapter/rest\"], function (_exports, _rest) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  class ApplicationAdapter extends _rest.default {}\n\n  _exports.default = ApplicationAdapter;\n});","define(\"m08-2020/app\", [\"exports\", \"ember-resolver\", \"ember-load-initializers\", \"m08-2020/config/environment\"], function (_exports, _emberResolver, _emberLoadInitializers, _environment) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  class App extends Ember.Application {\n    constructor(...args) {\n      super(...args);\n\n      _defineProperty(this, \"modulePrefix\", _environment.default.modulePrefix);\n\n      _defineProperty(this, \"podModulePrefix\", _environment.default.podModulePrefix);\n\n      _defineProperty(this, \"Resolver\", _emberResolver.default);\n    }\n\n  }\n\n  _exports.default = App;\n  (0, _emberLoadInitializers.default)(App, _environment.default.modulePrefix);\n});","define(\"m08-2020/cldrs/en\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  /*jslint eqeq: true*/\n  var _default = [{\n    \"locale\": \"en-US\",\n    \"parentLocale\": \"en\"\n  }, {\n    \"locale\": \"en\",\n    \"pluralRuleFunction\": function (n, ord) {\n      var s = String(n).split(\".\"),\n          v0 = !s[1],\n          t0 = Number(s[0]) == n,\n          n10 = t0 && s[0].slice(-1),\n          n100 = t0 && s[0].slice(-2);\n      if (ord) return n10 == 1 && n100 != 11 ? \"one\" : n10 == 2 && n100 != 12 ? \"two\" : n10 == 3 && n100 != 13 ? \"few\" : \"other\";\n      return n == 1 && v0 ? \"one\" : \"other\";\n    },\n    \"fields\": {\n      \"year\": {\n        \"displayName\": \"year\",\n        \"relative\": {\n          \"0\": \"this year\",\n          \"1\": \"next year\",\n          \"-1\": \"last year\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} year\",\n            \"other\": \"in {0} years\"\n          },\n          \"past\": {\n            \"one\": \"{0} year ago\",\n            \"other\": \"{0} years ago\"\n          }\n        }\n      },\n      \"year-short\": {\n        \"displayName\": \"yr.\",\n        \"relative\": {\n          \"0\": \"this yr.\",\n          \"1\": \"next yr.\",\n          \"-1\": \"last yr.\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} yr.\",\n            \"other\": \"in {0} yr.\"\n          },\n          \"past\": {\n            \"one\": \"{0} yr. ago\",\n            \"other\": \"{0} yr. ago\"\n          }\n        }\n      },\n      \"month\": {\n        \"displayName\": \"month\",\n        \"relative\": {\n          \"0\": \"this month\",\n          \"1\": \"next month\",\n          \"-1\": \"last month\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} month\",\n            \"other\": \"in {0} months\"\n          },\n          \"past\": {\n            \"one\": \"{0} month ago\",\n            \"other\": \"{0} months ago\"\n          }\n        }\n      },\n      \"month-short\": {\n        \"displayName\": \"mo.\",\n        \"relative\": {\n          \"0\": \"this mo.\",\n          \"1\": \"next mo.\",\n          \"-1\": \"last mo.\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} mo.\",\n            \"other\": \"in {0} mo.\"\n          },\n          \"past\": {\n            \"one\": \"{0} mo. ago\",\n            \"other\": \"{0} mo. ago\"\n          }\n        }\n      },\n      \"day\": {\n        \"displayName\": \"day\",\n        \"relative\": {\n          \"0\": \"today\",\n          \"1\": \"tomorrow\",\n          \"-1\": \"yesterday\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} day\",\n            \"other\": \"in {0} days\"\n          },\n          \"past\": {\n            \"one\": \"{0} day ago\",\n            \"other\": \"{0} days ago\"\n          }\n        }\n      },\n      \"day-short\": {\n        \"displayName\": \"day\",\n        \"relative\": {\n          \"0\": \"today\",\n          \"1\": \"tomorrow\",\n          \"-1\": \"yesterday\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} day\",\n            \"other\": \"in {0} days\"\n          },\n          \"past\": {\n            \"one\": \"{0} day ago\",\n            \"other\": \"{0} days ago\"\n          }\n        }\n      },\n      \"hour\": {\n        \"displayName\": \"hour\",\n        \"relative\": {\n          \"0\": \"this hour\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} hour\",\n            \"other\": \"in {0} hours\"\n          },\n          \"past\": {\n            \"one\": \"{0} hour ago\",\n            \"other\": \"{0} hours ago\"\n          }\n        }\n      },\n      \"hour-short\": {\n        \"displayName\": \"hr.\",\n        \"relative\": {\n          \"0\": \"this hour\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} hr.\",\n            \"other\": \"in {0} hr.\"\n          },\n          \"past\": {\n            \"one\": \"{0} hr. ago\",\n            \"other\": \"{0} hr. ago\"\n          }\n        }\n      },\n      \"minute\": {\n        \"displayName\": \"minute\",\n        \"relative\": {\n          \"0\": \"this minute\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} minute\",\n            \"other\": \"in {0} minutes\"\n          },\n          \"past\": {\n            \"one\": \"{0} minute ago\",\n            \"other\": \"{0} minutes ago\"\n          }\n        }\n      },\n      \"minute-short\": {\n        \"displayName\": \"min.\",\n        \"relative\": {\n          \"0\": \"this minute\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} min.\",\n            \"other\": \"in {0} min.\"\n          },\n          \"past\": {\n            \"one\": \"{0} min. ago\",\n            \"other\": \"{0} min. ago\"\n          }\n        }\n      },\n      \"second\": {\n        \"displayName\": \"second\",\n        \"relative\": {\n          \"0\": \"now\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} second\",\n            \"other\": \"in {0} seconds\"\n          },\n          \"past\": {\n            \"one\": \"{0} second ago\",\n            \"other\": \"{0} seconds ago\"\n          }\n        }\n      },\n      \"second-short\": {\n        \"displayName\": \"sec.\",\n        \"relative\": {\n          \"0\": \"now\"\n        },\n        \"relativeTime\": {\n          \"future\": {\n            \"one\": \"in {0} sec.\",\n            \"other\": \"in {0} sec.\"\n          },\n          \"past\": {\n            \"one\": \"{0} sec. ago\",\n            \"other\": \"{0} sec. ago\"\n          }\n        }\n      }\n    },\n    \"numbers\": {\n      \"decimal\": {\n        \"long\": [[1000, {\n          \"one\": [\"0 thousand\", 1],\n          \"other\": [\"0 thousand\", 1]\n        }], [10000, {\n          \"one\": [\"00 thousand\", 2],\n          \"other\": [\"00 thousand\", 2]\n        }], [100000, {\n          \"one\": [\"000 thousand\", 3],\n          \"other\": [\"000 thousand\", 3]\n        }], [1000000, {\n          \"one\": [\"0 million\", 1],\n          \"other\": [\"0 million\", 1]\n        }], [10000000, {\n          \"one\": [\"00 million\", 2],\n          \"other\": [\"00 million\", 2]\n        }], [100000000, {\n          \"one\": [\"000 million\", 3],\n          \"other\": [\"000 million\", 3]\n        }], [1000000000, {\n          \"one\": [\"0 billion\", 1],\n          \"other\": [\"0 billion\", 1]\n        }], [10000000000, {\n          \"one\": [\"00 billion\", 2],\n          \"other\": [\"00 billion\", 2]\n        }], [100000000000, {\n          \"one\": [\"000 billion\", 3],\n          \"other\": [\"000 billion\", 3]\n        }], [1000000000000, {\n          \"one\": [\"0 trillion\", 1],\n          \"other\": [\"0 trillion\", 1]\n        }], [10000000000000, {\n          \"one\": [\"00 trillion\", 2],\n          \"other\": [\"00 trillion\", 2]\n        }], [100000000000000, {\n          \"one\": [\"000 trillion\", 3],\n          \"other\": [\"000 trillion\", 3]\n        }]],\n        \"short\": [[1000, {\n          \"one\": [\"0K\", 1],\n          \"other\": [\"0K\", 1]\n        }], [10000, {\n          \"one\": [\"00K\", 2],\n          \"other\": [\"00K\", 2]\n        }], [100000, {\n          \"one\": [\"000K\", 3],\n          \"other\": [\"000K\", 3]\n        }], [1000000, {\n          \"one\": [\"0M\", 1],\n          \"other\": [\"0M\", 1]\n        }], [10000000, {\n          \"one\": [\"00M\", 2],\n          \"other\": [\"00M\", 2]\n        }], [100000000, {\n          \"one\": [\"000M\", 3],\n          \"other\": [\"000M\", 3]\n        }], [1000000000, {\n          \"one\": [\"0B\", 1],\n          \"other\": [\"0B\", 1]\n        }], [10000000000, {\n          \"one\": [\"00B\", 2],\n          \"other\": [\"00B\", 2]\n        }], [100000000000, {\n          \"one\": [\"000B\", 3],\n          \"other\": [\"000B\", 3]\n        }], [1000000000000, {\n          \"one\": [\"0T\", 1],\n          \"other\": [\"0T\", 1]\n        }], [10000000000000, {\n          \"one\": [\"00T\", 2],\n          \"other\": [\"00T\", 2]\n        }], [100000000000000, {\n          \"one\": [\"000T\", 3],\n          \"other\": [\"000T\", 3]\n        }]]\n      }\n    }\n  }];\n  _exports.default = _default;\n});","define(\"m08-2020/component-managers/glimmer\", [\"exports\", \"@glimmer/component/-private/ember-component-manager\"], function (_exports, _emberComponentManager) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _emberComponentManager.default;\n    }\n  });\n});","define(\"m08-2020/components/ember-jstree\", [\"exports\", \"ember-cli-jstree/components/ember-jstree\"], function (_exports, _emberJstree) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _emberJstree.default;\n    }\n  });\n});","define(\"m08-2020/components/file-dropzone\", [\"exports\", \"ember-file-upload/components/file-dropzone/component\"], function (_exports, _component) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _component.default;\n    }\n  });\n});","define(\"m08-2020/components/file-picker\", [\"exports\", \"ember-cli-file-picker/components/file-picker\"], function (_exports, _filePicker) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _filePicker.default;\n    }\n  });\n});","define(\"m08-2020/components/file-upload\", [\"exports\", \"ember-file-upload/components/file-upload/component\"], function (_exports, _component) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _component.default;\n    }\n  });\n});","define(\"m08-2020/components/welcome-page\", [\"exports\", \"ember-welcome-page/components/welcome-page\"], function (_exports, _welcomePage) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _welcomePage.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-branch\", [\"exports\", \"ember-simple-tree/components/x-tree-branch\"], function (_exports, _xTreeBranch) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeBranch.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-checkbox\", [\"exports\", \"ember-simple-tree/components/x-tree-checkbox\"], function (_exports, _xTreeCheckbox) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeCheckbox.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-children\", [\"exports\", \"ember-simple-tree/components/x-tree-children\"], function (_exports, _xTreeChildren) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeChildren.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-collapsed-icon\", [\"exports\", \"ember-simple-tree/components/x-tree-collapsed-icon\"], function (_exports, _xTreeCollapsedIcon) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeCollapsedIcon.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-expanded-icon\", [\"exports\", \"ember-simple-tree/components/x-tree-expanded-icon\"], function (_exports, _xTreeExpandedIcon) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeExpandedIcon.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-node\", [\"exports\", \"ember-simple-tree/components/x-tree-node\"], function (_exports, _xTreeNode) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeNode.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree-toggle\", [\"exports\", \"ember-simple-tree/components/x-tree-toggle\"], function (_exports, _xTreeToggle) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTreeToggle.default;\n    }\n  });\n});","define(\"m08-2020/components/x-tree\", [\"exports\", \"ember-simple-tree/components/x-tree\"], function (_exports, _xTree) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _xTree.default;\n    }\n  });\n});","define(\"m08-2020/controllers/application\", [\"exports\", \"three\", \"m08-2020/lib/ThreeModule\", \"jquery\", \"polybooljs\", \"@tweenjs/tween.js\"], function (_exports, THREE, _ThreeModule, _jquery, _polybooljs, _tween) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _obj;\n\n  function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }\n\n  //Schaltet die Deprication Warnungen aus\n  Ember.deprecate = function () {};\n\n  var _default = Ember.Controller.extend((_obj = {\n    version: \"##version##\",\n    loggedIn: true,\n    wuerth: false,\n    swg: true,\n    verbaende: false,\n    intl: Ember.inject.service(),\n    appController: Ember.inject.controller('application'),\n    init: function () {\n      this._super();\n    },\n    initialisierung: function () {\n      var self = this;\n    },\n\n    loadAction() {\n      (0, _jquery.default)('#file-input').trigger('click');\n    },\n\n    async openFileAction(file) {\n      console.log(\"fileLoaded action \");\n      var self = this;\n      console.log(\"jetzt werden die werte gesetzt!\");\n      var content = JSON.parse(await this.getFileData(event));\n      console.log('filecontent'); // console.log(content);\n\n      console.log(content.drawingObjects); // console.log(content.drawingObjects.length);\n      //\n      // console.log(content.drawingObjects.findBy('id', \"ZO1\"));\n      // console.log(content.drawingObjects.arrows);\n\n      self.graphicsThree3D.clearScene();\n      self.graphicsThree3D.drawCustom(content); // self.graphicsThree3D.drawObject(content);\n      // self.graphicsThree3D.drawFastenerAsync();\n    },\n\n    getFileData: function (event) {\n      var self = this; // getting a hold of the file reference\n\n      var file = event.target.files[0];\n      console.log(\"Dateiname: \" + event.target.files[0].name); // setting up the reader\n\n      var reader = new FileReader();\n      reader.readAsText(file, 'UTF-8'); //return data string\n\n      return new Promise(resolve => {\n        reader.onload = readerEvent => {\n          resolve(readerEvent.target.result);\n        };\n      });\n    },\n    jsonUrl: 'example.json',\n    //common ember part\n    x: \"\",\n    y: \"\",\n    z: \"\",\n    placeholderX: \"x\",\n    placeholderY: \"y\",\n    placeholderZ: \"z\",\n    graphicsThree3D: null,\n\n    setXInteractive(variable, value) {\n      this.get('model').application.objectAt(0).set(variable, value);\n      var newX = Number(this.get('model').application.objectAt(0).get('x'));\n      if (!this.isNumeric(newX)) newX = null;\n      this.graphicsThree3D.highlightX(newX);\n    },\n\n    setYInteractive(variable, value) {\n      this.get('model').application.objectAt(0).set(variable, value);\n      var newY = Number(this.get('model').application.objectAt(0).get('y'));\n      if (!this.isNumeric(newY)) newY = null;\n      this.graphicsThree3D.highlightY(newY);\n    },\n\n    setZInteractive(variable, value) {\n      this.get('model').application.objectAt(0).set(variable, value); //this.cube.geometry = new THREE.BoxGeometry(Number(this.get('model').application.objectAt(0).get('x')), Number(this.get('model').application.objectAt(0).get('y')), Number(this.get('model').application.objectAt(0).get('z')));\n\n      var newZ = Number(this.get('model').application.objectAt(0).get('z'));\n      if (!this.isNumeric(newZ)) newZ = null;\n      this.graphicsThree3D.highlightZ(newZ); //   while(this.scene.children.length > 0){\n      //     this.scene.remove(this.scene.children[0]);\n      // }\n      // this.meshArray=[];\n      // this.xPosIndeX=0;\n      // this.xPosIndeY=0;\n      // //console.log(newZ);\n      // if(newZ)\n      // for (let i = 0; i < this.extrusionVectorZOfObjectsArray.length; i++) {\n      //   this.extrusionVectorZOfObjectsArray[i] = newZ;\n      // }\n      // //var isInteractiveZ=newZ?false:true;\n      //   this.AddShapesToScene(newZ,true);\n    },\n\n    clearXInteractive() {\n      this.graphicsThree3D.clearXHighlight();\n    },\n\n    clearYInteractive() {\n      this.graphicsThree3D.clearYHighlight();\n    },\n\n    clearZInteractive() {\n      this.graphicsThree3D.clearZHighlight();\n    },\n\n    isNumeric(n) {\n      return !isNaN(parseFloat(n)) && isFinite(n);\n    },\n\n    //get objects json and initialize three.js scene\n    threejsscene: Ember.computed('', function () {\n      const self = this;\n      self.graphicsThree3D = new _ThreeModule.GraphicsThree3D();\n      let returnValue = self.graphicsThree3D.getGraphicsCanvas(); // self.get('geometrieController').set3D(-1, { target: { name: \"initialization\" }});\n\n      self.getAllJsonWithAjax(self);\n      return returnValue;\n    }),\n\n    getAllJsonWithAjax(self) {\n      _jquery.default.ajax({\n        'async': true,\n        type: \"GET\",\n        url: self.jsonUrl\n      }).then(function (response) {\n        self.graphicsThree3D.globalJSONData = response;\n        self.graphicsThree3D.drawCustom(response);\n        self.graphicsThree3D.ChangeCameraPosition0(); // let i = 1;\n        // setInterval(() => {\n        //   let count = `00${i}`.slice(-2);\n        //   console.log(count)\n        //   self.graphicsThree3D.clearScene();\n        //   let newResponse = `http://localhost:12345/Jsons/DoEx40_${count}_20210217.json`;\n        //   $.ajax({\n        //     'async': false,\n        //     type: \"GET\",\n        //     url: newResponse\n        //   }).then(function (response) {\n        //     self.graphicsThree3D.drawCustom(response);\n        //   }, function (error) {\n        //     console.log(error);\n        //   });\n        //   i++;\n        // }, 1000);\n      }, function (error) {\n        console.log(error);\n      });\n    },\n\n    ChangePosition0() {\n      this.graphicsThree3D.ChangeCameraPosition0();\n    },\n\n    ChangePosition1() {\n      this.graphicsThree3D.ChangeCameraPosition1();\n    },\n\n    ChangePosition2() {\n      this.graphicsThree3D.ChangeCameraPosition2();\n    },\n\n    ChangePosition3() {\n      this.graphicsThree3D.ChangeCameraPosition3();\n    },\n\n    changeFastenersVisibility() {\n      this.graphicsThree3D.changeFastenersVisibility();\n    },\n\n    changeTextsZoomableStatus() {\n      this.graphicsThree3D.changeTextsZoomableStatus();\n    },\n\n    changeTextSize(e) {\n      this.graphicsThree3D.changeTextsSize(e.target.id);\n      const element = document.getElementById('font-size-text');\n      element.innerHTML = this.graphicsThree3D.ZoomController.textScale;\n    },\n\n    ToggleWireframe() {\n      this.graphicsThree3D.ToggleWireframe();\n    },\n\n    loadDrawingPlanesHQ() {\n      this.graphicsThree3D.DrawingPlaneLoader.loadDrawingPlanes(\"high\");\n    },\n\n    loadDrawingPlanesMQ() {\n      this.graphicsThree3D.DrawingPlaneLoader.loadDrawingPlanes(\"medium\");\n    },\n\n    loadDrawingPlanesLQ() {\n      this.graphicsThree3D.DrawingPlaneLoader.loadDrawingPlanes(\"low\");\n    },\n\n    loadDXF3D() {\n      this.graphicsThree3D.dxfGenerator.downloadDXF3D();\n    },\n\n    changeDimChainViewThreshold(e) {\n      this.graphicsThree3D.DimChainViewController.setAngleThreshold(e.target.value);\n    },\n\n    SendBase64PNGToServer() {\n      this.graphicsThree3D.SendBase64PNGToServer();\n    } // @action\n    // changecamerax(){\n    //   this.camera.position.x+=0.1;\n    //   this.controls.update();\n    // },\n    // @action\n    // changecameray(){\n    //   this.camera.position.y-=0.1;\n    //   this.controls.update();\n    // },\n\n\n  }, (_applyDecoratedDescriptor(_obj, \"loadAction\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"loadAction\"), _obj), _applyDecoratedDescriptor(_obj, \"openFileAction\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"openFileAction\"), _obj), _applyDecoratedDescriptor(_obj, \"setXInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"setXInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"setYInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"setYInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"setZInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"setZInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"clearXInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"clearXInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"clearYInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"clearYInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"clearZInteractive\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"clearZInteractive\"), _obj), _applyDecoratedDescriptor(_obj, \"ChangePosition0\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"ChangePosition0\"), _obj), _applyDecoratedDescriptor(_obj, \"ChangePosition1\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"ChangePosition1\"), _obj), _applyDecoratedDescriptor(_obj, \"ChangePosition2\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"ChangePosition2\"), _obj), _applyDecoratedDescriptor(_obj, \"ChangePosition3\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"ChangePosition3\"), _obj), _applyDecoratedDescriptor(_obj, \"changeFastenersVisibility\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"changeFastenersVisibility\"), _obj), _applyDecoratedDescriptor(_obj, \"changeTextsZoomableStatus\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"changeTextsZoomableStatus\"), _obj), _applyDecoratedDescriptor(_obj, \"changeTextSize\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"changeTextSize\"), _obj), _applyDecoratedDescriptor(_obj, \"ToggleWireframe\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"ToggleWireframe\"), _obj), _applyDecoratedDescriptor(_obj, \"loadDrawingPlanesHQ\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"loadDrawingPlanesHQ\"), _obj), _applyDecoratedDescriptor(_obj, \"loadDrawingPlanesMQ\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"loadDrawingPlanesMQ\"), _obj), _applyDecoratedDescriptor(_obj, \"loadDrawingPlanesLQ\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"loadDrawingPlanesLQ\"), _obj), _applyDecoratedDescriptor(_obj, \"loadDXF3D\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"loadDXF3D\"), _obj), _applyDecoratedDescriptor(_obj, \"changeDimChainViewThreshold\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"changeDimChainViewThreshold\"), _obj), _applyDecoratedDescriptor(_obj, \"SendBase64PNGToServer\", [Ember._action], Object.getOwnPropertyDescriptor(_obj, \"SendBase64PNGToServer\"), _obj)), _obj));\n\n  _exports.default = _default;\n});","define(\"m08-2020/data-adapter\", [\"exports\", \"@ember-data/debug\"], function (_exports, _debug) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _debug.default;\n    }\n  });\n});","define(\"m08-2020/formats\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = {\n    time: {\n      hhmmss: {\n        hour: 'numeric',\n        minute: 'numeric',\n        second: 'numeric'\n      }\n    },\n    date: {\n      hhmmss: {\n        hour: 'numeric',\n        minute: 'numeric',\n        second: 'numeric'\n      }\n    },\n    number: {\n      EUR: {\n        style: 'currency',\n        currency: 'EUR',\n        minimumFractionDigits: 2,\n        maximumFractionDigits: 2\n      },\n      USD: {\n        style: 'currency',\n        currency: 'USD',\n        minimumFractionDigits: 2,\n        maximumFractionDigits: 2\n      }\n    }\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/helpers/app-version\", [\"exports\", \"m08-2020/config/environment\", \"ember-cli-app-version/utils/regexp\"], function (_exports, _environment, _regexp) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.appVersion = appVersion;\n  _exports.default = void 0;\n\n  function appVersion(_, hash = {}) {\n    const version = _environment.default.APP.version; // e.g. 1.0.0-alpha.1+4jds75hf\n    // Allow use of 'hideSha' and 'hideVersion' For backwards compatibility\n\n    let versionOnly = hash.versionOnly || hash.hideSha;\n    let shaOnly = hash.shaOnly || hash.hideVersion;\n    let match = null;\n\n    if (versionOnly) {\n      if (hash.showExtended) {\n        match = version.match(_regexp.versionExtendedRegExp); // 1.0.0-alpha.1\n      } // Fallback to just version\n\n\n      if (!match) {\n        match = version.match(_regexp.versionRegExp); // 1.0.0\n      }\n    }\n\n    if (shaOnly) {\n      match = version.match(_regexp.shaRegExp); // 4jds75hf\n    }\n\n    return match ? match[0] : version;\n  }\n\n  var _default = Ember.Helper.helper(appVersion);\n\n  _exports.default = _default;\n});","define(\"m08-2020/helpers/file-queue\", [\"exports\", \"ember-file-upload/helpers/file-queue\"], function (_exports, _fileQueue) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _fileQueue.default;\n    }\n  });\n});","define(\"m08-2020/helpers/format-date\", [\"exports\", \"ember-intl/helpers/format-date\"], function (_exports, _formatDate) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _formatDate.default;\n    }\n  });\n});","define(\"m08-2020/helpers/format-message\", [\"exports\", \"ember-intl/helpers/format-message\"], function (_exports, _formatMessage) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _formatMessage.default;\n    }\n  });\n});","define(\"m08-2020/helpers/format-number\", [\"exports\", \"ember-intl/helpers/format-number\"], function (_exports, _formatNumber) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _formatNumber.default;\n    }\n  });\n});","define(\"m08-2020/helpers/format-relative\", [\"exports\", \"ember-intl/helpers/format-relative\"], function (_exports, _formatRelative) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _formatRelative.default;\n    }\n  });\n});","define(\"m08-2020/helpers/format-time\", [\"exports\", \"ember-intl/helpers/format-time\"], function (_exports, _formatTime) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _formatTime.default;\n    }\n  });\n});","define(\"m08-2020/helpers/pluralize\", [\"exports\", \"ember-inflector/lib/helpers/pluralize\"], function (_exports, _pluralize) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = _pluralize.default;\n  _exports.default = _default;\n});","define(\"m08-2020/helpers/send\", [\"exports\", \"ember-component-inbound-actions/helpers/send\"], function (_exports, _send) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _send.default;\n    }\n  });\n});","define(\"m08-2020/helpers/singularize\", [\"exports\", \"ember-inflector/lib/helpers/singularize\"], function (_exports, _singularize) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = _singularize.default;\n  _exports.default = _default;\n});","define(\"m08-2020/helpers/t\", [\"exports\", \"ember-intl/helpers/t\"], function (_exports, _t) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _t.default;\n    }\n  });\n});","define(\"m08-2020/initializers/app-version\", [\"exports\", \"ember-cli-app-version/initializer-factory\", \"m08-2020/config/environment\"], function (_exports, _initializerFactory, _environment) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  let name, version;\n\n  if (_environment.default.APP) {\n    name = _environment.default.APP.name;\n    version = _environment.default.APP.version;\n  }\n\n  var _default = {\n    name: 'App Version',\n    initialize: (0, _initializerFactory.default)(name, version)\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/initializers/container-debug-adapter\", [\"exports\", \"ember-resolver/resolvers/classic/container-debug-adapter\"], function (_exports, _containerDebugAdapter) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = {\n    name: 'container-debug-adapter',\n\n    initialize() {\n      let app = arguments[1] || arguments[0];\n      app.register('container-debug-adapter:main', _containerDebugAdapter.default);\n      app.inject('container-debug-adapter:main', 'namespace', 'application:main');\n    }\n\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/initializers/ember-data-data-adapter\", [\"exports\", \"@ember-data/debug/setup\"], function (_exports, _setup) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _setup.default;\n    }\n  });\n});","define(\"m08-2020/initializers/ember-data\", [\"exports\", \"ember-data/setup-container\", \"ember-data\"], function (_exports, _setupContainer, _emberData) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  /*\n    This code initializes EmberData in an Ember application.\n  \n    It ensures that the `store` service is automatically injected\n    as the `store` property on all routes and controllers.\n  */\n  var _default = {\n    name: 'ember-data',\n    initialize: _setupContainer.default\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/initializers/export-application-global\", [\"exports\", \"m08-2020/config/environment\"], function (_exports, _environment) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  _exports.initialize = initialize;\n\n  function initialize() {\n    var application = arguments[1] || arguments[0];\n\n    if (_environment.default.exportApplicationGlobal !== false) {\n      var theGlobal;\n\n      if (typeof window !== 'undefined') {\n        theGlobal = window;\n      } else if (typeof global !== 'undefined') {\n        theGlobal = global;\n      } else if (typeof self !== 'undefined') {\n        theGlobal = self;\n      } else {\n        // no reasonable global, just bail\n        return;\n      }\n\n      var value = _environment.default.exportApplicationGlobal;\n      var globalName;\n\n      if (typeof value === 'string') {\n        globalName = value;\n      } else {\n        globalName = Ember.String.classify(_environment.default.modulePrefix);\n      }\n\n      if (!theGlobal[globalName]) {\n        theGlobal[globalName] = application;\n        application.reopen({\n          willDestroy: function () {\n            this._super.apply(this, arguments);\n\n            delete theGlobal[globalName];\n          }\n        });\n      }\n    }\n  }\n\n  var _default = {\n    name: 'export-application-global',\n    initialize: initialize\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/initializers/model-locale\", [\"exports\", \"ember-model-validator/initializers/model-locale\"], function (_exports, _modelLocale) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _modelLocale.default;\n    }\n  });\n  Object.defineProperty(_exports, \"initialize\", {\n    enumerable: true,\n    get: function () {\n      return _modelLocale.initialize;\n    }\n  });\n});","define(\"m08-2020/instance-initializers/ember-data\", [\"exports\", \"ember-data/initialize-store-service\"], function (_exports, _initializeStoreService) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = {\n    name: 'ember-data',\n    initialize: _initializeStoreService.default\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/lib old/CSGMesh\", [\"exports\"], function (_exports) {\n  \"use strict\"; //https://github.com/manthrax/THREE-CSGMesh\n  // ## License\n  //\n  // Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.\n  // THREE.js rework by thrax\n  // # class CSG\n  // Holds a binary space partition tree representing a 3D solid. Two solids can\n  // be combined using the `union()`, `subtract()`, and `intersect()` methods.\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.CsgFactory = CsgFactory;\n\n  function CsgFactory(THREE) {\n    class CSG {\n      constructor() {\n        this.polygons = [];\n      }\n\n      clone() {\n        var csg = new CSG();\n        csg.polygons = this.polygons.map(function (p) {\n          return p.clone();\n        });\n        return csg;\n      }\n\n      toPolygons() {\n        return this.polygons;\n      }\n\n      union(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.clipTo(b);\n        b.clipTo(a);\n        b.invert();\n        b.clipTo(a);\n        b.invert();\n        a.build(b.allPolygons());\n        return CSG.fromPolygons(a.allPolygons());\n      }\n\n      subtract(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.invert();\n        a.clipTo(b);\n        b.clipTo(a);\n        b.invert();\n        b.clipTo(a);\n        b.invert();\n        a.build(b.allPolygons());\n        a.invert();\n        return CSG.fromPolygons(a.allPolygons());\n      }\n\n      intersect(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.invert();\n        b.clipTo(a);\n        b.invert();\n        a.clipTo(b);\n        b.clipTo(a);\n        a.build(b.allPolygons());\n        a.invert();\n        return CSG.fromPolygons(a.allPolygons());\n      } // Return a new CSG solid with solid and empty space switched. This solid is\n      // not modified.\n\n\n      inverse() {\n        var csg = this.clone();\n        csg.polygons.map(function (p) {\n          p.flip();\n        });\n        return csg;\n      }\n\n    } // Construct a CSG solid from a list of `Polygon` instances.\n\n\n    CSG.fromPolygons = function (polygons) {\n      var csg = new CSG();\n      csg.polygons = polygons;\n      return csg;\n    }; // # class Vector\n    // Represents a 3D vector.\n    //\n    // Example usage:\n    //\n    //     new CSG.Vector(1, 2, 3);\n    //     new CSG.Vector([1, 2, 3]);\n    //     new CSG.Vector({ x: 1, y: 2, z: 3 });\n\n\n    class Vector extends THREE.Vector3 {\n      constructor(x, y, z) {\n        if (arguments.length == 3) super(x, y, z);else if (Array.isArray(x)) super(x[0], x[1], x[2]);else if (typeof x == 'object') super().copy(x);else throw \"Invalid constructor to vector\";\n      }\n\n      clone() {\n        return new Vector(this);\n      }\n\n      negated() {\n        return this.clone().multiplyScalar(-1);\n      }\n\n      plus(a) {\n        return this.clone().add(a);\n      }\n\n      minus(a) {\n        return this.clone().sub(a);\n      }\n\n      times(a) {\n        return this.clone().multiplyScalar(a);\n      }\n\n      dividedBy(a) {\n        return this.clone().divideScalar(a);\n      }\n\n      lerp(a, t) {\n        return this.plus(a.minus(this).times(t));\n      }\n\n      unit() {\n        return this.dividedBy(this.length());\n      }\n\n      cross(a) {\n        return THREE.Vector3.prototype.cross.call(this.clone(), a);\n      }\n\n    } // # class Vertex\n    // Represents a vertex of a polygon. Use your own vertex class instead of this\n    // one to provide additional features like texture coordinates and vertex\n    // colors. Custom vertex classes need to provide a `pos` property and `clone()`,\n    // `flip()`, and `interpolate()` methods that behave analogous to the ones\n    // defined by `CSG.Vertex`. This class provides `normal` so convenience\n    // functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`\n    // is not used anywhere else.\n\n\n    class Vertex {\n      constructor(pos, normal, uv) {\n        this.pos = new Vector(pos);\n        this.normal = new Vector(normal);\n        this.uv = new Vector(uv);\n      }\n\n      clone() {\n        return new Vertex(this.pos.clone(), this.normal.clone(), this.uv.clone());\n      } // Invert all orientation-specific data (e.g. vertex normal). Called when the\n      // orientation of a polygon is flipped.\n\n\n      flip() {\n        this.normal = this.normal.negated();\n      } // Create a new vertex between this vertex and `other` by linearly\n      // interpolating all properties using a parameter of `t`. Subclasses should\n      // override this to interpolate additional properties.\n\n\n      interpolate(other, t) {\n        return new Vertex(this.pos.lerp(other.pos, t), this.normal.lerp(other.normal, t), this.uv.lerp(other.uv, t));\n      }\n\n    }\n\n    ; // # class Plane\n    // Represents a plane in 3D space.\n\n    class Plane {\n      constructor(normal, w) {\n        this.normal = normal;\n        this.w = w;\n      }\n\n      clone() {\n        return new Plane(this.normal.clone(), this.w);\n      }\n\n      flip() {\n        this.normal = this.normal.negated();\n        this.w = -this.w;\n      } // Split `polygon` by this plane if needed, then put the polygon or polygon\n      // fragments in the appropriate lists. Coplanar polygons go into either\n      // `coplanarFront` or `coplanarBack` depending on their orientation with\n      // respect to this plane. Polygons in front or in back of this plane go into\n      // either `front` or `back`.\n\n\n      splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {\n        var COPLANAR = 0;\n        var FRONT = 1;\n        var BACK = 2;\n        var SPANNING = 3; // Classify each point as well as the entire polygon into one of the above\n        // four classes.\n\n        var polygonType = 0;\n        var types = [];\n\n        for (var i = 0; i < polygon.vertices.length; i++) {\n          var t = this.normal.dot(polygon.vertices[i].pos) - this.w;\n          var type = t < -Plane.EPSILON ? BACK : t > Plane.EPSILON ? FRONT : COPLANAR;\n          polygonType |= type;\n          types.push(type);\n        } // Put the polygon in the correct list, splitting it when necessary.\n\n\n        switch (polygonType) {\n          case COPLANAR:\n            (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);\n            break;\n\n          case FRONT:\n            front.push(polygon);\n            break;\n\n          case BACK:\n            back.push(polygon);\n            break;\n\n          case SPANNING:\n            var f = [],\n                b = [];\n\n            for (var i = 0; i < polygon.vertices.length; i++) {\n              var j = (i + 1) % polygon.vertices.length;\n              var ti = types[i],\n                  tj = types[j];\n              var vi = polygon.vertices[i],\n                  vj = polygon.vertices[j];\n              if (ti != BACK) f.push(vi);\n              if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi);\n\n              if ((ti | tj) == SPANNING) {\n                var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos));\n                var v = vi.interpolate(vj, t);\n                f.push(v);\n                b.push(v.clone());\n              }\n            }\n\n            if (f.length >= 3) front.push(new Polygon(f, polygon.shared));\n            if (b.length >= 3) back.push(new Polygon(b, polygon.shared));\n            break;\n        }\n      }\n\n    } // `Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a\n    // point is on the plane.\n\n\n    Plane.EPSILON = 1e-5;\n\n    Plane.fromPoints = function (a, b, c) {\n      var n = b.minus(a).cross(c.minus(a)).unit();\n      return new Plane(n, n.dot(a));\n    }; // # class Polygon\n    // Represents a convex polygon. The vertices used to initialize a polygon must\n    // be coplanar and form a convex loop. They do not have to be `Vertex`\n    // instances but they must behave similarly (duck typing can be used for\n    // customization).\n    //\n    // Each convex polygon has a `shared` property, which is shared between all\n    // polygons that are clones of each other or were split from the same polygon.\n    // This can be used to define per-polygon properties (such as surface color).\n\n\n    class Polygon {\n      constructor(vertices, shared) {\n        this.vertices = vertices;\n        this.shared = shared;\n        this.plane = Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);\n      }\n\n      clone() {\n        var vertices = this.vertices.map(function (v) {\n          return v.clone();\n        });\n        return new Polygon(vertices, this.shared);\n      }\n\n      flip() {\n        this.vertices.reverse().map(function (v) {\n          v.flip();\n        });\n        this.plane.flip();\n      }\n\n    } // # class Node\n    // Holds a node in a BSP tree. A BSP tree is built from a collection of polygons\n    // by picking a polygon to split along. That polygon (and all other coplanar\n    // polygons) are added directly to that node and the other polygons are added to\n    // the front and/or back subtrees. This is not a leafy BSP tree since there is\n    // no distinction between internal and leaf nodes.\n\n\n    class Node {\n      constructor(polygons) {\n        this.plane = null;\n        this.front = null;\n        this.back = null;\n        this.polygons = [];\n        if (polygons) this.build(polygons);\n      }\n\n      clone() {\n        var node = new Node();\n        node.plane = this.plane && this.plane.clone();\n        node.front = this.front && this.front.clone();\n        node.back = this.back && this.back.clone();\n        node.polygons = this.polygons.map(function (p) {\n          return p.clone();\n        });\n        return node;\n      } // Convert solid space to empty space and empty space to solid space.\n\n\n      invert() {\n        for (var i = 0; i < this.polygons.length; i++) this.polygons[i].flip();\n\n        this.plane.flip();\n        if (this.front) this.front.invert();\n        if (this.back) this.back.invert();\n        var temp = this.front;\n        this.front = this.back;\n        this.back = temp;\n      } // Recursively remove all polygons in `polygons` that are inside this BSP\n      // tree.\n\n\n      clipPolygons(polygons) {\n        if (!this.plane) return polygons.slice();\n        var front = [],\n            back = [];\n\n        for (var i = 0; i < polygons.length; i++) {\n          this.plane.splitPolygon(polygons[i], front, back, front, back);\n        }\n\n        if (this.front) front = this.front.clipPolygons(front);\n        if (this.back) back = this.back.clipPolygons(back);else back = [];\n        return front.concat(back);\n      } // Remove all polygons in this BSP tree that are inside the other BSP tree\n      // `bsp`.\n\n\n      clipTo(bsp) {\n        this.polygons = bsp.clipPolygons(this.polygons);\n        if (this.front) this.front.clipTo(bsp);\n        if (this.back) this.back.clipTo(bsp);\n      } // Return a list of all polygons in this BSP tree.\n\n\n      allPolygons() {\n        var polygons = this.polygons.slice();\n        if (this.front) polygons = polygons.concat(this.front.allPolygons());\n        if (this.back) polygons = polygons.concat(this.back.allPolygons());\n        return polygons;\n      } // Build a BSP tree out of `polygons`. When called on an existing tree, the\n      // new polygons are filtered down to the bottom of the tree and become new\n      // nodes there. Each set of polygons is partitioned using the first polygon\n      // (no heuristic is used to pick a good split).\n\n\n      build(polygons) {\n        if (!polygons.length) return;\n        if (!this.plane) this.plane = polygons[0].plane.clone();\n        var front = [],\n            back = [];\n\n        for (var i = 0; i < polygons.length; i++) {\n          this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n        }\n\n        if (front.length) {\n          if (!this.front) this.front = new Node();\n          this.front.build(front);\n        }\n\n        if (back.length) {\n          if (!this.back) this.back = new Node();\n          this.back.build(back);\n        }\n      }\n\n    }\n\n    CSG.fromGeometry = function (geom) {\n      if (geom.isBufferGeometry) geom = new THREE.Geometry().fromBufferGeometry(geom);\n      var fs = geom.faces;\n      var vs = geom.vertices;\n      var polys = [];\n      var fm = ['a', 'b', 'c'];\n\n      for (var i = 0; i < fs.length; i++) {\n        var f = fs[i];\n        var vertices = [];\n\n        for (var j = 0; j < 3; j++) vertices.push(new Vertex(vs[f[fm[j]]], f.vertexNormals[j], geom.faceVertexUvs[0][i][j]));\n\n        polys.push(new Polygon(vertices));\n      }\n\n      return CSG.fromPolygons(polys);\n    };\n\n    CSG._tmpm3 = new THREE.Matrix3();\n\n    CSG.fromMesh = function (mesh) {\n      var csg = CSG.fromGeometry(mesh.geometry);\n\n      CSG._tmpm3.getNormalMatrix(mesh.matrix);\n\n      for (var i = 0; i < csg.polygons.length; i++) {\n        var p = csg.polygons[i];\n\n        for (var j = 0; j < p.vertices.length; j++) {\n          var v = p.vertices[j];\n          v.pos.applyMatrix4(mesh.matrix);\n          v.normal.applyMatrix3(CSG._tmpm3);\n        }\n      }\n\n      return csg;\n    };\n\n    CSG.toMesh = function (csg, toMatrix) {\n      var geom = new THREE.Geometry();\n      var ps = csg.polygons;\n      var vs = geom.vertices;\n      var fvuv = geom.faceVertexUvs[0];\n\n      for (var i = 0; i < ps.length; i++) {\n        var p = ps[i];\n        var pvs = p.vertices;\n        var v0 = vs.length;\n        var pvlen = pvs.length;\n\n        for (var j = 0; j < pvlen; j++) vs.push(new THREE.Vector3().copy(pvs[j].pos));\n\n        for (var j = 3; j <= pvlen; j++) {\n          var fc = new THREE.Face3();\n          var fuv = [];\n          fvuv.push(fuv);\n          var fnml = fc.vertexNormals;\n          fc.a = v0;\n          fc.b = v0 + j - 2;\n          fc.c = v0 + j - 1;\n          fnml.push(new THREE.Vector3().copy(pvs[0].normal));\n          fnml.push(new THREE.Vector3().copy(pvs[j - 2].normal));\n          fnml.push(new THREE.Vector3().copy(pvs[j - 1].normal));\n          fuv.push(new THREE.Vector3().copy(pvs[0].uv));\n          fuv.push(new THREE.Vector3().copy(pvs[j - 2].uv));\n          fuv.push(new THREE.Vector3().copy(pvs[j - 1].uv));\n          fc.normal = new THREE.Vector3().copy(p.plane.normal);\n          geom.faces.push(fc);\n        }\n      }\n\n      var inv = new THREE.Matrix4().getInverse(toMatrix);\n      geom.applyMatrix4(inv);\n      geom.verticesNeedUpdate = geom.elementsNeedUpdate = geom.normalsNeedUpdate = true;\n      geom.computeBoundingSphere();\n      geom.computeBoundingBox();\n      var m = new THREE.Mesh(geom);\n      m.matrix.copy(toMatrix);\n      m.matrix.decompose(m.position, m.rotation, m.scale);\n      m.updateMatrixWorld();\n      return m;\n    };\n\n    CSG.ieval = function (tokens, index = 0) {\n      if (typeof tokens === 'string') CSG.currentOp = tokens;else if (tokens instanceof Array) {\n        for (let i = 0; i < tokens.length; i++) CSG.ieval(tokens[i], 0);\n      } else if (typeof tokens === 'object') {\n        var op = CSG.currentOp;\n        tokens.updateMatrix();\n        tokens.updateMatrixWorld();\n        if (!CSG.sourceMesh) CSG.currentPrim = CSG.fromMesh(CSG.sourceMesh = tokens);else {\n          CSG.nextPrim = CSG.fromMesh(tokens);\n          CSG.currentPrim = CSG.currentPrim[op](CSG.nextPrim);\n        }\n        if (CSG.doRemove) tokens.parent.remove(tokens);\n      } //union,subtract,intersect,inverse\n    };\n\n    CSG.eval = function (tokens, doRemove) {\n      //[['add',mesh,mesh,mesh,mesh],['sub',mesh,mesh,mesh,mesh]]\n      CSG.currentOp = null;\n      CSG.sourceMesh = null;\n      CSG.doRemove = doRemove;\n      CSG.ieval(tokens);\n      var result = CSG.toMesh(CSG.currentPrim, CSG.sourceMesh.matrix);\n      result.material = CSG.sourceMesh.material;\n      result.castShadow = result.receiveShadow = true;\n      return result;\n    };\n\n    return CSG;\n  } //export default CsgFactory\n  // Return a new CSG solid representing space in either this solid or in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.union(B)\n  //\n  //     +-------+            +-------+\n  //     |       |            |       |\n  //     |   A   |            |       |\n  //     |    +--+----+   =   |       +----+\n  //     +----+--+    |       +----+       |\n  //          |   B   |            |       |\n  //          |       |            |       |\n  //          +-------+            +-------+\n  //\n  // Return a new CSG solid representing space in this solid but not in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.subtract(B)\n  //\n  //     +-------+            +-------+\n  //     |       |            |       |\n  //     |   A   |            |       |\n  //     |    +--+----+   =   |    +--+\n  //     +----+--+    |       +----+\n  //          |   B   |\n  //          |       |\n  //          +-------+\n  //\n  // Return a new CSG solid representing space both this solid and in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.intersect(B)\n  //\n  //     +-------+\n  //     |       |\n  //     |   A   |\n  //     |    +--+----+   =   +--+\n  //     +----+--+    |       +--+\n  //          |   B   |\n  //          |       |\n  //          +-------+\n  //\n\n});","define(\"m08-2020/lib old/ClippingUtils/DynamicSectionalPlane\", [\"exports\", \"three\", \"m08-2020/lib old/Utils\"], function (_exports, THREE, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DynamicSectionalPlane = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class DynamicSectionalPlane {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.sectionalPlanes = [];\n      this.renderOrder = 1;\n    }\n\n    applySectionalPlanes() {\n      this.sectionalPlanes.forEach(planeData => {\n        let targets = planeData.targets;\n        targets.drawingObjects.forEach(({\n          mesh,\n          clippingPlanes\n        }) => {\n          let targetClippingPlanes = this.sectionalPlanes.filter(plane => clippingPlanes.includes(plane.id));\n          this.clipMesh(planeData, mesh, targetClippingPlanes);\n        });\n        this.addPlaneToGUI(planeData);\n      });\n    }\n\n    initSectionalPlanes(dynamicSectionalPlane) {\n      let planeData = dynamicSectionalPlane.planeData;\n      let sectionalPlaneData = {\n        clippingPlane: this.makePlane(planeData),\n        positionVector: utils.initVector3(planeData.positionVector),\n        id: planeData.id,\n        range: planeData.range,\n        factor: dynamicSectionalPlane.factor,\n        stencilPlanes: [],\n        targets: {\n          drawingObjects: []\n        }\n      };\n      this.sectionalPlanes.push(sectionalPlaneData);\n    }\n\n    addPlaneToGUI(planeData) {\n      const clippingPlane = planeData.clippingPlane;\n      let cutPositionChanger = document.getElementById(\"cut-position-changer\");\n      let baseConstant = clippingPlane.constant;\n      cutPositionChanger.min = -planeData.range;\n      cutPositionChanger.max = planeData.range;\n      cutPositionChanger.value = 0;\n      cutPositionChanger.addEventListener(\"input\", ev => {\n        clippingPlane.constant = baseConstant + parseInt(ev.target.value);\n        this.moveStencilPlanes(planeData);\n      });\n      let cutVisibilityChanger = document.getElementById(\"show-cut\");\n      cutVisibilityChanger.addEventListener(\"change\", ev => this.togglePlaneVisibility(planeData, clippingPlane, ev.target.checked));\n      let cutDirectionChanger = document.getElementById(\"invert-cut\");\n      cutDirectionChanger.addEventListener(\"change\", () => clippingPlane.negate());\n    }\n\n    moveStencilPlanes(planeData) {\n      let {\n        clippingPlane,\n        stencilPlanes\n      } = planeData;\n      stencilPlanes.forEach(({\n        stencilPlane\n      }) => {\n        let projectedPlanePos = stencilPlane.position.clone().projectOnVector(clippingPlane.normal);\n        let clippingPlanePos = clippingPlane.normal.clone().multiplyScalar(-clippingPlane.constant);\n        let delta = clippingPlanePos.sub(projectedPlanePos);\n        stencilPlane.position.add(delta);\n      });\n    }\n\n    clipMesh(planeData, mesh, targetPlanes) {\n      mesh.updateMatrix();\n      let clippingPlane = planeData.clippingPlane;\n      let positionVector = planeData.positionVector;\n      let targetScene = this.GraphicsThree3D.stencilScenes.length ? new THREE.Scene() : this.GraphicsThree3D.scene;\n\n      if (mesh.stencilNeeded) {\n        let stencilAndOutline = {\n          stencilPlane: null,\n          stencilClippingPlanes: targetPlanes,\n          targetMesh: mesh,\n          stencilOpacity: 0.0,\n          targetScene\n        };\n\n        if (mesh.material) {\n          let colorProps = this.getColorPropsFromMaterial(mesh.material, planeData.factor);\n          stencilAndOutline.stencilOpacity = colorProps.alpha / 255.0;\n          let [frontFaceStencilMat, backFaceStencilMat, planeStencilMat] = this.getClippingMaterials(colorProps);\n          this.clipStencilMaterials(clippingPlane, targetPlanes, [frontFaceStencilMat, backFaceStencilMat, planeStencilMat]);\n          let meshGeo = mesh.geometry.clone();\n          let frontMesh = new THREE.Mesh(meshGeo, frontFaceStencilMat);\n          frontMesh.applyMatrix4(mesh.matrix);\n          let backMesh = new THREE.Mesh(meshGeo, backFaceStencilMat);\n          backMesh.applyMatrix4(mesh.matrix);\n          frontMesh.renderOrder = this.renderOrder;\n          backMesh.renderOrder = this.renderOrder;\n          frontMesh.position.copy(mesh.position);\n          backMesh.position.copy(mesh.position);\n          targetScene.add(frontMesh);\n          targetScene.add(backMesh);\n          let planeMesh = this.createStencilPlaneMesh(clippingPlane, positionVector, planeStencilMat);\n          this.GraphicsThree3D.initWireframeObject(planeMesh, new THREE.Mesh(), this.GraphicsThree3D.jsonData, targetScene);\n          targetScene.add(planeMesh);\n          stencilAndOutline.stencilPlane = planeMesh;\n        }\n\n        planeData.stencilPlanes.push(stencilAndOutline);\n      }\n\n      this.GraphicsThree3D.stencilScenes.push(targetScene);\n      if (mesh.children) mesh.children.forEach(child => this.clipMaterial(child.material, clippingPlane));\n      this.clipMaterial(mesh.material, clippingPlane);\n    }\n\n    clipStencilMaterials(clippingPlane, targetPlanes, [frontFaceStencilMat, backFaceStencilMat, planeStencilMat]) {\n      frontFaceStencilMat.clippingPlanes = [clippingPlane];\n      backFaceStencilMat.clippingPlanes = [clippingPlane];\n      planeStencilMat.clippingPlanes = targetPlanes.filter(plane => !plane.clippingPlane.equals(clippingPlane)).map(plane => plane.clippingPlane);\n    }\n\n    createStencilPlaneMesh(clippingPlane, positionVector, planeStencilMat) {\n      let planeNormal = clippingPlane.normal.clone();\n      let forwardVector = new THREE.Vector3(0, 0, -1);\n      let planeGeom = new THREE.PlaneBufferGeometry();\n      let planeMesh = new THREE.Mesh(planeGeom, planeStencilMat);\n      planeMesh.scale.setScalar(1000);\n      planeMesh.position.copy(positionVector);\n      planeMesh.quaternion.setFromUnitVectors(forwardVector, planeNormal);\n\n      planeMesh.onAfterRender = function (renderer) {\n        renderer.clearStencil();\n      };\n\n      planeMesh.renderOrder = this.renderOrder + 0.1;\n      return planeMesh;\n    }\n\n    getClippingMaterials(colorProps) {\n      //https://discourse.threejs.org/t/capping-clipped-planes-using-stencil-on-a-buffergeometry/18407/18\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n      let backFaceStencilMat = this.createBackStencilMat(); // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      let frontFaceStencilMat = this.createFrontStencilMat(); // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      let planeStencilMat = this.createPlaneStencilMat(colorProps);\n      return [frontFaceStencilMat, backFaceStencilMat, planeStencilMat];\n    }\n\n    createPlaneStencilMat(colorProps) {\n      let planeStencilMat = this.GraphicsThree3D.getMaterialFromColor(colorProps);\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = THREE.ReplaceStencilOp;\n      return planeStencilMat;\n    }\n\n    createBackStencilMat() {\n      let backFaceStencilMat = new THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = THREE.BackSide;\n      backFaceStencilMat.stencilFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = THREE.IncrementWrapStencilOp;\n      return backFaceStencilMat;\n    }\n\n    createFrontStencilMat() {\n      let frontFaceStencilMat = new THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = THREE.DecrementWrapStencilOp;\n      return frontFaceStencilMat;\n    }\n\n    getColorPropsFromMaterial(meshMaterial, factor) {\n      let material = meshMaterial.length ? meshMaterial[0].clone() : meshMaterial.clone();\n      let {\n        red,\n        green,\n        blue\n      } = this.modifyColorWithFactor(material.color, factor);\n      let colorProps = {\n        alpha: material.opacity * 255,\n        red,\n        green,\n        blue\n      };\n      return colorProps;\n    }\n\n    modifyColorWithFactor(color, factor) {\n      let {\n        r,\n        g,\n        b\n      } = color;\n      let red = r * 255;\n      let green = g * 255;\n      let blue = b * 255;\n\n      if (factor > 0) {\n        red += (255 - red) * factor;\n        green += (255 - green) * factor;\n        blue += (255 - blue) * factor;\n      } else {\n        red += red * factor;\n        green += green * factor;\n        blue += blue * factor;\n      }\n\n      return {\n        red,\n        green,\n        blue\n      };\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    togglePlaneVisibility(planeData, clippingPlane, isVisible) {\n      if (isVisible) this.showClippingPlane(planeData, clippingPlane);else this.hideClippingPlane(planeData, clippingPlane);\n    }\n\n    showClippingPlane(planeData, clippingPlane) {\n      planeData.targets.drawingObjects.forEach(meshData => this.addClippingPlaneToMesh(meshData.mesh, clippingPlane));\n      this.addClippingWhenVisible(clippingPlane);\n      planeData.stencilPlanes.forEach(plane => plane.stencilPlane.material.opacity = plane.stencilOpacity);\n    }\n\n    hideClippingPlane(planeData, clippingPlane) {\n      planeData.targets.drawingObjects.forEach(meshData => this.removeClippingPlaneFromMesh(meshData.mesh, clippingPlane));\n      this.removeClippingWhenNotVisible(clippingPlane);\n      planeData.stencilPlanes.forEach(plane => plane.stencilPlane.material.opacity = 0);\n    }\n\n    addClippingWhenVisible(clippingPlane) {\n      this.sectionalPlanes.forEach(planeData => {\n        if (!planeData.clippingPlane.equals(clippingPlane)) {\n          planeData.stencilPlanes.forEach(stencilData => {\n            stencilData.stencilPlane.material.clippingPlanes.push(clippingPlane);\n          });\n        }\n      });\n    }\n\n    removeClippingWhenNotVisible(clippingPlane) {\n      this.sectionalPlanes.forEach(planeData => {\n        planeData.stencilPlanes.forEach(stencilData => {\n          stencilData.stencilPlane.material.clippingPlanes = stencilData.stencilPlane.material.clippingPlanes.filter(cp => !cp.equals(clippingPlane));\n        });\n      });\n    }\n\n    addClippingPlaneToMesh(mesh, plane) {\n      if (mesh.children) mesh.children.forEach(child => {\n        if (child.material.length > 0) child.material.forEach(mat => mat.clippingPlanes.push(plane));else child.material.clippingPlanes.push(plane);\n      });\n      if (mesh.material.length > 0) mesh.material.forEach(mat => mat.clippingPlanes.push(plane));else mesh.material.clippingPlanes.push(plane);\n    }\n\n    removeClippingPlaneFromMesh(mesh, plane) {\n      if (mesh.children) mesh.children.forEach(child => this.removeClippingPlane(child.material, plane));\n      this.removeClippingPlane(mesh.material, plane);\n    }\n\n    removeClippingPlane(material, plane) {\n      if (material.length > 0) material.forEach(mat => mat.clippingPlanes = mat.clippingPlanes.filter(cp => !cp.equals(plane)));else material.clippingPlanes = material.clippingPlanes.filter(cp => !cp.equals(plane));\n    }\n\n    makePlane(planeData) {\n      let positionVector = utils.initVector3(planeData.positionVector);\n      let normalVector = utils.initVector3(planeData.normalVector).negate().normalize();\n      let planeDistance = positionVector.clone().projectOnVector(normalVector).length();\n      let distanceSign = positionVector.normalize().dot(normalVector) > 0 ? -1 : 1;\n      planeDistance *= distanceSign;\n      let plane = new THREE.Plane(normalVector, planeDistance);\n      return plane;\n    }\n\n    clearSectionalPlanes() {\n      this.sectionalPlanes.forEach(planeData => {\n        planeData.stencilPlanes.forEach(stencilPlaneData => stencilPlaneData.targetScene.remove(stencilPlaneData.stencilPLane));\n      });\n      this.sectionalPlanes = [];\n    }\n\n  }\n\n  _exports.DynamicSectionalPlane = DynamicSectionalPlane;\n});","define(\"m08-2020/lib old/ClippingUtils/OutlineGenerator\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.OutlineGenerator = void 0;\n  const TOLERANCE = 0.01;\n\n  class OutlineGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.a = new THREE.Vector3();\n      this.b = new THREE.Vector3();\n      this.c = new THREE.Vector3();\n      this.planePointA = new THREE.Vector3();\n      this.planePointB = new THREE.Vector3();\n      this.planePointC = new THREE.Vector3();\n      this.lineAB = new THREE.Line3(), this.lineBC = new THREE.Line3(), this.lineCA = new THREE.Line3();\n      this.pointOfIntersection = new THREE.Vector3();\n      this.pointsOfIntersection = new THREE.Geometry();\n    }\n\n    drawStencilOutline(obj, mathPlane, lineProps) {\n      let self = this;\n      obj.geometry.faces.forEach(function (face, idx) {\n        obj.localToWorld(self.a.copy(obj.geometry.vertices[face.a]));\n        obj.localToWorld(self.b.copy(obj.geometry.vertices[face.b]));\n        obj.localToWorld(self.c.copy(obj.geometry.vertices[face.c]));\n        self.lineAB = new THREE.Line3(self.a, self.b);\n        self.lineBC = new THREE.Line3(self.b, self.c);\n        self.lineCA = new THREE.Line3(self.c, self.a);\n        self.setPointOfIntersection(self.lineAB, mathPlane, idx);\n        self.setPointOfIntersection(self.lineBC, mathPlane, idx);\n        self.setPointOfIntersection(self.lineCA, mathPlane, idx);\n      });\n      let lines = [];\n      let vertices = this.pointsOfIntersection.vertices;\n      let facesDict = this.getFaceIndexMap(vertices);\n      Object.values(facesDict).forEach(val => {\n        if (val.length > 1) {\n          let line = this.GraphicsThree3D.drawLine(val[0], val[1], lineProps);\n          lines.push(line);\n        }\n      });\n      return lines;\n    }\n\n    getFaceIndexMap(vertices) {\n      let dict = {};\n      vertices.forEach(vertex => {\n        if (dict.hasOwnProperty(vertex.faceIndex)) dict[vertex.faceIndex].push(vertex);else dict[vertex.faceIndex] = [vertex];\n      });\n      return dict;\n    }\n\n    setPointOfIntersection(line, plane, faceIdx) {\n      this.pointOfIntersection = plane.intersectLine(line, new THREE.Vector3());\n\n      if (this.pointOfIntersection && !line.start.equals(this.pointOfIntersection)) {\n        let p = this.pointOfIntersection.clone();\n        p.faceIndex = faceIdx;\n        p.checked = false;\n        this.pointsOfIntersection.vertices.push(p);\n      }\n    }\n\n    getContours(points, contours) {\n      let contour = []; // find first line for the contour\n\n      let firstPointIndex = 0;\n      let secondPointIndex = 0;\n      let firstPoint, secondPoint;\n\n      for (let i = 0; i < points.length; i++) {\n        if (points[i].checked == true) continue;\n        firstPointIndex = i;\n        firstPoint = points[firstPointIndex];\n        firstPoint.checked = true;\n        secondPointIndex = this.getPairIndex(firstPoint, firstPointIndex, points);\n        secondPoint = points[secondPointIndex];\n        secondPoint.checked = true;\n        contour.push(firstPoint.clone());\n        contour.push(secondPoint.clone());\n        break;\n      }\n\n      contour = this.getContour(secondPoint, points, contour);\n      contours.push(contour);\n      let allChecked = 0;\n      points.forEach(p => {\n        allChecked += p.checked == true ? 1 : 0;\n      });\n\n      if (allChecked != points.length) {\n        return this.getContours(points, contours);\n      }\n\n      return contours;\n    }\n\n    getContour(currentPoint, points, contour) {\n      let p1Index = this.getNearestPointIndex(currentPoint, points);\n      let p1 = points[p1Index];\n      p1.checked = true;\n      let p2Index = this.getPairIndex(p1, p1Index, points);\n      let p2 = points[p2Index];\n      p2.checked = true;\n      let isClosed = p2.equals(contour[0], TOLERANCE);\n\n      if (!isClosed) {\n        contour.push(p2.clone());\n        return this.getContour(p2, points, contour);\n      } else {\n        contour.push(contour[0].clone());\n        return contour;\n      }\n    }\n\n    getNearestPointIndex(point, points) {\n      let index = 0;\n\n      for (let i = 0; i < points.length; i++) {\n        let p = points[i];\n\n        if (p.checked == false && p.equals(point, TOLERANCE)) {\n          index = i;\n          break;\n        }\n      }\n\n      return index;\n    }\n\n    getPairIndex(point, pointIndex, points) {\n      let index = 0;\n\n      for (let i = 0; i < points.length; i++) {\n        let p = points[i];\n\n        if (i != pointIndex && p.checked == false && p.faceIndex == point.faceIndex) {\n          index = i;\n          break;\n        }\n      }\n\n      return index;\n    }\n\n    clearPointsOfIntersection() {\n      this.pointOfIntersection = new THREE.Vector3();\n      this.pointsOfIntersection = new THREE.Geometry();\n    }\n\n  }\n\n  _exports.OutlineGenerator = OutlineGenerator;\n});","define(\"m08-2020/lib old/ClippingUtils/SectionalPlane\", [\"exports\", \"three\", \"m08-2020/lib old/Utils\", \"m08-2020/lib old/ClippingUtils/OutlineGenerator\"], function (_exports, THREE, _Utils, _OutlineGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.SectionalPlane = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class SectionalPlane {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.OutlineGenerator = new _OutlineGenerator.OutlineGenerator(GraphicsThree3D);\n      this.clippingPlanes = [];\n      this.clipObjects = [];\n      this.renderOrder = 1;\n      this.sectionalPlanesData = {};\n    }\n\n    addToSectionalPlanes(insertionPoint, object, objectData) {\n      if (this.sectionalPlanesData.hasOwnProperty(objectData.id)) {\n        if (this.sectionalPlanesData[objectData.id].hasOwnProperty(insertionPoint.id)) {\n          this.sectionalPlanesData[objectData.id][insertionPoint.id].push({\n            object,\n            insertionPoint,\n            sectionalPlanes: objectData.components.sectionalPlanes,\n            properties: objectData.properties\n          });\n        } else {\n          this.sectionalPlanesData[objectData.id][insertionPoint.id] = [{\n            object,\n            insertionPoint,\n            sectionalPlanes: objectData.components.sectionalPlanes,\n            properties: objectData.properties\n          }];\n        }\n      } else {\n        this.sectionalPlanesData[objectData.id] = {};\n        this.sectionalPlanesData[objectData.id][insertionPoint.id] = [{\n          object,\n          insertionPoint,\n          sectionalPlanes: objectData.components.sectionalPlanes,\n          properties: objectData.properties\n        }];\n      }\n    }\n\n    applySectionalPlanes() {\n      Object.values(this.sectionalPlanesData).forEach(drawingObj => Object.values(drawingObj).forEach(insertionPt => insertionPt.forEach(data => {\n        let clippingPlanes = this.initClippingPlanes(data.sectionalPlanes, data.insertionPoint);\n        let intersectionLineProps = data.properties.intersectionLine;\n        let intersectionAreaProps = data.properties.intersectionArea;\n        data.object.stencilNeeded = true;\n        clippingPlanes.forEach(clippingPlane => {\n          this.clipMesh(clippingPlane, data.object, intersectionAreaProps, intersectionLineProps, clippingPlanes);\n        });\n      })));\n    }\n\n    initClippingPlanes(sectionalPlanes, insertionPoint) {\n      let clippingPlanes = [];\n      let insertionVector = utils.initVector3(insertionPoint.insertionVector);\n      let rotationAngle = insertionPoint.rotationAngle ? insertionPoint.rotationAngle * Math.PI / 180 : 0;\n      let rotationAxis = rotationAngle ? utils.initVector3(insertionPoint.rotationAxis) : 0;\n      let rotationPoint = rotationAngle ? utils.initVector3(insertionPoint.rotationPoint) : 0;\n      sectionalPlanes.forEach(planeData => {\n        let modifiedPlaneData = {};\n\n        if (rotationAngle && rotationAxis && rotationPoint) {\n          modifiedPlaneData.normalVector = utils.initVector3(planeData.normalVector).applyAxisAngle(rotationAxis, rotationAngle);\n          modifiedPlaneData.positionVector = utils.initVector3(planeData.positionVector).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint).add(insertionVector);\n        } else {\n          modifiedPlaneData.normalVector = planeData.normalVector;\n          modifiedPlaneData.positionVector = {\n            x: planeData.positionVector.x + insertionPoint.insertionVector.x,\n            y: planeData.positionVector.y + insertionPoint.insertionVector.y,\n            z: planeData.positionVector.z + insertionPoint.insertionVector.z\n          };\n        }\n\n        clippingPlanes.push({\n          clippingPlane: this.makePlane(modifiedPlaneData),\n          positionVector: utils.initVector3(modifiedPlaneData.positionVector)\n        });\n      });\n      return clippingPlanes;\n    }\n\n    applySectionalPlane(planeData, intersectionAreaProps, intersectionLineProps) {\n      this.clipObjects.forEach(({\n        object,\n        insertionPoint\n      }) => {\n        if (object.type == \"Mesh\" || object.type == \"Line\" || object.type == \"LineSegments\") {\n          this.clipMesh(planeData, object, insertionPoint, intersectionAreaProps, intersectionLineProps);\n        }\n      });\n    }\n\n    makePlane(planeData) {\n      let positionVector = utils.initVector3(planeData.positionVector);\n      let normalVector = utils.initVector3(planeData.normalVector).negate().normalize();\n      let planeDistance = positionVector.clone().projectOnVector(normalVector).length();\n      let distanceSign = positionVector.normalize().dot(normalVector) > 0 ? -1 : 1;\n      planeDistance *= distanceSign;\n      let plane = new THREE.Plane(normalVector, planeDistance);\n      return plane;\n    }\n\n    clipMesh(planeData, mesh, intersectionAreaProps, lineProps, clippingPlanes) {\n      mesh.updateMatrix();\n      mesh.updateMatrixWorld();\n      let clippingPlane = planeData.clippingPlane;\n      let positionVector = planeData.positionVector;\n      let targetScene = this.GraphicsThree3D.stencilScenes.length ? new THREE.Scene() : this.GraphicsThree3D.scene;\n\n      if (mesh.stencilNeeded) {\n        if (this.areaPropsDefined(intersectionAreaProps)) {\n          let [frontFaceStencilMat, backFaceStencilMat, planeStencilMat] = this.getClippingMaterials(intersectionAreaProps);\n          frontFaceStencilMat.clippingPlanes = [clippingPlane];\n          backFaceStencilMat.clippingPlanes = [clippingPlane];\n          planeStencilMat.clippingPlanes = clippingPlanes.filter(plane => !plane.clippingPlane.equals(clippingPlane)).map(plane => plane.clippingPlane);\n          let meshGeo = mesh.geometry.clone();\n          let frontMesh = new THREE.Mesh(meshGeo, frontFaceStencilMat);\n          frontMesh.applyMatrix4(mesh.matrix);\n          let backMesh = new THREE.Mesh(meshGeo, backFaceStencilMat);\n          backMesh.applyMatrix4(mesh.matrix);\n          frontMesh.renderOrder = this.renderOrder;\n          backMesh.renderOrder = this.renderOrder;\n          targetScene.add(frontMesh);\n          targetScene.add(backMesh);\n          let planeNormal = clippingPlane.normal.clone();\n          let forwardVector = new THREE.Vector3(0, 0, -1);\n          let planeGeom = new THREE.PlaneBufferGeometry();\n          let planeMesh = new THREE.Mesh(planeGeom, planeStencilMat);\n          planeMesh.scale.setScalar(1000);\n          planeMesh.position.copy(positionVector);\n          planeMesh.quaternion.setFromUnitVectors(forwardVector, planeNormal);\n          frontMesh.position.copy(mesh.position);\n          backMesh.position.copy(mesh.position);\n\n          planeMesh.onAfterRender = function (renderer) {\n            renderer.clearStencil();\n          };\n\n          planeMesh.renderOrder = this.renderOrder + 0.1;\n          this.GraphicsThree3D.initWireframeObject(planeMesh, new THREE.Mesh(), this.GraphicsThree3D.jsonData, targetScene);\n          targetScene.add(planeMesh);\n        }\n      }\n\n      if (lineProps) {\n        let lines = this.OutlineGenerator.drawStencilOutline(mesh, clippingPlane, lineProps);\n        this.OutlineGenerator.clearPointsOfIntersection();\n        lines.forEach(line => {\n          line.material.clippingPlanes = clippingPlanes.map(p => p.clippingPlane);\n          targetScene.add(line);\n        });\n      }\n\n      this.GraphicsThree3D.stencilScenes.push(targetScene);\n      if (mesh.children) mesh.children.forEach(child => this.clipMaterial(child.material, clippingPlane));\n      this.clipMaterial(mesh.material, clippingPlane);\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    getClippingMaterials(materialProps) {\n      let frontFaceStencilMat, backFaceStencilMat, planeStencilMat; //https://discourse.threejs.org/t/capping-clipped-planes-using-stencil-on-a-buffergeometry/18407/18\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n\n      backFaceStencilMat = new THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = THREE.BackSide;\n      backFaceStencilMat.stencilFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = THREE.IncrementWrapStencilOp; // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      frontFaceStencilMat = new THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = THREE.DecrementWrapStencilOp; // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      planeStencilMat = this.getPlaneStencilMaterial(materialProps);\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = THREE.ReplaceStencilOp;\n      return [frontFaceStencilMat, backFaceStencilMat, planeStencilMat];\n    }\n\n    getPlaneStencilMaterial(areaProps) {\n      const SIZE = 20;\n      let planeStencilMat;\n      let colorProps = areaProps.color;\n\n      if (areaProps.hatching.type === 2) {\n        let orientationVector = utils.normalizeVector(areaProps.hatching.orientationVector);\n        let hatchSpacing = areaProps.hatching.factor;\n        let hatchColor = areaProps.hatching.color;\n        let hatchThickness = areaProps.hatching.thickness;\n        planeStencilMat = this.GraphicsThree3D.makeCanvasMaterial(colorProps, hatchColor, orientationVector, hatchSpacing, hatchThickness);\n        planeStencilMat.map.wrapS = planeStencilMat.map.wrapT = THREE.RepeatWrapping;\n        planeStencilMat.map.repeat.set(SIZE, SIZE);\n      } else if (areaProps.hatching.type === 1) {\n        planeStencilMat = this.GraphicsThree3D.getMaterialFromColor(colorProps);\n      }\n\n      return planeStencilMat;\n    }\n\n    areaPropsDefined(areaProps) {\n      return areaProps && areaProps.hatching.type > 0 && areaProps.hatching.type < 3;\n    }\n\n    resetClipObjects() {\n      this.clipObjects = [];\n    }\n\n  }\n\n  _exports.SectionalPlane = SectionalPlane;\n});","define(\"m08-2020/lib old/DXFGenerator\", [\"exports\", \"three\", \"m08-2020/lib old/Utils\", \"m08-2020/lib old/dxf_colors\"], function (_exports, THREE, _Utils, _dxf_colors) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DXFGenerator = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class DXFGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D; // global dxf data -->\n\n      this.blockSection = \"\";\n      this.entitiesSection = \"\";\n      this.textStyles = \"\";\n      this.offsetFromTop = 0; // object data -->\n\n      this.referenceZeroPoint = new THREE.Vector3();\n      this.scaleFactor = 1;\n      this.blocks = {\n        fasteners: [],\n        drawingObjects: []\n      };\n      this.projectedPoints = [];\n      this.wireframePoints = [];\n      this.fastenerPoints = {};\n      this.pointObjectsData = [];\n      this.lineObjectsData = []; // TODO: Might need better solution.\n\n      this.outerFrameOffset = 0;\n      this.drawingScaleBarBBox = null;\n      this.drawingSizeHalf = {\n        width: 0,\n        height: 0\n      };\n    }\n\n    downloadDXF3D() {\n      this.generateBlockAndEntitySections3D();\n      let contentDXF = this.generateContentDXF();\n      this.downloadTextFile(\"scene.dxf\", contentDXF.trim());\n    }\n\n    generateBlockAndEntitySections3D() {\n      this.positionCamera();\n      this.drawingSizeHalf = {\n        width: this.GraphicsThree3D.camera.right,\n        height: this.GraphicsThree3D.camera.top\n      }; // TODO: Might need to refactor?\n\n      try {\n        this.getScale(this.wireframePoints, {\n          x: 1,\n          y: -1,\n          z: -1\n        });\n      } catch (error) {\n        console.error(error);\n        console.error(\"DXFGenerator: Cannot generate scale. Setting scale to 1.\");\n        this.scaleFactor = 1;\n      }\n\n      try {\n        this.referenceZeroPoint = this.getProjectedReferenceZeroPoint();\n      } catch (error) {\n        console.error(error);\n        console.error(\"DXFGenerator: Cannot set projected reference zero point. Setting projected reference zero point to (0, 0, 0).\");\n        this.referenceZeroPoint = new THREE.Vector3();\n      } //\n\n\n      let visProps = {\n        dxfFastenerVisualisation: {\n          addOn: true,\n          axis: true,\n          fastener: false\n        },\n        visualisation: {\n          arrow: true,\n          dimensionalChain: true,\n          drawingObject: true,\n          fastener: true\n        }\n      };\n      this.addToBlockSection(visProps);\n      this.addToEntitiesSection(visProps);\n    }\n\n    positionCamera() {\n      let controls = this.GraphicsThree3D.controls;\n      let camera = this.GraphicsThree3D.camera;\n      let midVector = this.GraphicsThree3D.objectMidVector;\n      let initialPos = this.GraphicsThree3D.initialPosition;\n      let cameraTargetPos = {\n        x: midVector.x + initialPos.x,\n        y: midVector.y + initialPos.y,\n        z: midVector.z + initialPos.z\n      };\n      camera.position.set(cameraTargetPos.x, cameraTargetPos.y, cameraTargetPos.z);\n      camera.zoom = 1;\n      camera.lookAt(midVector);\n      camera.updateProjectionMatrix();\n      controls.update();\n    }\n\n    downloadDXF2D(dxfName, drawingPlaneData) {\n      this.generateBlockAndEntitySections(drawingPlaneData);\n      let contentDXF = this.generateContentDXF();\n      this.downloadTextFile(dxfName, contentDXF.trim());\n      this.clearDXF();\n    }\n\n    generateContentDXF() {\n      let contentDXF = \"\"; ///\n      // Set units to centimeters\n\n      contentDXF += \"0\\nSECTION\\n2\\nHEADER\\n9\\n$INSUNITS\\n70\\n5\\n0\\nENDSEC\\n\"; ///\n\n      contentDXF += `0\\nSECTION\\n2\\nTABLES\\n0\\nTABLE\\n2\\nSTYLE\\n${this.textStyles}0\\nENDTAB\\n0\\nENDSEC\\n`;\n      contentDXF += `0\\nSECTION\\n2\\nBLOCKS\\n${this.blockSection}0\\nENDSEC\\n`;\n      contentDXF += `0\\nSECTION\\n2\\nENTITIES\\n${this.entitiesSection}0\\nENDSEC\\n0\\nEOF`;\n      return contentDXF;\n    }\n\n    generateBlockAndEntitySections(drawingPlaneData) {\n      this.GraphicsThree3D.camera.updateProjectionMatrix();\n      this.outerFrameOffset = drawingPlaneData.drawingComponents.frameDataAndProps.frameProps.outerLine.thickness / 2;\n      this.setDrawingSizeHalf(drawingPlaneData.drawingComponents.frameDataAndProps.frameData, drawingPlaneData.drawingComponents.frameDataAndProps.frameProps); // TODO: Might need to refactor?\n\n      try {\n        this.getScale(this.wireframePoints, drawingPlaneData.normal);\n      } catch (error) {\n        console.error(error);\n        console.error(\"DXFGenerator: Cannot generate scale. Setting scale to 1.\");\n        this.scaleFactor = 1;\n      }\n\n      try {\n        this.referenceZeroPoint = this.getProjectedReferenceZeroPoint();\n      } catch (error) {\n        console.error(error);\n        console.error(\"DXFGenerator: Cannot set projected reference zero point. Setting projected reference zero point to (0, 0, 0).\");\n        this.referenceZeroPoint = new THREE.Vector3();\n      } //\n\n\n      this.addToBlockSection(drawingPlaneData);\n      this.addToEntitiesSection(drawingPlaneData);\n      this.createDrawingTextStyles(drawingPlaneData);\n      this.addDrawingFrame(drawingPlaneData.drawingComponents.frameDataAndProps);\n      this.addDrawingFreeTexts(drawingPlaneData.drawingComponents.freeTextDataAndProps);\n      this.addDrawingScaleTextBlock(drawingPlaneData.drawingComponents.scaleTextBlockDataAndProps, drawingPlaneData.scale);\n      this.addDrawingUnitTextBlock(drawingPlaneData.drawingComponents.unitTextBlockDataAndProps, drawingPlaneData.units);\n      this.addDrawingScaleBar(drawingPlaneData.drawingComponents.scaleBarDataAndProps, drawingPlaneData.scale, drawingPlaneData.units);\n      this.offsetFromTop += drawingPlaneData.drawingComponents.frameDataAndProps.frameData.height;\n      this.clearObjectData();\n    }\n\n    addToBlockSection(drawingPlaneData) {\n      let visualisation = drawingPlaneData.visualisation;\n      if (visualisation.fastener) this.blockSection += this.generateFastenerBlocks(drawingPlaneData, this.fastenerPoints);\n    }\n\n    addToEntitiesSection(drawingPlaneData) {\n      let visualisation = drawingPlaneData.visualisation;\n\n      if (visualisation.drawingObject) {\n        this.drawWireframeLines(this.wireframePoints).forEach(line => this.entitiesSection += `${line.trim()}\\n`);\n        this.drawPoints(this.pointObjectsData).forEach(point => this.entitiesSection += `${point.trim()}\\n`);\n        this.drawLines(this.lineObjectsData).forEach(line => this.entitiesSection += `${line.trim()}\\n`);\n      }\n\n      if (visualisation.fastener) {\n        this.blocks.fasteners.forEach(({\n          blockName,\n          layerName\n        }) => this.entitiesSection += `0\\nINSERT\\n2\\n${blockName}\\n8\\n${layerName}\\n10\\n${this.referenceZeroPoint.x}\\n20\\n${this.referenceZeroPoint.y}\\n30\\n${this.referenceZeroPoint.z}\\n`);\n      }\n    }\n\n    createDrawingTextStyles(drawingPlaneData) {\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.freeTextDataAndProps.freeTextProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.scaleTextBlockDataAndProps.scaleTextBlockProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.unitTextBlockDataAndProps.unitTextBlockProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.scaleBarDataAndProps.scaleBarProps.text);\n    }\n\n    createDrawingTextStyle(textProps) {\n      let fontName = this.generateFontName(textProps); //\"ARIAL.TTF\";\n\n      this.textStyles += `0\\nSTYLE\\n2\\n${fontName}\\n70\\n0\\n40\\n0\\n41\\n0\\n50\\n0\\n71\\n0\\n42\\n0\\n3\\n${fontName + \".TTF\"}\\n`;\n    }\n\n    generateFontName(textProps) {\n      let name = \"\";\n\n      if (textProps) {\n        let {\n          style,\n          type\n        } = textProps;\n\n        switch (type) {\n          case 1:\n            name += \"ARIAL_\";\n            break;\n\n          case 2:\n            name += \"CALIBRI_\";\n            break;\n\n          case 3:\n            name += \"COURIER_\";\n            break;\n\n          case 4:\n            name += \"TIMESNEWROMAN_\";\n            break;\n\n          default:\n            break;\n        }\n\n        switch (style) {\n          case 1:\n            name += \"REGULAR\";\n            break;\n\n          case 2:\n            name += \"ITALIC\";\n            break;\n\n          case 3:\n            name += \"BOLD\";\n            break;\n\n          case 4:\n            name += \"BOLD_ITALIC\";\n            break;\n\n          default:\n            break;\n        }\n      } else name += \"ARIAL\";\n\n      return name;\n    }\n\n    addDrawingScaleBar(scaleBarDataAndProps, scale, units) {\n      let {\n        scaleBarData,\n        scaleBarProps\n      } = scaleBarDataAndProps;\n\n      if (scaleBarData && scaleBarProps) {\n        let areaProps = scaleBarProps.area;\n        let lineProps = scaleBarProps.line;\n        let textProps = scaleBarProps.text;\n        let areaColor = this.getColorIndexFromColor(this.getColorFromProps(areaProps));\n        let lineColor = this.getColorIndexFromColor(this.getColorFromProps(lineProps));\n        const NUM_OF_SEGMENTS = 5;\n        const SEGMENT_SIZE = 1;\n        const TEXT_ALIGNMENT = 1;\n        const Z_AXIS = new THREE.Vector3(0, 0, 1);\n        let insertionPoint = utils.initVector3(scaleBarData.insertionPoint);\n        let bboxSize = this.drawingScaleBarBBox.getSize(new THREE.Vector3());\n        let centerOffset = this.drawingScaleBarBBox.getCenter(new THREE.Vector3());\n        let alignmentOffset = this.GraphicsThree3D.DrawingPlaneLoader.getScaleBarAlignmentOffset(bboxSize, scaleBarData.relativePosition);\n        let rotation = this.getRotationFromDirection(scaleBarData.directionVector) * Math.PI / 180;\n        let barHeight = scaleBarData.height;\n        let barWidth = SEGMENT_SIZE * NUM_OF_SEGMENTS; /// Line -->\n\n        let linePoints = [new THREE.Vector3(0, 0, 0), new THREE.Vector3(barWidth, 0, 0), new THREE.Vector3(barWidth, barHeight, 0), new THREE.Vector3(0, barHeight, 0), new THREE.Vector3(0, 0, 0)].map(pt => pt.sub(centerOffset).add(alignmentOffset).applyAxisAngle(Z_AXIS, rotation).add(insertionPoint).sub(new THREE.Vector3(0, this.offsetFromTop, 0)));\n\n        for (let i = 0; i < linePoints.length - 1; i++) this.entitiesSection += this.drawLine(linePoints[i], linePoints[i + 1], lineColor, 0); /// <--\n        /// Fills -->\n\n\n        let solidPoints = [];\n\n        for (let i = 0; i < NUM_OF_SEGMENTS; i++) {\n          if (i % 2) solidPoints.push([new THREE.Vector3(SEGMENT_SIZE * i, 0, 0), new THREE.Vector3(SEGMENT_SIZE * (i + 1), 0, 0), new THREE.Vector3(SEGMENT_SIZE * i, barHeight, 0), new THREE.Vector3(SEGMENT_SIZE * (i + 1), barHeight, 0)].map(pt => pt.sub(centerOffset).add(alignmentOffset).applyAxisAngle(Z_AXIS, rotation).add(insertionPoint).sub(new THREE.Vector3(0, this.offsetFromTop, 0))));\n        }\n\n        solidPoints.forEach(ptArr => this.entitiesSection += this.drawSolid(ptArr[0], ptArr[1], ptArr[2], ptArr[3], areaColor, 0)); /// <--\n        /// Texts -->\n\n        let decimalPlaces = scaleBarData.decimalPlaces;\n        let distance = scaleBarData.distance;\n        let height = scaleBarData.height;\n        let textOffset = scaleBarData.textOffset;\n        let scaleFactor = this.GraphicsThree3D.DrawingPlaneLoader.getScaleFactor(units.length);\n        let heightOffset = -distance - textProps.size / 2;\n        let content = \"\";\n        let position = new THREE.Vector3(0, heightOffset, 0);\n\n        for (let i = 0; i <= NUM_OF_SEGMENTS; i++) {\n          content = (i * scale * scaleFactor).toFixed(decimalPlaces);\n          if (i === 5 && units.length) content += ` ${units.length}`;\n\n          if (textOffset) {\n            if (i % 2 === 1) position.y = height - heightOffset;else position.y = heightOffset;\n          }\n\n          position.x = i * SEGMENT_SIZE;\n          let textPosition = position.clone().sub(centerOffset).add(alignmentOffset).add(insertionPoint);\n          let textData = {\n            content,\n            directionVector: scaleBarData.directionVector,\n            insertionPoint: textPosition,\n            relativePosition: TEXT_ALIGNMENT\n          };\n          this.entitiesSection += this.makeTextFromDataAndProps(textData, textProps);\n        } /// <--\n\n      }\n    }\n\n    addDrawingUnitTextBlock(unitTextBlockDataAndProps, units) {\n      let {\n        unitTextBlockData,\n        unitTextBlockProps\n      } = unitTextBlockDataAndProps;\n\n      if (unitTextBlockData && unitTextBlockProps) {\n        let textData = { ...unitTextBlockData\n        };\n        textData.content = this.GraphicsThree3D.DrawingPlaneLoader.generateUnitTextContent(unitTextBlockData, units);\n        this.entitiesSection += this.makeTextFromDataAndProps(textData, unitTextBlockProps);\n      }\n    }\n\n    addDrawingScaleTextBlock(scaleTextBlockDataAndProps, scale) {\n      let {\n        scaleTextBlockData,\n        scaleTextBlockProps\n      } = scaleTextBlockDataAndProps;\n\n      if (scaleTextBlockData && scaleTextBlockProps) {\n        let textData = { ...scaleTextBlockData\n        };\n        textData.content = `${scaleTextBlockData.text} ${scale.toFixed(scaleTextBlockData.decimals)}`;\n        this.entitiesSection += this.makeTextFromDataAndProps(textData, scaleTextBlockProps);\n      }\n    }\n\n    addDrawingFreeTexts(freeTextDataAndProps) {\n      let {\n        freeTexts,\n        freeTextProps\n      } = freeTextDataAndProps;\n\n      if (freeTexts && freeTextProps) {\n        freeTexts.forEach(text => this.entitiesSection += this.makeTextFromDataAndProps(text, freeTextProps));\n      }\n    }\n\n    makeTextFromDataAndProps(textData, textProps) {\n      let {\n        size,\n        heightWidthRatio\n      } = textProps;\n      let color = this.getColorIndexFromColor(this.getColorFromProps(textProps));\n      let fontName = this.generateFontName(textProps);\n      let {\n        content,\n        directionVector,\n        relativePosition\n      } = textData;\n      let justifications = this.getTextJustifications(relativePosition);\n      let rotation = this.getRotationFromDirection(directionVector);\n      let insertionPoint = { ...textData.insertionPoint\n      };\n      insertionPoint.y -= this.offsetFromTop;\n      insertionPoint.z = 0;\n      let textParams = {\n        content,\n        position: insertionPoint,\n        height: size,\n        widthRatio: heightWidthRatio,\n        justifications,\n        rotation,\n        color,\n        fontName,\n        layerName: 0\n      };\n      return this.drawText(textParams);\n    }\n\n    getRotationFromDirection(directionVector) {\n      let xAxis = new THREE.Vector2(1, 0, 0);\n      let direction = utils.initVector2(directionVector).normalize();\n      let dot = direction.dot(xAxis);\n      let cross = direction.cross(xAxis);\n      let rotation = Math.acos(dot) * (cross < 0 ? 1 : -1) * 180 / Math.PI;\n      return rotation;\n    }\n\n    getTextJustifications(relativePosition) {\n      let justifications = {\n        justificHorizontal: 0,\n        justificVertical: 0\n      };\n\n      switch (relativePosition) {\n        case 1:\n          justifications.justificHorizontal = 1;\n          justifications.justificVertical = 2;\n          break;\n\n        case 2:\n          justifications.justificHorizontal = 0;\n          justifications.justificVertical = 1;\n          break;\n\n        case 3:\n          justifications.justificHorizontal = 0;\n          justifications.justificVertical = 3;\n          break;\n\n        case 4:\n          justifications.justificHorizontal = 2;\n          justifications.justificVertical = 3;\n          break;\n\n        case 5:\n          justifications.justificHorizontal = 2;\n          justifications.justificVertical = 1;\n          break;\n\n        default:\n          break;\n      }\n\n      return justifications;\n    }\n\n    drawSolid(p1, p2, p3, p4, color, layerName) {\n      let solid = `0\\nSOLID\\n8\\n${layerName}\\n62\\n${color}\\n` + `10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.y}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n` + `12\\n${p3.x}\\n22\\n${p3.y}\\n32\\n${p3.z}\\n13\\n${p4.x}\\n23\\n${p4.y}\\n33\\n${p4.z}\\n`;\n      return solid;\n    }\n\n    drawText({\n      content,\n      position,\n      height,\n      widthRatio,\n      justifications,\n      rotation,\n      color,\n      fontName,\n      layerName\n    }) {\n      let {\n        justificHorizontal,\n        justificVertical\n      } = justifications;\n      let text = `0\\nTEXT\\n8\\n${layerName}\\n1\\n${content}\\n40\\n${height}\\n41\\n${widthRatio}\\n72\\n${justificHorizontal}\\n73\\n${justificVertical}\\n` + `50\\n${rotation}\\n62\\n${color}\\n7\\n${fontName}\\n10\\n${0}\\n20\\n${0}\\n30\\n${0}\\n` + `11\\n${position.x}\\n21\\n${position.y}\\n31\\n${position.z}\\n`;\n      return text;\n    }\n\n    addDrawingFrame(frameDataAndProps) {\n      let {\n        frameData,\n        frameProps\n      } = frameDataAndProps;\n      this.generateFrameOuterLine(frameData, frameProps.outerLine);\n      this.generateFrameInnerLine(frameData, frameProps.innerLine);\n    }\n\n    generateFrameOuterLine(frameData, outerProps) {\n      let {\n        width,\n        height\n      } = frameData;\n      let thickness = 0; //outerProps.thickness;\n\n      let color = this.getColorIndexFromColor(this.getColorFromProps(outerProps));\n      let outerPoints = [{\n        x: 0,\n        y: -this.offsetFromTop,\n        z: 0\n      }, {\n        x: width,\n        y: -this.offsetFromTop,\n        z: 0\n      }, {\n        x: width,\n        y: height - this.offsetFromTop,\n        z: 0\n      }, {\n        x: 0,\n        y: height - this.offsetFromTop,\n        z: 0\n      }, {\n        x: 0,\n        y: -this.offsetFromTop,\n        z: 0\n      }];\n\n      for (let i = 0; i < outerPoints.length - 1; i++) {\n        let extrudeDirection = i % 2 ? {\n          x: 0,\n          y: 1,\n          z: 0\n        } : {\n          x: 1,\n          y: 0,\n          z: 0\n        };\n        this.entitiesSection += this.drawThickLine(outerPoints[i], outerPoints[i + 1], color, thickness, extrudeDirection, 0);\n      }\n    }\n\n    generateFrameInnerLine(frameData, innerProps) {\n      let {\n        width,\n        height\n      } = frameData;\n      let {\n        left,\n        right,\n        top,\n        bottom\n      } = frameData.border;\n      let thickness = 0; //innerProps.thickness;\n\n      let color = this.getColorIndexFromColor(this.getColorFromProps(innerProps));\n      let innerPoints = [{\n        x: left,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }, {\n        x: width - right,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }, {\n        x: width - right,\n        y: -this.offsetFromTop + height - top,\n        z: 0\n      }, {\n        x: left,\n        y: -this.offsetFromTop + height - top,\n        z: 0\n      }, {\n        x: left,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }];\n\n      for (let i = 0; i < innerPoints.length - 1; i++) {\n        let extrudeDirection = i % 2 ? {\n          x: 0,\n          y: 1,\n          z: 0\n        } : {\n          x: 1,\n          y: 0,\n          z: 0\n        };\n        this.entitiesSection += this.drawThickLine(innerPoints[i], innerPoints[i + 1], color, thickness, extrudeDirection, 0);\n      }\n    }\n\n    getProjectedReferenceZeroPoint() {\n      let camera = this.GraphicsThree3D.camera;\n      return this.projectPointOnScreen(new THREE.Vector3(), camera, this.drawingSizeHalf.width, this.drawingSizeHalf.height);\n    }\n\n    getScale(wireframePoints, planeNormal) {\n      let pointPairs = wireframePoints[0].pointPairs;\n      let planeDistance = 0,\n          index = 0;\n\n      while (planeDistance < 0.1) {\n        let pointPair = pointPairs[index];\n        let normalVec = utils.initVector3(planeNormal);\n        let projectedOnPlane = pointPair.map(point => point.clone().projectOnPlane(normalVec));\n        planeDistance = projectedOnPlane[0].distanceTo(projectedOnPlane[1]);\n        index++;\n      }\n\n      let {\n        x,\n        y\n      } = this.GraphicsThree3D.renderer.getSize(new THREE.Vector2());\n      this.scaleFactor = x / y / (this.drawingSizeHalf.width / this.drawingSizeHalf.height);\n    }\n\n    generateFastenerBlocks(drawingPlaneData, fastenerPoints) {\n      const CAMERA = this.GraphicsThree3D.camera;\n      let fastenerBlocks = \"\";\n      let dxfFastenerVisualisation = drawingPlaneData.dxfFastenerVisualisation;\n      const POINT_RADIUS = 0.1;\n      Object.entries(fastenerPoints).forEach(([id, pointData]) => {\n        let blockName = `${drawingPlaneData.id}_${id}`;\n        let layerName = `fastener_${id}`;\n        this.blocks.fasteners.push({\n          blockName,\n          layerName\n        });\n        fastenerBlocks += `0\\nBLOCK\\n8\\n${layerName}\\n2\\n${blockName}\\n70\\n0\\n10\\n${this.referenceZeroPoint.x}\\n20\\n${this.referenceZeroPoint.y}\\n30\\n${this.referenceZeroPoint.x}\\n3\\n${blockName}\\n`;\n\n        if (dxfFastenerVisualisation.axis) {\n          pointData.axis.forEach(({\n            pointPair,\n            properties\n          }) => {\n            let projectedPoints = this.getProjectedPointsArr([pointPair], CAMERA);\n            let lineColor = this.getColorIndexFromColor(this.getColorFromProps(properties.line));\n            let pointColor = this.getColorIndexFromColor(this.getColorFromProps(properties.point));\n            projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawLine(ptsPair[0], ptsPair[1], lineColor, layerName));\n            projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawCircle(ptsPair[0], POINT_RADIUS, pointColor, layerName));\n          });\n        }\n\n        if (dxfFastenerVisualisation.addOn) {\n          pointData.addon.forEach(({\n            points,\n            properties\n          }) => {\n            let color = this.getColorIndexFromColor(this.getColorFromProps(properties));\n            let projectedPoints = this.getProjectedPointsArr(points, CAMERA);\n            projectedPoints.forEach(pointsArr => {\n              for (let i = 0; i < pointsArr.length - 1; i++) fastenerBlocks += this.drawLine(pointsArr[i], pointsArr[i + 1], color, layerName);\n            });\n          });\n        }\n\n        fastenerBlocks += \"0\\nENDBLK\\n\";\n      });\n      return fastenerBlocks;\n    }\n\n    drawLines(linesData) {\n      let lines = [];\n      linesData.forEach(line => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(line.properties));\n        let projectedPointPairs = this.getProjectedPointsArr(line.pointPairs, this.GraphicsThree3D.camera);\n        projectedPointPairs.forEach(pair => lines.push(this.drawLine(pair[0], pair[1], color, `drawingObject_${line.id}`)));\n      });\n      return lines;\n    }\n\n    drawPoints(pointsData) {\n      let points = [];\n      pointsData.forEach(point => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(point.properties));\n        let radius = point.properties.size / 2;\n        let centerPos = utils.initVector3(point.position);\n        let projectedCenterPos = this.projectPointOnScreen(centerPos, this.GraphicsThree3D.camera, this.drawingSizeHalf.width, this.drawingSizeHalf.height);\n        points.push(this.drawCircle(projectedCenterPos, radius, color, `drawingObject_${point.id}`));\n      });\n      return points;\n    }\n\n    drawCircle(center, radius, color, layerName) {\n      return `0\\nCIRCLE\\n8\\n${layerName}\\n62\\n${color}\\n40\\n${radius}\\n10\\n${center.x}\\n20\\n${center.y}\\n30\\n${center.z}\\n`;\n    }\n\n    drawArc(center, radius, color, layerName) {\n      const START_ANGLE = 0,\n            END_ANGLE = 359.9;\n      return `0\\nARC\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${center.x}\\n20\\n${center.y}\\n30\\n${center.z}\\n40\\n${radius}\\n50\\n${START_ANGLE}\\n51\\n${END_ANGLE}`;\n    }\n\n    getProjectedPointsArr(ptPairs, camera) {\n      return ptPairs.map(pair => this.getProjectedPoints(pair, camera));\n    }\n\n    getProjectedPoints(points, camera) {\n      return points.map(pt => this.projectPointOnScreen(pt, camera));\n    }\n\n    projectPointOnScreen(point, camera) {\n      let widthHalf = this.drawingSizeHalf.width;\n      let heightHalf = this.drawingSizeHalf.height;\n      let projectedPoint = point.clone();\n      projectedPoint.project(camera);\n      projectedPoint.x = projectedPoint.x * widthHalf + widthHalf - this.outerFrameOffset;\n      projectedPoint.y = projectedPoint.y * heightHalf + heightHalf - this.offsetFromTop;\n      projectedPoint.z = 0;\n      this.projectedPoints.push(projectedPoint);\n      return projectedPoint;\n    }\n\n    setDrawingSizeHalf(frameData, frameProps) {\n      this.drawingSizeHalf = {\n        width: (frameData.width + frameProps.outerLine.thickness) / 2,\n        height: (frameData.height + frameProps.outerLine.thickness) / 2\n      };\n    }\n\n    getFacesFromScene(scene) {\n      let faces = [];\n      scene.traverse(child => {\n        if (child.addToDXF) {\n          faces.push(...this.getMeshFaces(child));\n        }\n      });\n      return faces;\n    }\n\n    getMeshFaces(mesh) {\n      let faces = [];\n      let color = this.getColorIndexFromColor(mesh.material[0].color);\n      let geometry = mesh.geometry;\n      let meshFaces = geometry.faces;\n      let meshVertices = geometry.vertices;\n      meshFaces.forEach(face => {\n        let vectorA = meshVertices[face.a].clone();\n        let vectorB = meshVertices[face.b].clone();\n        let vectorC = meshVertices[face.c].clone();\n        vectorA.applyMatrix4(mesh.matrix);\n        vectorB.applyMatrix4(mesh.matrix);\n        vectorC.applyMatrix4(mesh.matrix);\n        faces.push(this.drawFace(vectorA, vectorB, vectorC, color, mesh.dxfLayer));\n      });\n      return faces;\n    }\n\n    drawWireframeLines(wireframePoints) {\n      let lines = [];\n      wireframePoints.forEach(({\n        lineProps,\n        pointPairs,\n        id\n      }) => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(lineProps));\n        let projectedPoints = this.getProjectedPointsArr(pointPairs, this.GraphicsThree3D.camera);\n        let layerName = `drawingObject_${id}`;\n        projectedPoints.forEach(ptsPair => lines.push(this.drawLine(ptsPair[0], ptsPair[1], color, layerName)));\n      });\n      return lines;\n    }\n\n    drawThickLine(p1, p2, color, thickness, extrudeDirection, layerName) {\n      let line = `0\\nLINE\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n` + `39\\n${thickness}\\n210\\n${extrudeDirection.x}\\n220\\n${extrudeDirection.y}\\n230\\n${extrudeDirection.z}\\n`;\n      return line;\n    }\n\n    drawLine(p1, p2, color, layerName) {\n      return `0\\nLINE\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n`;\n    }\n\n    drawFace(p1, p2, p3, color, layerName) {\n      let face = [`0\\n3DFACE\\n8\\n${layerName}\\n62\\n${color}`, `10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}`, `11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}`, `12\\n${p3.x}\\n22\\n${p3.y}\\n32\\n${p3.z}`, `13\\n${p3.x}\\n23\\n${p3.y}\\n33\\n${p3.z}`].join(\"\\n\");\n      return face;\n    }\n\n    getColorIndexFromColor(color) {\n      let colorRGB = this.getRGBFromColor(color);\n      let colorIndex = this.getColorIndexFromRGB(colorRGB);\n      return colorIndex;\n    }\n\n    getColorIndexFromRGB([r, g, b]) {\n      let minDistance = Infinity;\n      let matchedIndex = 0;\n\n      for (let i = 0; i < 255; i++) {\n        let lookupColor = _dxf_colors.COLOR_LOOKUP[i];\n        let distance = Math.pow(r - lookupColor[0], 2) + Math.pow(g - lookupColor[1], 2) + Math.pow(b - lookupColor[2], 2);\n\n        if (distance < minDistance) {\n          minDistance = distance;\n          matchedIndex = i;\n        }\n      }\n\n      return matchedIndex;\n    }\n\n    getRGBFromColor(color) {\n      return [255 * color.r, 255 * color.g, 255 * color.b];\n    }\n\n    getColorFromProps(props) {\n      return {\n        r: props.color.red / 255,\n        g: props.color.green / 255,\n        b: props.color.blue / 255\n      };\n    }\n\n    downloadTextFile(filename, text) {\n      let element = document.createElement('a');\n      element.setAttribute('href', `data:text/plain;charset=utf-8,${text}`);\n      element.setAttribute('download', filename);\n      element.style.display = 'none';\n      document.body.appendChild(element);\n      element.click();\n      document.body.removeChild(element);\n    }\n\n    clearObjectData() {\n      this.referenceZeroPoint = new THREE.Vector3();\n      this.scaleFactor = 1;\n      this.blocks = {\n        fasteners: [],\n        drawingObjects: []\n      };\n      this.projectedPoints = [];\n      this.wireframePoints = [];\n      this.fastenerPoints = {};\n      this.pointObjectsData = [];\n      this.lineObjectsData = [];\n      this.outerFrameOffset = 0;\n      this.drawingScaleBarBBox = null;\n      this.drawingSizeHalf = {\n        width: 0,\n        height: 0\n      };\n    }\n\n    clearDXF() {\n      this.blockSection = \"\";\n      this.entitiesSection = \"\";\n      this.textStyles = \"\";\n      this.offsetFromTop = 0;\n      this.clearObjectData();\n    }\n\n  }\n\n  _exports.DXFGenerator = DXFGenerator;\n});","define(\"m08-2020/lib old/FigureLoaders/ArrowLoader\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ArrowLoader = void 0;\n\n  class ArrowLoader {\n    constructor(THREE, fontUrlArray, GraphicsThree3D) {\n      this.THREE = THREE;\n      this.fontUrlArray = fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = GraphicsThree3D.utils;\n      this.lineRenderOrder = -103; ///\n\n      this.drawArrows = this.drawArrows.bind(this);\n    }\n\n    drawArrows(arrowData) {\n      let meshArray = [];\n      let materialProps = {\n        lineProps: arrowData.properties.line,\n        volumeProps: arrowData.properties.volume,\n        textProps: arrowData.properties.text,\n        areaProps: arrowData.properties.area\n      };\n      let insertionPoints = arrowData.insertionPoints;\n      let forceArrows = arrowData.components.forceArrows;\n      let momentArrows = arrowData.components.momentArrows;\n      let grainDirectionArrows = arrowData.components.grainDirectionArrows;\n\n      if (forceArrows) {\n        forceArrows.forEach(forceArrow => meshArray.push(this.drawForceArrow(forceArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      if (momentArrows) {\n        momentArrows.forEach(momentArrow => meshArray.push(this.drawMomentArrows(momentArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      if (grainDirectionArrows) {\n        grainDirectionArrows.forEach(grainDirectionArrow => meshArray.push(this.drawGrainDirectionArrow(grainDirectionArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      meshArray.forEach(arrow => this.GraphicsThree3D.addObjectToVisControl(arrow, \"arrow\"));\n      return meshArray;\n    }\n\n    addToDimChainCollisionDetection(obj) {\n      obj.updateMatrix();\n      obj.traverse(child => {\n        if (child.type === \"Mesh\") {\n          let intersectionObject = child.clone();\n          intersectionObject.applyMatrix4(obj.matrix);\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(intersectionObject);\n          this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n        }\n      });\n    }\n\n    drawGrainDirectionArrow(grainDirectionArrowData, materialProps, insertionPoints, arrowData) {\n      let insertionPoint = grainDirectionArrowData.insertionPoint;\n      let grainDirectionArrows = new this.THREE.Group();\n\n      if (grainDirectionArrowData.directionVector && grainDirectionArrowData.normalVector && (grainDirectionArrowData.directionVector.x || grainDirectionArrowData.directionVector.y || grainDirectionArrowData.directionVector.z) && (grainDirectionArrowData.normalVector.x || grainDirectionArrowData.normalVector.y || grainDirectionArrowData.normalVector.z)) {\n        let directionVector = this.utils.initVector3(grainDirectionArrowData.directionVector);\n        let normalVector = this.utils.initVector3(grainDirectionArrowData.normalVector);\n\n        if (!directionVector.dot(normalVector) && grainDirectionArrowData.height > 0 && grainDirectionArrowData.length.value1 > 0 && grainDirectionArrowData.length.value2 > 0) {\n          let mainLength = grainDirectionArrowData.length.value1;\n          let miniLength = grainDirectionArrowData.length.value2;\n          let miniHeight = grainDirectionArrowData.height / 2;\n          let mainPointLeft = new this.THREE.Vector3(-mainLength / 2, 0, 0);\n          let mainPointRight = new this.THREE.Vector3(mainLength / 2, 0, 0);\n          let miniPointLeft = mainPointLeft.clone().add(new this.THREE.Vector3(miniLength, miniHeight, 0));\n          let miniPointRight = mainPointRight.clone().add(new this.THREE.Vector3(-miniLength, -miniHeight, 0));\n          insertionPoints.forEach(point => {\n            let grainDirectionArrow = new this.THREE.Object3D();\n\n            if (insertionPoint) {\n              let arrowMesh = this.utils.drawConnectedLines([miniPointLeft, mainPointLeft, mainPointRight, miniPointRight], materialProps.lineProps);\n              let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n              let dotProduct = arrowMesh.up.dot(newUpVector);\n              let rotationAngle = -Math.acos(dotProduct); // by default THREE.js rotates counterclockwise, so I switched sign (correct?)\n\n              arrowMesh.rotateOnAxis(normalVector, rotationAngle);\n              arrowMesh.up = newUpVector;\n              arrowMesh.lookAt(arrowMesh.position.clone().add(normalVector));\n              grainDirectionArrow.add(arrowMesh);\n              grainDirectionArrow.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n            }\n\n            if (materialProps.textProps) {\n              this.addTextToArrow(materialProps.textProps, arrowData.components.texts, grainDirectionArrow, insertionPoint);\n            }\n\n            let insertionVector = point.insertionVector;\n            let angle = point.rotationAngle * Math.PI / 180;\n            let rotationAxis, rotationPoint;\n\n            if (angle) {\n              rotationAxis = this.utils.initVector3(point.rotationAxis);\n              rotationPoint = this.utils.initVector3(point.rotationPoint);\n            }\n\n            let pivotPoint = new this.THREE.Object3D();\n\n            if (angle) {\n              pivotPoint.position.copy(rotationPoint);\n              pivotPoint.attach(grainDirectionArrow);\n              pivotPoint.rotateOnAxis(rotationAxis, angle);\n            } else {\n              pivotPoint.attach(grainDirectionArrow);\n            }\n\n            pivotPoint.position.x += insertionVector.x;\n            pivotPoint.position.y += insertionVector.y;\n            pivotPoint.position.z += insertionVector.z;\n            this.addToDimChainCollisionDetection(grainDirectionArrow);\n            grainDirectionArrows.add(pivotPoint);\n          });\n        }\n      }\n\n      return grainDirectionArrows;\n    }\n\n    drawMomentArrows(momentArrowData, materialProps, insertionPoints, arrowData) {\n      let insertionPoint = momentArrowData.insertionPoint;\n      let headRadius = momentArrowData.headSize.value2 / 2;\n      let headHeight = momentArrowData.headSize.value1;\n      let size = momentArrowData.size;\n      let arcAngle = momentArrowData.arcAngle;\n      let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n      let lineMaterial = this.getMaterialFromColor(materialProps.lineProps.color); // TODO: Move this for loop outside this function\n\n      let momentArrows = new this.THREE.Group();\n      insertionPoints.forEach(point => {\n        let insertionVector = point.insertionVector;\n        let rotationAngle = point.rotationAngle * Math.PI / 180;\n        let rotationAxis, rotationPoint;\n\n        if (rotationAngle) {\n          rotationAxis = this.utils.initVector3(point.rotationAxis);\n          rotationPoint = this.utils.initVector3(point.rotationPoint);\n        }\n\n        let momentArrow = new this.THREE.Object3D();\n\n        if (insertionPoint) {\n          if (!arcAngle && momentArrowData.directionVector1 && (momentArrowData.directionVector1.x || momentArrowData.directionVector1.y || momentArrowData.directionVector1.z)) {\n            let directionVectorOne = this.utils.initVector3(momentArrowData.directionVector1);\n            let pointer = this.makeArrow(headRadius, headHeight, volumeMaterial, lineMaterial, materialProps.lineProps);\n            pointer.rotation.z = Math.PI;\n            let pointerTwo = pointer.clone();\n            pointer.position.y += headHeight;\n            let arrow = new this.THREE.Object3D();\n            let axisPointOne = new this.THREE.Vector3(0, 0, 0);\n            let axisPointTwo = new this.THREE.Vector3(0, size, 0);\n            let axisMesh = this.GraphicsThree3D.drawLine(axisPointOne, axisPointTwo, materialProps.lineProps);\n            arrow.add(axisMesh);\n            arrow.add(pointer);\n            arrow.add(pointerTwo);\n            arrow.rotation.x = -Math.PI / 2;\n            let pivot = new this.THREE.Object3D();\n            pivot.add(arrow);\n            pivot.lookAt(directionVectorOne);\n            momentArrow.add(pivot);\n          } else if (arcAngle && momentArrowData.directionVector1 && momentArrowData.directionVector2 && (momentArrowData.directionVector1.x || momentArrowData.directionVector1.y || momentArrowData.directionVector1.z) && (momentArrowData.directionVector2.x || momentArrowData.directionVector2.y || momentArrowData.directionVector2.z)) {\n            let directionVectorOne = this.utils.initVector3(momentArrowData.directionVector1);\n            let directionVectorTwo = this.utils.initVector3(momentArrowData.directionVector2);\n\n            if (!directionVectorOne.dot(directionVectorTwo)) {\n              let pointer = this.makeArrow(headRadius, headHeight, volumeMaterial, lineMaterial, materialProps.lineProps, true);\n              pointer.rotation.z = Math.PI;\n              console.log(arcAngle);\n              let correctedArcAngle = arcAngle;\n              if (correctedArcAngle > 360 || correctedArcAngle < -360) materialProps.lineProps.thickness = 0;\n              if (correctedArcAngle > 360) correctedArcAngle = 360;\n              if (correctedArcAngle < -360) correctedArcAngle = -360;\n              let curveObject = new this.THREE.Object3D();\n              let curveAngle = correctedArcAngle * Math.PI / 180;\n              let startAngle, endAngle;\n\n              if (curveAngle > 0) {\n                startAngle = 0;\n                endAngle = curveAngle;\n              } else {\n                startAngle = curveAngle;\n                endAngle = 0;\n              }\n\n              let curve = new this.THREE.EllipseCurve(0, 0, size / 2, size / 2, startAngle, endAngle, false, 0);\n              let curvePoints = curve.getPoints(50).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n              let curveMesh = this.utils.drawConnectedLines(curvePoints, materialProps.lineProps);\n              let pointDir = new this.THREE.Vector3();\n\n              if (curveAngle > 0) {\n                for (let i = curvePoints.length - 2; i >= 0; i--) {\n                  let distance = Math.round(curvePoints[curvePoints.length - 1].distanceTo(curvePoints[i]));\n\n                  if (distance == Math.round(headHeight)) {\n                    pointDir.subVectors(curvePoints[curvePoints.length - 1], curvePoints[i]).normalize();\n                    break;\n                  }\n                }\n\n                pointer.rotation.x = -Math.PI / 2;\n                let object = new this.THREE.Object3D();\n                object.add(pointer);\n                object.lookAt(pointDir);\n                object.position.x += curvePoints[curvePoints.length - 1].x;\n                object.position.y += curvePoints[curvePoints.length - 1].y;\n                pointer = object;\n              } else {\n                for (let i = 0; i < curvePoints.length; i++) {\n                  let distance = Math.round(curvePoints[0].distanceTo(curvePoints[i]));\n\n                  if (distance == Math.round(headHeight)) {\n                    pointDir.subVectors(curvePoints[0], curvePoints[i]).normalize();\n                    break;\n                  }\n                }\n\n                pointer.rotation.x = -Math.PI / 2;\n                let object = new this.THREE.Object3D();\n                object.add(pointer);\n                object.lookAt(pointDir);\n                object.position.x += curvePoints[0].x;\n                object.position.y += curvePoints[0].y;\n                pointer = object;\n              }\n\n              curveObject.add(curveMesh);\n              curveObject.add(pointer);\n              this.GraphicsThree3D.alignObjectWithVectors(curveObject, new this.THREE.Vector3(0, 0, 0), directionVectorOne, directionVectorTwo);\n              momentArrow.add(curveObject);\n            }\n          }\n\n          momentArrow.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n        }\n\n        if (materialProps.textProps) {\n          this.addTextToArrow(materialProps.textProps, arrowData.components.texts, momentArrow, insertionPoint);\n        }\n\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.copy(rotationPoint);\n          pivotPoint.attach(momentArrow);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(momentArrow);\n        }\n\n        pivotPoint.position.x += insertionVector.x;\n        pivotPoint.position.y += insertionVector.y;\n        pivotPoint.position.z += insertionVector.z;\n        this.addToDimChainCollisionDetection(momentArrow);\n        momentArrows.add(pivotPoint);\n      });\n      return momentArrows;\n    }\n\n    drawForceArrow(forceArrowData, materialProps, insertionPoints, arrowData) {\n      let forceArrows = new this.THREE.Group();\n      let headSize = forceArrowData.headSize;\n      let height = forceArrowData.height;\n      let length = forceArrowData.length;\n      let insertionProps = {\n        insertionPoint: forceArrowData.insertionPoint,\n        directionVectorOne: forceArrowData.directionVector1,\n        directionVectorTwo: forceArrowData.directionVector2,\n        directionVectorThree: forceArrowData.directionVector3\n      };\n      let division = forceArrowData.division;\n      let tiltAngle = forceArrowData.tiltAngle;\n      insertionPoints.forEach(point => {\n        let insertionVector = this.utils.initVector3(point.insertionVector);\n        let rotationAngle = point.rotationAngle * Math.PI / 180;\n        let rotationAxis, rotationPoint;\n\n        if (rotationAngle) {\n          rotationAxis = this.utils.initVector3(point.rotationAxis);\n          rotationPoint = this.utils.initVector3(point.rotationPoint);\n        }\n\n        let newArrow;\n        if (!length.value1 && !length.value2) newArrow = this.drawPointForceArrow(forceArrowData, headSize, height, materialProps, insertionProps, arrowData);else if (length.value1 > 0 || length.value2 > 0) newArrow = this.drawLineForceArrow(forceArrowData, materialProps, insertionProps, division, tiltAngle, arrowData);\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.copy(rotationPoint);\n          pivotPoint.attach(newArrow);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(newArrow);\n        }\n\n        pivotPoint.position.add(insertionVector);\n        forceArrows.add(pivotPoint);\n      });\n      return forceArrows;\n    }\n\n    drawLineForceArrow(forceArrowData, materialProps, insertionProps, division, tiltAngle, arrowData) {\n      // TODO: Will need directionVectors\n      let pointerRadius = forceArrowData.headSize.value2 / 2;\n      let pointerHeight = forceArrowData.headSize.value1;\n      let offsetAngle = tiltAngle * Math.PI / 180;\n      let horizontalLength = forceArrowData.length.value1;\n      let depthLength = forceArrowData.length.value2;\n      let heightLeft = forceArrowData.height.value1;\n      let heightRight = forceArrowData.height.value2;\n      let lineProps = materialProps.lineProps;\n      let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n      let borderMaterial = this.getMaterialFromColor(lineProps.color);\n      let areaMaterial;\n      if (materialProps.areaProps && materialProps.areaProps.color.alpha) areaMaterial = this.getMaterialFromColor(materialProps.areaProps.color);\n      let lineForceArrow = new this.THREE.Object3D();\n      let directionsDefined = forceArrowData.directionVector1 && forceArrowData.directionVector2 && forceArrowData.directionVector3 && (forceArrowData.directionVector1.x || forceArrowData.directionVector1.y || forceArrowData.directionVector1.z) && (forceArrowData.directionVector2.x || forceArrowData.directionVector2.y || forceArrowData.directionVector2.z) && (!(heightLeft && heightRight) || forceArrowData.directionVector3.x || forceArrowData.directionVector3.y || forceArrowData.directionVector3.z);\n      let areOrthogonal = false;\n      let dirVectorNormal, dirVectorHorizontal;\n\n      if (directionsDefined) {\n        if (depthLength) {\n          dirVectorHorizontal = this.utils.initVector3(forceArrowData.directionVector2).normalize();\n          dirVectorNormal = this.utils.initVector3(forceArrowData.directionVector3).normalize();\n          let vectorOne = this.utils.initVector3(forceArrowData.directionVector1).normalize();\n          if (!dirVectorHorizontal.dot(dirVectorNormal) && !vectorOne.dot(dirVectorHorizontal) && !vectorOne.dot(dirVectorNormal)) areOrthogonal = true;\n        } else {\n          dirVectorHorizontal = this.utils.initVector3(forceArrowData.directionVector2).normalize();\n          dirVectorNormal = new this.THREE.Vector3().crossVectors(dirVectorHorizontal, this.utils.initVector3(forceArrowData.directionVector1).negate().normalize());\n          if (!dirVectorHorizontal.dot(dirVectorNormal)) areOrthogonal = true;\n        }\n\n        if (areOrthogonal && insertionProps.insertionPoint && !(depthLength && !horizontalLength) && tiltAngle > -90 && tiltAngle < 90) {\n          let botAxisPointOne = new this.THREE.Vector3(0, 0, 0);\n          let botAxisPointTwo = new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle), 0);\n          let topAxisPointOne = new this.THREE.Vector3(0, heightLeft, 0);\n          let topAxisPointTwo = new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle) + heightRight, 0);\n          let botLine = this.GraphicsThree3D.drawLine(botAxisPointOne, botAxisPointTwo, lineProps);\n          let topLine = this.GraphicsThree3D.drawLine(topAxisPointOne, topAxisPointTwo, lineProps);\n          botLine.renderOrder = this.lineRenderOrder;\n          topLine.renderOrder = this.lineRenderOrder;\n          lineForceArrow.add(botLine);\n          lineForceArrow.add(topLine);\n\n          if (depthLength > 0) {\n            let depthBotAxisPointOne = botAxisPointOne.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthBotAxisPointTwo = botAxisPointTwo.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthTopAxisPointOne = topAxisPointOne.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthTopAxisPointTwo = topAxisPointTwo.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthBotLine = this.GraphicsThree3D.drawLine(depthBotAxisPointOne, depthBotAxisPointTwo, lineProps);\n            let depthTopLine = this.GraphicsThree3D.drawLine(depthTopAxisPointOne, depthTopAxisPointTwo, lineProps);\n            depthBotLine.renderOrder = this.lineRenderOrder;\n            depthTopLine.renderOrder = this.lineRenderOrder;\n            lineForceArrow.add(depthBotLine);\n            lineForceArrow.add(depthTopLine);\n            let pointContainer = [[botAxisPointOne, depthBotAxisPointOne], [botAxisPointTwo, depthBotAxisPointTwo], [topAxisPointOne, depthTopAxisPointOne], [topAxisPointTwo, depthTopAxisPointTwo]];\n\n            if (areaMaterial) {\n              let bottomPlaneGeo = this.getPlaneGeoFromPoints([botAxisPointOne, botAxisPointTwo, depthBotAxisPointTwo, depthBotAxisPointOne]);\n              let bottomPlane = new this.THREE.Mesh(bottomPlaneGeo, areaMaterial);\n              lineForceArrow.add(bottomPlane);\n              bottomPlane.renderOrder = 102;\n\n              bottomPlane.onBeforeRender = function (renderer) {\n                renderer.clearDepth();\n              };\n\n              if (heightLeft && heightRight) {\n                let topPlaneGeo = this.getPlaneGeoFromPoints([topAxisPointOne, topAxisPointTwo, depthTopAxisPointTwo, depthTopAxisPointOne]);\n                let topPlane = new this.THREE.Mesh(topPlaneGeo, areaMaterial);\n                lineForceArrow.add(topPlane);\n                topPlane.renderOrder = 102;\n\n                topPlane.onBeforeRender = function (renderer) {\n                  renderer.clearDepth();\n                };\n              }\n            }\n\n            pointContainer.forEach(points => {\n              let lineMesh = this.GraphicsThree3D.drawLine(points[0], points[1], lineProps);\n              lineForceArrow.add(lineMesh);\n            });\n          }\n\n          let horizontalDivisions = division.value1;\n          let horizontalSegmentLength = horizontalLength / horizontalDivisions;\n          let depthDivisions = division.value2;\n          let depthSegmentLength = depthLength / depthDivisions;\n          let pointer = this.makeArrow(pointerRadius, pointerHeight, volumeMaterial, borderMaterial, lineProps);\n          pointer.rotation.z = Math.PI;\n\n          for (let i = 1; i < depthDivisions; i++) {\n            let bottomPointOne = new this.THREE.Vector3(0, 0, i * depthSegmentLength);\n            let topPointOne = bottomPointOne.clone().add(new this.THREE.Vector3(0, heightLeft, 0));\n            let bottomPointTwo = bottomPointOne.clone().add(new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle), 0));\n            let topPointTwo = bottomPointTwo.clone().add(new this.THREE.Vector3(0, heightRight, 0));\n\n            if (topPointOne.y.toFixed(2) != bottomPointOne.y.toFixed(2)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPointOne, topPointOne, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPointOne);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n\n            if (topPointTwo.y.toFixed(2) != bottomPointTwo.y.toFixed(2)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPointTwo, topPointTwo, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPointTwo);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n          }\n\n          for (let i = 0; i < horizontalDivisions + 1; i++) {\n            let bottomPoint = new this.THREE.Vector3(i * horizontalSegmentLength * Math.cos(offsetAngle), i * horizontalSegmentLength * Math.sin(offsetAngle), 0);\n            let topPoint = bottomPoint.clone().add(new this.THREE.Vector3(0, heightLeft - i * (heightLeft - heightRight) / horizontalDivisions, 0));\n\n            if (topPoint.y.toFixed(2) != bottomPoint.y.toFixed(2) && !(heightLeft && heightRight && (i == 0 || i == horizontalDivisions) && depthLength > 0 && depthDivisions == 0)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPoint, topPoint, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPoint);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n\n            if (depthLength > 0) {\n              let depthBottomPoint = bottomPoint.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n              let depthTopPoint = topPoint.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n\n              if (depthTopPoint.y.toFixed(2) != depthBottomPoint.y.toFixed(2) && !(heightLeft && heightRight && (i == 0 || i == horizontalDivisions) && depthDivisions == 0)) {\n                let newAxis = this.GraphicsThree3D.drawLine(depthBottomPoint, depthTopPoint, lineProps);\n                newAxis.renderOrder = this.lineRenderOrder;\n                let newPointer = pointer.clone();\n                newPointer.position.add(depthBottomPoint);\n                lineForceArrow.add(newPointer);\n                lineForceArrow.add(newAxis);\n              }\n            }\n          }\n        }\n      }\n\n      if (materialProps.textProps) {\n        this.addTextToArrow(materialProps.textProps, arrowData.components.texts, lineForceArrow, insertionProps.insertionPoint);\n      }\n\n      if (directionsDefined && areOrthogonal) {\n        let insertionPoint = new this.THREE.Vector3(0, 0, 0);\n\n        if (insertionProps.insertionPoint) {\n          insertionPoint.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n        }\n\n        if (directionsDefined && areOrthogonal) this.GraphicsThree3D.alignObjectWithVectors(lineForceArrow, insertionPoint, dirVectorNormal, dirVectorHorizontal);\n      } else {\n        let insertionPoint = new this.THREE.Vector3(0, 0, 0);\n\n        if (insertionProps.insertionPoint) {\n          insertionPoint.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n        }\n\n        lineForceArrow.position.add(insertionPoint);\n      }\n\n      this.addToDimChainCollisionDetection(lineForceArrow);\n      return lineForceArrow;\n    }\n\n    drawPointForceArrow(forceArrowData, headSize, height, materialProps, insertionProps, arrowData) {\n      let pointForceArrow = new this.THREE.Object3D();\n\n      if (forceArrowData.directionVector1 && (forceArrowData.directionVector1.x || forceArrowData.directionVector1.y || forceArrowData.directionVector1.z)) {\n        let directionVector = this.utils.initVector3(forceArrowData.directionVector1);\n        let pointerRadius = headSize.value2 / 2;\n        let pointerHeight = headSize.value1;\n        let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n        let borderMaterial = this.getMaterialFromColor(materialProps.lineProps.color);\n        let pointer = this.makeArrow(pointerRadius, pointerHeight, volumeMaterial, borderMaterial, materialProps.lineProps);\n        pointer.rotation.z = Math.PI;\n        let axisHeight = height.value1;\n        let axisPointOne = new this.THREE.Vector3(0, 0, 0);\n        let axisPointTwo = new this.THREE.Vector3(0, axisHeight, 0);\n        let axis = this.GraphicsThree3D.drawLine(axisPointOne, axisPointTwo, materialProps.lineProps);\n        let arrow = new this.THREE.Object3D();\n        let arrowParts = new this.THREE.Object3D();\n        arrowParts.add(pointer);\n        arrowParts.add(axis);\n        arrow.add(arrowParts);\n        arrowParts.rotation.x = -Math.PI / 2;\n        arrow.lookAt(directionVector);\n        pointForceArrow.add(arrow);\n      }\n\n      pointForceArrow.position.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n\n      if (materialProps.textProps) {\n        this.addTextToArrow(materialProps.textProps, arrowData.components.texts, pointForceArrow, insertionProps.insertionPoint);\n      }\n\n      this.addToDimChainCollisionDetection(pointForceArrow);\n      return pointForceArrow;\n    }\n\n    addTextToArrow(textProps, texts, object, objectInsertionPoint) {\n      if (textProps.type && textProps.style && textProps.color.alpha) {\n        let font = this.GraphicsThree3D.loadFont(textProps);\n\n        if (texts) {\n          texts.forEach(text => {\n            if (text.insertionPoint && text.directionVector && text.normalVector && (text.directionVector.x || text.directionVector.y || text.directionVector.z) && (text.normalVector.x || text.normalVector.y || text.normalVector.z) && text.content) {\n              let newText = this.GraphicsThree3D.drawText(text, textProps, font);\n\n              if (objectInsertionPoint) {\n                newText.position.sub(new this.THREE.Vector3(objectInsertionPoint.x, objectInsertionPoint.y, objectInsertionPoint.z));\n              }\n\n              object.add(newText);\n            }\n          });\n        }\n      }\n    }\n\n    getPlaneGeoFromPoints(points) {\n      let bottomPlaneGeo = new this.THREE.Geometry();\n      bottomPlaneGeo.vertices.push(...points);\n      bottomPlaneGeo.faces.push(new this.THREE.Face3(0, 1, 2), new this.THREE.Face3(0, 2, 3));\n      bottomPlaneGeo.faceVertexUvs[0].push([new this.THREE.Vector2(0, 0), new this.THREE.Vector2(1, 0), new this.THREE.Vector2(1, 1)], [new this.THREE.Vector2(0, 0), new this.THREE.Vector2(1, 1), new this.THREE.Vector2(0, 1)]);\n      bottomPlaneGeo.uvsNeedUpdate = true;\n      bottomPlaneGeo.computeFaceNormals();\n      bottomPlaneGeo.computeVertexNormals();\n      return bottomPlaneGeo;\n    }\n\n    getMaterialFromColor(color, isLine) {\n      let materialOptions = {\n        color: \"rgb(\" + color.red + \", \" + color.green + \", \" + color.blue + \")\",\n        polygonOffset: true,\n        polygonOffsetFactor: 1,\n        polygonOffsetUnits: 1,\n        transparent: true,\n        opacity: color.alpha / 255,\n        side: this.THREE.DoubleSide\n      };\n      let material = isLine ? new this.THREE.LineBasicMaterial(materialOptions) : new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    makeArrow(radius, height, areaMaterial, borderMaterial, lineProps, forArc = false) {\n      let localAreaMaterial = areaMaterial.clone();\n      localAreaMaterial.side = this.THREE.FrontSide;\n      let coneMesh, coneGeo, edgePts;\n\n      if (radius && height) {\n        coneGeo = new this.THREE.ConeGeometry(radius, height, 32);\n        coneGeo.translate(0, -height / 2, 0);\n        coneMesh = new this.THREE.Mesh(coneGeo, localAreaMaterial);\n        edgePts = coneGeo.vertices.slice(1, 33);\n        let line = this.utils.drawConnectedLines(edgePts, lineProps, true);\n        line.renderOrder = this.lineRenderOrder;\n        coneMesh.add(line);\n        let borderMesh;\n\n        if (lineProps.thickness && lineProps.color.alpha) {\n          let scale = 1 + lineProps.thickness / 4;\n          borderMesh = new this.THREE.Mesh(coneGeo, borderMaterial);\n          borderMesh.scale.multiplyScalar(scale);\n          borderMesh.material.side = this.THREE.BackSide;\n          borderMesh.position.y = (height - 1) * lineProps.thickness / 4;\n          coneMesh.add(borderMesh);\n          if (!forArc) coneMesh.position.y += 2 * lineProps.thickness;\n        }\n\n        let order = -99;\n        coneMesh.renderOrder = order;\n        if (borderMesh) borderMesh.renderOrder = order + 1;\n      } else coneMesh = new this.THREE.Mesh();\n\n      return coneMesh;\n    }\n\n  }\n\n  _exports.ArrowLoader = ArrowLoader;\n});","define(\"m08-2020/lib old/FigureLoaders/DimensionalChainLoader\", [\"exports\", \"m08-2020/lib old/Utils\", \"three-spritetext\"], function (_exports, _Utils, _threeSpritetext) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DimensionalChainLoader = void 0;\n\n  class DimensionalChainLoader {\n    constructor(GraphicsThree3D) {\n      this.THREE = GraphicsThree3D.THREE;\n      this.fontUrlArray = GraphicsThree3D.fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = new _Utils.Utils(GraphicsThree3D.THREE);\n      this.intersectionDetection = GraphicsThree3D.DimensionalChainIntersectionDetection; ///\n\n      this.drawMaskette = this.drawMaskette.bind(this);\n    }\n\n    drawDimensionalChains(dimensionalChains) {\n      if (dimensionalChains && dimensionalChains.length > 0 && !this.isOldDimChainJSON(dimensionalChains[0])) this.sortChainsByRank(dimensionalChains);\n      dimensionalChains.forEach(dimensionalChain => {\n        console.log(dimensionalChain);\n        let meshArray = this.drawMaskette(dimensionalChain);\n        meshArray.forEach(mesh => {\n          let drawnObject = new Object();\n          drawnObject.id = dimensionalChain.id;\n          drawnObject.type = \"maskette\";\n          drawnObject.data = dimensionalChain;\n          drawnObject.sceneObject = mesh;\n          drawnObject.isOnScene = mesh.IsHighLight ? false : true;\n          this.GraphicsThree3D.ThreeObjects.push(drawnObject);\n          if (!mesh.IsHighLight) this.GraphicsThree3D.scene.add(drawnObject.sceneObject);\n        });\n      });\n      this.intersectionDetection.chainArr = [];\n    }\n\n    sortChainsByRank(dimensionalChains) {\n      dimensionalChains.sort((a, b) => b.alignments.dimensionalChainArrangement.rankOrder - a.alignments.dimensionalChainArrangement.rankOrder);\n    }\n\n    isOldDimChainJSON(chainData) {\n      return chainData.alignments.view3d && chainData.alignments.view3d.hasOwnProperty(\"textAlignment\") || chainData.alignments.views2d && chainData.alignments.views2d[0].hasOwnProperty(\"textAlignment\");\n    }\n\n    drawMaskette(dimensionalChain, alignment2D = null) {\n      let meshArray = [];\n      let alignments = dimensionalChain.alignments;\n      let alignmentData = alignments.view3d;\n      let addToVisControl = true;\n\n      if (alignment2D) {\n        // addToVisControl = false;\n        alignmentData = { ...alignmentData,\n          ...alignment2D\n        };\n      }\n\n      if (this.isOldDimChainJSON(dimensionalChain)) {\n        alignments.textAlignment = alignmentData.textAlignment;\n        alignments.textArrangement = 2;\n        alignments.dimensionalChainArrangement = {\n          distance: 0,\n          rankOrder: 0,\n          type: 2\n        };\n      }\n\n      let alignmentOffset = alignmentData.offsetVector;\n      let insertionPoints = dimensionalChain.insertionPoints;\n\n      if (alignmentOffset && (alignmentOffset.x || alignmentOffset.y || alignmentOffset.z)) {\n        ///\n        if (alignments.dimensionalChainArrangement.type === 1) {\n          let chainType = this.getChainType(dimensionalChain);\n          let offsetVector = this.getChainOffsetVector(dimensionalChain, chainType, alignmentData);\n          alignmentOffset = {\n            x: offsetVector.x,\n            y: offsetVector.y,\n            z: offsetVector.z\n          };\n          alignmentData.offsetVector = alignmentOffset;\n        } ///\n\n\n        let chain = this.drawDimensionalChain(dimensionalChain, alignmentData);\n\n        if (chain) {\n          insertionPoints.forEach(insertionPoint => {\n            let insertionVector = insertionPoint.insertionVector;\n            let objectRotationAngle = insertionPoint.rotationAngle;\n            let chainObject = new this.THREE.Object3D();\n\n            if (objectRotationAngle) {\n              objectRotationAngle = objectRotationAngle * Math.PI / 180;\n              let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n              let rotationPoint = this.utils.initVector3(insertionPoint.rotationPoint);\n              chainObject.position.copy(rotationPoint);\n              chainObject.attach(chain.clone());\n              chainObject.updateMatrix();\n              chainObject.rotateOnAxis(rotationAxis, objectRotationAngle);\n            } else chainObject.add(chain.clone());\n\n            chainObject.position.add(insertionVector);\n            if (addToVisControl) this.GraphicsThree3D.addObjectToVisControl(chainObject, \"dimensionalChain\");\n            meshArray.push(chainObject);\n          });\n        }\n      }\n\n      return meshArray;\n    }\n\n    drawDimensionalChain(dimensionalChain, alignmentData) {\n      let referencePoints = dimensionalChain.referencePoints; // Components\n\n      let hasBaseline = dimensionalChain.components.baseLine;\n      let baseLineOverhang = dimensionalChain.components.baseLineOverhang;\n      let endLine = dimensionalChain.components.endLine;\n      let endSymbol = dimensionalChain.components.endSymbol;\n      let texts = dimensionalChain.components.texts; // Alignment\n\n      let alignments = dimensionalChain.alignments;\n      let alignmentOffset = this.utils.initVector3(alignmentData.offsetVector); // Properties\n\n      let lineProps = dimensionalChain.properties.line;\n      let areaProps = dimensionalChain.properties.area;\n      let textProps = dimensionalChain.properties.text; // Materials\n\n      let areaMaterial = areaProps ? this.GraphicsThree3D.getMaterialFromColor(areaProps.color) : null;\n      if (areaMaterial) areaMaterial.side = this.THREE.FrontSide;\n      let chain = new this.THREE.Object3D();\n      let chainType = this.getChainType(dimensionalChain);\n\n      if (chainType === \"linear\") {\n        let startPoint = this.utils.initVector3(referencePoints[0]);\n        let endPoint = this.utils.initVector3(referencePoints[referencePoints.length - 1]);\n        let chainDirection = new this.THREE.Vector3().copy(endPoint).sub(startPoint).normalize();\n        let chainNormal = new this.THREE.Vector3().crossVectors(chainDirection, alignmentOffset.clone().normalize()).normalize();\n        let dx = endPoint.x - startPoint.x;\n        let dy = endPoint.y - startPoint.y; // baseLine\n\n        if (hasBaseline) {\n          let lineMesh = this.utils.drawConnectedLines([startPoint, endPoint], lineProps);\n          chain.add(lineMesh); ///\n          // console.log(startPoint, endPoint)\n\n          let lineForIntersection = this.utils.drawLine(startPoint, endPoint, lineProps); // lineForIntersection.geometry.computeBoundingBox();\n          // lineForIntersection.updateMatrix();\n          // console.log(lineForIntersection)\n\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(lineForIntersection);\n          intersectionObjects.forEach(object => {\n            object.position.add(alignmentOffset);\n            object.updateMatrixWorld();\n          });\n          this.intersectionDetection.chainArr.push(intersectionObjects);\n        } // baseLineOverhang\n\n\n        if (baseLineOverhang && baseLineOverhang.position && baseLineOverhang.length) {\n          let overhangLength = baseLineOverhang.length;\n          let overhangPosition = baseLineOverhang.position;\n          let offset = new this.THREE.Vector3().copy(chainDirection).multiplyScalar(overhangLength);\n\n          if (overhangPosition === 1 || overhangPosition === 2) {\n            let overhangPoint = startPoint.clone().sub(offset);\n            let lineMesh = this.utils.drawConnectedLines([startPoint, overhangPoint], lineProps);\n            chain.add(lineMesh);\n          }\n\n          if (overhangPosition === 1 || overhangPosition === 3) {\n            let overhangPoint = endPoint.clone().add(offset);\n            let lineMesh = this.utils.drawConnectedLines([endPoint, overhangPoint], lineProps);\n            chain.add(lineMesh);\n          }\n        }\n\n        for (let i = 0; i < referencePoints.length - 1; i++) {\n          let referencePointOne = this.utils.initVector3(referencePoints[i]);\n          let referencePointTwo = this.utils.initVector3(referencePoints[i + 1]); // endLine\n\n          if (endLine) {\n            let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n            let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, referencePointOne);\n            chain.add(lineMesh);\n\n            if (i == referencePoints.length - 2) {\n              let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, referencePointTwo);\n              chain.add(lineMesh);\n            }\n          } // endSymbol\n\n\n          if (endSymbol) {\n            let endSymbolType = endSymbol.type;\n\n            if (endSymbolType === 1 && endSymbol.size) {\n              let circleRadius = endSymbol.size / 2;\n              let circle = this.drawEndSymbolCircle(circleRadius, areaMaterial, lineProps, chainNormal, referencePointOne);\n              chain.add(circle);\n\n              if (i == referencePoints.length - 2) {\n                let circle = this.drawEndSymbolCircle(circleRadius, areaMaterial, lineProps, chainNormal, referencePointTwo);\n                chain.add(circle);\n              }\n            } else if (endSymbolType === 2) {\n              let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n              let lineMesh = this.drawEndSymbolLine(offsetDir, chainNormal, referencePointOne, endSymbol, lineProps);\n              chain.add(lineMesh);\n\n              if (i == referencePoints.length - 2) {\n                let lineMesh = this.drawEndSymbolLine(offsetDir, chainNormal, referencePointTwo, endSymbol, lineProps);\n                chain.add(lineMesh);\n              }\n            } else if (endSymbolType === 3) {\n              let arrowRadius = endSymbol.size / 4;\n              let arrowLength = endSymbol.size;\n              let arrowOne = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              let arrowTwo = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              arrowOne.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n              arrowTwo.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n              arrowOne.position.add(referencePointOne);\n              arrowTwo.position.add(referencePointTwo);\n              chain.add(arrowOne);\n              chain.add(arrowTwo);\n            } else if (endSymbolType === 4) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n              arrow.position.add(referencePointOne);\n              chain.add(arrow);\n\n              if (i == referencePoints.length - 2) {\n                let arrowRadius = endSymbol.size / 2;\n                let arrowSize = endSymbol.size;\n                let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n                arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n                arrow.position.add(referencePointTwo);\n                chain.add(arrow);\n              }\n            } else if (endSymbolType === 5) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n              arrow.position.add(referencePointOne);\n              chain.add(arrow);\n\n              if (i == referencePoints.length - 2) {\n                let arrowRadius = endSymbol.size / 2;\n                let arrowSize = endSymbol.size;\n                let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n                arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n                arrow.position.add(referencePointTwo);\n                chain.add(arrow);\n              }\n            }\n          }\n        } // text\n\n\n        let canDrawText = this.canDrawText(dimensionalChain);\n\n        if (canDrawText) {\n          let font = this.GraphicsThree3D.loadFont(textProps);\n\n          if (alignments.textAlignment === 1) {\n            for (let text of texts) {\n              let textInsideChain = this.textInsideChain(text, startPoint, endPoint, chainDirection);\n\n              if (textInsideChain) {\n                let textInsertionPoint = text.insertionPoint;\n                let textContent = text.content;\n                let textOffset = text.offset;\n                let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n                let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                let textPosition = this.utils.initVector3(textInsertionPoint);\n                textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                let textMesh = this.makeTextSprite(textContent, textPosition, textProps);\n                chain.add(textMesh);\n              }\n            }\n          } else if (alignments.textAlignment === 2 && alignmentData.normalVector && alignmentData.directionVector) {\n            let textDataArr = [];\n            let tempIntersectionTexts = [];\n            let isIntersection = false;\n\n            for (let text of texts) {\n              let textInsideChain = this.textInsideChain(text, startPoint, endPoint, chainDirection);\n\n              if (textInsideChain) {\n                let textInsertionPoint = text.insertionPoint;\n                let textOffset = text.offset;\n                let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n                let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                let textPosition = this.utils.initVector3(textInsertionPoint);\n                textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                let textData = {\n                  content: text.content,\n                  id: text.id,\n                  insertionPoint: textPosition,\n                  offset: text.offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                }; ///\n\n                let normalVector = this.utils.initVector3(alignmentData.normalVector);\n                let directionVector = this.utils.initVector3(alignmentData.directionVector);\n\n                if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n                  let offsetVec = this.utils.initVector3(alignmentOffset);\n                  textData.negativeOffset = textOffsetDir.clone().normalize().multiplyScalar(2 * textData.offset);\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  box.position.add(offsetVec);\n\n                  if (alignments.textAlignment === 2 && alignments.textArrangement === 1) {\n                    let linePtsArr = [];\n                    linePoints.forEach(point => point.add(offsetVec));\n\n                    for (let i = 0; i < linePoints.length - 1; i++) linePtsArr.push([linePoints[i], linePoints[i + 1]]);\n\n                    isIntersection = this.intersectionDetection.checkCollision(linePtsArr, tempIntersectionTexts);\n                    let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n                    tempIntersectionTexts.push(intersectionObjects);\n                  }\n\n                  textDataArr.push({\n                    textData,\n                    box\n                  });\n                }\n              }\n            }\n\n            for (let i = 0; i < textDataArr.length; i++) {\n              let textData = textDataArr[i].textData;\n              let box = textDataArr[i].box;\n\n              if (isIntersection && alignments.textAlignment === 2 && alignments.textArrangement === 1) {\n                textData.offset = textDataArr[0].textData.offset;\n\n                if (i % 2 === 1) {\n                  textData.insertionPoint.sub(textData.negativeOffset);\n                  box.position.sub(textData.negativeOffset);\n                }\n              }\n\n              let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n              this.intersectionDetection.chainArr.push(intersectionObjects);\n              let textMesh = this.GraphicsThree3D.drawText(textData, textProps, font);\n              chain.add(textMesh);\n            }\n          }\n        }\n\n        chain.position.add(this.utils.initVector3(alignmentOffset));\n      } else if (chainType === \"arc\") {\n        let centerPoint = this.utils.initVector3(dimensionalChain.referencePoints[0]);\n        let arcRadius = alignmentOffset.length(); // baseLine\n\n        let rotationAngle = dimensionalChain.shape.rotationAngle;\n        let rotationAxis = new this.THREE.Vector3(0, 0, 1);\n        let shapeAxis = this.utils.initVector3(dimensionalChain.shape.rotationAxis).normalize();\n        let quaternion = new this.THREE.Quaternion().setFromUnitVectors(shapeAxis, rotationAxis);\n        alignmentOffset.applyQuaternion(quaternion);\n        let offsetVectorNormalized = alignmentOffset.clone().normalize();\n        let startAngle = -Math.acos(offsetVectorNormalized.x) + this.GraphicsThree3D.degreeToRadians(rotationAngle);\n        let endAngle = -Math.acos(offsetVectorNormalized.x);\n        let isClockwise = rotationAngle > 0;\n        let curve = new this.THREE.EllipseCurve(0, 0, arcRadius, arcRadius, startAngle, endAngle, isClockwise ? true : false, 0);\n        let curvePoints = curve.getPoints(50).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n\n        if (hasBaseline) {\n          let ellipse = this.utils.drawConnectedLines(curvePoints, lineProps);\n          chain.add(ellipse); ///\n\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(ellipse);\n          intersectionObjects.forEach(object => {\n            object.lookAt(shapeAxis);\n            object.position.add(centerPoint);\n            object.updateMatrixWorld();\n          });\n          this.intersectionDetection.chainArr.push(intersectionObjects);\n        }\n\n        let pseudoReferencePoints = [curvePoints[0], curvePoints[curvePoints.length - 1]];\n        pseudoReferencePoints.forEach((pseudoReferencePoint, i) => {\n          // baseLineOverhang\n          if (baseLineOverhang && baseLineOverhang.position) {\n            let overhangLength = baseLineOverhang.length;\n            let overHangPosition = baseLineOverhang.position;\n            let pivotPoint = new this.THREE.Vector3(0, 0, 0);\n            let overhangPoint;\n\n            if (overHangPosition == 1) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? overhangLength : -overhangLength, 0, 0);\n            } else if (overHangPosition == 2) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? 0 : -overhangLength, 0, 0);\n            } else if (overHangPosition == 3) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? overhangLength : 0, 0, 0);\n            }\n\n            if (!pivotPoint.equals(overhangPoint)) {\n              let lineMesh = this.utils.drawConnectedLines([pivotPoint, overhangPoint], lineProps);\n              lineMesh.rotateOnAxis(rotationAxis, i == 0 ? Math.PI / 2 + startAngle : Math.PI / 2 + endAngle);\n              if (rotationAngle < 0) lineMesh.rotateOnAxis(rotationAxis, Math.PI);\n              lineMesh.position.copy(pseudoReferencePoint);\n              chain.add(lineMesh);\n            }\n          } // endLine\n\n\n          if (endLine) {\n            let offsetDir = offsetVectorNormalized.clone();\n            if (i === 0) offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle));\n            let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, pseudoReferencePoint);\n            chain.add(lineMesh);\n          } // endSymbol\n\n\n          if (endSymbol) {\n            let endSymbolType = endSymbol.type;\n\n            if (endSymbolType === 1 && endSymbol.size) {\n              let circleRadius = endSymbol.size / 2;\n              let circle = this.makeCircleWithEdges(circleRadius, areaMaterial, lineProps);\n              circle.position.copy(pseudoReferencePoint);\n              chain.add(circle);\n            } else if (endSymbolType === 2) {\n              let offsetDir = offsetVectorNormalized.clone();\n              if (i === 0) offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle));\n              let lineMesh = this.drawEndSymbolLine(offsetDir, rotationAxis, pseudoReferencePoint, endSymbol, lineProps, true);\n              chain.add(lineMesh);\n            } else if (endSymbolType === 3) {\n              let arrowRadius = endSymbol.size / 4;\n              let arrowLength = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? startAngle : Math.PI + endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            } else if (endSymbolType === 4) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? startAngle : endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            } else if (endSymbolType === 5) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? Math.PI + startAngle : Math.PI + endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            }\n          }\n        }); // text\n\n        let canDrawText = this.canDrawText(dimensionalChain);\n\n        if (canDrawText) {\n          let font = this.GraphicsThree3D.loadFont(textProps);\n\n          for (let i = 0; i < texts.length; i++) {\n            let textContent = texts[i].content;\n            let textInsertionPoint = texts[i].insertionPoint;\n            let referencePoint = dimensionalChain.referencePoints[0];\n\n            if (textContent && textInsertionPoint && this.utils.checkVectorsEquality(textInsertionPoint, referencePoint)) {\n              let textOffset = texts[i].offset;\n              let offsetDir = new this.THREE.Vector3().copy(alignmentOffset);\n              offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle / 2));\n              let textPosition = new this.THREE.Vector3().copy(offsetDir);\n              offsetDir = offsetDir.normalize();\n              textPosition.add(offsetDir.multiplyScalar(textOffset));\n\n              if (alignments.textAlignment === 1) {\n                let textMesh = this.makeTextSprite(textContent, textPosition, textProps);\n                chain.add(textMesh);\n              } else if (alignments.textAlignment === 2 && alignmentData.normalVector && alignmentData.directionVector) {\n                let textData = {\n                  content: texts[i].content,\n                  id: texts[i].id,\n                  insertionPoint: textPosition,\n                  offset: texts[i].offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                };\n                let textMesh = this.GraphicsThree3D.drawText(textData, textProps, font);\n                chain.add(textMesh); ///\n\n                let normalVector = this.utils.initVector3(alignmentData.normalVector);\n                let directionVector = this.utils.initVector3(alignmentData.directionVector);\n\n                if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  let pivotObj = new this.THREE.Object3D();\n                  pivotObj.attach(box);\n                  pivotObj.lookAt(shapeAxis);\n                  pivotObj.position.add(centerPoint);\n                  pivotObj.updateMatrixWorld();\n                  pivotObj.children.forEach(child => child.applyMatrix4(pivotObj.matrixWorld));\n                  let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n                  this.intersectionDetection.chainArr.push(intersectionObjects);\n                }\n              }\n            }\n          }\n        }\n\n        chain.lookAt(shapeAxis);\n        chain.position.add(centerPoint);\n      }\n\n      return chain;\n    }\n\n    makeTextBoxWithLines(text, textProps, font) {\n      let normalVector = this.utils.initVector3(text.normalVector);\n      let directionVector = this.utils.initVector3(text.directionVector);\n      let textInsertionPoint = text.insertionPoint;\n      let textGeo = this.makeTextGeoLight(text, textProps, font);\n      textGeo.computeBoundingBox();\n      let bboxMax = textGeo.boundingBox.max;\n      let bboxMin = textGeo.boundingBox.min;\n      let linePoints = [new this.THREE.Vector3(bboxMin.x, bboxMax.y, 0), new this.THREE.Vector3(bboxMin.x, bboxMin.y, 0), new this.THREE.Vector3(bboxMax.x, bboxMin.y, 0), new this.THREE.Vector3(bboxMax.x, bboxMax.y, 0)];\n      let boxWidth = bboxMax.x - bboxMin.x;\n      let boxHeight = bboxMax.y - bboxMin.y;\n      let boxDepth = 0.1;\n      let textMaterial = new this.THREE.MeshBasicMaterial({\n        color: 0xff0000\n      });\n      let boxGeometry = new this.THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n      let textMeshBox = new this.THREE.Mesh(boxGeometry, textMaterial);\n      this.GraphicsThree3D.alignObjectWithVectors(textMeshBox, textInsertionPoint, normalVector, directionVector);\n      textMeshBox.updateMatrix();\n      linePoints.forEach(point => point.applyMatrix4(textMeshBox.matrix));\n      return [textMeshBox, linePoints];\n    }\n\n    textInsideChain(textData, startPoint, endPoint, chainDirection) {\n      const TOLERANCE = 0.0001;\n      let textInsertionPoint = textData.insertionPoint;\n      let textContent = textData.content;\n      let insideChain;\n\n      if (textInsertionPoint) {\n        let textInsertionVector = this.utils.initVector3(textInsertionPoint);\n        let insertionLength = textInsertionVector.length();\n        textInsertionVector.sub(startPoint).normalize();\n        insideChain = textContent && this.utils.initVector3(textInsertionPoint).distanceTo(startPoint) <= endPoint.distanceTo(startPoint) && (chainDirection.dot(textInsertionVector) > 1 - TOLERANCE || !textInsertionVector.length());\n        if (!insideChain) console.log(insertionLength);\n      }\n\n      return insideChain;\n    }\n\n    canDrawText(chainData) {\n      let texts = chainData.components.texts;\n      let alignments = chainData.alignments;\n      let textProps = chainData.properties.text;\n      return texts && textProps.type && textProps.style && alignments.textAlignment && alignments.textArrangement && textProps.size && textProps.heightWidthRatio;\n    }\n\n    makeTextGeoLight(text, textProps, font) {\n      let textContent = text.content;\n      let fontSize = textProps.size;\n      let heightWidthRatio = textProps.heightWidthRatio;\n      let textGeo = new this.THREE.TextBufferGeometry(textContent, {\n        font: font,\n        size: fontSize,\n        height: 0.1,\n        curveSegments: 1\n      });\n      textGeo.scale(heightWidthRatio, 1, 1);\n      textGeo.center();\n      return textGeo;\n    }\n\n    drawEndLine(offsetDir, lineData, lineProps, linePos) {\n      let [ptOne, ptTwo] = this.getEndLinePts(lineData, offsetDir);\n      let lineMesh = this.utils.drawConnectedLines([ptOne, ptTwo], lineProps);\n      lineMesh.position.add(linePos);\n      return lineMesh;\n    }\n\n    getEndLinePts(lineData, offsetDir) {\n      let length = lineData.lenght;\n      let overhang = lineData.overhang;\n      length *= -1;\n      console.log(lineData, offsetDir);\n      let ptOne = offsetDir.clone().multiplyScalar(length);\n      let ptTwo = offsetDir.clone().multiplyScalar(overhang);\n      return [ptOne, ptTwo];\n    }\n\n    drawEndSymbolCircle(radius, areaMaterial, lineProps, normal, position) {\n      let circle = this.makeCircleWithEdges(radius, areaMaterial, lineProps);\n      circle.lookAt(normal);\n      circle.position.copy(position);\n      return circle;\n    }\n\n    drawEndSymbolLine(offsetDir, normalVector, linePos, symbolData, lineProps, forArc = false) {\n      let lineSize = symbolData.size;\n      let ptOne = offsetDir.clone().multiplyScalar(lineSize / 2);\n      let ptTwo = offsetDir.clone().multiplyScalar(-lineSize / 2);\n      let rotation = Math.PI / 4;\n      if (forArc) rotation *= -1;\n      ptOne.applyAxisAngle(normalVector, rotation);\n      ptTwo.applyAxisAngle(normalVector, rotation);\n      let lineMesh = this.utils.drawConnectedLines([ptOne, ptTwo], lineProps);\n      lineMesh.position.add(linePos);\n      return lineMesh;\n    }\n\n    makeArrow(radius, height, areaMaterial, lineProps) {\n      let arrowMesh;\n\n      if (areaMaterial) {\n        let pts = [new this.THREE.Vector2(0, 0), new this.THREE.Vector2(-radius, -height), new this.THREE.Vector2(radius, -height)];\n        let arrowShape = new this.THREE.Shape(pts);\n        let arrowGeo = new this.THREE.ExtrudeBufferGeometry(arrowShape, {\n          depth: 0.1,\n          bevelEnabled: false\n        });\n        arrowGeo.translate(0, 0, -0.05);\n        arrowMesh = new this.THREE.Mesh(arrowGeo, areaMaterial);\n        let linePoints = pts.map(point => new this.THREE.Vector3(point.x, point.y, 0));\n        let line = this.utils.drawConnectedLines(linePoints, lineProps, true);\n        arrowMesh.add(line);\n        arrowMesh.renderOrder = 1;\n      } else {\n        arrowMesh = new this.THREE.Mesh(); // TODO: Might need better solution\n      }\n\n      return arrowMesh;\n    }\n\n    makeCircleWithEdges(radius, areaMaterial, lineProps, customParameters = null) {\n      let circleGeo;\n\n      if (customParameters) {\n        let thetaStart = customParameters.thetaStart;\n        let thetaEnd = customParameters.thetaEnd;\n        circleGeo = new this.THREE.CircleGeometry(radius, 32, thetaStart, thetaEnd);\n        let radiusTwo = customParameters.radiusTwo;\n        circleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radius, 1));\n      } else {\n        circleGeo = new this.THREE.CircleGeometry(radius, 32);\n      }\n\n      let areaMesh = new this.THREE.Mesh(circleGeo, areaMaterial);\n      let linePts = circleGeo.vertices.slice(1);\n      let line = this.utils.drawConnectedLines(linePts, lineProps, true);\n      areaMesh.add(line);\n      return areaMesh;\n    }\n\n    makeTextSprite(textContent, textPosition, textProps) {\n      let textColor = \"rgba(\" + textProps.color.red + \", \" + textProps.color.green + \", \" + textProps.color.blue + \", \" + textProps.color.alpha + \")\";\n      let textMesh = new _threeSpritetext.default(textContent);\n      textMesh.fontSize = 100;\n      textMesh.textHeight = textProps.size;\n      textMesh.geometry.scale(textProps.heightWidthRatio, 1, 1);\n      textMesh.color = textColor;\n      textMesh.renderOrder = 1;\n      if (textProps.type == 1) textMesh.fontFace = \"Arial\";else if (textProps.type == 2) textMesh.fontFace = \"Calibri\";else if (textProps.type == 3) textMesh.fontFace = \"Courier\";else if (textProps.type == 4) textMesh.fontFace = \"TimesNewRoman\";\n\n      if (textProps.style == 2 || textProps.style == 4) {\n        textMesh.geometry.applyMatrix4(new this.THREE.Matrix4().set(1, 0.1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1));\n      }\n\n      if (textProps.style == 3 || textProps.style == 4) {\n        textMesh.fontWeight = \"bold\";\n      }\n\n      textMesh.position.copy(textPosition);\n      return textMesh;\n    }\n\n    getChainType(dimensionalChain) {\n      let typeOfChain = dimensionalChain.shape.type;\n      let referencePoints = dimensionalChain.referencePoints;\n      if (typeOfChain === 1 && referencePoints.length >= 2) return \"linear\";else if (typeOfChain === 2 && dimensionalChain.referencePoints.length === 1 && dimensionalChain.shape.rotationAngle && dimensionalChain.shape.rotationAxis && (dimensionalChain.shape.rotationAxis.x || dimensionalChain.shape.rotationAxis.y || dimensionalChain.shape.rotationAxis.z)) return \"arc\";\n    }\n\n    getChainOffsetVector(chainData, chainType, alignmentData) {\n      let initOffset = alignmentData.offsetVector;\n      let offsetDir = this.utils.initVector3(initOffset).normalize();\n      let distance = chainData.alignments.dimensionalChainArrangement.distance;\n      let offsetDistance = new this.THREE.Vector3().copy(offsetDir).multiplyScalar(distance);\n      let offsetVector = new this.THREE.Vector3().copy(offsetDistance);\n      let meshArr = this.intersectionDetection.meshArr;\n      let chainArr = this.intersectionDetection.chainArr;\n      let isIntersection;\n\n      do {\n        isIntersection = false;\n        let linePtsArr = [];\n\n        if (chainType === \"linear\") {\n          let startPoint = this.utils.initVector3(chainData.referencePoints[0]).add(offsetVector);\n          let endPoint = this.utils.initVector3(chainData.referencePoints[chainData.referencePoints.length - 1]).add(offsetVector);\n          linePtsArr.push([startPoint, endPoint]);\n        } else {\n          let curvePoints = this.generateArcPoints(chainData, offsetVector);\n\n          for (let i = 0; i < curvePoints.length - 1; i++) linePtsArr.push([curvePoints[i], curvePoints[i + 1]]);\n        } ///\n\n\n        let texts = chainData.components.texts;\n        let textProps = chainData.properties.text;\n        let referencePoints = chainData.referencePoints;\n        let canDrawText = this.canDrawText(chainData);\n        let font = canDrawText ? this.GraphicsThree3D.loadFont(textProps) : null;\n\n        if (chainData.alignments.textAlignment === 2) {\n          if (chainType === \"linear\") {\n            let startPoint = this.utils.initVector3(referencePoints[0]);\n            let endPoint = this.utils.initVector3(referencePoints[referencePoints.length - 1]);\n            let chainDirection = new this.THREE.Vector3().copy(endPoint).sub(startPoint).normalize();\n\n            if (canDrawText) {\n              texts.forEach(text => {\n                let textInsideChain = this.textInsideChain(text, startPoint, endPoint, chainDirection);\n\n                if (textInsideChain) {\n                  let textInsertionPoint = text.insertionPoint;\n                  let textOffset = text.offset;\n                  let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                  let offsetDir = this.utils.initVector3(initOffset).normalize();\n                  let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                  let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                  let textPosition = this.utils.initVector3(textInsertionPoint);\n                  textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                  let textData = {\n                    content: text.content,\n                    id: text.id,\n                    insertionPoint: textPosition,\n                    offset: text.offset,\n                    normalVector: alignmentData.normalVector,\n                    directionVector: alignmentData.directionVector\n                  };\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  linePoints.forEach(point => point.add(offsetVector));\n\n                  for (let i = 0; i < linePoints.length - 1; i++) linePtsArr.push([linePoints[i], linePoints[i + 1]]);\n                }\n              });\n            }\n          } else {\n            let rotationAxis = this.utils.initVector3(chainData.shape.rotationAxis);\n            let rotationAngle = chainData.shape.rotationAngle;\n            let centerPoint = this.utils.initVector3(chainData.referencePoints[0]);\n\n            if (canDrawText) {\n              texts.forEach(text => {\n                let textContent = text.content;\n                let textInsertionPoint = text.insertionPoint;\n                let referencePoint = chainData.referencePoints[0];\n\n                if (textContent && textInsertionPoint && this.utils.checkVectorsEquality(textInsertionPoint, referencePoint)) {\n                  let textOffset = text.offset;\n                  let offsetDir = new this.THREE.Vector3().copy(offsetVector);\n                  offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle / 2));\n                  let textPosition = new this.THREE.Vector3().copy(offsetDir);\n                  offsetDir = offsetDir.normalize();\n                  textPosition.add(offsetDir.multiplyScalar(textOffset));\n                  let textData = {\n                    content: text.content,\n                    id: text.id,\n                    insertionPoint: textPosition,\n                    offset: text.offset,\n                    normalVector: alignmentData.normalVector,\n                    directionVector: alignmentData.directionVector\n                  };\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  linePoints.forEach(point => point.add(centerPoint));\n\n                  for (let i = 0; i < linePoints.length - 1; i++) linePtsArr.push([linePoints[i], linePoints[i + 1]]);\n                }\n              });\n            }\n          }\n        } ///\n\n\n        isIntersection = this.intersectionDetection.checkCollision(linePtsArr, meshArr) || this.intersectionDetection.checkCollision(linePtsArr, chainArr);\n        if (isIntersection) offsetVector.add(offsetDistance);\n      } while (isIntersection);\n\n      return offsetVector;\n    }\n\n    generateArcPoints(chainData, alignmentOffset) {\n      const DETAIL = 64;\n      let offsetVector = alignmentOffset.clone();\n      let arcRadius = offsetVector.length();\n      let rotationAngle = chainData.shape.rotationAngle;\n      let rotationAxis = new this.THREE.Vector3(0, 0, 1);\n      let shapeAxis = this.utils.initVector3(chainData.shape.rotationAxis).normalize();\n      let quaternion = new this.THREE.Quaternion().setFromUnitVectors(shapeAxis, rotationAxis);\n      offsetVector.applyQuaternion(quaternion);\n      let offsetVectorNormalized = offsetVector.clone().normalize();\n      let startAngle = -Math.acos(offsetVectorNormalized.x) + this.GraphicsThree3D.degreeToRadians(rotationAngle);\n      let endAngle = -Math.acos(offsetVectorNormalized.x);\n      let isClockwise = rotationAngle > 0;\n      let curve = new this.THREE.EllipseCurve(0, 0, arcRadius, arcRadius, startAngle, endAngle, isClockwise ? true : false, 0);\n      let numOfPoints = DETAIL * rotationAngle / 360;\n      let curvePoints = curve.getPoints(numOfPoints).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n      quaternion.conjugate();\n      curvePoints.forEach(point => {\n        point.applyQuaternion(quaternion);\n        point.add(this.utils.initVector3(chainData.referencePoints[0]));\n      });\n      return curvePoints;\n    }\n\n  }\n\n  _exports.DimensionalChainLoader = DimensionalChainLoader;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/ComplexVolume\", [\"exports\", \"m08-2020/lib old/Utils/BooleanOperations\"], function (_exports, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ComplexVolume = void 0;\n\n  class ComplexVolume {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    addMultilayeredObjectsToScene(multilayeredObjects, wireframes, properties, insertionPoints, jsonData, drawingObject) {\n      let meshArr = [];\n      let positives = [];\n      let negatives = [];\n      multilayeredObjects.positives.forEach(objData => {\n        let [geometry, faceSideNormal, grainDirection, midVector, eulerTwo] = objData;\n        let mesh = new this.THREE.Mesh(geometry);\n        mesh.position.add(midVector);\n        mesh.setRotationFromEuler(eulerTwo);\n        mesh.updateMatrix();\n        positives.push([mesh, faceSideNormal, grainDirection]);\n      });\n      multilayeredObjects.negatives.forEach(objData => {\n        let [geometry, faceSideNormal, grainDirection, midVector, eulerTwo] = objData;\n        let mesh = new this.THREE.Mesh(geometry);\n        mesh.position.add(midVector);\n        mesh.setRotationFromEuler(eulerTwo);\n        mesh.updateMatrix();\n        negatives.push(mesh);\n      });\n      positives.forEach(positive => negatives.forEach(negative => positive[0] = BooleanOperations.doCSG(positive[0], negative, \"subtract\")));\n      positives.forEach(posData => {\n        let [mesh, faceSideNormal, grainDirection] = posData;\n        this.GraphicsThree3D.indexVolumeFaces(mesh.geometry, faceSideNormal, grainDirection);\n        let materials = this.GraphicsThree3D.getVolumeMaterials(properties.volume.color);\n        mesh.material = materials;\n        meshArr.push(mesh);\n      });\n      meshArr.forEach(mesh => this.GraphicsThree3D.DrawingObjectLoader.addToDimChainCollisionDetection(mesh, insertionPoints));\n      meshArr.forEach(mesh => {\n        let transparentMesh = new this.THREE.Mesh();\n        transparentMesh.applyMatrix4(mesh.matrix);\n        this.WireframeGenerator.applyWireframesArea(mesh, transparentMesh, properties.line, drawingObject); // TODO: Change to volume wireframe function\n\n        this.GraphicsThree3D.addObjectToScene(mesh, transparentMesh, insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject);\n      });\n    }\n\n    generateBBoxDepthAndPoints(geometry) {\n      const SIZE_DELTA = 1;\n      geometry.computeBoundingBox();\n      let bbox = geometry.boundingBox;\n      let bboxDepth = (bbox.max.z - bbox.min.z + SIZE_DELTA) * 2;\n      let pts = [new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0), new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0)].map(pt => pt.multiplyScalar(2));\n      return [bboxDepth, pts];\n    }\n\n    makeExtrudedMeshForCSG(points2D, depth) {\n      let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n        points2D,\n        depth,\n        translation: [0, 0, 0],\n        skew: [0, 0]\n      });\n      let mesh = new this.THREE.Mesh(rawGeo);\n      return mesh;\n    }\n\n    drawMultilayeredEllipses(ellipses, properties, multilayeredObjects) {\n      let wireframes = [];\n      ellipses.forEach(ellipse => {\n        console.log(ellipse);\n\n        if (ellipse.objectGeneration.type === 3) {\n          let ellipseMesh = this.GraphicsThree3D.DrawingObjectLoader.Ellipse.drawEllipse(ellipse, properties);\n          let [bboxDepth, pts] = this.generateBBoxDepthAndPoints(ellipseMesh.geometry);\n          let extrusionVector = new this.THREE.Vector3(0, 0, bboxDepth);\n          let centerPoint = this.utils.initVector3(ellipse.centerPoint);\n          ellipseMesh.updateMatrix();\n          pts.forEach(pt => pt.sub(centerPoint));\n\n          if (properties.volume.type === 2) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              let rawMesh = this.makeExtrudedMeshForCSG(pointArr, bboxDepth);\n              let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n              let geometry = modifiedMesh.geometry;\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n            });\n          } else if (properties.volume.type === 3) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n            let grainDirection;\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n              let rawMesh = this.makeExtrudedMeshForCSG(pointArr.points, bboxDepth);\n              let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n              let geometry = modifiedMesh.geometry;\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n            });\n          } else if (properties.volume.type === 4) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n\n            for (let i = 0; i < segments.length; i++) {\n              let segment = segments[i];\n              let layer = properties.volume.layers[i];\n              let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n              if (segment.type) {\n                let layerCount = Math.ceil(extrusionVector.z / layer.width);\n                let layerWidth = bboxDepth / layerCount;\n                let pointArr = segment.points;\n\n                for (let j = 0; j < layerCount; j++) {\n                  let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: layerWidth,\n                    translation: [0, 0, j * layerWidth],\n                    skew: [0, 0]\n                  });\n                  let rawMesh = new this.THREE.Mesh(rawGeo);\n                  let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n                  let geometry = modifiedMesh.geometry;\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n                }\n              } else {\n                let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n                horizontalSegments.forEach(pointArr => {\n                  let rawMesh = this.makeExtrudedMeshForCSG(pointArr, bboxDepth);\n                  let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n                  let geometry = modifiedMesh.geometry;\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n                });\n              }\n            }\n          }\n        } else if (ellipse.objectGeneration.type === 2) {\n          let centerPoint = this.utils.initVector3(ellipse.centerPoint);\n          let extrusionVector = this.utils.initVector3(ellipse.objectGeneration.extrusionVector);\n          let extrusionNormalized = this.utils.initVector3(extrusionVector).normalize();\n          let skewParams = this.utils.getSkewParams(extrusionVector);\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipse);\n          let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n          pts.forEach(point => {\n            point.sub(midVector);\n            point.applyEuler(eulerOne);\n          });\n          extrusionVector.applyEuler(eulerOne);\n\n          if (properties.volume.type === 2) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                points2D: pointArr,\n                depth: extrusionVector.z,\n                translation: [0, 0, 0],\n                skew: skewParams\n              });\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n            });\n          } else if (properties.volume.type === 3) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n            let grainDirection;\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n              let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                points2D: pointArr.points,\n                depth: extrusionVector.z,\n                translation: [0, 0, 0],\n                skew: skewParams\n              });\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n            });\n          } else if (properties.volume.type === 4) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n\n            for (let i = 0; i < segments.length; i++) {\n              let segment = segments[i];\n              let layer = properties.volume.layers[i];\n              let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n              if (segment.type) {\n                let layerCount = Math.ceil(extrusionVector.z / layer.width);\n                let layerWidth = extrusionVector.z / layerCount;\n                let pointArr = segment.points;\n\n                for (let j = 0; j < layerCount; j++) {\n                  let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: layerWidth,\n                    translation: [0, 0, j * layerWidth],\n                    skew: skewParams\n                  });\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n                }\n              } else {\n                let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n                horizontalSegments.forEach(pointArr => {\n                  let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: extrusionVector.z,\n                    translation: [0, 0, 0],\n                    skew: skewParams\n                  });\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n                });\n              }\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    drawMultilayeredPolygons(polygons, properties, multilayeredObjects) {\n      let wireframes = [];\n      polygons.forEach(polygon => {\n        let extrusionVector = this.utils.initVector3(polygon.objectGeneration.extrusionVector);\n        let extrusionNormalized = this.utils.initVector3(extrusionVector).normalize();\n        let skewParams = this.utils.getSkewParams(extrusionVector);\n        let pts = this.GraphicsThree3D.DrawingObjectLoader.Polygon.getPolygonPlanarPoints(polygon);\n        let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n        pts.forEach(point => {\n          point.sub(midVector);\n          point.applyEuler(eulerOne);\n        });\n        extrusionVector.applyEuler(eulerOne);\n\n        if (properties.volume.type === 2) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          let polygonDepth = extrusionVector.z;\n          let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n          segments.forEach(pointArr => {\n            let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr,\n              depth: polygonDepth,\n              translation: [0, 0, translateZ],\n              skew: skewParams\n            });\n            this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n          });\n        } else if (properties.volume.type === 3) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n          let grainDirection;\n          let polygonDepth = extrusionVector.z;\n          let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n            let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr.points,\n              depth: polygonDepth,\n              translation: [0, 0, translateZ],\n              skew: skewParams\n            });\n            this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n          });\n        } else if (properties.volume.type === 4) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n\n          for (let i = 0; i < segments.length; i++) {\n            let segment = segments[i];\n            let layer = properties.volume.layers[i];\n            let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n            if (segment.type) {\n              let layerCount = Math.ceil(extrusionVector.z / layer.width);\n              let layerWidth = extrusionVector.z / layerCount;\n              let polygonDepth = layerWidth;\n              let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : polygonDepth;\n              let pointArr = segment.points;\n\n              for (let j = 0; j < layerCount; j++) {\n                let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: polygonDepth,\n                  translation: [0, 0, j * translateZ],\n                  skew: skewParams\n                });\n                this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n              }\n            } else {\n              let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n              let polygonDepth = extrusionVector.z;\n              let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n              horizontalSegments.forEach(pointArr => {\n                let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: polygonDepth,\n                  translation: [0, 0, translateZ],\n                  skew: skewParams\n                });\n                this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n              });\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    drawMultilayeredEllipsoids(ellipsoids, properties, multilayeredObjects) {\n      const SIZE_DELTA = 1;\n      let wireframes = [];\n      ellipsoids.forEach(ellipsoid => {\n        let ellipsoidMesh = this.GraphicsThree3D.DrawingObjectLoader.Ellipsoid.drawEllipsoid(ellipsoid);\n        ellipsoidMesh.geometry.computeBoundingBox();\n        let bbox = ellipsoidMesh.geometry.boundingBox;\n        let bboxDepth = bbox.max.z - bbox.min.z + SIZE_DELTA;\n        let pts = [new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0), new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0)];\n        let extrusionVector = new this.THREE.Vector3(0, 0, bboxDepth);\n        let centerPoint = this.utils.initVector3(ellipsoid.centerPoint);\n        ellipsoidMesh.position.sub(centerPoint).add(extrusionVector.clone().multiplyScalar(0.5));\n        ellipsoidMesh.updateMatrix();\n        centerPoint.add(extrusionVector.clone().multiplyScalar(0.5));\n\n        if (properties.volume.type === 2) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr,\n              depth: bboxDepth,\n              translation: [0, 0, 0],\n              skew: [0, 0]\n            });\n            let rawMesh = new this.THREE.Mesh(rawGeo);\n            let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n            let geometry = modifiedMesh.geometry;\n            this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n          });\n        } else if (properties.volume.type === 3) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n          let grainDirection;\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n            let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr.points,\n              depth: bboxDepth,\n              translation: [0, 0, 0],\n              skew: [0, 0]\n            });\n            let rawMesh = new this.THREE.Mesh(rawGeo);\n            let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n            let geometry = modifiedMesh.geometry;\n            this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n          });\n        } else if (properties.volume.type === 4) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n\n          for (let i = 0; i < segments.length; i++) {\n            let segment = segments[i];\n            let layer = properties.volume.layers[i];\n            let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n            if (segment.type) {\n              let layerCount = Math.ceil(extrusionVector.z / layer.width);\n              let layerWidth = bboxDepth / layerCount;\n              let pointArr = segment.points;\n\n              for (let j = 0; j < layerCount; j++) {\n                let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: layerWidth,\n                  translation: [0, 0, j * layerWidth],\n                  skew: [0, 0]\n                });\n                let rawMesh = new this.THREE.Mesh(rawGeo);\n                let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n                let geometry = modifiedMesh.geometry;\n                this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n              }\n            } else {\n              let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n              horizontalSegments.forEach(pointArr => {\n                let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: bboxDepth,\n                  translation: [0, 0, 0],\n                  skew: [0, 0]\n                });\n                let rawMesh = new this.THREE.Mesh(rawGeo);\n                let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n                let geometry = modifiedMesh.geometry;\n                this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n              });\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    addToComplexVolumeData(objectData, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]) {\n      if (objectData.objectType === 1) multilayeredObjects.positives.push([geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);else multilayeredObjects.negatives.push([geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n    }\n\n    segmentPointsVertical(points, volumeProps) {\n      let PolyBool = this.GraphicsThree3D.PolyBool;\n      let ptsPolygon = {\n        regions: [[]],\n        inverted: false\n      };\n      let maxX = 0,\n          maxY = 0;\n      let minX = Infinity,\n          minY = Infinity;\n      points.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        ptsPolygon.regions[0].push([point.x, point.y]);\n      });\n      let segments = [];\n\n      if (volumeProps.type == 2) {\n        let layerThickness = volumeProps.layers[0].thickness;\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n\n        while (minY < maxY) {\n          let eraserRegion = [];\n          eraserRegion.push([minX, minY]);\n          eraserRegion.push([maxX, minY]);\n          eraserRegion.push([maxX, minY + layerThickness]);\n          eraserRegion.push([minX, minY + layerThickness]);\n          eraserPolygon.regions.push(eraserRegion);\n          minY += 2 * layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach(region => {\n          let pointArr = [];\n          region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach(region => {\n          let pointArr = [];\n          region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      } else if (volumeProps.type == 3) {\n        let objectHeight = maxY - minY;\n        let layerThickness = objectHeight / Math.ceil(objectHeight / volumeProps.layers[0].thickness);\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n\n        while (minY < maxY) {\n          let eraserRegion = [];\n          eraserRegion.push([minX, minY]);\n          eraserRegion.push([maxX, minY]);\n          eraserRegion.push([maxX, minY + layerThickness]);\n          eraserRegion.push([minX, minY + layerThickness]);\n          eraserPolygon.regions.push(eraserRegion);\n          minY += 2 * layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach(region => {\n          let pointArr = {\n            points: [],\n            type: 0\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach(region => {\n          let pointArr = {\n            points: [],\n            type: 1\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      } else {\n        let layers = volumeProps.layers;\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n        let layersHeight = layers.map(layer => layer = layer.thickness).reduce((a, b) => a + b, 0);\n        let objectHeight = maxY - minY;\n\n        if (layersHeight != objectHeight) {\n          let proportion = objectHeight / layersHeight;\n          layers.forEach(layer => layer.thickness *= proportion);\n        }\n\n        let layerPropsEven = [];\n        let layerPropsOdd = [];\n\n        for (let i = 0; i < layers.length; i++) {\n          let layerThickness = layers[i].thickness;\n\n          if (i % 2 == 0) {\n            let eraserRegion = [];\n            eraserRegion.push([minX, minY]);\n            eraserRegion.push([maxX, minY]);\n            eraserRegion.push([maxX, minY + layerThickness]);\n            eraserRegion.push([minX, minY + layerThickness]);\n            eraserPolygon.regions.push(eraserRegion);\n            layerPropsEven.push(layers[i]);\n          } else {\n            layerPropsOdd.push(layers[i]);\n          }\n\n          minY += layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach((region, index) => {\n          let pointArr = {\n            points: [],\n            type: 0,\n            layerProps: layerPropsOdd[index]\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach((region, index) => {\n          let pointArr = {\n            points: [],\n            type: 1,\n            layerProps: layerPropsEven[index]\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      }\n\n      return segments;\n    }\n\n    segmentPointsHorizontal(points, layerProps) {\n      let PolyBool = this.GraphicsThree3D.PolyBool;\n      let segments = [];\n      let ptsPolygon = {\n        regions: [[]],\n        inverted: false\n      };\n      let maxX = 0,\n          maxY = 0;\n      let minX = Infinity,\n          minY = Infinity;\n      points.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        ptsPolygon.regions[0].push([point.x, point.y]);\n      });\n      let eraserPolygon = {\n        regions: [],\n        inverted: false\n      };\n      let objectWidth = maxX - minX;\n      let segmentWidth = layerProps.width;\n      let segmentCount = Math.ceil(objectWidth / segmentWidth);\n      segmentWidth = objectWidth / segmentCount;\n\n      while (minX < maxX) {\n        let eraserRegion = [];\n        eraserRegion.push([minX, maxY]);\n        eraserRegion.push([minX, minY]);\n        eraserRegion.push([minX + segmentWidth, minY]);\n        eraserRegion.push([minX + segmentWidth, maxY]);\n        eraserPolygon.regions.push(eraserRegion);\n        minX += 2 * segmentWidth;\n      }\n\n      let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n      let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n      polygonOne.regions.forEach(region => {\n        let pointArr = [];\n        region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n        segments.push(pointArr);\n      });\n      polygonTwo.regions.forEach(region => {\n        let pointArr = [];\n        region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n        segments.push(pointArr);\n      });\n      return segments;\n    }\n\n  }\n\n  _exports.ComplexVolume = ComplexVolume;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipse\", [\"exports\", \"m08-2020/lib old/ThreeExtensions/RotatedGeometry\", \"m08-2020/lib old/ThreeExtensions/PlanarRotationPointsGenerator\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipsoid\"], function (_exports, _RotatedGeometry, _PlanarRotationPointsGenerator, _Ellipsoid) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Ellipse = void 0;\n\n  class Ellipse {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n      this.Ellipsoid = new _Ellipsoid.Ellipsoid(GraphicsThree3D);\n    }\n\n    drawEllipse(ellipseData) {\n      let ellipseType = ellipseData.objectGeneration.type;\n      let ellipseMesh;\n\n      if (ellipseType === 1 || ellipseType === 3 && !ellipseData.objectGeneration.rotationAngle) {\n        ellipseMesh = this.drawEllipsePlanar(ellipseData);\n        ellipseMesh.generationType = \"area\";\n      } else if (ellipseType === 2) {\n        if (this.GraphicsThree3D.objectExtrudesPlanar(ellipseData, true)) {\n          ellipseMesh = this.makeEllipseExtrudedArea(ellipseData);\n          ellipseMesh.generationType = \"area\";\n        } else {\n          ellipseMesh = this.makeEllipseExtrudedVolume(ellipseData);\n          ellipseMesh.generationType = \"volume\";\n        }\n      } else if (ellipseType === 3) {\n        if (this.GraphicsThree3D.objectRotatesPlanar(ellipseData, true)) {\n          ellipseMesh = this.makeEllipseRotatedArea(ellipseData);\n          ellipseMesh.generationType = \"area\";\n        } else {\n          ellipseMesh = this.makeEllipseRotatedVolume(ellipseData);\n          ellipseMesh.generationType = \"volume\";\n        }\n      }\n\n      return ellipseMesh;\n    }\n\n    drawEllipsePlanar(ellipseData) {\n      let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n      let extrusionDepth = 0.1;\n      let extrusionVector = new this.THREE.Vector3(0, 0, extrusionDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        // this.GraphicsThree3D.wireframePoints.push(point.clone().add(centerPoint));\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let ellipseShape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: extrusionDepth,\n        bevelEnabled: false\n      };\n      let ellipseGeo = new this.THREE.ExtrudeGeometry(ellipseShape, extrudeSettings); // ellipseGeo.translate(0, 0, -extrusionVector.z);\n\n      let ellipse = new this.THREE.Mesh(ellipseGeo);\n      ellipse.position.add(midVector);\n      ellipse.updateMatrix();\n      ellipse.setRotationFromEuler(eulerTwo);\n      ellipse.position.add(centerPoint);\n      return ellipse;\n    }\n\n    makeEllipseExtrudedArea(ellipseData) {\n      let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n      let extrusionVector = new this.THREE.Vector3(ellipseData.objectGeneration.extrusionVector.x, ellipseData.objectGeneration.extrusionVector.y, 0);\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData); // TODO: Refactor this to be more readable end efficient\n\n      if (ellipseData.segmentVector1) {\n        if (!ellipseData.segmentHeight) {\n          curvePoints.splice(2, 0, curvePoints[1].clone());\n          curvePoints.splice(curvePoints.length - 1, 0, curvePoints[curvePoints.length - 1].clone());\n\n          for (let i = 2; i < curvePoints.length - 1; i++) {\n            curvePoints[i].add(extrusionVector);\n          }\n        } else {\n          let extrusionDir = extrusionVector.clone().normalize();\n          let midDir = curvePoints[Math.floor(curvePoints.length / 2)].clone().normalize(); ///\n\n          if (midDir.dot(extrusionDir) < 0) {\n            curvePoints.splice(2, 0, curvePoints[1].clone());\n            curvePoints.splice(curvePoints.length - 1, 0, curvePoints[curvePoints.length - 2].clone());\n\n            for (let i = 0; i < 2; i++) {\n              curvePoints[i].add(extrusionVector);\n            }\n\n            for (let i = curvePoints.length - 2; i < curvePoints.length; i++) {\n              curvePoints[i].add(extrusionVector);\n            }\n          } else {\n            curvePoints.splice(2, 0, curvePoints[1].clone());\n            curvePoints.splice(curvePoints.length - 1, 0, curvePoints[curvePoints.length - 2].clone());\n\n            for (let i = 2; i < curvePoints.length - 2; i++) {\n              curvePoints[i].add(extrusionVector);\n            }\n          }\n        }\n      } else {\n        curvePoints.push(curvePoints[0].clone());\n        let pointsNum = curvePoints.length;\n        let startIndex = (Math.PI / 2 + extrusionVector.angleTo(new this.THREE.Vector3(1, 0, 0))) / (2 * Math.PI) * pointsNum;\n        let endIndex = (3 * Math.PI / 2 + extrusionVector.angleTo(new this.THREE.Vector3(1, 0, 0))) / (2 * Math.PI) * pointsNum;\n\n        if (endIndex >= pointsNum) {\n          endIndex -= pointsNum;\n        }\n\n        startIndex = Math.floor(startIndex);\n        endIndex = Math.floor(endIndex);\n\n        if (startIndex < endIndex) {\n          for (let i = 0; i < startIndex; i++) {\n            curvePoints[i].add(extrusionVector);\n          }\n\n          for (let i = endIndex; i < pointsNum; i++) {\n            curvePoints[i].add(extrusionVector);\n          }\n        } else {\n          for (let i = endIndex; i < startIndex; i++) {\n            curvePoints[i].add(extrusionVector);\n          }\n        }\n      }\n\n      let curveShape = new this.THREE.Shape(curvePoints);\n      let curveGeo = new this.THREE.ExtrudeBufferGeometry(curveShape, {\n        depth: -0.01,\n        bevelEnabled: false\n      });\n      let ellipse = new this.THREE.Mesh(curveGeo);\n      ellipse.position.add(centerPoint);\n      return ellipse;\n    }\n\n    makeEllipseExtrudedVolume(ellipseData) {\n      let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n      let extrusionVector = ellipseData.objectGeneration.extrusionVector;\n      let extrusionNormalized = this.utils.initVector3(extrusionVector).normalize();\n      let skewX = extrusionNormalized.x / extrusionNormalized.z;\n      let skewY = extrusionNormalized.y / extrusionNormalized.z;\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData); // this.WireframeGenerator.getPlanarPoints(curvePoints.map(point => point.clone().add(centerPoint)), ellipseData.objectType);\n\n      let curveShape = new this.THREE.Shape(curvePoints);\n      let curveGeo = new this.THREE.ExtrudeGeometry(curveShape, {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      }); // curveGeo.translate(0, 0, extrusionVector.z);\n\n      curveGeo.applyMatrix4(new this.THREE.Matrix4().set(1, 0, skewX, 0, 0, 1, skewY, 0, 0, 0, 1, 0, 0, 0, 0, 1));\n      let ellipse = new this.THREE.Mesh(curveGeo);\n      ellipse.position.copy(centerPoint);\n      ellipse.updateMatrix(); ///\n\n      let linePairs = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(ellipse, Math.PI / 720).forEach(pair => linePairs.push([pair[0].clone().applyMatrix4(ellipse.matrix), pair[1].clone().applyMatrix4(ellipse.matrix)]));\n      let linePairsForOpaque = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(ellipse, 20 * Math.PI / 180).forEach(pair => linePairsForOpaque.push([pair[0].clone().applyMatrix4(ellipse.matrix), pair[1].clone().applyMatrix4(ellipse.matrix)]));\n      this.WireframeGenerator.getVolumeWireframeData(ellipse, linePairsForOpaque, linePairs, ellipseData.objectType); ///\n\n      return ellipse;\n    }\n\n    makeEllipseRotatedArea(ellipseData) {\n      const DELTA = 0.0001;\n      let centerPoint = ellipseData.centerPoint;\n      let rotationPoint = ellipseData.objectGeneration.rotationPoint;\n      let rotationAngle = ellipseData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = ellipseData.objectGeneration.rotationPitch;\n      let rotationAxis = this.utils.initVector3(ellipseData.objectGeneration.rotationAxis);\n      let centerVector = this.utils.initVector3(centerPoint);\n      let rotationVector = this.utils.initVector3(rotationPoint).sub(centerVector);\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData).map(point => point.sub(rotationVector));\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let [_, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(curvePoints, zAxis);\n      if (curvePoints[0].distanceTo(curvePoints[curvePoints.length - 1]) < DELTA) curvePoints.pop();\n      curvePoints.forEach(point => point.applyEuler(eulerOne));\n      if (rotationAxis.applyEuler(eulerOne).dot(zAxis) < 0) rotationAngle *= -1;\n      curvePoints = new _PlanarRotationPointsGenerator.PlanarRotationPointsGenerator(curvePoints, {\n        rotationAngle,\n        rotationPitch\n      }).rotatedPoints;\n      let ellipse = this.drawExtrudedEllipseMesh(curvePoints, -0.01);\n      ellipse.setRotationFromEuler(eulerTwo);\n      ellipse.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n      return ellipse;\n    }\n\n    drawExtrudedEllipseMesh(shapePts, extrDepth) {\n      let curveShape = new this.THREE.Shape(shapePts);\n      let curveGeo = new this.THREE.ExtrudeGeometry(curveShape, {\n        depth: extrDepth,\n        bevelEnabled: false\n      });\n      return new this.THREE.Mesh(curveGeo);\n    }\n\n    makeEllipseRotatedVolume(ellipseData) {\n      let ellipse;\n\n      if (this.rotationAxisDefined(ellipseData.objectGeneration.rotationAxis)) {\n        let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n        let rotationPoint = this.utils.initVector3(ellipseData.objectGeneration.rotationPoint);\n        let rotationAxis = this.utils.initVector3(ellipseData.objectGeneration.rotationAxis).normalize();\n        let rotationAngle = ellipseData.objectGeneration.rotationAngle * Math.PI / 180;\n        let rotationPitch = ellipseData.objectGeneration.rotationPitch;\n        let rotationRadius = centerPoint.distanceTo(rotationPoint);\n\n        if (this.generatesEllipsoid(rotationRadius, rotationAngle, rotationPitch)) {\n          let ellipsoidData = this.generateEllipsoidData(ellipseData, rotationAxis);\n          ellipse = this.Ellipsoid.drawEllipsoid(ellipsoidData);\n        } else {\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData).map(pt => pt.add(centerPoint));\n          let rotationParams = {\n            rotationPoint: this.utils.initVector3(ellipseData.objectGeneration.rotationPoint),\n            rotationAxis: this.utils.initVector3(ellipseData.objectGeneration.rotationAxis).normalize(),\n            rotationAngle: ellipseData.objectGeneration.rotationAngle * Math.PI / 180,\n            rotationPitch: ellipseData.objectGeneration.rotationPitch\n          };\n          let rotatedGeometry = new _RotatedGeometry.RotatedGeometry(pts.map(pt => pt.clone()), rotationParams);\n          ellipse = new this.THREE.Mesh(rotatedGeometry);\n          ellipse.updateMatrix();\n          this.addToWireframePts3D(ellipse, ellipseData, pts, rotationParams);\n        }\n      } else ellipse = new this.THREE.Mesh();\n\n      return ellipse;\n    }\n\n    addToWireframePts3D(ellipseMesh, ellipseData, ellipsePts, rotationParams) {\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationParams;\n      let wireframePts = [...ellipsePts, ellipsePts[0]];\n      let linePairs = this.WireframeGenerator.getRegularWireframePointPairs(ellipseMesh, Math.PI / 180).map(pair => [pair[0].clone().applyMatrix4(ellipseMesh.matrix), pair[1].clone().applyMatrix4(ellipseMesh.matrix)]);\n      let linePairsForOpaque = [];\n      let pitchVec = rotationAxis.clone().multiplyScalar(rotationPitch * Math.abs(rotationAngle) / (2 * Math.PI));\n\n      for (let i = 0, l = wireframePts.length - 1; i < l; i++) {\n        linePairsForOpaque.push([wireframePts[i], wireframePts[i + 1]]);\n        let rotatedPoints = [wireframePts[i], wireframePts[i + 1]].map(pt => pt.clone().sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint).add(pitchVec));\n        linePairsForOpaque.push(rotatedPoints);\n      }\n\n      if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n        let ptsToRotate = [];\n        ptsToRotate.push(ellipsePts[ellipsePts.length - 1], ellipsePts[ellipsePts.length - 2]);\n        ptsToRotate.push(ellipsePts[0]);\n        if (ellipseData.segmentHeight) ptsToRotate.push(ellipsePts[1]);\n        const DETAIL = 64;\n        let numOfSegments = DETAIL * Math.abs(rotationAngle) / (2 * Math.PI);\n        let pitchVector = new this.THREE.Vector3();\n\n        for (let point of ptsToRotate) {\n          let sidePoints = [];\n\n          for (let i = 0; i < numOfSegments; i++) {\n            let rotatedPoint = new this.THREE.Vector3().copy(point).sub(rotationPoint).applyAxisAngle(rotationAxis, (i + 1) * rotationAngle / numOfSegments).add(rotationPoint).add(pitchVector.copy(rotationAxis).multiplyScalar((i + 1) * rotationPitch / numOfSegments / 2));\n            sidePoints.push(rotatedPoint);\n          }\n\n          linePairsForOpaque.push([point, sidePoints[0]]);\n\n          for (let j = 0; j < sidePoints.length - 1; j++) linePairsForOpaque.push([sidePoints[j], sidePoints[j + 1]]);\n        }\n      }\n\n      this.WireframeGenerator.getVolumeWireframeData(ellipseMesh, linePairsForOpaque, linePairs, ellipseData.objectType);\n    }\n\n    rotationAxisDefined(rotationAxis) {\n      return rotationAxis && (rotationAxis.x || rotationAxis.y || rotationAxis.z);\n    }\n\n    generatesEllipsoid(rotationRadius, rotationAngle, rotationPitch) {\n      return rotationRadius <= 0.01 && !rotationPitch && rotationAngle >= Math.PI;\n    }\n\n    getProjectedVectorLengthSquared(initialVec, targetVec, targetLen) {\n      let projectedVectorLengthSquared = initialVec.clone().projectOnVector(targetVec).multiplyScalar(targetLen).lengthSq();\n      return projectedVectorLengthSquared;\n    }\n\n    generateEllipsoidData(ellipseData, rotationAxis) {\n      let dir1 = this.utils.initVector3(ellipseData.directionVector1),\n          dir2 = this.utils.initVector3(ellipseData.directionVector2);\n      let dir3 = new this.THREE.Vector3().crossVectors(dir1, dir2).normalize();\n      let projOneLenSq = this.getProjectedVectorLengthSquared(rotationAxis, dir1, ellipseData.radius1);\n      let projTwoLenSq = this.getProjectedVectorLengthSquared(rotationAxis, dir2, ellipseData.radius2);\n      let radius3 = Math.sqrt(projOneLenSq + projTwoLenSq);\n      let ellipsoidData = { ...ellipseData,\n        radius3,\n        directionVector3: dir3\n      };\n      return ellipsoidData;\n    }\n\n  }\n\n  _exports.Ellipse = Ellipse;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipsoid\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Ellipsoid = void 0;\n\n  class Ellipsoid {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawEllipsoid(ellipsoidData) {\n      console.log(ellipsoidData);\n      let centerPoint = this.utils.initVector3(ellipsoidData.centerPoint);\n      let directionVectorOne = this.utils.initVector3(ellipsoidData.directionVector1).normalize();\n      let directionVectorTwo = this.utils.initVector3(ellipsoidData.directionVector2).normalize();\n      let directionVectorThree = this.utils.initVector3(ellipsoidData.directionVector3).normalize();\n      let radiusOne = ellipsoidData.radius1;\n      let radiusTwo = ellipsoidData.radius2;\n      let radiusThree = ellipsoidData.radius3;\n      let segmentHeight = ellipsoidData.segmentHeight;\n      let ellipsoidMesh = this.makeEllipsoidMesh(radiusOne, radiusTwo, radiusThree);\n\n      if (this.segmentVectorsAreValid(ellipsoidData.segmentVector1, ellipsoidData.segmentVector2)) {\n        let segmentVectorOne = this.utils.initVector3(ellipsoidData.segmentVector1).normalize();\n        let segmentVectorTwo = this.utils.initVector3(ellipsoidData.segmentVector2).normalize();\n        let segmentsCross = this.utils.getCrossVector(segmentVectorOne, segmentVectorTwo);\n        let directionsCross = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n        let segmentsCrossLength = segmentsCross.length();\n        let crossVector = segmentsCrossLength !== 0 ? segmentsCross : directionsCross;\n        let isOnPlane = false;\n\n        for (let vector of [directionVectorOne, directionVectorTwo, directionVectorThree]) {\n          if (Math.abs(vector.dot(crossVector)) === 1) isOnPlane = true;\n          if (vector.dot(crossVector) === -1) crossVector.negate();\n        }\n\n        if (isOnPlane) {\n          let segmentQuaternion = new this.THREE.Quaternion().setFromUnitVectors(crossVector, new this.THREE.Vector3(0, 0, 1));\n          segmentVectorOne.applyQuaternion(segmentQuaternion);\n          segmentVectorTwo.applyQuaternion(segmentQuaternion);\n          let maxRadius = Math.max(radiusOne, radiusTwo, radiusThree) + 1;\n          let boxSize = 2 * maxRadius;\n          let pointsData = {\n            centerPoint: {\n              x: ellipsoidData.centerPoint.x,\n              y: ellipsoidData.centerPoint.y,\n              z: ellipsoidData.centerPoint.z\n            },\n            directionVector1: {\n              x: ellipsoidData.directionVector1.x,\n              y: ellipsoidData.directionVector1.y,\n              z: ellipsoidData.directionVector1.z\n            },\n            directionVector2: {\n              x: ellipsoidData.directionVector2.x,\n              y: ellipsoidData.directionVector2.y,\n              z: ellipsoidData.directionVector2.z\n            },\n            radius1: maxRadius,\n            radius2: maxRadius,\n            segmentHeight: 0,\n            segmentVector1: {\n              x: segmentVectorOne.x,\n              y: segmentVectorOne.y,\n              z: segmentVectorOne.z\n            },\n            segmentVector2: {\n              x: segmentVectorTwo.x,\n              y: segmentVectorTwo.y,\n              z: segmentVectorTwo.z\n            }\n          };\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(pointsData);\n          if (segmentsCrossLength === 0) pts.pop();\n          let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, new this.THREE.Vector3(0, 0, 1));\n          pts.forEach(point => {\n            point.sub(midVector);\n            point.applyEuler(eulerOne);\n            point.add(midVector);\n          });\n          let eraserPoints = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n          let eraserShape = new this.THREE.Shape(eraserPoints);\n          let eraserGeometry = new this.THREE.ExtrudeGeometry(eraserShape, {\n            steps: 1,\n            bevelEnabled: false,\n            depth: boxSize\n          });\n          eraserGeometry.translate(0, 0, -boxSize / 2);\n          let eraserMesh = new this.THREE.Mesh(eraserGeometry);\n          eraserMesh.lookAt(crossVector);\n          eraserMesh.updateMatrix();\n          let ellipsoidCSG = this.CSG.fromMesh(ellipsoidMesh);\n          let eraserCSG = this.CSG.fromMesh(eraserMesh);\n          ellipsoidCSG = ellipsoidCSG.intersect(eraserCSG);\n\n          if (segmentHeight) {\n            let [segOne, segTwo] = this.getEllipsoidSegments(ellipsoidCSG, segmentVectorOne, segmentVectorTwo);\n            let midVector = segmentsCrossLength !== 0 ? new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5).normalize() : new this.THREE.Vector3().crossVectors(segmentVectorOne, crossVector);\n            let maxDistance = 0,\n                midPoint = new this.THREE.Vector3();\n            ellipsoidCSG.polygons.forEach(polygon => polygon.vertices.forEach(vertex => {\n              let localVertex = new this.THREE.Vector3(vertex.pos.x, vertex.pos.y, vertex.pos.z);\n              let dir = localVertex.clone().normalize().dot(midVector);\n              let distance = midVector.clone().dot(localVertex);\n\n              if (dir > 0.99) {\n                if (distance > maxDistance) {\n                  maxDistance = distance;\n                  midPoint.copy(localVertex);\n                }\n              } else if (dir < -0.99) {\n                if (-distance > maxDistance) {\n                  maxDistance = -distance;\n                  midPoint.copy(localVertex.clone().negate());\n                }\n              }\n            }));\n            let midRadius = midPoint.length();\n\n            if (segmentsCrossLength === 0) {\n              let heightDelta = midRadius - segmentHeight;\n              let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n              eraserBox.lookAt(midVector.clone().normalize());\n              eraserBox.position.add(new this.THREE.Vector3().copy(midVector).normalize().multiplyScalar(boxSize / 2 - heightDelta));\n              eraserBox.updateMatrix();\n              let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n              ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n            } else {\n              if (pts.length > 32) {\n                let maxHeight = midRadius + new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5).length();\n\n                if (segmentHeight <= maxHeight) {\n                  if (segmentHeight > midRadius) {\n                    let heightDelta = segmentHeight - midRadius;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    let upVector = this.getMeshUpVector(eraserBox, dir);\n                    let delta = this.getDifferenceUpAndMid(upVector, new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5));\n                    eraserBox.position.add(new this.THREE.Vector3().copy(upVector).multiplyScalar(heightDelta + boxSize / 2 - delta));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  } else {\n                    let heightDelta = midRadius - segmentHeight;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    let upVector = this.getMeshUpVector(eraserBox, dir);\n                    let delta = this.getDifferenceUpAndMid(upVector, new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5));\n                    eraserBox.position.add(new this.THREE.Vector3().copy(upVector).multiplyScalar(boxSize / 2 - heightDelta + delta));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  }\n                }\n              } else {\n                let maxHeight = midRadius;\n\n                if (segmentHeight <= maxHeight) {\n                  let midNotNormalized = new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5);\n\n                  if (midRadius - midNotNormalized.length() > segmentHeight) {\n                    let heightDelta = midRadius - segmentHeight;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    eraserBox.lookAt(dir);\n                    eraserBox.position.add(new this.THREE.Vector3().copy(midVector).multiplyScalar(heightDelta - boxSize / 2));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  } else {\n                    let midPlane = new this.THREE.Plane(midVector);\n                    let differenceOne = Math.abs(midPlane.distanceToPoint(midPoint)) - Math.abs(midPlane.distanceToPoint(segOne));\n                    let differenceTwo = Math.abs(midPlane.distanceToPoint(midPoint)) - Math.abs(midPlane.distanceToPoint(segTwo));\n                    let segmentOne = Math.abs(segmentHeight - differenceOne);\n                    let segmentTwo = Math.abs(segmentHeight - differenceTwo);\n                    let proportionOne = segmentOne / midRadius;\n                    let proportionTwo = segmentTwo / midRadius;\n                    let proportion = Math.max(proportionOne, proportionTwo);\n                    let vecOne = segOne.clone().normalize().multiplyScalar(segOne.length() * proportion);\n                    let vecTwo = segTwo.clone().normalize().multiplyScalar(segTwo.length() * proportion);\n                    vecOne = new this.THREE.Vector2(vecOne.x, vecOne.y);\n                    vecTwo = new this.THREE.Vector2(vecTwo.x, vecTwo.y);\n                    let vecMid = new this.THREE.Vector2(0, 0);\n                    let heightEraserPoints = [vecMid, vecOne, vecTwo];\n                    let heightEraserShape = new this.THREE.Shape(heightEraserPoints);\n                    let heightEraserGeo = new this.THREE.ExtrudeGeometry(heightEraserShape, {\n                      steps: 1,\n                      bevelEnabled: false,\n                      depth: boxSize\n                    });\n                    heightEraserGeo.translate(0, 0, -boxSize / 2);\n                    let heightEraserMesh = new this.THREE.Mesh(heightEraserGeo);\n                    let heightEraserCSG = this.CSG.fromMesh(heightEraserMesh);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  }\n                }\n              }\n            }\n          }\n\n          ellipsoidMesh = this.CSG.toMesh(ellipsoidCSG, new this.THREE.Matrix4());\n        }\n      }\n\n      this.GraphicsThree3D.alignObjectWithVectors(ellipsoidMesh, centerPoint, directionVectorThree, directionVectorOne);\n      ellipsoidMesh.updateMatrix();\n      ellipsoidMesh.generationType = \"volume\"; ///\n\n      let linePairs = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(ellipsoidMesh, Math.PI / 720).forEach(pair => linePairs.push([pair[0].clone().applyMatrix4(ellipsoidMesh.matrix), pair[1].clone().applyMatrix4(ellipsoidMesh.matrix)]));\n      let linePairsForOpaque = [];\n\n      if (this.segmentVectorsAreValid(ellipsoidData.segmentVector1, ellipsoidData.segmentVector2)) {\n        this.WireframeGenerator.getRegularWireframePointPairs(ellipsoidMesh, 20 * Math.PI / 180).forEach(pair => linePairsForOpaque.push([pair[0].clone().applyMatrix4(ellipsoidMesh.matrix), pair[1].clone().applyMatrix4(ellipsoidMesh.matrix)]));\n      }\n\n      this.WireframeGenerator.getVolumeWireframeData(ellipsoidMesh, linePairsForOpaque, linePairs, ellipsoidData.objectType); ///\n\n      return ellipsoidMesh;\n    }\n\n    getEllipsoidSegments(ellipsoidCSG, segmentVectorOne, segmentVectorTwo) {\n      let segOne = new this.THREE.Vector3();\n      let segTwo = new this.THREE.Vector3();\n      let maxDistanceOne = 0,\n          maxDistanceTwo = 0;\n      ellipsoidCSG.polygons.forEach(polygon => polygon.vertices.forEach(vertex => {\n        let localVertex = new this.THREE.Vector3(vertex.pos.x, vertex.pos.y, vertex.pos.z);\n        let dirOne = localVertex.clone().normalize().dot(segmentVectorOne.clone().normalize());\n        let distanceOne = segmentVectorOne.clone().dot(localVertex);\n\n        if (dirOne > 0.9999) {\n          if (distanceOne > maxDistanceOne) {\n            maxDistanceOne = distanceOne;\n            segOne.copy(localVertex);\n          }\n        }\n\n        let dirTwo = localVertex.clone().normalize().dot(segmentVectorTwo.clone().normalize());\n        let distanceTwo = segmentVectorTwo.clone().dot(localVertex);\n\n        if (dirTwo > 0.9999) {\n          if (distanceTwo > maxDistanceTwo) {\n            maxDistanceTwo = distanceTwo;\n            segTwo.copy(localVertex);\n          }\n        }\n      }));\n      return [segOne, segTwo];\n    }\n\n    getMeshUpVector(mesh, direction) {\n      let upObject = new this.THREE.Object3D();\n      upObject.position.copy(mesh.up);\n      mesh.add(upObject);\n      mesh.lookAt(direction);\n      mesh.updateMatrixWorld();\n      let upVector = upObject.getWorldPosition(new this.THREE.Vector3());\n      return upVector;\n    }\n\n    getDifferenceUpAndMid(up, mid) {\n      let midLength = mid.length();\n      let upLength = midLength * Math.cos(up.angleTo(mid));\n      let delta = midLength - upLength;\n      return delta;\n    }\n\n    makeEllipsoidMesh(radiusOne, radiusTwo, radiusThree) {\n      const DETAIL = 32;\n      let ellipsoidGeo = new this.THREE.SphereGeometry(radiusOne, DETAIL, DETAIL).applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radiusOne, radiusThree / radiusOne));\n      let ellipsoidMesh = new this.THREE.Mesh(ellipsoidGeo);\n      return ellipsoidMesh;\n    }\n\n    segmentVectorsAreValid(segmentVector1, segmentVector2) {\n      return segmentVector1 && segmentVector2 && (segmentVector1.x || segmentVector1.y || segmentVector1.z) && (segmentVector2.x || segmentVector2.y || segmentVector2.z);\n    }\n\n  }\n\n  _exports.Ellipsoid = Ellipsoid;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Point\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Point = void 0;\n\n  class Point {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n    }\n\n    drawPoint(pointData, materialProps) {\n      let pointProps = materialProps.point;\n      let lineProps = materialProps.line;\n      let pointType = pointData.objectGeneration.type;\n      let pointMesh;\n\n      if (pointType == 1) {\n        pointMesh = this.drawPointPlanar(pointData, pointProps);\n        pointMesh.generationType = \"point\";\n      } else if (pointType == 2) {\n        pointMesh = this.drawPointExtruded(pointData, lineProps);\n        pointMesh.generationType = \"line\";\n      } else {\n        pointMesh = this.drawPointRotated(pointData, lineProps);\n        pointMesh.generationType = \"line\";\n      }\n\n      return pointMesh;\n    }\n\n    drawPointPlanar(pointData, pointProps) {\n      let insertionPoint = pointData.insertionPoint;\n      let pointRadius = pointProps.size / 2;\n      let pointColor = pointProps.color;\n      let pointGeo = new this.THREE.SphereBufferGeometry(pointRadius, 32, 16);\n      let pointMaterial = this.utils.getMaterialFromColorProps(pointColor);\n      let pointMesh = new this.THREE.Mesh(pointGeo, pointMaterial);\n      pointMesh.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n      return pointMesh;\n    }\n\n    drawPointExtruded(pointData, lineProps) {\n      let insertionPoint = pointData.insertionPoint;\n      let extrusionVector = pointData.objectGeneration.extrusionVector;\n      let [pointOne, pointTwo] = this.getExtrudedPointPoints(insertionPoint, extrusionVector);\n      let lineMesh = this.utils.drawConnectedLines([pointOne, pointTwo], lineProps);\n      return lineMesh;\n    }\n\n    getExtrudedPointPoints(insertionPoint, extrusionVector) {\n      let extrusion = this.utils.initVector3(extrusionVector);\n      let pointOne = this.utils.initVector3(insertionPoint);\n      let pointTwo = pointOne.clone().add(extrusion);\n      return [pointOne, pointTwo];\n    }\n\n    drawPointRotated(pointData, lineProps) {\n      let circlePoints = this.getRotatedPoints(pointData);\n      let lineMesh = this.utils.drawConnectedLines(circlePoints, lineProps);\n      return lineMesh;\n    }\n\n    getRotatedPoints(pointData) {\n      let insertionPoint = pointData.insertionPoint;\n      let rotationPoint = pointData.objectGeneration.rotationPoint;\n      let rotationAxis = this.utils.initVector3(pointData.objectGeneration.rotationAxis);\n      let rotationAngle = pointData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = pointData.objectGeneration.rotationPitch;\n      let pivotPoint = new this.THREE.Object3D();\n      pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n      let positionPoint = new this.THREE.Object3D();\n      positionPoint.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n      pivotPoint.attach(positionPoint);\n      let startPoint = this.utils.initVector3(insertionPoint);\n      let circlePoints = [startPoint];\n      let positionVector = new this.THREE.Vector3(); // dummy vector for obtaining results\n\n      let numOfSegments = 64 * rotationAngle / (Math.PI * 2);\n\n      for (let i = 0; i < numOfSegments; i++) {\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle / numOfSegments);\n        pivotPoint.updateMatrixWorld();\n        let newPoint = new this.THREE.Vector3().add(positionPoint.getWorldPosition(positionVector));\n        newPoint.add(rotationAxis.clone().normalize().multiplyScalar((i + 1) * rotationPitch / numOfSegments));\n        circlePoints.push(newPoint);\n      }\n\n      return circlePoints;\n    }\n\n  }\n\n  _exports.Point = Point;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polygon\", [\"exports\", \"m08-2020/lib old/ThreeExtensions/RotatedGeometry\", \"m08-2020/lib old/ThreeExtensions/PlanarRotationPointsGenerator\", \"m08-2020/lib old/ThreeExtensions/PlanarExtrusionPointsGenerator\"], function (_exports, _RotatedGeometry, _PlanarRotationPointsGenerator, _PlanarExtrusionPointsGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Polygon = void 0;\n\n  class Polygon {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawPolygon(polygonData) {\n      let polygon;\n      let isPlanar = this.polygonIsPlanar(polygonData);\n\n      if (polygonData.objectGeneration.type === 1) {\n        polygon = this.drawPolygonPlanar(polygonData);\n        polygon.generationType = \"area\";\n      } else if (polygonData.objectGeneration.type === 2) {\n        if (!isPlanar) {\n          polygon = this.drawPolygonExtruded(polygonData);\n          polygon.generationType = \"volume\";\n        } else {\n          polygon = this.drawPolygonExtrudedPlanar(polygonData);\n          polygon.generationType = \"area\";\n        }\n      } else {\n        if (isPlanar) {\n          polygon = this.drawPolygonRotatedPlanar(polygonData);\n          polygon.generationType = \"area\";\n        } else {\n          polygon = this.drawPolygonRotated(polygonData);\n          polygon.generationType = \"volume\";\n        }\n      }\n\n      return polygon;\n    }\n\n    drawPolygonPlanar(polygonData) {\n      let polygonDepth = 0.01;\n      let extrusionVector = new this.THREE.Vector3(0, 0, polygonDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne); // extrusionVector.z *= -1;\n\n      let extrudeSettings = {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings); // geometry.translate(0, 0, -extrusionVector.z);\n\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(midVector);\n      polygon.updateMatrix();\n      polygon.setRotationFromEuler(eulerTwo);\n      return polygon;\n    }\n\n    drawPolygonExtruded(polygonData) {\n      let extrusionVector = this.utils.initVector3(polygonData.objectGeneration.extrusionVector);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      extrusionVector.applyEuler(eulerOne); // TODO: I am not sure this logic is correct\n\n      crossVector.applyEuler(eulerOne);\n      let skewParams = this.utils.getSkewParams(extrusionVector);\n      let polygonDepth = extrusionVector.z;\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let geometry = this.GraphicsThree3D.makeExtrudeGeoRaw(shapePts, polygonDepth); // TODO: I am not sure this logic is correct\n\n      if (crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0) {\n        geometry.translate(0, 0, -polygonDepth);\n      }\n\n      this.utils.skewExtrudedGeometry(geometry, skewParams);\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(midVector);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      return polygon;\n    }\n\n    drawPolygonExtrudedPlanar(polygonData) {\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let polygonPoints = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(polygonPoints, zAxis);\n      polygonPoints.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let extrusionVector = this.utils.initVector3(polygonData.objectGeneration.extrusionVector).applyEuler(eulerOne);\n      let shapePts = new _PlanarExtrusionPointsGenerator.PlanarExtrusionPointsGenerator(polygonPoints, extrusionVector).extrudedPoints;\n      let shape = new this.THREE.Shape(shapePts);\n      let geometry = new this.THREE.ExtrudeGeometry(shape, {\n        depth: 0.01,\n        bevelEnabled: false\n      });\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(midVector);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      return polygon;\n    }\n\n    drawPolygonRotated(polygonData) {\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let rotatedGeometry = new _RotatedGeometry.RotatedGeometry(pts.map(pt => pt.clone()), {\n        rotationPoint: this.utils.initVector3(polygonData.objectGeneration.rotationPoint),\n        rotationAxis: this.utils.initVector3(polygonData.objectGeneration.rotationAxis).normalize(),\n        rotationAngle: polygonData.objectGeneration.rotationAngle * Math.PI / 180,\n        rotationPitch: polygonData.objectGeneration.rotationPitch\n      });\n      let polygon = new this.THREE.Mesh(rotatedGeometry); ///\n\n      let linePairs = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(polygon, Math.PI / 6).forEach(pair => linePairs.push([pair[0].clone().applyMatrix4(polygon.matrix), pair[1].clone().applyMatrix4(polygon.matrix)]));\n      this.WireframeGenerator.getVolumeWireframeData(polygon, linePairs, linePairs, polygonData.objectType); ///\n\n      return polygon;\n    }\n\n    drawPolygonRotatedPlanar(polygonData) {\n      console.log(\"here we are\", polygonData);\n      let rotationPoint = this.utils.initVector3(polygonData.objectGeneration.rotationPoint);\n      let rotationAxis = this.utils.initVector3(polygonData.objectGeneration.rotationAxis).normalize();\n      let rotationAngle = polygonData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = polygonData.objectGeneration.rotationPitch;\n      let polygonPoints = this.getPolygonPlanarPoints(polygonData);\n      let zeroPoint = new this.THREE.Vector3();\n      let yAxis = new this.THREE.Vector3(0, 1, 0);\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let [_, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(polygonPoints, zAxis);\n      polygonPoints.forEach(point => {\n        point.sub(rotationPoint);\n        point.applyEuler(eulerOne);\n      });\n      if (rotationAxis.applyEuler(eulerOne).dot(zAxis) < 0) rotationAngle *= -1; /// -->\n\n      polygonPoints = new _PlanarRotationPointsGenerator.PlanarRotationPointsGenerator(polygonPoints, {\n        rotationAngle,\n        rotationPitch\n      }).rotatedPoints; /// <--\n      // for ( let i = 0; i < polygonPoints.length - 1; i++ ) polygonPoints[ i ].nextPoint = polygonPoints[ i + 1 ];\n      // polygonPoints[ polygonPoints.length - 1 ].nextPoint = polygonPoints[ 0 ];\n      // let distancePlaneNormal = new this.THREE.Vector3().crossVectors( zAxis, yAxis );\n      // let { minPt, maxPt } = polygonPoints.reduce(({ maxDistance, maxPt, minDistance, minPt }, pt) => {\n      //   let projectedPt = pt.clone().projectOnVector( distancePlaneNormal );\n      //   let distance = pt.distanceTo( zeroPoint );\n      //   if ( projectedPt.dot( distancePlaneNormal ) < 0 ) distance *= -1;\n      //   if ( distance > maxDistance ) { maxDistance = distance; maxPt.copy( pt ); }\n      //   if ( distance < minDistance ) { minDistance = distance; minPt.copy( pt ); }\n      //   return { maxDistance, maxPt, minDistance, minPt };\n      // }, {\n      //   maxDistance: -Infinity,\n      //   minDistance: Infinity,\n      //   maxPt: new this.THREE.Vector3(),\n      //   minPt: new this.THREE.Vector3()\n      // });\n      // console.log(maxPt, minPt);\n\n      let shapePts = polygonPoints.map(point => new this.THREE.Vector3(point.x, point.y, 0));\n      let shape = new this.THREE.Shape(shapePts);\n      let extrudeSettings = {\n        depth: -0.01,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(rotationPoint);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      console.log(geometry);\n      return polygon;\n    }\n\n    getPolygonPlanarPoints(polygonData) {\n      let points = [];\n\n      for (let c = 0; c < polygonData.points.length; c++) {\n        let x = polygonData.points[c].x;\n        let y = polygonData.points[c].y;\n        let z = polygonData.points[c].z;\n        points.push(new this.THREE.Vector3(x, y, z));\n      }\n\n      return points;\n    }\n\n    polygonIsPlanar(polygonData) {\n      return polygonData.objectGeneration.type === 1 || polygonData.objectGeneration.type === 2 && this.utils.vectorIsZero(polygonData.objectGeneration.extrusionVector) || polygonData.objectGeneration.type === 2 && this.GraphicsThree3D.objectExtrudesPlanar(polygonData) || polygonData.objectGeneration.type === 3 && this.GraphicsThree3D.objectRotatesPlanar(polygonData);\n    }\n\n  }\n\n  _exports.Polygon = Polygon;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polyline\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Polyline = void 0;\n\n  class Polyline {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawPolyline(lineData, materialProps) {\n      let lineProps = materialProps.line;\n      let lineType = lineData.objectGeneration.type;\n      let lineMesh;\n\n      if (lineType == 1) {\n        lineMesh = this.drawPolylinePlanar(lineData, lineProps);\n        lineMesh.generationType = \"line\";\n      } else if (lineType == 2) {\n        lineMesh = this.drawPolylineExtruded(lineData);\n        lineMesh.generationType = \"area\";\n      } else if (lineType == 3) {\n        lineMesh = this.drawPolylineRotated(lineData);\n        lineMesh.generationType = \"area\";\n      }\n\n      return lineMesh;\n    }\n\n    drawPolylinePlanar(lineData, lineProps) {\n      let linePoints = lineData.points;\n      let lineVerticies = [];\n      linePoints.forEach(point => lineVerticies.push(this.utils.initVector3(point)));\n      let lineMesh = this.utils.drawConnectedLines(lineVerticies, lineProps);\n      return lineMesh;\n    }\n\n    drawPolylineExtruded(lineData) {\n      let polylineDepth = 0.01;\n      let depthExtrusion = new this.THREE.Vector3(0, 0, polylineDepth);\n      let extrusionNormalized = depthExtrusion.clone().normalize();\n      let linePoints = lineData.points;\n      let extrusionVector = lineData.objectGeneration.extrusionVector;\n      let basePoints = [];\n      let extrudePoints = [];\n      linePoints.forEach(point => {\n        let originVector = new this.THREE.Vector3(point.x, point.y, point.z);\n        let extrudedVector = new this.THREE.Vector3(point.x + extrusionVector.x, point.y + extrusionVector.y, point.z + extrusionVector.z);\n        basePoints.push(originVector);\n        extrudePoints.push(extrudedVector);\n      });\n      extrudePoints = extrudePoints.reverse();\n      let points = basePoints.concat(extrudePoints); // this.WireframeGenerator.getPlanarPoints(points.map(point => point.clone()), lineData.objectType);\n\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(points, extrusionNormalized);\n      points.forEach(point => {\n        // this.GraphicsThree3D.wireframePoints.push(point.clone());\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = points.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      depthExtrusion.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: depthExtrusion.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      let areaMesh = new this.THREE.Mesh(geometry);\n      areaMesh.position.add(midVector);\n      areaMesh.updateMatrix();\n      areaMesh.setRotationFromEuler(eulerTwo);\n      return areaMesh;\n    }\n\n    drawPolylineRotated(lineData) {\n      let linePoints = lineData.points;\n      let rotationPoint = lineData.objectGeneration.rotationPoint;\n      let rotationAxis = this.utils.initVector3(lineData.objectGeneration.rotationAxis);\n      let rotationAngle = lineData.objectGeneration.rotationAngle * Math.PI / 180;\n      let botPoints = [];\n      let topPoints = [];\n      let startPoints = [];\n      let endPoints = [];\n      let dummyVector = new this.THREE.Vector3();\n      linePoints.forEach((point, index) => {\n        botPoints.push(this.utils.initVector3(point));\n\n        if (index > 0 && index < linePoints.length - 1) {\n          let topObject = new this.THREE.Object3D();\n          topObject.position.set(point.x, point.y, point.z);\n          let pivotPoint = new this.THREE.Object3D();\n          pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n          pivotPoint.attach(topObject);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n          pivotPoint.updateMatrixWorld();\n          let newTopPoint = topObject.getWorldPosition(dummyVector).clone();\n          topPoints.push(newTopPoint);\n        }\n      });\n      let startObject = new this.THREE.Object3D();\n      startObject.position.set(linePoints[0].x, linePoints[0].y, linePoints[0].z);\n      let endObject = new this.THREE.Object3D();\n      endObject.position.set(linePoints[linePoints.length - 1].x, linePoints[linePoints.length - 1].y, linePoints[linePoints.length - 1].z);\n      let pivotPoint = new this.THREE.Object3D();\n      pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n      pivotPoint.attach(startObject);\n      pivotPoint.attach(endObject);\n      let numOfSegments = Math.abs(64 * rotationAngle / (Math.PI * 2));\n\n      for (let i = 0; i < numOfSegments; i++) {\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle / numOfSegments);\n        pivotPoint.updateMatrixWorld();\n        let newStartPoint = startObject.getWorldPosition(dummyVector).clone();\n        startPoints.push(newStartPoint);\n        let newEndPoint = endObject.getWorldPosition(dummyVector).clone();\n        endPoints.push(newEndPoint);\n      }\n\n      topPoints.reverse();\n      startPoints.reverse();\n      let polylineDepth = 0.01;\n      let extrusionVector = new this.THREE.Vector3(0, 0, polylineDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = botPoints.concat(endPoints).concat(topPoints).concat(startPoints); // this.WireframeGenerator.getPlanarPoints(pts.map(point => point.clone()), lineData.objectType);\n\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        // this.GraphicsThree3D.wireframePoints.push(point.clone());\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      let areaMesh = new this.THREE.Mesh(geometry);\n      areaMesh.position.add(midVector);\n      areaMesh.updateMatrix();\n      areaMesh.setRotationFromEuler(eulerTwo);\n      return areaMesh;\n    }\n\n  }\n\n  _exports.Polyline = Polyline;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingObjectLoader\", [\"exports\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Point\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polyline\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipse\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Polygon\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/Ellipsoid\", \"m08-2020/lib old/FigureLoaders/DrawingObjectComponents/ComplexVolume\", \"m08-2020/lib old/Utils/BooleanOperations\"], function (_exports, _Point, _Polyline, _Ellipse, _Polygon, _Ellipsoid, _ComplexVolume, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DrawingObjectLoader = void 0;\n  // TODO: Move this another place?\n  const LINE_MIN_THICKNESS = 0.4; //\n\n  class DrawingObjectLoader {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n      this.Ellipsoid = new _Ellipsoid.Ellipsoid(GraphicsThree3D);\n      this.Point = new _Point.Point(GraphicsThree3D);\n      this.Polyline = new _Polyline.Polyline(GraphicsThree3D);\n      this.Ellipse = new _Ellipse.Ellipse(GraphicsThree3D);\n      this.Polygon = new _Polygon.Polygon(GraphicsThree3D);\n      this.ComplexVolume = new _ComplexVolume.ComplexVolume(GraphicsThree3D);\n    }\n\n    drawPoints(points, properties, positivesAndNegatives) {\n      points.forEach(point => {\n        if (point.objectType > 0 && point.objectType < 3) {\n          let pointMesh = this.Point.drawPoint(point, properties);\n          if (point.objectGeneration.type === 1) this.addToPosAndNeg(pointMesh, point, positivesAndNegatives, \"point\");else if (point.objectGeneration.type === 2 || point.objectGeneration.type === 3) this.addToPosAndNeg(pointMesh, point, positivesAndNegatives, \"line\");\n        }\n      });\n    }\n\n    drawPolylines(polylines, properties, positivesAndNegatives) {\n      polylines.forEach(polyline => {\n        if (polyline.objectType > 0 && polyline.objectType < 3) {\n          let lineMesh = this.Polyline.drawPolyline(polyline, properties);\n          if (polyline.objectGeneration.type === 1) this.addToPosAndNeg(lineMesh, polyline, positivesAndNegatives, \"line\");else if (polyline.objectGeneration.type === 2 || polyline.objectGeneration.type === 3) this.addToPosAndNeg(lineMesh, polyline, positivesAndNegatives, \"area\");\n        }\n      });\n    }\n\n    drawEllipses(ellipses, positivesAndNegatives) {\n      ellipses.forEach(ellipse => {\n        console.log(ellipse);\n\n        if (ellipse.objectType > 0 && ellipse.objectType < 3) {\n          let ellipseMesh = this.Ellipse.drawEllipse(ellipse);\n          ellipseMesh.updateMatrix();\n\n          if (ellipse.objectGeneration.type === 1 || ellipse.objectGeneration.type === 2 && this.GraphicsThree3D.objectExtrudesPlanar(ellipse, true) || ellipse.objectGeneration.type === 3 && !ellipse.objectGeneration.rotationAngle || ellipse.objectGeneration.type === 3 && this.GraphicsThree3D.objectRotatesPlanar(ellipse, true)) {\n            this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"area\");\n          } else {\n            if (ellipse.objectGeneration.type === 2) this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"volumeRounded\");else if (ellipse.objectGeneration.type === 3) this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"volumeRounded\");\n          }\n        }\n      });\n    }\n\n    drawPolygons(polygons, positivesAndNegatives) {\n      polygons.forEach(polygon => {\n        if (polygon.objectType > 0 && polygon.objectType < 3 && polygon.objectGeneration.type > 0 && polygon.objectGeneration.type < 4) {\n          let polygonMesh = this.Polygon.drawPolygon(polygon);\n          if (this.Polygon.polygonIsPlanar(polygon)) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"area\");else {\n            if (polygon.objectGeneration.type === 2) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"volumeRegular\");else if (polygon.objectGeneration.type === 3) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"volumeRounded\");\n          }\n        }\n      });\n    }\n\n    drawEllipsoids(ellipsoids, positivesAndNegatives) {\n      ellipsoids.forEach(ellipsoid => {\n        if (ellipsoid.objectType > 0 && ellipsoid.objectType < 3) {\n          let ellipsoidMesh = this.Ellipsoid.drawEllipsoid(ellipsoid);\n          this.addToPosAndNeg(ellipsoidMesh, ellipsoid, positivesAndNegatives, \"volumeRounded\");\n        }\n      });\n    }\n\n    drawComplexVolumes(drawingObject, jsonData) {\n      let components = drawingObject.components;\n      let properties = drawingObject.properties;\n      let multilayeredObjects = {\n        positives: [],\n        negatives: []\n      };\n      let wireframes = [];\n      if (components.polygons) wireframes.push(...this.ComplexVolume.drawMultilayeredPolygons(components.polygons, properties, multilayeredObjects));\n      if (components.ellipses) wireframes.push(...this.ComplexVolume.drawMultilayeredEllipses(components.ellipses, properties, multilayeredObjects));\n      if (components.ellipsoids) wireframes.push(...this.ComplexVolume.drawMultilayeredEllipsoids(components.ellipsoids, properties, multilayeredObjects));\n      this.ComplexVolume.addMultilayeredObjectsToScene(multilayeredObjects, wireframes, properties, drawingObject.insertionPoints, jsonData, drawingObject);\n    }\n\n    drawRegularObjects(drawingObject, jsonData) {\n      if (drawingObject.insertionPoints) {\n        let components = drawingObject.components;\n        let properties = drawingObject.properties;\n        let positivesAndNegatives = {\n          area: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          volumeRegular: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          volumeRounded: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          point: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          line: {\n            positiveObjects: [],\n            negativeObjects: []\n          }\n        };\n        if (components.points) this.drawPoints(components.points, properties, positivesAndNegatives);\n        if (components.polylines) this.drawPolylines(components.polylines, properties, positivesAndNegatives);\n        if (components.ellipses) this.drawEllipses(components.ellipses, positivesAndNegatives);\n        if (components.polygons) this.drawPolygons(components.polygons, positivesAndNegatives);\n        if (components.ellipsoids) this.drawEllipsoids(components.ellipsoids, positivesAndNegatives);\n        let areas = positivesAndNegatives.area;\n        let volumesRegular = positivesAndNegatives.volumeRegular;\n        let volumesRounded = positivesAndNegatives.volumeRounded;\n        let points = positivesAndNegatives.point;\n        let lines = positivesAndNegatives.line;\n        this.generateAreas(areas, drawingObject, properties, jsonData); ///\n\n        this.generateVolumes(volumesRegular, volumesRounded, drawingObject, properties, jsonData); ///\n\n        this.generatePoints(points, drawingObject, properties, jsonData);\n        this.generateLines(lines, drawingObject, properties, jsonData);\n      }\n    }\n\n    generateAreas(areas, drawingObject, properties, jsonData) {\n      let areaPos = areas.positiveObjects;\n      let areaNeg = areas.negativeObjects;\n\n      if (areaPos.length > 0) {\n        let positiveObject = BooleanOperations.applyCSG(areaPos, areaNeg);\n        let objectMaterial = this.GraphicsThree3D.getAreaMaterial(positiveObject, properties);\n\n        if (objectMaterial !== null) {\n          positiveObject.material = objectMaterial;\n          positiveObject.material.side = this.THREE.FrontSide;\n          let object = positiveObject;\n          let objectTransparent = this.GraphicsThree3D.makeTransparentClone(object);\n          objectTransparent.applyMatrix4(object.matrix);\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          object.stencilNeeded = true;\n          object.addToDXF = true;\n          let lineProps = { ...properties.line,\n            thickness: properties.line.thickness >= LINE_MIN_THICKNESS ? properties.line.thickness : LINE_MIN_THICKNESS\n          };\n          this.WireframeGenerator.applyWireframesArea(object, objectTransparent, lineProps, drawingObject);\n          this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"area\", \"drawingObject\", drawingObject);\n        }\n      }\n    }\n\n    generateVolumes(volumesRegular, volumesRounded, drawingObject, properties, jsonData) {\n      let volumePosRegular = volumesRegular.positiveObjects;\n      let volumeNegRegular = volumesRegular.negativeObjects;\n      let volumePosRounded = volumesRounded.positiveObjects;\n      let volumeNegRounded = volumesRounded.negativeObjects;\n      let positiveObjectRegular = volumePosRegular.length > 0 ? BooleanOperations.applyCSG(volumePosRegular, volumeNegRegular) : null;\n      let positiveObjectRounded = volumePosRounded.length > 0 ? BooleanOperations.applyCSG(volumePosRounded, volumeNegRounded) : null;\n      let positiveObject;\n      if (positiveObjectRegular && positiveObjectRounded) positiveObject = BooleanOperations.doCSG(positiveObjectRegular, positiveObjectRounded, \"union\");else if (positiveObjectRegular && !positiveObjectRounded && volumeNegRounded.length > 0) {\n        positiveObject = positiveObjectRegular;\n        volumeNegRounded.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n      } else if (positiveObjectRegular && !positiveObjectRounded) positiveObject = positiveObjectRegular;else if (!positiveObjectRegular && positiveObjectRounded && volumeNegRegular.length > 0) {\n        positiveObject = positiveObjectRounded;\n        volumeNegRegular.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n      } else if (!positiveObjectRegular && positiveObjectRounded) positiveObject = positiveObjectRounded;else positiveObject = null;\n\n      if (positiveObject) {\n        ///\n        volumeNegRegular.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n        volumeNegRounded.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\")); ///\n\n        let objectMaterial = this.GraphicsThree3D.getVolumeMaterialsIndexFaces(positiveObject, properties); // objectMaterial.forEach( mat => mat.wireframe = true );\n\n        if (objectMaterial !== null) {\n          objectMaterial.forEach(mat => mat.side = this.THREE.FrontSide);\n          positiveObject.material = objectMaterial;\n          let [doubleSideObject, frontSideObject, backSideObject] = this.GraphicsThree3D.getThreeSideFaceObjects(positiveObject);\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          this.GraphicsThree3D.indexMeshFaces(frontSideObject, properties);\n          this.GraphicsThree3D.indexMeshFaces(backSideObject, properties);\n          let objectTransparent = this.GraphicsThree3D.makeTransparentClone(positiveObject);\n          let geo = positiveObject.geometry.clone();\n          backSideObject.renderOrder = 0; // DepthClear mesh\n\n          let depthClearMesh = new this.THREE.Mesh(geo, objectMaterial);\n          depthClearMesh.applyMatrix4(positiveObject.matrix);\n          depthClearMesh.renderOrder = -100;\n\n          depthClearMesh.onBeforeRender = function (renderer) {\n            renderer.clearDepth();\n          };\n\n          this.GraphicsThree3D.addObjectToScene(depthClearMesh, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject); // FrontSide material mesh\n\n          frontSideObject.stencilNeeded = true;\n          frontSideObject.addToDXF = true;\n          frontSideObject.dxfLayer = `drawingObject_${drawingObject.id}`;\n\n          if (positiveObjectRegular) {\n            let linePairs = this.WireframeGenerator.getRegularWireframePointPairs(positiveObjectRegular);\n            linePairs = linePairs.map(pair => [pair[0].clone().applyMatrix4(positiveObjectRegular.matrix), pair[1].clone().applyMatrix4(positiveObjectRegular.matrix)]);\n            this.WireframeGenerator.getVolumeWireframeData(positiveObjectRegular, linePairs, linePairs, 1);\n          }\n\n          let lineProps = { ...properties.line,\n            thickness: properties.line.thickness >= LINE_MIN_THICKNESS ? properties.line.thickness : LINE_MIN_THICKNESS\n          };\n          this.WireframeGenerator.applyWireframesVolume(frontSideObject, objectTransparent, lineProps, drawingObject);\n          this.GraphicsThree3D.addObjectToScene(frontSideObject, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject); // BackSide material mesh\n\n          this.GraphicsThree3D.addObjectToScene(backSideObject, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject);\n        }\n      }\n    }\n\n    generatePoints(points, drawingObject, properties, jsonData) {\n      let pointPos = points.positiveObjects;\n      let pointNeg = points.negativeObjects;\n\n      if (pointPos.length > 0) {\n        let positiveObject = BooleanOperations.applyCSG(pointPos, pointNeg);\n        let objectMaterial = this.GraphicsThree3D.getPointMaterial(properties);\n\n        if (objectMaterial !== null) {\n          positiveObject.material = objectMaterial;\n          let object = positiveObject;\n          let objectTransparent = null;\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"point\", \"drawingObject\", drawingObject);\n          this.addPointObjectsToDXF(drawingObject);\n        }\n      }\n    }\n\n    generateLines(lines, drawingObject, properties, jsonData) {\n      // TODO: AddLines back to dim chain collision detection\n      let linePos = lines.positiveObjects;\n      let lineNeg = lines.negativeObjects;\n\n      if (linePos.length > 0) {\n        linePos.forEach(line => {\n          // let objectMaterial = this.GraphicsThree3D.getlineMeshMaterial(properties);\n          if (line.material !== null) {\n            // line.material = objectMaterial;\n            let object = line;\n            object.renderOrder = -6;\n            let objectTransparent = null; // this.addToDimChainCollisionDetection(line, drawingObject.insertionPoints);\n\n            this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"line\", \"drawingObject\", drawingObject);\n            this.addLineObjectsToDXF(drawingObject);\n          }\n        }); // let positiveObject = BooleanOperations.applyCSG(linePos, lineNeg);\n        // let objectMaterial = this.GraphicsThree3D.getlineMeshMaterial(properties);\n        // if (objectMaterial !== null) {\n        //   positiveObject.material = objectMaterial;\n        //   let object = positiveObject;\n        //   object.renderOrder = -6;\n        //   let objectTransparent = null;\n        //   this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n        //   this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"line\", \"drawingObject\", drawingObject);\n        //   this.addLineObjectsToDXF(drawingObject);\n        // }\n      }\n    }\n\n    addLineObjectsToDXF(lineData) {\n      let insertionPoints = lineData.insertionPoints;\n      let points = lineData.components.points;\n      let polylines = lineData.components.polylines;\n      insertionPoints.forEach(insertionPoint => {\n        let insertionVector = insertionPoint.insertionVector;\n        if (points) points.forEach(point => {\n          if (point.objectType === 1) {\n            if (point.objectGeneration.type === 2) {\n              let pointPair = this.Point.getExtrudedPointPoints(point.insertionPoint, point.objectGeneration.extrusionVector);\n              if (insertionPoint.rotationAngle) pointPair = pointPair.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n              pointPair.forEach(point => point.add(insertionVector));\n              this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n                id: lineData.id,\n                properties: lineData.properties.line,\n                pointPairs: [pointPair]\n              });\n            } else if (point.objectGeneration.type === 3) {\n              let circlePoints = this.Point.getRotatedPoints(point);\n              if (insertionPoint.rotationAngle) circlePoints = circlePoints.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n              circlePoints.forEach(point => point.add(insertionVector));\n              let circlePointPairs = [];\n\n              for (let i = 0; i < circlePoints.length - 1; i++) circlePointPairs.push([circlePoints[i], circlePoints[i + 1]]);\n\n              this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n                id: lineData.id,\n                properties: lineData.properties.line,\n                pointPairs: circlePointPairs\n              });\n            }\n          }\n        });\n        if (polylines) polylines.forEach(polyline => {\n          if (polyline.objectType === 1 && polyline.objectGeneration.type === 1) {\n            let polylinePoints = polyline.points.map(point => this.utils.initVector3(point));\n            if (insertionPoint.rotationAngle) polylinePoints = polylinePoints.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n            polylinePoints.forEach(point => point.add(insertionVector));\n            let pointPairs = [];\n\n            for (let i = 0; i < polylinePoints.length - 1; i++) pointPairs.push([polylinePoints[i], polylinePoints[i + 1]]);\n\n            this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n              id: lineData.id,\n              properties: lineData.properties.line,\n              pointPairs\n            });\n          }\n        });\n      });\n    }\n\n    addPointObjectsToDXF(pointData) {\n      let insertionPoints = pointData.insertionPoints;\n      let points = pointData.components.points;\n      points.forEach(point => {\n        if (point.objectType === 1 && point.objectGeneration.type === 1) {\n          insertionPoints.forEach(insertionPoint => {\n            let insertionVector = insertionPoint.rotationAngle ? this.utils.applyRotationToVector(insertionPoint.insertionVector, insertionPoint) : insertionPoint.insertionVector;\n            this.GraphicsThree3D.dxfGenerator.pointObjectsData.push({\n              id: pointData.id,\n              properties: pointData.properties.point,\n              position: this.utils.addVectorObjects(point.insertionPoint, insertionVector)\n            });\n          });\n        }\n      });\n    }\n\n    addToDimChainCollisionDetection(object, insertionPoints) {\n      insertionPoints.forEach(insertionPoint => {\n        let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n        let newObject = object.clone();\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          pivotPoint.attach(newObject);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else pivotPoint.attach(newObject);\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        let baseObject = pivotPoint.children[0];\n        pivotPoint.children.forEach(child => child.applyMatrix4(pivotPoint.matrixWorld));\n        let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(baseObject);\n        this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n      });\n    }\n\n    addToPosAndNeg(mesh, objectData, positivesAndNegatives, type) {\n      if (objectData.objectType == 1) positivesAndNegatives[type].positiveObjects.push(mesh);else positivesAndNegatives[type].negativeObjects.push(mesh);\n    }\n\n    addPointsToWireframeRegions(points, wireframeRegions) {\n      points.forEach(pointsData => {\n        let offsetVector = pointsData.normal.clone().multiplyScalar(pointsData.distance);\n        let pointsArr = pointsData.pointsArrData;\n        let [eulerOne, eulerTwo] = this.utils.getEulerOneAndTwo(pointsData.normal);\n        let pointsPos = pointsArr.positives;\n        let pointsNeg = pointsArr.negatives;\n        let newRegionData = {\n          offsetVector,\n          eulerTwo,\n          positiveRegions: [],\n          negativeRegions: []\n        };\n        pointsPos.forEach(pointArr => {\n          let newRegion = {\n            regions: [[]],\n            inverted: false\n          };\n          pointArr.forEach(point => {\n            point.applyEuler(eulerOne);\n            newRegion.regions[0].push([point.x, point.y]);\n          });\n          newRegionData.positiveRegions.push(newRegion);\n        });\n        pointsNeg.forEach(pointArr => {\n          let newRegion = {\n            regions: [[]],\n            inverted: false\n          };\n          pointArr.forEach(point => {\n            point.applyEuler(eulerOne);\n            newRegion.regions[0].push([point.x, point.y]);\n          });\n          newRegionData.negativeRegions.push(newRegion);\n        });\n        wireframeRegions.push(newRegionData);\n      });\n    }\n\n  }\n\n  _exports.DrawingObjectLoader = DrawingObjectLoader;\n});","define(\"m08-2020/lib old/FigureLoaders/DrawingPlaneLoader\", [\"exports\", \"m08-2020/lib old/Utils\", \"m08-2020/lib old/FigureLoaders/DimensionalChainLoader\", \"svg-parser\", \"three/examples/jsm/renderers/SVGRenderer.js\"], function (_exports, _Utils, _DimensionalChainLoader, _svgParser, _SVGRenderer) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DrawingPlaneLoader = void 0;\n\n  class DrawingPlaneLoader {\n    constructor(GraphicsThree3D) {\n      this.THREE = GraphicsThree3D.THREE;\n      this.fontUrlArray = GraphicsThree3D.fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = new _Utils.Utils(GraphicsThree3D.THREE);\n      this.lastPlaneID = \"\"; ///\n\n      this.loadDrawingPlanes = this.loadDrawingPlanes.bind(this);\n    }\n\n    loadDrawingPlanes() {\n      let jsonData = this.GraphicsThree3D.jsonData;\n      let settingProps = jsonData.settings.drawingPlane.properties;\n      let unitsData = jsonData.units;\n      let drawingPlanes = jsonData.drawingPlanes;\n      let units = this.utils.processUnits(unitsData);\n      let dimensionalChainLoader = new _DimensionalChainLoader.DimensionalChainLoader(this.GraphicsThree3D);\n      let dimensionalChains = jsonData.dimensionalChains;\n      this.lastPlaneID = drawingPlanes[drawingPlanes.length - 1][\"id\"];\n      let initCamParams = this.getInitialCameraParameters();\n      drawingPlanes.forEach(drawingPlane => {\n        let [cameraZoom, canvasWidth, canvasHeight] = this.getGraphicsParameters(this.GraphicsThree3D); // Components\n\n        let drawingData = drawingPlane.components.drawing;\n        let frameData = drawingPlane.components.frame;\n        let freeTexts = drawingPlane.components.freeTexts;\n        let scaleBarData = drawingPlane.components.scaleBar;\n        let scaleTextBlockData = drawingPlane.components.scaleTextBlock;\n        let unitTextBlockData = drawingPlane.components.unitTextBlock; // Properties\n\n        let properties = drawingPlane.properties;\n        let drawingProps = properties.drawing ? properties.drawing : settingProps.drawing;\n        let frameProps = properties.frame ? properties.frame : settingProps.frame;\n        let freeTextProps = properties.freeText ? properties.freeText : settingProps.freeText;\n        let scaleBarProps = properties.scaleBar ? properties.scaleBar : settingProps.scaleBar;\n        let scaleTextBlockProps = properties.scaleTextBlock ? properties.scaleTextBlock : settingProps.scaleTextBlock;\n        let unitTextBlockProps = properties.unitTextBlock ? properties.unitTextBlock : settingProps.unitTextBlock; ///\n\n        frameData = { ...frameData,\n          drawingNormal: drawingData.normalVector,\n          drawingVerticalVector: drawingData.verticalVector\n        };\n\n        if (drawingData && drawingProps) {\n          this.removeDimChains3D();\n          this.drawDimensionalChains(dimensionalChainLoader, dimensionalChains, drawingData);\n          this.checkWrongScale(frameData);\n          this.makeDrawing(drawingData, drawingProps);\n\n          if (frameData && frameProps) {\n            let frame = this.makeFrame(frameData, frameProps, drawingData);\n            if (freeTextProps && frameData) this.drawFreeTexts(freeTexts, freeTextProps, frameData, frame);\n            if (scaleTextBlockData && scaleTextBlockProps) this.drawScaleTextBlock(scaleTextBlockData, scaleTextBlockProps, frameData, frame);\n            if (unitTextBlockData && unitTextBlockProps) this.drawUnitTextBlock(unitTextBlockData, unitTextBlockProps, units, frameData, frame);\n            if (scaleBarData && scaleBarProps) this.generateScaleBarObject(scaleBarData, scaleBarProps, units.length, frameData, frame);\n          }\n        }\n\n        this.scaleCanvas(frameData, frameProps, this.GraphicsThree3D);\n        let initialWireframeStatus = this.makeObjectFilled(this.GraphicsThree3D);\n        let scale = this.getScale(frameData);\n        let drawingComponents = {\n          frameDataAndProps: {\n            frameData,\n            frameProps\n          },\n          freeTextDataAndProps: {\n            freeTexts,\n            freeTextProps\n          },\n          scaleTextBlockDataAndProps: {\n            scaleTextBlockData,\n            scaleTextBlockProps\n          },\n          unitTextBlockDataAndProps: {\n            unitTextBlockData,\n            unitTextBlockProps\n          },\n          scaleBarDataAndProps: {\n            scaleBarData,\n            scaleBarProps\n          }\n        };\n        let dxfPlaneData = this.getDXFPlaneData(drawingPlane, drawingProps, scale, drawingComponents, units);\n        this.downloadPNGandDXFAndReset(cameraZoom, canvasWidth, canvasHeight, dxfPlaneData);\n        this.resetToInitialWireframeStatus(this.GraphicsThree3D, initialWireframeStatus);\n      });\n      this.resetInitialCameraParameters(initCamParams);\n    }\n\n    resetInitialCameraParameters(initialParameters) {\n      let camera = this.GraphicsThree3D.camera;\n      let controls = this.GraphicsThree3D.controls;\n      let {\n        camZoom,\n        camPos,\n        camTarget\n      } = initialParameters;\n      camera.position.copy(camPos);\n      camera.lookAt(camTarget);\n      camera.updateProjectionMatrix();\n      controls.update();\n      camera.zoom = camZoom;\n      controls.target.copy(camTarget);\n      camera.up = new this.THREE.Vector3(0, 1, 0);\n      camera.lookAt(controls.target);\n      controls.update();\n    }\n\n    getInitialCameraParameters() {\n      return {\n        camZoom: this.GraphicsThree3D.camera.zoom,\n        camPos: this.GraphicsThree3D.camera.position.clone(),\n        camTarget: this.GraphicsThree3D.controls.target.clone()\n      };\n    }\n\n    getDXFPlaneData(drawingPlane, drawingProps, scale, drawingComponents, units) {\n      return {\n        id: drawingPlane.id,\n        output: drawingPlane.output,\n        normal: drawingPlane.components.drawing.normalVector,\n        scale,\n        units,\n        drawingComponents,\n        visualisation: {\n          drawingObject: drawingProps.visualisation.drawingObject,\n          fastener: drawingProps.visualisation.fastener,\n          arrow: drawingProps.visualisation.arrow\n        },\n        dxfFastenerVisualisation: { ...drawingProps.dxfFastenerVisualisation\n        }\n      };\n    }\n\n    checkWrongScale(frameData) {\n      let scaleType = frameData.scaleType;\n      if (scaleType < 1 || scaleType > 2) this.GraphicsThree3D.clearScene();else {\n        let testScale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n        let checkScale = this.getScale(frameData);\n        if (checkScale < testScale) this.GraphicsThree3D.clearScene();\n      }\n    }\n    /**\n     * Download PNG\n     */\n\n\n    downloadPNGandDXFAndReset(cameraZoom, canvasWidth, canvasHeight, drawingPlaneData) {\n      let drawingOutput = drawingPlaneData.output;\n      this.GraphicsThree3D.renderAllScenes(this.GraphicsThree3D);\n\n      if (drawingOutput.dxf) {\n        if (drawingPlaneData.id === this.lastPlaneID) this.GraphicsThree3D.dxfGenerator.downloadDXF2D(\"plane.dxf\", drawingPlaneData);else this.GraphicsThree3D.dxfGenerator.generateBlockAndEntitySections(drawingPlaneData);\n      }\n\n      if (drawingOutput.image) this.GraphicsThree3D.downloadAsPNGImage(\"plane.png\");\n      this.GraphicsThree3D.clearScene();\n      this.GraphicsThree3D.camera.zoom = cameraZoom;\n      this.GraphicsThree3D.renderer.domElement.style.width = canvasWidth.toFixed(0) + \"px\";\n      this.GraphicsThree3D.renderer.domElement.style.height = canvasHeight.toFixed(0) + \"px\";\n      this.GraphicsThree3D.updateRendererAndCamera(this.GraphicsThree3D.renderer, this.GraphicsThree3D.camera);\n      this.GraphicsThree3D.renderAllScenes(this.GraphicsThree3D);\n      this.GraphicsThree3D.camera.up = new this.THREE.Vector3(0, 1, 0);\n      this.GraphicsThree3D.drawCustom(this.GraphicsThree3D.globalJSONData);\n    }\n\n    makeObjectFilled(GraphicsThree3D) {\n      let wireframeStatus = GraphicsThree3D.isWireFrameOnly;\n      GraphicsThree3D.isWireFrameOnly = true;\n      GraphicsThree3D.ToggleWireframe();\n      return wireframeStatus;\n    }\n\n    resetToInitialWireframeStatus(GraphicsThree3D, wireframeStatus) {\n      GraphicsThree3D.isWireFrameOnly = !wireframeStatus;\n      GraphicsThree3D.ToggleWireframe();\n    }\n\n    getGraphicsParameters(GraphicsThree3D) {\n      let cameraZoom = GraphicsThree3D.camera.zoom;\n      let rendererSize = GraphicsThree3D.renderer.getSize(new this.THREE.Vector2());\n      return [cameraZoom, rendererSize.x, rendererSize.y];\n    }\n    /**\n     * Scale Canvas\n     */\n\n\n    scaleCanvas(frameData, frameProps, GraphicsThree3D) {\n      let [frameWidth, frameRatio] = this.getCameraWidthAndRatio(frameData, frameProps);\n      this.changeRendererSize(frameRatio, GraphicsThree3D);\n      this.zoomCamera(GraphicsThree3D.camera, frameRatio, frameWidth);\n    }\n\n    getCameraWidthAndRatio(frameData, frameProps) {\n      let lineWidth = frameProps.outerLine.thickness;\n      let frameWidth = frameData.width + lineWidth;\n      let frameHeight = frameData.height + lineWidth;\n      let scale = this.getScale(frameData);\n      let scaledWidth = frameWidth * scale;\n      let frameRatio = frameWidth / frameHeight;\n      return [scaledWidth, frameRatio];\n    }\n\n    changeRendererSize(frameRatio, GraphicsThree3D) {\n      const IMG_WIDTH = 2000;\n      let canvasSize = GraphicsThree3D.renderer.getSize(new this.THREE.Vector2());\n      let canvasWidth = canvasSize.x;\n      let canvasHeight = canvasSize.y;\n      let canvasRatio = canvasWidth / canvasHeight;\n      const QUALITY_SCALE = IMG_WIDTH / canvasWidth;\n      GraphicsThree3D.renderer.setSize(QUALITY_SCALE * canvasWidth * frameRatio / canvasRatio, QUALITY_SCALE * canvasHeight);\n    }\n\n    zoomCamera(camera, frameRatio, frameWidth) {\n      const MULTIPLIER = 16;\n      const CAMERA_VIEW_SIZE = 1000;\n      camera.left = -CAMERA_VIEW_SIZE * frameRatio / MULTIPLIER;\n      camera.right = CAMERA_VIEW_SIZE * frameRatio / MULTIPLIER;\n      camera.top = CAMERA_VIEW_SIZE / MULTIPLIER;\n      camera.bottom = -CAMERA_VIEW_SIZE / MULTIPLIER;\n      let cameraWidth = camera.right - camera.left;\n      camera.zoom = cameraWidth / frameWidth;\n      camera.updateProjectionMatrix();\n    }\n\n    drawDimensionalChains(dimensionalChainLoader, dimensionalChains, drawingData) {\n      let drawingNormal = this.utils.initVector3(drawingData.normalVector).normalize();\n      let drawingVerticalVec = this.utils.initVector3(drawingData.verticalVector).normalize();\n      let drawingHorizontalVec = new this.THREE.Vector3().crossVectors(drawingNormal, drawingVerticalVec).normalize();\n      let clippingPlane;\n\n      if (drawingData.type === 2) {\n        let planeDistance = this.getClippingPlaneDistance(drawingData, drawingNormal);\n        clippingPlane = new this.THREE.Plane(drawingNormal, planeDistance);\n      }\n\n      if (dimensionalChains) dimensionalChains.forEach(chain => {\n        let view2dAlignments = chain.alignments.views2d; ///\n\n        if (view2dAlignments) {\n          view2dAlignments.forEach(alignment => {\n            if (chain.alignments.textAlignment === 1) {\n              let pointsDir = this.getTextDirectionVec({\n                drawingVerticalVec,\n                drawingHorizontalVec\n              }, chain.referencePoints);\n              alignment.normalVector = drawingNormal.clone().negate();\n              alignment.directionVector = pointsDir;\n            }\n          }); ///\n\n          let chainData = { ...chain\n          };\n          chainData.alignments = { ...chain.alignments\n          };\n          chainData.alignments.textAlignment = 2;\n          view2dAlignments.forEach(alignment => {\n            let normalVector = new this.THREE.Vector3().crossVectors(alignment.offsetVector, alignment.directionVector).normalize();\n            let dotProdAbs = Math.abs(normalVector.dot(drawingNormal));\n\n            if (dotProdAbs === 1) {\n              let chains = dimensionalChainLoader.drawMaskette(chainData, alignment);\n              chains.forEach(ch => {\n                if (drawingData.type === 1 || drawingData.type === 2 && this.chainOnPlane(ch, clippingPlane)) this.GraphicsThree3D.scene.add(ch);\n              });\n            }\n          });\n        }\n      });\n      this.GraphicsThree3D.centerScene();\n      this.GraphicsThree3D.DimensionalChainIntersectionDetection.chainArr = [];\n    }\n\n    getTextDirectionVec(drawingSpaceVectors, chainReferencePoints) {\n      let {\n        drawingVerticalVec,\n        drawingHorizontalVec\n      } = drawingSpaceVectors;\n      let startPoint = this.utils.initVector3(chainReferencePoints[0]);\n      let endPoint = this.utils.initVector3(chainReferencePoints[chainReferencePoints.length - 1]);\n      let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n      let yDot = drawingVerticalVec.dot(pointsDir);\n\n      if (Math.abs(yDot) === 1) {\n        if (yDot < 0) pointsDir.negate();\n      } else {\n        let xDot = drawingHorizontalVec.dot(pointsDir);\n        if (xDot < 0) pointsDir.negate();\n      }\n\n      return pointsDir;\n    }\n\n    removeDimChains3D() {\n      this.GraphicsThree3D.visualisationControlObjects.dimensionalChain.forEach(chain => this.GraphicsThree3D.scene.remove(chain));\n      this.GraphicsThree3D.visualisationControlObjects.dimensionalChain = [];\n    }\n\n    chainOnPlane(chain, plane) {\n      let isOnPlane = true;\n      chain.traverse(child => {\n        if (plane.distanceToPoint(child.position) !== 0) isOnPlane = false;\n      });\n      return isOnPlane;\n    }\n    /**\n     * ScaleBar\n     */\n\n\n    generateScaleBarObject(scaleBarData, scaleBarProps, unitsLength, frameData, frame) {\n      let areaProps = scaleBarProps.area;\n      let lineProps = scaleBarProps.line;\n      let textProps = scaleBarProps.text;\n      let normalVector = new this.THREE.Vector3(0, 0, 1);\n      let directionVector = this.utils.initVector3(scaleBarData.directionVector).normalize();\n      let insertionPoint = this.utils.initVector3(scaleBarData.insertionPoint);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      insertionPoint.add(outerPoints[0]);\n      let scale = this.getScale(frameData);\n      let scaleBarSize = {\n        height: scaleBarData.height,\n        segmentWidth: 1\n      };\n      let scaleBarObject = new this.THREE.Object3D();\n      let scaleBarLine = this.drawScaleBarLine(scaleBarSize, lineProps);\n      scaleBarObject.add(scaleBarLine);\n      let scaleBarArea = this.drawScaleBarAreas(scaleBarSize, areaProps);\n      scaleBarObject.add(scaleBarArea);\n      this.drawScaleBarTexts(scaleBarData, scale, unitsLength, textProps, scaleBarObject);\n      scaleBarObject = this.alignScaleBar(scaleBarObject, scaleBarData);\n      this.utils.alignObjectWithVectors(scaleBarObject, normalVector, directionVector);\n      scaleBarObject.position.add(insertionPoint);\n      frame.add(scaleBarObject);\n    }\n\n    alignScaleBar(scaleBarObject, scaleBarData) {\n      let relativePosition = scaleBarData.relativePosition;\n      let alignedScaleBar = new this.THREE.Object3D();\n      alignedScaleBar.add(scaleBarObject);\n      let bbox = new this.THREE.Box3().setFromObject(scaleBarObject);\n      this.GraphicsThree3D.dxfGenerator.drawingScaleBarBBox = bbox.clone();\n      let bboxSize = bbox.getSize(new this.THREE.Vector3());\n      let centerOffset = bbox.getCenter(new this.THREE.Vector3());\n      scaleBarObject.children.forEach(child => child.position.sub(centerOffset));\n      let alignmentOffset = this.getScaleBarAlignmentOffset(bboxSize, relativePosition);\n      scaleBarObject.position.add(alignmentOffset);\n      return alignedScaleBar;\n    }\n\n    getScaleBarAlignmentOffset(bboxSize, relativePosition) {\n      let alignmentOffset = new this.THREE.Vector3(0, 0, 0);\n\n      switch (relativePosition) {\n        case 2:\n          alignmentOffset.set(bboxSize.x / 2, bboxSize.y / 2, 0);\n          break;\n\n        case 3:\n          alignmentOffset.set(bboxSize.x / 2, -bboxSize.y / 2, 0);\n          break;\n\n        case 4:\n          alignmentOffset.set(-bboxSize.x / 2, -bboxSize.y / 2, 0);\n          break;\n\n        case 5:\n          alignmentOffset.set(-bboxSize.x / 2, bboxSize.y / 2, 0);\n          break;\n\n        default:\n          break;\n      }\n\n      return alignmentOffset;\n    }\n\n    drawScaleBarTexts(scaleBarData, scale, unitsLength, textProps, scaleBarObject) {\n      const NUM_OF_SEGMENTS = 5;\n      const SEGMENT_WIDTH = 1;\n      const TEXT_RELATIVE_POSITION = 1;\n      let decimalPlaces = scaleBarData.decimalPlaces;\n      let distance = scaleBarData.distance;\n      let height = scaleBarData.height;\n      let textOffset = scaleBarData.textOffset;\n      let scaleFactor = this.getScaleFactor(unitsLength);\n      let heightOffset = -distance - textProps.size / 2;\n      let textData = {\n        content: \"\",\n        normalVector: {\n          x: 0,\n          y: 0,\n          z: 1\n        },\n        directionVector: {\n          x: 1,\n          y: 0,\n          z: 0\n        },\n        insertionPoint: {\n          x: 0,\n          y: heightOffset,\n          z: 0\n        },\n        relativePosition: TEXT_RELATIVE_POSITION\n      };\n\n      for (let i = 0; i <= NUM_OF_SEGMENTS; i++) {\n        textData.content = (i * scale * scaleFactor).toFixed(decimalPlaces);\n        if (i === 5 && unitsLength) textData.content += ` ${unitsLength}`;\n\n        if (textOffset) {\n          if (i % 2 === 1) textData.insertionPoint.y = height - heightOffset;else textData.insertionPoint.y = heightOffset;\n        }\n\n        textData.insertionPoint.x = i * SEGMENT_WIDTH;\n        let font = this.GraphicsThree3D.loadFont(textProps);\n        let text = this.GraphicsThree3D.drawText(textData, textProps, font);\n        scaleBarObject.add(text);\n      }\n    }\n\n    getScaleFactor(unit) {\n      let factor = 1;\n\n      switch (unit) {\n        case \"mm\":\n          factor *= 10;\n          break;\n\n        case \"m\":\n          factor /= 100;\n          break;\n\n        case \"km\":\n          factor /= 100000;\n          break;\n\n        default:\n          break;\n      }\n\n      return factor;\n    }\n\n    drawScaleBarAreas(scaleBarSize, areaProps) {\n      const NUM_OF_SEGMENTS = 5;\n      let height = scaleBarSize.height;\n      let segmentWidth = scaleBarSize.segmentWidth;\n      let totalWidth = NUM_OF_SEGMENTS * segmentWidth;\n      let geoOne = new this.THREE.PlaneGeometry(segmentWidth, height);\n      let geoTwo = geoOne.clone();\n      geoOne.translate(-segmentWidth, 0, 0);\n      geoTwo.translate(segmentWidth, 0, 0);\n      geoOne.merge(geoTwo);\n      geoOne.translate(totalWidth / 2, height / 2, 0);\n      let mat = this.utils.getMaterialFromColorProps(areaProps.color);\n      let area = new this.THREE.Mesh(geoOne, mat);\n      return area;\n    }\n\n    drawScaleBarLine(scaleBarSize, lineProps) {\n      const NUM_OF_SEGMENTS = 5;\n      let height = scaleBarSize.height;\n      let segmentWidth = scaleBarSize.segmentWidth;\n      let totalWidth = NUM_OF_SEGMENTS * segmentWidth;\n      let pts = [new this.THREE.Vector3(0, 0, 0), new this.THREE.Vector3(totalWidth, 0, 0), new this.THREE.Vector3(totalWidth, height), new this.THREE.Vector3(0, height, 0)];\n      let line = this.drawOutline(pts, lineProps, true);\n      this.addPointsToVertices(lineProps.thickness, lineProps.color, pts, line);\n      return line;\n    }\n    /**\n     * UnitTextBlock\n     */\n\n\n    drawUnitTextBlock(unitTextBlockData, unitTextBlockProps, units, frameData, frame) {\n      let font = this.GraphicsThree3D.loadFont(unitTextBlockProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      let content = this.generateUnitTextContent(unitTextBlockData, units);\n      let data = this.generateTextData(content, unitTextBlockData, zeroPoint);\n      let text = this.GraphicsThree3D.drawText(data, unitTextBlockProps, font);\n      frame.add(text);\n    }\n\n    generateUnitTextContent(unitTextBlockData, units) {\n      let content = \"\";\n      let startText = unitTextBlockData.startText;\n      let endText = unitTextBlockData.endText;\n      let separator = unitTextBlockData.separator;\n      content += startText;\n      let unitValues = Object.values(units);\n      unitValues.forEach((value, index) => {\n        if (index && value) content += separator;\n        if (value) content += value;\n      });\n      content += endText;\n      return content;\n    }\n    /**\n     * ScaleTextBlock\n     */\n\n\n    drawScaleTextBlock(scaleTextBlockData, scaleTextBlockProps, frameData, frame) {\n      let font = this.GraphicsThree3D.loadFont(scaleTextBlockProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      let decimals = scaleTextBlockData.decimalPlaces;\n      let scale = this.getScale(frameData);\n      let content = `${scaleTextBlockData.text} ${scale.toFixed(decimals)}`;\n      let data = this.generateTextData(content, scaleTextBlockData, zeroPoint);\n      let text = this.GraphicsThree3D.drawText(data, scaleTextBlockProps, font);\n      frame.add(text);\n    }\n    /**\n     * FreeTexts\n     */\n\n\n    drawFreeTexts(freeTexts, textProps, frameData, frame) {\n      let font = this.GraphicsThree3D.loadFont(textProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      freeTexts.forEach(textData => {\n        let content = textData.content;\n        let data = this.generateTextData(content, textData, zeroPoint);\n        let text = this.GraphicsThree3D.drawText(data, textProps, font);\n        frame.add(text);\n      });\n    }\n\n    generateTextData(content, textData, zeroPoint) {\n      let id = textData.id;\n      let relativePosition = textData.relativePosition;\n      let directionVector = this.utils.initVector3(textData.directionVector).normalize();\n      let insertionPoint = this.utils.initVector3(textData.insertionPoint);\n      insertionPoint.add(zeroPoint);\n      let data = {\n        content,\n        id,\n        relativePosition,\n        directionVector: {\n          x: directionVector.x,\n          y: directionVector.y,\n          z: directionVector.z\n        },\n        normalVector: {\n          x: 0,\n          y: 0,\n          z: 1\n        },\n        insertionPoint: {\n          x: insertionPoint.x,\n          y: insertionPoint.y,\n          z: insertionPoint.z\n        }\n      };\n      return data;\n    }\n    /**\n     * Frame\n     */\n\n\n    makeFrame(frameData, frameProps, drawingData) {\n      let normalVector = this.utils.initVector3(drawingData.normalVector).normalize().negate();\n      let verticalVector = this.utils.initVector3(drawingData.verticalVector).normalize().negate();\n      let frame = this.generateFrameObject(frameData, frameProps);\n      this.scaleFrame(frame, frameData);\n      this.utils.alignObjectWithVectors(frame, normalVector, new this.THREE.Vector3().crossVectors(normalVector, verticalVector));\n      frame.position.copy(this.getFramePosition(drawingData, this.GraphicsThree3D));\n      this.GraphicsThree3D.scene.add(frame);\n      return frame;\n    }\n\n    getScaleToFitScene(frameData, GraphicsThree3D) {\n      let normalVec = this.utils.initVector3(frameData.drawingNormal);\n      let upVec = this.utils.initVector3(frameData.drawingVerticalVector);\n      let crossVec = new this.THREE.Vector3().crossVectors(normalVec, upVec);\n      let bboxPoints = [new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ)];\n      let projectedPoints = this.projectPointsOnPlane(bboxPoints, normalVec);\n      let [verticalPoints, horizontalPoints] = this.getVerticalAndHorizontalPoints(projectedPoints, upVec, crossVec);\n      let [maxWidth, maxHeight] = this.getMaxWidthHeightProjected(verticalPoints, horizontalPoints, upVec, crossVec);\n      let frameWidth = frameData.width - frameData.border.left - frameData.border.right;\n      let frameHeight = frameData.height - frameData.border.top - frameData.border.bottom; // let scale;\n      // if (frameWidth < frameHeight) scale = maxWidth / frameWidth;\n      // else scale = maxHeight / frameHeight;\n      // let scale = maxWidth / frameWidth;\n\n      let scale = Math.max(maxWidth / frameWidth, maxHeight / frameHeight);\n      return scale;\n    }\n\n    getMaxWidthHeightProjected(verticalPoints, horizontalPoints, upVec, crossVec) {\n      let maxHeight = 0;\n      let maxWidth = 0;\n      const EPSILON = 0.0001;\n      verticalPoints.up.forEach(upPoint => verticalPoints.down.forEach(downPoint => {\n        let distance = upPoint.distanceTo(downPoint);\n        let dirVec = downPoint.clone().sub(upPoint).normalize();\n        if (distance > maxHeight && Math.abs(dirVec.dot(upVec)) > 1 - EPSILON) maxHeight = distance;\n      }));\n      horizontalPoints.right.forEach(rightPoint => horizontalPoints.left.forEach(leftPoint => {\n        let distance = rightPoint.distanceTo(leftPoint);\n        let dirVec = rightPoint.clone().sub(leftPoint).normalize();\n        if (distance > maxWidth && Math.abs(dirVec.dot(crossVec)) > 1 - EPSILON) maxWidth = distance;\n      }));\n      return [maxWidth, maxHeight];\n    }\n\n    projectPointsOnPlane(pointsArr, normalVec) {\n      let projectedPoints = [];\n      pointsArr.forEach(point => {\n        projectedPoints.push(point.projectOnPlane(normalVec));\n      });\n      return projectedPoints;\n    }\n\n    getVerticalAndHorizontalPoints(pointsArr, upVec, crossVec) {\n      let verticalPoints = {\n        up: [],\n        down: []\n      };\n      let horizontalPoints = {\n        right: [],\n        left: []\n      };\n      pointsArr.forEach(point => {\n        if (point.dot(upVec) > 0) verticalPoints.up.push(point);else verticalPoints.down.push(point);\n        if (point.dot(crossVec) > 0) horizontalPoints.right.push(point);else horizontalPoints.left.push(point);\n      });\n      return [verticalPoints, horizontalPoints];\n    }\n\n    getScale(frameData) {\n      let scaleType = frameData.scaleType;\n      let scale = 1;\n\n      if (scaleType === 1) {\n        scale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n      } else if (scaleType === 2) {\n        let scaleList = frameData.scaleList;\n        let minimumScale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n        let maximumZoom = Infinity;\n        scaleList.forEach(scaleStr => {\n          let scaleParams = scaleStr.split(\":\");\n          let newScale = parseFloat(scaleParams[1]) / parseFloat(scaleParams[0]);\n\n          if (newScale >= minimumScale && scale < maximumZoom) {\n            scale = newScale;\n            maximumZoom = scale;\n          }\n        });\n      }\n\n      return scale;\n    }\n\n    scaleFrame(frame, frameData) {\n      let scale = this.getScale(frameData);\n      frame.scale.set(scale, scale, scale);\n    }\n\n    getFramePosition(drawingData, GraphicsThree3D) {\n      let normalVector = this.utils.initVector3(drawingData.normalVector).normalize();\n      let framePos;\n\n      if (drawingData.positionVector) {\n        let positionVec = this.utils.initVector3(drawingData.positionVector);\n        let objMidVec = GraphicsThree3D.objectMidVector.clone();\n        let posDir = positionVec.clone().normalize();\n        let posDist = positionVec.length();\n        let proj = objMidVec.dot(posDir);\n\n        if (proj > posDist) {\n          let delta = proj - posDist;\n          let proportion = delta / proj;\n          framePos = objMidVec.sub(normalVector.clone().multiplyScalar(proportion));\n        } else {\n          let positionDirection = positionVec.clone().normalize();\n          let planeDistance = positionVec.length();\n          if (normalVector.dot(positionDirection) > 0) planeDistance *= -1;\n          let viewPlane = new this.THREE.Plane(normalVector, planeDistance);\n          let projectedPoint = new this.THREE.Vector3();\n          viewPlane.projectPoint(objMidVec, projectedPoint);\n          framePos = projectedPoint;\n        }\n      } else {\n        let midVec = GraphicsThree3D.objectMidVector.clone();\n        let bboxPoints = [new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ)].map(point => point.sub(midVec));\n        let viewPlane = new this.THREE.Plane(normalVector);\n        let minDistance = 0;\n        bboxPoints.forEach(point => {\n          let distance = viewPlane.distanceToPoint(point);\n          if (distance < minDistance) minDistance = distance;\n        });\n        framePos = midVec.add(normalVector.clone().multiplyScalar(minDistance));\n      }\n\n      return framePos;\n    }\n\n    generateFrameObject(frameData, frameProps) {\n      let innerLineProps = frameProps.innerLine;\n      let outerLineProps = frameProps.outerLine;\n      let areaProps = frameProps.area;\n      let [outerPoints, innerPoints] = this.getFramePoints(frameData);\n      let frameObject = new this.THREE.Object3D();\n      let plane = this.drawPlane(frameData, areaProps);\n      frameObject.add(plane);\n      let outerLine = this.drawOutline(outerPoints, outerLineProps, true);\n      frameObject.add(outerLine);\n      let innerLine = this.drawOutline(innerPoints, innerLineProps, true);\n      frameObject.add(innerLine);\n      this.drawFrameVertexPoints(outerPoints, innerPoints, frameProps, frameObject);\n      return frameObject;\n    }\n\n    drawOutline(points, lineProps) {\n      let firstLine = new this.THREE.Mesh();\n\n      if (lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined)) {\n        if (lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0) {\n          let lineMeshArray = [];\n\n          for (let i = 0; i < points.length - 1; i++) {\n            if (points[i].distanceTo(points[i + 1]) > 0) {\n              let lineMesh = this.utils.drawLine(points[i], points[i + 1], lineProps, true);\n              lineMeshArray.push(lineMesh);\n            }\n          }\n\n          if (points[points.length - 1].distanceTo(points[0]) > 0) {\n            let lineMesh = this.utils.drawLine(points[points.length - 1], points[0], lineProps, true);\n            lineMeshArray.push(lineMesh);\n          }\n\n          firstLine = lineMeshArray.length > 0 ? lineMeshArray.splice(0, 1)[0] : new this.THREE.Mesh();\n          let lineMaterial = firstLine.material;\n          firstLine.updateMatrixWorld();\n          firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n          for (let i = 0; i < lineMeshArray.length; i++) {\n            lineMeshArray[i].updateMatrixWorld();\n            lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n            firstLine.geometry.merge(lineMeshArray[i].geometry);\n          }\n\n          firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n        }\n      }\n\n      return firstLine;\n    }\n\n    getFramePoints(frameData) {\n      let width = frameData.width;\n      let height = frameData.height;\n      let borderLeft = frameData.border.left;\n      let borderRight = frameData.border.right;\n      let borderTop = frameData.border.top;\n      let borderBot = frameData.border.bottom;\n      let outerPoints = [new this.THREE.Vector3(-width / 2, -height / 2, 0), new this.THREE.Vector3(width / 2, -height / 2, 0), new this.THREE.Vector3(width / 2, height / 2, 0), new this.THREE.Vector3(-width / 2, height / 2, 0)];\n      let innerPoints = [new this.THREE.Vector3(-width / 2 + borderLeft, -height / 2 + borderBot, 0), new this.THREE.Vector3(width / 2 - borderRight, -height / 2 + borderBot, 0), new this.THREE.Vector3(width / 2 - borderRight, height / 2 - borderTop, 0), new this.THREE.Vector3(-width / 2 + borderLeft, height / 2 - borderTop, 0)];\n      return [outerPoints, innerPoints];\n    }\n\n    drawFrameVertexPoints(outerPoints, innerPoints, frameProps, frameObject) {\n      let outerColor = frameProps.outerLine.color;\n      let innerColor = frameProps.innerLine.color;\n      let outerSize = frameProps.outerLine.thickness;\n      let innerSize = frameProps.innerLine.thickness;\n      if (outerSize) this.addPointsToVertices(outerSize, outerColor, outerPoints, frameObject);\n      if (innerSize) this.addPointsToVertices(innerSize, innerColor, innerPoints, frameObject);\n    }\n\n    addPointsToVertices(size, color, points, frameObject) {\n      let material = this.utils.getMaterialFromColorProps(color);\n      points.forEach(point => {\n        let mesh = this.drawPoint(point, size, material);\n        frameObject.add(mesh);\n      });\n    }\n\n    drawPoint(position, size, material) {\n      let geometry = new this.THREE.BoxBufferGeometry(size, size, size);\n      let mesh = new this.THREE.Mesh(geometry, material);\n      mesh.position.copy(position);\n      return mesh;\n    }\n\n    drawPlane(frameData, areaProps) {\n      let width = frameData.width;\n      let height = frameData.height;\n      let planeWidth = width;\n      let planeHeight = height;\n      let planeGeo = new this.THREE.PlaneBufferGeometry(planeWidth, planeHeight);\n      let planeMat = this.utils.getMaterialFromColorProps(areaProps.color);\n      planeMat.depthWrite = false;\n      let plane = new this.THREE.Mesh(planeGeo, planeMat);\n      return plane;\n    }\n    /**\n     * Drawing\n     */\n\n\n    makeDrawing(drawingData, drawingProps) {\n      if (drawingData.type === 1 || drawingData.type === 2) {\n        let normalVector = this.utils.initVector3(drawingData.normalVector).normalize();\n        let verticalVector = this.utils.initVector3(drawingData.verticalVector).normalize();\n        let scene = this.GraphicsThree3D.scene;\n        this.positionCamera(normalVector.clone().negate(), verticalVector);\n\n        if (drawingData.type === 2) {\n          let planeDistance = this.getClippingPlaneDistance(drawingData, normalVector);\n          let clippingPlane = new this.THREE.Plane(normalVector, planeDistance);\n          this.clipSceneObjects(scene, clippingPlane);\n        }\n\n        this.changeObjectsTransparency(this.GraphicsThree3D.transparencyControlObjects, drawingProps.transparency);\n        this.changeObjectsVisualisation(this.GraphicsThree3D.visualisationControlObjects, drawingProps.visualisation);\n      }\n    }\n\n    positionCamera(normalVector, verticalVector) {\n      let camera = this.GraphicsThree3D.camera;\n      let midVector = this.GraphicsThree3D.objectMidVector.clone();\n      let cameraDistance = 150;\n      let cameraPos = normalVector.clone().multiplyScalar(cameraDistance).add(midVector);\n      let upGenObj = new this.THREE.Object3D();\n      let upVector = this.GraphicsThree3D.getMeshUpVector(upGenObj, normalVector.clone());\n      let sceneRotAngle = upVector.angleTo(verticalVector);\n      camera.position.set(0, 0, 0);\n      camera.lookAt(new this.THREE.Vector3(0, 0, 1));\n      camera.rotation.z = sceneRotAngle;\n      camera.up = verticalVector;\n      camera.position.copy(cameraPos);\n      camera.lookAt(midVector);\n      camera.zoom = 1;\n      camera.updateProjectionMatrix();\n    }\n\n    getClippingPlaneDistance(drawingData, normalVector) {\n      let positionVector = this.utils.initVector3(drawingData.positionVector);\n      let positionDirection = positionVector.clone().normalize();\n      let planeDistance = positionVector.length();\n      if (normalVector.dot(positionDirection) > 0) planeDistance *= -1;\n      return planeDistance;\n    }\n\n    changeObjectsTransparency(objects, transparencyProps) {\n      for (let [key, value] of Object.entries(objects)) {\n        value.forEach(mesh => {\n          let material = mesh.material;\n          let transparency = transparencyProps[key] / 255.0;\n          if (material.length) material.forEach(mat => mat.opacity = transparency);else material.opacity = transparency;\n        });\n      }\n    }\n\n    changeObjectsVisualisation(objects, visualisationProps) {\n      for (let [key, value] of Object.entries(objects)) {\n        value.forEach(object => {\n          let visualisation = visualisationProps[key];\n          object.visible = visualisation;\n        });\n      }\n    }\n\n    clipSceneObjects(scene, clippingPlane) {\n      let objectIDs = {};\n      scene.traverse(object => {\n        if (object.type === \"Mesh\" || object.type === \"LineLoop\") {\n          let material = object.material;\n          this.clipMaterial(material, clippingPlane);\n          if (!object.isDrawingObject) this.clipMaterialBack(material, clippingPlane);\n          if (object.type === \"Mesh\") this.cutMesh(object, objectIDs, clippingPlane);\n        }\n      });\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    clipMaterialBack(material, plane) {\n      const OFFSET = 1;\n      let backPlane = plane.clone();\n      backPlane.normal.negate();\n      backPlane.constant = -backPlane.constant + OFFSET;\n      material.clippingPlanes.push(backPlane);\n    }\n\n    cutMesh(mesh, ids, plane) {\n      if (mesh.isDrawingObject) {\n        const ERASER_OFFSET = 1;\n        let eraserSize = 500;\n\n        if (!(mesh.uuid in ids)) {\n          ids[mesh.uuid] = 1;\n          let eraserPos = plane.normal.clone().multiplyScalar(-plane.constant + eraserSize / 2 + ERASER_OFFSET);\n          let eraserMesh = this.utils.makeTestMesh(eraserPos, eraserSize);\n          eraserMesh.updateMatrix();\n          let meshCSG = this.GraphicsThree3D.CSG.fromMesh(mesh);\n          let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserMesh);\n          let subCSG = meshCSG.subtract(eraserCSG);\n          mesh.geometry = this.GraphicsThree3D.CSG.toMesh(subCSG, new this.THREE.Matrix4()).geometry;\n        }\n      }\n    }\n\n    getStencilMaterials() {\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n      let backFaceStencilMat = new this.THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = this.THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = this.THREE.BackSide;\n      backFaceStencilMat.stencilFail = this.THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = this.THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = this.THREE.IncrementWrapStencilOp; // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      let frontFaceStencilMat = new this.THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = this.THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = this.THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = this.THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = this.THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = this.THREE.DecrementWrapStencilOp; // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      let planeStencilMat = new this.THREE.MeshNormalMaterial();\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = this.THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = this.THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = this.THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = this.THREE.ReplaceStencilOp;\n      return [backFaceStencilMat, frontFaceStencilMat, planeStencilMat];\n    }\n\n  }\n\n  _exports.DrawingPlaneLoader = DrawingPlaneLoader;\n\n  function saveSVG(GraphicsThree3D) {\n    let rendererSize = GraphicsThree3D.renderer.getSize(new GraphicsThree3D.THREE.Vector2());\n    var rendererSVG = new _SVGRenderer.SVGRenderer();\n    rendererSVG.setSize(rendererSize.x, rendererSize.y);\n    rendererSVG.render(GraphicsThree3D.scene, GraphicsThree3D.camera);\n    console.log(rendererSVG.domElement);\n    var XMLS = new XMLSerializer();\n    var svgfile = XMLS.serializeToString(rendererSVG.domElement);\n    console.log(svgfile);\n    var svgData = svgfile;\n    var preface = '<?xml version=\"1.0\" standalone=\"no\"?>\\r\\n';\n    var svgBlob = new Blob([preface, svgData], {\n      type: \"image/svg+xml;charset=utf-8\"\n    });\n    var svgUrl = URL.createObjectURL(svgBlob);\n    var downloadLink = document.createElement(\"a\");\n    downloadLink.href = svgUrl;\n    downloadLink.download = \"drawing.svg\";\n    document.body.appendChild(downloadLink);\n    downloadLink.click();\n    document.body.removeChild(downloadLink); // let parsed = parse(svgfile);\n    // console.log(parsed)\n    // let data = \"text/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(parsed))\n    // let link = document.createElement(\"a\");\n    // link.href = \"data: \" + data;\n    // link.download = \"svg_data.json\";\n    // link.click();\n    // let testMesh = this.utils.makeTestMesh(new this.THREE.Vector3(), 10);\n    // let exporter = new STLExporter();\n    // let stlData = exporter.parse(testMesh, { binary: true });\n    // console.log(stlData)\n    // // let link = document.createElement(\"a\");\n    // // link.style.display = \"none\";\n    // // document.body.appendChild(link);\n    // let blob = new Blob( [stlData], {type: \"application/octet-stream\"} );\n    // // link.href = URL.createObjectURL(blob);\n    // // link.download = \"testBox.stl\";\n    // // link.click();\n    // let deserializedStl = jscadIO.deserializers.stl(stlData, blob, {\n    //   output: \"csg\"\n    // });\n    // let dxfStringArray = jscadIO.dxfSerializer.serialize(deserializedStl);\n    // console.log(dxfStringArray)\n  }\n});","define(\"m08-2020/lib old/FigureLoaders/FastenerLoader\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.FastenerLoader = void 0;\n\n  class FastenerLoader {\n    constructor(THREE, GraphicsThree3D) {\n      this.THREE = THREE;\n      this.CSG = GraphicsThree3D.CSG;\n      this.fontUrlArray = GraphicsThree3D.fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = GraphicsThree3D.utils;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator; ///\n\n      this.drawFasteners = this.drawFasteners.bind(this);\n    }\n\n    drawFasteners() {\n      let jsonData = this.GraphicsThree3D.jsonData;\n      let dxfFastenerPoints = this.GraphicsThree3D.dxfGenerator.fastenerPoints;\n\n      for (let i = 0; i < jsonData.fasteners.length; i++) {\n        this.addToDXFPoints(dxfFastenerPoints, jsonData.fasteners[i]);\n\n        if (jsonData.fasteners[i].components.head && jsonData.fasteners[i].components.head.type > 8) {\n          this.drawClamp(jsonData.fasteners[i]);\n        } else {\n          let meshArray = this.drawFastener(jsonData.fasteners[i]); // meshArray.forEach(obj => this.addToDimChainCollisionDetection(obj));\n\n          for (let a = 0; a < meshArray.length; a++) {\n            this.setTime(meshArray, i, a);\n          }\n        }\n      }\n    }\n\n    addToDXFPoints(dxfFastenerPoints, fastenerData) {\n      if (fastenerData.components.axis && fastenerData.properties.axis && fastenerData.properties.axis.line && fastenerData.properties.axis.point) {\n        let axisLength = fastenerData.components.axis.length;\n        dxfFastenerPoints[fastenerData.id] = {\n          axis: [],\n          addon: []\n        };\n        fastenerData.insertionPoints.forEach(insertionPoint => {\n          let directionVec = this.utils.initVector3(insertionPoint.directionVector1);\n          let startPoint = this.utils.initVector3(insertionPoint.insertionPoint);\n          let endPoint = startPoint.clone().add(directionVec.multiplyScalar(axisLength));\n          let dxfData = {\n            pointPair: [startPoint, endPoint],\n            properties: {\n              line: fastenerData.properties.axis.line,\n              point: fastenerData.properties.axis.point\n            }\n          };\n          dxfFastenerPoints[fastenerData.id].axis.push(dxfData);\n        });\n      }\n    }\n\n    addToDimChainCollisionDetection(obj) {\n      obj.updateMatrix();\n      obj.traverse(child => {\n        if (child.type === \"Mesh\" && !(child.geometry.faceVertexUvs && child.geometry.faceVertexUvs[0].length === 0)) {\n          let intersectionObject = child.clone();\n          intersectionObject.applyMatrix4(obj.matrix);\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(intersectionObject);\n          this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n        }\n      });\n    }\n\n    drawFastener(fastener) {\n      let meshArray = [];\n\n      if (fastener.components.axis) {\n        let axisData = fastener.components.axis;\n        let shankData = fastener.components.shank;\n        let headData = fastener.components.head;\n        let threadsData = fastener.components.threads;\n        let tipData = fastener.components.tip;\n        let fastenerProps = fastener.properties; // Axis\n\n        let axisGeo, axisPointGeo;\n        let axisMat, axisPointMat;\n        let inversedDirection = false;\n\n        if (axisData.length < 0) {\n          inversedDirection = true;\n          axisData.length *= -1; // maybe change code not to modify json?\n        }\n\n        if (fastenerProps.axis) {\n          if (axisData.length > 0) {\n            let lineProps = fastenerProps.axis.line;\n            if (lineProps.thickness && lineProps.type > 0) axisGeo = this.getAxisGeo(axisData, fastenerProps.axis);\n            axisMat = this.GraphicsThree3D.getMaterialFromColor(lineProps.color);\n          }\n\n          let pointProps = fastenerProps.axis.point;\n          if (pointProps.size) axisPointGeo = this.getAxisPointGeo(pointProps);\n          axisPointMat = this.GraphicsThree3D.getMaterialFromColor(pointProps.color);\n        }\n\n        let headSideLineProps = fastenerProps.fastener.headSide.line;\n        let fastenerMaterial = this.getMaterial(fastener.properties, \"fastener\");\n        let lineMaterial = this.getLineMaterial(headSideLineProps);\n        let headSideMeshes = [];\n        let tipSideMeshes = []; // Heads\n\n        let headGeo = this.getFastenerHeadGeo(headData);\n        let head = this.drawFastenerHead(headData, headGeo, fastenerMaterial);\n        if (head) headSideMeshes.push(head);\n        let drawBottomFrameHead = true;\n\n        if (shankData && head) {\n          if (headData.type == 1 && (shankData.type == 1 || shankData.type == 2 && shankData.size >= headData.bottomDiameter.value)) drawBottomFrameHead = false;\n        }\n\n        let headWireframes;\n        if (headSideLineProps && head) headWireframes = this.makeHeadWireframe(headGeo, headSideLineProps, drawBottomFrameHead);\n\n        if (threadsData) {\n          let threadMaxPoint = threadsData[threadsData.length - 1].insertionDistance + threadsData[threadsData.length - 1].length;\n          if (axisData.length == 0 || threadMaxPoint > axisData.length) axisData.length = threadMaxPoint;\n        }\n\n        let segmentsLeft = [[0, axisData.length]];\n\n        if (head && headData && (headData.type == 1 || headData.type == 2 || headData.type == 6)) {\n          segmentsLeft[0][0] = headData.height.value;\n        }\n\n        if (tipData && tipData.type && tipData.height && (fastenerMaterial || fastenerProps.fastener.tipSide)) {\n          segmentsLeft[0][1] -= tipData.height;\n        }\n\n        let threadSegments = [];\n        if (threadsData) threadsData.forEach(thread => {\n          let minPoint = thread.insertionDistance;\n          let maxPoint = thread.insertionDistance + thread.length;\n\n          if (minPoint < segmentsLeft[0][0]) {\n            minPoint = segmentsLeft[0][0];\n          }\n\n          if (maxPoint > segmentsLeft[0][1]) {\n            maxPoint = segmentsLeft[0][1];\n          }\n\n          let threadParams = {\n            coreDiameter: thread.coreDiameter,\n            distance: thread.distance,\n            height: thread.height,\n            pitch: thread.pitch,\n            threadDiameter: thread.threadDiameter\n          };\n          threadSegments.push([minPoint, maxPoint, threadParams]);\n        });\n        threadSegments.forEach(threadSegment => {\n          let minPoint = threadSegment[0];\n          let maxPoint = threadSegment[1];\n          segmentsLeft.forEach(segment => {\n            if (minPoint > segment[0] && minPoint < segment[1] && maxPoint > segment[0] && maxPoint < segment[1]) {\n              segmentsLeft.push([maxPoint, segment[1]]);\n              segment[1] = minPoint;\n            } else {\n              if (minPoint > segment[0] && minPoint < segment[1]) segment[1] = minPoint;\n              if (maxPoint > segment[0] && maxPoint < segment[1]) segment[0] = maxPoint;\n            }\n          });\n        });\n        let tipDiameter;\n\n        if (threadSegments.length > 0 && segmentsLeft.length > 0) {\n          let lastThreadMax = threadSegments[threadSegments.length - 1][1];\n          let lastShankMax = segmentsLeft[segmentsLeft.length - 1][1];\n          let threadDiameter = threadSegments[threadSegments.length - 1][2].coreDiameter;\n          let shankSize = shankData ? shankData.size : 0;\n          if (lastThreadMax > lastShankMax) tipDiameter = threadDiameter;else if (!shankSize) tipDiameter = threadDiameter;else tipDiameter = shankSize;\n        } else if (threadSegments.length > 0) {\n          tipDiameter = threadSegments[threadSegments.length - 1][2].coreDiameter;\n        } else if (segmentsLeft.length > 0) {\n          tipDiameter = shankData.size;\n        }\n\n        if (tipDiameter == 0) {\n          tipDiameter = headData.bottomDiameter.value;\n        }\n\n        let tipSideProps = fastenerProps.fastener.tipSide;\n        let tipSideMaterial;\n\n        if (shankData && tipSideProps && shankData.tipSideLength) {\n          tipSideMaterial = this.getMaterial(fastenerProps, \"tipSide\");\n          let tipSideStartPoint = axisData.length - shankData.tipSideLength;\n          segmentsLeft.forEach((segment, index) => {\n            let minPoint = segment[0];\n            let maxPoint = segment[1];\n\n            if (tipSideStartPoint > minPoint && tipSideStartPoint < maxPoint) {\n              segmentsLeft.splice(index + 1, 0, [tipSideStartPoint, maxPoint, true]);\n              segment[1] = tipSideStartPoint;\n              segment[2] = false;\n            } else if (tipSideStartPoint < minPoint && tipSideStartPoint < maxPoint) {\n              segment[2] = true;\n            } else {\n              segment[2] = false;\n            }\n          });\n        }\n\n        let shankGeo = this.getFastenerShankGeo(segmentsLeft, fastener.components.shank, fastener);\n        let tipGeo = this.getFastenerTipGeo(fastener.components.tip, tipDiameter, fastener, axisData.length);\n        let isFastenerWithPitch = false;\n        let isFastenerWithoutPitch = false;\n        let cylGeo, spiralGeo, withoutPitchGeo, withoutPitchCylGeo, cylWireframes, coreWireframes;\n        let wireframeWithoutPitchPts, wireframeWithPitchPts;\n        let intersectionThreadMeshes = [];\n\n        if (threadsData) {\n          threadsData.forEach(thread => {\n            if (thread.pitch && !thread.distance) isFastenerWithPitch = true;else if (!thread.pitch && thread.distance) isFastenerWithoutPitch = true;\n          });\n          intersectionThreadMeshes.push(...this.getThreadIntersectionBoxes(threadSegments));\n\n          if (isFastenerWithPitch) {\n            [cylGeo, spiralGeo, wireframeWithPitchPts, cylWireframes] = this.getSpiralAndCylGeo(threadSegments, fastenerProps);\n          }\n\n          if (isFastenerWithoutPitch) {\n            [withoutPitchGeo, withoutPitchCylGeo, wireframeWithoutPitchPts, coreWireframes] = this.getThreadWithoutPitchGeo(threadSegments, fastenerProps);\n          }\n        } // Axis\n\n\n        let axis;\n        if (fastenerProps.axis) axis = this.drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat, fastener.properties.axis); // Threads\n\n        let threads = [];\n\n        if (threadsData) {\n          if (isFastenerWithPitch) {\n            let [cylinder, spiral] = this.drawFastenerThread_0_WithPitch(cylGeo, spiralGeo, fastenerMaterial, wireframeWithPitchPts, headSideLineProps, lineMaterial);\n            if (cylinder) headSideMeshes.push(cylinder);\n            threads.push(spiral);\n          }\n\n          if (isFastenerWithoutPitch) {\n            let [thread, core] = this.drawFastenerThread_1_WithoutPitch(withoutPitchGeo, withoutPitchCylGeo, fastenerMaterial, wireframeWithoutPitchPts, headSideLineProps);\n            threads.push(thread);\n            if (core) headSideMeshes.push(core);\n          }\n        } // Tip\n\n\n        let drawTopFrameShank = true;\n        let drawBotFrameShank = true;\n        let tipWireframes;\n        let tip = this.drawFastenerTip(tipGeo, fastenerMaterial, tipSideMaterial);\n\n        if (tip) {\n          let drawTopTip = true;\n\n          if (shankData && segmentsLeft[segmentsLeft.length - 1][1] == axisData.length - tipData.height) {\n            if (shankData.type == 1 && (tipData.type == 1 || tipData.type == 2)) {\n              drawTopTip = false;\n              drawBotFrameShank = false;\n            } else if (shankData.type == 2 && (tipData.type == 3 || tipData.type == 4 || tipData.type == 5 || tipData.type == 6)) {\n              drawTopTip = false;\n              drawBotFrameShank = false;\n            }\n          } else if (threadsData && threadSegments && threadSegments[threadSegments.length - 1][1] == axisData.length - tipData.height) {\n            if (tipData.type == 1 || tipData.type == 2) drawTopTip = false;\n          }\n\n          tipWireframes = this.makeTipWireframe(tipData, tipGeo, tipSideProps && shankData.tipSideLength ? tipSideProps.line : headSideLineProps, drawTopTip);\n          if (tip.isTipSide) tipSideMeshes.push(tip);else headSideMeshes.push(tip);\n        }\n\n        if (shankData && head) {\n          let diameterToCompare = headData.bottomDiameter.value !== 0 ? headData.bottomDiameter.value : headData.topDiameter.value;\n          if (headData.wrenchSize) diameterToCompare = headData.wrenchSize;\n\n          if (shankData.type == 1 && diameterToCompare >= shankData.size) {\n            drawTopFrameShank = false;\n          }\n        } // Shank\n\n\n        let [shanks, shankWireframes] = this.drawFastenerShank(fastener.components.shank, fastener.properties, shankGeo, drawTopFrameShank, drawBotFrameShank);\n        shanks.headSide.forEach(shank => {\n          if (shank) headSideMeshes.push(shank);\n        });\n        shanks.tipSide.forEach(shank => {\n          if (shank) tipSideMeshes.push(shank);\n        });\n        let headSideMesh = headSideMeshes.splice(0, 1)[0];\n        let tipSideMesh = tipSideMeshes.splice(0, 1)[0];\n        headSideMeshes.forEach(mesh => {\n          if (mesh) {\n            let firstCSG = this.CSG.fromMesh(headSideMesh);\n            let secondCSG = this.CSG.fromMesh(mesh);\n            let unionCSG = firstCSG.union(secondCSG);\n            let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n            headSideMesh = unionMesh;\n          }\n        });\n        tipSideMeshes.forEach(mesh => {\n          if (mesh) {\n            let firstCSG = this.CSG.fromMesh(tipSideMesh);\n            let secondCSG = this.CSG.fromMesh(mesh);\n            let unionCSG = firstCSG.union(secondCSG);\n            let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n            tipSideMesh = unionMesh;\n          }\n        });\n        if (headSideMesh && fastenerMaterial) headSideMesh.material = fastenerMaterial;\n        if (tipSideMesh && tipSideMaterial) tipSideMesh.material = tipSideMaterial;\n        if (headSideMesh) headSideMesh.material.side = this.THREE.FrontSide;\n        if (tipSideMesh) tipSideMesh.material.side = this.THREE.FrontSide;\n        let backSideHead, backSideTip;\n\n        if (headSideMesh) {\n          let geo = headSideMesh.geometry.clone();\n          let mat = headSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideHead = new this.THREE.Mesh(geo, mat);\n        }\n\n        if (tipSideMesh) {\n          let geo = tipSideMesh.geometry.clone();\n          let mat = tipSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideTip = new this.THREE.Mesh(geo, mat);\n        }\n\n        for (let b = 0; b < fastener.insertionPoints.length; b++) {\n          let intersectionPoint = fastener.insertionPoints[b];\n          let insertionPoint = intersectionPoint.insertionPoint;\n          let insertionVector = new this.THREE.Vector3(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n          let rotationVector = this.utils.normalizeVector(intersectionPoint.directionVector1);\n          let rotationVectorTwo = this.utils.normalizeVector(intersectionPoint.directionVector2);\n          let objectFastener = new this.THREE.Object3D();\n          let clonedHeadSide, clonedTipSide;\n\n          if (headSideMesh) {\n            clonedHeadSide = headSideMesh.clone();\n            objectFastener.add(clonedHeadSide);\n          }\n\n          if (tipSideMesh) {\n            clonedTipSide = tipSideMesh.clone();\n            objectFastener.add(clonedTipSide);\n          }\n\n          if (axis) objectFastener.add(axis.clone());\n          threads.forEach(thread => {\n            if (thread) objectFastener.add(thread.clone());\n          });\n\n          if (backSideHead) {\n            backSideHead = backSideHead.clone();\n            objectFastener.add(backSideHead);\n          }\n\n          if (backSideTip) {\n            backSideTip = backSideTip.clone();\n            objectFastener.add(backSideTip);\n          } // AddOns\n\n\n          let [addOns, addOnDxfPointsData] = this.generateAddons(fastener.components.addOns, fastenerProps.addOn);\n          addOns.forEach(addOn => {\n            if (addOn) {\n              addOn.renderOrder = -2;\n              addOn.isAddOn = true;\n              let backSide = this.drawBacksideMesh(addOn);\n              backSide.renderOrder = -3;\n              addOn.add(backSide);\n              objectFastener.add(addOn);\n            }\n          }); ///\n\n          intersectionThreadMeshes.forEach(mesh => objectFastener.add(mesh.clone())); ///\n\n          if (headWireframes) headWireframes.forEach(wireframeArr => wireframeArr.forEach(mesh => {\n            objectFastener.add(mesh.clone());\n          }));\n          if (shankWireframes) shankWireframes.forEach(wireframe => wireframe.forEach(segment => {\n            objectFastener.add(segment.clone());\n          }));\n          if (tipWireframes) tipWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => {\n            objectFastener.add(wireframe.clone());\n          }));\n          if (cylWireframes) cylWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => wireframe.forEach(line => objectFastener.add(line.clone()))));\n          if (coreWireframes) coreWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => wireframe.forEach(line => objectFastener.add(line.clone()))));\n          let directionVector = new this.THREE.Vector3(rotationVector.x, rotationVector.y, rotationVector.z);\n          let directionVectorTwo = new this.THREE.Vector3(rotationVectorTwo.x, rotationVectorTwo.y, rotationVectorTwo.z);\n\n          if (inversedDirection) {\n            directionVector.negate();\n            directionVectorTwo.negate();\n          }\n\n          if (clonedTipSide) {\n            let objects = {\n              clonedTipSide,\n              backSideTip,\n              clonedHeadSide,\n              backSideHead\n            };\n            let fastenerProps = {\n              axisData,\n              shankData,\n              directionVector,\n              insertionVector\n            };\n            this.removeHeadTipConnection(objects, fastenerProps);\n          }\n\n          objectFastener.children.forEach(child => {\n            if (!child.isAddOn) child.renderOrder = -4;\n          });\n          if (backSideHead) backSideHead.renderOrder = -5;\n          if (backSideTip) backSideTip.renderOrder = -5;\n          this.rotateObject(objectFastener, directionVector, directionVectorTwo);\n          objectFastener.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n          meshArray.push(objectFastener);\n          this.GraphicsThree3D.addObjectToVisControl(objectFastener, \"fastener\"); // Might need better solution\n\n          objectFastener.updateMatrix();\n          addOnDxfPointsData.forEach(ptData => {\n            if (ptData) {\n              ptData.points.forEach(pts => pts.forEach(pt => pt.applyMatrix4(objectFastener.matrix)));\n              this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id].addon.push(ptData);\n            }\n          });\n        }\n      }\n\n      return meshArray;\n    }\n\n    generateAddons(addOnsDataArr, addOnProps) {\n      let addOns = [];\n      let addOnDxfPointsData = [];\n\n      if (addOnsDataArr) {\n        addOnsDataArr.forEach(addOn => {\n          if (addOn.type == 1) {\n            let [mesh, dxfData] = this.drawPolygonNut(addOn, addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else if (addOn.type == 2) {\n            let [mesh, dxfData] = this.drawCircleWasher(addOn, addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else if (addOn.type == 3) {\n            let [mesh, dxfData] = this.drawRectangleWasher(addOn, addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else if (addOn.type == 4) {\n            let [mesh, dxfData] = this.drawAngleWasherTypeA(addOn, addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else if (addOn.type == 5) {\n            let [mesh, dxfData] = this.drawAngleWasherTypeB(addOn, addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else if (addOn.type == 6) {\n            let [mesh, dxfData] = this.drawFtConnector(addOnProps);\n            addOns.push(mesh);\n            addOnDxfPointsData.push(dxfData);\n          } else console.log(\"AddOn type undefined\");\n        });\n      }\n\n      return [addOns, addOnDxfPointsData];\n    }\n\n    removeHeadTipConnection(objects, fastenerProps) {\n      let axisData = fastenerProps.axisData;\n      let shankData = fastenerProps.shankData;\n      let directionVector = fastenerProps.directionVector;\n      let insertionVector = fastenerProps.insertionVector;\n      let clonedTipSide = objects.clonedTipSide;\n      let backSideTip = objects.backSideTip;\n      let clonedHeadSide = objects.clonedHeadSide;\n      let backSideHead = objects.backSideHead;\n      let shankLength = axisData.length;\n      let tipOffset = shankData.tipSideLength;\n      let planeVector = directionVector.clone().normalize();\n      let positionVector = insertionVector.clone().projectOnVector(planeVector);\n      let positionDistance = -(positionVector.length() + shankLength - tipOffset + 0.0001);\n      let clippingPlane = new this.THREE.Plane(planeVector, positionDistance);\n      clonedTipSide.material.clippingPlanes = [clippingPlane];\n      backSideTip.material.clippingPlanes = [clippingPlane];\n\n      if (clonedHeadSide) {\n        let shankLength = axisData.length;\n        let tipOffset = shankData.tipSideLength;\n        let planeVector = directionVector.clone().negate().normalize();\n        let positionVector = insertionVector.projectOnVector(planeVector);\n        let positionDistance = positionVector.length() + shankLength - tipOffset - 0.0001;\n        let clippingPlane = new this.THREE.Plane(planeVector, positionDistance);\n        clonedHeadSide.material.clippingPlanes = [clippingPlane];\n        backSideHead.material.clippingPlanes = [clippingPlane];\n      }\n    }\n\n    drawDoublesideFrame(geo, lineProps, drawTop = true, drawBot = true, drawEdges = false) {\n      let partOne = [];\n      let partTwo = [];\n      let wireframes = [];\n      let geoVertices = geo.vertices;\n      let firstPart = geoVertices[0].y;\n      geoVertices.forEach(vertex => {\n        if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n      });\n\n      if (geo.type == \"CylinderGeometry\") {\n        partOne.splice(partOne.length - 1, 1);\n        partTwo.splice(partTwo.length - 1, 1);\n      }\n\n      if (drawTop && partOne.length > 0) {\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        wireframes.push([wireframeOne]);\n      }\n\n      if (drawBot && partTwo.length > 0) {\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        wireframes.push([wireframeTwo]);\n      }\n\n      if (drawEdges) {\n        let edgesLineArr = this.drawEdgesFrame(partOne, partTwo, lineProps);\n        wireframes.push(edgesLineArr);\n      }\n\n      return wireframes;\n    }\n\n    drawEdgesFrame(partOne, partTwo, lineProps) {\n      // partOne must be array, partTwo can be vector\n      let edgesLineArr = [];\n\n      if (partOne.length > 0) {\n        if (partOne.length == partTwo.length) {\n          for (let i = 0; i < partOne.length; i++) {\n            let line = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(line);\n          }\n        } else {// TODO: vector as point\n        }\n      }\n\n      return edgesLineArr;\n    }\n\n    getThreadIntersectionBoxes(threadSegments) {\n      let boxArr = [];\n      threadSegments.forEach(segmentData => {\n        let boxHeight = segmentData[1] - segmentData[0];\n        let boxWidth = segmentData[2].threadDiameter;\n        let boxGeo = new this.THREE.BoxGeometry(boxWidth, boxHeight, boxWidth);\n        boxGeo.translate(0, -boxHeight / 2 - segmentData[0], 0);\n        let boxMat = new this.THREE.MeshBasicMaterial();\n        let box = new this.THREE.Mesh(boxGeo, boxMat);\n        box.visible = false;\n        boxArr.push(box);\n      });\n      return boxArr;\n    }\n\n    drawFastenerHead(headData, headGeo, fastenerMaterial) {\n      let fastenerHead;\n\n      if (headData) {\n        if (headData.type == 1 || headData.type == 2) {\n          fastenerHead = this.drawFastenerHead_1_Countersunk(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 3) {\n          fastenerHead = this.drawFastenerHead_3_Lentil(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 4) {\n          fastenerHead = this.drawFastenerHead_4_DiscFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 5) {\n          fastenerHead = this.drawFastenerHead_5_DiscBottomFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 6) {\n          fastenerHead = this.drawFastenerHead_6_DiscTopFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 7) {\n          fastenerHead = this.drawFastenerHead_7_PolygonWithoutWasher(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 8) {\n          fastenerHead = this.drawFastenerHead_8_PolygonWithWasher(headGeo, fastenerMaterial, headData);\n        } else {\n          console.log(\"Head type undefined\");\n        }\n      } else {\n        fastenerHead = null;\n      }\n\n      return fastenerHead;\n    }\n\n    drawBacksideMesh(mesh) {\n      let geo = mesh.geometry.clone();\n      let mat = mesh.material.clone();\n      mat.side = this.THREE.BackSide;\n      let backsideMesh = new this.THREE.Mesh(geo, mat);\n      return backsideMesh;\n    }\n\n    getAddonDxfData_PolygonWireframe(addonMesh, addonWireframes, addOnProps) {\n      let ptsArr = [];\n      addonWireframes.forEach(wireframe => wireframe.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n        if (line.type === \"LineLoop\") pts.push(pts[0].clone());\n        pts.forEach(pt => pt.applyMatrix4(addonMesh.matrix));\n        ptsArr.push(pts);\n      }));\n      return {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n    }\n\n    getAddonDxfData_CircleWasher(addonMesh, addonWireframes, addOnProps) {\n      let ptsArr = [];\n      let sidePoints = [];\n      addonWireframes.forEach(wireframe => wireframe.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n\n        if (pts.length > 0) {\n          pts.push(pts[0].clone());\n          pts.forEach(pt => pt.applyMatrix4(addonMesh.matrix));\n          ptsArr.push(pts);\n        }\n      }));\n\n      if (ptsArr.length === 2) {\n        ptsArr[0].forEach((point, idx) => sidePoints.push([point.clone(), ptsArr[1][idx].clone()]));\n        ptsArr.push(...sidePoints);\n      }\n\n      return {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n    }\n\n    drawPolygonNut(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height > 0 && addOnData.numberOfEdges > 2 && addOnProps && addOnData.wrenchSize > 0) {\n        let insertionDistance = addOnData.insertionDistance;\n        let height = addOnData.height;\n        let wrenchSize = addOnData.wrenchSize / 2;\n        let numberOfEdges = addOnData.numberOfEdges;\n        let angle = (numberOfEdges - 2) * 180 / numberOfEdges / 2;\n        angle *= Math.PI / 180;\n        let radius = wrenchSize / Math.sin(angle);\n        let geometry = new this.THREE.CylinderGeometry(radius, radius, height, numberOfEdges);\n        geometry.rotateY(Math.PI / 6); //temp\n\n        geometry.translate(0, -height / 2, 0);\n        geometry.isPolygon = true;\n        let volumeColor = addOnProps.volume.color;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let lineProps = addOnProps.line;\n        let addonWireframes = this.makeHeadWireframe(geometry, lineProps);\n        addonWireframes.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n        mesh.position.set(0, -insertionDistance, 0);\n        mesh.updateMatrix();\n        dxfData = this.getAddonDxfData_PolygonWireframe(mesh, addonWireframes, addOnProps);\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawCircleWasher(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height > 0 && addOnProps) {\n        let insertionDistance = addOnData.insertionDistance;\n        let height = addOnData.height;\n        let topRadius = addOnData.topDiameter / 2;\n        let botRadius = addOnData.bottomDiameter / 2;\n\n        if (topRadius > 0 || botRadius > 0) {\n          let geometry = new this.THREE.CylinderGeometry(topRadius, botRadius, height, 32);\n          geometry.translate(0, -height / 2, 0);\n          geometry.isAddOn = true;\n          let volumeColor = addOnProps.volume.color;\n          let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n          volumeMaterial.side = this.THREE.FrontSide;\n          mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n          let lineProps = addOnProps.line;\n          let addonWireframes = this.makeHeadWireframe(geometry, lineProps);\n          addonWireframes.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n          mesh.position.set(0, -insertionDistance, 0);\n          mesh.updateMatrix();\n          dxfData = this.getAddonDxfData_CircleWasher(mesh, addonWireframes, addOnProps);\n        }\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawRectangleWasher(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.width > 0 && addOnData.length > 0 && addOnData.height > 0 && addOnProps) {\n        let insertionDistance = addOnData.insertionDistance + 0.001;\n        let width = addOnData.width;\n        let length = addOnData.length;\n        let height = addOnData.height;\n        let geometry = new this.THREE.BoxGeometry(length, height, width);\n        geometry.translate(0, -height / 2, 0);\n        let volumeColor = addOnProps.volume.color;\n        let lineProps = addOnProps.line;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        volumeMaterial.side = this.THREE.FrontSide;\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let partOne = [];\n        let partTwo = [];\n        let geoVertices = geometry.vertices;\n        let firstPart = geoVertices[0].y;\n        geoVertices.forEach(vertex => {\n          if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n        });\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        mesh.add(wireframeOne);\n        mesh.add(wireframeTwo);\n        let edgesLineArr = [];\n\n        for (let i = 0; i < partOne.length; i++) {\n          let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n          edgesLineArr.push(newLine);\n        }\n\n        edgesLineArr.forEach(line => mesh.add(line)); ///\n\n        mesh.position.set(0, -insertionDistance, 0);\n        mesh.updateMatrix();\n        let ptsArr = [];\n        let partOneClone = partOne.map(pt => pt.clone().applyMatrix4(mesh.matrix));\n        let partTwoClone = partTwo.map(pt => pt.clone().applyMatrix4(mesh.matrix));\n        let edgePoints = partOneClone.reduce((ptsArr, pt, i) => {\n          ptsArr.push([pt.clone(), partTwoClone[i].clone()]);\n          return ptsArr;\n        }, []);\n        partOneClone.push(partOneClone[0].clone());\n        partTwoClone.push(partTwoClone[0].clone());\n        ptsArr.push(partOneClone, partTwoClone, ...edgePoints);\n        dxfData = {\n          points: ptsArr,\n          properties: addOnProps.line\n        };\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawAngleWasherTypeA(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnProps && addOnData.length && addOnData.width && addOnData.height && addOnData.normalLength && addOnData.orthogonalLength) {\n        let insertionDistance = addOnData.insertionDistance + 0.001;\n        let orthogonalLength = addOnData.orthogonalLength;\n        let height = addOnData.height;\n        let length = addOnData.length;\n        let width = addOnData.width;\n        let BT = Math.round(Math.sqrt(orthogonalLength / 2 * (orthogonalLength / 2) / 2) * 100) / 100;\n        let A = {\n          x: -BT,\n          y: BT - height\n        };\n        let B = {\n          x: -BT,\n          y: -BT\n        };\n        let C = {\n          x: BT,\n          y: BT\n        };\n        let D = {\n          x: length - BT,\n          y: BT - height\n        };\n        let pts = [];\n        pts.push(new this.THREE.Vector2(A.x, A.y));\n        pts.push(new this.THREE.Vector2(B.x, B.y));\n        pts.push(new this.THREE.Vector2(C.x, C.y));\n        pts.push(new this.THREE.Vector2(D.x, D.y));\n        let shape = new this.THREE.Shape(pts);\n        let extrudeSettings = {\n          depth: width,\n          bevelEnabled: false\n        };\n        let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n        let volumeColor = addOnProps.volume.color;\n        let lineProps = addOnProps.line;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let pointsArr = pts.map(point => new this.THREE.Vector3(point.x, point.y, width));\n        let polygonWirefame = this.makePolygonWireframe(pointsArr, width, lineProps);\n        polygonWirefame.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n        mesh.rotation.set(0, -Math.PI / 2, -Math.PI / 4);\n        mesh.position.set(width / 2, -insertionDistance, 0);\n        mesh.updateMatrix();\n        dxfData = this.getAddonDxfData_PolygonWireframe(mesh, polygonWirefame, addOnProps);\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawAngleWasherTypeB(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height && addOnData.width && addOnData.length && addOnData.normalLength && addOnData.orthogonalLength) {\n        let height = addOnData.height;\n        let length = addOnData.length;\n        let width = addOnData.width;\n        let ellipsoidgeometry = new this.THREE.Geometry();\n        let latitudeBands = 32,\n            longitudeBands = 32,\n            a = length / 2,\n            b = width / 2,\n            c = height,\n            size = 1;\n\n        for (let latNumber = 0; latNumber <= latitudeBands; latNumber++) {\n          let theta = latNumber * Math.PI / latitudeBands;\n          let sinTheta = Math.sin(theta);\n          let cosTheta = Math.cos(theta);\n\n          for (let longNumber = 0; longNumber <= longitudeBands; longNumber++) {\n            let phi = longNumber * 2 * Math.PI / longitudeBands;\n            let sinPhi = Math.sin(phi);\n            let cosPhi = Math.cos(phi);\n            let x = a * cosPhi * cosTheta;\n            let z = b * cosTheta * sinPhi;\n            let y = c * sinTheta;\n            ellipsoidgeometry.vertices.push(new this.THREE.Vector3(x * size, y * size, z * size));\n          }\n        }\n\n        for (let latNumber = 0; latNumber < latitudeBands; latNumber++) {\n          for (let longNumber = 0; longNumber < longitudeBands; longNumber++) {\n            let first = latNumber * (longitudeBands + 1) + longNumber;\n            let second = first + longitudeBands + 1;\n            ellipsoidgeometry.faces.push(new this.THREE.Face3(first, second, first + 1));\n            ellipsoidgeometry.faces.push(new this.THREE.Face3(second, second + 1, first + 1));\n          }\n        }\n\n        ellipsoidgeometry.computeFaceNormals();\n        let orthogonalLength = addOnData.orthogonalLength;\n        let normalLength = addOnData.normalLength;\n        let normalHeight = Math.sqrt(Math.pow(normalLength, 2) / 2);\n        let heightProportion = normalHeight / (height / 2);\n        let offsetX = Math.sqrt(Math.pow(orthogonalLength / 2, 2) / 2);\n        ellipsoidgeometry.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n        ellipsoidgeometry.rotateZ(-Math.PI / 4);\n        ellipsoidgeometry.rotateY(-Math.PI / 2);\n        let volumeColor = addOnProps.volume.color;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(ellipsoidgeometry, volumeMaterial);\n        let botCircleGeo = new this.THREE.CircleGeometry(b, 32);\n        botCircleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, a / b, 1));\n        botCircleGeo.rotateY(Math.PI / 2);\n        botCircleGeo.rotateZ(Math.PI / 2);\n        botCircleGeo.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n        botCircleGeo.rotateZ(-Math.PI / 4);\n        botCircleGeo.rotateY(-Math.PI / 2);\n        let botCircle = new this.THREE.Mesh(botCircleGeo, volumeMaterial);\n        mesh.add(botCircle);\n\n        if (addOnProps.line) {\n          let wireframePoints = this.getEllipsePointsXZ(a, b, 0, {\n            x: 0,\n            y: 0,\n            z: 0\n          });\n          let wireframe = this.drawLineFromPoints(wireframePoints, addOnProps.line, true);\n          wireframe.geometry.rotateZ(Math.PI / 2);\n          wireframe.geometry.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n          wireframe.geometry.rotateZ(-Math.PI / 4);\n          wireframe.geometry.rotateY(-Math.PI / 2);\n          mesh.add(wireframe);\n        }\n\n        mesh.updateMatrix();\n        let ptsArr = [];\n        this.WireframeGenerator.getWireframeLinePairs(ellipsoidgeometry, Math.PI / 360).forEach(([_, ptPairs]) => ptPairs.forEach(pair => {\n          ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(mesh.matrix)));\n        }));\n        dxfData = {\n          points: ptsArr,\n          properties: addOnProps.line\n        };\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawFtConnector(addOnProps) {\n      // TODO: CLEAN MESS!!!\n      let csg = this.GraphicsThree3D.CSG;\n      const colorAxis = addOnProps.volume.color;\n      const lineProps = addOnProps.line;\n      let addonMaterial = this.GraphicsThree3D.getMaterialFromColor(colorAxis);\n      addonMaterial.side = this.THREE.FrontSide; // top cyl\n\n      let topCylRadius = 1.25;\n      let topCylHeight = 8;\n      let topCylPosition = new this.THREE.Vector3(0, 4.2, 0); // bot cyl\n\n      let botCylRadius = 0.75;\n      let botCylHeight = 10;\n      let botCylPosition = new this.THREE.Vector3(0, -5.8, 0); // box one\n\n      let boxOneWidth = 3;\n      let boxOneHeight = 0.2;\n      let boxOneDepth = 4;\n      let boxOnePosition = new this.THREE.Vector3(0, 0.1, 0); // box two\n\n      let boxTwoWidth = 5;\n      let boxTwoHeight = 0.6;\n      let boxTwoDepth = 6;\n      let boxTwoPosition = new this.THREE.Vector3(0, -0.3, 0); // box three\n\n      let boxThreeWidth = 3;\n      let boxThreeHeight = 0.2;\n      let boxThreeDepth = 4;\n      let boxThreePosition = new this.THREE.Vector3(0, -0.7, 0); // box four\n\n      let boxFourWidth = 3;\n      let boxFourHeight = 0.2;\n      let boxFourDepth = 4;\n      let boxFourPosition = new this.THREE.Vector3(-2.747, -1.431, 0);\n      let boxFourRotationZ = 30 * Math.PI / 180; // box five\n\n      let boxFiveWidth = 11;\n      let boxFiveHeight = 0.2;\n      let boxFiveDepth = 4;\n      let boxFivePosition = new this.THREE.Vector3(-1.258, -6.808, 0);\n      let boxFiveRotationZ = -60 * Math.PI / 180; // eraser box top\n\n      let eraserBoxTopWidth = 7;\n      let eraserBoxTopHeight = 7;\n      let eraserBoxTopDepth = 7;\n      let eraserBoxTopPosition = new this.THREE.Vector3(2.611, 7.314, 0);\n      let eraserBoxTopRotationZ = 30 * Math.PI / 180; // eraser box bot\n\n      let eraserBoxBotWidth = 4;\n      let eraserBoxBotHeight = 4;\n      let eraserBoxBotDepth = 4;\n      let eraserBoxBotPosition = new this.THREE.Vector3(-1.416, -10.393, 0);\n      let eraserBoxBotRotationZ = -60 * Math.PI / 180; // eraser cyl top\n\n      let eraserCylTopRadius = 0.75;\n      let eraserCylTopHeight = 12;\n      let eraserCylTopPosition = new this.THREE.Vector3(0, 5.9, 0); // meshes\n\n      let topCylGeo = new this.THREE.CylinderGeometry(topCylRadius, topCylRadius, topCylHeight, 32);\n      let topCyl = new this.THREE.Mesh(topCylGeo);\n      topCyl.position.copy(topCylPosition);\n      topCyl.updateMatrix();\n      let botCylGeo = new this.THREE.CylinderGeometry(botCylRadius, botCylRadius, botCylHeight, 32);\n      let botCyl = new this.THREE.Mesh(botCylGeo);\n      botCyl.position.copy(botCylPosition);\n      botCyl.updateMatrix();\n      let boxOneGeo = new this.THREE.BoxGeometry(boxOneWidth, boxOneHeight, boxOneDepth);\n      let boxOne = new this.THREE.Mesh(boxOneGeo);\n      boxOne.position.copy(boxOnePosition);\n      boxOne.updateMatrix();\n      let boxTwoGeo = new this.THREE.BoxGeometry(boxTwoWidth, boxTwoHeight, boxTwoDepth);\n      let boxTwo = new this.THREE.Mesh(boxTwoGeo);\n      boxTwo.position.copy(boxTwoPosition);\n      boxTwo.updateMatrix();\n      let boxThreeGeo = new this.THREE.BoxGeometry(boxThreeWidth, boxThreeHeight, boxThreeDepth);\n      let boxThree = new this.THREE.Mesh(boxThreeGeo);\n      boxThree.position.copy(boxThreePosition);\n      boxThree.updateMatrix();\n      boxFourWidth = 3.4;\n      boxFourPosition = new this.THREE.Vector3(-2.747, -1.4, 0);\n      let boxFourGeo = new this.THREE.BoxGeometry(boxFourWidth, boxFourHeight, boxFourDepth);\n      let boxFour = new this.THREE.Mesh(boxFourGeo);\n      boxFour.rotation.z = boxFourRotationZ;\n      boxFour.position.copy(boxFourPosition);\n      boxFour.updateMatrix();\n      boxFivePosition = new this.THREE.Vector3(-1.408, -6.92, 0);\n      let boxFiveGeo = new this.THREE.BoxGeometry(boxFiveWidth, boxFiveHeight, boxFiveDepth);\n      let boxFive = new this.THREE.Mesh(boxFiveGeo);\n      boxFive.rotation.z = boxFiveRotationZ;\n      boxFive.position.copy(boxFivePosition);\n      boxFive.updateMatrix(); // erasers\n\n      let eraserBoxTopGeo = new this.THREE.BoxGeometry(eraserBoxTopWidth, eraserBoxTopHeight, eraserBoxTopDepth);\n      let eraserBoxTop = new this.THREE.Mesh(eraserBoxTopGeo);\n      eraserBoxTop.rotation.z = eraserBoxTopRotationZ;\n      eraserBoxTop.position.copy(eraserBoxTopPosition);\n      eraserBoxTop.updateMatrix();\n      eraserBoxBotPosition = new this.THREE.Vector3(-1.5, -10.6, 0);\n      let eraserBoxBotGeo = new this.THREE.BoxGeometry(eraserBoxBotWidth, eraserBoxBotHeight, eraserBoxBotDepth);\n      let eraserBoxBot = new this.THREE.Mesh(eraserBoxBotGeo);\n      eraserBoxBot.rotation.z = eraserBoxBotRotationZ;\n      eraserBoxBot.position.copy(eraserBoxBotPosition);\n      eraserBoxBot.updateMatrix();\n      let eraserCylTopGeo = new this.THREE.CylinderGeometry(eraserCylTopRadius, eraserCylTopRadius, eraserCylTopHeight, 32);\n      let eraserCylTop = new this.THREE.Mesh(eraserCylTopGeo);\n      eraserCylTop.position.copy(eraserCylTopPosition);\n      eraserCylTop.updateMatrix(); // wireframes\n\n      let boxOneVertices = boxOneGeo.clone().applyMatrix4(boxOne.matrix).vertices;\n      boxOneVertices = [boxOneVertices[0], boxOneVertices[2], boxOneVertices[7], boxOneVertices[5]];\n      let boxTwoVertices = boxTwoGeo.clone().applyMatrix4(boxTwo.matrix).vertices;\n      boxTwoVertices = [boxTwoVertices[0], boxTwoVertices[2], boxTwoVertices[7], boxTwoVertices[5]];\n      let boxThreeVertices = boxThreeGeo.clone().applyMatrix4(boxThree.matrix).vertices;\n      let boxFourVertices = boxFourGeo.clone().applyMatrix4(boxFour.matrix).vertices;\n      let polyVertices = [boxThreeVertices[0], boxThreeVertices[2], new this.THREE.Vector3(-1.51, -0.8, 2.002), new this.THREE.Vector3(-3.997, -2.2365, 2.002), new this.THREE.Vector3(1.432, -11.634, 2.002), new this.THREE.Vector3(1.254, -11.736, 2.002), boxFourVertices[5], new this.THREE.Vector3(-1.565, -0.6, 2.002)]; // let botCylVertices = botCylGeo.clone().applyMatrix4(botCyl.matrix).vertices.splice(0, 32);\n      // let topCylVertices = topCylGeo.clone().applyMatrix4(topCyl.matrix).vertices.splice(32, 32);\n      // let topCutMiddleVertices = this.getEllipsePointsXZ(1.5, 0.75, 30*Math.PI/180, {x: 0, y: 4.85, z: 0});\n\n      let topCutOuterVertices = this.getEllipsePointsXZ(2.5, 1.25, 30 * Math.PI / 180, {\n        x: 0,\n        y: 4.85,\n        z: 0\n      });\n      let botCutVertices = this.getEllipsePointsXZ(1.5, 0.75, 30 * Math.PI / 180, {\n        x: 0,\n        y: -9.15,\n        z: 0\n      });\n      let botCutWireframes = this.drawLineFromPoints(botCutVertices, lineProps, true);\n      let topCutOuterWireframes = this.drawLineFromPoints(topCutOuterVertices, lineProps, true); // let topCutMiddleWireframes = this.drawLineFromPoints(topCutMiddleVertices, lineProps, true);\n      // let topCylBotWireframes = this.drawLineFromPoints(topCylVertices, lineProps, true);\n      // let botCylTopWireframes = this.drawLineFromPoints(botCylVertices, lineProps, true);\n\n      let boxOneWireframes = this.makePolygonWireframe(boxOneVertices, boxOneDepth, lineProps);\n      let boxTwoWireframes = this.makePolygonWireframe(boxTwoVertices, boxTwoDepth, lineProps);\n      let botPolyWireframes = this.makePolygonWireframe(polyVertices, boxOneDepth, lineProps); // boolean operations\n      // TODO: Optimize!!!\n\n      let eraserBoxTopCSG = csg.fromMesh(eraserBoxTop);\n      let topCylCSG = csg.fromMesh(topCyl);\n      topCylCSG = topCylCSG.subtract(eraserBoxTopCSG);\n      topCyl = csg.toMesh(topCylCSG, new this.THREE.Matrix4());\n      let eraserBoxBotCSG = csg.fromMesh(eraserBoxBot);\n      let botCylCSG = csg.fromMesh(botCyl);\n      botCylCSG = botCylCSG.subtract(eraserBoxBotCSG);\n      botCyl = csg.toMesh(botCylCSG, new this.THREE.Matrix4());\n      let eraserCylTopCSG = csg.fromMesh(eraserCylTop);\n      topCylCSG = csg.fromMesh(topCyl);\n      let boxOneCSG = csg.fromMesh(boxOne);\n      let boxTwoCSG = csg.fromMesh(boxTwo);\n      topCylCSG = topCylCSG.subtract(eraserCylTopCSG);\n      boxOneCSG = boxOneCSG.subtract(eraserCylTopCSG);\n      boxTwoCSG = boxTwoCSG.subtract(eraserCylTopCSG);\n      topCyl = csg.toMesh(topCylCSG, new this.THREE.Matrix4());\n      boxOne = csg.toMesh(boxOneCSG, new this.THREE.Matrix4());\n      boxTwo = csg.toMesh(boxTwoCSG, new this.THREE.Matrix4());\n      topCylCSG = csg.fromMesh(topCyl);\n      botCylCSG = csg.fromMesh(botCyl);\n      boxOneCSG = csg.fromMesh(boxOne);\n      boxTwoCSG = csg.fromMesh(boxTwo);\n      let boxThreeCSG = csg.fromMesh(boxThree);\n      let boxFourCSG = csg.fromMesh(boxFour);\n      let boxFiveCSG = csg.fromMesh(boxFive);\n      let unionCSG = topCylCSG.union(boxOneCSG);\n      unionCSG = unionCSG.union(boxTwoCSG);\n      unionCSG = unionCSG.union(boxThreeCSG);\n      unionCSG = unionCSG.union(botCylCSG);\n      unionCSG = unionCSG.union(boxFourCSG);\n      unionCSG = unionCSG.union(boxFiveCSG);\n      let addon = csg.toMesh(unionCSG, new this.THREE.Matrix4());\n      boxOneWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line)));\n      boxTwoWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line)));\n      botPolyWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line))); // addon.add(botCylTopWireframes);\n      // addon.add(topCylBotWireframes);\n      // addon.add(topCutMiddleWireframes);\n\n      addon.add(topCutOuterWireframes);\n      addon.add(botCutWireframes);\n      addon.rotation.y = -Math.PI / 2;\n      addon.material = addonMaterial;\n      addon.updateMatrix();\n      let ptsArr = [];\n      let ptPairs_TopCyl = this.WireframeGenerator.getRegularWireframePointPairs(topCyl, Math.PI / 360);\n      let ptPairs_BotCyl = this.WireframeGenerator.getRegularWireframePointPairs(botCyl, Math.PI / 360);\n      ptPairs_TopCyl.forEach(pair => ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(addon.matrix))));\n      ptPairs_BotCyl.forEach(pair => ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(addon.matrix))));\n      [boxOneWireframes, boxTwoWireframes, botPolyWireframes].forEach(wireframe => wireframe.forEach(lineArr => lineArr.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n        if (line.type === \"LineLoop\") pts.push(pts[0].clone());\n        pts.forEach(pt => pt.applyMatrix4(addon.matrix));\n        ptsArr.push(pts);\n      })));\n      let dxfData = {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n      return [addon, dxfData];\n    }\n\n    makePolygonWireframe(pointsArr, depth, lineProps) {\n      let wireframes = [];\n      let partOne = pointsArr;\n      let partTwo = pointsArr.map(point => point.clone().sub(new this.THREE.Vector3(0, 0, depth)));\n      let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n      wireframes.push([wireframeOne]);\n      let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n      wireframes.push([wireframeTwo]);\n      let edgesLineArr = [];\n\n      for (let i = 0; i < partOne.length; i++) {\n        let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n        edgesLineArr.push(newLine);\n      }\n\n      wireframes.push(edgesLineArr);\n      return wireframes;\n    }\n\n    drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat) {\n      let axis = new this.THREE.Object3D();\n      let axisMesh, axisPointMesh;\n      if (axisGeo) axisMesh = new this.THREE.Mesh(axisGeo, axisMat);\n      if (axisPointGeo) axisPointMesh = new this.THREE.Mesh(axisPointGeo, axisPointMat);\n      if (axisGeo) axisMesh.renderOrder = -6;\n      if (axisPointGeo) axisPointMesh.renderOrder = -6;\n      if (axisMesh) axis.add(axisMesh);\n      if (axisPointMesh) axis.add(axisPointMesh);\n      return axis;\n    }\n\n    getMaterial(properties, target) {\n      let jsonColor;\n\n      if (target == \"axis\") {\n        jsonColor = properties.fastener.axis.line.color;\n      } else if (target == \"tipSide\") {\n        jsonColor = properties.fastener.tipSide.volume.color;\n      } else {\n        jsonColor = properties.fastener.headSide.volume.color;\n      }\n\n      let volumeColor = \"rgb(\" + jsonColor.red + \",\" + jsonColor.green + \",\" + jsonColor.blue + \")\";\n      let transparency = jsonColor.alpha / 255;\n      let material = new this.THREE.MeshBasicMaterial({\n        color: volumeColor,\n        opacity: transparency,\n        transparent: true,\n        side: this.THREE.DoubleSide\n      });\n      return material;\n    } // radiuses are for 2D\n\n\n    makeElipsoidGeo(radiusX, radiusY, height) {\n      let geometry = new this.THREE.Geometry();\n      let latitudeBands = 32,\n          longitudeBands = 32,\n          a = radiusX,\n          b = radiusY,\n          c = height,\n          size = 1;\n\n      for (let latNumber = 0; latNumber <= latitudeBands; latNumber++) {\n        let theta = latNumber * Math.PI / latitudeBands;\n        let sinTheta = Math.sin(theta);\n        let cosTheta = Math.cos(theta);\n\n        for (let longNumber = 0; longNumber <= longitudeBands; longNumber++) {\n          let phi = longNumber * 2 * Math.PI / longitudeBands;\n          let sinPhi = Math.sin(phi);\n          let cosPhi = Math.cos(phi);\n          let x = a * cosPhi * cosTheta;\n          let z = b * cosTheta * sinPhi;\n          let y = c * sinTheta;\n          geometry.vertices.push(new this.THREE.Vector3(x * size, y * size, z * size));\n        }\n      }\n\n      for (let latNumber = 0; latNumber < latitudeBands; latNumber++) {\n        for (let longNumber = 0; longNumber < longitudeBands; longNumber++) {\n          let first = latNumber * (longitudeBands + 1) + longNumber;\n          let second = first + longitudeBands + 1;\n          geometry.faces.push(new this.THREE.Face3(first, second, first + 1));\n          geometry.faces.push(new this.THREE.Face3(second, second + 1, first + 1));\n        }\n      }\n\n      geometry.computeFaceNormals();\n      return geometry;\n    } // Geometries\n\n\n    getAxisGeo(axisData, axisProps) {\n      const PRECISION = 8;\n      let axisLength = axisData.length;\n      let axisWidth = axisProps.line.thickness / 2;\n      let axisGeo = new this.THREE.CylinderBufferGeometry(axisWidth, axisWidth, axisLength, PRECISION);\n      axisGeo.translate(0, -axisLength / 2, 0);\n      return axisGeo;\n    }\n\n    getAxisPointGeo(axisProps) {\n      const PRECISION = 8;\n      let pointRadius = axisProps.size / 2;\n      let pointGeo = new this.THREE.SphereBufferGeometry(pointRadius, PRECISION, PRECISION);\n      return pointGeo;\n    }\n    /**\n     * Head Types\n     * 1: Countersunk\n     * 2: Cylinder\n     * 3: Lentil\n     * 4: Disk Flat\n     * 5: Disk Bottom Flat\n     * 6: Disk Top Flat\n     * 7: polygonWithoutWasher\n     * 8: polygonWithWasher\n     */\n\n\n    getFastenerHeadGeoCountersunk(topRadius, bottomRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, bottomRadius, height, radialSegments);\n      headGeo.translate(0, -height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoCylinder(topRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, topRadius, height, radialSegments);\n      headGeo.translate(0, -height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoLentil(topRadius, height, heightFactor, radialSegments) {\n      let topHeight = height * heightFactor;\n      let botHeight = height - topHeight; // let headGeoTop = this.makeElipsoidGeo(topRadius, topRadius, topHeight);\n\n      let headGeoTop = new this.THREE.SphereGeometry(topRadius, 24, 12);\n      headGeoTop.applyMatrix4(new this.THREE.Matrix4().makeScale(1, topHeight / topRadius, 1));\n      let headGeoBot = new this.THREE.CylinderGeometry(topRadius, topRadius, botHeight, radialSegments);\n      headGeoTop.translate(0, botHeight, 0);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    getFastenerHeadGeoDiskFlat(topRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, topRadius, height, radialSegments);\n      headGeo.translate(0, height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoDiskBottomFlat(topRadius, bottomRadius, topDiameterFactor, topDiameter, height, heightFactor, radialSegments) {\n      let topHeight = height * heightFactor;\n      let botHeight = height - topHeight;\n      let headGeoTop = new this.THREE.CylinderGeometry(topRadius, (topDiameter + 2 * topDiameterFactor * topDiameter) / 2, topHeight, radialSegments);\n      let headGeoBot = new this.THREE.CylinderGeometry(bottomRadius, bottomRadius, botHeight, radialSegments);\n      headGeoTop.isTypeFiveTop = true;\n      headGeoBot.isTypeSixBottom = true;\n      headGeoTop.translate(0, botHeight + topHeight / 2, 0);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    getFastenerHeadGeoPolyWithoutWasher(wrenchSize, numOfEdges, height) {\n      wrenchSize /= 2;\n      let angle = (numOfEdges - 2) * 180 / numOfEdges / 2;\n      angle *= Math.PI / 180;\n      let radius = wrenchSize / Math.sin(angle);\n      let headGeo = new this.THREE.CylinderGeometry(radius, radius, height, numOfEdges);\n      headGeo.rotateY(angle / 2);\n      headGeo.translate(0, height / 2, 0);\n      headGeo.isPolygon = true;\n      return headGeo;\n    }\n\n    getFastenerHeadGeoPolyWithWasher(wrenchSize, numOfEdges, height, heightFactor, topRadius) {\n      wrenchSize /= 2;\n      let angle = (numOfEdges - 2) * 180 / numOfEdges / 2;\n      angle *= Math.PI / 180;\n      let radius = wrenchSize / Math.sin(angle);\n      let botHeight = height * heightFactor;\n      let topHeight = height - botHeight;\n      let headGeoTop = new this.THREE.CylinderGeometry(radius, radius, topHeight, numOfEdges);\n      headGeoTop.rotateY(angle / 2);\n      headGeoTop.translate(0, topHeight / 2 + botHeight, 0);\n      headGeoTop.isPolygon = true;\n      let headGeoBot = new this.THREE.CylinderGeometry(topRadius, topRadius, botHeight, 32);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    makeHeadWireframe(geometry, lineProps, drawBottomFrameHead = true) {\n      let wireframes = [];\n\n      if (geometry) {\n        if (geometry.length > 0) {\n          geometry.forEach(geo => {\n            let newWireframes = this.makeHeadWireframe(geo, lineProps, drawBottomFrameHead);\n            wireframes.push(...newWireframes);\n          });\n        } else {\n          if (geometry.type == \"CylinderGeometry\") {\n            let drawTopFrame = true;\n            let drawBotFrame = drawBottomFrameHead;\n            if (geometry.isTypeSixBottom) drawTopFrame = false;\n            if (geometry.isAddon) drawBotFrame = true;\n            if (geometry.isTypeSixTop) drawBotFrame = false;\n            if (geometry.isTypeFiveBottom) drawTopFrame = false;\n            if (geometry.isTypeFiveTop) drawBotFrame = false;\n            let drawEdges = geometry.isPolygon ? true : false;\n            let topBotWireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopFrame, drawBotFrame, drawEdges);\n            wireframes.push(...topBotWireframes);\n          }\n        }\n      }\n\n      return wireframes;\n    }\n\n    getFastenerHeadGeo(headData) {\n      if (headData) {\n        let RADIAL_SEGMENTS = 32;\n        let headType = headData.type;\n        let topDiameter = headData.topDiameter.value;\n        let topDiameterFactor = headData.topDiameter.factor;\n        let bottomDiameter = headData.bottomDiameter.value;\n        let bottomDiameterFactor = headData.bottomDiameter.factor;\n        let topRadius = topDiameter / 2;\n        let bottomRadius = bottomDiameter / 2;\n        let height = headData.height.value;\n        let heightFactor = headData.height.factor;\n        let wrenchSize = headData.wrenchSize;\n        let numOfEdges = headData.numberOfEdges;\n        let headGeo;\n        let headGeoTop, headGeoBot;\n\n        if (headType == 1) {\n          if (topRadius == 0) topRadius = 0.001;\n          if (bottomRadius == 0) bottomRadius = 0.001;\n          headGeo = this.getFastenerHeadGeoCountersunk(topRadius, bottomRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 2) {\n          headGeo = this.getFastenerHeadGeoCylinder(topRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 3) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoLentil(topRadius, height, heightFactor, RADIAL_SEGMENTS);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 4) {\n          headGeo = this.getFastenerHeadGeoDiskFlat(topRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 5) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoDiskBottomFlat(topRadius, bottomRadius, topDiameterFactor, topDiameter, height, heightFactor, RADIAL_SEGMENTS);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 6) {\n          let topHeight = height * (1 - heightFactor);\n          let botHeight = height * heightFactor;\n          headGeoTop = new this.THREE.CylinderGeometry(topRadius, topRadius, topHeight, 32);\n          headGeoTop.isTypeSixTop = true;\n          headGeoBot = new this.THREE.CylinderGeometry((bottomDiameter + 2 * bottomDiameterFactor * bottomDiameter) / 2, bottomRadius, botHeight, RADIAL_SEGMENTS);\n          headGeoBot.isTypeSixBottom = true;\n          headGeoTop.translate(0, -topHeight / 2, 0);\n          headGeoBot.translate(0, -topHeight - botHeight / 2, 0);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 7) {\n          headGeo = this.getFastenerHeadGeoPolyWithoutWasher(wrenchSize, numOfEdges, height);\n        } else if (headType == 8) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoPolyWithWasher(wrenchSize, numOfEdges, height, heightFactor, topRadius);\n          headGeo = [headGeoTop, headGeoBot];\n        }\n\n        return headGeo;\n      }\n    }\n    /**\n     * Shank Types\n     * 1: Circle\n     * 2: Square\n     */\n\n\n    getFastenerShankGeo(segments, shankData, fastener) {\n      let geometries = [];\n\n      if (shankData) {\n        let diameter = shankData.size;\n        segments.forEach(segment => {\n          let length = segment[1] - segment[0];\n          let newGeo;\n          if (shankData.type == 1) newGeo = new this.THREE.CylinderGeometry(diameter / 2, diameter / 2, length, 32);else if (shankData.type == 2) newGeo = new this.THREE.BoxGeometry(diameter, length, diameter);else console.log(\"Shank type is undefined\");\n\n          if (newGeo) {\n            if (segment[2]) newGeo.isTipSide = true;\n            newGeo.translate(0, -length / 2 - segment[0], 0);\n            geometries.push(newGeo);\n          }\n        });\n      } else {\n        geometries = null;\n      }\n\n      return geometries;\n    }\n\n    makeTipWireframe(tipData, geometry, lineProps, drawTopTip = true, drawBotTip = true, drawEdgesTip = false) {\n      // TODO: Refactor/Clean code.\n      let wireframes = [];\n\n      if (tipData.type == 1) {\n        let geoVertices = [...geometry.vertices];\n        geoVertices.splice(geoVertices.length - 2, 2);\n\n        if (drawTopTip) {\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n      } else if (tipData.type == 2) {\n        if (geometry.parameters.radiusBottom) {\n          wireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopTip);\n        } else {\n          let geoVertices = [...geometry.vertices];\n          geoVertices.splice(geoVertices.length - 2, 2);\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n      } else if (tipData.type == 3) {\n        let depth = geometry.parameters.options.depth;\n        let partOne = [...geometry.vertices].splice(0, 3);\n        let partTwo = [];\n        partOne.forEach(vertex => partTwo.push(vertex.clone()));\n        partTwo.forEach(vertex => vertex.setComponent(2, vertex.z + depth));\n        let wireframesOne = this.drawLineFromPoints(partOne, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesOne]);\n        let wireframesTwo = this.drawLineFromPoints(partTwo, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesTwo]);\n\n        if (drawTopTip) {\n          let edgesLineArr = [];\n\n          for (let i = 0; i < partOne.length; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        } else {\n          let edgesLineArr = [];\n          let newLine = this.drawLineFromPoints([partOne[1], partTwo[1]], lineProps);\n          edgesLineArr.push(newLine);\n          wireframes.push(edgesLineArr);\n        }\n      } else if (tipData.type == 4) {\n        let depth = geometry.parameters.options.depth;\n        let partOne = [];\n\n        if (geometry.vertices[3].z > 0) {\n          partOne = [...geometry.vertices].splice(0, 3);\n        } else {\n          partOne = [...geometry.vertices].splice(0, 4);\n        }\n\n        let partTwo = [];\n        partOne.forEach(vertex => partTwo.push(vertex.clone()));\n        partTwo.forEach(vertex => vertex.setComponent(2, vertex.z + depth));\n        let wireframesOne = this.drawLineFromPoints(partOne, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesOne]);\n        let wireframesTwo = this.drawLineFromPoints(partTwo, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesTwo]);\n\n        if (drawTopTip) {\n          let edgesLineArr = [];\n\n          for (let i = 0; i < partOne.length; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        } else {\n          let edgesLineArr = [];\n\n          for (let i = 1; i < partOne.length - 1; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        }\n      } else if (tipData.type == 5) {\n        let geoVertices = [...geometry.vertices].splice(0, 4);\n\n        if (drawTopTip) {\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n\n        let pointVertex = geometry.vertices[4];\n        let pointArr = [];\n        geoVertices.forEach(vertex => {\n          let newLine = this.drawLineFromPoints([pointVertex, vertex], lineProps);\n          pointArr.push(newLine);\n        });\n        wireframes.push(pointArr);\n      } else if (tipData.type == 6) {\n        if (geometry.parameters.radiusBottom) {\n          wireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopTip, true, true);\n        } else {\n          let geoVertices = [...geometry.vertices];\n          geoVertices.splice(geoVertices.length - 2, 2);\n          let pointVertex = geometry.vertices[4];\n          let pointArr = [];\n          geoVertices.forEach(vertex => {\n            let newLine = this.drawLineFromPoints([pointVertex, vertex], lineProps);\n            pointArr.push(newLine);\n          });\n          wireframes.push(pointArr);\n\n          if (drawTopTip) {\n            let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n            wireframes.push([wireframe]);\n          }\n        }\n      }\n\n      return wireframes;\n    }\n    /**\n     * Tip Types\n     * 1: Circle Pointed\n     * 2: Circle Blunt\n     * 3: Square Half Pointed\n     * 4: Square Half Blunt\n     * 5: Square Full Pointed\n     * 6: Square Full Blunt\n     */\n\n\n    getFastenerTipGeo(tipData, tipDiameter, fastener, axisLength) {\n      let tipGeo;\n      let height = tipData ? tipData.height : 0;\n      let tipRadius = tipDiameter / 2;\n\n      if (height > 0) {\n        if (tipData.type == 1) {\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, 0, height, 32);\n        } else if (tipData.type == 2) {\n          let bottomRadius = tipRadius - height;\n          if (bottomRadius < 0) bottomRadius = 0;\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, bottomRadius, height, 32);\n        } else if (tipData.type == 3) {\n          let shape = new this.THREE.Shape();\n          shape.moveTo(0, -height / 2);\n          shape.lineTo(tipRadius, height / 2);\n          shape.lineTo(-tipRadius, height / 2);\n          shape.lineTo(0, -height / 2);\n          let extrudeSettings = {\n            steps: 32,\n            depth: 2 * tipRadius,\n            bevelEnabled: false\n          };\n          tipGeo = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n          tipGeo.translate(0, 0, -fastener.components.shank.size / 2);\n        } else if (tipData.type == 4) {\n          let bottomRadius = (tipDiameter - 2 * height) / 2;\n          if (bottomRadius < 0) bottomRadius = 0;\n          let shape = new this.THREE.Shape();\n          shape.moveTo(bottomRadius, -height / 2);\n          shape.lineTo(tipRadius, height / 2);\n          shape.lineTo(-tipRadius, height / 2);\n          shape.lineTo(-bottomRadius, -height / 2);\n          shape.lineTo(bottomRadius, -height / 2);\n          let extrudeSettings = {\n            steps: 32,\n            depth: 2 * tipRadius,\n            bevelEnabled: false\n          };\n          tipGeo = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n          tipGeo.translate(0, 0, -fastener.components.shank.size / 2);\n        } else if (tipData.type == 5) {\n          tipRadius = Math.sqrt(2 * Math.pow(tipDiameter, 2)) / 2;\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, 0, height, 4);\n          tipGeo.rotateY(Math.PI / 4);\n        } else if (tipData.type == 6) {\n          let topRadius = Math.sqrt(2 * Math.pow(tipRadius, 2));\n          let correctedRadius = tipRadius - height > 0 ? tipRadius - height : 0;\n          let bottomRadius = Math.sqrt(2 * Math.pow(correctedRadius, 2));\n          tipGeo = new this.THREE.CylinderGeometry(topRadius, bottomRadius, height, 4);\n          tipGeo.rotateY(Math.PI / 4);\n        } else {\n          tipGeo = null;\n        }\n      } else {\n        tipGeo = null;\n      }\n\n      if (tipGeo) {\n        tipGeo.computeBoundingBox();\n        tipGeo.center();\n        tipGeo.translate(0, height / 2 - axisLength, 0);\n      }\n\n      return tipGeo;\n    }\n    /**\n     * Thread Types\n     * 1: with pitch\n     * 2: without pitch\n     */\n\n\n    getSpiralAndCylGeo(threadSegments, fastenerProps) {\n      let cylGeometries = [];\n      let spiralGeometries = [];\n      let wireframePoints = [];\n      let cylWireframes = [];\n      threadSegments.forEach(segment => {\n        let threadData = segment[2];\n\n        if (threadData.pitch > 0) {\n          let threadDiameter = threadData.threadDiameter;\n          let coreDiameter = threadData.coreDiameter;\n          let pitch = threadData.pitch;\n          let height = threadData.height;\n          let length = segment[1] - segment[0];\n\n          if (threadDiameter && length > 0 && threadDiameter > coreDiameter) {\n            let spiral = new Spiral(coreDiameter / 2, length, pitch, (threadDiameter - coreDiameter) / 2, height, new this.THREE.MeshBasicMaterial(), this);\n            let cylGeo = spiral.getCylGeo();\n            let [spiralGeo, newWireframePoints] = spiral.getSpiralGeo();\n            wireframePoints.push(newWireframePoints);\n            cylGeo.translate(0, -segment[0], 0);\n            spiralGeo.translate(0, -segment[0], 0); ///\n            // let lineProps = fastenerProps.fastener.headSide.line;\n            // let cylWireframe = this.makeHeadWireframe(cylGeo, lineProps);\n            // cylWireframes.push(cylWireframe);\n            ///\n\n            cylGeometries.push(cylGeo);\n            spiralGeometries.push(spiralGeo);\n          }\n        }\n      });\n      let firstCyl, firstSpiral;\n\n      if (cylGeometries.length > 0 && spiralGeometries.length > 0) {\n        firstCyl = cylGeometries.splice(0, 1)[0];\n        cylGeometries.forEach(geo => firstCyl.merge(geo));\n        firstSpiral = spiralGeometries.splice(0, 1)[0];\n        spiralGeometries.forEach(geo => firstSpiral.merge(geo));\n      }\n\n      return [firstCyl, firstSpiral, wireframePoints, cylWireframes];\n    }\n\n    getThreadWithoutPitchGeo(threadSegments, fastenerProps) {\n      let geometries = [];\n      let wireframePoints = [];\n      let coreGeometries = [];\n      let coreWireframes = [];\n      threadSegments.forEach(segment => {\n        let insertionDistance = segment[0];\n        let length = segment[1] - segment[0];\n        let threadsData = segment[2];\n        let coreRadius = threadsData.coreDiameter / 2;\n        let threadRadius = threadsData.threadDiameter / 2;\n        let height = threadsData.height;\n        let distance = threadsData.distance;\n        let midGeoHeight = distance - height;\n        let threadPartHeight = height / 2; // top and bot geometry height\n\n        let pitch = threadsData.pitch;\n        if (threadPartHeight == 0) threadPartHeight = 0.001;\n        if (midGeoHeight == 0) midGeoHeight = 0.001;\n\n        if (pitch == 0 && length > 0 && (threadPartHeight > 0 || midGeoHeight > 0) && threadRadius > 0 && coreRadius >= 0 && threadRadius > coreRadius) {\n          if (coreRadius == 0) coreRadius = 0.01;\n          if (threadRadius == 0) threadRadius = 0.01;\n          let coreGeo = new this.THREE.CylinderGeometry(coreRadius, coreRadius, length, 32);\n          coreGeo.translate(0, -length / 2 - insertionDistance, 0); ///\n          // let lineProps = fastenerProps.fastener.headSide.line;\n          // let coreWireframe = this.makeHeadWireframe(coreGeo, lineProps);\n          // coreWireframes.push(coreWireframe);\n          ///\n\n          coreGeometries.push(coreGeo);\n          let wireframePointsOuter = [];\n          let wireframePointsInner = [];\n          let startAngle = 0;\n          let endAngle = 2 * Math.PI;\n          let step = endAngle / 32;\n\n          while (startAngle < endAngle) {\n            let widthCoeff = Math.cos(startAngle);\n            let depthCoeff = Math.sin(startAngle);\n            let outerPoint = new this.THREE.Vector3(threadRadius * widthCoeff, 0, threadRadius * depthCoeff);\n            let innerPoint = new this.THREE.Vector3(coreRadius * widthCoeff, 0, coreRadius * depthCoeff);\n            startAngle += step;\n            wireframePointsOuter.push(outerPoint);\n            wireframePointsInner.push(innerPoint);\n          }\n          /*\n            turns\n            0: topGeo\n            1: botGeo\n            2: midGeo\n          */\n\n\n          let turn = 0;\n          let distanceToLoop = segment[0] + length;\n\n          while (insertionDistance < distanceToLoop) {\n            if (turn == 0) {\n              if (threadPartHeight > 0) {\n                let topGeo = new this.THREE.CylinderGeometry(coreRadius, threadRadius, threadPartHeight, 32, 1, true);\n                topGeo.translate(0, -threadPartHeight / 2 - insertionDistance, 0); ///\n\n                let wireframeNewPoints = [...wireframePointsInner].map(point => point = point.clone().setComponent(1, -insertionDistance));\n                wireframePoints.push(wireframeNewPoints);\n                let wireframeNewPointsOuter = [...wireframePointsOuter].map(point => point = point.clone().setComponent(1, -insertionDistance - threadPartHeight));\n                wireframePoints.push(wireframeNewPointsOuter); ///\n\n                insertionDistance += threadPartHeight;\n                geometries.push(topGeo);\n              }\n            } else if (turn == 1) {\n              if (threadPartHeight > 0) {\n                let botGeo = new this.THREE.CylinderGeometry(threadRadius, coreRadius, threadPartHeight, 32, 1, true);\n                botGeo.translate(0, -threadPartHeight / 2 - insertionDistance, 0); ///\n\n                let wireframeNewPoints = [...wireframePointsInner].map(point => point = point.clone().setComponent(1, -insertionDistance - threadPartHeight));\n                wireframePoints.push(wireframeNewPoints); ///\n\n                insertionDistance += threadPartHeight;\n                geometries.push(botGeo);\n              }\n            } else {\n              if (midGeoHeight > 0) {\n                // let midGeo = new this.THREE.CylinderGeometry(coreRadius, coreRadius, midGeoHeight, 32, 1, true);\n                // midGeo.translate(0, -midGeoHeight/2 - insertionDistance, 0);\n                // geometries.push(midGeo);\n                insertionDistance += midGeoHeight;\n              }\n            }\n\n            turn++;\n            if (turn > 2) turn = 0;\n          }\n        }\n      });\n      let geometry = geometries.splice(0, 1)[0];\n      geometries.forEach(geo => geometry.merge(geo));\n      let coreGeometry = coreGeometries.splice(0, 1)[0];\n      coreGeometries.forEach(geo => coreGeometry.merge(geo));\n      return [geometry, coreGeometry, wireframePoints, coreWireframes];\n    }\n\n    drawFastenerHead_1_Countersunk(headGeo, fastenerMaterial, headData) {\n      let head;\n\n      if (headData.height.value > 0 && (headData.topDiameter.value > 0 || headData.bottomDiameter.value > 0)) {\n        head = new this.THREE.Mesh(headGeo, fastenerMaterial);\n      } else if (headData.type == 2 && headData.topDiameter.value > 0 && headData.height.value > 0) {\n        head = new this.THREE.Mesh(headGeo, fastenerMaterial);\n      } else {\n        head = null;\n      }\n\n      return head;\n    }\n\n    drawFastenerHead_4_DiscFlat(geometry, material, headData) {\n      let cylinder;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0) {\n        cylinder = new this.THREE.Mesh(geometry, material);\n      } else {\n        cylinder = null;\n      }\n\n      return cylinder;\n    }\n\n    drawFastenerHead_7_PolygonWithoutWasher(geometry, material, headData) {\n      let hexagonMesh;\n\n      if (headData.height.value > 0 && headData.wrenchSize > 0 && headData.numberOfEdges > 2) {\n        hexagonMesh = new this.THREE.Mesh(geometry, material);\n      } else {\n        hexagonMesh = null;\n      }\n\n      return hexagonMesh;\n    }\n\n    drawFastenerHead_8_PolygonWithWasher(geometry, material, headData) {\n      let polyWithWasher;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1 && headData.wrenchSize > 0 && headData.wrenchSize < headData.topDiameter.value && headData.numberOfEdges > 2) {\n        let [hexExtr, cylGeo] = geometry;\n        let hexagonMesh = new this.THREE.Mesh(hexExtr, material);\n        let cylinder = new this.THREE.Mesh(cylGeo, material);\n        let hexagonCSG = this.CSG.fromMesh(hexagonMesh);\n        let cylinderMesh = this.CSG.fromMesh(cylinder);\n        let unionCSG = hexagonCSG.union(cylinderMesh);\n        polyWithWasher = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        polyWithWasher.material = material;\n      } else {\n        polyWithWasher = null;\n      }\n\n      return polyWithWasher;\n    }\n\n    drawFastenerHead_5_DiscBottomFlat(geometry, material, headData) {\n      let discBottomFlat;\n\n      if (headData.topDiameter.value > 0 && headData.topDiameter.factor > 0 && 2 * (headData.topDiameter.factor * headData.topDiameter.value) + headData.topDiameter.value < headData.bottomDiameter.value && headData.bottomDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [geometry1, geometry2] = geometry;\n        let cylinderMesh = new this.THREE.Mesh(geometry1, material);\n        let cylinder = new this.THREE.Mesh(geometry2, material);\n        let csgOne = this.CSG.fromMesh(cylinderMesh);\n        let csgTwo = this.CSG.fromMesh(cylinder);\n        let unionCSG = csgOne.union(csgTwo);\n        discBottomFlat = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        discBottomFlat.material = material;\n      } else {\n        discBottomFlat = null;\n      }\n\n      return discBottomFlat;\n    }\n\n    drawFastenerHead_6_DiscTopFlat(geometry, material, headData) {\n      let diskTopFlat;\n\n      if (headData.topDiameter.value > 0 && headData.bottomDiameter.value > 0 && headData.bottomDiameter.factor > 0 && 2 * (headData.bottomDiameter.factor * headData.bottomDiameter.value) + headData.bottomDiameter.value < headData.topDiameter.value && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [geometry1, geometry2] = geometry;\n        let cylinder = new this.THREE.Mesh(geometry1, material);\n        let cylinderMesh = new this.THREE.Mesh(geometry2, material);\n        let csgOne = this.CSG.fromMesh(cylinder);\n        let csgTwo = this.CSG.fromMesh(cylinderMesh);\n        let unionCSG = csgOne.union(csgTwo);\n        diskTopFlat = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        diskTopFlat.material = material;\n      } else {\n        diskTopFlat = null;\n      }\n\n      return diskTopFlat;\n    }\n\n    drawFastenerHead_3_Lentil(geometry, material, headData) {\n      let lentilHead;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [ellipsoidGeo, cylinderGeo] = geometry;\n        let eraserGeo = new this.THREE.BoxGeometry(headData.topDiameter.value, headData.height.value, headData.topDiameter.value);\n        eraserGeo.translate(0, -headData.height.value / 2, 0);\n        let eraserMesh = new this.THREE.Mesh(eraserGeo);\n        var ellipsoidmesh = new this.THREE.Mesh(ellipsoidGeo);\n        var cylinder = new this.THREE.Mesh(cylinderGeo);\n        let eraserCSG = this.CSG.fromMesh(eraserMesh);\n        let ellipseCSG = this.CSG.fromMesh(ellipsoidmesh).subtract(eraserCSG);\n        let cylinderCSG = this.CSG.fromMesh(cylinder);\n        let lentilHeadCSG = ellipseCSG.union(cylinderCSG);\n        lentilHead = this.CSG.toMesh(lentilHeadCSG, new this.THREE.Matrix4());\n        lentilHead.material = material;\n      } else {\n        lentilHead = null;\n      }\n\n      return lentilHead;\n    }\n\n    drawFastenerShank(shank, properties, shankGeo, drawTopFrameShank = true, drawBotFrameShank = true) {\n      let shanks = {\n        headSide: [],\n        tipSide: []\n      };\n      let shankWireframes = [];\n      let tipSideProps = properties.fastener.tipSide;\n      let linePropsHeadSide = properties.fastener.headSide.line;\n      let lineProps = tipSideProps && shank.tipSideLength ? tipSideProps.line : linePropsHeadSide;\n      let drawEdges = shank && shank.type === 2 ? true : false;\n\n      if (shankGeo && shank.size > 0) {\n        shankGeo.forEach(geo => {\n          if (geo.isTipSide) drawTopFrameShank = false;\n          let newWireframe = this.drawDoublesideFrame(geo, lineProps, drawTopFrameShank, drawBotFrameShank, drawEdges);\n          shankWireframes.push(...newWireframe);\n          let shankMesh = new this.THREE.Mesh(geo);\n          if (geo.isTipSide) shanks.tipSide.push(shankMesh);else shanks.headSide.push(shankMesh);\n        });\n      }\n\n      return [shanks, shankWireframes];\n    } // TODO: add wireframe\n\n\n    drawFastenerThread_0_WithPitch(cylGeo, spiralGeo, fastenerMaterial, wireframePoints, lineProps, lineMaterial) {\n      let spiralMesh, cylMesh;\n\n      if (cylGeo && spiralGeo) {\n        spiralMesh = new this.THREE.Mesh(spiralGeo, fastenerMaterial.clone());\n        spiralMesh.material.side = this.THREE.DoubleSide;\n        cylMesh = new this.THREE.Mesh(cylGeo, fastenerMaterial);\n        wireframePoints.forEach(pointArr => {\n          pointArr.forEach(points => {\n            let wireframe = this.drawLineFromPoints(points, lineProps);\n            spiralMesh.add(wireframe);\n          });\n          let endPointsOne = [pointArr[0][0], pointArr[1][0], pointArr[2][0]];\n          let endWireframeOne = this.drawLineFromPoints(endPointsOne, lineProps);\n          spiralMesh.add(endWireframeOne);\n          let endPointsTwo = [pointArr[0][pointArr[0].length - 1], pointArr[1][pointArr[0].length - 1], pointArr[2][pointArr[0].length - 1]];\n          let endWireframeTwo = this.drawLineFromPoints(endPointsTwo, lineProps);\n          spiralMesh.add(endWireframeTwo);\n        });\n      } else {\n        spiralMesh = null;\n        cylMesh = null;\n      }\n\n      return [cylMesh, spiralMesh];\n    } // TODO: apply wireframe\n\n\n    drawFastenerThread_1_WithoutPitch(threadGeo, coreGeo, material, wireframePoints, lineProps) {\n      let thread;\n      if (threadGeo) thread = new this.THREE.Mesh(threadGeo, material.clone());else thread = null;\n      let core;\n      if (coreGeo) core = new this.THREE.Mesh(coreGeo);else core = null;\n\n      if (thread) {\n        thread.material.side = this.THREE.DoubleSide;\n        wireframePoints.forEach(points => {\n          let wireframe = this.drawLineFromPoints(points, lineProps, true);\n          thread.add(wireframe);\n        });\n      }\n\n      return [thread, core];\n    }\n\n    drawFastenerTip(tipGeo, fastenerMaterial, tipSideMaterial) {\n      let cylinder;\n\n      if (tipGeo && fastenerMaterial) {\n        cylinder = new this.THREE.Mesh(tipGeo);\n        if (tipSideMaterial) cylinder.isTipSide = true;\n      } else {\n        cylinder = null;\n      }\n\n      return cylinder;\n    }\n\n    drawLineFromPoints(points, props, isClosed = false) {\n      if (props.color.alpha == 0) {\n        let line = new this.THREE.Mesh(); // TODO: might need better solution\n\n        return line;\n      }\n\n      let colorValues = {\n        red: props.color.red,\n        green: props.color.green,\n        blue: props.color.blue\n      };\n      let addToColor = 20;\n      colorValues.red = colorValues.red + addToColor <= 255 ? colorValues.red + addToColor : Math.min(colorValues.red + addToColor, 255);\n      colorValues.green = colorValues.green + addToColor <= 255 ? colorValues.green + addToColor : Math.min(colorValues.green + addToColor, 255);\n      colorValues.blue = colorValues.blue + addToColor <= 255 ? colorValues.blue + addToColor : Math.min(colorValues.blue + addToColor, 255);\n      let lineColor = \"rgb(\" + colorValues.red + \", \" + colorValues.green + \", \" + colorValues.blue + \")\";\n      let material = new this.THREE.LineBasicMaterial({\n        color: lineColor\n      });\n      let geometry = new this.THREE.BufferGeometry().setFromPoints(points);\n      let line;\n      if (isClosed) line = new this.THREE.LineLoop(geometry, material);else line = new this.THREE.Line(geometry, material);\n      return line;\n    }\n\n    rotateObject(object, direction, directionTwo) {\n      let normalVector = new this.THREE.Vector3().crossVectors(direction, directionTwo);\n      let directionVector = new this.THREE.Vector3(directionTwo.x, directionTwo.y, directionTwo.z);\n      let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n      let dotProduct = object.up.dot(newUpVector);\n      let rotationAngle = -Math.acos(dotProduct); // by default THREE.js rotates counterclockwise, so I switched sign (correct?)\n\n      object.rotateOnAxis(normalVector, rotationAngle);\n      object.up = newUpVector;\n      object.lookAt(object.position.clone().add(normalVector));\n      return object;\n    }\n\n    setTime(meshArray, i, a) {\n      var drawnObject = new Object();\n      drawnObject.id = this.GraphicsThree3D.jsonData.fasteners[i].id;\n      drawnObject.type = \"fastener\";\n      drawnObject.data = this.GraphicsThree3D.jsonData;\n      drawnObject.sceneObject = meshArray[a];\n      this.GraphicsThree3D.ThreeObjects.push(drawnObject);\n      this.GraphicsThree3D.scene.add(drawnObject.sceneObject);\n    }\n\n    drawClamp(fastener) {\n      let tipDiameter = fastener.components.threads ? fastener.components.threads[0].coreDiameter : fastener.components.shank.size;\n      let tipGeo = this.getFastenerTipGeo(fastener.components.tip, tipDiameter, fastener, fastener.components.axis.length);\n\n      for (let b = 0; b < fastener.insertionPoints.length; b++) {\n        let insertionPoint = fastener.insertionPoints[b].insertionPoint;\n        let rotationVector = this.utils.normalizeVector(fastener.insertionPoints[b].directionVector1);\n        let rotationVectorTwo = this.utils.normalizeVector(fastener.insertionPoints[b].directionVector2);\n        let headSideMeshes = [];\n        let tipSideMeshes = [];\n        let [head, headWireframes] = this.drawClampHeadStapleSpine(fastener.components.shank, fastener.properties);\n        if (head) headSideMeshes.push(head);\n        let [leftAxisBot, leftAxisTop, axisWireframes] = this.drawClampAxis(fastener.components.axis, fastener.components.shank, fastener.components.tip, fastener.properties, true);\n        let [rightAxisBot, rightAxisTop, axisWireframesRight] = this.drawClampAxis(fastener.components.axis, fastener.components.shank, fastener.components.tip, fastener.properties, false);\n\n        if (fastener.properties.fastener.tipSide) {\n          if (leftAxisTop) headSideMeshes.push(leftAxisTop);\n          if (rightAxisTop) headSideMeshes.push(rightAxisTop);\n          if (leftAxisBot) tipSideMeshes.push(leftAxisBot);\n          if (rightAxisBot) tipSideMeshes.push(rightAxisBot);\n        } else {\n          if (leftAxisBot) headSideMeshes.push(leftAxisBot);\n          if (rightAxisBot) headSideMeshes.push(rightAxisBot);\n        }\n\n        let [tipLeft, tipWireframes] = this.drawClampTip(fastener.components.shank, fastener.properties, fastener.components.tip, true, tipGeo);\n        let [tipRight, tipWireframesRight] = this.drawClampTip(fastener.components.shank, fastener.properties, fastener.components.tip, false, tipGeo);\n\n        if (fastener.properties.fastener.tipSide) {\n          if (tipLeft) tipSideMeshes.push(tipLeft);\n          if (tipRight) tipSideMeshes.push(tipRight);\n        } else {\n          if (tipLeft) headSideMeshes.push(tipLeft);\n          if (tipRight) headSideMeshes.push(tipRight);\n        }\n\n        let headSideMesh, tipSideMesh;\n\n        if (fastener.properties.fastener.headSide) {\n          let headSideMaterial = this.getMaterial(fastener.properties, \"headSide\");\n          headSideMaterial.side = this.THREE.FrontSide;\n\n          if (headSideMeshes.length > 0) {\n            headSideMesh = headSideMeshes.splice(0, 1)[0];\n            headSideMesh.updateMatrix();\n            headSideMeshes.forEach(mesh => {\n              mesh.updateMatrix();\n              let firstCSG = this.CSG.fromMesh(headSideMesh);\n              let secondCSG = this.CSG.fromMesh(mesh);\n              let unionCSG = firstCSG.union(secondCSG);\n              let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n              headSideMesh = unionMesh;\n            });\n          }\n\n          headSideMesh.material = headSideMaterial;\n        }\n\n        if (fastener.properties.fastener.tipSide) {\n          let tipSideMaterial = this.getMaterial(fastener.properties, \"tipSide\");\n          tipSideMaterial.side = this.THREE.FrontSide;\n\n          if (tipSideMeshes.length > 0) {\n            tipSideMesh = tipSideMeshes.splice(0, 1)[0];\n            tipSideMesh.updateMatrix();\n            tipSideMeshes.forEach(mesh => {\n              mesh.updateMatrix();\n              let firstCSG = this.CSG.fromMesh(tipSideMesh);\n              let secondCSG = this.CSG.fromMesh(mesh);\n              let unionCSG = firstCSG.union(secondCSG);\n              let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n              tipSideMesh = unionMesh;\n            });\n          }\n\n          tipSideMesh.material = tipSideMaterial;\n        }\n\n        let fastenerClamp = new this.THREE.Object3D();\n\n        if (headSideMesh) {\n          headSideMesh.renderOrder = -4;\n          fastenerClamp.add(headSideMesh);\n        }\n\n        if (tipSideMesh) {\n          tipSideMesh.renderOrder = -4;\n          fastenerClamp.add(tipSideMesh);\n        }\n\n        let backSideHead, backSideTip;\n\n        if (headSideMesh) {\n          let geo = headSideMesh.geometry.clone();\n          let mat = headSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideHead = new this.THREE.Mesh(geo, mat);\n          backSideHead.renderOrder = -5;\n          fastenerClamp.add(backSideHead);\n        }\n\n        if (tipSideMesh) {\n          let geo = tipSideMesh.geometry.clone();\n          let mat = tipSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideTip = new this.THREE.Mesh(geo, mat);\n          backSideTip.renderOrder = -5;\n          fastenerClamp.add(backSideTip);\n        }\n\n        headWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        axisWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        axisWireframesRight.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        tipWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        tipWireframesRight.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        })); ///\n        // Axis\n\n        let axisGeo, axisPointGeo;\n        let axisMat, axisPointMat;\n        let inversedDirection = false;\n\n        if (fastener.components.axis.length < 0) {\n          inversedDirection = true;\n          fastener.components.axis.length *= -1; // maybe change code not to modify json?\n        }\n\n        if (fastener.properties.axis && fastener.components.axis.length > 0) {\n          let lineProps = fastener.properties.axis.line;\n\n          if (lineProps.thickness && lineProps.type > 0) {\n            axisGeo = this.getAxisGeo(fastener.components.axis, fastener.properties.axis);\n            axisMat = this.GraphicsThree3D.getMaterialFromColor(lineProps.color);\n          }\n\n          let pointProps = fastener.properties.axis.point;\n          axisPointGeo = this.getAxisPointGeo(pointProps);\n          axisPointMat = this.GraphicsThree3D.getMaterialFromColor(pointProps.color);\n        }\n\n        let axis;\n        if (fastener.properties.axis) axis = this.drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat);\n        if (axis) fastenerClamp.add(axis); ///\n\n        let directionVector = new this.THREE.Vector3(rotationVector.x, rotationVector.y, rotationVector.z);\n        let directionVectorTwo = new this.THREE.Vector3(rotationVectorTwo.x, rotationVectorTwo.y, rotationVectorTwo.z);\n\n        if (inversedDirection) {\n          directionVector.negate();\n          directionVectorTwo.negate();\n        }\n\n        this.rotateObject(fastenerClamp, directionVector, directionVectorTwo);\n        fastenerClamp.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z); // this.addToDimChainCollisionDetection(fastenerClamp);\n\n        this.GraphicsThree3D.scene.add(fastenerClamp);\n        this.GraphicsThree3D.addObjectToVisControl(fastenerClamp, \"fastener\");\n      }\n    }\n\n    drawClampHeadStapleSpine(shank, propertiesData) {\n      const depth = shank.size;\n      const width = shank.offsetLength;\n      const height = shank.size;\n      let headWireframes = [];\n      let lineProps = propertiesData.fastener.headSide.line;\n      let material = this.getMaterial(propertiesData);\n\n      if (shank.type == 1) {\n        var geometry = new this.THREE.CylinderGeometry(depth / 2, depth / 2, width, 32);\n        geometry.translate(-depth / 2, 0, 0);\n      } else if (shank.type == 2) {\n        var geometry = new this.THREE.BoxGeometry(depth, height, width);\n        geometry.translate(0, -height / 2, 0);\n      } //\n\n\n      let cube; // TODO: remove duplicate code / clean code\n\n      let cubeGeoCSG;\n\n      if (shank.type == 1) {\n        let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n        let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n        eraserBox.rotation.z = Math.PI / 4;\n        eraserBox.position.set(-depth, width / 2, 0);\n        eraserBox.updateMatrix();\n        let geomesh = new this.THREE.Mesh(geometry);\n        let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n        cubeGeoCSG = geoCSG.subtract(eraserCSG);\n        eraserBox.position.set(-depth, -width / 2, 0);\n        eraserBox.updateMatrix();\n        eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        cubeGeoCSG = cubeGeoCSG.subtract(eraserCSG);\n      } else if (shank.type == 2) {\n        let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n        let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n        eraserBox.rotation.x = Math.PI / 4;\n        eraserBox.position.set(0, -depth, width / 2);\n        eraserBox.updateMatrix();\n        let geomesh = new this.THREE.Mesh(geometry);\n        let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n        cubeGeoCSG = geoCSG.subtract(eraserCSG);\n        eraserBox.position.set(0, -depth, -width / 2);\n        eraserBox.updateMatrix();\n        eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        cubeGeoCSG = cubeGeoCSG.subtract(eraserCSG);\n      }\n\n      cube = this.GraphicsThree3D.CSG.toMesh(cubeGeoCSG, new this.THREE.Matrix4());\n      cube.material = material;\n\n      if (geometry.type == \"CylinderGeometry\") {\n        cube.rotation.z = Math.PI / 2;\n        let radiusX = Math.sqrt(2 * Math.pow(depth / 2, 2));\n        let partOne = this.getEllipsePointsXZ(radiusX, depth / 2, Math.PI / 4, {\n          x: -width / 2 + depth / 2,\n          y: -depth / 2,\n          z: 0\n        });\n        let partTwo = this.getEllipsePointsXZ(radiusX, depth / 2, -Math.PI / 4, {\n          x: width / 2 - depth / 2,\n          y: -depth / 2,\n          z: 0\n        });\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        headWireframes.push([wireframeOne]);\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        headWireframes.push([wireframeTwo]);\n      } else if (geometry.type == \"BoxGeometry\") {\n        cube.rotation.y = Math.PI / 2;\n        let cubeWireframe = [];\n        let pointsOne = [new this.THREE.Vector3(-width / 2, 0, -depth / 2), new this.THREE.Vector3(-width / 2, 0, depth / 2), new this.THREE.Vector3(-width / 2 + depth, -depth, depth / 2), new this.THREE.Vector3(-width / 2 + depth, -depth, -depth / 2)];\n        let pointsTwo = [new this.THREE.Vector3(width / 2, 0, -depth / 2), new this.THREE.Vector3(width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2 - depth, -depth, depth / 2), new this.THREE.Vector3(width / 2 - depth, -depth, -depth / 2)];\n\n        for (let i = 0; i < pointsOne.length; i++) {\n          let newLine = this.drawLineFromPoints([pointsOne[i], pointsTwo[i]], lineProps);\n          cubeWireframe.push(newLine);\n        }\n\n        headWireframes.push(cubeWireframe);\n        let edgePointsOne = [...pointsOne.slice(1, 3), ...pointsTwo.slice(1, 3)];\n        let edgePointsTwo = [pointsOne[0], pointsOne[3], pointsTwo[0], pointsTwo[3]];\n        let edgesLineArr = [];\n\n        for (let i = 0; i < edgePointsOne.length; i++) {\n          let newLine = this.drawLineFromPoints([edgePointsOne[i], edgePointsTwo[i]], lineProps);\n          edgesLineArr.push(newLine);\n        }\n\n        headWireframes.push(edgesLineArr);\n      }\n\n      return [cube, headWireframes];\n    }\n\n    drawClampAxis(axis, shank, tipData, propertiesData, isLeft) {\n      const depth = shank.size;\n      const width = shank.size;\n      let height = axis.length;\n      let axisWireframes = [];\n      let offsetFromCenter = isLeft ? -(shank.offsetLength / 2) : shank.offsetLength / 2;\n      let positiveOrNegative = Math.sign(offsetFromCenter);\n\n      if (tipData && tipData.type > 0) {\n        height -= tipData.height;\n      }\n\n      let lineProps = propertiesData.fastener.headSide.line;\n      let material = this.getMaterial(propertiesData);\n      let returnMeshes = [];\n      let geometry, geometryTwo;\n      let tipSideLength = shank.tipSideLength;\n\n      if (tipSideLength) {\n        height -= tipSideLength + 0.0001;\n\n        if (shank.type == 1) {\n          geometryTwo = new this.THREE.CylinderGeometry(width / 2, width / 2, tipSideLength, 32);\n        } else {\n          geometryTwo = new this.THREE.BoxGeometry(depth, tipSideLength, width);\n        }\n\n        geometryTwo.translate(0, -axis.length + tipSideLength / 2 + tipData.height, 0);\n        let cubeTwo;\n\n        if (propertiesData.fastener.tipSide) {\n          let lineProps = propertiesData.fastener.tipSide.line;\n          let materialTwo = this.GraphicsThree3D.getMaterialFromColor(propertiesData.fastener.tipSide.volume.color);\n          cubeTwo = new this.THREE.Mesh(geometryTwo, materialTwo);\n          cubeTwo.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n          returnMeshes.push(cubeTwo);\n\n          if (geometryTwo.type == \"BoxGeometry\") {\n            let partOne = [];\n            let partTwo = [];\n            let geoVertices = [...geometryTwo.vertices].map(vertex => vertex.clone().add(new this.THREE.Vector3(cubeTwo.position.x, 0, 0)));\n            let firstPart = geoVertices[0].y;\n            geoVertices.forEach(vertex => {\n              if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n            });\n\n            if (!propertiesData.fastener.headSide) {\n              let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n              axisWireframes.push([wireframeOne]);\n            }\n\n            if (!tipData) {\n              let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n              axisWireframes.push([wireframeTwo]);\n            } else if (tipData.type == 3 || tipData.type == 4) {\n              for (let i = 0; i <= 2; i += 2) {\n                let wireframeTwo = this.drawLineFromPoints(partTwo.slice(i, i + 2), lineProps, true);\n                axisWireframes.push([wireframeTwo]);\n              }\n            }\n\n            let edgesLineArr = [];\n\n            for (let i = 0; i < partOne.length; i++) {\n              let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n              edgesLineArr.push(newLine);\n            }\n\n            axisWireframes.push(edgesLineArr);\n          } else {\n            let partOne = [];\n            let partTwo = [];\n            let geoVertices = geometryTwo.vertices;\n            let firstPart = geoVertices[0].y;\n            geoVertices.forEach(vertex => {\n              if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n            });\n            partOne.splice(partOne.length - 1, 1);\n            partTwo.splice(partTwo.length - 1, 1);\n\n            if (!propertiesData.fastener.headSide) {\n              let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n              wireframeOne.position.x += offsetFromCenter - positiveOrNegative * width / 2;\n              axisWireframes.push([wireframeOne]);\n            }\n\n            if (!tipData) {\n              let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n              wireframeTwo.position.x += offsetFromCenter - positiveOrNegative * width / 2;\n              axisWireframes.push([wireframeTwo]);\n            }\n          }\n        }\n      }\n\n      if (shank.type == 1) {\n        geometry = new this.THREE.CylinderGeometry(width / 2, width / 2, height, 32);\n      } else if (shank.type == 2) {\n        geometry = new this.THREE.BoxGeometry(depth, height, width);\n        let cubeWireframe = [];\n        let pointsOne = [new this.THREE.Vector3(-width / 2, 0, -depth / 2), new this.THREE.Vector3(-width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2, 0, -depth / 2)];\n        let pointsTwo = [new this.THREE.Vector3(-width / 2, -height, -depth / 2), new this.THREE.Vector3(-width / 2, -height, depth / 2), new this.THREE.Vector3(width / 2, -height, depth / 2), new this.THREE.Vector3(width / 2, -height, -depth / 2)];\n\n        if (isLeft) {\n          pointsOne.map((point, index) => {\n            if (index > 1) point.sub(new this.THREE.Vector3(0, width, 0));\n          });\n        } else {\n          pointsOne.map((point, index) => {\n            if (index < 2) point.sub(new this.THREE.Vector3(0, width, 0));\n          });\n        }\n\n        for (let i = 0; i < pointsOne.length; i++) {\n          pointsOne[i].add(new this.THREE.Vector3(offsetFromCenter - positiveOrNegative * width / 2, 0, 0));\n          pointsTwo[i].add(new this.THREE.Vector3(offsetFromCenter - positiveOrNegative * width / 2, 0, 0));\n          let newLine = this.drawLineFromPoints([pointsOne[i], pointsTwo[i]], lineProps);\n          cubeWireframe.push(newLine);\n        }\n\n        axisWireframes.push(cubeWireframe);\n\n        if (tipData && (tipData.type == 3 || tipData.type == 4) && !propertiesData.fastener.tipSide) {\n          for (let i = 0; i <= 2; i += 2) {\n            let sideWireframe = this.drawLineFromPoints(pointsTwo.slice(i, i + 2), lineProps, true);\n            axisWireframes.push([sideWireframe]);\n          }\n        }\n      }\n\n      geometry.translate(0, -height / 2, 0); ///\n\n      let cube;\n      let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n      let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n      eraserBox.rotation.z = Math.PI / 4;\n      eraserBox.position.set(isLeft ? depth / 2 : -depth / 2, 0, 0);\n      eraserBox.updateMatrix();\n      let geomesh = new this.THREE.Mesh(geometry);\n      let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n      let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n      let cubeGeoCSG = geoCSG.subtract(eraserCSG);\n      cube = this.GraphicsThree3D.CSG.toMesh(cubeGeoCSG, new this.THREE.Matrix4());\n      cube.material = material; ///\n\n      cube.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n      returnMeshes.push(cube);\n      if (returnMeshes.length == 1) returnMeshes.push(null);\n      return [...returnMeshes, axisWireframes];\n    }\n\n    drawClampTip(shank, propertiesData, tip, isLeft, tipGeo) {\n      let tipSideProps = propertiesData.fastener.tipSide;\n      let headSideLineProps = propertiesData.fastener.headSide.line;\n      const colorAxis = tipSideProps && shank.tipSideLength ? tipSideProps.volume.color : propertiesData.fastener.headSide.volume.color;\n      let addTopWireframe = true;\n\n      if (shank.type == 1 && (tip.type == 1 || tip.type == 2)) {\n        addTopWireframe = false;\n      } else if (shank.type == 2 && (tip.type == 3 || tip.type == 4 || tip.type == 5 || tip.type == 6)) {\n        addTopWireframe = false;\n      }\n\n      let tipWireframes = this.makeTipWireframe(tip, tipGeo, tipSideProps && shank.tipSideLength ? tipSideProps.line : headSideLineProps, addTopWireframe);\n      let material = this.GraphicsThree3D.getMaterialFromColor(colorAxis);\n      var cylinder = new this.THREE.Mesh(tipGeo, material);\n      var offsetFromCenter = isLeft ? -(shank.offsetLength / 2) : shank.offsetLength / 2;\n      var positiveOrNegative = Math.sign(offsetFromCenter);\n      cylinder.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n      tipWireframes.forEach(wireframe => wireframe.forEach(line => line.position.add(new this.THREE.Vector3(cylinder.position.x, 0, 0))));\n      return [cylinder, tipWireframes];\n    }\n\n    getLineMaterial(lineProps) {\n      let lineColor = lineProps.color;\n      let materialOptions = {\n        color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n        transparent: true,\n        opacity: lineColor.alpha / 255\n      };\n      let lineMaterial = new this.THREE.LineBasicMaterial(materialOptions);\n      return lineMaterial;\n    }\n\n    getEllipsePointsXZ(radiusX, radiusZ, rotationZ, position) {\n      let geo = new this.THREE.CircleGeometry(radiusZ, 32);\n      geo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusX / radiusZ, 1));\n      geo.rotateY(Math.PI / 2);\n      geo.rotateZ(rotationZ);\n      geo.translate(position.x, position.y, position.z);\n      let vertices = geo.vertices;\n      vertices.splice(0, 1);\n      return vertices;\n    }\n\n  }\n\n  _exports.FastenerLoader = FastenerLoader;\n\n  class Spiral {\n    constructor(spiralRadius, spiralHeight, initDist, spiralDebth, spiralRowWidth, material, parent, color = 0xffaa00) {\n      this.color = color;\n      this.initDist = initDist;\n      this.spiralHeight = spiralHeight;\n      this.spiralDebth = spiralDebth;\n      this.spiralRadius = spiralRadius;\n      this.spiralRowWidth = spiralRowWidth;\n      this.currentHeight = -spiralHeight / 2;\n      this.x = 0;\n      this.y = 0;\n      this.angle = 0;\n      this.xPlus = 0;\n      this.zPlus = 0;\n      this.material = material;\n      this.parent = parent; //this.geometry = geometry;\n    }\n\n    currentPosition(dist) {\n      return this.currentHeight += dist;\n    }\n\n    currentAngle() {\n      this.angle -= 360 / 32;\n      this.x = Math.cos(Math.PI / 180 * this.angle);\n      this.z = Math.sin(Math.PI / 180 * this.angle);\n    }\n\n    getCylGeo() {\n      if (this.spiralHeight > 0 && this.spiralRadius == 0) this.spiralRadius += 0.0001;\n      const cylinder = new this.parent.THREE.CylinderGeometry(this.spiralRadius, this.spiralRadius, this.spiralHeight, 32);\n      cylinder.translate(0, -this.spiralHeight / 2, 0);\n      return cylinder;\n    }\n\n    getSpiralGeo() {\n      let wireframePoints = [[], [], []];\n      const dist = this.initDist / 32;\n      const c = this.currentPosition(dist);\n      this.currentAngle();\n      var geometry = new this.parent.THREE.Geometry();\n      let pointOne = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c, this.spiralRadius * this.z);\n      let pointTwo = new this.parent.THREE.Vector3((this.spiralDebth + this.spiralRadius) * this.x, c - this.spiralRowWidth / 2, (this.spiralDebth + this.spiralRadius) * this.z);\n      let pointThree = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c - this.spiralRowWidth, this.spiralRadius * this.z);\n      geometry.vertices.push(pointOne);\n      geometry.vertices.push(pointTwo);\n      geometry.vertices.push(pointThree);\n      wireframePoints[0].push(pointOne);\n      wireframePoints[1].push(pointTwo);\n      wireframePoints[2].push(pointThree);\n      var face0 = new this.parent.THREE.Face3(0, 1, 2);\n      geometry.faces.push(face0);\n      var l = 0;\n\n      for (let i = 0; i < 32 * (this.spiralHeight - this.spiralRowWidth) / this.initDist; i++) {\n        const c1 = this.currentPosition(dist);\n        this.currentAngle();\n        pointOne = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c1, this.spiralRadius * this.z);\n        pointTwo = new this.parent.THREE.Vector3((this.spiralDebth + this.spiralRadius) * this.x, c1 - this.spiralRowWidth / 2, (this.spiralDebth + this.spiralRadius) * this.z);\n        pointThree = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c1 - this.spiralRowWidth, this.spiralRadius * this.z);\n        geometry.vertices.push(pointOne);\n        geometry.vertices.push(pointTwo);\n        geometry.vertices.push(pointThree);\n        wireframePoints[0].push(pointOne);\n        wireframePoints[1].push(pointTwo);\n        wireframePoints[2].push(pointThree);\n        var face1 = new this.parent.THREE.Face3(l + 1, l + 3, l + 4);\n        var face2 = new this.parent.THREE.Face3(l + 1, l, l + 3);\n        var face3 = new this.parent.THREE.Face3(l, l + 2, l + 5);\n        var face4 = new this.parent.THREE.Face3(l, l + 3, l + 5);\n        var face5 = new this.parent.THREE.Face3(l + 1, l + 3, l + 2);\n        var face6 = new this.parent.THREE.Face3(l + 1, l + 4, l + 3);\n        var face7 = new this.parent.THREE.Face3(l + 1, l + 5, l + 2);\n        var face8 = new this.parent.THREE.Face3(l + 1, l + 4, l + 5);\n        var face9 = new this.parent.THREE.Face3(l, l + 1, l + 2); //add the face to the geometry's faces array\n\n        geometry.faces.push(face1);\n        geometry.faces.push(face2);\n        geometry.faces.push(face3);\n        geometry.faces.push(face4);\n        geometry.faces.push(face5);\n        geometry.faces.push(face6);\n        geometry.faces.push(face7);\n        geometry.faces.push(face8);\n        geometry.faces.push(face9); ///\n        // let testFaceOne = new this.parent.THREE.Face3(l, l + 1, l + 3);\n        // let testFaceTwo = new this.parent.THREE.Face3(l + 1, l + 4, l + 3);\n        // let testFaceThree = new this.parent.THREE.Face3(l + 1, l + 2, l + 4);\n        // let testFaceFour = new this.parent.THREE.Face3(l + 2, l + 5, l + 4);\n        // geometry.faces.push(testFaceOne);\n        // geometry.faces.push(testFaceTwo);\n        // geometry.faces.push(testFaceThree);\n        // geometry.faces.push(testFaceFour);\n\n        l += 3;\n      }\n\n      geometry.computeFaceNormals();\n      geometry.computeVertexNormals();\n      geometry.translate(0, -this.spiralHeight / 2 + this.spiralRowWidth - this.initDist / 32, 0);\n      wireframePoints.forEach(points => {\n        points.map(point => point = point.clone().setComponent(1, -this.spiralHeight / 2 + this.spiralRowWidth - this.initDist / 32));\n      });\n      return [geometry, wireframePoints];\n    }\n\n  }\n});","define(\"m08-2020/lib old/ThreeCSG\", [\"exports\"], function (_exports) {\n  'use strict';\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ThreeBSP = void 0;\n  var THREE;\n\n  const ThreeBSP = function () {\n    var ThreeBSP,\n        EPSILON = 1e-5,\n        COPLANAR = 0,\n        FRONT = 1,\n        BACK = 2,\n        SPANNING = 3;\n\n    ThreeBSP = function (geometry, threeRef) {\n      // Convert THREE.Geometry to ThreeBSP\n      var i,\n          _length_i,\n          face,\n          vertex,\n          faceVertexUvs,\n          polygon,\n          polygons = [],\n          tree;\n\n      if (threeRef) {\n        THREE = threeRef;\n      }\n\n      if (geometry instanceof THREE.Geometry) {\n        this.matrix = new THREE.Matrix4();\n      } else if (geometry instanceof THREE.Mesh) {\n        // #todo: add hierarchy support\n        geometry.updateMatrix();\n        this.matrix = geometry.matrixWorld.clone(); // Lee was here -- changed matrix to matrixWorld\n\n        geometry = geometry.geometry;\n      } else if (geometry instanceof ThreeBSP.Node) {\n        this.tree = geometry;\n        this.matrix = new THREE.Matrix4();\n        return this;\n      } else {\n        throw 'ThreeBSP: Given geometry is unsupported';\n      }\n\n      for (i = 0, _length_i = geometry.faces.length; i < _length_i; i++) {\n        face = geometry.faces[i];\n        faceVertexUvs = geometry.faceVertexUvs[0][i];\n        polygon = new ThreeBSP.Polygon();\n\n        if (face instanceof THREE.Face3) {\n          vertex = geometry.vertices[face.a];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[0].u, faceVertexUvs[0].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.b];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], new THREE.Vector2(faceVertexUvs[1].u, faceVertexUvs[1].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.c];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], new THREE.Vector2(faceVertexUvs[2].u, faceVertexUvs[2].v));\n          polygon.vertices.push(vertex);\n        } else if (typeof THREE.Face4) {\n          vertex = geometry.vertices[face.a];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[0].u, faceVertexUvs[0].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.b];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1], new THREE.Vector2(faceVertexUvs[1].u, faceVertexUvs[1].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.c];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2], new THREE.Vector2(faceVertexUvs[2].u, faceVertexUvs[2].v));\n          polygon.vertices.push(vertex);\n          vertex = geometry.vertices[face.d];\n          vertex.applyMatrix4(this.matrix);\n          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[3], new THREE.Vector2(faceVertexUvs[3].u, faceVertexUvs[3].v));\n          polygon.vertices.push(vertex);\n        } else {\n          throw 'Invalid face type at index ' + i;\n        }\n\n        polygon.calculateProperties();\n        polygons.push(polygon);\n      }\n\n      ;\n      this.tree = new ThreeBSP.Node(polygons);\n    };\n\n    ThreeBSP.prototype.subtract = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.invert();\n      a.clipTo(b);\n      b.clipTo(a);\n      b.invert();\n      b.clipTo(a);\n      b.invert();\n      a.build(b.allPolygons());\n      a.invert();\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.union = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.clipTo(b);\n      b.clipTo(a);\n      b.invert();\n      b.clipTo(a);\n      b.invert();\n      a.build(b.allPolygons());\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.intersect = function (other_tree) {\n      var a = this.tree.clone(),\n          b = other_tree.tree.clone();\n      a.invert();\n      b.clipTo(a);\n      b.invert();\n      a.clipTo(b);\n      b.clipTo(a);\n      a.build(b.allPolygons());\n      a.invert();\n      a = new ThreeBSP(a);\n      a.matrix = this.matrix;\n      return a;\n    };\n\n    ThreeBSP.prototype.toGeometry = function () {\n      var i,\n          j,\n          matrix = new THREE.Matrix4().getInverse(this.matrix),\n          geometry = new THREE.Geometry(),\n          polygons = this.tree.allPolygons(),\n          polygon_count = polygons.length,\n          polygon,\n          polygon_vertice_count,\n          vertice_dict = {},\n          vertex_idx_a,\n          vertex_idx_b,\n          vertex_idx_c,\n          vertex,\n          face,\n          verticeNormals,\n          verticeUvs;\n\n      for (i = 0; i < polygon_count; i++) {\n        polygon = polygons[i];\n        polygon_vertice_count = polygon.vertices.length;\n\n        for (j = 2; j < polygon_vertice_count; j++) {\n          verticeNormals = [];\n          verticeUvs = [];\n          vertex = polygon.vertices[0];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          vertex = polygon.vertices[j - 1];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          vertex = polygon.vertices[j];\n          verticeNormals.push(vertex.normal);\n          verticeUvs.push(new THREE.Vector2(vertex.uv.x, vertex.uv.y));\n          vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);\n          vertex.applyMatrix4(matrix);\n\n          if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {\n            vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];\n          } else {\n            geometry.vertices.push(vertex);\n            vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;\n          }\n\n          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));\n          face.vertexNormals.push(verticeNormals[0], verticeNormals[1], verticeNormals[2]);\n          geometry.faces.push(face);\n          geometry.faceVertexUvs[0].push(verticeUvs);\n        }\n      }\n\n      return geometry;\n    };\n\n    ThreeBSP.prototype.toMesh = function (material) {\n      var geometry = this.toGeometry(),\n          mesh = new THREE.Mesh(geometry, material);\n      mesh.position.getPositionFromMatrix(this.matrix); //mesh.rotation.setEulerFromRotationMatrix( this.matrix );//not working and commented\n\n      mesh.position.setFromMatrixPosition(this.matrix); //from stackoverflow (https://stackoverflow.com/questions/17711914/seteulerfromrotationmatrix-does-not-exist-in-three-js)\n\n      return mesh;\n    };\n\n    ThreeBSP.Polygon = function (vertices, normal, w) {\n      if (!(vertices instanceof Array)) {\n        vertices = [];\n      }\n\n      this.vertices = vertices;\n\n      if (vertices.length > 0) {\n        this.calculateProperties();\n      } else {\n        this.normal = this.w = undefined;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.calculateProperties = function () {\n      var a = this.vertices[0],\n          b = this.vertices[1],\n          c = this.vertices[2];\n      this.normal = b.clone().subtract(a).cross(c.clone().subtract(a)).normalize();\n      this.w = this.normal.clone().dot(a);\n      return this;\n    };\n\n    ThreeBSP.Polygon.prototype.clone = function () {\n      var i,\n          vertice_count,\n          polygon = new ThreeBSP.Polygon();\n\n      for (i = 0, vertice_count = this.vertices.length; i < vertice_count; i++) {\n        polygon.vertices.push(this.vertices[i].clone());\n      }\n\n      ;\n      polygon.calculateProperties();\n      return polygon;\n    };\n\n    ThreeBSP.Polygon.prototype.flip = function () {\n      var i,\n          vertices = [];\n      this.normal.multiplyScalar(-1);\n      this.w *= -1;\n\n      for (i = this.vertices.length - 1; i >= 0; i--) {\n        //this.vertices[i].normal.multiplyScalar( -1 );\n        vertices.push(this.vertices[i]);\n      }\n\n      ;\n      this.vertices = vertices;\n      return this;\n    };\n\n    ThreeBSP.Polygon.prototype.classifyVertex = function (vertex) {\n      var side_value = this.normal.dot(vertex) - this.w;\n\n      if (side_value < -EPSILON) {\n        return BACK;\n      } else if (side_value > EPSILON) {\n        return FRONT;\n      } else {\n        return COPLANAR;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.classifySide = function (polygon) {\n      var i,\n          vertex,\n          classification,\n          num_positive = 0,\n          num_negative = 0,\n          vertice_count = polygon.vertices.length;\n\n      for (i = 0; i < vertice_count; i++) {\n        vertex = polygon.vertices[i];\n        classification = this.classifyVertex(vertex);\n\n        if (classification === FRONT) {\n          num_positive++;\n        } else if (classification === BACK) {\n          num_negative++;\n        }\n      }\n\n      if (num_positive > 0 && num_negative === 0) {\n        return FRONT;\n      } else if (num_positive === 0 && num_negative > 0) {\n        return BACK;\n      } else if (num_positive === 0 && num_negative === 0) {\n        return COPLANAR;\n      } else {\n        return SPANNING;\n      }\n    };\n\n    ThreeBSP.Polygon.prototype.splitPolygon = function (polygon, coplanar_front, coplanar_back, front, back) {\n      var classification = this.classifySide(polygon);\n\n      if (classification === COPLANAR) {\n        (this.normal.dot(polygon.normal) > 0 ? coplanar_front : coplanar_back).push(polygon);\n      } else if (classification === FRONT) {\n        front.push(polygon);\n      } else if (classification === BACK) {\n        back.push(polygon);\n      } else {\n        var vertice_count,\n            i,\n            j,\n            ti,\n            tj,\n            vi,\n            vj,\n            t,\n            v,\n            f = [],\n            b = [];\n\n        for (i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++) {\n          j = (i + 1) % vertice_count;\n          vi = polygon.vertices[i];\n          vj = polygon.vertices[j];\n          ti = this.classifyVertex(vi);\n          tj = this.classifyVertex(vj);\n          if (ti != BACK) f.push(vi);\n          if (ti != FRONT) b.push(vi);\n\n          if ((ti | tj) === SPANNING) {\n            t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().subtract(vi));\n            v = vi.interpolate(vj, t);\n            f.push(v);\n            b.push(v);\n          }\n        }\n\n        if (f.length >= 3) front.push(new ThreeBSP.Polygon(f).calculateProperties());\n        if (b.length >= 3) back.push(new ThreeBSP.Polygon(b).calculateProperties());\n      }\n    };\n\n    ThreeBSP.Vertex = function (x, y, z, normal, uv) {\n      this.x = x;\n      this.y = y;\n      this.z = z;\n      this.normal = normal || new THREE.Vector3();\n      this.uv = uv || new THREE.Vector2();\n    };\n\n    ThreeBSP.Vertex.prototype.clone = function () {\n      return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());\n    };\n\n    ThreeBSP.Vertex.prototype.add = function (vertex) {\n      this.x += vertex.x;\n      this.y += vertex.y;\n      this.z += vertex.z;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.subtract = function (vertex) {\n      this.x -= vertex.x;\n      this.y -= vertex.y;\n      this.z -= vertex.z;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.multiplyScalar = function (scalar) {\n      this.x *= scalar;\n      this.y *= scalar;\n      this.z *= scalar;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.cross = function (vertex) {\n      var x = this.x,\n          y = this.y,\n          z = this.z;\n      this.x = y * vertex.z - z * vertex.y;\n      this.y = z * vertex.x - x * vertex.z;\n      this.z = x * vertex.y - y * vertex.x;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.normalize = function () {\n      var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);\n      this.x /= length;\n      this.y /= length;\n      this.z /= length;\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.dot = function (vertex) {\n      return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;\n    };\n\n    ThreeBSP.Vertex.prototype.lerp = function (a, t) {\n      this.add(a.clone().subtract(this).multiplyScalar(t));\n      this.normal.add(a.normal.clone().sub(this.normal).multiplyScalar(t));\n      this.uv.add(a.uv.clone().sub(this.uv).multiplyScalar(t));\n      return this;\n    };\n\n    ThreeBSP.Vertex.prototype.interpolate = function (other, t) {\n      return this.clone().lerp(other, t);\n    };\n\n    ThreeBSP.Node = function (polygons) {\n      var i,\n          polygon_count,\n          front = [],\n          back = [];\n      this.polygons = [];\n      this.front = this.back = undefined;\n      if (!(polygons instanceof Array) || polygons.length === 0) return;\n      this.divider = polygons[0].clone();\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n      }\n\n      if (front.length > 0) {\n        this.front = new ThreeBSP.Node(front);\n      }\n\n      if (back.length > 0) {\n        this.back = new ThreeBSP.Node(back);\n      }\n    };\n\n    ThreeBSP.Node.prototype.build = function (polygons) {\n      var i,\n          polygon_count,\n          front = [],\n          back = [];\n\n      if (!this.divider) {\n        this.divider = polygons[0].clone();\n      }\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n      }\n\n      if (front.length > 0) {\n        if (!this.front) this.front = new ThreeBSP.Node();\n        this.front.build(front);\n      }\n\n      if (back.length > 0) {\n        if (!this.back) this.back = new ThreeBSP.Node();\n        this.back.build(back);\n      }\n    };\n\n    ThreeBSP.Node.prototype.allPolygons = function () {\n      var polygons = this.polygons.slice();\n      if (this.front) polygons = polygons.concat(this.front.allPolygons());\n      if (this.back) polygons = polygons.concat(this.back.allPolygons());\n      return polygons;\n    };\n\n    ThreeBSP.Node.prototype.clone = function () {\n      var node = new ThreeBSP.Node();\n      node.divider = this.divider.clone();\n      node.polygons = this.polygons.map(function (polygon) {\n        return polygon.clone();\n      });\n      node.front = this.front && this.front.clone();\n      node.back = this.back && this.back.clone();\n      return node;\n    };\n\n    ThreeBSP.Node.prototype.invert = function () {\n      var i, polygon_count, temp;\n\n      for (i = 0, polygon_count = this.polygons.length; i < polygon_count; i++) {\n        this.polygons[i].flip();\n      }\n\n      this.divider.flip();\n      if (this.front) this.front.invert();\n      if (this.back) this.back.invert();\n      temp = this.front;\n      this.front = this.back;\n      this.back = temp;\n      return this;\n    };\n\n    ThreeBSP.Node.prototype.clipPolygons = function (polygons) {\n      var i, polygon_count, front, back;\n      if (!this.divider) return polygons.slice();\n      front = [], back = [];\n\n      for (i = 0, polygon_count = polygons.length; i < polygon_count; i++) {\n        this.divider.splitPolygon(polygons[i], front, back, front, back); //if (window.debug) console.debug( front.length, back.length );\n      }\n\n      if (this.front) front = this.front.clipPolygons(front);\n      if (this.back) back = this.back.clipPolygons(back);else back = [];\n      return front.concat(back);\n    };\n\n    ThreeBSP.Node.prototype.clipTo = function (node) {\n      this.polygons = node.clipPolygons(this.polygons);\n      if (this.front) this.front.clipTo(node);\n      if (this.back) this.back.clipTo(node);\n    };\n\n    return ThreeBSP;\n  }();\n\n  _exports.ThreeBSP = ThreeBSP;\n});","define(\"m08-2020/lib old/ThreeExtensions/CustomEllipseCurve\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.CustomEllipseCurve = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  class CustomEllipseCurve extends THREE.EllipseCurve {\n    constructor(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, rotationPitch) {\n      super(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);\n\n      _defineProperty(this, \"getPoint\", function (t) {\n        // this is modified version of three js curve.getPoint to return vector3 instead of vector2;\n        const point = new THREE.Vector3();\n        const twoPi = Math.PI * 2;\n        let deltaAngle = this.aEndAngle - this.aStartAngle;\n        const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI\n\n        while (deltaAngle < 0) deltaAngle += twoPi;\n\n        while (deltaAngle > twoPi) deltaAngle -= twoPi;\n\n        if (deltaAngle < Number.EPSILON) {\n          if (samePoints) {\n            deltaAngle = 0;\n          } else {\n            deltaAngle = twoPi;\n          }\n        }\n\n        if (this.aClockwise === true && !samePoints) {\n          if (deltaAngle === twoPi) {\n            deltaAngle = -twoPi;\n          } else {\n            deltaAngle = deltaAngle - twoPi;\n          }\n        }\n\n        const angle = this.aStartAngle + t * deltaAngle;\n        let x = this.aX + this.xRadius * Math.cos(angle);\n        let y = this.aY + this.yRadius * Math.sin(angle);\n\n        if (this.aRotation !== 0) {\n          const cos = Math.cos(this.aRotation);\n          const sin = Math.sin(this.aRotation);\n          const tx = x - this.aX;\n          const ty = y - this.aY; // Rotate the point about the center of the ellipse.\n\n          x = tx * cos - ty * sin + this.aX;\n          y = tx * sin + ty * cos + this.aY;\n        }\n\n        return point.set(x, -t * this.rotationPitch, y);\n      });\n\n      _defineProperty(this, \"getTangent\", function (t) {\n        // this is modified version of three js getTangent to modify tangent vector;\n        const delta = 0.0001;\n        let t1 = t - delta;\n        let t2 = t + delta; // Capping in case of danger\n\n        if (t1 < 0) t1 = 0;\n        if (t2 > 1) t2 = 1;\n        const pt1 = this.getPoint(t1);\n        const pt2 = this.getPoint(t2);\n        const tangent = new THREE.Vector3();\n        tangent.copy(pt2).add(new THREE.Vector3(0, this.rotationPitch * delta, 0)).sub(pt1).normalize();\n        return tangent;\n      });\n\n      _defineProperty(this, \"computeFrenetFrames\", function (segments, closed) {\n        // this is modified version of three js computeFrenetFrames to modify normal vector;\n        // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n        const normal = new THREE.Vector3();\n        const tangents = [];\n        const normals = [];\n        const binormals = [];\n        const vec = new THREE.Vector3();\n        const mat = new THREE.Matrix4(); // compute the tangent vectors for each segment on the curve\n\n        for (let i = 0; i <= segments; i++) {\n          const u = i / segments;\n          tangents[i] = this.getTangentAt(u, new THREE.Vector3());\n          tangents[i].normalize();\n        } // select an initial normal vector perpendicular to the first tangent vector,\n        // and in the direction of the minimum tangent xyz component\n\n\n        normals[0] = new THREE.Vector3();\n        binormals[0] = new THREE.Vector3();\n        let min = Number.MAX_VALUE;\n        const tx = Math.abs(tangents[0].x);\n        const ty = Math.abs(tangents[0].y);\n        const tz = Math.abs(tangents[0].z);\n\n        if (tx <= min) {\n          min = tx;\n          normal.set(1, 0, 0);\n        }\n\n        if (ty <= min) {\n          min = ty;\n          normal.set(0, 1, 0);\n        }\n\n        if (tz <= min) {\n          normal.set(0, 0, 1);\n        }\n\n        vec.crossVectors(tangents[0], normal).normalize();\n        normals[0].crossVectors(tangents[0], vec);\n        binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n        for (let i = 1; i <= segments; i++) {\n          normals[i] = normals[i - 1].clone();\n          binormals[i] = binormals[i - 1].clone();\n          vec.crossVectors(tangents[i - 1], tangents[i]);\n\n          if (vec.length() > Number.EPSILON) {\n            vec.normalize();\n            const theta = Math.acos(THREE.MathUtils.clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors\n\n            normals[i].applyAxisAngle(new THREE.Vector3(0, 1, 0), theta);\n          }\n\n          binormals[i].crossVectors(tangents[i], normals[i]);\n        } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n\n        if (closed === true) {\n          let theta = Math.acos(THREE.MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1));\n          theta /= segments;\n\n          if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {\n            theta = -theta;\n          }\n\n          for (let i = 1; i <= segments; i++) {\n            // twist a little...\n            normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i));\n            binormals[i].crossVectors(tangents[i], normals[i]);\n          }\n        }\n\n        return {\n          tangents: tangents,\n          normals: normals,\n          binormals: binormals\n        };\n      });\n\n      this.rotationPitch = rotationPitch;\n    }\n\n  }\n\n  _exports.CustomEllipseCurve = CustomEllipseCurve;\n});","define(\"m08-2020/lib old/ThreeExtensions/OutlinesGeometry\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.OutlinesGeometry = void 0;\n\n  class OutlinesGeometry extends THREE.Geometry {\n    constructor(geometry) {\n      super();\n      this.faceSplitFailed = false;\n      this.initGeometry(geometry);\n    }\n\n    initGeometry(geometry) {\n      let geometry2 = this.getGeometry(geometry);\n      this.vertices = geometry2.vertices;\n      this.faces = geometry2.faces;\n      this.splitFaces();\n    }\n\n    splitFaces() {\n      const DELTA = 1e-5;\n      const keys = ['a', 'b', 'c'];\n      let vertices = this.vertices;\n      let faces = this.faces;\n      let vertexDict = vertices.reduce((dictX, vertex, index) => {\n        if (vertex.x in dictX) dictX[vertex.x].push({\n          vertex,\n          index\n        });else {\n          dictX[vertex.x] = [];\n          dictX[vertex.x].push({\n            vertex,\n            index\n          });\n        }\n        return dictX;\n      }, {}); // for ( let keyX of Object.keys( vertexDict ) ) {\n      //   vertexDict[ keyX ] = vertexDict[ keyX ].reduce((dictY, vertexData) => {\n      //     if ( vertexData.vertex.y in dictY ) dictY[ vertexData.vertex.y ].push( vertexData );\n      //     else { dictY[ vertexData.vertex.y ] = []; dictY[ vertexData.vertex.y ].push( vertexData ) }\n      //     return dictY;\n      //   }, {});\n      //   for ( let keyY of Object.keys( vertexDict[ keyX ] ) ) {\n      //     vertexDict[ keyX ][ keyY ] = vertexDict[ keyX ][ keyY ].reduce((dictZ, vertexData) => {\n      //       if ( vertexData.vertex.z in dictZ ) dictZ[ vertexData.vertex.z ].push( vertexData );\n      //       else { dictZ[ vertexData.vertex.z ] = []; dictZ[ vertexData.vertex.z ].push( vertexData ) }\n      //       return dictZ;\n      //     }, {});\n      //   }\n      // }\n      // let sortedVertices = vertices\n      //                     .map(( vertex, index ) => {\n      //                       return { vertex, index };\n      //                     })\n      //                     .sort( ( a, b ) => a.vertex.x - b.vertex.x );\n\n      let sortedKeysX = Object.keys(vertexDict).sort((a, b) => parseFloat(a) - parseFloat(b));\n      let edge = [0, 0],\n          face;\n\n      for (let i = 0; i < faces.length; i++) {\n        face = faces[i];\n\n        EDGES_LOOP: for (let j = 0; j < 3; j++) {\n          edge[0] = face[keys[j]];\n          edge[1] = face[keys[(j + 1) % 3]];\n          let vertexOne = vertices[edge[0]];\n          let vertexTwo = vertices[edge[1]];\n          let lowerBound = findBoundIndex(vertexOne.x, sortedKeysX);\n          let upperBound = findBoundIndex(vertexTwo.x, sortedKeysX);\n          if (lowerBound > upperBound) [lowerBound, upperBound] = [upperBound, lowerBound];\n          lowerBound = Math.max(lowerBound - 2, 0);\n          upperBound = Math.min(upperBound + 2, sortedKeysX.length - 1);\n          let line = new THREE.Line3(vertexOne, vertexTwo); // for each vertex checks if it lies in the edge\n\n          for (let e = lowerBound; e < upperBound; e++) {\n            let keyX = sortedKeysX[e];\n\n            for (let vertexData of vertexDict[keyX]) {\n              let i = vertexData.index;\n              if (i === edge[0] || i === edge[1]) continue;\n              let v = vertexData.vertex;\n              let closestPoint = line.closestPointToPoint(v, true, new THREE.Vector3());\n\n              if (closestPoint.distanceTo(v) < DELTA && v.distanceTo(line.start) > DELTA && v.distanceTo(line.end) > DELTA) {\n                // mark the current face as splitted so that his cords won't be considered\n                face.splitted = true; // Add two new faces, created splitting the face in two\n\n                let faceOne = new THREE.Face3(i, face[keys[(j + 2) % 3]], face[keys[j % 3]], face.normal, face.color, face.materialIndex);\n                let faceTwo = new THREE.Face3(i, face[keys[(j + 2) % 3]], face[keys[(j + 1) % 3]], face.normal, face.color, face.materialIndex);\n                faces.push(faceOne);\n                faces.push(faceTwo);\n                break EDGES_LOOP;\n              }\n            }\n          }\n\n          if (face.splitted) break;\n        }\n      }\n    }\n\n    getGeometry(geometry) {\n      let geometry2;\n      if (geometry.isBufferGeometry) geometry2 = new THREE.Geometry().fromBufferGeometry(geometry);else geometry2 = geometry.clone();\n      geometry2.mergeVertices();\n      geometry2.computeFaceNormals();\n      return geometry2;\n    }\n\n  }\n\n  _exports.OutlinesGeometry = OutlinesGeometry;\n\n  function findBoundIndex(coord, array) {\n    let min = 0;\n    let max = array.length - 1;\n\n    while (min <= max) {\n      let mid = Math.floor((min + max) / 2);\n      if (array[mid] == coord) return mid;else if (parseFloat(array[mid]) > parseFloat(coord)) max = mid - 1;else min = mid + 1;\n    }\n\n    return 0;\n  }\n});","define(\"m08-2020/lib old/ThreeExtensions/PlanarExtrusionPointsGenerator\", [\"exports\", \"three\", \"polybooljs\"], function (_exports, THREE, _polybooljs) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.PlanarExtrusionPointsGenerator = void 0;\n\n  class PlanarExtrusionPointsGenerator {\n    constructor(points2D, extrusionVector2D) {\n      this.extrudedPoints = [];\n      this.generateExtrudedPoints(points2D, extrusionVector2D);\n    }\n\n    generateExtrudedPoints(points2D, extrusionVector2D) {\n      let oldPoints = points2D.map(pt => pt.clone());\n      let newPoints = points2D.map(pt => pt.clone().add(extrusionVector2D));\n      let polyPts = this.getPolygonPoints(oldPoints, newPoints);\n      let segments = this.generateSegmentsFromPoints(polyPts);\n      let finalPolygon = this.uniteSegmentsAndReturnPolygon(segments);\n      let finalPoints = this.getPointsFromFinalPolygon(finalPolygon);\n      this.extrudedPoints.push(...finalPoints);\n    }\n\n    getPointsFromFinalPolygon(finalPolygon) {\n      return finalPolygon.regions[0].map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n    }\n\n    uniteSegmentsAndReturnPolygon(segments) {\n      let finalSegments = segments[0];\n\n      for (let i = 1; i < segments.length; i++) {\n        let comb = _polybooljs.default.combine(finalSegments, segments[i]);\n\n        finalSegments = _polybooljs.default.selectUnion(comb);\n      }\n\n      return _polybooljs.default.polygon(finalSegments);\n    }\n\n    generateSegmentsFromPoints(pointsArr) {\n      return pointsArr.reduce((allSegments, currentPoints) => [...allSegments, _polybooljs.default.segments({\n        regions: [currentPoints.map(pt => [pt.x, pt.y])],\n        inverted: false\n      })], []);\n    }\n\n    getPolygonPoints(oldPoints, newPoints) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let startPts = [...oldPoints, oldPoints[0].clone()];\n      let endPts = [...newPoints, newPoints[0].clone()];\n      let polyPts = [];\n      let segmentPoints;\n\n      for (let i = 0; i < startPts.length - 1; i++) {\n        segmentPoints = [startPts[i], startPts[i + 1], endPts[i], endPts[i + 1]];\n        segmentPoints = this.sortPoints(segmentPoints, Z_AXIS);\n        polyPts.push(segmentPoints);\n      }\n\n      return polyPts;\n    }\n\n    sortPoints(points, normal) {\n      let centerPoint = this.getCenterPoint(points);\n\n      let sortOrder = (a, b) => normal.dot(new THREE.Vector3().crossVectors(a.clone().sub(centerPoint), b.clone().sub(centerPoint)));\n\n      points.sort(sortOrder);\n      return points;\n    }\n\n    getCenterPoint(pointsArr) {\n      let length = pointsArr.length;\n      let centerPoint = pointsArr.reduce((finalVec, currentVec) => finalVec.add(currentVec), new THREE.Vector3()).multiplyScalar(1 / length);\n      return centerPoint;\n    }\n\n  }\n\n  _exports.PlanarExtrusionPointsGenerator = PlanarExtrusionPointsGenerator;\n});","define(\"m08-2020/lib old/ThreeExtensions/PlanarRotationPointsGenerator\", [\"exports\", \"three\", \"polybooljs\", \"delaunator\", \"poly-decomp\", \"m08-2020/lib old/WireframeGenerator\"], function (_exports, THREE, _polybooljs, _delaunator, PolyDecomp, _WireframeGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.PlanarRotationPointsGenerator = void 0;\n\n  class PlanarRotationPointsGenerator {\n    constructor(pointsToRotate, rotationParameters) {\n      this.rotatedPoints = [];\n      this.generateRotatedPoints(pointsToRotate, rotationParameters);\n    }\n\n    generateRotatedPoints(pointsToRotate, rotationParameters) {\n      const DETAIL = 64;\n      let {\n        rotationAngle,\n        rotationPitch\n      } = rotationParameters;\n      let numOfSegments = DETAIL * Math.abs(rotationAngle) / Math.PI;\n      let segmentRotation = rotationAngle / numOfSegments;\n      let polyPts2D = pointsToRotate.map(pt => [pt.x, pt.y]);\n      PolyDecomp.makeCCW(polyPts2D);\n      let convexPolygons = PolyDecomp.quickDecomp(polyPts2D);\n      let polygonSegments = [];\n      convexPolygons.forEach(polygon => {\n        let polyPts3D = polygon.map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n        let rotatedPointsPolygons = this.getRotatedPointsPolygons(polyPts3D, numOfSegments, segmentRotation);\n        let decomposedPolySegments = rotatedPointsPolygons[0];\n\n        for (let i = 1; i < rotatedPointsPolygons.length; i++) {\n          let comb = _polybooljs.default.combine(decomposedPolySegments, rotatedPointsPolygons[i]);\n\n          decomposedPolySegments = _polybooljs.default.selectUnion(comb);\n        }\n\n        polygonSegments.push(decomposedPolySegments);\n      });\n      let finalPolygon = this.uniteSegmentsAndReturnPolygon(polygonSegments);\n      let finalPoints = this.getPointsFromFinalPolygon(finalPolygon);\n      this.rotatedPoints.push(...finalPoints);\n    }\n\n    getPointsFromFinalPolygon(finalPolygon) {\n      return finalPolygon.regions[0].map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n    }\n\n    uniteSegmentsAndReturnPolygon(segments) {\n      let finalSegments = segments[0];\n\n      for (let i = 1; i < segments.length; i++) {\n        let comb = _polybooljs.default.combine(finalSegments, segments[i]);\n\n        finalSegments = _polybooljs.default.selectUnion(comb);\n      }\n\n      return _polybooljs.default.polygon(finalSegments);\n    }\n\n    getRotatedPointsPolygons(points, numOfSegments, segmentRotation) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let rotatedPointsPolygons = [];\n      let previousPts = [...points];\n\n      for (let s = 0; s < numOfSegments; s++) {\n        let oldPoints = previousPts.map(pt => pt.clone());\n        let newPoints = previousPts.map(pt => pt.clone().applyAxisAngle(Z_AXIS, segmentRotation));\n        previousPts = newPoints.map(pt => pt.clone());\n        let pointsToGetEdges = [...oldPoints, ...newPoints];\n\n        for (let i = 0; i < oldPoints.length; i++) {\n          for (let j = 0.2; j < 1; j += 0.2) {\n            pointsToGetEdges.push(new THREE.Vector3().lerpVectors(oldPoints[i], newPoints[i], j));\n          }\n        }\n\n        let outerPoints = this.getOuterPointsFromPointCloud(pointsToGetEdges, Z_AXIS);\n        rotatedPointsPolygons.push(_polybooljs.default.segments({\n          regions: [outerPoints.map(pt => [pt.x, pt.y])],\n          inverted: false\n        }));\n      }\n\n      return rotatedPointsPolygons;\n    }\n\n    generateSegmentsFromPoints(pointsArr) {\n      return pointsArr.reduce((allSegments, currentPoints) => [...allSegments, _polybooljs.default.segments({\n        regions: [currentPoints.map(pt => [pt.x, pt.y])],\n        inverted: false\n      })], []);\n    }\n\n    getPolygonPoints(oldPoints, newPoints) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let startPts = [...oldPoints, oldPoints[0].clone()];\n      let endPts = [...newPoints, newPoints[0].clone()];\n      let polyPts = [];\n      let segmentPoints;\n\n      for (let i = 0; i < startPts.length - 1; i++) {\n        segmentPoints = [startPts[i], startPts[i + 1], endPts[i], endPts[i + 1]];\n        segmentPoints = this.sortPoints(segmentPoints, Z_AXIS);\n        polyPts.push(segmentPoints);\n      }\n\n      return polyPts;\n    }\n\n    getOuterPointsFromPointCloud(points, zAxis) {\n      let linePairs = this.getOuterLinePairsFromPoints(points);\n      let outerPoints = linePairs.reduce((allPts, currentPair) => {\n        allPts.push(...currentPair);\n        return allPts;\n      }, []).reduce((filteredPts, currentPt, idx) => {\n        if (idx % 2) filteredPts.push(currentPt);\n        return filteredPts;\n      }, []);\n      outerPoints = this.sortPoints(outerPoints, zAxis);\n      return outerPoints;\n    }\n\n    getOuterLinePairsFromPoints(points) {\n      let facesDict = this.getFacesDictFromPoints(points);\n      let linePairs = [];\n\n      for (let [edgeIndices, facePair] of Object.entries(facesDict)) {\n        if (facePair.length !== 2) {\n          let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n          let vectorOne = points[verticesIdx[0]];\n          let vectorTwo = points[verticesIdx[1]];\n          linePairs.push([vectorOne, vectorTwo]);\n        }\n      }\n\n      return linePairs;\n    }\n\n    getFacesDictFromPoints(points) {\n      let faces = this.getFacesFromPoints(points);\n      return new _WireframeGenerator.WireframeGenerator().getFacesDict(faces);\n    }\n\n    getFacesFromPoints(points) {\n      let delaunayFaces = _delaunator.default.from(points.map(p => [p.x, p.y])).triangles;\n\n      let faces = [];\n\n      for (let i = 0; i < delaunayFaces.length; i += 3) faces.push({\n        a: delaunayFaces[i],\n        b: delaunayFaces[i + 1],\n        c: delaunayFaces[i + 2]\n      });\n\n      return faces;\n    }\n\n    sortPoints(points, normal) {\n      let centerPoint = this.getCenterPoint(points);\n\n      let sortOrder = (a, b) => normal.dot(new THREE.Vector3().crossVectors(a.clone().sub(centerPoint), b.clone().sub(centerPoint)));\n\n      points.sort(sortOrder);\n      return points;\n    }\n\n    getCenterPoint(pointsArr) {\n      let length = pointsArr.length;\n      let centerPoint = pointsArr.reduce((finalVec, currentVec) => finalVec.add(currentVec), new THREE.Vector3()).multiplyScalar(1 / length);\n      return centerPoint;\n    }\n\n  }\n\n  _exports.PlanarRotationPointsGenerator = PlanarRotationPointsGenerator;\n});","define(\"m08-2020/lib old/ThreeExtensions/RotatedGeometry\", [\"exports\", \"three\", \"three/src/extras/Earcut\", \"m08-2020/lib old/Utils\"], function (_exports, THREE, _Earcut, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.RotatedGeometry = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class RotatedGeometry extends THREE.Geometry {\n    constructor(points, rotationData) {\n      super();\n      this.generateSelf(points, rotationData);\n    }\n\n    generateSelf(points, rotationData) {\n      const DETAIL = 64;\n      const NUM_OF_POINTS = points.length;\n      this.reveresePointsIfNeeded(points, rotationData);\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationData;\n      let numOfSegments = DETAIL * Math.abs(rotationAngle) / (2 * Math.PI);\n      this.vertices.push(...points);\n      let orientationFront = getOrientation(points, rotationAxis, rotationPoint);\n      let crossVecFront = getCrossProduct(points, orientationFront);\n      let pitchVector = new THREE.Vector3();\n\n      for (let i = 0; i < numOfSegments; i++) {\n        for (let point of points) {\n          let rotatedPoint = new THREE.Vector3().copy(point).sub(rotationPoint).applyAxisAngle(rotationAxis, (i + 1) * rotationAngle / numOfSegments).add(rotationPoint).add(pitchVector.copy(rotationAxis).multiplyScalar((i + 1) * rotationPitch / numOfSegments / 2));\n          this.vertices.push(rotatedPoint);\n        }\n      }\n\n      let frontFaces = triangulatePoints3D(points, crossVecFront);\n      let backIndexStart = this.vertices.length - NUM_OF_POINTS;\n      let backPoints = this.vertices.slice(backIndexStart);\n      let backIndexMap = {};\n\n      for (let i = 0; i < backPoints.length; i++) backIndexMap[i] = backIndexStart + i;\n\n      let orientationBack = getOrientation(backPoints, rotationAxis, rotationPoint);\n      let crossVecBack = getCrossProduct(backPoints, orientationBack);\n      let backFaces = triangulatePoints3D(backPoints, crossVecBack);\n      if (crossVecFront.dot(crossVecBack) > 0) backFaces.reverse();\n\n      if (!pointsAreSameFrontBack(points, backPoints)) {\n        // Front\n        for (let i = 0, l = frontFaces.length; i < l; i += 3) {\n          this.faces.push(new THREE.Face3(frontFaces[i], frontFaces[i + 1], frontFaces[i + 2]));\n          addUV(this.faceVertexUvs[0]);\n        } // Back\n\n\n        for (let i = 0, l = backFaces.length; i < l; i += 3) {\n          this.faces.push(new THREE.Face3(backIndexMap[backFaces[i]], backIndexMap[backFaces[i + 1]], backIndexMap[backFaces[i + 2]]));\n          addUV(this.faceVertexUvs[0]);\n        }\n      } // Side\n\n\n      for (let s = 0; s < numOfSegments; s++) {\n        let offsetFromStart = s * NUM_OF_POINTS;\n\n        for (let i = 0; i < NUM_OF_POINTS; i++) {\n          let correctedIndexBack = i === NUM_OF_POINTS - 1 ? NUM_OF_POINTS : i + NUM_OF_POINTS + 1; // Bad naming/style?\n\n          let correctedIndexFront = i === NUM_OF_POINTS - 1 ? 0 : i + 1;\n          this.faces.push(new THREE.Face3(i + offsetFromStart, i + NUM_OF_POINTS + offsetFromStart, correctedIndexBack + offsetFromStart));\n          this.faces.push(new THREE.Face3(i + offsetFromStart, correctedIndexBack + offsetFromStart, correctedIndexFront + offsetFromStart));\n          addUV(this.faceVertexUvs[0]);\n          addUV(this.faceVertexUvs[0]);\n        }\n      }\n\n      this.computeFaceNormals();\n      this.computeVertexNormals();\n    }\n\n    reveresePointsIfNeeded(points, rotationData) {\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle\n      } = rotationData;\n      let orientation = getOrientation(points, rotationAxis, rotationPoint);\n      let crossVec = getCrossProduct(points, orientation);\n      let extrusionDir = getExtrusionDirection(points[0], rotationPoint, rotationAxis, Math.sign(rotationAngle));\n      if (extrusionDir.dot(crossVec) < 0) points.reverse();\n    }\n\n  } // Helper functions\n\n\n  _exports.RotatedGeometry = RotatedGeometry;\n\n  function triangulatePoints3D(points, surfaceNormal) {\n    const Z_AXIS = new THREE.Vector3(0, 0, 1);\n    const QUATERNION = new THREE.Quaternion().setFromUnitVectors(surfaceNormal, Z_AXIS);\n    let dummyVec = new THREE.Vector3();\n    let pointsFlat = points.reduce((ptArr, pt) => {\n      dummyVec.copy(pt);\n      dummyVec.applyQuaternion(QUATERNION);\n      ptArr.push(dummyVec.x, dummyVec.y, dummyVec.z);\n      return ptArr;\n    }, []);\n\n    let faces = _Earcut.Earcut.triangulate(pointsFlat, [], 3);\n\n    if (surfaceNormal.dot(Z_AXIS) < 0) faces.reverse();\n    return faces;\n  }\n\n  function pointsAreSameFrontBack(frontPts, backPts) {\n    let allPointsAreEqual = true;\n\n    for (let i = 0; i < frontPts.length; i++) {\n      if (!utils.vectorsAreEqual(frontPts[i], backPts[i])) allPointsAreEqual = false;\n    }\n\n    return allPointsAreEqual;\n  }\n\n  function getExtrusionDirection(referencePoint, rotationPoint, rotationAxis, rotationSign) {\n    let rotatedPoint = new THREE.Vector3().copy(referencePoint).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationSign * 0.1).add(rotationPoint);\n    return new THREE.Vector3().subVectors(rotatedPoint, referencePoint);\n  }\n\n  function getOrientation(points, rotationAxis, rotationPoint) {\n    let orientation = {\n      xAxis: getAxisX(points, rotationAxis, rotationPoint),\n      yAxis: rotationAxis\n    };\n    orientation.zAxis = new THREE.Vector3().crossVectors(orientation.xAxis, orientation.yAxis);\n    return orientation;\n  }\n\n  function getAxisX(points, rotationAxis, rotationPoint) {\n    let midVector = getMidVector(points);\n    let surfaceNormal = utils.getCrossVectorFromPoints(points);\n    let rotationPointCorrected = rotationPoint.clone().sub(midVector);\n    let rotPointOnSurface = rotationPointCorrected.clone().projectOnPlane(surfaceNormal);\n    let rotPointOnAxis = rotPointOnSurface.clone().projectOnVector(rotationAxis);\n    return rotPointOnSurface.sub(rotPointOnAxis).normalize();\n  }\n\n  function getMidVector(pts) {\n    let maxX = -Infinity;\n    let maxY = -Infinity;\n    let maxZ = -Infinity;\n    let minX = Infinity;\n    let minY = Infinity;\n    let minZ = Infinity;\n    pts.forEach(point => {\n      if (point.x > maxX) maxX = point.x;\n      if (point.y > maxY) maxY = point.y;\n      if (point.z > maxZ) maxZ = point.z;\n      if (point.x < minX) minX = point.x;\n      if (point.y < minY) minY = point.y;\n      if (point.z < minZ) minZ = point.z;\n    });\n    let midX = minX + (maxX - minX) / 2;\n    let midY = minY + (maxY - minY) / 2;\n    let midZ = minZ + (maxZ - minZ) / 2;\n    let midVector = new THREE.Vector3(midX, midY, midZ);\n    return midVector;\n  }\n\n  function getCrossProduct(points, orientation) {\n    let {\n      xAxis,\n      yAxis,\n      zAxis\n    } = orientation;\n    let ptA = new THREE.Vector3(0, 0, 0);\n    let minY = Infinity;\n    let maxX = -Infinity;\n    let idxA = 0;\n    let zProjection = new THREE.Vector3().copy(points[0]).projectOnVector(zAxis);\n    points.forEach((point, idx) => {\n      let projectedPoint = point.clone().sub(zProjection);\n      let projectedX = projectedPoint.dot(xAxis);\n      let projectedY = projectedPoint.dot(yAxis);\n\n      if (projectedY <= minY && projectedX >= maxX) {\n        minY = projectedY;\n        maxX = projectedX;\n        ptA.copy(projectedPoint);\n        idxA = idx;\n      }\n    });\n    let idxB = idxA === 0 ? points.length - 1 : idxA - 1;\n    let idxC = idxA === points.length - 1 ? 0 : idxA + 1;\n    let vecAB = new THREE.Vector3().subVectors(points[idxB], points[idxA]);\n    let vecAC = new THREE.Vector3().subVectors(points[idxC], points[idxA]);\n    return vecAB.cross(vecAC);\n  }\n\n  function addUV(UVs) {\n    // TEMP Func\n    UVs.push([new THREE.Vector2(0, 0), new THREE.Vector2(0, 0), new THREE.Vector2(0, 0)]);\n  }\n});","define(\"m08-2020/lib old/ThreeModule\", [\"exports\", \"lodash\", \"three\", \"three/examples/jsm/controls/OrbitControls\", \"m08-2020/lib old/CSGMesh\", \"polybooljs\", \"@tweenjs/tween.js\", \"m08-2020/lib old/Utils\", \"m08-2020/lib old/Utils/BooleanOperations\", \"m08-2020/lib old/Utils/DimensionalChainIntersectionDetection\", \"m08-2020/lib old/WireframeGenerator\", \"m08-2020/lib old/ClippingUtils/SectionalPlane\", \"m08-2020/lib old/ClippingUtils/DynamicSectionalPlane\", \"m08-2020/lib old/FigureLoaders/DrawingObjectLoader\", \"m08-2020/lib old/FigureLoaders/ArrowLoader\", \"m08-2020/lib old/FigureLoaders/DimensionalChainLoader\", \"m08-2020/lib old/FigureLoaders/FastenerLoader\", \"m08-2020/lib old/FigureLoaders/DrawingPlaneLoader\", \"m08-2020/lib old/DXFGenerator\", \"m08-2020/lib old/ZoomController\"], function (_exports, lodash, THREE, _OrbitControls, _CSGMesh, _polybooljs, _tween, _Utils, BooleanOperations, _DimensionalChainIntersectionDetection, _WireframeGenerator, _SectionalPlane, _DynamicSectionalPlane, _DrawingObjectLoader, _ArrowLoader, _DimensionalChainLoader, _FastenerLoader, _DrawingPlaneLoader, _DXFGenerator, _ZoomController) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.GraphicsThree3D = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  class GraphicsThree3D {\n    //when clicked wireframe button here value changes\n    // Points for camera centering\n    // TODO: move intermediate points into specifice scope and leave only mid coords\n    constructor() {\n      _defineProperty(this, \"isWireFrameOnly\", false);\n\n      _defineProperty(this, \"fontUrlArray\", {\n        Regular: {\n          Arial: \"assets/fonts/Arial_Regular.json\",\n          Calibri: \"assets/fonts/Calibri_Regular.json\",\n          Courier: \"assets/fonts/Courier New_Regular.json\",\n          TimesNewRoman: \"assets/fonts/Times New Roman_Regular.json\"\n        },\n        Italic: {\n          Arial: \"assets/fonts/Arial_Italic.json\",\n          Calibri: \"assets/fonts/Calibri_Italic.json\",\n          Courier: \"assets/fonts/Courier New_Italic.json\",\n          TimesNewRoman: \"assets/fonts/Times New Roman_Italic.json\"\n        },\n        Bold: {\n          Arial: \"assets/fonts/Arial_Bold.json\",\n          Calibri: \"assets/fonts/Calibri_Bold.json\",\n          Courier: \"assets/fonts/Courier New_Bold.json\",\n          TimesNewRoman: \"assets/fonts/Times New Roman_Bold.json\"\n        },\n        BoldItalic: {\n          Arial: \"assets/fonts/Arial_Bold Italic.json\",\n          Calibri: \"assets/fonts/Calibri_Bold Italic.json\",\n          Courier: \"assets/fonts/Courier New_Bold Italic.json\",\n          TimesNewRoman: \"assets/fonts/Times New Roman_Bold Italic.json\"\n        }\n      });\n\n      _defineProperty(this, \"loadedFonts\", {});\n\n      _defineProperty(this, \"materialTransparentOptions\", null);\n\n      _defineProperty(this, \"interactiveZ\", false);\n\n      _defineProperty(this, \"extrusionVectorInteractiveZ\", null);\n\n      _defineProperty(this, \"initialPosition\", {\n        x: -50,\n        y: 50,\n        z: 100\n      });\n\n      _defineProperty(this, \"previousCameraPosition\", null);\n\n      _defineProperty(this, \"cameraTweenActive\", false);\n\n      _defineProperty(this, \"objectMaxX\", -Infinity);\n\n      _defineProperty(this, \"objectMaxY\", -Infinity);\n\n      _defineProperty(this, \"objectMaxZ\", -Infinity);\n\n      _defineProperty(this, \"objectMinX\", Infinity);\n\n      _defineProperty(this, \"objectMinY\", Infinity);\n\n      _defineProperty(this, \"objectMinZ\", Infinity);\n\n      _defineProperty(this, \"SceneCenterComputed\", false);\n\n      this.THREE = THREE;\n      this.OrbitControls = _OrbitControls.OrbitControls;\n      this.CSG = (0, _CSGMesh.CsgFactory)(THREE);\n      this.PolyBool = _polybooljs.default;\n      this.TWEEN = _tween.default;\n      this.canvasSize = {\n        width: 960,\n        height: 500\n      };\n      this.renderer = this.initRenderer(this.canvasSize);\n      this.camera = this.initCamera(this.canvasSize);\n      this.scene = new this.THREE.Scene();\n      this.scene.background = new this.THREE.Color(0xffffff);\n      this.stencilScenes = [];\n      this.controls = new this.OrbitControls(this.camera, this.renderer.domElement);\n      this.globalJSONData = {};\n      this.jsonData = {};\n      this.WireframeGenerator = new _WireframeGenerator.WireframeGenerator(this);\n      this.SectionalPlane = new _SectionalPlane.SectionalPlane(this);\n      this.DynamicSectionalPlane = new _DynamicSectionalPlane.DynamicSectionalPlane(this);\n      this.ThreeObjects = [];\n      this.objectMidVector = new this.THREE.Vector3(); /// Drawing Plane\n\n      this.transparencyControlObjects = {\n        area: [],\n        line: [],\n        point: [],\n        text: [],\n        volume: []\n      };\n      this.visualisationControlObjects = {\n        arrow: [],\n        dimensionalChain: [],\n        drawingObject: [],\n        fastener: []\n      }; ///\n\n      this.DimensionalChainIntersectionDetection = new _DimensionalChainIntersectionDetection.DimensionalChainIntersectionDetection();\n      this.dxfGenerator = new _DXFGenerator.DXFGenerator(this);\n      this.utils = new _Utils.Utils(THREE);\n      this.DrawingObjectLoader = new _DrawingObjectLoader.DrawingObjectLoader(this);\n      this.ArrowLoader = new _ArrowLoader.ArrowLoader(THREE, this.fontUrlArray, this);\n      this.DimensionalChainLoader = new _DimensionalChainLoader.DimensionalChainLoader(this);\n      this.FastenerLoader = new _FastenerLoader.FastenerLoader(THREE, this);\n      this.DrawingPlaneLoader = new _DrawingPlaneLoader.DrawingPlaneLoader(this);\n      this.ZoomController = new _ZoomController.ZoomController(this);\n      this.animate(this); ///\n      // this.renderer.domElement.style = \"position: absolute; width: 60%; height: 50%;\";\n      // this.scene.add( new this.THREE.AxesHelper( 10 ) );\n      ///\n    }\n\n    getGraphicsCanvas() {\n      return this.renderer.domElement;\n    }\n\n    initRenderer(canvasSize) {\n      let {\n        width,\n        height\n      } = canvasSize;\n      let renderer = new this.THREE.WebGLRenderer({\n        antialias: true,\n        preserveDrawingBuffer: true\n      });\n      renderer.setSize(width, height);\n      renderer.domElement.id = 'threeCanvas';\n      renderer.localClippingEnabled = true;\n      renderer.autoClear = false;\n      return renderer;\n    }\n\n    initCamera(canvasSize) {\n      const CAMERA_VIEW_SIZE = 5000;\n      const CAMERA_DEPTH = 5000;\n      const MULTIPLIER = 64;\n      let {\n        width,\n        height\n      } = canvasSize;\n      let cameraAspectRatio = width / height;\n      return new this.THREE.OrthographicCamera(CAMERA_VIEW_SIZE * cameraAspectRatio / -MULTIPLIER, CAMERA_VIEW_SIZE * cameraAspectRatio / MULTIPLIER, CAMERA_VIEW_SIZE / MULTIPLIER, CAMERA_VIEW_SIZE / -MULTIPLIER, -CAMERA_DEPTH, CAMERA_DEPTH);\n    }\n\n    makeTransparentClone(mesh) {\n      let transparentClone = new this.THREE.Mesh(); // transparentClone.applyMatrix4(mesh.matrix);\n\n      return transparentClone;\n    }\n\n    initWireframeObject(mesh, transparentMesh, jsonData, scene) {\n      let objectData = {\n        id: \"none\",\n        //TODO: Apply ID\n        data: jsonData,\n        sceneObject: mesh,\n        targetScene: scene\n      };\n\n      if (transparentMesh) {\n        mesh.IsCSGObject = true;\n        objectData.IsCSGObject = true;\n        objectData.sceneObjectTransparentCSG = transparentMesh;\n        objectData.type = \"polygon\"; // \"polygon\" is for wireframe to work // TODO: change it\n\n        mesh.customTypeName = \"polygon\";\n        transparentMesh.customTypeName = \"polygon\";\n      }\n\n      this.ThreeObjects.push(objectData);\n    }\n\n    makeWireframeFromPoints(points, extrusionVector, lineProps) {\n      const RENDER_ORDER = -99;\n      let wireframes = [];\n      let vectors = points.map(point => new this.THREE.Vector3(point.x, point.y, point.z));\n      let wireframe = this.utils.drawConnectedLines(vectors, lineProps, true);\n      wireframe.renderOrder = RENDER_ORDER;\n      wireframes.push(wireframe);\n      let wireframeClone = wireframe.clone();\n      wireframeClone.position.add(extrusionVector);\n      wireframes.push(wireframeClone);\n      wireframes.push(...this.drawWireframeSideLines(vectors, extrusionVector, lineProps));\n      return wireframes;\n    }\n\n    drawWireframeSideLines(points, extrusionVector, lineProps, renderOrder = -99) {\n      let sideLines = [];\n\n      for (let i = 0; i < points.length; i++) {\n        let indexThree = i + 2;\n        if (indexThree >= points.length) indexThree -= points.length;\n        let indexTwo = indexThree - 1;\n        if (indexTwo < 0) indexTwo += points.length;\n        let indexOne = indexTwo - 1;\n        if (indexOne < 0) indexOne += points.length;\n        let pointOne = points[indexOne].clone().normalize();\n        let pointTwo = points[indexTwo].clone().normalize();\n        let pointThree = points[indexThree].clone().normalize();\n        let vectorOne = pointTwo.clone().sub(pointOne);\n        let vectorTwo = pointThree.clone().sub(pointTwo);\n        const DELTA = 0.001;\n        let angleBetween = vectorOne.angleTo(vectorTwo);\n        if (angleBetween > Math.PI) angleBetween -= Math.PI;\n\n        if (angleBetween > Math.PI / 10 && angleBetween < Math.PI - DELTA) {\n          let pointOne = points[indexTwo];\n          let pointTwo = points[indexTwo].clone().add(extrusionVector);\n\n          if (pointOne.distanceTo(pointTwo) > 0) {\n            let sideLine = this.utils.drawConnectedLines([pointOne, pointTwo], lineProps);\n            sideLine.renderOrder = renderOrder;\n            sideLines.push(sideLine);\n          }\n        }\n      }\n\n      return sideLines;\n    }\n\n    makeWireframes(regions, extrusionVector, lineProps) {\n      let wireframes = [];\n      regions.forEach(region => {\n        let finalPolygons = region.positiveRegions.length > 0 || region.positiveRegions.length > 0 && region.negativeRegions.length > 0 ? BooleanOperations.makePolygonsFromPosAndNegRegions(region) : {\n          regions: []\n        };\n        finalPolygons.regions.forEach(polygonRegion => {\n          let vectors = polygonRegion.reduce((vectorArr, coords) => {\n            let newPoint = new this.THREE.Vector3(coords[0], coords[1], 0);\n            newPoint.applyEuler(region.eulerTwo);\n            newPoint.add(region.offsetVector);\n            vectorArr.push(newPoint);\n            return vectorArr;\n          }, []);\n          wireframes.push(...this.makeWireframeFromPoints(vectors, extrusionVector, lineProps));\n        });\n      });\n      return wireframes;\n    }\n\n    makePositionedExtrusion(geoData) {\n      let geometry = this.makeExtrudeGeoRaw(geoData.points2D, geoData.depth);\n      geometry.translate(...geoData.translation);\n      this.utils.skewExtrudedGeometry(geometry, geoData.skew);\n      return geometry;\n    }\n\n    makeExtrudeGeoRaw(points2D, depth) {\n      let shape = new this.THREE.Shape(points2D);\n      let extrudeSettings = {\n        depth,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      return geometry;\n    }\n\n    objectRotatesPlanar(objectData, isEllipse = false) {\n      let rotationAxis = this.utils.initVector3(objectData.objectGeneration.rotationAxis).normalize();\n      let crossVector;\n\n      if (isEllipse) {\n        let directionVectorOne = this.utils.initVector3(objectData.directionVector1);\n        let directionVectorTwo = this.utils.initVector3(objectData.directionVector2);\n        crossVector = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n      } else {\n        let points = objectData.points;\n        crossVector = this.utils.getCrossVectorFromPoints(points);\n      }\n\n      return Math.abs(crossVector.dot(rotationAxis)) == 1;\n    }\n\n    objectExtrudesPlanar(objectData, isEllipse = false) {\n      let extrusionVector = this.utils.initVector3(objectData.objectGeneration.extrusionVector).normalize();\n      let crossVector;\n\n      if (isEllipse) {\n        let directionVectorOne = this.utils.initVector3(objectData.directionVector1);\n        let directionVectorTwo = this.utils.initVector3(objectData.directionVector2);\n        crossVector = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n      } else {\n        let points = objectData.points;\n        crossVector = this.utils.getCrossVectorFromPoints(points);\n      }\n\n      return crossVector.dot(extrusionVector) == 0;\n    }\n\n    drawObject(jsonData) {\n      let drawingObjects = jsonData.drawingObjects;\n      drawingObjects.forEach(drawingObject => {\n        let components = drawingObject.components;\n        let properties = drawingObject.properties;\n        if (components.texts) this.loadTexts(properties.text, components.texts, jsonData, drawingObject.insertionPoints);\n        let isComplexVolume = this.utils.isComplexVolume(properties);\n        if (isComplexVolume) this.DrawingObjectLoader.drawComplexVolumes(drawingObject, jsonData);else this.DrawingObjectLoader.drawRegularObjects(drawingObject, jsonData);\n\n        if (components.sectionalPlanes) {\n          this.centerScene();\n          this.SceneCenterComputed = true;\n        }\n\n        if (drawingObject.dynamicSectionalPlanes) {\n          this.centerScene();\n          this.SceneCenterComputed = true;\n        }\n      });\n    }\n\n    addObjToTranspControl(object, type) {\n      switch (type) {\n        case \"area\":\n          this.transparencyControlObjects.area.push(object);\n          break;\n\n        case \"line\":\n          this.transparencyControlObjects.line.push(object);\n          break;\n\n        case \"point\":\n          this.transparencyControlObjects.point.push(object);\n          break;\n\n        case \"text\":\n          this.transparencyControlObjects.text.push(object);\n          break;\n\n        case \"volume\":\n          this.transparencyControlObjects.volume.push(object);\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    addObjectToVisControl(object, type) {\n      switch (type) {\n        case \"arrow\":\n          this.visualisationControlObjects.arrow.push(object);\n          break;\n\n        case \"dimensionalChain\":\n          this.visualisationControlObjects.dimensionalChain.push(object);\n          break;\n\n        case \"drawingObject\":\n          this.visualisationControlObjects.drawingObject.push(object);\n          break;\n\n        case \"fastener\":\n          this.visualisationControlObjects.fastener.push(object);\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    addObjectToScene(object, objectTransparent, insertionPoints, jsonData, transpType, visType, objectData = null) {\n      if (insertionPoints) insertionPoints.forEach(insertionPoint => {\n        let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n        let newMaterial = object.material.length ? object.material.map(mat => mat.clone()) : object.material.clone();\n        let newObject = object.clone();\n        newObject.material = newMaterial;\n        newObject.onBeforeRender = object.onBeforeRender;\n        newObject.stencilNeeded = object.stencilNeeded ? object.stencilNeeded : false;\n        newObject.addToDXF = object.addToDXF ? object.addToDXF : false;\n        newObject.dxfLayer = object.dxfLayer ? object.dxfLayer : \"No Layer\";\n        let newObjectTransparent = objectTransparent ? objectTransparent.clone() : null;\n        copyObjectWireframes(newObject, object);\n        if (newObjectTransparent) copyObjectWireframes(newObjectTransparent, objectTransparent);\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          pivotPoint.attach(newObject);\n          if (newObjectTransparent) pivotPoint.attach(newObjectTransparent);\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(newObject);\n          if (newObjectTransparent) pivotPoint.attach(newObjectTransparent);\n        }\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        let regularObject = pivotPoint.children[0];\n        let transparentObject = newObjectTransparent ? pivotPoint.children[1] : null;\n        pivotPoint.children.forEach(child => {\n          child.applyMatrix4(pivotPoint.matrixWorld);\n        });\n        regularObject.isDrawingObject = true; // Used for drawingPlanes\n\n        this.initWireframeObject(regularObject, transparentObject, jsonData, this.scene);\n        this.scene.add(regularObject); ///\n\n        this.ZoomController.getDrawingObjectWireframes(regularObject);\n        if (transparentObject) this.ZoomController.getDrawingObjectWireframes(transparentObject); ///\n\n        if (objectData && objectData.components.sectionalPlanes) this.SectionalPlane.addToSectionalPlanes(insertionPoint, regularObject, objectData);\n\n        if (objectData && objectData.dynamicSectionalPlanes) {\n          objectData.dynamicSectionalPlanes.forEach(planeID => {\n            this.DynamicSectionalPlane.sectionalPlanes.forEach(sectionalPlane => {\n              if (planeID === sectionalPlane.id) {\n                sectionalPlane.targets.drawingObjects.push({\n                  mesh: regularObject,\n                  clippingPlanes: objectData.dynamicSectionalPlanes\n                });\n              }\n            });\n          });\n        }\n\n        if (object.dxfPoints) object.dxfPoints.forEach(ptsData => {\n          ptsData.pointPairs.forEach(pair => pair.map(pt => pt.applyMatrix4(regularObject.matrix)));\n          this.dxfGenerator.wireframePoints.push(ptsData);\n        });\n        this.addObjToTranspControl(regularObject, transpType);\n        this.addObjectToVisControl(regularObject, visType);\n      });\n\n      function copyObjectWireframes(ObjectNew, objectOld) {\n        ObjectNew.children = objectOld.children.map(child => {\n          let childClone = child.clone();\n          if (child.needsZoomControl) childClone.needsZoomControl = true;\n          if (child.isLine) childClone.isLine = true;\n          if (child.pointsStartEnd) childClone.pointsStartEnd = child.pointsStartEnd;\n          return childClone;\n        });\n      }\n    }\n\n    getThreeSideFaceObjects(object) {\n      object.updateMatrixWorld();\n      let [doubleMaterial, frontMaterial, backMaterial] = this.getThreeSideFaceMaterials(object);\n      let geometry = object.geometry.clone();\n      let frontSideObject = new this.THREE.Mesh(geometry);\n      frontSideObject.applyMatrix4(object.matrix);\n      let backSideObject = new this.THREE.Mesh(geometry);\n      backSideObject.applyMatrix4(object.matrix);\n      let doubleSideObject = new this.THREE.Mesh(geometry);\n      doubleSideObject.applyMatrix4(object.matrix);\n      doubleSideObject = this.getPositionedObject(doubleSideObject, doubleMaterial);\n      frontSideObject = this.getPositionedObject(frontSideObject, frontMaterial);\n      backSideObject = this.getPositionedObject(backSideObject, backMaterial);\n      return [doubleSideObject, frontSideObject, backSideObject];\n    }\n\n    getPositionedObject(object, material) {\n      let bsp = this.CSG.fromMesh(object);\n      let result = this.CSG.toMesh(bsp, new this.THREE.Matrix4());\n      result.material = material;\n      return result;\n    }\n\n    getThreeSideFaceMaterials(object) {\n      let objectMaterial = object.material.length > 0 ? object.material.map(mat => {\n        let newMat = mat.clone();\n        return newMat;\n      }) : object.material.clone();\n      let frontMaterial;\n\n      if (object.material.length > 0) {\n        frontMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.FrontSide;\n          return newMat;\n        });\n      } else {\n        frontMaterial = objectMaterial.clone();\n        frontMaterial.side = this.THREE.FrontSide;\n      }\n\n      let backMaterial;\n\n      if (object.material.length > 0) {\n        backMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.BackSide;\n          return newMat;\n        });\n      } else {\n        backMaterial = objectMaterial.clone();\n        backMaterial.side = this.THREE.BackSide;\n      }\n\n      let doubleMaterial;\n\n      if (object.material.length > 0) {\n        doubleMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.DoubleSide;\n          return newMat;\n        });\n      } else {\n        doubleMaterial = objectMaterial.clone();\n        doubleMaterial.side = this.THREE.DoubleSide;\n      }\n\n      return [doubleMaterial, frontMaterial, backMaterial];\n    }\n\n    getTransformedObjects(objects, insertionPoint) {\n      let rotationAngle = insertionPoint.rotationAngle;\n      let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n      let pivotPoint = new this.THREE.Object3D();\n      let newObjects = [];\n      objects.forEach(object => {\n        let newObject = object.clone();\n        newObject.onBeforeRender = object.onBeforeRender;\n        newObjects.push(newObject);\n      });\n\n      if (rotationAngle) {\n        pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n        newObjects.forEach(obj => pivotPoint.attach(obj));\n        rotationAngle = rotationAngle * Math.PI / 180;\n        let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n      } else newObjects.forEach(obj => pivotPoint.attach(obj));\n\n      pivotPoint.position.add(insertionVector);\n      pivotPoint.updateMatrixWorld();\n      newObjects.forEach(obj => obj.applyMatrix4(pivotPoint.matrixWorld));\n      return newObjects;\n    }\n\n    getlineMeshMaterial(properties) {\n      return properties && properties.line ? this.getMaterialFromColor(properties.line.color) : null;\n    }\n\n    getPointMaterial(properties) {\n      return properties && properties.point ? this.getMaterialFromColor(properties.point.color) : null;\n    }\n\n    getAreaMaterial(mesh, properties) {\n      let objectMaterial;\n\n      if (properties && properties.area) {\n        let colorProps = properties.area.color; ///\n\n        if (properties.area.hatching.type == 2) {\n          let orientationVector = this.utils.normalizeVector(properties.area.hatching.orientationVector);\n          let hatchSpacing = properties.area.hatching.factor;\n          let hatchColor = properties.area.hatching.color;\n          let hatchThickness = properties.area.hatching.thickness;\n          objectMaterial = this.makeCanvasMaterial(colorProps, hatchColor, orientationVector, hatchSpacing, hatchThickness); // TODO: might need to optimize this\n\n          let localClone = new this.THREE.Mesh(mesh.geometry.clone(), mesh.material.clone());\n          this.scene.add(localClone);\n          this.centerScene();\n          let objectWidth = this.objectMaxX - this.objectMinX;\n          let objectHeight = this.objectMaxY - this.objectMinY;\n          this.scene.remove(localClone); ///\n          // let maxDistance = Math.max(objectWidth, objectHeight);\n\n          objectMaterial.map.wrapS = objectMaterial.map.wrapT = this.THREE.RepeatWrapping;\n          objectMaterial.map.repeat.set(1 / objectWidth, 1 / objectHeight);\n          objectMaterial.map.offset.set(0.5, 0.5);\n        } else if (properties.area.hatching.type == 1) {\n          objectMaterial = this.getMaterialFromColor(colorProps);\n        } else {\n          objectMaterial = null;\n        } ///\n\n      } else {\n        objectMaterial = null;\n      }\n\n      return objectMaterial;\n    }\n\n    getVolumeMaterialsIndexFaces(mesh, properties) {\n      let objectMaterial;\n\n      if (properties && properties.volume && mesh.geometry.faces) {\n        let faceSideColor = properties.volume.color.faceSide;\n        let edgeSideColor = properties.volume.color.edgeSide;\n        let endSideColor = properties.volume.color.endSide;\n        let cutSideColor = properties.volume.color.cutSide;\n        let faceSideMaterial = this.getMaterialFromColor(faceSideColor);\n        let edgeSideMaterial = this.getMaterialFromColor(edgeSideColor);\n        let endSideMaterial = this.getMaterialFromColor(endSideColor);\n        let cutSideMaterial = this.getMaterialFromColor(cutSideColor);\n        objectMaterial = [faceSideMaterial, edgeSideMaterial, endSideMaterial, cutSideMaterial];\n        this.indexMeshFaces(mesh, properties);\n      } else {\n        objectMaterial = null;\n      }\n\n      return objectMaterial;\n    }\n\n    indexMeshFaces(mesh, properties) {\n      let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n      let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n      mesh.geometry.faces.forEach(face => {\n        if (Math.abs(face.normal.dot(faceSideNormal)) > 0.9999) {\n          face.materialIndex = 0;\n        } else if (Math.abs(face.normal.dot(grainDirection)) < 0.0001) {\n          face.materialIndex = 1;\n        } else if (Math.abs(face.normal.dot(grainDirection)) > 0.9999) {\n          face.materialIndex = 2;\n        } else {\n          face.materialIndex = 3;\n        }\n      });\n    }\n\n    centerScene() {\n      let box = new this.THREE.BoxHelper(this.scene, 0xffff00);\n      let boxVertices = this.utils.getVerticesFromBufferGeometry(box.geometry);\n      let bbox = new this.THREE.Box3().setFromPoints(boxVertices);\n      this.objectMaxX = bbox.max.x;\n      this.objectMaxY = bbox.max.y;\n      this.objectMaxZ = bbox.max.z;\n      this.objectMinX = bbox.min.x;\n      this.objectMinY = bbox.min.y;\n      this.objectMinZ = bbox.min.z;\n      let objectWidth = this.objectMaxX - this.objectMinX;\n      let objectHeight = this.objectMaxY - this.objectMinY;\n      let objectDepth = this.objectMaxZ - this.objectMinZ;\n      let objectMidX = this.objectMinX + objectWidth / 2;\n      let objectMidY = this.objectMinY + objectHeight / 2;\n      let objectMidZ = this.objectMinZ + objectDepth / 2;\n      this.objectMidVector.set(objectMidX, objectMidY, objectMidZ);\n      this.controls.target.copy(this.objectMidVector);\n      this.controls.update();\n      this.camera.position.set(this.initialPosition.x, this.initialPosition.y, this.initialPosition.z).add(this.objectMidVector);\n      this.camera.lookAt(this.objectMidVector);\n      this.camera.updateProjectionMatrix();\n    }\n\n    clearScene() {\n      this.clearGraphicsState();\n      this.DrawingObjectLoader.WireframeGenerator.clearWireframeState();\n      this.DynamicSectionalPlane.clearSectionalPlanes();\n      this.ZoomController.drawingObjectWireframes();\n\n      while (this.scene.children.length > 0) {\n        this.scene.remove(this.scene.children[this.scene.children.length - 1]);\n      }\n    }\n\n    clearGraphicsState() {\n      this.previousCameraPosition = null;\n      this.cameraTweenActive = false;\n      this.ThreeObjects = [];\n      this.objectMaxX = -Infinity;\n      this.objectMaxY = -Infinity;\n      this.objectMaxZ = -Infinity;\n      this.objectMinX = Infinity;\n      this.objectMinY = Infinity;\n      this.objectMinZ = Infinity;\n      this.SceneCenterComputed = false;\n      this.stencilScenes = [];\n      this.transparencyControlObjects = {\n        area: [],\n        line: [],\n        point: [],\n        text: [],\n        volume: []\n      };\n      this.visualisationControlObjects = {\n        arrow: [],\n        dimensionalChain: [],\n        drawingObject: [],\n        fastener: []\n      };\n      this.DimensionalChainIntersectionDetection.meshArr = [];\n    }\n\n    getRotationParameters(pts, extrusionNormalized) {\n      let midVector = this.getMidVector(pts);\n      let crossVector = this.getCrossVector(pts, extrusionNormalized);\n      let [eulerOne, eulerTwo] = this.utils.getEulerOneAndTwo(crossVector);\n      return [midVector, eulerOne, eulerTwo, crossVector];\n    }\n\n    getMidVector(pts) {\n      let maxX = -Infinity;\n      let maxY = -Infinity;\n      let maxZ = -Infinity;\n      let minX = Infinity;\n      let minY = Infinity;\n      let minZ = Infinity;\n      pts.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.z > maxZ) maxZ = point.z;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        if (point.z < minZ) minZ = point.z;\n      });\n      let midX = minX + (maxX - minX) / 2;\n      let midY = minY + (maxY - minY) / 2;\n      let midZ = minZ + (maxZ - minZ) / 2;\n      let midVector = new this.THREE.Vector3(midX, midY, midZ);\n      return midVector;\n    }\n\n    getCrossVector(points, extrusionNormalized) {\n      let crossVector = this.utils.getCrossVectorFromPoints(points);\n      if (extrusionNormalized.dot(crossVector) < 0) crossVector.negate();\n      return crossVector;\n    }\n\n    getMaterialFromColor(colorProps) {\n      let {\n        color,\n        opacity\n      } = this.utils.getColorAndOpacity(colorProps);\n      let materialOptions = {\n        transparent: true,\n        color,\n        opacity,\n        side: this.THREE.DoubleSide\n      };\n      let material = new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    getVolumeMaterials(volumeColors) {\n      let faceSideColor = volumeColors.faceSide;\n      let edgeSideColor = volumeColors.edgeSide;\n      let endSideColor = volumeColors.endSide;\n      let cutSideColor = volumeColors.cutSide;\n      let faceSideMaterial = this.getMaterialFromColor(faceSideColor);\n      let edgeSideMaterial = this.getMaterialFromColor(edgeSideColor);\n      let endSideMaterial = this.getMaterialFromColor(endSideColor);\n      let cutSideMaterial = this.getMaterialFromColor(cutSideColor);\n      let materials = [faceSideMaterial, edgeSideMaterial, endSideMaterial, cutSideMaterial];\n      return materials;\n    }\n\n    indexVolumeFaces(geometry, faceSideNormal, grainDirection) {\n      geometry.faces.forEach(face => {\n        if (Math.abs(face.normal.dot(faceSideNormal)) == 1) {\n          face.materialIndex = 0;\n        } else if (face.normal.dot(grainDirection) == 0) {\n          face.materialIndex = 1;\n        } else if (Math.abs(face.normal.dot(grainDirection)) == 1) {\n          face.materialIndex = 2;\n        } else {\n          face.materialIndex = 3;\n        }\n      });\n    }\n\n    makeCircleWithEdges(radius, areaMaterial, borderMaterial, customParameters = null) {\n      let circleGeo;\n\n      if (customParameters) {\n        let thetaStart = customParameters.thetaStart;\n        let thetaEnd = customParameters.thetaEnd;\n        circleGeo = new this.THREE.CircleBufferGeometry(radius, 32, thetaStart, thetaEnd);\n        let radiusTwo = customParameters.radiusTwo;\n        circleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radius, 1));\n      } else {\n        circleGeo = new this.THREE.CircleBufferGeometry(radius, 32);\n      }\n\n      let areaMesh = new this.THREE.Mesh(circleGeo, areaMaterial);\n      let borderEdges = new this.THREE.EdgesGeometry(circleGeo);\n      let borderLine = new this.THREE.LineSegments(borderEdges, borderMaterial);\n      let circle = new this.THREE.Object3D();\n      circle.add(areaMesh);\n      circle.add(borderLine);\n      return circle;\n    }\n\n    makeArrow(radius, height, areaMaterial, borderMaterial) {\n      let coneGeo = new this.THREE.ConeBufferGeometry(radius, height, 32);\n      coneGeo.translate(0, -height / 2, 0);\n      let coneMesh = new this.THREE.Mesh(coneGeo, areaMaterial);\n      coneMesh.position.y = -height * 0.025;\n      let borderMesh = new this.THREE.Mesh(coneGeo, borderMaterial);\n      borderMesh.scale.multiplyScalar(1.05);\n      borderMesh.material.side = this.THREE.BackSide;\n      let cone = new this.THREE.Object3D();\n      cone.add(coneMesh);\n      cone.add(borderMesh);\n      return cone;\n    }\n\n    getLineMaterial(lineProps) {\n      let lineType = lineProps.type;\n      let lineColor = lineProps.color;\n      let lineMaterial;\n\n      if (lineType) {\n        if (lineType == 1) {\n          let materialOptions = {\n            color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n            transparent: true,\n            opacity: lineColor.alpha / 255\n          };\n          lineMaterial = new this.THREE.LineBasicMaterial(materialOptions);\n        } else {\n          lineMaterial = new this.THREE.LineDashedMaterial({\n            color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n            transparent: true,\n            opacity: lineColor.alpha / 255,\n            dashSize: lineType == 2 ? lineProps.factor : 0.2,\n            // TODO: might need to change this\n            gapSize: lineProps.factor ? lineProps.factor : 0\n          });\n        }\n      }\n\n      if (lineMaterial) lineMaterial.depthTest = false;else lineMaterial = new this.THREE.LineBasicMaterial({\n        transparent: true,\n        opacity: 0\n      });\n      return lineMaterial;\n    }\n\n    drawLine(pointOne, pointTwo, lineProps, forObjectGeneration = false) {\n      ///\n      // temporary solution. May be Fixed.\n      if (lineProps.type == 0 || lineProps.thickness == 0 || lineProps.color.alpha == 0) return new this.THREE.Mesh(); ///\n\n      let lineMaterial = this.getMaterialFromColor(lineProps.color);\n      let lineRadius = lineProps.thickness / 2;\n      let lineSize = pointOne.distanceTo(pointTwo);\n      let lineGeo = new this.THREE.CylinderGeometry(lineRadius, lineRadius, lineSize, 8);\n      lineGeo.rotateX(Math.PI / 2);\n      lineGeo.translate(0, 0, lineSize / 2); // if (!forObjectGeneration) lineGeo.merge(new this.THREE.SphereGeometry(lineRadius, 8, 8));\n\n      let lineMesh = new this.THREE.Mesh(lineGeo, lineMaterial);\n      lineMesh.position.copy(pointOne);\n      lineMesh.lookAt(pointTwo);\n      if (!forObjectGeneration) lineMesh.renderOrder = -101;\n      return lineMesh;\n    }\n\n    getEllipsePlanarPoints(ellipseData) {\n      const CIRCLE_DETAIL = 64;\n      let radiusOne = ellipseData.radius1;\n      let radiusTwo = ellipseData.radius2;\n      let segmentHeight = ellipseData.segmentHeight;\n      let directionVectorOne = this.utils.initVector3(ellipseData.directionVector1).normalize();\n      let directionVectorTwo = this.utils.initVector3(ellipseData.directionVector2).normalize();\n      let circleVertices = [];\n\n      if (!directionVectorOne.dot(directionVectorTwo)) {\n        let crossVector = new this.THREE.Vector3().crossVectors(directionVectorOne, directionVectorTwo).normalize();\n        let startAngle = 0;\n        let endAngle = 2 * Math.PI;\n\n        if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n          let segmentVectorOne = this.utils.initVector3(ellipseData.segmentVector1).normalize();\n          let segmentVectorTwo = this.utils.initVector3(ellipseData.segmentVector2).normalize(); ///\n\n          let segmentCross = new this.THREE.Vector3().crossVectors(segmentVectorOne, segmentVectorTwo);\n          if (segmentCross.length() === 0) segmentCross = new this.THREE.Vector3().crossVectors(directionVectorOne, directionVectorTwo).normalize();\n          let segOneToSegTwo = Math.acos(this.utils.clampNum(segmentVectorOne.dot(segmentVectorTwo), -1, 1));\n\n          if (crossVector.dot(segmentCross) < 0) {\n            segOneToSegTwo = -segOneToSegTwo;\n          }\n\n          let dirOneToSegOne = Math.acos(directionVectorOne.dot(segmentVectorOne));\n          let dirSegCross = new this.THREE.Vector3().crossVectors(directionVectorOne, segmentVectorOne);\n\n          if (crossVector.dot(dirSegCross) < 0) {\n            dirOneToSegOne = 2 * Math.PI - dirOneToSegOne;\n          }\n\n          startAngle = dirOneToSegOne;\n          endAngle = dirOneToSegOne + segOneToSegTwo;\n        }\n\n        let transformMatrix = new this.THREE.Matrix4().set(directionVectorOne.x, directionVectorTwo.x, crossVector.x, 0, directionVectorOne.y, directionVectorTwo.y, crossVector.y, 0, directionVectorOne.z, directionVectorTwo.z, crossVector.z, 0, 0, 0, 0, 1);\n        let angleDelta = 2 * Math.PI / CIRCLE_DETAIL;\n        let epsilon = 0.01;\n        let partOne = [];\n        let partTwo = [];\n\n        for (let i = 0; i < 2 * Math.PI; i += angleDelta) {\n          let localStartAngle = startAngle;\n          let localEndAngle = endAngle;\n\n          if (localStartAngle < localEndAngle) {\n            if (localEndAngle > 2 * Math.PI) {\n              localEndAngle -= 2 * Math.PI; // if ((i >= -epsilon && i <= localEndAngle + epsilon) || (i >= localStartAngle - epsilon && i <= 2 * Math.PI + epsilon)) {\n              //   let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n              //   newVector.applyMatrix4(transformMatrix);\n              //   partOne.push(newVector);\n              // }\n\n              if (i >= -epsilon && i <= localEndAngle + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partTwo.push(newVector);\n              }\n\n              if (i >= localStartAngle - epsilon && i <= 2 * Math.PI + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partOne.push(newVector);\n              }\n            } else {\n              if (i >= localStartAngle - epsilon && i <= localEndAngle + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partOne.push(newVector);\n              }\n            }\n          } else {\n            if (localEndAngle < 0) {\n              localEndAngle = 2 * Math.PI + localEndAngle;\n\n              if (i >= localStartAngle - epsilon && i <= localEndAngle + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partOne.push(newVector);\n              }\n            } else {\n              if (i >= -epsilon && i <= localEndAngle + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partTwo.push(newVector);\n              }\n\n              if (i >= localStartAngle - epsilon && i <= 2 * Math.PI + epsilon) {\n                let newVector = new this.THREE.Vector3(radiusOne * Math.cos(i), radiusTwo * Math.sin(i), 0);\n                newVector.applyMatrix4(transformMatrix);\n                partOne.push(newVector);\n              }\n            }\n          }\n        }\n\n        circleVertices = partOne;\n        circleVertices.push(...partTwo);\n        let zeroVector = new this.THREE.Vector3(0, 0, 0);\n\n        if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n          if (segmentHeight) {\n            let vectorOne = circleVertices[0].clone();\n            let vectorTwo = circleVertices[circleVertices.length - 1].clone();\n            let oneRadius = zeroVector.distanceTo(vectorOne);\n            let twoRadius = zeroVector.distanceTo(vectorTwo);\n            let midVector = circleVertices[Math.floor(circleVertices.length / 2)].clone();\n            let midRadius = zeroVector.distanceTo(midVector);\n            let planeNormal = midVector.clone().normalize();\n            let plane = new this.THREE.Plane(planeNormal); ///\n\n            let needToPushZero = false;\n            let distanceOne = plane.distanceToPoint(vectorOne);\n            let distanceTwo = plane.distanceToPoint(vectorTwo);\n\n            if (distanceOne < 0 && distanceTwo < 0) {\n              if (segmentHeight > midRadius) {\n                let heightDelta = segmentHeight - midRadius;\n                circleVertices.forEach(vertex => {\n                  if (-plane.distanceToPoint(vertex) > heightDelta) {\n                    vertex.markForDelete = true;\n                  }\n                });\n                circleVertices = circleVertices.filter(vertex => !vertex.markForDelete); ///\n\n                segmentHeight -= heightDelta;\n                let lengthOne = vectorOne.length();\n                let lengthTwo = vectorTwo.length();\n                let minRadius = Math.min(oneRadius, twoRadius);\n                oneRadius = minRadius;\n                twoRadius = minRadius;\n                vectorOne = vectorOne.normalize().multiplyScalar(lengthTwo);\n                vectorTwo = vectorTwo.normalize().multiplyScalar(lengthOne);\n                needToPushZero = true;\n              } else {\n                let heightDelta = segmentHeight - midRadius;\n                let vecOne = vectorTwo.clone().normalize().multiplyScalar(oneRadius);\n                let vecTwo = vectorOne.clone().normalize().multiplyScalar(twoRadius);\n                let localNormal = new this.THREE.Vector3().lerpVectors(vecOne, vecTwo, 0.5).normalize();\n                let localPlane = new this.THREE.Plane(localNormal);\n                circleVertices.forEach(vertex => {\n                  if (localPlane.distanceToPoint(vertex) > heightDelta) {\n                    vertex.markForDelete = true;\n                  }\n                });\n                circleVertices = circleVertices.filter(vertex => !vertex.markForDelete);\n                return circleVertices;\n              }\n            } ///\n\n\n            let differenceOne = Math.abs(plane.distanceToPoint(midVector)) - Math.abs(plane.distanceToPoint(vectorOne));\n            let differenceTwo = Math.abs(plane.distanceToPoint(midVector)) - Math.abs(plane.distanceToPoint(vectorTwo));\n            let segmentOne = Math.abs(segmentHeight - differenceOne);\n            let segmentTwo = Math.abs(segmentHeight - differenceTwo);\n            let proportionOne = 1 - segmentOne / midRadius;\n            let proportionTwo = 1 - segmentTwo / midRadius;\n            let proportion = Math.max(proportionOne, proportionTwo);\n            let firstPoint = vectorOne.clone().normalize().multiplyScalar(oneRadius * proportion);\n            let secondPoint = vectorTwo.clone().normalize().multiplyScalar(twoRadius * proportion);\n            circleVertices.unshift(firstPoint);\n            circleVertices.push(secondPoint); ///\n\n            if (needToPushZero) circleVertices.push(zeroVector);\n          } else {\n            circleVertices.push(zeroVector);\n          }\n        }\n      }\n\n      return circleVertices;\n    }\n\n    getRotationIndices(points, rotationVector, forEllipse = false) {\n      let addAngle = forEllipse ? 0 : Math.PI / 2;\n      let xAxis = new this.THREE.Vector3(1, 0, 0);\n      let pointsNum = points.length;\n      let startIndex = (addAngle + rotationVector.clone().normalize().angleTo(xAxis)) / (2 * Math.PI) * pointsNum;\n      let endIndex = (addAngle + Math.PI + rotationVector.clone().normalize().angleTo(xAxis)) / (2 * Math.PI) * pointsNum;\n\n      if (endIndex >= pointsNum) {\n        endIndex -= pointsNum;\n      }\n\n      startIndex = Math.floor(startIndex);\n      endIndex = Math.floor(endIndex);\n      return [startIndex, endIndex];\n    }\n\n    interpolateWithDistance(pointOne, pointTwo, distance) {\n      let difference = pointOne.distanceTo(pointTwo);\n      let points = [];\n\n      if (difference > distance) {\n        let proportion = difference / distance;\n\n        for (let i = 1; i < proportion; i++) {\n          let newPoint = new this.THREE.Vector3().lerpVectors(pointOne, pointTwo, i / proportion);\n          points.push(newPoint);\n        }\n      }\n\n      return points;\n    }\n\n    makeRotatedObjectWireframe(curvePoints, lineProps, rotationData, rotationObjectWireframes, forPolygon = false) {\n      let centerPoint = rotationData.positionPoint;\n      let rotationPitch = rotationData.rotationPitch;\n      let rotationAngle = rotationData.rotationAngle;\n      let rotationAxis = rotationData.rotationAxis; ///\n\n      let rotationPoints = curvePoints.map(point => point.clone()); ///\n\n      let renderOrders = [-99, -101];\n\n      for (let renderOrder of renderOrders) {\n        let startEdge = this.utils.drawConnectedLines(curvePoints, lineProps, true);\n        startEdge.renderOrder = renderOrder;\n        let endGeo = startEdge.geometry.clone();\n        rotationObjectWireframes.push(startEdge);\n        let endEdge = new this.THREE.Mesh(endGeo, startEdge.material);\n        endEdge.renderOrder = renderOrder;\n        rotationObjectWireframes.push(endEdge);\n        let yAxis = new this.THREE.Vector3(0, 1, 0);\n\n        if (!forPolygon) {\n          let edgeDetail = 64;\n          let numOfSegments = edgeDetail * Math.abs(rotationAngle / Math.PI / 2);\n          startEdge.rotateOnAxis(yAxis, Math.PI);\n          endEdge.rotateOnAxis(yAxis, Math.PI);\n          startEdge.geometry.translate(-centerPoint, 0, 0);\n          endEdge.rotateOnAxis(yAxis, -rotationAngle);\n          endEdge.geometry.translate(-centerPoint, -rotationPitch, 0);\n          let rotationPoints = curvePoints.map(point => point.clone().applyAxisAngle(yAxis, Math.PI));\n\n          for (let i = 0; i < rotationPoints.length; i++) {\n            let indexThree = i + 2;\n            if (indexThree >= rotationPoints.length) indexThree -= rotationPoints.length;\n            let indexTwo = indexThree - 1;\n            if (indexTwo < 0) indexTwo += rotationPoints.length;\n            let indexOne = indexTwo - 1;\n            if (indexOne < 0) indexOne += rotationPoints.length;\n            let pointOne = rotationPoints[indexOne].clone().normalize();\n            let pointTwo = rotationPoints[indexTwo].clone().normalize();\n            let pointThree = rotationPoints[indexThree].clone().normalize();\n            let vectorOne = pointTwo.clone().sub(pointOne);\n            let vectorTwo = pointThree.clone().sub(pointTwo);\n            let angleBetween = vectorOne.angleTo(vectorTwo);\n            if (angleBetween > Math.PI) angleBetween -= Math.PI;\n\n            if (angleBetween > Math.PI / 10) {\n              let localPoint = rotationPoints[indexTwo].clone();\n              let edgePoints = [];\n              localPoint.add(new this.THREE.Vector3(centerPoint, rotationPitch, 0));\n\n              for (let i = 0; i < numOfSegments + 1; i++) {\n                let newPoint = localPoint.clone().applyAxisAngle(yAxis, -i * rotationAngle / numOfSegments);\n                newPoint.sub(new this.THREE.Vector3(0, i * rotationPitch / numOfSegments, 0));\n                edgePoints.push(newPoint);\n              }\n\n              let edgeLine = this.utils.drawConnectedLines(edgePoints, lineProps);\n              edgeLine.renderOrder = renderOrder;\n              rotationObjectWireframes.push(edgeLine);\n            }\n          }\n        } else {\n          let edgeDetail = 32;\n          let numOfSegments = edgeDetail * Math.abs(rotationAngle / Math.PI / 2);\n          startEdge.geometry.translate(centerPoint, rotationPitch, 0);\n          endEdge.geometry.translate(centerPoint, 0, 0);\n          endEdge.rotateOnAxis(yAxis, -rotationAngle);\n          rotationPoints.forEach(point => {\n            let localPoint = point.clone();\n            let edgePoints = [];\n            localPoint.add(new this.THREE.Vector3(centerPoint, rotationPitch, 0));\n\n            for (let i = 0; i < numOfSegments + 1; i++) {\n              let newPoint = localPoint.clone().applyAxisAngle(yAxis, -i * rotationAngle / numOfSegments);\n              newPoint.sub(new this.THREE.Vector3(0, i * rotationPitch / numOfSegments, 0));\n              edgePoints.push(newPoint);\n            }\n\n            let edgeLine = this.utils.drawConnectedLines(edgePoints, lineProps);\n            edgeLine.renderOrder = renderOrder;\n            rotationObjectWireframes.push(edgeLine);\n          });\n        }\n      }\n    }\n\n    makeCanvasMaterial(areaColor, hatchColor, orientationVector, hatchSpacing, hatchThickness) {\n      const POSITION_PIXEL_RATIO = 10;\n      let canvasMaterial;\n\n      if (hatchSpacing && hatchThickness) {\n        let tangent = orientationVector.x / orientationVector.y;\n        let canvas = document.createElement(\"canvas\");\n        let ctx = canvas.getContext(\"2d\");\n        ctx.fillStyle = \"rgb(\" + areaColor.red + \", \" + areaColor.green + \", \" + areaColor.blue + \")\";\n        ctx.strokeStyle = \"rgb(\" + hatchColor.red + \", \" + hatchColor.green + \", \" + hatchColor.blue + \")\";\n        ctx.lineWidth = hatchThickness * POSITION_PIXEL_RATIO;\n        /**\n         * canvas defaults are\n         * width:  300px,\n         * height: 150px\n         */\n\n        let textureWidth = 300;\n        let textureHeight = 150;\n        ctx.fillRect(0, 0, textureWidth, textureHeight);\n        hatchSpacing += 2 * hatchThickness;\n        let correctedHatchSpacing = hatchSpacing * POSITION_PIXEL_RATIO;\n\n        for (let i = 0; i < (textureWidth + textureHeight) / correctedHatchSpacing; i++) {\n          // TODO: Might need to change logic\n          if (orientationVector.y == 0) {\n            ctx.beginPath();\n            ctx.moveTo(0, i * correctedHatchSpacing);\n            ctx.lineTo(300, i * correctedHatchSpacing);\n            ctx.closePath();\n            ctx.stroke();\n          } else if (orientationVector.x == 0) {\n            ctx.beginPath();\n            ctx.moveTo(i * correctedHatchSpacing, 0);\n            ctx.lineTo(i * correctedHatchSpacing, 150);\n            ctx.closePath();\n            ctx.stroke();\n          } else {\n            ctx.beginPath();\n            ctx.moveTo(i * correctedHatchSpacing * tangent, -5);\n            ctx.lineTo(-5, i * correctedHatchSpacing);\n            ctx.closePath();\n            ctx.stroke();\n          }\n        }\n\n        let canvasTexture = new this.THREE.CanvasTexture(canvas);\n        canvasMaterial = new this.THREE.MeshBasicMaterial({\n          map: canvasTexture,\n          side: this.THREE.DoubleSide\n        });\n      } else {\n        canvasMaterial = new this.THREE.MeshBasicMaterial({\n          color: new this.THREE.Color(`rgb(${areaColor.red}, ${areaColor.green}, ${areaColor.blue})`),\n          side: this.THREE.DoubleSide\n        });\n      }\n\n      return canvasMaterial;\n    }\n\n    loadTexts(textProps, texts, jsonData, insertionPoints) {\n      let font = this.loadFont(textProps);\n\n      if (font) {\n        let textArr = [];\n        texts.forEach(text => {\n          let newText = this.drawText(text, textProps, font);\n          newText.jsonID = text.id;\n          textArr.push(newText);\n        });\n        let positionedTexts = this.applyPositionAndRotation(textArr, insertionPoints);\n        positionedTexts.forEach(text => {\n          let objectInfo = {\n            id: text.jsonID,\n            data: jsonData,\n            sceneObject: text\n          };\n          this.ThreeObjects.push(objectInfo);\n          this.scene.add(text);\n        });\n      }\n    }\n\n    applyPositionAndRotation(meshArr, insertionPoints) {\n      let positionedMeshArr = [];\n      if (insertionPoints) insertionPoints.forEach(insertionPoint => {\n        let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          meshArr.forEach(mesh => pivotPoint.attach(mesh.clone()));\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          meshArr.forEach(mesh => pivotPoint.attach(mesh.clone()));\n        }\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        pivotPoint.children.forEach(child => {\n          child.applyMatrix4(pivotPoint.matrixWorld);\n          positionedMeshArr.push(child);\n        });\n      });\n      return positionedMeshArr;\n    }\n\n    getFontUrl(textProps) {\n      let fontType = textProps.type;\n      let fontStyle = textProps.style;\n      let fontUrl = \"\";\n      let fontName = \"\";\n\n      if (fontStyle == 1) {\n        if (fontType == 1) {\n          fontUrl = this.fontUrlArray.Regular[\"Arial\"];\n          fontName = \"FONT_REGULAR_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.fontUrlArray.Regular[\"Calibri\"];\n          fontName = \"FONT_REGULAR_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.fontUrlArray.Regular[\"Courier\"];\n          fontName = \"FONT_REGULAR_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.fontUrlArray.Regular[\"TimesNewRoman\"];\n          fontName = \"FONT_REGULAR_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 2) {\n        if (fontType == 1) {\n          fontUrl = this.fontUrlArray.Italic[\"Arial\"];\n          fontName = \"FONT_ITALIC_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.fontUrlArray.Italic[\"Calibri\"];\n          fontName = \"FONT_ITALIC_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.fontUrlArray.Italic[\"Courier\"];\n          fontName = \"FONT_ITALIC_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.fontUrlArray.Italic[\"TimesNewRoman\"];\n          fontName = \"FONT_ITALIC_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 3) {\n        if (fontType == 1) {\n          fontUrl = this.fontUrlArray.Bold[\"Arial\"];\n          fontName = \"FONT_BOLD_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.fontUrlArray.Bold[\"Calibri\"];\n          fontName = \"FONT_BOLD_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.fontUrlArray.Bold[\"Courier\"];\n          fontName = \"FONT_BOLD_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.fontUrlArray.Bold[\"TimesNewRoman\"];\n          fontName = \"FONT_BOLD_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 4) {\n        if (fontType == 1) {\n          fontUrl = this.fontUrlArray.BoldItalic[\"Arial\"];\n          fontName = \"FONT_BOLD_ITALIC_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.fontUrlArray.BoldItalic[\"Calibri\"];\n          fontName = \"FONT_BOLD_ITALIC_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.fontUrlArray.BoldItalic[\"Courier\"];\n          fontName = \"FONT_BOLD_ITALIC_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.fontUrlArray.BoldItalic[\"TimesNewRoman\"];\n          fontName = \"FONT_BOLD_ITALIC_TIMESNEWROMAN\";\n        }\n      }\n\n      return [fontUrl, fontName];\n    }\n\n    loadFont(textProps) {\n      let loadedFonts = this.loadedFonts;\n      let [fontUrl, fontName] = this.getFontUrl(textProps);\n      let storedFont = loadedFonts[fontName];\n      let loader = new this.THREE.FontLoader();\n      let font;\n\n      if (fontUrl) {\n        if (storedFont) {\n          font = storedFont;\n        } else {\n          $.ajax({\n            'async': false,\n            type: \"GET\",\n            url: fontUrl\n          }).then(function (dataFont) {\n            font = loader.parse(dataFont);\n            loadedFonts[fontName] = font;\n          });\n        }\n      }\n\n      return font;\n    }\n\n    getTextGeo(text, textProps, font) {\n      let textContent = text.content;\n      let fontSize = textProps.size;\n      let heightWidthRatio = textProps.heightWidthRatio;\n      let textGeo = new this.THREE.TextBufferGeometry(textContent, {\n        font: font,\n        size: fontSize,\n        height: 0.01,\n        curveSegments: 8\n      });\n      textGeo.scale(heightWidthRatio, 1, 1);\n      textGeo.computeBoundingBox();\n\n      switch (text.relativePosition) {\n        case 1:\n          textGeo.center();\n          break;\n\n        case 2:\n          textGeo.translate(0, -textGeo.boundingBox.min.y, 0);\n          break;\n\n        case 3:\n          textGeo.translate(0, -textGeo.boundingBox.max.y, 0);\n          break;\n\n        case 4:\n          textGeo.translate(-textGeo.boundingBox.max.x, -textGeo.boundingBox.max.y, 0);\n          break;\n\n        case 5:\n          textGeo.translate(-textGeo.boundingBox.max.x, -textGeo.boundingBox.min.y, 0);\n          break;\n\n        default:\n          textGeo.center();\n          break;\n      }\n\n      return textGeo;\n    }\n\n    drawText(text, textProps, font) {\n      let normalVector = this.utils.initVector3(text.normalVector);\n      let directionVector = this.utils.initVector3(text.directionVector);\n      let textMesh;\n\n      if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n        let textInsertionPoint = text.insertionPoint;\n        let textGeo = this.getTextGeo(text, textProps, font);\n        let textMaterial = this.getMaterialFromColor(textProps.color);\n        textMesh = new this.THREE.Mesh(textGeo, textMaterial);\n        this.alignObjectWithVectors(textMesh, textInsertionPoint, normalVector, directionVector);\n      } else {\n        textMesh = new this.THREE.Mesh();\n      }\n\n      return textMesh;\n    }\n\n    alignObjectWithVectors(textMesh, textInsertionPoint, normalVector, directionVector) {\n      let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n      let dotProduct = textMesh.up.dot(newUpVector);\n      let rotationAngle = -Math.acos(dotProduct);\n      textMesh.rotateOnAxis(normalVector, rotationAngle);\n      textMesh.position.set(textInsertionPoint.x, textInsertionPoint.y, textInsertionPoint.z);\n      textMesh.up = newUpVector;\n      textMesh.lookAt(textMesh.position.clone().add(normalVector));\n    }\n\n    drawCustom(jsonDataGlobal) {\n      let jsonData = lodash.cloneDeep(jsonDataGlobal);\n      console.log(jsonData); // DynamicSectionalPlanesData is located in public directory\n\n      if (DynamicSectionalPlanesData) {\n        this.assignDynamicSectionalPlanes(jsonData, DynamicSectionalPlanesData);\n        this.DynamicSectionalPlane.initSectionalPlanes(DynamicSectionalPlanesData);\n      }\n\n      this.settingsModifyJSON(jsonData);\n      this.jsonData = jsonData;\n      if (jsonData.fasteners) this.FastenerLoader.drawFasteners();\n      if (jsonData.drawingObjects) this.drawObject(jsonData);\n      if (jsonData.arrows) jsonData.arrows.forEach(arrowData => {\n        let meshArray = this.ArrowLoader.drawArrows(arrowData);\n        meshArray.forEach(mesh => {\n          let drawnObject = new Object();\n          drawnObject.id = arrowData.id;\n          drawnObject.type = \"arrow\";\n          drawnObject.data = arrowData;\n          drawnObject.sceneObject = mesh;\n          drawnObject.isOnScene = mesh.IsHighLight ? false : true;\n          this.ThreeObjects.push(drawnObject);\n          if (!mesh.IsHighLight) this.scene.add(drawnObject.sceneObject);\n        });\n      });\n      if (jsonData.dimensionalChains) this.DimensionalChainLoader.drawDimensionalChains(jsonData.dimensionalChains);\n      this.SectionalPlane.applySectionalPlanes();\n      this.DynamicSectionalPlane.applySectionalPlanes();\n      if (!this.SceneCenterComputed) this.centerScene();\n      if (!this.stencilScenes.length) this.stencilScenes.push(this.scene);\n\n      if (this.isWireFrameOnly) {\n        this.isWireFrameOnly = false;\n        this.ToggleWireframe();\n      }\n    }\n\n    assignDynamicSectionalPlanes(jsonData, dynamicSectionalPlane) {\n      if (dynamicSectionalPlane.targets.drawingObjects && jsonData.drawingObjects) {\n        dynamicSectionalPlane.targets.drawingObjects.forEach(targetID => {\n          jsonData.drawingObjects.forEach(object => {\n            if (object.id === targetID) {\n              object.dynamicSectionalPlanes = object.dynamicSectionalPlanes ? object.dynamicSectionalPlanes : [];\n              object.dynamicSectionalPlanes.push(dynamicSectionalPlane.planeData.id);\n            }\n          });\n        });\n      }\n    }\n\n    settingsModifyJSON(jsonData) {\n      let settings = jsonData.settings;\n      this.fillPropsWithSettingProps(jsonData, settings, \"arrows\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"dimensionalChains\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"drawingObjects\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"fasteners\");\n    }\n\n    fillPropsWithSettingProps(jsonData, settings, objType) {\n      let settingsType = objType.slice(0, objType.length - 1);\n      let objects = jsonData[objType];\n\n      if (settings[settingsType] && settings[settingsType].visualisation) {\n        let objectSettingProps = settings[settingsType].properties;\n        objects.forEach(obj => {\n          if (!obj.properties && objectSettingProps) obj.properties = objectSettingProps;else if (objectSettingProps) {\n            if (!obj.properties.area && objectSettingProps.area) obj.properties.area = objectSettingProps.area;\n            if (!obj.properties.line && objectSettingProps.line) obj.properties.line = objectSettingProps.line;\n            if (!obj.properties.text && objectSettingProps.text) obj.properties.text = objectSettingProps.text;\n            if (!obj.properties.volume && objectSettingProps.volume) obj.properties.volume = objectSettingProps.volume;\n            if (!obj.properties.point && objectSettingProps.point) obj.properties.point = objectSettingProps.point;\n            if (!obj.properties.intersectionArea && objectSettingProps.intersectionArea) obj.properties.intersectionArea = objectSettingProps.intersectionArea;\n            if (!obj.properties.intersectionLine && objectSettingProps.intersectionLine) obj.properties.intersectionLine = objectSettingProps.intersectionLine;\n            if (!obj.properties.fastener && objectSettingProps.fastener) obj.properties.fastener = objectSettingProps.fastener;\n            if (!obj.properties.axis && objectSettingProps.axis) obj.properties.axis = objectSettingProps.axis;\n            if (!obj.properties.addOn && objectSettingProps.addOn) obj.properties.addOn = objectSettingProps.addOn;\n          }\n        });\n      } else {\n        jsonData[objType] = null;\n      }\n    }\n\n    getMeshUpVector(mesh, direction) {\n      let upObject = new this.THREE.Object3D();\n      upObject.position.copy(mesh.up);\n      mesh.add(upObject);\n      mesh.lookAt(direction);\n      mesh.updateMatrixWorld();\n      let upVector = upObject.getWorldPosition(new this.THREE.Vector3());\n      return upVector;\n    }\n\n    removeObject(type) {\n      this.stencilScenes.forEach(scene => {\n        this.ThreeObjects.forEach(object => {\n          scene.children.forEach(child => {\n            if (object.type == type && object.sceneObject.uuid == child.uuid) {\n              object.isOnScene = false;\n              scene.remove(child);\n            }\n          });\n        });\n\n        for (let index = 0; index < 50; index++) {\n          //there may be more than one in that type and to remove all off them loop is needed\n          for (let i = 0; i < scene.children.length; i++) {\n            if (scene.children[i].customTypeName == type) {\n              scene.remove(scene.children[i]);\n\n              for (let to = 0; to < this.ThreeObjects.length; to++) {\n                if (this.ThreeObjects[to].type == type) {\n                  this.ThreeObjects[to].isOnScene = false;\n                }\n              }\n            }\n          }\n        }\n      });\n    }\n\n    removeObjectMaskette(XorY) {\n      // for (let index = 0; index < this.ThreeObjects.length; index++) {\n      //  if(this.ThreeObjects[index].type==\"maskette\"){\n      //     console.log(this.ThreeObjects[index]);\n      //  }\n      // }\n      for (let index = 0; index < 50; index++) {\n        //there may be more than one in that type and to remove all off them loop is needed\n        for (let i = 0; i < this.scene.children.length; i++) {\n          if (this.scene.children[i].customTypeName == \"maskette\" && this.scene.children[i].XorY == XorY) {\n            this.scene.remove(this.scene.children[i]);\n\n            for (let to = 0; to < this.ThreeObjects.length; to++) {\n              if (this.ThreeObjects[to].type == \"maskette\" && this.ThreeObjects[to].sceneObject.XorY == XorY) {\n                this.ThreeObjects[to].isOnScene = false;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    redrawObject(type) {\n      var self = this;\n      console.log(type);\n      this.ThreeObjects.forEach(function (element) {\n        if (type == \"maskette\" && element.type == \"maskette\" && !element.isOnScene) {\n          // self.drawMaskette(element.data);\n          self.scene.add(element.sceneObject);\n          element.isOnScene = true;\n        }\n      });\n    }\n\n    redrawObjectMaskette(XorY) {\n      var self = this;\n      this.ThreeObjects.forEach(function (element) {\n        if (element.type == \"maskette\" && !element.isOnScene && element.sceneObject.XorY == XorY) {\n          if (self.interactiveZ && element.sceneObject.IsHighLight) {\n            self.scene.add(element.sceneObject);\n            element.isOnScene = true;\n          } else if (!self.interactiveZ && !element.sceneObject.IsHighLight) {\n            self.scene.add(element.sceneObject);\n            element.isOnScene = true;\n          }\n        }\n      });\n    }\n\n    redrawCSGObject(element) {\n      let targetScene = element.targetScene;\n\n      if (element.IsCSGObject && !element.isOnScene) {\n        if (!this.isWireFrameOnly) targetScene.add(element.sceneObject);else targetScene.add(element.sceneObjectTransparentCSG);\n        element.isOnScene = true;\n      }\n    }\n\n    highlightX(value) {\n      this.interactiveZ = true;\n      this.removeObjectMaskette(\"x\");\n      this.redrawObjectMaskette(\"x\");\n    }\n\n    highlightY(value) {\n      this.interactiveZ = true;\n      this.removeObjectMaskette(\"y\");\n      this.redrawObjectMaskette(\"y\");\n    } // TODO: Add highlighting for dimensional chains\n\n\n    highlightZ(extrusionVectorInteractiveZ) {\n      this.interactiveZ = true;\n      this.extrusionVectorInteractiveZ = extrusionVectorInteractiveZ; //this.removeObject(\"maskette\");\n      //this.redrawObject(\"maskette\");\n    }\n\n    clearXHighlight() {\n      this.interactiveZ = false;\n      this.removeObjectMaskette(\"x\");\n      this.redrawObjectMaskette(\"x\");\n    }\n\n    clearYHighlight() {\n      this.interactiveZ = false;\n      this.removeObjectMaskette(\"y\");\n      this.redrawObjectMaskette(\"y\");\n    }\n\n    clearZHighlight() {\n      this.interactiveZ = false;\n    }\n\n    degreeToRadians(degree) {\n      return degree * Math.PI / 180;\n    }\n\n    radiansToDegree(radian) {\n      return radian * 180 / Math.PI;\n    }\n\n    animate(that) {\n      this.updateRendererAndCamera(that.renderer, that.camera);\n      this.renderAllScenes(that);\n      window.requestAnimationFrame(function () {\n        that.animate(that);\n        that.TWEEN.update();\n      }); // setTimeout(() => {\n      //   that.animate(that)\n      //   that.TWEEN.update();\n      // }, 100);\n    }\n\n    renderAllScenes(GraphicsThree3D) {\n      GraphicsThree3D.renderer.clear();\n      GraphicsThree3D.stencilScenes.forEach(scene => {\n        GraphicsThree3D.renderer.render(scene, GraphicsThree3D.camera);\n        GraphicsThree3D.renderer.clearDepth();\n      });\n    }\n\n    updateRendererAndCamera(renderer, camera) {\n      let canvas = renderer.domElement;\n      let width = canvas.clientWidth;\n      let height = canvas.clientHeight;\n      let needResize = canvas.width !== width || canvas.height !== height || canvas.style.width !== width || canvas.style.height !== height;\n\n      if (needResize) {\n        renderer.setSize(width, height, false);\n        let multiplier = 64;\n        let cameraAspectRatio = width / height;\n        let cameraViewSize = 5000;\n        camera.left = -cameraViewSize * cameraAspectRatio / multiplier;\n        camera.right = cameraViewSize * cameraAspectRatio / multiplier;\n        camera.top = cameraViewSize / multiplier;\n        camera.bottom = -cameraViewSize / multiplier;\n        camera.updateProjectionMatrix();\n      }\n    }\n\n    makeCameraTween(direction, distance) {\n      let controls = this.controls;\n      let camera = this.camera;\n      let midVector = this.objectMidVector;\n      let currentPosition = {\n        x: camera.position.x,\n        y: camera.position.y,\n        z: camera.position.z,\n        zoom: camera.zoom\n      };\n      let currentTarget = {\n        x: controls.target.x,\n        y: controls.target.y,\n        z: controls.target.z\n      };\n      let finalTarget = {\n        x: midVector.x,\n        y: midVector.y,\n        z: midVector.z\n      };\n      let targetsNotEqual = currentPosition.x != finalTarget.x && currentPosition.y != finalTarget.y && currentPosition.z != finalTarget.z;\n\n      if (direction == \"x\" && this.previousCameraPosition == \"y\") {\n        currentPosition.z += 5;\n      }\n\n      let changePosition;\n      this.cameraTweenActive = true;\n\n      if (direction == \"initial\") {\n        changePosition = {\n          x: midVector.x + this.initialPosition.x,\n          y: midVector.y + this.initialPosition.y,\n          z: midVector.z + this.initialPosition.z\n        };\n      } else if (direction == \"x\") {\n        changePosition = {\n          x: midVector.x - distance,\n          y: midVector.y,\n          z: midVector.z\n        };\n      } else if (direction == \"y\") {\n        changePosition = {\n          x: midVector.x,\n          y: midVector.y + distance,\n          z: midVector.z + 5\n        };\n      } else if (direction == \"z\") {\n        changePosition = {\n          x: midVector.x,\n          y: midVector.y,\n          z: midVector.z + distance\n        };\n      } else {\n        throw new Error(\"No direction is defined for tweening\");\n      }\n\n      changePosition.zoom = 1;\n      let speed = 350;\n      let type = this.TWEEN.Easing.Cubic.InOut;\n      let global = this;\n      let tween = new this.TWEEN.Tween(currentPosition).to(changePosition, speed).easing(type).onUpdate(function () {\n        camera.position.set(currentPosition.x, currentPosition.y, currentPosition.z);\n        camera.zoom = currentPosition.zoom;\n        camera.lookAt(midVector);\n        camera.updateProjectionMatrix();\n        controls.update();\n      }).onComplete(function () {\n        if (direction == \"x\") global.previousCameraPosition = \"x\";\n\n        if (direction == \"y\") {\n          camera.position.z -= 5;\n          camera.lookAt(midVector);\n          camera.updateProjectionMatrix();\n          controls.update();\n          global.previousCameraPosition = \"y\";\n        }\n\n        if (direction == \"z\") global.previousCameraPosition = \"z\";\n        if (direction == \"initial\") global.previousCameraPosition = \"inital\";\n        global.cameraTweenActive = false;\n      });\n\n      if (targetsNotEqual) {\n        camera.zoom = 1;\n        let controlsTween = new this.TWEEN.Tween(currentTarget).to(finalTarget, speed).easing(type).onUpdate(function () {\n          controls.target.set(currentTarget.x, currentTarget.y, currentTarget.z - 0.01);\n          camera.up = new global.THREE.Vector3(0, 1, 0);\n          camera.lookAt(controls.target);\n          controls.update();\n        }).chain(tween);\n        controlsTween.start();\n      } else {\n        tween.start();\n      }\n    }\n\n    ChangeCameraPosition0() {\n      if (!this.cameraTweenActive) this.makeCameraTween(\"initial\");\n      this.interactiveZ = false;\n    }\n\n    ChangeCameraPosition1() {\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"x\", 150);\n    }\n\n    ChangeCameraPosition2() {\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"y\", 150);\n    }\n\n    ChangeCameraPosition3() {\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"z\", 150);\n    }\n\n    ToggleWireframe() {\n      this.isWireFrameOnly = !this.isWireFrameOnly;\n      this.removeObject(\"polygon\");\n      this.removeObject(\"ellipse\");\n      this.ThreeObjects.forEach(element => this.redrawCSGObject(element));\n    }\n\n    SendBase64PNGToServer() {\n      let posVector = this.objectMidVector;\n      let posVectorOne = posVector.clone().add(new this.THREE.Vector3(this.initialPosition.x, this.initialPosition.y, this.initialPosition.z));\n      let posVectorTwo = posVector.clone().sub(new this.THREE.Vector3(150, 0, 0));\n      let posVectorThree = posVector.clone().add(new this.THREE.Vector3(0, 150, 0));\n      let posVectorFour = posVector.clone().add(new this.THREE.Vector3(0, 0, 150));\n      this.changeCameraPositionAndDownloadPNG(posVectorOne, \"grafik1.png\");\n      this.removeObject(\"maskette\");\n      this.changeCameraPositionAndDownloadPNG(posVectorTwo, \"grafik2.png\");\n      this.changeCameraPositionAndDownloadPNG(posVectorThree, \"grafik3.png\");\n      this.redrawObject(\"maskette\");\n      this.changeCameraPositionAndDownloadPNG(posVectorFour, \"grafik4.png\");\n    }\n\n    changeCameraPositionAndDownloadPNG(posVector, fileName) {\n      this.camera.position.copy(posVector);\n      this.camera.lookAt(this.objectMidVector);\n      this.camera.updateProjectionMatrix();\n      this.controls.update();\n      this.renderAllScenes(this);\n      this.downloadAsPNGImage(fileName);\n    }\n\n    downloadAsPNGImage(fileName) {\n      var strMime = \"image/png\";\n      var imgData = this.renderer.domElement.toDataURL(strMime); //download image\n\n      var link = document.createElement('a');\n\n      if (typeof link.download === 'string') {\n        document.body.appendChild(link); //Firefox requires the link to be in the body\n\n        link.download = fileName;\n        link.href = imgData;\n        link.click();\n        document.body.removeChild(link); //remove the link when done\n      } else {\n        location.replace(uri);\n      }\n    }\n\n  }\n\n  _exports.GraphicsThree3D = GraphicsThree3D;\n});","define(\"m08-2020/lib old/Utils\", [\"exports\", \"three/examples/jsm/lines/Line2\", \"three/examples/jsm/lines/LineMaterial\", \"three/examples/jsm/lines/LineGeometry\"], function (_exports, _Line, _LineMaterial, _LineGeometry) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Utils = void 0;\n\n  class Utils {\n    constructor(three) {\n      this.THREE = three;\n    }\n\n    initVector2(vectorData) {\n      return new this.THREE.Vector2(vectorData.x, vectorData.y);\n    }\n\n    initVector3(vectorData) {\n      let vector = new this.THREE.Vector3(vectorData.x, vectorData.y, vectorData.z ? vectorData.z : 0);\n      return vector;\n    }\n\n    vectorsAreEqual(vecA, vecB) {\n      const DELTA = 1e-4;\n      return Math.abs(vecA.x - vecB.x) < DELTA && Math.abs(vecA.y - vecB.y) < DELTA && Math.abs(vecA.z - vecB.z) < DELTA;\n    }\n\n    addVectorObjects(vecOne, vecTwo) {\n      return {\n        x: vecOne.x + vecTwo.x,\n        y: vecOne.y + vecTwo.y,\n        z: vecOne.z + vecTwo.z\n      };\n    }\n\n    applyRotationToVector(vectorData, rotationParams) {\n      let rotationAngle = rotationParams.rotationAngle * Math.PI / 180;\n      let rotationAxis = this.initVector3(rotationParams.rotationAxis).normalize();\n      let rotationPoint = this.initVector3(rotationParams.rotationPoint);\n      let vector = this.initVector3(vectorData).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint);\n      return vector;\n    }\n\n    normalizeVector(vector) {\n      let vectorLength = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2) + Math.pow(vector.z, 2));\n      let normalizedVector = {\n        x: vector.x / vectorLength,\n        y: vector.y / vectorLength,\n        z: vector.z / vectorLength\n      };\n      return normalizedVector;\n    }\n\n    roundToDecimals(num, decimals = 1) {\n      let multiplier = Math.pow(10, decimals);\n      return Math.round(num * multiplier) / multiplier;\n    }\n\n    checkVectorsEquality(vectorOne, vectorTwo, precision = 2) {\n      return this.roundToDecimals(vectorOne.x, precision) === this.roundToDecimals(vectorTwo.x, precision) && this.roundToDecimals(vectorOne.y, precision) === this.roundToDecimals(vectorTwo.y, precision) && this.roundToDecimals(vectorOne.z, precision) === this.roundToDecimals(vectorTwo.z, precision);\n    }\n\n    getEulerOneAndTwo(crossVector) {\n      let quaternionOne = new this.THREE.Quaternion();\n      quaternionOne.setFromUnitVectors(crossVector, new this.THREE.Vector3(0, 0, 1));\n      let eulerOne = new this.THREE.Euler();\n      eulerOne.setFromQuaternion(quaternionOne);\n      let quaternionTwo = new this.THREE.Quaternion();\n      quaternionTwo.setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), crossVector);\n      let eulerTwo = new this.THREE.Euler();\n      eulerTwo.setFromQuaternion(quaternionTwo);\n      return [eulerOne, eulerTwo];\n    }\n\n    getCrossVector(vecOne, vecTwo) {\n      return new this.THREE.Vector3().crossVectors(vecOne, vecTwo).normalize();\n    }\n\n    clampNum(num, min, max) {\n      return Math.min(Math.max(num, min), max);\n    }\n\n    getColorRGB(colorData) {\n      let color = \"rgb(\" + colorData.red + \", \" + colorData.green + \", \" + colorData.blue + \")\";\n      return color;\n    }\n\n    getMaterialFromColorProps(colorProps) {\n      let materialOptions = {\n        color: this.getColorRGB(colorProps),\n        transparent: true,\n        opacity: colorProps.alpha / 255,\n        side: this.THREE.DoubleSide\n      };\n      let material = new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    isComplexVolume(properties) {\n      return properties && properties.volume && properties.volume.type > 1;\n    }\n\n    skewExtrudedGeometry(geometry, [skewX, skewY]) {\n      geometry.applyMatrix4(new this.THREE.Matrix4().set(1, 0, skewX, 0, 0, 1, skewY, 0, 0, 0, 1, 0, 0, 0, 0, 1));\n    }\n\n    getSkewParams(extrusionVector) {\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let skewX = extrusionNormalized.x / extrusionNormalized.z;\n      let skewY = extrusionNormalized.y / extrusionNormalized.z;\n      return [skewX, skewY];\n    }\n\n    getVerticesFromBufferGeometry(geometry) {\n      let vertices = [];\n      let posAttr = geometry.attributes.position;\n\n      if (posAttr) {\n        for (let i = 0; i < posAttr.count; i++) {\n          vertices.push(new this.THREE.Vector3().fromBufferAttribute(posAttr, i));\n        }\n      }\n\n      return vertices;\n    }\n\n    mergeLinePoints(pairArr, lineDirection) {\n      pairArr.sort((pairOne, pairTwo) => pairTwo[0].clone().projectOnVector(lineDirection).lengthSq() - pairOne[0].clone().projectOnVector(lineDirection).lengthSq());\n      let startingLength = pairArr.length;\n      let previousLengthChanged = true;\n\n      while (previousLengthChanged) {\n        for (let i = 0; i < pairArr.length; i++) {\n          for (let j = i; j < pairArr.length; j++) {\n            let pairOne = pairArr[i];\n            let pairTwo = pairArr[j];\n\n            if (this.linesLayOnSamePlane(pairOne, pairTwo, lineDirection)) {\n              let mergedPair = this.mergeTwoLinePoints(pairOne, pairTwo, lineDirection);\n\n              if (mergedPair) {\n                pairArr[i] = mergedPair;\n                if (i !== j) pairArr.splice(j, 1);\n              }\n            }\n          }\n        }\n\n        if (pairArr.length === startingLength) previousLengthChanged = false;else startingLength = pairArr.length;\n      }\n\n      return pairArr;\n    }\n\n    mergeTwoLinePoints(pairOne, pairTwo, lineDirection) {\n      if (pairOne[0].equals(pairTwo[0]) && pairOne[1].equals(pairTwo[1])) return pairOne;\n      let pairOneSorted = [...pairOne].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let pairTwoSorted = [...pairTwo].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let projectedPoints = [...pairOneSorted, ...pairTwoSorted].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let minPoint = projectedPoints[0];\n      let [pOneL0, pOneL1] = [pairOneSorted[0].distanceTo(minPoint), pairOneSorted[1].distanceTo(minPoint)];\n      let [pTwoL0, pTwoL1] = [pairTwoSorted[0].distanceTo(minPoint), pairTwoSorted[1].distanceTo(minPoint)];\n      let merged;\n      if (pTwoL0 === pOneL0 && pTwoL1 === pOneL1) merged = pairTwoSorted;else if (pTwoL0 > pOneL0 && pTwoL1 < pOneL1) merged = pairOneSorted;else if (pOneL0 > pTwoL0 && pOneL1 < pTwoL1) merged = pairTwoSorted;else if (pTwoL0 <= pOneL0 && pTwoL1 >= pOneL0 && pTwoL1 <= pOneL1) merged = [pairTwoSorted[0], pairOneSorted[1]];else if (pTwoL0 >= pOneL0 && pTwoL0 <= pOneL1 && pTwoL1 >= pOneL1) merged = [pairOneSorted[0], pairTwoSorted[1]];else if (pTwoL0 >= pOneL1 && pTwoL0 <= pOneL0 && pTwoL1 <= pOneL1) merged = [pairOneSorted[0], pairTwoSorted[1]];else if (pTwoL0 >= pOneL0 && pTwoL1 <= pOneL0 && pTwoL1 >= pOneL1) merged = [pairTwoSorted[0], pairOneSorted[1]];\n      return merged;\n    }\n\n    linesLayOnSamePlane(pairOne, pairTwo, planeNormal) {\n      const DELTA = 0.0001;\n      let projectionOne = pairOne[0].clone().projectOnPlane(planeNormal);\n      let projectionTwo = pairTwo[0].clone().projectOnPlane(planeNormal);\n      return projectionOne.distanceTo(projectionTwo) < DELTA;\n    }\n\n    getCrossVectorFromPoints(points) {\n      let pointVectors = [];\n\n      for (let i = 0; i < points.length - 1; i++) {\n        pointVectors.push(new this.THREE.Vector3().subVectors(points[i + 1], points[i]));\n      }\n\n      pointVectors.push(new this.THREE.Vector3().subVectors(points[0], points[points.length - 1]));\n      let crossVector = new this.THREE.Vector3();\n\n      for (let i = 0; i < pointVectors.length - 1; i++) {\n        crossVector.add(new this.THREE.Vector3().crossVectors(pointVectors[i], pointVectors[i + 1]));\n      }\n\n      crossVector.add(new this.THREE.Vector3().crossVectors(pointVectors[pointVectors.length - 1], pointVectors[0]));\n      crossVector.normalize();\n      return crossVector;\n    }\n\n    vectorIsZero(vectorData) {\n      return !vectorData.x && !vectorData.y && !vectorData.z;\n    }\n\n    drawLine(pointOne, pointTwo, lineProps) {\n      if (lineProps.type === 0 || lineProps.thickness === 0 || lineProps.color.alpha === 0) return new this.THREE.Mesh();\n      let lineMaterial = this.getMaterialFromColorProps(lineProps.color);\n      let lineRadius = lineProps.thickness / 2;\n      let lineSize = pointOne.distanceTo(pointTwo);\n      let lineGeo = new this.THREE.CylinderGeometry(lineRadius, lineRadius, lineSize, 8);\n      lineGeo.rotateX(Math.PI / 2);\n      lineGeo.translate(0, 0, lineSize / 2);\n      let lineMesh = new this.THREE.Mesh(lineGeo, lineMaterial);\n      lineMesh.position.copy(pointOne);\n      lineMesh.lookAt(pointTwo);\n      return lineMesh;\n    }\n\n    drawSolidLine(points, lineProps, closed = false) {\n      let lineMeshArray = [];\n      let localPoints = points.map(pt => pt.clone());\n      if (closed) localPoints.push(localPoints[0].clone());\n\n      for (let i = 0; i < localPoints.length - 1; i++) {\n        if (localPoints[i].distanceTo(localPoints[i + 1]) > 0) {\n          let lineMesh = this.drawLine(localPoints[i], localPoints[i + 1], lineProps, true);\n          lineMeshArray.push(lineMesh);\n        }\n      }\n\n      let firstLine = lineMeshArray.length > 0 ? lineMeshArray.splice(0, 1)[0] : new this.THREE.Mesh();\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    }\n\n    drawDashedLine(pointOne, pointTwo, factor, pointPairs) {\n      let newSegment = true;\n      let direction = new this.THREE.Vector3().subVectors(pointTwo, pointOne).normalize();\n      let distance = pointOne.distanceTo(pointTwo);\n      let segmentSize = factor;\n\n      while (distance > segmentSize) {\n        let newPoint = new this.THREE.Vector3().copy(pointOne).add(direction.clone().multiplyScalar(segmentSize));\n        if (newSegment) pointPairs.push([pointOne, newPoint]);\n        pointOne = newPoint;\n        distance -= segmentSize;\n        newSegment = !newSegment;\n      }\n    }\n\n    makeDashSegments(points, factor) {\n      let distanceDelta = factor;\n\n      for (let i = 0; i < points.length - 1; i++) {\n        let distance = points[i].distanceTo(points[i + 1]);\n\n        if (distance < distanceDelta) {\n          distanceDelta -= distance;\n        } else {\n          let newPoint = new this.THREE.Vector3().lerpVectors(points[i], points[i + 1], distanceDelta / distance);\n          newPoint.insertedPoint = true;\n          points.splice(i + 1, 0, newPoint);\n          distanceDelta = factor;\n        }\n      }\n    }\n\n    mergeLines(pointPairs, lineProps) {\n      let lineMeshArray = [];\n      pointPairs.forEach(pair => {\n        if (pair[0].distanceTo(pair[1]) > 0) {\n          let lineMesh = this.drawLine(pair[0], pair[1], lineProps);\n          lineMeshArray.push(lineMesh);\n        }\n      });\n      let firstLine = lineMeshArray.splice(0, 1)[0];\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    }\n\n    drawConnectedLines(points, lineProps, closed = false) {\n      let localPoints = points.map(point => point.clone());\n      let firstLine;\n\n      if (canDrawLine(lineProps)) {\n        if (canDrawSolidLine(lineProps)) {\n          firstLine = this.drawSolidLine(points, lineProps, closed);\n        } else if (canDrawDashedLine(lineProps)) {\n          let factor = lineProps.factor;\n          let pointPairs = [];\n          let segmentSize = factor;\n          if (closed) localPoints.push(localPoints[0].clone());\n          let bigSegmentPts = [];\n          let smallSegmentPts = [];\n\n          for (let p = 0; p < localPoints.length - 1; p++) {\n            let distance = localPoints[p].distanceTo(localPoints[p + 1]);\n\n            if (distance > factor) {\n              bigSegmentPts.push(localPoints[p]);\n              bigSegmentPts.push(localPoints[p + 1]);\n            } else {\n              smallSegmentPts.push(localPoints[p]);\n              smallSegmentPts.push(localPoints[p + 1]);\n            }\n          }\n\n          for (let p = 0; p < bigSegmentPts.length - 1; p++) {\n            let distance = bigSegmentPts[p].distanceTo(bigSegmentPts[p + 1]);\n            if (distance > segmentSize) this.drawDashedLine(bigSegmentPts[p], bigSegmentPts[p + 1], factor, pointPairs);else segmentSize -= segmentSize - distance;\n          }\n\n          this.makeDashSegments(smallSegmentPts, factor);\n          let newSegment = true;\n\n          for (let p = 0; p < smallSegmentPts.length - 1; p++) {\n            if (smallSegmentPts[p].insertedPoint) newSegment = !newSegment;\n            if (newSegment) pointPairs.push([smallSegmentPts[p], smallSegmentPts[p + 1]]);\n          }\n\n          firstLine = this.mergeLines(pointPairs, lineProps);\n        } else if (canDrawDottedLine(lineProps)) {\n          let lineMaterial = this.getMaterialFromColorProps(lineProps.color);\n          let factor = lineProps.factor;\n          let radius = lineProps.thickness / 2;\n          let circleGeos = [];\n          let segmentSize = factor;\n\n          if (closed) {\n            localPoints.push(localPoints[0].clone());\n          } ///\n\n\n          let bigSegmentPts = [];\n          let smallSegmentPts = [];\n\n          for (let p = 0; p < localPoints.length - 1; p++) {\n            let distance = localPoints[p].distanceTo(localPoints[p + 1]);\n\n            if (distance > factor) {\n              bigSegmentPts.push(localPoints[p]);\n              bigSegmentPts.push(localPoints[p + 1]);\n            } else {\n              smallSegmentPts.push(localPoints[p]);\n              smallSegmentPts.push(localPoints[p + 1]);\n            }\n          }\n\n          this.makeDashSegments(smallSegmentPts, factor); ///\n\n          smallSegmentPts.forEach(point => {\n            if (point.insertedPoint) {\n              let circle = new this.THREE.SphereGeometry(radius, 8, 8);\n              circle.translate(point.x, point.y, point.z);\n              circleGeos.push(circle);\n            }\n          });\n\n          for (let p = 0; p < bigSegmentPts.length - 1; p++) {\n            let distance = bigSegmentPts[p].distanceTo(bigSegmentPts[p + 1]);\n\n            if (distance > 0) {\n              if (distance > segmentSize) {\n                let numOfSegments = Math.floor(distance / segmentSize);\n\n                for (let i = 0; i < numOfSegments; i++) {\n                  let newPoint = new this.THREE.Vector3().lerpVectors(bigSegmentPts[p], bigSegmentPts[p + 1], i / numOfSegments);\n                  let circle = new this.THREE.SphereGeometry(radius, 8, 8);\n                  circle.translate(newPoint.x, newPoint.y, newPoint.z);\n                  circleGeos.push(circle);\n                  if (segmentSize < factor) segmentSize = factor;\n                }\n              } else {\n                segmentSize -= segmentSize - distance;\n              }\n            }\n          }\n\n          let firstCircle = circleGeos.splice(0, 1)[0];\n          circleGeos.forEach(circle => firstCircle.merge(circle));\n          firstLine = new this.THREE.Mesh(firstCircle, lineMaterial);\n        }\n      } else firstLine = new this.THREE.Mesh();\n\n      return firstLine; // Helper functions -->\n\n      function canDrawLine(lineProps) {\n        return lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined);\n      }\n\n      function canDrawSolidLine(lineProps) {\n        return lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0;\n      }\n\n      function canDrawDashedLine(lineProps) {\n        return lineProps.type === 2;\n      }\n\n      function canDrawDottedLine(lineProps) {\n        return lineProps.type === 3;\n      } // <-- Helper functions\n\n    }\n\n    drawFatLine(pointsArr, lineProps) {\n      const MIN_THICKNESS = 0.002;\n      let thickness = lineProps.thickness * 5e-3;\n      let {\n        color,\n        opacity\n      } = this.getColorAndOpacity(lineProps.color);\n      let lineGeoPoints = pointsArr.reduce((geoPoints, point) => {\n        geoPoints.push(point.x, point.y, point.z);\n        return geoPoints;\n      }, []);\n      let lineGeometry = new _LineGeometry.LineGeometry();\n      lineGeometry.setPositions(lineGeoPoints);\n      let lineMaterial = new _LineMaterial.LineMaterial({\n        transparent: true,\n        color,\n        opacity,\n        linewidth: thickness >= MIN_THICKNESS ? thickness : MIN_THICKNESS\n      });\n      let line = new _Line.Line2(lineGeometry, lineMaterial);\n      line.scale.set(1, 1, 1);\n      return line;\n    }\n\n    getColorAndOpacity(colorProps) {\n      let color = \"rgb(\" + colorProps.red + \", \" + colorProps.green + \", \" + colorProps.blue + \")\";\n      let opacity = colorProps.alpha / 255;\n      return {\n        color,\n        opacity\n      };\n    }\n\n    alignObjectWithVectors(object, normalVector, directionVector) {\n      let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n      let dotProduct = object.up.dot(newUpVector);\n      let rotationAngle = -Math.acos(dotProduct);\n      object.rotateOnAxis(normalVector, rotationAngle);\n      object.up = newUpVector;\n      object.lookAt(normalVector.add(object.position));\n    }\n\n    makeTestMesh(position, size = 1, color = 0xff0000) {\n      let geo = new this.THREE.BoxBufferGeometry(size, size, size);\n      let mat = new this.THREE.MeshBasicMaterial({\n        color\n      });\n      let mesh = new this.THREE.Mesh(geo, mat);\n      mesh.position.copy(position);\n      return mesh;\n    }\n\n    processUnits(unitsData) {\n      let units = {\n        length: \"\",\n        angle: \"\",\n        mass: \"\",\n        force: \"\",\n        temperature: \"\"\n      };\n\n      switch (unitsData.length) {\n        case 1:\n          units.length = \"mm\";\n          break;\n\n        case 2:\n          units.length = \"cm\";\n          break;\n\n        case 3:\n          units.length = \"m\";\n          break;\n\n        case 4:\n          units.length = \"km\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.angle) {\n        case 1:\n          units.angle = \"deg\";\n          break;\n\n        case 2:\n          units.angle = \"rad\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.mass) {\n        case 1:\n          units.mass = \"g\";\n          break;\n\n        case 2:\n          units.mass = \"kg\";\n          break;\n\n        case 3:\n          units.mass = \"t\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.force) {\n        case 1:\n          units.force = \"N\";\n          break;\n\n        case 2:\n          units.force = \"kN\";\n          break;\n\n        case 3:\n          units.force = \"MN\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.temperature) {\n        case 1:\n          units.temperature = \"degC\";\n          break;\n\n        case 2:\n          units.temperature = \"degF\";\n          break;\n\n        default:\n          break;\n      }\n\n      return units;\n    }\n\n  }\n\n  _exports.Utils = Utils;\n});","define(\"m08-2020/lib old/Utils/BooleanOperations\", [\"exports\", \"three\", \"polybooljs\", \"m08-2020/lib/CSGMesh\"], function (_exports, THREE, _polybooljs, _CSGMesh) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.applyCSG = applyCSG;\n  _exports.doCSG = doCSG;\n  _exports.doSegmentsOperation = doSegmentsOperation;\n  _exports.makeCombinedSegments2D = makeCombinedSegments2D;\n  _exports.makePolygonsFromPosAndNegRegions = makePolygonsFromPosAndNegRegions;\n  const CSG = (0, _CSGMesh.CsgFactory)(THREE);\n\n  function applyCSG(positiveObjects, negativeObjects) {\n    let positiveObject = positiveObjects.splice(0, 1)[0];\n    positiveObjects.forEach(object => positiveObject = doCSG(positiveObject, object, \"union\"));\n    negativeObjects.forEach(object => positiveObject = doCSG(positiveObject, object, \"subtract\"));\n    return positiveObject;\n  }\n\n  function doCSG(a, b, op) {\n    let bspA = CSG.fromMesh(a);\n    let bspB = CSG.fromMesh(b);\n    let bspC = bspA[op](bspB);\n    let result = CSG.toMesh(bspC, new THREE.Matrix4());\n    return result;\n  }\n\n  function makePolygonsFromPosAndNegRegions(regions) {\n    let positives = regions.positiveRegions;\n    let negatives = regions.negativeRegions;\n    let segmentsPos = makeCombinedSegments2D(positives, \"selectUnion\");\n    let segmentsNeg = negatives.length > 0 ? makeCombinedSegments2D(negatives, \"selectUnion\") : null;\n    let finalSegments = segmentsNeg ? doSegmentsOperation(segmentsPos, segmentsNeg, \"selectDifference\") : segmentsPos;\n\n    let finalPolygons = _polybooljs.default.polygon(finalSegments);\n\n    return finalPolygons;\n  }\n\n  function makeCombinedSegments2D(regions, operation) {\n    let segments = _polybooljs.default.segments(regions[0]);\n\n    for (let i = 1; i < regions.length; i++) {\n      let tempSegment = _polybooljs.default.segments(regions[i]);\n\n      segments = doSegmentsOperation(segments, tempSegment, operation);\n    }\n\n    return segments;\n  }\n\n  function doSegmentsOperation(segmentOne, segmentTwo, operation) {\n    let combined = _polybooljs.default.combine(segmentOne, segmentTwo);\n\n    return _polybooljs.default[operation](combined);\n  }\n});","define(\"m08-2020/lib old/Utils/DimensionalChainIntersectionDetection\", [\"exports\", \"three\", \"m08-2020/lib old/Utils\"], function (_exports, THREE, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DimensionalChainIntersectionDetection = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class DimensionalChainIntersectionDetection {\n    constructor() {\n      this.meshArr = []; // [ DoubleFace, FrontFace, BackFace ]\n\n      this.chainArr = []; // [ DoubleFace, FrontFace, BackFace ]\n    }\n\n    checkCollision(linePtsArr, objArr) {\n      let isIntersection = false;\n      let raycaster = new THREE.Raycaster();\n      linePtsArr.forEach(([pointOne, pointTwo]) => {\n        let pointsDistance = pointOne.distanceTo(pointTwo);\n        let intersectionsOne = this.getIntersections(raycaster, [pointOne, pointTwo], objArr);\n        let intersectionsTwo = this.getIntersections(raycaster, [pointTwo, pointOne], objArr);\n        if (this.intersectionDetected(pointsDistance, intersectionsOne, intersectionsTwo)) isIntersection = true;\n      });\n      return isIntersection;\n    }\n\n    intersectionDetected(pointsDistance, intersectionsOne, intersectionsTwo) {\n      let [intersectionsOneDouble, intersectionsOneFront, intersectionsOneBack] = intersectionsOne;\n      let [intersectionsTwoDouble, intersectionsTwoFront, intersectionsTwoBack] = intersectionsTwo;\n      let mutualIntersections = this.getMutualIntersections(intersectionsOneDouble, intersectionsTwoDouble);\n      return mutualIntersections.length !== 1 && this.bothPointsOutsideMesh(mutualIntersections, pointsDistance) || this.onePointInsideMeshOneOut(mutualIntersections, intersectionsOneDouble, intersectionsTwoDouble) || this.bothPointsInMesh(intersectionsOneBack, intersectionsTwoBack, intersectionsOneFront, intersectionsTwoFront);\n    }\n\n    bothPointsOutsideMesh(mutualIntersections, pointsDistance) {\n      let intersectionInMiddle = false;\n      mutualIntersections.forEach(intersection => {\n        if (intersection.distance < pointsDistance) intersectionInMiddle = true;\n      });\n      return intersectionInMiddle;\n    }\n\n    onePointInsideMeshOneOut(mutualIntersections, intersectionsOneDouble, intersectionsTwoDouble) {\n      let pointOneInside = this.pointInsideMesh(intersectionsOneDouble);\n      let pointTwoInside = this.pointInsideMesh(intersectionsTwoDouble);\n      return mutualIntersections.length > 0 && (pointOneInside && !pointTwoInside || !pointOneInside && pointTwoInside);\n    }\n\n    bothPointsInMesh(intersectionsOneBack, intersectionsTwoBack, intersectionsOneFront, intersectionsTwoFront) {\n      return intersectionsOneBack.length > 0 && intersectionsTwoBack.length > 0 && intersectionsOneFront.length === 0 && intersectionsTwoFront.length === 0;\n    }\n\n    getIntersections(raycaster, ptsPair, meshArr) {\n      let [pointOne, pointTwo] = ptsPair;\n      let direction = pointTwo.clone().sub(pointOne).normalize();\n      raycaster.set(pointOne, direction);\n      let intersectionsDouble = [];\n      let intersectionsFront = [];\n      let intersectionsBack = [];\n      const DELTA = 1e-4;\n      meshArr.forEach(([meshDoubleFace, meshFrontFace, meshBackFace]) => {\n        intersectionsDouble.push(...raycaster.intersectObject(meshDoubleFace).filter(intersection => intersection.distance > DELTA));\n        intersectionsFront.push(...raycaster.intersectObject(meshFrontFace).filter(intersection => intersection.distance > DELTA));\n        intersectionsBack.push(...raycaster.intersectObject(meshBackFace).filter(intersection => intersection.distance > DELTA));\n      });\n      intersectionsDouble = this.removeDuplicateIntersections(intersectionsDouble);\n      intersectionsFront = this.removeDuplicateIntersections(intersectionsFront);\n      intersectionsBack = this.removeDuplicateIntersections(intersectionsBack);\n      return [intersectionsDouble, intersectionsFront, intersectionsBack];\n    }\n\n    getMutualIntersections(intersectionsOne, intersectionsTwo) {\n      let intersections = intersectionsOne.filter(intersectionOne => {\n        let isMutual = false;\n        let testPoint = intersectionOne.point;\n        intersectionsTwo.forEach(intersectionTwo => {\n          if (utils.checkVectorsEquality(intersectionTwo.point, testPoint)) isMutual = true;\n        });\n        return isMutual;\n      });\n      return intersections;\n    }\n\n    removeDuplicateIntersections(intersections) {\n      let filterArr = [];\n      intersections.forEach(intersection => {\n        let isUnique = true;\n        filterArr.forEach(([key, _]) => {\n          if (utils.vectorsAreEqual(intersection.point, key)) isUnique = false;\n        });\n        if (isUnique) filterArr.push([intersection.point, intersection]);\n      });\n      intersections = filterArr.map(([_, value]) => value);\n      return intersections;\n    }\n\n    intersectionsSharePoints(intersectionsOne, intersectionsTwo) {\n      let intersectionsSharePoints = false;\n\n      for (let intersectionOne of intersectionsOne) {\n        for (let intersectionTwo of intersectionsTwo) {\n          if (utils.vectorsAreEqual(intersectionOne.point, intersectionTwo.point)) intersectionsSharePoints = true;\n        }\n      }\n\n      return intersectionsSharePoints;\n    }\n\n    pointInsideMesh(intersections) {\n      return intersections.length % 2;\n    }\n\n  }\n\n  _exports.DimensionalChainIntersectionDetection = DimensionalChainIntersectionDetection;\n});","define(\"m08-2020/lib old/Utils/LineIntersectionDetection\", [\"exports\"], function (_exports) {\n  \"use strict\";\n  /**\n   * https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/\n   * Check if Lines intersect 2D\n   */\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.linesIntersect = linesIntersect;\n  _exports.onSegment = onSegment;\n  _exports.orientation = orientation;\n\n  function onSegment(p, q, r) {\n    // p, q and r points are colinear, q lies on line segment pr\n    return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);\n  }\n\n  function orientation(p, q, r) {\n    /**\n     * 0: Colinear\n     * 1: Clockwise\n     * 2: Counterclockwise\n     */\n    let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);\n    if (val > 0) return 1;else if (val < 0) return 2;else return 0;\n  }\n\n  function linesIntersect(p1, q1, p2, q2) {\n    /**\n     * p1, q1 -> first line points\n     * p2, q2 -> second line points\n     */\n    // Get orientations\n    let o1 = orientation(p1, q1, p2);\n    let o2 = orientation(p1, q1, q2);\n    let o3 = orientation(p2, q2, p1);\n    let o4 = orientation(p2, q2, q1); // General Case\n\n    if (o1 !== o2 && o3 !== o4) return true; // Special Cases\n    // p1, q1 and p2 are colinear and p2 lies on segment p1q1\n\n    if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and q2 are colinear and q2 lies on segment p1q1\n\n    if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p2, q2 and p1 are colinear and p1 lies on segment p2q2\n\n    if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and q1 are colinear and q1 lies on segment p2q2\n\n    if (o4 === 0 && onSegment(p2, q1, q2)) return true; // no intersection\n\n    return false;\n  }\n});","define(\"m08-2020/lib old/Utils/PointInsidePolygonCheck2D\", [\"exports\", \"m08-2020/lib old/Utils/LineIntersectionDetection\"], function (_exports, _LineIntersectionDetection) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Point2D = void 0;\n  _exports.pointInsidePolygon2D = pointInsidePolygon2D;\n\n  class Point2D {\n    constructor(x, y) {\n      this.x = x;\n      this.y = y;\n    }\n\n  }\n\n  _exports.Point2D = Point2D;\n\n  function pointInsidePolygon2D(polygon, point) {\n    const INFINITE_VALUE = Number.MAX_VALUE;\n    const N = polygon.length;\n    if (N < 3) return false;\n    let extremePoint = new Point2D(INFINITE_VALUE, point.y);\n    let count = 0;\n\n    for (let i = 0; i < N - 1; i++) {\n      let j = i + 1;\n\n      if ((0, _LineIntersectionDetection.linesIntersect)(polygon[i], polygon[j], point, extremePoint)) {\n        if ((0, _LineIntersectionDetection.orientation)(polygon[i], point, polygon[j]) === 0) return (0, _LineIntersectionDetection.onSegment)(polygon[i], point, polygon[j]);\n        count++;\n      }\n    }\n\n    return count % 2 === 1;\n  }\n});","define(\"m08-2020/lib old/WireframeGenerator\", [\"exports\", \"three\", \"m08-2020/lib old/Utils\", \"m08-2020/lib old/ThreeExtensions/OutlinesGeometry\", \"m08-2020/lib old/Utils/BooleanOperations\"], function (_exports, THREE, _Utils, _OutlinesGeometry, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.WireframeGenerator = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class WireframeGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.areaWireframesData = [];\n      this.volumeWireframesData = {\n        opaque: {\n          positives: [],\n          negatives: []\n        },\n        transparent: {\n          positives: [],\n          negatives: []\n        }\n      };\n    }\n\n    applyWireframesArea(object, objectTransparent, lineProps, objectData) {\n      const RENDER_ORDER = -99;\n      let pointPairs = this.getRegularWireframePointPairs(object);\n      pointPairs.forEach(pair => {\n        let wireframe = this.drawObjectLine(pair, lineProps);\n        wireframe.renderOrder = RENDER_ORDER;\n\n        wireframe.onBeforeRender = function (renderer) {\n          renderer.clearDepth();\n        };\n\n        let wireframeClone = wireframe.clone();\n        wireframeClone.isLine = true;\n        if (wireframe.needsZoomControl) wireframeClone.needsZoomControl = true;\n        if (wireframe.pointsStartEnd) wireframeClone.pointsStartEnd = wireframe.pointsStartEnd;\n        object.add(wireframe);\n        if (objectTransparent) objectTransparent.add(wireframeClone);\n      });\n      object.dxfPoints = [{\n        id: objectData.id,\n        pointPairs: pointPairs.map(pair => pair.map(pt => pt.clone())),\n        lineProps\n      }];\n    }\n\n    applyWireframesVolume(object, objectTransparent, lineProps, objectData) {\n      let pointPairs = [];\n      this.mergeVolumeWireframes(\"opaque\");\n      this.mergeVolumeWireframes(\"transparent\");\n      this.createLinesFromWireframeData(\"opaque\", object, lineProps);\n      this.createLinesFromWireframeData(\"transparent\", objectTransparent, lineProps, pointPairs);\n      this.clearWireframeState();\n      object.dxfPoints = [{\n        id: objectData.id,\n        pointPairs,\n        lineProps\n      }];\n    }\n\n    createLinesFromWireframeData(type, object, lineProps, pointPairs = null) {\n      const RENDER_ORDER = -99;\n      const DELTA = 0.0001;\n      this.volumeWireframesData[type].forEach(({\n        linePoints\n      }) => {\n        linePoints.forEach(([pointOne, pointTwo]) => {\n          if (pointOne.distanceTo(pointTwo) > DELTA) {\n            let line = this.drawObjectLine([pointOne, pointTwo], lineProps);\n            line.renderOrder = RENDER_ORDER;\n\n            line.onBeforeRender = function (renderer) {\n              renderer.clearDepth();\n            };\n\n            if (object) object.add(line);\n            if (pointPairs) pointPairs.push([pointOne.clone(), pointTwo.clone()]);\n          }\n        });\n      });\n    } ///\n\n\n    drawObjectLine(pointPair, lineProps) {\n      let firstLine;\n\n      if (canDrawLine(lineProps)) {\n        if (canDrawSolidLine(lineProps)) firstLine = this.drawSolidLine(pointPair, lineProps, true);else if (canDrawDashedLine(lineProps)) firstLine = this.drawDashedLine(pointPair, lineProps);else firstLine = utils.drawConnectedLines(pointPair, lineProps);\n      } else firstLine = new THREE.Mesh();\n\n      firstLine.isLine = true;\n      firstLine.pointsStartEnd = {\n        startPoint: pointPair[0],\n        endPoint: pointPair[1]\n      }; // Helper functions -->\n\n      function canDrawLine(lineProps) {\n        return lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined);\n      }\n\n      function canDrawSolidLine(lineProps) {\n        return lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0;\n      }\n\n      function canDrawDashedLine(lineProps) {\n        return lineProps.type === 2;\n      } // <-- Helper functions\n\n\n      return firstLine;\n    }\n\n    drawSolidLine(points, lineProps) {\n      let lineMesh;\n\n      if (points[0].distanceTo(points[1]) > 0) {\n        lineMesh = utils.drawLine(points[0], points[1], lineProps, true);\n      } else {\n        lineMesh = new THREE.Mesh();\n      }\n\n      lineMesh.needsZoomControl = true;\n      return lineMesh;\n    }\n\n    drawDashedLine(pointPair, lineProps) {\n      let localPoints = pointPair.map(pt => pt.clone().sub(pointPair[0]));\n      let factor = lineProps.factor;\n      let q = new THREE.Quaternion().setFromUnitVectors(localPoints[1].clone().normalize(), new THREE.Vector3(0, 0, 1));\n      localPoints[1].applyQuaternion(q);\n      let lineSegments = this.getDashedLineSegments(localPoints[0], localPoints[1], factor);\n      let lineMesh = this.drawDashedLineFromSegments(lineSegments, lineProps);\n      lineMesh.position.add(pointPair[0]);\n      lineMesh.lookAt(pointPair[1]);\n      lineMesh.needsZoomControl = true;\n      return lineMesh;\n    }\n\n    getDashedLineSegments(pointOne, pointTwo, factor) {\n      let pointPairs = [];\n      let newSegment = true;\n      let direction = new THREE.Vector3().subVectors(pointTwo, pointOne).normalize();\n      let distance = pointOne.distanceTo(pointTwo);\n      let segmentSize = factor;\n\n      if (distance > factor) {\n        while (distance > segmentSize) {\n          let newPoint = new THREE.Vector3().copy(pointOne).add(direction.clone().multiplyScalar(segmentSize));\n          if (newSegment) pointPairs.push([pointOne, newPoint]);\n          pointOne = newPoint;\n          distance -= segmentSize;\n          newSegment = !newSegment;\n        }\n      } else pointPairs.push([pointOne, pointTwo]);\n\n      return pointPairs;\n    }\n\n    drawDashedLineFromSegments(lineSegments, lineProps) {\n      let lineMeshArray = [];\n      lineSegments.forEach(pair => {\n        if (pair[0].distanceTo(pair[1]) > 0) {\n          let lineMesh = utils.drawLine(pair[0], pair[1], lineProps);\n          lineMeshArray.push(lineMesh);\n        }\n      });\n      let firstLine = lineMeshArray.splice(0, 1)[0];\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    } ///\n\n\n    mergeVolumeWireframes(type) {\n      const DELTA = 0.0001;\n      let raycaster = new THREE.Raycaster();\n      let positives = this.mergeSameTypeWireframe(raycaster, this.volumeWireframesData[type], \"positives\");\n      let negatives = this.mergeSameTypeWireframe(raycaster, this.volumeWireframesData[type], \"negatives\");\n      if (positives.mesh) positives.mesh.material.side = THREE.DoubleSide;\n      if (negatives.mesh) negatives.mesh.material.side = THREE.DoubleSide;\n      this.volumeWireframesData[type] = [positives, negatives];\n\n      for (let objectData of this.volumeWireframesData[type]) {\n        let id = objectData.id;\n        let mesh = objectData.mesh;\n        let objectType = objectData.objectType;\n        this.volumeWireframesData[type].filter(obj => id !== obj.id).map(obj => obj.mesh).forEach(objMesh => {\n          let newPoints = [];\n          objectData.linePoints.forEach(([pointOne, pointTwo]) => {\n            pointOne.applyMatrix4(mesh.matrix);\n            pointTwo.applyMatrix4(mesh.matrix);\n            let firstPointIntersection = this.pointInsideMesh(raycaster, pointOne, pointTwo, objMesh);\n            let secondPointIntersection = this.pointInsideMesh(raycaster, pointTwo, pointOne, objMesh);\n            let firstPointData = {\n              point: pointOne,\n              intersection: firstPointIntersection\n            };\n            let secondPointData = {\n              point: pointTwo,\n              intersection: secondPointIntersection\n            };\n            let operation = objectType === 1 ? \"AddPoints\" : \"SubtractPoints\";\n            let correctedPointPairs = this.getCorrectedLinePoints(firstPointData, secondPointData, operation);\n            correctedPointPairs.forEach(pair => {\n              if (pair[0].distanceTo(pair[1]) > DELTA) {\n                newPoints.push([pair[0], pair[1]]);\n              }\n            });\n          });\n          objectData.linePoints = newPoints;\n        });\n      }\n    }\n\n    mergeSameTypeWireframe(raycaster, volumeWireframes, type) {\n      let mergedWireframes = volumeWireframes[type];\n\n      for (let i = 0; i < mergedWireframes.length; i++) {\n        let objectData = mergedWireframes[i];\n        let id = objectData.id;\n        let mesh = objectData.mesh;\n        mergedWireframes.filter(obj => id !== obj.id).map(obj => obj.mesh).forEach(objMesh => {\n          let newPoints = [];\n          objectData.linePoints.forEach(([pointOne, pointTwo]) => {\n            pointOne.applyMatrix4(mesh.matrix);\n            pointTwo.applyMatrix4(mesh.matrix);\n            let firstPointIntersection = this.pointInsideMesh(raycaster, pointOne, pointTwo, objMesh);\n            let secondPointIntersection = this.pointInsideMesh(raycaster, pointTwo, pointOne, objMesh);\n            let firstPointData = {\n              point: pointOne,\n              intersection: firstPointIntersection\n            };\n            let secondPointData = {\n              point: pointTwo,\n              intersection: secondPointIntersection\n            };\n            let correctedPointPairs = this.getCorrectedLinePoints(firstPointData, secondPointData, \"AddPoints\");\n            correctedPointPairs.forEach(pair => {\n              if (pair[0].distanceTo(pair[1]) > 0.0001) {\n                newPoints.push([pair[0], pair[1]]);\n              }\n            });\n          });\n          objectData.linePoints = newPoints;\n        });\n      }\n\n      mergedWireframes = mergedWireframes.reduce((allPos, currentPos) => {\n        allPos.id = currentPos.id;\n        allPos.objectType = currentPos.objectType;\n        allPos.linePoints.push(...currentPos.linePoints);\n        allPos.mesh = allPos.mesh ? BooleanOperations.doCSG(allPos.mesh, currentPos.mesh, \"union\") : currentPos.mesh;\n        return allPos;\n      }, {\n        linePoints: []\n      });\n      return mergedWireframes;\n    }\n\n    getCorrectedLinePoints(firstPtIntersect, secondPtIntersect, operationType) {\n      let pointOne = firstPtIntersect.point;\n      let firstPointIntersection = firstPtIntersect.intersection;\n      let pointTwo = secondPtIntersect.point;\n      let secondPointIntersection = secondPtIntersect.intersection;\n      let linePairs = [];\n\n      if (operationType === \"AddPoints\") {\n        if (!firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh) {\n          if (firstPointIntersection.intersectionPoint && secondPointIntersection.intersectionPoint) {\n            let lenPointToPoint = pointOne.distanceTo(pointTwo);\n            let lenPointOneToIntersect = pointOne.distanceTo(firstPointIntersection.intersectionPoint);\n            let lenPointTwoToIntersect = pointTwo.distanceTo(secondPointIntersection.intersectionPoint);\n\n            if (lenPointToPoint > lenPointOneToIntersect && lenPointToPoint > lenPointTwoToIntersect) {\n              linePairs.push([pointOne, firstPointIntersection.intersectionPoint]);\n              linePairs.push([pointTwo, secondPointIntersection.intersectionPoint]);\n            } else {\n              linePairs.push([pointOne, pointTwo]);\n            }\n          } else {\n            linePairs.push([pointOne, pointTwo]);\n          }\n        } else if (!firstPointIntersection.isInMesh && secondPointIntersection.isInMesh) {\n          if (firstPointIntersection.intersectionPoint) linePairs.push([pointOne, secondPointIntersection.intersectionPoint]);\n        } else if (firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh) {\n          if (secondPointIntersection.intersectionPoint) linePairs.push([pointTwo, firstPointIntersection.intersectionPoint]);\n        }\n      } else if (operationType === \"SubtractPoints\") {\n        if (firstPointIntersection.isInMesh && secondPointIntersection.isInMesh) {\n          linePairs.push([pointOne, pointTwo]);\n        } // else if ( !firstPointIntersection.isInMesh && secondPointIntersection.isInMesh && firstPointIntersection.intersectionPoint )\n        // {\n        //   linePairs.push([ pointTwo, secondPointIntersection.intersectionPoint ]);\n        // }\n        // else if ( firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh && secondPointIntersection.intersectionPoint )\n        // {\n        //   linePairs.push([ pointOne, firstPointIntersection.intersectionPoint ]);\n        // }\n\n      }\n\n      return linePairs;\n    }\n\n    pointInsideMesh(raycaster, pointOne, pointTwo, object) {\n      if (!object) {\n        return {\n          isInMesh: false,\n          intersectionPoint: null\n        };\n      }\n\n      let direction = pointTwo.clone().sub(pointOne).normalize();\n      raycaster.set(pointOne, direction);\n      let intersects = raycaster.intersectObject(object);\n\n      for (let i = 0; i < intersects.length; i++) {\n        for (let j = 0; j < intersects.length; j++) {\n          if (i != j && Math.abs(intersects[i].distance - intersects[j].distance) < 0.1) {\n            intersects[i].markedToDelete = true && !intersects[j].markedToDelete;\n          }\n        }\n      }\n\n      intersects = intersects.filter(intersect => !intersect.markedToDelete);\n      let intersectionData = {\n        isInMesh: intersects.length % 2 ? true : false,\n        intersectionPoint: intersects[0] ? intersects[0].point : null\n      };\n      return intersectionData;\n    }\n\n    getRegularWireframePointPairs(object, threshold = Math.PI / 6) {\n      let edgesGeo = new _OutlinesGeometry.OutlinesGeometry(object.geometry);\n      let pointPairs = [];\n      this.getWireframeLinePairs(edgesGeo, threshold).forEach(([_, pairs]) => pairs.forEach(pair => pointPairs.push(pair)));\n      return pointPairs;\n    }\n\n    getWireframeLinePairs(edgesGeo, threshold = Math.PI / 6) {\n      const DELTA = 0.0001;\n      let facesDict = this.getFacesDict(edgesGeo.faces);\n      let linePairs = [];\n\n      for (let [edgeIndices, facePair] of Object.entries(facesDict)) {\n        if (facePair.length === 2) {\n          let faceOne = facePair[0];\n          let faceTwo = facePair[1];\n\n          if (faceOne.normal.angleTo(faceTwo.normal) > threshold) {\n            let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n            let vectorOne = edgesGeo.vertices[verticesIdx[0]];\n            let vectorTwo = edgesGeo.vertices[verticesIdx[1]];\n            let direction = new THREE.Vector3().subVectors(vectorTwo, vectorOne).normalize();\n            let needToAdd = false;\n\n            for (let k = 0; k < linePairs.length; k++) {\n              needToAdd = false;\n              let vectorData = linePairs[k];\n              let dir = vectorData[0];\n              let vectorsArr = vectorData[1];\n              let dirDot = dir.dot(direction);\n\n              if (dirDot > 1 - DELTA) {\n                vectorsArr.push([vectorOne, vectorTwo]);\n                k = linePairs.length;\n              } else if (dirDot < -1 + DELTA) {\n                vectorsArr.push([vectorTwo, vectorOne]);\n                k = linePairs.length;\n              } else needToAdd = true;\n            }\n\n            if (linePairs.length === 0) needToAdd = true;\n            if (needToAdd) linePairs.push([direction, [[vectorOne, vectorTwo]]]);\n          }\n        } else {\n          let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n          let vectorOne = edgesGeo.vertices[verticesIdx[0]];\n          let vectorTwo = edgesGeo.vertices[verticesIdx[1]];\n          let direction = new THREE.Vector3().subVectors(vectorTwo, vectorOne).normalize();\n          let needToAdd = false;\n\n          for (let k = 0; k < linePairs.length; k++) {\n            needToAdd = false;\n            let vectorData = linePairs[k];\n            let dir = vectorData[0];\n            let vectorsArr = vectorData[1];\n            let dirDot = dir.dot(direction);\n\n            if (dirDot > 1 - DELTA) {\n              vectorsArr.push([vectorOne, vectorTwo]);\n              k = linePairs.length;\n            } else if (dirDot < -1 + DELTA) {\n              vectorsArr.push([vectorTwo, vectorOne]);\n              k = linePairs.length;\n            } else needToAdd = true;\n          }\n\n          if (linePairs.length === 0) needToAdd = true;\n          if (needToAdd) linePairs.push([direction, [[vectorOne, vectorTwo]]]);\n        }\n      }\n\n      this.mergeLinePairsSameDirection(linePairs);\n      return linePairs;\n    }\n\n    getFacesDict(faces) {\n      let facesDict = {};\n\n      for (let face of faces) {\n        if (face.splitted) continue;\n        let edgeOne = `${face.a},${face.b}`;\n        let edgeTwo = `${face.b},${face.c}`;\n        let edgeThree = `${face.c},${face.a}`;\n        let edgeOneReversed = `${face.b},${face.a}`;\n        let edgeTwoReversed = `${face.c},${face.b}`;\n        let edgeThreeReversed = `${face.a},${face.c}`;\n        if (edgeOneReversed in facesDict) facesDict[edgeOneReversed].push(face);else if (edgeOne in facesDict) facesDict[edgeOne].push(face);else {\n          facesDict[edgeOne] = [];\n          facesDict[edgeOne].push(face);\n        }\n        if (edgeTwoReversed in facesDict) facesDict[edgeTwoReversed].push(face);else if (edgeTwo in facesDict) facesDict[edgeTwo].push(face);else {\n          facesDict[edgeTwo] = [];\n          facesDict[edgeTwo].push(face);\n        }\n        if (edgeThreeReversed in facesDict) facesDict[edgeThreeReversed].push(face);else if (edgeThree in facesDict) facesDict[edgeThree].push(face);else {\n          facesDict[edgeThree] = [];\n          facesDict[edgeThree].push(face);\n        }\n      }\n\n      return facesDict;\n    }\n\n    mergeLinePairsSameDirection(linePairs) {\n      linePairs.forEach(arrData => this.makeLinePairsUnique(arrData));\n      linePairs.forEach(([direction, pairs]) => utils.mergeLinePoints(pairs, direction));\n    }\n\n    makeLinePairsUnique(arrData) {\n      // [ 0: Direction, 1: Pairs array ]\n      let vectorPairsArr = arrData[1];\n      let uniquePairs = [];\n      vectorPairsArr.forEach(pair => {\n        let isUnique = true;\n        uniquePairs.forEach(uniquePair => {\n          if (pair[0].equals(uniquePair[0]) && pair[1].equals(uniquePair[1])) isUnique = false;\n        });\n        if (isUnique) uniquePairs.push(pair);\n      });\n      arrData[1] = uniquePairs;\n    }\n\n    getVolumeWireframeData(mesh, linePairsForOpaque, linePairsForTransparent, objectType) {\n      let intersectionGeo = mesh.geometry.clone(); // For checking if line intersects faces\n\n      intersectionGeo.applyMatrix4(mesh.matrix);\n      let intersectionMesh = new THREE.Mesh(intersectionGeo, new THREE.MeshBasicMaterial({\n        side: THREE.DoubleSide\n      }));\n\n      if (objectType === 1) {\n        this.volumeWireframesData.opaque.positives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForOpaque,\n          mesh: intersectionMesh,\n          objectType\n        });\n        this.volumeWireframesData.transparent.positives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForTransparent,\n          mesh: intersectionMesh,\n          objectType\n        });\n      } else if (objectType === 2) {\n        this.volumeWireframesData.opaque.negatives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForOpaque,\n          mesh: intersectionMesh,\n          objectType\n        });\n        this.volumeWireframesData.transparent.negatives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForTransparent,\n          mesh: intersectionMesh,\n          objectType\n        });\n      }\n    }\n\n    clearWireframeState() {\n      this.areaWireframesData = [];\n      this.volumeWireframesData = {\n        opaque: {\n          positives: [],\n          negatives: []\n        },\n        transparent: {\n          positives: [],\n          negatives: []\n        }\n      };\n    }\n\n  }\n\n  _exports.WireframeGenerator = WireframeGenerator;\n});","define(\"m08-2020/lib old/ZoomController\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ZoomController = void 0;\n\n  class ZoomController {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.controls = GraphicsThree3D.controls;\n      this.camera = GraphicsThree3D.camera;\n      this.drawingObjectWireframesSolid = []; ///\n\n      this.controls.addEventListener(\"change\", this.zoomDrawingObjectSolidLines()); ///\n    }\n\n    zoomDrawingObjectSolidLines() {\n      return () => {\n        let startingRotation = new THREE.Euler(),\n            startingPosition = new THREE.Vector3();\n        let scaleFactor = 1 / this.camera.zoom;\n        this.drawingObjectWireframesSolid.forEach(lineData => {\n          if (this.camera.zoom < 1) {\n            startingRotation.copy(lineData.line.rotation);\n            startingPosition.copy(lineData.line.position);\n            lineData.line.position.sub(lineData.line.position);\n            lineData.line.setRotationFromEuler(lineData.eulerOne);\n            lineData.line.scale.set(scaleFactor, scaleFactor, 1);\n            lineData.line.setRotationFromEuler(startingRotation);\n            lineData.line.position.add(startingPosition);\n          }\n        });\n      };\n    }\n\n    getDrawingObjectWireframes(object) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      object.children.forEach(child => {\n        // TODO: Add support for buffergeometries\n        if (child.isLine && child.needsZoomControl) {\n          let ptOne = child.pointsStartEnd.startPoint;\n          let ptTwo = child.pointsStartEnd.endPoint;\n          let lineDirection = new THREE.Vector3().copy(ptTwo).sub(ptOne).normalize();\n          let quaternion = new THREE.Quaternion().setFromUnitVectors(lineDirection, Z_AXIS);\n          let quaternionConjugate = new THREE.Quaternion().setFromUnitVectors(Z_AXIS, lineDirection);\n          let eulerOne = new THREE.Euler().setFromQuaternion(quaternion);\n          let eulerTwo = new THREE.Euler().setFromQuaternion(quaternionConjugate);\n          this.drawingObjectWireframesSolid.push({\n            line: child,\n            eulerOne,\n            eulerTwo\n          });\n        }\n      });\n    }\n\n    drawingObjectWireframes() {\n      this.drawingObjectWireframesSolid = [];\n    }\n\n  }\n\n  _exports.ZoomController = ZoomController;\n});","define(\"m08-2020/lib old/dxf_colors\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.COLOR_LOOKUP = void 0;\n  const COLOR_LOOKUP = [[0, 0, 0], [255, 0, 0], [255, 255, 0], [0, 255, 0], [0, 255, 255], [0, 0, 255], [255, 0, 255], [255, 255, 255], [128, 128, 128], [192, 192, 192], [255, 0, 0], [255, 127, 127], [204, 0, 0], [204, 102, 102], [153, 0, 0], [153, 76, 76], [127, 0, 0], [127, 63, 63], [76, 0, 0], [76, 38, 38], [255, 63, 0], [255, 159, 127], [204, 51, 0], [204, 127, 102], [153, 38, 0], [153, 95, 76], [127, 31, 0], [127, 79, 63], [76, 19, 0], [76, 47, 38], [255, 127, 0], [255, 191, 127], [204, 102, 0], [204, 153, 102], [153, 76, 0], [153, 114, 76], [127, 63, 0], [127, 95, 63], [76, 38, 0], [76, 57, 38], [255, 191, 0], [255, 223, 127], [204, 153, 0], [204, 178, 102], [153, 114, 0], [153, 133, 76], [127, 95, 0], [127, 111, 63], [76, 57, 0], [76, 66, 38], [255, 255, 0], [255, 255, 127], [204, 204, 0], [204, 204, 102], [153, 153, 0], [153, 153, 76], [127, 127, 0], [127, 127, 63], [76, 76, 0], [76, 76, 38], [191, 255, 0], [223, 255, 127], [153, 204, 0], [178, 204, 102], [114, 153, 0], [133, 153, 76], [95, 127, 0], [111, 127, 63], [57, 76, 0], [66, 76, 38], [127, 255, 0], [191, 255, 127], [102, 204, 0], [153, 204, 102], [76, 153, 0], [114, 153, 76], [63, 127, 0], [95, 127, 63], [38, 76, 0], [57, 76, 38], [63, 255, 0], [159, 255, 127], [51, 204, 0], [127, 204, 102], [38, 153, 0], [95, 153, 76], [31, 127, 0], [79, 127, 63], [19, 76, 0], [47, 76, 38], [0, 255, 0], [127, 255, 127], [0, 204, 0], [102, 204, 102], [0, 153, 0], [76, 153, 76], [0, 127, 0], [63, 127, 63], [0, 76, 0], [38, 76, 38], [0, 255, 63], [127, 255, 159], [0, 204, 51], [102, 204, 127], [0, 153, 38], [76, 153, 95], [0, 127, 31], [63, 127, 79], [0, 76, 19], [38, 76, 47], [0, 255, 127], [127, 255, 191], [0, 204, 102], [102, 204, 153], [0, 153, 76], [76, 153, 114], [0, 127, 63], [63, 127, 95], [0, 76, 38], [38, 76, 57], [0, 255, 191], [127, 255, 223], [0, 204, 153], [102, 204, 178], [0, 153, 114], [76, 153, 133], [0, 127, 95], [63, 127, 111], [0, 76, 57], [38, 76, 66], [0, 255, 255], [127, 255, 255], [0, 204, 204], [102, 204, 204], [0, 153, 153], [76, 153, 153], [0, 127, 127], [63, 127, 127], [0, 76, 76], [38, 76, 76], [0, 191, 255], [127, 223, 255], [0, 153, 204], [102, 178, 204], [0, 114, 153], [76, 133, 153], [0, 95, 127], [63, 111, 127], [0, 57, 76], [38, 66, 76], [0, 127, 255], [127, 191, 255], [0, 102, 204], [102, 153, 204], [0, 76, 153], [76, 114, 153], [0, 63, 127], [63, 95, 127], [0, 38, 76], [38, 57, 76], [0, 63, 255], [127, 159, 255], [0, 51, 204], [102, 127, 204], [0, 38, 153], [76, 95, 153], [0, 31, 127], [63, 79, 127], [0, 19, 76], [38, 47, 76], [0, 0, 255], [127, 127, 255], [0, 0, 204], [102, 102, 204], [0, 0, 153], [76, 76, 153], [0, 0, 127], [63, 63, 127], [0, 0, 76], [38, 38, 76], [63, 0, 255], [159, 127, 255], [51, 0, 204], [127, 102, 204], [38, 0, 153], [95, 76, 153], [31, 0, 127], [79, 63, 127], [19, 0, 76], [47, 38, 76], [127, 0, 255], [191, 127, 255], [102, 0, 204], [153, 102, 204], [76, 0, 153], [114, 76, 153], [63, 0, 127], [95, 63, 127], [38, 0, 76], [57, 38, 76], [191, 0, 255], [223, 127, 255], [153, 0, 204], [178, 102, 204], [114, 0, 153], [133, 76, 153], [95, 0, 127], [111, 63, 127], [57, 0, 76], [66, 38, 76], [255, 0, 255], [255, 127, 255], [204, 0, 204], [204, 102, 204], [153, 0, 153], [153, 76, 153], [127, 0, 127], [127, 63, 127], [76, 0, 76], [76, 38, 76], [255, 0, 191], [255, 127, 223], [204, 0, 153], [204, 102, 178], [153, 0, 114], [153, 76, 133], [127, 0, 95], [127, 63, 111], [76, 0, 57], [76, 38, 66], [255, 0, 127], [255, 127, 191], [204, 0, 102], [204, 102, 153], [153, 0, 76], [153, 76, 114], [127, 0, 63], [127, 63, 95], [76, 0, 38], [76, 38, 57], [255, 0, 63], [255, 127, 159], [204, 0, 51], [204, 102, 127], [153, 0, 38], [153, 76, 95], [127, 0, 31], [127, 63, 79], [76, 0, 19], [76, 38, 47], [51, 51, 51], [91, 91, 91], [132, 132, 132], [173, 173, 173], [214, 214, 214], [255, 255, 255]];\n  _exports.COLOR_LOOKUP = COLOR_LOOKUP;\n});","define(\"m08-2020/lib/CSGMesh\", [\"exports\"], function (_exports) {\n  \"use strict\"; //https://github.com/manthrax/THREE-CSGMesh\n  // ## License\n  //\n  // Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.\n  // THREE.js rework by thrax\n  // # class CSG\n  // Holds a binary space partition tree representing a 3D solid. Two solids can\n  // be combined using the `union()`, `subtract()`, and `intersect()` methods.\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.CsgFactory = CsgFactory;\n\n  function CsgFactory(THREE) {\n    class CSG {\n      constructor() {\n        this.polygons = [];\n      }\n\n      clone() {\n        var csg = new CSG();\n        csg.polygons = this.polygons.map(function (p) {\n          return p.clone();\n        });\n        return csg;\n      }\n\n      toPolygons() {\n        return this.polygons;\n      }\n\n      union(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.clipTo(b);\n        b.clipTo(a);\n        b.invert();\n        b.clipTo(a);\n        b.invert();\n        a.build(b.allPolygons());\n        return CSG.fromPolygons(a.allPolygons());\n      }\n\n      subtract(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.invert();\n        a.clipTo(b);\n        b.clipTo(a);\n        b.invert();\n        b.clipTo(a);\n        b.invert();\n        a.build(b.allPolygons());\n        a.invert();\n        return CSG.fromPolygons(a.allPolygons());\n      }\n\n      intersect(csg) {\n        var a = new Node(this.clone().polygons);\n        var b = new Node(csg.clone().polygons);\n        a.invert();\n        b.clipTo(a);\n        b.invert();\n        a.clipTo(b);\n        b.clipTo(a);\n        a.build(b.allPolygons());\n        a.invert();\n        return CSG.fromPolygons(a.allPolygons());\n      } // Return a new CSG solid with solid and empty space switched. This solid is\n      // not modified.\n\n\n      inverse() {\n        var csg = this.clone();\n        csg.polygons.map(function (p) {\n          p.flip();\n        });\n        return csg;\n      }\n\n    } // Construct a CSG solid from a list of `Polygon` instances.\n\n\n    CSG.fromPolygons = function (polygons) {\n      var csg = new CSG();\n      csg.polygons = polygons;\n      return csg;\n    }; // # class Vector\n    // Represents a 3D vector.\n    //\n    // Example usage:\n    //\n    //     new CSG.Vector(1, 2, 3);\n    //     new CSG.Vector([1, 2, 3]);\n    //     new CSG.Vector({ x: 1, y: 2, z: 3 });\n\n\n    class Vector extends THREE.Vector3 {\n      constructor(x, y, z) {\n        if (arguments.length == 3) super(x, y, z);else if (Array.isArray(x)) super(x[0], x[1], x[2]);else if (typeof x == 'object') super().copy(x);else throw \"Invalid constructor to vector\";\n      }\n\n      clone() {\n        return new Vector(this);\n      }\n\n      negated() {\n        return this.clone().multiplyScalar(-1);\n      }\n\n      plus(a) {\n        return this.clone().add(a);\n      }\n\n      minus(a) {\n        return this.clone().sub(a);\n      }\n\n      times(a) {\n        return this.clone().multiplyScalar(a);\n      }\n\n      dividedBy(a) {\n        return this.clone().divideScalar(a);\n      }\n\n      lerp(a, t) {\n        return this.plus(a.minus(this).times(t));\n      }\n\n      unit() {\n        return this.dividedBy(this.length());\n      }\n\n      cross(a) {\n        return THREE.Vector3.prototype.cross.call(this.clone(), a);\n      }\n\n    } // # class Vertex\n    // Represents a vertex of a polygon. Use your own vertex class instead of this\n    // one to provide additional features like texture coordinates and vertex\n    // colors. Custom vertex classes need to provide a `pos` property and `clone()`,\n    // `flip()`, and `interpolate()` methods that behave analogous to the ones\n    // defined by `CSG.Vertex`. This class provides `normal` so convenience\n    // functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`\n    // is not used anywhere else.\n\n\n    class Vertex {\n      constructor(pos, normal, uv) {\n        this.pos = new Vector(pos);\n        this.normal = new Vector(normal);\n        this.uv = new Vector(uv);\n      }\n\n      clone() {\n        return new Vertex(this.pos.clone(), this.normal.clone(), this.uv.clone());\n      } // Invert all orientation-specific data (e.g. vertex normal). Called when the\n      // orientation of a polygon is flipped.\n\n\n      flip() {\n        this.normal = this.normal.negated();\n      } // Create a new vertex between this vertex and `other` by linearly\n      // interpolating all properties using a parameter of `t`. Subclasses should\n      // override this to interpolate additional properties.\n\n\n      interpolate(other, t) {\n        return new Vertex(this.pos.lerp(other.pos, t), this.normal.lerp(other.normal, t), this.uv.lerp(other.uv, t));\n      }\n\n    }\n\n    ; // # class Plane\n    // Represents a plane in 3D space.\n\n    class Plane {\n      constructor(normal, w) {\n        this.normal = normal;\n        this.w = w;\n      }\n\n      clone() {\n        return new Plane(this.normal.clone(), this.w);\n      }\n\n      flip() {\n        this.normal = this.normal.negated();\n        this.w = -this.w;\n      } // Split `polygon` by this plane if needed, then put the polygon or polygon\n      // fragments in the appropriate lists. Coplanar polygons go into either\n      // `coplanarFront` or `coplanarBack` depending on their orientation with\n      // respect to this plane. Polygons in front or in back of this plane go into\n      // either `front` or `back`.\n\n\n      splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {\n        var COPLANAR = 0;\n        var FRONT = 1;\n        var BACK = 2;\n        var SPANNING = 3; // Classify each point as well as the entire polygon into one of the above\n        // four classes.\n\n        var polygonType = 0;\n        var types = [];\n\n        for (var i = 0; i < polygon.vertices.length; i++) {\n          var t = this.normal.dot(polygon.vertices[i].pos) - this.w;\n          var type = t < -Plane.EPSILON ? BACK : t > Plane.EPSILON ? FRONT : COPLANAR;\n          polygonType |= type;\n          types.push(type);\n        } // Put the polygon in the correct list, splitting it when necessary.\n\n\n        switch (polygonType) {\n          case COPLANAR:\n            (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);\n            break;\n\n          case FRONT:\n            front.push(polygon);\n            break;\n\n          case BACK:\n            back.push(polygon);\n            break;\n\n          case SPANNING:\n            var f = [],\n                b = [];\n\n            for (var i = 0; i < polygon.vertices.length; i++) {\n              var j = (i + 1) % polygon.vertices.length;\n              var ti = types[i],\n                  tj = types[j];\n              var vi = polygon.vertices[i],\n                  vj = polygon.vertices[j];\n              if (ti != BACK) f.push(vi);\n              if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi);\n\n              if ((ti | tj) == SPANNING) {\n                var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos));\n                var v = vi.interpolate(vj, t);\n                f.push(v);\n                b.push(v.clone());\n              }\n            }\n\n            if (f.length >= 3) front.push(new Polygon(f, polygon.shared));\n            if (b.length >= 3) back.push(new Polygon(b, polygon.shared));\n            break;\n        }\n      }\n\n    } // `Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a\n    // point is on the plane.\n\n\n    Plane.EPSILON = 1e-5;\n\n    Plane.fromPoints = function (a, b, c) {\n      var n = b.minus(a).cross(c.minus(a)).unit();\n      return new Plane(n, n.dot(a));\n    }; // # class Polygon\n    // Represents a convex polygon. The vertices used to initialize a polygon must\n    // be coplanar and form a convex loop. They do not have to be `Vertex`\n    // instances but they must behave similarly (duck typing can be used for\n    // customization).\n    //\n    // Each convex polygon has a `shared` property, which is shared between all\n    // polygons that are clones of each other or were split from the same polygon.\n    // This can be used to define per-polygon properties (such as surface color).\n\n\n    class Polygon {\n      constructor(vertices, shared) {\n        this.vertices = vertices;\n        this.shared = shared;\n        this.plane = Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);\n      }\n\n      clone() {\n        var vertices = this.vertices.map(function (v) {\n          return v.clone();\n        });\n        return new Polygon(vertices, this.shared);\n      }\n\n      flip() {\n        this.vertices.reverse().map(function (v) {\n          v.flip();\n        });\n        this.plane.flip();\n      }\n\n    } // # class Node\n    // Holds a node in a BSP tree. A BSP tree is built from a collection of polygons\n    // by picking a polygon to split along. That polygon (and all other coplanar\n    // polygons) are added directly to that node and the other polygons are added to\n    // the front and/or back subtrees. This is not a leafy BSP tree since there is\n    // no distinction between internal and leaf nodes.\n\n\n    class Node {\n      constructor(polygons) {\n        this.plane = null;\n        this.front = null;\n        this.back = null;\n        this.polygons = [];\n        if (polygons) this.build(polygons);\n      }\n\n      clone() {\n        var node = new Node();\n        node.plane = this.plane && this.plane.clone();\n        node.front = this.front && this.front.clone();\n        node.back = this.back && this.back.clone();\n        node.polygons = this.polygons.map(function (p) {\n          return p.clone();\n        });\n        return node;\n      } // Convert solid space to empty space and empty space to solid space.\n\n\n      invert() {\n        for (var i = 0; i < this.polygons.length; i++) this.polygons[i].flip();\n\n        this.plane.flip();\n        if (this.front) this.front.invert();\n        if (this.back) this.back.invert();\n        var temp = this.front;\n        this.front = this.back;\n        this.back = temp;\n      } // Recursively remove all polygons in `polygons` that are inside this BSP\n      // tree.\n\n\n      clipPolygons(polygons) {\n        if (!this.plane) return polygons.slice();\n        var front = [],\n            back = [];\n\n        for (var i = 0; i < polygons.length; i++) {\n          this.plane.splitPolygon(polygons[i], front, back, front, back);\n        }\n\n        if (this.front) front = this.front.clipPolygons(front);\n        if (this.back) back = this.back.clipPolygons(back);else back = [];\n        return front.concat(back);\n      } // Remove all polygons in this BSP tree that are inside the other BSP tree\n      // `bsp`.\n\n\n      clipTo(bsp) {\n        this.polygons = bsp.clipPolygons(this.polygons);\n        if (this.front) this.front.clipTo(bsp);\n        if (this.back) this.back.clipTo(bsp);\n      } // Return a list of all polygons in this BSP tree.\n\n\n      allPolygons() {\n        var polygons = this.polygons.slice();\n        if (this.front) polygons = polygons.concat(this.front.allPolygons());\n        if (this.back) polygons = polygons.concat(this.back.allPolygons());\n        return polygons;\n      } // Build a BSP tree out of `polygons`. When called on an existing tree, the\n      // new polygons are filtered down to the bottom of the tree and become new\n      // nodes there. Each set of polygons is partitioned using the first polygon\n      // (no heuristic is used to pick a good split).\n\n\n      build(polygons) {\n        if (!polygons.length) return;\n        if (!this.plane) this.plane = polygons[0].plane.clone();\n        var front = [],\n            back = [];\n\n        for (var i = 0; i < polygons.length; i++) {\n          this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n        }\n\n        if (front.length) {\n          if (!this.front) this.front = new Node();\n          this.front.build(front);\n        }\n\n        if (back.length) {\n          if (!this.back) this.back = new Node();\n          this.back.build(back);\n        }\n      }\n\n    }\n\n    CSG.fromGeometry = function (geom) {\n      if (geom.isBufferGeometry) geom = new THREE.Geometry().fromBufferGeometry(geom);\n      var fs = geom.faces;\n      var vs = geom.vertices;\n      var polys = [];\n      var fm = ['a', 'b', 'c'];\n\n      for (var i = 0; i < fs.length; i++) {\n        var f = fs[i];\n        var vertices = [];\n\n        for (var j = 0; j < 3; j++) vertices.push(new Vertex(vs[f[fm[j]]], f.vertexNormals[j], geom.faceVertexUvs[0][i][j]));\n\n        polys.push(new Polygon(vertices));\n      }\n\n      return CSG.fromPolygons(polys);\n    };\n\n    CSG._tmpm3 = new THREE.Matrix3();\n\n    CSG.fromMesh = function (mesh) {\n      var csg = CSG.fromGeometry(mesh.geometry);\n\n      CSG._tmpm3.getNormalMatrix(mesh.matrix);\n\n      for (var i = 0; i < csg.polygons.length; i++) {\n        var p = csg.polygons[i];\n\n        for (var j = 0; j < p.vertices.length; j++) {\n          var v = p.vertices[j];\n          v.pos.applyMatrix4(mesh.matrix);\n          v.normal.applyMatrix3(CSG._tmpm3);\n        }\n      }\n\n      return csg;\n    };\n\n    CSG.toMesh = function (csg, toMatrix) {\n      var geom = new THREE.Geometry();\n      var ps = csg.polygons;\n      var vs = geom.vertices;\n      var fvuv = geom.faceVertexUvs[0];\n\n      for (var i = 0; i < ps.length; i++) {\n        var p = ps[i];\n        var pvs = p.vertices;\n        var v0 = vs.length;\n        var pvlen = pvs.length;\n\n        for (var j = 0; j < pvlen; j++) vs.push(new THREE.Vector3().copy(pvs[j].pos));\n\n        for (var j = 3; j <= pvlen; j++) {\n          var fc = new THREE.Face3();\n          var fuv = [];\n          fvuv.push(fuv);\n          var fnml = fc.vertexNormals;\n          fc.a = v0;\n          fc.b = v0 + j - 2;\n          fc.c = v0 + j - 1;\n          fnml.push(new THREE.Vector3().copy(pvs[0].normal));\n          fnml.push(new THREE.Vector3().copy(pvs[j - 2].normal));\n          fnml.push(new THREE.Vector3().copy(pvs[j - 1].normal));\n          fuv.push(new THREE.Vector3().copy(pvs[0].uv));\n          fuv.push(new THREE.Vector3().copy(pvs[j - 2].uv));\n          fuv.push(new THREE.Vector3().copy(pvs[j - 1].uv));\n          fc.normal = new THREE.Vector3().copy(p.plane.normal);\n          geom.faces.push(fc);\n        }\n      }\n\n      var inv = new THREE.Matrix4().getInverse(toMatrix);\n      geom.applyMatrix4(inv);\n      geom.verticesNeedUpdate = geom.elementsNeedUpdate = geom.normalsNeedUpdate = true;\n      geom.computeBoundingSphere();\n      geom.computeBoundingBox();\n      var m = new THREE.Mesh(geom);\n      m.matrix.copy(toMatrix);\n      m.matrix.decompose(m.position, m.rotation, m.scale);\n      m.updateMatrixWorld();\n      return m;\n    };\n\n    CSG.ieval = function (tokens, index = 0) {\n      if (typeof tokens === 'string') CSG.currentOp = tokens;else if (tokens instanceof Array) {\n        for (let i = 0; i < tokens.length; i++) CSG.ieval(tokens[i], 0);\n      } else if (typeof tokens === 'object') {\n        var op = CSG.currentOp;\n        tokens.updateMatrix();\n        tokens.updateMatrixWorld();\n        if (!CSG.sourceMesh) CSG.currentPrim = CSG.fromMesh(CSG.sourceMesh = tokens);else {\n          CSG.nextPrim = CSG.fromMesh(tokens);\n          CSG.currentPrim = CSG.currentPrim[op](CSG.nextPrim);\n        }\n        if (CSG.doRemove) tokens.parent.remove(tokens);\n      } //union,subtract,intersect,inverse\n    };\n\n    CSG.eval = function (tokens, doRemove) {\n      //[['add',mesh,mesh,mesh,mesh],['sub',mesh,mesh,mesh,mesh]]\n      CSG.currentOp = null;\n      CSG.sourceMesh = null;\n      CSG.doRemove = doRemove;\n      CSG.ieval(tokens);\n      var result = CSG.toMesh(CSG.currentPrim, CSG.sourceMesh.matrix);\n      result.material = CSG.sourceMesh.material;\n      result.castShadow = result.receiveShadow = true;\n      return result;\n    };\n\n    return CSG;\n  } //export default CsgFactory\n  // Return a new CSG solid representing space in either this solid or in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.union(B)\n  //\n  //     +-------+            +-------+\n  //     |       |            |       |\n  //     |   A   |            |       |\n  //     |    +--+----+   =   |       +----+\n  //     +----+--+    |       +----+       |\n  //          |   B   |            |       |\n  //          |       |            |       |\n  //          +-------+            +-------+\n  //\n  // Return a new CSG solid representing space in this solid but not in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.subtract(B)\n  //\n  //     +-------+            +-------+\n  //     |       |            |       |\n  //     |   A   |            |       |\n  //     |    +--+----+   =   |    +--+\n  //     +----+--+    |       +----+\n  //          |   B   |\n  //          |       |\n  //          +-------+\n  //\n  // Return a new CSG solid representing space both this solid and in the\n  // solid `csg`. Neither this solid nor the solid `csg` are modified.\n  //\n  //     A.intersect(B)\n  //\n  //     +-------+\n  //     |       |\n  //     |   A   |\n  //     |    +--+----+   =   +--+\n  //     +----+--+    |       +--+\n  //          |   B   |\n  //          |       |\n  //          +-------+\n  //\n\n});","define(\"m08-2020/lib/ClippingUtils/DynamicSectionalPlane\", [\"exports\", \"three\", \"m08-2020/lib/Utils\"], function (_exports, THREE, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DynamicSectionalPlane = void 0;\n  const utils = new _Utils.Utils(THREE);\n  const CHANGE_EVENT = new Event(\"change\");\n\n  class DynamicSectionalPlane {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.sectionalPlanes = [];\n      this.renderOrder = 1;\n    }\n\n    applySectionalPlanes() {\n      console.log(\"***** Hier geths lang *****\");\n      let controllerContainers = document.getElementById(\"sectional-plane-controllers\");\n      console.log(document.getElementById(\"sectional-plane-controllers\"));\n      console.log(this.sectionalPlanes);\n\n      for (let planeData of this.sectionalPlanes) {\n        console.log(planeData.targets);\n        let targets = planeData.targets;\n        this.clipDrawingObjects(planeData, targets.drawingObjects);\n        this.clipObject3D(planeData, targets.fasteners);\n        this.clipObject3D(planeData, targets.dimensionalChains);\n        this.clipObject3D(planeData, targets.arrows);\n        if (!this.GraphicsThree3D.dynamicCutApplied) this.addPlaneToGUI(planeData, controllerContainers);else this.updatePlaneGUI(planeData);\n      }\n\n      this.GraphicsThree3D.dynamicCutApplied = true;\n    } // Clipping helper functions -->\n\n\n    clipDrawingObjects(planeData, drawingObjects) {\n      for (let {\n        object,\n        clippingPlanes\n      } of drawingObjects) {\n        let targetClippingPlanes = this.sectionalPlanes.filter(plane => clippingPlanes.includes(plane.id));\n        this.clipMesh(planeData, object, targetClippingPlanes);\n      }\n    }\n\n    clipObject3D(planeData, objectsData) {\n      for (let {\n        object,\n        clippingPlanes\n      } of objectsData) {\n        let targetClippingPlanes = this.sectionalPlanes.filter(plane => clippingPlanes.includes(plane.id));\n        object.traverse(child => {\n          if (child.type === \"Mesh\" || child.type === \"LineLoop\" || child.type === \"Sprite\") this.clipMesh(planeData, child, targetClippingPlanes);\n        });\n      }\n    } // Clipping helper functions <--\n\n\n    initSectionalPlanes(dynamicSectionalPlanes) {\n      for (let dynamicSectionalPlane of dynamicSectionalPlanes) {\n        let planeData = dynamicSectionalPlane.planeData;\n        let sectionalPlaneData = {\n          clippingPlane: this.makePlane(planeData),\n          positionVector: utils.initVector3(planeData.positionVector),\n          id: planeData.id,\n          range: planeData.range,\n          factor: dynamicSectionalPlane.factor,\n          stencilPlanes: [],\n          targets: {\n            drawingObjects: [],\n            fasteners: [],\n            dimensionalChains: [],\n            arrows: []\n          }\n        };\n        this.sectionalPlanes.push(sectionalPlaneData);\n      }\n    }\n\n    updatePlaneGUI(planeData) {\n      const self = this;\n      const clippingPlane = planeData.clippingPlane;\n      const ID = planeData.id;\n      replaceVisibilityController();\n      replaceInversionController();\n      replacePositionController();\n\n      function replaceVisibilityController() {\n        const visibilityController_Old = document.getElementById(`show-cut-${ID}`);\n        const visibilityController_New = visibilityController_Old.cloneNode(true);\n        visibilityController_Old.parentNode.replaceChild(visibilityController_New, visibilityController_Old);\n        visibilityController_New.addEventListener(\"change\", ev => self.togglePlaneVisibility(planeData, clippingPlane, ev.target.checked));\n        if (!visibilityController_New.checked) visibilityController_New.dispatchEvent(CHANGE_EVENT);\n      }\n\n      function replaceInversionController() {\n        const invertController_Old = document.getElementById(`invert-cut-${ID}`);\n        const invertController_New = invertController_Old.cloneNode(true);\n        invertController_Old.parentNode.replaceChild(invertController_New, invertController_Old);\n        invertController_New.addEventListener(\"change\", () => clippingPlane.negate());\n        if (invertController_New.checked) invertController_New.dispatchEvent(CHANGE_EVENT);\n      }\n\n      function replacePositionController() {\n        const positionController_Old = document.getElementById(`cut-position-changer-${ID}`);\n        const positionController_New = positionController_Old.cloneNode(true);\n        positionController_Old.parentNode.replaceChild(positionController_New, positionController_Old);\n        let baseConstant = clippingPlane.constant;\n        positionController_New.addEventListener(\"input\", ev => {\n          clippingPlane.constant = baseConstant + parseInt(ev.target.value);\n          self.moveStencilPlanes(planeData);\n        });\n      }\n    }\n\n    addPlaneToGUI(planeData, controllerContainers) {\n      const self = this;\n      const clippingPlane = planeData.clippingPlane;\n      const ID = planeData.id;\n      let container = document.createElement(\"li\");\n      [makeId(), makeCutVisibilityChanger(), makeCutDirectionChanger(), makeCutPositionChanger()].forEach(elem => container.appendChild(elem));\n      controllerContainers.appendChild(container); // Helper functions -->\n\n      function makeId() {\n        let idElem = document.createElement(\"span\");\n        idElem.textContent = ID;\n        return idElem;\n      }\n\n      function makeCutVisibilityChanger() {\n        let wrapper = document.createElement(\"div\");\n        wrapper.className = \"cut-controller\";\n        let input = document.createElement(\"input\");\n        input.type = \"checkbox\";\n        input.className = \"checkbox-button\";\n        input.name = \"show-cut-\" + ID;\n        input.id = \"show-cut-\" + ID;\n        input.checked = true;\n        input.addEventListener(\"change\", ev => self.togglePlaneVisibility(planeData, clippingPlane, ev.target.checked));\n        let label = document.createElement(\"label\");\n        label.htmlFor = \"show-cut-\" + ID;\n        label.textContent = \"Show\";\n        wrapper.appendChild(input);\n        wrapper.appendChild(label);\n        return wrapper;\n      }\n\n      function makeCutDirectionChanger() {\n        let wrapper = document.createElement(\"div\");\n        wrapper.className = \"cut-controller\";\n        let input = document.createElement(\"input\");\n        input.type = \"checkbox\";\n        input.className = \"checkbox-button\";\n        input.name = \"invert-cut-\" + ID;\n        input.id = \"invert-cut-\" + ID;\n        input.addEventListener(\"change\", () => clippingPlane.negate());\n        let label = document.createElement(\"label\");\n        label.htmlFor = \"invert-cut-\" + ID;\n        label.textContent = \"Invert\";\n        wrapper.appendChild(input);\n        wrapper.appendChild(label);\n        return wrapper;\n      }\n\n      function makeCutPositionChanger() {\n        let wrapper = document.createElement(\"div\");\n        wrapper.className = \"cut-controller\";\n        let input = document.createElement(\"input\");\n        input.type = \"range\";\n        input.id = \"cut-position-changer-\" + ID;\n        let baseConstant = clippingPlane.constant;\n        input.min = -planeData.range;\n        input.max = planeData.range;\n        input.value = 0;\n        input.addEventListener(\"input\", ev => {\n          clippingPlane.constant = baseConstant + parseInt(ev.target.value);\n          self.moveStencilPlanes(planeData);\n        });\n        wrapper.appendChild(input);\n        return wrapper;\n      } // Helper functions <--\n\n    }\n\n    moveStencilPlanes(planeData) {\n      let {\n        clippingPlane,\n        stencilPlanes\n      } = planeData;\n      stencilPlanes.forEach(({\n        stencilPlane\n      }) => {\n        let projectedPlanePos = stencilPlane.position.clone().projectOnVector(clippingPlane.normal);\n        let clippingPlanePos = clippingPlane.normal.clone().multiplyScalar(-clippingPlane.constant);\n        let delta = clippingPlanePos.sub(projectedPlanePos);\n        stencilPlane.position.add(delta);\n      });\n    }\n\n    clipMesh(planeData, mesh, targetPlanes) {\n      mesh.updateMatrix();\n      let clippingPlane = planeData.clippingPlane;\n      let positionVector = planeData.positionVector;\n      let targetScene = this.GraphicsThree3D.stencilScenes.length ? new THREE.Scene() : this.GraphicsThree3D.scene;\n\n      if (mesh.stencilNeeded) {\n        let stencilAndOutline = {\n          stencilPlane: null,\n          stencilClippingPlanes: targetPlanes,\n          targetMesh: mesh,\n          stencilOpacity: 0.0,\n          targetScene\n        };\n\n        if (mesh.material) {\n          let colorProps = this.getColorPropsFromMaterial(mesh.material, planeData.factor);\n          stencilAndOutline.stencilOpacity = colorProps.alpha / 255.0;\n          let [frontFaceStencilMat, backFaceStencilMat, planeStencilMat] = this.getClippingMaterials(colorProps);\n          this.clipStencilMaterials(clippingPlane, targetPlanes, [frontFaceStencilMat, backFaceStencilMat, planeStencilMat]);\n          let meshGeo = mesh.geometry.clone();\n\n          for (let stencilMat of [frontFaceStencilMat, backFaceStencilMat]) {\n            let stencilMesh = new THREE.Mesh(meshGeo, stencilMat);\n            stencilMesh.applyMatrix4(mesh.matrix);\n            stencilMesh.position.copy(mesh.position);\n            stencilMesh.renderOrder = this.renderOrder;\n            targetScene.add(stencilMesh);\n          }\n\n          let planeMesh = this.createStencilPlaneMesh(clippingPlane, positionVector, planeStencilMat);\n          this.GraphicsThree3D.initWireframeObject(planeMesh, new THREE.Mesh(), this.GraphicsThree3D.jsonData, targetScene);\n          targetScene.add(planeMesh);\n          stencilAndOutline.stencilPlane = planeMesh;\n        }\n\n        planeData.stencilPlanes.push(stencilAndOutline);\n      }\n\n      this.GraphicsThree3D.stencilScenes.push(targetScene);\n      if (mesh.children) mesh.children.forEach(child => this.clipMaterial(child.material, clippingPlane));\n      this.clipMaterial(mesh.material, clippingPlane);\n    }\n\n    clipStencilMaterials(clippingPlane, targetPlanes, [frontFaceStencilMat, backFaceStencilMat, planeStencilMat]) {\n      frontFaceStencilMat.clippingPlanes = [clippingPlane];\n      backFaceStencilMat.clippingPlanes = [clippingPlane];\n      planeStencilMat.clippingPlanes = targetPlanes.filter(plane => !plane.clippingPlane.equals(clippingPlane)).map(plane => plane.clippingPlane);\n    }\n\n    createStencilPlaneMesh(clippingPlane, positionVector, planeStencilMat) {\n      let planeNormal = clippingPlane.normal.clone();\n      let forwardVector = new THREE.Vector3(0, 0, -1);\n      let planeGeom = new THREE.PlaneBufferGeometry();\n      let planeMesh = new THREE.Mesh(planeGeom, planeStencilMat);\n      planeMesh.scale.setScalar(1000);\n      planeMesh.position.copy(positionVector);\n      planeMesh.quaternion.setFromUnitVectors(forwardVector, planeNormal);\n\n      planeMesh.onAfterRender = function (renderer) {\n        renderer.clearStencil();\n      };\n\n      planeMesh.renderOrder = this.renderOrder + 0.1;\n      planeMesh.isStencilPlane = true;\n      return planeMesh;\n    }\n\n    getClippingMaterials(colorProps) {\n      // https://discourse.threejs.org/t/capping-clipped-planes-using-stencil-on-a-buffergeometry/18407/18\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n      let backFaceStencilMat = this.createBackStencilMat(); // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      let frontFaceStencilMat = this.createFrontStencilMat(); // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      let planeStencilMat = this.createPlaneStencilMat(colorProps);\n      return [frontFaceStencilMat, backFaceStencilMat, planeStencilMat];\n    }\n\n    createPlaneStencilMat(colorProps) {\n      let planeStencilMat = this.GraphicsThree3D.getMaterialFromColor(colorProps); ///\n      // planeStencilMat.side = THREE.FrontSide;\n      ///\n\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = THREE.ReplaceStencilOp;\n      return planeStencilMat;\n    }\n\n    createBackStencilMat() {\n      let backFaceStencilMat = new THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = THREE.BackSide;\n      backFaceStencilMat.stencilFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = THREE.IncrementWrapStencilOp;\n      return backFaceStencilMat;\n    }\n\n    createFrontStencilMat() {\n      let frontFaceStencilMat = new THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = THREE.DecrementWrapStencilOp;\n      return frontFaceStencilMat;\n    }\n\n    getColorPropsFromMaterial(meshMaterial, factor) {\n      let material = meshMaterial.length ? meshMaterial[0].clone() : meshMaterial.clone();\n      let {\n        red,\n        green,\n        blue\n      } = this.modifyColorWithFactor(material.color, factor);\n      let colorProps = {\n        alpha: material.opacity * 255,\n        red,\n        green,\n        blue\n      };\n      return colorProps;\n    }\n\n    modifyColorWithFactor(color, factor) {\n      let {\n        r,\n        g,\n        b\n      } = color;\n      let red = r * 255;\n      let green = g * 255;\n      let blue = b * 255;\n\n      if (factor > 0) {\n        red += (255 - red) * factor;\n        green += (255 - green) * factor;\n        blue += (255 - blue) * factor;\n      } else {\n        red += red * factor;\n        green += green * factor;\n        blue += blue * factor;\n      }\n\n      red = Math.floor(red);\n      green = Math.floor(green);\n      blue = Math.floor(blue);\n      return {\n        red,\n        green,\n        blue\n      };\n    }\n\n    clipMaterial(material, plane) {\n      if (material) {\n        if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n      }\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    togglePlaneVisibility(planeData, clippingPlane, isVisible) {\n      if (isVisible) this.showClippingPlane(planeData, clippingPlane);else this.hideClippingPlane(planeData, clippingPlane);\n    }\n\n    showClippingPlane(planeData, clippingPlane) {\n      [planeData.targets.drawingObjects, planeData.targets.fasteners, planeData.targets.dimensionalChains, planeData.targets.arrows].forEach(targetObjects => targetObjects.forEach(objData => this.addClippingPlaneToObject(objData.object, clippingPlane)));\n      this.addClippingWhenVisible(clippingPlane);\n      planeData.stencilPlanes.forEach(plane => plane.stencilPlane.material.opacity = plane.stencilOpacity);\n    }\n\n    hideClippingPlane(planeData, clippingPlane) {\n      [planeData.targets.drawingObjects, planeData.targets.fasteners, planeData.targets.dimensionalChains, planeData.targets.arrows].forEach(targetObjects => targetObjects.forEach(objData => this.removeClippingPlaneFromObject(objData.object, clippingPlane)));\n      this.removeClippingWhenNotVisible(clippingPlane);\n      planeData.stencilPlanes.forEach(plane => plane.stencilPlane.material.opacity = 0);\n    }\n\n    addClippingWhenVisible(clippingPlane) {\n      this.sectionalPlanes.forEach(planeData => {\n        if (!planeData.clippingPlane.equals(clippingPlane)) {\n          planeData.stencilPlanes.forEach(stencilData => {\n            stencilData.stencilPlane.material.clippingPlanes.push(clippingPlane);\n          });\n        }\n      });\n    }\n\n    removeClippingWhenNotVisible(clippingPlane) {\n      this.sectionalPlanes.forEach(planeData => {\n        planeData.stencilPlanes.forEach(stencilData => {\n          stencilData.stencilPlane.material.clippingPlanes = stencilData.stencilPlane.material.clippingPlanes.filter(cp => !cp.equals(clippingPlane));\n        });\n      });\n    }\n\n    addClippingPlaneToObject(mesh, plane) {\n      if (mesh.children) mesh.traverse(child => this.clipMaterial(child.material, plane));\n      this.clipMaterial(mesh.material, plane);\n    }\n\n    removeClippingPlaneFromObject(mesh, plane) {\n      if (mesh.children) mesh.traverse(child => this.removeClippingPlane(child.material, plane));\n      this.removeClippingPlane(mesh.material, plane);\n    }\n\n    removeClippingPlane(material, plane) {\n      if (material) {\n        if (material.length > 0) material.forEach(mat => mat.clippingPlanes = mat.clippingPlanes.filter(cp => !cp.equals(plane)));else material.clippingPlanes = material.clippingPlanes.filter(cp => !cp.equals(plane));\n      }\n    }\n\n    makePlane(planeData) {\n      let positionVector = utils.initVector3(planeData.positionVector);\n      let normalVector = utils.initVector3(planeData.normalVector).negate().normalize();\n      let planeDistance = positionVector.clone().projectOnVector(normalVector).length();\n      let distanceSign = positionVector.normalize().dot(normalVector) > 0 ? -1 : 1;\n      planeDistance *= distanceSign;\n      let plane = new THREE.Plane(normalVector, planeDistance);\n      return plane;\n    }\n\n    clearSectionalPlanes() {\n      this.sectionalPlanes.forEach(planeData => {\n        planeData.stencilPlanes.forEach(stencilPlaneData => stencilPlaneData.targetScene.remove(stencilPlaneData.stencilPLane));\n      });\n      this.sectionalPlanes = [];\n    }\n\n  }\n\n  _exports.DynamicSectionalPlane = DynamicSectionalPlane;\n});","define(\"m08-2020/lib/ClippingUtils/OutlineGenerator\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.OutlineGenerator = void 0;\n  const TOLERANCE = 0.01;\n\n  let _a = new THREE.Vector3();\n\n  let _b = new THREE.Vector3();\n\n  let _c = new THREE.Vector3();\n\n  let _lineAB = new THREE.Line3();\n\n  let _lineBC = new THREE.Line3();\n\n  let _lineCA = new THREE.Line3();\n\n  let _pointOfIntersection = new THREE.Vector3();\n\n  let _pointsOfIntersection = new THREE.Geometry();\n\n  class OutlineGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n    }\n\n    drawStencilOutline(obj, mathPlane, lineProps) {\n      obj.geometry.faces.forEach((face, idx) => {\n        obj.localToWorld(_a.copy(obj.geometry.vertices[face.a]));\n        obj.localToWorld(_b.copy(obj.geometry.vertices[face.b]));\n        obj.localToWorld(_c.copy(obj.geometry.vertices[face.c]));\n        _lineAB = new THREE.Line3(_a, _b);\n        _lineBC = new THREE.Line3(_b, _c);\n        _lineCA = new THREE.Line3(_c, _a);\n        this.setPointOfIntersection(_lineAB, mathPlane, idx);\n        this.setPointOfIntersection(_lineBC, mathPlane, idx);\n        this.setPointOfIntersection(_lineCA, mathPlane, idx);\n      });\n      let lines = [];\n      let vertices = _pointsOfIntersection.vertices;\n      let facesDict = getFaceIndexMap(vertices);\n      Object.values(facesDict).forEach(val => {\n        if (val.length > 1) {\n          let line = this.GraphicsThree3D.drawLine(val[0], val[1], lineProps);\n          lines.push(line);\n        }\n      });\n      return lines;\n    }\n\n    setPointOfIntersection(line, plane, faceIdx) {\n      _pointOfIntersection = plane.intersectLine(line, new THREE.Vector3());\n\n      if (_pointOfIntersection && !line.start.equals(_pointOfIntersection)) {\n        let p = _pointOfIntersection.clone();\n\n        p.faceIndex = faceIdx;\n        p.checked = false;\n\n        _pointsOfIntersection.vertices.push(p);\n      }\n    }\n\n    getContours(points, contours) {\n      let contour = []; // find first line for the contour\n\n      let firstPointIndex = 0;\n      let secondPointIndex = 0;\n      let firstPoint, secondPoint;\n\n      for (let i = 0; i < points.length; i++) {\n        if (points[i].checked == true) continue;\n        firstPointIndex = i;\n        firstPoint = points[firstPointIndex];\n        firstPoint.checked = true;\n        secondPointIndex = this.getPairIndex(firstPoint, firstPointIndex, points);\n        secondPoint = points[secondPointIndex];\n        secondPoint.checked = true;\n        contour.push(firstPoint.clone());\n        contour.push(secondPoint.clone());\n        break;\n      }\n\n      contour = this.getContour(secondPoint, points, contour);\n      contours.push(contour);\n      let allChecked = 0;\n      points.forEach(p => {\n        allChecked += p.checked == true ? 1 : 0;\n      });\n\n      if (allChecked != points.length) {\n        return this.getContours(points, contours);\n      }\n\n      return contours;\n    }\n\n    getContour(currentPoint, points, contour) {\n      let p1Index = this.getNearestPointIndex(currentPoint, points);\n      let p1 = points[p1Index];\n      p1.checked = true;\n      let p2Index = this.getPairIndex(p1, p1Index, points);\n      let p2 = points[p2Index];\n      p2.checked = true;\n      let isClosed = p2.equals(contour[0], TOLERANCE);\n\n      if (!isClosed) {\n        contour.push(p2.clone());\n        return this.getContour(p2, points, contour);\n      } else {\n        contour.push(contour[0].clone());\n        return contour;\n      }\n    }\n\n    getNearestPointIndex(point, points) {\n      let index = 0;\n\n      for (let i = 0; i < points.length; i++) {\n        let p = points[i];\n\n        if (p.checked == false && p.equals(point, TOLERANCE)) {\n          index = i;\n          break;\n        }\n      }\n\n      return index;\n    }\n\n    getPairIndex(point, pointIndex, points) {\n      let index = 0;\n\n      for (let i = 0; i < points.length; i++) {\n        let p = points[i];\n\n        if (i != pointIndex && p.checked == false && p.faceIndex == point.faceIndex) {\n          index = i;\n          break;\n        }\n      }\n\n      return index;\n    }\n\n    clearPointsOfIntersection() {\n      _pointOfIntersection = new THREE.Vector3();\n      _pointsOfIntersection = new THREE.Geometry();\n    }\n\n    static generateCutLinePts(obj, mathPlane) {\n      const SCALE_MULTIPLIER = 1 + 1e-4;\n      const VERTEX_NUM = 3;\n      let centroid = new THREE.Vector3();\n      let keys = [\"a\", \"b\", \"c\"];\n      let vertices = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];\n      let lines = [new THREE.Line3(), new THREE.Line3(), new THREE.Line3()];\n      let pointOfIntersection = new THREE.Vector3();\n      let pointsOfIntersection = [];\n      setFacePointsOfIntersection(obj.geometry.faces);\n      let facesDict = getFaceIndexMap(pointsOfIntersection);\n      let linePts = Object.values(facesDict).filter(ptPair => ptPair.length > 1);\n      return linePts;\n\n      function setFacePointsOfIntersection(faces) {\n        faces.forEach((face, idx) => {\n          updateVertices(face);\n\n          for (let i = 0; i < VERTEX_NUM; i++) {\n            lines[i].set(vertices[i % VERTEX_NUM], vertices[(i + 1) % VERTEX_NUM]);\n            setPointOfIntersection(lines[i], mathPlane, idx);\n          }\n        });\n      }\n\n      function setPointOfIntersection(line, plane, faceIdx) {\n        pointOfIntersection = plane.intersectLine(line, new THREE.Vector3());\n\n        if (pointOfIntersection && !line.start.equals(pointOfIntersection)) {\n          let p = pointOfIntersection.clone();\n          p.faceIndex = faceIdx;\n          p.checked = false;\n          pointsOfIntersection.push(p);\n        }\n      }\n\n      function updateVertices(face) {\n        for (let i = 0; i < VERTEX_NUM; i++) obj.localToWorld(vertices[i].copy(obj.geometry.vertices[face[keys[i]]]));\n\n        centroid.copy(getCentroid(vertices));\n\n        for (let vec of vertices) vec.sub(centroid).multiplyScalar(SCALE_MULTIPLIER).add(centroid);\n      }\n    }\n\n  }\n\n  _exports.OutlineGenerator = OutlineGenerator;\n\n  function getCentroid(vecArr) {\n    let arrSize = vecArr.length;\n    let sum = {\n      x: 0,\n      y: 0,\n      z: 0\n    };\n\n    for (let vec of vecArr) {\n      sum.x += vec.x;\n      sum.y += vec.y;\n      sum.z += vec.z;\n    }\n\n    return new THREE.Vector3(sum.x / arrSize, sum.y / arrSize, sum.z / arrSize);\n  }\n\n  function getFaceIndexMap(vertices) {\n    let dict = {};\n    vertices.forEach(vertex => {\n      if (dict.hasOwnProperty(vertex.faceIndex)) dict[vertex.faceIndex].push(vertex);else dict[vertex.faceIndex] = [vertex];\n    });\n    return dict;\n  }\n});","define(\"m08-2020/lib/ClippingUtils/SectionalPlane\", [\"exports\", \"three\", \"m08-2020/lib/Utils\", \"m08-2020/lib/ClippingUtils/OutlineGenerator\"], function (_exports, THREE, _Utils, _OutlineGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.SectionalPlane = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class SectionalPlane {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.OutlineGenerator = new _OutlineGenerator.OutlineGenerator(GraphicsThree3D);\n      this.clippingPlanes = [];\n      this.clipObjects = [];\n      this.renderOrder = 1;\n      this.sectionalPlanesData = {};\n    }\n\n    addToSectionalPlanes(insertionPoint, object, objectData) {\n      if (this.sectionalPlanesData.hasOwnProperty(objectData.id)) {\n        if (this.sectionalPlanesData[objectData.id].hasOwnProperty(insertionPoint.id)) {\n          this.sectionalPlanesData[objectData.id][insertionPoint.id].push({\n            object,\n            insertionPoint,\n            sectionalPlanes: objectData.components.sectionalPlanes,\n            properties: objectData.properties\n          });\n        } else {\n          this.sectionalPlanesData[objectData.id][insertionPoint.id] = [{\n            object,\n            insertionPoint,\n            sectionalPlanes: objectData.components.sectionalPlanes,\n            properties: objectData.properties\n          }];\n        }\n      } else {\n        this.sectionalPlanesData[objectData.id] = {};\n        this.sectionalPlanesData[objectData.id][insertionPoint.id] = [{\n          object,\n          insertionPoint,\n          sectionalPlanes: objectData.components.sectionalPlanes,\n          properties: objectData.properties\n        }];\n      }\n    }\n\n    applySectionalPlanes() {\n      Object.values(this.sectionalPlanesData).forEach(drawingObj => Object.values(drawingObj).forEach(insertionPt => insertionPt.forEach(data => {\n        let clippingPlanes = this.initClippingPlanes(data.sectionalPlanes, data.insertionPoint);\n        let intersectionLineProps = data.properties.intersectionLine;\n        let intersectionAreaProps = data.properties.intersectionArea;\n        data.object.stencilNeeded = true;\n        clippingPlanes.forEach(clippingPlane => {\n          this.clipMesh(clippingPlane, data.object, intersectionAreaProps, intersectionLineProps, clippingPlanes);\n        });\n      })));\n    }\n\n    initClippingPlanes(sectionalPlanes, insertionPoint) {\n      let clippingPlanes = [];\n      let insertionVector = utils.initVector3(insertionPoint.insertionVector);\n      let rotationAngle = insertionPoint.rotationAngle ? insertionPoint.rotationAngle * Math.PI / 180 : 0;\n      let rotationAxis = rotationAngle ? utils.initVector3(insertionPoint.rotationAxis) : 0;\n      let rotationPoint = rotationAngle ? utils.initVector3(insertionPoint.rotationPoint) : 0;\n      sectionalPlanes.forEach(planeData => {\n        let modifiedPlaneData = {};\n\n        if (rotationAngle && rotationAxis && rotationPoint) {\n          modifiedPlaneData.normalVector = utils.initVector3(planeData.normalVector).applyAxisAngle(rotationAxis, rotationAngle);\n          modifiedPlaneData.positionVector = utils.initVector3(planeData.positionVector).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint).add(insertionVector);\n        } else {\n          modifiedPlaneData.normalVector = planeData.normalVector;\n          modifiedPlaneData.positionVector = {\n            x: planeData.positionVector.x + insertionPoint.insertionVector.x,\n            y: planeData.positionVector.y + insertionPoint.insertionVector.y,\n            z: planeData.positionVector.z + insertionPoint.insertionVector.z\n          };\n        }\n\n        clippingPlanes.push({\n          clippingPlane: this.makePlane(modifiedPlaneData),\n          positionVector: utils.initVector3(modifiedPlaneData.positionVector)\n        });\n      });\n      return clippingPlanes;\n    }\n\n    applySectionalPlane(planeData, intersectionAreaProps, intersectionLineProps) {\n      this.clipObjects.forEach(({\n        object,\n        insertionPoint\n      }) => {\n        if (object.type == \"Mesh\" || object.type == \"Line\" || object.type == \"LineSegments\") {\n          this.clipMesh(planeData, object, insertionPoint, intersectionAreaProps, intersectionLineProps);\n        }\n      });\n    }\n\n    makePlane(planeData) {\n      let positionVector = utils.initVector3(planeData.positionVector);\n      let normalVector = utils.initVector3(planeData.normalVector).negate().normalize();\n      let planeDistance = positionVector.clone().projectOnVector(normalVector).length();\n      let distanceSign = positionVector.normalize().dot(normalVector) > 0 ? -1 : 1;\n      planeDistance *= distanceSign;\n      let plane = new THREE.Plane(normalVector, planeDistance);\n      return plane;\n    }\n\n    clipMesh(planeData, mesh, intersectionAreaProps, lineProps, clippingPlanes) {\n      mesh.updateMatrix();\n      mesh.updateMatrixWorld();\n      let clippingPlane = planeData.clippingPlane;\n      let positionVector = planeData.positionVector;\n      let targetScene = this.GraphicsThree3D.stencilScenes.length ? new THREE.Scene() : this.GraphicsThree3D.scene;\n\n      if (mesh.stencilNeeded) {\n        if (this.areaPropsDefined(intersectionAreaProps)) {\n          let [frontFaceStencilMat, backFaceStencilMat, planeStencilMat] = this.getClippingMaterials(intersectionAreaProps);\n          frontFaceStencilMat.clippingPlanes = [clippingPlane];\n          backFaceStencilMat.clippingPlanes = [clippingPlane];\n          planeStencilMat.clippingPlanes = clippingPlanes.filter(plane => !plane.clippingPlane.equals(clippingPlane)).map(plane => plane.clippingPlane);\n          let meshGeo = mesh.geometry.clone();\n          let frontMesh = new THREE.Mesh(meshGeo, frontFaceStencilMat);\n          frontMesh.applyMatrix4(mesh.matrix);\n          let backMesh = new THREE.Mesh(meshGeo, backFaceStencilMat);\n          backMesh.applyMatrix4(mesh.matrix);\n          frontMesh.renderOrder = this.renderOrder;\n          backMesh.renderOrder = this.renderOrder;\n          targetScene.add(frontMesh);\n          targetScene.add(backMesh);\n          let planeNormal = clippingPlane.normal.clone();\n          let forwardVector = new THREE.Vector3(0, 0, -1);\n          let planeGeom = new THREE.PlaneBufferGeometry();\n          let planeMesh = new THREE.Mesh(planeGeom, planeStencilMat);\n          planeMesh.scale.setScalar(1000);\n          planeMesh.position.copy(positionVector);\n          planeMesh.quaternion.setFromUnitVectors(forwardVector, planeNormal);\n          frontMesh.position.copy(mesh.position);\n          backMesh.position.copy(mesh.position);\n\n          planeMesh.onAfterRender = function (renderer) {\n            renderer.clearStencil();\n          };\n\n          planeMesh.renderOrder = this.renderOrder + 0.1;\n          planeMesh.isSectionalPlane = true;\n          this.GraphicsThree3D.initWireframeObject(planeMesh, new THREE.Mesh(), this.GraphicsThree3D.jsonData, targetScene);\n          targetScene.add(planeMesh);\n        }\n      }\n\n      if (lineProps) {\n        let lines = this.OutlineGenerator.drawStencilOutline(mesh, clippingPlane, lineProps);\n        this.OutlineGenerator.clearPointsOfIntersection();\n        lines.forEach(line => {\n          line.material.clippingPlanes = clippingPlanes.map(p => p.clippingPlane);\n          targetScene.add(line);\n        });\n      }\n\n      this.GraphicsThree3D.stencilScenes.push(targetScene);\n      if (mesh.children) mesh.children.forEach(child => this.clipMaterial(child.material, clippingPlane));\n      this.clipMaterial(mesh.material, clippingPlane);\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    getClippingMaterials(materialProps) {\n      let frontFaceStencilMat, backFaceStencilMat, planeStencilMat; //https://discourse.threejs.org/t/capping-clipped-planes-using-stencil-on-a-buffergeometry/18407/18\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n\n      backFaceStencilMat = new THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = THREE.BackSide;\n      backFaceStencilMat.stencilFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = THREE.IncrementWrapStencilOp; // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      frontFaceStencilMat = new THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = THREE.DecrementWrapStencilOp; // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      planeStencilMat = this.getPlaneStencilMaterial(materialProps);\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = THREE.ReplaceStencilOp;\n      return [frontFaceStencilMat, backFaceStencilMat, planeStencilMat];\n    }\n\n    getPlaneStencilMaterial(areaProps) {\n      const SIZE = 20;\n      let planeStencilMat;\n      let colorProps = areaProps.color;\n\n      if (areaProps.hatching.type === 2) {\n        let orientationVector = utils.normalizeVector(areaProps.hatching.orientationVector);\n        let hatchSpacing = areaProps.hatching.factor;\n        let hatchColor = areaProps.hatching.color;\n        let hatchThickness = areaProps.hatching.thickness;\n        planeStencilMat = this.GraphicsThree3D.makeCanvasMaterial(colorProps, hatchColor, orientationVector, hatchSpacing, hatchThickness);\n        planeStencilMat.map.wrapS = planeStencilMat.map.wrapT = THREE.RepeatWrapping;\n        planeStencilMat.map.repeat.set(SIZE, SIZE);\n      } else if (areaProps.hatching.type === 1) {\n        planeStencilMat = this.GraphicsThree3D.getMaterialFromColor(colorProps);\n      }\n\n      return planeStencilMat;\n    }\n\n    areaPropsDefined(areaProps) {\n      return areaProps && areaProps.hatching.type > 0 && areaProps.hatching.type < 3;\n    }\n\n    resetClipObjects() {\n      this.clipObjects = [];\n    }\n\n  }\n\n  _exports.SectionalPlane = SectionalPlane;\n});","define(\"m08-2020/lib/FigureLoaders/ArrowLoader\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ArrowLoader = void 0;\n\n  class ArrowLoader {\n    constructor(THREE, fontUrlArray, GraphicsThree3D) {\n      this.THREE = THREE;\n      this.fontUrlArray = fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = GraphicsThree3D.utils;\n      this.lineRenderOrder = -103; ///\n\n      this.drawArrows = this.drawArrows.bind(this);\n    }\n\n    drawArrows(arrowData) {\n      let meshArray = [];\n      let materialProps = {\n        lineProps: arrowData.properties.line,\n        volumeProps: arrowData.properties.volume,\n        textProps: arrowData.properties.text,\n        areaProps: arrowData.properties.area\n      };\n      let insertionPoints = arrowData.insertionPoints;\n      let forceArrows = arrowData.components.forceArrows;\n      let momentArrows = arrowData.components.momentArrows;\n      let grainDirectionArrows = arrowData.components.grainDirectionArrows;\n\n      if (forceArrows) {\n        forceArrows.forEach(forceArrow => meshArray.push(this.drawForceArrow(forceArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      if (momentArrows) {\n        momentArrows.forEach(momentArrow => meshArray.push(this.drawMomentArrows(momentArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      if (grainDirectionArrows) {\n        grainDirectionArrows.forEach(grainDirectionArrow => meshArray.push(this.drawGrainDirectionArrow(grainDirectionArrow, materialProps, insertionPoints, arrowData)));\n      }\n\n      meshArray.forEach(arrow => {\n        this.GraphicsThree3D.addObjectToVisControl(arrow, \"arrow\");\n        this.GraphicsThree3D.addObjToDynamicSectionalPlanes(arrowData, arrow, \"arrows\");\n      });\n      return meshArray;\n    }\n\n    addToDimChainCollisionDetection(obj) {\n      obj.updateMatrix();\n      obj.traverse(child => {\n        if (child.type === \"Mesh\") {\n          let intersectionObject = child.clone();\n          intersectionObject.applyMatrix4(obj.matrix);\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(intersectionObject);\n          this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n        }\n      });\n    }\n\n    drawGrainDirectionArrow(grainDirectionArrowData, materialProps, insertionPoints, arrowData) {\n      let insertionPoint = grainDirectionArrowData.insertionPoint;\n      let grainDirectionArrows = new this.THREE.Group();\n\n      if (grainDirectionArrowData.directionVector && grainDirectionArrowData.normalVector && (grainDirectionArrowData.directionVector.x || grainDirectionArrowData.directionVector.y || grainDirectionArrowData.directionVector.z) && (grainDirectionArrowData.normalVector.x || grainDirectionArrowData.normalVector.y || grainDirectionArrowData.normalVector.z)) {\n        let directionVector = this.utils.initVector3(grainDirectionArrowData.directionVector);\n        let normalVector = this.utils.initVector3(grainDirectionArrowData.normalVector);\n\n        if (!directionVector.dot(normalVector) && grainDirectionArrowData.height > 0 && grainDirectionArrowData.length.value1 > 0 && grainDirectionArrowData.length.value2 > 0) {\n          let mainLength = grainDirectionArrowData.length.value1;\n          let miniLength = grainDirectionArrowData.length.value2;\n          let miniHeight = grainDirectionArrowData.height / 2;\n          let mainPointLeft = new this.THREE.Vector3(-mainLength / 2, 0, 0);\n          let mainPointRight = new this.THREE.Vector3(mainLength / 2, 0, 0);\n          let miniPointLeft = mainPointLeft.clone().add(new this.THREE.Vector3(miniLength, miniHeight, 0));\n          let miniPointRight = mainPointRight.clone().add(new this.THREE.Vector3(-miniLength, -miniHeight, 0));\n          insertionPoints.forEach(point => {\n            let grainDirectionArrow = new this.THREE.Object3D();\n\n            if (insertionPoint) {\n              let arrowMesh = this.utils.drawConnectedLines([miniPointLeft, mainPointLeft, mainPointRight, miniPointRight], materialProps.lineProps);\n              let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n              let dotProduct = arrowMesh.up.dot(newUpVector);\n              let rotationAngle = -Math.acos(dotProduct); // by default THREE.js rotates counterclockwise, so I switched sign (correct?)\n\n              arrowMesh.rotateOnAxis(normalVector, rotationAngle);\n              arrowMesh.up = newUpVector;\n              arrowMesh.lookAt(arrowMesh.position.clone().add(normalVector));\n              grainDirectionArrow.add(arrowMesh);\n              grainDirectionArrow.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n            }\n\n            if (materialProps.textProps) {\n              this.addTextToArrow(materialProps.textProps, arrowData.components.texts, grainDirectionArrow, insertionPoint);\n            }\n\n            let insertionVector = point.insertionVector;\n            let angle = point.rotationAngle * Math.PI / 180;\n            let rotationAxis, rotationPoint;\n\n            if (angle) {\n              rotationAxis = this.utils.initVector3(point.rotationAxis);\n              rotationPoint = this.utils.initVector3(point.rotationPoint);\n            }\n\n            let pivotPoint = new this.THREE.Object3D();\n\n            if (angle) {\n              pivotPoint.position.copy(rotationPoint);\n              pivotPoint.attach(grainDirectionArrow);\n              pivotPoint.rotateOnAxis(rotationAxis, angle);\n            } else {\n              pivotPoint.attach(grainDirectionArrow);\n            }\n\n            pivotPoint.position.x += insertionVector.x;\n            pivotPoint.position.y += insertionVector.y;\n            pivotPoint.position.z += insertionVector.z;\n            this.addToDimChainCollisionDetection(grainDirectionArrow);\n            grainDirectionArrows.add(pivotPoint);\n          });\n        }\n      }\n\n      return grainDirectionArrows;\n    }\n\n    drawMomentArrows(momentArrowData, materialProps, insertionPoints, arrowData) {\n      let insertionPoint = momentArrowData.insertionPoint;\n      let headRadius = momentArrowData.headSize.value2 / 2;\n      let headHeight = momentArrowData.headSize.value1;\n      let size = momentArrowData.size;\n      let arcAngle = momentArrowData.arcAngle;\n      let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n      let lineMaterial = this.getMaterialFromColor(materialProps.lineProps.color); // TODO: Move this for loop outside this function\n\n      let momentArrows = new this.THREE.Group();\n      insertionPoints.forEach(point => {\n        let insertionVector = point.insertionVector;\n        let rotationAngle = point.rotationAngle * Math.PI / 180;\n        let rotationAxis, rotationPoint;\n\n        if (rotationAngle) {\n          rotationAxis = this.utils.initVector3(point.rotationAxis);\n          rotationPoint = this.utils.initVector3(point.rotationPoint);\n        }\n\n        let momentArrow = new this.THREE.Object3D();\n\n        if (insertionPoint) {\n          if (!arcAngle && momentArrowData.directionVector1 && (momentArrowData.directionVector1.x || momentArrowData.directionVector1.y || momentArrowData.directionVector1.z)) {\n            let directionVectorOne = this.utils.initVector3(momentArrowData.directionVector1);\n            let pointer = this.makeArrow(headRadius, headHeight, volumeMaterial, lineMaterial, materialProps.lineProps);\n            pointer.rotation.z = Math.PI;\n            let pointerTwo = pointer.clone();\n            pointer.position.y += headHeight;\n            let arrow = new this.THREE.Object3D();\n            let axisPointOne = new this.THREE.Vector3(0, 0, 0);\n            let axisPointTwo = new this.THREE.Vector3(0, size, 0);\n            let axisMesh = this.GraphicsThree3D.drawLine(axisPointOne, axisPointTwo, materialProps.lineProps);\n            arrow.add(axisMesh);\n            arrow.add(pointer);\n            arrow.add(pointerTwo);\n            arrow.rotation.x = -Math.PI / 2;\n            let pivot = new this.THREE.Object3D();\n            pivot.add(arrow);\n            pivot.lookAt(directionVectorOne);\n            momentArrow.add(pivot);\n          } else if (arcAngle && momentArrowData.directionVector1 && momentArrowData.directionVector2 && (momentArrowData.directionVector1.x || momentArrowData.directionVector1.y || momentArrowData.directionVector1.z) && (momentArrowData.directionVector2.x || momentArrowData.directionVector2.y || momentArrowData.directionVector2.z)) {\n            let directionVectorOne = this.utils.initVector3(momentArrowData.directionVector1);\n            let directionVectorTwo = this.utils.initVector3(momentArrowData.directionVector2);\n\n            if (!directionVectorOne.dot(directionVectorTwo)) {\n              let pointer = this.makeArrow(headRadius, headHeight, volumeMaterial, lineMaterial, materialProps.lineProps, true);\n              pointer.rotation.z = Math.PI;\n              console.log(arcAngle);\n              let correctedArcAngle = arcAngle;\n              if (correctedArcAngle > 360 || correctedArcAngle < -360) materialProps.lineProps.thickness = 0;\n              if (correctedArcAngle > 360) correctedArcAngle = 360;\n              if (correctedArcAngle < -360) correctedArcAngle = -360;\n              let curveObject = new this.THREE.Object3D();\n              let curveAngle = correctedArcAngle * Math.PI / 180;\n              let startAngle, endAngle;\n\n              if (curveAngle > 0) {\n                startAngle = 0;\n                endAngle = curveAngle;\n              } else {\n                startAngle = curveAngle;\n                endAngle = 0;\n              }\n\n              let curve = new this.THREE.EllipseCurve(0, 0, size / 2, size / 2, startAngle, endAngle, false, 0);\n              let curvePoints = curve.getPoints(50).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n              let curveMesh = this.utils.drawConnectedLines(curvePoints, materialProps.lineProps);\n              let pointDir = new this.THREE.Vector3();\n\n              if (curveAngle > 0) {\n                for (let i = curvePoints.length - 2; i >= 0; i--) {\n                  let distance = Math.round(curvePoints[curvePoints.length - 1].distanceTo(curvePoints[i]));\n\n                  if (distance == Math.round(headHeight)) {\n                    pointDir.subVectors(curvePoints[curvePoints.length - 1], curvePoints[i]).normalize();\n                    break;\n                  }\n                }\n\n                pointer.rotation.x = -Math.PI / 2;\n                let object = new this.THREE.Object3D();\n                object.add(pointer);\n                object.lookAt(pointDir);\n                object.position.x += curvePoints[curvePoints.length - 1].x;\n                object.position.y += curvePoints[curvePoints.length - 1].y;\n                pointer = object;\n              } else {\n                for (let i = 0; i < curvePoints.length; i++) {\n                  let distance = Math.round(curvePoints[0].distanceTo(curvePoints[i]));\n\n                  if (distance == Math.round(headHeight)) {\n                    pointDir.subVectors(curvePoints[0], curvePoints[i]).normalize();\n                    break;\n                  }\n                }\n\n                pointer.rotation.x = -Math.PI / 2;\n                let object = new this.THREE.Object3D();\n                object.add(pointer);\n                object.lookAt(pointDir);\n                object.position.x += curvePoints[0].x;\n                object.position.y += curvePoints[0].y;\n                pointer = object;\n              }\n\n              curveObject.add(curveMesh);\n              curveObject.add(pointer);\n              this.GraphicsThree3D.alignObjectWithVectorsAndChangePos(curveObject, new this.THREE.Vector3(0, 0, 0), directionVectorOne, directionVectorTwo);\n              momentArrow.add(curveObject);\n            }\n          }\n\n          momentArrow.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n        }\n\n        if (materialProps.textProps) {\n          this.addTextToArrow(materialProps.textProps, arrowData.components.texts, momentArrow, insertionPoint);\n        }\n\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.copy(rotationPoint);\n          pivotPoint.attach(momentArrow);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(momentArrow);\n        }\n\n        pivotPoint.position.x += insertionVector.x;\n        pivotPoint.position.y += insertionVector.y;\n        pivotPoint.position.z += insertionVector.z;\n        this.addToDimChainCollisionDetection(momentArrow);\n        momentArrows.add(pivotPoint);\n      });\n      return momentArrows;\n    }\n\n    drawForceArrow(forceArrowData, materialProps, insertionPoints, arrowData) {\n      let forceArrows = new this.THREE.Group();\n      let headSize = forceArrowData.headSize;\n      let height = forceArrowData.height;\n      let length = forceArrowData.length;\n      let insertionProps = {\n        insertionPoint: forceArrowData.insertionPoint,\n        directionVectorOne: forceArrowData.directionVector1,\n        directionVectorTwo: forceArrowData.directionVector2,\n        directionVectorThree: forceArrowData.directionVector3\n      };\n      let division = forceArrowData.division;\n      let tiltAngle = forceArrowData.tiltAngle;\n      insertionPoints.forEach(point => {\n        let insertionVector = this.utils.initVector3(point.insertionVector);\n        let rotationAngle = point.rotationAngle * Math.PI / 180;\n        let rotationAxis, rotationPoint;\n\n        if (rotationAngle) {\n          rotationAxis = this.utils.initVector3(point.rotationAxis);\n          rotationPoint = this.utils.initVector3(point.rotationPoint);\n        }\n\n        let newArrow;\n        if (!length.value1 && !length.value2) newArrow = this.drawPointForceArrow(forceArrowData, headSize, height, materialProps, insertionProps, arrowData);else if (length.value1 > 0 || length.value2 > 0) newArrow = this.drawLineForceArrow(forceArrowData, materialProps, insertionProps, division, tiltAngle, arrowData);\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.copy(rotationPoint);\n          pivotPoint.attach(newArrow);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(newArrow);\n        }\n\n        pivotPoint.position.add(insertionVector);\n        forceArrows.add(pivotPoint);\n      });\n      return forceArrows;\n    }\n\n    drawLineForceArrow(forceArrowData, materialProps, insertionProps, division, tiltAngle, arrowData) {\n      let pointerRadius = forceArrowData.headSize.value2 / 2;\n      let pointerHeight = forceArrowData.headSize.value1;\n      let offsetAngle = tiltAngle * Math.PI / 180;\n      let horizontalLength = forceArrowData.length.value1;\n      let depthLength = forceArrowData.length.value2;\n      let heightLeft = forceArrowData.height.value1;\n      let heightRight = forceArrowData.height.value2;\n      let lineProps = materialProps.lineProps;\n      let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n      let borderMaterial = this.getMaterialFromColor(lineProps.color);\n      let areaMaterial;\n      if (materialProps.areaProps && materialProps.areaProps.color.alpha) areaMaterial = this.getMaterialFromColor(materialProps.areaProps.color);\n      let lineForceArrow = new this.THREE.Object3D();\n      let directionsDefined = forceArrowData.directionVector1 && forceArrowData.directionVector2 && forceArrowData.directionVector3 && (forceArrowData.directionVector1.x || forceArrowData.directionVector1.y || forceArrowData.directionVector1.z) && (forceArrowData.directionVector2.x || forceArrowData.directionVector2.y || forceArrowData.directionVector2.z) && (!(heightLeft && heightRight) || forceArrowData.directionVector3.x || forceArrowData.directionVector3.y || forceArrowData.directionVector3.z);\n      let areOrthogonal = false;\n      let dirVectorNormal, dirVectorHorizontal;\n\n      if (directionsDefined) {\n        if (depthLength) {\n          dirVectorHorizontal = this.utils.initVector3(forceArrowData.directionVector2).normalize();\n          dirVectorNormal = this.utils.initVector3(forceArrowData.directionVector3).normalize();\n          let vectorOne = this.utils.initVector3(forceArrowData.directionVector1).normalize();\n          if (!dirVectorHorizontal.dot(dirVectorNormal) && !vectorOne.dot(dirVectorHorizontal) && !vectorOne.dot(dirVectorNormal)) areOrthogonal = true;\n        } else {\n          dirVectorHorizontal = this.utils.initVector3(forceArrowData.directionVector2).normalize();\n          dirVectorNormal = new this.THREE.Vector3().crossVectors(dirVectorHorizontal, this.utils.initVector3(forceArrowData.directionVector1).negate().normalize());\n          if (!dirVectorHorizontal.dot(dirVectorNormal)) areOrthogonal = true;\n        }\n\n        if (areOrthogonal && insertionProps.insertionPoint && !(depthLength && !horizontalLength) && tiltAngle > -90 && tiltAngle < 90) {\n          let botAxisPointOne = new this.THREE.Vector3(0, 0, 0);\n          let botAxisPointTwo = new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle), 0);\n          let topAxisPointOne = new this.THREE.Vector3(0, heightLeft, 0);\n          let topAxisPointTwo = new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle) + heightRight, 0);\n          let botLine = this.GraphicsThree3D.drawLine(botAxisPointOne, botAxisPointTwo, lineProps);\n          let topLine = this.GraphicsThree3D.drawLine(topAxisPointOne, topAxisPointTwo, lineProps);\n          botLine.renderOrder = this.lineRenderOrder;\n          topLine.renderOrder = this.lineRenderOrder;\n          lineForceArrow.add(botLine);\n          lineForceArrow.add(topLine);\n\n          if (depthLength > 0) {\n            let depthBotAxisPointOne = botAxisPointOne.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthBotAxisPointTwo = botAxisPointTwo.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthTopAxisPointOne = topAxisPointOne.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthTopAxisPointTwo = topAxisPointTwo.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n            let depthBotLine = this.GraphicsThree3D.drawLine(depthBotAxisPointOne, depthBotAxisPointTwo, lineProps);\n            let depthTopLine = this.GraphicsThree3D.drawLine(depthTopAxisPointOne, depthTopAxisPointTwo, lineProps);\n            depthBotLine.renderOrder = this.lineRenderOrder;\n            depthTopLine.renderOrder = this.lineRenderOrder;\n            lineForceArrow.add(depthBotLine);\n            lineForceArrow.add(depthTopLine);\n            let pointContainer = [[botAxisPointOne, depthBotAxisPointOne], [botAxisPointTwo, depthBotAxisPointTwo], [topAxisPointOne, depthTopAxisPointOne], [topAxisPointTwo, depthTopAxisPointTwo]];\n\n            if (areaMaterial) {\n              let bottomPlaneGeo = this.getPlaneGeoFromPoints([botAxisPointOne, botAxisPointTwo, depthBotAxisPointTwo, depthBotAxisPointOne]);\n              let bottomPlane = new this.THREE.Mesh(bottomPlaneGeo, areaMaterial);\n              lineForceArrow.add(bottomPlane);\n              bottomPlane.renderOrder = 102;\n\n              bottomPlane.onBeforeRender = function (renderer) {\n                renderer.clearDepth();\n              };\n\n              if (heightLeft && heightRight) {\n                let topPlaneGeo = this.getPlaneGeoFromPoints([topAxisPointOne, topAxisPointTwo, depthTopAxisPointTwo, depthTopAxisPointOne]);\n                let topPlane = new this.THREE.Mesh(topPlaneGeo, areaMaterial);\n                lineForceArrow.add(topPlane);\n                topPlane.renderOrder = 102;\n\n                topPlane.onBeforeRender = function (renderer) {\n                  renderer.clearDepth();\n                };\n              }\n            }\n\n            pointContainer.forEach(points => {\n              let lineMesh = this.GraphicsThree3D.drawLine(points[0], points[1], lineProps);\n              lineForceArrow.add(lineMesh);\n            });\n          }\n\n          let horizontalDivisions = division.value1;\n          let horizontalSegmentLength = horizontalLength / horizontalDivisions;\n          let depthDivisions = division.value2;\n          let depthSegmentLength = depthLength / depthDivisions;\n          let pointer = this.makeArrow(pointerRadius, pointerHeight, volumeMaterial, borderMaterial, lineProps);\n          pointer.rotation.z = Math.PI;\n\n          for (let i = 1; i < depthDivisions; i++) {\n            let bottomPointOne = new this.THREE.Vector3(0, 0, i * depthSegmentLength);\n            let topPointOne = bottomPointOne.clone().add(new this.THREE.Vector3(0, heightLeft, 0));\n            let bottomPointTwo = bottomPointOne.clone().add(new this.THREE.Vector3(horizontalLength * Math.cos(offsetAngle), horizontalLength * Math.sin(offsetAngle), 0));\n            let topPointTwo = bottomPointTwo.clone().add(new this.THREE.Vector3(0, heightRight, 0));\n\n            if (topPointOne.y.toFixed(2) != bottomPointOne.y.toFixed(2)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPointOne, topPointOne, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPointOne);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n\n            if (topPointTwo.y.toFixed(2) != bottomPointTwo.y.toFixed(2)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPointTwo, topPointTwo, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPointTwo);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n          }\n\n          for (let i = 0; i < horizontalDivisions + 1; i++) {\n            let bottomPoint = new this.THREE.Vector3(i * horizontalSegmentLength * Math.cos(offsetAngle), i * horizontalSegmentLength * Math.sin(offsetAngle), 0);\n            let topPoint = bottomPoint.clone().add(new this.THREE.Vector3(0, heightLeft - i * (heightLeft - heightRight) / horizontalDivisions, 0));\n\n            if (topPoint.y.toFixed(2) != bottomPoint.y.toFixed(2) && !(heightLeft && heightRight && (i == 0 || i == horizontalDivisions) && depthLength > 0 && depthDivisions == 0)) {\n              let newAxis = this.GraphicsThree3D.drawLine(bottomPoint, topPoint, lineProps);\n              newAxis.renderOrder = this.lineRenderOrder;\n              let newPointer = pointer.clone();\n              newPointer.position.add(bottomPoint);\n              lineForceArrow.add(newPointer);\n              lineForceArrow.add(newAxis);\n            }\n\n            if (depthLength > 0) {\n              let depthBottomPoint = bottomPoint.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n              let depthTopPoint = topPoint.clone().add(new this.THREE.Vector3(0, 0, depthLength));\n\n              if (depthTopPoint.y.toFixed(2) != depthBottomPoint.y.toFixed(2) && !(heightLeft && heightRight && (i == 0 || i == horizontalDivisions) && depthDivisions == 0)) {\n                let newAxis = this.GraphicsThree3D.drawLine(depthBottomPoint, depthTopPoint, lineProps);\n                newAxis.renderOrder = this.lineRenderOrder;\n                let newPointer = pointer.clone();\n                newPointer.position.add(depthBottomPoint);\n                lineForceArrow.add(newPointer);\n                lineForceArrow.add(newAxis);\n              }\n            }\n          }\n        }\n      }\n\n      if (materialProps.textProps) {\n        this.addTextToArrow(materialProps.textProps, arrowData.components.texts, lineForceArrow, insertionProps.insertionPoint);\n      }\n\n      if (directionsDefined && areOrthogonal) {\n        let insertionPoint = new this.THREE.Vector3(0, 0, 0);\n\n        if (insertionProps.insertionPoint) {\n          insertionPoint.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n        }\n\n        if (directionsDefined && areOrthogonal) this.GraphicsThree3D.alignObjectWithVectorsAndChangePos(lineForceArrow, insertionPoint, dirVectorNormal, dirVectorHorizontal);\n      } else {\n        let insertionPoint = new this.THREE.Vector3(0, 0, 0);\n\n        if (insertionProps.insertionPoint) {\n          insertionPoint.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n        }\n\n        lineForceArrow.position.add(insertionPoint);\n      }\n\n      this.addToDimChainCollisionDetection(lineForceArrow);\n      return lineForceArrow;\n    }\n\n    drawPointForceArrow(forceArrowData, headSize, height, materialProps, insertionProps, arrowData) {\n      let pointForceArrow = new this.THREE.Object3D();\n\n      if (forceArrowData.directionVector1 && (forceArrowData.directionVector1.x || forceArrowData.directionVector1.y || forceArrowData.directionVector1.z)) {\n        let directionVector = this.utils.initVector3(forceArrowData.directionVector1);\n        let pointerRadius = headSize.value2 / 2;\n        let pointerHeight = headSize.value1;\n        let volumeMaterial = this.getMaterialFromColor(materialProps.volumeProps.color);\n        let borderMaterial = this.getMaterialFromColor(materialProps.lineProps.color);\n        let pointer = this.makeArrow(pointerRadius, pointerHeight, volumeMaterial, borderMaterial, materialProps.lineProps);\n        pointer.rotation.z = Math.PI;\n        let axisHeight = height.value1;\n        let axisPointOne = new this.THREE.Vector3(0, 0, 0);\n        let axisPointTwo = new this.THREE.Vector3(0, axisHeight, 0);\n        let axis = this.GraphicsThree3D.drawLine(axisPointOne, axisPointTwo, materialProps.lineProps);\n        let arrow = new this.THREE.Object3D();\n        let arrowParts = new this.THREE.Object3D();\n        arrowParts.add(pointer);\n        arrowParts.add(axis);\n        arrow.add(arrowParts);\n        arrowParts.rotation.x = -Math.PI / 2;\n        arrow.lookAt(directionVector);\n        pointForceArrow.add(arrow);\n      }\n\n      pointForceArrow.position.set(insertionProps.insertionPoint.x, insertionProps.insertionPoint.y, insertionProps.insertionPoint.z);\n\n      if (materialProps.textProps) {\n        this.addTextToArrow(materialProps.textProps, arrowData.components.texts, pointForceArrow, insertionProps.insertionPoint);\n      }\n\n      this.addToDimChainCollisionDetection(pointForceArrow);\n      return pointForceArrow;\n    }\n\n    addTextToArrow(textProps, texts, object, objectInsertionPoint) {\n      if (textProps.type && textProps.style && textProps.color.alpha) {\n        let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n\n        if (texts) {\n          texts.forEach(text => {\n            if (text.insertionPoint && text.directionVector && text.normalVector && (text.directionVector.x || text.directionVector.y || text.directionVector.z) && (text.normalVector.x || text.normalVector.y || text.normalVector.z) && text.content) {\n              let newText = this.GraphicsThree3D.TextLoader.drawText(text, textProps, font);\n\n              if (objectInsertionPoint) {\n                newText.position.sub(new this.THREE.Vector3(objectInsertionPoint.x, objectInsertionPoint.y, objectInsertionPoint.z));\n              }\n\n              object.add(newText);\n            }\n          });\n        }\n      }\n    }\n\n    getPlaneGeoFromPoints(points) {\n      let bottomPlaneGeo = new this.THREE.Geometry();\n      bottomPlaneGeo.vertices.push(...points);\n      bottomPlaneGeo.faces.push(new this.THREE.Face3(0, 1, 2), new this.THREE.Face3(0, 2, 3));\n      bottomPlaneGeo.faceVertexUvs[0].push([new this.THREE.Vector2(0, 0), new this.THREE.Vector2(1, 0), new this.THREE.Vector2(1, 1)], [new this.THREE.Vector2(0, 0), new this.THREE.Vector2(1, 1), new this.THREE.Vector2(0, 1)]);\n      bottomPlaneGeo.uvsNeedUpdate = true;\n      bottomPlaneGeo.computeFaceNormals();\n      bottomPlaneGeo.computeVertexNormals();\n      return bottomPlaneGeo;\n    }\n\n    getMaterialFromColor(color, isLine) {\n      let materialOptions = {\n        color: \"rgb(\" + color.red + \", \" + color.green + \", \" + color.blue + \")\",\n        polygonOffset: true,\n        polygonOffsetFactor: 1,\n        polygonOffsetUnits: 1,\n        transparent: true,\n        opacity: color.alpha / 255,\n        side: this.THREE.DoubleSide\n      };\n      let material = isLine ? new this.THREE.LineBasicMaterial(materialOptions) : new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    makeArrow(radius, height, areaMaterial, borderMaterial, lineProps, forArc = false) {\n      let localAreaMaterial = areaMaterial.clone();\n      localAreaMaterial.side = this.THREE.FrontSide;\n      let coneMesh, coneGeo, edgePts;\n\n      if (radius && height) {\n        coneGeo = new this.THREE.ConeGeometry(radius, height, 32);\n        coneGeo.translate(0, -height / 2, 0);\n        coneMesh = new this.THREE.Mesh(coneGeo, localAreaMaterial);\n        edgePts = coneGeo.vertices.slice(1, 33);\n        let line = this.utils.drawConnectedLines(edgePts, lineProps, true);\n        line.renderOrder = this.lineRenderOrder;\n        coneMesh.add(line);\n        let borderMesh;\n\n        if (lineProps.thickness && lineProps.color.alpha) {\n          let scale = 1 + lineProps.thickness / 4;\n          borderMesh = new this.THREE.Mesh(coneGeo, borderMaterial);\n          borderMesh.scale.multiplyScalar(scale);\n          borderMesh.material.side = this.THREE.BackSide;\n          borderMesh.position.y = (height - 1) * lineProps.thickness / 4;\n          coneMesh.add(borderMesh);\n          if (!forArc) coneMesh.position.y += 2 * lineProps.thickness;\n        }\n\n        let order = -99;\n        coneMesh.renderOrder = order;\n        if (borderMesh) borderMesh.renderOrder = order + 1;\n      } else coneMesh = new this.THREE.Mesh();\n\n      return coneMesh;\n    }\n\n  }\n\n  _exports.ArrowLoader = ArrowLoader;\n});","define(\"m08-2020/lib/FigureLoaders/DimensionalChainLoader\", [\"exports\", \"three\", \"m08-2020/lib/Utils\", \"m08-2020/lib/ThreeExtensions/VectorString\"], function (_exports, THREE, _Utils, _VectorString) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DimensionalChainLoader = void 0;\n  const utils = new _Utils.Utils(THREE);\n  const _planes = [{\n    normal: new THREE.Vector3(0, 0, 1),\n    vertical: new THREE.Vector3(0, 1, 0),\n    horizontal: new THREE.Vector3(1, 0, 0)\n  }, {\n    normal: new THREE.Vector3(-1, 0, 0),\n    vertical: new THREE.Vector3(0, 1, 0),\n    horizontal: new THREE.Vector3(0, 0, 1)\n  }, {\n    normal: new THREE.Vector3(0, 1, 0),\n    vertical: new THREE.Vector3(0, 0, -1),\n    horizontal: new THREE.Vector3(1, 0, 0)\n  }];\n\n  class DimensionalChainLoader {\n    constructor(GraphicsThree3D) {\n      this.THREE = THREE;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.DimChainViewController = GraphicsThree3D.DimChainViewController;\n      this.utils = utils;\n      this.intersectionDetection = GraphicsThree3D.DimensionalChainIntersectionDetection; ///\n\n      this.drawMaskette = this.drawMaskette.bind(this);\n    }\n\n    drawDimensionalChains(dimensionalChains) {\n      const self = this;\n      if (dimensionalChains && dimensionalChains.length > 0 && !this.isOldDimChainJSON(dimensionalChains[0])) this.sortChainsByRank(dimensionalChains);\n      this.DimChainViewController.clearState();\n      drawChains3D();\n\n      _planes.forEach(plane => drawChains2D(plane));\n\n      this.intersectionDetection.chainsForZoom = this.intersectionDetection.chainArr;\n      this.intersectionDetection.chainArr = [];\n\n      function drawChains3D() {\n        for (let dimensionalChain of dimensionalChains) {\n          let dimChains = self.drawMaskette(dimensionalChain);\n\n          for (let chainObj of dimChains) {\n            addChainToThreeObjects(self.GraphicsThree3D.ThreeObjects, dimensionalChain, chainObj);\n            self.GraphicsThree3D.scene.add(chainObj);\n            self.GraphicsThree3D.addObjToDynamicSectionalPlanes(dimensionalChain, chainObj, \"dimensionalChains\");\n          }\n\n          self.DimChainViewController.addChainsToView3D(dimensionalChain.id, dimChains);\n        }\n      }\n\n      function drawChains2D({\n        normal,\n        vertical,\n        horizontal\n      }) {\n        self.intersectionDetection.chainArr = [];\n\n        for (let dimensionalChain of dimensionalChains) {\n          let view2dAlignments = dimensionalChain.alignments.views2d;\n\n          if (view2dAlignments) {\n            view2dAlignments.forEach(alignment => {\n              if (dimensionalChain.alignments.textAlignment === 1) {\n                let pointsDir = self.GraphicsThree3D.DrawingPlaneLoader.getTextDirectionVec({\n                  drawingVerticalVec: vertical,\n                  drawingHorizontalVec: horizontal\n                }, dimensionalChain.referencePoints);\n                alignment.normalVector = normal.clone();\n                alignment.directionVector = pointsDir;\n              }\n            });\n            let chainData = { ...dimensionalChain,\n              alignments: { ...dimensionalChain.alignments,\n                textAlignment: 2\n              }\n            };\n            view2dAlignments.forEach(alignment => {\n              let normalVector = new self.THREE.Vector3().crossVectors(alignment.offsetVector, alignment.directionVector).normalize();\n              let dotProdAbs = Math.abs(normalVector.dot(normal));\n\n              if (dotProdAbs === 1) {\n                let chains = self.drawMaskette(chainData, alignment);\n\n                let keyNormal = _VectorString.VectorString.vecToString(alignment.normalVector);\n\n                self.DimChainViewController.initView2D(keyNormal, dimensionalChain.id);\n\n                for (let ch of chains) {\n                  self.GraphicsThree3D.scene.add(ch);\n                  ch.visible = false;\n                  self.DimChainViewController.addChainToView2D(keyNormal, dimensionalChain.id, ch);\n                  addChainToThreeObjects(self.GraphicsThree3D.ThreeObjects, dimensionalChain, ch);\n                }\n              }\n            });\n          }\n        }\n      }\n    }\n\n    sortChainsByRank(dimensionalChains) {\n      dimensionalChains.sort((a, b) => b.alignments.dimensionalChainArrangement.rankOrder - a.alignments.dimensionalChainArrangement.rankOrder);\n    }\n\n    isOldDimChainJSON(chainData) {\n      return chainData.alignments.view3d && chainData.alignments.view3d.hasOwnProperty(\"textAlignment\") || chainData.alignments.views2d && chainData.alignments.views2d[0].hasOwnProperty(\"textAlignment\");\n    }\n\n    drawMaskette(dimensionalChain, alignment2D = null) {\n      let meshArray = [];\n      let alignments = dimensionalChain.alignments;\n      let alignmentData = alignments.view3d;\n      let addToVisControl = true;\n      if (alignment2D) alignmentData = correct2DAlignmentData(alignmentData, alignment2D);\n      if (this.isOldDimChainJSON(dimensionalChain)) alignments = correctAlignmentsForOldJSON(alignments, alignmentData);\n      let alignmentOffset = alignmentData.offsetVector;\n\n      if (alignmentOffset && (alignmentOffset.x || alignmentOffset.y || alignmentOffset.z)) {\n        ///\n        if (alignments.dimensionalChainArrangement.type === 1) {\n          let chainType = this.getChainType(dimensionalChain);\n          let offsetVector = this.getChainOffsetVector(dimensionalChain, chainType, alignmentData);\n          alignmentData.offsetVector = {\n            x: offsetVector.x,\n            y: offsetVector.y,\n            z: offsetVector.z\n          };\n        } ///\n\n\n        let chain = this.drawDimensionalChain(dimensionalChain, alignmentData);\n\n        if (chain) {\n          dimensionalChain.insertionPoints.forEach(insertionPoint => {\n            let insertionVector = insertionPoint.insertionVector;\n            let objectRotationAngle = insertionPoint.rotationAngle;\n            let chainObject = new this.THREE.Object3D();\n\n            if (objectRotationAngle) {\n              objectRotationAngle = objectRotationAngle * Math.PI / 180;\n              let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n              let rotationPoint = this.utils.initVector3(insertionPoint.rotationPoint);\n              chainObject.position.copy(rotationPoint);\n              chainObject.updateMatrix();\n              chainObject.rotateOnAxis(rotationAxis, objectRotationAngle);\n            }\n\n            chainObject.position.add(insertionVector);\n            chain.applyMatrix4(chainObject.matrix);\n            chain.traverse(child => {\n              if (child.isDynamicText) this.GraphicsThree3D.DynamicTextsController.addToDynamicTexts(child);\n            });\n            if (addToVisControl) this.GraphicsThree3D.addObjectToVisControl(chain, \"dimensionalChain\");\n            meshArray.push(chain);\n          });\n        }\n      }\n\n      return meshArray; // Helper functions\n\n      function correct2DAlignmentData(alignmentData, alignment2D) {\n        return { ...alignmentData,\n          ...alignment2D,\n          for2DGeneration: true\n        };\n      }\n\n      function correctAlignmentsForOldJSON(alignments, alignmentData) {\n        return { ...alignments,\n          textAlignment: alignmentData.textAlignment,\n          textArrangement: 2,\n          dimensionalChainArrangement: {\n            distance: 0,\n            rankOrder: 0,\n            type: 2\n          }\n        };\n      }\n    }\n\n    drawDimensionalChain(dimensionalChain, alignmentData) {\n      let referencePoints = dimensionalChain.referencePoints; // Components\n\n      let hasBaseline = dimensionalChain.components.baseLine;\n      let baseLineOverhang = dimensionalChain.components.baseLineOverhang;\n      let endLine = dimensionalChain.components.endLine;\n      let endSymbol = dimensionalChain.components.endSymbol;\n      let texts = dimensionalChain.components.texts; // Alignment\n\n      let alignments = dimensionalChain.alignments;\n      let alignmentOffset = this.utils.initVector3(alignmentData.offsetVector); // Properties\n\n      let lineProps = dimensionalChain.properties.line;\n      let areaProps = dimensionalChain.properties.area;\n      let textProps = dimensionalChain.properties.text; // Materials\n\n      let areaMaterial = areaProps ? this.GraphicsThree3D.getMaterialFromColor(areaProps.color) : null;\n      if (areaMaterial) areaMaterial.side = this.THREE.FrontSide;\n      let chain = new this.THREE.Object3D();\n      let chainType = this.getChainType(dimensionalChain);\n\n      if (chainType === \"linear\") {\n        let startPoint = this.utils.initVector3(referencePoints[0]);\n        let endPoint = this.utils.initVector3(referencePoints[referencePoints.length - 1]);\n        let chainDirection = new this.THREE.Vector3().copy(endPoint).sub(startPoint).normalize();\n        let chainNormal = new this.THREE.Vector3().crossVectors(chainDirection, alignmentOffset.clone().normalize()).normalize();\n        let dx = endPoint.x - startPoint.x;\n        let dy = endPoint.y - startPoint.y; // baseLine\n\n        if (hasBaseline) {\n          let lineMesh = this.utils.drawConnectedLines([startPoint, endPoint], lineProps);\n          chain.add(lineMesh); ///\n          // console.log(startPoint, endPoint)\n\n          let lineForIntersection = this.utils.drawLine(startPoint, endPoint, lineProps); // lineForIntersection.geometry.computeBoundingBox();\n          // lineForIntersection.updateMatrix();\n          // console.log(lineForIntersection)\n\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(lineForIntersection);\n          intersectionObjects.forEach(object => {\n            object.position.add(alignmentOffset);\n            object.updateMatrixWorld();\n          });\n          this.intersectionDetection.chainArr.push(intersectionObjects);\n        } // baseLineOverhang\n\n\n        if (baseLineOverhang && baseLineOverhang.position && baseLineOverhang.length) {\n          let overhangLength = baseLineOverhang.length;\n          let overhangPosition = baseLineOverhang.position;\n          let offset = new this.THREE.Vector3().copy(chainDirection).multiplyScalar(overhangLength);\n\n          if (overhangPosition === 1 || overhangPosition === 2) {\n            let overhangPoint = startPoint.clone().sub(offset);\n            let lineMesh = this.utils.drawConnectedLines([startPoint, overhangPoint], lineProps);\n            chain.add(lineMesh);\n          }\n\n          if (overhangPosition === 1 || overhangPosition === 3) {\n            let overhangPoint = endPoint.clone().add(offset);\n            let lineMesh = this.utils.drawConnectedLines([endPoint, overhangPoint], lineProps);\n            chain.add(lineMesh);\n          }\n        }\n\n        for (let i = 0; i < referencePoints.length - 1; i++) {\n          let referencePointOne = this.utils.initVector3(referencePoints[i]);\n          let referencePointTwo = this.utils.initVector3(referencePoints[i + 1]); // endLine\n\n          if (endLine) {\n            let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n            let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, referencePointOne);\n            chain.add(lineMesh);\n\n            if (i == referencePoints.length - 2) {\n              let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, referencePointTwo);\n              chain.add(lineMesh);\n            }\n          } // endSymbol\n\n\n          if (endSymbol) {\n            let endSymbolType = endSymbol.type;\n\n            if (endSymbolType === 1 && endSymbol.size) {\n              let circleRadius = endSymbol.size / 2;\n              let circle = this.drawEndSymbolCircle(circleRadius, areaMaterial, lineProps, chainNormal, referencePointOne);\n              chain.add(circle);\n\n              if (i == referencePoints.length - 2) {\n                let circle = this.drawEndSymbolCircle(circleRadius, areaMaterial, lineProps, chainNormal, referencePointTwo);\n                chain.add(circle);\n              }\n            } else if (endSymbolType === 2) {\n              let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n              let lineMesh = this.drawEndSymbolLine(offsetDir, chainNormal, referencePointOne, endSymbol, lineProps);\n              chain.add(lineMesh);\n\n              if (i == referencePoints.length - 2) {\n                let lineMesh = this.drawEndSymbolLine(offsetDir, chainNormal, referencePointTwo, endSymbol, lineProps);\n                chain.add(lineMesh);\n              }\n            } else if (endSymbolType === 3) {\n              let arrowRadius = endSymbol.size / 4;\n              let arrowLength = endSymbol.size;\n              let arrowOne = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              let arrowTwo = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              arrowOne.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n              arrowTwo.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n              arrowOne.position.add(referencePointOne);\n              arrowTwo.position.add(referencePointTwo);\n              chain.add(arrowOne);\n              chain.add(arrowTwo);\n            } else if (endSymbolType === 4) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n              arrow.position.add(referencePointOne);\n              chain.add(arrow);\n\n              if (i == referencePoints.length - 2) {\n                let arrowRadius = endSymbol.size / 2;\n                let arrowSize = endSymbol.size;\n                let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n                arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.atan(dx / dy));\n                arrow.position.add(referencePointTwo);\n                chain.add(arrow);\n              }\n            } else if (endSymbolType === 5) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n              arrow.position.add(referencePointOne);\n              chain.add(arrow);\n\n              if (i == referencePoints.length - 2) {\n                let arrowRadius = endSymbol.size / 2;\n                let arrowSize = endSymbol.size;\n                let arrow = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n                arrow.rotateOnWorldAxis(new this.THREE.Vector3(0, 0, 1), -Math.PI - Math.atan(dx / dy));\n                arrow.position.add(referencePointTwo);\n                chain.add(arrow);\n              }\n            }\n          }\n        } // text\n\n\n        if (this.canDrawText(dimensionalChain)) {\n          let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n\n          if (alignments.textAlignment === 1) {\n            for (let text of texts) {\n              let textInsideChain = this.textInsideChain(text, startPoint, endPoint, chainDirection);\n\n              if (textInsideChain) {\n                let textInsertionPoint = text.insertionPoint;\n                let textOffset = text.offset;\n                let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n                let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                let textPosition = this.utils.initVector3(textInsertionPoint);\n                textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                let textMesh = this.drawDynamicText(text, textPosition, textProps, font);\n                chain.add(textMesh);\n              }\n            }\n          } else if (alignments.textAlignment === 2 && alignmentData.normalVector && alignmentData.directionVector) {\n            let textDataArr = [];\n            let tempIntersectionTexts = [];\n            let isIntersection = false;\n\n            for (let text of texts) {\n              if (this.textInsideChain(text, startPoint, endPoint, chainDirection)) {\n                let textInsertionPoint = text.insertionPoint;\n                let textOffset = text.offset;\n                let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                let offsetDir = this.utils.initVector3(alignmentOffset).normalize();\n                let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                let textPosition = this.utils.initVector3(textInsertionPoint);\n                textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                let textData = {\n                  content: text.content,\n                  id: text.id,\n                  insertionPoint: textPosition,\n                  offset: text.offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                }; ///\n\n                let normalVector = this.utils.initVector3(alignmentData.normalVector);\n                let directionVector = this.utils.initVector3(alignmentData.directionVector);\n\n                if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n                  let offsetVec = this.utils.initVector3(alignmentOffset);\n                  textData.negativeOffset = textOffsetDir.clone().normalize().multiplyScalar(2 * textData.offset);\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  box.position.add(offsetVec);\n\n                  if (alignments.textAlignment === 2 && alignments.textArrangement === 1) {\n                    let linePtsArr = [];\n                    linePoints.forEach(point => point.add(offsetVec));\n\n                    for (let i = 0; i < linePoints.length - 1; i++) linePtsArr.push([linePoints[i], linePoints[i + 1]]);\n\n                    isIntersection = this.intersectionDetection.checkCollision(linePtsArr, tempIntersectionTexts);\n                    let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n                    tempIntersectionTexts.push(intersectionObjects);\n                  }\n\n                  textDataArr.push({\n                    textData,\n                    box\n                  });\n                }\n              }\n            }\n\n            for (let i = 0; i < textDataArr.length; i++) {\n              let textData = textDataArr[i].textData;\n              let box = textDataArr[i].box;\n\n              if (isIntersection && alignments.textAlignment === 2 && alignments.textArrangement === 1) {\n                textData.offset = textDataArr[0].textData.offset;\n\n                if (i % 2 === 1) {\n                  textData.insertionPoint.sub(textData.negativeOffset);\n                  box.position.sub(textData.negativeOffset);\n                }\n              }\n\n              let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n              this.intersectionDetection.chainArr.push(intersectionObjects);\n              let textMesh = this.GraphicsThree3D.TextLoader.drawText(textData, textProps, font);\n              chain.add(textMesh);\n            }\n          }\n        }\n\n        chain.position.add(this.utils.initVector3(alignmentOffset));\n      } else if (chainType === \"arc\") {\n        let centerPoint = this.utils.initVector3(dimensionalChain.referencePoints[0]);\n        let arcRadius = alignmentOffset.length(); // baseLine\n\n        let rotationAngle = dimensionalChain.shape.rotationAngle;\n        let rotationAxis = new this.THREE.Vector3(0, 0, 1);\n        let shapeAxis = this.utils.initVector3(dimensionalChain.shape.rotationAxis).normalize();\n        let quaternion = new this.THREE.Quaternion().setFromUnitVectors(shapeAxis, rotationAxis);\n        alignmentOffset.applyQuaternion(quaternion);\n        let offsetVectorNormalized = alignmentOffset.clone().normalize();\n        let startAngle = -Math.acos(offsetVectorNormalized.x) + this.GraphicsThree3D.degreeToRadians(rotationAngle);\n        let endAngle = -Math.acos(offsetVectorNormalized.x);\n        let isClockwise = rotationAngle > 0;\n        let curve = new this.THREE.EllipseCurve(0, 0, arcRadius, arcRadius, startAngle, endAngle, isClockwise ? true : false, 0);\n        let curvePoints = curve.getPoints(50).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n\n        if (hasBaseline) {\n          let ellipse = this.utils.drawConnectedLines(curvePoints, lineProps);\n          chain.add(ellipse); ///\n\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(ellipse);\n          intersectionObjects.forEach(object => {\n            object.lookAt(shapeAxis);\n            object.position.add(centerPoint);\n            object.updateMatrixWorld();\n          });\n          this.intersectionDetection.chainArr.push(intersectionObjects);\n        }\n\n        let pseudoReferencePoints = [curvePoints[0], curvePoints[curvePoints.length - 1]];\n        pseudoReferencePoints.forEach((pseudoReferencePoint, i) => {\n          // baseLineOverhang\n          if (baseLineOverhang && baseLineOverhang.position) {\n            let overhangLength = baseLineOverhang.length;\n            let overHangPosition = baseLineOverhang.position;\n            let pivotPoint = new this.THREE.Vector3(0, 0, 0);\n            let overhangPoint;\n\n            if (overHangPosition == 1) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? overhangLength : -overhangLength, 0, 0);\n            } else if (overHangPosition == 2) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? 0 : -overhangLength, 0, 0);\n            } else if (overHangPosition == 3) {\n              overhangPoint = new this.THREE.Vector3(i == 0 ? overhangLength : 0, 0, 0);\n            }\n\n            if (!pivotPoint.equals(overhangPoint)) {\n              let lineMesh = this.utils.drawConnectedLines([pivotPoint, overhangPoint], lineProps);\n              lineMesh.rotateOnAxis(rotationAxis, i == 0 ? Math.PI / 2 + startAngle : Math.PI / 2 + endAngle);\n              if (rotationAngle < 0) lineMesh.rotateOnAxis(rotationAxis, Math.PI);\n              lineMesh.position.copy(pseudoReferencePoint);\n              chain.add(lineMesh);\n            }\n          } // endLine\n\n\n          if (endLine) {\n            let offsetDir = offsetVectorNormalized.clone();\n            if (i === 0) offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle));\n            let lineMesh = this.drawEndLine(offsetDir, endLine, lineProps, pseudoReferencePoint);\n            chain.add(lineMesh);\n          } // endSymbol\n\n\n          if (endSymbol) {\n            let endSymbolType = endSymbol.type;\n\n            if (endSymbolType === 1 && endSymbol.size) {\n              let circleRadius = endSymbol.size / 2;\n              let circle = this.makeCircleWithEdges(circleRadius, areaMaterial, lineProps);\n              circle.position.copy(pseudoReferencePoint);\n              chain.add(circle);\n            } else if (endSymbolType === 2) {\n              let offsetDir = offsetVectorNormalized.clone();\n              if (i === 0) offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle));\n              let lineMesh = this.drawEndSymbolLine(offsetDir, rotationAxis, pseudoReferencePoint, endSymbol, lineProps, true);\n              chain.add(lineMesh);\n            } else if (endSymbolType === 3) {\n              let arrowRadius = endSymbol.size / 4;\n              let arrowLength = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowLength, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? startAngle : Math.PI + endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            } else if (endSymbolType === 4) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? startAngle : endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            } else if (endSymbolType === 5) {\n              let arrowRadius = endSymbol.size / 2;\n              let arrowSize = endSymbol.size;\n              let arrowMesh = this.makeArrow(arrowRadius, arrowSize, areaMaterial, lineProps);\n              arrowMesh.rotateOnAxis(rotationAxis, i == 0 ? Math.PI + startAngle : Math.PI + endAngle);\n              arrowMesh.position.copy(pseudoReferencePoint);\n              chain.add(arrowMesh);\n            }\n          }\n        }); // text\n\n        let canDrawText = this.canDrawText(dimensionalChain);\n\n        if (canDrawText) {\n          let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n\n          for (let i = 0; i < texts.length; i++) {\n            let textContent = texts[i].content;\n            let textInsertionPoint = texts[i].insertionPoint;\n            let referencePoint = dimensionalChain.referencePoints[0];\n\n            if (textContent && textInsertionPoint && this.utils.checkVectorsEquality(textInsertionPoint, referencePoint)) {\n              let textOffset = texts[i].offset;\n              let offsetDir = new this.THREE.Vector3().copy(alignmentOffset);\n              offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle / 2));\n              let textPosition = new this.THREE.Vector3().copy(offsetDir);\n              offsetDir = offsetDir.normalize();\n              textPosition.add(offsetDir.multiplyScalar(textOffset));\n\n              if (alignments.textAlignment === 1) {\n                let textMesh = this.drawDynamicText(texts[i], textPosition, textProps, font);\n                chain.add(textMesh);\n              } else if (alignments.textAlignment === 2 && alignmentData.normalVector && alignmentData.directionVector) {\n                let textData = {\n                  content: texts[i].content,\n                  id: texts[i].id,\n                  insertionPoint: textPosition,\n                  offset: texts[i].offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                };\n                let textMesh = this.GraphicsThree3D.TextLoader.drawText(textData, textProps, font);\n                chain.add(textMesh); ///\n\n                let normalVector = this.utils.initVector3(alignmentData.normalVector);\n                let directionVector = this.utils.initVector3(alignmentData.directionVector);\n\n                if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n                  let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                  let pivotObj = new this.THREE.Object3D();\n                  pivotObj.attach(box);\n                  pivotObj.lookAt(shapeAxis);\n                  pivotObj.position.add(centerPoint);\n                  pivotObj.updateMatrixWorld();\n                  pivotObj.children.forEach(child => child.applyMatrix4(pivotObj.matrixWorld));\n                  let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(box);\n                  this.intersectionDetection.chainArr.push(intersectionObjects);\n                }\n              }\n            }\n          }\n        }\n\n        chain.lookAt(shapeAxis);\n        chain.position.add(centerPoint);\n      }\n\n      return chain;\n    }\n\n    makeTextBoxWithLines(text, textProps, font) {\n      let normalVector = this.utils.initVector3(text.normalVector);\n      let directionVector = this.utils.initVector3(text.directionVector);\n      let textInsertionPoint = text.insertionPoint;\n      let textGeo = this.makeTextGeoLight(text, textProps, font);\n      textGeo.computeBoundingBox();\n      let bboxMax = textGeo.boundingBox.max;\n      let bboxMin = textGeo.boundingBox.min;\n      let linePoints = [new this.THREE.Vector3(bboxMin.x, bboxMax.y, 0), new this.THREE.Vector3(bboxMin.x, bboxMin.y, 0), new this.THREE.Vector3(bboxMax.x, bboxMin.y, 0), new this.THREE.Vector3(bboxMax.x, bboxMax.y, 0)];\n      let boxWidth = bboxMax.x - bboxMin.x;\n      let boxHeight = bboxMax.y - bboxMin.y;\n      let boxDepth = 0.1;\n      let textMaterial = new this.THREE.MeshBasicMaterial({\n        color: 0xff0000\n      });\n      let boxGeometry = new this.THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n      let textMeshBox = new this.THREE.Mesh(boxGeometry, textMaterial);\n      this.GraphicsThree3D.alignObjectWithVectorsAndChangePos(textMeshBox, textInsertionPoint, normalVector, directionVector);\n      textMeshBox.updateMatrix();\n      linePoints.forEach(point => point.applyMatrix4(textMeshBox.matrix));\n      return [textMeshBox, linePoints];\n    }\n\n    textInsideChain(textData, startPoint, endPoint, chainDirection) {\n      const TOLERANCE = 0.0001;\n      let textInsertionPoint = textData.insertionPoint;\n      let textContent = textData.content;\n      let insideChain;\n\n      if (textInsertionPoint) {\n        let textInsertionVector = this.utils.initVector3(textInsertionPoint); // let insertionLength = textInsertionVector.length();\n\n        textInsertionVector.sub(startPoint).normalize();\n        insideChain = textContent && this.utils.initVector3(textInsertionPoint).distanceTo(startPoint) <= endPoint.distanceTo(startPoint) && (chainDirection.dot(textInsertionVector) > 1 - TOLERANCE || !textInsertionVector.length());\n      }\n\n      return insideChain;\n    }\n\n    canDrawText(chainData) {\n      let texts = chainData.components.texts;\n      let alignments = chainData.alignments;\n      let textProps = chainData.properties.text;\n      return texts && textProps.type && textProps.style && alignments.textAlignment && alignments.textArrangement && textProps.size && textProps.heightWidthRatio;\n    }\n\n    makeTextGeoLight(text, textProps, font) {\n      let textContent = text.content;\n      let fontSize = textProps.size;\n      let heightWidthRatio = textProps.heightWidthRatio;\n      let textGeo = new this.THREE.TextBufferGeometry(textContent, {\n        font: font,\n        size: fontSize,\n        height: 0.1,\n        curveSegments: 1\n      });\n      textGeo.scale(heightWidthRatio, 1, 1);\n      textGeo.center();\n      return textGeo;\n    }\n\n    drawEndLine(offsetDir, lineData, lineProps, linePos) {\n      let [ptOne, ptTwo] = this.getEndLinePts(lineData, offsetDir);\n      let lineMesh = this.utils.drawConnectedLines([ptOne, ptTwo], lineProps);\n      lineMesh.position.add(linePos);\n      return lineMesh;\n    }\n\n    getEndLinePts(lineData, offsetDir) {\n      let length = lineData.lenght;\n      let overhang = lineData.overhang;\n      length *= -1; // console.log(lineData, offsetDir)\n\n      let ptOne = offsetDir.clone().multiplyScalar(length);\n      let ptTwo = offsetDir.clone().multiplyScalar(overhang);\n      return [ptOne, ptTwo];\n    }\n\n    drawEndSymbolCircle(radius, areaMaterial, lineProps, normal, position) {\n      let circle = this.makeCircleWithEdges(radius, areaMaterial, lineProps);\n      circle.lookAt(normal);\n      circle.position.copy(position);\n      return circle;\n    }\n\n    drawEndSymbolLine(offsetDir, normalVector, linePos, symbolData, lineProps, forArc = false) {\n      let lineSize = symbolData.size;\n      let ptOne = offsetDir.clone().multiplyScalar(lineSize / 2);\n      let ptTwo = offsetDir.clone().multiplyScalar(-lineSize / 2);\n      let rotation = Math.PI / 4;\n      if (forArc) rotation *= -1;\n      ptOne.applyAxisAngle(normalVector, rotation);\n      ptTwo.applyAxisAngle(normalVector, rotation);\n      let lineMesh = this.utils.drawConnectedLines([ptOne, ptTwo], lineProps);\n      lineMesh.position.add(linePos);\n      return lineMesh;\n    }\n\n    makeArrow(radius, height, areaMaterial, lineProps) {\n      let arrowMesh;\n\n      if (areaMaterial) {\n        let pts = [new this.THREE.Vector2(0, 0), new this.THREE.Vector2(-radius, -height), new this.THREE.Vector2(radius, -height)];\n        let arrowShape = new this.THREE.Shape(pts);\n        let arrowGeo = new this.THREE.ExtrudeBufferGeometry(arrowShape, {\n          depth: 0.1,\n          bevelEnabled: false\n        });\n        arrowGeo.translate(0, 0, -0.05);\n        arrowMesh = new this.THREE.Mesh(arrowGeo, areaMaterial);\n        let linePoints = pts.map(point => new this.THREE.Vector3(point.x, point.y, 0));\n        let line = this.utils.drawConnectedLines(linePoints, lineProps, true);\n        arrowMesh.add(line);\n        arrowMesh.renderOrder = 1;\n      } else {\n        arrowMesh = new this.THREE.Mesh(); // TODO: Might need better solution\n      }\n\n      return arrowMesh;\n    }\n\n    makeCircleWithEdges(radius, areaMaterial, lineProps, customParameters = null) {\n      let circleGeo;\n\n      if (customParameters) {\n        let thetaStart = customParameters.thetaStart;\n        let thetaEnd = customParameters.thetaEnd;\n        circleGeo = new this.THREE.CircleGeometry(radius, 32, thetaStart, thetaEnd);\n        let radiusTwo = customParameters.radiusTwo;\n        circleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radius, 1));\n      } else {\n        circleGeo = new this.THREE.CircleGeometry(radius, 32);\n      }\n\n      let areaMesh = new this.THREE.Mesh(circleGeo, areaMaterial);\n      let linePts = circleGeo.vertices.slice(1);\n      let line = this.utils.drawConnectedLines(linePts, lineProps, true);\n      areaMesh.add(line);\n      return areaMesh;\n    }\n\n    getChainType(dimensionalChain) {\n      let typeOfChain = dimensionalChain.shape.type;\n      let referencePoints = dimensionalChain.referencePoints;\n      if (typeOfChain === 1 && referencePoints.length >= 2) return \"linear\";else if (typeOfChain === 2 && dimensionalChain.referencePoints.length === 1 && dimensionalChain.shape.rotationAngle && dimensionalChain.shape.rotationAxis && (dimensionalChain.shape.rotationAxis.x || dimensionalChain.shape.rotationAxis.y || dimensionalChain.shape.rotationAxis.z)) return \"arc\";\n    }\n\n    getChainOffsetVector(chainData, chainType, alignmentData) {\n      let initOffset = alignmentData.offsetVector;\n      let offsetDir = this.utils.initVector3(initOffset).normalize();\n      let distance = chainData.alignments.dimensionalChainArrangement.distance;\n      let offsetDistance = new this.THREE.Vector3().copy(offsetDir).multiplyScalar(distance);\n      let offsetVector = new this.THREE.Vector3().copy(offsetDistance);\n      let meshArr = this.intersectionDetection.meshArr;\n      let chainArr = this.intersectionDetection.chainArr;\n      let isIntersection;\n\n      do {\n        isIntersection = false;\n        let linePtsArr = [];\n\n        if (chainType === \"linear\") {\n          let startPoint = this.utils.initVector3(chainData.referencePoints[0]).add(offsetVector);\n          let endPoint = this.utils.initVector3(chainData.referencePoints[chainData.referencePoints.length - 1]).add(offsetVector);\n          linePtsArr.push([startPoint, endPoint]);\n        } else {\n          let curvePoints = this.generateArcPoints(chainData, offsetVector);\n\n          for (let i = 0; i < curvePoints.length - 1; i++) linePtsArr.push([curvePoints[i], curvePoints[i + 1]]);\n        } ///\n\n\n        if (this.canDrawText(chainData) && chainData.alignments.textAlignment === 2) {\n          let texts = chainData.components.texts;\n          let textProps = chainData.properties.text;\n          let referencePoints = chainData.referencePoints;\n          let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n\n          if (chainType === \"linear\") {\n            let startPoint = this.utils.initVector3(referencePoints[0]);\n            let endPoint = this.utils.initVector3(referencePoints[referencePoints.length - 1]);\n            let chainDirection = new this.THREE.Vector3().copy(endPoint).sub(startPoint).normalize();\n            texts.forEach(text => {\n              if (this.textInsideChain(text, startPoint, endPoint, chainDirection)) {\n                let textInsertionPoint = text.insertionPoint;\n                let textOffset = text.offset;\n                let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n                let offsetDir = this.utils.initVector3(initOffset).normalize();\n                let crossPointsOffset = new this.THREE.Vector3().crossVectors(pointsDir, offsetDir);\n                let textOffsetDir = new this.THREE.Vector3().crossVectors(crossPointsOffset, pointsDir).normalize();\n                let textPosition = this.utils.initVector3(textInsertionPoint);\n                textPosition.add(textOffsetDir.multiplyScalar(textOffset));\n                let textData = {\n                  content: text.content,\n                  id: text.id,\n                  insertionPoint: textPosition,\n                  offset: text.offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                };\n                let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                linePoints.forEach(point => point.add(offsetVector));\n\n                for (let i = 0, l = linePoints.length; i < l; i++) linePtsArr.push([linePoints[i % l], linePoints[(i + 1) % l]]);\n              }\n            });\n          } else {\n            let rotationAxis = this.utils.initVector3(chainData.shape.rotationAxis);\n            let rotationAngle = chainData.shape.rotationAngle;\n            let centerPoint = this.utils.initVector3(chainData.referencePoints[0]);\n            texts.forEach(text => {\n              let textContent = text.content;\n              let textInsertionPoint = text.insertionPoint;\n              let referencePoint = chainData.referencePoints[0];\n\n              if (textContent && textInsertionPoint && this.utils.checkVectorsEquality(textInsertionPoint, referencePoint)) {\n                let textOffset = text.offset;\n                let offsetDir = new this.THREE.Vector3().copy(offsetVector);\n                offsetDir.applyAxisAngle(rotationAxis, this.GraphicsThree3D.degreeToRadians(rotationAngle / 2));\n                let textPosition = new this.THREE.Vector3().copy(offsetDir);\n                offsetDir = offsetDir.normalize();\n                textPosition.add(offsetDir.multiplyScalar(textOffset));\n                let textData = {\n                  content: text.content,\n                  id: text.id,\n                  insertionPoint: textPosition,\n                  offset: text.offset,\n                  normalVector: alignmentData.normalVector,\n                  directionVector: alignmentData.directionVector\n                };\n                let [box, linePoints] = this.makeTextBoxWithLines(textData, textProps, font);\n                linePoints.forEach(point => point.add(centerPoint));\n\n                for (let i = 0, l = linePoints.length; i < l; i++) linePtsArr.push([linePoints[i % l], linePoints[(i + 1) % l]]);\n              }\n            });\n          }\n        } ///\n\n\n        if (alignmentData.for2DGeneration) isIntersection = this.intersectionDetected2D(linePtsArr, meshArr, chainArr, alignmentData);else isIntersection = this.intersectionDetected(linePtsArr, meshArr, chainArr);\n        if (isIntersection) offsetVector.add(offsetDistance);\n      } while (isIntersection);\n\n      return offsetVector;\n    }\n\n    intersectionDetected2D(linePtsArr, meshArr, chainArr, alignmentData) {\n      let normalVector = new this.THREE.Vector3().crossVectors(alignmentData.offsetVector, alignmentData.directionVector).normalize();\n      return this.intersectionDetection.checkCollision2D(linePtsArr, meshArr, normalVector) || this.intersectionDetection.checkCollision2D(linePtsArr, chainArr, normalVector);\n    }\n\n    intersectionDetected(linePtsArr, meshArr, chainArr) {\n      return this.intersectionDetection.checkCollision(linePtsArr, meshArr) || this.intersectionDetection.checkCollision(linePtsArr, chainArr);\n    }\n\n    generateArcPoints(chainData, alignmentOffset) {\n      const DETAIL = 64;\n      let offsetVector = alignmentOffset.clone();\n      let arcRadius = offsetVector.length();\n      let rotationAngle = chainData.shape.rotationAngle;\n      let rotationAxis = new this.THREE.Vector3(0, 0, 1);\n      let shapeAxis = this.utils.initVector3(chainData.shape.rotationAxis).normalize();\n      let quaternion = new this.THREE.Quaternion().setFromUnitVectors(shapeAxis, rotationAxis);\n      offsetVector.applyQuaternion(quaternion);\n      let offsetVectorNormalized = offsetVector.clone().normalize();\n      let startAngle = -Math.acos(offsetVectorNormalized.x) + this.GraphicsThree3D.degreeToRadians(rotationAngle);\n      let endAngle = -Math.acos(offsetVectorNormalized.x);\n      let isClockwise = rotationAngle > 0;\n      let curve = new this.THREE.EllipseCurve(0, 0, arcRadius, arcRadius, startAngle, endAngle, isClockwise ? true : false, 0);\n      let numOfPoints = DETAIL * rotationAngle / 360;\n      let curvePoints = curve.getPoints(numOfPoints).map(point => new this.THREE.Vector3(point.x, point.y, 0));\n      quaternion.conjugate();\n      curvePoints.forEach(point => {\n        point.applyQuaternion(quaternion);\n        point.add(this.utils.initVector3(chainData.referencePoints[0]));\n      });\n      return curvePoints;\n    }\n\n    drawDynamicText(data, pos, props, font) {\n      let textData = {\n        content: data.content,\n        id: data.id,\n        insertionPoint: pos,\n        offset: data.offset,\n        normalVector: new this.THREE.Vector3(0, 0, 1),\n        directionVector: new this.THREE.Vector3(1, 0, 0)\n      };\n      let mesh = this.GraphicsThree3D.TextLoader.drawText(textData, props, font);\n      mesh.isDynamicText = true;\n      return mesh;\n    }\n\n  }\n\n  _exports.DimensionalChainLoader = DimensionalChainLoader;\n\n  function addChainToThreeObjects(threeObjects, chainData, chainMesh) {\n    let drawnObject = new Object();\n    drawnObject.id = chainData.id;\n    drawnObject.type = \"maskette\";\n    drawnObject.data = chainData;\n    drawnObject.sceneObject = chainMesh;\n    threeObjects.push(drawnObject);\n  }\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/ComplexVolume\", [\"exports\", \"m08-2020/lib/Utils/BooleanOperations\"], function (_exports, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ComplexVolume = void 0;\n\n  class ComplexVolume {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    addMultilayeredObjectsToScene(multilayeredObjects, wireframes, properties, insertionPoints, jsonData, drawingObject) {\n      let meshArr = [];\n      let positives = [];\n      let negatives = [];\n      multilayeredObjects.positives.forEach(objData => {\n        let [geometry, faceSideNormal, grainDirection, midVector, eulerTwo] = objData;\n        let mesh = new this.THREE.Mesh(geometry);\n        mesh.position.add(midVector);\n        mesh.setRotationFromEuler(eulerTwo);\n        mesh.updateMatrix();\n        positives.push([mesh, faceSideNormal, grainDirection]);\n      });\n      multilayeredObjects.negatives.forEach(objData => {\n        let [geometry, faceSideNormal, grainDirection, midVector, eulerTwo] = objData;\n        let mesh = new this.THREE.Mesh(geometry);\n        mesh.position.add(midVector);\n        mesh.setRotationFromEuler(eulerTwo);\n        mesh.updateMatrix();\n        negatives.push(mesh);\n      });\n      positives.forEach(positive => negatives.forEach(negative => positive[0] = BooleanOperations.doCSG(positive[0], negative, \"subtract\")));\n      positives.forEach(posData => {\n        let [mesh, faceSideNormal, grainDirection] = posData;\n        this.GraphicsThree3D.indexVolumeFaces(mesh.geometry, faceSideNormal, grainDirection);\n        let materials = this.GraphicsThree3D.getVolumeMaterials(properties.volume.color);\n        mesh.material = materials;\n        meshArr.push(mesh);\n      });\n      meshArr.forEach(mesh => this.GraphicsThree3D.DrawingObjectLoader.addToDimChainCollisionDetection(mesh, insertionPoints));\n      meshArr.forEach(mesh => {\n        let transparentMesh = new this.THREE.Mesh();\n        transparentMesh.applyMatrix4(mesh.matrix);\n        this.WireframeGenerator.applyWireframesArea(mesh, transparentMesh, properties.line, drawingObject); // TODO: Change to volume wireframe function\n\n        this.GraphicsThree3D.addObjectToScene(mesh, transparentMesh, insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject);\n      });\n    }\n\n    generateBBoxDepthAndPoints(geometry) {\n      const SIZE_DELTA = 1;\n      geometry.computeBoundingBox();\n      let bbox = geometry.boundingBox;\n      let bboxDepth = (bbox.max.z - bbox.min.z + SIZE_DELTA) * 2;\n      let pts = [new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0), new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0)].map(pt => pt.multiplyScalar(2));\n      return [bboxDepth, pts];\n    }\n\n    makeExtrudedMeshForCSG(points2D, depth) {\n      let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n        points2D,\n        depth,\n        translation: [0, 0, 0],\n        skew: [0, 0]\n      });\n      let mesh = new this.THREE.Mesh(rawGeo);\n      return mesh;\n    }\n\n    drawMultilayeredEllipses(ellipses, properties, multilayeredObjects) {\n      let wireframes = [];\n      ellipses.forEach(ellipse => {\n        console.log(ellipse);\n\n        if (ellipse.objectGeneration.type === 3) {\n          let ellipseMesh = this.GraphicsThree3D.DrawingObjectLoader.Ellipse.drawEllipse(ellipse, properties);\n          let [bboxDepth, pts] = this.generateBBoxDepthAndPoints(ellipseMesh.geometry);\n          let extrusionVector = new this.THREE.Vector3(0, 0, bboxDepth);\n          let centerPoint = this.utils.initVector3(ellipse.centerPoint);\n          ellipseMesh.updateMatrix();\n          pts.forEach(pt => pt.sub(centerPoint));\n\n          if (properties.volume.type === 2) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              let rawMesh = this.makeExtrudedMeshForCSG(pointArr, bboxDepth);\n              let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n              let geometry = modifiedMesh.geometry;\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n            });\n          } else if (properties.volume.type === 3) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n            let grainDirection;\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n              let rawMesh = this.makeExtrudedMeshForCSG(pointArr.points, bboxDepth);\n              let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n              let geometry = modifiedMesh.geometry;\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n            });\n          } else if (properties.volume.type === 4) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n\n            for (let i = 0; i < segments.length; i++) {\n              let segment = segments[i];\n              let layer = properties.volume.layers[i];\n              let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n              if (segment.type) {\n                let layerCount = Math.ceil(extrusionVector.z / layer.width);\n                let layerWidth = bboxDepth / layerCount;\n                let pointArr = segment.points;\n\n                for (let j = 0; j < layerCount; j++) {\n                  let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: layerWidth,\n                    translation: [0, 0, j * layerWidth],\n                    skew: [0, 0]\n                  });\n                  let rawMesh = new this.THREE.Mesh(rawGeo);\n                  let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n                  let geometry = modifiedMesh.geometry;\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n                }\n              } else {\n                let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n                horizontalSegments.forEach(pointArr => {\n                  let rawMesh = this.makeExtrudedMeshForCSG(pointArr, bboxDepth);\n                  let modifiedMesh = BooleanOperations.doCSG(ellipseMesh, rawMesh, \"intersect\");\n                  let geometry = modifiedMesh.geometry;\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipseMesh.rotation]);\n                });\n              }\n            }\n          }\n        } else if (ellipse.objectGeneration.type === 2) {\n          let centerPoint = this.utils.initVector3(ellipse.centerPoint);\n          let extrusionVector = this.utils.initVector3(ellipse.objectGeneration.extrusionVector);\n          let extrusionNormalized = this.utils.initVector3(extrusionVector).normalize();\n          let skewParams = this.utils.getSkewParams(extrusionVector);\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipse);\n          let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n          pts.forEach(point => {\n            point.sub(midVector);\n            point.applyEuler(eulerOne);\n          });\n          extrusionVector.applyEuler(eulerOne);\n\n          if (properties.volume.type === 2) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                points2D: pointArr,\n                depth: extrusionVector.z,\n                translation: [0, 0, 0],\n                skew: skewParams\n              });\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n            });\n          } else if (properties.volume.type === 3) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n            let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n            let grainDirection;\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n            segments.forEach(pointArr => {\n              grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n              let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                points2D: pointArr.points,\n                depth: extrusionVector.z,\n                translation: [0, 0, 0],\n                skew: skewParams\n              });\n              this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n            });\n          } else if (properties.volume.type === 4) {\n            let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n            let segments = this.segmentPointsVertical(pts, properties.volume);\n\n            for (let i = 0; i < segments.length; i++) {\n              let segment = segments[i];\n              let layer = properties.volume.layers[i];\n              let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n              if (segment.type) {\n                let layerCount = Math.ceil(extrusionVector.z / layer.width);\n                let layerWidth = extrusionVector.z / layerCount;\n                let pointArr = segment.points;\n\n                for (let j = 0; j < layerCount; j++) {\n                  let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: layerWidth,\n                    translation: [0, 0, j * layerWidth],\n                    skew: skewParams\n                  });\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n                }\n              } else {\n                let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n                horizontalSegments.forEach(pointArr => {\n                  let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                    points2D: pointArr,\n                    depth: extrusionVector.z,\n                    translation: [0, 0, 0],\n                    skew: skewParams\n                  });\n                  this.addToComplexVolumeData(ellipse, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, eulerTwo]);\n                });\n              }\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    drawMultilayeredPolygons(polygons, properties, multilayeredObjects) {\n      let wireframes = [];\n      polygons.forEach(polygon => {\n        let extrusionVector = this.utils.initVector3(polygon.objectGeneration.extrusionVector);\n        let extrusionNormalized = this.utils.initVector3(extrusionVector).normalize();\n        let skewParams = this.utils.getSkewParams(extrusionVector);\n        let pts = this.GraphicsThree3D.DrawingObjectLoader.Polygon.getPolygonPlanarPoints(polygon);\n        let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n        pts.forEach(point => {\n          point.sub(midVector);\n          point.applyEuler(eulerOne);\n        });\n        extrusionVector.applyEuler(eulerOne);\n\n        if (properties.volume.type === 2) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          let polygonDepth = extrusionVector.z;\n          let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n          segments.forEach(pointArr => {\n            let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr,\n              depth: polygonDepth,\n              translation: [0, 0, translateZ],\n              skew: skewParams\n            });\n            this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n          });\n        } else if (properties.volume.type === 3) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n          let grainDirection;\n          let polygonDepth = extrusionVector.z;\n          let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n            let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr.points,\n              depth: polygonDepth,\n              translation: [0, 0, translateZ],\n              skew: skewParams\n            });\n            this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n          });\n        } else if (properties.volume.type === 4) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n\n          for (let i = 0; i < segments.length; i++) {\n            let segment = segments[i];\n            let layer = properties.volume.layers[i];\n            let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n            if (segment.type) {\n              let layerCount = Math.ceil(extrusionVector.z / layer.width);\n              let layerWidth = extrusionVector.z / layerCount;\n              let polygonDepth = layerWidth;\n              let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : polygonDepth;\n              let pointArr = segment.points;\n\n              for (let j = 0; j < layerCount; j++) {\n                let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: polygonDepth,\n                  translation: [0, 0, j * translateZ],\n                  skew: skewParams\n                });\n                this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n              }\n            } else {\n              let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n              let polygonDepth = extrusionVector.z;\n              let translateZ = crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0 ? -polygonDepth : 0;\n              horizontalSegments.forEach(pointArr => {\n                let geometry = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: polygonDepth,\n                  translation: [0, 0, translateZ],\n                  skew: skewParams\n                });\n                this.addToComplexVolumeData(polygon, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n              });\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    drawMultilayeredEllipsoids(ellipsoids, properties, multilayeredObjects) {\n      const SIZE_DELTA = 1;\n      let wireframes = [];\n      ellipsoids.forEach(ellipsoid => {\n        let ellipsoidMesh = this.GraphicsThree3D.DrawingObjectLoader.Ellipsoid.drawEllipsoid(ellipsoid);\n        ellipsoidMesh.geometry.computeBoundingBox();\n        let bbox = ellipsoidMesh.geometry.boundingBox;\n        let bboxDepth = bbox.max.z - bbox.min.z + SIZE_DELTA;\n        let pts = [new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.min.y - SIZE_DELTA, 0), new this.THREE.Vector3(bbox.max.x + SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0), new this.THREE.Vector3(bbox.min.x - SIZE_DELTA, bbox.max.y + SIZE_DELTA, 0)];\n        let extrusionVector = new this.THREE.Vector3(0, 0, bboxDepth);\n        let centerPoint = this.utils.initVector3(ellipsoid.centerPoint);\n        ellipsoidMesh.position.sub(centerPoint).add(extrusionVector.clone().multiplyScalar(0.5));\n        ellipsoidMesh.updateMatrix();\n        centerPoint.add(extrusionVector.clone().multiplyScalar(0.5));\n\n        if (properties.volume.type === 2) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr,\n              depth: bboxDepth,\n              translation: [0, 0, 0],\n              skew: [0, 0]\n            });\n            let rawMesh = new this.THREE.Mesh(rawGeo);\n            let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n            let geometry = modifiedMesh.geometry;\n            this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n          });\n        } else if (properties.volume.type === 3) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let grainDirectionOne = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n          let grainDirectionTwo = this.utils.initVector3(properties.volume.layers[1].grainDirectionVector).normalize();\n          let grainDirection;\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n          segments.forEach(pointArr => {\n            grainDirection = pointArr.type ? grainDirectionOne : grainDirectionTwo;\n            let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n              points2D: pointArr.points,\n              depth: bboxDepth,\n              translation: [0, 0, 0],\n              skew: [0, 0]\n            });\n            let rawMesh = new this.THREE.Mesh(rawGeo);\n            let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n            let geometry = modifiedMesh.geometry;\n            this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n          });\n        } else if (properties.volume.type === 4) {\n          let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n          let segments = this.segmentPointsVertical(pts, properties.volume);\n\n          for (let i = 0; i < segments.length; i++) {\n            let segment = segments[i];\n            let layer = properties.volume.layers[i];\n            let grainDirection = this.utils.initVector3(segment.layerProps.grainDirectionVector).normalize();\n\n            if (segment.type) {\n              let layerCount = Math.ceil(extrusionVector.z / layer.width);\n              let layerWidth = bboxDepth / layerCount;\n              let pointArr = segment.points;\n\n              for (let j = 0; j < layerCount; j++) {\n                let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: layerWidth,\n                  translation: [0, 0, j * layerWidth],\n                  skew: [0, 0]\n                });\n                let rawMesh = new this.THREE.Mesh(rawGeo);\n                let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n                let geometry = modifiedMesh.geometry;\n                this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n              }\n            } else {\n              let horizontalSegments = this.segmentPointsHorizontal(segment.points, segment.layerProps);\n              horizontalSegments.forEach(pointArr => {\n                let rawGeo = this.GraphicsThree3D.makePositionedExtrusion({\n                  points2D: pointArr,\n                  depth: bboxDepth,\n                  translation: [0, 0, 0],\n                  skew: [0, 0]\n                });\n                let rawMesh = new this.THREE.Mesh(rawGeo);\n                let modifiedMesh = BooleanOperations.doCSG(ellipsoidMesh, rawMesh, \"intersect\");\n                let geometry = modifiedMesh.geometry;\n                this.addToComplexVolumeData(ellipsoid, multilayeredObjects, [geometry, faceSideNormal, grainDirection, centerPoint, ellipsoidMesh.rotation]);\n              });\n            }\n          }\n        }\n      });\n      return wireframes;\n    }\n\n    addToComplexVolumeData(objectData, multilayeredObjects, [geometry, faceSideNormal, grainDirection, midVector, eulerTwo]) {\n      if (objectData.objectType === 1) multilayeredObjects.positives.push([geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);else multilayeredObjects.negatives.push([geometry, faceSideNormal, grainDirection, midVector, eulerTwo]);\n    }\n\n    segmentPointsVertical(points, volumeProps) {\n      let PolyBool = this.GraphicsThree3D.PolyBool;\n      let ptsPolygon = {\n        regions: [[]],\n        inverted: false\n      };\n      let maxX = 0,\n          maxY = 0;\n      let minX = Infinity,\n          minY = Infinity;\n      points.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        ptsPolygon.regions[0].push([point.x, point.y]);\n      });\n      let segments = [];\n\n      if (volumeProps.type == 2) {\n        let layerThickness = volumeProps.layers[0].thickness;\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n\n        while (minY < maxY) {\n          let eraserRegion = [];\n          eraserRegion.push([minX, minY]);\n          eraserRegion.push([maxX, minY]);\n          eraserRegion.push([maxX, minY + layerThickness]);\n          eraserRegion.push([minX, minY + layerThickness]);\n          eraserPolygon.regions.push(eraserRegion);\n          minY += 2 * layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach(region => {\n          let pointArr = [];\n          region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach(region => {\n          let pointArr = [];\n          region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      } else if (volumeProps.type == 3) {\n        let objectHeight = maxY - minY;\n        let layerThickness = objectHeight / Math.ceil(objectHeight / volumeProps.layers[0].thickness);\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n\n        while (minY < maxY) {\n          let eraserRegion = [];\n          eraserRegion.push([minX, minY]);\n          eraserRegion.push([maxX, minY]);\n          eraserRegion.push([maxX, minY + layerThickness]);\n          eraserRegion.push([minX, minY + layerThickness]);\n          eraserPolygon.regions.push(eraserRegion);\n          minY += 2 * layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach(region => {\n          let pointArr = {\n            points: [],\n            type: 0\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach(region => {\n          let pointArr = {\n            points: [],\n            type: 1\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      } else {\n        let layers = volumeProps.layers;\n        let eraserPolygon = {\n          regions: [],\n          inverted: false\n        };\n        let layersHeight = layers.map(layer => layer = layer.thickness).reduce((a, b) => a + b, 0);\n        let objectHeight = maxY - minY;\n\n        if (layersHeight != objectHeight) {\n          let proportion = objectHeight / layersHeight;\n          layers.forEach(layer => layer.thickness *= proportion);\n        }\n\n        let layerPropsEven = [];\n        let layerPropsOdd = [];\n\n        for (let i = 0; i < layers.length; i++) {\n          let layerThickness = layers[i].thickness;\n\n          if (i % 2 == 0) {\n            let eraserRegion = [];\n            eraserRegion.push([minX, minY]);\n            eraserRegion.push([maxX, minY]);\n            eraserRegion.push([maxX, minY + layerThickness]);\n            eraserRegion.push([minX, minY + layerThickness]);\n            eraserPolygon.regions.push(eraserRegion);\n            layerPropsEven.push(layers[i]);\n          } else {\n            layerPropsOdd.push(layers[i]);\n          }\n\n          minY += layerThickness;\n        }\n\n        let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n        let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n        polygonOne.regions.forEach((region, index) => {\n          let pointArr = {\n            points: [],\n            type: 0,\n            layerProps: layerPropsOdd[index]\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n        polygonTwo.regions.forEach((region, index) => {\n          let pointArr = {\n            points: [],\n            type: 1,\n            layerProps: layerPropsEven[index]\n          };\n          region.forEach(point => pointArr.points.push(new this.THREE.Vector2(point[0], point[1])));\n          segments.push(pointArr);\n        });\n      }\n\n      return segments;\n    }\n\n    segmentPointsHorizontal(points, layerProps) {\n      let PolyBool = this.GraphicsThree3D.PolyBool;\n      let segments = [];\n      let ptsPolygon = {\n        regions: [[]],\n        inverted: false\n      };\n      let maxX = 0,\n          maxY = 0;\n      let minX = Infinity,\n          minY = Infinity;\n      points.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        ptsPolygon.regions[0].push([point.x, point.y]);\n      });\n      let eraserPolygon = {\n        regions: [],\n        inverted: false\n      };\n      let objectWidth = maxX - minX;\n      let segmentWidth = layerProps.width;\n      let segmentCount = Math.ceil(objectWidth / segmentWidth);\n      segmentWidth = objectWidth / segmentCount;\n\n      while (minX < maxX) {\n        let eraserRegion = [];\n        eraserRegion.push([minX, maxY]);\n        eraserRegion.push([minX, minY]);\n        eraserRegion.push([minX + segmentWidth, minY]);\n        eraserRegion.push([minX + segmentWidth, maxY]);\n        eraserPolygon.regions.push(eraserRegion);\n        minX += 2 * segmentWidth;\n      }\n\n      let polygonOne = PolyBool.difference(ptsPolygon, eraserPolygon);\n      let polygonTwo = PolyBool.intersect(ptsPolygon, eraserPolygon);\n      polygonOne.regions.forEach(region => {\n        let pointArr = [];\n        region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n        segments.push(pointArr);\n      });\n      polygonTwo.regions.forEach(region => {\n        let pointArr = [];\n        region.forEach(point => pointArr.push(new this.THREE.Vector2(point[0], point[1])));\n        segments.push(pointArr);\n      });\n      return segments;\n    }\n\n  }\n\n  _exports.ComplexVolume = ComplexVolume;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipse\", [\"exports\", \"m08-2020/lib/ThreeExtensions/RotatedGeometry\", \"m08-2020/lib/ThreeExtensions/PlanarRotationPointsGenerator\", \"m08-2020/lib/ThreeExtensions/PlanarExtrusionPointsGenerator\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipsoid\"], function (_exports, _RotatedGeometry, _PlanarRotationPointsGenerator, _PlanarExtrusionPointsGenerator, _Ellipsoid) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Ellipse = void 0;\n\n  class Ellipse {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n      this.Ellipsoid = new _Ellipsoid.Ellipsoid(GraphicsThree3D);\n    }\n\n    drawEllipse(ellipseData) {\n      let ellipseType = ellipseData.objectGeneration.type;\n      let ellipseMesh;\n\n      if (ellipseType === 1 || ellipseType === 3 && !ellipseData.objectGeneration.rotationAngle) {\n        ellipseMesh = this.drawEllipsePlanar(ellipseData);\n        ellipseMesh.generationType = \"area\";\n      } else if (ellipseType === 2) {\n        if (this.GraphicsThree3D.objectExtrudesPlanar(ellipseData, true)) {\n          ellipseMesh = this.makeEllipseExtrudedArea(ellipseData);\n          ellipseMesh.generationType = \"area\";\n        } else {\n          ellipseMesh = this.makeEllipseExtrudedVolume(ellipseData);\n          ellipseMesh.generationType = \"volume\";\n        }\n      } else if (ellipseType === 3) {\n        let rotatesPlanar = this.GraphicsThree3D.objectRotatesPlanar(ellipseData, true);\n        let isPlanar = rotatesPlanar && !ellipseData.objectGeneration.rotationPitch;\n        let isPlanarWithPitch = rotatesPlanar && ellipseData.objectGeneration.rotationPitch;\n\n        if (isPlanar) {\n          ellipseMesh = this.makeEllipseRotatedArea(ellipseData);\n          ellipseMesh.generationType = \"area\";\n        } else {\n          ellipseMesh = this.makeEllipseRotatedVolume(ellipseData, isPlanarWithPitch);\n          ellipseMesh.generationType = \"volume\";\n        }\n      }\n\n      return ellipseMesh;\n    }\n\n    drawEllipsePlanar(ellipseData) {\n      let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n      let extrusionDepth = 0.1;\n      let extrusionVector = new this.THREE.Vector3(0, 0, extrusionDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        // point.sub( midVector );\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let ellipseShape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: extrusionDepth,\n        bevelEnabled: false\n      };\n      let ellipseGeo = new this.THREE.ExtrudeGeometry(ellipseShape, extrudeSettings); // ellipseGeo.translate(0, 0, -extrusionVector.z);\n\n      let ellipse = new this.THREE.Mesh(ellipseGeo); // ellipse.position.add( midVector );\n      // ellipse.updateMatrix();\n\n      ellipse.setRotationFromEuler(eulerTwo);\n      ellipse.position.add(centerPoint);\n      ellipse.updateMatrix();\n      return ellipse;\n    }\n\n    makeEllipseExtrudedArea(ellipseData) {\n      const Z_AXIS = new this.THREE.Vector3(0, 0, 1);\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(curvePoints, Z_AXIS);\n      curvePoints.forEach(point => point.applyEuler(eulerOne));\n      let extrusionVector = this.utils.initVector3(ellipseData.objectGeneration.extrusionVector).applyEuler(eulerOne);\n      let shapePts = new _PlanarExtrusionPointsGenerator.PlanarExtrusionPointsGenerator(curvePoints, extrusionVector).extrudedPoints;\n      let curveShape = new this.THREE.Shape(shapePts);\n      let curveGeo = new this.THREE.ExtrudeGeometry(curveShape, {\n        depth: -0.01,\n        bevelEnabled: false\n      });\n      curveGeo.translate(ellipseData.centerPoint.x, ellipseData.centerPoint.y, ellipseData.centerPoint.z);\n      let ellipse = new this.THREE.Mesh(curveGeo);\n      ellipse.setRotationFromEuler(eulerTwo);\n      ellipse.updateMatrix();\n      return ellipse;\n    }\n\n    makeEllipseExtrudedVolume(ellipseData) {\n      let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n      let extrusionVector = this.utils.initVector3(ellipseData.objectGeneration.extrusionVector);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let skewX = extrusionNormalized.x / extrusionNormalized.z;\n      let skewY = extrusionNormalized.y / extrusionNormalized.z;\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData); // this.WireframeGenerator.getPlanarPoints(curvePoints.map(point => point.clone().add(centerPoint)), ellipseData.objectType);\n\n      let curveShape = new this.THREE.Shape(curvePoints);\n      let curveGeo = new this.THREE.ExtrudeGeometry(curveShape, {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      }); // curveGeo.translate(0, 0, extrusionVector.z);\n\n      curveGeo.applyMatrix4(new this.THREE.Matrix4().set(1, 0, skewX, 0, 0, 1, skewY, 0, 0, 0, 1, 0, 0, 0, 0, 1));\n      let ellipse = new this.THREE.Mesh(curveGeo);\n      ellipse.position.copy(centerPoint);\n      ellipse.updateMatrix(); ///\n\n      let linePairs = [];\n\n      for (let i = 0, ptsLen = curvePoints.length; i < ptsLen; i++) {\n        let ptOne = curvePoints[i % ptsLen].clone().add(centerPoint);\n        let ptTwo = curvePoints[(i + 1) % ptsLen].clone().add(centerPoint);\n        linePairs.push([ptOne, ptTwo]);\n        let ptOneExtr = ptOne.clone().add(extrusionVector);\n        let ptTwoExtr = ptTwo.clone().add(extrusionVector);\n        linePairs.push([ptOneExtr, ptTwoExtr]);\n        linePairs.push([ptOne, ptOneExtr]);\n      } // this.WireframeGenerator.getRegularWireframePointPairs( ellipse, Math.PI / 720 )\n      //                        .forEach( pair => linePairs.push([\n      //                           pair[ 0 ].clone().applyMatrix4( ellipse.matrix ),\n      //                           pair[ 1 ].clone().applyMatrix4( ellipse.matrix )\n      //                         ]));\n\n\n      let linePairsForOpaque = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(ellipse, 20 * Math.PI / 180).forEach(pair => linePairsForOpaque.push([pair[0].clone().applyMatrix4(ellipse.matrix), pair[1].clone().applyMatrix4(ellipse.matrix)]));\n      this.WireframeGenerator.getVolumeWireframeData(ellipse, linePairsForOpaque, linePairs, ellipseData.objectType); ///\n\n      return ellipse;\n    }\n\n    makeEllipseRotatedArea(ellipseData) {\n      // TODO: Maybe need to implement the same logic as in polygon planar rotation?\n      const DELTA = 0.0001;\n      let centerPoint = ellipseData.centerPoint;\n      let rotationPoint = ellipseData.objectGeneration.rotationPoint;\n      let rotationAngle = ellipseData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = ellipseData.objectGeneration.rotationPitch;\n      let rotationAxis = this.utils.initVector3(ellipseData.objectGeneration.rotationAxis);\n      let centerVector = this.utils.initVector3(centerPoint);\n      let rotationVector = this.utils.initVector3(rotationPoint).sub(centerVector);\n      let curvePoints = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData).map(point => point.sub(rotationVector));\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let [_, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(curvePoints, zAxis);\n      if (curvePoints[0].distanceTo(curvePoints[curvePoints.length - 1]) < DELTA) curvePoints.pop();\n      curvePoints.forEach(point => point.applyEuler(eulerOne));\n      let zValue = curvePoints[0].z;\n      if (rotationAxis.applyEuler(eulerOne).dot(zAxis) < 0) rotationAngle *= -1;\n      let rotationPtsGen = new _PlanarRotationPointsGenerator.PlanarRotationPointsGenerator(curvePoints, {\n        rotationAngle,\n        rotationPitch\n      });\n      curvePoints = rotationPtsGen.rotatedPoints;\n      let holePtsArr = rotationPtsGen.holePoints;\n      let ellipse = this.drawExtrudedEllipseMesh(curvePoints, holePtsArr, -0.01);\n      ellipse.geometry.translate(rotationPoint.x, rotationPoint.y, rotationPoint.z + zValue);\n      ellipse.setRotationFromEuler(eulerTwo);\n      return ellipse;\n    }\n\n    drawExtrudedEllipseMesh(shapePts, holePtsArr, extrDepth) {\n      let curveShape = new this.THREE.Shape(shapePts);\n      holePtsArr.forEach(holePts => {\n        if (holePts.length > 2) curveShape.holes.push(new this.THREE.Path(holePts));\n      });\n      let curveGeo = new this.THREE.ExtrudeGeometry(curveShape, {\n        depth: extrDepth,\n        bevelEnabled: false\n      });\n      return new this.THREE.Mesh(curveGeo);\n    }\n\n    makeEllipseRotatedVolume(ellipseData, isPlanarWithPitch) {\n      let ellipse;\n\n      if (this.rotationAxisDefined(ellipseData.objectGeneration.rotationAxis)) {\n        let centerPoint = this.utils.initVector3(ellipseData.centerPoint);\n        let rotationPoint = this.utils.initVector3(ellipseData.objectGeneration.rotationPoint);\n        let rotationAxis = this.utils.initVector3(ellipseData.objectGeneration.rotationAxis).normalize();\n        let rotationAngle = ellipseData.objectGeneration.rotationAngle * Math.PI / 180;\n        let rotationPitch = ellipseData.objectGeneration.rotationPitch;\n        let rotationRadius = centerPoint.distanceTo(rotationPoint);\n\n        if (this.generatesEllipsoid(rotationRadius, rotationAngle, rotationPitch)) {\n          let ellipsoidData = this.generateEllipsoidData(ellipseData, rotationAxis);\n          ellipse = this.Ellipsoid.drawEllipsoid(ellipsoidData);\n        } else {\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(ellipseData).map(pt => pt.add(centerPoint));\n          let rotationParams = {\n            rotationPoint: this.utils.initVector3(ellipseData.objectGeneration.rotationPoint),\n            rotationAxis: this.utils.initVector3(ellipseData.objectGeneration.rotationAxis).normalize(),\n            rotationAngle: ellipseData.objectGeneration.rotationAngle * Math.PI / 180,\n            rotationPitch: ellipseData.objectGeneration.rotationPitch\n          };\n          let rotatedGeometry = new _RotatedGeometry.RotatedGeometry(pts.map(pt => pt.clone()), rotationParams);\n          ellipse = new this.THREE.Mesh(rotatedGeometry);\n          ellipse.updateMatrix();\n          this.addToWireframePts3D(ellipse, ellipseData, pts, rotationParams, isPlanarWithPitch);\n        }\n      } else ellipse = new this.THREE.Mesh();\n\n      return ellipse;\n    }\n\n    addToWireframePts3D(ellipseMesh, ellipseData, ellipsePts, rotationParams, isPlanarWithPitch) {\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationParams;\n      let pitchVec = rotationAxis.clone().multiplyScalar(rotationPitch * Math.abs(rotationAngle) / (2 * Math.PI));\n      let wireframePts = [...ellipsePts, ellipsePts[0]];\n      let linePairs;\n\n      if (isPlanarWithPitch) {\n        linePairs = [];\n\n        for (let i = 0, l = wireframePts.length - 1; i < l; i++) {\n          let pair = [wireframePts[i], wireframePts[i + 1]];\n          linePairs.push(pair);\n          let rotatedPoints = pair.map(pt => pt.clone().sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint).add(pitchVec));\n          linePairs.push(rotatedPoints);\n        }\n\n        for (let point of wireframePts) {\n          let sidePoints = this.getSidePoints(point, rotationParams);\n          linePairs.push([point, sidePoints[0]]);\n\n          for (let j = 0; j < sidePoints.length - 1; j++) linePairs.push([sidePoints[j], sidePoints[j + 1]]);\n        }\n      } else {\n        linePairs = this.WireframeGenerator.getRegularWireframePointPairs(ellipseMesh, Math.PI / 180).map(pair => [pair[0].clone().applyMatrix4(ellipseMesh.matrix), pair[1].clone().applyMatrix4(ellipseMesh.matrix)]);\n      }\n\n      let linePairsForOpaque = [];\n\n      for (let i = 0, l = wireframePts.length - 1; i < l; i++) {\n        linePairsForOpaque.push([wireframePts[i], wireframePts[i + 1]]);\n        let rotatedPoints = [wireframePts[i], wireframePts[i + 1]].map(pt => pt.clone().sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint).add(pitchVec));\n        linePairsForOpaque.push(rotatedPoints);\n      }\n\n      if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n        let ptsToRotate = [];\n        ptsToRotate.push(ellipsePts[ellipsePts.length - 1], ellipsePts[ellipsePts.length - 2]);\n        ptsToRotate.push(ellipsePts[0]);\n        if (ellipseData.segmentHeight) ptsToRotate.push(ellipsePts[1]);\n\n        for (let point of ptsToRotate) {\n          let sidePoints = this.getSidePoints(point, rotationParams);\n          linePairsForOpaque.push([point, sidePoints[0]]);\n\n          for (let j = 0; j < sidePoints.length - 1; j++) linePairsForOpaque.push([sidePoints[j], sidePoints[j + 1]]);\n        }\n      }\n\n      this.WireframeGenerator.getVolumeWireframeData(ellipseMesh, linePairsForOpaque, linePairs, ellipseData.objectType);\n    }\n\n    getSidePoints(point, rotationParams) {\n      const DETAIL = 64;\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationParams;\n      let numOfSegments = DETAIL * Math.abs(rotationAngle) / (2 * Math.PI);\n      let pitchVector = new this.THREE.Vector3();\n      let sidePoints = [];\n\n      for (let i = 0; i < numOfSegments; i++) {\n        let rotatedPoint = new this.THREE.Vector3().copy(point).sub(rotationPoint).applyAxisAngle(rotationAxis, (i + 1) * rotationAngle / numOfSegments).add(rotationPoint).add(pitchVector.copy(rotationAxis).multiplyScalar((i + 1) * rotationPitch / numOfSegments / 2));\n        sidePoints.push(rotatedPoint);\n      }\n\n      return sidePoints;\n    }\n\n    rotationAxisDefined(rotationAxis) {\n      return rotationAxis && (rotationAxis.x || rotationAxis.y || rotationAxis.z);\n    }\n\n    generatesEllipsoid(rotationRadius, rotationAngle, rotationPitch) {\n      return rotationRadius <= 0.01 && !rotationPitch && rotationAngle >= Math.PI;\n    }\n\n    getProjectedVectorLengthSquared(initialVec, targetVec, targetLen) {\n      let projectedVectorLengthSquared = initialVec.clone().projectOnVector(targetVec).multiplyScalar(targetLen).lengthSq();\n      return projectedVectorLengthSquared;\n    }\n\n    generateEllipsoidData(ellipseData, rotationAxis) {\n      let dir1 = this.utils.initVector3(ellipseData.directionVector1),\n          dir2 = this.utils.initVector3(ellipseData.directionVector2);\n      let dir3 = new this.THREE.Vector3().crossVectors(dir1, dir2).normalize();\n      let projOneLenSq = this.getProjectedVectorLengthSquared(rotationAxis, dir1, ellipseData.radius1);\n      let projTwoLenSq = this.getProjectedVectorLengthSquared(rotationAxis, dir2, ellipseData.radius2);\n      let radius3 = Math.sqrt(projOneLenSq + projTwoLenSq);\n      let ellipsoidData = { ...ellipseData,\n        radius3,\n        directionVector3: dir3\n      };\n      return ellipsoidData;\n    }\n\n  }\n\n  _exports.Ellipse = Ellipse;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipsoid\", [\"exports\", \"threejs-slice-geometry\", \"three/src/extras/Earcut\"], function (_exports, _threejsSliceGeometry, _Earcut) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Ellipsoid = void 0;\n\n  class Ellipsoid {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawEllipsoid(ellipsoidData) {\n      console.log(ellipsoidData);\n      let centerPoint = this.utils.initVector3(ellipsoidData.centerPoint);\n      let directionVectorOne = this.utils.initVector3(ellipsoidData.directionVector1).normalize();\n      let directionVectorTwo = this.utils.initVector3(ellipsoidData.directionVector2).normalize();\n      let directionVectorThree = this.utils.initVector3(ellipsoidData.directionVector3).normalize();\n      let radiusOne = ellipsoidData.radius1;\n      let radiusTwo = ellipsoidData.radius2;\n      let radiusThree = ellipsoidData.radius3;\n      let segmentHeight = ellipsoidData.segmentHeight;\n      let ellipsoidMesh = this.makeEllipsoidMesh(radiusOne, radiusTwo, radiusThree);\n\n      if (this.segmentVectorsAreValid(ellipsoidData.segmentVector1, ellipsoidData.segmentVector2)) {\n        let segmentVectorOne = this.utils.initVector3(ellipsoidData.segmentVector1).normalize();\n        let segmentVectorTwo = this.utils.initVector3(ellipsoidData.segmentVector2).normalize();\n        let segmentsCross = this.utils.getCrossVector(segmentVectorOne, segmentVectorTwo);\n        let directionsCross = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n        let segmentsCrossLength = segmentsCross.length();\n        let crossVector = segmentsCrossLength !== 0 ? segmentsCross : directionsCross;\n        let isOnPlane = false;\n\n        for (let vector of [directionVectorOne, directionVectorTwo, directionVectorThree]) {\n          if (Math.abs(vector.dot(crossVector)) === 1) isOnPlane = true;\n          if (vector.dot(crossVector) === -1) crossVector.negate();\n        }\n\n        if (isOnPlane) {\n          let segmentQuaternion = new this.THREE.Quaternion().setFromUnitVectors(crossVector, new this.THREE.Vector3(0, 0, 1));\n          segmentVectorOne.applyQuaternion(segmentQuaternion);\n          segmentVectorTwo.applyQuaternion(segmentQuaternion);\n          let maxRadius = Math.max(radiusOne, radiusTwo, radiusThree) + 1;\n          let boxSize = 2 * maxRadius;\n          let pointsData = {\n            centerPoint: {\n              x: ellipsoidData.centerPoint.x,\n              y: ellipsoidData.centerPoint.y,\n              z: ellipsoidData.centerPoint.z\n            },\n            directionVector1: {\n              x: ellipsoidData.directionVector1.x,\n              y: ellipsoidData.directionVector1.y,\n              z: ellipsoidData.directionVector1.z\n            },\n            directionVector2: {\n              x: ellipsoidData.directionVector2.x,\n              y: ellipsoidData.directionVector2.y,\n              z: ellipsoidData.directionVector2.z\n            },\n            radius1: maxRadius,\n            radius2: maxRadius,\n            segmentHeight: 0,\n            segmentVector1: {\n              x: segmentVectorOne.x,\n              y: segmentVectorOne.y,\n              z: segmentVectorOne.z\n            },\n            segmentVector2: {\n              x: segmentVectorTwo.x,\n              y: segmentVectorTwo.y,\n              z: segmentVectorTwo.z\n            }\n          };\n          let pts = this.GraphicsThree3D.getEllipsePlanarPoints(pointsData);\n          if (segmentsCrossLength === 0) pts.pop();\n          let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, new this.THREE.Vector3(0, 0, 1));\n          pts.forEach(point => {\n            point.sub(midVector);\n            point.applyEuler(eulerOne);\n            point.add(midVector);\n          });\n          let eraserPoints = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n          let eraserShape = new this.THREE.Shape(eraserPoints);\n          let eraserGeometry = new this.THREE.ExtrudeGeometry(eraserShape, {\n            steps: 1,\n            bevelEnabled: false,\n            depth: boxSize\n          });\n          eraserGeometry.translate(0, 0, -boxSize / 2);\n          let eraserMesh = new this.THREE.Mesh(eraserGeometry);\n          eraserMesh.lookAt(crossVector);\n          eraserMesh.updateMatrix();\n          let ellipsoidCSG = this.CSG.fromMesh(ellipsoidMesh);\n          let eraserCSG = this.CSG.fromMesh(eraserMesh);\n          ellipsoidCSG = ellipsoidCSG.intersect(eraserCSG);\n\n          if (segmentHeight) {\n            // let pointsData = {\n            //   centerPoint: {\n            //     x: ellipsoidData.centerPoint.x,\n            //     y: ellipsoidData.centerPoint.y,\n            //     z: ellipsoidData.centerPoint.z\n            //   },\n            //   directionVector1: {\n            //     x: ellipsoidData.directionVector1.x,\n            //     y: ellipsoidData.directionVector1.y,\n            //     z: ellipsoidData.directionVector1.z\n            //   },\n            //   directionVector2: {\n            //     x: ellipsoidData.directionVector2.x,\n            //     y: ellipsoidData.directionVector2.y,\n            //     z: ellipsoidData.directionVector2.z\n            //   },\n            //   radius1: ellipsoidData.radius1,\n            //   radius2: ellipsoidData.radius2,\n            //   segmentHeight: ellipsoidData.segmentHeight,\n            //   segmentVector1: {\n            //     x: segmentVectorOne.x,\n            //     y: segmentVectorOne.y,\n            //     z: segmentVectorOne.z\n            //   },\n            //   segmentVector2: {\n            //     x: segmentVectorTwo.x,\n            //     y: segmentVectorTwo.y,\n            //     z: segmentVectorTwo.z\n            //   }\n            // };\n            // let pts = this.GraphicsThree3D.getEllipsePlanarPoints( pointsData );\n            // let needToInvert = false;\n            // if ( this.utils.vectorsAreEqual( pts[ pts.length - 1 ], new this.THREE.Vector3() ) )\n            // {\n            //   pts.pop();\n            //   needToInvert = true;\n            // }\n            // console.log(pts.map(pt => pt.clone()))\n            // let heightPts = [ pts[ 0 ], pts[ pts.length - 1 ] ];\n            // let mid = new this.THREE.Vector3().lerpVectors( heightPts[ 0 ], heightPts[ 1 ], 0.5 );\n            // let planeNormal = mid.clone().normalize();\n            // let plane = new this.THREE.Plane( planeNormal, mid.length() );\n            // if ( needToInvert ) plane.negate();\n            // let [ midVector, eulerOne, eulerTwo ] = this.GraphicsThree3D.getRotationParameters( pts, new this.THREE.Vector3( 0, 0, 1 ) );\n            // pts.forEach( point => point.applyEuler( eulerOne ) );\n            // this.GraphicsThree3D.scene.add( new this.THREE.PlaneHelper( plane, 30, 0xff0000 ) );\n            // ellipsoidMesh = this.CSG.toMesh(ellipsoidCSG, new this.THREE.Matrix4());\n            // let geo = SliceGeometry()(ellipsoidMesh.geometry, plane, true);\n            // console.log(geo)\n            // let pointsOnPlane = [];\n            // geo.vertices.forEach(( vertex, index ) => {\n            //   if ( Math.abs( plane.distanceToPoint( vertex ) ) < 0.0001 ) pointsOnPlane.push( { vertex: vertex.clone(), index } );\n            // });\n            // let [ midVec, euler1, euler2 ] = this.GraphicsThree3D.getRotationParameters( pointsOnPlane.map(ptData => ptData.vertex), new this.THREE.Vector3( 0, 0, 1 ) );\n            // pointsOnPlane.forEach( ({ vertex }) => {\n            //   vertex.sub( midVec );\n            //   vertex.applyEuler( euler1 );\n            // } );\n            // // Helpers -->\n            // let THREE = this.THREE;\n            // function sortPoints(points, normal) {\n            //   let centerPoint = getCenterPoint(points);\n            //   let sortOrder = (a, b) => normal.dot(new THREE.Vector3().crossVectors(a.vertex.clone().sub(centerPoint), b.vertex.clone().sub(centerPoint)));\n            //   points.sort( sortOrder );\n            //   return points;\n            // }\n            // function getCenterPoint(pointsArr) {\n            //   let length = pointsArr.length;\n            //   let centerPoint = pointsArr\n            //                       .reduce((finalVec, currentVecData) => finalVec.add(currentVecData.vertex), new THREE.Vector3())\n            //                       .multiplyScalar(1 / length);\n            //   return centerPoint;\n            // }\n            // // <-- Helpers\n            // pointsOnPlane = sortPoints( pointsOnPlane, new THREE.Vector3( 0, 0, 1 ) );\n            // let pointsFlat = pointsOnPlane.reduce((flatPts, vecData) => {\n            //   return [ ...flatPts, vecData.vertex.x, vecData.vertex.y, vecData.vertex.z ];\n            // }, []);\n            // let faces = Earcut.triangulate( pointsFlat, [], 3 );\n            // let correctedFaces = [];\n            // faces.forEach(face => correctedFaces.push( pointsOnPlane[ face ].index ))\n            // correctedFaces.reverse()\n            // for ( let i = 0, l = correctedFaces.length; i < l; i += 3 ) {\n            //   geo.faces.push( new THREE.Face3( correctedFaces[ i ], correctedFaces[ i + 1 ], correctedFaces[ i + 2 ] ) );\n            //   geo.faceVertexUvs[0].push([ new THREE.Vector2( 0, 0 ), new THREE.Vector2( 0, 0 ), new THREE.Vector2( 0, 0 ) ]);\n            // }\n            // geo.computeFaceNormals();\n            // geo.computeVertexNormals();\n            // geo.mergeVertices();\n            // pointsOnPlane.forEach(({vertex}) => this.GraphicsThree3D.scene.add(this.utils.makeTestMesh(vertex, 1, 0x00ff00)));\n            // ellipsoidMesh = new this.THREE.Mesh(geo, new this.THREE.MeshBasicMaterial({color: 0xff0000}))\n            // // this.GraphicsThree3D.scene.add(ellipsoidMesh)\n            // ellipsoidCSG = this.CSG.fromMesh(ellipsoidMesh);\n            // let something = this.CSG.toMesh(ellipsoidCSG, new this.THREE.Matrix4());\n            // ellipsoidMesh = something\n            // return ellipsoidMesh;\n            /// -->\n            let [segOne, segTwo] = this.getEllipsoidSegments(ellipsoidCSG, segmentVectorOne, segmentVectorTwo);\n            let midVector = segmentsCrossLength !== 0 ? new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5).normalize() : new this.THREE.Vector3().crossVectors(segmentVectorOne, crossVector);\n            let maxDistance = 0,\n                midPoint = new this.THREE.Vector3();\n            ellipsoidCSG.polygons.forEach(polygon => polygon.vertices.forEach(vertex => {\n              let localVertex = new this.THREE.Vector3(vertex.pos.x, vertex.pos.y, vertex.pos.z);\n              let dir = localVertex.clone().normalize().dot(midVector);\n              let distance = midVector.clone().dot(localVertex);\n\n              if (dir > 0.99) {\n                if (distance > maxDistance) {\n                  maxDistance = distance;\n                  midPoint.copy(localVertex);\n                }\n              } else if (dir < -0.99) {\n                if (-distance > maxDistance) {\n                  maxDistance = -distance;\n                  midPoint.copy(localVertex.clone().negate());\n                }\n              }\n            }));\n            let midRadius = midPoint.length();\n\n            if (segmentsCrossLength === 0) {\n              let heightDelta = midRadius - segmentHeight;\n              let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n              eraserBox.lookAt(midVector.clone().normalize());\n              eraserBox.position.add(new this.THREE.Vector3().copy(midVector).normalize().multiplyScalar(boxSize / 2 - heightDelta));\n              eraserBox.updateMatrix();\n              let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n              ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n            } else {\n              if (pts.length > 32) {\n                let maxHeight = midRadius + new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5).length();\n\n                if (segmentHeight <= maxHeight) {\n                  if (segmentHeight > midRadius) {\n                    let heightDelta = segmentHeight - midRadius;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    let upVector = this.getMeshUpVector(eraserBox, dir);\n                    let delta = this.getDifferenceUpAndMid(upVector, new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5));\n                    eraserBox.position.add(new this.THREE.Vector3().copy(upVector).multiplyScalar(heightDelta + boxSize / 2 - delta));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  } else {\n                    let heightDelta = midRadius - segmentHeight;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    let upVector = this.getMeshUpVector(eraserBox, dir);\n                    let delta = this.getDifferenceUpAndMid(upVector, new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5));\n                    eraserBox.position.add(new this.THREE.Vector3().copy(upVector).multiplyScalar(boxSize / 2 - heightDelta + delta));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  }\n                }\n              } else {\n                let maxHeight = midRadius;\n\n                if (segmentHeight <= maxHeight) {\n                  let midNotNormalized = new this.THREE.Vector3().lerpVectors(segOne, segTwo, 0.5);\n\n                  if (midRadius - midNotNormalized.length() > segmentHeight) {\n                    let heightDelta = midRadius - segmentHeight;\n                    let dir = segOne.clone().sub(segTwo).normalize();\n                    let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(boxSize, boxSize, boxSize));\n                    eraserBox.lookAt(dir);\n                    eraserBox.position.add(new this.THREE.Vector3().copy(midVector).multiplyScalar(heightDelta - boxSize / 2));\n                    eraserBox.updateMatrix();\n                    let heightEraserCSG = this.CSG.fromMesh(eraserBox);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  } else {\n                    let midPlane = new this.THREE.Plane(midVector);\n                    let differenceOne = Math.abs(midPlane.distanceToPoint(midPoint)) - Math.abs(midPlane.distanceToPoint(segOne));\n                    let differenceTwo = Math.abs(midPlane.distanceToPoint(midPoint)) - Math.abs(midPlane.distanceToPoint(segTwo));\n                    let segmentOne = Math.abs(segmentHeight - differenceOne);\n                    let segmentTwo = Math.abs(segmentHeight - differenceTwo);\n                    let proportionOne = segmentOne / midRadius;\n                    let proportionTwo = segmentTwo / midRadius;\n                    let proportion = Math.max(proportionOne, proportionTwo);\n                    let vecOne = segOne.clone().normalize().multiplyScalar(segOne.length() * proportion);\n                    let vecTwo = segTwo.clone().normalize().multiplyScalar(segTwo.length() * proportion);\n                    vecOne = new this.THREE.Vector2(vecOne.x, vecOne.y);\n                    vecTwo = new this.THREE.Vector2(vecTwo.x, vecTwo.y);\n                    let vecMid = new this.THREE.Vector2(0, 0);\n                    let heightEraserPoints = [vecMid, vecOne, vecTwo];\n                    let heightEraserShape = new this.THREE.Shape(heightEraserPoints);\n                    let heightEraserGeo = new this.THREE.ExtrudeGeometry(heightEraserShape, {\n                      steps: 1,\n                      bevelEnabled: false,\n                      depth: boxSize\n                    });\n                    heightEraserGeo.translate(0, 0, -boxSize / 2);\n                    let heightEraserMesh = new this.THREE.Mesh(heightEraserGeo);\n                    let heightEraserCSG = this.CSG.fromMesh(heightEraserMesh);\n                    ellipsoidCSG = ellipsoidCSG.subtract(heightEraserCSG);\n                  }\n                }\n              }\n            }\n          }\n\n          ellipsoidMesh = this.CSG.toMesh(ellipsoidCSG, new this.THREE.Matrix4());\n        }\n      }\n\n      this.GraphicsThree3D.alignObjectWithVectorsAndChangePos(ellipsoidMesh, centerPoint, directionVectorThree, directionVectorOne);\n      ellipsoidMesh.updateMatrix();\n      ellipsoidMesh.generationType = \"volume\"; ///\n\n      let linePairs = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(ellipsoidMesh, Math.PI / 720).forEach(pair => linePairs.push([pair[0].clone().applyMatrix4(ellipsoidMesh.matrix), pair[1].clone().applyMatrix4(ellipsoidMesh.matrix)]));\n      let linePairsForOpaque = [];\n\n      if (this.segmentVectorsAreValid(ellipsoidData.segmentVector1, ellipsoidData.segmentVector2)) {\n        this.WireframeGenerator.getRegularWireframePointPairs(ellipsoidMesh, 20 * Math.PI / 180).forEach(pair => linePairsForOpaque.push([pair[0].clone().applyMatrix4(ellipsoidMesh.matrix), pair[1].clone().applyMatrix4(ellipsoidMesh.matrix)]));\n      }\n\n      this.WireframeGenerator.getVolumeWireframeData(ellipsoidMesh, linePairsForOpaque, linePairs, ellipsoidData.objectType); ///\n\n      return ellipsoidMesh;\n    }\n\n    getEllipsoidSegments(ellipsoidCSG, segmentVectorOne, segmentVectorTwo) {\n      let segOne = new this.THREE.Vector3();\n      let segTwo = new this.THREE.Vector3();\n      let maxDistanceOne = 0,\n          maxDistanceTwo = 0;\n      ellipsoidCSG.polygons.forEach(polygon => polygon.vertices.forEach(vertex => {\n        let localVertex = new this.THREE.Vector3(vertex.pos.x, vertex.pos.y, vertex.pos.z);\n        let dirOne = localVertex.clone().normalize().dot(segmentVectorOne.clone().normalize());\n        let distanceOne = segmentVectorOne.clone().dot(localVertex);\n\n        if (dirOne > 0.9999) {\n          if (distanceOne > maxDistanceOne) {\n            maxDistanceOne = distanceOne;\n            segOne.copy(localVertex);\n          }\n        }\n\n        let dirTwo = localVertex.clone().normalize().dot(segmentVectorTwo.clone().normalize());\n        let distanceTwo = segmentVectorTwo.clone().dot(localVertex);\n\n        if (dirTwo > 0.9999) {\n          if (distanceTwo > maxDistanceTwo) {\n            maxDistanceTwo = distanceTwo;\n            segTwo.copy(localVertex);\n          }\n        }\n      }));\n      return [segOne, segTwo];\n    }\n\n    getMeshUpVector(mesh, direction) {\n      let upObject = new this.THREE.Object3D();\n      upObject.position.copy(mesh.up);\n      mesh.add(upObject);\n      mesh.lookAt(direction);\n      mesh.updateMatrixWorld();\n      let upVector = upObject.getWorldPosition(new this.THREE.Vector3());\n      return upVector;\n    }\n\n    getDifferenceUpAndMid(up, mid) {\n      let midLength = mid.length();\n      let upLength = midLength * Math.cos(up.angleTo(mid));\n      let delta = midLength - upLength;\n      return delta;\n    }\n\n    makeEllipsoidMesh(radiusOne, radiusTwo, radiusThree) {\n      const DETAIL = 32;\n      let ellipsoidGeo = new this.THREE.SphereGeometry(radiusOne, DETAIL, DETAIL).applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radiusOne, radiusThree / radiusOne));\n      let ellipsoidMesh = new this.THREE.Mesh(ellipsoidGeo);\n      return ellipsoidMesh;\n    }\n\n    segmentVectorsAreValid(segmentVector1, segmentVector2) {\n      return segmentVector1 && segmentVector2 && (segmentVector1.x || segmentVector1.y || segmentVector1.z) && (segmentVector2.x || segmentVector2.y || segmentVector2.z);\n    }\n\n  }\n\n  _exports.Ellipsoid = Ellipsoid;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Point\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Point = void 0;\n\n  class Point {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n    }\n\n    drawPoint(pointData, materialProps) {\n      let pointProps = materialProps.point;\n      let lineProps = materialProps.line;\n      let pointType = pointData.objectGeneration.type;\n      let pointMesh;\n\n      if (pointType == 1) {\n        pointMesh = this.drawPointPlanar(pointData, pointProps);\n        pointMesh.generationType = \"point\";\n      } else if (pointType == 2) {\n        pointMesh = this.drawPointExtruded(pointData, lineProps);\n        pointMesh.generationType = \"line\";\n      } else {\n        pointMesh = this.drawPointRotated(pointData, lineProps);\n        pointMesh.generationType = \"line\";\n      }\n\n      return pointMesh;\n    }\n\n    drawPointPlanar(pointData, pointProps) {\n      let insertionPoint = pointData.insertionPoint;\n      let pointRadius = pointProps.size / 2;\n      let pointColor = pointProps.color;\n      let pointGeo = new this.THREE.SphereBufferGeometry(pointRadius, 32, 16);\n      let pointMaterial = this.utils.getMaterialFromColorProps(pointColor);\n      let pointMesh = new this.THREE.Mesh(pointGeo, pointMaterial);\n      pointMesh.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n      return pointMesh;\n    }\n\n    drawPointExtruded(pointData, lineProps) {\n      let insertionPoint = pointData.insertionPoint;\n      let extrusionVector = pointData.objectGeneration.extrusionVector;\n      let [pointOne, pointTwo] = this.getExtrudedPointPoints(insertionPoint, extrusionVector);\n      let lineMesh = this.utils.drawConnectedLines([pointOne, pointTwo], lineProps);\n      return lineMesh;\n    }\n\n    getExtrudedPointPoints(insertionPoint, extrusionVector) {\n      let extrusion = this.utils.initVector3(extrusionVector);\n      let pointOne = this.utils.initVector3(insertionPoint);\n      let pointTwo = pointOne.clone().add(extrusion);\n      return [pointOne, pointTwo];\n    }\n\n    drawPointRotated(pointData, lineProps) {\n      let circlePoints = this.getRotatedPoints(pointData);\n      let lineMesh = this.utils.drawConnectedLines(circlePoints, lineProps);\n      return lineMesh;\n    }\n\n    getRotatedPoints(pointData) {\n      let insertionPoint = pointData.insertionPoint;\n      let rotationPoint = pointData.objectGeneration.rotationPoint;\n      let rotationAxis = this.utils.initVector3(pointData.objectGeneration.rotationAxis);\n      let rotationAngle = pointData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = pointData.objectGeneration.rotationPitch;\n      let pivotPoint = new this.THREE.Object3D();\n      pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n      let positionPoint = new this.THREE.Object3D();\n      positionPoint.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n      pivotPoint.attach(positionPoint);\n      let startPoint = this.utils.initVector3(insertionPoint);\n      let circlePoints = [startPoint];\n      let positionVector = new this.THREE.Vector3(); // dummy vector for obtaining results\n\n      let numOfSegments = 64 * rotationAngle / (Math.PI * 2);\n\n      for (let i = 0; i < numOfSegments; i++) {\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle / numOfSegments);\n        pivotPoint.updateMatrixWorld();\n        let newPoint = new this.THREE.Vector3().add(positionPoint.getWorldPosition(positionVector));\n        newPoint.add(rotationAxis.clone().normalize().multiplyScalar((i + 1) * rotationPitch / numOfSegments));\n        circlePoints.push(newPoint);\n      }\n\n      return circlePoints;\n    }\n\n  }\n\n  _exports.Point = Point;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polygon\", [\"exports\", \"m08-2020/lib/ThreeExtensions/RotatedGeometry\", \"m08-2020/lib/ThreeExtensions/PlanarRotationPointsGenerator\", \"m08-2020/lib/ThreeExtensions/PlanarExtrusionPointsGenerator\"], function (_exports, _RotatedGeometry, _PlanarRotationPointsGenerator, _PlanarExtrusionPointsGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Polygon = void 0;\n\n  class Polygon {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawPolygon(polygonData) {\n      let polygon;\n      let isPlanar = this.polygonIsPlanar(polygonData);\n\n      if (polygonData.objectGeneration.type === 1) {\n        polygon = this.drawPolygonPlanar(polygonData);\n        polygon.generationType = \"area\";\n      } else if (polygonData.objectGeneration.type === 2) {\n        if (!isPlanar) {\n          polygon = this.drawPolygonExtruded(polygonData);\n          polygon.generationType = \"volume\";\n        } else {\n          polygon = this.drawPolygonExtrudedPlanar(polygonData);\n          polygon.generationType = \"area\";\n        }\n      } else {\n        if (isPlanar) {\n          polygon = this.drawPolygonRotatedPlanar(polygonData);\n          polygon.generationType = \"area\";\n        } else {\n          polygon = this.drawPolygonRotated(polygonData);\n          polygon.generationType = \"volume\";\n        }\n      }\n\n      return polygon;\n    }\n\n    drawPolygonPlanar(polygonData) {\n      let polygonDepth = 0.01;\n      let extrusionVector = new this.THREE.Vector3(0, 0, polygonDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne); // extrusionVector.z *= -1;\n\n      let extrudeSettings = {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings); // geometry.translate(0, 0, -extrusionVector.z);\n\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(midVector);\n      polygon.updateMatrix();\n      polygon.setRotationFromEuler(eulerTwo);\n      return polygon;\n    }\n\n    drawPolygonExtruded(polygonData) {\n      let extrusionVector = this.utils.initVector3(polygonData.objectGeneration.extrusionVector);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo, crossVector] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      extrusionVector.applyEuler(eulerOne); // TODO: I am not sure this logic is correct\n\n      crossVector.applyEuler(eulerOne);\n      let skewParams = this.utils.getSkewParams(extrusionVector);\n      let polygonDepth = extrusionVector.z;\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let geometry = this.GraphicsThree3D.makeExtrudeGeoRaw(shapePts, polygonDepth); // TODO: I am not sure this logic is correct\n\n      if (crossVector.dot(new this.THREE.Vector3(0, 0, -1)) > 0) {\n        geometry.translate(0, 0, -polygonDepth);\n      }\n\n      this.utils.skewExtrudedGeometry(geometry, skewParams);\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.position.add(midVector);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      return polygon;\n    }\n\n    drawPolygonExtrudedPlanar(polygonData) {\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let polygonPoints = this.getPolygonPlanarPoints(polygonData);\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(polygonPoints, zAxis);\n      polygonPoints.forEach(point => {\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let extrusionVector = this.utils.initVector3(polygonData.objectGeneration.extrusionVector).applyEuler(eulerOne);\n      let shapePts = new _PlanarExtrusionPointsGenerator.PlanarExtrusionPointsGenerator(polygonPoints, extrusionVector).extrudedPoints;\n      let shape = new this.THREE.Shape(shapePts);\n      let geometry = new this.THREE.ExtrudeGeometry(shape, {\n        depth: 0.01,\n        bevelEnabled: false\n      });\n      geometry.translate(midVector.x, midVector.y, midVector.z);\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      return polygon;\n    }\n\n    drawPolygonRotated(polygonData) {\n      let pts = this.getPolygonPlanarPoints(polygonData);\n      let rotatesPlanar = this.GraphicsThree3D.objectRotatesPlanar(polygonData);\n      let rotatedGeometry = new _RotatedGeometry.RotatedGeometry(pts.map(pt => pt.clone()), {\n        rotationPoint: this.utils.initVector3(polygonData.objectGeneration.rotationPoint),\n        rotationAxis: this.utils.initVector3(polygonData.objectGeneration.rotationAxis).normalize(),\n        rotationAngle: polygonData.objectGeneration.rotationAngle * Math.PI / 180,\n        rotationPitch: polygonData.objectGeneration.rotationPitch\n      }, rotatesPlanar);\n      let polygon = new this.THREE.Mesh(rotatedGeometry); ///\n\n      let linePairs = [];\n      this.WireframeGenerator.getRegularWireframePointPairs(polygon, Math.PI / 6).forEach(pair => linePairs.push([pair[0].clone().applyMatrix4(polygon.matrix), pair[1].clone().applyMatrix4(polygon.matrix)]));\n      this.WireframeGenerator.getVolumeWireframeData(polygon, linePairs, linePairs, polygonData.objectType); ///\n\n      return polygon;\n    }\n\n    drawPolygonRotatedPlanar(polygonData) {\n      let rotationPoint = this.utils.initVector3(polygonData.objectGeneration.rotationPoint);\n      let rotationAxis = this.utils.initVector3(polygonData.objectGeneration.rotationAxis).normalize();\n      let rotationAngle = polygonData.objectGeneration.rotationAngle * Math.PI / 180;\n      let rotationPitch = polygonData.objectGeneration.rotationPitch;\n      let polygonPoints = this.getPolygonPlanarPoints(polygonData);\n      let zAxis = new this.THREE.Vector3(0, 0, 1);\n      let [_, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(polygonPoints, zAxis);\n      polygonPoints.forEach(point => {\n        point.sub(rotationPoint);\n        point.applyEuler(eulerOne);\n      });\n      let zValue = polygonPoints[0].z;\n      if (rotationAxis.applyEuler(eulerOne).dot(zAxis) < 0) rotationAngle *= -1;\n      let rotationPtsGen = new _PlanarRotationPointsGenerator.PlanarRotationPointsGenerator(polygonPoints, {\n        rotationAngle,\n        rotationPitch\n      });\n      polygonPoints = rotationPtsGen.rotatedPoints;\n      let holePtsArr = rotationPtsGen.holePoints;\n      let shapePts = polygonPoints.map(point => new this.THREE.Vector3(point.x, point.y, 0));\n      let shape = new this.THREE.Shape(shapePts);\n      holePtsArr.forEach(holePts => {\n        if (holePts.length > 2) shape.holes.push(new this.THREE.Path(holePts));\n      });\n      let geometry = new this.THREE.ExtrudeGeometry(shape, {\n        depth: -0.01,\n        bevelEnabled: false\n      });\n      geometry.translate(rotationPoint.x, rotationPoint.y, rotationPoint.z + zValue);\n      let polygon = new this.THREE.Mesh(geometry);\n      polygon.setRotationFromEuler(eulerTwo);\n      polygon.updateMatrix();\n      return polygon;\n    }\n\n    getPolygonPlanarPoints(polygonData) {\n      let points = [];\n\n      for (let c = 0; c < polygonData.points.length; c++) {\n        let x = polygonData.points[c].x;\n        let y = polygonData.points[c].y;\n        let z = polygonData.points[c].z;\n        points.push(new this.THREE.Vector3(x, y, z));\n      }\n\n      return points;\n    }\n\n    polygonIsPlanar(polygonData) {\n      return polygonData.objectGeneration.type === 1 || polygonData.objectGeneration.type === 2 && this.utils.vectorIsZero(polygonData.objectGeneration.extrusionVector) || polygonData.objectGeneration.type === 2 && this.GraphicsThree3D.objectExtrudesPlanar(polygonData) || polygonData.objectGeneration.type === 3 && this.GraphicsThree3D.objectRotatesPlanar(polygonData) && !polygonData.objectGeneration.rotationPitch;\n    }\n\n  }\n\n  _exports.Polygon = Polygon;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polyline\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Polyline = void 0;\n\n  class Polyline {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.CSG = GraphicsThree3D.CSG;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n    }\n\n    drawPolyline(lineData, materialProps) {\n      let lineProps = materialProps.line;\n      let lineType = lineData.objectGeneration.type;\n      let lineMesh;\n\n      if (lineType == 1) {\n        lineMesh = this.drawPolylinePlanar(lineData, lineProps);\n        lineMesh.generationType = \"line\";\n      } else if (lineType == 2) {\n        lineMesh = this.drawPolylineExtruded(lineData);\n        lineMesh.generationType = \"area\";\n      } else if (lineType == 3) {\n        lineMesh = this.drawPolylineRotated(lineData);\n        lineMesh.generationType = \"area\";\n      }\n\n      return lineMesh;\n    }\n\n    drawPolylinePlanar(lineData, lineProps) {\n      let linePoints = lineData.points;\n      let lineVerticies = [];\n      linePoints.forEach(point => lineVerticies.push(this.utils.initVector3(point)));\n      let lineMesh = this.utils.drawConnectedLines(lineVerticies, lineProps);\n      return lineMesh;\n    }\n\n    drawPolylineExtruded(lineData) {\n      let polylineDepth = 0.01;\n      let depthExtrusion = new this.THREE.Vector3(0, 0, polylineDepth);\n      let extrusionNormalized = depthExtrusion.clone().normalize();\n      let linePoints = lineData.points;\n      let extrusionVector = lineData.objectGeneration.extrusionVector;\n      let basePoints = [];\n      let extrudePoints = [];\n      linePoints.forEach(point => {\n        let originVector = new this.THREE.Vector3(point.x, point.y, point.z);\n        let extrudedVector = new this.THREE.Vector3(point.x + extrusionVector.x, point.y + extrusionVector.y, point.z + extrusionVector.z);\n        basePoints.push(originVector);\n        extrudePoints.push(extrudedVector);\n      });\n      extrudePoints = extrudePoints.reverse();\n      let points = basePoints.concat(extrudePoints); // this.WireframeGenerator.getPlanarPoints(points.map(point => point.clone()), lineData.objectType);\n\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(points, extrusionNormalized);\n      points.forEach(point => {\n        // this.GraphicsThree3D.wireframePoints.push(point.clone());\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = points.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      depthExtrusion.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: depthExtrusion.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      let areaMesh = new this.THREE.Mesh(geometry);\n      areaMesh.position.add(midVector);\n      areaMesh.updateMatrix();\n      areaMesh.setRotationFromEuler(eulerTwo);\n      return areaMesh;\n    }\n\n    drawPolylineRotated(lineData) {\n      let linePoints = lineData.points;\n      let rotationPoint = lineData.objectGeneration.rotationPoint;\n      let rotationAxis = this.utils.initVector3(lineData.objectGeneration.rotationAxis);\n      let rotationAngle = lineData.objectGeneration.rotationAngle * Math.PI / 180;\n      let botPoints = [];\n      let topPoints = [];\n      let startPoints = [];\n      let endPoints = [];\n      let dummyVector = new this.THREE.Vector3();\n      linePoints.forEach((point, index) => {\n        botPoints.push(this.utils.initVector3(point));\n\n        if (index > 0 && index < linePoints.length - 1) {\n          let topObject = new this.THREE.Object3D();\n          topObject.position.set(point.x, point.y, point.z);\n          let pivotPoint = new this.THREE.Object3D();\n          pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n          pivotPoint.attach(topObject);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n          pivotPoint.updateMatrixWorld();\n          let newTopPoint = topObject.getWorldPosition(dummyVector).clone();\n          topPoints.push(newTopPoint);\n        }\n      });\n      let startObject = new this.THREE.Object3D();\n      startObject.position.set(linePoints[0].x, linePoints[0].y, linePoints[0].z);\n      let endObject = new this.THREE.Object3D();\n      endObject.position.set(linePoints[linePoints.length - 1].x, linePoints[linePoints.length - 1].y, linePoints[linePoints.length - 1].z);\n      let pivotPoint = new this.THREE.Object3D();\n      pivotPoint.position.set(rotationPoint.x, rotationPoint.y, rotationPoint.z);\n      pivotPoint.attach(startObject);\n      pivotPoint.attach(endObject);\n      let numOfSegments = Math.abs(64 * rotationAngle / (Math.PI * 2));\n\n      for (let i = 0; i < numOfSegments; i++) {\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle / numOfSegments);\n        pivotPoint.updateMatrixWorld();\n        let newStartPoint = startObject.getWorldPosition(dummyVector).clone();\n        startPoints.push(newStartPoint);\n        let newEndPoint = endObject.getWorldPosition(dummyVector).clone();\n        endPoints.push(newEndPoint);\n      }\n\n      topPoints.reverse();\n      startPoints.reverse();\n      let polylineDepth = 0.01;\n      let extrusionVector = new this.THREE.Vector3(0, 0, polylineDepth);\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let pts = botPoints.concat(endPoints).concat(topPoints).concat(startPoints); // this.WireframeGenerator.getPlanarPoints(pts.map(point => point.clone()), lineData.objectType);\n\n      let [midVector, eulerOne, eulerTwo] = this.GraphicsThree3D.getRotationParameters(pts, extrusionNormalized);\n      pts.forEach(point => {\n        // this.GraphicsThree3D.wireframePoints.push(point.clone());\n        point.sub(midVector);\n        point.applyEuler(eulerOne);\n      });\n      let shapePts = pts.map(point => new this.THREE.Vector2(point.x, point.y));\n      let shape = new this.THREE.Shape(shapePts);\n      extrusionVector.applyEuler(eulerOne);\n      extrusionNormalized.applyEuler(eulerOne);\n      let extrudeSettings = {\n        depth: extrusionVector.z,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      let areaMesh = new this.THREE.Mesh(geometry);\n      areaMesh.position.add(midVector);\n      areaMesh.updateMatrix();\n      areaMesh.setRotationFromEuler(eulerTwo);\n      return areaMesh;\n    }\n\n  }\n\n  _exports.Polyline = Polyline;\n});","define(\"m08-2020/lib/FigureLoaders/DrawingObjectLoader\", [\"exports\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Point\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polyline\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipse\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Polygon\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/Ellipsoid\", \"m08-2020/lib/FigureLoaders/DrawingObjectComponents/ComplexVolume\", \"m08-2020/lib/Utils/BooleanOperations\"], function (_exports, _Point, _Polyline, _Ellipse, _Polygon, _Ellipsoid, _ComplexVolume, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DrawingObjectLoader = void 0;\n  // TODO: Move this another place?\n  const LINE_MIN_THICKNESS = 0.4; //\n\n  class DrawingObjectLoader {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.THREE = GraphicsThree3D.THREE;\n      this.utils = GraphicsThree3D.utils;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator;\n      this.Ellipsoid = new _Ellipsoid.Ellipsoid(GraphicsThree3D);\n      this.Point = new _Point.Point(GraphicsThree3D);\n      this.Polyline = new _Polyline.Polyline(GraphicsThree3D);\n      this.Ellipse = new _Ellipse.Ellipse(GraphicsThree3D);\n      this.Polygon = new _Polygon.Polygon(GraphicsThree3D);\n      this.ComplexVolume = new _ComplexVolume.ComplexVolume(GraphicsThree3D);\n    }\n\n    drawPoints(points, properties, positivesAndNegatives) {\n      points.forEach(point => {\n        if (point.objectType > 0 && point.objectType < 3) {\n          let pointMesh = this.Point.drawPoint(point, properties);\n          if (point.objectGeneration.type === 1) this.addToPosAndNeg(pointMesh, point, positivesAndNegatives, \"point\");else if (point.objectGeneration.type === 2 || point.objectGeneration.type === 3) this.addToPosAndNeg(pointMesh, point, positivesAndNegatives, \"line\");\n        }\n      });\n    }\n\n    drawPolylines(polylines, properties, positivesAndNegatives) {\n      polylines.forEach(polyline => {\n        if (polyline.objectType > 0 && polyline.objectType < 3) {\n          let lineMesh = this.Polyline.drawPolyline(polyline, properties);\n          if (polyline.objectGeneration.type === 1) this.addToPosAndNeg(lineMesh, polyline, positivesAndNegatives, \"line\");else if (polyline.objectGeneration.type === 2 || polyline.objectGeneration.type === 3) this.addToPosAndNeg(lineMesh, polyline, positivesAndNegatives, \"area\");\n        }\n      });\n    }\n\n    drawEllipses(ellipses, positivesAndNegatives) {\n      ellipses.forEach(ellipse => {\n        if (ellipse.objectType > 0 && ellipse.objectType < 3) {\n          let ellipseMesh = this.Ellipse.drawEllipse(ellipse);\n          ellipseMesh.updateMatrix();\n\n          if (ellipse.objectGeneration.type === 1 || ellipse.objectGeneration.type === 2 && this.GraphicsThree3D.objectExtrudesPlanar(ellipse, true) || ellipse.objectGeneration.type === 3 && !ellipse.objectGeneration.rotationAngle || ellipse.objectGeneration.type === 3 && this.GraphicsThree3D.objectRotatesPlanar(ellipse, true) && !ellipse.objectGeneration.rotationPitch) {\n            this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"area\");\n          } else {\n            if (ellipse.objectGeneration.type === 2) this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"volumeRounded\");else if (ellipse.objectGeneration.type === 3) this.addToPosAndNeg(ellipseMesh, ellipse, positivesAndNegatives, \"volumeRounded\");\n          }\n        }\n      });\n    }\n\n    drawPolygons(polygons, positivesAndNegatives) {\n      polygons.forEach(polygon => {\n        if (polygon.objectType > 0 && polygon.objectType < 3 && polygon.objectGeneration.type > 0 && polygon.objectGeneration.type < 4) {\n          let polygonMesh = this.Polygon.drawPolygon(polygon);\n          if (this.Polygon.polygonIsPlanar(polygon)) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"area\");else {\n            if (polygon.objectGeneration.type === 2) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"volumeRegular\");else if (polygon.objectGeneration.type === 3) this.addToPosAndNeg(polygonMesh, polygon, positivesAndNegatives, \"volumeRounded\");\n          }\n        }\n      });\n    }\n\n    drawEllipsoids(ellipsoids, positivesAndNegatives) {\n      ellipsoids.forEach(ellipsoid => {\n        if (ellipsoid.objectType > 0 && ellipsoid.objectType < 3) {\n          let ellipsoidMesh = this.Ellipsoid.drawEllipsoid(ellipsoid);\n          this.addToPosAndNeg(ellipsoidMesh, ellipsoid, positivesAndNegatives, \"volumeRounded\");\n        }\n      });\n    }\n\n    drawComplexVolumes(drawingObject, jsonData) {\n      let components = drawingObject.components;\n      let properties = drawingObject.properties;\n      let multilayeredObjects = {\n        positives: [],\n        negatives: []\n      };\n      let wireframes = [];\n      if (components.polygons) wireframes.push(...this.ComplexVolume.drawMultilayeredPolygons(components.polygons, properties, multilayeredObjects));\n      if (components.ellipses) wireframes.push(...this.ComplexVolume.drawMultilayeredEllipses(components.ellipses, properties, multilayeredObjects));\n      if (components.ellipsoids) wireframes.push(...this.ComplexVolume.drawMultilayeredEllipsoids(components.ellipsoids, properties, multilayeredObjects));\n      this.ComplexVolume.addMultilayeredObjectsToScene(multilayeredObjects, wireframes, properties, drawingObject.insertionPoints, jsonData, drawingObject);\n    }\n\n    drawRegularObjects(drawingObject, jsonData) {\n      if (drawingObject.insertionPoints) {\n        let components = drawingObject.components;\n        let properties = drawingObject.properties;\n        let positivesAndNegatives = {\n          area: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          volumeRegular: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          volumeRounded: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          point: {\n            positiveObjects: [],\n            negativeObjects: []\n          },\n          line: {\n            positiveObjects: [],\n            negativeObjects: []\n          }\n        };\n        if (components.points) this.drawPoints(components.points, properties, positivesAndNegatives);\n        if (components.polylines) this.drawPolylines(components.polylines, properties, positivesAndNegatives);\n        if (components.ellipses) this.drawEllipses(components.ellipses, positivesAndNegatives);\n        if (components.polygons) this.drawPolygons(components.polygons, positivesAndNegatives);\n        if (components.ellipsoids) this.drawEllipsoids(components.ellipsoids, positivesAndNegatives);\n        let areas = positivesAndNegatives.area;\n        let volumesRegular = positivesAndNegatives.volumeRegular;\n        let volumesRounded = positivesAndNegatives.volumeRounded;\n        let points = positivesAndNegatives.point;\n        let lines = positivesAndNegatives.line;\n        this.generateAreas(areas, drawingObject, properties, jsonData); ///\n\n        this.generateVolumes(volumesRegular, volumesRounded, drawingObject, properties, jsonData); ///\n\n        this.generatePoints(points, drawingObject, properties, jsonData);\n        this.generateLines(lines, drawingObject, properties, jsonData);\n      }\n    }\n\n    generateAreas(areas, drawingObject, properties, jsonData) {\n      let areaPos = areas.positiveObjects;\n      let areaNeg = areas.negativeObjects;\n\n      if (areaPos.length > 0) {\n        let positiveObject = BooleanOperations.applyCSG(areaPos, areaNeg);\n        let objectMaterial = this.GraphicsThree3D.getAreaMaterial(positiveObject, properties);\n\n        if (objectMaterial !== null) {\n          positiveObject.material = objectMaterial;\n          positiveObject.material.side = this.THREE.FrontSide;\n          let object = positiveObject;\n          let objectTransparent = this.GraphicsThree3D.makeTransparentClone(object);\n          objectTransparent.applyMatrix4(object.matrix);\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          object.stencilNeeded = true;\n          object.addToDXF = true;\n          let lineProps = { ...properties.line,\n            thickness: properties.line.thickness >= LINE_MIN_THICKNESS ? properties.line.thickness : LINE_MIN_THICKNESS\n          };\n          this.WireframeGenerator.applyWireframesArea(object, objectTransparent, lineProps, drawingObject);\n          this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"area\", \"drawingObject\", drawingObject);\n        }\n      }\n    }\n\n    generateVolumes(volumesRegular, volumesRounded, drawingObject, properties, jsonData) {\n      let volumePosRegular = volumesRegular.positiveObjects;\n      let volumeNegRegular = volumesRegular.negativeObjects;\n      let volumePosRounded = volumesRounded.positiveObjects;\n      let volumeNegRounded = volumesRounded.negativeObjects;\n      let positiveObjectRegular = volumePosRegular.length > 0 ? BooleanOperations.applyCSG(volumePosRegular, volumeNegRegular) : null;\n      let positiveObjectRounded = volumePosRounded.length > 0 ? BooleanOperations.applyCSG(volumePosRounded, volumeNegRounded) : null;\n      let positiveObject;\n      if (positiveObjectRegular && positiveObjectRounded) positiveObject = BooleanOperations.doCSG(positiveObjectRegular, positiveObjectRounded, \"union\");else if (positiveObjectRegular && !positiveObjectRounded && volumeNegRounded.length > 0) {\n        positiveObject = positiveObjectRegular;\n        volumeNegRounded.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n      } else if (positiveObjectRegular && !positiveObjectRounded) positiveObject = positiveObjectRegular;else if (!positiveObjectRegular && positiveObjectRounded && volumeNegRegular.length > 0) {\n        positiveObject = positiveObjectRounded;\n        volumeNegRegular.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n      } else if (!positiveObjectRegular && positiveObjectRounded) positiveObject = positiveObjectRounded;else positiveObject = null;\n\n      if (positiveObject) {\n        ///\n        volumeNegRegular.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\"));\n        volumeNegRounded.forEach(negativeObject => positiveObject = BooleanOperations.doCSG(positiveObject, negativeObject, \"subtract\")); ///\n\n        let objectMaterial = this.GraphicsThree3D.getVolumeMaterialsIndexFaces(positiveObject, properties); // objectMaterial.forEach( mat => mat.wireframe = true );\n\n        if (objectMaterial !== null) {\n          objectMaterial.forEach(mat => mat.side = this.THREE.FrontSide);\n          positiveObject.material = objectMaterial;\n          let [doubleSideObject, frontSideObject, backSideObject] = this.GraphicsThree3D.getThreeSideFaceObjects(positiveObject);\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          this.GraphicsThree3D.indexMeshFaces(frontSideObject, properties);\n          this.GraphicsThree3D.indexMeshFaces(backSideObject, properties);\n          let objectTransparent = this.GraphicsThree3D.makeTransparentClone(positiveObject);\n          let geo = positiveObject.geometry.clone();\n          backSideObject.renderOrder = 0; // DepthClear mesh\n\n          let depthClearMesh = new this.THREE.Mesh(geo, objectMaterial);\n          depthClearMesh.applyMatrix4(positiveObject.matrix);\n          depthClearMesh.renderOrder = -100;\n\n          depthClearMesh.onBeforeRender = function (renderer) {\n            renderer.clearDepth();\n          };\n\n          this.GraphicsThree3D.addObjectToScene(depthClearMesh, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject); // FrontSide material mesh\n\n          frontSideObject.stencilNeeded = true;\n          frontSideObject.addToDXF = true;\n          frontSideObject.dxfLayer = `drawingObject_${drawingObject.id}`;\n\n          if (positiveObjectRegular) {\n            let linePairs = this.WireframeGenerator.getRegularWireframePointPairs(positiveObjectRegular);\n            linePairs = linePairs.map(pair => [pair[0].clone().applyMatrix4(positiveObjectRegular.matrix), pair[1].clone().applyMatrix4(positiveObjectRegular.matrix)]);\n            this.WireframeGenerator.getVolumeWireframeData(positiveObjectRegular, linePairs, linePairs, 1);\n          }\n\n          let lineProps = { ...properties.line,\n            thickness: properties.line.thickness >= LINE_MIN_THICKNESS ? properties.line.thickness : LINE_MIN_THICKNESS\n          };\n          this.WireframeGenerator.applyWireframesVolume(frontSideObject, objectTransparent, lineProps, drawingObject);\n          this.GraphicsThree3D.addObjectToScene(frontSideObject, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject); // BackSide material mesh\n\n          this.GraphicsThree3D.addObjectToScene(backSideObject, objectTransparent, drawingObject.insertionPoints, jsonData, \"volume\", \"drawingObject\", drawingObject);\n        }\n      }\n    }\n\n    generatePoints(points, drawingObject, properties, jsonData) {\n      let pointPos = points.positiveObjects;\n      let pointNeg = points.negativeObjects;\n\n      if (pointPos.length > 0) {\n        let positiveObject = BooleanOperations.applyCSG(pointPos, pointNeg);\n        let objectMaterial = this.GraphicsThree3D.getPointMaterial(properties);\n\n        if (objectMaterial !== null) {\n          positiveObject.material = objectMaterial;\n          let object = positiveObject;\n          let objectTransparent = null;\n          this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n          this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"point\", \"drawingObject\", drawingObject);\n          this.addPointObjectsToDXF(drawingObject);\n        }\n      }\n    }\n\n    generateLines(lines, drawingObject, properties, jsonData) {\n      // TODO: AddLines back to dim chain collision detection\n      let linePos = lines.positiveObjects;\n      let lineNeg = lines.negativeObjects;\n\n      if (linePos.length > 0) {\n        linePos.forEach(line => {\n          let objectMaterial = this.GraphicsThree3D.getlineMeshMaterial(properties);\n\n          if (line.material !== null) {\n            line.material = objectMaterial;\n            let object = line;\n            object.renderOrder = -6;\n            let objectTransparent = null;\n            this.addToDimChainCollisionDetection(line, drawingObject.insertionPoints);\n            this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"line\", \"drawingObject\", drawingObject);\n            this.addLineObjectsToDXF(drawingObject);\n          }\n        }); // let positiveObject = BooleanOperations.applyCSG(linePos, lineNeg);\n        // let objectMaterial = this.GraphicsThree3D.getlineMeshMaterial(properties);\n        // if (objectMaterial !== null) {\n        //   positiveObject.material = objectMaterial;\n        //   let object = positiveObject;\n        //   object.renderOrder = -6;\n        //   let objectTransparent = null;\n        //   this.addToDimChainCollisionDetection(positiveObject, drawingObject.insertionPoints);\n        //   this.GraphicsThree3D.addObjectToScene(object, objectTransparent, drawingObject.insertionPoints, jsonData, \"line\", \"drawingObject\", drawingObject);\n        //   this.addLineObjectsToDXF(drawingObject);\n        // }\n      }\n    }\n\n    addLineObjectsToDXF(lineData) {\n      let insertionPoints = lineData.insertionPoints;\n      let points = lineData.components.points;\n      let polylines = lineData.components.polylines;\n      insertionPoints.forEach(insertionPoint => {\n        let insertionVector = insertionPoint.insertionVector;\n        if (points) points.forEach(point => {\n          if (point.objectType === 1) {\n            if (point.objectGeneration.type === 2) {\n              let pointPair = this.Point.getExtrudedPointPoints(point.insertionPoint, point.objectGeneration.extrusionVector);\n              if (insertionPoint.rotationAngle) pointPair = pointPair.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n              pointPair.forEach(point => point.add(insertionVector));\n              this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n                id: lineData.id,\n                properties: lineData.properties.line,\n                pointPairs: [pointPair]\n              });\n            } else if (point.objectGeneration.type === 3) {\n              let circlePoints = this.Point.getRotatedPoints(point);\n              if (insertionPoint.rotationAngle) circlePoints = circlePoints.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n              circlePoints.forEach(point => point.add(insertionVector));\n              let circlePointPairs = [];\n\n              for (let i = 0; i < circlePoints.length - 1; i++) circlePointPairs.push([circlePoints[i], circlePoints[i + 1]]);\n\n              this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n                id: lineData.id,\n                properties: lineData.properties.line,\n                pointPairs: circlePointPairs\n              });\n            }\n          }\n        });\n        if (polylines) polylines.forEach(polyline => {\n          if (polyline.objectType === 1 && polyline.objectGeneration.type === 1) {\n            let polylinePoints = polyline.points.map(point => this.utils.initVector3(point));\n            if (insertionPoint.rotationAngle) polylinePoints = polylinePoints.map(point => this.utils.applyRotationToVector(point, insertionPoint));\n            polylinePoints.forEach(point => point.add(insertionVector));\n            let pointPairs = [];\n\n            for (let i = 0; i < polylinePoints.length - 1; i++) pointPairs.push([polylinePoints[i], polylinePoints[i + 1]]);\n\n            this.GraphicsThree3D.dxfGenerator.lineObjectsData.push({\n              id: lineData.id,\n              properties: lineData.properties.line,\n              pointPairs\n            });\n          }\n        });\n      });\n    }\n\n    addPointObjectsToDXF(pointData) {\n      let insertionPoints = pointData.insertionPoints;\n      let points = pointData.components.points;\n      points.forEach(point => {\n        if (point.objectType === 1 && point.objectGeneration.type === 1) {\n          insertionPoints.forEach(insertionPoint => {\n            let insertionVector = insertionPoint.rotationAngle ? this.utils.applyRotationToVector(insertionPoint.insertionVector, insertionPoint) : insertionPoint.insertionVector;\n            this.GraphicsThree3D.dxfGenerator.pointObjectsData.push({\n              id: pointData.id,\n              properties: pointData.properties.point,\n              position: this.utils.addVectorObjects(point.insertionPoint, insertionVector)\n            });\n          });\n        }\n      });\n    }\n\n    addToDimChainCollisionDetection(object, insertionPoints) {\n      insertionPoints.forEach(insertionPoint => {\n        let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n        let newObject = object.clone();\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          pivotPoint.attach(newObject);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else pivotPoint.attach(newObject);\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        let baseObject = pivotPoint.children[0];\n        pivotPoint.children.forEach(child => child.applyMatrix4(pivotPoint.matrixWorld));\n        let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(baseObject);\n        this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n      });\n    }\n\n    addToPosAndNeg(mesh, objectData, positivesAndNegatives, type) {\n      if (objectData.objectType == 1) positivesAndNegatives[type].positiveObjects.push(mesh);else positivesAndNegatives[type].negativeObjects.push(mesh);\n    }\n\n    addPointsToWireframeRegions(points, wireframeRegions) {\n      points.forEach(pointsData => {\n        let offsetVector = pointsData.normal.clone().multiplyScalar(pointsData.distance);\n        let pointsArr = pointsData.pointsArrData;\n        let [eulerOne, eulerTwo] = this.utils.getEulerOneAndTwo(pointsData.normal);\n        let pointsPos = pointsArr.positives;\n        let pointsNeg = pointsArr.negatives;\n        let newRegionData = {\n          offsetVector,\n          eulerTwo,\n          positiveRegions: [],\n          negativeRegions: []\n        };\n        pointsPos.forEach(pointArr => {\n          let newRegion = {\n            regions: [[]],\n            inverted: false\n          };\n          pointArr.forEach(point => {\n            point.applyEuler(eulerOne);\n            newRegion.regions[0].push([point.x, point.y]);\n          });\n          newRegionData.positiveRegions.push(newRegion);\n        });\n        pointsNeg.forEach(pointArr => {\n          let newRegion = {\n            regions: [[]],\n            inverted: false\n          };\n          pointArr.forEach(point => {\n            point.applyEuler(eulerOne);\n            newRegion.regions[0].push([point.x, point.y]);\n          });\n          newRegionData.negativeRegions.push(newRegion);\n        });\n        wireframeRegions.push(newRegionData);\n      });\n    }\n\n  }\n\n  _exports.DrawingObjectLoader = DrawingObjectLoader;\n});","define(\"m08-2020/lib/FigureLoaders/FastenerLoader\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.FastenerLoader = void 0;\n\n  function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, \"set\"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }\n\n  function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError(\"attempted to set read only private field\"); } descriptor.value = value; } }\n\n  function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, \"get\"); return _classApplyDescriptorGet(receiver, descriptor); }\n\n  function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError(\"attempted to \" + action + \" private field on non-instance\"); } return privateMap.get(receiver); }\n\n  function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }\n\n  class FastenerLoader {\n    constructor(THREE, GraphicsThree3D) {\n      _fasteners.set(this, {\n        writable: true,\n        value: []\n      });\n\n      _visibleStatus.set(this, {\n        writable: true,\n        value: true\n      });\n\n      this.THREE = THREE;\n      this.CSG = GraphicsThree3D.CSG;\n      this.fontUrlArray = GraphicsThree3D.fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = GraphicsThree3D.utils;\n      this.WireframeGenerator = GraphicsThree3D.WireframeGenerator; ///\n\n      this.drawFasteners = this.drawFasteners.bind(this);\n    }\n\n    drawFasteners() {\n      let jsonData = this.GraphicsThree3D.jsonData;\n      let dxfFastenerPoints = this.GraphicsThree3D.dxfGenerator.fastenerPoints;\n\n      for (let fastener of jsonData.fasteners) {\n        this.addToDXFPoints(dxfFastenerPoints, fastener);\n\n        if (fastener.components.head && fastener.components.head.type > 8) {\n          this.drawClamp(fastener);\n        } else {\n          let meshArray = this.drawFastener(fastener);\n\n          _classPrivateFieldGet(this, _fasteners).push(...meshArray); // meshArray.forEach(obj => this.addToDimChainCollisionDetection(obj));\n\n\n          meshArray.forEach(obj => this.addToScene(fastener, obj));\n        }\n      }\n    }\n\n    changeFastenersVisibility() {\n      _classPrivateFieldSet(this, _visibleStatus, !_classPrivateFieldGet(this, _visibleStatus));\n\n      for (const fastener of _classPrivateFieldGet(this, _fasteners)) {\n        for (const child of fastener.children) {\n          if (child.type !== 'Object3D' && !child.isIntersectionBox) child.visible = _classPrivateFieldGet(this, _visibleStatus);\n        }\n      }\n    }\n\n    clearFastenerVisibilityState() {\n      _classPrivateFieldSet(this, _fasteners, []);\n\n      _classPrivateFieldSet(this, _visibleStatus, true);\n    }\n\n    addToDXFPoints(dxfFastenerPoints, fastenerData) {\n      if (fastenerData.components.axis && fastenerData.properties.axis && fastenerData.properties.axis.line && fastenerData.properties.axis.point) {\n        let axisLength = fastenerData.components.axis.length;\n        dxfFastenerPoints[fastenerData.id] = {\n          axis: [],\n          addon: [],\n          fastener: []\n        };\n        fastenerData.insertionPoints.forEach(insertionPoint => {\n          let directionVec = this.utils.initVector3(insertionPoint.directionVector1);\n          let startPoint = this.utils.initVector3(insertionPoint.insertionPoint);\n          let endPoint = startPoint.clone().add(directionVec.multiplyScalar(axisLength));\n          let dxfData = {\n            pointPair: [startPoint, endPoint],\n            properties: {\n              line: fastenerData.properties.axis.line,\n              point: fastenerData.properties.axis.point\n            }\n          };\n          dxfFastenerPoints[fastenerData.id].axis.push(dxfData);\n        });\n      }\n    }\n\n    addToDimChainCollisionDetection(obj) {\n      obj.updateMatrix();\n      obj.traverse(child => {\n        if (child.type === \"Mesh\" && !(child.geometry.faceVertexUvs && child.geometry.faceVertexUvs[0].length === 0)) {\n          let intersectionObject = child.clone();\n          intersectionObject.applyMatrix4(obj.matrix);\n          let intersectionObjects = this.GraphicsThree3D.getThreeSideFaceObjects(intersectionObject);\n          this.GraphicsThree3D.DimensionalChainIntersectionDetection.meshArr.push(intersectionObjects);\n        }\n      });\n    }\n\n    drawFastener(fastener) {\n      let meshArray = [];\n\n      if (fastener.components.axis) {\n        let axisData = fastener.components.axis;\n        let shankData = fastener.components.shank;\n        let headData = fastener.components.head;\n        let threadsData = fastener.components.threads;\n        let tipData = fastener.components.tip;\n        let fastenerProps = fastener.properties; // Axis\n\n        let axisGeo, axisPointGeo;\n        let axisMat, axisPointMat;\n        let inversedDirection = false;\n\n        if (axisData.length < 0) {\n          inversedDirection = true;\n          axisData.length *= -1;\n        }\n\n        if (fastenerProps.axis) {\n          if (axisData.length > 0) {\n            let lineProps = fastenerProps.axis.line;\n            if (lineProps.thickness && lineProps.type > 0) axisGeo = this.getAxisGeo(axisData, fastenerProps.axis);\n            axisMat = this.GraphicsThree3D.getMaterialFromColor(lineProps.color);\n          }\n\n          let pointProps = fastenerProps.axis.point;\n          if (pointProps.size) axisPointGeo = this.getAxisPointGeo(pointProps);\n          axisPointMat = this.GraphicsThree3D.getMaterialFromColor(pointProps.color);\n        }\n\n        let headSideLineProps = fastenerProps.fastener.headSide.line;\n        let fastenerMaterial = this.getMaterial(fastener.properties, \"fastener\");\n        let headSideMeshes = [];\n        let tipSideMeshes = []; // Heads\n\n        let headGeo = this.getFastenerHeadGeo(headData);\n        let head = this.drawFastenerHead(headData, headGeo, fastenerMaterial);\n        if (head) headSideMeshes.push(head);\n        let drawBottomFrameHead = true;\n\n        if (shankData && head) {\n          if (headData.type == 1 && (shankData.type == 1 || shankData.type == 2 && shankData.size >= headData.bottomDiameter.value)) drawBottomFrameHead = false;\n        }\n\n        let headWireframes;\n        if (headSideLineProps && head) headWireframes = this.makeHeadWireframe(headGeo, headSideLineProps, drawBottomFrameHead);\n\n        if (threadsData) {\n          let threadMaxPoint = threadsData[threadsData.length - 1].insertionDistance + threadsData[threadsData.length - 1].length;\n          if (axisData.length == 0 || threadMaxPoint > axisData.length) axisData.length = threadMaxPoint;\n        }\n\n        let segmentsLeft = [[0, axisData.length]];\n\n        if (head && headData && (headData.type == 1 || headData.type == 2 || headData.type == 6)) {\n          segmentsLeft[0][0] = headData.height.value;\n        }\n\n        if (tipData && tipData.type && tipData.height && (fastenerMaterial || fastenerProps.fastener.tipSide)) {\n          segmentsLeft[0][1] -= tipData.height;\n        }\n\n        let threadSegments = [];\n        if (threadsData) threadsData.forEach(thread => {\n          let minPoint = thread.insertionDistance;\n          let maxPoint = thread.insertionDistance + thread.length;\n\n          if (minPoint < segmentsLeft[0][0]) {\n            minPoint = segmentsLeft[0][0];\n          }\n\n          if (maxPoint > segmentsLeft[0][1]) {\n            maxPoint = segmentsLeft[0][1];\n          }\n\n          let threadParams = {\n            coreDiameter: thread.coreDiameter,\n            distance: thread.distance,\n            height: thread.height,\n            pitch: thread.pitch,\n            threadDiameter: thread.threadDiameter\n          };\n          threadSegments.push([minPoint, maxPoint, threadParams]);\n        });\n        threadSegments.forEach(threadSegment => {\n          let minPoint = threadSegment[0];\n          let maxPoint = threadSegment[1];\n          segmentsLeft.forEach(segment => {\n            if (minPoint > segment[0] && minPoint < segment[1] && maxPoint > segment[0] && maxPoint < segment[1]) {\n              segmentsLeft.push([maxPoint, segment[1]]);\n              segment[1] = minPoint;\n            } else {\n              if (minPoint > segment[0] && minPoint < segment[1]) segment[1] = minPoint;\n              if (maxPoint > segment[0] && maxPoint < segment[1]) segment[0] = maxPoint;\n            }\n          });\n        });\n        let tipDiameter;\n\n        if (threadSegments.length > 0 && segmentsLeft.length > 0) {\n          let lastThreadMax = threadSegments[threadSegments.length - 1][1];\n          let lastShankMax = segmentsLeft[segmentsLeft.length - 1][1];\n          let threadDiameter = threadSegments[threadSegments.length - 1][2].coreDiameter;\n          let shankSize = shankData ? shankData.size : 0;\n          if (lastThreadMax > lastShankMax) tipDiameter = threadDiameter;else if (!shankSize) tipDiameter = threadDiameter;else tipDiameter = shankSize;\n        } else if (threadSegments.length > 0) {\n          tipDiameter = threadSegments[threadSegments.length - 1][2].coreDiameter;\n        } else if (segmentsLeft.length > 0) {\n          tipDiameter = shankData.size;\n        }\n\n        if (tipDiameter == 0) {\n          tipDiameter = headData.bottomDiameter.value;\n        }\n\n        let tipSideProps = fastenerProps.fastener.tipSide;\n        let tipSideMaterial;\n\n        if (shankData && tipSideProps && shankData.tipSideLength) {\n          tipSideMaterial = this.getMaterial(fastenerProps, \"tipSide\");\n          let tipSideStartPoint = axisData.length - shankData.tipSideLength;\n          segmentsLeft.forEach((segment, index) => {\n            let minPoint = segment[0];\n            let maxPoint = segment[1];\n\n            if (tipSideStartPoint > minPoint && tipSideStartPoint < maxPoint) {\n              segmentsLeft.splice(index + 1, 0, [tipSideStartPoint, maxPoint, true]);\n              segment[1] = tipSideStartPoint;\n              segment[2] = false;\n            } else if (tipSideStartPoint < minPoint && tipSideStartPoint < maxPoint) {\n              segment[2] = true;\n            } else {\n              segment[2] = false;\n            }\n          });\n        }\n\n        let shankGeo = this.getFastenerShankGeo(segmentsLeft, fastener.components.shank, fastener);\n        let tipGeo = this.getFastenerTipGeo(fastener.components.tip, tipDiameter, fastener, axisData.length);\n        let isFastenerWithPitch = false;\n        let isFastenerWithoutPitch = false;\n        let cylGeo, spiralGeo, withoutPitchGeo, withoutPitchCylGeo, cylWireframes, coreWireframes;\n        let wireframeWithoutPitchPts, wireframeWithPitchPts;\n        let intersectionThreadMeshes = [];\n\n        if (threadsData) {\n          threadsData.forEach(thread => {\n            if (thread.pitch && !thread.distance) isFastenerWithPitch = true;else if (!thread.pitch && thread.distance) isFastenerWithoutPitch = true;\n          });\n          intersectionThreadMeshes.push(...this.getThreadIntersectionBoxes(threadSegments));\n\n          if (isFastenerWithPitch) {\n            [cylGeo, spiralGeo, wireframeWithPitchPts, cylWireframes] = this.getSpiralAndCylGeo(threadSegments, fastenerProps);\n          }\n\n          if (isFastenerWithoutPitch) {\n            [withoutPitchGeo, withoutPitchCylGeo, wireframeWithoutPitchPts, coreWireframes] = this.getThreadWithoutPitchGeo(threadSegments, fastenerProps);\n          }\n        } // Axis\n\n\n        let axis;\n        if (fastenerProps.axis) axis = this.drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat, fastener.properties.axis); // Threads\n\n        let threads = [];\n\n        if (threadsData) {\n          if (isFastenerWithPitch) {\n            let [cylinder, spiral] = this.drawFastenerThread_0_WithPitch(cylGeo, spiralGeo, fastenerMaterial, wireframeWithPitchPts, headSideLineProps);\n            if (cylinder) headSideMeshes.push(cylinder);\n            threads.push(spiral);\n          }\n\n          if (isFastenerWithoutPitch) {\n            let [thread, core] = this.drawFastenerThread_1_WithoutPitch(withoutPitchGeo, withoutPitchCylGeo, fastenerMaterial, wireframeWithoutPitchPts, headSideLineProps);\n            threads.push(thread);\n            if (core) headSideMeshes.push(core);\n          }\n        } // Tip\n\n\n        let drawTopFrameShank = true;\n        let drawBotFrameShank = true;\n        let tipWireframes;\n        let tip = this.drawFastenerTip(tipGeo, fastenerMaterial, tipSideMaterial);\n\n        if (tip) {\n          let drawTopTip = true;\n\n          if (shankData && segmentsLeft[segmentsLeft.length - 1][1] == axisData.length - tipData.height) {\n            if (shankData.type == 1 && (tipData.type == 1 || tipData.type == 2)) {\n              drawTopTip = false;\n              drawBotFrameShank = false;\n            } else if (shankData.type == 2 && (tipData.type == 3 || tipData.type == 4 || tipData.type == 5 || tipData.type == 6)) {\n              drawTopTip = false;\n              drawBotFrameShank = false;\n            }\n          } else if (threadsData && threadSegments && threadSegments[threadSegments.length - 1][1] == axisData.length - tipData.height) {\n            if (tipData.type == 1 || tipData.type == 2) drawTopTip = false;\n          }\n\n          tipWireframes = this.makeTipWireframe(tipData, tipGeo, tipSideProps && shankData.tipSideLength ? tipSideProps.line : headSideLineProps, drawTopTip);\n          if (tip.isTipSide) tipSideMeshes.push(tip);else headSideMeshes.push(tip);\n        }\n\n        if (shankData && head) {\n          let diameterToCompare = headData.bottomDiameter.value !== 0 ? headData.bottomDiameter.value : headData.topDiameter.value;\n          if (headData.wrenchSize) diameterToCompare = headData.wrenchSize;\n\n          if (shankData.type == 1 && diameterToCompare >= shankData.size) {\n            drawTopFrameShank = false;\n          }\n        } // Shank\n\n\n        let [shanks, shankWireframes] = this.drawFastenerShank(fastener.components.shank, fastener.properties, shankGeo, drawTopFrameShank, drawBotFrameShank);\n        shanks.headSide.forEach(shank => {\n          if (shank) headSideMeshes.push(shank);\n        });\n        shanks.tipSide.forEach(shank => {\n          if (shank) tipSideMeshes.push(shank);\n        });\n        let headSideMesh = headSideMeshes.splice(0, 1)[0];\n        let tipSideMesh = tipSideMeshes.splice(0, 1)[0];\n        headSideMeshes.forEach(mesh => {\n          if (mesh) {\n            let firstCSG = this.CSG.fromMesh(headSideMesh);\n            let secondCSG = this.CSG.fromMesh(mesh);\n            let unionCSG = firstCSG.union(secondCSG);\n            let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n            headSideMesh = unionMesh;\n          }\n        });\n        tipSideMeshes.forEach(mesh => {\n          if (mesh) {\n            let firstCSG = this.CSG.fromMesh(tipSideMesh);\n            let secondCSG = this.CSG.fromMesh(mesh);\n            let unionCSG = firstCSG.union(secondCSG);\n            let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n            tipSideMesh = unionMesh;\n          }\n        });\n        if (headSideMesh && fastenerMaterial) headSideMesh.material = fastenerMaterial;\n        if (tipSideMesh && tipSideMaterial) tipSideMesh.material = tipSideMaterial;\n        if (headSideMesh) headSideMesh.material.side = this.THREE.FrontSide;\n        if (tipSideMesh) tipSideMesh.material.side = this.THREE.FrontSide;\n        let backSideHead, backSideTip;\n\n        if (headSideMesh) {\n          let geo = headSideMesh.geometry.clone();\n          let mat = headSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideHead = new this.THREE.Mesh(geo, mat);\n        }\n\n        if (tipSideMesh) {\n          let geo = tipSideMesh.geometry.clone();\n          let mat = tipSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideTip = new this.THREE.Mesh(geo, mat);\n        }\n\n        for (let b = 0; b < fastener.insertionPoints.length; b++) {\n          let intersectionPoint = fastener.insertionPoints[b];\n          let insertionPoint = intersectionPoint.insertionPoint;\n          let insertionVector = this.utils.initVector3(insertionPoint);\n          let rotationVector = this.utils.normalizeVector(intersectionPoint.directionVector1);\n          let rotationVectorTwo = this.utils.normalizeVector(intersectionPoint.directionVector2);\n          let objectFastener = new this.THREE.Object3D();\n          let clonedHeadSide, clonedTipSide;\n\n          if (headSideMesh) {\n            clonedHeadSide = headSideMesh.clone();\n            objectFastener.add(clonedHeadSide);\n            clonedHeadSide.isHeadSide = true;\n          }\n\n          if (tipSideMesh) {\n            clonedTipSide = tipSideMesh.clone();\n            objectFastener.add(clonedTipSide);\n            clonedHeadSide.isTipSide = true;\n          }\n\n          if (axis) objectFastener.add(axis.clone());\n          threads.forEach(thread => {\n            if (thread) {\n              let clonedThread = thread.clone();\n              clonedThread.isThread = true;\n              objectFastener.add(clonedThread);\n            }\n          });\n\n          if (backSideHead) {\n            backSideHead = backSideHead.clone();\n            objectFastener.add(backSideHead);\n          }\n\n          if (backSideTip) {\n            backSideTip = backSideTip.clone();\n            objectFastener.add(backSideTip);\n          } // AddOns\n\n\n          let [addOns, addOnDxfPointsData] = this.generateAddons(fastener.components.addOns, fastenerProps.addOn);\n          addOns.forEach(addOn => {\n            if (addOn) {\n              addOn.renderOrder = -2;\n              addOn.isAddOn = true;\n              let backSide = this.drawBacksideMesh(addOn);\n              backSide.renderOrder = -3;\n              addOn.add(backSide);\n              objectFastener.add(addOn);\n            }\n          }); ///\n\n          intersectionThreadMeshes.forEach(mesh => {\n            let clonedBox = mesh.clone();\n            clonedBox.isIntersectionBox = true;\n            objectFastener.add(clonedBox);\n          }); ///\n\n          if (headWireframes) headWireframes.forEach(wireframeArr => wireframeArr.forEach(mesh => {\n            objectFastener.add(mesh.clone());\n          }));\n          if (shankWireframes) shankWireframes.forEach(wireframe => wireframe.forEach(segment => {\n            objectFastener.add(segment.clone());\n          }));\n          if (tipWireframes) tipWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => {\n            objectFastener.add(wireframe.clone());\n          }));\n          if (cylWireframes) cylWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => wireframe.forEach(line => objectFastener.add(line.clone()))));\n          if (coreWireframes) coreWireframes.forEach(wireframeArr => wireframeArr.forEach(wireframe => wireframe.forEach(line => objectFastener.add(line.clone()))));\n          let directionVector = new this.THREE.Vector3(rotationVector.x, rotationVector.y, rotationVector.z);\n          let directionVectorTwo = new this.THREE.Vector3(rotationVectorTwo.x, rotationVectorTwo.y, rotationVectorTwo.z);\n\n          if (inversedDirection) {\n            directionVector.negate();\n            directionVectorTwo.negate();\n          }\n\n          if (clonedTipSide) {\n            let objects = {\n              clonedTipSide,\n              backSideTip,\n              clonedHeadSide,\n              backSideHead\n            };\n            let fastenerProps = {\n              axisData,\n              shankData,\n              directionVector,\n              insertionVector\n            };\n            this.removeHeadTipConnection(objects, fastenerProps);\n          }\n\n          objectFastener.children.forEach(child => {\n            if (!child.isAddOn) child.renderOrder = -4;\n          });\n          if (backSideHead) backSideHead.renderOrder = -5;\n          if (backSideTip) backSideTip.renderOrder = -5;\n          this.rotateObject(objectFastener, directionVector, directionVectorTwo);\n          objectFastener.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z);\n          meshArray.push(objectFastener);\n          objectFastener.children.forEach(child => {\n            if (child.type === \"Mesh\") {\n              if (fastener.properties.fastener.headSide && (child.isHeadSide || child.isThread) && this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id]) {\n                this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id].fastener.push({\n                  mesh: child,\n                  color: fastener.properties.fastener.headSide.volume.color\n                });\n              } else if (fastener.properties.fastener.tipSide && child.isTipSide && this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id]) {\n                this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id].fastener.push({\n                  mesh: child,\n                  color: fastener.properties.fastener.tipSide.volume.color\n                });\n              }\n            }\n          });\n          this.GraphicsThree3D.addObjectToVisControl(objectFastener, \"fastener\"); // Might need better solution\n\n          objectFastener.updateMatrix();\n          addOnDxfPointsData.forEach(ptData => {\n            if (ptData) {\n              ptData.mesh.applyMatrix4(objectFastener.matrix);\n              ptData.mesh.updateMatrixWorld();\n              ptData.points.forEach(pts => pts.forEach(pt => pt.applyMatrix4(objectFastener.matrix)));\n              this.GraphicsThree3D.dxfGenerator.fastenerPoints[fastener.id].addon.push(ptData);\n            }\n          });\n        }\n      }\n\n      for (let object of meshArray) this.GraphicsThree3D.addObjToDynamicSectionalPlanes(fastener, object, \"fasteners\");\n\n      return meshArray;\n    }\n\n    generateAddons(addOnsDataArr, addOnProps) {\n      let addOns = [];\n      let addOnDxfPointsData = [];\n\n      if (addOnsDataArr) {\n        addOnsDataArr.forEach(addOn => {\n          let mesh, dxfData;\n          if (addOn.type === 1) [mesh, dxfData] = this.drawPolygonNut(addOn, addOnProps);else if (addOn.type === 2) [mesh, dxfData] = this.drawCircleWasher(addOn, addOnProps);else if (addOn.type === 3) [mesh, dxfData] = this.drawRectangleWasher(addOn, addOnProps);else if (addOn.type === 4) [mesh, dxfData] = this.drawAngleWasherTypeA(addOn, addOnProps);else if (addOn.type === 5) [mesh, dxfData] = this.drawAngleWasherTypeB(addOn, addOnProps);else if (addOn.type === 6) [mesh, dxfData] = this.drawFtConnector(addOnProps);\n\n          if (mesh && dxfData) {\n            addOns.push(mesh);\n            addOnDxfPointsData.push({ ...dxfData,\n              mesh: mesh.clone()\n            });\n          }\n        });\n      }\n\n      return [addOns, addOnDxfPointsData];\n    }\n\n    removeHeadTipConnection(objects, fastenerProps) {\n      let axisData = fastenerProps.axisData;\n      let shankData = fastenerProps.shankData;\n      let directionVector = fastenerProps.directionVector;\n      let insertionVector = fastenerProps.insertionVector;\n      let clonedTipSide = objects.clonedTipSide;\n      let backSideTip = objects.backSideTip;\n      let clonedHeadSide = objects.clonedHeadSide;\n      let backSideHead = objects.backSideHead;\n      let shankLength = axisData.length;\n      let tipOffset = shankData.tipSideLength;\n      let planeVector = directionVector.clone().normalize();\n      let positionVector = insertionVector.clone().projectOnVector(planeVector);\n      let positionDistance = -(positionVector.length() + shankLength - tipOffset + 0.0001);\n      let clippingPlane = new this.THREE.Plane(planeVector, positionDistance);\n      clonedTipSide.material.clippingPlanes = [clippingPlane];\n      backSideTip.material.clippingPlanes = [clippingPlane];\n\n      if (clonedHeadSide) {\n        let shankLength = axisData.length;\n        let tipOffset = shankData.tipSideLength;\n        let planeVector = directionVector.clone().negate().normalize();\n        let positionVector = insertionVector.projectOnVector(planeVector);\n        let positionDistance = positionVector.length() + shankLength - tipOffset - 0.0001;\n        let clippingPlane = new this.THREE.Plane(planeVector, positionDistance);\n        clonedHeadSide.material.clippingPlanes = [clippingPlane];\n        backSideHead.material.clippingPlanes = [clippingPlane];\n      }\n    }\n\n    drawDoublesideFrame(geo, lineProps, drawTop = true, drawBot = true, drawEdges = false) {\n      let partOne = [];\n      let partTwo = [];\n      let wireframes = [];\n      let geoVertices = geo.vertices;\n      let firstPart = geoVertices[0].y;\n      geoVertices.forEach(vertex => {\n        if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n      });\n\n      if (geo.type == \"CylinderGeometry\") {\n        partOne.splice(partOne.length - 1, 1);\n        partTwo.splice(partTwo.length - 1, 1);\n      }\n\n      if (drawTop && partOne.length > 0) {\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        wireframes.push([wireframeOne]);\n      }\n\n      if (drawBot && partTwo.length > 0) {\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        wireframes.push([wireframeTwo]);\n      }\n\n      if (drawEdges) {\n        let edgesLineArr = this.drawEdgesFrame(partOne, partTwo, lineProps);\n        wireframes.push(edgesLineArr);\n      }\n\n      return wireframes;\n    }\n\n    drawEdgesFrame(partOne, partTwo, lineProps) {\n      // partOne must be array, partTwo can be vector\n      let edgesLineArr = [];\n\n      if (partOne.length > 0) {\n        if (partOne.length == partTwo.length) {\n          for (let i = 0; i < partOne.length; i++) {\n            let line = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(line);\n          }\n        } else {// TODO: vector as point\n        }\n      }\n\n      return edgesLineArr;\n    }\n\n    getThreadIntersectionBoxes(threadSegments) {\n      let boxArr = [];\n      threadSegments.forEach(segmentData => {\n        let boxHeight = segmentData[1] - segmentData[0];\n        let boxWidth = segmentData[2].threadDiameter;\n        let boxGeo = new this.THREE.BoxGeometry(boxWidth, boxHeight, boxWidth);\n        boxGeo.translate(0, -boxHeight / 2 - segmentData[0], 0);\n        let boxMat = new this.THREE.MeshBasicMaterial();\n        let box = new this.THREE.Mesh(boxGeo, boxMat);\n        box.visible = false;\n        boxArr.push(box);\n      });\n      return boxArr;\n    }\n\n    drawFastenerHead(headData, headGeo, fastenerMaterial) {\n      let fastenerHead;\n\n      if (headData) {\n        if (headData.type == 1 || headData.type == 2) {\n          fastenerHead = this.drawFastenerHead_1_Countersunk(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 3) {\n          fastenerHead = this.drawFastenerHead_3_Lentil(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 4) {\n          fastenerHead = this.drawFastenerHead_4_DiscFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 5) {\n          fastenerHead = this.drawFastenerHead_5_DiscBottomFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 6) {\n          fastenerHead = this.drawFastenerHead_6_DiscTopFlat(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 7) {\n          fastenerHead = this.drawFastenerHead_7_PolygonWithoutWasher(headGeo, fastenerMaterial, headData);\n        } else if (headData.type == 8) {\n          fastenerHead = this.drawFastenerHead_8_PolygonWithWasher(headGeo, fastenerMaterial, headData);\n        } else {\n          console.log(\"Head type undefined\");\n        }\n      } else {\n        fastenerHead = null;\n      }\n\n      return fastenerHead;\n    }\n\n    drawBacksideMesh(mesh) {\n      let geo = mesh.geometry.clone();\n      let mat = mesh.material.clone();\n      mat.side = this.THREE.BackSide;\n      let backsideMesh = new this.THREE.Mesh(geo, mat);\n      return backsideMesh;\n    }\n\n    getAddonDxfData_PolygonWireframe(addonMesh, addonWireframes, addOnProps) {\n      let ptsArr = [];\n      addonWireframes.forEach(wireframe => wireframe.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n        if (line.type === \"LineLoop\") pts.push(pts[0].clone());\n        pts.forEach(pt => pt.applyMatrix4(addonMesh.matrix));\n        ptsArr.push(pts);\n      }));\n      return {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n    }\n\n    getAddonDxfData_CircleWasher(addonMesh, addonWireframes, addOnProps) {\n      let ptsArr = [];\n      let sidePoints = [];\n      addonWireframes.forEach(wireframe => wireframe.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n\n        if (pts.length > 0) {\n          pts.push(pts[0].clone());\n          pts.forEach(pt => pt.applyMatrix4(addonMesh.matrix));\n          ptsArr.push(pts);\n        }\n      }));\n\n      if (ptsArr.length === 2) {\n        ptsArr[0].forEach((point, idx) => sidePoints.push([point.clone(), ptsArr[1][idx].clone()]));\n        ptsArr.push(...sidePoints);\n      }\n\n      return {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n    }\n\n    drawPolygonNut(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height > 0 && addOnData.numberOfEdges > 2 && addOnProps && addOnData.wrenchSize > 0) {\n        let insertionDistance = addOnData.insertionDistance;\n        let height = addOnData.height;\n        let wrenchSize = addOnData.wrenchSize / 2;\n        let numberOfEdges = addOnData.numberOfEdges;\n        let angle = (numberOfEdges - 2) * 180 / numberOfEdges / 2;\n        angle *= Math.PI / 180;\n        let radius = wrenchSize / Math.sin(angle);\n        let geometry = new this.THREE.CylinderGeometry(radius, radius, height, numberOfEdges);\n        geometry.rotateY(Math.PI / 6); //temp\n\n        geometry.translate(0, -height / 2, 0);\n        geometry.isPolygon = true;\n        let volumeColor = addOnProps.volume.color;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let lineProps = addOnProps.line;\n        let addonWireframes = this.makeHeadWireframe(geometry, lineProps);\n        addonWireframes.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n        mesh.position.set(0, -insertionDistance, 0);\n        mesh.updateMatrix();\n        dxfData = this.getAddonDxfData_PolygonWireframe(mesh, addonWireframes, addOnProps);\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawCircleWasher(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height > 0 && addOnProps) {\n        let insertionDistance = addOnData.insertionDistance;\n        let height = addOnData.height;\n        let topRadius = addOnData.topDiameter / 2;\n        let botRadius = addOnData.bottomDiameter / 2;\n\n        if (topRadius > 0 || botRadius > 0) {\n          let geometry = new this.THREE.CylinderGeometry(topRadius, botRadius, height, 32);\n          geometry.translate(0, -height / 2, 0);\n          geometry.isAddOn = true;\n          let volumeColor = addOnProps.volume.color;\n          let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n          volumeMaterial.side = this.THREE.FrontSide;\n          mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n          let lineProps = addOnProps.line;\n          let addonWireframes = this.makeHeadWireframe(geometry, lineProps);\n          addonWireframes.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n          mesh.position.set(0, -insertionDistance, 0);\n          mesh.updateMatrix();\n          dxfData = this.getAddonDxfData_CircleWasher(mesh, addonWireframes, addOnProps);\n        }\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawRectangleWasher(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.width > 0 && addOnData.length > 0 && addOnData.height > 0 && addOnProps) {\n        let insertionDistance = addOnData.insertionDistance + 0.001;\n        let width = addOnData.width;\n        let length = addOnData.length;\n        let height = addOnData.height;\n        let geometry = new this.THREE.BoxGeometry(length, height, width);\n        geometry.translate(0, -height / 2, 0);\n        let volumeColor = addOnProps.volume.color;\n        let lineProps = addOnProps.line;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        volumeMaterial.side = this.THREE.FrontSide;\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let partOne = [];\n        let partTwo = [];\n        let geoVertices = geometry.vertices;\n        let firstPart = geoVertices[0].y;\n        geoVertices.forEach(vertex => {\n          if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n        });\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        mesh.add(wireframeOne);\n        mesh.add(wireframeTwo);\n        let edgesLineArr = [];\n\n        for (let i = 0; i < partOne.length; i++) {\n          let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n          edgesLineArr.push(newLine);\n        }\n\n        edgesLineArr.forEach(line => mesh.add(line)); ///\n\n        mesh.position.set(0, -insertionDistance, 0);\n        mesh.updateMatrix();\n        let ptsArr = [];\n        let partOneClone = partOne.map(pt => pt.clone().applyMatrix4(mesh.matrix));\n        let partTwoClone = partTwo.map(pt => pt.clone().applyMatrix4(mesh.matrix));\n        let edgePoints = partOneClone.reduce((ptsArr, pt, i) => {\n          ptsArr.push([pt.clone(), partTwoClone[i].clone()]);\n          return ptsArr;\n        }, []);\n        partOneClone.push(partOneClone[0].clone());\n        partTwoClone.push(partTwoClone[0].clone());\n        ptsArr.push(partOneClone, partTwoClone, ...edgePoints);\n        dxfData = {\n          points: ptsArr,\n          properties: addOnProps.line\n        };\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawAngleWasherTypeA(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnProps && addOnData.length && addOnData.width && addOnData.height && addOnData.normalLength && addOnData.orthogonalLength) {\n        let insertionDistance = addOnData.insertionDistance + 0.001;\n        let orthogonalLength = addOnData.orthogonalLength;\n        let height = addOnData.height;\n        let length = addOnData.length;\n        let width = addOnData.width;\n        let BT = Math.round(Math.sqrt(orthogonalLength / 2 * (orthogonalLength / 2) / 2) * 100) / 100;\n        let A = {\n          x: -BT,\n          y: BT - height\n        };\n        let B = {\n          x: -BT,\n          y: -BT\n        };\n        let C = {\n          x: BT,\n          y: BT\n        };\n        let D = {\n          x: length - BT,\n          y: BT - height\n        };\n        let pts = [];\n        pts.push(new this.THREE.Vector2(A.x, A.y));\n        pts.push(new this.THREE.Vector2(B.x, B.y));\n        pts.push(new this.THREE.Vector2(C.x, C.y));\n        pts.push(new this.THREE.Vector2(D.x, D.y));\n        let shape = new this.THREE.Shape(pts);\n        let extrudeSettings = {\n          depth: width,\n          bevelEnabled: false\n        };\n        let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n        let volumeColor = addOnProps.volume.color;\n        let lineProps = addOnProps.line;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(geometry, volumeMaterial); ///\n\n        let pointsArr = pts.map(point => new this.THREE.Vector3(point.x, point.y, width));\n        let polygonWirefame = this.makePolygonWireframe(pointsArr, width, lineProps);\n        polygonWirefame.forEach(wireframe => wireframe.forEach(line => mesh.add(line))); ///\n\n        mesh.rotation.set(0, -Math.PI / 2, -Math.PI / 4);\n        mesh.position.set(width / 2, -insertionDistance, 0);\n        mesh.updateMatrix();\n        dxfData = this.getAddonDxfData_PolygonWireframe(mesh, polygonWirefame, addOnProps);\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawAngleWasherTypeB(addOnData, addOnProps) {\n      let mesh, dxfData;\n\n      if (addOnData.height && addOnData.width && addOnData.length && addOnData.normalLength && addOnData.orthogonalLength) {\n        let height = addOnData.height;\n        let length = addOnData.length;\n        let width = addOnData.width;\n        let ellipsoidgeometry = new this.THREE.Geometry();\n        let latitudeBands = 32,\n            longitudeBands = 32,\n            a = length / 2,\n            b = width / 2,\n            c = height,\n            size = 1;\n\n        for (let latNumber = 0; latNumber <= latitudeBands; latNumber++) {\n          let theta = latNumber * Math.PI / latitudeBands;\n          let sinTheta = Math.sin(theta);\n          let cosTheta = Math.cos(theta);\n\n          for (let longNumber = 0; longNumber <= longitudeBands; longNumber++) {\n            let phi = longNumber * 2 * Math.PI / longitudeBands;\n            let sinPhi = Math.sin(phi);\n            let cosPhi = Math.cos(phi);\n            let x = a * cosPhi * cosTheta;\n            let z = b * cosTheta * sinPhi;\n            let y = c * sinTheta;\n            ellipsoidgeometry.vertices.push(new this.THREE.Vector3(x * size, y * size, z * size));\n          }\n        }\n\n        for (let latNumber = 0; latNumber < latitudeBands; latNumber++) {\n          for (let longNumber = 0; longNumber < longitudeBands; longNumber++) {\n            let first = latNumber * (longitudeBands + 1) + longNumber;\n            let second = first + longitudeBands + 1;\n            ellipsoidgeometry.faces.push(new this.THREE.Face3(first, second, first + 1));\n            ellipsoidgeometry.faces.push(new this.THREE.Face3(second, second + 1, first + 1));\n          }\n        }\n\n        ellipsoidgeometry.computeFaceNormals();\n        let orthogonalLength = addOnData.orthogonalLength;\n        let normalLength = addOnData.normalLength;\n        let normalHeight = Math.sqrt(Math.pow(normalLength, 2) / 2);\n        let heightProportion = normalHeight / (height / 2);\n        let offsetX = Math.sqrt(Math.pow(orthogonalLength / 2, 2) / 2);\n        ellipsoidgeometry.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n        ellipsoidgeometry.rotateZ(-Math.PI / 4);\n        ellipsoidgeometry.rotateY(-Math.PI / 2);\n        let volumeColor = addOnProps.volume.color;\n        let volumeMaterial = this.GraphicsThree3D.getMaterialFromColor(volumeColor);\n        mesh = new this.THREE.Mesh(ellipsoidgeometry, volumeMaterial);\n        let botCircleGeo = new this.THREE.CircleGeometry(b, 32);\n        botCircleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, a / b, 1));\n        botCircleGeo.rotateY(Math.PI / 2);\n        botCircleGeo.rotateZ(Math.PI / 2);\n        botCircleGeo.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n        botCircleGeo.rotateZ(-Math.PI / 4);\n        botCircleGeo.rotateY(-Math.PI / 2);\n        let botCircle = new this.THREE.Mesh(botCircleGeo, volumeMaterial);\n        mesh.add(botCircle);\n\n        if (addOnProps.line) {\n          let wireframePoints = this.getEllipsePointsXZ(a, b, 0, {\n            x: 0,\n            y: 0,\n            z: 0\n          });\n          let wireframe = this.drawLineFromPoints(wireframePoints, addOnProps.line, true);\n          wireframe.geometry.rotateZ(Math.PI / 2);\n          wireframe.geometry.translate(length / 2 - offsetX, -(height / 2) * heightProportion, 0);\n          wireframe.geometry.rotateZ(-Math.PI / 4);\n          wireframe.geometry.rotateY(-Math.PI / 2);\n          mesh.add(wireframe);\n        }\n\n        mesh.updateMatrix();\n        let ptsArr = [];\n        this.WireframeGenerator.getWireframeLinePairs(ellipsoidgeometry, Math.PI / 360).forEach(([_, ptPairs]) => ptPairs.forEach(pair => {\n          ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(mesh.matrix)));\n        }));\n        dxfData = {\n          points: ptsArr,\n          properties: addOnProps.line\n        };\n      }\n\n      return [mesh, dxfData];\n    }\n\n    drawFtConnector(addOnProps) {\n      // TODO: CLEAN MESS!!!\n      let csg = this.GraphicsThree3D.CSG;\n      const colorAxis = addOnProps.volume.color;\n      const lineProps = addOnProps.line;\n      let addonMaterial = this.GraphicsThree3D.getMaterialFromColor(colorAxis);\n      addonMaterial.side = this.THREE.FrontSide; // top cyl\n\n      let topCylRadius = 1.25;\n      let topCylHeight = 8;\n      let topCylPosition = new this.THREE.Vector3(0, 4.2, 0); // bot cyl\n\n      let botCylRadius = 0.75;\n      let botCylHeight = 10;\n      let botCylPosition = new this.THREE.Vector3(0, -5.8, 0); // box one\n\n      let boxOneWidth = 3;\n      let boxOneHeight = 0.2;\n      let boxOneDepth = 4;\n      let boxOnePosition = new this.THREE.Vector3(0, 0.1, 0); // box two\n\n      let boxTwoWidth = 5;\n      let boxTwoHeight = 0.6;\n      let boxTwoDepth = 6;\n      let boxTwoPosition = new this.THREE.Vector3(0, -0.3, 0); // box three\n\n      let boxThreeWidth = 3;\n      let boxThreeHeight = 0.2;\n      let boxThreeDepth = 4;\n      let boxThreePosition = new this.THREE.Vector3(0, -0.7, 0); // box four\n\n      let boxFourWidth = 3;\n      let boxFourHeight = 0.2;\n      let boxFourDepth = 4;\n      let boxFourPosition = new this.THREE.Vector3(-2.747, -1.431, 0);\n      let boxFourRotationZ = 30 * Math.PI / 180; // box five\n\n      let boxFiveWidth = 11;\n      let boxFiveHeight = 0.2;\n      let boxFiveDepth = 4;\n      let boxFivePosition = new this.THREE.Vector3(-1.258, -6.808, 0);\n      let boxFiveRotationZ = -60 * Math.PI / 180; // eraser box top\n\n      let eraserBoxTopWidth = 7;\n      let eraserBoxTopHeight = 7;\n      let eraserBoxTopDepth = 7;\n      let eraserBoxTopPosition = new this.THREE.Vector3(2.611, 7.314, 0);\n      let eraserBoxTopRotationZ = 30 * Math.PI / 180; // eraser box bot\n\n      let eraserBoxBotWidth = 4;\n      let eraserBoxBotHeight = 4;\n      let eraserBoxBotDepth = 4;\n      let eraserBoxBotPosition = new this.THREE.Vector3(-1.416, -10.393, 0);\n      let eraserBoxBotRotationZ = -60 * Math.PI / 180; // eraser cyl top\n\n      let eraserCylTopRadius = 0.75;\n      let eraserCylTopHeight = 12;\n      let eraserCylTopPosition = new this.THREE.Vector3(0, 5.9, 0); // meshes\n\n      let topCylGeo = new this.THREE.CylinderGeometry(topCylRadius, topCylRadius, topCylHeight, 32);\n      let topCyl = new this.THREE.Mesh(topCylGeo);\n      topCyl.position.copy(topCylPosition);\n      topCyl.updateMatrix();\n      let botCylGeo = new this.THREE.CylinderGeometry(botCylRadius, botCylRadius, botCylHeight, 32);\n      let botCyl = new this.THREE.Mesh(botCylGeo);\n      botCyl.position.copy(botCylPosition);\n      botCyl.updateMatrix();\n      let boxOneGeo = new this.THREE.BoxGeometry(boxOneWidth, boxOneHeight, boxOneDepth);\n      let boxOne = new this.THREE.Mesh(boxOneGeo);\n      boxOne.position.copy(boxOnePosition);\n      boxOne.updateMatrix();\n      let boxTwoGeo = new this.THREE.BoxGeometry(boxTwoWidth, boxTwoHeight, boxTwoDepth);\n      let boxTwo = new this.THREE.Mesh(boxTwoGeo);\n      boxTwo.position.copy(boxTwoPosition);\n      boxTwo.updateMatrix();\n      let boxThreeGeo = new this.THREE.BoxGeometry(boxThreeWidth, boxThreeHeight, boxThreeDepth);\n      let boxThree = new this.THREE.Mesh(boxThreeGeo);\n      boxThree.position.copy(boxThreePosition);\n      boxThree.updateMatrix();\n      boxFourWidth = 3.4;\n      boxFourPosition = new this.THREE.Vector3(-2.747, -1.4, 0);\n      let boxFourGeo = new this.THREE.BoxGeometry(boxFourWidth, boxFourHeight, boxFourDepth);\n      let boxFour = new this.THREE.Mesh(boxFourGeo);\n      boxFour.rotation.z = boxFourRotationZ;\n      boxFour.position.copy(boxFourPosition);\n      boxFour.updateMatrix();\n      boxFivePosition = new this.THREE.Vector3(-1.408, -6.92, 0);\n      let boxFiveGeo = new this.THREE.BoxGeometry(boxFiveWidth, boxFiveHeight, boxFiveDepth);\n      let boxFive = new this.THREE.Mesh(boxFiveGeo);\n      boxFive.rotation.z = boxFiveRotationZ;\n      boxFive.position.copy(boxFivePosition);\n      boxFive.updateMatrix(); // erasers\n\n      let eraserBoxTopGeo = new this.THREE.BoxGeometry(eraserBoxTopWidth, eraserBoxTopHeight, eraserBoxTopDepth);\n      let eraserBoxTop = new this.THREE.Mesh(eraserBoxTopGeo);\n      eraserBoxTop.rotation.z = eraserBoxTopRotationZ;\n      eraserBoxTop.position.copy(eraserBoxTopPosition);\n      eraserBoxTop.updateMatrix();\n      eraserBoxBotPosition = new this.THREE.Vector3(-1.5, -10.6, 0);\n      let eraserBoxBotGeo = new this.THREE.BoxGeometry(eraserBoxBotWidth, eraserBoxBotHeight, eraserBoxBotDepth);\n      let eraserBoxBot = new this.THREE.Mesh(eraserBoxBotGeo);\n      eraserBoxBot.rotation.z = eraserBoxBotRotationZ;\n      eraserBoxBot.position.copy(eraserBoxBotPosition);\n      eraserBoxBot.updateMatrix();\n      let eraserCylTopGeo = new this.THREE.CylinderGeometry(eraserCylTopRadius, eraserCylTopRadius, eraserCylTopHeight, 32);\n      let eraserCylTop = new this.THREE.Mesh(eraserCylTopGeo);\n      eraserCylTop.position.copy(eraserCylTopPosition);\n      eraserCylTop.updateMatrix(); // wireframes\n\n      let boxOneVertices = boxOneGeo.clone().applyMatrix4(boxOne.matrix).vertices;\n      boxOneVertices = [boxOneVertices[0], boxOneVertices[2], boxOneVertices[7], boxOneVertices[5]];\n      let boxTwoVertices = boxTwoGeo.clone().applyMatrix4(boxTwo.matrix).vertices;\n      boxTwoVertices = [boxTwoVertices[0], boxTwoVertices[2], boxTwoVertices[7], boxTwoVertices[5]];\n      let boxThreeVertices = boxThreeGeo.clone().applyMatrix4(boxThree.matrix).vertices;\n      let boxFourVertices = boxFourGeo.clone().applyMatrix4(boxFour.matrix).vertices;\n      let polyVertices = [boxThreeVertices[0], boxThreeVertices[2], new this.THREE.Vector3(-1.51, -0.8, 2.002), new this.THREE.Vector3(-3.997, -2.2365, 2.002), new this.THREE.Vector3(1.432, -11.634, 2.002), new this.THREE.Vector3(1.254, -11.736, 2.002), boxFourVertices[5], new this.THREE.Vector3(-1.565, -0.6, 2.002)]; // let botCylVertices = botCylGeo.clone().applyMatrix4(botCyl.matrix).vertices.splice(0, 32);\n      // let topCylVertices = topCylGeo.clone().applyMatrix4(topCyl.matrix).vertices.splice(32, 32);\n      // let topCutMiddleVertices = this.getEllipsePointsXZ(1.5, 0.75, 30*Math.PI/180, {x: 0, y: 4.85, z: 0});\n\n      let topCutOuterVertices = this.getEllipsePointsXZ(2.5, 1.25, 30 * Math.PI / 180, {\n        x: 0,\n        y: 4.85,\n        z: 0\n      });\n      let botCutVertices = this.getEllipsePointsXZ(1.5, 0.75, 30 * Math.PI / 180, {\n        x: 0,\n        y: -9.15,\n        z: 0\n      });\n      let botCutWireframes = this.drawLineFromPoints(botCutVertices, lineProps, true);\n      let topCutOuterWireframes = this.drawLineFromPoints(topCutOuterVertices, lineProps, true); // let topCutMiddleWireframes = this.drawLineFromPoints(topCutMiddleVertices, lineProps, true);\n      // let topCylBotWireframes = this.drawLineFromPoints(topCylVertices, lineProps, true);\n      // let botCylTopWireframes = this.drawLineFromPoints(botCylVertices, lineProps, true);\n\n      let boxOneWireframes = this.makePolygonWireframe(boxOneVertices, boxOneDepth, lineProps);\n      let boxTwoWireframes = this.makePolygonWireframe(boxTwoVertices, boxTwoDepth, lineProps);\n      let botPolyWireframes = this.makePolygonWireframe(polyVertices, boxOneDepth, lineProps); // boolean operations\n      // TODO: Optimize!!!\n\n      let eraserBoxTopCSG = csg.fromMesh(eraserBoxTop);\n      let topCylCSG = csg.fromMesh(topCyl);\n      topCylCSG = topCylCSG.subtract(eraserBoxTopCSG);\n      topCyl = csg.toMesh(topCylCSG, new this.THREE.Matrix4());\n      let eraserBoxBotCSG = csg.fromMesh(eraserBoxBot);\n      let botCylCSG = csg.fromMesh(botCyl);\n      botCylCSG = botCylCSG.subtract(eraserBoxBotCSG);\n      botCyl = csg.toMesh(botCylCSG, new this.THREE.Matrix4());\n      let eraserCylTopCSG = csg.fromMesh(eraserCylTop);\n      topCylCSG = csg.fromMesh(topCyl);\n      let boxOneCSG = csg.fromMesh(boxOne);\n      let boxTwoCSG = csg.fromMesh(boxTwo);\n      topCylCSG = topCylCSG.subtract(eraserCylTopCSG);\n      boxOneCSG = boxOneCSG.subtract(eraserCylTopCSG);\n      boxTwoCSG = boxTwoCSG.subtract(eraserCylTopCSG);\n      topCyl = csg.toMesh(topCylCSG, new this.THREE.Matrix4());\n      boxOne = csg.toMesh(boxOneCSG, new this.THREE.Matrix4());\n      boxTwo = csg.toMesh(boxTwoCSG, new this.THREE.Matrix4());\n      topCylCSG = csg.fromMesh(topCyl);\n      botCylCSG = csg.fromMesh(botCyl);\n      boxOneCSG = csg.fromMesh(boxOne);\n      boxTwoCSG = csg.fromMesh(boxTwo);\n      let boxThreeCSG = csg.fromMesh(boxThree);\n      let boxFourCSG = csg.fromMesh(boxFour);\n      let boxFiveCSG = csg.fromMesh(boxFive);\n      let unionCSG = topCylCSG.union(boxOneCSG);\n      unionCSG = unionCSG.union(boxTwoCSG);\n      unionCSG = unionCSG.union(boxThreeCSG);\n      unionCSG = unionCSG.union(botCylCSG);\n      unionCSG = unionCSG.union(boxFourCSG);\n      unionCSG = unionCSG.union(boxFiveCSG);\n      let addon = csg.toMesh(unionCSG, new this.THREE.Matrix4());\n      boxOneWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line)));\n      boxTwoWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line)));\n      botPolyWireframes.forEach(wireframe => wireframe.forEach(line => addon.add(line))); // addon.add(botCylTopWireframes);\n      // addon.add(topCylBotWireframes);\n      // addon.add(topCutMiddleWireframes);\n\n      addon.add(topCutOuterWireframes);\n      addon.add(botCutWireframes);\n      addon.rotation.y = -Math.PI / 2;\n      addon.material = addonMaterial;\n      addon.updateMatrix();\n      let ptsArr = [];\n      let ptPairs_TopCyl = this.WireframeGenerator.getRegularWireframePointPairs(topCyl, Math.PI / 360);\n      let ptPairs_BotCyl = this.WireframeGenerator.getRegularWireframePointPairs(botCyl, Math.PI / 360);\n      ptPairs_TopCyl.forEach(pair => ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(addon.matrix))));\n      ptPairs_BotCyl.forEach(pair => ptsArr.push(pair.map(pt => pt.clone().applyMatrix4(addon.matrix))));\n      [boxOneWireframes, boxTwoWireframes, botPolyWireframes].forEach(wireframe => wireframe.forEach(lineArr => lineArr.forEach(line => {\n        let pts = this.utils.getVerticesFromBufferGeometry(line.geometry);\n        if (line.type === \"LineLoop\") pts.push(pts[0].clone());\n        pts.forEach(pt => pt.applyMatrix4(addon.matrix));\n        ptsArr.push(pts);\n      })));\n      let dxfData = {\n        points: ptsArr,\n        properties: addOnProps.line\n      };\n      return [addon, dxfData];\n    }\n\n    makePolygonWireframe(pointsArr, depth, lineProps) {\n      let wireframes = [];\n      let partOne = pointsArr;\n      let partTwo = pointsArr.map(point => point.clone().sub(new this.THREE.Vector3(0, 0, depth)));\n      let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n      wireframes.push([wireframeOne]);\n      let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n      wireframes.push([wireframeTwo]);\n      let edgesLineArr = [];\n\n      for (let i = 0; i < partOne.length; i++) {\n        let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n        edgesLineArr.push(newLine);\n      }\n\n      wireframes.push(edgesLineArr);\n      return wireframes;\n    }\n\n    drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat) {\n      let axis = new this.THREE.Object3D();\n      let axisMesh, axisPointMesh;\n      if (axisGeo) axisMesh = new this.THREE.Mesh(axisGeo, axisMat);\n      if (axisPointGeo) axisPointMesh = new this.THREE.Mesh(axisPointGeo, axisPointMat);\n      if (axisGeo) axisMesh.renderOrder = -6;\n      if (axisPointGeo) axisPointMesh.renderOrder = -6;\n      if (axisMesh) axis.add(axisMesh);\n      if (axisPointMesh) axis.add(axisPointMesh);\n      return axis;\n    }\n\n    getMaterial(properties, target) {\n      let jsonColor;\n      if (target == \"axis\") jsonColor = properties.fastener.axis.line.color;else if (target == \"tipSide\") jsonColor = properties.fastener.tipSide.volume.color;else jsonColor = properties.fastener.headSide.volume.color;\n      let volumeColor = \"rgb(\" + jsonColor.red + \",\" + jsonColor.green + \",\" + jsonColor.blue + \")\";\n      let transparency = jsonColor.alpha / 255;\n      let material = new this.THREE.MeshBasicMaterial({\n        color: volumeColor,\n        opacity: transparency,\n        transparent: true,\n        side: this.THREE.DoubleSide\n      });\n      return material;\n    } // radiuses are for 2D\n\n\n    makeElipsoidGeo(radiusX, radiusY, height) {\n      let geometry = new this.THREE.Geometry();\n      let latitudeBands = 32,\n          longitudeBands = 32,\n          a = radiusX,\n          b = radiusY,\n          c = height,\n          size = 1;\n\n      for (let latNumber = 0; latNumber <= latitudeBands; latNumber++) {\n        let theta = latNumber * Math.PI / latitudeBands;\n        let sinTheta = Math.sin(theta);\n        let cosTheta = Math.cos(theta);\n\n        for (let longNumber = 0; longNumber <= longitudeBands; longNumber++) {\n          let phi = longNumber * 2 * Math.PI / longitudeBands;\n          let sinPhi = Math.sin(phi);\n          let cosPhi = Math.cos(phi);\n          let x = a * cosPhi * cosTheta;\n          let z = b * cosTheta * sinPhi;\n          let y = c * sinTheta;\n          geometry.vertices.push(new this.THREE.Vector3(x * size, y * size, z * size));\n        }\n      }\n\n      for (let latNumber = 0; latNumber < latitudeBands; latNumber++) {\n        for (let longNumber = 0; longNumber < longitudeBands; longNumber++) {\n          let first = latNumber * (longitudeBands + 1) + longNumber;\n          let second = first + longitudeBands + 1;\n          geometry.faces.push(new this.THREE.Face3(first, second, first + 1));\n          geometry.faces.push(new this.THREE.Face3(second, second + 1, first + 1));\n        }\n      }\n\n      geometry.computeFaceNormals();\n      return geometry;\n    } // Geometries\n\n\n    getAxisGeo(axisData, axisProps) {\n      const PRECISION = 8;\n      let axisLength = axisData.length;\n      let axisWidth = axisProps.line.thickness / 2;\n      let axisGeo = new this.THREE.CylinderBufferGeometry(axisWidth, axisWidth, axisLength, PRECISION);\n      axisGeo.translate(0, -axisLength / 2, 0);\n      return axisGeo;\n    }\n\n    getAxisPointGeo(axisProps) {\n      const PRECISION = 8;\n      let pointRadius = axisProps.size / 2;\n      let pointGeo = new this.THREE.SphereBufferGeometry(pointRadius, PRECISION, PRECISION);\n      return pointGeo;\n    }\n    /**\n     * Head Types\n     * 1: Countersunk\n     * 2: Cylinder\n     * 3: Lentil\n     * 4: Disk Flat\n     * 5: Disk Bottom Flat\n     * 6: Disk Top Flat\n     * 7: polygonWithoutWasher\n     * 8: polygonWithWasher\n     */\n\n\n    getFastenerHeadGeoCountersunk(topRadius, bottomRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, bottomRadius, height, radialSegments);\n      headGeo.translate(0, -height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoCylinder(topRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, topRadius, height, radialSegments);\n      headGeo.translate(0, -height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoLentil(topRadius, height, heightFactor, radialSegments) {\n      let topHeight = height * heightFactor;\n      let botHeight = height - topHeight; // let headGeoTop = this.makeElipsoidGeo(topRadius, topRadius, topHeight);\n\n      let headGeoTop = new this.THREE.SphereGeometry(topRadius, 24, 12);\n      headGeoTop.applyMatrix4(new this.THREE.Matrix4().makeScale(1, topHeight / topRadius, 1));\n      let headGeoBot = new this.THREE.CylinderGeometry(topRadius, topRadius, botHeight, radialSegments);\n      headGeoTop.translate(0, botHeight, 0);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    getFastenerHeadGeoDiskFlat(topRadius, height, radialSegments) {\n      let headGeo = new this.THREE.CylinderGeometry(topRadius, topRadius, height, radialSegments);\n      headGeo.translate(0, height / 2, 0);\n      return headGeo;\n    }\n\n    getFastenerHeadGeoDiskBottomFlat(topRadius, bottomRadius, topDiameterFactor, topDiameter, height, heightFactor, radialSegments) {\n      let topHeight = height * heightFactor;\n      let botHeight = height - topHeight;\n      let headGeoTop = new this.THREE.CylinderGeometry(topRadius, (topDiameter + 2 * topDiameterFactor * topDiameter) / 2, topHeight, radialSegments);\n      let headGeoBot = new this.THREE.CylinderGeometry(bottomRadius, bottomRadius, botHeight, radialSegments);\n      headGeoTop.isTypeFiveTop = true;\n      headGeoBot.isTypeSixBottom = true;\n      headGeoTop.translate(0, botHeight + topHeight / 2, 0);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    getFastenerHeadGeoPolyWithoutWasher(wrenchSize, numOfEdges, height) {\n      wrenchSize /= 2;\n      let angle = (numOfEdges - 2) * 180 / numOfEdges / 2;\n      angle *= Math.PI / 180;\n      let radius = wrenchSize / Math.sin(angle);\n      let headGeo = new this.THREE.CylinderGeometry(radius, radius, height, numOfEdges);\n      headGeo.rotateY(angle / 2);\n      headGeo.translate(0, height / 2, 0);\n      headGeo.isPolygon = true;\n      return headGeo;\n    }\n\n    getFastenerHeadGeoPolyWithWasher(wrenchSize, numOfEdges, height, heightFactor, topRadius) {\n      wrenchSize /= 2;\n      let angle = (numOfEdges - 2) * 180 / numOfEdges / 2;\n      angle *= Math.PI / 180;\n      let radius = wrenchSize / Math.sin(angle);\n      let botHeight = height * heightFactor;\n      let topHeight = height - botHeight;\n      let headGeoTop = new this.THREE.CylinderGeometry(radius, radius, topHeight, numOfEdges);\n      headGeoTop.rotateY(angle / 2);\n      headGeoTop.translate(0, topHeight / 2 + botHeight, 0);\n      headGeoTop.isPolygon = true;\n      let headGeoBot = new this.THREE.CylinderGeometry(topRadius, topRadius, botHeight, 32);\n      headGeoBot.translate(0, botHeight / 2, 0);\n      return [headGeoTop, headGeoBot];\n    }\n\n    makeHeadWireframe(geometry, lineProps, drawBottomFrameHead = true) {\n      let wireframes = [];\n\n      if (geometry) {\n        if (geometry.length > 0) {\n          geometry.forEach(geo => {\n            let newWireframes = this.makeHeadWireframe(geo, lineProps, drawBottomFrameHead);\n            wireframes.push(...newWireframes);\n          });\n        } else {\n          if (geometry.type == \"CylinderGeometry\") {\n            let drawTopFrame = true;\n            let drawBotFrame = drawBottomFrameHead;\n            if (geometry.isTypeSixBottom) drawTopFrame = false;\n            if (geometry.isAddon) drawBotFrame = true;\n            if (geometry.isTypeSixTop) drawBotFrame = false;\n            if (geometry.isTypeFiveBottom) drawTopFrame = false;\n            if (geometry.isTypeFiveTop) drawBotFrame = false;\n            let drawEdges = geometry.isPolygon ? true : false;\n            let topBotWireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopFrame, drawBotFrame, drawEdges);\n            wireframes.push(...topBotWireframes);\n          }\n        }\n      }\n\n      return wireframes;\n    }\n\n    getFastenerHeadGeo(headData) {\n      if (headData) {\n        let RADIAL_SEGMENTS = 32;\n        let headType = headData.type;\n        let topDiameter = headData.topDiameter.value;\n        let topDiameterFactor = headData.topDiameter.factor;\n        let bottomDiameter = headData.bottomDiameter.value;\n        let bottomDiameterFactor = headData.bottomDiameter.factor;\n        let topRadius = topDiameter / 2;\n        let bottomRadius = bottomDiameter / 2;\n        let height = headData.height.value;\n        let heightFactor = headData.height.factor;\n        let wrenchSize = headData.wrenchSize;\n        let numOfEdges = headData.numberOfEdges;\n        let headGeo;\n        let headGeoTop, headGeoBot;\n\n        if (headType == 1) {\n          if (topRadius == 0) topRadius = 0.001;\n          if (bottomRadius == 0) bottomRadius = 0.001;\n          headGeo = this.getFastenerHeadGeoCountersunk(topRadius, bottomRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 2) {\n          headGeo = this.getFastenerHeadGeoCylinder(topRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 3) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoLentil(topRadius, height, heightFactor, RADIAL_SEGMENTS);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 4) {\n          headGeo = this.getFastenerHeadGeoDiskFlat(topRadius, height, RADIAL_SEGMENTS);\n        } else if (headType == 5) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoDiskBottomFlat(topRadius, bottomRadius, topDiameterFactor, topDiameter, height, heightFactor, RADIAL_SEGMENTS);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 6) {\n          let topHeight = height * (1 - heightFactor);\n          let botHeight = height * heightFactor;\n          headGeoTop = new this.THREE.CylinderGeometry(topRadius, topRadius, topHeight, 32);\n          headGeoTop.isTypeSixTop = true;\n          headGeoBot = new this.THREE.CylinderGeometry((bottomDiameter + 2 * bottomDiameterFactor * bottomDiameter) / 2, bottomRadius, botHeight, RADIAL_SEGMENTS);\n          headGeoBot.isTypeSixBottom = true;\n          headGeoTop.translate(0, -topHeight / 2, 0);\n          headGeoBot.translate(0, -topHeight - botHeight / 2, 0);\n          headGeo = [headGeoTop, headGeoBot];\n        } else if (headType == 7) {\n          headGeo = this.getFastenerHeadGeoPolyWithoutWasher(wrenchSize, numOfEdges, height);\n        } else if (headType == 8) {\n          [headGeoTop, headGeoBot] = this.getFastenerHeadGeoPolyWithWasher(wrenchSize, numOfEdges, height, heightFactor, topRadius);\n          headGeo = [headGeoTop, headGeoBot];\n        }\n\n        return headGeo;\n      }\n    }\n    /**\n     * Shank Types\n     * 1: Circle\n     * 2: Square\n     */\n\n\n    getFastenerShankGeo(segments, shankData, fastener) {\n      let geometries = [];\n\n      if (shankData) {\n        let diameter = shankData.size;\n        segments.forEach(segment => {\n          let length = segment[1] - segment[0];\n          let newGeo;\n          if (shankData.type == 1) newGeo = new this.THREE.CylinderGeometry(diameter / 2, diameter / 2, length, 32);else if (shankData.type == 2) newGeo = new this.THREE.BoxGeometry(diameter, length, diameter);else console.log(\"Shank type is undefined\");\n\n          if (newGeo) {\n            if (segment[2]) newGeo.isTipSide = true;\n            newGeo.translate(0, -length / 2 - segment[0], 0);\n            geometries.push(newGeo);\n          }\n        });\n      } else {\n        geometries = null;\n      }\n\n      return geometries;\n    }\n\n    makeTipWireframe(tipData, geometry, lineProps, drawTopTip = true, drawBotTip = true, drawEdgesTip = false) {\n      // TODO: Refactor/Clean code.\n      let wireframes = [];\n\n      if (tipData.type == 1) {\n        let geoVertices = [...geometry.vertices];\n        geoVertices.splice(geoVertices.length - 2, 2);\n\n        if (drawTopTip) {\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n      } else if (tipData.type == 2) {\n        if (geometry.parameters.radiusBottom) {\n          wireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopTip);\n        } else {\n          let geoVertices = [...geometry.vertices];\n          geoVertices.splice(geoVertices.length - 2, 2);\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n      } else if (tipData.type == 3) {\n        let depth = geometry.parameters.options.depth;\n        let partOne = [...geometry.vertices].splice(0, 3);\n        let partTwo = [];\n        partOne.forEach(vertex => partTwo.push(vertex.clone()));\n        partTwo.forEach(vertex => vertex.setComponent(2, vertex.z + depth));\n        let wireframesOne = this.drawLineFromPoints(partOne, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesOne]);\n        let wireframesTwo = this.drawLineFromPoints(partTwo, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesTwo]);\n\n        if (drawTopTip) {\n          let edgesLineArr = [];\n\n          for (let i = 0; i < partOne.length; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        } else {\n          let edgesLineArr = [];\n          let newLine = this.drawLineFromPoints([partOne[1], partTwo[1]], lineProps);\n          edgesLineArr.push(newLine);\n          wireframes.push(edgesLineArr);\n        }\n      } else if (tipData.type == 4) {\n        let depth = geometry.parameters.options.depth;\n        let partOne = [];\n\n        if (geometry.vertices[3].z > 0) {\n          partOne = [...geometry.vertices].splice(0, 3);\n        } else {\n          partOne = [...geometry.vertices].splice(0, 4);\n        }\n\n        let partTwo = [];\n        partOne.forEach(vertex => partTwo.push(vertex.clone()));\n        partTwo.forEach(vertex => vertex.setComponent(2, vertex.z + depth));\n        let wireframesOne = this.drawLineFromPoints(partOne, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesOne]);\n        let wireframesTwo = this.drawLineFromPoints(partTwo, lineProps, drawTopTip ? true : false);\n        wireframes.push([wireframesTwo]);\n\n        if (drawTopTip) {\n          let edgesLineArr = [];\n\n          for (let i = 0; i < partOne.length; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        } else {\n          let edgesLineArr = [];\n\n          for (let i = 1; i < partOne.length - 1; i++) {\n            let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n            edgesLineArr.push(newLine);\n          }\n\n          wireframes.push(edgesLineArr);\n        }\n      } else if (tipData.type == 5) {\n        let geoVertices = [...geometry.vertices].splice(0, 4);\n\n        if (drawTopTip) {\n          let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n          wireframes.push([wireframe]);\n        }\n\n        let pointVertex = geometry.vertices[4];\n        let pointArr = [];\n        geoVertices.forEach(vertex => {\n          let newLine = this.drawLineFromPoints([pointVertex, vertex], lineProps);\n          pointArr.push(newLine);\n        });\n        wireframes.push(pointArr);\n      } else if (tipData.type == 6) {\n        if (geometry.parameters.radiusBottom) {\n          wireframes = this.drawDoublesideFrame(geometry, lineProps, drawTopTip, true, true);\n        } else {\n          let geoVertices = [...geometry.vertices];\n          geoVertices.splice(geoVertices.length - 2, 2);\n          let pointVertex = geometry.vertices[4];\n          let pointArr = [];\n          geoVertices.forEach(vertex => {\n            let newLine = this.drawLineFromPoints([pointVertex, vertex], lineProps);\n            pointArr.push(newLine);\n          });\n          wireframes.push(pointArr);\n\n          if (drawTopTip) {\n            let wireframe = this.drawLineFromPoints(geoVertices, lineProps, true);\n            wireframes.push([wireframe]);\n          }\n        }\n      }\n\n      return wireframes;\n    }\n    /**\n     * Tip Types\n     * 1: Circle Pointed\n     * 2: Circle Blunt\n     * 3: Square Half Pointed\n     * 4: Square Half Blunt\n     * 5: Square Full Pointed\n     * 6: Square Full Blunt\n     */\n\n\n    getFastenerTipGeo(tipData, tipDiameter, fastener, axisLength) {\n      let tipGeo;\n      let height = tipData ? tipData.height : 0;\n      let tipRadius = tipDiameter / 2;\n\n      if (height > 0) {\n        if (tipData.type == 1) {\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, 0, height, 32);\n        } else if (tipData.type == 2) {\n          let bottomRadius = tipRadius - height;\n          if (bottomRadius < 0) bottomRadius = 0;\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, bottomRadius, height, 32);\n        } else if (tipData.type == 3) {\n          let shape = new this.THREE.Shape();\n          shape.moveTo(0, -height / 2);\n          shape.lineTo(tipRadius, height / 2);\n          shape.lineTo(-tipRadius, height / 2);\n          shape.lineTo(0, -height / 2);\n          let extrudeSettings = {\n            steps: 32,\n            depth: 2 * tipRadius,\n            bevelEnabled: false\n          };\n          tipGeo = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n          tipGeo.translate(0, 0, -fastener.components.shank.size / 2);\n        } else if (tipData.type == 4) {\n          let bottomRadius = (tipDiameter - 2 * height) / 2;\n          if (bottomRadius < 0) bottomRadius = 0;\n          let shape = new this.THREE.Shape();\n          shape.moveTo(bottomRadius, -height / 2);\n          shape.lineTo(tipRadius, height / 2);\n          shape.lineTo(-tipRadius, height / 2);\n          shape.lineTo(-bottomRadius, -height / 2);\n          shape.lineTo(bottomRadius, -height / 2);\n          let extrudeSettings = {\n            steps: 32,\n            depth: 2 * tipRadius,\n            bevelEnabled: false\n          };\n          tipGeo = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n          tipGeo.translate(0, 0, -fastener.components.shank.size / 2);\n        } else if (tipData.type == 5) {\n          tipRadius = Math.sqrt(2 * Math.pow(tipDiameter, 2)) / 2;\n          tipGeo = new this.THREE.CylinderGeometry(tipRadius, 0, height, 4);\n          tipGeo.rotateY(Math.PI / 4);\n        } else if (tipData.type == 6) {\n          let topRadius = Math.sqrt(2 * Math.pow(tipRadius, 2));\n          let correctedRadius = tipRadius - height > 0 ? tipRadius - height : 0;\n          let bottomRadius = Math.sqrt(2 * Math.pow(correctedRadius, 2));\n          tipGeo = new this.THREE.CylinderGeometry(topRadius, bottomRadius, height, 4);\n          tipGeo.rotateY(Math.PI / 4);\n        } else {\n          tipGeo = null;\n        }\n      } else {\n        tipGeo = null;\n      }\n\n      if (tipGeo) {\n        tipGeo.computeBoundingBox();\n        tipGeo.center();\n        tipGeo.translate(0, height / 2 - axisLength, 0);\n      }\n\n      return tipGeo;\n    }\n    /**\n     * Thread Types\n     * 1: with pitch\n     * 2: without pitch\n     */\n\n\n    getSpiralAndCylGeo(threadSegments, fastenerProps) {\n      let cylGeometries = [];\n      let spiralGeometries = [];\n      let wireframePoints = [];\n      let cylWireframes = [];\n      threadSegments.forEach(segment => {\n        let threadData = segment[2];\n\n        if (threadData.pitch > 0) {\n          let threadDiameter = threadData.threadDiameter;\n          let coreDiameter = threadData.coreDiameter;\n          let pitch = threadData.pitch;\n          let height = threadData.height;\n          let length = segment[1] - segment[0];\n\n          if (threadDiameter && length > 0 && threadDiameter > coreDiameter) {\n            let spiral = new Spiral(coreDiameter / 2, length, pitch, (threadDiameter - coreDiameter) / 2, height, new this.THREE.MeshBasicMaterial(), this);\n            let cylGeo = spiral.getCylGeo();\n            let [spiralGeo, newWireframePoints] = spiral.getSpiralGeo();\n            wireframePoints.push(newWireframePoints);\n            cylGeo.translate(0, -segment[0], 0);\n            spiralGeo.translate(0, -segment[0], 0); ///\n            // let lineProps = fastenerProps.fastener.headSide.line;\n            // let cylWireframe = this.makeHeadWireframe(cylGeo, lineProps);\n            // cylWireframes.push(cylWireframe);\n            ///\n\n            cylGeometries.push(cylGeo);\n            spiralGeometries.push(spiralGeo);\n          }\n        }\n      });\n      let firstCyl, firstSpiral;\n\n      if (cylGeometries.length > 0 && spiralGeometries.length > 0) {\n        firstCyl = cylGeometries.splice(0, 1)[0];\n        cylGeometries.forEach(geo => firstCyl.merge(geo));\n        firstSpiral = spiralGeometries.splice(0, 1)[0];\n        spiralGeometries.forEach(geo => firstSpiral.merge(geo));\n      }\n\n      return [firstCyl, firstSpiral, wireframePoints, cylWireframes];\n    }\n\n    getThreadWithoutPitchGeo(threadSegments, fastenerProps) {\n      let geometries = [];\n      let wireframePoints = [];\n      let coreGeometries = [];\n      let coreWireframes = [];\n      threadSegments.forEach(segment => {\n        let insertionDistance = segment[0];\n        let length = segment[1] - segment[0];\n        let threadsData = segment[2];\n        let coreRadius = threadsData.coreDiameter / 2;\n        let threadRadius = threadsData.threadDiameter / 2;\n        let height = threadsData.height;\n        let distance = threadsData.distance;\n        let midGeoHeight = distance - height;\n        let threadPartHeight = height / 2; // top and bot geometry height\n\n        let pitch = threadsData.pitch;\n        if (threadPartHeight == 0) threadPartHeight = 0.001;\n        if (midGeoHeight == 0) midGeoHeight = 0.001;\n\n        if (pitch == 0 && length > 0 && (threadPartHeight > 0 || midGeoHeight > 0) && threadRadius > 0 && coreRadius >= 0 && threadRadius > coreRadius) {\n          if (coreRadius == 0) coreRadius = 0.01;\n          if (threadRadius == 0) threadRadius = 0.01;\n          let coreGeo = new this.THREE.CylinderGeometry(coreRadius, coreRadius, length, 32);\n          coreGeo.translate(0, -length / 2 - insertionDistance, 0); ///\n          // let lineProps = fastenerProps.fastener.headSide.line;\n          // let coreWireframe = this.makeHeadWireframe(coreGeo, lineProps);\n          // coreWireframes.push(coreWireframe);\n          ///\n\n          coreGeometries.push(coreGeo);\n          let wireframePointsOuter = [];\n          let wireframePointsInner = [];\n          let startAngle = 0;\n          let endAngle = 2 * Math.PI;\n          let step = endAngle / 32;\n\n          while (startAngle < endAngle) {\n            let widthCoeff = Math.cos(startAngle);\n            let depthCoeff = Math.sin(startAngle);\n            let outerPoint = new this.THREE.Vector3(threadRadius * widthCoeff, 0, threadRadius * depthCoeff);\n            let innerPoint = new this.THREE.Vector3(coreRadius * widthCoeff, 0, coreRadius * depthCoeff);\n            startAngle += step;\n            wireframePointsOuter.push(outerPoint);\n            wireframePointsInner.push(innerPoint);\n          }\n          /*\n            turns\n            0: topGeo\n            1: botGeo\n            2: midGeo\n          */\n\n\n          let turn = 0;\n          let distanceToLoop = segment[0] + length;\n\n          while (insertionDistance < distanceToLoop) {\n            if (turn == 0) {\n              if (threadPartHeight > 0) {\n                let topGeo = new this.THREE.CylinderGeometry(coreRadius, threadRadius, threadPartHeight, 32, 1, true);\n                topGeo.translate(0, -threadPartHeight / 2 - insertionDistance, 0); ///\n\n                let wireframeNewPoints = [...wireframePointsInner].map(point => point = point.clone().setComponent(1, -insertionDistance));\n                wireframePoints.push(wireframeNewPoints);\n                let wireframeNewPointsOuter = [...wireframePointsOuter].map(point => point = point.clone().setComponent(1, -insertionDistance - threadPartHeight));\n                wireframePoints.push(wireframeNewPointsOuter); ///\n\n                insertionDistance += threadPartHeight;\n                geometries.push(topGeo);\n              }\n            } else if (turn == 1) {\n              if (threadPartHeight > 0) {\n                let botGeo = new this.THREE.CylinderGeometry(threadRadius, coreRadius, threadPartHeight, 32, 1, true);\n                botGeo.translate(0, -threadPartHeight / 2 - insertionDistance, 0); ///\n\n                let wireframeNewPoints = [...wireframePointsInner].map(point => point = point.clone().setComponent(1, -insertionDistance - threadPartHeight));\n                wireframePoints.push(wireframeNewPoints); ///\n\n                insertionDistance += threadPartHeight;\n                geometries.push(botGeo);\n              }\n            } else {\n              if (midGeoHeight > 0) {\n                // let midGeo = new this.THREE.CylinderGeometry(coreRadius, coreRadius, midGeoHeight, 32, 1, true);\n                // midGeo.translate(0, -midGeoHeight/2 - insertionDistance, 0);\n                // geometries.push(midGeo);\n                insertionDistance += midGeoHeight;\n              }\n            }\n\n            turn++;\n            if (turn > 2) turn = 0;\n          }\n        }\n      });\n      let geometry = geometries.splice(0, 1)[0];\n      geometries.forEach(geo => geometry.merge(geo));\n      let coreGeometry = coreGeometries.splice(0, 1)[0];\n      coreGeometries.forEach(geo => coreGeometry.merge(geo));\n      return [geometry, coreGeometry, wireframePoints, coreWireframes];\n    }\n\n    drawFastenerHead_1_Countersunk(headGeo, fastenerMaterial, headData) {\n      let head;\n\n      if (headData.height.value > 0 && (headData.topDiameter.value > 0 || headData.bottomDiameter.value > 0)) {\n        head = new this.THREE.Mesh(headGeo, fastenerMaterial);\n      } else if (headData.type == 2 && headData.topDiameter.value > 0 && headData.height.value > 0) {\n        head = new this.THREE.Mesh(headGeo, fastenerMaterial);\n      } else {\n        head = null;\n      }\n\n      return head;\n    }\n\n    drawFastenerHead_4_DiscFlat(geometry, material, headData) {\n      let cylinder;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0) {\n        cylinder = new this.THREE.Mesh(geometry, material);\n      } else {\n        cylinder = null;\n      }\n\n      return cylinder;\n    }\n\n    drawFastenerHead_7_PolygonWithoutWasher(geometry, material, headData) {\n      let hexagonMesh;\n\n      if (headData.height.value > 0 && headData.wrenchSize > 0 && headData.numberOfEdges > 2) {\n        hexagonMesh = new this.THREE.Mesh(geometry, material);\n      } else {\n        hexagonMesh = null;\n      }\n\n      return hexagonMesh;\n    }\n\n    drawFastenerHead_8_PolygonWithWasher(geometry, material, headData) {\n      let polyWithWasher;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1 && headData.wrenchSize > 0 && headData.wrenchSize < headData.topDiameter.value && headData.numberOfEdges > 2) {\n        let [hexExtr, cylGeo] = geometry;\n        let hexagonMesh = new this.THREE.Mesh(hexExtr, material);\n        let cylinder = new this.THREE.Mesh(cylGeo, material);\n        let hexagonCSG = this.CSG.fromMesh(hexagonMesh);\n        let cylinderMesh = this.CSG.fromMesh(cylinder);\n        let unionCSG = hexagonCSG.union(cylinderMesh);\n        polyWithWasher = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        polyWithWasher.material = material;\n      } else {\n        polyWithWasher = null;\n      }\n\n      return polyWithWasher;\n    }\n\n    drawFastenerHead_5_DiscBottomFlat(geometry, material, headData) {\n      let discBottomFlat;\n\n      if (headData.topDiameter.value > 0 && headData.topDiameter.factor > 0 && 2 * (headData.topDiameter.factor * headData.topDiameter.value) + headData.topDiameter.value < headData.bottomDiameter.value && headData.bottomDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [geometry1, geometry2] = geometry;\n        let cylinderMesh = new this.THREE.Mesh(geometry1, material);\n        let cylinder = new this.THREE.Mesh(geometry2, material);\n        let csgOne = this.CSG.fromMesh(cylinderMesh);\n        let csgTwo = this.CSG.fromMesh(cylinder);\n        let unionCSG = csgOne.union(csgTwo);\n        discBottomFlat = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        discBottomFlat.material = material;\n      } else {\n        discBottomFlat = null;\n      }\n\n      return discBottomFlat;\n    }\n\n    drawFastenerHead_6_DiscTopFlat(geometry, material, headData) {\n      let diskTopFlat;\n\n      if (headData.topDiameter.value > 0 && headData.bottomDiameter.value > 0 && headData.bottomDiameter.factor > 0 && 2 * (headData.bottomDiameter.factor * headData.bottomDiameter.value) + headData.bottomDiameter.value < headData.topDiameter.value && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [geometry1, geometry2] = geometry;\n        let cylinder = new this.THREE.Mesh(geometry1, material);\n        let cylinderMesh = new this.THREE.Mesh(geometry2, material);\n        let csgOne = this.CSG.fromMesh(cylinder);\n        let csgTwo = this.CSG.fromMesh(cylinderMesh);\n        let unionCSG = csgOne.union(csgTwo);\n        diskTopFlat = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n        diskTopFlat.material = material;\n      } else {\n        diskTopFlat = null;\n      }\n\n      return diskTopFlat;\n    }\n\n    drawFastenerHead_3_Lentil(geometry, material, headData) {\n      let lentilHead;\n\n      if (headData.topDiameter.value > 0 && headData.height.value > 0 && headData.height.factor > 0 && headData.height.factor < 1) {\n        let [ellipsoidGeo, cylinderGeo] = geometry;\n        let eraserGeo = new this.THREE.BoxGeometry(headData.topDiameter.value, headData.height.value, headData.topDiameter.value);\n        eraserGeo.translate(0, -headData.height.value / 2, 0);\n        let eraserMesh = new this.THREE.Mesh(eraserGeo);\n        var ellipsoidmesh = new this.THREE.Mesh(ellipsoidGeo);\n        var cylinder = new this.THREE.Mesh(cylinderGeo);\n        let eraserCSG = this.CSG.fromMesh(eraserMesh);\n        let ellipseCSG = this.CSG.fromMesh(ellipsoidmesh).subtract(eraserCSG);\n        let cylinderCSG = this.CSG.fromMesh(cylinder);\n        let lentilHeadCSG = ellipseCSG.union(cylinderCSG);\n        lentilHead = this.CSG.toMesh(lentilHeadCSG, new this.THREE.Matrix4());\n        lentilHead.material = material;\n      } else {\n        lentilHead = null;\n      }\n\n      return lentilHead;\n    }\n\n    drawFastenerShank(shank, properties, shankGeo, drawTopFrameShank = true, drawBotFrameShank = true) {\n      let shanks = {\n        headSide: [],\n        tipSide: []\n      };\n      let shankWireframes = [];\n      let tipSideProps = properties.fastener.tipSide;\n      let linePropsHeadSide = properties.fastener.headSide.line;\n      let lineProps = tipSideProps && shank.tipSideLength ? tipSideProps.line : linePropsHeadSide;\n      let drawEdges = shank && shank.type === 2 ? true : false;\n\n      if (shankGeo && shank.size > 0) {\n        shankGeo.forEach(geo => {\n          if (geo.isTipSide) drawTopFrameShank = false;\n          let newWireframe = this.drawDoublesideFrame(geo, lineProps, drawTopFrameShank, drawBotFrameShank, drawEdges);\n          shankWireframes.push(...newWireframe);\n          let shankMesh = new this.THREE.Mesh(geo);\n          if (geo.isTipSide) shanks.tipSide.push(shankMesh);else shanks.headSide.push(shankMesh);\n        });\n      }\n\n      return [shanks, shankWireframes];\n    }\n\n    drawFastenerThread_0_WithPitch(cylGeo, spiralGeo, fastenerMaterial, wireframePoints, lineProps) {\n      let spiralMesh, cylMesh;\n\n      if (cylGeo && spiralGeo) {\n        spiralMesh = new this.THREE.Mesh(spiralGeo, fastenerMaterial.clone());\n        spiralMesh.material.side = this.THREE.DoubleSide;\n        cylMesh = new this.THREE.Mesh(cylGeo, fastenerMaterial);\n        wireframePoints.forEach(pointArr => {\n          pointArr.forEach(points => {\n            let wireframe = this.drawLineFromPoints(points, lineProps);\n            spiralMesh.add(wireframe);\n          });\n          let endPointsOne = [pointArr[0][0], pointArr[1][0], pointArr[2][0]];\n          let endWireframeOne = this.drawLineFromPoints(endPointsOne, lineProps);\n          spiralMesh.add(endWireframeOne);\n          let endPointsTwo = [pointArr[0][pointArr[0].length - 1], pointArr[1][pointArr[0].length - 1], pointArr[2][pointArr[0].length - 1]];\n          let endWireframeTwo = this.drawLineFromPoints(endPointsTwo, lineProps);\n          spiralMesh.add(endWireframeTwo);\n        });\n      } else {\n        spiralMesh = null;\n        cylMesh = null;\n      }\n\n      return [cylMesh, spiralMesh];\n    }\n\n    drawFastenerThread_1_WithoutPitch(threadGeo, coreGeo, material, wireframePoints, lineProps) {\n      let thread = threadGeo ? new this.THREE.Mesh(threadGeo, material.clone()) : null;\n      let core = coreGeo ? new this.THREE.Mesh(coreGeo) : null;\n\n      if (thread) {\n        thread.material.side = this.THREE.DoubleSide;\n        wireframePoints.forEach(points => {\n          let wireframe = this.drawLineFromPoints(points, lineProps, true);\n          thread.add(wireframe);\n        });\n      }\n\n      return [thread, core];\n    }\n\n    drawFastenerTip(tipGeo, fastenerMaterial, tipSideMaterial) {\n      let cylinder;\n\n      if (tipGeo && fastenerMaterial) {\n        cylinder = new this.THREE.Mesh(tipGeo);\n        if (tipSideMaterial) cylinder.isTipSide = true;\n      } else {\n        cylinder = null;\n      }\n\n      return cylinder;\n    }\n\n    drawLineFromPoints(points, props, isClosed = false) {\n      if (props.color.alpha == 0) {\n        let line = new this.THREE.Mesh(); // TODO: might need better solution\n\n        return line;\n      }\n\n      let colorValues = {\n        red: props.color.red,\n        green: props.color.green,\n        blue: props.color.blue\n      };\n      let addToColor = 20;\n      colorValues.red = colorValues.red + addToColor <= 255 ? colorValues.red + addToColor : Math.min(colorValues.red + addToColor, 255);\n      colorValues.green = colorValues.green + addToColor <= 255 ? colorValues.green + addToColor : Math.min(colorValues.green + addToColor, 255);\n      colorValues.blue = colorValues.blue + addToColor <= 255 ? colorValues.blue + addToColor : Math.min(colorValues.blue + addToColor, 255);\n      let lineColor = \"rgb(\" + colorValues.red + \", \" + colorValues.green + \", \" + colorValues.blue + \")\";\n      let material = new this.THREE.LineBasicMaterial({\n        color: lineColor\n      });\n      let geometry = new this.THREE.BufferGeometry().setFromPoints(points);\n      let line;\n      if (isClosed) line = new this.THREE.LineLoop(geometry, material);else line = new this.THREE.Line(geometry, material);\n      return line;\n    }\n\n    rotateObject(object, direction, directionTwo) {\n      let normalVector = new this.THREE.Vector3().crossVectors(direction, directionTwo);\n      let directionVector = new this.THREE.Vector3(directionTwo.x, directionTwo.y, directionTwo.z);\n      let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n      let dotProduct = object.up.dot(newUpVector);\n      let rotationAngle = -Math.acos(dotProduct); // by default THREE.js rotates counterclockwise, so I switched sign (correct?)\n\n      object.rotateOnAxis(normalVector, rotationAngle);\n      object.up = newUpVector;\n      object.lookAt(object.position.clone().add(normalVector));\n      return object;\n    }\n\n    addToScene(fastener, obj) {\n      let drawnObject = new Object();\n      drawnObject.id = fastener.id;\n      drawnObject.type = \"fastener\";\n      drawnObject.data = this.GraphicsThree3D.jsonData;\n      drawnObject.sceneObject = obj;\n      this.GraphicsThree3D.ThreeObjects.push(drawnObject);\n      this.GraphicsThree3D.scene.add(drawnObject.sceneObject);\n    }\n\n    drawClamp(fastener) {\n      let tipDiameter = fastener.components.threads ? fastener.components.threads[0].coreDiameter : fastener.components.shank.size;\n      let tipGeo = this.getFastenerTipGeo(fastener.components.tip, tipDiameter, fastener, fastener.components.axis.length);\n\n      for (let b = 0; b < fastener.insertionPoints.length; b++) {\n        let insertionPoint = fastener.insertionPoints[b].insertionPoint;\n        let rotationVector = this.utils.normalizeVector(fastener.insertionPoints[b].directionVector1);\n        let rotationVectorTwo = this.utils.normalizeVector(fastener.insertionPoints[b].directionVector2);\n        let headSideMeshes = [];\n        let tipSideMeshes = [];\n        let [head, headWireframes] = this.drawClampHeadStapleSpine(fastener.components.shank, fastener.properties);\n        if (head) headSideMeshes.push(head);\n        let [leftAxisBot, leftAxisTop, axisWireframes] = this.drawClampAxis(fastener.components.axis, fastener.components.shank, fastener.components.tip, fastener.properties, true);\n        let [rightAxisBot, rightAxisTop, axisWireframesRight] = this.drawClampAxis(fastener.components.axis, fastener.components.shank, fastener.components.tip, fastener.properties, false);\n\n        if (fastener.properties.fastener.tipSide) {\n          if (leftAxisTop) headSideMeshes.push(leftAxisTop);\n          if (rightAxisTop) headSideMeshes.push(rightAxisTop);\n          if (leftAxisBot) tipSideMeshes.push(leftAxisBot);\n          if (rightAxisBot) tipSideMeshes.push(rightAxisBot);\n        } else {\n          if (leftAxisBot) headSideMeshes.push(leftAxisBot);\n          if (rightAxisBot) headSideMeshes.push(rightAxisBot);\n        }\n\n        let [tipLeft, tipWireframes] = this.drawClampTip(fastener.components.shank, fastener.properties, fastener.components.tip, true, tipGeo);\n        let [tipRight, tipWireframesRight] = this.drawClampTip(fastener.components.shank, fastener.properties, fastener.components.tip, false, tipGeo);\n\n        if (fastener.properties.fastener.tipSide) {\n          if (tipLeft) tipSideMeshes.push(tipLeft);\n          if (tipRight) tipSideMeshes.push(tipRight);\n        } else {\n          if (tipLeft) headSideMeshes.push(tipLeft);\n          if (tipRight) headSideMeshes.push(tipRight);\n        }\n\n        let headSideMesh, tipSideMesh;\n\n        if (fastener.properties.fastener.headSide) {\n          let headSideMaterial = this.getMaterial(fastener.properties, \"headSide\");\n          headSideMaterial.side = this.THREE.FrontSide;\n\n          if (headSideMeshes.length > 0) {\n            headSideMesh = headSideMeshes.splice(0, 1)[0];\n            headSideMesh.updateMatrix();\n            headSideMeshes.forEach(mesh => {\n              mesh.updateMatrix();\n              let firstCSG = this.CSG.fromMesh(headSideMesh);\n              let secondCSG = this.CSG.fromMesh(mesh);\n              let unionCSG = firstCSG.union(secondCSG);\n              let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n              headSideMesh = unionMesh;\n            });\n          }\n\n          headSideMesh.material = headSideMaterial;\n        }\n\n        if (fastener.properties.fastener.tipSide) {\n          let tipSideMaterial = this.getMaterial(fastener.properties, \"tipSide\");\n          tipSideMaterial.side = this.THREE.FrontSide;\n\n          if (tipSideMeshes.length > 0) {\n            tipSideMesh = tipSideMeshes.splice(0, 1)[0];\n            tipSideMesh.updateMatrix();\n            tipSideMeshes.forEach(mesh => {\n              mesh.updateMatrix();\n              let firstCSG = this.CSG.fromMesh(tipSideMesh);\n              let secondCSG = this.CSG.fromMesh(mesh);\n              let unionCSG = firstCSG.union(secondCSG);\n              let unionMesh = this.CSG.toMesh(unionCSG, new this.THREE.Matrix4());\n              tipSideMesh = unionMesh;\n            });\n          }\n\n          tipSideMesh.material = tipSideMaterial;\n        }\n\n        let fastenerClamp = new this.THREE.Object3D();\n\n        if (headSideMesh) {\n          headSideMesh.renderOrder = -4;\n          fastenerClamp.add(headSideMesh);\n        }\n\n        if (tipSideMesh) {\n          tipSideMesh.renderOrder = -4;\n          fastenerClamp.add(tipSideMesh);\n        }\n\n        let backSideHead, backSideTip;\n\n        if (headSideMesh) {\n          let geo = headSideMesh.geometry.clone();\n          let mat = headSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideHead = new this.THREE.Mesh(geo, mat);\n          backSideHead.renderOrder = -5;\n          fastenerClamp.add(backSideHead);\n        }\n\n        if (tipSideMesh) {\n          let geo = tipSideMesh.geometry.clone();\n          let mat = tipSideMesh.material.clone();\n          mat.side = this.THREE.BackSide;\n          backSideTip = new this.THREE.Mesh(geo, mat);\n          backSideTip.renderOrder = -5;\n          fastenerClamp.add(backSideTip);\n        }\n\n        headWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        axisWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        axisWireframesRight.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        tipWireframes.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        }));\n        tipWireframesRight.forEach(wireframe => wireframe.forEach(line => {\n          fastenerClamp.add(line);\n        })); ///\n        // Axis\n\n        let axisGeo, axisPointGeo;\n        let axisMat, axisPointMat;\n        let inversedDirection = false;\n\n        if (fastener.components.axis.length < 0) {\n          inversedDirection = true;\n          fastener.components.axis.length *= -1; // maybe change code not to modify json?\n        }\n\n        if (fastener.properties.axis && fastener.components.axis.length > 0) {\n          let lineProps = fastener.properties.axis.line;\n\n          if (lineProps.thickness && lineProps.type > 0) {\n            axisGeo = this.getAxisGeo(fastener.components.axis, fastener.properties.axis);\n            axisMat = this.GraphicsThree3D.getMaterialFromColor(lineProps.color);\n          }\n\n          let pointProps = fastener.properties.axis.point;\n          axisPointGeo = this.getAxisPointGeo(pointProps);\n          axisPointMat = this.GraphicsThree3D.getMaterialFromColor(pointProps.color);\n        }\n\n        let axis;\n        if (fastener.properties.axis) axis = this.drawAxis(axisGeo, axisPointGeo, axisMat, axisPointMat);\n        if (axis) fastenerClamp.add(axis); ///\n\n        let directionVector = new this.THREE.Vector3(rotationVector.x, rotationVector.y, rotationVector.z);\n        let directionVectorTwo = new this.THREE.Vector3(rotationVectorTwo.x, rotationVectorTwo.y, rotationVectorTwo.z);\n\n        if (inversedDirection) {\n          directionVector.negate();\n          directionVectorTwo.negate();\n        }\n\n        this.rotateObject(fastenerClamp, directionVector, directionVectorTwo);\n        fastenerClamp.position.set(insertionPoint.x, insertionPoint.y, insertionPoint.z); // this.addToDimChainCollisionDetection(fastenerClamp);\n\n        this.GraphicsThree3D.addObjToDynamicSectionalPlanes(fastener, fastenerClamp, \"fasteners\");\n        this.GraphicsThree3D.scene.add(fastenerClamp);\n        this.GraphicsThree3D.addObjectToVisControl(fastenerClamp, \"fastener\");\n\n        _classPrivateFieldGet(this, _fasteners).push(fastenerClamp);\n      }\n    }\n\n    drawClampHeadStapleSpine(shank, propertiesData) {\n      const depth = shank.size;\n      const width = shank.offsetLength;\n      const height = shank.size;\n      let headWireframes = [];\n      let lineProps = propertiesData.fastener.headSide.line;\n      let material = this.getMaterial(propertiesData);\n\n      if (shank.type == 1) {\n        var geometry = new this.THREE.CylinderGeometry(depth / 2, depth / 2, width, 32);\n        geometry.translate(-depth / 2, 0, 0);\n      } else if (shank.type == 2) {\n        var geometry = new this.THREE.BoxGeometry(depth, height, width);\n        geometry.translate(0, -height / 2, 0);\n      } //\n\n\n      let cube; // TODO: remove duplicate code / clean code\n\n      let cubeGeoCSG;\n\n      if (shank.type == 1) {\n        let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n        let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n        eraserBox.rotation.z = Math.PI / 4;\n        eraserBox.position.set(-depth, width / 2, 0);\n        eraserBox.updateMatrix();\n        let geomesh = new this.THREE.Mesh(geometry);\n        let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n        cubeGeoCSG = geoCSG.subtract(eraserCSG);\n        eraserBox.position.set(-depth, -width / 2, 0);\n        eraserBox.updateMatrix();\n        eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        cubeGeoCSG = cubeGeoCSG.subtract(eraserCSG);\n      } else if (shank.type == 2) {\n        let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n        let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n        eraserBox.rotation.x = Math.PI / 4;\n        eraserBox.position.set(0, -depth, width / 2);\n        eraserBox.updateMatrix();\n        let geomesh = new this.THREE.Mesh(geometry);\n        let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n        cubeGeoCSG = geoCSG.subtract(eraserCSG);\n        eraserBox.position.set(0, -depth, -width / 2);\n        eraserBox.updateMatrix();\n        eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n        cubeGeoCSG = cubeGeoCSG.subtract(eraserCSG);\n      }\n\n      cube = this.GraphicsThree3D.CSG.toMesh(cubeGeoCSG, new this.THREE.Matrix4());\n      cube.material = material;\n\n      if (geometry.type == \"CylinderGeometry\") {\n        cube.rotation.z = Math.PI / 2;\n        let radiusX = Math.sqrt(2 * Math.pow(depth / 2, 2));\n        let partOne = this.getEllipsePointsXZ(radiusX, depth / 2, Math.PI / 4, {\n          x: -width / 2 + depth / 2,\n          y: -depth / 2,\n          z: 0\n        });\n        let partTwo = this.getEllipsePointsXZ(radiusX, depth / 2, -Math.PI / 4, {\n          x: width / 2 - depth / 2,\n          y: -depth / 2,\n          z: 0\n        });\n        let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n        headWireframes.push([wireframeOne]);\n        let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n        headWireframes.push([wireframeTwo]);\n      } else if (geometry.type == \"BoxGeometry\") {\n        cube.rotation.y = Math.PI / 2;\n        let cubeWireframe = [];\n        let pointsOne = [new this.THREE.Vector3(-width / 2, 0, -depth / 2), new this.THREE.Vector3(-width / 2, 0, depth / 2), new this.THREE.Vector3(-width / 2 + depth, -depth, depth / 2), new this.THREE.Vector3(-width / 2 + depth, -depth, -depth / 2)];\n        let pointsTwo = [new this.THREE.Vector3(width / 2, 0, -depth / 2), new this.THREE.Vector3(width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2 - depth, -depth, depth / 2), new this.THREE.Vector3(width / 2 - depth, -depth, -depth / 2)];\n\n        for (let i = 0; i < pointsOne.length; i++) {\n          let newLine = this.drawLineFromPoints([pointsOne[i], pointsTwo[i]], lineProps);\n          cubeWireframe.push(newLine);\n        }\n\n        headWireframes.push(cubeWireframe);\n        let edgePointsOne = [...pointsOne.slice(1, 3), ...pointsTwo.slice(1, 3)];\n        let edgePointsTwo = [pointsOne[0], pointsOne[3], pointsTwo[0], pointsTwo[3]];\n        let edgesLineArr = [];\n\n        for (let i = 0; i < edgePointsOne.length; i++) {\n          let newLine = this.drawLineFromPoints([edgePointsOne[i], edgePointsTwo[i]], lineProps);\n          edgesLineArr.push(newLine);\n        }\n\n        headWireframes.push(edgesLineArr);\n      }\n\n      return [cube, headWireframes];\n    }\n\n    drawClampAxis(axis, shank, tipData, propertiesData, isLeft) {\n      const depth = shank.size;\n      const width = shank.size;\n      let height = axis.length;\n      let axisWireframes = [];\n      let offsetFromCenter = isLeft ? -(shank.offsetLength / 2) : shank.offsetLength / 2;\n      let positiveOrNegative = Math.sign(offsetFromCenter);\n\n      if (tipData && tipData.type > 0) {\n        height -= tipData.height;\n      }\n\n      let lineProps = propertiesData.fastener.headSide.line;\n      let material = this.getMaterial(propertiesData);\n      let returnMeshes = [];\n      let geometry, geometryTwo;\n      let tipSideLength = shank.tipSideLength;\n\n      if (tipSideLength) {\n        height -= tipSideLength + 0.0001;\n\n        if (shank.type == 1) {\n          geometryTwo = new this.THREE.CylinderGeometry(width / 2, width / 2, tipSideLength, 32);\n        } else {\n          geometryTwo = new this.THREE.BoxGeometry(depth, tipSideLength, width);\n        }\n\n        geometryTwo.translate(0, -axis.length + tipSideLength / 2 + tipData.height, 0);\n        let cubeTwo;\n\n        if (propertiesData.fastener.tipSide) {\n          let lineProps = propertiesData.fastener.tipSide.line;\n          let materialTwo = this.GraphicsThree3D.getMaterialFromColor(propertiesData.fastener.tipSide.volume.color);\n          cubeTwo = new this.THREE.Mesh(geometryTwo, materialTwo);\n          cubeTwo.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n          returnMeshes.push(cubeTwo);\n\n          if (geometryTwo.type == \"BoxGeometry\") {\n            let partOne = [];\n            let partTwo = [];\n            let geoVertices = [...geometryTwo.vertices].map(vertex => vertex.clone().add(new this.THREE.Vector3(cubeTwo.position.x, 0, 0)));\n            let firstPart = geoVertices[0].y;\n            geoVertices.forEach(vertex => {\n              if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n            });\n\n            if (!propertiesData.fastener.headSide) {\n              let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n              axisWireframes.push([wireframeOne]);\n            }\n\n            if (!tipData) {\n              let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n              axisWireframes.push([wireframeTwo]);\n            } else if (tipData.type == 3 || tipData.type == 4) {\n              for (let i = 0; i <= 2; i += 2) {\n                let wireframeTwo = this.drawLineFromPoints(partTwo.slice(i, i + 2), lineProps, true);\n                axisWireframes.push([wireframeTwo]);\n              }\n            }\n\n            let edgesLineArr = [];\n\n            for (let i = 0; i < partOne.length; i++) {\n              let newLine = this.drawLineFromPoints([partOne[i], partTwo[i]], lineProps);\n              edgesLineArr.push(newLine);\n            }\n\n            axisWireframes.push(edgesLineArr);\n          } else {\n            let partOne = [];\n            let partTwo = [];\n            let geoVertices = geometryTwo.vertices;\n            let firstPart = geoVertices[0].y;\n            geoVertices.forEach(vertex => {\n              if (vertex.y == firstPart) partOne.push(vertex);else partTwo.push(vertex);\n            });\n            partOne.splice(partOne.length - 1, 1);\n            partTwo.splice(partTwo.length - 1, 1);\n\n            if (!propertiesData.fastener.headSide) {\n              let wireframeOne = this.drawLineFromPoints(partOne, lineProps, true);\n              wireframeOne.position.x += offsetFromCenter - positiveOrNegative * width / 2;\n              axisWireframes.push([wireframeOne]);\n            }\n\n            if (!tipData) {\n              let wireframeTwo = this.drawLineFromPoints(partTwo, lineProps, true);\n              wireframeTwo.position.x += offsetFromCenter - positiveOrNegative * width / 2;\n              axisWireframes.push([wireframeTwo]);\n            }\n          }\n        }\n      }\n\n      if (shank.type == 1) {\n        geometry = new this.THREE.CylinderGeometry(width / 2, width / 2, height, 32);\n      } else if (shank.type == 2) {\n        geometry = new this.THREE.BoxGeometry(depth, height, width);\n        let cubeWireframe = [];\n        let pointsOne = [new this.THREE.Vector3(-width / 2, 0, -depth / 2), new this.THREE.Vector3(-width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2, 0, depth / 2), new this.THREE.Vector3(width / 2, 0, -depth / 2)];\n        let pointsTwo = [new this.THREE.Vector3(-width / 2, -height, -depth / 2), new this.THREE.Vector3(-width / 2, -height, depth / 2), new this.THREE.Vector3(width / 2, -height, depth / 2), new this.THREE.Vector3(width / 2, -height, -depth / 2)];\n\n        if (isLeft) {\n          pointsOne.map((point, index) => {\n            if (index > 1) point.sub(new this.THREE.Vector3(0, width, 0));\n          });\n        } else {\n          pointsOne.map((point, index) => {\n            if (index < 2) point.sub(new this.THREE.Vector3(0, width, 0));\n          });\n        }\n\n        for (let i = 0; i < pointsOne.length; i++) {\n          pointsOne[i].add(new this.THREE.Vector3(offsetFromCenter - positiveOrNegative * width / 2, 0, 0));\n          pointsTwo[i].add(new this.THREE.Vector3(offsetFromCenter - positiveOrNegative * width / 2, 0, 0));\n          let newLine = this.drawLineFromPoints([pointsOne[i], pointsTwo[i]], lineProps);\n          cubeWireframe.push(newLine);\n        }\n\n        axisWireframes.push(cubeWireframe);\n\n        if (tipData && (tipData.type == 3 || tipData.type == 4) && !propertiesData.fastener.tipSide) {\n          for (let i = 0; i <= 2; i += 2) {\n            let sideWireframe = this.drawLineFromPoints(pointsTwo.slice(i, i + 2), lineProps, true);\n            axisWireframes.push([sideWireframe]);\n          }\n        }\n      }\n\n      geometry.translate(0, -height / 2, 0); ///\n\n      let cube;\n      let eraserBoxSize = Math.sqrt(2 * Math.pow(depth, 2));\n      let eraserBox = new this.THREE.Mesh(new this.THREE.BoxGeometry(eraserBoxSize, eraserBoxSize, eraserBoxSize));\n      eraserBox.rotation.z = Math.PI / 4;\n      eraserBox.position.set(isLeft ? depth / 2 : -depth / 2, 0, 0);\n      eraserBox.updateMatrix();\n      let geomesh = new this.THREE.Mesh(geometry);\n      let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserBox);\n      let geoCSG = this.GraphicsThree3D.CSG.fromMesh(geomesh);\n      let cubeGeoCSG = geoCSG.subtract(eraserCSG);\n      cube = this.GraphicsThree3D.CSG.toMesh(cubeGeoCSG, new this.THREE.Matrix4());\n      cube.material = material; ///\n\n      cube.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n      returnMeshes.push(cube);\n      if (returnMeshes.length == 1) returnMeshes.push(null);\n      return [...returnMeshes, axisWireframes];\n    }\n\n    drawClampTip(shank, propertiesData, tip, isLeft, tipGeo) {\n      let tipSideProps = propertiesData.fastener.tipSide;\n      let headSideLineProps = propertiesData.fastener.headSide.line;\n      const colorAxis = tipSideProps && shank.tipSideLength ? tipSideProps.volume.color : propertiesData.fastener.headSide.volume.color;\n      let addTopWireframe = true;\n\n      if (shank.type == 1 && (tip.type == 1 || tip.type == 2)) {\n        addTopWireframe = false;\n      } else if (shank.type == 2 && (tip.type == 3 || tip.type == 4 || tip.type == 5 || tip.type == 6)) {\n        addTopWireframe = false;\n      }\n\n      let tipWireframes = this.makeTipWireframe(tip, tipGeo, tipSideProps && shank.tipSideLength ? tipSideProps.line : headSideLineProps, addTopWireframe);\n      let material = this.GraphicsThree3D.getMaterialFromColor(colorAxis);\n      let cylinder = new this.THREE.Mesh(tipGeo, material);\n      let offsetFromCenter = isLeft ? -(shank.offsetLength / 2) : shank.offsetLength / 2;\n      let positiveOrNegative = Math.sign(offsetFromCenter);\n      cylinder.position.x = offsetFromCenter - shank.size / 2 * positiveOrNegative;\n      tipWireframes.forEach(wireframe => wireframe.forEach(line => line.position.add(new this.THREE.Vector3(cylinder.position.x, 0, 0))));\n      return [cylinder, tipWireframes];\n    }\n\n    getLineMaterial(lineProps) {\n      let lineColor = lineProps.color;\n      let materialOptions = {\n        color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n        transparent: true,\n        opacity: lineColor.alpha / 255\n      };\n      let lineMaterial = new this.THREE.LineBasicMaterial(materialOptions);\n      return lineMaterial;\n    }\n\n    getEllipsePointsXZ(radiusX, radiusZ, rotationZ, position) {\n      let geo = new this.THREE.CircleGeometry(radiusZ, 32);\n      geo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusX / radiusZ, 1));\n      geo.rotateY(Math.PI / 2);\n      geo.rotateZ(rotationZ);\n      geo.translate(position.x, position.y, position.z);\n      let vertices = geo.vertices;\n      vertices.splice(0, 1);\n      return vertices;\n    }\n\n  }\n\n  _exports.FastenerLoader = FastenerLoader;\n\n  var _fasteners = new WeakMap();\n\n  var _visibleStatus = new WeakMap();\n\n  class Spiral {\n    constructor(spiralRadius, spiralHeight, initDist, spiralDebth, spiralRowWidth, material, parent, color = 0xffaa00) {\n      this.color = color;\n      this.initDist = initDist;\n      this.spiralHeight = spiralHeight;\n      this.spiralDebth = spiralDebth;\n      this.spiralRadius = spiralRadius;\n      this.spiralRowWidth = spiralRowWidth;\n      this.currentHeight = -spiralHeight / 2;\n      this.x = 0;\n      this.y = 0;\n      this.angle = 0;\n      this.xPlus = 0;\n      this.zPlus = 0;\n      this.material = material;\n      this.parent = parent; //this.geometry = geometry;\n    }\n\n    currentPosition(dist) {\n      return this.currentHeight += dist;\n    }\n\n    currentAngle() {\n      this.angle -= 360 / 32;\n      this.x = Math.cos(Math.PI / 180 * this.angle);\n      this.z = Math.sin(Math.PI / 180 * this.angle);\n    }\n\n    getCylGeo() {\n      if (this.spiralHeight > 0 && this.spiralRadius == 0) this.spiralRadius += 0.0001;\n      const cylinder = new this.parent.THREE.CylinderGeometry(this.spiralRadius, this.spiralRadius, this.spiralHeight, 32);\n      cylinder.translate(0, -this.spiralHeight / 2, 0);\n      return cylinder;\n    }\n\n    getSpiralGeo() {\n      let wireframePoints = [[], [], []];\n      const dist = this.initDist / 32;\n      const c = this.currentPosition(dist);\n      this.currentAngle();\n      var geometry = new this.parent.THREE.Geometry();\n      let pointOne = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c, this.spiralRadius * this.z);\n      let pointTwo = new this.parent.THREE.Vector3((this.spiralDebth + this.spiralRadius) * this.x, c - this.spiralRowWidth / 2, (this.spiralDebth + this.spiralRadius) * this.z);\n      let pointThree = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c - this.spiralRowWidth, this.spiralRadius * this.z);\n      geometry.vertices.push(pointOne);\n      geometry.vertices.push(pointTwo);\n      geometry.vertices.push(pointThree);\n      wireframePoints[0].push(pointOne);\n      wireframePoints[1].push(pointTwo);\n      wireframePoints[2].push(pointThree);\n      var face0 = new this.parent.THREE.Face3(0, 1, 2);\n      geometry.faces.push(face0);\n      var l = 0;\n\n      for (let i = 0; i < 32 * (this.spiralHeight - this.spiralRowWidth) / this.initDist; i++) {\n        const c1 = this.currentPosition(dist);\n        this.currentAngle();\n        pointOne = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c1, this.spiralRadius * this.z);\n        pointTwo = new this.parent.THREE.Vector3((this.spiralDebth + this.spiralRadius) * this.x, c1 - this.spiralRowWidth / 2, (this.spiralDebth + this.spiralRadius) * this.z);\n        pointThree = new this.parent.THREE.Vector3(this.spiralRadius * this.x, c1 - this.spiralRowWidth, this.spiralRadius * this.z);\n        geometry.vertices.push(pointOne);\n        geometry.vertices.push(pointTwo);\n        geometry.vertices.push(pointThree);\n        wireframePoints[0].push(pointOne);\n        wireframePoints[1].push(pointTwo);\n        wireframePoints[2].push(pointThree);\n        var face1 = new this.parent.THREE.Face3(l + 1, l + 3, l + 4);\n        var face2 = new this.parent.THREE.Face3(l + 1, l, l + 3);\n        var face3 = new this.parent.THREE.Face3(l, l + 2, l + 5);\n        var face4 = new this.parent.THREE.Face3(l, l + 3, l + 5);\n        var face5 = new this.parent.THREE.Face3(l + 1, l + 3, l + 2);\n        var face6 = new this.parent.THREE.Face3(l + 1, l + 4, l + 3);\n        var face7 = new this.parent.THREE.Face3(l + 1, l + 5, l + 2);\n        var face8 = new this.parent.THREE.Face3(l + 1, l + 4, l + 5);\n        var face9 = new this.parent.THREE.Face3(l, l + 1, l + 2); //add the face to the geometry's faces array\n\n        geometry.faces.push(face1);\n        geometry.faces.push(face2);\n        geometry.faces.push(face3);\n        geometry.faces.push(face4);\n        geometry.faces.push(face5);\n        geometry.faces.push(face6);\n        geometry.faces.push(face7);\n        geometry.faces.push(face8);\n        geometry.faces.push(face9); ///\n        // let testFaceOne = new this.parent.THREE.Face3(l, l + 1, l + 3);\n        // let testFaceTwo = new this.parent.THREE.Face3(l + 1, l + 4, l + 3);\n        // let testFaceThree = new this.parent.THREE.Face3(l + 1, l + 2, l + 4);\n        // let testFaceFour = new this.parent.THREE.Face3(l + 2, l + 5, l + 4);\n        // geometry.faces.push(testFaceOne);\n        // geometry.faces.push(testFaceTwo);\n        // geometry.faces.push(testFaceThree);\n        // geometry.faces.push(testFaceFour);\n\n        l += 3;\n      }\n\n      geometry.computeFaceNormals();\n      geometry.computeVertexNormals();\n      geometry.translate(0, -this.spiralHeight / 2 + this.spiralRowWidth - this.initDist / 32, 0);\n      wireframePoints.forEach(points => {\n        points.map(point => point = point.clone().setComponent(1, -this.spiralHeight / 2 + this.spiralRowWidth - this.initDist / 32));\n      });\n      return [geometry, wireframePoints];\n    }\n\n  }\n});","define(\"m08-2020/lib/FigureLoaders/TextLoader\", [\"exports\", \"three\", \"m08-2020/lib/Utils\"], function (_exports, THREE, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.TextLoader = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class TextLoader {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n    }\n\n    loadTexts(textProps, texts, jsonData, insertionPoints) {\n      let font = this.loadFont(textProps);\n\n      if (font) {\n        let textArr = [];\n        texts.forEach(text => {\n          let newText = this.drawText(text, textProps, font);\n          newText.jsonID = text.id;\n          textArr.push(newText);\n        });\n        let positionedTexts = this.applyPositionAndRotation(textArr, insertionPoints);\n        positionedTexts.forEach(text => {\n          let objectInfo = {\n            id: text.jsonID,\n            data: jsonData,\n            sceneObject: text\n          }; ///\n\n          this.GraphicsThree3D.ThreeObjects.push(objectInfo);\n          this.GraphicsThree3D.scene.add(text); ///\n        });\n      }\n    }\n\n    drawText(text, textProps, font) {\n      let normalVector = utils.initVector3(text.normalVector);\n      let directionVector = utils.initVector3(text.directionVector);\n      let textMesh;\n\n      if (!normalVector.dot(directionVector) && normalVector.lengthSq() && directionVector.lengthSq()) {\n        let textInsertionPoint = text.insertionPoint;\n        let textGeo = this.getTextGeo(text, textProps, font);\n        let textMaterial = this.GraphicsThree3D.getMaterialFromColor(textProps.color);\n        textMesh = new THREE.Mesh(textGeo, textMaterial);\n        this.GraphicsThree3D.alignObjectWithVectorsAndChangePos(textMesh, textInsertionPoint, normalVector, directionVector);\n      } else {\n        textMesh = new THREE.Mesh();\n      }\n\n      return textMesh;\n    }\n\n    loadFont(textProps) {\n      let loadedFonts = this.GraphicsThree3D.loadedFonts;\n      let [fontUrl, fontName] = this.getFontUrl(textProps);\n      let storedFont = loadedFonts[fontName];\n      let loader = new THREE.FontLoader();\n      let font;\n\n      if (fontUrl) {\n        if (storedFont) {\n          font = storedFont;\n        } else {\n          $.ajax({\n            'async': false,\n            type: \"GET\",\n            url: fontUrl\n          }).then(function (dataFont) {\n            font = loader.parse(dataFont);\n            loadedFonts[fontName] = font;\n          });\n        }\n      }\n\n      return font;\n    }\n\n    getTextGeo(text, textProps, font) {\n      let textContent = text.content;\n      let fontSize = textProps.size;\n      let heightWidthRatio = textProps.heightWidthRatio;\n      let textGeo = new THREE.TextBufferGeometry(textContent, {\n        font: font,\n        size: fontSize,\n        height: 0.01,\n        curveSegments: 8\n      });\n      textGeo.scale(heightWidthRatio, 1, 1);\n      textGeo.computeBoundingBox();\n\n      switch (text.relativePosition) {\n        case 1:\n          textGeo.center();\n          break;\n\n        case 2:\n          textGeo.translate(0, -textGeo.boundingBox.min.y, 0);\n          break;\n\n        case 3:\n          textGeo.translate(0, -textGeo.boundingBox.max.y, 0);\n          break;\n\n        case 4:\n          textGeo.translate(-textGeo.boundingBox.max.x, -textGeo.boundingBox.max.y, 0);\n          break;\n\n        case 5:\n          textGeo.translate(-textGeo.boundingBox.max.x, -textGeo.boundingBox.min.y, 0);\n          break;\n\n        default:\n          textGeo.center();\n          break;\n      }\n\n      return textGeo;\n    }\n\n    applyPositionAndRotation(textArr, insertionPoints) {\n      let positionedMeshArr = [];\n      if (insertionPoints) insertionPoints.forEach(insertionPoint => {\n        let insertionVector = utils.initVector3(insertionPoint.insertionVector);\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          textArr.forEach(mesh => pivotPoint.attach(mesh.clone()));\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          textArr.forEach(mesh => pivotPoint.attach(mesh.clone()));\n        }\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        pivotPoint.children.forEach(child => {\n          child.applyMatrix4(pivotPoint.matrixWorld);\n          positionedMeshArr.push(child);\n        });\n      });\n      return positionedMeshArr;\n    }\n\n    getFontUrl(textProps) {\n      let fontType = textProps.type;\n      let fontStyle = textProps.style;\n      let fontUrl = \"\";\n      let fontName = \"\";\n\n      if (fontStyle == 1) {\n        if (fontType == 1) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Regular[\"Arial\"];\n          fontName = \"FONT_REGULAR_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Regular[\"Calibri\"];\n          fontName = \"FONT_REGULAR_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Regular[\"Courier\"];\n          fontName = \"FONT_REGULAR_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Regular[\"TimesNewRoman\"];\n          fontName = \"FONT_REGULAR_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 2) {\n        if (fontType == 1) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Italic[\"Arial\"];\n          fontName = \"FONT_ITALIC_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Italic[\"Calibri\"];\n          fontName = \"FONT_ITALIC_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Italic[\"Courier\"];\n          fontName = \"FONT_ITALIC_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Italic[\"TimesNewRoman\"];\n          fontName = \"FONT_ITALIC_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 3) {\n        if (fontType == 1) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Bold[\"Arial\"];\n          fontName = \"FONT_BOLD_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Bold[\"Calibri\"];\n          fontName = \"FONT_BOLD_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Bold[\"Courier\"];\n          fontName = \"FONT_BOLD_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.Bold[\"TimesNewRoman\"];\n          fontName = \"FONT_BOLD_TIMESNEWROMAN\";\n        }\n      } else if (fontStyle == 4) {\n        if (fontType == 1) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.BoldItalic[\"Arial\"];\n          fontName = \"FONT_BOLD_ITALIC_ARIAL\";\n        } else if (fontType == 2) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.BoldItalic[\"Calibri\"];\n          fontName = \"FONT_BOLD_ITALIC_CALIBRI\";\n        } else if (fontType == 3) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.BoldItalic[\"Courier\"];\n          fontName = \"FONT_BOLD_ITALIC_COURIER\";\n        } else if (fontType == 4) {\n          fontUrl = this.GraphicsThree3D.fontUrlArray.BoldItalic[\"TimesNewRoman\"];\n          fontName = \"FONT_BOLD_ITALIC_TIMESNEWROMAN\";\n        }\n      }\n\n      return [fontUrl, fontName];\n    }\n\n  }\n\n  _exports.TextLoader = TextLoader;\n});","define(\"m08-2020/lib/FileGeneration/DXFGenerator\", [\"exports\", \"three\", \"m08-2020/lib/Utils\", \"m08-2020/lib/ClippingUtils/OutlineGenerator\", \"m08-2020/lib/FileGeneration/dxf_colors\"], function (_exports, THREE, _Utils, _OutlineGenerator, _dxf_colors) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DXFGenerator = void 0;\n  const POINT_SIZE = 0.1;\n  const utils = new _Utils.Utils(THREE);\n\n  class DXFGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D; // global dxf data -->\n\n      this.blockSection = \"\";\n      this.entitiesSection = \"\";\n      this.textStyles = \"\";\n      this.offsetFromTop = 0; // object data -->\n\n      this.referenceZeroPoint = new THREE.Vector3();\n      this.scaleFactor = 1;\n      this.blocks = {\n        fasteners: [],\n        drawingObjects: []\n      };\n      this.projectedPoints = [];\n      this.wireframePoints = [];\n      this.fastenerPoints = {};\n      this.pointObjectsData = [];\n      this.lineObjectsData = []; // TODO: Might need better solution.\n\n      this.outerFrameOffset = 0;\n      this.drawingScaleBarBBox = null;\n      this.drawingSizeHalf = {\n        width: 0,\n        height: 0\n      };\n    }\n\n    downloadDXF3D() {\n      this.generateBlockAndEntitySections3D();\n      let contentDXF = this.generateContentDXF();\n      downloadTextFile(\"scene.dxf\", contentDXF.trim());\n    }\n\n    generateBlockAndEntitySections3D() {\n      this.positionCamera();\n      this.drawingSizeHalf = {\n        width: this.GraphicsThree3D.camera.right,\n        height: this.GraphicsThree3D.camera.top\n      };\n      this.setScaleAndReferencePoint();\n      let visProps = {\n        cutParams: {\n          type: 1\n        },\n        dxfFastenerVisualisation: {\n          addOn: true,\n          axis: true,\n          fastener: false\n        },\n        visualisation: {\n          arrow: true,\n          dimensionalChain: true,\n          drawingObject: true,\n          fastener: true\n        }\n      };\n      this.addToBlockSection(visProps);\n      this.addToEntitiesSection(visProps);\n    }\n\n    positionCamera() {\n      let controls = this.GraphicsThree3D.controls;\n      let camera = this.GraphicsThree3D.camera;\n      let midVector = this.GraphicsThree3D.objectMidVector;\n      let initialPos = new THREE.Vector3(-1, 1, 1).normalize().multiplyScalar(150).add(midVector);\n      let cameraTargetPos = {\n        x: midVector.x + initialPos.x,\n        y: midVector.y + initialPos.y,\n        z: midVector.z + initialPos.z\n      };\n      camera.position.set(cameraTargetPos.x, cameraTargetPos.y, cameraTargetPos.z);\n      camera.zoom = 1;\n      camera.lookAt(midVector);\n      camera.updateProjectionMatrix();\n      controls.update();\n    }\n\n    downloadDXF2D(dxfName, drawingPlaneData) {\n      this.generateBlockAndEntitySections(drawingPlaneData);\n      let contentDXF = this.generateContentDXF();\n      downloadTextFile(dxfName, contentDXF.trim());\n      this.clearDXF();\n    }\n\n    generateContentDXF() {\n      let contentDXF = \"\"; // Set units to centimeters\n\n      contentDXF += \"0\\nSECTION\\n2\\nHEADER\\n9\\n$INSUNITS\\n70\\n5\\n0\\nENDSEC\\n\";\n      contentDXF += `0\\nSECTION\\n2\\nTABLES\\n0\\nTABLE\\n2\\nSTYLE\\n${this.textStyles}0\\nENDTAB\\n0\\nENDSEC\\n`;\n      contentDXF += `0\\nSECTION\\n2\\nBLOCKS\\n${this.blockSection}0\\nENDSEC\\n`;\n      contentDXF += `0\\nSECTION\\n2\\nENTITIES\\n${this.entitiesSection}0\\nENDSEC\\n0\\nEOF`;\n      return contentDXF;\n    }\n\n    generateBlockAndEntitySections(drawingPlaneData) {\n      this.GraphicsThree3D.camera.updateProjectionMatrix();\n      this.outerFrameOffset = drawingPlaneData.drawingComponents.frameDataAndProps.frameProps.outerLine.thickness / 2;\n      this.setDrawingSizeHalf(drawingPlaneData.drawingComponents.frameDataAndProps.frameData, drawingPlaneData.drawingComponents.frameDataAndProps.frameProps);\n      this.setScaleAndReferencePoint();\n      this.addToBlockSection(drawingPlaneData);\n      this.addToEntitiesSection(drawingPlaneData);\n      this.createDrawingTextStyles(drawingPlaneData);\n      this.addDrawingFrame(drawingPlaneData.drawingComponents.frameDataAndProps);\n      this.addDrawingFreeTexts(drawingPlaneData.drawingComponents.freeTextDataAndProps);\n      this.addDrawingScaleTextBlock(drawingPlaneData.drawingComponents.scaleTextBlockDataAndProps, drawingPlaneData.scale);\n      this.addDrawingUnitTextBlock(drawingPlaneData.drawingComponents.unitTextBlockDataAndProps, drawingPlaneData.units);\n      this.addDrawingScaleBar(drawingPlaneData.drawingComponents.scaleBarDataAndProps, drawingPlaneData.scale, drawingPlaneData.units);\n      this.offsetFromTop += drawingPlaneData.drawingComponents.frameDataAndProps.frameData.height;\n      this.clearObjectData();\n    }\n\n    addToBlockSection(drawingPlaneData) {\n      let visualisation = drawingPlaneData.visualisation;\n      if (visualisation.fastener) this.blockSection += this.generateFastenerBlocks(drawingPlaneData, this.fastenerPoints);\n    }\n\n    addToEntitiesSection(drawingPlaneData) {\n      let visualisation = drawingPlaneData.visualisation;\n      if (visualisation.drawingObject) this.addDrawingObjectsToEntities(drawingPlaneData);\n      if (visualisation.fastener) this.addFastenerBlocksToEntities();\n    }\n\n    addDrawingObjectsToEntities(drawingPlaneData) {\n      let {\n        wireframePtsCorrected,\n        pointsCorrected\n      } = this.getCorrectedObjectPoints(drawingPlaneData.cutParams);\n      this.drawWireframeLines(wireframePtsCorrected).forEach(line => this.entitiesSection += `${line.trim()}\\n`);\n      this.drawPoints(pointsCorrected, drawingPlaneData.scale).forEach(point => this.entitiesSection += `${point.trim()}\\n`);\n      if (drawingPlaneData.cutParams.type === 1) this.drawLines(this.lineObjectsData).forEach(line => this.entitiesSection += `${line.trim()}\\n`);\n    }\n\n    addFastenerBlocksToEntities() {\n      this.blocks.fasteners.forEach(({\n        blockName,\n        layerName\n      }) => this.entitiesSection += `0\\nINSERT\\n2\\n${blockName}\\n8\\n${layerName}\\n10\\n${this.referenceZeroPoint.x}\\n20\\n${this.referenceZeroPoint.y}\\n30\\n${this.referenceZeroPoint.z}\\n`);\n    }\n\n    getCorrectedObjectPoints(cutParams) {\n      let wireframePtsCorrected, pointsCorrected;\n\n      if (cutParams.type === 1) {\n        wireframePtsCorrected = this.wireframePoints;\n        pointsCorrected = this.pointObjectsData;\n      } else {\n        wireframePtsCorrected = cutObjects(cutParams, this.wireframePoints);\n        pointsCorrected = getFilteredPoints(cutParams, this.pointObjectsData);\n        pointsCorrected.push(...cutLines(cutParams, this.lineObjectsData));\n      }\n\n      return {\n        wireframePtsCorrected,\n        pointsCorrected\n      };\n    }\n\n    createDrawingTextStyles(drawingPlaneData) {\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.freeTextDataAndProps.freeTextProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.scaleTextBlockDataAndProps.scaleTextBlockProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.unitTextBlockDataAndProps.unitTextBlockProps);\n      this.createDrawingTextStyle(drawingPlaneData.drawingComponents.scaleBarDataAndProps.scaleBarProps.text);\n    }\n\n    createDrawingTextStyle(textProps) {\n      let fontName = this.generateFontName(textProps); //\"ARIAL.TTF\";\n\n      this.textStyles += `0\\nSTYLE\\n2\\n${fontName}\\n70\\n0\\n40\\n0\\n41\\n0\\n50\\n0\\n71\\n0\\n42\\n0\\n3\\n${fontName + \".TTF\"}\\n`;\n    }\n\n    generateFontName(textProps) {\n      let name = \"\";\n\n      if (textProps) {\n        let {\n          style,\n          type\n        } = textProps;\n\n        switch (type) {\n          case 1:\n            name += \"ARIAL_\";\n            break;\n\n          case 2:\n            name += \"CALIBRI_\";\n            break;\n\n          case 3:\n            name += \"COURIER_\";\n            break;\n\n          case 4:\n            name += \"TIMESNEWROMAN_\";\n            break;\n\n          default:\n            break;\n        }\n\n        switch (style) {\n          case 1:\n            name += \"REGULAR\";\n            break;\n\n          case 2:\n            name += \"ITALIC\";\n            break;\n\n          case 3:\n            name += \"BOLD\";\n            break;\n\n          case 4:\n            name += \"BOLD_ITALIC\";\n            break;\n\n          default:\n            break;\n        }\n      } else name += \"ARIAL\";\n\n      return name;\n    }\n\n    addDrawingScaleBar(scaleBarDataAndProps, scale, units) {\n      let {\n        scaleBarData,\n        scaleBarProps\n      } = scaleBarDataAndProps;\n\n      if (scaleBarData && scaleBarProps) {\n        let areaProps = scaleBarProps.area;\n        let lineProps = scaleBarProps.line;\n        let textProps = scaleBarProps.text;\n        let areaColor = this.getColorIndexFromColor(this.getColorFromProps(areaProps));\n        let lineColor = this.getColorIndexFromColor(this.getColorFromProps(lineProps));\n        const NUM_OF_SEGMENTS = 5;\n        const SEGMENT_SIZE = 1;\n        const TEXT_ALIGNMENT = 1;\n        const Z_AXIS = new THREE.Vector3(0, 0, 1);\n        let insertionPoint = utils.initVector3(scaleBarData.insertionPoint);\n        let bboxSize = this.drawingScaleBarBBox.getSize(new THREE.Vector3());\n        let centerOffset = this.drawingScaleBarBBox.getCenter(new THREE.Vector3());\n        let alignmentOffset = this.GraphicsThree3D.DrawingPlaneLoader.getScaleBarAlignmentOffset(bboxSize, scaleBarData.relativePosition);\n        let rotation = this.getRotationFromDirection(scaleBarData.directionVector) * Math.PI / 180;\n        let barHeight = scaleBarData.height;\n        let barWidth = SEGMENT_SIZE * NUM_OF_SEGMENTS; /// Line -->\n\n        let linePoints = [new THREE.Vector3(0, 0, 0), new THREE.Vector3(barWidth, 0, 0), new THREE.Vector3(barWidth, barHeight, 0), new THREE.Vector3(0, barHeight, 0), new THREE.Vector3(0, 0, 0)].map(pt => pt.sub(centerOffset).add(alignmentOffset).applyAxisAngle(Z_AXIS, rotation).add(insertionPoint).sub(new THREE.Vector3(0, this.offsetFromTop, 0)));\n\n        for (let i = 0; i < linePoints.length - 1; i++) this.entitiesSection += this.drawLine(linePoints[i], linePoints[i + 1], lineColor, 0); /// <--\n        /// Fills -->\n\n\n        let solidPoints = [];\n\n        for (let i = 0; i < NUM_OF_SEGMENTS; i++) {\n          if (i % 2) solidPoints.push([new THREE.Vector3(SEGMENT_SIZE * i, 0, 0), new THREE.Vector3(SEGMENT_SIZE * (i + 1), 0, 0), new THREE.Vector3(SEGMENT_SIZE * i, barHeight, 0), new THREE.Vector3(SEGMENT_SIZE * (i + 1), barHeight, 0)].map(pt => pt.sub(centerOffset).add(alignmentOffset).applyAxisAngle(Z_AXIS, rotation).add(insertionPoint).sub(new THREE.Vector3(0, this.offsetFromTop, 0))));\n        }\n\n        solidPoints.forEach(ptArr => this.entitiesSection += this.drawSolid(ptArr[0], ptArr[1], ptArr[2], ptArr[3], areaColor, 0)); /// <--\n        /// Texts -->\n\n        let decimalPlaces = scaleBarData.decimalPlaces;\n        let distance = scaleBarData.distance;\n        let height = scaleBarData.height;\n        let textOffset = scaleBarData.textOffset;\n        let scaleFactor = this.GraphicsThree3D.DrawingPlaneLoader.getScaleFactor(units.length);\n        let heightOffset = -distance - textProps.size / 2;\n        let content = \"\";\n        let position = new THREE.Vector3(0, heightOffset, 0);\n\n        for (let i = 0; i <= NUM_OF_SEGMENTS; i++) {\n          content = (i * scale * scaleFactor).toFixed(decimalPlaces);\n          if (i === 5 && units.length) content += ` ${units.length}`;\n\n          if (textOffset) {\n            if (i % 2 === 1) position.y = height - heightOffset;else position.y = heightOffset;\n          }\n\n          position.x = i * SEGMENT_SIZE;\n          let textPosition = position.clone().sub(centerOffset).add(alignmentOffset).add(insertionPoint);\n          let textData = {\n            content,\n            directionVector: scaleBarData.directionVector,\n            insertionPoint: textPosition,\n            relativePosition: TEXT_ALIGNMENT\n          };\n          this.entitiesSection += this.makeTextFromDataAndProps(textData, textProps);\n        } /// <--\n\n      }\n    }\n\n    addDrawingUnitTextBlock(unitTextBlockDataAndProps, units) {\n      let {\n        unitTextBlockData,\n        unitTextBlockProps\n      } = unitTextBlockDataAndProps;\n\n      if (unitTextBlockData && unitTextBlockProps) {\n        let textData = { ...unitTextBlockData\n        };\n        textData.content = this.GraphicsThree3D.DrawingPlaneLoader.generateUnitTextContent(unitTextBlockData, units);\n        this.entitiesSection += this.makeTextFromDataAndProps(textData, unitTextBlockProps);\n      }\n    }\n\n    addDrawingScaleTextBlock(scaleTextBlockDataAndProps, scale) {\n      let {\n        scaleTextBlockData,\n        scaleTextBlockProps\n      } = scaleTextBlockDataAndProps;\n\n      if (scaleTextBlockData && scaleTextBlockProps) {\n        let textData = { ...scaleTextBlockData\n        };\n        textData.content = `${scaleTextBlockData.text} ${scale.toFixed(scaleTextBlockData.decimals)}`;\n        this.entitiesSection += this.makeTextFromDataAndProps(textData, scaleTextBlockProps);\n      }\n    }\n\n    addDrawingFreeTexts(freeTextDataAndProps) {\n      let {\n        freeTexts,\n        freeTextProps\n      } = freeTextDataAndProps;\n\n      if (freeTexts && freeTextProps) {\n        freeTexts.forEach(text => this.entitiesSection += this.makeTextFromDataAndProps(text, freeTextProps));\n      }\n    }\n\n    makeTextFromDataAndProps(textData, textProps) {\n      let {\n        size,\n        heightWidthRatio\n      } = textProps;\n      let color = this.getColorIndexFromColor(this.getColorFromProps(textProps));\n      let fontName = this.generateFontName(textProps);\n      let {\n        content,\n        directionVector,\n        relativePosition\n      } = textData;\n      let justifications = this.getTextJustifications(relativePosition);\n      let rotation = this.getRotationFromDirection(directionVector);\n      let insertionPoint = { ...textData.insertionPoint\n      };\n      insertionPoint.y -= this.offsetFromTop;\n      insertionPoint.z = 0;\n      let textParams = {\n        content,\n        position: insertionPoint,\n        height: size,\n        widthRatio: heightWidthRatio,\n        justifications,\n        rotation,\n        color,\n        fontName,\n        layerName: 0\n      };\n      return this.drawText(textParams);\n    }\n\n    getRotationFromDirection(directionVector) {\n      let xAxis = new THREE.Vector2(1, 0, 0);\n      let direction = utils.initVector2(directionVector).normalize();\n      let dot = direction.dot(xAxis);\n      let cross = direction.cross(xAxis);\n      let rotation = Math.acos(dot) * (cross < 0 ? 1 : -1) * 180 / Math.PI;\n      return rotation;\n    }\n\n    getTextJustifications(relativePosition) {\n      let justifications = {\n        justificHorizontal: 0,\n        justificVertical: 0\n      };\n\n      switch (relativePosition) {\n        case 1:\n          justifications.justificHorizontal = 1;\n          justifications.justificVertical = 2;\n          break;\n\n        case 2:\n          justifications.justificHorizontal = 0;\n          justifications.justificVertical = 1;\n          break;\n\n        case 3:\n          justifications.justificHorizontal = 0;\n          justifications.justificVertical = 3;\n          break;\n\n        case 4:\n          justifications.justificHorizontal = 2;\n          justifications.justificVertical = 3;\n          break;\n\n        case 5:\n          justifications.justificHorizontal = 2;\n          justifications.justificVertical = 1;\n          break;\n\n        default:\n          break;\n      }\n\n      return justifications;\n    }\n\n    drawSolid(p1, p2, p3, p4, color, layerName) {\n      let solid = `0\\nSOLID\\n8\\n${layerName}\\n62\\n${color}\\n` + `10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.y}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n` + `12\\n${p3.x}\\n22\\n${p3.y}\\n32\\n${p3.z}\\n13\\n${p4.x}\\n23\\n${p4.y}\\n33\\n${p4.z}\\n`;\n      return solid;\n    }\n\n    drawText({\n      content,\n      position,\n      height,\n      widthRatio,\n      justifications,\n      rotation,\n      color,\n      fontName,\n      layerName\n    }) {\n      let {\n        justificHorizontal,\n        justificVertical\n      } = justifications;\n      let text = `0\\nTEXT\\n8\\n${layerName}\\n1\\n${content}\\n40\\n${height}\\n41\\n${widthRatio}\\n72\\n${justificHorizontal}\\n73\\n${justificVertical}\\n` + `50\\n${rotation}\\n62\\n${color}\\n7\\n${fontName}\\n10\\n${0}\\n20\\n${0}\\n30\\n${0}\\n` + `11\\n${position.x}\\n21\\n${position.y}\\n31\\n${position.z}\\n`;\n      return text;\n    }\n\n    addDrawingFrame(frameDataAndProps) {\n      let {\n        frameData,\n        frameProps\n      } = frameDataAndProps;\n      this.generateFrameOuterLine(frameData, frameProps.outerLine);\n      this.generateFrameInnerLine(frameData, frameProps.innerLine);\n    }\n\n    generateFrameOuterLine(frameData, outerProps) {\n      let {\n        width,\n        height\n      } = frameData;\n      let thickness = 0; //outerProps.thickness;\n\n      let color = this.getColorIndexFromColor(this.getColorFromProps(outerProps));\n      let outerPoints = [{\n        x: 0,\n        y: -this.offsetFromTop,\n        z: 0\n      }, {\n        x: width,\n        y: -this.offsetFromTop,\n        z: 0\n      }, {\n        x: width,\n        y: height - this.offsetFromTop,\n        z: 0\n      }, {\n        x: 0,\n        y: height - this.offsetFromTop,\n        z: 0\n      }, {\n        x: 0,\n        y: -this.offsetFromTop,\n        z: 0\n      }];\n\n      for (let i = 0; i < outerPoints.length - 1; i++) {\n        let extrudeDirection = i % 2 ? {\n          x: 0,\n          y: 1,\n          z: 0\n        } : {\n          x: 1,\n          y: 0,\n          z: 0\n        };\n        this.entitiesSection += this.drawThickLine(outerPoints[i], outerPoints[i + 1], color, thickness, extrudeDirection, 0);\n      }\n    }\n\n    generateFrameInnerLine(frameData, innerProps) {\n      let {\n        width,\n        height\n      } = frameData;\n      let {\n        left,\n        right,\n        top,\n        bottom\n      } = frameData.border;\n      let thickness = 0; //innerProps.thickness;\n\n      let color = this.getColorIndexFromColor(this.getColorFromProps(innerProps));\n      let innerPoints = [{\n        x: left,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }, {\n        x: width - right,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }, {\n        x: width - right,\n        y: -this.offsetFromTop + height - top,\n        z: 0\n      }, {\n        x: left,\n        y: -this.offsetFromTop + height - top,\n        z: 0\n      }, {\n        x: left,\n        y: -this.offsetFromTop + bottom,\n        z: 0\n      }];\n\n      for (let i = 0; i < innerPoints.length - 1; i++) {\n        let extrudeDirection = i % 2 ? {\n          x: 0,\n          y: 1,\n          z: 0\n        } : {\n          x: 1,\n          y: 0,\n          z: 0\n        };\n        this.entitiesSection += this.drawThickLine(innerPoints[i], innerPoints[i + 1], color, thickness, extrudeDirection, 0);\n      }\n    }\n\n    setScaleAndReferencePoint() {\n      this.getScale();\n      this.referenceZeroPoint.copy(this.getProjectedReferenceZeroPoint());\n    }\n\n    getProjectedReferenceZeroPoint() {\n      return this.projectPointOnScreen(new THREE.Vector3(), this.GraphicsThree3D.camera);\n    }\n\n    getScale() {\n      let {\n        x,\n        y\n      } = this.GraphicsThree3D.renderer.getSize(new THREE.Vector2());\n      this.scaleFactor = x / y / (this.drawingSizeHalf.width / this.drawingSizeHalf.height);\n    }\n\n    generateFastenerBlocks(drawingPlaneData, fastenerPoints) {\n      const CAMERA = this.GraphicsThree3D.camera;\n      const POINT_SIZE_CORRECTED = POINT_SIZE / drawingPlaneData.scale;\n      let fastenerBlocks = \"\";\n      let dxfFastenerVisualisation = drawingPlaneData.dxfFastenerVisualisation;\n      Object.entries(fastenerPoints).forEach(([id, pointData]) => {\n        let blockName = `${drawingPlaneData.id}_${id}`;\n        let layerName = `fastener_${id}`;\n        this.blocks.fasteners.push({\n          blockName,\n          layerName\n        });\n        fastenerBlocks += `0\\nBLOCK\\n8\\n${layerName}\\n2\\n${blockName}\\n70\\n0\\n10\\n${this.referenceZeroPoint.x}\\n20\\n${this.referenceZeroPoint.y}\\n30\\n${this.referenceZeroPoint.x}\\n3\\n${blockName}\\n`;\n        let isCutDrawing = drawingPlaneData.cutParams.type === 2;\n\n        if (dxfFastenerVisualisation.axis) {\n          pointData.axis.forEach(({\n            pointPair,\n            properties\n          }) => {\n            let lineColor = this.getColorIndexFromColor(this.getColorFromProps(properties.line));\n            let pointColor = this.getColorIndexFromColor(this.getColorFromProps(properties.point));\n\n            if (isCutDrawing) {\n              const DELTA = 1e-4;\n              let plane = getDrawingCuttingPlane(drawingPlaneData.cutParams);\n              let distanceOne = plane.distanceToPoint(pointPair[0]);\n              let distanceTwo = plane.distanceToPoint(pointPair[1]);\n\n              if (Math.abs(distanceOne) < DELTA && Math.abs(distanceTwo) < DELTA) {\n                let projectedPoints = this.getProjectedPointsArr([pointPair], CAMERA);\n                projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawLine(ptsPair[0], ptsPair[1], lineColor, layerName));\n                projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawCircle(ptsPair[0], POINT_SIZE_CORRECTED, pointColor, layerName));\n              } else {\n                let line = new THREE.Line3(pointPair[0], pointPair[1]);\n                let point = plane.intersectLine(line, new THREE.Vector3());\n                if (point) fastenerBlocks += this.drawCircle(this.projectPointOnScreen(point, CAMERA), POINT_SIZE_CORRECTED, lineColor, layerName);\n              }\n            } else {\n              let projectedPoints = this.getProjectedPointsArr([pointPair], CAMERA);\n              projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawLine(ptsPair[0], ptsPair[1], lineColor, layerName));\n              projectedPoints.forEach(ptsPair => fastenerBlocks += this.drawCircle(ptsPair[0], POINT_SIZE_CORRECTED, pointColor, layerName));\n            }\n          });\n        }\n\n        if (dxfFastenerVisualisation.addOn) {\n          pointData.addon.forEach(({\n            points,\n            mesh,\n            properties\n          }) => {\n            let color = this.getColorIndexFromColor(this.getColorFromProps(properties));\n\n            if (isCutDrawing) {\n              let cuttingPlane = getDrawingCuttingPlane(drawingPlaneData.cutParams);\n\n              _OutlineGenerator.OutlineGenerator.generateCutLinePts(mesh, cuttingPlane).forEach(ptPair => {\n                let transfromedPts = ptPair.map(pt => pt.clone());\n                let pts = this.getProjectedPoints(transfromedPts, CAMERA);\n                fastenerBlocks += this.drawLine(pts[0], pts[1], color, layerName);\n              });\n            } else {\n              let projectedPoints = this.getProjectedPointsArr(points, CAMERA);\n              projectedPoints.forEach(pointsArr => {\n                for (let i = 0; i < pointsArr.length - 1; i++) fastenerBlocks += this.drawLine(pointsArr[i], pointsArr[i + 1], color, layerName);\n              });\n            }\n          });\n        }\n\n        if (dxfFastenerVisualisation.fastener) {\n          let cuttingPlane = isCutDrawing ? getDrawingCuttingPlane(drawingPlaneData.cutParams) : getFastenerCuttingPlane(drawingPlaneData.normal);\n          pointData.fastener.forEach(({\n            mesh,\n            color\n          }) => {\n            let fastenerColor = this.getColorIndexFromColor(this.getColorFromProps({\n              color\n            }));\n            let cutMesh = mesh.clone();\n            if (!isCutDrawing) cutMesh.matrixWorld.identity();\n\n            _OutlineGenerator.OutlineGenerator.generateCutLinePts(cutMesh, cuttingPlane).forEach(ptPair => {\n              let transfromedPts = ptPair.map(pt => isCutDrawing ? pt.clone() : mesh.localToWorld(pt.clone()));\n              let pts = this.getProjectedPoints(transfromedPts, CAMERA);\n              fastenerBlocks += this.drawLine(pts[0], pts[1], fastenerColor, layerName);\n            });\n          });\n        }\n\n        fastenerBlocks += \"0\\nENDBLK\\n\";\n      });\n      return fastenerBlocks;\n    }\n\n    drawLines(linesData) {\n      let lines = [];\n      linesData.forEach(line => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(line.properties));\n        let projectedPointPairs = this.getProjectedPointsArr(line.pointPairs, this.GraphicsThree3D.camera);\n        projectedPointPairs.forEach(pair => lines.push(this.drawLine(pair[0], pair[1], color, `drawingObject_${line.id}`)));\n      });\n      return lines;\n    }\n\n    drawPoints(pointsData, drawingScale) {\n      let points = [];\n      pointsData.forEach(point => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(point.properties));\n        let radius = point.properties.size / drawingScale;\n        let centerPos = utils.initVector3(point.position);\n        let projectedCenterPos = this.projectPointOnScreen(centerPos, this.GraphicsThree3D.camera);\n        points.push(this.drawCircle(projectedCenterPos, radius, color, `drawingObject_${point.id}`));\n      });\n      return points;\n    }\n\n    drawCircle(center, radius, color, layerName) {\n      return `0\\nCIRCLE\\n8\\n${layerName}\\n62\\n${color}\\n40\\n${radius}\\n10\\n${center.x}\\n20\\n${center.y}\\n30\\n${center.z}\\n`;\n    }\n\n    drawArc(center, radius, color, layerName) {\n      const START_ANGLE = 0,\n            END_ANGLE = 359.9;\n      return `0\\nARC\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${center.x}\\n20\\n${center.y}\\n30\\n${center.z}\\n40\\n${radius}\\n50\\n${START_ANGLE}\\n51\\n${END_ANGLE}`;\n    }\n\n    getProjectedPointsArr(ptPairs, camera) {\n      return ptPairs.map(pair => this.getProjectedPoints(pair, camera));\n    }\n\n    getProjectedPoints(points, camera) {\n      return points.map(pt => this.projectPointOnScreen(pt, camera));\n    }\n\n    projectPointOnScreen(point, camera) {\n      let widthHalf = this.drawingSizeHalf.width;\n      let heightHalf = this.drawingSizeHalf.height;\n      let projectedPoint = point.clone();\n      projectedPoint.project(camera);\n      projectedPoint.x = projectedPoint.x * widthHalf + widthHalf - this.outerFrameOffset;\n      projectedPoint.y = projectedPoint.y * heightHalf + heightHalf - this.offsetFromTop;\n      projectedPoint.z = 0;\n      this.projectedPoints.push(projectedPoint);\n      return projectedPoint;\n    }\n\n    setDrawingSizeHalf(frameData, frameProps) {\n      this.drawingSizeHalf = {\n        width: (frameData.width + frameProps.outerLine.thickness) / 2,\n        height: (frameData.height + frameProps.outerLine.thickness) / 2\n      };\n    }\n\n    getFacesFromScene(scene) {\n      let faces = [];\n      scene.traverse(child => {\n        if (child.addToDXF) {\n          faces.push(...this.getMeshFaces(child));\n        }\n      });\n      return faces;\n    }\n\n    getMeshFaces(mesh) {\n      let faces = [];\n      let color = this.getColorIndexFromColor(mesh.material[0].color);\n      let geometry = mesh.geometry;\n      let meshFaces = geometry.faces;\n      let meshVertices = geometry.vertices;\n      meshFaces.forEach(face => {\n        let vectorA = meshVertices[face.a].clone();\n        let vectorB = meshVertices[face.b].clone();\n        let vectorC = meshVertices[face.c].clone();\n        vectorA.applyMatrix4(mesh.matrix);\n        vectorB.applyMatrix4(mesh.matrix);\n        vectorC.applyMatrix4(mesh.matrix);\n        faces.push(this.drawFace(vectorA, vectorB, vectorC, color, mesh.dxfLayer));\n      });\n      return faces;\n    }\n\n    drawWireframeLines(wireframePoints) {\n      let lines = [];\n      wireframePoints.forEach(({\n        lineProps,\n        pointPairs,\n        id\n      }) => {\n        let color = this.getColorIndexFromColor(this.getColorFromProps(lineProps));\n        let projectedPoints = this.getProjectedPointsArr(pointPairs, this.GraphicsThree3D.camera);\n        let layerName = `drawingObject_${id}`;\n        projectedPoints.forEach(ptsPair => lines.push(this.drawLine(ptsPair[0], ptsPair[1], color, layerName)));\n      });\n      return lines;\n    }\n\n    drawThickLine(p1, p2, color, thickness, extrudeDirection, layerName) {\n      let line = `0\\nLINE\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n` + `39\\n${thickness}\\n210\\n${extrudeDirection.x}\\n220\\n${extrudeDirection.y}\\n230\\n${extrudeDirection.z}\\n`;\n      return line;\n    }\n\n    drawLine(p1, p2, color, layerName) {\n      return `0\\nLINE\\n8\\n${layerName}\\n62\\n${color}\\n10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}\\n11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}\\n`;\n    }\n\n    drawFace(p1, p2, p3, color, layerName) {\n      let face = [`0\\n3DFACE\\n8\\n${layerName}\\n62\\n${color}`, `10\\n${p1.x}\\n20\\n${p1.y}\\n30\\n${p1.z}`, `11\\n${p2.x}\\n21\\n${p2.y}\\n31\\n${p2.z}`, `12\\n${p3.x}\\n22\\n${p3.y}\\n32\\n${p3.z}`, `13\\n${p3.x}\\n23\\n${p3.y}\\n33\\n${p3.z}`].join(\"\\n\");\n      return face;\n    }\n\n    getColorIndexFromColor(color) {\n      let colorRGB = this.getRGBFromColor(color);\n      let colorIndex = this.getColorIndexFromRGB(colorRGB);\n      return colorIndex;\n    }\n\n    getColorIndexFromRGB([r, g, b]) {\n      let minDistance = Infinity;\n      let matchedIndex = 0;\n\n      for (let i = 0; i < 255; i++) {\n        let lookupColor = _dxf_colors.COLOR_LOOKUP[i];\n        let distance = Math.pow(r - lookupColor[0], 2) + Math.pow(g - lookupColor[1], 2) + Math.pow(b - lookupColor[2], 2);\n\n        if (distance < minDistance) {\n          minDistance = distance;\n          matchedIndex = i;\n        }\n      }\n\n      return matchedIndex;\n    }\n\n    getRGBFromColor(color) {\n      return [255 * color.r, 255 * color.g, 255 * color.b];\n    }\n\n    getColorFromProps(props) {\n      return {\n        r: props.color.red / 255,\n        g: props.color.green / 255,\n        b: props.color.blue / 255\n      };\n    }\n\n    clearObjectData() {\n      this.referenceZeroPoint = new THREE.Vector3();\n      this.scaleFactor = 1;\n      this.blocks = {\n        fasteners: [],\n        drawingObjects: []\n      };\n      this.projectedPoints = [];\n      this.wireframePoints = [];\n      this.fastenerPoints = {};\n      this.pointObjectsData = [];\n      this.lineObjectsData = [];\n      this.outerFrameOffset = 0;\n      this.drawingScaleBarBBox = null;\n      this.drawingSizeHalf = {\n        width: 0,\n        height: 0\n      };\n    }\n\n    clearDXF() {\n      this.blockSection = \"\";\n      this.entitiesSection = \"\";\n      this.textStyles = \"\";\n      this.offsetFromTop = 0;\n      this.clearObjectData();\n    }\n\n  }\n\n  _exports.DXFGenerator = DXFGenerator;\n\n  function cutLines(cutParams, lineObjectsData) {\n    let generatedPts = [];\n    let plane = getDrawingCuttingPlane(cutParams);\n    lineObjectsData.forEach(data => {\n      for (let pair of data.pointPairs) {\n        if (Math.sign(plane.distanceToPoint(pair[0])) !== Math.sign(plane.distanceToPoint(pair[1]))) {\n          let line = new THREE.Line3(pair[0], pair[1]);\n          generatedPts.push({\n            id: data.id,\n            position: plane.intersectLine(line, new THREE.Vector3()),\n            properties: {\n              color: data.properties.color,\n              size: data.properties.thickness\n            }\n          });\n          break;\n        }\n      }\n    });\n    return generatedPts;\n  }\n\n  function getFastenerCuttingPlane(drawingNormal) {\n    let cutNormal = getFastenerCutNormal(drawingNormal);\n    return new THREE.Plane(cutNormal, 0);\n  }\n\n  function getFastenerCutNormal(normal) {\n    let drawingNormal = utils.initVector3(normal).normalize();\n    let axisArr = [new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 0, 1)];\n    let maxDotProd = 0;\n    let cutNormal = new THREE.Vector3();\n\n    for (let axis of axisArr) {\n      let dotProdAbs = Math.abs(axis.dot(drawingNormal));\n\n      if (dotProdAbs > maxDotProd) {\n        maxDotProd = dotProdAbs;\n        cutNormal.copy(axis);\n      }\n    }\n\n    return cutNormal;\n  }\n\n  function getFilteredPoints(cutParams, pointsDataArr) {\n    const MIN_DISTANCE = 1e-4;\n    let plane = getDrawingCuttingPlane(cutParams);\n    return pointsDataArr.filter(data => Math.abs(plane.distanceToPoint(utils.initVector3(data.position))) < MIN_DISTANCE);\n  }\n\n  function cutObjects(cutParams, objectDataArr) {\n    let plane = getDrawingCuttingPlane(cutParams);\n    let objectsCutted = objectDataArr.map(data => ({ ...data,\n      pointPairs: _OutlineGenerator.OutlineGenerator.generateCutLinePts(data.object, plane)\n    }));\n    return objectsCutted;\n  }\n\n  function getDrawingCuttingPlane(cutParams) {\n    let normalVec = utils.initVector3(cutParams.normalVector);\n    let constant = getCuttingPlaneDistance(cutParams.positionVector, normalVec);\n    return new THREE.Plane(normalVec, constant);\n  }\n\n  function getCuttingPlaneDistance(positionVector, normalVector) {\n    // maybe use method \"getClippingPlaneDistance\" from drawing plane loader?\n    let positionVec = utils.initVector3(positionVector);\n    let positionDirection = positionVec.clone().normalize();\n    let planeDistance = positionVec.length();\n    if (normalVector.dot(positionDirection) > 0) planeDistance *= -1;\n    return planeDistance;\n  }\n\n  function downloadTextFile(filename, text) {\n    let element = document.createElement('a');\n    element.setAttribute('href', `data:text/plain;charset=utf-8,${text}`);\n    element.setAttribute('download', filename);\n    element.style.display = 'none';\n    document.body.appendChild(element);\n    element.click();\n    document.body.removeChild(element);\n  }\n});","define(\"m08-2020/lib/FileGeneration/DrawingPlaneLoader\", [\"exports\", \"m08-2020/lib/Utils\", \"m08-2020/lib/FigureLoaders/DimensionalChainLoader\"], function (_exports, _Utils, _DimensionalChainLoader) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DrawingPlaneLoader = void 0;\n  const _defaultImgSize = 2000;\n\n  class DrawingPlaneLoader {\n    constructor(GraphicsThree3D) {\n      this.THREE = GraphicsThree3D.THREE;\n      this.fontUrlArray = GraphicsThree3D.fontUrlArray;\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.utils = new _Utils.Utils(GraphicsThree3D.THREE);\n      this.imgQualityWidth = 2000;\n      this.lastPlaneID = \"\";\n    }\n\n    loadDrawingPlanes(quality) {\n      this.setImageQuality(quality);\n      this.generateDrawingPlanes();\n      this.setImageQuality(\"high\");\n    }\n\n    setImageQuality(quality) {\n      switch (quality) {\n        case \"high\":\n          this.setImgWidth(2000);\n          break;\n\n        case \"medium\":\n          this.setImgWidth(1200);\n          break;\n\n        case \"low\":\n          this.setImgWidth(600);\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    setImgWidth(width) {\n      this.imgQualityWidth = width;\n    }\n\n    generateDrawingPlanes(storeZoomState = true, uploadPNGImages, getDXF, cid) {\n      let jsonData = this.GraphicsThree3D.jsonData;\n      let settingProps = jsonData.settings.drawingPlane.properties;\n      let unitsData = jsonData.units;\n      let drawingPlanes = jsonData.drawingPlanes;\n      let units = this.utils.processUnits(unitsData);\n      let dimensionalChainLoader = new _DimensionalChainLoader.DimensionalChainLoader(this.GraphicsThree3D);\n      let dimensionalChains = jsonData.dimensionalChains;\n      this.lastPlaneID = drawingPlanes[drawingPlanes.length - 1][\"id\"];\n      let initCamParams = this.getInitialCameraParameters();\n      let images = [];\n\n      for (let drawingPlane of drawingPlanes) {\n        let initGraphicsParams = this.getGraphicsParameters(this.GraphicsThree3D); // Components\n\n        let drawingData = drawingPlane.components.drawing;\n        let frameData = { ...drawingPlane.components.frame,\n          drawingNormal: drawingData.normalVector,\n          drawingVerticalVector: drawingData.verticalVector\n        };\n        let freeTexts = drawingPlane.components.freeTexts;\n        let scaleBarData = drawingPlane.components.scaleBar;\n        let scaleTextBlockData = drawingPlane.components.scaleTextBlock;\n        let unitTextBlockData = drawingPlane.components.unitTextBlock; // Properties\n\n        let properties = drawingPlane.properties;\n        let drawingProps = properties.drawing ? properties.drawing : settingProps.drawing;\n        let frameProps = properties.frame ? properties.frame : settingProps.frame;\n        let freeTextProps = properties.freeText ? properties.freeText : settingProps.freeText;\n        let scaleBarProps = properties.scaleBar ? properties.scaleBar : settingProps.scaleBar;\n        let scaleTextBlockProps = properties.scaleTextBlock ? properties.scaleTextBlock : settingProps.scaleTextBlock;\n        let unitTextBlockProps = properties.unitTextBlock ? properties.unitTextBlock : settingProps.unitTextBlock;\n\n        if (drawingData && drawingProps) {\n          let chainZoomState = this.GraphicsThree3D.drawingPlaneZoomStateDimChain;\n\n          if (dimensionalChains) {\n            for (let chain of dimensionalChains) {\n              if (!storeZoomState) zoomChainComponents(chainZoomState, chain, drawingPlane);else saveChainZoomState(this.GraphicsThree3D, chain);\n            }\n          }\n\n          this.removeDimChains3D();\n          this.drawDimensionalChains(dimensionalChainLoader, dimensionalChains, drawingData);\n          this.checkWrongScale(frameData);\n          this.makeDrawing(drawingData, drawingProps);\n\n          if (frameData && frameProps) {\n            let frame = this.makeFrame(frameData, frameProps, drawingData);\n            if (freeTextProps && frameData) this.drawFreeTexts(freeTexts, freeTextProps, frameData, frame);\n            if (scaleTextBlockData && scaleTextBlockProps) this.drawScaleTextBlock(scaleTextBlockData, scaleTextBlockProps, frameData, frame);\n            if (unitTextBlockData && unitTextBlockProps) this.drawUnitTextBlock(unitTextBlockData, unitTextBlockProps, units, frameData, frame);\n            if (scaleBarData && scaleBarProps) this.generateScaleBarObject(scaleBarData, scaleBarProps, units.length, frameData, frame);\n          }\n        }\n\n        let scale = this.GraphicsThree3D.drawingPlaneZoomStateDimChain.initialFactors[drawingPlane.id] ? this.GraphicsThree3D.drawingPlaneZoomStateDimChain.initialFactors[drawingPlane.id] : this.getScale(frameData);\n        this.scaleCanvas(frameData, frameProps, scale);\n        let initialWireframeStatus = this.makeObjectFilled();\n\n        if (storeZoomState) {\n          if (this.GraphicsThree3D.drawingPlaneZoomStateDimChain.finalFactorMax < scale) this.GraphicsThree3D.drawingPlaneZoomStateDimChain.finalFactorMax = scale;\n          if (this.GraphicsThree3D.drawingPlaneZoomStateDimChain.finalFactorMin > scale) this.GraphicsThree3D.drawingPlaneZoomStateDimChain.finalFactorMin = scale;\n          this.GraphicsThree3D.drawingPlaneZoomStateDimChain.initialFactors[drawingPlane.id] = scale;\n        }\n\n        let drawingComponents = {\n          frameDataAndProps: {\n            frameData,\n            frameProps\n          },\n          freeTextDataAndProps: {\n            freeTexts,\n            freeTextProps\n          },\n          scaleTextBlockDataAndProps: {\n            scaleTextBlockData,\n            scaleTextBlockProps\n          },\n          unitTextBlockDataAndProps: {\n            unitTextBlockData,\n            unitTextBlockProps\n          },\n          scaleBarDataAndProps: {\n            scaleBarData,\n            scaleBarProps\n          }\n        };\n        let dxfPlaneData = this.getDXFPlaneData(drawingPlane, drawingProps, scale, drawingComponents, units); // this.downloadPNGandDXFAndReset( initGraphicsParams, dxfPlaneData, storeZoomState );\n\n        images.push({\n          filename: drawingPlane.id + \"_\" + cid + \".png\",\n          data: this.downloadPNGandDXFAndReset(initGraphicsParams, dxfPlaneData, storeZoomState, cid.toString(), uploadPNGImages, getDXF)\n        });\n        this.resetToInitialWireframeStatus(this.GraphicsThree3D, initialWireframeStatus);\n      }\n\n      this.resetInitialCameraParameters(initCamParams);\n      if (storeZoomState) this.generateDrawingPlanes(false);\n\n      if (!storeZoomState) {\n        this.GraphicsThree3D.drawingPlaneZoomStateDimChain = {\n          initialFactors: {},\n          initialTextSizes: {},\n          initialComponentSizes: {},\n          finalFactorMax: 0,\n          finalFactorMin: Infinity\n        };\n      }\n\n      return images;\n    }\n\n    resetInitialCameraParameters(initialParameters) {\n      let camera = this.GraphicsThree3D.camera;\n      let controls = this.GraphicsThree3D.controls;\n      let {\n        camZoom,\n        camPos,\n        camTarget\n      } = initialParameters;\n      camera.position.copy(camPos);\n      camera.lookAt(camTarget);\n      camera.updateProjectionMatrix();\n      controls.update();\n      camera.zoom = camZoom;\n      controls.target.copy(camTarget);\n      camera.up = new this.THREE.Vector3(0, 1, 0);\n      camera.lookAt(controls.target);\n      controls.update();\n    }\n\n    getInitialCameraParameters() {\n      return {\n        camZoom: this.GraphicsThree3D.camera.zoom,\n        camPos: this.GraphicsThree3D.camera.position.clone(),\n        camTarget: this.GraphicsThree3D.controls.target.clone()\n      };\n    }\n\n    getDXFPlaneData(drawingPlane, drawingProps, scale, drawingComponents, units) {\n      return {\n        id: drawingPlane.id,\n        output: drawingPlane.output,\n        normal: drawingPlane.components.drawing.normalVector,\n        cutParams: {\n          type: drawingPlane.components.drawing.type,\n          normalVector: drawingPlane.components.drawing.normalVector,\n          positionVector: drawingPlane.components.drawing.positionVector\n        },\n        scale,\n        units,\n        drawingComponents,\n        visualisation: {\n          drawingObject: drawingProps.visualisation.drawingObject,\n          fastener: drawingProps.visualisation.fastener,\n          arrow: drawingProps.visualisation.arrow\n        },\n        dxfFastenerVisualisation: { ...drawingProps.dxfFastenerVisualisation\n        }\n      };\n    }\n\n    checkWrongScale(frameData) {\n      let scaleType = frameData.scaleType;\n      if (scaleType < 1 || scaleType > 2) this.GraphicsThree3D.clearScene();else {\n        let testScale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n        let checkScale = this.getScale(frameData);\n        if (checkScale < testScale) this.GraphicsThree3D.clearScene();\n      }\n    }\n    /**\n     * Download PNG\n     */\n\n\n    downloadPNGandDXFAndReset(initGraphicsParams, drawingPlaneData, storeZoomState, imgName, uploadPNGImages, getDXF) {\n      let [cameraZoom, canvasWidth, canvasHeight] = initGraphicsParams;\n      let drawingOutput = drawingPlaneData.output;\n      let imgData = null;\n      this.GraphicsThree3D.renderAllScenes();\n\n      if (!storeZoomState) {\n        if (getDXF && drawingOutput.dxf) {\n          if (drawingPlaneData.id === this.lastPlaneID) imgData = this.GraphicsThree3D.dxfGenerator.downloadDXF2D(imgName + \".dxf\", drawingPlaneData);else this.GraphicsThree3D.dxfGenerator.generateBlockAndEntitySections(drawingPlaneData); // if ( drawingPlaneData.id === this.lastPlaneID ) this.GraphicsThree3D.dxfGenerator.downloadDXF2D( \"plane.dxf\", drawingPlaneData );\n          // else this.GraphicsThree3D.dxfGenerator.generateBlockAndEntitySections( drawingPlaneData );\n        }\n\n        if (uploadPNGImages && drawingOutput.image) imgData = this.GraphicsThree3D.downloadAsPNGImage(_defaultImgSize, imgName); //this.GraphicsThree3D.downloadAsPNGImage( \"plane.png\", _defaultImgSize );\n      }\n\n      this.GraphicsThree3D.clearScene();\n      this.GraphicsThree3D.camera.zoom = cameraZoom;\n      this.GraphicsThree3D.renderer.domElement.style.width = canvasWidth.toFixed(0) + \"px\";\n      this.GraphicsThree3D.renderer.domElement.style.height = canvasHeight.toFixed(0) + \"px\";\n      this.GraphicsThree3D.updateRendererAndCamera(this.GraphicsThree3D.renderer, this.GraphicsThree3D.camera);\n      this.GraphicsThree3D.renderAllScenes();\n      this.GraphicsThree3D.camera.up = new this.THREE.Vector3(0, 1, 0);\n      this.GraphicsThree3D.drawCustom(this.GraphicsThree3D.globalJSONData);\n    }\n\n    makeObjectFilled() {\n      let wireframeStatus = this.GraphicsThree3D.isWireFrameOnly;\n      this.GraphicsThree3D.isWireFrameOnly = true;\n      this.GraphicsThree3D.ToggleWireframe();\n      return wireframeStatus;\n    }\n\n    resetToInitialWireframeStatus(GraphicsThree3D, wireframeStatus) {\n      GraphicsThree3D.isWireFrameOnly = !wireframeStatus;\n      GraphicsThree3D.ToggleWireframe();\n    }\n\n    getGraphicsParameters(GraphicsThree3D) {\n      let cameraZoom = GraphicsThree3D.camera.zoom;\n      let rendererSize = GraphicsThree3D.renderer.getSize(new this.THREE.Vector2());\n      return [cameraZoom, rendererSize.x, rendererSize.y];\n    }\n    /**\n     * Scale Canvas\n     */\n\n\n    scaleCanvas(frameData, frameProps, scale) {\n      let [frameWidth, frameRatio] = this.getCameraWidthAndRatio(frameData, frameProps, scale);\n      this.changeRendererSize(frameRatio, this.GraphicsThree3D);\n      this.zoomCamera(this.GraphicsThree3D.camera, frameRatio, frameWidth);\n    }\n\n    getCameraWidthAndRatio(frameData, frameProps, scale) {\n      let lineWidth = frameProps.outerLine.thickness;\n      let frameWidth = frameData.width + lineWidth;\n      let frameHeight = frameData.height + lineWidth;\n      let scaledWidth = frameWidth * scale;\n      let frameRatio = frameWidth / frameHeight;\n      return [scaledWidth, frameRatio];\n    }\n\n    changeRendererSize(frameRatio, GraphicsThree3D) {\n      let canvasSize = GraphicsThree3D.renderer.getSize(new this.THREE.Vector2());\n      let canvasWidth = canvasSize.x;\n      let canvasHeight = canvasSize.y;\n      let canvasRatio = canvasWidth / canvasHeight;\n      const QUALITY_SCALE = this.imgQualityWidth / canvasWidth;\n      let correctedWidth = Math.floor(QUALITY_SCALE * canvasWidth * frameRatio / canvasRatio);\n      let correctedHeight = Math.floor(QUALITY_SCALE * canvasHeight);\n      GraphicsThree3D.renderer.setSize(correctedWidth, correctedHeight);\n    }\n\n    zoomCamera(camera, frameRatio, frameWidth) {\n      const MULTIPLIER = 16;\n      const CAMERA_VIEW_SIZE = 1000;\n      camera.left = -CAMERA_VIEW_SIZE * frameRatio / MULTIPLIER;\n      camera.right = CAMERA_VIEW_SIZE * frameRatio / MULTIPLIER;\n      camera.top = CAMERA_VIEW_SIZE / MULTIPLIER;\n      camera.bottom = -CAMERA_VIEW_SIZE / MULTIPLIER;\n      let cameraWidth = camera.right - camera.left;\n      camera.zoom = cameraWidth / frameWidth;\n      camera.updateProjectionMatrix();\n    }\n\n    drawDimensionalChains(dimensionalChainLoader, dimensionalChains, drawingData) {\n      let drawingNormal = this.utils.initVector3(drawingData.normalVector).normalize();\n      let drawingVerticalVec = this.utils.initVector3(drawingData.verticalVector).normalize();\n      let drawingHorizontalVec = new this.THREE.Vector3().crossVectors(drawingNormal, drawingVerticalVec).normalize();\n      let clippingPlane;\n\n      if (drawingData.type === 2) {\n        let planeDistance = this.getClippingPlaneDistance(drawingData, drawingNormal);\n        clippingPlane = new this.THREE.Plane(drawingNormal, planeDistance);\n      }\n\n      if (dimensionalChains) dimensionalChains.forEach(chain => {\n        let view2dAlignments = chain.alignments.views2d;\n\n        if (view2dAlignments) {\n          view2dAlignments.forEach(alignment => {\n            if (chain.alignments.textAlignment === 1) {\n              let pointsDir = this.getTextDirectionVec({\n                drawingVerticalVec,\n                drawingHorizontalVec\n              }, chain.referencePoints);\n              alignment.normalVector = drawingNormal.clone().negate();\n              alignment.directionVector = pointsDir;\n            }\n          });\n          let chainData = { ...chain,\n            alignments: { ...chain.alignments,\n              textAlignment: 2\n            }\n          };\n          view2dAlignments.forEach(alignment => {\n            let normalVector = new this.THREE.Vector3().crossVectors(alignment.offsetVector, alignment.directionVector).normalize();\n            let dotProdAbs = Math.abs(normalVector.dot(drawingNormal));\n\n            if (dotProdAbs === 1) {\n              let chains = dimensionalChainLoader.drawMaskette(chainData, alignment);\n\n              for (let ch of chains) {\n                if (drawingData.type === 1 || drawingData.type === 2 && this.chainOnPlane(ch, clippingPlane)) this.GraphicsThree3D.scene.add(ch);\n              }\n            }\n          });\n        }\n      });\n      this.GraphicsThree3D.centerScene();\n      this.GraphicsThree3D.DimensionalChainIntersectionDetection.chainArr = [];\n    }\n\n    getTextDirectionVec(drawingSpaceVectors, chainReferencePoints) {\n      let {\n        drawingVerticalVec,\n        drawingHorizontalVec\n      } = drawingSpaceVectors;\n      let startPoint = this.utils.initVector3(chainReferencePoints[0]);\n      let endPoint = this.utils.initVector3(chainReferencePoints[chainReferencePoints.length - 1]);\n      let pointsDir = new this.THREE.Vector3().subVectors(endPoint, startPoint).normalize();\n      let yDot = drawingVerticalVec.dot(pointsDir);\n      if (Math.abs(yDot) === 1 && yDot < 0) pointsDir.negate();else if (drawingHorizontalVec.dot(pointsDir) < 0) pointsDir.negate();\n      return pointsDir;\n    }\n\n    removeDimChains3D() {\n      this.GraphicsThree3D.visualisationControlObjects.dimensionalChain.forEach(chain => this.GraphicsThree3D.scene.remove(chain));\n      this.GraphicsThree3D.visualisationControlObjects.dimensionalChain = [];\n    }\n\n    chainOnPlane(chain, plane) {\n      let isOnPlane = true;\n      chain.traverse(child => {\n        if (plane.distanceToPoint(child.position) !== 0) isOnPlane = false;\n      });\n      return isOnPlane;\n    }\n    /**\n     * ScaleBar\n     */\n\n\n    generateScaleBarObject(scaleBarData, scaleBarProps, unitsLength, frameData, frame) {\n      let areaProps = scaleBarProps.area;\n      let lineProps = scaleBarProps.line;\n      let textProps = scaleBarProps.text;\n      let normalVector = new this.THREE.Vector3(0, 0, 1);\n      let directionVector = this.utils.initVector3(scaleBarData.directionVector).normalize();\n      let insertionPoint = this.utils.initVector3(scaleBarData.insertionPoint);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      insertionPoint.add(outerPoints[0]);\n      let scale = this.getScale(frameData);\n      let scaleBarSize = {\n        height: scaleBarData.height,\n        segmentWidth: 1\n      };\n      let scaleBarObject = new this.THREE.Object3D();\n      let scaleBarLine = this.drawScaleBarLine(scaleBarSize, lineProps);\n      scaleBarObject.add(scaleBarLine);\n      let scaleBarArea = this.drawScaleBarAreas(scaleBarSize, areaProps);\n      scaleBarObject.add(scaleBarArea);\n      this.drawScaleBarTexts(scaleBarData, scale, unitsLength, textProps, scaleBarObject);\n      scaleBarObject = this.alignScaleBar(scaleBarObject, scaleBarData);\n      this.utils.alignObjectWithVectors(scaleBarObject, normalVector, directionVector);\n      scaleBarObject.position.add(insertionPoint);\n      frame.add(scaleBarObject);\n    }\n\n    alignScaleBar(scaleBarObject, scaleBarData) {\n      let relativePosition = scaleBarData.relativePosition;\n      let alignedScaleBar = new this.THREE.Object3D();\n      alignedScaleBar.add(scaleBarObject);\n      let bbox = new this.THREE.Box3().setFromObject(scaleBarObject);\n      this.GraphicsThree3D.dxfGenerator.drawingScaleBarBBox = bbox.clone();\n      let bboxSize = bbox.getSize(new this.THREE.Vector3());\n      let centerOffset = bbox.getCenter(new this.THREE.Vector3());\n      scaleBarObject.children.forEach(child => child.position.sub(centerOffset));\n      let alignmentOffset = this.getScaleBarAlignmentOffset(bboxSize, relativePosition);\n      scaleBarObject.position.add(alignmentOffset);\n      return alignedScaleBar;\n    }\n\n    getScaleBarAlignmentOffset(bboxSize, relativePosition) {\n      let alignmentOffset = new this.THREE.Vector3(0, 0, 0);\n\n      switch (relativePosition) {\n        case 2:\n          alignmentOffset.set(bboxSize.x / 2, bboxSize.y / 2, 0);\n          break;\n\n        case 3:\n          alignmentOffset.set(bboxSize.x / 2, -bboxSize.y / 2, 0);\n          break;\n\n        case 4:\n          alignmentOffset.set(-bboxSize.x / 2, -bboxSize.y / 2, 0);\n          break;\n\n        case 5:\n          alignmentOffset.set(-bboxSize.x / 2, bboxSize.y / 2, 0);\n          break;\n\n        default:\n          break;\n      }\n\n      return alignmentOffset;\n    }\n\n    drawScaleBarTexts(scaleBarData, scale, unitsLength, textProps, scaleBarObject) {\n      const NUM_OF_SEGMENTS = 5;\n      const SEGMENT_WIDTH = 1;\n      const TEXT_RELATIVE_POSITION = 1;\n      let decimalPlaces = scaleBarData.decimalPlaces;\n      let distance = scaleBarData.distance;\n      let height = scaleBarData.height;\n      let textOffset = scaleBarData.textOffset;\n      let scaleFactor = this.getScaleFactor(unitsLength);\n      let heightOffset = -distance - textProps.size / 2;\n      let textData = {\n        content: \"\",\n        normalVector: {\n          x: 0,\n          y: 0,\n          z: 1\n        },\n        directionVector: {\n          x: 1,\n          y: 0,\n          z: 0\n        },\n        insertionPoint: {\n          x: 0,\n          y: heightOffset,\n          z: 0\n        },\n        relativePosition: TEXT_RELATIVE_POSITION\n      };\n\n      for (let i = 0; i <= NUM_OF_SEGMENTS; i++) {\n        textData.content = (i * scale * scaleFactor).toFixed(decimalPlaces);\n        if (i === 5 && unitsLength) textData.content += ` ${unitsLength}`;\n\n        if (textOffset) {\n          if (i % 2 === 1) textData.insertionPoint.y = height - heightOffset;else textData.insertionPoint.y = heightOffset;\n        }\n\n        textData.insertionPoint.x = i * SEGMENT_WIDTH;\n        let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n        let text = this.GraphicsThree3D.TextLoader.drawText(textData, textProps, font);\n        scaleBarObject.add(text);\n      }\n    }\n\n    getScaleFactor(unit) {\n      let factor = 1;\n\n      switch (unit) {\n        case \"mm\":\n          factor *= 10;\n          break;\n\n        case \"m\":\n          factor /= 100;\n          break;\n\n        case \"km\":\n          factor /= 100000;\n          break;\n\n        default:\n          break;\n      }\n\n      return factor;\n    }\n\n    drawScaleBarAreas(scaleBarSize, areaProps) {\n      const NUM_OF_SEGMENTS = 5;\n      let height = scaleBarSize.height;\n      let segmentWidth = scaleBarSize.segmentWidth;\n      let totalWidth = NUM_OF_SEGMENTS * segmentWidth;\n      let geoOne = new this.THREE.PlaneGeometry(segmentWidth, height);\n      let geoTwo = geoOne.clone();\n      geoOne.translate(-segmentWidth, 0, 0);\n      geoTwo.translate(segmentWidth, 0, 0);\n      geoOne.merge(geoTwo);\n      geoOne.translate(totalWidth / 2, height / 2, 0);\n      let mat = this.utils.getMaterialFromColorProps(areaProps.color);\n      let area = new this.THREE.Mesh(geoOne, mat);\n      return area;\n    }\n\n    drawScaleBarLine(scaleBarSize, lineProps) {\n      const NUM_OF_SEGMENTS = 5;\n      let height = scaleBarSize.height;\n      let segmentWidth = scaleBarSize.segmentWidth;\n      let totalWidth = NUM_OF_SEGMENTS * segmentWidth;\n      let pts = [new this.THREE.Vector3(0, 0, 0), new this.THREE.Vector3(totalWidth, 0, 0), new this.THREE.Vector3(totalWidth, height), new this.THREE.Vector3(0, height, 0)];\n      let line = this.drawOutline(pts, lineProps, true);\n      this.addPointsToVertices(lineProps.thickness, lineProps.color, pts, line);\n      return line;\n    }\n    /**\n     * UnitTextBlock\n     */\n\n\n    drawUnitTextBlock(unitTextBlockData, unitTextBlockProps, units, frameData, frame) {\n      let font = this.GraphicsThree3D.TextLoader.loadFont(unitTextBlockProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      let content = this.generateUnitTextContent(unitTextBlockData, units);\n      let data = this.generateTextData(content, unitTextBlockData, zeroPoint);\n      let text = this.GraphicsThree3D.TextLoader.drawText(data, unitTextBlockProps, font);\n      frame.add(text);\n    }\n\n    generateUnitTextContent(unitTextBlockData, units) {\n      let content = \"\";\n      let startText = unitTextBlockData.startText;\n      let endText = unitTextBlockData.endText;\n      let separator = unitTextBlockData.separator;\n      content += startText;\n      let unitValues = Object.values(units);\n      unitValues.forEach((value, index) => {\n        if (index && value) content += separator;\n        if (value) content += value;\n      });\n      content += endText;\n      return content;\n    }\n    /**\n     * ScaleTextBlock\n     */\n\n\n    drawScaleTextBlock(scaleTextBlockData, scaleTextBlockProps, frameData, frame) {\n      let font = this.GraphicsThree3D.TextLoader.loadFont(scaleTextBlockProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      let decimals = scaleTextBlockData.decimalPlaces;\n      let scale = this.getScale(frameData);\n      let content = `${scaleTextBlockData.text} ${scale.toFixed(decimals)}`;\n      let data = this.generateTextData(content, scaleTextBlockData, zeroPoint);\n      let text = this.GraphicsThree3D.TextLoader.drawText(data, scaleTextBlockProps, font);\n      frame.add(text);\n    }\n    /**\n     * FreeTexts\n     */\n\n\n    drawFreeTexts(freeTexts, textProps, frameData, frame) {\n      let font = this.GraphicsThree3D.TextLoader.loadFont(textProps);\n      let [outerPoints, _] = this.getFramePoints(frameData);\n      let zeroPoint = outerPoints[0];\n      freeTexts.forEach(textData => {\n        let content = textData.content;\n        let data = this.generateTextData(content, textData, zeroPoint);\n        let text = this.GraphicsThree3D.TextLoader.drawText(data, textProps, font);\n        frame.add(text);\n      });\n    }\n\n    generateTextData(content, textData, zeroPoint) {\n      let id = textData.id;\n      let relativePosition = textData.relativePosition;\n      let directionVector = this.utils.initVector3(textData.directionVector).normalize();\n      let insertionPoint = this.utils.initVector3(textData.insertionPoint);\n      insertionPoint.add(zeroPoint);\n      let data = {\n        content,\n        id,\n        relativePosition,\n        directionVector: {\n          x: directionVector.x,\n          y: directionVector.y,\n          z: directionVector.z\n        },\n        normalVector: {\n          x: 0,\n          y: 0,\n          z: 1\n        },\n        insertionPoint: {\n          x: insertionPoint.x,\n          y: insertionPoint.y,\n          z: insertionPoint.z\n        }\n      };\n      return data;\n    }\n    /**\n     * Frame\n     */\n\n\n    makeFrame(frameData, frameProps, drawingData) {\n      let normalVector = this.utils.initVector3(drawingData.normalVector).normalize().negate();\n      let verticalVector = this.utils.initVector3(drawingData.verticalVector).normalize().negate();\n      let frame = this.generateFrameObject(frameData, frameProps);\n      this.scaleFrame(frame, frameData);\n      this.utils.alignObjectWithVectors(frame, normalVector, new this.THREE.Vector3().crossVectors(normalVector, verticalVector));\n      frame.position.copy(this.getFramePosition(drawingData, this.GraphicsThree3D));\n      this.GraphicsThree3D.scene.add(frame);\n      return frame;\n    }\n\n    getScaleToFitScene(frameData, GraphicsThree3D) {\n      let normalVec = this.utils.initVector3(frameData.drawingNormal);\n      let upVec = this.utils.initVector3(frameData.drawingVerticalVector);\n      let crossVec = new this.THREE.Vector3().crossVectors(normalVec, upVec);\n      let bboxPoints = [new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ)];\n      let projectedPoints = this.projectPointsOnPlane(bboxPoints, normalVec);\n      let [verticalPoints, horizontalPoints] = this.getVerticalAndHorizontalPoints(projectedPoints, upVec, crossVec);\n      let [maxWidth, maxHeight] = this.getMaxWidthHeightProjected(verticalPoints, horizontalPoints, upVec, crossVec);\n      let frameWidth = frameData.width - frameData.border.left - frameData.border.right;\n      let frameHeight = frameData.height - frameData.border.top - frameData.border.bottom; // let scale;\n      // if (frameWidth < frameHeight) scale = maxWidth / frameWidth;\n      // else scale = maxHeight / frameHeight;\n      // let scale = maxWidth / frameWidth;\n\n      let scale = Math.max(maxWidth / frameWidth, maxHeight / frameHeight);\n      return scale;\n    }\n\n    getMaxWidthHeightProjected(verticalPoints, horizontalPoints, upVec, crossVec) {\n      let maxHeight = 0;\n      let maxWidth = 0;\n      const EPSILON = 0.0001;\n      verticalPoints.up.forEach(upPoint => verticalPoints.down.forEach(downPoint => {\n        let distance = upPoint.distanceTo(downPoint);\n        let dirVec = downPoint.clone().sub(upPoint).normalize();\n        if (distance > maxHeight && Math.abs(dirVec.dot(upVec)) > 1 - EPSILON) maxHeight = distance;\n      }));\n      horizontalPoints.right.forEach(rightPoint => horizontalPoints.left.forEach(leftPoint => {\n        let distance = rightPoint.distanceTo(leftPoint);\n        let dirVec = rightPoint.clone().sub(leftPoint).normalize();\n        if (distance > maxWidth && Math.abs(dirVec.dot(crossVec)) > 1 - EPSILON) maxWidth = distance;\n      }));\n      return [maxWidth, maxHeight];\n    }\n\n    projectPointsOnPlane(pointsArr, normalVec) {\n      let projectedPoints = [];\n      pointsArr.forEach(point => {\n        projectedPoints.push(point.projectOnPlane(normalVec));\n      });\n      return projectedPoints;\n    }\n\n    getVerticalAndHorizontalPoints(pointsArr, upVec, crossVec) {\n      let verticalPoints = {\n        up: [],\n        down: []\n      };\n      let horizontalPoints = {\n        right: [],\n        left: []\n      };\n      pointsArr.forEach(point => {\n        if (point.dot(upVec) > 0) verticalPoints.up.push(point);else verticalPoints.down.push(point);\n        if (point.dot(crossVec) > 0) horizontalPoints.right.push(point);else horizontalPoints.left.push(point);\n      });\n      return [verticalPoints, horizontalPoints];\n    }\n\n    getScale(frameData) {\n      let scaleType = frameData.scaleType;\n      let scale = 1;\n\n      if (scaleType === 1) {\n        scale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n      } else if (scaleType === 2) {\n        let scaleList = frameData.scaleList;\n        let minimumScale = this.getScaleToFitScene(frameData, this.GraphicsThree3D);\n        let maximumZoom = Infinity;\n        scaleList.forEach(scaleStr => {\n          let scaleParams = scaleStr.split(\":\");\n          let newScale = parseFloat(scaleParams[1]) / parseFloat(scaleParams[0]);\n\n          if (newScale >= minimumScale && scale < maximumZoom) {\n            scale = newScale;\n            maximumZoom = scale;\n          }\n        });\n      }\n\n      return scale;\n    }\n\n    scaleFrame(frame, frameData) {\n      let scale = this.getScale(frameData);\n      frame.scale.set(scale, scale, scale);\n    }\n\n    getFramePosition(drawingData, GraphicsThree3D) {\n      let normalVector = this.utils.initVector3(drawingData.normalVector).normalize();\n      let framePos;\n\n      if (drawingData.positionVector) {\n        let positionVec = this.utils.initVector3(drawingData.positionVector);\n        let objMidVec = GraphicsThree3D.objectMidVector.clone();\n        let posDir = positionVec.clone().normalize();\n        let posDist = positionVec.length();\n        let proj = objMidVec.dot(posDir);\n\n        if (proj > posDist) {\n          let delta = proj - posDist;\n          let proportion = delta / proj;\n          framePos = objMidVec.sub(normalVector.clone().multiplyScalar(proportion));\n        } else {\n          let positionDirection = positionVec.clone().normalize();\n          let planeDistance = positionVec.length();\n          if (normalVector.dot(positionDirection) > 0) planeDistance *= -1;\n          let viewPlane = new this.THREE.Plane(normalVector, planeDistance);\n          let projectedPoint = new this.THREE.Vector3();\n          viewPlane.projectPoint(objMidVec, projectedPoint);\n          framePos = projectedPoint;\n        }\n      } else {\n        let midVec = GraphicsThree3D.objectMidVector.clone();\n        let bboxPoints = [new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMaxZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMinY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMaxX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ), new this.THREE.Vector3(GraphicsThree3D.objectMinX, GraphicsThree3D.objectMaxY, GraphicsThree3D.objectMinZ)].map(point => point.sub(midVec));\n        let viewPlane = new this.THREE.Plane(normalVector);\n        let minDistance = 0;\n        bboxPoints.forEach(point => {\n          let distance = viewPlane.distanceToPoint(point);\n          if (distance < minDistance) minDistance = distance;\n        });\n        framePos = midVec.add(normalVector.clone().multiplyScalar(minDistance));\n      }\n\n      return framePos;\n    }\n\n    generateFrameObject(frameData, frameProps) {\n      let innerLineProps = frameProps.innerLine;\n      let outerLineProps = frameProps.outerLine;\n      let areaProps = frameProps.area;\n      let [outerPoints, innerPoints] = this.getFramePoints(frameData);\n      let frameObject = new this.THREE.Object3D();\n      let plane = this.drawPlane(frameData, areaProps);\n      frameObject.add(plane);\n      let outerLine = this.drawOutline(outerPoints, outerLineProps, true);\n      frameObject.add(outerLine);\n      let innerLine = this.drawOutline(innerPoints, innerLineProps, true);\n      frameObject.add(innerLine);\n      this.drawFrameVertexPoints(outerPoints, innerPoints, frameProps, frameObject);\n      return frameObject;\n    }\n\n    drawOutline(points, lineProps) {\n      let firstLine = new this.THREE.Mesh();\n\n      if (lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined)) {\n        if (lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0) {\n          let lineMeshArray = [];\n\n          for (let i = 0; i < points.length - 1; i++) {\n            if (points[i].distanceTo(points[i + 1]) > 0) {\n              let lineMesh = this.utils.drawLine(points[i], points[i + 1], lineProps, true);\n              lineMeshArray.push(lineMesh);\n            }\n          }\n\n          if (points[points.length - 1].distanceTo(points[0]) > 0) {\n            let lineMesh = this.utils.drawLine(points[points.length - 1], points[0], lineProps, true);\n            lineMeshArray.push(lineMesh);\n          }\n\n          firstLine = lineMeshArray.length > 0 ? lineMeshArray.splice(0, 1)[0] : new this.THREE.Mesh();\n          let lineMaterial = firstLine.material;\n          firstLine.updateMatrixWorld();\n          firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n          for (let i = 0; i < lineMeshArray.length; i++) {\n            lineMeshArray[i].updateMatrixWorld();\n            lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n            firstLine.geometry.merge(lineMeshArray[i].geometry);\n          }\n\n          firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n        }\n      }\n\n      return firstLine;\n    }\n\n    getFramePoints(frameData) {\n      let width = frameData.width;\n      let height = frameData.height;\n      let borderLeft = frameData.border.left;\n      let borderRight = frameData.border.right;\n      let borderTop = frameData.border.top;\n      let borderBot = frameData.border.bottom;\n      let outerPoints = [new this.THREE.Vector3(-width / 2, -height / 2, 0), new this.THREE.Vector3(width / 2, -height / 2, 0), new this.THREE.Vector3(width / 2, height / 2, 0), new this.THREE.Vector3(-width / 2, height / 2, 0)];\n      let innerPoints = [new this.THREE.Vector3(-width / 2 + borderLeft, -height / 2 + borderBot, 0), new this.THREE.Vector3(width / 2 - borderRight, -height / 2 + borderBot, 0), new this.THREE.Vector3(width / 2 - borderRight, height / 2 - borderTop, 0), new this.THREE.Vector3(-width / 2 + borderLeft, height / 2 - borderTop, 0)];\n      return [outerPoints, innerPoints];\n    }\n\n    drawFrameVertexPoints(outerPoints, innerPoints, frameProps, frameObject) {\n      let outerColor = frameProps.outerLine.color;\n      let innerColor = frameProps.innerLine.color;\n      let outerSize = frameProps.outerLine.thickness;\n      let innerSize = frameProps.innerLine.thickness;\n      if (outerSize) this.addPointsToVertices(outerSize, outerColor, outerPoints, frameObject);\n      if (innerSize) this.addPointsToVertices(innerSize, innerColor, innerPoints, frameObject);\n    }\n\n    addPointsToVertices(size, color, points, frameObject) {\n      let material = this.utils.getMaterialFromColorProps(color);\n      points.forEach(point => {\n        let mesh = this.drawPoint(point, size, material);\n        frameObject.add(mesh);\n      });\n    }\n\n    drawPoint(position, size, material) {\n      let geometry = new this.THREE.BoxBufferGeometry(size, size, size);\n      let mesh = new this.THREE.Mesh(geometry, material);\n      mesh.position.copy(position);\n      return mesh;\n    }\n\n    drawPlane(frameData, areaProps) {\n      let width = frameData.width;\n      let height = frameData.height;\n      let planeWidth = width;\n      let planeHeight = height;\n      let planeGeo = new this.THREE.PlaneBufferGeometry(planeWidth, planeHeight);\n      let planeMat = this.utils.getMaterialFromColorProps(areaProps.color);\n      planeMat.depthWrite = false;\n      let plane = new this.THREE.Mesh(planeGeo, planeMat);\n      return plane;\n    }\n    /**\n     * Drawing\n     */\n\n\n    makeDrawing(drawingData, drawingProps) {\n      if (drawingData.type === 1 || drawingData.type === 2) {\n        let normalVector = this.utils.initVector3(drawingData.normalVector).normalize();\n        let verticalVector = this.utils.initVector3(drawingData.verticalVector).normalize();\n        let scene = this.GraphicsThree3D.scene;\n        this.positionCamera(normalVector.clone().negate(), verticalVector);\n\n        if (drawingData.type === 2) {\n          let planeDistance = this.getClippingPlaneDistance(drawingData, normalVector);\n          let clippingPlane = new this.THREE.Plane(normalVector, planeDistance);\n          this.clipSceneObjects(scene, clippingPlane);\n        }\n\n        this.changeObjectsTransparency(this.GraphicsThree3D.transparencyControlObjects, drawingProps.transparency);\n        this.changeObjectsVisualisation(this.GraphicsThree3D.visualisationControlObjects, drawingProps.visualisation);\n      }\n    }\n\n    positionCamera(normalVector, verticalVector) {\n      let camera = this.GraphicsThree3D.camera;\n      let midVector = this.GraphicsThree3D.objectMidVector.clone();\n      let cameraDistance = 150;\n      let cameraPos = normalVector.clone().multiplyScalar(cameraDistance).add(midVector);\n      let upGenObj = new this.THREE.Object3D();\n      let upVector = this.utils.getMeshUpVector(upGenObj, normalVector.clone());\n      let sceneRotAngle = upVector.angleTo(verticalVector);\n      camera.position.set(0, 0, 0);\n      camera.lookAt(new this.THREE.Vector3(0, 0, 1));\n      camera.rotation.z = sceneRotAngle;\n      camera.up = verticalVector;\n      camera.position.copy(cameraPos);\n      camera.lookAt(midVector);\n      camera.zoom = 1;\n      camera.updateProjectionMatrix();\n    }\n\n    getClippingPlaneDistance(drawingData, normalVector) {\n      let positionVector = this.utils.initVector3(drawingData.positionVector);\n      let positionDirection = positionVector.clone().normalize();\n      let planeDistance = positionVector.length();\n      if (normalVector.dot(positionDirection) > 0) planeDistance *= -1;\n      return planeDistance;\n    }\n\n    changeObjectsTransparency(objects, transparencyProps) {\n      for (let [key, value] of Object.entries(objects)) {\n        value.forEach(mesh => {\n          let material = mesh.material;\n          let transparency = transparencyProps[key] / 255.0;\n          if (material.length) material.forEach(mat => mat.opacity = transparency);else material.opacity = transparency;\n        });\n      }\n    }\n\n    changeObjectsVisualisation(objects, visualisationProps) {\n      for (let [key, value] of Object.entries(objects)) {\n        value.forEach(object => {\n          let visualisation = visualisationProps[key];\n          object.visible = visualisation;\n        });\n      }\n    }\n\n    clipSceneObjects(scene, clippingPlane) {\n      let objectIDs = {};\n      scene.traverse(object => {\n        if (object.type === \"Mesh\" || object.type === \"LineLoop\") {\n          let material = object.material;\n          this.clipMaterial(material, clippingPlane);\n          if (!object.isDrawingObject) this.clipMaterialBack(material, clippingPlane);\n          if (object.type === \"Mesh\") this.cutMesh(object, objectIDs, clippingPlane);\n        }\n      });\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    clipMaterialBack(material, plane) {\n      const OFFSET = 1;\n      let backPlane = plane.clone();\n      backPlane.normal.negate();\n      backPlane.constant = -backPlane.constant + OFFSET;\n      material.clippingPlanes.push(backPlane);\n    }\n\n    cutMesh(mesh, ids, plane) {\n      if (mesh.isDrawingObject) {\n        const ERASER_OFFSET = 1;\n        let eraserSize = 500;\n\n        if (!(mesh.uuid in ids)) {\n          ids[mesh.uuid] = 1;\n          let eraserPos = plane.normal.clone().multiplyScalar(-plane.constant + eraserSize / 2 + ERASER_OFFSET);\n          let eraserMesh = this.utils.makeTestMesh(eraserPos, eraserSize);\n          eraserMesh.updateMatrix();\n          let meshCSG = this.GraphicsThree3D.CSG.fromMesh(mesh);\n          let eraserCSG = this.GraphicsThree3D.CSG.fromMesh(eraserMesh);\n          let subCSG = meshCSG.subtract(eraserCSG);\n          mesh.geometry = this.GraphicsThree3D.CSG.toMesh(subCSG, new this.THREE.Matrix4()).geometry;\n        }\n      }\n    }\n\n    getStencilMaterials() {\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n      let backFaceStencilMat = new this.THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = this.THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = this.THREE.BackSide;\n      backFaceStencilMat.stencilFail = this.THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = this.THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = this.THREE.IncrementWrapStencilOp; // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      let frontFaceStencilMat = new this.THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = this.THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = this.THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = this.THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = this.THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = this.THREE.DecrementWrapStencilOp; // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      let planeStencilMat = new this.THREE.MeshNormalMaterial();\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = this.THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = this.THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = this.THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = this.THREE.ReplaceStencilOp;\n      return [backFaceStencilMat, frontFaceStencilMat, planeStencilMat];\n    }\n\n  }\n\n  _exports.DrawingPlaneLoader = DrawingPlaneLoader;\n\n  function zoomChainComponents(chainZoomState, chain, drawingPlane) {\n    let initialFactor = chainZoomState.initialFactors[drawingPlane.id];\n    let maxFactor = chainZoomState.finalFactorMax;\n    let multiplier = initialFactor / maxFactor;\n    let storedTextSize = chainZoomState.initialTextSizes[chain.id];\n    let storedBaseLineOverhangSize = chainZoomState.initialComponentSizes[chain.id].baseLineOverhang;\n    let storedEndLineLengthSize = chainZoomState.initialComponentSizes[chain.id].endLine.lenght;\n    let storedEndLineOverhangSize = chainZoomState.initialComponentSizes[chain.id].endLine.overhang;\n    let storedEndSymbolSize = chainZoomState.initialComponentSizes[chain.id].endSymbol;\n    chain.properties.text.size = storedTextSize * multiplier;\n    chain.components.baseLineOverhang.length = storedBaseLineOverhangSize * multiplier;\n    chain.components.endLine.lenght = storedEndLineLengthSize * multiplier;\n    chain.components.endLine.overhang = storedEndLineOverhangSize * multiplier;\n    chain.components.endSymbol.size = storedEndSymbolSize * multiplier;\n  }\n\n  function saveChainZoomState(GraphicsThree3D, chain) {\n    GraphicsThree3D.drawingPlaneZoomStateDimChain.initialTextSizes[chain.id] = chain.properties.text.size;\n    GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id] = {};\n    if (chain.components.baseLineOverhang) GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id].baseLineOverhang = chain.components.baseLineOverhang.length;\n\n    if (chain.components.endLine) {\n      GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id].endLine = {};\n      GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id].endLine.lenght = chain.components.endLine.lenght;\n      GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id].endLine.overhang = chain.components.endLine.overhang;\n    }\n\n    if (chain.components.endSymbol) {\n      GraphicsThree3D.drawingPlaneZoomStateDimChain.initialComponentSizes[chain.id].endSymbol = chain.components.endSymbol.size;\n    }\n  }\n});","define(\"m08-2020/lib/FileGeneration/dxf_colors\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.COLOR_LOOKUP = void 0;\n  const COLOR_LOOKUP = [[0, 0, 0], [255, 0, 0], [255, 255, 0], [0, 255, 0], [0, 255, 255], [0, 0, 255], [255, 0, 255], [255, 255, 255], [128, 128, 128], [192, 192, 192], [255, 0, 0], [255, 127, 127], [204, 0, 0], [204, 102, 102], [153, 0, 0], [153, 76, 76], [127, 0, 0], [127, 63, 63], [76, 0, 0], [76, 38, 38], [255, 63, 0], [255, 159, 127], [204, 51, 0], [204, 127, 102], [153, 38, 0], [153, 95, 76], [127, 31, 0], [127, 79, 63], [76, 19, 0], [76, 47, 38], [255, 127, 0], [255, 191, 127], [204, 102, 0], [204, 153, 102], [153, 76, 0], [153, 114, 76], [127, 63, 0], [127, 95, 63], [76, 38, 0], [76, 57, 38], [255, 191, 0], [255, 223, 127], [204, 153, 0], [204, 178, 102], [153, 114, 0], [153, 133, 76], [127, 95, 0], [127, 111, 63], [76, 57, 0], [76, 66, 38], [255, 255, 0], [255, 255, 127], [204, 204, 0], [204, 204, 102], [153, 153, 0], [153, 153, 76], [127, 127, 0], [127, 127, 63], [76, 76, 0], [76, 76, 38], [191, 255, 0], [223, 255, 127], [153, 204, 0], [178, 204, 102], [114, 153, 0], [133, 153, 76], [95, 127, 0], [111, 127, 63], [57, 76, 0], [66, 76, 38], [127, 255, 0], [191, 255, 127], [102, 204, 0], [153, 204, 102], [76, 153, 0], [114, 153, 76], [63, 127, 0], [95, 127, 63], [38, 76, 0], [57, 76, 38], [63, 255, 0], [159, 255, 127], [51, 204, 0], [127, 204, 102], [38, 153, 0], [95, 153, 76], [31, 127, 0], [79, 127, 63], [19, 76, 0], [47, 76, 38], [0, 255, 0], [127, 255, 127], [0, 204, 0], [102, 204, 102], [0, 153, 0], [76, 153, 76], [0, 127, 0], [63, 127, 63], [0, 76, 0], [38, 76, 38], [0, 255, 63], [127, 255, 159], [0, 204, 51], [102, 204, 127], [0, 153, 38], [76, 153, 95], [0, 127, 31], [63, 127, 79], [0, 76, 19], [38, 76, 47], [0, 255, 127], [127, 255, 191], [0, 204, 102], [102, 204, 153], [0, 153, 76], [76, 153, 114], [0, 127, 63], [63, 127, 95], [0, 76, 38], [38, 76, 57], [0, 255, 191], [127, 255, 223], [0, 204, 153], [102, 204, 178], [0, 153, 114], [76, 153, 133], [0, 127, 95], [63, 127, 111], [0, 76, 57], [38, 76, 66], [0, 255, 255], [127, 255, 255], [0, 204, 204], [102, 204, 204], [0, 153, 153], [76, 153, 153], [0, 127, 127], [63, 127, 127], [0, 76, 76], [38, 76, 76], [0, 191, 255], [127, 223, 255], [0, 153, 204], [102, 178, 204], [0, 114, 153], [76, 133, 153], [0, 95, 127], [63, 111, 127], [0, 57, 76], [38, 66, 76], [0, 127, 255], [127, 191, 255], [0, 102, 204], [102, 153, 204], [0, 76, 153], [76, 114, 153], [0, 63, 127], [63, 95, 127], [0, 38, 76], [38, 57, 76], [0, 63, 255], [127, 159, 255], [0, 51, 204], [102, 127, 204], [0, 38, 153], [76, 95, 153], [0, 31, 127], [63, 79, 127], [0, 19, 76], [38, 47, 76], [0, 0, 255], [127, 127, 255], [0, 0, 204], [102, 102, 204], [0, 0, 153], [76, 76, 153], [0, 0, 127], [63, 63, 127], [0, 0, 76], [38, 38, 76], [63, 0, 255], [159, 127, 255], [51, 0, 204], [127, 102, 204], [38, 0, 153], [95, 76, 153], [31, 0, 127], [79, 63, 127], [19, 0, 76], [47, 38, 76], [127, 0, 255], [191, 127, 255], [102, 0, 204], [153, 102, 204], [76, 0, 153], [114, 76, 153], [63, 0, 127], [95, 63, 127], [38, 0, 76], [57, 38, 76], [191, 0, 255], [223, 127, 255], [153, 0, 204], [178, 102, 204], [114, 0, 153], [133, 76, 153], [95, 0, 127], [111, 63, 127], [57, 0, 76], [66, 38, 76], [255, 0, 255], [255, 127, 255], [204, 0, 204], [204, 102, 204], [153, 0, 153], [153, 76, 153], [127, 0, 127], [127, 63, 127], [76, 0, 76], [76, 38, 76], [255, 0, 191], [255, 127, 223], [204, 0, 153], [204, 102, 178], [153, 0, 114], [153, 76, 133], [127, 0, 95], [127, 63, 111], [76, 0, 57], [76, 38, 66], [255, 0, 127], [255, 127, 191], [204, 0, 102], [204, 102, 153], [153, 0, 76], [153, 76, 114], [127, 0, 63], [127, 63, 95], [76, 0, 38], [76, 38, 57], [255, 0, 63], [255, 127, 159], [204, 0, 51], [204, 102, 127], [153, 0, 38], [153, 76, 95], [127, 0, 31], [127, 63, 79], [76, 0, 19], [76, 38, 47], [51, 51, 51], [91, 91, 91], [132, 132, 132], [173, 173, 173], [214, 214, 214], [255, 255, 255]];\n  _exports.COLOR_LOOKUP = COLOR_LOOKUP;\n});","define(\"m08-2020/lib/OutlinesGeometry\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.OutlinesGeometry = OutlinesGeometry;\n\n  function OutlinesGeometry(geometry, thresholdAngle) {\n    /**\n     * https://github.com/mrdoob/three.js/issues/10517\n     */\n    // THREE.BufferGeometry.call( this );\n    THREE.Geometry.call(this);\n    thresholdAngle = thresholdAngle !== undefined ? thresholdAngle : 1;\n    var thresholdDot = Math.cos(Math.PI / 180 * thresholdAngle);\n    var edge = [0, 0],\n        hash = {},\n        i,\n        j,\n        l,\n        face,\n        key;\n\n    function sortFunction(a, b) {\n      return a - b;\n    }\n\n    var keys = ['a', 'b', 'c'];\n    var geometry2;\n\n    if (geometry.isBufferGeometry) {\n      geometry2 = new THREE.Geometry();\n      geometry2.fromBufferGeometry(geometry);\n    } else {\n      geometry2 = geometry.clone();\n    }\n\n    geometry2.mergeVertices();\n    geometry2.computeFaceNormals();\n    var vertices = geometry2.vertices;\n    var faces = geometry2.faces;\n\n    for (i = 0; i < faces.length; i++) {\n      face = faces[i];\n\n      for (j = 0; j < 3; j++) {\n        edge[0] = face[keys[j]];\n        edge[1] = face[keys[(j + 1) % 3]];\n        var line = new THREE.Line3(vertices[edge[0]], vertices[edge[1]]); // for each vertex checks if it lies in the edge\n\n        for (var e = vertices.length - 1; e >= 0; e--) {\n          if (e === edge[0] || e === edge[1]) continue;\n          var v = vertices[e];\n          var closestPoint = line.closestPointToPoint(v, true, new THREE.Vector3());\n\n          if (new THREE.Line3(closestPoint, v).distance() < 1e-5) {\n            //1e-5\n            // mark the current face as splitted so that his cords won't be considered\n            face.splitted = true; // Add two new faces, created splitting the face in two\n\n            faces.push(new THREE.Face3(e, face[keys[(j + 2) % 3]], face[keys[j % 3]], face.normal, face.color, face.materialIndex));\n            faces.push(new THREE.Face3(e, face[keys[(j + 2) % 3]], face[keys[(j + 1) % 3]], face.normal, face.color, face.materialIndex));\n            break;\n          }\n        }\n\n        if (face.splitted) break;\n      }\n    }\n\n    for (i = faces.length - 1; i >= 0; i--) {\n      face = faces[i];\n      if (face.splitted) continue;\n\n      for (j = 0; j < 3; j++) {\n        edge[0] = face[keys[j]];\n        edge[1] = face[keys[(j + 1) % 3]];\n        edge.sort(sortFunction);\n        key = edge.toString();\n\n        if (hash[key] === undefined) {\n          hash[key] = {\n            vert1: edge[0],\n            vert2: edge[1],\n            face1: i,\n            face2: undefined\n          };\n        } else {\n          hash[key].face2 = i;\n        }\n      }\n    }\n\n    var coords = [];\n\n    for (key in hash) {\n      var h = hash[key]; // An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.\n\n      if (h.face2 !== undefined && faces[h.face1].normal.dot(faces[h.face2].normal) <= thresholdDot) {\n        var vertex = vertices[h.vert1];\n        coords.push(vertex.x);\n        coords.push(vertex.y);\n        coords.push(vertex.z);\n        vertex = vertices[h.vert2];\n        coords.push(vertex.x);\n        coords.push(vertex.y);\n        coords.push(vertex.z);\n      }\n    } // this.setAttribute( 'position', new THREE.Float32BufferAttribute( coords, 3 ) );\n    ///\n\n\n    this.vertices = vertices;\n    this.faces = faces;\n  }\n\n  OutlinesGeometry.prototype = Object.create(THREE.BufferGeometry.prototype);\n  OutlinesGeometry.prototype.constructor = OutlinesGeometry;\n});","define(\"m08-2020/lib/SectionalPlane\", [\"exports\", \"three\", \"m08-2020/lib/Utils\"], function (_exports, THREE, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.SectionalPlane = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class SectionalPlane {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.clipObjects = [];\n      this.renderOrder = 1;\n    }\n\n    applySectionalPlanes(sectionalPlanes, properties) {\n      let intersectionLineProps = properties.intersectionLine;\n      let intersectionAreaProps = properties.intersectionArea; // TODO: Implement sectionalPlane properties for lines and hatching\n\n      sectionalPlanes.forEach(sectionalPlane => this.applySectionalPlane(sectionalPlane, intersectionAreaProps ? intersectionAreaProps : intersectionLineProps));\n    }\n\n    applySectionalPlane(sectionalPlaneData, intersectionAreaProps) {\n      let positionVector = utils.initVector3(sectionalPlaneData.positionVector);\n      let normalVector = utils.initVector3(sectionalPlaneData.normalVector).negate();\n      let planeDistance = positionVector.clone().multiply(normalVector).length();\n      let distanceSign = positionVector.normalize().dot(normalVector) > 0 ? -1 : 1;\n      planeDistance *= distanceSign;\n      let clippingPlane = new THREE.Plane(normalVector, planeDistance);\n      this.clipObjects.forEach(child => {\n        if (child.type == \"Mesh\" || child.type == \"Line\" || child.type == \"LineSegments\") {\n          this.clipMesh(clippingPlane, child, intersectionAreaProps);\n        }\n      });\n    }\n\n    clipMesh(clippingPlane, mesh, intersectionAreaProps) {\n      if (intersectionAreaProps) {\n        let [frontFaceStencilMat, backFaceStencilMat, planeStencilMat] = this.getClippingMaterials(intersectionAreaProps);\n        frontFaceStencilMat.clippingPlanes = [clippingPlane];\n        backFaceStencilMat.clippingPlanes = [clippingPlane];\n        let meshGeo = mesh.geometry.clone();\n        let frontMesh = new THREE.Mesh(meshGeo, frontFaceStencilMat);\n        frontMesh.applyMatrix4(mesh.matrix);\n        this.GraphicsThree3D.scene.add(frontMesh);\n        let backMesh = new THREE.Mesh(meshGeo, backFaceStencilMat);\n        backMesh.applyMatrix4(mesh.matrix);\n        this.GraphicsThree3D.scene.add(backMesh); ///\n\n        frontMesh.renderOrder = this.renderOrder;\n        frontMesh.renderOrder = this.renderOrder;\n        this.renderOrder++; ///\n\n        let planeNormal = clippingPlane.normal.clone();\n        let forwardVector = new THREE.Vector3(0, 0, -1);\n        let planeGeom = new THREE.PlaneBufferGeometry();\n        let planeMesh = new THREE.Mesh(planeGeom, planeStencilMat);\n        planeMesh.scale.setScalar(1000);\n        clippingPlane.coplanarPoint(planeMesh.position);\n        planeMesh.quaternion.setFromUnitVectors(forwardVector, planeNormal);\n        planeMesh.renderOrder = 1;\n        planeMesh.position.copy(mesh.position);\n        frontMesh.position.copy(mesh.position);\n        backMesh.position.copy(mesh.position);\n\n        planeMesh.onBeforeRender = function (renderer) {\n          renderer.clearDepth();\n        }; ///\n\n\n        planeMesh.onAfterRender = function (renderer) {\n          renderer.clearStencil();\n        };\n\n        planeMesh.renderOrder = this.renderOrder + 1.1; ///\n\n        this.GraphicsThree3D.scene.add(planeMesh);\n      } ///\n\n\n      if (mesh.children) mesh.children.forEach(child => this.clipMaterial(child.material, clippingPlane));\n      this.clipMaterial(mesh.material, clippingPlane);\n    }\n\n    clipMaterial(material, plane) {\n      if (material.length) material.forEach(mat => this.addClippingPlane(mat, plane));else this.addClippingPlane(material, plane);\n    }\n\n    addClippingPlane(material, plane) {\n      if (material.clippingPlanes) material.clippingPlanes.push(plane);else material.clippingPlanes = [plane];\n    }\n\n    getClippingMaterials(materialProps) {\n      let frontFaceStencilMat, backFaceStencilMat, planeStencilMat; //https://discourse.threejs.org/t/capping-clipped-planes-using-stencil-on-a-buffergeometry/18407/18\n      // PASS 1\n      // everywhere that the back faces are visible (clipped region) the stencil\n      // buffer is incremented by 1.\n\n      backFaceStencilMat = new THREE.MeshBasicMaterial();\n      backFaceStencilMat.depthWrite = false;\n      backFaceStencilMat.depthTest = false;\n      backFaceStencilMat.colorWrite = false;\n      backFaceStencilMat.stencilWrite = true;\n      backFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      backFaceStencilMat.side = THREE.BackSide;\n      backFaceStencilMat.stencilFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZFail = THREE.IncrementWrapStencilOp;\n      backFaceStencilMat.stencilZPass = THREE.IncrementWrapStencilOp; // PASS 2\n      // everywhere that the front faces are visible the stencil\n      // buffer is decremented back to 0.\n\n      frontFaceStencilMat = new THREE.MeshBasicMaterial();\n      frontFaceStencilMat.depthWrite = false;\n      frontFaceStencilMat.depthTest = false;\n      frontFaceStencilMat.colorWrite = false;\n      frontFaceStencilMat.stencilWrite = true;\n      frontFaceStencilMat.stencilFunc = THREE.AlwaysStencilFunc;\n      frontFaceStencilMat.side = THREE.FrontSide;\n      frontFaceStencilMat.stencilFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZFail = THREE.DecrementWrapStencilOp;\n      frontFaceStencilMat.stencilZPass = THREE.DecrementWrapStencilOp; // PASS 3\n      // draw the plane everywhere that the stencil buffer != 0, which will\n      // only be in the clipped region where back faces are visible.\n\n      planeStencilMat = this.getPlaneStencilMaterial(materialProps);\n      planeStencilMat.stencilWrite = true;\n      planeStencilMat.stencilRef = 0;\n      planeStencilMat.stencilFunc = THREE.NotEqualStencilFunc;\n      planeStencilMat.stencilFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZFail = THREE.ReplaceStencilOp;\n      planeStencilMat.stencilZPass = THREE.ReplaceStencilOp;\n      return [frontFaceStencilMat, backFaceStencilMat, planeStencilMat];\n    }\n\n    getPlaneStencilMaterial(areaProps) {\n      const SIZE = 20;\n      let planeStencilMat;\n      let colorProps = areaProps.color;\n\n      if (areaProps.hatching.type === 2) {\n        let orientationVector = utils.normalizeVector(areaProps.hatching.orientationVector);\n        let hatchSpacing = areaProps.hatching.factor;\n        let hatchColor = areaProps.hatching.color;\n        let hatchThickness = areaProps.hatching.thickness;\n        planeStencilMat = this.GraphicsThree3D.makeCanvasMaterial(colorProps, hatchColor, orientationVector, hatchSpacing, hatchThickness);\n        planeStencilMat.map.wrapS = planeStencilMat.map.wrapT = THREE.RepeatWrapping;\n        planeStencilMat.map.repeat.set(SIZE, SIZE);\n        planeStencilMat.map.offset.set(0.5, 0.5);\n      } else if (areaProps.hatching.type === 1) {\n        planeStencilMat = this.GraphicsThree3D.getMaterialFromColor(colorProps);\n      } // planeStencilMat.side = THREE.FrontSide;\n\n\n      return planeStencilMat;\n    }\n\n    resetClipObjects() {\n      this.clipObjects = [];\n    }\n\n  }\n\n  _exports.SectionalPlane = SectionalPlane;\n});","define(\"m08-2020/lib/StateControl/DimChainViewController\", [\"exports\", \"three\", \"m08-2020/lib/ThreeExtensions/VectorString\"], function (_exports, THREE, _VectorString) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DimChainViewController = void 0;\n\n  const _cameraLookVec = new THREE.Vector3();\n\n  const _affected3DVisIDs = new Set();\n\n  const _chainNormal = new _VectorString.VectorString();\n\n  let _activeView2D = false;\n\n  class DimChainViewController {\n    constructor() {\n      this.dimChainVisAngles = {\n        view3d: {},\n        view2d: {}\n      };\n      this.angleThreshold = 0.9;\n    }\n\n    updateChainVisualization(camera) {\n      camera.getWorldDirection(_cameraLookVec); // _affected3DVisIDs.clear();\n\n      _activeView2D = false;\n      Object.keys(this.dimChainVisAngles[\"view2d\"]).forEach(key => {\n        _chainNormal.setFromString(key);\n\n        if (_chainNormal.dot(_cameraLookVec) < -this.angleThreshold) this.show2D_hide3D(key);else this.hide2D_show3D(key);\n      });\n    } // Hide all 3D views when 2D is active -->\n\n\n    show2D_hide3D(key) {\n      Object.values(this.dimChainVisAngles[\"view2d\"][key]).forEach(chainArr => {\n        _activeView2D = true;\n        chainArr.forEach(chain => chain.visible = true);\n      });\n      Object.values(this.dimChainVisAngles[\"view3d\"]).forEach(chainArr => chainArr.forEach(chain => chain.visible = false));\n    }\n\n    hide2D_show3D(key) {\n      Object.values(this.dimChainVisAngles[\"view2d\"][key]).forEach(chainArr => chainArr.forEach(chain => chain.visible = false));\n      if (!_activeView2D) Object.values(this.dimChainVisAngles[\"view3d\"]).forEach(chainArr => chainArr.forEach(chain => chain.visible = true));\n    } // <-- Hide all 3D views when 2D is active\n    // Toggle only specific views -->\n\n\n    makeVisible2D(key) {\n      Object.entries(this.dimChainVisAngles[\"view2d\"][key]).forEach(([id, chainArr]) => {\n        _affected3DVisIDs.add(id);\n\n        chainArr.forEach(chain => chain.visible = true);\n        Object.values(this.dimChainVisAngles[\"view3d\"][id]).forEach(chain => chain.visible = false);\n      });\n    }\n\n    makeVisible3D(key) {\n      Object.entries(this.dimChainVisAngles[\"view2d\"][key]).forEach(([id, chainArr]) => {\n        chainArr.forEach(chain => chain.visible = false);\n        if (!_affected3DVisIDs.has(id)) Object.values(this.dimChainVisAngles[\"view3d\"][id]).forEach(chain => chain.visible = true);\n      });\n    } // <-- Toggle only specific views\n\n\n    setAngleThreshold(rawValue) {\n      this.angleThreshold = +rawValue / 100;\n    }\n\n    clearState() {\n      this.dimChainVisAngles[\"view3d\"] = {};\n      this.dimChainVisAngles[\"view2d\"] = {};\n    }\n\n    initView2D(normal, id) {\n      if (!this.dimChainVisAngles[\"view2d\"][normal]) this.dimChainVisAngles[\"view2d\"][normal] = {};\n      if (!this.dimChainVisAngles[\"view2d\"][normal][id]) this.dimChainVisAngles[\"view2d\"][normal][id] = [];\n    }\n\n    addChainsToView3D(id, chains) {\n      this.dimChainVisAngles[\"view3d\"][id] = [...chains];\n    }\n\n    addChainToView2D(normal, id, chain) {\n      this.dimChainVisAngles[\"view2d\"][normal][id].push(chain);\n    }\n\n  }\n\n  _exports.DimChainViewController = DimChainViewController;\n});","define(\"m08-2020/lib/StateControl/DynamicTextsController\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DynamicTextsController = void 0;\n\n  const _textWorldPos = new THREE.Vector3();\n\n  const _cameraLookDir = new THREE.Vector3();\n\n  const _lookDirVec = new THREE.Vector3();\n\n  class DynamicTextsController {\n    constructor() {\n      this.dynamicTexts = [];\n    }\n\n    resetState() {\n      this.dynamicTexts = [];\n    }\n\n    updateDynamicTexts(camera) {\n      camera.getWorldDirection(_cameraLookDir);\n\n      _cameraLookDir.negate();\n\n      this.dynamicTexts.forEach(text => {\n        text.getWorldPosition(_textWorldPos);\n\n        _lookDirVec.addVectors(_textWorldPos, _cameraLookDir);\n\n        text.lookAt(_lookDirVec);\n      });\n    }\n\n    addToDynamicTexts(text) {\n      this.dynamicTexts.push(text);\n    }\n\n  }\n\n  _exports.DynamicTextsController = DynamicTextsController;\n});","define(\"m08-2020/lib/StateControl/ZoomController\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.ZoomController = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  const DEFAULT_SCALE = 1;\n\n  const _lineStartingRotation = new THREE.Euler();\n\n  const _lineStartingPosition = new THREE.Vector3();\n\n  class ZoomController {\n    constructor(GraphicsThree3D) {\n      _defineProperty(this, \"changeTextsZoomableStatus\", () => {\n        this.textsZoomableStatus = !this.textsZoomableStatus;\n\n        if (!this.textsZoomableStatus) {\n          this.setTextsScale(DEFAULT_SCALE);\n        } else {\n          if (this.zoomScale > this.textScale) this.setTextsScale(this.zoomScale);else this.setTextsScale(this.textScale);\n        }\n      });\n\n      _defineProperty(this, \"updateTextsScale\", scale => {\n        if (!this.textsZoomableStatus || this.textScale <= ZoomController.getDefaultScaleAmount) return;\n        const previousScale = this.textScale;\n        this.textScale = scale;\n        this.textScale = parseFloat(this.textScale.toFixed(1));\n\n        if (this.zoomScale > previousScale) {\n          this.zoomScale /= previousScale;\n          this.zoomScale *= this.textScale;\n          this.setTextsScale(this.zoomScale);\n          return;\n        }\n\n        this.setTextsScale(this.textScale);\n      });\n\n      _defineProperty(this, \"setTextsScale\", scale => {\n        this.drawingTexts.forEach(item => {\n          item.scale.set(scale, scale, 1);\n        });\n      });\n\n      _defineProperty(this, \"getDrawingTexts\", () => {\n        this.GraphicsThree3D.ThreeObjects.forEach(item => {\n          item.sceneObject.traverse(object => {\n            if (object.geometry && object.geometry.type === 'TextBufferGeometry') {\n              this.drawingTexts.push(object);\n            }\n          });\n        });\n      });\n\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.controls = GraphicsThree3D.controls;\n      this.camera = GraphicsThree3D.camera;\n      this.drawingObjectWireframesSolid = [];\n      this.textsZoomableStatus = true;\n      this.drawingTexts = [];\n      this.textScale = DEFAULT_SCALE;\n      this.zoomScale = DEFAULT_SCALE; ///\n\n      this.controls.addEventListener(\"change\", this.zoomDrawingObjectSolidLines()); ///\n    }\n\n    static get getDefaultScaleAmount() {\n      return 0.1;\n    }\n\n    zoomDrawingObjectSolidLines() {\n      return () => this.updateScale();\n    }\n\n    resetScale() {\n      this.updateLinesScale(DEFAULT_SCALE);\n      this.updateTextsScale(DEFAULT_SCALE);\n    }\n\n    updateScale() {\n      if (this.camera.zoom < 1) {\n        let scaleFactor = 1 / this.camera.zoom;\n        this.updateLinesScale(scaleFactor);\n        this.zoomScale = this.textScale * scaleFactor;\n        if (this.textsZoomableStatus) this.setTextsScale(this.zoomScale);\n      }\n    }\n\n    updateLinesScale(scaleFactor) {\n      this.drawingObjectWireframesSolid.forEach(lineData => this.lineScaleUpdate(lineData, scaleFactor));\n    }\n\n    lineScaleUpdate(lineData, scaleFactor) {\n      _lineStartingRotation.copy(lineData.line.rotation);\n\n      _lineStartingPosition.copy(lineData.line.position);\n\n      lineData.line.position.sub(lineData.line.position);\n      lineData.line.setRotationFromEuler(lineData.eulerOne);\n      lineData.line.scale.set(scaleFactor, scaleFactor, 1);\n      lineData.line.setRotationFromEuler(_lineStartingRotation);\n      lineData.line.position.add(_lineStartingPosition);\n    }\n\n    getDrawingObjectWireframes(object) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      object.children.forEach(child => {\n        // TODO: Add support for buffergeometries\n        if (child.isLine && child.needsZoomControl) {\n          let ptOne = child.pointsStartEnd.startPoint;\n          let ptTwo = child.pointsStartEnd.endPoint;\n          let lineDirection = new THREE.Vector3().copy(ptTwo).sub(ptOne).normalize();\n          let quaternion = new THREE.Quaternion().setFromUnitVectors(lineDirection, Z_AXIS);\n          let quaternionConjugate = new THREE.Quaternion().setFromUnitVectors(Z_AXIS, lineDirection);\n          let eulerOne = new THREE.Euler().setFromQuaternion(quaternion);\n          let eulerTwo = new THREE.Euler().setFromQuaternion(quaternionConjugate);\n          this.drawingObjectWireframesSolid.push({\n            line: child,\n            eulerOne,\n            eulerTwo\n          });\n        }\n      });\n    }\n\n    clearZoomState() {\n      this.drawingObjectWireframesSolid = [];\n      this.drawingTexts = [];\n    }\n\n  }\n\n  _exports.ZoomController = ZoomController;\n});","define(\"m08-2020/lib/ThreeExtensions/CustomEllipseCurve\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.CustomEllipseCurve = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  class CustomEllipseCurve extends THREE.EllipseCurve {\n    constructor(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation, rotationPitch) {\n      super(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);\n\n      _defineProperty(this, \"getPoint\", function (t) {\n        // this is modified version of three js curve.getPoint to return vector3 instead of vector2;\n        const point = new THREE.Vector3();\n        const twoPi = Math.PI * 2;\n        let deltaAngle = this.aEndAngle - this.aStartAngle;\n        const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI\n\n        while (deltaAngle < 0) deltaAngle += twoPi;\n\n        while (deltaAngle > twoPi) deltaAngle -= twoPi;\n\n        if (deltaAngle < Number.EPSILON) {\n          if (samePoints) {\n            deltaAngle = 0;\n          } else {\n            deltaAngle = twoPi;\n          }\n        }\n\n        if (this.aClockwise === true && !samePoints) {\n          if (deltaAngle === twoPi) {\n            deltaAngle = -twoPi;\n          } else {\n            deltaAngle = deltaAngle - twoPi;\n          }\n        }\n\n        const angle = this.aStartAngle + t * deltaAngle;\n        let x = this.aX + this.xRadius * Math.cos(angle);\n        let y = this.aY + this.yRadius * Math.sin(angle);\n\n        if (this.aRotation !== 0) {\n          const cos = Math.cos(this.aRotation);\n          const sin = Math.sin(this.aRotation);\n          const tx = x - this.aX;\n          const ty = y - this.aY; // Rotate the point about the center of the ellipse.\n\n          x = tx * cos - ty * sin + this.aX;\n          y = tx * sin + ty * cos + this.aY;\n        }\n\n        return point.set(x, -t * this.rotationPitch, y);\n      });\n\n      _defineProperty(this, \"getTangent\", function (t) {\n        // this is modified version of three js getTangent to modify tangent vector;\n        const delta = 0.0001;\n        let t1 = t - delta;\n        let t2 = t + delta; // Capping in case of danger\n\n        if (t1 < 0) t1 = 0;\n        if (t2 > 1) t2 = 1;\n        const pt1 = this.getPoint(t1);\n        const pt2 = this.getPoint(t2);\n        const tangent = new THREE.Vector3();\n        tangent.copy(pt2).add(new THREE.Vector3(0, this.rotationPitch * delta, 0)).sub(pt1).normalize();\n        return tangent;\n      });\n\n      _defineProperty(this, \"computeFrenetFrames\", function (segments, closed) {\n        // this is modified version of three js computeFrenetFrames to modify normal vector;\n        // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n        const normal = new THREE.Vector3();\n        const tangents = [];\n        const normals = [];\n        const binormals = [];\n        const vec = new THREE.Vector3();\n        const mat = new THREE.Matrix4(); // compute the tangent vectors for each segment on the curve\n\n        for (let i = 0; i <= segments; i++) {\n          const u = i / segments;\n          tangents[i] = this.getTangentAt(u, new THREE.Vector3());\n          tangents[i].normalize();\n        } // select an initial normal vector perpendicular to the first tangent vector,\n        // and in the direction of the minimum tangent xyz component\n\n\n        normals[0] = new THREE.Vector3();\n        binormals[0] = new THREE.Vector3();\n        let min = Number.MAX_VALUE;\n        const tx = Math.abs(tangents[0].x);\n        const ty = Math.abs(tangents[0].y);\n        const tz = Math.abs(tangents[0].z);\n\n        if (tx <= min) {\n          min = tx;\n          normal.set(1, 0, 0);\n        }\n\n        if (ty <= min) {\n          min = ty;\n          normal.set(0, 1, 0);\n        }\n\n        if (tz <= min) {\n          normal.set(0, 0, 1);\n        }\n\n        vec.crossVectors(tangents[0], normal).normalize();\n        normals[0].crossVectors(tangents[0], vec);\n        binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n        for (let i = 1; i <= segments; i++) {\n          normals[i] = normals[i - 1].clone();\n          binormals[i] = binormals[i - 1].clone();\n          vec.crossVectors(tangents[i - 1], tangents[i]);\n\n          if (vec.length() > Number.EPSILON) {\n            vec.normalize();\n            const theta = Math.acos(THREE.MathUtils.clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors\n\n            normals[i].applyAxisAngle(new THREE.Vector3(0, 1, 0), theta);\n          }\n\n          binormals[i].crossVectors(tangents[i], normals[i]);\n        } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n\n        if (closed === true) {\n          let theta = Math.acos(THREE.MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1));\n          theta /= segments;\n\n          if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {\n            theta = -theta;\n          }\n\n          for (let i = 1; i <= segments; i++) {\n            // twist a little...\n            normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i));\n            binormals[i].crossVectors(tangents[i], normals[i]);\n          }\n        }\n\n        return {\n          tangents: tangents,\n          normals: normals,\n          binormals: binormals\n        };\n      });\n\n      this.rotationPitch = rotationPitch;\n    }\n\n  }\n\n  _exports.CustomEllipseCurve = CustomEllipseCurve;\n});","define(\"m08-2020/lib/ThreeExtensions/OutlinesGeometry\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.OutlinesGeometry = void 0;\n\n  class OutlinesGeometry extends THREE.Geometry {\n    constructor(geometry) {\n      super();\n      this.faceSplitFailed = false;\n      this.initGeometry(geometry);\n    }\n\n    initGeometry(geometry) {\n      let geometry2 = this.getGeometry(geometry);\n      this.vertices = geometry2.vertices;\n      this.faces = geometry2.faces;\n      this.splitFaces();\n    }\n\n    splitFaces() {\n      const DELTA = 1e-5;\n      const keys = ['a', 'b', 'c'];\n      let vertices = this.vertices;\n      let faces = this.faces; // console.log(faces)\n      // let _plane = new THREE.Plane();\n      // let facesByNormal = [];\n      // for ( let face of faces )\n      // {\n      //   let needToAddFace = true;\n      //   for ( let faceGroup of facesByNormal )\n      //   {\n      //     if ( faceGroup.normal && faceGroup.normal.dot( face.normal ) > 0.99 )\n      //     {\n      //       _plane.set( faceGroup.normal, 0 );\n      //       let distance = _plane.distanceToPoint( vertices[ face.a ].clone().projectOnVector( faceGroup.normal ) );\n      //       if ( distance <= faceGroup.distance + 0.01 && distance >= faceGroup.distance - 0.01 )\n      //       {\n      //         faceGroup.facesArr.push( face );\n      //         needToAddFace = false;\n      //         break;\n      //       }\n      //     }\n      //   }\n      //   if ( needToAddFace )\n      //   {\n      //     let faceNormal = face.normal.clone();\n      //     _plane.set( faceNormal, 0 );\n      //     let distance = _plane.distanceToPoint( vertices[ face.a ].clone().projectOnVector( faceNormal ) );\n      //     let newGroup = {\n      //       normal: faceNormal,\n      //       distance,\n      //       facesArr: [ face ]\n      //     };\n      //     facesByNormal.push( newGroup );\n      //   }\n      // }\n      // console.log(facesByNormal)\n\n      let vertexDict = vertices.reduce((dictX, vertex, index) => {\n        if (vertex.x in dictX) dictX[vertex.x].push({\n          vertex,\n          index\n        });else {\n          dictX[vertex.x] = [];\n          dictX[vertex.x].push({\n            vertex,\n            index\n          });\n        }\n        return dictX;\n      }, {}); // for ( let keyX of Object.keys( vertexDict ) ) {\n      //   vertexDict[ keyX ] = vertexDict[ keyX ].reduce((dictY, vertexData) => {\n      //     if ( vertexData.vertex.y in dictY ) dictY[ vertexData.vertex.y ].push( vertexData );\n      //     else { dictY[ vertexData.vertex.y ] = []; dictY[ vertexData.vertex.y ].push( vertexData ) }\n      //     return dictY;\n      //   }, {});\n      //   for ( let keyY of Object.keys( vertexDict[ keyX ] ) ) {\n      //     vertexDict[ keyX ][ keyY ] = vertexDict[ keyX ][ keyY ].reduce((dictZ, vertexData) => {\n      //       if ( vertexData.vertex.z in dictZ ) dictZ[ vertexData.vertex.z ].push( vertexData );\n      //       else { dictZ[ vertexData.vertex.z ] = []; dictZ[ vertexData.vertex.z ].push( vertexData ) }\n      //       return dictZ;\n      //     }, {});\n      //   }\n      // }\n      // let sortedVertices = vertices\n      //                     .map(( vertex, index ) => {\n      //                       return { vertex, index };\n      //                     })\n      //                     .sort( ( a, b ) => a.vertex.x - b.vertex.x );\n\n      let sortedKeysX = Object.keys(vertexDict).sort((a, b) => parseFloat(a) - parseFloat(b));\n      let edge = [0, 0],\n          face;\n\n      for (let i = 0; i < faces.length; i++) {\n        face = faces[i];\n\n        EDGES_LOOP: for (let j = 0; j < 3; j++) {\n          edge[0] = face[keys[j]];\n          edge[1] = face[keys[(j + 1) % 3]];\n          let vertexOne = vertices[edge[0]];\n          let vertexTwo = vertices[edge[1]];\n          let lowerBound = findBoundIndex(vertexOne.x, sortedKeysX);\n          let upperBound = findBoundIndex(vertexTwo.x, sortedKeysX);\n          if (lowerBound > upperBound) [lowerBound, upperBound] = [upperBound, lowerBound];\n          lowerBound = Math.max(lowerBound - 2, 0);\n          upperBound = Math.min(upperBound + 2, sortedKeysX.length - 1);\n          let line = new THREE.Line3(vertexOne, vertexTwo); // for each vertex checks if it lies in the edge\n\n          for (let e = lowerBound; e < upperBound; e++) {\n            let keyX = sortedKeysX[e];\n\n            for (let vertexData of vertexDict[keyX]) {\n              let i = vertexData.index;\n              if (i === edge[0] || i === edge[1]) continue;\n              let v = vertexData.vertex;\n              let closestPoint = line.closestPointToPoint(v, true, new THREE.Vector3());\n\n              if (closestPoint.distanceTo(v) < DELTA && v.distanceTo(line.start) > DELTA && v.distanceTo(line.end) > DELTA) {\n                // mark the current face as splitted so that his cords won't be considered\n                face.splitted = true; // Add two new faces, created splitting the face in two\n\n                let faceOne = new THREE.Face3(i, face[keys[(j + 2) % 3]], face[keys[j % 3]], face.normal, face.color, face.materialIndex);\n                let faceTwo = new THREE.Face3(i, face[keys[(j + 2) % 3]], face[keys[(j + 1) % 3]], face.normal, face.color, face.materialIndex);\n                faces.push(faceOne);\n                faces.push(faceTwo);\n                break EDGES_LOOP;\n              }\n            }\n          }\n\n          if (face.splitted) break;\n        }\n      }\n    }\n\n    getGeometry(geometry) {\n      let geometry2;\n      if (geometry.isBufferGeometry) geometry2 = new THREE.Geometry().fromBufferGeometry(geometry);else geometry2 = geometry.clone();\n      geometry2.mergeVertices();\n      geometry2.computeFaceNormals();\n      return geometry2;\n    }\n\n  }\n\n  _exports.OutlinesGeometry = OutlinesGeometry;\n\n  function findBoundIndex(coord, array) {\n    let min = 0;\n    let max = array.length - 1;\n\n    while (min <= max) {\n      let mid = Math.floor((min + max) / 2);\n      if (array[mid] == coord) return mid;else if (parseFloat(array[mid]) > parseFloat(coord)) max = mid - 1;else min = mid + 1;\n    }\n\n    return 0;\n  }\n});","define(\"m08-2020/lib/ThreeExtensions/PlanarExtrusionPointsGenerator\", [\"exports\", \"three\", \"polybooljs\", \"poly-decomp\", \"hull.js\"], function (_exports, THREE, _polybooljs, PolyDecomp, hull) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.PlanarExtrusionPointsGenerator = void 0;\n\n  class PlanarExtrusionPointsGenerator {\n    constructor(points2D, extrusionVector2D) {\n      this.extrudedPoints = [];\n      this.generateExtrudedPoints(points2D, extrusionVector2D);\n    }\n\n    generateExtrudedPoints(points2D, extrusionVector2D) {\n      let polyPts2D = points2D.map(pt => [pt.x, pt.y]);\n      PolyDecomp.makeCCW(polyPts2D);\n      let convexPolygons = PolyDecomp.quickDecomp(polyPts2D);\n      let polygonSegments = this.generatePolygonSegments(convexPolygons, extrusionVector2D);\n      let finalPolygon = this.uniteSegmentsAndReturnPolygon(polygonSegments);\n      let finalPoints = this.getPointsFromFinalPolygon(finalPolygon);\n      this.extrudedPoints.push(...finalPoints);\n    }\n\n    getPointsFromFinalPolygon(finalPolygon) {\n      return finalPolygon.regions[0].map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n    }\n\n    uniteSegmentsAndReturnPolygon(segments) {\n      let finalSegments = segments[0];\n\n      for (let i = 1; i < segments.length; i++) {\n        let comb = _polybooljs.default.combine(finalSegments, segments[i]);\n\n        finalSegments = _polybooljs.default.selectUnion(comb);\n      }\n\n      return _polybooljs.default.polygon(finalSegments);\n    }\n\n    generatePolygonSegments(convexPolygons, extrusionVector2D) {\n      let polygonSegments = [];\n      convexPolygons.forEach(polygon => {\n        let polyPts = polygon.map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n        let polyPtsExtr = polyPts.map(pt => pt.clone().add(extrusionVector2D));\n        let pointsToGetEdges = [...polyPts, ...polyPtsExtr];\n        let outerPoints = hull(pointsToGetEdges.map(pt => [pt.x, pt.y]), Infinity);\n        polygonSegments.push(_polybooljs.default.segments({\n          regions: [outerPoints],\n          inverted: false\n        }));\n      });\n      return polygonSegments;\n    }\n\n    generateSegmentsFromPoints(pointsArr) {\n      return pointsArr.reduce((allSegments, currentPoints) => [...allSegments, _polybooljs.default.segments({\n        regions: [currentPoints.map(pt => [pt.x, pt.y])],\n        inverted: false\n      })], []);\n    }\n\n    getPolygonPoints(oldPoints, newPoints) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let startPts = [...oldPoints, oldPoints[0].clone()];\n      let endPts = [...newPoints, newPoints[0].clone()];\n      let polyPts = [];\n      let segmentPoints;\n\n      for (let i = 0; i < startPts.length - 1; i++) {\n        segmentPoints = [startPts[i], startPts[i + 1], endPts[i], endPts[i + 1]];\n        segmentPoints = this.sortPoints(segmentPoints, Z_AXIS);\n        polyPts.push(segmentPoints);\n      }\n\n      return polyPts;\n    }\n\n    sortPoints(points, normal) {\n      let centerPoint = this.getCenterPoint(points);\n\n      let sortOrder = (a, b) => normal.dot(new THREE.Vector3().crossVectors(a.clone().sub(centerPoint), b.clone().sub(centerPoint)));\n\n      points.sort(sortOrder);\n      return points;\n    }\n\n    getCenterPoint(pointsArr) {\n      let length = pointsArr.length;\n      let centerPoint = pointsArr.reduce((finalVec, currentVec) => finalVec.add(currentVec), new THREE.Vector3()).multiplyScalar(1 / length);\n      return centerPoint;\n    }\n\n  }\n\n  _exports.PlanarExtrusionPointsGenerator = PlanarExtrusionPointsGenerator;\n});","define(\"m08-2020/lib/ThreeExtensions/PlanarRotationPointsGenerator\", [\"exports\", \"three\", \"polybooljs\", \"delaunator\", \"poly-decomp\", \"hull.js\", \"m08-2020/lib/WireframeGenerator\"], function (_exports, THREE, _polybooljs, _delaunator, PolyDecomp, hull, _WireframeGenerator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.PlanarRotationPointsGenerator = void 0;\n\n  class PlanarRotationPointsGenerator {\n    constructor(pointsToRotate, rotationParameters) {\n      this.rotatedPoints = [];\n      this.holePoints = [];\n      this.generateRotatedPoints(pointsToRotate, rotationParameters);\n    }\n\n    generateRotatedPoints(pointsToRotate, rotationParameters) {\n      const DETAIL = 64;\n      let {\n        rotationAngle,\n        rotationPitch\n      } = rotationParameters;\n      let numOfSegments = DETAIL * Math.abs(rotationAngle) / Math.PI;\n      let segmentRotation = rotationAngle / numOfSegments;\n      let polyPts2D = pointsToRotate.map(pt => [pt.x, pt.y]);\n      PolyDecomp.makeCCW(polyPts2D);\n      let convexPolygons = PolyDecomp.quickDecomp(polyPts2D);\n      let polygonSegments = [];\n      convexPolygons.forEach(polygon => {\n        let polyPts3D = polygon.map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n        let rotatedPointsPolygons = this.getRotatedPointsPolygons(polyPts3D, numOfSegments, segmentRotation);\n        let decomposedPolySegments = rotatedPointsPolygons[0];\n\n        for (let i = 1; i < rotatedPointsPolygons.length; i++) {\n          let comb = _polybooljs.default.combine(decomposedPolySegments, rotatedPointsPolygons[i]);\n\n          decomposedPolySegments = _polybooljs.default.selectUnion(comb);\n        }\n\n        polygonSegments.push(decomposedPolySegments);\n      });\n      let finalPolygon = this.uniteSegmentsAndReturnPolygon(polygonSegments);\n      this.getPointsFromFinalPolygon(finalPolygon);\n    }\n\n    getPointsFromFinalPolygon(finalPolygon) {\n      // TODO: Maybe need to check radiuses of segments to check which ones are outer shape and which are holes?\n      if (finalPolygon.regions.length > 1) {\n        for (let i = 0; i < finalPolygon.regions.length - 1; i++) {\n          this.holePoints.push(finalPolygon.regions[i].map(pt => new THREE.Vector3(pt[0], pt[1], 0)));\n        }\n      }\n\n      this.rotatedPoints = finalPolygon.regions[finalPolygon.regions.length - 1].map(pt => new THREE.Vector3(pt[0], pt[1], 0));\n    }\n\n    uniteSegmentsAndReturnPolygon(segments) {\n      let finalSegments = segments[0];\n\n      for (let i = 1; i < segments.length; i++) {\n        let comb = _polybooljs.default.combine(finalSegments, segments[i]);\n\n        finalSegments = _polybooljs.default.selectUnion(comb);\n      }\n\n      return _polybooljs.default.polygon(finalSegments);\n    }\n\n    getRotatedPointsPolygons(points, numOfSegments, segmentRotation) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let rotatedPointsPolygons = [];\n      let previousPts = [...points];\n\n      for (let s = 0; s < numOfSegments; s++) {\n        let oldPoints = previousPts.map(pt => pt.clone());\n        let newPoints = previousPts.map(pt => pt.clone().applyAxisAngle(Z_AXIS, segmentRotation));\n        previousPts = newPoints.map(pt => pt.clone());\n        let pointsToGetEdges = [...oldPoints, ...newPoints];\n        let outerPoints = hull(pointsToGetEdges.map(pt => [pt.x, pt.y]), Infinity);\n        rotatedPointsPolygons.push(_polybooljs.default.segments({\n          regions: [outerPoints],\n          inverted: false\n        }));\n      }\n\n      return rotatedPointsPolygons;\n    }\n\n    generateSegmentsFromPoints(pointsArr) {\n      return pointsArr.reduce((allSegments, currentPoints) => [...allSegments, _polybooljs.default.segments({\n        regions: [currentPoints.map(pt => [pt.x, pt.y])],\n        inverted: false\n      })], []);\n    }\n\n    getPolygonPoints(oldPoints, newPoints) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let startPts = [...oldPoints, oldPoints[0].clone()];\n      let endPts = [...newPoints, newPoints[0].clone()];\n      let polyPts = [];\n      let segmentPoints;\n\n      for (let i = 0; i < startPts.length - 1; i++) {\n        segmentPoints = [startPts[i], startPts[i + 1], endPts[i], endPts[i + 1]];\n        segmentPoints = this.sortPoints(segmentPoints, Z_AXIS);\n        polyPts.push(segmentPoints);\n      }\n\n      return polyPts;\n    }\n\n    getOuterPointsFromPointCloud(points, zAxis) {\n      let linePairs = this.getOuterLinePairsFromPoints(points);\n      let outerPoints = linePairs.reduce((allPts, currentPair) => {\n        allPts.push(...currentPair);\n        return allPts;\n      }, []).reduce((filteredPts, currentPt, idx) => {\n        if (idx % 2) filteredPts.push(currentPt);\n        return filteredPts;\n      }, []);\n      outerPoints = this.sortPoints(outerPoints, zAxis);\n      return outerPoints;\n    }\n\n    getOuterLinePairsFromPoints(points) {\n      let facesDict = this.getFacesDictFromPoints(points);\n      let linePairs = [];\n\n      for (let [edgeIndices, facePair] of Object.entries(facesDict)) {\n        if (facePair.length !== 2) {\n          let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n          let vectorOne = points[verticesIdx[0]];\n          let vectorTwo = points[verticesIdx[1]];\n          linePairs.push([vectorOne, vectorTwo]);\n        }\n      }\n\n      return linePairs;\n    }\n\n    getFacesDictFromPoints(points) {\n      let faces = this.getFacesFromPoints(points);\n      return new _WireframeGenerator.WireframeGenerator().getFacesDict(faces);\n    }\n\n    getFacesFromPoints(points) {\n      let delaunayFaces = _delaunator.default.from(points.map(p => [p.x, p.y])).triangles;\n\n      let faces = [];\n\n      for (let i = 0; i < delaunayFaces.length; i += 3) faces.push({\n        a: delaunayFaces[i],\n        b: delaunayFaces[i + 1],\n        c: delaunayFaces[i + 2]\n      });\n\n      return faces;\n    }\n\n    sortPoints(points, normal) {\n      let centerPoint = this.getCenterPoint(points);\n\n      let sortOrder = (a, b) => normal.dot(new THREE.Vector3().crossVectors(a.clone().sub(centerPoint), b.clone().sub(centerPoint)));\n\n      points.sort(sortOrder);\n      return points;\n    }\n\n    getCenterPoint(pointsArr) {\n      let length = pointsArr.length;\n      let centerPoint = pointsArr.reduce((finalVec, currentVec) => finalVec.add(currentVec), new THREE.Vector3()).multiplyScalar(1 / length);\n      return centerPoint;\n    }\n\n  }\n\n  _exports.PlanarRotationPointsGenerator = PlanarRotationPointsGenerator;\n});","define(\"m08-2020/lib/ThreeExtensions/RaycastModdedMesh\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.RaycastModdedMesh = void 0;\n\n  /**\n   * Original authors:\n   * mrdoob / http://mrdoob.com/\n   * alteredq / http://alteredqualia.com/\n   * mikael emtinger / http://gomo.se/\n   * jonobr1 / http://jonobr1.com/\n   */\n  const THRESEHOLD = 1e-4;\n\n  const _inverseMatrix = new THREE.Matrix4();\n\n  const _ray = new THREE.Ray();\n\n  const _sphere = new THREE.Sphere();\n\n  const _vA = new THREE.Vector3();\n\n  const _vB = new THREE.Vector3();\n\n  const _vC = new THREE.Vector3();\n\n  const _tempA = new THREE.Vector3();\n\n  const _tempB = new THREE.Vector3();\n\n  const _tempC = new THREE.Vector3();\n\n  const _morphA = new THREE.Vector3();\n\n  const _morphB = new THREE.Vector3();\n\n  const _morphC = new THREE.Vector3();\n\n  const _uvA = new THREE.Vector2();\n\n  const _uvB = new THREE.Vector2();\n\n  const _uvC = new THREE.Vector2();\n\n  const _intersectionPoint = new THREE.Vector3();\n\n  const _intersectionPointWorld = new THREE.Vector3();\n\n  class RaycastModdedMesh extends THREE.Mesh {\n    constructor(geometry, material) {\n      super(geometry, material);\n    }\n\n    raycast(raycaster, intersects) {\n      const geometry = this.geometry;\n      const material = this.material;\n      const matrixWorld = this.matrixWorld;\n      if (material === undefined) return; // Checking boundingSphere distance to ray\n\n      if (geometry.boundingSphere === null) geometry.computeBoundingSphere();\n\n      _sphere.copy(geometry.boundingSphere);\n\n      _sphere.applyMatrix4(matrixWorld);\n\n      if (raycaster.ray.intersectsSphere(_sphere) === false) return; //\n\n      _inverseMatrix.getInverse(matrixWorld);\n\n      _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); // Check boundingBox before continuing\n\n\n      if (geometry.boundingBox !== null) {\n        if (_ray.intersectsBox(geometry.boundingBox) === false) return;\n      }\n\n      let intersection;\n\n      if (geometry.isBufferGeometry) {\n        const index = geometry.index;\n        const position = geometry.attributes.position;\n        const morphPosition = geometry.morphAttributes.position;\n        const morphTargetsRelative = geometry.morphTargetsRelative;\n        const uv = geometry.attributes.uv;\n        const uv2 = geometry.attributes.uv2;\n        const groups = geometry.groups;\n        const drawRange = geometry.drawRange;\n\n        if (index !== null) {\n          // indexed buffer geometry\n          if (Array.isArray(material)) {\n            for (let i = 0, il = groups.length; i < il; i++) {\n              const group = groups[i];\n              const groupMaterial = material[group.materialIndex];\n              const start = Math.max(group.start, drawRange.start);\n              const end = Math.min(group.start + group.count, drawRange.start + drawRange.count);\n\n              for (let j = start, jl = end; j < jl; j += 3) {\n                const a = index.getX(j);\n                const b = index.getX(j + 1);\n                const c = index.getX(j + 2);\n                intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c);\n\n                if (intersection) {\n                  intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics\n\n                  intersection.face.materialIndex = group.materialIndex;\n                  intersects.push(intersection);\n                }\n              }\n            }\n          } else {\n            const start = Math.max(0, drawRange.start);\n            const end = Math.min(index.count, drawRange.start + drawRange.count);\n\n            for (let i = start, il = end; i < il; i += 3) {\n              const a = index.getX(i);\n              const b = index.getX(i + 1);\n              const c = index.getX(i + 2);\n              intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c);\n\n              if (intersection) {\n                intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics\n\n                intersects.push(intersection);\n              }\n            }\n          }\n        } else if (position !== undefined) {\n          // non-indexed buffer geometry\n          if (Array.isArray(material)) {\n            for (let i = 0, il = groups.length; i < il; i++) {\n              const group = groups[i];\n              const groupMaterial = material[group.materialIndex];\n              const start = Math.max(group.start, drawRange.start);\n              const end = Math.min(group.start + group.count, drawRange.start + drawRange.count);\n\n              for (let j = start, jl = end; j < jl; j += 3) {\n                const a = j;\n                const b = j + 1;\n                const c = j + 2;\n                intersection = checkBufferGeometryIntersection(this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c);\n\n                if (intersection) {\n                  intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics\n\n                  intersection.face.materialIndex = group.materialIndex;\n                  intersects.push(intersection);\n                }\n              }\n            }\n          } else {\n            const start = Math.max(0, drawRange.start);\n            const end = Math.min(position.count, drawRange.start + drawRange.count);\n\n            for (let i = start, il = end; i < il; i += 3) {\n              const a = i;\n              const b = i + 1;\n              const c = i + 2;\n              intersection = checkBufferGeometryIntersection(this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c);\n\n              if (intersection) {\n                intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics\n\n                intersects.push(intersection);\n              }\n            }\n          }\n        }\n      } else if (geometry.isGeometry) {\n        const isMultiMaterial = Array.isArray(material);\n        const vertices = geometry.vertices;\n        const faces = geometry.faces;\n        let uvs;\n        const faceVertexUvs = geometry.faceVertexUvs[0];\n        if (faceVertexUvs.length > 0) uvs = faceVertexUvs;\n\n        for (let f = 0, fl = faces.length; f < fl; f++) {\n          const face = faces[f];\n          const faceMaterial = isMultiMaterial ? material[face.materialIndex] : material;\n          if (faceMaterial === undefined) continue;\n          const fvA = vertices[face.a].clone();\n          const fvB = vertices[face.b].clone();\n          const fvC = vertices[face.c].clone();\n          let centerVec = new THREE.Vector3((fvA.x + fvB.x + fvC.x) / 3, (fvA.y + fvB.y + fvC.y) / 3, (fvA.z + fvB.z + fvC.z) / 3);\n          let dirA = new THREE.Vector3().subVectors(fvA, centerVec).normalize().multiplyScalar(THRESEHOLD);\n          let dirB = new THREE.Vector3().subVectors(fvB, centerVec).normalize().multiplyScalar(THRESEHOLD);\n          let dirC = new THREE.Vector3().subVectors(fvC, centerVec).normalize().multiplyScalar(THRESEHOLD);\n          fvA.add(dirA);\n          fvB.add(dirB);\n          fvC.add(dirC);\n          intersection = checkIntersection(this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint);\n\n          if (intersection) {\n            if (uvs && uvs[f]) {\n              const uvs_f = uvs[f];\n\n              _uvA.copy(uvs_f[0]);\n\n              _uvB.copy(uvs_f[1]);\n\n              _uvC.copy(uvs_f[2]);\n\n              intersection.uv = THREE.Triangle.getUV(_intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new THREE.Vector2());\n            }\n\n            intersection.face = face;\n            intersection.faceIndex = f;\n            intersects.push(intersection);\n          }\n        }\n      }\n    }\n\n  }\n\n  _exports.RaycastModdedMesh = RaycastModdedMesh;\n\n  function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) {\n    let intersect;\n\n    if (material.side === THREE.BackSide) {\n      intersect = ray.intersectTriangle(pC, pB, pA, true, point);\n    } else {\n      intersect = ray.intersectTriangle(pA, pB, pC, material.side !== THREE.DoubleSide, point);\n    }\n\n    if (intersect === null) return null;\n\n    _intersectionPointWorld.copy(point);\n\n    _intersectionPointWorld.applyMatrix4(object.matrixWorld);\n\n    const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld);\n    if (distance < raycaster.near || distance > raycaster.far) return null;\n    return {\n      distance: distance,\n      point: _intersectionPointWorld.clone(),\n      object: object\n    };\n  }\n\n  function checkBufferGeometryIntersection(object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c) {\n    _vA.fromBufferAttribute(position, a);\n\n    _vB.fromBufferAttribute(position, b);\n\n    _vC.fromBufferAttribute(position, c);\n\n    const morphInfluences = object.morphTargetInfluences;\n\n    if (material.morphTargets && morphPosition && morphInfluences) {\n      _morphA.set(0, 0, 0);\n\n      _morphB.set(0, 0, 0);\n\n      _morphC.set(0, 0, 0);\n\n      for (let i = 0, il = morphPosition.length; i < il; i++) {\n        const influence = morphInfluences[i];\n        const morphAttribute = morphPosition[i];\n        if (influence === 0) continue;\n\n        _tempA.fromBufferAttribute(morphAttribute, a);\n\n        _tempB.fromBufferAttribute(morphAttribute, b);\n\n        _tempC.fromBufferAttribute(morphAttribute, c);\n\n        if (morphTargetsRelative) {\n          _morphA.addScaledVector(_tempA, influence);\n\n          _morphB.addScaledVector(_tempB, influence);\n\n          _morphC.addScaledVector(_tempC, influence);\n        } else {\n          _morphA.addScaledVector(_tempA.sub(_vA), influence);\n\n          _morphB.addScaledVector(_tempB.sub(_vB), influence);\n\n          _morphC.addScaledVector(_tempC.sub(_vC), influence);\n        }\n      }\n\n      _vA.add(_morphA);\n\n      _vB.add(_morphB);\n\n      _vC.add(_morphC);\n    }\n\n    if (object.isSkinnedMesh) {\n      object.boneTransform(a, _vA);\n      object.boneTransform(b, _vB);\n      object.boneTransform(c, _vC);\n    }\n\n    const intersection = checkIntersection(object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint);\n\n    if (intersection) {\n      if (uv) {\n        _uvA.fromBufferAttribute(uv, a);\n\n        _uvB.fromBufferAttribute(uv, b);\n\n        _uvC.fromBufferAttribute(uv, c);\n\n        intersection.uv = THREE.Triangle.getUV(_intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new THREE.Vector2());\n      }\n\n      if (uv2) {\n        _uvA.fromBufferAttribute(uv2, a);\n\n        _uvB.fromBufferAttribute(uv2, b);\n\n        _uvC.fromBufferAttribute(uv2, c);\n\n        intersection.uv2 = THREE.Triangle.getUV(_intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new THREE.Vector2());\n      }\n\n      const face = new Face3(a, b, c);\n      THREE.Triangle.getNormal(_vA, _vB, _vC, face.normal);\n      intersection.face = face;\n    }\n\n    return intersection;\n  }\n});","define(\"m08-2020/lib/ThreeExtensions/RotatedGeometry\", [\"exports\", \"three\", \"three/src/extras/Earcut\", \"m08-2020/lib/Utils\"], function (_exports, THREE, _Earcut, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.RotatedGeometry = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class RotatedGeometry extends THREE.Geometry {\n    constructor(points, rotationData, rotatesPlanar) {\n      super();\n      this.rotatesPlanar = rotatesPlanar;\n      this.generateSelf(points, rotationData);\n    }\n\n    generateSelf(points, rotationData) {\n      const DETAIL = 64;\n      const NUM_OF_POINTS = points.length;\n      this.reveresePointsIfNeeded(points, rotationData);\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationData;\n      let numOfSegments = DETAIL * Math.abs(rotationAngle) / (2 * Math.PI);\n      this.vertices.push(...points);\n      let orientationFront = getOrientation(points, rotationAxis, rotationPoint);\n      let crossVecFront = this.getCrossProduct(points, orientationFront);\n      let pitchVector = new THREE.Vector3();\n      let pitchValue = rotationPitch * Math.abs(rotationAngle) / (2 * Math.PI);\n\n      for (let i = 0; i < numOfSegments; i++) {\n        for (let point of points) {\n          let rotatedPoint = new THREE.Vector3().copy(point).sub(rotationPoint).applyAxisAngle(rotationAxis, (i + 1) * rotationAngle / numOfSegments).add(rotationPoint).add(pitchVector.copy(rotationAxis).multiplyScalar((i + 1) * pitchValue / numOfSegments));\n          this.vertices.push(rotatedPoint);\n        }\n      }\n\n      let frontFaces = triangulatePoints3D(points, crossVecFront);\n      let backIndexStart = this.vertices.length - NUM_OF_POINTS;\n      let backPoints = this.vertices.slice(backIndexStart);\n      let backIndexMap = {};\n\n      for (let i = 0; i < backPoints.length; i++) backIndexMap[i] = backIndexStart + i;\n\n      let orientationBack = getOrientation(backPoints, rotationAxis, rotationPoint);\n      let crossVecBack = this.getCrossProduct(backPoints, orientationBack);\n      let backFaces = triangulatePoints3D(backPoints, crossVecBack);\n      if (crossVecFront.dot(crossVecBack) > 0) backFaces.reverse();\n\n      if (!pointsAreSameFrontBack(points, backPoints)) {\n        // Front\n        for (let i = 0, l = frontFaces.length; i < l; i += 3) {\n          this.faces.push(new THREE.Face3(frontFaces[i], frontFaces[i + 1], frontFaces[i + 2]));\n          addUV(this.faceVertexUvs[0]);\n        } // Back\n\n\n        for (let i = 0, l = backFaces.length; i < l; i += 3) {\n          this.faces.push(new THREE.Face3(backIndexMap[backFaces[i]], backIndexMap[backFaces[i + 1]], backIndexMap[backFaces[i + 2]]));\n          addUV(this.faceVertexUvs[0]);\n        }\n      } // Side\n\n\n      for (let s = 0; s < numOfSegments; s++) {\n        let offsetFromStart = s * NUM_OF_POINTS;\n\n        for (let i = 0; i < NUM_OF_POINTS; i++) {\n          let correctedIndexBack = i === NUM_OF_POINTS - 1 ? NUM_OF_POINTS : i + NUM_OF_POINTS + 1; // Bad naming/style?\n\n          let correctedIndexFront = i === NUM_OF_POINTS - 1 ? 0 : i + 1;\n          this.faces.push(new THREE.Face3(i + offsetFromStart, i + NUM_OF_POINTS + offsetFromStart, correctedIndexBack + offsetFromStart));\n          this.faces.push(new THREE.Face3(i + offsetFromStart, correctedIndexBack + offsetFromStart, correctedIndexFront + offsetFromStart));\n          addUV(this.faceVertexUvs[0]);\n          addUV(this.faceVertexUvs[0]);\n        }\n      }\n\n      this.computeFaceNormals();\n      this.computeVertexNormals();\n    }\n\n    reveresePointsIfNeeded(points, rotationData) {\n      let {\n        rotationPoint,\n        rotationAxis\n      } = rotationData;\n      let orientation = getOrientation(points, rotationAxis, rotationPoint);\n      let crossVec = this.getCrossProduct(points, orientation);\n      let extrusionDir = this.getExtrusionDirection(points[0], rotationData);\n      if (extrusionDir.dot(crossVec) < 0) points.reverse();\n    }\n\n    getCrossProduct(points, orientation) {\n      let {\n        xAxis,\n        yAxis,\n        zAxis\n      } = orientation;\n      if (this.rotatesPlanar) [zAxis, yAxis] = [yAxis, zAxis];\n      let ptA = new THREE.Vector3();\n      let minY = Infinity;\n      let maxX = -Infinity;\n      let idxA = 0;\n      let zProjection = new THREE.Vector3().copy(points[0]).projectOnVector(zAxis);\n      points.forEach((point, idx) => {\n        let projectedPoint = point.clone().sub(zProjection);\n        let projectedX = projectedPoint.dot(xAxis);\n        let projectedY = projectedPoint.dot(yAxis);\n\n        if (projectedY <= minY && projectedX >= maxX) {\n          minY = projectedY;\n          maxX = projectedX;\n          ptA.copy(projectedPoint);\n          idxA = idx;\n        }\n      });\n      let idxB = idxA === 0 ? points.length - 1 : idxA - 1;\n      let idxC = idxA === points.length - 1 ? 0 : idxA + 1;\n      let vecAB = new THREE.Vector3().subVectors(points[idxB], points[idxA]);\n      let vecAC = new THREE.Vector3().subVectors(points[idxC], points[idxA]);\n      return vecAB.cross(vecAC).normalize();\n    }\n\n    getExtrusionDirection(referencePoint, rotationData) {\n      let {\n        rotationPoint,\n        rotationAxis,\n        rotationAngle,\n        rotationPitch\n      } = rotationData;\n      let rotationSign = Math.sign(rotationAngle);\n      let pitchSign = Math.sign(rotationPitch);\n\n      if (this.rotatesPlanar) {\n        let extrusionDir = rotationAxis.clone();\n        if (pitchSign) extrusionDir.negate();\n        return extrusionDir;\n      }\n\n      let rotatedPoint = new THREE.Vector3().copy(referencePoint).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationSign * 0.1).add(rotationPoint);\n      return new THREE.Vector3().subVectors(rotatedPoint, referencePoint);\n    }\n\n  } // Helper functions\n\n\n  _exports.RotatedGeometry = RotatedGeometry;\n\n  function triangulatePoints3D(points, surfaceNormal) {\n    const Z_AXIS = new THREE.Vector3(0, 0, 1);\n    const QUATERNION = new THREE.Quaternion().setFromUnitVectors(surfaceNormal, Z_AXIS);\n    let dummyVec = new THREE.Vector3();\n    let pointsFlat = points.reduce((ptArr, pt) => {\n      dummyVec.copy(pt);\n      dummyVec.applyQuaternion(QUATERNION);\n      ptArr.push(dummyVec.x, dummyVec.y, dummyVec.z);\n      return ptArr;\n    }, []);\n\n    let faces = _Earcut.Earcut.triangulate(pointsFlat, [], 3);\n\n    if (surfaceNormal.dot(Z_AXIS) < 0) faces.reverse();\n    return faces;\n  }\n\n  function pointsAreSameFrontBack(frontPts, backPts) {\n    let allPointsAreEqual = true;\n\n    for (let i = 0; i < frontPts.length; i++) {\n      if (!utils.vectorsAreEqual(frontPts[i], backPts[i])) allPointsAreEqual = false;\n    }\n\n    return allPointsAreEqual;\n  }\n\n  function getOrientation(points, rotationAxis, rotationPoint) {\n    let orientation = {\n      xAxis: getAxisX(points, rotationAxis, rotationPoint),\n      yAxis: rotationAxis\n    };\n    orientation.zAxis = new THREE.Vector3().crossVectors(orientation.xAxis, orientation.yAxis);\n    return orientation;\n  }\n\n  function getAxisX(points, rotationAxis, rotationPoint) {\n    let midVector = getMidVector(points);\n    let surfaceNormal = utils.getCrossVectorFromPoints(points);\n    let rotationPointCorrected = rotationPoint.clone().sub(midVector);\n    let rotPointOnSurface = rotationPointCorrected.clone().projectOnPlane(surfaceNormal);\n    let rotPointOnAxis = rotPointOnSurface.clone().projectOnVector(rotationAxis);\n    return rotPointOnSurface.sub(rotPointOnAxis).normalize();\n  }\n\n  function getMidVector(pts) {\n    let maxX = -Infinity;\n    let maxY = -Infinity;\n    let maxZ = -Infinity;\n    let minX = Infinity;\n    let minY = Infinity;\n    let minZ = Infinity;\n    pts.forEach(point => {\n      if (point.x > maxX) maxX = point.x;\n      if (point.y > maxY) maxY = point.y;\n      if (point.z > maxZ) maxZ = point.z;\n      if (point.x < minX) minX = point.x;\n      if (point.y < minY) minY = point.y;\n      if (point.z < minZ) minZ = point.z;\n    });\n    let midX = minX + (maxX - minX) / 2;\n    let midY = minY + (maxY - minY) / 2;\n    let midZ = minZ + (maxZ - minZ) / 2;\n    let midVector = new THREE.Vector3(midX, midY, midZ);\n    return midVector;\n  }\n\n  function addUV(UVs) {\n    // TEMP Func\n    UVs.push([new THREE.Vector2(0, 0), new THREE.Vector2(0, 0), new THREE.Vector2(0, 0)]);\n  }\n});","define(\"m08-2020/lib/ThreeExtensions/VectorString\", [\"exports\", \"three\"], function (_exports, THREE) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.VectorString = void 0;\n\n  class VectorString extends THREE.Vector3 {\n    constructor() {\n      super();\n    }\n\n    convertToString() {\n      return `${this.x},${this.y},${this.z}`;\n    }\n\n    setFromString(string) {\n      let elems = string.split(\",\");\n      this.set(+elems[0], +elems[1], +elems[2]);\n    }\n\n    static vecToString(vec) {\n      return `${vec.x},${vec.y},${vec.z}`;\n    }\n\n    static generateVectorsFromString(string) {\n      const STEP_SIZE = 3;\n      let vectors = [];\n      let elems = string.split(\",\");\n\n      for (let i = 0; i < elems.length; i += STEP_SIZE) {\n        let vec = new VectorString().setFromString(elems.slice(i, i + STEP_SIZE).join(\",\"));\n        vectors.push(vec);\n      }\n\n      return vectors;\n    }\n\n    static generateStringFromVertices(vertices) {\n      return vertices.reduce((arr, vec) => {\n        arr.push(vec.x, vec.y, vec.z);\n        return arr;\n      }, []).join(\",\");\n    }\n\n  }\n\n  _exports.VectorString = VectorString;\n});","define(\"m08-2020/lib/ThreeModule\", [\"exports\", \"lodash\", \"three\", \"three/examples/jsm/controls/OrbitControls\", \"m08-2020/lib/CSGMesh\", \"polybooljs\", \"@tweenjs/tween.js\", \"m08-2020/lib/Utils\", \"m08-2020/lib/Utils/BooleanOperations\", \"m08-2020/lib/Utils/DimensionalChainIntersectionDetection\", \"m08-2020/lib/WireframeGenerator\", \"m08-2020/lib/ClippingUtils/SectionalPlane\", \"m08-2020/lib/ClippingUtils/DynamicSectionalPlane\", \"m08-2020/lib/FigureLoaders/DrawingObjectLoader\", \"m08-2020/lib/FigureLoaders/ArrowLoader\", \"m08-2020/lib/FigureLoaders/DimensionalChainLoader\", \"m08-2020/lib/FigureLoaders/FastenerLoader\", \"m08-2020/lib/FigureLoaders/TextLoader\", \"m08-2020/lib/FileGeneration/DrawingPlaneLoader\", \"m08-2020/lib/FileGeneration/DXFGenerator\", \"m08-2020/lib/StateControl/ZoomController\", \"m08-2020/lib/StateControl/DimChainViewController\", \"m08-2020/lib/StateControl/DynamicTextsController\", \"m08-2020/lib/font_urls\"], function (_exports, lodash, THREE, _OrbitControls, _CSGMesh, _polybooljs, _tween, _Utils, BooleanOperations, _DimensionalChainIntersectionDetection, _WireframeGenerator, _SectionalPlane, _DynamicSectionalPlane, _DrawingObjectLoader, _ArrowLoader, _DimensionalChainLoader, _FastenerLoader, _TextLoader, _DrawingPlaneLoader, _DXFGenerator, _ZoomController, _DimChainViewController, _DynamicTextsController, _font_urls) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.GraphicsThree3D = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  const utils = new _Utils.Utils(THREE);\n  const CANVAS_SIZE = {\n    width: 960,\n    height: 500\n  };\n  const CAMERA_POSITION_DISTANCE = 150;\n\n  class GraphicsThree3D {\n    //when clicked wireframe button here value changes\n    constructor() {\n      _defineProperty(this, \"isWireFrameOnly\", false);\n\n      _defineProperty(this, \"fontUrlArray\", _font_urls.FONT_URLS);\n\n      _defineProperty(this, \"loadedFonts\", {});\n\n      _defineProperty(this, \"materialTransparentOptions\", null);\n\n      _defineProperty(this, \"interactiveZ\", false);\n\n      _defineProperty(this, \"extrusionVectorInteractiveZ\", null);\n\n      _defineProperty(this, \"previousCameraPosition\", null);\n\n      _defineProperty(this, \"cameraTweenActive\", false);\n\n      _defineProperty(this, \"objectMaxX\", -Infinity);\n\n      _defineProperty(this, \"objectMaxY\", -Infinity);\n\n      _defineProperty(this, \"objectMaxZ\", -Infinity);\n\n      _defineProperty(this, \"objectMinX\", Infinity);\n\n      _defineProperty(this, \"objectMinY\", Infinity);\n\n      _defineProperty(this, \"objectMinZ\", Infinity);\n\n      _defineProperty(this, \"SceneCenterComputed\", false);\n\n      this.THREE = THREE;\n      this.OrbitControls = _OrbitControls.OrbitControls;\n      this.CSG = (0, _CSGMesh.CsgFactory)(THREE);\n      this.PolyBool = _polybooljs.default;\n      this.TWEEN = _tween.default;\n      this.renderer = this.initRenderer(CANVAS_SIZE);\n      this.camera = this.initCamera(CANVAS_SIZE);\n      this.scene = new this.THREE.Scene();\n      this.scene.background = new this.THREE.Color(0xffffff);\n      this.stencilScenes = [];\n      this.controls = new this.OrbitControls(this.camera, this.renderer.domElement);\n      this.globalJSONData = {};\n      this.jsonData = {};\n      this.WireframeGenerator = new _WireframeGenerator.WireframeGenerator(this);\n      this.SectionalPlane = new _SectionalPlane.SectionalPlane(this);\n      this.DynamicSectionalPlane = new _DynamicSectionalPlane.DynamicSectionalPlane(this);\n      this.ThreeObjects = [];\n      this.objectMidVector = new this.THREE.Vector3(); /// Drawing Plane\n\n      this.transparencyControlObjects = {\n        area: [],\n        line: [],\n        point: [],\n        text: [],\n        volume: []\n      };\n      this.visualisationControlObjects = {\n        arrow: [],\n        dimensionalChain: [],\n        drawingObject: [],\n        fastener: []\n      }; ///\n      ///\n\n      this.dynamicCutApplied = false; ///\n\n      this.zoomPaddingController = document.getElementById(\"zoom-padding\");\n      this.zoomPaddingController.addEventListener(\"change\", () => this.zoomDrawing());\n      this.DimensionalChainIntersectionDetection = new _DimensionalChainIntersectionDetection.DimensionalChainIntersectionDetection();\n      this.dxfGenerator = new _DXFGenerator.DXFGenerator(this);\n      this.utils = utils;\n      this.DimChainViewController = new _DimChainViewController.DimChainViewController();\n      this.DynamicTextsController = new _DynamicTextsController.DynamicTextsController();\n      this.TextLoader = new _TextLoader.TextLoader(this);\n      this.DrawingObjectLoader = new _DrawingObjectLoader.DrawingObjectLoader(this);\n      this.ArrowLoader = new _ArrowLoader.ArrowLoader(THREE, this.fontUrlArray, this);\n      this.DimensionalChainLoader = new _DimensionalChainLoader.DimensionalChainLoader(this);\n      this.FastenerLoader = new _FastenerLoader.FastenerLoader(THREE, this);\n      this.DrawingPlaneLoader = new _DrawingPlaneLoader.DrawingPlaneLoader(this);\n      this.ZoomController = new _ZoomController.ZoomController(this); ///\n\n      this.drawingPlaneZoomStateDimChain = {\n        initialFactors: {},\n        initialTextSizes: {},\n        initialComponentSizes: {},\n        finalFactorMax: 0,\n        finalFactorMin: Infinity\n      }; ///\n\n      this.animate();\n    }\n\n    getGraphicsCanvas() {\n      return this.renderer.domElement;\n    }\n\n    initRenderer(canvasSize) {\n      let {\n        width,\n        height\n      } = canvasSize;\n      let renderer = new this.THREE.WebGLRenderer({\n        antialias: true,\n        preserveDrawingBuffer: true\n      });\n      renderer.setSize(width, height);\n      renderer.domElement.id = 'threeCanvas';\n      renderer.domElement.style = \"position: absolute; width: 60%; height: 50%;\";\n      renderer.localClippingEnabled = true;\n      renderer.autoClear = false;\n      return renderer;\n    }\n\n    initCamera(canvasSize) {\n      const CAMERA_VIEW_SIZE = 5000;\n      const CAMERA_DEPTH = 5000;\n      const MULTIPLIER = 64;\n      let {\n        width,\n        height\n      } = canvasSize;\n      let cameraAspectRatio = width / height;\n      return new this.THREE.OrthographicCamera(CAMERA_VIEW_SIZE * cameraAspectRatio / -MULTIPLIER, CAMERA_VIEW_SIZE * cameraAspectRatio / MULTIPLIER, CAMERA_VIEW_SIZE / MULTIPLIER, CAMERA_VIEW_SIZE / -MULTIPLIER, -CAMERA_DEPTH, CAMERA_DEPTH);\n    }\n\n    makeTransparentClone(mesh) {\n      let transparentClone = new this.THREE.Mesh(); // transparentClone.applyMatrix4(mesh.matrix);\n\n      return transparentClone;\n    }\n\n    initWireframeObject(mesh, transparentMesh, jsonData, scene) {\n      let objectData = {\n        id: \"none\",\n        //TODO: Apply ID\n        data: jsonData,\n        sceneObject: mesh,\n        targetScene: scene\n      };\n\n      if (transparentMesh) {\n        mesh.IsCSGObject = true;\n        objectData.IsCSGObject = true;\n        objectData.sceneObjectTransparentCSG = transparentMesh;\n        objectData.type = \"polygon\"; // \"polygon\" is for wireframe to work // TODO: change it\n\n        mesh.customTypeName = \"polygon\";\n        transparentMesh.customTypeName = \"polygon\";\n      }\n\n      this.ThreeObjects.push(objectData);\n    }\n\n    makeWireframeFromPoints(points, extrusionVector, lineProps) {\n      const RENDER_ORDER = -99;\n      let wireframes = [];\n      let vectors = points.map(point => new this.THREE.Vector3(point.x, point.y, point.z));\n      let wireframe = this.utils.drawConnectedLines(vectors, lineProps, true);\n      wireframe.renderOrder = RENDER_ORDER;\n      wireframes.push(wireframe);\n      let wireframeClone = wireframe.clone();\n      wireframeClone.position.add(extrusionVector);\n      wireframes.push(wireframeClone);\n      wireframes.push(...this.drawWireframeSideLines(vectors, extrusionVector, lineProps));\n      return wireframes;\n    }\n\n    drawWireframeSideLines(points, extrusionVector, lineProps, renderOrder = -99) {\n      let sideLines = [];\n\n      for (let i = 0; i < points.length; i++) {\n        let indexThree = i + 2;\n        if (indexThree >= points.length) indexThree -= points.length;\n        let indexTwo = indexThree - 1;\n        if (indexTwo < 0) indexTwo += points.length;\n        let indexOne = indexTwo - 1;\n        if (indexOne < 0) indexOne += points.length;\n        let pointOne = points[indexOne].clone().normalize();\n        let pointTwo = points[indexTwo].clone().normalize();\n        let pointThree = points[indexThree].clone().normalize();\n        let vectorOne = pointTwo.clone().sub(pointOne);\n        let vectorTwo = pointThree.clone().sub(pointTwo);\n        const DELTA = 0.001;\n        let angleBetween = vectorOne.angleTo(vectorTwo);\n        if (angleBetween > Math.PI) angleBetween -= Math.PI;\n\n        if (angleBetween > Math.PI / 10 && angleBetween < Math.PI - DELTA) {\n          let pointOne = points[indexTwo];\n          let pointTwo = points[indexTwo].clone().add(extrusionVector);\n\n          if (pointOne.distanceTo(pointTwo) > 0) {\n            let sideLine = this.utils.drawConnectedLines([pointOne, pointTwo], lineProps);\n            sideLine.renderOrder = renderOrder;\n            sideLines.push(sideLine);\n          }\n        }\n      }\n\n      return sideLines;\n    }\n\n    makeWireframes(regions, extrusionVector, lineProps) {\n      let wireframes = [];\n      regions.forEach(region => {\n        let finalPolygons = region.positiveRegions.length > 0 || region.positiveRegions.length > 0 && region.negativeRegions.length > 0 ? BooleanOperations.makePolygonsFromPosAndNegRegions(region) : {\n          regions: []\n        };\n        finalPolygons.regions.forEach(polygonRegion => {\n          let vectors = polygonRegion.reduce((vectorArr, coords) => {\n            let newPoint = new this.THREE.Vector3(coords[0], coords[1], 0);\n            newPoint.applyEuler(region.eulerTwo);\n            newPoint.add(region.offsetVector);\n            vectorArr.push(newPoint);\n            return vectorArr;\n          }, []);\n          wireframes.push(...this.makeWireframeFromPoints(vectors, extrusionVector, lineProps));\n        });\n      });\n      return wireframes;\n    }\n\n    makePositionedExtrusion(geoData) {\n      let geometry = this.makeExtrudeGeoRaw(geoData.points2D, geoData.depth);\n      geometry.translate(...geoData.translation);\n      this.utils.skewExtrudedGeometry(geometry, geoData.skew);\n      return geometry;\n    }\n\n    makeExtrudeGeoRaw(points2D, depth) {\n      let shape = new this.THREE.Shape(points2D);\n      let extrudeSettings = {\n        depth,\n        bevelEnabled: false\n      };\n      let geometry = new this.THREE.ExtrudeGeometry(shape, extrudeSettings);\n      return geometry;\n    }\n\n    objectRotatesPlanar(objectData, isEllipse = false) {\n      let rotationAxis = this.utils.initVector3(objectData.objectGeneration.rotationAxis).normalize();\n      let crossVector;\n\n      if (isEllipse) {\n        let directionVectorOne = this.utils.initVector3(objectData.directionVector1);\n        let directionVectorTwo = this.utils.initVector3(objectData.directionVector2);\n        crossVector = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n      } else {\n        let points = objectData.points;\n        crossVector = this.utils.getCrossVectorFromPoints(points);\n      }\n\n      return Math.abs(crossVector.dot(rotationAxis)) == 1;\n    }\n\n    objectExtrudesPlanar(objectData, isEllipse = false) {\n      let extrusionVector = this.utils.initVector3(objectData.objectGeneration.extrusionVector).normalize();\n      let crossVector;\n\n      if (isEllipse) {\n        let directionVectorOne = this.utils.initVector3(objectData.directionVector1);\n        let directionVectorTwo = this.utils.initVector3(objectData.directionVector2);\n        crossVector = this.utils.getCrossVector(directionVectorOne, directionVectorTwo);\n      } else {\n        let points = objectData.points;\n        crossVector = this.utils.getCrossVectorFromPoints(points);\n      }\n\n      return crossVector.dot(extrusionVector) == 0;\n    }\n\n    drawObject(jsonData) {\n      let drawingObjects = jsonData.drawingObjects;\n      drawingObjects.forEach(drawingObject => {\n        let components = drawingObject.components;\n        let properties = drawingObject.properties;\n        if (components.texts) this.TextLoader.loadTexts(properties.text, components.texts, jsonData, drawingObject.insertionPoints);\n        let isComplexVolume = this.utils.isComplexVolume(properties);\n        if (isComplexVolume) this.DrawingObjectLoader.drawComplexVolumes(drawingObject, jsonData);else this.DrawingObjectLoader.drawRegularObjects(drawingObject, jsonData);\n\n        if (components.sectionalPlanes) {\n          this.centerScene();\n          this.SceneCenterComputed = true;\n        }\n\n        if (drawingObject.dynamicSectionalPlanes) {\n          this.centerScene();\n          this.SceneCenterComputed = true;\n        }\n      });\n    }\n\n    addObjToTranspControl(object, type) {\n      switch (type) {\n        case \"area\":\n          this.transparencyControlObjects.area.push(object);\n          break;\n\n        case \"line\":\n          this.transparencyControlObjects.line.push(object);\n          break;\n\n        case \"point\":\n          this.transparencyControlObjects.point.push(object);\n          break;\n\n        case \"text\":\n          this.transparencyControlObjects.text.push(object);\n          break;\n\n        case \"volume\":\n          this.transparencyControlObjects.volume.push(object);\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    addObjectToVisControl(object, type) {\n      switch (type) {\n        case \"arrow\":\n          this.visualisationControlObjects.arrow.push(object);\n          break;\n\n        case \"dimensionalChain\":\n          this.visualisationControlObjects.dimensionalChain.push(object);\n          break;\n\n        case \"drawingObject\":\n          this.visualisationControlObjects.drawingObject.push(object);\n          break;\n\n        case \"fastener\":\n          this.visualisationControlObjects.fastener.push(object);\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    addObjectToScene(object, objectTransparent, insertionPoints, jsonData, transpType, visType, objectData = null) {\n      if (insertionPoints) insertionPoints.forEach(insertionPoint => {\n        let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n        let newMaterial = object.material.length ? object.material.map(mat => mat.clone()) : object.material.clone();\n        let newObject = object.clone();\n        newObject.material = newMaterial;\n        newObject.onBeforeRender = object.onBeforeRender;\n        newObject.stencilNeeded = object.stencilNeeded ? object.stencilNeeded : false;\n        newObject.addToDXF = object.addToDXF ? object.addToDXF : false;\n        newObject.dxfLayer = object.dxfLayer ? object.dxfLayer : \"No Layer\";\n        let newObjectTransparent = objectTransparent ? objectTransparent.clone() : null;\n        copyObjectWireframes(newObject, object);\n        if (newObjectTransparent) copyObjectWireframes(newObjectTransparent, objectTransparent);\n        let rotationAngle = insertionPoint.rotationAngle;\n        let pivotPoint = new this.THREE.Object3D();\n\n        if (rotationAngle) {\n          pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n          pivotPoint.attach(newObject);\n          if (newObjectTransparent) pivotPoint.attach(newObjectTransparent);\n          rotationAngle = rotationAngle * Math.PI / 180;\n          let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n          pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n        } else {\n          pivotPoint.attach(newObject);\n          if (newObjectTransparent) pivotPoint.attach(newObjectTransparent);\n        }\n\n        pivotPoint.position.add(insertionVector);\n        pivotPoint.updateMatrixWorld();\n        let regularObject = pivotPoint.children[0];\n        let transparentObject = newObjectTransparent ? pivotPoint.children[1] : null;\n        pivotPoint.children.forEach(child => {\n          child.applyMatrix4(pivotPoint.matrixWorld);\n        });\n        regularObject.isDrawingObject = true; // Used for drawingPlanes\n\n        this.initWireframeObject(regularObject, transparentObject, jsonData, this.scene);\n        this.scene.add(regularObject); ///\n\n        this.ZoomController.getDrawingObjectWireframes(regularObject);\n        if (transparentObject) this.ZoomController.getDrawingObjectWireframes(transparentObject); ///\n\n        if (objectData && objectData.components.sectionalPlanes) this.SectionalPlane.addToSectionalPlanes(insertionPoint, regularObject, objectData);\n\n        if (objectData && objectData.dynamicSectionalPlanes) {\n          objectData.dynamicSectionalPlanes.forEach(planeID => {\n            this.DynamicSectionalPlane.sectionalPlanes.forEach(sectionalPlane => {\n              if (planeID === sectionalPlane.id) {\n                sectionalPlane.targets.drawingObjects.push({\n                  object: regularObject,\n                  clippingPlanes: objectData.dynamicSectionalPlanes\n                });\n              }\n            });\n          });\n        }\n\n        if (object.dxfPoints) object.dxfPoints.forEach(ptsData => {\n          ptsData.pointPairs.forEach(pair => pair.map(pt => pt.applyMatrix4(regularObject.matrix)));\n          this.dxfGenerator.wireframePoints.push({ ...ptsData,\n            object: regularObject\n          });\n        }); ///\n\n        regularObject.children.forEach(child => child.applyMatrix4(regularObject.matrix));\n        if (transparentObject) transparentObject.children.forEach(child => child.applyMatrix4(regularObject.matrix)); ///\n\n        this.addObjToTranspControl(regularObject, transpType);\n        this.addObjectToVisControl(regularObject, visType);\n      });\n\n      function copyObjectWireframes(ObjectNew, objectOld) {\n        ObjectNew.children = objectOld.children.map(child => {\n          let childClone = child.clone();\n          if (child.needsZoomControl) childClone.needsZoomControl = true;\n          if (child.isLine) childClone.isLine = true;\n          if (child.pointsStartEnd) childClone.pointsStartEnd = child.pointsStartEnd;\n          return childClone;\n        });\n      }\n    }\n\n    getThreeSideFaceObjects(object) {\n      object.updateMatrixWorld();\n      let [doubleMaterial, frontMaterial, backMaterial] = this.getThreeSideFaceMaterials(object);\n      let geometry = object.geometry.clone();\n      let frontSideObject = new this.THREE.Mesh(geometry);\n      frontSideObject.applyMatrix4(object.matrix);\n      let backSideObject = new this.THREE.Mesh(geometry);\n      backSideObject.applyMatrix4(object.matrix);\n      let doubleSideObject = new this.THREE.Mesh(geometry);\n      doubleSideObject.applyMatrix4(object.matrix);\n      doubleSideObject = this.getPositionedObject(doubleSideObject, doubleMaterial);\n      frontSideObject = this.getPositionedObject(frontSideObject, frontMaterial);\n      backSideObject = this.getPositionedObject(backSideObject, backMaterial);\n      return [doubleSideObject, frontSideObject, backSideObject];\n    }\n\n    getPositionedObject(object, material) {\n      let bsp = this.CSG.fromMesh(object);\n      let result = this.CSG.toMesh(bsp, new this.THREE.Matrix4());\n      result.material = material;\n      return result;\n    }\n\n    getThreeSideFaceMaterials(object) {\n      let objectMaterial = object.material.length > 0 ? object.material.map(mat => {\n        let newMat = mat.clone();\n        return newMat;\n      }) : object.material.clone();\n      let frontMaterial;\n\n      if (object.material.length > 0) {\n        frontMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.FrontSide;\n          return newMat;\n        });\n      } else {\n        frontMaterial = objectMaterial.clone();\n        frontMaterial.side = this.THREE.FrontSide;\n      }\n\n      let backMaterial;\n\n      if (object.material.length > 0) {\n        backMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.BackSide;\n          return newMat;\n        });\n      } else {\n        backMaterial = objectMaterial.clone();\n        backMaterial.side = this.THREE.BackSide;\n      }\n\n      let doubleMaterial;\n\n      if (object.material.length > 0) {\n        doubleMaterial = objectMaterial.map(mat => {\n          let newMat = mat.clone();\n          newMat.side = this.THREE.DoubleSide;\n          return newMat;\n        });\n      } else {\n        doubleMaterial = objectMaterial.clone();\n        doubleMaterial.side = this.THREE.DoubleSide;\n      }\n\n      return [doubleMaterial, frontMaterial, backMaterial];\n    }\n\n    getTransformedObjects(objects, insertionPoint) {\n      let rotationAngle = insertionPoint.rotationAngle;\n      let insertionVector = this.utils.initVector3(insertionPoint.insertionVector);\n      let pivotPoint = new this.THREE.Object3D();\n      let newObjects = [];\n      objects.forEach(object => {\n        let newObject = object.clone();\n        newObject.onBeforeRender = object.onBeforeRender;\n        newObjects.push(newObject);\n      });\n\n      if (rotationAngle) {\n        pivotPoint.position.set(insertionPoint.rotationPoint.x, insertionPoint.rotationPoint.y, insertionPoint.rotationPoint.z);\n        newObjects.forEach(obj => pivotPoint.attach(obj));\n        rotationAngle = rotationAngle * Math.PI / 180;\n        let rotationAxis = this.utils.initVector3(insertionPoint.rotationAxis);\n        pivotPoint.rotateOnAxis(rotationAxis, rotationAngle);\n      } else newObjects.forEach(obj => pivotPoint.attach(obj));\n\n      pivotPoint.position.add(insertionVector);\n      pivotPoint.updateMatrixWorld();\n      newObjects.forEach(obj => obj.applyMatrix4(pivotPoint.matrixWorld));\n      return newObjects;\n    }\n\n    getlineMeshMaterial(properties) {\n      return properties && properties.line ? this.getMaterialFromColor(properties.line.color) : null;\n    }\n\n    getPointMaterial(properties) {\n      return properties && properties.point ? this.getMaterialFromColor(properties.point.color) : null;\n    }\n\n    getAreaMaterial(mesh, properties) {\n      let objectMaterial;\n\n      if (properties && properties.area) {\n        let colorProps = properties.area.color;\n\n        if (properties.area.hatching.type === 2) {\n          /// TODO: might need to optimize this\n          let localClone = new this.THREE.Mesh(mesh.geometry.clone());\n          this.scene.add(localClone);\n          this.centerScene();\n          let objectWidth = this.objectMaxX - this.objectMinX;\n          let objectHeight = this.objectMaxY - this.objectMinY;\n          this.scene.remove(localClone); ///\n\n          let canvasSize = Math.max(objectWidth, objectHeight);\n          this.normalizeGeometryUVs(mesh.geometry, canvasSize);\n          let hatchingProps = {\n            areaColor: colorProps,\n            hatchColor: properties.area.hatching.color,\n            hatchSpacing: properties.area.hatching.factor,\n            hatchThickness: properties.area.hatching.thickness,\n            orientationVector: this.utils.normalizeVector(properties.area.hatching.orientationVector)\n          };\n          objectMaterial = this.makeCanvasMaterial(canvasSize, hatchingProps); // objectMaterial.map.wrapS = objectMaterial.map.wrapT = this.THREE.RepeatWrapping;\n          // objectMaterial.map.repeat.set( 1 / objectWidth, 1 / objectHeight );\n          // objectMaterial.map.offset.set( 0.5, 0.5 );\n        } else if (properties.area.hatching.type === 1) {\n          objectMaterial = this.getMaterialFromColor(colorProps);\n        } else objectMaterial = null;\n      } else objectMaterial = null;\n\n      return objectMaterial;\n    }\n\n    normalizeGeometryUVs(geometry, canvasSize) {\n      let uvs = geometry.faceVertexUvs[0];\n      let maxX = -Infinity;\n      let maxY = -Infinity;\n      let minX = Infinity;\n      let minY = Infinity;\n\n      for (let uvArr of uvs) {\n        for (let uv of uvArr) {\n          if (uv.x > maxX) maxX = uv.x;\n          if (uv.y > maxY) maxY = uv.y;\n          if (uv.x < minX) minX = uv.x;\n          if (uv.y < minY) minY = uv.y;\n        }\n      }\n\n      for (let uvArr of uvs) {\n        for (let uv of uvArr) {\n          uv.x = (uv.x - minX) / canvasSize;\n          uv.y = (uv.y - minY) / canvasSize;\n        }\n      }\n    }\n\n    getVolumeMaterialsIndexFaces(mesh, properties) {\n      let objectMaterial;\n\n      if (properties && properties.volume && mesh.geometry.faces) {\n        let faceSideColor = properties.volume.color.faceSide;\n        let edgeSideColor = properties.volume.color.edgeSide;\n        let endSideColor = properties.volume.color.endSide;\n        let cutSideColor = properties.volume.color.cutSide;\n        let faceSideMaterial = this.getMaterialFromColor(faceSideColor);\n        let edgeSideMaterial = this.getMaterialFromColor(edgeSideColor);\n        let endSideMaterial = this.getMaterialFromColor(endSideColor);\n        let cutSideMaterial = this.getMaterialFromColor(cutSideColor);\n        objectMaterial = [faceSideMaterial, edgeSideMaterial, endSideMaterial, cutSideMaterial];\n        this.indexMeshFaces(mesh, properties);\n      } else {\n        objectMaterial = null;\n      }\n\n      return objectMaterial;\n    }\n\n    indexMeshFaces(mesh, properties) {\n      let faceSideNormal = this.utils.initVector3(properties.volume.faceSideNormalVector).normalize();\n      let grainDirection = this.utils.initVector3(properties.volume.layers[0].grainDirectionVector).normalize();\n      mesh.geometry.faces.forEach(face => {\n        if (Math.abs(face.normal.dot(faceSideNormal)) > 0.9999) {\n          face.materialIndex = 0;\n        } else if (Math.abs(face.normal.dot(grainDirection)) < 0.0001) {\n          face.materialIndex = 1;\n        } else if (Math.abs(face.normal.dot(grainDirection)) > 0.9999) {\n          face.materialIndex = 2;\n        } else {\n          face.materialIndex = 3;\n        }\n      });\n    }\n\n    centerScene() {\n      let box = new this.THREE.BoxHelper(this.scene, 0xff0000); // this.scene.add(box);\n\n      let boxVertices = this.utils.getVerticesFromBufferGeometry(box.geometry);\n      let bbox = new this.THREE.Box3().setFromPoints(boxVertices);\n      this.objectMaxX = bbox.max.x;\n      this.objectMaxY = bbox.max.y;\n      this.objectMaxZ = bbox.max.z;\n      this.objectMinX = bbox.min.x;\n      this.objectMinY = bbox.min.y;\n      this.objectMinZ = bbox.min.z;\n      let objectWidth = this.objectMaxX - this.objectMinX;\n      let objectHeight = this.objectMaxY - this.objectMinY;\n      let objectDepth = this.objectMaxZ - this.objectMinZ;\n      let objectMidX = this.objectMinX + objectWidth / 2;\n      let objectMidY = this.objectMinY + objectHeight / 2;\n      let objectMidZ = this.objectMinZ + objectDepth / 2;\n      this.objectMidVector.set(objectMidX, objectMidY, objectMidZ);\n      this.controls.target.copy(this.objectMidVector);\n      this.controls.update();\n      this.camera.position.copy(new this.THREE.Vector3(-1, 1, 1).normalize().multiplyScalar(CAMERA_POSITION_DISTANCE)).add(this.objectMidVector);\n      this.camera.lookAt(this.objectMidVector);\n      this.camera.updateProjectionMatrix();\n    }\n\n    zoomDrawing(inplace = true, isoView = false) {\n      let bboxPoints = [new this.THREE.Vector3(this.objectMinX, this.objectMinY, this.objectMaxZ), new this.THREE.Vector3(this.objectMaxX, this.objectMinY, this.objectMaxZ), new this.THREE.Vector3(this.objectMaxX, this.objectMaxY, this.objectMaxZ), new this.THREE.Vector3(this.objectMinX, this.objectMaxY, this.objectMaxZ), new this.THREE.Vector3(this.objectMinX, this.objectMinY, this.objectMinZ), new this.THREE.Vector3(this.objectMaxX, this.objectMinY, this.objectMinZ), new this.THREE.Vector3(this.objectMaxX, this.objectMaxY, this.objectMinZ), new this.THREE.Vector3(this.objectMinX, this.objectMaxY, this.objectMinZ)];\n      let objPoints = [];\n      this.DimensionalChainIntersectionDetection.chainsForZoom.forEach(([child]) => {\n        if (child.geometry && child.geometry.vertices) {\n          child.updateMatrixWorld(false, false);\n          child.geometry.vertices.forEach(vertex => {\n            objPoints.push(vertex.clone().applyMatrix4(child.matrixWorld));\n          });\n        }\n      });\n      this.scene.traverse(child => {\n        if (child.geometry) {\n          child.updateMatrixWorld(false, false);\n\n          if (child.geometry.vertices) {\n            child.geometry.vertices.forEach(vertex => {\n              objPoints.push(vertex.clone().applyMatrix4(child.matrixWorld));\n            });\n          } else if (!child.isStencilPlane && child.geometry.attributes && child.geometry.attributes.position) {\n            let vertexBufferArray = child.geometry.attributes.position.array;\n\n            for (let i = 0; i < vertexBufferArray.length; i += 3) {\n              let newVertex = new THREE.Vector3(vertexBufferArray[i], vertexBufferArray[i + 1], vertexBufferArray[i + 2]);\n              newVertex.applyMatrix4(child.matrixWorld);\n              objPoints.push(newVertex);\n            }\n          }\n        }\n      });\n      let canvasSize = this.renderer.getSize(new this.THREE.Vector2());\n      let padding = parseInt(this.zoomPaddingController.value);\n      let frameWidth = canvasSize.x;\n      let frameHeight = canvasSize.y;\n      let widthHalf = frameWidth / 2;\n      let heightHalf = frameHeight / 2;\n      this.camera.zoom = 1;\n      this.camera.updateProjectionMatrix();\n      let [minWidth, minHeight, maxWidth, maxHeight] = getExtremePts(objPoints, this.camera);\n      let [minXScene, minYScene, maxXScene, maxYScene] = getExtremePts(bboxPoints, this.camera);\n      frameWidth -= padding;\n      frameHeight -= padding;\n      let midBox = new this.THREE.Vector2(minXScene + (maxXScene - minXScene) / 2, minYScene + (maxYScene - minYScene) / 2);\n      let midObj = new this.THREE.Vector2(minWidth + (maxWidth - minWidth) / 2, minHeight + (maxHeight - minHeight) / 2);\n      let midDelta = new this.THREE.Vector2().subVectors(midObj, midBox);\n      let midDeltaUnProj = new THREE.Vector3((midDelta.x + canvasSize.x / 2) / canvasSize.x * 2 - 1, (midDelta.y + canvasSize.y / 2) / canvasSize.y * 2 - 1, (this.camera.near + this.camera.far) / (this.camera.near - this.camera.far));\n      midDeltaUnProj.unproject(this.camera);\n      midDeltaUnProj.projectOnPlane(new this.THREE.Vector3(-1, 1, 1).normalize());\n\n      if (isoView) {\n        if (!this.midOffsetVector) this.midOffsetVector = midDeltaUnProj.clone().add(new this.THREE.Vector3(-1, 1, 1).normalize().multiplyScalar(CAMERA_POSITION_DISTANCE));\n        this.camera.position.copy(this.midOffsetVector);\n      }\n\n      let scale = isoView ? Math.max((maxWidth - minWidth) / frameWidth, (maxHeight - minHeight) / frameHeight) : Math.max((maxXScene - minXScene) / frameWidth, (maxYScene - minYScene) / frameHeight);\n      if (inplace) this.camera.zoom = 1 / scale;\n      return 1 / scale;\n\n      function getExtremePts(pts, camera) {\n        let minX = Infinity;\n        let minY = Infinity;\n        let maxX = -Infinity;\n        let maxY = -Infinity;\n        pts.map(point => {\n          point.project(camera);\n          point.x = point.x * widthHalf + widthHalf;\n          point.y = point.y * heightHalf + heightHalf;\n          point.z = 0;\n          return point;\n        }).forEach(pt => {\n          if (pt.x > maxX) maxX = pt.x;\n          if (pt.y > maxY) maxY = pt.y;\n          if (pt.x < minX) minX = pt.x;\n          if (pt.y < minY) minY = pt.y;\n        });\n        return [minX, minY, maxX, maxY];\n      }\n    }\n\n    clearScene() {\n      this.clearGraphicsState();\n      this.DrawingObjectLoader.WireframeGenerator.clearWireframeState();\n      this.DynamicSectionalPlane.clearSectionalPlanes();\n      this.ZoomController.clearZoomState();\n      this.DimChainViewController.clearState();\n      this.DynamicTextsController.resetState();\n      this.dxfGenerator.clearObjectData();\n\n      while (this.scene.children.length > 0) {\n        this.scene.remove(this.scene.children[this.scene.children.length - 1]);\n      }\n    }\n\n    clearGraphicsState() {\n      this.previousCameraPosition = null;\n      this.cameraTweenActive = false;\n      this.ThreeObjects = [];\n      this.objectMaxX = -Infinity;\n      this.objectMaxY = -Infinity;\n      this.objectMaxZ = -Infinity;\n      this.objectMinX = Infinity;\n      this.objectMinY = Infinity;\n      this.objectMinZ = Infinity;\n      this.midOffsetVector = null;\n      this.SceneCenterComputed = false;\n      this.stencilScenes = [];\n      this.transparencyControlObjects = {\n        area: [],\n        line: [],\n        point: [],\n        text: [],\n        volume: []\n      };\n      this.visualisationControlObjects = {\n        arrow: [],\n        dimensionalChain: [],\n        drawingObject: [],\n        fastener: []\n      };\n      this.DimensionalChainIntersectionDetection.meshArr = [];\n      this.DimensionalChainIntersectionDetection.chainsForZoom = [];\n      this.FastenerLoader.clearFastenerVisibilityState();\n    }\n\n    getRotationParameters(pts, extrusionNormalized) {\n      let midVector = this.getMidVector(pts);\n      let crossVector = this.getCrossVector(pts, extrusionNormalized);\n      let [eulerOne, eulerTwo] = this.utils.getEulerOneAndTwo(crossVector);\n      return [midVector, eulerOne, eulerTwo, crossVector];\n    }\n\n    getMidVector(pts) {\n      let maxX = -Infinity;\n      let maxY = -Infinity;\n      let maxZ = -Infinity;\n      let minX = Infinity;\n      let minY = Infinity;\n      let minZ = Infinity;\n      pts.forEach(point => {\n        if (point.x > maxX) maxX = point.x;\n        if (point.y > maxY) maxY = point.y;\n        if (point.z > maxZ) maxZ = point.z;\n        if (point.x < minX) minX = point.x;\n        if (point.y < minY) minY = point.y;\n        if (point.z < minZ) minZ = point.z;\n      });\n      let midX = minX + (maxX - minX) / 2;\n      let midY = minY + (maxY - minY) / 2;\n      let midZ = minZ + (maxZ - minZ) / 2;\n      let midVector = new this.THREE.Vector3(midX, midY, midZ);\n      return midVector;\n    }\n\n    getCrossVector(points, extrusionNormalized) {\n      let crossVector = this.utils.getCrossVectorFromPoints(points);\n      if (extrusionNormalized.dot(crossVector) < 0) crossVector.negate();\n      return crossVector;\n    }\n\n    getMaterialFromColor(colorProps) {\n      let {\n        color,\n        opacity\n      } = this.utils.getColorAndOpacity(colorProps);\n      let materialOptions = {\n        transparent: true,\n        color,\n        opacity,\n        side: this.THREE.DoubleSide\n      };\n      let material = new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    getVolumeMaterials(volumeColors) {\n      let faceSideColor = volumeColors.faceSide;\n      let edgeSideColor = volumeColors.edgeSide;\n      let endSideColor = volumeColors.endSide;\n      let cutSideColor = volumeColors.cutSide;\n      let faceSideMaterial = this.getMaterialFromColor(faceSideColor);\n      let edgeSideMaterial = this.getMaterialFromColor(edgeSideColor);\n      let endSideMaterial = this.getMaterialFromColor(endSideColor);\n      let cutSideMaterial = this.getMaterialFromColor(cutSideColor);\n      let materials = [faceSideMaterial, edgeSideMaterial, endSideMaterial, cutSideMaterial];\n      return materials;\n    }\n\n    indexVolumeFaces(geometry, faceSideNormal, grainDirection) {\n      geometry.faces.forEach(face => {\n        if (Math.abs(face.normal.dot(faceSideNormal)) == 1) {\n          face.materialIndex = 0;\n        } else if (face.normal.dot(grainDirection) == 0) {\n          face.materialIndex = 1;\n        } else if (Math.abs(face.normal.dot(grainDirection)) == 1) {\n          face.materialIndex = 2;\n        } else {\n          face.materialIndex = 3;\n        }\n      });\n    }\n\n    makeCircleWithEdges(radius, areaMaterial, borderMaterial, customParameters = null) {\n      let circleGeo;\n\n      if (customParameters) {\n        let thetaStart = customParameters.thetaStart;\n        let thetaEnd = customParameters.thetaEnd;\n        circleGeo = new this.THREE.CircleBufferGeometry(radius, 32, thetaStart, thetaEnd);\n        let radiusTwo = customParameters.radiusTwo;\n        circleGeo.applyMatrix4(new this.THREE.Matrix4().makeScale(1, radiusTwo / radius, 1));\n      } else {\n        circleGeo = new this.THREE.CircleBufferGeometry(radius, 32);\n      }\n\n      let areaMesh = new this.THREE.Mesh(circleGeo, areaMaterial);\n      let borderEdges = new this.THREE.EdgesGeometry(circleGeo);\n      let borderLine = new this.THREE.LineSegments(borderEdges, borderMaterial);\n      let circle = new this.THREE.Object3D();\n      circle.add(areaMesh);\n      circle.add(borderLine);\n      return circle;\n    }\n\n    makeArrow(radius, height, areaMaterial, borderMaterial) {\n      let coneGeo = new this.THREE.ConeBufferGeometry(radius, height, 32);\n      coneGeo.translate(0, -height / 2, 0);\n      let coneMesh = new this.THREE.Mesh(coneGeo, areaMaterial);\n      coneMesh.position.y = -height * 0.025;\n      let borderMesh = new this.THREE.Mesh(coneGeo, borderMaterial);\n      borderMesh.scale.multiplyScalar(1.05);\n      borderMesh.material.side = this.THREE.BackSide;\n      let cone = new this.THREE.Object3D();\n      cone.add(coneMesh);\n      cone.add(borderMesh);\n      return cone;\n    }\n\n    getLineMaterial(lineProps) {\n      let lineType = lineProps.type;\n      let lineColor = lineProps.color;\n      let lineMaterial;\n\n      if (lineType) {\n        if (lineType == 1) {\n          let materialOptions = {\n            color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n            transparent: true,\n            opacity: lineColor.alpha / 255\n          };\n          lineMaterial = new this.THREE.LineBasicMaterial(materialOptions);\n        } else {\n          lineMaterial = new this.THREE.LineDashedMaterial({\n            color: \"rgb(\" + lineColor.red + \", \" + lineColor.green + \", \" + lineColor.blue + \")\",\n            transparent: true,\n            opacity: lineColor.alpha / 255,\n            dashSize: lineType == 2 ? lineProps.factor : 0.2,\n            // TODO: might need to change this\n            gapSize: lineProps.factor ? lineProps.factor : 0\n          });\n        }\n      }\n\n      if (lineMaterial) lineMaterial.depthTest = false;else lineMaterial = new this.THREE.LineBasicMaterial({\n        transparent: true,\n        opacity: 0\n      });\n      return lineMaterial;\n    }\n\n    drawLine(pointOne, pointTwo, lineProps, forObjectGeneration = false) {\n      ///\n      // temporary solution. May be Fixed.\n      if (lineProps.type == 0 || lineProps.thickness == 0 || lineProps.color.alpha == 0) return new this.THREE.Mesh(); ///\n\n      let lineMaterial = this.getMaterialFromColor(lineProps.color);\n      let lineRadius = lineProps.thickness / 2;\n      let lineSize = pointOne.distanceTo(pointTwo);\n      let lineGeo = new this.THREE.CylinderGeometry(lineRadius, lineRadius, lineSize, 8);\n      lineGeo.rotateX(Math.PI / 2);\n      lineGeo.translate(0, 0, lineSize / 2); // if (!forObjectGeneration) lineGeo.merge(new this.THREE.SphereGeometry(lineRadius, 8, 8));\n\n      let lineMesh = new this.THREE.Mesh(lineGeo, lineMaterial);\n      lineMesh.position.copy(pointOne);\n      lineMesh.lookAt(pointTwo);\n      if (!forObjectGeneration) lineMesh.renderOrder = -101;\n      return lineMesh;\n    }\n\n    getEllipsePlanarPoints(ellipseData) {\n      const DELTA = 1e-4;\n      const CIRCLE_DETAIL = 64;\n      let radiusOne = ellipseData.radius1;\n      let radiusTwo = ellipseData.radius2;\n      let segmentHeight = ellipseData.segmentHeight;\n      let directionVectorOne = this.utils.initVector3(ellipseData.directionVector1).normalize();\n      let directionVectorTwo = this.utils.initVector3(ellipseData.directionVector2).normalize();\n      let circleVertices = [];\n\n      if (!directionVectorOne.dot(directionVectorTwo)) {\n        let crossVector = new this.THREE.Vector3().crossVectors(directionVectorOne, directionVectorTwo).normalize();\n        let [startAngle, endAngle] = getAnglesStartEnd(ellipseData, crossVector);\n        let [partOne, partTwo] = getPointParts(crossVector, startAngle, endAngle);\n        circleVertices = [...partOne, ...partTwo];\n\n        if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n          let segmentAngle = Math.abs(endAngle - startAngle);\n          if (startAngle > endAngle) segmentAngle = 2 * Math.PI - segmentAngle;\n          if (segmentHeight) circleVertices = cutPointsWithHeight(circleVertices, crossVector, segmentAngle);else circleVertices.push(new this.THREE.Vector3());\n        }\n      }\n\n      return circleVertices; // Helper functions -->\n\n      function cutPointsWithHeight(circleVertices, crossVector, segmentAngle) {\n        let zeroVector = new THREE.Vector3();\n        let vectorOne = circleVertices[0].clone();\n        let vectorTwo = circleVertices[circleVertices.length - 1].clone();\n        let midNormal = getMidNormal(vectorOne, vectorTwo);\n        let cuttingPlane = getCuttingPlane(midNormal, crossVector);\n        let midRadius = getMidRadius(cuttingPlane, circleVertices);\n        let planeNormal = getPlaneNormal(crossVector, midNormal, vectorOne, vectorTwo);\n        let planeConstant = getPlaneConstant(midNormal, planeNormal, segmentHeight - midRadius);\n        let plane = new THREE.Plane(planeNormal, planeConstant);\n\n        if (segmentAngle > Math.PI + DELTA) {\n          if (segmentHeight > midRadius) {\n            let invertedPlane = new THREE.Plane(plane.normal.clone().negate(), plane.constant);\n            circleVertices = filterPointsAndUpdateEdges(circleVertices, invertedPlane);\n            let vecOne = invertedPlane.intersectLine(new THREE.Line3(zeroVector, vectorOne), new THREE.Vector3());\n            let vecTwo = invertedPlane.intersectLine(new THREE.Line3(zeroVector, vectorTwo), new THREE.Vector3());\n            if (vecOne) circleVertices.unshift(vecOne);\n            if (vecTwo) circleVertices.push(vecTwo);\n            circleVertices.push(new THREE.Vector3());\n            return circleVertices;\n          } else {\n            let invertedPlane = new THREE.Plane(plane.normal.clone().negate(), plane.constant);\n            return filterPointsAndUpdateEdges(circleVertices, invertedPlane);\n          }\n        } else if (segmentAngle < Math.PI - DELTA) {\n          if (segmentHeight < midRadius) {\n            circleVertices = filterPointsAndUpdateEdges(circleVertices, plane);\n            let vecOne = plane.intersectLine(new THREE.Line3(zeroVector, vectorOne), new THREE.Vector3());\n            let vecTwo = plane.intersectLine(new THREE.Line3(zeroVector, vectorTwo), new THREE.Vector3());\n            if (vecOne) circleVertices.unshift(vecOne);\n            if (vecTwo) circleVertices.push(vecTwo);\n            return circleVertices;\n          } else if (segmentHeight > midRadius) {\n            circleVertices.push(zeroVector);\n            return circleVertices;\n          } else return circleVertices;\n        } else {\n          if (circleVertices[Math.floor(circleVertices.length / 2)].dot(plane.normal.clone()) < 0) {\n            plane.normal.negate();\n          }\n\n          if (segmentHeight >= midRadius) return circleVertices;else return filterPointsAndUpdateEdges(circleVertices, plane);\n        }\n      }\n\n      function filterPointsAndUpdateEdges(circleVertices, plane) {\n        let edgePts = [];\n\n        for (let i = 0; i < circleVertices.length - 1; i++) {\n          let pt1 = circleVertices[i];\n          let pt2 = circleVertices[i + 1];\n          let dist1 = plane.distanceToPoint(pt1);\n          let dist2 = plane.distanceToPoint(pt2);\n\n          if (dist1 > 0 && dist2 < 0 || dist1 < 0 && dist2 > 0) {\n            let edgePt = new THREE.Vector3();\n            let line = new THREE.Line3(pt1, pt2);\n            plane.intersectLine(line, edgePt);\n            edgePts.push(edgePt);\n          }\n        }\n\n        circleVertices = circleVertices.filter(vertex => plane.distanceToPoint(vertex) >= 0);\n        let startPoint = circleVertices[0];\n        let endPoint = circleVertices[circleVertices.length - 1];\n\n        for (let pt of edgePts) {\n          if (startPoint.distanceTo(pt) < endPoint.distanceTo(pt)) circleVertices.unshift(pt);else circleVertices.push(pt);\n        }\n\n        return circleVertices;\n      }\n\n      function getPlaneConstant(midNormal, planeNormal, heightDelta) {\n        return midNormal.clone().multiplyScalar(heightDelta).projectOnVector(planeNormal).length() * Math.sign(heightDelta);\n      }\n\n      function getPlaneNormal(crossVector, midNormal, vectorOne, vectorTwo) {\n        let ptsDirection = new THREE.Vector3().copy(vectorOne).sub(vectorTwo).normalize();\n        let planeNormal = new THREE.Vector3().crossVectors(ptsDirection, crossVector).normalize();\n        if (planeNormal.dot(midNormal) < 0) planeNormal.negate();\n        return planeNormal;\n      }\n\n      function getCuttingPlane(midNormal, crossVector) {\n        let cuttingPlaneNormal = new THREE.Vector3().crossVectors(midNormal, crossVector).normalize();\n        return new THREE.Plane(cuttingPlaneNormal);\n      }\n\n      function getMidNormal(vectorOne, vectorTwo) {\n        let midVector = new THREE.Vector3().lerpVectors(vectorOne, vectorTwo, 0.5);\n        return new THREE.Vector3().copy(midVector).normalize();\n      }\n\n      function getMidRadius(cuttingPlane, circleVertices) {\n        let intersectPt = new THREE.Vector3();\n\n        for (let i = 0; i < circleVertices.length - 1; i++) {\n          let pt1 = circleVertices[i];\n          let pt2 = circleVertices[i + 1];\n          let dist1 = cuttingPlane.distanceToPoint(pt1);\n          let dist2 = cuttingPlane.distanceToPoint(pt2);\n\n          if (dist1 > 0 && dist2 < 0 || dist1 < 0 && dist2 > 0) {\n            let line = new THREE.Line3(pt1, pt2);\n            cuttingPlane.intersectLine(line, intersectPt);\n            break;\n          }\n        }\n\n        return intersectPt.length();\n      }\n\n      function getPointParts(crossVector, startAngle, endAngle) {\n        const EPSILON = 0.01;\n        let angleDelta = 2 * Math.PI / CIRCLE_DETAIL;\n        let partOne = [];\n        let partTwo = [];\n\n        for (let i = 0; i < 2 * Math.PI; i += angleDelta) {\n          let newVector = makeRotatedPoint(crossVector, i);\n          let localStartAngle = startAngle;\n          let localEndAngle = endAngle;\n\n          if (localStartAngle < localEndAngle) {\n            if (localEndAngle > 2 * Math.PI) {\n              localEndAngle -= 2 * Math.PI;\n              if (i >= -EPSILON && i <= localEndAngle + EPSILON) partTwo.push(newVector);\n              if (i >= localStartAngle - EPSILON && i <= 2 * Math.PI + EPSILON) partOne.push(newVector);\n            } else if (i >= localStartAngle - EPSILON && i <= localEndAngle + EPSILON) partOne.push(newVector);\n          } else {\n            if (localEndAngle < 0) {\n              localEndAngle = 2 * Math.PI + localEndAngle;\n              if (i >= localStartAngle - EPSILON && i <= localEndAngle + EPSILON) partOne.push(newVector);\n            } else {\n              if (i >= -EPSILON && i <= localEndAngle + EPSILON) partTwo.push(newVector);\n              if (i >= localStartAngle - EPSILON && i <= 2 * Math.PI + EPSILON) partOne.push(newVector);\n            }\n          }\n        }\n\n        return [partOne, partTwo];\n      }\n\n      function makeRotatedPoint(planeNormal, angle) {\n        let vecLen = radiusOne * radiusTwo / Math.sqrt(Math.pow(radiusOne * Math.sin(angle), 2) + Math.pow(radiusTwo * Math.cos(angle), 2));\n        let newVector = new THREE.Vector3().copy(directionVectorOne).multiplyScalar(vecLen).applyAxisAngle(planeNormal, angle);\n        return newVector;\n      }\n\n      function getAnglesStartEnd(ellipseData, crossVector) {\n        let startAngle = 0;\n        let endAngle = 2 * Math.PI;\n\n        if (ellipseData.segmentVector1 && ellipseData.segmentVector2) {\n          let segmentVectorOne = utils.initVector3(ellipseData.segmentVector1).normalize();\n          let segmentVectorTwo = utils.initVector3(ellipseData.segmentVector2).normalize();\n          let segmentCross = new THREE.Vector3().crossVectors(segmentVectorOne, segmentVectorTwo);\n          if (segmentCross.length() === 0) segmentCross = new THREE.Vector3().crossVectors(directionVectorOne, directionVectorTwo).normalize();\n          let segOneToSegTwo = Math.acos(utils.clampNum(segmentVectorOne.dot(segmentVectorTwo), -1, 1));\n          if (crossVector.dot(segmentCross) < 0) segOneToSegTwo = -segOneToSegTwo;\n          let dirOneToSegOne = Math.acos(directionVectorOne.dot(segmentVectorOne));\n          let dirSegCross = new THREE.Vector3().crossVectors(directionVectorOne, segmentVectorOne);\n          if (crossVector.dot(dirSegCross) < 0) dirOneToSegOne = 2 * Math.PI - dirOneToSegOne;\n          startAngle = dirOneToSegOne;\n          endAngle = dirOneToSegOne + segOneToSegTwo;\n        }\n\n        return [startAngle, endAngle];\n      } // <-- Helper functions\n\n    }\n\n    getRotationIndices(points, rotationVector, forEllipse = false) {\n      let addAngle = forEllipse ? 0 : Math.PI / 2;\n      let xAxis = new this.THREE.Vector3(1, 0, 0);\n      let pointsNum = points.length;\n      let startIndex = (addAngle + rotationVector.clone().normalize().angleTo(xAxis)) / (2 * Math.PI) * pointsNum;\n      let endIndex = (addAngle + Math.PI + rotationVector.clone().normalize().angleTo(xAxis)) / (2 * Math.PI) * pointsNum;\n\n      if (endIndex >= pointsNum) {\n        endIndex -= pointsNum;\n      }\n\n      startIndex = Math.floor(startIndex);\n      endIndex = Math.floor(endIndex);\n      return [startIndex, endIndex];\n    }\n\n    interpolateWithDistance(pointOne, pointTwo, distance) {\n      let difference = pointOne.distanceTo(pointTwo);\n      let points = [];\n\n      if (difference > distance) {\n        let proportion = difference / distance;\n\n        for (let i = 1; i < proportion; i++) {\n          let newPoint = new this.THREE.Vector3().lerpVectors(pointOne, pointTwo, i / proportion);\n          points.push(newPoint);\n        }\n      }\n\n      return points;\n    }\n\n    makeRotatedObjectWireframe(curvePoints, lineProps, rotationData, rotationObjectWireframes, forPolygon = false) {\n      let centerPoint = rotationData.positionPoint;\n      let rotationPitch = rotationData.rotationPitch;\n      let rotationAngle = rotationData.rotationAngle;\n      let rotationAxis = rotationData.rotationAxis; ///\n\n      let rotationPoints = curvePoints.map(point => point.clone()); ///\n\n      let renderOrders = [-99, -101];\n\n      for (let renderOrder of renderOrders) {\n        let startEdge = this.utils.drawConnectedLines(curvePoints, lineProps, true);\n        startEdge.renderOrder = renderOrder;\n        let endGeo = startEdge.geometry.clone();\n        rotationObjectWireframes.push(startEdge);\n        let endEdge = new this.THREE.Mesh(endGeo, startEdge.material);\n        endEdge.renderOrder = renderOrder;\n        rotationObjectWireframes.push(endEdge);\n        let yAxis = new this.THREE.Vector3(0, 1, 0);\n\n        if (!forPolygon) {\n          let edgeDetail = 64;\n          let numOfSegments = edgeDetail * Math.abs(rotationAngle / Math.PI / 2);\n          startEdge.rotateOnAxis(yAxis, Math.PI);\n          endEdge.rotateOnAxis(yAxis, Math.PI);\n          startEdge.geometry.translate(-centerPoint, 0, 0);\n          endEdge.rotateOnAxis(yAxis, -rotationAngle);\n          endEdge.geometry.translate(-centerPoint, -rotationPitch, 0);\n          let rotationPoints = curvePoints.map(point => point.clone().applyAxisAngle(yAxis, Math.PI));\n\n          for (let i = 0; i < rotationPoints.length; i++) {\n            let indexThree = i + 2;\n            if (indexThree >= rotationPoints.length) indexThree -= rotationPoints.length;\n            let indexTwo = indexThree - 1;\n            if (indexTwo < 0) indexTwo += rotationPoints.length;\n            let indexOne = indexTwo - 1;\n            if (indexOne < 0) indexOne += rotationPoints.length;\n            let pointOne = rotationPoints[indexOne].clone().normalize();\n            let pointTwo = rotationPoints[indexTwo].clone().normalize();\n            let pointThree = rotationPoints[indexThree].clone().normalize();\n            let vectorOne = pointTwo.clone().sub(pointOne);\n            let vectorTwo = pointThree.clone().sub(pointTwo);\n            let angleBetween = vectorOne.angleTo(vectorTwo);\n            if (angleBetween > Math.PI) angleBetween -= Math.PI;\n\n            if (angleBetween > Math.PI / 10) {\n              let localPoint = rotationPoints[indexTwo].clone();\n              let edgePoints = [];\n              localPoint.add(new this.THREE.Vector3(centerPoint, rotationPitch, 0));\n\n              for (let i = 0; i < numOfSegments + 1; i++) {\n                let newPoint = localPoint.clone().applyAxisAngle(yAxis, -i * rotationAngle / numOfSegments);\n                newPoint.sub(new this.THREE.Vector3(0, i * rotationPitch / numOfSegments, 0));\n                edgePoints.push(newPoint);\n              }\n\n              let edgeLine = this.utils.drawConnectedLines(edgePoints, lineProps);\n              edgeLine.renderOrder = renderOrder;\n              rotationObjectWireframes.push(edgeLine);\n            }\n          }\n        } else {\n          let edgeDetail = 32;\n          let numOfSegments = edgeDetail * Math.abs(rotationAngle / Math.PI / 2);\n          startEdge.geometry.translate(centerPoint, rotationPitch, 0);\n          endEdge.geometry.translate(centerPoint, 0, 0);\n          endEdge.rotateOnAxis(yAxis, -rotationAngle);\n          rotationPoints.forEach(point => {\n            let localPoint = point.clone();\n            let edgePoints = [];\n            localPoint.add(new this.THREE.Vector3(centerPoint, rotationPitch, 0));\n\n            for (let i = 0; i < numOfSegments + 1; i++) {\n              let newPoint = localPoint.clone().applyAxisAngle(yAxis, -i * rotationAngle / numOfSegments);\n              newPoint.sub(new this.THREE.Vector3(0, i * rotationPitch / numOfSegments, 0));\n              edgePoints.push(newPoint);\n            }\n\n            let edgeLine = this.utils.drawConnectedLines(edgePoints, lineProps);\n            edgeLine.renderOrder = renderOrder;\n            rotationObjectWireframes.push(edgeLine);\n          });\n        }\n      }\n    }\n\n    makeCanvasMaterial(canvasSize, hatchingProps) {\n      const POSITION_PIXEL_RATIO = 100;\n      const MAGIC_CONSTANT = -5; // TODO: FIND OUT REASON FOR THIS NUMBER!!!\n\n      let {\n        areaColor,\n        hatchColor,\n        orientationVector,\n        hatchSpacing,\n        hatchThickness\n      } = hatchingProps;\n      let correctedCanvasSize = canvasSize * POSITION_PIXEL_RATIO;\n      let correctedHatchSpacing = hatchSpacing * POSITION_PIXEL_RATIO;\n      let correctedHatchThickness = hatchThickness * POSITION_PIXEL_RATIO;\n      let canvasMaterial;\n\n      if (hatchSpacing && hatchThickness) {\n        let textureWidth = correctedCanvasSize;\n        let textureHeight = correctedCanvasSize;\n        let tangent = orientationVector.x / orientationVector.y;\n        let canvas = document.createElement(\"canvas\");\n        canvas.width = correctedCanvasSize;\n        canvas.height = correctedCanvasSize;\n        let ctx = canvas.getContext(\"2d\");\n        ctx.fillStyle = \"rgb(\" + areaColor.red + \", \" + areaColor.green + \", \" + areaColor.blue + \")\";\n        ctx.strokeStyle = \"rgb(\" + hatchColor.red + \", \" + hatchColor.green + \", \" + hatchColor.blue + \")\";\n        ctx.lineWidth = correctedHatchThickness;\n        ctx.fillRect(0, 0, textureWidth, textureHeight);\n        let stepSize = correctedHatchSpacing + correctedHatchThickness;\n        if (orientationVector.x !== 0 && orientationVector.y !== 0) stepSize += correctedHatchThickness;\n\n        for (let i = 0; i < (textureWidth + textureHeight) / correctedHatchSpacing; i++) {\n          if (orientationVector.y === 0) {\n            ctx.beginPath();\n            ctx.moveTo(0, i * stepSize);\n            ctx.lineTo(textureWidth, i * stepSize);\n            ctx.closePath();\n            ctx.stroke();\n          } else if (orientationVector.x === 0) {\n            ctx.beginPath();\n            ctx.moveTo(i * stepSize, 0);\n            ctx.lineTo(i * stepSize, textureHeight);\n            ctx.closePath();\n            ctx.stroke();\n          } else {\n            ctx.beginPath();\n            ctx.moveTo(i * stepSize * tangent, MAGIC_CONSTANT);\n            ctx.lineTo(MAGIC_CONSTANT, i * stepSize);\n            ctx.closePath();\n            ctx.stroke();\n          }\n        }\n\n        let canvasTexture = new this.THREE.CanvasTexture(canvas);\n        canvasMaterial = new this.THREE.MeshBasicMaterial({\n          map: canvasTexture,\n          side: this.THREE.DoubleSide\n        });\n      } else {\n        canvasMaterial = new this.THREE.MeshBasicMaterial({\n          color: new this.THREE.Color(`rgb(${areaColor.red}, ${areaColor.green}, ${areaColor.blue})`),\n          side: this.THREE.DoubleSide\n        });\n      }\n\n      return canvasMaterial;\n    }\n\n    alignObjectWithVectorsAndChangePos(textMesh, textInsertionPoint, normalVector, directionVector) {\n      this.utils.alignObjectWithVectors(textMesh, normalVector, directionVector);\n      textMesh.position.add(textInsertionPoint);\n    }\n\n    drawCustom(jsonDataGlobal) {\n      let jsonData = lodash.cloneDeep(jsonDataGlobal);\n      console.log(jsonData); // DynamicSectionalPlanesData is located in public directory\n\n      if (DynamicSectionalPlanesData) {\n        this.assignDynamicSectionalPlanes(jsonData, DynamicSectionalPlanesData);\n        this.DynamicSectionalPlane.initSectionalPlanes(DynamicSectionalPlanesData);\n      }\n\n      this.settingsModifyJSON(jsonData);\n      this.jsonData = jsonData;\n      if (jsonData.fasteners) this.FastenerLoader.drawFasteners();\n      if (jsonData.arrows) jsonData.arrows.forEach(arrowData => {\n        let meshArray = this.ArrowLoader.drawArrows(arrowData);\n        meshArray.forEach(mesh => {\n          let drawnObject = new Object();\n          drawnObject.id = arrowData.id;\n          drawnObject.type = \"arrow\";\n          drawnObject.data = arrowData;\n          drawnObject.sceneObject = mesh;\n          this.ThreeObjects.push(drawnObject);\n          this.scene.add(drawnObject.sceneObject);\n        });\n      });\n      if (jsonData.drawingObjects) this.drawObject(jsonData);\n\n      if (jsonData.dimensionalChains) {\n        this.DimensionalChainLoader.drawDimensionalChains(jsonData.dimensionalChains);\n        this.ZoomController.getDrawingTexts();\n      } /// TEMP?\n\n\n      this.centerScene(); // if ( !this.SceneCenterComputed ) this.centerScene();\n      ///\n\n      this.SectionalPlane.applySectionalPlanes();\n      this.DynamicSectionalPlane.applySectionalPlanes();\n      if (!this.SceneCenterComputed) this.centerScene();\n      if (!this.stencilScenes.length) this.stencilScenes.push(this.scene);\n\n      if (this.isWireFrameOnly) {\n        this.isWireFrameOnly = false;\n        this.ToggleWireframe();\n      }\n    }\n\n    assignDynamicSectionalPlanes(jsonData, dynamicSectionalPlanes) {\n      for (let dynamicSectionalPlane of dynamicSectionalPlanes) {\n        assignToObject(\"drawingObjects\", dynamicSectionalPlane);\n        assignToObject(\"fasteners\", dynamicSectionalPlane);\n        assignToObject(\"dimensionalChains\", dynamicSectionalPlane);\n        assignToObject(\"arrows\", dynamicSectionalPlane);\n      }\n\n      function assignToObject(objectType, dynamicSectionalPlane) {\n        if (dynamicSectionalPlane.targets[objectType] && jsonData[objectType]) {\n          if (dynamicSectionalPlane.targets.all) assignAllObjects(objectType, dynamicSectionalPlane);else assignSpecificObjects(objectType, dynamicSectionalPlane);\n        }\n      }\n\n      function assignAllObjects(objectType, dynamicSectionalPlane) {\n        for (let object of jsonData[objectType]) {\n          object.dynamicSectionalPlanes = object.dynamicSectionalPlanes ? object.dynamicSectionalPlanes : [];\n          object.dynamicSectionalPlanes.push(dynamicSectionalPlane.planeData.id);\n        }\n      }\n\n      function assignSpecificObjects(objectType, dynamicSectionalPlane) {\n        for (let targetID of dynamicSectionalPlane.targets[objectType]) {\n          for (let object of jsonData[objectType]) {\n            if (object.id === targetID) {\n              object.dynamicSectionalPlanes = object.dynamicSectionalPlanes ? object.dynamicSectionalPlanes : [];\n              object.dynamicSectionalPlanes.push(dynamicSectionalPlane.planeData.id);\n            }\n          }\n        }\n      }\n    }\n\n    addObjToDynamicSectionalPlanes(data, object, target) {\n      if (data && data.dynamicSectionalPlanes) {\n        for (let planeID of data.dynamicSectionalPlanes) {\n          for (let sectionalPlane of this.DynamicSectionalPlane.sectionalPlanes) {\n            if (planeID === sectionalPlane.id) {\n              sectionalPlane.targets[target].push({\n                object,\n                clippingPlanes: data.dynamicSectionalPlanes\n              });\n            }\n          }\n        }\n      }\n    }\n\n    settingsModifyJSON(jsonData) {\n      let settings = jsonData.settings;\n      this.fillPropsWithSettingProps(jsonData, settings, \"arrows\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"dimensionalChains\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"drawingObjects\");\n      this.fillPropsWithSettingProps(jsonData, settings, \"fasteners\");\n    }\n\n    fillPropsWithSettingProps(jsonData, settings, objType) {\n      let settingsType = objType.slice(0, objType.length - 1);\n      let objects = jsonData[objType];\n\n      if (settings[settingsType] && settings[settingsType].visualisation) {\n        let objectSettingProps = settings[settingsType].properties;\n        objects.forEach(obj => {\n          if (!obj.properties && objectSettingProps) obj.properties = objectSettingProps;else if (objectSettingProps) {\n            if (!obj.properties.area && objectSettingProps.area) obj.properties.area = objectSettingProps.area;\n            if (!obj.properties.line && objectSettingProps.line) obj.properties.line = objectSettingProps.line;\n            if (!obj.properties.text && objectSettingProps.text) obj.properties.text = objectSettingProps.text;\n            if (!obj.properties.volume && objectSettingProps.volume) obj.properties.volume = objectSettingProps.volume;\n            if (!obj.properties.point && objectSettingProps.point) obj.properties.point = objectSettingProps.point;\n            if (!obj.properties.intersectionArea && objectSettingProps.intersectionArea) obj.properties.intersectionArea = objectSettingProps.intersectionArea;\n            if (!obj.properties.intersectionLine && objectSettingProps.intersectionLine) obj.properties.intersectionLine = objectSettingProps.intersectionLine;\n            if (!obj.properties.fastener && objectSettingProps.fastener) obj.properties.fastener = objectSettingProps.fastener;\n            if (!obj.properties.axis && objectSettingProps.axis) obj.properties.axis = objectSettingProps.axis;\n            if (!obj.properties.addOn && objectSettingProps.addOn) obj.properties.addOn = objectSettingProps.addOn;\n          }\n        });\n      } else {\n        jsonData[objType] = null;\n      }\n    }\n\n    removeObject(type) {\n      this.stencilScenes.forEach(scene => {\n        this.ThreeObjects.forEach(object => {\n          scene.children.forEach(child => {\n            if (object.type == type && object.sceneObject.uuid == child.uuid) {\n              object.isOnScene = false;\n              scene.remove(child);\n            }\n          });\n        });\n\n        for (let index = 0; index < 50; index++) {\n          //there may be more than one in that type and to remove all off them loop is needed\n          for (let i = 0; i < scene.children.length; i++) {\n            if (scene.children[i].customTypeName == type) {\n              scene.remove(scene.children[i]);\n\n              for (let to = 0; to < this.ThreeObjects.length; to++) {\n                if (this.ThreeObjects[to].type == type) {\n                  this.ThreeObjects[to].isOnScene = false;\n                }\n              }\n            }\n          }\n        }\n      });\n    }\n\n    redrawObject(type) {\n      this.ThreeObjects.forEach(element => {\n        if (type == \"maskette\" && element.type == \"maskette\" && !element.isOnScene) {\n          this.scene.add(element.sceneObject);\n          element.isOnScene = true;\n        }\n      });\n    }\n\n    redrawCSGObject(element) {\n      let targetScene = element.targetScene;\n\n      if (element.IsCSGObject && !element.isOnScene) {\n        if (!this.isWireFrameOnly) targetScene.add(element.sceneObject);else targetScene.add(element.sceneObjectTransparentCSG);\n        element.isOnScene = true;\n      }\n    } /// Legacy compatibility -->\n\n\n    highlightX(value) {\n      this.interactiveZ = true;\n    }\n\n    highlightY(value) {\n      this.interactiveZ = true;\n    }\n\n    highlightZ(extrusionVectorInteractiveZ) {\n      this.interactiveZ = true;\n      this.extrusionVectorInteractiveZ = extrusionVectorInteractiveZ;\n    }\n\n    clearXHighlight() {\n      this.interactiveZ = false;\n    }\n\n    clearYHighlight() {\n      this.interactiveZ = false;\n    }\n\n    clearZHighlight() {\n      this.interactiveZ = false;\n    } /// Legacy compatibility -->\n\n\n    degreeToRadians(degree) {\n      return degree * Math.PI / 180;\n    }\n\n    radiansToDegree(radian) {\n      return radian * 180 / Math.PI;\n    }\n\n    animate() {\n      this.updateRendererAndCamera(this.renderer, this.camera);\n      this.updateChainVisualisation();\n      this.renderAllScenes();\n      window.requestAnimationFrame(() => {\n        this.animate();\n        this.TWEEN.update();\n      }); // setTimeout(() => {\n      //   that.animate(that)\n      //   that.TWEEN.update();\n      // }, 100);\n    }\n\n    renderAllScenes() {\n      this.renderer.clear();\n      this.stencilScenes.forEach(scene => {\n        this.renderer.render(scene, this.camera);\n        this.renderer.clearDepth();\n      });\n    }\n\n    updateRendererAndCamera(renderer, camera) {\n      let canvas = renderer.domElement;\n      let width = canvas.clientWidth;\n      let height = canvas.clientHeight;\n      let needResize = canvas.width !== width || canvas.height !== height || canvas.style.width !== width || canvas.style.height !== height;\n\n      if (needResize) {\n        renderer.setSize(width, height, false);\n        let multiplier = 64;\n        let cameraAspectRatio = width / height;\n        let cameraViewSize = 5000;\n        camera.left = -cameraViewSize * cameraAspectRatio / multiplier;\n        camera.right = cameraViewSize * cameraAspectRatio / multiplier;\n        camera.top = cameraViewSize / multiplier;\n        camera.bottom = -cameraViewSize / multiplier;\n        camera.updateProjectionMatrix();\n      }\n    }\n\n    makeCameraTween(direction, distance) {\n      let controls = this.controls;\n      let camera = this.camera;\n      let midVector = this.objectMidVector;\n      let currentPosition = { ...camera.position,\n        zoom: camera.zoom\n      };\n      let currentTarget = { ...controls.target\n      };\n      let finalTarget = { ...midVector\n      };\n      let targetsNotEqual = currentPosition.x != finalTarget.x && currentPosition.y != finalTarget.y && currentPosition.z != finalTarget.z;\n      if (direction === \"x\" && this.previousCameraPosition === \"y\") currentPosition.z += 5;\n      let changePosition;\n      this.cameraTweenActive = true;\n\n      if (direction === \"initial\") {\n        changePosition = { ...midVector.clone().add(new this.THREE.Vector3(-1, 1, 1).normalize().multiplyScalar(CAMERA_POSITION_DISTANCE))\n        };\n      } else if (direction === \"x\") {\n        changePosition = {\n          x: midVector.x - distance,\n          y: midVector.y,\n          z: midVector.z\n        };\n      } else if (direction === \"y\") {\n        changePosition = {\n          x: midVector.x,\n          y: midVector.y + distance,\n          z: midVector.z + 5\n        };\n      } else if (direction === \"z\") {\n        changePosition = {\n          x: midVector.x,\n          y: midVector.y,\n          z: midVector.z + distance\n        };\n      } else throw new Error(\"No direction is defined for tweening\");\n\n      changePosition.zoom = 1;\n      let speed = 350;\n      let type = this.TWEEN.Easing.Cubic.InOut;\n      let global = this;\n      let tween = new this.TWEEN.Tween(currentPosition).to(changePosition, speed).easing(type).onUpdate(function () {\n        camera.position.set(currentPosition.x, currentPosition.y, currentPosition.z);\n        camera.zoom = currentPosition.zoom;\n        camera.lookAt(midVector);\n        camera.updateProjectionMatrix();\n        controls.update();\n      }).onComplete(function () {\n        if (direction == \"x\") global.previousCameraPosition = \"x\";else if (direction == \"y\") {\n          camera.position.z -= 5;\n          camera.lookAt(midVector);\n          camera.updateProjectionMatrix();\n          controls.update();\n          global.previousCameraPosition = \"y\";\n        } else if (direction == \"z\") global.previousCameraPosition = \"z\";else if (direction == \"initial\") global.previousCameraPosition = \"inital\";\n        global.cameraTweenActive = false; ///\n\n        let startZoom = 1;\n        let finalZoom = global.zoomDrawing(false, direction === \"initial\");\n        let duration = 500;\n        let zoomDelta = finalZoom - startZoom;\n        let nextZoom;\n        let startTime = performance.now();\n        let timeDelta = 0;\n\n        let zoomCamera = zoom => {\n          if (zoom <= finalZoom) {\n            camera.zoom = zoom;\n            camera.updateProjectionMatrix();\n          }\n\n          if (zoom < finalZoom) requestAnimationFrame(currentTime => {\n            timeDelta = Math.min((currentTime - startTime) / duration, 1);\n            nextZoom = zoom + zoomDelta * timeDelta;\n            zoomCamera(Math.min(nextZoom, finalZoom));\n          });\n        };\n\n        zoomCamera(startZoom);\n      });\n\n      if (targetsNotEqual) {\n        new this.TWEEN.Tween(currentTarget).to(finalTarget, speed).easing(type).onUpdate(function () {\n          controls.target.set(currentTarget.x, currentTarget.y, currentTarget.z - 0.01);\n          camera.up = new global.THREE.Vector3(0, 1, 0);\n          camera.lookAt(controls.target);\n          controls.update();\n        }).chain(tween).start();\n      } else tween.start();\n    }\n\n    ChangeCameraPosition0() {\n      if (!this.cameraTweenActive) this.makeCameraTween(\"initial\");\n      this.interactiveZ = false;\n    }\n\n    ChangeCameraPosition1() {\n      this.ZoomController.resetScale();\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"x\", CAMERA_POSITION_DISTANCE);\n    }\n\n    ChangeCameraPosition2() {\n      this.ZoomController.resetScale();\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"y\", CAMERA_POSITION_DISTANCE);\n    }\n\n    ChangeCameraPosition3() {\n      this.ZoomController.resetScale();\n      this.interactiveZ = false;\n      if (!this.cameraTweenActive) this.makeCameraTween(\"z\", CAMERA_POSITION_DISTANCE);\n    }\n\n    changeFastenersVisibility() {\n      this.FastenerLoader.changeFastenersVisibility();\n    }\n\n    changeTextsZoomableStatus() {\n      this.ZoomController.changeTextsZoomableStatus();\n    }\n\n    changeTextsSize(action) {\n      if (action === 'increment') this.ZoomController.updateTextsScale(this.ZoomController.textScale + _ZoomController.ZoomController.getDefaultScaleAmount);else if (action === 'decrement') this.ZoomController.updateTextsScale(this.ZoomController.textScale - _ZoomController.ZoomController.getDefaultScaleAmount);\n    }\n\n    ToggleWireframe() {\n      this.isWireFrameOnly = !this.isWireFrameOnly;\n      this.removeObject(\"polygon\");\n      this.removeObject(\"ellipse\");\n      this.ThreeObjects.forEach(element => this.redrawCSGObject(element));\n    }\n\n    SendBase64PNGToServer() {\n      let posVector = this.objectMidVector;\n      let posVectorOne = posVector.clone().add(new THREE.Vector3(-1, 1, 1).normalize().multiplyScalar(CAMERA_POSITION_DISTANCE));\n      let posVectorTwo = posVector.clone().sub(new THREE.Vector3(CAMERA_POSITION_DISTANCE, 0, 0));\n      let posVectorThree = posVector.clone().add(new THREE.Vector3(0, CAMERA_POSITION_DISTANCE, 0));\n      let posVectorFour = posVector.clone().add(new THREE.Vector3(0, 0, CAMERA_POSITION_DISTANCE));\n      this.changeCameraPositionAndDownloadPNG(posVectorOne, \"grafik1.png\", true); // this.removeObject(\"maskette\");\n\n      this.changeCameraPositionAndDownloadPNG(posVectorTwo, \"grafik2.png\");\n      this.changeCameraPositionAndDownloadPNG(posVectorThree, \"grafik3.png\"); // this.redrawObject(\"maskette\");\n\n      this.changeCameraPositionAndDownloadPNG(posVectorFour, \"grafik4.png\");\n    }\n\n    changeCameraPositionAndDownloadPNG(posVector, fileName, isISO = false) {\n      this.camera.position.copy(posVector);\n      this.camera.lookAt(this.objectMidVector);\n      this.camera.updateProjectionMatrix();\n      this.controls.update();\n      this.updateChainVisualisation();\n      this.zoomDrawing(true, isISO);\n      this.camera.updateProjectionMatrix();\n      this.renderAllScenes();\n      this.downloadAsPNGImage(fileName);\n    }\n\n    updateChainVisualisation() {\n      this.DimChainViewController.updateChainVisualization(this.camera);\n      this.DynamicTextsController.updateDynamicTexts(this.camera);\n    }\n\n    downloadAsPNGImage(fileName, customSize = null) {\n      let strMime = \"image/png\";\n      let imgData = this.renderer.domElement.toDataURL(fileName);\n      return imgData; //resize image\n      // if ( customSize )\n      // {\n      //   let { x: iw, y: ih } = this.renderer.getSize( new this.THREE.Vector2() );\n      //   let img = new Image();\n      //   img.src = imgData;\n      //   let c = document.createElement('canvas');\n      //   let ctx = c.getContext('2d');\n      //   let scaleFactor = customSize / iw;\n      //   let scaledWidth = iw * scaleFactor;\n      //   let scaledHeight = ih * scaleFactor;\n      //   c.width = scaledWidth;\n      //   c.height = scaledHeight;\n      //   img.onload = () => {\n      //     ctx.drawImage( img, 0, 0, scaledWidth, scaledHeight );\n      //     imgData = c.toDataURL( strMime );\n      //     downloadImage( fileName, imgData );\n      //   };\n      // }\n      // else\n      //   downloadImage( fileName, imgData );\n    }\n\n  }\n\n  _exports.GraphicsThree3D = GraphicsThree3D;\n\n  function downloadImage(fileName, imgData) {\n    let link = document.createElement('a');\n    document.body.appendChild(link); //Firefox requires the link to be in the body\n\n    link.download = fileName;\n    link.href = imgData;\n    link.click();\n    document.body.removeChild(link); //remove the link when done\n  }\n});","define(\"m08-2020/lib/Utils\", [\"exports\", \"three/examples/jsm/lines/Line2\", \"three/examples/jsm/lines/LineMaterial\", \"three/examples/jsm/lines/LineGeometry\"], function (_exports, _Line, _LineMaterial, _LineGeometry) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Utils = void 0;\n\n  class Utils {\n    constructor(three) {\n      this.THREE = three;\n    }\n\n    initVector2(vectorData) {\n      return new this.THREE.Vector2(vectorData.x, vectorData.y);\n    }\n\n    initVector3(vectorData) {\n      let vector = new this.THREE.Vector3(vectorData.x, vectorData.y, vectorData.z ? vectorData.z : 0);\n      return vector;\n    }\n\n    vectorsAreEqual(vecA, vecB) {\n      const DELTA = 1e-4;\n      return Math.abs(vecA.x - vecB.x) < DELTA && Math.abs(vecA.y - vecB.y) < DELTA && Math.abs(vecA.z - vecB.z) < DELTA;\n    }\n\n    addVectorObjects(vecOne, vecTwo) {\n      return {\n        x: vecOne.x + vecTwo.x,\n        y: vecOne.y + vecTwo.y,\n        z: vecOne.z + vecTwo.z\n      };\n    }\n\n    applyRotationToVector(vectorData, rotationParams) {\n      let rotationAngle = rotationParams.rotationAngle * Math.PI / 180;\n      let rotationAxis = this.initVector3(rotationParams.rotationAxis).normalize();\n      let rotationPoint = this.initVector3(rotationParams.rotationPoint);\n      let vector = this.initVector3(vectorData).sub(rotationPoint).applyAxisAngle(rotationAxis, rotationAngle).add(rotationPoint);\n      return vector;\n    }\n\n    normalizeVector(vector) {\n      let vectorLength = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2) + Math.pow(vector.z, 2));\n      let normalizedVector = {\n        x: vector.x / vectorLength,\n        y: vector.y / vectorLength,\n        z: vector.z / vectorLength\n      };\n      return normalizedVector;\n    }\n\n    roundToDecimals(num, decimals = 1) {\n      let multiplier = Math.pow(10, decimals);\n      return Math.round(num * multiplier) / multiplier;\n    }\n\n    checkVectorsEquality(vectorOne, vectorTwo, precision = 2) {\n      return this.roundToDecimals(vectorOne.x, precision) === this.roundToDecimals(vectorTwo.x, precision) && this.roundToDecimals(vectorOne.y, precision) === this.roundToDecimals(vectorTwo.y, precision) && this.roundToDecimals(vectorOne.z, precision) === this.roundToDecimals(vectorTwo.z, precision);\n    }\n\n    getEulerOneAndTwo(crossVector) {\n      let quaternionOne = new this.THREE.Quaternion();\n      quaternionOne.setFromUnitVectors(crossVector, new this.THREE.Vector3(0, 0, 1));\n      let eulerOne = new this.THREE.Euler();\n      eulerOne.setFromQuaternion(quaternionOne);\n      let quaternionTwo = new this.THREE.Quaternion();\n      quaternionTwo.setFromUnitVectors(new this.THREE.Vector3(0, 0, 1), crossVector);\n      let eulerTwo = new this.THREE.Euler();\n      eulerTwo.setFromQuaternion(quaternionTwo);\n      return [eulerOne, eulerTwo];\n    }\n\n    getCrossVector(vecOne, vecTwo) {\n      return new this.THREE.Vector3().crossVectors(vecOne, vecTwo).normalize();\n    }\n\n    clampNum(num, min, max) {\n      return Math.min(Math.max(num, min), max);\n    }\n\n    getColorRGB(colorData) {\n      let color = \"rgb(\" + colorData.red + \", \" + colorData.green + \", \" + colorData.blue + \")\";\n      return color;\n    }\n\n    getMaterialFromColorProps(colorProps) {\n      let materialOptions = {\n        color: this.getColorRGB(colorProps),\n        transparent: true,\n        opacity: colorProps.alpha / 255,\n        side: this.THREE.DoubleSide\n      };\n      let material = new this.THREE.MeshBasicMaterial(materialOptions);\n      return material;\n    }\n\n    isComplexVolume(properties) {\n      return properties && properties.volume && properties.volume.type > 1;\n    }\n\n    skewExtrudedGeometry(geometry, [skewX, skewY]) {\n      geometry.applyMatrix4(new this.THREE.Matrix4().set(1, 0, skewX, 0, 0, 1, skewY, 0, 0, 0, 1, 0, 0, 0, 0, 1));\n    }\n\n    getSkewParams(extrusionVector) {\n      let extrusionNormalized = extrusionVector.clone().normalize();\n      let skewX = extrusionNormalized.x / extrusionNormalized.z;\n      let skewY = extrusionNormalized.y / extrusionNormalized.z;\n      return [skewX, skewY];\n    }\n\n    getVerticesFromBufferGeometry(geometry) {\n      let vertices = [];\n      let posAttr = geometry.attributes.position;\n\n      if (posAttr) {\n        for (let i = 0; i < posAttr.count; i++) {\n          vertices.push(new this.THREE.Vector3().fromBufferAttribute(posAttr, i));\n        }\n      }\n\n      return vertices;\n    }\n\n    mergeLinePoints(pairArr, lineDirection) {\n      pairArr.sort((pairOne, pairTwo) => pairTwo[0].clone().projectOnVector(lineDirection).lengthSq() - pairOne[0].clone().projectOnVector(lineDirection).lengthSq());\n      let startingLength = pairArr.length;\n      let previousLengthChanged = true;\n\n      while (previousLengthChanged) {\n        for (let i = 0; i < pairArr.length; i++) {\n          for (let j = i; j < pairArr.length; j++) {\n            let pairOne = pairArr[i];\n            let pairTwo = pairArr[j];\n\n            if (this.linesLayOnSamePlane(pairOne, pairTwo, lineDirection)) {\n              let mergedPair = this.mergeTwoLinePoints(pairOne, pairTwo, lineDirection);\n\n              if (mergedPair) {\n                pairArr[i] = mergedPair;\n                if (i !== j) pairArr.splice(j, 1);\n              }\n            }\n          }\n        }\n\n        if (pairArr.length === startingLength) previousLengthChanged = false;else startingLength = pairArr.length;\n      }\n\n      return pairArr;\n    }\n\n    mergeTwoLinePoints(pairOne, pairTwo, lineDirection) {\n      if (pairOne[0].equals(pairTwo[0]) && pairOne[1].equals(pairTwo[1])) return pairOne;\n      let pairOneSorted = [...pairOne].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let pairTwoSorted = [...pairTwo].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let projectedPoints = [...pairOneSorted, ...pairTwoSorted].sort((a, b) => {\n        let projA = new this.THREE.Vector3().copy(a).projectOnVector(lineDirection);\n        let projB = new this.THREE.Vector3().copy(b).projectOnVector(lineDirection);\n        return projA.dot(lineDirection) - projB.dot(lineDirection);\n      });\n      let minPoint = projectedPoints[0];\n      let [pOneL0, pOneL1] = [pairOneSorted[0].distanceTo(minPoint), pairOneSorted[1].distanceTo(minPoint)];\n      let [pTwoL0, pTwoL1] = [pairTwoSorted[0].distanceTo(minPoint), pairTwoSorted[1].distanceTo(minPoint)];\n      let merged;\n      if (pTwoL0 === pOneL0 && pTwoL1 === pOneL1) merged = pairTwoSorted;else if (pTwoL0 > pOneL0 && pTwoL1 < pOneL1) merged = pairOneSorted;else if (pOneL0 > pTwoL0 && pOneL1 < pTwoL1) merged = pairTwoSorted;else if (pTwoL0 <= pOneL0 && pTwoL1 >= pOneL0 && pTwoL1 <= pOneL1) merged = [pairTwoSorted[0], pairOneSorted[1]];else if (pTwoL0 >= pOneL0 && pTwoL0 <= pOneL1 && pTwoL1 >= pOneL1) merged = [pairOneSorted[0], pairTwoSorted[1]];else if (pTwoL0 >= pOneL1 && pTwoL0 <= pOneL0 && pTwoL1 <= pOneL1) merged = [pairOneSorted[0], pairTwoSorted[1]];else if (pTwoL0 >= pOneL0 && pTwoL1 <= pOneL0 && pTwoL1 >= pOneL1) merged = [pairTwoSorted[0], pairOneSorted[1]];\n      return merged;\n    }\n\n    linesLayOnSamePlane(pairOne, pairTwo, planeNormal) {\n      const DELTA = 0.0001;\n      let projectionOne = pairOne[0].clone().projectOnPlane(planeNormal);\n      let projectionTwo = pairTwo[0].clone().projectOnPlane(planeNormal);\n      return projectionOne.distanceTo(projectionTwo) < DELTA;\n    }\n\n    getCrossVectorFromPoints(points) {\n      let pointVectors = [];\n\n      for (let i = 0; i < points.length - 1; i++) {\n        pointVectors.push(new this.THREE.Vector3().subVectors(points[i + 1], points[i]));\n      }\n\n      pointVectors.push(new this.THREE.Vector3().subVectors(points[0], points[points.length - 1]));\n      let crossVector = new this.THREE.Vector3();\n\n      for (let i = 0; i < pointVectors.length - 1; i++) {\n        crossVector.add(new this.THREE.Vector3().crossVectors(pointVectors[i], pointVectors[i + 1]));\n      }\n\n      crossVector.add(new this.THREE.Vector3().crossVectors(pointVectors[pointVectors.length - 1], pointVectors[0]));\n      crossVector.normalize();\n      return crossVector;\n    }\n\n    vectorIsZero(vectorData) {\n      return !vectorData.x && !vectorData.y && !vectorData.z;\n    }\n\n    drawLine(pointOne, pointTwo, lineProps) {\n      if (lineProps.type === 0 || lineProps.thickness === 0 || lineProps.color.alpha === 0) return new this.THREE.Mesh();\n      let lineMaterial = this.getMaterialFromColorProps(lineProps.color);\n      let lineRadius = lineProps.thickness / 2;\n      let lineSize = pointOne.distanceTo(pointTwo);\n      let lineGeo = new this.THREE.CylinderGeometry(lineRadius, lineRadius, lineSize, 8);\n      lineGeo.rotateX(Math.PI / 2);\n      lineGeo.translate(0, 0, lineSize / 2);\n      let lineMesh = new this.THREE.Mesh(lineGeo, lineMaterial);\n      lineMesh.position.copy(pointOne);\n      lineMesh.lookAt(pointTwo);\n      return lineMesh;\n    }\n\n    drawSolidLine(points, lineProps, closed = false) {\n      let lineMeshArray = [];\n      let localPoints = points.map(pt => pt.clone());\n      if (closed) localPoints.push(localPoints[0].clone());\n\n      for (let i = 0; i < localPoints.length - 1; i++) {\n        if (localPoints[i].distanceTo(localPoints[i + 1]) > 0) {\n          let lineMesh = this.drawLine(localPoints[i], localPoints[i + 1], lineProps, true);\n          lineMeshArray.push(lineMesh);\n        }\n      }\n\n      let firstLine = lineMeshArray.length > 0 ? lineMeshArray.splice(0, 1)[0] : new this.THREE.Mesh();\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    }\n\n    drawDashedLine(pointOne, pointTwo, factor, pointPairs) {\n      let newSegment = true;\n      let direction = new this.THREE.Vector3().subVectors(pointTwo, pointOne).normalize();\n      let distance = pointOne.distanceTo(pointTwo);\n      let segmentSize = factor;\n\n      while (distance > segmentSize) {\n        let newPoint = new this.THREE.Vector3().copy(pointOne).add(direction.clone().multiplyScalar(segmentSize));\n        if (newSegment) pointPairs.push([pointOne, newPoint]);\n        pointOne = newPoint;\n        distance -= segmentSize;\n        newSegment = !newSegment;\n      }\n    }\n\n    makeDashSegments(points, factor) {\n      let distanceDelta = factor;\n\n      for (let i = 0; i < points.length - 1; i++) {\n        let distance = points[i].distanceTo(points[i + 1]);\n\n        if (distance < distanceDelta) {\n          distanceDelta -= distance;\n        } else {\n          let newPoint = new this.THREE.Vector3().lerpVectors(points[i], points[i + 1], distanceDelta / distance);\n          newPoint.insertedPoint = true;\n          points.splice(i + 1, 0, newPoint);\n          distanceDelta = factor;\n        }\n      }\n    }\n\n    mergeLines(pointPairs, lineProps) {\n      let lineMeshArray = [];\n      pointPairs.forEach(pair => {\n        if (pair[0].distanceTo(pair[1]) > 0) {\n          let lineMesh = this.drawLine(pair[0], pair[1], lineProps);\n          lineMeshArray.push(lineMesh);\n        }\n      });\n      let firstLine = lineMeshArray.splice(0, 1)[0];\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new this.THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    }\n\n    drawConnectedLines(points, lineProps, closed = false) {\n      let localPoints = points.map(point => point.clone());\n      let firstLine;\n\n      if (canDrawLine(lineProps)) {\n        if (canDrawSolidLine(lineProps)) {\n          firstLine = this.drawSolidLine(points, lineProps, closed);\n        } else if (canDrawDashedLine(lineProps)) {\n          let factor = lineProps.factor;\n          let pointPairs = [];\n          let segmentSize = factor;\n          if (closed) localPoints.push(localPoints[0].clone());\n          let bigSegmentPts = [];\n          let smallSegmentPts = [];\n\n          for (let p = 0; p < localPoints.length - 1; p++) {\n            let distance = localPoints[p].distanceTo(localPoints[p + 1]);\n\n            if (distance > factor) {\n              bigSegmentPts.push(localPoints[p]);\n              bigSegmentPts.push(localPoints[p + 1]);\n            } else {\n              smallSegmentPts.push(localPoints[p]);\n              smallSegmentPts.push(localPoints[p + 1]);\n            }\n          }\n\n          for (let p = 0; p < bigSegmentPts.length - 1; p++) {\n            let distance = bigSegmentPts[p].distanceTo(bigSegmentPts[p + 1]);\n            if (distance > segmentSize) this.drawDashedLine(bigSegmentPts[p], bigSegmentPts[p + 1], factor, pointPairs);else segmentSize -= segmentSize - distance;\n          }\n\n          this.makeDashSegments(smallSegmentPts, factor);\n          let newSegment = true;\n\n          for (let p = 0; p < smallSegmentPts.length - 1; p++) {\n            if (smallSegmentPts[p].insertedPoint) newSegment = !newSegment;\n            if (newSegment) pointPairs.push([smallSegmentPts[p], smallSegmentPts[p + 1]]);\n          }\n\n          firstLine = this.mergeLines(pointPairs, lineProps);\n        } else if (canDrawDottedLine(lineProps)) {\n          let lineMaterial = this.getMaterialFromColorProps(lineProps.color);\n          let factor = lineProps.factor;\n          let radius = lineProps.thickness / 2;\n          let circleGeos = [];\n          let segmentSize = factor;\n\n          if (closed) {\n            localPoints.push(localPoints[0].clone());\n          } ///\n\n\n          let bigSegmentPts = [];\n          let smallSegmentPts = [];\n\n          for (let p = 0; p < localPoints.length - 1; p++) {\n            let distance = localPoints[p].distanceTo(localPoints[p + 1]);\n\n            if (distance > factor) {\n              bigSegmentPts.push(localPoints[p]);\n              bigSegmentPts.push(localPoints[p + 1]);\n            } else {\n              smallSegmentPts.push(localPoints[p]);\n              smallSegmentPts.push(localPoints[p + 1]);\n            }\n          }\n\n          this.makeDashSegments(smallSegmentPts, factor); ///\n\n          smallSegmentPts.forEach(point => {\n            if (point.insertedPoint) {\n              let circle = new this.THREE.SphereGeometry(radius, 8, 8);\n              circle.translate(point.x, point.y, point.z);\n              circleGeos.push(circle);\n            }\n          });\n\n          for (let p = 0; p < bigSegmentPts.length - 1; p++) {\n            let distance = bigSegmentPts[p].distanceTo(bigSegmentPts[p + 1]);\n\n            if (distance > 0) {\n              if (distance > segmentSize) {\n                let numOfSegments = Math.floor(distance / segmentSize);\n\n                for (let i = 0; i < numOfSegments; i++) {\n                  let newPoint = new this.THREE.Vector3().lerpVectors(bigSegmentPts[p], bigSegmentPts[p + 1], i / numOfSegments);\n                  let circle = new this.THREE.SphereGeometry(radius, 8, 8);\n                  circle.translate(newPoint.x, newPoint.y, newPoint.z);\n                  circleGeos.push(circle);\n                  if (segmentSize < factor) segmentSize = factor;\n                }\n              } else {\n                segmentSize -= segmentSize - distance;\n              }\n            }\n          }\n\n          let firstCircle = circleGeos.splice(0, 1)[0];\n          circleGeos.forEach(circle => firstCircle.merge(circle));\n          firstLine = new this.THREE.Mesh(firstCircle, lineMaterial);\n        }\n      } else firstLine = new this.THREE.Mesh();\n\n      return firstLine; // Helper functions -->\n\n      function canDrawLine(lineProps) {\n        return lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined);\n      }\n\n      function canDrawSolidLine(lineProps) {\n        return lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0;\n      }\n\n      function canDrawDashedLine(lineProps) {\n        return lineProps.type === 2;\n      }\n\n      function canDrawDottedLine(lineProps) {\n        return lineProps.type === 3;\n      } // <-- Helper functions\n\n    }\n\n    drawFatLine(pointsArr, lineProps) {\n      const MIN_THICKNESS = 0.002;\n      let thickness = lineProps.thickness * 5e-3;\n      let {\n        color,\n        opacity\n      } = this.getColorAndOpacity(lineProps.color);\n      let lineGeoPoints = pointsArr.reduce((geoPoints, point) => {\n        geoPoints.push(point.x, point.y, point.z);\n        return geoPoints;\n      }, []);\n      let lineGeometry = new _LineGeometry.LineGeometry();\n      lineGeometry.setPositions(lineGeoPoints);\n      let lineMaterial = new _LineMaterial.LineMaterial({\n        transparent: true,\n        color,\n        opacity,\n        linewidth: thickness >= MIN_THICKNESS ? thickness : MIN_THICKNESS\n      });\n      let line = new _Line.Line2(lineGeometry, lineMaterial);\n      line.scale.set(1, 1, 1);\n      return line;\n    }\n\n    getColorAndOpacity(colorProps) {\n      let color = \"rgb(\" + colorProps.red + \", \" + colorProps.green + \", \" + colorProps.blue + \")\";\n      let opacity = colorProps.alpha / 255;\n      return {\n        color,\n        opacity\n      };\n    }\n\n    alignObjectWithVectors(object, normalVector, directionVector) {\n      let newUpVector = new this.THREE.Vector3().crossVectors(normalVector, directionVector);\n      let dotProduct = object.up.dot(newUpVector);\n      let rotationAngle = -Math.acos(dotProduct);\n      object.rotateOnAxis(normalVector, rotationAngle);\n      object.up = newUpVector;\n      object.lookAt(normalVector.add(object.position));\n    }\n\n    getMeshUpVector(mesh, direction) {\n      let upObject = new this.THREE.Object3D();\n      upObject.position.copy(mesh.up);\n      mesh.add(upObject);\n      mesh.lookAt(direction);\n      mesh.updateMatrixWorld();\n      let upVector = upObject.getWorldPosition(new this.THREE.Vector3());\n      return upVector;\n    }\n\n    makeTestMesh(position, size = 1, color = 0xff0000) {\n      let geo = new this.THREE.BoxBufferGeometry(size, size, size);\n      let mat = new this.THREE.MeshBasicMaterial({\n        color\n      });\n      let mesh = new this.THREE.Mesh(geo, mat);\n      mesh.position.copy(position);\n      return mesh;\n    }\n\n    processUnits(unitsData) {\n      let units = {\n        length: \"\",\n        angle: \"\",\n        mass: \"\",\n        force: \"\",\n        temperature: \"\"\n      };\n\n      switch (unitsData.length) {\n        case 1:\n          units.length = \"mm\";\n          break;\n\n        case 2:\n          units.length = \"cm\";\n          break;\n\n        case 3:\n          units.length = \"m\";\n          break;\n\n        case 4:\n          units.length = \"km\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.angle) {\n        case 1:\n          units.angle = \"deg\";\n          break;\n\n        case 2:\n          units.angle = \"rad\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.mass) {\n        case 1:\n          units.mass = \"g\";\n          break;\n\n        case 2:\n          units.mass = \"kg\";\n          break;\n\n        case 3:\n          units.mass = \"t\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.force) {\n        case 1:\n          units.force = \"N\";\n          break;\n\n        case 2:\n          units.force = \"kN\";\n          break;\n\n        case 3:\n          units.force = \"MN\";\n          break;\n\n        default:\n          break;\n      }\n\n      switch (unitsData.temperature) {\n        case 1:\n          units.temperature = \"degC\";\n          break;\n\n        case 2:\n          units.temperature = \"degF\";\n          break;\n\n        default:\n          break;\n      }\n\n      return units;\n    }\n\n  }\n\n  _exports.Utils = Utils;\n});","define(\"m08-2020/lib/Utils/BooleanOperations\", [\"exports\", \"three\", \"polybooljs\", \"m08-2020/lib/CSGMesh\"], function (_exports, THREE, _polybooljs, _CSGMesh) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.applyCSG = applyCSG;\n  _exports.doCSG = doCSG;\n  _exports.doSegmentsOperation = doSegmentsOperation;\n  _exports.makeCombinedSegments2D = makeCombinedSegments2D;\n  _exports.makePolygonsFromPosAndNegRegions = makePolygonsFromPosAndNegRegions;\n  const CSG = (0, _CSGMesh.CsgFactory)(THREE);\n\n  function applyCSG(positiveObjects, negativeObjects) {\n    let positiveObject = positiveObjects.splice(0, 1)[0];\n    positiveObjects.forEach(object => positiveObject = doCSG(positiveObject, object, \"union\"));\n    negativeObjects.forEach(object => positiveObject = doCSG(positiveObject, object, \"subtract\"));\n    return positiveObject;\n  }\n\n  function doCSG(a, b, op) {\n    let bspA = CSG.fromMesh(a);\n    let bspB = CSG.fromMesh(b);\n    let bspC = bspA[op](bspB);\n    let result = CSG.toMesh(bspC, new THREE.Matrix4());\n    return result;\n  }\n\n  function makePolygonsFromPosAndNegRegions(regions) {\n    let positives = regions.positiveRegions;\n    let negatives = regions.negativeRegions;\n    let segmentsPos = makeCombinedSegments2D(positives, \"selectUnion\");\n    let segmentsNeg = negatives.length > 0 ? makeCombinedSegments2D(negatives, \"selectUnion\") : null;\n    let finalSegments = segmentsNeg ? doSegmentsOperation(segmentsPos, segmentsNeg, \"selectDifference\") : segmentsPos;\n\n    let finalPolygons = _polybooljs.default.polygon(finalSegments);\n\n    return finalPolygons;\n  }\n\n  function makeCombinedSegments2D(regions, operation) {\n    let segments = _polybooljs.default.segments(regions[0]);\n\n    for (let i = 1; i < regions.length; i++) {\n      let tempSegment = _polybooljs.default.segments(regions[i]);\n\n      segments = doSegmentsOperation(segments, tempSegment, operation);\n    }\n\n    return segments;\n  }\n\n  function doSegmentsOperation(segmentOne, segmentTwo, operation) {\n    let combined = _polybooljs.default.combine(segmentOne, segmentTwo);\n\n    return _polybooljs.default[operation](combined);\n  }\n});","define(\"m08-2020/lib/Utils/DimensionalChainIntersectionDetection\", [\"exports\", \"three\", \"hull.js\", \"intersects\", \"m08-2020/lib/Utils\"], function (_exports, THREE, hull, intersects, _Utils) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.DimensionalChainIntersectionDetection = void 0;\n  let utils = new _Utils.Utils(THREE);\n\n  class DimensionalChainIntersectionDetection {\n    constructor() {\n      this.meshArr = []; // [ DoubleFace, FrontFace, BackFace ]\n\n      this.chainArr = []; // [ DoubleFace, FrontFace, BackFace ]\n\n      this.chainsForZoom = []; // [ DoubleFace, FrontFace, BackFace ]\n    }\n\n    checkCollision2D(linePtsArr, objArr, drawingNormal) {\n      const Z_AXIS = new THREE.Vector3(0, 0, 1);\n      let quaternion = new THREE.Quaternion().setFromUnitVectors(drawingNormal, Z_AXIS);\n      let projectedLinePts = projectLinePoints(linePtsArr);\n      let projectedObjPts = projectObjectPoints(objArr);\n      return isCollision(projectedLinePts, projectedObjPts); // Helper functions -->\n\n      function isCollision(projectedLinePts, projectedObjPts) {\n        const TOLERANCE = 1e-4;\n        let collisionDetected = false;\n\n        for (let line of projectedLinePts) {\n          for (let polygon of projectedObjPts) {\n            let pts = [];\n            polygon.forEach(pt => pts.push(pt[0], pt[1]));\n            if (intersects.linePolygon(line[0][0], line[0][1], line[1][0], line[1][1], pts, TOLERANCE)) collisionDetected = true;\n          }\n        }\n\n        return collisionDetected;\n      }\n\n      function projectLinePoints(linePtsArr) {\n        let projectedLinePts = [];\n\n        for (let ptPair of linePtsArr) {\n          let ptsProj = [];\n\n          for (let pt of ptPair) {\n            let projectedPt = pt.clone().projectOnPlane(drawingNormal).applyQuaternion(quaternion);\n            ptsProj.push([projectedPt.x, projectedPt.y]);\n          }\n\n          projectedLinePts.push(ptsProj);\n        }\n\n        return projectedLinePts;\n      }\n\n      function projectObjectPoints(objArr) {\n        let projectedObjPts = [];\n\n        for (let meshArr of objArr) {\n          let mesh = meshArr[0];\n          let ptsProj = [];\n\n          for (let vertex of mesh.geometry.vertices) {\n            ptsProj.push(vertex.clone().applyMatrix4(mesh.matrix).projectOnPlane(drawingNormal).applyQuaternion(quaternion));\n          }\n\n          projectedObjPts.push(hull(ptsProj.map(pt => [pt.x, pt.y]), Infinity));\n        }\n\n        return projectedObjPts;\n      } // Helper functions <--\n\n    }\n\n    checkCollision(linePtsArr, objArr) {\n      let isIntersection = false;\n      let raycaster = new THREE.Raycaster();\n\n      for (let [pointOne, pointTwo] of linePtsArr) {\n        let pointsDistance = pointOne.distanceTo(pointTwo);\n\n        for (let meshArr of objArr) {\n          let intersectionsOne = this.getIntersections(raycaster, [pointOne, pointTwo], meshArr);\n          let intersectionsTwo = this.getIntersections(raycaster, [pointTwo, pointOne], meshArr);\n          if (this.intersectionDetected(pointsDistance, intersectionsOne, intersectionsTwo)) isIntersection = true;\n        }\n      }\n\n      return isIntersection;\n    }\n\n    intersectionDetected(pointsDistance, intersectionsOne, intersectionsTwo) {\n      let [intersectionsOneDouble, intersectionsOneFront, intersectionsOneBack] = intersectionsOne;\n      let [intersectionsTwoDouble, intersectionsTwoFront, intersectionsTwoBack] = intersectionsTwo;\n      let mutualIntersections = this.getMutualIntersections(intersectionsOneDouble, intersectionsTwoDouble);\n      return mutualIntersections.length !== 1 && this.bothPointsOutsideMesh(mutualIntersections, pointsDistance) || this.onePointInsideMeshOneOut(mutualIntersections, intersectionsOneDouble, intersectionsTwoDouble) || this.bothPointsInMesh(intersectionsOneBack, intersectionsTwoBack, intersectionsOneFront, intersectionsTwoFront);\n    }\n\n    bothPointsOutsideMesh(mutualIntersections, pointsDistance) {\n      let intersectionInMiddle = false;\n      mutualIntersections.forEach(intersection => {\n        if (intersection.distance < pointsDistance) intersectionInMiddle = true;\n      });\n      return intersectionInMiddle;\n    }\n\n    onePointInsideMeshOneOut(mutualIntersections, intersectionsOneDouble, intersectionsTwoDouble) {\n      let pointOneInside = this.pointInsideMesh(intersectionsOneDouble);\n      let pointTwoInside = this.pointInsideMesh(intersectionsTwoDouble);\n      return mutualIntersections.length > 0 && (pointOneInside && !pointTwoInside || !pointOneInside && pointTwoInside);\n    }\n\n    bothPointsInMesh(intersectionsOneBack, intersectionsTwoBack, intersectionsOneFront, intersectionsTwoFront) {\n      return intersectionsOneBack.length > 0 && intersectionsTwoBack.length > 0 && intersectionsOneFront.length === 0 && intersectionsTwoFront.length === 0;\n    }\n\n    getIntersections(raycaster, ptsPair, [meshDoubleFace, meshFrontFace, meshBackFace]) {\n      let [pointOne, pointTwo] = ptsPair;\n      let direction = pointTwo.clone().sub(pointOne).normalize();\n      raycaster.set(pointOne, direction);\n      let intersectionsDouble = [];\n      let intersectionsFront = [];\n      let intersectionsBack = [];\n      const DELTA = 1e-4;\n      intersectionsDouble.push(...raycaster.intersectObject(meshDoubleFace).filter(intersection => intersection.distance > DELTA));\n      intersectionsFront.push(...raycaster.intersectObject(meshFrontFace).filter(intersection => intersection.distance > DELTA));\n      intersectionsBack.push(...raycaster.intersectObject(meshBackFace).filter(intersection => intersection.distance > DELTA));\n      intersectionsDouble = this.removeDuplicateIntersections(intersectionsDouble);\n      intersectionsFront = this.removeDuplicateIntersections(intersectionsFront);\n      intersectionsBack = this.removeDuplicateIntersections(intersectionsBack);\n      return [intersectionsDouble, intersectionsFront, intersectionsBack];\n    }\n\n    getMutualIntersections(intersectionsOne, intersectionsTwo) {\n      let intersections = intersectionsOne.filter(intersectionOne => {\n        let isMutual = false;\n        let testPoint = intersectionOne.point;\n        intersectionsTwo.forEach(intersectionTwo => {\n          if (utils.checkVectorsEquality(intersectionTwo.point, testPoint)) isMutual = true;\n        });\n        return isMutual;\n      });\n      return intersections;\n    }\n\n    removeDuplicateIntersections(intersections) {\n      let filterArr = [];\n      intersections.forEach(intersection => {\n        let isUnique = true;\n        filterArr.forEach(([key, _]) => {\n          if (utils.vectorsAreEqual(intersection.point, key)) isUnique = false;\n        });\n        if (isUnique) filterArr.push([intersection.point, intersection]);\n      });\n      intersections = filterArr.map(([_, value]) => value);\n      return intersections;\n    }\n\n    intersectionsSharePoints(intersectionsOne, intersectionsTwo) {\n      let intersectionsSharePoints = false;\n\n      for (let intersectionOne of intersectionsOne) {\n        for (let intersectionTwo of intersectionsTwo) {\n          if (utils.vectorsAreEqual(intersectionOne.point, intersectionTwo.point)) intersectionsSharePoints = true;\n        }\n      }\n\n      return intersectionsSharePoints;\n    }\n\n    pointInsideMesh(intersections) {\n      return intersections.length % 2;\n    }\n\n  }\n\n  _exports.DimensionalChainIntersectionDetection = DimensionalChainIntersectionDetection;\n});","define(\"m08-2020/lib/Utils/LineIntersectionDetection\", [\"exports\"], function (_exports) {\n  \"use strict\";\n  /**\n   * https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/\n   * Check if Lines intersect 2D\n   */\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.linesIntersect = linesIntersect;\n  _exports.onSegment = onSegment;\n  _exports.orientation = orientation;\n\n  function onSegment(p, q, r) {\n    // p, q and r points are colinear, q lies on line segment pr\n    return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);\n  }\n\n  function orientation(p, q, r) {\n    /**\n     * 0: Colinear\n     * 1: Clockwise\n     * 2: Counterclockwise\n     */\n    let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);\n    if (val > 0) return 1;else if (val < 0) return 2;else return 0;\n  }\n\n  function linesIntersect(p1, q1, p2, q2) {\n    /**\n     * p1, q1 -> first line points\n     * p2, q2 -> second line points\n     */\n    // Get orientations\n    let o1 = orientation(p1, q1, p2);\n    let o2 = orientation(p1, q1, q2);\n    let o3 = orientation(p2, q2, p1);\n    let o4 = orientation(p2, q2, q1); // General Case\n\n    if (o1 !== o2 && o3 !== o4) return true; // Special Cases\n    // p1, q1 and p2 are colinear and p2 lies on segment p1q1\n\n    if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and q2 are colinear and q2 lies on segment p1q1\n\n    if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p2, q2 and p1 are colinear and p1 lies on segment p2q2\n\n    if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and q1 are colinear and q1 lies on segment p2q2\n\n    if (o4 === 0 && onSegment(p2, q1, q2)) return true; // no intersection\n\n    return false;\n  }\n});","define(\"m08-2020/lib/Utils/PointInsidePolygonCheck2D\", [\"exports\", \"m08-2020/lib/Utils/LineIntersectionDetection\"], function (_exports, _LineIntersectionDetection) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.Point2D = void 0;\n  _exports.pointInsidePolygon2D = pointInsidePolygon2D;\n\n  class Point2D {\n    constructor(x, y) {\n      this.x = x;\n      this.y = y;\n    }\n\n  }\n\n  _exports.Point2D = Point2D;\n\n  function pointInsidePolygon2D(polygon, point) {\n    const INFINITE_VALUE = Number.MAX_VALUE;\n    const N = polygon.length;\n    if (N < 3) return false;\n    let extremePoint = new Point2D(INFINITE_VALUE, point.y);\n    let count = 0;\n\n    for (let i = 0; i < N - 1; i++) {\n      let j = i + 1;\n\n      if ((0, _LineIntersectionDetection.linesIntersect)(polygon[i], polygon[j], point, extremePoint)) {\n        if ((0, _LineIntersectionDetection.orientation)(polygon[i], point, polygon[j]) === 0) return (0, _LineIntersectionDetection.onSegment)(polygon[i], point, polygon[j]);\n        count++;\n      }\n    }\n\n    return count % 2 === 1;\n  }\n});","define(\"m08-2020/lib/WireframeGenerator\", [\"exports\", \"three\", \"m08-2020/lib/Utils\", \"m08-2020/lib/ThreeExtensions/OutlinesGeometry\", \"m08-2020/lib/ThreeExtensions/RaycastModdedMesh\", \"m08-2020/lib/Utils/BooleanOperations\"], function (_exports, THREE, _Utils, _OutlinesGeometry, _RaycastModdedMesh, BooleanOperations) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.WireframeGenerator = void 0;\n  const utils = new _Utils.Utils(THREE);\n\n  class WireframeGenerator {\n    constructor(GraphicsThree3D) {\n      this.GraphicsThree3D = GraphicsThree3D;\n      this.areaWireframesData = [];\n      this.volumeWireframesData = {\n        opaque: {\n          positives: [],\n          negatives: []\n        },\n        transparent: {\n          positives: [],\n          negatives: []\n        }\n      };\n    }\n\n    applyWireframesArea(object, objectTransparent, lineProps, objectData) {\n      const RENDER_ORDER = -99;\n      let pointPairs = this.getRegularWireframePointPairs(object);\n      pointPairs.forEach(pair => {\n        let wireframe = this.drawObjectLine(pair, lineProps);\n        wireframe.renderOrder = RENDER_ORDER;\n\n        wireframe.onBeforeRender = function (renderer) {\n          renderer.clearDepth();\n        };\n\n        let wireframeClone = wireframe.clone();\n        wireframeClone.isLine = true;\n        if (wireframe.needsZoomControl) wireframeClone.needsZoomControl = true;\n        if (wireframe.pointsStartEnd) wireframeClone.pointsStartEnd = wireframe.pointsStartEnd;\n        object.add(wireframe);\n        if (objectTransparent) objectTransparent.add(wireframeClone);\n      });\n      object.dxfPoints = [{\n        id: objectData.id,\n        pointPairs: pointPairs.map(pair => pair.map(pt => pt.clone())),\n        lineProps\n      }];\n    }\n\n    applyWireframesVolume(object, objectTransparent, lineProps, objectData) {\n      let pointPairs = [];\n      this.mergeVolumeWireframes(\"opaque\");\n      this.mergeVolumeWireframes(\"transparent\");\n      this.createLinesFromWireframeData(\"opaque\", object, lineProps);\n      this.createLinesFromWireframeData(\"transparent\", objectTransparent, lineProps, pointPairs);\n      this.clearWireframeState();\n      object.dxfPoints = [{\n        id: objectData.id,\n        pointPairs,\n        lineProps\n      }];\n    }\n\n    createLinesFromWireframeData(type, object, lineProps, pointPairs = null) {\n      const RENDER_ORDER = -99;\n      const DELTA = 0.0001;\n      this.volumeWireframesData[type].forEach(({\n        linePoints\n      }) => {\n        linePoints.forEach(([pointOne, pointTwo]) => {\n          if (pointOne.distanceTo(pointTwo) > DELTA) {\n            let line = this.drawObjectLine([pointOne, pointTwo], lineProps);\n            line.renderOrder = RENDER_ORDER;\n\n            line.onBeforeRender = function (renderer) {\n              renderer.clearDepth();\n            };\n\n            if (object) object.add(line);\n            if (pointPairs) pointPairs.push([pointOne.clone(), pointTwo.clone()]);\n          }\n        });\n      });\n    } ///\n\n\n    drawObjectLine(pointPair, lineProps) {\n      let firstLine;\n\n      if (canDrawLine(lineProps)) {\n        if (canDrawSolidLine(lineProps)) firstLine = this.drawSolidLine(pointPair, lineProps, true);else if (canDrawDashedLine(lineProps)) firstLine = this.drawDashedLine(pointPair, lineProps);else firstLine = utils.drawConnectedLines(pointPair, lineProps);\n      } else firstLine = new THREE.Mesh();\n\n      firstLine.isLine = true;\n      firstLine.pointsStartEnd = {\n        startPoint: pointPair[0],\n        endPoint: pointPair[1]\n      }; // Helper functions -->\n\n      function canDrawLine(lineProps) {\n        return lineProps.thickness && lineProps.color.alpha && (lineProps.type || lineProps.type === undefined);\n      }\n\n      function canDrawSolidLine(lineProps) {\n        return lineProps.type === 1 || lineProps.type === undefined || lineProps.factor === 0 && lineProps.type !== null && lineProps.type !== 0;\n      }\n\n      function canDrawDashedLine(lineProps) {\n        return lineProps.type === 2;\n      } // <-- Helper functions\n\n\n      return firstLine;\n    }\n\n    drawSolidLine(points, lineProps) {\n      let lineMesh = points[0].distanceTo(points[1]) > 0 ? utils.drawLine(points[0], points[1], lineProps, true) : new THREE.Mesh();\n      lineMesh.needsZoomControl = true;\n      return lineMesh;\n    }\n\n    drawDashedLine(pointPair, lineProps) {\n      let localPoints = pointPair.map(pt => pt.clone().sub(pointPair[0]));\n      let factor = lineProps.factor;\n      let q = new THREE.Quaternion().setFromUnitVectors(localPoints[1].clone().normalize(), new THREE.Vector3(0, 0, 1));\n      localPoints[1].applyQuaternion(q);\n      let lineSegments = this.getDashedLineSegments(localPoints[0], localPoints[1], factor);\n      let lineMesh = this.drawDashedLineFromSegments(lineSegments, lineProps);\n      lineMesh.position.add(pointPair[0]);\n      lineMesh.lookAt(pointPair[1]);\n      lineMesh.needsZoomControl = true;\n      return lineMesh;\n    }\n\n    getDashedLineSegments(pointOne, pointTwo, factor) {\n      let pointPairs = [];\n      let newSegment = true;\n      let direction = new THREE.Vector3().subVectors(pointTwo, pointOne).normalize();\n      let distance = pointOne.distanceTo(pointTwo);\n      let segmentSize = factor;\n\n      if (distance > factor) {\n        while (distance > segmentSize) {\n          let newPoint = new THREE.Vector3().copy(pointOne).add(direction.clone().multiplyScalar(segmentSize));\n          if (newSegment) pointPairs.push([pointOne, newPoint]);\n          pointOne = newPoint;\n          distance -= segmentSize;\n          newSegment = !newSegment;\n        }\n      } else pointPairs.push([pointOne, pointTwo]);\n\n      return pointPairs;\n    }\n\n    drawDashedLineFromSegments(lineSegments, lineProps) {\n      let lineMeshArray = [];\n      lineSegments.forEach(pair => {\n        if (pair[0].distanceTo(pair[1]) > 0) {\n          let lineMesh = utils.drawLine(pair[0], pair[1], lineProps);\n          lineMeshArray.push(lineMesh);\n        }\n      });\n      let firstLine = lineMeshArray.splice(0, 1)[0];\n      let lineMaterial = firstLine.material;\n      firstLine.updateMatrixWorld();\n      firstLine.geometry.applyMatrix4(firstLine.matrixWorld);\n\n      for (let i = 0; i < lineMeshArray.length; i++) {\n        lineMeshArray[i].updateMatrixWorld();\n        lineMeshArray[i].geometry.applyMatrix4(lineMeshArray[i].matrixWorld);\n        firstLine.geometry.merge(lineMeshArray[i].geometry);\n      }\n\n      firstLine = new THREE.Mesh(firstLine.geometry, lineMaterial);\n      return firstLine;\n    } ///\n\n\n    mergeVolumeWireframes(type) {\n      const DELTA = 0.0001;\n      let raycaster = new THREE.Raycaster();\n      let positives = this.mergeSameTypeWireframe(raycaster, this.volumeWireframesData[type], \"positives\");\n      let negatives = this.mergeSameTypeWireframe(raycaster, this.volumeWireframesData[type], \"negatives\");\n      if (positives.mesh) positives.mesh.material.side = THREE.DoubleSide;\n      if (negatives.mesh) negatives.mesh.material.side = THREE.DoubleSide;\n      this.volumeWireframesData[type] = [positives, negatives];\n\n      for (let objectData of this.volumeWireframesData[type]) {\n        let {\n          id,\n          mesh,\n          objectType\n        } = objectData;\n        this.volumeWireframesData[type].filter(obj => id !== obj.id).map(obj => obj.mesh).forEach(objMesh => {\n          let newPoints = [];\n          objectData.linePoints.forEach(([pointOne, pointTwo]) => {\n            pointOne.applyMatrix4(mesh.matrix);\n            pointTwo.applyMatrix4(mesh.matrix);\n            let firstPointIntersection = this.pointInsideMesh(raycaster, pointOne, pointTwo, objMesh);\n            let secondPointIntersection = this.pointInsideMesh(raycaster, pointTwo, pointOne, objMesh);\n            let firstPointData = {\n              point: pointOne,\n              intersection: firstPointIntersection\n            };\n            let secondPointData = {\n              point: pointTwo,\n              intersection: secondPointIntersection\n            };\n            let operation = objectType === 1 ? \"AddPoints\" : \"SubtractPoints\";\n            let correctedPointPairs = this.getCorrectedLinePoints(firstPointData, secondPointData, operation);\n            correctedPointPairs.forEach(pair => {\n              if (pair[0].distanceTo(pair[1]) > DELTA) newPoints.push([pair[0], pair[1]]);\n            });\n          });\n          objectData.linePoints = newPoints;\n        });\n      }\n    }\n\n    mergeSameTypeWireframe(raycaster, volumeWireframes, type) {\n      let mergedWireframes = volumeWireframes[type];\n\n      for (let i = 0; i < mergedWireframes.length; i++) {\n        let objectData = mergedWireframes[i];\n        let id = objectData.id;\n        let mesh = objectData.mesh;\n        mergedWireframes.filter(obj => id !== obj.id).map(obj => obj.mesh).forEach(objMesh => {\n          let newPoints = [];\n          objectData.linePoints.forEach(([pointOne, pointTwo]) => {\n            pointOne.applyMatrix4(mesh.matrix);\n            pointTwo.applyMatrix4(mesh.matrix);\n            let firstPointIntersection = this.pointInsideMesh(raycaster, pointOne, pointTwo, objMesh);\n            let secondPointIntersection = this.pointInsideMesh(raycaster, pointTwo, pointOne, objMesh);\n            let firstPointData = {\n              point: pointOne,\n              intersection: firstPointIntersection\n            };\n            let secondPointData = {\n              point: pointTwo,\n              intersection: secondPointIntersection\n            };\n            let correctedPointPairs = this.getCorrectedLinePoints(firstPointData, secondPointData, \"AddPoints\");\n            correctedPointPairs.forEach(pair => {\n              if (pair[0].distanceTo(pair[1]) > 0.0001) newPoints.push([pair[0], pair[1]]);\n            });\n          });\n          objectData.linePoints = newPoints;\n        });\n      }\n\n      mergedWireframes = mergedWireframes.reduce((allPos, currentPos) => {\n        allPos.id = currentPos.id;\n        allPos.objectType = currentPos.objectType;\n        allPos.linePoints.push(...currentPos.linePoints);\n        allPos.mesh = allPos.mesh ? BooleanOperations.doCSG(allPos.mesh, currentPos.mesh, \"union\") : currentPos.mesh;\n        return allPos;\n      }, {\n        linePoints: []\n      });\n      return mergedWireframes;\n    }\n\n    getCorrectedLinePoints(firstPtIntersect, secondPtIntersect, operationType) {\n      const DELTA = 0.0001;\n      let pointOne = firstPtIntersect.point;\n      let firstPointIntersection = firstPtIntersect.intersection;\n      let pointTwo = secondPtIntersect.point;\n      let secondPointIntersection = secondPtIntersect.intersection;\n      let linePairs = [];\n\n      if (operationType === \"AddPoints\") {\n        if (!firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh) {\n          if (firstPointIntersection.intersectionPoint && secondPointIntersection.intersectionPoint) {\n            let lenPointToPoint = pointOne.distanceTo(pointTwo);\n            let lenPointOneToIntersect = pointOne.distanceTo(firstPointIntersection.intersectionPoint);\n            let lenPointTwoToIntersect = pointTwo.distanceTo(secondPointIntersection.intersectionPoint);\n\n            if (lenPointToPoint > lenPointOneToIntersect && lenPointToPoint > lenPointTwoToIntersect) {\n              linePairs.push([pointOne, firstPointIntersection.intersectionPoint]);\n              linePairs.push([pointTwo, secondPointIntersection.intersectionPoint]);\n            } else {\n              linePairs.push([pointOne, pointTwo]);\n            }\n          } else {\n            linePairs.push([pointOne, pointTwo]);\n          }\n        } else if (intersectionIsValid(secondPtIntersect, firstPtIntersect)) {\n          linePairs.push([pointOne, secondPointIntersection.intersectionPoint]);\n        } else if (intersectionIsValid(firstPtIntersect, secondPtIntersect)) {\n          linePairs.push([pointTwo, firstPointIntersection.intersectionPoint]);\n        } else if (mustNotBeIntersection(firstPtIntersect, secondPtIntersect)) {\n          let direction = pointTwo.clone().sub(pointOne).normalize();\n          if (firstPointIntersection.theIntersect && direction.dot(firstPointIntersection.theIntersect.face.normal) < 0) linePairs.push([pointTwo, pointOne]);\n        } else if (mustNotBeIntersection(secondPtIntersect, firstPtIntersect)) {\n          let direction = pointOne.clone().sub(pointTwo).normalize();\n          if (secondPointIntersection.theIntersect && direction.dot(secondPointIntersection.theIntersect.face.normal) < 0) linePairs.push([pointTwo, pointOne]); // console.log( firstPointIntersection, secondPointIntersection, pointOne, pointTwo )\n          // linePairs.push([ pointTwo, pointOne ]);\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     pointOne, 1, 0x00ff00\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     pointTwo, 1, 0x0000ff\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     secondPointIntersection.intersectionPoint, 2, 0xff0000\n          //   )\n          // );\n          // secondPointIntersection.allIntersects.forEach(intersect => {\n          //   this.GraphicsThree3D.scene.add(\n          //     utils.makeTestMesh(\n          //       intersect.point, 1, 0xff0000\n          //     )\n          //   );\n          // });\n          // this.GraphicsThree3D.scene.add(\n          //   utils.drawLine(\n          //     pointOne,\n          //     pointTwo,\n          //     {\n          //       color: {\n          //         alpha: 255,\n          //         red: Math.floor( Math.random() * 100 )+ 100,\n          //         green: 0,\n          //         blue: 0\n          //       },\n          //       type: 1,\n          //       thickness: 1\n          //     }\n          //   )\n          // );\n        }\n      } else if (operationType === \"SubtractPoints\") {\n        if (firstPointIntersection.isInMesh && secondPointIntersection.isInMesh) {\n          linePairs.push([pointOne, pointTwo]);\n        } else if (intersectionIsValid(firstPtIntersect, secondPtIntersect)) {\n          linePairs.push([pointOne, firstPointIntersection.intersectionPoint]);\n        } else if (intersectionIsValid(secondPtIntersect, firstPtIntersect)) {\n          linePairs.push([pointTwo, secondPointIntersection.intersectionPoint]);\n        } else if (bothPointsAreInside(firstPtIntersect, secondPtIntersect)) {\n          linePairs.push([pointTwo, pointOne]);\n        } else if (mustNotBeIntersection(firstPtIntersect, secondPtIntersect)) {\n          let direction = pointTwo.clone().sub(pointOne).normalize();\n          if (firstPointIntersection.theIntersect && direction.dot(firstPointIntersection.theIntersect.face.normal) > 0) linePairs.push([pointTwo, pointOne]);\n        } else if (mustNotBeIntersection(secondPtIntersect, firstPtIntersect)) {\n          let direction = pointOne.clone().sub(pointTwo).normalize();\n          if (secondPointIntersection.theIntersect && direction.dot(secondPointIntersection.theIntersect.face.normal) > 0) linePairs.push([pointTwo, pointOne]);\n        } else if ( // points are on faces\n        !firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh && firstPointIntersection.intersectionPoint && secondPointIntersection.intersectionPoint && firstPointIntersection.intersectionPoint.distanceTo(pointOne) < DELTA && secondPointIntersection.intersectionPoint.distanceTo(pointTwo) < DELTA) {\n          linePairs.push([pointTwo, pointOne]); // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     firstPointIntersection.intersectionPoint, 1, 0xff0000\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     secondPointIntersection.intersectionPoint, 1, 0x0000ff\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     pointOne, 1, 0xff0000\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.makeTestMesh(\n          //     pointTwo, 1, 0x0000ff\n          //   )\n          // );\n          // this.GraphicsThree3D.scene.add(\n          //   utils.drawLine(\n          //     pointOne,\n          //     pointTwo,\n          //     {\n          //       color: {\n          //         alpha: 255,\n          //         red: 255,\n          //         green: 0,\n          //         blue: 0\n          //       },\n          //       type: 1,\n          //       thickness: 0.1\n          //     }\n          //   )\n          // );\n        }\n      }\n\n      return linePairs; // Helper functions -->\n\n      function intersectionIsValid(firstPtIntersect, secondPtIntersect) {\n        let pointOne = firstPtIntersect.point;\n        let firstPointIntersection = firstPtIntersect.intersection;\n        let pointTwo = secondPtIntersect.point;\n        let secondPointIntersection = secondPtIntersect.intersection;\n        return firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh && secondPointIntersection.intersectionPoint && pointTwo.distanceTo(secondPointIntersection.intersectionPoint) < pointTwo.distanceTo(pointOne);\n      }\n\n      function mustNotBeIntersection(firstPtIntersect, secondPtIntersect) {\n        let pointOne = firstPtIntersect.point;\n        let firstPointIntersection = firstPtIntersect.intersection;\n        let pointTwo = secondPtIntersect.point;\n        let secondPointIntersection = secondPtIntersect.intersection;\n        return firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh && firstPointIntersection.intersectionPoint && !secondPointIntersection.intersectionPoint && pointOne.distanceTo(firstPointIntersection.intersectionPoint) > pointOne.distanceTo(pointTwo);\n      }\n\n      function bothPointsAreInside(firstPtIntersect, secondPtIntersect) {\n        let pointOne = firstPtIntersect.point;\n        let firstPointIntersection = firstPtIntersect.intersection;\n        let pointTwo = secondPtIntersect.point;\n        let secondPointIntersection = secondPtIntersect.intersection;\n        return (!firstPointIntersection.isInMesh && secondPointIntersection.isInMesh || firstPointIntersection.isInMesh && !secondPointIntersection.isInMesh) && firstPointIntersection.intersectionPoint && secondPointIntersection.intersectionPoint && pointTwo.distanceTo(firstPointIntersection.intersectionPoint) > pointTwo.distanceTo(pointOne);\n      } // Helper functions <--\n\n    }\n\n    pointInsideMesh(raycaster, pointOne, pointTwo, object) {\n      const MIN_DISTANCE = 1e-4;\n\n      if (!object) {\n        return {\n          isInMesh: false,\n          intersectionPoint: null\n        };\n      }\n\n      let direction = pointTwo.clone().sub(pointOne).normalize();\n      raycaster.set(pointOne, direction);\n      let intersects = raycaster.intersectObject(object);\n\n      for (let i = 0; i < intersects.length; i++) {\n        for (let j = 0; j < intersects.length; j++) {\n          if (i != j && Math.abs(intersects[i].distance - intersects[j].distance) < MIN_DISTANCE) {\n            intersects[i].markedToDelete = true * !intersects[j].markedToDelete;\n          }\n        }\n      }\n\n      intersects = intersects.filter(intersect => !intersect.markedToDelete);\n      let intersectionData = {\n        isInMesh: intersects.length % 2 ? true : false,\n        intersectionPoint: intersects[0] ? intersects[0].point : null,\n        theIntersect: intersects[0],\n        allIntersects: intersects\n      };\n      return intersectionData;\n    }\n\n    getRegularWireframePointPairs(object, threshold = Math.PI / 6) {\n      let edgesGeo = new _OutlinesGeometry.OutlinesGeometry(object.geometry);\n      let pointPairs = [];\n      this.getWireframeLinePairs(edgesGeo, threshold).forEach(([_, pairs]) => pairs.forEach(pair => pointPairs.push(pair)));\n      return pointPairs;\n    }\n\n    getWireframeLinePairs(edgesGeo, threshold = Math.PI / 6) {\n      const DELTA = 0.0001;\n      let facesDict = this.getFacesDict(edgesGeo.faces);\n      let linePairs = [];\n\n      for (let [edgeIndices, facePair] of Object.entries(facesDict)) {\n        if (facePair.length === 2) {\n          let faceOne = facePair[0];\n          let faceTwo = facePair[1];\n\n          if (faceOne.normal.angleTo(faceTwo.normal) > threshold) {\n            let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n            let vectorOne = edgesGeo.vertices[verticesIdx[0]];\n            let vectorTwo = edgesGeo.vertices[verticesIdx[1]];\n            let direction = new THREE.Vector3().subVectors(vectorTwo, vectorOne).normalize();\n            let needToAdd = false;\n\n            for (let k = 0; k < linePairs.length; k++) {\n              needToAdd = false;\n              let vectorData = linePairs[k];\n              let dir = vectorData[0];\n              let vectorsArr = vectorData[1];\n              let dirDot = dir.dot(direction);\n\n              if (dirDot > 1 - DELTA) {\n                vectorsArr.push([vectorOne, vectorTwo]);\n                k = linePairs.length;\n              } else if (dirDot < -1 + DELTA) {\n                vectorsArr.push([vectorTwo, vectorOne]);\n                k = linePairs.length;\n              } else needToAdd = true;\n            }\n\n            if (linePairs.length === 0) needToAdd = true;\n            if (needToAdd) linePairs.push([direction, [[vectorOne, vectorTwo]]]);\n          }\n        } else if (facePair.length === 1) {\n          let verticesIdx = edgeIndices.split(\",\").map(val => parseInt(val));\n          let vectorOne = edgesGeo.vertices[verticesIdx[0]];\n          let vectorTwo = edgesGeo.vertices[verticesIdx[1]];\n          let direction = new THREE.Vector3().subVectors(vectorTwo, vectorOne).normalize();\n          let needToAdd = false;\n\n          for (let k = 0; k < linePairs.length; k++) {\n            needToAdd = false;\n            let vectorData = linePairs[k];\n            let dir = vectorData[0];\n            let vectorsArr = vectorData[1];\n            let dirDot = dir.dot(direction);\n\n            if (dirDot > 1 - DELTA) {\n              vectorsArr.push([vectorOne, vectorTwo]);\n              k = linePairs.length;\n            } else if (dirDot < -1 + DELTA) {\n              vectorsArr.push([vectorTwo, vectorOne]);\n              k = linePairs.length;\n            } else needToAdd = true;\n          }\n\n          if (linePairs.length === 0) needToAdd = true;\n          if (needToAdd) linePairs.push([direction, [[vectorOne, vectorTwo]]]);\n        }\n      }\n\n      this.mergeLinePairsSameDirection(linePairs);\n      return linePairs;\n    }\n\n    getFacesDict(faces) {\n      let facesDict = {};\n\n      for (let face of faces) {\n        if (face.splitted) continue;\n        let edgeOne = `${face.a},${face.b}`;\n        let edgeTwo = `${face.b},${face.c}`;\n        let edgeThree = `${face.c},${face.a}`;\n        let edgeOneReversed = `${face.b},${face.a}`;\n        let edgeTwoReversed = `${face.c},${face.b}`;\n        let edgeThreeReversed = `${face.a},${face.c}`;\n        if (edgeOneReversed in facesDict) facesDict[edgeOneReversed].push(face);else if (edgeOne in facesDict) facesDict[edgeOne].push(face);else {\n          facesDict[edgeOne] = [];\n          facesDict[edgeOne].push(face);\n        }\n        if (edgeTwoReversed in facesDict) facesDict[edgeTwoReversed].push(face);else if (edgeTwo in facesDict) facesDict[edgeTwo].push(face);else {\n          facesDict[edgeTwo] = [];\n          facesDict[edgeTwo].push(face);\n        }\n        if (edgeThreeReversed in facesDict) facesDict[edgeThreeReversed].push(face);else if (edgeThree in facesDict) facesDict[edgeThree].push(face);else {\n          facesDict[edgeThree] = [];\n          facesDict[edgeThree].push(face);\n        }\n      }\n\n      return facesDict;\n    }\n\n    mergeLinePairsSameDirection(linePairs) {\n      linePairs.forEach(arrData => this.makeLinePairsUnique(arrData));\n      linePairs.forEach(([direction, pairs]) => utils.mergeLinePoints(pairs, direction));\n    }\n\n    makeLinePairsUnique(arrData) {\n      // [ 0: Direction, 1: Pairs array ]\n      let vectorPairsArr = arrData[1];\n      let uniquePairs = [];\n      vectorPairsArr.forEach(pair => {\n        let isUnique = true;\n        uniquePairs.forEach(uniquePair => {\n          if (pair[0].equals(uniquePair[0]) && pair[1].equals(uniquePair[1])) isUnique = false;\n        });\n        if (isUnique) uniquePairs.push(pair);\n      });\n      arrData[1] = uniquePairs;\n    }\n\n    getVolumeWireframeData(mesh, linePairsForOpaque, linePairsForTransparent, objectType) {\n      let intersectionGeo = mesh.geometry.clone(); // For checking if line intersects faces\n\n      intersectionGeo.applyMatrix4(mesh.matrix);\n      let intersectionMesh = new _RaycastModdedMesh.RaycastModdedMesh(intersectionGeo, new THREE.MeshBasicMaterial({\n        side: THREE.DoubleSide\n      }));\n\n      if (objectType === 1) {\n        this.volumeWireframesData.opaque.positives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForOpaque,\n          mesh: intersectionMesh,\n          objectType\n        });\n        this.volumeWireframesData.transparent.positives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForTransparent,\n          mesh: intersectionMesh,\n          objectType\n        });\n      } else if (objectType === 2) {\n        this.volumeWireframesData.opaque.negatives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForOpaque,\n          mesh: intersectionMesh,\n          objectType\n        });\n        this.volumeWireframesData.transparent.negatives.push({\n          id: intersectionMesh.uuid,\n          linePoints: linePairsForTransparent,\n          mesh: intersectionMesh,\n          objectType\n        });\n      }\n    }\n\n    clearWireframeState() {\n      this.areaWireframesData = [];\n      this.volumeWireframesData = {\n        opaque: {\n          positives: [],\n          negatives: []\n        },\n        transparent: {\n          positives: [],\n          negatives: []\n        }\n      };\n    }\n\n  }\n\n  _exports.WireframeGenerator = WireframeGenerator;\n});","define(\"m08-2020/lib/font_urls\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.FONT_URLS = void 0;\n  const FONT_URLS = {\n    Regular: {\n      Arial: \"assets/fonts/Arial_Regular.json\",\n      Calibri: \"assets/fonts/Calibri_Regular.json\",\n      Courier: \"assets/fonts/Courier New_Regular.json\",\n      TimesNewRoman: \"assets/fonts/Times New Roman_Regular.json\"\n    },\n    Italic: {\n      Arial: \"assets/fonts/Arial_Italic.json\",\n      Calibri: \"assets/fonts/Calibri_Italic.json\",\n      Courier: \"assets/fonts/Courier New_Italic.json\",\n      TimesNewRoman: \"assets/fonts/Times New Roman_Italic.json\"\n    },\n    Bold: {\n      Arial: \"assets/fonts/Arial_Bold.json\",\n      Calibri: \"assets/fonts/Calibri_Bold.json\",\n      Courier: \"assets/fonts/Courier New_Bold.json\",\n      TimesNewRoman: \"assets/fonts/Times New Roman_Bold.json\"\n    },\n    BoldItalic: {\n      Arial: \"assets/fonts/Arial_Bold Italic.json\",\n      Calibri: \"assets/fonts/Calibri_Bold Italic.json\",\n      Courier: \"assets/fonts/Courier New_Bold Italic.json\",\n      TimesNewRoman: \"assets/fonts/Times New Roman_Bold Italic.json\"\n    }\n  };\n  _exports.FONT_URLS = FONT_URLS;\n});","define(\"m08-2020/mixins/model-validator\", [\"exports\", \"ember-model-validator/mixins/model-validator\"], function (_exports, _modelValidator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = _modelValidator.default;\n  _exports.default = _default;\n});","define(\"m08-2020/mixins/object-validator\", [\"exports\", \"ember-model-validator/mixins/object-validator\"], function (_exports, _objectValidator) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = _objectValidator.default;\n  _exports.default = _default;\n});","define(\"m08-2020/models/application\", [\"exports\", \"@ember-data/model\"], function (_exports, _model) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  // var lower = 0;\n  // var upper = 100000000;\n  // var calcId = Math.floor(Math.random()*(upper-lower))+lower;\n  var application = _model.default.extend({\n    Calculation_ID: (0, _model.attr)('string'),\n    firm: (0, _model.attr)('string'),\n    //firm\n    spracheinput: (0, _model.attr)('string'),\n    //spracheinput\n    spracheoutput: (0, _model.attr)('string'),\n    //spracheoutput\n    katalog: (0, _model.attr)('string'),\n    //katalog\n    timberCode: (0, _model.attr)('string'),\n    //timberCode\n    timberNA: (0, _model.attr)('string'),\n    //timberNA\n    loadsCode: (0, _model.attr)('string'),\n    //loadsCode\n    loadsNA: (0, _model.attr)('string'),\n    //loadsNA\n    initialized: (0, _model.attr)('boolean'),\n    selectedScrew: (0, _model.attr)('string'),\n    treeLoaded: (0, _model.attr)('boolean'),\n    kennungland: (0, _model.attr)('string'),\n    userID: (0, _model.attr)('string'),\n    userName: (0, _model.attr)('string'),\n    pdfErstellt: (0, _model.attr)('boolean')\n  });\n\n  var _default = application;\n  _exports.default = _default;\n});","define(\"m08-2020/models/position\", [\"exports\", \"@ember-data/model\"], function (_exports, _model) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = _model.default.extend({\n    x: (0, _model.attr)('number'),\n    y: (0, _model.attr)('number'),\n    z: (0, _model.attr)('number')\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/models/rotation\", [\"exports\", \"@ember-data/model\"], function (_exports, _model) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = _model.default.extend({\n    x: (0, _model.attr)('number'),\n    y: (0, _model.attr)('number'),\n    z: (0, _model.attr)('number'),\n    winkel: (0, _model.attr)('number') // rotation: belongsTo('bauteil', {async: true})\n\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/router\", [\"exports\", \"m08-2020/config/environment\"], function (_exports, _environment) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n  class Router extends Ember.Router {\n    constructor(...args) {\n      super(...args);\n\n      _defineProperty(this, \"location\", _environment.default.locationType);\n\n      _defineProperty(this, \"rootURL\", _environment.default.rootURL);\n    }\n\n  }\n\n  _exports.default = Router;\n  Router.map(function () {\n    this.route('projektdaten');\n  });\n});","define(\"m08-2020/routes/application\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = Ember.Route.extend({\n    intl: Ember.inject.service(),\n\n    beforeModel() {\n      this.intl.setLocale(['de']);\n    },\n\n    model: function () {\n      var self = this;\n      return Ember.RSVP.hash({\n        application: self.store.findAll('application')\n      });\n    },\n\n    setupController(controller, model) {\n      this._super(controller, model);\n\n      setTimeout(function () {\n        console.log('setupController');\n        scene.set3DSceneSize(); // ./vendor/scenesizemanagement/scenesizemanagement.js\n      }, 400);\n      $(window).resize(function () {\n        scene.set3DSceneSize(); // ./vendor/scenesizemanagement/scenesizemanagement.js\n      });\n    }\n\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/serializers/-default\", [\"exports\", \"@ember-data/serializer/json\"], function (_exports, _json) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _json.default;\n    }\n  });\n});","define(\"m08-2020/serializers/-json-api\", [\"exports\", \"@ember-data/serializer/json-api\"], function (_exports, _jsonApi) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _jsonApi.default;\n    }\n  });\n});","define(\"m08-2020/serializers/-rest\", [\"exports\", \"@ember-data/serializer/rest\"], function (_exports, _rest) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _rest.default;\n    }\n  });\n});","define(\"m08-2020/services/file-queue\", [\"exports\", \"ember-file-upload/services/file-queue\"], function (_exports, _fileQueue) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _fileQueue.default;\n    }\n  });\n});","define(\"m08-2020/services/intl\", [\"exports\", \"ember-intl/services/intl\"], function (_exports, _intl) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _intl.default;\n    }\n  });\n});","define(\"m08-2020/services/store\", [\"exports\", \"ember-data/store\"], function (_exports, _store) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _store.default;\n    }\n  });\n});","define(\"m08-2020/templates/application\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = Ember.HTMLBars.template({\n    \"id\": \"sVzmt7J6\",\n    \"block\": \"{\\\"symbols\\\":[],\\\"statements\\\":[[0,\\\"  \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"container\\\"],[8],[0,\\\"\\\\n\\\\n    \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"tlbbtn\\\"],[8],[0,\\\"\\\\n      \\\"],[7,\\\"button\\\",false],[12,\\\"class\\\",\\\"btn btn-default btn-xs\\\"],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"loadAction\\\"]]],null]]],[8],[0,\\\"\\\\n        \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/folder_page.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n      \\\"],[9],[0,\\\"\\\\n      \\\"],[7,\\\"input\\\",false],[12,\\\"id\\\",\\\"file-input\\\"],[12,\\\"name\\\",\\\"name\\\"],[12,\\\"type\\\",\\\"file\\\"],[3,\\\"on\\\",[\\\"change\\\",[28,\\\"fn\\\",[[23,0,[\\\"openFileAction\\\"]]],null]]],[8],[9],[0,\\\"\\\\n    \\\"],[9],[0,\\\"\\\\n\\\\n    \\\"],[7,\\\"div\\\",true],[8],[0,\\\"\\\\n      \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"x3dsteuerelemente\\\"],[8],[0,\\\"\\\\n        \\\"],[7,\\\"button\\\",false],[12,\\\"onclick\\\",\\\"setCameraBinding('viewpointDefault')\\\"],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"ChangePosition0\\\"]],\\\"viewpointDefault\\\"],null]]],[8],[0,\\\"\\\\n          \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/viewdefault.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n        \\\"],[9],[0,\\\"\\\\n        \\\"],[7,\\\"button\\\",false],[12,\\\"onclick\\\",\\\"setCameraBinding('viewpointLinks')\\\"],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"ChangePosition1\\\"]]],null]]],[8],[0,\\\"\\\\n          \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/viewleft.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n        \\\"],[9],[0,\\\"\\\\n        \\\"],[7,\\\"button\\\",false],[12,\\\"onclick\\\",\\\"setCameraBinding('viewpointOben')\\\"],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"ChangePosition2\\\"]]],null]]],[8],[0,\\\"\\\\n          \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/viewtop.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n        \\\"],[9],[0,\\\"\\\\n        \\\"],[7,\\\"button\\\",false],[12,\\\"onclick\\\",\\\"setCameraBinding('viewpointFront')\\\"],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"ChangePosition3\\\"]]],null]]],[8],[0,\\\"\\\\n          \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/viewfront.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n        \\\"],[9],[0,\\\"\\\\n        \\\"],[7,\\\"button\\\",false],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"ToggleWireframe\\\"]]],null]]],[8],[0,\\\"\\\\n          \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/gitter.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n        \\\"],[9],[0,\\\"\\\\n\\\"],[0,\\\"            \\\"],[7,\\\"button\\\",false],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"loadDrawingPlanesHQ\\\"]]],null]]],[8],[0,\\\"\\\\n              \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/HQ.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"button\\\",false],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"loadDrawingPlanesMQ\\\"]]],null]]],[8],[0,\\\"\\\\n              \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/MQ.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"button\\\",false],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"loadDrawingPlanesLQ\\\"]]],null]]],[8],[0,\\\"\\\\n              \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/LQ.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n\\\"],[0,\\\"            \\\"],[7,\\\"button\\\",false],[12,\\\"type\\\",\\\"button\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"loadDXF3D\\\"]]],null]]],[8],[0,\\\"\\\\n              \\\"],[7,\\\"img\\\",true],[10,\\\"src\\\",\\\"assets/images/loadDXF3D.png\\\"],[10,\\\"alt\\\",\\\"\\\"],[10,\\\"role\\\",\\\"presentation\\\"],[8],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"div\\\",true],[10,\\\"style\\\",\\\"display: inline-block; font-size: 16px; padding: 0 8px\\\"],[8],[0,\\\"\\\\n              \\\"],[7,\\\"input\\\",false],[12,\\\"id\\\",\\\"visible-status\\\"],[12,\\\"name\\\",\\\"visible-status\\\"],[12,\\\"checked\\\",true],[12,\\\"type\\\",\\\"checkbox\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"changeFastenersVisibility\\\"]]],null]]],[8],[9],[0,\\\"\\\\n              \\\"],[7,\\\"label\\\",true],[10,\\\"for\\\",\\\"visible-status\\\"],[8],[0,\\\"visible\\\"],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"div\\\",true],[10,\\\"style\\\",\\\"display: inline-block; font-size: 16px; padding: 0 8px\\\"],[8],[0,\\\"\\\\n              \\\"],[7,\\\"input\\\",false],[12,\\\"id\\\",\\\"zoom-status\\\"],[12,\\\"name\\\",\\\"zoom-status\\\"],[12,\\\"checked\\\",true],[12,\\\"type\\\",\\\"checkbox\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"changeTextsZoomableStatus\\\"]]],null]]],[8],[9],[0,\\\"\\\\n              \\\"],[7,\\\"label\\\",true],[10,\\\"for\\\",\\\"zoom-status\\\"],[8],[0,\\\"zoom\\\"],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"div\\\",true],[10,\\\"style\\\",\\\"display: inline-block; font-size: 16px; padding: 0 16px;\\\"],[8],[0,\\\"\\\\n              \\\"],[7,\\\"div\\\",true],[10,\\\"style\\\",\\\"text-align: center; background: white; display: inline-block; border-radius: 16px; padding: 0 16px;\\\"],[8],[0,\\\"\\\\n                \\\"],[7,\\\"button\\\",false],[12,\\\"id\\\",\\\"increment\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"changeTextSize\\\"]]],null]]],[8],[0,\\\"+\\\"],[9],[0,\\\"\\\\n              \\\"],[9],[0,\\\"\\\\n              \\\"],[7,\\\"div\\\",true],[10,\\\"style\\\",\\\"text-align: center; background: white; display: inline-block; border-radius: 16px; padding: 0 16px;\\\"],[8],[0,\\\"\\\\n                \\\"],[7,\\\"button\\\",false],[12,\\\"id\\\",\\\"decrement\\\"],[3,\\\"on\\\",[\\\"click\\\",[28,\\\"fn\\\",[[23,0,[\\\"changeTextSize\\\"]]],null]]],[8],[0,\\\"-\\\"],[9],[0,\\\"\\\\n              \\\"],[9],[0,\\\"\\\\n              \\\"],[7,\\\"p\\\",true],[10,\\\"style\\\",\\\"display: inline-block;\\\"],[10,\\\"id\\\",\\\"font-size-text\\\"],[8],[0,\\\"1\\\"],[9],[0,\\\"\\\\n            \\\"],[9],[0,\\\"\\\\n\\\\n            \\\"],[7,\\\"input\\\",true],[10,\\\"id\\\",\\\"zoom-padding\\\"],[10,\\\"name\\\",\\\"zoom-padding\\\"],[10,\\\"value\\\",\\\"0\\\"],[10,\\\"type\\\",\\\"number\\\"],[8],[9],[0,\\\"\\\\n            \\\"],[7,\\\"label\\\",true],[10,\\\"for\\\",\\\"zoom-padding\\\"],[8],[0,\\\"Zoom Padding\\\"],[9],[0,\\\"\\\\n            \\\"],[7,\\\"input\\\",false],[12,\\\"id\\\",\\\"dim-chain-view-threshold\\\"],[12,\\\"name\\\",\\\"dim-chain-view-threshold\\\"],[12,\\\"value\\\",\\\"90\\\"],[12,\\\"max\\\",\\\"100\\\"],[12,\\\"min\\\",\\\"0\\\"],[12,\\\"type\\\",\\\"number\\\"],[3,\\\"on\\\",[\\\"change\\\",[28,\\\"fn\\\",[[23,0,[\\\"changeDimChainViewThreshold\\\"]]],null]]],[8],[9],[0,\\\"\\\\n            \\\"],[7,\\\"label\\\",true],[10,\\\"for\\\",\\\"dim-chain-view-threshold\\\"],[8],[0,\\\"View Threshold\\\"],[9],[0,\\\"\\\\n\\\\n      \\\"],[9],[0,\\\"\\\\n    \\\"],[9],[0,\\\"\\\\n\\\\n    \\\"],[7,\\\"div\\\",true],[10,\\\"id\\\",\\\"threedee\\\"],[10,\\\"class\\\",\\\"threedee\\\"],[8],[0,\\\"\\\\n\\\\n        \\\"],[1,[23,0,[\\\"threejsscene\\\"]],false],[0,\\\"\\\\n\\\\n          \\\"],[7,\\\"ul\\\",true],[10,\\\"id\\\",\\\"sectional-plane-controllers\\\"],[8],[0,\\\"\\\\n\\\\n            \\\"],[2,\\\"\\\\n              Sectional Plane controllers will be generated here\\\\n            \\\"],[0,\\\"\\\\n\\\\n          \\\"],[9],[0,\\\"\\\\n\\\\n    \\\"],[9],[0,\\\"\\\\n\\\\n\\\\n    \\\"],[7,\\\"div\\\",true],[10,\\\"id\\\",\\\"screenshotPreviews\\\"],[8],[0,\\\"\\\\n\\\\n    \\\"],[9],[0,\\\"\\\\n\\\\n    \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"footer\\\"],[8],[0,\\\"\\\\n      \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"footerlinks\\\"],[8],[0,\\\"\\\\n         (c) SWG Engineering \\\"],[1,[23,0,[\\\"version\\\"]],false],[0,\\\"\\\\n      \\\"],[9],[0,\\\"\\\\n    \\\"],[9],[0,\\\"\\\\n\\\\n  \\\"],[9],[0,\\\"\\\\n\\\"]],\\\"hasEval\\\":false}\",\n    \"meta\": {\n      \"moduleName\": \"m08-2020/templates/application.hbs\"\n    }\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/templates/components/custom-input\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = Ember.HTMLBars.template({\n    \"id\": \"QmJDY46m\",\n    \"block\": \"{\\\"symbols\\\":[\\\"&default\\\"],\\\"statements\\\":[[14,1],[0,\\\"\\\\n\\\"]],\\\"hasEval\\\":false}\",\n    \"meta\": {\n      \"moduleName\": \"m08-2020/templates/components/custom-input.hbs\"\n    }\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/templates/components/file-picker\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n\n  var _default = Ember.HTMLBars.template({\n    \"id\": \"DtMFDMHF\",\n    \"block\": \"{\\\"symbols\\\":[\\\"&default\\\"],\\\"statements\\\":[[4,\\\"if\\\",[[24,[\\\"dropzone\\\"]]],null,{\\\"statements\\\":[[0,\\\"  \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"file-picker__dropzone\\\"],[8],[0,\\\"\\\\n    \\\"],[14,1],[0,\\\"\\\\n  \\\"],[9],[0,\\\"\\\\n\\\"]],\\\"parameters\\\":[]},{\\\"statements\\\":[[0,\\\"  \\\"],[14,1],[0,\\\"\\\\n\\\"]],\\\"parameters\\\":[]}],[0,\\\"\\\\n\\\"],[4,\\\"if\\\",[[24,[\\\"preview\\\"]]],null,{\\\"statements\\\":[[0,\\\"  \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"file-picker__preview\\\"],[8],[9],[0,\\\"\\\\n\\\"]],\\\"parameters\\\":[]},null],[0,\\\"\\\\n\\\"],[4,\\\"if\\\",[[24,[\\\"progress\\\"]]],null,{\\\"statements\\\":[[4,\\\"if\\\",[[24,[\\\"showProgress\\\"]]],null,{\\\"statements\\\":[[4,\\\"if\\\",[[24,[\\\"isProgressSupported\\\"]]],null,{\\\"statements\\\":[[0,\\\"      \\\"],[7,\\\"progress\\\",true],[11,\\\"value\\\",[22,\\\"progressValue\\\"]],[10,\\\"max\\\",\\\"100\\\"],[10,\\\"class\\\",\\\"file-picker__progress\\\"],[8],[1,[22,\\\"progress\\\"],false],[0,\\\" %\\\"],[9],[0,\\\"\\\\n\\\"]],\\\"parameters\\\":[]},{\\\"statements\\\":[[0,\\\"      \\\"],[7,\\\"div\\\",true],[10,\\\"class\\\",\\\"file-picker__progress\\\"],[8],[0,\\\"\\\\n        \\\"],[7,\\\"span\\\",true],[10,\\\"class\\\",\\\"file-picker__progress__value\\\"],[11,\\\"style\\\",[22,\\\"progressStyle\\\"]],[8],[9],[0,\\\"\\\\n      \\\"],[9],[0,\\\"\\\\n\\\"]],\\\"parameters\\\":[]}]],\\\"parameters\\\":[]},null]],\\\"parameters\\\":[]},null],[0,\\\"\\\\n\\\"],[1,[28,\\\"input\\\",null,[[\\\"type\\\",\\\"value\\\",\\\"accept\\\",\\\"multiple\\\",\\\"class\\\"],[\\\"file\\\",[24,[\\\"file\\\"]],[24,[\\\"accept\\\"]],[24,[\\\"multiple\\\"]],\\\"file-picker__input\\\"]]],false],[0,\\\"\\\\n\\\"]],\\\"hasEval\\\":false}\",\n    \"meta\": {\n      \"moduleName\": \"m08-2020/templates/components/file-picker.hbs\"\n    }\n  });\n\n  _exports.default = _default;\n});","define(\"m08-2020/transforms/boolean\", [\"exports\", \"@ember-data/serializer/-private\"], function (_exports, _private) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _private.BooleanTransform;\n    }\n  });\n});","define(\"m08-2020/transforms/date\", [\"exports\", \"@ember-data/serializer/-private\"], function (_exports, _private) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _private.DateTransform;\n    }\n  });\n});","define(\"m08-2020/transforms/number\", [\"exports\", \"@ember-data/serializer/-private\"], function (_exports, _private) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _private.NumberTransform;\n    }\n  });\n});","define(\"m08-2020/transforms/string\", [\"exports\", \"@ember-data/serializer/-private\"], function (_exports, _private) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _private.StringTransform;\n    }\n  });\n});","define(\"m08-2020/translations/en-us\", [\"exports\"], function (_exports) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  _exports.default = void 0;\n  var _default = {\n    \"price_banner\": \"The {product} costs {price, number, USD}\"\n  };\n  _exports.default = _default;\n});","define(\"m08-2020/utils/intl/missing-message\", [\"exports\", \"ember-intl/utils/missing-message\"], function (_exports, _missingMessage) {\n  \"use strict\";\n\n  Object.defineProperty(_exports, \"__esModule\", {\n    value: true\n  });\n  Object.defineProperty(_exports, \"default\", {\n    enumerable: true,\n    get: function () {\n      return _missingMessage.default;\n    }\n  });\n});","\n","define('m08-2020/config/environment', [], function() {\n  var prefix = 'm08-2020';\ntry {\n  var metaName = prefix + '/config/environment';\n  var rawConfig = document.querySelector('meta[name=\"' + metaName + '\"]').getAttribute('content');\n  var config = JSON.parse(decodeURIComponent(rawConfig));\n\n  var exports = { 'default': config };\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n  return exports;\n}\ncatch(err) {\n  throw new Error('Could not read config from meta tag with name \"' + metaName + '\".');\n}\n\n});\n","\n          if (!runningTests) {\n            require(\"m08-2020/app\")[\"default\"].create({\"name\":\"m08-2020\",\"version\":\"0.0.0+12abdbe6\",\"validatorDefaultLocale\":\"en\"});\n          }\n        \n"],"names":[],"mappings":"AAAA;AACA;AACA;;ACFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1gBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5OA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxiBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1zBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACllBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7zBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/mBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9TA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACx/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChiFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACthBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5zDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChkBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxiBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACplBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACl4BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/mBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7kFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3NA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACn6BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7iCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9NA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtwDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1kBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpnBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACXA;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;AACA;AACA;","file":"m08-2020.js"}