diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-01-03 03:47:20 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-01-03 03:47:20 +0300 |
commit | a7d15a24d0179b06b64daf1be5f4e08c488cb930 (patch) | |
tree | b3823c82f6031da7a6c10571945973a88390778a | |
parent | 92588fd1592236d5fef67391e55cf05b6111ed9b (diff) |
v2.1.1v2.1.1
* (Add) About box: Primary screen identifier and open on screen identifier
* (Add) Calibrator - External tests
* (Change) Rewrite 'Action - Import Layer(s)' to support file formats and add the followig importation types:
* **Insert:** Insert layers. (Requires images with bounds equal or less than file resolution)
* **Replace:** Replace layers. (Requires images with bounds equal or less than file resolution)
* **Stack:** Stack layers content. (Requires images with bounds equal or less than file resolution)
* **Merge:** Merge/Sum layers content. (Requires images with same resolution)
* **Subtract:** Subtract layers content. (Requires images with same resolution)
* **BitwiseAnd:** Perform a 'bitwise and' operation over layer pixels. (Requires images with same resolution)
* **BitwiseOr:** Perform a 'bitwise or' operation over layer pixels. (Requires images with same resolution)
* **BitwiseXOr:** Perform a 'bitwise xor' operation over layer pixels. (Requires images with same resolution)
* (Change) Icon for Tool - Raft Relief
* (Change) Windows and dialogs max size are now calculated to where window is opened instead of use the primary or first screen all the time
25 files changed, 713 insertions, 194 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c9fb8..853079a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 03/12/2020 - v2.1.1 + +* (Add) About box: Primary screen identifier and open on screen identifier +* (Add) Calibrator - External tests +* (Change) Rewrite 'Action - Import Layer(s)' to support file formats and add the followig importation types: + * **Insert:** Insert layers. (Requires images with bounds equal or less than file resolution) + * **Replace:** Replace layers. (Requires images with bounds equal or less than file resolution) + * **Stack:** Stack layers content. (Requires images with bounds equal or less than file resolution) + * **Merge:** Merge/Sum layers content. (Requires images with same resolution) + * **Subtract:** Subtract layers content. (Requires images with same resolution) + * **BitwiseAnd:** Perform a 'bitwise and' operation over layer pixels. (Requires images with same resolution) + * **BitwiseOr:** Perform a 'bitwise or' operation over layer pixels. (Requires images with same resolution) + * **BitwiseXOr:** Perform a 'bitwise xor' operation over layer pixels. (Requires images with same resolution) +* (Change) Icon for Tool - Raft Relief +* (Change) Windows and dialogs max size are now calculated to where window is opened instead of use the primary or first screen all the time + ## 29/12/2020 - v2.1.0 * (Add) Tool - Redraw model/supports: Redraw the model or supports with a set brightness. This requires an extra sliced file from same object but without any supports and raft, straight to the build plate. @@ -193,15 +193,15 @@ sudo apt-get install -y libjpeg-dev libpng-dev libgeotiff-dev libdc1394-22 libav **Arch/Manjaro/Similars** ```bash -sudo pacman -S openjpeg2 libjpeg-turbo libpng libgeotiff libdc1394 libdc1394 ffmpeg openexr tbb dotnet-runtime +sudo pacman -S openjpeg2 libjpeg-turbo libpng libgeotiff libdc1394 libdc1394 ffmpeg openexr tbb ``` To run UVtools open it folder on a terminal and call one of: * Double-click UVtools file * `./UVtools` -* `dotnet UVtools.dll` [For universal package only] -* `sh UVtools.sh` [For universal package only] +* `sh UVtools.sh` +* `dotnet UVtools.dll` [For universal package only, requires dotnet-runtime] * As a pratical alternative you can create a shortcut on Desktop ### Mac @@ -213,20 +213,19 @@ To run UVtools open it folder on a terminal and call one of: <!--- * Donwload and install: https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.101-macos-x64-installer brew install libjpeg libpng libgeotiff libdc1394 ffmpeg openexr tbb ---> - ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" brew analytics off brew cask install dotnet ``` +--> To run UVtools open it folder on a terminal and call one of: * Double-click UVtools file * `./UVtools` -* `dotnet UVtools.dll` [For universal package only] -* `sh UVtools.sh` [For universal package only] +* `sh UVtools.sh` +* `dotnet UVtools.dll` [For universal package only, requires dotnet-runtime] * As a pratical alternative you can create a shortcut on Desktop ## How to use diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index a48a5c7..42db640 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -255,5 +255,16 @@ namespace UVtools.Core.Extensions return mat; } + public static Mat RoiFromCenter(this Mat mat, Size size) + { + if (size == mat.Size) return mat; + return new Mat(mat, new Rectangle( + mat.Size.Width / 2 - size.Width / 2, + mat.Size.Height / 2 - size.Height / 2, + size.Width, + size.Height + )); + } + } } diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 0db56ef..68229b9 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -275,15 +275,15 @@ namespace UVtools.Core.FileFormats #endregion #region Properties - public Printer PrinterSettings { get; private set; } + public Printer PrinterSettings { get; private set; } = new(); - public Material MaterialSettings { get; private set; } + public Material MaterialSettings { get; private set; } = new(); - public Print PrintSettings { get; private set; } + public Print PrintSettings { get; private set; } = new(); - public OutputConfig OutputConfigSettings { get; private set; } + public OutputConfig OutputConfigSettings { get; private set; } = new(); - public Statistics Statistics { get; } = new Statistics(); + public Statistics Statistics { get; } = new (); public override FileFormatType FileType => FileFormatType.Archive; diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index 875ca1b..568692b 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -204,7 +204,7 @@ namespace UVtools.Core { get { - Mat mat = new Mat(); + Mat mat = new(); CvInvoke.Imdecode(CompressedBytes, ImreadModes.Grayscale, mat); return mat; } @@ -246,26 +246,28 @@ namespace UVtools.Core GetBoundingRectangle(); }*/ - if (!(parentLayerManager is null)) - { - _positionZ = SlicerFile.GetHeightFromLayer(index); - _exposureTime = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime); - _liftHeight = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); - _liftSpeed = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); - _retractSpeed = SlicerFile.RetractSpeed; - _lightPwm = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); - } + if (parentLayerManager is null) return; + _positionZ = SlicerFile.GetHeightFromLayer(index); + _exposureTime = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime); + _liftHeight = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); + _liftSpeed = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); + _retractSpeed = SlicerFile.RetractSpeed; + _lightPwm = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); } + public Layer(uint index, byte[] compressedBytes, FileFormat slicerFile) : this(index, compressedBytes, slicerFile.LayerManager) + {} + public Layer(uint index, Mat layerMat, LayerManager parentLayerManager) : this(index, new byte[0], parentLayerManager) { LayerMat = layerMat; IsModified = false; } + public Layer(uint index, Mat layerMat, FileFormat slicerFile) : this(index, layerMat, slicerFile.LayerManager) { } - public Layer(uint index, Stream stream, LayerManager parentLayerManager) : this(index, stream.ToArray(), parentLayerManager) - { } + public Layer(uint index, Stream stream, LayerManager parentLayerManager) : this(index, stream.ToArray(), parentLayerManager) { } + public Layer(uint index, Stream stream, FileFormat slicerFile) : this(index, stream.ToArray(), slicerFile.LayerManager) { } #endregion #region Equatables @@ -366,6 +368,7 @@ namespace UVtools.Core bool needDispose = false; if (mat is null) { + if (_compressedBytes is null || _compressedBytes.Length == 0) return Rectangle.Empty; mat = LayerMat; needDispose = true; } diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index f354ea2..5a2924c 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -203,17 +203,15 @@ namespace UVtools.Core public Rectangle GetBoundingRectangle(OperationProgress progress = null) { + progress ??= new OperationProgress(OperationProgress.StatusOptimizingBounds, Count-1); if (!_boundingRectangle.IsEmpty || Count == 0 || this[0] is null) return _boundingRectangle; _boundingRectangle = this[0].BoundingRectangle; if (_boundingRectangle.IsEmpty) // Safe checking { - progress?.Reset(OperationProgress.StatusOptimizingBounds, Count); + progress.Reset(OperationProgress.StatusOptimizingBounds, Count-1); Parallel.For(0, Count, layerIndex => { - if (!ReferenceEquals(progress, null) && progress.Token.IsCancellationRequested) - { - return; - } + if (progress.Token.IsCancellationRequested) return; this[layerIndex].GetBoundingRectangle(); @@ -233,12 +231,11 @@ namespace UVtools.Core } - progress?.Reset(OperationProgress.StatusCalculatingBounds, Count); + progress.Reset(OperationProgress.StatusCalculatingBounds, Count-1); for (int i = 1; i < Count; i++) { if(this[i].BoundingRectangle.IsEmpty) continue; _boundingRectangle = Rectangle.Union(_boundingRectangle, this[i].BoundingRectangle); - if (ReferenceEquals(progress, null)) continue; progress++; } @@ -1149,7 +1146,7 @@ namespace UVtools.Core /// Reallocate with new size /// </summary> /// <returns></returns> - public LayerManager Reallocate(uint newLayerCount, bool makeClone = false) + public LayerManager ReallocateNew(uint newLayerCount, bool makeClone = false) { LayerManager layerManager = new LayerManager(newLayerCount, SlicerFile); foreach (var layer in this) @@ -1164,6 +1161,69 @@ namespace UVtools.Core } /// <summary> + /// Reallocate with add size + /// </summary> + /// <returns></returns> + public void Reallocate(uint insertAtLayerIndex, uint layerCount, bool initBlack = false) + { + var layers = Layers; + Layers = new Layer[Count + layerCount]; + + // Rearrange + for (uint layerIndex = 0; layerIndex < insertAtLayerIndex; layerIndex++) + { + Layers[layerIndex] = layers[layerIndex]; + } + + // Rearrange + for (uint layerIndex = insertAtLayerIndex; layerIndex < layers.Length; layerIndex++) + { + Layers[layerCount + layerIndex] = layers[layerIndex]; + Layers[layerCount + layerIndex].Index = layerCount + layerIndex; + } + + // Allocate new layers + if (initBlack) + { + Parallel.For(insertAtLayerIndex, insertAtLayerIndex + layerCount, layerIndex => + { + Layers[layerIndex] = new Layer((uint) layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this); + }); + } + /*for (uint layerIndex = insertAtLayerIndex; layerIndex < insertAtLayerIndex + layerCount; layerIndex++) + { + Layers[layerIndex] = initBlack ? new Layer(layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this) : null; + }*/ + } + + public void ReallocateRange(uint startLayerIndex, uint endLayerIndex) + { + var layers = Layers; + if ((int)(endLayerIndex - startLayerIndex) < 0) return; + Layers = new Layer[1 + endLayerIndex - startLayerIndex]; + + uint currentLayerIndex = 0; + for (uint layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++) + { + Layers[currentLayerIndex++] = layers[layerIndex]; + } + + BoundingRectangle = Rectangle.Empty; + } + + /// <summary> + /// Reallocate at start + /// </summary> + /// <returns></returns> + public void ReallocateStart(uint layerCount, bool initBlack = false) => Reallocate(0, layerCount, initBlack); + + /// <summary> + /// Reallocate at end + /// </summary> + /// <returns></returns> + public void ReallocateEnd(uint layerCount, bool initBlack = false) => Reallocate(Count, layerCount, initBlack); + + /// <summary> /// Clone this object /// </summary> /// <returns></returns> diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs index abb1a68..f87085f 100644 --- a/UVtools.Core/Managers/ClipboardManager.cs +++ b/UVtools.Core/Managers/ClipboardManager.cs @@ -122,7 +122,7 @@ namespace UVtools.Core.Managers var clip = this[i]; if (layerManager.Count != clip.LayerCount) // Need resize layer manager { - layerManager = layerManager.Reallocate(clip.LayerCount); + layerManager = layerManager.ReallocateNew(clip.LayerCount); ReallocatedLayerCount = true; } diff --git a/UVtools.Core/Operations/OperationCalibrateExternalTests.cs b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs new file mode 100644 index 0000000..64a5bb6 --- /dev/null +++ b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs @@ -0,0 +1,47 @@ +/* + * 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. + */ + + +namespace UVtools.Core.Operations +{ + public class OperationCalibrateExternalTests : Operation + { + #region Members + #endregion + + #region Overrides + + public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None; + public override bool CanROI => false; + public override bool CanHaveProfiles => false; + public override string ButtonOkText => null; + public override string Title => "External tests"; + public override string Description => + "A set of useful external tests to run within your slicer.\nClick on a button to open website and instructions."; + + public override string ConfirmationText => null; + + public override string ProgressTitle => null; + + public override string ProgressAction => null; + + #endregion + + #region Properties + + #endregion + + #region Equality + + #endregion + + #region Methods + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index 17c4dba..7e5b822 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -6,15 +6,17 @@ * of this license document, but changing it is not allowed. */ using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; +using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; @@ -23,12 +25,26 @@ namespace UVtools.Core.Operations [Serializable] public sealed class OperationLayerImport : Operation { - private uint _insertAfterLayerIndex; - private bool _replaceStartLayer; - private bool _replaceSubsequentLayers; - private bool _discardRemainingLayers; - private bool _mergeImages; - private ObservableCollection<StringTag> _files = new ObservableCollection<StringTag>(); + #region Enums + public enum ImportTypes : byte + { + Insert, + Replace, + Stack, + Merge, + Subtract, + BitwiseAnd, + BitwiseOr, + BitwiseXOr, + } + #endregion + + private ImportTypes _importType = ImportTypes.Stack; + private uint _startLayerIndex; + private bool _extendBeyondLayerCount = true; + private bool _discardUnmodifiedLayers; + private ushort _stackMargin = 50; + private ObservableCollection<StringTag> _files = new(); #region Overrides @@ -37,26 +53,25 @@ namespace UVtools.Core.Operations public override string Title => "Import Layers"; public override string Description => - "Import layers from local files into the model at a selected layer height.\n\n" + + "Import layers from local files into the model at a selected layer height.\n" + "NOTE: Imported images must be greyscale and have the same resolution as the model."; - public override string ConfirmationText => $"import {Count} layer{(Count!=1?"s":"")}?"; + public override string ConfirmationText => $"{_importType} import {Count} file{(Count>=1?"s":"")}?"; public override string ProgressTitle => - $"Importing {Count} layer{(Count!=1 ? "s" : "")}"; + $"{_importType} importing {Count} file{(Count>=1 ? "s" : "")}"; public override string ProgressAction => "Imported layers"; public override bool CanCancel => true; - public override uint LayerIndexStart => InsertAfterLayerIndex + (ReplaceStartLayer ? 0u : 1); - public override uint LayerIndexEnd => (uint)(LayerIndexStart + Count - 1); + public override uint LayerIndexEnd => _startLayerIndex + Count - 1; public override bool CanHaveProfiles => false; public override StringTag Validate(params object[] parameters) { - var result = new ConcurrentBag<StringTag>(); + /*var result = new ConcurrentBag<StringTag>(); Parallel.ForEach(Files, file => { using (Mat mat = CvInvoke.Imread(file.TagString, ImreadModes.AnyColor)) @@ -83,52 +98,76 @@ namespace UVtools.Core.Operations } message.AppendLine(file.Content); } + + + return new StringTag(message.ToString(), result);*/ + + StringBuilder sb = new(); + + if (Files.Count == 0) + { + sb.AppendLine("No files to import."); + } - return new StringTag(message.ToString(), result); + return new StringTag(sb.ToString()); } #endregion #region Properties + [XmlIgnore] public Size FileResolution { get; } - public uint InsertAfterLayerIndex + public ImportTypes ImportType { - get => _insertAfterLayerIndex; - set => RaiseAndSetIfChanged(ref _insertAfterLayerIndex, value); + get => _importType; + set + { + if(!RaiseAndSetIfChanged(ref _importType, value)) return; + RaisePropertyChanged(nameof(IsImportStackType)); + RaisePropertyChanged(nameof(IsExtendBeyondLayerCountVisible)); + } } - public bool ReplaceStartLayer + + public static Array ImportTypesItems => Enum.GetValues(typeof(ImportTypes)); + + public bool IsImportStackType => _importType == ImportTypes.Stack; + + public uint StartLayerIndex { - get => _replaceStartLayer; - set => RaiseAndSetIfChanged(ref _replaceStartLayer, value); + get => _startLayerIndex; + set => RaiseAndSetIfChanged(ref _startLayerIndex, value); } - public bool ReplaceSubsequentLayers + public bool ExtendBeyondLayerCount { - get => _replaceSubsequentLayers; - set => RaiseAndSetIfChanged(ref _replaceSubsequentLayers, value); + get => _extendBeyondLayerCount; + set => RaiseAndSetIfChanged(ref _extendBeyondLayerCount, value); } - public bool DiscardRemainingLayers + public bool IsExtendBeyondLayerCountVisible => _importType == ImportTypes.Replace || _importType == ImportTypes.Stack || _importType == ImportTypes.Merge; + + public bool DiscardUnmodifiedLayers { - get => _discardRemainingLayers; - set => RaiseAndSetIfChanged(ref _discardRemainingLayers, value); + get => _discardUnmodifiedLayers; + set => RaiseAndSetIfChanged(ref _discardUnmodifiedLayers, value); } - public bool MergeImages + public ushort StackMargin { - get => _mergeImages; - set => RaiseAndSetIfChanged(ref _mergeImages, value); + get => _stackMargin; + set => RaiseAndSetIfChanged(ref _stackMargin, value); } + [XmlIgnore] public ObservableCollection<StringTag> Files { get => _files; set => RaiseAndSetIfChanged(ref _files, value); } - public int Count => Files.Count; + public uint Count => (uint) Files.Count; #endregion #region Constructor @@ -163,7 +202,7 @@ namespace UVtools.Core.Operations } - public uint CalculateTotalLayers(uint totalLayers) + /*public uint CalculateTotalLayers(uint totalLayers) { if (DiscardRemainingLayers) { @@ -176,7 +215,7 @@ namespace UVtools.Core.Operations } return (uint)(totalLayers + Files.Count - (ReplaceStartLayer ? 1 : 0)); - } + }*/ public override string ToString() { @@ -188,67 +227,275 @@ namespace UVtools.Core.Operations public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) { progress ??= new OperationProgress(); - progress.Reset(ProgressAction, (uint)Count); - - var oldLayers = slicerFile.LayerManager.Layers; - uint newLayerCount = CalculateTotalLayers((uint)oldLayers.Length); - uint startIndex = LayerIndexStart; - var layers = new Layer[newLayerCount]; - - // Keep same layers up to InsertAfterLayerIndex - for (uint i = 0; i <= InsertAfterLayerIndex; i++) + slicerFile.SuppressRebuildProperties = true; + + List<FileFormat> fileFormats = new(); + List<KeyValuePair<uint, string>> keyImage = new(); + int lastProcessedLayerIndex = -1; + + // Order raw images + for (int i = 0; i < Count; i++) { - layers[i] = oldLayers[i]; + if (!Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && + !Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && + !Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && + !Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && + !Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + keyImage.Add(new KeyValuePair<uint, string>((uint) keyImage.Count, Files[i].TagString)); } - // Keep all old layers if not discarding them - if (ReplaceSubsequentLayers) + // Create virtual file format with images + if (keyImage.Count > 0) { - if (!DiscardRemainingLayers) + progress.Reset("Packing images", (uint) keyImage.Count); + SL1File format = new SL1File(); + format.LayerManager = new LayerManager((uint) keyImage.Count, format); + + Parallel.ForEach(keyImage, pair => { - for (uint i = InsertAfterLayerIndex + 1; i < oldLayers.Length; i++) + if (progress.Token.IsCancellationRequested) return; + using var mat = CvInvoke.Imread(pair.Value, ImreadModes.Grayscale); + if (pair.Key == 0) format.Resolution = mat.Size; + format[pair.Key] = new Layer(pair.Key, mat, format); + + lock (progress.Mutex) { - layers[i] = oldLayers[i]; + progress++; } - } + }); + + progress.Token.ThrowIfCancellationRequested(); + fileFormats.Add(format); } - else // Push remaining layers to the end of imported layers + + // Order remaining possible file formats + for (int i = 0; i < Count; i++) + { + if (Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || + Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) || + Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || + Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || + Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + + var fileFormat = FileFormat.FindByExtension(Files[i].TagString, true, true); + if(fileFormat is null) continue; + fileFormat.FileFullPath = Files[i].TagString; + fileFormats.Add(fileFormat); + } + + progress.Token.ThrowIfCancellationRequested(); + + if (fileFormats.Count == 0) return false; + + if (_importType == ImportTypes.Stack) + { + new OperationMove(slicerFile, Enumerations.Anchor.TopLeft){LayerIndexEnd = slicerFile.LastLayerIndex}.Execute(slicerFile, progress); + } + + foreach (var fileFormat in fileFormats) { - uint oldLayerIndex = InsertAfterLayerIndex; - for (uint i = LayerIndexEnd + 1; i < newLayerCount; i++) + if (!string.IsNullOrEmpty(fileFormat.FileFullPath)) { - oldLayerIndex++; - layers[i] = oldLayers[oldLayerIndex]; + fileFormat.Decode(fileFormat.FileFullPath, progress); } - } + var boundingRectangle = slicerFile.LayerManager.GetBoundingRectangle(progress); + var fileFormatBoundingRectangle = fileFormat.LayerManager.GetBoundingRectangle(progress); + var roiRectangle = Rectangle.Empty; - Parallel.For(0, Count, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - i => + // Check if is possible to process this file + switch (_importType) { - var mat = CvInvoke.Imread(Files[i].TagString, ImreadModes.Grayscale); - uint layerIndex = (uint)(startIndex + i); - if (MergeImages) - { - if (layers[layerIndex] is not null) + case ImportTypes.Insert: + if (slicerFile.Resolution != fileFormat.Resolution && + (slicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || + slicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; + slicerFile.LayerManager.Reallocate(_startLayerIndex, fileFormat.LayerCount); + break; + case ImportTypes.Replace: + case ImportTypes.Stack: + if (slicerFile.Resolution != fileFormat.Resolution && + (slicerFile.Resolution.Width < fileFormat.LayerManager.BoundingRectangle.Width || + slicerFile.Resolution.Height < fileFormat.LayerManager.BoundingRectangle.Height)) continue; + + + if(fileFormatBoundingRectangle.Width >= slicerFile.ResolutionX || fileFormatBoundingRectangle.Height >= slicerFile.ResolutionY) + continue; + + int x = 0; + int y = 0; + + if (boundingRectangle.Right + _stackMargin + fileFormatBoundingRectangle.Width < + slicerFile.ResolutionX) { - using var oldMat = layers[layerIndex].LayerMat; - CvInvoke.Add(oldMat, mat, mat); + x = boundingRectangle.Right + _stackMargin; + } + else + { + y = boundingRectangle.Bottom + _stackMargin; } - } - layers[layerIndex] = new Layer(layerIndex, mat, slicerFile.LayerManager); - lock (progress.Mutex) + if (y >= slicerFile.ResolutionY) + continue; + + roiRectangle = new Rectangle(x, y, fileFormatBoundingRectangle.Width, fileFormatBoundingRectangle.Height); + + if (_extendBeyondLayerCount) + { + int layerCountDifference = (int) (_startLayerIndex + fileFormat.LayerCount - slicerFile.LayerCount); + if (layerCountDifference > 0) + { + slicerFile.LayerManager.ReallocateEnd((uint) layerCountDifference, _importType == ImportTypes.Stack); + } + } + + break; + case ImportTypes.Merge: + if (slicerFile.Resolution != fileFormat.Resolution) continue; + if (_extendBeyondLayerCount) + { + int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - slicerFile.LayerCount); + if (layerCountDifference > 0) + { + slicerFile.LayerManager.ReallocateEnd((uint)layerCountDifference, true); + } + } + break; + case ImportTypes.Subtract: + case ImportTypes.BitwiseAnd: + case ImportTypes.BitwiseOr: + case ImportTypes.BitwiseXOr: + if (slicerFile.Resolution != fileFormat.Resolution) continue; + break; + } + + progress.Reset(ProgressAction, fileFormat.LayerCount); + Parallel.For(0, fileFormat.LayerCount, + //new ParallelOptions{MaxDegreeOfParallelism = 1}, + i => { - progress++; - } - }); + if (progress.Token.IsCancellationRequested) return; + uint layerIndex = (uint)(_startLayerIndex + i); + + switch (_importType) + { + case ImportTypes.Insert: + { + if (layerIndex >= slicerFile.LayerCount) return; + if (slicerFile.Resolution == fileFormat.Resolution) + { + slicerFile[layerIndex] = fileFormat[i]; + break; + } + + using var layer = fileFormat[i].LayerMat; + using var layerRoi = layer.RoiFromCenter(slicerFile.Resolution); + slicerFile[layerIndex] = new Layer(layerIndex, layerRoi, slicerFile); + + break; + } + case ImportTypes.Replace: + { + if (layerIndex >= slicerFile.LayerCount) return; + if (slicerFile.Resolution == fileFormat.Resolution) + { + slicerFile[layerIndex] = fileFormat[i]; + break; + } + + using var layer = fileFormat[i].LayerMat; + using var layerRoi = layer.RoiFromCenter(slicerFile.Resolution); + slicerFile[layerIndex] = new Layer(layerIndex, layerRoi, slicerFile); + break; + } + case ImportTypes.Stack: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var mat = slicerFile[layerIndex].LayerMat; + using var importMat = fileFormat[i].LayerMat; + var matRoi = new Mat(mat, roiRectangle); + var importMatRoi = new Mat(importMat, fileFormatBoundingRectangle); + importMatRoi.CopyTo(matRoi); + slicerFile[layerIndex].LayerMat = mat; + + break; + } + case ImportTypes.Merge: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var originalMat = slicerFile[layerIndex].LayerMat; + using var newMat = fileFormat[i].LayerMat; + CvInvoke.Add(originalMat, newMat, newMat); + slicerFile[layerIndex].LayerMat = newMat; + break; + } + case ImportTypes.Subtract: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var originalMat = slicerFile[layerIndex].LayerMat; + using var newMat = fileFormat[i].LayerMat; + CvInvoke.Subtract(originalMat, newMat, newMat); + slicerFile[layerIndex].LayerMat = newMat; + break; + } + case ImportTypes.BitwiseAnd: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var originalMat = slicerFile[layerIndex].LayerMat; + using var newMat = fileFormat[i].LayerMat; + CvInvoke.BitwiseAnd(originalMat, newMat, newMat); + slicerFile[layerIndex].LayerMat = newMat; + break; + } + case ImportTypes.BitwiseOr: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var originalMat = slicerFile[layerIndex].LayerMat; + using var newMat = fileFormat[i].LayerMat; + CvInvoke.BitwiseOr(originalMat, newMat, newMat); + slicerFile[layerIndex].LayerMat = newMat; + break; + } + case ImportTypes.BitwiseXOr: + { + if (layerIndex >= slicerFile.LayerCount) return; + using var originalMat = slicerFile[layerIndex].LayerMat; + using var newMat = fileFormat[i].LayerMat; + CvInvoke.BitwiseXor(originalMat, newMat, newMat); + slicerFile[layerIndex].LayerMat = newMat; + break; + } + default: + throw new ArgumentOutOfRangeException(); + } - slicerFile.LayerManager.Layers = layers; - slicerFile.RequireFullEncode = true; - progress.Token.ThrowIfCancellationRequested(); + lock (progress.Mutex) + { + lastProcessedLayerIndex = Math.Max(lastProcessedLayerIndex, (int)layerIndex); + progress++; + } + }); + + fileFormat.Dispose(); + progress.Token.ThrowIfCancellationRequested(); + } + + if (_importType == ImportTypes.Stack) + { + new OperationMove(slicerFile) { LayerIndexEnd = slicerFile.LastLayerIndex }.Execute(slicerFile, progress); + } + + if (lastProcessedLayerIndex <= -1) return false; + + if (lastProcessedLayerIndex + 1 < slicerFile.LayerCount && _discardUnmodifiedLayers) + { + slicerFile.LayerManager.ReallocateRange(0, (uint) lastProcessedLayerIndex); + } + + slicerFile.LayerManager.RebuildLayersProperties(); + slicerFile.SuppressRebuildProperties = false; + slicerFile.RequireFullEncode = true; return true; } diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs index 09f046d..12bcae0 100644 --- a/UVtools.Core/Operations/OperationMove.cs +++ b/UVtools.Core/Operations/OperationMove.cs @@ -227,6 +227,14 @@ namespace UVtools.Core.Operations { } + public OperationMove(FileFormat slicerFile, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) + { + ROI = slicerFile.LayerManager.BoundingRectangle; + ImageWidth = slicerFile.ResolutionX; + ImageHeight = slicerFile.ResolutionY; + Anchor = anchor; + } + public OperationMove(Rectangle srcRoi, uint imageWidth, uint imageHeight, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) { ROI = srcRoi; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 73ec8e0..28f66fc 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, repair, conversion and manipulation</Description> - <Version>2.1.0</Version> + <Version>2.1.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs index 4d14ccc..c4b717a 100644 --- a/UVtools.WPF/App.axaml.cs +++ b/UVtools.WPF/App.axaml.cs @@ -13,9 +13,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Threading; using Avalonia; -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; @@ -38,8 +36,6 @@ namespace UVtools.WPF public static AppVersionChecker VersionChecker { get; } = new AppVersionChecker(); - public static Size MaxWindowSize = Size.Empty; - public override void Initialize() { AvaloniaXamlLoader.Load(this); @@ -124,15 +120,15 @@ namespace UVtools.WPF { try { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) { Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }).Dispose(); } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (OperatingSystem.IsLinux()) { Process.Start("xdg-open", url).Dispose(); } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (OperatingSystem.IsMacOS()) { Process.Start("open", url).Dispose(); } diff --git a/UVtools.WPF/Assets/Icons/bookmark-16x16.png b/UVtools.WPF/Assets/Icons/bookmark-16x16.png Binary files differnew file mode 100644 index 0000000..b6e8b3e --- /dev/null +++ b/UVtools.WPF/Assets/Icons/bookmark-16x16.png diff --git a/UVtools.WPF/Assets/Icons/cookie-16x16.png b/UVtools.WPF/Assets/Icons/cookie-16x16.png Binary files differnew file mode 100644 index 0000000..2c90a8b --- /dev/null +++ b/UVtools.WPF/Assets/Icons/cookie-16x16.png diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml new file mode 100644 index 0000000..0b8421f --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml @@ -0,0 +1,35 @@ +<UserControl xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="UVtools.WPF.Controls.Calibrators.CalibrateExternalTestsControl"> + <Grid + RowDefinitions="Auto,Auto,Auto" + ColumnDefinitions="400"> + + <Button Grid.Row="0" + Padding="5" + Content="Photonsters Validation Matrix / Exposure finder" + Command="{Binding ButtonClicked}" + VerticalAlignment="Center" + CommandParameter="https://www.thingiverse.com/thing:4707289"/> + + <Button Grid.Row="1" + Margin="0,5,0,0" + Padding="5" + Content="Support pull test" + Command="{Binding ButtonClicked}" + VerticalAlignment="Center" + CommandParameter="https://www.thingiverse.com/thing:4308175"/> + + <Button Grid.Row="2" + Margin="0,5,0,0" + Padding="5" + Content="AmeraLabs town calibration part" + Command="{Binding ButtonClicked}" + VerticalAlignment="Center" + CommandParameter="https://www.thingiverse.com/thing:2810666"/> + + </Grid> +</UserControl> diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs new file mode 100644 index 0000000..c2ed96a --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs @@ -0,0 +1,30 @@ +using System.Diagnostics; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; +using UVtools.WPF.Controls.Tools; + +namespace UVtools.WPF.Controls.Calibrators +{ + public class CalibrateExternalTestsControl : ToolControl + { + public OperationCalibrateExternalTests Operation => BaseOperation as OperationCalibrateExternalTests; + public CalibrateExternalTestsControl() + { + this.InitializeComponent(); + BaseOperation = new OperationCalibrateExternalTests(); + Debug.WriteLine("asdasdasdasd"); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void ButtonClicked(string url) + { + App.OpenBrowser(url); + } + } +} diff --git a/UVtools.WPF/Controls/Helpers.cs b/UVtools.WPF/Controls/Helpers.cs index 9530e78..28ec99f 100644 --- a/UVtools.WPF/Controls/Helpers.cs +++ b/UVtools.WPF/Controls/Helpers.cs @@ -13,7 +13,7 @@ namespace UVtools.WPF.Controls { public static class Helpers { - public static readonly List<FileDialogFilter> ImagesFileFilter = new List<FileDialogFilter> + public static readonly List<FileDialogFilter> ImagesFileFilter = new() { new FileDialogFilter { diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml index ce0164f..fa74937 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml @@ -6,42 +6,75 @@ x:Class="UVtools.WPF.Controls.Tools.ToolLayerImportControl"> <StackPanel Spacing="10"> - <StackPanel Spacing="10" Orientation="Horizontal"> - <TextBlock - VerticalAlignment="Center" - Text="Insert after layer:"/> - <NumericUpDown - Width="70" - Minimum="0" - Maximum="{Binding MaximumLayer}" - Value="{Binding Operation.InsertAfterLayerIndex}" - /> - <TextBlock - VerticalAlignment="Center" - Text="{Binding InfoLayerHeightStr}"/> - </StackPanel> - - <StackPanel Spacing="10" Orientation="Horizontal"> - <CheckBox - IsChecked="{Binding Operation.ReplaceStartLayer}" - Content="Replace this layer"/> - - <CheckBox - IsChecked="{Binding Operation.ReplaceSubsequentLayers}" - Content="Replace subsequent layers"/> - - <CheckBox - IsChecked="{Binding Operation.DiscardRemainingLayers}" - Content="Discard remaining layers"/> - - <CheckBox - IsChecked="{Binding Operation.MergeImages}" - Content="Merge images"/> - </StackPanel> - + <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,100,5,Auto,20,Auto"> + + <TextBlock Grid.Row="0" Grid.Column="0" + VerticalAlignment="Center" + ToolTip.Tip="Insert: Insert layers. (Requires images with bounds equal or less than file resolution) +
Replace: Replace layers. (Requires images with bounds equal or less than file resolution) +
Stack: Stack layers content. (Requires images with bounds equal or less than file resolution) +
Merge: Merge/Sum layers content. (Requires images with same resolution) +
Subtract: Subtract layers content. (Requires images with same resolution) +
BitwiseAnd: Perform a 'bitwise and' operation over layer pixels. (Requires images with same resolution) +
BitwiseOr: Perform a 'bitwise or' operation over layer pixels. (Requires images with same resolution) +
BitwiseXOr: Perform a 'bitwise xor' operation over layer pixels. (Requires images with same resolution)" + Text="Import type:"/> + + <ComboBox Grid.Row="0" Grid.Column="2" + VerticalAlignment="Center" + SelectedItem="{Binding Operation.ImportType}" + Items="{Binding Operation.ImportTypesItems}"/> + + <TextBlock Grid.Row="0" Grid.Column="4" + VerticalAlignment="Center" + Text="layers"/> + + <CheckBox Grid.Row="0" Grid.Column="6" + IsChecked="{Binding Operation.ExtendBeyondLayerCount}" + IsVisible="{Binding Operation.IsExtendBeyondLayerCountVisible}" + ToolTip.Tip="If enabled, imported images that go beyond last layer are appended. The file will grow in layer count if necessary to fit all imported layers. +
If disabled, all imported images that go beyond last layer are discarded. The file will maintain same layer count." + Content="Extend beyond layer count"/> + + <TextBlock Grid.Row="2" Grid.Column="0" + VerticalAlignment="Center" + Text="Start at layer:"/> + <NumericUpDown Grid.Row="2" Grid.Column="2" + ClipValueToMinMax="True" + Minimum="0" + Maximum="{Binding MaximumLayer}" + Value="{Binding Operation.StartLayerIndex}"/> + <TextBlock Grid.Row="2" Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding InfoLayerHeightStr}"/> + + <CheckBox Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="5" + IsChecked="{Binding Operation.DiscardUnmodifiedLayers}" + ToolTip.Tip="If enabled, all unmodified layers forward the last inserted layer are discarded. +
If disabled, the unmodified layers are kept." + Content="Discard unmodified layers"/> + + <TextBlock Grid.Row="6" Grid.Column="0" + VerticalAlignment="Center" + IsVisible="{Binding Operation.IsImportStackType}" + Text="Stack margin:"/> + <NumericUpDown Grid.Row="6" Grid.Column="2" + ClipValueToMinMax="True" + IsVisible="{Binding Operation.IsImportStackType}" + Minimum="0" + Maximum="65535" + Value="{Binding Operation.StackMargin}"/> + <TextBlock Grid.Row="6" Grid.Column="4" + VerticalAlignment="Center" + IsVisible="{Binding Operation.IsImportStackType}" + Text="px"/> + </Grid> + + <CheckBox IsChecked="{Binding IsAutoSortLayersByFileNameChecked}" - Content="Auto sort layers by file name"/> + Content="Auto sort files by filename"/> <TextBlock Text="{Binding InfoImportResult}" /> @@ -90,7 +123,7 @@ <TextBlock VerticalAlignment="Center" - Text="{Binding Operation.Files.Count, StringFormat=Layers: \{0\}}"/> + Text="{Binding Operation.Files.Count, StringFormat=Files: \{0\}}"/> </StackPanel> </Grid> </Border> diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs index f96b5f9..db58958 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs @@ -1,4 +1,6 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; @@ -7,6 +9,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using DynamicData; using MessageBox.Avalonia.Enums; +using UVtools.Core.FileFormats; using UVtools.Core.Objects; using UVtools.Core.Operations; using UVtools.WPF.Extensions; @@ -26,7 +29,7 @@ namespace UVtools.WPF.Controls.Tools public uint MaximumLayer => App.SlicerFile.LastLayerIndex; - public string InfoLayerHeightStr => $"({App.SlicerFile.GetHeightFromLayer(Operation.InsertAfterLayerIndex)}mm)"; + public string InfoLayerHeightStr => $"({App.SlicerFile.GetHeightFromLayer(Operation.StartLayerIndex)}mm)"; public bool IsAutoSortLayersByFileNameChecked { @@ -39,7 +42,7 @@ namespace UVtools.WPF.Controls.Tools get { if (Operation.Files.Count <= 0) return null; - uint modelTotalLayers = Operation.CalculateTotalLayers(App.SlicerFile.LayerCount); + /*uint modelTotalLayers = (uint) Operation.Files.Count;//Operation.CalculateTotalLayers(App.SlicerFile.LayerCount); string textFactor = "grow"; if (modelTotalLayers < App.SlicerFile.LayerCount) { @@ -48,10 +51,11 @@ namespace UVtools.WPF.Controls.Tools else if (modelTotalLayers == App.SlicerFile.LayerCount) { textFactor = "keep"; - } - return - $"{Operation.Files.Count} layers will be imported into model starting from layer {Operation.InsertAfterLayerIndex} {InfoLayerHeightStr}.\n" + - $"Model will {textFactor} from layers {App.SlicerFile.LayerCount} ({App.SlicerFile.TotalHeight}mm) to {modelTotalLayers} ({App.SlicerFile.GetHeightFromLayer(modelTotalLayers, false)}mm)"; + }*/ + + return + $"{Operation.Files.Count} files will be imported into model starting from layer {Operation.StartLayerIndex} {InfoLayerHeightStr}."; + //$"Model will {textFactor} from layers {App.SlicerFile.LayerCount} ({App.SlicerFile.TotalHeight}mm) to {modelTotalLayers} ({App.SlicerFile.GetHeightFromLayer(modelTotalLayers, false)}mm)"; } } @@ -67,6 +71,11 @@ namespace UVtools.WPF.Controls.Tools PreviewImage = null; return; } + if (!_selectedFile.TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return; PreviewImage = new Bitmap(_selectedFile.TagString); } } @@ -118,7 +127,7 @@ namespace UVtools.WPF.Controls.Tools AvaloniaXamlLoader.Load(this); } - public override async Task<bool> ValidateForm() + /*public override async Task<bool> ValidateForm() { UpdateOperation(); var message = Operation.Validate(); @@ -136,14 +145,14 @@ namespace UVtools.WPF.Controls.Tools } return false; - } + }*/ public override void Callback(ToolWindow.Callbacks callback) { switch (callback) { case ToolWindow.Callbacks.Init: - ParentWindow.ButtonOkEnabled = false; + RefreshGUI(); break; } } @@ -157,10 +166,19 @@ namespace UVtools.WPF.Controls.Tools public async void AddFiles() { + var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia); + var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] }; + for (int i = 0; i < filters.Count; i++) + { + if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue; + orderedFilters.Add(filters[i]); + } + var dialog = new OpenFileDialog { AllowMultiple = true, - Filters = Helpers.ImagesFileFilter + Filters = orderedFilters, + Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile }; var files = await dialog.ShowAsync(ParentWindow); diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs index 8ad4405..22a871f 100644 --- a/UVtools.WPF/Extensions/WindowExtensions.cs +++ b/UVtools.WPF/Extensions/WindowExtensions.cs @@ -8,8 +8,10 @@ using System.Threading; using System.Threading.Tasks; +using Avalonia; using Avalonia.Controls; using Avalonia.Threading; +using Avalonia.VisualTree; using MessageBox.Avalonia.DTO; using MessageBox.Avalonia.Enums; @@ -31,7 +33,7 @@ namespace UVtools.WPF.Extensions WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")), WindowStartupLocation = location, CanResize = false, - MaxWidth = App.MaxWindowSize.Width - UserSettings.Instance.General.WindowsHorizontalMargin, + MaxWidth = window.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin, ShowInCenter = true }); @@ -76,6 +78,18 @@ namespace UVtools.WPF.Extensions window.DataContext = new object(); window.DataContext = old; } + + public static System.Drawing.Size GetScreenWorkingArea(this Window window) + { + var screen = window.Screens.ScreenFromVisual(window) ?? + window.Screens.ScreenFromVisual(App.MainWindow) ?? + window.Screens.Primary ?? + window.Screens.All[0]; + + return new System.Drawing.Size( + UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Width / screen.PixelDensity) : screen.WorkingArea.Width, + UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Height / screen.PixelDensity) : screen.WorkingArea.Height); + } } } diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 78de0d8..a704012 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -127,7 +127,7 @@ namespace UVtools.WPF Tag = new OperationRaftRelief(), Icon = new Avalonia.Controls.Image { - Source = new Bitmap(App.GetAsset("/Assets/Icons/dot-circle-16x16.png")) + Source = new Bitmap(App.GetAsset("/Assets/Icons/cookie-16x16.png")) } }, new MenuItem @@ -254,6 +254,14 @@ namespace UVtools.WPF Source = new Bitmap(App.GetAsset("/Assets/Icons/chart-pie-16x16.png")) } }, + new() + { + Tag = new OperationCalibrateExternalTests(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/bookmark-16x16.png")) + } + }, }; public static MenuItem[] LayerActionsMenu { get; } = @@ -448,14 +456,6 @@ namespace UVtools.WPF //PropertyChanged += OnPropertyChanged; UpdateTitle(); - UpdateMaxWindowsSize(); - - if (Settings.General.StartMaximized - || ClientSize.Width > App.MaxWindowSize.Width - || ClientSize.Height > App.MaxWindowSize.Height) - { - WindowState = WindowState.Maximized; - } var clientSizeObs = this.GetObservable(ClientSizeProperty); clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues()); @@ -471,24 +471,20 @@ namespace UVtools.WPF }); } - public void UpdateMaxWindowsSize() - { - /*Console.WriteLine($"Settings is null?: {Settings is null}"); - Console.WriteLine($"Settings.General is null?: {Settings.General is null}"); - Console.WriteLine($"Screens is null?: {Screens is null}"); - Console.WriteLine($"ScreenCount: {Screens.ScreenCount}"); - Console.WriteLine($"Screens.Primary is null?: {Screens.Primary is null}");*/ - var screen = Screens.Primary ?? Screens.All[0]; - App.MaxWindowSize = new System.Drawing.Size( - Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Width / screen.PixelDensity) : screen.WorkingArea.Width, - Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Height / screen.PixelDensity) : screen.WorkingArea.Height); - } - protected override void OnOpened(EventArgs e) { base.OnOpened(e); - - + + var windowSize = this.GetScreenWorkingArea(); + + if (Settings.General.StartMaximized + || ClientSize.Width > windowSize.Width + || ClientSize.Height > windowSize.Height) + { + WindowState = WindowState.Maximized; + } + + AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds); if (Settings.General.CheckForUpdatesOnStartup) @@ -768,7 +764,6 @@ namespace UVtools.WPF { SettingsWindow settingsWindow = new SettingsWindow(); await settingsWindow.ShowDialog(this); - UpdateMaxWindowsSize(); } public void OpenWebsite() diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 3c153db..c3ab44a 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.1.0</Version> + <Version>2.1.1</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -82,6 +82,9 @@ <Compile Update="Controls\AdvancedImageBox.axaml.cs"> <DependentUpon>AdvancedImageBox.axaml</DependentUpon> </Compile> + <Compile Update="Controls\Calibrators\CalibrateExternalTestsControl.axaml.cs"> + <DependentUpon>CalibrateExternalTestsControl.axaml</DependentUpon> + </Compile> <Compile Update="Controls\Tools\ToolLayerRemoveControl.axaml.cs"> <DependentUpon>ToolLayerRemoveControl.axaml</DependentUpon> </Compile> diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs index 6926d67..b94becd 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml.cs +++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs @@ -20,9 +20,9 @@ namespace UVtools.WPF.Windows public string FrameworkDescription => RuntimeInformation.FrameworkDescription; public int ProcessorCount => Environment.ProcessorCount; public int ScreenCount => Screens.ScreenCount; - public string ScreenResolution => $"{Screens.Primary.Bounds.Width} x {Screens.Primary.Bounds.Height} @ {Screens.Primary.PixelDensity*100}%"; - public string WorkingArea => $"{Screens.Primary.WorkingArea.Width} x {Screens.Primary.WorkingArea.Height}"; - public string RealWorkingArea => $"{App.MaxWindowSize.Width} x {App.MaxWindowSize.Height}"; + //public string ScreenResolution => $"{Screens.Primary.Bounds.Width} x {Screens.Primary.Bounds.Height} @ {Screens.Primary.PixelDensity*100}%"; + //public string WorkingArea => $"{Screens.Primary.WorkingArea.Width} x {Screens.Primary.WorkingArea.Height}"; + //public string RealWorkingArea => $"{App.MaxWindowSize.Width} x {App.MaxWindowSize.Height}"; public string ScreensDescription { @@ -31,8 +31,12 @@ namespace UVtools.WPF.Windows var result = new StringBuilder(); for (int i = 0; i < Screens.All.Count; i++) { + var onScreen = Screens.ScreenFromVisual(App.MainWindow); var screen = Screens.All[i]; - result.AppendLine($"{i+1}: {screen.Bounds.Width} x {screen.Bounds.Height} @ {screen.PixelDensity * 100}%"); + result.AppendLine($"{i+1}: {screen.Bounds.Width} x {screen.Bounds.Height} @ {screen.PixelDensity * 100}%" + + (screen.Primary ? " (Primary)" : string.Empty) + + (onScreen == screen ? " (On this)" : string.Empty) + ); result.AppendLine($" WA: {screen.WorkingArea.Width} x {screen.WorkingArea.Height} UA: {Math.Round(screen.WorkingArea.Width / screen.PixelDensity)} x {Math.Round(screen.WorkingArea.Height / screen.PixelDensity)}"); } return result.ToString().TrimEnd(); diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml.cs b/UVtools.WPF/Windows/SettingsWindow.axaml.cs index 3eac5ea..de4a1b5 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml.cs +++ b/UVtools.WPF/Windows/SettingsWindow.axaml.cs @@ -60,7 +60,7 @@ namespace UVtools.WPF.Windows s => Convert.ToString(s / 100, CultureInfo.InvariantCulture) + "x").ToArray(); - ScrollViewerMaxHeight = App.MaxWindowSize.Height - Settings.General.WindowsVerticalMargin; + ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Settings.General.WindowsVerticalMargin; DataContext = this; diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs index fafd25b..8f34b67 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml.cs +++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs @@ -524,7 +524,7 @@ namespace UVtools.WPF.Windows DispatcherTimer.Run(() => { if (Bounds.Width == 0) return true; - ScrollViewerMaxHeight = App.MaxWindowSize.Height - Bounds.Height + ToolControl.Bounds.Height - UserSettings.Instance.General.WindowsVerticalMargin; + ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Bounds.Height + ToolControl.Bounds.Height - UserSettings.Instance.General.WindowsVerticalMargin; DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl.Bounds.Width) - 40; Description = toolControl.BaseOperation.Description; return false; |