mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+15
-12
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -1163,6 +1160,69 @@ namespace UVtools.Core
|
||||
return layerManager;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
return new StringTag(message.ToString(), result);*/
|
||||
|
||||
StringBuilder sb = new();
|
||||
|
||||
if (Files.Count == 0)
|
||||
{
|
||||
sb.AppendLine("No files to import.");
|
||||
}
|
||||
|
||||
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,56 +227,36 @@ namespace UVtools.Core.Operations
|
||||
public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
|
||||
{
|
||||
progress ??= new OperationProgress();
|
||||
progress.Reset(ProgressAction, (uint)Count);
|
||||
slicerFile.SuppressRebuildProperties = true;
|
||||
|
||||
List<FileFormat> fileFormats = new();
|
||||
List<KeyValuePair<uint, string>> keyImage = new();
|
||||
int lastProcessedLayerIndex = -1;
|
||||
|
||||
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++)
|
||||
// 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)
|
||||
{
|
||||
for (uint i = InsertAfterLayerIndex + 1; i < oldLayers.Length; i++)
|
||||
{
|
||||
layers[i] = oldLayers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Push remaining layers to the end of imported layers
|
||||
{
|
||||
uint oldLayerIndex = InsertAfterLayerIndex;
|
||||
for (uint i = LayerIndexEnd + 1; i < newLayerCount; i++)
|
||||
{
|
||||
oldLayerIndex++;
|
||||
layers[i] = oldLayers[oldLayerIndex];
|
||||
}
|
||||
}
|
||||
progress.Reset("Packing images", (uint) keyImage.Count);
|
||||
SL1File format = new SL1File();
|
||||
format.LayerManager = new LayerManager((uint) keyImage.Count, format);
|
||||
|
||||
|
||||
Parallel.For(0, Count,
|
||||
//new ParallelOptions{MaxDegreeOfParallelism = 1},
|
||||
i =>
|
||||
Parallel.ForEach(keyImage, pair =>
|
||||
{
|
||||
var mat = CvInvoke.Imread(Files[i].TagString, ImreadModes.Grayscale);
|
||||
uint layerIndex = (uint)(startIndex + i);
|
||||
if (MergeImages)
|
||||
{
|
||||
if (layers[layerIndex] is not null)
|
||||
{
|
||||
using var oldMat = layers[layerIndex].LayerMat;
|
||||
CvInvoke.Add(oldMat, mat, mat);
|
||||
}
|
||||
}
|
||||
layers[layerIndex] = new Layer(layerIndex, mat, slicerFile.LayerManager);
|
||||
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)
|
||||
{
|
||||
@@ -245,11 +264,239 @@ namespace UVtools.Core.Operations
|
||||
}
|
||||
});
|
||||
|
||||
slicerFile.LayerManager.Layers = layers;
|
||||
slicerFile.RequireFullEncode = true;
|
||||
progress.Token.ThrowIfCancellationRequested();
|
||||
fileFormats.Add(format);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fileFormat.FileFullPath))
|
||||
{
|
||||
fileFormat.Decode(fileFormat.FileFullPath, progress);
|
||||
}
|
||||
|
||||
var boundingRectangle = slicerFile.LayerManager.GetBoundingRectangle(progress);
|
||||
var fileFormatBoundingRectangle = fileFormat.LayerManager.GetBoundingRectangle(progress);
|
||||
var roiRectangle = Rectangle.Empty;
|
||||
|
||||
// Check if is possible to process this file
|
||||
switch (_importType)
|
||||
{
|
||||
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)
|
||||
{
|
||||
x = boundingRectangle.Right + _stackMargin;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = boundingRectangle.Bottom + _stackMargin;
|
||||
}
|
||||
|
||||
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 =>
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 114 B |
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
|
||||
ColumnDefinitions="Auto,10,100,5,Auto,20,Auto">
|
||||
|
||||
<StackPanel Spacing="10" Orientation="Horizontal">
|
||||
<CheckBox
|
||||
IsChecked="{Binding Operation.ReplaceStartLayer}"
|
||||
Content="Replace this layer"/>
|
||||
<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:"/>
|
||||
|
||||
<CheckBox
|
||||
IsChecked="{Binding Operation.ReplaceSubsequentLayers}"
|
||||
Content="Replace subsequent layers"/>
|
||||
<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 Operation.DiscardRemainingLayers}"
|
||||
Content="Discard remaining layers"/>
|
||||
|
||||
<CheckBox
|
||||
IsChecked="{Binding Operation.MergeImages}"
|
||||
Content="Merge images"/>
|
||||
</StackPanel>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user