checkpoint rebuilder
This commit is contained in:
parent
d847640feb
commit
d634a0ce1c
4 changed files with 116 additions and 55 deletions
|
|
@ -65,6 +65,7 @@
|
||||||
t = p.x;
|
t = p.x;
|
||||||
p.x = p.z;
|
p.x = p.z;
|
||||||
p.z = t;
|
p.z = t;
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.swapYZ = function() {
|
PRO.swapYZ = function() {
|
||||||
|
|
@ -72,6 +73,7 @@
|
||||||
t = p.y;
|
t = p.y;
|
||||||
p.y = p.z;
|
p.y = p.z;
|
||||||
p.z = t;
|
p.z = t;
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.round = function(precision) {
|
PRO.round = function(precision) {
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,14 @@
|
||||||
zMax = options.zmax || 0,
|
zMax = options.zmax || 0,
|
||||||
zInc = options.zinc || 0,
|
zInc = options.zinc || 0,
|
||||||
zIndexes = options.indices || [],
|
zIndexes = options.indices || [],
|
||||||
|
minStep = options.minstep || 0,
|
||||||
zFlat = {}, // map area of z index flat areas
|
zFlat = {}, // map area of z index flat areas
|
||||||
zList = {}, // fast map of z indexes
|
zList = {}, // fast map of z indexes
|
||||||
zScale, // bucket span in z units
|
zScale, // bucket span in z units
|
||||||
zSum = 0.0, // sanity check that points enclose non-zere volume
|
zSum = 0.0, // sanity check that points enclose non-zere volume
|
||||||
buckets = [], // banded/grouped faces to speed up slice/search
|
buckets = [], // banded/grouped faces to speed up slice/search
|
||||||
onupdate = options.onupdate || function() {},
|
onupdate = options.onupdate || function() {},
|
||||||
|
{ debug, flat, autoDim } = options,
|
||||||
i, j, p1, p2, p3;
|
i, j, p1, p2, p3;
|
||||||
|
|
||||||
if (!(points && points.length)) {
|
if (!(points && points.length)) {
|
||||||
|
|
@ -40,25 +42,35 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert threejs position array into points array
|
// convert threejs position array into points array
|
||||||
if (options.flat) {
|
if (flat) {
|
||||||
let array = [];
|
let array = [];
|
||||||
for (i=0, j=points.length; i<j; ) {
|
for (i=0, j=points.length; i<j; ) {
|
||||||
array.push(newPoint(points[i++], points[i++], points[i++]));
|
array.push(newPoint(
|
||||||
|
points[i++].round(3),
|
||||||
|
points[i++].round(3),
|
||||||
|
points[i++].round(3)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
points = array;
|
points = array;
|
||||||
|
} else {
|
||||||
|
// round points
|
||||||
|
for (i = 0; i < points.length; i++) {
|
||||||
|
points[i] = points[i].round(3);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather z-index stats
|
// gather z-index stats
|
||||||
for (i = 0; i < points.length;) {
|
for (i = 0; i < points.length;) {
|
||||||
p1 = points[i++].round(3);
|
p1 = points[i++];
|
||||||
p2 = points[i++].round(3);
|
p2 = points[i++];
|
||||||
p3 = points[i++].round(3);
|
p3 = points[i++];
|
||||||
// used to calculate buckets
|
// used to calculate buckets (rough sum of z span)
|
||||||
zSum += (Math.abs(p1.z - p2.z) + Math.abs(p2.z - p3.z) + Math.abs(p3.z - p1.z));
|
zSum += (Math.abs(p1.z - p2.z) + Math.abs(p2.z - p3.z) + Math.abs(p3.z - p1.z));
|
||||||
// use co-flat and co-line detection to adjust slice Z
|
// use co-flat and co-line detection to adjust slice Z
|
||||||
if (p1.z === p2.z && p2.z === p3.z && p1.z >= zMin) {
|
if (p1.z === p2.z && p2.z === p3.z && p1.z >= zMin) {
|
||||||
// detect faces co-planar with Z and sum the enclosed area
|
// detect faces co-planar with Z and sum the enclosed area
|
||||||
let zkey = p1.z.toFixed(5),
|
let zkey = p1.z,
|
||||||
area = Math.abs(util.area2(p1,p2,p3)) / 2;
|
area = Math.abs(util.area2(p1,p2,p3)) / 2;
|
||||||
if (!zFlat[zkey]) {
|
if (!zFlat[zkey]) {
|
||||||
zFlat[zkey] = area;
|
zFlat[zkey] = area;
|
||||||
|
|
@ -66,7 +78,7 @@
|
||||||
zFlat[zkey] += area;
|
zFlat[zkey] += area;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.autoDim) {
|
if (autoDim) {
|
||||||
zMin = Math.min(zMin, p1.z, p2.z, p3.z);
|
zMin = Math.min(zMin, p1.z, p2.z, p3.z);
|
||||||
zMax = Math.max(zMax, p1.z, p2.z, p3.z);
|
zMax = Math.max(zMax, p1.z, p2.z, p3.z);
|
||||||
}
|
}
|
||||||
|
|
@ -81,6 +93,17 @@
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
zIndexes = Object.values(zList).sort((a,b) => a - b);
|
zIndexes = Object.values(zList).sort((a,b) => a - b);
|
||||||
|
if (minStep > 0) {
|
||||||
|
let lastOut;
|
||||||
|
zIndexes = zIndexes.filter(v => {
|
||||||
|
if (lastOut !== undefined && v - lastOut < minStep) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
lastOut = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** short-circuit for microscopic and invalid objects */
|
/** short-circuit for microscopic and invalid objects */
|
||||||
|
|
@ -92,30 +115,38 @@
|
||||||
* bucket polygons into z-bounded groups (inside or crossing)
|
* bucket polygons into z-bounded groups (inside or crossing)
|
||||||
* to reduce the search space in complex models
|
* to reduce the search space in complex models
|
||||||
*/
|
*/
|
||||||
|
let zSpan = zMax - zMin;
|
||||||
|
let zSpanAvg = zSum / points.length;
|
||||||
let bucketCount = options.bucket !== false ?
|
let bucketCount = options.bucket !== false ?
|
||||||
Math.max(1, Math.ceil(zMax / (zSum / points.length)) - 1) : 1;
|
Math.max(1, Math.floor(zSpan / zSpanAvg)) : 1;
|
||||||
|
|
||||||
zScale = 1 / (zMax / bucketCount);
|
zScale = 1 / (zSpan / bucketCount);
|
||||||
|
|
||||||
// console.log({zMin, zMax, zIndexes, points, bucketCount, zScale, zSum});
|
if (debug) {
|
||||||
|
console.log({
|
||||||
|
zMin, zMax, zIndexes, zScale, zSum, zSpanAvg,
|
||||||
|
points, bucketCount,
|
||||||
|
options, buckets
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// create empty buckets
|
||||||
|
for (i = 0; i < bucketCount + 1; i++) {
|
||||||
|
buckets.push({ points: [], slices: [] });
|
||||||
|
}
|
||||||
|
|
||||||
if (bucketCount > 1) {
|
if (bucketCount > 1) {
|
||||||
// create empty buckets
|
|
||||||
for (i = 0; i < bucketCount + 1; i++) {
|
|
||||||
buckets.push({ points: [], slices: [] });
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy triples into all matching z-buckets
|
// copy triples into all matching z-buckets
|
||||||
for (i = 0; i < points.length;) {
|
for (i = 0; i < points.length;) {
|
||||||
p1 = points[i++];
|
p1 = points[i++];
|
||||||
p2 = points[i++];
|
p2 = points[i++];
|
||||||
p3 = points[i++];
|
p3 = points[i++];
|
||||||
let zm = Math.min(p1.z, p2.z, p3.z),
|
let zm = Math.min(p1.z, p2.z, p3.z) - zMin,
|
||||||
zM = Math.max(p1.z, p2.z, p3.z),
|
zM = Math.max(p1.z, p2.z, p3.z) - zMin,
|
||||||
bm = Math.floor(zm * zScale),
|
bm = Math.floor(zm * zScale),
|
||||||
bM = Math.ceil(zM * zScale);
|
bM = Math.ceil(zM * zScale);
|
||||||
if (bm < 0) bm = 0;
|
// add point to all buckets in range
|
||||||
for (j = bm; j < bM; j++) {
|
for (j = bm; j <= bM; j++) {
|
||||||
buckets[j].points.push(p1);
|
buckets[j].points.push(p1);
|
||||||
buckets[j].points.push(p2);
|
buckets[j].points.push(p2);
|
||||||
buckets[j].points.push(p3);
|
buckets[j].points.push(p3);
|
||||||
|
|
@ -126,25 +157,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// create buckets data structure
|
// create buckets data structure
|
||||||
for (let i = 0; i < zIndexes.length; i++) {
|
for (let z of zIndexes) {
|
||||||
bucketZ(i, zIndexes[i]);
|
buckets[Math.floor((z - zMin) * zScale)].slices.push(z);
|
||||||
onupdate((i / zIndexes.length) * 0.1);
|
onupdate((i / zIndexes.length) * 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bucketZ(index, z) {
|
|
||||||
buckets[Math.floor(z * zScale)].slices.push({
|
|
||||||
index, z, total: zIndexes.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sliceBuckets() {
|
async function sliceBuckets() {
|
||||||
let output = [];
|
let output = [];
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let opt = { ...options, zmin: zMin };
|
let opt = { ...options, zMin, zMax };
|
||||||
for (let bucket of buckets) {
|
for (let bucket of buckets) {
|
||||||
let { points, slices } = bucket;
|
let { points, slices } = bucket;
|
||||||
for (let params of slices) {
|
for (let z of slices) {
|
||||||
output.push(sliceZ(params.z, points, opt));
|
output.push(sliceZ(z, points, opt));
|
||||||
onupdate(0.1 + (count++ / zIndexes.length) * 0.9);
|
onupdate(0.1 + (count++ / zIndexes.length) * 0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +178,9 @@
|
||||||
|
|
||||||
// create slices from each bucketed region
|
// create slices from each bucketed region
|
||||||
let slices = await sliceBuckets();
|
let slices = await sliceBuckets();
|
||||||
return slices.sort((a,b) => a.index - b.index);
|
slices = slices.sort((a,b) => a.z - b.z);
|
||||||
|
|
||||||
|
return { slices, points, zMin, zMax, zIndexes, zFlat };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -235,10 +262,10 @@
|
||||||
* @param {number} z
|
* @param {number} z
|
||||||
*/
|
*/
|
||||||
function sliceZ(z, points, options = {}) {
|
function sliceZ(z, points, options = {}) {
|
||||||
let phash = {},
|
let { zMin, zMax, under, over, both } = options,
|
||||||
|
phash = {},
|
||||||
lines = [],
|
lines = [],
|
||||||
p1, p2, p3,
|
p1, p2, p3;
|
||||||
zmin = options.zmin;
|
|
||||||
|
|
||||||
// iterate over matching buckets for this z offset
|
// iterate over matching buckets for this z offset
|
||||||
for (let i = 0; i < points.length; ) {
|
for (let i = 0; i < points.length; ) {
|
||||||
|
|
@ -255,7 +282,10 @@
|
||||||
// one side of triangle is on the Z plane and 3rd is below
|
// one side of triangle is on the Z plane and 3rd is below
|
||||||
// drop lines with 3rd above because that leads to ambiguities
|
// drop lines with 3rd above because that leads to ambiguities
|
||||||
// with complex nested polygons on flat surface
|
// with complex nested polygons on flat surface
|
||||||
if (where.under.length === 1 || z === zmin) {
|
let add2 = both ||
|
||||||
|
(over && (where.over.length === 1 || z === zMax)) ||
|
||||||
|
(under && (where.under.length === 1 || z === zMin));
|
||||||
|
if (add2) {
|
||||||
lines.push(makeZLine(phash, where.on[0], where.on[1], false, true));
|
lines.push(makeZLine(phash, where.on[0], where.on[1], false, true));
|
||||||
}
|
}
|
||||||
} else if (where.on.length === 3) {
|
} else if (where.on.length === 3) {
|
||||||
|
|
@ -284,11 +314,16 @@
|
||||||
|
|
||||||
// de-dup and group lines
|
// de-dup and group lines
|
||||||
lines = removeDuplicateLines(lines);
|
lines = removeDuplicateLines(lines);
|
||||||
let groups = options.connect !== false ?
|
|
||||||
connectLines(lines, z, { debug: options.debug }) :
|
|
||||||
undefined;
|
|
||||||
|
|
||||||
return { lines, groups };
|
// connect lines into longest contiguous chains
|
||||||
|
if (options.group) {
|
||||||
|
return {
|
||||||
|
z, lines,
|
||||||
|
groups: connectLines(lines, z, { debug: options.debug })
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return { z, lines };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -160,14 +160,8 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
console.log({rebuild: this.id, data});
|
console.log({rebuild: this.id, data});
|
||||||
let points = [];
|
let points = [];
|
||||||
for (let arr of data) {
|
for (let i=0; i<data.length;) {
|
||||||
arr.push(arr[0]);
|
points.push(new THREE.Vector3(data[i++], data[i++], data[i++]));
|
||||||
for (let i=0; i<arr.length-1; i++) {
|
|
||||||
let p1 = arr[i];
|
|
||||||
let p2 = arr[i+1];
|
|
||||||
points.push(new THREE.Vector3().fromArray(p1));
|
|
||||||
points.push(new THREE.Vector3().fromArray(p2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let material = new THREE.LineBasicMaterial( { color: 0xdddddd } );
|
let material = new THREE.LineBasicMaterial( { color: 0xdddddd } );
|
||||||
let geometry = new THREE.BufferGeometry().setFromPoints(points);
|
let geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ gapp.finalize("mesh.work", [
|
||||||
"moto.client", // dep: moto.client
|
"moto.client", // dep: moto.client
|
||||||
"moto.worker", // dep: moto.worker
|
"moto.worker", // dep: moto.worker
|
||||||
"mesh.tool", // dep: mesh.tool
|
"mesh.tool", // dep: mesh.tool
|
||||||
|
"mesh.util", // dep: mesh.util
|
||||||
"add.three", // dep: add.three
|
"add.three", // dep: add.three
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ let { Matrix4, Vector3, BufferGeometry, BufferAttribute, computeFaceNormal } = T
|
||||||
let core_matrix = new Matrix4().makeRotationX(Math.PI / 2);
|
let core_matrix = new Matrix4().makeRotationX(Math.PI / 2);
|
||||||
|
|
||||||
let { client, worker } = moto;
|
let { client, worker } = moto;
|
||||||
|
let { util } = mesh;
|
||||||
let cache = {};
|
let cache = {};
|
||||||
|
|
||||||
function log(msg) {
|
function log(msg) {
|
||||||
|
|
@ -297,17 +299,45 @@ let model = {
|
||||||
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`);
|
||||||
base.slice(points, { flat: true, autoDim: true, zinc: 0 })
|
base.slice(points, {
|
||||||
.then(sliced => {
|
autoDim: true,
|
||||||
let layers = [];
|
flat: true,
|
||||||
log(`${id} | ${sliced.length} slices`);
|
zinc: 0,
|
||||||
for (let slice of sliced) {
|
both: true,
|
||||||
for (let poly of slice.groups) {
|
// debug: true,
|
||||||
layers.push(poly.points.map(p => [ p.x, p.y, p.z ]));
|
minstep: 0.25,
|
||||||
|
}).then(output => {
|
||||||
|
let layers = [];
|
||||||
|
let { points, slices } = output;
|
||||||
|
log(`${id} | ${slices.length} slices Z`);
|
||||||
|
for (let slice of slices) {
|
||||||
|
for (let line of slice.lines) {
|
||||||
|
layers.appendAll(util.extract(line.p1));
|
||||||
|
layers.appendAll(util.extract(line.p2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// send.done(layers);
|
||||||
|
for (let p of points) p.swapXZ();
|
||||||
|
base.slice(points, {
|
||||||
|
autoDim: true,
|
||||||
|
zinc: 0,
|
||||||
|
both: true,
|
||||||
|
// debug: true,
|
||||||
|
minstep: 0.25,
|
||||||
|
}).then(output => {
|
||||||
|
let { points, slices } = output;
|
||||||
|
log(`${id} | ${slices.length} slices X`);
|
||||||
|
for (let slice of slices) {
|
||||||
|
for (let line of slice.lines) {
|
||||||
|
if (!line.p1.swapped) { line.p1.swapXZ().swapped = true }
|
||||||
|
if (!line.p2.swapped) { line.p2.swapXZ().swapped = true }
|
||||||
|
layers.appendAll(util.extract(line.p1));
|
||||||
|
layers.appendAll(util.extract(line.p2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
send.done(layers);
|
send.done(layers);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue