using MatterHackers.Agg; using MatterHackers.Agg.PlatformAbstract; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.GCodeVisualizer; using MatterHackers.Localizations; using MatterHackers.MatterControl.ConfigurationPage.PrintLeveling; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.DataStorage; using MatterHackers.MatterControl.PrinterCommunication.Io; using MatterHackers.MatterControl.PrintQueue; using MatterHackers.MatterControl.Queue.OptionsMenu; using MatterHackers.MatterControl.SlicerConfiguration; using MatterHackers.PolygonMesh; using MatterHackers.PolygonMesh.Processors; using MatterHackers.VectorMath; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Threading.Tasks; namespace MatterHackers.MatterControl { public class ExportPrintItemWindow : SystemWindow { private CheckBox showInFolderAfterSave; private CheckBox applyLeveling; private PrintItemWrapper printItemWrapper; private string gcodePathAndFilenameToSave; private string x3gPathAndFilenameToSave; private bool partIsGCode = false; private string documentsPath; public ExportPrintItemWindow(PrintItemWrapper printItemWrapper) : base(400, 300) { this.printItemWrapper = printItemWrapper; if (Path.GetExtension(printItemWrapper.FileLocation).ToUpper() == ".GCODE") { partIsGCode = true; } string McExportFileTitleBeg = "MatterControl".Localize(); string McExportFileTitleEnd = "Export File".Localize(); string McExportFileTitleFull = string.Format("{0}: {1}", McExportFileTitleBeg, McExportFileTitleEnd); this.Title = McExportFileTitleFull; this.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; this.Name = "Export Item Window"; CreateWindowContent(); PrinterSettings.PrintLevelingEnabledChanged.RegisterEvent(ReloadAfterPrinterProfileChanged, ref unregisterEvents); } private string applyLevelingDuringExportString = "Apply leveling to G-Code during export".Localize(); public void CreateWindowContent() { this.RemoveAllChildren(); TextImageButtonFactory textImageButtonFactory = new TextImageButtonFactory(); FlowLayoutWidget topToBottom = new FlowLayoutWidget(FlowDirection.TopToBottom); topToBottom.Padding = new BorderDouble(3, 0, 3, 5); topToBottom.AnchorAll(); // Creates Header FlowLayoutWidget headerRow = new FlowLayoutWidget(FlowDirection.LeftToRight); headerRow.HAnchor = HAnchor.ParentLeftRight; headerRow.Margin = new BorderDouble(0, 3, 0, 0); headerRow.Padding = new BorderDouble(0, 3, 0, 3); BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; //Creates Text and adds into header { TextWidget elementHeader = new TextWidget("File export options:".Localize(), pointSize: 14); elementHeader.TextColor = ActiveTheme.Instance.PrimaryTextColor; elementHeader.HAnchor = HAnchor.ParentLeftRight; elementHeader.VAnchor = Agg.UI.VAnchor.ParentBottom; headerRow.AddChild(elementHeader); topToBottom.AddChild(headerRow); } // Creates container in the middle of window FlowLayoutWidget middleRowContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); { middleRowContainer.HAnchor = HAnchor.ParentLeftRight; middleRowContainer.VAnchor = VAnchor.ParentBottomTop; middleRowContainer.Padding = new BorderDouble(5); middleRowContainer.BackgroundColor = ActiveTheme.Instance.SecondaryBackgroundColor; } bool modelCanBeExported = !partIsGCode; if(modelCanBeExported && printItemWrapper != null && (printItemWrapper.PrintItem.Protected || printItemWrapper.PrintItem.ReadOnly)) { modelCanBeExported = false; } if (modelCanBeExported) { // put in stl export string exportStlText = "Export as".Localize(); string exportStlTextFull = string.Format("{0} STL", exportStlText); Button exportAsStlButton = textImageButtonFactory.Generate(exportStlTextFull); exportAsStlButton.Name = "Export as STL button"; exportAsStlButton.HAnchor = HAnchor.ParentLeft; exportAsStlButton.Cursor = Cursors.Hand; exportAsStlButton.Click += exportSTL_Click; middleRowContainer.AddChild(exportAsStlButton); // put in amf export string exportAmfText = "Export as".Localize(); string exportAmfTextFull = string.Format("{0} AMF", exportAmfText); Button exportAsAmfButton = textImageButtonFactory.Generate(exportAmfTextFull); exportAsAmfButton.Name = "Export as AMF button"; exportAsAmfButton.HAnchor = HAnchor.ParentLeft; exportAsAmfButton.Cursor = Cursors.Hand; exportAsAmfButton.Click += exportAMF_Click; middleRowContainer.AddChild(exportAsAmfButton); } bool showExportGCodeButton = ActiveSliceSettings.Instance.PrinterSelected || partIsGCode; if (showExportGCodeButton) { Button exportGCode = textImageButtonFactory.Generate(string.Format("{0} G-Code", "Export as".Localize())); exportGCode.Name = "Export as GCode Button"; exportGCode.HAnchor = HAnchor.ParentLeft; exportGCode.Cursor = Cursors.Hand; exportGCode.Click += (s, e) => { UiThread.RunOnIdle(ExportGCode_Click); }; middleRowContainer.AddChild(exportGCode); PluginFinder exportPluginFinder = new PluginFinder(); foreach (ExportGcodePlugin plugin in exportPluginFinder.Plugins) { if (plugin.EnabledForCurrentPart(printItemWrapper)) { //Create export button for each Plugin found string exportButtonText = plugin.GetButtonText().Localize(); Button exportButton = textImageButtonFactory.Generate(exportButtonText); exportButton.HAnchor = HAnchor.ParentLeft; exportButton.Cursor = Cursors.Hand; exportButton.Click += (s, e) => { UiThread.RunOnIdle(() => { // Close the export window Close(); // Open a SaveFileDialog. If Save is clicked, slice the part if needed and pass the plugin the // path to the gcode file and the target save path FileDialog.SaveFileDialog( new SaveFileDialogParams(plugin.GetExtensionFilter()) { Title = "MatterControl: Export File", FileName = printItemWrapper.Name, ActionButtonLabel = "Export" }, (SaveFileDialogParams saveParam) => { string extension = Path.GetExtension(saveParam.FileName); if (extension == "") { saveParam.FileName += plugin.GetFileExtension(); } if (partIsGCode) { try { plugin.Generate(printItemWrapper.FileLocation, saveParam.FileName); } catch (Exception exception) { UiThread.RunOnIdle(() => { StyledMessageBox.ShowMessageBox(null, exception.Message, "Couldn't save file".Localize()); }); } } else { SlicingQueue.Instance.QueuePartForSlicing(printItemWrapper); printItemWrapper.SlicingDone += (printItem, eventArgs) => { PrintItemWrapper sliceItem = (PrintItemWrapper)printItem; if (File.Exists(sliceItem.GetGCodePathAndFileName())) { try { plugin.Generate(sliceItem.GetGCodePathAndFileName(), saveParam.FileName); } catch (Exception exception) { UiThread.RunOnIdle(() => { StyledMessageBox.ShowMessageBox(null, exception.Message, "Couldn't save file".Localize()); }); } } }; } }); }); }; // End exportButton Click handler middleRowContainer.AddChild(exportButton); } } } middleRowContainer.AddChild(new VerticalSpacer()); // 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(LocalizedString.Get(applyLevelingDuringExportString), ActiveTheme.Instance.PrimaryTextColor, 10); applyLeveling.Checked = true; applyLeveling.HAnchor = HAnchor.ParentLeft; applyLeveling.Cursor = Cursors.Hand; //applyLeveling.Margin = new BorderDouble(top: 10); middleRowContainer.AddChild(applyLeveling); } // TODO: make this work on the mac and then delete this if if (OsInformation.OperatingSystem == OSType.Windows || OsInformation.OperatingSystem == OSType.X11) { showInFolderAfterSave = new CheckBox("Show file in folder after save".Localize(), ActiveTheme.Instance.PrimaryTextColor, 10); showInFolderAfterSave.HAnchor = HAnchor.ParentLeft; showInFolderAfterSave.Cursor = Cursors.Hand; //showInFolderAfterSave.Margin = new BorderDouble(top: 10); middleRowContainer.AddChild(showInFolderAfterSave); } if (!showExportGCodeButton) { string noGCodeMessageTextBeg = "Note".Localize(); string noGCodeMessageTextEnd = "To enable GCode export, select a printer profile.".Localize(); string noGCodeMessageTextFull = string.Format("{0}: {1}", noGCodeMessageTextBeg, noGCodeMessageTextEnd); TextWidget noGCodeMessage = new TextWidget(noGCodeMessageTextFull, textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 10); noGCodeMessage.HAnchor = HAnchor.ParentLeft; middleRowContainer.AddChild(noGCodeMessage); } //Creates button container on the bottom of window FlowLayoutWidget buttonRow = new FlowLayoutWidget(FlowDirection.LeftToRight); { BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; buttonRow.HAnchor = HAnchor.ParentLeftRight; buttonRow.Padding = new BorderDouble(0, 3); } Button cancelButton = textImageButtonFactory.Generate("Cancel".Localize()); cancelButton.Name = "Export Item Window Cancel Button"; cancelButton.Cursor = Cursors.Hand; cancelButton.Click += (sender, e) => { CloseOnIdle(); }; buttonRow.AddChild(new HorizontalSpacer()); buttonRow.AddChild(cancelButton); topToBottom.AddChild(middleRowContainer); topToBottom.AddChild(buttonRow); this.AddChild(topToBottom); } private string Get8Name(string longName) { longName.Replace(' ', '_'); return longName.Substring(0, Math.Min(longName.Length, 8)); } private void ExportGCode_Click() { SaveFileDialogParams saveParams = new SaveFileDialogParams("Export GCode|*.gcode", title: "Export GCode"); saveParams.Title = "MatterControl: Export File"; saveParams.ActionButtonLabel = "Export"; saveParams.FileName = Path.GetFileNameWithoutExtension(printItemWrapper.Name); Close(); FileDialog.SaveFileDialog(saveParams, onExportGcodeFileSelected); } private void onExportGcodeFileSelected(SaveFileDialogParams saveParams) { if (!string.IsNullOrEmpty(saveParams.FileName)) { ExportGcodeCommandLineUtility(saveParams.FileName); } } public void ExportGcodeCommandLineUtility(String nameOfFile) { try { if (!string.IsNullOrEmpty(nameOfFile)) { gcodePathAndFilenameToSave = nameOfFile; string extension = Path.GetExtension(gcodePathAndFilenameToSave); if (extension == "") { File.Delete(gcodePathAndFilenameToSave); gcodePathAndFilenameToSave += ".gcode"; } string sourceExtension = Path.GetExtension(printItemWrapper.FileLocation).ToUpper(); if (MeshFileIo.ValidFileExtensions().Contains(sourceExtension)) { SlicingQueue.Instance.QueuePartForSlicing(printItemWrapper); printItemWrapper.SlicingDone += sliceItem_Done; } else if (partIsGCode) { SaveGCodeToNewLocation(printItemWrapper.FileLocation, gcodePathAndFilenameToSave); } } } catch { } } private void SaveGCodeToNewLocation(string gcodeFilename, string dest) { try { if (ActiveSliceSettings.Instance.GetValue(SettingsKey.print_leveling_enabled)) { if (applyLeveling.Checked) { GCodeFile loadedGCode = GCodeFile.Load(gcodeFilename); GCodeFileStream gCodeFileStream0 = new GCodeFileStream(loadedGCode); PrintLevelingStream printLevelingStream4 = new PrintLevelingStream(gCodeFileStream0, false); // this is added to ensure we are rewriting the G0 G1 commands as needed FeedRateMultiplyerStream extrusionMultiplyerStream = new FeedRateMultiplyerStream(printLevelingStream4); using (StreamWriter file = new StreamWriter(dest)) { string nextLine = extrusionMultiplyerStream.ReadLine(); while (nextLine != null) { file.WriteLine(nextLine); nextLine = extrusionMultiplyerStream.ReadLine(); } } } else { File.Copy(gcodeFilename, dest, true); } } else { File.Copy(gcodeFilename, dest, true); } ShowFileIfRequested(dest); } catch (Exception e) { UiThread.RunOnIdle (() => { StyledMessageBox.ShowMessageBox(null, e.Message, "Couldn't save file".Localize()); }); } } private void ShowFileIfRequested(string filename) { if (OsInformation.OperatingSystem == OSType.Windows) { if (showInFolderAfterSave.Checked) { #if IS_WINDOWS_FORMS WindowsFormsAbstract.ShowFileInFolder(filename); #endif } } } public override void OnClosed(ClosedEventArgs e) { printItemWrapper.SlicingDone -= sliceItem_Done; if (unregisterEvents != null) { unregisterEvents(this, null); } base.OnClosed(e); } private EventHandler unregisterEvents; private void ReloadAfterPrinterProfileChanged(object sender, EventArgs e) { CreateWindowContent(); } private void exportAMF_Click(object sender, EventArgs mouseEvent) { UiThread.RunOnIdle(() => { SaveFileDialogParams saveParams = new SaveFileDialogParams("Save as AMF|*.amf", initialDirectory: documentsPath) { Title = "MatterControl: Export File", ActionButtonLabel = "Export", FileName = printItemWrapper.Name }; Close(); FileDialog.SaveFileDialog(saveParams, onExportAmfFileSelected); }); } private async void onExportAmfFileSelected(SaveFileDialogParams saveParams) { await Task.Run(() => SaveAmf(saveParams)); } private void SaveAmf(SaveFileDialogParams saveParams) { try { if (!string.IsNullOrEmpty(saveParams.FileName)) { string filePathToSave = saveParams.FileName; if (filePathToSave != null && filePathToSave != "") { string extension = Path.GetExtension(filePathToSave); if (extension == "") { File.Delete(filePathToSave); filePathToSave += ".amf"; } if (Path.GetExtension(printItemWrapper.FileLocation).ToUpper() == Path.GetExtension(filePathToSave).ToUpper()) { File.Copy(printItemWrapper.FileLocation, filePathToSave, true); } else { IObject3D item = Object3D.Load(printItemWrapper.FileLocation); MeshFileIo.Save(item, filePathToSave); } ShowFileIfRequested(filePathToSave); } } } catch (Exception e) { UiThread.RunOnIdle (() => { StyledMessageBox.ShowMessageBox(null, e.Message, "Couldn't save file".Localize()); }); } } private void exportSTL_Click(object sender, EventArgs mouseEvent) { UiThread.RunOnIdle(() => { SaveFileDialogParams saveParams = new SaveFileDialogParams("Save as STL|*.stl"); saveParams.Title = "MatterControl: Export File"; saveParams.ActionButtonLabel = "Export"; saveParams.FileName = printItemWrapper.Name; Close(); FileDialog.SaveFileDialog(saveParams, onExportStlFileSelected); }); } private async void onExportStlFileSelected(SaveFileDialogParams saveParams) { await Task.Run(() => SaveStl(saveParams)); } private void SaveStl(SaveFileDialogParams saveParams) { try { if (!string.IsNullOrEmpty(saveParams.FileName)) { string filePathToSave = saveParams.FileName; if (filePathToSave != null && filePathToSave != "") { string extension = Path.GetExtension(filePathToSave); if (extension == "") { File.Delete(filePathToSave); filePathToSave += ".stl"; } if (Path.GetExtension(printItemWrapper.FileLocation).ToUpper() == Path.GetExtension(filePathToSave).ToUpper()) { File.Copy(printItemWrapper.FileLocation, filePathToSave, true); } else { IObject3D loadedItem = Object3D.Load(printItemWrapper.FileLocation); if (!MeshFileIo.Save(new List { loadedItem.Flatten() }, filePathToSave)) { UiThread.RunOnIdle (() => { StyledMessageBox.ShowMessageBox(null, "AMF to STL conversion failed", "Couldn't save file".Localize()); }); } } ShowFileIfRequested(filePathToSave); } } } catch (Exception e) { UiThread.RunOnIdle (() => { StyledMessageBox.ShowMessageBox(null, e.Message, "Couldn't save file".Localize()); }); } } private void sliceItem_Done(object sender, EventArgs e) { PrintItemWrapper sliceItem = (PrintItemWrapper)sender; printItemWrapper.SlicingDone -= sliceItem_Done; SaveGCodeToNewLocation(sliceItem.GetGCodePathAndFileName(), gcodePathAndFilenameToSave); } private void x3gItemSlice_Complete(object sender, EventArgs e) { PrintItemWrapper sliceItem = (PrintItemWrapper)sender; printItemWrapper.SlicingDone -= x3gItemSlice_Complete; if (File.Exists(sliceItem.GetGCodePathAndFileName())) { generateX3GfromGcode(sliceItem.GetGCodePathAndFileName(), x3gPathAndFilenameToSave); } } private string getGpxExectutablePath() { switch (OsInformation.OperatingSystem) { case OSType.Windows: string gpxRelativePath = Path.Combine("..", "gpx.exe"); if (!File.Exists(gpxRelativePath)) { gpxRelativePath = Path.Combine(".", "gpx.exe"); } return Path.GetFullPath(gpxRelativePath); case OSType.Mac: return Path.Combine(ApplicationDataStorage.Instance.ApplicationPath, "gpx"); case OSType.X11: return Path.GetFullPath(Path.Combine(".", "gpx.exe")); default: throw new NotImplementedException(); } } private void generateX3GfromGcode(string gcodeInputPath, string x3gOutputPath, string machineType = "r2") { string gpxExecutablePath = getGpxExectutablePath(); string gpxArgs = string.Format("-p -m {2} \"{0}\" \"{1}\" ", gcodeInputPath, x3gOutputPath, machineType); ProcessStartInfo exportX3GProcess = new ProcessStartInfo(gpxExecutablePath); exportX3GProcess.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; exportX3GProcess.Arguments = gpxArgs; Process.Start(exportX3GProcess); ShowFileIfRequested(x3gOutputPath); } } }