diff options
32 files changed, 702 insertions, 193 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9c89d..4bf3cbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 29/05/2021 - v2.13.1 + +- (Add) Layer preview - Outline: Skeletonize +- (Add) Actions - Export layers to skeleton: Export a layer range to a skeletonized image that is the sum of each layer skeleton +- (Add) Pixel editor - Text: Allow to rotate text placement by any angle (#206) +- (Add) Calibrate - XYZ Accuracy: Drain hole diameter (#205) + ## 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 diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 3a55047..4240372 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -8,23 +8,20 @@ using System; using System.Drawing; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; using Emgu.CV.Util; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace UVtools.Core.Extensions { public static class EmguExtensions { - public static readonly MCvScalar WhiteByte = new(255); - public static readonly MCvScalar White3Byte = new(255, 255, 255); - public static readonly MCvScalar BlackByte = new(0); - public static readonly MCvScalar Black3Byte = new(0, 0, 0); - public static readonly MCvScalar Transparent4Byte = new(0, 0, 0, 0); - public static readonly MCvScalar Black4Byte = new(0, 0, 0, 255); + public static readonly MCvScalar WhiteColor = new(255, 255, 255, 255); + public static readonly MCvScalar BlackColor = new(0, 0, 0, 255); + //public static readonly MCvScalar TransparentColor = new(); public static unsafe byte* GetBytePointer(this Mat mat) { @@ -72,7 +69,7 @@ namespace UVtools.Core.Extensions public static unsafe Span<T> GetPixelColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0) { var colMat = mat.Col(x); - return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length); + return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length); } /// <summary> @@ -95,25 +92,89 @@ namespace UVtools.Core.Extensions public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear) { //var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels); - using (var translateTransform = new Matrix<double>(2, 3) + using var translateTransform = new Matrix<double>(2, 3) { [0, 0] = xScale, // xScale [1, 1] = yScale, // yScale [0, 2] = xTrans, //x translation + compensation of x scaling [1, 2] = yTrans // y translation + compensation of y scaling - }) + }; + CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation); + } + + /// <summary> + /// Rotates a Mat by an angle while keeping the image size + /// </summary> + /// <param name="src"></param> + /// <param name="angle"></param> + /// <param name="scale"></param> + public static void Rotate(this Mat src, double angle, Size newSize = default, double scale = 1.0) => Rotate(src, src, angle, newSize, scale); + + /// <summary> + /// Rotates a Mat by an angle while keeping the image size + /// </summary> + /// <param name="src"></param> + /// <param name="dst"></param> + /// <param name="angle"></param> + /// <param name="scale"></param> + public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = default, double scale = 1.0) + { + if (angle % 360 == 0 && scale == 1.0) return; + if (newSize.IsEmpty) + { + newSize = src.Size; + } + + var halfWidth = src.Width / 2.0f; + var halfHeight = src.Height / 2.0f; + using var translateTransform = new Matrix<double>(2, 3); + CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform); + + if (src.Size != newSize) { - CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation); + // adjust the rotation matrix to take into account translation + translateTransform[0, 2] += newSize.Width / 2.0 - halfWidth; + translateTransform[1, 2] += newSize.Height / 2.0 - halfHeight; } + + CvInvoke.WarpAffine(src, dst, translateTransform, newSize); } - public static void Rotate(this Mat src, double angle) + /// <summary> + /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content + /// </summary> + /// <param name="src"></param> + /// <param name="angle"></param> + /// <param name="scale"></param> + public static void RotateAdjustBounds(this Mat src, double angle, double scale = 1.0) => RotateAdjustBounds(src, src, angle, scale); + + /// <summary> + /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content + /// </summary> + /// <param name="src"></param> + /// <param name="dst"></param> + /// <param name="angle"></param> + /// <param name="scale"></param> + public static void RotateAdjustBounds(this Mat src, Mat dst, double angle, double scale = 1.0) { + if (angle % 360 == 0 && scale == 1.0) return; var halfWidth = src.Width / 2.0f; var halfHeight = src.Height / 2.0f; using var translateTransform = new Matrix<double>(2, 3); - CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, 1.0, translateTransform); - CvInvoke.WarpAffine(src, src, translateTransform, src.Size); + CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform); + var cos = Math.Abs(translateTransform[0, 0]); + var sin = Math.Abs(translateTransform[0, 1]); + + // compute the new bounding dimensions of the image + int newWidth = (int) (src.Height * sin + src.Width * cos); + int newHeight = (int) (src.Height * cos + src.Width * sin); + + // adjust the rotation matrix to take into account translation + translateTransform[0, 2] += newWidth / 2.0 - halfWidth; + translateTransform[1, 2] += newHeight / 2.0 - halfHeight; + + + CvInvoke.WarpAffine(src, dst, translateTransform, new Size(newWidth, newHeight)); } /// <summary> @@ -134,6 +195,85 @@ namespace UVtools.Core.Extensions yTrans + (src.Height - src.Height * yScale) / 2.0, dstSize, interpolation); } + public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale, MCvScalar color, + int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, double angle = 0) + { + if (angle % 360 == 0) // No rotation needed, cheaper cycle + { + CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin); + return; + } + + using var rotatedSrc = src.Clone(); + rotatedSrc.RotateAdjustBounds(-angle); + var sizeDifference = (rotatedSrc.Size - src.Size).Half(); + org.Offset(sizeDifference.ToPoint()); + org = org.Rotate(-angle, new Point(rotatedSrc.Size.Width / 2, rotatedSrc.Size.Height / 2)); + CvInvoke.PutText(rotatedSrc, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin); + + using var mask = rotatedSrc.CloneBlank(); + CvInvoke.PutText(mask, text, org, fontFace, fontScale, WhiteColor, thickness, lineType, bottomLeftOrigin); + + rotatedSrc.Rotate(angle, src.Size); + mask.Rotate(angle, src.Size); + + rotatedSrc.CopyTo(src, mask); + } + + /// <summary> + /// Determine the area (i.e. total number of pixels in the image), + /// initialize the output skeletonized image, and construct the + /// morphological structuring element + /// </summary> + /// <param name="src"></param> + /// <param name="iterations">Number of iterations required to perform the skeletoize</param> + /// <param name="ksize"></param> + /// <param name="elementShape"></param> + public static Mat Skeletonize(this Mat src, out int iterations, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle) + { + if (ksize.IsEmpty) ksize = new Size(3, 3); + Point anchor = new(-1, -1); + var skeleton = src.CloneBlank(); + using var kernel = CvInvoke.GetStructuringElement(elementShape, ksize, anchor); + + var image = src; + using var temp = new Mat(); + iterations = 0; + while (true) + { + iterations++; + + // erode and dilate the image using the structuring element + using var eroded = new Mat(); + CvInvoke.Erode(image, eroded, kernel, anchor, 1, BorderType.Reflect101, default); + CvInvoke.Dilate(eroded, temp, kernel, anchor, 1, BorderType.Reflect101, default); + + // subtract the temporary image from the original, eroded + // image, then take the bitwise 'or' between the skeleton + // and the temporary image + CvInvoke.Subtract(image, temp, temp); + CvInvoke.BitwiseOr(skeleton, temp, skeleton); + image = eroded.Clone(); + + // if there are no more 'white' pixels in the image, then + // break from the loop + if (CvInvoke.CountNonZero(image) == 0) break; + } + + return skeleton; + } + + /// <summary> + /// Determine the area (i.e. total number of pixels in the image), + /// initialize the output skeletonized image, and construct the + /// morphological structuring element + /// </summary> + /// <param name="src"></param> + /// <param name="ksize"></param> + /// <param name="elementShape"></param> + public static Mat Skeletonize(this Mat src, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle) + => src.Skeletonize(out _, ksize, elementShape); + /// <summary> /// Copy a <see cref="Mat"/> to center of other <see cref="Mat"/> /// </summary> @@ -226,6 +366,13 @@ namespace UVtools.Core.Extensions return new(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels); } + public static Mat New(this Mat src, MCvScalar color) + { + Mat mat = new(src.Rows, src.Cols, src.Depth, src.NumberOfChannels); + mat.SetTo(color); + return mat; + } + /// <summary> /// Clone this <see cref="Mat"/> blanked (All zeros) /// </summary> diff --git a/UVtools.Core/Extensions/PointExtensions.cs b/UVtools.Core/Extensions/PointExtensions.cs index 8b0056c..8d9820f 100644 --- a/UVtools.Core/Extensions/PointExtensions.cs +++ b/UVtools.Core/Extensions/PointExtensions.cs @@ -16,7 +16,7 @@ namespace UVtools.Core.Extensions public static Point Rotate(this Point point, double angleDegree, Point pivot = default) { - if (angleDegree is 0 or 360) return point; + if (angleDegree % 360 == 0) return point; double angle = angleDegree * Math.PI / 180; double cos = Math.Cos(angle); double sin = Math.Sin(angle); @@ -25,9 +25,30 @@ namespace UVtools.Core.Extensions double x = cos * dx - sin * dy + pivot.X; double y = sin * dx + cos * dy + pivot.Y; - Point rotated = new((int)Math.Round(x), (int)Math.Round(y)); - return rotated; + return new((int)Math.Round(x), (int)Math.Round(y)); } + public static PointF Rotate(this PointF point, double angleDegree, PointF pivot = default) + { + if (angleDegree % 360 == 0) return point; + double angle = angleDegree * Math.PI / 180; + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + double dx = point.X - pivot.X; + double dy = point.Y - pivot.Y; + double x = cos * dx - sin * dy + pivot.X; + double y = sin * dx + cos * dy + pivot.Y; + + return new((float) x, (float) y); + } + + public static Point Half(this Point point)=> new(point.X / 2, point.Y / 2); + + public static PointF Half(this PointF point) => new(point.X / 2, point.Y / 2); + + public static Size ToSize(this Point point) => new(point.X, point.Y); + + public static SizeF ToSize(this PointF point) => new(point.X, point.Y); + } } diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index e16893c..8bbc547 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -43,6 +43,8 @@ namespace UVtools.Core.Extensions SizeSuffixes[mag]); } + public static Size Inflate(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height); + public static Size Inflate(this Size size) => size.Inflate(size); public static Size Inflate(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels); public static Size Inflate(this Size size, int width, int height) => new (size.Width + width, size.Height + height); @@ -51,10 +53,7 @@ namespace UVtools.Core.Extensions /// </summary> /// <param name="size"></param> /// <returns></returns> - public static bool HaveZero(this Size size) - { - return size.Width <= 0 && size.Height <= 0; - } + public static bool HaveZero(this Size size) => size.Width <= 0 && size.Height <= 0; /// <summary> /// Exchange width with height @@ -63,45 +62,50 @@ namespace UVtools.Core.Extensions /// <returns></returns> public static Size Invert(this Size size) => new(size.Height, size.Width); - public static int Area(this Rectangle rect) - { - return rect.Width * rect.Height; - } + public static int Area(this Rectangle rect) => rect.Width * rect.Height; - public static int Area(this Size size) - { - return size.Width * size.Height; - } + public static int Area(this Size size) => size.Width * size.Height; - public static int Max(this Size size) - { - return Math.Max(size.Width, size.Height); - } + public static int Max(this Size size) => Math.Max(size.Width, size.Height); - public static float Area(this RectangleF rect, int round = -1) - { - return round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height; - } + public static float Area(this RectangleF rect, int round = -1) => round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height; /// <summary> /// Gets if this size have a zero value on width or height /// </summary> /// <param name="size"></param> /// <returns></returns> - public static bool HaveZero(this SizeF size) - { - return size.Width <= 0 && size.Height <= 0; - } + public static bool HaveZero(this SizeF size) => size.Width <= 0 && size.Height <= 0; - public static float Area(this SizeF size, int round = -1) - { - return round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height; - } + public static float Area(this SizeF size, int round = -1) => round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height; + + public static float Max(this SizeF size) => Math.Max(size.Width, size.Height); + + public static Size Half(this Size size) => new(size.Width / 2, size.Height / 2); + + public static SizeF Half(this SizeF size) => new(size.Width / 2, size.Height / 2); + + public static Point ToPoint(this Size size) => new(size.Width, size.Height); + public static PointF ToPoint(this SizeF size) => new(size.Width, size.Height); - public static float Max(this SizeF size) + public static Size Rotate(this Size size, double angleDegree) { - return Math.Max(size.Width, size.Height); + if (angleDegree % 360 == 0) return size; + var sizeHalf = size.Half(); + double angle = angleDegree * Math.PI / 180; + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + // var newImgWidth = + (float)(x0 + Math.Abs((x - x0) * Math.Cos(rad)) + Math.Abs((y - y0) * Math.Sin(rad))); + //var newImgHeight = -(float)(y0 + Math.Abs((x - x0) * Math.Sin(rad)) + Math.Abs((y - y0) * Math.Cos(rad))); + int dx = size.Width - sizeHalf.Width; + int dy = size.Height - sizeHalf.Height; + //double width = sizeHalf.Width + Math.Abs(dx * cos) + Math.Abs(dy * sin); + //double height = sizeHalf.Height + Math.Abs(dx * sin) + Math.Abs(dy * cos); + double width = Math.Abs(cos * dx) - Math.Abs(sin * dy) + sizeHalf.Width; + double height = Math.Abs(sin * dx) + Math.Abs(cos * dy) + sizeHalf.Height; + + return new((int)Math.Round(width), (int)Math.Round(height)); } } diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index 4374b38..7a227b1 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -489,7 +489,7 @@ namespace UVtools.Core.FileFormats using var mat = EmguExtensions.InitMat(Resolution); foreach (var line in layerDefs[layerIndex].Lines) { - CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteByte); + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteColor); } this[layerIndex] = new Layer((uint) layerIndex, mat, this); diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 7530199..73af073 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -454,7 +454,7 @@ namespace UVtools.Core.FileFormats using var mat = EmguExtensions.InitMat(Resolution); foreach (var line in layerDefs[layerIndex].Lines) { - CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteByte); + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteColor); } this[layerIndex] = new Layer((uint) layerIndex, mat, this); diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index e7b9c7d..773e728 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -1432,7 +1432,7 @@ namespace UVtools.Core using (var vec = new VectorOfVectorOfPoint(new VectorOfPoint(checkArea.Contour))) { - CvInvoke.DrawContours(emguImage, vec, -1, EmguExtensions.WhiteByte, -1); + CvInvoke.DrawContours(emguImage, vec, -1, EmguExtensions.WhiteColor, -1); } using (var intersectingAreasMat = image.CloneBlank()) @@ -1638,7 +1638,7 @@ namespace UVtools.Core var operationText = (PixelText)operation; var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat); - CvInvoke.PutText(mat, operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror); + mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.Angle); } else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser) { diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index acf78b2..d4abaf1 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -415,7 +415,7 @@ namespace UVtools.Core.Operations var mask = EmguExtensions.InitMat(mat.Size); using VectorOfVectorOfPoint vec = new(points); - CvInvoke.DrawContours(mask, vec, -1, EmguExtensions.WhiteByte, -1); + CvInvoke.DrawContours(mask, vec, -1, EmguExtensions.WhiteColor, -1); return GetRoiOrDefault(mask); } diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index eaa3c5f..8903780 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -489,11 +489,11 @@ namespace UVtools.Core.Operations maxY += ellipseHeight; using Mat shape = EmguExtensions.InitMat(new Size(maxX + startX, maxY + startY)); - CvInvoke.FillPoly(shape, new VectorOfPoint(pointList.ToArray()), EmguExtensions.WhiteByte, lineType); - CvInvoke.Circle(shape, new Point(0, 0), length / 4, EmguExtensions.BlackByte, -1, lineType); + CvInvoke.FillPoly(shape, new VectorOfPoint(pointList.ToArray()), EmguExtensions.WhiteColor, lineType); + CvInvoke.Circle(shape, new Point(0, 0), length / 4, EmguExtensions.BlackColor, -1, lineType); CvInvoke.Ellipse(shape, new Point(maxX / 2, maxY - ellipseHeight), new Size(maxX / 3, ellipseHeight), 0, 0, 360, - EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Circle(shape, new Point(length / 2, (int) (maxY - 100 * _partScale)), length / 5, EmguExtensions.BlackByte, -1, lineType); + EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Circle(shape, new Point(length / 2, (int) (maxY - 100 * _partScale)), length / 5, EmguExtensions.BlackColor, -1, lineType); int currentX = 0; int currentY = 0; @@ -510,7 +510,7 @@ namespace UVtools.Core.Operations void addText(Mat mat, ushort number, params string[] text) { - var color = _extrudeText ? EmguExtensions.WhiteByte : EmguExtensions.BlackByte; + var color = _extrudeText ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor; CvInvoke.PutText(mat, number.ToString(), new Point((int) (100 * _partScale), (int) (55 * _partScale)), font, 1.5 * (double) _partScale, color, (int) (4 * _partScale), lineType); CvInvoke.PutText(mat, "UVtools EP", new Point(fontStartX, fontStartY), font, 0.8 * (double) _partScale, color, (int) (2 * _partScale), lineType); CvInvoke.PutText(mat, $"{Microns}um", new Point(fontStartX, fontStartY + fontMargin), font, fontScale, color, fontThickness, lineType); @@ -679,8 +679,8 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "Elephant Foot Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); /*thumbnail.SetTo(EmguExtensions.Black3Byte); diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 3f1d0f2..8430fe5 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1236,13 +1236,13 @@ namespace UVtools.Core.Operations var layers = new Mat[2]; layers[0] = EmguExtensions.InitMat(rect.Size); - CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); layers[1] = layers[0].CloneBlank(); if (holes.Length > 0) { CvInvoke.Rectangle(layers[1], new Rectangle(rect.Size.Width - holePanelWidth, 0, rect.Size.Width, layers[0].Height), - EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } @@ -1254,7 +1254,7 @@ namespace UVtools.Core.Operations { CvInvoke.Rectangle(layers[1], new Rectangle(0, 0, layers[1].Size.Width-holePanelWidth, _staircaseThickness), - EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } // Print holes @@ -1297,13 +1297,13 @@ namespace UVtools.Core.Operations case CalibrateExposureFinderShapes.Square: CvInvoke.Rectangle(layers[layerIndex], new Rectangle(new Point(xPos, yPos), new Size(diameter-1, diameter-1)), - EmguExtensions.WhiteByte, -1, + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); break; case CalibrateExposureFinderShapes.Circle: CvInvoke.Circle(layers[layerIndex], new Point(xPos, yPos), - radius, EmguExtensions.WhiteByte, -1, + radius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); break; } @@ -1334,13 +1334,13 @@ namespace UVtools.Core.Operations case CalibrateExposureFinderShapes.Square: CvInvoke.Rectangle(layers[layerIndex], new Rectangle(new Point(xPos, yPos), new Size(diameter-1, diameter-1)), - EmguExtensions.BlackByte, -1, + EmguExtensions.BlackColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); break; case CalibrateExposureFinderShapes.Circle: CvInvoke.Circle(layers[layerIndex], new Point(xPos, yPos), - radius, EmguExtensions.BlackByte, -1, + radius, EmguExtensions.BlackColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); break; } @@ -1374,17 +1374,17 @@ namespace UVtools.Core.Operations { // Print positive bottom CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1), - EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); // Print positive top yPos += barSpacingPx; CvInvoke.Rectangle(layers[1], new Rectangle(xPos + barLengthPx + _barVerticalSplitter, yPos, barLengthPx - 1, bars[i] - 1), - EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); yPos += bars[i]; } // Left over CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1), - EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); yPos += barSpacingPx; @@ -1395,7 +1395,7 @@ namespace UVtools.Core.Operations yStartPos - 1, barsPanelWidth - _barFenceThickness + 1, yPos - yStartPos + _barFenceThickness / 2 + _barFenceOffset + 1), - EmguExtensions.WhiteByte, _barFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + EmguExtensions.WhiteColor, _barFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); yPos += _barFenceThickness * 2 + _barFenceOffset * 2; } @@ -1406,7 +1406,7 @@ namespace UVtools.Core.Operations if (!textSize.IsEmpty) { CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90CounterClockwise); - CvInvoke.PutText(layers[1], _text, new Point(_staircaseThickness + featuresMarginX, layers[1].Height - barsPanelWidth - featuresMarginX * (barsPanelWidth > 0 ? 2 : 1)), _textFont, _textScale, EmguExtensions.WhiteByte, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], _text, new Point(_staircaseThickness + featuresMarginX, layers[1].Height - barsPanelWidth - featuresMarginX * (barsPanelWidth > 0 ? 2 : 1)), _textFont, _textScale, EmguExtensions.WhiteColor, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90Clockwise); } @@ -1416,7 +1416,7 @@ namespace UVtools.Core.Operations yPos = bullseyeYPos; foreach (var circle in bulleyes) { - CvInvoke.Circle(layers[1], new Point(bullseyeXPos, yPos), circle.Radius, EmguExtensions.WhiteByte, circle.Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Circle(layers[1], new Point(bullseyeXPos, yPos), circle.Radius, EmguExtensions.WhiteColor, circle.Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } if (_bullsEyeInvertQuadrants) @@ -1442,7 +1442,7 @@ namespace UVtools.Core.Operations new Size( bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness, bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness)), - EmguExtensions.WhiteByte, + EmguExtensions.WhiteColor, _bullsEyeFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } @@ -1454,14 +1454,14 @@ namespace UVtools.Core.Operations if (isPreview) { var textHeightStart = layers[1].Height - featuresMarginY - TextMarkingSpacing; - CvInvoke.PutText(layers[1], $"{Microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{Microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); if (holes.Length > 0) { - CvInvoke.PutText(layers[1], $"{Microns}u", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{Microns}u", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } } @@ -1508,7 +1508,7 @@ namespace UVtools.Core.Operations foreach (var triangle in triangles) { using var vec = new VectorOfPoint(triangle); - CvInvoke.FillPoly(layers[1], vec, EmguExtensions.WhiteByte, + CvInvoke.FillPoly(layers[1], vec, EmguExtensions.WhiteColor, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } @@ -1528,7 +1528,7 @@ namespace UVtools.Core.Operations CvInvoke.Rectangle(layers[1], new Rectangle( new Point(triangles[0][0].X - 0, triangles[0][0].Y - 0), new Size(triangleWidth * 2 + 0, triangleHeight + 0) - ), EmguExtensions.WhiteByte, outlineThickness, + ), EmguExtensions.WhiteColor, outlineThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } } @@ -1587,14 +1587,14 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "Exposure Time Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); if (_patternModel) { - CvInvoke.PutText(thumbnail, $"Patterned Model", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"Patterned Model", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); } else { - CvInvoke.PutText(thumbnail, $"Features: {(_staircaseThickness > 0 ? 1 : 0) + Holes.Length + Bars.Length + BullsEyes.Length + (_counterTrianglesEnabled ? 1 : 0)}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"Features: {(_staircaseThickness > 0 ? 1 : 0) + Holes.Length + Bars.Length + BullsEyes.Length + (_counterTrianglesEnabled ? 1 : 0)}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); } @@ -1679,7 +1679,7 @@ namespace UVtools.Core.Operations { if (_patternModelGlueBottomLayers) { - newMatRoi.SetTo(EmguExtensions.WhiteByte); + newMatRoi.SetTo(EmguExtensions.WhiteColor); } } @@ -1697,11 +1697,11 @@ namespace UVtools.Core.Operations if (_multipleBrightness) { - CvInvoke.PutText(newMatRoi, brightness.ToString(), new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 4), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(newMatRoi, brightness.ToString(), new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 4), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } - CvInvoke.PutText(newMatRoi, $"{microns}u", new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(newMatRoi, $"{group.Key.BottomExposure}s", new(xHalf - 60, yHalf + 20), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(newMatRoi, $"{group.Key.Exposure}s", new(xHalf - 60, yHalf + 20 + TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackByte, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(newMatRoi, $"{microns}u", new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(newMatRoi, $"{group.Key.BottomExposure}s", new(xHalf - 60, yHalf + 20), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(newMatRoi, $"{group.Key.Exposure}s", new(xHalf - 60, yHalf + 20 + TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } if (brightness < 255) @@ -1863,7 +1863,7 @@ namespace UVtools.Core.Operations { CvInvoke.Rectangle(matRoi, new Rectangle(staircaseWidth - staircaseWidthForLayer, 0, staircaseWidthForLayer, _staircaseThickness), - EmguExtensions.WhiteByte, -1, + EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } } @@ -1890,19 +1890,19 @@ namespace UVtools.Core.Operations } var textHeightStart = matRoi.Height - featuresMarginY - TextMarkingSpacing; - CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); if (holes.Length > 0) { - CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); - CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } if (_multipleBrightness) { - CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteByte, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); if (brightness < 255 && (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None || _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !isBottomLayer || diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs index db8dce4..dd9e6b5 100644 --- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs +++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs @@ -362,7 +362,7 @@ namespace UVtools.Core.Operations int innerRadius = Math.Max(100, radius - _innerMargin); double topLineLength = 0; - CvInvoke.Circle(layers[0], center, radius, EmguExtensions.WhiteByte, -1, LineType.AntiAlias); + CvInvoke.Circle(layers[0], center, radius, EmguExtensions.WhiteColor, -1, LineType.AntiAlias); layers[1] = layers[0].Clone(); layers[2] = layers[0].Clone(); @@ -414,7 +414,7 @@ namespace UVtools.Core.Operations { text = $"{Math.Round(brightness * _normalExposure / byte.MaxValue, 2)}s"; } - CvInvoke.PutText(layers[2], text, fontPoint, fontFace, fontScale, EmguExtensions.BlackByte, fontThickness, lineType); + CvInvoke.PutText(layers[2], text, fontPoint, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType); rotatedAngle += AngleStep; layers[2].Rotate(AngleStep); } @@ -426,12 +426,12 @@ namespace UVtools.Core.Operations var holeRadius = Math.Min(radius, _centerHoleDiameter) / 2; if (_innerMargin > 0) { - CvInvoke.Circle(layers[2], center, holeRadius + _innerMargin, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Circle(layers[2], center, holeRadius + _innerMargin, EmguExtensions.WhiteColor, -1, lineType); } foreach (var layer in layers) { - CvInvoke.Circle(layer, center, holeRadius, EmguExtensions.BlackByte, -1, lineType); + CvInvoke.Circle(layer, center, holeRadius, EmguExtensions.BlackColor, -1, lineType); } } @@ -439,11 +439,11 @@ namespace UVtools.Core.Operations fontThickness = 3; CvInvoke.PutText(layers[0], $"{Microns}um at {_bottomExposure}s/{_normalExposure}s", new Point(center.X - radius / 2, center.Y + radius / 2 +40), - fontFace, fontScale, EmguExtensions.BlackByte, fontThickness, lineType, true); + fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true); CvInvoke.PutText(layers[0], $"{_startBrightness}-{_endBrightness} S:{_brightnessSteps}", new Point(center.X - radius / 2, center.Y + radius / 2 - 40), - fontFace, fontScale, EmguExtensions.BlackByte, fontThickness, lineType, true); + fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true); if (_mirrorOutput) { @@ -466,8 +466,8 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "Grayscale Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"Divs:{Divisions} Angle:{AngleStep}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"Divs:{Divisions} Angle:{AngleStep}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); return thumbnail; } diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index 314b753..f9f5fa2 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -341,7 +341,7 @@ namespace UVtools.Core.Operations Parallel.For(0, baseLayers, layerIndex => { int chamferOffset = (int) Math.Max(0, _chamferLayers - layerIndex); - CvInvoke.Circle(layers[layerIndex], center, (int) baseRadius - chamferOffset, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Circle(layers[layerIndex], center, (int) baseRadius - chamferOffset, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); }); @@ -363,11 +363,11 @@ namespace UVtools.Core.Operations for (uint spiralLayerIndex = (uint) layerIndex; spiralLayerIndex < maxLayer; spiralLayerIndex++) { - CvInvoke.Circle(layers[spiralLayerIndex], locationCW, (int)spiralRadius, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Circle(layers[spiralLayerIndex], locationCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); if (_spiralDirection == SpiralDirections.Both) { spiralAngle = -spiralAngle; - CvInvoke.Circle(layers[spiralLayerIndex], locationCCW, (int)spiralRadius, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Circle(layers[spiralLayerIndex], locationCCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); } } } @@ -378,7 +378,7 @@ namespace UVtools.Core.Operations Parallel.For(0, ceilLayers, i => { uint layerIndex = (uint)(currrentlayer + i); - CvInvoke.Circle(layers[layerIndex], center, (int)baseRadius, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + CvInvoke.Circle(layers[layerIndex], center, (int)baseRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); }); @@ -404,8 +404,8 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "Stress Tower", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"{_spirals} Spirals @ {_spiralAngleStepPerLayer}deg", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"{_spirals} Spirals @ {_spiralAngleStepPerLayer}deg", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); return thumbnail; } diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index 6681186..87f7f03 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -523,16 +523,16 @@ namespace UVtools.Core.Operations case Shapes.Circle: currentX += (int) FemaleDiameterXPixels / 2; currentY += (int) FemaleDiameterXPixels / 2; - CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleDiameterXPixels / 2), EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackByte, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType); currentX += (int) FemaleDiameterXPixels / 2 + PartMargin; break; case Shapes.Square: int offsetX = (int) ((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2); int offsetY = (int) ((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2); - CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int) FemaleDiameterXPixels, (int) FemaleDiameterXPixels), EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int) FemaleHoleDiameterXPixels, (int) FemaleHoleDiameterYPixels), EmguExtensions.BlackByte, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int) FemaleDiameterXPixels, (int) FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int) FemaleHoleDiameterXPixels, (int) FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType); currentX += (int)FemaleDiameterXPixels + PartMargin; currentY = startY + (int) FemaleDiameterYPixels / 2; break; @@ -567,18 +567,18 @@ namespace UVtools.Core.Operations switch (Shape) { case Shapes.Circle: - CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleDiameterXPixels / 2), EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackByte, -1, lineType); - CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), xPixels / 2, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), xPixels / 2, EmguExtensions.WhiteColor, -1, lineType); break; case Shapes.Square: int offsetX = (int)((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2); int offsetY = (int)((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2); - CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int)FemaleDiameterXPixels, (int)FemaleDiameterXPixels), EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int)FemaleHoleDiameterXPixels, (int)FemaleHoleDiameterYPixels), EmguExtensions.BlackByte, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int)FemaleDiameterXPixels, (int)FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int)FemaleHoleDiameterXPixels, (int)FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType); offsetX = (int)((FemaleDiameterXPixels - xPixels) / 2); offsetY = (int)((FemaleDiameterYPixels - yPixels) / 2); - CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, xPixels, yPixels), EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType); break; } @@ -604,10 +604,10 @@ namespace UVtools.Core.Operations switch (Shape) { case Shapes.Circle: - CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), halfDiameterX, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), halfDiameterX, EmguExtensions.WhiteColor, -1, lineType); break; case Shapes.Square: - CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, xPixels, yPixels), EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType); break; } @@ -680,7 +680,7 @@ namespace UVtools.Core.Operations { foreach (var keyValuePair in pointTextList) { - CvInvoke.PutText(layers[layerIndex], keyValuePair.Value, keyValuePair.Key, fontFace, fontScale, EmguExtensions.BlackByte, fontThickness, lineType); + CvInvoke.PutText(layers[layerIndex], keyValuePair.Value, keyValuePair.Key, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType); } }); @@ -705,8 +705,8 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "Tolerance Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"Objects: {OutputObjects}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"Objects: {OutputObjects}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); /*thumbnail.SetTo(EmguExtensions.Black3Byte); diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs index 8784f5b..00d78e3 100644 --- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs +++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs @@ -49,6 +49,7 @@ namespace UVtools.Core.Operations private bool _outputBLObject; private bool _outputBCObject; private bool _outputBRObject; + private decimal _drainHoleArea = 3; #endregion @@ -252,6 +253,12 @@ namespace UVtools.Core.Operations public uint LayerCount => (uint) Math.Floor(ZSize / LayerHeight); + public decimal DrainHoleArea + { + get => _drainHoleArea; + set => RaiseAndSetIfChanged(ref _drainHoleArea, value); + } + public bool CenterHoleRelief { get => _centerHoleRelief; @@ -602,7 +609,7 @@ namespace UVtools.Core.Operations public Mat[] GetLayers() { - var layers = new Mat[2]; + var layers = new Mat[3]; for (byte i = 0; i < layers.Length; i++) { layers[i] = EmguExtensions.InitMat(SlicerFile.Resolution); @@ -620,6 +627,9 @@ namespace UVtools.Core.Operations const double fontScale = 1.3; const byte fontThickness = 3; + var xPixels = XPixels; + var yPixels = YPixels; + for (int y = 0; y < 3; y++) { switch (y) @@ -629,11 +639,11 @@ namespace UVtools.Core.Operations positionYStr = "T"; break; case 1: - currentY = (int)(SlicerFile.Resolution.Height / 2 - YPixels / 2); + currentY = (int)(SlicerFile.Resolution.Height / 2 - yPixels / 2); positionYStr = "M"; break; case 2: - currentY = (int)(SlicerFile.Resolution.Height - YPixels - _topBottomMargin); + currentY = (int)(SlicerFile.Resolution.Height - yPixels - _topBottomMargin); positionYStr = "B"; break; } @@ -646,11 +656,11 @@ namespace UVtools.Core.Operations positionStr = $"{positionYStr}L"; break; case 1: - currentX = (int)(SlicerFile.Resolution.Width / 2 - XPixels / 2); + currentX = (int)(SlicerFile.Resolution.Width / 2 - xPixels / 2); positionStr = $"{positionYStr}C"; break; case 2: - currentX = (int)(SlicerFile.Resolution.Width - XPixels - _leftRightMargin); + currentX = (int)(SlicerFile.Resolution.Width - xPixels - _leftRightMargin); positionStr = $"{positionYStr}R"; break; } @@ -669,31 +679,39 @@ namespace UVtools.Core.Operations if(y == 2 && x == 2 && !_outputBRObject) continue; var layer = layers[i]; CvInvoke.Rectangle(layer, - new Rectangle(currentX, currentY, (int) XPixels, (int) YPixels), - EmguExtensions.WhiteByte, -1); + new Rectangle(currentX, currentY, (int)xPixels, (int) yPixels), + EmguExtensions.WhiteColor, -1); CvInvoke.PutText(layer, positionStr, new Point(currentX + fontStartX, currentY + fontStartY), fontFace, fontScale, - EmguExtensions.BlackByte, fontThickness); + EmguExtensions.BlackColor, fontThickness); CvInvoke.PutText(layer, $"{XSize},{YSize},{ZSize}", - new Point(currentX + fontStartX, (int) (currentY + YPixels - fontStartY + 25)), fontFace, fontScale, - EmguExtensions.BlackByte, fontThickness); + new Point(currentX + fontStartX, (int) (currentY + yPixels - fontStartY + 25)), fontFace, fontScale, + EmguExtensions.BlackColor, fontThickness); if (CenterHoleRelief) { CvInvoke.Circle(layer, - new Point((int) (currentX + XPixels / 2), (int) (currentY + YPixels / 2)), - (int) (Math.Min(XPixels, YPixels) / 4), - EmguExtensions.Black3Byte, -1); + new Point((int) (currentX + xPixels / 2), (int) (currentY + yPixels / 2)), + (int) (Math.Min(xPixels, yPixels) / 4), + EmguExtensions.BlackColor, -1); } - if (_hollowModel && i != 0 && _wallThickness > 0) + if (_hollowModel && i > 0 && _wallThickness > 0) { - Size rectSize = new((int) (XPixels - WallThicknessXPixels * 2), (int) (YPixels - WallThicknessYPixels * 2)); + Size rectSize = new((int) (xPixels - WallThicknessXPixels * 2), (int) (yPixels - WallThicknessYPixels * 2)); Point rectLocation = new((int) (currentX + WallThicknessXPixels), (int) (currentY + WallThicknessYPixels)); CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize), - EmguExtensions.Black3Byte, -1); + EmguExtensions.BlackColor, -1); + } + + if (i == 2 && _drainHoleArea > 0) + { + Size rectSize = new((int)xPixels, (int)(Yppmm * _drainHoleArea)); + Point rectLocation = new(currentX, (int)(currentY + xPixels / 2 - rectSize.Height / 2)); + CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize), + EmguExtensions.BlackColor, -1); } } } @@ -720,8 +738,8 @@ namespace UVtools.Core.Operations CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "XYZ Accuracy Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"{XSize} x {YSize} x {ZSize} mm", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"{XSize} x {YSize} x {ZSize} mm", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); /*thumbnail.SetTo(EmguExtensions.Black3Byte); @@ -752,10 +770,18 @@ namespace UVtools.Core.Operations { IsModified = true }; + var ventLayer = new Layer(0, layers[2], SlicerFile.LayerManager) + { + IsModified = true + }; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - newLayers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone()); + newLayers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), + (_hollowModel || _centerHoleRelief) && _drainHoleArea > 0 && layerIndex <= _bottomLayers + (int)Math.Floor(_drainHoleArea / _layerHeight) + ? ventLayer.Clone() : layer.Clone()); + progress++; } diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs index 2c0bccb..3817514 100644 --- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs +++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs @@ -28,10 +28,10 @@ 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" + + "Export a layer range to a grayscale heat map image that represents the median of the mass in the Z depth/perception.\n" + "The pixel brightness/intensity shows where the most mass are concentrated."; public override string ConfirmationText => diff --git a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs new file mode 100644 index 0000000..0fd56d0 --- /dev/null +++ b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs @@ -0,0 +1,139 @@ +/* + * 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.Threading.Tasks; +using Emgu.CV; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationLayerExportSkeleton : Operation + { + #region Members + private string _filePath; + private bool _cropByRoi = true; + + #endregion + + #region Overrides + + public override bool CanHaveProfiles => false; + public override string Title => "Export layers to skeleton"; + + public override string Description => + "Export a layer range to a skeletonized image that is the sum of each layer skeleton."; + + public override string ConfirmationText => + $"skeletonize from layers {LayerIndexStart} through {LayerIndexEnd}?"; + + public override string ProgressTitle => + $"Skeletonizing from layers {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => "Skeletonized layers"; + + public override string ToString() + { + var result = $"[Crop by ROI: {_cropByRoi}]" + + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + #endregion + + #region Properties + + public string FilePath + { + get => _filePath; + set => RaiseAndSetIfChanged(ref _filePath, value); + } + + public bool CropByROI + { + get => _cropByRoi; + set => RaiseAndSetIfChanged(ref _cropByRoi, value); + } + + #endregion + + #region Constructor + + public OperationLayerExportSkeleton() + { } + + public OperationLayerExportSkeleton(FileFormat slicerFile) : base(slicerFile) + { + _filePath = SlicerFile.FileFullPath + ".skeleton.png"; + } + + #endregion + + #region Methods + + protected override bool ExecuteInternally(OperationProgress progress) + { + using var skeletonSum = EmguExtensions.InitMat(SlicerFile.Resolution); + var skeletonSumRoi = GetRoiOrDefault(skeletonSum); + using var mask = GetMask(skeletonSum); + + + Parallel.For(LayerIndexStart, LayerIndexEnd+1, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + + using var mat = SlicerFile[layerIndex].LayerMat; + var matRoi = GetRoiOrDefault(mat); + using var skeletonRoi = matRoi.Skeletonize(); + lock (progress.Mutex) + { + CvInvoke.Add(skeletonSumRoi, skeletonRoi, skeletonSumRoi, mask); + progress++; + } + }); + + if (!progress.Token.IsCancellationRequested) + { + if (_cropByRoi && HaveROI) + { + skeletonSumRoi.Save(_filePath); + } + else + { + skeletonSum.Save(_filePath); + } + } + + return !progress.Token.IsCancellationRequested; + } + + #endregion + + #region Equality + + private bool Equals(OperationLayerExportSkeleton other) + { + return _filePath == other._filePath && _cropByRoi == other._cropByRoi; + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationLayerExportSkeleton other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_filePath, _cropByRoi); + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index 427d7be..ff1ca0e 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -295,13 +295,13 @@ namespace UVtools.Core.Operations case PixelArithmeticOperators.KeepRegion: { using var targetClone = target.Clone(); - original.SetTo(EmguExtensions.BlackByte); - mat.SetTo(EmguExtensions.BlackByte); + original.SetTo(EmguExtensions.BlackColor); + mat.SetTo(EmguExtensions.BlackColor); targetClone.CopyTo(target); break; } case PixelArithmeticOperators.DiscardRegion: - target.SetTo(EmguExtensions.BlackByte); + target.SetTo(EmguExtensions.BlackColor); break; default: throw new NotImplementedException(); diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index 2ba34f0..3e9c29d 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -255,7 +255,7 @@ namespace UVtools.Core.Operations CvInvoke.DrawContours(image, vec, -1, - EmguExtensions.WhiteByte, + EmguExtensions.WhiteColor, -1); } } diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index ea5e389..0990182 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -127,7 +127,7 @@ namespace UVtools.Core.Operations } - CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteByte, -1); + CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteColor, -1); } ApplyMask(original, target); diff --git a/UVtools.Core/PixelEditor/PixelText.cs b/UVtools.Core/PixelEditor/PixelText.cs index beab7e6..5a420a9 100644 --- a/UVtools.Core/PixelEditor/PixelText.cs +++ b/UVtools.Core/PixelEditor/PixelText.cs @@ -19,6 +19,7 @@ namespace UVtools.Core.PixelEditor private ushort _thickness = 1; private string _text; private bool _mirror; + private double _angle; private byte _removePixelBrightness; public override PixelOperationType OperationType => PixelOperationType.Text; @@ -54,6 +55,12 @@ namespace UVtools.Core.PixelEditor set => RaiseAndSetIfChanged(ref _mirror, value); } + public double Angle + { + get => _angle; + set => RaiseAndSetIfChanged(ref _angle, value); + } + public byte RemovePixelBrightness { get => _removePixelBrightness; @@ -74,13 +81,14 @@ namespace UVtools.Core.PixelEditor public PixelText(){} - public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness) + public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, double angle, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness) { _font = font; _fontScale = fontScale; _thickness = thickness; _text = text; _mirror = mirror; + _angle = angle; IsAdd = isAdd; _removePixelBrightness = removePixelBrightness; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 4a97b3d..d0f18c5 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -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.13.0</Version> + <Version>2.13.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs index 70e283c..5e1dbde 100644 --- a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs +++ b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs @@ -89,27 +89,27 @@ namespace UVtools.ScriptSample // Do the left eye x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2; y = faceSpacing; - CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Circle(mats[1], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Circle(mats[1], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType); Progress++; // Do the right eye, the mirror of left... x = (int)(SlicerFile.ResolutionX - x); - CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Circle(mats[3], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Circle(mats[3], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType); Progress++; // Do the noose x = xCenter - noseThickness / 2; - CvInvoke.Rectangle(mats[0], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Rectangle(mats[2], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Rectangle(mats[0], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Rectangle(mats[2], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType); Progress++; // Do the mouth x = xCenter; y += noseHeight + faceSpacing; - CvInvoke.Ellipse(mats[0], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteByte, -1, lineType); - CvInvoke.Ellipse(mats[4], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteByte, -1, lineType); + CvInvoke.Ellipse(mats[0], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType); + CvInvoke.Ellipse(mats[4], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType); SlicerFile.LayerManager.AllocateAndSetFromMat(mats); // Replace layers and rebuild properties diff --git a/UVtools.ScriptSample/ScriptVATClean.cs b/UVtools.ScriptSample/ScriptVATClean.cs index aefee4c..bd769b5 100644 --- a/UVtools.ScriptSample/ScriptVATClean.cs +++ b/UVtools.ScriptSample/ScriptVATClean.cs @@ -91,7 +91,7 @@ namespace UVtools.ScriptSample CvInvoke.Rectangle(mat, new Rectangle( new Point(InputInset.Value, InputInset.Value), new Size((int) (SlicerFile.ResolutionX - InputInset.Value*2)-1, (int) (SlicerFile.ResolutionY - InputInset.Value*2)-1) - ), EmguExtensions.WhiteByte, -1, LineType.FourConnected); + ), EmguExtensions.WhiteColor, -1, LineType.FourConnected); layer.LayerMat = mat; SlicerFile.SuppressRebuildPropertiesWork(() => @@ -132,8 +132,8 @@ namespace UVtools.ScriptSample CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); CvInvoke.PutText(thumbnail, "VAT Clean Utility", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); - CvInvoke.PutText(thumbnail, $"Exposure time: {SlicerFile.ExposureTime}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); - CvInvoke.PutText(thumbnail, $"Use the spatula in!", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"Exposure time: {SlicerFile.ExposureTime}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + CvInvoke.PutText(thumbnail, $"Use the spatula in!", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); return thumbnail; } diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml index 97d2126..d95b166 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml +++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml @@ -200,16 +200,45 @@ VerticalAlignment="Center" Text="px"/> - <CheckBox Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" - Content="Hollow model" - IsChecked="{Binding Operation.HollowModel}"/> + <TextBlock Grid.Row="12" Grid.Column="0" + VerticalAlignment="Center" + Text="Drain hole diameter:"> + <TextBlock.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.Or}"> + <Binding Path="Operation.HollowModel"/> + <Binding Path="Operation.CenterHoleRelief"/> + </MultiBinding> + </TextBlock.IsEnabled> + </TextBlock> + <NumericUpDown Grid.Row="12" Grid.Column="2" + Increment="0.5" + Minimum="0" + Maximum="100" + FormatString="F2" + Value="{Binding Operation.DrainHoleArea}"> + <NumericUpDown.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.Or}"> + <Binding Path="Operation.HollowModel"/> + <Binding Path="Operation.CenterHoleRelief"/> + </MultiBinding> + </NumericUpDown.IsEnabled> + </NumericUpDown> + <TextBlock Grid.Row="12" Grid.Column="4" + VerticalAlignment="Center" + Text="mm³"> + <TextBlock.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.Or}"> + <Binding Path="Operation.HollowModel"/> + <Binding Path="Operation.CenterHoleRelief"/> + </MultiBinding> + </TextBlock.IsEnabled> + </TextBlock> <TextBlock Grid.Row="12" Grid.Column="6" Grid.ColumnSpan="3" Text="Wall thickness:" VerticalAlignment="Center" IsEnabled="{Binding Operation.HollowModel}"/> <NumericUpDown Grid.Row="12" Grid.Column="8" - Increment="0.5" Minimum="0" Maximum="100" @@ -221,16 +250,21 @@ IsEnabled="{Binding Operation.HollowModel}" Text="mm"/> + <CheckBox Grid.Row="14" Grid.Column="0" + Grid.ColumnSpan="2" + ToolTip.Tip="Most of the printers requires a mirror output to print with the correct orientation" + IsChecked="{Binding Operation.MirrorOutput}" + Content="Mirror output" /> + <CheckBox Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="4" VerticalAlignment="Center" IsChecked="{Binding Operation.CenterHoleRelief}" - Content="Relief with a center hole"/> + Content="Relief base with a center hole"/> - <CheckBox Grid.Row="14" Grid.Column="8" - Grid.ColumnSpan="3" - ToolTip.Tip="Most of the printers requires a mirror output to print with the correct orientation" - IsChecked="{Binding Operation.MirrorOutput}" - Content="Mirror output" /> + + <CheckBox Grid.Row="14" Grid.Column="8" Grid.ColumnSpan="3" + Content="Hollow model" + IsChecked="{Binding Operation.HollowModel}"/> <TextBlock Grid.Row="16" Grid.Column="0" VerticalAlignment="Center" diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs index 04f256e..c3ded15 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs @@ -62,7 +62,7 @@ namespace UVtools.WPF.Controls.Calibrators { _timer.Stop(); _timer.Start(); - if (e.PropertyName == nameof(Operation.ScaleXFactor) || e.PropertyName == nameof(Operation.ScaleYFactor)) + if (e.PropertyName is nameof(Operation.ScaleXFactor) or nameof(Operation.ScaleYFactor)) { RaisePropertyChanged(nameof(IsProfileAddEnabled)); return; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml new file mode 100644 index 0000000..df056b9 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml @@ -0,0 +1,30 @@ +<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.ToolLayerExportSkeletonControl"> + <StackPanel Spacing="10"> + + <StackPanel Orientation="Horizontal" Spacing="5"> + <TextBox + Watermark="Output filepath" + UseFloatingWatermark="True" + VerticalAlignment="Center" + IsReadOnly="True" + Width="500" + Text="{Binding Operation.FilePath}"/> + <Button + VerticalAlignment="Stretch" + Command="{Binding ChooseFilePath}"> + <Image Source="/Assets/Icons/open-16x16.png"/> + </Button> + </StackPanel> + + <CheckBox + Content="Crop image by selected ROI" + IsVisible="{Binding ParentWindow.IsROIVisible}" + IsChecked="{Binding Operation.CropByROI}"/> + + </StackPanel> +</UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs new file mode 100644 index 0000000..02091f4 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs @@ -0,0 +1,36 @@ +using System.IO; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public partial class ToolLayerExportSkeletonControl : ToolControl + { + public OperationLayerExportSkeleton Operation => BaseOperation as OperationLayerExportSkeleton; + public ToolLayerExportSkeletonControl() + { + InitializeComponent(); + BaseOperation = new OperationLayerExportSkeleton(SlicerFile); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public async void ChooseFilePath() + { + var dialog = new SaveFileDialog + { + Filters = Helpers.ImagesFullFileFilter, + InitialFileName = Path.GetFileName(SlicerFile.FileFullPath) + ".skeleton.png", + Directory = Path.GetDirectoryName(SlicerFile.FileFullPath), + }; + var file = await dialog.ShowAsync(ParentWindow); + if (string.IsNullOrWhiteSpace(file)) return; + Operation.FilePath = file; + } + } +} diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs index 49ed2d8..f781800 100644 --- a/UVtools.WPF/MainWindow.LayerPreview.cs +++ b/UVtools.WPF/MainWindow.LayerPreview.cs @@ -74,6 +74,8 @@ namespace UVtools.WPF private bool _showLayerOutlineLayerBoundary; private bool _showLayerOutlineHollowAreas; private bool _showLayerOutlineEdgeDetection; + private bool _showLayerOutlineSkeletonize; + private bool _isTooltipOverlayVisible; private string _tooltipOverlayText; @@ -379,6 +381,16 @@ namespace UVtools.WPF } } + public bool ShowLayerOutlineSkeletonize + { + get => _showLayerOutlineSkeletonize; + set + { + if (!RaiseAndSetIfChanged(ref _showLayerOutlineSkeletonize, value)) return; + ShowLayer(); + } + } + public bool IsPixelEditorActive { get => _isPixelEditorActive; @@ -693,7 +705,12 @@ namespace UVtools.WPF { using var canny = new Mat(); CvInvoke.Canny(LayerCache.Image, canny, 80, 40, 3, true); - CvInvoke.CvtColor(canny, LayerCache.ImageBgr, ColorConversion.Gray2Bgra); + CvInvoke.CvtColor(canny, LayerCache.ImageBgr, ColorConversion.Gray2Bgr); + } + else if (_showLayerOutlineSkeletonize) + { + using var skeletonize = LayerCache.Image.Skeletonize(); + CvInvoke.CvtColor(skeletonize, LayerCache.ImageBgr, ColorConversion.Gray2Bgr); } else if (_showLayerImageDifference) { @@ -1000,9 +1017,14 @@ namespace UVtools.WPF ? Settings.PixelEditor.RemovePixelHighlightColor : Settings.PixelEditor.RemovePixelColor); - CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, operationText.Location, + + + /*CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R), - operationText.Thickness, operationText.LineType, operationText.Mirror); + operationText.Thickness, operationText.LineType, operationText.Mirror);*/ + LayerCache.ImageBgr.PutTextRotated(operationText.Text, operationText.Location, + operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R), + operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.Angle); } else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser) { @@ -1876,12 +1898,17 @@ namespace UVtools.WPF int baseLine = 0; var size = CvInvoke.GetTextSize(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine); - cursor = EmguExtensions.InitMat(new Size(size.Width * 2, size.Height * 2), 4); + //var rotatedSize = size.Rotate(DrawingPixelText.Angle); + //Point point = (rotatedSize.Inflate(rotatedSize)).Rotate(DrawingPixelText.Angle, rotatedSize.ToPoint()); + cursor = EmguExtensions.InitMat(size.Inflate(), 4); //CvInvoke.Rectangle(cursor, new Rectangle(Point.Empty, size), _pixelEditorCursorColor, -1, DrawingPixelText.LineType); //_pixelEditorCursorColor.V3 = 255; //CvInvoke.Rectangle(cursor, new Rectangle(new Point(size.Width, 0), size), _pixelEditorCursorColor, 1, DrawingPixelText.LineType); - CvInvoke.PutText(cursor, text, new Point(size.Width, size.Height), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror); + CvInvoke.PutText(cursor, text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror); + cursor.RotateAdjustBounds(DrawingPixelText.Angle); + //cursor.Rotate(DrawingPixelText.Angle); + //cursor.PutTextRotated(text, cursor.Size.ToPoint().Half(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.Angle); if (_showLayerImageFlipped) { var flipType = FlipType.None; diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs index 1a4d9b0..80f127a 100644 --- a/UVtools.WPF/MainWindow.PixelEditor.cs +++ b/UVtools.WPF/MainWindow.PixelEditor.cs @@ -262,7 +262,7 @@ namespace UVtools.WPF { var operationText = new PixelText(layerIndex, realLocation, DrawingPixelText.LineType, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, - DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd); + DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.Angle, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd); //if (PixelHistory.Contains(operation)) continue; //PixelHistory.Add(operation); diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 50fd61d..89f1090 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -982,7 +982,7 @@ </Border> <Grid - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,10,130,5,40"> <TextBlock @@ -1063,46 +1063,64 @@ Grid.Row="12" Grid.Column="0" VerticalAlignment="Center" - Text="Remove pixel brightness:" /> + Text="Rotation angle:" /> <NumericUpDown Grid.Row="12" Grid.Column="2" + FormatString="F2" + Minimum="-360" + Maximum="360" + Value="{Binding DrawingPixelText.Angle}"/> + <TextBlock + Grid.Row="12" + Grid.Column="4" + VerticalAlignment="Center" + Text="º" /> + + <TextBlock + Grid.Row="14" + Grid.Column="0" + VerticalAlignment="Center" + Text="Remove pixel brightness:" /> + <NumericUpDown + Grid.Row="14" + Grid.Column="2" Minimum="0" Maximum="255" Value="{Binding DrawingPixelText.RemovePixelBrightness}"/> <TextBlock - Grid.Row="12" + Grid.Row="14" Grid.Column="4" VerticalAlignment="Center" Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock - Grid.Row="14" + Grid.Row="16" Grid.Column="0" VerticalAlignment="Center" Text="Add pixel brightness:" /> <NumericUpDown - Grid.Row="14" + Grid.Row="16" Grid.Column="2" Minimum="1" Maximum="255" Value="{Binding DrawingPixelText.PixelBrightness}"/> <TextBlock - Grid.Row="14" + Grid.Row="16" Grid.Column="4" VerticalAlignment="Center" Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> <TextBlock - Grid.Row="16" + Grid.Row="18" Grid.Column="0" VerticalAlignment="Center" Text="Layers depth below:" /> <NumericUpDown - Grid.Row="16" + Grid.Row="18" Grid.Column="2" Grid.ColumnSpan="3" Minimum="0" @@ -1111,12 +1129,12 @@ <TextBlock - Grid.Row="18" + Grid.Row="20" Grid.Column="0" VerticalAlignment="Center" Text="Layers depth above:" /> <NumericUpDown - Grid.Row="18" + Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="3" Minimum="0" @@ -1824,6 +1842,10 @@ <CheckBox IsChecked="{Binding ShowLayerOutlineEdgeDetection}" Content="Edge detection"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineSkeletonize}" + IsEnabled="{Binding !ShowLayerOutlineEdgeDetection}" + Content="Skeletonize"/> </ContextMenu> </Button.ContextMenu> <StackPanel Orientation="Horizontal"> diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index d146014..67fc9b7 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -351,6 +351,14 @@ namespace UVtools.WPF }, new() { + Tag = new OperationLayerExportSkeleton(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/file-image-16x16.png")) + } + }, + new() + { Tag = new OperationLayerExportHeatMap(), Icon = new Avalonia.Controls.Image { diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 16f4847..eeab339 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.13.0</Version> + <Version>2.13.1</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |