Files
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

445 lines
16 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 UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
public class OperationCalculator : Operation
{
#region Overrides
public override bool CanRunInPartialMode => true;
public override string IconClass => "fa-solid fa-calculator";
public override string Title => "Calculator";
public override string Description => null!;
public override string ConfirmationText => null!;
public override string ProgressTitle => null!;
public override string ProgressAction => null!;
public override LayerRangeSelection StartLayerRangeSelection => LayerRangeSelection.None;
public override bool CanROI => false;
public override bool CanHaveProfiles => false;
#endregion
#region Properties
public MillimetersToPixels CalcMillimetersToPixels { get; set; } = null!;
public LightOffDelayC CalcLightOffDelay { get; set; } = null!;
public OptimalModelTilt CalcOptimalModelTilt { get; set; } = null!;
#endregion
#region Constructor
public OperationCalculator() { }
public OperationCalculator(FileFormat slicerFile) : base(slicerFile)
{ }
public override void InitWithSlicerFile()
{
base.InitWithSlicerFile();
CalcMillimetersToPixels = new MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display);
CalcLightOffDelay = new LightOffDelayC(
(decimal)SlicerFile.LiftHeight, (decimal)SlicerFile.BottomLiftHeight,
(decimal)SlicerFile.LiftSpeed, (decimal)SlicerFile.BottomLiftSpeed,
(decimal)SlicerFile.RetractSpeed, (decimal)SlicerFile.RetractSpeed);
CalcOptimalModelTilt = new OptimalModelTilt(SlicerFile.Resolution, SlicerFile.Display, (decimal)SlicerFile.LayerHeight);
}
#endregion
public abstract class Calculation : BindableBase
{
public abstract string Description { get; }
public abstract string Formula { get; }
}
public sealed class MillimetersToPixels : Calculation
{
private uint _resolutionX;
private uint _resolutionY;
private decimal _displayWidth;
private decimal _displayHeight;
private decimal _millimeters = 1;
public override string Description => "Converts from Millimeters to Pixels";
public override string Formula => "Pixels = Resolution / Display * Millimeters";
public MillimetersToPixels(Size resolution, SizeF display, decimal millimeters = 1)
{
_resolutionX = (uint) resolution.Width;
_resolutionY = (uint) resolution.Height;
_displayWidth = (decimal) display.Width;
_displayHeight = (decimal) display.Height;
_millimeters = millimeters;
}
public uint ResolutionX
{
get => _resolutionX;
set
{
if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
RaisePropertyChanged(nameof(PixelsPerMillimeterX));
RaisePropertyChanged(nameof(PixelsX));
}
}
public uint ResolutionY
{
get => _resolutionY;
set
{
if(!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
RaisePropertyChanged(nameof(PixelsPerMillimeterY));
RaisePropertyChanged(nameof(PixelsY));
}
}
public decimal DisplayWidth
{
get => _displayWidth;
set
{
if(!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
RaisePropertyChanged(nameof(PixelsPerMillimeterX));
RaisePropertyChanged(nameof(PixelsX));
}
}
public decimal DisplayHeight
{
get => _displayHeight;
set
{
if(!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
RaisePropertyChanged(nameof(PixelsPerMillimeterY));
RaisePropertyChanged(nameof(PixelsY));
}
}
public decimal Millimeters
{
get => _millimeters;
set
{
if(!RaiseAndSetIfChanged(ref _millimeters, value)) return;
RaisePropertyChanged(nameof(PixelsX));
RaisePropertyChanged(nameof(PixelsY));
}
}
public decimal PixelsPerMillimeterX => DisplayWidth > 0 ? Math.Round(ResolutionX / DisplayWidth, 2) : 0;
public decimal PixelsPerMillimeterY => DisplayHeight > 0 ? Math.Round(ResolutionY / DisplayHeight, 2) : 0;
public decimal PixelsX => Math.Round(PixelsPerMillimeterX * Millimeters, 2);
public decimal PixelsY => Math.Round(PixelsPerMillimeterY * Millimeters, 2);
}
public sealed class LightOffDelayC : Calculation
{
private decimal _liftHeight;
private decimal _bottomLiftHeight;
private decimal _liftSpeed;
private decimal _bottomLiftSpeed;
private decimal _retractSpeed;
private decimal _bottomRetractSpeed;
private decimal _waitTime = 2.5m;
private decimal _bottomWaitTime = 3m;
public override string Description =>
"Calculates the required light-off delay (Moving time from the build plate + additional time for resin to stabilize) given the lifting height, speed and retract to wait x seconds before cure a new layer.\n" +
"Light-off delay is crucial for gaining higher-resolution and sharper prints.\n" +
"When the build plate retracts, it is important to allow enough time for the resin to stabilize before the UV lights turn on. This would ideally be around 2-3s.";
public override string Formula => "Light-off delay = Lifting height / (Lifting speed / 60) + Lifting height / (Retract speed / 60) + Desired wait seconds";
public decimal LiftHeight
{
get => _liftHeight;
set
{
if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return;
RaisePropertyChanged(nameof(LightOffDelay));
}
}
public decimal BottomLiftHeight
{
get => _bottomLiftHeight;
set
{
if(!RaiseAndSetIfChanged(ref _bottomLiftHeight, value)) return;
RaisePropertyChanged(nameof(BottomLightOffDelay));
}
}
public decimal LiftSpeed
{
get => _liftSpeed;
set
{
if(!RaiseAndSetIfChanged(ref _liftSpeed, value)) return;
RaisePropertyChanged(nameof(LightOffDelay));
}
}
public decimal BottomLiftSpeed
{
get => _bottomLiftSpeed;
set
{
if(!RaiseAndSetIfChanged(ref _bottomLiftSpeed, value)) return;
RaisePropertyChanged(nameof(BottomLightOffDelay));
}
}
public decimal RetractSpeed
{
get => _retractSpeed;
set
{
if(!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
RaisePropertyChanged(nameof(LightOffDelay));
BottomRetractSpeed = _retractSpeed;
}
}
public decimal BottomRetractSpeed
{
get => _bottomRetractSpeed;
set
{
if (!RaiseAndSetIfChanged(ref _bottomRetractSpeed, value)) return;
RaisePropertyChanged(nameof(BottomLightOffDelay));
}
}
public decimal WaitTime
{
get => _waitTime;
set
{
if(!RaiseAndSetIfChanged(ref _waitTime, value)) return;
RaisePropertyChanged(nameof(LightOffDelay));
}
}
public decimal BottomWaitTime
{
get => _bottomWaitTime;
set
{
if (!RaiseAndSetIfChanged(ref _bottomWaitTime, value)) return;
RaisePropertyChanged(nameof(BottomLightOffDelay));
}
}
public decimal LightOffDelay => CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, _waitTime);
public decimal BottomLightOffDelay => CalculateSeconds(_bottomLiftHeight, _bottomLiftSpeed, _bottomRetractSpeed, _bottomWaitTime);
public LightOffDelayC()
{
}
public LightOffDelayC(decimal liftHeight, decimal bottomLiftHeight, decimal liftSpeed, decimal bottomLiftSpeed, decimal retractSpeed, decimal bottomRetractSpeed, decimal waitTime = 2.5m, decimal bottomWaitTime = 3m)
{
_liftHeight = liftHeight;
_bottomLiftHeight = bottomLiftHeight;
_liftSpeed = liftSpeed;
_bottomLiftSpeed = bottomLiftSpeed;
_retractSpeed = retractSpeed;
_bottomRetractSpeed = bottomRetractSpeed;
_waitTime = waitTime;
_bottomWaitTime = bottomWaitTime;
}
public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retractSpeed, decimal extraWaitTime = 0)
{
var time = extraWaitTime;
if (liftSpeed > 0)
{
time += liftHeight / (liftSpeed / 60m);
}
if (retractSpeed > 0)
{
time += liftHeight / (retractSpeed / 60m);
}
return Math.Round(time, 2);
}
public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0,
float liftHeight2 = 0, float liftSpeed2 = 0, float retractHeight2 = 0, float retractSpeed2 = 0)
{
var time = extraWaitTime;
if (liftHeight > 0 && liftSpeed > 0)
time += liftHeight / (liftSpeed / 60f);
if (liftHeight2 > 0 && liftSpeed2 > 0)
time += liftHeight2 / (liftSpeed2 / 60f);
if (retractHeight2 > 0 && retractSpeed2 > 0)
time += retractHeight2 / (retractSpeed2 / 60f);
var remainingRetractHeight = liftHeight + liftHeight2 - retractHeight2;
if (remainingRetractHeight > 0 && retractSpeed > 0)
{
time += remainingRetractHeight / (retractSpeed / 60f);
}
return (float)Math.Round(time, 2);
}
public static float CalculateSeconds(Layer layer, float extraTime)
=> CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2);
public static uint CalculateMilliseconds(Layer layer, float extraTime)
=> (uint)(CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2) * 1000);
public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) =>
(uint)(CalculateSeconds(liftHeight, liftSpeed, retract, extraWaitTime) * 1000);
public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0)
{
var time = extraWaitTime;
if (liftHeight > 0 && liftSpeed > 0) time += (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2);
if (liftHeight2 > 0 && liftSpeed2 > 0) time += (float)Math.Round(liftHeight2 / (liftSpeed2 / 60f) + extraWaitTime, 2);
return time;
}
public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0) =>
(uint)(CalculateSecondsLiftOnly(liftHeight, liftSpeed, liftHeight2, liftSpeed2, extraWaitTime) * 1000);
public static float CalculateSecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime);
public static uint CalculateMillisecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
(uint)(CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime) * 1000);
}
public sealed class OptimalModelTilt : Calculation
{
private uint _resolutionX;
private uint _resolutionY;
private decimal _displayWidth;
private decimal _displayHeight;
private decimal _layerHeight = 0.05m;
public override string Description => "Calculates the optimal model tilt angle for printing and to minimize the visual layer effect.";
public override string Formula => "Angleº = arctan(Layer height / XYResolution) * (180 / PI)";
public OptimalModelTilt(Size resolution, SizeF display, decimal layerHeight = 0.05m)
{
_resolutionX = (uint)resolution.Width;
_resolutionY = (uint)resolution.Height;
_displayWidth = (decimal)display.Width;
_displayHeight = (decimal)display.Height;
_layerHeight = layerHeight;
}
public uint ResolutionX
{
get => _resolutionX;
set
{
if (!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
RaisePropertyChanged(nameof(XYResolution));
RaisePropertyChanged(nameof(XYResolutionUm));
RaisePropertyChanged(nameof(TiltAngleDegrees));
}
}
public uint ResolutionY
{
get => _resolutionY;
set
{
if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
RaisePropertyChanged(nameof(XYResolution));
RaisePropertyChanged(nameof(XYResolutionUm));
RaisePropertyChanged(nameof(TiltAngleDegrees));
}
}
public decimal DisplayWidth
{
get => _displayWidth;
set
{
if (!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
RaisePropertyChanged(nameof(XYResolution));
RaisePropertyChanged(nameof(XYResolutionUm));
RaisePropertyChanged(nameof(TiltAngleDegrees));
}
}
public decimal DisplayHeight
{
get => _displayHeight;
set
{
if (!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
RaisePropertyChanged(nameof(XYResolution));
RaisePropertyChanged(nameof(XYResolutionUm));
RaisePropertyChanged(nameof(TiltAngleDegrees));
}
}
public decimal LayerHeight
{
get => _layerHeight;
set
{
if (!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
RaisePropertyChanged(nameof(XYResolution));
RaisePropertyChanged(nameof(XYResolutionUm));
RaisePropertyChanged(nameof(TiltAngleDegrees));
}
}
public decimal XYResolution => DisplayWidth > 0 || DisplayHeight > 0 ?
Math.Max(
DisplayWidth / ResolutionX,
DisplayHeight / ResolutionY
)
: 0;
public decimal XYResolutionUm => Math.Round(XYResolution * 1000, 2);
public decimal TiltAngleDegrees =>
XYResolution > 0 ? (decimal) Math.Round(Math.Atan((double) (_layerHeight / XYResolution)) * (180 / Math.PI), 3) : 0;
}
}