testing csg. add geo triangulate (todo replace earcut calls with this)
This commit is contained in:
parent
c0f158d4e4
commit
78a2e0a35e
4 changed files with 533 additions and 4 deletions
|
|
@ -461,6 +461,60 @@
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wrapper for earcut that handles higher order dimensions and finds the
|
||||||
|
// two with the greatest delta to pass to the earcut algorith, then returns
|
||||||
|
// an unwrapped array in the original dimensions
|
||||||
|
function triangulate(array, holes, dims) {
|
||||||
|
let narray, d1, d2;
|
||||||
|
if (dims === 2) {
|
||||||
|
narray = array;
|
||||||
|
} else {
|
||||||
|
let min = new Array(dims).fill(Infinity);
|
||||||
|
let max = new Array(dims).fill(-Infinity);
|
||||||
|
for (let i=0, l=array.length; i<l; ) {
|
||||||
|
for (let j=0, av; j<dims; j++) {
|
||||||
|
av = array[i++];
|
||||||
|
min[j] = Math.min(min[j], av);
|
||||||
|
max[j] = Math.max(max[j], av);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let delta = new Array(dims);
|
||||||
|
for (let i=0; i<dims; i++) {
|
||||||
|
delta[i] = max[i] - min[i];
|
||||||
|
}
|
||||||
|
console.log({delta: delta.slice()});
|
||||||
|
let dmax = 0; //d1, d2, dmax = 0;
|
||||||
|
for (let i=0; i<dims; i++) {
|
||||||
|
if (delta[i] > dmax) {
|
||||||
|
dmax = delta[i];
|
||||||
|
d1 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delta[d1] = dmax = 0;
|
||||||
|
for (let i=0; i<dims; i++) {
|
||||||
|
if (delta[i] > dmax) {
|
||||||
|
dmax = delta[i];
|
||||||
|
d2 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
narray = new Array((array.length / dims) * 2);
|
||||||
|
for (let i=0, j=0; i<array.length; i += dims) {
|
||||||
|
narray[j++] = array[i + d1];
|
||||||
|
narray[j++] = array[i + d2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ec = earcut(narray, holes, 2);
|
||||||
|
let oa = new Array(ec.length * dims);
|
||||||
|
for (let i=0, e=0, l=ec.length, ai; i<l; i++) {
|
||||||
|
ai = ec[i] * dims;
|
||||||
|
for (let j=0; j<dims; j++) {
|
||||||
|
oa[e++] = array[ ai + j ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log({d1, d2, array, narray, ec, oa});
|
||||||
|
return oa;
|
||||||
|
}
|
||||||
|
|
||||||
/** ******************************************************************
|
/** ******************************************************************
|
||||||
* Connect to base
|
* Connect to base
|
||||||
******************************************************************* */
|
******************************************************************* */
|
||||||
|
|
@ -566,6 +620,7 @@
|
||||||
numOrDefault,
|
numOrDefault,
|
||||||
orderClosest,
|
orderClosest,
|
||||||
zInPlane,
|
zInPlane,
|
||||||
|
triangulate,
|
||||||
comma: (v) => {
|
comma: (v) => {
|
||||||
if (!v) return v;
|
if (!v) return v;
|
||||||
let [lt,rt] = v.toString().split('.');
|
let [lt,rt] = v.toString().split('.');
|
||||||
|
|
|
||||||
448
src/geo/csg.js
Normal file
448
src/geo/csg.js
Normal file
|
|
@ -0,0 +1,448 @@
|
||||||
|
// ## License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.
|
||||||
|
// Modified for GridApps by Stewart Allen (sa@grid.space)
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
// dep: geo.base
|
||||||
|
let base = self.base;
|
||||||
|
if (base.CSG) return;
|
||||||
|
|
||||||
|
// # class CSG
|
||||||
|
// Holds a binary space partition tree representing a 3D solid. Two solids can
|
||||||
|
// be combined using the `union()`, `subtract()`, and `intersect()` methods.
|
||||||
|
class CSG {
|
||||||
|
constructor(polygons) {
|
||||||
|
this.polygons = polygons || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
let csg = new CSG();
|
||||||
|
csg.polygons = this.polygons.map(p=>p.clone())
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
|
||||||
|
toPolygons() {
|
||||||
|
return this.polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
union(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
subtract(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
intersect(csg) {
|
||||||
|
let a = new Node(this.clone().polygons);
|
||||||
|
let b = new 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() {
|
||||||
|
let csg = this.clone();
|
||||||
|
csg.polygons.forEach(p=>p.flip());
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a CSG solid from a list of `Polygon` instances.
|
||||||
|
CSG.fromPolygons = function(polygons) {
|
||||||
|
return new CSG(polygons);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct a CSG from a THREE Geometry BufferAttribute array (or similar)
|
||||||
|
CSG.fromPositionArray = function(array) {
|
||||||
|
let polys = [];
|
||||||
|
for (let i=0, l=array.length; i<l; ) {
|
||||||
|
polys.push(new Polygon([
|
||||||
|
new Vertex(new Vector(
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
)),
|
||||||
|
new Vertex(new Vector(
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
)),
|
||||||
|
new Vertex(new Vector(
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
array[i++],
|
||||||
|
)),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
return CSG.fromPolygons(polys);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.computeFaceNormal = function(vA,vB,vC) {
|
||||||
|
let cb = vC.clone().sub(vB);
|
||||||
|
let ab = vA.clone().sub(vB);
|
||||||
|
cb.cross(ab);
|
||||||
|
return cb.normalize();
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Vector
|
||||||
|
class Vector {
|
||||||
|
constructor(x=0, y=0, z=0) {
|
||||||
|
this.x=x;
|
||||||
|
this.y=y;
|
||||||
|
this.z=z;
|
||||||
|
}
|
||||||
|
copy(v){
|
||||||
|
this.x=v.x;
|
||||||
|
this.y=v.y;
|
||||||
|
this.z=v.z;
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
return new Vector(this.x,this.y,this.z)
|
||||||
|
}
|
||||||
|
negate() {
|
||||||
|
this.x*=-1;
|
||||||
|
this.y*=-1;
|
||||||
|
this.z*=-1;
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
add(a) {
|
||||||
|
this.x+=a.x
|
||||||
|
this.y+=a.y
|
||||||
|
this.z+=a.z
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
sub(a) {
|
||||||
|
this.x-=a.x
|
||||||
|
this.y-=a.y
|
||||||
|
this.z-=a.z
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
times(a) {
|
||||||
|
this.x*=a
|
||||||
|
this.y*=a
|
||||||
|
this.z*=a
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
dividedBy(a) {
|
||||||
|
this.x/=a
|
||||||
|
this.y/=a
|
||||||
|
this.z/=a
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
lerp(a, t) {
|
||||||
|
return this.add(tv0.copy(a).sub(this).times(t))
|
||||||
|
}
|
||||||
|
unit() {
|
||||||
|
return this.dividedBy(this.length())
|
||||||
|
}
|
||||||
|
length(){
|
||||||
|
return Math.sqrt((this.x**2)+(this.y**2)+(this.z**2))
|
||||||
|
}
|
||||||
|
normalize(){
|
||||||
|
return this.unit()
|
||||||
|
}
|
||||||
|
cross(b) {
|
||||||
|
let a = this;
|
||||||
|
const ax = a.x, ay = a.y, az = a.z;
|
||||||
|
const bx = b.x, by = b.y, bz = b.z;
|
||||||
|
|
||||||
|
this.x = ay * bz - az * by;
|
||||||
|
this.y = az * bx - ax * bz;
|
||||||
|
this.z = ax * by - ay * bx;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
dot(b){
|
||||||
|
return (this.x*b.x)+(this.y*b.y)+(this.z*b.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Temporaries used to avoid internal allocation..
|
||||||
|
let tv0 = new Vector()
|
||||||
|
let tv1 = new Vector()
|
||||||
|
|
||||||
|
// zero normal for Vertices when none provided
|
||||||
|
let nnn = new Vector();
|
||||||
|
|
||||||
|
// # 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.
|
||||||
|
class Vertex {
|
||||||
|
constructor(pos, normal, uv, color) {
|
||||||
|
this.pos = new Vector().copy(pos);
|
||||||
|
this.normal = new Vector().copy(normal || nnn);
|
||||||
|
uv && (this.uv = new Vector().copy(uv)) && (this.uv.z=0);
|
||||||
|
color && (this.color = new Vector().copy(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Vertex(this.pos,this.normal,this.uv,this.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert all orientation-specific data (e.g. vertex normal). Called when the
|
||||||
|
// orientation of a polygon is flipped.
|
||||||
|
flip() {
|
||||||
|
this.normal.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(other, t) {
|
||||||
|
return new Vertex(this.pos.clone().lerp(other.pos, t),this.normal.clone().lerp(other.normal, t),this.uv&&other.uv&&this.uv.clone().lerp(other.uv, t), this.color&&other.color&&this.color.clone().lerp(other.color,t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// # class Plane
|
||||||
|
// Represents a plane in 3D space.
|
||||||
|
class Plane {
|
||||||
|
constructor(normal, w) {
|
||||||
|
this.normal = normal;
|
||||||
|
this.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Plane(this.normal.clone(),this.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
flip() {
|
||||||
|
this.normal.negate();
|
||||||
|
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(polygon, coplanarFront, coplanarBack, front, back) {
|
||||||
|
const COPLANAR = 0;
|
||||||
|
const FRONT = 1;
|
||||||
|
const BACK = 2;
|
||||||
|
const SPANNING = 3;
|
||||||
|
|
||||||
|
// Classify each point as well as the entire polygon into one of the above
|
||||||
|
// four classes.
|
||||||
|
let polygonType = 0;
|
||||||
|
let types = [];
|
||||||
|
for (let i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
let t = this.normal.dot(polygon.vertices[i].pos) - this.w;
|
||||||
|
let type = (t < -Plane.EPSILON) ? BACK : (t > 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:
|
||||||
|
let f = []
|
||||||
|
, b = [];
|
||||||
|
for (let i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
let j = (i + 1) % polygon.vertices.length;
|
||||||
|
let ti = types[i]
|
||||||
|
, tj = types[j];
|
||||||
|
let 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) {
|
||||||
|
let t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(tv0.copy(vj.pos).sub(vi.pos));
|
||||||
|
let v = vi.interpolate(vj, t);
|
||||||
|
f.push(v);
|
||||||
|
b.push(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f.length >= 3)
|
||||||
|
front.push(new Polygon(f,polygon.shared));
|
||||||
|
if (b.length >= 3)
|
||||||
|
back.push(new Polygon(b,polygon.shared));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// `Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
|
||||||
|
// point is on the plane.
|
||||||
|
Plane.EPSILON = 1e-5;
|
||||||
|
|
||||||
|
Plane.fromPoints = function(a, b, c) {
|
||||||
|
let n = tv0.copy(b).sub(a).cross(tv1.copy(c).sub(a)).normalize()
|
||||||
|
return new Plane(n.clone(),n.dot(a));
|
||||||
|
};
|
||||||
|
|
||||||
|
// # 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 `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).
|
||||||
|
class Polygon {
|
||||||
|
constructor(vertices, shared) {
|
||||||
|
this.vertices = vertices;
|
||||||
|
this.shared = shared;
|
||||||
|
this.plane = Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
return new Polygon(this.vertices.map(v=>v.clone()),this.shared);
|
||||||
|
}
|
||||||
|
flip() {
|
||||||
|
this.vertices.reverse().forEach(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.
|
||||||
|
class Node {
|
||||||
|
constructor(polygons) {
|
||||||
|
this.plane = null;
|
||||||
|
this.front = null;
|
||||||
|
this.back = null;
|
||||||
|
this.polygons = [];
|
||||||
|
if (polygons)
|
||||||
|
this.build(polygons);
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
let node = new 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(p=>p.clone());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert solid space to empty space and empty space to solid space.
|
||||||
|
invert() {
|
||||||
|
for (let i = 0; i < this.polygons.length; i++)
|
||||||
|
this.polygons[i].flip();
|
||||||
|
|
||||||
|
this.plane && this.plane.flip();
|
||||||
|
this.front && this.front.invert();
|
||||||
|
this.back && this.back.invert();
|
||||||
|
let temp = this.front;
|
||||||
|
this.front = this.back;
|
||||||
|
this.back = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively remove all polygons in `polygons` that are inside this BSP tree.
|
||||||
|
clipPolygons(polygons) {
|
||||||
|
if (!this.plane)
|
||||||
|
return polygons.slice();
|
||||||
|
let front = []
|
||||||
|
, back = [];
|
||||||
|
for (let 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;
|
||||||
|
return front.concat(back);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all polygons in this BSP tree that are inside the other BSP tree `bsp`.
|
||||||
|
clipTo(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() {
|
||||||
|
let 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(polygons) {
|
||||||
|
if (!polygons.length)
|
||||||
|
return;
|
||||||
|
if (!this.plane)
|
||||||
|
this.plane = polygons[0].plane.clone();
|
||||||
|
let front = []
|
||||||
|
, back = [];
|
||||||
|
for (let 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 Node();
|
||||||
|
this.front.build(front);
|
||||||
|
}
|
||||||
|
if (back.length) {
|
||||||
|
if (!this.back)
|
||||||
|
this.back = new Node();
|
||||||
|
this.back.build(back);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to base objects
|
||||||
|
base.CSG = { Solid: CSG, Vector, Vertex, Plane, Node };
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
@ -367,7 +367,7 @@ let tool = {
|
||||||
mesh.api.group.new(models)
|
mesh.api.group.new(models)
|
||||||
.centerModels()
|
.centerModels()
|
||||||
.position(mid.x, mid.y, mid.z)
|
.position(mid.x, mid.y, mid.z)
|
||||||
.select();
|
.setSelected();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -391,7 +391,7 @@ let tool = {
|
||||||
}
|
}
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
if (newmdl.length) {
|
if (newmdl.length) {
|
||||||
mesh.api.group.new(newmdl, undefined, "patch").select();
|
mesh.api.group.new(newmdl, undefined, "patch").setSelected();
|
||||||
}
|
}
|
||||||
api.log.emit('analysis complete').unpin();
|
api.log.emit('analysis complete').unpin();
|
||||||
});
|
});
|
||||||
|
|
@ -405,6 +405,10 @@ let tool = {
|
||||||
id: m.id, opt
|
id: m.id, opt
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
// mesh.api.group.new([new mesh.model({
|
||||||
|
// file: `healed`,
|
||||||
|
// mesh: data.vertices
|
||||||
|
// })]).floor().centerXY().setSelected();
|
||||||
m.reload(
|
m.reload(
|
||||||
data.vertices,
|
data.vertices,
|
||||||
data.indices,
|
data.indices,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ moto.client.start(`/code/mesh_pool?${gapp.version}`, moto.client.max() * 0);
|
||||||
// dep: geo.polygons
|
// dep: geo.polygons
|
||||||
// dep: geo.bounds
|
// dep: geo.bounds
|
||||||
// dep: geo.slicer
|
// dep: geo.slicer
|
||||||
|
// dep: geo.csg
|
||||||
// dep: ext.clip2
|
// dep: ext.clip2
|
||||||
gapp.finalize("mesh.work", [
|
gapp.finalize("mesh.work", [
|
||||||
"moto.license", // dep: moto.license
|
"moto.license", // dep: moto.license
|
||||||
|
|
@ -243,8 +244,29 @@ let model = {
|
||||||
let { id, opt } = data;
|
let { id, opt } = data;
|
||||||
let tool = analyze(id, opt);
|
let tool = analyze(id, opt);
|
||||||
log(`${id} | unrolling...`);
|
log(`${id} | unrolling...`);
|
||||||
|
let unrolled = tool.unrolled();
|
||||||
|
|
||||||
|
// log(`${id} | intersecting...`);
|
||||||
|
// let box = new THREE.BoxGeometry(10000, 10000, 10000).attributes.position.array;
|
||||||
|
// box = base.CSG.Solid.fromPositionArray(box);
|
||||||
|
//
|
||||||
|
// let solid = base.CSG.Solid.fromPositionArray(unrolled);
|
||||||
|
// // let inter = box.intersect(solid);
|
||||||
|
// let inter = solid.union(solid.clone());
|
||||||
|
// let ears = inter.polygons
|
||||||
|
// .map(p => p.vertices.map(v => v.pos))
|
||||||
|
// .map(a => {
|
||||||
|
// let pa = a.map( v => [ v.x, v.y, v.z ]).flat().map(v => v.round(2));
|
||||||
|
// let ec = base.util.triangulate(pa, undefined, 3);
|
||||||
|
// console.log({pa, ec});
|
||||||
|
// return ec;
|
||||||
|
// });//.flat();
|
||||||
|
//
|
||||||
|
// console.log({solid, inter, ears, ef: ears.flat()});
|
||||||
|
// unrolled = ears.flat();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vertices: tool.unrolled().toFloat32(),
|
vertices: unrolled.toFloat32(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -295,7 +317,7 @@ let model = {
|
||||||
|
|
||||||
rebuild(data, send) {
|
rebuild(data, send) {
|
||||||
send.async();
|
send.async();
|
||||||
let { id, matrix } = data;
|
let { id, matrix, mode } = data;
|
||||||
log(`${id} | rebuilding...`);
|
log(`${id} | rebuilding...`);
|
||||||
let points = translate_encode(id, matrix);
|
let points = translate_encode(id, matrix);
|
||||||
log(`${id} | ${points.length} points`);
|
log(`${id} | ${points.length} points`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue