diff options
-rw-r--r-- | CHANGELOG.md | 5 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 4 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/IFileFormat.cs | 5 | ||||
-rw-r--r-- | UVtools.Core/Layer/LayerManager.cs | 10 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationLayerImport.cs | 1 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 6 | ||||
-rw-r--r-- | UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs | 18 | ||||
-rw-r--r-- | UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs | 5 | ||||
-rw-r--r-- | UVtools.GUI/FrmMain.cs | 101 | ||||
-rw-r--r-- | UVtools.GUI/Properties/AssemblyInfo.cs | 4 | ||||
-rw-r--r-- | UVtools.GUI/UVtools.GUI.csproj | 4 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml | 144 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml.cs | 83 | ||||
-rw-r--r-- | UVtools.WPF/Structures/SlicerProperty.cs | 41 |
14 files changed, 331 insertions, 100 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index a729c1f..f431d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 22/09/2020 - v0.8.2.4 + +* (Add) Layer Importer: Option to merge images +* (Improvement) Layer difference computation time, faster render + ## 19/09/2020 - v0.8.2.3 * (Add) Tooltip for next and previous layer buttons with associated shortcut (#61) diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 7c72f15..3f245f2 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -354,7 +354,9 @@ namespace UVtools.Core.FileFormats public abstract float PrintTime { get; } - + + public float PrintTimeHours => (float) Math.Round(PrintTime / 3600, 2); + public abstract float UsedMaterial { get; } public abstract float MaterialCost { get; } diff --git a/UVtools.Core/FileFormats/IFileFormat.cs b/UVtools.Core/FileFormats/IFileFormat.cs index e166b55..3b77858 100644 --- a/UVtools.Core/FileFormats/IFileFormat.cs +++ b/UVtools.Core/FileFormats/IFileFormat.cs @@ -186,6 +186,11 @@ namespace UVtools.Core.FileFormats float PrintTime { get; } /// <summary> + /// Gets the estimate print time in hours + /// </summary> + float PrintTimeHours { get; } + + /// <summary> /// Gets the estimate used material in ml /// </summary> float UsedMaterial { get; } diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index ad3e149..183a333 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -1343,6 +1343,16 @@ namespace UVtools.Core { var mat = CvInvoke.Imread(operation.Files[i], ImreadModes.Grayscale); uint layerIndex = (uint) (startIndex + i); + if (operation.MergeImages) + { + if (!(this[layerIndex] is null)) + { + using (var oldMat = this[layerIndex].LayerMat) + { + CvInvoke.Add(oldMat, mat, mat); + } + } + } this[layerIndex] = new Layer(layerIndex, mat); lock (progress.Mutex) diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index 89e3159..b7c8502 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -80,6 +80,7 @@ namespace UVtools.Core.Operations public bool ReplaceStartLayer { get; set; } public bool ReplaceSubsequentLayers { get; set; } public bool DiscardRemainingLayers { get; set; } + public bool MergeImages { get; set; } public List<string> Files { get; } = new List<string>(); diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 9d1d0f1..df16c45 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,12 +10,12 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> - <Version>0.8.2.3</Version> + <Version>0.8.2.4</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> - <AssemblyVersion>0.8.2.3</AssemblyVersion> - <FileVersion>0.8.2.3</FileVersion> + <AssemblyVersion>0.8.2.4</AssemblyVersion> + <FileVersion>0.8.2.4</FileVersion> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs index 66a17c1..371dff4 100644 --- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs +++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs @@ -48,6 +48,7 @@ this.pbSelectedImage = new System.Windows.Forms.PictureBox(); this.cbReplaceSubsequentLayers = new System.Windows.Forms.CheckBox(); this.cbDiscardRemainingLayers = new System.Windows.Forms.CheckBox(); + this.cbMergeImages = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.nmInsertAfterLayer)).BeginInit(); this.tsBar.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); @@ -276,11 +277,25 @@ this.cbDiscardRemainingLayers.UseVisualStyleBackColor = true; this.cbDiscardRemainingLayers.CheckedChanged += new System.EventHandler(this.EventClick); // + // cbMergeImages + // + this.cbMergeImages.AutoSize = true; + this.cbMergeImages.Enabled = false; + this.cbMergeImages.Location = new System.Drawing.Point(244, 83); + this.cbMergeImages.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cbMergeImages.Name = "cbMergeImages"; + this.cbMergeImages.Size = new System.Drawing.Size(128, 24); + this.cbMergeImages.TabIndex = 41; + this.cbMergeImages.Text = "Merge images"; + this.toolTip.SetToolTip(this.cbMergeImages, "Merge source layer with target layer\r\n(Require \"Replace subsequent layers\")"); + this.cbMergeImages.UseVisualStyleBackColor = true; + this.cbMergeImages.CheckedChanged += new System.EventHandler(this.EventClick); + // // CtrlToolLayerImport // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.Controls.Add(this.cbMergeImages); this.Controls.Add(this.cbDiscardRemainingLayers); this.Controls.Add(this.cbReplaceSubsequentLayers); this.Controls.Add(this.lbResult); @@ -330,5 +345,6 @@ private System.Windows.Forms.PictureBox pbSelectedImage; private System.Windows.Forms.CheckBox cbReplaceSubsequentLayers; private System.Windows.Forms.CheckBox cbDiscardRemainingLayers; + private System.Windows.Forms.CheckBox cbMergeImages; } } diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs index 41b591a..53090af 100644 --- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs +++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs @@ -40,6 +40,7 @@ namespace UVtools.GUI.Controls.Tools Operation.ReplaceStartLayer = cbReplaceStartLayer.Checked; Operation.ReplaceSubsequentLayers = cbReplaceSubsequentLayers.Checked; Operation.DiscardRemainingLayers = cbDiscardRemainingLayers.Checked; + Operation.MergeImages = cbMergeImages.Checked; return true; } @@ -79,10 +80,12 @@ namespace UVtools.GUI.Controls.Tools { if (ReferenceEquals(sender, cbReplaceSubsequentLayers)) { - cbDiscardRemainingLayers.Enabled = cbReplaceSubsequentLayers.Checked; + cbDiscardRemainingLayers.Enabled = cbDiscardRemainingLayers.Enabled = cbReplaceSubsequentLayers.Checked; + cbMergeImages.Enabled = cbReplaceSubsequentLayers.Checked; if (!cbReplaceSubsequentLayers.Checked) { cbDiscardRemainingLayers.Checked = false; + cbMergeImages.Checked = false; } } diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs index 12867f6..ac3fe8c 100644 --- a/UVtools.GUI/FrmMain.cs +++ b/UVtools.GUI/FrmMain.cs @@ -2275,7 +2275,7 @@ namespace UVtools.GUI /// Shows a layer number /// </summary> /// <param name="layerNum">Layer number</param> - void ShowLayer(uint layerNum) + unsafe void ShowLayer(uint layerNum) { if (SlicerFile is null) return; @@ -2316,8 +2316,10 @@ namespace UVtools.GUI CvInvoke.CvtColor(ActualLayerImage, ActualLayerImageBgr, ColorConversion.Gray2Bgr); - var imageSpan = ActualLayerImage.GetPixelSpan<byte>(); - var imageBgrSpan = ActualLayerImageBgr.GetPixelSpan<byte>(); + //var imageSpan = ActualLayerImage.GetPixelSpan<byte>(); + //var imageBgrSpan = ActualLayerImageBgr.GetPixelSpan<byte>(); + var imageSpan = ActualLayerImage.GetBytePointer(); + var imageBgrSpan = ActualLayerImageBgr.GetBytePointer(); if (btnLayerImageLayerOutlineEdgeDetection.Checked) @@ -2332,71 +2334,30 @@ namespace UVtools.GUI { if (layerNum > 0 && layerNum < SlicerFile.LayerCount - 1) { - using (var previousImage = SlicerFile[layerNum - 1].LayerMat) - using (var nextImage = SlicerFile[layerNum + 1].LayerMat) + Mat previousImage = null; + Mat nextImage = null; + + // Can improve performance on >4K images? + Parallel.Invoke( + () => { previousImage = SlicerFile[ActualLayer - 1].LayerMat; }, + () => { nextImage = SlicerFile[ActualLayer + 1].LayerMat; }); + + /*using (var previousImage = SlicerFile[_actualLayer - 1].LayerMat) + using (var nextImage = SlicerFile[_actualLayer + 1].LayerMat) + {*/ + //var previousSpan = previousImage.GetPixelSpan<byte>(); + //var nextSpan = nextImage.GetPixelSpan<byte>(); + + var previousSpan = previousImage.GetBytePointer(); + var nextSpan = nextImage.GetBytePointer(); + + int width = ActualLayerImage.Width; + int channels = ActualLayerImageBgr.NumberOfChannels; + Parallel.For(0, ActualLayerImageBgr.Height, y => { - var previousSpan = previousImage.GetPixelSpan<byte>(); - var nextSpan = nextImage.GetPixelSpan<byte>(); - - /*Parallel.For(0, imageSpan.Length, i => - { - var currentByte = ActualLayerImage.GetSinglePixelSpan<byte>(i); - var previousByte = previousImage.GetSinglePixelSpan<byte>(i); - var nextByte = nextImage.GetSinglePixelSpan<byte>(i); - - if (currentByte[0] != 0) return; - Color color = Color.Empty; - if (previousByte[0] > 0 && nextByte[0] > 0) - { - color = Settings.Default.PreviousNextLayerColor; - } - else if (previousByte[0] > 0) - { - color = Settings.Default.PreviousLayerColor; - } - else if (nextByte[0] > 0) - { - color = Settings.Default.NextLayerColor; - } - - if (color.IsEmpty) return; - ActualLayerImageBgr.SetByte(i * 3, new[]{ color.B , color.G, color.R }); - });*/ - - /*Parallel.For(0, ActualLayerImage.Height, y => - { - var currentSpan = ActualLayerImage.GetPixelRowSpan<byte>(y); - var currentRGBSpan = ActualLayerImageBgr.GetPixelRowSpan<byte>(y); - var previousSpan = previousImage.GetPixelRowSpan<byte>(y); - var nextSpan = nextImage.GetPixelRowSpan<byte>(y); - - for (int x = 0; x < currentSpan.Length; x++) - { - if (currentSpan[x] != 0) continue; - Color color = Color.Empty; - if (previousSpan[x] > 0 && nextSpan[x] > 0) - { - color = Settings.Default.PreviousNextLayerColor; - } - else if (previousSpan[x] > 0) - { - color = Settings.Default.PreviousLayerColor; - } - else if (nextSpan[x] > 0) - { - color = Settings.Default.NextLayerColor; - } - - if (color.IsEmpty) continue; - var bgrPixel = x * 3; - currentRGBSpan[bgrPixel] = color.B; // B - currentRGBSpan[++bgrPixel] = color.G; // G - currentRGBSpan[++bgrPixel] = color.R; // R - } - });*/ - - for (int pixel = 0; pixel < imageSpan.Length; pixel++) + for (int x = 0; x < width; x++) { + int pixel = y * width + x; if (imageSpan[pixel] != 0) continue; Color color = Color.Empty; if (previousSpan[pixel] > 0 && nextSpan[pixel] > 0) @@ -2413,12 +2374,16 @@ namespace UVtools.GUI } if (color.IsEmpty) continue; - var bgrPixel = pixel * 3; + var bgrPixel = pixel * channels; imageBgrSpan[bgrPixel] = color.B; // B imageBgrSpan[++bgrPixel] = color.G; // G imageBgrSpan[++bgrPixel] = color.R; // R + //imageBgrSpan[++bgrPixel] = color.A; // A } - } + }); + + previousImage.Dispose(); + nextImage.Dispose(); } } diff --git a/UVtools.GUI/Properties/AssemblyInfo.cs b/UVtools.GUI/Properties/AssemblyInfo.cs index e011fe6..dd226f7 100644 --- a/UVtools.GUI/Properties/AssemblyInfo.cs +++ b/UVtools.GUI/Properties/AssemblyInfo.cs @@ -35,5 +35,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.2.3")] -[assembly: AssemblyFileVersion("0.8.2.3")] +[assembly: AssemblyVersion("0.8.2.4")] +[assembly: AssemblyFileVersion("0.8.2.4")] diff --git a/UVtools.GUI/UVtools.GUI.csproj b/UVtools.GUI/UVtools.GUI.csproj index b1269db..4b2b1c5 100644 --- a/UVtools.GUI/UVtools.GUI.csproj +++ b/UVtools.GUI/UVtools.GUI.csproj @@ -41,6 +41,7 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> @@ -51,6 +52,7 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup> <ApplicationIcon>UVtools.ico</ApplicationIcon> @@ -67,6 +69,7 @@ <LangVersion>7.3</LangVersion> <ErrorReport>prompt</ErrorReport> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <OutputPath>bin\x64\Release\</OutputPath> @@ -77,6 +80,7 @@ <LangVersion>7.3</LangVersion> <ErrorReport>prompt</ErrorReport> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup> <SignAssembly>false</SignAssembly> diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 76f60c1..b18591d 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -108,35 +108,135 @@ </MenuItem> </Menu> - <Menu DockPanel.Dock="Bottom" - IsEnabled="{Binding IsFileLoaded}" - > - <MenuItem Header="_File"> - <MenuItem Header="_Open..."/> - <Separator/> - <MenuItem Header="_Exit"/> - </MenuItem> - <MenuItem Header="_Edit"> - <MenuItem Header="Copy"/> - <MenuItem Header="Paste"/> - </MenuItem> - </Menu> + <Border Padding="5" DockPanel.Dock="Bottom" Background="WhiteSmoke" IsVisible="{Binding IsFileLoaded}"> + <StackPanel + VerticalAlignment="Center" + Orientation="Horizontal" + Spacing="5" + > + <TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/> + <TextBlock Text="|"/> + + <TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: \{0\}}"/> + <TextBlock Text="|"/> + + <TextBlock Text="{Binding SlicerFile.BottomExposureTime, StringFormat=Bottom exposure: \{0\}s}"/> + <TextBlock Text="|"/> + + <TextBlock Text="{Binding SlicerFile.ExposureTime, StringFormat=Exposure: \{0\}s}"/> + + <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" + Text="|"/> + <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" + Text="{Binding SlicerFile.PrintTimeHours, StringFormat=Print time: \{0\}h}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.UsedMaterial}" + Text="|"/> + <TextBlock IsVisible="{Binding SlicerFile.UsedMaterial}" + Text="{Binding SlicerFile.UsedMaterial, StringFormat=Used material: \{0\}ml}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" + Text="|"/> + <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" + Text="{Binding SlicerFile.MaterialCost, StringFormat=Material cost: \{0\}ml}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="|"/> + <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="{Binding SlicerFile.MaterialName, StringFormat=Material: \{0\}}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="|"/> + <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="{Binding SlicerFile.MachineName, StringFormat=Machine: \{0\}}"/> + </StackPanel> + </Border> <TabControl - IsEnabled="{Binding IsFileLoaded}" DockPanel.Dock="Left" Width="400" - SelectedIndex="3"> - <TabItem> + SelectedIndex="{Binding TabSelectedIndex}"> + <TabItem IsEnabled="{Binding IsFileLoaded}" > <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/button-info-16x16.png" Width="16"/> <TextBlock Margin="5,0,0,0">Information</TextBlock> </StackPanel> </TabItem.Header> + + <Grid IsVisible="{Binding IsFileLoadeds}" RowDefinitions="Auto,Auto,Auto,*"> + <StackPanel Background="WhiteSmoke" + Grid.Row="0" + Orientation="Horizontal" + Spacing="5" + VerticalAlignment="Center"> + <Button + IsEnabled="{Binding ThumbnailCanGoPrevious}" + Command="{Binding ThumbnailGoPrevious}" + > + <Image Source="/Assets/Icons/back-16x16.png" Width="16"/> + </Button> + + <TextBlock VerticalAlignment="Center"> + <TextBlock.Text> + <MultiBinding StringFormat="\{0\}/\{1\}"> + <Binding Path="VisibleThumbnailIndex"/> + <Binding Path="SlicerFile.CreatedThumbnailsCount"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> + + <Button + IsEnabled="{Binding ThumbnailCanGoNext}" + Command="{Binding ThumbnailGoNext}" + > + <Image Source="/Assets/Icons/next-16x16.png" Width="16"/> + </Button> + + </StackPanel> + + <StackPanel + Grid.Row="0" + Orientation="Horizontal" + Spacing="5" + HorizontalAlignment="Right" + VerticalAlignment="Center"> + <TextBlock VerticalAlignment="Center" + Text="{Binding VisibleThumbnailResolution}"/> + </StackPanel> + + <Image Grid.Row="1" Source="{Binding VisibleThumbnailImage}"/> + + <DataGrid Grid.Row="3" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="All" + IsReadOnly="True" + ClipboardCopyMode="IncludeHeader" + Items="{Binding SlicerProperties}"> + <DataGrid.Columns> + <DataGridTextColumn Header="Name" + Binding="{Binding Name}" + Width="Auto" /> + <DataGridTextColumn Header="Value" + Binding="{Binding Value}" + Width="Auto" /> + <DataGridTextColumn Header="Group" + Binding="{Binding Group}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + </Grid> + </TabItem> - <TabItem IsVisible="{Binding SlicerFile.HaveGCode}"> + <TabItem IsVisible="{Binding HaveGCode}"> <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/code-16x16.png" Width="16"/> @@ -146,7 +246,7 @@ </TabItem> - <TabItem> + <TabItem IsEnabled="{Binding IsFileLoaded}" > <TabItem.Header> <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> <Image Source="/Assets/Icons/warning-16x16.png" Width="16"/> @@ -275,7 +375,6 @@ Name="Layer.Navigation.Slider" Minimum="0" Maximum="{Binding SliderMaximumValue}" - Ticks="{Binding SliderMaximumValue}" Value="{Binding ActualLayer}" TickFrequency="1" TickPlacement="Outside" @@ -357,7 +456,7 @@ <StackPanel HorizontalAlignment="Left" Grid.Row="0" Orientation="Horizontal" Spacing="1"> <ToggleButton IsChecked="{Binding ShowLayerImageRotated}" - HotKey="Ctrl + R" + HotKey="Ctrl+R" ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]" > <StackPanel Orientation="Horizontal"> @@ -397,10 +496,11 @@ </ToggleButton> <Button - ToolTip.Tip="Right click to access the various outlines." + ToolTip.Tip="Click to access the various outlines." + Command="{Binding #LayerPreviewOutlineContextMenu.Open}" > <Button.ContextMenu> - <ContextMenu PlacementMode="Bottom"> + <ContextMenu Name="LayerPreviewOutlineContextMenu" PlacementMode="Bottom"> <CheckBox IsChecked="{Binding ShowLayerOutlinePrintVolumeBoundary}" Content="Print volume boundary"/> diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 39c9101..88a0895 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -19,6 +19,7 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; +using Avalonia.Media.Imaging; using Avalonia.Threading; using Emgu.CV; using Emgu.CV.CvEnum; @@ -102,6 +103,7 @@ namespace UVtools.WPF public Border LayerNavigationTooltipBorder; #region DataSets + public ObservableCollection<SlicerProperty> SlicerProperties { get; } = new ObservableCollection<SlicerProperty>(); public ObservableCollection<LogItem> Logs { get; } = new ObservableCollection<LogItem>(); public ObservableCollection<LayerIssue> Issues { get; } = new ObservableCollection<LayerIssue>(); public ObservableCollection<PixelOperation> Drawings { get; } = new ObservableCollection<PixelOperation>(); @@ -113,6 +115,9 @@ namespace UVtools.WPF private uint _actualLayer; private bool _isGUIEnabled = true; private bool _canSave; + private int _tabSelectedIndex; + private uint _visibleThumbnailIndex; + private Bitmap _visibleThumbnailImage; private bool _isVerbose; private bool _showLayerImageRotated; private bool _showLayerImageDifference; @@ -135,7 +140,7 @@ namespace UVtools.WPF { if (!SetProperty(ref _isGUIEnabled, value)) return; if (!_isGUIEnabled) return; - if (ProgressWindow.IsActive) + if (!(ProgressWindow is null)) { Dispatcher.UIThread.InvokeAsync(() => { @@ -151,16 +156,73 @@ namespace UVtools.WPF public bool IsFileLoaded { - get => !ReferenceEquals(SlicerFile, null); + get => !(SlicerFile is null); set => OnPropertyChanged(); } + public bool HaveGCode => IsFileLoaded && SlicerFile.HaveGCode; + public bool CanSave { get => _canSave; set => SetProperty(ref _canSave, value); } + public int TabSelectedIndex + { + get => _tabSelectedIndex; + set => SetProperty(ref _tabSelectedIndex, value); + } + + public uint VisibleThumbnailIndex + { + get => _visibleThumbnailIndex; + set + { + SetProperty(ref _visibleThumbnailIndex, value); + OnPropertyChanged(nameof(ThumbnailCanGoPrevious)); + OnPropertyChanged(nameof(ThumbnailCanGoNext)); + if (value == 0) + { + VisibleThumbnailImage = null; + return; + } + + if (SlicerFile is null) return; + if (_visibleThumbnailIndex > SlicerFile.Thumbnails.Length) return; + VisibleThumbnailImage = SlicerFile.Thumbnails[_visibleThumbnailIndex-1].ToBitmap(); + } + } + + public bool ThumbnailCanGoPrevious => SlicerFile is { } && _visibleThumbnailIndex > 1; + public bool ThumbnailCanGoNext => SlicerFile is { } && _visibleThumbnailIndex < SlicerFile.Thumbnails.Length; + + public void ThumbnailGoPrevious() + { + if (!ThumbnailCanGoPrevious) return; + VisibleThumbnailIndex--; + } + + public void ThumbnailGoNext() + { + if (!ThumbnailCanGoNext) return; + VisibleThumbnailIndex++; + } + + public Bitmap VisibleThumbnailImage + { + get => _visibleThumbnailImage; + set + { + SetProperty(ref _visibleThumbnailImage, value); + OnPropertyChanged(nameof(VisibleThumbnailResolution)); + } + } + + public string VisibleThumbnailResolution => _visibleThumbnailImage is null ? null : $"{{Width: {_visibleThumbnailImage.Size.Width}, Height: {_visibleThumbnailImage.Size.Height}}}"; + + + public bool IsVerbose { get => _isVerbose; @@ -433,7 +495,9 @@ namespace UVtools.WPF SlicerFile?.Dispose(); App.SlicerFile = null; LayerCache.Clear(); + VisibleThumbnailIndex = 0; LayerImageBox.Image = null; + SlicerProperties.Clear(); ResetDataContext(); } @@ -512,6 +576,21 @@ namespace UVtools.WPF LayerImageBox.Image = bitmapRgb;*/ _actualLayer = actualLayer; + if(SlicerFile.CreatedThumbnailsCount > 0) VisibleThumbnailIndex = 1; + + if (!(SlicerFile.Configs is null)) + { + foreach (var config in SlicerFile.Configs) + { + var type = config.GetType(); + foreach (var property in type.GetProperties()) + { + SlicerProperties.Add(new SlicerProperty(property.Name, property.GetValue(config, null)?.ToString(), type.Name)); + } + } + } + + ShowLayer(); ResetDataContext(); diff --git a/UVtools.WPF/Structures/SlicerProperty.cs b/UVtools.WPF/Structures/SlicerProperty.cs new file mode 100644 index 0000000..94cdc82 --- /dev/null +++ b/UVtools.WPF/Structures/SlicerProperty.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using Avalonia; +using ReactiveUI; + +namespace UVtools.WPF.Structures +{ + public class SlicerProperty : ReactiveObject + { + private string _name; + private string _value; + private string _group; + + public string Name + { + get => _name; + set => this.RaiseAndSetIfChanged(ref _name, value); + } + + public string Value + { + get => _value; + set => this.RaiseAndSetIfChanged(ref _value, value); + } + + public string Group + { + get => _group; + set => this.RaiseAndSetIfChanged(ref _group, value); + } + + public SlicerProperty(string name, string value, string group = null) + { + _name = name; + _value = value; + _group = group; + } + } +} |