414 lines
No EOL
11 KiB
C#
414 lines
No EOL
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using MatterHackers.Agg;
|
|
using MatterHackers.Agg.PlatformAbstract;
|
|
using MatterHackers.Agg.UI;
|
|
using MatterHackers.DataConverters3D;
|
|
using MatterHackers.GCodeVisualizer;
|
|
using MatterHackers.Localizations;
|
|
using MatterHackers.MatterControl.CustomWidgets;
|
|
using MatterHackers.MatterControl.DataStorage;
|
|
using MatterHackers.MatterControl.Library;
|
|
using MatterHackers.MatterControl.PrinterCommunication.Io;
|
|
using MatterHackers.MatterControl.PrintQueue;
|
|
using MatterHackers.MatterControl.Queue.OptionsMenu;
|
|
using MatterHackers.MatterControl.SlicerConfiguration;
|
|
using MatterHackers.PolygonMesh;
|
|
|
|
namespace MatterHackers.MatterControl
|
|
{
|
|
public class ExportPrintItemPage : WizardPage
|
|
{
|
|
private CheckBox showInFolderAfterSave;
|
|
private CheckBox applyLeveling;
|
|
private string gcodePathAndFilenameToSave;
|
|
private bool partIsGCode = false;
|
|
private string documentsPath;
|
|
|
|
private EventHandler unregisterEvents;
|
|
|
|
private Dictionary<RadioButton, ExportGcodePlugin> exportPluginButtons;
|
|
|
|
private ILibraryContentStream libraryContent;
|
|
|
|
public ExportPrintItemPage(ILibraryContentStream libraryContent)
|
|
: base(unlocalizedTextForTitle: "File export options:")
|
|
{
|
|
partIsGCode = Path.GetExtension(libraryContent.FileName).ToUpper() == ".GCODE";
|
|
|
|
this.libraryContent = libraryContent;
|
|
this.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor;
|
|
this.Name = "Export Item Window";
|
|
|
|
CreateWindowContent();
|
|
|
|
// TODO: Why? ***************************************************************************************************
|
|
PrinterSettings.PrintLevelingEnabledChanged.RegisterEvent((s, e) => CreateWindowContent(), ref unregisterEvents);
|
|
}
|
|
|
|
public void CreateWindowContent()
|
|
{
|
|
bool modelCanBeExported = !libraryContent.IsProtected;
|
|
|
|
var commonMargin = new BorderDouble(4, 2);
|
|
|
|
// stl export
|
|
var exportAsStlButton = new RadioButton("Export as".Localize() + " STL", textColor: ActiveTheme.Instance.PrimaryTextColor)
|
|
{
|
|
Name = "Export as STL button",
|
|
Margin = commonMargin,
|
|
HAnchor = HAnchor.Left,
|
|
Cursor = Cursors.Hand
|
|
};
|
|
|
|
// amf export
|
|
var exportAsAmfButton = new RadioButton("Export as".Localize() + " AMF", textColor: ActiveTheme.Instance.PrimaryTextColor)
|
|
{
|
|
Name = "Export as AMF button",
|
|
Margin = commonMargin,
|
|
HAnchor = HAnchor.Left,
|
|
Cursor = Cursors.Hand
|
|
};
|
|
if (modelCanBeExported)
|
|
{
|
|
contentRow.AddChild(exportAsStlButton);
|
|
contentRow.AddChild(exportAsAmfButton);
|
|
}
|
|
|
|
// 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<RadioButton, ExportGcodePlugin>();
|
|
|
|
foreach (ExportGcodePlugin plugin in PluginFinder.CreateInstancesOf<ExportGcodePlugin>())
|
|
{
|
|
if (plugin.EnabledForCurrentPart(libraryContent))
|
|
{
|
|
// Create export button for each plugin
|
|
var pluginButton = new RadioButton(plugin.GetButtonText().Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor)
|
|
{
|
|
HAnchor = HAnchor.Left,
|
|
Margin = commonMargin,
|
|
Cursor = Cursors.Hand
|
|
};
|
|
contentRow.AddChild(pluginButton);
|
|
|
|
exportPluginButtons.Add(pluginButton, plugin);
|
|
}
|
|
}
|
|
}
|
|
|
|
contentRow.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<bool>(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);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
HAnchor = HAnchor.Left,
|
|
Cursor = Cursors.Hand
|
|
};
|
|
contentRow.AddChild(showInFolderAfterSave);
|
|
}
|
|
|
|
if (!showExportGCodeButton)
|
|
{
|
|
var noGCodeMessage = new TextWidget(
|
|
"Note".Localize() + ": " + "To enable GCode export, select a printer profile.".Localize(),
|
|
textColor: ActiveTheme.Instance.PrimaryTextColor,
|
|
pointSize: 10);
|
|
noGCodeMessage.HAnchor = HAnchor.Left;
|
|
contentRow.AddChild(noGCodeMessage);
|
|
}
|
|
|
|
var exportButton = textImageButtonFactory.Generate("Export".Localize());
|
|
exportButton.Click += (s, e) =>
|
|
{
|
|
string fileTypeFilter = "";
|
|
string targetExtension = "";
|
|
|
|
ExportGcodePlugin activePlugin = null;
|
|
|
|
if (exportAsStlButton.Checked)
|
|
{
|
|
fileTypeFilter = "Save as STL|*.stl";
|
|
targetExtension = ".stl";
|
|
}
|
|
else if (exportAsAmfButton.Checked)
|
|
{
|
|
fileTypeFilter = "Save as AMF|*.amf";
|
|
targetExtension = ".amf";
|
|
}
|
|
else if (exportGCode.Checked)
|
|
{
|
|
fileTypeFilter = "Export GCode|*.gcode";
|
|
targetExtension = ".gcode";
|
|
}
|
|
else
|
|
{
|
|
// 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.GetExtensionFilter();
|
|
targetExtension = activePlugin.GetFileExtension();
|
|
}
|
|
|
|
this.Parent.CloseOnIdle();
|
|
UiThread.RunOnIdle(() =>
|
|
{
|
|
string title = "MatterControl: " + "Export File".Localize();
|
|
FileDialog.SaveFileDialog(
|
|
new SaveFileDialogParams(fileTypeFilter)
|
|
{
|
|
Title = title,
|
|
ActionButtonLabel = "Export".Localize(),
|
|
FileName = libraryContent.Name
|
|
},
|
|
(saveParams) =>
|
|
{
|
|
string savePath = saveParams.FileName;
|
|
|
|
if (!string.IsNullOrEmpty(savePath))
|
|
{
|
|
Task.Run(async () =>
|
|
{
|
|
string extension = Path.GetExtension(savePath);
|
|
if (extension != targetExtension)
|
|
{
|
|
savePath += targetExtension;
|
|
}
|
|
|
|
bool succeeded = false;
|
|
|
|
if (exportAsStlButton.Checked)
|
|
{
|
|
succeeded = await SaveAmf(libraryContent, savePath);
|
|
}
|
|
else if (exportAsAmfButton.Checked)
|
|
{
|
|
succeeded = await SaveAmf(libraryContent, savePath);
|
|
}
|
|
else if (exportGCode.Checked)
|
|
{
|
|
succeeded = await SaveGCode(savePath);
|
|
}
|
|
else if (activePlugin != null)
|
|
{
|
|
succeeded = await ExportToPlugin(activePlugin, savePath);
|
|
}
|
|
|
|
if (succeeded)
|
|
{
|
|
ShowFileIfRequested(saveParams.FileName);
|
|
}
|
|
else
|
|
{
|
|
UiThread.RunOnIdle(() =>
|
|
{
|
|
StyledMessageBox.ShowMessageBox(null, "Export failed".Localize(), title);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
footerRow.AddChild(exportButton);
|
|
|
|
footerRow.AddChild(new HorizontalSpacer());
|
|
footerRow.AddChild(cancelButton);
|
|
}
|
|
|
|
private void ShowFileIfRequested(string filename)
|
|
{
|
|
if (OsInformation.OperatingSystem == OSType.Windows)
|
|
{
|
|
if (showInFolderAfterSave.Checked)
|
|
{
|
|
#if IS_WINDOWS_FORMS
|
|
WindowsFormsAbstract.ShowFileInFolder(filename);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<bool> SaveAmf(ILibraryContentStream source, string filePathToSave)
|
|
{
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(filePathToSave))
|
|
{
|
|
if (Path.GetExtension(libraryContent.FileName).ToUpper() == Path.GetExtension(filePathToSave).ToUpper())
|
|
{
|
|
using (var result = await libraryContent.GetContentStream(null))
|
|
using (var fileStream = File.Create(filePathToSave))
|
|
{
|
|
result.Stream.CopyTo(fileStream);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
using (var result = await libraryContent.GetContentStream(null))
|
|
{
|
|
IObject3D item = Object3D.Load(result.Stream, Path.GetExtension(libraryContent.FileName), CancellationToken.None);
|
|
return MeshFileIo.Save(item, filePathToSave);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Trace.WriteLine("Error exporting file: " + ex.Message);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async Task<bool> ExportToPlugin(ExportGcodePlugin plugin, string filePathToSave)
|
|
{
|
|
try
|
|
{
|
|
string newGCodePath = await SliceFileIfNeeded();
|
|
|
|
if (File.Exists(newGCodePath))
|
|
{
|
|
plugin.Generate(newGCodePath, filePathToSave);
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async Task<bool> SaveGCode(string filePathToSave)
|
|
{
|
|
try
|
|
{
|
|
string newGCodePath = await SliceFileIfNeeded();
|
|
|
|
if (File.Exists(newGCodePath))
|
|
{
|
|
SaveGCodeToNewLocation(newGCodePath, filePathToSave);
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private async Task<string> 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<bool>(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);
|
|
base.OnClosed(e);
|
|
}
|
|
}
|
|
} |