Files
UVtools/UVtools.Core/Operations/OperationRaftRelief.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

272 lines
9.8 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.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationRaftRelief : Operation
{
#region Enums
public enum RaftReliefTypes : byte
{
Relief,
Dimming,
Decimate
}
#endregion
#region Members
private RaftReliefTypes _reliefType = RaftReliefTypes.Relief;
private byte _ignoreFirstLayers;
private byte _brightness;
private byte _dilateIterations = 10;
private byte _wallMargin = 20;
private byte _holeDiameter = 50;
private byte _holeSpacing = 20;
#endregion
#region Overrides
public override string Title => "Raft relief";
public override string Description =>
"Relief raft by adding holes in between to reduce FEP suction, save resin and easier to remove the prints.";
public override string ConfirmationText =>
$"relief the raft";
public override string ProgressTitle =>
$"Relieving raft";
public override string ProgressAction => "Relieved layers";
public override Enumerations.LayerRangeSelection StartLayerRangeSelection =>
Enumerations.LayerRangeSelection.None;
public override string ToString()
{
var result = $"[{_reliefType}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Constructor
public OperationRaftRelief() { }
public OperationRaftRelief(FileFormat slicerFile) : base(slicerFile) { }
#endregion
#region Properties
public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes));
public RaftReliefTypes ReliefType
{
get => _reliefType;
set
{
if(!RaiseAndSetIfChanged(ref _reliefType, value)) return;
RaisePropertyChanged(nameof(IsRelief));
RaisePropertyChanged(nameof(IsDimming));
RaisePropertyChanged(nameof(IsDecimate));
}
}
public bool IsRelief => _reliefType == RaftReliefTypes.Relief;
public bool IsDimming => _reliefType == RaftReliefTypes.Dimming;
public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate;
public byte IgnoreFirstLayers
{
get => _ignoreFirstLayers;
set => RaiseAndSetIfChanged(ref _ignoreFirstLayers, value);
}
public byte Brightness
{
get => _brightness;
set
{
if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
RaisePropertyChanged(nameof(BrightnessPercent));
}
}
public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
public byte DilateIterations
{
get => _dilateIterations;
set => RaiseAndSetIfChanged(ref _dilateIterations, value);
}
public byte WallMargin
{
get => _wallMargin;
set => RaiseAndSetIfChanged(ref _wallMargin, value);
}
public byte HoleDiameter
{
get => _holeDiameter;
set => RaiseAndSetIfChanged(ref _holeDiameter, value);
}
public byte HoleSpacing
{
get => _holeSpacing;
set => RaiseAndSetIfChanged(ref _holeSpacing, value);
}
#endregion
#region Equality
protected bool Equals(OperationRaftRelief other)
{
return _reliefType == other._reliefType && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((OperationRaftRelief) obj);
}
public override int GetHashCode()
{
return HashCode.Combine((int) _reliefType, _ignoreFirstLayers, _brightness, _dilateIterations, _wallMargin, _holeDiameter, _holeSpacing);
}
#endregion
#region Methods
protected override bool ExecuteInternally(OperationProgress progress)
{
progress.ItemCount = 0;
const uint minLength = 5;
Mat supportsMat = null;
var anchor = new Point(-1, -1);
var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
uint firstSupportLayerIndex = 0;
for (; firstSupportLayerIndex < SlicerFile.LayerCount; firstSupportLayerIndex++)
{
progress.Reset("Tracing raft", SlicerFile.LayerCount, firstSupportLayerIndex);
if (progress.Token.IsCancellationRequested) return false;
supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 20, 100, 30, 5, 200);
if (circles.Length >= minLength) break;
supportsMat.Dispose();
supportsMat = null;
}
if (supportsMat is null) return false;
Mat patternMat = null;
if (DilateIterations > 0)
{
CvInvoke.Dilate(supportsMat, supportsMat,
CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(3, 3), new Point(-1, -1)),
new Point(-1, -1), DilateIterations, BorderType.Reflect101, new MCvScalar());
}
var color = new MCvScalar(255 - Brightness);
switch (ReliefType)
{
case RaftReliefTypes.Relief:
patternMat = EmguExtensions.InitMat(supportsMat.Size);
int shapeSize = HoleDiameter + HoleSpacing;
using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize)))
{
int center = HoleDiameter / 2;
//int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
int radius = center;
CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
CvInvoke.Circle(shape, new Point(0, 0), radius / 2, color, -1);
CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, color, -1);
CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, color, -1);
CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, color, -1);
CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
patternMat = new Mat(patternMat, new Rectangle(0, 0, supportsMat.Width, supportsMat.Height));
}
break;
case RaftReliefTypes.Dimming:
patternMat = EmguExtensions.InitMat(supportsMat.Size, color);
break;
}
progress.Reset(ProgressAction, firstSupportLayerIndex);
Parallel.For(IgnoreFirstLayers, firstSupportLayerIndex, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
using (Mat dst = SlicerFile[layerIndex].LayerMat)
{
var target = GetRoiOrDefault(dst);
switch (ReliefType)
{
case RaftReliefTypes.Relief:
case RaftReliefTypes.Dimming:
using (Mat mask = new Mat())
{
/*CvInvoke.Subtract(target, supportsMat, mask);
CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar());
CvInvoke.Subtract(target, patternMat, target, mask);*/
CvInvoke.Erode(target, mask, kernel, anchor, WallMargin, BorderType.Reflect101, default);
CvInvoke.Subtract(mask, supportsMat, mask);
CvInvoke.Subtract(target, patternMat, target, mask);
}
break;
case RaftReliefTypes.Decimate:
supportsMat.CopyTo(target);
break;
}
SlicerFile[layerIndex].LayerMat = dst;
}
lock (progress.Mutex)
{
progress++;
}
});
supportsMat.Dispose();
patternMat?.Dispose();
return !progress.Token.IsCancellationRequested;
}
#endregion
}
}