From d634a0ce1c61daa5dc3dff4a7b405a816612f829 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 27 Jan 2022 11:34:37 -0500 Subject: [PATCH] checkpoint rebuilder --- src/geo/point.js | 2 + src/geo/slicer.js | 115 ++++++++++++++++++++++++++++++---------------- src/mesh/model.js | 10 +--- src/mesh/work.js | 44 +++++++++++++++--- 4 files changed, 116 insertions(+), 55 deletions(-) diff --git a/src/geo/point.js b/src/geo/point.js index ac21c6c7..dfbbcee4 100644 --- a/src/geo/point.js +++ b/src/geo/point.js @@ -65,6 +65,7 @@ t = p.x; p.x = p.z; p.z = t; + return this; }; PRO.swapYZ = function() { @@ -72,6 +73,7 @@ t = p.y; p.y = p.z; p.z = t; + return this; }; PRO.round = function(precision) { diff --git a/src/geo/slicer.js b/src/geo/slicer.js index d03f4544..9490730a 100644 --- a/src/geo/slicer.js +++ b/src/geo/slicer.js @@ -27,12 +27,14 @@ zMax = options.zmax || 0, zInc = options.zinc || 0, zIndexes = options.indices || [], + minStep = options.minstep || 0, zFlat = {}, // map area of z index flat areas zList = {}, // fast map of z indexes zScale, // bucket span in z units zSum = 0.0, // sanity check that points enclose non-zere volume buckets = [], // banded/grouped faces to speed up slice/search onupdate = options.onupdate || function() {}, + { debug, flat, autoDim } = options, i, j, p1, p2, p3; if (!(points && points.length)) { @@ -40,25 +42,35 @@ } // convert threejs position array into points array - if (options.flat) { + if (flat) { let array = []; for (i=0, j=points.length; i= zMin) { // 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; if (!zFlat[zkey]) { zFlat[zkey] = area; @@ -66,7 +78,7 @@ zFlat[zkey] += area; } } - if (options.autoDim) { + if (autoDim) { zMin = Math.min(zMin, p1.z, p2.z, p3.z); zMax = Math.max(zMax, p1.z, p2.z, p3.z); } @@ -81,6 +93,17 @@ } } else { 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 */ @@ -92,30 +115,38 @@ * bucket polygons into z-bounded groups (inside or crossing) * to reduce the search space in complex models */ + let zSpan = zMax - zMin; + let zSpanAvg = zSum / points.length; 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) { - // create empty buckets - for (i = 0; i < bucketCount + 1; i++) { - buckets.push({ points: [], slices: [] }); - } - // copy triples into all matching z-buckets for (i = 0; i < points.length;) { p1 = points[i++]; p2 = points[i++]; p3 = points[i++]; - let zm = Math.min(p1.z, p2.z, p3.z), - zM = Math.max(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) - zMin, bm = Math.floor(zm * zScale), bM = Math.ceil(zM * zScale); - if (bm < 0) bm = 0; - for (j = bm; j < bM; j++) { + // add point to all buckets in range + for (j = bm; j <= bM; j++) { buckets[j].points.push(p1); buckets[j].points.push(p2); buckets[j].points.push(p3); @@ -126,25 +157,19 @@ } // create buckets data structure - for (let i = 0; i < zIndexes.length; i++) { - bucketZ(i, zIndexes[i]); + for (let z of zIndexes) { + buckets[Math.floor((z - zMin) * zScale)].slices.push(z); 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() { let output = []; let count = 0; - let opt = { ...options, zmin: zMin }; + let opt = { ...options, zMin, zMax }; for (let bucket of buckets) { let { points, slices } = bucket; - for (let params of slices) { - output.push(sliceZ(params.z, points, opt)); + for (let z of slices) { + output.push(sliceZ(z, points, opt)); onupdate(0.1 + (count++ / zIndexes.length) * 0.9); } } @@ -153,7 +178,9 @@ // create slices from each bucketed region 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 */ function sliceZ(z, points, options = {}) { - let phash = {}, + let { zMin, zMax, under, over, both } = options, + phash = {}, lines = [], - p1, p2, p3, - zmin = options.zmin; + p1, p2, p3; // iterate over matching buckets for this z offset for (let i = 0; i < points.length; ) { @@ -255,7 +282,10 @@ // one side of triangle is on the Z plane and 3rd is below // drop lines with 3rd above because that leads to ambiguities // 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)); } } else if (where.on.length === 3) { @@ -284,11 +314,16 @@ // de-dup and group 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 }; + } } /** diff --git a/src/mesh/model.js b/src/mesh/model.js index 0d41df30..59044fd0 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -160,14 +160,8 @@ mesh.model = class MeshModel extends mesh.object { }).then(data => { console.log({rebuild: this.id, data}); let points = []; - for (let arr of data) { - arr.push(arr[0]); - for (let i=0; i { - let layers = []; - log(`${id} | ${sliced.length} slices`); - for (let slice of sliced) { - for (let poly of slice.groups) { - layers.push(poly.points.map(p => [ p.x, p.y, p.z ])); + base.slice(points, { + autoDim: true, + flat: true, + zinc: 0, + both: true, + // debug: true, + 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); }); + }); } };