diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-09 01:37:59 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-09 01:37:59 +0300 |
commit | dc0e90a61311b2d78104e46ac34c8faf382dcbd3 (patch) | |
tree | 03afc3cceb7dbacbddea34c535700f042d42502e /UVtools.WPF | |
parent | ea410468fcb8c8ba93c955b3a69e6654e48f0b2f (diff) |
v2.11.0v2.11.0
- **Tools:**
- (Add) Pixel Arithmetic
- (Add) Layer arithmetic: Operator $ to perform a absolute difference
- (Add) Allow to save and auto restore operation settings per session (#195)
- (Add) Allow to auto select the print volume ROI
- (Add) Allow to export and import operation settings from files
- (Improvement) Calculator - LightOff delay: Hide the bottom properties or the tab if the file format don't support them (#193)
- (Change) 'Arithmetic' to 'Layer arithmetic'
- (Remove) 'Threshold pixels'
- (Fix) Solidfy was unable to save profiles
- (Fix) A redo operation (Ctrl + Shift + Z) wasn't restoring the settings when a default profile is set
- **Operations:**
- (Fix) Passing a roi mat to `ApplyMask` would cause unwanted results
- (Improvement) Allow pass a full/original size mask to `ApplyMask`
- **Scripting:**
- (Add) an script to create an printable file to clean the VAT (#170)
- (Improvement) Allow to change user input properties outside the initialization
- (Improvement) Auto format numerical input box with the fixed decimal cases
- (Add) Settings: Section 'Tools'
- (Improvement) GUI: The 'Lift, Retract and Light-off' at status bar now only shows for the supported formats
- (Fix) Print time estimation calculation was wrong since v2.9.3 due a lacking of parentheses on the logic
Diffstat (limited to 'UVtools.WPF')
17 files changed, 501 insertions, 117 deletions
diff --git a/UVtools.WPF/Assets/Icons/file-export-16x16.png b/UVtools.WPF/Assets/Icons/file-export-16x16.png Binary files differnew file mode 100644 index 0000000..885e637 --- /dev/null +++ b/UVtools.WPF/Assets/Icons/file-export-16x16.png diff --git a/UVtools.WPF/Controls/Helpers.cs b/UVtools.WPF/Controls/Helpers.cs index ca66103..61046f2 100644 --- a/UVtools.WPF/Controls/Helpers.cs +++ b/UVtools.WPF/Controls/Helpers.cs @@ -15,7 +15,7 @@ namespace UVtools.WPF.Controls { public static readonly List<FileDialogFilter> ImagesFileFilter = new() { - new FileDialogFilter + new() { Name = "Image Files", Extensions = new List<string> @@ -29,9 +29,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List<FileDialogFilter> PngFileFilter = new List<FileDialogFilter> + public static readonly List<FileDialogFilter> PngFileFilter = new() { - new FileDialogFilter + new() { Name = "Image Files", Extensions = new List<string> @@ -41,9 +41,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List<FileDialogFilter> TxtFileFilter = new List<FileDialogFilter> + public static readonly List<FileDialogFilter> TxtFileFilter = new() { - new FileDialogFilter + new() { Name = "Text Files", Extensions = new List<string> @@ -53,9 +53,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List<FileDialogFilter> IniFileFilter = new List<FileDialogFilter> + public static readonly List<FileDialogFilter> IniFileFilter = new() { - new FileDialogFilter + new() { Name = "Ini Files", Extensions = new List<string> @@ -65,9 +65,21 @@ namespace UVtools.WPF.Controls } }; + public static readonly List<FileDialogFilter> OperationSettingFileFilter = new() + { + new() + { + Name = "UVtools operation settings", + Extensions = new List<string> + { + "uvtop", + } + } + }; + public static readonly List<FileDialogFilter> ScriptsFileFilter = new() { - new FileDialogFilter + new() { Name = "Script Files", Extensions = new List<string> @@ -87,9 +99,9 @@ namespace UVtools.WPF.Controls public static List<FileDialogFilter> ToAvaloniaFilter(string name, string extension) { - return new List<FileDialogFilter>(1) + return new(1) { - new FileDialogFilter {Name = name, Extensions = new List<string>(1) {extension}} + new() {Name = name, Extensions = new List<string>(1) {extension}} }; } } diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml index c179b3b..37a6410 100644 --- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml @@ -232,7 +232,7 @@ </TabItem> - <TabItem Header="Light-off delay"> + <TabItem Header="Light-off delay" IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}"> <Grid RowDefinitions="Auto,10,Auto"> <Border @@ -403,12 +403,16 @@ <TextBlock Grid.Row="0" Grid.Column="6" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" Text="Bottom lift height:"/> <NumericUpDown Grid.Row="0" Grid.Column="8" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Minimum="0" Maximum="1000" @@ -418,18 +422,24 @@ <TextBlock Grid.Row="0" Grid.Column="10" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Text="mm"/> <TextBlock Grid.Row="2" Grid.Column="6" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" Text="Bottom lift speed:"/> <NumericUpDown Grid.Row="2" Grid.Column="8" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Minimum="0" Maximum="1000" @@ -439,18 +449,24 @@ <TextBlock Grid.Row="2" Grid.Column="10" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Text="mm/min"/> <TextBlock Grid.Row="6" Grid.Column="6" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" Text="Bottom desired wait time:"/> <NumericUpDown Grid.Row="6" Grid.Column="8" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Minimum="0" Maximum="1000" @@ -460,12 +476,16 @@ <TextBlock Grid.Row="6" Grid.Column="10" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" Text="s"/> <TextBlock Grid.Row="8" Grid.Column="6" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" FontWeight="Bold" @@ -473,6 +493,8 @@ <TextBox Grid.Row="8" Grid.Column="8" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" IsReadOnly="true" FontWeight="Bold" @@ -480,6 +502,8 @@ <TextBlock Grid.Row="8" Grid.Column="10" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" FontWeight="Bold" VerticalAlignment="Center" Text="s"/> @@ -488,6 +512,8 @@ Grid.Row="10" Grid.Column="6" Grid.ColumnSpan="3" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" Padding="10" @@ -499,6 +525,8 @@ Grid.Row="12" Grid.Column="6" Grid.ColumnSpan="5" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding SlicerFile.BottomLightOffDelay, StringFormat=Current value: \{0\}}"/> diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml index 9239816..87dff5c 100644 --- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" - x:Class="UVtools.WPF.Controls.Tools.ToolArithmeticControl" + x:Class="UVtools.WPF.Controls.Tools.ToolLayerArithmeticControl" Width="720" > diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs index 18ad8ff..fc07e66 100644 --- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs @@ -4,14 +4,14 @@ using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools { - public class ToolArithmeticControl : ToolControl + public class ToolLayerArithmeticControl : ToolControl { - public OperationArithmetic Operation => BaseOperation as OperationArithmetic; + public OperationLayerArithmetic Operation => BaseOperation as OperationLayerArithmetic; - public ToolArithmeticControl() + public ToolLayerArithmeticControl() { InitializeComponent(); - BaseOperation = new OperationArithmetic(SlicerFile); + BaseOperation = new OperationLayerArithmetic(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml new file mode 100644 index 0000000..c4ca28e --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml @@ -0,0 +1,99 @@ +<UserControl xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="UVtools.WPF.Controls.Tools.ToolPixelArithmeticControl"> + + <StackPanel Spacing="10"> + <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto,20,Auto,10,Auto,20,Auto,10,Auto"> + + <TextBlock Grid.Row="0" Grid.Column="0" + VerticalAlignment="Center" + Text="Operator:"/> + + <ComboBox Grid.Row="0" Grid.Column="2" + Grid.ColumnSpan="9" + Width="610" + Items="{Binding Operation.Operator, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.Operator, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> + + <TextBlock Grid.Row="2" Grid.Column="0" + VerticalAlignment="Center" + IsVisible="{Binding Operation.ValueEnabled}" + IsEnabled="{Binding Operation.ValueEnabled}" + Text="Brightness:"/> + + <StackPanel Grid.Row="2" Grid.Column="2" + VerticalAlignment="Center" + Orientation="Horizontal" Spacing="5" + IsVisible="{Binding Operation.ValueEnabled}" + IsEnabled="{Binding Operation.ValueEnabled}"> + + <NumericUpDown + Minimum="0" + Maximum="255" + Value="{Binding Operation.Value}"/> + + <TextBlock VerticalAlignment="Center" + Text="{Binding Operation.ValuePercent, StringFormat={}{0}%}"/> + + </StackPanel> + + <CheckBox Grid.Row="2" Grid.Column="4" + Content="Affect empty/black pixels" + IsVisible="{Binding Operation.AffectBackPixelsEnabled}" + IsChecked="{Binding Operation.AffectBackPixels}"/> + + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + HorizontalAlignment="Right" + IsVisible="{Binding Operation.ThresholdEnabled}" + IsEnabled="{Binding Operation.ThresholdEnabled}" + Text="Max.:"/> + + <NumericUpDown Grid.Row="2" Grid.Column="6" + Minimum="0" + Maximum="255" + IsVisible="{Binding Operation.ThresholdEnabled}" + IsEnabled="{Binding Operation.ThresholdEnabled}" + Value="{Binding Operation.ThresholdMaxValue}"/> + + <TextBlock Grid.Row="2" Grid.Column="8" + VerticalAlignment="Center" + HorizontalAlignment="Right" + IsVisible="{Binding Operation.ThresholdEnabled}" + IsEnabled="{Binding Operation.ThresholdEnabled}" + Text="Threshold:"/> + + <ComboBox Grid.Row="2" Grid.Column="10" + Width="130" + IsVisible="{Binding Operation.ThresholdEnabled}" + IsEnabled="{Binding Operation.ThresholdEnabled}" + Items="{Binding Operation.ThresholdType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.ThresholdType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> + + + <TextBlock Grid.Row="4" Grid.Column="0" + VerticalAlignment="Center" + Text="Presets:"/> + + <StackPanel Grid.Row="4" Grid.Column="2" + Grid.ColumnSpan="9" + VerticalAlignment="Center" + Orientation="Horizontal" Spacing="5"> + <Button + Command="{Binding Operation.PresetStripAntiAliasing}" + Content="Strip anti-aliasing"/> + + <Button + IsVisible="{Binding Operation.ValueEnabled}" + Command="{Binding Operation.PresetHalfBrightness}" + Content="Half brightness"/> + </StackPanel> + + </Grid> + </StackPanel> + +</UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs new file mode 100644 index 0000000..eda6fcd --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs @@ -0,0 +1,20 @@ +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public partial class ToolPixelArithmeticControl : ToolControl + { + public OperationPixelArithmetic Operation => BaseOperation as OperationPixelArithmetic; + public ToolPixelArithmeticControl() + { + InitializeComponent(); + BaseOperation = new OperationPixelArithmetic(SlicerFile); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs index eaff935..f68e6f2 100644 --- a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs @@ -383,6 +383,11 @@ namespace UVtools.WPF.Controls.Tools MinWidth = 150 }; + if (numFLOAT.DecimalPlates > 0) + { + control.FormatString = $"F{numFLOAT.DecimalPlates}"; + } + var valueProperty = control.GetObservable(NumericUpDown.ValueProperty); valueProperty.Subscribe(value => { @@ -407,6 +412,11 @@ namespace UVtools.WPF.Controls.Tools MinWidth = 150 }; + if (numDOUBLE.DecimalPlates > 0) + { + control.FormatString = $"F{numDOUBLE.DecimalPlates}"; + } + var valueProperty = control.GetObservable(NumericUpDown.ValueProperty); valueProperty.Subscribe(value => { @@ -431,6 +441,11 @@ namespace UVtools.WPF.Controls.Tools MinWidth = 150 }; + if (numDECIMAL.DecimalPlates > 0) + { + control.FormatString = $"F{numDECIMAL.DecimalPlates}"; + } + var valueProperty = control.GetObservable(NumericUpDown.ValueProperty); valueProperty.Subscribe(value => { diff --git a/UVtools.WPF/MainWindow.Clipboard.cs b/UVtools.WPF/MainWindow.Clipboard.cs index bcbf66c..32784c7 100644 --- a/UVtools.WPF/MainWindow.Clipboard.cs +++ b/UVtools.WPF/MainWindow.Clipboard.cs @@ -70,7 +70,7 @@ namespace UVtools.WPF return; } if (clip?.Operation is null) return; - if (clip.Operation.HaveROI) + /*if (clip.Operation.HaveROI) { ROI = GetTransposedRectangle(clip.Operation.ROI); } @@ -78,7 +78,7 @@ namespace UVtools.WPF if (clip.Operation.HaveMask) { AddMaskPoints(clip.Operation.MaskPoints); - } + }*/ var operation = await ShowRunOperation(clip.Operation.GetType(), clip.Operation); if (operation is null) diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 019700e..01577b8 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -284,67 +284,24 @@ VerticalAlignment="Center"> <TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/> - <TextBlock Text=" | Bottom layers: "/> - <TextBlock Text="{Binding SlicerFile.BottomLayerCount}"/> - - <TextBlock Text=" | Exposures: "/> - - <TextBlock> - <TextBlock.Text> - <MultiBinding StringFormat="\{0\}s/\{1\}s"> - <Binding Path="SlicerFile.BottomExposureTime"/> - <Binding Path="SlicerFile.ExposureTime"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - - <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}" Text=" | Lift: "/> - <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}"> - <TextBlock.Text> - <MultiBinding StringFormat="\{0\}/\{1\}mm @ \{2\}/\{3\}mm/min"> - <Binding Path="SlicerFile.BottomLiftHeight"/> - <Binding Path="SlicerFile.LiftHeight"/> - <Binding Path="SlicerFile.BottomLiftSpeed"/> - <Binding Path="SlicerFile.LiftSpeed"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> + <TextBlock Text=" | "/> + <TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/> - <!-- - <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}" Text="| Bottom lift:"/> - <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}"> - <TextBlock.Text> - <MultiBinding StringFormat="\{0\}mm @ \{1\}mm/min"> - <Binding Path="SlicerFile.BottomLiftHeight"/> - <Binding Path="SlicerFile.BottomLiftSpeed"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - - <TextBlock IsVisible="{Binding SlicerFile.LiftHeight}" Text="| Lift:"/> - <TextBlock IsVisible="{Binding SlicerFile.LiftHeight}"> - <TextBlock.Text> - <MultiBinding StringFormat="\{0\}mm @ \{1\}mm/min"> - <Binding Path="SlicerFile.LiftHeight"/> - <Binding Path="SlicerFile.LiftSpeed"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - !--> - - <TextBlock IsVisible="{Binding SlicerFile.RetractSpeed}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.RetractSpeed}" - Text="{Binding SlicerFile.RetractSpeed, StringFormat=Retract Speed: \{0\}mm/min}"/> - - <TextBlock IsVisible="{Binding SlicerFile.LightOffDelay}" Text=" | Light-off: "/> - <TextBlock IsVisible="{Binding SlicerFile.LightOffDelay}"> - <TextBlock.Text> - <MultiBinding StringFormat="\{0\}s/\{1\}s"> - <Binding Path="SlicerFile.BottomLightOffDelay"/> - <Binding Path="SlicerFile.LightOffDelay"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> + <TextBlock Text=" | "/> + <TextBlock Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" + Text="{Binding SlicerFile.LiftRepresentation, StringFormat=Lift: {0}}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" + Text="{Binding SlicerFile.RetractRepresentation, StringFormat=Retract: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" + Text="{Binding SlicerFile.LightOffDelayRepresentation, StringFormat=Light-off: {0}}"/> <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" Text=" | "/> <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 84d074a..fd456c1 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -132,17 +132,25 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/code-branch-16x16.png")) } }, - new() + /*new() { Tag = new OperationThreshold(), Icon = new Avalonia.Controls.Image { Source = new Bitmap(App.GetAsset("/Assets/Icons/th-16x16.png")) } + },*/ + new() + { + Tag = new OperationLayerArithmetic(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png")) + } }, new() { - Tag = new OperationArithmetic(), + Tag = new OperationPixelArithmetic(), Icon = new Avalonia.Controls.Image { Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png")) @@ -792,6 +800,9 @@ namespace UVtools.WPF ClearROIAndMask(); + if(!Settings.Tools.LastUsedSettingsKeepOnCloseFile) + OperationSessionManager.Instance.Clear(); + ResetDataContext(); } @@ -1550,7 +1561,8 @@ namespace UVtools.WPF return false; }); - + OperationSessionManager.Instance.Add(baseOperation); + IsGUIEnabled = true; if (result) diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs index ff1e78e..b7fd8e2 100644 --- a/UVtools.WPF/Structures/OperationProfiles.cs +++ b/UVtools.WPF/Structures/OperationProfiles.cs @@ -24,31 +24,25 @@ namespace UVtools.WPF.Structures /// </summary> private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "operation_profiles.xml"); - [XmlElement(typeof(OperationArithmetic))] - [XmlElement(typeof(OperationBlur))] - //[XmlElement(typeof(OperationCalculator))] - [XmlElement(typeof(OperationChangeResolution))] - //[XmlElement(typeof(OperationEditParameters))] + [XmlElement(typeof(OperationResize))] [XmlElement(typeof(OperationFlip))] - //[XmlElement(typeof(OperationLayerClone))] - //[XmlElement(typeof(OperationLayerImport))] - [XmlElement(typeof(OperationDynamicLayerHeight))] - [XmlElement(typeof(OperationDynamicLifts))] - //[XmlElement(typeof(OperationLayerReHeight))] - //[XmlElement(typeof(OperationLayerRemove))] - //[XmlElement(typeof(OperationMask))] + [XmlElement(typeof(OperationRotate))] + [XmlElement(typeof(OperationSolidify))] [XmlElement(typeof(OperationMorph))] [XmlElement(typeof(OperationRaftRelief))] [XmlElement(typeof(OperationRedrawModel))] - //[XmlElement(typeof(OperationMove))] - //[XmlElement(typeof(OperationPattern))] + [XmlElement(typeof(OperationThreshold))] + [XmlElement(typeof(OperationLayerArithmetic))] + [XmlElement(typeof(OperationPixelArithmetic))] [XmlElement(typeof(OperationPixelDimming))] [XmlElement(typeof(OperationInfill))] - //[XmlElement(typeof(OperationRepairLayers))] - [XmlElement(typeof(OperationResize))] - [XmlElement(typeof(OperationRotate))] - [XmlElement(typeof(OperationThreshold))] + [XmlElement(typeof(OperationBlur))] + [XmlElement(typeof(OperationDynamicLayerHeight))] + [XmlElement(typeof(OperationDynamicLifts))] + [XmlElement(typeof(OperationChangeResolution))] + [XmlElement(typeof(OperationLayerExportGif))] + [XmlElement(typeof(OperationCalibrateExposureFinder))] [XmlElement(typeof(OperationCalibrateElephantFoot))] [XmlElement(typeof(OperationCalibrateXYZAccuracy))] diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index e0bae71..1793bcc 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.10.0</Version> + <Version>2.11.0</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -74,6 +74,9 @@ <AvaloniaResource Include="Assets\Icons\*" /> </ItemGroup> <ItemGroup> + <Compile Update="Controls\Tools\ToolLayerArithmeticControl.axaml.cs"> + <DependentUpon>ToolLayerArithmeticControl.axaml</DependentUpon> + </Compile> <Compile Update="Windows\PrusaSlicerManagerWindow.axaml.cs"> <DependentUpon>PrusaSlicerManagerWindow.axaml</DependentUpon> </Compile> diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index 2ce94f2..26fc181 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -22,7 +22,7 @@ namespace UVtools.WPF public sealed class UserSettings : BindableBase { #region Constants - public const ushort SETTINGS_VERSION = 2; + public const ushort SETTINGS_VERSION = 3; #endregion #region Sub classes @@ -1122,6 +1122,36 @@ namespace UVtools.WPF } #endregion + #region Tools + + [Serializable] + public sealed class ToolsUserSettings : BindableBase + { + private bool _restoreLastUsedSettings; + private bool _lastUsedSettingsKeepOnCloseFile = true; + private bool _lastUsedSettingsPriorityOverDefaultProfile = true; + + public bool RestoreLastUsedSettings + { + get => _restoreLastUsedSettings; + set => RaiseAndSetIfChanged(ref _restoreLastUsedSettings, value); + } + + public bool LastUsedSettingsKeepOnCloseFile + { + get => _lastUsedSettingsKeepOnCloseFile; + set => RaiseAndSetIfChanged(ref _lastUsedSettingsKeepOnCloseFile, value); + } + + public bool LastUsedSettingsPriorityOverDefaultProfile + { + get => _lastUsedSettingsPriorityOverDefaultProfile; + set => RaiseAndSetIfChanged(ref _lastUsedSettingsPriorityOverDefaultProfile, value); + } + } + + #endregion + #region Automations [Serializable] @@ -1214,6 +1244,7 @@ namespace UVtools.WPF private IssuesUserSettings _issues; private PixelEditorUserSettings _pixelEditor; private LayerRepairUserSettings _layerRepair; + private ToolsUserSettings _tools; private AutomationsUserSettings _automations; private ushort _settingsVersion = SETTINGS_VERSION; private string _appVersion; @@ -1255,6 +1286,12 @@ namespace UVtools.WPF set => _layerRepair = value; } + public ToolsUserSettings Tools + { + get => _tools ??= new ToolsUserSettings(); + set => _tools = value; + } + public AutomationsUserSettings Automations { get => _automations ??= new AutomationsUserSettings(); diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index a300e3f..c12fcb8 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -1413,7 +1413,7 @@ </ScrollViewer> </TabItem> - <TabItem Header="Automations" VerticalContentAlignment="Center"> + <TabItem Header="Tools" VerticalContentAlignment="Center"> <ScrollViewer Name="ScrollViewer5"> <StackPanel Orientation="Vertical" Spacing="5"> <Border @@ -1423,6 +1423,36 @@ <StackPanel Orientation="Vertical"> <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/> <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> + <CheckBox IsChecked="{Binding Settings.Tools.RestoreLastUsedSettings}" + Content="Keep and restore the last used settings on operations per user session/instance"/> + + <ToggleSwitch IsChecked="{Binding Settings.Tools.LastUsedSettingsKeepOnCloseFile}" + OffContent="Discard the session when closing or loading files" + OnContent="Keep the session when closing or loading files"/> + + <ToggleSwitch IsChecked="{Binding Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile}" + IsEnabled="{Binding Settings.Tools.RestoreLastUsedSettings}" + OffContent="Default profile will priority over the session" + OnContent="Session will priority over the default profile"/> + </StackPanel> + + </StackPanel> + </Border> + + </StackPanel> + </ScrollViewer> + </TabItem> + + <TabItem Header="Automations" VerticalContentAlignment="Center"> + <ScrollViewer Name="ScrollViewer6"> + <StackPanel Orientation="Vertical" Spacing="5"> + <Border + Classes="GroupBox" + Margin="5"> + + <StackPanel Orientation="Vertical"> + <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/> + <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> <CheckBox IsChecked="{Binding Settings.Automations.SaveFileAfterModifications}" Content="Auto save the file after apply any automation(s)"/> </StackPanel> @@ -1513,6 +1543,8 @@ </ScrollViewer> </TabItem> + + </TabControl> <Border Grid.Row="1" Classes="FooterActions"> diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml index 7e8f766..ff1936d 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml +++ b/UVtools.WPF/Windows/ToolWindow.axaml @@ -365,6 +365,44 @@ <Grid RowDefinitions="Auto" ColumnDefinitions="*"> <StackPanel Spacing="10" Orientation="Horizontal"> + <Button + Command="{Binding #OptionsContextMenu.Open}" + Padding="10" + Content="☰"> + <Button.ContextMenu> + <ContextMenu Name="OptionsContextMenu" PlacementMode="Top"> + <MenuItem + IsVisible="{Binding CanROI}" + Command="{Binding SelectVolumeBoundingRectangle}" + Header="Select print volume ROI"> + <MenuItem.Icon> + <Image Source="/Assets/Icons/expand-16x16.png"/> + </MenuItem.Icon> + </MenuItem> + + <Separator IsVisible="{Binding CanROI}"/> + + <MenuItem + IsVisible="{Binding CanHaveProfiles}" + Command="{Binding ImportSettings}" + Header="Import settings"> + <MenuItem.Icon> + <Image Source="/Assets/Icons/file-import-16x16.png"/> + </MenuItem.Icon> + </MenuItem> + + <MenuItem + IsVisible="{Binding CanHaveProfiles}" + Command="{Binding ExportSettings}" + Header="Export settings"> + <MenuItem.Icon> + <Image Source="/Assets/Icons/file-export-16x16.png"/> + </MenuItem.Icon> + </MenuItem> + </ContextMenu> + </Button.ContextMenu> + </Button> + <Button Padding="10" IsDefault="True" @@ -388,7 +426,8 @@ Spacing="10" HorizontalAlignment="Right" Orientation="Horizontal"> - <Button + + <Button Padding="10" IsDefault="True" IsVisible="{Binding ButtonOkVisible}" diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs index eba5086..de40575 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml.cs +++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs @@ -1,6 +1,8 @@ using System; using System.Collections.ObjectModel; using System.Drawing; +using System.IO; +using System.Xml.Serialization; using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -9,12 +11,13 @@ using Avalonia.Threading; using MessageBox.Avalonia.Enums; using UVtools.Core; using UVtools.Core.Extensions; +using UVtools.Core.Managers; using UVtools.Core.Operations; using UVtools.WPF.Controls; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; -using Point = Avalonia.Point; +using Helpers = UVtools.WPF.Controls.Helpers; namespace UVtools.WPF.Windows { @@ -285,7 +288,20 @@ namespace UVtools.WPF.Windows } } - public Rectangle ROI => App.MainWindow.ROI; + public bool CanROI => ToolControl.BaseOperation.CanROI; + + public Rectangle ROI + { + get => App.MainWindow.ROI; + set + { + App.MainWindow.ROI = App.MainWindow.GetTransposedRectangle(value); + ToolControl.BaseOperation.ROI = value; + IsROIVisible = !value.IsEmpty; + RaisePropertyChanged(); + } + } + public System.Drawing.Point[][] Masks => App.MainWindow.MaskPoints?.ToArray(); public bool ClearROIAndMaskAfterOperation @@ -299,8 +315,8 @@ namespace UVtools.WPF.Windows if (await this.MessageBoxQuestion("Are you sure you want to clear the current ROI?\n" + "This action can not be reverted, to select another ROI you must quit this window and select it on layer preview.", "Clear the current ROI?") != ButtonResult.Yes) return; - IsROIVisible = false; - App.MainWindow.ClearROI(); + + ROI = Rectangle.Empty; ToolControl?.Callback(Callbacks.ClearROI); } @@ -311,12 +327,21 @@ namespace UVtools.WPF.Windows "Clear the all masks?") != ButtonResult.Yes) return; IsMasksVisible = false; App.MainWindow.ClearMask(); + ToolControl.BaseOperation.ClearMasks(); ToolControl?.Callback(Callbacks.ClearROI); } + public void SelectVolumeBoundingRectangle() + { + ROI = SlicerFile.BoundingRectangle; + } + #endregion #region Profiles + + public bool CanHaveProfiles => ToolControl.BaseOperation.CanHaveProfiles; + public bool IsProfilesVisible { get => _isProfilesVisible; @@ -565,8 +590,42 @@ namespace UVtools.WPF.Windows _buttonOkText = toolControl.BaseOperation.ButtonOkText; _buttonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction; - if (toolControl.BaseOperation.HaveExecuted) // Come from a redo or something + bool fromSession = false; + if (!toolControl.BaseOperation.HaveExecuted && Settings.Tools.RestoreLastUsedSettings) + { + var operation = OperationSessionManager.Instance.Find(toolControl.BaseOperation.GetType()); + if (operation is not null) + { + toolControl.BaseOperation = operation.Clone(); + toolControl.BaseOperation.ClearROIandMasks(); + + switch (operation.LayerRangeSelection) + { + case Enumerations.LayerRangeSelection.None: + LayerIndexStart = operation.LayerIndexStart; + LayerIndexEnd = operation.LayerIndexEnd; + break; + default: + SelectLayers(operation.LayerRangeSelection); + break; + } + + fromSession = true; + } + } + + if (toolControl.BaseOperation.HaveExecuted) // Come from a redo or session { + if (toolControl.BaseOperation.HaveROI) + { + ROI = toolControl.BaseOperation.ROI; + } + + if (toolControl.BaseOperation.HaveMask) + { + App.MainWindow.AddMaskPoints(toolControl.BaseOperation.MaskPoints); + } + LayerIndexStart = toolControl.BaseOperation.LayerIndexStart; LayerIndexEnd = toolControl.BaseOperation.LayerIndexEnd; } @@ -575,25 +634,38 @@ namespace UVtools.WPF.Windows SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection); } - //RaisePropertyChanged(nameof(IsContentVisible)); - //RaisePropertyChanged(nameof(IsROIVisible)); - if (ToolControl.BaseOperation.CanHaveProfiles) { + _isProfilesVisible = true; var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType()); - Profiles.AddRange(profiles); - IsProfilesVisible = true; + _profiles.AddRange(profiles); - foreach (var operation in Profiles) + if (!toolControl.BaseOperation.HaveExecuted || + (toolControl.BaseOperation.HaveExecuted && fromSession && !Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile)) { - if (operation.ProfileIsDefault) + //Operation profile = _profiles.FirstOrDefault(operation => operation.ProfileIsDefault); + foreach (var operation in Profiles) { - SelectedProfileItem = operation; - break; + if (operation.ProfileIsDefault) + { + SelectedProfileItem = operation; + break; + } } } } + if (!ReferenceEquals(toolControl.BaseOperation.SlicerFile, SlicerFile)) // Sanitize + { + toolControl.BaseOperation.SlicerFile = SlicerFile; + } + + + //RaisePropertyChanged(nameof(IsContentVisible)); + //RaisePropertyChanged(nameof(IsROIVisible)); + + + // Ensure the description don't stretch window DispatcherTimer.Run(() => { @@ -729,5 +801,69 @@ namespace UVtools.WPF.Windows if (parent is null) return; menu.Open(parent); } + + public async void ExportSettings() + { + if (ToolControl.BaseOperation is null) return; + var dialog = new SaveFileDialog + { + Filters = Helpers.OperationSettingFileFilter, + InitialFileName = ToolControl.BaseOperation.Id + }; + + var file = await dialog.ShowAsync(this); + + if (string.IsNullOrWhiteSpace(file)) return; + + try + { + XmlSerializer serializer = new(ToolControl.BaseOperation.GetType()); + await using StreamWriter writer = new(file); + serializer.Serialize(writer, ToolControl.BaseOperation); + } + catch (Exception e) + { + await this.MessageBoxError(e.ToString(), "Error while trying to export the settings"); + } + } + + public async void ImportSettings() + { + var dialog = new OpenFileDialog + { + AllowMultiple = false, + Filters = Helpers.OperationSettingFileFilter + }; + + var files = await dialog.ShowAsync(this); + + if (files is null || files.Length == 0) return; + + try + { + XmlSerializer serializer = new(ToolControl.BaseOperation.GetType()); + await using var stream = File.OpenRead(files[0]); + var operation = (Operation)serializer.Deserialize(stream); + + operation.SlicerFile = SlicerFile; + ToolControl.BaseOperation = operation; + switch (operation.LayerRangeSelection) + { + case Enumerations.LayerRangeSelection.None: + LayerIndexStart = operation.LayerIndexStart; + LayerIndexEnd = operation.LayerIndexEnd; + break; + default: + SelectLayers(operation.LayerRangeSelection); + break; + } + + ToolControl.Callback(Callbacks.ProfileLoaded); + } + catch (Exception e) + { + await this.MessageBoxError(e.ToString(), "Error while trying to import the settings"); + } + } } } |