mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-15 21:05:22 +01:00
redo algorithm for merging csg polygons
This commit is contained in:
parent
9f6aae0aa7
commit
a803c5e5f4
3 changed files with 248 additions and 185 deletions
160
web/app/3d/hashmap.js
Normal file
160
web/app/3d/hashmap.js
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
TCAD.struct = {};
|
||||
|
||||
TCAD.struct.HashTable = function(hashCodeF, equalsF) {
|
||||
this.hashCodeF = hashCodeF;
|
||||
this.equalsF = equalsF;
|
||||
this.setTableSize(8);
|
||||
this.size = 0;
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.hash = function(key) {
|
||||
return Math.abs(this.hashCodeF(key) % this.table.length);
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.get = function(key) {
|
||||
var entry = this._findEntry(key, this._findBucket(key));
|
||||
if (entry == null) return null;
|
||||
return entry[1];
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.put = function(key, value) {
|
||||
if (this.size >= 0.75 * this.table.length) {
|
||||
this.rebuild();
|
||||
}
|
||||
this._put(key, value);
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype._findBucket = function(key) {
|
||||
var hash = this.hash(key);
|
||||
var bucket = this.table[hash];
|
||||
if (bucket === null) {
|
||||
bucket = [];
|
||||
this.table[hash] = bucket;
|
||||
}
|
||||
return bucket;
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype._findEntry = function(key, bucket) {
|
||||
for (var i = 0; i < bucket.length; i++) {
|
||||
if (this.equalsF(bucket[i][0], key)) {
|
||||
return bucket[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype._put = function(key, value) {
|
||||
var bucket = this._findBucket(key);
|
||||
var entry = this._findEntry(key, bucket);
|
||||
if (entry == null) {
|
||||
bucket.push([key, value]);
|
||||
} else {
|
||||
entry[1] = value;
|
||||
}
|
||||
this.size++;
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.rebuild = function() {
|
||||
this.size = 0;
|
||||
var oldTable = this.table;
|
||||
this.setTableSize(this.table.length * 2);
|
||||
for (var i = 0; i < oldTable.length; i++) {
|
||||
var e = oldTable[i];
|
||||
if (e != null) {
|
||||
for (var j = 0; j < e.length; j++) {
|
||||
var bucket = e[j];
|
||||
this._put(bucket[0], bucket[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.entries = function(callback) {
|
||||
for (var i = 0; i < this.table.length; i++) {
|
||||
var e = this.table[i];
|
||||
if (e != null) {
|
||||
for (var j = 0; j < e.length; j++) {
|
||||
var bucket = e[j];
|
||||
callback(bucket[0], bucket[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TCAD.struct.HashTable.prototype.setTableSize = function(newSize) {
|
||||
this.table = [];
|
||||
for (var i = 0; i < newSize; i++) {
|
||||
this.table[i] = null;
|
||||
}
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable = {};
|
||||
|
||||
TCAD.struct.hashTable.DoubleHelper = function() {
|
||||
this.dv = new DataView(new ArrayBuffer(8));
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.DoubleHelper.prototype.hash = function(v) {
|
||||
this.dv.setFloat64(0, v);
|
||||
return this.dv.getInt32(0) ^ this.dv.getInt32(4);
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.vectorEquals = function(a, b) {
|
||||
return a.x === b.x && a.y === b.y && a.z === b.z;
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.forVector3d = function() {
|
||||
var doubleHelper = new TCAD.struct.hashTable.DoubleHelper();
|
||||
function hash(v) {
|
||||
return doubleHelper.hash(v.x) ^ doubleHelper.hash(v.y) ^ doubleHelper.hash(v.z);
|
||||
}
|
||||
return new TCAD.struct.HashTable(hash, TCAD.struct.hashTable.vectorEquals);
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.forEdge = function() {
|
||||
var doubleHelper = new TCAD.struct.hashTable.DoubleHelper();
|
||||
function hash(v) {
|
||||
return doubleHelper.hash(v[0].x) ^ doubleHelper.hash(v[0].y) ^ doubleHelper.hash(v[0].z)
|
||||
^doubleHelper.hash(v[1].x) ^ doubleHelper.hash(v[1].y) ^ doubleHelper.hash(v[1].z);
|
||||
}
|
||||
function veq(a, b) {
|
||||
return a.x === b.x && a.y === b.y && a.z === b.z;
|
||||
}
|
||||
function eq(e1, e2) {
|
||||
var a1 = e1[0];
|
||||
var b1 = e1[1];
|
||||
var a2 = e2[0];
|
||||
var b2 = e2[1];
|
||||
return (veq(a1, a2) && veq(b1, b2)) || (veq(a1, b2) && veq(b1, a2));
|
||||
}
|
||||
return new TCAD.struct.HashTable(hash, eq);
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.forVector2d = function() {
|
||||
var doubleHelper = new TCAD.struct.hashTable.DoubleHelper();
|
||||
function hash(v) {
|
||||
return doubleHelper.hash(v.x) ^ doubleHelper.hash(v.y) ;
|
||||
}
|
||||
function eq(a, b) {
|
||||
return a.x === b.x && a.y === b.y;
|
||||
}
|
||||
return new TCAD.struct.HashTable(hash, eq);
|
||||
};
|
||||
|
||||
TCAD.struct.hashTable.forDoubleArray = function() {
|
||||
var doubleHelper = new TCAD.struct.hashTable.DoubleHelper();
|
||||
function hash(v) {
|
||||
var hash = 0;
|
||||
for (var i = 0; i < v.length; i++) {
|
||||
hash ^= v[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
function eq(a, b) {
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return new TCAD.struct.HashTable(hash, eq);
|
||||
};
|
||||
|
|
@ -202,14 +202,8 @@ TCAD.craft._mergeCSGPolygonsTest = function() {
|
|||
};
|
||||
|
||||
TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
||||
var pointToPoly = {};
|
||||
var points = [];
|
||||
var pkey = TCAD.craft.pkey;
|
||||
|
||||
function pnkey(point, normal) {
|
||||
return pkey(point) + ":" + pkey(normal);
|
||||
}
|
||||
|
||||
function vec(p) {
|
||||
var v = new TCAD.Vector();
|
||||
v.setV(p);
|
||||
|
|
@ -219,14 +213,6 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
|||
// var tol = Math.round(1 / TCAD.TOLERANCE);
|
||||
var tol = 1E6;
|
||||
|
||||
function round(num) {
|
||||
return Math.round(num * tol) / tol;
|
||||
}
|
||||
|
||||
function roundV(v) {
|
||||
return v.set(round(v.x), round(v.y), round(v.z));
|
||||
}
|
||||
|
||||
function prepare(__cgsPolygons) {
|
||||
var counter = 0;
|
||||
var polygons = __cgsPolygons.map(function (cp) {
|
||||
|
|
@ -284,12 +270,6 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for (var i = 0; i < points.length; i++) {
|
||||
// roundV(points[i]);
|
||||
// }
|
||||
|
||||
//polygons = polygons.filter(function(e){return e.normal.equals(new TCAD.Vector(-1,0,0)) });
|
||||
return polygons;
|
||||
}
|
||||
|
||||
|
|
@ -325,12 +305,8 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
|||
for (var gi = 0; gi < gons.length; gi++) {
|
||||
var pointPoly = gons[gi];
|
||||
if (poly.id === pointPoly.id) continue POLYGONS;
|
||||
if (pointPoly.normal.equals(poly.normal)) {
|
||||
hasNormal = true;
|
||||
}
|
||||
}
|
||||
if (!hasNormal) continue;
|
||||
|
||||
|
||||
var n = poly.vertices.length;
|
||||
|
||||
var add = [];
|
||||
|
|
@ -356,116 +332,11 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
|||
return polygons;
|
||||
}
|
||||
|
||||
function triangulate(polygons) {
|
||||
var triangles = [];
|
||||
for (var ei = 0; ei < polygons.length; ++ei) {
|
||||
var poly = polygons[ei];
|
||||
var nvec = poly.normal;
|
||||
var refs = new TCAD.Polygon(poly.vertices, [], nvec).triangulate();
|
||||
for ( var i = 0; i < refs.length; ++ i ) {
|
||||
var a = refs[i][0];
|
||||
var b = refs[i][1];
|
||||
var c = refs[i][2];
|
||||
var triangle = {
|
||||
vertices : [
|
||||
poly.vertices[a],
|
||||
poly.vertices[b],
|
||||
poly.vertices[c]
|
||||
],
|
||||
normal : poly.normal
|
||||
};
|
||||
triangles.push(triangle);
|
||||
}
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
var polygons = prepare(__cgsPolygons);
|
||||
polygons = mergeVertices(polygons);
|
||||
//polygons = triangulate(polygons);
|
||||
// return polygons;
|
||||
//return polygons;
|
||||
|
||||
var pi, vi, poly, key, vert;
|
||||
var pid = 0;
|
||||
for (pi = 0; pi < polygons.length; pi++) {
|
||||
poly = polygons[pi];
|
||||
poly.id = pi;
|
||||
for (vi = 0; vi < poly.vertices.length; vi++) {
|
||||
vert = poly.vertices[vi];
|
||||
key = pkey(vert);
|
||||
var pList = pointToPoly[key];
|
||||
if (pList === undefined) {
|
||||
pointToPoly[key] = [poly];
|
||||
points.push(vert);
|
||||
} else {
|
||||
pList.push(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNeighbors(vertices, i) {
|
||||
var a = i - 1;
|
||||
var b = i + 1;
|
||||
if (a < 0) a = vertices.length - 1;
|
||||
if (b == vertices.length) b = 0;
|
||||
return [a, b];
|
||||
}
|
||||
|
||||
function pointIdx(vertices, key) {
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
var v = vertices[i];
|
||||
if (pkey(v) === key) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getDirs(vertices, key) {
|
||||
var idx = pointIdx(vertices, key);
|
||||
if (idx != -1) {
|
||||
return getNeighbors(vertices, idx).map(function(i) { return vertices[i]; });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function sharesEdge(masterPolyId, v1, v2, key1, key2, normalKey) {
|
||||
var e1 = v2.minus(v1).normalize();
|
||||
var pp1 = pointToPoly[key1];
|
||||
function along(v) {
|
||||
var e = v.minus(v1).normalize();
|
||||
return e.equals(e1);
|
||||
}
|
||||
for (var ii = 0; ii < pp1.length; ii++) {
|
||||
var poly = pp1[ii];
|
||||
if (pkey(poly.normal) !== normalKey) {
|
||||
continue;
|
||||
}
|
||||
if (masterPolyId === poly.id) continue;
|
||||
var idx = pointIdx(poly.vertices, key1);
|
||||
if (idx != -1) {
|
||||
var neighbors = getNeighbors(poly.vertices, idx);
|
||||
if (along(poly.vertices[neighbors[0]]) ||
|
||||
along(poly.vertices[neighbors[1]])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var paths = [];
|
||||
var path;
|
||||
var visited = {};
|
||||
function nextUnvisitedPolygon(p, key) {
|
||||
var polygons = pointToPoly[key];
|
||||
for (var pi = 0; pi < polygons.length; pi++) {
|
||||
var poly = polygons[pi];
|
||||
var nkey = pnkey(p, poly.normal);
|
||||
if (visited[nkey] === undefined) return poly;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function deleteRedundantPoints(path) {
|
||||
var n = path.length;
|
||||
if (n < 3) return path;
|
||||
|
|
@ -494,72 +365,103 @@ TCAD.craft._mergeCSGPolygons = function (__cgsPolygons, allPoints) {
|
|||
return path;
|
||||
}
|
||||
|
||||
var p, pCurr, keyCurr, keyPrev, keyStart, pStart;
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
var point = points[i];
|
||||
key = pkey(point);
|
||||
var unvPoly = nextUnvisitedPolygon(point, key);
|
||||
if (unvPoly == null) {
|
||||
continue;
|
||||
var edges = TCAD.struct.hashTable.forEdge();
|
||||
|
||||
for (var pi = 0; pi < polygons.length; pi++) {
|
||||
var poly = polygons[pi];
|
||||
var n = poly.vertices.length, p, q;
|
||||
for (p = n - 1, q = 0; q < n; p = q ++) {
|
||||
var a = poly.vertices[p];
|
||||
var b = poly.vertices[q];
|
||||
|
||||
var edge = [a, b, poly];
|
||||
var shares = edges.get(edge);
|
||||
if (shares === null) {
|
||||
shares = 0;
|
||||
}
|
||||
edges.put(edge, shares + 1);
|
||||
}
|
||||
var normal = unvPoly.normal;
|
||||
var w = unvPoly.w;
|
||||
var normalKey = pkey(normal);
|
||||
}
|
||||
|
||||
pCurr = point;
|
||||
pStart = point;
|
||||
keyCurr = key;
|
||||
keyStart = key;
|
||||
|
||||
path = [];
|
||||
keyPrev = null;
|
||||
var veq = TCAD.struct.hashTable.vectorEquals;
|
||||
|
||||
visited[pnkey(pCurr, normal)] = true;
|
||||
var foundNext = true;
|
||||
var csgInfo;
|
||||
while (foundNext) {
|
||||
foundNext = false;
|
||||
path.push(vec(pCurr));
|
||||
var gons = pointToPoly[keyCurr];
|
||||
POLY:
|
||||
for (pi = 0; pi < gons.length; pi++) {
|
||||
poly = gons[pi];
|
||||
csgInfo = poly.csgInfo;
|
||||
if (normalKey != pkey(poly.normal)) continue;
|
||||
var dirs = getDirs(poly.vertices, keyCurr);
|
||||
if (dirs == null) continue;
|
||||
for (vi = 0; vi < dirs.length; vi++) {
|
||||
p = dirs[vi];
|
||||
key = pkey(p);
|
||||
|
||||
if (keyStart === key) continue;
|
||||
if (keyCurr === key) continue;
|
||||
if (keyPrev != null && keyPrev === key) continue;
|
||||
var nkey = pnkey(p, poly.normal);
|
||||
if (sharesEdge(poly.id, pCurr, p, keyCurr, key, normalKey)) continue;
|
||||
if (visited[nkey] !== undefined) continue;
|
||||
visited[nkey] = true;
|
||||
var paths = [];
|
||||
var csgDatas = [];
|
||||
var index = TCAD.struct.hashTable.forVector3d();
|
||||
|
||||
pCurr = p;
|
||||
keyPrev = keyCurr;
|
||||
keyCurr = key;
|
||||
foundNext = true;
|
||||
break POLY;
|
||||
}
|
||||
function indexPoint(p, edge) {
|
||||
var edges = index.get(p);
|
||||
if (edges === null) {
|
||||
edges = [];
|
||||
index.put(p, edges);
|
||||
}
|
||||
edges.push(edge);
|
||||
}
|
||||
|
||||
var edgesToProcess = [];
|
||||
edges.entries(function(k, v) {
|
||||
if (v === 1) {
|
||||
indexPoint(k[0], k);
|
||||
indexPoint(k[1], k);
|
||||
k[3] = false;
|
||||
edgesToProcess.push(k);
|
||||
}
|
||||
});
|
||||
|
||||
function nextPoint(p) {
|
||||
var edges = index.get(p);
|
||||
if (edges === null) return null;
|
||||
for (var i = 0; i < edges.length; i++) {
|
||||
var edge = edges[i]
|
||||
if (edge[3]) continue;
|
||||
var res = null;
|
||||
if (veq(p, edge[0])) res = edge[1];
|
||||
if (veq(p, edge[1])) res = edge[0];
|
||||
if (res != null) {
|
||||
edge[3] = true;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
path = deleteRedundantPoints(path);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var ei = 0; ei < edgesToProcess.length; ei++) {
|
||||
var edge = edgesToProcess[ei];
|
||||
if (edge[3]) {
|
||||
continue;
|
||||
}
|
||||
edge[3] = true;
|
||||
var path = [edge[0], edge[1]];
|
||||
paths.push(path);
|
||||
csgDatas.push(edge[2]);
|
||||
var next = nextPoint(edge[1]);
|
||||
while (next !== null) {
|
||||
if (!veq(next, path[0])) {
|
||||
path.push(next);
|
||||
next = nextPoint(next);
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var filteredPaths = [];
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
var path = deleteRedundantPoints(paths[i]);
|
||||
var csgData = csgDatas[i];
|
||||
if (path.length > 2) {
|
||||
paths.push({
|
||||
filteredPaths.push({
|
||||
vertices : path,
|
||||
normal : normal,
|
||||
w : w,
|
||||
csgInfo : csgInfo
|
||||
normal : csgData.normal,
|
||||
w : csgData.w,
|
||||
csgInfo : csgData.csgInfo
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
return filteredPaths;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<script src="app/math/math.js"></script>
|
||||
<script src="app/workbench.js"></script>
|
||||
<script src="app/math/graph.js"></script>
|
||||
<script src="app/3d/hashmap.js"></script>
|
||||
|
||||
<script>window.onload = function() {
|
||||
window._TCAD_APP = new TCAD.App();
|
||||
|
|
|
|||
Loading…
Reference in a new issue