WPF settings

This commit is contained in:
Tiago Conceição
2020-09-17 23:21:38 +01:00
parent 5859a20d30
commit 518e7a0ec1
24 changed files with 719 additions and 245 deletions
+3
View File
@@ -1,5 +1,8 @@
# Changelog
## /09/2020 - v0.8.2.3
## 16/09/2020 - v0.8.2.2
* (Add) Support for PHZ zip files when renamed to .zip
+1 -1
View File
@@ -1,7 +1,7 @@
cd $PSScriptRoot
$version = (Get-Command UVtools.GUI\bin\Release\UVtools.Core.dll).FileVersionInfo.FileVersion
Remove-Item "$PSScriptRoot\UVtools.GUI\bin\Release\Logs" -Recurse
Remove-Item "$PSScriptRoot\UVtools.GUI\bin\Release\Logs" -Recurse -ErrorAction Ignore
Add-Type -A System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.GUI\bin\Release", "$PSScriptRoot\UVtools.GUI\bin\UVtools_v$version.zip")
+2
View File
@@ -350,6 +350,8 @@ namespace UVtools.Core.FileFormats
public override void Decode(string fileFullPath, OperationProgress progress = null)
{
base.Decode(fileFullPath, progress);
if(progress is null) progress = new OperationProgress();
progress.Reset(OperationProgress.StatusGatherLayers, LayerCount);
FileFullPath = fileFullPath;
using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+6
View File
@@ -49,6 +49,12 @@ namespace UVtools.Core.Objects
return true;
}
protected bool SetProperty([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
+3 -3
View File
@@ -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.2</Version>
<Version>0.8.2.3</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyVersion>0.8.2.2</AssemblyVersion>
<FileVersion>0.8.2.2</FileVersion>
<AssemblyVersion>0.8.2.3</AssemblyVersion>
<FileVersion>0.8.2.3</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+2 -2
View File
@@ -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.2")]
[assembly: AssemblyFileVersion("0.8.2.2")]
[assembly: AssemblyVersion("0.8.2.3")]
[assembly: AssemblyFileVersion("0.8.2.3")]
+13 -9
View File
@@ -1,11 +1,15 @@
using System.Diagnostics;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.ThemeManager;
using MessageBox.Avalonia.DTO;
using MessageBox.Avalonia.Enums;
using MessageBox.Avalonia.Models;
using UVtools.Core.FileFormats;
namespace UVtools.WPF
@@ -25,16 +29,16 @@ namespace UVtools.WPF
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
Selector = ThemeSelector.Create("Themes");
Selector.LoadSelectedTheme("UVtools.theme");
UserSettings.Load();
Selector = ThemeSelector.Create("Assets/Themes");
Selector.LoadSelectedTheme("Assets/selected.theme");
desktop.MainWindow = new MainWindow
{
//DataContext = Selector
};
desktop.Exit += (sender, e)
=> Selector.SaveSelectedTheme("UVtools.theme");
Debug.WriteLine(Selector.Themes[1].Name);
=> Selector.SaveSelectedTheme("Assets/selected.theme");
}
base.OnFrameworkInitializationCompleted();
Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

+123 -55
View File
@@ -1,4 +1,11 @@
using System;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Avalonia;
@@ -16,6 +23,55 @@ namespace UVtools.WPF.Extensions
/// </summary>
public static class BitmapExtension
{
public static int GetStep(this WriteableBitmap bitmap)
=> (int)(bitmap.Size.Width * 4);
/// <summary>
/// Gets the total length of this <see cref="WriteableBitmap"/></param>
/// </summary>
/// <param name="bitmap"></param>
/// <returns>The total length of this <see cref="WriteableBitmap"/></returns>
public static int GetLength(this WriteableBitmap bitmap)
=> (int) (bitmap.Size.Width * 4 * bitmap.Size.Height);
public static int GetPixelPos(this WriteableBitmap bitmap, int x, int y)
=> (int)(bitmap.Size.Width * 4 * y + x);
/// <summary>
/// Gets a single pixel span to manipulate or read pixels
/// </summary>
/// <typeparam name="T">Pixel type</typeparam>
/// <param name="mat"><see cref="Mat"/> Input</param>
/// <returns>A <see cref="Span{T}"/> containing all pixels in data memory</returns>
public static unsafe Span<byte> GetPixelSpan(this WriteableBitmap bitmap)
{
using var l = bitmap.Lock();
return new Span<byte>(l.Address.ToPointer(), bitmap.GetLength());
}
public static unsafe Span<byte> GetPixelSpan(this WriteableBitmap bitmap, int length, int offset = 0)
{
using var l = bitmap.Lock();
return new Span<byte>(IntPtr.Add(l.Address, offset).ToPointer(), length);
}
public static Span<byte> GetSinglePixelSpan(this WriteableBitmap bitmap, int x, int y, int length = 3)
{
using var l = bitmap.Lock();
return bitmap.GetPixelSpan(length, bitmap.GetPixelPos(x, y));
}
public static Span<byte> GetSinglePixelPosSpan(this WriteableBitmap bitmap, int pos, int length = 3)
=> bitmap.GetPixelSpan(length, pos);
public static unsafe Span<byte> GetPixelRowSpan(this WriteableBitmap bitmap, int y, int length = 0, int offset = 0)
{
using var l = bitmap.Lock();
return new Span<byte>(IntPtr.Add(l.Address, (int) (bitmap.Size.Width * 4 * y + offset)).ToPointer(), (int) (length == 0 ? bitmap.Size.Width * 4 : length));
}
public static SKBitmap ToSkBitmap(this Mat mat)
{
SKBitmap bitmap;
@@ -52,65 +108,77 @@ namespace UVtools.WPF.Extensions
return SKImage.FromBitmap(bitmap);
}
public static Bitmap ToBitmap(this Mat mat)
public static WriteableBitmap ToBitmap(this Mat mat)
{
if (mat.NumberOfChannels == 1)
{
var writeableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(72, 72),
PixelFormat.Rgba8888, AlphaFormat.Unpremul);
var span = mat.GetPixelSpan<byte>();
var bytes = new[] {0, 0, 0, 255};
using (var lockBuffer = writeableBitmap.Lock())
{
for (var i = 1; i < span.Length; i++)
{
bytes[0] = bytes[1] = bytes[2] = span[i];
Marshal.Copy(bytes, 0,
new IntPtr(lockBuffer.Address.ToInt64() + i * 4), bytes.Length);
}
}
var dataCount = mat.Width * mat.Height;
return writeableBitmap;
var writableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(96, 96),
PixelFormat.Bgra8888, AlphaFormat.Unpremul);
using var lockBuffer = writableBitmap.Lock();
unsafe
{
var targetPixels = (uint*) (void*) lockBuffer.Address;
switch (mat.NumberOfChannels)
{
//Stopwatch sw = Stopwatch.StartNew();
// Method 1 (Span copy)
case 1:
var srcPixels1 = (byte*) (void*) mat.DataPointer;
for (var i = 0; i < dataCount; i++)
{
var color = srcPixels1[i];
targetPixels[i] = (uint) (color | color << 8 | color << 16 | 0xff << 24);
}
break;
case 3:
var srcPixels2 = (byte*) (void*) mat.DataPointer;
uint pixel = 0;
for (uint i = 0; i < dataCount; i++)
{
targetPixels[i] = (uint)(srcPixels2[pixel++] | srcPixels2[pixel++] << 8 | srcPixels2[pixel++] << 16 | 0xff << 24);
}
break;
case 4:
var srcPixels4 = (uint*) (void*) mat.DataPointer;
for (uint i = 0; i < dataCount; i++)
{
targetPixels[i] = srcPixels4[i];
}
break;
}
}
return null;
}
/*PixelFormat targetPixelFormat = PixelFormat.Bgra8888;
switch (mat.NumberOfChannels)
{
case 3:
targetPixelFormat = PixelFormat.Rgb565;
break;
case 4:
targetPixelFormat = PixelFormat.Bgra8888;
break;
default:
throw new Exception("Unknown color type");
}
return writableBitmap;
/*Debug.WriteLine($"Method 1 (Span copy): {sw.ElapsedMilliseconds}ms");
// Method 2 (OpenCV Convertion + Copy Marshal)
sw.Restart();
CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
var buffer = target.GetBytes();
Marshal.Copy(buffer, 0, lockBuffer.Address, buffer.Length);
Debug.WriteLine($"Method 2 (OpenCV Convertion + Copy Marshal): {sw.ElapsedMilliseconds}ms");
var writeableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(72, 72), targetPixelFormat, AlphaFormat.Unpremul);
using var lockBuffer = writeableBitmap.Lock();
var buffer = mat.GetBytes();
for (var y = 0; y < mat.Height; y++)
{
Marshal.Copy(buffer, y * lockBuffer.RowBytes, new IntPtr(lockBuffer.Address.ToInt64() + y * lockBuffer.RowBytes), lockBuffer.RowBytes);
//sw.Restart();
CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
unsafe
{
var srcAddress = (uint*)(void*)target.DataPointer;
var targetAddress = (uint*)(void*)lockBuffer.Address;
*targetAddress = *srcAddress;
}
//Debug.WriteLine($"Method 3 (OpenCV Convertion + Set Address): {sw.ElapsedMilliseconds}ms");
return writableBitmap;
*/
/* for (var y = 0; y < mat.Height; y++)
{
Marshal.Copy(buffer, y * lockBuffer.RowBytes, new IntPtr(lockBuffer.Address.ToInt64() + y * lockBuffer.RowBytes), lockBuffer.RowBytes);
}*/
}
//Marshal.Copy(buffer, 0, lockBuffer.Address, buffer.Length);
SKBitmap bitmap = new SKBitmap(new SKImageInfo(mat.Width, mat.Height, SKColorType.Gray8));
bitmap.SetPixels(mat.DataPointer);
Debug.WriteLine(bitmap.Info.ColorType);
bitmap = SKBitmap.Decode(buffer.AsSpan(), new SKImageInfo
{
Width = mat.Width,
Height = mat.Height,
ColorType = SKColorType.Gray8,
});*/
/*return writeableBitmap;
}*/
}
}
-131
View File
@@ -1,131 +0,0 @@
using System;
using Avalonia;
namespace UVtools.WPF.Extensions
{
/// <summary>
/// Avalonia Matrix helper methods.
/// </summary>
public static class MatrixHelper
{
/// <summary>
/// Creates a translation matrix using the specified offsets.
/// </summary>
/// <param name="offsetX">X-coordinate offset.</param>
/// <param name="offsetY">Y-coordinate offset.</param>
/// <returns>The created translation matrix.</returns>
public static Matrix Translate(double offsetX, double offsetY)
{
return new Matrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY);
}
/// <summary>
/// Prepends a translation around the center of provided matrix.
/// </summary>
/// <param name="matrix">The matrix to prepend translation.</param>
/// <param name="offsetX">X-coordinate offset.</param>
/// <param name="offsetY">Y-coordinate offset.</param>
/// <returns>The created translation matrix.</returns>
public static Matrix TranslatePrepend(Matrix matrix, double offsetX, double offsetY)
{
return Translate(offsetX, offsetY) * matrix;
}
/// <summary>
/// Creates a matrix that scales along the x-axis and y-axis.
/// </summary>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix Scale(double scaleX, double scaleY)
{
return new Matrix(scaleX, 0, 0, scaleY, 0.0, 0.0);
}
/// <summary>
/// Creates a matrix that is scaling from a specified center.
/// </summary>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <param name="centerX">The center X-coordinate of the scaling.</param>
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix ScaleAt(double scaleX, double scaleY, double centerX, double centerY)
{
return new Matrix(scaleX, 0, 0, scaleY, centerX - (scaleX * centerX), centerY - (scaleY * centerY));
}
/// <summary>
/// Prepends a scale around the center of provided matrix.
/// </summary>
/// <param name="matrix">The matrix to prepend scale.</param>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <param name="centerX">The center X-coordinate of the scaling.</param>
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix ScaleAtPrepend(Matrix matrix, double scaleX, double scaleY, double centerX, double centerY)
{
return ScaleAt(scaleX, scaleY, centerX, centerY) * matrix;
}
/// <summary>
/// Creates a skew matrix.
/// </summary>
/// <param name="angleX">Angle of skew along the X-axis in radians.</param>
/// <param name="angleY">Angle of skew along the Y-axis in radians.</param>
/// <returns>When the method completes, contains the created skew matrix.</returns>
public static Matrix Skew(float angleX, float angleY)
{
return new Matrix(1.0, Math.Tan(angleX), Math.Tan(angleY), 1.0, 0.0, 0.0);
}
/// <summary>
/// Creates a matrix that rotates.
/// </summary>
/// <param name="radians">Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double radians)
{
double cos = Math.Cos(radians);
double sin = Math.Sin(radians);
return new Matrix(cos, sin, -sin, cos, 0, 0);
}
/// <summary>
/// Creates a matrix that rotates about a specified center.
/// </summary>
/// <param name="angle">Angle of rotation in radians.</param>
/// <param name="centerX">The center X-coordinate of the rotation.</param>
/// <param name="centerY">The center Y-coordinate of the rotation.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double angle, double centerX, double centerY)
{
return Translate(-centerX, -centerY) * Rotation(angle) * Translate(centerX, centerY);
}
/// <summary>
/// Creates a matrix that rotates about a specified center.
/// </summary>
/// <param name="angle">Angle of rotation in radians.</param>
/// <param name="center">The center of the rotation.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double angle, Vector center)
{
return Translate(-center.X, -center.Y) * Rotation(angle) * Translate(center.X, center.Y);
}
/// <summary>
/// Transforms a point by this matrix.
/// </summary>
/// <param name="matrix">The matrix to use as a transformation matrix.</param>
/// <param name="point">>The original point to apply the transformation.</param>
/// <returns>The result of the transformation for the input point.</returns>
public static Point TransformPoint(Matrix matrix, Point point)
{
return new Point(
(point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31,
(point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32);
}
}
}
@@ -1,4 +1,12 @@
using Avalonia;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using Avalonia;
namespace UVtools.WPF.Extensions
{
+7 -4
View File
@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.Threading.Tasks;
using Avalonia.Controls;
using MessageBox.Avalonia.DTO;
using MessageBox.Avalonia.Enums;
using MessageBox.Avalonia.Models;
namespace UVtools.WPF.Extensions
{
+12 -2
View File
@@ -7,9 +7,11 @@
xmlns:idc="clr-namespace:Dock.Avalonia.Controls;assembly=Dock.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.MainWindow"
Title="UVtools">
Title="UVtools"
Icon="/Assets/Icons/UVtools.ico"
>
<DockPanel>
<DockPanel IsEnabled="{Binding IsGUIEnabled}">
<Menu DockPanel.Dock="Top">
<MenuItem Name="MainMenu.File" Header="_File">
<MenuItem Name="MainMenu.File.Open" Header="_Open" InputGesture="Ctrl+O" Command="{Binding MenuFileOpenClicked}">
@@ -57,6 +59,14 @@
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Name="MainMenu.File.Settings" Header="_Settings" InputGesture="F12" HotKey="F12" Command="{Binding MenuFileSettingsClicked}">
<MenuItem.Icon>
<Image Source="\Assets\Icons\settings-16x16.png"/>
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Name="MainMenu.File.Exit" Header="_Exit Alt+F4" InputGesture="Alt+F4">
+23 -33
View File
@@ -1,18 +1,23 @@
using System;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using MessageBox.Avalonia.DTO;
using MessageBox.Avalonia.Enums;
using MessageBox.Avalonia.Models;
using SkiaSharp;
using UVtools.Core.FileFormats;
using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
using UVtools.WPF.ViewModels;
using UVtools.WPF.Windows;
namespace UVtools.WPF
{
@@ -71,9 +76,6 @@ namespace UVtools.WPF
public AdvancedImageBox LayerImage;
public Button ZoomToFitButton;
public Button CenterButton;
@@ -133,6 +135,8 @@ namespace UVtools.WPF
App.SlicerFile = FileFormat.FindByExtension(fileName, true, true);
if (App.SlicerFile is null) return;
ViewModel.IsGUIEnabled = false;
var task = Task.Factory.StartNew(() =>
{
try
@@ -153,37 +157,23 @@ namespace UVtools.WPF
}
});
//ProgressWindow progressWindow = new ProgressWindow();
//progressWindow.ShowDialog(this);
task.Wait();
var mat = App.SlicerFile[0].LayerMat;
var matRgb = App.SlicerFile[0].BrgMat;
var skbitmapGray = mat.ToSkBitmap();
using (var image = SKImage.FromBitmap(skbitmapGray))
using (var data = image.Encode(SKEncodedImageFormat.Png, 80))
{
// save the data to a stream
using (var stream = File.OpenWrite("D:\\gray.png"))
{
data.SaveTo(stream);
}
}
Debug.WriteLine("4K grayscale - BGRA convertion:");
var bitmap = mat.ToBitmap();
Debug.WriteLine("4K BGR - BGRA convertion:");
var bitmapRgb = matRgb.ToBitmap();
LayerImage.Image = bitmapRgb;
var skbitmapRBG = matRgb.ToSkBitmap();
using (var image = SKImage.FromBitmap(skbitmapRBG))
using (var data = image.Encode(SKEncodedImageFormat.Png, 80))
{
// save the data to a stream
using (var stream = File.OpenWrite("D:\\rgb.png"))
{
data.SaveTo(stream);
}
}
//Avalonia.Skia.SkiaSharpExtensions.
//LayerImage.Image = bitmap;
ViewModel.IsGUIEnabled = true;
}
}
}
+25 -3
View File
@@ -4,6 +4,20 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>UVtools</AssemblyName>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
<Authors>Tiago Conceição</Authors>
<Company>PTRTECH</Company>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.0-preview5" />
@@ -13,6 +27,7 @@
<PackageReference Include="Dock.Avalonia" Version="0.10.0-preview5" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4061" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.0-preview3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UVtools.Core\UVtools.Core.csproj" />
@@ -20,6 +35,9 @@
<ItemGroup>
<AvaloniaResource Include="Assets\Icons\*" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\Icons\UVtools.ico" />
</ItemGroup>
<ItemGroup>
<None Update="Themes - Copy\UVtoolsDark.xaml">
<Generator>MSBuild:Compile</Generator>
@@ -27,14 +45,18 @@
<None Update="Themes - Copy\UVtoolsLight.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<None Update="Themes\UVtools.themes">
<None Update="Assets\Themes\UVtools.themes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Themes\UVtoolsDark.xaml">
<None Update="Assets\Themes\UVtoolsDark.xaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Themes\UVtoolsLight.xaml">
<None Update="Assets\Themes\UVtoolsLight.xaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>
+231
View File
@@ -0,0 +1,231 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using Avalonia.Media;
namespace UVtools.WPF
{
[Serializable]
public sealed class UserSettings
{
#region Sub classes
[Serializable]
public sealed class GeneralUserSettings
{
public bool StartMaximized { get; set; } = true;
public bool CheckForUpdatesOnStartup { get; set; } = true;
public byte DefaultOpenFileExtensionIndex { get; set; }
public string DefaultDirectoryOpenFile { get; set; }
public string DefaultDirectorySaveFile { get; set; }
public string DefaultDirectoryExtractFile { get; set; }
public string DefaultDirectoryConvertFile { get; set; }
public bool PromptOverwriteFileSave { get; set; } = true;
public string FileSaveNamePrefix { get; set; }
public string FileSaveNameSuffix { get; set; } = "_copy";
}
[Serializable]
public sealed class LayerPreviewUserSettings
{
public Color TooltipOverlayBackgroundColor { get; set; } = new Color(210, 255, 255, 192);
public bool TooltipOverlay { get; set; } = true;
public Color VolumeBoundsOutlineColor { get; set; } = new Color(210, 0, 255, 0);
public byte VolumeBoundsOutlineThickness { get; set; } = 3;
public bool VolumeBoundsOutline { get; set; } = true;
public Color LayerBoundsOutlineColor { get; set; } = new Color(210, 0, 255, 0);
public byte LayerBoundsOutlineThickness { get; set; } = 3;
public bool LayerBoundsOutline { get; set; } = true;
public Color HollowOutlineColor { get; set; } = new Color(210, 255, 165, 0);
public byte HollowOutlineLineThickness { get; set; } = 3;
public bool HollowOutline { get; set; } = true;
public Color PreviousLayerDifferenceColor { get; set; } = new Color(255, 255, 0, 255);
public Color NextLayerDifferenceColor { get; set; } = new Color(255, 0, 255, 255);
public Color BothLayerDifferenceColor { get; set; } = new Color(255, 255, 0, 0);
public bool ShowLayerDifference { get; set; } = true;
public Color IslandColor { get; set; } = new Color(255, 255,215, 0);
public Color IslandHighlightColor { get; set; } = new Color(255, 255,255, 0);
public Color ResinTrapColor { get; set; } = new Color(255, 244, 164, 96);
public Color ResinTrapHighlightColor { get; set; } = new Color(255, 255, 165, 0);
public Color TouchingBoundsColor { get; set; } = new Color(255, 255, 0, 0);
public Color CrosshairColor { get; set; } = new Color(255, 255, 0, 0);
public bool ZoomToFitPrintVolumeBounds { get; set; } = true;
public byte ZoomLockLevelIndex { get; set; } = 7;
public bool ZoomIssues { get; set; } = true;
public bool CrosshairShowOnlyOnSelectedIssues { get; set; } = false;
public byte CrosshairFadeLevelIndex { get; set; } = 5;
public uint CrosshairLength { get; set; } = 20;
public byte CrosshairMargin { get; set; } = 5;
public bool AutoRotateLayerBestView { get; set; } = true;
public bool LayerZoomToFitOnLoad { get; set; } = true;
}
[Serializable]
public sealed class IssuesUserSettings
{
public bool ComputeIssuesOnLoad { get; set; } = false;
public bool ComputeIssuesOnClickTab { get; set; } = true;
public bool ComputeIslands { get; set; } = true;
public bool ComputeResinTraps { get; set; } = true;
public bool ComputeTouchingBounds { get; set; } = true;
public bool ComputeEmptyLayers { get; set; } = true;
public bool IslandAllowDiagonalBonds { get; set; } = false;
public byte IslandBinaryThreshold { get; set; } = 0;
public byte IslandRequiredAreaToProcessCheck { get; set; } = 1;
public byte IslandRequiredPixelsToSupport { get; set; } = 10;
public byte IslandRequiredPixelBrightnessToSupport { get; set; } = 150;
public byte ResinTrapBinaryThreshold { get; set; } = 127;
public byte ResinTrapRequiredAreaToProcessCheck { get; set; } = 17;
public byte ResinTrapRequiredBlackPixelsToDrain { get; set; } = 10;
public byte ResinTrapMaximumPixelBrightnessToDrain { get; set; } = 30;
}
[Serializable]
public sealed class PixelEditorUserSettings
{
public Color AddPixelColor { get; set; } = new Color(255, 144, 238, 144);
public Color AddPixelHighlightColor { get; set; } = new Color(255, 0, 255, 0);
public Color RemovePixelColor { get; set; } = new Color(255, 219, 112, 147);
public Color RemovePixelHighlightColor { get; set; } = new Color(255, 139, 0, 0);
public Color SupportsColor { get; set; } = new Color(255, 0, 255, 255);
public Color SupportsHighlightColor { get; set; } = new Color(255, 0, 139, 139);
public Color DrainHolesColor { get; set; } = new Color(255, 142, 69, 133);
public Color DrainHolesHighlightColor { get; set; } = new Color(255, 159, 0, 197);
public bool PartialUpdateIslandsOnEditing { get; set; } = true;
public bool CloseEditorOnApply { get; set; } = false;
}
[Serializable]
public sealed class LayerRepairUserSettings
{
public byte ClosingIterations { get; set; } = 2;
public byte OpeningIterations { get; set; } = 0;
public byte RemoveIslandsBelowEqualPixels { get; set; } = 10;
public bool RepairIslands { get; set; } = true;
public bool RepairResinTraps { get; set; } = true;
public bool RemoveEmptyLayers { get; set; } = true;
}
#endregion
#region Singleton
/// <summary>
/// Default filepath for store <see cref="UserSettings"/>
/// </summary>
private const string FilePath = "Assets/usersettings.xml";
private static UserSettings _instance;
/// <summary>
/// Instance of <see cref="UserSettings"/> (singleton)
/// </summary>
public static UserSettings Instance => _instance ??= new UserSettings();
#endregion
#region Properties
public GeneralUserSettings General { get; set; } = new GeneralUserSettings();
public LayerPreviewUserSettings LayerPreview { get; set; } = new LayerPreviewUserSettings();
public IssuesUserSettings Issues { get; set; } = new IssuesUserSettings();
public PixelEditorUserSettings PixelEditor { get; set; } = new PixelEditorUserSettings();
public LayerRepairUserSettings LayerRepair { get; set; } = new LayerRepairUserSettings();
/// <summary>
/// Gets or sets the number of times this file has been saved
/// </summary>
public uint SavesCount { get; set; }
/// <summary>
/// Gets or sets the last time this file has been modified
/// </summary>
public DateTime ModifiedDateTime { get; set; }
#endregion
#region Constructor
private UserSettings()
{ }
#endregion
#region Static Methods
/// <summary>
/// Reset settings to defaults
/// </summary>
/// <param name="save">True to save settings on file, otherwise false</param>
public static void Reset(bool save = false)
{
_instance = new UserSettings();
if(save) Save();
}
/// <summary>
/// Load settings from file
/// </summary>
public static void Load()
{
if (!File.Exists(FilePath))
{
return;
}
var serializer = new XmlSerializer(typeof(UserSettings));
using StreamReader myXmlReader = new StreamReader(FilePath);
try
{
_instance = (UserSettings)serializer.Deserialize(myXmlReader);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
/// <summary>
/// Save settings to file
/// </summary>
public static void Save()
{
Instance.SavesCount++;
_instance.ModifiedDateTime = DateTime.Now;
var serializer = new XmlSerializer(_instance.GetType());
using StreamWriter myXmlWriter = new StreamWriter(FilePath);
try
{
serializer.Serialize(myXmlWriter, _instance);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
#endregion
}
}
+22 -1
View File
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Reflection;
using Avalonia.Controls;
using JetBrains.Annotations;
using MessageBox.Avalonia.DTO;
@@ -6,13 +7,26 @@ using MessageBox.Avalonia.Enums;
using MessageBox.Avalonia.Models;
using UVtools.Core.Objects;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
namespace UVtools.WPF.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private MainWindow Parent;
public bool IsFileLoaded => !ReferenceEquals(App.SlicerFile, null);
private bool _isGUIEnabled = true;
public bool IsGUIEnabled
{
get => _isGUIEnabled;
set => SetProperty(ref _isGUIEnabled, value);
}
public bool IsFileLoaded
{
get => !ReferenceEquals(App.SlicerFile, null);
set => SetProperty();
}
public MainWindowViewModel(MainWindow parent)
{
@@ -28,5 +42,12 @@ namespace UVtools.WPF.ViewModels
var files = await dialog.ShowAsync(Parent);
Parent.ProcessFiles(files);
}
public async void MenuFileSettingsClicked()
{
SettingsWindow settingsWindow = new SettingsWindow();
settingsWindow.Title += $" [v{Assembly.GetEntryAssembly().GetName().Version}]";
await settingsWindow.ShowDialog(Parent);
}
}
}
+25
View File
@@ -0,0 +1,25 @@
<Window 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="400" d:DesignHeight="400"
x:Class="UVtools.WPF.Windows.ProgressWindow"
Title="ProgressWindow"
CanResize="False"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
MinWidth="400"
SizeToContent="WidthAndHeight"
SystemDecorations="BorderOnly"
Icon="/Assets/Icons/UVtools.ico"
>
<StackPanel Orientation="Vertical">
<TextBlock Margin="10" Text="Title"/>
<TextBlock Margin="10,0,10,10" Text="Elapsed Time: "/>
<TextBlock Margin="10,0,10,10" Text="Progress" HorizontalAlignment="Center"/>
<Grid RowDefinitions="30" ColumnDefinitions="*,Auto">
<ProgressBar Grid.Column="0" ShowProgressText="True"/>
<Button Grid.Column="1" Padding="30,0" IsCancel="True">Cancel</Button>
</Grid>
</StackPanel>
</Window>
@@ -0,0 +1,26 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace UVtools.WPF.Windows
{
public class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
+167
View File
@@ -0,0 +1,167 @@
<Window 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="500" d:DesignHeight="450"
x:Class="UVtools.WPF.Windows.SettingsWindow"
WindowStartupLocation="CenterOwner"
SizeToContent="WidthAndHeight"
CanResize="false"
SystemDecorations="Full"
MinWidth="500"
Title="UVtools - Settings"
Icon="/Assets/Icons/UVtools.ico"
>
<StackPanel Orientation="Vertical" Spacing="10">
<TabControl>
<TabItem Header="General" VerticalContentAlignment="Center">
<StackPanel Orientation="Vertical" Spacing="5">
<Border
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Startup"/>
<StackPanel Margin="15" Orientation="Vertical" Spacing="15">
<CheckBox Content="Start maximized"/>
<CheckBox Content="Check for updates on startup"/>
</StackPanel>
</StackPanel>
</Border>
<Border
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="File dialog"/>
<Grid Margin="15" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,*,Auto,Auto">
<TextBlock
VerticalAlignment="Center"
Grid.Row="0" Grid.Column="0"
Text="File open dialog filters:"/>
<ComboBox Margin="10,0,0,0"
Grid.ColumnSpan="3" Grid.Row="0" Grid.Column="1"/>
<TextBlock VerticalAlignment="Center"
Grid.Row="2" Grid.Column="0"
Text="File open default directory:"/>
<TextBox Margin="10,0,0,0" Grid.Row="2" Grid.Column="1" IsReadOnly="True"/>
<Button Grid.Row="2" Grid.Column="2">
<Image Source="/Assets/Icons/open-16x16.png"/>
</Button>
<Button Grid.Row="2" Grid.Column="3">
<Image Source="/Assets/Icons/delete-16x16.png"/>
</Button>
<TextBlock VerticalAlignment="Center"
Grid.Row="4" Grid.Column="0"
Text="File save as default directory:"/>
<TextBox Margin="10,0,0,0" Grid.Row="4" Grid.Column="1" IsReadOnly="True"/>
<Button Grid.Row="4" Grid.Column="2">
<Image Source="/Assets/Icons/open-16x16.png"/>
</Button>
<Button Grid.Row="4" Grid.Column="3">
<Image Source="/Assets/Icons/delete-16x16.png"/>
</Button>
<TextBlock VerticalAlignment="Center"
Grid.Row="6" Grid.Column="0"
Text="File extract default directory:"/>
<TextBox Margin="10,0,0,0" Grid.Row="6" Grid.Column="1" IsReadOnly="True"/>
<Button Grid.Row="6" Grid.Column="2">
<Image Source="/Assets/Icons/open-16x16.png"/>
</Button>
<Button Grid.Row="6" Grid.Column="3">
<Image Source="/Assets/Icons/delete-16x16.png"/>
</Button>
<TextBlock VerticalAlignment="Center"
Grid.Row="8" Grid.Column="0"
Text="File convert default directory:"/>
<TextBox Margin="10,0,0,0" Grid.Row="8" Grid.Column="1" IsReadOnly="True"/>
<Button Grid.Row="8" Grid.Column="2">
<Image Source="/Assets/Icons/open-16x16.png"/>
</Button>
<Button Grid.Row="8" Grid.Column="3">
<Image Source="/Assets/Icons/delete-16x16.png"/>
</Button>
</Grid>
<CheckBox Margin="15,0" Content="On file 'Save' prompt for file overwrite for the first time" />
<Grid Margin="15" ColumnDefinitions="Auto,*,Auto,*">
<TextBlock VerticalAlignment="Center" Grid.Column="0" Text="File 'Save as' prefix:"/>
<TextBox Grid.Column="1" Margin="10,0,10,0"/>
<TextBlock VerticalAlignment="Center" Grid.Column="2" Text="Suffix:"/>
<TextBox Grid.Column="3" Margin="10,0,0,0"/>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</TabItem>
<TabItem Header="Layer preview" VerticalContentAlignment="Center">
</TabItem>
<TabItem Header="Issues" VerticalContentAlignment="Center">
</TabItem>
<TabItem Header="Pixel editor" VerticalContentAlignment="Center">
</TabItem>
<TabItem Header="Layer repair" VerticalContentAlignment="Center">
</TabItem>
</TabControl>
<Grid ColumnDefinitions="*,*" Background="LightGray">
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="10">
<Button Padding="10">
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Icons/undo-alt-16x16.png"/>
<TextBlock Margin="10,0,0,0">Reset all settings</TextBlock>
</StackPanel>
</Button>
</StackPanel>
<StackPanel Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal"
Margin="10"
>
<Button Margin="0,0,10,0" Padding="10" IsDefault="True">
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Icons/save-16x16.png"/>
<TextBlock Margin="10,0,0,0">Save</TextBlock>
</StackPanel>
</Button>
<Button Padding="10" IsCancel="True">
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Icons/exit-16x16.png"/>
<TextBlock Margin="10,0,0,0">Cancel</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</Grid>
</StackPanel>
</Window>
@@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace UVtools.WPF.Windows
{
public class SettingsWindow : Window
{
public SettingsWindow()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}