diff options
Diffstat (limited to 'UVtools.Core/Operations/OperationDynamicLifts.cs')
-rw-r--r-- | UVtools.Core/Operations/OperationDynamicLifts.cs | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs new file mode 100644 index 0000000..03c7412 --- /dev/null +++ b/UVtools.Core/Operations/OperationDynamicLifts.cs @@ -0,0 +1,344 @@ +/* + * 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.Linq; +using System.Text; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationDynamicLifts : Operation + { + private float _minBottomLiftHeight; + private float _maxBottomLiftHeight; + private float _minLiftHeight; + private float _maxLiftHeight; + private float _minBottomLiftSpeed; + private float _maxBottomLiftSpeed; + private float _minLiftSpeed; + private float _maxLiftSpeed; + private bool _updateLightOffDelay = true; + private float _lightOffDelayBottomExtraTime = 3; + private float _lightOffDelayExtraTime = 2.5f; + + #region Members + + #endregion + + #region Overrides + + public override string Title => "Dynamic lifts"; + + public override string Description => + "Generate dynamic lift height and speeds for each layer given it mass\n" + + "Larger masses requires more lift height and less speed while smaller masses can go with shorter lift height and more speed.\n" + + "If you have a raft, start after it layer number to not influence the calculations.\n" + + "Note: Only few printers support this. Running this on an unsupported printer will cause no harm."; + + public override string ConfirmationText => + $"generate dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}?"; + + public override string ProgressTitle => + $"Generating dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => "Generated lifts"; + + public override string ValidateInternally() + { + var sb = new StringBuilder(); + + if (_minBottomLiftHeight > _maxBottomLiftHeight) + { + sb.AppendLine("Minimum bottom lift height can't be higher than the maximum."); + } + if (_minBottomLiftSpeed > _maxBottomLiftSpeed) + { + sb.AppendLine("Minimum bottom lift speed can't be higher than the maximum."); + } + + if (_minLiftHeight > _maxLiftHeight) + { + sb.AppendLine("Minimum lift height can't be higher than the maximum."); + } + if (_minLiftSpeed > _maxLiftSpeed) + { + sb.AppendLine("Minimum lift speed can't be higher than the maximum."); + } + + if (_minBottomLiftHeight == _maxBottomLiftHeight && + _minBottomLiftSpeed == _maxBottomLiftSpeed && + _minLiftHeight == _maxLiftHeight && + _minLiftSpeed == _maxLiftSpeed) + { + sb.AppendLine("The selected min/max settings are all equal and will not produce a change."); + } + + return sb.ToString(); + } + + public override string ToString() + { + var result = + $"[Bottom height: {_minBottomLiftHeight}/{_maxBottomLiftHeight}mm]" + + $" [Bottom speed: {_minBottomLiftSpeed}/{_maxBottomLiftSpeed}mm/min]" + + $" [Height: {_minLiftHeight}/{_maxLiftHeight}mm]" + + $" [Speed: {_minLiftSpeed}/{_maxLiftSpeed}mm/min]" + + $" [Light-off: {_updateLightOffDelay} {_lightOffDelayBottomExtraTime}/{_lightOffDelayExtraTime}s]" + + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + #endregion + + #region Properties + + public float MinBottomLiftHeight + { + get => _minBottomLiftHeight; + set => RaiseAndSetIfChanged(ref _minBottomLiftHeight, (float)Math.Round(value, 2)); + } + + public float MaxBottomLiftHeight + { + get => _maxBottomLiftHeight; + set => RaiseAndSetIfChanged(ref _maxBottomLiftHeight, (float)Math.Round(value, 2)); + } + + public float MinLiftHeight + { + get => _minLiftHeight; + set => RaiseAndSetIfChanged(ref _minLiftHeight, (float)Math.Round(value, 2)); + } + + public float MaxLiftHeight + { + get => _maxLiftHeight; + set => RaiseAndSetIfChanged(ref _maxLiftHeight, (float)Math.Round(value, 2)); + } + + public float MinBottomLiftSpeed + { + get => _minBottomLiftSpeed; + set => RaiseAndSetIfChanged(ref _minBottomLiftSpeed, (float)Math.Round(value, 2)); + } + + public float MaxBottomLiftSpeed + { + get => _maxBottomLiftSpeed; + set => RaiseAndSetIfChanged(ref _maxBottomLiftSpeed, (float)Math.Round(value, 2)); + } + + public float MinLiftSpeed + { + get => _minLiftSpeed; + set => RaiseAndSetIfChanged(ref _minLiftSpeed, (float)Math.Round(value, 2)); + } + + public float MaxLiftSpeed + { + get => _maxLiftSpeed; + set => RaiseAndSetIfChanged(ref _maxLiftSpeed, (float)Math.Round(value, 2)); + } + + public bool UpdateLightOffDelay + { + get => _updateLightOffDelay; + set => RaiseAndSetIfChanged(ref _updateLightOffDelay, value); + } + + public float LightOffDelayBottomExtraTime + { + get => _lightOffDelayBottomExtraTime; + set => RaiseAndSetIfChanged(ref _lightOffDelayBottomExtraTime, (float)Math.Round(value, 2)); + } + + public float LightOffDelayExtraTime + { + get => _lightOffDelayExtraTime; + set => RaiseAndSetIfChanged(ref _lightOffDelayExtraTime, (float)Math.Round(value, 2)); + } + + //public uint MinBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount); + public uint MinBottomLayerPixels => (from layer in SlicerFile + where layer.IsBottomLayer + where !layer.IsEmpty + where layer.Index >= LayerIndexStart + where layer.Index <= LayerIndexEnd + select layer.NonZeroPixelCount).Min(); + + //public uint MinNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount); + public uint MinNormalLayerPixels => (from layer in SlicerFile + where layer.IsNormalLayer + where !layer.IsEmpty + where layer.Index >= LayerIndexStart + where layer.Index <= LayerIndexEnd + select layer.NonZeroPixelCount).Min(); + + //public uint MaxBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount); + public uint MaxBottomLayerPixels => (from layer in SlicerFile + where layer.IsBottomLayer + where !layer.IsEmpty + where layer.Index >= LayerIndexStart + where layer.Index <= LayerIndexEnd + select layer.NonZeroPixelCount).Max(); + //public uint MaxNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount); + public uint MaxNormalLayerPixels => (from layer in SlicerFile + where layer.IsNormalLayer + where !layer.IsEmpty + where layer.Index >= LayerIndexStart + where layer.Index <= LayerIndexEnd + select layer.NonZeroPixelCount).Max(); + + public Layer MinBottomLayer => SlicerFile.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).OrderBy(layer => layer.NonZeroPixelCount).First(); + public Layer MaxBottomLayer => SlicerFile.Where(layer => layer.IsBottomLayer).OrderByDescending(layer => layer.NonZeroPixelCount).First(); + public Layer MinLayer => SlicerFile.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).OrderBy(layer => layer.NonZeroPixelCount).First(); + public Layer MaxLayer => SlicerFile.Where(layer => layer.IsNormalLayer).OrderByDescending(layer => layer.NonZeroPixelCount).First(); + + #endregion + + #region Constructor + + public OperationDynamicLifts() + { } + + public OperationDynamicLifts(FileFormat slicerFile) : base(slicerFile) + { + _minBottomLiftHeight = _maxBottomLiftHeight = SlicerFile.BottomLiftHeight; + _minLiftHeight = _maxLiftHeight = SlicerFile.LiftHeight; + + _minBottomLiftSpeed = _maxBottomLiftSpeed = SlicerFile.BottomLiftSpeed; + _minLiftSpeed = _maxLiftSpeed = SlicerFile.LiftSpeed; + } + + #endregion + + #region Methods + + protected override bool ExecuteInternally(OperationProgress progress) + { + uint minBottomPixels = 0; + uint minNormalPixels = 0; + uint maxBottomPixels = 0; + uint maxNormalPixels = 0; + + try + { + minBottomPixels = MinBottomLayerPixels; + } + catch + { + } + + try + { + minNormalPixels = MinNormalLayerPixels; + } + catch + { + } + + try + { + maxBottomPixels = MaxBottomLayerPixels; + } + catch + { + } + + try + { + maxNormalPixels = MaxNormalLayerPixels; + } + catch + { + } + + float liftHeight; + float liftSpeed; + + uint max = (from layer in SlicerFile where !layer.IsBottomLayer where !layer.IsEmpty where layer.Index >= LayerIndexStart where layer.Index <= LayerIndexEnd select layer).Aggregate<Layer, uint>(0, (current, layer) => Math.Max(layer.NonZeroPixelCount, current)); + + for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + var layer = SlicerFile[layerIndex]; + + // Height + // min - largestpixelcount + // x - pixelcount + + // Speed + // max - minpixelCount + // x - pixelcount + + if (layer.IsBottomLayer) + { + liftHeight = (_maxBottomLiftHeight * layer.NonZeroPixelCount / maxBottomPixels).Clamp(_minBottomLiftHeight, _maxBottomLiftHeight); + liftSpeed = (_maxBottomLiftSpeed - (_maxBottomLiftSpeed * layer.NonZeroPixelCount / maxNormalPixels)).Clamp(_minBottomLiftSpeed, _maxBottomLiftSpeed); + } + else + { + liftHeight = (_maxLiftHeight * layer.NonZeroPixelCount / maxNormalPixels).Clamp(_minLiftHeight, _maxLiftHeight); + liftSpeed = (_maxLiftSpeed - (_maxLiftSpeed * layer.NonZeroPixelCount / maxNormalPixels)).Clamp(_minLiftSpeed, _maxLiftSpeed); + } + + layer.LiftHeight = (float) Math.Round(liftHeight, 2); + layer.LiftSpeed = (float) Math.Round(liftSpeed, 2); + + if (_updateLightOffDelay) + { + layer.UpdateLightOffDelay(layer.IsBottomLayer ? _lightOffDelayBottomExtraTime : _lightOffDelayExtraTime); + } + + progress++; + } + + SlicerFile.UpdatePrintTime(); + + return !progress.Token.IsCancellationRequested; + } + + + #endregion + + #region Equality + + private bool Equals(OperationDynamicLifts other) + { + return _minBottomLiftHeight.Equals(other._minBottomLiftHeight) && _maxBottomLiftHeight.Equals(other._maxBottomLiftHeight) && _minLiftHeight.Equals(other._minLiftHeight) && _maxLiftHeight.Equals(other._maxLiftHeight) && _minBottomLiftSpeed.Equals(other._minBottomLiftSpeed) && _maxBottomLiftSpeed.Equals(other._maxBottomLiftSpeed) && _minLiftSpeed.Equals(other._minLiftSpeed) && _maxLiftSpeed.Equals(other._maxLiftSpeed) && _updateLightOffDelay == other._updateLightOffDelay && _lightOffDelayBottomExtraTime.Equals(other._lightOffDelayBottomExtraTime) && _lightOffDelayExtraTime.Equals(other._lightOffDelayExtraTime); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationDynamicLifts other && Equals(other); + } + + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add(_minBottomLiftHeight); + hashCode.Add(_maxBottomLiftHeight); + hashCode.Add(_minLiftHeight); + hashCode.Add(_maxLiftHeight); + hashCode.Add(_minBottomLiftSpeed); + hashCode.Add(_maxBottomLiftSpeed); + hashCode.Add(_minLiftSpeed); + hashCode.Add(_maxLiftSpeed); + hashCode.Add(_updateLightOffDelay); + hashCode.Add(_lightOffDelayBottomExtraTime); + hashCode.Add(_lightOffDelayExtraTime); + return hashCode.ToHashCode(); + } + + #endregion + } +} |