diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c941466..d0e641f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,17 @@
# Changelog
+## 09/01/2025 - v5.0.5
+
+- (Add) PrusaSlicer printer: Elegoo Saturn 4 Ultra 16K
+- (Improvement) Goo: Implement and support the tilting vat printers
+- (Improvement) All shapes in pixel editor will now respect the non-equal pixel pitch and compensate the lower side to print a regular shape, this also affects the polygons on PCB exposure tool and other tools as well
+- (Fix) PCB Exposure: Use raw polygons instead of angle aligned polygons to respect the gerber implementation (#976)
+
## 08/01/2025 - v5.0.4
- PCB Exposure:
- - (Fix) Polygon primitive vertex count not parsing correctly when having argument (#976)
+ - (Fix) Unable to parse primitive multiplications (#976)
+ - (Fix) Polygon primitive vertex count parsing to the incorrect variable (#976)
- (Fix) Obround aperture to follow the correct implementation (two semicircles connected by parallel lines tangent to their endpoints) (#976)
- (Fix) Implement the "hole diameter" argument in all apertures (#976)
- (Fix) Implement the "rotation" argument for the polygon aperture
diff --git a/Directory.Build.props b/Directory.Build.props
index 83c02323..6143ee9b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -37,7 +37,7 @@
true
- 5.0.4
+ 5.0.5
11.2.3
diff --git a/PrusaSlicer/printer/Elegoo Saturn 4 Ultra.ini b/PrusaSlicer/printer/Elegoo Saturn 4 Ultra 12K.ini
similarity index 83%
rename from PrusaSlicer/printer/Elegoo Saturn 4 Ultra.ini
rename to PrusaSlicer/printer/Elegoo Saturn 4 Ultra 12K.ini
index 8cf7ca67..2b146a46 100644
--- a/PrusaSlicer/printer/Elegoo Saturn 4 Ultra.ini
+++ b/PrusaSlicer/printer/Elegoo Saturn 4 Ultra 12K.ini
@@ -1,4 +1,4 @@
-# generated by PrusaSlicer 2.8.0+win64 on 2024-07-24 at 21:26:44 UTC
+# generated by PrusaSlicer 2.9.0 on 2025-01-08 at 03:32:14 UTC
absolute_correction = 0
bed_custom_model =
bed_custom_texture =
@@ -26,7 +26,7 @@ min_exposure_time = 1
min_initial_exposure_time = 1
print_host =
printer_model = SL1
-printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_SATURN4_ULTRA\nFILEFORMAT_ENCRYPTED.CTB\n\nSTART_CUSTOM_VALUES\nBottomLiftHeight_0.05\nLiftHeight_0.05\nBottomLiftSpeed_0.05\nLiftSpeed_0.05\nRetractSpeed_0.05\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
+printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_SATURN4_ULTRA_12K\nFILEFORMAT_ENCRYPTED.CTB\n\nSTART_CUSTOM_VALUES\nBottomLiftHeight_0.05\nLiftHeight_0.05\nBottomLiftSpeed_0.05\nLiftSpeed_0.05\nRetractSpeed_0.05\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
printer_settings_id =
printer_technology = SLA
printer_variant = default
diff --git a/PrusaSlicer/printer/Elegoo Saturn 4 Ultra 16K.ini b/PrusaSlicer/printer/Elegoo Saturn 4 Ultra 16K.ini
new file mode 100644
index 00000000..577349a1
--- /dev/null
+++ b/PrusaSlicer/printer/Elegoo Saturn 4 Ultra 16K.ini
@@ -0,0 +1,43 @@
+# generated by PrusaSlicer 2.9.0 on 2025-01-08 at 03:33:33 UTC
+absolute_correction = 0
+bed_custom_model =
+bed_custom_texture =
+bed_shape = 0x0,211.68x0,211.68x118.37,0x118.37
+default_sla_material_profile = Prusa Orange Tough 0.05
+default_sla_print_profile = 0.05 Normal
+display_height = 118.37
+display_mirror_x = 1
+display_mirror_y = 0
+display_orientation = landscape
+display_pixels_x = 15120
+display_pixels_y = 6230
+display_width = 211.68
+elefant_foot_compensation = 0.2
+elefant_foot_min_width = 0.2
+fast_tilt_time = 5
+gamma_correction = 1
+high_viscosity_tilt_time = 10
+host_type = octoprint
+inherits = Original Prusa SL1
+max_exposure_time = 120
+max_initial_exposure_time = 300
+max_print_height = 220
+min_exposure_time = 1
+min_initial_exposure_time = 1
+print_host =
+printer_model = SL1
+printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_SATURN4_ULTRA_16K\nFILEFORMAT_ENCRYPTED.CTB\n\nSTART_CUSTOM_VALUES\nBottomLiftHeight_0.05\nLiftHeight_0.05\nBottomLiftSpeed_0.05\nLiftSpeed_0.05\nRetractSpeed_0.05\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES
+printer_settings_id =
+printer_technology = SLA
+printer_variant = default
+printer_vendor =
+printhost_apikey =
+printhost_cafile =
+relative_correction = 1,1
+relative_correction_x = 1
+relative_correction_y = 1
+relative_correction_z = 1
+sla_archive_format = SL1
+sla_output_precision = 0.001
+slow_tilt_time = 8
+thumbnails = 290x290/PNG, 116x116/PNG
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 115fcc4d..787161e6 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,5 @@
-- PCB Exposure:
- - (Fix) Polygon primitive vertex count not parsing correctly when having argument (#976)
- - (Fix) Obround aperture to follow the correct implementation (two semicircles connected by parallel lines tangent to their endpoints) (#976)
- - (Fix) Implement the "hole diameter" argument in all apertures (#976)
- - (Fix) Implement the "rotation" argument for the polygon aperture
+- (Add) PrusaSlicer printer: Elegoo Saturn 4 Ultra 16K
+- (Improvement) Goo: Implement and support the tilting vat printers
+- (Improvement) All shapes in pixel editor will now respect the non-equal pixel pitch and compensate the lower side to print a regular shape, this also affects the polygons on PCB exposure tool and other tools as well
+- (Fix) PCB Exposure: Use raw polygons instead of angle aligned polygons to respect the gerber implementation (#976)
diff --git a/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs
index b58ed507..86441cd9 100644
--- a/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs
+++ b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs
@@ -63,9 +63,8 @@ private Mat GenerateDotPattern() {
var evenRow = false;
for (int y = 0; y < pattern.Size.Height; y += yStep) {
for (int x = 0; x < pattern.Size.Width; x += xStep) {
- CvInvoke.Circle(pattern,
- new Point(x + (evenRow ? xStep / 2 : 0), y),
- GrainSize.Value / 2,
+ pattern.DrawCircle(new Point(x + (evenRow ? xStep / 2 : 0), y),
+ SlicerFile.PixelsToNormalizedPitch(GrainSize.Value / 2),
EmguExtensions.WhiteColor,
-1, LineType.FourConnected);
}
diff --git a/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
index 19f010e0..780aec52 100644
--- a/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
+++ b/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
@@ -90,14 +90,15 @@ public bool ScriptExecute()
// Do the left eye
var x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2;
int y = faceSpacing;
- 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);
+ var radius = SlicerFile.PixelsToNormalizedPitch(eyeDiameter / 2);
+ mats[0].DrawCircle(new Point(x, y), radius, EmguExtensions.WhiteColor, -1, lineType);
+ mats[1].DrawCircle(new Point(x, y), radius, 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.WhiteColor, -1, lineType);
- CvInvoke.Circle(mats[3], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType);
+ mats[0].DrawCircle(new Point(x, y), radius, EmguExtensions.WhiteColor, -1, lineType);
+ mats[3].DrawCircle(new Point(x, y), radius, EmguExtensions.WhiteColor, -1, lineType);
Progress++;
// Do the noose
diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
index f01ef945..c28a0ce5 100644
--- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
+++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
@@ -9,7 +9,7 @@
- ExtendedNumericUpDown: Initial value with a reset button and value unit label
- IndexSelector: Allow to choose an index from a collection count and display the selected number
- GroupBox: Similar to GroupBox of WinForms, it contain an Header and Content
- 4.0.2
+ 4.0.3
MIT
https://github.com/sn4k3/UVtools/tree/master/UVtools.AvaloniaControls
diff --git a/UVtools.Core/Excellon/ExcellonDrillFormat.cs b/UVtools.Core/Excellon/ExcellonDrillFormat.cs
index 8ccb11c1..efede372 100644
--- a/UVtools.Core/Excellon/ExcellonDrillFormat.cs
+++ b/UVtools.Core/Excellon/ExcellonDrillFormat.cs
@@ -374,6 +374,10 @@ public Point PositionMmToPx(PointF atMm)
public int SizeMmToPx(float sizeMm)
=> (int)Math.Max(1, Math.Round(sizeMm * XYppmm.Max() * SizeScale, (MidpointRounding)SizeMidpointRounding));
+
+ public Size SizeMmToPx(float sizeMmX, float sizeMmY)
+ => new ((int)Math.Max(1, Math.Round(sizeMmX * XYppmm.Width * SizeScale, (MidpointRounding)SizeMidpointRounding)),
+ (int)Math.Max(1, Math.Round(sizeMmX * XYppmm.Height * SizeScale, (MidpointRounding)SizeMidpointRounding)));
#endregion
#region Static methods
@@ -383,9 +387,10 @@ public static void ParseAndDraw(ExcellonDrillFormat document, string filePath, M
foreach (var drill in document.Drills)
{
+ var radiusMillimeters = document.GetMillimeters(drill.Diameter / 2);
var position = document.PositionMmToPx(document.GetMillimeters(drill.Position));
- var radius = document.SizeMmToPx(document.GetMillimeters(drill.Diameter / 2));
- CvInvoke.Circle(mat, position, radius,
+ var radius = document.SizeMmToPx(radiusMillimeters, radiusMillimeters);
+ mat.DrawCircle(position, radius,
document.InversePolarity ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
-1,
enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
diff --git a/UVtools.Core/Extensions/DrawingExtensions.cs b/UVtools.Core/Extensions/DrawingExtensions.cs
index c407da0a..72eac893 100644
--- a/UVtools.Core/Extensions/DrawingExtensions.cs
+++ b/UVtools.Core/Extensions/DrawingExtensions.cs
@@ -38,12 +38,73 @@ public static double CalculatePolygonRadiusFromSideLength(double length, int sid
return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
}
-
- public static Point[] GetPolygonVertices(int sides, double diameter, PointF center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ public static Point[] GetPolygonVertices(int sides, SizeF diameter, PointF center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
{
if (sides < 3)
throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
+ var vertices = new Point[sides];
+ var radiusX = diameter.Width / 2; // X radius for pixel pitch
+ var radiusY = diameter.Height / 2; // Y radius for pixel pitch
+
+ if (sides == 4)
+ {
+ var rotatedRect = new RotatedRect(center, new SizeF(diameter.Width - 1, diameter.Height - 1), (float)startingAngle);
+ var verticesF = rotatedRect.GetVertices();
+ for (var i = 0; i < verticesF.Length; i++)
+ {
+ vertices[i] = verticesF[i].ToPoint(midpointRounding);
+ }
+ }
+ else
+ {
+ var angleIncrement = 2 * Math.PI / sides;
+ var startRotationAngleRadians = startingAngle * Math.PI / 180;
+
+ for (int i = 0; i < sides; i++)
+ {
+ var angle = startRotationAngleRadians + i * angleIncrement;
+
+ // Scale the X and Y coordinates independently for pixel pitch
+ var x = (int)Math.Round(center.X + radiusX * Math.Cos(angle), midpointRounding);
+ var y = (int)Math.Round(center.Y + radiusY * Math.Sin(angle), midpointRounding);
+
+ vertices[i] = new Point(x, y);
+ }
+ }
+
+ if (flipHorizontally)
+ {
+ var startX = center.X - radiusX;
+ var endX = center.X + radiusX;
+ for (int i = 0; i < sides; i++)
+ {
+ vertices[i].X = (int)Math.Round(endX - (vertices[i].X - startX), midpointRounding);
+ }
+ }
+
+ if (flipVertically)
+ {
+ var startY = center.Y - radiusY;
+ var endY = center.Y + radiusY;
+ for (int i = 0; i < sides; i++)
+ {
+ vertices[i].Y = (int)Math.Round(endY - (vertices[i].Y - startY), midpointRounding);
+ }
+ }
+
+ return vertices;
+ }
+
+
+ public static Point[] GetAlignedPolygonVertices(int sides, SizeF diameter, PointF center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ {
+ if (sides != 4) startingAngle += (180 - (360.0 / sides)) / 2;
+ return GetPolygonVertices(sides, diameter, center, startingAngle, flipHorizontally, flipVertically, midpointRounding);
+
+ /*if (sides < 3)
+ throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
+
var vertices = new Point[sides];
var radius = diameter / 2;
@@ -58,6 +119,7 @@ public static Point[] GetPolygonVertices(int sides, double diameter, PointF cent
}
else
{
+ // Aligned version
var angleIncrement = 360.0 / sides; //calculate the rotation angle
var rad = Math.PI / 180.0;
@@ -110,5 +172,12 @@ public static Point[] GetPolygonVertices(int sides, double diameter, PointF cent
}
return vertices;
+ */
+ }
+
+ public static Point[] GetAlignedPolygonVertices(int sides, float diameter, PointF center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ {
+ if (sides != 4) startingAngle += (180 - (360.0 / sides)) / 2;
+ return GetPolygonVertices(sides, new SizeF(diameter, diameter), center, startingAngle, flipHorizontally, flipVertically, midpointRounding);
}
}
\ No newline at end of file
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index cf8c03b9..ef61ef3e 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -22,6 +22,10 @@
using CommunityToolkit.Diagnostics;
using UVtools.Core.EmguCV;
using UVtools.Core.Objects;
+using Emgu.CV.Reg;
+using static UVtools.Core.FileFormats.UVJFile;
+using System.Reflection.Metadata;
+using Size = System.Drawing.Size;
namespace UVtools.Core.Extensions;
@@ -1892,7 +1896,7 @@ public static void DrawCenteredRectangle(this Mat src, Size size, Point center,
///
///
/// Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle
- /// Diameter
+ /// Diameter for both X and Y axis
/// Center position
///
///
@@ -1900,12 +1904,12 @@ public static void DrawCenteredRectangle(this Mat src, Size size, Point center,
///
///
///
- public static void DrawPolygon(this Mat src, int sides, double diameter, PointF center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ public static void DrawPolygon(this Mat src, int sides, SizeF diameter, PointF center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
{
if (sides == 1)
{
- var point1 = center with { X = (float)Math.Round(center.X - diameter / 2, midpointRounding) };
- var point2 = point1 with { X = (float)(point1.X + diameter - 1) };
+ var point1 = center with { X = (float)Math.Round(center.X - diameter.Width / 2, midpointRounding) };
+ var point2 = point1 with { X = point1.X + diameter.Width - 1 };
point1 = point1.Rotate(startingAngle, center);
point2 = point2.Rotate(startingAngle, center);
@@ -1930,7 +1934,9 @@ public static void DrawPolygon(this Mat src, int sides, double diameter, PointF
}
if (sides >= 100)
{
- CvInvoke.Circle(src, center.ToPoint(midpointRounding), (int)Math.Round(diameter / 2, midpointRounding), color, thickness, lineType);
+ src.DrawCircle(center.ToPoint(midpointRounding),
+ new Size((int)Math.Round(diameter.Width / 2, midpointRounding), (int)Math.Round(diameter.Height / 2, midpointRounding)),
+ color, -1, lineType);
return;
}
@@ -1938,7 +1944,7 @@ public static void DrawPolygon(this Mat src, int sides, double diameter, PointF
flip is FlipType.Horizontal or FlipType.Both,
flip is FlipType.Vertical or FlipType.Both,
midpointRounding);
-
+
if (thickness <= 0)
{
using var vec = new VectorOfPoint(points);
@@ -1948,7 +1954,90 @@ public static void DrawPolygon(this Mat src, int sides, double diameter, PointF
{
CvInvoke.Polylines(src, points, true, color, thickness, lineType);
}
-
+ }
+
+ ///
+ /// Draw a polygon given number of sides and diameter
+ ///
+ ///
+ /// Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle
+ /// Diameter
+ /// Center position
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void DrawPolygon(this Mat src, int sides, float diameter, PointF center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ {
+ DrawPolygon(src, sides, new SizeF(diameter, diameter), center, color, startingAngle, thickness, lineType, flip, midpointRounding);
+ }
+
+ ///
+ /// Draw a polygon given number of sides and diameter (Aligned in X axis)
+ ///
+ ///
+ /// Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle
+ /// Diameter for both X and Y axis
+ /// Center position
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void DrawAlignedPolygon(this Mat src, int sides, SizeF diameter, PointF center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ {
+ if (sides >= 3 && sides != 4)
+ {
+ startingAngle += (180 - (360.0 / sides)) / 2;
+ }
+ DrawPolygon(src, sides, diameter, center, color, startingAngle, thickness, lineType, flip, midpointRounding);
+ }
+
+ public static void DrawAlignedPolygon(this Mat src, int sides, float diameter, PointF center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null, MidpointRounding midpointRounding = MidpointRounding.AwayFromZero)
+ {
+ if (sides >= 3 && sides != 4)
+ {
+ startingAngle += (180 - (360.0 / sides)) / 2;
+ }
+ DrawPolygon(src, sides, new SizeF(diameter, diameter), center, color, startingAngle, thickness, lineType, flip, midpointRounding);
+ }
+
+ ///
+ /// Draw a circle with a center point and radius
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void DrawCircle(this Mat src, Point center, Size radius, MCvScalar color, int thickness = -1, LineType lineType = LineType.EightConnected)
+ {
+ if (Math.Abs(radius.Width - radius.Height) < 0.01)
+ {
+ CvInvoke.Circle(src, center, radius.Width, color, thickness, lineType);
+ }
+ else
+ {
+ CvInvoke.Ellipse(src, center, radius, 0, 0, 360, color, thickness, lineType);
+ }
+ }
+
+ ///
+ /// Draw a circle with a center point and radius
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void DrawCircle(this Mat src, Point center, int radius, MCvScalar color, int thickness = -1, LineType lineType = LineType.EightConnected)
+ {
+ CvInvoke.Circle(src, center, radius, color, thickness, lineType);
}
#endregion
diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
index cf4da2aa..a8ca4b1b 100644
--- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs
+++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
@@ -593,8 +593,8 @@ public override PrintParameterModifier[] PrintParameterModifiers
{
if (HaveTiltingVat)
{
- return new[]
- {
+ return
+ [
PrintParameterModifier.BottomLayerCount,
PrintParameterModifier.TransitionLayerCount,
@@ -615,11 +615,11 @@ public override PrintParameterModifier[] PrintParameterModifiers
PrintParameterModifier.BottomLightPWM,
PrintParameterModifier.LightPWM
- };
+ ];
}
- return new[]
- {
+ return
+ [
PrintParameterModifier.BottomLayerCount,
PrintParameterModifier.TransitionLayerCount,
@@ -656,7 +656,7 @@ public override PrintParameterModifier[] PrintParameterModifiers
PrintParameterModifier.BottomLightPWM,
PrintParameterModifier.LightPWM
- };
+ ];
}
}
@@ -666,8 +666,8 @@ public override PrintParameterModifier[] PrintParameterPerLayerModifiers
{
if (HaveTiltingVat)
{
- return new[]
- {
+ return
+ [
PrintParameterModifier.PositionZ,
PrintParameterModifier.LightOffDelay,
PrintParameterModifier.WaitTimeBeforeCure,
@@ -675,11 +675,11 @@ public override PrintParameterModifier[] PrintParameterPerLayerModifiers
PrintParameterModifier.WaitTimeAfterCure,
PrintParameterModifier.WaitTimeAfterLift,
PrintParameterModifier.LightPWM
- };
+ ];
}
- return new[]
- {
+ return
+ [
PrintParameterModifier.PositionZ,
PrintParameterModifier.LightOffDelay,
PrintParameterModifier.WaitTimeBeforeCure,
@@ -694,7 +694,7 @@ public override PrintParameterModifier[] PrintParameterPerLayerModifiers
PrintParameterModifier.RetractHeight2,
PrintParameterModifier.RetractSpeed2,
PrintParameterModifier.LightPWM
- };
+ ];
}
}
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 80ec8e09..ea953d48 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -2245,6 +2245,95 @@ public virtual float MachineZ
///
public float PixelAreaMicrons => PixelSizeMicrons.Area();
+ ///
+ /// Gets the pixel scale normalized with X relative to Y
+ ///
+ public float PixelScaleNormalizeXRelativeToY
+ {
+ get
+ {
+ var pixelWidth = PixelWidth;
+ var pixelHeigth = PixelHeight;
+ return pixelWidth > 0 && pixelHeigth > 0
+ ? pixelHeigth / pixelWidth
+ : 1;
+ }
+ }
+
+ ///
+ /// Gets the pixel scale normalized with Y relative to X
+ ///
+ public float PixelScaleNormalizeYRelativeToX
+ {
+ get
+ {
+ var pixelWidth = PixelWidth;
+ var pixelHeigth = PixelHeight;
+ return pixelWidth > 0 && pixelHeigth > 0
+ ? pixelWidth / pixelHeigth
+ : 1;
+ }
+ }
+
+ ///
+ /// Gets the pixel scale normalized with X relative to Y and Y relative to X
+ ///
+ public SizeF PixelScaleNormalized
+ {
+ get
+ {
+ var pixelWidth = PixelWidth;
+ var pixelHeigth = PixelHeight;
+ return pixelWidth > 0 && pixelHeigth > 0
+ ? new SizeF(pixelHeigth / pixelWidth, pixelWidth / pixelHeigth)
+ : new SizeF(1f, 1f);
+ }
+ }
+
+ ///
+ /// Translates a pixel size to a X/Y normalized pitch where it compensates the lower side
+ ///
+ ///
+ ///
+ public SizeF PixelsToNormalizedPitchF(int size)
+ {
+ var pixelScaleNormalized = PixelScaleNormalized;
+ if (pixelScaleNormalized.Width == PixelScaleNormalized.Height)
+ {
+ return new SizeF(size, size);
+ }
+ if (pixelScaleNormalized.Width > PixelScaleNormalized.Height)
+ {
+ return new SizeF(size * pixelScaleNormalized.Width, size);
+ }
+ else
+ {
+ return new SizeF(size, size * pixelScaleNormalized.Height);
+ }
+ }
+
+ ///
+ /// Translates a pixel size to a X/Y normalized pitch where it compensates the lower side
+ ///
+ ///
+ ///
+ public Size PixelsToNormalizedPitch(int size)
+ {
+ var pixelScaleNormalized = PixelScaleNormalized;
+ if (pixelScaleNormalized.Width == PixelScaleNormalized.Height)
+ {
+ return new Size(size, size);
+ }
+ if (pixelScaleNormalized.Width > PixelScaleNormalized.Height)
+ {
+ return new Size((int)(size * pixelScaleNormalized.Width), size);
+ }
+ else
+ {
+ return new Size(size, (int)(size * pixelScaleNormalized.Height));
+ }
+ }
+
///
/// Gets the file volume (XYZ) in mm^3
///
@@ -7640,7 +7729,11 @@ or PixelOperation.PixelOperationType.Text
continue;
}
- mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize,
+ var pixelWidth = PixelWidth;
+ var pixelHeigth = PixelHeight;
+ var diameter = PixelsToNormalizedPitchF(operationDrawing.BrushSize);
+
+ mat.DrawAlignedPolygon((byte)operationDrawing.BrushShape, diameter,
operationDrawing.Location,
new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle,
operationDrawing.Thickness, operationDrawing.LineType);
@@ -7725,12 +7818,14 @@ is PixelOperation.PixelOperationType.Supports
int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2);
int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2);
- using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
+ var tipDiameter = PixelsToNormalizedPitch(operationSupport.TipDiameter);
+ var tipRadius = PixelsToNormalizedPitch(operationSupport.TipDiameter / 2);
+ var pillarDiameter = PixelsToNormalizedPitch(operationSupport.PillarDiameter);
+
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, tipDiameter.Width, tipDiameter.Height)))
{
using var matCircleMask = matCircleRoi.NewZeros();
- CvInvoke.Circle(matCircleMask,
- new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2),
- operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1);
+ matCircleMask.DrawCircle(tipRadius.ToPoint(), tipRadius, new MCvScalar(operation.PixelBrightness), -1);
CvInvoke.BitwiseAnd(matCircleRoi, matCircleMask, matCircleMask);
whitePixels = (uint) CvInvoke.CountNonZero(matCircleMask);
}
@@ -7743,7 +7838,7 @@ is PixelOperation.PixelOperationType.Supports
continue; // White area end supporting
}
- CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType);
+ mat.DrawCircle(operation.Location, PixelsToNormalizedPitch(radius), new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType);
isMatModified = true;
drawnSupportLayers++;
}
@@ -7751,18 +7846,19 @@ is PixelOperation.PixelOperationType.Supports
{
var operationDrainHole = (PixelDrainHole) operation;
- int radius = operationDrainHole.Diameter / 2;
+ var diameterPitched = PixelsToNormalizedPitch(operationDrainHole.Diameter);
+ var radius = PixelsToNormalizedPitch(operationDrainHole.Diameter / 2);
uint blackPixels;
- int yStart = Math.Max(0, operation.Location.Y - radius);
- int xStart = Math.Max(0, operation.Location.X - radius);
+ int xStart = Math.Max(0, operation.Location.X - radius.Width);
+ int yStart = Math.Max(0, operation.Location.Y - radius.Height);
- using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, diameterPitched.Width, diameterPitched.Height)))
{
using var matCircleRoiInv = new Mat();
CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
using var matCircleMask = matCircleRoi.NewZeros();
- CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, EmguExtensions.WhiteColor, -1);
+ matCircleMask.DrawCircle(radius.ToPoint(), radius, EmguExtensions.WhiteColor, -1);
CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask);
blackPixels = (uint) CvInvoke.CountNonZero(matCircleMask);
}
@@ -7774,7 +7870,7 @@ is PixelOperation.PixelOperationType.Supports
continue; // Stop drill drain found!
}
- CvInvoke.Circle(mat, operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType);
+ mat.DrawCircle(operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType);
isMatModified = true;
drawnDrainHoleLayers++;
}
diff --git a/UVtools.Core/FileFormats/GooFile.cs b/UVtools.Core/FileFormats/GooFile.cs
index 90d4964a..7a67909c 100644
--- a/UVtools.Core/FileFormats/GooFile.cs
+++ b/UVtools.Core/FileFormats/GooFile.cs
@@ -521,61 +521,125 @@ public override string ToString()
public FileFooter Footer { get; private set; } = new();
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
-
- //PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
-
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
-
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
-
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
-
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
-
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM
- };
-
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM
- };
+ public override PrintParameterModifier[] PrintParameterModifiers
+ {
+ get
+ {
+ if (HaveTiltingVat)
+ {
+ return
+ [
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
+
+ //PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM
+ ];
+ }
+
+ return
+ [
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
+
+ //PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM
+ ];
+ }
+ }
+
+ public override PrintParameterModifier[] PrintParameterPerLayerModifiers
+ {
+ get
+ {
+ if (HaveTiltingVat)
+ {
+ return
+ [
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.LightPWM
+ ];
+ }
+
+ return
+ [
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM
+ ];
+ }
+ }
+
+ public override bool HaveTiltingVat
+ {
+ get
+ {
+ if (MachineName.Contains("Saturn 4 Ultra", StringComparison.OrdinalIgnoreCase)) return true;
+ return LayerHeight == LiftHeight && LiftHeight < 0.5 && LiftSpeed < 0.5;
+ }
+ }
public override uint ResolutionX
{
@@ -1013,6 +1077,19 @@ protected override void OnBeforeEncode(bool isPartialEncode)
Header.PerLayerSettings = UsingPerLayerSettings;
Header.Volume = Volume;
Header.MaterialGrams = MaterialMilliliters;
+
+ if (HaveTiltingVat)
+ {
+ BottomLiftHeightTotal = LayerHeight;
+ LiftHeightTotal = LayerHeight;
+ if (BottomLiftSpeed > 0.5 || LiftSpeed > 0.5 || BottomRetractSpeed > 0.5 || RetractSpeed > 0.5)
+ {
+ BottomLiftSpeed = 0.05f;
+ LiftSpeed = 0.05f;
+ BottomRetractSpeed = 0.05f;
+ RetractSpeed = 0.05f;
+ }
+ }
}
protected override void EncodeInternally(OperationProgress progress)
diff --git a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
index b6460a36..64122053 100644
--- a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
@@ -34,11 +34,11 @@ public PolygonAperture(GerberFormat document, int index, double diameter, ushort
if (holeDiameter > 0) HoleDiameter = document.GetMillimeters(holeDiameter);
}
#endregion
-
+
public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
var location = Document.PositionMmToPx(at);
- mat.DrawPolygon(Vertices, Document.SizeMmToPx(Diameter), location, color, Rotation, -1, lineType);
+ mat.DrawPolygon(Vertices, Document.SizeMmToPx(Diameter, Diameter), location, color, Rotation, -1, lineType);
if (HoleDiameter > 0)
{
var invertColor = color.Equals(EmguExtensions.BlackColor) ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor;
diff --git a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
index 749d8894..f0e20909 100644
--- a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
@@ -91,7 +91,7 @@ public override void DrawFlashD3(Mat mat, PointF at, LineType lineType = LineTyp
if (Diameter <= 0) return;
mat.DrawPolygon(VerticesCount,
- Document.SizeMmToPx(Diameter),
+ Document.SizeMmToPx(Diameter, Diameter),
Document.PositionMmToPx(at.X + CenterX, at.Y + CenterY),
Document.GetPolarityColor(Exposure), Rotation, -1, lineType);
}
diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs
index 6986e0b1..dcf082e5 100644
--- a/UVtools.Core/Managers/IssueManager.cs
+++ b/UVtools.Core/Managers/IssueManager.cs
@@ -1155,11 +1155,12 @@ public MainIssue[] DrillSuctionCupsForIssues(IEnumerable issues, int
{
var drillOps = new List();
var drilledIssues = new List();
+ var radius = SlicerFile.PixelsToNormalizedPitch(ventHoleDiameter / 2);
//var suctionReliefSize = (ushort)Math.Max(SlicerFile.PpmmMax * 0.8, 17);
/* for each suction cup issue that is an initial layer */
foreach (var mainIssue in issues)
{
- var drillPoint = GetDrillLocation((IssueOfContours)mainIssue[0], ventHoleDiameter);
+ var drillPoint = GetDrillLocation((IssueOfContours)mainIssue[0], radius);
if (drillPoint.IsAnyNegative()) continue;
drillOps.Add(new PixelDrainHole(mainIssue.StartLayerIndex, drillPoint, (ushort)ventHoleDiameter));
drilledIssues.Add(mainIssue);
@@ -1170,7 +1171,7 @@ public MainIssue[] DrillSuctionCupsForIssues(IEnumerable issues, int
return drilledIssues.ToArray();
}
- public static Point GetDrillLocation(IssueOfContours issue, int diameter)
+ public static Point GetDrillLocation(IssueOfContours issue, Size radius)
{
using var vecCentroid = new VectorOfPoint(issue.Contours[0]);
var centroid = EmguContour.GetCentroid(vecCentroid);
@@ -1181,7 +1182,7 @@ public static Point GetDrillLocation(IssueOfContours issue, int diameter)
var inverseOffset = new Point(issue.BoundingRectangle.X * -1, issue.BoundingRectangle.Y * -1);
using var vec = new VectorOfVectorOfPoint(issue.Contours);
CvInvoke.DrawContours(contourMat, vec, -1, EmguExtensions.WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, inverseOffset);
- CvInvoke.Circle(circleCheck, new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), diameter, EmguExtensions.WhiteColor, -1);
+ circleCheck.DrawCircle(new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), radius, EmguExtensions.WhiteColor, -1);
CvInvoke.BitwiseAnd(circleCheck, contourMat, circleCheck);
return CvInvoke.HasNonZero(circleCheck)
diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
index 43d96a7c..ea96d3ba 100644
--- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
+++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
@@ -474,10 +474,10 @@ void addPoint()
maxY += ellipseHeight;
using Mat shape = EmguExtensions.InitMat(new Size(maxX + startX, maxY + startY));
CvInvoke.FillPoly(shape, new VectorOfPoint(pointList.ToArray()), EmguExtensions.WhiteColor, lineType);
- CvInvoke.Circle(shape, new Point(0, 0), length / 4, EmguExtensions.BlackColor, -1, lineType);
+ shape.DrawCircle(new Point(0, 0), SlicerFile.PixelsToNormalizedPitch(length / 4), EmguExtensions.BlackColor, -1, lineType);
CvInvoke.Ellipse(shape, new Point(maxX / 2, maxY - ellipseHeight), new Size(maxX / 3, ellipseHeight), 0, 0, 360,
EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(shape, new Point(length / 2, (int) (maxY - 100 * _partScale)), length / 5, EmguExtensions.BlackColor, -1, lineType);
+ shape.DrawCircle(new Point(length / 2, (int) (maxY - 100 * _partScale)), SlicerFile.PixelsToNormalizedPitch(length / 5), EmguExtensions.BlackColor, -1, lineType);
int currentX = 0;
int currentY = 0;
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 099fae49..b2f2917c 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -1444,9 +1444,8 @@ public Mat[] GetLayers(out Point markingTextPositivePosition, out Point markingT
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
case CalibrateExposureFinderShapes.Circle:
- CvInvoke.Circle(layers[layerIndex],
- new Point(xPos, yPos),
- radius, EmguExtensions.WhiteColor, -1,
+ layers[layerIndex].DrawCircle(new Point(xPos, yPos),
+ SlicerFile.PixelsToNormalizedPitch(radius), EmguExtensions.WhiteColor, -1,
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
}
@@ -1482,9 +1481,8 @@ public Mat[] GetLayers(out Point markingTextPositivePosition, out Point markingT
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
case CalibrateExposureFinderShapes.Circle:
- CvInvoke.Circle(layers[layerIndex],
- new Point(xPos, yPos),
- radius, EmguExtensions.BlackColor, -1,
+ layers[layerIndex].DrawCircle(new Point(xPos, yPos),
+ SlicerFile.PixelsToNormalizedPitch(radius), EmguExtensions.BlackColor, -1,
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
}
diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
index 4ee5f8ab..f4256362 100644
--- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
+++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
@@ -664,9 +664,8 @@ public Mat[] GetLayers()
if (CenterHoleRelief)
{
- CvInvoke.Circle(layer,
- new Point((int) (currentX + xPixels / 2), (int) (currentY + yPixels / 2)),
- (int) (Math.Min(xPixels, yPixels) / 4),
+ layer.DrawCircle(new Point((int) (currentX + xPixels / 2), (int) (currentY + yPixels / 2)),
+ SlicerFile.PixelsToNormalizedPitch((int) (Math.Min(xPixels, yPixels) / 4)),
EmguExtensions.BlackColor, -1);
}
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index b41ff5a6..1fe2e13e 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -303,12 +303,12 @@ protected override bool ExecuteInternally(OperationProgress progress)
int center = HoleDiameter / 2;
//int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
- int radius = center;
- CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
- CvInvoke.Circle(shape, new Point(0, 0), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, color, -1);
+ var radius = SlicerFile.PixelsToNormalizedPitch(center);
+ shape.DrawCircle(new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
+ shape.DrawCircle(new Point(0, 0), radius / 2, color, -1);
+ shape.DrawCircle(new Point(0, shapeSize), radius / 2, color, -1);
+ shape.DrawCircle(new Point(shapeSize, 0), radius / 2, color, -1);
+ shape.DrawCircle(new Point(shapeSize, shapeSize), radius / 2, color, -1);
CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
diff --git a/UVtools.Core/Printer/Machine.cs b/UVtools.Core/Printer/Machine.cs
index ebdd26e2..f2cbb318 100644
--- a/UVtools.Core/Printer/Machine.cs
+++ b/UVtools.Core/Printer/Machine.cs
@@ -290,7 +290,8 @@ public Machine Clone()
new(PrinterBrand.Elegoo, "Elegoo Saturn 2", "Saturn 2", 7680, 4320, 218.88f, 123.12f, 250f, FlipDirection.Horizontally),
new(PrinterBrand.Elegoo, "Elegoo Saturn 3", "Saturn 3", 11520, 5120, 218.88f, 122.88f, 249.7f, FlipDirection.Horizontally),
new(PrinterBrand.Elegoo, "Elegoo Saturn 3 Ultra", "Saturn 3 Ultra", 11520, 5120, 218.88f, 122.88f, 260f, FlipDirection.Horizontally),
- new(PrinterBrand.Elegoo, "Elegoo Saturn 4 Ultra", "Saturn 4 Ultra", 11520, 5120, 218.88f, 122.88f, 220f, FlipDirection.Horizontally),
+ new(PrinterBrand.Elegoo, "Elegoo Saturn 4 Ultra 12K", "Saturn 4 Ultra 12K", 11520, 5120, 218.88f, 122.88f, 220f, FlipDirection.Horizontally),
+ new(PrinterBrand.Elegoo, "Elegoo Saturn 4 Ultra 16K", "Saturn 4 Ultra 16K", 15120, 6230, 211.68f, 118.37f, 220f, FlipDirection.Horizontally),
new(PrinterBrand.Elegoo, "Elegoo Saturn 8K", "Saturn 8K", 7680, 4320, 218.88f, 123.12f, 210f, FlipDirection.Horizontally),
new(PrinterBrand.Elegoo, "Elegoo Saturn S", "Saturn S", 4098, 2560, 196.704f, 122.88f, 210f, FlipDirection.Horizontally),
diff --git a/UVtools.UI/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.UI/Controls/Tools/ToolMaskControl.axaml.cs
index 79b0e0ee..168d4799 100644
--- a/UVtools.UI/Controls/Tools/ToolMaskControl.axaml.cs
+++ b/UVtools.UI/Controls/Tools/ToolMaskControl.axaml.cs
@@ -131,7 +131,7 @@ public void GenerateMask()
for (decimal i = 1; i < radius; i++)
{
int color = (int)(_genMinimumBrightness - i / radius * colorDifference); //or some another color calculation
- CvInvoke.Circle(Operation.Mask, center, (int)i, new MCvScalar(color), 2);
+ Operation.Mask.DrawCircle(center, SlicerFile!.PixelsToNormalizedPitch((int)i), new MCvScalar(color), 2);
}
if (_isMaskInverted) Operation.InvertMask();
diff --git a/UVtools.UI/MainWindow.LayerPreview.cs b/UVtools.UI/MainWindow.LayerPreview.cs
index b7412bed..498361c8 100644
--- a/UVtools.UI/MainWindow.LayerPreview.cs
+++ b/UVtools.UI/MainWindow.LayerPreview.cs
@@ -1353,23 +1353,8 @@ is not MainIssue.IssueType.PrintHeight
continue;
}
- LayerCache.ImageBgra.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize, operationDrawing.Location,
+ LayerCache.ImageBgra.DrawAlignedPolygon((byte)operationDrawing.BrushShape, SlicerFile.PixelsToNormalizedPitchF(operationDrawing.BrushSize), operationDrawing.Location,
color.ToMCvScalar(), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
- /*switch (operationDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(LayerCache.ImageBgr, operationDrawing.Rectangle,
- color.ToMCvScalar(), operationDrawing.Thickness,
- operationDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationDrawing.BrushSize / 2,
- color.ToMCvScalar(), operationDrawing.Thickness,
- operationDrawing.LineType);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }*/
}
else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
{
@@ -1419,7 +1404,8 @@ is not MainIssue.IssueType.PrintHeight
? Settings.PixelEditor.SupportsHighlightColor
: Settings.PixelEditor.SupportsColor;
- CvInvoke.Circle(LayerCache.ImageBgra, operation.Location, operationSupport.TipDiameter / 2,
+ LayerCache.ImageBgra.DrawCircle(operation.Location,
+ SlicerFile.PixelsToNormalizedPitch(operationSupport.TipDiameter / 2),
color.ToMCvScalar(), -1);
}
else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
@@ -1429,7 +1415,7 @@ is not MainIssue.IssueType.PrintHeight
? Settings.PixelEditor.DrainHoleHighlightColor
: Settings.PixelEditor.DrainHoleColor;
- CvInvoke.Circle(LayerCache.ImageBgra, operation.Location, operationDrainHole.Diameter / 2, color.ToMCvScalar(), -1);
+ LayerCache.ImageBgra.DrawCircle(operation.Location, SlicerFile.PixelsToNormalizedPitch(operationDrainHole.Diameter / 2), color.ToMCvScalar(), -1);
}
}
@@ -2346,7 +2332,7 @@ public void UpdatePixelEditorCursor()
//if (cursorSize % 2 != 0) cursorSize++;
- cursor = EmguExtensions.InitMat(new Size(cursorSize, cursorSize), 4);
+ cursor = EmguExtensions.InitMat(SlicerFile!.PixelsToNormalizedPitch(cursorSize), 4);
//cursor.SetTo(new MCvScalar(255,255,255,255)); // Debug
/*FlipType? flip = null;
@@ -2357,7 +2343,9 @@ public void UpdatePixelEditorCursor()
else if (_showLayerImageFlippedVertically) flip = FlipType.Vertical;
}*/
- cursor.DrawPolygon((byte) DrawingPixelDrawing.BrushShape, DrawingPixelDrawing.BrushSize, new PointF(cursor.Width / 2.0f, cursor.Height / 2.0f),
+ cursor.DrawAlignedPolygon((byte) DrawingPixelDrawing.BrushShape,
+ SlicerFile!.PixelsToNormalizedPitchF(DrawingPixelDrawing.BrushSize),
+ new PointF(cursor.Width / 2.0f, cursor.Height / 2.0f),
pixelEditorCursorColor, DrawingPixelDrawing.RotationAngle, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
if (DrawingPixelDrawing.BrushShape != PixelDrawing.BrushShapeType.Circle)
@@ -2385,35 +2373,6 @@ public void UpdatePixelEditorCursor()
}
}
}
-
- /*switch (DrawingPixelDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(cursor,
- new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize, DrawingPixelDrawing.BrushSize)),
- _pixelEditorCursorColor, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
- _pixelEditorCursorColor.V3 = 255;
- CvInvoke.Rectangle(cursor,
- new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize-1, DrawingPixelDrawing.BrushSize-1)),
- _pixelEditorCursorColor, 1, DrawingPixelDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- var center = new Point(DrawingPixelDrawing.BrushSize / 2, DrawingPixelDrawing.BrushSize / 2);
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType
- );
- _pixelEditorCursorColor.V3 = 255;
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- 1, DrawingPixelDrawing.LineType
- );
- break;
- }*/
}
break;
case PixelOperation.PixelOperationType.Text:
@@ -2461,18 +2420,18 @@ public void UpdatePixelEditorCursor()
if (diameter >= PixelEditorCursorMinDiameter)
{
- cursor = EmguExtensions.InitMat(new Size(diameter, diameter), 4);
- var center = new Point(diameter / 2, diameter / 2);
- CvInvoke.Circle(cursor,
- center,
- center.X,
+ var diameterPitched = SlicerFile!.PixelsToNormalizedPitch(diameter);
+ var radiusPitched = SlicerFile!.PixelsToNormalizedPitch(diameter / 2);
+ cursor = EmguExtensions.InitMat(diameterPitched, 4);
+ var center = radiusPitched.ToPoint();
+ cursor.DrawCircle(center,
+ radiusPitched,
pixelEditorCursorColor,
-1, LineType.AntiAlias
);
pixelEditorCursorColor.V3 = 255;
- CvInvoke.Circle(cursor,
- center,
- center.X,
+ cursor.DrawCircle(center,
+ radiusPitched,
pixelEditorCursorColor,
1, LineType.AntiAlias
);
diff --git a/UVtools.UI/MainWindow.PixelEditor.cs b/UVtools.UI/MainWindow.PixelEditor.cs
index d6baa2bd..1eee95f5 100644
--- a/UVtools.UI/MainWindow.PixelEditor.cs
+++ b/UVtools.UI/MainWindow.PixelEditor.cs
@@ -359,8 +359,8 @@ void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers)
}
}
- var vertices = DrawingExtensions.GetPolygonVertices((byte)operationDrawing.BrushShape,
- operationDrawing.BrushSize,
+ var vertices = DrawingExtensions.GetAlignedPolygonVertices((byte)operationDrawing.BrushShape,
+ SlicerFile.PixelsToNormalizedPitchF(operationDrawing.BrushSize),
location, angle, _showLayerImageFlipped && _showLayerImageFlippedHorizontally,
_showLayerImageFlipped && _showLayerImageFlippedVertically);
@@ -489,7 +489,7 @@ void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers)
//if (PixelHistory.Contains(operation)) return;
AddDrawing(operationSupport);
- CvInvoke.Circle(LayerCache.ImageBgra, location, operationSupport.TipDiameter / 2,
+ LayerCache.ImageBgra.DrawCircle(location, SlicerFile!.PixelsToNormalizedPitch(operationSupport.TipDiameter / 2),
Settings.PixelEditor.SupportsColor.ToMCvScalar(), -1);
RefreshLayerImage();
return;
@@ -502,7 +502,7 @@ void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers)
//if (PixelHistory.Contains(operation)) return;
AddDrawing(operationDrainHole);
- CvInvoke.Circle(LayerCache.ImageBgra, location, operationDrainHole.Diameter / 2,
+ LayerCache.ImageBgra.DrawCircle(location, SlicerFile!.PixelsToNormalizedPitch(operationDrainHole.Diameter / 2),
Settings.PixelEditor.DrainHoleColor.ToMCvScalar(), -1);
RefreshLayerImage();
return;
diff --git a/UVtools.UI/MainWindow.axaml.cs b/UVtools.UI/MainWindow.axaml.cs
index 2fe60465..b8a434f8 100644
--- a/UVtools.UI/MainWindow.axaml.cs
+++ b/UVtools.UI/MainWindow.axaml.cs
@@ -1961,7 +1961,7 @@ await this.MessageBoxWaring(
PopulateSuggestions();
- if (SlicerFile is CTBEncryptedFile)
+ /*if (SlicerFile is CTBEncryptedFile)
{
if (Settings.General.LockedFilesOpenCounter == 0)
{
@@ -1974,7 +1974,7 @@ await this.MessageBoxWaring(
Settings.General.LockedFilesOpenCounter = 0;
}
UserSettings.Save();
- }
+ }*/
if (!string.IsNullOrWhiteSpace(Settings.Automations.EventAfterFileLoadScriptFile) &&
File.Exists(Settings.Automations.EventAfterFileLoadScriptFile))
diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml
index ed181a4d..ed206401 100644
--- a/documentation/UVtools.Core.xml
+++ b/documentation/UVtools.Core.xml
@@ -1804,7 +1804,22 @@
-
+
+
+ Draw a polygon given number of sides and diameter
+
+
+ Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle
+ Diameter for both X and Y axis
+ Center position
+
+
+
+
+
+
+
+
Draw a polygon given number of sides and diameter
@@ -1819,6 +1834,43 @@
+
+
+ Draw a polygon given number of sides and diameter (Aligned in X axis)
+
+
+ Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle
+ Diameter for both X and Y axis
+ Center position
+
+
+
+
+
+
+
+
+
+ Draw a circle with a center point and radius
+
+
+
+
+
+
+
+
+
+
+ Draw a circle with a center point and radius
+
+
+
+
+
+
+
+
Left aligned without trimming, openCV default call
@@ -4284,6 +4336,35 @@
Gets the pixel area in millimeters
+
+
+ Gets the pixel scale normalized with X relative to Y
+
+
+
+
+ Gets the pixel scale normalized with Y relative to X
+
+
+
+
+ Gets the pixel scale normalized with X relative to Y and Y relative to X
+
+
+
+
+ Translates a pixel size to a X/Y normalized pitch where it compensates the lower side
+
+
+
+
+
+
+ Translates a pixel size to a X/Y normalized pitch where it compensates the lower side
+
+
+
+
Gets the file volume (XYZ) in mm^3