From 2aba5f185dbfbcaff73ceec7339631590ae52154 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 13:47:33 -0800 Subject: [PATCH 01/11] Add Save method to ILibraryWritableContainer --- Library/Interfaces/ILibraryContainer.cs | 3 +++ .../Providers/FileSystem/FileSystemContainer.cs | 14 ++++++++++++++ Library/Providers/WritableContainer.cs | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/Library/Interfaces/ILibraryContainer.cs b/Library/Interfaces/ILibraryContainer.cs index 6cac49ee8..5ab0fb3d1 100644 --- a/Library/Interfaces/ILibraryContainer.cs +++ b/Library/Interfaces/ILibraryContainer.cs @@ -31,6 +31,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using MatterHackers.Agg.Image; +using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.Library { @@ -67,6 +68,8 @@ namespace MatterHackers.MatterControl.Library void Rename(ILibraryItem item, string revisedName); void Move(IEnumerable items, ILibraryContainer targetContainer); + void Save(ILibraryItem item, IObject3D content); + void SetThumbnail(ILibraryItem item, int width, int height, ImageBuffer imageBuffer); bool AllowAction(ContainerActions containerActions); } diff --git a/Library/Providers/FileSystem/FileSystemContainer.cs b/Library/Providers/FileSystem/FileSystemContainer.cs index 12fde2c70..32a5d186b 100644 --- a/Library/Providers/FileSystem/FileSystemContainer.cs +++ b/Library/Providers/FileSystem/FileSystemContainer.cs @@ -36,6 +36,9 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.MatterControl.DataStorage; +using MatterHackers.MeshVisualizer; namespace MatterHackers.MatterControl.Library { @@ -327,6 +330,17 @@ namespace MatterHackers.MatterControl.Library } } + public override void Save(ILibraryItem item, IObject3D content) + { + if (item is FileSystemFileItem fileItem) + { + if (content is InteractiveScene scene) + { + scene.Save(fileItem.Path, ApplicationDataStorage.Instance.ApplicationLibraryDataPath); + } + } + } + public class DirectoryContainerLink : FileSystemItem, ILibraryContainerLink { public DirectoryContainerLink(string path) diff --git a/Library/Providers/WritableContainer.cs b/Library/Providers/WritableContainer.cs index 1cc75699e..1edd43c56 100644 --- a/Library/Providers/WritableContainer.cs +++ b/Library/Providers/WritableContainer.cs @@ -30,6 +30,7 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; using MatterHackers.Agg.Image; +using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.Library { @@ -54,6 +55,10 @@ namespace MatterHackers.MatterControl.Library { } + public virtual void Save(ILibraryItem item, IObject3D content) + { + } + public virtual void Move(IEnumerable items, ILibraryContainer targetContainer) { } From 23d7dedee760796ebf7b86deb5e5986252dbc63a Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 14:02:56 -0800 Subject: [PATCH 02/11] Remove coupling to PrintItemWrapper, revise naming to follow suite --- PrinterCommunication/Io/PauseHandlingStream.cs | 4 ++-- PrinterCommunication/PrinterConnection.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/PrinterCommunication/Io/PauseHandlingStream.cs b/PrinterCommunication/Io/PauseHandlingStream.cs index 403314a3b..5cf38883a 100644 --- a/PrinterCommunication/Io/PauseHandlingStream.cs +++ b/PrinterCommunication/Io/PauseHandlingStream.cs @@ -105,12 +105,12 @@ namespace MatterHackers.MatterControl.PrinterCommunication.Io case PauseReason.PauseLayerReached: case PauseReason.GCodeRequest: - printer.Connection.PauseOnLayer.CallEvents(printer.Connection, new PrintItemWrapperEventArgs(printer.Bed.printItem)); + printer.Connection.PauseOnLayer.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext.SourceItem.Name)); UiThread.RunOnIdle(() => StyledMessageBox.ShowMessageBox(ResumePrint, layerPauseMessage.FormatWith(layerNumber), pauseCaption, StyledMessageBox.MessageType.YES_NO, "Ok".Localize(), "Resume".Localize())); break; case PauseReason.FilamentRunout: - printer.Connection.FilamentRunout.CallEvents(printer.Connection, new PrintItemWrapperEventArgs(printer.Bed.printItem)); + printer.Connection.FilamentRunout.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext.SourceItem.Name)); UiThread.RunOnIdle(() => StyledMessageBox.ShowMessageBox(ResumePrint, filamentPauseMessage, pauseCaption, StyledMessageBox.MessageType.YES_NO, "Ok".Localize(), "Resume".Localize())); break; } diff --git a/PrinterCommunication/PrinterConnection.cs b/PrinterCommunication/PrinterConnection.cs index e664c6fa8..8e09b5542 100644 --- a/PrinterCommunication/PrinterConnection.cs +++ b/PrinterCommunication/PrinterConnection.cs @@ -473,7 +473,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication // Set this early as we always want our functions to know the state we are in. communicationState = value; timeSinceStartedPrint.Stop(); - PrintFinished.CallEvents(this, new PrintItemWrapperEventArgs(printer.Bed.printItem)); + PrintFinished.CallEvents(this, new NamedItemEventArgs(printer.Bed.EditContext.SourceItem.Name)); // clear single use setting on print completion foreach (var keyValue in printer.Settings.BaseLayer) @@ -2846,14 +2846,14 @@ namespace MatterHackers.MatterControl.PrinterCommunication } } - public class PrintItemWrapperEventArgs : EventArgs + public class NamedItemEventArgs : EventArgs { - public PrintItemWrapperEventArgs(PrintItemWrapper printItemWrapper) + public NamedItemEventArgs(string name) { - this.PrintItemWrapper = printItemWrapper; + this.ItemName = name; } - public PrintItemWrapper PrintItemWrapper { get; } + public string ItemName { get; } } /// From 79274a1419899697a6fff37d23c91cea86eaf784 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 14:15:34 -0800 Subject: [PATCH 03/11] Add PlatingHistory container - Issue MatterHackers/MCCentral#2246 Investigate using a library container for plating history --- ApplicationView/ApplicationController.cs | 11 +- Library/Providers/LibraryConfig.cs | 2 + .../MatterControl/PlatingHistoryContainer.cs | 109 ++++++++++++++++++ MatterControl.csproj | 1 + 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 Library/Providers/MatterControl/PlatingHistoryContainer.cs diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index c4bea8342..c97530dc1 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -74,6 +74,8 @@ namespace MatterHackers.MatterControl private static PrinterConfig emptyPrinter = new PrinterConfig(false, PrinterSettings.Empty); + private static string cacheDirectory = Path.Combine(ApplicationDataStorage.ApplicationUserDataPath, "data", "temp", "cache"); + // TODO: Any references to this property almost certainly need to be reconsidered. ActiveSliceSettings static references that assume a single printer // selection are being redirected here. This allows us to break the dependency to the original statics and consolidates // us down to a single point where code is making assumptions about the presence of a printer, printer counts, etc. If we previously checked for @@ -451,6 +453,13 @@ namespace MatterHackers.MatterControl IsReadOnly = true }); + this.Library.PlatingHistory = new PlatingHistoryContainer(); + + this.Library.RegisterRootProvider( + new DynamicContainerLink( + "Plating History".Localize(), + LibraryProviderHelpers.LoadInvertIcon("FileDialog", "folder.png"), + () => ApplicationController.Instance.Library.PlatingHistory)); } public ApplicationController() @@ -632,8 +641,6 @@ namespace MatterHackers.MatterControl return await LoadCacheableAsync(cacheKey, cacheScope, staticDataFallbackPath); } - private static string cacheDirectory = Path.Combine(ApplicationDataStorage.ApplicationUserDataPath, "data", "temp", "cache"); - public static string CacheablePath(string cacheScope, string cacheKey) { string scopeDirectory = Path.Combine(cacheDirectory, cacheScope); diff --git a/Library/Providers/LibraryConfig.cs b/Library/Providers/LibraryConfig.cs index fc1c0eb32..0b44eec9e 100644 --- a/Library/Providers/LibraryConfig.cs +++ b/Library/Providers/LibraryConfig.cs @@ -124,6 +124,8 @@ namespace MatterHackers.MatterControl.Library } } + public PlatingHistoryContainer PlatingHistory { get; internal set; } + public IContentProvider GetContentProvider(ILibraryItem item) { string contentType = (item as ILibraryContentStream)?.ContentType ?? (item as ILibraryContentItem)?.ContentType; diff --git a/Library/Providers/MatterControl/PlatingHistoryContainer.cs b/Library/Providers/MatterControl/PlatingHistoryContainer.cs new file mode 100644 index 000000000..58cf5872b --- /dev/null +++ b/Library/Providers/MatterControl/PlatingHistoryContainer.cs @@ -0,0 +1,109 @@ +/* +Copyright (c) 2017, 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 MatterHackers.Agg.Image; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DataStorage; +using MatterHackers.MatterControl.PrintHistory; +using MatterHackers.MatterControl.Library; + +namespace MatterHackers.MatterControl.Library +{ + public class PlatingHistoryContainer : LibraryContainer, ILibraryWritableContainer + { + public PlatingHistoryContainer() + { + this.ChildContainers = new List(); + this.Items = new List(); + this.Name = "Plating History".Localize(); + } + + public event EventHandler ItemContentChanged; + + public void Add(IEnumerable items) + { + throw new NotImplementedException(); + } + + public bool AllowAction(ContainerActions containerActions) + { + switch(containerActions) + { + case ContainerActions.AddItems: + case ContainerActions.AddContainers: + case ContainerActions.RemoveItems: + case ContainerActions.RenameItems: + return false; + + default: + System.Diagnostics.Debugger.Break(); + return false; + } + } + + public override void Load() + { + // PrintItems projected onto FileSystemFileItem + Items = new DirectoryInfo(ApplicationDataStorage.Instance.PlatingDirectory).GetFiles("*.mcx").OrderByDescending(f => f.CreationTime).Take(25).Select(f => new FileSystemFileItem(f.FullName)).ToList(); + } + + public void Move(IEnumerable items, ILibraryContainer targetContainer) + { + throw new NotImplementedException(); + } + + public void Remove(IEnumerable items) + { + throw new NotImplementedException(); + } + + public void Rename(ILibraryItem item, string revisedName) + { + throw new NotImplementedException(); + } + + public void Save(ILibraryItem item, IObject3D content) + { + if (item is FileSystemFileItem fileItem) + { + // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters + File.WriteAllText(fileItem.Path, content.ToJson()); + } + } + + public void SetThumbnail(ILibraryItem item, int width, int height, ImageBuffer imageBuffer) + { + } + } +} diff --git a/MatterControl.csproj b/MatterControl.csproj index f923444ff..0a87b27f5 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -108,6 +108,7 @@ + From 40c038032d5cf92827f5758eb01aff845d0c910f Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 14:19:02 -0800 Subject: [PATCH 04/11] Move RemoveTab call to parent, only invoke clear printer on printers --- PartPreviewWindow/MainTab.cs | 1 + PartPreviewWindow/Toolbar.cs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PartPreviewWindow/MainTab.cs b/PartPreviewWindow/MainTab.cs index fd36ddbed..4c201e5d4 100644 --- a/PartPreviewWindow/MainTab.cs +++ b/PartPreviewWindow/MainTab.cs @@ -68,6 +68,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { UiThread.RunOnIdle(() => { + this.parentTabControl.RemoveTab(this); this.CloseClicked?.Invoke(this, null); }); }; diff --git a/PartPreviewWindow/Toolbar.cs b/PartPreviewWindow/Toolbar.cs index d4818f0c3..871bad2e5 100644 --- a/PartPreviewWindow/Toolbar.cs +++ b/PartPreviewWindow/Toolbar.cs @@ -244,12 +244,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - private void MainTab_CloseClicked(object sender, EventArgs e) + private async void MainTab_CloseClicked(object sender, EventArgs e) { - if (sender is ITab tab) + if (sender is ITab tab + && tab.TabContent is PrinterTabPage) { - this.RemoveTab(sender as ITab); - ApplicationController.Instance.ClearActivePrinter(); + await ApplicationController.Instance.ClearActivePrinter(); } } From fb84b6a18a54582ad4556f1c1291db642e78cbb1 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 15:45:23 -0800 Subject: [PATCH 05/11] Revise scene content loading - Remove file system dependencies - Use library container interfaces - Issue MatterHackers/MCCentral#2248 Bed no longer persists and rounds trips application restarts - Issue MatterHackers/MCCentral#1764 Get edit part mode working --- ApplicationView/ApplicationController.cs | 29 +++++- ApplicationView/PrinterModels.cs | 94 +++++++++++++------ CustomWidgets/PrintingWindow/BasicBody.cs | 2 +- Library/Widgets/PrintLibraryWidget.cs | 35 +++---- PartPreviewWindow/PlusTabPage.cs | 10 +- .../Settings/ProfileManager.cs | 26 +++-- .../MatterControl/SliceSettingsFieldTests.cs | 2 +- Tests/MatterControl.Tests/SceneTests.cs | 8 +- 8 files changed, 145 insertions(+), 61 deletions(-) diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index c97530dc1..9c0c31925 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -41,6 +41,7 @@ using MatterHackers.MatterControl.PrinterCommunication; using MatterHackers.MatterControl.PrintQueue; using MatterHackers.MatterControl.SlicerConfiguration; using Newtonsoft.Json; +using MatterHackers.MatterControl.Library; namespace MatterHackers.MatterControl { @@ -72,7 +73,7 @@ namespace MatterHackers.MatterControl // A list of printers which are open (i.e. displaying a tab) on this instance of MatterControl public IEnumerable ActivePrinters { get; } = new List(); - private static PrinterConfig emptyPrinter = new PrinterConfig(false, PrinterSettings.Empty); + private static PrinterConfig emptyPrinter = new PrinterConfig(null, PrinterSettings.Empty); private static string cacheDirectory = Path.Combine(ApplicationDataStorage.ApplicationUserDataPath, "data", "temp", "cache"); @@ -114,11 +115,17 @@ namespace MatterHackers.MatterControl private Queue> queuedThumbCallbacks = new Queue>(); - public void SetActivePrinter(PrinterConfig printer, bool allowChangedEvent = true) + public async Task SetActivePrinter(PrinterConfig printer, bool allowChangedEvent = true) { var initialPrinter = this.ActivePrinter; if (initialPrinter?.Settings.ID != printer.Settings.ID) { + // TODO: Consider if autosave is appropriate + if (initialPrinter != emptyPrinter) + { + initialPrinter.Bed.Save(); + } + // If we have an active printer, run Disable if (initialPrinter.Settings != PrinterSettings.Empty) { @@ -129,6 +136,11 @@ namespace MatterHackers.MatterControl (this.ActivePrinters as List).Add(printer); this.ActivePrinter = printer; + if (printer != emptyPrinter) + { + await printer.Bed.LoadContent(); + } + // TODO: Decide if non-printer contexts should prompt for a printer, if we should have a default printer, or get "ActiveTab printer" working // HACK: short term solution to resolve printer reference for non-printer related contexts DragDropData.Printer = printer; @@ -162,9 +174,9 @@ namespace MatterHackers.MatterControl } } - internal void ClearActivePrinter() + internal async Task ClearActivePrinter() { - this.SetActivePrinter(emptyPrinter); + await this.SetActivePrinter(emptyPrinter); } public void RefreshActiveInstance(PrinterSettings updatedPrinterSettings) @@ -411,6 +423,7 @@ namespace MatterHackers.MatterControl () => new SqliteLibraryContainer(rootLibraryCollection.Id))); } + this.Library.RegisterRootProvider( new DynamicContainerLink( "Print History".Localize(), @@ -707,6 +720,13 @@ namespace MatterHackers.MatterControl public void OnApplicationClosed() { + // Save changes before close + if (this.ActivePrinter != null + && this.ActivePrinter != emptyPrinter) + { + this.ActivePrinter.Bed.Save(); + } + ApplicationClosed?.Invoke(null, null); } @@ -975,6 +995,7 @@ namespace MatterHackers.MatterControl { AggContext.StaticData.LoadImageData(stream, imageToLoadInto); } + imageToLoadInto.MarkImageChanged(); } catch diff --git a/ApplicationView/PrinterModels.cs b/ApplicationView/PrinterModels.cs index 0671580ba..d7e55d441 100644 --- a/ApplicationView/PrinterModels.cs +++ b/ApplicationView/PrinterModels.cs @@ -44,6 +44,7 @@ namespace MatterHackers.MatterControl using MatterHackers.Agg; using MatterHackers.DataConverters3D; using MatterHackers.GCodeVisualizer; + using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.PrinterCommunication; using MatterHackers.MeshVisualizer; using MatterHackers.PolygonMesh; @@ -61,40 +62,31 @@ namespace MatterHackers.MatterControl public PrinterConfig Printer { get; set; } + public EditContext EditContext { get; private set; } + public Mesh PrinterShape { get; private set; } - public BedConfig(PrinterConfig printer = null, bool loadLastBedplate = false) + public BedConfig(EditContext editContext, PrinterConfig printer = null) { + this.EditContext = editContext; this.Printer = printer; + } - if (loadLastBedplate) - { - // Find the last used bed plate mcx - var directoryInfo = new DirectoryInfo(ApplicationDataStorage.Instance.PlatingDirectory); - var firstFile = directoryInfo.GetFileSystemInfos("*.mcx").OrderByDescending(fl => fl.CreationTime).FirstOrDefault(); + public async Task LoadContent() + { + // View or caller should invoke LoadContent + this.EditContext.Content = await EditContext.SourceItem.CreateContent(null); + this.Scene.Load(this.EditContext.Content); + } - // Set as the current item - should be restored as the Active scene in the MeshViewer - if (firstFile != null) - { - try - { - var loadedItem = new PrintItemWrapper(new PrintItem(firstFile.Name, firstFile.FullName)); - if (loadedItem != null) - { - this.printItem = loadedItem; - } + internal static ILibraryItem NewPlatingItem() + { + string now = DateTime.Now.ToString("yyyyMMdd-HHmmss"); + string mcxPath = Path.Combine(ApplicationDataStorage.Instance.PlatingDirectory, now + ".mcx"); - this.Scene.Load(firstFile.FullName); - } - catch { } - } - } + File.WriteAllText(mcxPath, new Object3D().ToJson()); - // Clear if not assigned above - if (this.printItem == null) - { - this.ClearPlate(); - } + return new FileSystemFileItem(mcxPath); } internal void ClearPlate() @@ -109,9 +101,13 @@ namespace MatterHackers.MatterControl this.printItem = new PrintItemWrapper(new PrintItem(now, mcxPath)); - File.WriteAllText(mcxPath, new Object3D().ToJson()); + var content = new Object3D(); - this.Scene.Load(mcxPath); + this.Scene.Load(content); + + //this.Scene.Save() + + File.WriteAllText(mcxPath, content.ToJson()); // TODO: Define and fire event and eliminate ActiveView3DWidget - model objects need to be dependency free. For the time being prevent application spin up in ClearPlate due to the call below - if MC isn't loaded, don't notify if (!MatterControlApplication.IsLoading) @@ -120,6 +116,22 @@ namespace MatterHackers.MatterControl } } + internal static ILibraryItem LoadLastPlateOrNew() + { + // Find the last used bed plate mcx + var directoryInfo = new DirectoryInfo(ApplicationDataStorage.Instance.PlatingDirectory); + var firstFile = directoryInfo.GetFileSystemInfos("*.mcx").OrderByDescending(fl => fl.CreationTime).FirstOrDefault(); + + // Set as the current item - should be restored as the Active scene in the MeshViewer + if (firstFile != null) + { + return new FileSystemFileItem(firstFile.FullName); + } + + // Otherwise generate a new plating item + return NewPlatingItem(); + } + private GCodeFile loadedGCode; public GCodeFile LoadedGCode { @@ -320,6 +332,28 @@ namespace MatterHackers.MatterControl // Invalidate bed mesh cache _bedMesh = null; } + + internal void Save() + { + if (this.Scene.Persistable) + { + this.Scene.PersistAssets(ApplicationDataStorage.Instance.ApplicationLibraryDataPath); + this.EditContext.Save(); + } + } + } + + public class EditContext + { + public ILibraryWritableContainer LibraryContainer { get; set; } + public ILibraryItem SourceItem { get; set; } + public IObject3D Content { get; set; } + + internal void Save() + { + // Call save on the provider + this.LibraryContainer.Save(this.SourceItem, this.Content); + } } public class PrinterViewState @@ -386,9 +420,9 @@ namespace MatterHackers.MatterControl private EventHandler unregisterEvents; - public PrinterConfig(bool loadLastBedplate, PrinterSettings settings) + public PrinterConfig(EditContext editContext, PrinterSettings settings) { - this.Bed = new BedConfig(this, loadLastBedplate); + this.Bed = new BedConfig(editContext, this); this.Connection = new PrinterConnection(printer: this); diff --git a/CustomWidgets/PrintingWindow/BasicBody.cs b/CustomWidgets/PrintingWindow/BasicBody.cs index 2513251e7..8b2560bba 100644 --- a/CustomWidgets/PrintingWindow/BasicBody.cs +++ b/CustomWidgets/PrintingWindow/BasicBody.cs @@ -206,7 +206,7 @@ namespace MatterHackers.MatterControl.CustomWidgets progressContainer.AddChild(printerName); - partName = new TextWidget(printer.Bed.printItem.GetFriendlyName(), pointSize: 16, textColor: ActiveTheme.Instance.PrimaryTextColor) + partName = new TextWidget(printer.Bed.EditContext.SourceItem.Name, pointSize: 16, textColor: ActiveTheme.Instance.PrimaryTextColor) { HAnchor = HAnchor.Center, MinimumSize = new Vector2(maxTextWidth, MinimumSize.Y), diff --git a/Library/Widgets/PrintLibraryWidget.cs b/Library/Widgets/PrintLibraryWidget.cs index 2d38f6ae8..f3b4a4888 100644 --- a/Library/Widgets/PrintLibraryWidget.cs +++ b/Library/Widgets/PrintLibraryWidget.cs @@ -423,26 +423,29 @@ namespace MatterHackers.MatterControl.PrintLibrary AllowMultiple = false, AllowProtected = false, AllowContainers = false, - Action = (selectedLibraryItems, listView) => + Action = async (selectedLibraryItems, listView) => { - var firstItem = selectedLibraryItems.FirstOrDefault(); - if (firstItem != null) + if (selectedLibraryItems.FirstOrDefault() is ILibraryItem firstItem + && ApplicationController.Instance.Library.ActiveContainer is ILibraryWritableContainer writableContainer) { - var bedConfig = new BedConfig(); + BedConfig bed; - var newTab = partPreviewContent.CreatePartTab(firstItem.Name, bedConfig, theme); - if (newTab.TabContent is PartTabPage printerTab) + var newTab = partPreviewContent.CreatePartTab( + firstItem.Name, + bed = new BedConfig( + new EditContext() + { + LibraryContainer = writableContainer, + SourceItem = firstItem + }), + theme); + + // Load content after UI widgets to support progress notification during acquire/load + await bed.LoadContent(); + + if (newTab.TabContent is PartTabPage partTab) { - bedConfig.Scene.Children.Modify(list => - { - list.Add( - new InsertionGroup( - selectedLibraryItems.Take(1), - printerTab.view3DWidget, - bedConfig.Scene, - bedConfig.BedCenter, - () => false)); - }); + // TODO: Restore ability to render progress loading } } } diff --git a/PartPreviewWindow/PlusTabPage.cs b/PartPreviewWindow/PlusTabPage.cs index 139a7996e..d334ab522 100644 --- a/PartPreviewWindow/PlusTabPage.cs +++ b/PartPreviewWindow/PlusTabPage.cs @@ -61,7 +61,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow UiThread.RunOnIdle(() => { simpleTabs.RemoveTab(simpleTabs.ActiveTab); - partPreviewContent.CreatePartTab("New Part", new BedConfig(), theme); + partPreviewContent.CreatePartTab( + "New Part", + new BedConfig( + new EditContext() + { + LibraryContainer = ApplicationController.Instance.Library.PlatingHistory, + SourceItem = BedConfig.NewPlatingItem() + }), + theme); }); }; diff --git a/SlicerConfiguration/Settings/ProfileManager.cs b/SlicerConfiguration/Settings/ProfileManager.cs index 130027005..65d92f3c4 100644 --- a/SlicerConfiguration/Settings/ProfileManager.cs +++ b/SlicerConfiguration/Settings/ProfileManager.cs @@ -61,8 +61,12 @@ namespace MatterHackers.MatterControl.SlicerConfiguration // Load or download on a background thread the last loaded settings ApplicationController.Instance.SetActivePrinter( new PrinterConfig( - true, - LoadProfileAsync(activeInstance.LastProfileID).Result)); + new EditContext() + { + LibraryContainer = ApplicationController.Instance.Library.PlatingHistory, + SourceItem = BedConfig.LoadLastPlateOrNew() + }, + LoadProfileAsync(activeInstance.LastProfileID).Result)).ConfigureAwait(false); } } } @@ -142,9 +146,13 @@ namespace MatterHackers.MatterControl.SlicerConfiguration { ProfileManager.Instance.LastProfileID = printerID; - ApplicationController.Instance.SetActivePrinter( + await ApplicationController.Instance.SetActivePrinter( new PrinterConfig( - false, + new EditContext() + { + LibraryContainer = ApplicationController.Instance.Library.PlatingHistory, + SourceItem = BedConfig.LoadLastPlateOrNew() + }, await ProfileManager.LoadProfileAsync(printerID))); } @@ -472,9 +480,15 @@ namespace MatterHackers.MatterControl.SlicerConfiguration // Set as active profile ProfileManager.Instance.LastProfileID = guid; - var printer = new PrinterConfig(false, printerSettings); + var printer = new PrinterConfig( + new EditContext() + { + LibraryContainer = ApplicationController.Instance.Library.PlatingHistory, + SourceItem = BedConfig.LoadLastPlateOrNew() + }, + printerSettings); - ApplicationController.Instance.SetActivePrinter(printer); + await ApplicationController.Instance.SetActivePrinter(printer); return printer; } diff --git a/Tests/MatterControl.Tests/MatterControl/SliceSettingsFieldTests.cs b/Tests/MatterControl.Tests/MatterControl/SliceSettingsFieldTests.cs index 61c72c106..5c00b24ca 100644 --- a/Tests/MatterControl.Tests/MatterControl/SliceSettingsFieldTests.cs +++ b/Tests/MatterControl.Tests/MatterControl/SliceSettingsFieldTests.cs @@ -277,7 +277,7 @@ namespace MatterControl.Tests.MatterControl AggContext.StaticData = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData")); MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); - var field = new ComPortField(new PrinterConfig(false, PrinterSettings.Empty)); + var field = new ComPortField(new PrinterConfig(null, PrinterSettings.Empty)); await ValidateAgainstValueMap( field, diff --git a/Tests/MatterControl.Tests/SceneTests.cs b/Tests/MatterControl.Tests/SceneTests.cs index 7e28bb64f..0765d8095 100644 --- a/Tests/MatterControl.Tests/SceneTests.cs +++ b/Tests/MatterControl.Tests/SceneTests.cs @@ -35,6 +35,7 @@ using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.DataConverters3D; using MatterHackers.MatterControl; +using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.Tests.Automation; using MatterHackers.MeshVisualizer; using Newtonsoft.Json; @@ -101,7 +102,7 @@ namespace MatterHackers.PolygonMesh.UnitTests MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); #endif - var sceneContext = new BedConfig(); + var sceneContext = new BedConfig(null); var scene = sceneContext.Scene; scene.Children.Add(new Object3D @@ -135,7 +136,10 @@ namespace MatterHackers.PolygonMesh.UnitTests scene.Children.Modify(list => list.Clear()); // Reload the model - await Task.Run(() => sceneContext.Scene.Load(filePath)); + await Task.Run(() => + { + sceneContext.Scene.Load(Object3D.Load(filePath, CancellationToken.None)); + }); // Serialize and compare the two trees string onDiskData = JsonConvert.SerializeObject(loadedItem, Formatting.Indented); From ddfeb79d17e33256c8b710f0ca2fbff78097c3c6 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 15:47:45 -0800 Subject: [PATCH 06/11] Interactive save shouldn't be more encumbered than auto save - Don't prompt for Save location if we're already auto saving to default locations in other contexts --- PartPreviewWindow/View3D/View3DWidget.cs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/PartPreviewWindow/View3D/View3DWidget.cs b/PartPreviewWindow/View3D/View3DWidget.cs index bc59532ad..5582d00e1 100644 --- a/PartPreviewWindow/View3D/View3DWidget.cs +++ b/PartPreviewWindow/View3D/View3DWidget.cs @@ -343,14 +343,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Title = "Save".Localize(), Action = async () => { - if (sceneContext.printItem == null) - { - UiThread.RunOnIdle(OpenSaveAsWindow); - } - else - { - await this.SaveChanges(); - } + await this.SaveChanges(); } }, new NamedAction() @@ -2017,16 +2010,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow try { - // Force to .mcx - if (Path.GetExtension(sceneContext.printItem.FileLocation) != ".mcx") - { - sceneContext.printItem.FileLocation = Path.ChangeExtension(sceneContext.printItem.FileLocation, ".mcx"); - } - - // TODO: Hook up progress reporting - Scene.Save(sceneContext.printItem.FileLocation, ApplicationDataStorage.Instance.ApplicationLibraryDataPath); - - sceneContext.printItem.PrintItem.Commit(); + sceneContext.Save(); } catch (Exception ex) { From 28baf658d50c833c6d8f8ffca54749cba835ad09 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 16:08:13 -0800 Subject: [PATCH 07/11] Render root mesh if present - Render only, no editing abilities due to selection behavior --- PartPreviewWindow/View3D/MeshViewerWidget.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PartPreviewWindow/View3D/MeshViewerWidget.cs b/PartPreviewWindow/View3D/MeshViewerWidget.cs index 3427c0ea1..a2af89e6c 100644 --- a/PartPreviewWindow/View3D/MeshViewerWidget.cs +++ b/PartPreviewWindow/View3D/MeshViewerWidget.cs @@ -567,6 +567,13 @@ namespace MatterHackers.MeshVisualizer private void Draw_GlOpaqueContent(object sender, DrawEventArgs e) { List transparentMeshes = new List(); + + // TODO: Adding in to test out how editing InteractiveScene roots might work. Remove/adjust after discussing + if (scene.Mesh != null) + { + GLHelper.Render(scene.Mesh, scene.WorldColor(), scene.Matrix, RenderTypes.Shaded); + } + foreach (var object3D in scene.Children) { DrawObject(object3D, transparentMeshes, false, e); From bfd6fb54d3a7a0d3ba6d19d597b74b89ab86529f Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 16:34:25 -0800 Subject: [PATCH 08/11] Short term workaround to run sync during load --- SlicerConfiguration/Settings/ProfileManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SlicerConfiguration/Settings/ProfileManager.cs b/SlicerConfiguration/Settings/ProfileManager.cs index 65d92f3c4..1946f0abc 100644 --- a/SlicerConfiguration/Settings/ProfileManager.cs +++ b/SlicerConfiguration/Settings/ProfileManager.cs @@ -66,7 +66,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration LibraryContainer = ApplicationController.Instance.Library.PlatingHistory, SourceItem = BedConfig.LoadLastPlateOrNew() }, - LoadProfileAsync(activeInstance.LastProfileID).Result)).ConfigureAwait(false); + // Short term workaround to run sync during load + LoadProfileAsync(activeInstance.LastProfileID).Result)).Wait(); } } } From 6df0dba376b891f5feb2da3a67b68fc5cfed0cc8 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 16:54:27 -0800 Subject: [PATCH 09/11] Perform FileSystemFileItem edits and update thumbnails - Move common FileSystemFileItem edit into base WritableContainer - Issue MatterHackers/MCCentral#2251 Thumbnails not updated after edits --- ApplicationView/PrinterModels.cs | 6 ++++++ Library/Providers/FileSystem/FileSystemContainer.cs | 13 ------------- .../MatterControl/PlatingHistoryContainer.cs | 3 +-- Library/Providers/WritableContainer.cs | 10 +++++++++- Submodules/agg-sharp | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ApplicationView/PrinterModels.cs b/ApplicationView/PrinterModels.cs index d7e55d441..c300e380e 100644 --- a/ApplicationView/PrinterModels.cs +++ b/ApplicationView/PrinterModels.cs @@ -351,6 +351,12 @@ namespace MatterHackers.MatterControl internal void Save() { + var thumbnailPath = ApplicationController.Instance.ThumbnailCachePath(this.SourceItem); + if (File.Exists(thumbnailPath)) + { + File.Delete(thumbnailPath); + } + // Call save on the provider this.LibraryContainer.Save(this.SourceItem, this.Content); } diff --git a/Library/Providers/FileSystem/FileSystemContainer.cs b/Library/Providers/FileSystem/FileSystemContainer.cs index 32a5d186b..34f459f51 100644 --- a/Library/Providers/FileSystem/FileSystemContainer.cs +++ b/Library/Providers/FileSystem/FileSystemContainer.cs @@ -37,8 +37,6 @@ using System.Threading.Tasks; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; -using MatterHackers.MatterControl.DataStorage; -using MatterHackers.MeshVisualizer; namespace MatterHackers.MatterControl.Library { @@ -330,17 +328,6 @@ namespace MatterHackers.MatterControl.Library } } - public override void Save(ILibraryItem item, IObject3D content) - { - if (item is FileSystemFileItem fileItem) - { - if (content is InteractiveScene scene) - { - scene.Save(fileItem.Path, ApplicationDataStorage.Instance.ApplicationLibraryDataPath); - } - } - } - public class DirectoryContainerLink : FileSystemItem, ILibraryContainerLink { public DirectoryContainerLink(string path) diff --git a/Library/Providers/MatterControl/PlatingHistoryContainer.cs b/Library/Providers/MatterControl/PlatingHistoryContainer.cs index 58cf5872b..4f115109e 100644 --- a/Library/Providers/MatterControl/PlatingHistoryContainer.cs +++ b/Library/Providers/MatterControl/PlatingHistoryContainer.cs @@ -35,8 +35,6 @@ using MatterHackers.Agg.Image; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.DataStorage; -using MatterHackers.MatterControl.PrintHistory; -using MatterHackers.MatterControl.Library; namespace MatterHackers.MatterControl.Library { @@ -99,6 +97,7 @@ namespace MatterHackers.MatterControl.Library { // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters File.WriteAllText(fileItem.Path, content.ToJson()); + this.ItemContentChanged?.Invoke(this, new ItemChangedEventArgs(fileItem)); } } diff --git a/Library/Providers/WritableContainer.cs b/Library/Providers/WritableContainer.cs index 1edd43c56..92dbfbc07 100644 --- a/Library/Providers/WritableContainer.cs +++ b/Library/Providers/WritableContainer.cs @@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; +using System.IO; using MatterHackers.Agg.Image; using MatterHackers.DataConverters3D; @@ -40,7 +41,7 @@ namespace MatterHackers.MatterControl.Library public virtual void OnItemContentChanged(ItemChangedEventArgs args) { - ItemContentChanged?.Invoke(this, args); + this.ItemContentChanged?.Invoke(this, args); } public virtual void Add(IEnumerable items) @@ -57,6 +58,13 @@ namespace MatterHackers.MatterControl.Library public virtual void Save(ILibraryItem item, IObject3D content) { + if (item is FileSystemFileItem fileItem) + { + // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters + File.WriteAllText(fileItem.Path, content.ToJson()); + + this.OnItemContentChanged(new ItemChangedEventArgs(fileItem)); + } } public virtual void Move(IEnumerable items, ILibraryContainer targetContainer) diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 157ae3944..80de803ed 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 157ae3944409f34e7381ef0c72d8022a34914111 +Subproject commit 80de803ed4923a7c6aae1bc121f6ad3b72687060 From 1861add2079676a50fbf32ad58a5bdfc7ed1ab3a Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 17:55:03 -0800 Subject: [PATCH 10/11] Use custom serialization implementation --- Tests/MatterControl.Tests/SceneTests.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Tests/MatterControl.Tests/SceneTests.cs b/Tests/MatterControl.Tests/SceneTests.cs index 0765d8095..a7a1ce7e5 100644 --- a/Tests/MatterControl.Tests/SceneTests.cs +++ b/Tests/MatterControl.Tests/SceneTests.cs @@ -127,10 +127,11 @@ namespace MatterHackers.PolygonMesh.UnitTests Assert.AreEqual(1, Directory.GetFiles(tempPath).Length, "Only .mcx file should exists"); Assert.AreEqual(1, Directory.GetFiles(Path.Combine(tempPath, "Assets")).Length, "Only 1 asset should exist"); - var originalFiles = Directory.GetFiles(tempPath).ToArray(); ; + var originalFiles = Directory.GetFiles(tempPath).ToArray(); + // Load the file from disk IObject3D loadedItem = Object3D.Load(filePath, CancellationToken.None); - Assert.IsTrue(loadedItem.Children.Count == 1); + Assert.AreEqual(1, loadedItem.Children.Count); // Ensure the UI scene is cleared scene.Children.Modify(list => list.Clear()); @@ -142,13 +143,17 @@ namespace MatterHackers.PolygonMesh.UnitTests }); // Serialize and compare the two trees - string onDiskData = JsonConvert.SerializeObject(loadedItem, Formatting.Indented); - string inMemoryData = JsonConvert.SerializeObject(scene, Formatting.Indented); - Assert.IsTrue(inMemoryData == onDiskData); + string onDiskData = loadedItem.ToJson(); + string inMemoryData = scene.ToJson(); + + //File.WriteAllText(@"c:\temp\file-a.txt", onDiskData); + //File.WriteAllText(@"c:\temp\file-b.txt", inMemoryData); + + Assert.AreEqual(inMemoryData, onDiskData, "Serialized content should match"); // Save the scene a second time, validate that things remain the same scene.Save(filePath, tempPath); - onDiskData = JsonConvert.SerializeObject(loadedItem, Formatting.Indented); + onDiskData = loadedItem.ToJson(); Assert.IsTrue(inMemoryData == onDiskData); From 02443adebc807a28a833e1b55e24e5fcfe5db517 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 14 Nov 2017 17:56:16 -0800 Subject: [PATCH 11/11] Guard for test scenarios with uninitialized environment --- PrinterCommunication/Io/PauseHandlingStream.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PrinterCommunication/Io/PauseHandlingStream.cs b/PrinterCommunication/Io/PauseHandlingStream.cs index 5cf38883a..debe71031 100644 --- a/PrinterCommunication/Io/PauseHandlingStream.cs +++ b/PrinterCommunication/Io/PauseHandlingStream.cs @@ -105,12 +105,12 @@ namespace MatterHackers.MatterControl.PrinterCommunication.Io case PauseReason.PauseLayerReached: case PauseReason.GCodeRequest: - printer.Connection.PauseOnLayer.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext.SourceItem.Name)); + printer.Connection.PauseOnLayer.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext?.SourceItem?.Name ?? "Unknown")); UiThread.RunOnIdle(() => StyledMessageBox.ShowMessageBox(ResumePrint, layerPauseMessage.FormatWith(layerNumber), pauseCaption, StyledMessageBox.MessageType.YES_NO, "Ok".Localize(), "Resume".Localize())); break; case PauseReason.FilamentRunout: - printer.Connection.FilamentRunout.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext.SourceItem.Name)); + printer.Connection.FilamentRunout.CallEvents(printer.Connection, new NamedItemEventArgs(printer.Bed.EditContext?.SourceItem?.Name ?? "Unknown")); UiThread.RunOnIdle(() => StyledMessageBox.ShowMessageBox(ResumePrint, filamentPauseMessage, pauseCaption, StyledMessageBox.MessageType.YES_NO, "Ok".Localize(), "Resume".Localize())); break; }