Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2021-05-17 05:34:31 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-05-17 05:34:31 +0300
commit51c82318cf27b8e0d3425aa2ea1b093fc5b7d23f (patch)
tree1059ece01344eec934792d72bf8b0ed9aad4a06d
parentedd9984a31a90791edf3bcf594855d08507428b5 (diff)
v2.12.0v2.12.0
- **Layer arithmetic:** - (Add) Allow to use ':' to define a layer range to set, eg, 0:20 to select from 0 to 20 layers - (Improvement) Modifications with set ROI and/or Mask(s) are only applied to target layer on that same regions - (Improvement) Disallow set one layer to the same layer without any modification - (Improvement) Clear and sanitize non-existing layers indexes - (Improvement) Disable the layer range selector from dialog - (Fix) Prevent error when using non-existing layers indexes - (Fix) Allow use only a mask for operations - (Fix) Implement the progress bar - **File formats:** - (Add) VDA.ZIP (Voxeldance Additive) - (Improvement) Add a check to global `LightPWM` if 0 it will force to 255 - (Improvement) Add a check to layer `LightPWM` if 0 it will force to 255 - (Add) Allow to save the selected region (ROI) to a image file - (Update) .NET 5.0.5 to 5.0.6 - (Fix) Getting the transposed rectangle in fliped images are offseting the position by -1 - (Fix) Tools: Hide ROI Region text when empty/not selected
-rw-r--r--CHANGELOG.md20
-rw-r--r--README.md1
-rw-r--r--UVtools.Core/About.cs5
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs34
-rw-r--r--UVtools.Core/FileFormats/VDAFile.cs432
-rw-r--r--UVtools.Core/FileFormats/ZCodeFile.cs4
-rw-r--r--UVtools.Core/GCode/GCodeBuilder.cs10
-rw-r--r--UVtools.Core/Layer/Layer.cs25
-rw-r--r--UVtools.Core/Layer/LayerManager.cs43
-rw-r--r--UVtools.Core/Objects/BindableBase.cs5
-rw-r--r--UVtools.Core/Operations/Operation.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExposureFinder.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerArithmetic.cs57
-rw-r--r--UVtools.Core/UVtools.Core.csproj4
-rw-r--r--UVtools.WPF/Controls/AdvancedImageBox.axaml17
-rw-r--r--UVtools.WPF/Controls/AdvancedImageBox.axaml.cs768
-rw-r--r--UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs2
-rw-r--r--UVtools.WPF/Extensions/BitmapExtension.cs4
-rw-r--r--UVtools.WPF/Extensions/DrawingExtensions.cs8
-rw-r--r--UVtools.WPF/LayerCache.cs10
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs101
-rw-r--r--UVtools.WPF/MainWindow.axaml31
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs2
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs1
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--UVtools.WPF/UserSettings.cs68
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml1
-rw-r--r--build/CreateRelease.WPF.ps12
28 files changed, 1241 insertions, 420 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4b7b1f..4cb3b8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## 17/05/2021 - v2.12.0
+
+- **Layer arithmetic:**
+ - (Add) Allow to use ':' to define a layer range to set, eg, 0:20 to select from 0 to 20 layers
+ - (Improvement) Modifications with set ROI and/or Mask(s) are only applied to target layer on that same regions
+ - (Improvement) Disallow set one layer to the same layer without any modification
+ - (Improvement) Clear and sanitize non-existing layers indexes
+ - (Improvement) Disable the layer range selector from dialog
+ - (Fix) Prevent error when using non-existing layers indexes
+ - (Fix) Allow use only a mask for operations
+ - (Fix) Implement the progress bar
+- **File formats:**
+ - (Add) VDA.ZIP (Voxeldance Additive)
+ - (Improvement) Add a check to global `LightPWM` if 0 it will force to 255
+ - (Improvement) Add a check to layer `LightPWM` if 0 it will force to 255
+- (Add) Allow to save the selected region (ROI) to a image file
+- (Update) .NET 5.0.5 to 5.0.6
+- (Fix) Getting the transposed rectangle in fliped images are offseting the position by -1
+- (Fix) Tools: Hide ROI Region text when empty/not selected
+
## 13/05/2021 - v2.11.2
- (Improvement) Applied some refactorings on code
diff --git a/README.md b/README.md
index 93ffe98..4a16bee 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,7 @@ But also, i need victims for test subject. Proceed at your own risk!
* CXDLP (Creality Box)
* LGS (Longer Orange 10)
* LGS30 (Longer Orange 30)
+* VDA.ZIP (Voxeldance Additive)
* VDT (Voxeldance Tango)
* UVJ (Zip file format for manual manipulation)
* Image files (png, jpg, jpeg, gif, bmp)
diff --git a/UVtools.Core/About.cs b/UVtools.Core/About.cs
index b6c740d..35373a1 100644
--- a/UVtools.Core/About.cs
+++ b/UVtools.Core/About.cs
@@ -6,7 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.IO;
+using System.Reflection;
namespace UVtools.Core
{
@@ -19,5 +21,8 @@ namespace UVtools.Core
public static string Donate = "https://paypal.me/SkillTournament";
public static string DemoFile = "UVtools_demo_file.sl1";
+
+ public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
+ public static string VersionStr => Assembly.GetExecutingAssembly().GetName().Version.ToString(3);
}
}
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 0f0ade7..c667d84 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -216,6 +216,7 @@ namespace UVtools.Core.FileFormats
new GR1File(), // GR1 Workshop
new CXDLPFile(), // Creality Box
new LGSFile(), // LGS, LGS30
+ new VDAFile(), // VDA
new VDTFile(), // VDT
new UVJFile(), // UVJ
new ImageFile(), // images
@@ -367,12 +368,12 @@ namespace UVtools.Core.FileFormats
/// <summary>
/// Gets the available <see cref="FileFormat.PrintParameterModifier"/>
/// </summary>
- public abstract PrintParameterModifier[] PrintParameterModifiers { get; }
+ public virtual PrintParameterModifier[] PrintParameterModifiers => null;
/// <summary>
/// Gets the available <see cref="FileFormat.PrintParameterModifier"/> per layer
/// </summary>
- public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = null;
+ public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers => null;
/// <summary>
/// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters
@@ -476,7 +477,7 @@ namespace UVtools.Core.FileFormats
/// <summary>
/// Gets the original thumbnail sizes
/// </summary>
- public abstract Size[] ThumbnailsOriginalSize { get; }
+ public virtual Size[] ThumbnailsOriginalSize => null;
/// <summary>
/// Gets the thumbnails for this <see cref="FileFormat"/>
@@ -587,12 +588,12 @@ namespace UVtools.Core.FileFormats
/// <summary>
/// Gets or sets the display width in millimeters
/// </summary>
- public abstract float DisplayWidth { get; set; }
+ public virtual float DisplayWidth { get; set; }
/// <summary>
/// Gets or sets the display height in millimeters
/// </summary>
- public abstract float DisplayHeight { get; set; }
+ public virtual float DisplayHeight { get; set; }
/// <summary>
/// Gets or sets if images need to be mirrored on lcd to print on the correct orientation
@@ -904,6 +905,8 @@ namespace UVtools.Core.FileFormats
set => RaiseAndSet(ref _lightPwm, value);
}
+ public bool CanUseBottomLayerCount => HavePrintParameterModifier(PrintParameterModifier.BottomLayerCount);
+
public bool CanUseBottomExposureTime => HavePrintParameterModifier(PrintParameterModifier.BottomExposureSeconds);
public bool CanUseExposureTime => HavePrintParameterModifier(PrintParameterModifier.ExposureSeconds);
public bool CanUseAnyExposureTime => CanUseBottomExposureTime || CanUseExposureTime;
@@ -1528,26 +1531,7 @@ namespace UVtools.Core.FileFormats
"Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath);
}
- bool reSaveFile = false;
-
- if(ResolutionX == 0 || ResolutionY == 0)
- {
- var layer = FirstLayer;
- if (layer is not null)
- {
- using var mat = layer.LayerMat;
-
- if (mat.Size.HaveZero())
- {
- throw new FileLoadException($"File resolution ({Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", fileFullPath);
- }
-
- Resolution = mat.Size;
- reSaveFile = true;
- }
- }
-
- reSaveFile |= _layerManager.Sanitize();
+ bool reSaveFile = _layerManager.Sanitize();
if (reSaveFile)
{
diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs
new file mode 100644
index 0000000..ab6ab3a
--- /dev/null
+++ b/UVtools.Core/FileFormats/VDAFile.cs
@@ -0,0 +1,432 @@
+/*
+ * 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.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.OpenSsl;
+using UVtools.Core.Extensions;
+using UVtools.Core.GCode;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.FileFormats
+{
+ [Serializable]
+ [XmlRoot(ElementName = "root")]
+ public class VDARoot
+ {
+ [Serializable]
+ public class VDAFileInfo
+ {
+ [Serializable]
+ public class VDAVersion
+ {
+ public ushort Major { get; set; } = 1;
+ public ushort Minor { get; set; } = 2;
+
+ }
+
+ [Serializable]
+ public class VDAWritten
+ {
+ [Serializable]
+ [XmlRoot(ElementName = "By")]
+ public class VDABy
+ {
+ [XmlAttribute]
+ public string ApplicationName { get; set; } = About.Software;
+
+ [XmlAttribute]
+ public string ApplicationVersion { get; set; } = About.VersionStr;
+
+ public override string ToString()
+ {
+ return $"{ApplicationName} v{ApplicationVersion}";
+ }
+
+ public void Reset()
+ {
+ ApplicationName = About.Software;
+ ApplicationVersion = About.VersionStr;
+ }
+ }
+
+ public VDABy By { get; set; } = new();
+
+ public string When { get; set; } = DateTime.Now.ToString("u");
+
+ public void Reset()
+ {
+ When = DateTime.Now.ToString("u");
+ By.Reset();
+ }
+ }
+
+
+ public VDAVersion Version { get; set; } = new();
+
+ public VDAWritten Written { get; set; } = new();
+ }
+
+ [Serializable]
+ public class VDASlices
+ {
+ public ushort Count { get; set; } = 1;
+
+ [XmlElement("thickness")]
+ public float LayerHeight { get; set; }
+
+ [XmlElement("startHeight")]
+ public float StartHeight { get; set; }
+
+ [XmlElement("endHeight")]
+ public float EndHeight { get; set; }
+
+ [XmlElement("layersCount")]
+ public uint LayerCount { get; set; }
+ }
+
+ [Serializable]
+ public class VDAMachines
+ {
+ public string FileType { get; set; } = "ZIP File";
+ public string Resolution { get; set; } = "1920*1080P";
+ public string PixelXSize { get; set; } = "50um";
+ public string PixelYSize { get; set; } = "50um";
+
+ [XmlElement("Anti-Aliasing")]
+ public byte AntiAliasing { get; set; } = 1;
+
+ public float XLength { get; set; }
+ public float YWidth { get; set; }
+ public float ZHeight { get; set; }
+ }
+
+ public class VDALayer
+ {
+ [XmlElement("Index")]
+ public uint Index { get; set; }
+
+ [XmlElement("zvalue")]
+ public float ZPosition { get; set; }
+
+ [XmlElement("filename")]
+ public string Filename { get; set; }
+
+ public VDALayer()
+ {
+ }
+
+ public VDALayer(uint index, float zPosition, string filename)
+ {
+ Index = index;
+ ZPosition = zPosition;
+ Filename = filename;
+ }
+ }
+
+
+ public VDAFileInfo FileInfo { get; set; } = new();
+ public VDASlices Slices { get; set; } = new();
+ public VDAMachines Machines { get; set; } = new();
+ public List<VDALayer> Layers { get; set; } = new();
+ }
+
+ public class VDAFile : FileFormat
+ {
+ #region Constants
+
+ #endregion
+
+ #region Properties
+ public VDARoot ManifestFile { get; set; } = new ();
+
+ public override FileFormatType FileType => FileFormatType.Archive;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new("vda.zip", "Voxeldance Additive Zip")
+ };
+
+ public override uint ResolutionX
+ {
+ get
+ {
+ var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
+ if (resolution.Length < 2) return 0;
+ uint.TryParse(resolution[0], out var xRes);
+ return xRes;
+ }
+ set
+ {
+ ManifestFile.Machines.Resolution = $"{value}*{ResolutionY}P";
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint ResolutionY
+ {
+ get
+ {
+ var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
+ if (resolution.Length < 2) return 0;
+ resolution[1] = resolution[1].TrimEnd('P');
+ uint.TryParse(resolution[1], out var yRes);
+ return yRes;
+ }
+ set
+ {
+ ManifestFile.Machines.Resolution = $"{ResolutionX}*{value}P";
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float DisplayWidth
+ {
+ get
+ {
+ if (ManifestFile.Machines.XLength > 0) return ManifestFile.Machines.XLength;
+
+ var umStr= ManifestFile.Machines.PixelXSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
+
+ if (ushort.TryParse(umStr, out var um) && um > 0)
+ {
+ return (float) Math.Round(ResolutionX * um / 1000f, 2);
+ }
+
+ return ManifestFile.Machines.XLength;
+ }
+ set
+ {
+ ManifestFile.Machines.XLength = (float) Math.Round(value, 2);
+ ManifestFile.Machines.PixelXSize = $"{Math.Round(value / ResolutionX * 1000, 2)}um";
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float DisplayHeight
+ {
+ get
+ {
+ if (ManifestFile.Machines.YWidth > 0) return ManifestFile.Machines.YWidth;
+
+ var umStr = ManifestFile.Machines.PixelYSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
+
+ if (ushort.TryParse(umStr, out var um) && um > 0)
+ {
+ return (float)Math.Round(ResolutionY * um / 1000f, 2);
+ }
+
+ return ManifestFile.Machines.YWidth;
+ }
+ set
+ {
+ ManifestFile.Machines.YWidth = (float)Math.Round(value, 2);
+ ManifestFile.Machines.PixelYSize = $"{Math.Round(value / ResolutionY * 1000, 2)}um";
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float MachineZ
+ {
+ get => ManifestFile.Machines.ZHeight > 0 ? ManifestFile.Machines.ZHeight : base.MachineZ;
+ set
+ {
+ ManifestFile.Machines.ZHeight = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override byte AntiAliasing
+ {
+ get => ManifestFile.Machines.AntiAliasing;
+ set
+ {
+ ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16);
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float LayerHeight
+ {
+ get => ManifestFile.Slices.LayerHeight;
+ set
+ {
+ ManifestFile.Slices.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount;
+ }
+
+
+ public override object[] Configs => new object[] {
+ ManifestFile.FileInfo.Version,
+ ManifestFile.FileInfo.Written,
+ ManifestFile.Machines,
+ ManifestFile.Slices };
+
+ #endregion
+
+ #region Constructor
+ public VDAFile()
+ { }
+ #endregion
+
+ #region Methods
+
+ protected override void EncodeInternally(string fileFullPath, OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create);
+ var manifestFilename = Path.GetFileName(fileFullPath).
+ Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
+ Replace($".{FileExtensions[0].Extension}", ".xml");
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ var filename = $"{layerIndex + 1}".PadLeft(4, '0') + ".png";
+ outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
+ }
+
+ UpdateManifest();
+
+ XmlSerializer serializer = new(ManifestFile.GetType());
+ XmlSerializerNamespaces ns = new();
+ ns.Add("", "");
+ var entry = outputFile.CreateEntry(manifestFilename);
+ using var stream = entry.Open();
+ serializer.Serialize(stream, ManifestFile, ns);
+ }
+
+ protected override void DecodeInternally(string fileFullPath, OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+ {
+ var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name.EndsWith(".xml"));
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException($".xml manifest not found", fileFullPath);
+ }
+
+ try
+ {
+ var serializer = new XmlSerializer(ManifestFile.GetType());
+ using var stream = entry.Open();
+ ManifestFile = (VDARoot)serializer.Deserialize(stream);
+ }
+ catch (Exception e)
+ {
+ Clear();
+ throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", fileFullPath);
+ }
+
+
+ LayerManager.Init(ManifestFile.Slices.LayerCount);
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ if (progress.Token.IsCancellationRequested) break;
+ var filename = $"{layerIndex + 1}".PadLeft(4, '0')+".png";
+ entry = inputFile.GetEntry(filename);
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException($"Layer {filename} not found", fileFullPath);
+ }
+
+ using var stream = entry.Open();
+ this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
+
+ progress++;
+ }
+ }
+
+ LayerManager.GetBoundingRectangle(progress);
+ }
+
+ public override void SaveAs(string filePath = null, OperationProgress progress = null)
+ {
+ if (RequireFullEncode)
+ {
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ FileFullPath = filePath;
+ }
+ Encode(FileFullPath, progress);
+ return;
+ }
+
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ File.Copy(FileFullPath, filePath, true);
+ FileFullPath = filePath;
+ }
+
+ using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
+ bool deleted;
+
+ do
+ {
+ deleted = false;
+ foreach (var zipEntry in outputFile.Entries)
+ {
+ if (!zipEntry.Name.EndsWith(".xml")) continue;
+ zipEntry.Delete();
+ deleted = true;
+ break;
+ }
+ } while (deleted);
+
+ var manifestFilename = Path.GetFileName(FileFullPath).
+ Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
+ Replace($".{FileExtensions[0].Extension}", ".xml");
+
+ UpdateManifest();
+
+ XmlSerializer serializer = new(ManifestFile.GetType());
+ XmlSerializerNamespaces ns = new();
+ ns.Add("", "");
+ var entry = outputFile.CreateEntry(manifestFilename);
+ using var stream = entry.Open();
+ serializer.Serialize(stream, ManifestFile, ns);
+ }
+
+ public void UpdateManifest()
+ {
+ ManifestFile.FileInfo.Written.Reset();
+ ManifestFile.Slices.StartHeight = FirstLayer.PositionZ;
+ ManifestFile.Slices.EndHeight = LastLayer.PositionZ;
+ ManifestFile.Layers.Clear();
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ var layer = this[layerIndex];
+ ManifestFile.Layers.Add(new VDARoot.VDALayer(layerIndex, layer.PositionZ, layer.FormatFileName(4, false)));
+ }
+ }
+ #endregion
+ }
+}
diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs
index 412adb5..698e87f 100644
--- a/UVtools.Core/FileFormats/ZCodeFile.cs
+++ b/UVtools.Core/FileFormats/ZCodeFile.cs
@@ -35,7 +35,7 @@ namespace UVtools.Core.FileFormats
public class ZcodePrintDevice
{
[XmlAttribute("z")]
- public ushort MachineZ { get; set; } = 220;
+ public float MachineZ { get; set; } = 220;
[XmlAttribute("height")]
public uint ResolutionY { get; set; } = 2400;
@@ -257,7 +257,7 @@ namespace UVtools.Core.FileFormats
get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MachineZ;
set
{
- ManifestFile.Device.MachineZ = (ushort) value;
+ ManifestFile.Device.MachineZ = value;
RaisePropertyChanged();
}
}
diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs
index 18d5c51..97b4667 100644
--- a/UVtools.Core/GCode/GCodeBuilder.cs
+++ b/UVtools.Core/GCode/GCodeBuilder.cs
@@ -514,10 +514,10 @@ namespace UVtools.Core.GCode
AppendLineIfCanComment(BeginLayerComments, layerIndex, layer.PositionZ);
- if (layer.CanExpose)
- {
- AppendShowImageM6054(GetShowImageString(layerIndex));
- }
+ //if (layer.CanExpose)
+ //{ Dont check this for compability
+ AppendShowImageM6054(GetShowImageString(layerIndex));
+ //}
if (liftHeight > 0 && liftZPosAbs > layer.PositionZ)
{
@@ -627,7 +627,7 @@ namespace UVtools.Core.GCode
float positionZ = 0;
for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
{
- var layer = slicerFile[layerIndex];
+ var layer = slicerFile[layerIndex];
if(layer is null) continue;
var startStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex));
var endStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex+1));
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index f9f9695..c1e2fc9 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -202,7 +202,8 @@ namespace UVtools.Core
get => _lightPwm;
set
{
- if (value <= 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
+ if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
+ if (value == 0) value = FileFormat.DefaultLightPWM;
RaiseAndSetIfChanged(ref _lightPwm, value);
}
}
@@ -511,11 +512,23 @@ namespace UVtools.Core
LightOffDelay = CalculateLightOffDelay(extraTime);
}
- public string FormatFileName(string name)
+ public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true)
{
- return $"{name}{Index.ToString().PadLeft(ParentLayerManager.LayerDigits, '0')}.png";
+ var index = Index;
+ if (!layerIndexZeroStarted)
+ {
+ index++;
+ }
+ return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}.png";
}
+ public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true)
+ => FormatFileName(prepend, ParentLayerManager.LayerDigits, layerIndexZeroStarted);
+
+ public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true)
+ => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted);
+
+
public Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false)
{
if (_nonZeroPixelCount > 0 && !reCalculate)
@@ -625,8 +638,8 @@ namespace UVtools.Core
// These arrays are used to
// get row and column numbers
// of 8 neighbors of a given cell
- List<LayerIssue> result = new List<LayerIssue>();
- List<Point> pixels = new List<Point>();
+ List<LayerIssue> result = new();
+ List<Point> pixels = new();
@@ -700,7 +713,7 @@ namespace UVtools.Core
int y2;
- Queue<Point> queue = new Queue<Point>();
+ Queue<Point> queue = new();
queue.Enqueue(new Point(x, y));
// Mark this cell as visited
visited[x, y] = true;
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index df3a950..e7b9c7d 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -441,6 +441,23 @@ namespace UVtools.Core
if (this[layerIndex - 1].PositionZ > this[layerIndex].PositionZ) throw new InvalidDataException($"Layer {layerIndex - 1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n");
}
+ if (SlicerFile.ResolutionX == 0 || SlicerFile.ResolutionY == 0)
+ {
+ var layer = FirstLayer;
+ if (layer is not null)
+ {
+ using var mat = layer.LayerMat;
+
+ if (mat.Size.HaveZero())
+ {
+ throw new FileLoadException($"File resolution ({SlicerFile.Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", SlicerFile.FileFullPath);
+ }
+
+ SlicerFile.Resolution = mat.Size;
+ appliedCorrections = true;
+ }
+ }
+
// Fix 0mm positions at layer 0
if (this[0].PositionZ == 0)
{
@@ -452,6 +469,18 @@ namespace UVtools.Core
appliedCorrections = true;
}
+ // Fix LightPWM of 0
+ if (SlicerFile.LightPWM == 0)
+ {
+ SlicerFile.LightPWM = FileFormat.DefaultLightPWM;
+ appliedCorrections = true;
+ }
+ if (SlicerFile.BottomLightPWM == 0)
+ {
+ SlicerFile.BottomLightPWM = FileFormat.DefaultBottomLightPWM;
+ appliedCorrections = true;
+ }
+
return appliedCorrections;
}
@@ -779,7 +808,7 @@ namespace UVtools.Core
if (touchBoundConfig.Enabled)
{
// TouchingBounds Checker
- List<Point> pixels = new List<Point>();
+ List<Point> pixels = new();
bool touchTop = layer.BoundingRectangle.Top <= touchBoundConfig.MarginTop;
bool touchBottom = layer.BoundingRectangle.Bottom >= image.Height - touchBoundConfig.MarginBottom;
bool touchLeft = layer.BoundingRectangle.Left <= touchBoundConfig.MarginLeft;
@@ -1113,7 +1142,7 @@ namespace UVtools.Core
for (int i = 1; i < numLabels; i++)
{
- Rectangle rect = new Rectangle(
+ Rectangle rect = new(
(int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Left),
(int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Top),
(int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Width),
@@ -1128,7 +1157,7 @@ namespace UVtools.Core
previousSpan = previousImage.GetPixelSpan<byte>();
}
- List<Point> points = new List<Point>();
+ List<Point> points = new();
uint pixelsSupportingIsland = 0;
for (int y = rect.Y; y < rect.Bottom; y++)
@@ -1573,7 +1602,7 @@ namespace UVtools.Core
progress ??= new OperationProgress();
progress.Reset("Drawings", (uint) drawings.Count);
- ConcurrentDictionary<uint, Mat> modifiedLayers = new ConcurrentDictionary<uint, Mat>();
+ ConcurrentDictionary<uint, Mat> modifiedLayers = new();
for (var i = 0; i < drawings.Count; i++)
{
var operation = drawings[i];
@@ -1648,7 +1677,7 @@ namespace UVtools.Core
int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2);
int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2);
- using (Mat matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
+ using (Mat matCircleRoi = new(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
{
using (Mat matCircleMask = matCircleRoi.CloneBlank())
{
@@ -1683,9 +1712,9 @@ namespace UVtools.Core
int yStart = Math.Max(0, operation.Location.Y - radius);
int xStart = Math.Max(0, operation.Location.X - radius);
- using (Mat matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
+ using (Mat matCircleRoi = new(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
{
- using (Mat matCircleRoiInv = new Mat())
+ using (Mat matCircleRoiInv = new())
{
CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
using (Mat matCircleMask = matCircleRoi.CloneBlank())
diff --git a/UVtools.Core/Objects/BindableBase.cs b/UVtools.Core/Objects/BindableBase.cs
index 68a7611..9e63a1f 100644
--- a/UVtools.Core/Objects/BindableBase.cs
+++ b/UVtools.Core/Objects/BindableBase.cs
@@ -21,12 +21,11 @@ namespace UVtools.Core.Objects
/// Multicast event for property change notifications.
/// </summary>
private PropertyChangedEventHandler _propertyChanged;
- private List<string> events = new List<string>();
public event PropertyChangedEventHandler PropertyChanged
{
- add { _propertyChanged += value; events.Add("added"); }
- remove { _propertyChanged -= value; events.Add("removed"); }
+ add => _propertyChanged += value;
+ remove => _propertyChanged -= value;
}
/// <summary>
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index b7f7955..5cb7cad 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -235,6 +235,8 @@ namespace UVtools.Core.Operations
public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0;
+ public bool HaveROIorMask => HaveROI || HaveMask;
+
/// <summary>
/// Gets if this operation have been executed once
/// </summary>
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 9109e29..9fb8d6b 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -128,7 +128,7 @@ namespace UVtools.Core.Operations
private bool _bullsEyeInvertQuadrants = true;
private bool _counterTrianglesEnabled = true;
- private sbyte _counterTrianglesTipOffset = 1;
+ private sbyte _counterTrianglesTipOffset = 3;
private bool _counterTrianglesFence = false;
private bool _patternModel;
diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs
index 2ea9bc8..721f8c9 100644
--- a/UVtools.Core/Operations/OperationLayerArithmetic.cs
+++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
@@ -53,6 +54,8 @@ namespace UVtools.Core.Operations
#endregion
#region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override string Title => "Layer arithmetic";
public override string Description =>
"Perform arithmetic operations over the layers\n" +
@@ -63,7 +66,7 @@ namespace UVtools.Core.Operations
"Syntax: <set_to_layer_indexes> = <layer_index> <operator> <layer_index>\n" +
"When: \"<set_to_layer_indexes> =\" is omitted, the result will assign to the first layer on the sentence.\n\n" +
"Example 1: 10+11\n" +
- "Example 2: 10,11,12 = 11+12-10*5\n" +
+ "Example 2: 10,11,12 = 11+12-10*5 Same as: 10:12 = 11+12-10*5\n" +
"On example 1 the layer 10 will be set with the result of layer 10 plus layer 11.\n" +
"On example 2 the layers 10,11,12 will be set with the result of layer 11 plus 12 minus 10 all multiplied by layer 5.\n\n" +
"Note: Calculation are made sequential, math order rules wont apply here.";
@@ -87,13 +90,15 @@ namespace UVtools.Core.Operations
sb.AppendLine("No layers to assign.");
else if (Operations.Count == 0)
sb.AppendLine("No operations to perform.");
+ else if (!IsValid)
+ sb.AppendLine("The operation will have no impact and will not be performed.");
return sb.ToString();
}
public override string ToString()
{
- var result = $"{_sentence}" + LayerRangeString;
+ var result = $"{_sentence}";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
@@ -106,12 +111,13 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _sentence, value);
}
[XmlIgnore]
- public List<ArithmeticOperation> Operations { get; } = new();
+ public List<ArithmeticOperation> Operations { get; private set; } = new();
[XmlIgnore]
- public List<uint> SetLayers { get; } = new List<uint>();
+ public List<uint> SetLayers { get; private set; } = new();
- public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0;
+ public bool IsValid => SetLayers.Count > 0 && Operations.Count > 0 &&
+ !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex);
#endregion
#region Constructor
@@ -135,15 +141,31 @@ namespace UVtools.Core.Operations
if (splitSentence.Length >= 2)
{
operations = splitSentence[1];
- var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',');
+ var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',', StringSplitOptions.TrimEntries);
foreach (var layer in setLayers)
{
+ var rangeSplit = layer.Split(':', StringSplitOptions.TrimEntries);
+ if (rangeSplit.Length > 1)
+ {
+ uint.TryParse(rangeSplit[0].Trim(), out var startLayer);
+ if (!uint.TryParse(rangeSplit[1].Trim(), out var endLayer)) endLayer = SlicerFile.LastLayerIndex;
+ for (var index = startLayer; index <= endLayer; index++)
+ {
+ if (SetLayers.Contains(index)) continue;
+ SetLayers.Add(index);
+ }
+ continue;
+ }
+
if (!uint.TryParse(layer.Trim(), out var layerIndex)) continue;
if (SetLayers.Contains(layerIndex)) continue;
SetLayers.Add(layerIndex);
}
}
+ SetLayers = SetLayers.Where(layerIndex => layerIndex <= SlicerFile.LastLayerIndex).ToList();
+ SetLayers.Sort();
+
operations = operations.Replace(" ", string.Empty);
if (string.IsNullOrWhiteSpace(operations)) return false;
@@ -208,8 +230,10 @@ namespace UVtools.Core.Operations
}
}
- if (Operations.Count == 0) return false;
- if (SetLayers.Count == 0)
+ Operations = Operations.Where(op => op.LayerIndex <= SlicerFile.LastLayerIndex).ToList();
+
+ //if (Operations.Count == 0) return false;
+ if (SetLayers.Count == 0 && Operations.Count > 0)
{
SetLayers.Add(Operations[0].LayerIndex);
}
@@ -223,12 +247,15 @@ namespace UVtools.Core.Operations
using var result = SlicerFile[Operations[0].LayerIndex].LayerMat;
using var resultRoi = GetRoiOrDefault(result);
+ using var imageMask = GetMask(resultRoi);
+
+ progress.ItemCount = (uint) Operations.Count;
for (int i = 1; i < Operations.Count; i++)
{
progress.Token.ThrowIfCancellationRequested();
using var image = SlicerFile[Operations[i].LayerIndex].LayerMat;
var imageRoi = GetRoiOrDefault(image);
- using var imageMask = GetMask(image);
+
switch (Operations[i - 1].Operator)
{
case LayerArithmeticOperators.Add:
@@ -256,20 +283,26 @@ namespace UVtools.Core.Operations
CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi);
break;
}
+
+ progress++;
}
+ progress.Reset("Applied layers", (uint) SetLayers.Count);
Parallel.ForEach(SetLayers, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
- if (Operations.Count == 1 && HaveROI)
+ progress.LockAndIncrement();
+ if (Operations.Count == 1 || HaveROIorMask)
{
- var mat = SlicerFile[layerIndex].LayerMat;
+ using var mat = SlicerFile[layerIndex].LayerMat;
var matRoi = GetRoiOrDefault(mat);
- using var imageMask = GetMask(mat);
resultRoi.CopyTo(matRoi, imageMask);
SlicerFile[layerIndex].LayerMat = mat;
return;
}
+
+ //ApplyMask(mat, resultRoi, imageMask);
+
SlicerFile[layerIndex].LayerMat = result;
});
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index d3eac6a..46b05c3 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
- <Version>2.11.2</Version>
+ <Version>2.12.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -49,7 +49,7 @@
<PackageReference Include="AnimatedGif" Version="1.0.5" />
<PackageReference Include="BinarySerializer" Version="8.6.0" />
<PackageReference Include="Emgu.CV" Version="4.5.1.4349" />
- <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-2.final" />
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-3.final" />
<PackageReference Include="morelinq" Version="3.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
diff --git a/UVtools.WPF/Controls/AdvancedImageBox.axaml b/UVtools.WPF/Controls/AdvancedImageBox.axaml
index 2c288f6..6866ddf 100644
--- a/UVtools.WPF/Controls/AdvancedImageBox.axaml
+++ b/UVtools.WPF/Controls/AdvancedImageBox.axaml
@@ -8,10 +8,9 @@
RowDefinitions="*,Auto"
ColumnDefinitions="*,Auto">
- <ContentControl
- Name="ViewPort"
- Background="Transparent"
- />
+ <ContentPresenter Grid.Row="0" Grid.Column="0"
+ Name="ViewPort"
+ Background="Transparent"/>
<ScrollBar
Name="VerticalScrollBar"
@@ -20,8 +19,7 @@
ViewportSize="{Binding #ViewPort.Bounds.Height}"
Minimum="0"
Maximum="0"
- Visibility="Auto"
- />
+ Visibility="Auto"/>
<ScrollBar
Name="HorizontalScrollBar"
@@ -30,13 +28,12 @@
ViewportSize="{Binding #ViewPort.Bounds.Width}"
Minimum="0"
Maximum="0"
- Visibility="Auto"
- />
+ Visibility="Auto"/>
<Border
Grid.Row="1"
Grid.Column="1"
- Background="WhiteSmoke"
- />
+ Background="WhiteSmoke"/>
+
</Grid>
</UserControl>
diff --git a/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs b/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
index 02e8f87..94910fc 100644
--- a/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
+++ b/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
@@ -2,15 +2,17 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
using UVtools.Core.Extensions;
using UVtools.WPF.Extensions;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
@@ -26,7 +28,7 @@ namespace UVtools.WPF.Controls
{
public ScrollBar HorizontalScrollBar { get; }
public ScrollBar VerticalScrollBar { get; }
- public ContentControl ViewPortControl { get; }
+ public ContentPresenter ViewPort { get; }
public Vector Offset
{
@@ -40,19 +42,18 @@ namespace UVtools.WPF.Controls
}
}
- public Size Viewport => ViewPortControl.Bounds.Size;
+ public Size ViewPortSize => ViewPort.Bounds.Size;
#region Bindable Base
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
private PropertyChangedEventHandler _propertyChanged;
- private readonly List<string> events = new ();
public new event PropertyChangedEventHandler PropertyChanged
{
- add { _propertyChanged += value; events.Add("added"); }
- remove { _propertyChanged -= value; events.Add("removed"); }
+ add => _propertyChanged += value;
+ remove => _propertyChanged -= value;
}
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
@@ -123,16 +124,10 @@ namespace UVtools.WPF.Controls
/// <summary>
/// Returns the default zoom levels
/// </summary>
- public static ZoomLevelCollection Default
- {
- get
- {
- return new ZoomLevelCollection(new[]
- {
- 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
- });
- }
- }
+ public static ZoomLevelCollection Default =>
+ new(new[] {
+ 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
+ });
#endregion
@@ -233,9 +228,9 @@ namespace UVtools.WPF.Controls
/// <param name="arrayIndex">A 64-bit integer that represents the index in the <see cref="Array"/> at which storing begins.</param>
public void CopyTo(int[] array, int arrayIndex)
{
- for (int i = 0; i < this.Count; i++)
+ for (int i = 0; i < Count; i++)
{
- array[arrayIndex + i] = this.List.Values[i];
+ array[arrayIndex + i] = List.Values[i];
}
}
@@ -245,7 +240,7 @@ namespace UVtools.WPF.Controls
/// <param name="zoomLevel">The zoom level.</param>
public int FindNearest(int zoomLevel)
{
- int nearestValue = this.List.Values[0];
+ int nearestValue = List.Values[0];
int nearestDifference = Math.Abs(nearestValue - zoomLevel);
for (int i = 1; i < Count; i++)
{
@@ -297,8 +292,8 @@ namespace UVtools.WPF.Controls
/// <returns>The next matching increased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
public int NextZoom(int zoomLevel)
{
- var index = IndexOf(this.FindNearest(zoomLevel));
- if (index < this.Count - 1)
+ var index = IndexOf(FindNearest(zoomLevel));
+ if (index < Count - 1)
{
index++;
}
@@ -381,7 +376,7 @@ namespace UVtools.WPF.Controls
public enum SizeModes : byte
{
/// <summary>
- /// The image is disiplayed according to current zoom and scroll properties.
+ /// The image is displayed according to current zoom and scroll properties.
/// </summary>
Normal,
@@ -393,7 +388,7 @@ namespace UVtools.WPF.Controls
/// <summary>
/// The image is stretched to fill as much of the client area of the control as possible, whilst retaining the same aspect ratio for the width and height.
/// </summary>
- //Fit
+ Fit
}
[Flags]
@@ -452,68 +447,95 @@ namespace UVtools.WPF.Controls
#endregion
- #region Constants
- public static readonly int MinZoom = 10;
- public static readonly int MaxZoom = 3500;
- #endregion
+ public static readonly DirectProperty<AdvancedImageBox, bool> CanRenderProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(CanRender),
+ o => o.CanRender);
+ /// <summary>
+ /// Gets or sets if control can render the image
+ /// </summary>
public bool CanRender
{
get => _canRender;
set
{
- if (!RaiseAndSetIfChanged(ref _canRender, value)) return;
+ if (!SetAndRaise(CanRenderProperty, ref _canRender, value)) return;
if (_canRender) TriggerRender();
}
}
+ public static readonly StyledProperty<byte> GridCellSizeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, byte>(nameof(GridCellSize), 15);
+
/// <summary>
- /// Gets or sets the basic cell size
+ /// Gets or sets the grid cell size
/// </summary>
public byte GridCellSize
{
- get => _gridCellSize;
- set => RaiseAndSetIfChanged(ref _gridCellSize, value);
+ get => GetValue(GridCellSizeProperty);
+ set => SetValue(GridCellSizeProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> GridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColor), Brushes.Gainsboro);
+
/// <summary>
/// Gets or sets the color used to create the checkerboard style background
/// </summary>
public ISolidColorBrush GridColor
{
- get => _gridColor;
- set => RaiseAndSetIfChanged(ref _gridColor, value);
+ get => GetValue(GridColorProperty);
+ set => SetValue(GridColorProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> GridColorAlternateProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColorAlternate), Brushes.White);
+
/// <summary>
/// Gets or sets the color used to create the checkerboard style background
/// </summary>
public ISolidColorBrush GridColorAlternate
{
- get => _gridColorAlternate;
- set => RaiseAndSetIfChanged(ref _gridColorAlternate, value);
+ get => GetValue(GridColorAlternateProperty);
+ set => SetValue(GridColorAlternateProperty, value);
}
+ public static readonly StyledProperty<Bitmap> ImageProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Bitmap>(nameof(Image));
+
/// <summary>
/// Gets or sets the image to be displayed
/// </summary>
public Bitmap Image
{
- get => _image;
+ get => GetValue(ImageProperty);
set
{
- if (!RaiseAndSetIfChanged(ref _image, value)) return;
+ SetValue(ImageProperty, value);
- if (Image is null)
+ if (value is null)
{
SelectNone();
}
UpdateViewPort();
TriggerRender();
+
+ RaisePropertyChanged(nameof(IsImageLoaded));
}
}
+ public WriteableBitmap ImageAsWriteableBitmap => (WriteableBitmap) Image;
+
+ public bool IsImageLoaded => Image is not null;
+
+ public static readonly DirectProperty<AdvancedImageBox, Bitmap> TrackerImageProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Bitmap>(
+ nameof(TrackerImage),
+ o => o.TrackerImage,
+ (o, v) => o.TrackerImage = v);
+
/// <summary>
/// Gets or sets an image to follow the mouse pointer
/// </summary>
@@ -522,29 +544,33 @@ namespace UVtools.WPF.Controls
get => _trackerImage;
set
{
- if (!RaiseAndSetIfChanged(ref _trackerImage, value)) return;
- RaisePropertyChanged(nameof(HaveTrackerImage));
+ if (!SetAndRaise(TrackerImageProperty, ref _trackerImage, value)) return;
TriggerRender();
+ RaisePropertyChanged(nameof(HaveTrackerImage));
}
}
- public bool HaveTrackerImage => !(_trackerImage is null);
+ public bool HaveTrackerImage => _trackerImage is not null;
+
+ public static readonly StyledProperty<bool> TrackerImageAutoZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(TrackerImageAutoZoom), true);
/// <summary>
/// Gets or sets if the tracker image will be scaled to the current zoom
/// </summary>
public bool TrackerImageAutoZoom
{
- get => _trackerImageAutoZoom;
- set => RaiseAndSetIfChanged(ref _trackerImageAutoZoom, value);
+ get => GetValue(TrackerImageAutoZoomProperty);
+ set => SetValue(TrackerImageAutoZoomProperty, value);
}
-
+
public bool IsHorizontalBarVisible
{
get
{
if (Image is null) return false;
- return ScaledImageWidth > Viewport.Width;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageWidth > ViewPortSize.Width;
}
}
@@ -553,34 +579,51 @@ namespace UVtools.WPF.Controls
get
{
if (Image is null) return false;
- return ScaledImageHeight > Viewport.Height;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageHeight > ViewPortSize.Height;
}
}
- public static readonly DirectProperty<AdvancedImageBox, bool> ShowGridProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(nameof(ShowGrid),
- c => c.ShowGrid, (c, v) => c.ShowGrid = v);
+ public static readonly StyledProperty<bool> ShowGridProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(ShowGrid), true);
+
/// <summary>
- /// Gets or sets if the checkerboard background should be displayed
+ /// Gets or sets the grid visibility when reach high zoom levels
/// </summary>
public bool ShowGrid
{
- get => _showGrid;
- set => SetAndRaise(ShowGridProperty, ref _showGrid, value);
+ get => GetValue(ShowGridProperty);
+ set => SetValue(ShowGridProperty, value);
}
+ public static readonly DirectProperty<AdvancedImageBox, Point> PointerPositionProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Point>(
+ nameof(PointerPosition),
+ o => o.PointerPosition);
+
+ /// <summary>
+ /// Gets the current pointer position
+ /// </summary>
public Point PointerPosition
{
get => _pointerPosition;
- private set => RaiseAndSetIfChanged(ref _pointerPosition, value);
+ private set => SetAndRaise(PointerPositionProperty, ref _pointerPosition, value);
}
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsPanningProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsPanning),
+ o => o.IsPanning);
+
+ /// <summary>
+ /// Gets if control is currently panning
+ /// </summary>
public bool IsPanning
{
get => _isPanning;
protected set
{
- if (!RaiseAndSetIfChanged(ref _isPanning, value)) return;
+ if (!SetAndRaise(IsPanningProperty, ref _isPanning, value)) return;
_startScrollPosition = Offset;
if (value)
@@ -596,94 +639,242 @@ namespace UVtools.WPF.Controls
}
}
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsSelectingProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsSelecting),
+ o => o.IsSelecting);
+
+ /// <summary>
+ /// Gets if control is currently selecting a ROI
+ /// </summary>
public bool IsSelecting
{
get => _isSelecting;
- protected set => RaiseAndSetIfChanged(ref _isSelecting, value);
+ protected set => SetAndRaise(IsSelectingProperty, ref _isSelecting, value);
}
+ /// <summary>
+ /// Gets the center point of the viewport
+ /// </summary>
public Point CenterPoint
{
get
{
var viewport = GetImageViewPort();
- return new Point(viewport.Width / 2, viewport.Height / 2);
+ return new(viewport.Width / 2, viewport.Height / 2);
}
}
+ public static readonly StyledProperty<bool> AutoPanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoPan), true);
+
+ /// <summary>
+ /// Gets or sets if the control can pan with the mouse
+ /// </summary>
public bool AutoPan
{
- get => _autoPan;
- set => RaiseAndSetIfChanged(ref _autoPan, value);
+ get => GetValue(AutoPanProperty);
+ set => SetValue(AutoPanProperty, value);
}
+ public static readonly StyledProperty<MouseButtons> PanWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(PanWithMouseButtons), MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton);
+
+ /// <summary>
+ /// Gets or sets the mouse buttons to pan the image
+ /// </summary>
public MouseButtons PanWithMouseButtons
{
- get => _panWithMouseButtons;
- set => RaiseAndSetIfChanged(ref _panWithMouseButtons, value);
+ get => GetValue(PanWithMouseButtonsProperty);
+ set => SetValue(PanWithMouseButtonsProperty, value);
}
+ public static readonly StyledProperty<bool> PanWithArrowsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(PanWithArrows), true);
+
+ /// <summary>
+ /// Gets or sets if the control can pan with the keyboard arrows
+ /// </summary>
public bool PanWithArrows
{
- get => _panWithArrows;
- set => RaiseAndSetIfChanged(ref _panWithArrows, value);
+ get => GetValue(PanWithArrowsProperty);
+ set => SetValue(PanWithArrowsProperty, value);
}
+ public static readonly StyledProperty<MouseButtons> SelectWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(SelectWithMouseButtons), MouseButtons.LeftButton | MouseButtons.RightButton);
+
+
+ /// <summary>
+ /// Gets or sets the mouse buttons to select a region on image
+ /// </summary>
public MouseButtons SelectWithMouseButtons
{
- get => _selectWithMouseButtons;
- set => RaiseAndSetIfChanged(ref _selectWithMouseButtons, value);
+ get => GetValue(SelectWithMouseButtonsProperty);
+ set => SetValue(SelectWithMouseButtonsProperty, value);
}
- public bool InvertMouse
+ public static readonly StyledProperty<bool> InvertMousePanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(InvertMousePan), false);
+
+ /// <summary>
+ /// Gets or sets if mouse pan is inverted
+ /// </summary>
+ public bool InvertMousePan
{
- get => _invertMouse;
- set => RaiseAndSetIfChanged(ref _invertMouse, value);
+ get => GetValue(InvertMousePanProperty);
+ set => SetValue(InvertMousePanProperty, value);
}
+ public static readonly StyledProperty<bool> AutoCenterProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoCenter), true);
+
+ /// <summary>
+ /// Gets or sets if image is auto centered
+ /// </summary>
public bool AutoCenter
{
- get => _autoCenter;
- set => RaiseAndSetIfChanged(ref _autoCenter, value);
+ get => GetValue(AutoCenterProperty);
+ set => SetValue(AutoCenterProperty, value);
}
+ public static readonly StyledProperty<SizeModes> SizeModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SizeModes>(nameof(SizeMode), SizeModes.Normal);
+
+ /// <summary>
+ /// Gets or sets the image size mode
+ /// </summary>
public SizeModes SizeMode
{
- get => _sizeMode;
- set => RaiseAndSetIfChanged(ref _sizeMode, value);
+ get => GetValue(SizeModeProperty);
+ set
+ {
+ SetValue(SizeModeProperty, value);
+ SizeModeChanged();
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
+ }
}
- private bool _allowZoom = true;
- public virtual bool AllowZoom
+ private void SizeModeChanged()
{
- get => _allowZoom;
- set => RaiseAndSetIfChanged(ref _allowZoom, value);
+ switch (SizeMode)
+ {
+ case SizeModes.Normal:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ break;
+ case SizeModes.Stretch:
+ case SizeModes.Fit:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
+ }
}
+ public static readonly StyledProperty<bool> AllowZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AllowZoom), true);
+
+ /// <summary>
+ /// Gets or sets if zoom is allowed
+ /// </summary>
+ public bool AllowZoom
+ {
+ get => GetValue(AllowZoomProperty);
+ set => SetValue(AllowZoomProperty, value);
+ }
+
+ public static readonly DirectProperty<AdvancedImageBox, ZoomLevelCollection> ZoomLevelsProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, ZoomLevelCollection>(
+ nameof(ZoomLevels),
+ o => o.ZoomLevels,
+ (o, v) => o.ZoomLevels = v);
+
ZoomLevelCollection _zoomLevels = ZoomLevelCollection.Default;
/// <summary>
/// Gets or sets the zoom levels.
/// </summary>
/// <value>The zoom levels.</value>
- public virtual ZoomLevelCollection ZoomLevels
+ public ZoomLevelCollection ZoomLevels
{
get => _zoomLevels;
- set => RaiseAndSetIfChanged(ref _zoomLevels, value);
+ set => SetAndRaise(ZoomLevelsProperty, ref _zoomLevels, value);
}
+ public static readonly StyledProperty<int> MinZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MinZoom), 10);
+
+ /// <summary>
+ /// Gets or sets the minimum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MinZoom
+ {
+ get => GetValue(MinZoomProperty);
+ set => SetValue(MinZoomProperty, value);
+ }
+
+ public static readonly StyledProperty<int> MaxZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MaxZoom), 3500);
+
+ /// <summary>
+ /// Gets or sets the maximum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MaxZoom
+ {
+ get => GetValue(MaxZoomProperty);
+ set => SetValue(MaxZoomProperty, value);
+ }
+
+
+ public static readonly DirectProperty<AdvancedImageBox, int> OldZoomProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, int>(
+ nameof(OldZoom),
+ o => o.OldZoom);
+
private int _oldZoom = 100;
- private int _zoom = 100;
/// <summary>
- /// Gets or sets the zoom.
+ /// Gets the previous zoom value
/// </summary>
/// <value>The zoom.</value>
- public virtual int OldZoom
+ public int OldZoom
{
get => _oldZoom;
- set => RaiseAndSetIfChanged(ref _oldZoom, value);
+ private set => SetAndRaise(OldZoomProperty, ref _oldZoom, value);
}
+ public static readonly StyledProperty<int> ZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(Zoom), 100);
+
+ /// <summary>
+ /// Gets or sets the zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int Zoom
+ {
+ get => GetValue(ZoomProperty);
+ set
+ {
+ var newZoom = value.Clamp(MinZoom, MaxZoom);
+
+ var previousZoom = Zoom;
+ if (previousZoom == newZoom) return;
+ OldZoom = previousZoom;
+ SetValue(ZoomProperty, value);
+
+ UpdateViewPort();
+ TriggerRender();
+
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
+ }
+ }
+
+ /*
/// <summary>
/// Gets or sets the zoom.
/// </summary>
@@ -708,52 +899,90 @@ namespace UVtools.WPF.Controls
//SetZoom(value, value > Zoom ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut);
}
}
+ */
- public virtual bool IsActualSize => Zoom == 100;
+ public bool IsActualSize => Zoom == 100;
+
+ public static readonly StyledProperty<ISolidColorBrush> PixelGridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(PixelGridColor), Brushes.DimGray);
- private ISolidColorBrush _pixelGridColor = Brushes.DimGray;
/// <summary>
/// Gets or sets the color of the pixel grid.
/// </summary>
/// <value>The color of the pixel grid.</value>
public virtual ISolidColorBrush PixelGridColor
{
- get => _pixelGridColor;
- set => RaiseAndSetIfChanged(ref _pixelGridColor, value);
+ get => GetValue(PixelGridColorProperty);
+ set => SetValue(PixelGridColorProperty, value);
}
- private int _pixelGridThreshold = 5;
+ public static readonly StyledProperty<int> PixelGridZoomThresholdProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(PixelGridZoomThreshold), 5);
+
/// <summary>
/// Gets or sets the minimum size of zoomed pixel's before the pixel grid will be drawn
/// </summary>
/// <value>The pixel grid threshold.</value>
- public virtual int PixelGridThreshold
+ public int PixelGridZoomThreshold
{
- get => _pixelGridThreshold;
- set => RaiseAndSetIfChanged(ref _pixelGridThreshold, value);
+ get => GetValue(PixelGridZoomThresholdProperty);
+ set => SetValue(PixelGridZoomThresholdProperty, value);
}
+ public static readonly StyledProperty<SelectionModes> SelectionModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SelectionModes>(nameof(SelectionMode), SelectionModes.None);
+
public SelectionModes SelectionMode
{
- get => _selectionMode;
- set => RaiseAndSetIfChanged(ref _selectionMode, value);
+ get => GetValue(SelectionModeProperty);
+ set => SetValue(SelectionModeProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> SelectionColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(SelectionColor), new SolidColorBrush(new Color(127, 0, 128, 255)));
+
public ISolidColorBrush SelectionColor
{
- get => _selectionColor;
- set => RaiseAndSetIfChanged(ref _selectionColor, value);
+ get => GetValue(SelectionColorProperty);
+ set => SetValue(SelectionColorProperty, value);
}
+ public static readonly StyledProperty<Rect> SelectionRegionProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Rect>(nameof(SelectionRegion), Rect.Empty);
+
+
public Rect SelectionRegion
{
- get => _selectionRegion;
+ get => GetValue(SelectionRegionProperty);
set
{
- if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
+ SetValue(SelectionRegionProperty, value);
+ //if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
TriggerRender();
RaisePropertyChanged(nameof(HaveSelection));
+ RaisePropertyChanged(nameof(SelectionRegionNet));
+ RaisePropertyChanged(nameof(SelectionPixelSize));
+ }
+ }
+
+ public Rectangle SelectionRegionNet
+ {
+ get
+ {
+ var rect = SelectionRegion;
+ return new Rectangle((int) Math.Ceiling(rect.X), (int)Math.Ceiling(rect.Y),
+ (int)Math.Floor(rect.Width), (int)Math.Floor(rect.Height)
+ );
+ }
+ }
+
+ public PixelSize SelectionPixelSize
+ {
+ get
+ {
+ var rect = SelectionRegion;
+ return new PixelSize((int) Math.Floor(rect.Width), (int) Math.Floor(rect.Height));
}
}
@@ -765,23 +994,7 @@ namespace UVtools.WPF.Controls
private Vector _startScrollPosition;
private bool _isPanning;
private bool _isSelecting;
- private Bitmap _image;
private Bitmap _trackerImage;
- private bool _trackerImageAutoZoom = true;
- private byte _gridCellSize;
- private ISolidColorBrush _gridColor = Brushes.Gainsboro;
- private ISolidColorBrush _gridColorAlternate = Brushes.White;
- private bool _showGrid = true;
- private bool _autoPan = true;
- private MouseButtons _panWithMouseButtons = MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton;
- private bool _panWithArrows = true;
- private MouseButtons _selectWithMouseButtons = MouseButtons.LeftButton | MouseButtons.RightButton;
- private bool _invertMouse = false;
- private bool _autoCenter = true;
- private SizeModes _sizeMode = SizeModes.Normal;
- private ISolidColorBrush _selectionColor = new SolidColorBrush(new Color(127, 0, 128, 255));
- private Rect _selectionRegion = Rect.Empty;
- private SelectionModes _selectionMode = SelectionModes.None;
private bool _canRender = true;
private Point _pointerPosition;
@@ -796,11 +1009,13 @@ namespace UVtools.WPF.Controls
HorizontalScrollBar = this.FindControl<ScrollBar>("HorizontalScrollBar");
VerticalScrollBar = this.FindControl<ScrollBar>("VerticalScrollBar");
- ViewPortControl = this.FindControl<ContentControl>("ViewPort");
+ ViewPort = this.FindControl<ContentPresenter>("ViewPort");
+
+ SizeModeChanged();
HorizontalScrollBar.Scroll += ScrollBarOnScroll;
VerticalScrollBar.Scroll += ScrollBarOnScroll;
- ViewPortControl.PointerWheelChanged += FillContainerOnPointerWheelChanged;
+ ViewPort.PointerWheelChanged += FillContainerOnPointerWheelChanged;
}
private void ScrollBarOnScroll(object? sender, ScrollEventArgs e)
@@ -828,7 +1043,6 @@ namespace UVtools.WPF.Controls
private void FillContainerOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
- Debug.WriteLine("mouse whell");
e.Handled = true;
if (Image is null) return;
if (AllowZoom && SizeMode == SizeModes.Normal)
@@ -839,7 +1053,7 @@ namespace UVtools.WPF.Controls
// TODO: Really should update the source method to handle multiple increments rather than calling it multiple times
/*for (int i = 0; i < spins; i++)
{*/
- ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPortControl));
+ ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPort));
//}
}
}
@@ -865,10 +1079,10 @@ namespace UVtools.WPF.Controls
var result = action switch
{
ZoomActions.None => Zoom,
- ZoomActions.ZoomIn => ZoomLevels.NextZoom(Zoom),
- ZoomActions.ZoomOut => ZoomLevels.PreviousZoom(Zoom),
+ ZoomActions.ZoomIn => _zoomLevels.NextZoom(Zoom),
+ ZoomActions.ZoomOut => _zoomLevels.PreviousZoom(Zoom),
ZoomActions.ActualSize => 100,
- _ => throw new ArgumentOutOfRangeException(nameof(action)),
+ _ => throw new ArgumentOutOfRangeException(nameof(action), action, null),
};
return result;
}
@@ -895,8 +1109,8 @@ namespace UVtools.WPF.Controls
int currentZoom = Zoom;
int newZoom = GetZoomLevel(action);
- if (preservePosition && Zoom != currentZoom)
- CanRender = false;
+ /*if (preservePosition && Zoom != currentZoom)
+ CanRender = false;*/
RestoreSizeMode();
Zoom = newZoom;
@@ -949,7 +1163,7 @@ namespace UVtools.WPF.Controls
/// </param>
/// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
public Point PointToImage(double x, double y, bool fitToBounds = true)
- => PointToImage(x, y, fitToBounds);
+ => PointToImage(new Point(x, y), fitToBounds);
/// <summary>
/// Converts the given client size point to represent a coordinate on the source image.
@@ -962,7 +1176,7 @@ namespace UVtools.WPF.Controls
/// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
public Point PointToImage(int x, int y, bool fitToBounds = true)
{
- return PointToImage(x, y, fitToBounds);
+ return PointToImage(new Point(x, y), fitToBounds);
}
/// <summary>
@@ -985,10 +1199,11 @@ namespace UVtools.WPF.Controls
x = (point.X + Offset.X - viewport.X) / ZoomFactor;
y = (point.Y + Offset.Y - viewport.Y) / ZoomFactor;
+ var image = Image;
if (fitToBounds)
{
- x = x.Clamp(0, Image.Size.Width-1);
- y = y.Clamp(0, Image.Size.Height-1);
+ x = x.Clamp(0, image.Size.Width-1);
+ y = y.Clamp(0, image.Size.Height-1);
}
}
else
@@ -997,7 +1212,7 @@ namespace UVtools.WPF.Controls
y = 0;
}
- return new Point(x, y);
+ return new(x, y);
}
/// <summary>
@@ -1028,8 +1243,9 @@ namespace UVtools.WPF.Controls
public virtual void ScrollTo(Point imageLocation, Point relativeDisplayPoint)
{
//CanRender = false;
- var x = imageLocation.X * ZoomFactor - relativeDisplayPoint.X;
- var y = imageLocation.Y * ZoomFactor - relativeDisplayPoint.Y;
+ var zoomFactor = ZoomFactor;
+ var x = imageLocation.X * zoomFactor - relativeDisplayPoint.X;
+ var y = imageLocation.Y * zoomFactor - relativeDisplayPoint.Y;
_canRender = true;
@@ -1077,34 +1293,35 @@ namespace UVtools.WPF.Controls
}
/// <summary>
- /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
+ /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
/// </summary>
public virtual void ZoomToFit()
{
- if (Image is null) return;
+ var image = Image;
+ if (image is null) return;
double zoom;
double aspectRatio;
- if (Image.Size.Width > Image.Size.Height)
+ if (image.Size.Width > image.Size.Height)
{
- aspectRatio = Viewport.Width / Image.Size.Width;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
zoom = aspectRatio * 100.0;
- if (Viewport.Height < Image.Size.Height * zoom / 100.0)
+ if (ViewPortSize.Height < image.Size.Height * zoom / 100.0)
{
- aspectRatio = Viewport.Height / Image.Size.Height;
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
zoom = aspectRatio * 100.0;
}
}
else
{
- aspectRatio = Viewport.Height / Image.Size.Height;
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
zoom = aspectRatio * 100.0;
- if (Viewport.Width < Image.Size.Width * zoom / 100.0)
+ if (ViewPortSize.Width < image.Size.Width * zoom / 100.0)
{
- aspectRatio = Viewport.Width / Image.Size.Width;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
zoom = aspectRatio * 100.0;
}
}
@@ -1143,18 +1360,18 @@ namespace UVtools.WPF.Controls
/// </summary>
/// <param name="rectangle">The rectangle to fit the view port to.</param>
/// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public virtual void ZoomToRegion(Rectangle rectangle, double margin = 0) => ZoomToRegion(rectangle.ToAvalonia(), margin);
+ public void ZoomToRegion(Rectangle rectangle, double margin = 0) => ZoomToRegion(rectangle.ToAvalonia(), margin);
/// <summary>
/// Adjusts the view port to fit the given region
/// </summary>
/// <param name="rectangle">The rectangle to fit the view port to.</param>
/// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public virtual void ZoomToRegion(Rect rectangle, double margin = 0)
+ public void ZoomToRegion(Rect rectangle, double margin = 0)
{
if (margin > 0) rectangle = rectangle.Inflate(margin);
- var ratioX = Viewport.Width / rectangle.Width;
- var ratioY = Viewport.Height / rectangle.Height;
+ var ratioX = ViewPortSize.Width / rectangle.Width;
+ var ratioY = ViewPortSize.Height / rectangle.Height;
var zoomFactor = Math.Min(ratioX, ratioY);
var cx = rectangle.X + rectangle.Width / 2;
var cy = rectangle.Y + rectangle.Height / 2;
@@ -1178,14 +1395,14 @@ namespace UVtools.WPF.Controls
/// </summary>
/// <param name="imageLocation">The point of the image to attempt to center.</param>
public virtual void CenterAt(System.Drawing.Point imageLocation)
- => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(Viewport.Width / 2, Viewport.Height / 2));
+ => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
/// <summary>
/// Centers the given point in the image in the center of the control
/// </summary>
/// <param name="imageLocation">The point of the image to attempt to center.</param>
public virtual void CenterAt(Point imageLocation)
- => ScrollTo(imageLocation, new Point(Viewport.Width / 2, Viewport.Height / 2));
+ => ScrollTo(imageLocation, new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
/// <summary>
/// Centers the given point in the image in the center of the control
@@ -1228,13 +1445,13 @@ namespace UVtools.WPF.Controls
//var height = scaledImageHeight <= Viewport.Height ? Viewport.Height : scaledImageHeight;
bool changed = false;
- if (HorizontalScrollBar.Maximum != width)
+ if (Math.Abs(HorizontalScrollBar.Maximum - width) > 0.01)
{
HorizontalScrollBar.Maximum = width;
changed = true;
}
- if (VerticalScrollBar.Maximum != scaledImageHeight)
+ if (Math.Abs(VerticalScrollBar.Maximum - scaledImageHeight) > 0.01)
{
VerticalScrollBar.Maximum = height;
changed = true;
@@ -1304,7 +1521,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
public virtual Point GetScaledPoint(Point source)
{
- return new Point(source.X * ZoomFactor, source.Y * ZoomFactor);
+ return new(source.X * ZoomFactor, source.Y * ZoomFactor);
}
/// <summary>
@@ -1314,7 +1531,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="PointF"/> which has been scaled to match the current zoom level</returns>
public virtual PointF GetScaledPoint(PointF source)
{
- return new PointF((float)(source.X * this.ZoomFactor), (float)(source.Y * this.ZoomFactor));
+ return new((float)(source.X * ZoomFactor), (float)(source.Y * ZoomFactor));
}
/// <summary>
@@ -1372,7 +1589,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
public virtual Rect GetScaledRectangle(Rect source)
{
- return new Rect(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
+ return new(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
}
/// <summary>
@@ -1382,7 +1599,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
public virtual RectangleF GetScaledRectangle(RectangleF source)
{
- return new RectangleF((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
+ return new((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
}
/// <summary>
@@ -1393,7 +1610,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
public SizeF GetScaledSize(float width, float height)
{
- return this.GetScaledSize(new SizeF(width, height));
+ return GetScaledSize(new SizeF(width, height));
}
/// <summary>
@@ -1404,7 +1621,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
public Size GetScaledSize(int width, int height)
{
- return this.GetScaledSize(new Size(width, height));
+ return GetScaledSize(new Size(width, height));
}
/// <summary>
@@ -1414,7 +1631,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
public virtual SizeF GetScaledSize(SizeF source)
{
- return new SizeF((float)(source.Width * this.ZoomFactor), (float)(source.Height * this.ZoomFactor));
+ return new((float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
}
/// <summary>
@@ -1424,7 +1641,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
public virtual Size GetScaledSize(Size source)
{
- return new Size(source.Width * ZoomFactor, source.Height * ZoomFactor);
+ return new(source.Width * ZoomFactor, source.Height * ZoomFactor);
}
/// <summary>
@@ -1433,12 +1650,13 @@ namespace UVtools.WPF.Controls
/// <exception cref="System.InvalidOperationException">Thrown if no image is currently set</exception>
public virtual void SelectAll()
{
- if (Image is null) return;
- SelectionRegion = new Rect(0, 0, Image.Size.Width, Image.Size.Height);
+ var image = Image;
+ if (image is null) return;
+ SelectionRegion = new Rect(0, 0, image.Size.Width, image.Size.Height);
}
/// <summary>
- /// Clears any existing selection region
+ /// Clears any existing selection region
/// </summary>
public virtual void SelectNone()
{
@@ -1454,25 +1672,29 @@ namespace UVtools.WPF.Controls
public override void Render(DrawingContext context)
{
- Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
+ //Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
base.Render(context);
// Draw Grid
- if (ShowGrid)
+ var gridCellSize = GridCellSize;
+ if (ShowGrid & gridCellSize > 0 && (!IsHorizontalBarVisible || !IsVerticalBarVisible))
{
// draw the background
- var currentColor = GridColor;
- for (int y = 0; y < Viewport.Height; y += GridCellSize)
+ var gridColor = GridColor;
+ var altColor = GridColorAlternate;
+ var currentColor = gridColor;
+ for (int y = 0; y < ViewPortSize.Height; y += gridCellSize)
{
var firstRowColor = currentColor;
- for (int x = 0; x < Viewport.Width; x += GridCellSize)
+
+ for (int x = 0; x < ViewPortSize.Width; x += gridCellSize)
{
- context.FillRectangle(currentColor, new Rect(x, y, GridCellSize, GridCellSize));
- currentColor = ReferenceEquals(currentColor, GridColor) ? GridColorAlternate : GridColor;
+ context.FillRectangle(currentColor, new Rect(x, y, gridCellSize, gridCellSize));
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
- if (firstRowColor == currentColor)
- currentColor = ReferenceEquals(currentColor, GridColor) ? GridColorAlternate : GridColor;
+ if (Equals(firstRowColor, currentColor))
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
}
@@ -1481,18 +1703,22 @@ namespace UVtools.WPF.Controls
context.FillRectangle(Background, new Rect(0, 0, Viewport.Width, Viewport.Height));
}*/
- if (Image is null) return;
+ var image = Image;
+ if (image is null) return;
// Draw iamge
- context.DrawImage(_image,
+ context.DrawImage(image,
GetSourceImageRegion(),
GetImageViewPort()
);
+ var zoomFactor = ZoomFactor;
+
+
if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0)
{
var destSize = TrackerImageAutoZoom
- ? new Size(_trackerImage.Size.Width * ZoomFactor, _trackerImage.Size.Height * ZoomFactor)
- : _image.Size;
+ ? new Size(_trackerImage.Size.Width * zoomFactor, _trackerImage.Size.Height * zoomFactor)
+ : image.Size;
var destPos = new Point(
_pointerPosition.X - destSize.Width / 2,
@@ -1505,20 +1731,19 @@ namespace UVtools.WPF.Controls
//SkiaContext.SkCanvas.dr
// Draw pixel grid
- var pixelSize = ZoomFactor;
- if (pixelSize > PixelGridThreshold)
+ if (zoomFactor > PixelGridZoomThreshold && SizeMode == SizeModes.Normal)
{
var viewport = GetImageViewPort();
- var offsetX = Offset.X % pixelSize;
- var offsetY = Offset.Y % pixelSize;
+ var offsetX = Offset.X % zoomFactor;
+ var offsetY = Offset.Y % zoomFactor;
Pen pen = new(PixelGridColor);
- for (double x = viewport.X + pixelSize - offsetX; x < viewport.Right; x += pixelSize)
+ for (double x = viewport.X + zoomFactor - offsetX; x < viewport.Right; x += zoomFactor)
{
context.DrawLine(pen, new Avalonia.Point(x, viewport.X), new Avalonia.Point(x, viewport.Bottom));
}
- for (double y = viewport.Y + pixelSize - offsetY; y < viewport.Bottom; y += pixelSize)
+ for (double y = viewport.Y + zoomFactor - offsetY; y < viewport.Bottom; y += zoomFactor)
{
context.DrawLine(pen, new Avalonia.Point(viewport.Y, y), new Avalonia.Point(viewport.Right, y));
}
@@ -1529,9 +1754,10 @@ namespace UVtools.WPF.Controls
if (!SelectionRegion.IsEmpty)
{
var rect = GetOffsetRectangle(SelectionRegion);
- context.FillRectangle(SelectionColor, rect);
- Color solidColor = Color.FromArgb(255, SelectionColor.Color.R, SelectionColor.Color.G, SelectionColor.Color.B);
- context.DrawRectangle(new Pen(solidColor.ToUint32()), rect);
+ var selectionColor = SelectionColor;
+ context.FillRectangle(selectionColor, rect);
+ Color color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B);
+ context.DrawRectangle(new Pen(color.ToUint32()), rect);
}
}
@@ -1544,7 +1770,7 @@ namespace UVtools.WPF.Controls
{
var offset = GetOffsetPoint(new Point (source.X, source.Y));
- return new Point((int)offset.X, (int)offset.Y);
+ return new((int)offset.X, (int)offset.Y);
}
/// <summary>
@@ -1581,7 +1807,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left + Offset.X;
var offsetY = viewport.Top + Offset.Y;
- return new Point(scaled.X + offsetX, scaled.Y + offsetY);
+ return new(scaled.X + offsetX, scaled.Y + offsetY);
}
/// <summary>
@@ -1596,7 +1822,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left - Offset.X;
var offsetY = viewport.Top - Offset.Y;
- return new Rect(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
+ return new(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
}
/// <summary>
@@ -1609,7 +1835,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
public Rectangle GetOffsetRectangle(int x, int y, int width, int height)
{
- return this.GetOffsetRectangle(new Rectangle(x, y, width, height));
+ return GetOffsetRectangle(new Rectangle(x, y, width, height));
}
/// <summary>
@@ -1637,8 +1863,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left + Offset.X;
var offsetY = viewport.Top + Offset.Y;
- return new Rectangle(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)),
- new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
+ return new(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)), new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
}
/// <summary>
@@ -1650,7 +1875,8 @@ namespace UVtools.WPF.Controls
/// </returns>
public Rectangle FitRectangle(Rectangle rectangle)
{
- if (Image is null) return Rectangle.Empty;
+ var image = Image;
+ if (image is null) return Rectangle.Empty;
var x = rectangle.X;
var y = rectangle.Y;
var w = rectangle.Width;
@@ -1666,17 +1892,17 @@ namespace UVtools.WPF.Controls
y = 0;
}
- if (x + w > Image.Size.Width)
+ if (x + w > image.Size.Width)
{
- w = (int)(Image.Size.Width - x);
+ w = (int)(image.Size.Width - x);
}
- if (y + h > Image.Size.Height)
+ if (y + h > image.Size.Height)
{
- h = (int)(Image.Size.Height - y);
+ h = (int)(image.Size.Height - y);
}
- return new Rectangle(x, y, w, h);
+ return new(x, y, w, h);
}
/// <summary>
@@ -1688,7 +1914,8 @@ namespace UVtools.WPF.Controls
/// </returns>
public Rect FitRectangle(Rect rectangle)
{
- if (Image is null) return Rect.Empty;
+ var image = Image;
+ if (image is null) return Rect.Empty;
var x = rectangle.X;
var y = rectangle.Y;
var w = rectangle.Width;
@@ -1706,94 +1933,119 @@ namespace UVtools.WPF.Controls
y = 0;
}
- if (x + w > Image.Size.Width)
+ if (x + w > image.Size.Width)
{
- w = Image.Size.Width - x;
+ w = image.Size.Width - x;
}
- if (y + h > Image.Size.Height)
+ if (y + h > image.Size.Height)
{
- h = Image.Size.Height - y;
+ h = image.Size.Height - y;
}
- return new Rect(x, y, w, h);
+ return new(x, y, w, h);
}
/// <summary>
/// Gets the source image region.
/// </summary>
/// <returns></returns>
- public virtual Rect GetSourceImageRegion()
+ public Rect GetSourceImageRegion()
{
- if (Image is null) return Rect.Empty;
+ var image = Image;
+ if (image is null) return Rect.Empty;
- if (SizeMode != SizeModes.Stretch)
+ switch (SizeMode)
{
- var viewPort = GetImageViewPort();
- double sourceLeft = (Offset.X / ZoomFactor);
- double sourceTop = (Offset.Y / ZoomFactor);
- double sourceWidth = (viewPort.Width / ZoomFactor);
- double sourceHeight = (viewPort.Height / ZoomFactor);
-
- return new Rect(sourceLeft, sourceTop, sourceWidth, sourceHeight);
+ case SizeModes.Normal:
+ var offset = Offset;
+ var viewPort = GetImageViewPort();
+ var zoomFactor = ZoomFactor;
+ double sourceLeft = (offset.X / zoomFactor);
+ double sourceTop = (offset.Y / zoomFactor);
+ double sourceWidth = (viewPort.Width / zoomFactor);
+ double sourceHeight = (viewPort.Height / zoomFactor);
+
+ return new(sourceLeft, sourceTop, sourceWidth, sourceHeight);
}
- return new Rect(0, 0, Image.Size.Width, Image.Size.Height);
+ return new(0, 0, image.Size.Width, image.Size.Height);
+
}
/// <summary>
/// Gets the image view port.
/// </summary>
/// <returns></returns>
- public virtual Rect GetImageViewPort()
+ public Rect GetImageViewPort()
{
- if (Viewport.Width == 0 && Viewport.Height == 0) return Rect.Empty;
+ if (ViewPortSize.Width == 0 && ViewPortSize.Height == 0) return Rect.Empty;
double xOffset = 0;
double yOffset = 0;
- double width;
- double height;
+ double width = 0;
+ double height = 0;
- if (SizeMode != SizeModes.Stretch)
+ switch (SizeMode)
{
- if (AutoCenter)
- {
- xOffset = (!IsHorizontalBarVisible ? (Viewport.Width - ScaledImageWidth) / 2 : 0);
- yOffset = (!IsVerticalBarVisible ? (Viewport.Height - ScaledImageHeight) / 2 : 0);
- }
+ case SizeModes.Normal:
+ if (AutoCenter)
+ {
+ xOffset = (!IsHorizontalBarVisible ? (ViewPortSize.Width - ScaledImageWidth) / 2 : 0);
+ yOffset = (!IsVerticalBarVisible ? (ViewPortSize.Height - ScaledImageHeight) / 2 : 0);
+ }
- width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), Viewport.Width);
- height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), Viewport.Height);
- }
- else
- {
- width = Viewport.Width;
- height = Viewport.Height;
+ width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), ViewPortSize.Width);
+ height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), ViewPortSize.Height);
+ break;
+ case SizeModes.Stretch:
+ width = ViewPortSize.Width;
+ height = ViewPortSize.Height;
+ break;
+ case SizeModes.Fit:
+ var image = Image;
+ double scaleFactor = Math.Min(ViewPortSize.Width / image.Size.Width, ViewPortSize.Height / image.Size.Height);
+
+ width = Math.Floor(image.Size.Width * scaleFactor);
+ height = Math.Floor(image.Size.Height * scaleFactor);
+
+ if (AutoCenter)
+ {
+ xOffset = (ViewPortSize.Width - width) / 2;
+ yOffset = (ViewPortSize.Height - height) / 2;
+ }
+
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
}
- return new Rect(xOffset, yOffset, width, height);
+ return new(xOffset, yOffset, width, height);
}
/// <summary>
- /// Gets the width of the scaled image.
+ /// Gets the width of the scaled image.
/// </summary>
/// <value>The width of the scaled image.</value>
- protected virtual double ScaledImageWidth => Image.Size.Width * ZoomFactor;
+ protected double ScaledImageWidth => Image.Size.Width * ZoomFactor;
/// <summary>
- /// Gets the height of the scaled image.
+ /// Gets the height of the scaled image.
/// </summary>
/// <value>The height of the scaled image.</value>
- protected virtual double ScaledImageHeight => Image.Size.Height * ZoomFactor;
+ protected double ScaledImageHeight => Image.Size.Height * ZoomFactor;
- public double ZoomFactor => _zoom / 100.0;
+ /// <summary>
+ /// Gets the zoom factor, the zoom / 100
+ /// </summary>
+ public double ZoomFactor => Zoom / 100.0;
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.Handled
- || IsPanning
- || IsSelecting
+ || _isPanning
+ || _isSelecting
|| Image is null) return;
var pointer = e.GetCurrentPoint(this);
@@ -1816,6 +2068,7 @@ namespace UVtools.WPF.Controls
pointer.Properties.IsRightButtonPressed && (PanWithMouseButtons & MouseButtons.RightButton) != 0
)
|| !AutoPan
+ || SizeMode != SizeModes.Normal
) return;
@@ -1824,8 +2077,8 @@ namespace UVtools.WPF.Controls
var location = pointer.Position;
- if (location.X > Viewport.Width) return;
- if (location.Y > Viewport.Height) return;
+ if (location.X > ViewPortSize.Width) return;
+ if (location.Y > ViewPortSize.Height) return;
_startMousePosition = location;
}
@@ -1854,18 +2107,18 @@ namespace UVtools.WPF.Controls
var pointer = e.GetCurrentPoint(this);
PointerPosition = pointer.Position;
- if (!IsPanning && !IsSelecting)
+ if (!_isPanning && !_isSelecting)
{
TriggerRender(true);
return;
}
- if (IsPanning)
+ if (_isPanning)
{
double x;
double y;
- if (!InvertMouse)
+ if (!InvertMousePan)
{
x = _startScrollPosition.X + (_startMousePosition.X - _pointerPosition.X);
y = _startScrollPosition.Y + (_startMousePosition.Y - _pointerPosition.Y);
@@ -1878,7 +2131,7 @@ namespace UVtools.WPF.Controls
Offset = new Vector(x, y);
}
- else if (IsSelecting)
+ else if (_isSelecting)
{
double x;
double y;
@@ -1912,10 +2165,11 @@ namespace UVtools.WPF.Controls
x -= imageOffset.X - Offset.X;
y -= imageOffset.Y - Offset.Y;
- x /= ZoomFactor;
- y /= ZoomFactor;
- w /= ZoomFactor;
- h /= ZoomFactor;
+ var zoomFactor = ZoomFactor;
+ x /= zoomFactor;
+ y /= zoomFactor;
+ w /= zoomFactor;
+ h /= zoomFactor;
if (w != 0 && h != 0)
{
@@ -1925,6 +2179,38 @@ namespace UVtools.WPF.Controls
e.Handled = true;
}
+
+ public Bitmap GetSelectedBitmap()
+ {
+ var image = ImageAsWriteableBitmap;
+ if (image is null || !HaveSelection) return null;
+ var selection = SelectionRegionNet;
+ var pixelSize = SelectionPixelSize;
+ using var frameBuffer = image.Lock();
+
+ var newBitmap = new WriteableBitmap(pixelSize, image.Dpi, frameBuffer.Format, AlphaFormat.Unpremul);
+ using var newFrameBuffer = newBitmap.Lock();
+
+ int i = 0;
+
+ unsafe
+ {
+ var inputPixels = (uint*) (void*) frameBuffer.Address;
+ var targetPixels = (uint*) (void*) newFrameBuffer.Address;
+
+ for (int y = selection.Y; y < selection.Bottom; y++)
+ {
+ var thisY = y * frameBuffer.Size.Width;
+ for (int x = selection.X; x < selection.Right; x++)
+ {
+ targetPixels[i++] = inputPixels[thisY + x];
+ }
+ }
+ }
+
+ return newBitmap;
+ }
+
#endregion
}
}
diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
index f71bef9..2ab8826 100644
--- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
@@ -11,6 +11,7 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
+using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
namespace UVtools.WPF.Controls.Tools
@@ -117,6 +118,7 @@ namespace UVtools.WPF.Controls.Tools
if (Operation.Modifiers is null || Operation.Modifiers.Length == 0)
{
CanRun = false;
+ App.MainWindow.MessageBoxError("No available properties to edit on this file format.", BaseOperation.Title).GetAwaiter();
return;
}
diff --git a/UVtools.WPF/Extensions/BitmapExtension.cs b/UVtools.WPF/Extensions/BitmapExtension.cs
index ae5f5ce..748f190 100644
--- a/UVtools.WPF/Extensions/BitmapExtension.cs
+++ b/UVtools.WPF/Extensions/BitmapExtension.cs
@@ -118,9 +118,7 @@ namespace UVtools.WPF.Extensions
using var lockBuffer = writableBitmap.Lock();
-
-
-
+
unsafe
{
diff --git a/UVtools.WPF/Extensions/DrawingExtensions.cs b/UVtools.WPF/Extensions/DrawingExtensions.cs
index b848c48..dbaea1e 100644
--- a/UVtools.WPF/Extensions/DrawingExtensions.cs
+++ b/UVtools.WPF/Extensions/DrawingExtensions.cs
@@ -15,22 +15,22 @@ namespace UVtools.WPF.Extensions
{
public static Avalonia.Media.Color ToAvalonia(this System.Drawing.Color color)
{
- return new Avalonia.Media.Color(color.A, color.R, color.G, color.B);
+ return new(color.A, color.R, color.G, color.B);
}
public static System.Drawing.Color ToDotNet(this Avalonia.Media.Color color)
{
- return System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B);
+ return Color.FromArgb(color.A, color.R, color.G, color.B);
}
public static Rect ToAvalonia(this Rectangle rectangle)
{
- return new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return new(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
}
public static Rectangle ToDotNet(this Rect rectangle)
{
- return new Rectangle((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
+ return new((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
}
}
}
diff --git a/UVtools.WPF/LayerCache.cs b/UVtools.WPF/LayerCache.cs
index d68f9a0..79bd632 100644
--- a/UVtools.WPF/LayerCache.cs
+++ b/UVtools.WPF/LayerCache.cs
@@ -68,12 +68,10 @@ namespace UVtools.WPF
{
get
{
- using (var framebuffer = Bitmap.Lock())
- {
- var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
- framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
- return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
- }
+ using var framebuffer = Bitmap.Lock();
+ var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
+ framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
+ return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
}
}
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index 5553887..b837352 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -93,62 +93,54 @@ namespace UVtools.WPF
LayerNavigationTooltipBorder = this.FindControl<Border>("Layer.Navigation.Tooltip.Border");
_issuesSliderCanvas = this.Find<Canvas>("Layer.Navigation.IssuesCanvas");
-
-
_showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
_showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
_showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
_showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
-
+
LayerImageBox.ZoomLevels = new AdvancedImageBox.ZoomLevelCollection(AppSettings.ZoomLevels);
- LayerImageBox.PropertyChanged += (sender, e) =>
+
+ LayerImageBox.GetObservable(AdvancedImageBox.ZoomProperty).Subscribe(zoom =>
{
- if (e.PropertyName == nameof(LayerImageBox.Zoom))
+ var oldZoom = LayerImageBox.OldZoom;
+ RaisePropertyChanged(nameof(LayerZoomStr));
+ AddLogVerbose($"Zoomed from {oldZoom} to {zoom}");
+
+ if (_showLayerImageCrosshairs &&
+ Issues.Count > 0 &&
+ (oldZoom < 50 &&
+ zoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
+ || oldZoom > 100 && zoom <= 100
+ || oldZoom is >= 50 and <= 100 && (zoom is < 50 or > 100)
+ || oldZoom <= AppSettings.CrosshairFadeLevel &&
+ zoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
+ || oldZoom > AppSettings.CrosshairFadeLevel && zoom <= AppSettings.CrosshairFadeLevel)
+
+ )
{
- RaisePropertyChanged(nameof(LayerZoomStr));
- AddLogVerbose($"Zoomed from {LayerImageBox.OldZoom} to {LayerImageBox.Zoom}");
-
- if (ShowLayerImageCrosshairs &&
- Issues.Count > 0 &&
- (LayerImageBox.OldZoom < 50 &&
- LayerImageBox.Zoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
- || LayerImageBox.OldZoom > 100 && LayerImageBox.Zoom <= 100
- || LayerImageBox.OldZoom >= 50 && LayerImageBox.OldZoom <= 100 && (LayerImageBox.Zoom < 50 || LayerImageBox.Zoom > 100)
- || LayerImageBox.OldZoom <= AppSettings.CrosshairFadeLevel &&
- LayerImageBox.Zoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
- || LayerImageBox.OldZoom > AppSettings.CrosshairFadeLevel && LayerImageBox.Zoom <= AppSettings.CrosshairFadeLevel)
-
- )
+ if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
{
- if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
- {
- if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<LayerIssue>().Any(
- issue => // Find a valid candidate to update layer preview, otherwise quit
- issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
- issue.Type != LayerIssue.IssueType.TouchingBound)) return;
- }
- else
- {
- if (!Issues.Any(
- issue => // Find a valid candidate to update layer preview, otherwise quit
- issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
- issue.Type != LayerIssue.IssueType.TouchingBound)) return;
- }
-
- // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
- // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
- ShowLayer();
+ if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<LayerIssue>().Any(
+ issue => // Find a valid candidate to update layer preview, otherwise quit
+ issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
+ issue.Type != LayerIssue.IssueType.TouchingBound)) return;
+ }
+ else
+ {
+ if (!Issues.Any(
+ issue => // Find a valid candidate to update layer preview, otherwise quit
+ issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
+ issue.Type != LayerIssue.IssueType.TouchingBound)) return;
}
- return;
- }
-
- if (e.PropertyName == nameof(LayerImageBox.SelectionRegion))
- {
- RaisePropertyChanged(nameof(LayerROIStr));
+ // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
+ // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
+ ShowLayer();
}
+ });
- };
+ LayerImageBox.GetObservable(AdvancedImageBox.SelectionRegionProperty)
+ .Subscribe(rect => RaisePropertyChanged(nameof(LayerROIStr)));
LayerImageBox.PointerMoved += LayerImageBoxOnPointerMoved;
LayerImageBox.KeyUp += LayerImageBox_KeyUp;
@@ -1248,7 +1240,7 @@ namespace UVtools.WPF
if (!_showLayerImageFlipped) return;
if (_showLayerImageFlippedHorizontally)
{
- rectangle.Location = new Point(LayerCache.Image.Width - 1 - rectangle.Right, rectangle.Y);
+ rectangle.Location = new Point(LayerCache.Image.Width - rectangle.Right, rectangle.Y);
}
if (_showLayerImageFlippedVertically)
@@ -1801,7 +1793,8 @@ namespace UVtools.WPF
public async void SaveCurrentLayerImage()
{
- SaveFileDialog dialog = new SaveFileDialog
+ if (!IsFileLoaded) return;
+ SaveFileDialog dialog = new()
{
Filters = Helpers.PngFileFilter,
DefaultExtension = ".png",
@@ -1814,6 +1807,22 @@ namespace UVtools.WPF
LayerCache.ImageBgr.Save(result);
}
+ public async void SaveCurrentROIImage()
+ {
+ if (!IsFileLoaded || !LayerImageBox.HaveSelection) return;
+ SaveFileDialog dialog = new()
+ {
+ Filters = Helpers.PngFileFilter,
+ DefaultExtension = ".png",
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}_ROI.png"
+ };
+
+ var result = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
+
+ LayerImageBox.GetSelectedBitmap()?.Save(result);
+ }
+
const byte _pixelEditorCursorMinDiamater = 10;
public void UpdatePixelEditorCursor()
{
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index ce4c87d..50fd61d 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -284,11 +284,13 @@
VerticalAlignment="Center">
<TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/>
- <TextBlock Text=" | "/>
- <TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}"
+ Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
- <TextBlock Text=" | "/>
- <TextBlock Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}"
+ Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}"
@@ -1875,8 +1877,20 @@
ToolTip.Tip="Save layer image to a file"
VerticalAlignment="Stretch"
Margin="1,0,0,0">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png"/>
+ <Button.ContextMenu>
+ <ContextMenu PlacementMode="Bottom">
+ <MenuItem
+ Command="{Binding SaveCurrentROIImage}"
+ Header="Save the selected region (ROI)">
+ <MenuItem.Icon>
+ <Image Source="/Assets/Icons/object-group-16x16.png"/>
+ </MenuItem.Icon>
+ </MenuItem>
+ </ContextMenu>
+ </Button.ContextMenu>
+ <StackPanel Orientation="Horizontal">
+ <Image Source="/Assets/Icons/save-16x16.png"/>
+ <TextBlock Text=" ⮟"/>
</StackPanel>
</Button>
@@ -1891,9 +1905,8 @@
>
<uc:AdvancedImageBox
ShowGrid="{Binding Settings.LayerPreview.ShowBackgroudGrid}"
- GridCellSize="15"
- Name="LayerImage"
- />
+ GridCellSize="15"
+ Name="LayerImage"/>
</Border>
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index b8c454e..de6127a 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -508,7 +508,7 @@ namespace UVtools.WPF
var clientSizeObs = this.GetObservable(ClientSizeProperty);
clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
var windowStateObs = this.GetObservable(WindowStateProperty);
- windowStateObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
+ windowStateObs.Subscribe(windowsState => UpdateLayerTrackerHighlightIssues());
DataContext = this;
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 83ba22a..89cceaa 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -116,7 +116,6 @@ namespace UVtools.WPF.Structures
Debug.WriteLine($"Version checker: v{App.VersionStr} <=> v{tag_name}");
Version checkVersion = new(tag_name);
Changelog = json.body;
-
//if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
if (App.Version.CompareTo(checkVersion) < 0)
{
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index f5b81db..3eb829a 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.11.2</Version>
+ <Version>2.12.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index c66f81e..38ebee4 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -224,7 +224,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush TooltipOverlayBackgroundBrush
{
- get => new SolidColorBrush(_tooltipOverlayBackgroundColor.ToAvalonia());
+ get => new(_tooltipOverlayBackgroundColor.ToAvalonia());
set => TooltipOverlayBackgroundColor = new Color(value);
}
@@ -247,7 +247,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush VolumeBoundsOutlineBrush
{
- get => new SolidColorBrush(_volumeBoundsOutlineColor.ToAvalonia());
+ get => new(_volumeBoundsOutlineColor.ToAvalonia());
set => VolumeBoundsOutlineColor = new Color(value);
}
@@ -276,7 +276,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush LayerBoundsOutlineBrush
{
- get => new SolidColorBrush(_layerBoundsOutlineColor.ToAvalonia());
+ get => new(_layerBoundsOutlineColor.ToAvalonia());
set => LayerBoundsOutlineColor = new Color(value);
}
@@ -305,7 +305,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush HollowOutlineBrush
{
- get => new SolidColorBrush(_hollowOutlineColor.ToAvalonia());
+ get => new(_hollowOutlineColor.ToAvalonia());
set => HollowOutlineColor = new Color(value);
}
@@ -334,7 +334,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush MaskOutlineBrush
{
- get => new SolidColorBrush(_maskOutlineColor.ToAvalonia());
+ get => new(_maskOutlineColor.ToAvalonia());
set => MaskOutlineColor = new Color(value);
}
@@ -363,7 +363,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush PreviousLayerDifferenceBrush
{
- get => new SolidColorBrush(_previousLayerDifferenceColor.ToAvalonia());
+ get => new(_previousLayerDifferenceColor.ToAvalonia());
set => PreviousLayerDifferenceColor = new Color(value);
}
@@ -380,7 +380,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush NextLayerDifferenceBrush
{
- get => new SolidColorBrush(_nextLayerDifferenceColor.ToAvalonia());
+ get => new(_nextLayerDifferenceColor.ToAvalonia());
set => NextLayerDifferenceColor = new Color(value);
}
@@ -397,7 +397,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush BothLayerDifferenceBrush
{
- get => new SolidColorBrush(_bothLayerDifferenceColor.ToAvalonia());
+ get => new(_bothLayerDifferenceColor.ToAvalonia());
set => BothLayerDifferenceColor = new Color(value);
}
@@ -426,7 +426,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush IslandBrush
{
- get => new SolidColorBrush(_islandColor.ToAvalonia());
+ get => new(_islandColor.ToAvalonia());
set => IslandColor = new Color(value);
}
@@ -443,7 +443,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush IslandHighlightBrush
{
- get => new SolidColorBrush(_islandHighlightColor.ToAvalonia());
+ get => new(_islandHighlightColor.ToAvalonia());
set => IslandHighlightColor = new Color(value);
}
@@ -460,7 +460,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush OverhangBrush
{
- get => new SolidColorBrush(_overhangColor.ToAvalonia());
+ get => new(_overhangColor.ToAvalonia());
set => OverhangColor = new Color(value);
}
@@ -477,7 +477,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush OverhangHighlightBrush
{
- get => new SolidColorBrush(_overhangHighlightColor.ToAvalonia());
+ get => new(_overhangHighlightColor.ToAvalonia());
set => OverhangHighlightColor = new Color(value);
}
@@ -494,7 +494,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush ResinTrapBrush
{
- get => new SolidColorBrush(_resinTrapColor.ToAvalonia());
+ get => new(_resinTrapColor.ToAvalonia());
set => ResinTrapColor = new Color(value);
}
@@ -511,7 +511,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush ResinTrapHighlightBrush
{
- get => new SolidColorBrush(_resinTrapHighlightColor.ToAvalonia());
+ get => new(_resinTrapHighlightColor.ToAvalonia());
set => ResinTrapHighlightColor = new Color(value);
}
public Color TouchingBoundsColor
@@ -527,7 +527,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush TouchingBoundsBrush
{
- get => new SolidColorBrush(_touchingBoundsColor.ToAvalonia());
+ get => new(_touchingBoundsColor.ToAvalonia());
set => TouchingBoundsColor = new Color(value);
}
@@ -544,7 +544,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush CrosshairBrush
{
- get => new SolidColorBrush(_crosshairColor.ToAvalonia());
+ get => new(_crosshairColor.ToAvalonia());
set => CrosshairColor = new Color(value);
}
@@ -877,15 +877,15 @@ namespace UVtools.WPF
[Serializable]
public sealed class PixelEditorUserSettings : BindableBase
{
- private Color _addPixelColor = new Color(255, 144, 238, 144);
- private Color _addPixelHighlightColor = new Color(255, 0, 255, 0);
- private Color _removePixelColor = new Color(255, 219, 112, 147);
- private Color _removePixelHighlightColor = new Color(255, 139, 0, 0);
- private Color _supportsColor = new Color(255, 0, 255, 255);
- private Color _supportsHighlightColor = new Color(255, 0, 139, 139);
- private Color _drainHoleColor = new Color(255, 142, 69, 133);
- private Color _drainHoleHighlightColor = new Color(255, 159, 0, 197);
- private Color _cursorColor = new Color(150, 52, 152, 219);
+ private Color _addPixelColor = new(255, 144, 238, 144);
+ private Color _addPixelHighlightColor = new(255, 0, 255, 0);
+ private Color _removePixelColor = new(255, 219, 112, 147);
+ private Color _removePixelHighlightColor = new(255, 139, 0, 0);
+ private Color _supportsColor = new(255, 0, 255, 255);
+ private Color _supportsHighlightColor = new(255, 0, 139, 139);
+ private Color _drainHoleColor = new(255, 142, 69, 133);
+ private Color _drainHoleHighlightColor = new(255, 159, 0, 197);
+ private Color _cursorColor = new(150, 52, 152, 219);
private bool _partialUpdateIslandsOnEditing = true;
private bool _closeEditorOnApply;
@@ -902,7 +902,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush AddPixelBrush
{
- get => new SolidColorBrush(_addPixelColor.ToAvalonia());
+ get => new(_addPixelColor.ToAvalonia());
set => AddPixelColor = new Color(value);
}
@@ -919,7 +919,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush AddPixelHighlightBrush
{
- get => new SolidColorBrush(_addPixelHighlightColor.ToAvalonia());
+ get => new(_addPixelHighlightColor.ToAvalonia());
set => AddPixelHighlightColor = new Color(value);
}
@@ -936,7 +936,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush RemovePixelBrush
{
- get => new SolidColorBrush(_removePixelColor.ToAvalonia());
+ get => new(_removePixelColor.ToAvalonia());
set => RemovePixelColor = new Color(value);
}
@@ -953,7 +953,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush RemovePixelHighlightBrush
{
- get => new SolidColorBrush(_removePixelHighlightColor.ToAvalonia());
+ get => new(_removePixelHighlightColor.ToAvalonia());
set => RemovePixelHighlightColor = new Color(value);
}
@@ -970,7 +970,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush SupportsBrush
{
- get => new SolidColorBrush(_supportsColor.ToAvalonia());
+ get => new(_supportsColor.ToAvalonia());
set => SupportsColor = new Color(value);
}
@@ -987,7 +987,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush SupportsHighlightBrush
{
- get => new SolidColorBrush(_supportsHighlightColor.ToAvalonia());
+ get => new(_supportsHighlightColor.ToAvalonia());
set => SupportsHighlightColor = new Color(value);
}
@@ -1004,7 +1004,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush DrainHoleBrush
{
- get => new SolidColorBrush(_drainHoleColor.ToAvalonia());
+ get => new(_drainHoleColor.ToAvalonia());
set => DrainHoleColor = new Color(value);
}
@@ -1021,7 +1021,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush DrainHoleHighlightBrush
{
- get => new SolidColorBrush(_drainHoleHighlightColor.ToAvalonia());
+ get => new(_drainHoleHighlightColor.ToAvalonia());
set => DrainHoleHighlightColor = new Color(value);
}
@@ -1038,7 +1038,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush CursorBrush
{
- get => new SolidColorBrush(_cursorColor.ToAvalonia());
+ get => new(_cursorColor.ToAvalonia());
set => CursorColor = new Color(value);
}
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml
index 5b244dc..15f20dc 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml
+++ b/UVtools.WPF/Windows/ToolWindow.axaml
@@ -224,6 +224,7 @@
<StackPanel Margin="15" Spacing="5">
<TextBlock VerticalAlignment="Center"
+ IsVisible="{Binding IsROIVisible}"
Text="{Binding ROI, StringFormat=Region: \{0\}}" />
<CheckBox
diff --git a/build/CreateRelease.WPF.ps1 b/build/CreateRelease.WPF.ps1
index e2f658c..24e73e9 100644
--- a/build/CreateRelease.WPF.ps1
+++ b/build/CreateRelease.WPF.ps1
@@ -225,7 +225,7 @@ if($enableMSI)
# Clean and build MSI
Remove-Item "$installer\obj" -Recurse -ErrorAction Ignore
Remove-Item "$installer\bin" -Recurse -ErrorAction Ignore
- iex "& $msbuild $installer\$installer.wixproj"
+ Invoke-Expression "& $msbuild $installer\$installer.wixproj"
}
Write-Output "Coping $runtime MSI to: $msiTargetFile"