Files
UVtools/UVtools.Core/Operations/OperationChangeResolution.cs
T
Tiago Conceição 0ba61780b7 v2.4.0
* (Upgrade) EmguCV/OpenCV to v4.5.1
* (Upgrade) AvaloniaUI to 1.0
* (Improvement) GUI re-touched
* (Improvement) Make pixel editor tab to disappear when pixel editor is disabled
* (Improvement) Simplify the output filename from PrusaSlicer profiles
* (Improvement) All operations require a slicer file at constructor rather than on execute, this allow exposure the open file to the operation before run it
* (Improvement) Calibrations: Auto set "Mirror Output" if open file have MirrorDisplay set
* (Change) Tool - Redraw model/supports icon
* (Change) photon and cbddlp to use version 3 by default
* (Add) Tool - Dynamic layer height: Analyze and optimize the model with dynamic layer heights, larger angles will slice at lower layer height
        while more straight angles will slice larger layer height. (#131)
* (Add) Calibration - Exposure time finder: Generates test models with various strategies and increments to verify the best exposure time for a given layer height
* (Add) File load checks, trigger error when a file have critical errors and attempt to fix non-critical errors
  * Layers must have an valid image, otherwise trigger an error
  * Layers must have a incremental or equal position Z than it previous, otherwise trigger an error
  * If layer 0 starts at 0mm it will auto fix all layers, it will add Layer Height to the current z at every layer
* (Add) Tool - Edit print parameters: Allow set parameters to each x layers and skip n layers inside the given range.
        This allow the use of optimizations in a layer pattern, for example, to set 3s for a layer but 2.5s for the next.
* (Add) Layer height property to "Layer Data" table: Shows layer height for the slice
* (Fix) When automations applied and file is saved, it will not warn user about file overwrite for the first time save
* (Fix) Tool - Redraw model/supports: Disable apply button when no file selected
* (Fix) Tool - Infill: Lack of equality member to test if same infill profile already exists
* (Fix) Auto converted files from SL1 where clipping filename at first dot (.), now it only strips known extensions
* (Fix) SL1 encoded files wasn't generating the right information and lead to printer crash
* (Fix) PrusaSlicer printer "Anycubic Photon S" LiftSpeed was missing and contains a typo (#135)
* (Fix) PrusaSlicer profile manager wasnt marking missing profiles to be installed (#135)
* (Fix) PrusaSlicer folder search on linux to also look at %HOME%/.config/PrusaSlicer (#135, #136)
* (Fix) Operations were revised and some bug fixed, most about can't cancel the progress
* (Fix) Some typos on tooltips
* (Fix) Prevent PhotonS from enconding, it will trigger error now as this format is read-only
* **(Fix) Ctrl + Shift + Z to redo the last operation:**
  * The layer range is reseted instead of pull the used values
  * Tool - Arithmetic always disabled
  * Action - Layer import didn't generate info and always disabled
2021-02-06 22:09:55 +00:00

228 lines
8.4 KiB
C#

/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
[Serializable]
public sealed class OperationChangeResolution : Operation
{
#region Members
private uint _newResolutionX;
private uint _newResolutionY;
#endregion
#region Subclasses
public class Resolution
{
public uint ResolutionX { get; }
public uint ResolutionY { get; }
public string Name { get; }
public bool IsEmpty => ResolutionX == 0 && ResolutionY == 0;
public Resolution(uint resolutionX, uint resolutionY, string name = null)
{
ResolutionX = resolutionX;
ResolutionY = resolutionY;
Name = name;
}
public override string ToString()
{
if(IsEmpty) return string.Empty;
var str = $"{ResolutionX} x {ResolutionY}";
if (!string.IsNullOrEmpty(Name))
{
str += $" ({Name})";
}
return str;
}
}
#endregion
#region Overrides
public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI => false;
public override string Title => "Change print resolution";
public override string Description =>
"Crops or resizes all layer images to fit an alternate print area\n\n" +
"Useful to make files printable on a different printer than they were originally sliced for without the need to re-slice.\n\n" +
"NOTE: Please ensure that the actual model will fit within the new print resolution. The operation will be aborted if it will result in any of the actual model being clipped.";
public override string ConfirmationText =>
"change print resolution " +
$"from {SlicerFile.ResolutionX}x{SlicerFile.ResolutionY} " +
$"to {NewResolutionX}x{NewResolutionY}?";
public override string ProgressTitle =>
$"Changing print resolution from ({SlicerFile.ResolutionX}x{SlicerFile.ResolutionY}) to ({NewResolutionX}x{NewResolutionY})";
public override string ProgressAction => "Changed layers";
public override StringTag Validate(params object[] parameters)
{
var sb = new StringBuilder();
if (SlicerFile.ResolutionX == NewResolutionX && SlicerFile.ResolutionY == NewResolutionY)
{
sb.AppendLine($"The new resolution must be different from current resolution ({SlicerFile.ResolutionX} x {SlicerFile.ResolutionY}).");
}
if (NewResolutionX < SlicerFile.BoundingRectangle.Width || NewResolutionY < SlicerFile.BoundingRectangle.Height)
{
sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({SlicerFile.BoundingRectangle.Width} x {SlicerFile.BoundingRectangle.Height}), continuing operation would clip the model");
sb.AppendLine("To fix this, try to rotate the object and/or resize to fit on this new resolution.");
}
return new StringTag(sb.ToString());
}
public override string ToString()
{
var result = $"{_newResolutionX} x {_newResolutionY}";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Properties
public uint NewResolutionX
{
get => _newResolutionX;
set => RaiseAndSetIfChanged(ref _newResolutionX, value);
}
public uint NewResolutionY
{
get => _newResolutionY;
set => RaiseAndSetIfChanged(ref _newResolutionY, value);
}
public Size VolumeBondsSize => SlicerFile.BoundingRectangle.Size;
#endregion
#region Constructor
public OperationChangeResolution() { }
public OperationChangeResolution(FileFormat slicerFile) : base(slicerFile)
{
NewResolutionX = SlicerFile.ResolutionX;
NewResolutionY = SlicerFile.ResolutionY;
}
#endregion
#region Methods
public static Resolution[] GetResolutions()
{
return new [] {
//new Resolution(0, 0, string.Empty),
new Resolution(854, 480, "FWVGA"),
new Resolution(960, 1708),
new Resolution(1080, 1920, "FHD"),
new Resolution(1440, 2560, "QHD"),
new Resolution(1600, 2560, "WQXGA"),
new Resolution(1620, 2560, "WQXGA"),
new Resolution(1920, 1080, "FHD"),
new Resolution(2160, 3840, "4K UHD"),
new Resolution(2531, 1410, "QHD"),
new Resolution(2560, 1440, "QHD"),
new Resolution(2560, 1600, "WQXGA"),
new Resolution(2560, 1620, "WQXGA"),
new Resolution(3840, 2160, "4K UHD"),
new Resolution(3840, 2400, "WQUXGA"),
};
}
public static Resolution[] Presets => GetResolutions();
protected override bool ExecuteInternally(OperationProgress progress)
{
progress.ItemCount = SlicerFile.LayerCount;
Parallel.For(0, SlicerFile.LayerCount, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
using var mat = SlicerFile[layerIndex].LayerMat;
using var matRoi = new Mat(mat, SlicerFile.BoundingRectangle);
using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
using var matDstRoi = new Mat(matDst,
new Rectangle((int)(NewResolutionX / 2 - SlicerFile.BoundingRectangle.Width / 2),
(int)NewResolutionY / 2 - SlicerFile.BoundingRectangle.Height / 2,
SlicerFile.BoundingRectangle.Width, SlicerFile.BoundingRectangle.Height));
matRoi.CopyTo(matDstRoi);
//Execute(mat);
SlicerFile[layerIndex].LayerMat = matDst;
lock (progress.Mutex)
{
progress++;
}
});
progress.Token.ThrowIfCancellationRequested();
SlicerFile.ResolutionX = NewResolutionX;
SlicerFile.ResolutionY = NewResolutionY;
return !progress.Token.IsCancellationRequested;
}
/*public override bool Execute(Mat mat, params object[] arguments)
{
//mat.Transform(1, 1, newResolutionX-mat.Width+roi.Width, newResolutionY-mat.Height - roi.Height/2, new Size((int) newResolutionX, (int) newResolutionY));
using var matRoi = new Mat(mat, VolumeBonds);
using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
using var matDstRoi = new Mat(matDst,
new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2),
(int)NewResolutionY / 2 - VolumeBonds.Height / 2,
VolumeBonds.Width, VolumeBonds.Height));
matRoi.CopyTo(matDstRoi);
return true;
}*/
#endregion
#region Equality
private bool Equals(OperationChangeResolution other)
{
return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY;
}
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is OperationChangeResolution other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _newResolutionX * 397) ^ (int) _newResolutionY;
}
}
#endregion
}
}