- (Add) Tool - Light bleed compensation: Compensate the over-curing and light bleed from clear resins by dimming the sequential pixels
- (Add) Infill: Honeycomb infill type
- (Upgrade) MessageBox from 1.2.0 to 1.3.1 to fix the small size messages
This commit is contained in:
Tiago Conceição
2021-05-23 23:19:58 +01:00
parent ad92a0aa5c
commit ff4e0dfe28
26 changed files with 652 additions and 70 deletions
+7 -1
View File
@@ -1,6 +1,12 @@
# Changelog
## /05/2021 - v2.12.2
## 23/05/2021 - v2.13.0
- (Add) Tool - Light bleed compensation: Compensate the over-curing and light bleed from clear resins by dimming the sequential pixels
- (Add) Infill: Honeycomb infill type
- (Upgrade) MessageBox from 1.2.0 to 1.3.1 to fix the small size messages
## 20/05/2021 - v2.12.2
- (Add) Layer action - Export layers to heat map: Export a layer range to a grayscale heat map image that represents the median of the mass in the Z depth/perception. The pixel brightness/intensity shows where the most mass are concentrated.
+1 -1
View File
@@ -16,7 +16,7 @@ namespace UVtools.Core.Extensions
public static Point Rotate(this Point point, double angleDegree, Point pivot = default)
{
if (angleDegree == 0 || angleDegree == 360) return point;
if (angleDegree is 0 or 360) return point;
double angle = angleDegree * Math.PI / 180;
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
+8
View File
@@ -383,6 +383,14 @@ namespace UVtools.Core.Operations
ROI = roi;
}
public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
public Size GetRoiSizeOrDefault(Mat defaultMat) => GetRoiSizeOrDefault(defaultMat.Size);
public Size GetRoiSizeOrDefault(Size defaultSize)
{
return HaveROI && defaultSize != _roi.Size ? _roi.Size : defaultSize;
}
public Mat GetRoiOrDefault(Mat defaultMat)
{
return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat;
+4 -2
View File
@@ -21,15 +21,17 @@ namespace UVtools.Core.Operations
[Serializable]
public sealed class OperationBlur : Operation
{
#region Members
private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
private uint _size = 1;
#endregion
#region Overrides
public override string Title => "Blur";
public override string Description =>
$"Blur layer images by applying a low pass filter\n\n" +
"NOTE: Target printer must support AntiAliasing in order to use this function.\n\n" +
$"Blur layer images by applying a low pass filter.\n\n" +
"NOTE: Target printer must support AntiAliasing in order to use this function.\n" +
"See https://docs.opencv.org/master/d4/d13/tutorial_py_filtering.html";
public override string ConfirmationText =>
@@ -12,7 +12,6 @@ using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
@@ -58,7 +57,7 @@ namespace UVtools.Core.Operations
public override bool CanROI => false;
public override string Title => "Change print resolution";
public override string Description =>
"Crops or resizes all layer images to fit an alternate print resolution\n" +
"Crops or resizes all layer images to fit an alternate print resolution.\n" +
"Useful to make files printable on a different printer than they were originally sliced for without the need to re-slice.\n\n" +
"NOTE: Please ensure that the actual model will fit within the new print resolution. The operation will be aborted if it will result in any of the actual model being clipped.\n" +
"Only use this tool if both source and target printer have the same pixel pitch spec, otherwise the model size will be invalidated and result in a different size than the originally sliced for. " +
@@ -54,7 +54,7 @@ namespace UVtools.Core.Operations
public override string Title => "Dynamic lifts";
public override string Description =>
"Generate dynamic lift height and speeds for each layer given it mass\n" +
"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.";
+109 -49
View File
@@ -33,7 +33,7 @@ namespace UVtools.Core.Operations
public override string Title => "Infill";
public override string Description =>
$"Generate infill patterns in the model\n\nNOTES:\n1) You must exclude floor and ceil layers from the range.\n2) You must take care of drain holes after the operation.";
$"Generate infill patterns in the model.\n\nNOTES:\n1) You must exclude floor and ceil layers from the range.\n2) You must take care of drain holes after the operation.";
public override string ConfirmationText =>
$"infill model with {InfillType} from layers {LayerIndexStart} through {LayerIndexEnd}?";
@@ -53,6 +53,7 @@ namespace UVtools.Core.Operations
CubicCenterLink,
CubicDynamicLink,
CubicInterlinked,
Honeycomb
}
#endregion
@@ -128,17 +129,23 @@ namespace UVtools.Core.Operations
protected override bool ExecuteInternally(OperationProgress progress)
{
Mat mask = null;
if (_infillType == InfillAlgorithm.Honeycomb)
{
mask = GetHoneycombMask(GetRoiSizeOrDefault());
}
Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
using var mat = SlicerFile[layerIndex].LayerMat;
Execute(mat, layerIndex);
Execute(mat, layerIndex, mask);
SlicerFile[layerIndex].LayerMat = mat;
progress.LockAndIncrement();
});
mask?.Dispose();
return !progress.Token.IsCancellationRequested;
}
@@ -149,22 +156,22 @@ namespace UVtools.Core.Operations
var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
uint index = Convert.ToUInt32(arguments[0]);
uint layerIndex = index - LayerIndexStart;
var infillColor = new MCvScalar(InfillBrightness);
var infillColor = new MCvScalar(_infillBrightness);
Mat patternMask = null;
using Mat erode = new ();
using Mat diff = new ();
Mat target = GetRoiOrDefault(mat);
var target = GetRoiOrDefault(mat);
using var mask = GetMask(mat);
bool disposeTargetMask = true;
if (InfillType == InfillAlgorithm.Cubic ||
InfillType == InfillAlgorithm.CubicCenterLink ||
InfillType == InfillAlgorithm.CubicDynamicLink ||
InfillType == InfillAlgorithm.CubicInterlinked)
if (_infillType is InfillAlgorithm.Cubic
or InfillAlgorithm.CubicCenterLink
or InfillAlgorithm.CubicDynamicLink
or InfillAlgorithm.CubicInterlinked)
{
using var infillPattern = EmguExtensions.InitMat(new Size(InfillSpacing, InfillSpacing));
using Mat matPattern = mat.CloneBlank();
using var infillPattern = EmguExtensions.InitMat(new Size(_infillSpacing, _infillSpacing));
using var matPattern = mat.CloneBlank();
bool firstPattern = true;
uint accumulator = 0;
bool dynamicCenter = false;
@@ -172,16 +179,16 @@ namespace UVtools.Core.Operations
{
dynamicCenter = !dynamicCenter;
firstPattern = true;
accumulator += InfillSpacing;
accumulator += _infillSpacing;
if (accumulator >= layerIndex) break;
firstPattern = false;
accumulator += InfillThickness;
accumulator += _infillThickness;
}
if (firstPattern)
{
int thickness = InfillThickness / 2;
int thickness = _infillThickness / 2;
// Top Left
CvInvoke.Rectangle(infillPattern,
new Rectangle(0, 0, thickness, thickness),
@@ -207,106 +214,159 @@ namespace UVtools.Core.Operations
int margin = (int) (InfillSpacing - accumulator + layerIndex) - thickness;
int marginInv = (int) (accumulator - layerIndex) - thickness;
if (InfillType == InfillAlgorithm.CubicCenterLink ||
(InfillType == InfillAlgorithm.CubicDynamicLink &&
if (_infillType == InfillAlgorithm.CubicCenterLink ||
(_infillType == InfillAlgorithm.CubicDynamicLink &&
dynamicCenter) ||
InfillType == InfillAlgorithm.CubicInterlinked)
_infillType == InfillAlgorithm.CubicInterlinked)
{
CvInvoke.Rectangle(infillPattern,
new Rectangle(margin, margin, InfillThickness, InfillThickness),
new Rectangle(margin, margin, _infillThickness, _infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(marginInv, marginInv, InfillThickness,
InfillThickness),
new Rectangle(marginInv, marginInv, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(margin, marginInv, InfillThickness,
InfillThickness),
new Rectangle(margin, marginInv, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(marginInv, margin, InfillThickness,
InfillThickness),
new Rectangle(marginInv, margin, _infillThickness,
_infillThickness),
infillColor, -1);
}
if (InfillType == InfillAlgorithm.CubicInterlinked ||
(InfillType == InfillAlgorithm.CubicDynamicLink &&
if (_infillType == InfillAlgorithm.CubicInterlinked ||
(_infillType == InfillAlgorithm.CubicDynamicLink &&
!dynamicCenter))
{
CvInvoke.Rectangle(infillPattern,
new Rectangle(margin, -thickness, InfillThickness,
InfillThickness),
new Rectangle(margin, -thickness, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(marginInv, -thickness, InfillThickness,
InfillThickness),
new Rectangle(marginInv, -thickness, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(-thickness, margin, InfillThickness,
InfillThickness),
new Rectangle(-thickness, margin, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(-thickness, marginInv, InfillThickness,
InfillThickness),
new Rectangle(-thickness, marginInv, _infillThickness,
_infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(InfillSpacing - thickness, margin,
InfillThickness, InfillThickness),
_infillThickness, _infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(InfillSpacing - thickness, marginInv,
InfillThickness, InfillThickness),
_infillThickness, _infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(margin, InfillSpacing - thickness,
InfillThickness, InfillThickness),
_infillThickness, _infillThickness),
infillColor, -1);
CvInvoke.Rectangle(infillPattern,
new Rectangle(marginInv, InfillSpacing - thickness,
InfillThickness, InfillThickness),
_infillThickness, _infillThickness),
infillColor, -1);
}
}
else
{
CvInvoke.Rectangle(infillPattern,
new Rectangle(0, 0, InfillSpacing, InfillSpacing),
infillColor, InfillThickness);
new Rectangle(0, 0, _infillSpacing, _infillSpacing),
infillColor, _infillThickness);
}
CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
target.Cols / infillPattern.Cols + 1, matPattern);
patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
disposeTargetMask = true;
}
else if (_infillType == InfillAlgorithm.Honeycomb)
{
if (arguments.Length >= 2)
{
CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
target.Cols / infillPattern.Cols + 1, matPattern);
patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
patternMask = (Mat)arguments[1];
disposeTargetMask = false;
}
else
{
patternMask = GetHoneycombMask(target.Size);
disposeTargetMask = true;
}
}
//patternMask.Save("D:\\pattern.png");
CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101,
default);
CvInvoke.Subtract(target, erode, diff);
CvInvoke.BitwiseAnd(erode, patternMask, target, mask);
CvInvoke.Add(target, diff, target, mask);
patternMask?.Dispose();
if (disposeTargetMask)
{
patternMask.Dispose();
}
return true;
}
public Mat GetHoneycombMask(Size targetSize)
{
var patternMask = EmguExtensions.InitMat(targetSize);
var halfInfillSpacing = _infillSpacing / 2;
var halfThickenss = _infillThickness / 2;
int width = (int)Math.Round(4 * (_infillSpacing / 2.0 / Math.Sqrt(3)));
var infillColor = new MCvScalar(_infillBrightness);
for (int col = 0; col <= targetSize.Width / _infillSpacing; col++)
{
for (int row = 0; row <= targetSize.Height / _infillSpacing; row++)
{
// Move over for the column number.
int x = (int)Math.Round(col * (width * 0.75f));
// Move down the required number of rows.
int y = row * _infillSpacing;
// If the column is odd, move down half a hex more.
if (col % 2 == 1) y += halfInfillSpacing;
var points = new Point[]
{
new(x, y),
new((int) Math.Round(x + width * 0.25f), y - _infillSpacing / 2),
new((int) Math.Round(x + width * 0.75f), y - _infillSpacing / 2),
new(x + width, y),
new((int) Math.Round(x + width * 0.75f), y + _infillSpacing / 2),
new((int) Math.Round(x + width * 0.25f), y + _infillSpacing / 2),
};
CvInvoke.Polylines(patternMask, points, true, infillColor, _infillThickness);
}
}
return patternMask;
}
#endregion
}
}
@@ -65,7 +65,7 @@ namespace UVtools.Core.Operations
public override string Title => "Export layers to GIF";
public override string Description =>
"Export a layer range to an animated GIF file\n" +
"Export a layer range to an animated GIF file.\n" +
"Note: This process is slow, optimize the parameters to output few layers as possible and/or scale them down.";
public override string ConfirmationText =>
@@ -28,7 +28,7 @@ namespace UVtools.Core.Operations
#region Overrides
public override bool CanHaveProfiles => false;
public override string Title => "Export layers to heat map";
public override string Title => "Export layers to heat map.";
public override string Description =>
"Export a layer range to a grayscale heat map image that represents the median of the mass in the Z depth/perception\n" +
@@ -34,6 +34,7 @@ namespace UVtools.Core.Operations
Average
}
#endregion
#region Members
private OperationLayerReHeightItem _selectedItem;
private OperationLayerReHeightAntiAliasingType _antiAliasingType;
@@ -46,7 +47,7 @@ namespace UVtools.Core.Operations
public override bool CanROI => false;
public override string Title => "Adjust layer height";
public override string Description =>
"Adjust the layer height of the model\n\n" +
"Adjust the layer height of the model.\n\n" +
"Adjusting to values lower than current height will reduce layer lines, adjusting to values higher" +
" than current height will reduce model detail.\n\n" +
"Note: Using dedicated slicer software to re-slice will usually yeild better results.";
@@ -0,0 +1,277 @@
/*
* 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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationLightBleedCompensation : Operation
{
#region Enums
public enum LightBleedCompensationLookupMode : byte
{
[Description("Previous: Look for sequential pixels relative to the previous layers")]
Previous,
[Description("Next: Look for sequential pixels relative to the next layers")]
Next,
[Description("Both: Look for sequential pixels relative to the previous and next layers")]
Both
}
#endregion
#region Members
private LightBleedCompensationLookupMode _lookupMode = LightBleedCompensationLookupMode.Next;
private string _dimBy = "25,15,10,5";
#endregion
#region Overrides
public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
public override string Title => "Light bleed compensation";
public override string Description =>
"Compensate the over-curing and light bleed from clear resins by dimming the sequential pixels.\n" +
"Note: You need to find the optimal minimum pixel brightness that such resin can print in order to optimize this process.\n" +
"With more translucent resins you can go with lower brightness but stick to a limit that can form the layer without loss." +
" Tiny details can be lost when using low brightness level.\n" +
"After apply a light bleed compensation, do not apply or re-run this tool over.";
public override string ConfirmationText =>
$"compensate layers {LayerIndexStart} through {LayerIndexEnd}?";
public override string ProgressTitle =>
$"Compensate layers {LayerIndexStart} through {LayerIndexEnd}";
public override string ProgressAction => "Compensated layers";
public override string ValidateInternally()
{
StringBuilder sb = new();
if (DimByArray.Length == 0)
{
sb.AppendLine($"The dim levels are invalid or not set.");
}
if (MaximumSubtraction >= byte.MaxValue)
{
sb.AppendLine($"The sum of dim levels are producing black pixels.");
}
return sb.ToString();
}
public override string ToString()
{
var result = $"[Lookup: {_lookupMode}]" +
$" [Dim by: {_dimBy}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Constructor
public OperationLightBleedCompensation() { }
public OperationLightBleedCompensation(FileFormat slicerFile) : base(slicerFile) { }
#endregion
#region Properties
public LightBleedCompensationLookupMode LookupMode
{
get => _lookupMode;
set => RaiseAndSetIfChanged(ref _lookupMode, value);
}
public string DimBy
{
get => _dimBy;
set
{
if(!RaiseAndSetIfChanged(ref _dimBy, value)) return;
RaisePropertyChanged(nameof(MinimumBrightness));
RaisePropertyChanged(nameof(MaximumSubtraction));
}
}
public int MinimumBrightness => 255 - MaximumSubtraction;
public int MaximumSubtraction => DimByArray.Aggregate(0, (current, dim) => current + dim);
public byte[] DimByArray
{
get
{
List<byte> levels = new();
var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
foreach (var str in split)
{
if (!byte.TryParse(str, out var brightness)) continue;
if (brightness is byte.MinValue or byte.MaxValue) continue;
levels.Add(brightness);
}
return levels.ToArray();
}
}
public MCvScalar[] DimByMCvScalar
{
get
{
List<MCvScalar> levels = new();
var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
foreach (var str in split)
{
if (!byte.TryParse(str, out var brightness)) continue;
if (brightness is byte.MinValue or byte.MaxValue) continue;
levels.Add(new MCvScalar(brightness));
}
return levels.ToArray();
}
}
#endregion
#region Equality
protected bool Equals(OperationLightBleedCompensation other)
{
return _lookupMode == other._lookupMode && _dimBy == other._dimBy;
}
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((OperationLightBleedCompensation) obj);
}
public override int GetHashCode()
{
return HashCode.Combine((int) _lookupMode, _dimBy);
}
#endregion
#region Methods
/// <summary>
/// Get the cached dim mat's
/// </summary>
/// <returns></returns>
public Mat[] GetDimMats()
{
var dimLevels = DimByMCvScalar;
if (dimLevels.Length == 0) return Array.Empty<Mat>();
var mats = new Mat[dimLevels.Length];
var matSize = GetRoiSizeOrDefault();
for (var i = 0; i < mats.Length; i++)
{
mats[i] = EmguExtensions.InitMat(matSize, dimLevels[i]);
}
return mats;
}
protected override bool ExecuteInternally(OperationProgress progress)
{
var dimMats = GetDimMats();
if (dimMats.Length == 0) return false;
Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
var layer = SlicerFile[layerIndex];
using var mat = layer.LayerMat;
using var original = mat.Clone();
var target = GetRoiOrDefault(mat);
for (byte i = 0; i < dimMats.Length; i++)
{
Mat mask = null;
Mat previousMat = null;
Mat previousMatRoi = null;
Mat nextMat = null;
Mat nextMatRoi = null;
if (_lookupMode is LightBleedCompensationLookupMode.Previous or LightBleedCompensationLookupMode.Both)
{
int layerPreviousIndex = (int)layerIndex - i - 1;
if (layerPreviousIndex >= LayerIndexStart)
{
previousMat = SlicerFile[layerPreviousIndex].LayerMat;
mask = previousMatRoi = GetRoiOrDefault(previousMat);
}
}
if (_lookupMode is LightBleedCompensationLookupMode.Next or LightBleedCompensationLookupMode.Both)
{
uint layerIndexNext = (uint) (layerIndex + i + 1);
if (layerIndexNext <= LayerIndexEnd)
{
nextMat = SlicerFile[layerIndexNext].LayerMat;
mask = nextMatRoi = GetRoiOrDefault(nextMat);
}
}
if (previousMat is null && nextMat is null) break; // Nothing more to do
if (previousMat is not null && nextMat is not null) // both, need to merge previous with next layer
{
CvInvoke.Add(previousMatRoi, nextMatRoi, previousMatRoi);
mask = previousMatRoi;
}
CvInvoke.Subtract(target, dimMats[i], target, mask);
previousMat?.Dispose();
nextMat?.Dispose();
}
// Apply the results only to the selected masked area, if user selected one
ApplyMask(original, target);
// Set current layer image with the modified mat we just manipulated
layer.LayerMat = mat;
// Increment progress bar by 1
progress.LockAndIncrement();
});
foreach (var dimMat in dimMats)
{
dimMat.Dispose();
}
// return true if not cancelled by user
return !progress.Token.IsCancellationRequested;
}
#endregion
}
}
@@ -72,7 +72,7 @@ namespace UVtools.Core.Operations
public override string Title => "Pixel arithmetic";
public override string Description =>
"Perform arithmetic operations over the pixels";
"Perform arithmetic operations over the pixels.";
public override string ConfirmationText =>
$"arithmetic {_operator}" +
@@ -12,7 +12,6 @@ using System.Xml.Serialization;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
using UVtools.Core.Scripting;
namespace UVtools.Core.Operations
@@ -36,7 +35,7 @@ namespace UVtools.Core.Operations
public override string Title => "Scripting";
public override string Description =>
$"Run external scripts to manipulate the loaded file\n" +
$"Run external scripts to manipulate the loaded file.\n" +
$"The scripts have wide access to your system and able to do modifications, read/write files, etc. " +
$"Make sure to run only the scripts you trust! Or run UVtools in a sandbox while executing this.";
+1 -1
View File
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
<Version>2.12.2</Version>
<Version>2.13.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}"/usr/bin/:"${PATH}"
EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
exec "${EXEC}" $@
@@ -0,0 +1,12 @@
# Desktop Entry Specification: https://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
# https://github.com/AppImage/AppImageKit/wiki/Bundling-.NET-Core-apps
[Desktop Entry]
Type=Application
Name=UVtools
Comment=MSLA/DLP, file analysis, calibration, repair, conversion and manipulation
URL=https://github.com/sn4k3/UVtools
Icon=UVtools
Exec=UVtools
Path=~
Terminal=true
Categories=Development;
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

@@ -0,0 +1,125 @@
/*
* 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.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UVtools.Core.Scripting;
using Emgu.CV;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
namespace UVtools.ScriptSample
{
/// <summary>
/// Performs a black inset around objects
/// </summary>
public class ScriptLightBleedCompensationSample : ScriptGlobals
{
ScriptTextBoxInput BrightnessesInput = new()
{
Label = "Brightnesses",
ToolTip = "Brightness to reduce each subsequent repeated pixels",
Unit = "1-255",
Value = "25,20,15,10,5"
};
public byte[] Levels
{
get
{
List<byte> levels = new();
var split = BrightnessesInput.Value.Split(',', StringSplitOptions.TrimEntries);
foreach (var str in split)
{
if(!byte.TryParse(str, out var brightness)) continue;
if(brightness is byte.MinValue or byte.MaxValue) continue;
levels.Add(brightness);
}
return levels.ToArray();
}
}
/// <summary>
/// Set configurations here, this function trigger just after load a script
/// </summary>
public void ScriptInit()
{
Script.Name = "Light bleed compensation";
Script.Description = "Dim sequential pixels";
Script.Author = "Tiago Conceição";
Script.Version = new Version(0, 1);
Script.UserInputs.Add(BrightnessesInput);
}
/// <summary>
/// Validate user inputs here, this function trigger when user click on execute
/// </summary>
/// <returns>A error message, empty or null if validation passes.</returns>
public string ScriptValidate()
{
StringBuilder sb = new();
if (Levels.Length == 0)
{
sb.AppendLine($"No brightness levels are set");
}
return sb.ToString();
}
/// <summary>
/// Execute the script, this function trigger when when user click on execute and validation passes
/// </summary>
/// <returns>True if executes successfully to the end, otherwise false.</returns>
public bool ScriptExecute()
{
Progress.Reset("Bleed compensation", Operation.LayerRangeCount); // Sets the progress name and number of items to process
var brightnesses = Levels;
// Loop user selected layers in parallel, this will put each core of CPU working here on parallel
Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, layerIndex =>
{
if (Progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
using var mat = layer.LayerMat; // Gets this layer mat/image
var original = mat.Clone(); // Keep a original mat copy
var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
for (byte i = 0; i < brightnesses.Length; i++)
{
uint layerIndexNext = (uint) (layerIndex + i + 1);
if (layerIndexNext > Operation.LayerIndexEnd) break;
using var subtractMat = EmguExtensions.InitMat(target.Size, new MCvScalar(brightnesses[i]));
using var nextMat = SlicerFile[layerIndexNext].LayerMat;
var nextMatRoi = Operation.GetRoiOrDefault(nextMat);
CvInvoke.Subtract(target, subtractMat, target, nextMatRoi);
}
// Apply the results only to the selected masked area, if user selected one
Operation.ApplyMask(original, target);
// Set current layer image with the modified mat we just manipulated
layer.LayerMat = mat;
// Increment progress bar by 1
Progress.LockAndIncrement();
});
// return true if not cancelled by user
return !Progress.Token.IsCancellationRequested;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

@@ -0,0 +1,42 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLightBleedCompensationControl">
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,10,500">
<TextBlock Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
Text="Pixel lookup mode:"/>
<ComboBox Grid.Row="0" Grid.Column="2"
HorizontalAlignment="Stretch"
Items="{Binding Operation.LookupMode, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
SelectedItem="{Binding Operation.LookupMode, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
VerticalAlignment="Center"
ToolTip.Tip="List of brightnesses to subtract for each subsequent pixel, format: 1-254, 1-254, 1-254, ..."
Text="Dim subsequent pixels by:"/>
<TextBox Grid.Row="2" Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding Operation.DimBy}"/>
<TextBlock Grid.Row="4" Grid.Column="0"
VerticalAlignment="Center"
Text="Minimum pixel brightness:"/>
<TextBlock Grid.Row="4" Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding Operation.MinimumBrightness, StringFormat={}{0} of 255}"/>
<TextBlock Grid.Row="6" Grid.Column="0"
VerticalAlignment="Center"
Text="Maximum subtraction:"/>
<TextBlock Grid.Row="6" Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding Operation.MaximumSubtraction, StringFormat={}{0} of 255}"/>
</Grid>
</UserControl>
@@ -0,0 +1,20 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
namespace UVtools.WPF.Controls.Tools
{
public partial class ToolLightBleedCompensationControl : ToolControl
{
public OperationLightBleedCompensation Operation => BaseOperation as OperationLightBleedCompensation;
public ToolLightBleedCompensationControl()
{
InitializeComponent();
BaseOperation = new OperationLightBleedCompensation(SlicerFile);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
@@ -34,6 +34,8 @@ namespace UVtools.WPF.Extensions
WindowStartupLocation = location,
CanResize = false,
MaxWidth = window.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin,
MaxHeight = window.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin,
SizeToContent = SizeToContent.WidthAndHeight,
ShowInCenter = true
});
return await messageBoxStandardWindow.ShowDialog(window);
+8
View File
@@ -174,6 +174,14 @@ namespace UVtools.WPF
}
},
new()
{
Tag = new OperationLightBleedCompensation(),
Icon = new Avalonia.Controls.Image
{
Source = new Bitmap(App.GetAsset("/Assets/Icons/lightbulb-solid-16x16.png"))
}
},
new()
{
Tag = new OperationInfill(),
Icon = new Avalonia.Controls.Image
+2 -2
View File
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<Version>2.12.2</Version>
<Version>2.13.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -30,7 +30,7 @@
<PackageReference Include="Avalonia.Desktop" Version="0.10.5" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.5" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.1.4349" />
<PackageReference Include="MessageBox.Avalonia" Version="1.2.0" />
<PackageReference Include="MessageBox.Avalonia" Version="1.3.1" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.4" />
</ItemGroup>
<ItemGroup>
+21 -5
View File
@@ -33,8 +33,8 @@ Set-Location $PSScriptRoot\..
### Configuration ###
####################################
$enableMSI = $true
#$buildOnly = $null
#$buildOnly = "win-x64"
#$buildOnly = 'linux-x64'
#$buildOnly = 'win-x64'
# Profilling
$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
$deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
@@ -51,6 +51,11 @@ $objFolder = "$project\obj\$buildWith\$netFolder"
$publishFolder = "publish"
$platformsFolder = "UVtools.Platforms"
# Not supported yet! No fuse on WSL
$appImageFile = 'appimagetool-x86_64.AppImage'
$appImageFilePath = "$platformsFolder/AppImage/$appImageFile"
$appImageUrl = "https://github.com/AppImage/AppImageKit/releases/download/continuous/$appImageFile"
$macIcns = "UVtools.CAD/UVtools.icns"
#$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion
@@ -120,6 +125,11 @@ $runtimes =
}
}
# https://github.com/AppImage/AppImageKit/wiki/Bundling-.NET-Core-apps
#Invoke-WebRequest -Uri $appImageUrl -OutFile $appImageFilePath
#wsl chmod a+x $appImageFilePath
foreach ($obj in $runtimes.GetEnumerator()) {
if(![string]::IsNullOrWhiteSpace($buildOnly) -and !$buildOnly.Equals($obj.Name)) {continue}
# Configuration
@@ -154,9 +164,13 @@ Building: $runtime"
Copy-Item "$platformsFolder\$runtime\$includeObj" -Destination "$publishFolder\$runtime" -Recurse -ErrorAction Ignore
}
Write-Output "Compressing $runtime to: $targetZip"
Write-Output $targetZip "$publishFolder/$runtime"
#if($runtime.Equals('linux-x64')){
# $appDirDest = "$publishFolder/AppImage.$runtime/AppDir"
# Copy-Item "$platformsFolder/AppImage/AppDir" $appDirDest -Force -Recurse
# wsl chmod 755 "$appDirDest/AppRun"
# wsl cp -a "$publishFolder/$runtime/." "$appDirDest/usr/bin"
# wsl $appImageFilePath $appDirDest
#}
if($runtime.Equals('osx-x64')){
$macAppFolder = "${software}.app"
$macPublishFolder = "$publishFolder/${macAppFolder}"
@@ -179,6 +193,8 @@ Building: $runtime"
}
else {
Write-Output "Compressing $runtime to: $targetZip"
Write-Output $targetZip "$publishFolder/$runtime"
wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$targetZip" .
}