diff --git a/CHANGELOG.md b/CHANGELOG.md index f44504cf..68d99b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 21/06/2023 - v3.14.4 + +- (Add) File - Rename: Allow to rename the current file with a new name (Ctrl + F2) +- (Improvement) Tool - Edit print parameters: It now apply settings without close the window, allowing user to do continuous work. After all editing is done the user must manually close the window (#731) +- (Improvement) Resin traps and suction cups: Optimization of contour grouping will now make the detection faster if it contains a large number of contours +- (Change) Lower the default setting for binary threshold for resin traps, from 127 to 100 +- (Fix) macOS: Unable to have settings on Monterey or above due the settings folder no longer exists on recent systems. (#728) + Your current settings will not be automatically transferred to the new location, to do such please copy them over or use the following command before upgrade: `mv "$HOME/.local/share/UVtools" "$HOME/Library/Application Support"` + If you already ran UVtools and would like to transfer old settings, use: `cp -Rf "$HOME/.local/share/UVtools/" "$HOME/Library/Application Support/UVtools/"` +- (Upgrade) .NET from 6.0.16 to 6.0.18 + ## 11/06/2023 - v3.14.3 - **Settings:** diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 091a18e9..7f671e2e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,9 +1,9 @@ -- **Settings:** - - (Add) After save the file replace on it name with the updated print time and material if possible - - (Add) File 'Save as' default name with extended variables (#725) - - (Add) File 'Save as' name cleanup regex - - (Add) When save as a file and if the file name already exists on that directory it will append a number up to one available - - (Remove) File 'Save as' suffix and prefix -- (Add) About: Graphic card name -- (Fix) CTB Encrypted: Check the checksum as last step to be compatible with new Chitubox 1.4.5 CTB files (#696, #726) +- (Add) File - Rename: Allow to rename the current file with a new name (Ctrl + F2) +- (Improvement) Tool - Edit print parameters: It now apply settings without close the window, allowing user to do continuous work. After all editing is done the user must manually close the window (#731) +- (Improvement) Resin traps and suction cups: Optimization of contour grouping will now make the detection faster if it contain a large number of contours +- (Change) Lower the default setting for binary threshold for resin traps, from 127 to 100 +- (Fix) macOS: Unable to have settings on Monterey or above due the settings folder no longer exists on recent systems. (#728) + Your current settings will not be automatically transferred to the new location, to do such please copy them over or use the following command before upgrade: `mv "$HOME/.local/share/UVtools" "$HOME/Library/Application Support"` + If you already ran UVtools and would like to transfer old settings, use: `cp -Rf "$HOME/.local/share/UVtools/" "$HOME/Library/Application Support/UVtools/"` +- (Upgrade) .NET from 6.0.16 to 6.0.18 diff --git a/Scripts/010 Editor/FileFormats.1pj b/Scripts/010 Editor/FileFormats.1pj index a6c26eec..d1a11aed 100644 --- a/Scripts/010 Editor/FileFormats.1pj +++ b/Scripts/010 Editor/FileFormats.1pj @@ -8,6 +8,7 @@ ctb_decrypted.bt ctb_encrypted.bt cxdlp.bt + cxdlpv4.bt cxdlp_v1.bt fdg.bt gr1.bt diff --git a/Scripts/010 Editor/cxdlp.bt b/Scripts/010 Editor/cxdlp.bt index b92fae4b..8da2f509 100644 --- a/Scripts/010 Editor/cxdlp.bt +++ b/Scripts/010 Editor/cxdlp.bt @@ -35,8 +35,8 @@ typedef struct(int size) { } RgbPreviewImageRawData; struct HEADER { - uint32 HeaderSize ; - char Header[HeaderSize] ; + uint32 MagicSize ; + char Magic[MagicSize] ; uint16 Version ; diff --git a/Scripts/010 Editor/cxdlpv4.bt b/Scripts/010 Editor/cxdlpv4.bt new file mode 100644 index 00000000..64501d9c --- /dev/null +++ b/Scripts/010 Editor/cxdlpv4.bt @@ -0,0 +1,179 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: cxdlpv4 +// Authors: Tiago Conceição +//------------------------------------------------ + +LittleEndian(); + +struct HEADER { + BigEndian(); + uint32 MagicSize ; + char Magic[MagicSize] ; + ushort Version ; + + uint32 PrinterModelSize ; + char PrinterModel[PrinterModelSize] ; + LittleEndian(); + + ushort ResolutionX ; + ushort ResolutionY ; + + float BedSizeX ; + float BedSizeY ; + float BedSizeZ ; + + float TotalHeightMilimeter ; + float LayerHeightMilimeter ; + uint BottomLayersCount ; + + uint PreviewSmallOffsetAddress ; + uint LayersDefinitionOffsetAddress ; + uint LayerCount ; + uint PreviewLargeOffsetAddress ; + uint PrintTime ; + uint ProjectorType ; + uint PrintParametersOffsetAddress ; + uint PrintParametersSize ; + uint AntiAliasLevel ; + ushort LightPWM ; + ushort BottomLightPWM ; + uint EncryptionKey ; + uint SlicerOffset ; + uint SlicerSize ; +} header; + +struct PREVIEW { + uint ResolutionX ; + uint ResolutionY ; + uint ImageOffset ; + uint ImageLength ; + uint Unknown1 ; + uint Unknown2 ; + uint Unknown3 ; + uint Unknown4 ; + + ubyte Data[ImageLength] ; +}; + +struct PRINT_PARAMETERS { + float BottomLiftHeight ; + float BottomLiftSpeed ; + float LiftHeight ; + float LiftSpeed ; + float RetractSpeed ; + float VolumeMl ; + float WeightG ; + float CostDollars ; + float BottomLightOffDelay ; + float LightOffDelay ; + + uint BottomLayerCount ; + float ExposureTime ; + float BottomExposureTime ; + uint Padding ; + uint Padding ; + uint Padding ; + uint Padding ; +}; + +struct SLICER_INFO { + float BottomLiftHeight2 ; + float BottomLiftSpeed2 ; + float LiftHeight2 ; + float LiftSpeed2 ; + float RetractHeight2 ; + float RetractSpeed2 ; + float RestTimeAfterLift ; + + uint PerLayerSettings ; // 0 to not support, 1 to support + uint TimestampMinutes ; + uint AntiAliasLevel ; + uint SoftwareVersion ; // 0 + float RestTimeAfterRetract ; + float RestTimeBeforeLift ; + float BottomExposureTime ; + float ExposureTime ; + float RestTimeAfterLift ; + uint TransitionLayerCount ; + uint Padding ; + uint Padding ; +}; + +if(header.PreviewSmallOffsetAddress > 0) +{ + FSeek(header.PreviewSmallOffsetAddress); + PREVIEW previewSmall ; +} + +if(header.PreviewLargeOffsetAddress > 0) +{ + FSeek(header.PreviewLargeOffsetAddress); + PREVIEW previewLarge ; +} + +if(header.PrintParametersOffsetAddress > 0){ + FSeek(header.PrintParametersOffsetAddress); + PRINT_PARAMETERS parameters ; +} + +if(header.SlicerOffset > 0){ + FSeek(header.SlicerOffset); + SLICER_INFO SlicerInfo ; +} + +struct LAYER_DATA { + float LayerPositionZ ; + float LayerExposure ; + float LightOffSeconds ; + uint DataAddress ; + uint DataSize ; + uint DataType ; + uint CentroidDistance ; + uint TotalArea ; + uint Unknown ; + uint Unknown ; +}; + +struct LAYER_DATAEX { + float LiftHeight ; + float LiftSpeed ; + float LiftHeight2 ; + float LiftSpeed2 ; + float RetractSpeed ; + float RetractHeight2 ; + float RetractSpeed2 ; + float RestTimeBeforeLift ; + float RestTimeAfterLift ; + float RestTimeAfterRetract ; + float LightPWM ; +}; + + +typedef struct(int size) { + ubyte layerDataBlock[size] ; +} LAYER_RLE; + +FSeek(header.LayersDefinitionOffsetAddress); +struct LAYERS { + local uint i; + for( i = 0; i < header.LayerCount; i++ ){ + LAYER_DATA layerData ; + } +} layers; + +struct LAYERSEX { + local uint i; + + for( i = 0; i < header.LayerCount; i++ ){ + LAYER_DATAEX layerDataEx; + LAYER_RLE lD(layers.layerData[i].DataSize - 44); + } + +} layersEx; + + +BigEndian(); +uint CheckSum ; +LittleEndian(); \ No newline at end of file diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs index 870a7c25..9ca28b70 100644 --- a/UVtools.Core/CoreSettings.cs +++ b/UVtools.Core/CoreSettings.cs @@ -108,6 +108,11 @@ public static string DefaultSettingsFolder { get { + if (OperatingSystem.IsMacOS()) + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", About.Software); + } + var folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index 555b7520..101c20a2 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -258,6 +258,24 @@ public static List[] GetContoursInGroups(VectorOfVectorOf /// public static List GetPositiveContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy) { + var vectorSize = contours.Size; + var result = new List(); + VectorOfVectorOfPoint? vec = null; + for (int i = 0; i < vectorSize; i++) + { + if (hierarchy[i, EmguContour.HierarchyParent] == -1) + { + vec = new VectorOfVectorOfPoint(contours[i]); + result.Add(vec); + } + else + { + vec.Push(contours[i]); + } + } + + return result; + /* Old var result = new List(); var vectorSize = contours.Size; var processedContours = new bool[vectorSize]; @@ -275,7 +293,7 @@ public static List GetPositiveContoursInGroups(VectorOfVe } } - return result; + return result;*/ } /// @@ -287,6 +305,26 @@ public static List GetNegativeContoursInGroups(VectorOfVe { var result = new List(); var vectorSize = contours.Size; + for (int i = 1; i < vectorSize; i++) + { + if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue; + var vec = new VectorOfVectorOfPoint(contours[i]); + result.Add(vec); + + var contourId = i; + for (i += 1; i < vectorSize && hierarchy[i, EmguContour.HierarchyParent] >= contourId; i++) + { + vec.Push(contours[i]); + } + + i--; + } + + return result; + + // Old code + /*var result = new List(); + var vectorSize = contours.Size; var processedContours = new bool[vectorSize]; for (int i = 0; i < vectorSize; i++) { @@ -303,7 +341,7 @@ public static List GetNegativeContoursInGroups(VectorOfVe } } - return result; + return result;*/ } /// diff --git a/UVtools.Core/Layers/LayerIssueConfiguration.cs b/UVtools.Core/Layers/LayerIssueConfiguration.cs index 70055b81..20a3029c 100644 --- a/UVtools.Core/Layers/LayerIssueConfiguration.cs +++ b/UVtools.Core/Layers/LayerIssueConfiguration.cs @@ -138,7 +138,7 @@ public sealed class IslandDetectionConfiguration : DetectionConfiguration public bool AllowDiagonalBonds { get; set; } = false; /// - /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white + /// Gets or sets the binary threshold, all pixels equal or below this value will turn in black, otherwise white /// Set to 0 to disable this operation /// public byte BinaryThreshold { get; set; } = 1; @@ -218,10 +218,10 @@ public sealed class ResinTrapDetectionConfiguration : DetectionConfiguration public uint StartLayerIndex { get; set; } /// - /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white + /// Gets or sets the binary threshold, all pixels equal or below this value will turn in black, otherwise white /// Set to 0 to disable this operation /// - public byte BinaryThreshold { get; set; } = 127; + public byte BinaryThreshold { get; set; } = 100; /// /// Gets the required area size (x*y) to consider process a hollow area (0-255) diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs index 183f9bc5..02cb98b0 100644 --- a/UVtools.Core/Managers/IssueManager.cs +++ b/UVtools.Core/Managers/IssueManager.cs @@ -237,7 +237,10 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndexInt => { progress.PauseIfRequested(); - if (progress.Token.IsCancellationRequested) return; + if (progress.Token.IsCancellationRequested) + { + return; + } uint layerIndex = (uint)layerIndexInt; var layer = SlicerFile[layerIndex]; @@ -255,7 +258,6 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO ) { progress.LockAndIncrement(); - return; } @@ -606,9 +608,9 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO } var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle); - using var contours = contourLayer.FindContours(out var heirarchy, RetrType.Tree); - externalContours[layerIndex] = EmguContours.GetExternalContours(contours, heirarchy); - hollows[layerIndex] = EmguContours.GetNegativeContoursInGroups(contours, heirarchy); + using var contours = contourLayer.FindContours(out var hierarchy, RetrType.Tree); + externalContours[layerIndex] = EmguContours.GetExternalContours(contours, hierarchy); + hollows[layerIndex] = EmguContours.GetNegativeContoursInGroups(contours, hierarchy); resinTrapsContoursArea[layerIndex] = EmguContours.GetContoursArea(hollows[layerIndex]); if (needDispose) @@ -647,6 +649,8 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO }); // Parallel end } + if (progress.Token.IsCancellationRequested) return GetResult(); + if (resinTrapConfig.Enabled) { //progress.Reset("Detecting Air Boundaries (Resin traps)", LayerCount); diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index f20eee3f..48c06786 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.14.3 + 3.14.4 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -91,7 +91,7 @@ - + diff --git a/UVtools.WPF/Controls/RenameFileControl.axaml b/UVtools.WPF/Controls/RenameFileControl.axaml new file mode 100644 index 00000000..e848845e --- /dev/null +++ b/UVtools.WPF/Controls/RenameFileControl.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/UVtools.WPF/Controls/RenameFileControl.axaml.cs b/UVtools.WPF/Controls/RenameFileControl.axaml.cs new file mode 100644 index 00000000..e778b9c7 --- /dev/null +++ b/UVtools.WPF/Controls/RenameFileControl.axaml.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Markup.Xaml; +using MessageBox.Avalonia.Enums; +using UVtools.Core.FileFormats; +using UVtools.WPF.Extensions; + +namespace UVtools.WPF.Controls +{ + public partial class RenameFileControl : ToolBaseControl + { + private string _newFileNameNoExt; + private readonly string _fileExtension; + private bool _overwrite; + + public string OldFilePath { get; } + public string OldFileNameNoExt { get; } + + public string FileExtension => _fileExtension; + + public string NewFileNameNoExt + { + get => _newFileNameNoExt; + set + { + if (!RaiseAndSetIfChanged(ref _newFileNameNoExt, value)) return; + RaisePropertyChanged(nameof(NewFileName)); + RaisePropertyChanged(nameof(NewFilePath)); + ParentWindow.ButtonOkEnabled = CanRename; + } + } + + public string NewFileName => $"{_newFileNameNoExt}.{_fileExtension}"; + + public string NewFilePath => Path.Combine(SlicerFile.DirectoryPath, NewFileName); + + public bool Overwrite + { + get => _overwrite; + set => RaiseAndSetIfChanged(ref _overwrite, value); + } + + public bool CanRename => !string.IsNullOrWhiteSpace(_newFileNameNoExt) && !string.Equals(OldFileNameNoExt, _newFileNameNoExt.Trim(), StringComparison.Ordinal) && _newFileNameNoExt.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; + + public RenameFileControl() + { + OldFilePath = SlicerFile.FileFullPath; + _newFileNameNoExt = OldFileNameNoExt = FileFormat.GetFileNameStripExtensions(OldFilePath, out _fileExtension); + + DataContext = this; + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + protected override void OnInitialized() + { + ParentWindow.ButtonOkEnabled = false; + } + + public override string Validate() + { + var sb = new StringBuilder(); + + if (string.IsNullOrWhiteSpace(_newFileNameNoExt)) + { + sb.AppendLine("The filename can not be blank."); + } + + if (string.Equals(OldFileNameNoExt, _newFileNameNoExt, StringComparison.Ordinal)) + { + sb.AppendLine("The old and new filename can not be the same."); + } + + var invalidChars = Path.GetInvalidFileNameChars(); + if (_newFileNameNoExt.IndexOfAny(invalidChars) >= 0) + { + return $"The filename have invalid characters. The following characters are forbidden:\n{string.Join(", ", Path.GetInvalidFileNameChars())}"; + } + + return sb.ToString(); + } + + public override async Task OnBeforeProcess() + { + NewFileNameNoExt = _newFileNameNoExt.Trim(); + if (!CanRename) + { + return false; + } + + if (_overwrite) return true; + + if (!File.Exists(NewFilePath)) return true; + if (await ParentWindow.MessageBoxQuestion( + $"The file \"{_newFileNameNoExt}\" already exists, do you want to overwrite?", + "File already exists") == ButtonResult.Yes) + { + Overwrite = true; + return true; + } + + return false; + } + } +} diff --git a/UVtools.WPF/Controls/ToolBaseControl.cs b/UVtools.WPF/Controls/ToolBaseControl.cs new file mode 100644 index 00000000..a69645c9 --- /dev/null +++ b/UVtools.WPF/Controls/ToolBaseControl.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using UVtools.WPF.Extensions; +using UVtools.WPF.Windows; + +namespace UVtools.WPF.Controls; + +public class ToolBaseControl : UserControlEx +{ + public ToolWindow ParentWindow { get; set; } = null; + + public bool CanRun { get; set; } = true; + + public virtual string Validate() => null; + + public virtual bool ValidateSpawn() => true; + + /// + /// Validates if is safe to continue with operation + /// + /// + public virtual async Task ValidateForm() + { + return await ValidateFormFromString(Validate()); + } + + /// + /// Validates if is safe to continue with operation, if not shows a message box with the error + /// + /// + /// + public async Task ValidateFormFromString(string text) + { + if (string.IsNullOrEmpty(text)) return true; + await ParentWindow.MessageBoxError(text); + return false; + } + + /// + /// Called before process, ie click on Ok button + /// + /// True if can continue processing the operation, otherwise false + public virtual Task OnBeforeProcess() => Task.FromResult(true); + + /// + /// Called after process, ie click on Ok button + /// + /// + public virtual Task OnAfterProcess() => Task.FromResult(true); +} \ No newline at end of file diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml b/UVtools.WPF/Controls/Tools/ToolControl.axaml deleted file mode 100644 index 9fca8863..00000000 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.cs similarity index 56% rename from UVtools.WPF/Controls/Tools/ToolControl.axaml.cs rename to UVtools.WPF/Controls/Tools/ToolControl.cs index 6b2f2d1e..756deee5 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolControl.cs @@ -1,14 +1,12 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System.Threading.Tasks; -using UVtools.Core.Objects; using UVtools.Core.Operations; using UVtools.WPF.Extensions; using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools; -public class ToolControl : UserControlEx +public class ToolControl : ToolBaseControl { private Operation _baseOperation; @@ -39,37 +37,27 @@ public Operation BaseOperation } } - public ToolWindow ParentWindow { get; set; } = null; - - public bool CanRun { get; set; } = true; - public ToolControl() { - InitializeComponent(); } public ToolControl(Operation operation) { BaseOperation = operation; if (!ValidateSpawn()) return; - InitializeComponent(); } - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - public bool ValidateSpawn() + public override bool ValidateSpawn() { if (Design.IsDesignMode) return true; - if(_baseOperation is null) + if (_baseOperation is null) { App.MainWindow.MessageBoxInfo("The operation does not contain a valid configuration.\n" + "Please contact the support/developer.", BaseOperation.NotSupportedTitle).ConfigureAwait(false); CanRun = false; return false; } + if (!_baseOperation.ValidateSpawn(out var message)) { App.MainWindow.MessageBoxInfo(message, BaseOperation.NotSupportedTitle).ConfigureAwait(false); @@ -80,43 +68,20 @@ public bool ValidateSpawn() return true; } + public virtual void Callback(ToolWindow.Callbacks callback) { } public virtual bool UpdateOperation() => true; - /*public virtual void SetOperation(Operation operation) + public override string Validate() { - BaseOperation = operation; - ResetDataContext(); - }*/ - - /// - /// Validates if is safe to continue with operation - /// - /// - public virtual async Task ValidateForm() + return BaseOperation.Validate(); + } + + public override async Task ValidateForm() { if (BaseOperation is null) return true; if (!UpdateOperation()) return false; - return await ValidateFormFromString(BaseOperation.Validate()); - } - - /// - /// Validates if is safe to continue with operation, if not shows a message box with the error - /// - /// - /// - public async Task ValidateFormFromString(string text) - { - if (string.IsNullOrEmpty(text)) return true; - await ParentWindow.MessageBoxError(text); - return false; + return await base.ValidateForm(); } - - /// - /// Validates if is safe to continue with operation, if not shows a message box with the error - /// - /// - /// - public async Task ValidateFormFromString(ValueDescription text) => await ValidateFormFromString(text?.ToString()); } \ No newline at end of file diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs index 83ee0092..ebbce927 100644 --- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs @@ -6,6 +6,7 @@ using System; using System.ComponentModel; using System.Globalization; +using System.Threading.Tasks; using UVtools.Core.FileFormats; using UVtools.Core.Operations; using UVtools.WPF.Windows; @@ -179,6 +180,25 @@ private void InitializeComponent() AvaloniaXamlLoader.Load(this); } + protected override void OnInitialized() + { + ParentWindow.CloseWindowAfterProcess = false; + } + + public void RefreshModifiers() + { + if (Operation.PerLayerOverride) + { + Operation.Modifiers = SlicerFile.PrintParameterPerLayerModifiers; + SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart); + } + else + { + Operation.Modifiers = SlicerFile.PrintParameterModifiers; + SlicerFile.RefreshPrintParametersModifiersValues(); + } + } + public override void Callback(ToolWindow.Callbacks callback) { switch (callback) @@ -207,20 +227,18 @@ private void OperationOnPropertyChanged(object sender, PropertyChangedEventArgs } if (e.PropertyName == nameof(Operation.PerLayerOverride)) { - if (Operation.PerLayerOverride) - { - Operation.Modifiers = SlicerFile.PrintParameterPerLayerModifiers; - SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart); - } - else - { - Operation.Modifiers = SlicerFile.PrintParameterModifiers; - SlicerFile.RefreshPrintParametersModifiersValues(); - } - + RefreshModifiers(); ParentWindow.LayerRangeVisible = Operation.PerLayerOverride; PopulateGrid(); return; } } + + public override Task OnAfterProcess() + { + Operation.Execute(); + RefreshModifiers(); + PopulateGrid(); + return Task.FromResult(true); + } } \ No newline at end of file diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs index f5ad69c4..10209acc 100644 --- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs @@ -20,7 +20,23 @@ private void InitializeComponent() AvaloniaXamlLoader.Load(this); } - public static OperationRepairLayers GetOperationRepairLayers() => new (App.SlicerFile) + public static OperationRepairLayers GetOperationDisabledRepair() => new (App.SlicerFile) + { + RepairIslands = false, + RepairResinTraps = false, + RepairSuctionCups = false, + RemoveEmptyLayers = false, + RemoveIslandsBelowEqualPixelCount = 0, + RemoveIslandsRecursiveIterations = 0, + AttachIslandsBelowLayers = 0, + ResinTrapsOverlapBy = 0, + SuctionCupsVentHole = 0, + GapClosingIterations = 0, + NoiseRemovalIterations = 0, + IssuesDetectionConfig = App.MainWindow.GetIssuesDetectionConfiguration() + }; + + public static OperationRepairLayers GetOperationRepairLayers() => new(App.SlicerFile) { RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands, RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps, diff --git a/UVtools.WPF/ErrorLog.cs b/UVtools.WPF/ErrorLog.cs index 1dd6775d..340906e9 100644 --- a/UVtools.WPF/ErrorLog.cs +++ b/UVtools.WPF/ErrorLog.cs @@ -9,14 +9,13 @@ public static class ErrorLog { private const string Filename = "errors.log"; - public static string FullPath = Path.Combine(UserSettings.SettingsFolder, Filename); + public static string FullPath => Path.Combine(CoreSettings.DefaultSettingsFolderAndEnsureCreation, Filename); public static void AppendLine(string errorType, string text) { try { - File.AppendAllText(FullPath, - $"[v{About.VersionStr}] ({errorType}) @ {DateTime.Now}: {text}{Environment.NewLine}"); + File.AppendAllText(FullPath, $"[v{About.VersionStr}] ({errorType}) @ {DateTime.Now}: {text}{Environment.NewLine}"); } catch (Exception exception) { diff --git a/UVtools.WPF/MainWindow.Progress.cs b/UVtools.WPF/MainWindow.Progress.cs index c7fedc5d..67af78f4 100644 --- a/UVtools.WPF/MainWindow.Progress.cs +++ b/UVtools.WPF/MainWindow.Progress.cs @@ -46,9 +46,7 @@ public void InitProgress() Debug.WriteLine(_lastTotalSeconds);*/ _progressLastTotalSeconds = elapsedSeconds; - Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render); - }; } diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 888442f6..f98df384 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -53,6 +53,13 @@ Command="{Binding ReloadFile}" i:MenuItem.Icon="mdi-file-restore"/> + + (this); + + if (result != DialogResults.OK) return; + + try + { + if (SlicerFile.RenameFile(control.NewFileNameNoExt, control.Overwrite)) + { + RemoveRecentFile(control.OldFilePath); + AddRecentFile(control.NewFilePath); + } + + } + catch (Exception e) + { + await this.MessageBoxError(e.ToString(), "Error while trying to rename the file"); + } + + } public async void MenuFileSaveClicked() { @@ -1769,17 +1799,21 @@ await this.MessageBoxWaring( if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Full) { - if (Settings.Issues.ComputeIssuesOnLoad) + if (Settings.Issues.ComputeIssuesOnFileLoad == UserSettings.IssuesUserSettings.ComputeIssuesOnFileLoadType.EnabledIssues) { _firstTimeOnIssues = false; await OnClickDetectIssues(); if (SlicerFile.IssueManager.Count > 0) { - SelectedTabItem = TabIssues; if (Settings.Issues.AutoRepairIssuesOnLoad) await RunOperation(ToolRepairLayersControl.GetOperationRepairLayers()); + + if (SlicerFile.IssueManager.Count > 0) + { + SelectedTabItem = TabIssues; + } } } - else + else if (Settings.Issues.ComputeIssuesOnFileLoad == UserSettings.IssuesUserSettings.ComputeIssuesOnFileLoadType.TimeInexpensiveIssues) { var config = GetIssuesDetectionConfiguration(false); config.PrintHeightConfig.Enable(); @@ -1787,7 +1821,15 @@ await this.MessageBoxWaring( await ComputeIssues(config); if (SlicerFile.IssueManager.Count > 0) { - SelectedTabItem = TabIssues; + var operation = ToolRepairLayersControl.GetOperationDisabledRepair(); + operation.RemoveEmptyLayers = true; + if (Settings.Issues.AutoRepairIssuesOnLoad) await RunOperation(operation); + + if (SlicerFile.IssueManager.Count > 0) + { + _firstTimeOnIssues = false; + SelectedTabItem = TabIssues; + } } } } @@ -2172,7 +2214,7 @@ public async Task RunOperation(Operation baseOperation) switch (baseOperation) { case OperationEditParameters operation: - operation.Execute(); + //operation.Execute(); Already applied inside control RefreshProperties(); RefreshCurrentLayerData(); ResetDataContext(); @@ -2193,7 +2235,7 @@ public async Task RunOperation(Operation baseOperation) if (config.IslandConfig.Enabled || config.ResinTrapConfig.Enabled) { - ComputeIssues(config); + await ComputeIssues(config); } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index f28ff88f..e286cc84 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 3.14.3 + 3.14.4 AnyCPU;x64 UVtools.png README.md diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index 9ea70e2d..b0981175 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; @@ -878,7 +879,17 @@ public LayerPreviewUserSettings Clone() public sealed class IssuesUserSettings : BindableBase { - private bool _computeIssuesOnLoad; + public enum ComputeIssuesOnFileLoadType : byte + { + [Description("Do not compute issues")] + None, + [Description("Compute time inexpensive issues (Empty layers and print height)")] + TimeInexpensiveIssues, + [Description("Compute the enabled issues")] + EnabledIssues, + } + + private ComputeIssuesOnFileLoadType _computeIssuesOnFileLoad = ComputeIssuesOnFileLoadType.TimeInexpensiveIssues; private bool _autoRepairIssuesOnLoad; private bool _computeIssuesOnClickTab = true; private bool _computeIslands = true; @@ -915,10 +926,10 @@ public sealed class IssuesUserSettings : BindableBase private decimal _printHeightOffset; private IssuesOrderBy _dataGridOrderBy = IssuesOrderBy.TypeAscLayerAscAreaDesc; - public bool ComputeIssuesOnLoad + public ComputeIssuesOnFileLoadType ComputeIssuesOnFileLoad { - get => _computeIssuesOnLoad; - set => RaiseAndSetIfChanged(ref _computeIssuesOnLoad, value); + get => _computeIssuesOnFileLoad; + set => RaiseAndSetIfChanged(ref _computeIssuesOnFileLoad, value); } public bool AutoRepairIssuesOnLoad diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs index 7bc1b1a7..1007496e 100644 --- a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs +++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs @@ -67,10 +67,10 @@ public enum BenchmarkResolution /*PW0 8K Encode*/ new BenchmarkTestResult(45.15f, 678.43f), /*PNG 4K Compress*/ new BenchmarkTestResult(86.96f, 1479.29f), /*PNG 8K Compress*/ new BenchmarkTestResult(22.17f, 369.00f), - /*GZip 4K Compress*/ new BenchmarkTestResult(204.08f, 1096.49f), - /*GZip 8K Compress*/ new BenchmarkTestResult(45.56f, 264.47f), - /*Deflate 4K Compress*/ new BenchmarkTestResult(208.33f, 1084.60f), - /*Deflate 8K Compress*/ new BenchmarkTestResult(46.087f, 256.54f), + /*GZip 4K Compress*/ new BenchmarkTestResult(243.9f, 4000.00f), + /*GZip 8K Compress*/ new BenchmarkTestResult(63.29f, 1020.41f), + /*Deflate 4K Compress*/ new BenchmarkTestResult(246.91f, 4000.00f), + /*Deflate 8K Compress*/ new BenchmarkTestResult(64.10f, 1033.06f), /*LZ4 4K Compress*/ new BenchmarkTestResult(1052.63f, 6666.67f), /*LZ4 8K Compress*/ new BenchmarkTestResult(281.69f, 2155.17f), /*Stress CPU test*/ new BenchmarkTestResult(0f, 0f), diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index 306ad3eb..7cd43829 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -1153,32 +1153,32 @@ + + + + - - + - - + IsChecked="{Binding Settings.Issues.ComputeIssuesOnClickTab}"/> - - - + + + @@ -1208,17 +1208,13 @@ - + - + diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml index 09218355..6f2555bf 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml +++ b/UVtools.WPF/Windows/ToolWindow.axaml @@ -351,8 +351,9 @@ ColumnDefinitions="*">