mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-13 20:07:40 +02:00
3f838001c7
* **Core:** * Write "Unhandled Exceptions" to an "errors.log" file at user settings directory * Increase layer height max precision from 2 to 3 decimals * **Settings - Layer Preview:** * Allow to set hollow line thickness to -1 to fill the area * Add tooltip for "Auto rotate on load": Auto rotate the layer preview on file load for a landscape viewport * Add option to masks outline color and thickness * Add option to clear ROI when adding masks * Add option "Auto flip on load": Auto flip the layer preview on file load if the file is marked to print mirrored on the printer LCD * **Layer preview:** * Add selectable rotation directions 90º (CW and CCW) * Add preview flip (CTRL+F) horizontally and/or vertically * Add maskable regions to process on a layer (SHIFT + Alt + Click) on a area * ROI: Shortcut "Shift + left click" now also selects hollow black areas inside a white perimeter * ROI: Shortcut "ESC + Shift" to clear only the ROI and leave masks in * Fix a crash when using the pixel picker tool outside image bounds * **Pixel editor:** * Change drawings brush diameter limit from 255 to 4000 maximum * When using layers below go lower than layer 0 it no longer apply the modifications * **File formats:** * Add an internal GCodeBuilder to generate identical gcode within formats with auto convertion, managing features and parsing information * Internally rearrange formats properties and pass values to the base class * Fix "Save As" filename when using formats with dual extensions * CBDDLP and CTB: "LightPWM" was setting "BottomLightPWM" instead * CWS: Fix problem with filenames with dots (.) and ending with numbers (#171) * CWS: Improved the enconding and decoding performance * CWS: Implement all available print paramenters and per layer, missing values are got from gcode interpretation * CWS: Use set "light off delay" layer value instead of calculating it * CWS: Get light off delay per layer parsed from gcode * CWS - RGB flavour (Bene4 Mono): Warn about wrong resolution if width not multiples of 3 (#171) * ZCode: Allow to set Bottom and Light intensity (LightPWM) on paramters and per layer * ZCode: Allow to change bottom light pwm independent from normal light pwm * LGS: Light off and bottom light off was setting the value on the wrong units * UVJ: Unable to set per layer parameters * **Issues:** * When computing islands and resin traps together, they will not compute in parallel anymore to prevent CPU and tasks exaustion, it will calculate islands first and then resin traps, this should also speed up the process on weaker machines * Gather resin trap areas together when computing for other issues to spare a decoding cycle latter * When using a threshold for islands detection it was also appling it to the overhangs * Fix the spare decoding conditional cycle for partial scans * Change resin trap search from parallel to sync to prevent fake detections and missing joints at cost of speed (#13) * **Tools:** * Add layer selector: 'From first to current layer' and 'From current to last layer' * I printed this file: Multiplier - Number of time(s) the file has been printed. Half numbers can be used to consume from a failed print. Example: 0.5x if a print canceled at 50% progress * Pixel dimming: Increase wall thickness default from 5px to 10px * Import layers: Importing layers was not marking layers as modified, then the save file won't save the new images in, to prevent other similar bugs, all layers that got replaced will be auto marked as modified
259 lines
9.5 KiB
C#
259 lines
9.5 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.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Serialization;
|
|
using Emgu.CV;
|
|
using Emgu.CV.CvEnum;
|
|
using Emgu.CV.Structure;
|
|
using Emgu.CV.Util;
|
|
using UVtools.Core.Extensions;
|
|
using UVtools.Core.FileFormats;
|
|
using UVtools.Core.Objects;
|
|
|
|
namespace UVtools.Core.Operations
|
|
{
|
|
[Serializable]
|
|
public class OperationRedrawModel : Operation
|
|
{
|
|
#region Members
|
|
|
|
private string _filePath;
|
|
private byte _brightness = 220;
|
|
private bool _contactPointsOnly = true;
|
|
private RedrawTypes _redrawType = RedrawTypes.Supports;
|
|
private bool _ignoreContactLessPixels = true;
|
|
|
|
#endregion
|
|
|
|
#region Overrides
|
|
|
|
public override Enumerations.LayerRangeSelection StartLayerRangeSelection { get; } = Enumerations.LayerRangeSelection.None;
|
|
|
|
public override string Title => "Redraw model/supports";
|
|
|
|
public override string Description =>
|
|
"Redraw the model or supports with a set brightness. This requires an extra sliced file from same object but without any supports and raft, straight to the build plate.\n" +
|
|
"Note: Run this tool prior to any made modification. You must find the optimal exposure/brightness combo, or supports can fail.";
|
|
|
|
public override string ConfirmationText => "redraw the "+ (_redrawType == RedrawTypes.Supports ? "supports" : "model") +
|
|
$" with an brightness of {_brightness}?";
|
|
|
|
public override string ProgressTitle => "Redrawing " + (_redrawType == RedrawTypes.Supports ? "supports" : "model");
|
|
|
|
public override string ProgressAction => "Redraw layers";
|
|
|
|
public override StringTag Validate(params object[] parameters)
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
if (IsFileValid() is null)
|
|
{
|
|
sb.AppendLine("The selected file is not valid.");
|
|
}
|
|
|
|
return new StringTag(sb.ToString());
|
|
}
|
|
|
|
|
|
public override string ToString()
|
|
{
|
|
var result = $"[{_redrawType}] [B: {_brightness}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]";
|
|
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
|
|
return result;
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Enums
|
|
public enum RedrawTypes : byte
|
|
{
|
|
Supports,
|
|
Model,
|
|
}
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public OperationRedrawModel() { }
|
|
|
|
public OperationRedrawModel(FileFormat slicerFile) : base(slicerFile) { }
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
[XmlIgnore]
|
|
public string FilePath
|
|
{
|
|
get => _filePath;
|
|
set => RaiseAndSetIfChanged(ref _filePath, value);
|
|
}
|
|
|
|
public RedrawTypes RedrawType
|
|
{
|
|
get => _redrawType;
|
|
set => RaiseAndSetIfChanged(ref _redrawType, value);
|
|
}
|
|
|
|
public static Array RedrawTypesItems => Enum.GetValues(typeof(RedrawTypes));
|
|
|
|
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 bool ContactPointsOnly
|
|
{
|
|
get => _contactPointsOnly;
|
|
set => RaiseAndSetIfChanged(ref _contactPointsOnly, value);
|
|
}
|
|
|
|
public bool IgnoreContactLessPixels
|
|
{
|
|
get => _ignoreContactLessPixels;
|
|
set => RaiseAndSetIfChanged(ref _ignoreContactLessPixels, value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Equality
|
|
|
|
protected bool Equals(OperationRedrawModel other)
|
|
{
|
|
return _brightness == other._brightness && _contactPointsOnly == other._contactPointsOnly && _redrawType == other._redrawType && _ignoreContactLessPixels == other._ignoreContactLessPixels;
|
|
}
|
|
|
|
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((OperationRedrawModel) obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return HashCode.Combine(_brightness, _contactPointsOnly, (int) _redrawType, _ignoreContactLessPixels);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public FileFormat IsFileValid(bool returnNewInstance = false) =>
|
|
FileFormat.FindByExtension(_filePath, true, returnNewInstance);
|
|
|
|
protected override bool ExecuteInternally(OperationProgress progress)
|
|
{
|
|
var otherFile = IsFileValid(true);
|
|
otherFile.Decode(_filePath, progress);
|
|
|
|
progress.Reset(ProgressAction, otherFile.LayerCount);
|
|
|
|
int startLayerIndex = (int)(SlicerFile.LayerCount - otherFile.LayerCount);
|
|
if (startLayerIndex < 0) return false;
|
|
Parallel.For(0, otherFile.LayerCount, layerIndex =>
|
|
{
|
|
if (progress.Token.IsCancellationRequested) return;
|
|
var fullMatLayerIndex = startLayerIndex + layerIndex;
|
|
using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat;
|
|
using var original = fullMat.Clone();
|
|
using var bodyMat = otherFile[layerIndex].LayerMat;
|
|
using var fullMatRoi = GetRoiOrDefault(fullMat);
|
|
using var bodyMatRoi = GetRoiOrDefault(bodyMat);
|
|
using var patternMat = EmguExtensions.InitMat(fullMatRoi.Size, new MCvScalar(255 - _brightness));
|
|
using var supportsMat = new Mat();
|
|
|
|
bool modified = false;
|
|
if (_redrawType == RedrawTypes.Supports && _contactPointsOnly)
|
|
{
|
|
if (layerIndex + 1 >= otherFile.LayerCount) return;
|
|
CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
|
|
using var contours = new VectorOfVectorOfPoint();
|
|
using var hierarchyMat = new Mat();
|
|
CvInvoke.FindContours(supportsMat, contours, hierarchyMat, RetrType.List, ChainApproxMethod.ChainApproxSimple);
|
|
if (contours.Size <= 0) return;
|
|
using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
|
|
using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
|
|
var fullSpan = fullMatRoi.GetPixelSpan<byte>();
|
|
var supportsSpan = supportsMat.GetPixelSpan<byte>();
|
|
var nextSpan = nextLayerMatRoi.GetPixelSpan<byte>();
|
|
for (int i = 0; i < contours.Size; i++)
|
|
{
|
|
var foundContour = false;
|
|
var rectangle = CvInvoke.BoundingRectangle(contours[i]);
|
|
for (int y = rectangle.Y; y < rectangle.Bottom && !foundContour; y++)
|
|
for (int x = rectangle.X; x < rectangle.Right; x++)
|
|
{
|
|
var pos = supportsMat.GetPixelPos(x, y);
|
|
if (_ignoreContactLessPixels)
|
|
{
|
|
if (supportsSpan[pos] <= 10) continue;
|
|
if (nextSpan[pos] <= 0) continue;
|
|
modified = true;
|
|
fullSpan[pos] = _brightness;
|
|
}
|
|
else
|
|
{
|
|
if (supportsSpan[pos] <= 100) continue;
|
|
if (nextSpan[pos] <= 150) continue;
|
|
CvInvoke.DrawContours(fullMatRoi, contours, i, new MCvScalar(_brightness), -1, LineType.AntiAlias);
|
|
modified = true;
|
|
foundContour = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (_redrawType)
|
|
{
|
|
case RedrawTypes.Supports:
|
|
CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
|
|
break;
|
|
case RedrawTypes.Model:
|
|
CvInvoke.BitwiseAnd(fullMatRoi, bodyMatRoi, supportsMat); // Model
|
|
break;
|
|
}
|
|
|
|
CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat);
|
|
modified = true;
|
|
}
|
|
|
|
if (modified)
|
|
{
|
|
ApplyMask(original, fullMat);
|
|
SlicerFile[fullMatLayerIndex].LayerMat = fullMat;
|
|
}
|
|
|
|
lock (progress.Mutex)
|
|
{
|
|
progress++;
|
|
}
|
|
});
|
|
|
|
return !progress.Token.IsCancellationRequested;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|