Files
UVtools/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
Tiago Conceição 8e4995a6a5 v3.8.3
- **UVtoolsCmd:**
  - **print-properties:**
    - (Change) `-b`, `--base` option to `-a`, `-all` to indicate all properties and sub-properties, now defaults to only show base properties
    - (Add) `-r`, `--range` option to prints only the matching layer(s) index(es) in a range
    - (Add) `-i`, `--indexes` option to prints only the matching layer(s) index(es)
  - (Add) Command: `set-properties <input-file> <property=value> Set properties in a file or to it layers with new values`
  - (Add) Command: `print-issues <input-file> Detect and print issues in a file`
  - (Add) New option to the `run` command: `-p, --property <property=value>  Set a property with a new value (Compatible with operations only)`
  - (Remove) Command: `print-layers` as it has been moved to `print-properties`, use `-r :` to obtain same result as default on `print-layers`
- **Issues:**
  - (Fix) Issues groups with only one issue was displaying the wrong area value
  - (Fix) Volume incorrectly calculated, resulting in a high value for group of issues
  - (Fix) Incorrect calculation of the bounding rectangle for a group of issues
- **Repair layers:**
  - (Add) Switch to opt between "Re-detect the selected issues before repair" and "Use and repair the previous detected issues" (Default)
  - (Improvement) Do not allow to run the tool if there are no detected issues when the option "Use and repair the previous detected issues" is selected
