diff --git a/web/app/3d/hashmap.js b/web/app/3d/hashmap.js new file mode 100644 index 00000000..116ee16b --- /dev/null +++ b/web/app/3d/hashmap.js @@ -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); +}; diff --git a/web/app/workbench.js b/web/app/workbench.js index 39807bf7..1f42c393 100644 --- a/web/app/workbench.js +++ b/web/app/workbench.js @@ -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; }; diff --git a/web/index.html b/web/index.html index e51a213a..32d18711 100644 --- a/web/index.html +++ b/web/index.html @@ -23,6 +23,7 @@ +