mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-11 19:12:31 +02:00
8e4995a6a5
- **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
313 lines
10 KiB
C#
313 lines
10 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 System;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using UVtools.Core.EmguCV;
|
|
using UVtools.Core.Extensions;
|
|
using UVtools.Core.FileFormats;
|
|
|
|
namespace UVtools.Core.Operations;
|
|
|
|
|
|
public sealed class OperationLayerExportImage : Operation
|
|
{
|
|
#region Enums
|
|
|
|
public enum LayerExportImageTypes : byte
|
|
{
|
|
[Description("PNG: Portable Network Graphics")]
|
|
PNG,
|
|
[Description("JPG: Joint Photographic Experts Group")]
|
|
JPG,
|
|
[Description("JPEG: Joint Photographic Experts Group")]
|
|
JPEG,
|
|
[Description("JP2: Joint Photographic Experts Group (JPEG 2000)")]
|
|
JP2,
|
|
[Description("TIF: Tag Image File Format")]
|
|
TIF,
|
|
[Description("TIFF: Tag Image File Format")]
|
|
TIFF,
|
|
[Description("BMP: Bitmap")]
|
|
BMP,
|
|
[Description("PBM: Portable Bitmap")]
|
|
PBM,
|
|
[Description("PGM: Portable Greymap")]
|
|
PGM,
|
|
//[Description("PPM: Portable Pixmap")]
|
|
//PPM,
|
|
[Description("SR: Sun raster")]
|
|
SR,
|
|
[Description("RAS: Sun raster")]
|
|
RAS,
|
|
[Description("SVG: Scalable Vector Graphics")]
|
|
SVG
|
|
}
|
|
#endregion
|
|
|
|
#region Members
|
|
|
|
private string _outputFolder = null!;
|
|
private string _filename = "layer";
|
|
private LayerExportImageTypes _imageType = LayerExportImageTypes.PNG;
|
|
private RotateDirection _rotateDirection = RotateDirection.None;
|
|
private FlipDirection _flipDirection = FlipDirection.None;
|
|
private bool _padLayerIndex = true;
|
|
private bool _cropByRoi = true;
|
|
|
|
#endregion
|
|
|
|
#region Overrides
|
|
|
|
public override bool CanHaveProfiles => false;
|
|
|
|
public override string IconClass => "fa-solid fa-file-image";
|
|
public override string Title => "Export layers to images";
|
|
|
|
public override string Description =>
|
|
"Export a layer range to images.";
|
|
|
|
public override string ConfirmationText =>
|
|
$"export {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}?";
|
|
|
|
public override string ProgressTitle =>
|
|
$"Exporting {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}";
|
|
|
|
public override string ProgressAction => $"Exported {_imageType} images";
|
|
|
|
/*public override string ValidateInternally()
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
if (LayerRangeCount < 2)
|
|
{
|
|
sb.AppendLine("To generate a heat map at least two layers are required.");
|
|
}
|
|
|
|
return sb.ToString();
|
|
}*/
|
|
|
|
public override string ToString()
|
|
{
|
|
var result = $"[Crop by ROI: {_cropByRoi}] [Pad index: {_padLayerIndex}]" +
|
|
LayerRangeString;
|
|
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string OutputFolder
|
|
{
|
|
get => _outputFolder;
|
|
set => RaiseAndSetIfChanged(ref _outputFolder, value);
|
|
}
|
|
|
|
public string Filename
|
|
{
|
|
get => _filename;
|
|
set => RaiseAndSetIfChanged(ref _filename, value);
|
|
}
|
|
|
|
public LayerExportImageTypes ImageType
|
|
{
|
|
get => _imageType;
|
|
set => RaiseAndSetIfChanged(ref _imageType, value);
|
|
}
|
|
|
|
public RotateDirection RotateDirection
|
|
{
|
|
get => _rotateDirection;
|
|
set => RaiseAndSetIfChanged(ref _rotateDirection, value);
|
|
}
|
|
|
|
public FlipDirection FlipDirection
|
|
{
|
|
get => _flipDirection;
|
|
set => RaiseAndSetIfChanged(ref _flipDirection, value);
|
|
}
|
|
|
|
public bool PadLayerIndex
|
|
{
|
|
get => _padLayerIndex;
|
|
set => RaiseAndSetIfChanged(ref _padLayerIndex, value);
|
|
}
|
|
|
|
public bool CropByROI
|
|
{
|
|
get => _cropByRoi;
|
|
set => RaiseAndSetIfChanged(ref _cropByRoi, value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public OperationLayerExportImage()
|
|
{ }
|
|
|
|
public OperationLayerExportImage(FileFormat slicerFile) : base(slicerFile)
|
|
{
|
|
_flipDirection = SlicerFile.DisplayMirror;
|
|
}
|
|
|
|
public override void InitWithSlicerFile()
|
|
{
|
|
_outputFolder = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath) ?? string.Empty);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
protected override bool ExecuteInternally(OperationProgress progress)
|
|
{
|
|
if (!Directory.Exists(_outputFolder))
|
|
{
|
|
Directory.CreateDirectory(_outputFolder);
|
|
}
|
|
|
|
var slicedFileNameNoExt = SlicerFile.FilenameNoExt;
|
|
|
|
Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.GetParallelOptions(progress), layerIndex =>
|
|
{
|
|
using var mat = SlicerFile[layerIndex].LayerMat;
|
|
var matRoi = mat;
|
|
if (_cropByRoi && HaveROI)
|
|
{
|
|
matRoi = GetRoiOrDefault(mat);
|
|
}
|
|
|
|
if (_flipDirection != FlipDirection.None)
|
|
{
|
|
CvInvoke.Flip(matRoi, matRoi, (FlipType)_flipDirection);
|
|
}
|
|
|
|
if (_rotateDirection != RotateDirection.None)
|
|
{
|
|
CvInvoke.Rotate(matRoi, matRoi, (RotateFlags)_rotateDirection);
|
|
}
|
|
|
|
var filename = SlicerFile[layerIndex].FormatFileName(_filename, _padLayerIndex ? SlicerFile.LayerDigits : byte.MinValue, IndexStartNumber.Zero, string.Empty);
|
|
var fileFullPath = Path.Combine(_outputFolder, $"{filename}.{_imageType.ToString().ToLower()}");
|
|
|
|
if (_imageType != LayerExportImageTypes.SVG)
|
|
{
|
|
matRoi.Save(fileFullPath);
|
|
}
|
|
else
|
|
{
|
|
// SVG
|
|
|
|
var paths = matRoi.GetSvgPath(ChainApproxMethod.ChainApproxTc89Kcos);
|
|
|
|
using TextWriter tw = new StreamWriter(fileFullPath);
|
|
tw.WriteLine("<!--");
|
|
tw.WriteLine($"# Generated by {About.Software} v{About.VersionStr} {About.SystemBits} @ {DateTime.UtcNow} #");
|
|
tw.WriteLine($"File: {SlicerFile.Filename}");
|
|
tw.WriteLine($"{SlicerFile[layerIndex].ToString().Replace(", ", "\n")}");
|
|
tw.WriteLine("-->");
|
|
tw.WriteLine("<svg " +
|
|
"xmlns=\"http://www.w3.org/2000/svg\" " +
|
|
//"xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
|
|
//"version=\"1.1\" " +
|
|
$"id=\"{slicedFileNameNoExt}_{filename}\" " +
|
|
$"data-name=\"{slicedFileNameNoExt}_{filename}\" " +
|
|
//"x=\"0\" " +
|
|
//"y=\"0\" " +
|
|
$"width=\"{matRoi.Width}\" " +
|
|
$"height=\"{matRoi.Height}\" " +
|
|
$"viewBox=\"0 0 {matRoi.Width} {matRoi.Height}\">");
|
|
tw.WriteLine("\t<defs>");
|
|
tw.WriteLine("\t\t<style>");
|
|
//tw.WriteLine("\t\tsvg { background-color: #000000; }");
|
|
tw.WriteLine("\t\t.background { fill: #000000; }");
|
|
//tw.WriteLine("\t\t.black { fill: #000000; fill-rule: evenodd; }");
|
|
//tw.WriteLine("\t\t.white { fill: #FFFFFF; fill-rule: evenodd; }");
|
|
tw.WriteLine("\t\tpath { fill: #FFFFFF; fill-rule: evenodd; }");
|
|
tw.WriteLine("\t\t</style>");
|
|
tw.WriteLine("\t</defs>");
|
|
tw.WriteLine($"\t<title>{slicedFileNameNoExt} #{layerIndex}</title>");
|
|
|
|
tw.WriteLine($"\t<g id=\"layer{layerIndex}\">");
|
|
tw.WriteLine($"\t<rect class=\"background\" width=\"{mat.Width}\" height=\"{mat.Height}\"/>");
|
|
|
|
foreach (var path in paths)
|
|
{
|
|
tw.WriteLine($"\t<path d=\"{path}\"/>");
|
|
}
|
|
|
|
/*bool firstTime = true;
|
|
for (int i = 0; i < contours.Size; i++)
|
|
{
|
|
if (hierarchy[i, EmguContour.HierarchyParent] == -1) // Top hierarchy
|
|
{
|
|
if (firstTime)
|
|
{
|
|
firstTime = false;
|
|
}
|
|
else
|
|
{
|
|
tw.WriteLine("\"/>");
|
|
}
|
|
|
|
tw.Write("\t<path d=\"");
|
|
}
|
|
else
|
|
{
|
|
tw.Write(" ");
|
|
}
|
|
|
|
tw.Write($"M {contours[i][0].X} {contours[i][0].Y} L");
|
|
for (int x = 1; x < contours[i].Size; x++)
|
|
{
|
|
tw.Write($" {contours[i][x].X} {contours[i][x].Y}");
|
|
}
|
|
tw.Write(" Z");
|
|
}
|
|
|
|
if(!firstTime) tw.WriteLine("\"/>");*/
|
|
|
|
|
|
tw.WriteLine("\t</g>");
|
|
tw.WriteLine("</svg>");
|
|
}
|
|
|
|
progress.LockAndIncrement();
|
|
});
|
|
|
|
return !progress.Token.IsCancellationRequested;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Equality
|
|
|
|
private bool Equals(OperationLayerExportImage other)
|
|
{
|
|
return _outputFolder == other._outputFolder && _filename == other._filename && _imageType == other._imageType && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _padLayerIndex == other._padLayerIndex && _cropByRoi == other._cropByRoi;
|
|
}
|
|
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return ReferenceEquals(this, obj) || obj is OperationLayerExportImage other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return HashCode.Combine(_outputFolder, _filename, (int) _imageType, (int) _rotateDirection, (int) _flipDirection, _padLayerIndex, _cropByRoi);
|
|
}
|
|
|
|
#endregion
|
|
} |