public OptimizedMesh(SimpleMesh simpleMesh, OptimizedMeshCollection containingCollection) { this.containingCollection = containingCollection; vertices.Capacity = simpleMesh.faceTriangles.Count * 3; facesTriangle.Capacity = simpleMesh.faceTriangles.Count; Dictionary <int, List <int> > indexMap = new Dictionary <int, List <int> >(); Stopwatch t = new Stopwatch(); t.Start(); for (int faceIndex = 0; faceIndex < simpleMesh.faceTriangles.Count; faceIndex++) { if (MatterSlice.Canceled) { return; } OptimizedFace optimizedFace = new OptimizedFace(); if ((faceIndex % 1000 == 0) && t.Elapsed.TotalSeconds > 2) { LogOutput.logProgress("optimized", faceIndex + 1, simpleMesh.faceTriangles.Count); } for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { IntPoint p = simpleMesh.faceTriangles[faceIndex].vertices[vertexIndex]; int hash = (int)(((p.X + MELD_DIST / 2) / MELD_DIST) ^ (((p.Y + MELD_DIST / 2) / MELD_DIST) << 10) ^ (((p.Z + MELD_DIST / 2) / MELD_DIST) << 20)); int idx = 0; bool add = true; if (indexMap.ContainsKey(hash)) { for (int n = 0; n < indexMap[hash].Count; n++) { if ((vertices[indexMap[hash][n]].position - p).Length() < MELD_DIST) { idx = indexMap[hash][n]; add = false; break; } } } if (add) { if (!indexMap.ContainsKey(hash)) { indexMap.Add(hash, new List <int>()); } indexMap[hash].Add(vertices.Count); idx = vertices.Count; vertices.Add(new OptimizedPoint3(p)); } optimizedFace.vertexIndex[vertexIndex] = idx; } if (optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[1] && optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[2] && optimizedFace.vertexIndex[1] != optimizedFace.vertexIndex[2]) { //Check if there is a face with the same points bool duplicate = false; for (int _idx0 = 0; _idx0 < vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Count; _idx0++) { for (int _idx1 = 0; _idx1 < vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Count; _idx1++) { for (int _idx2 = 0; _idx2 < vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Count; _idx2++) { if (vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[1]].usedByFacesList[_idx1] && vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[2]].usedByFacesList[_idx2]) { duplicate = true; } } } } if (!duplicate) { vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Add(facesTriangle.Count); facesTriangle.Add(optimizedFace); } } } //fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t)); int openFacesCount = 0; for (int faceIndex = 0; faceIndex < facesTriangle.Count; faceIndex++) { OptimizedFace optimizedFace = facesTriangle[faceIndex]; optimizedFace.touchingFaces[0] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[0], optimizedFace.vertexIndex[1], faceIndex); optimizedFace.touchingFaces[1] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[1], optimizedFace.vertexIndex[2], faceIndex); optimizedFace.touchingFaces[2] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[2], optimizedFace.vertexIndex[0], faceIndex); if (optimizedFace.touchingFaces[0] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[1] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[2] == -1) { openFacesCount++; } } //fprintf(stdout, " Number of open faces: %i\n", openFacesCount); }
public void GenerateSupportGrid(OptimizedModel model, ConfigSettings config) { this.generated = false; if (config.supportEndAngle < 0) { return; } this.generated = true; this.gridOffset.X = model.minXYZ_um.x; this.gridOffset.Y = model.minXYZ_um.y; this.gridScale = 200; this.gridWidth = (model.size_um.x / this.gridScale) + 1; this.gridHeight = (model.size_um.y / this.gridScale) + 1; int gridSize = this.gridWidth * this.gridHeight; this.xYGridOfSupportPoints = new List <List <SupportPoint> >(gridSize); for (int i = 0; i < gridSize; i++) { this.xYGridOfSupportPoints.Add(new List <SupportPoint>()); } this.endAngleDegrees = config.supportEndAngle; this.generateInternalSupport = config.generateInternalSupport; this.supportXYDistance_um = config.supportXYDistance_um; this.supportLayerHeight_um = config.layerThickness_um; this.supportZGapLayers = config.supportNumberOfLayersToSkipInZ; this.supportInterfaceLayers = config.supportInterfaceLayers; // This should really be a ray intersection as later code is going to count on it being an even odd list of bottoms and tops. // As it is we are finding the hit on the plane but not checking for good intersection with the triangle. for (int volumeIndex = 0; volumeIndex < model.volumes.Count; volumeIndex++) { OptimizedVolume vol = model.volumes[volumeIndex]; for (int faceIndex = 0; faceIndex < vol.facesTriangle.Count; faceIndex++) { OptimizedFace faceTriangle = vol.facesTriangle[faceIndex]; Point3 v0 = vol.vertices[faceTriangle.vertexIndex[0]].position; Point3 v1 = vol.vertices[faceTriangle.vertexIndex[1]].position; Point3 v2 = vol.vertices[faceTriangle.vertexIndex[2]].position; // get the angle of this polygon double angleFromHorizon; { FPoint3 v0f = new FPoint3(v0); FPoint3 v1f = new FPoint3(v1); FPoint3 v2f = new FPoint3(v2); FPoint3 normal = (v1f - v0f).Cross(v2f - v0f); normal.z = Math.Abs(normal.z); angleFromHorizon = (Math.PI / 2) - FPoint3.CalculateAngle(normal, FPoint3.Up); } v0.x = (int)((v0.x - this.gridOffset.X) / (double)this.gridScale + .5); v0.y = (int)((v0.y - this.gridOffset.Y) / (double)this.gridScale + .5); v1.x = (int)((v1.x - this.gridOffset.X) / (double)this.gridScale + .5); v1.y = (int)((v1.y - this.gridOffset.Y) / (double)this.gridScale + .5); v2.x = (int)((v2.x - this.gridOffset.X) / (double)this.gridScale + .5); v2.y = (int)((v2.y - this.gridOffset.Y) / (double)this.gridScale + .5); if (v0.x > v1.x) { swap(ref v0, ref v1); } if (v1.x > v2.x) { swap(ref v1, ref v2); } if (v0.x > v1.x) { swap(ref v0, ref v1); } for (long x = v0.x; x < v1.x; x++) { long y0 = (long)(v0.y + (v1.y - v0.y) * (x - v0.x) / (double)(v1.x - v0.x) + .5); long y1 = (long)(v0.y + (v2.y - v0.y) * (x - v0.x) / (double)(v2.x - v0.x) + .5); long z0 = (long)(v0.z + (v1.z - v0.z) * (x - v0.x) / (double)(v1.x - v0.x) + .5); long z1 = (long)(v0.z + (v2.z - v0.z) * (x - v0.x) / (double)(v2.x - v0.x) + .5); if (y0 > y1) { swap(ref y0, ref y1); swap(ref z0, ref z1); } for (long y = y0; y < y1; y++) { SupportPoint newSupportPoint = new SupportPoint((int)(z0 + (z1 - z0) * (y - y0) / (double)(y1 - y0) + .5), angleFromHorizon); this.xYGridOfSupportPoints[(int)(x + y * this.gridWidth)].Add(newSupportPoint); } } for (int x = v1.x; x < v2.x; x++) { long y0 = (long)(v1.y + (v2.y - v1.y) * (x - v1.x) / (double)(v2.x - v1.x) + .5); long y1 = (long)(v0.y + (v2.y - v0.y) * (x - v0.x) / (double)(v2.x - v0.x) + .5); long z0 = (long)(v1.z + (v2.z - v1.z) * (x - v1.x) / (double)(v2.x - v1.x) + .5); long z1 = (long)(v0.z + (v2.z - v0.z) * (x - v0.x) / (double)(v2.x - v0.x) + .5); if (y0 > y1) { swap(ref y0, ref y1); swap(ref z0, ref z1); } for (int y = (int)y0; y < y1; y++) { this.xYGridOfSupportPoints[x + y * this.gridWidth].Add(new SupportPoint((int)(z0 + (z1 - z0) * (double)(y - y0) / (double)(y1 - y0) + .5), angleFromHorizon)); } } } } for (int x = 0; x < this.gridWidth; x++) { for (int y = 0; y < this.gridHeight; y++) { int gridIndex = x + y * this.gridWidth; List <SupportPoint> currentList = this.xYGridOfSupportPoints[gridIndex]; currentList.Sort(SortSupportsOnZ); if (currentList.Count > 1) { // now remove duplicates (try to make it a better bottom and top list) for (int i = currentList.Count - 1; i >= 1; i--) { if (currentList[i].z == currentList[i - 1].z) { currentList.RemoveAt(i); } } } } } this.gridOffset.X += this.gridScale / 2; this.gridOffset.Y += this.gridScale / 2; }
public void MakePolygons(OptimizedVolume optomizedMesh, ConfigConstants.REPAIR_OUTLINES outlineRepairTypes) { for (int startingSegmentIndex = 0; startingSegmentIndex < segmentList.Count; startingSegmentIndex++) { if (segmentList[startingSegmentIndex].hasBeenAddedToPolygon) { continue; } Polygon poly = new Polygon(); // We start by adding the start, as we will add ends from now on. IntPoint polygonStartPosition = segmentList[startingSegmentIndex].start; poly.Add(polygonStartPosition); int segmentIndexBeingAdded = startingSegmentIndex; bool canClose; bool lastAddWasAStart = true; while (true) { canClose = false; segmentList[segmentIndexBeingAdded].hasBeenAddedToPolygon = true; IntPoint addedSegmentEndPoint = segmentList[segmentIndexBeingAdded].end; if (!lastAddWasAStart) { // the last added point was an end so add the start as the text end point addedSegmentEndPoint = segmentList[segmentIndexBeingAdded].start; } poly.Add(addedSegmentEndPoint); int nextSegmentToCheckIndex = -1; OptimizedFace face = optomizedMesh.facesTriangle[segmentList[segmentIndexBeingAdded].faceIndex]; for (int connectedFaceIndex = 0; connectedFaceIndex < 3; connectedFaceIndex++) { int testFaceIndex = face.touchingFaces[connectedFaceIndex]; if (testFaceIndex > -1) { // If the connected face has an edge that is in the segment list if (faceTo2DSegmentIndex.ContainsKey(testFaceIndex)) { int touchingSegmentIndex = faceTo2DSegmentIndex[testFaceIndex]; IntPoint foundSegmentStart = segmentList[touchingSegmentIndex].start; if (addedSegmentEndPoint == foundSegmentStart) { // if we have looped back around to where we started if (addedSegmentEndPoint == polygonStartPosition) { canClose = true; } // If this segment has already been added if (segmentList[touchingSegmentIndex].hasBeenAddedToPolygon) { continue; } nextSegmentToCheckIndex = touchingSegmentIndex; lastAddWasAStart = true; } else // let's check if the other side of this segment can hook up (the normal is facing the wrong way) { IntPoint foundSegmentEnd = segmentList[touchingSegmentIndex].end; if (addedSegmentEndPoint == foundSegmentEnd) { // if we have looped back around to where we started if (addedSegmentEndPoint == polygonStartPosition) { canClose = true; } // If this segment has already been added if (segmentList[touchingSegmentIndex].hasBeenAddedToPolygon) { continue; } nextSegmentToCheckIndex = touchingSegmentIndex; lastAddWasAStart = false; } } } } } if (nextSegmentToCheckIndex == -1) { break; } segmentIndexBeingAdded = nextSegmentToCheckIndex; } if (canClose) { polygonList.Add(poly); } else { openPolygonList.Add(poly); } } // Link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time. while (true) { long bestScore = 10000 * 10000; int bestA = -1; int bestB = -1; bool reversed = false; for (int polygonAIndex = 0; polygonAIndex < openPolygonList.Count; polygonAIndex++) { if (openPolygonList[polygonAIndex].Count < 1) { continue; } for (int polygonBIndex = 0; polygonBIndex < openPolygonList.Count; polygonBIndex++) { if (openPolygonList[polygonBIndex].Count < 1) { continue; } IntPoint diff1 = openPolygonList[polygonAIndex][openPolygonList[polygonAIndex].Count - 1] - openPolygonList[polygonBIndex][0]; long distSquared1 = (diff1).LengthSquared(); if (distSquared1 < bestScore) { bestScore = distSquared1; bestA = polygonAIndex; bestB = polygonBIndex; reversed = false; if (bestScore == 0) { // found a perfect match stop looking break; } } if (polygonAIndex != polygonBIndex) { IntPoint diff2 = openPolygonList[polygonAIndex][openPolygonList[polygonAIndex].Count - 1] - openPolygonList[polygonBIndex][openPolygonList[polygonBIndex].Count - 1]; long distSquared2 = (diff2).LengthSquared(); if (distSquared2 < bestScore) { bestScore = distSquared2; bestA = polygonAIndex; bestB = polygonBIndex; reversed = true; if (bestScore == 0) { // found a perfect match stop looking break; } } } } if (bestScore == 0) { // found a perfect match stop looking break; } } if (bestScore >= 10000 * 10000) { break; } if (bestA == bestB) { polygonList.Add(new Polygon(openPolygonList[bestA])); openPolygonList[bestA].Clear(); } else { if (reversed) { if (openPolygonList[bestA].PolygonLength() > openPolygonList[bestB].PolygonLength()) { if (openPolygonList[bestA].PolygonLength() > openPolygonList[bestB].PolygonLength()) { openPolygonList[bestA].AddRange(openPolygonList[bestB]); openPolygonList[bestB].Clear(); } else { openPolygonList[bestB].AddRange(openPolygonList[bestA]); openPolygonList[bestA].Clear(); } } else { openPolygonList[bestB].AddRange(openPolygonList[bestA]); openPolygonList[bestB].Clear(); } } else { openPolygonList[bestA].AddRange(openPolygonList[bestB]); openPolygonList[bestB].Clear(); } } } if ((outlineRepairTypes & ConfigConstants.REPAIR_OUTLINES.EXTENSIVE_STITCHING) == ConfigConstants.REPAIR_OUTLINES.EXTENSIVE_STITCHING) { //For extensive stitching find 2 open polygons that are touching 2 closed polygons. // Then find the sortest path over this polygon that can be used to connect the open polygons, // And generate a path over this shortest bit to link up the 2 open polygons. // (If these 2 open polygons are the same polygon, then the final result is a closed polyon) while (true) { int bestA = -1; int bestB = -1; GapCloserResult bestResult = new GapCloserResult(); bestResult.len = long.MaxValue; bestResult.polygonIndex = -1; bestResult.pointIndexA = -1; bestResult.pointIndexB = -1; for (int i = 0; i < openPolygonList.Count; i++) { if (openPolygonList[i].Count < 1) { continue; } { GapCloserResult res = FindPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].Count - 1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = i; bestResult = res; } } for (int j = 0; j < openPolygonList.Count; j++) { if (openPolygonList[j].Count < 1 || i == j) { continue; } GapCloserResult res = FindPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].Count - 1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = j; bestResult = res; } } } if (bestResult.len < long.MaxValue) { if (bestA == bestB) { if (bestResult.pointIndexA == bestResult.pointIndexB) { polygonList.Add(new Polygon(openPolygonList[bestA])); openPolygonList[bestA].Clear(); } else if (bestResult.AtoB) { Polygon poly = new Polygon(); polygonList.Add(poly); for (int j = bestResult.pointIndexA; j != bestResult.pointIndexB; j = (j + 1) % polygonList[bestResult.polygonIndex].Count) { poly.Add(polygonList[bestResult.polygonIndex][j]); } poly.AddRange(openPolygonList[bestA]); openPolygonList[bestA].Clear(); } else { int n = polygonList.Count; polygonList.Add(new Polygon(openPolygonList[bestA])); for (int j = bestResult.pointIndexB; j != bestResult.pointIndexA; j = (j + 1) % polygonList[bestResult.polygonIndex].Count) { polygonList[n].Add(polygonList[bestResult.polygonIndex][j]); } openPolygonList[bestA].Clear(); } } else { if (bestResult.pointIndexA == bestResult.pointIndexB) { openPolygonList[bestB].AddRange(openPolygonList[bestA]); openPolygonList[bestA].Clear(); } else if (bestResult.AtoB) { Polygon poly = new Polygon(); for (int n = bestResult.pointIndexA; n != bestResult.pointIndexB; n = (n + 1) % polygonList[bestResult.polygonIndex].Count) { poly.Add(polygonList[bestResult.polygonIndex][n]); } for (int n = poly.Count - 1; (int)(n) >= 0; n--) { openPolygonList[bestB].Add(poly[n]); } for (int n = 0; n < openPolygonList[bestA].Count; n++) { openPolygonList[bestB].Add(openPolygonList[bestA][n]); } openPolygonList[bestA].Clear(); } else { for (int n = bestResult.pointIndexB; n != bestResult.pointIndexA; n = (n + 1) % polygonList[bestResult.polygonIndex].Count) { openPolygonList[bestB].Add(polygonList[bestResult.polygonIndex][n]); } for (int n = openPolygonList[bestA].Count - 1; n >= 0; n--) { openPolygonList[bestB].Add(openPolygonList[bestA][n]); } openPolygonList[bestA].Clear(); } } } else { break; } } } if ((outlineRepairTypes & ConfigConstants.REPAIR_OUTLINES.KEEP_OPEN) == ConfigConstants.REPAIR_OUTLINES.KEEP_OPEN) { for (int n = 0; n < openPolygonList.Count; n++) { if (openPolygonList[n].Count > 0) { polygonList.Add(new Polygon(openPolygonList[n])); } } } //Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print. int snapDistance = 1000; for (int polygonIndex = 0; polygonIndex < polygonList.Count; polygonIndex++) { int length = 0; for (int intPointIndex = 1; intPointIndex < polygonList[polygonIndex].Count; intPointIndex++) { length += (polygonList[polygonIndex][intPointIndex] - polygonList[polygonIndex][intPointIndex - 1]).vSize(); if (length > snapDistance) { break; } } if (length < snapDistance) { polygonList.RemoveAt(polygonIndex); polygonIndex--; } } //Finally optimize all the polygons. Every point removed saves time in the long run. double minimumDistanceToCreateNewPosition = 1.415; polygonList = Clipper.CleanPolygons(polygonList, minimumDistanceToCreateNewPosition); }
public OptimizedVolume(SimpleVolume volume, OptimizedModel model) { this.model = model; vertices.Capacity = volume.faceTriangles.Count * 3; facesTriangle.Capacity = volume.faceTriangles.Count; Dictionary <int, List <int> > indexMap = new Dictionary <int, List <int> >(); Stopwatch t = new Stopwatch(); t.Start(); for (int i = 0; i < volume.faceTriangles.Count; i++) { OptimizedFace f = new OptimizedFace(); if ((i % 1000 == 0) && t.Elapsed.Seconds > 2) { LogOutput.logProgress("optimized", i + 1, volume.faceTriangles.Count); } for (int j = 0; j < 3; j++) { Point3 p = volume.faceTriangles[i].v[j]; int hash = (int)(((p.x + MELD_DIST / 2) / MELD_DIST) ^ (((p.y + MELD_DIST / 2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST / 2) / MELD_DIST) << 20)); int idx = 0; bool add = true; if (indexMap.ContainsKey(hash)) { for (int n = 0; n < indexMap[hash].Count; n++) { if ((vertices[indexMap[hash][n]].position - p).testLength(MELD_DIST)) { idx = indexMap[hash][n]; add = false; break; } } } if (add) { if (!indexMap.ContainsKey(hash)) { indexMap.Add(hash, new List <int>()); } indexMap[hash].Add(vertices.Count); idx = vertices.Count; vertices.Add(new OptimizedPoint3(p)); } f.vertexIndex[j] = idx; } if (f.vertexIndex[0] != f.vertexIndex[1] && f.vertexIndex[0] != f.vertexIndex[2] && f.vertexIndex[1] != f.vertexIndex[2]) { //Check if there is a face with the same points bool duplicate = false; for (int _idx0 = 0; _idx0 < vertices[f.vertexIndex[0]].usedByFacesList.Count; _idx0++) { for (int _idx1 = 0; _idx1 < vertices[f.vertexIndex[1]].usedByFacesList.Count; _idx1++) { for (int _idx2 = 0; _idx2 < vertices[f.vertexIndex[2]].usedByFacesList.Count; _idx2++) { if (vertices[f.vertexIndex[0]].usedByFacesList[_idx0] == vertices[f.vertexIndex[1]].usedByFacesList[_idx1] && vertices[f.vertexIndex[0]].usedByFacesList[_idx0] == vertices[f.vertexIndex[2]].usedByFacesList[_idx2]) { duplicate = true; } } } } if (!duplicate) { vertices[f.vertexIndex[0]].usedByFacesList.Add(facesTriangle.Count); vertices[f.vertexIndex[1]].usedByFacesList.Add(facesTriangle.Count); vertices[f.vertexIndex[2]].usedByFacesList.Add(facesTriangle.Count); facesTriangle.Add(f); } } } //fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t)); int openFacesCount = 0; for (int i = 0; i < facesTriangle.Count; i++) { OptimizedFace f = facesTriangle[i]; f.touchingFaces[0] = getFaceIdxWithPoints(f.vertexIndex[0], f.vertexIndex[1], i); f.touchingFaces[1] = getFaceIdxWithPoints(f.vertexIndex[1], f.vertexIndex[2], i); f.touchingFaces[2] = getFaceIdxWithPoints(f.vertexIndex[2], f.vertexIndex[0], i); if (f.touchingFaces[0] == -1) { openFacesCount++; } if (f.touchingFaces[1] == -1) { openFacesCount++; } if (f.touchingFaces[2] == -1) { openFacesCount++; } } //fprintf(stdout, " Number of open faces: %i\n", openFacesCount); }
public OptimizedMesh(SimpleMesh simpleMesh, OptimizedMeshCollection containingCollection) { this.containingCollection = containingCollection; vertices.Capacity = simpleMesh.faceTriangles.Count * 3; facesTriangle.Capacity = simpleMesh.faceTriangles.Count; Dictionary<int, List<int>> indexMap = new Dictionary<int, List<int>>(); Stopwatch t = new Stopwatch(); t.Start(); for (int faceIndex = 0; faceIndex < simpleMesh.faceTriangles.Count; faceIndex++) { if (MatterSlice.Canceled) { return; } OptimizedFace optimizedFace = new OptimizedFace(); if ((faceIndex % 1000 == 0) && t.Elapsed.TotalSeconds > 2) { LogOutput.logProgress("optimized", faceIndex + 1, simpleMesh.faceTriangles.Count); } for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { Point3 p = simpleMesh.faceTriangles[faceIndex].vertices[vertexIndex]; int hash = (int)(((p.x + MELD_DIST / 2) / MELD_DIST) ^ (((p.y + MELD_DIST / 2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST / 2) / MELD_DIST) << 20)); int idx = 0; bool add = true; if (indexMap.ContainsKey(hash)) { for (int n = 0; n < indexMap[hash].Count; n++) { if ((vertices[indexMap[hash][n]].position - p).AbsLengthLEQ(MELD_DIST)) { idx = indexMap[hash][n]; add = false; break; } } } if (add) { if (!indexMap.ContainsKey(hash)) { indexMap.Add(hash, new List<int>()); } indexMap[hash].Add(vertices.Count); idx = vertices.Count; vertices.Add(new OptimizedPoint3(p)); } optimizedFace.vertexIndex[vertexIndex] = idx; } if (optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[1] && optimizedFace.vertexIndex[0] != optimizedFace.vertexIndex[2] && optimizedFace.vertexIndex[1] != optimizedFace.vertexIndex[2]) { //Check if there is a face with the same points bool duplicate = false; for (int _idx0 = 0; _idx0 < vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Count; _idx0++) { for (int _idx1 = 0; _idx1 < vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Count; _idx1++) { for (int _idx2 = 0; _idx2 < vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Count; _idx2++) { if (vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[1]].usedByFacesList[_idx1] && vertices[optimizedFace.vertexIndex[0]].usedByFacesList[_idx0] == vertices[optimizedFace.vertexIndex[2]].usedByFacesList[_idx2]) duplicate = true; } } } if (!duplicate) { vertices[optimizedFace.vertexIndex[0]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[1]].usedByFacesList.Add(facesTriangle.Count); vertices[optimizedFace.vertexIndex[2]].usedByFacesList.Add(facesTriangle.Count); facesTriangle.Add(optimizedFace); } } } //fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t)); int openFacesCount = 0; for (int faceIndex = 0; faceIndex < facesTriangle.Count; faceIndex++) { OptimizedFace optimizedFace = facesTriangle[faceIndex]; optimizedFace.touchingFaces[0] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[0], optimizedFace.vertexIndex[1], faceIndex); optimizedFace.touchingFaces[1] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[1], optimizedFace.vertexIndex[2], faceIndex); optimizedFace.touchingFaces[2] = getOtherFaceIndexContainingVertices(optimizedFace.vertexIndex[2], optimizedFace.vertexIndex[0], faceIndex); if (optimizedFace.touchingFaces[0] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[1] == -1) { openFacesCount++; } if (optimizedFace.touchingFaces[2] == -1) { openFacesCount++; } } //fprintf(stdout, " Number of open faces: %i\n", openFacesCount); }