using MatterHackers.Agg; using System; using System.Collections.Generic; using System.Text; namespace MatterHackers.MatterControl.SlicerConfiguration { public static class GCodeProcessing { private static Dictionary replaceWithSettingsStrings = new Dictionary() { // Have a mapping so that MatterSlice while always use a setting that can be set. (the user cannot set first_layer_bedTemperature in MatterSlice) {"first_layer_temperature", "temperature"}, {"temperature","temperature"}, {"first_layer_bed_temperature","bed_temperature"}, {"bed_temperature","bed_temperature"}, {"bed_remove_part_temperature","bed_remove_part_temperature"}, {"extruder_wipe_temperature","extruder_wipe_temperature"}, {"z_offset","z_offset"}, {"retract_length","retract_length"}, {"filament_diameter","filament_diameter"}, {"first_layer_speed","first_layer_speed"}, {"infill_speed","infill_speed"}, {"max_fan_speed","max_fan_speed"}, {"min_fan_speed","min_fan_speed"}, {"min_print_speed","min_print_speed"}, {"perimeter_speed","perimeter_speed"}, {"retract_speed","retract_speed"}, {"support_material_speed","support_material_speed"}, {"travel_speed","travel_speed"}, {"bridge_fan_speed","bridge_fan_speed"}, {"bridge_speed","bridge_speed"}, {"raft_print_speed","raft_print_speed"}, {"external_perimeter_speed","external_perimeter_speed"}, }; public static string ReplaceMacroValues(string gcodeWithMacros) { foreach (KeyValuePair keyValue in replaceWithSettingsStrings) { // do the replacement with {} (curly brackets) { string thingToReplace = "{" + "{0}".FormatWith(keyValue.Key) + "}"; gcodeWithMacros = gcodeWithMacros.Replace(thingToReplace, ActiveSliceSettings.Instance.GetActiveValue(keyValue.Value)); } // do the replacement with [] (square brackets) Slic3r uses only square brackets { string thingToReplace = "[" + "{0}".FormatWith(keyValue.Key) + "]"; gcodeWithMacros = gcodeWithMacros.Replace(thingToReplace, ActiveSliceSettings.Instance.GetActiveValue(keyValue.Value)); } } return gcodeWithMacros; } } public class MapItem { private string mappedKey; private string originalKey; public MapItem(string mappedKey, string originalKey) { this.mappedKey = mappedKey; this.originalKey = originalKey; } protected static double ParseValueString(string valueString, double valueOnError = 0) { double value = valueOnError; if (!double.TryParse(valueString, out value)) { #if DEBUG throw new Exception("Slicing value is not a double."); #endif } return value; } public static double GetValueForKey(string originalKey, double valueOnError = 0) { return ParseValueString(ActiveSliceSettings.Instance.GetActiveValue(originalKey), valueOnError); } public string MappedKey { get { return mappedKey; } } public string OriginalKey { get { return originalKey; } } public string OriginalValue { get { return ActiveSliceSettings.Instance.GetActiveValue(originalKey); } } public virtual string MappedValue { get { return OriginalValue; } } } public class MapFirstValue : MapItem { public MapFirstValue(string mappedKey, string originalKey) : base(mappedKey, originalKey) { } public override string MappedValue { get { string mappedValue = base.MappedValue; if (mappedValue.Contains(",")) { string[] splitValues = mappedValue.Split(','); return splitValues[0]; } return mappedValue; } } } public class VisibleButNotMappedToEngine : MapItem { public override string MappedValue { get { return null; } } /// /// This key will be show in the editor, but it will not be passed to the actual slicing engine. /// Some of these values are used in other parts of MatterControl, not slicing, but are held in the slicing data. /// /// public VisibleButNotMappedToEngine(string originalKey) : base("", originalKey) { } } public class MapStartGCode : InjectGCodeCommands { private bool replaceCRs; public override string MappedValue { get { StringBuilder newStartGCode = new StringBuilder(); foreach (string line in PreStartGCode(SlicingQueue.extrudersUsed)) { newStartGCode.Append(line + "\n"); } newStartGCode.Append(GCodeProcessing.ReplaceMacroValues(base.MappedValue)); foreach (string line in PostStartGCode(SlicingQueue.extrudersUsed)) { newStartGCode.Append("\n"); newStartGCode.Append(line); } if (replaceCRs) { return newStartGCode.ToString().Replace("\n", "\\n"); } return newStartGCode.ToString(); } } public MapStartGCode(string mappedKey, string originalKey, bool replaceCRs) : base(mappedKey, originalKey) { this.replaceCRs = replaceCRs; } public List PreStartGCode(List extrudersUsed) { string startGCode = ActiveSliceSettings.Instance.GetActiveValue("start_gcode"); string[] preStartGCodeLines = startGCode.Split(new string[] { "\\n" }, StringSplitOptions.RemoveEmptyEntries); List preStartGCode = new List(); preStartGCode.Add("; automatic settings before start_gcode"); AddDefaultIfNotPresent(preStartGCode, "G21", preStartGCodeLines, "set units to millimeters"); AddDefaultIfNotPresent(preStartGCode, "M107", preStartGCodeLines, "fan off"); double bed_temperature = ActiveSliceSettings.Instance.BedTemperature; if (bed_temperature > 0) { string setBedTempString = string.Format("M190 S{0}", bed_temperature); AddDefaultIfNotPresent(preStartGCode, setBedTempString, preStartGCodeLines, "wait for bed temperature to be reached"); } int numberOfHeatedExtruders = 1; if (!ActiveSliceSettings.Instance.ExtrudersShareTemperature) { numberOfHeatedExtruders = ActiveSliceSettings.Instance.ExtruderCount; } // Start heating all the extruder that we are going to use. for (int extruderIndex0Based = 0; extruderIndex0Based < numberOfHeatedExtruders; extruderIndex0Based++) { if (extrudersUsed.Count > extruderIndex0Based && extrudersUsed[extruderIndex0Based]) { string materialTemperature = ActiveSliceSettings.Instance.GetMaterialValue("temperature", extruderIndex0Based + 1); if (materialTemperature != "0") { string setTempString = "M104 T{0} S{1}".FormatWith(extruderIndex0Based, materialTemperature); AddDefaultIfNotPresent(preStartGCode, setTempString, preStartGCodeLines, string.Format("start heating extruder {0}", extruderIndex0Based + 1)); } } } // If we need to wait for the heaters to heat up before homing then set them to M109 (heat and wait). if (ActiveSliceSettings.Instance.GetActiveValue("heat_extruder_before_homing") == "1") { for (int extruderIndex0Based = 0; extruderIndex0Based < numberOfHeatedExtruders; extruderIndex0Based++) { if (extrudersUsed.Count > extruderIndex0Based && extrudersUsed[extruderIndex0Based]) { string materialTemperature = ActiveSliceSettings.Instance.GetMaterialValue("temperature", extruderIndex0Based + 1); if (materialTemperature != "0") { string setTempString = "M109 T{0} S{1}".FormatWith(extruderIndex0Based, materialTemperature); AddDefaultIfNotPresent(preStartGCode, setTempString, preStartGCodeLines, string.Format("wait for extruder {0}", extruderIndex0Based + 1)); } } } } SwitchToFirstActiveExtruder(extrudersUsed, preStartGCodeLines, preStartGCode); preStartGCode.Add("; settings from start_gcode"); return preStartGCode; } private void SwitchToFirstActiveExtruder(List extrudersUsed, string[] preStartGCodeLines, List preStartGCode) { // make sure we are on the first active extruder for (int extruderIndex = 0; extruderIndex < extrudersUsed.Count; extruderIndex++) { if (extrudersUsed[extruderIndex]) { // set the active extruder to the first one that will be printing AddDefaultIfNotPresent(preStartGCode, "T{0}".FormatWith(extruderIndex), preStartGCodeLines, "set the active extruder to {0}".FormatWith(extruderIndex)); break; // then break so we don't set it to a different ones } } } public List PostStartGCode(List extrudersUsed) { string startGCode = ActiveSliceSettings.Instance.GetActiveValue("start_gcode"); string[] postStartGCodeLines = startGCode.Split(new string[] { "\\n" }, StringSplitOptions.RemoveEmptyEntries); List postStartGCode = new List(); postStartGCode.Add("; automatic settings after start_gcode"); int numberOfHeatedExtruders = 1; if (!ActiveSliceSettings.Instance.ExtrudersShareTemperature) { numberOfHeatedExtruders = ActiveSliceSettings.Instance.ExtruderCount; } // don't set the extrudes to heating if we alread waited for them to reach temp if (ActiveSliceSettings.Instance.GetActiveValue("heat_extruder_before_homing") != "1") { for (int extruderIndex0Based = 0; extruderIndex0Based < numberOfHeatedExtruders; extruderIndex0Based++) { if (extrudersUsed.Count > extruderIndex0Based && extrudersUsed[extruderIndex0Based]) { string materialTemperature = ActiveSliceSettings.Instance.GetMaterialValue("temperature", extruderIndex0Based + 1); if (materialTemperature != "0") { string setTempString = "M109 T{0} S{1}".FormatWith(extruderIndex0Based, materialTemperature); AddDefaultIfNotPresent(postStartGCode, setTempString, postStartGCodeLines, string.Format("wait for extruder {0} to reach temperature", extruderIndex0Based + 1)); } } } } SwitchToFirstActiveExtruder(extrudersUsed, postStartGCodeLines, postStartGCode); AddDefaultIfNotPresent(postStartGCode, "G90", postStartGCodeLines, "use absolute coordinates"); postStartGCode.Add(string.Format("{0} ; {1}", "G92 E0", "reset the expected extruder position")); AddDefaultIfNotPresent(postStartGCode, "M82", postStartGCodeLines, "use absolute distance for extrusion"); return postStartGCode; } } public class MapItemToBool : MapItem { public override string MappedValue { get { if (base.MappedValue == "1") { return "True"; } return "False"; } } public MapItemToBool(string mappedKey, string originalKey) : base(mappedKey, originalKey) { } } public class ScaledSingleNumber : MapFirstValue { internal double scale; public override string MappedValue { get { double ratio = 0; if (OriginalValue.Contains("%")) { string withoutPercent = OriginalValue.Replace("%", ""); ratio = MapItem.ParseValueString(withoutPercent) / 100.0; } else { ratio = MapItem.ParseValueString(base.MappedValue); } return (ratio * scale).ToString(); } } internal ScaledSingleNumber(string mappedKey, string originalKey, double scale = 1) : base(mappedKey, originalKey) { this.scale = scale; } } public class InjectGCodeCommands : ConvertCRs { public InjectGCodeCommands(string mappedKey, string originalKey) : base(mappedKey, originalKey) { } protected void AddDefaultIfNotPresent(List linesAdded, string commandToAdd, string[] linesToCheckIfAlreadyPresent, string comment) { string command = commandToAdd.Split(' ')[0].Trim(); bool foundCommand = false; foreach (string line in linesToCheckIfAlreadyPresent) { if (line.StartsWith(command)) { foundCommand = true; break; } } if (!foundCommand) { linesAdded.Add(string.Format("{0} ; {1}", commandToAdd, comment)); } } } public class ConvertCRs : MapItem { public override string MappedValue { get { string actualCRs = base.MappedValue.Replace("\\n", "\n"); return actualCRs; } } public ConvertCRs(string mappedKey, string originalKey) : base(mappedKey, originalKey) { } } public class AsCountOrDistance : MapItem { private string keyToUseAsDenominatorForCount; public AsCountOrDistance(string mappedKey, string originalKey, string keyToUseAsDenominatorForCount) : base(mappedKey, originalKey) { this.keyToUseAsDenominatorForCount = keyToUseAsDenominatorForCount; } public override string MappedValue { get { if (OriginalValue.Contains("mm")) { string withoutMm = OriginalValue.Replace("mm", ""); string distanceString = ActiveSliceSettings.Instance.GetActiveValue(keyToUseAsDenominatorForCount); double denominator = MapItem.ParseValueString(distanceString, 1); int layers = (int)(MapItem.ParseValueString(withoutMm) / denominator + .5); return layers.ToString(); } return base.MappedValue; } } } public class AsPercentOfReferenceOrDirect : MapItem { string originalReference; double scale; public override string MappedValue { get { double finalValue = 0; if (OriginalValue.Contains("%")) { string withoutPercent = OriginalValue.Replace("%", ""); double ratio = MapItem.ParseValueString(withoutPercent) / 100.0; string originalReferenceString = ActiveSliceSettings.Instance.GetActiveValue(originalReference); double valueToModify = MapItem.ParseValueString(originalReferenceString); finalValue = valueToModify * ratio; } else { finalValue = MapItem.ParseValueString(OriginalValue); } if (finalValue == 0) { finalValue = MapItem.ParseValueString(ActiveSliceSettings.Instance.GetActiveValue(originalReference)); } finalValue *= scale; return finalValue.ToString(); } } public AsPercentOfReferenceOrDirect(string mappedKey, string originalKey, string originalReference, double scale = 1) : base(mappedKey, originalKey) { this.scale = scale; this.originalReference = originalReference; } } }