diff --git a/CustomWidgets/ExportPrintItemPage.cs b/CustomWidgets/ExportPrintItemPage.cs index 7d07ec97d..fe29d0533 100644 --- a/CustomWidgets/ExportPrintItemPage.cs +++ b/CustomWidgets/ExportPrintItemPage.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; @@ -22,7 +23,6 @@ namespace MatterHackers.MatterControl public class ExportPrintItemPage : WizardPage { private CheckBox showInFolderAfterSave; - private CheckBox applyLeveling; private string gcodePathAndFilenameToSave; private bool partIsGCode = false; private string documentsPath; @@ -31,14 +31,12 @@ namespace MatterHackers.MatterControl private Dictionary exportPluginButtons; - private ILibraryContentStream libraryContent; + private IEnumerable libraryItems; - public ExportPrintItemPage(ILibraryContentStream libraryContent) + public ExportPrintItemPage(IEnumerable libraryItems) : base(unlocalizedTextForTitle: "File export options:") { - partIsGCode = Path.GetExtension(libraryContent.FileName).ToUpper() == ".GCODE"; - - this.libraryContent = libraryContent; + this.libraryItems = libraryItems; this.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; this.Name = "Export Item Window"; @@ -53,52 +51,38 @@ namespace MatterHackers.MatterControl var commonMargin = new BorderDouble(4, 2); // GCode export - var exportGCode = new RadioButton("Export as".Localize() + " G-Code", textColor: ActiveTheme.Instance.PrimaryTextColor) - { - Name = "Export as GCode Button", - Margin = commonMargin, - HAnchor = HAnchor.Left, - Cursor = Cursors.Hand - }; - bool showExportGCodeButton = ActiveSliceSettings.Instance.PrinterSelected || partIsGCode; if (showExportGCodeButton) { - contentRow.AddChild(exportGCode); - exportPluginButtons = new Dictionary(); foreach (IExportPlugin plugin in PluginFinder.CreateInstancesOf()) { - if (plugin.EnabledForCurrentPart(libraryContent)) + // Create export button for each plugin + var pluginButton = new RadioButton(plugin.ButtonText.Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor) { - // Create export button for each plugin - var pluginButton = new RadioButton(plugin.ButtonText.Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor) - { - HAnchor = HAnchor.Left, - Margin = commonMargin, - Cursor = Cursors.Hand - }; - contentRow.AddChild(pluginButton); + HAnchor = HAnchor.Left, + Margin = commonMargin, + Cursor = Cursors.Hand + }; + contentRow.AddChild(pluginButton); - exportPluginButtons.Add(pluginButton, plugin); + var optionPanel = plugin.GetOptionsPanel(); + if (optionPanel != null) + { + optionPanel.HAnchor = HAnchor.Stretch; + optionPanel.VAnchor = VAnchor.Fit; + contentRow.AddChild(optionPanel); } + + exportPluginButtons.Add(pluginButton, plugin); } } - contentRow.AddChild(new VerticalSpacer()); + //if (plugin.EnabledForCurrentPart(libraryContent)) + - // If print leveling is enabled then add in a check box 'Apply Leveling During Export' and default checked. - if (showExportGCodeButton && ActiveSliceSettings.Instance.GetValue(SettingsKey.print_leveling_enabled)) - { - applyLeveling = new CheckBox("Apply leveling to G-Code during export".Localize(), ActiveTheme.Instance.PrimaryTextColor, 10) - { - Checked = true, - HAnchor = HAnchor.Left, - Cursor = Cursors.Hand - }; - contentRow.AddChild(applyLeveling); - } + contentRow.AddChild(new VerticalSpacer()); // TODO: make this work on the mac and then delete this if if (OsInformation.OperatingSystem == OSType.Windows @@ -130,33 +114,25 @@ namespace MatterHackers.MatterControl IExportPlugin activePlugin = null; - if (exportGCode.Checked) + // Loop over all plugin buttons, break on the first checked item found + foreach(var button in this.exportPluginButtons.Keys) { - fileTypeFilter = "Export GCode|*.gcode"; - targetExtension = ".gcode"; + if (button.Checked) + { + activePlugin = exportPluginButtons[button]; + break; + } } - else + + // Early exit if no plugin radio button is selected + if (activePlugin == null) { - // Loop over all plugin buttons, break on the first checked item found - foreach(var button in this.exportPluginButtons.Keys) - { - if (button.Checked) - { - activePlugin = exportPluginButtons[button]; - break; - } - } - - // Early exit if no plugin radio button is selected - if (activePlugin == null) - { - return; - } - - fileTypeFilter = activePlugin.ExtensionFilter; - targetExtension = activePlugin.FileExtension; + return; } + fileTypeFilter = activePlugin.ExtensionFilter; + targetExtension = activePlugin.FileExtension; + this.Parent.CloseOnIdle(); UiThread.RunOnIdle(() => { @@ -166,7 +142,7 @@ namespace MatterHackers.MatterControl { Title = title, ActionButtonLabel = "Export".Localize(), - FileName = Path.GetFileNameWithoutExtension(libraryContent.Name) + FileName = Path.GetFileNameWithoutExtension(libraryItems.FirstOrDefault()?.Name ?? DateTime.Now.ToString("yyyyMMdd-HHmmss")) }, (saveParams) => { @@ -184,15 +160,9 @@ namespace MatterHackers.MatterControl bool succeeded = false; - if (exportGCode.Checked) + if (activePlugin != null) { - succeeded = await SaveGCode(savePath); - } - else if (activePlugin != null) - { - succeeded = await activePlugin.Generate(libraryContent, savePath); - //await SaveAmf(libraryContent, savePath) - //succeeded = await ExportToPlugin(activePlugin, savePath); + succeeded = await activePlugin.Generate(libraryItems, savePath); } if (succeeded) @@ -231,106 +201,6 @@ namespace MatterHackers.MatterControl } } - public async Task ExportToPlugin(IExportPlugin plugin, string filePathToSave) - { - try - { - string generatedOrExistingFilePath = await SliceFileIfNeeded(); - - var gcodeFileItem = new FileSystemFileItem(generatedOrExistingFilePath); - - - if (File.Exists(generatedOrExistingFilePath)) - { - plugin.Generate(gcodeFileItem, filePathToSave); - return true; - } - } - catch - { - } - - return false; - } - - public async Task SaveGCode(string filePathToSave) - { - try - { - string newGCodePath = await SliceFileIfNeeded(); - - if (File.Exists(newGCodePath)) - { - SaveGCodeToNewLocation(newGCodePath, filePathToSave); - return true; - } - } - catch - { - } - - return false; - } - - private async Task SliceFileIfNeeded() - { - // TODO: How to handle gcode files in library content? - //string fileToProcess = partIsGCode ? printItemWrapper.FileLocation : ""; - string fileToProcess = ""; - - string sourceExtension = Path.GetExtension(libraryContent.FileName).ToUpper(); - if (MeshFileIo.ValidFileExtensions().Contains(sourceExtension) - || sourceExtension == ".MCX") - { - // Save any pending changes before starting the print - await ApplicationController.Instance.ActiveView3DWidget.PersistPlateIfNeeded(); - - var printItem = ApplicationController.Instance.ActivePrintItem; - - await SlicingQueue.SliceFileAsync(printItem, null); - - fileToProcess = printItem.GetGCodePathAndFileName(); - } - - return fileToProcess; - } - - private void SaveGCodeToNewLocation(string gcodeFilename, string dest) - { - try - { - GCodeFileStream gCodeFileStream = new GCodeFileStream(GCodeFile.Load(gcodeFilename, CancellationToken.None)); - - bool addLevelingStream = ActiveSliceSettings.Instance.GetValue(SettingsKey.print_leveling_enabled) && applyLeveling.Checked; - var queueStream = new QueuedCommandsStream(gCodeFileStream); - - // this is added to ensure we are rewriting the G0 G1 commands as needed - GCodeStream finalStream = addLevelingStream - ? new ProcessWriteRegexStream(new PrintLevelingStream(queueStream, false), queueStream) - : new ProcessWriteRegexStream(queueStream, queueStream); - - using (StreamWriter file = new StreamWriter(dest)) - { - string nextLine = finalStream.ReadLine(); - while (nextLine != null) - { - if (nextLine.Trim().Length > 0) - { - file.WriteLine(nextLine); - } - nextLine = finalStream.ReadLine(); - } - } - } - catch (Exception e) - { - UiThread.RunOnIdle(() => - { - StyledMessageBox.ShowMessageBox(null, e.Message, "Couldn't save file".Localize()); - }); - } - } - public override void OnClosed(ClosedEventArgs e) { unregisterEvents?.Invoke(this, null); diff --git a/Library/Export/GCodeExport.cs b/Library/Export/GCodeExport.cs new file mode 100644 index 000000000..6fbad2033 --- /dev/null +++ b/Library/Export/GCodeExport.cs @@ -0,0 +1,175 @@ +/* +Copyright (c) 2017, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.GCodeVisualizer; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.PrinterCommunication.Io; +using MatterHackers.MatterControl.SlicerConfiguration; + +namespace MatterHackers.MatterControl.Library.Export +{ + public class GCodeExport : IExportPlugin + { + public string ButtonText => "G-Code File".Localize(); + + public string FileExtension => ".gcode"; + + public string ExtensionFilter => "Export GCode|*.gcode"; + + public bool EnabledForCurrentPart(ILibraryContentStream libraryContent) + { + return !libraryContent.IsProtected; + } + + public GuiWidget GetOptionsPanel() + { + // If print leveling is enabled then add in a check box 'Apply Leveling During Export' and default checked. + if (ActiveSliceSettings.Instance.GetValue(SettingsKey.print_leveling_enabled)) + { + var container = new FlowLayoutWidget(); + + var checkbox = new CheckBox("Apply leveling to G-Code during export".Localize(), ActiveTheme.Instance.PrimaryTextColor, 10) + { + Checked = true, + Cursor = Cursors.Hand, + }; + checkbox.CheckedStateChanged += (s, e) => + { + this.ApplyLeveling = checkbox.Checked; + }; + container.AddChild(checkbox); + + return container; + } + + return null; + } + + public async Task Generate(IEnumerable libraryItems, string outputPath) + { + ILibraryContentStream libraryContent = libraryItems.OfType().FirstOrDefault(); + + if (libraryContent != null) + { + try + { + string newGCodePath = await SliceFileIfNeeded(libraryContent); + + if (File.Exists(newGCodePath)) + { + SaveGCodeToNewLocation(newGCodePath, outputPath); + return true; + } + } + catch + { + } + + } + + return false; + + } + + // partIsGCode = Path.GetExtension(libraryContent.FileName).ToUpper() == ".GCODE"; + + + private async Task SliceFileIfNeeded(ILibraryContentStream libraryContent) + { + // TODO: How to handle gcode files in library content? + //string fileToProcess = partIsGCode ? printItemWrapper.FileLocation : ""; + string fileToProcess = ""; + + string sourceExtension = Path.GetExtension(libraryContent.FileName).ToUpper(); + if (MeshFileIo.ValidFileExtensions().Contains(sourceExtension) + || sourceExtension == ".MCX") + { + // Save any pending changes before starting the print + await ApplicationController.Instance.ActiveView3DWidget.PersistPlateIfNeeded(); + + var printItem = ApplicationController.Instance.ActivePrintItem; + + await SlicingQueue.SliceFileAsync(printItem, null); + + fileToProcess = printItem.GetGCodePathAndFileName(); + } + + return fileToProcess; + } + + public bool ApplyLeveling { get; set; } + + private void SaveGCodeToNewLocation(string gcodeFilename, string dest) + { + try + { + GCodeFileStream gCodeFileStream = new GCodeFileStream(GCodeFile.Load(gcodeFilename, CancellationToken.None)); + + bool addLevelingStream = ActiveSliceSettings.Instance.GetValue(SettingsKey.print_leveling_enabled) && this.ApplyLeveling; + var queueStream = new QueuedCommandsStream(gCodeFileStream); + + // this is added to ensure we are rewriting the G0 G1 commands as needed + GCodeStream finalStream = addLevelingStream + ? new ProcessWriteRegexStream(new PrintLevelingStream(queueStream, false), queueStream) + : new ProcessWriteRegexStream(queueStream, queueStream); + + using (StreamWriter file = new StreamWriter(dest)) + { + string nextLine = finalStream.ReadLine(); + while (nextLine != null) + { + if (nextLine.Trim().Length > 0) + { + file.WriteLine(nextLine); + } + nextLine = finalStream.ReadLine(); + } + } + } + catch (Exception e) + { + UiThread.RunOnIdle(() => + { + StyledMessageBox.ShowMessageBox(null, e.Message, "Couldn't save file".Localize()); + }); + } + } + + + } +} diff --git a/MatterControl.csproj b/MatterControl.csproj index fa1fed6e8..e3bf1521b 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -169,6 +169,7 @@ +