public void AllMovesRequiringRetractionDoRetraction() { string baseFileName = "ab retraction test"; string stlToLoad = TestUtlities.GetStlPath(baseFileName + ".stl"); // check that default is support printed with extruder 0 { string gcodeToCreate = TestUtlities.GetTempGCodePath(baseFileName + "_retract_" + ".gcode"); ConfigSettings config = new ConfigSettings(); config.RetractionZHop = 5; config.MinimumTravelToCauseRetraction = 2; config.MinimumExtrusionBeforeRetraction = 0; config.FirstLayerExtrusionWidth = .5; fffProcessor processor = new fffProcessor(config); processor.SetTargetFile(gcodeToCreate); processor.LoadStlFile(stlToLoad); // slice and save it processor.DoProcessing(); processor.finalize(); string[] gcodeContents = TestUtlities.LoadGCodeFile(gcodeToCreate); int layerCount = TestUtlities.CountLayers(gcodeContents); bool firstPosition = true; for(int i=0; i<layerCount; i++) { string[] layerGCode = TestUtlities.GetGCodeForLayer(gcodeContents, i); MovementInfo lastMovement = new MovementInfo(); foreach (MovementInfo movement in TestUtlities.Movements(layerGCode)) { if (!firstPosition) { bool isTravel = lastMovement.extrusion == movement.extrusion; if (isTravel) { Vector3 lastPosition = lastMovement.position; lastPosition.z = 0; Vector3 currenPosition = movement.position; currenPosition.z = 0; double xyLength = (lastPosition - currenPosition).Length; if (xyLength > config.MinimumTravelToCauseRetraction) { Assert.IsTrue(movement.position.z > lastMovement.position.z); } } } lastMovement = movement; firstPosition = false; } } Assert.IsFalse(TestUtlities.UsesExtruder(gcodeContents, 1)); Assert.IsFalse(TestUtlities.UsesExtruder(gcodeContents, 2)); } }
public static IEnumerable<MovementInfo> Movements(string[] gcodeContents) { MovementInfo currentPosition = new MovementInfo(); foreach (string line in gcodeContents) { if (line.StartsWith("G1 ")) { GetFirstNumberAfter("X", line, ref currentPosition.position.x); GetFirstNumberAfter("Y", line, ref currentPosition.position.y); GetFirstNumberAfter("Z", line, ref currentPosition.position.z); GetFirstNumberAfter("E", line, ref currentPosition.extrusion); GetFirstNumberAfter("F", line, ref currentPosition.feedRate); yield return currentPosition; } } }
private static void CheckCylinder(string stlFile, string gcodeFile) { string cylinderStlFile = TestUtlities.GetStlPath(stlFile); string cylinderGCodeFileName = TestUtlities.GetTempGCodePath(gcodeFile); ConfigSettings config = new ConfigSettings(); config.firstLayerThickness = .2; config.centerObjectInXy = false; config.layerThickness = .2; config.numberOfBottomLayers = 0; config.continuousSpiralOuterPerimeter = true; fffProcessor processor = new fffProcessor(config); processor.SetTargetFile(cylinderGCodeFileName); processor.LoadStlFile(cylinderStlFile); // slice and save it processor.DoProcessing(); processor.finalize(); string[] cylinderGCodeContent = TestUtlities.LoadGCodeFile(cylinderGCodeFileName); // test .1 layer height int layerCount = TestUtlities.CountLayers(cylinderGCodeContent); Assert.IsTrue(layerCount == 100); for (int i = 2; i < layerCount - 3; i++) { string[] layerInfo = TestUtlities.GetGCodeForLayer(cylinderGCodeContent, i); // check that all layers move up continuously MovementInfo lastMovement = new MovementInfo(); foreach (MovementInfo movement in TestUtlities.Movements(layerInfo)) { Assert.IsTrue(movement.position.z > lastMovement.position.z); lastMovement = movement; } bool first = true; lastMovement = new MovementInfo(); // check that all moves are on the outside of the cylinder (not crossing to a new point) foreach (MovementInfo movement in TestUtlities.Movements(layerInfo)) { if (!first) { Assert.IsTrue((movement.position - lastMovement.position).Length < 2); Vector3 xyOnly = new Vector3(movement.position.x, movement.position.y, 0); Assert.AreEqual(9.8, xyOnly.Length, .3); } lastMovement = movement; first = false; } } }
public static Polygons GetExtrusionPolygons(string[] gcode, long movementToIgnore = 0) { MovementInfo movementInfo = new MovementInfo(); return(GetExtrusionPolygons(gcode, ref movementInfo, movementToIgnore)); }
public static Polygons GetExtrusionPolygons(string[] gcode, ref MovementInfo movementInfo, long movementToIgnore = 0) { Polygons foundPolygons = new Polygons(); bool extruding = false; // check that all moves are on the outside of the cylinder (not crossing to a new point) int movementCount = 0; double movementAmount = double.MaxValue / 2; // always add a new extrusion the first time MovementInfo lastMovement = movementInfo; foreach (MovementInfo currentMovement in TestUtilities.Movements(gcode, lastMovement)) { bool isExtrude = currentMovement.extrusion != lastMovement.extrusion; if (extruding) { if (isExtrude) { // add to the extrusion foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(currentMovement.position.x * 1000), (long)(currentMovement.position.y * 1000), (long)(currentMovement.position.z * 1000))); } else { extruding = false; movementAmount = 0; } } else // not extruding { if (isExtrude) { if (movementAmount >= movementToIgnore) { // starting a new extrusion foundPolygons.Add(new Polygon()); } foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(currentMovement.position.x * 1000), (long)(currentMovement.position.y * 1000), (long)(currentMovement.position.z * 1000))); extruding = true; } else // do nothing waiting for extrude { movementAmount += (currentMovement.position - lastMovement.position).Length; } } lastMovement = currentMovement; movementCount++; } for (int i = foundPolygons.Count - 1; i >= 0; i--) { if (foundPolygons[i].Count == 1) { foundPolygons.RemoveAt(i); } } movementInfo = lastMovement; return(foundPolygons); }
private static void CheckSpiralCone(string stlFile, string gcodeFile, bool enableThinWalls = false) { string cylinderStlFile = TestUtilities.GetStlPath(stlFile); string cylinderGCodeFileName = TestUtilities.GetTempGCodePath(gcodeFile); var config = new ConfigSettings { FirstLayerThickness = .2, LayerThickness = .2, NumberOfBottomLayers = 0, ContinuousSpiralOuterPerimeter = true }; if (enableThinWalls) { config.ExpandThinWalls = true; config.FillThinGaps = true; } var processor = new FffProcessor(config); processor.SetTargetFile(cylinderGCodeFileName); processor.LoadStlFile(cylinderStlFile); // slice and save it processor.DoProcessing(); processor.Finalize(); string[] cylinderGCodeContent = TestUtilities.LoadGCodeFile(cylinderGCodeFileName); // test .1 layer height int layerCount = TestUtilities.CountLayers(cylinderGCodeContent); Assert.AreEqual(50, layerCount, "SpiralCone should have 50 layers"); for (int i = 2; i < layerCount - 3; i++) { string[] layerInfo = TestUtilities.GetGCodeForLayer(cylinderGCodeContent, i); // check that all layers move up continuously MovementInfo lastMovement = new MovementInfo(); foreach (MovementInfo movement in TestUtilities.Movements(layerInfo)) { #if __ANDROID__ Assert.IsTrue(movement.position.z > lastMovement.position.z); #else Assert.Greater(movement.position.z, lastMovement.position.z, "Z position should increment per layer"); #endif lastMovement = movement; } double radiusForLayer = 5.0 + (20.0 - 5.0) / layerCount * i; bool first = true; lastMovement = new MovementInfo(); // check that all moves are on the outside of the cylinder (not crossing to a new point) foreach (MovementInfo movement in TestUtilities.Movements(layerInfo)) { if (!first) { Assert.IsTrue((movement.position - lastMovement.position).Length < 2); Vector3 xyOnly = new Vector3(movement.position.x, movement.position.y, 0); Assert.AreEqual(radiusForLayer, xyOnly.Length, .3); } lastMovement = movement; first = false; } } }
private static void CheckSpiralCylinder(string stlFile, string gcodeFile, int expectedLayers, bool enableThinWalls = false) { string cylinderStlFile = TestUtilities.GetStlPath(stlFile); string cylinderGCodeFileName = TestUtilities.GetTempGCodePath(gcodeFile); ConfigSettings config = new ConfigSettings(); config.FirstLayerThickness = .2; config.LayerThickness = .2; if (enableThinWalls) { config.ExpandThinWalls = true; config.FillThinGaps = true; } config.NumberOfBottomLayers = 0; config.ContinuousSpiralOuterPerimeter = true; FffProcessor processor = new FffProcessor(config); processor.SetTargetFile(cylinderGCodeFileName); processor.LoadStlFile(cylinderStlFile); // slice and save it processor.DoProcessing(); processor.Finalize(); string[] cylinderGCodeContent = TestUtilities.LoadGCodeFile(cylinderGCodeFileName); // test .1 layer height int layerCount = TestUtilities.CountLayers(cylinderGCodeContent); Assert.IsTrue(layerCount == expectedLayers); for (int i = 2; i < layerCount - 3; i++) { string[] layerInfo = TestUtilities.GetGCodeForLayer(cylinderGCodeContent, i); // check that all layers move up continuously MovementInfo lastMovement = new MovementInfo(); foreach (MovementInfo movement in TestUtilities.Movements(layerInfo)) { Assert.IsTrue(movement.position.z > lastMovement.position.z); lastMovement = movement; } bool first = true; lastMovement = new MovementInfo(); // check that all moves are on the outside of the cylinder (not crossing to a new point) foreach (MovementInfo movement in TestUtilities.Movements(layerInfo)) { if (!first) { Assert.IsTrue((movement.position - lastMovement.position).Length < 2); Vector3 xyOnly = new Vector3(movement.position.x, movement.position.y, 0); Assert.AreEqual(9.8, xyOnly.Length, .3); } lastMovement = movement; first = false; } } }
public void DualMaterialPrintMovesCorrectly(bool createWipeTower) { string leftPart = "Box Left"; string rightPart = "Box Right"; string leftStlFile = TestUtilities.GetStlPath(leftPart); string rightStlFile = TestUtilities.GetStlPath(rightPart); string outputGCodeFileName = TestUtilities.GetTempGCodePath("DualPartMoves"); ConfigSettings config = new ConfigSettings(); config.ExtruderCount = 2; config.FirstLayerThickness = .2; config.LayerThickness = .2; config.NumberOfBottomLayers = 0; if (createWipeTower) { config.WipeTowerSize = 10; } else { config.WipeTowerSize = 0; } var processor = new FffProcessor(config); processor.SetTargetFile(outputGCodeFileName); processor.LoadStlFile(leftStlFile); processor.LoadStlFile(rightStlFile); // slice and save it processor.DoProcessing(); processor.Finalize(); string[] gCodeContent = TestUtilities.LoadGCodeFile(outputGCodeFileName); // test .1 layer height int layerCount = TestUtilities.CountLayers(gCodeContent); Assert.IsTrue(layerCount == 50); bool hadMoveLessThan85 = false; MovementInfo lastMovement = new MovementInfo(); for (int i = 0; i < layerCount - 3; i++) { string[] layerInfo = TestUtilities.GetGCodeForLayer(gCodeContent, i); // check that all layers move up continuously foreach (MovementInfo movement in TestUtilities.Movements(layerInfo, lastMovement, onlyG1s: true)) { if (i > 2) { if (createWipeTower) { Assert.IsTrue(movement.position.x > 75 && movement.position.y > 10, "Moves don't go to 0"); if (movement.position.x < 85) { hadMoveLessThan85 = true; } } else { Assert.IsTrue(movement.position.x > 85 && movement.position.y > 10, "Moves don't go to 0"); } } lastMovement = movement; } } if (createWipeTower) { Assert.IsTrue(hadMoveLessThan85, "found a wipe tower"); } }
public void DoHas2WallRingsAllTheWayUp(string fileName, int expectedLayerCount, bool checkRadius = false) { string stlFile = TestUtilities.GetStlPath(fileName); string gCodeFile = TestUtilities.GetTempGCodePath(fileName + ".gcode"); ConfigSettings config = new ConfigSettings(); config.InfillPercent = 0; config.NumberOfPerimeters = 1; config.FirstLayerExtrusionWidth = .2; config.LayerThickness = .2; config.NumberOfBottomLayers = 0; config.NumberOfTopLayers = 0; FffProcessor processor = new FffProcessor(config); processor.SetTargetFile(gCodeFile); processor.LoadStlFile(stlFile); // slice and save it processor.DoProcessing(); processor.Finalize(); string[] gcodeLines = TestUtilities.LoadGCodeFile(gCodeFile); int layerCount = TestUtilities.CountLayers(gcodeLines); Assert.IsTrue(layerCount == expectedLayerCount); MovementInfo movement = new MovementInfo(); for (int i = 0; i < layerCount - 5; i++) { string[] layerInfo = TestUtilities.GetGCodeForLayer(gcodeLines, i); if (i > 0) { Polygons layerPolygons = TestUtilities.GetExtrusionPolygons(layerInfo, ref movement); Assert.IsTrue(layerPolygons.Count == 2); if (checkRadius) { Assert.IsTrue(layerPolygons[0].Count > 10); Assert.IsTrue(layerPolygons[1].Count > 10); if (false) { foreach (var polygon in layerPolygons) { double radiusForPolygon = polygon[0].LengthMm(); foreach (var point in polygon) { Assert.AreEqual(radiusForPolygon, point.LengthMm(), 15); } } } } } else { TestUtilities.GetExtrusionPolygons(layerInfo, ref movement); } } }
public void AllMovesRequiringRetractionDoRetraction(string baseFileName, string settingsIniFile = "") { string stlToLoad = TestUtilities.GetStlPath(baseFileName + ".stl"); // check that default is support printed with extruder 0 string gcodeToCreate = TestUtilities.GetTempGCodePath(baseFileName + "_retract_.gcode"); ConfigSettings config = new ConfigSettings(); if (settingsIniFile == "") { config.MinimumTravelToCauseRetraction = 2; config.MinimumExtrusionBeforeRetraction = 0; config.MergeOverlappingLines = false; config.FirstLayerExtrusionWidth = .5; } else { config.ReadSettings(settingsIniFile); } // this is what we detect config.RetractionZHop = 5; FffProcessor processor = new FffProcessor(config); processor.SetTargetFile(gcodeToCreate); processor.LoadStlFile(stlToLoad); // slice and save it processor.DoProcessing(); processor.Finalize(); string[] gcodeContents = TestUtilities.LoadGCodeFile(gcodeToCreate); int layerCount = TestUtilities.CountLayers(gcodeContents); bool firstPosition = true; MovementInfo lastMovement = new MovementInfo(); MovementInfo lastExtrusion = new MovementInfo(); bool lastMoveIsExtrusion = true; for (int layerIndex = 0; layerIndex < layerCount; layerIndex++) { string[] layerGCode = TestUtilities.GetGCodeForLayer(gcodeContents, layerIndex); int movementIndex = 0; foreach (MovementInfo movement in TestUtilities.Movements(layerGCode, lastMovement)) { if (!firstPosition) { bool isTravel = lastMovement.extrusion == movement.extrusion; if (isTravel) { Vector3 lastPosition = lastMovement.position; lastPosition.z = 0; Vector3 currenPosition = movement.position; currenPosition.z = 0; double xyLength = (lastPosition - currenPosition).Length; if (xyLength > config.MinimumTravelToCauseRetraction && lastMoveIsExtrusion) { Assert.GreaterOrEqual(movement.position.z, lastExtrusion.position.z); } lastMoveIsExtrusion = false; } else { lastMoveIsExtrusion = true; lastExtrusion = movement; } lastMoveIsExtrusion = !isTravel; } lastMovement = movement; firstPosition = false; movementIndex++; } } // make sure we don't switch extruders Assert.IsFalse(TestUtilities.UsesExtruder(gcodeContents, 1)); Assert.IsFalse(TestUtilities.UsesExtruder(gcodeContents, 2)); }
public static List <(Polygon polygon, PolygonTypes type)> GetLayerPolygons(IEnumerable <MovementInfo> layerMovements, ref MovementInfo lastMovement) { var layerPolygons = new List <(Polygon polygon, PolygonTypes type)>(); Polygon currentPolygon = null; var lastPolygonType = PolygonTypes.Unknown; foreach (var movement in layerMovements) { PolygonTypes segmentType = PolygonTypes.Unknown; // if the next movement is changed if (!movement.Equals(lastMovement)) { // figure out what type of movement it is if (movement.extrusion != lastMovement.extrusion) { // a retraction or an extrusion if (lastMovement.position.x == movement.position.x && lastMovement.position.y == movement.position.y) { // retraction segmentType = PolygonTypes.Retraction; } else { // extrusion segmentType = PolygonTypes.Extrusion; } } else { // a travel segmentType = PolygonTypes.Travel; } // if we have a change in movement type add a polygon if (segmentType != lastPolygonType) { currentPolygon = new Polygon(); layerPolygons.Add((currentPolygon, segmentType)); lastPolygonType = segmentType; currentPolygon.Add(new IntPoint(lastMovement.position.x * 1000, lastMovement.position.y * 1000, lastMovement.position.z * 1000) { Speed = (long)movement.feedRate }); } // and add to the current polygon currentPolygon.Add(new IntPoint(movement.position.x * 1000, movement.position.y * 1000, movement.position.z * 1000) { Speed = (long)movement.feedRate }); } lastMovement = movement; } return(layerPolygons); }
public static Polygons GetExtrusionPolygonsForLayer(this string[] layerGCode, ref MovementInfo movementInfo, bool validateOverlaps = true) { var foundPolygons = new Polygons(); bool extruding = false; int movementCount = 0; double movementAmount = double.MaxValue / 2; // always add a new extrusion the first time MovementInfo lastMovement = movementInfo; MovementInfo lastLastMovement = movementInfo; foreach (MovementInfo currentMovement in TestUtilities.GetLayerMovements(layerGCode, lastMovement)) { bool isRetraction = currentMovement.extrusion != lastMovement.extrusion && (currentMovement.position == lastLastMovement.position); bool isZLift = currentMovement.position.x == lastLastMovement.position.x && currentMovement.position.y == lastLastMovement.position.y && currentMovement.position.z != lastLastMovement.position.z; bool isExtrude = !isRetraction && !isZLift && currentMovement.extrusion != lastMovement.extrusion; if (extruding) { if (isExtrude) { // add to the extrusion foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(currentMovement.position.x * 1000), (long)(currentMovement.position.y * 1000), (long)(currentMovement.position.z * 1000))); } else { // we are switching so add in the point to the last extrude extruding = false; movementAmount = 0; if (foundPolygons[foundPolygons.Count - 1].Count == 1) { foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(lastLastMovement.position.x * 1000), (long)(lastLastMovement.position.y * 1000), (long)(lastLastMovement.position.z * 1000))); } } } else // not extruding { if (isExtrude) { if (movementAmount >= 0) { // starting a new extrusion foundPolygons.Add(new Polygon()); } foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(currentMovement.position.x * 1000), (long)(currentMovement.position.y * 1000), (long)(currentMovement.position.z * 1000))); extruding = true; } else // do nothing waiting for extrude { movementAmount += (currentMovement.position - lastMovement.position).Length; } } lastLastMovement = lastMovement; lastMovement = currentMovement; movementCount++; } for (int i = foundPolygons.Count - 1; i >= 0; i--) { if (!extruding && foundPolygons[i].Count == 1) { foundPolygons.RemoveAt(i); } else if (foundPolygons[foundPolygons.Count - 1].Count == 1) { foundPolygons[foundPolygons.Count - 1].Add(new IntPoint( (long)(lastLastMovement.position.x * 1000), (long)(lastLastMovement.position.y * 1000), (long)(lastLastMovement.position.z * 1000))); break; } } movementInfo = lastMovement; // validate that the polygons do not double extrude if (validateOverlaps) { Assert.IsFalse(HasOverlapingSegments(foundPolygons)); } return(foundPolygons); }