diff --git a/PartPreviewWindow/PartPreviewContent.cs b/PartPreviewWindow/PartPreviewContent.cs index 8a1c4d345..7141131da 100644 --- a/PartPreviewWindow/PartPreviewContent.cs +++ b/PartPreviewWindow/PartPreviewContent.cs @@ -109,7 +109,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public class PrinterTabPage : GuiWidget { private View3DWidget modelViewer; - private ViewGcodeBasic gcodeViewer; + internal ViewGcodeBasic gcodeViewer; private PrintItemWrapper printItem; private ViewControls3D viewControls3D; @@ -160,7 +160,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.AddChild(topToBottom); // Must come after we have an instance of View3DWidget an its undo buffer - topToBottom.AddChild(new PrinterActionsBar(modelViewer) + topToBottom.AddChild(new PrinterActionsBar(modelViewer, this) { Padding = new BorderDouble(bottom: 2) }); @@ -241,6 +241,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow sideBar.AddPage("Terminal".Localize(), terminalControls); } + public void SwitchToLayerView() + { + gcodeViewer.Visible = true; + modelViewer.ShowSliceLayers = true; + } + public void ToggleView() { gcodeViewer.Visible = !gcodeViewer.Visible; diff --git a/PartPreviewWindow/View3D/PrinterActionsBar.cs b/PartPreviewWindow/View3D/PrinterActionsBar.cs index ac5ed4bc9..5f4e47224 100644 --- a/PartPreviewWindow/View3D/PrinterActionsBar.cs +++ b/PartPreviewWindow/View3D/PrinterActionsBar.cs @@ -36,6 +36,7 @@ using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.EeProm; using MatterHackers.MatterControl.PrinterCommunication; using MatterHackers.MatterControl.SlicerConfiguration; +using MatterHackers.MeshVisualizer; namespace MatterHackers.MatterControl.PartPreviewWindow { @@ -48,10 +49,39 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private OverflowDropdown overflowDropdown; - public PrinterActionsBar(View3DWidget modelViewer) + private SliceProgressReporter sliceProgressReporter; + + public class SliceProgressReporter : IProgress + { + private MeshViewerWidget meshViewer; + + public SliceProgressReporter(MeshViewerWidget meshViewer) + { + this.meshViewer = meshViewer; + } + + public void StartReporting() + { + meshViewer.BeginProgressReporting("Slicing Part"); + } + + public void EndReporting() + { + meshViewer.EndProgressReporting(); + } + + public void Report(string value) + { + meshViewer.partProcessingInfo.centeredInfoDescription.Text = value; + } + } + + public PrinterActionsBar(View3DWidget modelViewer, PartPreviewContent.PrinterTabPage printerTabPage) { UndoBuffer undoBuffer = modelViewer.UndoBuffer; + sliceProgressReporter = new SliceProgressReporter(modelViewer.meshViewerWidget); + this.HAnchor = HAnchor.ParentLeftRight; this.VAnchor = VAnchor.FitToChildren; @@ -65,6 +95,55 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var initialMargin = buttonFactory.Margin; + var sliceButton = buttonFactory.Generate("Slice".Localize()); + sliceButton.ToolTipText = "Slice Parts".Localize(); + sliceButton.Name = "Generate Gcode Button"; + sliceButton.Margin = new BorderDouble(8, 0); + sliceButton.Click += async (s, e) => + { + if (ActiveSliceSettings.Instance.PrinterSelected) + { + var printItem = ApplicationController.Instance.ActivePrintItem; + + if (ActiveSliceSettings.Instance.IsValid() && printItem != null) + { + sliceButton.Enabled = false; + + try + { + sliceProgressReporter.StartReporting(); + + // Save any pending changes before starting the print + await ApplicationController.Instance.ActiveView3DWidget.PersistPlateIfNeeded(); + + await SlicingQueue.SliceFileAsync(printItem, sliceProgressReporter); + + sliceProgressReporter.EndReporting(); + + printerTabPage.SwitchToLayerView(); + + // HACK: directly fire method which previously ran on SlicingDone event on PrintItemWrapper + UiThread.RunOnIdle(printerTabPage.gcodeViewer.CreateAndAddChildren); + } + catch (Exception ex) + { + Console.WriteLine("Error slicing file: " + ex.Message); + } + + sliceButton.Enabled = true; + }; + } + else + { + UiThread.RunOnIdle(() => + { + StyledMessageBox.ShowMessageBox(null, "Oops! Please select a printer in order to continue slicing.", "Select Printer", StyledMessageBox.MessageType.OK); + }); + } + }; + + this.AddChild(sliceButton); + this.AddChild(new TemperatureWidgetExtruder(ApplicationController.Instance.Theme.MenuButtonFactory) { Margin = new BorderDouble(right: 10) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index ed46ef4b1..42ad91b43 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -59,7 +59,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private GCode2DWidget gcode2DWidget; private PrintItemWrapper printItem => ApplicationController.Instance.ActivePrintItem; private bool startedSliceFromGenerateButton = false; - private Button generateGCodeButton; private FlowLayoutWidget buttonBottomPanel; private FlowLayoutWidget layerSelectionButtonsPanel; @@ -183,7 +182,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private GCodeFile loadedGCode => printer.BedPlate.LoadedGCode; - private void CreateAndAddChildren() + internal void CreateAndAddChildren() { CloseAllChildren(); @@ -202,39 +201,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow buttonBottomPanel.Padding = new BorderDouble(3, 3); buttonBottomPanel.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; - generateGCodeButton = buttonFactory.Generate("Generate".Localize()); - generateGCodeButton.Name = "Generate Gcode Button"; - generateGCodeButton.Click += (s, e) => - { - UiThread.RunOnIdle(() => - { - if (ActiveSliceSettings.Instance.PrinterSelected) - { - // Save any pending changes before starting the print - ApplicationController.Instance.ActiveView3DWidget.PersistPlateIfNeeded().ContinueWith((t) => - { - if (ActiveSliceSettings.Instance.IsValid() && printItem != null) - { - generateGCodeButton.Visible = false; - SlicingQueue.Instance.QueuePartForSlicing(printItem); - startedSliceFromGenerateButton = true; - } - }); - } - else - { - StyledMessageBox.ShowMessageBox(null, "Oops! Please select a printer in order to continue slicing.", "Select Printer", StyledMessageBox.MessageType.OK); - } - }); - }; - - buttonBottomPanel.AddChild(generateGCodeButton); - + layerSelectionButtonsPanel = new FlowLayoutWidget(FlowDirection.RightToLeft); layerSelectionButtonsPanel.HAnchor = HAnchor.ParentLeftRight; layerSelectionButtonsPanel.Padding = new BorderDouble(0); - GuiWidget holdPanelOpen = new GuiWidget(1, generateGCodeButton.Height); + GuiWidget holdPanelOpen = new GuiWidget(1, 40); layerSelectionButtonsPanel.AddChild(holdPanelOpen); if (windowMode == WindowMode.StandAlone) @@ -252,6 +224,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow HAnchor = HAnchor.ParentLeftRight, VAnchor = VAnchor.ParentBottomTop }; + string firstProcessingMessage = "Press 'Add' to select an item.".Localize(); if (printItem != null) @@ -292,10 +265,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } } - else - { - generateGCodeButton.Visible = false; - } SetProcessingMessage(firstProcessingMessage); @@ -523,8 +492,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { printItem.SlicingOutputMessage -= sliceItem_SlicingOutputMessage; printItem.SlicingDone -= sliceItem_Done; - - generateGCodeButton.Visible = false; } } @@ -589,8 +556,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // register for done slicing and slicing messages printItem.SlicingOutputMessage += sliceItem_SlicingOutputMessage; printItem.SlicingDone += sliceItem_Done; - - generateGCodeButton.Visible = true; } SetSyncToPrintVisibility(); } diff --git a/SlicerConfiguration/SlicingQueue.cs b/SlicerConfiguration/SlicingQueue.cs index 84a501a6b..e3c3618fc 100644 --- a/SlicerConfiguration/SlicingQueue.cs +++ b/SlicerConfiguration/SlicingQueue.cs @@ -33,6 +33,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Threading; +using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.PlatformAbstract; using MatterHackers.Agg.UI; @@ -293,6 +294,24 @@ namespace MatterHackers.MatterControl.SlicerConfiguration public static bool runInProcess = false; private static Process slicerProcess = null; + + private class SliceMessageReporter : IProgress + { + private PrintItemWrapper printItem; + public SliceMessageReporter(PrintItemWrapper printItem) + { + this.printItem = printItem; + } + + public void Report(string message) + { + UiThread.RunOnIdle(() => + { + printItem.OnSlicingOutputMessage(new StringEventArgs(message)); + }); + } + } + private static void CreateSlicedPartsThread() { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; @@ -302,148 +321,41 @@ namespace MatterHackers.MatterControl.SlicerConfiguration if (listOfSlicingItems.Count > 0) { PrintItemWrapper itemToSlice = listOfSlicingItems[0]; - string mergeRules = ""; - - string[] stlFileLocations = GetStlFileLocations(itemToSlice.FileLocation, ref mergeRules); - string fileToSlice = stlFileLocations[0]; - // check that the STL file is currently on disk - if (File.Exists(fileToSlice)) + /* + * + #if false + Mesh loadedMesh = StlProcessing.Load(fileToSlice); + SliceLayers layers = new SliceLayers(); + layers.GetPerimetersForAllLayers(loadedMesh, .2, .2); + layers.DumpSegmentsToGcode("test.gcode"); + #endif + * */ + + if (File.Exists(itemToSlice.FileLocation)) { itemToSlice.CurrentlySlicing = true; - string configFilePath = Path.Combine( - ApplicationDataStorage.Instance.GCodeOutputPath, - string.Format("config_{0}.ini", ActiveSliceSettings.Instance.GetLongHashCode().ToString())); - string gcodeFilePath = itemToSlice.GetGCodePathAndFileName(); - bool gcodeFileIsComplete = itemToSlice.IsGCodeFileComplete(gcodeFilePath); - if (!File.Exists(gcodeFilePath) || !gcodeFileIsComplete) + var reporter = new SliceMessageReporter(itemToSlice); + + if (OsInformation.OperatingSystem == OSType.Android + || OsInformation.OperatingSystem == OSType.Mac + || runInProcess) { - string commandArgs = ""; - EngineMappingsMatterSlice.WriteSliceSettingsFile(configFilePath); - if (mergeRules == "") - { - commandArgs = $"-v -o \"{gcodeFilePath}\" -c \"{configFilePath}\""; - } - else - { - commandArgs = $"-b {mergeRules} -v -o \"{gcodeFilePath}\" -c \"{configFilePath}\""; - } + itemCurrentlySlicing = itemToSlice; + MatterHackers.MatterSlice.LogOutput.GetLogWrites += SendProgressToItem; - foreach (string filename in stlFileLocations) - { - commandArgs += $" \"{filename}\""; - } -#if false - Mesh loadedMesh = StlProcessing.Load(fileToSlice); - SliceLayers layers = new SliceLayers(); - layers.GetPerimetersForAllLayers(loadedMesh, .2, .2); - layers.DumpSegmentsToGcode("test.gcode"); -#endif + SliceFile(itemToSlice.FileLocation, gcodeFilePath, itemToSlice.IsGCodeFileComplete(gcodeFilePath), reporter); - if (OsInformation.OperatingSystem == OSType.Android - || OsInformation.OperatingSystem == OSType.Mac - || runInProcess) - { - itemCurrentlySlicing = itemToSlice; - MatterHackers.MatterSlice.LogOutput.GetLogWrites += SendProgressToItem; - MatterSlice.MatterSlice.ProcessArgs(commandArgs); - MatterHackers.MatterSlice.LogOutput.GetLogWrites -= SendProgressToItem; - itemCurrentlySlicing = null; - } - else - { - slicerProcess = new Process() - { - StartInfo = new ProcessStartInfo() - { - Arguments = commandArgs, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - RedirectStandardError = true, - RedirectStandardOutput = true, - FileName = MatterSliceInfo.GetEnginePath(), - UseShellExecute = false - } - }; - - slicerProcess.OutputDataReceived += (sender, args) => - { - if (args.Data != null) - { - string message = args.Data.Replace("=>", "").Trim(); - if (message.Contains(".gcode")) - { - message = "Saving intermediate file"; - } - message += "..."; - - UiThread.RunOnIdle(() => - { - itemToSlice.OnSlicingOutputMessage(new StringEventArgs(message)); - }); - } - }; - - slicerProcess.Start(); - slicerProcess.BeginOutputReadLine(); - string stdError = slicerProcess.StandardError.ReadToEnd(); - - slicerProcess.WaitForExit(); - lock(slicerProcess) - { - slicerProcess = null; - } - } + MatterHackers.MatterSlice.LogOutput.GetLogWrites -= SendProgressToItem; + itemCurrentlySlicing = null; } - - try - { - if (File.Exists(gcodeFilePath) - && File.Exists(configFilePath)) - { - // make sure we have not already written the settings onto this file - bool fileHasSettings = false; - int bufferSize = 32000; - using (Stream fileStream = File.OpenRead(gcodeFilePath)) - { - // Read the tail of the file to determine if the given token exists - byte[] buffer = new byte[bufferSize]; - fileStream.Seek(Math.Max(0, fileStream.Length - bufferSize), SeekOrigin.Begin); - int numBytesRead = fileStream.Read(buffer, 0, bufferSize); - string fileEnd = System.Text.Encoding.UTF8.GetString(buffer); - if (fileEnd.Contains("GCode settings used")) - { - fileHasSettings = true; - } - } - - if (!fileHasSettings) - { - using (StreamWriter gcodeWriter = File.AppendText(gcodeFilePath)) - { - string oemName = "MatterControl"; - if (OemSettings.Instance.WindowTitleExtra != null && OemSettings.Instance.WindowTitleExtra.Trim().Length > 0) - { - oemName += $" - {OemSettings.Instance.WindowTitleExtra}"; - } - - gcodeWriter.WriteLine("; {0} Version {1} Build {2} : GCode settings used", oemName, VersionInfo.Instance.ReleaseVersion, VersionInfo.Instance.BuildVersion); - gcodeWriter.WriteLine("; Date {0} Time {1}:{2:00}", DateTime.Now.Date, DateTime.Now.Hour, DateTime.Now.Minute); - - foreach (string line in File.ReadLines(configFilePath)) - { - gcodeWriter.WriteLine("; {0}", line); - } - } - } - } - } - catch (Exception) + else { + SliceFile(itemToSlice.FileLocation, gcodeFilePath, itemToSlice.IsGCodeFileComplete(gcodeFilePath), reporter); } } @@ -453,7 +365,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration itemToSlice.DoneSlicing = true; }); - lock(listOfSlicingItems) + lock (listOfSlicingItems) { listOfSlicingItems.RemoveAt(0); } @@ -463,6 +375,154 @@ namespace MatterHackers.MatterControl.SlicerConfiguration } } + public static async Task SliceFileAsync(PrintItemWrapper printItem, IProgress progressReporter) + { + string gcodeFilePath = printItem.GetGCodePathAndFileName(); + + await Task.Run(() => SliceFile( + printItem.FileLocation, + gcodeFilePath, + printItem.IsGCodeFileComplete(gcodeFilePath), + progressReporter)); + } + + public static async Task SliceFileAsync(string sourceFile, string gcodeFilePath, bool gcodeFileIsComplete, IProgress progressReporter) + { + await Task.Run(() => SliceFile(sourceFile, gcodeFilePath, gcodeFileIsComplete, progressReporter)); + } + + private static void SliceFile(string sourceFile, string gcodeFilePath, bool gcodeFileIsComplete, IProgress progressReporter) + { + string mergeRules = ""; + + string[] stlFileLocations = GetStlFileLocations(sourceFile, ref mergeRules); + string fileToSlice = stlFileLocations[0]; + + // check that the STL file is currently on disk + if (File.Exists(fileToSlice)) + { + string configFilePath = Path.Combine( + ApplicationDataStorage.Instance.GCodeOutputPath, + string.Format("config_{0}.ini", ActiveSliceSettings.Instance.GetLongHashCode().ToString())); + + if (!File.Exists(gcodeFilePath) || !gcodeFileIsComplete) + { + string commandArgs; + + EngineMappingsMatterSlice.WriteSliceSettingsFile(configFilePath); + + if (mergeRules == "") + { + commandArgs = $"-v -o \"{gcodeFilePath}\" -c \"{configFilePath}\""; + } + else + { + commandArgs = $"-b {mergeRules} -v -o \"{gcodeFilePath}\" -c \"{configFilePath}\""; + } + + foreach (string filename in stlFileLocations) + { + commandArgs += $" \"{filename}\""; + } + + if (OsInformation.OperatingSystem == OSType.Android + || OsInformation.OperatingSystem == OSType.Mac + || runInProcess) + { + MatterSlice.MatterSlice.ProcessArgs(commandArgs); + } + else + { + slicerProcess = new Process() + { + StartInfo = new ProcessStartInfo() + { + Arguments = commandArgs, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + RedirectStandardError = true, + RedirectStandardOutput = true, + FileName = MatterSliceInfo.GetEnginePath(), + UseShellExecute = false + } + }; + + slicerProcess.OutputDataReceived += (sender, args) => + { + if (args.Data != null) + { + string message = args.Data.Replace("=>", "").Trim(); + if (message.Contains(".gcode")) + { + message = "Saving intermediate file"; + } + message += "..."; + + progressReporter.Report(message); + } + }; + + slicerProcess.Start(); + slicerProcess.BeginOutputReadLine(); + + string stdError = slicerProcess.StandardError.ReadToEnd(); + + slicerProcess.WaitForExit(); + lock (slicerProcess) + { + slicerProcess = null; + } + } + } + + try + { + if (File.Exists(gcodeFilePath) + && File.Exists(configFilePath)) + { + // make sure we have not already written the settings onto this file + bool fileHasSettings = false; + int bufferSize = 32000; + using (Stream fileStream = File.OpenRead(gcodeFilePath)) + { + // Read the tail of the file to determine if the given token exists + byte[] buffer = new byte[bufferSize]; + fileStream.Seek(Math.Max(0, fileStream.Length - bufferSize), SeekOrigin.Begin); + int numBytesRead = fileStream.Read(buffer, 0, bufferSize); + string fileEnd = System.Text.Encoding.UTF8.GetString(buffer); + if (fileEnd.Contains("GCode settings used")) + { + fileHasSettings = true; + } + } + + if (!fileHasSettings) + { + using (StreamWriter gcodeWriter = File.AppendText(gcodeFilePath)) + { + string oemName = "MatterControl"; + if (OemSettings.Instance.WindowTitleExtra != null && OemSettings.Instance.WindowTitleExtra.Trim().Length > 0) + { + oemName += $" - {OemSettings.Instance.WindowTitleExtra}"; + } + + gcodeWriter.WriteLine("; {0} Version {1} Build {2} : GCode settings used", oemName, VersionInfo.Instance.ReleaseVersion, VersionInfo.Instance.BuildVersion); + gcodeWriter.WriteLine("; Date {0} Time {1}:{2:00}", DateTime.Now.Date, DateTime.Now.Hour, DateTime.Now.Minute); + + foreach (string line in File.ReadLines(configFilePath)) + { + gcodeWriter.WriteLine("; {0}", line); + } + } + } + } + } + catch (Exception) + { + } + } + } + private static PrintItemWrapper itemCurrentlySlicing; private static void SendProgressToItem(object sender, EventArgs args)