public static bool loadModelSTL_ascii(SimpleModel simpleModel, string filename, FMatrix3x3 matrix) { SimpleVolume vol = new SimpleVolume(); using (StreamReader f = new StreamReader(filename)) { // check for "SOLID" FPoint3 vertex = new FPoint3(); int n = 0; Point3 v0 = new Point3(0, 0, 0); Point3 v1 = new Point3(0, 0, 0); Point3 v2 = new Point3(0, 0, 0); string line = f.ReadLine(); Regex onlySingleSpaces = new Regex("\\s+", RegexOptions.Compiled); while (line != null) { line = onlySingleSpaces.Replace(line, " "); var parts = line.Trim().Split(' '); if (parts[0].Trim() == "vertex") { vertex.x = Convert.ToDouble(parts[1]); vertex.y = Convert.ToDouble(parts[2]); vertex.z = Convert.ToDouble(parts[3]); // change the scale from mm to micrometers vertex *= 1000.0; n++; switch (n) { case 1: v0 = matrix.apply(vertex); break; case 2: v1 = matrix.apply(vertex); break; case 3: v2 = matrix.apply(vertex); vol.addFaceTriangle(v0, v1, v2); n = 0; break; } } line = f.ReadLine(); } } if (vol.faceTriangles.Count > 3) { simpleModel.volumes.Add(vol); return true; } return false; }
public static List<Segment> ConvertPathToSegments(IList<IntPoint> path, long zHeight, bool isPerimeter = true) { List<Segment> polySegments = new List<Segment>(path.Count); int endIndex = isPerimeter ? path.Count : path.Count - 1; for (int i = 0; i < endIndex; i++) { Point3 point = new Point3(path[i].X, path[i].Y, zHeight); int nextIndex = (i + 1) % path.Count; Point3 nextPoint = new Point3(path[nextIndex].X, path[nextIndex].Y, zHeight); polySegments.Add(new Segment() { Start = point, End = nextPoint, }); } return polySegments; }
public FPoint3(Point3 v0) { this.x = v0.x; this.y = v0.y; this.z = v0.z; }
public void ForceMinimumLayerTime(double minTime, int minimumPrintingSpeed) { Point3 lastPosition = gcodeExport.GetPosition(); double travelTime = 0.0; double extrudeTime = 0.0; for (int n = 0; n < paths.Count; n++) { GCodePath path = paths[n]; for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { Point3 currentPosition = path.points[pointIndex]; double thisTime = (lastPosition - currentPosition).LengthMm() / (double)(path.config.speed); if (path.config.lineWidth_um != 0) { extrudeTime += thisTime; } else { travelTime += thisTime; } lastPosition = currentPosition; } } double totalTime = extrudeTime + travelTime; if (totalTime < minTime && extrudeTime > 0.0) { double minExtrudeTime = minTime - travelTime; if (minExtrudeTime < 1) { minExtrudeTime = 1; } double factor = extrudeTime / minExtrudeTime; for (int n = 0; n < paths.Count; n++) { GCodePath path = paths[n]; if (path.config.lineWidth_um == 0) { continue; } int speed = (int)(path.config.speed * factor); if (speed < minimumPrintingSpeed) { factor = (double)(minimumPrintingSpeed) / (double)(path.config.speed); } } //Only slow down with the minimum time if that will be slower then a factor already set. First layer slowdown also sets the speed factor. if (factor * 100 < getExtrudeSpeedFactor()) { SetExtrudeSpeedFactor((int)(factor * 100)); } else { factor = getExtrudeSpeedFactor() / 100.0; } if (minTime - (extrudeTime / factor) - travelTime > 0.1) { //TODO: Use up this extra time (circle around the print?) this.extraTime = minTime - (extrudeTime / factor) - travelTime; } this.totalPrintTime = (extrudeTime / factor) + travelTime; } else { this.totalPrintTime = totalTime; } }
public Point3 Cross(Point3 p) { return new Point3( y * p.z - z * p.y, z * p.x - x * p.z, x * p.y - y * p.x); }
public SimpleFace(Point3 v0, Point3 v1, Point3 v2) { vertices[0] = v0; vertices[1] = v1; vertices[2] = v2; }
private static bool loadModelSTL_binary(SimpleModel simpleModel, string filename, FMatrix3x3 matrix) { SimpleVolume vol = new SimpleVolume(); using (FileStream stlStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { // load it as a binary stl // skip the first 80 bytes // read in the number of triangles stlStream.Position = 0; BinaryReader br = new BinaryReader(stlStream); byte[] fileContents = br.ReadBytes((int)stlStream.Length); int currentPosition = 80; uint numTriangles = System.BitConverter.ToUInt32(fileContents, currentPosition); long bytesForNormals = numTriangles * 3 * 4; long bytesForVertices = numTriangles * 3 * 4; long bytesForAttributs = numTriangles * 2; currentPosition += 4; long numBytesRequiredForVertexData = currentPosition + bytesForNormals + bytesForVertices + bytesForAttributs; if (fileContents.Length < numBytesRequiredForVertexData || numTriangles < 4) { stlStream.Close(); return false; } Point3[] vector = new Point3[3]; for (int i = 0; i < numTriangles; i++) { // skip the normal currentPosition += 3 * 4; for (int j = 0; j < 3; j++) { vector[j] = new Point3( System.BitConverter.ToSingle(fileContents, currentPosition + 0 * 4) * 1000, System.BitConverter.ToSingle(fileContents, currentPosition + 1 * 4) * 1000, System.BitConverter.ToSingle(fileContents, currentPosition + 2 * 4) * 1000); currentPosition += 3 * 4; } currentPosition += 2; // skip the attribute vol.addFaceTriangle(vector[2], vector[1], vector[0]); } } if (vol.faceTriangles.Count > 3) { simpleModel.volumes.Add(vol); return true; } return false; }
public Slicer(OptimizedMesh ov, ConfigSettings config) { int initialLayerThickness_um = config.FirstLayerThickness_um; int layerThickness_um = config.LayerThickness_um; modelSize = ov.containingCollection.size_um; modelMin = ov.containingCollection.minXYZ_um; long 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 different size) layerCount++; } if (config.outputOnlyFirstLayer) { layerCount = 1; } 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 MeshProcessingLayer(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; long minZ = p0.z; long 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; } SlicePerimeterSegment 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(); } }
public SlicePerimeterSegment GetCrossingAtZ(Point3 singlePointOnSide, Point3 otherSide1, Point3 otherSide2, int z) { SlicePerimeterSegment seg = new SlicePerimeterSegment(); seg.start.X = (long)(singlePointOnSide.x + (double)(otherSide1.x - singlePointOnSide.x) * (double)(z - singlePointOnSide.z) / (double)(otherSide1.z - singlePointOnSide.z) + .5); seg.start.Y = (long)(singlePointOnSide.y + (double)(otherSide1.y - singlePointOnSide.y) * (double)(z - singlePointOnSide.z) / (double)(otherSide1.z - singlePointOnSide.z) + .5); seg.end.X = (long)(singlePointOnSide.x + (double)(otherSide2.x - singlePointOnSide.x) * (double)(z - singlePointOnSide.z) / (double)(otherSide2.z - singlePointOnSide.z) + .5); seg.end.Y = (long)(singlePointOnSide.y + (double)(otherSide2.y - singlePointOnSide.y) * (double)(z - singlePointOnSide.z) / (double)(otherSide2.z - singlePointOnSide.z) + .5); return(seg); }
public long Dot(Point3 p) { return this.x * p.x + this.y * p.y + this.z * p.z; }
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; }
private static void swap(ref Point3 p0, ref Point3 p1) { Point3 tmp = p0; p0 = p1; p1 = tmp; }
public OptimizedPoint3(Point3 position) { this.position = position; }
public void SetPositionAndSize(SimpleMeshCollection simpleMeshCollection, long xCenter_um, long yCenter_um, long zClip_um, bool centerObjectInXy) { minXYZ_um = simpleMeshCollection.minXYZ_um(); maxXYZ_um = simpleMeshCollection.maxXYZ_um(); if (centerObjectInXy) { Point3 modelXYCenterZBottom_um = new Point3((minXYZ_um.x + maxXYZ_um.x) / 2, (minXYZ_um.y + maxXYZ_um.y) / 2, minXYZ_um.z); modelXYCenterZBottom_um -= new Point3(xCenter_um, yCenter_um, zClip_um); for (int optimizedMeshIndex = 0; optimizedMeshIndex < OptimizedMeshes.Count; optimizedMeshIndex++) { for (int n = 0; n < OptimizedMeshes[optimizedMeshIndex].vertices.Count; n++) { OptimizedMeshes[optimizedMeshIndex].vertices[n].position -= modelXYCenterZBottom_um; } } minXYZ_um -= modelXYCenterZBottom_um; maxXYZ_um -= modelXYCenterZBottom_um; } else // we still need to put in the bottom clip { // Ofset by bed center and correctly position in z Point3 modelZBottom_um = new Point3(0, 0, minXYZ_um.z - zClip_um); for (int optimizedMeshIndex = 0; optimizedMeshIndex < OptimizedMeshes.Count; optimizedMeshIndex++) { for (int vertexIndex = 0; vertexIndex < OptimizedMeshes[optimizedMeshIndex].vertices.Count; vertexIndex++) { OptimizedMeshes[optimizedMeshIndex].vertices[vertexIndex].position -= modelZBottom_um; } } minXYZ_um -= modelZBottom_um; maxXYZ_um -= modelZBottom_um; } size_um = maxXYZ_um - minXYZ_um; }
public SlicerSegment GetCrossingAtZ(Point3 singlePointOnSide, Point3 otherSide1, Point3 otherSide2, int z) { SlicerSegment seg = new SlicerSegment(); seg.start.X = (long)(singlePointOnSide.x + (double)(otherSide1.x - singlePointOnSide.x) * (double)(z - singlePointOnSide.z) / (double)(otherSide1.z - singlePointOnSide.z) + .5); seg.start.Y = (long)(singlePointOnSide.y + (double)(otherSide1.y - singlePointOnSide.y) * (double)(z - singlePointOnSide.z) / (double)(otherSide1.z - singlePointOnSide.z) + .5); seg.end.X = (long)(singlePointOnSide.x + (double)(otherSide2.x - singlePointOnSide.x) * (double)(z - singlePointOnSide.z) / (double)(otherSide2.z - singlePointOnSide.z) + .5); seg.end.Y = (long)(singlePointOnSide.y + (double)(otherSide2.y - singlePointOnSide.y) * (double)(z - singlePointOnSide.z) / (double)(otherSide2.z - singlePointOnSide.z) + .5); return seg; }
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 = (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 Vector3(Point3 v0) { this.x = v0.x; this.y = v0.y; this.z = v0.z; }
public void addFaceTriangle(Point3 v0, Point3 v1, Point3 v2) { faceTriangles.Add(new SimpleFace(v0, v1, v2)); }
public void WriteMove(Point3 movePosition_um, double speed, long lineWidth_um) { StringBuilder lineToWrite = new StringBuilder(); //Normal E handling. if (lineWidth_um != 0) { Point3 diff = movePosition_um - GetPosition(); if (isRetracted) { if (retractionZHop_mm > 0) { double zWritePosition = (double)(currentPosition_um.z - extruderOffset_um[extruderIndex].z) / 1000; lineToWrite.Append("G1 Z{0:0.###}\n".FormatWith(zWritePosition)); } if (extrusionAmount_mm > 10000.0) { //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. ResetExtrusionValue(retractionAmount_mm); } lineToWrite.Append("G1 F{0} E{1:0.#####}\n".FormatWith(retractionSpeed * 60, extrusionAmount_mm)); currentSpeed = retractionSpeed; estimateCalculator.plan(new TimeEstimateCalculator.Position( currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), currentSpeed); isRetracted = false; } extrusionAmount_mm += extrusionPerMm * lineWidth_um / 1000.0 * diff.LengthMm(); lineToWrite.Append("G1"); } else { lineToWrite.Append("G0"); } if (currentSpeed != speed) { lineToWrite.Append(" F{0}".FormatWith(speed * 60)); currentSpeed = speed; } double xWritePosition = (double)(movePosition_um.x - extruderOffset_um[extruderIndex].x) / 1000.0; double yWritePosition = (double)(movePosition_um.y - extruderOffset_um[extruderIndex].y) / 1000.0; lineToWrite.Append(" X{0:0.###} Y{1:0.###}".FormatWith(xWritePosition, yWritePosition)); if (movePosition_um.z != currentPosition_um.z) { double zWritePosition = (double)(movePosition_um.z - extruderOffset_um[extruderIndex].z) / 1000.0; if (lineWidth_um == 0 && isRetracted) { zWritePosition += retractionZHop_mm; } lineToWrite.Append(" Z{0:0.###}".FormatWith(zWritePosition)); } if (lineWidth_um != 0) { lineToWrite.Append(" E{0:0.#####}".FormatWith(extrusionAmount_mm)); } lineToWrite.Append("\n"); if (lineToWrite.Length > 0) { string lineAsString = lineToWrite.ToString(); gcodeFileStream.Write(lineAsString); } //If wipe enabled remember path, but stop at 100 moves to keep memory usage low if (wipeAfterRetraction) { retractionWipePath.Add(movePosition_um); if (retractionWipePath.Count > 100) { retractionWipePath.RemoveAt(0); } } currentPosition_um = movePosition_um; estimateCalculator.plan(new TimeEstimateCalculator.Position(currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), speed); }
public List <List <Point3> > GetPathsWithOverlapsRemoved(List <Point3> perimeter, int overlapMergeAmount_um) { // make a copy that has every point duplicated (so that we have them as segments). List <Point3> polySegments = new List <Point3>(perimeter.Count * 2); for (int i = 0; i < perimeter.Count - 1; i++) { Point3 point = perimeter[i]; Point3 nextPoint = perimeter[i + 1]; polySegments.Add(point); polySegments.Add(nextPoint); } Altered[] markedAltered = new Altered[polySegments.Count / 2]; int segmentCount = polySegments.Count / 2; // now walk every segment and check if there is another segment that is similar enough to merge them together for (int firstSegmentIndex = 0; firstSegmentIndex < segmentCount; firstSegmentIndex++) { int firstPointIndex = firstSegmentIndex * 2; for (int checkSegmentIndex = firstSegmentIndex + 1; checkSegmentIndex < segmentCount; checkSegmentIndex++) { int checkPointIndex = checkSegmentIndex * 2; // The first point of start and the last point of check (the path will be coming back on itself). long startDelta = (polySegments[firstPointIndex] - polySegments[checkPointIndex + 1]).Length(); // if the segments are similar enough if (startDelta < overlapMergeAmount_um) { // The last point of start and the first point of check (the path will be coming back on itself). long endDelta = (polySegments[firstPointIndex + 1] - polySegments[checkPointIndex]).Length(); if (endDelta < overlapMergeAmount_um) { // move the first segments points to the average of the merge positions polySegments[firstPointIndex] = (polySegments[firstPointIndex] + polySegments[checkPointIndex + 1]) / 2; // the start polySegments[firstPointIndex + 1] = (polySegments[firstPointIndex + 1] + polySegments[checkPointIndex]) / 2; // the end markedAltered[firstSegmentIndex] = Altered.merged; // mark this segment for removal markedAltered[checkSegmentIndex] = Altered.remove; // We only expect to find one match for each segment, so move on to the next segment break; } } } } // Check for perimeter edges that need to be removed that are the u turns of sections that go back on themselves. // __________ // |__________| -> |--------| the 2 vertical sections should be removed for (int segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++) { int prevSegmentIndex = (int)((uint)(segmentIndex - 1) % (uint)segmentCount); int nextSegmentIndex = (segmentIndex + 1) % segmentCount; if ((markedAltered[nextSegmentIndex] == Altered.merged && markedAltered[prevSegmentIndex] == Altered.remove) || (markedAltered[nextSegmentIndex] == Altered.remove && markedAltered[prevSegmentIndex] == Altered.merged)) { markedAltered[segmentIndex] = Altered.remove; } } // remove the marked segments for (int segmentIndex = segmentCount - 1; segmentIndex >= 0; segmentIndex--) { int pointIndex = segmentIndex * 2; if (markedAltered[segmentIndex] == Altered.remove) { polySegments.RemoveRange(pointIndex, 2); } } // go through the polySegments and create a new polygon for every connected set of segments List <List <Point3> > separatedPolygons = new List <List <Point3> >(); List <Point3> currentPolygon = new List <Point3>(); separatedPolygons.Add(currentPolygon); // put in the first point for (int segmentIndex = 0; segmentIndex < polySegments.Count; segmentIndex += 2) { // add the start point currentPolygon.Add(polySegments[segmentIndex]); // if the next segment is not connected to this one if (segmentIndex < polySegments.Count - 2 && polySegments[segmentIndex + 1] != polySegments[segmentIndex + 2]) { // add the end point currentPolygon.Add(polySegments[segmentIndex + 1]); // create a new polygon currentPolygon = new List <Point3>(); separatedPolygons.Add(currentPolygon); } } // add the end point currentPolygon.Add(polySegments[polySegments.Count - 1]); return(separatedPolygons); }
public void WriteQueuedGCode(int layerThickness) { GCodePathConfig lastConfig = null; int extruderIndex = gcodeExport.GetExtruderIndex(); for (int pathIndex = 0; pathIndex < paths.Count; pathIndex++) { GCodePath path = paths[pathIndex]; if (extruderIndex != path.extruderIndex) { extruderIndex = path.extruderIndex; gcodeExport.SwitchExtruder(extruderIndex); } else if (path.Retract) { gcodeExport.WriteRetraction(); } if (path.config != travelConfig && lastConfig != path.config) { gcodeExport.WriteComment("TYPE:{0}".FormatWith(path.config.gcodeComment)); lastConfig = path.config; } double speed = path.config.speed; if (path.config.lineWidth_um != 0) { // Only apply the extrudeSpeedFactor to extrusion moves speed = speed * extrudeSpeedFactor / 100; } else { speed = speed * travelSpeedFactor / 100; } if (path.points.Count == 1 && path.config != travelConfig && (gcodeExport.GetPositionXY() - path.points[0].XYPoint).ShorterThen(path.config.lineWidth_um * 2)) { //Check for lots of small moves and combine them into one large line Point3 nextPosition = path.points[0]; int i = pathIndex + 1; while (i < paths.Count && paths[i].points.Count == 1 && (nextPosition - paths[i].points[0]).ShorterThen(path.config.lineWidth_um * 2)) { nextPosition = paths[i].points[0]; i++; } if (paths[i - 1].config == travelConfig) { i--; } if (i > pathIndex + 2) { nextPosition = gcodeExport.GetPosition(); for (int x = pathIndex; x < i - 1; x += 2) { long oldLen = (nextPosition - paths[x].points[0]).Length(); Point3 newPoint = (paths[x].points[0] + paths[x + 1].points[0]) / 2; long newLen = (gcodeExport.GetPosition() - newPoint).Length(); if (newLen > 0) { gcodeExport.WriteMove(newPoint, speed, (int)(path.config.lineWidth_um * oldLen / newLen)); } nextPosition = paths[x + 1].points[0]; } gcodeExport.WriteMove(paths[i - 1].points[0], speed, path.config.lineWidth_um); pathIndex = i - 1; continue; } } bool spiralize = path.config.spiralize; if (spiralize) { //Check if we are the last spiralize path in the list, if not, do not spiralize. for (int m = pathIndex + 1; m < paths.Count; m++) { if (paths[m].config.spiralize) { spiralize = false; } } } if (spiralize) // if we are still in spiralize mode { //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. double totalLength = 0; long z = gcodeExport.GetPositionZ(); IntPoint currentPosition = gcodeExport.GetPositionXY(); for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { IntPoint nextPosition = path.points[pointIndex].XYPoint; totalLength += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; } double length = 0.0; currentPosition = gcodeExport.GetPositionXY(); for (int i = 0; i < path.points.Count; i++) { IntPoint nextPosition = path.points[i].XYPoint; length += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; Point3 nextExtrusion = path.points[i]; nextExtrusion.z = (int)(z + layerThickness * length / totalLength + .5); gcodeExport.WriteMove(nextExtrusion, speed, path.config.lineWidth_um); } } else { // This is test code to remove double drawn small perimeter lines. if (RemoveDoubleDrawPerimeterLines(path, speed)) { return; } else { //TrimPerimeterIfNeeded(path); for (int i = 0; i < path.points.Count; i++) { gcodeExport.WriteMove(path.points[i], speed, path.config.lineWidth_um); } } } } gcodeExport.UpdateTotalPrintTime(); }
public void WriteMove(Point3 movePosition_um, double speed, int lineWidth_um) { StringBuilder lineToWrite = new StringBuilder(); //Normal E handling. if (lineWidth_um != 0) { Point3 diff = movePosition_um - GetPosition(); if (isRetracted) { if (retractionZHop_mm > 0) { double zWritePosition = (double)(currentPosition_um.z - extruderOffset_um[extruderIndex].z) / 1000; lineToWrite.Append("G1 Z{0:0.###}\n".FormatWith(zWritePosition)); } if (extrusionAmount_mm > 10000.0) { //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. ResetExtrusionValue(retractionAmount_mm); } if (outputType == ConfigConstants.OUTPUT_TYPE.ULTIGCODE) { lineToWrite.Append("G11\n"); } else { lineToWrite.Append("G1 F{0} {1}{2:0.#####}\n".FormatWith(retractionSpeed * 60, extruderCharacter[extruderIndex], extrusionAmount_mm)); currentSpeed = retractionSpeed; estimateCalculator.plan(new TimeEstimateCalculator.Position( currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), currentSpeed); } isRetracted = false; } extrusionAmount_mm += extrusionPerMm * lineWidth_um / 1000.0 * diff.LengthMm(); lineToWrite.Append("G1"); } else { lineToWrite.Append("G0"); } if (currentSpeed != speed) { lineToWrite.Append(" F{0}".FormatWith(speed * 60)); currentSpeed = speed; } double xWritePosition = (double)(movePosition_um.x - extruderOffset_um[extruderIndex].x) / 1000.0; double yWritePosition = (double)(movePosition_um.y - extruderOffset_um[extruderIndex].y) / 1000.0; lineToWrite.Append(" X{0:0.###} Y{1:0.###}".FormatWith(xWritePosition, yWritePosition)); if (movePosition_um.z != currentPosition_um.z) { double zWritePosition = (double)(movePosition_um.z - extruderOffset_um[extruderIndex].z) / 1000.0; lineToWrite.Append(" Z{0:0.###}".FormatWith(zWritePosition)); } if (lineWidth_um != 0) { lineToWrite.Append(" {0}{1:0.#####}".FormatWith(extruderCharacter[extruderIndex], extrusionAmount_mm)); } lineToWrite.Append("\n"); if (lineToWrite.Length > 0) { string lineAsString = lineToWrite.ToString(); gcodeFileStream.Write(lineAsString); } //If wipe enabled remember path, but stop at 100 moves to keep memory usage low if (wipeAfterRetraction) { retractionWipePath.Add(movePosition_um); if (retractionWipePath.Count > 100) { retractionWipePath.RemoveAt(0); } } currentPosition_um = movePosition_um; estimateCalculator.plan(new TimeEstimateCalculator.Position(currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), speed); }
public void writeMove(IntPoint movePosition_um, int speed, int lineWidth_um) { if (outputType == ConfigConstants.OUTPUT_TYPE.BFB) { //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values, they use RPM values double fspeed = speed * 60; double rpm = (extrusionPerMm * (double)(lineWidth_um) / 1000.0) * speed * 60; //All BFB machines have 4mm per RPM extrusion. const double mm_per_rpm = 4.0; rpm /= mm_per_rpm; if (rpm > 0) { if (isRetracted) { if (currentSpeed != (int)(rpm * 10)) { //f.Write("; %f e-per-mm %d mm-width %d mm/s\n", extrusionPerMM, lineWidth, speed); f.Write("M108 S{0:0.0}\n".FormatWith(rpm * 10)); currentSpeed = (int)(rpm * 10); } f.Write("M101\n"); isRetracted = false; } // Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (Round(rpm * 100) / 100)); //Increase the extrusion amount to calculate the amount of filament used. IntPoint diff = movePosition_um - getPositionXY(); extrusionAmount_mm += extrusionPerMm * lineWidth_um / 1000.0 * diff.LengthMm(); } else { //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!isRetracted) { f.Write("M103\n"); isRetracted = true; } } f.Write("G1 X{0:0.00} Y{1:0.00} Z{2:0.00} F{3:0.0}\n".FormatWith((double)(movePosition_um.X - extruderOffset_um[extruderIndex].X) / 1000, (double)(movePosition_um.Y - extruderOffset_um[extruderIndex].Y) / 1000, (double)(zPos_um) / 1000, fspeed)); } else { //Normal E handling. if (lineWidth_um != 0) { IntPoint diff = movePosition_um - getPositionXY(); if (isRetracted) { if (retractionZHop_mm > 0) { f.Write("G1 Z{0:0.00}\n".FormatWith(currentPosition_um.z / 1000.0)); } if (outputType == ConfigConstants.OUTPUT_TYPE.ULTIGCODE) { f.Write("G11\n"); } else { f.Write("G1 F{0} {1}{2:0.00000}\n".FormatWith(retractionSpeed * 60, extruderCharacter[extruderIndex], extrusionAmount_mm)); currentSpeed = retractionSpeed; estimateCalculator.plan(new TimeEstimateCalculator.Position( currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), currentSpeed); } if (extrusionAmount_mm > 10000.0) { //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. resetExtrusionValue(); } isRetracted = false; } extrusionAmount_mm += extrusionPerMm * lineWidth_um / 1000.0 * diff.LengthMm(); f.Write("G1"); } else { f.Write("G0"); } if (currentSpeed != speed) { f.Write(" F{0}".FormatWith(speed * 60)); currentSpeed = speed; } f.Write(" X{0:0.00} Y{1:0.00}".FormatWith((movePosition_um.X - extruderOffset_um[extruderIndex].X) / 1000.0, (movePosition_um.Y - extruderOffset_um[extruderIndex].Y) / 1000.0)); if (zPos_um != currentPosition_um.z) { f.Write(" Z{0:0.00}".FormatWith((zPos_um) / 1000.0)); } if (lineWidth_um != 0) { f.Write(" {0}{1:0.00000}".FormatWith(extruderCharacter[extruderIndex], extrusionAmount_mm)); } f.Write("\n"); } currentPosition_um = new Point3(movePosition_um.X, movePosition_um.Y, zPos_um); estimateCalculator.plan(new TimeEstimateCalculator.Position(currentPosition_um.x / 1000.0, currentPosition_um.y / 1000.0, currentPosition_um.z / 1000.0, extrusionAmount_mm), speed); }