public Slicer(OptimizedVolume ov, ConfigSettings config) { int initialLayerThickness_um = config.firstLayerThickness_um; int layerThickness_um = config.layerThickness_um; ConfigConstants.REPAIR_OUTLINES outlineRepairTypes = config.repairOutlines; modelSize = ov.parentModel.size_um; modelMin = ov.parentModel.minXYZ_um; int heightWithoutFirstLayer = modelSize.z - initialLayerThickness_um - config.bottomClipAmount_um; int countOfNormalThicknessLayers = Math.Max(0, (int)((heightWithoutFirstLayer / (double)layerThickness_um) + .5)); int layerCount = countOfNormalThicknessLayers; if (initialLayerThickness_um > 0) { // we have to add in the first layer (that is a differnt size) layerCount++; } LogOutput.Log(string.Format("Layer count: {0}\n", layerCount)); layers.Capacity = layerCount; for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { int z; if (layerIndex == 0) { z = initialLayerThickness_um / 2; } else { z = initialLayerThickness_um + layerThickness_um / 2 + layerThickness_um * (layerIndex - 1); } layers.Add(new SlicerLayer(z)); } for (int faceIndex = 0; faceIndex < ov.facesTriangle.Count; faceIndex++) { Point3 p0 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[0]].position; Point3 p1 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[1]].position; Point3 p2 = ov.vertices[ov.facesTriangle[faceIndex].vertexIndex[2]].position; int minZ = p0.z; int maxZ = p0.z; if (p1.z < minZ) { minZ = p1.z; } if (p2.z < minZ) { minZ = p2.z; } if (p1.z > maxZ) { maxZ = p1.z; } if (p2.z > maxZ) { maxZ = p2.z; } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { int z = layers[layerIndex].Z; if (z < minZ || z > maxZ) { continue; } SlicerSegment polyCrossingAtThisZ; if (p0.z < z && p1.z >= z && p2.z >= z) { // p1 p2 // -------- // p0 polyCrossingAtThisZ = GetCrossingAtZ(p0, p2, p1, z); } else if (p0.z >= z && p1.z < z && p2.z < z) { // p0 // -------- // p1 p2 polyCrossingAtThisZ = GetCrossingAtZ(p0, p1, p2, z); } else if (p1.z < z && p0.z >= z && p2.z >= z) { // p0 p2 // -------- // p1 polyCrossingAtThisZ = GetCrossingAtZ(p1, p0, p2, z); } else if (p1.z >= z && p0.z < z && p2.z < z) { // p1 // -------- // p0 p2 polyCrossingAtThisZ = GetCrossingAtZ(p1, p2, p0, z); } else if (p2.z < z && p1.z >= z && p0.z >= z) { // p1 p0 // -------- // p2 polyCrossingAtThisZ = GetCrossingAtZ(p2, p1, p0, z); } else if (p2.z >= z && p1.z < z && p0.z < z) { // p2 // -------- // p1 p0 polyCrossingAtThisZ = GetCrossingAtZ(p2, p0, p1, z); } else { //Not all cases create a segment, because a point of a face could create just a dot, and two touching faces // on the slice would create two segments continue; } polyCrossingAtThisZ.hasBeenAddedToPolygon = false; layers[layerIndex].SegmentList.Add(polyCrossingAtThisZ); } } for (int layerIndex = 0; layerIndex < layers.Count; layerIndex++) { layers[layerIndex].MakePolygons(outlineRepairTypes); } }
public void MakePolygons(ConfigConstants.REPAIR_OUTLINES outlineRepairTypes) { if (false) // you can use this output segments for debugging { using (StreamWriter stream = File.AppendText("segments.txt")) { stream.WriteLine(DumpSegmentListToString(SegmentList)); } } CreateFastIndexLookup(); 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; while (true) { canClose = false; SegmentList[segmentIndexBeingAdded].hasBeenAddedToPolygon = true; IntPoint addedSegmentEndPoint = SegmentList[segmentIndexBeingAdded].end; poly.Add(addedSegmentEndPoint); segmentIndexBeingAdded = GetTouchingSegmentIndex(addedSegmentEndPoint); if (segmentIndexBeingAdded == -1) { break; } else { IntPoint foundSegmentStart = SegmentList[segmentIndexBeingAdded].start; if (addedSegmentEndPoint == foundSegmentStart) { // if we have looped back around to where we started if (addedSegmentEndPoint == polygonStartPosition) { canClose = true; } } } } 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) // This loop connects to itself, close the polygon. { PolygonList.Add(new Polygon(openPolygonList[bestA])); openPolygonList[bestA].Clear(); // B is cleared as it is A } else { if (reversed) { 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[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 minimumPerimeter = 1000; for (int polygonIndex = 0; polygonIndex < PolygonList.Count; polygonIndex++) { long perimeterLength = 0; for (int intPointIndex = 1; intPointIndex < PolygonList[polygonIndex].Count; intPointIndex++) { perimeterLength += (PolygonList[polygonIndex][intPointIndex] - PolygonList[polygonIndex][intPointIndex - 1]).Length(); if (perimeterLength > minimumPerimeter) { break; } } if (perimeterLength < minimumPerimeter) { PolygonList.RemoveAt(polygonIndex); polygonIndex--; } } //Finally optimize all the polygons. Every point removed saves time in the long run. double minimumDistanceToCreateNewPosition = 10; PolygonList = Clipper.CleanPolygons(PolygonList, minimumDistanceToCreateNewPosition); }
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; while (true) { canClose = false; segmentList[segmentIndexBeingAdded].hasBeenAddedToPolygon = true; IntPoint addedSegmentEndPoint = segmentList[segmentIndexBeingAdded].end; 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; } } } } if (nextSegmentToCheckIndex == -1) { break; } segmentIndexBeingAdded = nextSegmentToCheckIndex; } if (canClose) { polygonList.Add(poly); } else { openPolygonList.Add(poly); } } // Clear the segmentList to save memory, it is no longer needed after this point. #if !DEBUG segmentList.Clear(); #endif // Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons // First link up polygon ends that are within 2 microns. 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; } // get the delta between the last point of A and the first point of B IntPoint deltaLastAToFirstB = openPolygonList[polygonAIndex][openPolygonList[polygonAIndex].Count - 1] - openPolygonList[polygonBIndex][0]; long distSquared = deltaLastAToFirstB.LengthSquared(); if (distSquared < 2 * 2) { // If they are the same list than we can close this polygon to itself if (polygonAIndex == polygonBIndex) { polygonList.Add(new Polygon(openPolygonList[polygonAIndex])); openPolygonList[polygonAIndex].Clear(); break; } else // B can continue onto A so add it to the end { // openPolygonList[polygonAIndex].AddRange(openPolygonList[polygonBIndex]); openPolygonList[polygonBIndex].Clear(); } } } } //Next 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 (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 >= 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. long minimumDistanceToCreateNewPosition = 10; polygonList = Clipper.CleanPolygons(polygonList, minimumDistanceToCreateNewPosition); }