WPF Progress

This commit is contained in:
Tiago Conceição
2020-10-03 01:09:18 +01:00
parent 47263dc271
commit caed241b93
17 changed files with 678 additions and 144 deletions
+4
View File
@@ -9,6 +9,10 @@
* (Improvement) GUI is now scalable
* (Fix) Some bug found and fixed during convertion
## 01/10/2020 - v0.8.4.1
* (Fix) Tool - Pattern: Unable to use an anchor
## 01/10/2020 - v0.8.4.0
* (Add) Tool: Arithmetic operations
+1 -1
View File
@@ -1753,7 +1753,7 @@ namespace UVtools.Core
progress.Token.ThrowIfCancellationRequested();
if (operation.Anchor == Enumerations.Anchor.None) return;
var operationMove = new OperationMove(BoundingRectangle, 0, 0, operation.Anchor)
var operationMove = new OperationMove(BoundingRectangle, operation.ImageWidth, operation.ImageHeight, operation.Anchor)
{
LayerIndexStart = operation.LayerIndexStart, LayerIndexEnd = operation.LayerIndexEnd
};
+32 -20
View File
@@ -13,6 +13,9 @@ namespace UVtools.Core.Operations
{
public sealed class OperationBlur : Operation
{
private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
private uint _size = 1;
#region Overrides
public override string Title => "Blur";
@@ -55,26 +58,6 @@ namespace UVtools.Core.Operations
#endregion
#region Properties
public static StringTag[] BlurTypes => new[]
{
new StringTag("Blur: Normalized box filter", BlurAlgorithm.Blur),
new StringTag("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition", BlurAlgorithm.Pyramid),
new StringTag("Median Blur: Each pixel becomes the median of its surrounding pixels", BlurAlgorithm.MedianBlur),
new StringTag("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood", BlurAlgorithm.GaussianBlur),
new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D),
};
public BlurAlgorithm BlurOperation { get; set; } = BlurAlgorithm.Blur;
public uint Size { get; set; } = 1;
public Kernel Kernel { get; set; } = new Kernel();
#endregion
#region Enums
public enum BlurAlgorithm
{
@@ -85,5 +68,34 @@ namespace UVtools.Core.Operations
Filter2D
}
#endregion
#region Properties
public static StringTag[] BlurTypes => new[]
{
new StringTag("Blur: Normalized box filter", BlurAlgorithm.Blur),
new StringTag("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition", BlurAlgorithm.Pyramid),
new StringTag("Median Blur: Each pixel becomes the median of its surrounding pixels", BlurAlgorithm.MedianBlur),
new StringTag("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood", BlurAlgorithm.GaussianBlur),
new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D),
};
public BlurAlgorithm BlurOperation
{
get => _blurOperation;
set => RaiseAndSetIfChanged(ref _blurOperation, value);
}
public uint Size
{
get => _size;
set => RaiseAndSetIfChanged(ref _size, value);
}
public Kernel Kernel { get; set; } = new Kernel();
#endregion
}
}
+1 -1
View File
@@ -205,7 +205,7 @@ namespace UVtools.Core.Operations
{
}
public OperationMove(Rectangle srcRoi, uint imageWidth = 0, uint imageHeight = 0, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter)
public OperationMove(Rectangle srcRoi, uint imageWidth, uint imageHeight, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter)
{
ROI = srcRoi;
ImageWidth = imageWidth;
+143 -64
View File
@@ -13,6 +13,17 @@ namespace UVtools.Core.Operations
{
public class OperationPattern : Operation
{
private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
private ushort _colSpacing;
private ushort _rowSpacing;
private ushort _maxColSpacing;
private ushort _maxRowSpacing;
private ushort _cols = 1;
private ushort _rows = 1;
private ushort _maxCols;
private ushort _maxRows;
private bool _isWithinBoundary = true;
#region Overrides
public override string Title => "Pattern";
@@ -45,24 +56,121 @@ namespace UVtools.Core.Operations
#endregion
public Enumerations.Anchor Anchor { get; set; }
public Enumerations.Anchor Anchor
{
get => _anchor;
set => RaiseAndSetIfChanged(ref _anchor, value);
}
public uint ImageWidth { get; }
public uint ImageHeight { get; }
public ushort MarginCol { get; set; }
public ushort MarginRow { get; set; }
public ushort Cols
{
get => _cols;
set
{
if (!RaiseAndSetIfChanged(ref _cols, value)) return;
RaisePropertyChanged(nameof(InfoCols));
RaisePropertyChanged(nameof(InfoWidthStr));
RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
ValidateBounds();
}
}
public ushort MaxMarginCol { get; set; }
public ushort MaxMarginRow { get; set; }
public ushort Rows
{
get => _rows;
set
{
if (!RaiseAndSetIfChanged(ref _rows, value)) return;
RaisePropertyChanged(nameof(InfoRows));
RaisePropertyChanged(nameof(InfoHeightStr));
RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
ValidateBounds();
}
}
public ushort Cols { get; set; } = 1;
public ushort Rows { get; set; } = 1;
public ushort MaxCols
{
get => _maxCols;
set
{
if(!RaiseAndSetIfChanged(ref _maxCols, value)) return;
RaisePropertyChanged(nameof(InfoCols));
ValidateBounds();
}
}
public ushort MaxCols { get; set; }
public ushort MaxRows { get; set; }
public ushort MaxRows
{
get => _maxRows;
set
{
if (!RaiseAndSetIfChanged(ref _maxRows, value)) return;
RaisePropertyChanged(nameof(InfoRows));
ValidateBounds();
}
}
public Size GetPatternVolume => new Size(Cols * ROI.Width + (Cols - 1) * MarginCol, Rows * ROI.Height + (Rows - 1) * MarginRow);
public ushort ColSpacing
{
get => _colSpacing;
set
{
if(!RaiseAndSetIfChanged(ref _colSpacing, value)) return;
RaisePropertyChanged(nameof(InfoWidthStr));
RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
ValidateBounds();
}
}
public ushort RowSpacing
{
get => _rowSpacing;
set
{
if (!RaiseAndSetIfChanged(ref _rowSpacing, value)) return;
RaisePropertyChanged(nameof(InfoHeightStr));
RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
ValidateBounds();
}
}
public ushort MaxColSpacing
{
get => _maxColSpacing;
set => RaiseAndSetIfChanged(ref _maxColSpacing, value);
}
public ushort MaxRowSpacing
{
get => _maxRowSpacing;
set => RaiseAndSetIfChanged(ref _maxRowSpacing, value);
}
public string InfoCols => $"Columns: {Cols} / {MaxCols}";
public string InfoRows => $"Rows: {Rows} / {MaxRows}";
public string InfoWidthStr =>
$"Width: {GetPatternVolume.Width} (Min: {ROI.Width}, Max: {ImageWidth})";
public string InfoHeightStr =>
$"Width: {GetPatternVolume.Height} (Min: {ROI.Height}, Max: {ImageHeight})";
public bool IsWithinBoundary
{
get => _isWithinBoundary;
set
{
if (!RaiseAndSetIfChanged(ref _isWithinBoundary, value)) return;
RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
}
}
public string InfoModelWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
public Size GetPatternVolume => new Size(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing);
public OperationPattern()
{
@@ -74,6 +182,12 @@ namespace UVtools.Core.Operations
ImageHeight = (uint) resolution.Height;
SetRoi(srcRoi);
Fill();
}
public void SetAnchor(byte value)
{
Anchor = (Enumerations.Anchor)value;
}
public void SetRoi(Rectangle srcRoi)
@@ -83,73 +197,29 @@ namespace UVtools.Core.Operations
MaxCols = (ushort)(ImageWidth / srcRoi.Width);
MaxRows = (ushort)(ImageHeight / srcRoi.Height);
MaxMarginCol = CalculateMarginCol(MaxCols);
MaxMarginRow = CalculateMarginRow(MaxRows);
MaxColSpacing = CalculateAutoColSpacing(MaxCols);
MaxRowSpacing = CalculateAutoRowSpacing(MaxRows);
}
/*public void CalculateDstRoi()
{
_dstRoi.Size = SrcRoi.Size;
switch (Anchor)
{
case Anchor.TopLeft:
_dstRoi.Location = new Point(0, 0);
break;
case Anchor.TopCenter:
_dstRoi.Location = new Point((int)(ImageWidth / 2 - SrcRoi.Width / 2), 0);
break;
case Anchor.TopRight:
_dstRoi.Location = new Point((int)(ImageWidth - SrcRoi.Width), 0);
break;
case Anchor.MiddleLeft:
_dstRoi.Location = new Point(0, (int)(ImageHeight / 2 - SrcRoi.Height / 2));
break;
case Anchor.MiddleCenter:
_dstRoi.Location = new Point((int)(ImageWidth / 2 - SrcRoi.Width / 2), (int)(ImageHeight / 2 - SrcRoi.Height / 2));
break;
case Anchor.MiddleRight:
_dstRoi.Location = new Point((int)(ImageWidth - SrcRoi.Width), (int)(ImageHeight / 2 - SrcRoi.Height / 2));
break;
case Anchor.BottomLeft:
_dstRoi.Location = new Point(0, (int)(ImageHeight - SrcRoi.Height));
break;
case Anchor.BottomCenter:
_dstRoi.Location = new Point((int)(ImageWidth / 2 - SrcRoi.Width / 2), (int)(ImageHeight - SrcRoi.Height));
break;
case Anchor.BottomRight:
_dstRoi.Location = new Point((int)(ImageWidth - SrcRoi.Width), (int)(ImageHeight - SrcRoi.Height));
break;
default:
throw new ArgumentOutOfRangeException();
}
_dstRoi.X += MarginLeft;
_dstRoi.X -= MarginRight;
_dstRoi.Y += MarginTop;
_dstRoi.Y -= MarginBottom;
}*/
/// <summary>
/// Fills the plate with maximum cols and rows
/// </summary>
public void Fill()
{
Cols = MaxCols;
MarginCol = MaxMarginCol;
ColSpacing = MaxColSpacing;
Rows = MaxRows;
MarginRow = MaxMarginRow;
RowSpacing = MaxRowSpacing;
}
public ushort CalculateMarginCol(ushort cols)
public ushort CalculateAutoColSpacing(ushort cols)
{
if (cols <= 1) return 0;
return (ushort)((ImageWidth - ROI.Width * cols) / cols);
}
public ushort CalculateMarginRow(ushort rows)
public ushort CalculateAutoRowSpacing(ushort rows)
{
if (rows <= 1) return 0;
return (ushort)((ImageHeight - ROI.Height * rows) / rows);
@@ -160,15 +230,24 @@ namespace UVtools.Core.Operations
var patternVolume = GetPatternVolume;
return new Rectangle(new Point(
(int) (col * ROI.Width + col * MarginCol + (ImageWidth - patternVolume.Width) / 2),
(int) (row * ROI.Height + row * MarginRow + (ImageHeight - patternVolume.Height) / 2)), ROI.Size);
(int) (col * ROI.Width + col * ColSpacing + (ImageWidth - patternVolume.Width) / 2),
(int) (row * ROI.Height + row * RowSpacing + (ImageHeight - patternVolume.Height) / 2)), ROI.Size);
}
public void FillColumnSpacing()
{
ColSpacing = CalculateAutoColSpacing(_cols);
}
public void FillRowSpacing()
{
RowSpacing = CalculateAutoRowSpacing(_rows);
}
public bool ValidateBounds()
{
var volume = GetPatternVolume;
if (volume.Width > ImageWidth || volume.Height > ImageHeight) return false;
return true;
return IsWithinBoundary = volume.Width <= ImageWidth && volume.Height <= ImageHeight;
}
}
}
@@ -46,8 +46,8 @@ namespace UVtools.GUI.Controls.Tools
nmCols.Maximum = Operation.MaxCols;
nmRows.Maximum = Operation.MaxRows;
nmMarginCol.Value = Operation.MaxMarginCol;
nmMarginRow.Value = Operation.MaxMarginRow;
nmMarginCol.Value = Operation.MaxColSpacing;
nmMarginRow.Value = Operation.MaxRowSpacing;
nmCols.Value = Operation.MaxCols;
nmRows.Value = Operation.MaxRows;
Operation.Fill();
@@ -86,8 +86,8 @@ namespace UVtools.GUI.Controls.Tools
i++;
}
Operation.MarginCol = (ushort)nmMarginCol.Value;
Operation.MarginRow = (ushort)nmMarginRow.Value;
Operation.ColSpacing = (ushort)nmMarginCol.Value;
Operation.RowSpacing = (ushort)nmMarginRow.Value;
Operation.Cols = (ushort)nmCols.Value;
Operation.Rows = (ushort)nmRows.Value;
@@ -102,8 +102,8 @@ namespace UVtools.GUI.Controls.Tools
ButtonOkEnabled = insideBounds && (Operation.Cols > 1 || Operation.Rows > 1);
lbInsideBounds.Text = "Model within boundary: " + (insideBounds ? "Yes" : "No");
lbVolumeWidth.Text = $"Width: {Operation.GetPatternVolume.Width} (Min:{Operation.ROI.Width}, Max:{Operation.ImageWidth})";
lbVolumeHeight.Text = $"Height: {Operation.GetPatternVolume.Height} (Min:{Operation.ROI.Height}, Max:{Operation.ImageHeight})";
lbVolumeWidth.Text = $"Width: {Operation.GetPatternVolume.Width} (Min: {Operation.ROI.Width}, Max: {Operation.ImageWidth})";
lbVolumeHeight.Text = $"Height: {Operation.GetPatternVolume.Height} (Min: {Operation.ROI.Height}, Max: {Operation.ImageHeight})";
lbCols.Text = $"Columns: {nmCols.Value} / {Operation.MaxCols}";
lbRows.Text = $"Rows: {nmRows.Value} / {Operation.MaxRows}";
@@ -114,14 +114,14 @@ namespace UVtools.GUI.Controls.Tools
if (ReferenceEquals(sender, btnAutoMarginCol))
{
UpdateOperation();
nmMarginCol.Value = Operation.CalculateMarginCol((ushort)nmCols.Value).Clamp(0, (ushort) nmMarginCol.Maximum);
nmMarginCol.Value = Operation.CalculateAutoColSpacing((ushort)nmCols.Value).Clamp(0, (ushort) nmMarginCol.Maximum);
return;
}
if (ReferenceEquals(sender, btnAutoMarginRow))
{
UpdateOperation();
nmMarginRow.Value = Operation.CalculateMarginRow((ushort)nmRows.Value).Clamp(0, (ushort) nmMarginRow.Maximum);
nmMarginRow.Value = Operation.CalculateAutoRowSpacing((ushort)nmRows.Value).Clamp(0, (ushort) nmMarginRow.Maximum);
return;
}
}
+66 -41
View File
@@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Timers;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@@ -13,6 +14,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Skia;
using Avalonia.Styling;
using Avalonia.Threading;
using UVtools.Core.Extensions;
using UVtools.WPF.Extensions;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
@@ -586,7 +588,19 @@ namespace UVtools.WPF.Controls
set => RaiseAndSetIfChanged(ref _zoomLevels, value);
}
private int _oldZoom = 100;
private int _zoom = 100;
/// <summary>
/// Gets or sets the zoom.
/// </summary>
/// <value>The zoom.</value>
public virtual int OldZoom
{
get => _oldZoom;
set => RaiseAndSetIfChanged(ref _oldZoom, value);
}
/// <summary>
/// Gets or sets the zoom.
/// </summary>
@@ -594,7 +608,24 @@ namespace UVtools.WPF.Controls
public virtual int Zoom
{
get => _zoom;
set => SetZoom(value, value > Zoom ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut);
set
{
var previousZoom = _zoom;
var newZoom = value.Clamp(MinZoom, MaxZoom);
if (_zoom == newZoom) return;
_zoom = newZoom;
if (!UpdateViewPort())
{
InvalidateArrange();
}
OldZoom = previousZoom;
RaisePropertyChanged(nameof(Zoom));
//this.OnZoomChanged(EventArgs.Empty);
//this.OnZoomed(new ImageBoxZoomEventArgs(actions, source, previousZoom, this.Zoom));
//SetZoom(value, value > Zoom ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut);
}
}
public virtual bool IsActualSize => Zoom == 100;
@@ -751,7 +782,7 @@ namespace UVtools.WPF.Controls
int newZoom = GetZoomLevel(action);
RestoreSizeMode();
SetZoom(newZoom, action);
Zoom = newZoom;
if (preservePosition && Zoom != currentZoom)
{
@@ -891,7 +922,7 @@ namespace UVtools.WPF.Controls
var y = imageLocation.Y * ZoomFactor - relativeDisplayPoint.Y;
Offset = new Vector(x, y);
Debug.WriteLine(
/*Debug.WriteLine(
$"X/Y: {x},{y} | \n" +
$"Offset: {Offset} | \n" +
$"ZoomFactor: {ZoomFactor} | \n" +
@@ -899,32 +930,7 @@ namespace UVtools.WPF.Controls
$"MAX: {HorizontalScrollBarMaximum},{VerticalScrollBarMaximum} \n" +
$"ViewPort: {Viewport.Width},{Viewport.Height} \n" +
$"Container: {SizedContainer.Width},{SizedContainer.Height} \n" +
$"Relative: {relativeDisplayPoint}");
}
/// <summary>
/// Updates the current zoom.
/// </summary>
/// <param name="value">The new zoom value.</param>
/// <param name="actions">The zoom actions that caused the value to be updated.</param>
/// <param name="source">The source of the zoom operation.</param>
private void SetZoom(int value, ImageZoomActions actions)
{
var previousZoom = _zoom;
value = value.Clamp(MinZoom, MaxZoom);
if (_zoom != value)
{
_zoom = value;
if (!UpdateViewPort())
{
InvalidateArrange();
}
RaisePropertyChanged(nameof(Zoom));
//this.OnZoomChanged(EventArgs.Empty);
//this.OnZoomed(new ImageBoxZoomEventArgs(actions, source, previousZoom, this.Zoom));
}
$"Relative: {relativeDisplayPoint}");*/
}
/// <summary>
@@ -939,7 +945,7 @@ namespace UVtools.WPF.Controls
/// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
public virtual void ZoomIn(bool preservePosition)
{
//this.PerformZoomIn(ImageBoxActionSources.Unknown, preservePosition);
PerformZoom(ImageZoomActions.ZoomIn, preservePosition);
}
/// <summary>
@@ -954,7 +960,7 @@ namespace UVtools.WPF.Controls
/// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
public virtual void ZoomOut(bool preservePosition)
{
// this.PerformZoomOut(ImageBoxActionSources.Unknown, preservePosition);
PerformZoom(ImageZoomActions.ZoomOut, preservePosition);
}
/// <summary>
@@ -1029,16 +1035,34 @@ namespace UVtools.WPF.Controls
/// <param name="rectangle">The rectangle to fit the view port to.</param>
public virtual void ZoomToRegion(Rect rectangle)
{
{
var ratioX = Viewport.Width / rectangle.Width;
var ratioY = Viewport.Height / rectangle.Height;
var zoomFactor = Math.Min(ratioX, ratioY);
var cx = rectangle.X + rectangle.Width / 2;
var cy = rectangle.Y + rectangle.Height / 2;
var ratioX = Viewport.Width / rectangle.Width;
var ratioY = Viewport.Height / rectangle.Height;
var zoomFactor = Math.Min(ratioX, ratioY);
var cx = rectangle.X + rectangle.Width / 2;
var cy = rectangle.Y + rectangle.Height / 2;
Debug.WriteLine($"Before zoom: {Viewport}");
Zoom = (int) (zoomFactor * 100); // This function sets the zoom so viewport will change
Debug.WriteLine($"After zoom, viewport changed: {Viewport}");
//CenterAt(new Point(cx, cy)); // If i call this here, it will move to the wrong position due wrong viewport
Timer timer = new Timer(1)
{
AutoReset = false,
};
timer.Elapsed += (sender, args) =>
{
Dispatcher.UIThread.InvokeAsync(() =>
{
// This will fix centerAt position
Debug.WriteLine($"1ms delayed viewport: {Viewport}");
CenterAt(new Point(cx, cy));
});
};
timer.Start();
Zoom = (int)(zoomFactor * 100);
CenterAt(new Point(cx, cy));
}
}
/// <summary>
@@ -1110,7 +1134,8 @@ namespace UVtools.WPF.Controls
private void PerformActualSize()
{
SizeMode = SizeModes.Normal;
SetZoom(100, ImageZoomActions.ActualSize | (Zoom < 100 ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut));
//SetZoom(100, ImageZoomActions.ActualSize | (Zoom < 100 ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut));
Zoom = 100;
}
private void EventOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+1
View File
@@ -12,6 +12,7 @@
VerticalAlignment="Stretch"
AcceptsReturn="True"
Watermark="Kernel matrix"
UseFloatingWatermark="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{Binding MatrixText}"
@@ -0,0 +1,48 @@
<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"
xmlns:uc="clr-namespace:UVtools.WPF.Controls"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolBlurControl">
<StackPanel Spacing="10">
<Grid
ColumnDefinitions="Auto,10,*"
RowDefinitions="Auto,10,Auto">
<TextBlock
VerticalAlignment="Center"
Text="Algorithm:"/>
<ComboBox
Grid.Column="2"
HorizontalAlignment="Left"
SelectedIndex="{Binding SelectedAlgorithmIndex}"
Width="450"
Items="{Binding Operation.BlurTypes}"
/>
<TextBlock
VerticalAlignment="Center"
Grid.Row="2"
IsEnabled="{Binding IsSizeEnabled}"
Text="Size:"/>
<NumericUpDown
Grid.Row="2"
Grid.Column="2"
HorizontalAlignment="Left"
Width="80"
Minimum="1"
IsEnabled="{Binding IsSizeEnabled}"
Value="{Binding Operation.Size}"
/>
</Grid>
<uc:KernelControl
Name="KernelCtrl"
IsVisible="{Binding $parent[UserControl].IsKernelVisible}"
/>
</StackPanel>
</UserControl>
@@ -0,0 +1,59 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
namespace UVtools.WPF.Controls.Tools
{
public class ToolBlurControl : ToolControl
{
private int _selectedAlgorithmIndex;
private bool _isSizeEnabled = true;
private bool _isKernelVisible;
private KernelControl _kernelCtrl;
public OperationBlur Operation { get; }
public int SelectedAlgorithmIndex
{
get => _selectedAlgorithmIndex;
set
{
if(!RaiseAndSetIfChanged(ref _selectedAlgorithmIndex, value) || value < 0) return;
Operation.BlurOperation = (OperationBlur.BlurAlgorithm) OperationBlur.BlurTypes[_selectedAlgorithmIndex].Tag;
IsKernelVisible = Operation.BlurOperation == OperationBlur.BlurAlgorithm.Filter2D;
IsSizeEnabled = Operation.BlurOperation != OperationBlur.BlurAlgorithm.Pyramid &&
Operation.BlurOperation != OperationBlur.BlurAlgorithm.Filter2D;
}
}
public bool IsSizeEnabled
{
get => _isSizeEnabled;
set => RaiseAndSetIfChanged(ref _isSizeEnabled, value);
}
public bool IsKernelVisible
{
get => _isKernelVisible;
set => RaiseAndSetIfChanged(ref _isKernelVisible, value);
}
public ToolBlurControl()
{
InitializeComponent();
BaseOperation = Operation = new OperationBlur();
_kernelCtrl = this.Find<KernelControl>("KernelCtrl");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public override bool UpdateOperation()
{
Operation.Kernel.Matrix = _kernelCtrl.GetMatrix();
Operation.Kernel.Anchor = _kernelCtrl.Anchor;
return !(Operation.Kernel.Matrix is null);
}
}
}
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="250"
x:Class="UVtools.WPF.Controls.Tools.ToolMoveControl">
<Grid RowDefinitions="Auto" ColumnDefinitions="Auto,10,Auto">
<StackPanel Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
@@ -1,5 +1,4 @@
using System;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
@@ -0,0 +1,228 @@
<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.ToolPatternControl">
<StackPanel>
<Grid
Margin="0,0,0,15"
RowDefinitions="Auto,10,Auto"
ColumnDefinitions="Auto,10,Auto,20,Auto,10,Auto,5,Auto,10,Auto"
>
<!-- Cols -->
<TextBlock Text="Columns:"
VerticalAlignment="Center"
HorizontalAlignment="Right"
ToolTip.Tip="Number of copies in X direction."
/>
<NumericUpDown
Grid.Column="2"
Grid.Row="0"
Width="80"
Minimum="1"
Maximum="{Binding Operation.MaxCols}"
Value="{Binding Operation.Cols}"
ToolTip.Tip="Number of copies in X direction."
/>
<TextBlock Text="Spacing:"
Grid.Column="4"
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
ToolTip.Tip="Spacing between copies in X direction."
/>
<NumericUpDown
Grid.Column="6"
Grid.Row="0"
Width="80"
Minimum="1"
Maximum="65535"
Value="{Binding Operation.ColSpacing}"
ToolTip.Tip="Spacing between copies in X direction"
/>
<Button
Grid.Column="8"
Grid.Row="0"
Padding="10,5"
ToolTip.Tip="Fill spacing given the current number of columns, object size and left over space."
Command="{Binding Operation.FillColumnSpacing}"
>
<Image Source="/Assets/Icons/resize-16x16.png" />
</Button>
<TextBlock
Grid.Column="10"
Grid.Row="0"
VerticalAlignment="Center"
Text="{Binding Operation.InfoCols}"/>
<!-- Rows -->
<TextBlock Text="Rows:"
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
ToolTip.Tip="Number of copies in Y direction."
/>
<NumericUpDown
Grid.Column="2"
Grid.Row="2"
Width="80"
Minimum="1"
Maximum="{Binding Operation.MaxRows}"
Value="{Binding Operation.Rows}"
ToolTip.Tip="Number of copies in Y direction."
/>
<TextBlock Text="Spacing:"
Grid.Column="4"
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
ToolTip.Tip="Spacing between copies in Y direction."
/>
<NumericUpDown
Grid.Column="6"
Grid.Row="2"
Width="80"
Minimum="1"
Maximum="65535"
Value="{Binding Operation.RowSpacing}"
ToolTip.Tip="Spacing between copies in Y direction"
/>
<Button
Grid.Column="8"
Grid.Row="2"
Padding="10,5"
ToolTip.Tip="Fill spacing given the current number of rows, object size and left over space."
Command="{Binding Operation.FillRowSpacing}"
>
<Image Source="/Assets/Icons/resize-16x16.png" />
</Button>
<TextBlock
Grid.Column="10"
Grid.Row="2"
VerticalAlignment="Center"
Text="{Binding Operation.InfoRows}"/>
</Grid>
<Grid
RowDefinitions="Auto"
ColumnDefinitions="Auto,20,Auto"
>
<StackPanel Spacing="10">
<TextBlock Text="{Binding Operation.InfoWidthStr}" />
<TextBlock Text="{Binding Operation.InfoHeightStr}" />
<TextBlock Text="{Binding Operation.InfoModelWithinBoundaryStr}" />
</StackPanel>
<Grid
Grid.Column="2"
RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,Auto,Auto,5,Auto"
>
<RadioButton
Grid.Row="0" Grid.Column="0"
ToolTip.Tip="Top Left"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="0"
/>
<RadioButton
Grid.Row="0" Grid.Column="1"
ToolTip.Tip="Top Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="1"
/>
<RadioButton
Grid.Row="0" Grid.Column="2"
ToolTip.Tip="Top Right"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="2"
/>
<RadioButton
Grid.Row="1" Grid.Column="0"
ToolTip.Tip="Middle Left"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="3"
/>
<RadioButton
Grid.Row="1" Grid.Column="1"
ToolTip.Tip="Middle Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="5"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="4"
/>
<RadioButton
Grid.Row="1" Grid.Column="2"
ToolTip.Tip="Middle Right"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="5"
/>
<RadioButton
Grid.Row="2" Grid.Column="0"
ToolTip.Tip="Bottom Left"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="6"
/>
<RadioButton
Grid.Row="2" Grid.Column="1"
ToolTip.Tip="Bottom Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="7"
/>
<RadioButton
Grid.Row="2" Grid.Column="2"
ToolTip.Tip="Bottom Right"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
Command="{Binding Operation.SetAnchor}"
CommandParameter="8"
/>
<RadioButton
Grid.Row="1" Grid.Column="4"
ToolTip.Tip="Computed center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
GroupName="Anchor"
IsChecked="{Binding IsDefaultAnchorChecked}"
Command="{Binding Operation.SetAnchor}"
CommandParameter="9"
Content="Computed center"
/>
</Grid>
</Grid>
</StackPanel>
</UserControl>
@@ -0,0 +1,66 @@
using System;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
namespace UVtools.WPF.Controls.Tools
{
public class ToolPatternControl : ToolControl
{
public OperationPattern Operation { get; }
private bool _isDefaultAnchorChecked = true;
public bool IsDefaultAnchorChecked
{
get => _isDefaultAnchorChecked;
set => RaiseAndSetIfChanged(ref _isDefaultAnchorChecked, value);
}
public ToolPatternControl()
{
InitializeComponent();
var roi = App.MainWindow.ROI;
BaseOperation = Operation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution);
if (Operation.MaxRows < 2 && Operation.MaxCols < 2)
{
App.MainWindow.MessageBoxInfo("The available free volume is not enough to pattern this object.\n" +
"To run this tool the free space must allow at least 1 copy.", "Unable to pattern");
CanRun = false;
return;
}
Operation.PropertyChanged += (sender, e) =>
{
if (e.PropertyName.Equals(nameof(Operation.IsWithinBoundary)))
{
ParentWindow.ButtonOkEnabled = Operation.IsWithinBoundary;
}
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public override void Callback(ToolWindow.Callbacks callback)
{
switch (callback)
{
case ToolWindow.Callbacks.Init:
ParentWindow.IsButton1Visible = true;
break;
case ToolWindow.Callbacks.ClearROI:
Operation.SetRoi(App.SlicerFile.LayerManager.BoundingRectangle);
break;
case ToolWindow.Callbacks.Button1:
Operation.Fill();
IsDefaultAnchorChecked = true;
break;
}
}
}
}
+2 -2
View File
@@ -71,10 +71,10 @@ namespace UVtools.WPF.Controls
Close(DialogResult);
}
public void ResetDataContext()
public virtual void ResetDataContext()
{
var old = DataContext;
DataContext = new object();
DataContext = null;
DataContext = old;
}
}
+4 -2
View File
@@ -248,7 +248,9 @@
</StackPanel>
</TabItem.Header>
<Grid IsVisible="{Binding IsFileLoaded}" RowDefinitions="Auto,Auto,Auto,*">
<Grid
IsVisible="{Binding IsFileLoaded}"
RowDefinitions="Auto,Auto,Auto,*">
<!-- Thumbnails -->
<StackPanel
IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}"
@@ -309,6 +311,7 @@
<Image
IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}"
Stretch="None"
Grid.Row="1" Source="{Binding VisibleThumbnailImage}"/>
@@ -759,7 +762,6 @@
Maximum="{Binding SliderMaximumValue}"
Value="{Binding ActualLayer}"
TickFrequency="1"
TickPlacement="Outside"
SmallChange="1"
LargeChange="10"
IsSnapToTickEnabled="True"
+13 -3
View File
@@ -1135,8 +1135,8 @@ namespace UVtools.WPF
public PixelPicker LayerPixelPicker { get; } = new PixelPicker();
public string LayerZoomStr => $"{LayerImageBox.Zoom / 100}x" +
(AppSettings.LockedZoomLevel == LayerImageBox.Zoom ? "🔒" : string.Empty);
public string LayerZoomStr => $"{LayerImageBox.Zoom / 100m}x" +
(AppSettings.LockedZoomLevel == LayerImageBox.Zoom ? " 🔒" : string.Empty);
public string LayerResolutionStr => SlicerFile?.Resolution.ToString() ?? "Unloaded";
public uint ActualLayer
@@ -1144,6 +1144,7 @@ namespace UVtools.WPF
get => _actualLayer;
set
{
if (DataContext is null) return;
if (!RaiseAndSetIfChanged(ref _actualLayer, value)) return;
ShowLayer();
InvalidateLayerNavigation();
@@ -1240,7 +1241,16 @@ namespace UVtools.WPF
menuTool.Click += async (sender, args) => await ShowRunOperation(operation.GetType());
}
}
LayerImageBox.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == nameof(LayerImageBox.Zoom))
{
RaisePropertyChanged(nameof(LayerZoomStr));
return;
}
};
/*LayerSlider.PropertyChanged += (sender, args) =>
{