redo algorithm for merging csg polygons

This commit is contained in:
Val Erastov 2015-08-30 01:38:26 -07:00
parent 9f6aae0aa7
commit a803c5e5f4
3 changed files with 248 additions and 185 deletions

160
web/app/3d/hashmap.js Normal file
View 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);
};

View file

@ -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;
};

View file

@ -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();