diff --git a/.gitignore b/.gitignore index 6704566..aec42b3 100644 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,7 @@ typings/ # Nuxt.js build / generate output .nuxt -dist +# dist # Gatsby files .cache/ diff --git a/dist/lib.esm.js b/dist/lib.esm.js new file mode 100644 index 0000000..d41da5d --- /dev/null +++ b/dist/lib.esm.js @@ -0,0 +1,2 @@ +function t(){this.polygons=[]}t.fromPolygons=function(n){var o=new t;return o.polygons=n,o},t.prototype={clone:function(){var n=new t;return n.polygons=this.polygons.map((function(t){return t.clone()})),n},toPolygons:function(){return this.polygons},union:function(n){var o=new t.Node(this.clone().polygons),e=new t.Node(n.clone().polygons);return o.clipTo(e),e.clipTo(o),e.invert(),e.clipTo(o),e.invert(),o.build(e.allPolygons()),t.fromPolygons(o.allPolygons())},subtract:function(n){var o=new t.Node(this.clone().polygons),e=new t.Node(n.clone().polygons);return o.invert(),o.clipTo(e),e.clipTo(o),e.invert(),e.clipTo(o),e.invert(),o.build(e.allPolygons()),o.invert(),t.fromPolygons(o.allPolygons())},intersect:function(n){var o=new t.Node(this.clone().polygons),e=new t.Node(n.clone().polygons);return o.invert(),e.clipTo(o),e.invert(),o.clipTo(e),e.clipTo(o),o.build(e.allPolygons()),o.invert(),t.fromPolygons(o.allPolygons())},inverse:function(){var t=this.clone();return t.polygons.map((function(t){t.flip()})),t}},t.Vector=function(t,n,o){3==arguments.length?(this.x=t,this.y=n,this.z=o):"x"in t?(this.x=t.x,this.y=t.y,this.z=t.z):(this.x=t[0],this.y=t[1],this.z=t[2])},t.Vector.prototype={clone:function(){return new t.Vector(this.x,this.y,this.z)},negated:function(){return new t.Vector(-this.x,-this.y,-this.z)},plus:function(n){return new t.Vector(this.x+n.x,this.y+n.y,this.z+n.z)},minus:function(n){return new t.Vector(this.x-n.x,this.y-n.y,this.z-n.z)},times:function(n){return new t.Vector(this.x*n,this.y*n,this.z*n)},dividedBy:function(n){return new t.Vector(this.x/n,this.y/n,this.z/n)},dot:function(t){return this.x*t.x+this.y*t.y+this.z*t.z},lerp:function(t,n){return this.plus(t.minus(this).times(n))},length:function(){return Math.sqrt(this.dot(this))},unit:function(){return this.dividedBy(this.length())},cross:function(n){return new t.Vector(this.y*n.z-this.z*n.y,this.z*n.x-this.x*n.z,this.x*n.y-this.y*n.x)}},t.Vertex=function(n,o,e,s){this.pos=new t.Vector(n),this.normal=new t.Vector(o),this.uv=e&&e.clone(),this.color=s&&new t.Vector(s)},t.Vertex.prototype={clone:function(){return new t.Vertex(this.pos.clone(),this.normal.clone(),this.uv&&this.uv.clone(),this.color&&this.color.clone())},flip:function(){this.normal=this.normal.negated()},interpolate:function(n,o){return new t.Vertex(this.pos.lerp(n.pos,o),this.normal.lerp(n.normal,o),this.uv&&n.uv&&this.uv.clone().lerp(n.uv,o),this.color&&n.color&&this.color.lerp(n.color,o))}},t.Plane=function(t,n){this.normal=t,this.w=n},t.Plane.EPSILON=1e-5,t.Plane.fromPoints=function(n,o,e){var s=o.minus(n).cross(e.minus(n)).unit();return new t.Plane(s,s.dot(n))},t.Plane.prototype={clone:function(){return new t.Plane(this.normal.clone(),this.w)},flip:function(){this.normal=this.normal.negated(),this.w=-this.w},splitPolygon:function(n,o,e,s,i){for(var r=0,l=[],c=0;ct.Plane.EPSILON?1:0;r|=h,l.push(h)}switch(r){case 0:(this.normal.dot(n.plane.normal)>0?o:e).push(n);break;case 1:s.push(n);break;case 2:i.push(n);break;case 3:var u=[],a=[];for(c=0;c=3&&s.push(new t.Polygon(u,n.shared)),a.length>=3&&i.push(new t.Polygon(a,n.shared))}}},t.Polygon=function(n,o){this.vertices=n,this.shared=o,this.plane=t.Plane.fromPoints(n[0].pos,n[1].pos,n[2].pos)},t.Polygon.prototype={clone:function(){var n=this.vertices.map((function(t){return t.clone()}));return new t.Polygon(n,this.shared)},flip:function(){this.vertices.reverse().map((function(t){t.flip()})),this.plane.flip()}},t.Node=function(t){this.plane=null,this.front=null,this.back=null,this.polygons=[],t&&this.build(t)},t.Node.prototype={clone:function(){var n=new t.Node;return n.plane=this.plane&&this.plane.clone(),n.front=this.front&&this.front.clone(),n.back=this.back&&this.back.clone(),n.polygons=this.polygons.map((function(t){return t.clone()})),n},invert:function(){for(var t=0;t65536?new t.Uint32BufferAttribute(k,1):new t.Uint16BufferAttribute(k,1),u.setAttribute("position",new t.BufferAttribute(i,3)),d&&u.setAttribute("normal",new t.BufferAttribute(r,3));P&&u.setAttribute("uv",new t.BufferAttribute(l,2));A&&u.setAttribute("color",new t.BufferAttribute(c,3));return new t.Mesh(u,h)}(this._THREE,this._csg)}static _fromMesh(o,e){return function(o,e){const s=new n(o);return s._csg=t.fromPolygons(function(n,o){const e=o.clone();e.updateMatrix(),Array.isArray(o.material)&&(e.material=Array.from(o.material));const s=[],i=o.geometry,r=i.attributes.position.array,{normal:l,uv:c,color:h}=i.attributes,u=void 0!==l,a=u?l.array:null,p=void 0!==c,f=p?c.array:null,y=void 0!==h,g=y?h.array:null,m=i.index,v=m?m.count:r.length/3,w=!e.matrix.equals(new n.Matrix4);for(let o=0,l=0,c=0;o=t&&o 0) vertex((i + 1) / slices, j / stacks);\n// if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks);\n// vertex(i / slices, (j + 1) / stacks);\n// polygons.push(new CSG.Polygon(vertices));\n// }\n// }\n// return CSG.fromPolygons(polygons);\n// };\n\n// // Construct a solid cylinder. Optional parameters are `start`, `end`,\n// // `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and\n// // `16`. The `slices` parameter controls the tessellation.\n// // \n// // Example usage:\n// // \n// // var cylinder = CSG.cylinder({\n// // start: [0, -1, 0],\n// // end: [0, 1, 0],\n// // radius: 1,\n// // slices: 16\n// // });\n// CSG.cylinder = function(options) {\n// options = options || {};\n// var s = new CSG.Vector(options.start || [0, -1, 0]);\n// var e = new CSG.Vector(options.end || [0, 1, 0]);\n// var ray = e.minus(s);\n// var r = options.radius || 1;\n// var slices = options.slices || 16;\n// var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5);\n// var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit();\n// var axisY = axisX.cross(axisZ).unit();\n// var start = new CSG.Vertex(s, axisZ.negated());\n// var end = new CSG.Vertex(e, axisZ.unit());\n// var polygons = [];\n// function point(stack, slice, normalBlend) {\n// var angle = slice * Math.PI * 2;\n// var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle)));\n// var pos = s.plus(ray.times(stack)).plus(out.times(r));\n// var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend));\n// return new CSG.Vertex(pos, normal);\n// }\n// for (var i = 0; i < slices; i++) {\n// var t0 = i / slices, t1 = (i + 1) / slices;\n// polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)]));\n// polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)]));\n// polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)]));\n// }\n// return CSG.fromPolygons(polygons);\n// };\n\n// # class Vector\n\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\nCSG.Vector = function(x, y, z) {\n if (arguments.length == 3) {\n this.x = x;\n this.y = y;\n this.z = z;\n } else if ('x' in x) {\n this.x = x.x;\n this.y = x.y;\n this.z = x.z;\n } else {\n this.x = x[0];\n this.y = x[1];\n this.z = x[2];\n }\n};\n\nCSG.Vector.prototype = {\n clone: function() {\n return new CSG.Vector(this.x, this.y, this.z);\n },\n\n negated: function() {\n return new CSG.Vector(-this.x, -this.y, -this.z);\n },\n\n plus: function(a) {\n return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z);\n },\n\n minus: function(a) {\n return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z);\n },\n\n times: function(a) {\n return new CSG.Vector(this.x * a, this.y * a, this.z * a);\n },\n\n dividedBy: function(a) {\n return new CSG.Vector(this.x / a, this.y / a, this.z / a);\n },\n\n dot: function(a) {\n return this.x * a.x + this.y * a.y + this.z * a.z;\n },\n\n lerp: function(a, t) {\n return this.plus(a.minus(this).times(t));\n },\n\n length: function() {\n return Math.sqrt(this.dot(this));\n },\n\n unit: function() {\n return this.dividedBy(this.length());\n },\n\n cross: function(a) {\n return new CSG.Vector(\n this.y * a.z - this.z * a.y,\n this.z * a.x - this.x * a.z,\n this.x * a.y - this.y * a.x\n );\n }\n};\n\n// # class Vertex\n\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\nCSG.Vertex = function(pos, normal, uv, color) {\n this.pos = new CSG.Vector(pos);\n this.normal = new CSG.Vector(normal);\n this.uv = uv && uv.clone();\n this.color = color && new CSG.Vector(color);\n};\n\nCSG.Vertex.prototype = {\n clone: function() {\n return new CSG.Vertex(\n this.pos.clone(), \n this.normal.clone(),\n this.uv && this.uv.clone(),\n this.color && this.color.clone()\n );\n },\n\n // Invert all orientation-specific data (e.g. vertex normal). Called when the\n // orientation of a polygon is flipped.\n flip: function() {\n this.normal = this.normal.negated();\n },\n\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 interpolate: function(other, t) { \n return new CSG.Vertex(\n this.pos.lerp(other.pos, t),\n this.normal.lerp(other.normal, t),\n this.uv && other.uv && this.uv.clone().lerp(other.uv, t),\n this.color && other.color && this.color.lerp(other.color, t),\n );\n }\n};\n\n// # class Plane\n\n// Represents a plane in 3D space.\n\nCSG.Plane = function(normal, w) {\n this.normal = normal;\n this.w = w;\n};\n\n// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a\n// point is on the plane.\nCSG.Plane.EPSILON = 1e-5;\n\nCSG.Plane.fromPoints = function(a, b, c) {\n var n = b.minus(a).cross(c.minus(a)).unit();\n return new CSG.Plane(n, n.dot(a));\n};\n\nCSG.Plane.prototype = {\n clone: function() {\n return new CSG.Plane(this.normal.clone(), this.w);\n },\n\n flip: function() {\n this.normal = this.normal.negated();\n this.w = -this.w;\n },\n\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 splitPolygon: function(polygon, coplanarFront, coplanarBack, front, back) {\n var COPLANAR = 0;\n var FRONT = 1;\n var BACK = 2;\n var SPANNING = 3;\n\n // Classify each point as well as the entire polygon into one of the above\n // four classes.\n var polygonType = 0;\n var types = [];\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 < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR;\n polygonType |= type;\n types.push(type);\n }\n\n // Put the polygon in the correct list, splitting it when necessary.\n switch (polygonType) {\n case COPLANAR:\n (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);\n break;\n case FRONT:\n front.push(polygon);\n break;\n case BACK:\n back.push(polygon);\n break;\n case SPANNING:\n var f = [], b = [];\n for (var i = 0; i < polygon.vertices.length; i++) {\n var j = (i + 1) % polygon.vertices.length;\n var ti = types[i], tj = types[j];\n var vi = polygon.vertices[i], vj = polygon.vertices[j];\n if (ti != BACK) f.push(vi);\n if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi);\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 if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared));\n if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared));\n break;\n }\n }\n};\n\n// # class Polygon\n\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 `CSG.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\nCSG.Polygon = function(vertices, shared) {\n this.vertices = vertices;\n this.shared = shared;\n this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);\n};\n\nCSG.Polygon.prototype = {\n clone: function() {\n var vertices = this.vertices.map(function(v) { return v.clone(); });\n return new CSG.Polygon(vertices, this.shared);\n },\n\n flip: function() {\n this.vertices.reverse().map(function(v) { v.flip(); });\n this.plane.flip();\n }\n};\n\n// # class Node\n\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\nCSG.Node = function(polygons) {\n this.plane = null;\n this.front = null;\n this.back = null;\n this.polygons = [];\n if (polygons) this.build(polygons);\n};\n\nCSG.Node.prototype = {\n clone: function() {\n var node = new CSG.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) { return p.clone(); });\n return node;\n },\n\n // Convert solid space to empty space and empty space to solid space.\n invert: function() {\n for (var i = 0; i < this.polygons.length; i++) {\n 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 },\n\n // Recursively remove all polygons in `polygons` that are inside this BSP\n // tree.\n clipPolygons: function(polygons) {\n if (!this.plane) return polygons.slice();\n var front = [], back = [];\n for (var i = 0; i < polygons.length; i++) {\n this.plane.splitPolygon(polygons[i], front, back, front, back);\n }\n if (this.front) front = this.front.clipPolygons(front);\n if (this.back) back = this.back.clipPolygons(back);\n else back = [];\n return front.concat(back);\n },\n\n // Remove all polygons in this BSP tree that are inside the other BSP tree\n // `bsp`.\n clipTo: function(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 },\n\n // Return a list of all polygons in this BSP tree.\n 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 // 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 build: function(polygons) {\n if (!polygons.length) return;\n if (!this.plane) this.plane = polygons[0].plane.clone();\n var front = [], back = [];\n for (var i = 0; i < polygons.length; i++) {\n this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);\n }\n if (front.length) {\n if (!this.front) this.front = new CSG.Node();\n this.front.build(front);\n }\n if (back.length) {\n if (!this.back) this.back = new CSG.Node();\n this.back.build(back);\n }\n }\n};\n","import CSG from \"../lib/csg.js\"\r\n\r\nexport default class Model {\r\n\r\n constructor(THREE) {\r\n this._THREE = THREE;\r\n this._csg = null;\r\n }\r\n\r\n union(model) {\r\n return csgToModel(this._THREE, this._csg.union(model._csg));\r\n }\r\n\r\n subtract(model) {\r\n return csgToModel(this._THREE, this._csg.subtract(model._csg));\r\n }\r\n\r\n intersect(model) {\r\n return csgToModel(this._THREE, this._csg.intersect(model._csg));\r\n }\r\n\r\n applyMatrix4(matrix) {\r\n const mesh = this.build();\r\n mesh.geometry.applyMatrix4(matrix);\r\n return Model._fromMesh(this._THREE, mesh);\r\n }\r\n\r\n build() {\r\n return csgToMesh(this._THREE, this._csg);\r\n }\r\n\r\n static _fromMesh(THREE, mesh) {\r\n return meshToModel(THREE, mesh);\r\n }\r\n\r\n}\r\n\r\nfunction csgToModel(THREE, csg) {\r\n const m = new Model(THREE);\r\n m._csg = csg;\r\n return m;\r\n}\r\n\r\nfunction meshToModel(THREE, mesh) {\r\n const m = new Model(THREE);\r\n m._csg = CSG.fromPolygons(meshToPolygons(THREE, mesh));\r\n return m;\r\n}\r\n\r\nfunction meshToPolygons(THREE, mesh) {\r\n\r\n // Compute transformation matrix.\r\n const clone = mesh.clone();\r\n clone.updateMatrix();\r\n\r\n // Snapshot material array.\r\n if (Array.isArray(mesh.material)) {\r\n clone.material = Array.from(mesh.material);\r\n }\r\n\r\n const polygons = [];\r\n const geom = mesh.geometry;\r\n const positions = geom.attributes.position.array;\r\n const { normal, uv, color } = geom.attributes;\r\n const hasNormalAttrib = normal !== undefined;\r\n const normals = hasNormalAttrib ? normal.array : null;\r\n const hasUvAttrib = uv !== undefined;\r\n const uvs = hasUvAttrib ? uv.array : null;\r\n const hasColorAttrib = color !== undefined;\r\n const colors = hasColorAttrib ? color.array : null;\r\n const indexAttrib = geom.index;\r\n const elemCount = indexAttrib ? indexAttrib.count : positions.length / 3;\r\n const shouldApplyMatrix = !clone.matrix.equals(new THREE.Matrix4());\r\n\r\n for (let elemIdx = 0, $3i = 0, $3i3 = 0; elemIdx < elemCount; elemIdx += 3) {\r\n const vertices = [];\r\n for (let j = 0; j < 3; ++j) {\r\n const i = (indexAttrib === null) ? elemIdx + j : indexAttrib.array[elemIdx + j];\r\n $3i = 3 * i;\r\n $3i3 = $3i + 3;\r\n const position = shouldApplyMatrix\r\n ? new THREE.Vector3().fromArray(positions, $3i).applyMatrix4(clone.matrix)\r\n : positions.subarray($3i, $3i3);\r\n const normal = hasNormalAttrib\r\n ? normals.subarray($3i, $3i3)\r\n : null;\r\n const uv = hasUvAttrib\r\n ? new THREE.Vector2().fromArray(uvs, 2 * i)\r\n : null;\r\n const color = hasColorAttrib\r\n ? colors.subarray($3i, $3i3)\r\n : null;\r\n vertices.push(new CSG.Vertex(position, normal, uv, color));\r\n }\r\n\r\n let mI = 0;\r\n for (const { start, count, materialIndex } of geom.groups) {\r\n if (elemIdx >= start && elemIdx < start + count) {\r\n mI = materialIndex;\r\n break;\r\n }\r\n }\r\n\r\n polygons.push(new CSG.Polygon(vertices, { m: clone.material, mI }));\r\n }\r\n\r\n return polygons;\r\n}\r\n\r\nfunction csgToMesh(THREE, csg) {\r\n const polygons = csg.toPolygons();\r\n\r\n // Group vertices by `Material`.\r\n\r\n let vertsCount = 0;\r\n const matMap = new Map();\r\n for (const { vertices, shared: { m, mI } } of polygons) {\r\n const mat = Array.isArray(m) ? m[mI] : m;\r\n matMap.has(mat) || matMap.set(mat, []);\r\n matMap.get(mat).push(vertices);\r\n vertsCount += vertices.length;\r\n }\r\n\r\n // Alloc typedarrays to hold buffer attributes data.\r\n\r\n const positions = new Float32Array(vertsCount * 3);\r\n const normals = new Float32Array(vertsCount * 3);\r\n const uvs = new Float32Array(vertsCount * 2);\r\n const colors = new Float32Array(vertsCount * 3);\r\n\r\n const materials = [];\r\n const geom = new THREE.BufferGeometry();\r\n\r\n let start = 0;\r\n let count = 0; // how many indices a render group contains.\r\n let positionsIdx = 0;\r\n let normalsIdx = 0;\r\n let uvsIdx = 0;\r\n let colorsIdx = 0;\r\n let materialIndex = 0;\r\n\r\n let hasNormal = false, someHasNormal = false;\r\n let hasUv = false, someHasUv = false;\r\n let hasColor = false, someHasColor = false;\r\n\r\n const indices = []; // holding actual data of element index buffer\r\n let index = 0; // index number already used\r\n\r\n for (const [material, vertsArray] of matMap.entries()) {\r\n\r\n // Indice count. Since `vertesArray[n]` holds triangle fan verts \r\n // if it has 4 verts, indice count should += 6 (2 tris).\r\n count = 0;\r\n for (const verts of vertsArray) {\r\n \r\n // Populate indices\r\n for (let i = 1, I = verts.length - 1; i < I; ++i) {\r\n indices.push(index, index + i, index + i + 1);\r\n }\r\n index += verts.length;\r\n count += (verts.length - 2) * 3;\r\n\r\n // Populate buffer attributes\r\n for (const { pos, normal, uv, color } of verts) {\r\n positions.set([pos.x, pos.y, pos.z], positionsIdx);\r\n positionsIdx += 3;\r\n\r\n someHasNormal |= (hasNormal = normal !== null);\r\n normals.set(hasNormal ? [normal.x, normal.y, normal.z] : [0, 0, 0], normalsIdx);\r\n normalsIdx += 3;\r\n\r\n someHasUv |= (hasUv = uv !== null);\r\n uvs.set(hasUv ? uv.toArray() : [0, 0], uvsIdx);\r\n uvsIdx += 2;\r\n\r\n someHasColor |= (hasColor = color !== null);\r\n colors.set(hasColor ? [color.x, color.y, color.z] : [0, 0, 0], colorsIdx);\r\n colorsIdx += 3;\r\n }\r\n }\r\n\r\n materials.push(material);\r\n geom.addGroup(start, count, materialIndex);\r\n start += count;\r\n materialIndex += 1;\r\n }\r\n\r\n // Set element index buffer.\r\n\r\n geom.index = (index > 65536)\r\n ? new THREE.Uint32BufferAttribute(indices, 1)\r\n : new THREE.Uint16BufferAttribute(indices, 1);\r\n\r\n // Pluck buffer attributes.\r\n\r\n geom.setAttribute(\"position\", new THREE.BufferAttribute(positions, 3));\r\n\r\n if (someHasNormal) {\r\n geom.setAttribute(\"normal\", new THREE.BufferAttribute(normals, 3));\r\n }\r\n\r\n if (someHasUv) {\r\n geom.setAttribute(\"uv\", new THREE.BufferAttribute(uvs, 2));\r\n }\r\n\r\n if (someHasColor) {\r\n geom.setAttribute(\"color\", new THREE.BufferAttribute(colors, 3));\r\n }\r\n\r\n return new THREE.Mesh(geom, materials);\r\n}\r\n","import Model from \"./Model.js\"\r\n\r\nexport default class Modeller {\r\n\r\n constructor(THREE) {\r\n this._THREE = THREE;\r\n }\r\n\r\n model(mesh) {\r\n return Model._fromMesh(this._THREE, mesh);\r\n }\r\n\r\n}"],"names":["CSG","this","polygons","fromPolygons","csg","prototype","clone","map","p","toPolygons","union","a","Node","b","clipTo","invert","build","allPolygons","subtract","intersect","inverse","flip","Vector","x","y","z","arguments","length","negated","plus","minus","times","dividedBy","dot","lerp","t","Math","sqrt","unit","cross","Vertex","pos","normal","uv","color","interpolate","other","Plane","w","EPSILON","fromPoints","c","n","splitPolygon","polygon","coplanarFront","coplanarBack","front","back","polygonType","types","i","vertices","type","push","plane","f","j","ti","tj","vi","vj","v","Polygon","shared","reverse","node","temp","clipPolygons","slice","concat","bsp","Model","[object Object]","THREE","_THREE","_csg","model","csgToModel","matrix","mesh","geometry","applyMatrix4","_fromMesh","vertsCount","matMap","Map","m","mI","mat","Array","isArray","has","set","get","positions","Float32Array","normals","uvs","colors","materials","geom","BufferGeometry","start","count","positionsIdx","normalsIdx","uvsIdx","colorsIdx","materialIndex","hasNormal","someHasNormal","hasUv","someHasUv","hasColor","someHasColor","indices","index","material","vertsArray","entries","verts","I","toArray","addGroup","Uint32BufferAttribute","Uint16BufferAttribute","setAttribute","BufferAttribute","Mesh","csgToMesh","updateMatrix","from","attributes","position","array","hasNormalAttrib","undefined","hasUvAttrib","hasColorAttrib","indexAttrib","elemCount","shouldApplyMatrix","equals","Matrix4","elemIdx","$3i","$3i3","Vector3","fromArray","subarray","Vector2","groups","meshToPolygons","meshToModel"],"mappings":"AAkDe,SAASA,IACtBC,KAAKC,SAAW,GAIlBF,EAAIG,aAAe,SAASD,GAC1B,IAAIE,EAAM,IAAIJ,EAEd,OADAI,EAAIF,SAAWA,EACRE,GAGTJ,EAAIK,UAAY,CACdC,MAAO,WACL,IAAIF,EAAM,IAAIJ,EAEd,OADAI,EAAIF,SAAWD,KAAKC,SAASK,KAAI,SAASC,GAAK,OAAOA,EAAEF,WACjDF,GAGTK,WAAY,WACV,OAAOR,KAAKC,UAiBdQ,MAAO,SAASN,GACd,IAAIO,EAAI,IAAIX,EAAIY,KAAKX,KAAKK,QAAQJ,UAC9BW,EAAI,IAAIb,EAAIY,KAAKR,EAAIE,QAAQJ,UAOjC,OANAS,EAAEG,OAAOD,GACTA,EAAEC,OAAOH,GACTE,EAAEE,SACFF,EAAEC,OAAOH,GACTE,EAAEE,SACFJ,EAAEK,MAAMH,EAAEI,eACHjB,EAAIG,aAAaQ,EAAEM,gBAiB5BC,SAAU,SAASd,GACjB,IAAIO,EAAI,IAAIX,EAAIY,KAAKX,KAAKK,QAAQJ,UAC9BW,EAAI,IAAIb,EAAIY,KAAKR,EAAIE,QAAQJ,UASjC,OARAS,EAAEI,SACFJ,EAAEG,OAAOD,GACTA,EAAEC,OAAOH,GACTE,EAAEE,SACFF,EAAEC,OAAOH,GACTE,EAAEE,SACFJ,EAAEK,MAAMH,EAAEI,eACVN,EAAEI,SACKf,EAAIG,aAAaQ,EAAEM,gBAiB5BE,UAAW,SAASf,GAClB,IAAIO,EAAI,IAAIX,EAAIY,KAAKX,KAAKK,QAAQJ,UAC9BW,EAAI,IAAIb,EAAIY,KAAKR,EAAIE,QAAQJ,UAQjC,OAPAS,EAAEI,SACFF,EAAEC,OAAOH,GACTE,EAAEE,SACFJ,EAAEG,OAAOD,GACTA,EAAEC,OAAOH,GACTA,EAAEK,MAAMH,EAAEI,eACVN,EAAEI,SACKf,EAAIG,aAAaQ,EAAEM,gBAK5BG,QAAS,WACP,IAAIhB,EAAMH,KAAKK,QAEf,OADAF,EAAIF,SAASK,KAAI,SAASC,GAAKA,EAAEa,UAC1BjB,IAoIXJ,EAAIsB,OAAS,SAASC,EAAGC,EAAGC,GACF,GAApBC,UAAUC,QACZ1B,KAAKsB,EAAIA,EACTtB,KAAKuB,EAAIA,EACTvB,KAAKwB,EAAIA,GACA,MAAOF,GAChBtB,KAAKsB,EAAIA,EAAEA,EACXtB,KAAKuB,EAAID,EAAEC,EACXvB,KAAKwB,EAAIF,EAAEE,IAEXxB,KAAKsB,EAAIA,EAAE,GACXtB,KAAKuB,EAAID,EAAE,GACXtB,KAAKwB,EAAIF,EAAE,KAIfvB,EAAIsB,OAAOjB,UAAY,CACrBC,MAAO,WACL,OAAO,IAAIN,EAAIsB,OAAOrB,KAAKsB,EAAGtB,KAAKuB,EAAGvB,KAAKwB,IAG7CG,QAAS,WACP,OAAO,IAAI5B,EAAIsB,QAAQrB,KAAKsB,GAAItB,KAAKuB,GAAIvB,KAAKwB,IAGhDI,KAAM,SAASlB,GACb,OAAO,IAAIX,EAAIsB,OAAOrB,KAAKsB,EAAIZ,EAAEY,EAAGtB,KAAKuB,EAAIb,EAAEa,EAAGvB,KAAKwB,EAAId,EAAEc,IAG/DK,MAAO,SAASnB,GACd,OAAO,IAAIX,EAAIsB,OAAOrB,KAAKsB,EAAIZ,EAAEY,EAAGtB,KAAKuB,EAAIb,EAAEa,EAAGvB,KAAKwB,EAAId,EAAEc,IAG/DM,MAAO,SAASpB,GACd,OAAO,IAAIX,EAAIsB,OAAOrB,KAAKsB,EAAIZ,EAAGV,KAAKuB,EAAIb,EAAGV,KAAKwB,EAAId,IAGzDqB,UAAW,SAASrB,GAClB,OAAO,IAAIX,EAAIsB,OAAOrB,KAAKsB,EAAIZ,EAAGV,KAAKuB,EAAIb,EAAGV,KAAKwB,EAAId,IAGzDsB,IAAK,SAAStB,GACZ,OAAOV,KAAKsB,EAAIZ,EAAEY,EAAItB,KAAKuB,EAAIb,EAAEa,EAAIvB,KAAKwB,EAAId,EAAEc,GAGlDS,KAAM,SAASvB,EAAGwB,GAChB,OAAOlC,KAAK4B,KAAKlB,EAAEmB,MAAM7B,MAAM8B,MAAMI,KAGvCR,OAAQ,WACN,OAAOS,KAAKC,KAAKpC,KAAKgC,IAAIhC,QAG5BqC,KAAM,WACJ,OAAOrC,KAAK+B,UAAU/B,KAAK0B,WAG7BY,MAAO,SAAS5B,GACd,OAAO,IAAIX,EAAIsB,OACbrB,KAAKuB,EAAIb,EAAEc,EAAIxB,KAAKwB,EAAId,EAAEa,EAC1BvB,KAAKwB,EAAId,EAAEY,EAAItB,KAAKsB,EAAIZ,EAAEc,EAC1BxB,KAAKsB,EAAIZ,EAAEa,EAAIvB,KAAKuB,EAAIb,EAAEY,KAehCvB,EAAIwC,OAAS,SAASC,EAAKC,EAAQC,EAAIC,GACrC3C,KAAKwC,IAAM,IAAIzC,EAAIsB,OAAOmB,GAC1BxC,KAAKyC,OAAS,IAAI1C,EAAIsB,OAAOoB,GAC7BzC,KAAK0C,GAAKA,GAAMA,EAAGrC,QACnBL,KAAK2C,MAAQA,GAAS,IAAI5C,EAAIsB,OAAOsB,IAGvC5C,EAAIwC,OAAOnC,UAAY,CACrBC,MAAO,WACL,OAAO,IAAIN,EAAIwC,OACbvC,KAAKwC,IAAInC,QACTL,KAAKyC,OAAOpC,QACZL,KAAK0C,IAAM1C,KAAK0C,GAAGrC,QACnBL,KAAK2C,OAAS3C,KAAK2C,MAAMtC,UAM7Be,KAAM,WACJpB,KAAKyC,OAASzC,KAAKyC,OAAOd,WAM5BiB,YAAa,SAASC,EAAOX,GAC3B,OAAO,IAAInC,EAAIwC,OACbvC,KAAKwC,IAAIP,KAAKY,EAAML,IAAKN,GACzBlC,KAAKyC,OAAOR,KAAKY,EAAMJ,OAAQP,GAC/BlC,KAAK0C,IAAMG,EAAMH,IAAM1C,KAAK0C,GAAGrC,QAAQ4B,KAAKY,EAAMH,GAAIR,GACtDlC,KAAK2C,OAASE,EAAMF,OAAS3C,KAAK2C,MAAMV,KAAKY,EAAMF,MAAOT,MAShEnC,EAAI+C,MAAQ,SAASL,EAAQM,GAC3B/C,KAAKyC,OAASA,EACdzC,KAAK+C,EAAIA,GAKXhD,EAAI+C,MAAME,QAAU,KAEpBjD,EAAI+C,MAAMG,WAAa,SAASvC,EAAGE,EAAGsC,GACpC,IAAIC,EAAIvC,EAAEiB,MAAMnB,GAAG4B,MAAMY,EAAErB,MAAMnB,IAAI2B,OACrC,OAAO,IAAItC,EAAI+C,MAAMK,EAAGA,EAAEnB,IAAItB,KAGhCX,EAAI+C,MAAM1C,UAAY,CACpBC,MAAO,WACL,OAAO,IAAIN,EAAI+C,MAAM9C,KAAKyC,OAAOpC,QAASL,KAAK+C,IAGjD3B,KAAM,WACJpB,KAAKyC,OAASzC,KAAKyC,OAAOd,UAC1B3B,KAAK+C,GAAK/C,KAAK+C,GAQjBK,aAAc,SAASC,EAASC,EAAeC,EAAcC,EAAOC,GAUlE,IATA,IAOIC,EAAc,EACdC,EAAQ,GACHC,EAAI,EAAGA,EAAIP,EAAQQ,SAASnC,OAAQkC,IAAK,CAChD,IACIE,GADA5B,EAAIlC,KAAKyC,OAAOT,IAAIqB,EAAQQ,SAASD,GAAGpB,KAAOxC,KAAK+C,IACvChD,EAAI+C,MAAME,QATlB,EASqCd,EAAInC,EAAI+C,MAAME,QAVlD,EADG,EAYbU,GAAeI,EACfH,EAAMI,KAAKD,GAIb,OAAQJ,GACN,KAlBa,GAmBV1D,KAAKyC,OAAOT,IAAIqB,EAAQW,MAAMvB,QAAU,EAAIa,EAAgBC,GAAcQ,KAAKV,GAChF,MACF,KApBU,EAqBRG,EAAMO,KAAKV,GACX,MACF,KAtBS,EAuBPI,EAAKM,KAAKV,GACV,MACF,KAxBa,EAyBX,IAAIY,EAAI,GAAIrD,EAAI,GAChB,IAASgD,EAAI,EAAGA,EAAIP,EAAQQ,SAASnC,OAAQkC,IAAK,CAChD,IAAIM,GAAKN,EAAI,GAAKP,EAAQQ,SAASnC,OAC/ByC,EAAKR,EAAMC,GAAIQ,EAAKT,EAAMO,GAC1BG,EAAKhB,EAAQQ,SAASD,GAAIU,EAAKjB,EAAQQ,SAASK,GAGpD,GAjCK,GA+BDC,GAAYF,EAAEF,KAAKM,GAhCjB,GAiCFF,GAAavD,EAAEmD,KAhCd,GAgCmBI,EAAaE,EAAGhE,QAAUgE,GA/BzC,IAgCJF,EAAKC,GAAiB,CACzB,IAAIlC,GAAKlC,KAAK+C,EAAI/C,KAAKyC,OAAOT,IAAIqC,EAAG7B,MAAQxC,KAAKyC,OAAOT,IAAIsC,EAAG9B,IAAIX,MAAMwC,EAAG7B,MACzE+B,EAAIF,EAAGzB,YAAY0B,EAAIpC,GAC3B+B,EAAEF,KAAKQ,GACP3D,EAAEmD,KAAKQ,EAAElE,UAGT4D,EAAEvC,QAAU,GAAG8B,EAAMO,KAAK,IAAIhE,EAAIyE,QAAQP,EAAGZ,EAAQoB,SACrD7D,EAAEc,QAAU,GAAG+B,EAAKM,KAAK,IAAIhE,EAAIyE,QAAQ5D,EAAGyC,EAAQoB,YAiBhE1E,EAAIyE,QAAU,SAASX,EAAUY,GAC/BzE,KAAK6D,SAAWA,EAChB7D,KAAKyE,OAASA,EACdzE,KAAKgE,MAAQjE,EAAI+C,MAAMG,WAAWY,EAAS,GAAGrB,IAAKqB,EAAS,GAAGrB,IAAKqB,EAAS,GAAGrB,MAGlFzC,EAAIyE,QAAQpE,UAAY,CACtBC,MAAO,WACL,IAAIwD,EAAW7D,KAAK6D,SAASvD,KAAI,SAASiE,GAAK,OAAOA,EAAElE,WACxD,OAAO,IAAIN,EAAIyE,QAAQX,EAAU7D,KAAKyE,SAGxCrD,KAAM,WACJpB,KAAK6D,SAASa,UAAUpE,KAAI,SAASiE,GAAKA,EAAEnD,UAC5CpB,KAAKgE,MAAM5C,SAYfrB,EAAIY,KAAO,SAASV,GAClBD,KAAKgE,MAAQ,KACbhE,KAAKwD,MAAQ,KACbxD,KAAKyD,KAAO,KACZzD,KAAKC,SAAW,GACZA,GAAUD,KAAKe,MAAMd,IAG3BF,EAAIY,KAAKP,UAAY,CACnBC,MAAO,WACL,IAAIsE,EAAO,IAAI5E,EAAIY,KAKnB,OAJAgE,EAAKX,MAAQhE,KAAKgE,OAAShE,KAAKgE,MAAM3D,QACtCsE,EAAKnB,MAAQxD,KAAKwD,OAASxD,KAAKwD,MAAMnD,QACtCsE,EAAKlB,KAAOzD,KAAKyD,MAAQzD,KAAKyD,KAAKpD,QACnCsE,EAAK1E,SAAWD,KAAKC,SAASK,KAAI,SAASC,GAAK,OAAOA,EAAEF,WAClDsE,GAIT7D,OAAQ,WACN,IAAK,IAAI8C,EAAI,EAAGA,EAAI5D,KAAKC,SAASyB,OAAQkC,IACxC5D,KAAKC,SAAS2D,GAAGxC,OAEnBpB,KAAKgE,MAAM5C,OACPpB,KAAKwD,OAAOxD,KAAKwD,MAAM1C,SACvBd,KAAKyD,MAAMzD,KAAKyD,KAAK3C,SACzB,IAAI8D,EAAO5E,KAAKwD,MAChBxD,KAAKwD,MAAQxD,KAAKyD,KAClBzD,KAAKyD,KAAOmB,GAKdC,aAAc,SAAS5E,GACrB,IAAKD,KAAKgE,MAAO,OAAO/D,EAAS6E,QAEjC,IADA,IAAItB,EAAQ,GAAIC,EAAO,GACdG,EAAI,EAAGA,EAAI3D,EAASyB,OAAQkC,IACnC5D,KAAKgE,MAAMZ,aAAanD,EAAS2D,GAAIJ,EAAOC,EAAMD,EAAOC,GAK3D,OAHIzD,KAAKwD,QAAOA,EAAQxD,KAAKwD,MAAMqB,aAAarB,IACjCC,EAAXzD,KAAKyD,KAAazD,KAAKyD,KAAKoB,aAAapB,GACjC,GACLD,EAAMuB,OAAOtB,IAKtB5C,OAAQ,SAASmE,GACfhF,KAAKC,SAAW+E,EAAIH,aAAa7E,KAAKC,UAClCD,KAAKwD,OAAOxD,KAAKwD,MAAM3C,OAAOmE,GAC9BhF,KAAKyD,MAAMzD,KAAKyD,KAAK5C,OAAOmE,IAIlChE,YAAa,WACX,IAAIf,EAAWD,KAAKC,SAAS6E,QAG7B,OAFI9E,KAAKwD,QAAOvD,EAAWA,EAAS8E,OAAO/E,KAAKwD,MAAMxC,gBAClDhB,KAAKyD,OAAMxD,EAAWA,EAAS8E,OAAO/E,KAAKyD,KAAKzC,gBAC7Cf,GAOTc,MAAO,SAASd,GACd,GAAKA,EAASyB,OAAd,CACK1B,KAAKgE,QAAOhE,KAAKgE,MAAQ/D,EAAS,GAAG+D,MAAM3D,SAEhD,IADA,IAAImD,EAAQ,GAAIC,EAAO,GACdG,EAAI,EAAGA,EAAI3D,EAASyB,OAAQkC,IACnC5D,KAAKgE,MAAMZ,aAAanD,EAAS2D,GAAI5D,KAAKC,SAAUD,KAAKC,SAAUuD,EAAOC,GAExED,EAAM9B,SACH1B,KAAKwD,QAAOxD,KAAKwD,MAAQ,IAAIzD,EAAIY,MACtCX,KAAKwD,MAAMzC,MAAMyC,IAEfC,EAAK/B,SACF1B,KAAKyD,OAAMzD,KAAKyD,KAAO,IAAI1D,EAAIY,MACpCX,KAAKyD,KAAK1C,MAAM0C,OCtlBP,MAAMwB,EAEjBC,YAAYC,GACRnF,KAAKoF,OAASD,EACdnF,KAAKqF,KAAO,KAGhBH,MAAMI,GACF,OAAOC,EAAWvF,KAAKoF,OAAQpF,KAAKqF,KAAK5E,MAAM6E,EAAMD,OAGzDH,SAASI,GACL,OAAOC,EAAWvF,KAAKoF,OAAQpF,KAAKqF,KAAKpE,SAASqE,EAAMD,OAG5DH,UAAUI,GACN,OAAOC,EAAWvF,KAAKoF,OAAQpF,KAAKqF,KAAKnE,UAAUoE,EAAMD,OAG7DH,aAAaM,GACT,MAAMC,EAAOzF,KAAKe,QAElB,OADA0E,EAAKC,SAASC,aAAaH,GACpBP,EAAMW,UAAU5F,KAAKoF,OAAQK,GAGxCP,QACI,OAiFR,SAAmBC,EAAOhF,GACtB,MAAMF,EAAWE,EAAIK,aAIrB,IAAIqF,EAAa,EACjB,MAAMC,EAAS,IAAIC,IACnB,IAAK,MAAMlC,SAAEA,EAAUY,QAAQuB,EAAEA,EAACC,GAAEA,MAAUhG,EAAU,CACpD,MAAMiG,EAAMC,MAAMC,QAAQJ,GAAKA,EAAEC,GAAMD,EACvCF,EAAOO,IAAIH,IAAQJ,EAAOQ,IAAIJ,EAAK,IACnCJ,EAAOS,IAAIL,GAAKnC,KAAKF,GACrBgC,GAAchC,EAASnC,OAK3B,MAAM8E,EAAY,IAAIC,aAA0B,EAAbZ,GAC7Ba,EAAU,IAAID,aAA0B,EAAbZ,GAC3Bc,EAAM,IAAIF,aAA0B,EAAbZ,GACvBe,EAAS,IAAIH,aAA0B,EAAbZ,GAE1BgB,EAAY,GACZC,EAAO,IAAI3B,EAAM4B,eAEvB,IAAIC,EAAQ,EACRC,EAAQ,EACRC,EAAe,EACfC,EAAa,EACbC,EAAS,EACTC,EAAY,EACZC,EAAgB,EAEhBC,GAAY,EAAOC,GAAgB,EACnCC,GAAQ,EAAOC,GAAY,EAC3BC,GAAW,EAAOC,GAAe,EAErC,MAAMC,EAAU,GAChB,IAAIC,EAAQ,EAEZ,IAAK,MAAOC,EAAUC,KAAelC,EAAOmC,UAAW,CAInDhB,EAAQ,EACR,IAAK,MAAMiB,KAASF,EAAY,CAG5B,IAAK,IAAIpE,EAAI,EAAGuE,EAAID,EAAMxG,OAAS,EAAGkC,EAAIuE,IAAKvE,EAC3CiE,EAAQ9D,KAAK+D,EAAOA,EAAQlE,EAAGkE,EAAQlE,EAAI,GAE/CkE,GAASI,EAAMxG,OACfuF,GAA8B,GAApBiB,EAAMxG,OAAS,GAGzB,IAAK,MAAMc,IAAEA,EAAGC,OAAEA,EAAMC,GAAEA,EAAEC,MAAEA,KAAWuF,EACrC1B,EAAUF,IAAI,CAAC9D,EAAIlB,EAAGkB,EAAIjB,EAAGiB,EAAIhB,GAAI0F,GACrCA,GAAgB,EAEhBM,GAAkBD,EAAuB,OAAX9E,EAC9BiE,EAAQJ,IAAIiB,EAAY,CAAC9E,EAAOnB,EAAGmB,EAAOlB,EAAGkB,EAAOjB,GAAK,CAAC,EAAG,EAAG,GAAI2F,GACpEA,GAAc,EAEdO,GAAcD,EAAe,OAAP/E,EACtBiE,EAAIL,IAAImB,EAAQ/E,EAAG0F,UAAY,CAAC,EAAG,GAAIhB,GACvCA,GAAU,EAEVQ,GAAiBD,EAAqB,OAAVhF,EAC5BiE,EAAON,IAAIqB,EAAW,CAAChF,EAAMrB,EAAGqB,EAAMpB,EAAGoB,EAAMnB,GAAK,CAAC,EAAG,EAAG,GAAI6F,GAC/DA,GAAa,EAIrBR,EAAU9C,KAAKgE,GACfjB,EAAKuB,SAASrB,EAAOC,EAAOK,GAC5BN,GAASC,EACTK,GAAiB,EAKrBR,EAAKgB,MAASA,EAAQ,MAChB,IAAI3C,EAAMmD,sBAAsBT,EAAS,GACzC,IAAI1C,EAAMoD,sBAAsBV,EAAS,GAI/Cf,EAAK0B,aAAa,WAAY,IAAIrD,EAAMsD,gBAAgBjC,EAAW,IAE/DgB,GACAV,EAAK0B,aAAa,SAAU,IAAIrD,EAAMsD,gBAAgB/B,EAAS,IAG/DgB,GACAZ,EAAK0B,aAAa,KAAM,IAAIrD,EAAMsD,gBAAgB9B,EAAK,IAGvDiB,GACAd,EAAK0B,aAAa,QAAS,IAAIrD,EAAMsD,gBAAgB7B,EAAQ,IAGjE,OAAO,IAAIzB,EAAMuD,KAAK5B,EAAMD,GArLjB8B,CAAU3I,KAAKoF,OAAQpF,KAAKqF,MAGvCH,iBAAiBC,EAAOM,GACpB,OAWR,SAAqBN,EAAOM,GACxB,MAAMO,EAAI,IAAIf,EAAME,GAEpB,OADAa,EAAEX,KAAOtF,EAAIG,aAIjB,SAAwBiF,EAAOM,GAG3B,MAAMpF,EAAQoF,EAAKpF,QACnBA,EAAMuI,eAGFzC,MAAMC,QAAQX,EAAKsC,YACnB1H,EAAM0H,SAAW5B,MAAM0C,KAAKpD,EAAKsC,WAGrC,MAAM9H,EAAW,GACX6G,EAAOrB,EAAKC,SACZc,EAAYM,EAAKgC,WAAWC,SAASC,OACrCvG,OAAEA,EAAMC,GAAEA,EAAEC,MAAEA,GAAUmE,EAAKgC,WAC7BG,OAA6BC,IAAXzG,EAClBiE,EAAUuC,EAAkBxG,EAAOuG,MAAQ,KAC3CG,OAAqBD,IAAPxG,EACdiE,EAAMwC,EAAczG,EAAGsG,MAAQ,KAC/BI,OAA2BF,IAAVvG,EACjBiE,EAASwC,EAAiBzG,EAAMqG,MAAQ,KACxCK,EAAcvC,EAAKgB,MACnBwB,EAAYD,EAAcA,EAAYpC,MAAQT,EAAU9E,OAAS,EACjE6H,GAAqBlJ,EAAMmF,OAAOgE,OAAO,IAAIrE,EAAMsE,SAEzD,IAAK,IAAIC,EAAU,EAAGC,EAAM,EAAGC,EAAO,EAAGF,EAAUJ,EAAWI,GAAW,EAAG,CACxE,MAAM7F,EAAW,GACjB,IAAK,IAAIK,EAAI,EAAGA,EAAI,IAAKA,EAAG,CACxB,MAAMN,EAAqB,OAAhByF,EAAwBK,EAAUxF,EAAImF,EAAYL,MAAMU,EAAUxF,GAC7EyF,EAAM,EAAI/F,EACVgG,EAAOD,EAAM,EACb,MAAMZ,EAAWQ,GACX,IAAIpE,EAAM0E,SAAUC,UAAUtD,EAAWmD,GAAKhE,aAAatF,EAAMmF,QACjEgB,EAAUuD,SAASJ,EAAKC,GACxBnH,EAASwG,EACTvC,EAAQqD,SAASJ,EAAKC,GACtB,KACAlH,EAAKyG,GACL,IAAIhE,EAAM6E,SAAUF,UAAUnD,EAAK,EAAI/C,GACvC,KACAjB,EAAQyG,EACRxC,EAAOmD,SAASJ,EAAKC,GACrB,KACN/F,EAASE,KAAK,IAAIhE,EAAIwC,OAAOwG,EAAUtG,EAAQC,EAAIC,IAGvD,IAAIsD,EAAK,EACT,IAAK,MAAMe,MAAEA,EAAKC,MAAEA,EAAKK,cAAEA,KAAmBR,EAAKmD,OAC/C,GAAIP,GAAW1C,GAAS0C,EAAU1C,EAAQC,EAAO,CAC7ChB,EAAKqB,EACL,MAIRrH,EAAS8D,KAAK,IAAIhE,EAAIyE,QAAQX,EAAU,CAAEmC,EAAG3F,EAAM0H,SAAU9B,GAAAA,KAGjE,OAAOhG,EA7DmBiK,CAAe/E,EAAOM,IACzCO,EAdImE,CAAYhF,EAAOM,IAKlC,SAASF,EAAWJ,EAAOhF,GACvB,MAAM6F,EAAI,IAAIf,EAAME,GAEpB,OADAa,EAAEX,KAAOlF,EACF6F,iBCtCI,MAEXd,YAAYC,GACRnF,KAAKoF,OAASD,EAGlBD,MAAMO,GACF,OAAOR,EAAMW,UAAU5F,KAAKoF,OAAQK"} \ No newline at end of file diff --git a/lib/csg.js b/lib/csg.js index 28ce69e..55cece6 100644 --- a/lib/csg.js +++ b/lib/csg.js @@ -1,2 +1,604 @@ -export default function CSG(){this.polygons=[]}CSG.fromPolygons=function(n){var t=new CSG;return t.polygons=n,t},CSG.prototype={clone:function(){var n=new CSG;return n.polygons=this.polygons.map((function(n){return n.clone()})),n},toPolygons:function(){return this.polygons},union:function(n){var t=new CSG.Node(this.clone().polygons),o=new CSG.Node(n.clone().polygons);return t.clipTo(o),o.clipTo(t),o.invert(),o.clipTo(t),o.invert(),t.build(o.allPolygons()),CSG.fromPolygons(t.allPolygons())},subtract:function(n){var t=new CSG.Node(this.clone().polygons),o=new CSG.Node(n.clone().polygons);return t.invert(),t.clipTo(o),o.clipTo(t),o.invert(),o.clipTo(t),o.invert(),t.build(o.allPolygons()),t.invert(),CSG.fromPolygons(t.allPolygons())},intersect:function(n){var t=new CSG.Node(this.clone().polygons),o=new CSG.Node(n.clone().polygons);return t.invert(),o.clipTo(t),o.invert(),t.clipTo(o),o.clipTo(t),t.build(o.allPolygons()),t.invert(),CSG.fromPolygons(t.allPolygons())},inverse:function(){var n=this.clone();return n.polygons.map((function(n){n.flip()})),n}},CSG.Vector=function(n,t,o){3==arguments.length?(this.x=n,this.y=t,this.z=o):"x"in n?(this.x=n.x,this.y=n.y,this.z=n.z):(this.x=n[0],this.y=n[1],this.z=n[2])},CSG.Vector.prototype={clone:function(){return new CSG.Vector(this.x,this.y,this.z)},negated:function(){return new CSG.Vector(-this.x,-this.y,-this.z)},plus:function(n){return new CSG.Vector(this.x+n.x,this.y+n.y,this.z+n.z)},minus:function(n){return new CSG.Vector(this.x-n.x,this.y-n.y,this.z-n.z)},times:function(n){return new CSG.Vector(this.x*n,this.y*n,this.z*n)},dividedBy:function(n){return new CSG.Vector(this.x/n,this.y/n,this.z/n)},dot:function(n){return this.x*n.x+this.y*n.y+this.z*n.z},lerp:function(n,t){return this.plus(n.minus(this).times(t))},length:function(){return Math.sqrt(this.dot(this))},unit:function(){return this.dividedBy(this.length())},cross:function(n){return new CSG.Vector(this.y*n.z-this.z*n.y,this.z*n.x-this.x*n.z,this.x*n.y-this.y*n.x)}},CSG.Vertex=function(n,t,o,i){this.pos=new CSG.Vector(n),this.normal=new CSG.Vector(t),this.uv=o&&o.clone(),this.color=i&&new CSG.Vector(i)},CSG.Vertex.prototype={clone:function(){return new CSG.Vertex(this.pos.clone(),this.normal.clone(),this.uv&&this.uv.clone(),this.color&&this.color.clone())},flip:function(){this.normal=this.normal.negated()},interpolate:function(n,t){return new CSG.Vertex(this.pos.lerp(n.pos,t),this.normal.lerp(n.normal,t),this.uv&&n.uv&&this.uv.clone().lerp(n.uv,t),this.color&&n.color&&this.color.lerp(n.color,t))}},CSG.Plane=function(n,t){this.normal=n,this.w=t},CSG.Plane.EPSILON=1e-5,CSG.Plane.fromPoints=function(n,t,o){var i=t.minus(n).cross(o.minus(n)).unit();return new CSG.Plane(i,i.dot(n))},CSG.Plane.prototype={clone:function(){return new CSG.Plane(this.normal.clone(),this.w)},flip:function(){this.normal=this.normal.negated(),this.w=-this.w},splitPolygon:function(n,t,o,i,s){for(var e=0,l=[],r=0;rCSG.Plane.EPSILON?1:0;e|=h,l.push(h)}switch(e){case 0:(this.normal.dot(n.plane.normal)>0?t:o).push(n);break;case 1:i.push(n);break;case 2:s.push(n);break;case 3:var c=[],u=[];for(r=0;r=3&&i.push(new CSG.Polygon(c,n.shared)),u.length>=3&&s.push(new CSG.Polygon(u,n.shared))}}},CSG.Polygon=function(n,t){this.vertices=n,this.shared=t,this.plane=CSG.Plane.fromPoints(n[0].pos,n[1].pos,n[2].pos)},CSG.Polygon.prototype={clone:function(){var n=this.vertices.map((function(n){return n.clone()}));return new CSG.Polygon(n,this.shared)},flip:function(){this.vertices.reverse().map((function(n){n.flip()})),this.plane.flip()}},CSG.Node=function(n){this.plane=null,this.front=null,this.back=null,this.polygons=[],n&&this.build(n)},CSG.Node.prototype={clone:function(){var n=new CSG.Node;return n.plane=this.plane&&this.plane.clone(),n.front=this.front&&this.front.clone(),n.back=this.back&&this.back.clone(),n.polygons=this.polygons.map((function(n){return n.clone()})),n},invert:function(){for(var n=0;n 0) vertex((i + 1) / slices, j / stacks); +// if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks); +// vertex(i / slices, (j + 1) / stacks); +// polygons.push(new CSG.Polygon(vertices)); +// } +// } +// return CSG.fromPolygons(polygons); +// }; + +// // Construct a solid cylinder. Optional parameters are `start`, `end`, +// // `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and +// // `16`. The `slices` parameter controls the tessellation. +// // +// // Example usage: +// // +// // var cylinder = CSG.cylinder({ +// // start: [0, -1, 0], +// // end: [0, 1, 0], +// // radius: 1, +// // slices: 16 +// // }); +// CSG.cylinder = function(options) { +// options = options || {}; +// var s = new CSG.Vector(options.start || [0, -1, 0]); +// var e = new CSG.Vector(options.end || [0, 1, 0]); +// var ray = e.minus(s); +// var r = options.radius || 1; +// var slices = options.slices || 16; +// var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5); +// var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit(); +// var axisY = axisX.cross(axisZ).unit(); +// var start = new CSG.Vertex(s, axisZ.negated()); +// var end = new CSG.Vertex(e, axisZ.unit()); +// var polygons = []; +// function point(stack, slice, normalBlend) { +// var angle = slice * Math.PI * 2; +// var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle))); +// var pos = s.plus(ray.times(stack)).plus(out.times(r)); +// var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend)); +// return new CSG.Vertex(pos, normal); +// } +// for (var i = 0; i < slices; i++) { +// var t0 = i / slices, t1 = (i + 1) / slices; +// polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)])); +// polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)])); +// polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)])); +// } +// return CSG.fromPolygons(polygons); +// }; + +// # class Vector + +// Represents a 3D vector. +// +// Example usage: +// +// new CSG.Vector(1, 2, 3); +// new CSG.Vector([1, 2, 3]); +// new CSG.Vector({ x: 1, y: 2, z: 3 }); + +CSG.Vector = function(x, y, z) { + if (arguments.length == 3) { + this.x = x; + this.y = y; + this.z = z; + } else if ('x' in x) { + this.x = x.x; + this.y = x.y; + this.z = x.z; + } else { + this.x = x[0]; + this.y = x[1]; + this.z = x[2]; + } +}; + +CSG.Vector.prototype = { + clone: function() { + return new CSG.Vector(this.x, this.y, this.z); + }, + + negated: function() { + return new CSG.Vector(-this.x, -this.y, -this.z); + }, + + plus: function(a) { + return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z); + }, + + minus: function(a) { + return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z); + }, + + times: function(a) { + return new CSG.Vector(this.x * a, this.y * a, this.z * a); + }, + + dividedBy: function(a) { + return new CSG.Vector(this.x / a, this.y / a, this.z / a); + }, + + dot: function(a) { + return this.x * a.x + this.y * a.y + this.z * a.z; + }, + + lerp: function(a, t) { + return this.plus(a.minus(this).times(t)); + }, + + length: function() { + return Math.sqrt(this.dot(this)); + }, + + unit: function() { + return this.dividedBy(this.length()); + }, + + cross: function(a) { + return new CSG.Vector( + this.y * a.z - this.z * a.y, + this.z * a.x - this.x * a.z, + this.x * a.y - this.y * a.x + ); + } +}; + +// # class Vertex + +// Represents a vertex of a polygon. Use your own vertex class instead of this +// one to provide additional features like texture coordinates and vertex +// colors. Custom vertex classes need to provide a `pos` property and `clone()`, +// `flip()`, and `interpolate()` methods that behave analogous to the ones +// defined by `CSG.Vertex`. This class provides `normal` so convenience +// functions like `CSG.sphere()` can return a smooth vertex normal, but `normal` +// is not used anywhere else. + +CSG.Vertex = function(pos, normal, uv, color) { + this.pos = new CSG.Vector(pos); + this.normal = new CSG.Vector(normal); + this.uv = uv && uv.clone(); + this.color = color && new CSG.Vector(color); +}; + +CSG.Vertex.prototype = { + clone: function() { + return new CSG.Vertex( + this.pos.clone(), + this.normal.clone(), + this.uv && this.uv.clone(), + this.color && this.color.clone() + ); + }, + + // Invert all orientation-specific data (e.g. vertex normal). Called when the + // orientation of a polygon is flipped. + flip: function() { + this.normal = this.normal.negated(); + }, + + // Create a new vertex between this vertex and `other` by linearly + // interpolating all properties using a parameter of `t`. Subclasses should + // override this to interpolate additional properties. + interpolate: function(other, t) { + return new CSG.Vertex( + this.pos.lerp(other.pos, t), + this.normal.lerp(other.normal, t), + this.uv && other.uv && this.uv.clone().lerp(other.uv, t), + this.color && other.color && this.color.lerp(other.color, t), + ); + } +}; + +// # class Plane + +// Represents a plane in 3D space. + +CSG.Plane = function(normal, w) { + this.normal = normal; + this.w = w; +}; + +// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a +// point is on the plane. +CSG.Plane.EPSILON = 1e-5; + +CSG.Plane.fromPoints = function(a, b, c) { + var n = b.minus(a).cross(c.minus(a)).unit(); + return new CSG.Plane(n, n.dot(a)); +}; + +CSG.Plane.prototype = { + clone: function() { + return new CSG.Plane(this.normal.clone(), this.w); + }, + + flip: function() { + this.normal = this.normal.negated(); + this.w = -this.w; + }, + + // Split `polygon` by this plane if needed, then put the polygon or polygon + // fragments in the appropriate lists. Coplanar polygons go into either + // `coplanarFront` or `coplanarBack` depending on their orientation with + // respect to this plane. Polygons in front or in back of this plane go into + // either `front` or `back`. + splitPolygon: function(polygon, coplanarFront, coplanarBack, front, back) { + var COPLANAR = 0; + var FRONT = 1; + var BACK = 2; + var SPANNING = 3; + + // Classify each point as well as the entire polygon into one of the above + // four classes. + var polygonType = 0; + var types = []; + for (var i = 0; i < polygon.vertices.length; i++) { + var t = this.normal.dot(polygon.vertices[i].pos) - this.w; + var type = (t < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR; + polygonType |= type; + types.push(type); + } + + // Put the polygon in the correct list, splitting it when necessary. + switch (polygonType) { + case COPLANAR: + (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon); + break; + case FRONT: + front.push(polygon); + break; + case BACK: + back.push(polygon); + break; + case SPANNING: + var f = [], b = []; + for (var i = 0; i < polygon.vertices.length; i++) { + var j = (i + 1) % polygon.vertices.length; + var ti = types[i], tj = types[j]; + var vi = polygon.vertices[i], vj = polygon.vertices[j]; + if (ti != BACK) f.push(vi); + if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi); + if ((ti | tj) == SPANNING) { + var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos)); + var v = vi.interpolate(vj, t); + f.push(v); + b.push(v.clone()); + } + } + if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared)); + if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared)); + break; + } + } +}; + +// # class Polygon + +// Represents a convex polygon. The vertices used to initialize a polygon must +// be coplanar and form a convex loop. They do not have to be `CSG.Vertex` +// instances but they must behave similarly (duck typing can be used for +// customization). +// +// Each convex polygon has a `shared` property, which is shared between all +// polygons that are clones of each other or were split from the same polygon. +// This can be used to define per-polygon properties (such as surface color). + +CSG.Polygon = function(vertices, shared) { + this.vertices = vertices; + this.shared = shared; + this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos); +}; + +CSG.Polygon.prototype = { + clone: function() { + var vertices = this.vertices.map(function(v) { return v.clone(); }); + return new CSG.Polygon(vertices, this.shared); + }, + + flip: function() { + this.vertices.reverse().map(function(v) { v.flip(); }); + this.plane.flip(); + } +}; + +// # class Node + +// Holds a node in a BSP tree. A BSP tree is built from a collection of polygons +// by picking a polygon to split along. That polygon (and all other coplanar +// polygons) are added directly to that node and the other polygons are added to +// the front and/or back subtrees. This is not a leafy BSP tree since there is +// no distinction between internal and leaf nodes. + +CSG.Node = function(polygons) { + this.plane = null; + this.front = null; + this.back = null; + this.polygons = []; + if (polygons) this.build(polygons); +}; + +CSG.Node.prototype = { + clone: function() { + var node = new CSG.Node(); + node.plane = this.plane && this.plane.clone(); + node.front = this.front && this.front.clone(); + node.back = this.back && this.back.clone(); + node.polygons = this.polygons.map(function(p) { return p.clone(); }); + return node; + }, + + // Convert solid space to empty space and empty space to solid space. + invert: function() { + for (var i = 0; i < this.polygons.length; i++) { + this.polygons[i].flip(); + } + this.plane.flip(); + if (this.front) this.front.invert(); + if (this.back) this.back.invert(); + var temp = this.front; + this.front = this.back; + this.back = temp; + }, + + // Recursively remove all polygons in `polygons` that are inside this BSP + // tree. + clipPolygons: function(polygons) { + if (!this.plane) return polygons.slice(); + var front = [], back = []; + for (var i = 0; i < polygons.length; i++) { + this.plane.splitPolygon(polygons[i], front, back, front, back); + } + if (this.front) front = this.front.clipPolygons(front); + if (this.back) back = this.back.clipPolygons(back); + else back = []; + return front.concat(back); + }, + + // Remove all polygons in this BSP tree that are inside the other BSP tree + // `bsp`. + clipTo: function(bsp) { + this.polygons = bsp.clipPolygons(this.polygons); + if (this.front) this.front.clipTo(bsp); + if (this.back) this.back.clipTo(bsp); + }, + + // Return a list of all polygons in this BSP tree. + allPolygons: function() { + var polygons = this.polygons.slice(); + if (this.front) polygons = polygons.concat(this.front.allPolygons()); + if (this.back) polygons = polygons.concat(this.back.allPolygons()); + return polygons; + }, + + // Build a BSP tree out of `polygons`. When called on an existing tree, the + // new polygons are filtered down to the bottom of the tree and become new + // nodes there. Each set of polygons is partitioned using the first polygon + // (no heuristic is used to pick a good split). + build: function(polygons) { + if (!polygons.length) return; + if (!this.plane) this.plane = polygons[0].plane.clone(); + var front = [], back = []; + for (var i = 0; i < polygons.length; i++) { + this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back); + } + if (front.length) { + if (!this.front) this.front = new CSG.Node(); + this.front.build(front); + } + if (back.length) { + if (!this.back) this.back = new CSG.Node(); + this.back.build(back); + } + } +}; diff --git a/lib/csg.js.map b/lib/csg.js.map deleted file mode 100644 index 80c6286..0000000 --- a/lib/csg.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["csg.max.js"],"names":["CSG","this","polygons","fromPolygons","csg","prototype","clone","map","p","toPolygons","union","a","Node","b","clipTo","invert","build","allPolygons","subtract","intersect","inverse","flip","Vector","x","y","z","arguments","length","negated","plus","minus","times","dividedBy","dot","lerp","t","Math","sqrt","unit","cross","Vertex","pos","normal","uv","color","interpolate","other","Plane","w","EPSILON","fromPoints","c","n","splitPolygon","polygon","coplanarFront","coplanarBack","front","back","polygonType","types","i","vertices","type","push","plane","f","j","ti","tj","vi","vj","v","Polygon","shared","reverse","node","temp","clipPolygons","slice","concat","bsp"],"mappings":"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"} \ No newline at end of file diff --git a/lib/csg.max.js b/lib/csg.max.js deleted file mode 100644 index 55cece6..0000000 --- a/lib/csg.max.js +++ /dev/null @@ -1,604 +0,0 @@ -// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean -// operations like union and intersection to combine 3D solids. This library -// implements CSG operations on meshes elegantly and concisely using BSP trees, -// and is meant to serve as an easily understandable implementation of the -// algorithm. All edge cases involving overlapping coplanar polygons in both -// solids are correctly handled. -// -// Example usage: -// -// var cube = CSG.cube(); -// var sphere = CSG.sphere({ radius: 1.3 }); -// var polygons = cube.subtract(sphere).toPolygons(); -// -// ## Implementation Details -// -// All CSG operations are implemented in terms of two functions, `clipTo()` and -// `invert()`, which remove parts of a BSP tree inside another BSP tree and swap -// solid and empty space, respectively. To find the union of `a` and `b`, we -// want to remove everything in `a` inside `b` and everything in `b` inside `a`, -// then combine polygons from `a` and `b` into one solid: -// -// a.clipTo(b); -// b.clipTo(a); -// a.build(b.allPolygons()); -// -// The only tricky part is handling overlapping coplanar polygons in both trees. -// The code above keeps both copies, but we need to keep them in one tree and -// remove them in the other tree. To remove them from `b` we can clip the -// inverse of `b` against `a`. The code for union now looks like this: -// -// a.clipTo(b); -// b.clipTo(a); -// b.invert(); -// b.clipTo(a); -// b.invert(); -// a.build(b.allPolygons()); -// -// Subtraction and intersection naturally follow from set operations. If -// union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is -// `A & B = ~(~A | ~B)` where `~` is the complement operator. -// -// ## License -// -// Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license. - -// # class CSG - -// Holds a binary space partition tree representing a 3D solid. Two solids can -// be combined using the `union()`, `subtract()`, and `intersect()` methods. - -export default function CSG() { - this.polygons = []; -}; - -// Construct a CSG solid from a list of `CSG.Polygon` instances. -CSG.fromPolygons = function(polygons) { - var csg = new CSG(); - csg.polygons = polygons; - return csg; -}; - -CSG.prototype = { - clone: function() { - var csg = new CSG(); - csg.polygons = this.polygons.map(function(p) { return p.clone(); }); - return csg; - }, - - toPolygons: function() { - return this.polygons; - }, - - // Return a new CSG solid representing space in either this solid or in the - // solid `csg`. Neither this solid nor the solid `csg` are modified. - // - // A.union(B) - // - // +-------+ +-------+ - // | | | | - // | A | | | - // | +--+----+ = | +----+ - // +----+--+ | +----+ | - // | B | | | - // | | | | - // +-------+ +-------+ - // - union: function(csg) { - var a = new CSG.Node(this.clone().polygons); - var b = new CSG.Node(csg.clone().polygons); - a.clipTo(b); - b.clipTo(a); - b.invert(); - b.clipTo(a); - b.invert(); - a.build(b.allPolygons()); - return CSG.fromPolygons(a.allPolygons()); - }, - - // Return a new CSG solid representing space in this solid but not in the - // solid `csg`. Neither this solid nor the solid `csg` are modified. - // - // A.subtract(B) - // - // +-------+ +-------+ - // | | | | - // | A | | | - // | +--+----+ = | +--+ - // +----+--+ | +----+ - // | B | - // | | - // +-------+ - // - subtract: function(csg) { - var a = new CSG.Node(this.clone().polygons); - var b = new CSG.Node(csg.clone().polygons); - a.invert(); - a.clipTo(b); - b.clipTo(a); - b.invert(); - b.clipTo(a); - b.invert(); - a.build(b.allPolygons()); - a.invert(); - return CSG.fromPolygons(a.allPolygons()); - }, - - // Return a new CSG solid representing space both this solid and in the - // solid `csg`. Neither this solid nor the solid `csg` are modified. - // - // A.intersect(B) - // - // +-------+ - // | | - // | A | - // | +--+----+ = +--+ - // +----+--+ | +--+ - // | B | - // | | - // +-------+ - // - intersect: function(csg) { - var a = new CSG.Node(this.clone().polygons); - var b = new CSG.Node(csg.clone().polygons); - a.invert(); - b.clipTo(a); - b.invert(); - a.clipTo(b); - b.clipTo(a); - a.build(b.allPolygons()); - a.invert(); - return CSG.fromPolygons(a.allPolygons()); - }, - - // Return a new CSG solid with solid and empty space switched. This solid is - // not modified. - inverse: function() { - var csg = this.clone(); - csg.polygons.map(function(p) { p.flip(); }); - return csg; - } -}; - -// // Construct an axis-aligned solid cuboid. Optional parameters are `center` and -// // `radius`, which default to `[0, 0, 0]` and `[1, 1, 1]`. The radius can be -// // specified using a single number or a list of three numbers, one for each axis. -// // -// // Example code: -// // -// // var cube = CSG.cube({ -// // center: [0, 0, 0], -// // radius: 1 -// // }); -// CSG.cube = function(options) { -// options = options || {}; -// var c = new CSG.Vector(options.center || [0, 0, 0]); -// var r = !options.radius ? [1, 1, 1] : options.radius.length ? -// options.radius : [options.radius, options.radius, options.radius]; -// return CSG.fromPolygons([ -// [[0, 4, 6, 2], [-1, 0, 0]], -// [[1, 3, 7, 5], [+1, 0, 0]], -// [[0, 1, 5, 4], [0, -1, 0]], -// [[2, 6, 7, 3], [0, +1, 0]], -// [[0, 2, 3, 1], [0, 0, -1]], -// [[4, 5, 7, 6], [0, 0, +1]] -// ].map(function(info) { -// return new CSG.Polygon(info[0].map(function(i) { -// var pos = new CSG.Vector( -// c.x + r[0] * (2 * !!(i & 1) - 1), -// c.y + r[1] * (2 * !!(i & 2) - 1), -// c.z + r[2] * (2 * !!(i & 4) - 1) -// ); -// return new CSG.Vertex(pos, new CSG.Vector(info[1])); -// })); -// })); -// }; - -// // Construct a solid sphere. Optional parameters are `center`, `radius`, -// // `slices`, and `stacks`, which default to `[0, 0, 0]`, `1`, `16`, and `8`. -// // The `slices` and `stacks` parameters control the tessellation along the -// // longitude and latitude directions. -// // -// // Example usage: -// // -// // var sphere = CSG.sphere({ -// // center: [0, 0, 0], -// // radius: 1, -// // slices: 16, -// // stacks: 8 -// // }); -// CSG.sphere = function(options) { -// options = options || {}; -// var c = new CSG.Vector(options.center || [0, 0, 0]); -// var r = options.radius || 1; -// var slices = options.slices || 16; -// var stacks = options.stacks || 8; -// var polygons = [], vertices; -// function vertex(theta, phi) { -// theta *= Math.PI * 2; -// phi *= Math.PI; -// var dir = new CSG.Vector( -// Math.cos(theta) * Math.sin(phi), -// Math.cos(phi), -// Math.sin(theta) * Math.sin(phi) -// ); -// vertices.push(new CSG.Vertex(c.plus(dir.times(r)), dir)); -// } -// for (var i = 0; i < slices; i++) { -// for (var j = 0; j < stacks; j++) { -// vertices = []; -// vertex(i / slices, j / stacks); -// if (j > 0) vertex((i + 1) / slices, j / stacks); -// if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks); -// vertex(i / slices, (j + 1) / stacks); -// polygons.push(new CSG.Polygon(vertices)); -// } -// } -// return CSG.fromPolygons(polygons); -// }; - -// // Construct a solid cylinder. Optional parameters are `start`, `end`, -// // `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and -// // `16`. The `slices` parameter controls the tessellation. -// // -// // Example usage: -// // -// // var cylinder = CSG.cylinder({ -// // start: [0, -1, 0], -// // end: [0, 1, 0], -// // radius: 1, -// // slices: 16 -// // }); -// CSG.cylinder = function(options) { -// options = options || {}; -// var s = new CSG.Vector(options.start || [0, -1, 0]); -// var e = new CSG.Vector(options.end || [0, 1, 0]); -// var ray = e.minus(s); -// var r = options.radius || 1; -// var slices = options.slices || 16; -// var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5); -// var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit(); -// var axisY = axisX.cross(axisZ).unit(); -// var start = new CSG.Vertex(s, axisZ.negated()); -// var end = new CSG.Vertex(e, axisZ.unit()); -// var polygons = []; -// function point(stack, slice, normalBlend) { -// var angle = slice * Math.PI * 2; -// var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle))); -// var pos = s.plus(ray.times(stack)).plus(out.times(r)); -// var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend)); -// return new CSG.Vertex(pos, normal); -// } -// for (var i = 0; i < slices; i++) { -// var t0 = i / slices, t1 = (i + 1) / slices; -// polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)])); -// polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)])); -// polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)])); -// } -// return CSG.fromPolygons(polygons); -// }; - -// # class Vector - -// Represents a 3D vector. -// -// Example usage: -// -// new CSG.Vector(1, 2, 3); -// new CSG.Vector([1, 2, 3]); -// new CSG.Vector({ x: 1, y: 2, z: 3 }); - -CSG.Vector = function(x, y, z) { - if (arguments.length == 3) { - this.x = x; - this.y = y; - this.z = z; - } else if ('x' in x) { - this.x = x.x; - this.y = x.y; - this.z = x.z; - } else { - this.x = x[0]; - this.y = x[1]; - this.z = x[2]; - } -}; - -CSG.Vector.prototype = { - clone: function() { - return new CSG.Vector(this.x, this.y, this.z); - }, - - negated: function() { - return new CSG.Vector(-this.x, -this.y, -this.z); - }, - - plus: function(a) { - return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z); - }, - - minus: function(a) { - return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z); - }, - - times: function(a) { - return new CSG.Vector(this.x * a, this.y * a, this.z * a); - }, - - dividedBy: function(a) { - return new CSG.Vector(this.x / a, this.y / a, this.z / a); - }, - - dot: function(a) { - return this.x * a.x + this.y * a.y + this.z * a.z; - }, - - lerp: function(a, t) { - return this.plus(a.minus(this).times(t)); - }, - - length: function() { - return Math.sqrt(this.dot(this)); - }, - - unit: function() { - return this.dividedBy(this.length()); - }, - - cross: function(a) { - return new CSG.Vector( - this.y * a.z - this.z * a.y, - this.z * a.x - this.x * a.z, - this.x * a.y - this.y * a.x - ); - } -}; - -// # class Vertex - -// Represents a vertex of a polygon. Use your own vertex class instead of this -// one to provide additional features like texture coordinates and vertex -// colors. Custom vertex classes need to provide a `pos` property and `clone()`, -// `flip()`, and `interpolate()` methods that behave analogous to the ones -// defined by `CSG.Vertex`. This class provides `normal` so convenience -// functions like `CSG.sphere()` can return a smooth vertex normal, but `normal` -// is not used anywhere else. - -CSG.Vertex = function(pos, normal, uv, color) { - this.pos = new CSG.Vector(pos); - this.normal = new CSG.Vector(normal); - this.uv = uv && uv.clone(); - this.color = color && new CSG.Vector(color); -}; - -CSG.Vertex.prototype = { - clone: function() { - return new CSG.Vertex( - this.pos.clone(), - this.normal.clone(), - this.uv && this.uv.clone(), - this.color && this.color.clone() - ); - }, - - // Invert all orientation-specific data (e.g. vertex normal). Called when the - // orientation of a polygon is flipped. - flip: function() { - this.normal = this.normal.negated(); - }, - - // Create a new vertex between this vertex and `other` by linearly - // interpolating all properties using a parameter of `t`. Subclasses should - // override this to interpolate additional properties. - interpolate: function(other, t) { - return new CSG.Vertex( - this.pos.lerp(other.pos, t), - this.normal.lerp(other.normal, t), - this.uv && other.uv && this.uv.clone().lerp(other.uv, t), - this.color && other.color && this.color.lerp(other.color, t), - ); - } -}; - -// # class Plane - -// Represents a plane in 3D space. - -CSG.Plane = function(normal, w) { - this.normal = normal; - this.w = w; -}; - -// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a -// point is on the plane. -CSG.Plane.EPSILON = 1e-5; - -CSG.Plane.fromPoints = function(a, b, c) { - var n = b.minus(a).cross(c.minus(a)).unit(); - return new CSG.Plane(n, n.dot(a)); -}; - -CSG.Plane.prototype = { - clone: function() { - return new CSG.Plane(this.normal.clone(), this.w); - }, - - flip: function() { - this.normal = this.normal.negated(); - this.w = -this.w; - }, - - // Split `polygon` by this plane if needed, then put the polygon or polygon - // fragments in the appropriate lists. Coplanar polygons go into either - // `coplanarFront` or `coplanarBack` depending on their orientation with - // respect to this plane. Polygons in front or in back of this plane go into - // either `front` or `back`. - splitPolygon: function(polygon, coplanarFront, coplanarBack, front, back) { - var COPLANAR = 0; - var FRONT = 1; - var BACK = 2; - var SPANNING = 3; - - // Classify each point as well as the entire polygon into one of the above - // four classes. - var polygonType = 0; - var types = []; - for (var i = 0; i < polygon.vertices.length; i++) { - var t = this.normal.dot(polygon.vertices[i].pos) - this.w; - var type = (t < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR; - polygonType |= type; - types.push(type); - } - - // Put the polygon in the correct list, splitting it when necessary. - switch (polygonType) { - case COPLANAR: - (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon); - break; - case FRONT: - front.push(polygon); - break; - case BACK: - back.push(polygon); - break; - case SPANNING: - var f = [], b = []; - for (var i = 0; i < polygon.vertices.length; i++) { - var j = (i + 1) % polygon.vertices.length; - var ti = types[i], tj = types[j]; - var vi = polygon.vertices[i], vj = polygon.vertices[j]; - if (ti != BACK) f.push(vi); - if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi); - if ((ti | tj) == SPANNING) { - var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos)); - var v = vi.interpolate(vj, t); - f.push(v); - b.push(v.clone()); - } - } - if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared)); - if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared)); - break; - } - } -}; - -// # class Polygon - -// Represents a convex polygon. The vertices used to initialize a polygon must -// be coplanar and form a convex loop. They do not have to be `CSG.Vertex` -// instances but they must behave similarly (duck typing can be used for -// customization). -// -// Each convex polygon has a `shared` property, which is shared between all -// polygons that are clones of each other or were split from the same polygon. -// This can be used to define per-polygon properties (such as surface color). - -CSG.Polygon = function(vertices, shared) { - this.vertices = vertices; - this.shared = shared; - this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos); -}; - -CSG.Polygon.prototype = { - clone: function() { - var vertices = this.vertices.map(function(v) { return v.clone(); }); - return new CSG.Polygon(vertices, this.shared); - }, - - flip: function() { - this.vertices.reverse().map(function(v) { v.flip(); }); - this.plane.flip(); - } -}; - -// # class Node - -// Holds a node in a BSP tree. A BSP tree is built from a collection of polygons -// by picking a polygon to split along. That polygon (and all other coplanar -// polygons) are added directly to that node and the other polygons are added to -// the front and/or back subtrees. This is not a leafy BSP tree since there is -// no distinction between internal and leaf nodes. - -CSG.Node = function(polygons) { - this.plane = null; - this.front = null; - this.back = null; - this.polygons = []; - if (polygons) this.build(polygons); -}; - -CSG.Node.prototype = { - clone: function() { - var node = new CSG.Node(); - node.plane = this.plane && this.plane.clone(); - node.front = this.front && this.front.clone(); - node.back = this.back && this.back.clone(); - node.polygons = this.polygons.map(function(p) { return p.clone(); }); - return node; - }, - - // Convert solid space to empty space and empty space to solid space. - invert: function() { - for (var i = 0; i < this.polygons.length; i++) { - this.polygons[i].flip(); - } - this.plane.flip(); - if (this.front) this.front.invert(); - if (this.back) this.back.invert(); - var temp = this.front; - this.front = this.back; - this.back = temp; - }, - - // Recursively remove all polygons in `polygons` that are inside this BSP - // tree. - clipPolygons: function(polygons) { - if (!this.plane) return polygons.slice(); - var front = [], back = []; - for (var i = 0; i < polygons.length; i++) { - this.plane.splitPolygon(polygons[i], front, back, front, back); - } - if (this.front) front = this.front.clipPolygons(front); - if (this.back) back = this.back.clipPolygons(back); - else back = []; - return front.concat(back); - }, - - // Remove all polygons in this BSP tree that are inside the other BSP tree - // `bsp`. - clipTo: function(bsp) { - this.polygons = bsp.clipPolygons(this.polygons); - if (this.front) this.front.clipTo(bsp); - if (this.back) this.back.clipTo(bsp); - }, - - // Return a list of all polygons in this BSP tree. - allPolygons: function() { - var polygons = this.polygons.slice(); - if (this.front) polygons = polygons.concat(this.front.allPolygons()); - if (this.back) polygons = polygons.concat(this.back.allPolygons()); - return polygons; - }, - - // Build a BSP tree out of `polygons`. When called on an existing tree, the - // new polygons are filtered down to the bottom of the tree and become new - // nodes there. Each set of polygons is partitioned using the first polygon - // (no heuristic is used to pick a good split). - build: function(polygons) { - if (!polygons.length) return; - if (!this.plane) this.plane = polygons[0].plane.clone(); - var front = [], back = []; - for (var i = 0; i < polygons.length; i++) { - this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back); - } - if (front.length) { - if (!this.front) this.front = new CSG.Node(); - this.front.build(front); - } - if (back.length) { - if (!this.back) this.back = new CSG.Node(); - this.back.build(back); - } - } -}; diff --git a/package-lock.json b/package-lock.json index 6f111f4..ec60cd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,18 +4,189 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@types/node": { + "version": "14.11.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz", + "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "jest-worker": { + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", + "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "rollup": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.29.0.tgz", + "integrity": "sha512-gtU0sjxMpsVlpuAf4QXienPmUAhd6Kc7owQ4f5lypoxBW18fw2UNYZ4NssLGsri6WhUZkE/Ts3EMRebN+gNLiQ==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -40,6 +211,15 @@ } } }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, "terser": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.4.tgz", diff --git a/package.json b/package.json index 443e3a5..4b83320 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,13 @@ "name": "three-csg-modeller", "version": "0.1.4", "description": "Solid mesh modeling for three.js", + "repository": "github:ycw/three-csg-modeller", "author": "ycw (https://github.com/ycw)", - "main": "src/index.js", + "main": "dist/lib.esm.js", "type": "module", + "typings": "src/index.d.ts", "scripts": { - "build:csg": "cd lib && terser --source-map \"url='csg.js.map'\" -c -m -o csg.js csg.max.js" + "build": "rollup -c rollup.config.js" }, "peerDependencies": { "three": ">= 0.120.0" @@ -16,11 +18,15 @@ "CSG", "modeling" ], + "files": [ + "lib", + "src", + "dist" + ], "devDependencies": { + "rollup": "^2.29.0", + "rollup-plugin-terser": "^7.0.2", "terser": "^5.3.4" }, - "files": [ - "lib", - "src" - ] + "dependencies": {} } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..c372e21 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,16 @@ +import { terser } from "rollup-plugin-terser"; + +export default { + input: "src/index.js", + output: [ + // { file: "lib.js", format: "cjs" }, + // { file: "lib.min.js", format: "cjs", plugins: [terser()] }, + // { file: "lib.esm.js", format: "esm", plugins: [terser()] }, + { + file: "dist/lib.esm.js", + format: "esm", + plugins: [terser()], + sourcemap: true + }, + ], +}; \ No newline at end of file