- (Improvement) Linux: Recompile libcvextern.so on a older system to be able to run on both older and newest system (#603)
- (Upgrade) .NET from 6.0.10 to 6.0.11
2022-11-10 04:09:00 +00:00

443 lines
15 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 Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Drawing;
using System.Text;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
public sealed class OperationCalibrateLiftHeight : Operation
{
#region Members
private decimal _layerHeight;
private ushort _bottomLayers = 3;
private ushort _normalLayers = 2;
private decimal _bottomExposure;
private decimal _normalExposure;
private decimal _bottomLiftHeight;
private decimal _liftHeight;
private decimal _bottomLiftSpeed;
private decimal _liftSpeed;
private decimal _retractSpeed;
private ushort _leftRightMargin = 200;
private ushort _topBottomMargin = 200;
private bool _decreaseImage = true;
private byte _decreaseImageFactor = 10;
private byte _minimumImageFactor = 10;
#endregion
#region Overrides
public override bool CanROI => false;
public override bool CanCancel => false;
public override LayerRangeSelection StartLayerRangeSelection => LayerRangeSelection.None;
public override string IconClass => "mdi-arrow-expand-up";
public override string Title => "Lift height";
public override string Description =>
"Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area.\n" +
"You must have a tool to measure the lift height / forces as it moves up, record the values to determine the lowest safe value for the lift.\n" +
"After find the height where it peels, you must give from 1mm to 2mm more for safeness.\n" +
"You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
"Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
public override string ConfirmationText =>
$"generate the lift height test?";
public override string ProgressTitle =>
$"Generating the lift height test";
public override string ProgressAction => "Generated";
public override string? ValidateInternally()
{
var sb = new StringBuilder();
if (SlicerFile.ResolutionX - _leftRightMargin * 2 <= 0)
sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
if (SlicerFile.ResolutionY - _topBottomMargin * 2 <= 0)
sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
if (_decreaseImage)
{
if(_decreaseImageFactor is 0 or >= 100)
sb.AppendLine("The image decrease factor must be between 1 and 99%");
if(_minimumImageFactor is 0 or >= 100)
sb.AppendLine("The minimum image decrease factor must be between 1 and 99%");
}
return sb.ToString();
}
public override string ToString()
{
var result = $"[Layer Height: {_layerHeight}] " +
$"[Layers: {_bottomLayers}/{_normalLayers}] " +
$"[Exposure: {_bottomExposure}/{_normalExposure}s] " +
$"[Lift: {_bottomLiftHeight}/{_liftHeight}mm @ {_bottomLiftSpeed}/{_liftSpeed}mm/min]" +
$"[Retract speed: {_retractSpeed}mm/min]" +
$"[Decrease image: {_decreaseImage} @ {_decreaseImageFactor}-{_minimumImageFactor}%]";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Properties
public decimal LayerHeight
{
get => _layerHeight;
set
{
if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
RaisePropertyChanged(nameof(BottomHeight));
RaisePropertyChanged(nameof(NormalHeight));
RaisePropertyChanged(nameof(TotalHeight));
}
}
public ushort Microns => (ushort) (LayerHeight * 1000);
public ushort BottomLayers
{
get => _bottomLayers;
set
{
if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
RaisePropertyChanged(nameof(BottomHeight));
RaisePropertyChanged(nameof(TotalHeight));
RaisePropertyChanged(nameof(LayerCount));
}
}
public ushort NormalLayers
{
get => _normalLayers;
set
{
if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
RaisePropertyChanged(nameof(NormalHeight));
RaisePropertyChanged(nameof(TotalHeight));
RaisePropertyChanged(nameof(LayerCount));
}
}
public uint LayerCount
{
get
{
uint layerCount = (uint)(_bottomLayers + _normalLayers);
if (_decreaseImage)
{
layerCount += (100u - _minimumImageFactor) / _decreaseImageFactor;
//layerCount += (uint)Math.Ceiling((100.0 - _minimumImageFactor - _decreaseImageFactor) / _decreaseImageFactor);
//for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
// layerCount++;
}
return layerCount;
}
}
public decimal BottomHeight => Layer.RoundHeight(_layerHeight * _bottomLayers);
public decimal NormalHeight => Layer.RoundHeight(_layerHeight * (LayerCount - _bottomLayers));
public decimal TotalHeight => BottomHeight + NormalHeight;
public decimal BottomExposure
{
get => _bottomExposure;
set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
}
public decimal NormalExposure
{
get => _normalExposure;
set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
}
public decimal BottomLiftHeight
{
get => _bottomLiftHeight;
set => RaiseAndSetIfChanged(ref _bottomLiftHeight, value);
}
public decimal LiftHeight
{
get => _liftHeight;
set => RaiseAndSetIfChanged(ref _liftHeight, value);
}
public decimal BottomLiftSpeed
{
get => _bottomLiftSpeed;
set => RaiseAndSetIfChanged(ref _bottomLiftSpeed, value);
}
public decimal LiftSpeed
{
get => _liftSpeed;
set => RaiseAndSetIfChanged(ref _liftSpeed, value);
}
public decimal RetractSpeed
{
get => _retractSpeed;
set => RaiseAndSetIfChanged(ref _retractSpeed, value);
}
public ushort LeftRightMargin
{
get => _leftRightMargin;
set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
}
public ushort MaxLeftRightMargin => (ushort)((SlicerFile.ResolutionX - 100) / 2);
public ushort TopBottomMargin
{
get => _topBottomMargin;
set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
}
public ushort MaxTopBottomMargin => (ushort) ((SlicerFile.ResolutionY - 100) / 2);
public bool DecreaseImage
{
get => _decreaseImage;
set
{
RaiseAndSetIfChanged(ref _decreaseImage, value);
RaisePropertyChanged(nameof(TotalHeight));
RaisePropertyChanged(nameof(LayerCount));
}
}
public byte DecreaseImageFactor
{
get => _decreaseImageFactor;
set
{
RaiseAndSetIfChanged(ref _decreaseImageFactor, value);
RaisePropertyChanged(nameof(TotalHeight));
RaisePropertyChanged(nameof(LayerCount));
}
}
public byte MinimumImageFactor
{
get => _minimumImageFactor;
set
{
RaiseAndSetIfChanged(ref _minimumImageFactor, value);
RaisePropertyChanged(nameof(TotalHeight));
RaisePropertyChanged(nameof(LayerCount));
}
}
public Rectangle WhiteBlock
{
get
{
int width = (int) (SlicerFile.ResolutionX - _leftRightMargin * 2);
int height = (int) (SlicerFile.ResolutionY - _topBottomMargin * 2);
int x = (int) (SlicerFile.ResolutionX - width) / 2;
int y = (int) (SlicerFile.ResolutionY - height) / 2;
return new Rectangle(x, y, width, height);
}
}
#endregion
#region Constructor
public OperationCalibrateLiftHeight() { }
public OperationCalibrateLiftHeight(FileFormat slicerFile) : base(slicerFile)
{ }
public override void InitWithSlicerFile()
{
base.InitWithSlicerFile();
if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
if (_bottomLiftHeight <= 0) _bottomLiftHeight = (decimal)SlicerFile.BottomLiftHeight;
if (_liftHeight <= 0) _liftHeight = (decimal)SlicerFile.LiftHeight;
if (_bottomLiftSpeed <= 0) _bottomLiftSpeed = (decimal) SlicerFile.BottomLiftSpeed;
if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed;
if (_retractSpeed <= 0) _retractSpeed = (decimal) SlicerFile.RetractSpeed;
}
#endregion
#region Equality
private bool Equals(OperationCalibrateLiftHeight other)
{
return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _bottomLiftHeight == other._bottomLiftHeight && _liftHeight == other._liftHeight && _bottomLiftSpeed == other._bottomLiftSpeed && _liftSpeed == other._liftSpeed && _retractSpeed == other._retractSpeed && _leftRightMargin == other._leftRightMargin && _topBottomMargin == other._topBottomMargin && _decreaseImage == other._decreaseImage && _decreaseImageFactor == other._decreaseImageFactor && _minimumImageFactor == other._minimumImageFactor;
}
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is OperationCalibrateLiftHeight other && Equals(other);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(_layerHeight);
hashCode.Add(_bottomLayers);
hashCode.Add(_normalLayers);
hashCode.Add(_bottomExposure);
hashCode.Add(_normalExposure);
hashCode.Add(_bottomLiftHeight);
hashCode.Add(_liftHeight);
hashCode.Add(_bottomLiftSpeed);
hashCode.Add(_liftSpeed);
hashCode.Add(_retractSpeed);
hashCode.Add(_leftRightMargin);
hashCode.Add(_topBottomMargin);
hashCode.Add(_decreaseImage);
hashCode.Add(_decreaseImageFactor);
hashCode.Add(_minimumImageFactor);
return hashCode.ToHashCode();
}
#endregion
#region Methods
/// <summary>
/// Gets the bottom and normal layers, 0 = bottom | 1 = normal
/// </summary>
/// <returns></returns>
public Mat[] GetLayers()
{
var layers = new Mat[1];
layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
CvInvoke.Rectangle(layers[0], WhiteBlock, EmguExtensions.WhiteColor, -1);
return layers;
}
public Mat GetThumbnail()
{
Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
var fontFace = FontFace.HersheyDuplex;
var fontScale = 1;
var fontThickness = 2;
const byte xSpacing = 45;
const byte ySpacing = 45;
CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
CvInvoke.PutText(thumbnail, "Lift Height Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
//CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
return thumbnail;
}
protected override bool ExecuteInternally(OperationProgress progress)
{
progress.ItemCount = LayerCount;
var newLayers = new Layer[LayerCount];
var layers = GetLayers();
progress++;
var layer = new Layer(0, layers[0], SlicerFile)
{
IsModified = true
};
uint layerIndex = 0;
for (; layerIndex < _bottomLayers + _normalLayers; layerIndex++)
{
newLayers[layerIndex] = layer.Clone();
progress++;
}
if (_decreaseImage)
{
var rect = WhiteBlock;
for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
{
using var mat = layers[0].NewBlank();
// size - 100
// x - factor
int width = rect.Width * factor / 100;
int height = rect.Height * factor / 100;
int x = (int)(SlicerFile.ResolutionX - width) / 2;
int y = (int)(SlicerFile.ResolutionY - height) / 2;
CvInvoke.Rectangle(mat,
new Rectangle(x, y, width, height),
EmguExtensions.WhiteColor, -1);
newLayers[layerIndex] = new Layer(0, mat, SlicerFile)
{
IsModified = true
};
layerIndex++;
progress++;
}
}
foreach (var mat in layers)
{
mat.Dispose();
}
if (SlicerFile.ThumbnailsCount > 0)
SlicerFile.SetThumbnails(GetThumbnail());
progress++;
SlicerFile.SuppressRebuildPropertiesWork(() =>
{
SlicerFile.LayerHeight = (float)_layerHeight;
SlicerFile.BottomExposureTime = (float)_bottomExposure;
SlicerFile.ExposureTime = (float)_normalExposure;
SlicerFile.BottomLiftHeight = (float)_bottomLiftHeight;
SlicerFile.LiftHeight = (float)_liftHeight;
SlicerFile.BottomLiftSpeed = (float)_bottomLiftSpeed;
SlicerFile.LiftSpeed = (float)_liftSpeed;
SlicerFile.RetractSpeed = (float)_retractSpeed;
SlicerFile.BottomLayerCount = _bottomLayers;
SlicerFile.TransitionLayerCount = 0;
SlicerFile.Layers = newLayers;
}, true);
return !progress.Token.IsCancellationRequested;
}
#endregion
}